@bjornpagen/bumbledb 0.15.0 → 0.17.1
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 +33 -49
- package/README.md +3 -3
- package/dist/capacity.d.ts +24 -136
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +18 -40
- package/dist/capacity.js.map +1 -1
- package/dist/closed.d.ts +0 -156
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +0 -104
- package/dist/closed.js.map +1 -1
- package/dist/db.d.ts +7 -223
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +147 -396
- package/dist/db.js.map +1 -1
- package/dist/face.d.ts +0 -133
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +0 -33
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +1 -145
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +2 -91
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +12 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -13
- package/dist/index.js.map +1 -1
- package/dist/law.d.ts +111 -93
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +23 -27
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts +9 -35
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +8 -53
- package/dist/lower.js.map +1 -1
- package/dist/marshal.d.ts +0 -65
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js +0 -72
- package/dist/marshal.js.map +1 -1
- package/dist/native.d.ts +31 -289
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +15 -64
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +10 -276
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +1 -96
- package/dist/query/atom.js.map +1 -1
- package/dist/query/find.d.ts +10 -76
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +0 -30
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +58 -146
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +19 -256
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +0 -7
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +1 -13
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts +0 -36
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +0 -44
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +24 -180
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +2 -66
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +2 -50
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +2 -37
- package/dist/relation.js.map +1 -1
- package/dist/schema.d.ts +13 -63
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +118 -92
- package/dist/schema.js.map +1 -1
- package/dist/spec.d.ts +1 -140
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js +1 -68
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +6 -137
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +16 -119
- package/dist/statements.js.map +1 -1
- package/package.json +2 -2
- package/src/capacity.ts +26 -140
- package/src/closed.ts +5 -206
- package/src/db.ts +203 -692
- package/src/face.ts +0 -142
- package/src/fields.ts +4 -172
- package/src/index.ts +10 -15
- package/src/law.ts +201 -129
- package/src/lower.ts +8 -53
- package/src/marshal.ts +1 -85
- package/src/native.ts +58 -321
- package/src/query/atom.ts +26 -313
- package/src/query/find.ts +24 -110
- package/src/query/lower.ts +126 -377
- package/src/query/parse-ir.ts +1 -14
- package/src/query/run.ts +0 -45
- package/src/query/scope.ts +25 -186
- package/src/relation.ts +2 -66
- package/src/schema.ts +143 -122
- package/src/spec.ts +1 -160
- package/src/statements.ts +22 -174
package/src/law.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The law-typing engine (owner ruling 2026-07-18, "option 2, zero debate"):
|
|
3
|
-
* THE LAWS TYPE THE COLUMNS. Domains are declared nowhere — `schema
|
|
3
|
+
* THE LAWS TYPE THE COLUMNS. Domains are declared nowhere — `schema`
|
|
4
4
|
* computes every field's domain FROM the statement list, at BOTH the type
|
|
5
5
|
* level (this module's type machinery, reading the statements tuple type)
|
|
6
6
|
* and at runtime (a plain union-find over the same pairs), and the two
|
|
@@ -8,37 +8,41 @@
|
|
|
8
8
|
*
|
|
9
9
|
* The three class laws (ratified; implemented exactly):
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
11
|
+
* 1. GENERATORS — a `fresh` field is a generator and names its class by
|
|
12
|
+
* its declaration coordinate (`"Account.id"`); a closed relation's
|
|
13
|
+
* synthetic id is a generator named `"Kind.id"`.
|
|
14
|
+
* 2. GENERATOR-LESS classes are named by their least member coordinate
|
|
15
|
+
* in relation-declaration × field-declaration order (readable off the
|
|
16
|
+
* relation record and each member's frozen field list at the VALUE
|
|
17
|
+
* tier — deterministic, pinned forever; the wire reads only this
|
|
18
|
+
* tier). At the TYPE tier the same class is carried as its
|
|
19
|
+
* member-coordinate SET (see {@link ClassOfCoord}): TypeScript's
|
|
20
|
+
* union member order is not observably deterministic, so a type-level
|
|
21
|
+
* least-member pick would drift between compilations — the set is the
|
|
22
|
+
* canonical deterministic spelling, the runtime name is always a
|
|
23
|
+
* member of it, and the join judgment is identical at both tiers.
|
|
24
|
+
* 3. BARE — a field in no law has NO class and pairs only with bare in
|
|
25
|
+
* queries (the deliberate sum-domain pointer stays legal).
|
|
26
26
|
*
|
|
27
27
|
* THE WALL: a class containing more than one generator is a contradiction
|
|
28
28
|
* (two mints cannot share a carrier) — a schema-level COMPILE error (the
|
|
29
29
|
* named, self-locating {@link ClassWall}: which generator coordinates
|
|
30
30
|
* collided, through which paired slots) with a construction-time runtime
|
|
31
31
|
* twin (`computeClasses` throws with the same content, naming the exact
|
|
32
|
-
* statement).
|
|
32
|
+
* statement). The TARGET-KEY WALL ({@link TargetKeyWall},
|
|
33
|
+
* 60-containment-parity) rides the same constraint seam: a containment/
|
|
34
|
+
* mirrors/capacity target projection that resolves no key of its relation
|
|
35
|
+
* is the same kind of schema-level compile error, with `schema`'s
|
|
36
|
+
* `verifyTargetKeys` as its authoritative runtime twin.
|
|
33
37
|
*
|
|
34
38
|
* Every paired face of the statement tuple unions its positionwise field
|
|
35
39
|
* slots: containment (ψ-selected targets included — a selection changes
|
|
36
40
|
* pairing not at all), the `==` bijection, and capacity source/target pairs.
|
|
37
|
-
* `key
|
|
41
|
+
* `key` statements pair nothing (an FD constrains one relation's own
|
|
38
42
|
* rows; it identifies no carriers).
|
|
39
43
|
*
|
|
40
44
|
* The type tier reads pairs off the statement types' exact face data, so
|
|
41
|
-
* spell the statement list INLINE in `schema
|
|
45
|
+
* spell the statement list INLINE in `schema` (the `const` type
|
|
42
46
|
* parameter keeps the tuple precise). A widened `Statement[]` list
|
|
43
47
|
* degrades the TYPE tier to generators-only (no pair is readable off a
|
|
44
48
|
* widened type) — the runtime map stays complete and authoritative, and
|
|
@@ -58,75 +62,42 @@ import type { AnyRelation, RelationFields } from "#relation.ts"
|
|
|
58
62
|
import type { SchemaRelation, SchemaRelations } from "#schema.ts"
|
|
59
63
|
import { renderStatement, type Statement } from "#statements.ts"
|
|
60
64
|
|
|
61
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
62
|
-
// The class-map shapes.
|
|
63
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
64
|
-
|
|
65
|
-
/** One relation's computed classes: field name → class name, `undefined` = bare. */
|
|
66
65
|
type RelationClasses = { readonly [field: string]: string | undefined }
|
|
67
66
|
|
|
68
|
-
/**
|
|
69
|
-
* The class map a schema carries — relation name → field name → the
|
|
70
|
-
* computed class name (`undefined` = bare). THE domain authority: queries
|
|
71
|
-
* and the wire lowering read domains from here and nowhere else. The wide
|
|
72
|
-
* shape is the default every `Schema`-generic surface accepts; a concrete
|
|
73
|
-
* schema's `classes` property carries the EXACT computed map
|
|
74
|
-
* ({@link ClassesOf}) at the type level and the frozen runtime twin
|
|
75
|
-
* (`computeClasses`) at the value level — one property, two tiers, one
|
|
76
|
-
* computation.
|
|
77
|
-
*/
|
|
78
67
|
type SchemaClasses = { readonly [relation: string]: RelationClasses }
|
|
79
68
|
|
|
80
|
-
/** Looks one relation's class record up in a schema's class map (absent relation = no classes). */
|
|
81
69
|
type ClassRecordOf<Classes extends SchemaClasses, N extends string> = N extends keyof Classes
|
|
82
70
|
? Classes[N]
|
|
83
71
|
: Record<never, never>
|
|
84
72
|
|
|
85
|
-
/** Looks one field's class up in a relation's class record (absent field = bare). */
|
|
86
73
|
type ClassLookup<CR, K> = K extends keyof CR ? CR[K] & (string | undefined) : undefined
|
|
87
74
|
|
|
88
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
89
|
-
// Coordinates.
|
|
90
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
91
|
-
|
|
92
|
-
/** A closed member's declared payload-column record (`never` on ordinary relations). */
|
|
93
75
|
type MemberColumns<M> = M extends AnyClosed ? M["columns"] : never
|
|
94
76
|
|
|
95
|
-
/** The field names of one schema member: a relation's declared fields; a closed relation's sealed `id` + columns. */
|
|
96
77
|
type MemberFieldNames<M extends SchemaRelation> = M extends AnyClosed
|
|
97
78
|
? "id" | (keyof MemberColumns<M> & string)
|
|
98
79
|
: M extends AnyRelation
|
|
99
80
|
? keyof RelationFields<M> & string
|
|
100
81
|
: never
|
|
101
82
|
|
|
102
|
-
/** The fresh-marked field names of one field block. */
|
|
103
83
|
type FreshFieldNames<Fields> = {
|
|
104
84
|
[F in keyof Fields & string]: Fields[F] extends { readonly fresh: true } ? F : never
|
|
105
85
|
}[keyof Fields & string]
|
|
106
86
|
|
|
107
|
-
/** One member's generator coordinates: fresh fields; a closed relation's synthetic id. */
|
|
108
87
|
type MemberGenerators<N extends string, M extends SchemaRelation> = M extends AnyClosed
|
|
109
88
|
? `${N}.id`
|
|
110
89
|
: M extends AnyRelation
|
|
111
90
|
? `${N}.${FreshFieldNames<RelationFields<M>>}`
|
|
112
91
|
: never
|
|
113
92
|
|
|
114
|
-
/** Every generator coordinate of a relation record (a union). */
|
|
115
93
|
type GeneratorsOf<Rels extends SchemaRelations> = {
|
|
116
94
|
[N in keyof Rels & string]: MemberGenerators<N, Rels[N]>
|
|
117
95
|
}[keyof Rels & string]
|
|
118
96
|
|
|
119
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
120
|
-
// Pairs: the positionwise slot pairs of every paired face.
|
|
121
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
122
|
-
|
|
123
|
-
/** One paired-slot pair of coordinates. */
|
|
124
97
|
type Pair = readonly [string, string]
|
|
125
98
|
|
|
126
|
-
/** A list of slot pairs. */
|
|
127
99
|
type PairList = readonly Pair[]
|
|
128
100
|
|
|
129
|
-
/** Zips two faces' projections into coordinate pairs, positionwise. */
|
|
130
101
|
type ZipCoords<
|
|
131
102
|
SN extends string,
|
|
132
103
|
SP extends readonly string[],
|
|
@@ -139,13 +110,6 @@ type ZipCoords<
|
|
|
139
110
|
: Acc
|
|
140
111
|
: Acc
|
|
141
112
|
|
|
142
|
-
/**
|
|
143
|
-
* One statement's slot pairs: containments (bidirectional included — pair
|
|
144
|
-
* unions are symmetric) and capacity statements pair their two faces
|
|
145
|
-
* positionwise;
|
|
146
|
-
* `key()` pairs nothing. A widened face (owner name or projection no
|
|
147
|
-
* longer literal) contributes nothing — the runtime map stays complete.
|
|
148
|
-
*/
|
|
149
113
|
type StatementPairs<St extends Statement> = St["data"] extends {
|
|
150
114
|
readonly source: infer S extends FaceData
|
|
151
115
|
readonly target: infer T extends FaceData
|
|
@@ -157,7 +121,6 @@ type StatementPairs<St extends Statement> = St["data"] extends {
|
|
|
157
121
|
: ZipCoords<S["owner"]["name"], S["projection"], T["owner"]["name"], T["projection"]>
|
|
158
122
|
: []
|
|
159
123
|
|
|
160
|
-
/** Every slot pair of a statements tuple, in written order. */
|
|
161
124
|
type PairsOf<Stmts extends readonly Statement[], Acc extends PairList = []> = Stmts extends readonly [
|
|
162
125
|
infer H extends Statement,
|
|
163
126
|
...infer T extends readonly Statement[]
|
|
@@ -165,11 +128,6 @@ type PairsOf<Stmts extends readonly Statement[], Acc extends PairList = []> = St
|
|
|
165
128
|
? PairsOf<T, readonly [...Acc, ...StatementPairs<H>]>
|
|
166
129
|
: Acc
|
|
167
130
|
|
|
168
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
169
|
-
// Union-find over the pairs: connected components as coordinate unions.
|
|
170
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
171
|
-
|
|
172
|
-
/** The component (a union of coordinates) containing `X`, or `never` when `X` is in none. */
|
|
173
131
|
type CompOf<Comps extends readonly string[], X extends string> = Comps extends readonly [
|
|
174
132
|
infer H extends string,
|
|
175
133
|
...infer T extends readonly string[]
|
|
@@ -179,7 +137,6 @@ type CompOf<Comps extends readonly string[], X extends string> = Comps extends r
|
|
|
179
137
|
: CompOf<T, X>
|
|
180
138
|
: never
|
|
181
139
|
|
|
182
|
-
/** Rebuilds the component list without the component `C` (components are disjoint; identity is mutual extension). */
|
|
183
140
|
type WithoutComp<
|
|
184
141
|
Comps extends readonly string[],
|
|
185
142
|
C extends string,
|
|
@@ -190,7 +147,6 @@ type WithoutComp<
|
|
|
190
147
|
: WithoutComp<T, C, readonly [...Acc, H]>
|
|
191
148
|
: Acc
|
|
192
149
|
|
|
193
|
-
/** Unions one pair into the component list: create, extend, keep, or merge. */
|
|
194
150
|
type AddPair<Comps extends readonly string[], A extends string, B extends string> = [
|
|
195
151
|
CompOf<Comps, A>,
|
|
196
152
|
CompOf<Comps, B>
|
|
@@ -206,7 +162,6 @@ type AddPair<Comps extends readonly string[], A extends string, B extends string
|
|
|
206
162
|
: readonly [...WithoutComp<WithoutComp<Comps, CA>, CB>, CA | CB]
|
|
207
163
|
: Comps
|
|
208
164
|
|
|
209
|
-
/** Folds every pair into connected components (tail-recursive — the whole walk is one loop). */
|
|
210
165
|
type BuildComps<Pairs extends PairList, Comps extends readonly string[] = readonly []> = Pairs extends readonly [
|
|
211
166
|
infer P extends Pair,
|
|
212
167
|
...infer T extends PairList
|
|
@@ -214,21 +169,8 @@ type BuildComps<Pairs extends PairList, Comps extends readonly string[] = readon
|
|
|
214
169
|
? BuildComps<T, AddPair<Comps, P[0], P[1]>>
|
|
215
170
|
: Comps
|
|
216
171
|
|
|
217
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
218
|
-
// The wall and the names.
|
|
219
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
220
|
-
|
|
221
|
-
/** Whether a union holds two or more members (`All` captures the whole union across distribution). */
|
|
222
172
|
type IsMulti<U, All = U> = [U] extends [never] ? false : U extends unknown ? ([All] extends [U] ? false : true) : never
|
|
223
173
|
|
|
224
|
-
/**
|
|
225
|
-
* The named, self-locating compile verdict of the one-generator wall: the
|
|
226
|
-
* generator coordinates that collided and the paired slots (rendered
|
|
227
|
-
* `A.x ~ B.y`, statement order) whose chain unified them. Intersected into
|
|
228
|
-
* `schema()`'s statements parameter, so the error lands ON the statement
|
|
229
|
-
* list with this key naming the law. The runtime twin throws from
|
|
230
|
-
* `computeClasses` with the same content, naming the exact statement.
|
|
231
|
-
*/
|
|
232
174
|
interface ClassWall<Generators extends string, Chain extends readonly string[]> {
|
|
233
175
|
readonly "schema class wall — the statements unify two generators into one class (two mints cannot share a carrier)": {
|
|
234
176
|
readonly generators: Generators
|
|
@@ -236,7 +178,6 @@ interface ClassWall<Generators extends string, Chain extends readonly string[]>
|
|
|
236
178
|
}
|
|
237
179
|
}
|
|
238
180
|
|
|
239
|
-
/** The paired slots lying inside component `C`, rendered — the wall's self-locating chain. */
|
|
240
181
|
type ChainOf<Pairs extends PairList, C extends string, Acc extends readonly string[] = []> = Pairs extends readonly [
|
|
241
182
|
infer P extends Pair,
|
|
242
183
|
...infer T extends PairList
|
|
@@ -246,7 +187,6 @@ type ChainOf<Pairs extends PairList, C extends string, Acc extends readonly stri
|
|
|
246
187
|
: ChainOf<T, C, Acc>
|
|
247
188
|
: Acc
|
|
248
189
|
|
|
249
|
-
/** Scans the components for a two-generator class: `unknown` (lawful) or the {@link ClassWall}. */
|
|
250
190
|
type WallScan<Comps extends readonly string[], Gens extends string, Pairs extends PairList> = Comps extends readonly [
|
|
251
191
|
infer H extends string,
|
|
252
192
|
...infer T extends readonly string[]
|
|
@@ -256,35 +196,181 @@ type WallScan<Comps extends readonly string[], Gens extends string, Pairs extend
|
|
|
256
196
|
: WallScan<T, Gens, Pairs>
|
|
257
197
|
: unknown
|
|
258
198
|
|
|
199
|
+
type SetEq<A extends string, B extends string> = [A] extends [B] ? ([B] extends [A] ? true : false) : false
|
|
200
|
+
|
|
201
|
+
type KeyEntry = readonly [string, string]
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Every declared `key` of a statements tuple as {@link KeyEntry} rows —
|
|
205
|
+
* the declared half of the target-key roster (the implied half is read off
|
|
206
|
+
* each target face's own relation value: fresh marks, closed ids). A
|
|
207
|
+
* statement-tuple union or any undecidable key element degrades the WHOLE
|
|
208
|
+
* wall to silent before this roster is ever consulted
|
|
209
|
+
* ({@link DecidableRoster}) — the skip arm here is the same judgment
|
|
210
|
+
* stated locally, kept as belt, as is {@link DeclaredKeyMatch}'s
|
|
211
|
+
* unjudgeable-projection arm.
|
|
212
|
+
*/
|
|
213
|
+
type DeclaredKeysOf<Stmts extends readonly Statement[], Acc extends readonly KeyEntry[] = []> = Stmts extends readonly [
|
|
214
|
+
infer H extends Statement,
|
|
215
|
+
...infer T extends readonly Statement[]
|
|
216
|
+
]
|
|
217
|
+
? H["data"] extends {
|
|
218
|
+
readonly kind: "key"
|
|
219
|
+
readonly owner: infer O extends AnyRelation
|
|
220
|
+
readonly projection: infer P extends readonly string[]
|
|
221
|
+
}
|
|
222
|
+
? string extends O["name"]
|
|
223
|
+
? DeclaredKeysOf<T, Acc>
|
|
224
|
+
: DeclaredKeysOf<T, readonly [...Acc, readonly [O["name"], P[number]]]>
|
|
225
|
+
: DeclaredKeysOf<T, Acc>
|
|
226
|
+
: Acc
|
|
227
|
+
|
|
228
|
+
type LiteralProjection<P extends readonly string[]> = [true] extends [IsMulti<P>]
|
|
229
|
+
? false
|
|
230
|
+
: [number] extends [P["length"]]
|
|
231
|
+
? false
|
|
232
|
+
: [P] extends [readonly [infer H extends string, ...infer T extends readonly string[]]]
|
|
233
|
+
? [string] extends [H]
|
|
234
|
+
? false
|
|
235
|
+
: [true] extends [IsMulti<H>]
|
|
236
|
+
? false
|
|
237
|
+
: LiteralProjection<T>
|
|
238
|
+
: true
|
|
239
|
+
|
|
240
|
+
type DecidableKeyData<D> = [D] extends [
|
|
241
|
+
{
|
|
242
|
+
readonly kind: "key"
|
|
243
|
+
readonly owner: infer O extends AnyRelation
|
|
244
|
+
readonly projection: infer P extends readonly string[]
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
? [true] extends [IsMulti<D>]
|
|
248
|
+
? false
|
|
249
|
+
: [string] extends [O["name"]]
|
|
250
|
+
? false
|
|
251
|
+
: [true] extends [IsMulti<O["name"]>]
|
|
252
|
+
? false
|
|
253
|
+
: LiteralProjection<P>
|
|
254
|
+
: false
|
|
255
|
+
|
|
259
256
|
/**
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
257
|
+
* THE total decidability detector of the type-tier target-key judgment —
|
|
258
|
+
* a strict WHITELIST of provably-judgeable shapes, never a blocklist of
|
|
259
|
+
* known-bad spellings (representation over control flow: the detector is
|
|
260
|
+
* total by construction, so a spelling nobody has met yet degrades
|
|
261
|
+
* instead of firing). It answers `true` ONLY when ALL hold, each check
|
|
262
|
+
* tuple-bracketed against distribution:
|
|
263
|
+
*
|
|
264
|
+
* - SINGULAR — `Stmts` itself is a single non-union type (a ternary
|
|
265
|
+
* between two individually-lawful `as const` statement lists infers a
|
|
266
|
+
* UNION of tuples; a naked `Stmts` would distribute the scan
|
|
267
|
+
* ({@link TargetKeyScan}) and the roster ({@link DeclaredKeysOf})
|
|
268
|
+
* INDEPENDENTLY, cross-judging one arm's faces against the other
|
|
269
|
+
* arm's keys). The union test is the house device ({@link IsMulti}).
|
|
270
|
+
* - FIXED-LENGTH — `Stmts` is a literal tuple with no rest tail:
|
|
271
|
+
* `[number] extends [Stmts["length"]]` detects a rest/array length. A
|
|
272
|
+
* rest-tail tuple (`readonly [typeof stmt,...Statement[]]`) peels its
|
|
273
|
+
* literal head and hides EVERY key in the tail from a head-peeling
|
|
274
|
+
* roster read — the scan would judge the head against a roster blind
|
|
275
|
+
* to keys the value tier can see.
|
|
276
|
+
* - CONCRETE ELEMENTS — every element is a single non-union statement
|
|
277
|
+
* (per-element {@link IsMulti}), and every element whose
|
|
278
|
+
* `data["kind"]` type INCLUDES `"key"` is a single concrete `KeyData`
|
|
279
|
+
* with a literal owner name and a projection that is itself a single
|
|
280
|
+
* non-union fixed-length tuple of string literals
|
|
281
|
+
* ({@link DecidableKeyData}, {@link LiteralProjection}). A bare
|
|
282
|
+
* `Statement` element, a `Statement` union, a `KeyStatement` union, a
|
|
283
|
+
* widened owner, a widened or union projection each make the roster
|
|
284
|
+
* UNKNOWABLE.
|
|
285
|
+
*
|
|
286
|
+
* Any failure is the tier's one forbidden verdict in waiting — a false
|
|
287
|
+
* wall — so anything outside the whitelist degrades the WHOLE
|
|
288
|
+
* {@link TargetKeyWall} to silent (the degradation law: best effort
|
|
289
|
+
* degrades to silent, never to a wrong judgment), exactly as a widened
|
|
290
|
+
* FACE already silences its own judgment; the value tier stays
|
|
291
|
+
* authoritative. ONE recorded limit, pre-existing and outside this
|
|
292
|
+
* detector's reach: a generic type parameter CONSTRAINED to a union of
|
|
293
|
+
* tuples hangs tsc in the deferred-scan machinery before any verdict is
|
|
294
|
+
* reached — a degenerate spelling no shipped surface writes, recorded
|
|
295
|
+
* here as the tier's known boundary, not fixed.
|
|
263
296
|
*/
|
|
297
|
+
type DecidableRoster<Stmts extends readonly Statement[]> = [true] extends [IsMulti<Stmts>]
|
|
298
|
+
? false
|
|
299
|
+
: [number] extends [Stmts["length"]]
|
|
300
|
+
? false
|
|
301
|
+
: [Stmts] extends [readonly [infer H extends Statement, ...infer T extends readonly Statement[]]]
|
|
302
|
+
? [true] extends [IsMulti<H>]
|
|
303
|
+
? false
|
|
304
|
+
: "key" extends H["data"]["kind"]
|
|
305
|
+
? [DecidableKeyData<H["data"]>] extends [true]
|
|
306
|
+
? DecidableRoster<T>
|
|
307
|
+
: false
|
|
308
|
+
: DecidableRoster<T>
|
|
309
|
+
: true
|
|
310
|
+
|
|
311
|
+
interface TargetKeyWall<Target extends string, Projection extends string> {
|
|
312
|
+
readonly "schema target-key wall — a containment/mirrors/capacity target projection matches no key of the target relation (declare the key() it resolves, or project an existing one)": {
|
|
313
|
+
readonly target: Target
|
|
314
|
+
readonly projection: Projection
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
type DeclaredKeyMatch<N extends string, PU extends string, Keys extends readonly KeyEntry[]> = Keys extends readonly [
|
|
319
|
+
infer H extends KeyEntry,
|
|
320
|
+
...infer T extends readonly KeyEntry[]
|
|
321
|
+
]
|
|
322
|
+
? SetEq<H[0], N> extends true
|
|
323
|
+
? string extends H[1]
|
|
324
|
+
? true
|
|
325
|
+
: SetEq<PU, H[1]> extends true
|
|
326
|
+
? true
|
|
327
|
+
: DeclaredKeyMatch<N, PU, T>
|
|
328
|
+
: DeclaredKeyMatch<N, PU, T>
|
|
329
|
+
: TargetKeyWall<N, PU>
|
|
330
|
+
|
|
331
|
+
type FreshKeyMatch<PU extends string, O extends AnyRelation> = [PU] extends [FreshFieldNames<RelationFields<O>>]
|
|
332
|
+
? true extends IsMulti<PU>
|
|
333
|
+
? false
|
|
334
|
+
: true
|
|
335
|
+
: false
|
|
336
|
+
|
|
337
|
+
type JudgeTargetFace<F extends FaceData, Keys extends readonly KeyEntry[]> = string extends F["owner"]["name"]
|
|
338
|
+
? true
|
|
339
|
+
: string extends F["projection"][number]
|
|
340
|
+
? true
|
|
341
|
+
: F["owner"] extends AnyClosed
|
|
342
|
+
? SetEq<F["projection"][number], "id"> extends true
|
|
343
|
+
? true
|
|
344
|
+
: TargetKeyWall<F["owner"]["name"], F["projection"][number]>
|
|
345
|
+
: F["owner"] extends AnyRelation
|
|
346
|
+
? FreshKeyMatch<F["projection"][number], F["owner"]> extends true
|
|
347
|
+
? true
|
|
348
|
+
: DeclaredKeyMatch<F["owner"]["name"], F["projection"][number], Keys>
|
|
349
|
+
: true
|
|
350
|
+
|
|
351
|
+
type JudgeStatement<St extends Statement, Keys extends readonly KeyEntry[]> = St["data"] extends {
|
|
352
|
+
readonly source: FaceData
|
|
353
|
+
readonly target: infer Tg extends FaceData
|
|
354
|
+
}
|
|
355
|
+
? JudgeTargetFace<Tg, Keys>
|
|
356
|
+
: true
|
|
357
|
+
|
|
358
|
+
type TargetKeyScan<Stmts extends readonly Statement[], Keys extends readonly KeyEntry[]> = Stmts extends readonly [
|
|
359
|
+
infer H extends Statement,
|
|
360
|
+
...infer T extends readonly Statement[]
|
|
361
|
+
]
|
|
362
|
+
? [JudgeStatement<H, Keys>] extends [true]
|
|
363
|
+
? TargetKeyScan<T, Keys>
|
|
364
|
+
: JudgeStatement<H, Keys>
|
|
365
|
+
: unknown
|
|
366
|
+
|
|
264
367
|
type LawfulStatements<Rels extends SchemaRelations, Stmts extends readonly Statement[]> = WallScan<
|
|
265
368
|
BuildComps<PairsOf<Stmts>>,
|
|
266
369
|
GeneratorsOf<Rels>,
|
|
267
370
|
PairsOf<Stmts>
|
|
268
|
-
>
|
|
371
|
+
> &
|
|
372
|
+
([DecidableRoster<Stmts>] extends [true] ? TargetKeyScan<Stmts, DeclaredKeysOf<Stmts>> : unknown)
|
|
269
373
|
|
|
270
|
-
/**
|
|
271
|
-
* One coordinate's class per the three laws, at the TYPE tier: its
|
|
272
|
-
* component's single generator (the exact literal — a generator names its
|
|
273
|
-
* class); a component-less generator is its own class; a component-less
|
|
274
|
-
* non-generator is bare (`undefined`); and a GENERATOR-LESS component is
|
|
275
|
-
* carried as its member-coordinate SET (the union of the component's
|
|
276
|
-
* coordinates — a canonical, deterministic type). The set REPRESENTS the
|
|
277
|
-
* runtime's least-member class name faithfully: the runtime name is by
|
|
278
|
-
* construction a member, two slots share a class exactly when their sets
|
|
279
|
-
* are identical (so `JoinOk` judges identically at both tiers), and bare
|
|
280
|
-
* never equals a set. The least-member PICK itself is deliberately not
|
|
281
|
-
* made at the type tier: TypeScript's union member order is not observably
|
|
282
|
-
* deterministic (the same key union tuples differently across checking
|
|
283
|
-
* contexts — measured, not conjectured), so any type-level "least in
|
|
284
|
-
* declaration order" would drift between compilations; the ratified
|
|
285
|
-
* declaration-order name lives at the VALUE tier (`computeClasses`), which
|
|
286
|
-
* is the only tier the wire reads.
|
|
287
|
-
*/
|
|
288
374
|
type ClassOfCoord<Comps extends readonly string[], Gens extends string, C extends string> = [CompOf<Comps, C>] extends [
|
|
289
375
|
infer M extends string
|
|
290
376
|
]
|
|
@@ -299,43 +385,23 @@ type ClassOfCoord<Comps extends readonly string[], Gens extends string, C extend
|
|
|
299
385
|
: never
|
|
300
386
|
: never
|
|
301
387
|
|
|
302
|
-
/** The computed class map over precomputed components/generators. */
|
|
303
388
|
type ComputedClasses<Rels extends SchemaRelations, Comps extends readonly string[], Gens extends string> = {
|
|
304
389
|
readonly [N in keyof Rels & string]: {
|
|
305
390
|
readonly [F in MemberFieldNames<Rels[N]>]: ClassOfCoord<Comps, Gens, `${N}.${F}`>
|
|
306
391
|
}
|
|
307
392
|
}
|
|
308
393
|
|
|
309
|
-
/**
|
|
310
|
-
* THE type-level class map of a schema: relation name → field name → the
|
|
311
|
-
* law-computed class (`undefined` = bare) — what `schema()` returns as the
|
|
312
|
-
* `classes` property's type, and what query joins compare. Generator
|
|
313
|
-
* classes are exact name literals; generator-less classes are their
|
|
314
|
-
* member-coordinate sets (see {@link ClassOfCoord} — the runtime map's
|
|
315
|
-
* least-member name is always a member, so the property type is honest).
|
|
316
|
-
*/
|
|
317
394
|
type ClassesOf<Rels extends SchemaRelations, Stmts extends readonly Statement[]> = ComputedClasses<
|
|
318
395
|
Rels,
|
|
319
396
|
BuildComps<PairsOf<Stmts>>,
|
|
320
397
|
GeneratorsOf<Rels>
|
|
321
398
|
>
|
|
322
399
|
|
|
323
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
324
|
-
// The runtime twin: the same computation as a plain union-find.
|
|
325
|
-
// ————————————————————————————————————————————————————————————————————————
|
|
326
|
-
|
|
327
|
-
/** One relation's declared coordinates and generator flags, in declaration order. */
|
|
328
400
|
interface MemberCoords {
|
|
329
401
|
readonly relation: string
|
|
330
402
|
readonly fields: ReadonlyArray<{ readonly name: string; readonly generator: boolean }>
|
|
331
403
|
}
|
|
332
404
|
|
|
333
|
-
/**
|
|
334
|
-
* Reads every member's coordinates off the relation record, declaration
|
|
335
|
-
* order throughout — the sealed shape through THE one reader
|
|
336
|
-
* (`sealedFieldsOf`): a closed member's generator is its synthetic `id`
|
|
337
|
-
* (ordinal 0), an ordinary member's generators are its fresh-marked fields.
|
|
338
|
-
*/
|
|
339
405
|
function memberCoords(relations: SchemaRelations): MemberCoords[] {
|
|
340
406
|
const out: MemberCoords[] = []
|
|
341
407
|
for (const [relationName, member] of Object.entries(relations)) {
|
|
@@ -351,7 +417,6 @@ function memberCoords(relations: SchemaRelations): MemberCoords[] {
|
|
|
351
417
|
return out
|
|
352
418
|
}
|
|
353
419
|
|
|
354
|
-
/** A plain union-find over coordinate strings, with per-root generator rosters. */
|
|
355
420
|
interface UnionFind {
|
|
356
421
|
find(coord: string): string
|
|
357
422
|
union(a: string, b: string): string
|
|
@@ -359,7 +424,6 @@ interface UnionFind {
|
|
|
359
424
|
markGenerator(coord: string): void
|
|
360
425
|
}
|
|
361
426
|
|
|
362
|
-
/** Builds the union-find. */
|
|
363
427
|
function makeUnionFind(): UnionFind {
|
|
364
428
|
const parent = new Map<string, string>()
|
|
365
429
|
const generators = new Map<string, string[]>()
|
|
@@ -402,7 +466,6 @@ function makeUnionFind(): UnionFind {
|
|
|
402
466
|
}
|
|
403
467
|
}
|
|
404
468
|
|
|
405
|
-
/** The paired faces of one statement, or undefined for a key (an FD pairs nothing). */
|
|
406
469
|
function statementFaces(statement: Statement): readonly [FaceData, FaceData] | undefined {
|
|
407
470
|
const data = statement.data
|
|
408
471
|
if (data.kind === "key") {
|
|
@@ -517,5 +580,14 @@ function classesComplete<Classes extends SchemaClasses>(
|
|
|
517
580
|
})
|
|
518
581
|
}
|
|
519
582
|
|
|
520
|
-
export type {
|
|
583
|
+
export type {
|
|
584
|
+
ClassesOf,
|
|
585
|
+
ClassLookup,
|
|
586
|
+
ClassRecordOf,
|
|
587
|
+
ClassWall,
|
|
588
|
+
LawfulStatements,
|
|
589
|
+
RelationClasses,
|
|
590
|
+
SchemaClasses,
|
|
591
|
+
TargetKeyWall
|
|
592
|
+
}
|
|
521
593
|
export { classesComplete, computeClasses }
|
package/src/lower.ts
CHANGED
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
* data (`#spec.ts`), which the napi bridge marshals verbatim. Lowering is
|
|
4
4
|
* TOTAL on well-typed inputs — no validation lives here beyond what the
|
|
5
5
|
* types and the construction boundaries already guarantee — and it is the
|
|
6
|
-
* only place statement internals are read for the wire.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* only place statement internals are read for the wire. In particular,
|
|
7
|
+
* lowering never emits an engine-refused containment target: it accepts
|
|
8
|
+
* only `schema` outputs, and `schema`'s target-key wall already
|
|
9
|
+
* refused any non-key target projection (60-containment-parity —
|
|
10
|
+
* totality INHERITED, not re-checked). Ordering is declaration order
|
|
11
|
+
* throughout, and every output object is built with one fixed key order,
|
|
12
|
+
* so serialization is deterministic (byte-stable).
|
|
9
13
|
*/
|
|
10
14
|
|
|
11
15
|
import * as errors from "@superbuilders/errors"
|
|
@@ -27,12 +31,6 @@ import type {
|
|
|
27
31
|
} from "#spec.ts"
|
|
28
32
|
import type { Statement } from "#statements.ts"
|
|
29
33
|
|
|
30
|
-
/**
|
|
31
|
-
* Lowers one field descriptor's structural type to the wire
|
|
32
|
-
* {@link ValueTypeSpec}: the S1 kind tags map 1:1 onto the `ValueType`
|
|
33
|
-
* vocabulary (`str` spells `string`, `bytes` spells `fixedBytes` with its
|
|
34
|
-
* width label as `len`; intervals carry element and width labels through).
|
|
35
|
-
*/
|
|
36
34
|
function valueTypeOf(field: AnyField): ValueTypeSpec {
|
|
37
35
|
switch (field.kind) {
|
|
38
36
|
case "bool":
|
|
@@ -50,16 +48,6 @@ function valueTypeOf(field: AnyField): ValueTypeSpec {
|
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
/**
|
|
54
|
-
* Lowers one field descriptor to its {@link FieldSpec}: the structural
|
|
55
|
-
* type, the structural fresh mark (`fresh` is the literal `true` exactly
|
|
56
|
-
* on a fresh-marked u64 — on an unmarked one the property holds the marked
|
|
57
|
-
* descriptor, never `true`), and the wire's `newtype` — the COMPUTED class
|
|
58
|
-
* name `schema()` derived from the statement list (the laws type the
|
|
59
|
-
* columns), `undefined` on a bare field. The engine reads newtypes for
|
|
60
|
-
* handle resolution and the coherence check only and DROPS them at
|
|
61
|
-
* descriptor lowering — class names are never fingerprinted.
|
|
62
|
-
*/
|
|
63
51
|
function lowerField(name: string, field: AnyField, newtype: string | undefined): FieldSpec {
|
|
64
52
|
return {
|
|
65
53
|
name,
|
|
@@ -69,7 +57,6 @@ function lowerField(name: string, field: AnyField, newtype: string | undefined):
|
|
|
69
57
|
}
|
|
70
58
|
}
|
|
71
59
|
|
|
72
|
-
/** Lowers one face to a `SideSpec`: names only, σ as (field, set) pairs. */
|
|
73
60
|
function lowerFace(face: FaceData): SideSpec {
|
|
74
61
|
return {
|
|
75
62
|
relation: face.owner.name,
|
|
@@ -80,12 +67,6 @@ function lowerFace(face: FaceData): SideSpec {
|
|
|
80
67
|
}
|
|
81
68
|
}
|
|
82
69
|
|
|
83
|
-
/**
|
|
84
|
-
* Lowers one statement. `mirrors` stays ONE spec statement
|
|
85
|
-
* (`bidirectional: true`) — the engine performs the `==` lowering to two
|
|
86
|
-
* adjacent containments, `source <= target` first, exactly as the macro
|
|
87
|
-
* does.
|
|
88
|
-
*/
|
|
89
70
|
function lowerStatement(statement: Statement): StatementSpec {
|
|
90
71
|
const data = statement.data
|
|
91
72
|
switch (data.kind) {
|
|
@@ -116,13 +97,6 @@ function lowerStatement(statement: Statement): StatementSpec {
|
|
|
116
97
|
}
|
|
117
98
|
}
|
|
118
99
|
|
|
119
|
-
/**
|
|
120
|
-
* Lowers one ordinary relation to its `RelationSpec` fragment: fields in
|
|
121
|
-
* declaration order, each carrying its law-computed class name as the
|
|
122
|
-
* `newtype` (`classes` — the schema's class record for this relation;
|
|
123
|
-
* bare fields carry `undefined`), `closed: undefined` (the option is the
|
|
124
|
-
* kind — one sum, R7).
|
|
125
|
-
*/
|
|
126
100
|
function lowerRelation(relation: AnyRelation, classes: RelationClasses): RelationSpec {
|
|
127
101
|
const fields: FieldSpec[] = relation.data.fields.map(function lowerDeclared(declared) {
|
|
128
102
|
return lowerField(declared.name, declared.field, classes[declared.name])
|
|
@@ -130,16 +104,6 @@ function lowerRelation(relation: AnyRelation, classes: RelationClasses): Relatio
|
|
|
130
104
|
return { name: relation.name, fields, closed: undefined }
|
|
131
105
|
}
|
|
132
106
|
|
|
133
|
-
/**
|
|
134
|
-
* Lowers one closed relation to its `RelationSpec` fragment: declared
|
|
135
|
-
* intrinsic columns only (the engine materializes the synthetic `id`) and
|
|
136
|
-
* the fused closedness sum (R7) — the handle newtype (the COMPUTED class
|
|
137
|
-
* name of the id's generator class, `"Kind.id"`, always present: a closed
|
|
138
|
-
* id is a generator; every referencing field shares it by law, which is
|
|
139
|
-
* how the engine resolves a handle literal back to its roster) together
|
|
140
|
-
* with the ground axioms in declaration order (row id = index); the
|
|
141
|
-
* literals were already lowered at `closed()` construction.
|
|
142
|
-
*/
|
|
143
107
|
function lowerClosed(member: AnyClosed, classes: RelationClasses): RelationSpec {
|
|
144
108
|
const fields: FieldSpec[] = member.data.columns.map(function lowerColumn(column) {
|
|
145
109
|
return lowerField(column.name, column.field, classes[column.name])
|
|
@@ -154,17 +118,8 @@ function lowerClosed(member: AnyClosed, classes: RelationClasses): RelationSpec
|
|
|
154
118
|
return { name: member.name, fields, closed: { newtype, rows } }
|
|
155
119
|
}
|
|
156
120
|
|
|
157
|
-
/** The frozen empty class record a relation outside the schema's map lowers under (nothing classed). */
|
|
158
121
|
const noClasses: RelationClasses = Object.freeze({})
|
|
159
122
|
|
|
160
|
-
/**
|
|
161
|
-
* Lowers a whole theory to the `SchemaSpec` the bridge takes: relations in
|
|
162
|
-
* record declaration order, DECLARED statements only in written order (the
|
|
163
|
-
* engine materializes the fresh-implied and closed auto-keys itself), and
|
|
164
|
-
* every field's `newtype` slot fed from the schema's law-computed class
|
|
165
|
-
* map — the ONE domain authority (fingerprint-neutral: the engine drops
|
|
166
|
-
* newtypes at descriptor lowering).
|
|
167
|
-
*/
|
|
168
123
|
function lower(theory: AnySchema): SchemaSpec {
|
|
169
124
|
const relations: RelationSpec[] = Object.entries(theory.relations).map(function lowerMember([name, member]) {
|
|
170
125
|
const classes = theory.classes[name] ?? noClasses
|
|
@@ -176,4 +131,4 @@ function lower(theory: AnySchema): SchemaSpec {
|
|
|
176
131
|
return { relations, statements: theory.statements.map(lowerStatement) }
|
|
177
132
|
}
|
|
178
133
|
|
|
179
|
-
export { lower
|
|
134
|
+
export { lower }
|