@bjornpagen/bumbledb 0.20.2 → 1.0.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 (192) hide show
  1. package/COOKBOOK.md +283 -1611
  2. package/README.md +156 -118
  3. package/dist/capacity.d.ts +16 -12
  4. package/dist/capacity.d.ts.map +1 -1
  5. package/dist/capacity.js +16 -48
  6. package/dist/capacity.js.map +1 -1
  7. package/dist/changes.d.ts +54 -0
  8. package/dist/changes.d.ts.map +1 -0
  9. package/dist/changes.js +245 -0
  10. package/dist/changes.js.map +1 -0
  11. package/dist/close.d.ts +24 -0
  12. package/dist/close.d.ts.map +1 -0
  13. package/dist/close.js +34 -0
  14. package/dist/close.js.map +1 -0
  15. package/dist/closed.d.ts +1 -3
  16. package/dist/closed.d.ts.map +1 -1
  17. package/dist/closed.js +24 -12
  18. package/dist/closed.js.map +1 -1
  19. package/dist/codec.d.ts +56 -0
  20. package/dist/codec.d.ts.map +1 -0
  21. package/dist/codec.js +289 -0
  22. package/dist/codec.js.map +1 -0
  23. package/dist/compile.d.ts +68 -0
  24. package/dist/compile.d.ts.map +1 -0
  25. package/dist/compile.js +75 -0
  26. package/dist/compile.js.map +1 -0
  27. package/dist/db-native.d.ts +170 -0
  28. package/dist/db-native.d.ts.map +1 -0
  29. package/dist/db-native.js +5 -0
  30. package/dist/db-native.js.map +1 -0
  31. package/dist/db.d.ts +101 -233
  32. package/dist/db.d.ts.map +1 -1
  33. package/dist/db.js +232 -1084
  34. package/dist/db.js.map +1 -1
  35. package/dist/errors.d.ts +57 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +32 -0
  38. package/dist/errors.js.map +1 -0
  39. package/dist/face.d.ts.map +1 -1
  40. package/dist/face.js +2 -2
  41. package/dist/face.js.map +1 -1
  42. package/dist/fields.d.ts +65 -24
  43. package/dist/fields.d.ts.map +1 -1
  44. package/dist/fields.js +94 -24
  45. package/dist/fields.js.map +1 -1
  46. package/dist/index.d.ts +37 -32
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +17 -21
  49. package/dist/index.js.map +1 -1
  50. package/dist/internal/log.d.ts +24 -0
  51. package/dist/internal/log.d.ts.map +1 -0
  52. package/dist/internal/log.js +10 -0
  53. package/dist/internal/log.js.map +1 -0
  54. package/dist/law.d.ts +8 -13
  55. package/dist/law.d.ts.map +1 -1
  56. package/dist/law.js +6 -59
  57. package/dist/law.js.map +1 -1
  58. package/dist/lower.d.ts +0 -13
  59. package/dist/lower.d.ts.map +1 -1
  60. package/dist/lower.js +9 -17
  61. package/dist/lower.js.map +1 -1
  62. package/dist/migration.d.ts +33 -0
  63. package/dist/migration.d.ts.map +1 -0
  64. package/dist/migration.js +48 -0
  65. package/dist/migration.js.map +1 -0
  66. package/dist/native.d.ts +256 -301
  67. package/dist/native.d.ts.map +1 -1
  68. package/dist/native.js +65 -108
  69. package/dist/native.js.map +1 -1
  70. package/dist/query/atom.d.ts +24 -13
  71. package/dist/query/atom.d.ts.map +1 -1
  72. package/dist/query/atom.js +7 -3
  73. package/dist/query/atom.js.map +1 -1
  74. package/dist/query/compute.d.ts +68 -0
  75. package/dist/query/compute.d.ts.map +1 -0
  76. package/dist/query/compute.js +141 -0
  77. package/dist/query/compute.js.map +1 -0
  78. package/dist/query/find.d.ts +15 -8
  79. package/dist/query/find.d.ts.map +1 -1
  80. package/dist/query/find.js +6 -2
  81. package/dist/query/find.js.map +1 -1
  82. package/dist/query/lower.d.ts +23 -2
  83. package/dist/query/lower.d.ts.map +1 -1
  84. package/dist/query/lower.js +433 -80
  85. package/dist/query/lower.js.map +1 -1
  86. package/dist/query/parse-ir.d.ts.map +1 -1
  87. package/dist/query/parse-ir.js +17 -8
  88. package/dist/query/parse-ir.js.map +1 -1
  89. package/dist/query/run.d.ts +12 -2
  90. package/dist/query/run.d.ts.map +1 -1
  91. package/dist/query/run.js +26 -9
  92. package/dist/query/run.js.map +1 -1
  93. package/dist/query/scope.d.ts +47 -3
  94. package/dist/query/scope.d.ts.map +1 -1
  95. package/dist/query/scope.js +83 -19
  96. package/dist/query/scope.js.map +1 -1
  97. package/dist/relation.d.ts +3 -8
  98. package/dist/relation.d.ts.map +1 -1
  99. package/dist/relation.js +16 -10
  100. package/dist/relation.js.map +1 -1
  101. package/dist/result.d.ts +47 -0
  102. package/dist/result.d.ts.map +1 -0
  103. package/dist/result.js +50 -0
  104. package/dist/result.js.map +1 -0
  105. package/dist/rows.d.ts +69 -0
  106. package/dist/rows.d.ts.map +1 -0
  107. package/dist/rows.js +328 -0
  108. package/dist/rows.js.map +1 -0
  109. package/dist/runtime-codes.d.ts +2 -0
  110. package/dist/runtime-codes.d.ts.map +1 -0
  111. package/dist/runtime-codes.js +19 -0
  112. package/dist/runtime-codes.js.map +1 -0
  113. package/dist/runtime-errors.d.ts +106 -0
  114. package/dist/runtime-errors.d.ts.map +1 -0
  115. package/dist/runtime-errors.js +62 -0
  116. package/dist/runtime-errors.js.map +1 -0
  117. package/dist/runtime-native.d.ts +171 -0
  118. package/dist/runtime-native.d.ts.map +1 -0
  119. package/dist/runtime-native.js +5 -0
  120. package/dist/runtime-native.js.map +1 -0
  121. package/dist/runtime.d.ts +73 -0
  122. package/dist/runtime.d.ts.map +1 -0
  123. package/dist/runtime.js +293 -0
  124. package/dist/runtime.js.map +1 -0
  125. package/dist/scalar.d.ts +165 -0
  126. package/dist/scalar.d.ts.map +1 -0
  127. package/dist/scalar.js +344 -0
  128. package/dist/scalar.js.map +1 -0
  129. package/dist/schema.d.ts +1 -1
  130. package/dist/schema.d.ts.map +1 -1
  131. package/dist/schema.js +34 -37
  132. package/dist/schema.js.map +1 -1
  133. package/dist/shape.d.ts +28 -0
  134. package/dist/shape.d.ts.map +1 -0
  135. package/dist/shape.js +2 -0
  136. package/dist/shape.js.map +1 -0
  137. package/dist/spec.d.ts +27 -3
  138. package/dist/spec.d.ts.map +1 -1
  139. package/dist/spec.js +22 -1
  140. package/dist/spec.js.map +1 -1
  141. package/dist/statements.d.ts +16 -3
  142. package/dist/statements.d.ts.map +1 -1
  143. package/dist/statements.js +54 -28
  144. package/dist/statements.js.map +1 -1
  145. package/dist/uuid.d.ts +23 -0
  146. package/dist/uuid.d.ts.map +1 -0
  147. package/dist/uuid.js +54 -0
  148. package/dist/uuid.js.map +1 -0
  149. package/pack-provenance.json +7 -0
  150. package/package.json +12 -16
  151. package/src/capacity.ts +34 -54
  152. package/src/changes.ts +361 -0
  153. package/src/close.ts +53 -0
  154. package/src/closed.ts +25 -19
  155. package/src/codec.ts +379 -0
  156. package/src/compile.ts +149 -0
  157. package/src/db-native.ts +247 -0
  158. package/src/db.ts +461 -1632
  159. package/src/errors.ts +50 -0
  160. package/src/face.ts +2 -2
  161. package/src/fields.ts +182 -55
  162. package/src/index.ts +62 -126
  163. package/src/internal/log.ts +51 -0
  164. package/src/law.ts +13 -28
  165. package/src/lower.ts +9 -4
  166. package/src/migration.ts +69 -0
  167. package/src/native.ts +326 -487
  168. package/src/query/atom.ts +61 -25
  169. package/src/query/compute.ts +237 -0
  170. package/src/query/find.ts +31 -12
  171. package/src/query/lower.ts +531 -143
  172. package/src/query/parse-ir.ts +18 -13
  173. package/src/query/run.ts +32 -16
  174. package/src/query/scope.ts +140 -8
  175. package/src/relation.ts +17 -21
  176. package/src/result.ts +130 -0
  177. package/src/rows.ts +404 -0
  178. package/src/runtime-codes.ts +18 -0
  179. package/src/runtime-errors.ts +69 -0
  180. package/src/runtime-native.ts +242 -0
  181. package/src/runtime.ts +440 -0
  182. package/src/scalar.ts +560 -0
  183. package/src/schema.ts +35 -37
  184. package/src/shape.ts +31 -0
  185. package/src/spec.ts +33 -3
  186. package/src/statements.ts +75 -68
  187. package/src/uuid.ts +69 -0
  188. package/dist/marshal.d.ts +0 -44
  189. package/dist/marshal.d.ts.map +0 -1
  190. package/dist/marshal.js +0 -165
  191. package/dist/marshal.js.map +0 -1
  192. package/src/marshal.ts +0 -200
