@sentropic/focus 0.3.0 → 0.5.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +9 -0
  2. package/dist/cli/index.d.ts +1 -1
  3. package/dist/cli/index.d.ts.map +1 -1
  4. package/dist/cli/index.js +12 -3
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/editing/anchors.d.ts +14 -0
  7. package/dist/editing/anchors.d.ts.map +1 -0
  8. package/dist/editing/anchors.js +113 -0
  9. package/dist/editing/anchors.js.map +1 -0
  10. package/dist/editing/codec.d.ts +24 -0
  11. package/dist/editing/codec.d.ts.map +1 -0
  12. package/dist/editing/codec.js +143 -0
  13. package/dist/editing/codec.js.map +1 -0
  14. package/dist/editing/contract.d.ts +129 -0
  15. package/dist/editing/contract.d.ts.map +1 -0
  16. package/dist/editing/contract.js +76 -0
  17. package/dist/editing/contract.js.map +1 -0
  18. package/dist/editing/diff.d.ts +12 -0
  19. package/dist/editing/diff.d.ts.map +1 -0
  20. package/dist/editing/diff.js +125 -0
  21. package/dist/editing/diff.js.map +1 -0
  22. package/dist/editing/digest.d.ts +6 -0
  23. package/dist/editing/digest.d.ts.map +1 -0
  24. package/dist/editing/digest.js +56 -0
  25. package/dist/editing/digest.js.map +1 -0
  26. package/dist/editing/index.d.ts +7 -0
  27. package/dist/editing/index.d.ts.map +1 -0
  28. package/dist/editing/index.js +7 -0
  29. package/dist/editing/index.js.map +1 -0
  30. package/dist/editing/reconcile.d.ts +66 -0
  31. package/dist/editing/reconcile.d.ts.map +1 -0
  32. package/dist/editing/reconcile.js +167 -0
  33. package/dist/editing/reconcile.js.map +1 -0
  34. package/dist/editing/structure.d.ts +24 -0
  35. package/dist/editing/structure.d.ts.map +1 -0
  36. package/dist/editing/structure.js +71 -0
  37. package/dist/editing/structure.js.map +1 -0
  38. package/dist/index.d.ts +4 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/model.d.ts +144 -2
  43. package/dist/model.d.ts.map +1 -1
  44. package/dist/model.js +1 -1
  45. package/dist/model.js.map +1 -1
  46. package/dist/registry/definitions.d.ts +159 -0
  47. package/dist/registry/definitions.d.ts.map +1 -0
  48. package/dist/registry/definitions.js +509 -0
  49. package/dist/registry/definitions.js.map +1 -0
  50. package/dist/render/evidence.d.ts +10 -0
  51. package/dist/render/evidence.d.ts.map +1 -0
  52. package/dist/render/evidence.js +37 -0
  53. package/dist/render/evidence.js.map +1 -0
  54. package/dist/render/html.d.ts.map +1 -1
  55. package/dist/render/html.js +14 -1
  56. package/dist/render/html.js.map +1 -1
  57. package/dist/render/md.d.ts.map +1 -1
  58. package/dist/render/md.js +15 -1
  59. package/dist/render/md.js.map +1 -1
  60. package/dist/render/presentation.d.ts +4 -0
  61. package/dist/render/presentation.d.ts.map +1 -0
  62. package/dist/render/presentation.js +25 -0
  63. package/dist/render/presentation.js.map +1 -0
  64. package/dist/render/terminal.d.ts.map +1 -1
  65. package/dist/render/terminal.js +15 -1
  66. package/dist/render/terminal.js.map +1 -1
  67. package/dist/track/index.d.ts.map +1 -1
  68. package/dist/track/index.js +24 -10
  69. package/dist/track/index.js.map +1 -1
  70. package/dist/validation/evidence-bytes.d.ts +5 -0
  71. package/dist/validation/evidence-bytes.d.ts.map +1 -0
  72. package/dist/validation/evidence-bytes.js +57 -0
  73. package/dist/validation/evidence-bytes.js.map +1 -0
  74. package/dist/validation/evidence-shape.d.ts +12 -0
  75. package/dist/validation/evidence-shape.d.ts.map +1 -0
  76. package/dist/validation/evidence-shape.js +54 -0
  77. package/dist/validation/evidence-shape.js.map +1 -0
  78. package/dist/validation/evidence-sources.d.ts +7 -0
  79. package/dist/validation/evidence-sources.d.ts.map +1 -0
  80. package/dist/validation/evidence-sources.js +56 -0
  81. package/dist/validation/evidence-sources.js.map +1 -0
  82. package/dist/validation/evidence.d.ts +5 -0
  83. package/dist/validation/evidence.d.ts.map +1 -0
  84. package/dist/validation/evidence.js +131 -0
  85. package/dist/validation/evidence.js.map +1 -0
  86. package/dist/validation/index.d.ts +59 -0
  87. package/dist/validation/index.d.ts.map +1 -0
  88. package/dist/validation/index.js +500 -0
  89. package/dist/validation/index.js.map +1 -0
  90. package/dist/validation/readability.d.ts +28 -0
  91. package/dist/validation/readability.d.ts.map +1 -0
  92. package/dist/validation/readability.js +265 -0
  93. package/dist/validation/readability.js.map +1 -0
  94. package/package.json +36 -8
  95. package/registry/entries.v1.json +140 -0
  96. package/schemas/decision-dossier-document.v1.schema.json +193 -0
  97. package/schemas/decision-dossier-document.v2.schema.json +925 -0
  98. package/schemas/focus-edit-return.v1.schema.json +950 -0
  99. package/schemas/focus-editing-manifest.v1.schema.json +167 -0
  100. package/skills/focus/SKILL.md +181 -0
