@bjornpagen/bumbledb 0.15.0 → 0.17.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 (103) hide show
  1. package/COOKBOOK.md +33 -49
  2. package/README.md +3 -3
  3. package/dist/capacity.d.ts +24 -136
  4. package/dist/capacity.d.ts.map +1 -1
  5. package/dist/capacity.js +18 -40
  6. package/dist/capacity.js.map +1 -1
  7. package/dist/closed.d.ts +0 -156
  8. package/dist/closed.d.ts.map +1 -1
  9. package/dist/closed.js +0 -104
  10. package/dist/closed.js.map +1 -1
  11. package/dist/db.d.ts +7 -223
  12. package/dist/db.d.ts.map +1 -1
  13. package/dist/db.js +147 -396
  14. package/dist/db.js.map +1 -1
  15. package/dist/face.d.ts +0 -133
  16. package/dist/face.d.ts.map +1 -1
  17. package/dist/face.js +0 -33
  18. package/dist/face.js.map +1 -1
  19. package/dist/fields.d.ts +1 -145
  20. package/dist/fields.d.ts.map +1 -1
  21. package/dist/fields.js +2 -91
  22. package/dist/fields.js.map +1 -1
  23. package/dist/index.d.ts +12 -15
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +10 -13
  26. package/dist/index.js.map +1 -1
  27. package/dist/law.d.ts +111 -93
  28. package/dist/law.d.ts.map +1 -1
  29. package/dist/law.js +23 -27
  30. package/dist/law.js.map +1 -1
  31. package/dist/lower.d.ts +9 -35
  32. package/dist/lower.d.ts.map +1 -1
  33. package/dist/lower.js +8 -53
  34. package/dist/lower.js.map +1 -1
  35. package/dist/marshal.d.ts +0 -65
  36. package/dist/marshal.d.ts.map +1 -1
  37. package/dist/marshal.js +0 -72
  38. package/dist/marshal.js.map +1 -1
  39. package/dist/native.d.ts +31 -289
  40. package/dist/native.d.ts.map +1 -1
  41. package/dist/native.js +15 -64
  42. package/dist/native.js.map +1 -1
  43. package/dist/query/atom.d.ts +10 -276
  44. package/dist/query/atom.d.ts.map +1 -1
  45. package/dist/query/atom.js +1 -96
  46. package/dist/query/atom.js.map +1 -1
  47. package/dist/query/find.d.ts +10 -76
  48. package/dist/query/find.d.ts.map +1 -1
  49. package/dist/query/find.js +0 -30
  50. package/dist/query/find.js.map +1 -1
  51. package/dist/query/lower.d.ts +58 -146
  52. package/dist/query/lower.d.ts.map +1 -1
  53. package/dist/query/lower.js +19 -256
  54. package/dist/query/lower.js.map +1 -1
  55. package/dist/query/parse-ir.d.ts +0 -7
  56. package/dist/query/parse-ir.d.ts.map +1 -1
  57. package/dist/query/parse-ir.js +1 -13
  58. package/dist/query/parse-ir.js.map +1 -1
  59. package/dist/query/run.d.ts +0 -36
  60. package/dist/query/run.d.ts.map +1 -1
  61. package/dist/query/run.js +0 -44
  62. package/dist/query/run.js.map +1 -1
  63. package/dist/query/scope.d.ts +24 -180
  64. package/dist/query/scope.d.ts.map +1 -1
  65. package/dist/query/scope.js +2 -66
  66. package/dist/query/scope.js.map +1 -1
  67. package/dist/relation.d.ts +2 -50
  68. package/dist/relation.d.ts.map +1 -1
  69. package/dist/relation.js +2 -37
  70. package/dist/relation.js.map +1 -1
  71. package/dist/schema.d.ts +13 -63
  72. package/dist/schema.d.ts.map +1 -1
  73. package/dist/schema.js +118 -92
  74. package/dist/schema.js.map +1 -1
  75. package/dist/spec.d.ts +1 -140
  76. package/dist/spec.d.ts.map +1 -1
  77. package/dist/spec.js +1 -68
  78. package/dist/spec.js.map +1 -1
  79. package/dist/statements.d.ts +6 -137
  80. package/dist/statements.d.ts.map +1 -1
  81. package/dist/statements.js +16 -119
  82. package/dist/statements.js.map +1 -1
  83. package/package.json +2 -2
  84. package/src/capacity.ts +26 -140
  85. package/src/closed.ts +5 -206
  86. package/src/db.ts +203 -692
  87. package/src/face.ts +0 -142
  88. package/src/fields.ts +4 -172
  89. package/src/index.ts +10 -15
  90. package/src/law.ts +201 -129
  91. package/src/lower.ts +8 -53
  92. package/src/marshal.ts +1 -85
  93. package/src/native.ts +58 -321
  94. package/src/query/atom.ts +26 -313
  95. package/src/query/find.ts +24 -110
  96. package/src/query/lower.ts +126 -377
  97. package/src/query/parse-ir.ts +1 -14
  98. package/src/query/run.ts +0 -45
  99. package/src/query/scope.ts +25 -186
  100. package/src/relation.ts +2 -66
  101. package/src/schema.ts +143 -122
  102. package/src/spec.ts +1 -160
  103. package/src/statements.ts +22 -174
@@ -1,36 +1,3 @@
1
- /**
2
- * `query()` and the IR lowering, REFERENCE-IDENTITY edition. A query is
3
- * built kysely-shaped — variables minted by {@link v} outside the rule and
4
- * reused by REFERENCE to join:
5
- *
6
- * query(S).rule((r) => {
7
- * const acct = v(Account)
8
- * const h = v(Holder)
9
- * return r
10
- * .match(Account, { id: acct.id, holder: acct.holder })
11
- * .match(Holder, { id: acct.holder })
12
- * .where(r.eq(acct.holder, r.param("root")))
13
- * .find({ account: acct.id, holder: acct.holder })
14
- * })
15
- *
16
- * — and is an INERT value: `Query<Rels, Row, Params>` with `Row` inferred
17
- * from each rule's `.find` RECORD (its keys ARE the answer columns) and
18
- * `Params` inferred to be EXACTLY the params the rules use (params are typed
19
- * BY USE; a param no rule uses never registers). Variable IDENTITY is the
20
- * object reference: reusing one value across binding positions IS the join,
21
- * lowering is a pure function of the query value down to the
22
- * bridge's `QueryIr` (`bumbledb/crates/bumbledb/src/ir.rs`): relations by
23
- * declaration ordinal, variables by dense per-rule first-occurrence ids
24
- * (keyed on the object REFERENCE — the discipline is unchanged, only the map
25
- * key moved from name to reference), params by first-use order. Lowering is
26
- * STABLE — the same query value lowers to deeply-equal IR every time, and
27
- * two identically-written queries (fresh mints each) lower identically.
28
- * Construction validates negation safety and boundness (typed by the var's
29
- * label — object identity is invisible to the type tier, so these are
30
- * construction-time walls); everything else (types, aggregate
31
- * rosters, rule caps) is the ENGINE's judge, surfacing at prepare.
32
- */
33
-
34
1
  import * as errors from "@superbuilders/errors"
35
2
  import { sealedFieldsOf } from "#closed.ts"
36
3
  import type { AnyField, ClosedRoster } from "#fields.ts"
@@ -80,97 +47,60 @@ import { allen, and, eq, ge, gt, le, lt, ne, not, or, pointIn } from "#query/ato
80
47
  import type { CheckFind, CheckRecFind, FindShape, HeadRecordOf, RowOfFind } from "#query/find.ts"
81
48
  import { count, max, min, pack, sum } from "#query/find.ts"
82
49
  import { parseQueryIr } from "#query/parse-ir.ts"
