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