@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/schema.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { AuthoringError, SdkInvariantError } from "#errors.ts"
1
2
  /**
2
3
  * `schema` — assembles relations and statements into a theory value (the
3
4
  * `Theory` analog; what `Db.create`/`Db.open` take). Construction-time
@@ -10,12 +11,11 @@
10
11
  * engine's exact rule so `lower` never emits an engine-refused
11
12
  * containment. The type tier is `law.ts`'s `TargetKeyWall` (best effort,
12
13
  * statically known tuples); every OTHER semantic judgment (key-internal
13
- * legality, fresh-on-u64, …) stays the engine's `SchemaError` at
14
+ * legality, …) stays the engine's `SchemaError` at
14
15
  * `Db.create` — the engine is the final authority for every boundary,
15
16
  * this wall just makes the SDK agree with it first.
16
17
  */
17
18
 
18
- import * as errors from "@superbuilders/errors"
19
19
  import type { AnyClosed } from "#closed.ts"
20
20
  import { isClosedMember, sealedFieldOf } from "#closed.ts"
21
21
  import type { FaceData } from "#face.ts"
@@ -37,19 +37,13 @@ function collectImplied(name: string, relations: SchemaRelations): ImpliedKeys {
37
37
  for (const [recordKey, member] of Object.entries(relations)) {
38
38
  assertDeclarationOrderKey(`schema ${name} relation`, recordKey)
39
39
  if (member.name !== recordKey) {
40
- throw errors.new(
41
- `schema ${name}: record key ${recordKey} holds relation ${member.name} — the key must equal the relation's declared name`
42
- )
40
+ throw new AuthoringError({
41
+ message: `schema ${name}: record key ${recordKey} holds relation ${member.name} — the key must equal the relation's declared name`
42
+ })
43
43
  }
44
44
  const projections: Array<readonly string[]> = []
45
45
  if (isClosedMember(member)) {
46
46
  projections.push(Object.freeze(["id"]))
47
- } else {
48
- for (const declared of member.data.fields) {
49
- if ("fresh" in declared.field && declared.field.fresh === true) {
50
- projections.push(Object.freeze([declared.name]))
51
- }
52
- }
53
47
  }
54
48
  for (const projection of projections) {
55
49
  rendered.add(`${member.name}(${projection.join(", ")}) -> ${member.name}`)
@@ -71,12 +65,14 @@ function verifyMembership(name: string, relations: SchemaRelations, statement: S
71
65
  for (const owner of statementOwners(statement)) {
72
66
  const member = relations[owner.name]
73
67
  if (member === undefined) {
74
- throw errors.new(`schema ${name}: relation ${owner.name} is not declared in this schema — ${rendered}`)
68
+ throw new AuthoringError({
69
+ message: `schema ${name}: relation ${owner.name} is not declared in this schema — ${rendered}`
70
+ })
75
71
  }
76
72
  if (member !== owner) {
77
- throw errors.new(
78
- `schema ${name}: statement references a different relation value named ${owner.name} than the one this schema declares — ${rendered}`
79
- )
73
+ throw new AuthoringError({
74
+ message: `schema ${name}: statement references a different relation value named ${owner.name} than the one this schema declares — ${rendered}`
75
+ })
80
76
  }
81
77
  }
82
78
  }
@@ -100,12 +96,14 @@ function verifyBindingHandles(
100
96
  continue
101
97
  }
102
98
  if (roster === undefined) {
103
- throw errors.new(
104
- `schema ${name}: ${face.owner.name}.${binding.field} is not a closed-relation reference — the handle literal ${literal.handle} is legal only on a field carrying a closed relation's roster — ${rendered}`
105
- )
99
+ throw new AuthoringError({
100
+ message: `schema ${name}: ${face.owner.name}.${binding.field} is not a closed-relation reference — the handle literal ${literal.handle} is legal only on a field carrying a closed relation's roster — ${rendered}`
101
+ })
106
102
  }
107
103
  if (!roster.handles.includes(literal.handle)) {
108
- throw errors.new(`schema ${name}: closed relation ${roster.name} has no handle ${literal.handle} — ${rendered}`)
104
+ throw new AuthoringError({
105
+ message: `schema ${name}: closed relation ${roster.name} has no handle ${literal.handle} — ${rendered}`
106
+ })
109
107
  }
110
108
  }
111
109
  }
@@ -185,9 +183,9 @@ function verifyClosedReferenceBinding(
185
183
  }
186
184
  const resolved = closedTargetOf(statements, face.owner.name, binding.field)
187
185
  if (resolved !== roster.name) {
188
- throw errors.new(
189
- `schema ${name}: ${face.owner.name}.${binding.field} spells a ${roster.name} handle, but no declared containment resolves the closed reference — a closed reference is the plain u64 column plus its declared containment; declare contained(on(${face.owner.name}, "${binding.field}"), on(${roster.name}, "id")) — ${rendered}`
190
- )
186
+ throw new AuthoringError({
187
+ message: `schema ${name}: ${face.owner.name}.${binding.field} spells a ${roster.name} handle, but no declared containment resolves the closed reference — a closed reference is the plain u64 column plus its declared containment; declare contained(on(${face.owner.name}, "${binding.field}"), on(${roster.name}, "id")) — ${rendered}`
188
+ })
191
189
  }
192
190
  }