83
- import type { AnyVar, ClassedField, Flatten, InferredOf, ParamEntry, ParamsRecord, ShapeOf } from "#query/scope.ts"
84
- import {
85
- fieldJoins,
86
- inferred,
87
- isTerm,
88
- makeDuration,
89
- makeParam,
90
- makeSetParam,
91
- renderFieldKind,
92
- term
50
+ import type {
51
+ AnyVar,
52
+ ClassedField,
53
+ ExactVars,
54
+ Flatten,
55
+ InferredOf,
56
+ ParamEntry,
57
+ ParamsRecord,
58
+ ShapeOf,
59
+ VarsOf
93
60
  } from "#query/scope.ts"
61
+ import { fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term } from "#query/scope.ts"
94
62
  import type { AnySchema, Schema, SchemaRelations } from "#schema.ts"
95
63
 
96
- /**
97
- * The matchable members of a schema's record — ordinary relations AND
98
- * closed vocabularies (ψ query atoms; the ENGINE decides folding vs virtual
99
- * image, the SDK lowers pass-through).
100
- */
101
64
  type QueryRelation<Rels extends SchemaRelations> = Extract<Rels[keyof Rels], MatchOwner>
102
65
 
103
- /** Reads an inferred-params carrier off a rec reference or rule value. */
104
66
  type ParamsOf<T> = InferredOf<T> extends { readonly params: infer P extends ParamsRecord } ? P : Record<never, never>
105
67
 
106
- /** Reads an inferred-row carrier off a rule value or query. */
107
68
  type RowOf<T> = InferredOf<T> extends { readonly row: infer R } ? R : never
108
69
 
109
- /**
110
- * A derived table's HEAD signature as classed slots, keyed by column
111
- * name; `undefined` on values that carry no head.
112
- */
113
70
  type HeadShape = Readonly<Record<string, ClassedField>> | undefined
114
71
 
115
- /**
116
- * One finished rule as a plain value: the runtime data plus the inferred
117
- * row/params carrier (and, for an interior or rec rule, the head
118
- * record of classed slots an `.interior(name)` join pairs against).
119
- * `.rule(...)` consumes it.
120
- */
121
72
  interface RuleValue<Row, P extends ParamsRecord, Head extends HeadShape = undefined> {
122
73
  readonly rule: RuleData
123
74
  readonly [inferred]?: { readonly row: Row; readonly params: P; readonly head: Head }
124
75
  }
125
76
 
126
- /** Any finished rule value. */
127
77
  type AnyRuleValue = RuleValue<unknown, ParamsRecord, HeadShape>
128
78
 
129
- /** Reads an inferred-head carrier off a rule value or rec reference. */
130
79
  type HeadOf<T> =
131
80
  InferredOf<T> extends { readonly head: infer H extends Readonly<Record<string, ClassedField>> } ? H : undefined
132
81
 
133
- /** One `.interior(name)` position's judgment: a variable. */
134
82
  type InteriorBindingOk<V> = V extends AnyVar ? true : false
135
83
 
136
- /**
137
- * The validated `.interior(name)` bindings record: every entry must be a
138
- * variable. Arity and class against the named table's head are
139
- * construction-time (the name is a string, so the head is not a type-level
140
- * fact).
141
- */
142
84
  type CheckInteriorBindings<B> = {
143
85
  readonly [K in keyof B]: InteriorBindingOk<B[K]> extends true ? B[K] : never
144
86
  }
145
87
 
146
- /** One interior-rule builder function. */
147
88
  type InteriorBuild<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> = (
148
89
  r: InteriorRuleScope<Rels, Classes>
149
90
  ) => AnyRuleValue
150
91
 
151
- /** One rec-arm builder function. */
152
92
  type RecBuild<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> = (
153
93
  r: RecRuleScope<Rels, Classes>
154
94
  ) => AnyRuleValue
155
95
 
156
- /** A build function's rule value. */
157
96
  type BuiltRule<F> = F extends (r: never) => infer RV ? RV : never
158
97
 
159
- /** The intersected params record of a tuple of rule builds. */
160
98
  type BuildsParams<Builds extends readonly ((r: never) => AnyRuleValue)[]> = ShapeOf<ParamsOf<BuiltRule<Builds[number]>>>
161
99
 
162
- /**
163
- * The term/predicate/aggregate constructor vocabulary every rule builder
164
- * carries — pure value builders. Variables are minted by the free {@link v},
165
- * outside the rule, and reused by reference; `r` no longer mints them.
166
- */
167
100
  interface TermOps {
168
- /** Names one scalar parameter: typed by its use; the key of the execute params object. */
169
101
  readonly param: typeof makeParam
170
- /** Names one ∈-set parameter (the IR's `ParamSet`): bound to a readonly array at execution. */
102
+
171
103
  readonly inSet: typeof makeSetParam
172
- /** The measure of an interval-typed variable: `|[s, e)| = e − s`, u64. */
173
- readonly duration: typeof makeDuration
174
104
  readonly eq: typeof eq
175
105
  readonly ne: typeof ne
176
106
  readonly lt: typeof lt
@@ -189,52 +119,82 @@ interface TermOps {
189
119
  readonly pack: typeof pack
190
120
  }
191
121
 
192
- /** The rule builder a `query(S).rule(...)` callback receives (`Classes` — the join judge's authority). */
193
122
  interface QueryRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> extends TermOps {
194
- /** The first EDB atom of the rule: fields bind variables, params, ∈-sets, or bare literals; absence is the wildcard. */
123
+ /**
124
+ * The FULL binding: every column of R bound to its own v(R) mint — the
125
+ * identity atom, stated as a signature so it holds for GENERIC R too
126
+ * (VarsOf unifies with itself by identity; the general form's deferred
127
+ * conditionals cannot). The mint invariant — a variable's mint slot IS its
128
+ * position slot, same owner, same column — discharges the join judgment by
129
+ * construction; an all-var record contributes no params, so the chain starts
130
+ * paramless. {@link ExactVars} maps a foreign key to `never`, so an
131
+ * aliased extra-key record falls to the general form's judgment.
132
+ */
133
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
134
+ relation: R,
135
+ bindings: B & ExactVars<R, B>
136
+ ): QueryRuleChain<Rels, Record<never, never>, Classes>
137
+
195
138
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
196
139
  relation: R,
197
140
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
198
141
  ): QueryRuleChain<Rels, BindParamsShape<MatchFields<R>, B>, Classes>
199
- /**
200
- * A rule may START with a finished table: an interior atom is a positive
201
- * occurrence exactly as the engine represents it, so its variables ground
202
- * — the identity projection `(c) | reach(c);` is spellable with no
203
- * re-grounding join over a domain relation.
204
- */
142
+
205
143
  interior<const B extends Readonly<Record<string, AnyVar>>>(
206
144
  name: string,
207
145
  bindings: B & CheckInteriorBindings<B>
208
146
  ): QueryRuleChain<Rels, Record<never, never>, Classes>
209
147
  }
210
148
 
211
- /** The chain of a plain query rule: more atoms, residual predicates, then the head. */
212
149
  interface QueryRuleChain<
213
150
  Rels extends SchemaRelations,
214
151
  P extends ParamsRecord,
215
152
  Classes extends SchemaClasses = SchemaClasses
216
153
  > {
217
- /** One more positive EDB atom — variable reuse joins, class-equal by the mint-slot judgment. */
154
+ /**
155
+ * The FULL binding: every column of R bound to its own v(R) mint — the
156
+ * identity atom, generic R included. The mint invariant (a variable's mint
157
+ * slot IS its position slot) discharges the join judgment by construction;
158
+ * an all-var record contributes no params — P rides through unchanged.
159
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
160
+ * extra-key record falls to the general form's judgment.
161
+ */
162
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
163
+ relation: R,
164
+ bindings: B & ExactVars<R, B>
165
+ ): QueryRuleChain<Rels, P, Classes>
166
+
218
167
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
219
168
  relation: R,
220
169
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
221
170
  ): QueryRuleChain<Rels, Flatten<P & BindParamsShape<MatchFields<R>, B>>, Classes>
222
- /** One residual predicate: a comparison, an `and`/`or` tree, or a negated atom (`r.not`). */
171
+
223
172
  where<const C extends AnyCond>(
224
173
  cond: CheckCond<Classes, C> & C
225
174
  ): QueryRuleChain<Rels, Flatten<P & CondParamsShape<C>>, Classes>
226
- /** One interior atom over a finished table (an earlier interior, or the rec). */
175
+
227
176
  interior<const B extends Readonly<Record<string, AnyVar>>>(
228
177
  name: string,
229
178
  bindings: B & CheckInteriorBindings<B>
230
179
  ): QueryRuleChain<Rels, P, Classes>
231
- /** The head projection: a `find` RECORD whose keys name the answer columns. */
180
+
232
181
  find<const F extends FindShape>(entries: F & CheckFind<F>): RuleValue<RowOfFind<F>, P>
233
182
  }
234
183
 
235
- /** The rule builder an `interior("mid", ...)` callback receives. */
236
184
  interface InteriorRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses>