@@ -0,0 +1,76 @@
1
+ import { baseDigest, canonicalJson, returnDigest } from './digest.js';
2
+ import { validateManifest, fieldKey, sourceNodes } from './anchors.js';
3
+ import { matchesSchema, manifestSchema, returnSchema } from './structure.js';
4
+ export const validateManifestStructure = (v) => matchesSchema(manifestSchema, v);
5
+ export const validateReturnStructure = (v) => matchesSchema(returnSchema, v);
6
+ export function validateReturn(value, doc, manifest) {
7
+ const issues = [];
8
+ if (!validateReturnStructure(value))
9
+ return { ok: false, issues: ['unsupported-version-or-structure'] };
10
+ try {
11
+ if (value.digest !== returnDigest(value))
12
+ issues.push('return-digest');
13
+ if (!validateManifest(manifest, doc).ok || !validateManifest(value.manifest, doc).ok || canonicalJson(value.manifest) !== canonicalJson(manifest))
14
+ issues.push('manifest-mismatch');
15
+ if (value.source.documentRef !== doc.ref || value.source.hash !== doc.hash || value.source.cursor !== doc.cursor || value.source.baseHash !== baseDigest(doc) || value.source.manifestId !== manifest.manifestId || value.source.manifestBaseHash !== manifest.manifestBaseHash)
16
+ issues.push('source-mismatch');
17
+ const ancestry = value.editReturn.supersededReturnIds;
18
+ if (ancestry.includes(value.returnId) || (ancestry.length === 0) !== (value.editReturn.supersedesReturnId === undefined) || (ancestry.length && ancestry[0] !== value.editReturn.supersedesReturnId))
19
+ issues.push('ancestry');
20
+ const nodes = sourceNodes(doc), targets = new Set([doc.ref, ...nodes.map(n => n.targetRef)]);
21
+ for (const note of value.notes)
22
+ if (!targets.has(note.targetRef))
23
+ issues.push('note-target');
24
+ const answerIds = new Set();
25
+ for (const answer of value.answers) {
26
+ if (answerIds.has(answer.questionId))
27
+ issues.push('duplicate-answer');
28
+ answerIds.add(answer.questionId);
29
+ const question = nodes.find(n => n.kind === 'question' && n.id === answer.questionId && n.targetRef === answer.targetRef);
30
+ const options = doc.sections.flatMap(n => n.kind === 'optionSet' && n.questionId === answer.questionId ? n.options : []);
31
+ if (!question || answer.optionIds.some(id => !options.some(o => o.id === id)))
32
+ issues.push('answer-target');
33
+ }
34
+ const txIds = new Set(), changeIds = new Set();
35
+ for (const tx of value.editReturn.transactions) {
36
+ if (txIds.has(tx.transactionId))
37
+ issues.push('duplicate-transaction-id');
38
+ txIds.add(tx.transactionId);
39
+ for (const change of tx.changes) {
40
+ if (changeIds.has(change.changeId))
41
+ issues.push('duplicate-change-id');
42
+ changeIds.add(change.changeId);
43
+ const f = manifest.fields.find(f => fieldKey(f) === fieldKey(change));
44
+ if (!f || f.contentFormat !== change.format || change.documentRef !== doc.ref)
45
+ issues.push('address');
46
+ if (change.operation === 'move' && change.before !== change.after)
47
+ issues.push('move-content');
48
+ if (change.operation === 'insert' && (manifest.paragraphs.some(p => p.paragraphId === change.paragraphId) || manifest.retiredParagraphIds.includes(change.paragraphId)))
49
+ issues.push('reused-paragraph-id');
50
+ }
51
+ }
52
+ }
53
+ catch {
54
+ issues.push('invalid-json-value');
55
+ }
56
+ return issues.length ? { ok: false, issues } : { ok: true, value };
57
+ }
58
+ /** Capture metadata/identity is supplied by the host, never invented by the pure core. */
59
+ export function freezeReturn(input) {
60
+ const { document: doc, manifest, previous } = input;
61
+ if (previous && !validateReturn(previous, doc, manifest).ok)
62
+ throw new Error('Invalid previous frozen return: recover complete ancestry');
63
+ const value = { kind: 'focus.return', schemaVersion: 1, returnId: input.returnId, digest: '', capture: { at: input.capturedAt },
64
+ scope: input.scope, source: { documentRef: doc.ref, hash: doc.hash, cursor: doc.cursor, baseHash: manifest.baseHash, manifestId: manifest.manifestId, manifestBaseHash: manifest.manifestBaseHash },
65
+ production: input.production, manifest, answers: input.answers, notes: input.notes,
66
+ editReturn: { schemaVersion: 1, mode: 'cumulative', transactions: input.transactions.filter(t => t.changes.length), supersededReturnIds: previous ? [previous.returnId, ...previous.editReturn.supersededReturnIds] : [], ...(previous ? { supersedesReturnId: previous.returnId } : {}) } };
67
+ // Re-copy/download the same completed revision without allocating a new identity.
68
+ if (previous && canonicalJson([value.answers, value.notes, value.editReturn.transactions, value.production, value.scope]) === canonicalJson([previous.answers, previous.notes, previous.editReturn.transactions, previous.production, previous.scope]))
69
+ return structuredClone(previous);
70
+ value.digest = returnDigest(value);
71
+ const check = validateReturn(value, doc, manifest);
72
+ if (!check.ok)
73
+ throw new Error(check.issues.join(', '));
74
+ return structuredClone(value);
75
+ }
76
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/editing/contract.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAsB7E,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAU,EAA6B,EAAE,CAAC,aAAa,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;AACrH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAU,EAAoB,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;AACxG,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,GAA4B,EAAE,QAA8B;IACzG,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,kCAAkC,CAAC,EAAE,CAAC;IACxG,IAAI,CAAC;QACH,IAAI,KAAK,CAAC,MAAM,KAAK,YAAY,CAAC,KAAK,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACvE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,aAAa,CAAC,QAAQ,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACpL,IAAI,KAAK,CAAC,MAAM,CAAC,WAAW,KAAK,GAAG,CAAC,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,KAAK,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,gBAAgB,KAAK,QAAQ,CAAC,gBAAgB;YAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChT,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,mBAAmB,CAAC;QACtD,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,kBAAkB,KAAK,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,UAAU,CAAC,kBAAkB,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC9N,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC7F,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7F,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACxG,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,SAAS,KAAK,MAAM,CAAC,SAAS,CAAC,CAAC;YAC1H,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,UAAU,KAAK,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACzH,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC9G,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,EAAE,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/D,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,aAAa,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;YAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;YACtG,KAAK,MAAM,MAAM,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;gBAChC,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;oBAAE,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;gBAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACvG,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;gBACtE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,WAAW,KAAK,GAAG,CAAC,GAAG;oBAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtG,IAAI,MAAM,CAAC,SAAS,KAAK,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK;oBAAE,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC/F,IAAI,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,MAAM,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;oBAAE,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YAC9M,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAAC,CAAC;IAC9C,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACrE,CAAC;AAMD,0FAA0F;AAC1F,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACpD,IAAI,QAAQ,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC1I,MAAM,KAAK,GAAgB,EAAE,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,UAAU,EAAE;QAC1I,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,EAAE;QACnM,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK;QAClF,UAAU,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,mBAAmB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC/R,kFAAkF;IAClF,IAAI,QAAQ,IAAI,aAAa,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,YAAY,EAAE,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC;IACzR,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5G,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { DecisionDossierDocument } from '../model.js';
2
+ import type { Anchors, EditTransaction, FocusEditingManifest, ParagraphValue } from './contract.js';
3
+ export declare const paragraphAnchors: (values: ParagraphValue[], index: number) => Anchors;
4
+ export declare function diffParagraphs(baseline: ParagraphValue[], draft: ParagraphValue[], transactionId: string): EditTransaction;
5
+ /** Applies one atomic batch to source tokens. Opaque gaps are never reserialized.
6
+ * A retired identity is restored only when explicitly listed in `restoreIds`:
7
+ * reviving any other retired identity silently reassigns it (D13). */
8
+ export declare function applyParagraphChanges(document: DecisionDossierDocument, manifest: FocusEditingManifest, transactions: EditTransaction[], restoreIds?: ReadonlySet<string> | null): {
9
+ document: DecisionDossierDocument;
10
+ manifest: FocusEditingManifest;
11
+ };
12
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/editing/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAmB,cAAc,EAAa,MAAM,eAAe,CAAC;AAKhI,eAAO,MAAM,gBAAgB,GAAI,QAAQ,cAAc,EAAE,EAAE,OAAO,MAAM,KAAG,OAAqJ,CAAC;AACjO,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,eAAe,CA2B1H;AAED;;sEAEsE;AACtE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,uBAAuB,EAAE,QAAQ,EAAE,oBAAoB,EAAE,YAAY,EAAE,eAAe,EAAE,EAAE,UAAU,GAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAW,GAAG;IAAE,QAAQ,EAAE,uBAAuB,CAAC;IAAC,QAAQ,EAAE,oBAAoB,CAAA;CAAE,CA8C9P"}
@@ -0,0 +1,125 @@
1
+ import { fieldKey, readParagraphs, sealManifest, sourceNode } from './anchors.js';
2
+ import { baseDigest, canonicalJson } from './digest.js';
3
+ import { assertParagraphText } from './codec.js';
4
+ export const paragraphAnchors = (values, index) => ({ parentId: null, previousParagraphId: values[index - 1]?.paragraphId ?? null, nextParagraphId: values[index + 1]?.paragraphId ?? null });
5
+ export function diffParagraphs(baseline, draft, transactionId) {
6
+ for (const list of [baseline, draft])
7
+ if (new Set(list.map(p => p.paragraphId)).size !== list.length)
8
+ throw new Error('duplicate-paragraph-id');
9
+ const changes = [];
10
+ const emit = (p, operation, before, after, beforeAnchors, anchors) => {
11
+ const { text: _text, ...address } = p;
12
+ changes.push({ ...address, operation, before, after, beforeAnchors, anchors, changeId: `${transactionId}:${changes.length + 1}` });
13
+ };
14
+ const keys = [...new Set([...baseline, ...draft].map(fieldKey))];
15
+ for (const key of keys) {
16
+ const work = baseline.filter(p => fieldKey(p) === key).map(p => ({ ...p })), desired = draft.filter(p => fieldKey(p) === key);
17
+ for (let i = work.length - 1; i >= 0; i--)
18
+ if (!desired.some(p => p.paragraphId === work[i].paragraphId)) {
19
+ const anchors = paragraphAnchors(work, i);
20
+ emit(work[i], 'delete', work[i].text, null, anchors, anchors);
21
+ work.splice(i, 1);
22
+ }
23
+ for (const p of desired) {
24
+ const index = work.findIndex(w => w.paragraphId === p.paragraphId);
25
+ if (index >= 0 && work[index].text !== p.text) {
26
+ const anchors = paragraphAnchors(work, index);
27
+ emit(p, 'replace', work[index].text, p.text, anchors, anchors);
28
+ work[index].text = p.text;
29
+ }
30
+ }
31
+ desired.forEach((p, index) => {
32
+ const at = work.findIndex(w => w.paragraphId === p.paragraphId);
33
+ if (at === index)
34
+ return;
35
+ const before = at < 0 ? null : paragraphAnchors(work, at);
36
+ if (at >= 0)
37
+ work.splice(at, 1);
38
+ const anchors = { parentId: null, previousParagraphId: work[index - 1]?.paragraphId ?? null, nextParagraphId: work[index]?.paragraphId ?? null };
39
+ emit(p, at < 0 ? 'insert' : 'move', at < 0 ? null : p.text, p.text, before, anchors);
40
+ work.splice(index, 0, p);
41
+ });
42
+ }
43
+ return { transactionId, changes };
44
+ }
45
+ /** Applies one atomic batch to source tokens. Opaque gaps are never reserialized.
46
+ * A retired identity is restored only when explicitly listed in `restoreIds`:
47
+ * reviving any other retired identity silently reassigns it (D13). */
48
+ export function applyParagraphChanges(document, manifest, transactions, restoreIds = null) {
49
+ const values = readParagraphs(document, manifest), changes = transactions.flatMap(t => t.changes);
50
+ if (!changes.length)
51
+ return { document: structuredClone(document), manifest: structuredClone(manifest) };
52
+ const next = structuredClone(document), paragraphs = [], retired = new Set(manifest.retiredParagraphIds);
53
+ for (const f of manifest.fields) {
54
+ const key = fieldKey(f), source = sourceNode(document, f)[f.field];
55
+ const ps = manifest.paragraphs.filter(p => fieldKey(p) === key).sort((a, b) => a.range.start - b.range.start);
56
+ const tokens = [];
57
+ let offset = 0;
58
+ for (const p of ps) {
59
+ tokens.push({ text: source.slice(offset, p.range.start) }, { text: source.slice(p.range.start, p.range.end), value: values.find(v => v.paragraphId === p.paragraphId) });
60
+ offset = p.range.end;
61
+ }
62
+ tokens.push({ text: source.slice(offset) });
63
+ const active = () => tokens.filter(t => t.value).map(t => t.value);
64
+ for (const c of changes.filter(c => fieldKey(c) === key)) {
65
+ if (c.documentRef !== document.ref || c.format !== f.contentFormat)
66
+ throw new Error('address');
67
+ let tokenIndex = tokens.findIndex(t => t.value?.paragraphId === c.paragraphId);
68
+ const list = active(), index = list.findIndex(p => p.paragraphId === c.paragraphId);
69
+ if (c.operation === 'insert') {
70
+ if (values.some(p => p.paragraphId === c.paragraphId) || (retired.has(c.paragraphId) && !restoreIds?.has(c.paragraphId)) || tokenIndex >= 0 || c.before !== null || c.after === null)
71
+ throw new Error('reused-paragraph-id');
72
+ }
73
+ else {
74
+ if (index < 0 || list[index].text !== c.before)
75
+ throw new Error('paragraph-precondition');
76
+ if (canonicalJson(paragraphAnchors(list, index)) !== canonicalJson(c.beforeAnchors))
77
+ throw new Error('structural-precondition');
78
+ // Topology-preserving operations carry the same anchors out as in: a
79
+ // forged output topology would otherwise apply cleanly and break its
80
+ // own revocation with a structural-precondition failure.
81
+ if ((c.operation === 'replace' || c.operation === 'delete') && canonicalJson(c.anchors) !== canonicalJson(c.beforeAnchors))
82
+ throw new Error('structural-precondition');
83
+ }
84
+ if (c.after !== null)
85
+ assertParagraphText(c.after, c.format);
86
+ if (c.operation === 'replace') {
87
+ if (c.after === null)
88
+ throw new Error('replace-value');
89
+ tokens[tokenIndex] = { text: c.after, value: { ...list[index], text: c.after } };
90
+ continue;
91
+ }
92
+ if (c.operation === 'delete') {
93
+ if (c.after !== null)
94
+ throw new Error('delete-value');
95
+ tokens.splice(tokenIndex, 1);
96
+ retired.add(c.paragraphId);
97
+ continue;
98
+ }
99
+ if (c.operation === 'move') {
100
+ if (c.after !== c.before)
101
+ throw new Error('move-content');
102
+ tokens.splice(tokenIndex, 1);
103
+ }
104
+ const now = active(), previous = c.anchors.previousParagraphId, following = c.anchors.nextParagraphId;
105
+ const position = previous === null ? 0 : now.findIndex(p => p.paragraphId === previous) + 1;
106
+ if ((previous !== null && position === 0) || (now[position]?.paragraphId ?? null) !== following || c.anchors.parentId !== null)
107
+ throw new Error('ambiguous-placement');
108
+ tokenIndex = previous === null ? (following === null ? tokens.length : tokens.findIndex(t => t.value?.paragraphId === following)) : tokens.findIndex(t => t.value?.paragraphId === previous) + 1;
109
+ const value = { documentRef: document.ref, nodeId: c.nodeId, targetRef: c.targetRef, field: c.field, paragraphId: c.paragraphId, blockKind: 'paragraph', format: c.format, text: c.after };
110
+ tokens.splice(tokenIndex, 0, ...(previous === null ? [] : [{ text: '\n\n' }]), { text: c.after, value }, ...(previous === null && following !== null ? [{ text: '\n\n' }] : []));
111
+ retired.delete(c.paragraphId);
112
+ }
113
+ let text = '', order = 0;
114
+ for (const token of tokens) {
115
+ if (token.value)
116
+ paragraphs.push({ nodeId: f.nodeId, targetRef: f.targetRef, field: f.field, paragraphId: token.value.paragraphId, blockKind: 'paragraph', parentId: null, order: order++, range: { start: text.length, end: text.length + token.text.length, unit: 'utf16' } });
117
+ text += token.text;
118
+ }
119
+ sourceNode(next, f)[f.field] = text;
120
+ }
121
+ if (changes.some(c => !manifest.fields.some(f => fieldKey(f) === fieldKey(c))))
122
+ throw new Error('unsupported-field');
123
+ return { document: next, manifest: sealManifest({ schemaVersion: 1, manifestId: manifest.manifestId, documentRef: manifest.documentRef, baseHash: baseDigest(next), fields: structuredClone(manifest.fields), paragraphs, retiredParagraphIds: [...retired] }) };
124
+ }
125
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/editing/diff.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAwB,EAAE,KAAa,EAAW,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,WAAW,IAAI,IAAI,EAAE,eAAe,EAAE,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,WAAW,IAAI,IAAI,EAAE,CAAC,CAAC;AACjO,MAAM,UAAU,cAAc,CAAC,QAA0B,EAAE,KAAuB,EAAE,aAAqB;IACvG,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAChJ,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,CAAC,CAAiB,EAAE,SAAuC,EAAE,MAAqB,EAAE,KAAoB,EAAE,aAA6B,EAAE,OAAgB,EAAE,EAAE;QACxK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,GAAG,CAAC,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IACrI,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;QAC9H,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;YAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC;gBACzG,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC9H,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;YACnE,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;gBAAC,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAAC,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;YAAC,CAAC;QAC9L,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;YAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;YAChE,IAAI,EAAE,KAAK,KAAK;gBAAE,OAAO;YACzB,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1D,IAAI,EAAE,IAAI,CAAC;gBAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAChC,MAAM,OAAO,GAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,WAAW,IAAI,IAAI,EAAE,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,IAAI,IAAI,EAAE,CAAC;YAC1J,IAAI,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACjH,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC;AAED;;sEAEsE;AACtE,MAAM,UAAU,qBAAqB,CAAC,QAAiC,EAAE,QAA8B,EAAE,YAA+B,EAAE,aAAyC,IAAI;IACrL,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAClG,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;IACzG,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,EAAE,UAAU,GAAgB,EAAE,EAAE,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAEtH,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAW,CAAC;QAC7E,MAAM,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9G,MAAM,MAAM,GAAY,EAAE,CAAC;QAAC,IAAI,MAAM,GAAG,CAAC,CAAC;QAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,CAAE,EAAE,CAAC,CAAC;YAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;QAAC,CAAC;QACxN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAM,CAAC,CAAC;QACpE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,aAAa;gBAAE,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;YAC/F,IAAI,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;YAC/E,MAAM,IAAI,GAAG,MAAM,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;YACpF,IAAI,CAAC,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAC7B,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,UAAU,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YAC/N,CAAC;iBAAM,CAAC;gBACN,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBAC1F,IAAI,aAAa,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;gBAChI,qEAAqE;gBACrE,qEAAqE;gBACrE,yDAAyD;gBACzD,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YACzK,CAAC;YACD,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI;gBAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;YAC7D,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBAAC,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;gBAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;gBAAC,SAAS;YAAC,CAAC;YACtL,IAAI,CAAC,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAAC,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;gBAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;gBAAC,SAAS;YAAC,CAAC;YAC5J,IAAI,CAAC,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;gBAAC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;gBAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YAAC,CAAC;YACxH,MAAM,GAAG,GAAG,MAAM,EAAE,EAAE,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;YACtG,MAAM,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC5F,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,WAAW,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACvK,UAAU,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC;YACjM,MAAM,KAAK,GAAmB,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,KAAM,EAAE,CAAC;YAC5M,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,EAAE,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,KAAM,EAAE,KAAK,EAAE,EAAE,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACnN,CAAC;QACD,IAAI,IAAI,GAAG,EAAE,EAAE,KAAK,GAAG,CAAC,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,KAAK,CAAC,KAAK;gBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;YACjR,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;QACrB,CAAC;QACD,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACrH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,mBAAmB,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;AACnQ,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** RFC 8785 JSON values: UTF-16 key order, ECMAScript numbers, no coercion. */
2
+ export declare function canonicalJson(value: unknown): string;
3
+ export declare const baseDigest: (value: unknown) => string;
4
+ export declare function manifestDigest(value: object): string;
5
+ export declare function returnDigest(value: object): string;
6
+ //# sourceMappingURL=digest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../src/editing/digest.ts"],"names":[],"mappings":"AAGA,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA4BpD;AACD,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,MAAwF,CAAC;AACrI,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGpD;AACD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGlD"}
@@ -0,0 +1,56 @@
1
+ import { sha256 } from '@noble/hashes/sha2.js';
2
+ import { bytesToHex } from '@noble/hashes/utils.js';
3
+ /** RFC 8785 JSON values: UTF-16 key order, ECMAScript numbers, no coercion. */
4
+ export function canonicalJson(value) {
5
+ const ancestors = new Set();
6
+ const string = (s) => {
7
+ for (let i = 0; i < s.length; i++) {
8
+ const c = s.charCodeAt(i);
9
+ if (c >= 0xd800 && c <= 0xdbff) {
10
+ const next = s.charCodeAt(++i);
11
+ if (!(next >= 0xdc00 && next <= 0xdfff))
12
+ throw new Error('Non-I-JSON surrogate');
13
+ }
14
+ else if (c >= 0xdc00 && c <= 0xdfff)
15
+ throw new Error('Non-I-JSON surrogate');
16
+ }
17
+ return JSON.stringify(s);
18
+ };
19
+ const encode = (v) => {
20
+ if (v === null)
21
+ return 'null';
22
+ if (typeof v === 'string')
23
+ return string(v);
24
+ if (typeof v === 'boolean')
25
+ return String(v);
26
+ if (typeof v === 'number' && Number.isFinite(v))
27
+ return JSON.stringify(v);
28
+ if (typeof v !== 'object' || ancestors.has(v))
29
+ throw new Error('Expected acyclic JSON value');
30
+ ancestors.add(v);
31
+ let result;
32
+ if (Array.isArray(v)) {
33
+ if (Object.keys(v).length !== v.length)
34
+ throw new Error('Expected dense JSON array');
35
+ result = `[${v.map(encode).join(',')}]`;
36
+ }
37
+ else {
38
+ if (Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null)
39
+ throw new Error('Expected JSON object');
40
+ result = `{${Object.keys(v).sort().map(k => `${string(k)}:${encode(v[k])}`).join(',')}}`;
41
+ }
42
+ ancestors.delete(v);
43
+ return result;
44
+ };
45
+ return encode(value);
46
+ }
47
+ export const baseDigest = (value) => `sha256:${bytesToHex(sha256(new TextEncoder().encode(canonicalJson(value))))}`;
48
+ export function manifestDigest(value) {
49
+ const { manifestBaseHash: _ignored, ...projection } = value;
50
+ return baseDigest(projection);
51
+ }
52
+ export function returnDigest(value) {
53
+ const { digest: _ignored, ...projection } = value;
54
+ return baseDigest(projection);
55
+ }
56
+ //# sourceMappingURL=digest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest.js","sourceRoot":"","sources":["../../src/editing/digest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from './contract.js';
2
+ export * from './digest.js';
3
+ export * from './anchors.js';
4
+ export * from './codec.js';
5
+ export * from './diff.js';
6
+ export * from './reconcile.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/editing/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from './contract.js';
2
+ export * from './digest.js';
3
+ export * from './anchors.js';
4
+ export * from './codec.js';
5
+ export * from './diff.js';
6
+ export * from './reconcile.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/editing/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,66 @@
1
+ import type { DecisionDossierDocument } from '../model.js';
2
+ import type { EditTransaction, FocusEditingManifest, Production } from './contract.js';
3
+ export interface EditingSnapshot {
4
+ document: DecisionDossierDocument;
5
+ manifest: FocusEditingManifest;
6
+ }
7
+ export interface EditingConflict {
8
+ paragraphId?: string;
9
+ reason: string;
10
+ baseline?: string | null;
11
+ lineageApplied?: string | null;
12
+ current?: string | null;
13
+ requested?: string | null;
14
+ }
15
+ export interface EditReceipt {
16
+ returnId: string;
17
+ digest: string;
18
+ status: string;
19
+ baseHash?: string;
20
+ resultHash?: string;
21
+ previousStatus?: string;
22
+ transactions: {
23
+ transactionId: string;
24
+ changeIds: string[];
25
+ status: string;
26
+ }[];
27
+ conflicts: EditingConflict[];
28
+ }
29
+ export interface ReturnLineage {
30
+ baseHash: string;
31
+ manifestBaseHash: string;
32
+ production: Production;
33
+ head?: {
34
+ returnId: string;
35
+ supersededReturnIds: string[];
36
+ };
37
+ tombstones: string[];
38
+ seen: {
39
+ returnId: string;
40
+ digest: string;
41
+ receipt: EditReceipt;
42
+ }[];
43
+ applied: EditTransaction[];
44
+ applications: {
45
+ returnId: string;
46
+ baseHash: string;
47
+ resultHash: string;
48
+ transactions: EditTransaction[];
49
+ }[];
50
+ }
51
+ export interface ReconcileInput {
52
+ baseline: EditingSnapshot;
53
+ current: EditingSnapshot;
54
+ value: unknown;
55
+ production: Production;
56
+ lineage?: ReturnLineage;
57
+ }
58
+ export interface ReconcileResult extends EditingSnapshot {
59
+ ok: boolean;
60
+ lineage: ReturnLineage;
61
+ receipt: EditReceipt;
62
+ conflicts: EditingConflict[];
63
+ }
64
+ /** Data in/data out. Persistence and the final publication boundary belong to the host. */
65
+ export declare function reconcile(input: ReconcileInput): ReconcileResult;
66
+ //# sourceMappingURL=reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/editing/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAgC,UAAU,EAAE,MAAM,eAAe,CAAC;AAMrH,MAAM,WAAW,eAAe;IAAG,QAAQ,EAAE,uBAAuB,CAAC;IAAC,QAAQ,EAAE,oBAAoB,CAAA;CAAE;AACtG,MAAM,WAAW,eAAe;IAAG,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AACvL,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/E,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AACD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,UAAU,CAAC;IACnE,IAAI,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC3D,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,EAAE,CAAC;IACzF,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,eAAe,EAAE,CAAA;KAAE,EAAE,CAAC;CAC7G;AACD,MAAM,WAAW,cAAc;IAAG,QAAQ,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAA;CAAE;AACxJ,MAAM,WAAW,eAAgB,SAAQ,eAAe;IAAG,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,eAAe,EAAE,CAAA;CAAE;AAqDpJ,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAsFhE"}
@@ -0,0 +1,167 @@
1
+ import { validateReturn, validateReturnStructure } from './contract.js';
2
+ import { fieldKey, readParagraphs, validateManifest, sourceNode, sealManifest } from './anchors.js';
3
+ import { applyParagraphChanges, diffParagraphs, paragraphAnchors } from './diff.js';
4
+ import { baseDigest, canonicalJson } from './digest.js';
5
+ /** Source gaps are addressed by paragraph identities, never matched by prose similarity. */
6
+ function fieldGaps(snapshot, field) {
7
+ const source = sourceNode(snapshot.document, field)[field.field];
8
+ const ps = snapshot.manifest.paragraphs.filter(p => fieldKey(p) === fieldKey(field)).sort((a, b) => a.order - b.order);
9
+ const gaps = new Map();
10
+ for (let i = 0; i <= ps.length; i++)
11
+ gaps.set(canonicalJson([ps[i - 1]?.paragraphId ?? null, ps[i]?.paragraphId ?? null]), source.slice(ps[i - 1]?.range.end ?? 0, ps[i]?.range.start ?? source.length));
12
+ return gaps;
13
+ }
14
+ /** Restore the original layout of structural edits, replaying independent third-party units.
15
+ * The source baseline supplies exact separators; unrelated source gaps remain current. */
16
+ function restoreStructuralFields(baseline, applied, current, keys, restoreIds) {
17
+ const document = structuredClone(current.document), paragraphs = structuredClone(current.manifest.paragraphs.filter(p => !keys.has(fieldKey(p))));
18
+ for (const f of baseline.manifest.fields.filter(f => keys.has(fieldKey(f)))) {
19
+ const previous = readParagraphs(applied.document, applied.manifest).filter(p => fieldKey(p) === fieldKey(f));
20
+ const now = readParagraphs(current.document, current.manifest).filter(p => fieldKey(p) === fieldKey(f));
21
+ const independent = diffParagraphs(previous, now, 'reconciler:independent');
22
+ // Pure text corrections carry the applied layout in their anchors. Re-anchor
23
+ // them onto the baseline topology before replaying: the text precondition
24
+ // stays third-party, but the topology precondition must describe the base
25
+ // being replayed onto, not the layout the lineage had already changed.
26
+ const baseValues = readParagraphs(baseline.document, baseline.manifest).filter(p => fieldKey(p) === fieldKey(f));
27
+ for (const c of independent.changes) {
28
+ if (c.operation !== 'replace')
29
+ continue;
30
+ const at = baseValues.findIndex(p => p.paragraphId === c.paragraphId);
31
+ if (at < 0)
32
+ continue;
33
+ const reanchored = paragraphAnchors(baseValues, at);
34
+ c.beforeAnchors = { ...reanchored };
35
+ c.anchors = { ...reanchored };
36
+ }
37
+ const rebased = applyParagraphChanges(baseline.document, baseline.manifest, independent.changes.length ? [independent] : [], restoreIds);
38
+ const baseGaps = fieldGaps(baseline, f), appliedGaps = fieldGaps(applied, f), currentGaps = fieldGaps(current, f), restoredGaps = fieldGaps(rebased, f);
39
+ for (const [key, gap] of currentGaps) {
40
+ if (appliedGaps.has(key) && gap !== appliedGaps.get(key) && baseGaps.get(key) !== appliedGaps.get(key))
41
+ throw new Error('structural-gap-diverged');
42
+ if (!appliedGaps.has(key) || baseGaps.get(key) === appliedGaps.get(key))
43
+ restoredGaps.set(key, gap);
44
+ }
45
+ const ps = rebased.manifest.paragraphs.filter(p => fieldKey(p) === fieldKey(f)).sort((a, b) => a.order - b.order);
46
+ const source = sourceNode(rebased.document, f)[f.field];
47
+ let text = '';
48
+ ps.forEach((p, index) => {
49
+ text += restoredGaps.get(canonicalJson([ps[index - 1]?.paragraphId ?? null, p.paragraphId])) ?? '';
50
+ const value = source.slice(p.range.start, p.range.end), start = text.length;
51
+ text += value;
52
+ paragraphs.push({ ...p, range: { start, end: text.length, unit: 'utf16' } });
53
+ });
54
+ text += restoredGaps.get(canonicalJson([ps.at(-1)?.paragraphId ?? null, null])) ?? '';
55
+ sourceNode(document, f)[f.field] = text;
56
+ }
57
+ const active = new Set(paragraphs.map(p => p.paragraphId));
58
+ const retiredParagraphIds = [...new Set([...current.manifest.retiredParagraphIds, ...current.manifest.paragraphs.map(p => p.paragraphId)])].filter(id => !active.has(id));
59
+ return { document, manifest: sealManifest({ ...current.manifest, baseHash: baseDigest(document), paragraphs, retiredParagraphIds }) };
60
+ }
61
+ /** Data in/data out. Persistence and the final publication boundary belong to the host. */
62
+ export function reconcile(input) {
63
+ const { baseline, current, production } = input;
64
+ const lineage = structuredClone(input.lineage ?? { baseHash: baseline.manifest.baseHash, manifestBaseHash: baseline.manifest.manifestBaseHash, production, tombstones: [], seen: [], applied: [], applications: [] });
65
+ const value = input.value;
66
+ let rememberIdentity = false;
67
+ const result = (status, conflicts = [], candidate = current, ok = false, appliedTransition) => {
68
+ const transactions = (validateReturnStructure(value) ? value.editReturn.transactions : []).map(t => ({ transactionId: t.transactionId, changeIds: t.changes.map(c => c.changeId), status }));
69
+ // The receipt names the changes the reconciler effectively applied or
70
+ // revoked — including revocation inverses the incoming return never
71
+ // listed — so a revocation is auditable by change identity.
72
+ if (ok && appliedTransition && appliedTransition.changes.length)
73
+ transactions.push({ transactionId: appliedTransition.transactionId, changeIds: appliedTransition.changes.map(c => c.changeId), status });
74
+ const receipt = { returnId: typeof value?.returnId === 'string' ? value.returnId : '', digest: typeof value?.digest === 'string' ? value.digest : '', status,
75
+ transactions, conflicts };
76
+ if (ok) {
77
+ receipt.baseHash = baseDigest(current.document);
78
+ receipt.resultHash = baseDigest(candidate.document);
79
+ }
80
+ if (rememberIdentity && !lineage.seen.some(s => s.returnId === value.returnId))
81
+ lineage.seen.push({ returnId: value.returnId, digest: value.digest, receipt });
82
+ return { ok, ...structuredClone(candidate), lineage, receipt, conflicts };
83
+ };
84
+ const validation = validateReturn(value, baseline.document, baseline.manifest);
85
+ if (!validation.ok)
86
+ return result('validation-refused', validation.issues.map(reason => ({ reason })));
87
+ if (canonicalJson(value.production) !== canonicalJson(production) || lineage.baseHash !== baseline.manifest.baseHash || lineage.manifestBaseHash !== baseline.manifest.manifestBaseHash || canonicalJson(lineage.production) !== canonicalJson(production))
88
+ return result('generation-mismatch');
89
+ const seen = lineage.seen.find(s => s.returnId === value.returnId);
90
+ if (seen) {
91
+ const out = result(seen.digest === value.digest ? 'already-processed' : 'identity-reuse');
92
+ // A replay reports the original diagnostic, never an empty success: the
93
+ // unsuccessful character of a refused return survives the replay.
94
+ out.receipt.previousStatus = seen.receipt.status;
95
+ out.receipt.conflicts = seen.receipt.conflicts;
96
+ if (seen.digest === value.digest) {
97
+ out.receipt.baseHash = seen.receipt.baseHash;
98
+ out.receipt.resultHash = seen.receipt.resultHash;
99
+ }
100
+ return out;
101
+ }
102
+ rememberIdentity = true;
103
+ if (lineage.tombstones.includes(value.returnId) || lineage.head?.supersededReturnIds.includes(value.returnId))
104
+ return result('superseded');
105
+ if (lineage.head) {
106
+ const at = value.editReturn.supersededReturnIds.indexOf(lineage.head.returnId);
107
+ if (at < 0 || canonicalJson(value.editReturn.supersededReturnIds.slice(at + 1)) !== canonicalJson(lineage.head.supersededReturnIds)) {
108
+ const fork = value.editReturn.supersedesReturnId === lineage.head.supersededReturnIds[0];
109
+ return result('lineage-order-indeterminate', [{ reason: fork ? 'fork' : 'ancestry-does-not-prove-succession' }]);
110
+ }
111
+ }
112
+ if (!validateManifest(current.manifest, current.document).ok || current.manifest.manifestId !== baseline.manifest.manifestId)
113
+ return result('conflict', [{ reason: 'mapping-loss' }]);
114
+ try {
115
+ const desired = applyParagraphChanges(baseline.document, baseline.manifest, value.editReturn.transactions);
116
+ // Compare the whole submitted batch before publication, including previously applied units
117
+ // that this cumulative head no longer requests.
118
+ const base = readParagraphs(baseline.document, baseline.manifest), actual = readParagraphs(current.document, current.manifest);
119
+ const applied = applyParagraphChanges(baseline.document, baseline.manifest, lineage.applied);
120
+ const appliedValues = readParagraphs(applied.document, applied.manifest), desiredValues = readParagraphs(desired.document, desired.manifest);
121
+ const oldChanges = lineage.applied.flatMap(t => t.changes), newChanges = value.editReturn.transactions.flatMap(t => t.changes);
122
+ const affected = new Set([...oldChanges, ...newChanges].map(c => c.paragraphId)), conflicts = [];
123
+ for (const id of affected) {
124
+ const b = base.find(p => p.paragraphId === id), a = actual.find(p => p.paragraphId === id), r = appliedValues.find(p => p.paragraphId === id), d = desiredValues.find(p => p.paragraphId === id);
125
+ const recorded = oldChanges.filter(c => c.paragraphId === id), expected = recorded.length ? r : b;
126
+ const sameAddress = !expected || (!!a && fieldKey(a) === fieldKey(expected));
127
+ let matches = (a?.text ?? null) === (expected?.text ?? null) && sameAddress;
128
+ // Every affected unit carries a topology precondition, including pure
129
+ // text replacements: a paragraph moved by a third party no longer
130
+ // matches, even when its text is unchanged.
131
+ if (matches && expected && a) {
132
+ const expectedList = (recorded.length ? appliedValues : base).filter(p => fieldKey(p) === fieldKey(expected));
133
+ const actualList = actual.filter(p => fieldKey(p) === fieldKey(a));
134
+ matches = canonicalJson(paragraphAnchors(expectedList, expectedList.indexOf(expected))) === canonicalJson(paragraphAnchors(actualList, actualList.indexOf(a)));
135
+ }
136
+ if (!matches)
137
+ conflicts.push({ paragraphId: id, reason: 'precondition-or-attribution', baseline: b?.text ?? null, ...(recorded.length ? { lineageApplied: r?.text ?? null } : {}), current: a?.text ?? null, requested: d?.text ?? null });
138
+ }
139
+ if (conflicts.length)
140
+ return result('conflict', conflicts);
141
+ const structuralFields = new Set(oldChanges.filter(c => c.operation !== 'replace').map(fieldKey));
142
+ // Only identities this lineage retired may be restored: revocation must
143
+ // revive lineage content, while a collision with a third-party
144
+ // retirement refuses instead of reassigning the identity (D13).
145
+ const deletedIds = (txs) => txs.flatMap(t => t.changes).filter(c => c.operation === 'delete').map(c => c.paragraphId);
146
+ const restoreIds = new Set([...deletedIds(lineage.applied), ...lineage.applications.flatMap(a => deletedIds(a.transactions))]);
147
+ const sourceRestored = restoreStructuralFields(baseline, applied, current, structuralFields, restoreIds);
148
+ const inverse = oldChanges.filter(c => !structuralFields.has(fieldKey(c))).slice().reverse().map(c => ({ ...c, operation: c.operation === 'insert' ? 'delete' : c.operation === 'delete' ? 'insert' : c.operation,
149
+ before: c.after, after: c.before, beforeAnchors: c.operation === 'delete' ? null : c.anchors, anchors: c.beforeAnchors ?? c.anchors }));
150
+ const restored = applyParagraphChanges(sourceRestored.document, sourceRestored.manifest, inverse.length ? [{ transactionId: 'reconciler:revoke', changes: inverse }] : [], restoreIds);
151
+ const candidate = applyParagraphChanges(restored.document, restored.manifest, value.editReturn.transactions, restoreIds);
152
+ if (!validateManifest(candidate.manifest, candidate.document).ok)
153
+ return result('conflict', [{ reason: 'result-mapping' }]);
154
+ const transition = diffParagraphs(actual, readParagraphs(candidate.document, candidate.manifest), `application:${value.returnId}`);
155
+ const revoked = oldChanges.some(c => !newChanges.some(n => n.paragraphId === c.paragraphId));
156
+ const out = result(revoked ? 'revocation-applied' : 'applied', [], candidate, true, transition);
157
+ lineage.head = { returnId: value.returnId, supersededReturnIds: [...value.editReturn.supersededReturnIds] };
158
+ lineage.tombstones = [...new Set([...lineage.tombstones, ...value.editReturn.supersededReturnIds])];
159
+ lineage.applied = structuredClone(value.editReturn.transactions);
160
+ lineage.applications.push({ returnId: value.returnId, baseHash: out.receipt.baseHash, resultHash: out.receipt.resultHash, transactions: transition.changes.length ? [transition] : [] });
161
+ return out;
162
+ }
163
+ catch (error) {
164
+ return result('conflict', [{ reason: error instanceof Error ? error.message : 'reconciliation-failed' }]);
165
+ }
166
+ }
167
+ //# sourceMappingURL=reconcile.js.map