@bjornpagen/bumbledb 0.1.0 → 0.3.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 (112) hide show
  1. package/COOKBOOK.md +1450 -0
  2. package/README.md +69 -30
  3. package/dist/closed.d.ts +157 -38
  4. package/dist/closed.d.ts.map +1 -1
  5. package/dist/closed.js +232 -47
  6. package/dist/closed.js.map +1 -1
  7. package/dist/count.d.ts +64 -17
  8. package/dist/count.d.ts.map +1 -1
  9. package/dist/count.js +37 -14
  10. package/dist/count.js.map +1 -1
  11. package/dist/db.d.ts +27 -8
  12. package/dist/db.d.ts.map +1 -1
  13. package/dist/db.js +33 -10
  14. package/dist/db.js.map +1 -1
  15. package/dist/exhume.d.ts +12 -11
  16. package/dist/exhume.d.ts.map +1 -1
  17. package/dist/exhume.js +7 -6
  18. package/dist/exhume.js.map +1 -1
  19. package/dist/face.d.ts +135 -30
  20. package/dist/face.d.ts.map +1 -1
  21. package/dist/face.js +44 -19
  22. package/dist/face.js.map +1 -1
  23. package/dist/fields.d.ts +130 -130
  24. package/dist/fields.d.ts.map +1 -1
  25. package/dist/fields.js +60 -94
  26. package/dist/fields.js.map +1 -1
  27. package/dist/index.d.ts +29 -24
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +19 -14
  30. package/dist/index.js.map +1 -1
  31. package/dist/law.d.ts +224 -0
  32. package/dist/law.d.ts.map +1 -0
  33. package/dist/law.js +224 -0
  34. package/dist/law.js.map +1 -0
  35. package/dist/lower.d.ts +17 -7
  36. package/dist/lower.d.ts.map +1 -1
  37. package/dist/lower.js +66 -24
  38. package/dist/lower.js.map +1 -1
  39. package/dist/marshal.d.ts +36 -21
  40. package/dist/marshal.d.ts.map +1 -1
  41. package/dist/marshal.js +41 -25
  42. package/dist/marshal.js.map +1 -1
  43. package/dist/native.d.ts +6 -2
  44. package/dist/native.d.ts.map +1 -1
  45. package/dist/native.js +2 -2
  46. package/dist/native.js.map +1 -1
  47. package/dist/query/atom.d.ts +370 -174
  48. package/dist/query/atom.d.ts.map +1 -1
  49. package/dist/query/atom.js +75 -172
  50. package/dist/query/atom.js.map +1 -1
  51. package/dist/query/lower.d.ts +312 -109
  52. package/dist/query/lower.d.ts.map +1 -1
  53. package/dist/query/lower.js +906 -387
  54. package/dist/query/lower.js.map +1 -1
  55. package/dist/query/predicate.d.ts +70 -88
  56. package/dist/query/predicate.d.ts.map +1 -1
  57. package/dist/query/predicate.js +118 -72
  58. package/dist/query/predicate.js.map +1 -1
  59. package/dist/query/run.d.ts +19 -16
  60. package/dist/query/run.d.ts.map +1 -1
  61. package/dist/query/run.js +31 -24
  62. package/dist/query/run.js.map +1 -1
  63. package/dist/query/scope.d.ts +178 -127
  64. package/dist/query/scope.d.ts.map +1 -1
  65. package/dist/query/scope.js +111 -108
  66. package/dist/query/scope.js.map +1 -1
  67. package/dist/query/select.d.ts +102 -80
  68. package/dist/query/select.d.ts.map +1 -1
  69. package/dist/query/select.js +39 -34
  70. package/dist/query/select.js.map +1 -1
  71. package/dist/relation.d.ts +48 -38
  72. package/dist/relation.d.ts.map +1 -1
  73. package/dist/relation.js +24 -19
  74. package/dist/relation.js.map +1 -1
  75. package/dist/schema.d.ts +41 -3
  76. package/dist/schema.d.ts.map +1 -1
  77. package/dist/schema.js +34 -9
  78. package/dist/schema.js.map +1 -1
  79. package/dist/spec.d.ts +7 -5
  80. package/dist/spec.d.ts.map +1 -1
  81. package/dist/spec.js.map +1 -1
  82. package/dist/statements.d.ts +91 -36
  83. package/dist/statements.d.ts.map +1 -1
  84. package/dist/statements.js +42 -12
  85. package/dist/statements.js.map +1 -1
  86. package/package.json +4 -3
  87. package/src/closed.ts +448 -95
  88. package/src/count.ts +112 -18
  89. package/src/db.ts +45 -17
  90. package/src/exhume.ts +12 -11
  91. package/src/face.ts +211 -41
  92. package/src/fields.ts +199 -234
  93. package/src/index.ts +78 -61
  94. package/src/law.ts +519 -0
  95. package/src/lower.ts +82 -27
  96. package/src/marshal.ts +48 -30
  97. package/src/native.ts +8 -4
  98. package/src/query/atom.ts +525 -374
  99. package/src/query/lower.ts +1469 -538
  100. package/src/query/predicate.ts +208 -161
  101. package/src/query/run.ts +35 -25
  102. package/src/query/scope.ts +264 -218
  103. package/src/query/select.ts +168 -93
  104. package/src/relation.ts +47 -47
  105. package/src/schema.ts +69 -18
  106. package/src/spec.ts +7 -5
  107. package/src/statements.ts +124 -41
  108. package/dist/brand.d.ts +0 -59
  109. package/dist/brand.d.ts.map +0 -1
  110. package/dist/brand.js +0 -47
  111. package/dist/brand.js.map +0 -1
  112. package/src/brand.ts +0 -82
