// node modules/test/test-virtual-fs-require-leaf.js // Proves the 2026-11-09 virtual-fs patch: phantom deletes are refused (_requireLeaf) and // checkpoints stop cloning _mods / .semantic_index (while rollback keeps the live copies). // Offline: no browser, no real localStorage, no IndexedDB, no timers. const fsMod = require('fs'), path = require('path'); const ROOT = path.join(__dirname, '..', 'core'); // ── persistence stub: in-memory localStorage, IndexedDB that refuses to exist ── const ls = {}; global.localStorage = { getItem: k => (k in ls ? ls[k] : null), setItem: (k, v) => { ls[k] = String(v); }, removeItem: k => { delete ls[k]; } }; global.indexedDB = { open() { throw new Error('no idb in tests'); }, deleteDatabase() { throw new Error('no idb in tests'); } }; global.window = global; // The file ends with `window.VirtualFileSystem = VirtualFileSystem;` — so the class is a // window global *and* is in scope at the bottom of the script. Return the real class object. function load(file, cls) { const src = fsMod.readFileSync(path.join(ROOT, file), 'utf8'); return new Function(src + '\nreturn ' + cls + ';')(); } const VFS = load('virtual-fs.js', 'VirtualFileSystem'); let fails = 0, passes = 0; function ok(name, cond, detail) { if (cond) { passes++; return; } fails++; console.log('FAIL ' + name + (detail ? ' — ' + detail : '')); } function mk() { const vfs = new VFS(); vfs.saveToStorage = () => true; // keep the 2s setTimeout saver out of the test vfs.root = { type: 'dir', content: {} }; vfs.currentPath = []; if (vfs._ready && typeof vfs._ready.catch === 'function') vfs._ready.catch(() => {}); return vfs; } const keysOf = node => Object.keys(node.content).sort(); // ── 0. export shape ──────────────────────────────────────────────────────────── ok('0 export shape: window.VirtualFileSystem is the class', typeof VFS === 'function' && global.window.VirtualFileSystem === VFS); ok('0 constructor name', VFS.name === 'VirtualFileSystem', VFS.name); ok('0 has _requireLeaf guard', typeof VFS.prototype._requireLeaf === 'function'); // ── A. deleteFile of a missing leaf must be refused, sibling untouched ───────── { const vfs = mk(); vfs.mkdirRecursive('/docs'); vfs.writeFile('/docs/real.txt', 'keep me'); ok('A1 deleteFile("/docs/ghost.txt") returns false', vfs.deleteFile('/docs/ghost.txt') === false); ok('A2 sibling survived', vfs.readFile('/docs/real.txt') === 'keep me'); ok('A3 nothing was trashed', !vfs.root.content['.trash'] || keysOf(vfs.root.content['.trash']).length === 0); ok('A4 deleteFile("/no/such/dir/x.txt") returns false', vfs.deleteFile('/no/such/dir/x.txt') === false); // ── B. deleteFile of a real file removes exactly that file ──────────────── vfs.writeFile('/docs/other.txt', 'other'); ok('B1 deleteFile of a real file returns true', vfs.deleteFile('/docs/real.txt') === true); ok('B2 the file is gone', vfs.readFile('/docs/real.txt') === null); ok('B3 sibling intact', vfs.readFile('/docs/other.txt') === 'other'); ok('B4 only that entry was removed', JSON.stringify(keysOf(vfs.root.content.docs)) === '["other.txt"]', JSON.stringify(keysOf(vfs.root.content.docs))); } // ── C. deleteCheckpoint of a nonexistent checkpoint is refused ──────────────── { const vfs = mk(); vfs.mkdirRecursive('/_snapshots'); vfs.writeFile('/_snapshots/checkpoint-keep.json', JSON.stringify({ createdAt: '2026-01-01T00:00:00.000Z' })); ok('C1 deleteCheckpoint("nope") is falsy', !vfs.deleteCheckpoint('nope')); ok('C2 _snapshots unchanged', JSON.stringify(keysOf(vfs.root.content['_snapshots'])) === '["checkpoint-keep.json"]', JSON.stringify(keysOf(vfs.root.content['_snapshots']))); ok('C3 existing checkpoint still readable', !!vfs.fsReadJSON('/_snapshots/checkpoint-keep.json')); ok('C4 real checkpoint delete still returns true', vfs.deleteCheckpoint('keep') === true); ok('C5 it was actually removed', keysOf(vfs.root.content['_snapshots']).length === 0); } // ── D. a checkpoint must not carry _mods or .semantic_index ─────────────────── { const vfs = mk(); vfs.mkdirRecursive('/_mods'); vfs.writeFile('/_mods/mod-a.js', 'MOD-ORIGINAL'); vfs.mkdirRecursive('/.semantic_index'); vfs.writeFile('/.semantic_index/idx.json', '{"nodes":1}'); vfs.writeFile('/notes.txt', 'plain'); const name = vfs.checkpoint('excl'); const snap = vfs.fsReadJSON('/_snapshots/checkpoint-' + name + '.json'); ok('D1 checkpoint written', !!snap && !!snap.root); ok('D2 _mods absent from the snapshot root', !('_mods' in snap.root.content), JSON.stringify(Object.keys(snap.root.content))); ok('D3 .semantic_index absent from the snapshot root', !('.semantic_index' in snap.root.content)); ok('D4 ordinary files are still snapshotted', snap.root.content['notes.txt'] && snap.root.content['notes.txt'].content === 'plain'); ok('D5 scaffolding still excluded (_snapshots/_sessions/.trash)', !('_snapshots' in snap.root.content) && !('_sessions' in snap.root.content) && !('.trash' in snap.root.content)); // ── E. rollback keeps the LIVE _mods / .semantic_index ──────────────────── vfs.writeFile('/notes.txt', 'edited after checkpoint'); ok('E0 pre-rollback edit in place', vfs.readFile('/notes.txt') === 'edited after checkpoint'); ok('E1 rollback returns true', vfs.rollback(name) === true); ok('E2 live _mods survives rollback', vfs.readFile('/_mods/mod-a.js') === 'MOD-ORIGINAL', String(vfs.readFile('/_mods/mod-a.js'))); ok('E3 live .semantic_index survives rollback', vfs.readFile('/.semantic_index/idx.json') === '{"nodes":1}'); ok('E4 ordinary file rolled back to the snapshot content', vfs.readFile('/notes.txt') === 'plain', String(vfs.readFile('/notes.txt'))); ok('E5 snapshot bookkeeping survived rollback', keysOf(vfs.root.content['_snapshots']).indexOf('checkpoint-' + name + '.json') !== -1); } // ── F. a legacy checkpoint that *does* embed _mods must not clobber the live one ── { const vfs = mk(); vfs.mkdirRecursive('/_mods'); vfs.writeFile('/_mods/live.js', 'LIVE'); vfs.mkdirRecursive('/.semantic_index'); vfs.writeFile('/.semantic_index/live.json', '{"live":true}'); vfs.writeFile('/f.txt', 'v1'); const name = vfs.checkpoint('legacy'); const p = '/_snapshots/checkpoint-' + name + '.json'; const snap = vfs.fsReadJSON(p); // simulate a pre-patch 4MB snapshot snap.root.content['_mods'] = { type: 'dir', content: { 'stale.js': { type: 'file', content: 'STALE' } } }; snap.root.content['.semantic_index'] = { type: 'dir', content: { 'stale.json': { type: 'file', content: 'STALE' } } }; vfs.writeFile(p, JSON.stringify(snap)); ok('F1 rollback of a legacy snapshot succeeds', vfs.rollback(name) === true); ok('F2 live _mods wins over the stale copy', vfs.readFile('/_mods/live.js') === 'LIVE' && vfs.readFile('/_mods/stale.js') === null); ok('F3 live .semantic_index wins over the stale copy', vfs.readFile('/.semantic_index/live.json') === '{"live":true}' && vfs.readFile('/.semantic_index/stale.json') === null); } // ── G. plain checkpoint/restore round trip is unbroken ─────────────────────── { const vfs = mk(); vfs.mkdirRecursive('/work'); vfs.writeFile('/work/a.txt', 'version-1'); vfs.writeFile('/work/b.txt', 'b-1'); const name = vfs.checkpoint('roundtrip'); vfs.writeFile('/work/a.txt', 'version-2'); vfs.deleteFile('/work/b.txt'); vfs.writeFile('/work/new.txt', 'added-later'); ok('G1 rollback returns true', vfs.rollback(name) === true); ok('G2 modified file restored', vfs.readFile('/work/a.txt') === 'version-1', String(vfs.readFile('/work/a.txt'))); ok('G3 deleted file restored', vfs.readFile('/work/b.txt') === 'b-1'); ok('G4 post-checkpoint file removed by the rollback', vfs.readFile('/work/new.txt') === null); } // ── H. measured saving on a realistic 2 MB _mods ──────────────────────────── { const vfs = mk(); vfs.mkdirRecursive('/_mods'); vfs.writeFile('/_mods/bundle.js', 'x'.repeat(2 * 1024 * 1024)); // 2 MiB module originals vfs.mkdirRecursive('/.semantic_index'); vfs.writeFile('/.semantic_index/idx.json', JSON.stringify({ nodes: 'y'.repeat(4096) })); vfs.writeFile('/doc.txt', 'hello'); const name = vfs.checkpoint('big'); const raw = vfs.readFile('/_snapshots/checkpoint-' + name + '.json'); const after = raw.length; // what the old code produced: the same snapshot with the two dirs cloned back in const snap = JSON.parse(raw); snap.root.content['_mods'] = JSON.parse(JSON.stringify(vfs.root.content['_mods'])); snap.root.content['.semantic_index'] = JSON.parse(JSON.stringify(vfs.root.content['.semantic_index'])); const before = JSON.stringify(snap).length; console.log('MEASURE checkpoint bytes before exclusion: ' + before.toLocaleString('en-US')); console.log('MEASURE checkpoint bytes after exclusion: ' + after.toLocaleString('en-US')); console.log('MEASURE saved: ' + (before - after).toLocaleString('en-US') + ' bytes (' + (100 * (before - after) / before).toFixed(1) + '%)'); ok('H1 excluded snapshot stays small (< 200 KB)', after < 200 * 1024, after + ' bytes'); ok('H2 the old shape would have carried the 2 MiB _mods', before > 2 * 1024 * 1024, before + ' bytes'); } if (fails === 0) { console.log('test-virtual-fs-require-leaf: ' + passes + ' passed, 0 failed'); } else { console.log('test-virtual-fs-require-leaf: ' + passes + ' passed, ' + fails + ' FAILED'); process.exit(1); }