@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
@@ -1,7 +1,7 @@
1
1
  // omgbase's OQX entry point, served by `@omgbase/oqx`: parse the source, run it
2
2
  // through the in-memory engine over a store-backed DataContext (context.ts), and
3
- // shape the engine's result into omgbase's OqxResult (lean {id, path, …} hits
4
- // — the envelope keys ARE the attributes, spec/surface §1.4 3.0 — keyset
3
+ // shape the engine's result into omgbase's OqxResult (lean {_id, _path, …}
4
+ // hits — the envelope keys ARE the attributes, spec/surface §1.4 (5.0) — keyset
5
5
  // pagination, count/exists scalars). Same signature + shape as the former
6
6
  // in-tree compiler, so every caller and the corpus are unchanged.
7
7
  import { parse, PlannedEngine, InMemoryEngine, OqxError, resolveAliases, transform, visit, build, semantics } from "@omgbase/oqx";
@@ -29,18 +29,18 @@ const ROW_FNS = new Set([
29
29
  "yaml_path", "json_pointer", "has_edge", "has_anchor", "child_count", "parent_type",
30
30
  ]);
31
31
  // Paths on the surface are the reference form (spec/surface §1 "Paths", 2.0):
32
- // `path` and the edge field `dst_path` read `/a/b.md`. A string literal a query
33
- // compares with one of them — `path == "a.md"`, `"a.md" != dst_path`,
34
- // `path.startsWith("lab/")`, `doc.path == "a.md"` — is rooted first, so both
32
+ // `_path` and the edge field `_dst_path` read `/a/b.md`. A string literal a
33
+ // query compares with one of them — `_path == "a.md"`, `"a.md" != _dst_path`,
34
+ // `_path.startsWith("lab/")`, `_doc._path == "a.md"` — is rooted first, so both
35
35
  // spellings match; a property or binding compared with a path is never touched
36
- // (it is the author's value), and neither is an authored `path:` key read
37
- // through a source handle (`data.path`, `frontmatter.path`, `inline.path`): a
38
- // member read is a path read only through a ROW handle (`doc`, `block`, `dst`,
39
- // `$it`).
36
+ // (it is the author's value) — so an authored `path:` key, bare or through a
37
+ // source handle (`path`, `_data.path`, `_frontmatter.path`), is compared as
38
+ // written: a member read is a path read only through a ROW handle (`_doc`,
39
+ // `_block`, `_dst`, `$it`).
40
40
  // The planner sees the rewritten tree too, so the pushed SQL and the in-memory
41
41
  // engine agree by construction.