package/src/fields.ts CHANGED
@@ -1,203 +1,211 @@
1
1
  /**
2
- * Field type constructors — the value half of the `schema!` field grammar
3
- * (`docs/architecture/70-api.md`): `bool`, `u64`, `i64`, `str`, `bytes(n)`,
4
- * `interval(u64|i64[, width])`, each a plain frozen value carrying its
5
- * structural type at runtime and its host value type in a phantom generic.
6
- * Newtypes are DECLARATION-FIRST, one spelling only (owner ruling
7
- * 2026-07-16): `const AccountId = u64.newtype("AccountId")` declares the
8
- * brand ONCE as a value that IS the field, paired with
9
- * `type AccountId = Infer<typeof AccountId>` for signatures; every field
10
- * position references the declared value (`holder: HolderId`). The macro's
11
- * refusals are reproduced representationally: `.newtype` exists only where
12
- * Rust's `as` is legal (u64, i64, bytes, intervals — never bool/str),
13
- * `.fresh` exists only on declared u64 newtypes (the macro demands `as
14
- * NewType` on fresh fields), and no field-level constraint vocabulary of
15
- * any kind exists — `unique`/`fk` are unwritable, not rejected.
2
+ * Field descriptors — the value half of the `schema!` field grammar
3
+ * (`docs/architecture/70-api.md`), MINIMAL edition: `bool`, `u64`, `i64`,
4
+ * `str`, `bytes(n)`, `interval(u64|i64[, width])`, each a plain frozen value
5
+ * that IS its own descriptor type `{ kind, width?, element?, fresh? }` —
6
+ * honest at runtime and in the type alike. A field's VALUE type is its bare
7
+ * structural type (`u64` `bigint`, `str` → `string`, `bytes(n)`
8
+ * `Uint8Array`, intervals `{ start, end }`): no brands, no phantoms, no
9
+ * minting casts. A descriptor carries STRUCTURE ONLY domains are never
10
+ * declared anywhere (the owner ruling: THE LAWS TYPE THE COLUMNS): a
11
+ * field's domain is COMPUTED by `schema()` from the statement list, where
12
+ * the dependencies themselves induce the equivalence classes. The macro's
13
+ * refusals are reproduced representationally: `.fresh` exists only on u64,
14
+ * and no field-level constraint vocabulary of any kind exists —
15
+ * `unique`/`fk` are unwritable, not rejected.
16
16
  */
17
17
 
18
18
  import * as errors from "@superbuilders/errors"
19
- import type { Brand, Interval, IntervalValue } from "#brand.ts"
20
- import { phantom } from "#brand.ts"
21
- import type { LiteralSpec, ValueTypeSpec } from "#spec.ts"
19
+ import type { LiteralSpec } from "#spec.ts"
22
20
 