193
191
 
@@ -197,7 +195,7 @@ function verifyClosedReferenceBinding(
197
195
  * `resolve_capacity_target` mirrored exactly): every `contained`/
198
196
  * `mirrors`/`capacity` statement's target projection must resolve a key
199
197
  * of the target relation, judged over the SAME key population the engine
200
- * materializes — the fresh-implied and closed auto-keys
198
+ * materializes — the closed auto-keys
201
199
  * ({@link collectImplied}'s roster) first, then the declared `key`
202
200
  * statements in written order (a key may be declared after its probe, so
203
201
  * this wall runs over the COMPLETE list, never inside the statement
@@ -262,9 +260,9 @@ function verifyTargetKeyFace(
262
260
  if (face.projection.length === 1 && face.projection[0] === "id") {
263
261
  return
264
262
  }
265
- throw errors.new(
266
- `schema ${name}: ${rendered}: closed target ${face.owner.name} is addressed by its synthetic id only — projection (${face.projection.join(", ")}) must be exactly (id) (rewrite the target side as on(${face.owner.name}, "id"))`
267
- )
263
+ throw new AuthoringError({
264
+ message: `schema ${name}: ${rendered}: closed target ${face.owner.name} is addressed by its synthetic id only — projection (${face.projection.join(", ")}) must be exactly (id) (rewrite the target side as on(${face.owner.name}, "id"))`
265
+ })
268
266
  }
269
267
  const roster = [...(implied.get(face.owner.name) ?? []), ...(declared.get(face.owner.name) ?? [])]
270
268
  const want = new Set(face.projection)
@@ -294,9 +292,9 @@ function verifyTargetKeyFace(
294
292
  return descriptor !== undefined && descriptor.kind === "interval"
295
293
  })
296
294
  const hint = pointwise ? "; hint: declare the exact pointwise key `R(prefix…, interval) -> R`" : ""
297
- throw errors.new(
298
- `schema ${name}: ${rendered}: target projection (${face.projection.join(", ")}) matches no declared key of ${face.owner.name} — available keys: ${available}${hint}`
299
- )
295
+ throw new AuthoringError({
296
+ message: `schema ${name}: ${rendered}: target projection (${face.projection.join(", ")}) matches no declared key of ${face.owner.name} — available keys: ${available}${hint}`
297
+ })
300
298
  }
301
299
 
302
300
  type SchemaRelation = AnyRelation | AnyClosed
@@ -325,19 +323,19 @@ function schema<const Rels extends SchemaRelations, const Stmts extends readonly
325
323
  const seen = new Set<string>()
326
324
  for (const statement of statements) {
327
325
  if (!isStatement(statement)) {
328
- throw errors.new(
329
- `schema ${name}: a statement is minted only by key/contained/mirrors/capacity — a structural literal skips the construction-time arity and roster walls`
330
- )
326
+ throw new AuthoringError({
327
+ message: `schema ${name}: a statement is minted only by key/contained/mirrors/capacity — a structural literal skips the construction-time arity and roster walls`
328
+ })
331
329
  }
332
330
  const rendered = renderStatement(statement)
333
331
  verifyMembership(name, relations, statement, rendered)
334
332
  if (implied.rendered.has(rendered)) {
335
- throw errors.new(
336
- `schema ${name}: ${rendered} is redundant here (the fresh mark or closedness already implies it) — and rejected as a duplicate`
337
- )
333
+ throw new AuthoringError({
334
+ message: `schema ${name}: ${rendered} is redundant here (closedness already implies it) — and rejected as a duplicate`
335
+ })
338
336
  }
339
337
  if (seen.has(rendered)) {
340
- throw errors.new(`schema ${name}: duplicate statement — ${rendered}`)
338
+ throw new AuthoringError({ message: `schema ${name}: duplicate statement — ${rendered}` })
341
339
  }
342
340
  seen.add(rendered)
343
341
  verifyHandles(name, statement, rendered)
@@ -346,7 +344,7 @@ function schema<const Rels extends SchemaRelations, const Stmts extends readonly
346
344
  verifyTargetKeys(name, statements, implied.roster)
347
345
  const classes = computeClasses(name, relations, statements)
348
346
  if (!classesComplete<EvaluatedClasses<ClassesOf<Rels, Stmts>>>(classes, relations)) {
349
- throw errors.new(`schema ${name}: class-map construction incomplete`)
347
+ throw new SdkInvariantError({ message: `schema ${name}: class-map construction incomplete` })
350
348
  }
351
349
  return Object.freeze({ name, relations, statements: Object.freeze([...statements]), classes })
352
350
  }
