@omgbase/core 0.10.1 → 0.11.1

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 (63) hide show
  1. package/README.md +10 -10
  2. package/dist/src/core/read/document.d.ts +18 -5
  3. package/dist/src/core/read/document.js +21 -7
  4. package/dist/src/core/read/nodes.d.ts +7 -7
  5. package/dist/src/core/read/nodes.js +5 -3
  6. package/dist/src/core/read/reader.d.ts +11 -7
  7. package/dist/src/core/read/reader.js +8 -10
  8. package/dist/src/format/adapter.d.ts +1 -1
  9. package/dist/src/graph/history.d.ts +20 -14
  10. package/dist/src/graph/history.js +12 -12
  11. package/dist/src/graph/inbound-links.d.ts +4 -4
  12. package/dist/src/graph/inbound-links.js +4 -4
  13. package/dist/src/graph/link-health.d.ts +8 -8
  14. package/dist/src/graph/link-health.js +6 -6
  15. package/dist/src/graph/links.d.ts +3 -3
  16. package/dist/src/graph/links.js +4 -4
  17. package/dist/src/index.d.ts +2 -1
  18. package/dist/src/index.js +2 -1
  19. package/dist/src/mcp/arguments.d.ts +27 -0
  20. package/dist/src/mcp/arguments.js +147 -0
  21. package/dist/src/mcp/errors.d.ts +1 -1
  22. package/dist/src/mcp/graph.d.ts +16 -9
  23. package/dist/src/mcp/graph.js +58 -35
  24. package/dist/src/mcp/reference.d.ts +1 -1
  25. package/dist/src/mcp/reference.js +200 -111
  26. package/dist/src/mcp/server.d.ts +9 -1
  27. package/dist/src/mcp/server.js +359 -268
  28. package/dist/src/mutate/apply.d.ts +11 -4
  29. package/dist/src/mutate/apply.js +3 -2
  30. package/dist/src/mutate/docs.js +7 -7
  31. package/dist/src/mutate/macros.d.ts +4 -1
  32. package/dist/src/mutate/macros.js +1 -1
  33. package/dist/src/mutate/ops.js +2 -2
  34. package/dist/src/mutate/opset.d.ts +8 -5
  35. package/dist/src/mutate/plan-update.js +9 -8
  36. package/dist/src/oqx-js/context.d.ts +17 -4
  37. package/dist/src/oqx-js/context.js +268 -131
  38. package/dist/src/oqx-js/planner.d.ts +3 -3
  39. package/dist/src/oqx-js/planner.js +14 -15
  40. package/dist/src/oqx-js/root-row.d.ts +10 -3
  41. package/dist/src/oqx-js/root-row.js +18 -8
  42. package/dist/src/oqx-js/run.d.ts +6 -0
  43. package/dist/src/oqx-js/run.js +72 -25
  44. package/dist/src/oqx-js/sql/scan.js +7 -7
  45. package/dist/src/oqx-js/sql/translate.d.ts +12 -9
  46. package/dist/src/oqx-js/sql/translate.js +183 -75
  47. package/dist/src/oqx-js/store-index.d.ts +1 -1
  48. package/dist/src/oqx-js/store-index.js +28 -26
  49. package/dist/src/search/cel/ast.d.ts +1 -1
  50. package/dist/src/search/cel/compile.d.ts +1 -1
  51. package/dist/src/search/cel/compile.js +43 -70
  52. package/dist/src/search/cel/parser.js +1 -1
  53. package/dist/src/search/resolve.d.ts +2 -0
  54. package/dist/src/search/rrf.js +2 -2
  55. package/dist/src/search/text.d.ts +2 -2
  56. package/dist/src/search/text.js +1 -1
  57. package/dist/src/spec-versions.d.ts +6 -6
  58. package/dist/src/spec-versions.js +5 -5
  59. package/dist/src/surface-paths.d.ts +41 -8
  60. package/dist/src/surface-paths.js +34 -18
  61. package/dist/src/sync/admin.d.ts +2 -1
  62. package/dist/src/sync/admin.js +1 -1
  63. package/package.json +2 -2
