@omgbase/core 0.28.0 → 0.29.2

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 (74) hide show
  1. package/README.md +7 -5
  2. package/dist/src/core/read/document.d.ts +11 -10
  3. package/dist/src/core/read/document.js +12 -11
  4. package/dist/src/core/read/nodes.d.ts +10 -10
  5. package/dist/src/core/read/nodes.js +11 -11
  6. package/dist/src/core/read/reader.d.ts +37 -24
  7. package/dist/src/core/read/reader.js +44 -31
  8. package/dist/src/graph/history.d.ts +25 -22
  9. package/dist/src/graph/history.js +17 -17
  10. package/dist/src/graph/inbound-links.d.ts +7 -7
  11. package/dist/src/graph/inbound-links.js +7 -7
  12. package/dist/src/graph/link-health.d.ts +9 -9
  13. package/dist/src/graph/link-health.js +9 -9
  14. package/dist/src/graph/links.d.ts +3 -3
  15. package/dist/src/graph/links.js +4 -4
  16. package/dist/src/mcp/arguments.d.ts +10 -3
  17. package/dist/src/mcp/arguments.js +32 -9
  18. package/dist/src/mcp/errors.d.ts +1 -1
  19. package/dist/src/mcp/graph.d.ts +22 -22
  20. package/dist/src/mcp/graph.js +54 -47
  21. package/dist/src/mcp/reference.d.ts +29 -1
  22. package/dist/src/mcp/reference.js +398 -255
  23. package/dist/src/mcp/server.d.ts +9 -0
  24. package/dist/src/mcp/server.js +170 -108
  25. package/dist/src/mutate/apply.d.ts +6 -6
  26. package/dist/src/mutate/apply.js +1 -1
  27. package/dist/src/mutate/canonical.d.ts +1 -1
  28. package/dist/src/mutate/canonical.js +2 -2
  29. package/dist/src/mutate/commit.d.ts +3 -3
  30. package/dist/src/mutate/commit.js +4 -4
  31. package/dist/src/mutate/docs.d.ts +1 -1
  32. package/dist/src/mutate/docs.js +8 -8
  33. package/dist/src/mutate/effects.d.ts +17 -17
  34. package/dist/src/mutate/effects.js +113 -113
  35. package/dist/src/mutate/macros.d.ts +5 -5
  36. package/dist/src/mutate/macros.js +1 -1
  37. package/dist/src/mutate/materialize.js +2 -2
  38. package/dist/src/mutate/ops.js +2 -2
  39. package/dist/src/mutate/opset.d.ts +7 -7
  40. package/dist/src/mutate/plan-update.js +2 -2
  41. package/dist/src/mutate/plan.d.ts +8 -8
  42. package/dist/src/mutate/plan.js +25 -25
  43. package/dist/src/mutate/select.d.ts +4 -4
  44. package/dist/src/mutate/select.js +57 -56
  45. package/dist/src/mutate/sugar.d.ts +10 -10
  46. package/dist/src/mutate/sugar.js +21 -21
  47. package/dist/src/oqx-js/context.d.ts +8 -4
  48. package/dist/src/oqx-js/context.js +206 -189
  49. package/dist/src/oqx-js/names.d.ts +28 -17
  50. package/dist/src/oqx-js/names.js +115 -78
  51. package/dist/src/oqx-js/planner.d.ts +4 -8
  52. package/dist/src/oqx-js/planner.js +6 -10
  53. package/dist/src/oqx-js/root-row.d.ts +6 -5
  54. package/dist/src/oqx-js/root-row.js +15 -16
  55. package/dist/src/oqx-js/run.d.ts +18 -17
  56. package/dist/src/oqx-js/run.js +57 -54
  57. package/dist/src/oqx-js/sql/translate.d.ts +5 -5
  58. package/dist/src/oqx-js/sql/translate.js +162 -161
  59. package/dist/src/oqx-js/store-index.d.ts +1 -1
  60. package/dist/src/oqx-js/store-index.js +28 -30
  61. package/dist/src/search/external.d.ts +10 -0
  62. package/dist/src/search/external.js +20 -2
  63. package/dist/src/search/resolve.d.ts +2 -2
  64. package/dist/src/search/resolve.js +1 -1
  65. package/dist/src/search/rrf.js +2 -2
  66. package/dist/src/search/text.d.ts +5 -5
  67. package/dist/src/search/text.js +2 -2
  68. package/dist/src/spec-versions.d.ts +7 -7
  69. package/dist/src/spec-versions.js +6 -6
  70. package/dist/src/surface-paths.d.ts +18 -17
  71. package/dist/src/surface-paths.js +23 -21
  72. package/dist/src/sync/admin.d.ts +2 -2
  73. package/dist/src/sync/admin.js +1 -1
  74. package/package.json +2 -2
@@ -75,11 +75,11 @@ function bareAnchor(at, into, here, f) {
75
75
  const message = `${here}: \`at\` must be "start", "end", { before: <block id> } or { after: <block id> } — "${at} block X" is into: X's parent (a block, or the document when X is top-level), at: { ${at}: X }`;
76
76
  if (f === undefined || into.kind !== "block")
77
77
  return effectInvalid(message);
78
- const row = f.parentOf(into.id);
78
+ const row = f.parentOf(into._id);
79
79
  if (row === null)
80
80
  return effectInvalid(message);
81
- const parent = row.parent === null ? referencePath(row.path) : { kind: "block", id: row.parent };
82
- return effectInvalid(message, f.build({ ...f.written, into: parent, at: { [at]: into.id } }));
81
+ const parent = row.parent === null ? referencePath(row.path) : { kind: "block", _id: row.parent };
82
+ return effectInvalid(message, f.build({ ...f.written, into: parent, at: { [at]: into._id } }));
83
83
  }