237
185
  extends TermOps {
186
+ /**
187
+ * The FULL binding: every column of R bound to its own v(R) mint — the
188
+ * identity atom, generic R included. The mint invariant (a variable's mint
189
+ * slot IS its position slot) discharges the join judgment by construction;
190
+ * an all-var record contributes no params, so the chain starts paramless.
191
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
192
+ * extra-key record falls to the general form's judgment.
193
+ */
194
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
195
+ relation: R,
196
+ bindings: B & ExactVars<R, B>
197
+ ): InteriorRuleChain<Rels, Record<never, never>, Classes>
238
198
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
239
199
  relation: R,
240
200
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
@@ -245,12 +205,23 @@ interface InteriorRuleScope<Rels extends SchemaRelations, Classes extends Schema
245
205
  ): InteriorRuleChain<Rels, Record<never, never>, Classes>
246
206
  }
247
207
 
248
- /** The chain of an interior rule: bound-variable heads only. */
249
208
  interface InteriorRuleChain<
250
209
  Rels extends SchemaRelations,
251
210
  P extends ParamsRecord,
252
211
  Classes extends SchemaClasses = SchemaClasses
253
212
  > {
213
+ /**
214
+ * The FULL binding: every column of R bound to its own v(R) mint — the
215
+ * identity atom, generic R included. The mint invariant (a variable's mint
216
+ * slot IS its position slot) discharges the join judgment by construction;
217
+ * an all-var record contributes no params — P rides through unchanged.
218
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
219
+ * extra-key record falls to the general form's judgment.
220
+ */
221
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
222
+ relation: R,
223
+ bindings: B & ExactVars<R, B>
224
+ ): InteriorRuleChain<Rels, P, Classes>
254
225
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
255
226
  relation: R,
256
227
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
@@ -265,8 +236,19 @@ interface InteriorRuleChain<
265
236
  find<const F extends FindShape>(entries: F & CheckRecFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
266
237
  }
267
238
 
268
- /** The rule builder a `.reach("reach", { base, rec })` arm receives. */
269
239
  interface RecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> extends TermOps {
240
+ /**
241
+ * The FULL binding: every column of R bound to its own v(R) mint — the
242
+ * identity atom, generic R included. The mint invariant (a variable's mint
243
+ * slot IS its position slot) discharges the join judgment by construction;
244
+ * an all-var record contributes no params, so the chain starts paramless.
245
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
246
+ * extra-key record falls to the general form's judgment.
247
+ */
248
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
249
+ relation: R,
250
+ bindings: B & ExactVars<R, B>
251
+ ): RecRuleChain<Rels, Record<never, never>, Classes>
270
252
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
271
253
  relation: R,
272
254
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
@@ -277,17 +259,23 @@ interface RecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClass
277
259
  ): RecRuleChain<Rels, Record<never, never>, Classes>
278
260
  }
279
261
 
280
- /**
281
- * The chain of a rec arm. `.interior("reach", …)` is the self-atom
282
- * on rec arms (and a prior interior on either list). `find` takes bound
283
- * variables only — aggregates and the measure are unrepresentable in a
284
- * rec head.
285
- */
286
262
  interface RecRuleChain<
287
263
  Rels extends SchemaRelations,
288
264
  P extends ParamsRecord,
289
265
  Classes extends SchemaClasses = SchemaClasses
290
266
  > {
267
+ /**
268
+ * The FULL binding: every column of R bound to its own v(R) mint — the
269
+ * identity atom, generic R included. The mint invariant (a variable's mint
270
+ * slot IS its position slot) discharges the join judgment by construction;
271
+ * an all-var record contributes no params — P rides through unchanged.
272
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
273
+ * extra-key record falls to the general form's judgment.
274
+ */
275
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
276
+ relation: R,
277
+ bindings: B & ExactVars<R, B>
278
+ ): RecRuleChain<Rels, P, Classes>
291
279
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
292
280
  relation: R,
293
281
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
@@ -302,38 +290,32 @@ interface RecRuleChain<
302
290
  find<const F extends FindShape>(entries: F & CheckRecFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
303
291
  }
304
292
 
305
- /** A query's runtime description — everything lowering, the wire marshal, and answer decode read. */
306
293
  type QueryData =
307
294
  | {
308
295
  readonly kind: "cq"
309
- /** Named interiors in declaration order (DAG). */
296
+
310
297
  readonly interiors: readonly InteriorData[]
311
- /** The main rules in written order (multiple rules = set union). */
298
+
312
299
  readonly rules: readonly RuleData[]
313
- /** The head columns (every rule derives the same head; written order = answer column order). */
300
+
314
301
  readonly finds: readonly FindColumn[]
315
- /** The registered params in first-use order across the query walk (= dense `ParamId`s). */
302
+
316
303
  readonly params: readonly ParamEntry[]
317
304
  }
318
305
  | {
319
306
  readonly kind: "reach"
320
- /** Named interiors in declaration order (DAG). */
307
+
321
308
  readonly interiors: readonly InteriorData[]
322
- /** The linear rec (base and rec arms nonempty by type). */
309
+
323
310
  readonly rec: RecData
324
- /** The main rules in written order (multiple rules = set union). */
311
+
325
312
  readonly rules: readonly RuleData[]
326
- /** The head columns (every rule derives the same head; written order = answer column order). */
313
+
327
314
  readonly finds: readonly FindColumn[]
328
- /** The registered params in first-use order across the query walk (= dense `ParamId`s). */
315
+
329
316
  readonly params: readonly ParamEntry[]
330
317
  }
331
318
 
332
- /**
333
- * An inert query value. `Row` is the inferred answer-row object type;
334
- * `Params` the inferred execute-params object type — exactly the params the
335
- * rules use. Prepare with `db.prepare(q)`.
336
- */
337
319
  interface Query<
338
320
  Rels extends SchemaRelations,
339
321
  Row,
@@ -342,34 +324,26 @@ interface Query<
342
324
  > {
343
325
  readonly schema: Schema<Rels, Classes>
344
326
  readonly data: QueryData
345
- /** One more rule — the query's answers are the SET UNION of its rules' answers; every rule derives the same head. */
327
+
346
328
  rule<RV extends AnyRuleValue>(
347
329
  build: (r: QueryRuleScope<Rels, Classes>) => RV
348
330
  ): Query<Rels, Row | RowOf<RV>, Flatten<Params & ParamsOf<RV>>, Classes>
349
- /** Construction error: interiors precede main rules. Uncallable after `.rule()`. */
331
+
350
332
  interior(name: string, ...builds: never[]): never
351
- /** Construction error: reach precedes main rules. Uncallable after `.rule()`. */
333
+
352
334
  reach(name: string, arms: never): never
353
335
  readonly [inferred]?: { readonly row: Row; readonly params: Params }
354
336
  }
355
337
 
356
- /** Any query value as lowering and the runtime consume it. */
357
338
  interface AnyQuery {
358
339
  readonly schema: AnySchema
359
340
  readonly data: QueryData
360
341
  }
361
342
 
362
- /** Extracts a query value's inferred answer-row type. */
363
343
  type QueryRow<Q extends AnyQuery> = RowOf<Q>
364
344
 
365
- /** Extracts a query value's inferred execute-params type. */
366
345
  type QueryParams<Q extends AnyQuery> = ParamsOf<Q>
367
346
 
368
- /**
369
- * The entry value of `query(S)`: interiors, then reach (moves to
370
- * {@link QueryReachStart}), then the first `.rule` mints the query.
371
- * `interior` / `reach` exist only on this CQ start.
372
- */
373
347
  type QueryStart<
374
348
  Rels extends SchemaRelations,
375
349
  Classes extends SchemaClasses = SchemaClasses,
@@ -388,7 +362,6 @@ type QueryStart<
388
362
  ): QueryReachStart<Rels, Classes, Flatten<P & BuildsParams<Base> & BuildsParams<Step>>>
389
363
  }
390
364
 
391
- /** After `.reach()`: interior/reach are unrepresentable; only `.rule` remains. */
392
365
  type QueryReachStart<
393
366
  Rels extends SchemaRelations,
394
367
  Classes extends SchemaClasses = SchemaClasses,
@@ -399,11 +372,9 @@ type QueryReachStart<
399
372
  ): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes>
400
373
  }
401
374
 
