@omgbase/core 0.28.0 → 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
@@ -38,40 +38,47 @@ function tagAll(rows, t) {
38
38
  r[TARGET] = t;
39
39
  return rows;
40
40
  }
41
- // THE NAME RULE (spec/surface §1.2, 3.0). `$` belongs to OQX: `$it`, `$key`,
41
+ // THE NAME RULE (spec/surface §1.2, 5.0: "underscore = engine"). On every
42
+ // record kind a BARE name is a user-authored entry and an UNDERSCORE-prefixed
43
+ // name is engine-provided, however derived — the prefix declares an engine
44
+ // field on a record, and the bare namespace is the user's on every kind (any
45
+ // record may carry user data later, so docs, blocks, nodes, links, revisions
46
+ // and the root row all follow it now). `$` belongs to OQX: `$it`, `$key`,
42
47
  // `$depth`, `$stop`, `$leaf`, `$frontier`, `$ordinal` are values the LANGUAGE
43
- // injects into a scope; omgbase adds no `$` names. Every engine attribute is a
44
- // bare snake_case name that WINS on collision — the convention the relations
45
- // (`children`, `blocks`, `out_links`, `doc`, `frontmatter`, …) always followed:
46
- // a bare name that is an engine attribute reads the engine's value, never the
47
- // author's. The attributes: every row's `id`/`path`; docs' and blocks' `raw`/
48
- // `hash`/`updated_at`/`created_at` (4.10: the commit `ts` of the thing's first
49
- // revision / earliest `block_changes` row — "first observed by omgbase", not a
50
- // filesystem time); docs' `title` (the computed title) and `tags` (authored
51
- // tags ∪ body hashtags) and `format`; a foreign key named for what it is
52
- // (`doc_id`, `block_id`, `via_id`, `dst_id`, `dst_path`, `uri`), a column its
53
- // column (`type`, `text`, `depth`, `ordinal`, `kind`, `predicate`, `field`).
54
- // User data is bare too, read by any non-colliding key; where it collides it is
55
- // reached through a source handle — `data` (the merged authored view:
56
- // frontmatter + inline by the property rule, exactly the read a bare
57
- // non-colliding key performs), `frontmatter`, `inline` — so `data.path` is the
58
- // author's `path:` key, `data.tags` the authored list alone, `data.title` the
59
- // authored title. There is no loud-error set: a bare `id`/`path`/`updated_at`/
60
- // `hash`/`raw` is simply the engine's attribute (before this batch of 3.0 the
61
- // attributes were `$`-prefixed and the bare spelling was an error, §9). A bare
62
- // target name — `docs`, `links`, … — is a loud error of its own (root-row.ts).
63
- // `revisions` on a document is a RELATION (its revision list, §1.2, 4.7) in
64
- // `rel.docs` below, and names.ts's `RELATIONS.docs` knows it as one.
65
- const DOC_ATTRIBUTES = new Set(["id", "path", "raw", "hash", "updated_at", "created_at", "title", "tags", "format"]);
66
- const BLOCK_ATTRIBUTES = new Set(["id", "path", "raw", "hash", "updated_at", "created_at", "doc_id", "type", "text", "attrs", "depth", "ordinal"]);
67
- const NODE_ATTRIBUTES = new Set(["id", "path", "doc_id", "block_id", "kind", "name", "value", "attrs"]);
68
- const EDGE_ATTRIBUTES = new Set(["id", "path", "doc_id", "block_id", "via_id", "dst_id", "dst_path", "uri", "field", "dst_kind", "predicate", "provenance", "anchor"]);
48
+ // injects into a scope; omgbase adds no `$` names. The engine names: every
49
+ // row's `_id`/`_path`; docs' and blocks' `_raw`/`_hash`/`_updated_at`/
50
+ // `_created_at` (4.10: the commit `ts` of the thing's first revision / earliest
51
+ // `block_changes` row — "first observed by omgbase", not a filesystem time);
52
+ // docs' `_title` (the computed title) and `_tags` (authored tags ∪ body
53
+ // hashtags) and `_format`; a foreign key named for what it is (`_doc_id`,
54
+ // `_block_id`, `_via_id`, `_dst_id`, `_dst_path`, `_uri`), a column its column
55
+ // (`_type`, `_text`, `_depth`, `_ordinal`, `_kind`, `_predicate`, `_field`); the
56
+ // handles `_data`/`_frontmatter`/`_inline`, the reach-throughs `_doc`/`_block`/
57
+ // `_dst` and the relations (`_blocks`, `_children`, `_out_links`, …). A bare
58
+ // name is ALWAYS the author's: on a document a property (frontmatter + inline
59
+ // by the property rule — `title`, `tags`, `id`, `path`, `doc` included; nothing
60
+ // is reserved), on a block or node an `attrs` key (`checked`). `_data.<k>` is
61
+ // the same merged read as the bare `<k>` (kept as the handle for
62
+ // `entries(_data)`), `_frontmatter.<k>` / `_inline.<k>` one source each — and
63
+ // the only way to reach an authored key that itself begins with `_`. The self
64
+ // aliases (`doc` on a document, `block` on a block, `section` on a node) are
65
+ // gone: `$it` is the self. Collections stay bare (`from docs`, `^docs`) — a
66
+ // root name names a collection, not a row property — and so do the row
67
+ // functions in call position (`text("x")`, `under(…)`): a call is never an
68
+ // entry read. A bare target name — `docs`, `links`, … — inside a block is a
69
+ // loud error of its own (root-row.ts). `_revisions` on a document is a
70
+ // RELATION (its revision list, §1.2, 4.7) in `rel.docs` below, and names.ts's
71
+ // `RELATIONS.docs` knows it as one.
72
+ const DOC_ATTRIBUTES = new Set(["_id", "_path", "_raw", "_hash", "_updated_at", "_created_at", "_title", "_tags", "_format"]);
73
+ const BLOCK_ATTRIBUTES = new Set(["_id", "_path", "_raw", "_hash", "_updated_at", "_created_at", "_doc_id", "_type", "_text", "_attrs", "_depth", "_ordinal"]);
74
+ const NODE_ATTRIBUTES = new Set(["_id", "_path", "_doc_id", "_block_id", "_kind", "_name", "_value", "_attrs"]);
75
+ const EDGE_ATTRIBUTES = new Set(["_id", "_path", "_doc_id", "_block_id", "_via_id", "_dst_id", "_dst_path", "_uri", "_field", "_dst_kind", "_predicate", "_provenance", "_anchor"]);
69
76
  // The history target (spec/surface §1.2, 4.7): a revision row's own columns
