Fix diff
Some checks failed
Check / build (pull_request) Failing after 3m57s

This commit is contained in:
Andras Schmelczer 2026-05-31 19:53:33 +01:00
parent 3416264d6b
commit 2623959c10
5 changed files with 72 additions and 7 deletions

View file

@ -84,8 +84,8 @@ See the [example website source](examples/website/src/index.ts) for a more compl
React Native's default engine, Hermes, does not expose a runtime `WebAssembly`
global, so the WebAssembly build cannot run there. For React Native, the package
ships a [`wasm2js`](https://github.com/WebAssembly/binaryen) (pure-JavaScript)
build via its `react-native` entry point.
ships a pure-JavaScript build produced by [Binaryen's `wasm2js`](https://github.com/WebAssembly/binaryen)
via its `react-native` entry point.
### Python

View file

@ -33,7 +33,7 @@
],
"scripts": {
"build": "node scripts/build-rn.mjs && webpack --mode production",
"format": "prettier --write \"./**/*.(ts|scss|json|html)\"",
"format": "prettier --write \"./**/*.(ts|mjs|scss|json|html)\"",
"test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest"
},
"devDependencies": {

View file

@ -27,7 +27,7 @@ export interface WasmBackend {
ensureReady(): void;
}
// Define the enum values as const arrays to avoid duplication
// Define the enum values as a const array to avoid duplication
const BUILTIN_TOKENIZERS = ['Character', 'Line', 'Markdown', 'Word'] as const;
/**
@ -148,7 +148,8 @@ export interface ReconcileApi {
* @param left - The left version of the text (either string or TextWithCursors with cursor positions)
* @param right - The right version of the text (either string or TextWithCursors with cursor positions)
* @param tokenizer - The tokenisation strategy: "Word" (default, recommended for prose),
* "Character" (fine-grained), or "Line" (similar to git merge)
* "Character" (fine-grained), "Line" (similar to git merge), or
* "Markdown" (splits on Markdown structure)
* @returns The reconciled text with automatically repositioned cursor positions
*
* @example
@ -219,7 +220,8 @@ export interface ReconcileApi {
* @param left - The left version of the text (either string or TextWithCursors with cursor positions)
* @param right - The right version of the text (either string or TextWithCursors with cursor positions)
* @param tokenizer - The tokenisation strategy: "Word" (default, recommended for prose),
* "Character" (fine-grained), or "Line" (similar to git merge)
* "Character" (fine-grained), "Line" (similar to git merge), or
* "Markdown" (splits on Markdown structure)
* @returns The reconciled text with cursor positions and detailed change history
*
* @example
@ -351,7 +353,15 @@ export function makeReconcileApi(backend: WasmBackend): ReconcileApi {
backend.ensureReady();
assertTokenizer(tokenizer);
return backend.undiff(original, diffValue, tokenizer);
// The real-WebAssembly backend's `diff` emits BigInt spans, whereas the
// wasm2js (React Native) backend rejects BigInt outright. Normalise to
// plain numbers - exactly as `diff` does on the way out - so a `diff`
// result round-trips through `undiff` identically on every platform.
return backend.undiff(
original,
diffValue.map((item) => (typeof item === 'bigint' ? Number(item) : item)),
tokenizer
);
}
function reconcileWithHistory(

View file

@ -60,6 +60,46 @@ describe('reconcile (wasm2js / React Native build)', () => {
expect(result.text).toEqual('Hi world');
expect(result.history.length).toBeGreaterThan(0);
});
it('undiff accepts bigint entries (per the Array<number | bigint | string> type)', () => {
const original = 'Hello world';
const changed = 'Hello cruel world';
// `diff` returns plain numbers; emulate a caller that supplies BigInt, which
// the public signature permits. The wasm2js (React Native) build rejects raw
// BigInt, so the wrapper must normalise it to keep both backends identical.
const withBigints = diff(original, changed).map((item) =>
typeof item === 'number' ? BigInt(item) : item
);
expect(withBigints.some((item) => typeof item === 'bigint')).toBe(true);
expect(undiff(original, withBigints)).toEqual(changed);
});
it('runs every operation with no WebAssembly global (Hermes parity)', () => {
// Hermes exposes no `WebAssembly` global - the entire reason the React
// Native entry point links a wasm2js (pure-JS) build instead of the real
// module. Remove the global to prove the operations never reach for it.
const descriptor = Object.getOwnPropertyDescriptor(globalThis, 'WebAssembly');
delete (globalThis as { WebAssembly?: unknown }).WebAssembly;
try {
expect((globalThis as { WebAssembly?: unknown }).WebAssembly).toBeUndefined();
expect(reconcile('Hello', 'Hello world', 'Hi world').text).toEqual('Hi world');
const changes = diff('Hello world', 'Hello cruel world');
expect(undiff('Hello world', changes)).toEqual('Hello cruel world');
expect(
reconcileWithHistory('Hello', 'Hello world', 'Hi world').history.length
).toBeGreaterThan(0);
} finally {
// Restore the global so the leak check and later suites are unaffected.
if (descriptor) {
Object.defineProperty(globalThis, 'WebAssembly', descriptor);
}
}
});
});
describe('test_diff_and_undiff_are_inverse (wasm2js / React Native build)', () => {

View file

@ -60,6 +60,21 @@ describe('reconcile', () => {
expect(result.text).toEqual('Hi world');
expect(result.history.length).toBeGreaterThan(0);
});
it('undiff accepts bigint entries (per the Array<number | bigint | string> type)', () => {
const original = 'Hello world';
const changed = 'Hello cruel world';
// `diff` returns plain numbers; emulate a caller that supplies BigInt, which
// the public signature permits. The wasm2js (React Native) build rejects raw
// BigInt, so the wrapper must normalise it to keep both backends identical.
const withBigints = diff(original, changed).map((item) =>
typeof item === 'number' ? BigInt(item) : item
);
expect(withBigints.some((item) => typeof item === 'bigint')).toBe(true);
expect(undiff(original, withBigints)).toEqual(changed);
});
});
describe('test_diff_and_undiff_are_inverse', () => {