@bjornpagen/bumbledb 1.2.1 → 1.3.1

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 (108) hide show
  1. package/COOKBOOK.md +240 -9
  2. package/README.md +9 -6
  3. package/dist/alternatives.d.ts +25 -0
  4. package/dist/alternatives.d.ts.map +1 -0
  5. package/dist/alternatives.js +59 -0
  6. package/dist/alternatives.js.map +1 -0
  7. package/dist/changes.d.ts +0 -1
  8. package/dist/changes.d.ts.map +1 -1
  9. package/dist/changes.js +2 -8
  10. package/dist/changes.js.map +1 -1
  11. package/dist/codec.d.ts +8 -1
  12. package/dist/codec.d.ts.map +1 -1
  13. package/dist/codec.js +29 -2
  14. package/dist/codec.js.map +1 -1
  15. package/dist/db-native.d.ts +2 -9
  16. package/dist/db-native.d.ts.map +1 -1
  17. package/dist/db-native.js.map +1 -1
  18. package/dist/index.d.ts +5 -4
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/internal/log.d.ts +4 -4
  23. package/dist/internal/log.d.ts.map +1 -1
  24. package/dist/internal/log.js +2 -2
  25. package/dist/internal/log.js.map +1 -1
  26. package/dist/native.d.ts +23 -9
  27. package/dist/native.d.ts.map +1 -1
  28. package/dist/native.js.map +1 -1
  29. package/dist/query/atom.d.ts +2 -1
  30. package/dist/query/atom.d.ts.map +1 -1
  31. package/dist/query/atom.js.map +1 -1
  32. package/dist/query/compute.d.ts +19 -4
  33. package/dist/query/compute.d.ts.map +1 -1
  34. package/dist/query/compute.js +17 -13
  35. package/dist/query/compute.js.map +1 -1
  36. package/dist/query/description.d.ts +10 -2
  37. package/dist/query/description.d.ts.map +1 -1
  38. package/dist/query/description.js +14 -5
  39. package/dist/query/description.js.map +1 -1
  40. package/dist/query/find.d.ts +13 -6
  41. package/dist/query/find.d.ts.map +1 -1
  42. package/dist/query/find.js +1 -1
  43. package/dist/query/find.js.map +1 -1
  44. package/dist/query/lower.d.ts +14 -2
  45. package/dist/query/lower.d.ts.map +1 -1
  46. package/dist/query/lower.js +64 -50
  47. package/dist/query/lower.js.map +1 -1
  48. package/dist/query/parse-ir.d.ts.map +1 -1
  49. package/dist/query/parse-ir.js +26 -3
  50. package/dist/query/parse-ir.js.map +1 -1
  51. package/dist/query/scope.d.ts.map +1 -1
  52. package/dist/query/scope.js +6 -1
  53. package/dist/query/scope.js.map +1 -1
  54. package/dist/query/segments.d.ts +20 -0
  55. package/dist/query/segments.d.ts.map +1 -0
  56. package/dist/query/segments.js +35 -0
  57. package/dist/query/segments.js.map +1 -0
  58. package/dist/runtime-codes.d.ts +1 -1
  59. package/dist/runtime-codes.d.ts.map +1 -1
  60. package/dist/runtime-codes.js +1 -0
  61. package/dist/runtime-codes.js.map +1 -1
  62. package/dist/runtime-errors.d.ts +23 -3
  63. package/dist/runtime-errors.d.ts.map +1 -1
  64. package/dist/runtime-errors.js +16 -3
  65. package/dist/runtime-errors.js.map +1 -1
  66. package/dist/runtime-native.d.ts +3 -8
  67. package/dist/runtime-native.d.ts.map +1 -1
  68. package/dist/runtime-native.js.map +1 -1
  69. package/dist/runtime.d.ts +0 -23
  70. package/dist/runtime.d.ts.map +1 -1
  71. package/dist/runtime.js +0 -36
  72. package/dist/runtime.js.map +1 -1
  73. package/dist/scalar.d.ts +46 -94
  74. package/dist/scalar.d.ts.map +1 -1
  75. package/dist/scalar.js +69 -156
  76. package/dist/scalar.js.map +1 -1
  77. package/dist/schema-file.d.ts +7 -0
  78. package/dist/schema-file.d.ts.map +1 -0
  79. package/dist/schema-file.js +24 -0
  80. package/dist/schema-file.js.map +1 -0
  81. package/pack-provenance.json +4 -4
  82. package/package.json +4 -4
  83. package/src/alternatives.ts +95 -0
  84. package/src/changes.ts +2 -8
  85. package/src/codec.ts +43 -3
  86. package/src/db-native.ts +2 -10
  87. package/src/index.ts +23 -6
  88. package/src/internal/log.ts +2 -4
  89. package/src/native.ts +21 -7
  90. package/src/query/atom.ts +2 -0
  91. package/src/query/compute.ts +46 -16
  92. package/src/query/description.ts +38 -8
  93. package/src/query/find.ts +37 -26
  94. package/src/query/lower.ts +89 -52
  95. package/src/query/parse-ir.ts +27 -3
  96. package/src/query/scope.ts +8 -1
  97. package/src/query/segments.ts +61 -0
  98. package/src/runtime-codes.ts +1 -0
  99. package/src/runtime-errors.ts +16 -3
  100. package/src/runtime-native.ts +1 -9
  101. package/src/runtime.ts +1 -70
  102. package/src/scalar.ts +138 -285
  103. package/src/schema-file.ts +38 -0
  104. package/dist/migration.d.ts +0 -31
  105. package/dist/migration.d.ts.map +0 -1
  106. package/dist/migration.js +0 -47
  107. package/dist/migration.js.map +0 -1
  108. package/src/migration.ts +0 -61
