vercel-labs / vercel-labs/native

@native-sdk/core/text fails the subset checker's integer proofs once TextEditState is a Model field

Open
#406 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Zig
Stars
7.7k
Forks
314
Avg merge
5h
Merged PRs (30d)
13

Description

Summary

applyTextInputEvent is the documented way to drive a markup text control from a TypeScript core: hold a TextEditState in the Model, feed it TextInputEvents, store the result. Doing exactly that fails native build with 19 integer-proof errors across 10 lines, all inside sdk/text.ts itself. The app's own code is never named.

native check passes. Only native build fails.

Reproduced against native 0.9.3 (b25cefe), macOS 26.5.2, Zig 0.16.0, from a clean native init.

Repro

// src/core.ts
import { Cmd, asciiBytes } from "@native-sdk/core";
import { type TextEditState, type TextInputEvent, applyTextInputEvent } from "@native-sdk/core/text";

export interface Model {
  readonly name: TextEditState;   // storing it in the Model is what triggers this
}

export type Msg =
  | { readonly kind: "name_edit"; readonly edit: TextInputEvent }
  | { readonly kind: "noop" };

export function initialModel(): Model {
  return { name: { text: asciiBytes(""), selection: { anchor: 0, focus: 0 }, composition: null } };
}

export function nameText(model: Model): Uint8Array {
  return model.name.text;
}

export function update(model: Model, msg: Msg): Model | [Model, Cmd<Msg>] {
  switch (msg.kind) {
    case "name_edit": {
      const next = applyTextInputEvent(model.name, msg.edit, 120);
      if (!next) return model;
      return { ...model, name: next };
    }
    case "noop":
      return model;
  }
}
<!-- src/app.native -->
<column gap="12" padding="16">
  <text-field text="{nameText}" placeholder="Type here" on-input="name_edit" />
</column>

Actual

sdk/text.ts:79  SC4023  'TextRange.start' / 'TextRange.end' - range failed:
                        proven range [-Infinity, 9007199254740991]
sdk/text.ts:243,244,325,355,375,394,414,439,459
                SC4022  'TextRange.start' / 'TextRange.end' - wholeness failed:
                        the value may be NaN

Line 79 is rangeNormalized, where Math.min is float-classed:

const start = Math.min(r.start, textLen);
const end = Math.min(r.end, textLen);

The SC4022 sites are other TextRange constructions - snapTextOffset(...), previousTextCaretOffset(...), { start: result.insertedStart, end: result.insertedEnd }.

Scope

Two things I checked before filing:

  • It does not reproduce unless TextEditState is a Model field. Calling applyTextInputEvent and storing only next.text builds fine - the integer slots are only instantiated once the record becomes model state.
  • Fixing rangeNormalized alone is not sufficient. Replacing that Math.min pair with | 0-pinned ternaries clears all four SC4023 errors at line 79, but the nine SC4022 sites remain. This looks systemic across TextRange construction rather than a single bad line.

Workaround

| 0 restores integer classification (the remedy in #251), applied both where a derived index is produced and at the slot it lands in. I hand-rolled an equivalent caret/selection editor that compiles under those rules, so the constraint is satisfiable in app code - it just isn't satisfied in the shipped module.

Impact

Without this, a TypeScript core cannot use the SDK's own text engine, so text fields are limited to whatever the app hand-rolls. In my case that meant append/backspace-only editing until I worked out the | 0 pattern - a user could not delete a word from the start of a field.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the TypeScript core example with native build, then inspect sdk/text.ts starting at rangeNormalized around line 79 and the reported TextRange constructions at lines 243, 244, 325, 355, 375, 394, 414, 439, and 459. Confirm the fix across the full text engine by rebuilding the repro and verifying that the integer-proof errors no longer occur.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript, zig
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.