refactor(pull): extract tested vault-layout module; harden pull; close review findings

Address the Increment-1 code review (3 warnings + suggestions).

- layout: new pure src/layout.ts (buildVaultLayout) — page-tree -> vault paths,
  sibling + full-path collision disambiguation (sanitized ~slugId suffix), parent
  cycle guard; pull.ts is now a thin I/O loop
- layout: resolve orphan/root collisions at the NAME stage so an orphan ancestor
  can't desync its children's folder segments (fixes review Major); covered by test
- pull: per-page try/catch (one bad page no longer aborts the mirror), bounded
  concurrency (6), progress logging, process.exitCode=1 on partial mirror
- security: filename disambiguation suffix now passes through sanitizeTitle
- docs: AGENTS.md -> Increment 1 status/structure/run targets; pull.ts meta-block
  comment; collectRecentSince JSDoc (lexicographic UTC-ISO precondition)
- tests: layout (9), markdown-document round-trip (no comments block, SPEC §3),
  firstDivergence; export firstDivergence. 49 tests green.
This commit is contained in:
vvzvlad
2026-06-16 21:09:40 +03:00
parent 447d2508ae
commit c6edd73324
8 changed files with 511 additions and 87 deletions

40
test/divergence.test.ts Normal file
View File

@@ -0,0 +1,40 @@
import { describe, expect, it } from 'vitest';
import { firstDivergence } from '../src/roundtrip.js';
describe('firstDivergence', () => {
it('returns null for equal nested objects', () => {
const a = { k1: { k2: 1, k3: [1, 2, 3] }, n: 'x' };
const b = { k1: { k2: 1, k3: [1, 2, 3] }, n: 'x' };
expect(firstDivergence(a, b)).toBeNull();
});
it('reports the correct path for a differing leaf', () => {
const a = { k1: { k2: 1 } };
const b = { k1: { k2: 2 } };
const d = firstDivergence(a, b);
expect(d).not.toBeNull();
expect(d!.path).toBe('$.k1.k2');
expect(d!.a).toBe(1);
expect(d!.b).toBe(2);
});
it('reports an array length mismatch at $.arr.length', () => {
const a = { arr: [1, 2, 3] };
const b = { arr: [1, 2] };
const d = firstDivergence(a, b);
expect(d).not.toBeNull();
expect(d!.path).toBe('$.arr.length');
expect(d!.a).toBe(3);
expect(d!.b).toBe(2);
});
it('reports a key present only in a', () => {
const a = { only: 'here', shared: 1 };
const b = { shared: 1 };
const d = firstDivergence(a, b);
expect(d).not.toBeNull();
expect(d!.path).toBe('$.only');
expect(d!.a).toBe('here');
expect(d!.b).toBeUndefined();
});
});