@bjornpagen/bumbledb 0.15.0 → 0.17.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 (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 +11 -15
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +9 -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 +25 -290
  40. package/dist/native.d.ts.map +1 -1
  41. package/dist/native.js +6 -66
  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 +64 -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 +9 -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 +47 -323
  94. package/src/query/atom.ts +26 -313
  95. package/src/query/find.ts +24 -110
  96. package/src/query/lower.ts +132 -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,85 @@ 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 (proposals/one-representation/50-generic-binding.md, "The
130
+ * ruling"); an all-var record contributes no params, so the chain starts
131
+ * paramless. {@link ExactVars} maps a foreign key to `never`, so an
132
+ * aliased extra-key record falls to the general form's judgment.
133
+ */
134
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
135
+ relation: R,
136
+ bindings: B & ExactVars<R, B>
137
+ ): QueryRuleChain<Rels, Record<never, never>, Classes>
138
+
195
139
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
196
140
  relation: R,
197
141
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
198
142
  ): 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
- */
143
+
205
144
  interior<const B extends Readonly<Record<string, AnyVar>>>(
206
145
  name: string,
207
146
  bindings: B & CheckInteriorBindings<B>
208
147
  ): QueryRuleChain<Rels, Record<never, never>, Classes>
209
148
  }
210
149
 
211
- /** The chain of a plain query rule: more atoms, residual predicates, then the head. */
212
150
  interface QueryRuleChain<
213
151
  Rels extends SchemaRelations,
214
152
  P extends ParamsRecord,
215
153
  Classes extends SchemaClasses = SchemaClasses
216
154
  > {
217
- /** One more positive EDB atom — variable reuse joins, class-equal by the mint-slot judgment. */
155
+ /**
156
+ * The FULL binding: every column of R bound to its own v(R) mint — the
157
+ * identity atom, generic R included. The mint invariant (a variable's mint
158
+ * slot IS its position slot) discharges the join judgment by construction
159
+ * (proposals/one-representation/50-generic-binding.md, "The ruling"); an
160
+ * all-var record contributes no params — P rides through unchanged.
161
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
162
+ * extra-key record falls to the general form's judgment.
163
+ */
164
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
165
+ relation: R,
166
+ bindings: B & ExactVars<R, B>
167
+ ): QueryRuleChain<Rels, P, Classes>
168
+
218
169
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
219
170
  relation: R,