70
- // under the names `docs_history` already uses (`rev`, `seq`, `commit`, `origin`,
71
- // `actor`, `hash`), the commit's `ts` and `reason`, `is_current`, and (4.9)
72
- // `archived` — the revision's bodies live in an archive segment (spec/store §7
77
+ // under the names `docs_history` uses (`_rev`, `_seq`, `_commit`, `_origin`,
78
+ // `_actor`, `_hash`), the commit's `_ts` and `_reason`, `_is_current`, and (4.9)
79
+ // `_archived` — the revision's bodies live in an archive segment (spec/store §7
73
80
  // "Retention"; `docs_read { at }` reads through it when attached).
74
- const REVISION_ATTRIBUTES = new Set(["id", "path", "rev", "seq", "doc_id", "commit", "ts", "origin", "actor", "reason", "hash", "is_current", "archived"]);
81
+ const REVISION_ATTRIBUTES = new Set(["_id", "_path", "_rev", "_seq", "_doc_id", "_commit", "_ts", "_origin", "_actor", "_reason", "_hash", "_is_current", "_archived"]);
75
82
  /** The engine attributes per kind (spec/surface §1.2 table). Exported so the
76
83
  * translator's tests pin the two against each other. */
77
84
  export const ATTRIBUTES = {
@@ -85,9 +92,21 @@ export const ATTRIBUTES = {
85
92
  // `current_rev`.
86
93
  const REVISION_COLS = "r.rev_id, r.doc_id, r.seq, r.rendered_hash, r.path, r.commit_id, c.ts, c.origin, c.actor, c.reason, (d.current_rev = r.rev_id) AS is_current, (r.archive IS NOT NULL) AS archived";
87
94
  const REVISION_FROM = "revisions r JOIN docs d ON d.doc_id = r.doc_id JOIN commits c ON c.commit_id = r.commit_id";
88
- /** The source handles of a document (§1.2): `data` is the merged authored view
95
+ /** The source handles of a document (§1.2): `_data` is the merged authored view
89
96
  * (frontmatter + inline), the other two one source each. */
90
- export const SOURCE_HANDLES = new Set(["data", "frontmatter", "inline"]);
97
+ export const SOURCE_HANDLES = new Set(["_data", "_frontmatter", "_inline"]);
98
+ /** The `properties.source` a handle narrows a read to: `_data` → both authored sources (null). */
99
+ export function handleSource(handle) {
100
+ return handle === "_data" ? null : handle.slice(1);
101
+ }
102
+ /** The reach-through handles per kind (§1.2) — each a statically single row of another kind. */
103
+ export const REACH_THROUGHS = {
104
+ docs: new Set(),
105
+ blocks: new Set(["_doc"]),
106
+ nodes: new Set(["_doc", "_block"]),
107
+ links: new Set(["_doc", "_dst", "_block"]),
108
+ revisions: new Set(["_doc"]),
109
+ };
91
110
  /** The value of a statically SINGLE relation that has no row — `dst` on an
92
111
  * external or dangling link, `block` on a node without one (oqx 0.20, SEMANTICS
93
112
  * §3): an object the engine treats as one row with no rows, so `dst { title }`
@@ -314,23 +333,23 @@ export function makeStoreContext(store, repoId, opts = {}) {
314
333
  // ---- relations (return row arrays) ----------------------------------------
315
334
  const rel = {
316
335
  docs: {
317
- nodes: (r) => docNodesInOrder(r.doc_id, r.path),
318
- blocks: (r) => tagAll(docBlocksPreorder(r.doc_id, r.path).map((x) => x.row), "blocks"),
336
+ _nodes: (r) => docNodesInOrder(r.doc_id, r.path),
337
+ _blocks: (r) => tagAll(docBlocksPreorder(r.doc_id, r.path).map((x) => x.row), "blocks"),
319
338
  // A document's revision list (spec/surface §1.2, 4.7), oldest first.
320
- revisions: (r) => docRevisions(r.doc_id),
339
+ _revisions: (r) => docRevisions(r.doc_id),
321
340
  // The linked / linking DOCUMENTS are reached through the link rows (3.0
322
- // follow-up, §9: the `out` / `in` relations are gone): `out_links collect
323
- // distinct { from dst }` and `in_links collect distinct { from doc }`.
324
- out_links: (r) => tagAll(all(`SELECT e.*, ? AS __path FROM edges e WHERE e.src_doc = ? AND e.to_commit IS NULL ORDER BY e.predicate, e.edge_id`, r.path, r.doc_id), "links"),
325
- in_links: (r) => tagAll(all(`SELECT e.*, d.path AS __path FROM edges e JOIN docs d ON d.doc_id = e.src_doc
341
+ // follow-up, §9: the `out` / `in` relations are gone): `_out_links collect
342
+ // distinct { from _dst }` and `_in_links collect distinct { from _doc }`.
343
+ _out_links: (r) => tagAll(all(`SELECT e.*, ? AS __path FROM edges e WHERE e.src_doc = ? AND e.to_commit IS NULL ORDER BY e.predicate, e.edge_id`, r.path, r.doc_id), "links"),
344
+ _in_links: (r) => tagAll(all(`SELECT e.*, d.path AS __path FROM edges e JOIN docs d ON d.doc_id = e.src_doc
326
345
  WHERE e.dst_node = ? AND e.to_commit IS NULL AND d.deleted_commit IS NULL ORDER BY e.predicate, e.edge_id`, r.doc_id), "links"),
327
346
  },
328
347
  blocks: {
329
- children: (r) => tagAll(all(`SELECT b.*, ? AS __path FROM blocks b WHERE b.parent_block = ? AND b.deleted_commit IS NULL ORDER BY b.ordinal, b.block_id`, r.__path, r.block_id), "blocks"),
330
- nodes: (r) => tagAll(all(`SELECT n.*, ? AS __path FROM nodes n WHERE n.block_id = ? ORDER BY n.span_start, n.node_id`, r.__path, r.block_id), "nodes"),
331
- out_links: (r) => tagAll(all(`SELECT e.*, ? AS __path FROM edges e WHERE e.src_block = ? AND e.to_commit IS NULL ORDER BY e.predicate, e.edge_id`, r.__path, r.block_id), "links"),
348
+ _children: (r) => tagAll(all(`SELECT b.*, ? AS __path FROM blocks b WHERE b.parent_block = ? AND b.deleted_commit IS NULL ORDER BY b.ordinal, b.block_id`, r.__path, r.block_id), "blocks"),
349
+ _nodes: (r) => tagAll(all(`SELECT n.*, ? AS __path FROM nodes n WHERE n.block_id = ? ORDER BY n.span_start, n.node_id`, r.__path, r.block_id), "nodes"),
350
+ _out_links: (r) => tagAll(all(`SELECT e.*, ? AS __path FROM edges e WHERE e.src_block = ? AND e.to_commit IS NULL ORDER BY e.predicate, e.edge_id`, r.__path, r.block_id), "links"),
332
351
  // enclosing section node(s): md:section whose range contains this block's top ordinal.
333
- section: (r) => {
352
+ _section: (r) => {
334
353
  const top = topOrdinal(r);
335
354
  return tagAll(all(`SELECT n.*, ? AS __path FROM nodes n WHERE n.doc_id = ? AND n.kind = 'md:section'
336
355
  AND json_extract(n.attrs,'$.first_ordinal') <= ? AND json_extract(n.attrs,'$.last_ordinal') >= ?
@@ -338,8 +357,8 @@ export function makeStoreContext(store, repoId, opts = {}) {
338
357
  },
339
358
  },
340
359
  nodes: {
341
- // section.* — the node interpreted as a section (range attrs).
342
- blocks: (r) => {
360
+ // the node interpreted as a section (range attrs).
361
+ _blocks: (r) => {
343
362
  const f = jattr(r, "first_ordinal"), l = jattr(r, "last_ordinal");
344
363
  if (f == null || l == null)
345
364
  return [];
@@ -347,7 +366,7 @@ export function makeStoreContext(store, repoId, opts = {}) {
347
366
  const kept = docBlocksPreorder(r.doc_id, r.__path).filter((x) => x.top >= f && x.top <= l).map((x) => x.row);
348
367
  return tagAll(kept, "blocks");
349
368
  },
350
- subsections: (r) => {
369
+ _subsections: (r) => {
351
370
  const f = jattr(r, "first_ordinal"), l = jattr(r, "last_ordinal"), lvl = jattr(r, "level");
352
371
  if (f == null)
353
372
  return [];
@@ -355,7 +374,7 @@ export function makeStoreContext(store, repoId, opts = {}) {
355
374
  AND json_extract(n.attrs,'$.first_ordinal') >= ? AND json_extract(n.attrs,'$.last_ordinal') <= ?
356
375
  AND json_extract(n.attrs,'$.level') > ? ORDER BY json_extract(n.attrs,'$.first_ordinal'), n.node_id`, r.__path, r.doc_id, f, l, lvl), "nodes");
357
376
  },
358
- children: (r) => {
377
+ _children: (r) => {
359
378
  const f = jattr(r, "first_ordinal"), l = jattr(r, "last_ordinal"), lvl = jattr(r, "level");
360
379
  if (f == null)
361
380
  return [];
@@ -382,12 +401,12 @@ export function makeStoreContext(store, repoId, opts = {}) {
382
401
  // (spec/surface §1, 3.0): the owner is handed out as a LAZY row whose identity
383
402
  // columns are already known from the child — a block's/node's `doc_id` and
384
403
  // `__path` name its document, an edge's `src_doc` and `__path` its source doc,
385
- // a node's `block_id` (+ `doc_id`, `__path`) its block — so `doc.id` and
386
- // `doc.path` (and `block.id`/`block.path`/`block.doc_id` on a node) read
387
- // those columns and run NO statement, exactly like the bare `doc_id`; the full
388
- // owner row is loaded on the first read of anything else (`doc.layer`,
389
- // `block.text`). The translator mirrors this in SQL (`doc.id` → the FK
390
- // column). A Proxy over the backing object so the engine sees a plain tagged
404
+ // a node's `block_id` (+ `doc_id`, `__path`) its block — so `_doc._id` and
405
+ // `_doc._path` (and `_block._id`/`_block._path`/`_block._doc_id` on a node)
406
+ // read those columns and run NO statement, exactly like the bare `_doc_id`;
407
+ // the full owner row is loaded on the first read of anything else
408
+ // (`_doc.layer`, `_block._text`). The translator mirrors this in SQL
409
+ // (`_doc._id` → the FK column). A Proxy over the backing object so the engine sees a plain tagged
391
410
  // row either way (indexing, rendering, `rowRef`).
392
411
  const lazyOwner = (known, t, load) => {
393
412
  const backing = tag({ ...known }, t);
@@ -428,20 +447,20 @@ export function makeStoreContext(store, repoId, opts = {}) {
428
447
  return tag(one(`SELECT * FROM docs WHERE doc_id = ?`, id), "docs");
429
448
  return lazyOwner({ doc_id: id, path }, "docs", () => one(`SELECT * FROM docs WHERE doc_id = ?`, id));
430
449
  };
431
- // `dst` on a link row (spec/surface §1.2, 3.0 follow-up): the destination
432
- // DOCUMENT row when the link points at one (`dst_kind == "document"` and the
450
+ // `_dst` on a link row (spec/surface §1.2, 3.0 follow-up): the destination
451
+ // DOCUMENT row when the link points at one (`_dst_kind == "document"` and the
433
452
  // document exists — a phantom target has the kind and no row), read by
434
- // `dst_id` exactly as `dst_path` is; absent for a `block` or `external`
435
- // destination (never reached into). `doc` stays the source document.
453
+ // `_dst_id` exactly as `_dst_path` is; absent for a `block` or `external`
454
+ // destination (never reached into). `_doc` stays the source document.
436
455
  const destinationDoc = (row) => {
437
456
  if (row.dst_kind !== "document")
438
457
  return undefined;
439
458
  return tag(one(`SELECT * FROM docs WHERE doc_id = ?`, row.dst_node), "docs");
440
459
  };
441
- // `doc` on a revision row (spec/surface §1.2, 4.7): the document's CURRENT
442
- // live row — the revision's `path` may be an earlier one — and absent (the
443
- // single-with-no-row marker, as `dst` on a dangling link) for a tombstoned
444
- // document: its revisions are rows, `doc == null` tells them apart.
460
+ // `_doc` on a revision row (spec/surface §1.2, 4.7): the document's CURRENT
461
+ // live row — the revision's `_path` may be an earlier one — and absent (the
462
+ // single-with-no-row marker, as `_dst` on a dangling link) for a tombstoned
463
+ // document: its revisions are rows, `_doc == null` tells them apart.
445
464
  const revisionDoc = (row) => tag(one(`SELECT * FROM docs WHERE doc_id = ? AND deleted_commit IS NULL`, row.doc_id), "docs");
446
465
  const owningBlock = (row) => {
447
466
  if (!row.block_id)
@@ -451,10 +470,10 @@ export function makeStoreContext(store, repoId, opts = {}) {
451
470
  return tag(load(), "blocks");
452
471
  return lazyOwner({ block_id: row.block_id, doc_id: row.doc_id, __path: row.__path }, "blocks", load);
453
472
  };
454
- // `block` on a link row (spec/surface §1.2, 4.11): the SOURCE block the link
455
- // was written in — the blocks row `block_id` (the edge's `src_block`) names,
473
+ // `_block` on a link row (spec/surface §1.2, 4.11): the SOURCE block the link
474
+ // was written in — the blocks row `_block_id` (the edge's `src_block`) names,
456
475
  // in the source document (`__path` is already the block's document path) —
457
- // mirroring `doc`; absent when the link has none (a frontmatter link).
476
+ // mirroring `_doc`; absent when the link has none (a frontmatter link).
458
477
  const linkBlock = (row) => {
459
478
  if (!row.src_block)
460
479
  return undefined;
@@ -464,114 +483,114 @@ export function makeStoreContext(store, repoId, opts = {}) {
464
483
  return lazyOwner({ block_id: row.src_block, doc_id: row.src_doc, __path: row.__path }, "blocks", load);
465
484
  };
466
485
  // ---- engine attributes -----------------------------------------------------
467
- // Bare, snake_case, winning on collision (see the name rule above; spec/surface
468
- // §1.2, 3.0). `path` (and the edge field `dst_path`) is the REFERENCE form
469
- // (`/a/b.md`, §1 "Paths", 2.0): the rows carry the storage form (`path`, the
470
- // `__path` join column) and the read roots it, so nothing below the binding
471
- // changes. run.ts roots the string literals a query compares them with.
472
- // `undefined` means "not an attribute" — the caller falls through to the
473
- // handles, relations and the author's data.
486
+ // `_`-prefixed, snake_case (see the name rule above; spec/surface §1.2, 5.0).
487
+ // `_path` (and the edge field `_dst_path`) is the REFERENCE form (`/a/b.md`,
488
+ // §1 "Paths", 2.0): the rows carry the storage form (`path`, the `__path` join
489
+ // column) and the read roots it, so nothing below the binding changes. run.ts
490
+ // roots the string literals a query compares them with. `undefined` means
491
+ // "not an attribute" — the caller falls through to the handles, relations and
492
+ // the author's data.
474
493
  const attribute = (row, t, name) => {
475
494
  if (t === "docs")
476
495
  switch (name) {
477
- case "id": return row.doc_id;
478
- case "path": return referencePath(String(row.path));
479
- case "hash": return row.file_hash == null ? null : hex(row.file_hash);
480
- case "updated_at": return scalar(`SELECT c.ts FROM revisions r JOIN commits c ON c.commit_id = r.commit_id WHERE r.rev_id = ?`, row.current_rev) ?? null;
481
- // `created_at` (4.10): the commit `ts` of the document's first revision
482
- // (`seq = 1`, the row `revisions first { ts values }` reads) — when
496
+ case "_id": return row.doc_id;
497
+ case "_path": return referencePath(String(row.path));
498
+ case "_hash": return row.file_hash == null ? null : hex(row.file_hash);
499
+ case "_updated_at": return scalar(`SELECT c.ts FROM revisions r JOIN commits c ON c.commit_id = r.commit_id WHERE r.rev_id = ?`, row.current_rev) ?? null;
500
+ // `_created_at` (4.10): the commit `ts` of the document's first revision
501
+ // (`seq = 1`, the row `_revisions first { _ts values }` reads) — when
483
502
  // omgbase first observed the document, not a filesystem time. The
484
503
  // retention squash (spec/store §7.1, 15.0) keeps every revision and
485
504
  // commit row, so the value survives archiving.
486
- case "created_at": return scalar(`SELECT c.ts FROM revisions r JOIN commits c ON c.commit_id = r.commit_id WHERE r.doc_id = ? AND r.seq = 1`, row.doc_id) ?? null;
487
- // `raw`: the exact file bytes (spec/store §6.1, frontmatter included).
488
- case "raw": return docsRead(store, String(row.doc_id))?.raw ?? null;
489
- // `title`: the computed title (spec/properties §3.3 — the authored
490
- // title, else the first H1, else absent → null). `data.title` is the
491
- // authored one alone.
492
- case "title": return docProp(String(row.doc_id), "$title", "computed") ?? null;
493
- // `tags`: the union of the authored tags (frontmatter + inline, by the
505
+ case "_created_at": return scalar(`SELECT c.ts FROM revisions r JOIN commits c ON c.commit_id = r.commit_id WHERE r.doc_id = ? AND r.seq = 1`, row.doc_id) ?? null;
506
+ // `_raw`: the exact file bytes (spec/store §6.1, frontmatter included).
507
+ case "_raw": return docsRead(store, String(row.doc_id))?._raw ?? null;
508
+ // `_title`: the computed title (spec/properties §3.3 — the authored
509
+ // title, else the first H1, else absent → null). The bare `title` is the
510
+ // authored key alone.
511
+ case "_title": return docProp(String(row.doc_id), "$title", "computed") ?? null;
512
+ // `_tags`: the union of the authored tags (frontmatter + inline, by the
494
513
  // property rule) and the body `#hashtags` (the computed `$tags` row,
495
514
  // spec/properties §3.3), deduped, authored first in authored order, then
496
- // the hashtags in document order; null when both are absent.
497
- // `data.tags` is the authored list alone.
498
- case "tags": return docTags(String(row.doc_id));
499
- case "format": return row.format;
515
+ // the hashtags in document order; null when both are absent. The bare
516
+ // `tags` is the authored list alone.
517
+ case "_tags": return docTags(String(row.doc_id));
518
+ case "_format": return row.format;
500
519
  }
501
520
  if (t === "blocks")
502
521
  switch (name) {
503
- case "id": return row.block_id;
504
- case "path": return referencePath(String(row.__path));
505
- // `raw`: the block's raw source bytes — the blob its `raw_hash` names (a
506
- // container's raw holds its children verbatim); `text` is the normalized text.
507
- case "raw": return row.raw_hash == null ? null : scalar(`SELECT bytes FROM blobs WHERE hash = ?`, row.raw_hash)?.toString("utf8") ?? null;
508
- case "hash": return row.raw_hash == null ? null : hex(row.raw_hash);
509
- case "updated_at": return scalar(`SELECT MAX(c.ts) FROM block_changes bc JOIN commits c ON c.commit_id = bc.commit_id WHERE bc.block_id = ?`, row.block_id) ?? null;
510
- // `created_at` (4.10): the EARLIEST commit `ts` among the block's
511
- // `block_changes` — the same join as `updated_at` under MIN: the commit
522
+ case "_id": return row.block_id;
523
+ case "_path": return referencePath(String(row.__path));
524
+ // `_raw`: the block's raw source bytes — the blob its `raw_hash` names (a
525
+ // container's raw holds its children verbatim); `_text` is the normalized text.
526
+ case "_raw": return row.raw_hash == null ? null : scalar(`SELECT bytes FROM blobs WHERE hash = ?`, row.raw_hash)?.toString("utf8") ?? null;
527
+ case "_hash": return row.raw_hash == null ? null : hex(row.raw_hash);
528
+ case "_updated_at": return scalar(`SELECT MAX(c.ts) FROM block_changes bc JOIN commits c ON c.commit_id = bc.commit_id WHERE bc.block_id = ?`, row.block_id) ?? null;
529
+ // `_created_at` (4.10): the EARLIEST commit `ts` among the block's
530
+ // `block_changes` — the same join as `_updated_at` under MIN: the commit
512
531
  // that first wrote the id (`inserted`, or the split/merge lineage row
513
532
  // that minted it). Store 14.2 keeps that row and the 15.0 squash keeps
514
533
  // each block's earliest row (spec/store §7.1), so it survives archiving;
515
534
  // a resurrected block keeps its original creation.
516
- case "created_at": return scalar(`SELECT MIN(c.ts) FROM block_changes bc JOIN commits c ON c.commit_id = bc.commit_id WHERE bc.block_id = ?`, row.block_id) ?? null;
517
- case "doc_id": return row.doc_id;
518
- case "type": return row.type;
519
- case "text": return row.text;
520
- case "attrs": return parseJson(row.attrs);
521
- case "ordinal": return row.ordinal;
522
- case "depth": return row.depth;
535
+ case "_created_at": return scalar(`SELECT MIN(c.ts) FROM block_changes bc JOIN commits c ON c.commit_id = bc.commit_id WHERE bc.block_id = ?`, row.block_id) ?? null;
536
+ case "_doc_id": return row.doc_id;
537
+ case "_type": return row.type;
538
+ case "_text": return row.text;
539
+ case "_attrs": return parseJson(row.attrs);
540
+ case "_ordinal": return row.ordinal;
541
+ case "_depth": return row.depth;
523
542
  }
524
543
  if (t === "nodes")
525
544
  switch (name) {
526
- case "id": return row.node_id;
527
- case "path": return referencePath(String(row.__path));
528
- case "doc_id": return row.doc_id;
529
- case "block_id": return row.block_id ?? null;
530
- case "kind": return row.kind;
531
- case "name": return row.name;
532
- case "value": return row.value;
533
- case "attrs": return parseJson(row.attrs);
545
+ case "_id": return row.node_id;
546
+ case "_path": return referencePath(String(row.__path));
547
+ case "_doc_id": return row.doc_id;
548
+ case "_block_id": return row.block_id ?? null;
549
+ case "_kind": return row.kind;
550
+ case "_name": return row.name;
551
+ case "_value": return row.value;
552
+ case "_attrs": return parseJson(row.attrs);
534
553
  }
535
554
  if (t === "links")
536
555
  switch (name) {
537
556
  // the store's columns under the surface's names (spec/surface §1.2).
538
- case "id": return row.edge_id;
539
- case "path": return referencePath(String(row.__path));
540
- case "doc_id": return row.src_doc;
541
- case "block_id": return row.src_block ?? null;
542
- case "via_id": return row.via_node ?? null;
543
- case "dst_id": return row.dst_node;
544
- case "dst_path": {
557
+ case "_id": return row.edge_id;
558
+ case "_path": return referencePath(String(row.__path));
559
+ case "_doc_id": return row.src_doc;
560
+ case "_block_id": return row.src_block ?? null;
561
+ case "_via_id": return row.via_node ?? null;
562
+ case "_dst_id": return row.dst_node;
563
+ case "_dst_path": {
545
564
  const p = scalar(`SELECT path FROM docs WHERE doc_id = ?`, row.dst_node);
546
565
  return p == null ? null : referencePath(String(p));
547
566
  }
548
- case "uri": return scalar(`SELECT uri FROM external_nodes WHERE node_id = ?`, row.dst_node) ?? null;
549
- case "field": return row.src_field ?? null;
550
- case "predicate":
551
- case "provenance":
552
- case "dst_kind":
553
- case "anchor": return row[name];
567
+ case "_uri": return scalar(`SELECT uri FROM external_nodes WHERE node_id = ?`, row.dst_node) ?? null;
568
+ case "_field": return row.src_field ?? null;
569
+ case "_predicate":
570
+ case "_provenance":
571
+ case "_dst_kind":
572
+ case "_anchor": return row[name.slice(1)];
554
573
  }
555
574
  if (t === "revisions")
556
575
  switch (name) {
557
- // the `revisions` × `commits` columns under docs_history's names (spec/surface §1.2, 4.7).
558
- case "id":
559
- case "rev": return row.rev_id;
560
- case "path": return referencePath(String(row.path));
561
- case "seq": return row.seq;
562
- case "doc_id": return row.doc_id;
563
- case "commit": return row.commit_id;
564
- case "ts": return row.ts;
565
- case "origin": return row.origin;
566
- case "actor": return row.actor ?? null;
567
- case "reason": return row.reason ?? null;
568
- case "hash": return hex(row.rendered_hash);
569
- case "is_current": return row.is_current === 1 || row.is_current === true;
570
- case "archived": return row.archived === 1 || row.archived === true;
576
+ // the `revisions` × `commits` columns under docs_history's names, `_`-prefixed (spec/surface §1.2, 4.7).
577
+ case "_id":
578
+ case "_rev": return row.rev_id;
579
+ case "_path": return referencePath(String(row.path));
580
+ case "_seq": return row.seq;
581
+ case "_doc_id": return row.doc_id;
582
+ case "_commit": return row.commit_id;
583
+ case "_ts": return row.ts;
584
+ case "_origin": return row.origin;
585
+ case "_actor": return row.actor ?? null;
586
+ case "_reason": return row.reason ?? null;
587
+ case "_hash": return hex(row.rendered_hash);
588
+ case "_is_current": return row.is_current === 1 || row.is_current === true;
589
+ case "_archived": return row.archived === 1 || row.archived === true;
571
590
  }
572
591
  return undefined;
573
592
  };
574
- // `tags` on a document: authored ∪ hashtags, each list as the property rule
593
+ // `_tags` on a document: authored ∪ hashtags, each list as the property rule
575
594
  // reads it (a scalar-authored `tags: x` is one element), deduped by value
576
595
  // (canonical JSON, so both engines agree on non-string elements).
577
596
  const docTags = (docId) => {
@@ -595,10 +614,10 @@ export function makeStoreContext(store, repoId, opts = {}) {
595
614
  const getFrom = (row, key) => {
596
615
  if (row == null)
597
616
  return undefined;
598
- // The root row: `^$it.id` is the repository id, `^$it.<target>` the lazy
617
+ // The root row: `^$it._id` is the repository id, `^$it.<target>` the lazy
599
618
  // root scan (the same handle `^<target>` / `root(name)` hands out).
600
619
  if (row[ROOT_ROW]) {
601
- if (key === "id")
620
+ if (key === "_id")
602
621
  return repoId;
603
622
  return rootFns[key] ? rootFns[key]() : undefined;
604
623
  }
@@ -616,33 +635,38 @@ export function makeStoreContext(store, repoId, opts = {}) {
616
635
  // the row) or unknown — never an omgbase attribute. Row-scoped domain
617
636
  // functions (text/semantic/under_*/…) never arrive here: run.ts rewrites
618
637
  // them to `$it.fn(…)`, so they reach callMethod with the row as receiver.
619
- // engine attributes win on collision (the name rule above).
620
- if (ATTRIBUTES[t].has(key))
621
- return attribute(r, t, key);
622
- // self-alias namespaces: `doc.x` (docs), `block.x` (blocks), `section.x` (nodes).
623
- if (t === "docs" && key === "doc")
624
- return r;
625
- if (t === "blocks" && key === "block")
626
- return r;
627
- if (t === "nodes" && key === "section")
628
- return r;
629
- // reach-through to owning entities, from a link row to its destination and
630
- // from a revision to its document's current row — statically single
631
- // relations: absent is the single-with-no-row marker.
632
- if (t === "revisions" && key === "doc")
633
- return revisionDoc(r) ?? ABSENT_SINGLE;
634
- if (t !== "docs" && key === "doc")
635
- return owningDoc(r) ?? ABSENT_SINGLE;
636
- if (t === "nodes" && key === "block")
637
- return owningBlock(r) ?? ABSENT_SINGLE;
638
- if (t === "links" && key === "dst")
639
- return destinationDoc(r) ?? ABSENT_SINGLE;
640
- if (t === "links" && key === "block")
641
- return linkBlock(r) ?? ABSENT_SINGLE;
642
- // relations.
643
- const rels = rel[t];
644
- if (rels[key])
645
- return rels[key](r);
638
+ // An underscore-prefixed name is the engine's (the name rule above): an
639
+ // attribute, a reach-through, a relation or a source handle — never the
640
+ // author's.
641
+ if (key.startsWith("_")) {
642
+ if (ATTRIBUTES[t].has(key))
643
+ return attribute(r, t, key);
644
+ // reach-through to owning entities, from a link row to its destination and
645
+ // from a revision to its document's current row — statically single
646
+ // relations: absent is the single-with-no-row marker.
647
+ if (t === "revisions" && key === "_doc")
648
+ return revisionDoc(r) ?? ABSENT_SINGLE;
649
+ if (t !== "docs" && key === "_doc")
650
+ return owningDoc(r) ?? ABSENT_SINGLE;
651
+ if (t === "nodes" && key === "_block")
652
+ return owningBlock(r) ?? ABSENT_SINGLE;
653
+ if (t === "links" && key === "_dst")
654
+ return destinationDoc(r) ?? ABSENT_SINGLE;
655
+ if (t === "links" && key === "_block")
656
+ return linkBlock(r) ?? ABSENT_SINGLE;
657
+ // relations.
658
+ const rels = rel[t];
659
+ if (rels[key])
660
+ return rels[key](r);
661
+ // The source handles of a document: `_data` is the merged authored view
662
+ // (frontmatter + inline), the other two one source each; `_data.<k>` is
663
+ // the bare `<k>`, and the one way to an authored key spelled `_x`.
664
+ if (t === "docs" && SOURCE_HANDLES.has(key))
665
+ return { [PROP_SOURCE]: true, docId: String(r.doc_id), source: handleSource(key) };
666
+ // Any other `_name` is no engine name of this kind (names.ts refuses it
667
+ // statically, before any row is read): absent.
668
+ return undefined;
669
+ }
646
670
  // A bare target name that is not one of this row's relations reads a
647
671
  // property the row has none of (spec/surface §1.1, 2.0): loud, naming the
648
672
  // root-row spelling. `docs`/`links` never get here (run.ts refuses them
@@ -650,19 +674,12 @@ export function makeStoreContext(store, repoId, opts = {}) {
650
674
  // row and `revisions` on anything but a document do.
651
675
  if (ROOTS.has(key))
652
676
  throw new FilterInvalid(bareTargetMessage(key, null), "OQX");
653
- // the author's data.
654
- if (t === "docs") {
655
- // The source handles: `data` is the merged authored view (frontmatter +
656
- // inline), the other two one source each; `data.<k>` reaches an authored
657
- // key whose bare name is an engine attribute (`data.path`, `data.tags`).
658
- if (SOURCE_HANDLES.has(key))
659
- return { [PROP_SOURCE]: true, docId: String(r.doc_id), source: key === "data" ? null : key };
677
+ // the author's data — every bare name.
678
+ if (t === "docs")
660
679
  return docProp(String(r.doc_id), key);
661
- }
662
- // blocks / nodes: a bare non-structural identifier flattens into attrs:
663
- // `checked` reads `attrs.checked`. The attributes above win on collision; an
664
- // absent key is undefined (silently false in a predicate), matching a doc's
665
- // missing frontmatter key. The `attrs.<k>` form still works unchanged.
680
+ // blocks / nodes: a bare identifier flattens into attrs: `checked` reads
681
+ // `_attrs.checked`. An absent key is undefined (silently false in a
682
+ // predicate), matching a doc's missing frontmatter key.
666
683
  if (t === "blocks" || t === "nodes")
667
684
  return jattr(r, key);
668
685
  return undefined;
@@ -847,12 +864,12 @@ export function makeStoreContext(store, repoId, opts = {}) {
847
864
  };
848
865
  return {
849
866
  // The root scope's names (spec/oqx §2 step 5 at the root): the five lazy
850
- // scans and `id`, the repository id — so `^docs` / `0^docs` from any row is
851
- // the scan and `^id` / `0^id` the id.
867
+ // scans and `_id`, the repository id — so `^docs` / `0^docs` from any row is
868
+ // the scan and `^_id` / `0^_id` the id.
852
869
  root(name) {
853
870
  if (opts.rowsRoot && name === opts.rowsRoot.name)
854
871
  return opts.rowsRoot.rows;
855
- if (name === "id")
872
+ if (name === "_id")
856
873
  return repoId;
857
874
  return rootFns[name] ? rootFns[name]() : undefined;
858
875
  },
@@ -908,7 +925,7 @@ export function makeStoreContext(store, repoId, opts = {}) {
908
925
  // pushdown can serve the property's autopromoted/cached bounds — see
909
926
  // detectRange + val_json in store/properties.ts.
910
927
  callFunction(name, args) {
911
- // `entries(data)` / `entries(frontmatter)` / `entries(inline)`: the source
928
+ // `entries(_data)` / `entries(_frontmatter)` / `entries(_inline)`: the source
912
929
  // handle is lazy (a PropSourceRef, not a plain object), so materialize its
913
930
  // property bag as entries here; any other argument falls through to the
914
931
  // oqx builtin.
@@ -965,7 +982,7 @@ export function tagRows(rows, target) {
965
982
  }
966
983
  /**
967
984
  * A store row surfacing as a VALUE in a result (spec/surface §1.4, rows as
968
- * values, 1.2) is rendered as `{ id, path }` (3.0) — its id column and its
985
+ * values, 1.2) is rendered as `{ _id, _path }` (5.0) — its id column and its
969
986
  * document's path in the reference form (`/a.md`, §1 "Paths"), the same two keys
970
987
  * every hit carries, spelled as the attributes that read them — never the store
971
988
  * row itself.
@@ -981,7 +998,7 @@ export function rowRef(value) {
981
998
  // a revision's path is `revisions.path` — the path it was committed under
982
999
  // (a move re-paths the current revision; §1.2, 4.7).
983
1000
  const path = t === "docs" || t === "revisions" ? r.path : r.__path;
984
- return { id: String(id), path: path == null ? "" : referencePath(String(path)) };
1001
+ return { _id: String(id), _path: path == null ? "" : referencePath(String(path)) };
985
1002
  }
986
1003
  export { TARGET };
987
1004
  //# sourceMappingURL=context.js.map