@bjornpagen/bumbledb 0.10.0 → 0.12.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 (69) hide show
  1. package/COOKBOOK.md +155 -136
  2. package/README.md +5 -9
  3. package/dist/db.d.ts +77 -109
  4. package/dist/db.d.ts.map +1 -1
  5. package/dist/db.js +121 -339
  6. package/dist/db.js.map +1 -1
  7. package/dist/index.d.ts +12 -17
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +8 -11
  10. package/dist/index.js.map +1 -1
  11. package/dist/lower.d.ts.map +1 -1
  12. package/dist/lower.js +8 -1
  13. package/dist/lower.js.map +1 -1
  14. package/dist/native.d.ts +77 -51
  15. package/dist/native.d.ts.map +1 -1
  16. package/dist/native.js.map +1 -1
  17. package/dist/query/atom.d.ts +94 -112
  18. package/dist/query/atom.d.ts.map +1 -1
  19. package/dist/query/atom.js +8 -17
  20. package/dist/query/atom.js.map +1 -1
  21. package/dist/query/find.d.ts +18 -35
  22. package/dist/query/find.d.ts.map +1 -1
  23. package/dist/query/find.js +13 -32
  24. package/dist/query/find.js.map +1 -1
  25. package/dist/query/lower.d.ts +128 -124
  26. package/dist/query/lower.d.ts.map +1 -1
  27. package/dist/query/lower.js +366 -258
  28. package/dist/query/lower.js.map +1 -1
  29. package/dist/query/parse-ir.d.ts +12 -0
  30. package/dist/query/parse-ir.d.ts.map +1 -0
  31. package/dist/query/parse-ir.js +71 -0
  32. package/dist/query/parse-ir.js.map +1 -0
  33. package/dist/query/run.d.ts +2 -2
  34. package/dist/query/run.d.ts.map +1 -1
  35. package/dist/query/run.js +2 -13
  36. package/dist/query/run.js.map +1 -1
  37. package/dist/query/scope.d.ts +4 -16
  38. package/dist/query/scope.d.ts.map +1 -1
  39. package/dist/query/scope.js +1 -6
  40. package/dist/query/scope.js.map +1 -1
  41. package/dist/schema.js +2 -2
  42. package/dist/schema.js.map +1 -1
  43. package/dist/statements.d.ts +8 -4
  44. package/dist/statements.d.ts.map +1 -1
  45. package/dist/statements.js +7 -9
  46. package/dist/statements.js.map +1 -1
  47. package/package.json +2 -2
  48. package/src/db.ts +182 -443
  49. package/src/index.ts +11 -24
  50. package/src/lower.ts +8 -1
  51. package/src/native.ts +79 -43
  52. package/src/query/atom.ts +105 -138
  53. package/src/query/find.ts +39 -80
  54. package/src/query/lower.ts +626 -434
  55. package/src/query/parse-ir.ts +82 -0
  56. package/src/query/run.ts +2 -14
  57. package/src/query/scope.ts +3 -21
  58. package/src/schema.ts +2 -2
  59. package/src/statements.ts +17 -13
  60. package/dist/order.d.ts +0 -87
  61. package/dist/order.d.ts.map +0 -1
  62. package/dist/order.js +0 -153
  63. package/dist/order.js.map +0 -1
  64. package/dist/query/predicate.d.ts +0 -91
  65. package/dist/query/predicate.d.ts.map +0 -1
  66. package/dist/query/predicate.js +0 -156
  67. package/dist/query/predicate.js.map +0 -1
  68. package/src/order.ts +0 -234
  69. 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, term } 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 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
