@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.
Files changed (214) hide show
  1. package/LICENSE +21 -0
  2. package/dist/src/cli/eval-matcher.d.ts +13 -0
  3. package/dist/src/cli/eval-matcher.js +36 -0
  4. package/dist/src/core/attach.d.ts +5 -0
  5. package/dist/src/core/attach.js +38 -0
  6. package/dist/src/core/cursor.d.ts +11 -0
  7. package/dist/src/core/cursor.js +35 -0
  8. package/dist/src/core/hash.d.ts +23 -0
  9. package/dist/src/core/hash.js +82 -0
  10. package/dist/src/core/ids.d.ts +6 -0
  11. package/dist/src/core/ids.js +26 -0
  12. package/dist/src/core/index.d.ts +1 -0
  13. package/dist/src/core/index.js +2 -0
  14. package/dist/src/core/ingest.d.ts +43 -0
  15. package/dist/src/core/ingest.js +287 -0
  16. package/dist/src/core/order-key.d.ts +8 -0
  17. package/dist/src/core/order-key.js +72 -0
  18. package/dist/src/core/parse/parse.d.ts +3 -0
  19. package/dist/src/core/parse/parse.js +104 -0
  20. package/dist/src/core/parse/render.d.ts +3 -0
  21. package/dist/src/core/parse/render.js +20 -0
  22. package/dist/src/core/parse/tree.d.ts +8 -0
  23. package/dist/src/core/parse/tree.js +43 -0
  24. package/dist/src/core/parse/types.d.ts +52 -0
  25. package/dist/src/core/parse/types.js +9 -0
  26. package/dist/src/core/read/document.d.ts +107 -0
  27. package/dist/src/core/read/document.js +191 -0
  28. package/dist/src/core/read/nodes.d.ts +47 -0
  29. package/dist/src/core/read/nodes.js +102 -0
  30. package/dist/src/core/read/outline.d.ts +12 -0
  31. package/dist/src/core/read/outline.js +76 -0
  32. package/dist/src/core/read/reader.d.ts +125 -0
  33. package/dist/src/core/read/reader.js +206 -0
  34. package/dist/src/core/read/refs.d.ts +15 -0
  35. package/dist/src/core/read/refs.js +51 -0
  36. package/dist/src/core/store/edges.d.ts +26 -0
  37. package/dist/src/core/store/edges.js +101 -0
  38. package/dist/src/core/store/fts.d.ts +10 -0
  39. package/dist/src/core/store/fts.js +22 -0
  40. package/dist/src/core/store/gc.d.ts +18 -0
  41. package/dist/src/core/store/gc.js +69 -0
  42. package/dist/src/core/store/nodes.d.ts +5 -0
  43. package/dist/src/core/store/nodes.js +57 -0
  44. package/dist/src/core/store/properties.d.ts +38 -0
  45. package/dist/src/core/store/properties.js +177 -0
  46. package/dist/src/core/store/rebuild.d.ts +3 -0
  47. package/dist/src/core/store/rebuild.js +29 -0
  48. package/dist/src/core/store/schema.d.ts +9 -0
  49. package/dist/src/core/store/schema.js +386 -0
  50. package/dist/src/core/store/sections.d.ts +2 -0
  51. package/dist/src/core/store/sections.js +29 -0
  52. package/dist/src/core/store/store.d.ts +24 -0
  53. package/dist/src/core/store/store.js +164 -0
  54. package/dist/src/core/store/writers.d.ts +52 -0
  55. package/dist/src/core/store/writers.js +89 -0
  56. package/dist/src/core/vec.d.ts +10 -0
  57. package/dist/src/core/vec.js +33 -0
  58. package/dist/src/format/adapter.d.ts +97 -0
  59. package/dist/src/format/adapter.js +14 -0
  60. package/dist/src/format/index.d.ts +6 -0
  61. package/dist/src/format/index.js +15 -0
  62. package/dist/src/format/json.d.ts +3 -0
  63. package/dist/src/format/json.js +324 -0
  64. package/dist/src/format/markdown.d.ts +5 -0
  65. package/dist/src/format/markdown.js +217 -0
  66. package/dist/src/format/registry.d.ts +6 -0
  67. package/dist/src/format/registry.js +24 -0
  68. package/dist/src/format/yaml.d.ts +5 -0
  69. package/dist/src/format/yaml.js +413 -0
  70. package/dist/src/graph/extract.d.ts +38 -0
  71. package/dist/src/graph/extract.js +242 -0
  72. package/dist/src/graph/history.d.ts +103 -0
  73. package/dist/src/graph/history.js +210 -0
  74. package/dist/src/graph/inbound-links.d.ts +33 -0
  75. package/dist/src/graph/inbound-links.js +130 -0
  76. package/dist/src/graph/link-destinations.d.ts +22 -0
  77. package/dist/src/graph/link-destinations.js +84 -0
  78. package/dist/src/graph/link-health.d.ts +79 -0
  79. package/dist/src/graph/link-health.js +146 -0
  80. package/dist/src/graph/links.d.ts +23 -0
  81. package/dist/src/graph/links.js +42 -0
  82. package/dist/src/index.d.ts +60 -0
  83. package/dist/src/index.js +70 -0
  84. package/dist/src/mcp/errors.d.ts +17 -0
  85. package/dist/src/mcp/errors.js +18 -0
  86. package/dist/src/mcp/graph.d.ts +63 -0
  87. package/dist/src/mcp/graph.js +212 -0
  88. package/dist/src/mcp/reference.d.ts +1 -0
  89. package/dist/src/mcp/reference.js +227 -0
  90. package/dist/src/mcp/server.d.ts +30 -0
  91. package/dist/src/mcp/server.js +1076 -0
  92. package/dist/src/mcp/stdio.d.ts +13 -0
  93. package/dist/src/mcp/stdio.js +24 -0
  94. package/dist/src/migrate/mrplex.d.ts +27 -0
  95. package/dist/src/migrate/mrplex.js +29 -0
  96. package/dist/src/mutate/apply.d.ts +83 -0
  97. package/dist/src/mutate/apply.js +302 -0
  98. package/dist/src/mutate/doc-store.d.ts +49 -0
  99. package/dist/src/mutate/doc-store.js +71 -0
  100. package/dist/src/mutate/docs.d.ts +60 -0
  101. package/dist/src/mutate/docs.js +232 -0
  102. package/dist/src/mutate/known-ids.d.ts +12 -0
  103. package/dist/src/mutate/known-ids.js +154 -0
  104. package/dist/src/mutate/load.d.ts +3 -0
  105. package/dist/src/mutate/load.js +66 -0
  106. package/dist/src/mutate/lower.d.ts +32 -0
  107. package/dist/src/mutate/lower.js +232 -0
  108. package/dist/src/mutate/macros.d.ts +91 -0
  109. package/dist/src/mutate/macros.js +230 -0
  110. package/dist/src/mutate/ops.d.ts +40 -0
  111. package/dist/src/mutate/ops.js +513 -0
  112. package/dist/src/mutate/opset.d.ts +92 -0
  113. package/dist/src/mutate/opset.js +79 -0
  114. package/dist/src/mutate/plan-update.d.ts +51 -0
  115. package/dist/src/mutate/plan-update.js +187 -0
  116. package/dist/src/mutate/tree.d.ts +46 -0
  117. package/dist/src/mutate/tree.js +167 -0
  118. package/dist/src/oqx/run.d.ts +1 -0
  119. package/dist/src/oqx/run.js +6 -0
  120. package/dist/src/oqx-js/context.d.ts +23 -0
  121. package/dist/src/oqx-js/context.js +564 -0
  122. package/dist/src/oqx-js/planner.d.ts +10 -0
  123. package/dist/src/oqx-js/planner.js +91 -0
  124. package/dist/src/oqx-js/run.d.ts +41 -0
  125. package/dist/src/oqx-js/run.js +332 -0
  126. package/dist/src/oqx-js/sql/translate.d.ts +27 -0
  127. package/dist/src/oqx-js/sql/translate.js +290 -0
  128. package/dist/src/reconcile/crossdoc.d.ts +34 -0
  129. package/dist/src/reconcile/crossdoc.js +76 -0
  130. package/dist/src/reconcile/eval/generator.d.ts +21 -0
  131. package/dist/src/reconcile/eval/generator.js +129 -0
  132. package/dist/src/reconcile/eval/metrics.d.ts +32 -0
  133. package/dist/src/reconcile/eval/metrics.js +74 -0
  134. package/dist/src/reconcile/flatten.d.ts +12 -0
  135. package/dist/src/reconcile/flatten.js +37 -0
  136. package/dist/src/reconcile/phase5.d.ts +8 -0
  137. package/dist/src/reconcile/phase5.js +167 -0
  138. package/dist/src/reconcile/phase6a.d.ts +2 -0
  139. package/dist/src/reconcile/phase6a.js +159 -0
  140. package/dist/src/reconcile/phases.d.ts +28 -0
  141. package/dist/src/reconcile/phases.js +0 -0
  142. package/dist/src/reconcile/reconcile.d.ts +21 -0
  143. package/dist/src/reconcile/reconcile.js +115 -0
  144. package/dist/src/reconcile/similarity.d.ts +7 -0
  145. package/dist/src/reconcile/similarity.js +41 -0
  146. package/dist/src/reconcile/types.d.ts +46 -0
  147. package/dist/src/reconcile/types.js +19 -0
  148. package/dist/src/search/cel/ast.d.ts +74 -0
  149. package/dist/src/search/cel/ast.js +3 -0
  150. package/dist/src/search/cel/compile.d.ts +60 -0
  151. package/dist/src/search/cel/compile.js +836 -0
  152. package/dist/src/search/cel/lexer.d.ts +9 -0
  153. package/dist/src/search/cel/lexer.js +139 -0
  154. package/dist/src/search/cel/parser.d.ts +7 -0
  155. package/dist/src/search/cel/parser.js +219 -0
  156. package/dist/src/search/drain.d.ts +39 -0
  157. package/dist/src/search/drain.js +97 -0
  158. package/dist/src/search/embeddings.d.ts +131 -0
  159. package/dist/src/search/embeddings.js +263 -0
  160. package/dist/src/search/external.d.ts +13 -0
  161. package/dist/src/search/external.js +177 -0
  162. package/dist/src/search/fts-query.d.ts +1 -0
  163. package/dist/src/search/fts-query.js +64 -0
  164. package/dist/src/search/provider.d.ts +17 -0
  165. package/dist/src/search/provider.js +13 -0
  166. package/dist/src/search/resolve.d.ts +18 -0
  167. package/dist/src/search/resolve.js +23 -0
  168. package/dist/src/search/rrf.d.ts +36 -0
  169. package/dist/src/search/rrf.js +84 -0
  170. package/dist/src/search/tasks.d.ts +12 -0
  171. package/dist/src/search/tasks.js +131 -0
  172. package/dist/src/search/text.d.ts +16 -0
  173. package/dist/src/search/text.js +21 -0
  174. package/dist/src/search/vector.d.ts +18 -0
  175. package/dist/src/search/vector.js +49 -0
  176. package/dist/src/sync/admin.d.ts +59 -0
  177. package/dist/src/sync/admin.js +53 -0
  178. package/dist/src/sync/attach.d.ts +13 -0
  179. package/dist/src/sync/attach.js +29 -0
  180. package/dist/src/sync/checkpoint.d.ts +21 -0
  181. package/dist/src/sync/checkpoint.js +52 -0
  182. package/dist/src/sync/driver.d.ts +25 -0
  183. package/dist/src/sync/driver.js +80 -0
  184. package/dist/src/sync/external-source.d.ts +14 -0
  185. package/dist/src/sync/external-source.js +137 -0
  186. package/dist/src/sync/freshness.d.ts +42 -0
  187. package/dist/src/sync/freshness.js +152 -0
  188. package/dist/src/sync/fs-util.d.ts +12 -0
  189. package/dist/src/sync/fs-util.js +55 -0
  190. package/dist/src/sync/git-heuristics.d.ts +6 -0
  191. package/dist/src/sync/git-heuristics.js +16 -0
  192. package/dist/src/sync/observe.d.ts +63 -0
  193. package/dist/src/sync/observe.js +84 -0
  194. package/dist/src/sync/plugin.d.ts +54 -0
  195. package/dist/src/sync/plugin.js +12 -0
  196. package/dist/src/sync/reconciling-ingest.d.ts +12 -0
  197. package/dist/src/sync/reconciling-ingest.js +209 -0
  198. package/dist/src/sync/recovery.d.ts +6 -0
  199. package/dist/src/sync/recovery.js +34 -0
  200. package/dist/src/sync/settings.d.ts +18 -0
  201. package/dist/src/sync/settings.js +58 -0
  202. package/dist/src/sync/sources.d.ts +37 -0
  203. package/dist/src/sync/sources.js +75 -0
  204. package/dist/src/sync/tombstone.d.ts +9 -0
  205. package/dist/src/sync/tombstone.js +55 -0
  206. package/dist/src/sync/watch-lease.d.ts +14 -0
  207. package/dist/src/sync/watch-lease.js +91 -0
  208. package/dist/src/sync/watcher.d.ts +19 -0
  209. package/dist/src/sync/watcher.js +34 -0
  210. package/dist/src/sync/workspace.d.ts +34 -0
  211. package/dist/src/sync/workspace.js +120 -0
  212. package/dist/src/sync/writer-lock.d.ts +16 -0
  213. package/dist/src/sync/writer-lock.js +121 -0
  214. package/package.json +80 -0
