@bjornpagen/bumbledb 0.3.0 → 0.5.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 (98) hide show
  1. package/COOKBOOK.md +246 -92
  2. package/README.md +31 -16
  3. package/dist/closed.d.ts +80 -75
  4. package/dist/closed.d.ts.map +1 -1
  5. package/dist/closed.js +102 -127
  6. package/dist/closed.js.map +1 -1
  7. package/dist/db.d.ts +37 -7
  8. package/dist/db.d.ts.map +1 -1
  9. package/dist/db.js +95 -55
  10. package/dist/db.js.map +1 -1
  11. package/dist/exhume.d.ts.map +1 -1
  12. package/dist/exhume.js +1 -14
  13. package/dist/exhume.js.map +1 -1
  14. package/dist/face.d.ts +40 -40
  15. package/dist/face.d.ts.map +1 -1
  16. package/dist/face.js +9 -17
  17. package/dist/face.js.map +1 -1
  18. package/dist/fields.d.ts +46 -15
  19. package/dist/fields.d.ts.map +1 -1
  20. package/dist/fields.js +58 -29
  21. package/dist/fields.js.map +1 -1
  22. package/dist/index.d.ts +13 -8
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -5
  25. package/dist/index.js.map +1 -1
  26. package/dist/law.d.ts +2 -1
  27. package/dist/law.d.ts.map +1 -1
  28. package/dist/law.js +15 -14
  29. package/dist/law.js.map +1 -1
  30. package/dist/lower.d.ts.map +1 -1
  31. package/dist/lower.js +1 -7
  32. package/dist/lower.js.map +1 -1
  33. package/dist/marshal.d.ts +33 -6
  34. package/dist/marshal.d.ts.map +1 -1
  35. package/dist/marshal.js +75 -26
  36. package/dist/marshal.js.map +1 -1
  37. package/dist/native.d.ts +21 -2
  38. package/dist/native.d.ts.map +1 -1
  39. package/dist/native.js +20 -3
  40. package/dist/native.js.map +1 -1
  41. package/dist/order.d.ts +36 -0
  42. package/dist/order.d.ts.map +1 -0
  43. package/dist/order.js +135 -0
  44. package/dist/order.js.map +1 -0
  45. package/dist/query/atom.d.ts +76 -28
  46. package/dist/query/atom.d.ts.map +1 -1
  47. package/dist/query/atom.js +12 -16
  48. package/dist/query/atom.js.map +1 -1
  49. package/dist/query/lower.d.ts +5 -8
  50. package/dist/query/lower.d.ts.map +1 -1
  51. package/dist/query/lower.js +308 -72
  52. package/dist/query/lower.js.map +1 -1
  53. package/dist/query/predicate.d.ts.map +1 -1
  54. package/dist/query/predicate.js +34 -2
  55. package/dist/query/predicate.js.map +1 -1
  56. package/dist/query/run.d.ts +15 -5
  57. package/dist/query/run.d.ts.map +1 -1
  58. package/dist/query/run.js +27 -8
  59. package/dist/query/run.js.map +1 -1
  60. package/dist/query/scope.d.ts +37 -29
  61. package/dist/query/scope.d.ts.map +1 -1
  62. package/dist/query/scope.js +19 -47
  63. package/dist/query/scope.js.map +1 -1
  64. package/dist/relation.d.ts +17 -29
  65. package/dist/relation.d.ts.map +1 -1
  66. package/dist/relation.js +41 -38
  67. package/dist/relation.js.map +1 -1
  68. package/dist/schema.d.ts.map +1 -1
  69. package/dist/schema.js +7 -31
  70. package/dist/schema.js.map +1 -1
  71. package/dist/spec.d.ts +3 -2
  72. package/dist/spec.d.ts.map +1 -1
  73. package/dist/spec.js.map +1 -1
  74. package/dist/statements.d.ts +13 -4
  75. package/dist/statements.d.ts.map +1 -1
  76. package/dist/statements.js +75 -8
  77. package/dist/statements.js.map +1 -1
  78. package/package.json +2 -5
  79. package/src/closed.ts +144 -206
  80. package/src/db.ts +143 -68
  81. package/src/exhume.ts +1 -15
  82. package/src/face.ts +38 -48
  83. package/src/fields.ts +103 -49
  84. package/src/index.ts +11 -10
  85. package/src/law.ts +15 -14
  86. package/src/lower.ts +2 -9
  87. package/src/marshal.ts +82 -31
  88. package/src/native.ts +22 -4
  89. package/src/order.ts +156 -0
  90. package/src/query/atom.ts +70 -35
  91. package/src/query/lower.ts +354 -82
  92. package/src/query/predicate.ts +39 -4
  93. package/src/query/run.ts +27 -9
  94. package/src/query/scope.ts +52 -70
  95. package/src/relation.ts +54 -68
  96. package/src/schema.ts +7 -33
  97. package/src/spec.ts +3 -2
  98. package/src/statements.ts +82 -8