220
171
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
221
172
  ): 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`). */
173
+
223
174
  where<const C extends AnyCond>(
224
175
  cond: CheckCond<Classes, C> & C
225
176
  ): QueryRuleChain<Rels, Flatten<P & CondParamsShape<C>>, Classes>
226
- /** One interior atom over a finished table (an earlier interior, or the rec). */
177
+
227
178
  interior<const B extends Readonly<Record<string, AnyVar>>>(
228
179
  name: string,
229
180
  bindings: B & CheckInteriorBindings<B>
230
181
  ): QueryRuleChain<Rels, P, Classes>
231
- /** The head projection: a `find` RECORD whose keys name the answer columns. */
182
+
232
183
  find<const F extends FindShape>(entries: F & CheckFind<F>): RuleValue<RowOfFind<F>, P>
233
184
  }
234
185
 
235
- /** The rule builder an `interior("mid", ...)` callback receives. */
236
186
  interface InteriorRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses>
237
187
  extends TermOps {
188
+ /**
189
+ * The FULL binding: every column of R bound to its own v(R) mint — the
190
+ * identity atom, generic R included. The mint invariant (a variable's mint
191
+ * slot IS its position slot) discharges the join judgment by construction
192
+ * (proposals/one-representation/50-generic-binding.md, "The ruling"); an
193
+ * all-var record contributes no params, so the chain starts paramless.
194
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
195
+ * extra-key record falls to the general form's judgment.
196
+ */
197
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
198
+ relation: R,
199
+ bindings: B & ExactVars<R, B>
200
+ ): InteriorRuleChain<Rels, Record<never, never>, Classes>
238
201
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
239
202
  relation: R,
240
203
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
@@ -245,12 +208,24 @@ interface InteriorRuleScope<Rels extends SchemaRelations, Classes extends Schema
245
208
  ): InteriorRuleChain<Rels, Record<never, never>, Classes>
246
209
  }
247
210
 
248
- /** The chain of an interior rule: bound-variable heads only. */
249
211
  interface InteriorRuleChain<
250
212
  Rels extends SchemaRelations,
251
213
  P extends ParamsRecord,
252
214
  Classes extends SchemaClasses = SchemaClasses
253
215
  > {
216
+ /**
217
+ * The FULL binding: every column of R bound to its own v(R) mint — the
218
+ * identity atom, generic R included. The mint invariant (a variable's mint
219
+ * slot IS its position slot) discharges the join judgment by construction
220
+ * (proposals/one-representation/50-generic-binding.md, "The ruling"); an
221
+ * all-var record contributes no params — P rides through unchanged.
222
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
223
+ * extra-key record falls to the general form's judgment.
224
+ */
225
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
226
+ relation: R,
227
+ bindings: B & ExactVars<R, B>
228
+ ): InteriorRuleChain<Rels, P, Classes>
254
229
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
255
230
  relation: R,
256
231
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
@@ -265,8 +240,20 @@ interface InteriorRuleChain<
265
240
  find<const F extends FindShape>(entries: F & CheckRecFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
266
241
  }
267
242
 
268
- /** The rule builder a `.reach("reach", { base, rec })` arm receives. */
269
243
  interface RecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> extends TermOps {
244
+ /**
245
+ * The FULL binding: every column of R bound to its own v(R) mint — the
246
+ * identity atom, generic R included. The mint invariant (a variable's mint
247
+ * slot IS its position slot) discharges the join judgment by construction
248
+ * (proposals/one-representation/50-generic-binding.md, "The ruling"); an
249
+ * all-var record contributes no params, so the chain starts paramless.
250
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
251
+ * extra-key record falls to the general form's judgment.
252
+ */
253
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
254
+ relation: R,
255
+ bindings: B & ExactVars<R, B>
256
+ ): RecRuleChain<Rels, Record<never, never>, Classes>
270
257
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
271
258
  relation: R,
272
259
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
@@ -277,17 +264,24 @@ interface RecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClass
277
264
  ): RecRuleChain<Rels, Record<never, never>, Classes>
278
265
  }
279
266
 
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
267
  interface RecRuleChain<
287
268
  Rels extends SchemaRelations,
288
269
  P extends ParamsRecord,
289
270
  Classes extends SchemaClasses = SchemaClasses
290
271
  > {
272
+ /**
273
+ * The FULL binding: every column of R bound to its own v(R) mint — the
274
+ * identity atom, generic R included. The mint invariant (a variable's mint
275
+ * slot IS its position slot) discharges the join judgment by construction
276
+ * (proposals/one-representation/50-generic-binding.md, "The ruling"); an
277
+ * all-var record contributes no params — P rides through unchanged.
278
+ * {@link ExactVars} maps a foreign key to `never`, so an aliased
279
+ * extra-key record falls to the general form's judgment.
280
+ */
281
+ match<R extends QueryRelation<Rels>, B extends VarsOf<R>>(
282
+ relation: R,
283
+ bindings: B & ExactVars<R, B>
284
+ ): RecRuleChain<Rels, P, Classes>
291
285
  match<R extends QueryRelation<Rels>, const B extends MatchShape<MatchFields<R>>>(
292
286
  relation: R,
293
287
  bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
@@ -302,38 +296,32 @@ interface RecRuleChain<
302
296
  find<const F extends FindShape>(entries: F & CheckRecFind<F>): RuleValue<RowOfFind<F>, P, HeadRecordOf<Classes, F>>
303
297
  }
304
298
 
305
- /** A query's runtime description — everything lowering, the wire marshal, and answer decode read. */
306
299
  type QueryData =
307
300
  | {
308
301
  readonly kind: "cq"
309
- /** Named interiors in declaration order (DAG). */
302
+
310
303
  readonly interiors: readonly InteriorData[]
311
- /** The main rules in written order (multiple rules = set union). */
304
+
312
305
  readonly rules: readonly RuleData[]
313
- /** The head columns (every rule derives the same head; written order = answer column order). */
306
+
314
307
  readonly finds: readonly FindColumn[]
315
- /** The registered params in first-use order across the query walk (= dense `ParamId`s). */
308
+
316
309
  readonly params: readonly ParamEntry[]
317
310
  }
318
311
  | {
319
312
  readonly kind: "reach"
320
- /** Named interiors in declaration order (DAG). */
313
+
321
314
  readonly interiors: readonly InteriorData[]
322
- /** The linear rec (base and rec arms nonempty by type). */
315
+
323
316
  readonly rec: RecData
324
- /** The main rules in written order (multiple rules = set union). */
317
+
325
318
  readonly rules: readonly RuleData[]
326
- /** The head columns (every rule derives the same head; written order = answer column order). */
319
+
327
320
  readonly finds: readonly FindColumn[]
328
- /** The registered params in first-use order across the query walk (= dense `ParamId`s). */
321
+
329
322
  readonly params: readonly ParamEntry[]
330
323
  }
331
324
 
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
325
  interface Query<
338
326
  Rels extends SchemaRelations,
339
327
  Row,
@@ -342,34 +330,26 @@ interface Query<
342
330
  > {
343
331
  readonly schema: Schema<Rels, Classes>
344
332
  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. */
333
+
346
334
  rule<RV extends AnyRuleValue>(
347
335
  build: (r: QueryRuleScope<Rels, Classes>) => RV
348
336
  ): Query<Rels, Row | RowOf<RV>, Flatten<Params & ParamsOf<RV>>, Classes>
349
- /** Construction error: interiors precede main rules. Uncallable after `.rule()`. */
337
+
350
338
  interior(name: string, ...builds: never[]): never
351
- /** Construction error: reach precedes main rules. Uncallable after `.rule()`. */
339
+
352
340
  reach(name: string, arms: never): never
353
341
  readonly [inferred]?: { readonly row: Row; readonly params: Params }
354
342
  }
355
343
 
356
- /** Any query value as lowering and the runtime consume it. */
357
344
  interface AnyQuery {
358
345
  readonly schema: AnySchema
359
346
  readonly data: QueryData
360
347
  }
361
348
 
362
- /** Extracts a query value's inferred answer-row type. */
363
349
  type QueryRow<Q extends AnyQuery> = RowOf<Q>
364
350
 
365
- /** Extracts a query value's inferred execute-params type. */
366
351
  type QueryParams<Q extends AnyQuery> = ParamsOf<Q>
367
352
 
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
353
  type QueryStart<
374
354
  Rels extends SchemaRelations,
375
355
  Classes extends SchemaClasses = SchemaClasses,
@@ -388,7 +368,6 @@ type QueryStart<
388
368
  ): QueryReachStart<Rels, Classes, Flatten<P & BuildsParams<Base> & BuildsParams<Step>>>
389
369
  }
390
370
 
391
- /** After `.reach()`: interior/reach are unrepresentable; only `.rule` remains. */
392
371
  type QueryReachStart<
393
372
  Rels extends SchemaRelations,
394
373
  Classes extends SchemaClasses = SchemaClasses,
@@ -399,11 +378,9 @@ type QueryReachStart<
399
378
  ): Query<Rels, RowOf<RV>, Flatten<P & ParamsOf<RV>>, Classes>
400
379
  }
401
380
 
402
- /** The frozen constructor vocabulary every rule builder spreads. */
403
381
  const termOps: TermOps = Object.freeze({
404
382
  param: makeParam,
405
383
  inSet: makeSetParam,
406
- duration: makeDuration,
407
384
  eq,
408
385
  ne,
409
386
  lt,
@@ -422,21 +399,18 @@ const termOps: TermOps = Object.freeze({
422
399
  pack
423
400
  })
424
401
 
425
- /** One rule under construction: immutable — every chain step is a fresh state. Boundness rides the `bound` set of var references. */
426
402
  interface RuleBuildState {
427
403
  readonly items: readonly RuleItem[]
428
404
  readonly bound: ReadonlySet<AnyVar>
429
405
  readonly paramUses: readonly ParamUse[]
430
406
  }
431
407
 
432
- /** The empty rule state. */
433
408
  const EMPTY_RULE: RuleBuildState = Object.freeze({
434
409
  items: Object.freeze([]),
435
410
  bound: new Set<AnyVar>(),
436
411
  paramUses: Object.freeze([])
437
412
  })
438
413
 
439
- /** One resolved bindings record: the atom entries, the variable references it binds, and the params it uses. */
440
414
  interface ResolvedBindings {
441
415
  readonly atom: AtomData
442
416
  readonly vars: readonly AnyVar[]
@@ -461,11 +435,6 @@ function mintSlotOf(context: ChainContext, ref: AnyVar): ClassedField {
461
435
  return { field: ref.field, class: context.classes[ref.owner.name]?.[ref.column] }
462
436
  }
463
437
 
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
438
  function membershipSet(
470
439
  context: string,
471
440
  field: AnyField,
@@ -502,14 +471,6 @@ function membershipSet(
502
471
  return { name: `∈ ${roster.name} ${JSON.stringify(key)}`, members: Object.freeze(members) }
503
472
  }
504
473
 
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
474
  function resolveBindings(
514
475
  context: ChainContext,
515
476
  label: string,
@@ -574,10 +535,6 @@ function resolveBindings(
574
535
  )
575
536
  break
576
537
  }
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
538
  }
582
539
  } else if (Array.isArray(value)) {
583
540
  const set = membershipSet(`${label}.${fieldName}`, declared.field, value)
@@ -599,7 +556,6 @@ function resolveBindings(
599
556
  return { atom: Object.freeze({ relation, bindings: Object.freeze(entries) }), vars, uses }
600
557
  }
601
558
 
602
- /** Extends a rule state with one positive atom; the bound variable references accumulate into the boundness set. */
603
559
  function advanceMatch(
604
560
  context: ChainContext,
605
561
  state: RuleBuildState,
@@ -618,7 +574,6 @@ function advanceMatch(
618
574
  })
619
575
  }
620
576
 
621
- /** Resolves one comparison side to its runtime term (variables and the measure ride by reference). */
622
577
  function cmpTermDataOf(value: unknown): CmpTermData {
623
578
  if (isTerm(value)) {
624
579
  switch (value[term]) {
@@ -628,30 +583,16 @@ function cmpTermDataOf(value: unknown): CmpTermData {
628
583
  return Object.freeze({ kind: "param" as const, name: value.name })
629
584
  case "setParam":
630
585
  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
586
  }
634
587
  }
635
588
  return Object.freeze({ kind: "literal" as const, value })
636
589
  }
637
590
 
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
591
  function sideUses(op: CmpKind, side: CmpTermData, sibling: CmpTermData, uses: ParamUse[]): void {
644
592
  if (side.kind !== "param" && side.kind !== "setParam") {
645
593
  return
646
594
  }
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
- }
595
+ const anchor = sibling.kind === "var" ? sibling.ref.field : undefined
655
596
  uses.push(
656
597
  Object.freeze({
657
598
  name: side.name,
@@ -663,7 +604,6 @@ function sideUses(op: CmpKind, side: CmpTermData, sibling: CmpTermData, uses: Pa
663
604
  )
664
605
  }
665
606
 
666
- /** Lowers one condition VALUE to its runtime data, recording param uses. */
667
607
  function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
668
608
  if (cond.cond === "cmp") {
669
609
  const lhs = cmpTermDataOf(cond.lhs)
@@ -695,7 +635,6 @@ function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
695
635
  )
696
636
  }
697
637
 
698
- /** Extends a rule state with one `.where` item (a condition or a negated atom). */
699
638
  function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCond): RuleBuildState {
700
639
  if (typeof cond !== "object" || cond === null || !("cond" in cond)) {
701
640
  throw errors.new("where() takes a comparison, an and()/or() tree, or a negated atom")
@@ -731,15 +670,6 @@ function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCon
731
670
  })
732
671
  }
733
672
 
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
673
  function advanceInterior(
744
674
  state: RuleBuildState,
745
675
  target: DerivedTable,
@@ -771,7 +701,6 @@ function advanceInterior(
771
701
  })
772
702
  }
773
703
 
774
- /** Narrows a find entry to an aggregate value. */
775
704
  function isAggregateEntry(value: unknown): value is { readonly agg: string; readonly over?: unknown } {
776
705
  return typeof value === "object" && value !== null && "agg" in value
777
706
  }
@@ -784,7 +713,6 @@ function asVarTerm(context: string, value: unknown): AnyVar {
784
713
  throw errors.new(`${context}: expected a variable`)
785
714
  }
786
715
 
787
- /** Classifies one aggregate find entry into its runtime data (variables ride by reference). */
788
716
  function aggDataOf(name: string, entry: { readonly agg: string; readonly over?: unknown }): AggData {
789
717
  if (entry.agg === "count") {
790
718
  return Object.freeze({ op: "count" as const })
@@ -797,10 +725,7 @@ function aggDataOf(name: string, entry: { readonly agg: string; readonly over?:
797
725
  if (isTerm(over) && over[term] === "var") {
798
726
  return Object.freeze({ op: "fold" as const, fold: entry.agg, over })
799
727
  }
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)`)
728
+ throw errors.new(`find ${name} (${entry.agg}): takes a variable`)
804
729
  }
