.
This commit is contained in:
parent
909096969a
commit
f87ad0d786
4 changed files with 50 additions and 14 deletions
|
|
@ -20,7 +20,7 @@
|
||||||
"fs-extra": "^11.3.2",
|
"fs-extra": "^11.3.2",
|
||||||
"mini-css-extract-plugin": "^2.9.4",
|
"mini-css-extract-plugin": "^2.9.4",
|
||||||
"obsidian": "1.11.0",
|
"obsidian": "1.11.0",
|
||||||
"reconcile-text": "^0.11.0",
|
"reconcile-text": "^0.12.1",
|
||||||
"resolve-url-loader": "^5.0.0",
|
"resolve-url-loader": "^5.0.0",
|
||||||
"sass": "^1.96.0",
|
"sass": "^1.96.0",
|
||||||
"sass-loader": "^16.0.6",
|
"sass-loader": "^16.0.6",
|
||||||
|
|
|
||||||
17
frontend/package-lock.json
generated
17
frontend/package-lock.json
generated
|
|
@ -5954,9 +5954,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/reconcile-text": {
|
"node_modules/reconcile-text": {
|
||||||
"version": "0.8.0",
|
"version": "0.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/reconcile-text/-/reconcile-text-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/reconcile-text/-/reconcile-text-0.12.1.tgz",
|
||||||
"integrity": "sha512-evskVha3YgpP2ZelsFxP9t7CuKnwE7TrsH3FdrH2mfKbzjUWiNF7scHXsFbFS921lmFlAOB94DHNAWPvL34Mqg==",
|
"integrity": "sha512-UtDykRp8k/hDhMMqcexkeCmGCFy/ScrKFYkIj2lpT7OYEN5eXL6BeUORgf9jPZ+sMf6/z5tZWSOC1Q87jNzhUQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
|
@ -7686,7 +7686,7 @@
|
||||||
"fs-extra": "^11.3.2",
|
"fs-extra": "^11.3.2",
|
||||||
"mini-css-extract-plugin": "^2.9.4",
|
"mini-css-extract-plugin": "^2.9.4",
|
||||||
"obsidian": "1.11.0",
|
"obsidian": "1.11.0",
|
||||||
"reconcile-text": "^0.11.0",
|
"reconcile-text": "^0.12.1",
|
||||||
"resolve-url-loader": "^5.0.0",
|
"resolve-url-loader": "^5.0.0",
|
||||||
"sass": "^1.96.0",
|
"sass": "^1.96.0",
|
||||||
"sass-loader": "^16.0.6",
|
"sass-loader": "^16.0.6",
|
||||||
|
|
@ -7740,13 +7740,6 @@
|
||||||
"@codemirror/view": "6.38.6"
|
"@codemirror/view": "6.38.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"obsidian-plugin/node_modules/reconcile-text": {
|
|
||||||
"version": "0.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/reconcile-text/-/reconcile-text-0.11.0.tgz",
|
|
||||||
"integrity": "sha512-a3sy3obazoc1BMEHx6IQn8ESZKnakVWZuRLi7OSEB56E8evRtrXBMj7yuo10fMoG4JkcZC6tokOfzpwZAKX+PQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"obsidian-plugin/node_modules/typescript": {
|
"obsidian-plugin/node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
|
@ -7775,7 +7768,7 @@
|
||||||
"@types/node": "^24.8.1",
|
"@types/node": "^24.8.1",
|
||||||
"byte-base64": "^1.1.0",
|
"byte-base64": "^1.1.0",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
"reconcile-text": "^0.8.0",
|
"reconcile-text": "^0.12.1",
|
||||||
"ts-loader": "^9.5.2",
|
"ts-loader": "^9.5.2",
|
||||||
"tslib": "2.8.1",
|
"tslib": "2.8.1",
|
||||||
"tsx": "^4.20.6",
|
"tsx": "^4.20.6",
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"byte-base64": "^1.1.0",
|
"byte-base64": "^1.1.0",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
"reconcile-text": "^0.8.0",
|
"reconcile-text": "^0.12.1",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
"@types/node": "^24.8.1",
|
"@types/node": "^24.8.1",
|
||||||
"ts-loader": "^9.5.2",
|
"ts-loader": "^9.5.2",
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,49 @@ test("content edited after planning is merged with the incoming snapshot", async
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("repeated large text replacements reuse WebAssembly memory", async () => {
|
||||||
|
// The replacement from fuzz-193329 exhausted reconcile-text 0.8.0's
|
||||||
|
// allocator over repeated merges, eventually stalling sync and trapping.
|
||||||
|
const original = (
|
||||||
|
" مرحباéשלום\t日本語\ncaféalphaalphaनमस्ते🦊🌍café\n" +
|
||||||
|
"🌍مرحبامرحباनमस्तेשלום\talphaalphacaféمرحبا🌍\r\n日本語مرحبا"
|
||||||
|
).repeat(128);
|
||||||
|
const replacement = await hashSnapshot({
|
||||||
|
content: bytes(
|
||||||
|
(
|
||||||
|
"alpha🌍éनमस्ते日本語שלום\uFEFFשלום\t\r\n" +
|
||||||
|
"\uFEFF🦊 🦊🌍नमस्ते\uFEFFéمرحبا\r\ncafé🌍é🌍"
|
||||||
|
).repeat(128)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
const f = await fixture({ "a.md": original });
|
||||||
|
const expected = (await f.files.readSnapshot("a.md"))!;
|
||||||
|
const apply = async () => {
|
||||||
|
await f.disk.userWrite("a.md", bytes(original));
|
||||||
|
await f.files.applyChanges(structuredClone(f.database.state), {
|
||||||
|
a: { expected, replacement }
|
||||||
|
});
|
||||||
|
assert.deepEqual(f.disk.userFiles().get("a.md"), replacement.content);
|
||||||
|
assert.equal(
|
||||||
|
f.database.state.documents.a.observedHash,
|
||||||
|
replacement.hash
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
await apply(); // Warm up the module and its allocator before measuring.
|
||||||
|
const warmedMemory = process.memoryUsage.rss();
|
||||||
|
for (let repeat = 0; repeat < 4; repeat++) {
|
||||||
|
await apply();
|
||||||
|
// Measure resident memory because V8's external counter can omit WASM
|
||||||
|
// memory after GC. The old allocator grew ~150 MiB on every call.
|
||||||
|
const growth = process.memoryUsage.rss() - warmedMemory;
|
||||||
|
assert(
|
||||||
|
growth < 64 * 1024 * 1024,
|
||||||
|
`Resident memory grew by ${growth} bytes`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("deleting the source after planning does not resurrect it", async () => {
|
test("deleting the source after planning does not resurrect it", async () => {
|
||||||
const f = await fixture();
|
const f = await fixture();
|
||||||
const next = structuredClone(f.database.state);
|
const next = structuredClone(f.database.state);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue