@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/errors.ts ADDED
@@ -0,0 +1,50 @@
1
+ import { Data } from "effect"
2
+
3
+ /**
4
+ * Pure authoring refusals and SDK-invariant defects. These are the ONLY
5
+ * non-`DbError` throw families in the core package: chapter 35 permits
6
+ * programmer-facing AST misuse to throw synchronously (no I/O ever), and a
7
+ * contradiction between SDK and native state is a defect, not a domain
8
+ * outcome. Every operational failure of effectful work is the single
9
+ * `DbError` tagged-reason class in `#runtime-errors.ts` — the old
10
+ * per-surface `Err*` wrapper family is deleted (duplicate error wrappers
11
+ * are banned by C02/C10).
12
+ */
13
+
14
+ /** A pure schema, query, parameter, or value-authoring refusal. */
15
+ export class AuthoringError extends Data.TaggedError("AuthoringError")<{
16
+ readonly message: string
17
+ }> {}
18
+
19
+ /** A contradiction in an SDK/native result, not an application refusal. */
20
+ export class SdkInvariantError extends Data.TaggedError("SdkInvariantError")<{
21
+ readonly message: string
22
+ }> {}
23
+
24
+ export class NativeLoadError extends Data.TaggedError("NativeLoadError")<{
25
+ readonly package: string
26
+ readonly operation: "resolve" | "load"
27
+ readonly message: string
28
+ readonly cause: unknown
29
+ }> {}
30
+
31
+ /** A native bridge call's contextual failure; the exact thrown value is retained. */
32
+ export class NativeOperationError extends Data.TaggedError("NativeOperationError")<{
33
+ readonly operation: string
34
+ readonly cause: unknown
35
+ }> {
36
+ override get message(): string {
37
+ const cause = this.cause
38
+ const detail =
39
+ typeof cause === "object" && cause !== null && "message" in cause && typeof cause.message === "string"
40
+ ? cause.message
41
+ : String(cause)
42
+ return `${this.operation}: ${detail}`
43
+ }
44
+ }
45
+
46
+ export class NativeReportedError extends Data.TaggedError("NativeReportedError")<{
47
+ readonly kind: string
48
+ readonly message: string
49
+ readonly cause: unknown
50
+ }> {}
package/src/face.ts CHANGED
@@ -1,5 +1,5 @@
1
- import * as errors from "@superbuilders/errors"
2
1
  import type { AnyClosed, AnySelectedClosed, PayloadField } from "#closed.ts"
2
+ import { AuthoringError } from "#errors.ts"
3
3
  import type { AnyField, SignatureOf } from "#fields.ts"
4
4
  import type { Same } from "#judgment.ts"
5
5
  import type { AnyRelation, AnySelected, FieldsShape, RelationFields, SelectionBinding } from "#relation.ts"
@@ -121,7 +121,7 @@ function on<S extends FaceSource>(source: S, fields: string | readonly string[])
121
121
  })
122
122
  const value = Object.freeze({ source, projection, data })
123
123
  if (!faceMinted<S, readonly string[]>(value, source, projection)) {
124
- throw errors.new(`face over ${parts.owner.name}: face construction incomplete`)
124
+ throw new AuthoringError({ message: `face over ${parts.owner.name}: face construction incomplete` })
125
125
  }
126
126
  return value
127
127
  }
package/src/fields.ts CHANGED
@@ -1,17 +1,19 @@
1
- import * as errors from "@superbuilders/errors"
2
1
  import { regex } from "arkregex"
2
+ import { Result } from "effect"
3
+ import { AuthoringError } from "#errors.ts"
4
+ import { DbError } from "#runtime-errors.ts"
3
5
  import type { LiteralSpec } from "#spec.ts"
6
+ import { Uuid } from "#uuid.ts"
4
7
 
5
8
  const INTEGER_INDEX_NAME = regex("^(?:0|[1-9][0-9]*)$")
6
9
 
7
10
  /**
8
- * A half-open interval `[start, end)` as a plain value object — the ONE
9
- * interval value type, whatever the field's element type or width label.
10
- * The ray is representable (`end` = the element type's MAX_END); widths
11
- * and signedness are NOT modeled on the value — they are descriptor-type
12
- * labels the engine judges at the typed write boundary. Interval fields
13
- * derive no order (the Rust refusal,,
14
- * so no comparators exist on the value type.
11
+ * A half-open integer interval `[start, end)` as a plain value object —
12
+ * the ONE discrete interval value type, whatever the field's element type
13
+ * or width label. The ray is representable (`end` = the element type's
14
+ * MAX_END); widths and signedness are NOT modeled on the value — they are
15
+ * descriptor-type labels the engine judges at the typed write boundary.
16
+ * Interval fields derive no order, so no comparators exist on the value.
15
17
  */
16
18
  interface IntervalValue {
17
19
  readonly start: bigint
@@ -19,17 +21,55 @@ interface IntervalValue {
19
21
  }
20
22
 
21
23
  /**
22
- * Constructs an interval literal the `start..end` spelling. Half-open
23
- * and nonempty by construction: `start >= end` is a typed construction
24
- * error (parse, don't validate the same invariant Rust's
25
- * `Interval::new` enforces at the host boundary). The value is bare and
26
- * structural: it is assignable to any interval field.
24
+ * A half-open dense float interval `[start, end)` as a plain value object
25
+ * (chapter 11): two canonical binary64 bounds on the dense numeric line.
26
+ * NaN is never an endpoint, signed zero is normalized at the checked
27
+ * constructor and again by the native boundary, and strict `start < end`
28
+ * makes empty spans unrepresentable through {@link span}. Infinite bounds
29
+ * denote a missing bound, not a member point.
27
30
  */
28
- function span(start: bigint, end: bigint): IntervalValue {
29
- if (start >= end) {
30
- throw errors.new(`interval is half-open and nonempty: start must be < end (got ${start}..${end})`)
31
+ interface FloatIntervalValue {
32
+ readonly start: number
33
+ readonly end: number
34
+ }
35
+
36
+ /**
37
+ * Constructs a checked interval literal — the `start..end` spelling.
38
+ * Half-open and nonempty by construction. This is one of chapter 35's
39
+ * "checked small interval constructors": genuinely fallible pure parsing
40
+ * returns `Result` (use `Effect.fromResult(span(...))` inside a generator),
41
+ * never hidden I/O and never a thrown domain outcome.
42
+ *
43
+ * Two element domains, selected by argument type: `span(0n, 60n)` is a
44
+ * discrete integer interval; `span(0.5, 1.5)` is a dense float interval
45
+ * with canonical endpoints (NaN refused, `-0` normalized to `+0`,
46
+ * `-Infinity` legal only as the lower bound and `+Infinity` only as the
47
+ * upper bound — both enforced by strict numeric `start < end`).
48
+ */
49
+ function span(start: bigint, end: bigint): Result.Result<IntervalValue, DbError>
50
+ function span(start: number, end: number): Result.Result<FloatIntervalValue, DbError>
51
+ function span(
52
+ start: bigint | number,
53
+ end: bigint | number
54
+ ): Result.Result<IntervalValue, DbError> | Result.Result<FloatIntervalValue, DbError> {
55
+ if (typeof start === "bigint" && typeof end === "bigint") {
56
+ if (start >= end) {
57
+ return Result.fail(new DbError({ operation: "span", reason: { _tag: "InvalidArgument" } }))
58
+ }
59
+ return Result.succeed(Object.freeze({ start, end }))
60
+ }
61
+ if (typeof start === "number" && typeof end === "number") {
62
+ if (Number.isNaN(start) || Number.isNaN(end)) {
63
+ return Result.fail(new DbError({ operation: "span", reason: { _tag: "InvalidArgument" } }))
64
+ }
65
+ const lo = Object.is(start, -0) ? 0 : start
66
+ const hi = Object.is(end, -0) ? 0 : end
67
+ if (!(lo < hi)) {
68
+ return Result.fail(new DbError({ operation: "span", reason: { _tag: "InvalidArgument" } }))
69
+ }
70
+ return Result.succeed(Object.freeze({ start: lo, end: hi }))
31
71
  }
32
- return Object.freeze({ start, end })
72
+ return Result.fail(new DbError({ operation: "span", reason: { _tag: "InvalidArgument" } }))
33
73
  }
34
74
 
35
75
  /**
@@ -56,27 +96,37 @@ interface StrField {
56
96
  readonly kind: "str"
57
97
  }
58
98
 
59
- interface FreshU64Field {
60
- readonly kind: "u64"
61
- readonly fresh: true
62
- }
63
-
64
99
  interface U64Field {
65
100
  readonly kind: "u64"
66
- readonly fresh: FreshU64Field
67
101
  }
68
102
 
69
103
  interface I64Field {
70
104
  readonly kind: "i64"
71
105
  }
72
106
 
107
+ interface F64Field {
108
+ readonly kind: "f64"
109
+ }
110
+
111
+ /**
112
+ * The application-owned 128-bit identity scalar (chapter 30/34): sixteen
113
+ * exact bytes, spelled as the canonical hyphenated UUID {@link Uuid}
114
+ * host value. There is no `fresh` mark anywhere: the database issues no
115
+ * identity, and key laws are declared statements.
116
+ */
117
+ interface UuidField {
118
+ readonly kind: "uuid"
119
+ }
120
+
73
121
  interface BytesField<Width extends number = number> {
74
122
  readonly kind: "bytes"
75
123
  readonly width: Width
76
124
  }
77
125
 
126
+ type IntervalElementKind = "u64" | "i64" | "f64"
127
+
78
128
  interface IntervalField<
79
- Element extends "u64" | "i64" = "u64" | "i64",
129
+ Element extends IntervalElementKind = IntervalElementKind,
80
130
  Width extends bigint | undefined = bigint | undefined
81
131
  > {
82
132
  readonly kind: "interval"
@@ -96,8 +146,9 @@ type AnyField =
96
146
  | BoolField
97
147
  | StrField
98
148
  | U64Field
99
- | FreshU64Field
100
149
  | I64Field
150
+ | F64Field
151
+ | UuidField
101
152
  | BytesField
102
153
  | IntervalField
103
154
  | AnyClosedIdField
@@ -131,20 +182,26 @@ type Infer<F extends AnyField> = F extends { readonly kind: "bool" }
131
182
  ? bigint
132
183
  : F extends { readonly kind: "i64" }
133
184
  ? bigint
134
- : F extends { readonly kind: "bytes" }
135
- ? Uint8Array
136
- : F extends { readonly kind: "interval" }
137
- ? IntervalValue
138
- : never
185
+ : F extends { readonly kind: "f64" }
186
+ ? number
187
+ : F extends { readonly kind: "uuid" }
188
+ ? Uuid
189
+ : F extends { readonly kind: "bytes" }
190
+ ? Uint8Array
191
+ : F extends { readonly kind: "interval"; readonly element: "f64" }
192
+ ? FloatIntervalValue
193
+ : F extends { readonly kind: "interval" }
194
+ ? IntervalValue
195
+ : never
139
196
 
140
197
  /**
141
198
  * The typed shape refusal shared by every literal machine — the selection
142
- * lowering here, the row marshaler (`marshal.ts`), and the query-literal
199
+ * lowering here, the row codec (`rows.ts`), and the query-literal
143
200
  * tagger (`query/lower.ts`) all throw through this ONE voice; reached only
144
201
  * through ill-typed input (the well-typed surfaces make it unrepresentable).
145
202
  */
146
203
  function literalShapeError(context: string, expected: string, value: unknown): Error {
147
- return errors.new(`${context}: expected ${expected}, got ${typeof value}`)
204
+ return new AuthoringError({ message: `${context}: expected ${expected}, got ${typeof value}` })
148
205
  }
149
206
 
150
207
  function rosterOf(field: AnyField | undefined): AnyClosedRoster | undefined {
@@ -196,17 +253,48 @@ function isIntervalValue(value: unknown): value is IntervalValue {
196
253
  )
197
254
  }
198
255
 
256
+ function isFloatIntervalValue(value: unknown): value is FloatIntervalValue {
257
+ return (
258
+ typeof value === "object" &&
259
+ value !== null &&
260
+ "start" in value &&
261
+ "end" in value &&
262
+ typeof value.start === "number" &&
263
+ typeof value.end === "number"
264
+ )
265
+ }
266
+
199
267
  function handleLiteral(closed: AnyClosedRoster, value: unknown): LiteralSpec {
200
268
  if (typeof value !== "string") {
201
269
  throw literalShapeError("selection literal", `a ${closed.name} handle name (string)`, value)
202
270
  }
203
271
  if (!closed.handles.includes(value)) {
204
- throw errors.new(`"${value}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`)
272
+ throw new AuthoringError({
273
+ message: `"${value}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`
274
+ })
205
275
  }
206
276
  return { kind: "handle", handle: value }
207
277
  }
208
278
 
209
- function intervalLiteral(element: "u64" | "i64", value: unknown): LiteralSpec {
279
+ function intervalLiteral(element: IntervalElementKind, value: unknown): LiteralSpec {
280
+ if (element === "f64") {
281
+ if (!isFloatIntervalValue(value)) {
282
+ throw literalShapeError("selection literal", "float interval ({ start, end } numbers)", value)
283
+ }
284
+ if (Number.isNaN(value.start) || Number.isNaN(value.end) || !(value.start < value.end)) {
285
+ throw new AuthoringError({
286
+ message: "selection literal: a float interval is half-open and nonempty with non-NaN canonical endpoints"
287
+ })
288
+ }
289
+ return {
290
+ kind: "value",
291
+ value: {
292
+ kind: "intervalF64",
293
+ start: Object.is(value.start, -0) ? 0 : value.start,
294
+ end: Object.is(value.end, -0) ? 0 : value.end
295
+ }
296
+ }
297
+ }
210
298
  if (!isIntervalValue(value)) {
211
299
  throw literalShapeError("selection literal", "interval ({ start, end } bigints)", value)
212
300
  }
@@ -218,55 +306,76 @@ function intervalLiteral(element: "u64" | "i64", value: unknown): LiteralSpec {
218
306
 
219
307
  function assertDeclarationOrderKey(where: string, name: string): void {
220
308
  if (INTEGER_INDEX_NAME.test(name)) {
221
- throw errors.new(
222
- `${where}: name ${name} is an integer index — JavaScript object keys re-order integer indices, breaking the declaration-order law; use a non-numeric name`
223
- )
309
+ throw new AuthoringError({
310
+ message: `${where}: name ${name} is an integer index — JavaScript object keys re-order integer indices, breaking the declaration-order law; use a non-numeric name`
311
+ })
224
312
  }
225
313
  if (name.includes(".")) {
226
- throw errors.new(
227
- `${where}: name ${name} contains a dot — the law classes key on the \`relation.field\` coordinate, so a dotted name would alias unrelated slots (macro parity: Rust identifiers cannot contain dots); use a dot-free name`
228
- )
314
+ throw new AuthoringError({
315
+ message: `${where}: name ${name} contains a dot — the law classes key on the \`relation.field\` coordinate, so a dotted name would alias unrelated slots (macro parity: Rust identifiers cannot contain dots); use a dot-free name`
316
+ })
229
317
  }
230
318
  }
231
319
 
232
320
  function assertDeclarationRecord(where: string, record: object): void {
233
321
  const proto = Object.getPrototypeOf(record)
234
322
  if (proto !== Object.prototype && proto !== null) {
235
- throw errors.new(
236
- `${where}: the declaration record's prototype was replaced — a plain \`__proto__: {...}\` entry is the prototype setter, so its key silently vanishes from the declaration; spell it computed (["__proto__"]: {...}) to declare it as data`
237
- )
323
+ throw new AuthoringError({
324
+ message: `${where}: the declaration record's prototype was replaced — a plain \`__proto__: {...}\` entry is the prototype setter, so its key silently vanishes from the declaration; spell it computed (["__proto__"]: {...}) to declare it as data`
325
+ })
238
326
  }
239
327
  }
240
328
 
241
- const freshU64: FreshU64Field = Object.freeze({ kind: "u64", fresh: true })
242
-
243
- const u64: U64Field = Object.freeze({ kind: "u64", fresh: freshU64 })
329
+ const u64: U64Field = Object.freeze({ kind: "u64" })
244
330
 
245
331
  const i64: I64Field = Object.freeze({ kind: "i64" })
246
332
 
333
+ /** Binary64. The native value boundary canonicalizes NaN and signed zero. */
334
+ const f64: F64Field = Object.freeze({ kind: "f64" })
335
+
336
+ /** The application-owned 128-bit identity scalar; no fresh mark exists. */
337
+ const uuid: UuidField = Object.freeze({ kind: "uuid" })
338
+
247
339
  const bool: BoolField = Object.freeze({ kind: "bool" })
248
340
 
249
341
  const str: StrField = Object.freeze({ kind: "str" })
250
342
 
251
343
  function bytes<const Width extends number>(width: Width): BytesField<Width> {
252
344
  if (!Number.isInteger(width) || width < 1 || width > 64) {
253
- throw errors.new(`bytes width must be an integer in 1..=64 (got ${width}) — the range is pinned at declaration`)
345
+ throw new AuthoringError({
346
+ message: `bytes width must be an integer in 1..=64 (got ${width}) — the range is pinned at declaration`
347
+ })
254
348
  }
255
349
  return Object.freeze({ kind: "bytes", width })
256
350
  }
257
351
 
258
- function interval<Element extends U64Field | I64Field>(element: Element): IntervalField<Element["kind"], undefined>
352
+ function interval<Element extends U64Field | I64Field | F64Field>(
353
+ element: Element
354
+ ): IntervalField<Element["kind"], undefined>
259
355
  function interval<Element extends U64Field | I64Field, const Width extends bigint>(
260
356
  element: Element,
261
357
  width: Width
262
358
  ): IntervalField<Element["kind"], Width>
263
- function interval(element: U64Field | I64Field, width?: bigint): IntervalField<"u64" | "i64", bigint | undefined> {
359
+ function interval(
360
+ element: U64Field | I64Field | F64Field,
361
+ width?: bigint
362
+ ): IntervalField<IntervalElementKind, bigint | undefined> {
264
363
  const elementKind = element.kind
265
- if (elementKind !== "u64" && elementKind !== "i64") {
266
- throw errors.new(`interval element must be the u64 or i64 field constructor (got ${elementKind})`)
364
+ if (elementKind !== "u64" && elementKind !== "i64" && elementKind !== "f64") {
365
+ throw new AuthoringError({
366
+ message: `interval element must be the u64, i64 or f64 field constructor (got ${elementKind})`
367
+ })
368
+ }
369
+ if (width !== undefined && elementKind === "f64") {
370
+ throw new AuthoringError({
371
+ message:
372
+ "interval(f64) takes no width — a fixed-width float interval is unrepresentable (rounded start + width is not an exact fixed length on the dense line); applications supply two checked bounds"
373
+ })
267
374
  }
268
375
  if (width !== undefined && width < 1n) {
269
- throw errors.new(`interval width must be >= 1 (got ${width}) — w >= 1 is pinned at declaration`)
376
+ throw new AuthoringError({
377
+ message: `interval width must be >= 1 (got ${width}) — w >= 1 is pinned at declaration`
378
+ })
270
379
  }
271
380
  return Object.freeze({ kind: "interval", element: elementKind, width })
272
381
  }
@@ -305,6 +414,18 @@ function literalOf(field: AnyField, value: unknown): LiteralSpec {
305
414
  }
306
415
  return { kind: "value", value: { kind: "string", value } }
307
416
  }
417
+ case "f64": {
418
+ if (typeof value !== "number") {
419
+ throw literalShapeError("selection literal", "number", value)
420
+ }
421
+ return { kind: "value", value: { kind: "f64", value } }
422
+ }
423
+ case "uuid": {
424
+ if (!Uuid.isUuid(value)) {
425
+ throw literalShapeError("selection literal", "a UUID (canonical UUID text)", value)
426
+ }
427
+ return { kind: "value", value: { kind: "uuid", value } }
428
+ }
308
429
  case "bytes": {
309
430
  if (!(value instanceof Uint8Array)) {
310
431
  throw literalShapeError("selection literal", "Uint8Array", value)
@@ -325,22 +446,27 @@ export type {
325
446
  ClosedHandleTuple,
326
447
  ClosedIdField,
327
448
  ClosedRoster,
328
- FreshU64Field,
449
+ F64Field,
450
+ FloatIntervalValue,
329
451
  I64Field,
330
452
  Infer,
453
+ IntervalElementKind,
331
454
  IntervalField,
332
455
  IntervalValue,
333
456
  SignatureOf,
334
457
  StrField,
335
- U64Field
458
+ U64Field,
459
+ UuidField
336
460
  }
337
461
  export {
338
462
  assertDeclarationOrderKey,
339
463
  assertDeclarationRecord,
340
464
  bool,
341
465
  bytes,
466
+ f64,
342
467
  i64,
343
468
  interval,
469
+ isFloatIntervalValue,
344
470
  isIntervalValue,
345
471
  literalOf,
346
472
  literalShapeError,
@@ -349,5 +475,6 @@ export {
349
475
  signaturesAgree,
350
476
  span,
351
477
  str,
352
- u64
478
+ u64,
479
+ uuid
353
480
  }