@@ -0,0 +1,41 @@
1
+ import type { Query } from "@omgbase/oqx";
2
+ import type { Store } from "../core/store/store.js";
3
+ import type { SemanticVec } from "../search/cel/compile.js";
4
+ export type OqxConsumer = "collect" | "count" | "exists" | "none" | "first" | "single";
5
+ export type EmbedQuery = (text: string) => Promise<{
6
+ model: string;
7
+ vec: Float32Array;
8
+ }>;
9
+ export interface OqxHit {
10
+ id: string;
11
+ path: string;
12
+ [k: string]: unknown;
13
+ }
14
+ export interface OqxResult {
15
+ hits: OqxHit[];
16
+ truncated: boolean;
17
+ cursor: string | null;
18
+ consumer: OqxConsumer;
19
+ count?: number;
20
+ exists?: boolean;
21
+ /** The `none` consumer's result: true iff the query yields no rows. */
22
+ none?: boolean;
23
+ /** Present for a top-level `values` projection (`select <expr> values`): the
24
+ * bare projected values in page order, in place of `hits` (which is then
25
+ * empty). Paged/deduped exactly like hits — `truncated`/`cursor` apply. For
26
+ * first/single it holds zero or one value. */
27
+ values?: unknown[];
28
+ }
29
+ export interface OqxOptions {
30
+ limit?: number;
31
+ cursor?: string | null;
32
+ semanticVectors?: Map<string, SemanticVec>;
33
+ /** Force the pure in-memory engine (skip tier-3 pushdown). Internal — used by
34
+ * the differential conformance suite to prove planned == in-memory. */
35
+ plan?: boolean;
36
+ }
37
+ export declare function collectSemanticPhrases(source: string): string[];
38
+ export declare function oqxRun(store: Store, repoId: string, source: string, opts?: OqxOptions): OqxResult;
39
+ export declare function oqxRunAsync(store: Store, repoId: string, source: string, opts?: OqxOptions, embedQuery?: EmbedQuery): Promise<OqxResult>;
40
+ /** Parse an OQX source into the `@omgbase/oqx` AST (re-exported for callers). */
41
+ export declare function parseOqx(source: string): Query;
@@ -0,0 +1,332 @@
1
+ // omgbase's OQX entry point, served by `@omgbase/oqx`: parse the source, run it
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
+ // keyset pagination, count/exists scalars). Same signature + shape as the former
5
+ // in-tree compiler, so every caller and the corpus are unchanged.
6
+ import { parse, PlannedEngine, InMemoryEngine, OqxError } from "@omgbase/oqx";
7
+ import { makeStoreContext } from "./context.js";
8
+ import { SQLiteQueryPlanner } from "./planner.js";
9
+ import { FilterInvalid } from "../search/cel/parser.js";
10
+ import { float32ToBlob } from "../core/vec.js";
11
+ import { encodeCursor as encodeKeyset, decodeCursor as decodeKeyset } from "../core/cursor.js";
12
+ // Row-scoped domain functions: authored as free calls (`text("x")`) that
13
+ // implicitly reference the current row. `@omgbase/oqx` free functions receive no
14
+ // row, so we rewrite them to `$self.fn(…)` (a method whose receiver is the row).
15
+ const ROW_FNS = new Set([
16
+ "text", "semantic", "under", "under_heading", "within", "under_kind",
17
+ "yaml_path", "json_pointer", "has_edge", "has_anchor", "child_count", "parent_type",
18
+ ]);
19
+ const selfRef = { kind: "ident", name: "$self" };
20
+ function rewriteExpr(e) {
21
+ switch (e.kind) {
22
+ case "member": return { ...e, recv: rewriteExpr(e.recv) };
23
+ case "index": return { ...e, recv: rewriteExpr(e.recv), index: rewriteExpr(e.index) };
24
+ case "unary": return { ...e, expr: rewriteExpr(e.expr) };
25
+ case "binary":
26
+ case "logical":
27
+ case "in":
28
+ return { ...e, left: rewriteExpr(e.left), right: rewriteExpr(e.right) };
29
+ case "range":
30
+ return { ...e, lo: e.lo ? rewriteExpr(e.lo) : null, hi: e.hi ? rewriteExpr(e.hi) : null };
31
+ case "call": {
32
+ const recv = e.recv ? rewriteExpr(e.recv) : null;
33
+ const args = e.args.map(rewriteExpr);
34
+ if (recv === null && ROW_FNS.has(e.name))
35
+ return { kind: "call", recv: selfRef, name: e.name, args };
36
+ return { kind: "call", recv, name: e.name, args };
37
+ }
38
+ default: return e; // lit, ident, outer, binding
39
+ }
40
+ }
41
+ function rewriteWhere(w) {
42
+ switch (w.kind) {
43
+ case "and": return { kind: "and", parts: w.parts.map(rewriteWhere) };
44
+ case "or": return { kind: "or", parts: w.parts.map(rewriteWhere) };
45
+ case "not": return { kind: "not", expr: rewriteWhere(w.expr) };
46
+ case "scalar": return { kind: "scalar", expr: rewriteExpr(w.expr) };
47
+ case "op": return rewriteOp(w);
48
+ }
49
+ }
50
+ function rewriteOp(op) {
51
+ return { ...op, receiver: rewriteExpr(op.receiver), sub: rewriteSub(op.sub) };
52
+ }
53
+ function rewriteFollow(f) {
54
+ return {
55
+ ...f,
56
+ receiver: rewriteExpr(f.receiver),
57
+ where: f.where ? rewriteExpr(f.where) : null,
58
+ frontier: f.frontier ? rewriteExpr(f.frontier) : null,
59
+ by: f.by ? rewriteExpr(f.by) : null,
60
+ };
61
+ }
62
+ function rewriteSelect(items) {
63
+ return items.map((it) => it.kind === "field" ? { ...it, expr: rewriteExpr(it.expr) } : { ...it, op: rewriteOp(it.op) });
64
+ }
65
+ function rewriteSub(s) {
66
+ return {
67
+ from: s.from.map(rewriteExpr),
68
+ where: s.where ? rewriteWhere(s.where) : null,
69
+ select: rewriteSelect(s.select),
70
+ orderBy: s.orderBy ? s.orderBy.map((o) => ({ ...o, expr: rewriteExpr(o.expr) })) : null,
71
+ follow: s.follow ? rewriteFollow(s.follow) : null,
72
+ ...(s.values ? { values: true } : {}),
73
+ ...(s.limit ? { limit: rewriteExpr(s.limit) } : {}),
74
+ ...(s.offset ? { offset: rewriteExpr(s.offset) } : {}),
75
+ };
76
+ }
77
+ function rewriteQuery(q) {
78
+ return {
79
+ source: rewriteExpr(q.source),
80
+ from: q.from.map(rewriteExpr),
81
+ where: q.where ? rewriteWhere(q.where) : null,
82
+ select: rewriteSelect(q.select),
83
+ orderBy: q.orderBy ? q.orderBy.map((o) => ({ ...o, expr: rewriteExpr(o.expr) })) : null,
84
+ consumer: q.consumer,
85
+ follow: q.follow ? rewriteFollow(q.follow) : null,
86
+ ...(q.distinct ? { distinct: true } : {}),
87
+ ...(q.values ? { values: true } : {}),
88
+ ...(q.limit ? { limit: rewriteExpr(q.limit) } : {}),
89
+ ...(q.offset ? { offset: rewriteExpr(q.offset) } : {}),
90
+ };
91
+ }
92
+ // A top-level `limit`/`offset` on the collect path is applied by the runner (see
93
+ // oqxRunInner), so it must be a plain number literal here — there is no row to
94
+ // evaluate anything else against at the root, and omgbase queries carry no
95
+ // bindings.
96
+ function constBound(e, word) {
97
+ if (!e)
98
+ return null;
99
+ if (e.kind === "lit" && typeof e.value === "number" && Number.isInteger(e.value) && e.value >= 0)
100
+ return e.value;
101
+ throw new FilterInvalid(`top-level ${word} must be a non-negative integer literal`, "OQX");
102
+ }
103
+ // Distinct phrases referenced by `semantic("…")` (free calls in the raw parse).
104
+ export function collectSemanticPhrases(source) {
105
+ const phrases = new Set();
106
+ const visitExpr = (e) => {
107
+ switch (e.kind) {
108
+ case "call":
109
+ if (e.recv === null && e.name === "semantic" && e.args[0]?.kind === "lit" && typeof e.args[0].value === "string") {
110
+ phrases.add(e.args[0].value);
111
+ }
112
+ if (e.recv)
113
+ visitExpr(e.recv);
114
+ e.args.forEach(visitExpr);
115
+ return;
116
+ case "member":
117
+ visitExpr(e.recv);
118
+ return;
119
+ case "index":
120
+ visitExpr(e.recv);
121
+ visitExpr(e.index);
122
+ return;
123
+ case "unary":
124
+ visitExpr(e.expr);
125
+ return;
126
+ case "binary":
127
+ case "logical":
128
+ case "in":
129
+ visitExpr(e.left);
130
+ visitExpr(e.right);
131
+ return;
132
+ case "range":
133
+ if (e.lo)
134
+ visitExpr(e.lo);
135
+ if (e.hi)
136
+ visitExpr(e.hi);
137
+ return;
138
+ default: return;
139
+ }
140
+ };
141
+ const visitWhere = (w) => {
142
+ switch (w.kind) {
143
+ case "and":
144
+ case "or":
145
+ w.parts.forEach(visitWhere);
146
+ return;
147
+ case "not":
148
+ visitWhere(w.expr);
149
+ return;
150
+ case "scalar":
151
+ visitExpr(w.expr);
152
+ return;
153
+ case "op":
154
+ visitOp(w);
155
+ return;
156
+ }
157
+ };
158
+ const visitOp = (op) => { visitExpr(op.receiver); visitSub(op.sub); };
159
+ const visitSub = (s) => {
160
+ s.from.forEach(visitExpr);
161
+ if (s.where)
162
+ visitWhere(s.where);
163
+ s.select.forEach((it) => it.kind === "field" ? visitExpr(it.expr) : visitOp(it.op));
164
+ if (s.orderBy)
165
+ s.orderBy.forEach((o) => visitExpr(o.expr));
166
+ if (s.follow) {
167
+ visitExpr(s.follow.receiver);
168
+ [s.follow.where, s.follow.frontier, s.follow.by].forEach((x) => x && visitExpr(x));
169
+ }
170
+ };
171
+ let q;
172
+ try {
173
+ q = parse(source);
174
+ }
175
+ catch {
176
+ return [];
177
+ }
178
+ visitExpr(q.source);
179
+ q.from.forEach(visitExpr);
180
+ if (q.where)
181
+ visitWhere(q.where);
182
+ q.select.forEach((it) => it.kind === "field" ? visitExpr(it.expr) : visitOp(it.op));
183
+ if (q.orderBy)
184
+ q.orderBy.forEach((o) => visitExpr(o.expr));
185
+ if (q.follow) {
186
+ visitExpr(q.follow.receiver);
187
+ [q.follow.where, q.follow.frontier, q.follow.by].forEach((x) => x && visitExpr(x));
188
+ }
189
+ return [...phrases];
190
+ }
191
+ const ID_ITEM = { kind: "field", name: "__oqx_id", expr: { kind: "ident", name: "$id" }, lift: 0 };
192
+ const PATH_ITEM = { kind: "field", name: "__oqx_path", expr: { kind: "ident", name: "$path" }, lift: 0 };
193
+ // The reserved key a top-level `values` projection's single item is renamed to,
194
+ // so it rides through id/path injection, keyset paging, and distinct as an
195
+ // ordinary record field and is peeled off at the end.
196
+ const VALUE_KEY = "__oqx_value";
197
+ function toHit(row) {
198
+ const o = row;
199
+ const { __oqx_id, __oqx_path, ...rest } = o;
200
+ return { id: String(__oqx_id), path: String(__oqx_path ?? ""), ...rest };
201
+ }
202
+ // Top-level `select distinct`: dedup hits by their USER projection (every field
203
+ // except the injected id/path), keeping the first (so the retained hit carries a
204
+ // real id/path). Order-preserving.
205
+ function dedupHitsByProjection(hits) {
206
+ const seen = new Set();
207
+ const out = [];
208
+ for (const h of hits) {
209
+ const { id: _id, path: _path, ...proj } = h;
210
+ void _id;
211
+ void _path;
212
+ const key = JSON.stringify(Object.keys(proj).sort().map((k) => [k, proj[k]]));
213
+ if (seen.has(key))
214
+ continue;
215
+ seen.add(key);
216
+ out.push(h);
217
+ }
218
+ return out;
219
+ }
220
+ export function oqxRun(store, repoId, source, opts = {}) {
221
+ try {
222
+ return oqxRunInner(store, repoId, source, opts);
223
+ }
224
+ catch (e) {
225
+ // Normalize the library's errors to omgbase's contract (filter_invalid).
226
+ if (e instanceof OqxError)
227
+ throw new FilterInvalid(e.message, "OQX");
228
+ throw e;
229
+ }
230
+ }
231
+ function oqxRunInner(store, repoId, source, opts) {
232
+ const parsed = rewriteQuery(parse(source));
233
+ const consumer = parsed.consumer;
234
+ const ctxOpts = opts.semanticVectors ? { semanticVectors: opts.semanticVectors } : {};
235
+ const ctx = makeStoreContext(store, repoId, ctxOpts);
236
+ // Tier-3 pushdown reduces the scan in SQL; the in-memory fallback finishes the
237
+ // residual (and runs anything not pushable), so results match a pure scan.
238
+ const engine = opts.plan === false
239
+ ? new InMemoryEngine(ctx)
240
+ : new PlannedEngine(new SQLiteQueryPlanner(store, repoId, ctxOpts), ctx);
241
+ if (consumer === "exists") {
242
+ const res = engine.run(parsed, []);
243
+ return { hits: [], truncated: false, cursor: null, consumer: "exists", exists: res.consumer === "exists" ? res.exists : false };
244
+ }
245
+ if (consumer === "count") {
246
+ const res = engine.run(parsed, []);
247
+ return { hits: [], truncated: false, cursor: null, consumer: "count", count: res.consumer === "count" ? res.count : 0 };
248
+ }
249
+ if (consumer === "none") {
250
+ const res = engine.run(parsed, []);
251
+ return { hits: [], truncated: false, cursor: null, consumer: "none", none: res.consumer === "none" ? res.none : true };
252
+ }
253
+ // collect / first / single: inject id + path so every hit carries them. A
254
+ // top-level `select distinct` is applied HERE, not in the engine: the injected
255
+ // id/path are unique per row and would defeat the engine's projection dedup, so
256
+ // we run without engine-distinct and dedup hits by their USER projection below,
257
+ // keeping the first row's id/path.
258
+ const topDistinct = !!parsed.distinct;
259
+ // A top-level `values` projection runs as a RECORD projection whose single
260
+ // item is renamed to VALUE_KEY (the engine's own values mode is switched off),
261
+ // so pagination and distinct work unchanged; the bare values are peeled off
262
+ // the final page below and returned as `values` with `hits` empty.
263
+ const topValues = !!parsed.values;
264
+ const userSelect = topValues ? [{ ...parsed.select[0], name: VALUE_KEY }] : parsed.select;
265
+ // On the collect path a top-level `limit`/`offset` is ALSO taken out of the
266
+ // engine query and applied here, after the runner's own distinct — the engine
267
+ // would otherwise bound the raw rows before dedup (`select distinct type
268
+ // limit 3` must be three distinct types). first/single keep theirs: the
269
+ // engine's offset-aware cap is exactly right for them.
270
+ const { limit: topLimit, offset: topOffset, ...unbounded } = parsed;
271
+ const base = consumer === "collect" ? unbounded : parsed;
272
+ const q = { ...base, distinct: false, values: false, select: [ID_ITEM, PATH_ITEM, ...userSelect] };
273
+ const res = engine.run(q, []);
274
+ if (consumer === "first" || consumer === "single") {
275
+ const row = res.consumer === "first" || res.consumer === "single" ? res.row : null;
276
+ if (topValues)
277
+ return { hits: [], truncated: false, cursor: null, consumer, values: row == null ? [] : [toHit(row)[VALUE_KEY]] };
278
+ return { hits: row == null ? [] : [toHit(row)], truncated: false, cursor: null, consumer };
279
+ }
280
+ // collect: keyset pagination on (path, id) when the order is the default.
281
+ let rows = (res.consumer === "collect" ? res.rows : []).map(toHit);
282
+ if (topDistinct)
283
+ rows = dedupHitsByProjection(rows);
284
+ // The query's own bound defines the result SET; the `limit`/`cursor` options
285
+ // then page within it.
286
+ const offset = constBound(topOffset, "offset") ?? 0;
287
+ const limit = constBound(topLimit, "limit");
288
+ if (offset || limit != null)
289
+ rows = rows.slice(offset, limit == null ? undefined : offset + limit);
290
+ const custom = !!parsed.orderBy && parsed.orderBy.length > 0;
291
+ const cap = opts.limit ?? 50;
292
+ let page = rows;
293
+ if (!custom && opts.cursor) {
294
+ const { path, id } = decodeCursor(opts.cursor);
295
+ page = page.filter((h) => h.path > path || (h.path === path && h.id > id));
296
+ }
297
+ const truncated = page.length > cap;
298
+ page = page.slice(0, cap);
299
+ const last = page[page.length - 1];
300
+ const cursor = truncated && last && !custom ? encodeCursor(last.path, last.id) : null;
301
+ if (topValues)
302
+ return { hits: [], truncated, cursor, consumer: "collect", values: page.map((h) => h[VALUE_KEY]) };
303
+ return { hits: page, truncated, cursor, consumer: "collect" };
304
+ }
305
+ export async function oqxRunAsync(store, repoId, source, opts = {}, embedQuery) {
306
+ const phrases = collectSemanticPhrases(source);
307
+ if (phrases.length === 0)
308
+ return oqxRun(store, repoId, source, opts);
309
+ if (!embedQuery)
310
+ throw new FilterInvalid("semantic(...) needs an embedding provider; none is configured", "OQX semantic");
311
+ const semanticVectors = new Map();
312
+ for (const phrase of phrases) {
313
+ const { model, vec } = await embedQuery(phrase);
314
+ semanticVectors.set(phrase, { model, vec: float32ToBlob(vec) });
315
+ }
316
+ return oqxRun(store, repoId, source, { ...opts, semanticVectors });
317
+ }
318
+ // The collect page's keyset is (path, id): the kernel's shared cursor encoding
319
+ // (core/cursor.ts) with a two-part tuple. A malformed cursor is CursorInvalid,
320
+ // which the MCP layer maps to filter_invalid.
321
+ function encodeCursor(path, id) {
322
+ return encodeKeyset([path, id]);
323
+ }
324
+ function decodeCursor(cursor) {
325
+ const [path, id] = decodeKeyset(cursor, "query", 2);
326
+ return { path: path, id: id };
327
+ }
328
+ /** Parse an OQX source into the `@omgbase/oqx` AST (re-exported for callers). */
329
+ export function parseOqx(source) {
330
+ return parse(source);
331
+ }
332
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1,27 @@
1
+ import type { Expr } from "@omgbase/oqx";
2
+ /** SQL row domain, matching the store's tables. */
3
+ export type Target = "docs" | "blocks" | "nodes" | "edges";
4
+ /** The SQL aliases the compiler assigned to the current scope's row (`self`) and
5
+ * its owning document (`doc`). On the `docs` target the row IS the document, so
6
+ * both are the same alias. */
7
+ export interface Aliases {
8
+ self: string;
9
+ doc: string;
10
+ }
11
+ export interface TranslateCtx extends Aliases {
12
+ target: Target;
13
+ /** Query bindings, used to resolve `${…}` interpolations (`{kind:"binding"}`)
14
+ * to their values (mirrors `@omgbase/oqx`'s `constValue`). */
15
+ params: readonly unknown[];
16
+ }
17
+ /** A SQL fragment plus its positional bind params, in statement order. */
18
+ export interface Frag {
19
+ sql: string;
20
+ params: unknown[];
21
+ }
22
+ /** Translate an expression used as a VALUE (comparison operand, method receiver,
23
+ * function argument) to a SQL scalar. Returns null if not faithfully translatable. */
24
+ export declare function translateValue(e: Expr, ctx: TranslateCtx): Frag | null;
25
+ /** Translate an expression used as a boolean PREDICATE to a SQL boolean, or null
26
+ * if it cannot be pushed faithfully. Only positive, AND-safe forms are handled. */
27
+ export declare function translatePredicate(e: Expr, ctx: TranslateCtx): Frag | null;