@omgbase/core 0.19.0 → 0.21.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 (36) hide show
  1. package/dist/src/core/ingest.d.ts +2 -0
  2. package/dist/src/core/ingest.js +13 -0
  3. package/dist/src/core/read/document.d.ts +9 -8
  4. package/dist/src/core/read/document.js +0 -2
  5. package/dist/src/core/store/gc.d.ts +13 -1
  6. package/dist/src/core/store/gc.js +24 -5
  7. package/dist/src/core/store/size.d.ts +4 -1
  8. package/dist/src/core/store/size.js +11 -2
  9. package/dist/src/mcp/arguments.d.ts +10 -2
  10. package/dist/src/mcp/arguments.js +37 -3
  11. package/dist/src/mcp/reference.d.ts +1 -1
  12. package/dist/src/mcp/reference.js +36 -4
  13. package/dist/src/mcp/server.js +50 -23
  14. package/dist/src/mutate/effects.d.ts +23 -5
  15. package/dist/src/mutate/effects.js +111 -22
  16. package/dist/src/mutate/known-ids.d.ts +19 -0
  17. package/dist/src/mutate/known-ids.js +47 -7
  18. package/dist/src/mutate/ops.d.ts +1 -0
  19. package/dist/src/mutate/ops.js +20 -1
  20. package/dist/src/mutate/select.d.ts +0 -11
  21. package/dist/src/mutate/select.js +33 -9
  22. package/dist/src/oqx-js/context.d.ts +9 -2
  23. package/dist/src/oqx-js/context.js +74 -28
  24. package/dist/src/oqx-js/names.d.ts +40 -0
  25. package/dist/src/oqx-js/names.js +347 -0
  26. package/dist/src/oqx-js/planner.d.ts +3 -3
  27. package/dist/src/oqx-js/planner.js +13 -9
  28. package/dist/src/oqx-js/root-row.d.ts +15 -4
  29. package/dist/src/oqx-js/root-row.js +18 -10
  30. package/dist/src/oqx-js/run.d.ts +18 -1
  31. package/dist/src/oqx-js/run.js +31 -12
  32. package/dist/src/oqx-js/sql/translate.js +1 -1
  33. package/dist/src/spec-versions.d.ts +5 -5
  34. package/dist/src/spec-versions.js +4 -4
  35. package/dist/src/sync/observe.js +9 -3
  36. package/package.json +2 -2
@@ -6,8 +6,9 @@
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";
8
8
  import { makeStoreContext, rowRef } from "./context.js";
9
- import { SQLiteQueryPlanner, rootTarget } from "./planner.js";
10
- import { REPO_REMOVED_MESSAGE, TARGETS, bareTargetMessage, pastRootMessage, unknownTargetMessage } from "./root-row.js";
9
+ import { SQLiteQueryPlanner } from "./planner.js";
10
+ import { REPO_REMOVED_MESSAGE, ROOTS, bareTargetMessage, pastRootMessage, unknownTargetMessage } from "./root-row.js";
11
+ import { checkNames } from "./names.js";
11
12
  import { FilterInvalid } from "../search/cel/parser.js";
12
13
  import { float32ToBlob } from "../core/vec.js";
13
14
  import { encodeCursor as encodeKeyset, decodePathCursor } from "../core/cursor.js";