805
730
  case "pack":
806
731
  return Object.freeze({ op: "pack" as const, over: asVarTerm(`find ${name} (pack)`, over) })
@@ -809,11 +734,6 @@ function aggDataOf(name: string, entry: { readonly agg: string; readonly over?:
809
734
  }
810
735
  }
811
736
 
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
737
  function findColumnOf(name: string, entry: unknown): FindColumn {
818
738
  if (isTerm(entry)) {
819
739
  if (entry[term] === "var") {
@@ -824,17 +744,7 @@ function findColumnOf(name: string, entry: unknown): FindColumn {
824
744
  slot: undefined
825
745
  })
826
746
  }
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
- )
747
+ throw errors.new(`find ${name}: a ${entry[term]} is not projectable — find takes variables or aggregates`)
838
748
  }
839
749
  if (isAggregateEntry(entry)) {
840
750
  return Object.freeze({
@@ -844,32 +754,29 @@ function findColumnOf(name: string, entry: unknown): FindColumn {
844
754
  slot: undefined
845
755
  })
846
756
  }
847
- throw errors.new(`find ${name}: not a find entry — find takes variables, r.duration(v), or aggregates`)
757
+ throw errors.new(`find ${name}: not a find entry — find takes variables or aggregates`)
848
758
  }
849
759
 