package/src/order.ts ADDED
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Host-side answer ordering — the census-fired convenience
3
+ * (`docs/architecture/70-api.md` § the freeze ledger). Answers are SETS and
4
+ * the ENGINE NEVER ORDERS; the language owns the sort (`rows.sort(...)`)
5
+ * and the limit (`.slice(0, n)`) — the drizzle law. What JS lacks is a
6
+ * number-returning comparator over the SDK's bigint-bearing cells, so the
7
+ * SDK ships exactly that: sort keys are DATA — a bare column name is
8
+ * ascending (the punning spelling; no `asc` wrapper exists — one spelling
9
+ * per meaning) and `desc(name)` is the one descending spelling — folded by
10
+ * `by(...)` into one row-typed comparator. Cross-type cells cannot arise
11
+ * within one column (one column, one domain), and the cell order is TOTAL
12
+ * anyway (the type-rank wall), so the comparator never throws.
13
+ */
14
+
15
+ import type { FactValue } from "#native.ts"
16
+
17
+ /** One DESCENDING sort key, plain data — built by {@link desc}. */
18
+ interface Desc<K extends string> {
19
+ readonly key: K
20
+ readonly desc: true
21
+ }
22
+
23
+ /** One sort key: a bare column name (ascending — the punning spelling) or `desc(name)`. */
24
+ type SortKey<K extends string> = K | Desc<K>
25
+
26
+ /**
27
+ * The type-rank wall: boolean 0, bigint 1, string 2, bytes 3, interval 4.
28
+ * One column carries one domain, so a mixed pair never arises from decoded
29
+ * answer rows — the wall exists to keep the cell order TOTAL (never a
30
+ * throw), not to be reached.
31
+ */
32
+ function cellRank(value: FactValue): number {
33
+ if (typeof value === "boolean") {
34
+ return 0
35
+ }
36
+ if (typeof value === "bigint") {
37
+ return 1
38
+ }
39
+ if (typeof value === "string") {
40
+ return 2
41
+ }
42
+ if (value instanceof Uint8Array) {
43
+ return 3
44
+ }
45
+ return 4
46
+ }
47
+
48
+ /**
49
+ * One cell against one cell. Same-type arms: boolean orders false < true;
50
+ * bigint by `<`/`>`; string by the host language's own `<`/`>` (flavor,
51
+ * recorded); bytes bytewise over the shared prefix, then by length;
52
+ * intervals by start, then end. A mixed pair falls through to the
53
+ * type-rank wall.
54
+ */
55
+ function cellCmp(left: FactValue, right: FactValue): number {
56
+ if (typeof left === "boolean" && typeof right === "boolean") {
57
+ if (left === right) {
58
+ return 0
59
+ }
60
+ if (left) {
61
+ return 1
62
+ }
63
+ return -1
64
+ }
65
+ if (typeof left === "bigint" && typeof right === "bigint") {
66
+ if (left < right) {
67
+ return -1
68
+ }
69
+ if (left > right) {
70
+ return 1
71
+ }
72
+ return 0
73
+ }
74
+ if (typeof left === "string" && typeof right === "string") {
75
+ if (left < right) {
76
+ return -1
77
+ }
78
+ if (left > right) {
79
+ return 1
80
+ }
81
+ return 0
82
+ }
83
+ if (left instanceof Uint8Array && right instanceof Uint8Array) {
84
+ const shared = Math.min(left.length, right.length)
85
+ for (let index = 0; index < shared; index += 1) {
86
+ const leftByte = left[index]
87
+ const rightByte = right[index]
88
+ // `index < shared` keeps both reads in bounds; the `undefined`
89
+ // arms are the checker's indexed-access tax, never taken.
90
+ if (leftByte !== undefined && rightByte !== undefined && leftByte !== rightByte) {
91
+ return leftByte - rightByte
92
+ }
93
+ }
94
+ return left.length - right.length
95
+ }
96
+ if (
97
+ typeof left === "object" &&
98
+ !(left instanceof Uint8Array) &&
99
+ typeof right === "object" &&
100
+ !(right instanceof Uint8Array)
101
+ ) {
102
+ if (left.start < right.start) {
103
+ return -1
104
+ }
105
+ if (left.start > right.start) {
106
+ return 1
107
+ }
108
+ if (left.end < right.end) {
109
+ return -1
110
+ }
111
+ if (left.end > right.end) {
112
+ return 1
113
+ }
114
+ return 0
115
+ }
116
+ return cellRank(left) - cellRank(right)
117
+ }
118
+
119
+ /** Marks one sort key DESCENDING — the single descending spelling (a bare name is already ascending). */
120
+ function desc<const K extends string>(key: K): Desc<K> {
121
+ const marker: Desc<K> = { key, desc: true }
122
+ return Object.freeze(marker)
123
+ }
124
+
125
+ /**
126
+ * Folds sort keys into ONE comparator typed against the row —
127
+ * `Row extends Readonly<Record<K, FactValue>>` — so a key the row lacks, or
128
+ * a column typed `number` (outside the cell domain), is a COMPILE error at
129
+ * the `.sort` call site: the laws typed the columns, and the row type
130
+ * carries that proof here (parse-don't-validate). The generic RETURN is the
131
+ * load-bearing trick: `rows.sort(by("rank"))` instantiates `Row` from the
132
+ * array's own element type and checks the key set right there.
133
+ */
134
+ function by<const K extends string>(
135
+ first: SortKey<K>,
136
+ ...rest: ReadonlyArray<SortKey<K>>
137
+ ): <Row extends Readonly<Record<K, FactValue>>>(left: Row, right: Row) => number {
138
+ const entries = [first, ...rest].map(function normalizeKey(sortKey): { readonly key: K; readonly factor: 1 | -1 } {
139
+ if (typeof sortKey === "string") {
140
+ return { key: sortKey, factor: 1 }
141
+ }
142
+ return { key: sortKey.key, factor: -1 }
143
+ })
144
+ return function compare<Row extends Readonly<Record<K, FactValue>>>(left: Row, right: Row): number {
145
+ for (const entry of entries) {
146
+ const order = cellCmp(left[entry.key], right[entry.key]) * entry.factor
147
+ if (order !== 0) {
148
+ return order
149
+ }
150
+ }
151
+ return 0
152
+ }
153
+ }
154
+
155
+ export type { Desc, SortKey }
156
+ export { by, desc }
package/src/query/atom.ts CHANGED
@@ -4,9 +4,12 @@
4
4
  * (`bumbledb/crates/bumbledb/src/ir.rs`, the bijection target;
5
5
  * `docs/architecture/20-query-ir.md` normative). A `match` binding record