package/src/shape.ts ADDED
@@ -0,0 +1,31 @@
1
+ import type { Query } from "#query/lower.ts"
2
+ import type { ParamsRecord } from "#query/scope.ts"
3
+ /**
4
+ * Shared derived-type vocabulary of the chapter 35 core roster: `S` is a
5
+ * declared core schema value's type, `Rel<S>` its ordinary (writable)
6
+ * relations, `Fact<R>` the inferred row object, `Key<R>` the primary-key
7
+ * object accepted by `QueryReader.get`, and `QueryTemplate<S, P, A>` the
8
+ * immutable schema-bound logical query template. All are derived from the
9
+ * existing typed descriptors — no second hand-maintained roster.
10
+ */
11
+ import type { AnyRelation, Fact } from "#relation.ts"
12
+ import type { AnySchema } from "#schema.ts"
13
+
14
+ /** The ordinary relations of a schema (closed vocabularies are ground axioms, never ingestion targets). */
15
+ type Rel<S extends AnySchema> = Extract<S["relations"][keyof S["relations"]], AnyRelation>
16
+
17
+ /**
18
+ * The primary-key object of a relation: the fields of its FIRST
19
+ * materialized key statement (a closed relation's synthetic `(id)`,
20
+ * otherwise the first declared `key`). Keys are declared statements now —
21
+ * there is no fresh-implied key — so the exact projection is a schema
22
+ * fact, verified at execution against the compiled tables; the type admits
23
+ * any subset of the fact's own fields and the runtime refuses a mismatch
24
+ * with a typed `DbError` before any native work.
25
+ */
26
+ type Key<R extends AnyRelation> = Partial<Fact<R>>
27
+
28
+ /** Chapter 35's spelling of the immutable typed query template. */
29
+ type QueryTemplate<S extends AnySchema, P extends ParamsRecord, A> = Query<S["relations"], A, P>
30
+
31
+ export type { Key, QueryTemplate, Rel }
package/src/spec.ts CHANGED
@@ -4,11 +4,13 @@ type ValueTypeSpec =
4
4
  | { readonly kind: "bool" }
5
5
  | { readonly kind: "u64" }
6
6
  | { readonly kind: "i64" }
7
+ | { readonly kind: "f64" }
8
+ | { readonly kind: "uuid" }
7
9
  | { readonly kind: "string" }
8
10
  | { readonly kind: "fixedBytes"; readonly len: number }
9
11
  | {
10
12
  readonly kind: "interval"
11
- readonly element: "u64" | "i64"
13
+ readonly element: "u64" | "i64" | "f64"
12
14
  readonly width: bigint | undefined
13
15
  }
14
16
 
@@ -16,10 +18,13 @@ type ValueSpec =
16
18
  | { readonly kind: "bool"; readonly value: boolean }
17
19
  | { readonly kind: "u64"; readonly value: bigint }
18
20
  | { readonly kind: "i64"; readonly value: bigint }
21
+ | { readonly kind: "f64"; readonly value: number }
22
+ | { readonly kind: "uuid"; readonly value: string }
19
23
  | { readonly kind: "string"; readonly value: string }
20
24
  | { readonly kind: "fixedBytes"; readonly value: Uint8Array }
21
25
  | { readonly kind: "intervalU64"; readonly start: bigint; readonly end: bigint }
22
26
  | { readonly kind: "intervalI64"; readonly start: bigint; readonly end: bigint }
27
+ | { readonly kind: "intervalF64"; readonly start: number; readonly end: number }
23
28
 
24
29
  type LiteralSpec =
25
30
  | { readonly kind: "value"; readonly value: ValueSpec }
@@ -50,11 +55,15 @@ type CapacityWindowSpec =
50
55
  | { readonly kind: "range"; readonly lo: CapacityBoundSpec; readonly hi: CapacityBoundSpec }
51
56
  | { readonly kind: "floor"; readonly lo: CapacityBoundSpec }
52
57
 
