@omgbase/core 0.27.1 → 0.29.0

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 (71) hide show
  1. package/dist/src/core/read/document.d.ts +11 -10
  2. package/dist/src/core/read/document.js +12 -11
  3. package/dist/src/core/read/nodes.d.ts +10 -10
  4. package/dist/src/core/read/nodes.js +11 -11
  5. package/dist/src/core/read/reader.d.ts +37 -24
  6. package/dist/src/core/read/reader.js +44 -31
  7. package/dist/src/graph/history.d.ts +25 -22
  8. package/dist/src/graph/history.js +17 -17
  9. package/dist/src/graph/inbound-links.d.ts +7 -7
  10. package/dist/src/graph/inbound-links.js +7 -7
  11. package/dist/src/graph/link-health.d.ts +9 -9
  12. package/dist/src/graph/link-health.js +9 -9
  13. package/dist/src/graph/links.d.ts +3 -3
  14. package/dist/src/graph/links.js +4 -4
  15. package/dist/src/mcp/arguments.d.ts +10 -3
  16. package/dist/src/mcp/arguments.js +32 -9
  17. package/dist/src/mcp/errors.d.ts +1 -1
  18. package/dist/src/mcp/graph.d.ts +22 -22
  19. package/dist/src/mcp/graph.js +53 -46
  20. package/dist/src/mcp/reference.d.ts +29 -1
  21. package/dist/src/mcp/reference.js +350 -222
  22. package/dist/src/mcp/server.d.ts +9 -0
  23. package/dist/src/mcp/server.js +169 -107
  24. package/dist/src/mutate/apply.d.ts +6 -6
  25. package/dist/src/mutate/apply.js +1 -1
  26. package/dist/src/mutate/canonical.d.ts +1 -1
  27. package/dist/src/mutate/canonical.js +2 -2
  28. package/dist/src/mutate/commit.d.ts +3 -3
  29. package/dist/src/mutate/commit.js +4 -4
  30. package/dist/src/mutate/docs.d.ts +1 -1
  31. package/dist/src/mutate/docs.js +8 -8
  32. package/dist/src/mutate/effects.d.ts +17 -17
  33. package/dist/src/mutate/effects.js +113 -113
  34. package/dist/src/mutate/macros.d.ts +5 -5
  35. package/dist/src/mutate/macros.js +1 -1
  36. package/dist/src/mutate/materialize.js +2 -2
  37. package/dist/src/mutate/ops.js +2 -2
  38. package/dist/src/mutate/opset.d.ts +7 -7
  39. package/dist/src/mutate/plan-update.js +2 -2
  40. package/dist/src/mutate/plan.d.ts +8 -8
  41. package/dist/src/mutate/plan.js +25 -25
  42. package/dist/src/mutate/select.d.ts +4 -4
  43. package/dist/src/mutate/select.js +52 -52
  44. package/dist/src/mutate/sugar.d.ts +10 -10
  45. package/dist/src/mutate/sugar.js +21 -21
  46. package/dist/src/oqx-js/context.d.ts +8 -4
  47. package/dist/src/oqx-js/context.js +206 -189
  48. package/dist/src/oqx-js/names.d.ts +28 -17
  49. package/dist/src/oqx-js/names.js +115 -78
  50. package/dist/src/oqx-js/planner.d.ts +4 -8
  51. package/dist/src/oqx-js/planner.js +6 -10
  52. package/dist/src/oqx-js/root-row.d.ts +6 -5
  53. package/dist/src/oqx-js/root-row.js +15 -16
  54. package/dist/src/oqx-js/run.d.ts +18 -17
  55. package/dist/src/oqx-js/run.js +57 -54
  56. package/dist/src/oqx-js/sql/translate.d.ts +5 -5
  57. package/dist/src/oqx-js/sql/translate.js +162 -161
  58. package/dist/src/oqx-js/store-index.d.ts +1 -1
  59. package/dist/src/oqx-js/store-index.js +28 -30
  60. package/dist/src/search/resolve.d.ts +2 -2
  61. package/dist/src/search/resolve.js +1 -1
  62. package/dist/src/search/rrf.js +2 -2
  63. package/dist/src/search/text.d.ts +5 -5
  64. package/dist/src/search/text.js +2 -2
  65. package/dist/src/spec-versions.d.ts +6 -6
  66. package/dist/src/spec-versions.js +6 -6
  67. package/dist/src/surface-paths.d.ts +18 -17
  68. package/dist/src/surface-paths.js +23 -21
  69. package/dist/src/sync/admin.d.ts +2 -2
  70. package/dist/src/sync/admin.js +1 -1
  71. package/package.json +1 -1
@@ -10,16 +10,16 @@ export type OpsetKind = "doc_update";
10
10
  export type PlanDisposition = "same" | "edited" | "moved" | "edited_moved" | "inserted" | "deleted" | "split_from" | "merged_into" | "copied_from" | "resurrected" | "bulk_rewrite" | "retiled";
11
11
  /** State the opset was planned against; applying against different state
12
12
  * fails. The document is named by its row attributes (spec/surface §1.2,
13
- * 3.0): `id`, `path`, and `hash` — its current hash at plan time;
13
+ * 5.0 "underscore = engine"): `_id`, `_path`, and `_hash` — its current hash at plan time;
14
14
  * `baseRevision` names a role and keeps its name. */
15
15
  export interface OpsetPrecondition {
16
16
  /** doc id the plan targets */
17
- id: string;
18
- path: string;
17
+ _id: string;
18
+ _path: string;
19
19
  /** the document's current revision id at plan time (null for a doc with no committed revision yet) */
20
20
  baseRevision: string | null;
21
- /** sha256 hex of the document's current rendered bytes (docs.file_hash) at plan time — its `hash` */
22
- hash: string;
21
+ /** sha256 hex of the document's current rendered bytes (docs.file_hash) at plan time — its `_hash` */
22
+ _hash: string;
23
23
  }