@@ -77,12 +77,19 @@ export interface ApplyRequest {
77
77
  export interface OpResult {
78
78
  ids: string[];
79
79
  }
80
+ /** One touched document in an apply result (spec/mutate §4, 2.0): its row
81
+ * attributes `id`, `path` (the storage form here; the surface roots it) and
82
+ * `hash` — the hex sha256 of the rendered bytes, i.e. the hash the committed
83
+ * revision carries (a dry run reports the hash it would carry). The same
84
+ * three keys a `changes_since` revision carries. */
85
+ export interface ApplyRevision {
86
+ id: string;
87
+ path: string;
88
+ hash: string;
89
+ }
80
90
  export interface ApplyResult {
81
91
  results: OpResult[];
82
- revisions: {
83
- doc: string;
84
- path: string;
85
- }[];
92
+ revisions: ApplyRevision[];
86
93
  diffs?: Record<string, {
87
94
  before: string;
88
95
  after: string;
@@ -152,8 +152,9 @@ export function apply(store, req) {
152
152
  const revisions = [];
153
153
  const diffs = {};
154
154
  for (const [docId, d] of loaded) {
155
- diffs[d.path] = { before: before.get(docId) ?? "", after: renderDoc(d) };
156
- revisions.push({ doc: docId, path: d.path });
155
+ const after = renderDoc(d);
156
+ diffs[d.path] = { before: before.get(docId) ?? "", after };
157
+ revisions.push({ id: docId, path: d.path, hash: sha256(after).toString("hex") });
157
158
  }
158
159
  if (req.dryRun) {
159
160
  return { results, revisions, diffs, committed: false };
@@ -121,9 +121,9 @@ export function docsMove(store, ctx, docRef, toPath, opts = {}) {
121
121
  db.prepare("UPDATE edges SET dst_node = ? WHERE dst_node = ? AND to_commit IS NULL AND src_doc != ?").run(phantom, info.docId, info.docId);
122
122
  const selfEdge = db.prepare("UPDATE edges SET dst_node = ? WHERE dst_node = ? AND to_commit IS NULL AND src_doc = ? AND src_block = ? AND anchor IS ?");
123
123
  for (const l of inbound) {
124
- if (l.doc !== info.docId || l.block === null)
124
+ if (l.doc_id !== info.docId || l.block_id === null)
125
125
  continue;
126
- selfEdge.run(phantom, info.docId, info.docId, l.block, l.anchor);
126
+ selfEdge.run(phantom, info.docId, info.docId, l.block_id, l.anchor);
127
127
  affected.add(info.docId);
128
128
  }
129
129
  for (const d of affected)
@@ -172,8 +172,8 @@ function applyCtx(ctx) {
172
172
  function planInboundRetarget(store, docId, fromPath, toRel, inbound) {
173
173
  const byBlock = new Map();
174
174
  for (const l of inbound)
175
- if (l.block !== null && !byBlock.has(l.block))
176
- byBlock.set(l.block, l);
175
+ if (l.block_id !== null && !byBlock.has(l.block_id))
176
+ byBlock.set(l.block_id, l);
177
177
  // A container's raw includes its children's raw, so a link inside a list item
178
178
  // is reported for BOTH the list and the item. Rewrite the deepest block only:
179
179
  // updating the container would re-parse (and re-identify) its children, and
@@ -202,17 +202,17 @@ function planInboundRetarget(store, docId, fromPath, toRel, inbound) {
202
202
  // Match against the directory the link was RESOLVED in (the source's path at
203
203
  // extraction time); write relative forms against the source's CURRENT
204
204
  // directory — they differ only for links inside the moved doc itself.
205
- const writeDir = src.doc === docId ? dirOf(toRel) : dirOf(src.path);
205
+ const writeDir = src.doc_id === docId ? dirOf(toRel) : dirOf(src.path);
206
206
  const newRaw = retargetLinksInRaw(row.bytes.toString("utf8"), dirOf(src.path), writeDir, fromPath, toRel);
207
207
  if (newRaw === null)
208
208
  continue;
209
209
  ops.push({ op: "update", block: blockId, markdown: newRaw, expect: { content_hash: row.raw_hash.toString("hex") } });
210
210
  blocks.push(blockId);
211
- docs.add(src.doc);
211
+ docs.add(src.doc_id);
212
212
  }
213
213
  // Containers whose rewritten child covered them count as rewritten too.
214
214
  const rewritten = new Set([...blocks, ...containers]);
215
- const dangling = inbound.filter((l) => l.block === null || !rewritten.has(l.block));
215
+ const dangling = inbound.filter((l) => l.block_id === null || !rewritten.has(l.block_id));
216
216
  return { ops, blocks, docs: [...docs], dangling };
217
217
  }
218
218
  /** docs_delete: mark a document deleted and remove its file (resurrection-poolable). */
@@ -28,8 +28,11 @@ export declare function listsInsertItem(anchor: string, at: "start" | "end" | {
28
28
  } | {
29
29
  after: string;
30
30
  }, markdown: string): Op[];
31
+ /** One rewritten block of a retarget/repair (spec/surface §4, 3.0): the block's
32
+ * `id`, its document's `path` (the storage form here; the surface roots it),
33
+ * and the raw before/after. */
31
34
  export interface RetargetHit {
32
- block: string;
35
+ id: string;
33
36
  /** repo-relative path of the block's document */
34
37
  path: string;
35
38
  oldRaw: string;
@@ -230,7 +230,7 @@ export function linksRepair(store, repoId, repairs, opts = {}) {
230
230
  c.tally.forEach((n, i) => { effective[i].hits += n; });
231
231
  const hash = rawHashOfBlock(store, blockId);
232
232
  ops.push({ op: "update", block: blockId, markdown: c.newRaw, ...(hash ? { expect: { content_hash: hash } } : {}) });
233
- hits.push({ block: blockId, path: c.path, oldRaw: c.oldRaw, newRaw: c.newRaw });
233
+ hits.push({ id: blockId, path: c.path, oldRaw: c.oldRaw, newRaw: c.newRaw });
234
234
  }
235
235
  return { ops, hits, pairs };
236
236
  }
@@ -109,7 +109,7 @@ function checkContentHash(block, expect, opIndex) {
109
109
  throw new MutationError("stale_expectation", "expect.content_hash required", {
110
110
  op_index: opIndex,
111
111
  block: block.id,
112
- current: { content_hash: rawHashHex(block.raw), markdown: block.raw },
112
+ current: { hash: rawHashHex(block.raw), markdown: block.raw },
113
113
  retriable: true,
114
114
  });
115
115
  }
@@ -119,7 +119,7 @@ function checkContentHash(block, expect, opIndex) {
119
119
  op_index: opIndex,
120
120
  block: block.id,
121
121
  expected_content_hash: expect.content_hash,
122
- current: { content_hash: current, markdown: block.raw },
122
+ current: { hash: current, markdown: block.raw },
123
123
  retriable: true,
124
124
  });
125
125
  }
@@ -8,15 +8,18 @@ export type OpsetKind = "doc_update";
8
8
  * reproduce the proposed bytes exactly.
9
9
  */
10
10
  export type PlanDisposition = "same" | "edited" | "moved" | "edited_moved" | "inserted" | "deleted" | "split_from" | "merged_into" | "copied_from" | "resurrected" | "bulk_rewrite" | "retiled";
11
- /** State the opset was planned against; applying against different state fails. */
11
+ /** State the opset was planned against; applying against different state
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;
14
+ * `baseRevision` names a role and keeps its name. */
12
15
  export interface OpsetPrecondition {
13
16
  /** doc id the plan targets */
14
- doc: string;
17
+ id: string;
15
18
  path: string;
16
19
  /** the document's current revision id at plan time (null for a doc with no committed revision yet) */
17
20
  baseRevision: string | null;
18
- /** sha256 hex of the document's current rendered bytes (docs.file_hash) at plan time */
19
- baseContentHash: string;
21
+ /** sha256 hex of the document's current rendered bytes (docs.file_hash) at plan time — its `hash` */
22
+ hash: string;
20
23
  }
21
24
  /** One kernel op plus the identity consequence the planner attributes to it. */
22
25
  export interface PlanOp {
@@ -51,7 +54,7 @@ export interface Opset {
51
54
  version: 1;
52
55
  kind: OpsetKind;
53
56
  target: {
54
- doc: string;
57
+ id: string;
55
58
  path: string;
56
59
  };
57
60
  precondition: OpsetPrecondition;
@@ -84,8 +84,8 @@ export function planUpdate(store, repoId, rootPath, docRef, content, opts = {})
84
84
  return {
85
85
  version: 1,
86
86
  kind: "doc_update",
87
- target: { doc: docId, path },
88
- precondition: { doc: docId, path, baseRevision: info.currentRev, baseContentHash },
87
+ target: { id: docId, path: path },
88
+ precondition: { id: docId, path: path, baseRevision: info.currentRev, hash: baseContentHash },
89
89
  matcherV: config.matcherV,
90
90
  ops,
91
91
  ...(fmChanged ? { frontmatter: { raw: targetFm } } : {}),
@@ -145,15 +145,16 @@ export function applyOpset(store, req) {
145
145
  }
146
146
  // Precondition: the document must still be at the revision/content the plan
147
147
  // was computed against.
148
- const row = store.db.prepare("SELECT current_rev, lower(hex(file_hash)) fh FROM docs WHERE doc_id = ? AND deleted_commit IS NULL").get(opset.precondition.doc);
148
+ const docId = opset.precondition.id;
149
+ const row = store.db.prepare("SELECT current_rev, lower(hex(file_hash)) fh FROM docs WHERE doc_id = ? AND deleted_commit IS NULL").get(docId);
149
150
  if (!row)
150
- throw new MutationError("doc_missing", `doc ${opset.precondition.doc} not found`, { doc: opset.precondition.doc });
151
- const stale = (opset.precondition.baseRevision !== null && row.current_rev !== opset.precondition.baseRevision) || (row.fh !== null && row.fh !== opset.precondition.baseContentHash);
151
+ throw new MutationError("doc_missing", `doc ${docId} not found`, { doc: docId });
152
+ const stale = (opset.precondition.baseRevision !== null && row.current_rev !== opset.precondition.baseRevision) || (row.fh !== null && row.fh !== opset.precondition.hash);
152
153
  if (stale) {
153
154
  throw new MutationError("stale_plan", "document changed since the plan was computed; re-plan", {
154
- doc: opset.precondition.doc,
155
+ doc: docId,
155
156
  expected_revision: opset.precondition.baseRevision,
156
- current: { revision: row.current_rev, content_hash: row.fh },
157
+ current: { revision: row.current_rev, hash: row.fh },
157
158
  retriable: true,
158
159
  });
159
160
  }
@@ -166,7 +167,7 @@ export function applyOpset(store, req) {
166
167
  ...(req.dryRun !== undefined ? { dryRun: req.dryRun } : {}),
167
168
  ...(req.omgbaseDir !== undefined ? { omgbaseDir: req.omgbaseDir } : {}),
168
169
  ...(req.ts !== undefined ? { ts: req.ts } : {}),
169
- ...(opset.frontmatter ? { setFrontmatter: [{ doc: opset.precondition.doc, raw: opset.frontmatter.raw }] } : {}),
170
+ ...(opset.frontmatter ? { setFrontmatter: [{ doc: docId, raw: opset.frontmatter.raw }] } : {}),
170
171
  });
171
172
  }
172
173
  /**
@@ -1,8 +1,20 @@
1
1
  import type { DataContext } from "@omgbase/oqx";
2
2
  import type { Store } from "../core/store/store.js";
3
3
  import type { SemanticVec } from "../search/cel/compile.js";
4
- export type Target = "docs" | "blocks" | "nodes" | "edges";
4
+ export type Target = "docs" | "blocks" | "nodes" | "links";
5
5
  declare const TARGET: unique symbol;
6
+ /** The engine attributes per kind (spec/surface §1.2 table). Exported so the
7
+ * translator's tests pin the two against each other. */
8
+ export declare const ATTRIBUTES: Readonly<Record<Target, ReadonlySet<string>>>;
9
+ /** The source handles of a document (§1.2): `data` is the merged authored view
10
+ * (frontmatter + inline), the other two one source each. */
11
+ export declare const SOURCE_HANDLES: ReadonlySet<string>;
12
+ /** The value of a statically SINGLE relation that has no row — `dst` on an
13
+ * external or dangling link, `block` on a node without one (oqx 0.20, SEMANTICS
14
+ * §3): an object the engine treats as one row with no rows, so `dst { title }`
15
+ * is absent rather than `[]`, while as a VALUE it is absent (`materialize`),
16
+ * so `dst == null`, `has(dst)` and `dst.title` read as before. */
17
+ export declare const ABSENT_SINGLE: object;
6
18
  export interface StoreContextOptions {
7
19
  /** Pre-computed query vectors for `semantic("phrase")`, keyed by phrase (the
8
20
  * async runner fills this; the sync path leaves it empty and a semantic() with
@@ -22,9 +34,10 @@ export declare function makeStoreContext(store: Store, repoId: string, opts?: St
22
34
  export declare function tagRows(rows: Record<string, unknown>[], target: Target): unknown[];
23
35
  /**
24
36
  * A store row surfacing as a VALUE in a result (spec/surface §1.4, rows as
25
- * values, 1.2) is rendered as `{ id, path }` — its id column and its document's
26
- * path in the reference form (`/a.md`, §1 "Paths"), the same two keys every hit
27
- * carries — never the store row itself.
37
+ * values, 1.2) is rendered as `{ id, path }` (3.0) — its id column and its
38
+ * document's path in the reference form (`/a.md`, §1 "Paths"), the same two keys
39
+ * every hit carries, spelled as the attributes that read them — never the store
40
+ * row itself.
28
41
  * Returns undefined for anything that is not a tagged row (run.ts leaves those
29
42
  * alone).
30
43
  */