@bjornpagen/bumbledb 0.9.0 → 0.11.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 (73) hide show
  1. package/COOKBOOK.md +155 -136
  2. package/README.md +3 -7
  3. package/dist/capacity.d.ts +14 -1
  4. package/dist/capacity.d.ts.map +1 -1
  5. package/dist/capacity.js.map +1 -1
  6. package/dist/db.d.ts +77 -109
  7. package/dist/db.d.ts.map +1 -1
  8. package/dist/db.js +121 -339
  9. package/dist/db.js.map +1 -1
  10. package/dist/index.d.ts +11 -16
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7 -10
  13. package/dist/index.js.map +1 -1
  14. package/dist/lower.d.ts.map +1 -1
  15. package/dist/lower.js +8 -1
  16. package/dist/lower.js.map +1 -1
  17. package/dist/native.d.ts +69 -50
  18. package/dist/native.d.ts.map +1 -1
  19. package/dist/native.js.map +1 -1
  20. package/dist/query/atom.d.ts +162 -122
  21. package/dist/query/atom.d.ts.map +1 -1
  22. package/dist/query/atom.js +26 -22
  23. package/dist/query/atom.js.map +1 -1
  24. package/dist/query/find.d.ts +18 -35
  25. package/dist/query/find.d.ts.map +1 -1
  26. package/dist/query/find.js +13 -32
  27. package/dist/query/find.js.map +1 -1
  28. package/dist/query/lower.d.ts +113 -122
  29. package/dist/query/lower.d.ts.map +1 -1
  30. package/dist/query/lower.js +336 -260
  31. package/dist/query/lower.js.map +1 -1
  32. package/dist/query/parse-ir.d.ts +12 -0
  33. package/dist/query/parse-ir.d.ts.map +1 -0
  34. package/dist/query/parse-ir.js +71 -0
  35. package/dist/query/parse-ir.js.map +1 -0
  36. package/dist/query/run.d.ts +2 -2
  37. package/dist/query/run.d.ts.map +1 -1
  38. package/dist/query/run.js +2 -13
  39. package/dist/query/run.js.map +1 -1
  40. package/dist/query/scope.d.ts +4 -16
  41. package/dist/query/scope.d.ts.map +1 -1
  42. package/dist/query/scope.js +1 -6
  43. package/dist/query/scope.js.map +1 -1
  44. package/dist/schema.js +2 -2
  45. package/dist/schema.js.map +1 -1
  46. package/dist/statements.d.ts +15 -9
  47. package/dist/statements.d.ts.map +1 -1
  48. package/dist/statements.js +14 -9
  49. package/dist/statements.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/capacity.ts +24 -1
  52. package/src/db.ts +182 -443
  53. package/src/index.ts +9 -23
  54. package/src/lower.ts +8 -1
  55. package/src/native.ts +69 -42
  56. package/src/query/atom.ts +255 -165
  57. package/src/query/find.ts +39 -80
  58. package/src/query/lower.ts +578 -432
  59. package/src/query/parse-ir.ts +82 -0
  60. package/src/query/run.ts +2 -14
  61. package/src/query/scope.ts +3 -21
  62. package/src/schema.ts +2 -2
  63. package/src/statements.ts +33 -17
  64. package/dist/order.d.ts +0 -87
  65. package/dist/order.d.ts.map +0 -1
  66. package/dist/order.js +0 -153
  67. package/dist/order.js.map +0 -1
  68. package/dist/query/predicate.d.ts +0 -91
  69. package/dist/query/predicate.d.ts.map +0 -1
  70. package/dist/query/predicate.js +0 -156
  71. package/dist/query/predicate.js.map +0 -1
  72. package/src/order.ts +0 -234
  73. package/src/query/predicate.ts +0 -269
package/src/query/atom.ts CHANGED
@@ -23,9 +23,9 @@
23
23
  * wall only.
24
24
  *
25
25
  * This module also owns the plain runtime DATA a built rule is made of
26
- * (`RuleData`/`RecData` and friends): frozen values the lowering walks —
27
- * pure data (variable references included), so lowering stays a pure,
28
- * stable function of the query value.
26
+ * (`RuleData`/`InteriorData`/`RecData` and friends): frozen values the
27
+ * lowering walks — pure data (variable references included), so lowering
28
+ * stays a pure, stable function of the query value.
29
29
  */
30
30
 
31
31
  import * as errors from "@superbuilders/errors"
@@ -36,23 +36,21 @@ import type {
36
36
  ClassedField,
37
37
  Duration,
38
38
  JoinOk,
39
- MaskParam,
40
39
  MatchFields,
41
40
  MatchOwner,
42
41
  MintSlotOf,
43
42
  Param,
44
- ParamsRecord,
45
43
  ParamValueAt,
46
44
  SetParam,
47
45
  ShapeOf
48
46
  } from "#query/scope.ts"