@@ -473,16 +485,15 @@ const ALLEN = Object.freeze({
473
485
  /**
474
486
  * THE interval-pair comparison (`ir::CmpOp::Allen`): two interval terms of
475
487
  * one element type, satisfied iff the pair's classification is in the
476
- * 13-bit mask — a literal built from the `ALLEN` constants, or a mask
477
- * parameter (`r.maskParam`).
488
+ * 13-bit mask — a literal built from the `ALLEN` constants.
478
489
  */
479
- 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>(
480
491
  left: A,
481
- mask: M,
492
+ mask: number,
482
493
  right: B
483
- ): Cmp<"allen", A, B, M> {
494
+ ): Cmp<"allen", A, B, number> {
484
495
  assertTermSide("allen", left, right)
485
- if (typeof mask === "number" && (!Number.isInteger(mask) || mask < 0 || mask > ALLEN_ALL_BITS)) {
496
+ if (!Number.isInteger(mask) || mask < 0 || mask > ALLEN_ALL_BITS) {
486
497
  throw errors.new(
487
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)`
488
499
  )
@@ -515,35 +526,25 @@ function or<const C extends readonly AnyTreeChild[]>(...children: C): Tree<C> {
515
526
  * nothing, only rejects: every variable it names must be positively bound
516
527
  * in the rule, a construction-time wall (the engine's safety refusal stands
517
528
  * behind it). A CLOSED owner is legal here too — and so is a FINISHED
518
- * STRATUM: `not(reach, { c })` in an output rule negates the rec's
519
- * finished set (a named record over its head keys, variables only — the
520
- * same wall `idb()` holds), the one spelling of the engine-legal
521
- * 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.
522
532
  */
523
533
  function not<R extends MatchOwner, const B extends MatchShape<MatchFields<R>>>(relation: R, bindings: B): NotAtom<R, B>
524
- function not<Target extends RecTarget, const B extends Readonly<Record<string, AnyVar>>>(
525
- target: Target,
534
+ function not<const Name extends string, const B extends Readonly<Record<string, AnyVar>>>(
535
+ name: Name,
526
536
  bindings: B
527
- ): NotIdbAtom<Target, B>
537
+ ): NotInteriorAtom<B>
528
538
  function not(
529
- relation: MatchOwner | RecTarget,
539
+ relation: MatchOwner | string,
530
540
  bindings: Readonly<Record<string, unknown>>
531
- ): NotAtom<MatchOwner, unknown> | NotIdbAtom<RecTarget, unknown> {
532
- if (isRecTarget(relation)) {
533
- 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 })
534
544
  }
535
545
  return Object.freeze({ cond: "not" as const, relation, bindings })
536
546
  }
537
547
 
538
- /**
539
- * THE negation-target discriminant: a rec's runtime data carries its rules,
540
- * a relation's its fields (and a closed relation's its handle roster) — the
541
- * shapes are disjoint by construction, so the dispatch is total.
542
- */
543
- function isRecTarget(value: MatchOwner | RecTarget): value is RecTarget {
544
- return "rules" in value.data
545
- }
546
-
547
548
  /**
548
549
  * Whether a variable's OWN field is NUMERIC (u64/i64) — the judgment the
549
550
  * point side of `pointIn` and the `sum` input read: a point lives in the
@@ -726,48 +727,15 @@ type NotOk<Classes extends SchemaClasses, F extends FieldsShape, CR, B> = false
726
727
  ? false
727
728
  : true
728
729
 
729
- /** Reads a rec target's sealed head signature off its inference slot (`undefined` on an unthreaded handle). */
730
- type RecHeadOf<T> = T extends { readonly [inferred]?: infer S }
731
- ? Exclude<S, undefined> extends { readonly head: infer H }
732
- ? H
733
- : undefined
734
- : undefined
735
-
736
- /** Reads a rec target's params record off its inference slot. */
737
- type RecParamsOf<T> = T extends { readonly [inferred]?: infer S }
738
- ? Exclude<S, undefined> extends { readonly params: infer P }
739
- ? P
740
- : never
741
- : never
742
-
743
- /** One negated finished-stratum position's judgment: a variable, class-equal to its head slot when the head is carried. */
744
- type NotIdbBindingOk<Classes extends SchemaClasses, HeadSlot, V> = V extends AnyVar
745
- ? HeadSlot extends ClassedField
746
- ? JoinOk<HeadSlot, MintSlotOf<Classes, V>> extends true
747
- ? true
748
- : false
749
- : true
750
- : false
730
+ /** One negated finished-table position's judgment: a variable. */
731
+ type NotInteriorBindingOk<V> = V extends AnyVar ? true : false
751
732
 
752
733
  /**
753
- * The whole negated finished-stratum atom's judgment — the negation twin of
754
- * the `idb()` chain's `CheckIdbBindings`: when the target carries its head
755
- * (a threaded rec handle), the bindings record's key set must EXACTLY equal
756
- * the head's and each variable must be class-equal to its head slot; an
757
- * 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).
758
737
  */
759
- type NotIdbOk<Classes extends SchemaClasses, Head, B> =
760
- Head extends Readonly<Record<string, ClassedField>>
761
- ? [keyof B] extends [keyof Head]
762
- ? [keyof Head] extends [keyof B]
763
- ? false extends { [K in keyof B]: NotIdbBindingOk<Classes, Head[K & keyof Head], B[K]> }[keyof B]
764
- ? false
765
- : true
766
- : false
767
- : false
768
- : false extends { [K in keyof B]: B[K] extends AnyVar ? true : false }[keyof B]
769
- ? false
770
- : true
738
+ type NotInteriorOk<B> = false extends { [K in keyof B]: NotInteriorBindingOk<B[K]> }[keyof B] ? false : true
771
739
 
772
740
  /**
773
741
  * One condition's judgment — the type-level twin of the engine's comparison
@@ -795,8 +763,8 @@ type CondOkBool<Classes extends SchemaClasses, C> = [AnyTreeChild] extends [C]
795
763
  ? false extends CondOkBool<Classes, Ch[number]>
796
764
  ? false
797
765
  : true
798
- : C extends NotIdbAtom<infer T extends RecTarget, infer B>
799
- ? NotIdbOk<Classes, RecHeadOf<T>, B>
766
+ : C extends NotInteriorAtom<infer B>
767
+ ? NotInteriorOk<B>
800
768
  : C extends NotAtom<infer R extends MatchOwner, infer B>
801
769
  ? NotOk<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
802
770
  : false
@@ -829,9 +797,6 @@ type PointParams<T> = T extends Param<infer P extends string> ? { readonly [Q in
829
797
  /** An interval side's params contribution. */
830
798
  type IntervalSideParams<T> = T extends Param<infer P extends string> ? { readonly [Q in P]: IntervalValue } : never
831
799
 
832
- /** The mask position's params contribution. */
833
- type MaskParams<M> = M extends MaskParam<infer P extends string> ? { readonly [Q in P]: number } : never
834
-
835
800
  /**
836
801
  * One condition's params-object fragments (a union; the rule builder folds
837
802
  * them into the inferred `Params` record) — every param typed by its use.
@@ -839,7 +804,7 @@ type MaskParams<M> = M extends MaskParam<infer P extends string> ? { readonly [Q
839
804
  */
840
805
  type CondParams<C> = [AnyTreeChild] extends [C]
841
806
  ? never
842
- : C extends Cmp<infer Op, infer L, infer R, infer M>
807
+ : C extends Cmp<infer Op, infer L, infer R, infer _M>
843
808
  ? Op extends "eq" | "ne"
844
809
  ? EqParams<L, R>
845
810
  : Op extends "lt" | "le" | "gt" | "ge"
@@ -847,12 +812,12 @@ type CondParams<C> = [AnyTreeChild] extends [C]
847
812
  : Op extends "pointIn"
848
813
  ? IntervalSideParams<L> | PointParams<R>
849
814
  : Op extends "allen"
850
- ? IntervalSideParams<L> | IntervalSideParams<R> | MaskParams<M>
815
+ ? IntervalSideParams<L> | IntervalSideParams<R>
851
816
  : never
852
817
  : C extends Tree<infer Ch extends readonly AnyTreeChild[]>
853
818
  ? CondParams<Ch[number]>
854
- : C extends NotIdbAtom<infer T extends RecTarget, unknown>
855
- ? RecParamsOf<T>
819
+ : C extends NotInteriorAtom<unknown>
820
+ ? never
856
821
  : C extends NotAtom<infer R extends MatchOwner, infer B>
857
822
  ? BindParams<MatchFields<R>, B>
858
823
  : never
@@ -885,23 +850,25 @@ export type {
885
850
  CondOkBool,
886
851
  CondParams,
887
852
  CondParamsShape,
853
+ DerivedTable,
888
854
  FindColumn,
889
855
  FindEntryData,
856
+ InteriorData,
890
857
  IntervalSide,
891
858
  IntervalVarOk,
892
- MaskData,
893
859
  MatchFields,
894
860
  MatchOwner,
895
861
  MatchShape,
896
862
  NotAtom,
897
- NotIdbAtom,
863
+ NotInteriorAtom,
898
864
  NumericVarOk,
899
865
  OrderSide,
900
866
  OrderVarOk,
901
867
  ParamUse,
902
868
  PointSide,
903
869
  RecData,
904
- RecTarget,
870
+ RecHandle,
871
+ RecHead,
905
872
  RuleData,
906
873
  RuleItem,
907
874
  SlotAt,
package/src/query/find.ts CHANGED
@@ -3,11 +3,10 @@
3
3
  * vocabulary, mirroring the IR's aggregate roster exactly
4
4
  * (`bumbledb/crates/bumbledb/src/ir.rs` `AggOp`/`FindTerm`;
5
5
  * `docs/architecture/20-query-ir.md` § aggregation): `count` (nullary),
6
- * `countDistinct`, `sum`/`min`/`max` (over an orderable variable or the
7
- * measure), `argMax`/`argMin` (arg-restriction: carried value + orderable
8
- * key), and `pack` (the coalescing fold — RELATION-SHAPED, the result
9
- * position interval-typed). Aggregates fold VARIABLES by reference —
10
- * `r.sum(w)` — and are typed by the variable's own descriptor.
6
+ * `sum`/`min`/`max` (over an orderable variable or the measure), and
7
+ * `pack` (the coalescing fold — RELATION-SHAPED, the result position
8
+ * interval-typed). Aggregates fold VARIABLES by reference — `r.sum(w)` —
9
+ * and are typed by the variable's own descriptor.
11
10
  *
12
11
  * `select(strings)` is DEAD: the head is a `find` RECORD, whose KEYS name
13
12
  * the answer columns (`find({ total: r.count(), owner: h })`). The keys are
@@ -25,25 +24,19 @@ import type { IntervalVarOk, NumericVarOk, OrderVarOk } from "#query/atom.ts"
25
24
  import type { AnyVar, Duration, MintSlotOf } from "#query/scope.ts"
26
25
 
27
26
  /** One aggregate operator name of the find vocabulary. */
28
- type AggOpName = "count" | "countDistinct" | "sum" | "min" | "max" | "argMax" | "argMin" | "pack"
27
+ type AggOpName = "count" | "sum" | "min" | "max" | "pack"
29
28
 
30
29
  /**
31
- * One aggregate find VALUE: the op, the variable (or measure) it folds BY
32
- * REFERENCE, and — for the Arg forms — the orderable key variable. The
33
- * variable's own descriptor types the result.
30
+ * One aggregate find VALUE: the op and the variable (or measure) it folds
31
+ * BY REFERENCE. The variable's own descriptor types the result.
34
32
  */
35
- interface Agg<
36
- Op extends AggOpName,
37
- Over extends AnyVar | Duration | undefined,
38
- Key extends AnyVar | undefined = undefined
39
- > {
33
+ interface Agg<Op extends AggOpName, Over extends AnyVar | Duration | undefined> {
40
34
  readonly agg: Op
41
35
  readonly over: Over
42
- readonly key: Key
43
36
  }
44
37
 
45
38
  /** Any aggregate find value. */
46
- type AnyAgg = Agg<AggOpName, AnyVar | Duration | undefined, AnyVar | undefined>
39
+ type AnyAgg = Agg<AggOpName, AnyVar | Duration | undefined>
47
40
 
48
41
  /** One find entry: a projected variable, the measure, or an aggregate. */
49
42
  type FindEntry = AnyVar | Duration | AnyAgg
@@ -52,22 +45,16 @@ type FindEntry = AnyVar | Duration | AnyAgg
52
45
  type FindShape = Readonly<Record<string, FindEntry>>
53
46
 
54
47
  /** Builds one aggregate value. */
55
- function aggregate<Op extends AggOpName, Over extends AnyVar | Duration | undefined, Key extends AnyVar | undefined>(
48
+ function aggregate<Op extends AggOpName, Over extends AnyVar | Duration | undefined>(
56
49
  op: Op,
57
- over: Over,
58
- key: Key
59
- ): Agg<Op, Over, Key> {
60
- return Object.freeze({ agg: op, over, key })
50
+ over: Over
51
+ ): Agg<Op, Over> {
52
+ return Object.freeze({ agg: op, over })
61
53
  }
62
54
 
63
55
  /** Nullary count: |the group's set of distinct full bindings|, `bigint`. */
64
56
  function count(): Agg<"count", undefined> {
65
- return aggregate("count", undefined, undefined)
66
- }
67
-
68
- /** |the distinct values of the variable across the group|, `bigint`; legal over every type. */
69
- function countDistinct<const V extends AnyVar>(over: V): Agg<"countDistinct", V> {
70
- return aggregate("countDistinct", over, undefined)
57
+ return aggregate("count", undefined)
71
58
  }
72
59
 
73
60
  /**
@@ -77,33 +64,17 @@ function countDistinct<const V extends AnyVar>(over: V): Agg<"countDistinct", V>
77
64
  * quantifier is not an addition (R3).
78
65
  */
79
66
  function sum<const O extends AnyVar | Duration>(over: O): Agg<"sum", O> {
80
- return aggregate("sum", over, undefined)
67
+ return aggregate("sum", over)
81
68
  }
82
69
 
83
70
  /** Minimum over an orderable variable (u64/i64/bool — over bool, `Min` is the ALL quantifier; R3) or the measure. */
84
71
  function min<const O extends AnyVar | Duration>(over: O): Agg<"min", O> {
85
- return aggregate("min", over, undefined)
72
+ return aggregate("min", over)
86
73
  }
87
74
 
88
75
  /** Maximum over an orderable variable (u64/i64/bool — over bool, `Max` is the ANY quantifier; R3) or the measure. */
89
76
  function max<const O extends AnyVar | Duration>(over: O): Agg<"max", O> {
90
- return aggregate("max", over, undefined)
91
- }
92
-
93
- /**
94
- * Arg-restriction toward the maximum of `key` (`ir::AggOp::ArgMax`): the
95
- * group's binding set is restricted to the bindings attaining the extreme
96
- * of the orderable key, and `value` is the carried payload — a tie yields
97
- * every attaining row. All Arg entries of one query share one key and one
98
- * direction; Arg and fold aggregates never mix (both the engine's rules).
99
- */
100
- function argMax<const V extends AnyVar, const K extends AnyVar>(value: V, key: K): Agg<"argMax", V, K> {
101
- return aggregate("argMax", value, key)
102
- }
103
-
104
- /** Arg-restriction toward the minimum of `key`; rules as {@link argMax}. */
105
- function argMin<const V extends AnyVar, const K extends AnyVar>(value: V, key: K): Agg<"argMin", V, K> {
106
- return aggregate("argMin", value, key)
77
+ return aggregate("max", over)
107
78
  }
108
79
 
109
80
  /**
@@ -111,10 +82,10 @@ function argMin<const V extends AnyVar, const K extends AnyVar>(value: V, key: K
111
82
  * the maximal disjoint half-open segments of the union of the group's
112
83
  * interval point sets — RELATION-SHAPED, one answer row per (group, maximal
113
84
  * segment), the result position carrying one interval of the input's element
114
- * type. At most one `pack` per find, never beside a fold or an Arg entry.
85
+ * type. At most one `pack` per find, never beside a fold entry.
115
86
  */
116
87
  function pack<const V extends AnyVar>(over: V): Agg<"pack", V> {
117
- return aggregate("pack", over, undefined)
88
+ return aggregate("pack", over)
118
89
  }
119
90
 
120
91
  /**
@@ -142,7 +113,7 @@ type FoldOverOk<O> = O extends AnyVar
142
113
  /**
143
114
  * One find entry's judgment (off the entry's own descriptor — no env, no
144
115
  * class map needed): a projected variable is ok, the measure and `pack`
145
- * demand interval-typed variables, folds and Arg keys demand orderable ones.
116
+ * demand interval-typed variables, folds demand orderable ones.
146
117
  */
147
118
  type FindEntryOk<E> = E extends AnyVar
148
119
  ? true
@@ -150,19 +121,13 @@ type FindEntryOk<E> = E extends AnyVar
150
121
  ? IntervalVarOk<V>
151
122
  : E extends Agg<"count", undefined>
152
123
  ? true
153
- : E extends Agg<"countDistinct", AnyVar>
154
- ? true
155
- : E extends Agg<"sum", infer O>
156
- ? SumOverOk<O>
157
- : E extends Agg<"min" | "max", infer O>
158
- ? FoldOverOk<O>
159
- : E extends Agg<"argMax" | "argMin", AnyVar, infer K extends AnyVar>
160
- ? OrderVarOk<K> extends true
161
- ? true
162
- : false
163
- : E extends Agg<"pack", infer V extends AnyVar>
164
- ? IntervalVarOk<V>
165
- : false
124
+ : E extends Agg<"sum", infer O>
125
+ ? SumOverOk<O>
126
+ : E extends Agg<"min" | "max", infer O>
127
+ ? FoldOverOk<O>
128
+ : E extends Agg<"pack", infer V extends AnyVar>
129
+ ? IntervalVarOk<V>
130
+ : false
166
131
 
167
132
  /** The validated find record (intersect with the inferred entries — errors land on the offending key). */
168
133
  type CheckFind<F extends FindShape> = {
@@ -171,8 +136,7 @@ type CheckFind<F extends FindShape> = {
171
136
 
172
137
  /**
173
138
  * The validated find record of a RECURSIVE rule: every entry must be a plain
174
- * variable (aggregates and the measure are unwritable in a recursive head —
175
- * the strata quarantine).
139
+ * variable (aggregates and the measure are unwritable in a rec head).
176
140
  */
177
141
  type CheckRecFind<F extends FindShape> = {
178
142
  readonly [K in keyof F]: F[K] extends AnyVar ? F[K] : never
@@ -180,9 +144,8 @@ type CheckRecFind<F extends FindShape> = {
180
144
 
181
145
  /**
182
146
  * One find entry's answer-column value type: a variable carries its field's
183
- * type, the measure/count/countDistinct are `bigint`, folds carry their
184
- * input's type (a measure fold is `bigint`), the Arg forms carry the
185
- * payload's type, `pack` its interval type.
147
+ * type, the measure/count are `bigint`, folds carry their input's type
148
+ * (a measure fold is `bigint`), `pack` its interval type.
186
149
  */
187
150
  type FindValue<E> = E extends AnyVar
188
151
  ? Infer<E["field"]>
@@ -190,17 +153,13 @@ type FindValue<E> = E extends AnyVar
190
153
  ? bigint
191
154
  : E extends Agg<"count", undefined>
192
155
  ? bigint
193
- : E extends Agg<"countDistinct", AnyVar>
194
- ? bigint
195
- : E extends Agg<"sum" | "min" | "max", infer O>
196
- ? O extends AnyVar
197
- ? Infer<O["field"]>
198
- : bigint
199
- : E extends Agg<"argMax" | "argMin", infer V extends AnyVar, AnyVar>
200
- ? Infer<V["field"]>
201
- : E extends Agg<"pack", infer V extends AnyVar>
202
- ? Infer<V["field"]>
203
- : never
156
+ : E extends Agg<"sum" | "min" | "max", infer O>
157
+ ? O extends AnyVar
158
+ ? Infer<O["field"]>
159
+ : bigint
160
+ : E extends Agg<"pack", infer V extends AnyVar>
161
+ ? Infer<V["field"]>
162
+ : never
204
163
 
205
164
  /** The inferred answer-row object type of a find record — the keys ARE the columns. */
206
165
  type RowOfFind<F extends FindShape> = { readonly [K in keyof F]: FindValue<F[K]> }
@@ -208,7 +167,7 @@ type RowOfFind<F extends FindShape> = { readonly [K in keyof F]: FindValue<F[K]>
208
167
  /**
209
168
  * The head signature of a recursive rule's find record as classed mint
210
169
  * slots (descriptor + law-computed class), keyed by column name — the
211
- * signature an `idb` join pairs against (`F` is variable-only there).
170
+ * signature an interior join pairs against (`F` is variable-only there).
212
171
  */
213
172
  type HeadRecordOf<Classes extends SchemaClasses, F extends FindShape> = {
214
173
  readonly [K in keyof F]: F[K] extends AnyVar ? MintSlotOf<Classes, F[K]> : never
@@ -227,4 +186,4 @@ export type {
227
186
  HeadRecordOf,
228
187
  RowOfFind
229
188
  }
230
- export { argMax, argMin, count, countDistinct, max, min, pack, sum }
189
+ export { count, max, min, pack, sum }