@bjornpagen/bumbledb 1.0.1 → 1.2.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 (172) hide show
  1. package/COOKBOOK.md +176 -36
  2. package/README.md +70 -26
  3. package/dist/capacity.d.ts +15 -55
  4. package/dist/capacity.d.ts.map +1 -1
  5. package/dist/capacity.js +63 -12
  6. package/dist/capacity.js.map +1 -1
  7. package/dist/changes.d.ts +28 -6
  8. package/dist/changes.d.ts.map +1 -1
  9. package/dist/changes.js +119 -71
  10. package/dist/changes.js.map +1 -1
  11. package/dist/closed.d.ts +23 -51
  12. package/dist/closed.d.ts.map +1 -1
  13. package/dist/closed.js +110 -168
  14. package/dist/closed.js.map +1 -1
  15. package/dist/codec.d.ts +8 -14
  16. package/dist/codec.d.ts.map +1 -1
  17. package/dist/codec.js +113 -188
  18. package/dist/codec.js.map +1 -1
  19. package/dist/compile.d.ts +17 -18
  20. package/dist/compile.d.ts.map +1 -1
  21. package/dist/compile.js +41 -20
  22. package/dist/compile.js.map +1 -1
  23. package/dist/db-native.d.ts +76 -41
  24. package/dist/db-native.d.ts.map +1 -1
  25. package/dist/db-native.js +8 -0
  26. package/dist/db-native.js.map +1 -1
  27. package/dist/db.d.ts +61 -26
  28. package/dist/db.d.ts.map +1 -1
  29. package/dist/db.js +152 -85
  30. package/dist/db.js.map +1 -1
  31. package/dist/errors.d.ts +11 -3
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +13 -1
  34. package/dist/errors.js.map +1 -1
  35. package/dist/face.d.ts +19 -25
  36. package/dist/face.d.ts.map +1 -1
  37. package/dist/face.js +30 -24
  38. package/dist/face.js.map +1 -1
  39. package/dist/fields.d.ts +13 -26
  40. package/dist/fields.d.ts.map +1 -1
  41. package/dist/fields.js +89 -116
  42. package/dist/fields.js.map +1 -1
  43. package/dist/immutable.d.ts +10 -0
  44. package/dist/immutable.d.ts.map +1 -0
  45. package/dist/immutable.js +75 -0
  46. package/dist/immutable.js.map +1 -0
  47. package/dist/index.d.ts +16 -12
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +6 -4
  50. package/dist/index.js.map +1 -1
  51. package/dist/internal/log.d.ts +6 -4
  52. package/dist/internal/log.d.ts.map +1 -1
  53. package/dist/internal/log.js +3 -1
  54. package/dist/internal/log.js.map +1 -1
  55. package/dist/law.d.ts +11 -11
  56. package/dist/law.d.ts.map +1 -1
  57. package/dist/law.js +1 -1
  58. package/dist/law.js.map +1 -1
  59. package/dist/lower.d.ts.map +1 -1
  60. package/dist/lower.js +11 -6
  61. package/dist/lower.js.map +1 -1
  62. package/dist/migration.d.ts +2 -3
  63. package/dist/migration.d.ts.map +1 -1
  64. package/dist/migration.js +5 -5
  65. package/dist/migration.js.map +1 -1
  66. package/dist/native.d.ts +2 -2
  67. package/dist/native.d.ts.map +1 -1
  68. package/dist/native.js.map +1 -1
  69. package/dist/query/atom.d.ts +1 -1
  70. package/dist/query/atom.d.ts.map +1 -1
  71. package/dist/query/atom.js.map +1 -1
  72. package/dist/query/description.d.ts +33 -0
  73. package/dist/query/description.d.ts.map +1 -0
  74. package/dist/query/description.js +302 -0
  75. package/dist/query/description.js.map +1 -0
  76. package/dist/query/lower.d.ts +60 -4
  77. package/dist/query/lower.d.ts.map +1 -1
  78. package/dist/query/lower.js +126 -161
  79. package/dist/query/lower.js.map +1 -1
  80. package/dist/query/parse-ir.d.ts +7 -2
  81. package/dist/query/parse-ir.d.ts.map +1 -1
  82. package/dist/query/parse-ir.js +240 -53
  83. package/dist/query/parse-ir.js.map +1 -1
  84. package/dist/query/run.d.ts.map +1 -1
  85. package/dist/query/run.js +8 -10
  86. package/dist/query/run.js.map +1 -1
  87. package/dist/query/scope.d.ts +3 -4
  88. package/dist/query/scope.d.ts.map +1 -1
  89. package/dist/query/scope.js +5 -10
  90. package/dist/query/scope.js.map +1 -1
  91. package/dist/relation.d.ts +22 -48
  92. package/dist/relation.d.ts.map +1 -1
  93. package/dist/relation.js +30 -82
  94. package/dist/relation.js.map +1 -1
  95. package/dist/result.d.ts +10 -25
  96. package/dist/result.d.ts.map +1 -1
  97. package/dist/result.js +7 -12
  98. package/dist/result.js.map +1 -1
  99. package/dist/rows.d.ts +11 -14
  100. package/dist/rows.d.ts.map +1 -1
  101. package/dist/rows.js +49 -237
  102. package/dist/rows.js.map +1 -1
  103. package/dist/runtime-codes.d.ts +1 -1
  104. package/dist/runtime-codes.d.ts.map +1 -1
  105. package/dist/runtime-codes.js +1 -2
  106. package/dist/runtime-codes.js.map +1 -1
  107. package/dist/runtime-errors.d.ts +27 -15
  108. package/dist/runtime-errors.d.ts.map +1 -1
  109. package/dist/runtime-errors.js +44 -8
  110. package/dist/runtime-errors.js.map +1 -1
  111. package/dist/runtime-native.d.ts +16 -34
  112. package/dist/runtime-native.d.ts.map +1 -1
  113. package/dist/runtime-native.js.map +1 -1
  114. package/dist/runtime.d.ts +11 -28
  115. package/dist/runtime.d.ts.map +1 -1
  116. package/dist/runtime.js +14 -54
  117. package/dist/runtime.js.map +1 -1
  118. package/dist/schema.d.ts +9 -1
  119. package/dist/schema.d.ts.map +1 -1
  120. package/dist/schema.js +85 -24
  121. package/dist/schema.js.map +1 -1
  122. package/dist/selection.d.ts +27 -0
  123. package/dist/selection.d.ts.map +1 -0
  124. package/dist/selection.js +104 -0
  125. package/dist/selection.js.map +1 -0
  126. package/dist/shape.d.ts +6 -9
  127. package/dist/shape.d.ts.map +1 -1
  128. package/dist/statements.d.ts +17 -52
  129. package/dist/statements.d.ts.map +1 -1
  130. package/dist/statements.js +92 -111
  131. package/dist/statements.js.map +1 -1
  132. package/dist/values.d.ts +25 -0
  133. package/dist/values.d.ts.map +1 -0
  134. package/dist/values.js +203 -0
  135. package/dist/values.js.map +1 -0
  136. package/pack-provenance.json +3 -3
  137. package/package.json +4 -4
  138. package/src/capacity.ts +86 -76
  139. package/src/changes.ts +225 -94
  140. package/src/closed.ts +161 -314
  141. package/src/codec.ts +123 -213
  142. package/src/compile.ts +61 -38
  143. package/src/db-native.ts +73 -68
  144. package/src/db.ts +268 -165
  145. package/src/errors.ts +16 -1
  146. package/src/face.ts +44 -74
  147. package/src/fields.ts +100 -146
  148. package/src/immutable.ts +76 -0
  149. package/src/index.ts +38 -31
  150. package/src/internal/log.ts +4 -5
  151. package/src/law.ts +14 -14
  152. package/src/lower.ts +13 -10
  153. package/src/migration.ts +5 -12
  154. package/src/native.ts +2 -1
  155. package/src/query/atom.ts +1 -0
  156. package/src/query/description.ts +394 -0
  157. package/src/query/lower.ts +139 -173
  158. package/src/query/parse-ir.ts +247 -54
  159. package/src/query/run.ts +15 -9
  160. package/src/query/scope.ts +10 -18
  161. package/src/relation.ts +58 -142
  162. package/src/result.ts +16 -45
  163. package/src/rows.ts +57 -250
  164. package/src/runtime-codes.ts +1 -2
  165. package/src/runtime-errors.ts +48 -8
  166. package/src/runtime-native.ts +13 -46
  167. package/src/runtime.ts +51 -119
  168. package/src/schema.ts +99 -29
  169. package/src/selection.ts +124 -0
  170. package/src/shape.ts +9 -9
  171. package/src/statements.ts +138 -189
  172. package/src/values.ts +223 -0