49
- import { inferred, isTerm } from "#query/scope.ts"
47
+ import { isTerm, term } from "#query/scope.ts"
50
48
  import type { FieldsShape } from "#relation.ts"
51
49
 
52
50
  /**
53
51
  * One atom-binding position as runtime data. A variable rides BY REFERENCE
54
52
  * (`ref`) — object identity is the join. `literalSet` is a membership
55
- * ARRAY at a closed-reference field, folded into the program.
53
+ * ARRAY at a closed-reference field, folded into the query.
56
54
  */
57
55
  type BindingTermData =
58
56
  | { readonly kind: "var"; readonly ref: AnyVar }
@@ -86,17 +84,15 @@ type CmpTermData =
86
84
  | { readonly kind: "measure"; readonly ref: AnyVar }
87
85
  | { readonly kind: "literal"; readonly value: unknown }
88
86
 
89
- /** The `allen` mask position as runtime data. */
90
- type MaskData = { readonly kind: "literal"; readonly mask: number } | { readonly kind: "param"; readonly name: string }
91
-
92
- /** One comparison condition as runtime data (`mask` present exactly for `allen`). */
93
- interface CmpData {
87
+ /** One comparison condition as runtime data; the mask lives inside `allen`. */
88
+ type CmpData = {
94
89
  readonly kind: "cmp"
95
- readonly op: CmpKind
96
- readonly mask: MaskData | undefined
97
90
  readonly lhs: CmpTermData
98
91
  readonly rhs: CmpTermData
99
- }
92
+ } & (
93
+ | { readonly op: { readonly kind: "allen"; readonly mask: number } }
94
+ | { readonly op: { readonly kind: Exclude<CmpKind, "allen"> } }
95
+ )
100
96
 
101
97
  /** One condition-tree node as runtime data (`ir::ConditionTree`). */