84
84
  /**
85
85
  * §8 (3.4): a plural-target `expect` written as `{ content_hash: { <id>: <hash>, … } }`
@@ -135,8 +135,8 @@ export function isTargetId(s) {
135
135
  }
136
136
  /**
137
137
  * A bare string where a target object is expected (3.2, §11.8 "Sugar"): a
138
- * path-looking string → `{ kind: "document", path }`; a `b_`/`d_`/`n_` id →
139
- * that kind; `$new.n` → `{ kind: "symbol", id }` (its kind is the symbol
138
+ * path-looking string → `{ kind: "document", _path }`; a `b_`/`d_`/`n_` id →
139
+ * that kind; `$new.n` → `{ kind: "symbol", _id }` (its kind is the symbol
140
140
  * table's at resolution); a `#` locator → `filter_invalid` with a `fix`;
141
141
  * anything else is no target. A `-> ` leader never reaches here from the
142
142
  * planner (`select.ts` desugars it first); as a library input it is refused
@@ -152,15 +152,15 @@ export function bareTarget(ref, here) {
152
152
  throw new MutationError("filter_invalid", reason, { reason, hint: "arguments", fix: "resolve the locator to an id first: `blocks_resolve { locator }`, or a `query` whose hit is the block" });
153
153
  }
154
154
  if (isNewSymbol(ref))
155
- return { kind: "symbol", id: ref };
155
+ return { kind: "symbol", _id: ref };
156
156
  if (isValidId(ref, "b"))
157
- return { kind: "block", id: ref };
157
+ return { kind: "block", _id: ref };
158
158
  if (isValidId(ref, "d"))
159
- return { kind: "document", id: ref };
159
+ return { kind: "document", _id: ref };
160
160
  if (NODE_ID_RE.test(ref))
161
- return { kind: "node", id: ref };
161
+ return { kind: "node", _id: ref };
162
162
  if (looksLikePath(ref))
163
- return { kind: "document", path: ref };
163
+ return { kind: "document", _path: ref };
164
164
  throw effectInvalid(`${here}: a target is { kind: …, … } or a bare document path, a \`b_\`/\`d_\`/\`n_\` id or \`$new.n\` (got ${JSON.stringify(ref)})`);
165
165
  }
166
166
  export function validateTarget(v, here, kinds) {
@@ -169,25 +169,25 @@ export function validateTarget(v, here, kinds) {
169
169
  if (!isRecord(v) || typeof v.kind !== "string")
170
170
  throw effectInvalid(`${here}: a target is { kind: ${kinds.map((k) => `"${k}"`).join(" | ")}, … }`);
171
171
  // A bare `$new.n` defers its kind check to resolution (`resolveEffect`); the `kinds` are the slot's.
172
- if (v.kind === "symbol" && typeof v.id === "string" && isNewSymbol(v.id) && Object.keys(v).length === 2)
173
- return { kind: "symbol", id: v.id };
172
+ if (v.kind === "symbol" && typeof v._id === "string" && isNewSymbol(v._id) && Object.keys(v).length === 2)
173
+ return { kind: "symbol", _id: v._id };
174
174
  if (!kinds.includes(v.kind))
175
175
  throw effectInvalid(`${here}: \`kind\` must be one of ${kinds.join(", ")} (got "${v.kind}")`);
176
176
  switch (v.kind) {
177
177
  case "document": {
178
- onlyKeys(v, ["kind", "id", "path"], here);
179
- const id = optionalString(v, "id", here);
180
- const path = optionalString(v, "path", here);
178
+ onlyKeys(v, ["kind", "_id", "_path"], here);
179
+ const id = optionalString(v, "_id", here);
180
+ const path = optionalString(v, "_path", here);
181
181
  if (id === undefined && path === undefined)
182
- throw effectInvalid(`${here}: a document target needs \`id\` or \`path\``);
183
- return { kind: "document", ...(id !== undefined ? { id } : {}), ...(path !== undefined ? { path } : {}) };
182
+ throw effectInvalid(`${here}: a document target needs \`_id\` or \`_path\``);
183
+ return { kind: "document", ...(id !== undefined ? { _id: id } : {}), ...(path !== undefined ? { _path: path } : {}) };
184
184
  }
185
185
  case "block":
186
- onlyKeys(v, ["kind", "id"], here);
187
- return { kind: "block", id: requireString(v, "id", here) };
186
+ onlyKeys(v, ["kind", "_id"], here);
187
+ return { kind: "block", _id: requireString(v, "_id", here) };
188
188
  case "node":
189
- onlyKeys(v, ["kind", "id"], here);
190
- return { kind: "node", id: requireString(v, "id", here) };
189
+ onlyKeys(v, ["kind", "_id"], here);
190
+ return { kind: "node", _id: requireString(v, "_id", here) };
191
191
  case "section":
192
192
  onlyKeys(v, ["kind", "heading_id"], here);
193
193
  return { kind: "section", heading_id: requireString(v, "heading_id", here) };
@@ -365,26 +365,26 @@ export function validateEffects(v, fix) {
365
365
  return v.map((e, i) => validateEffect(e, i, fix));
366
366
  }
367
367
  // §11.2: the `field` spellings per target kind.
368
- export const FRONTMATTER_FIELD = /^frontmatter\.(.+)$/;
368
+ export const FRONTMATTER_FIELD = /^_frontmatter\.(.+)$/;
369
369
  function checkField(kind, field, here, op, f) {
370
370
  if (kind === "document") {
371
371
  if (FRONTMATTER_FIELD.test(field) || (op === "set" && field === "content"))
372
372
  return;
373
- throw effectInvalid(`${here}: a document's \`field\` is \`frontmatter.<key>\`${op === "set" ? " or `content`" : ""} (got "${field}")`);
373
+ throw effectInvalid(`${here}: a document's \`field\` is \`_frontmatter.<key>\`${op === "set" ? " or `content`" : ""} (got "${field}")`);
374
374
  }
375
375
  if (kind === "block") {
376
- if (field === "markdown" || field === "attrs.checked")
376
+ if (field === "markdown" || field === "_attrs.checked")
377
377
  return;
378
378
  // (3.4, §8) `text` / `content` / `body` / `raw` on a block: the fix spells `markdown`.
379
379
  const fix = f !== undefined && BLOCK_FIELD_ALIASES.has(field) ? f.build({ ...f.written, field: "markdown" }) : undefined;
380
- throw effectInvalid(`${here}: a block's \`field\` is \`markdown\` or \`attrs.checked\` (got "${field}")`, fix);
380
+ throw effectInvalid(`${here}: a block's \`field\` is \`markdown\` or \`_attrs.checked\` (got "${field}")`, fix);
381
381
  }
382
382
  // node: the adapter's editable props (`node_not_editable` names them when unknown)
383
383
  if (field === "" || field.includes("."))
384
384
  throw effectInvalid(`${here}: a node's \`field\` is one of its editable properties (got "${field}")`);
385
385
  }
386
386
  export function conflictingEffects(effects, focus) {
387
- const what = focus.field !== undefined ? `${focus.target.kind} ${focus.target.id ?? focus.target.path} ${focus.field}` : `${focus.target.kind} ${focus.target.id ?? focus.target.path}`;
387
+ const what = focus.field !== undefined ? `${focus.target.kind} ${focus.target._id ?? focus.target._path} ${focus.field}` : `${focus.target.kind} ${focus.target._id ?? focus.target._path}`;
388
388
  return new MutationError("conflicting_effects", `effects ${effects[0]} and ${effects[1]} contradict each other on ${what}`, { effects, focus, retriable: false });
389
389
  }
390
390
  /** The focus bookkeeping over an ordered effect list (§11.3). */
