@bjornpagen/bumbledb 0.14.0 → 0.17.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 +58 -62
- package/README.md +82 -56
- 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 +93 -290
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +713 -556
- 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 +13 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -20
- 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 +97 -390
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +39 -61
- 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 +64 -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 +3 -3
- package/src/capacity.ts +26 -140
- package/src/closed.ts +5 -206
- package/src/db.ts +997 -854
- package/src/face.ts +0 -142
- package/src/fields.ts +4 -172
- package/src/index.ts +32 -35
- package/src/law.ts +201 -129
- package/src/lower.ts +8 -53
- package/src/marshal.ts +1 -85
- package/src/native.ts +192 -413
- package/src/query/atom.ts +26 -313
- package/src/query/find.ts +24 -110
- package/src/query/lower.ts +132 -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/dist/exhume.d.ts +0 -143
- package/dist/exhume.d.ts.map +0 -1
- package/dist/exhume.js +0 -166
- package/dist/exhume.js.map +0 -1
- package/src/exhume.ts +0 -267
package/src/index.ts
CHANGED
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @bjornpagen/bumbledb — the type-theoretic TypeScript SDK for the
|
|
3
3
|
* bumbledb embedded relational engine. Public surface: the structural type
|
|
4
|
-
* kernel (fields as pure structure, `relation
|
|
5
|
-
* never declared: THE LAWS TYPE THE COLUMNS, `schema
|
|
4
|
+
* kernel (fields as pure structure, `relation`, `closed` — domains are
|
|
5
|
+
* never declared: THE LAWS TYPE THE COLUMNS, `schema` computing every
|
|
6
6
|
* field's equivalence class FROM the statement list at both tiers), the
|
|
7
|
-
* statement algebra with `schema
|
|
8
|
-
* runtime (exclusive-lock stores, transactions, typed violations,
|
|
9
|
-
*
|
|
10
|
-
* closables), the query surface (kysely-shaped:
|
|
7
|
+
* statement algebra with `schema` and `SchemaSpec` lowering (PRD-06), the `Db`
|
|
8
|
+
* runtime (exclusive-lock stores, transactions, typed violations, callback
|
|
9
|
+
* instance reads, one-shot `write`/`writeFrom` with `abandon` — PRD-07), the query surface (kysely-shaped:
|
|
11
10
|
* `query(S).rule(r => { const { id, name } = v(Holder); return r.match(Holder, { id, name }).find({ name }) })` —
|
|
12
|
-
* variables minted by `v
|
|
11
|
+
* variables minted by `v` and joined by OBJECT REFERENCE (reuse is the
|
|
13
12
|
* join), the head a `find` RECORD whose keys name the answer columns
|
|
14
13
|
* (renames are real), params still STRING-named, plus negation,
|
|
15
14
|
* conditions, aggregates, and interiors / one linear rec via
|
|
16
15
|
* `q.interior` / `q.reach` —
|
|
17
|
-
* `db.prepare` as a plain value;
|
|
18
|
-
*
|
|
19
|
-
* common host identifiers — import aliasing is the answer; the SDK does
|
|
20
|
-
* not rename for collision-avoidance), the exhume surface
|
|
21
|
-
* (`Db.exhume` — the one schema-independent read path: the store's
|
|
22
|
-
* self-described shapes and raw facts by name, typed at bare structural
|
|
23
|
-
* values, deliberately schema-free). The raw native bridge is not exported.
|
|
16
|
+
* `db.prepare` as a plain value; comparisons and connectives live on
|
|
17
|
+
* the rule scope). The raw native bridge is not exported.
|
|
24
18
|
*/
|
|
25
19
|
|
|
26
20
|
export type {
|
|
@@ -51,7 +45,9 @@ export { closed } from "#closed.ts"
|
|
|
51
45
|
export type {
|
|
52
46
|
Abandon,
|
|
53
47
|
AbandonedArm,
|
|
48
|
+
Admission,
|
|
54
49
|
CapacityViolation,
|
|
50
|
+
Committed,
|
|
55
51
|
ContainmentViolation,
|
|
56
52
|
DeclaredKeyFact,
|
|
57
53
|
DeclaredKeyViolation,
|
|
@@ -62,26 +58,30 @@ export type {
|
|
|
62
58
|
MirrorViolation,
|
|
63
59
|
MutationReport,
|
|
64
60
|
OffendingFact,
|
|
61
|
+
OwnedInstance,
|
|
65
62
|
Prepared,
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
ReadInstance,
|
|
64
|
+
SyncResult,
|
|
68
65
|
Violation,
|
|
69
|
-
|
|
66
|
+
Witness,
|
|
67
|
+
WriteFromOutcome,
|
|
68
|
+
WriteOutcome,
|
|
69
|
+
WriteTx
|
|
70
70
|
} from "#db.ts"
|
|
71
|
-
export { abandon, Db, ErrGenerationMoved, ErrNewtypeMismatch } from "#db.ts"
|
|
72
|
-
export type {
|
|
73
|
-
Exhumed,
|
|
74
|
-
ExhumedAxiom,
|
|
75
|
-
ExhumedDescriptor,
|
|
76
|
-
ExhumedFact,
|
|
77
|
-
ExhumedField,
|
|
78
|
-
ExhumedRelation
|
|
79
|
-
} from "#exhume.ts"
|
|
80
71
|
export {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
abandon,
|
|
73
|
+
Db,
|
|
74
|
+
ErrAsyncCallback,
|
|
75
|
+
ErrFingerprintMismatch,
|
|
76
|
+
ErrForeignPrepared,
|
|
77
|
+
ErrForeignWitness,
|
|
78
|
+
ErrIrError,
|
|
79
|
+
ErrNewtypeMismatch,
|
|
80
|
+
ErrSchemaError,
|
|
81
|
+
ErrSpentHandle,
|
|
82
|
+
ErrUseAfterScope,
|
|
83
|
+
InstanceBuilder
|
|
84
|
+
} from "#db.ts"
|
|
85
85
|
export type {
|
|
86
86
|
AnyFace,
|
|
87
87
|
Arity,
|
|
@@ -115,7 +115,7 @@ export type {
|
|
|
115
115
|
} from "#fields.ts"
|
|
116
116
|
export { bool, bytes, i64, interval, span, str, u64 } from "#fields.ts"
|
|
117
117
|
export type { ClassesOf, ClassWall, LawfulStatements, RelationClasses, SchemaClasses } from "#law.ts"
|
|
118
|
-
export { lower
|
|
118
|
+
export { lower } from "#lower.ts"
|
|
119
119
|
export type { KeyFact } from "#marshal.ts"
|
|
120
120
|
export type { FactValue, ParsedQuery, QueryIr, StatementKindTag } from "#native.ts"
|
|
121
121
|
|
|
@@ -131,7 +131,7 @@ export type {
|
|
|
131
131
|
RuleData,
|
|
132
132
|
Tree
|
|
133
133
|
} from "#query/atom.ts"
|
|
134
|
-
export { ALLEN
|
|
134
|
+
export { ALLEN } from "#query/atom.ts"
|
|
135
135
|
export type { Agg, FindEntry } from "#query/find.ts"
|
|
136
136
|
export type {
|
|
137
137
|
AnyQuery,
|
|
@@ -151,10 +151,8 @@ export type {
|
|
|
151
151
|
TermOps
|
|
152
152
|
} from "#query/lower.ts"
|
|
153
153
|
export { lowerQuery, query } from "#query/lower.ts"
|
|
154
|
-
export { parseQueryIr } from "#query/parse-ir.ts"
|
|
155
154
|
export type {
|
|
156
155
|
ClassedField,
|
|
157
|
-
Duration,
|
|
158
156
|
MatchFields,
|
|
159
157
|
MatchOwner,
|
|
160
158
|
Param,
|
|
@@ -197,7 +195,6 @@ export type {
|
|
|
197
195
|
ValueTypeSpec,
|
|
198
196
|
WeightSpec
|
|
199
197
|
} from "#spec.ts"
|
|
200
|
-
export { renderCapacityBound, renderCapacityWindow, renderLiteral, renderLiteralSet, renderWeight } from "#spec.ts"
|
|
201
198
|
export type {
|
|
202
199
|
CapacityData,
|
|
203
200
|
CapacityStatement,
|
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 }
|