24
24
  /** One kernel op plus the identity consequence the planner attributes to it. */
25
25
  export interface PlanOp {
@@ -54,8 +54,8 @@ export interface Opset {
54
54
  version: 1;
55
55
  kind: OpsetKind;
56
56
  target: {
57
- id: string;
58
- path: string;
57
+ _id: string;
58
+ _path: string;
59
59
  };
60
60
  precondition: OpsetPrecondition;
61
61
  /** matcher version that produced the identity decisions (reproducibility) */
@@ -86,8 +86,8 @@ export function planUpdate(store, repoId, rootPath, docRef, content, opts = {})
86
86
  return {
87
87
  version: 1,
88
88
  kind: "doc_update",
89
- target: { id: docId, path: path },
90
- precondition: { id: docId, path: path, baseRevision: info.currentRev, hash: baseContentHash },
89
+ target: { _id: docId, _path: path },
90
+ precondition: { _id: docId, _path: path, baseRevision: info.currentRev, _hash: baseContentHash },
91
91
  matcherV: config.matcherV,
92
92
  ops,
93
93
  ...(fmChanged ? { frontmatter: { raw: targetFm } } : {}),
@@ -24,18 +24,18 @@ export type WireOp = Exclude<Op, {
24
24
  };
25
25
  /** The lowered form `execute` replays (§11.4): every key present, `[]` when empty. */
26
26
  export interface PlanOpset {
27
- /** `create` → the composed file, its document symbol and the index of the first kernel op planned after it (3.0; a later `set frontmatter.<k>` on the symbol is a head below) */
27
+ /** `create` → the composed file, its document symbol and the index of the first kernel op planned after it (3.0; a later `set _frontmatter.<k>` on the symbol is a head below) */
28
28
  creates: CreateEntry[];
29
29
  /** `move document` → the row/file rename (its retarget updates are in `ops`) */
30
30
  moves: {
31
- id: string;
31
+ _id: string;
32
32
  from: string;
33
33
  to: string;
34
34
  }[];
35
35
  /** `remove document` */
36
36
  tombstones: {
37
- id: string;
38
- path: string;
37
+ _id: string;
38
+ _path: string;
39
39
  }[];
40
40
  /** per document, the file head — everything before the first block (frontmatter incl. fences and separator) */
41
41
  set_frontmatter: {
@@ -48,14 +48,14 @@ export interface PlanOpset {
48
48
  export interface PlanPreconditions {
49
49
  /** the row triples (`spec/mutate` §4 step 5) of every document the plan touches, loaded ones first in load order */
50
50
  docs: {
51
- id: string;
52
- path: string;
51
+ _id: string;
52
+ _path: string;
53
53
  base_revision: string | null;
54
- hash: string | null;
54
+ _hash: string | null;
55
55
  }[];
56
56
  /** every block content hash the plan pins (caller-given or pinned from the live row), first-pin order */
57
57
  blocks: {
58
- id: string;
58
+ _id: string;
59
59
  content_hash: string;
60
60
  }[];
61
61
  /** the caller's destination-parent CAS pins */
@@ -116,19 +116,19 @@ export function planEffects(store, req) {
116
116
  const changeset = planner.finish();
117
117
  const opset = {
118
118
  creates: ctx.creates,
119
- moves: ctx.moves.map((m) => ({ id: m.id, from: m.from, to: m.to })),
120
- tombstones: ctx.tombstones.map((t) => ({ id: t.id, path: t.path })),
119
+ moves: ctx.moves.map((m) => ({ _id: m.id, from: m.from, to: m.to })),
120
+ tombstones: ctx.tombstones.map((t) => ({ _id: t.id, _path: t.path })),
121
121
  set_frontmatter: [...ctx.heads].map(([doc, raw]) => ({ doc, raw })),
122
122
  ops: ctx.ops.map(wireOp),
123
123
  };
124
- const docs = changeset.preconditions.map((p) => ({ id: p.id, path: p.path, base_revision: p.currentRev, hash: p.fileHash }));
124
+ const docs = changeset.preconditions.map((p) => ({ _id: p.id, _path: p.path, base_revision: p.currentRev, _hash: p.fileHash }));
125
125
  for (const [id, base] of ctx.docBases) {
126
- if (!docs.some((d) => d.id === id))
127
- docs.push({ id, path: base.path, base_revision: base.currentRev, hash: base.fileHash });
126
+ if (!docs.some((d) => d._id === id))
127
+ docs.push({ _id: id, _path: base.path, base_revision: base.currentRev, _hash: base.fileHash });
128
128
  }
129
129
  const preconditions = {
130
130
  docs,
131
- blocks: [...ctx.pins].map(([id, content_hash]) => ({ id, content_hash })),
131
+ blocks: [...ctx.pins].map(([id, content_hash]) => ({ _id: id, content_hash })),
132
132
  parents: ctx.parents,
133
133
  absent_paths: ctx.absentPaths,
134
134
  };
@@ -151,9 +151,9 @@ export function planEffects(store, req) {
151
151
  for (const t of ctx.tombstones)
152
152
  diffs[t.path] = { before: docStore.read(t.path) ?? "", after: "" };
153
153
  for (const r of revisions) {
154
- const moved = ctx.moves.find((m) => m.id === r.id);
154
+ const moved = ctx.moves.find((m) => m.id === r._id);
155
155
  if (moved)
156
- r.path = moved.to;
156
+ r._path = moved.to;
157
157
  }
158
158
  const preview = { results: changeset.results, revisions, diffs, summary: ctx.summary };
159
159
  if (ctx.identity.size > 0)
@@ -171,7 +171,7 @@ function requestOf(req) {
171
171
  /**
172
172
  * `conflicting_effects`: keep the later effect — the request minus the earlier
173
173
  * one; under a selector, a cross-hit collision narrows the selector to the
174
- * later hit (`from <root> where id == …`, `expect.max: 1`) and a same-hit
174
+ * later hit (`from <root> where _id == …`, `expect.max: 1`) and a same-hit
175
175
  * collision drops the earlier template. `stale_plan` at plan time (an
176
176
  * `expect.revision` that no longer holds): the same request with the current
177
177
  * revision in that guard. `target_missing { symbol }` for a `$new.n` no earlier
@@ -191,8 +191,8 @@ function planningFix(err, i, req, origins, symbols) {
191
191
  const ob = origins[b];
192
192
  if (oa === undefined || ob === undefined || oa.template_index === undefined)
193
193
  return undefined;
194
- if (oa.row.id !== ob.row.id)
195
- return fixRequest({ select: narrowedTo(rootOfId(ob.row.id), ob.row.id), effects, expect: { max: 1 } });
194
+ if (oa.row._id !== ob.row._id)
195
+ return fixRequest({ select: narrowedTo(rootOfId(ob.row._id), ob.row._id), effects, expect: { max: 1 } });
196
196
  return fixRequest({ ...requestOf(req), effects: effects.filter((_, k) => k !== oa.template_index) });
197
197
  }
198
198
  if (err.code === "stale_plan" && isRecord(err.data.current) && typeof err.data.current.revision === "string") {
@@ -386,14 +386,14 @@ function underLock(req, fn) {
386
386
  */
387
387
  function executeAdmitted(store, req, plan, docStore) {
388
388
  for (const d of plan.preconditions.docs) {
389
- const row = readDocBase(store.db, d.id);
389
+ const row = readDocBase(store.db, d._id);
390
390
  if (!row)
391
- throw new MutationError("doc_missing", `doc ${d.id} no longer exists`, { doc: d.id });
392
- if (row.path !== d.path || row.currentRev !== d.base_revision || row.fileHash !== d.hash) {
393
- throw new MutationError("stale_plan", `document ${d.id} changed since the plan was made; re-plan`, {
394
- doc: d.id,
391
+ throw new MutationError("doc_missing", `doc ${d._id} no longer exists`, { doc: d._id });
392
+ if (row.path !== d._path || row.currentRev !== d.base_revision || row.fileHash !== d._hash) {
393
+ throw new MutationError("stale_plan", `document ${d._id} changed since the plan was made; re-plan`, {
394
+ doc: d._id,
395
395
  expected_revision: d.base_revision,
396
- current: { revision: row.currentRev, hash: row.fileHash },
396
+ current: { revision: row.currentRev, _hash: row.fileHash },
397
397
  retriable: true,
398
398
  fix: replanFix(plan),
399
399
  });
@@ -418,7 +418,7 @@ function executeAdmitted(store, req, plan, docStore) {
418
418
  }),
419
419
  };
420
420
  const stat = req.omgbaseDir !== undefined;
421
- const moved = new Map(plan.opset.moves.map((m) => [m.id, m]));
421
+ const moved = new Map(plan.opset.moves.map((m) => [m._id, m]));
422
422
  const blockSymbols = plan.new_ids.filter((s) => s.kind === "block").map((s) => s.symbol);
423
423
  const planner = new ChangesetPlanner(store, req.repoId, req.omgbaseDir);
424
424
  const { effects, outcome } = observeMints((prefix, id) => {
@@ -459,8 +459,8 @@ function executeAdmitted(store, req, plan, docStore) {
459
459
  const effects = [];
460
460
  for (const m of plan.opset.moves) {
461
461
  const bytes = docStore.read(m.from) ?? "";
462
- const rev = readDocBase(store.db, m.id)?.currentRev ?? null;
463
- effects.push({ kind: "move_path", docId: m.id, from: m.from, to: m.to, currentRev: rev, inbound: inboundLinksTo(store, req.repoId, m.id, m.from), bytes });
462
+ const rev = readDocBase(store.db, m._id)?.currentRev ?? null;
463
+ effects.push({ kind: "move_path", docId: m._id, from: m.from, to: m.to, currentRev: rev, inbound: inboundLinksTo(store, req.repoId, m._id, m.from), bytes });
464
464
  if (stat)
465
465
  effects.push({ kind: "clear_stat", path: m.from }, { kind: "record_stat", path: m.to, bytes });
466
466
  }
@@ -477,10 +477,10 @@ function executeAdmitted(store, req, plan, docStore) {
477
477
  effects.push(e);
478
478
  }
479
479
  for (const t of plan.opset.tombstones) {
480
- const rev = readDocBase(store.db, t.id)?.currentRev ?? null;
481
- effects.push({ kind: "tombstone", docId: t.id, path: t.path, currentRev: rev, pool: false });
480
+ const rev = readDocBase(store.db, t._id)?.currentRev ?? null;
481
+ effects.push({ kind: "tombstone", docId: t._id, path: t._path, currentRev: rev, pool: false });
482
482
  if (stat)
483
- effects.push({ kind: "clear_stat", path: t.path });
483
+ effects.push({ kind: "clear_stat", path: t._path });
484
484
  }
485
485
  const outcome = commitChangeset(store, req.repoId, head, effects, ts, { docStore, ...(req.omgbaseDir !== undefined ? { omgbaseDir: req.omgbaseDir } : {}) });
486
486
  return { effects, outcome };
@@ -491,9 +491,9 @@ function executeAdmitted(store, req, plan, docStore) {
491
491
  let j = 0;
492
492
  for (const e of effects) {
493
493
  if (e.kind === "ingest_reconciled")
494
- revisions.push({ id: outcome.ingests[j++].docId, path: e.path, hash: sha256(e.source).toString("hex") });
494
+ revisions.push({ _id: outcome.ingests[j++].docId, _path: e.path, _hash: sha256(e.source).toString("hex") });
495
495
  else if (e.kind === "ingest_known")
496
- revisions.push({ id: outcome.ingests[j++].docId, path: e.path, hash: sha256(e.rendered).toString("hex") });
496
+ revisions.push({ _id: outcome.ingests[j++].docId, _path: e.path, _hash: sha256(e.rendered).toString("hex") });
497
497
  }
498
498
  // `$new.n` → minted id: the i-th block symbol is the i-th `b` the replay
499
499
  // minted (all before the transaction, in the plan's order — the aliases
@@ -22,8 +22,8 @@ export interface Selection {
22
22
  export interface EffectOrigin {
23
23
  template_index?: number;
24
24
  row: {
25
- id: string;
26
- path: string;
25
+ _id: string;
26
+ _path: string;
27
27
  };
28
28
  }
29
29
  export interface CompiledSelection {
@@ -56,8 +56,8 @@ export declare function desugarLeader(v: string, here: string): unknown;
56
56
  * Desugar one effect or template as written (§11.8 "Sugar"), before the shape
57
57
  * check and the admission: in every slot that admits an expression — the
58
58
  * strings `markdown`, `to`, `path`, `separator`, `at.before` / `at.after`,
59
- * the JSON `value`, each top-level `create.frontmatter` value, a target's `id`
60
- * / `path`, and a whole target (`into`, `target`, `targets[k]`) — a string
59
+ * the JSON `value`, each top-level `create.frontmatter` value, a target's `_id`
60
+ * / `_path`, and a whole target (`into`, `target`, `targets[k]`) — a string
61
61
  * beginning `-> ` becomes `{ oqx }` (or folds to its literal); a `-> ` string
62
62
  * anywhere else (`expect`, `field`, `op`, a section's `heading_id`, …) is
63
63
  * refused. Every other string is literal data, with no escaping and no `${`
@@ -42,9 +42,9 @@ function isRecord(v) {
42
42
  function isOqx(v) {
43
43
  return isRecord(v) && Object.keys(v).length === 1 && typeof v.oqx === "string";
44
44
  }
45
- /** A row rendered as a value by the runner (`{ id, path }` and nothing else, spec/surface §1.4). */
45
+ /** A row rendered as a value by the runner (`{ _id, _path }` and nothing else, spec/surface §1.4). */
46
46
  function isRowRef(v) {
47
- return isRecord(v) && Object.keys(v).length === 2 && typeof v.id === "string" && typeof v.path === "string";
47
+ return isRecord(v) && Object.keys(v).length === 2 && typeof v._id === "string" && typeof v._path === "string";
48
48
  }
49
49
  function describeValue(v) {
50
50
  if (v === null || v === undefined)
@@ -144,8 +144,8 @@ function refuseLeader(v, here, path, fix) {
144
144
  * Desugar one effect or template as written (§11.8 "Sugar"), before the shape
145
145
  * check and the admission: in every slot that admits an expression — the
146
146
  * strings `markdown`, `to`, `path`, `separator`, `at.before` / `at.after`,
147
- * the JSON `value`, each top-level `create.frontmatter` value, a target's `id`
148
- * / `path`, and a whole target (`into`, `target`, `targets[k]`) — a string
147
+ * the JSON `value`, each top-level `create.frontmatter` value, a target's `_id`
148
+ * / `_path`, and a whole target (`into`, `target`, `targets[k]`) — a string
149
149
  * beginning `-> ` becomes `{ oqx }` (or folds to its literal); a `-> ` string
150
150
  * anywhere else (`expect`, `field`, `op`, a section's `heading_id`, …) is
151
151
  * refused. Every other string is literal data, with no escaping and no `${`
@@ -174,11 +174,11 @@ export function desugarEffect(raw, index, request) {
174
174
  if (isLeader(t))
175
175
  out[key] = desugarLeader(t, `${here}.${key}`);
176
176
  else if (isRecord(t)) {
177
- for (const member of ["id", "path"])
177
+ for (const member of ["_id", "_path"])
178
178
  if (isLeader(t[member]))
179
179
  t[member] = desugarLeader(t[member], `${here}.${key}.${member}`);
180
180
  for (const k of Object.keys(t))
181
- if (k !== "id" && k !== "path")
181
+ if (k !== "_id" && k !== "_path")
182
182
  refuse(t[k], `${here}.${key}.${k}`, [key, k]);
183
183
  }
184
184
  };
@@ -191,10 +191,10 @@ export function desugarEffect(raw, index, request) {
191
191
  if (isLeader(t))
192
192
  list[k] = desugarLeader(t, `${here}.targets[${k}]`);
193
193
  else if (isRecord(t)) {
194
- if (isLeader(t.id))
195
- t.id = desugarLeader(t.id, `${here}.targets[${k}].id`);
194
+ if (isLeader(t._id))
195
+ t._id = desugarLeader(t._id, `${here}.targets[${k}]._id`);
196
196
  for (const m of Object.keys(t))
197
- if (m !== "id")
197
+ if (m !== "_id")
198
198
  refuse(t[m], `${here}.targets[${k}].${m}`, ["targets", String(k), m]);
199
199
  }
200
200
  });
@@ -328,7 +328,7 @@ function admitTemplate(raw, index, root, plain, fix) {
328
328
  probe[key] = null;
329
329
  slots.push({ name: key, kind: "json", here: `${here}.${key}`, path: [key], expression: src, item: expr(src, key), put: (e, v) => { e[key] = v; } });
330
330
  };
331
- // A target member: `{ kind, id: { oqx } }` or `{ kind: "document", path: { oqx } }`, or (3.2) the whole target
331
+ // A target member: `{ kind, _id: { oqx } }` or `{ kind: "document", _path: { oqx } }`, or (3.2) the whole target
332
332
  // `{ oqx }` — the identity the value names; absent → the hit when `defaultable` (never on a plain effect).
333
333
  // `probeKind` is a kind the slot admits, standing in for the whole-target expression's unknown kind.
334
334
  const targetSlot = (key, defaultable, probeKind, primary) => {
@@ -337,7 +337,7 @@ function admitTemplate(raw, index, root, plain, fix) {
337
337
  if (t === undefined) {
338
338
  if (!defaultable || plain)
339
339
  return null;
340
- probe[key] = { kind: HIT_KIND[root], id: "$sel" };
340
+ probe[key] = { kind: HIT_KIND[root], _id: "$sel" };
341
341
  return { key, list: false };
342
342
  }
343
343
  if (isOqx(t)) {
@@ -345,19 +345,19 @@ function admitTemplate(raw, index, root, plain, fix) {
345
345
  throw needsSelector(`${here}.${key}`);
346
346
  const src = t.oqx;
347
347
  delete base[key];
348
- probe[key] = { kind: probeKind, id: "$sel" };
348
+ probe[key] = { kind: probeKind, _id: "$sel" };
349
349
  slots.push({ name: key, kind: "target", here: `${here}.${key}`, path: [key], expression: src, item: expr(src, key), put: (e, v) => { e[key] = v; } });
350
350
  return null;
351
351
  }
352
352
  if (isRecord(t)) {
353
- const idX = isOqx(t.id);
354
- const pathX = isOqx(t.path);
353
+ const idX = isOqx(t._id);
354
+ const pathX = isOqx(t._path);
355
355
  if (idX && pathX)
356
- throw selectionInvalid(`${here}.${key}: give \`id\` or \`path\` as an expression, not both`);
356
+ throw selectionInvalid(`${here}.${key}: give \`_id\` or \`_path\` as an expression, not both`);
357
357
  if (idX || pathX) {
358
358
  if (plain && primary)
359
359
  throw needsSelector(`${here}.${key}`);
360
- const member = idX ? "id" : "path";
360
+ const member = idX ? "_id" : "_path";
361
361
  const src = t[member].oqx;
362
362
  const literal = { ...t };
363
363
  delete literal[member];
@@ -369,12 +369,12 @@ function admitTemplate(raw, index, root, plain, fix) {
369
369
  });
370
370
  }
371
371
  for (const k of Object.keys(t))
372
- if (k !== "id" && k !== "path" && containsOqx(t[k]))
372
+ if (k !== "_id" && k !== "_path" && containsOqx(t[k]))
373
373
  throw selectionInvalid(`${here}.${key}.${k}: \`{ oqx }\` is not allowed here`);
374
374
  }
375
375
  return null;
376
376
  };
377
- // A `targets` list: each element `{ kind: "block", id: { oqx } }` (or, 3.2, a whole `{ oqx }`) yields one block;
377
+ // A `targets` list: each element `{ kind: "block", _id: { oqx } }` (or, 3.2, a whole `{ oqx }`) yields one block;
378
378
  // absent → `[the hit]` when `defaultable`. The list is always the primary target of its op.
379
379
  const targetsSlot = (defaultable) => {
380
380
  slotKeys.add("targets");
@@ -382,7 +382,7 @@ function admitTemplate(raw, index, root, plain, fix) {
382
382
  if (list === undefined) {
383
383
  if (!defaultable || plain)
384
384
  return null;
385
- probe.targets = [{ kind: HIT_KIND[root], id: "$sel" }];
385
+ probe.targets = [{ kind: HIT_KIND[root], _id: "$sel" }];
386
386
  return { key: "targets", list: true };
387
387
  }
388
388
  if (Array.isArray(list)) {
@@ -392,7 +392,7 @@ function admitTemplate(raw, index, root, plain, fix) {
392
392
  throw needsSelector(`${here}.targets[${k}]`);
393
393
  const src = t.oqx;
394
394
  base.targets[k] = {};
395
- probe.targets[k] = { kind: "block", id: "$sel" };
395
+ probe.targets[k] = { kind: "block", _id: "$sel" };
396
396
  slots.push({
397
397
  name: `targets_${k}`, kind: "target", here: `${here}.targets[${k}]`, path: ["targets", String(k)], expression: src, item: expr(src, `targets[${k}]`),
398
398
  put: (e, v) => { e.targets[k] = v; },
@@ -401,23 +401,23 @@ function admitTemplate(raw, index, root, plain, fix) {
401
401
  }
402
402
  if (!isRecord(t))
403
403
  return;
404
- if (isOqx(t.path))
405
- throw selectionInvalid(`${here}.targets[${k}]: a block target has \`id\`, not \`path\``);
406
- if (isOqx(t.id)) {
404
+ if (isOqx(t._path))
405
+ throw selectionInvalid(`${here}.targets[${k}]: a block target has \`_id\`, not \`_path\``);
406
+ if (isOqx(t._id)) {
407
407
  if (plain)
408
- throw needsSelector(`${here}.targets[${k}].id`);
409
- const src = t.id.oqx;
408
+ throw needsSelector(`${here}.targets[${k}]._id`);
409
+ const src = t._id.oqx;
410
410
  const literal = { ...t };
411
- delete literal.id;
411
+ delete literal._id;
412
412
  base.targets[k] = literal;
413
- probe.targets[k].id = "$sel";
413
+ probe.targets[k]._id = "$sel";
414
414
  slots.push({
415
- name: `targets_${k}`, kind: "identity", here: `${here}.targets[${k}].id`, path: ["targets", String(k), "id"], expression: src, item: expr(src, `targets[${k}].id`),
416
- put: (e, v) => { const arr = e.targets; arr[k] = { ...arr[k], ...identityMember("id", v) }; },
415
+ name: `targets_${k}`, kind: "identity", here: `${here}.targets[${k}]._id`, path: ["targets", String(k), "_id"], expression: src, item: expr(src, `targets[${k}]._id`),
416
+ put: (e, v) => { const arr = e.targets; arr[k] = { ...arr[k], ...identityMember("_id", v) }; },
417
417
  });
418
418
  }
419
419
  for (const key of Object.keys(t))
420
- if (key !== "id" && containsOqx(t[key]))
420
+ if (key !== "_id" && containsOqx(t[key]))
421
421
  throw selectionInvalid(`${here}.targets[${k}].${key}: \`{ oqx }\` is not allowed here`);
422
422
  });
423
423
  }
@@ -505,10 +505,10 @@ function admitTemplate(raw, index, root, plain, fix) {
505
505
  validateEffect(probe, index, fix);
506
506
  return { index, base, slots, defaultTarget };
507
507
  }
508
- /** The `id`/`path` member a bound identity value supplies: a string as given, a row ref by its id. */
508
+ /** The `_id`/`_path` member a bound identity value supplies: a string as given, a row ref by its id. */
509
509
  function identityMember(member, v) {
510
510
  if (isRowRef(v))
511
- return { id: v.id };
511
+ return { _id: v._id };
512
512
  return { [member]: v };
513
513
  }
514
514
  // ---- the request ---------------------------------------------------------------------
@@ -624,7 +624,7 @@ export function compileSelection(store, repoId, select, expectRaw, templatesRaw)
624
624
  }
625
625
  // Row → effects: hits in result order × templates in order (k = h·T + t). A
626
626
  // selector with `flatten` (oqx 0.25; §11.8) fans out per FLATTENED row: each
627
- // hit is the parent row (its `id`/`path`), the slot items read the bound name
627
+ // hit is the parent row (its `_id`/`_path`), the slot items read the bound name
628
628
  // (`-> tag`) in the flattened scope, and `expect.max` — the page bound above —
629
629
  // counts the flattened hits.
630
630
  const effects = [];
@@ -632,7 +632,7 @@ export function compileSelection(store, repoId, select, expectRaw, templatesRaw)
632
632
  hits.forEach((hit) => {
633
633
  for (const t of templates) {
634
634
  const k = effects.length;
635
- const row = { id: hit.id, path: hit.path };
635
+ const row = { _id: hit._id, _path: hit._path };
636
636
  const origin = { template_index: t.index, row };
637
637
  effects.push(bind(hit, t, k, origin, root, request));
638
638
  origins.push(origin);
@@ -643,7 +643,7 @@ export function compileSelection(store, repoId, select, expectRaw, templatesRaw)
643
643
  ...(expect !== undefined ? { expect } : {}),
644
644
  templates: templatesRaw,
645
645
  cardinality: hits.length,
646
- identities: hits.map((h) => h.id),
646
+ identities: hits.map((h) => h._id),
647
647
  };
648
648
  return { effects, selection, origins };
649
649
  }
@@ -682,20 +682,20 @@ function slotName(t, slot) {
682
682
  */
683
683
  function ambiguousFix(request, t, s, hit, root, list) {
684
684
  const head = list[0];
685
- const first = isRowRef(head) ? head.id : head;
685
+ const first = isRowRef(head) ? head._id : head;
686
686
  if (typeof first !== "string")
687
687
  return {};
688
688
  const effects = withMember(request.effects, [String(t.index), ...s.path], first);
689
689
  if (request.select === undefined)
690
690
  return { fix: fixRequest({ effects }) };
691
- return { fix: fixRequest({ select: narrowedTo(root, hit.id), effects, expect: { max: 1 } }) };
691
+ return { fix: fixRequest({ select: narrowedTo(root, hit._id), effects, expect: { max: 1 } }) };
692
692
  }
693
693
  /** One hit × one template → the concrete effect (the binding table of §11.8). */
694
694
  function bind(hit, t, k, origin, root, request) {
695
695
  const e = clone(t.base);
696
696
  const data = { effect_index: k, ...(origin.template_index !== undefined ? { template_index: origin.template_index } : {}), row: origin.row };
697
697
  if (t.defaultTarget !== null) {
698
- const target = { kind: HIT_KIND[root], id: hit.id };
698
+ const target = { kind: HIT_KIND[root], _id: hit._id };
699
699
  e[t.defaultTarget.key] = t.defaultTarget.list ? [target] : target;
700
700
  }
701
701
  for (const s of t.slots) {
@@ -705,10 +705,10 @@ function bind(hit, t, k, origin, root, request) {
705
705
  case "identity":
706
706
  case "target": {
707
707
  if (absent || (Array.isArray(v) && v.length === 0)) {
708
- throw new MutationError("target_missing", `${s.here}: the expression yielded no identity for row ${hit.id}`, { ...data, expression: s.expression });
708
+ throw new MutationError("target_missing", `${s.here}: the expression yielded no identity for row ${hit._id}`, { ...data, expression: s.expression });
709
709
  }
710
710
  if (Array.isArray(v)) {
711
- throw new MutationError("ambiguous_target", `${s.here}: the expression yielded ${v.length} identities for row ${hit.id}; a target resolves to exactly one`, { ...data, expression: s.expression, count: v.length, ...ambiguousFix(request, t, s, hit, root, v) });
711
+ throw new MutationError("ambiguous_target", `${s.here}: the expression yielded ${v.length} identities for row ${hit._id}; a target resolves to exactly one`, { ...data, expression: s.expression, count: v.length, ...ambiguousFix(request, t, s, hit, root, v) });
712
712
  }
713
713
  if (typeof v !== "string" && !isRowRef(v))
714
714
  throw selectionInvalid(`${s.here}: the expression yielded ${describeValue(v)}, not an identity (a string or a row)`, { ...data, expression: s.expression });
@@ -717,7 +717,7 @@ function bind(hit, t, k, origin, root, request) {
717
717
  break;
718
718
  }
719
719
  // (3.2) a whole target: a row by its id's prefix, a string as a bare target (a path → document, an id → its kind).
720
- const ref = isRowRef(v) ? v.id : v;
720
+ const ref = isRowRef(v) ? v._id : v;
721
721
  if (!isRowRef(v) && !looksLikePath(ref) && !isTargetId(ref) && !isNewSymbol(ref)) {
722
722
  throw selectionInvalid(`${s.here}: the expression yielded ${describeValue(v)}, not an identity (a document path, an id or a row)`, { ...data, expression: s.expression });
723
723
  }
@@ -726,14 +726,14 @@ function bind(hit, t, k, origin, root, request) {
726
726
  }
727
727
  case "string":
728
728
  if (absent)
729
- throw selectionInvalid(`${s.here}: the expression yielded absent for row ${hit.id}`, { ...data, expression: s.expression });
729
+ throw selectionInvalid(`${s.here}: the expression yielded absent for row ${hit._id}`, { ...data, expression: s.expression });
730
730
  if (typeof v !== "string")
731
731
  throw selectionInvalid(`${s.here}: the expression yielded ${describeValue(v)}, expected a string`, { ...data, expression: s.expression });
732
732
  s.put(e, v);
733
733
  break;
734
734
  case "json":
735
735
  if (absent)
736
- throw selectionInvalid(`${s.here}: the expression yielded absent for row ${hit.id}${s.name === "value" ? " — use `unset` to remove a key" : ""}`, { ...data, expression: s.expression });
736
+ throw selectionInvalid(`${s.here}: the expression yielded absent for row ${hit._id}${s.name === "value" ? " — use `unset` to remove a key" : ""}`, { ...data, expression: s.expression });
737
737
  s.put(e, v);
738
738
  break;
739
739
  }
@@ -813,25 +813,25 @@ function scopeOf(store, repoId, e, i) {
813
813
  validateEffect(e, i); // an unknown op (or `retarget`): the shape check refuses it
814
814
  throw effectInvalid(`${here}: \`op\` ${JSON.stringify(e.op)} is not row-shaped`);
815
815
  }
816
- if (isOqx(v) || (isRecord(v) && (isOqx(v.id) || isOqx(v.path))))
816
+ if (isOqx(v) || (isRecord(v) && (isOqx(v._id) || isOqx(v._path))))
817
817
  throw needsSelector(`${here}.${key}`);
818
818
  const t = validateTarget(v, `${here}.${key}`, kinds);
819
819
  switch (t.kind) {
820
820
  case "symbol":
821
- throw selectionInvalid(`${here}.${key}: ${t.id} has no row yet — it is created by this plan; use a literal target or a selector`);
821
+ throw selectionInvalid(`${here}.${key}: ${t._id} has no row yet — it is created by this plan; use a literal target or a selector`);
822
822
  case "document": {
823
823
  const d = findDocumentTarget(store, repoId, t);
824
- return { root: "docs", id: d.id, missing: () => new MutationError("doc_missing", `doc ${d.id} not found`, { doc: d.id }) };
824
+ return { root: "docs", id: d._id, missing: () => new MutationError("doc_missing", `doc ${d._id} not found`, { doc: d._id }) };
825
825
  }
826
826
  case "block":
827
827
  return {
828
- root: "blocks", id: t.id,
829
- missing: () => (placement ? new MutationError("parent_missing", `parent ${t.id} not found`) : new MutationError("block_missing", `block ${t.id} not found`, { block: t.id })),
828
+ root: "blocks", id: t._id,
829
+ missing: () => (placement ? new MutationError("parent_missing", `parent ${t._id} not found`) : new MutationError("block_missing", `block ${t._id} not found`, { block: t._id })),
830
830
  };
831
831
  case "section":
832
832
  return { root: "blocks", id: t.heading_id, missing: () => new MutationError("parent_missing", `parent ${t.heading_id} not found`) };
833
833
  case "node":
834
- return { root: "nodes", id: t.id, missing: () => new MutationError("block_missing", `node ${t.id} not found`, {}) };
834
+ return { root: "nodes", id: t._id, missing: () => new MutationError("block_missing", `node ${t._id} not found`, {}) };
835
835
  }
836
836
  }
837
837
  /**
@@ -839,7 +839,7 @@ function scopeOf(store, repoId, e, i) {
839
839
  * then — when nothing is an expression — the shape check as before (3.0
840
840
  * byte-identical). Otherwise each effect with expressions is admitted as a
841
841
  * template over its own primary target's row: the effects are grouped by that
842
- * row's identity, one synthetic query per identity — `from <root> where id ==
842
+ * row's identity, one synthetic query per identity — `from <root> where _id ==
843
843
  * "<id>"` with the slot items appended, named `__omg_sel_<effectIndex>_<slot>`
844
844
  * — runs, and the row binds the slots exactly as a selector's hit would. The
845
845
  * concrete effects feed the lowering unchanged; `templates` is the list as
@@ -893,7 +893,7 @@ export function compilePlain(store, repoId, raw) {
893
893
  g.members.push(t);
894
894
  });
895
895
  for (const g of scopes.values()) {
896
- const q = parse(`from ${g.root} where id == ${JSON.stringify(g.id)}`);
896
+ const q = parse(`from ${g.root} where _id == ${JSON.stringify(g.id)}`);
897
897
  const items = [];
898
898
  for (const t of g.members)
899
899
  for (const s of t.slots)
@@ -903,7 +903,7 @@ export function compilePlain(store, repoId, raw) {
903
903
  if (hit === undefined)
904
904
  throw g.missing();
905
905
  for (const t of g.members) {
906
- const origin = { row: { id: hit.id, path: hit.path } };
906
+ const origin = { row: { _id: hit._id, _path: hit._path } };
907
907
  effects[t.index] = bind(hit, t, t.index, origin, g.root, request);
908
908
  origins[t.index] = origin;
909
909
  }
@@ -8,10 +8,10 @@ export interface Sugar {
8
8
  effects: Effect[];
9
9
  reason?: string;
10
10
  }
11
- /** A live document as the builders address it: its id and its storage-form path (the reasons spell the path). */
11
+ /** A live document as the builders address it: its row attributes `_id` and `_path` (the storage-form path; the reasons spell it). */
12
12
  export interface SugarDoc {
13
- id: string;
14
- path: string;
13
+ _id: string;
14
+ _path: string;
15
15
  }
16
16
  /** A caller's guard as an effect guard: absent stays absent, an empty object stays an empty guard (the planner then pins what it needs). */
17
17
  export declare function guardOf(e: {
@@ -40,21 +40,21 @@ export declare function moveDocEffects(doc: SugarDoc, toPath: string, retargetIn
40
40
  /** `docs_delete` / `omg rm --doc`: one `remove { target: document }`; reason `delete <path>`. */
41
41
  export declare function deleteDocEffects(doc: SugarDoc): Sugar;
42
42
  /**
43
- * `docs_set_meta` / `omg meta`: one `set`/`unset frontmatter.<k>` per key, `unset`
43
+ * `docs_set_meta` / `omg meta`: one `set`/`unset _frontmatter.<k>` per key, `unset`
44
44
  * winning a key named in both (the legacy merge spread `set` then deleted `unset`);
45
45
  * reason `set_meta <path>`. An empty patch yields no effect — the caller refuses
46
46
  * it (`filter_invalid` "pass `set` and/or `unset`", spec/surface §9).
47
47
  */
48
48
  export declare function setMetaEffects(doc: SugarDoc, set: Record<string, unknown> | undefined, unset: readonly string[] | undefined): Sugar;
49
49
  /** `docs_update` / `omg update <doc>`: one `set { document, content }`; the reason is the caller's. */
50
- export declare function updateContentEffects(doc: Pick<SugarDoc, "id">, content: string, reason?: string): Sugar;
50
+ export declare function updateContentEffects(doc: Pick<SugarDoc, "_id">, content: string, reason?: string): Sugar;
51
51
  /** `blocks_insert` / `omg insert`: `insert into <document|block> at …`; the caller's `expect` is the destination-parent CAS. */
52
52
  export declare function insertEffects(into: EffectTarget, at: EffectAt, markdown: string, expect?: {
53
53
  parent_children_hash?: string | undefined;
54
54
  }): Sugar;
55
55
  /**
56
56
  * `blocks_update` / `omg update <block>` / `omg edit`: `set markdown` and/or `set
57
- * attrs.checked`. The caller's `expect` guards the FIRST effect (the content CAS
57
+ * _attrs.checked`. The caller's `expect` guards the FIRST effect (the content CAS
58
58
  * is checked once, against the bytes the caller saw); a second effect is pinned
59
59
  * by the planner to the block as the first left it. Nothing to set → no effect
60
60
  * (the caller refuses it).
@@ -77,7 +77,7 @@ export declare function removeBlocksEffects(ids: readonly string[]): Sugar;
77
77
  export declare function splitEffects(id: string, at: readonly number[]): Sugar;
78
78
  /** `blocks_merge` / `omg merge`: one `merge { targets, separator? }`. */
79
79
  export declare function mergeEffects(ids: readonly string[], separator?: string): Sugar;
80
- /** `blocks_check` / `omg done`: N × `set attrs.checked` (a block named twice is one write — the planner drops the duplicate). */
80
+ /** `blocks_check` / `omg done`: N × `set _attrs.checked` (a block named twice is one write — the planner drops the duplicate). */
81
81
  export declare function checkEffects(ids: readonly string[], checked: boolean): Sugar;
82
82
  /** `nodes_set` / `omg node set`: one `set { node, field: prop, value }`. */
83
83
  export declare function nodeSetEffects(nodeId: string, prop: string, value: string): Sugar;
@@ -108,10 +108,10 @@ export declare function applySugar(store: Store, request: SugarRequest & {
108
108
  plan: Plan;
109
109
  receipt?: Receipt;
110
110
  };
111
- /** The document-operation result the `omg` document verbs and the fixture `docs` step record (`spec/mutate` §9): `{ id, path, committed }`, the path in the storage form. */
111
+ /** The document-operation result the `omg` document verbs and the fixture `docs` step record (`spec/mutate` §9): `{ _id, _path, committed }`, the path in the storage form. */
112
112
  export interface DocOpOutcome {
113
- id: string;
114
- path: string;
113
+ _id: string;
114
+ _path: string;
115
115
  committed: true;
116
116
  }
117
117
  /** `docs_create`'s document is the receipt's first revision (`new_ids["$new.0"]` is the same id); every other operation's is the one addressed. */