850
760
  /**
851
- * The orderable ban's pointed refusal (`docs/architecture/10-data-model.md`
761
+ * The orderable ban's pointed refusal
852
762
  * § orderability): a closed reference is equality-and-membership only.
853
763
  */
854
764
  function closedOrderError(context: string, position: string, vocabulary: string): Error {
855
765
  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)`
766
+ `${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (equality, membership, and counting remain)`
857
767
  )
858
768
  }
859
769
 
860
- /** The comparison ops under the orderable ban (order roster + point membership). */
861
770
  function isOrderOp(op: CmpKind | "binding"): op is "lt" | "le" | "gt" | "ge" | "pointIn" {
862
771
  return op === "lt" || op === "le" || op === "gt" || op === "ge" || op === "pointIn"
863
772
  }
864
773
 
865
- /** Requires a variable to be bound by a relation atom of the rule (the boundness wall — invisible to the type tier). */
866
774
  function assertBound(where: string, bound: ReadonlySet<AnyVar>, ref: AnyVar): void {
867
775
  if (!bound.has(ref)) {
868
776
  throw errors.new(`${where}: the variable ${ref.label} is not bound by a relation atom of the rule`)
869
777
  }
870
778
  }
871
779
 
872
- /** Requires a variable to be interval-typed (the measure's and pack's domain), off its own descriptor. */
873
780
  function assertInterval(where: string, ref: AnyVar): void {
874
781
  if (ref.field.kind !== "interval") {
875
782
  throw errors.new(
@@ -878,7 +785,6 @@ function assertInterval(where: string, ref: AnyVar): void {
878
785
  }
879
786
  }
880
787
 
881
- /** Requires a variable's own field to be non-closed (the orderable ban's runtime twin). */
882
788
  function assertNotClosed(where: string, position: string, ref: AnyVar): void {
883
789
  const roster = rosterOf(ref.field)
884
790
  if (roster !== undefined) {
@@ -886,11 +792,12 @@ function assertNotClosed(where: string, position: string, ref: AnyVar): void {
886
792
  }
887
793
  }
888
794
 
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
- */
795
+ function assertNumeric(where: string, position: string, ref: AnyVar): void {
796
+ if (ref.field.kind !== "u64" && ref.field.kind !== "i64") {
797
+ throw errors.new(`${where}: ${position} ${ref.label} is ${ref.field.kind}, not numeric — a fold reads u64/i64 only`)
798
+ }
799
+ }
800
+
894
801
  function findColumnSlotOf(context: ChainContext, column: FindColumn): ClassedField | undefined {
895
802
  const entry = column.entry
896
803
  if (entry.kind === "var") {
@@ -899,7 +806,6 @@ function findColumnSlotOf(context: ChainContext, column: FindColumn): ClassedFie
899
806
  return undefined
900
807
  }
901
808
 
902
- /** Validates one find column's variable references (boundness + the orderable/interval walls, off the var's own field). */
903
809
  function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, column: FindColumn): void {
904
810
  const where = `${contextLabel(context)} find ${column.name}`
905
811
  const entry = column.entry
@@ -907,23 +813,14 @@ function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, colum
907
813
  assertBound(where, bound, entry.over)
908
814
  return
909
815
  }
910
- if (entry.kind === "measure") {
911
- assertBound(where, bound, entry.over)
912
- assertInterval(where, entry.over)
913
- return
914
- }
915
816
  const agg = entry.agg
916
817
  switch (agg.op) {
917
818
  case "count":
918
819
  return
919
820
  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
821
  assertBound(where, bound, agg.over)
926
822
  assertNotClosed(where, `the ${agg.fold} input`, agg.over)
823
+ assertNumeric(where, `the ${agg.fold} input`, agg.over)
927
824
  return
928
825
  }
929
826
  case "pack":
@@ -933,11 +830,6 @@ function validateColumn(context: ChainContext, bound: ReadonlySet<AnyVar>, colum
933
830
  }
934
831
  }
935
832
 
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
833
  function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: CondData): void {
942
834
  const label = contextLabel(context)
943
835
  if (cond.kind === "cmp") {
@@ -949,10 +841,6 @@ function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: C
949
841
  throw closedOrderError(label, `the ${cond.op.kind} side ${side.ref.label}`, roster.name)
950
842
  }
951
843
  }
952
- if (side.kind === "measure") {
953
- assertBound(label, bound, side.ref)
954
- assertInterval(label, side.ref)
955
- }
956
844
  }
957
845
  if ((cond.op.kind === "eq" || cond.op.kind === "ne") && cond.lhs.kind === "var" && cond.rhs.kind === "var") {
958
846
  assertBound(label, bound, cond.lhs.ref)
@@ -972,16 +860,6 @@ function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: C
972
860
  }
973
861
  }
974
862
 
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
863
  function validateInterior(
986
864
  context: ChainContext,
987
865
  bound: ReadonlySet<AnyVar>,
@@ -1035,12 +913,6 @@ function validateInterior(
1035
913
  }
1036
914
  }
1037
915
 
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
916
  function completeRule(context: ChainContext, state: RuleBuildState, rawColumns: readonly FindColumn[]): RuleData {
1045
917
  const label = contextLabel(context)
1046
918
  if (rawColumns.length === 0) {
@@ -1072,15 +944,10 @@ function completeRule(context: ChainContext, state: RuleBuildState, rawColumns:
1072
944
  return Object.freeze({ items: state.items, finds: Object.freeze(columns), paramUses: state.paramUses })
1073
945
  }
1074
946
 
1075
- /** Builds one typed rule value over completed rule data. */
1076
947
  function makeRuleValue<Row, P extends ParamsRecord>(rule: RuleData): RuleValue<Row, P> {
1077
948
  return Object.freeze({ rule })
1078
949
  }
1079
950
 
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
951
  interface RawChain {
1085
952
  match(relation: MatchOwner, bindings: Readonly<Record<string, unknown>>): RawChain
1086
953
  where(cond: AnyCond): RawChain
@@ -1088,18 +955,15 @@ interface RawChain {
1088
955
  find(entries: Readonly<Record<string, unknown>>): RuleValue<never, never>
1089
956
  }
1090
957
 
1091
- /** The runtime rule-builder shape beneath every typed scope. */
1092
958
  interface RawScope extends TermOps {
1093
959
  match(relation: MatchOwner, bindings: Readonly<Record<string, unknown>>): RawChain
1094
960
  interior(name: string, bindings: Readonly<Record<string, unknown>>): RawChain
1095
961
  }
1096
962
 
1097
- /** The declared derived tables a chain may name. */
1098
963
  type DerivedEnv =
1099
964
  | { readonly interiors: readonly InteriorData[] }
1100
965
  | { readonly interiors: readonly InteriorData[]; readonly rec: RecHandle | RecHead | RecData }
1101
966
 
1102
- /** Which rule family a chain builds — plus the schema's runtime class map and theory value (the join judge's authority). */
1103
967
  type ChainContext = { readonly classes: SchemaClasses; readonly theory: AnySchema } & DerivedEnv &
1104
968
  (
1105
969
  | { readonly kind: "query" }
@@ -1108,7 +972,6 @@ type ChainContext = { readonly classes: SchemaClasses; readonly theory: AnySchem
1108
972
  | { readonly kind: "rec-arm"; readonly self: RecHead }
1109
973
  )
1110
974
 
1111
- /** The diagnostic label of a chain context. */
1112
975
  function contextLabel(context: ChainContext): string {
1113
976
  switch (context.kind) {
1114
977
  case "query":
@@ -1122,12 +985,10 @@ function contextLabel(context: ChainContext): string {
1122
985
  }
1123
986
  }
1124
987
 
1125
- /** RecHandle is name-only staging; RecHead/RecData carry the sealed finds. */
1126
988
  function isRecHead(rec: RecHandle | RecHead | RecData): rec is RecHead {
1127
989
  return Array.isArray((rec as RecHead).finds)
1128
990
  }
1129
991
 
1130
- /** Resolves a derived-table name against the context's visible tables. */
1131
992
  function lookupDerived(context: ChainContext, name: string): DerivedTable {
1132
993
  const interior = context.interiors.find(function byName(candidate) {
1133
994
  return candidate.name === name
@@ -1156,7 +1017,6 @@ function lookupDerived(context: ChainContext, name: string): DerivedTable {
1156
1017
  throw errors.new(`${contextLabel(context)}: no derived table named ${name} is in scope`)
1157
1018
  }
1158
1019
 
1159
- /** Validates and records one interior atom per the context's cut. */
1160
1020
  function interiorAdvance(
1161
1021
  context: ChainContext,
1162
1022
  state: RuleBuildState,
@@ -1166,11 +1026,6 @@ function interiorAdvance(
1166
1026
  return advanceInterior(state, lookupDerived(context, name), bindings, "interior")
1167
1027
  }
1168
1028
 
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
1029
  function notInteriorAdvance(
1175
1030
  context: ChainContext,
1176
1031
  state: RuleBuildState,
@@ -1185,7 +1040,6 @@ function notInteriorAdvance(
1185
1040
  return advanceInterior(state, lookupDerived(context, name), bindings, "negatedInterior")
1186
1041
  }
1187
1042
 
1188
- /** Classifies one find record per the context (interior and rec heads project bound variables only). */
1189
1043
  function findColumns(context: ChainContext, entries: Readonly<Record<string, unknown>>): FindColumn[] {
1190
1044
  const columns: FindColumn[] = []
1191
1045
  const derivedHead = context.kind !== "query"
@@ -1204,7 +1058,6 @@ function findColumns(context: ChainContext, entries: Readonly<Record<string, unk
1204
1058
  return columns
1205
1059
  }
1206
1060
 
1207
- /** Builds one runtime chain (immutably — every step is a fresh chain over fresh state). */
1208
1061
  function makeRawChain(context: ChainContext, state: RuleBuildState): RawChain {
1209
1062
  const chain: RawChain = {
1210
1063
  match(relation, bindings) {
@@ -1224,7 +1077,6 @@ function makeRawChain(context: ChainContext, state: RuleBuildState): RawChain {
1224
1077
  return chain
1225
1078
  }
1226
1079
 
1227
- /** Builds one runtime rule-builder over a context. */
1228
1080
  function makeRawScope(context: ChainContext): RawScope {
1229
1081
  const scope: RawScope = {
1230
1082
  ...termOps,
@@ -1252,7 +1104,6 @@ function isTypedScope<S>(scope: RawScope): scope is RawScope & S {
1252
1104
  return typeof scope.match === "function"
1253
1105
  }
1254
1106
 
1255
- /** Builds one query-rule builder (the typed face of the raw builder). */
1256
1107
  function makeQueryRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1257
1108
  theory: AnySchema,
1258
1109
  env: DerivedEnv
@@ -1264,7 +1115,6 @@ function makeQueryRuleScope<Rels extends SchemaRelations, Classes extends Schema
1264
1115
  return raw
1265
1116
  }
1266
1117
 
1267
- /** Builds one interior-rule builder. */
1268
1118
  function makeInteriorRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1269
1119
  theory: AnySchema,
1270
1120
  env: DerivedEnv,
@@ -1277,7 +1127,6 @@ function makeInteriorRuleScope<Rels extends SchemaRelations, Classes extends Sch
1277
1127
  return raw
1278
1128
  }
1279
1129
 
1280
- /** Builds one rec-arm builder. */
1281
1130
  function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1282
1131
  theory: AnySchema,
1283
1132
  env: DerivedEnv,
@@ -1318,15 +1167,13 @@ function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaCl
1318
1167
  return raw
1319
1168
  }
1320
1169
 
1321
- /** Renders one head column's closed slice for the rule-alignment check's diagnostics. */
1322
1170
  function renderClosedSlice(closed: ClosedRoster | undefined): string {
1323
1171
  return closed === undefined ? "a bare value" : `a ${closed.name} reference`
1324
1172
  }
1325
1173
 
1326
- /** Renders one head column's signature for the rule-alignment check. */
1327
1174
  function headSignature(column: FindColumn): string {
1328
1175
  const entry = column.entry
1329
- if (entry.kind === "var" || entry.kind === "measure") {
1176
+ if (entry.kind === "var") {
1330
1177
  return `${column.name}:var`
1331
1178
  }
1332
1179
  const agg = entry.agg
@@ -1336,12 +1183,6 @@ function headSignature(column: FindColumn): string {
1336
1183
  return `${column.name}:${agg.op}`
1337
1184
  }
1338
1185
 
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
1186
  function renderParamAnchor(roster: ClosedRoster | undefined): string {
1346
1187
  return roster === undefined ? "a non-closed position" : `a ${roster.name} reference`
1347
1188
  }
@@ -1397,8 +1238,8 @@ function paramRegistryOf(
1397
1238
  )
1398
1239
  }
1399
1240
  if (existing.anchor !== undefined && use.anchor !== undefined) {
1400
- const registered = anchorRosterOf(existing.anchor)
1401
- const anchored = anchorRosterOf(use.anchor)
1241
+ const registered = rosterOf(existing.anchor)
1242
+ const anchored = rosterOf(use.anchor)
1402
1243
  if (registered !== anchored) {
1403
1244
  throw errors.new(
1404
1245
  `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 +1274,7 @@ function paramRegistryOf(
1433
1274
  if (entry === undefined) {
1434
1275
  throw errors.new(`query param ${name} lost its registry entry`)
1435
1276
  }
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
- */
1277
+
1444
1278
  let membership: QueryParam | undefined
1445
1279
  if (entry.members !== undefined) {
1446
1280
  const anchor = entry.anchor
@@ -1461,7 +1295,6 @@ function paramRegistryOf(
1461
1295
  )
1462
1296
  }
1463
1297
 
1464
- /** The runtime query shape beneath the typed `Query` face. */
1465
1298
  interface RawQuery {
1466
1299
  readonly schema: AnySchema
1467
1300
  readonly data: QueryData
@@ -1470,7 +1303,6 @@ interface RawQuery {
1470
1303
  reach(name: string, arms: never): never
1471
1304
  }
1472
1305
 
1473
- /** Asserts every rule in a list derives the same head (name, aggregate shape, closed slice, class). */
1474
1306
  function assertAlignedHeads(label: string, rules: readonly RuleData[]): void {
1475
1307
  const first = rules[0]
1476
1308
  if (first === undefined) {
@@ -1509,13 +1341,6 @@ function afterMainError(what: string): Error {
1509
1341
  )
1510
1342
  }
1511
1343
 
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
1344
  function makeRawQuery(
1520
1345
  theory: AnySchema,
1521
1346
  interiors: readonly InteriorData[],
@@ -1575,7 +1400,6 @@ function makeQuery<Rels extends SchemaRelations, Row, P extends ParamsRecord, Cl
1575
1400
  return makeRawQuery(theory, interiors, rec, rules) as unknown as Query<Rels, Row, P, Classes>
1576
1401
  }
1577
1402
 
1578
- /** Collects one Interior from its builders. */
1579
1403
  function collectInterior<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1580
1404
  theory: Schema<Rels, Classes>,
1581
1405
  env: DerivedEnv,
@@ -1596,7 +1420,6 @@ function collectInterior<Rels extends SchemaRelations, Classes extends SchemaCla
1596
1420
  return Object.freeze({ name, finds: first.finds, rules: Object.freeze(rules) })
1597
1421
  }
1598
1422
 
1599
- /** Collects the Rec from tagged base/rec builder arrays. */
1600
1423
  function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1601
1424
  theory: Schema<Rels, Classes>,
1602
1425
  interiors: readonly InteriorData[],
@@ -1646,7 +1469,6 @@ function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>
1646
1469
  return recData
1647
1470
  }
1648
1471
 
1649
- /** Builds the CQ query start (interiors, then reach or the first main rule). */
1650
1472
  function makeQueryStart<Rels extends SchemaRelations, Classes extends SchemaClasses, P extends ParamsRecord>(
1651
1473
  theory: Schema<Rels, Classes>,
1652
1474
  interiors: readonly InteriorData[]
@@ -1702,7 +1524,6 @@ function makeQueryStart<Rels extends SchemaRelations, Classes extends SchemaClas
1702
1524
  return start as unknown as QueryStart<Rels, Classes, P>
1703
1525
  }
1704
1526
 
1705
- /** Builds the Reach query start (rec sealed; only the first main rule remains). */
1706
1527
  function makeQueryReachStart<Rels extends SchemaRelations, Classes extends SchemaClasses, P extends ParamsRecord>(
1707
1528
  theory: Schema<Rels, Classes>,
1708
1529
  interiors: readonly InteriorData[],
@@ -1721,24 +1542,12 @@ function makeQueryReachStart<Rels extends SchemaRelations, Classes extends Schem
1721
1542
  return start as unknown as QueryReachStart<Rels, Classes, P>
1722
1543
  }
1723
1544
 
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
1545
  function query<Rels extends SchemaRelations, Classes extends SchemaClasses>(
1732
1546
  theory: Schema<Rels, Classes>
1733
1547
  ): QueryStart<Rels, Classes> {
1734
1548
  return makeQueryStart<Rels, Classes, Record<never, never>>(theory, [])
1735
1549
  }
1736
1550
 
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
1551
  function taggedHandleId(
1743
1552
  context: string,
1744
1553
  closed: { readonly name: string; readonly handles: readonly string[] },
@@ -1756,11 +1565,6 @@ function taggedHandleId(
1756
1565
  return { kind: "u64", value: BigInt(id) }
1757
1566
  }
1758
1567
 
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
1568
  function taggedAtElementDomain(context: string, element: "u64" | "i64", value: unknown): TaggedValue {
1765
1569
  if (typeof value === "bigint") {
1766
1570
  if (element === "u64") {
@@ -1777,10 +1581,6 @@ function taggedAtElementDomain(context: string, element: "u64" | "i64", value: u
1777
1581
  throw literalShapeError(context, "bigint (point) or { start, end } (interval)", value)
1778
1582
  }
1779
1583
 
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
1584
  function taggedLiteral(context: string, field: AnyField, value: unknown): TaggedValue {
1785
1585
  const roster = rosterOf(field)
1786
1586
  if (roster !== undefined) {
@@ -1834,18 +1634,7 @@ function taggedLiteral(context: string, field: AnyField, value: unknown): Tagged
1834
1634
  * interval operand of `pointIn(t, span(...))`; under every other operator an
1835
1635
  * interval shape against a scalar sibling stays refused.
1836
1636
  */
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
- }
1637
+ function taggedCmpLiteral(context: string, sibling: AnyField, value: unknown, op: CmpKind | "binding"): TaggedValue {
1849
1638
  if (rosterOf(sibling) === undefined && sibling.kind === "interval") {
1850
1639
  return taggedAtElementDomain(context, sibling.element, value)
1851
1640
  }
@@ -1860,7 +1649,6 @@ function taggedCmpLiteral(
1860
1649
  return taggedLiteral(context, sibling, value)
1861
1650
  }
1862
1651
 
1863
- /** The shared lowering context of one `lowerQuery` run. */
1864
1652
  interface LowerContext {
1865
1653
  readonly theory: AnySchema
1866
1654
  readonly relationIds: ReadonlyMap<string, number>
@@ -1869,12 +1657,10 @@ interface LowerContext {
1869
1657
  readonly params: ReadonlyMap<string, ParamEntry>
1870
1658
  }
1871
1659
 
1872
- /** One rule's dense variable numbering: first occurrence in written order, keyed on the object REFERENCE. */
1873
1660
  interface VarIds {
1874
1661
  of(ref: AnyVar): number
1875
1662
  }
1876
1663
 
1877
- /** Creates one rule-scoped variable numberer. */
1878
1664
  function freshVarIds(): VarIds {
1879
1665
  const assigned = new Map<AnyVar, number>()
1880
1666
  return {
@@ -1890,7 +1676,6 @@ function freshVarIds(): VarIds {
1890
1676
  }
1891
1677
  }
1892
1678
 
1893
- /** Resolves a param name to its dense positional id. */
1894
1679
  function paramIdOf(ctx: LowerContext, name: string): number {
1895
1680
  const id = ctx.paramIds.get(name)
1896
1681
  if (id === undefined) {
@@ -1899,10 +1684,6 @@ function paramIdOf(ctx: LowerContext, name: string): number {
1899
1684
  return id
1900
1685
  }
1901
1686
 
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
1687
  function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIr {
1907
1688
  const member = ctx.theory.relations[atom.relation.name]
1908
1689
  if (member !== atom.relation) {
@@ -1927,7 +1708,6 @@ function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIr {
1927
1708
  return { source: { kind: "edb", relation: relationId }, bindings }
1928
1709
  }
1929
1710
 
1930
- /** Lowers one binding term. A membership ARRAY lowers to the existing param-set term over its content-addressed entry. */
1931
1711
  function lowerBindingTerm(ctx: LowerContext, context: string, binding: BindingEntry, ids: VarIds): TermIr {
1932
1712
  const bound = binding.term
1933
1713
  switch (bound.kind) {
@@ -1975,7 +1755,6 @@ function lowerInteriorAtom(
1975
1755
  return { source: { kind: "interior", interior }, bindings: irBindings }
1976
1756
  }
1977
1757
 
1978
- /** Lowers one comparison side; literals tag by the sibling's anchor (op-aware at `pointIn`). */
1979
1758
  function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData, ids: VarIds, op: CmpKind): TermIr {
1980
1759
  switch (side.kind) {
1981
1760
  case "var":
@@ -1984,13 +1763,11 @@ function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData
1984
1763
  return { kind: "param", param: paramIdOf(ctx, side.name) }
1985
1764
  case "setParam":
1986
1765
  return { kind: "paramSet", param: paramIdOf(ctx, side.name) }
1987
- case "measure":
1988
- return { kind: "measure", var: ids.of(side.ref) }
1989
1766
  case "literal": {
1990
1767
  const anchor = cmpAnchorOf(ctx, sibling)
1991
1768
  if (anchor === undefined) {
1992
1769
  throw errors.new(
1993
- "query lowering: a comparison literal needs a bound-variable, measure, or anchored-param sibling to type it"
1770
+ "query lowering: a comparison literal needs a bound-variable or anchored-param sibling to type it"
1994
1771
  )
1995
1772
  }
1996
1773
  return { kind: "literal", value: taggedCmpLiteral("comparison literal", anchor, side.value, op) }
@@ -1998,21 +1775,16 @@ function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData
1998
1775
  }
1999
1776
  }
2000
1777
 
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 {
1778
+ function cmpAnchorOf(ctx: LowerContext, sibling: CmpTermData): AnyField | undefined {
2003
1779
  if (sibling.kind === "var") {
2004
1780
  return sibling.ref.field
2005
1781
  }
2006
- if (sibling.kind === "measure") {
2007
- return "measure"
2008
- }
2009
1782
  if (sibling.kind === "param" || sibling.kind === "setParam") {
2010
1783
  return ctx.params.get(sibling.name)?.anchor
2011
1784
  }
2012
1785
  return undefined
2013
1786
  }
2014
1787
 
2015
- /** Lowers one comparison. */
2016
1788
  function lowerComparison(ctx: LowerContext, cmp: CmpData, ids: VarIds): ComparisonIr {
2017
1789
  if (cmp.op.kind === "allen") {
2018
1790
  return {
@@ -2028,7 +1800,6 @@ function lowerComparison(ctx: LowerContext, cmp: CmpData, ids: VarIds): Comparis
2028
1800
  }
2029
1801
  }
2030
1802
 
2031
- /** Lowers one condition node (comparison leaf or and/or tree). */
2032
1803
  function lowerCondition(ctx: LowerContext, cond: CondData, ids: VarIds): ConditionTreeIr {
2033
1804
  if (cond.kind === "cmp") {
2034
1805
  return { kind: "leaf", cmp: lowerComparison(ctx, cond, ids) }
@@ -2041,30 +1812,21 @@ function lowerCondition(ctx: LowerContext, cond: CondData, ids: VarIds): Conditi
2041
1812
  }
2042
1813
  }
2043
1814
 
2044
- /** Lowers one find entry to its per-rule find term. */
2045
1815
  function lowerFind(entry: FindEntryData, ids: VarIds): FindTermIr {
2046
1816
  if (entry.kind === "var") {
2047
1817
  return { kind: "var", var: ids.of(entry.over) }
2048
1818
  }
2049
- if (entry.kind === "measure") {
2050
- return { kind: "measure", var: ids.of(entry.over) }
2051
- }
2052
1819
  const agg = entry.agg
2053
1820
  switch (agg.op) {
2054
1821
  case "count":
2055
1822
  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
- }
1823
+ case "fold":
2060
1824
  return { kind: "aggregate", op: { kind: agg.fold }, over: ids.of(agg.over) }
2061
- }
2062
1825
  case "pack":
2063
1826
  return { kind: "pack", over: ids.of(agg.over) }
2064
1827
  }
2065
1828
  }
2066
1829
 
2067
- /** One aggregate's var-free head-op kind (`AggOp::head_op`). */
2068
1830
  function headOpOf(agg: AggData): HeadOpIr {
2069
1831
  switch (agg.op) {
2070
1832
  case "count":
@@ -2076,16 +1838,14 @@ function headOpOf(agg: AggData): HeadOpIr {
2076
1838
  }
2077
1839
  }
2078
1840
 
2079
- /** One find entry's var-free head shape. */
2080
1841
  function headTermOf(column: FindColumn): HeadTermIr {
2081
1842
  const entry = column.entry
2082
- if (entry.kind === "var" || entry.kind === "measure") {
1843
+ if (entry.kind === "var") {
2083
1844
  return { kind: "var" }
2084
1845
  }
2085
1846
  return { kind: "aggregate", op: headOpOf(entry.agg) }
2086
1847
  }
2087
1848
 
2088
- /** Lowers one rule: body walked in written order (var ids by first occurrence), finds last. */
2089
1849
  function lowerRule(ctx: LowerContext, rule: RuleData): RuleIr {
2090
1850
  const ids = freshVarIds()
2091
1851
  const atoms: AtomIr[] = []
@@ -2125,11 +1885,6 @@ function lowerRule(ctx: LowerContext, rule: RuleData): RuleIr {
2125
1885
  }
2126
1886
  }
2127
1887
 
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
1888
  function lowerQuery(q: AnyQuery): ParsedQuery {
2134
1889
  const theory = q.schema
2135
1890
  const relationIds = new Map<string, number>()