@@ -404,22 +404,22 @@ export class ConflictTracker {
404
404
  for (const d of info.docs) {
405
405
  const by = this.retiredDocs.get(d);
406
406
  if (by !== undefined)
407
- throw conflictingEffects([by, i], { kind: "identity", target: { kind: "document", id: d } });
407
+ throw conflictingEffects([by, i], { kind: "identity", target: { kind: "document", _id: d } });
408
408
  }
409
409
  for (const b of info.blocks) {
410
410
  const by = this.retiredBlocks.get(b);
411
411
  if (by !== undefined)
412
- throw conflictingEffects([by, i], { kind: "identity", target: { kind: "block", id: b } });
412
+ throw conflictingEffects([by, i], { kind: "identity", target: { kind: "block", _id: b } });
413
413
  }
414
414
  for (const p of info.paths) {
415
415
  const by = this.retiredPaths.get(p);
416
416
  if (by !== undefined)
417
- throw conflictingEffects([by, i], { kind: "identity", target: { kind: "path", path: p } });
417
+ throw conflictingEffects([by, i], { kind: "identity", target: { kind: "path", _path: p } });
418
418
  }
419
419
  if (info.wholeDoc !== undefined) {
420
420
  const by = this.touchedDocs.get(info.wholeDoc);
421
421
  if (by !== undefined)
422
- throw conflictingEffects([by, i], { kind: "identity", target: { kind: "document", id: info.wholeDoc } });
422
+ throw conflictingEffects([by, i], { kind: "identity", target: { kind: "document", _id: info.wholeDoc } });
423
423
  }
424
424
  if (info.scalar) {
425
425
  const prev = this.scalar.get(info.scalar.key);
@@ -470,30 +470,30 @@ export class SymbolTable {
470
470
  function docMissing(ref) {
471
471
  return new MutationError("doc_missing", `doc ${ref} not found`, { doc: ref });
472
472
  }
473
- /** Resolve a document target: an existing document's `{ id, path }`, or a `$new.n` document this plan creates. */
473
+ /** Resolve a document target: an existing document's `{ _id, _path }`, or a `$new.n` document this plan creates. */
474
474
  export function resolveDocumentTarget(ctx, t) {
475
- if (t.id !== undefined && isNewSymbol(t.id)) {
476
- const created = ctx.creates.find((c) => c.symbol === t.id);
475
+ if (t._id !== undefined && isNewSymbol(t._id)) {
476
+ const created = ctx.creates.find((c) => c.symbol === t._id);
477
477
  if (!created)
478
- throw symbolMissing(ctx, t.id, "document");
479
- if (t.path !== undefined && canonicalDocPath(t.path) !== created.path)
480
- throw effectInvalid(`id ${JSON.stringify(t.id)} and path ${JSON.stringify(t.path)} name different documents`);
481
- return { kind: "document", id: t.id, path: created.path };
478
+ throw symbolMissing(ctx, t._id, "document");
479
+ if (t._path !== undefined && canonicalDocPath(t._path) !== created.path)
480
+ throw effectInvalid(`\`_id\` ${JSON.stringify(t._id)} and \`_path\` ${JSON.stringify(t._path)} name different documents`);
481
+ return { kind: "document", _id: t._id, _path: created.path };
482
482
  }
483
483
  return findDocumentTarget(ctx.store, ctx.repoId, t);
484
484
  }
485
- /** An existing document's `{ id, path }` for a literal document target (`doc_missing`; `filter_invalid` when `id` and `path` disagree). */
485
+ /** An existing document's `{ _id, _path }` for a literal document target (`doc_missing`; `filter_invalid` when `_id` and `_path` disagree). */
486
486
  export function findDocumentTarget(store, repoId, t) {
487
- const byId = t.id !== undefined ? findDoc(store, { docId: t.id }) : null;
488
- if (t.id !== undefined && (!byId || byId.repoId !== repoId))
489
- throw docMissing(t.id);
490
- const byPath = t.path !== undefined ? findDoc(store, { repoId, path: t.path }) : null;
491
- if (t.path !== undefined && !byPath)
492
- throw docMissing(t.path);
487
+ const byId = t._id !== undefined ? findDoc(store, { docId: t._id }) : null;
488
+ if (t._id !== undefined && (!byId || byId.repoId !== repoId))
489
+ throw docMissing(t._id);
490
+ const byPath = t._path !== undefined ? findDoc(store, { repoId, path: t._path }) : null;
491
+ if (t._path !== undefined && !byPath)
492
+ throw docMissing(t._path);
493
493
  if (byId && byPath && byId.docId !== byPath.docId)
494
- throw effectInvalid(`id ${JSON.stringify(t.id)} and path ${JSON.stringify(t.path)} name different documents`);
494
+ throw effectInvalid(`\`_id\` ${JSON.stringify(t._id)} and \`_path\` ${JSON.stringify(t._path)} name different documents`);
495
495
  const info = (byId ?? byPath);
496
- return { kind: "document", id: info.docId, path: info.path };
496
+ return { kind: "document", _id: info.docId, _path: info.path };
497
497
  }
498
498
  /** A bare `$new.n` target (3.2): the kind the symbol table recorded, checked against the slot's admitted kinds. */
499
499
  function resolveSymbolTarget(ctx, symbol, kinds, here) {
@@ -502,7 +502,7 @@ function resolveSymbolTarget(ctx, symbol, kinds, here) {
502
502
  throw new MutationError("target_missing", `${symbol} is not created by an earlier effect`, { symbol });
503
503
  if (!kinds.includes(kind))
504
504
  throw effectInvalid(`${here}: \`kind\` must be one of ${kinds.join(", ")} (got "${kind}")`);
505
- return { kind, id: symbol };
505
+ return { kind, _id: symbol };
506
506
  }
507
507
  function symbolMissing(ctx, symbol, wanted) {
508
508
  const kind = ctx.symbols.kindOf(symbol);
@@ -614,9 +614,9 @@ function placeWords(ctx, into, at, verb) {
614
614
  case "section":
615
615
  return `${edge} of ${sectionName(ctx, into.heading_id)} in ${path}`;
616
616
  case "block":
617
- return `${edge} of ${blockName(ctx, into.id)} in ${path}`;
617
+ return `${edge} of ${blockName(ctx, into._id)} in ${path}`;
618
618
  case "node":
619
- return `${edge} of node ${into.id} in ${path}`;
619
+ return `${edge} of node ${into._id} in ${path}`;
620
620
  }
621
621
  }
622
622
  /** The document of a block list's first block (a kernel `remove`/`merge` names blocks of one document). */
@@ -652,7 +652,7 @@ function descendantsOf(ctx, id) {
652
652
  function targetDoc(ctx, t) {
653
653
  switch (t.kind) {
654
654
  case "document":
655
- return t.id;
655
+ return t._id;
656
656
  case "section": {
657
657
  const inTree = locateLoaded(ctx, t.heading_id);
658
658
  if (inTree)
@@ -663,18 +663,18 @@ function targetDoc(ctx, t) {
663
663
  return row.doc_id;
664
664
  }
665
665
  case "block": {
666
- const inTree = locateLoaded(ctx, t.id);
666
+ const inTree = locateLoaded(ctx, t._id);
667
667
  if (inTree)
668
668
  return inTree.docId;
669
- if (isNewSymbol(t.id))
670
- throw symbolMissing(ctx, t.id, "block");
671
- const row = ctx.store.db.prepare("SELECT doc_id FROM blocks WHERE block_id = ? AND deleted_commit IS NULL").get(t.id);
669
+ if (isNewSymbol(t._id))
670
+ throw symbolMissing(ctx, t._id, "block");
671
+ const row = ctx.store.db.prepare("SELECT doc_id FROM blocks WHERE block_id = ? AND deleted_commit IS NULL").get(t._id);
672
672
  if (!row)
673
- throw new MutationError("parent_missing", `parent ${t.id} not found`);
673
+ throw new MutationError("parent_missing", `parent ${t._id} not found`);
674
674
  return row.doc_id;
675
675
  }
676
676
  case "node":
677
- return nodeBlock(ctx, t.id).docId;
677
+ return nodeBlock(ctx, t._id).docId;
678
678
  }
679
679
  }
680
680
  function nodeBlock(ctx, nodeId) {
@@ -693,7 +693,7 @@ function placement(ctx, into, at) {
693
693
  case "section":
694
694
  return { parent: { heading: kernelBlockId(ctx, into.heading_id), scope: "section" }, at: kernel };
695
695
  case "block":
696
- return { parent: kernelBlockId(ctx, into.id), at: kernel };
696
+ return { parent: kernelBlockId(ctx, into._id), at: kernel };
697
697
  case "node":
698
698
  throw effectInvalid("a node is not a placement target");
699
699
  }
@@ -711,7 +711,7 @@ function checkRevision(ctx, docId, expect) {
711
711
  throw new MutationError("stale_plan", `document ${docId} is at revision ${base.currentRev ?? "null"}, not ${expect.revision}; re-plan`, {
712
712
  doc: docId,
713
713
  expected_revision: expect.revision,
714
- current: { revision: base.currentRev, hash: base.fileHash },
714
+ current: { revision: base.currentRev, _hash: base.fileHash },
715
715
  retriable: true,
716
716
  });
717
717
  }
@@ -745,7 +745,7 @@ function kernelExpect(e, contentHash) {
745
745
  function recordParentPin(ctx, doc, into, expect) {
746
746
  if (expect?.parent_children_hash === undefined)
747
747
  return;
748
- const parent = into.kind === "block" ? into.id : null;
748
+ const parent = into.kind === "block" ? into._id : null;
749
749
  ctx.parents.push({ doc, parent, parent_children_hash: expect.parent_children_hash });
750
750
  }
751
751
  // ---- lowering (§11.3, the table) -----------------------------------------------------
@@ -763,18 +763,18 @@ function describe(e) {
763
763
  }
764
764
  }
765
765
  function targetName(t) {
766
- return t.kind === "section" ? `section ${t.heading_id}` : `${t.kind} ${t.id}`;
766
+ return t.kind === "section" ? `section ${t.heading_id}` : `${t.kind} ${t._id}`;
767
767
  }
768
768
  /** The target kinds each slot admits (§11.1); `validateTarget` checks a literal, `resolveEffect` a bare `$new.n`. */
769
769
  const PLACEMENT_KINDS = ["document", "section", "block"];
770
770
  const DOCUMENT_KIND = ["document"];
771
771
  const BLOCK_KIND = ["block"];
772
772
  const SET_KINDS = ["document", "block", "node"];
773
- /** Resolve one validated effect's targets (§11.4): documents to `{ id, path }`, defaults made explicit; `i` names the effect in messages. */
773
+ /** Resolve one validated effect's targets (§11.4): documents to `{ _id, _path }`, defaults made explicit; `i` names the effect in messages. */
774
774
  export function resolveEffect(ctx, e, i = 0) {
775
775
  const here = `effects[${i}]`;
776
776
  const target = (t, kinds, member) => {
777
- const literal = t.kind === "symbol" ? resolveSymbolTarget(ctx, t.id, kinds, `${here}.${member}`) : t;
777
+ const literal = t.kind === "symbol" ? resolveSymbolTarget(ctx, t._id, kinds, `${here}.${member}`) : t;
778
778
  return literal.kind === "document" ? resolveDocumentTarget(ctx, literal) : literal;
779
779
  };
780
780
  const targets = (list) => list.map((t, k) => target(t, BLOCK_KIND, `targets[${k}]`));
@@ -839,10 +839,10 @@ function requireMarkdownForFrontmatter(ctx, docId, field) {
839
839
  throw frontmatterNotMarkdown(docPathOf(ctx, docId), d.format, field, isNewSymbol(docId) ? null : docId);
840
840
  }
841
841
  function frontmatterNotMarkdown(path, format, field, docId) {
842
- return new MutationError("type_mismatch", `frontmatter effects apply to markdown documents; ${referencePath(path)} is a ${format} document — its top-level mapping is edited with \`set content\` (the whole file) until frontmatter effects learn yaml and json`, { ...(docId !== null ? { doc: docId } : {}), path: referencePath(path), format, field });
842
+ return new MutationError("type_mismatch", `frontmatter effects apply to markdown documents; ${referencePath(path)} is a ${format} document — its top-level mapping is edited with \`set content\` (the whole file) until frontmatter effects learn yaml and json`, { ...(docId !== null ? { doc: docId } : {}), _path: referencePath(path), _format: format, field });
843
843
  }
844
844
  /**
845
- * spec/mutate §11.3 `set`/`unset frontmatter.<key>` (3.6, "Frontmatter
845
+ * spec/mutate §11.3 `set`/`unset _frontmatter.<key>` (3.6, "Frontmatter
846
846
  * entries"): the frontmatter is edited BY ENTRY. The document's head is cut
847
847
  * into its parts (`splitFrontmatterRaw`, the §11.3 split rule): a `set`
848
848
  * replaces the key's part — the lead lines (a comment documenting the key)
@@ -978,7 +978,7 @@ function focusOf(ctx, e) {
978
978
  return { docs: [], blocks: [], paths: [e.path] };
979
979
  case "insert": {
980
980
  const doc = targetDoc(ctx, e.into);
981
- const blocks = [...(e.into.kind === "block" ? [e.into.id] : e.into.kind === "section" ? [e.into.heading_id] : []), ...anchorIds(e.at)];
981
+ const blocks = [...(e.into.kind === "block" ? [e.into._id] : e.into.kind === "section" ? [e.into.heading_id] : []), ...anchorIds(e.at)];
982
982
  return { docs: [doc], blocks, paths: [] };
983
983
  }
984
984
  case "set":
@@ -986,62 +986,62 @@ function focusOf(ctx, e) {
986
986
  if (e.target.kind === "document") {
987
987
  const field = e.field === "content" ? "content" : e.field;
988
988
  return {
989
- scalar: { key: `document:${e.target.id}:${field}`, target: { kind: "document", id: e.target.id }, field, op: e.op, value: e.op === "set" ? e.value : undefined },
990
- docs: [e.target.id], blocks: [], paths: [],
991
- ...(field === "content" ? { wholeDoc: e.target.id, retiresDocs: [e.target.id], retiresBlocks: allBlocksOf(ctx, e.target.id) } : {}),
989
+ scalar: { key: `document:${e.target._id}:${field}`, target: { kind: "document", _id: e.target._id }, field, op: e.op, value: e.op === "set" ? e.value : undefined },
990
+ docs: [e.target._id], blocks: [], paths: [],
991
+ ...(field === "content" ? { wholeDoc: e.target._id, retiresDocs: [e.target._id], retiresBlocks: allBlocksOf(ctx, e.target._id) } : {}),
992
992
  };
993
993
  }
994
994
  if (e.target.kind === "block") {
995
- const { docId } = blockInfo(ctx, e.target.id);
995
+ const { docId } = blockInfo(ctx, e.target._id);
996
996
  return {
997
- scalar: { key: `block:${e.target.id}:${e.field}`, target: { kind: "block", id: e.target.id }, field: e.field, op: e.op, value: e.op === "set" ? e.value : undefined },
998
- docs: [docId], blocks: [e.target.id], paths: [],
999
- ...(e.field === "markdown" ? { retiresBlocks: descendantsOf(ctx, e.target.id) } : {}),
997
+ scalar: { key: `block:${e.target._id}:${e.field}`, target: { kind: "block", _id: e.target._id }, field: e.field, op: e.op, value: e.op === "set" ? e.value : undefined },
998
+ docs: [docId], blocks: [e.target._id], paths: [],
999
+ ...(e.field === "markdown" ? { retiresBlocks: descendantsOf(ctx, e.target._id) } : {}),
1000
1000
  };
1001
1001
  }
1002
1002
  const node = nodeTarget(e.target);
1003
- const { docId, blockId } = nodeBlock(ctx, node.id);
1003
+ const { docId, blockId } = nodeBlock(ctx, node._id);
1004
1004
  return {
1005
- scalar: { key: `node:${node.id}:${e.field}`, target: { kind: "node", id: node.id }, field: e.field, op: e.op, value: e.op === "set" ? e.value : undefined },
1005
+ scalar: { key: `node:${node._id}:${e.field}`, target: { kind: "node", _id: node._id }, field: e.field, op: e.op, value: e.op === "set" ? e.value : undefined },
1006
1006
  docs: [docId], blocks: [blockId], paths: [],
1007
1007
  };
1008
1008
  }
1009
1009
  case "move": {
1010
1010
  if ("targets" in e) {
1011
- const ids = e.targets.map((t) => blockTarget(t).id);
1011
+ const ids = e.targets.map((t) => blockTarget(t)._id);
1012
1012
  const { docs } = blocksOf(ids);
1013
1013
  const dst = targetDoc(ctx, e.into);
1014
- const blocks = [...ids, ...(e.into.kind === "block" ? [e.into.id] : e.into.kind === "section" ? [e.into.heading_id] : []), ...anchorIds(e.at)];
1014
+ const blocks = [...ids, ...(e.into.kind === "block" ? [e.into._id] : e.into.kind === "section" ? [e.into.heading_id] : []), ...anchorIds(e.at)];
1015
1015
  return { docs: [...new Set([...docs, dst])], blocks, paths: [] };
1016
1016
  }
1017
1017
  const t = docTarget(e.target);
1018
1018
  return {
1019
- scalar: { key: `document:${t.id}:path`, target: { kind: "document", id: t.id }, field: "path", op: "set", value: e.to },
1020
- docs: [t.id], blocks: [], paths: [e.to], retiresPaths: [t.path],
1019
+ scalar: { key: `document:${t._id}:path`, target: { kind: "document", _id: t._id }, field: "_path", op: "set", value: e.to },
1020
+ docs: [t._id], blocks: [], paths: [e.to], retiresPaths: [t._path],
1021
1021
  };
1022
1022
  }
1023
1023
  case "remove": {
1024
1024
  if ("targets" in e) {
1025
- const ids = e.targets.map((t) => blockTarget(t).id);
1025
+ const ids = e.targets.map((t) => blockTarget(t)._id);
1026
1026
  const { docs } = blocksOf(ids);
1027
1027
  return { docs, blocks: ids, paths: [], retiresBlocks: ids.flatMap((id) => [id, ...descendantsOf(ctx, id)]) };
1028
1028
  }
1029
1029
  const t = docTarget(e.target);
1030
- return { docs: [t.id], blocks: [], paths: [], retiresDocs: [t.id], retiresBlocks: allBlocksOf(ctx, t.id) };
1030
+ return { docs: [t._id], blocks: [], paths: [], retiresDocs: [t._id], retiresBlocks: allBlocksOf(ctx, t._id) };
1031
1031
  }
1032
1032
  case "split": {
1033
- const id = blockTarget(e.target).id;
1033
+ const id = blockTarget(e.target)._id;
1034
1034
  return { ...blocksOf([id]), paths: [] };
1035
1035
  }
1036
1036
  case "merge": {
1037
- const ids = e.targets.map((t) => blockTarget(t).id);
1037
+ const ids = e.targets.map((t) => blockTarget(t)._id);
1038
1038
  return { ...blocksOf(ids), paths: [], retiresBlocks: ids.slice(1).flatMap((id) => [id, ...descendantsOf(ctx, id)]) };
1039
1039
  }
1040
1040
  case "retarget": {
1041
1041
  const plan = linksRepair(ctx.store, ctx.repoId, e.repairs, e.path_glob !== undefined ? { pathGlob: e.path_glob } : {});
1042
1042
  const ids = plan.ops.map((o) => o.block);
1043
1043
  return {
1044
- scalars: ids.map((id) => ({ key: `block:${id}:markdown`, target: { kind: "block", id }, field: "markdown" })),
1044
+ scalars: ids.map((id) => ({ key: `block:${id}:markdown`, target: { kind: "block", _id: id }, field: "markdown" })),
1045
1045
  ...blocksOf(ids), paths: [],
1046
1046
  };
1047
1047
  }
@@ -1098,7 +1098,7 @@ function applyLowering(ctx, i, e) {
1098
1098
  case "set":
1099
1099
  case "unset": {
1100
1100
  if (e.target.kind === "document") {
1101
- const docId = e.target.id;
1101
+ const docId = e.target._id;
1102
1102
  checkRevision(ctx, docId, e.expect);
1103
1103
  if (e.field === "content") {
1104
1104
  if (e.op !== "set" || typeof e.value !== "string")
@@ -1109,7 +1109,7 @@ function applyLowering(ctx, i, e) {
1109
1109
  ctx.summary.push(`set content of ${docPathOf(ctx, docId)}`);
1110
1110
  return;
1111
1111
  }
1112
- // `frontmatter.<key>` — on a created document too (3.0): its tree is loaded, the patch is a head like any other.
1112
+ // `_frontmatter.<key>` — on a created document too (3.0): its tree is loaded, the patch is a head like any other.
1113
1113
  const key = FRONTMATTER_FIELD.exec(e.field)[1];
1114
1114
  requireMarkdownForFrontmatter(ctx, docId, e.field);
1115
1115
  patchFrontmatterKey(ctx, docId, key, e.op === "set" ? { value: e.value } : null);
@@ -1119,7 +1119,7 @@ function applyLowering(ctx, i, e) {
1119
1119
  if (e.op !== "set")
1120
1120
  throw effectInvalid("`unset` takes a document target");
1121
1121
  if (e.target.kind === "block") {
1122
- const id = e.target.id;
1122
+ const id = e.target._id;
1123
1123
  const { docId } = blockInfo(ctx, id);
1124
1124
  checkRevision(ctx, docId, e.expect);
1125
1125
  const hash = pin(ctx, id, e.expect?.content_hash);
@@ -1132,7 +1132,7 @@ function applyLowering(ctx, i, e) {
1132
1132
  }
1133
1133
  else {
1134
1134
  if (typeof e.value !== "boolean")
1135
- throw effectInvalid("`attrs.checked` takes a boolean value");
1135
+ throw effectInvalid("`_attrs.checked` takes a boolean value");
1136
1136
  pushOp(ctx, { op: "update", block: kernelBlockId(ctx, id), attrs: { checked: e.value }, expect: kernelExpect(e.expect, hash) });
1137
1137
  ctx.summary.push(`${e.value ? "check" : "uncheck"} ${name} in ${docPathOf(ctx, docId)}`);
1138
1138
  }
@@ -1140,20 +1140,20 @@ function applyLowering(ctx, i, e) {
1140
1140
  }
1141
1141
  // node: the adapter's editor → one update (macros.ts nodeSet)
1142
1142
  const node = nodeTarget(e.target);
1143
- const { docId, blockId } = nodeBlock(ctx, node.id);
1143
+ const { docId, blockId } = nodeBlock(ctx, node._id);
1144
1144
  checkRevision(ctx, docId, e.expect);
1145
- const ops = nodeSet(ctx.store, node.id, e.field, typeof e.value === "string" ? e.value : ctx.canonical(e.value));
1145
+ const ops = nodeSet(ctx.store, node._id, e.field, typeof e.value === "string" ? e.value : ctx.canonical(e.value));
1146
1146
  for (const op of ops) {
1147
1147
  const u = op;
1148
1148
  const hash = pin(ctx, blockId, e.expect?.content_hash ?? u.expect?.content_hash);
1149
1149
  pushOp(ctx, { ...u, expect: { ...(u.expect ?? {}), content_hash: hash } });
1150
1150
  }
1151
- ctx.summary.push(`set ${e.field} = ${ctx.canonical(e.value)} on node ${node.id} in ${docPathOf(ctx, docId)}`);
1151
+ ctx.summary.push(`set ${e.field} = ${ctx.canonical(e.value)} on node ${node._id} in ${docPathOf(ctx, docId)}`);
1152
1152
  return;
1153
1153
  }
1154
1154
  case "move": {
1155
1155
  if ("targets" in e) {
1156
- const ids = e.targets.map((t) => blockTarget(t).id);
1156
+ const ids = e.targets.map((t) => blockTarget(t)._id);
1157
1157
  const dst = targetDoc(ctx, e.into);
1158
1158
  checkRevision(ctx, dst, e.expect);
1159
1159
  recordParentPin(ctx, dst, e.into, e.expect);
@@ -1168,11 +1168,11 @@ function applyLowering(ctx, i, e) {
1168
1168
  return;
1169
1169
  }
1170
1170
  const t = docTarget(e.target);
1171
- if (isNewSymbol(t.id))
1172
- throw new MutationError("target_missing", `${t.id} is a document this plan creates; it cannot be moved in the same plan`, { symbol: t.id });
1173
- const base = readDocBase(ctx.store.db, t.id);
1171
+ if (isNewSymbol(t._id))
1172
+ throw new MutationError("target_missing", `${t._id} is a document this plan creates; it cannot be moved in the same plan`, { symbol: t._id });
1173
+ const base = readDocBase(ctx.store.db, t._id);
1174
1174
  if (!base)
1175
- throw docMissing(t.id);
1175
+ throw docMissing(t._id);
1176
1176
  if (findDoc(ctx.store, { repoId: ctx.repoId, path: e.to }))
1177
1177
  throw new MutationError("path_taken", `a document already exists at ${e.to}`, { path: e.to });
1178
1178
  if (ctx.absentPaths.includes(e.to))
@@ -1180,13 +1180,13 @@ function applyLowering(ctx, i, e) {
1180
1180
  if (ctx.docStore.exists(e.to))
1181
1181
  throw new MutationError("path_taken", `file already exists on disk at ${e.to}`, { path: e.to });
1182
1182
  ctx.absentPaths.push(e.to);
1183
- if (!ctx.docBases.has(t.id))
1184
- ctx.docBases.set(t.id, base);
1185
- const inbound = inboundLinksTo(ctx.store, ctx.repoId, t.id, t.path);
1183
+ if (!ctx.docBases.has(t._id))
1184
+ ctx.docBases.set(t._id, base);
1185
+ const inbound = inboundLinksTo(ctx.store, ctx.repoId, t._id, t._path);
1186
1186
  let retargeted = null;
1187
1187
  let links = "";
1188
1188
  if (e.retarget_inbound && inbound.length > 0) {
1189
- const plan = planInboundRetarget(ctx.store, t.id, t.path, e.to, inbound);
1189
+ const plan = planInboundRetarget(ctx.store, t._id, t._path, e.to, inbound);
1190
1190
  for (const op of plan.ops) {
1191
1191
  const u = op;
1192
1192
  pin(ctx, u.block, u.expect?.content_hash);
@@ -1195,13 +1195,13 @@ function applyLowering(ctx, i, e) {
1195
1195
  retargeted = { blocks: plan.blocks, docs: plan.docs };
1196
1196
  links = ` (retarget ${plural(inbound.length - plan.dangling.length, "link")} in ${plural(plan.docs.length, "document")})`;
1197
1197
  }
1198
- ctx.moves.push({ id: t.id, from: t.path, to: e.to, currentRev: base.currentRev, inbound, retargeted });
1199
- ctx.summary.push(`move ${referencePath(t.path)} → ${referencePath(e.to)}${links}`);
1198
+ ctx.moves.push({ id: t._id, from: t._path, to: e.to, currentRev: base.currentRev, inbound, retargeted });
1199
+ ctx.summary.push(`move ${referencePath(t._path)} → ${referencePath(e.to)}${links}`);
1200
1200
  return;
1201
1201
  }
1202
1202
  case "remove": {
1203
1203
  if ("targets" in e) {
1204
- const ids = e.targets.map((t) => blockTarget(t).id);
1204
+ const ids = e.targets.map((t) => blockTarget(t)._id);
1205
1205
  const expect = {};
1206
1206
  for (const id of ids) {
1207
1207
  const given = e.expect?.[id];
@@ -1214,19 +1214,19 @@ function applyLowering(ctx, i, e) {
1214
1214
  return;
1215
1215
  }
1216
1216
  const t = docTarget(e.target);
1217
- if (isNewSymbol(t.id))
1218
- throw new MutationError("target_missing", `${t.id} is a document this plan creates; it cannot be removed in the same plan`, { symbol: t.id });
1219
- const base = readDocBase(ctx.store.db, t.id);
1217
+ if (isNewSymbol(t._id))
1218
+ throw new MutationError("target_missing", `${t._id} is a document this plan creates; it cannot be removed in the same plan`, { symbol: t._id });
1219
+ const base = readDocBase(ctx.store.db, t._id);
1220
1220
  if (!base)
1221
- throw docMissing(t.id);
1222
- if (!ctx.docBases.has(t.id))
1223
- ctx.docBases.set(t.id, base);
1224
- ctx.tombstones.push({ id: t.id, path: t.path, currentRev: base.currentRev });
1225
- ctx.summary.push(`remove ${referencePath(t.path)}`);
1221
+ throw docMissing(t._id);
1222
+ if (!ctx.docBases.has(t._id))
1223
+ ctx.docBases.set(t._id, base);
1224
+ ctx.tombstones.push({ id: t._id, path: t._path, currentRev: base.currentRev });
1225
+ ctx.summary.push(`remove ${referencePath(t._path)}`);
1226
1226
  return;
1227
1227
  }
1228
1228
  case "split": {
1229
- const id = blockTarget(e.target).id;
1229
+ const id = blockTarget(e.target)._id;
1230
1230
  const { docId } = blockInfo(ctx, id);
1231
1231
  checkRevision(ctx, docId, e.expect);
1232
1232
  const hash = pin(ctx, id, e.expect?.content_hash);
@@ -1236,7 +1236,7 @@ function applyLowering(ctx, i, e) {
1236
1236
  return;
1237
1237
  }
1238
1238
  case "merge": {
1239
- const ids = e.targets.map((t) => blockTarget(t).id);
1239
+ const ids = e.targets.map((t) => blockTarget(t)._id);
1240
1240
  const expect = {};
1241
1241
  for (const id of ids) {
1242
1242
  const given = e.expect?.[id];
@@ -1256,7 +1256,7 @@ function applyLowering(ctx, i, e) {
1256
1256
  pin(ctx, u.block, u.expect?.content_hash);
1257
1257
  pushOp(ctx, u);
1258
1258
  }
1259
- const docs = new Set(plan.hits.map((h) => h.path)).size;
1259
+ const docs = new Set(plan.hits.map((h) => h._path)).size;
1260
1260
  const pair = e.repairs.length === 1 ? `${e.repairs[0].from} → ${e.repairs[0].to}` : `over ${plural(e.repairs.length, "pair")}`;
1261
1261
  ctx.summary.push(`retarget ${plural(plan.hits.length, "link")} ${pair} in ${plural(docs, "document")}`);
1262
1262
  return;
@@ -28,13 +28,13 @@ export declare function listsInsertItem(anchor: string, at: "start" | "end" | {
28
28
  } | {
29
29
  after: string;
30
30
  }, markdown: string): Op[];
31
- /** One rewritten block of a retarget/repair (spec/surface §4, 3.0): the block's
32
- * `id`, its document's `path` (the storage form here; the surface roots it),
33
- * and the raw before/after. */
31
+ /** One rewritten block of a retarget/repair (spec/surface §4, 3.0; 5.0 "underscore = engine"): the block's
32
+ * `_id`, its document's `_path` (the storage form here; the surface roots it),
33
+ * and the raw before/after (`oldRaw`/`newRaw` name roles). */
34
34
  export interface RetargetHit {
35
- id: string;
35
+ _id: string;
36
36
  /** repo-relative path of the block's document */
37
- path: string;
37
+ _path: string;
38
38
  oldRaw: string;
39
39
  newRaw: string;
40
40
  }
@@ -230,7 +230,7 @@ export function linksRepair(store, repoId, repairs, opts = {}) {
230
230
  c.tally.forEach((n, i) => { effective[i].hits += n; });
231
231
  const hash = rawHashOfBlock(store, blockId);
232
232
  ops.push({ op: "update", block: blockId, markdown: c.newRaw, ...(hash ? { expect: { content_hash: hash } } : {}) });
233
- hits.push({ id: blockId, path: c.path, oldRaw: c.oldRaw, newRaw: c.newRaw });
233
+ hits.push({ _id: blockId, _path: c.path, oldRaw: c.oldRaw, newRaw: c.newRaw });
234
234
  }
235
235
  return { ops, hits, pairs };
236
236
  }
@@ -41,9 +41,9 @@ function renderedBytes(store, row) {
41
41
  if (row.revId === null)
42
42
  return null;
43
43
  const at = readDocumentAtRevision(store, row.docId, row.revId);
44
- if (!at || !sha256(at.raw).equals(row.renderedHash))
44
+ if (!at || !sha256(at._raw).equals(row.renderedHash))
45
45
  return null;
46
- return at.raw;
46
+ return at._raw;
47
47
  }
48
48
  /**
49
49
  * Materialize one row through `docStore` and, when the file is as recorded,
@@ -109,7 +109,7 @@ function checkContentHash(block, expect, opIndex) {
109
109
  throw new MutationError("stale_expectation", "expect.content_hash required", {
110
110
  op_index: opIndex,
111
111
  block: block.id,
112
- current: { hash: rawHashHex(block.raw), markdown: block.raw },
112
+ current: { _hash: rawHashHex(block.raw), markdown: block.raw },
113
113
  retriable: true,
114
114
  });
115
115
  }
@@ -119,7 +119,7 @@ function checkContentHash(block, expect, opIndex) {
119
119
  op_index: opIndex,
120
120
  block: block.id,
121
121
  expected_content_hash: expect.content_hash,
122
- current: { hash: current, markdown: block.raw },
122
+ current: { _hash: current, markdown: block.raw },
123
123
  retriable: true,
124
124
  });
125
125
  }