package/src/scalar.ts CHANGED
@@ -1,31 +1,17 @@
1
- /**
2
- * The ONE leaf-scoped scalar AST (C1/C8): one tagged literal grammar and
3
- * operator roster, parameterized by {@link ScalarLeafScope}. TypeScript never
4
- * evaluates — the native compiler binds source fields and the engine
5
- * `ScalarEvaluator` executes. Construction is constant work against cached
6
- * depth/kind summaries; it does not re-walk the tree.
7
- *
8
- * Query-var leaves carry a known schema kind. Source-field leaves stay
9
- * `unresolved` until native snapshot binding. A result kind is recorded only
10
- * when it is derivable from known operands; otherwise it stays unresolved.
11
- * Known incompatible kinds refuse here. Unresolved trees are inert metadata,
12
- * not typechecked programs.
13
- *
14
- * Wire spellings (the grammar arms, not a second flattened interpretation):
15
- * migration plan JSON — field/literal/operator nodes; one-arm literals (`{ u64: n }`)
16
- * query IR — `{ kind: "literal", value: ValueSpec }` via `literalWireOf`
17
- */
1
+ /** Query scalar nodes, with cached kind and depth. Construction is constant
2
+ * work; native preparation binds variables and evaluates the expressions. */
18
3
  import { AuthoringError } from "#errors.ts"
4
+ import type { ScalarExprIr } from "#native.ts"
19
5
  import type { ValueSpec } from "#spec.ts"
20
6
 
21
- /** F0: leaf scope for the shared AST (C1). Query vars are typed; source fields stay unresolved. */
22
- export type ScalarLeafScope = "query-var" | "source-field"
23
-
24
7
  /** The engine's scalar result vocabulary — distinct at the type level. */
25
8
  type ScalarKind = "u64" | "i64" | "f64" | "bool"
26
9
 
27
10
  /** Cached result kind: known only when derivable; otherwise honestly unresolved. */
28
- type ScalarResultKind = ScalarKind | "unresolved"
11
+ type IntervalKind = "intervalU64" | "intervalI64" | "intervalF64"
12
+ type ScalarInputKind = ScalarKind | IntervalKind
13
+ type ScalarResultKind = ScalarInputKind
14
+ type Rounding = "towardZero" | "nearestTiesAwayFromZero" | "nearestTiesToEven"
29
15
 
30
16
  /** Host value for a derived scalar kind. */
31
17
  type ScalarValue<K extends ScalarKind> = K extends "f64" ? number : K extends "bool" ? boolean : bigint
@@ -34,33 +20,24 @@ type NumericCast = "toF64" | "toF64Exact" | "toI64Exact" | "toU64Exact"
34
20
 
35
21
  type NumericKind = "u64" | "i64" | "f64"
36
22
 
37
- type NumericOrUnresolved = NumericKind | "unresolved"
38
-
39
- /**
40
- * Combine known numeric kinds, or stay unresolved when a source-field leaf
41
- * still needs native binding. Distinct known kinds collapse to `never`
42
- * (static refusal) and throw at the constructor.
43
- */
44
- type CombineNumeric<L, R> = L extends "unresolved"
45
- ? R extends "bool"
46
- ? never
47
- : "unresolved"
48
- : R extends "unresolved"
49
- ? L extends "bool"
50
- ? never
51
- : "unresolved"
52
- : L extends R
53
- ? L
54
- : never
55
-
56
- /** The tagged literal subset shared by every leaf scope. */
57
23
  type ScalarLiteral =
58
24
  | { readonly bool: boolean }
59
25
  | { readonly u64: bigint }
60
26
  | { readonly i64: bigint }
61
27
  | { readonly f64: number }
62
28
 
