vercel-labs / vercel-labs/native
@native-sdk/core/text fails the subset checker's integer proofs once TextEditState is a Model field
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
TextEditStateis a Model field. CallingapplyTextInputEventand storing onlynext.textbuilds fine - the integer slots are only instantiated once the record becomes model state. - Fixing
rangeNormalizedalone is not sufficient. Replacing thatMath.minpair with| 0-pinned ternaries clears all fourSC4023errors at line 79, but the nineSC4022sites remain. This looks systemic acrossTextRangeconstruction 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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