402
- /** The frozen constructor vocabulary every rule builder spreads. */
403
375
  const termOps: TermOps = Object.freeze({
404
376
  param: makeParam,
405
377
  inSet: makeSetParam,
406
- duration: makeDuration,
407
378
  eq,
408
379
  ne,
409
380
  lt,
@@ -422,21 +393,18 @@ const termOps: TermOps = Object.freeze({
422
393
  pack
423
394
  })
424
395
 
425
- /** One rule under construction: immutable — every chain step is a fresh state. Boundness rides the `bound` set of var references. */
426
396
  interface RuleBuildState {
427
397
  readonly items: readonly RuleItem[]
428
398
  readonly bound: ReadonlySet<AnyVar>
429
399
  readonly paramUses: readonly ParamUse[]
430
400
  }
431
401
 
432
- /** The empty rule state. */
433
402
  const EMPTY_RULE: RuleBuildState = Object.freeze({
434
403
  items: Object.freeze([]),
435
404
  bound: new Set<AnyVar>(),
436
405
  paramUses: Object.freeze([])
437
406
  })
438
407
 
439
- /** One resolved bindings record: the atom entries, the variable references it binds, and the params it uses. */
440
408
  interface ResolvedBindings {
441
409
  readonly atom: AtomData
442
410
  readonly vars: readonly AnyVar[]
@@ -461,11 +429,6 @@ function mintSlotOf(context: ChainContext, ref: AnyVar): ClassedField {
461
429
  return { field: ref.field, class: context.classes[ref.owner.name]?.[ref.column] }
462
430
  }
463
431
 
464
- /**
465
- * Judges one membership ARRAY at a binding position — legal exactly at a
466
- * CLOSED-reference field, holding ≥ 2 DISTINCT handle names. The returned
467
- * name is CONTENT-ADDRESSED (vocabulary + the member SET).
468
- */
469
432
  function membershipSet(
470
433
  context: string,
471
434
  field: AnyField,
@@ -502,14 +465,6 @@ function membershipSet(
502
465
  return { name: `∈ ${roster.name} ${JSON.stringify(key)}`, members: Object.freeze(members) }
503
466
  }
504
467
 
505
- /**
506
- * Resolves a bindings record against an atom owner's matchable fields, in
507
- * the record's written order: terms classify by their runtime tag,
508
- * everything else is a bare literal. Every VARIABLE binding judges
509
- * `fieldJoins(mintSlot, positionSlot)` and throws on a class-unequal reuse
510
- * (the runtime twin of `CheckBindings`); the bound refs are collected for
511
- * the rule's boundness set.
512
- */
513
468
  function resolveBindings(
514
469
  context: ChainContext,
515
470
  label: string,
@@ -574,10 +529,6 @@ function resolveBindings(
574
529
  )
575
530
  break
576
531
  }
577
- case "duration":
578
- throw errors.new(
579
- `${label}.${fieldName}: the measure is not a field-typed value — it lives in comparisons and find entries`
580
- )
581
532
  }
582
533
  } else if (Array.isArray(value)) {
583
534
  const set = membershipSet(`${label}.${fieldName}`, declared.field, value)
@@ -599,7 +550,6 @@ function resolveBindings(
599
550
  return { atom: Object.freeze({ relation, bindings: Object.freeze(entries) }), vars, uses }
600
551
  }
601
552
 
602
- /** Extends a rule state with one positive atom; the bound variable references accumulate into the boundness set. */
603
553
  function advanceMatch(
604
554
  context: ChainContext,
605
555
  state: RuleBuildState,
@@ -618,7 +568,6 @@ function advanceMatch(
618
568
  })
619
569
  }
620
570
 
621
- /** Resolves one comparison side to its runtime term (variables and the measure ride by reference). */
622
571
  function cmpTermDataOf(value: unknown): CmpTermData {
623
572
  if (isTerm(value)) {
624
573
  switch (value[term]) {
@@ -628,30 +577,16 @@ function cmpTermDataOf(value: unknown): CmpTermData {
628
577
  return Object.freeze({ kind: "param" as const, name: value.name })
629
578
  case "setParam":
630
579
  return Object.freeze({ kind: "setParam" as const, name: value.name })
631
- case "duration":
632
- return Object.freeze({ kind: "measure" as const, ref: value.over })
633
580
  }
634
581
  }
635
582
  return Object.freeze({ kind: "literal" as const, value })
636
583
  }
637
584
 
638
- /**
639
- * One comparison side's contribution to the param census: a param/set side
640
- * anchors to its SIBLING — a variable's field descriptor or the measure; an
641
- * unanchorable use records with no anchor.
642
- */
643
585
  function sideUses(op: CmpKind, side: CmpTermData, sibling: CmpTermData, uses: ParamUse[]): void {
644
586
  if (side.kind !== "param" && side.kind !== "setParam") {
645
587
  return
646
588
  }
647
- let anchor: AnyField | "measure" | undefined
648
- if (sibling.kind === "var") {
649
- anchor = sibling.ref.field
650
- } else if (sibling.kind === "measure") {
651
- anchor = "measure"
652
- } else {
653
- anchor = undefined
654
- }
589
+ const anchor = sibling.kind === "var" ? sibling.ref.field : undefined
655
590
  uses.push(
656
591
  Object.freeze({
657
592
  name: side.name,
@@ -663,7 +598,6 @@ function sideUses(op: CmpKind, side: CmpTermData, sibling: CmpTermData, uses: Pa
663
598
  )
664
599
  }
665
600
 
666
- /** Lowers one condition VALUE to its runtime data, recording param uses. */
667
601
  function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
668
602
  if (cond.cond === "cmp") {
669
603
  const lhs = cmpTermDataOf(cond.lhs)
@@ -695,7 +629,6 @@ function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
695
629
  )
696
630
  }
697
631
 
698
- /** Extends a rule state with one `.where` item (a condition or a negated atom). */
699
632
  function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCond): RuleBuildState {
700
633
  if (typeof cond !== "object" || cond === null || !("cond" in cond)) {
701
634
  throw errors.new("where() takes a comparison, an and()/or() tree, or a negated atom")
@@ -731,15 +664,6 @@ function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCon
731
664
  })
732
665
  }
733
666
 
734
- /**
735
- * Extends a rule state with one interior atom (a named record over head keys;
736
- * vars validated at completion). A POSITIVE interior atom is a positive
737
- * occurrence exactly as the engine represents it (`check_atoms` walks Interior
738
- * and Edb in one loop), so its variables GROUND: they enter the rule's
739
- * boundness set, may ride the head, and satisfy negation safety — the
740
- * interior-only identity projection of a finished table is spellable with no
741
- * re-grounding join. A NEGATED one binds nothing, only rejects.
742
- */
743
667
  function advanceInterior(
744
668
  state: RuleBuildState,
745
669
  target: DerivedTable,
@@ -771,7 +695,6 @@ function advanceInterior(
771
695
  })
772
696
  }
773
697
 
774
- /** Narrows a find entry to an aggregate value. */
775
698
  function isAggregateEntry(value: unknown): value is { readonly agg: string; readonly over?: unknown } {
776
699
  return typeof value === "object" && value !== null && "agg" in value
777
700
  }
@@ -784,7 +707,6 @@ function asVarTerm(context: string, value: unknown): AnyVar {
784
707
  throw errors.new(`${context}: expected a variable`)
785
708
  }
786
709
 
787
- /** Classifies one aggregate find entry into its runtime data (variables ride by reference). */
788
710
  function aggDataOf(name: string, entry: { readonly agg: string; readonly over?: unknown }): AggData {
789
711
  if (entry.agg === "count") {
790
712
  return Object.freeze({ op: "count" as const })
@@ -797,10 +719,7 @@ function aggDataOf(name: string, entry: { readonly agg: string; readonly over?:
797
719
  if (isTerm(over) && over[term] === "var") {
798
720
  return Object.freeze({ op: "fold" as const, fold: entry.agg, over })
799
721
  }
800
- if (isTerm(over) && over[term] === "duration") {
801
- return Object.freeze({ op: "fold" as const, fold: entry.agg, over: Object.freeze({ duration: over.over }) })
802
- }
803
- throw errors.new(`find ${name} (${entry.agg}): takes a variable or r.duration(v)`)
722
+ throw errors.new(`find ${name} (${entry.agg}): takes a variable`)
804
723
  }
805
724
  case "pack":
806
725
  return Object.freeze({ op: "pack" as const, over: asVarTerm(`find ${name} (pack)`, over) })
@@ -809,11 +728,6 @@ function aggDataOf(name: string, entry: { readonly agg: string; readonly over?:
809
728
  }
810
729
  }
811
730
 
812
- /**
813
- * Classifies one find entry into its named answer column (the KEY names the
814
- * column, `count` included). The `slot`/`closed` slices are resolved LATER,
815
- * at rule completion, where boundness and the mint slots are in hand.
816
- */
817
731
  function findColumnOf(name: string, entry: unknown): FindColumn {
818
732
  if (isTerm(entry)) {
819
733
  if (entry[term] === "var") {
@@ -824,17 +738,7 @@ function findColumnOf(name: string, entry: unknown): FindColumn {
824
738
  slot: undefined
825
739
  })
826
740
  }
827
- if (entry[term] === "duration") {
828
- return Object.freeze({
829
- name,
830
- entry: Object.freeze({ kind: "measure" as const, over: entry.over }),
831
- closed: undefined,
832
- slot: undefined
833
- })
834
- }
835
- throw errors.new(
836
- `find ${name}: a ${entry[term]} is not projectable — find takes variables, r.duration(v), or aggregates`
837
- )
741
+ throw errors.new(`find ${name}: a ${entry[term]} is not projectable — find takes variables or aggregates`)
838
742
  }
839
743
  if (isAggregateEntry(entry)) {
840
744
  return Object.freeze({
@@ -844,32 +748,29 @@ function findColumnOf(name: string, entry: unknown): FindColumn {
844
748
  slot: undefined
845
749
  })
846
750
  }
847
- throw errors.new(`find ${name}: not a find entry — find takes variables, r.duration(v), or aggregates`)
751
+ throw errors.new(`find ${name}: not a find entry — find takes variables or aggregates`)
848
752
  }
849
753
 
850
754
  /**
851
- * The orderable ban's pointed refusal (`docs/architecture/10-data-model.md`
755
+ * The orderable ban's pointed refusal
852
756
  * § orderability): a closed reference is equality-and-membership only.
853
757
  */
854
758
  function closedOrderError(context: string, position: string, vocabulary: string): Error {
855
759
  return errors.new(
856
- `${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (docs/architecture/10-data-model.md; equality, membership, and counting remain)`
760
+ `${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (equality, membership, and counting remain)`
857
761
  )
858
762
  }
859
763
 
860
- /** The comparison ops under the orderable ban (order roster + point membership). */
861
764
  function isOrderOp(op: CmpKind | "binding"): op is "lt" | "le" | "gt" | "ge" | "pointIn" {
862
765
  return op === "lt" || op === "le" || op === "gt" || op === "ge" || op === "pointIn"
863
766
  }
864
767
 
865
- /** Requires a variable to be bound by a relation atom of the rule (the boundness wall — invisible to the type tier). */
866
768
  function assertBound(where: string, bound: ReadonlySet<AnyVar>, ref: AnyVar): void {
867
769
  if (!bound.has(ref)) {
868
770
  throw errors.new(`${where}: the variable ${ref.label} is not bound by a relation atom of the rule`)
869
771
  }
870
772
  }
871
773
 
872
- /** Requires a variable to be interval-typed (the measure's and pack's domain), off its own descriptor. */
873
774
  function assertInterval(where: string, ref: AnyVar): void {
874
775
  if (ref.field.kind !== "interval") {
875
776
  throw errors.new(
@@ -878,7 +779,6 @@ function assertInterval(where: string, ref: AnyVar): void {
878
779
  }
879
780
  }
880
781
 
881
- /** Requires a variable's own field to be non-closed (the orderable ban's runtime twin). */
882
782
  function assertNotClosed(where: string, position: string, ref: AnyVar): void {
883
783
  const roster = rosterOf(ref.field)
884
784
  if (roster !== undefined) {
@@ -886,11 +786,12 @@ function assertNotClosed(where: string, position: string, ref: AnyVar): void {
886
786
  }
887
787
  }
888
788
 
889
- /**
890
- * The classed mint slot one answer column's VALUES flow from: a projected
891
- * variable's mint slot. Counts, folds, `pack` and the measure derive
892
- * numbers/intervals, so they resolve no slot.
893
- */
789
+ function assertNumeric(where: string, position: string, ref: AnyVar): void {
790
+ if (ref.field.kind !== "u64" && ref.field.kind !== "i64") {
791
+ throw errors.new(`${where}: ${position} ${ref.label} is ${ref.field.kind}, not numeric — a fold reads u64/i64 only`)
792
+ }
793
+ }
794
+
894
795
  function findColumnSlotOf(context: ChainContext, column: FindColumn): ClassedField | undefined {
895
796
  const entry = column.entry
896
797
  if (entry.kind === "var") {
@@ -899,7 +800,6 @@ function findColumnSlotOf(context: ChainContext, column: FindColumn): ClassedFie
899
800
  return undefined
900
801
  }
901
802
 
902
- /** Validates one find column's variable references (boundness + the orderable/interval walls, off the var's own field). */
903
803
  function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, column: FindColumn): void {
904
804
  const where = `${contextLabel(context)} find ${column.name}`
905
805
  const entry = column.entry
@@ -907,23 +807,14 @@ function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, colum
907
807
  assertBound(where, bound, entry.over)
908
808
  return
909
809
  }
910
- if (entry.kind === "measure") {
911
- assertBound(where, bound, entry.over)
912
- assertInterval(where, entry.over)
913
- return
914
- }
915
810
  const agg = entry.agg
916
811
  switch (agg.op) {
917
812
  case "count":
918
813
  return
919
814
  case "fold": {
920
- if ("duration" in agg.over) {
921
- assertBound(where, bound, agg.over.duration)
922
- assertInterval(where, agg.over.duration)
923
- return
924
- }
925
815
  assertBound(where, bound, agg.over)
926
816
  assertNotClosed(where, `the ${agg.fold} input`, agg.over)
817
+ assertNumeric(where, `the ${agg.fold} input`, agg.over)
927
818
  return
928
819
  }
929
820
  case "pack":
@@ -933,11 +824,6 @@ function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, colum
933
824
  }
934
825
  }
935
826
 
936
- /**
937
- * Validates one condition's variable references against the rule's bound
938
- * set — and, for `eq`/`ne` over two variables, holds the class wall through
939
- * the mint slots (the unification IS a join; bare pairs only with bare).
940
- */
941
827
  function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: CondData): void {
942
828
  const label = contextLabel(context)
943
829
  if (cond.kind === "cmp") {
@@ -949,10 +835,6 @@ function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: C
949
835
  throw closedOrderError(label, `the ${cond.op.kind} side ${side.ref.label}`, roster.name)
950
836
  }
951
837
  }
952
- if (side.kind === "measure") {
953
- assertBound(label, bound, side.ref)
954
- assertInterval(label, side.ref)
955
- }
956
838
  }
957
839
  if ((cond.op.kind === "eq" || cond.op.kind === "ne") && cond.lhs.kind === "var" && cond.rhs.kind === "var") {
958
840
  assertBound(label, bound, cond.lhs.ref)
@@ -972,16 +854,6 @@ function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: C
972
854
  }
973
855
  }
974
856
 
975
- /**
976
- * Validates one interior item: every head column of the table is bound exactly
977
- * once (a missing or extra key is a pointed error) and each variable joins
978
- * its head column's classed slot. A POSITIVE interior atom GROUNDS its
979
- * variables (a positive occurrence, exactly the engine's representation),
980
- * so no boundness precondition exists; a NEGATED one binds nothing — its
981
- * variables must be positively bound elsewhere in the rule, the same
982
- * safety rule as EDB negation. When the table's own first rule is in flight
983
- * (`finds` empty), the completing rule's OWN find columns ARE the head.
984
- */
985
857
  function validateInterior(
986
858
  context: ChainContext,
987
859
  bound: ReadonlySet<AnyVar>,
@@ -1035,12 +907,6 @@ function validateInterior(
1035
907
  }
1036
908
  }
1037
909
 
1038
- /**
1039
- * Completes one rule: enriches the find columns (declaration-order-safe
1040
- * keys, boundness validated, each column's classed slot and closed slice
1041
- * resolved), then walks the body walls — negated-atom boundness safety, interior
1042
- * head pairing, and condition validation.
1043
- */
1044
910
  function completeRule(context: ChainContext, state: RuleBuildState, rawColumns: readonly FindColumn[]): RuleData {
1045
911
  const label = contextLabel(context)
1046
912
  if (rawColumns.length === 0) {
@@ -1072,15 +938,10 @@ function completeRule(context: ChainContext, state: RuleBuildState, rawColumns:
1072
938
  return Object.freeze({ items: state.items, finds: Object.freeze(columns), paramUses: state.paramUses })
1073
939
  }
1074
940
 
1075
- /** Builds one typed rule value over completed rule data. */
1076
941
  function makeRuleValue<Row, P extends ParamsRecord>(rule: RuleData): RuleValue<Row, P> {
1077
942
  return Object.freeze({ rule })
1078
943
  }
1079
944
 
1080
- /**
1081
- * The one runtime chain every context shares — non-generic on purpose. The
1082
- * typed chain interfaces apply at the scope factories' boundaries.
1083
- */
1084
945
  interface RawChain {
1085
946
  match(relation: MatchOwner, bindings: Readonly<Record<string, unknown>>): RawChain
1086
947
  where(cond: AnyCond): RawChain
@@ -1088,18 +949,15 @@ interface RawChain {
1088
949
  find(entries: Readonly<Record<string, unknown>>): RuleValue<never, never>
1089
950
  }
1090
951
 
1091
- /** The runtime rule-builder shape beneath every typed scope. */
1092
952
  interface RawScope extends TermOps {
1093
953
  match(relation: MatchOwner, bindings: Readonly<Record<string, unknown>>): RawChain
1094
954
  interior(name: string, bindings: Readonly<Record<string, unknown>>): RawChain
1095
955
  }
1096
956
 
1097
- /** The declared derived tables a chain may name. */
1098
957
  type DerivedEnv =
1099
958
  | { readonly interiors: readonly InteriorData[] }
1100
959
  | { readonly interiors: readonly InteriorData[]; readonly rec: RecHandle | RecHead | RecData }
1101
960
 
1102
- /** Which rule family a chain builds — plus the schema's runtime class map and theory value (the join judge's authority). */
1103
961
  type ChainContext = { readonly classes: SchemaClasses; readonly theory: AnySchema } & DerivedEnv &
1104
962
  (
1105
963
  | { readonly kind: "query" }
@@ -1108,7 +966,6 @@ type ChainContext = { readonly classes: SchemaClasses; readonly theory: AnySchem
1108
966
  | { readonly kind: "rec-arm"; readonly self: RecHead }
1109
967
  )
1110
968
 
1111
- /** The diagnostic label of a chain context. */
1112
969
  function contextLabel(context: ChainContext): string {
1113
970
  switch (context.kind) {
1114
971
  case "query":
@@ -1122,12 +979,10 @@ function contextLabel(context: ChainContext): string {
1122
979
  }
1123
980
  }
1124
981
 
1125
- /** RecHandle is name-only staging; RecHead/RecData carry the sealed finds. */
1126
982
  function isRecHead(rec: RecHandle | RecHead | RecData): rec is RecHead {
1127
983
  return Array.isArray((rec as RecHead).finds)
1128
984
  }
1129
985
 
1130
- /** Resolves a derived-table name against the context's visible tables. */
1131
986
  function lookupDerived(context: ChainContext, name: string): DerivedTable {
1132
987
  const interior = context.interiors.find(function byName(candidate) {
1133
988
  return candidate.name === name
@@ -1156,7 +1011,6 @@ function lookupDerived(context: ChainContext, name: string): DerivedTable {
1156
1011
  throw errors.new(`${contextLabel(context)}: no derived table named ${name} is in scope`)
1157
1012
  }
1158
1013
 
1159
- /** Validates and records one interior atom per the context's cut. */
1160
1014
  function interiorAdvance(
1161
1015
  context: ChainContext,
1162
1016
  state: RuleBuildState,
@@ -1166,11 +1020,6 @@ function interiorAdvance(
1166
1020
  return advanceInterior(state, lookupDerived(context, name), bindings, "interior")
1167
1021
  }
1168
1022
 
1169
- /**
1170
- * Validates and records one NEGATED finished-table atom — main and interior
1171
- * rules: a finished set is a set. Rec bodies refuse every negation
1172
- * (`NegationInRec` — self is the wall; EDB / earlier-interior is this-cut).
1173
- */
1174
1023
  function notInteriorAdvance(
1175
1024
  context: ChainContext,
1176
1025
  state: RuleBuildState,
@@ -1185,7 +1034,6 @@ function notInteriorAdvance(
1185
1034
  return advanceInterior(state, lookupDerived(context, name), bindings, "negatedInterior")
1186
1035
  }
1187
1036
 
1188
- /** Classifies one find record per the context (interior and rec heads project bound variables only). */
1189
1037
  function findColumns(context: ChainContext, entries: Readonly<Record<string, unknown>>): FindColumn[] {
1190
1038
  const columns: FindColumn[] = []
1191
1039
  const derivedHead = context.kind !== "query"
@@ -1204,7 +1052,6 @@ function findColumns(context: ChainContext, entries: Readonly<Record<string, unk
1204
1052
  return columns
1205
1053
  }
1206
1054
 
1207
- /** Builds one runtime chain (immutably — every step is a fresh chain over fresh state). */
1208
1055
  function makeRawChain(context: ChainContext, state: RuleBuildState): RawChain {
1209
1056
  const chain: RawChain = {
1210
1057
  match(relation, bindings) {
@@ -1224,7 +1071,6 @@ function makeRawChain(context: ChainContext, state: RuleBuildState): RawChain {
1224
1071
  return chain
1225
1072
  }
1226
1073
 
1227
- /** Builds one runtime rule-builder over a context. */
1228
1074
  function makeRawScope(context: ChainContext): RawScope {
1229
1075
  const scope: RawScope = {
1230
1076
  ...termOps,
@@ -1252,7 +1098,6 @@ function isTypedScope<S>(scope: RawScope): scope is RawScope & S {
1252
1098
  return typeof scope.match === "function"
1253
1099
  }
1254
1100
 
1255
- /** Builds one query-rule builder (the typed face of the raw builder). */
1256
1101
  function makeQueryRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1257
1102
  theory: AnySchema,
1258
1103
  env: DerivedEnv
@@ -1264,7 +1109,6 @@ function makeQueryRuleScope<Rels extends SchemaRelations, Classes extends Schema
1264
1109
  return raw
1265
1110
  }
1266
1111
 
1267
- /** Builds one interior-rule builder. */
1268
1112
  function makeInteriorRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1269
1113
  theory: AnySchema,
1270
1114
  env: DerivedEnv,
@@ -1277,7 +1121,6 @@ function makeInteriorRuleScope<Rels extends SchemaRelations, Classes extends Sch
1277
1121
  return raw
1278
1122
  }
1279
1123
 
1280
- /** Builds one rec-arm builder. */
1281
1124
  function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1282
1125
  theory: AnySchema,
1283
1126
  env: DerivedEnv,
@@ -1318,15 +1161,13 @@ function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaCl
1318
1161
  return raw
1319
1162
  }
1320
1163
 
1321
- /** Renders one head column's closed slice for the rule-alignment check's diagnostics. */
1322
1164
  function renderClosedSlice(closed: ClosedRoster | undefined): string {
1323
1165
  return closed === undefined ? "a bare value" : `a ${closed.name} reference`
1324
1166
  }
1325
1167
 
1326
- /** Renders one head column's signature for the rule-alignment check. */
1327
1168
  function headSignature(column: FindColumn): string {
1328
1169
  const entry = column.entry
1329
- if (entry.kind === "var" || entry.kind === "measure") {
1170
+ if (entry.kind === "var") {
1330
1171
  return `${column.name}:var`
1331
1172
  }
1332
1173
  const agg = entry.agg
@@ -1336,12 +1177,6 @@ function headSignature(column: FindColumn): string {
1336
1177
  return `${column.name}:${agg.op}`
1337
1178
  }
1338
1179
 
1339
- /** The roster a param anchor carries: present exactly on a closed-reference field anchor. */
1340
- function anchorRosterOf(anchor: AnyField | "measure" | undefined): ClosedRoster | undefined {
1341
- return anchor === "measure" ? undefined : rosterOf(anchor)
1342
- }
1343
-
1344
- /** Renders one param anchor's closedness for the registry's coherence diagnostics. */
1345
1180
  function renderParamAnchor(roster: ClosedRoster | undefined): string {
1346
1181
  return roster === undefined ? "a non-closed position" : `a ${roster.name} reference`
1347
1182
  }
@@ -1397,8 +1232,8 @@ function paramRegistryOf(
1397
1232
  )
1398
1233
  }
1399
1234
  if (existing.anchor !== undefined && use.anchor !== undefined) {
1400
- const registered = anchorRosterOf(existing.anchor)
1401
- const anchored = anchorRosterOf(use.anchor)
1235
+ const registered = rosterOf(existing.anchor)
1236
+ const anchored = rosterOf(use.anchor)
1402
1237
  if (registered !== anchored) {
1403
1238
  throw errors.new(
1404
1239
  `query param ${use.name} is anchored at ${renderParamAnchor(registered)} and at ${renderParamAnchor(anchored)} — a closed-anchored param translates handle names through ONE roster (one name, one domain); name the params differently`
@@ -1433,14 +1268,7 @@ function paramRegistryOf(
1433
1268
  if (entry === undefined) {
1434
1269
  throw errors.new(`query param ${name} lost its registry entry`)
1435
1270
  }
1436
- /**
1437
- * A membership array's handle names are program constants, so the
1438
- * entry stores the resolved IMAGE: each name rides the one
1439
- * roster-verification point (`taggedHandleId`, through
1440
- * `taggedCmpLiteral`) exactly once, HERE — an out-of-roster name
1441
- * fails at build, and every execute returns this frozen value by
1442
- * reference.
1443
- */
1271
+
1444
1272
  let membership: QueryParam | undefined
1445
1273
  if (entry.members !== undefined) {
1446
1274
  const anchor = entry.anchor
@@ -1461,7 +1289,6 @@ function paramRegistryOf(
1461
1289
  )
1462
1290
  }
1463
1291
 
1464
- /** The runtime query shape beneath the typed `Query` face. */
1465
1292
  interface RawQuery {
1466
1293
  readonly schema: AnySchema
1467
1294
  readonly data: QueryData
@@ -1470,7 +1297,6 @@ interface RawQuery {
1470
1297
  reach(name: string, arms: never): never
1471
1298
  }
1472
1299
 
1473
- /** Asserts every rule in a list derives the same head (name, aggregate shape, closed slice, class). */
1474
1300
  function assertAlignedHeads(label: string, rules: readonly RuleData[]): void {
1475
1301
  const first = rules[0]
1476
1302
  if (first === undefined) {
@@ -1509,13 +1335,6 @@ function afterMainError(what: string): Error {
1509
1335
  )
1510
1336
  }
1511
1337
 
1512
- /**
1513
- * Assembles the runtime query value over completed rules: every rule must
1514
- * derive the SAME head (name and aggregate shape, position for position —
1515
- * the decode labels and the engine's alignment rule agree), and the param
1516
- * registry folds in query-walk order. CQ lowering does not mention rec;
1517
- * Reach carries RecData by value.
1518
- */
1519
1338
  function makeRawQuery(
1520
1339
  theory: AnySchema,
1521
1340
  interiors: readonly InteriorData[],
@@ -1575,7 +1394,6 @@ function makeQuery<Rels extends SchemaRelations, Row, P extends ParamsRecord, Cl
1575
1394
  return makeRawQuery(theory, interiors, rec, rules) as unknown as Query<Rels, Row, P, Classes>
1576
1395
  }
1577
1396
 
1578
- /** Collects one Interior from its builders. */
1579
1397
  function collectInterior<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1580
1398
  theory: Schema<Rels, Classes>,
1581
1399
  env: DerivedEnv,
@@ -1596,7 +1414,6 @@ function collectInterior<Rels extends SchemaRelations, Classes extends SchemaCla
1596
1414
  return Object.freeze({ name, finds: first.finds, rules: Object.freeze(rules) })
1597
1415
  }
1598
1416
 
1599
- /** Collects the Rec from tagged base/rec builder arrays. */
1600
1417
  function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1601
1418
  theory: Schema<Rels, Classes>,
1602
1419
  interiors: readonly InteriorData[],
@@ -1646,7 +1463,6 @@ function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>
1646
1463
  return recData
1647
1464
  }
1648
1465
 
1649
- /** Builds the CQ query start (interiors, then reach or the first main rule). */
1650
1466
  function makeQueryStart<Rels extends SchemaRelations, Classes extends SchemaClasses, P extends ParamsRecord>(
1651
1467
  theory: Schema<Rels, Classes>,
1652
1468
  interiors: readonly InteriorData[]
@@ -1702,7 +1518,6 @@ function makeQueryStart<Rels extends SchemaRelations, Classes extends SchemaClas
1702
1518
  return start as unknown as QueryStart<Rels, Classes, P>
1703
1519
  }
1704
1520
 
1705
- /** Builds the Reach query start (rec sealed; only the first main rule remains). */
1706
1521
  function makeQueryReachStart<Rels extends SchemaRelations, Classes extends SchemaClasses, P extends ParamsRecord>(
1707
1522
  theory: Schema<Rels, Classes>,
1708
1523
  interiors: readonly InteriorData[],
@@ -1721,24 +1536,12 @@ function makeQueryReachStart<Rels extends SchemaRelations, Classes extends Schem
1721
1536
  return start as unknown as QueryReachStart<Rels, Classes, P>
1722
1537
  }
1723
1538
 
1724
- /**
1725
- * Opens a query over a schema: `query(S).rule(r => ...)`, optionally with
1726
- * `interior` / `reach` first. Each `.rule` adds one conjunctive rule;
1727
- * multiple rules are the set union. The schema's law-computed class map and
1728
- * theory value ride into every rule builder — the join walls compare
1729
- * against the mint slots off it.
1730
- */
1731
1539
  function query<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1732
1540
  theory: Schema<Rels, Classes>
1733
1541
  ): QueryStart<Rels, Classes> {
1734
1542
  return makeQueryStart<Rels, Classes, Record<never, never>>(theory, [])
1735
1543
  }
1736
1544
 
1737
- /**
1738
- * Tags one closed-reference literal: the handle NAME, verified against the
1739
- * roster and translated to its declaration-order row id, tagged u64. THE
1740
- * single roster-verification point of the query surface.
1741
- */
1742
1545
  function taggedHandleId(
1743
1546
  context: string,
1744
1547
  closed: { readonly name: string; readonly handles: readonly string[] },
@@ -1756,11 +1559,6 @@ function taggedHandleId(
1756
1559
  return { kind: "u64", value: BigInt(id) }
1757
1560
  }
1758
1561
 
1759
- /**
1760
- * Tags one literal in an interval element domain: a bigint tags as the
1761
- * element (the membership typing rule's point side), an interval-shaped
1762
- * value as the interval (value equality).
1763
- */
1764
1562
  function taggedAtElementDomain(context: string, element: "u64" | "i64", value: unknown): TaggedValue {
1765
1563
  if (typeof value === "bigint") {
1766
1564
  if (element === "u64") {
@@ -1777,10 +1575,6 @@ function taggedAtElementDomain(context: string, element: "u64" | "i64", value: u
1777
1575
  throw literalShapeError(context, "bigint (point) or { start, end } (interval)", value)
1778
1576
  }
1779
1577
 
1780
- /**
1781
- * Tags one host literal at a FIELD position (atom bindings): the field's
1782
- * structural kind directs the tag, never a guess.
1783
- */
1784
1578
  function taggedLiteral(context: string, field: AnyField, value: unknown): TaggedValue {
1785
1579
  const roster = rosterOf(field)
1786
1580
  if (roster !== undefined) {
@@ -1834,18 +1628,7 @@ function taggedLiteral(context: string, field: AnyField, value: unknown): Tagged
1834
1628
  * interval operand of `pointIn(t, span(...))`; under every other operator an
1835
1629
  * interval shape against a scalar sibling stays refused.
1836
1630
  */
1837
- function taggedCmpLiteral(
1838
- context: string,
1839
- sibling: AnyField | "measure",
1840
- value: unknown,
1841
- op: CmpKind | "binding"
1842
- ): TaggedValue {
1843
- if (sibling === "measure") {
1844
- if (typeof value !== "bigint") {
1845
- throw literalShapeError(context, "bigint (the measure is u64)", value)
1846
- }
1847
- return { kind: "u64", value }
1848
- }
1631
+ function taggedCmpLiteral(context: string, sibling: AnyField, value: unknown, op: CmpKind | "binding"): TaggedValue {
1849
1632
  if (rosterOf(sibling) === undefined && sibling.kind === "interval") {
1850
1633
  return taggedAtElementDomain(context, sibling.element, value)
1851
1634
  }
@@ -1860,7 +1643,6 @@ function taggedCmpLiteral(
1860
1643
  return taggedLiteral(context, sibling, value)
1861
1644
  }
1862
1645
 
1863
- /** The shared lowering context of one `lowerQuery` run. */
1864
1646
  interface LowerContext {
1865
1647
  readonly theory: AnySchema
1866
1648
  readonly relationIds: ReadonlyMap<string, number>
@@ -1869,12 +1651,10 @@ interface LowerContext {
1869
1651
  readonly params: ReadonlyMap<string, ParamEntry>
1870
1652
  }
1871
1653
 
1872
- /** One rule's dense variable numbering: first occurrence in written order, keyed on the object REFERENCE. */
1873
1654
  interface VarIds {
1874
1655
  of(ref: AnyVar): number
1875
1656
  }
1876
1657
 
1877
- /** Creates one rule-scoped variable numberer. */
1878
1658
  function freshVarIds(): VarIds {
1879
1659
  const assigned = new Map<AnyVar, number>()
1880
1660
  return {
@@ -1890,7 +1670,6 @@ function freshVarIds(): VarIds {
1890
1670
  }
1891
1671
  }
1892
1672
 
1893
- /** Resolves a param name to its dense positional id. */
1894
1673
  function paramIdOf(ctx: LowerContext, name: string): number {
1895
1674
  const id = ctx.paramIds.get(name)
1896
1675
  if (id === undefined) {
@@ -1899,10 +1678,6 @@ function paramIdOf(ctx: LowerContext, name: string): number {
1899
1678
  return id
1900
1679
  }
1901
1680
 
1902
- /**
1903
- * Lowers one EDB atom (either polarity). A CLOSED owner lowers through the
1904
- * same edb source, with field ordinals over the SEALED shape.
1905
- */
1906
1681
  function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIr {
1907
1682
  const member = ctx.theory.relations[atom.relation.name]
1908
1683
  if (member !== atom.relation) {
@@ -1927,7 +1702,6 @@ function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIr {
1927
1702
  return { source: { kind: "edb", relation: relationId }, bindings }
1928
1703
  }
1929
1704
 
1930
- /** Lowers one binding term. A membership ARRAY lowers to the existing param-set term over its content-addressed entry. */
1931
1705
  function lowerBindingTerm(ctx: LowerContext, context: string, binding: BindingEntry, ids: VarIds): TermIr {
1932
1706
  const bound = binding.term
1933
1707
  switch (bound.kind) {
@@ -1975,7 +1749,6 @@ function lowerInteriorAtom(
1975
1749
  return { source: { kind: "interior", interior }, bindings: irBindings }
1976
1750
  }
1977
1751
 
1978
- /** Lowers one comparison side; literals tag by the sibling's anchor (op-aware at `pointIn`). */
1979
1752
  function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData, ids: VarIds, op: CmpKind): TermIr {
1980
1753
  switch (side.kind) {
1981
1754
  case "var":
@@ -1984,13 +1757,11 @@ function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData
1984
1757
  return { kind: "param", param: paramIdOf(ctx, side.name) }
1985
1758
  case "setParam":
1986
1759
  return { kind: "paramSet", param: paramIdOf(ctx, side.name) }
1987
- case "measure":
1988
- return { kind: "measure", var: ids.of(side.ref) }
1989
1760
  case "literal": {
1990
1761
  const anchor = cmpAnchorOf(ctx, sibling)
1991
1762
  if (anchor === undefined) {
1992
1763
  throw errors.new(
1993
- "query lowering: a comparison literal needs a bound-variable, measure, or anchored-param sibling to type it"
1764
+ "query lowering: a comparison literal needs a bound-variable or anchored-param sibling to type it"
1994
1765
  )
1995
1766
  }
1996
1767
  return { kind: "literal", value: taggedCmpLiteral("comparison literal", anchor, side.value, op) }
@@ -1998,21 +1769,16 @@ function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData
1998
1769
  }
1999
1770
  }
2000
1771
 
2001
- /** Resolves the anchor a comparison literal tags by: the sibling variable's field, the measure, or an anchored param. */
2002
- function cmpAnchorOf(ctx: LowerContext, sibling: CmpTermData): AnyField | "measure" | undefined {
1772
+ function cmpAnchorOf(ctx: LowerContext, sibling: CmpTermData): AnyField | undefined {
2003
1773
  if (sibling.kind === "var") {
2004
1774
  return sibling.ref.field
2005
1775
  }
2006
- if (sibling.kind === "measure") {
2007
- return "measure"
2008
- }
2009
1776
  if (sibling.kind === "param" || sibling.kind === "setParam") {
2010
1777
  return ctx.params.get(sibling.name)?.anchor
2011
1778
  }
2012
1779
  return undefined
2013
1780
  }
2014
1781
 
2015
- /** Lowers one comparison. */
2016
1782
  function lowerComparison(ctx: LowerContext, cmp: CmpData, ids: VarIds): ComparisonIr {
2017
1783
  if (cmp.op.kind === "allen") {
2018
1784
  return {
@@ -2028,7 +1794,6 @@ function lowerComparison(ctx: LowerContext, cmp: CmpData, ids: VarIds): Comparis
2028
1794
  }
2029
1795
  }
2030
1796
 
2031
- /** Lowers one condition node (comparison leaf or and/or tree). */
2032
1797
  function lowerCondition(ctx: LowerContext, cond: CondData, ids: VarIds): ConditionTreeIr {
2033
1798
  if (cond.kind === "cmp") {
2034
1799
  return { kind: "leaf", cmp: lowerComparison(ctx, cond, ids) }
@@ -2041,30 +1806,21 @@ function lowerCondition(ctx: LowerContext, cond: CondData, ids: VarIds): Conditi
2041
1806
  }
2042
1807
  }
2043
1808
 
2044
- /** Lowers one find entry to its per-rule find term. */
2045
1809
  function lowerFind(entry: FindEntryData, ids: VarIds): FindTermIr {
2046
1810
  if (entry.kind === "var") {
2047
1811
  return { kind: "var", var: ids.of(entry.over) }
2048
1812
  }
2049
- if (entry.kind === "measure") {
2050
- return { kind: "measure", var: ids.of(entry.over) }
2051
- }
2052
1813
  const agg = entry.agg
2053
1814
  switch (agg.op) {
2054
1815
  case "count":
2055
1816
  return { kind: "count" }
2056
- case "fold": {
2057
- if ("duration" in agg.over) {
2058
- return { kind: "aggregateMeasure", op: { kind: agg.fold }, over: ids.of(agg.over.duration) }
2059
- }
1817
+ case "fold":
2060
1818
  return { kind: "aggregate", op: { kind: agg.fold }, over: ids.of(agg.over) }
2061
- }
2062
1819
  case "pack":
2063
1820
  return { kind: "pack", over: ids.of(agg.over) }
2064
1821
  }
2065
1822
  }
2066
1823
 
2067
- /** One aggregate's var-free head-op kind (`AggOp::head_op`). */
2068
1824
  function headOpOf(agg: AggData): HeadOpIr {
2069
1825
  switch (agg.op) {
2070
1826
  case "count":
@@ -2076,16 +1832,14 @@ function headOpOf(agg: AggData): HeadOpIr {
2076
1832
  }
2077
1833
  }
2078
1834
 
2079
- /** One find entry's var-free head shape. */
2080
1835
  function headTermOf(column: FindColumn): HeadTermIr {
2081
1836
  const entry = column.entry
2082
- if (entry.kind === "var" || entry.kind === "measure") {
1837
+ if (entry.kind === "var") {
2083
1838
  return { kind: "var" }
2084
1839
  }
2085
1840
  return { kind: "aggregate", op: headOpOf(entry.agg) }
2086
1841
  }
2087
1842
 
2088
- /** Lowers one rule: body walked in written order (var ids by first occurrence), finds last. */
2089
1843
  function lowerRule(ctx: LowerContext, rule: RuleData): RuleIr {
2090
1844
  const ids = freshVarIds()
2091
1845
  const atoms: AtomIr[] = []
@@ -2125,11 +1879,6 @@ function lowerRule(ctx: LowerContext, rule: RuleData): RuleIr {
2125
1879
  }
2126
1880
  }
2127
1881
 
2128
- /**
2129
- * Lowers a query value to the bridge's `QueryIr` — pure and stable:
2130
- * interiors in declaration order, then CQ or Reach, then main. Every
2131
- * registered param must carry a field anchor by now.
2132
- */
2133
1882
  function lowerQuery(q: AnyQuery): ParsedQuery {
2134
1883
  const theory = q.schema
2135
1884
  const relationIds = new Map<string, number>()