63
- type ScalarOpKind = "literal" | "negate" | "isNaN" | "isFinite" | "add" | "subtract" | "multiply" | "divide" | "cast"
29
+ type ScalarOpKind =
30
+ | "literal"
31
+ | "negate"
32
+ | "isNaN"
33
+ | "isFinite"
34
+ | "add"
35
+ | "subtract"
36
+ | "multiply"
37
+ | "divide"
38
+ | "cast"
39
+ | "mulDiv"
40
+ | "measure"
64
41
 
65
42
  /**
66
43
  * Structural query-variable leaf. The query layer supplies a bound variable
@@ -74,46 +51,32 @@ interface ScalarQueryVar {
74
51
 
75
52
  type QueryVarLeaf = { readonly kind: "var"; readonly ref: ScalarQueryVar }
76
53
 
77
- type SourceFieldLeaf = { readonly kind: "field"; readonly name: string }
78
-
79
- type ScalarLeaf<S extends ScalarLeafScope> = S extends "query-var" ? QueryVarLeaf : SourceFieldLeaf
80
-
81
- type ScalarNodeBody<S extends ScalarLeafScope> =
82
- | ScalarLeaf<S>
54
+ type ScalarNodeBody =
55
+ | { readonly kind: "measure"; readonly expr: ScalarNode }
56
+ | {
57
+ readonly kind: "mulDiv"
58
+ readonly a: ScalarNode
59
+ readonly b: ScalarNode
60
+ readonly divisor: ScalarNode
61
+ readonly rounding: Rounding
62
+ }
63
+ | QueryVarLeaf
83
64
  | { readonly kind: "literal"; readonly value: ScalarLiteral }
84
- | { readonly kind: "negate"; readonly expr: ScalarNode<S> }
85
- | { readonly kind: "isNaN"; readonly expr: ScalarNode<S> }
86
- | { readonly kind: "isFinite"; readonly expr: ScalarNode<S> }
87
- | { readonly kind: "add"; readonly left: ScalarNode<S>; readonly right: ScalarNode<S> }
88
- | { readonly kind: "subtract"; readonly left: ScalarNode<S>; readonly right: ScalarNode<S> }
89
- | { readonly kind: "multiply"; readonly left: ScalarNode<S>; readonly right: ScalarNode<S> }
90
- | { readonly kind: "divide"; readonly left: ScalarNode<S>; readonly right: ScalarNode<S> }
91
- | { readonly kind: "cast"; readonly cast: NumericCast; readonly expr: ScalarNode<S> }
92
-
93
- /**
94
- * One authored node: roster arms plus cached `scope` / `result` / `depth`.
95
- * Summaries are construction metadata; lowering and L17 serialization read
96
- * the grammar arms and ignore the summaries.
97
- */
98
- type ScalarNode<
99
- S extends ScalarLeafScope = ScalarLeafScope,
100
- K extends ScalarResultKind = ScalarResultKind
101
- > = ScalarNodeBody<S> & {
102
- readonly scope: S
65
+ | { readonly kind: "negate"; readonly expr: ScalarNode }
66
+ | { readonly kind: "isNaN"; readonly expr: ScalarNode }
67
+ | { readonly kind: "isFinite"; readonly expr: ScalarNode }
68
+ | { readonly kind: "add"; readonly left: ScalarNode; readonly right: ScalarNode }
69
+ | { readonly kind: "subtract"; readonly left: ScalarNode; readonly right: ScalarNode }
70
+ | { readonly kind: "multiply"; readonly left: ScalarNode; readonly right: ScalarNode }
71
+ | { readonly kind: "divide"; readonly left: ScalarNode; readonly right: ScalarNode }
72
+ | { readonly kind: "cast"; readonly cast: NumericCast; readonly expr: ScalarNode }
73
+
74
+ /** Cached summaries never replace the expression grammar. */
75
+ type ScalarNode<K extends ScalarResultKind = ScalarResultKind> = ScalarNodeBody & {
103
76
  readonly result: K
104
77
  readonly depth: number
105
78
  }
106
79
 
107
- /**
108
- * Migration authoring expression (source-field scope). `K` is the cached
109
- * result kind, not a host value type: I64 and U64 remain distinct even
110
- * though both use bigint in JavaScript.
111
- */
112
- type ScalarExpr<K extends ScalarResultKind = ScalarResultKind> = ScalarNode<"source-field", K>
113
-
114
- /** A migration field read: unresolved until native snapshot binding. */
115
- type ScalarFieldRef = ScalarNode<"source-field", "unresolved"> & SourceFieldLeaf
116
-
117
80
  const MAX_SCALAR_DEPTH = 128
118
81
  const U64_MAX = (1n << 64n) - 1n
119
82
  const I64_MIN = -(1n << 63n)
@@ -126,9 +89,10 @@ function scalarAuthoringWork(): number {
126
89
  return authoringWork
127
90
  }
128
91
 
129
- function admit<
130
- const Node extends { readonly scope: ScalarLeafScope; readonly result: ScalarResultKind; readonly depth: number }
131
- >(where: string, node: Node): Readonly<Node> {
92
+ function admit<const Node extends { readonly result: ScalarResultKind; readonly depth: number }>(
93
+ where: string,
94
+ node: Node
95
+ ): Readonly<Node> {
132
96
  authoringWork += 1
133
97
  assertDepth(where, node.depth)
134
98
  return Object.freeze(node)
@@ -197,49 +161,28 @@ function assertDepth(where: string, depth: number): void {
197
161
  }
198
162
  }
199
163
 
200
- function assertNumeric(where: string, kind: ScalarKind): void {
201
- if (kind === "bool") {
164
+ function assertNumeric(where: string, kind: ScalarInputKind): asserts kind is NumericKind {
165
+ if (kind !== "u64" && kind !== "i64" && kind !== "f64") {
202
166
  throw new AuthoringError({ message: `${where}: the operand is bool, not numeric (u64/i64/f64)` })
203
167
  }
204
168
  }
205
169
 
206
- function assertSameKind(where: string, left: ScalarKind, right: ScalarKind): ScalarKind {
170
+ function assertSameKind(where: string, left: ScalarInputKind, right: ScalarInputKind): ScalarInputKind {
207
171
  if (left !== right) {
208
172
  throw new AuthoringError({
209
- message: `${where}: operand kinds differ (${left} vs ${right}) — the engine has no mixed promotion; cast explicitly (Scalar.toF64/ toF64Exact/ toI64Exact/ toU64Exact)`
173
+ message: `${where}: operand kinds differ (${left} vs ${right}) — the engine has no mixed promotion; cast explicitly (Compute.toF64/ toF64Exact/ toI64Exact/ toU64Exact)`
210
174
  })
211
175
  }
212
176
  return left
213
177
  }
214
178
 
215
- function assertScope<S extends ScalarLeafScope>(where: string, left: ScalarNode<S>, right: ScalarNode<S>): S {
216
- if (left.scope !== right.scope) {
217
- throw new AuthoringError({
218
- message: `${where}: leaf scopes differ (${left.scope} vs ${right.scope}) — query-var and source-field trees do not mix`
219
- })
220
- }
221
- return left.scope
222
- }
223
-
224
179
  function combineNumeric(where: string, left: ScalarResultKind, right: ScalarResultKind): ScalarResultKind {
225
- if (left === "unresolved" || right === "unresolved") {
226
- if (left !== "unresolved") {
227
- assertNumeric(where, left)
228
- }
229
- if (right !== "unresolved") {
230
- assertNumeric(where, right)
231
- }
232
- return "unresolved"
233
- }
234
180
  assertNumeric(where, left)
235
181
  assertNumeric(where, right)
236
182
  return assertSameKind(where, left, right)
237
183
  }
238
184
 
239
185
  function negateKind(where: string, inner: ScalarResultKind): ScalarResultKind {
240
- if (inner === "unresolved") {
241
- return "unresolved"
242
- }
243
186
  if (inner !== "i64" && inner !== "f64") {
244
187
  throw new AuthoringError({
245
188
  message: `${where}: the operand is ${inner} — negation is defined over i64 and f64 only`
@@ -249,137 +192,127 @@ function negateKind(where: string, inner: ScalarResultKind): ScalarResultKind {
249
192
  }
250
193
 
251
194
  function assertCastOperand(where: string, inner: ScalarResultKind): void {
252
- if (inner === "unresolved") {
253
- return
254
- }
255
195
  assertNumeric(where, inner)
256
196
  }
257
197
 
258
198
  function assertFloatOperand(where: string, inner: ScalarResultKind): void {
259
- if (inner === "unresolved") {
260
- return
261
- }
262
199
  if (inner !== "f64") {
263
200
  throw new AuthoringError({ message: `${where}: the operand is ${inner} — the float predicates read f64` })
264
201
  }
265
202
  }
266
203
 
267
204
  function isScalarNode(value: unknown): value is ScalarNode {
268
- return (
269
- typeof value === "object" &&
270
- value !== null &&
271
- "kind" in value &&
272
- "scope" in value &&
273
- "result" in value &&
274
- "depth" in value
275
- )
276
- }
277
-
278
- function isUnresolvedScalar(node: ScalarNode): boolean {
279
- return node.result === "unresolved"
280
- }
281
-
282
- /** Source-field leaf — unresolved until L14 binds it against the verified snapshot. */
283
- function sourceField(name: string): ScalarFieldRef {
284
- if (typeof name !== "string" || name.length === 0) {
285
- throw new AuthoringError({ message: "scalar field(...) names a nonempty source field" })
286
- }
287
- return admit("Scalar.field", {
288
- kind: "field",
289
- name,
290
- scope: "source-field",
291
- result: "unresolved",
292
- depth: 1
293
- })
205
+ return typeof value === "object" && value !== null && "kind" in value && "result" in value && "depth" in value
294
206
  }
295
207
 
296
208
  /** Query-var leaf — kind is the variable's schema kind, already known. */
297
- function queryVarLeaf<K extends ScalarKind>(ref: ScalarQueryVar, kind: K): ScalarNode<"query-var", K> {
298
- return admit("Scalar.queryVar", {
209
+ function queryVarLeaf<K extends ScalarInputKind>(ref: ScalarQueryVar, kind: K): ScalarNode<K> {
210
+ return admit("Compute.variable", {
299
211
  kind: "var",
300
212
  ref,
301
- scope: "query-var",
302
213
  result: kind,
303
214
  depth: 1
304
215
  })
305
216
  }
306
217
 
307
- function scalarLiteral<S extends ScalarLeafScope>(scope: S, value: ScalarLiteral): ScalarNode<S, ScalarKind> {
218
+ function scalarLiteral(value: ScalarLiteral): ScalarNode<ScalarKind> {
308
219
  const kind = literalKindOf(value)
309
- return admit("Scalar.literal", {
220
+ return admit("Compute.literal", {
310
221
  kind: "literal",
311
222
  value: literalValue(value),
312
- scope,
313
223
  result: kind,
314
224
  depth: 1
315
- }) as ScalarNode<S, ScalarKind>
225
+ }) as ScalarNode<ScalarKind>
316
226
  }
317
227
 
318
228
  type BinaryOp = "add" | "subtract" | "multiply" | "divide"
319
229
 
320
- function scalarBinary<S extends ScalarLeafScope>(
321
- where: string,
322
- op: BinaryOp,
323
- left: ScalarNode<S>,
324
- right: ScalarNode<S>
325
- ): ScalarNode<S, ScalarResultKind> {
326
- const scope = assertScope(where, left, right)
230
+ function scalarBinary(where: string, op: BinaryOp, left: ScalarNode, right: ScalarNode): ScalarNode {
327
231
  return admit(where, {
328
232
  kind: op,
329
233
  left,
330
234
  right,
331
- scope,
332
235
  result: combineNumeric(where, left.result, right.result),
333
236
  depth: Math.max(left.depth, right.depth) + 1
334
237
  })
335
238
  }
336
239
 
337
- function scalarNegate<S extends ScalarLeafScope>(where: string, expr: ScalarNode<S>): ScalarNode<S, ScalarResultKind> {
240
+ function roundingMode(value: unknown): Rounding {
241
+ if (value !== "towardZero" && value !== "nearestTiesAwayFromZero" && value !== "nearestTiesToEven")
242
+ throw new AuthoringError({ message: "unknown integer rounding mode" })
243
+ return value
244
+ }
245
+
246
+ function scalarMulDiv(
247
+ where: string,
248
+ a: ScalarNode,
249
+ b: ScalarNode,
250
+ divisor: ScalarNode,
251
+ rounding: Rounding
252
+ ): ScalarNode {
253
+ let known: "i64" | "u64" | undefined
254
+ for (const operand of [a, b, divisor]) {
255
+ if ((operand.result !== "i64" && operand.result !== "u64") || (known !== undefined && known !== operand.result))
256
+ throw new AuthoringError({ message: `${where}: operands must have one matching integer kind` })
257
+ known = operand.result
258
+ }
259
+ return admit(where, {
260
+ kind: "mulDiv",
261
+ a,
262
+ b,
263
+ divisor,
264
+ rounding: roundingMode(rounding),
265
+ result: a.result,
266
+ depth: Math.max(a.depth, b.depth, divisor.depth) + 1
267
+ })
268
+ }
269
+
270
+ function scalarMeasure(where: string, expr: ScalarNode): ScalarNode {
271
+ const kind = expr.result
272
+ if (kind !== "intervalU64" && kind !== "intervalI64" && kind !== "intervalF64")
273
+ throw new AuthoringError({ message: `${where}: measure requires an interval` })
274
+ return admit(where, {
275
+ kind: "measure",
276
+ expr,
277
+ result: ({ intervalF64: "f64", intervalI64: "u64", intervalU64: "u64" } as const)[kind],
278
+ depth: expr.depth + 1
279
+ })
280
+ }
281
+
282
+ function scalarNegate(where: string, expr: ScalarNode): ScalarNode {
338
283
  return admit(where, {
339
284
  kind: "negate",
340
285
  expr,
341
- scope: expr.scope,
342
286
  result: negateKind(where, expr.result),
343
287
  depth: expr.depth + 1
344
288
  })
345
289
  }
346
290
 
347
- function scalarCast<S extends ScalarLeafScope>(
348
- where: string,
349
- cast: NumericCast,
350
- result: ScalarKind,
351
- expr: ScalarNode<S>
352
- ): ScalarNode<S, ScalarKind> {
291
+ function scalarCast(where: string, cast: NumericCast, result: ScalarKind, expr: ScalarNode): ScalarNode<ScalarKind> {
353
292
  assertCastOperand(where, expr.result)
354
293
  return admit(where, {
355
294
  kind: "cast",
356
295
  cast,
357
296
  expr,
358
- scope: expr.scope,
359
297
  result,
360
298
  depth: expr.depth + 1
361
299
  })
362
300
  }
363
301
 
364
- function scalarFloatPredicate<S extends ScalarLeafScope>(
365
- where: string,
366
- op: "isNaN" | "isFinite",
367
- expr: ScalarNode<S>
368
- ): ScalarNode<S, "bool"> {
302
+ function scalarFloatPredicate(where: string, op: "isNaN" | "isFinite", expr: ScalarNode): ScalarNode<"bool"> {
369
303
  assertFloatOperand(where, expr.result)
370
304
  return admit(where, {
371
305
  kind: op,
372
306
  expr,
373
- scope: expr.scope,
374
307
  result: "bool",
375
308
  depth: expr.depth + 1
376
309
  })
377
310
  }
378
311
 
379
312
  /** Query-var leaves collected once at find-binding time — not during construction. */
380
- function queryVarsOf(node: ScalarNode<"query-var">): readonly ScalarQueryVar[] {
313
+ function queryVarsOf(node: ScalarNode): readonly ScalarQueryVar[] {
381
314
  const vars: ScalarQueryVar[] = []
382
- const pending: ScalarNode<"query-var">[] = [node]
315
+ const pending: ScalarNode[] = [node]
383
316
  while (pending.length > 0) {
384
317
  const next = pending.pop()
385
318
  if (next === undefined) {
@@ -391,148 +324,66 @@ function queryVarsOf(node: ScalarNode<"query-var">): readonly ScalarQueryVar[] {
391
324
  break
392
325
  case "literal":
393
326
  break
327
+ case "measure":
394
328
  case "negate":
395
329
  case "isNaN":
396
330
  case "isFinite":
397
331
  case "cast":
398
332
  pending.push(next.expr)
399
333
  break
334
+ case "mulDiv":
335
+ pending.push(next.a, next.b, next.divisor)
336
+ break
400
337
  case "add":
401
338
  case "subtract":
402
339
  case "multiply":
403
340
  case "divide":
404
341
  pending.push(next.left, next.right)
405
342
  break
406
- default:
407
- throw new AuthoringError({
408
- message: "queryVarsOf: a query-var tree cannot carry a source-field leaf"
409
- })
410
343
  }
411
344
  }
412
345
  return vars
413
346
  }
414
347
 
415
- function field(name: string): ScalarFieldRef {
416
- return sourceField(name)
417
- }
418
-
419
- function literal(value: ScalarLiteral): ScalarExpr<ScalarKind> {
420
- return scalarLiteral("source-field", value)
421
- }
422
-
423
- function u64(value: bigint): ScalarExpr<"u64"> {
424
- checkU64(value, "Scalar.u64")
425
- return scalarLiteral("source-field", { u64: value }) as ScalarExpr<"u64">
426
- }
427
-
428
- function i64(value: bigint): ScalarExpr<"i64"> {
429
- checkI64(value, "Scalar.i64")
430
- return scalarLiteral("source-field", { i64: value }) as ScalarExpr<"i64">
431
- }
432
-
433
- function f64(value: number): ScalarExpr<"f64"> {
434
- checkF64(value, "Scalar.f64")
435
- return scalarLiteral("source-field", { f64: value }) as ScalarExpr<"f64">
436
- }
437
-
438
- function bool(value: boolean): ScalarExpr<"bool"> {
439
- checkBool(value, "Scalar.bool")
440
- return scalarLiteral("source-field", { bool: value }) as ScalarExpr<"bool">
441
- }
442
-
443
- function negate<K extends "i64" | "f64" | "unresolved">(expr: ScalarExpr<K>): ScalarExpr<K> {
444
- return scalarNegate("Scalar.negate", expr) as ScalarExpr<K>
445
- }
446
-
447
- function add<L extends NumericOrUnresolved, R extends CombineNumeric<L, R> extends never ? never : NumericOrUnresolved>(
448
- left: ScalarExpr<L>,
449
- right: ScalarExpr<R>
450
- ): ScalarExpr<CombineNumeric<L, R>> {
451
- return scalarBinary("Scalar.add", "add", left, right) as ScalarExpr<CombineNumeric<L, R>>
452
- }
453
-
454
- function subtract<
455
- L extends NumericOrUnresolved,
456
- R extends CombineNumeric<L, R> extends never ? never : NumericOrUnresolved
457
- >(left: ScalarExpr<L>, right: ScalarExpr<R>): ScalarExpr<CombineNumeric<L, R>> {
458
- return scalarBinary("Scalar.subtract", "subtract", left, right) as ScalarExpr<CombineNumeric<L, R>>
459
- }
460
-
461
- function multiply<
462
- L extends NumericOrUnresolved,
463
- R extends CombineNumeric<L, R> extends never ? never : NumericOrUnresolved
464
- >(left: ScalarExpr<L>, right: ScalarExpr<R>): ScalarExpr<CombineNumeric<L, R>> {
465
- return scalarBinary("Scalar.multiply", "multiply", left, right) as ScalarExpr<CombineNumeric<L, R>>
466
- }
467
-
468
- function divide<
469
- L extends NumericOrUnresolved,
470
- R extends CombineNumeric<L, R> extends never ? never : NumericOrUnresolved
471
- >(left: ScalarExpr<L>, right: ScalarExpr<R>): ScalarExpr<CombineNumeric<L, R>> {
472
- return scalarBinary("Scalar.divide", "divide", left, right) as ScalarExpr<CombineNumeric<L, R>>
473
- }
474
-
475
- function toF64(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"f64"> {
476
- return scalarCast("Scalar.toF64", "toF64", "f64", expr) as ScalarExpr<"f64">
477
- }
478
-
479
- function toF64Exact(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"f64"> {
480
- return scalarCast("Scalar.toF64Exact", "toF64Exact", "f64", expr) as ScalarExpr<"f64">
481
- }
482
-
483
- function toI64Exact(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"i64"> {
484
- return scalarCast("Scalar.toI64Exact", "toI64Exact", "i64", expr) as ScalarExpr<"i64">
485
- }
486
-
487
- function toU64Exact(expr: ScalarExpr<NumericOrUnresolved>): ScalarExpr<"u64"> {
488
- return scalarCast("Scalar.toU64Exact", "toU64Exact", "u64", expr) as ScalarExpr<"u64">
489
- }
490
-
491
- function isNaNExpr(expr: ScalarExpr<"f64" | "unresolved">): ScalarExpr<"bool"> {
492
- return scalarFloatPredicate("Scalar.isNaN", "isNaN", expr)
493
- }
494
-
495
- function isFiniteExpr(expr: ScalarExpr<"f64" | "unresolved">): ScalarExpr<"bool"> {
496
- return scalarFloatPredicate("Scalar.isFinite", "isFinite", expr)
348
+ /** Lower each query variable through its existing binding. */
349
+ function scalarWire(node: ScalarNode, leaf: (node: QueryVarLeaf) => number): ScalarExprIr {
350
+ const walk = (expr: ScalarNode): ScalarExprIr => scalarWire(expr, leaf)
351
+ switch (node.kind) {
352
+ case "var":
353
+ return { kind: "var", var: leaf(node) }
354
+ case "literal":
355
+ return { kind: "literal", value: literalWireOf(node.value) }
356
+ case "measure":
357
+ case "negate":
358
+ case "isNaN":
359
+ case "isFinite":
360
+ return { kind: node.kind, expr: walk(node.expr) }
361
+ case "cast":
362
+ return { kind: node.kind, cast: node.cast, expr: walk(node.expr) }
363
+ case "mulDiv":
364
+ return { kind: node.kind, a: walk(node.a), b: walk(node.b), divisor: walk(node.divisor), rounding: node.rounding }
365
+ case "add":
366
+ case "subtract":
367
+ case "multiply":
368
+ case "divide":
369
+ return { kind: node.kind, left: walk(node.left), right: walk(node.right) }
370
+ }
497
371
  }
498
372
 
499
- /** The authoring namespace: `Scalar.field("units")`, `Scalar.add(...)`. */
500
- const Scalar = Object.freeze({
501
- field,
502
- literal,
503
- u64,
504
- i64,
505
- f64,
506
- bool,
507
- negate,
508
- add,
509
- subtract,
510
- multiply,
511
- divide,
512
- toF64,
513
- toF64Exact,
514
- toI64Exact,
515
- toU64Exact,
516
- isNaN: isNaNExpr,
517
- isFinite: isFiniteExpr
518
- })
519
-
520
373
  export type {
521
- CombineNumeric,
374
+ IntervalKind,
522
375
  NumericCast,
523
376
  NumericKind,
524
- NumericOrUnresolved,
525
377
  QueryVarLeaf,
526
- ScalarExpr,
527
- ScalarFieldRef,
378
+ Rounding,
379
+ ScalarInputKind,
528
380
  ScalarKind,
529
381
  ScalarLiteral,
530
382
  ScalarNode,
531
383
  ScalarOpKind,
532
384
  ScalarQueryVar,
533
385
  ScalarResultKind,
534
- ScalarValue,
535
- SourceFieldLeaf
386
+ ScalarValue
536
387
  }
537
388
  export {
538
389
  assertDepth,
@@ -543,18 +394,20 @@ export {
543
394
  checkI64,
544
395
  checkU64,
545
396
  isScalarNode,
546
- isUnresolvedScalar,
547
397
  literalKindOf,
548
398
  literalValue,
549
399
  literalWireOf,
550
400
  MAX_SCALAR_DEPTH,
551
401
  queryVarLeaf,
552
402
  queryVarsOf,
553
- Scalar,
403
+ roundingMode,
554
404
  scalarAuthoringWork,
555
405
  scalarBinary,
556
406
  scalarCast,
557
407
  scalarFloatPredicate,
558
408
  scalarLiteral,
559
- scalarNegate
409
+ scalarMeasure,
410
+ scalarMulDiv,
411
+ scalarNegate,
412
+ scalarWire
560
413
  }
@@ -0,0 +1,38 @@
1
+ import { Effect } from "effect"
2
+ import { dbNative } from "#db-native.ts"
3
+ import { nativeOperationWith, runtimeHandle } from "#runtime.ts"
4
+ import { DbError, dbError } from "#runtime-errors.ts"
5
+ import type { SchemaSpec } from "#spec.ts"
6
+
7
+ const decoder = new TextDecoder("utf-8", { fatal: true })
8
+
9
+ function text(operation: string, bytes: Uint8Array | null): string {
10
+ if (bytes === null) throw dbError(operation, { _tag: "Internal" })
11
+ return decoder.decode(bytes)
12
+ }
13
+
14
+ const internalSchemaSnapshot = Effect.fn("internalSchemaSnapshot")(function* (spec: SchemaSpec) {
15
+ const runtime = yield* runtimeHandle()
16
+ return yield* nativeOperationWith(
17
+ "internalSchemaSnapshot",
18
+ (callback) => dbNative.runtimeSchemaSnapshot(runtime, spec, callback),
19
+ dbNative.runtimeBytesTake,
20
+ (bytes) => text("internalSchemaSnapshot", bytes)
21
+ )
22
+ })
23
+
24
+ const internalSchemaBindings = Effect.fn("internalSchemaBindings")(function* (snapshot: string) {
25
+ if (typeof snapshot !== "string" || !snapshot.isWellFormed()) {
26
+ return yield* Effect.fail(new DbError({ operation: "internalSchemaBindings", reason: { _tag: "InvalidArgument" } }))
27
+ }
28
+ const runtime = yield* runtimeHandle()
29
+ const bytes = new TextEncoder().encode(snapshot)
30
+ return yield* nativeOperationWith(
31
+ "internalSchemaBindings",
32
+ (callback) => dbNative.runtimeSchemaBindings(runtime, bytes, callback),
33
+ dbNative.runtimeBytesTake,
34
+ (bytes) => text("internalSchemaBindings", bytes)
35
+ )
36
+ })
37
+
38
+ export { internalSchemaBindings, internalSchemaSnapshot }
@@ -1,31 +0,0 @@
1
- /**
2
- * Private log-migration integration entrypoints: the two read-only
3
- * native migration-codec verbs the `@bjornpagen/bumbledb-log/migrations`
4
- * generator imports, wired over the shared executor onto the native
5
- * `schema_file`, `migration::plan` and `migration::manifest` modules.
6
- *
7
- * Both follow `hashChunk`'s shape: bounded owned input, bounded owned JSON
8
- * response bytes, ONE registered cancellable operation under the acquired
9
- * `NativeRuntime`. The schema verb takes the SDK `SchemaSpec` object — the
10
- * same wire that already crosses the bridge for open/create — never a
11
- * respelled text form. Neither verb opens, initializes, freezes or migrates
12
- * a database; the durable migrate/activate/abort workflow is the log's
13
- * admin surface, not this seam.
14
- */
15
- import { Effect } from "effect";
16
- import { DbError } from "./runtime-errors.js";
17
- import type { SchemaSpec } from "./spec.js";
18
- /**
19
- * Canonical schema admission for the migration generator: validates the
20
- * spec natively and yields bounded JSON bytes carrying the canonical
21
- * `schemaId` and the rendered schema snapshot.
22
- */
23
- declare const internalMigrationSchema: (spec: SchemaSpec) => Effect.Effect<Uint8Array<ArrayBufferLike>, DbError, import("./runtime.js").NativeRuntime>;
24
- /**
25
- * One bounded read-only migration-codec request (plan parse/validate/
26
- * render/digest, manifest verify/append preview, plan-set digests): owned
27
- * JSON request bytes in, owned JSON response bytes out.
28
- */
29
- declare const internalMigrationRead: (request: Uint8Array<ArrayBufferLike>) => Effect.Effect<Uint8Array<ArrayBufferLike>, DbError, import("./runtime.js").NativeRuntime>;
30
- export { internalMigrationRead, internalMigrationSchema };
31
- //# sourceMappingURL=migration.d.ts.map