58
+ /**
59
+ * One field: name, structural type, and host newtype label. There is no
60
+ * `fresh` mark: the database issues no identity, and key laws are declared
61
+ * statements (C01, chapter 30).
62
+ */
53
63
  interface FieldSpec {
54
64
  readonly name: string
55
65
  readonly valueType: ValueTypeSpec
56
66
  readonly newtype: string | undefined
57
- readonly fresh: boolean
58
67
  }
59
68
 
60
69
  interface RowSpec {
@@ -170,6 +179,10 @@ function renderLiteral(literal: LiteralSpec): string {
170
179
  case "u64":
171
180
  case "i64":
172
181
  return value.value.toString()
182
+ case "f64":
183
+ return `f64:0x${f64BitsHex(value.value)}`
184
+ case "uuid":
185
+ return `uuid:${value.value}`
173
186
  case "string": {
174
187
  let out = '"'
175
188
  for (const ch of value.value) {
@@ -187,7 +200,24 @@ function renderLiteral(literal: LiteralSpec): string {
187
200
  case "intervalU64":
188
201
  case "intervalI64":
189
202
  return `${value.start}..${value.end}`
203
+ case "intervalF64":
204
+ return `f64:0x${f64BitsHex(value.start)}..f64:0x${f64BitsHex(value.end)}`
205
+ }
206
+ }
207
+
208
+ /**
209
+ * The canonical binary64 bit image as sixteen lowercase hex digits — the
210
+ * one f64 rendering (chapter 11): every NaN is the quiet canonical NaN and
211
+ * `-0` renders as `+0`, mirroring the engine's `f64:0x{bits:016x}`.
212
+ */
213
+ function f64BitsHex(value: number): string {
214
+ const image = new DataView(new ArrayBuffer(8))
215
+ if (Number.isNaN(value)) {
216
+ image.setBigUint64(0, 0x7ff8000000000000n)
217
+ } else {
218
+ image.setFloat64(0, value === 0 ? 0 : value)
190
219
  }
220
+ return image.getBigUint64(0).toString(16).padStart(16, "0")
191
221
  }
192
222
 
193
223
  function renderLiteralSet(set: LiteralSetSpec): string {
@@ -245,4 +275,4 @@ export type {
245
275
  ValueTypeSpec,
246
276
  WeightSpec
247
277
  }
248
- export { renderCapacityWindow, renderLiteral, renderLiteralSet, renderWeight }
278
+ export { f64BitsHex, renderCapacityWindow, renderLiteral, renderLiteralSet, renderWeight }
package/src/statements.ts CHANGED
@@ -1,4 +1,3 @@
1
- import * as errors from "@superbuilders/errors"
2
1
  import {
3
2
  type BoundsOnTarget,
4
3
  type CapacityWeight,
@@ -6,11 +5,11 @@ import {
6
5
  isCapacityWeight,
7
6
  isCapacityWindow,
8
7
  type UnitDimensionBan,
9
- type UnitWindowBan,
10
8
  unitWeight,
11
9
  type WeightOnSource
12
10
  } from "#capacity.ts"
13
11
  import { isClosedMember, sealedFieldOf } from "#closed.ts"
12
+ import { AuthoringError } from "#errors.ts"
14
13
  import { type AnyFace, type FaceData, renderFace, type SameArity, type SameShapes } from "#face.ts"
15
14
  import { type AnyClosedRoster, rosterOf, rostersAgree } from "#fields.ts"
16
15
  import type { AnyRelation, RelationFields } from "#relation.ts"
@@ -82,9 +81,9 @@ function renderRosterSide(roster: AnyClosedRoster | undefined): string {
82
81
  */
83
82
  function assertArityAgreement(source: FaceData, target: FaceData, statement: Statement): void {
84
83
  if (source.projection.length !== target.projection.length) {
85
- throw errors.new(
86
- `${source.owner.name}(${source.projection.join(", ")}) and ${target.owner.name}(${target.projection.join(", ")}) project ${source.projection.length} vs ${target.projection.length} fields — positional pairing requires both faces to project equally many — ${renderStatement(statement)}`
87
- )
84
+ throw new AuthoringError({
85
+ message: `${source.owner.name}(${source.projection.join(", ")}) and ${target.owner.name}(${target.projection.join(", ")}) project ${source.projection.length} vs ${target.projection.length} fields — positional pairing requires both faces to project equally many — ${renderStatement(statement)}`
86
+ })
88
87
  }
89
88
  }
90
89
 
@@ -97,9 +96,9 @@ function assertRosterAgreement(source: FaceData, target: FaceData, statement: St
97
96
  const sourceRoster = rosterOf(sealedFieldOf(source.owner, fieldName))
98
97
  const targetRoster = rosterOf(sealedFieldOf(target.owner, targetField))
99
98
  if (!rostersAgree(sourceRoster, targetRoster)) {
100
- throw errors.new(
101
- `${source.owner.name}.${fieldName} is ${renderRosterSide(sourceRoster)} but ${target.owner.name}.${targetField} is ${renderRosterSide(targetRoster)} — closedness rides the descriptor: a closed reference is spelled with the vocabulary's own id descriptor (one meaning, one spelling), so faces pair closed-with-closed through one roster or bare-with-bare, never across — ${renderStatement(statement)}`
102
- )
99
+ throw new AuthoringError({
100
+ message: `${source.owner.name}.${fieldName} is ${renderRosterSide(sourceRoster)} but ${target.owner.name}.${targetField} is ${renderRosterSide(targetRoster)} — closedness rides the descriptor: a closed reference is spelled with the vocabulary's own id descriptor (one meaning, one spelling), so faces pair closed-with-closed through one roster or bare-with-bare, never across — ${renderStatement(statement)}`
101
+ })
103
102
  }
104
103
  })
105
104
  }
@@ -125,16 +124,16 @@ function key<
125
124
  const Projection extends readonly [keyof RelationFields<R> & string, ...(keyof RelationFields<R> & string)[]]
126
125
  >(relation: R, fields: Projection): KeyStatement<R, Projection> {
127
126
  if (isClosedMember(relation)) {
128
- throw errors.new(
129
- `key(${relation.name}, ...): closedness already materializes ${relation.name}(id) -> ${relation.name} — an explicit key on a closed relation is rejected as a duplicate`
130
- )
127
+ throw new AuthoringError({
128
+ message: `key(${relation.name}, ...): closedness already materializes ${relation.name}(id) -> ${relation.name} — an explicit key on a closed relation is rejected as a duplicate`
129
+ })
131
130
  }
132
131
  const seen = new Set<string>()
133
132
  for (const fieldName of fields) {
134
133
  if (seen.has(fieldName)) {
135
- throw errors.new(
136
- `key(${relation.name}, ...): the projection spells ${fieldName} twice — write it once (the canonical-utterance law: one meaning, one spelling)`
137
- )
134
+ throw new AuthoringError({
135
+ message: `key(${relation.name}, ...): the projection spells ${fieldName} twice — write it once (the canonical-utterance law: one meaning, one spelling)`
136
+ })
138
137
  }
139
138
  seen.add(fieldName)
140
139
  }
@@ -182,19 +181,19 @@ function assertWeightOnSource(weight: WeightSpec, source: FaceData, statement: S
182
181
  }
183
182
  const field = sealedFieldOf(source.owner, weight.field)
184
183
  if (field === undefined) {
185
- throw errors.new(
186
- `${source.owner.name} has no field ${weight.field} — a weight names a field of the SOURCE's own row (the weight vocabulary is closed at the row) — ${renderStatement(statement)}`
187
- )
184
+ throw new AuthoringError({
185
+ message: `${source.owner.name} has no field ${weight.field} — a weight names a field of the SOURCE's own row (the weight vocabulary is closed at the row) — ${renderStatement(statement)}`
186
+ })
188
187
  }
189
188
  if (weight.kind === "field" && field.kind !== "u64") {
190
- throw errors.new(
191
- `${source.owner.name}.${weight.field} is ${field.kind}, not u64 — a weight is u64-encoded (a signed weight would break the polarity scheduler: an insert could lower a sum) — ${renderStatement(statement)}`
192
- )
189
+ throw new AuthoringError({
190
+ message: `${source.owner.name}.${weight.field} is ${field.kind}, not u64 — a weight is u64-encoded (a signed weight would break the polarity scheduler: an insert could lower a sum) — ${renderStatement(statement)}`
191
+ })
193
192
  }
194
193
  if (weight.kind === "durationField" && field.kind !== "interval") {
195
- throw errors.new(
196
- `${source.owner.name}.${weight.field} is ${field.kind}, not an interval — Duration(...) weighs an interval field's measure — ${renderStatement(statement)}`
197
- )
194
+ throw new AuthoringError({
195
+ message: `${source.owner.name}.${weight.field} is ${field.kind}, not an interval — Duration(...) weighs an interval field's measure — ${renderStatement(statement)}`
196
+ })
198
197
  }
199
198
  }
200
199
 
@@ -206,75 +205,83 @@ function assertBoundsOnTarget(window: CapacityWindowSpec, target: FaceData, stat
206
205
  }
207
206
  const field = sealedFieldOf(target.owner, bound.field)
208
207
  if (field === undefined) {
209
- throw errors.new(
210
- `${target.owner.name} has no field ${bound.field} — a dependent bound names a field of the TARGET's own row (bound names resolve against the target's full roster) — ${renderStatement(statement)}`
211
- )
208
+ throw new AuthoringError({
209
+ message: `${target.owner.name} has no field ${bound.field} — a dependent bound names a field of the TARGET's own row (bound names resolve against the target's full roster) — ${renderStatement(statement)}`
210
+ })
212
211
  }
213
212
  if (bound.kind === "field" && field.kind !== "u64") {
214
- throw errors.new(
215
- `${target.owner.name}.${bound.field} is ${field.kind}, not u64 — a dependent bound reads a u64 field of the TARGET row (Duration(...) is the interval-measure spelling) — ${renderStatement(statement)}`
216
- )
213
+ throw new AuthoringError({
214
+ message: `${target.owner.name}.${bound.field} is ${field.kind}, not u64 — a dependent bound reads a u64 field of the TARGET row (Duration(...) is the interval-measure spelling) — ${renderStatement(statement)}`
215
+ })
217
216
  }
218
217
  if (bound.kind === "durationField" && field.kind !== "interval") {
219
- throw errors.new(
220
- `${target.owner.name}.${bound.field} is ${field.kind}, not an interval — Duration(...) bounds by an interval field's measure — ${renderStatement(statement)}`
221
- )
218
+ throw new AuthoringError({
219
+ message: `${target.owner.name}.${bound.field} is ${field.kind}, not an interval — Duration(...) bounds by an interval field's measure — ${renderStatement(statement)}`
220
+ })
222
221
  }
223
222
  }
224
223
  }
225
224
 
225
+ /**
226
+ * The chapter 34 NAMED-OPTIONS capacity call — it replaces the old four
227
+ * positional arguments: `capacity(target, { from, weight?, within })`.
228
+ * Unit weight is the default; `weigh(...)`/`within(...)` remain the only
229
+ * mints for the weight/window values.
230
+ */
226
231
  function capacity<B extends AnyFace, W extends CapacityWindow, A extends AnyFace>(
227
232
  target: B,
228
- window: W & UnitWindowBan<W> & UnitDimensionBan<W> & BoundsOnTarget<W, B>,
229
- source: A & SameArity<B, A> & SameShapes<B, A>
233
+ options: {
234
+ readonly from: A & SameArity<B, A> & SameShapes<B, A>
235
+ readonly within: W & UnitDimensionBan<W> & BoundsOnTarget<W, B>
236
+ }
230
237
  ): CapacityStatement<B["data"], A["data"]>
231
238
  function capacity<B extends AnyFace, M extends CapacityWeight, W extends CapacityWindow, A extends AnyFace>(
232
239
  target: B,
233
- weight: M & WeightOnSource<M, A>,
234
- window: W & BoundsOnTarget<W, B>,
235
- source: A & SameArity<B, A> & SameShapes<B, A>
240
+ options: {
241
+ readonly from: A & SameArity<B, A> & SameShapes<B, A>
242
+ readonly weight: M & WeightOnSource<M, A>
243
+ readonly within: W & BoundsOnTarget<W, B>
244
+ }
236
245
  ): CapacityStatement<B["data"], A["data"]>
237
246
  function capacity(
238
247
  target: AnyFace,
239
- second: unknown,
240
- third: unknown,
241
- fourth?: AnyFace
248
+ options: {
249
+ readonly from: AnyFace
250
+ readonly weight?: unknown
251
+ readonly within: unknown
252
+ }
242
253
  ): CapacityStatement<FaceData, FaceData> {
243
- const weighted = fourth !== undefined
244
- const windowValue = weighted ? third : second
245
- const source = weighted ? fourth : (third as AnyFace)
254
+ if (typeof options !== "object" || options === null || !("from" in options) || !("within" in options)) {
255
+ throw new AuthoringError({
256
+ message:
257
+ "capacity takes named options — capacity(target, { from, weight?, within }) (chapter 34's one spelling; the positional arguments are deleted)"
258
+ })
259
+ }
260
+ const windowValue = options.within
261
+ const source = options.from
246
262
  if (!isCapacityWindow(windowValue)) {
247
- throw errors.new(
248
- "a capacity window is minted only by within() — a structural literal skips the ban table (the canonical-utterance law)"
249
- )
263
+ throw new AuthoringError({
264
+ message:
265
+ "a capacity window is minted only by within() — a structural literal skips the ban table (the canonical-utterance law)"
266
+ })
250
267
  }
251
268
  let weight: WeightSpec = unitWeight
252
- if (weighted) {
253
- if (!isCapacityWeight(second)) {
254
- throw errors.new(
255
- "a capacity weight is minted only by weigh() — a structural literal skips the row-local weight wall"
256
- )
269
+ if (options.weight !== undefined) {
270
+ if (!isCapacityWeight(options.weight)) {
271
+ throw new AuthoringError({
272
+ message: "a capacity weight is minted only by weigh() — a structural literal skips the row-local weight wall"
273
+ })
257
274
  }
258
- weight = second.weight
275
+ weight = options.weight.weight
259
276
  }
260
277
  const window = windowValue.window
261
- if (weight.kind === "unit" && window.kind === "floor" && window.lo.kind === "lit" && window.lo.value === 1n) {
262
- throw errors.new(
263
- "`{1..*}` on the unit instance says only what the bare containment says drop the annotation and write the containment: contained(source, target)"
264
- )
265
- }
266
- if (weight.kind === "unit" && window.kind === "floor") {
267
- throw errors.new(
268
- "`{N..*}` on the unit instance — a bare count floor is refused; weigh the source (`<=[w]{N..*}` stays legal) or drop the bound"
269
- )
270
- }
271
-
272
- // CapacityDimensionMixing twin — ruled 2026-07-24): a count of facts
273
-
278
+ // The unit-floor and vacuous-window ban tables are deleted (C01):
279
+ // `{1..*}`, `{N..*}` and `{0..*}` are accepted canonical laws now.
280
+ // CapacityDimensionMixing (C18) remainsgenuinely different semantics.
274
281
  if (weight.kind === "unit" && window.kind === "range" && window.hi.kind === "durationField") {
275
- throw errors.new(
276
- `a unit (count) window against the duration() bound on ${window.hi.field} mixes dimensions (C18) — weigh the source with weigh(duration(field)), or bound by a u64 field or literal`
277
- )
282
+ throw new AuthoringError({
283
+ message: `a unit (count) window against the duration() bound on ${window.hi.field} mixes dimensions (C18) — weigh the source with weigh(duration(field)), or bound by a u64 field or literal`
284
+ })
278
285
  }
279
286
  const data: CapacityData = Object.freeze({
280
287
  kind: "capacity",
package/src/uuid.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { Result } from "effect"
2
+ import { DbError } from "#runtime-errors.ts"
3
+
4
+ /** Structural UUID spelling, compatible with host generators and ordinary literals.
5
+ * Exact hexadecimal width and canonical case are checked at runtime boundaries.
6
+ */
7
+ type Uuid = `${string}-${string}-${string}-${string}-${string}`
8
+
9
+ const SYNTAX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
10
+ const HEX = "0123456789abcdef"
11
+
12
+ function invalid(operation: string): DbError {
13
+ return new DbError({ operation, reason: { _tag: "InvalidArgument" } })
14
+ }
15
+
16
+ /** Pure parsing: normalize hexadecimal case, never inspect UUID version or variant. */
17
+ function parse(text: string): Result.Result<Uuid, DbError> {
18
+ if (typeof text !== "string" || text.length !== 36) {
19
+ return Result.fail(invalid("Uuid.parse"))
20
+ }
21
+ const canonical = text.toLowerCase()
22
+ return isUuid(canonical) ? Result.succeed(canonical) : Result.fail(invalid("Uuid.parse"))
23
+ }
24
+
25
+ /** Canonical wire validation. No normalization is permitted at the native boundary. */
26
+ function isUuid(value: unknown): value is Uuid {
27
+ return typeof value === "string" && value.length === 36 && SYNTAX.test(value)
28
+ }
29
+
30
+ /** All sixteen-byte payloads are representable, including Nil, Max, and future versions. */
31
+ function fromBytes(bytes: Uint8Array): Result.Result<Uuid, DbError> {
32
+ if (!(bytes instanceof Uint8Array) || bytes.length !== 16) {
33
+ return Result.fail(invalid("Uuid.fromBytes"))
34
+ }
35
+ let text = ""
36
+ for (const byte of bytes) {
37
+ text += HEX.charAt(byte >>> 4)
38
+ text += HEX.charAt(byte & 15)
39
+ }
40
+ return Result.succeed<Uuid>(
41
+ `${text.slice(0, 8)}-${text.slice(8, 12)}-${text.slice(12, 16)}-${text.slice(16, 20)}-${text.slice(20)}`
42
+ )
43
+ }
44
+
45
+ function digit(code: number): number {
46
+ return code <= 57 ? code - 48 : code - 87
47
+ }
48
+
49
+ /** Validate the structural string before decoding fresh bytes in standard UUID order. */
50
+ function toBytes(value: Uuid): Result.Result<Uint8Array, DbError> {
51
+ if (!isUuid(value)) {
52
+ return Result.fail(invalid("Uuid.toBytes"))
53
+ }
54
+ const bytes = new Uint8Array(16)
55
+ let offset = 0
56
+ for (let index = 0; index < 16; index += 1) {
57
+ if (value.charCodeAt(offset) === 45) {
58
+ offset += 1
59
+ }
60
+ bytes[index] = (digit(value.charCodeAt(offset)) << 4) | digit(value.charCodeAt(offset + 1))
61
+ offset += 2
62
+ }
63
+ return Result.succeed(bytes)
64
+ }
65
+
66
+ /** Generate UUIDs with a host library; preserve the result across database retries. */
67
+ const Uuid = Object.freeze({ parse, fromBytes, toBytes, isUuid })
68
+
69
+ export { Uuid }
package/dist/marshal.d.ts DELETED
@@ -1,44 +0,0 @@
1
- /**
2
- * The marshal layer: fact object ⇄ positional `FactValue[]` by field
3
- * ordinal, schema-directed, in ONE place only. The write side lowers named
4
- * host objects to rows in the relation's field-declaration order
5
- * (declaration order = ordinal ids, the macro's law); the read side decodes
6
- * rows back to named objects of BARE structural values — the marshal
7
- * boundary is pure both ways. CAST-FREE, LITERALLY: with structural values
8
- * there is no brand to assert on the way out (the historical "one
9
- * sanctioned marshal cast" died with the brand era), so product code
10
- * carries zero casts — the only trusted seams are the completeness
11
- * PREDICATES below, which verify the checkable half (every declared field
12
- * present) and rely on the store as the proof carrier for the rest: a row
13
- * the engine admitted IS a legal fact of its relation (the same trust
14
- * direction as Rust's typed readback). Shape mismatches here are genuine
15
- * failures and THROW typed; they are never domain data.
16
- *
17
- * THE CLOSED BIJECTION (0.4.0): a closed-referencing cell crosses this
18
- * boundary as its handle NAME — the write side lowers name → u64 row id
19
- * (declaration order = row ids, the sealed roster's own law, ≤ 256 rows),
20
- * the read side lifts id → name, and both directions are total and static
21
- * over the roster. An unknown name is a pointed THROW at the write seam —
22
- * a deliberate UPGRADE over 0.3.0, where any bigint sailed through the
23
- * marshal to a commit-time containment violation; the wrong spelling now
24
- * dies here, before the engine ever sees the row. An out-of-roster id on
25
- * the read side (reachable only in a store whose closed-typed column was
26
- * never pinned by its containment law) is equally pointed — never a
27
- * silent fallback, never `undefined`.
28
- */
29
- import type { AnyClosedRoster, AnyField } from "./fields.js";
30
- import type { FactValue } from "./native.js";
31
- import type { AnyRelation, Fact, FreshKeys, RelationData } from "./relation.js";
32
- declare function isFreshField(field: AnyField): boolean;
33
- type KeyFact<R extends AnyRelation> = [FreshKeys<R>] extends [never] ? Partial<Fact<R>> : {
34
- [K in FreshKeys<R>]: Fact<R>[K];
35
- };
36
- declare function recordOf(fact: object): Readonly<Record<string, unknown>>;
37
- declare function handleOf(context: string, closed: AnyClosedRoster, cell: FactValue): string;
38
- declare function cellOf(context: string, field: AnyField, value: unknown): FactValue;
39
- declare function rowOf(relation: RelationData, fact: Readonly<Record<string, unknown>>): FactValue[];
40
- declare function keyRowOf(relation: RelationData, projection: readonly string[], key: Readonly<Record<string, unknown>>): FactValue[];
41
- declare function factOf<R extends AnyRelation>(relation: R, row: readonly FactValue[]): Fact<R>;
42
- export type { KeyFact };
43
- export { cellOf, factOf, handleOf, isFreshField, keyRowOf, recordOf, rowOf };
44
- //# sourceMappingURL=marshal.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"marshal.d.ts","sourceRoot":"","sources":["../src/marshal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE9E,iBAAS,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAE9C;AAED,KAAK,OAAO,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAChB;KAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAA;AAEtC,iBAAS,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAKjE;AAyBD,iBAAS,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,CAWnF;AAED,iBAAS,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,CA6C3E;AAED,iBAAS,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,EAAE,CAQ3F;AAED,iBAAS,QAAQ,CAChB,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACpC,SAAS,EAAE,CAgBb;AAWD,iBAAS,MAAM,CAAC,CAAC,SAAS,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAsBtF;AAED,YAAY,EAAE,OAAO,EAAE,CAAA;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA"}