@bjornpagen/bumbledb 0.3.0 → 0.4.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 +66 -46
- package/README.md +28 -15
- package/dist/closed.d.ts +50 -73
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +38 -109
- package/dist/closed.js.map +1 -1
- package/dist/db.d.ts +4 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +26 -3
- package/dist/db.js.map +1 -1
- package/dist/face.d.ts +39 -39
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +7 -16
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +28 -14
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +15 -14
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/marshal.d.ts +33 -6
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js +67 -6
- package/dist/marshal.js.map +1 -1
- package/dist/query/atom.d.ts +59 -14
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +3 -0
- package/dist/query/atom.js.map +1 -1
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +251 -26
- package/dist/query/lower.js.map +1 -1
- package/dist/query/run.d.ts +15 -5
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +26 -6
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +35 -13
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +16 -4
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +8 -7
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +32 -10
- package/dist/relation.js.map +1 -1
- package/dist/spec.d.ts +3 -2
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +10 -4
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +79 -7
- package/dist/statements.js.map +1 -1
- package/package.json +2 -2
- package/src/closed.ts +72 -179
- package/src/db.ts +42 -5
- package/src/face.ts +34 -45
- package/src/fields.ts +46 -34
- package/src/index.ts +1 -2
- package/src/marshal.ts +74 -7
- package/src/query/atom.ts +58 -15
- package/src/query/lower.ts +301 -28
- package/src/query/run.ts +26 -6
- package/src/query/scope.ts +49 -15
- package/src/relation.ts +45 -17
- package/src/spec.ts +3 -2
- package/src/statements.ts +86 -7
package/src/face.ts
CHANGED
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
* positional: tuple order is preserved in the type, and the statement
|
|
12
12
|
* constructors pair the two sides' tuples by arity ({@link SameArity}) AND
|
|
13
13
|
* by structural shape ({@link SameShapes}) — every projected field's
|
|
14
|
-
* kind/width/element
|
|
15
|
-
* kernel: descriptors are pure structure
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* kind/width/element/roster quadruple is read off the schema type (the
|
|
15
|
+
* minimal kernel: descriptors are pure structure, and a closed reference's
|
|
16
|
+
* roster IS part of that structure) and compared positionwise. There is no
|
|
17
|
+
* domain to compare at construction — domains are LAW-BORN: the statements
|
|
18
|
+
* themselves define the equivalence classes, and `schema()` is where they
|
|
19
|
+
* aggregate and get judged (the one-generator-per-class wall).
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
22
|
import * as errors from "@superbuilders/errors"
|
|
@@ -54,27 +55,6 @@ function faceParts(source: FaceSource): {
|
|
|
54
55
|
return { owner: source, selection: emptySelection }
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
/**
|
|
58
|
-
* A disjunctive literal set for a selection binding — `field == {A, B}`.
|
|
59
|
-
* The signature of {@link oneOf} demands two leading literals, so the
|
|
60
|
-
* one-element set (banned: it is the bare literal) and the empty set
|
|
61
|
-
* (banned: it selects nothing) are unwritable.
|
|
62
|
-
*/
|
|
63
|
-
interface OneOf<V> {
|
|
64
|
-
readonly literals: readonly [V, V, ...V[]]
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Constructs a literal set (read disjunctively) for a `where()` binding.
|
|
69
|
-
* Two leading arguments by signature: the degenerate sets have no spelling
|
|
70
|
-
* (the canonical-utterance law, `docs/architecture/70-api.md`).
|
|
71
|
-
*/
|
|
72
|
-
function oneOf<V>(first: V, second: V, ...rest: V[]): OneOf<V> {
|
|
73
|
-
const literals: readonly [V, V, ...V[]] = [first, second, ...rest]
|
|
74
|
-
Object.freeze(literals)
|
|
75
|
-
return Object.freeze({ literals })
|
|
76
|
-
}
|
|
77
|
-
|
|
78
58
|
/** The relation a face projects from — ordinary or closed. */
|
|
79
59
|
type FaceOwner = AnyRelation | AnyClosed
|
|
80
60
|
|
|
@@ -131,20 +111,28 @@ type FaceFields<S extends FaceSource> = S extends AnySelected
|
|
|
131
111
|
: never
|
|
132
112
|
|
|
133
113
|
/**
|
|
134
|
-
* One descriptor's structural comparand: the kind/width/element
|
|
135
|
-
* exactly the structure the minimal kernel carries
|
|
136
|
-
* engine's Q1 law pairs positions
|
|
137
|
-
* bound (bytes<16> vs bytes<32>
|
|
138
|
-
*
|
|
139
|
-
* domain and not a width, so
|
|
140
|
-
* (
|
|
141
|
-
*
|
|
142
|
-
* u64-vs-i64 interval
|
|
114
|
+
* One descriptor's structural comparand: the kind/width/element/roster
|
|
115
|
+
* quadruple — exactly the structure the minimal kernel carries. The first
|
|
116
|
+
* three slots compare exactly as the engine's Q1 law pairs positions
|
|
117
|
+
* (`schema/validate.rs`): a `bytes` width is bound (bytes<16> vs bytes<32>
|
|
118
|
+
* mismatch), while an INTERVAL width is FREE — the pointwise judgments
|
|
119
|
+
* quantify over points, which carry an element domain and not a width, so
|
|
120
|
+
* `interval(u64)` pairs with `interval(u64, 1n)` (recipe 9's extent/slot
|
|
121
|
+
* mirrors, recipe 29's mixed-width zones) and the width slot reads
|
|
122
|
+
* `undefined` for every interval. Elements stay bound: u64-vs-i64 interval
|
|
123
|
+
* pairs still mismatch. The ROSTER slot is SDK-only structure (the engine's
|
|
124
|
+
* wire carries plain u64s): a closed reference contributes its handle
|
|
125
|
+
* union, every other kind `undefined`, so a plain u64 face cannot pair with
|
|
126
|
+
* a closed `[id]` face — the vocabulary's own descriptor (`Kind.id`) is the
|
|
127
|
+
* ONE spelling of a closed reference at this surface, and a bare column
|
|
128
|
+
* cannot alias a vocabulary through a declared law. The runtime twin is the
|
|
129
|
+
* statement constructors' roster-identity walk (`statements.ts`).
|
|
143
130
|
*/
|
|
144
131
|
type ShapeOf<F extends AnyField> = readonly [
|
|
145
132
|
F["kind"],
|
|
146
133
|
F extends { readonly element: unknown } ? undefined : F extends { readonly width: infer W } ? W : undefined,
|
|
147
|
-
F extends { readonly element: infer E } ? E : undefined
|
|
134
|
+
F extends { readonly element: infer E } ? E : undefined,
|
|
135
|
+
F extends { readonly closed: { readonly handles: readonly (infer H extends string)[] } } ? H : undefined
|
|
148
136
|
]
|
|
149
137
|
|
|
150
138
|
/** One field's structural shape within a declared field block (`undefined` when the name is foreign). */
|
|
@@ -216,10 +204,11 @@ type SameArity<A extends AnyFace, B extends AnyFace> =
|
|
|
216
204
|
* bijection, or window project structurally incompatible fields at any
|
|
217
205
|
* position, this type is intersected into the second face's parameter and
|
|
218
206
|
* names both shape tuples — a u64 face against a str face, a bytes width
|
|
219
|
-
* mismatch,
|
|
207
|
+
* mismatch, an interval element mismatch, or a bare column against a
|
|
208
|
+
* closed reference (the roster slot) is a COMPILE error.
|
|
220
209
|
*/
|
|
221
210
|
interface FaceShapeMismatch<Left, Right> {
|
|
222
|
-
readonly "face shape mismatch — positionwise kind, width, and
|
|
211
|
+
readonly "face shape mismatch — positionwise kind, width, element, and closed roster must be equal on both sides": readonly [
|
|
223
212
|
Left,
|
|
224
213
|
Right
|
|
225
214
|
]
|
|
@@ -229,11 +218,12 @@ interface FaceShapeMismatch<Left, Right> {
|
|
|
229
218
|
* Resolves to `unknown` (a no-op intersection) when the two faces project
|
|
230
219
|
* positionwise-equal structural shapes, and to {@link FaceShapeMismatch}
|
|
231
220
|
* otherwise. Equality is mutual tuple assignability over the
|
|
232
|
-
* kind/width/element
|
|
233
|
-
* deliberately: there is no domain to compare
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
221
|
+
* kind/width/element/roster quadruples. This is the whole
|
|
222
|
+
* construction-time wall — deliberately: there is no domain to compare
|
|
223
|
+
* here (the roster is descriptor STRUCTURE, not a domain). The domain wall
|
|
224
|
+
* lives where domains are BORN: `schema()` computes every field's class
|
|
225
|
+
* from the statement list and holds the one-generator-per-class law, and
|
|
226
|
+
* query joins compare class names off the schema type.
|
|
237
227
|
*/
|
|
238
228
|
type SameShapes<A extends AnyFace, B extends AnyFace> =
|
|
239
229
|
FaceShapes<A> extends FaceShapes<B>
|
|
@@ -323,9 +313,8 @@ export type {
|
|
|
323
313
|
FaceShapeMismatch,
|
|
324
314
|
FaceShapes,
|
|
325
315
|
FaceSource,
|
|
326
|
-
OneOf,
|
|
327
316
|
OwnerOf,
|
|
328
317
|
SameArity,
|
|
329
318
|
SameShapes
|
|
330
319
|
}
|
|
331
|
-
export { on,
|
|
320
|
+
export { on, renderFace }
|
package/src/fields.ts
CHANGED
|
@@ -50,11 +50,15 @@ function span(start: bigint, end: bigint): IntervalValue {
|
|
|
50
50
|
* A closed relation's roster as seen from a referencing field: the handle
|
|
51
51
|
* namespace `where()` selections and ground axioms resolve bare handle ids
|
|
52
52
|
* through (the macro's own rule: a handle is legal exactly on a field that
|
|
53
|
-
* references a closed relation).
|
|
53
|
+
* references a closed relation). The handle union is PRECISE — `H` carries
|
|
54
|
+
* the literal handle names in declaration order (the unbound `string`
|
|
55
|
+
* default exists only as the fallback where no roster is in scope); the
|
|
56
|
+
* runtime twin is the same frozen declaration-order array that was always
|
|
57
|
+
* there.
|
|
54
58
|
*/
|
|
55
|
-
interface ClosedRoster {
|
|
59
|
+
interface ClosedRoster<H extends string = string> {
|
|
56
60
|
readonly name: string
|
|
57
|
-
readonly handles: readonly
|
|
61
|
+
readonly handles: readonly H[]
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
/** The `bool` field descriptor: value type `boolean`. No `.fresh` (macro parity). */
|
|
@@ -125,14 +129,16 @@ interface IntervalField<
|
|
|
125
129
|
|
|
126
130
|
/**
|
|
127
131
|
* A closed relation's reference field descriptor (`Kind.id`) — a u64
|
|
128
|
-
* descriptor carrying the closed linkage: the roster resolves
|
|
129
|
-
*
|
|
130
|
-
* generator class `"Kind.id"`.
|
|
131
|
-
*
|
|
132
|
+
* descriptor carrying the closed linkage: the roster resolves handle
|
|
133
|
+
* literals in selections and ground axioms, and `schema()` names the id's
|
|
134
|
+
* generator class `"Kind.id"`. The handle union `H` is the field's VALUE
|
|
135
|
+
* TYPE (see {@link Infer}); `kind: "u64"` stays load-bearing for the class
|
|
136
|
+
* map and JoinOk, which compare kind/class/width/element. Terminal: no
|
|
137
|
+
* `.fresh` — a vocabulary's rows are ground axioms, never minted.
|
|
132
138
|
*/
|
|
133
|
-
interface ClosedIdField {
|
|
139
|
+
interface ClosedIdField<H extends string = string> {
|
|
134
140
|
readonly kind: "u64"
|
|
135
|
-
readonly closed: ClosedRoster
|
|
141
|
+
readonly closed: ClosedRoster<H>
|
|
136
142
|
}
|
|
137
143
|
|
|
138
144
|
/** Any field descriptor, whatever its kind or marks. */
|
|
@@ -142,21 +148,28 @@ type AnyField = BoolField | StrField | U64Field | FreshU64Field | I64Field | Byt
|
|
|
142
148
|
* The bare structural VALUE type of a field descriptor — the one total
|
|
143
149
|
* definition every fact, result row, and query term reads: `bool` →
|
|
144
150
|
* `boolean`, `str` → `string`, `u64`/`i64` → `bigint`, `bytes<N>` →
|
|
145
|
-
* `Uint8Array`, intervals → {@link IntervalValue}
|
|
151
|
+
* `Uint8Array`, intervals → {@link IntervalValue}, and a closed reference →
|
|
152
|
+
* its PRECISE handle union (`"DirectPass" | "Failed"` — the string-literal
|
|
153
|
+
* union IS the value type at the TS surface; the engine keeps u64 row ids
|
|
154
|
+
* and the marshal owns the bijection). The closed arm precedes the `u64`
|
|
155
|
+
* arm because a closed reference is structurally a u64 descriptor plus the
|
|
156
|
+
* roster.
|
|
146
157
|
*/
|
|
147
158
|
type Infer<F extends AnyField> = F extends { readonly kind: "bool" }
|
|
148
159
|
? boolean
|
|
149
160
|
: F extends { readonly kind: "str" }
|
|
150
161
|
? string
|
|
151
|
-
: F extends { readonly
|
|
152
|
-
?
|
|
153
|
-
: F extends { readonly kind: "
|
|
162
|
+
: F extends { readonly closed: { readonly handles: readonly (infer H extends string)[] } }
|
|
163
|
+
? H
|
|
164
|
+
: F extends { readonly kind: "u64" }
|
|
154
165
|
? bigint
|
|
155
|
-
: F extends { readonly kind: "
|
|
156
|
-
?
|
|
157
|
-
: F extends { readonly kind: "
|
|
158
|
-
?
|
|
159
|
-
:
|
|
166
|
+
: F extends { readonly kind: "i64" }
|
|
167
|
+
? bigint
|
|
168
|
+
: F extends { readonly kind: "bytes" }
|
|
169
|
+
? Uint8Array
|
|
170
|
+
: F extends { readonly kind: "interval" }
|
|
171
|
+
? IntervalValue
|
|
172
|
+
: never
|
|
160
173
|
|
|
161
174
|
/**
|
|
162
175
|
* The typed shape refusal of the selection-literal machine — reached only
|
|
@@ -179,22 +192,22 @@ function isIntervalLiteral(value: unknown): value is IntervalValue {
|
|
|
179
192
|
}
|
|
180
193
|
|
|
181
194
|
/**
|
|
182
|
-
* Resolves one closed-handle literal: the handle
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
195
|
+
* Resolves one closed-handle literal: the handle NAME, verified against the
|
|
196
|
+
* roster — an unknown name is a construction error, the belt the wide
|
|
197
|
+
* fallback type deliberately does not provide (structural values make any
|
|
198
|
+
* string spellable here; the roster judges). The name IS the value at the
|
|
199
|
+
* TS surface (the drizzle law); the wire literal already crossed as
|
|
200
|
+
* `{ kind: "handle", handle }`, so the output — and every fingerprint
|
|
201
|
+
* derived from it — is untouched.
|
|
186
202
|
*/
|
|
187
203
|
function handleLiteral(closed: ClosedRoster, value: unknown): LiteralSpec {
|
|
188
|
-
if (typeof value !== "
|
|
189
|
-
throw literalShapeError(`a ${closed.name} handle
|
|
204
|
+
if (typeof value !== "string") {
|
|
205
|
+
throw literalShapeError(`a ${closed.name} handle name (string)`, value)
|
|
190
206
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
throw errors.new(
|
|
194
|
-
`closed relation ${closed.name} has no handle with id ${value} (roster holds ${closed.handles.length})`
|
|
195
|
-
)
|
|
207
|
+
if (!closed.handles.includes(value)) {
|
|
208
|
+
throw errors.new(`"${value}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`)
|
|
196
209
|
}
|
|
197
|
-
return { kind: "handle", handle }
|
|
210
|
+
return { kind: "handle", handle: value }
|
|
198
211
|
}
|
|
199
212
|
|
|
200
213
|
/** Lowers one interval literal at its element type. */
|
|
@@ -288,10 +301,9 @@ function interval(element: U64Field | I64Field, width?: bigint): IntervalField<"
|
|
|
288
301
|
* Lowers one host literal at its field position to the wire
|
|
289
302
|
* {@link LiteralSpec} — the selection-literal machine ground axioms and
|
|
290
303
|
* `where()` bindings both ride (one machine, same errors — the macro's own
|
|
291
|
-
* rule). A value on a closed-reference field
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
* the field's structural kind.
|
|
304
|
+
* rule). A value on a closed-reference field IS its handle NAME (verified
|
|
305
|
+
* against the roster: an unknown name is a construction error); everything
|
|
306
|
+
* else lowers to a plain value tagged by the field's structural kind.
|
|
295
307
|
*/
|
|
296
308
|
function literalOf(field: AnyField, value: unknown): LiteralSpec {
|
|
297
309
|
if ("closed" in field) {
|
package/src/index.ts
CHANGED
|
@@ -75,12 +75,11 @@ export type {
|
|
|
75
75
|
FaceShapeMismatch,
|
|
76
76
|
FaceShapes,
|
|
77
77
|
FaceSource,
|
|
78
|
-
OneOf,
|
|
79
78
|
OwnerOf,
|
|
80
79
|
SameArity,
|
|
81
80
|
SameShapes
|
|
82
81
|
} from "#face.ts"
|
|
83
|
-
export { on
|
|
82
|
+
export { on } from "#face.ts"
|
|
84
83
|
export type {
|
|
85
84
|
AnyField,
|
|
86
85
|
BoolField,
|
package/src/marshal.ts
CHANGED
|
@@ -13,10 +13,22 @@
|
|
|
13
13
|
* the engine admitted IS a legal fact of its relation (the same trust
|
|
14
14
|
* direction as Rust's typed readback). Shape mismatches here are genuine
|
|
15
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`.
|
|
16
28
|
*/
|
|
17
29
|
|
|
18
30
|
import * as errors from "@superbuilders/errors"
|
|
19
|
-
import type { AnyField } from "#fields.ts"
|
|
31
|
+
import type { AnyField, ClosedRoster } from "#fields.ts"
|
|
20
32
|
import type { FactValue } from "#native.ts"
|
|
21
33
|
import type { AnyRelation, Fact, FreshKeys, RelationData } from "#relation.ts"
|
|
22
34
|
|
|
@@ -80,14 +92,64 @@ function recordOf(fact: object): Record<string, unknown> {
|
|
|
80
92
|
return Object.fromEntries(Object.entries(fact))
|
|
81
93
|
}
|
|
82
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The write half of the closed bijection: one handle NAME to its u64 row
|
|
97
|
+
* id (declaration order = row ids — the engine's own minting of the
|
|
98
|
+
* sealed extension). An unknown name is a pointed refusal naming the
|
|
99
|
+
* vocabulary and its roster — the 0.4.0 upgrade over any-bigint-compiles:
|
|
100
|
+
* the wrong spelling dies at the marshal, never as a commit-time
|
|
101
|
+
* violation. `indexOf` is the whole machine (the roster is ≤ 256 rows,
|
|
102
|
+
* engine law — no map is warranted).
|
|
103
|
+
*/
|
|
104
|
+
function closedCellOf(context: string, closed: ClosedRoster, name: string): FactValue {
|
|
105
|
+
const id = closed.handles.indexOf(name)
|
|
106
|
+
if (id === -1) {
|
|
107
|
+
throw errors.new(
|
|
108
|
+
`${context}: "${name}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
return BigInt(id)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The read half of the closed bijection: one u64 row id back to its handle
|
|
116
|
+
* NAME (`Number(cell)` is safe — the sealed extension holds at most 256
|
|
117
|
+
* rows, engine law). An id outside the roster THROWS pointed, never a
|
|
118
|
+
* silent fallback and never `undefined`: the state is reachable only in a
|
|
119
|
+
* store whose closed-typed column was never pinned by its containment law,
|
|
120
|
+
* and the error names that missing piece.
|
|
121
|
+
*/
|
|
122
|
+
function handleOf(context: string, closed: ClosedRoster, cell: FactValue): string {
|
|
123
|
+
if (typeof cell !== "bigint") {
|
|
124
|
+
throw cellShapeError(context, `a ${closed.name} handle id (bigint)`, cell)
|
|
125
|
+
}
|
|
126
|
+
const handle = closed.handles[Number(cell)]
|
|
127
|
+
if (handle === undefined) {
|
|
128
|
+
throw errors.new(
|
|
129
|
+
`${context}: id ${cell} is outside the ${closed.name} roster (${closed.handles.join(", ")}) — the column types ${closed.name} but no law pins it — a containment statement is the missing piece`
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
return handle
|
|
133
|
+
}
|
|
134
|
+
|
|
83
135
|
/**
|
|
84
136
|
* Marshals one host cell at its field position to the natural wire value,
|
|
85
137
|
* schema-directed by the field descriptor's structural kind (never
|
|
86
|
-
* guessed).
|
|
87
|
-
*
|
|
88
|
-
*
|
|
138
|
+
* guessed). A closed-referencing cell arrives as its handle NAME and
|
|
139
|
+
* lowers through {@link closedCellOf} — the arm precedes the switch
|
|
140
|
+
* because a closed reference is structurally a u64 descriptor plus the
|
|
141
|
+
* roster (the same precedence `Infer` pins at the type level). Everything
|
|
142
|
+
* else is bare, so the runtime values ARE the wire's natural JS values;
|
|
143
|
+
* widths and domain labels are the engine's own judgment at the write
|
|
144
|
+
* boundary.
|
|
89
145
|
*/
|
|
90
146
|
function cellOf(context: string, field: AnyField, value: unknown): FactValue {
|
|
147
|
+
if ("closed" in field) {
|
|
148
|
+
if (typeof value !== "string") {
|
|
149
|
+
throw cellShapeError(context, `a ${field.closed.name} handle name (string)`, value)
|
|
150
|
+
}
|
|
151
|
+
return closedCellOf(context, field.closed, value)
|
|
152
|
+
}
|
|
91
153
|
switch (field.kind) {
|
|
92
154
|
case "bool": {
|
|
93
155
|
if (typeof value !== "boolean") {
|
|
@@ -210,7 +272,9 @@ function isMintedFresh<R extends AnyRelation>(
|
|
|
210
272
|
/**
|
|
211
273
|
* Unmarshals one positional row to the relation's named, frozen fact object
|
|
212
274
|
* of bare structural values — the inverse of {@link rowOf},
|
|
213
|
-
* ordinal-directed by the same declaration order.
|
|
275
|
+
* ordinal-directed by the same declaration order. Closed-referencing cells
|
|
276
|
+
* lift id → handle NAME through {@link handleOf} (the read half of the
|
|
277
|
+
* bijection), so every fact a user sees speaks the roster's vocabulary.
|
|
214
278
|
*/
|
|
215
279
|
function factOf<R extends AnyRelation>(relation: R, row: readonly FactValue[]): Fact<R> {
|
|
216
280
|
const data = relation.data
|
|
@@ -225,7 +289,10 @@ function factOf<R extends AnyRelation>(relation: R, row: readonly FactValue[]):
|
|
|
225
289
|
if (cell === undefined) {
|
|
226
290
|
throw errors.new(`relation ${data.name}: row cell ${ordinal} (${declared.name}) is absent`)
|
|
227
291
|
}
|
|
228
|
-
decoded[declared.name] =
|
|
292
|
+
decoded[declared.name] =
|
|
293
|
+
"closed" in declared.field
|
|
294
|
+
? handleOf(`relation ${data.name} field ${declared.name}`, declared.field.closed, cell)
|
|
295
|
+
: cell
|
|
229
296
|
})
|
|
230
297
|
Object.freeze(decoded)
|
|
231
298
|
if (!isCompleteFact(relation, decoded)) {
|
|
@@ -235,4 +302,4 @@ function factOf<R extends AnyRelation>(relation: R, row: readonly FactValue[]):
|
|
|
235
302
|
}
|
|
236
303
|
|
|
237
304
|
export type { KeyFact, Minted }
|
|
238
|
-
export { cellOf, factOf, isFreshField, isMintedFresh, keyRowOf, recordOf, rowOf }
|
|
305
|
+
export { cellOf, factOf, handleOf, isFreshField, isMintedFresh, keyRowOf, recordOf, rowOf }
|
package/src/query/atom.ts
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* (`bumbledb/crates/bumbledb/src/ir.rs`, the bijection target;
|
|
5
5
|
* `docs/architecture/20-query-ir.md` normative). A `match` binding record
|
|
6
6
|
* binds fields to vars, params, ∈-set params, or bare structural literals
|
|
7
|
+
* — a closed-reference field's literal is its handle NAME, and a plain
|
|
8
|
+
* ARRAY of names there is membership, folded into the program (closed-only
|
|
9
|
+
* by owner ruling; see {@link BindingInput})
|
|
7
10
|
* (unmentioned fields ARE the wildcard — no wildcard value exists);
|
|
8
11
|
* `not(Rel, {...})` is negation-as-position (anti-join); `eq`/`ne` and the
|
|
9
12
|
* order roster, `pointIn`/`covers` (both spellings of `ir::CmpOp::PointIn`,
|
|
@@ -26,7 +29,7 @@
|
|
|
26
29
|
|
|
27
30
|
import * as errors from "@superbuilders/errors"
|
|
28
31
|
import type { AnyClosed } from "#closed.ts"
|
|
29
|
-
import type { AnyField, ClosedIdField, Infer, IntervalValue } from "#fields.ts"
|
|
32
|
+
import type { AnyField, ClosedIdField, ClosedRoster, Infer, IntervalValue } from "#fields.ts"
|
|
30
33
|
import type { ClassLookup, ClassRecordOf, SchemaClasses } from "#law.ts"
|
|
31
34
|
import type {
|
|
32
35
|
ClassedField,
|
|
@@ -55,23 +58,32 @@ type MatchOwner = AnyRelation | AnyClosed
|
|
|
55
58
|
/**
|
|
56
59
|
* The matchable field block of an atom owner: a relation's declared
|
|
57
60
|
* fields; a closed relation's SEALED shape — the synthetic `id` (the
|
|
58
|
-
* roster-carrying descriptor
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* `
|
|
62
|
-
*
|
|
61
|
+
* value's OWN roster-carrying descriptor, at its precise type: the handle
|
|
62
|
+
* union rides into ψ id bindings and joins exactly as it does on a
|
|
63
|
+
* referencing column) first, then the declared payload columns read
|
|
64
|
+
* through the typed `columns` carrier (the one source of payload typing —
|
|
65
|
+
* no parallel column table exists). The runtime twin is `matchFieldsOf` in
|
|
66
|
+
* `#query/lower.ts`; the id-first ordinal shift the two tiers share is
|
|
67
|
+
* pinned by the lowering golden.
|
|
63
68
|
*/
|
|
64
69
|
type MatchFields<R extends MatchOwner> = R extends AnyClosed
|
|
65
|
-
? { readonly id:
|
|
70
|
+
? { readonly id: R["id"] } & R["columns"]
|
|
66
71
|
: R extends AnyRelation
|
|
67
72
|
? RelationFields<R>
|
|
68
73
|
: never
|
|
69
74
|
|
|
70
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* One atom-binding position as runtime data. `literalSet` is a membership
|
|
77
|
+
* ARRAY at a closed-reference field, folded into the program: `name` is
|
|
78
|
+
* the content-addressed registry key its dense `ParamId` is minted under
|
|
79
|
+
* (the lowering rides the existing param-set term; the SDK itself supplies
|
|
80
|
+
* the translated member set at every execute — never the host).
|
|
81
|
+
*/
|
|
71
82
|
type BindingTermData =
|
|
72
83
|
| { readonly kind: "var"; readonly name: string }
|
|
73
84
|
| { readonly kind: "param"; readonly name: string }
|
|
74
85
|
| { readonly kind: "setParam"; readonly name: string }
|
|
86
|
+
| { readonly kind: "literalSet"; readonly name: string; readonly members: readonly string[] }
|
|
75
87
|
| { readonly kind: "literal"; readonly value: unknown }
|
|
76
88
|
|
|
77
89
|
/** One resolved binding: the field's name, its descriptor, its law-computed class, and the term. */
|
|
@@ -135,10 +147,18 @@ type SelectEntryData =
|
|
|
135
147
|
| { readonly kind: "measure"; readonly over: string }
|
|
136
148
|
| { readonly kind: "aggregate"; readonly agg: AggData }
|
|
137
149
|
|
|
138
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* One answer column: its name (the row object key), its entry, and — when
|
|
152
|
+
* the column's value is a closed reference (a projected var or an
|
|
153
|
+
* Arg-carried payload bound at a closed-referencing field) — the roster the
|
|
154
|
+
* decode lifts row ids back to handle NAMES through (the read half of the
|
|
155
|
+
* marshal bijection; `undefined` on every bare column). The slice is
|
|
156
|
+
* SDK-side marshaling data only: the wire `ProgramIr` never carries it.
|
|
157
|
+
*/
|
|
139
158
|
interface SelectColumn {
|
|
140
159
|
readonly name: string
|
|
141
160
|
readonly entry: SelectEntryData
|
|
161
|
+
readonly closed: ClosedRoster | undefined
|
|
142
162
|
}
|
|
143
163
|
|
|
144
164
|
/** One body item of a rule, in written order. */
|
|
@@ -151,13 +171,17 @@ type RuleItem =
|
|
|
151
171
|
/**
|
|
152
172
|
* One use of a parameter inside a rule, in written order: the census the
|
|
153
173
|
* query-level registry folds (first use mints the dense `ParamId`, first
|
|
154
|
-
* FIELD-ANCHORED use types the wire).
|
|
174
|
+
* FIELD-ANCHORED use types the wire). `members` is present exactly on a
|
|
175
|
+
* membership-array use (a literal set folded into the program): the handle
|
|
176
|
+
* names the SDK itself translates and supplies at execute — the entry
|
|
177
|
+
* never appears in the host's params object.
|
|
155
178
|
*/
|
|
156
179
|
interface ParamUse {
|
|
157
180
|
readonly name: string
|
|
158
181
|
readonly shape: "value" | "set" | "mask"
|
|
159
182
|
readonly anchor: AnyField | "measure" | undefined
|
|
160
183
|
readonly op: "binding" | CmpKind
|
|
184
|
+
readonly members: readonly string[] | undefined
|
|
161
185
|
}
|
|
162
186
|
|
|
163
187
|
/** One complete rule as runtime data. */
|
|
@@ -184,10 +208,18 @@ interface RecData {
|
|
|
184
208
|
* of the field's value type, a var/param/∈-set-param term — and, when the
|
|
185
209
|
* field is interval-typed, a bare point literal (the IR's membership
|
|
186
210
|
* typing rule: an element-typed term at an interval field is point
|
|
187
|
-
* membership; an interval-typed term is value equality).
|
|
211
|
+
* membership; an interval-typed term is value equality). A
|
|
212
|
+
* CLOSED-reference field additionally takes a plain ARRAY of handle names
|
|
213
|
+
* read as membership — `kind: ["Practice", "Review"]` (the drizzle law:
|
|
214
|
+
* set membership is an array, never an operator). Arrays are CLOSED-ONLY
|
|
215
|
+
* in this packet by owner ruling: ordinary u64/str membership already has
|
|
216
|
+
* its spelling through `r.inSet` params; widening literal arrays to every
|
|
217
|
+
* literal-capable kind is a separate future taste call — deliberately not
|
|
218
|
+
* done here.
|
|
188
219
|
*/
|
|
189
220
|
type BindingInput<F extends AnyField> =
|
|
190
221
|
| Infer<F>
|
|
222
|
+
| (F extends ClosedIdField ? readonly Infer<F>[] : never)
|
|
191
223
|
| (F extends { readonly kind: "interval" } ? bigint : never)
|
|
192
224
|
| Var<string>
|
|
193
225
|
| Param<string>
|
|
@@ -516,11 +548,22 @@ function not<R extends MatchOwner, const B extends MatchShape<MatchFields<R>>>(
|
|
|
516
548
|
return Object.freeze(value)
|
|
517
549
|
}
|
|
518
550
|
|
|
519
|
-
/**
|
|
551
|
+
/**
|
|
552
|
+
* Whether a var name is bound in the environment at an orderable (u64/i64)
|
|
553
|
+
* field. A CLOSED reference is excluded even though its kind is `u64`: a
|
|
554
|
+
* vocabulary's declaration-id order is an accident, not semantics
|
|
555
|
+
* (`docs/architecture/10-data-model.md` § orderability — order on it is
|
|
556
|
+
* refused exactly as the enum's ordinal order was), so every
|
|
557
|
+
* order-comparison and fold position refuses closed-bound terms — this
|
|
558
|
+
* judgment is the one gate they all read, and the construction-time
|
|
559
|
+
* validations in `#query/lower.ts` are its runtime twin.
|
|
560
|
+
*/
|
|
520
561
|
type OrderVarOk<Env extends EnvShape, N extends string> = N extends keyof Env
|
|
521
|
-
? Env[N]["field"]
|
|
522
|
-
?
|
|
523
|
-
:
|
|
562
|
+
? Env[N]["field"] extends { readonly closed: ClosedRoster }
|
|
563
|
+
? false
|
|
564
|
+
: Env[N]["field"]["kind"] extends "u64" | "i64"
|
|
565
|
+
? true
|
|
566
|
+
: false
|
|
524
567
|
: false
|
|
525
568
|
|
|
526
569
|
/** Whether a var name is bound at an interval field. */
|