package/src/capacity.ts CHANGED
@@ -1,55 +1,15 @@
1
1
  import { AuthoringError } from "#errors.ts"
2
- /**
3
- * Capacity-law mints the window, weight, and dependent-bound vocabulary
4
- * the one `capacity` statement constructor consumes
5
- * § the canonical-utterance law, restated
6
- * around the aggregate form). `within` is the ONE window spelling —
7
- * `within(n)` exact (`within(0n)` IS the exclusion's one spelling),
8
- * `within(lo, hi)` range (`within(0n, hi)` the canonical ceiling),
9
- * `within(lo, "*")` floor — `weigh` names the measure on the SOURCE row
10
- * (`weigh("watts")` a u64 field, `weigh(duration("booked"))` an interval's
11
- * measure), and `ref`/`duration` read a dependent bound from the
12
- * TARGET row (hi slot only — ruled 2026-07-24, C6). The ban table is
13
- * enforced REPRESENTATIONALLY in two tiers, split per-aggregate where
14
- * weight-sensitive (design § 6: a ban is canonical-utterance policing when
15
- * it is weight-independent, semantic deduplication when it is not):
16
- *
17
- * - **The type tier**: a banned spelling written as a LITERAL does not
18
- * compile — every negative bound, `within(n, n)`, `within(0n, 0n)`, and
19
- * `within(0n, "*")` are type errors naming the canonical form. The
20
- * weight-SENSITIVE row rides the `capacity` overloads themselves:
21
- * `within(1n, "*")` is banned on the unit overload only (`{1..*}` on the
22
- * count instance is the bare containment respelled —
23
- * `window_floor_containment`), and LEGAL on the weighted one ("positive
24
- * total" is not an existence claim over rows).
25
- * - **The construction tier**: a bound the type level cannot judge — a
26
- * COMPUTED `bigint`, or an inverted `within(lo, hi)` order — is judged at
27
- * construction with the same canonical-naming errors; and past both
28
- * tiers the engine's own spec validation remains the law for a hostile
29
- * FFI caller (the standing two-tier ban enforcement).
30
- *
31
- * The weight vocabulary is closed at the row (ruled 2026-07-24, ruling 6):
32
- * a path weight (`weigh("model.watts")`) is a typed refusal at BOTH tiers
33
- * whose diagnostic names the pinned-column idiom — the two-column
34
- * containment IS the join, stated as a law. Bounds are `bigint` (u64
35
- * crosses as bigint always, PRD-04's law); the witnessed measure comes
36
- * back as `bigint` too (u128-wide engine accumulator, C3).
37
- */
2
+ /** Structural capacity measures and windows. Constructors and generated
3
+ * descriptions share the same checked representation. Bounds are u64;
4
+ * dependent bounds read the target row, measures read the source row. */
38
5
 
