Always init

This commit is contained in:
Andras Schmelczer 2025-07-07 22:30:33 +01:00
parent c38b7c9972
commit 70ef5cc0de
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
4 changed files with 20 additions and 59 deletions

View file

@ -59,9 +59,6 @@ Then use in your application:
```javascript
import { init, reconcile } from 'reconcile';
// One-time setup: initialise the WASM module
await init();
// Same example as above
const parent = 'Hello world';
const left = 'Hello beautiful world';

View file

@ -1,4 +1,4 @@
import { init, reconcileWithHistory } from 'reconcile';
import { reconcileWithHistory } from 'reconcile';
import type { Tokenizer } from 'reconcile';
import './style.scss';
@ -13,8 +13,6 @@ const tokenizerRadios = document.querySelectorAll(
const sampleText = `The \`reconcile\` Rust library is embedded on this page as a WASM module and powers these text boxes. Experiment with changing the "Original", "First concurrent edit", and "Second concurrent edit" text boxes to see competing changes get merged in real-time within the "Deconflicted result" box. Here, you will see color-coded tokens marking the origin of each token, including ones that got deleted. The result highly depends on the tokenization strategy, for example, deciding how casing or whitespace is taken into account.`;
async function main(): Promise<void> {
await init();
originalTextArea.addEventListener('input', updateMergedText);
leftTextArea.addEventListener('input', updateMergedText);
rightTextArea.addEventListener('input', updateMergedText);

View file

@ -1,24 +1,11 @@
import { init, reconcile, reconcileWithHistory } from './index';
import * as fs from 'fs';
import { reconcile, reconcileWithHistory } from './index';
describe('reconcile', () => {
it('tries calling functions without init', () => {
expect(() => reconcile('Hello', 'Hello world', 'Hi world')).toThrow(/call init()/);
expect(() => reconcileWithHistory('Hello', 'Hello world', 'Hi world')).toThrow(
/call init()/
);
});
it('call reconcile without cursors', async () => {
await initWasm();
it('call reconcile without cursors', () => {
expect(reconcile('Hello', 'Hello world', 'Hi world').text).toEqual('Hi world');
});
it('call reconcile with cursors', async () => {
await initWasm();
it('call reconcile with cursors', () => {
const result = reconcile(
'Hello',
{
@ -50,17 +37,10 @@ describe('reconcile', () => {
]);
});
it('call reconcileWithHistory', async () => {
await initWasm();
it('call reconcileWithHistory', () => {
const result = reconcileWithHistory('Hello', 'Hello world', 'Hi world');
expect(result.text).toEqual('Hi world');
expect(result.history.length).toBeGreaterThan(0);
});
});
async function initWasm() {
const wasmBin = fs.readFileSync('../pkg/reconcile_bg.wasm');
await init({ module_or_path: wasmBin });
}

View file

@ -6,9 +6,11 @@ import wasmInit, {
BuiltinTokenizer,
reconcileWithHistory as wasmReconcileWithHistory,
History,
InitInput,
initSync,
} from 'reconcile';
import wasm from 'reconcile/reconcile_bg.wasm';
export interface TextWithCursors {
/** The document's entire content */
text: string;
@ -44,32 +46,10 @@ const TOKENIZERS = ['Line', 'Word', 'Character'];
let isInitialised = false;
const UNINITIALISED_MODULE_ERROR =
'Reconcile module has not been initialized. Please call init() before using any other functions.';
const UNSUPPORTED_TOKENIZER_ERROR = `Unsupported tokenizer. Only ${TOKENIZERS.join(
', '
)} are supported.`;
/**
* Initializes the WASM module for text reconciliation.
* Must be called before using any other functions.
*
* The function is idempotent.
*
* @param content - Optional initialization input for the WASM module during testing.
* @returns Promise that resolves when initialization is complete
*/
export async function init(content?: InitInput) {
if (isInitialised) {
return;
}
await wasmInit(content);
isInitialised = true;
}
/**
* Merges three versions of text (original, left, right) and cursor positions.
*
@ -85,9 +65,7 @@ export function reconcile(
right: string | TextWithCursors,
tokenizer: BuiltinTokenizer = 'Word'
): TextWithCursors {
if (!isInitialised) {
throw new Error(UNINITIALISED_MODULE_ERROR);
}
init();
if (!TOKENIZERS.includes(tokenizer)) {
throw new Error(UNSUPPORTED_TOKENIZER_ERROR);
@ -124,9 +102,7 @@ export function reconcileWithHistory(
right: string | TextWithCursors,
tokenizer: BuiltinTokenizer = 'Word'
): TextWithCursorsAndHistory {
if (!isInitialised) {
throw new Error(UNINITIALISED_MODULE_ERROR);
}
init();
if (!TOKENIZERS.includes(tokenizer)) {
throw new Error(UNSUPPORTED_TOKENIZER_ERROR);
@ -150,6 +126,16 @@ export function reconcileWithHistory(
};
}
function init() {
if (isInitialised) {
return;
}
initSync({ module: (wasm as any).default });
isInitialised = true;
}
function toWasmTextWithCursors(text: string | TextWithCursors): wasmTextWithCursors {
const isInputString = typeof text == 'string';
const leftText = isInputString ? text : text.text;