23
21
  /**
24
- * The typed shape refusal of the selection-literal machinereached only
25
- * through ill-typed input (the well-typed surfaces make it unrepresentable).
22
+ * A half-open interval `[start, end)` as a plain value object the ONE
23
+ * interval value type, whatever the field's element type or width label.
24
+ * The ray is representable (`end` = the element type's MAX_END); widths
25
+ * and signedness are NOT modeled on the value — they are descriptor-type
26
+ * labels the engine judges at the typed write boundary. Interval fields
27
+ * derive no order (the Rust refusal, `docs/architecture/10-data-model.md`),
28
+ * so no comparators exist on the value type.
26
29
  */
27
- function literalShapeError(expected: string, value: unknown): Error {
28
- return errors.new(`selection literal shape mismatch: expected ${expected}, got ${typeof value}`)
29
- }
30
-
31
- /** Narrows an interval literal: a plain object with bigint start/end. */
32
- function isIntervalLiteral(value: unknown): value is IntervalValue {
33
- return (
34
- typeof value === "object" &&
35
- value !== null &&
36
- "start" in value &&
37
- "end" in value &&
38
- typeof value.start === "bigint" &&
39
- typeof value.end === "bigint"
40
- )
30
+ interface IntervalValue {
31
+ readonly start: bigint
32
+ readonly end: bigint
41
33
  }
42
34
 
43
35
  /**
44
- * Resolves one closed-handle literal: the branded id (a bigint at runtime)
45
- * back to its handle NAME through the roster an out-of-roster id is a
46
- * construction error, the belt the type level cannot provide against
47
- * forged brands.
36
+ * Constructs an interval literal the `start..end` spelling. Half-open
37
+ * and nonempty by construction: `start >= end` is a typed construction
38
+ * error (parse, don't validate the same invariant Rust's
39
+ * `Interval::new` enforces at the host boundary). The value is bare and
40
+ * structural: it is assignable to any interval field.
48
41
  */
49
- function handleLiteral(closed: ClosedRoster, value: unknown): LiteralSpec {
50
- if (typeof value !== "bigint") {
51
- throw literalShapeError(`a ${closed.name} handle id (bigint)`, value)
42
+ function span(start: bigint, end: bigint): IntervalValue {
43
+ if (start >= end) {
44
+ throw errors.new(`interval is half-open and nonempty: start must be < end (got ${start}..${end})`)
52
45
  }
53
- const handle = closed.handles[Number(value)]
54
- if (handle === undefined) {
55
- throw errors.new(
56
- `closed relation ${closed.name} has no handle with id ${value} (roster holds ${closed.handles.length})`
57
- )
58
- }
59
- return { kind: "handle", handle }
60
- }
61
-
62
- /** Lowers one interval literal at its element type. */
63
- function intervalLiteral(element: "u64" | "i64", value: unknown): LiteralSpec {
64
- if (!isIntervalLiteral(value)) {
65
- throw literalShapeError("interval ({ start, end } bigints)", value)
66
- }
67
- if (element === "u64") {
68
- return { kind: "value", value: { kind: "intervalU64", start: value.start, end: value.end } }
69
- }
70
- return { kind: "value", value: { kind: "intervalI64", start: value.start, end: value.end } }
46
+ return Object.freeze({ start, end })
71
47
  }
72
48
 
73
49
  /**
74
50
  * A closed relation's roster as seen from a referencing field: the handle
75
- * namespace `where()` selections and ground axioms resolve bare handles
76
- * through (the macro's own rule: a handle is legal exactly on a field whose
77
- * newtype is a closed relation's handle newtype).
51
+ * namespace `where()` selections and ground axioms resolve bare handle ids
52
+ * through (the macro's own rule: a handle is legal exactly on a field that
53
+ * references a closed relation).
78
54
  */
79
55
  interface ClosedRoster {
80
56
  readonly name: string
81
57
  readonly handles: readonly string[]
82
58
  }
83
59
 
84
- /**
85
- * One field's runtime description: structural type, host newtype name, the
86
- * `fresh` mint mark (`minted` — the property name `fresh` is taken by the
87
- * builder surface), and the closed-relation reference when the field is a
88
- * closed relation's id type.
89
- */
90
- interface FieldData<Minted extends boolean = boolean> {
91
- readonly type: ValueTypeSpec
92
- readonly newtype: string | undefined
93
- readonly minted: Minted
94
- readonly closed: ClosedRoster | undefined
60
+ /** The `bool` field descriptor: value type `boolean`. No `.fresh` (macro parity). */
61
+ interface BoolField {
62
+ readonly kind: "bool"
63
+ }
64
+
65
+ /** The `str` field descriptor: value type `string`. No `.fresh` (macro parity). */
66
+ interface StrField {
67
+ readonly kind: "str"
95
68
  }
96
69
 
97
70
  /**
98
- * The base shape every field value shares: runtime data plus the phantom
99
- * host value type `V` (never present at runtime).
71
+ * A `fresh`-marked u64 field descriptor — `id: u64.fresh`. The mark is a
72
+ * structural label (`fresh: true`) in the descriptor type AND on the
73
+ * runtime value; it implies the key `R(field) -> R`, which the ENGINE
74
+ * materializes (`SchemaDescriptor::materialized_statements`), and it makes
75
+ * the field a GENERATOR — `schema()` names its equivalence class by the
76
+ * declaration coordinate (`"Account.id"`). Terminal: no builder property
77
+ * survives the mark.
100
78
  */
101
- interface Field<V> {
102
- readonly data: FieldData
103
- readonly [phantom]?: V
79
+ interface FreshU64Field {
80
+ readonly kind: "u64"
81
+ readonly fresh: true
104
82
  }
105
83
 
106
- /** Extracts a field value's host value type from its phantom. */
107
- type FieldValue<F> = F extends Field<infer V> ? V : never
108
-
109
- /** Any field value, whatever its host value type. */
110
- type AnyField = Field<unknown>
111
-
112
- /** The `bool` field: host type `boolean`. No `.newtype`, no `.fresh` (macro parity). */
113
- interface BoolField extends Field<boolean> {
114
- readonly data: FieldData<false>
84
+ /**
85
+ * The `u64` field descriptor. `.fresh` marks the field as engine-minted —
86
+ * the property doubles as the mark itself: on an unmarked descriptor it
87
+ * holds the marked descriptor, on a marked one it IS the literal `true`
88
+ * (one structural property, read either way).
89
+ */
90
+ interface U64Field {
91
+ readonly kind: "u64"
92
+ readonly fresh: FreshU64Field
115
93
  }
116
94
 
117
- /** The `str` field: host type `string`. No `.newtype`, no `.fresh` (macro parity). */
118
- interface StrField extends Field<string> {
119
- readonly data: FieldData<false>
95
+ /** The `i64` field descriptor. Terminal: `.fresh` is legal on u64 only. */
96
+ interface I64Field {
97
+ readonly kind: "i64"
120
98
  }
121
99
 
122
100
  /**
123
- * A `fresh`-marked u64 newtype field `id: AccountId.fresh` (Rust: `id:
124
- * u64 as AccountId, fresh`). Terminal: the mark implies the key
125
- * `R(field) -> R`, which the ENGINE materializes
126
- * (`SchemaDescriptor::materialized_statements`); `schema()` rejects an
127
- * explicit duplicate of it (macro parity).
101
+ * A `bytes<N>` field descriptor. The width is a descriptor-type label
102
+ * (load-bearing: the engine enforces it at the write boundary) and the
103
+ * value type is bare `Uint8Array`. No order is derived no comparators
104
+ * exist on the value type (the engine refuses order on bytes).
128
105
  */
129
- interface FreshU64Newtype<Name extends string> extends Field<Brand<bigint, Name>> {
130
- readonly data: FieldData<true>
106
+ interface BytesField<Width extends number = number> {
107
+ readonly kind: "bytes"
108
+ readonly width: Width
131
109
  }
132
110
 
133
111
  /**
134
- * A declared u64 newtype — `const AccountId = u64.newtype("AccountId")`
135
- * (Rust: `u64 as AccountId`). The value IS the field: relation blocks
136
- * reference it (`holder: HolderId`). `.fresh` marks it as minted; the
137
- * property exists ONLY here (the macro demands `as NewType` on fresh
138
- * fields, so bare-u64 fresh is unwritable).
112
+ * An interval field descriptor — `interval(i64)` general (rays
113
+ * representable), `interval(u64, w)` the fixed-width family. Element and
114
+ * width are descriptor-type labels; the value type is always the bare
115
+ * {@link IntervalValue}.
139
116
  */
140
- interface U64Newtype<Name extends string> extends Field<Brand<bigint, Name>> {
141
- readonly data: FieldData<false>
142
- readonly fresh: FreshU64Newtype<Name>
117
+ interface IntervalField<
118
+ Element extends "u64" | "i64" = "u64" | "i64",
119
+ Width extends bigint | undefined = bigint | undefined
120
+ > {
121
+ readonly kind: "interval"
122
+ readonly element: Element
123
+ readonly width: Width
143
124
  }
144
125
 
145
- /** The bare `u64` field; `.newtype(name)` declares a branded u64 newtype. */
146
- interface U64Field extends Field<bigint> {
147
- readonly data: FieldData<false>
148
- newtype<const Name extends string>(name: Name): U64Newtype<Name>
149
- }
150
-
151
- /** A declared i64 newtype. Terminal: `fresh` is legal on u64 only. */
152
- interface I64Newtype<Name extends string> extends Field<Brand<bigint, Name>> {
153
- readonly data: FieldData<false>
126
+ /**
127
+ * A closed relation's reference field descriptor (`Kind.id`) — a u64
128
+ * descriptor carrying the closed linkage: the roster resolves bare handle
129
+ * ids in selections and ground axioms, and `schema()` names the id's
130
+ * generator class `"Kind.id"`. Terminal: no `.fresh` — a vocabulary's rows
131
+ * are ground axioms, never minted.
132
+ */
133
+ interface ClosedIdField {
134
+ readonly kind: "u64"
135
+ readonly closed: ClosedRoster
154
136
  }
155
137
 
156
- /** The bare `i64` field; `.newtype(name)` declares a branded i64 newtype. */
157
- interface I64Field extends Field<bigint> {
158
- readonly data: FieldData<false>
159
- newtype<const Name extends string>(name: Name): I64Newtype<Name>
160
- }
138
+ /** Any field descriptor, whatever its kind or marks. */
139
+ type AnyField = BoolField | StrField | U64Field | FreshU64Field | I64Field | BytesField | IntervalField | ClosedIdField
161
140
 
162
- /** A declared `bytes<N>` newtype (no order derived — no comparators exist). */
163
- interface BytesNewtype<Name extends string> extends Field<Brand<Uint8Array, Name>> {
164
- readonly data: FieldData<false>
165
- }
166
-
167
- /** A `bytes<N>` field; `.newtype(name)` declares a branded bytes newtype. */
168
- interface BytesField extends Field<Uint8Array> {
169
- readonly data: FieldData<false>
170
- newtype<const Name extends string>(name: Name): BytesNewtype<Name>
171
- }
141
+ /**
142
+ * The bare structural VALUE type of a field descriptor — the one total
143
+ * definition every fact, result row, and query term reads: `bool` →
144
+ * `boolean`, `str` → `string`, `u64`/`i64` → `bigint`, `bytes<N>` →
145
+ * `Uint8Array`, intervals → {@link IntervalValue}.
146
+ */
147
+ type Infer<F extends AnyField> = F extends { readonly kind: "bool" }
148
+ ? boolean
149
+ : F extends { readonly kind: "str" }
150
+ ? string
151
+ : F extends { readonly kind: "u64" }
152
+ ? bigint
153
+ : F extends { readonly kind: "i64" }
154
+ ? bigint
155
+ : F extends { readonly kind: "bytes" }
156
+ ? Uint8Array
157
+ : F extends { readonly kind: "interval" }
158
+ ? IntervalValue
159
+ : never
172
160
 
173
- /** A declared interval newtype — Rust: `interval<i64> as ActiveDuring`. */
174
- interface IntervalNewtype<Name extends string> extends Field<Interval<Name>> {
175
- readonly data: FieldData<false>
161
+ /**
162
+ * The typed shape refusal of the selection-literal machine — reached only
163
+ * through ill-typed input (the well-typed surfaces make it unrepresentable).
164
+ */
165
+ function literalShapeError(expected: string, value: unknown): Error {
166
+ return errors.new(`selection literal shape mismatch: expected ${expected}, got ${typeof value}`)
176
167
  }
177
168
 
178
- /** An interval field; `.newtype(name)` brands the whole `{ start, end }` object. */
179
- interface IntervalField extends Field<IntervalValue> {
180
- readonly data: FieldData<false>
181
- newtype<const Name extends string>(name: Name): IntervalNewtype<Name>
169
+ /** Narrows an interval literal: a plain object with bigint start/end. */
170
+ function isIntervalLiteral(value: unknown): value is IntervalValue {
171
+ return (
172
+ typeof value === "object" &&
173
+ value !== null &&
174
+ "start" in value &&
175
+ "end" in value &&
176
+ typeof value.start === "bigint" &&
177
+ typeof value.end === "bigint"
178
+ )
182
179
  }
183
180
 
184
181
  /**
185
- * The branded value type of a declared newtype the type half of the
186
- * declaration-first pairing (owner ruling 2026-07-16): `const AccountId =
187
- * u64.newtype("AccountId")` + `type AccountId = Infer<typeof AccountId>`.
188
- * Reads the phantom, so it works on any field value (a closed relation's
189
- * `id` field infers its handle brand the same way).
182
+ * Resolves one closed-handle literal: the handle id (a bare bigint) back to
183
+ * its handle NAME through the roster — an out-of-roster id is a
184
+ * construction error, the belt the type level deliberately does not provide
185
+ * (structural values make any bigint spellable here; the roster judges).
190
186
  */
191
- type Infer<F extends AnyField> = F extends Field<infer V> ? V : never
187
+ function handleLiteral(closed: ClosedRoster, value: unknown): LiteralSpec {
188
+ if (typeof value !== "bigint") {
189
+ throw literalShapeError(`a ${closed.name} handle id (bigint)`, value)
190
+ }
191
+ const handle = closed.handles[Number(value)]
192
+ if (handle === undefined) {
193
+ throw errors.new(
194
+ `closed relation ${closed.name} has no handle with id ${value} (roster holds ${closed.handles.length})`
195
+ )
196
+ }
197
+ return { kind: "handle", handle }
198
+ }
192
199
 
193
- /**
194
- * A closed relation's id field constructor (`Kind.id`) a u64 field
195
- * pre-branded with the closed relation's handle newtype, for use in other
196
- * relations' field blocks (`kind: Kind.id`). Terminal: its newtype IS the
197
- * closed relation, so `.newtype` and `.fresh` do not exist.
198
- */
199
- interface ClosedIdField<Name extends string> extends Field<Brand<bigint, Name>> {
200
- readonly data: FieldData<false>
200
+ /** Lowers one interval literal at its element type. */
201
+ function intervalLiteral(element: "u64" | "i64", value: unknown): LiteralSpec {
202
+ if (!isIntervalLiteral(value)) {
203
+ throw literalShapeError("interval ({ start, end } bigints)", value)
204
+ }
205
+ if (element === "u64") {
206
+ return { kind: "value", value: { kind: "intervalU64", start: value.start, end: value.end } }
207
+ }
208
+ return { kind: "value", value: { kind: "intervalI64", start: value.start, end: value.end } }
201
209
  }
202
210
 
203
211
  /**
@@ -218,84 +226,53 @@ function assertDeclarationOrderKey(where: string, name: string): void {
218
226
  }
219
227
  }
220
228
 
221
- /**
222
- * Builds one frozen {@link FieldData}. Internal seam shared by the field
223
- * constructors here and by `closed()` (which mints {@link ClosedIdField}
224
- * values against its own roster).
225
- */
226
- function fieldData<Minted extends boolean>(
227
- type: ValueTypeSpec,
228
- newtype: string | undefined,
229
- minted: Minted,
230
- closed: ClosedRoster | undefined
231
- ): FieldData<Minted> {
232
- return Object.freeze({ type: Object.freeze(type), newtype, minted, closed })
233
- }
229
+ /** The one fresh-marked u64 descriptor (the `.fresh` property of the unmarked one). */
230
+ const freshU64: FreshU64Field = Object.freeze({ kind: "u64", fresh: true })
234
231
 
235
- /** The one `u64` field constructor value. */
236
- const u64: U64Field = Object.freeze({
237
- data: fieldData({ kind: "u64" }, undefined, false, undefined),
238
- newtype<const Name extends string>(name: Name): U64Newtype<Name> {
239
- const fresh: FreshU64Newtype<Name> = Object.freeze({
240
- data: fieldData({ kind: "u64" }, name, true, undefined)
241
- })
242
- return Object.freeze({
243
- data: fieldData({ kind: "u64" }, name, false, undefined),
244
- fresh
245
- })
246
- }
247
- })
232
+ /** The one `u64` constructor value. */
233
+ const u64: U64Field = Object.freeze({ kind: "u64", fresh: freshU64 })
248
234
 
249
- /** The one `i64` field constructor value. */
250
- const i64: I64Field = Object.freeze({
251
- data: fieldData({ kind: "i64" }, undefined, false, undefined),
252
- newtype<const Name extends string>(name: Name): I64Newtype<Name> {
253
- return Object.freeze({ data: fieldData({ kind: "i64" }, name, false, undefined) })
254
- }
255
- })
235
+ /** The one `i64` constructor value. */
236
+ const i64: I64Field = Object.freeze({ kind: "i64" })
256
237
 
257
- /** The one `bool` field constructor value. */
258
- const bool: BoolField = Object.freeze({
259
- data: fieldData({ kind: "bool" }, undefined, false, undefined)
260
- })
238
+ /** The one `bool` constructor value. */
239
+ const bool: BoolField = Object.freeze({ kind: "bool" })
261
240
 
262
- /** The one `str` field constructor value. */
263
- const str: StrField = Object.freeze({
264
- data: fieldData({ kind: "string" }, undefined, false, undefined)
265
- })
241
+ /** The one `str` constructor value. */
242
+ const str: StrField = Object.freeze({ kind: "str" })
266
243
 
267
244
  /**
268
- * The `bytes<N>` field constructor. The width is mandatory and part of the
269
- * type; `len` is validated to 1..=64 here because the grammar pins that
270
- * range at declaration (`docs/architecture/70-api.md` § the `schema!`
271
- * grammar: N ∈ 1..=64 — bare `bytes` does not parse), the macro-expansion
272
- * boundary's analog being construction.
245
+ * The `bytes<N>` field constructor. The width is mandatory and a
246
+ * descriptor-type label; `width` is validated to 1..=64 here because the
247
+ * grammar pins that range at declaration (`docs/architecture/70-api.md`
248
+ * § the `schema!` grammar: N ∈ 1..=64 — bare `bytes` does not parse), the
249
+ * macro-expansion boundary's analog being construction.
273
250
  */
274
- function bytes(len: number): BytesField {
275
- if (!Number.isInteger(len) || len < 1 || len > 64) {
251
+ function bytes<const Width extends number>(width: Width): BytesField<Width> {
252
+ if (!Number.isInteger(width) || width < 1 || width > 64) {
276
253
  throw errors.new(
277
- `bytes width must be an integer in 1..=64 (got ${len}) — docs/architecture/70-api.md pins the range at declaration`
254
+ `bytes width must be an integer in 1..=64 (got ${width}) — docs/architecture/70-api.md pins the range at declaration`
278
255
  )
279
256
  }
280
- return Object.freeze({
281
- data: fieldData({ kind: "fixedBytes", len }, undefined, false, undefined),
282
- newtype<const Name extends string>(name: Name): BytesNewtype<Name> {
283
- return Object.freeze({ data: fieldData({ kind: "fixedBytes", len }, name, false, undefined) })
284
- }
285
- })
257
+ return Object.freeze({ kind: "bytes", width })
286
258
  }
287
259
 
288
260
  /**
289
261
  * The interval field constructor — `interval(u64)` / `interval(i64)` for
290
262
  * the general type (rays representable), `interval(u64, w)` for the
291
- * fixed-width family whose width IS the type. The element is spelled with
292
- * the u64/i64 field constructor values themselves, never a string. `width
293
- * >= 1` is validated here because the grammar pins it at declaration
263
+ * fixed-width family whose width IS a descriptor-type label. The element is
264
+ * spelled with the u64/i64 constructor values themselves, never a string.
265
+ * `width >= 1` is validated here because the grammar pins it at declaration
294
266
  * (`docs/architecture/70-api.md`: w ≥ 1; `interval<u64, 0>` is an
295
267
  * expansion error naming the field).
296
268
  */
297
- function interval(element: U64Field | I64Field, width?: bigint): IntervalField {
298
- const elementKind = element.data.type.kind
269
+ function interval<Element extends U64Field | I64Field>(element: Element): IntervalField<Element["kind"], undefined>
270
+ function interval<Element extends U64Field | I64Field, const Width extends bigint>(
271
+ element: Element,
272
+ width: Width
273
+ ): IntervalField<Element["kind"], Width>
274
+ function interval(element: U64Field | I64Field, width?: bigint): IntervalField<"u64" | "i64", bigint | undefined> {
275
+ const elementKind = element.kind
299
276
  if (elementKind !== "u64" && elementKind !== "i64") {
300
277
  throw errors.new(`interval element must be the u64 or i64 field constructor (got ${elementKind})`)
301
278
  }
@@ -304,13 +281,7 @@ function interval(element: U64Field | I64Field, width?: bigint): IntervalField {
304
281
  `interval width must be >= 1 (got ${width}) — docs/architecture/70-api.md pins w >= 1 at declaration`
305
282
  )
306
283
  }
307
- const type: ValueTypeSpec = { kind: "interval", element: elementKind, width }
308
- return Object.freeze({
309
- data: fieldData(type, undefined, false, undefined),
310
- newtype<const Name extends string>(name: Name): IntervalNewtype<Name> {
311
- return Object.freeze({ data: fieldData(type, name, false, undefined) })
312
- }
313
- })
284
+ return Object.freeze({ kind: "interval", element: elementKind, width })
314
285
  }
315
286
 
316
287
  /**
@@ -318,15 +289,15 @@ function interval(element: U64Field | I64Field, width?: bigint): IntervalField {
318
289
  * {@link LiteralSpec} — the selection-literal machine ground axioms and
319
290
  * `where()` bindings both ride (one machine, same errors — the macro's own
320
291
  * rule). A value on a closed-reference field resolves to its handle NAME
321
- * (the id is re-verified against the roster: an out-of-roster id is a
292
+ * (the id is verified against the roster: an out-of-roster id is a
322
293
  * construction error); everything else lowers to a plain value tagged by
323
- * the field's structural type.
294
+ * the field's structural kind.
324
295
  */
325
- function literalOf(field: FieldData, value: unknown): LiteralSpec {
326
- if (field.closed !== undefined) {
296
+ function literalOf(field: AnyField, value: unknown): LiteralSpec {
297
+ if ("closed" in field) {
327
298
  return handleLiteral(field.closed, value)
328
299
  }
329
- switch (field.type.kind) {
300
+ switch (field.kind) {
330
301
  case "bool": {
331
302
  if (typeof value !== "boolean") {
332
303
  throw literalShapeError("boolean", value)
@@ -345,20 +316,20 @@ function literalOf(field: FieldData, value: unknown): LiteralSpec {
345
316
  }
346
317
  return { kind: "value", value: { kind: "i64", value } }
347
318
  }
348
- case "string": {
319
+ case "str": {
349
320
  if (typeof value !== "string") {
350
321
  throw literalShapeError("string", value)
351
322
  }
352
323
  return { kind: "value", value: { kind: "string", value } }
353
324
  }
354
- case "fixedBytes": {
325
+ case "bytes": {
355
326
  if (!(value instanceof Uint8Array)) {
356
327
  throw literalShapeError("Uint8Array", value)
357
328
  }
358
329
  return { kind: "value", value: { kind: "fixedBytes", value } }
359
330
  }
360
331
  case "interval":
361
- return intervalLiteral(field.type.element, value)
332
+ return intervalLiteral(field.element, value)
362
333
  }
363
334
  }
364
335
 
@@ -366,20 +337,14 @@ export type {
366
337
  AnyField,
367
338
  BoolField,
368
339
  BytesField,
369
- BytesNewtype,
370
340
  ClosedIdField,
371
341
  ClosedRoster,
372
- Field,
373
- FieldData,
374
- FieldValue,
375
- FreshU64Newtype,
342
+ FreshU64Field,
376
343
  I64Field,
377
- I64Newtype,
378
344
  Infer,
379
345
  IntervalField,
380
- IntervalNewtype,
346
+ IntervalValue,
381
347
  StrField,
382
- U64Field,
383
- U64Newtype
348
+ U64Field
384
349
  }
385
- export { assertDeclarationOrderKey, bool, bytes, fieldData, i64, interval, literalOf, str, u64 }
350
+ export { assertDeclarationOrderKey, bool, bytes, i64, interval, literalOf, span, str, u64 }