39
6
  import type { AnyFace, FaceFields, FaceSource, ProjectedShape } from "#face.ts"
40
7
  import type { CapacityBoundSpec, CapacityWindowSpec, WeightSpec } from "#spec.ts"
8
+ import { integerValue, recordValue } from "#values.ts"
41
9
 
42
- const admitted: unique symbol = Symbol("bumbledb.capacity.admitted")
43
-
44
- interface CapacityWindow<S extends CapacityWindowSpec = CapacityWindowSpec> {
45
- readonly window: S
46
- readonly [admitted]: true
47
- }
10
+ type CapacityWindow<S extends CapacityWindowSpec = CapacityWindowSpec> = S
48
11
 
49
- interface CapacityWeight<S extends WeightSpec = WeightSpec> {
50
- readonly weight: S
51
- readonly [admitted]: true
52
- }
12
+ type CapacityWeight<S extends WeightSpec = WeightSpec> = S
53
13
 
54
14
  interface FieldRef<F extends string = string> {
55
15
  readonly kind: "field"
@@ -61,14 +21,6 @@ interface DurationRef<F extends string = string> {
61
21
  readonly field: F
62
22
  }
63
23
 
64
- function isCapacityWindow(value: unknown): value is CapacityWindow {
65
- return typeof value === "object" && value !== null && admitted in value && "window" in value
66
- }
67
-
68
- function isCapacityWeight(value: unknown): value is CapacityWeight {
69
- return typeof value === "object" && value !== null && admitted in value && "weight" in value
70
- }
71
-
72
24
  interface BannedWindow<Canonical extends string> {
73
25
  readonly "banned window spelling — the canonical-utterance law names the one legal form": Canonical
74
26
  }
@@ -129,7 +81,7 @@ type RangeBan<Lo extends bigint, Hi extends bigint> = bigint extends Lo
129
81
  * only: Duration weights pair with Duration-capable bounds, so the
130
82
  * weighted overload takes the same window freely.
131
83
  */
132
- type UnitDimensionBan<W extends CapacityWindow> = W["window"] extends {
84
+ type UnitDimensionBan<W extends CapacityWindow> = W extends {
133
85
  readonly hi: { readonly kind: "durationField" }
134
86
  }
135
87
  ? BannedWindow<"a count of facts bounded by a span of time mixes dimensions (C18) — weigh the source with weigh(duration(field)), or bound by a u64 field or literal">
@@ -154,17 +106,17 @@ interface BoundKindMismatch<K, Want> {
154
106
 
155
107
  type BoundOnTarget<K extends string, Want extends "u64" | "interval", B extends AnyFace> = string extends K
156
108
  ? unknown
157
- : K extends FaceFields<B["source"]>
158
- ? KindAt<B["source"], K> extends Want
109
+ : K extends FaceFields<B["owner"]>
110
+ ? KindAt<B["owner"], K> extends Want
159
111
  ? unknown
160
112
  : BoundKindMismatch<K, Want>
161
- : BoundOffTargetRoster<K, FaceFields<B["source"]>>
113
+ : BoundOffTargetRoster<K, FaceFields<B["owner"]>>
162
114
 
163
- type BoundsOnTarget<W extends CapacityWindow, B extends AnyFace> = W["window"] extends {
115
+ type BoundsOnTarget<W extends CapacityWindow, B extends AnyFace> = W extends {
164
116
  readonly hi: FieldRef<infer K>
165
117
  }
166
118
  ? BoundOnTarget<K, "u64", B>
167
- : W["window"] extends { readonly hi: DurationRef<infer K> }
119
+ : W extends { readonly hi: DurationRef<infer K> }
168
120
  ? BoundOnTarget<K, "interval", B>
169
121
  : unknown
170
122
 
@@ -189,30 +141,32 @@ interface WeightKindMismatch<K, Want> {
189
141
  * position. Checked at the `capacity` call where the source face is
190
142
  * inferred — the proven constrain-after-inference pattern.
191
143
  */
192
- type WeightOnSource<M extends CapacityWeight, A extends AnyFace> = M["weight"] extends {
144
+ type WeightOnSource<M extends CapacityWeight, A extends AnyFace> = M extends {
193
145
  readonly kind: "field"
194
146
  readonly field: infer K extends string
195
147
  }
196
148
  ? string extends K
197
149
  ? unknown
198
- : K extends FaceFields<A["source"]>
199
- ? KindAt<A["source"], K> extends "u64"
150
+ : K extends FaceFields<A["owner"]>
151
+ ? KindAt<A["owner"], K> extends "u64"
200
152
  ? unknown
201
153
  : WeightKindMismatch<K, "u64">
202
- : WeightOffSourceRoster<K, FaceFields<A["source"]>>
203
- : M["weight"] extends { readonly kind: "durationField"; readonly field: infer K extends string }
154
+ : WeightOffSourceRoster<K, FaceFields<A["owner"]>>
155
+ : M extends { readonly kind: "durationField"; readonly field: infer K extends string }
204
156
  ? string extends K
205
157
  ? unknown
206
- : K extends FaceFields<A["source"]>
207
- ? KindAt<A["source"], K> extends "interval"
158
+ : K extends FaceFields<A["owner"]>
159
+ ? KindAt<A["owner"], K> extends "interval"
208
160
  ? unknown
209
161
  : WeightKindMismatch<K, "interval">
210
- : WeightOffSourceRoster<K, FaceFields<A["source"]>>
162
+ : WeightOffSourceRoster<K, FaceFields<A["owner"]>>
211
163
  : unknown
212
164
 
213
165
  const unitWeight: WeightSpec = Object.freeze({ kind: "unit" })
214
166
 
215
- function assertRowLocal(field: string, role: string): string {
167
+ function assertRowLocal<F extends string>(field: F, role: string): F {
168
+ if (typeof field !== "string" || !field.isWellFormed())
169
+ throw new AuthoringError({ message: `${role}: expected a well-formed field name` })
216
170
  if (field.includes(".")) {
217
171
  throw new AuthoringError({
218
172
  message: `${role} \`${field}\` walks a reference — the vocabulary is closed at the row (ruling 6): pin the column with a two-column containment (Source(ref, f) <= Catalog(id, f)) and name the local field`
@@ -222,8 +176,8 @@ function assertRowLocal(field: string, role: string): string {
222
176
  }
223
177
 
224
178
  function admitWindow<S extends CapacityWindowSpec>(window: S): CapacityWindow<S> {
225
- const value: CapacityWindow<S> = { window, [admitted]: true }
226
- return Object.freeze(value)
179
+ capacityWindow(window)
180
+ return Object.freeze(window)
227
181
  }
228
182
 
229
183
  function lit(value: bigint): CapacityBoundSpec {
@@ -265,6 +219,8 @@ function within<const Lo extends bigint, const Hi extends bigint>(
265
219
  | { readonly kind: "exact"; readonly n: { readonly kind: "lit"; readonly value: Lo } }
266
220
  >
267
221
  function within(lo: bigint, hi?: bigint | "*" | FieldRef | DurationRef): CapacityWindow {
222
+ integerValue("capacity lower bound", "u64", lo)
223
+ if (typeof hi === "bigint") integerValue("capacity upper bound", "u64", hi)
268
224
  if (lo < 0n) {
269
225
  throw new AuthoringError({
270
226
  message: `capacity bounds are u64: within(${lo}${hi === undefined ? "" : ", …"}) is out of domain`
@@ -302,8 +258,8 @@ function within(lo: bigint, hi?: bigint | "*" | FieldRef | DurationRef): Capacit
302
258
  }
303
259
 
304
260
  function admitWeight<S extends WeightSpec>(weight: S): CapacityWeight<S> {
305
- const value: CapacityWeight<S> = { weight, [admitted]: true }
306
- return Object.freeze(value)
261
+ capacityWeight(weight)
262
+ return Object.freeze(weight)
307
263
  }
308
264
 
309
265
  /**
@@ -326,7 +282,7 @@ function weigh(measure: string | DurationRef): CapacityWeight {
326
282
  }
327
283
 
328
284
  function ref<const F extends string>(field: F & PathBan<F>): FieldRef<F> {
329
- return Object.freeze({ kind: "field", field: assertRowLocal(field, "dependent bound") }) as FieldRef<F>
285
+ return Object.freeze({ kind: "field", field: assertRowLocal(field, "dependent bound") })
330
286
  }
331
287
 
332
288
  /**
@@ -336,8 +292,62 @@ function ref<const F extends string>(field: F & PathBan<F>): FieldRef<F> {
336
292
  * (Duration weights pair with Duration-capable bounds — C18).
337
293
  */
338
294
  function duration<const F extends string>(field: F & PathBan<F>): DurationRef<F> {
339
- return Object.freeze({ kind: "durationField", field: assertRowLocal(field, "Duration measure") }) as DurationRef<F>
295
+ return Object.freeze({ kind: "durationField", field: assertRowLocal(field, "Duration measure") })
340
296
  }
341
297
 
342
298
  export type { BoundsOnTarget, CapacityWeight, CapacityWindow, DurationRef, FieldRef, UnitDimensionBan, WeightOnSource }
343
- export { duration, isCapacityWeight, isCapacityWindow, ref, unitWeight, weigh, within }
299
+
300
+ /** Read a checked bound without retaining a caller-owned record. */
301
+ function capacityBound(input: unknown): CapacityBoundSpec {
302
+ const kind = typeof input === "object" && input !== null && "kind" in input ? input.kind : undefined
303
+ if (kind === "lit") {
304
+ const bound = recordValue("capacity bound", input, ["kind", "value"])
305
+ return Object.freeze({ kind, value: integerValue("capacity bound", "u64", bound.value) })
306
+ }
307
+ if (kind === "field" || kind === "durationField") {
308
+ const bound = recordValue("capacity bound", input, ["kind", "field"])
309
+ if (typeof bound.field !== "string") throw new AuthoringError({ message: "capacity bound: expected a field name" })
310
+ return Object.freeze({ kind, field: assertRowLocal(bound.field, "dependent bound") })
311
+ }
312
+ throw new AuthoringError({ message: "capacity bound: unknown kind" })
313
+ }
314
+
315
+ function literalBound(input: unknown): CapacityBoundSpec & { readonly kind: "lit" } {
316
+ const bound = capacityBound(input)
317
+ if (bound.kind !== "lit")
318
+ throw new AuthoringError({ message: "dependent bounds are allowed only at a range's upper bound" })
319
+ return bound
320
+ }
321
+
322
+ function capacityWindow(input: unknown): CapacityWindow {
323
+ const kind = typeof input === "object" && input !== null && "kind" in input ? input.kind : undefined
324
+ if (kind === "exact") {
325
+ const window = recordValue("capacity window", input, ["kind", "n"])
326
+ return Object.freeze({ kind, n: literalBound(window.n) })
327
+ }
328
+ if (kind === "floor") {
329
+ const window = recordValue("capacity window", input, ["kind", "lo"])
330
+ return Object.freeze({ kind, lo: literalBound(window.lo) })
331
+ }
332
+ if (kind === "range") {
333
+ const window = recordValue("capacity window", input, ["kind", "lo", "hi"])
334
+ const lo = literalBound(window.lo)
335
+ const hi = capacityBound(window.hi)
336
+ if (hi.kind === "lit" && hi.value < lo.value) throw new AuthoringError({ message: "capacity window is inverted" })
337
+ return Object.freeze({ kind, lo, hi })
338
+ }
339
+ throw new AuthoringError({ message: "capacity window: unknown kind" })
340
+ }
341
+
342
+ function capacityWeight(input: unknown): CapacityWeight {
343
+ const kind = typeof input === "object" && input !== null && "kind" in input ? input.kind : undefined
344
+ if (kind === "unit") {
345
+ recordValue("capacity weight", input, ["kind"])
346
+ return unitWeight
347
+ }
348
+ const weight = capacityBound(input)
349
+ if (weight.kind === "lit") throw new AuthoringError({ message: "capacity weight: expected unit or a source field" })
350
+ return weight
351
+ }
352
+
353
+ export { capacityWeight, capacityWindow, duration, ref, unitWeight, weigh, within }