102
98
  interface TreeData {
@@ -111,13 +107,11 @@ type CondData = CmpData | TreeData
111
107
  /** One aggregate's runtime description (find vocabulary, over variable REFERENCES). */
112
108
  type AggData =
113
109
  | { readonly op: "count" }
114
- | { readonly op: "countDistinct"; readonly over: AnyVar }
115
110
  | {
116
111
  readonly op: "fold"
117
112
  readonly fold: "sum" | "min" | "max"
118
113
  readonly over: AnyVar | { readonly duration: AnyVar }
119
114
  }
120
- | { readonly op: "arg"; readonly direction: "argMax" | "argMin"; readonly over: AnyVar; readonly key: AnyVar }
121
115
  | { readonly op: "pack"; readonly over: AnyVar }
122
116
 
123
117
  /** One classified find entry as runtime data (variables and the measure ride BY REFERENCE). */
@@ -129,12 +123,12 @@ type FindEntryData =
129
123
  /**
130
124
  * One answer column: its name (the row object key — the find record's key,
131
125
  * so renames are real and a duplicate column is unrepresentable), its entry,
132
- * the classed mint SLOT its values flow from (a projected var or an
133
- * Arg-carried payload; `undefined` for counts/folds/measures/pack, which
134
- * derive numbers or intervals), and — when that slot is a closed reference —
126
+ * the classed mint SLOT its values flow from (a projected var;
127
+ * `undefined` for counts/folds/measures/pack, which derive numbers or
128
+ * intervals), and — when that slot is a closed reference —
135
129
  * the roster the decode lifts row ids back to handle NAMES through
136
130
  * (`undefined` on every bare column). The slice is SDK-side marshaling data
137
- * only: the wire `ProgramIr` never carries it.
131
+ * only: the wire `QueryIr` never carries it.
138
132
  */
139
133
  interface FindColumn {
140
134
  readonly name: string
@@ -144,19 +138,23 @@ interface FindColumn {
144
138
  }
145
139
 
146
140
  /**
147
- * One body item of a rule, in written order. The idb join is a NAMED record
148
- * over the rec's head keys (`key`) bound to local variables (`ref`) — the
149
- * typed wall a record's unordered keys would otherwise lose.
141
+ * One body item of a rule, in written order. An interior join is a NAMED
142
+ * record over the derived table's head keys (`key`) bound to local
143
+ * variables (`ref`) — the typed wall a record's unordered keys would
144
+ * otherwise lose.
150
145
  */
151
146
  type RuleItem =
152
147
  | { readonly kind: "atom"; readonly atom: AtomData }
153
148
  | { readonly kind: "negated"; readonly atom: AtomData }
154
149
  | {
155
- readonly kind: "idb"
156
- readonly rec: RecData
150
+ readonly kind: "interior"
151
+ readonly target: DerivedTable
152
+ readonly bindings: ReadonlyArray<{ readonly key: string; readonly ref: AnyVar }>
153
+ }
154
+ | {
155
+ readonly kind: "negatedInterior"
156
+ readonly target: DerivedTable
157
157
  readonly bindings: ReadonlyArray<{ readonly key: string; readonly ref: AnyVar }>
158
- /** `true` on a negated finished-stratum atom (`r.not(rec, {...})`): probed through its anti-probe, binds nothing. */
159
- readonly negated: boolean
160
158
  }
161
159
  | { readonly kind: "cond"; readonly cond: CondData }
162
160
 
@@ -168,7 +166,7 @@ type RuleItem =
168
166
  */
169
167
  interface ParamUse {
170
168
  readonly name: string
171
- readonly shape: "value" | "set" | "mask"
169
+ readonly shape: "value" | "set"
172
170
  readonly anchor: AnyField | "measure" | undefined
173
171
  readonly op: "binding" | CmpKind
174
172
  readonly members: readonly string[] | undefined
@@ -181,16 +179,46 @@ interface RuleData {
181
179
  readonly paramUses: readonly ParamUse[]
182
180
  }
183
181
 
182
+ /** One named interior's runtime description — identity keys the dense `InteriorId` at lowering. */
183
+ interface InteriorData {
184
+ readonly name: string
185
+ readonly finds: readonly FindColumn[]
186
+ readonly rules: readonly RuleData[]
187
+ }
188
+
189
+ /** A nonempty frozen list — empty base/rec/finds are unrepresentable on RecData. */
190
+ type NonEmpty<T> = readonly [T, ...T[]]
191
+
184
192
  /**
185
- * One recursive predicate's runtime description — identity keys the dense
186
- * `PredId` at lowering. `rules` is appended by `rec.rule(...)` and sealed
187
- * (frozen) when the program's output is declared.
193
+ * Name-only rec identity used while base arms are in flight. Rec-base
194
+ * arms cannot read the rec; they must not observe a head.
188
195
  */
189
- interface RecData {
196
+ interface RecHandle {
190
197
  readonly name: string
191
- readonly rules: RuleData[]
192
198
  }
193
199
 
200
+ /**
201
+ * Rec identity plus the sealed head, used while rec arms are in flight
202
+ * and as the target those arms capture.
203
+ */
204
+ interface RecHead {
205
+ readonly name: string
206
+ readonly finds: NonEmpty<FindColumn>
207
+ }
208
+
209
+ /**
210
+ * The optional linear rec's runtime description — identity keys the dense
211
+ * `InteriorId` (`interiors.length`) at lowering. Base and rec arms are
212
+ * nonempty by type and sealed in one assignment.
213
+ */
214
+ interface RecData extends RecHead {
215
+ readonly base: NonEmpty<RuleData>
216
+ readonly rec: NonEmpty<RuleData>
217
+ }
218
+
219
+ /** A named derived table (an interior or the rec) as `.interior(name)` / `r.not(name)` resolve it. */
220
+ type DerivedTable = InteriorData | RecHead
221
+
194
222
  /**
195
223
  * What a binding position of field `F` accepts: a bare structural literal
196
224
  * of the field's value type, a variable/param/∈-set-param term — and, when
@@ -286,31 +314,15 @@ interface NotAtom<R extends MatchOwner, B> {
286
314
  }
287
315
 
288
316
  /**
289
- * A recursive predicate as a NEGATION target — the structural half of the
290
- * rec reference (`data` carries rules, never fields, so an EDB or closed
291
- * owner can never match it and the `not()` overloads stay disjoint).
317
+ * One negated finished-table atom — negation OF an interior or of the
318
+ * finished rec is engine-legal in main (a finished set is a set), and
319
+ * this value is its one spelling: `r.not("reach", { c })`. Binds nothing,
320
+ * only rejects — every variable it names must be positively bound in the
321
+ * rule (the same safety rule as EDB negation).
292
322
  */
293
- interface RecTarget {
323
+ interface NotInteriorAtom<B> {
324
+ readonly cond: "notInterior"
294
325
  readonly name: string
295
- readonly data: RecData
296
- readonly [inferred]?: { readonly params: ParamsRecord; readonly head: HeadShapeOf }
297
- }
298
-
299
- /** The head signature a threaded rec target carries (`undefined` before its first rule seals one). */
300
- type HeadShapeOf = Readonly<Record<string, ClassedField>> | undefined
301
-
302
- /**
303
- * One negated FINISHED-STRATUM atom — negation OF a lower stratum is
304
- * engine-legal (the strata judge refuses only negation *through* a cycle:
305
- * a finished set is what keeps the operator monotone), and this value is
306
- * its one spelling: `r.not(reach, { c })` in an output rule rejects every
307
- * binding the finished stratum extends, through the engine's anti-probe.
308
- * Binds nothing, only rejects — every variable it names must be positively
309
- * bound in the rule (the same safety rule as EDB negation).
310
- */
311
- interface NotIdbAtom<Target extends RecTarget, B> {
312
- readonly cond: "notIdb"
313
- readonly target: Target
314
326
  readonly bindings: B
315
327
  }
316
328
 
@@ -323,11 +335,11 @@ type AnyTreeChild = AnyCmp | Tree<readonly AnyTreeChild[]>
323
335
  /** Any negated-atom value. */
324
336
  type AnyNotAtom = NotAtom<MatchOwner, unknown>
325
337
 
326
- /** Any negated finished-stratum value. */
327
- type AnyNotIdbAtom = NotIdbAtom<RecTarget, unknown>
338
+ /** Any negated finished-table value. */
339
+ type AnyNotInteriorAtom = NotInteriorAtom<unknown>
328
340
 
329
- /** Any `.where` input: a comparison, a condition tree, or a negated atom (EDB, closed, or finished stratum). */
330
- type AnyCond = AnyCmp | Tree<readonly AnyTreeChild[]> | AnyNotAtom | AnyNotIdbAtom
341
+ /** Any `.where` input: a comparison, a condition tree, or a negated atom (EDB, closed, or finished table). */
342
+ type AnyCond = AnyCmp | Tree<readonly AnyTreeChild[]> | AnyNotAtom | AnyNotInteriorAtom
331
343
 
332
344
  /** What `eq`'s right side accepts (`ParamSet` is `Eq`-only — the IR's rule). */
333
345
  type EqRight = AnyVar | Param<string> | SetParam<string> | bigint | string | boolean | Uint8Array | IntervalValue
@@ -350,14 +362,28 @@ function comparison<Op extends CmpKind, L, R, M>(op: Op, lhs: L, rhs: R, mask: M
350
362
  }
351
363
 
352
364
  /**
353
- * Rejects a comparison with no term side: it is constant-valued, the
354
- * engine's own validation refuses it, and the lowering has no anchored
355
- * position to type the literals by — fail here with the same verdict.
365
+ * Whether a comparison side is a VARIABLE side in the engine's sense — a
366
+ * bound variable or the measure. A param is a constant at execution, so a
367
+ * comparison whose every side is a param or literal is constant-valued:
368
+ * the engine's `ConstantComparison` conviction
369
+ * (`bumbledb/crates/bumbledb/src/ir/validate/context.rs`,
370
+ * `comparison_shape`'s last arm).
371
+ */
372
+ function isVariableSide(value: unknown): boolean {
373
+ return isTerm(value) && (value[term] === "var" || value[term] === "duration")
374
+ }
375
+
376
+ /**
377
+ * Rejects a comparison with no variable side: it is constant-valued —
378
+ * params included, a param is a constant the moment it binds — the
379
+ * engine's own validation refuses it (`ConstantComparison`), and the
380
+ * lowering has no anchored position to type the literals by — fail here
381
+ * with the same verdict.
356
382
  */
357
383
  function assertTermSide(op: string, lhs: unknown, rhs: unknown): void {
358
- if (!isTerm(lhs) && !isTerm(rhs)) {
384
+ if (!isVariableSide(lhs) && !isVariableSide(rhs)) {
359
385
  throw errors.new(
360
- `${op}: a comparison without a variable or parameter side is constant-valued — write the query you mean`
386
+ `${op}: a comparison without a variable side is constant-valued (a parameter is a constant at execution) — write the query you mean`
361
387
  )
362
388
  }
363
389
  }
@@ -459,16 +485,15 @@ const ALLEN = Object.freeze({
459
485
  /**
460
486
  * THE interval-pair comparison (`ir::CmpOp::Allen`): two interval terms of
461
487
  * one element type, satisfied iff the pair's classification is in the
462
- * 13-bit mask — a literal built from the `ALLEN` constants, or a mask
463
- * parameter (`r.maskParam`).
488
+ * 13-bit mask — a literal built from the `ALLEN` constants.
464
489
  */
465
- function allen<const A extends IntervalSide, const M extends number | MaskParam<string>, const B extends IntervalSide>(
490
+ function allen<const A extends IntervalSide, const B extends IntervalSide>(
466
491
  left: A,
467
- mask: M,
492
+ mask: number,
468
493
  right: B
469
- ): Cmp<"allen", A, B, M> {
494
+ ): Cmp<"allen", A, B, number> {
470
495
  assertTermSide("allen", left, right)
471
- if (typeof mask === "number" && (!Number.isInteger(mask) || mask < 0 || mask > ALLEN_ALL_BITS)) {
496
+ if (!Number.isInteger(mask) || mask < 0 || mask > ALLEN_ALL_BITS) {
472
497
  throw errors.new(
473
498
  `allen mask ${mask} is not a 13-bit mask — build masks from the ALLEN constants (bumbledb allen.rs: bits above the low 13 are unrepresentable)`
474
499
  )
@@ -501,35 +526,25 @@ function or<const C extends readonly AnyTreeChild[]>(...children: C): Tree<C> {
501
526
  * nothing, only rejects: every variable it names must be positively bound
502
527
  * in the rule, a construction-time wall (the engine's safety refusal stands
503
528
  * behind it). A CLOSED owner is legal here too — and so is a FINISHED
504
- * STRATUM: `not(reach, { c })` in an output rule negates the rec's
505
- * finished set (a named record over its head keys, variables only — the
506
- * same wall `idb()` holds), the one spelling of the engine-legal
507
- * complement query (`(n) | Node(id: n), !reach(n);` on the Rust surface).
529
+ * TABLE: `not("reach", { c })` in a main rule negates the rec's finished
530
+ * set (a named record over its head keys, variables only), the one
531
+ * spelling of the engine-legal complement query.
508
532
  */
509
533
  function not<R extends MatchOwner, const B extends MatchShape<MatchFields<R>>>(relation: R, bindings: B): NotAtom<R, B>
510
- function not<Target extends RecTarget, const B extends Readonly<Record<string, AnyVar>>>(
511
- target: Target,
534
+ function not<const Name extends string, const B extends Readonly<Record<string, AnyVar>>>(
535
+ name: Name,
512
536
  bindings: B
513
- ): NotIdbAtom<Target, B>
537
+ ): NotInteriorAtom<B>
514
538
  function not(
515
- relation: MatchOwner | RecTarget,
539
+ relation: MatchOwner | string,
516
540
  bindings: Readonly<Record<string, unknown>>
517
- ): NotAtom<MatchOwner, unknown> | NotIdbAtom<RecTarget, unknown> {
518
- if (isRecTarget(relation)) {
519
- return Object.freeze({ cond: "notIdb" as const, target: relation, bindings })
541
+ ): NotAtom<MatchOwner, unknown> | NotInteriorAtom<unknown> {
542
+ if (typeof relation === "string") {
543
+ return Object.freeze({ cond: "notInterior" as const, name: relation, bindings })
520
544
  }
521
545
  return Object.freeze({ cond: "not" as const, relation, bindings })
522
546
  }
523
547
 
524
- /**
525
- * THE negation-target discriminant: a rec's runtime data carries its rules,
526
- * a relation's its fields (and a closed relation's its handle roster) — the
527
- * shapes are disjoint by construction, so the dispatch is total.
528
- */
529
- function isRecTarget(value: MatchOwner | RecTarget): value is RecTarget {
530
- return "rules" in value.data
531
- }
532
-
533
548
  /**
534
549
  * Whether a variable's OWN field is NUMERIC (u64/i64) — the judgment the
535
550
  * point side of `pointIn` and the `sum` input read: a point lives in the
@@ -560,18 +575,130 @@ type OrderVarOk<V extends AnyVar> = V["field"]["kind"] extends "bool" ? true : N
560
575
  /** Whether a variable's OWN field is interval-typed. */
561
576
  type IntervalVarOk<V extends AnyVar> = V["field"]["kind"] extends "interval" ? true : false
562
577
 
563
- /** One order-comparison side's judgment (off the term's own field). */
564
- type OrderSideOk<T> = T extends AnyVar
565
- ? OrderVarOk<T>
578
+ /**
579
+ * Whether a comparison has a VARIABLE side (a var or the measure) — the
580
+ * type-level twin of the engine's `ConstantComparison` rule: a param is a
581
+ * constant at execution, so param-vs-param and param-vs-literal are
582
+ * constant-valued whatever their domains ({@link assertTermSide} is the
583
+ * runtime wall behind this one).
584
+ */
585
+ type CmpVarSideOk<L, R> = L extends AnyVar | Duration ? true : R extends AnyVar | Duration ? true : false
586
+
587
+ /**
588
+ * One order side's DOMAIN — the type-level twin of the engine's resolved
589
+ * operand type (`ir/validate/context.rs`, `classify`'s `Ord*` arms): a
590
+ * variable names its own field kind, the measure is u64 by definition (and
591
+ * never meets another measure — `DurationBothSides`), a bigint literal
592
+ * spans the integer domains, a param is typed by its sibling (`"open"`).
593
+ * `never` marks a side that is itself unorderable: interval/bytes/string
594
+ * vars (the operand screen), closed-bound vars (R4), a measure over a
595
+ * non-interval var.
596
+ */
597
+ type OrderDomain<T> = T extends AnyVar
598
+ ? OrderVarOk<T> extends true
599
+ ? T["field"]["kind"]
600
+ : never
566
601
  : T extends Duration<infer V extends AnyVar>
567
- ? IntervalVarOk<V>
568
- : true
602
+ ? IntervalVarOk<V> extends true
603
+ ? "duration"
604
+ : never
605
+ : T extends Param<string>
606
+ ? "open"
607
+ : T extends bigint
608
+ ? "integer"
609
+ : T extends boolean
610
+ ? "bool"
611
+ : never
569
612
 
570
- /** One point side's judgment (numeric only — a point lives in the interval's element domain, never bool). */
571
- type PointSideOk<T> = T extends AnyVar ? NumericVarOk<T> : true
613
+ /**
614
+ * Whether two order domains may MEET under one operator — the engine's
615
+ * same-type rule judged pairwise, not per side: bool meets only bool (a
616
+ * bigint literal against a bool var is exactly `check_const`'s
617
+ * conviction; R3 made bool orderable, never cross-orderable), u64 and i64
618
+ * never meet, the measure's sibling must live in u64 (`OrdMeasureVar`:
619
+ * scalar ≠ U64 is `IllegalComparison`), two measures never meet, and an
620
+ * open side (param, integer literal) takes whatever its sibling names.
621
+ */
622
+ type OrderDomainsOk<A, B> = [A] extends [never]
623
+ ? false
624
+ : [B] extends [never]
625
+ ? false
626
+ : A extends "open"
627
+ ? true
628
+ : B extends "open"
629
+ ? true
630
+ : A extends "duration"
631
+ ? B extends "u64" | "integer"
632
+ ? true
633
+ : false
634
+ : B extends "duration"
635
+ ? A extends "u64" | "integer"
636
+ ? true
637
+ : false
638
+ : A extends "bool"
639
+ ? B extends "bool"
640
+ ? true
641
+ : false
642
+ : B extends "bool"
643
+ ? false
644
+ : A extends "integer"
645
+ ? true
646
+ : B extends "integer"
647
+ ? true
648
+ : A extends B
649
+ ? true
650
+ : false
651
+
652
+ /** The whole order comparison's judgment: a variable side exists, and the sides' domains meet. */
653
+ type OrderPairOk<L, R> = CmpVarSideOk<L, R> extends true ? OrderDomainsOk<OrderDomain<L>, OrderDomain<R>> : false
572
654
 
573
- /** One interval side's judgment. */
574
- type IntervalSideOk<T> = T extends AnyVar ? IntervalVarOk<T> : true
655
+ /**
656
+ * An interval side's ELEMENT domain: a variable names its field's element
657
+ * (`never` on a non-interval var), a param or bare interval literal is
658
+ * `"open"` — no type exists to name its element at this tier; the engine
659
+ * anchors it at prepare.
660
+ */
661
+ type IntervalElementDomain<T> = T extends AnyVar
662
+ ? T["field"] extends { readonly kind: "interval"; readonly element: infer E extends "u64" | "i64" }
663
+ ? E
664
+ : never
665
+ : "open"
666
+
667
+ /**
668
+ * The point side's domain — a point lives in the interval's element
669
+ * domain: a numeric var names its kind (never bool, never closed), a
670
+ * param or bigint literal is open.
671
+ */
672
+ type PointDomain<T> = T extends AnyVar ? (NumericVarOk<T> extends true ? T["field"]["kind"] : never) : "open"
673
+
674
+ /** Whether two element domains meet: both legal, and equal unless one is open. */
675
+ type ElementMeets<A, B> = [A] extends [never]
676
+ ? false
677
+ : [B] extends [never]
678
+ ? false
679
+ : A extends "open"
680
+ ? true
681
+ : B extends "open"
682
+ ? true
683
+ : A extends B
684
+ ? true
685
+ : false
686
+
687
+ /**
688
+ * The whole `pointIn` judgment (operands already sealed interval-left):
689
+ * the point's domain must BE the interval's element domain — `pointIn(t,
690
+ * w)` with an i64-typed `t` against `interval(u64)` is the engine's
691
+ * `IllegalComparison`, judged here first.
692
+ */
693
+ type PointInPairOk<L, R> =
694
+ CmpVarSideOk<L, R> extends true ? ElementMeets<IntervalElementDomain<L>, PointDomain<R>> : false
695
+
696
+ /**
697
+ * The whole `allen` judgment: two interval sides of ONE element domain
698
+ * (Q1 — widths meet freely, u64-vs-i64 stays illegal), judged pairwise.
699
+ */
700
+ type AllenPairOk<L, R> =
701
+ CmpVarSideOk<L, R> extends true ? ElementMeets<IntervalElementDomain<L>, IntervalElementDomain<R>> : false
575
702
 
576
703
  /** The `eq`/`ne` judgment: var-var joins by mint slot; var-literal is exact-typed by the var's own field. */
577
704
  type EqOk<Classes extends SchemaClasses, L, R> = L extends AnyVar
@@ -600,55 +727,25 @@ type NotOk<Classes extends SchemaClasses, F extends FieldsShape, CR, B> = false
600
727
  ? false
601
728
  : true
602
729
 
603
- /** Reads a rec target's sealed head signature off its inference slot (`undefined` on an unthreaded handle). */
604
- type RecHeadOf<T> = T extends { readonly [inferred]?: infer S }
605
- ? Exclude<S, undefined> extends { readonly head: infer H }
606
- ? H
607
- : undefined
608
- : undefined
609
-
610
- /** Reads a rec target's params record off its inference slot. */
611
- type RecParamsOf<T> = T extends { readonly [inferred]?: infer S }
612
- ? Exclude<S, undefined> extends { readonly params: infer P }
613
- ? P
614
- : never
615
- : never
616
-
617
- /** One negated finished-stratum position's judgment: a variable, class-equal to its head slot when the head is carried. */
618
- type NotIdbBindingOk<Classes extends SchemaClasses, HeadSlot, V> = V extends AnyVar
619
- ? HeadSlot extends ClassedField
620
- ? JoinOk<HeadSlot, MintSlotOf<Classes, V>> extends true
621
- ? true
622
- : false
623
- : true
624
- : false
730
+ /** One negated finished-table position's judgment: a variable. */
731
+ type NotInteriorBindingOk<V> = V extends AnyVar ? true : false
625
732
 
626
733
  /**
627
- * The whole negated finished-stratum atom's judgment — the negation twin of
628
- * the `idb()` chain's `CheckIdbBindings`: when the target carries its head
629
- * (a threaded rec handle), the bindings record's key set must EXACTLY equal
630
- * the head's and each variable must be class-equal to its head slot; an
631
- * unthreaded handle still takes variables only.
734
+ * The whole negated finished-table atom's judgment — variables only;
735
+ * arity and class against the named table's head are construction-time
736
+ * (the name is a string, so the head is not a type-level fact).
632
737
  */
633
- type NotIdbOk<Classes extends SchemaClasses, Head, B> =
634
- Head extends Readonly<Record<string, ClassedField>>
635
- ? [keyof B] extends [keyof Head]
636
- ? [keyof Head] extends [keyof B]
637
- ? false extends { [K in keyof B]: NotIdbBindingOk<Classes, Head[K & keyof Head], B[K]> }[keyof B]
638
- ? false
639
- : true
640
- : false
641
- : false
642
- : false extends { [K in keyof B]: B[K] extends AnyVar ? true : false }[keyof B]
643
- ? false
644
- : true
738
+ type NotInteriorOk<B> = false extends { [K in keyof B]: NotInteriorBindingOk<B[K]> }[keyof B] ? false : true
645
739
 
646
740
  /**
647
741
  * One condition's judgment — the type-level twin of the engine's comparison
648
- * roster: class-equal joins (off the mint slots), orderable order sides (an
649
- * interval var under a non-`pointIn` op is exactly here refused),
650
- * kind-correct `pointIn`/`allen` sides, and negated-atom class safety. The
651
- * leading `[AnyTreeChild] extends [C]` arm is the recursion's base case.
742
+ * roster: class-equal joins (off the mint slots), PAIRWISE-judged order
743
+ * domains (an interval var under a non-`pointIn` op is refused per side;
744
+ * bool-vs-numeric, u64-vs-i64, and measure-vs-non-u64 are refused as
745
+ * pairs — the engine's same-type rule), element-matched `pointIn`/`allen`
746
+ * pairs, the no-variable-side (constant comparison) rule, and
747
+ * negated-atom class safety. The leading `[AnyTreeChild] extends [C]` arm
748
+ * is the recursion's base case.
652
749
  */
653
750
  type CondOkBool<Classes extends SchemaClasses, C> = [AnyTreeChild] extends [C]
654
751
  ? true
@@ -656,24 +753,18 @@ type CondOkBool<Classes extends SchemaClasses, C> = [AnyTreeChild] extends [C]
656
753
  ? Op extends "eq" | "ne"
657
754
  ? EqOk<Classes, L, R>
658
755
  : Op extends "lt" | "le" | "gt" | "ge"
659
- ? [OrderSideOk<L>, OrderSideOk<R>] extends [true, true]
660
- ? true
661
- : false
756
+ ? OrderPairOk<L, R>
662
757
  : Op extends "pointIn"
663
- ? [IntervalSideOk<L>, PointSideOk<R>] extends [true, true]
664
- ? true
665
- : false
758
+ ? PointInPairOk<L, R>
666
759
  : Op extends "allen"
667
- ? [IntervalSideOk<L>, IntervalSideOk<R>] extends [true, true]
668
- ? true
669
- : false
760
+ ? AllenPairOk<L, R>
670
761
  : false
671
762
  : C extends Tree<infer Ch extends readonly AnyTreeChild[]>
672
763
  ? false extends CondOkBool<Classes, Ch[number]>
673
764
  ? false
674
765
  : true
675
- : C extends NotIdbAtom<infer T extends RecTarget, infer B>
676
- ? NotIdbOk<Classes, RecHeadOf<T>, B>
766
+ : C extends NotInteriorAtom<infer B>
767
+ ? NotInteriorOk<B>
677
768
  : C extends NotAtom<infer R extends MatchOwner, infer B>
678
769
  ? NotOk<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
679
770
  : false
@@ -706,9 +797,6 @@ type PointParams<T> = T extends Param<infer P extends string> ? { readonly [Q in
706
797
  /** An interval side's params contribution. */
707
798
  type IntervalSideParams<T> = T extends Param<infer P extends string> ? { readonly [Q in P]: IntervalValue } : never
708
799
 
709
- /** The mask position's params contribution. */
710
- type MaskParams<M> = M extends MaskParam<infer P extends string> ? { readonly [Q in P]: number } : never
711
-
712
800
  /**
713
801
  * One condition's params-object fragments (a union; the rule builder folds
714
802
  * them into the inferred `Params` record) — every param typed by its use.
@@ -716,7 +804,7 @@ type MaskParams<M> = M extends MaskParam<infer P extends string> ? { readonly [Q
716
804
  */
717
805
  type CondParams<C> = [AnyTreeChild] extends [C]
718
806
  ? never
719
- : C extends Cmp<infer Op, infer L, infer R, infer M>
807
+ : C extends Cmp<infer Op, infer L, infer R, infer _M>
720
808
  ? Op extends "eq" | "ne"
721
809
  ? EqParams<L, R>
722
810
  : Op extends "lt" | "le" | "gt" | "ge"
@@ -724,12 +812,12 @@ type CondParams<C> = [AnyTreeChild] extends [C]
724
812
  : Op extends "pointIn"
725
813
  ? IntervalSideParams<L> | PointParams<R>
726
814
  : Op extends "allen"
727
- ? IntervalSideParams<L> | IntervalSideParams<R> | MaskParams<M>
815
+ ? IntervalSideParams<L> | IntervalSideParams<R>
728
816
  : never
729
817
  : C extends Tree<infer Ch extends readonly AnyTreeChild[]>
730
818
  ? CondParams<Ch[number]>
731
- : C extends NotIdbAtom<infer T extends RecTarget, unknown>
732
- ? RecParamsOf<T>
819
+ : C extends NotInteriorAtom<unknown>
820
+ ? never
733
821
  : C extends NotAtom<infer R extends MatchOwner, infer B>
734
822
  ? BindParams<MatchFields<R>, B>
735
823
  : never
@@ -762,23 +850,25 @@ export type {
762
850
  CondOkBool,
763
851
  CondParams,
764
852
  CondParamsShape,
853
+ DerivedTable,
765
854
  FindColumn,
766
855
  FindEntryData,
856
+ InteriorData,
767
857
  IntervalSide,
768
858
  IntervalVarOk,
769
- MaskData,
770
859
  MatchFields,
771
860
  MatchOwner,
772
861
  MatchShape,
773
862
  NotAtom,
774
- NotIdbAtom,
863
+ NotInteriorAtom,
775
864
  NumericVarOk,
776
865
  OrderSide,
777
866
  OrderVarOk,
778
867
  ParamUse,
779
868
  PointSide,
780
869
  RecData,
781
- RecTarget,
870
+ RecHandle,
871
+ RecHead,
782
872
  RuleData,
783
873
  RuleItem,
784
874
  SlotAt,