package/src/query/atom.ts CHANGED
@@ -1,6 +1,7 @@
1
- import * as errors from "@superbuilders/errors"
2
- import type { AnyClosedRoster, AnyField, Infer, IntervalValue } from "#fields.ts"
1
+ import { AuthoringError } from "#errors.ts"
2
+ import type { AnyClosedRoster, AnyField, FloatIntervalValue, Infer, IntervalValue } from "#fields.ts"
3
3
  import type { ClassLookup, ClassRecordOf, SchemaClasses } from "#law.ts"
4
+ import type { QueryNode } from "#query/compute.ts"
4
5
  import type {
5
6
  AntiJoinOk,
6
7
  AnyVar,
@@ -16,6 +17,8 @@ import type {
16
17
  } from "#query/scope.ts"
17
18
  import { isTerm, term } from "#query/scope.ts"
18
19
  import type { FieldsShape } from "#relation.ts"
20
+ import type { ScalarKind } from "#scalar.ts"
21
+ import type { Uuid } from "#uuid.ts"
19
22
 
20
23
  type BindingTermData =
21
24
  | { readonly kind: "var"; readonly ref: AnyVar }
@@ -65,14 +68,16 @@ type AggData =
65
68
  | { readonly op: "count" }
66
69
  | {
67
70
  readonly op: "fold"
68
- readonly fold: "sum" | "min" | "max"
71
+ readonly fold: "sum" | "mean" | "min" | "max"
69
72
  readonly over: AnyVar
70
73
  }
71
74
  | { readonly op: "pack"; readonly over: AnyVar }
72
75
 
76
+ /** Compute `expr` is the shared `#scalar.ts` query-var node (one grammar). */
73
77
  type FindEntryData =
74
78
  | { readonly kind: "var"; readonly over: AnyVar }
75
79
  | { readonly kind: "aggregate"; readonly agg: AggData }
80
+ | { readonly kind: "compute"; readonly expr: QueryNode; readonly result: ScalarKind }
76
81
 
77
82
  interface FindColumn {
78
83
  readonly name: string
@@ -141,7 +146,11 @@ type DerivedTable = InteriorData | RecHead
141
146
  type BindingInput<F extends AnyField> =
142
147
  | Infer<F>
143
148
  | (F extends { readonly closed: { readonly handles: readonly string[] } } ? readonly Infer<F>[] : never)
144
- | (F extends { readonly kind: "interval" } ? bigint : never)
149
+ | (F extends { readonly kind: "interval"; readonly element: "f64" }
150
+ ? number
151
+ : F extends { readonly kind: "interval" }
152
+ ? bigint
153
+ : never)
145
154
  | AnyVar
146
155
  | Param<string>
147
156
  | SetParam<string>
@@ -216,15 +225,34 @@ type AnyNotInteriorAtom = NotInteriorAtom<unknown>
216
225
 
217
226
  type AnyCond = AnyCmp | Tree<readonly AnyTreeChild[]> | AnyNotAtom | AnyNotInteriorAtom
218
227
 
219
- type EqRight = AnyVar | Param<string> | SetParam<string> | bigint | string | boolean | Uint8Array | IntervalValue
220
-
221
- type NeRight = AnyVar | Param<string> | bigint | string | boolean | Uint8Array | IntervalValue
228
+ type EqRight =
229
+ | AnyVar
230
+ | Param<string>
231
+ | SetParam<string>
232
+ | bigint
233
+ | number
234
+ | string
235
+ | boolean
236
+ | Uint8Array
237
+ | IntervalValue
238
+ | FloatIntervalValue
239
+
240
+ type NeRight =
241
+ | AnyVar
242
+ | Param<string>
243
+ | bigint
244
+ | number
245
+ | string
246
+ | boolean
247
+ | Uint8Array
248
+ | IntervalValue
249
+ | FloatIntervalValue
222
250
 
223
- type OrderSide = AnyVar | Param<string> | bigint | boolean
251
+ type OrderSide = AnyVar | Param<string> | bigint | number | boolean | Uuid
224
252
 
225
- type PointSide = AnyVar | Param<string> | bigint
253
+ type PointSide = AnyVar | Param<string> | bigint | number
226
254
 
227
- type IntervalSide = AnyVar | Param<string> | IntervalValue
255
+ type IntervalSide = AnyVar | Param<string> | IntervalValue | FloatIntervalValue
228
256
 
229
257
  function comparison<Op extends CmpKind, L, R, M>(op: Op, lhs: L, rhs: R, mask: M): Cmp<Op, L, R, M> {
230
258
  return Object.freeze({ cond: "cmp", op, lhs, rhs, mask })
@@ -236,9 +264,9 @@ function isVariableSide(value: unknown): boolean {
236
264
 
237
265
  function assertTermSide(op: string, lhs: unknown, rhs: unknown): void {
238
266
  if (!isVariableSide(lhs) && !isVariableSide(rhs)) {
239
- throw errors.new(
240
- `${op}: a comparison without a variable side is constant-valued (a parameter is a constant at execution) — write the query you mean`
241
- )
267
+ throw new AuthoringError({
268
+ message: `${op}: a comparison without a variable side is constant-valued (a parameter is a constant at execution) — write the query you mean`
269
+ })
242
270
  }
243
271
  }
244
272
 
@@ -313,9 +341,9 @@ function allen<const A extends IntervalSide, const B extends IntervalSide>(
313
341
  ): Cmp<"allen", A, B, number> {
314
342
  assertTermSide("allen", left, right)
315
343
  if (!Number.isInteger(mask) || mask < 0 || mask > ALLEN_ALL_BITS) {
316
- throw errors.new(
317
- `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)`
318
- )
344
+ throw new AuthoringError({
345
+ message: `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)`
346
+ })
319
347
  }
320
348
  return comparison("allen", left, right, mask)
321
349
  }
@@ -354,7 +382,7 @@ function not(
354
382
  }
355
383
 
356
384
  /**
357
- * Whether a variable's OWN field is NUMERIC (u64/i64) — the judgment the
385
+ * Whether a variable's OWN field is NUMERIC (u64/i64/f64) — the judgment the
358
386
  * point side of `pointIn` and the `sum` input read: a point lives in the
359
387
  * interval's element domain, and a quantifier is not an addition, so bool
360
388
  * (orderable, never numeric) is exactly here refused. A CLOSED reference
@@ -366,11 +394,11 @@ function not(
366
394
  */
367
395
  type NumericVarOk<V extends AnyVar> = V["field"] extends { readonly closed: AnyClosedRoster }
368
396
  ? false
369
- : V["field"]["kind"] extends "u64" | "i64"
397
+ : V["field"]["kind"] extends "u64" | "i64" | "f64"
370
398
  ? true
371
399
  : false
372
400
 
373
- type OrderVarOk<V extends AnyVar> = V["field"]["kind"] extends "bool" ? true : NumericVarOk<V>
401
+ type OrderVarOk<V extends AnyVar> = V["field"]["kind"] extends "bool" | "uuid" ? true : NumericVarOk<V>
374
402
 
375
403
  type IntervalVarOk<V extends AnyVar> = V["field"]["kind"] extends "interval" ? true : false
376
404
 
@@ -384,9 +412,13 @@ type OrderDomain<T> = T extends AnyVar
384
412
  ? "open"
385
413
  : T extends bigint
386
414
  ? "integer"
387
- : T extends boolean
388
- ? "bool"
389
- : never
415
+ : T extends number
416
+ ? "f64"
417
+ : T extends boolean
418
+ ? "bool"
419
+ : T extends Uuid
420
+ ? "uuid"
421
+ : never
390
422
 
391
423
  type OrderDomainsOk<A, B> = [A] extends [never]
392
424
  ? false
@@ -403,9 +435,13 @@ type OrderDomainsOk<A, B> = [A] extends [never]
403
435
  : B extends "bool"
404
436
  ? false
405
437
  : A extends "integer"
406
- ? true
407
- : B extends "integer"
438
+ ? B extends "integer" | "u64" | "i64"
408
439
  ? true
440
+ : false
441
+ : B extends "integer"
442
+ ? A extends "u64" | "i64"
443
+ ? true
444
+ : false
409
445
  : A extends B
410
446
  ? true
411
447
  : false
@@ -413,7 +449,7 @@ type OrderDomainsOk<A, B> = [A] extends [never]
413
449
  type OrderPairOk<L, R> = CmpVarSideOk<L, R> extends true ? OrderDomainsOk<OrderDomain<L>, OrderDomain<R>> : false
414
450
 
415
451
  type IntervalElementDomain<T> = T extends AnyVar
416
- ? T["field"] extends { readonly kind: "interval"; readonly element: infer E extends "u64" | "i64" }
452
+ ? T["field"] extends { readonly kind: "interval"; readonly element: infer E extends "u64" | "i64" | "f64" }
417
453
  ? E
418
454
  : never
419
455
  : "open"
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Query-scoped scalar expressions (C05 `FindTerm::Compute`): the
3
+ * variable-addressed leaf scope over the ONE shared operator roster in
4
+ * `#scalar.ts`. Leaves are bound query variables that lower to `VarId`s.
5
+ * Operators are the shared constructors — this module only admits known
6
+ * schema kinds at query-var leaves.
7
+ *
8
+ * `ComputeExpr<K>` is a `ScalarNode<"query-var", K>`. Distinct I64 and U64
9
+ * survive host inference; they do not collapse to plain `bigint`.
10
+ */
11
+ import { AuthoringError } from "#errors.ts"
12
+ import type { AnyField } from "#fields.ts"
13
+ import { bool as boolField, f64 as f64Field, i64 as i64Field, rosterOf, u64 as u64Field } from "#fields.ts"
14
+ import type { AnyVar } from "#query/scope.ts"
15
+ import { isTerm, term } from "#query/scope.ts"
16
+ import type { ScalarKind, ScalarLiteral, ScalarNode } from "#scalar.ts"
17
+ import {
18
+ checkBool,
19
+ checkF64,
20
+ checkI64,
21
+ checkU64,
22
+ isScalarNode,
23
+ MAX_SCALAR_DEPTH,
24
+ queryVarLeaf,
25
+ queryVarsOf,
26
+ scalarBinary,
27
+ scalarCast,
28
+ scalarFloatPredicate,
29
+ scalarLiteral,
30
+ scalarNegate
31
+ } from "#scalar.ts"
32
+
33
+ /** Query-var tree: the shared AST restricted to this leaf scope. */
34
+ type QueryNode = ScalarNode<"query-var">
35
+
36
+ /** Host value for a derived query compute kind. */
37
+ type ComputeValue<K extends ScalarKind> = K extends "f64" ? number : K extends "bool" ? boolean : bigint
38
+
39
+ type ComputeExpr<K extends ScalarKind> = ScalarNode<"query-var", K>
40
+
41
+ type AnyComputeExpr = ComputeExpr<"u64"> | ComputeExpr<"i64"> | ComputeExpr<"f64"> | ComputeExpr<"bool">
42
+
43
+ /** One operand as the runtime judgment sees it. */
44
+ type Operand = AnyVar | AnyComputeExpr
45
+
46
+ type NumericVarOperand = AnyVar & { readonly field: { readonly kind: "u64" | "i64" | "f64" } }
47
+ type ArithmeticOperand = ComputeExpr<"u64"> | ComputeExpr<"i64"> | ComputeExpr<"f64"> | NumericVarOperand
48
+ type SignedOperand =
49
+ | ComputeExpr<"i64">
50
+ | ComputeExpr<"f64">
51
+ | (AnyVar & { readonly field: { readonly kind: "i64" | "f64" } })
52
+ type FloatOperand = ComputeExpr<"f64"> | (AnyVar & { readonly field: { readonly kind: "f64" } })
53
+
54
+ type OperandKind<O> =
55
+ O extends ComputeExpr<infer K extends ScalarKind>
56
+ ? K
57
+ : O extends AnyVar
58
+ ? O["field"]["kind"] extends "u64" | "i64" | "f64" | "bool"
59
+ ? O["field"]["kind"]
60
+ : never
61
+ : never
62
+
63
+ function isComputeExpr(value: unknown): value is AnyComputeExpr {
64
+ return isScalarNode(value) && value.scope === "query-var" && value.result !== "unresolved"
65
+ }
66
+
67
+ function varKindOf(where: string, ref: AnyVar): ScalarKind {
68
+ const roster = rosterOf(ref.field)
69
+ if (roster !== undefined) {
70
+ throw new AuthoringError({
71
+ message: `${where}: ${ref.label} is a ${roster.name} reference — declaration order is an accident, not semantics: a closed reference never enters arithmetic`
72
+ })
73
+ }
74
+ const kind = ref.field.kind
75
+ if (kind === "u64" || kind === "i64" || kind === "f64" || kind === "bool") {
76
+ return kind
77
+ }
78
+ throw new AuthoringError({
79
+ message: `${where}: ${ref.label} is ${kind} — a scalar expression reads u64/i64/f64/bool variables only`
80
+ })
81
+ }
82
+
83
+ function asQueryNode(where: string, operand: Operand): QueryNode {
84
+ if (isComputeExpr(operand)) {
85
+ return operand
86
+ }
87
+ if (isTerm(operand) && operand[term] === "var") {
88
+ return queryVarLeaf(operand, varKindOf(where, operand))
89
+ }
90
+ throw new AuthoringError({
91
+ message: `${where}: expected a query variable or a Compute expression — literals are tagged (Compute.u64/i64/f64/bool)`
92
+ })
93
+ }
94
+
95
+ function literal<K extends ScalarKind>(value: ScalarLiteral): ComputeExpr<K> {
96
+ return scalarLiteral("query-var", value) as ComputeExpr<K>
97
+ }
98
+
99
+ function u64(value: bigint): ComputeExpr<"u64"> {
100
+ checkU64(value, "Compute.u64")
101
+ return literal({ u64: value })
102
+ }
103
+
104
+ function i64(value: bigint): ComputeExpr<"i64"> {
105
+ checkI64(value, "Compute.i64")
106
+ return literal({ i64: value })
107
+ }
108
+
109
+ function f64(value: number): ComputeExpr<"f64"> {
110
+ checkF64(value, "Compute.f64")
111
+ return literal({ f64: value })
112
+ }
113
+
114
+ function bool(value: boolean): ComputeExpr<"bool"> {
115
+ checkBool(value, "Compute.bool")
116
+ return literal({ bool: value })
117
+ }
118
+
119
+ type BinaryKind = "add" | "subtract" | "multiply" | "divide"
120
+
121
+ /** The right operand must have the left operand's kind, never an inferred union. */
122
+ type SameKind<L, R> = [OperandKind<R>] extends [OperandKind<L>]
123
+ ? [OperandKind<L>] extends [OperandKind<R>]
124
+ ? R
125
+ : never
126
+ : never
127
+
128
+ function binary<K extends "u64" | "i64" | "f64">(
129
+ op: BinaryKind,
130
+ left: ArithmeticOperand,
131
+ right: ArithmeticOperand
132
+ ): ComputeExpr<K> {
133
+ const where = `Compute.${op}`
134
+ return scalarBinary(where, op, asQueryNode(where, left), asQueryNode(where, right)) as ComputeExpr<K>
135
+ }
136
+
137
+ function add<L extends ArithmeticOperand, R extends ArithmeticOperand>(
138
+ left: L,
139
+ right: R & SameKind<NoInfer<L>, R>
140
+ ): ComputeExpr<OperandKind<L> & OperandKind<R> & ("u64" | "i64" | "f64")> {
141
+ return binary("add", left, right)
142
+ }
143
+
144
+ function subtract<L extends ArithmeticOperand, R extends ArithmeticOperand>(
145
+ left: L,
146
+ right: R & SameKind<NoInfer<L>, R>
147
+ ): ComputeExpr<OperandKind<L> & OperandKind<R> & ("u64" | "i64" | "f64")> {
148
+ return binary("subtract", left, right)
149
+ }
150
+
151
+ function multiply<L extends ArithmeticOperand, R extends ArithmeticOperand>(
152
+ left: L,
153
+ right: R & SameKind<NoInfer<L>, R>
154
+ ): ComputeExpr<OperandKind<L> & OperandKind<R> & ("u64" | "i64" | "f64")> {
155
+ return binary("multiply", left, right)
156
+ }
157
+
158
+ function divide<L extends ArithmeticOperand, R extends ArithmeticOperand>(
159
+ left: L,
160
+ right: R & SameKind<NoInfer<L>, R>
161
+ ): ComputeExpr<OperandKind<L> & OperandKind<R> & ("u64" | "i64" | "f64")> {
162
+ return binary("divide", left, right)
163
+ }
164
+
165
+ function negate<O extends SignedOperand>(operand: O): ComputeExpr<OperandKind<O> & ("i64" | "f64")> {
166
+ const where = "Compute.negate"
167
+ return scalarNegate(where, asQueryNode(where, operand)) as ComputeExpr<OperandKind<O> & ("i64" | "f64")>
168
+ }
169
+
170
+ function toF64(operand: ArithmeticOperand): ComputeExpr<"f64"> {
171
+ const where = "Compute.toF64"
172
+ return scalarCast(where, "toF64", "f64", asQueryNode(where, operand)) as ComputeExpr<"f64">
173
+ }
174
+
175
+ function toF64Exact(operand: ArithmeticOperand): ComputeExpr<"f64"> {
176
+ const where = "Compute.toF64Exact"
177
+ return scalarCast(where, "toF64Exact", "f64", asQueryNode(where, operand)) as ComputeExpr<"f64">
178
+ }
179
+
180
+ function toI64Exact(operand: ArithmeticOperand): ComputeExpr<"i64"> {
181
+ const where = "Compute.toI64Exact"
182
+ return scalarCast(where, "toI64Exact", "i64", asQueryNode(where, operand)) as ComputeExpr<"i64">
183
+ }
184
+
185
+ function toU64Exact(operand: ArithmeticOperand): ComputeExpr<"u64"> {
186
+ const where = "Compute.toU64Exact"
187
+ return scalarCast(where, "toU64Exact", "u64", asQueryNode(where, operand)) as ComputeExpr<"u64">
188
+ }
189
+
190
+ function isNaNExpr(operand: FloatOperand): ComputeExpr<"bool"> {
191
+ const where = "Compute.isNaN"
192
+ return scalarFloatPredicate(where, "isNaN", asQueryNode(where, operand))
193
+ }
194
+
195
+ function isFiniteExpr(operand: FloatOperand): ComputeExpr<"bool"> {
196
+ const where = "Compute.isFinite"
197
+ return scalarFloatPredicate(where, "isFinite", asQueryNode(where, operand))
198
+ }
199
+
200
+ /** Every variable the expression reads (validation binds them like any term). */
201
+ function computeVarsOf(data: QueryNode): readonly AnyVar[] {
202
+ return queryVarsOf(data) as readonly AnyVar[]
203
+ }
204
+
205
+ function computeFieldOf(kind: ScalarKind): AnyField {
206
+ switch (kind) {
207
+ case "u64":
208
+ return u64Field
209
+ case "i64":
210
+ return i64Field
211
+ case "f64":
212
+ return f64Field
213
+ case "bool":
214
+ return boolField
215
+ }
216
+ }
217
+
218
+ const Compute = Object.freeze({
219
+ u64,
220
+ i64,
221
+ f64,
222
+ bool,
223
+ negate,
224
+ add,
225
+ subtract,
226
+ multiply,
227
+ divide,
228
+ toF64,
229
+ toF64Exact,
230
+ toI64Exact,
231
+ toU64Exact,
232
+ isNaN: isNaNExpr,
233
+ isFinite: isFiniteExpr
234
+ })
235
+
236
+ export type { AnyComputeExpr, ComputeExpr, ComputeValue, QueryNode }
237
+ export { Compute, computeFieldOf, computeVarsOf, isComputeExpr, MAX_SCALAR_DEPTH as MAX_COMPUTE_DEPTH }
package/src/query/find.ts CHANGED
@@ -1,9 +1,11 @@
1
- import type { Infer } from "#fields.ts"
1
+ import type { AnyField, Infer } from "#fields.ts"
2
2
  import type { SchemaClasses } from "#law.ts"
3
3
  import type { IntervalVarOk, NumericVarOk } from "#query/atom.ts"
4
+ import type { AnyComputeExpr, ComputeExpr, ComputeValue } from "#query/compute.ts"
4
5
  import type { AnyVar, MintSlotOf } from "#query/scope.ts"
6
+ import type { ScalarKind } from "#scalar.ts"
5
7
 
6
- type FoldOpName = "sum" | "min" | "max" | "pack"
8
+ type FoldOpName = "sum" | "mean" | "min" | "max" | "pack"
7
9
 
8
10
  interface CountAgg {
9
11
  readonly agg: "count"
@@ -16,7 +18,7 @@ interface Agg<Op extends FoldOpName, Over extends AnyVar> {
16
18
 
17
19
  type AnyAgg = CountAgg | Agg<FoldOpName, AnyVar>
18
20
 
19
- type FindEntry = AnyVar | AnyAgg
21
+ type FindEntry = AnyVar | AnyAgg | AnyComputeExpr
20
22
 
21
23
  type FindShape = Readonly<Record<string, FindEntry>>
22
24
 
@@ -29,7 +31,7 @@ function count(): CountAgg {
29
31
  }
30
32
 
31
33
  /**
32
- * Exact checked sum over a NUMERIC (u64/i64) variable — wide accumulator,
34
+ * Exact checked sum over a NUMERIC (u64/i64/f64) variable — wide accumulator,
33
35
  * one finalize range check; overflow is the engine's typed runtime error —
34
36
  * or over the measure (`r.sum(r.duration(w))`). Bool stays refused: a
35
37
  * quantifier is not an addition (R3).
@@ -38,6 +40,11 @@ function sum<const O extends AnyVar>(over: O): Agg<"sum", O> {
38
40
  return aggregate("sum", over)
39
41
  }
40
42
 
43
+ /** Exact sum divided by the binding count, rounded once. F64 inputs only. */
44
+ function mean<const O extends AnyVar>(over: O): Agg<"mean", O> {
45
+ return aggregate("mean", over)
46
+ }
47
+
41
48
  function min<const O extends AnyVar>(over: O): Agg<"min", O> {
42
49
  return aggregate("min", over)
43
50
  }
@@ -54,11 +61,17 @@ type FindEntryOk<E> = E extends AnyVar
54
61
  ? true
55
62
  : E extends CountAgg
56
63
  ? true
57
- : E extends Agg<"sum" | "min" | "max", infer O extends AnyVar>
58
- ? NumericVarOk<O>
59
- : E extends Agg<"pack", infer V extends AnyVar>
60
- ? IntervalVarOk<V>
64
+ : E extends Agg<"mean", infer O extends AnyVar>
65
+ ? O["field"]["kind"] extends "f64"
66
+ ? true
61
67
  : false
68
+ : E extends Agg<"sum" | "min" | "max", infer O extends AnyVar>
69
+ ? NumericVarOk<O>
70
+ : E extends Agg<"pack", infer V extends AnyVar>
71
+ ? IntervalVarOk<V>
72
+ : E extends AnyComputeExpr
73
+ ? true
74
+ : false
62
75
 
63
76
  type CheckFind<F extends FindShape> = {
64
77
  readonly [K in keyof F]: FindEntryOk<F[K]> extends true ? F[K] : never
@@ -72,11 +85,13 @@ type FindValue<E> = E extends AnyVar
72
85
  ? Infer<E["field"]>
73
86
  : E extends CountAgg
74
87
  ? bigint
75
- : E extends Agg<"sum" | "min" | "max", infer O extends AnyVar>
88
+ : E extends Agg<"sum" | "mean" | "min" | "max", infer O extends AnyVar>
76
89
  ? Infer<O["field"]>
77
90
  : E extends Agg<"pack", infer V extends AnyVar>
78
91
  ? Infer<V["field"]>
79
- : never
92
+ : E extends ComputeExpr<infer K extends ScalarKind>
93
+ ? ComputeValue<K>
94
+ : never
80
95
 
81
96
  type RowOfFind<F extends FindShape> = { readonly [K in keyof F]: FindValue<F[K]> }
82
97
 
@@ -86,8 +101,12 @@ type RowOfFind<F extends FindShape> = { readonly [K in keyof F]: FindValue<F[K]>
86
101
  * signature an interior join pairs against (`F` is variable-only there).
87
102
  */
88
103
  type HeadRecordOf<Classes extends SchemaClasses, F extends FindShape> = {
89
- readonly [K in keyof F]: F[K] extends AnyVar ? MintSlotOf<Classes, F[K]> : never
104
+ readonly [K in keyof F]: F[K] extends AnyVar
105
+ ? MintSlotOf<Classes, F[K]>
106
+ : F[K] extends AnyComputeExpr
107
+ ? { readonly field: AnyField; readonly class: undefined }
108
+ : never
90
109
  }
91
110
 
92
111
  export type { Agg, CheckFind, CheckRecFind, FindEntry, FindShape, HeadRecordOf, RowOfFind }
93
- export { count, max, min, pack, sum }
112
+ export { count, max, mean, min, pack, sum }