42
- const PATH_ATTRIBUTES = new Set(["path", "dst_path"]);
43
- const ROW_HANDLES = new Set(["doc", "block", "dst", "$it"]);
42
+ const PATH_ATTRIBUTES = new Set(["_path", "_dst_path"]);
43
+ const ROW_HANDLES = new Set(["_doc", "_block", "_dst", "$it"]);
44
44
  function isPathRead(e) {
45
45
  if (e.kind === "ident" || e.kind === "outer")
46
46
  return PATH_ATTRIBUTES.has(e.name);
@@ -65,11 +65,11 @@ export function rootPathLiterals(e) {
65
65
  return e;
66
66
  }
67
67
  // The root row (root-row.ts, spec/surface §1.1, 2.0): `$repo` anywhere, and a
68
- // bare `docs`/`links` at scope depth ≥ 1 (a property read of a row that has
69
- // none), are refused before evaluation — one `visit` over the parsed tree, so
70
- // the error does not depend on which rows the data happens to hold and the
71
- // planned and in-memory paths agree trivially. `blocks`/`nodes` are relations of
72
- // some rows and are judged by the context at read time (same message). A
68
+ // bare root name at scope depth ≥ 1 (a property read of a row that has none —
69
+ // since 5.0 every relation is `_`-prefixed, so a bare `blocks`/`nodes`/
70
+ // `revisions` is never one either), are refused before evaluation — one `visit`
71
+ // over the parsed tree, so the error does not depend on which rows the data
72
+ // happens to hold and the planned and in-memory paths agree trivially. A
73
73
  // `^<target>` with more carets than enclosing scopes (`^docs count { }` at the
74
74
  // top level) would read absent and count 0: refused too, naming the bare
75
75
  // spelling.
@@ -85,7 +85,7 @@ export function checkRootSpellings(q) {
85
85
  return;
86
86
  if (node.name === "$repo")
87
87
  throw new FilterInvalid(REPO_REMOVED_MESSAGE, "OQX");
88
- if (node.kind === "ident" && ctx.depth >= 1 && (node.name === "docs" || node.name === "links")) {
88
+ if (node.kind === "ident" && ctx.depth >= 1 && ROOTS.has(node.name)) {
89
89
  throw new FilterInvalid(bareTargetMessage(node.name, ctx.depth), "OQX");
90
90
  }
91
91
  if (node.kind === "outer" && ROOTS.has(node.name) && node.levels > ctx.depth) {
@@ -154,14 +154,15 @@ export function collectSemanticPhrases(source) {
154
154
  });
155
155
  return [...phrases];
156
156
  }
157
- const ID_ITEM = build.field("__oqx_id", build.ident("id"));
158
- const PATH_ITEM = build.field("__oqx_path", build.ident("path"));
157
+ const ID_ITEM = build.field("__oqx_id", build.ident("_id"));
158
+ const PATH_ITEM = build.field("__oqx_path", build.ident("_path"));
159
159
  // The runner's own projection names (`__oqx_id`, `__oqx_path`, `__oqx_self`,
160
160
  // `__oqx_value`) and the selector compiler's slots (`__omg_sel_<t>_<slot>`,
161
161
  // mutate/select.ts) share two reserved prefixes. A user alias under either would
162
162
  // shadow an injected item (`select __oqx_id: 1` overwrote the hit's `id` through
163
163
  // 4.0) or collide with a slot, so it is refused before anything runs — over the
164
- // query's own top-level items, the only ones the runner injects beside.
164
+ // query's own top-level items, the only ones the runner injects beside. (The
165
+ // engine prefix of 5.0 is a single `_`; two underscores stay the runner's.)
165
166
  const RESERVED_ALIAS_PREFIXES = ["__oqx_", "__omg_"];
166
167
  export function reservedAlias(items) {
167
168
  for (const item of items) {
@@ -172,23 +173,25 @@ export function reservedAlias(items) {
172
173
  return null;
173
174
  }
174
175
  // Result projections may not overwrite the envelope (spec/surface §1.4, 3.0):
175
- // a projection whose output key is `id` or `path` is allowed only when it IS
176
- // the envelope's own selection — the bare attribute of the same name, which
177
- // collapses onto the envelope key (`select id, path` is simply the envelope) —
178
- // and is otherwise a loud error asking for an alias: `select data.path` must be
179
- // `select p: data.path`, `select path: doc.layer` likewise. Checked over the
180
- // resolved items (an alias is just a name here), before the engine runs, so the
181
- // error never depends on the rows. A `values` projection returns no hits, so
182
- // the envelope — and the rule — do not apply.
183
- const ENVELOPE_KEYS = new Set(["id", "path"]);
176
+ // a projection whose output key is `_id` or `_path` is allowed only when it IS
177
+ // the envelope's own selection — the attribute of the same name, which
178
+ // collapses onto the envelope key (`select _id, _path` is simply the envelope)
179
+ // — and is otherwise a loud error asking for an alias: `select _path: layer`
180
+ // must pick another name. (Since 5.0 the author's `path:`/`id:` keys are the
181
+ // bare `path`/`id`, their own columns — nothing collides by accident; only an
182
+ // alias spelled `_id:`/`_path:` can.) Checked over the resolved items (an alias
183
+ // is just a name here), before the engine runs, so the error never depends on
184
+ // the rows. A `values` projection returns no hits, so the envelope — and the
185
+ // rule — do not apply.
186
+ const ENVELOPE_KEYS = new Set(["_id", "_path"]);
184
187
  function envelopeCollision(items) {
185
188
  for (const item of items) {
186
189
  if (!ENVELOPE_KEYS.has(item.name))
187
190
  continue;
188
191
  if (item.kind === "field" && item.expr.kind === "ident" && item.expr.name === item.name)
189
192
  continue;
190
- const hint = item.name === "path" ? "p: data.path" : "i: data.id";
191
- return new FilterInvalid(`projection \`${item.name}\` collides with the hit envelope — alias it (\`${hint}\`)`, "OQX");
193
+ const hint = item.name === "_path" ? "p" : "i";
194
+ return new FilterInvalid(`projection \`${item.name}\` collides with the hit envelope — alias it (\`${hint}: …\`)`, "OQX");
192
195
  }
193
196
  return null;
194
197
  }
@@ -197,8 +200,8 @@ function envelopeCollision(items) {
197
200
  // the five collections, `revisions` included — the planner's `rootTarget` is
198
201
  // narrower: the four it can plan) and nothing re-projects them; a `follow` destination (`follow before` over a
199
202
  // frontmatter list of paths), a `from E` re-projection or any other source can
200
- // reach a scalar, which the injected `id`/`path` reads would render as the
201
- // junk hit `{ id: "undefined", path: "" }`. For those queries the row itself is
203
+ // reach a scalar, which the injected `_id`/`_path` reads would render as the
204
+ // junk hit `{ _id: "undefined", _path: "" }`. For those queries the row itself is
202
205
  // projected too (`$it`), and `toHit` fails the query when it is not a row. Only
203
206
  // then: a row clone per hit is free here but not in the Rust port, and a root
204
207
  // scan cannot need it. A `values` projection returns no hits, so it is exempt
@@ -236,8 +239,8 @@ const VALUE_KEY = "__oqx_value";
236
239
  // spec/oqx §12), a `values` projection of `$it`, a field bound to a
237
240
  // row — and a store row is not a wire shape (column names, `attrs` as a JSON
238
241
  // string, the `__path` join column, Buffers). Walk the projected value and
239
- // render every tagged row as `{ id, path }` — the hit envelope's own keys
240
- // (3.0), one notation for "a document reference" everywhere a row shape
242
+ // render every tagged row as `{ _id, _path }` — the hit envelope's own keys
243
+ // (5.0), one notation for "a document reference" everywhere a row shape
241
244
  // appears in a result; everything else passes through untouched (plain records
242
245
  // and arrays are descended, scalars kept). Hits are records the engine built
243
246
  // from the injected id/path items, never rows.
@@ -264,22 +267,22 @@ function shapeRow(row) {
264
267
  throw notAStoreRow(__oqx_self);
265
268
  return { id: String(__oqx_id), path: String(__oqx_path ?? ""), proj: renderValue(rest) };
266
269
  }
267
- // The envelope keys lead; a user projection of `id`/`path` collapses onto
270
+ // The envelope keys lead; a user projection of `_id`/`_path` collapses onto
268
271
  // them in place (the same value, read by the same attribute).
269
272
  function toHit(s) {
270
- return { id: s.id, path: s.path, ...s.proj };
273
+ return { _id: s.id, _path: s.path, ...s.proj };
271
274
  }
272
- // `bare` (4.2): the projection alone. A projected `id`/`path` is an ordinary
273
- // column here — the intrinsic the attribute reads, under the key the query
274
- // gave it — and nothing is injected beside it.
275
+ // `bare` (4.2): the projection alone. A projected `_id`/`_path` is an ordinary
276
+ // column here — the attribute's value, under the key the query gave it — and
277
+ // nothing is injected beside it.
275
278
  function toBareHit(s) {
276
279
  return s.proj;
277
280
  }
278
281
  // Top-level `select distinct`: dedup hits by their USER projection — every
279
- // field except the envelope's `id`/`path`, which a projection of the attribute
280
- // collapses onto (so `select distinct id` dedups on nothing but the empty
282
+ // field except the envelope's `_id`/`_path`, which a projection of the attribute
283
+ // collapses onto (so `select distinct _id` dedups on nothing but the empty
281
284
  // projection, as it always has); under `bare` there is no envelope, so a
282
- // projected `id`/`path` IS part of the projection and counts. Keeps the first
285
+ // projected `_id`/`_path` IS part of the projection and counts. Keeps the first
283
286
  // (so the retained hit carries a real identity). Order-preserving.
284
287
  function dedupByProjection(rows, bare) {
285
288
  const seen = new Set();
@@ -460,11 +463,11 @@ function runResolved(store, repoId, raw, opts) {
460
463
  return { hits: [], truncated: false, cursor: null, consumer: "none", none: res.consumer === "none" ? res.none : true };
461
464
  }
462
465
  // A grouped query (oqx 0.24, SEMANTICS §24) yields GROUPS, not store rows:
463
- // the envelope does not apply — there is no `id`/`path` to inject and no
466
+ // the envelope does not apply — there is no `_id`/`_path` to inject and no
464
467
  // keyset to page by — so a hit is the group's projection as the query wrote
465
468
  // it (`{ layer, n }`), exactly what `bare` yields for a row query; `bare` is
466
469
  // accepted and changes nothing. Rows that surface as values (`rows collect {
467
- // }` with an empty projection, `collect(doc)`) render as `{ id, path }` refs
470
+ // }` with an empty projection, `collect($it)`) render as `{ _id, _path }` refs
468
471
  // like every row-as-value. Paging is by position: the query's own
469
472
  // `limit`/`offset` define the set, the tool's `limit` takes the first page,
470
473
  // `truncated` says whether more groups follow, the cursor is always null (as
@@ -478,7 +481,7 @@ function runResolved(store, repoId, raw, opts) {
478
481
  // cannot land inside a document's elements: a flattened query pages
479
482
  // positionally with a null cursor, as a grouped query and a custom `order by`
480
483
  // do (a keyset extended with the flatten ordinal is a later round). A bound
481
- // name of `id` or `path` would shadow the envelope's own reads (the name
484
+ // name of `_id` or `_path` would shadow the envelope's own reads (the name
482
485
  // shadows a same-named row property on the flattened row): refused, like a
483
486
  // projection that collides with the envelope.
484
487
  const flattened = parsed.flatten.length > 0;
@@ -486,11 +489,11 @@ function runResolved(store, repoId, raw, opts) {
486
489
  if (ENVELOPE_KEYS.has(f.name))
487
490
  throw new FilterInvalid(`flatten \`as ${f.name}\` collides with the hit envelope — a flattened row is still the row, whose \`${f.name}\` the envelope reads; bind another name`, "OQX");
488
491
  }
489
- // collect / first / single: inject id + path so every hit carries them. A
492
+ // collect / first / single: inject _id + _path so every hit carries them. A
490
493
  // top-level `select distinct` is applied HERE, not in the engine: the injected
491
- // id/path are unique per row and would defeat the engine's projection dedup, so
492
- // we run without engine-distinct and dedup hits by their USER projection below,
493
- // keeping the first row's id/path.
494
+ // _id/_path are unique per row and would defeat the engine's projection dedup,
495
+ // so we run without engine-distinct and dedup hits by their USER projection
496
+ // below, keeping the first row's _id/_path.
494
497
  const topDistinct = parsed.distinct;
495
498
  // A top-level `values` projection runs as a RECORD projection whose single
496
499
  // item is renamed to VALUE_KEY (the engine's own values mode is switched off),
@@ -500,7 +503,7 @@ function runResolved(store, repoId, raw, opts) {
500
503
  const userSelect = topValues ? [{ ...parsed.select[0], name: VALUE_KEY }] : parsed.select;
501
504
  // `bare` (4.2): the hits are the projection alone — refused without one and
502
505
  // with `values`; with no envelope there is no collision to refuse either, a
503
- // projected `id`/`path` is simply a column.
506
+ // projected `_id`/`_path` is simply a column.
504
507
  const bare = opts.bare === true;
505
508
  if (bare) {
506
509
  const misuse = bareMisuse(parsed);
@@ -597,10 +600,10 @@ export async function oqxRunAsync(store, repoId, source, opts = {}, embedQuery)
597
600
  }
598
601
  return oqxRun(store, repoId, source, { ...opts, semanticVectors });
599
602
  }
600
- // The collect page's keyset is (path, id) — the hit's `path`, so the reference
601
- // form (`/a.md`), and its `id`: the kernel's shared cursor encoding
603
+ // The collect page's keyset is (path, id) — the hit's `_path`, so the reference
604
+ // form (`/a.md`), and its `_id`: the kernel's shared cursor encoding
602
605
  // (core/cursor.ts) with a two-part tuple of VALUES (no key names ride in it, so
603
- // a 2.x cursor stays valid on 3.0). A malformed cursor is CursorInvalid, which the MCP layer maps
606
+ // a 2.x cursor stays valid on 5.0). A malformed cursor is CursorInvalid, which the MCP layer maps
604
607
  // to filter_invalid; so is a cursor whose path is not rooted — one issued by a
605
608
  // 1.x surface, whose keyset would otherwise sort before every rooted hit and
606
609
  // silently replay the first page (spec/surface §1.4, 2.0).
@@ -59,12 +59,12 @@ export interface Operand extends Frag {
59
59
  pathCol?: string;
60
60
  }
61
61
  /**
62
- * Per target, the bare identifiers (and `doc.<k>` keys, via the docs set) that
62
+ * Per target, the engine names (and `_doc.<k>` keys, via the docs set) that
63
63
  * `context.ts` resolves to something other than a scalar field or property: the
64
- * self alias, the reach-through handles, the relations, the property source
65
- * handles and (blocks/nodes) the whole `attrs` object. A comparison naming one of
66
- * these is declined (spec/surface §1 decline (b)) — read as a property it would
67
- * be `NULL`, where in memory the value is a row, an array or an object.
64
+ * reach-through handles, the relations, the property source handles and
65
+ * (blocks/nodes) the whole `_attrs` object. A comparison naming one of these is
66
+ * declined (spec/surface §1 decline (b)) — read as a property it would be
67
+ * `NULL`, where in memory the value is a row, an array or an object.
68
68
  * `translate.test.ts` asserts this set against the store context.
69
69
  */
70
70
  export declare const NON_PROPERTY_NAMES: Readonly<Record<Target, ReadonlySet<string>>>;