Fix TS typecheck of bindings

This commit is contained in:
Andras Schmelczer 2026-10-27 20:48:58 +00:00
commit 253f0b95b3
3 changed files with 62 additions and 22 deletions

View file

@ -7,19 +7,63 @@
// implementation, since Hermes does not expose a runtime // implementation, since Hermes does not expose a runtime
// `WebAssembly` global. See `scripts/build-rn.mjs`. // `WebAssembly` global. See `scripts/build-rn.mjs`.
type WasmModule = typeof import('reconcile-text'); // Keep these structural types local: the raw wasm-bindgen package and this
// wrapper share a name, so an import of 'reconcile-text' in emitted declarations
// would resolve to the wrapper's public API in a consumer.
interface WasmCursorPosition {
id(): number;
characterIndex(): number;
free(): void;
}
interface WasmTextWithCursors {
text(): string;
cursors(): WasmCursorPosition[];
free(): void;
}
interface WasmSpanWithHistory {
text(): string;
history(): History;
free(): void;
}
interface WasmTextWithCursorsAndHistory extends WasmTextWithCursors {
history(): WasmSpanWithHistory[];
}
/** /**
* The generated wasm-bindgen surface this library wraps, plus a hook to make * The generated wasm-bindgen surface this library wraps, plus a hook to make
* sure the underlying module is ready. Supplied by a platform entrypoint. * sure the underlying module is ready. Supplied by a platform entrypoint.
*/ */
export interface WasmBackend { export interface WasmBackend<
CursorPosition: WasmModule['CursorPosition']; Cursor extends WasmCursorPosition,
TextWithCursors: WasmModule['TextWithCursors']; Text extends WasmTextWithCursors,
reconcile: WasmModule['reconcile']; > {
reconcileWithHistory: WasmModule['reconcileWithHistory']; CursorPosition: new (id: number, position: number) => Cursor;
diff: WasmModule['diff']; TextWithCursors: new (text: string, cursors: Cursor[]) => Text;
undiff: WasmModule['undiff']; reconcile(
original: string,
left: Text,
right: Text,
tokenizer: BuiltinTokenizer
): WasmTextWithCursors;
reconcileWithHistory(
original: string,
left: Text,
right: Text,
tokenizer: BuiltinTokenizer
): WasmTextWithCursorsAndHistory;
diff(
original: string,
changed: Text,
tokenizer: BuiltinTokenizer
): Array<number | bigint | string>;
undiff(
original: string,
changes: Array<number | bigint | string>,
tokenizer: BuiltinTokenizer
): string;
/** /**
* Make the wasm module ready for use. Invoked before every operation, so it * Make the wasm module ready for use. Invoked before every operation, so it
* must be cheap and idempotent (a no-op once initialised). * must be cheap and idempotent (a no-op once initialised).
@ -255,7 +299,10 @@ const UNSUPPORTED_TOKENIZER_ERROR = `Unsupported tokenizer, only ${BUILTIN_TOKEN
* objects. The behaviour is identical regardless of whether the backend is a * objects. The behaviour is identical regardless of whether the backend is a
* real WebAssembly module or its wasm2js translation. * real WebAssembly module or its wasm2js translation.
*/ */
export function makeReconcileApi(backend: WasmBackend): ReconcileApi { export function makeReconcileApi<
Cursor extends WasmCursorPosition,
Text extends WasmTextWithCursors,
>(backend: WasmBackend<Cursor, Text>): ReconcileApi {
function assertTokenizer(tokenizer: BuiltinTokenizer): void { function assertTokenizer(tokenizer: BuiltinTokenizer): void {
if (!BUILTIN_TOKENIZERS.includes(tokenizer)) { if (!BUILTIN_TOKENIZERS.includes(tokenizer)) {
throw new Error(UNSUPPORTED_TOKENIZER_ERROR); throw new Error(UNSUPPORTED_TOKENIZER_ERROR);
@ -273,10 +320,7 @@ export function makeReconcileApi(backend: WasmBackend): ReconcileApi {
); );
} }
function toTextWithCursors(textWithCursor: { function toTextWithCursors(textWithCursor: WasmTextWithCursors): TextWithCursors {
text(): string;
cursors(): Array<{ id(): number; characterIndex(): number; free(): void }>;
}): TextWithCursors {
const wasmCursors = textWithCursor.cursors(); const wasmCursors = textWithCursor.cursors();
const cursors = wasmCursors.map((cursor) => ({ const cursors = wasmCursors.map((cursor) => ({
id: cursor.id(), id: cursor.id(),
@ -292,11 +336,7 @@ export function makeReconcileApi(backend: WasmBackend): ReconcileApi {
}; };
} }
function toSpanWithHistory(span: { function toSpanWithHistory(span: WasmSpanWithHistory): SpanWithHistory {
text(): string;
history(): History;
free(): void;
}): SpanWithHistory {
const result = { const result = {
text: span.text(), text: span.text(),
history: span.history(), history: span.history(),

View file

@ -20,9 +20,9 @@ import {
undiff as wasmUndiff, undiff as wasmUndiff,
} from '../pkg-rn/reconcile_text.js'; } from '../pkg-rn/reconcile_text.js';
import { makeReconcileApi, type WasmBackend } from './core'; import { makeReconcileApi } from './core';
const backend: WasmBackend = { const backend = {
CursorPosition: wasmCursorPosition, CursorPosition: wasmCursorPosition,
TextWithCursors: wasmTextWithCursors, TextWithCursors: wasmTextWithCursors,
reconcile: wasmReconcile, reconcile: wasmReconcile,

View file

@ -10,11 +10,11 @@ import {
import wasmBytes from 'reconcile-text/reconcile_text_bg.wasm'; import wasmBytes from 'reconcile-text/reconcile_text_bg.wasm';
import { makeReconcileApi, type WasmBackend } from './core'; import { makeReconcileApi } from './core';
let isInitialised = false; let isInitialised = false;
const backend: WasmBackend = { const backend = {
CursorPosition: wasmCursorPosition, CursorPosition: wasmCursorPosition,
TextWithCursors: wasmTextWithCursors, TextWithCursors: wasmTextWithCursors,
reconcile: wasmReconcile, reconcile: wasmReconcile,