@omgbase/core 0.1.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.
- package/LICENSE +21 -0
- package/dist/src/cli/eval-matcher.d.ts +13 -0
- package/dist/src/cli/eval-matcher.js +36 -0
- package/dist/src/core/attach.d.ts +5 -0
- package/dist/src/core/attach.js +38 -0
- package/dist/src/core/cursor.d.ts +11 -0
- package/dist/src/core/cursor.js +35 -0
- package/dist/src/core/hash.d.ts +23 -0
- package/dist/src/core/hash.js +82 -0
- package/dist/src/core/ids.d.ts +6 -0
- package/dist/src/core/ids.js +26 -0
- package/dist/src/core/index.d.ts +1 -0
- package/dist/src/core/index.js +2 -0
- package/dist/src/core/ingest.d.ts +43 -0
- package/dist/src/core/ingest.js +287 -0
- package/dist/src/core/order-key.d.ts +8 -0
- package/dist/src/core/order-key.js +72 -0
- package/dist/src/core/parse/parse.d.ts +3 -0
- package/dist/src/core/parse/parse.js +104 -0
- package/dist/src/core/parse/render.d.ts +3 -0
- package/dist/src/core/parse/render.js +20 -0
- package/dist/src/core/parse/tree.d.ts +8 -0
- package/dist/src/core/parse/tree.js +43 -0
- package/dist/src/core/parse/types.d.ts +52 -0
- package/dist/src/core/parse/types.js +9 -0
- package/dist/src/core/read/document.d.ts +107 -0
- package/dist/src/core/read/document.js +191 -0
- package/dist/src/core/read/nodes.d.ts +47 -0
- package/dist/src/core/read/nodes.js +102 -0
- package/dist/src/core/read/outline.d.ts +12 -0
- package/dist/src/core/read/outline.js +76 -0
- package/dist/src/core/read/reader.d.ts +125 -0
- package/dist/src/core/read/reader.js +206 -0
- package/dist/src/core/read/refs.d.ts +15 -0
- package/dist/src/core/read/refs.js +51 -0
- package/dist/src/core/store/edges.d.ts +26 -0
- package/dist/src/core/store/edges.js +101 -0
- package/dist/src/core/store/fts.d.ts +10 -0
- package/dist/src/core/store/fts.js +22 -0
- package/dist/src/core/store/gc.d.ts +18 -0
- package/dist/src/core/store/gc.js +69 -0
- package/dist/src/core/store/nodes.d.ts +5 -0
- package/dist/src/core/store/nodes.js +57 -0
- package/dist/src/core/store/properties.d.ts +38 -0
- package/dist/src/core/store/properties.js +177 -0
- package/dist/src/core/store/rebuild.d.ts +3 -0
- package/dist/src/core/store/rebuild.js +29 -0
- package/dist/src/core/store/schema.d.ts +9 -0
- package/dist/src/core/store/schema.js +386 -0
- package/dist/src/core/store/sections.d.ts +2 -0
- package/dist/src/core/store/sections.js +29 -0
- package/dist/src/core/store/store.d.ts +24 -0
- package/dist/src/core/store/store.js +164 -0
- package/dist/src/core/store/writers.d.ts +52 -0
- package/dist/src/core/store/writers.js +89 -0
- package/dist/src/core/vec.d.ts +10 -0
- package/dist/src/core/vec.js +33 -0
- package/dist/src/format/adapter.d.ts +97 -0
- package/dist/src/format/adapter.js +14 -0
- package/dist/src/format/index.d.ts +6 -0
- package/dist/src/format/index.js +15 -0
- package/dist/src/format/json.d.ts +3 -0
- package/dist/src/format/json.js +324 -0
- package/dist/src/format/markdown.d.ts +5 -0
- package/dist/src/format/markdown.js +217 -0
- package/dist/src/format/registry.d.ts +6 -0
- package/dist/src/format/registry.js +24 -0
- package/dist/src/format/yaml.d.ts +5 -0
- package/dist/src/format/yaml.js +413 -0
- package/dist/src/graph/extract.d.ts +38 -0
- package/dist/src/graph/extract.js +242 -0
- package/dist/src/graph/history.d.ts +103 -0
- package/dist/src/graph/history.js +210 -0
- package/dist/src/graph/inbound-links.d.ts +33 -0
- package/dist/src/graph/inbound-links.js +130 -0
- package/dist/src/graph/link-destinations.d.ts +22 -0
- package/dist/src/graph/link-destinations.js +84 -0
- package/dist/src/graph/link-health.d.ts +79 -0
- package/dist/src/graph/link-health.js +146 -0
- package/dist/src/graph/links.d.ts +23 -0
- package/dist/src/graph/links.js +42 -0
- package/dist/src/index.d.ts +60 -0
- package/dist/src/index.js +70 -0
- package/dist/src/mcp/errors.d.ts +17 -0
- package/dist/src/mcp/errors.js +18 -0
- package/dist/src/mcp/graph.d.ts +63 -0
- package/dist/src/mcp/graph.js +212 -0
- package/dist/src/mcp/reference.d.ts +1 -0
- package/dist/src/mcp/reference.js +227 -0
- package/dist/src/mcp/server.d.ts +30 -0
- package/dist/src/mcp/server.js +1076 -0
- package/dist/src/mcp/stdio.d.ts +13 -0
- package/dist/src/mcp/stdio.js +24 -0
- package/dist/src/migrate/mrplex.d.ts +27 -0
- package/dist/src/migrate/mrplex.js +29 -0
- package/dist/src/mutate/apply.d.ts +83 -0
- package/dist/src/mutate/apply.js +302 -0
- package/dist/src/mutate/doc-store.d.ts +49 -0
- package/dist/src/mutate/doc-store.js +71 -0
- package/dist/src/mutate/docs.d.ts +60 -0
- package/dist/src/mutate/docs.js +232 -0
- package/dist/src/mutate/known-ids.d.ts +12 -0
- package/dist/src/mutate/known-ids.js +154 -0
- package/dist/src/mutate/load.d.ts +3 -0
- package/dist/src/mutate/load.js +66 -0
- package/dist/src/mutate/lower.d.ts +32 -0
- package/dist/src/mutate/lower.js +232 -0
- package/dist/src/mutate/macros.d.ts +91 -0
- package/dist/src/mutate/macros.js +230 -0
- package/dist/src/mutate/ops.d.ts +40 -0
- package/dist/src/mutate/ops.js +513 -0
- package/dist/src/mutate/opset.d.ts +92 -0
- package/dist/src/mutate/opset.js +79 -0
- package/dist/src/mutate/plan-update.d.ts +51 -0
- package/dist/src/mutate/plan-update.js +187 -0
- package/dist/src/mutate/tree.d.ts +46 -0
- package/dist/src/mutate/tree.js +167 -0
- package/dist/src/oqx/run.d.ts +1 -0
- package/dist/src/oqx/run.js +6 -0
- package/dist/src/oqx-js/context.d.ts +23 -0
- package/dist/src/oqx-js/context.js +564 -0
- package/dist/src/oqx-js/planner.d.ts +10 -0
- package/dist/src/oqx-js/planner.js +91 -0
- package/dist/src/oqx-js/run.d.ts +41 -0
- package/dist/src/oqx-js/run.js +332 -0
- package/dist/src/oqx-js/sql/translate.d.ts +27 -0
- package/dist/src/oqx-js/sql/translate.js +290 -0
- package/dist/src/reconcile/crossdoc.d.ts +34 -0
- package/dist/src/reconcile/crossdoc.js +76 -0
- package/dist/src/reconcile/eval/generator.d.ts +21 -0
- package/dist/src/reconcile/eval/generator.js +129 -0
- package/dist/src/reconcile/eval/metrics.d.ts +32 -0
- package/dist/src/reconcile/eval/metrics.js +74 -0
- package/dist/src/reconcile/flatten.d.ts +12 -0
- package/dist/src/reconcile/flatten.js +37 -0
- package/dist/src/reconcile/phase5.d.ts +8 -0
- package/dist/src/reconcile/phase5.js +167 -0
- package/dist/src/reconcile/phase6a.d.ts +2 -0
- package/dist/src/reconcile/phase6a.js +159 -0
- package/dist/src/reconcile/phases.d.ts +28 -0
- package/dist/src/reconcile/phases.js +0 -0
- package/dist/src/reconcile/reconcile.d.ts +21 -0
- package/dist/src/reconcile/reconcile.js +115 -0
- package/dist/src/reconcile/similarity.d.ts +7 -0
- package/dist/src/reconcile/similarity.js +41 -0
- package/dist/src/reconcile/types.d.ts +46 -0
- package/dist/src/reconcile/types.js +19 -0
- package/dist/src/search/cel/ast.d.ts +74 -0
- package/dist/src/search/cel/ast.js +3 -0
- package/dist/src/search/cel/compile.d.ts +60 -0
- package/dist/src/search/cel/compile.js +836 -0
- package/dist/src/search/cel/lexer.d.ts +9 -0
- package/dist/src/search/cel/lexer.js +139 -0
- package/dist/src/search/cel/parser.d.ts +7 -0
- package/dist/src/search/cel/parser.js +219 -0
- package/dist/src/search/drain.d.ts +39 -0
- package/dist/src/search/drain.js +97 -0
- package/dist/src/search/embeddings.d.ts +131 -0
- package/dist/src/search/embeddings.js +263 -0
- package/dist/src/search/external.d.ts +13 -0
- package/dist/src/search/external.js +177 -0
- package/dist/src/search/fts-query.d.ts +1 -0
- package/dist/src/search/fts-query.js +64 -0
- package/dist/src/search/provider.d.ts +17 -0
- package/dist/src/search/provider.js +13 -0
- package/dist/src/search/resolve.d.ts +18 -0
- package/dist/src/search/resolve.js +23 -0
- package/dist/src/search/rrf.d.ts +36 -0
- package/dist/src/search/rrf.js +84 -0
- package/dist/src/search/tasks.d.ts +12 -0
- package/dist/src/search/tasks.js +131 -0
- package/dist/src/search/text.d.ts +16 -0
- package/dist/src/search/text.js +21 -0
- package/dist/src/search/vector.d.ts +18 -0
- package/dist/src/search/vector.js +49 -0
- package/dist/src/sync/admin.d.ts +59 -0
- package/dist/src/sync/admin.js +53 -0
- package/dist/src/sync/attach.d.ts +13 -0
- package/dist/src/sync/attach.js +29 -0
- package/dist/src/sync/checkpoint.d.ts +21 -0
- package/dist/src/sync/checkpoint.js +52 -0
- package/dist/src/sync/driver.d.ts +25 -0
- package/dist/src/sync/driver.js +80 -0
- package/dist/src/sync/external-source.d.ts +14 -0
- package/dist/src/sync/external-source.js +137 -0
- package/dist/src/sync/freshness.d.ts +42 -0
- package/dist/src/sync/freshness.js +152 -0
- package/dist/src/sync/fs-util.d.ts +12 -0
- package/dist/src/sync/fs-util.js +55 -0
- package/dist/src/sync/git-heuristics.d.ts +6 -0
- package/dist/src/sync/git-heuristics.js +16 -0
- package/dist/src/sync/observe.d.ts +63 -0
- package/dist/src/sync/observe.js +84 -0
- package/dist/src/sync/plugin.d.ts +54 -0
- package/dist/src/sync/plugin.js +12 -0
- package/dist/src/sync/reconciling-ingest.d.ts +12 -0
- package/dist/src/sync/reconciling-ingest.js +209 -0
- package/dist/src/sync/recovery.d.ts +6 -0
- package/dist/src/sync/recovery.js +34 -0
- package/dist/src/sync/settings.d.ts +18 -0
- package/dist/src/sync/settings.js +58 -0
- package/dist/src/sync/sources.d.ts +37 -0
- package/dist/src/sync/sources.js +75 -0
- package/dist/src/sync/tombstone.d.ts +9 -0
- package/dist/src/sync/tombstone.js +55 -0
- package/dist/src/sync/watch-lease.d.ts +14 -0
- package/dist/src/sync/watch-lease.js +91 -0
- package/dist/src/sync/watcher.d.ts +19 -0
- package/dist/src/sync/watcher.js +34 -0
- package/dist/src/sync/workspace.d.ts +34 -0
- package/dist/src/sync/workspace.js +120 -0
- package/dist/src/sync/writer-lock.d.ts +16 -0
- package/dist/src/sync/writer-lock.js +121 -0
- package/package.json +80 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** The bare kernel ops, in order — what `apply` actually replays. */
|
|
2
|
+
export function opsetKernelOps(opset) {
|
|
3
|
+
return opset.ops.map((p) => p.op);
|
|
4
|
+
}
|
|
5
|
+
/** Compute the summary from the planned ops + carried/ambiguous counts. */
|
|
6
|
+
export function summarizeOps(ops, counts) {
|
|
7
|
+
const s = {
|
|
8
|
+
preserved: counts.preserved,
|
|
9
|
+
updated: 0, moved: 0, created: 0, removed: 0, split: 0, merged: 0,
|
|
10
|
+
ambiguous: counts.ambiguous,
|
|
11
|
+
};
|
|
12
|
+
for (const p of ops) {
|
|
13
|
+
switch (p.disposition) {
|
|
14
|
+
case "edited":
|
|
15
|
+
s.updated++;
|
|
16
|
+
break;
|
|
17
|
+
case "moved":
|
|
18
|
+
s.moved++;
|
|
19
|
+
break;
|
|
20
|
+
case "edited_moved":
|
|
21
|
+
s.updated++;
|
|
22
|
+
s.moved++;
|
|
23
|
+
break;
|
|
24
|
+
case "inserted":
|
|
25
|
+
case "copied_from":
|
|
26
|
+
case "resurrected":
|
|
27
|
+
s.created++;
|
|
28
|
+
break;
|
|
29
|
+
case "deleted":
|
|
30
|
+
s.removed++;
|
|
31
|
+
break;
|
|
32
|
+
case "split_from":
|
|
33
|
+
s.split++;
|
|
34
|
+
break;
|
|
35
|
+
case "merged_into":
|
|
36
|
+
s.merged++;
|
|
37
|
+
break;
|
|
38
|
+
// "retiled"/"same"/"bulk_rewrite" carry no summary bucket of their own.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return s;
|
|
42
|
+
}
|
|
43
|
+
/** Serialize an opset to a stable JSON string (ops are plain JSON). */
|
|
44
|
+
export function serializeOpset(opset) {
|
|
45
|
+
return JSON.stringify(opset);
|
|
46
|
+
}
|
|
47
|
+
/** Parse a serialized opset, validating the version tag. */
|
|
48
|
+
export function parseOpset(text) {
|
|
49
|
+
const o = JSON.parse(text);
|
|
50
|
+
if (o.version !== 1 || o.kind !== "doc_update") {
|
|
51
|
+
throw new Error(`unrecognized opset (version ${String(o.version)})`);
|
|
52
|
+
}
|
|
53
|
+
return o;
|
|
54
|
+
}
|
|
55
|
+
// A short human-/agent-readable rendering that makes identity consequences
|
|
56
|
+
// obvious (04-mutation §, the note's example). One line per op + a summary.
|
|
57
|
+
const VERB = {
|
|
58
|
+
same: "KEEP ", edited: "UPDATE", moved: "MOVE ", edited_moved: "UPDATE",
|
|
59
|
+
inserted: "INSERT", deleted: "REMOVE", split_from: "SPLIT ", merged_into: "MERGE ",
|
|
60
|
+
copied_from: "COPY ", resurrected: "RESURR", bulk_rewrite: "REWRITE", retiled: "RETILE",
|
|
61
|
+
};
|
|
62
|
+
export function renderOpsetPlan(opset) {
|
|
63
|
+
const lines = [];
|
|
64
|
+
for (const p of opset.ops) {
|
|
65
|
+
const subject = p.blocks[0] ?? "(new)";
|
|
66
|
+
const conf = p.confidence !== null ? ` ~${p.confidence.toFixed(2)}` : "";
|
|
67
|
+
const why = p.reason ? ` [${p.reason}]` : "";
|
|
68
|
+
lines.push(`${VERB[p.disposition]} ${subject.padEnd(9)} ${p.disposition}${conf}${why}`);
|
|
69
|
+
}
|
|
70
|
+
const s = opset.summary;
|
|
71
|
+
lines.push("");
|
|
72
|
+
lines.push(`preserved: ${s.preserved} updated: ${s.updated} moved: ${s.moved} ` +
|
|
73
|
+
`created: ${s.created} removed: ${s.removed} split: ${s.split} ` +
|
|
74
|
+
`merged: ${s.merged} ambiguous: ${s.ambiguous}`);
|
|
75
|
+
if (!opset.converges)
|
|
76
|
+
lines.push(`WARNING: plan does not reproduce the proposed content exactly — will not apply.`);
|
|
77
|
+
return lines.join("\n");
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=opset.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Store } from "../core/store/store.js";
|
|
2
|
+
import { type ReconcileConfig } from "../reconcile/types.js";
|
|
3
|
+
import { type ApplyResult } from "./apply.js";
|
|
4
|
+
import { type Opset } from "./opset.js";
|
|
5
|
+
export interface PlanUpdateOptions {
|
|
6
|
+
config?: ReconcileConfig;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Plan a whole-document update. `docRef` is a doc id or repo-relative path;
|
|
10
|
+
* `content` is the proposed complete document. Throws doc_missing if the
|
|
11
|
+
* document does not exist (a create is docsCreate's job). Never writes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function planUpdate(store: Store, repoId: string, rootPath: string, docRef: string, content: string, opts?: PlanUpdateOptions): Opset;
|
|
14
|
+
export interface ApplyOpsetRequest {
|
|
15
|
+
repoId: string;
|
|
16
|
+
rootPath: string;
|
|
17
|
+
opset: Opset;
|
|
18
|
+
origin: {
|
|
19
|
+
actor: string;
|
|
20
|
+
reason?: string;
|
|
21
|
+
};
|
|
22
|
+
dryRun?: boolean;
|
|
23
|
+
omgbaseDir?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Apply an opset through the kernel write path. Validates the plan's
|
|
27
|
+
* preconditions against current state first (a stale plan fails, carrying
|
|
28
|
+
* current truth, rather than silently reinterpreting) and refuses a plan that
|
|
29
|
+
* did not verify byte-convergent.
|
|
30
|
+
*/
|
|
31
|
+
export declare function applyOpset(store: Store, req: ApplyOpsetRequest): ApplyResult;
|
|
32
|
+
export interface DocsUpdateContext {
|
|
33
|
+
repoId: string;
|
|
34
|
+
rootPath: string;
|
|
35
|
+
omgbaseDir?: string;
|
|
36
|
+
actor?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface DocsUpdateResult {
|
|
39
|
+
opset: Opset;
|
|
40
|
+
/** null on dry_run (plan only) */
|
|
41
|
+
result: ApplyResult | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Whole-document update convenience: plan then apply. `dryRun` returns the
|
|
45
|
+
* opset without executing (the note's docs.update(..., dry_run=true)).
|
|
46
|
+
*/
|
|
47
|
+
export declare function docsUpdate(store: Store, ctx: DocsUpdateContext, docRef: string, content: string, opts?: {
|
|
48
|
+
dryRun?: boolean;
|
|
49
|
+
config?: ReconcileConfig;
|
|
50
|
+
reason?: string;
|
|
51
|
+
}): DocsUpdateResult;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { sha256 } from "../core/hash.js";
|
|
2
|
+
import { parseTree } from "../core/parse/tree.js";
|
|
3
|
+
import { adapterForPath } from "../format/index.js";
|
|
4
|
+
import { findDocByRef } from "../core/read/reader.js";
|
|
5
|
+
import { reconcileDocument } from "../reconcile/reconcile.js";
|
|
6
|
+
import { flatten } from "../reconcile/flatten.js";
|
|
7
|
+
import { DEFAULT_CONFIG } from "../reconcile/types.js";
|
|
8
|
+
import { loadOldMatchBlocks } from "../sync/reconciling-ingest.js";
|
|
9
|
+
import { loadMutDoc } from "./load.js";
|
|
10
|
+
import { renderDoc, MutationError } from "./tree.js";
|
|
11
|
+
import { apply } from "./apply.js";
|
|
12
|
+
import { lowerTopLevel, lowerReplace } from "./lower.js";
|
|
13
|
+
import { summarizeOps, opsetKernelOps } from "./opset.js";
|
|
14
|
+
function toFlatSource(b) {
|
|
15
|
+
return { type: b.type, raw: b.raw, children: b.children.map(toFlatSource) };
|
|
16
|
+
}
|
|
17
|
+
function extractFrontmatter(blocks) {
|
|
18
|
+
if (blocks.length > 0 && blocks[0].type === "frontmatter")
|
|
19
|
+
return { fmBlock: blocks[0], rest: blocks.slice(1) };
|
|
20
|
+
return { fmBlock: null, rest: blocks };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Plan a whole-document update. `docRef` is a doc id or repo-relative path;
|
|
24
|
+
* `content` is the proposed complete document. Throws doc_missing if the
|
|
25
|
+
* document does not exist (a create is docsCreate's job). Never writes.
|
|
26
|
+
*/
|
|
27
|
+
export function planUpdate(store, repoId, rootPath, docRef, content, opts = {}) {
|
|
28
|
+
const config = opts.config ?? DEFAULT_CONFIG;
|
|
29
|
+
const info = findDocByRef(store, repoId, docRef);
|
|
30
|
+
if (!info)
|
|
31
|
+
throw new MutationError("doc_missing", `doc ${docRef} not found`, { doc: docRef });
|
|
32
|
+
const docId = info.docId;
|
|
33
|
+
const path = info.path;
|
|
34
|
+
const oldDoc = loadMutDoc(store.db, docId);
|
|
35
|
+
if (!oldDoc)
|
|
36
|
+
throw new MutationError("doc_missing", `doc ${docRef} not found`, { doc: docRef });
|
|
37
|
+
// Base state the plan is pinned to. baseContentHash is the current rendered
|
|
38
|
+
// bytes (== docs.file_hash at quiescence); a stale plan is rejected at apply.
|
|
39
|
+
const currentContent = renderDoc(oldDoc);
|
|
40
|
+
const baseContentHash = sha256(currentContent).toString("hex");
|
|
41
|
+
// Parse the proposed content with the doc's format adapter.
|
|
42
|
+
const adapter = adapterForPath(path);
|
|
43
|
+
const tree = adapter ? adapter.parse(content) : parseTree(content);
|
|
44
|
+
const { fmBlock, rest } = extractFrontmatter(tree.children);
|
|
45
|
+
// Reconcile the proposed body against the current tree (no resurrection pool:
|
|
46
|
+
// the op path mints new ids for new structure rather than reusing tombstones).
|
|
47
|
+
const oldMatch = loadOldMatchBlocks(store.db, docId);
|
|
48
|
+
const newMatch = flatten(rest.map(toFlatSource));
|
|
49
|
+
const result = reconcileDocument(oldMatch, newMatch, { config });
|
|
50
|
+
// Frontmatter is a document-level unit outside the six block ops. loadMutDoc
|
|
51
|
+
// builds frontmatterRaw as `bytes + separator`; mirror that for the target.
|
|
52
|
+
const targetFm = fmBlock ? fmBlock.raw + fmBlock.trivia : null;
|
|
53
|
+
const fmChanged = targetFm !== oldDoc.frontmatterRaw;
|
|
54
|
+
const setFrontmatter = fmChanged ? [{ doc: docId, raw: targetFm }] : undefined;
|
|
55
|
+
const diagnostics = [];
|
|
56
|
+
// Try lowerings in order of decreasing identity preservation; take the first
|
|
57
|
+
// that reproduces the proposed content exactly.
|
|
58
|
+
let chosen = null;
|
|
59
|
+
let converges = false;
|
|
60
|
+
const t2 = lowerTopLevel(oldDoc, rest, assignmentOf(result), dispositionsOf(result));
|
|
61
|
+
const v2 = verify(store, repoId, rootPath, path, opsOf(t2), setFrontmatter, content, currentContent);
|
|
62
|
+
if (v2.ok) {
|
|
63
|
+
chosen = t2;
|
|
64
|
+
converges = true;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
diagnostics.push(`top-level lowering did not reproduce the proposed content byte-for-byte (${describeDivergence(v2, tree.children, content)}); falling back to full replace`);
|
|
68
|
+
const t3 = lowerReplace(oldDoc, content);
|
|
69
|
+
const v3 = verify(store, repoId, rootPath, path, opsOf(t3), setFrontmatter, content, currentContent);
|
|
70
|
+
if (v3.ok) {
|
|
71
|
+
chosen = t3;
|
|
72
|
+
converges = true;
|
|
73
|
+
diagnostics.push("full-replace plan converges (block identity not preserved)");
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Neither converged: return the identity-preserving plan but flag it. apply
|
|
77
|
+
// refuses a non-convergent plan (loud failure), so no wrong bytes land.
|
|
78
|
+
chosen = t2;
|
|
79
|
+
diagnostics.push(`no lowering reproduced the proposed content exactly (full replace: ${describeDivergence(v3, tree.children, content)}); plan will not apply — inspect and re-plan`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const ops = chosen.ops;
|
|
83
|
+
const summary = summarizeOps(ops, { preserved: chosen.preserved, ambiguous: chosen.ambiguous });
|
|
84
|
+
return {
|
|
85
|
+
version: 1,
|
|
86
|
+
kind: "doc_update",
|
|
87
|
+
target: { doc: docId, path },
|
|
88
|
+
precondition: { doc: docId, path, baseRevision: info.currentRev, baseContentHash },
|
|
89
|
+
matcherV: config.matcherV,
|
|
90
|
+
ops,
|
|
91
|
+
...(fmChanged ? { frontmatter: { raw: targetFm } } : {}),
|
|
92
|
+
summary,
|
|
93
|
+
converges,
|
|
94
|
+
diagnostics,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function assignmentOf(r) { return r.assignment; }
|
|
98
|
+
function dispositionsOf(r) {
|
|
99
|
+
return r.dispositions;
|
|
100
|
+
}
|
|
101
|
+
function opsOf(l) { return l.ops.map((p) => p.op); }
|
|
102
|
+
// Simulate a candidate op list and check the rendered result equals the exact
|
|
103
|
+
// proposed content. An invalid script throws in apply → treated as non-convergent.
|
|
104
|
+
function verify(store, repoId, rootPath, path, ops, setFrontmatter, expected, currentContent) {
|
|
105
|
+
try {
|
|
106
|
+
const res = apply(store, { repoId, rootPath, ops, origin: { actor: "plan:verify" }, dryRun: true, ...(setFrontmatter ? { setFrontmatter } : {}) });
|
|
107
|
+
// A doc untouched by any op (e.g. an empty plan) is absent from diffs; its
|
|
108
|
+
// effective result is the unchanged current content.
|
|
109
|
+
const after = res.diffs?.[path]?.after ?? currentContent;
|
|
110
|
+
return after === expected ? { ok: true } : { ok: false, actual: after, error: null };
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
return { ok: false, actual: null, error: e instanceof Error ? `${e.code ?? "error"}: ${e.message}` : String(e) };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Name the failure precisely: the first differing byte offset and the proposed
|
|
117
|
+
// top-level block (index + type) whose span covers it, so a caller learns WHICH
|
|
118
|
+
// construct failed to round-trip rather than just "no lowering converged".
|
|
119
|
+
function describeDivergence(v, proposed, expected) {
|
|
120
|
+
if (v.ok)
|
|
121
|
+
return "converged";
|
|
122
|
+
if (v.actual === null)
|
|
123
|
+
return `simulation threw ${v.error}`;
|
|
124
|
+
const actual = v.actual;
|
|
125
|
+
let off = 0;
|
|
126
|
+
const n = Math.min(actual.length, expected.length);
|
|
127
|
+
while (off < n && actual.charCodeAt(off) === expected.charCodeAt(off))
|
|
128
|
+
off++;
|
|
129
|
+
const idx = proposed.findIndex((b) => off >= b.span.start && off < b.span.end + b.trivia.length);
|
|
130
|
+
const where = idx >= 0 ? `proposed block #${idx} (${proposed[idx].type}, bytes ${proposed[idx].span.start}-${proposed[idx].span.end})` : off >= expected.length ? "past the end of the proposed content" : "leading trivia";
|
|
131
|
+
const snippet = (s) => JSON.stringify(s.slice(off, off + 24));
|
|
132
|
+
return `first divergence at byte ${off} in ${where}: expected ${snippet(expected)}, rendered ${snippet(actual)}`;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Apply an opset through the kernel write path. Validates the plan's
|
|
136
|
+
* preconditions against current state first (a stale plan fails, carrying
|
|
137
|
+
* current truth, rather than silently reinterpreting) and refuses a plan that
|
|
138
|
+
* did not verify byte-convergent.
|
|
139
|
+
*/
|
|
140
|
+
export function applyOpset(store, req) {
|
|
141
|
+
const { opset } = req;
|
|
142
|
+
if (!opset.converges) {
|
|
143
|
+
const why = opset.diagnostics.length > 0 ? ` — ${opset.diagnostics[opset.diagnostics.length - 1]}` : "";
|
|
144
|
+
throw new MutationError("plan_not_convergent", `opset does not reproduce the proposed content; re-plan${why}`, { diagnostics: opset.diagnostics });
|
|
145
|
+
}
|
|
146
|
+
// Precondition: the document must still be at the revision/content the plan
|
|
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);
|
|
149
|
+
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);
|
|
152
|
+
if (stale) {
|
|
153
|
+
throw new MutationError("stale_plan", "document changed since the plan was computed; re-plan", {
|
|
154
|
+
doc: opset.precondition.doc,
|
|
155
|
+
expected_revision: opset.precondition.baseRevision,
|
|
156
|
+
current: { revision: row.current_rev, content_hash: row.fh },
|
|
157
|
+
retriable: true,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return apply(store, {
|
|
161
|
+
repoId: req.repoId,
|
|
162
|
+
rootPath: req.rootPath,
|
|
163
|
+
ops: opsetKernelOps(opset),
|
|
164
|
+
origin: req.origin,
|
|
165
|
+
...(req.dryRun !== undefined ? { dryRun: req.dryRun } : {}),
|
|
166
|
+
...(req.omgbaseDir !== undefined ? { omgbaseDir: req.omgbaseDir } : {}),
|
|
167
|
+
...(opset.frontmatter ? { setFrontmatter: [{ doc: opset.precondition.doc, raw: opset.frontmatter.raw }] } : {}),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Whole-document update convenience: plan then apply. `dryRun` returns the
|
|
172
|
+
* opset without executing (the note's docs.update(..., dry_run=true)).
|
|
173
|
+
*/
|
|
174
|
+
export function docsUpdate(store, ctx, docRef, content, opts = {}) {
|
|
175
|
+
const opset = planUpdate(store, ctx.repoId, ctx.rootPath, docRef, content, opts.config ? { config: opts.config } : {});
|
|
176
|
+
if (opts.dryRun)
|
|
177
|
+
return { opset, result: null };
|
|
178
|
+
const result = applyOpset(store, {
|
|
179
|
+
repoId: ctx.repoId,
|
|
180
|
+
rootPath: ctx.rootPath,
|
|
181
|
+
opset,
|
|
182
|
+
origin: { actor: ctx.actor ?? "agent:update", ...(opts.reason !== undefined ? { reason: opts.reason } : {}) },
|
|
183
|
+
...(ctx.omgbaseDir !== undefined ? { omgbaseDir: ctx.omgbaseDir } : {}),
|
|
184
|
+
});
|
|
185
|
+
return { opset, result };
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=plan-update.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface MutBlock {
|
|
2
|
+
id: string;
|
|
3
|
+
type: string;
|
|
4
|
+
raw: string;
|
|
5
|
+
trivia: string;
|
|
6
|
+
attrs: Record<string, unknown>;
|
|
7
|
+
children: MutBlock[];
|
|
8
|
+
/** true if raw was supplied by an op (serialize as-is; 03 §2.2) */
|
|
9
|
+
dirty?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface MutDoc {
|
|
12
|
+
docId: string;
|
|
13
|
+
path: string;
|
|
14
|
+
format: string;
|
|
15
|
+
leadingTrivia: string;
|
|
16
|
+
frontmatterRaw: string | null;
|
|
17
|
+
children: MutBlock[];
|
|
18
|
+
}
|
|
19
|
+
export declare class MutationError extends Error {
|
|
20
|
+
code: string;
|
|
21
|
+
data: Record<string, unknown>;
|
|
22
|
+
constructor(code: string, message: string, data?: Record<string, unknown>);
|
|
23
|
+
}
|
|
24
|
+
/** Find a block by id anywhere in the doc; returns the block + its parent list. */
|
|
25
|
+
export declare function locate(doc: MutDoc, blockId: string): {
|
|
26
|
+
block: MutBlock;
|
|
27
|
+
siblings: MutBlock[];
|
|
28
|
+
index: number;
|
|
29
|
+
} | null;
|
|
30
|
+
export declare function rawHashHex(raw: string): string;
|
|
31
|
+
/** The block that owns `children` (by reference identity), or null at top level. */
|
|
32
|
+
export declare function ownerOf(doc: MutDoc, children: MutBlock[]): MutBlock | null;
|
|
33
|
+
/**
|
|
34
|
+
* A container's raw is the authoritative render source (03 §2.2), so a
|
|
35
|
+
* structural change to its children (insert/remove/move) must invalidate the
|
|
36
|
+
* container's cached raw. Mark the owning container dirty so renderBlock
|
|
37
|
+
* reconstructs it from the mutated children rather than emitting the now-stale
|
|
38
|
+
* raw verbatim; ancestors see it via hasDirtyDescendant and reconstruct in turn.
|
|
39
|
+
* Top-level siblings (doc.children) need no marking — renderDoc renders each
|
|
40
|
+
* top-level block directly.
|
|
41
|
+
*/
|
|
42
|
+
export declare function markContainerDirty(doc: MutDoc, children: MutBlock[]): void;
|
|
43
|
+
export declare function renderDoc(doc: MutDoc): string;
|
|
44
|
+
/** Ordered ids of a parent's direct children (for parent_children_hash CAS). */
|
|
45
|
+
export declare function childIds(list: MutBlock[]): string[];
|
|
46
|
+
export declare function parentChildrenHash(list: MutBlock[]): string;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { sha256 } from "../core/hash.js";
|
|
2
|
+
export class MutationError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
data;
|
|
5
|
+
constructor(code, message, data = {}) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.data = data;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Find a block by id anywhere in the doc; returns the block + its parent list. */
|
|
12
|
+
export function locate(doc, blockId) {
|
|
13
|
+
const search = (list) => {
|
|
14
|
+
for (let i = 0; i < list.length; i++) {
|
|
15
|
+
const b = list[i];
|
|
16
|
+
if (b.id === blockId)
|
|
17
|
+
return { block: b, siblings: list, index: i };
|
|
18
|
+
const found = search(b.children);
|
|
19
|
+
if (found)
|
|
20
|
+
return found;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
return search(doc.children);
|
|
25
|
+
}
|
|
26
|
+
export function rawHashHex(raw) {
|
|
27
|
+
return sha256(raw).toString("hex");
|
|
28
|
+
}
|
|
29
|
+
/** The block that owns `children` (by reference identity), or null at top level. */
|
|
30
|
+
export function ownerOf(doc, children) {
|
|
31
|
+
if (children === doc.children)
|
|
32
|
+
return null;
|
|
33
|
+
let found = null;
|
|
34
|
+
const walk = (list) => {
|
|
35
|
+
for (const b of list) {
|
|
36
|
+
if (b.children === children) {
|
|
37
|
+
found = b;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
walk(b.children);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
walk(doc.children);
|
|
44
|
+
return found;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A container's raw is the authoritative render source (03 §2.2), so a
|
|
48
|
+
* structural change to its children (insert/remove/move) must invalidate the
|
|
49
|
+
* container's cached raw. Mark the owning container dirty so renderBlock
|
|
50
|
+
* reconstructs it from the mutated children rather than emitting the now-stale
|
|
51
|
+
* raw verbatim; ancestors see it via hasDirtyDescendant and reconstruct in turn.
|
|
52
|
+
* Top-level siblings (doc.children) need no marking — renderDoc renders each
|
|
53
|
+
* top-level block directly.
|
|
54
|
+
*/
|
|
55
|
+
export function markContainerDirty(doc, children) {
|
|
56
|
+
const owner = ownerOf(doc, children);
|
|
57
|
+
if (owner)
|
|
58
|
+
owner.dirty = true;
|
|
59
|
+
}
|
|
60
|
+
// Render the doc by splice (03 §2.2): leading trivia, optional frontmatter, then
|
|
61
|
+
// each top-level block + trivia. A block whose subtree contains no dirty node
|
|
62
|
+
// emits its retained raw verbatim. When a descendant is dirty, the block is
|
|
63
|
+
// re-rendered from its children (indented to their container) so nested edits
|
|
64
|
+
// — e.g. a task checkbox toggle inside a list — surface without disturbing
|
|
65
|
+
// untouched siblings.
|
|
66
|
+
export function renderDoc(doc) {
|
|
67
|
+
const out = [doc.leadingTrivia];
|
|
68
|
+
if (doc.frontmatterRaw !== null)
|
|
69
|
+
out.push(doc.frontmatterRaw);
|
|
70
|
+
for (const b of doc.children) {
|
|
71
|
+
out.push(renderBlock(b, 0));
|
|
72
|
+
out.push(b.trivia);
|
|
73
|
+
}
|
|
74
|
+
return out.join("");
|
|
75
|
+
}
|
|
76
|
+
function hasDirtyDescendant(b) {
|
|
77
|
+
if (b.dirty)
|
|
78
|
+
return true;
|
|
79
|
+
return b.children.some(hasDirtyDescendant);
|
|
80
|
+
}
|
|
81
|
+
// Render one block. Leaf or fully-clean subtree ⇒ retained raw. Otherwise
|
|
82
|
+
// reconstruct from children (a container with a dirty descendant).
|
|
83
|
+
function renderBlock(b, depth) {
|
|
84
|
+
if (b.children.length === 0 || !hasDirtyDescendant(b))
|
|
85
|
+
return b.raw;
|
|
86
|
+
// A list is reconstructed from its items: each item's raw already carries its
|
|
87
|
+
// own marker + nested content (correctly indented), so we re-mark (ordered
|
|
88
|
+
// lists renumber after a reorder/insert) and join with the list's own
|
|
89
|
+
// tight/loose separator. This matches the canonical renderer, which treats a
|
|
90
|
+
// top-level block's raw as the authoritative source for its whole subtree.
|
|
91
|
+
if (b.type === "list")
|
|
92
|
+
return renderList(b);
|
|
93
|
+
// A blockquote's `> ` markers live only in the container raw, so a rebuild
|
|
94
|
+
// must re-prefix every line of every child (blank separator lines become a
|
|
95
|
+
// bare `>`), or the quote silently degrades to plain paragraphs.
|
|
96
|
+
if (b.type === "blockquote")
|
|
97
|
+
return renderBlockquote(b, depth);
|
|
98
|
+
// A table's delimiter row (`| --- | --- |`) is not a child node (mdast keeps
|
|
99
|
+
// only header/body rows), so re-insert the retained delimiter line after the
|
|
100
|
+
// header row when rebuilding from rows.
|
|
101
|
+
if (b.type === "table")
|
|
102
|
+
return renderTable(b, depth);
|
|
103
|
+
// Other containers (a list_item outside a list): fall back to the
|
|
104
|
+
// child-indent rebuild. Lists are the common mutable container.
|
|
105
|
+
const indent = " ".repeat(depth);
|
|
106
|
+
return b.children.map((c) => indent + renderBlock(c, depth + 1).replace(/\n/g, "\n" + indent)).join("\n");
|
|
107
|
+
}
|
|
108
|
+
function renderBlockquote(bq, depth) {
|
|
109
|
+
const body = bq.children.map((c) => renderBlock(c, depth + 1)).join("\n\n");
|
|
110
|
+
return body.split("\n").map((ln) => (ln.length ? `> ${ln}` : ">")).join("\n");
|
|
111
|
+
}
|
|
112
|
+
function renderTable(t, depth) {
|
|
113
|
+
const rows = t.children.map((c) => renderBlock(c, depth + 1));
|
|
114
|
+
const lines = t.raw.split("\n");
|
|
115
|
+
const delimiter = lines.length >= 2 && /^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(lines[1]) ? lines[1] : null;
|
|
116
|
+
if (delimiter !== null && rows.length >= 1)
|
|
117
|
+
rows.splice(1, 0, delimiter);
|
|
118
|
+
return rows.join("\n");
|
|
119
|
+
}
|
|
120
|
+
const MARKER_RE = /^(\s*)([-*+]|\d+[.)])(\s+)/;
|
|
121
|
+
// Reconstruct a markdown list from its (possibly reordered / edited / added /
|
|
122
|
+
// removed) items. Item raws are self-contained (marker + text + nested); we
|
|
123
|
+
// only re-derive the leading marker so an ordered list renumbers correctly.
|
|
124
|
+
function renderList(list) {
|
|
125
|
+
const ordered = Boolean(list.attrs.ordered);
|
|
126
|
+
const start = typeof list.attrs.start === "number" ? list.attrs.start : 1;
|
|
127
|
+
const sep = list.raw.includes("\n\n") ? "\n\n" : "\n";
|
|
128
|
+
return list.children.map((item, i) => renderItem(item, ordered ? `${start + i}. ` : "- ")).join(sep);
|
|
129
|
+
}
|
|
130
|
+
// Render one list item under the given marker. A retained item (no strictly
|
|
131
|
+
// deeper change) keeps its raw, only re-marking the first line so ordered
|
|
132
|
+
// numbers follow the current position. An item with a dirty descendant is
|
|
133
|
+
// rebuilt from its child blocks, marker on the first line and continuations
|
|
134
|
+
// indented by the marker width.
|
|
135
|
+
function renderItem(item, marker) {
|
|
136
|
+
if (item.children.some(hasDirtyDescendant)) {
|
|
137
|
+
const body = item.children.map((c) => (c.type === "list" ? renderList(c) : renderBlock(c, 0))).join("\n");
|
|
138
|
+
return applyMarker(body, marker);
|
|
139
|
+
}
|
|
140
|
+
return applyMarker(stripMarker(item.raw), marker);
|
|
141
|
+
}
|
|
142
|
+
// Remove a leading list marker + its indentation from the first line, and the
|
|
143
|
+
// same indent width from continuation lines, yielding the marker-free body.
|
|
144
|
+
function stripMarker(raw) {
|
|
145
|
+
const lines = raw.split("\n");
|
|
146
|
+
const m = MARKER_RE.exec(lines[0] ?? "");
|
|
147
|
+
if (!m)
|
|
148
|
+
return raw;
|
|
149
|
+
const width = m[0].length;
|
|
150
|
+
return lines
|
|
151
|
+
.map((ln, i) => (i === 0 ? ln.slice(width) : ln.slice(0, width).trim() === "" ? ln.slice(width) : ln))
|
|
152
|
+
.join("\n");
|
|
153
|
+
}
|
|
154
|
+
// Prepend a marker to a marker-free body: marker on the first line, the rest
|
|
155
|
+
// indented by the marker width so nested content stays under the item.
|
|
156
|
+
function applyMarker(body, marker) {
|
|
157
|
+
const pad = " ".repeat(marker.length);
|
|
158
|
+
return body.split("\n").map((ln, i) => (i === 0 ? marker + ln : ln.length ? pad + ln : ln)).join("\n");
|
|
159
|
+
}
|
|
160
|
+
/** Ordered ids of a parent's direct children (for parent_children_hash CAS). */
|
|
161
|
+
export function childIds(list) {
|
|
162
|
+
return list.map((b) => b.id);
|
|
163
|
+
}
|
|
164
|
+
export function parentChildrenHash(list) {
|
|
165
|
+
return sha256(childIds(list).join(",")).toString("hex");
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { oqxRun, oqxRunAsync, collectSemanticPhrases, parseOqx, type OqxResult, type OqxHit, type OqxOptions, type EmbedQuery, type OqxConsumer, } from "../oqx-js/run.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// OQX is now served by `@omgbase/oqx` (ADR-013). This module is a thin re-export
|
|
2
|
+
// of the store-backed engine in ../oqx-js so every caller and the corpus keep
|
|
3
|
+
// importing `../oqx/run.js` unchanged. The former in-tree compiler
|
|
4
|
+
// (lexer/parser/lower/ir/compile/relations/scalar) is superseded.
|
|
5
|
+
export { oqxRun, oqxRunAsync, collectSemanticPhrases, parseOqx, } from "../oqx-js/run.js";
|
|
6
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DataContext } from "@omgbase/oqx";
|
|
2
|
+
import type { Store } from "../core/store/store.js";
|
|
3
|
+
import type { SemanticVec } from "../search/cel/compile.js";
|
|
4
|
+
export type Target = "docs" | "blocks" | "nodes" | "edges";
|
|
5
|
+
declare const TARGET: unique symbol;
|
|
6
|
+
export interface StoreContextOptions {
|
|
7
|
+
/** Pre-computed query vectors for `semantic("phrase")`, keyed by phrase (the
|
|
8
|
+
* async runner fills this; the sync path leaves it empty and a semantic() with
|
|
9
|
+
* no entry is a loud error). */
|
|
10
|
+
semanticVectors?: Map<string, SemanticVec>;
|
|
11
|
+
/** Rows a tier-3 plan produced, bound to the residual query's synthetic
|
|
12
|
+
* `ROWS_ROOT` source (planner.ts). The rows are already target-tagged store
|
|
13
|
+
* rows, so `get`/relations/domain-fns resolve against them unchanged. */
|
|
14
|
+
rowsRoot?: {
|
|
15
|
+
name: string;
|
|
16
|
+
rows: unknown[];
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare function makeStoreContext(store: Store, repoId: string, opts?: StoreContextOptions): DataContext;
|
|
20
|
+
/** Tag SQL rows with their target so the store context resolves them (used by the
|
|
21
|
+
* tier-3 planner to hand produced rows back for residual evaluation). */
|
|
22
|
+
export declare function tagRows(rows: Record<string, unknown>[], target: Target): unknown[];
|
|
23
|
+
export { TARGET };
|