@@ -72,8 +73,8 @@ export function rootPathLiterals(e) {
72
73
  export function checkRootSpellings(q) {
73
74
  // A top-level `from <name>` that is no collection (`from edges`, the links
74
75
  // target's name before 3.0) would scan an absent root and come back empty:
75
- // refused, naming the four collections (root-row.ts).
76
- if (q.source.kind === "ident" && !TARGETS.has(q.source.name))
76
+ // refused, naming the five collections (root-row.ts).
77
+ if (q.source.kind === "ident" && !ROOTS.has(q.source.name))
77
78
  throw new FilterInvalid(unknownTargetMessage(q.source.name), "OQX");
78
79
  visit(q, {
79
80
  enter(node, ctx) {
@@ -84,7 +85,7 @@ export function checkRootSpellings(q) {
84
85
  if (node.kind === "ident" && ctx.depth >= 1 && (node.name === "docs" || node.name === "links")) {
85
86
  throw new FilterInvalid(bareTargetMessage(node.name, ctx.depth), "OQX");
86
87
  }
87
- if (node.kind === "outer" && TARGETS.has(node.name) && node.levels > ctx.depth) {
88
+ if (node.kind === "outer" && ROOTS.has(node.name) && node.levels > ctx.depth) {
88
89
  throw new FilterInvalid(pastRootMessage(node.name, node.levels), "OQX");
89
90
  }
90
91
  },
@@ -165,8 +166,9 @@ function envelopeCollision(items) {
165
166
  return null;
166
167
  }
167
168
  // A hit is a store row (spec/surface §1.4, 1.5). The engine's top-level rows
168
- // are store rows by construction when the source is a bare root scan and
169
- // nothing re-projects them; a `follow` destination (`follow before` over a
169
+ // are store rows by construction when the source is a bare root scan (one of
170
+ // the five collections, `revisions` included — the planner's `rootTarget` is
171
+ // narrower: the four it can plan) and nothing re-projects them; a `follow` destination (`follow before` over a
170
172
  // frontmatter list of paths), a `from E` re-projection or any other source can
171
173
  // reach a scalar, which the injected `id`/`path` reads would render as the
172
174
  // junk hit `{ id: "undefined", path: "" }`. For those queries the row itself is
@@ -177,7 +179,7 @@ function envelopeCollision(items) {
177
179
  const SELF_KEY = "__oqx_self";
178
180
  const SELF_ITEM = build.field(SELF_KEY, build.ident("$it"));
179
181
  function mayReachNonRows(q) {
180
- return q.follow !== null || q.from.length > 0 || rootTarget(q.source) === null;
182
+ return q.follow !== null || q.from.length > 0 || !(q.source.kind === "ident" && ROOTS.has(q.source.name));
181
183
  }
182
184
  function describeValue(v) {
183
185
  if (v === null || v === undefined)
@@ -195,7 +197,7 @@ function describeValue(v) {
195
197
  return "an object";
196
198
  }
197
199
  function notAStoreRow(v) {
198
- return new FilterInvalid(`a hit must be a document, block, node or link row — the query reached ${describeValue(v)}; to follow document references held in a property use refs(<field>)`, "OQX");
200
+ return new FilterInvalid(`a hit must be a document, block, node, link or revision row — the query reached ${describeValue(v)}; to follow document references held in a property use refs(<field>)`, "OQX");
199
201
  }
200
202
  // The reserved key a top-level `values` projection's single item is renamed to,
201
203
  // so it rides through id/path injection, keyset paging, and distinct as an
@@ -294,19 +296,36 @@ function oqxRunInner(store, repoId, source, opts) {
294
296
  const reserved = reservedAlias(raw.select);
295
297
  if (reserved)
296
298
  throw reserved;
297
- return oqxRunQuery(store, repoId, raw, opts);
299
+ // The source entry is every read: a tier-3 name on an open kind is a hint.
300
+ return oqxRunQuery(store, repoId, raw, { unknownNames: "hint", ...opts });
298
301
  }
299
302
  /**
300
303
  * The AST entry (4.1): run an already parsed query — the source path above
301
304
  * after `parse`, and the selector compiler (`mutate/select.ts`), which appends
302
305
  * its slot items to the RAW tree so a user alias a template reads is inlined
303
306
  * by the alias resolution here. Errors are the library's own (`OqxError`);
304
- * `oqxRun` normalizes them, a caller of this entry does the same.
307
+ * `oqxRun` normalizes them, a caller of this entry does the same. Unknown
308
+ * names (4.6) default to `"refuse"` here: this entry's caller is the write
309
+ * selector, where a name no row carries would select every row; the source
310
+ * entry passes `"hint"`.
305
311
  */
306
312
  export function oqxRunQuery(store, repoId, raw, opts = {}) {
313
+ checkRootSpellings(raw);
314
+ // Unknown names (spec/surface §1.5): judged over the RAW tree — before alias
315
+ // resolution and the runner's own items — so an alias is never mistaken for
316
+ // a row property and the verdict never depends on the rows. The selector
317
+ // compiler's slot items (`__omg_sel_*`, the templates' expressions) are not
318
+ // the selector: an expression reading an absent property there is
319
+ // spec/mutate's `target_missing` / absent value, not an unknown name.
320
+ const own = { ...raw, select: raw.select.filter((it) => !RESERVED_ALIAS_PREFIXES.some((p) => it.name.startsWith(p))) };
321
+ const hints = checkNames(store, repoId, own, opts.unknownNames ?? "refuse");
322
+ const result = runResolved(store, repoId, raw, opts);
323
+ // `hints` is the last member, present only when there is something to say.
324
+ return hints.length === 0 ? result : { ...result, hints };
325
+ }
326
+ function runResolved(store, repoId, raw, opts) {
307
327
  // The query's `select` aliases are resolved HERE, once, before the runner
308
328
  // renames/injects items (an engine evaluates the query it is given).
309
- checkRootSpellings(raw);
310
329
  const parsed = rewriteQuery(resolveAliases(raw));
311
330
  const consumer = parsed.consumer;
312
331
  const ctxOpts = opts.semanticVectors ? { semanticVectors: opts.semanticVectors } : {};
@@ -53,7 +53,7 @@ import { TARGETS } from "../root-row.js";
53
53
  * `translate.test.ts` asserts this set against the store context.
54
54
  */
55
55
  export const NON_PROPERTY_NAMES = {
56
- docs: new Set(["doc", "nodes", "blocks", "out_links", "in_links", "data", "frontmatter", "inline"]),
56
+ docs: new Set(["doc", "nodes", "blocks", "out_links", "in_links", "revisions", "data", "frontmatter", "inline"]),
57
57
  blocks: new Set(["block", "doc", "children", "nodes", "out_links", "section", "attrs"]),
58
58
  nodes: new Set(["section", "doc", "block", "blocks", "subsections", "children", "attrs"]),
59
59
  links: new Set(["doc", "dst"]),
@@ -1,14 +1,14 @@
1
1
  export declare const SPEC_VERSIONS: {
2
- readonly oqx: "0.21";
2
+ readonly oqx: "0.23";
3
3
  readonly format: "0.2";
4
4
  readonly reconcile: "2.3";
5
- readonly store: "14.1";
5
+ readonly store: "14.2";
6
6
  readonly properties: "1.1";
7
7
  readonly graph: "1.1";
8
8
  readonly search: "2.0";
9
- readonly mutate: "3.3";
9
+ readonly mutate: "3.5";
10
10
  readonly sync: "2.2";
11
- readonly surface: "4.4";
12
- readonly cli: "4.3";
11
+ readonly surface: "4.7";
12
+ readonly cli: "4.5";
13
13
  };
14
14
  export type SpecName = keyof typeof SPEC_VERSIONS;
@@ -13,13 +13,13 @@ export const SPEC_VERSIONS = {
13
13
  oqx: LANGUAGE_VERSION,
14
14
  format: "0.2",
15
15
  reconcile: "2.3",
16
- store: "14.1",
16
+ store: "14.2",
17
17
  properties: "1.1",
18
18
  graph: "1.1",
19
19
  search: "2.0",
20
- mutate: "3.3",
20
+ mutate: "3.5",
21
21
  sync: "2.2",
22
- surface: "4.4",
23
- cli: "4.3",
22
+ surface: "4.7",
23
+ cli: "4.5",
24
24
  };
25
25
  //# sourceMappingURL=spec-versions.js.map
@@ -189,9 +189,15 @@ function commitPrepared(store, repoId, prepared, ts, oldHashHex, newHashHex) {
189
189
  const conflicted = hasConflictMarkers(content);
190
190
  const resolveIds = makeReconcilingResolver(store, repoId, { ts, path, prepared });
191
191
  const res = ingestFile(store, repoId, path, content, { ts, origin: "observed", resolveIds, parsed: prepared.tree, conflicted });
192
- const rows = store.db
193
- .prepare("SELECT kind, count(*) n FROM dispositions WHERE commit_id = ? GROUP BY kind ORDER BY kind")
194
- .all(res.commitId);
192
+ // The outcome counts every verdict the matcher produced, `same` included —
193
+ // it describes what the matcher saw; the `dispositions` table describes
194
+ // what changed (spec/store §5.4 step 10, 14.2: `same` is not persisted), so
195
+ // the counts come from the in-memory result, not the table. Kinds in
196
+ // bytewise order, as the table query listed them through 14.1.
197
+ const counts = new Map();
198
+ for (const d of prepared.result.dispositions)
199
+ counts.set(d.kind, (counts.get(d.kind) ?? 0) + 1);
200
+ const rows = [...counts].map(([kind, n]) => ({ kind, n })).sort((a, b) => (a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0));
195
201
  return {
196
202
  docId: res.docId,
197
203
  path,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omgbase/core",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Open Markdown Graph Base — embedded engine",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "@modelcontextprotocol/sdk": "^1.30.0",
58
- "@omgbase/oqx": "^0.21.0",
58
+ "@omgbase/oqx": "^0.23.0",
59
59
  "better-sqlite3": "^13.0.3",
60
60
  "mdast-util-from-markdown": "^2.0.2",
61
61
  "mdast-util-frontmatter": "^2.0.1",