6
6
  * binds fields to vars, params, ∈-set params, or bare structural literals
7
+ * — a closed-reference field's literal is its handle NAME, and a plain
8
+ * ARRAY of names there is membership, folded into the program (closed-only
9
+ * by owner ruling; see {@link BindingInput})
7
10
  * (unmentioned fields ARE the wildcard — no wildcard value exists);
8
11
  * `not(Rel, {...})` is negation-as-position (anti-join); `eq`/`ne` and the
9
- * order roster, `pointIn`/`covers` (both spellings of `ir::CmpOp::PointIn`,
12
+ * order roster, `pointIn` (the one spelling of `ir::CmpOp::PointIn`,
10
13
  * always lowered interval-left), `allen` (the 13-bit mask pair
11
14
  * comparison), and `and`/`or` (the input condition-tree grammar) complete
12
15
  * the roster. Nothing beyond the IR exists here — and the walls the engine
@@ -26,7 +29,7 @@
26
29
 
27
30
  import * as errors from "@superbuilders/errors"
28
31
  import type { AnyClosed } from "#closed.ts"
29
- import type { AnyField, ClosedIdField, Infer, IntervalValue } from "#fields.ts"
32
+ import type { AnyField, ClosedIdField, ClosedRoster, Infer, IntervalValue } from "#fields.ts"
30
33
  import type { ClassLookup, ClassRecordOf, SchemaClasses } from "#law.ts"
31
34
  import type {
32
35
  ClassedField,
@@ -55,23 +58,32 @@ type MatchOwner = AnyRelation | AnyClosed
55
58
  /**
56
59
  * The matchable field block of an atom owner: a relation's declared
57
60
  * fields; a closed relation's SEALED shape — the synthetic `id` (the
58
- * roster-carrying descriptor) first, then the declared payload columns
59
- * read through the typed `columns` carrier (the one source of payload
60
- * typing — no parallel column table exists). The runtime twin is
61
- * `matchFieldsOf` in `#query/lower.ts`; the id-first ordinal shift the two
62
- * tiers share is pinned by the lowering golden.
61
+ * value's OWN roster-carrying descriptor, at its precise type: the handle
62
+ * union rides into ψ id bindings and joins exactly as it does on a
63
+ * referencing column) first, then the declared payload columns read
64
+ * through the typed `columns` carrier (the one source of payload typing —
65
+ * no parallel column table exists). The runtime twin is `sealedFieldsOf` in
66
+ * `#query/lower.ts`; the id-first ordinal shift the two tiers share is
67
+ * pinned by the lowering golden.
63
68
  */
64
69
  type MatchFields<R extends MatchOwner> = R extends AnyClosed
65
- ? { readonly id: ClosedIdField } & R["columns"]
70
+ ? { readonly id: R["id"] } & R["columns"]
66
71
  : R extends AnyRelation
67
72
  ? RelationFields<R>
68
73
  : never
69
74
 
70
- /** One atom-binding position as runtime data. */
75
+ /**
76
+ * One atom-binding position as runtime data. `literalSet` is a membership
77
+ * ARRAY at a closed-reference field, folded into the program: `name` is
78
+ * the content-addressed registry key its dense `ParamId` is minted under
79
+ * (the lowering rides the existing param-set term; the SDK itself supplies
80
+ * the translated member set at every execute — never the host).
81
+ */
71
82
  type BindingTermData =
72
83
  | { readonly kind: "var"; readonly name: string }
73
84
  | { readonly kind: "param"; readonly name: string }
74
85
  | { readonly kind: "setParam"; readonly name: string }
86
+ | { readonly kind: "literalSet"; readonly name: string; readonly members: readonly string[] }
75
87
  | { readonly kind: "literal"; readonly value: unknown }
76
88
 
77
89
  /** One resolved binding: the field's name, its descriptor, its law-computed class, and the term. */
@@ -135,10 +147,18 @@ type SelectEntryData =
135
147
  | { readonly kind: "measure"; readonly over: string }
136
148
  | { readonly kind: "aggregate"; readonly agg: AggData }
137
149
 
138
- /** One answer column: its name (the row object key) and its entry. */
150
+ /**
151
+ * One answer column: its name (the row object key), its entry, and — when
152
+ * the column's value is a closed reference (a projected var or an
153
+ * Arg-carried payload bound at a closed-referencing field) — the roster the
154
+ * decode lifts row ids back to handle NAMES through (the read half of the
155
+ * marshal bijection; `undefined` on every bare column). The slice is
156
+ * SDK-side marshaling data only: the wire `ProgramIr` never carries it.
157
+ */
139
158
  interface SelectColumn {
140
159
  readonly name: string
141
160
  readonly entry: SelectEntryData
161
+ readonly closed: ClosedRoster | undefined
142
162
  }
143
163
 
144
164
  /** One body item of a rule, in written order. */
@@ -151,13 +171,17 @@ type RuleItem =
151
171
  /**
152
172
  * One use of a parameter inside a rule, in written order: the census the
153
173
  * query-level registry folds (first use mints the dense `ParamId`, first
154
- * FIELD-ANCHORED use types the wire).
174
+ * FIELD-ANCHORED use types the wire). `members` is present exactly on a
175
+ * membership-array use (a literal set folded into the program): the handle
176
+ * names the SDK itself translates and supplies at execute — the entry
177
+ * never appears in the host's params object.
155
178
  */
156
179
  interface ParamUse {
157
180
  readonly name: string
158
181
  readonly shape: "value" | "set" | "mask"
159
182
  readonly anchor: AnyField | "measure" | undefined
160
183
  readonly op: "binding" | CmpKind
184
+ readonly members: readonly string[] | undefined
161
185
  }
162
186
 
163
187
  /** One complete rule as runtime data. */
@@ -184,10 +208,18 @@ interface RecData {
184
208
  * of the field's value type, a var/param/∈-set-param term — and, when the
185
209
  * field is interval-typed, a bare point literal (the IR's membership
186
210
  * typing rule: an element-typed term at an interval field is point
187
- * membership; an interval-typed term is value equality).
211
+ * membership; an interval-typed term is value equality). A
212
+ * CLOSED-reference field additionally takes a plain ARRAY of handle names
213
+ * read as membership — `kind: ["Practice", "Review"]` (the drizzle law:
214
+ * set membership is an array, never an operator). Arrays are CLOSED-ONLY
215
+ * in this packet by owner ruling: ordinary u64/str membership already has
216
+ * its spelling through `r.inSet` params; widening literal arrays to every
217
+ * literal-capable kind is a separate future taste call — deliberately not
218
+ * done here.
188
219
  */
189
220
  type BindingInput<F extends AnyField> =
190
221
  | Infer<F>
222
+ | (F extends ClosedIdField ? readonly Infer<F>[] : never)
191
223
  | (F extends { readonly kind: "interval" } ? bigint : never)
192
224
  | Var<string>
193
225
  | Param<string>
@@ -323,10 +355,10 @@ type NeRight = Var<string> | Param<string> | bigint | string | boolean | Uint8Ar
323
355
  /** One side of an order comparison: orderable terms only (the IR's comparison rules). */
324
356
  type OrderSide = Var<string> | Param<string> | Duration<string> | bigint
325
357
 
326
- /** The point side of `pointIn`/`covers`. */
358
+ /** The point side of `pointIn`. */
327
359
  type PointSide = Var<string> | Param<string> | bigint
328
360
 
329
- /** The interval side of `pointIn`/`covers`/`allen`. */
361
+ /** The interval side of `pointIn`/`allen`. */
330
362
  type IntervalSide = Var<string> | Param<string> | IntervalValue
331
363
 
332
364
  /** Builds one comparison value. */
@@ -394,29 +426,21 @@ function ge<const L extends OrderSide, const R extends OrderSide>(left: L, right
394
426
  }
395
427
 
396
428
  /**
397
- * Point membership as a predicate (`ir::CmpOp::PointIn`), membership
429
+ * Point membership as a predicate (`ir::CmpOp::PointIn`) — THE one
398
430
  * spelling: `pointIn(t, w)` holds iff `w.start ≤ t < w.end`. The IR
399
431
  * orders the operands interval-left, point-right; the value stores them
400
- * that way whatever the surface spelling.
432
+ * that way whatever the surface argument order (a literal `span(...)`
433
+ * interval operand is legal and tags by the point sibling's element
434
+ * domain — the bug-hunt fix, now also a type-level guarantee).
435
+ * Interval ⊇ interval is NOT this operator; that predicate is
436
+ * `allen(a, ALLEN.covers, b)` — the name `covers` belongs to the Allen
437
+ * roster alone (the canonical-utterance law: one meaning, one spelling).
401
438
  */
402
439
  function pointIn<const P extends PointSide, const I extends IntervalSide>(point: P, interval: I): Cmp<"pointIn", I, P> {
403
440
  assertTermSide("pointIn", point, interval)
404
441
  return comparison("pointIn", interval, point, undefined)
405
442
  }
406
443
 
407
- /**
408
- * Point membership, coverage spelling — `covers(w, t)` is `pointIn(t, w)`
409
- * with the interval written first (the IR's own operand order; a literal
410
- * `span(...)` left operand is legal and tags by the point sibling's
411
- * element domain — the bug-hunt fix, now also a type-level guarantee).
412
- * Interval ⊇ interval is NOT this operator; that predicate is
413
- * `allen(a, ALLEN.covers, b)`.
414
- */
415
- function covers<const I extends IntervalSide, const P extends PointSide>(interval: I, point: P): Cmp<"pointIn", I, P> {
416
- assertTermSide("covers", interval, point)
417
- return comparison("pointIn", interval, point, undefined)
418
- }
419
-
420
444
  /**
421
445
  * The 13-bit mask range: bits above the low 13 are unrepresentable in the
422
446
  * engine's `AllenMask` (`bumbledb/crates/bumbledb/src/allen.rs`:
@@ -516,11 +540,22 @@ function not<R extends MatchOwner, const B extends MatchShape<MatchFields<R>>>(
516
540
  return Object.freeze(value)
517
541
  }
518
542
 
519
- /** Whether a var name is bound in the environment at an orderable (u64/i64) field. */
543
+ /**
544
+ * Whether a var name is bound in the environment at an orderable (u64/i64)
545
+ * field. A CLOSED reference is excluded even though its kind is `u64`: a
546
+ * vocabulary's declaration-id order is an accident, not semantics
547
+ * (`docs/architecture/10-data-model.md` § orderability — order on it is
548
+ * refused exactly as the enum's ordinal order was), so every
549
+ * order-comparison and fold position refuses closed-bound terms — this
550
+ * judgment is the one gate they all read, and the construction-time
551
+ * validations in `#query/lower.ts` are its runtime twin.
552
+ */
520
553
  type OrderVarOk<Env extends EnvShape, N extends string> = N extends keyof Env
521
- ? Env[N]["field"]["kind"] extends "u64" | "i64"
522
- ? true
523
- : false
554
+ ? Env[N]["field"] extends { readonly closed: ClosedRoster }
555
+ ? false
556
+ : Env[N]["field"]["kind"] extends "u64" | "i64"
557
+ ? true
558
+ : false
524
559
  : false
525
560
 
526
561
  /** Whether a var name is bound at an interval field. */
@@ -576,7 +611,7 @@ type NotOk<Env extends EnvShape, F extends FieldsShape, CR, B> = false extends {
576
611
  * twin of the engine's comparison roster: class-equal joins (off the
577
612
  * schema type's class map), orderable order sides (an interval var under a
578
613
  * non-`pointIn` op is exactly here refused), kind-correct
579
- * `pointIn`/`covers`/`allen` sides, and negated-atom safety (the negated
614
+ * `pointIn`/`allen` sides, and negated-atom safety (the negated
580
615
  * relation's class record is resolved through `Classes` by its name). The
581
616
  * leading `[AnyTreeChild] extends [C]` arm is the recursion's base case:
582
617
  * at an UNRESOLVED constraint (the whole condition union — or a tree's
@@ -704,4 +739,4 @@ export type {
704
739
  Tree,
705
740
  TreeData
706
741
  }
707
- export { ALLEN, allen, and, comparison, covers, eq, ge, gt, le, lt, ne, not, or, pointIn }
742
+ export { ALLEN, ALLEN_ALL_BITS, allen, and, comparison, eq, ge, gt, le, lt, ne, not, or, pointIn }