@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/dist/statements.js
CHANGED
|
@@ -8,10 +8,16 @@
|
|
|
8
8
|
* Every field reference is checked against the relation it names in the
|
|
9
9
|
* TYPE — existence through {@link FaceFields} (`on(R, "nope")` does not
|
|
10
10
|
* compile) and STRUCTURAL compatibility through {@link SameShapes}: the two
|
|
11
|
-
* faces' projected kind/width/element
|
|
12
|
-
* (the minimal kernel — descriptors are pure structure) and
|
|
13
|
-
* positionwise equal, so a u64 face against a str face, a
|
|
14
|
-
* mismatch,
|
|
11
|
+
* faces' projected kind/width/element/roster quadruples are read off the
|
|
12
|
+
* schema type (the minimal kernel — descriptors are pure structure) and
|
|
13
|
+
* constrained positionwise equal, so a u64 face against a str face, a
|
|
14
|
+
* bytes width mismatch, an interval element mismatch, or a bare column
|
|
15
|
+
* against a closed reference is a compile error. The ROSTER slot carries a
|
|
16
|
+
* construction-time runtime twin here ({@link assertRosterAgreement} —
|
|
17
|
+
* roster IDENTITY, positionwise: a closed vocabulary's referencing column
|
|
18
|
+
* is spelled with the vocabulary's own id descriptor, the ONE spelling, so
|
|
19
|
+
* a plain u64 column can never alias a vocabulary through a declared law
|
|
20
|
+
* and the SDK's descriptor-keyed closed judgments stay sound). Domains are
|
|
15
21
|
* NOT compared here — there is no domain to compare at construction: the
|
|
16
22
|
* statements themselves are what define the equivalence classes, and the
|
|
17
23
|
* domain wall lives where they aggregate — `schema()` (the
|
|
@@ -27,6 +33,66 @@
|
|
|
27
33
|
import * as errors from "@superbuilders/errors";
|
|
28
34
|
import { renderFace } from "#face.ts";
|
|
29
35
|
import { renderWindow } from "#spec.ts";
|
|
36
|
+
/**
|
|
37
|
+
* The field descriptor one face position projects: an ordinary relation's
|
|
38
|
+
* declared field; a closed relation's SEALED shape — the synthetic `id`
|
|
39
|
+
* (the value's own roster-carrying descriptor, by identity) or a declared
|
|
40
|
+
* payload column. `undefined` when the name is foreign (the type tier makes
|
|
41
|
+
* that unwritable; the engine re-judges projections at `Db.create`).
|
|
42
|
+
*/
|
|
43
|
+
function projectedFieldOf(face, fieldName) {
|
|
44
|
+
const owner = face.owner;
|
|
45
|
+
if ("axioms" in owner) {
|
|
46
|
+
if (fieldName === "id") {
|
|
47
|
+
return owner.id;
|
|
48
|
+
}
|
|
49
|
+
const column = owner.data.columns.find(function byName(candidate) {
|
|
50
|
+
return candidate.name === fieldName;
|
|
51
|
+
});
|
|
52
|
+
return column?.field;
|
|
53
|
+
}
|
|
54
|
+
const declared = owner.data.fields.find(function byName(candidate) {
|
|
55
|
+
return candidate.name === fieldName;
|
|
56
|
+
});
|
|
57
|
+
return declared?.field;
|
|
58
|
+
}
|
|
59
|
+
/** The roster a descriptor carries: present exactly on a closed reference, absent on every other kind. */
|
|
60
|
+
function rosterOfField(field) {
|
|
61
|
+
if (field !== undefined && "closed" in field) {
|
|
62
|
+
return field.closed;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
/** Renders one face position's closedness for the roster-agreement diagnostics. */
|
|
67
|
+
function renderRosterSide(roster) {
|
|
68
|
+
return roster === undefined ? "a bare column" : `a ${roster.name} reference`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The runtime twin of {@link SameShapes}'s roster slot: the two faces'
|
|
72
|
+
* projected descriptors must agree POSITIONWISE on closedness — the same
|
|
73
|
+
* roster (value identity — vocabulary identity is value identity, the
|
|
74
|
+
* SDK's membership rule everywhere) or none. Without this wall a plain u64
|
|
75
|
+
* column could alias a closed vocabulary through a declared containment
|
|
76
|
+
* (`docs/architecture/10-data-model.md` spells the ENGINE encoding that
|
|
77
|
+
* way), and every descriptor-keyed closed judgment — the orderable ban,
|
|
78
|
+
* the name↔id marshal, answer decode — would silently miss it. The
|
|
79
|
+
* vocabulary's own descriptor (`Kind.id`) is the ONE spelling of a closed
|
|
80
|
+
* reference at this surface (the canonical-utterance law); the engine
|
|
81
|
+
* cannot backstop this one — the wire carries plain u64s, no rosters.
|
|
82
|
+
*/
|
|
83
|
+
function assertRosterAgreement(source, target, statement) {
|
|
84
|
+
source.projection.forEach(function agreeAt(fieldName, position) {
|
|
85
|
+
const targetField = target.projection[position];
|
|
86
|
+
if (targetField === undefined) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const sourceRoster = rosterOfField(projectedFieldOf(source, fieldName));
|
|
90
|
+
const targetRoster = rosterOfField(projectedFieldOf(target, targetField));
|
|
91
|
+
if (sourceRoster !== targetRoster) {
|
|
92
|
+
throw errors.new(`${source.owner.name}.${fieldName} is ${renderRosterSide(sourceRoster)} but ${target.owner.name}.${targetField} is ${renderRosterSide(targetRoster)} — closedness rides the descriptor: a closed reference is spelled with the vocabulary's own id descriptor (one meaning, one spelling), so faces pair closed-with-closed through one roster or bare-with-bare, never across — ${renderStatement(statement)}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
30
96
|
/**
|
|
31
97
|
* `R(X) -> R` — the FD key form, composite keys as tuples. No selection
|
|
32
98
|
* parameter exists (the FD-with-selection shape is unrepresentable, as in
|
|
@@ -65,7 +131,9 @@ function contained(source, target) {
|
|
|
65
131
|
target: target.data,
|
|
66
132
|
bidirectional: false
|
|
67
133
|
});
|
|
68
|
-
|
|
134
|
+
const statement = Object.freeze({ data });
|
|
135
|
+
assertRosterAgreement(data.source, data.target, statement);
|
|
136
|
+
return statement;
|
|
69
137
|
}
|
|
70
138
|
/**
|
|
71
139
|
* `A(X|φ) == B(Y|ψ)` — the bidirectional abbreviation, one utterance: the
|
|
@@ -83,7 +151,9 @@ function mirrors(source, target) {
|
|
|
83
151
|
target: target.data,
|
|
84
152
|
bidirectional: true
|
|
85
153
|
});
|
|
86
|
-
|
|
154
|
+
const statement = Object.freeze({ data });
|
|
155
|
+
assertRosterAgreement(data.source, data.target, statement);
|
|
156
|
+
return statement;
|
|
87
157
|
}
|
|
88
158
|
/**
|
|
89
159
|
* `B(Y|ψ) <={window} A(X|φ)` — the cardinality window. READ CAREFULLY: the
|
|
@@ -102,7 +172,9 @@ function window(target, count, source) {
|
|
|
102
172
|
window: count.window,
|
|
103
173
|
source: source.data
|
|
104
174
|
});
|
|
105
|
-
|
|
175
|
+
const statement = Object.freeze({ data });
|
|
176
|
+
assertRosterAgreement(data.source, data.target, statement);
|
|
177
|
+
return statement;
|
|
106
178
|
}
|
|
107
179
|
/**
|
|
108
180
|
* Renders one statement in the CANONICAL macro spelling
|
package/dist/statements.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statements.js","sourceRoot":"","sources":["../src/statements.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"statements.js","sourceRoot":"","sources":["../src/statements.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bjornpagen/bumbledb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Type-theoretic TypeScript SDK for the bumbledb embedded relational engine",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@superbuilders/errors": "^4.0.2"
|
|
45
45
|
},
|
|
46
46
|
"optionalDependencies": {
|
|
47
|
-
"@bjornpagen/bumbledb-darwin-arm64": "0.
|
|
47
|
+
"@bjornpagen/bumbledb-darwin-arm64": "0.4.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@biomejs/biome": "^2.0.0-beta.5",
|
package/src/closed.ts
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Closed relations (`docs/architecture/10-data-model.md` § closed
|
|
3
3
|
* relations): a vocabulary whose extension is declared in the schema — two
|
|
4
|
-
* tiers, one function.
|
|
5
|
-
* macro's (host-enum analog): handle CONSTANTS on the value
|
|
6
|
-
* (`Kind.Checking`, ids = declaration order, each a BARE `bigint` — no
|
|
7
|
-
* brand), the `fromId` weld, an `id` field descriptor carrying the CLOSED
|
|
8
|
-
* LINKAGE (the roster — pure structure, no declared domain: the laws type
|
|
9
|
-
* the columns, and `schema()` names the id's generator class `"Kind.id"`)
|
|
10
|
-
* for other relations' field blocks (`kind: Kind.id`), payload readback
|
|
11
|
-
* (`Kind.axioms`), and the declared payload column descriptors
|
|
12
|
-
* (`Kind.columns` — the runtime twin of the `Cols` type parameter, which
|
|
13
|
-
* the face layer's structural wall reads). Bare tier: `closed("Kind", ["Checking",
|
|
4
|
+
* tiers, one function. Bare tier: `closed("Kind", ["Checking",
|
|
14
5
|
* "Savings"])`. Payload tier: `closed("Sev", { pages: bool }, { Critical:
|
|
15
6
|
* { pages: true }, ... })` — one call, three arguments (the curried tier-2
|
|
16
7
|
* spelling is DELETED — canonical utterance): the axioms record IS the
|
|
17
8
|
* handle declaration, every handle carrying every column exactly once
|
|
18
|
-
* (type-enforced).
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
9
|
+
* (type-enforced). At the host surface a handle is its NAME — a string
|
|
10
|
+
* literal of the roster's union (the drizzle law: translation, not
|
|
11
|
+
* abstraction; dispatch over a vocabulary is native `switch` narrowing, so
|
|
12
|
+
* no match operator is minted and no handle constants exist — the literal
|
|
13
|
+
* `"Checking"` is the ONE spelling). Handles are pure DATA, not properties
|
|
14
|
+
* of the value, so NO handle name is reserved: a vocabulary may legally
|
|
15
|
+
* contain handles named `match`, `where`, or `id` — the axioms record and
|
|
16
|
+
* the roster are their own namespaces. The value's whole surface: `name`;
|
|
17
|
+
* `id` — the field descriptor carrying the CLOSED LINKAGE (the roster —
|
|
18
|
+
* pure structure, no declared domain: the laws type the columns, and
|
|
19
|
+
* `schema()` names the id's generator class `"Kind.id"`) for other
|
|
20
|
+
* relations' field blocks (`kind: Kind.id`); `data` (the lowering
|
|
21
|
+
* carrier); `axioms` (payload readback, `Kind.axioms`); `columns` (the
|
|
22
|
+
* runtime twin of the `Cols` type parameter, which the face layer's
|
|
23
|
+
* structural wall reads); and — exactly when payload columns exist —
|
|
24
|
+
* `where()`, the ψ-selection surface (`Kind.where({ mastered: true })` as a
|
|
25
|
+
* face source), resolved through the ONE selection machine
|
|
26
|
+
* (`relation.ts::resolveSelection`); the bare tier has no payload columns
|
|
27
|
+
* to select on, so `.where` is absent there, at the type AND on the value.
|
|
28
|
+
* No fact type and no insert surface exist — closed relations are
|
|
29
|
+
* unwritable by construction: the value simply lacks the writable relation
|
|
30
|
+
* shape.
|
|
28
31
|
*/
|
|
29
32
|
|
|
30
33
|
import * as errors from "@superbuilders/errors"
|
|
31
|
-
import type { OneOf } from "#face.ts"
|
|
32
34
|
import {
|
|
33
35
|
type AnyField,
|
|
34
36
|
assertDeclarationOrderKey,
|
|
@@ -37,31 +39,9 @@ import {
|
|
|
37
39
|
type Infer,
|
|
38
40
|
literalOf
|
|
39
41
|
} from "#fields.ts"
|
|
40
|
-
import { resolveSelection, type SelectionBinding } from "#relation.ts"
|
|
42
|
+
import { resolveSelection, type SelectionBinding, type SelectionInput } from "#relation.ts"
|
|
41
43
|
import type { LiteralSpec } from "#spec.ts"
|
|
42
44
|
|
|
43
|
-
/**
|
|
44
|
-
* The value-surface property names a handle may not shadow — the macro's
|
|
45
|
-
* name-collision diagnostic, here over the closed value's own properties
|
|
46
|
-
* (`relation`/`selection` are reserved so a closed value can never be
|
|
47
|
-
* mistaken for a selected relation by `on()`'s discriminant; `where` is
|
|
48
|
-
* reserved because the payload tier mints the ψ-selection method under
|
|
49
|
-
* exactly that name; `match` because BOTH tiers mint the exhaustive
|
|
50
|
-
* dispatch under exactly that name).
|
|
51
|
-
*/
|
|
52
|
-
const reservedHandleNames: readonly string[] = Object.freeze([
|
|
53
|
-
"name",
|
|
54
|
-
"id",
|
|
55
|
-
"data",
|
|
56
|
-
"axioms",
|
|
57
|
-
"columns",
|
|
58
|
-
"fromId",
|
|
59
|
-
"where",
|
|
60
|
-
"match",
|
|
61
|
-
"relation",
|
|
62
|
-
"selection"
|
|
63
|
-
])
|
|
64
|
-
|
|
65
45
|
/**
|
|
66
46
|
* A payload column of a closed relation: any field descriptor except a
|
|
67
47
|
* fresh-marked one (a vocabulary's rows are ground axioms, never minted).
|
|
@@ -126,12 +106,14 @@ interface ClosedCore<Name extends string, Handles extends string, Cols extends R
|
|
|
126
106
|
readonly name: Name
|
|
127
107
|
/**
|
|
128
108
|
* The closed reference descriptor: `kind: Kind.id` in another relation's
|
|
129
|
-
* field block is the reference through which
|
|
130
|
-
* legal in that relation's selections. Pure structure plus the
|
|
131
|
-
*
|
|
132
|
-
*
|
|
109
|
+
* field block is the reference through which handle literals become
|
|
110
|
+
* legal in that relation's selections. Pure structure plus the PRECISE
|
|
111
|
+
* roster (`ClosedIdField<Handles>` — the handle union is the field's
|
|
112
|
+
* value type under `Infer`); the referencing field's domain is law-born:
|
|
113
|
+
* `schema()` computes it from the declared containment (`"Kind.id"`, the
|
|
114
|
+
* generator class).
|
|
133
115
|
*/
|
|
134
|
-
readonly id: ClosedIdField
|
|
116
|
+
readonly id: ClosedIdField<Handles>
|
|
135
117
|
readonly data: ClosedData
|
|
136
118
|
/** Payload readback: handle to its declared column values, bare and structural. */
|
|
137
119
|
readonly axioms: Axioms<Handles, Cols>
|
|
@@ -144,20 +126,18 @@ interface ClosedCore<Name extends string, Handles extends string, Cols extends R
|
|
|
144
126
|
* descriptors in declaration order for the lowering).
|
|
145
127
|
*/
|
|
146
128
|
readonly columns: Cols
|
|
147
|
-
/** The weld: declaration-order id back to its handle, or undefined beyond the roster. */
|
|
148
|
-
fromId(id: bigint): Handles | undefined
|
|
149
129
|
}
|
|
150
130
|
|
|
151
131
|
/**
|
|
152
|
-
* The `where()` argument of a closed relation:
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
132
|
+
* The `where()` argument of a closed relation: EXACTLY the relation
|
|
133
|
+
* surface's {@link SelectionInput}, over the declared payload columns — the
|
|
134
|
+
* ONE selection vocabulary, so a spelling change there (H3's membership
|
|
135
|
+
* arrays) flows through with no local change here. The synthetic `id` is
|
|
136
|
+
* deliberately unspellable ({@link PayloadColumns} refuses an `id` column):
|
|
137
|
+
* an id selection is spelled only as handle literals on the REFERENCING
|
|
138
|
+
* side (the canonical-utterance law).
|
|
157
139
|
*/
|
|
158
|
-
type ClosedSelectionInput<Cols extends Record<string, PayloadField>> =
|
|
159
|
-
readonly [C in keyof Cols]?: Infer<Cols[C]> | OneOf<Infer<Cols[C]>>
|
|
160
|
-
}
|
|
140
|
+
type ClosedSelectionInput<Cols extends Record<string, PayloadField>> = SelectionInput<Cols>
|
|
161
141
|
|
|
162
142
|
/**
|
|
163
143
|
* A closed relation with a ψ selection applied — what `on()` consumes as a
|
|
@@ -190,44 +170,19 @@ interface ClosedSelectable<Name extends string, Handles extends string, Cols ext
|
|
|
190
170
|
}
|
|
191
171
|
|
|
192
172
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
173
|
+
* A closed relation value: the core surface plus — exactly when payload
|
|
174
|
+
* columns exist — `where()` (the bare tier has nothing to select on, so
|
|
175
|
+
* the method is ABSENT there, not merely uncallable). NOTHING else:
|
|
176
|
+
* handles are data on the roster, never properties of the value (the
|
|
177
|
+
* handle constants, the match operator, and the id-to-handle weld died
|
|
178
|
+
* with the bigint era — dispatch is native `switch` narrowing over the
|
|
179
|
+
* handle union).
|
|
200
180
|
*/
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
* Exhaustive dispatch over a PAYLOAD-tier closed vocabulary: the same
|
|
207
|
-
* mapped-type exhaustiveness as the bare tier, and each arm receives its
|
|
208
|
-
* handle's typed axiom row (the declared columns, bare and structural —
|
|
209
|
-
* the frozen readback row from `axioms`).
|
|
210
|
-
*/
|
|
211
|
-
interface ClosedMatchPayload<Handles extends string, Cols extends Record<string, PayloadField>> {
|
|
212
|
-
match<T>(id: bigint, arms: { readonly [H in Handles]: (row: AxiomRow<Cols>) => T }): T
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* A closed relation value: the core surface plus one BARE constant per
|
|
217
|
-
* handle (`Kind.Checking: bigint`, ids = declaration order — the value is
|
|
218
|
-
* structural; the roster judges out-of-vocabulary ids at construction and
|
|
219
|
-
* the engine at commit), plus `match()` on BOTH tiers (bare arms take
|
|
220
|
-
* nothing; payload arms receive the typed axiom row), plus — exactly when
|
|
221
|
-
* payload columns exist — `where()` (the bare tier has nothing to select
|
|
222
|
-
* on, so the method is ABSENT there, not merely uncallable).
|
|
223
|
-
*/
|
|
224
|
-
type Closed<Name extends string, Handles extends string, Cols extends Record<string, PayloadField>> = ClosedCore<
|
|
225
|
-
Name,
|
|
226
|
-
Handles,
|
|
227
|
-
Cols
|
|
228
|
-
> & { readonly [H in Handles]: bigint } & ([keyof Cols] extends [never]
|
|
229
|
-
? ClosedMatchBare<Handles>
|
|
230
|
-
: ClosedSelectable<Name, Handles, Cols> & ClosedMatchPayload<Handles, Cols>)
|
|
181
|
+
type Closed<Name extends string, Handles extends string, Cols extends Record<string, PayloadField>> = [
|
|
182
|
+
keyof Cols
|
|
183
|
+
] extends [never]
|
|
184
|
+
? ClosedCore<Name, Handles, Cols>
|
|
185
|
+
: ClosedCore<Name, Handles, Cols> & ClosedSelectable<Name, Handles, Cols>
|
|
231
186
|
|
|
232
187
|
/** Any closed relation value, whatever its roster and columns. */
|
|
233
188
|
interface AnyClosed {
|
|
@@ -282,21 +237,6 @@ function axiomsMinted<Handles extends string, Cols extends Record<string, Payloa
|
|
|
282
237
|
})
|
|
283
238
|
}
|
|
284
239
|
|
|
285
|
-
/**
|
|
286
|
-
* The trusted seam of the handle-constant mint: every handle reads back as
|
|
287
|
-
* an own bigint — verified before the record is admitted at the constants
|
|
288
|
-
* type (a "__proto__"-named handle riding the object-protocol accessor
|
|
289
|
-
* would fail exactly this check).
|
|
290
|
-
*/
|
|
291
|
-
function constantsMinted<Handles extends string>(
|
|
292
|
-
record: Readonly<Record<string, bigint>>,
|
|
293
|
-
handles: readonly Handles[]
|
|
294
|
-
): record is Readonly<Record<string, bigint>> & { readonly [H in Handles]: bigint } {
|
|
295
|
-
return handles.every(function constantMinted(handle) {
|
|
296
|
-
return typeof record[handle] === "bigint"
|
|
297
|
-
})
|
|
298
|
-
}
|
|
299
|
-
|
|
300
240
|
/**
|
|
301
241
|
* Reads one handle's ground axiom row for lowering. The typed payload
|
|
302
242
|
* surface makes absence unrepresentable ({@link Axioms} carries every
|
|
@@ -338,22 +278,6 @@ function mintAxioms<Handles extends string, Cols extends Record<string, PayloadF
|
|
|
338
278
|
return out
|
|
339
279
|
}
|
|
340
280
|
|
|
341
|
-
/** Mints the handle constants: one own bigint per handle, ids = declaration order. */
|
|
342
|
-
function mintHandleConstants<Handles extends string>(
|
|
343
|
-
name: string,
|
|
344
|
-
handles: readonly Handles[]
|
|
345
|
-
): { readonly [H in Handles]: bigint } {
|
|
346
|
-
const out: Record<string, bigint> = {}
|
|
347
|
-
handles.forEach(function mintHandleConstant(handle, index) {
|
|
348
|
-
Object.defineProperty(out, handle, { value: BigInt(index), enumerable: true })
|
|
349
|
-
})
|
|
350
|
-
Object.freeze(out)
|
|
351
|
-
if (!constantsMinted(out, handles)) {
|
|
352
|
-
throw errors.new(`closed relation ${name}: handle-constant minting incomplete`)
|
|
353
|
-
}
|
|
354
|
-
return out
|
|
355
|
-
}
|
|
356
|
-
|
|
357
281
|
/** Bare tier: `closed("Kind", ["Checking", "Savings"])` — handles only. */
|
|
358
282
|
function closed<const Name extends string, const Handles extends readonly [string, ...string[]]>(
|
|
359
283
|
name: Name,
|
|
@@ -429,19 +353,18 @@ function closedPayload<Name extends string, Handles extends string, Cols extends
|
|
|
429
353
|
}
|
|
430
354
|
|
|
431
355
|
/**
|
|
432
|
-
* The trusted seam of the ergonomic-surface mint: `
|
|
433
|
-
* own function
|
|
434
|
-
*
|
|
435
|
-
* (`
|
|
436
|
-
*
|
|
356
|
+
* The trusted seam of the ergonomic-surface mint: `where` reads back as an
|
|
357
|
+
* own function exactly when payload columns exist, and is ABSENT otherwise
|
|
358
|
+
* — the runtime twin of the {@link Closed} type's conditional arm
|
|
359
|
+
* (`ClosedCore` alone vs `ClosedCore & ClosedSelectable`), verified before
|
|
360
|
+
* the minted value is admitted at the conditional type.
|
|
437
361
|
*/
|
|
438
362
|
function surfaceMinted<Name extends string, Handles extends string, Cols extends Record<string, PayloadField>>(
|
|
439
|
-
value: ClosedCore<Name, Handles, Cols
|
|
363
|
+
value: ClosedCore<Name, Handles, Cols>,
|
|
440
364
|
cols: readonly ClosedColumn[]
|
|
441
|
-
): value is Closed<Name, Handles, Cols> {
|
|
442
|
-
const matchable = "match" in value && typeof value.match === "function"
|
|
365
|
+
): value is ClosedCore<Name, Handles, Cols> & Closed<Name, Handles, Cols> {
|
|
443
366
|
const selectable = "where" in value && typeof value.where === "function"
|
|
444
|
-
return
|
|
367
|
+
return cols.length > 0 ? selectable : !selectable
|
|
445
368
|
}
|
|
446
369
|
|
|
447
370
|
/**
|
|
@@ -449,8 +372,7 @@ function surfaceMinted<Name extends string, Handles extends string, Cols extends
|
|
|
449
372
|
* typed end to end (a wrong-shaped mint is a compile error here, not a
|
|
450
373
|
* laundered `unknown`): roster checks, eager axiom lowering, the
|
|
451
374
|
* roster-carrying `id` descriptor, the frozen `columns` carrier (the runtime
|
|
452
|
-
* twin of the `Cols` type parameter), the
|
|
453
|
-
* `match()` on both tiers, and — on the payload tier only — the
|
|
375
|
+
* twin of the `Cols` type parameter), and — on the payload tier only — the
|
|
454
376
|
* ψ-selection `where()`.
|
|
455
377
|
*/
|
|
456
378
|
function mintClosed<Name extends string, Handles extends string, Cols extends Record<string, PayloadField>>(
|
|
@@ -468,14 +390,9 @@ function mintClosed<Name extends string, Handles extends string, Cols extends Re
|
|
|
468
390
|
throw errors.new(`closed relation ${name}: duplicate handle ${handle}`)
|
|
469
391
|
}
|
|
470
392
|
seen.add(handle)
|
|
471
|
-
if (reservedHandleNames.includes(handle)) {
|
|
472
|
-
throw errors.new(
|
|
473
|
-
`closed relation ${name}: handle ${handle} collides with the closed value's own surface (${reservedHandleNames.join(", ")})`
|
|
474
|
-
)
|
|
475
|
-
}
|
|
476
393
|
}
|
|
477
394
|
const handleList: readonly Handles[] = Object.freeze([...handles])
|
|
478
|
-
const roster: ClosedRoster = Object.freeze({ name, handles: handleList })
|
|
395
|
+
const roster: ClosedRoster<Handles> = Object.freeze({ name, handles: handleList })
|
|
479
396
|
const cols: ClosedColumn[] = []
|
|
480
397
|
for (const [columnName, field] of Object.entries(columns)) {
|
|
481
398
|
assertDeclarationOrderKey(`closed relation ${name} column`, columnName)
|
|
@@ -500,25 +417,18 @@ function mintClosed<Name extends string, Handles extends string, Cols extends Re
|
|
|
500
417
|
columns: cols,
|
|
501
418
|
rows: Object.freeze(rows)
|
|
502
419
|
})
|
|
503
|
-
const id: ClosedIdField = Object.freeze({ kind: "u64", closed: roster })
|
|
420
|
+
const id: ClosedIdField<Handles> = Object.freeze({ kind: "u64", closed: roster })
|
|
504
421
|
/**
|
|
505
|
-
* Handle names are arbitrary identifiers, so rows
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
* value whose type claims a bigint constant but reads back an object.
|
|
512
|
-
* (Object SPREAD is CreateDataProperty by spec, so the copies below are
|
|
513
|
-
* own-property safe for column names too.)
|
|
422
|
+
* Handle names are arbitrary identifiers, so axiom rows are minted with
|
|
423
|
+
* OWN-property definition (inside {@link mintAxioms}), never assignment:
|
|
424
|
+
* a handle named "__proto__" would otherwise ride the Object.prototype
|
|
425
|
+
* accessor — silently swapping the record's prototype instead of
|
|
426
|
+
* creating the row. (Object SPREAD is CreateDataProperty by spec, so the
|
|
427
|
+
* copies below are own-property safe for column names too.)
|
|
514
428
|
*/
|
|
515
429
|
const axiomsOut = mintAxioms<Handles, Cols>(name, handleList, cols, axioms)
|
|
516
|
-
const constants = mintHandleConstants(name, handleList)
|
|
517
430
|
const columnsOut: Cols = { ...columns }
|
|
518
431
|
Object.freeze(columnsOut)
|
|
519
|
-
function fromId(idValue: bigint): Handles | undefined {
|
|
520
|
-
return handleList[Number(idValue)]
|
|
521
|
-
}
|
|
522
432
|
const holder: { value: Closed<Name, Handles, Cols> | undefined } = { value: undefined }
|
|
523
433
|
/**
|
|
524
434
|
* The ψ selection: resolved against the declared payload columns through
|
|
@@ -536,27 +446,9 @@ function mintClosed<Name extends string, Handles extends string, Cols extends Re
|
|
|
536
446
|
selection: resolveSelection(name, cols, Object.entries(selection))
|
|
537
447
|
})
|
|
538
448
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
* and the chosen arm receives the handle's frozen axiom row (the bare
|
|
543
|
-
* tier's arms are typed to take nothing; the empty row rides along
|
|
544
|
-
* unread). One implementation serves both tiers — the conditional
|
|
545
|
-
* {@link Closed} arm claims the tier-exact arm signature and
|
|
546
|
-
* {@link surfaceMinted} is the trusted seam that admits it.
|
|
547
|
-
*/
|
|
548
|
-
function match<T>(idValue: bigint, arms: { readonly [H in Handles]: (row: AxiomRow<Cols>) => T }): T {
|
|
549
|
-
const handle = fromId(idValue)
|
|
550
|
-
if (handle === undefined) {
|
|
551
|
-
throw errors.new(`closed relation ${name}: match on id ${idValue} misses the roster (${handleList.join(", ")})`)
|
|
552
|
-
}
|
|
553
|
-
return arms[handle](axiomsOut[handle])
|
|
554
|
-
}
|
|
555
|
-
const core = { name, id, data, axioms: axiomsOut, columns: columnsOut, fromId }
|
|
556
|
-
const value: ClosedCore<Name, Handles, Cols> & { readonly [H in Handles]: bigint } =
|
|
557
|
-
cols.length > 0
|
|
558
|
-
? Object.freeze({ ...constants, ...core, where, match })
|
|
559
|
-
: Object.freeze({ ...constants, ...core, match })
|
|
449
|
+
const core = { name, id, data, axioms: axiomsOut, columns: columnsOut }
|
|
450
|
+
const value: ClosedCore<Name, Handles, Cols> =
|
|
451
|
+
cols.length > 0 ? Object.freeze({ ...core, where }) : Object.freeze(core)
|
|
560
452
|
if (!surfaceMinted<Name, Handles, Cols>(value, cols)) {
|
|
561
453
|
throw errors.new(`closed relation ${name}: ergonomic-surface minting incomplete`)
|
|
562
454
|
}
|
|
@@ -574,6 +466,7 @@ export type {
|
|
|
574
466
|
ClosedCore,
|
|
575
467
|
ClosedData,
|
|
576
468
|
ClosedRow,
|
|
469
|
+
ClosedSelectable,
|
|
577
470
|
ClosedSelectionInput,
|
|
578
471
|
PayloadField,
|
|
579
472
|
SelectedClosed
|
package/src/db.ts
CHANGED
|
@@ -35,7 +35,17 @@ import * as errors from "@superbuilders/errors"
|
|
|
35
35
|
import type { Exhumed } from "#exhume.ts"
|
|
36
36
|
import { exhumeStore } from "#exhume.ts"
|
|
37
37
|
import { lower } from "#lower.ts"
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
factOf,
|
|
40
|
+
handleOf,
|
|
41
|
+
isFreshField,
|
|
42
|
+
isMintedFresh,
|
|
43
|
+
type KeyFact,
|
|
44
|
+
keyRowOf,
|
|
45
|
+
type Minted,
|
|
46
|
+
recordOf,
|
|
47
|
+
rowOf
|
|
48
|
+
} from "#marshal.ts"
|
|
39
49
|
|
|
40
50
|
import type {
|
|
41
51
|
DbHandle,
|
|
@@ -55,7 +65,7 @@ import type { Query } from "#query/lower.ts"
|
|
|
55
65
|
import { lowerQuery } from "#query/lower.ts"
|
|
56
66
|
import { decodeAnswers, wireParams } from "#query/run.ts"
|
|
57
67
|
import type { ParamEntry, ParamsRecord } from "#query/scope.ts"
|
|
58
|
-
import type { AnyRelation, Fact, InsertFact } from "#relation.ts"
|
|
68
|
+
import type { AnyRelation, Fact, InsertFact, RelationField } from "#relation.ts"
|
|
59
69
|
import type { AnySchema, Schema, SchemaRelation, SchemaRelations } from "#schema.ts"
|
|
60
70
|
import type { KeyStatement, Statement } from "#statements.ts"
|
|
61
71
|
|
|
@@ -79,7 +89,10 @@ type DeclaredKeyFact<R extends AnyRelation, Projection extends readonly string[]
|
|
|
79
89
|
/**
|
|
80
90
|
* One offending fact of a violation: the cited relation's name (a member
|
|
81
91
|
* of the schema's record) and the fact decoded to a named natural-value
|
|
82
|
-
* object — partial exactly as the engine cites it.
|
|
92
|
+
* object — partial exactly as the engine cites it. Closed-referencing
|
|
93
|
+
* cells arrive as handle NAMES (the marshal bijection's read half), so the
|
|
94
|
+
* record and the violation's `canonical` string — which the engine already
|
|
95
|
+
* renders with handle names — agree on the one spelling.
|
|
83
96
|
*/
|
|
84
97
|
interface OffendingFact<Rels extends SchemaRelations> {
|
|
85
98
|
readonly relation: keyof Rels & string
|
|
@@ -525,6 +538,22 @@ function orientationOf(reversed: boolean | undefined): "written" | "mirrored" |
|
|
|
525
538
|
return "written"
|
|
526
539
|
}
|
|
527
540
|
|
|
541
|
+
/**
|
|
542
|
+
* The declared field descriptors a violation's offending cells decode
|
|
543
|
+
* through: an ordinary relation's declared fields; a closed relation's
|
|
544
|
+
* SEALED shape — the roster-carrying synthetic `id` (the member's own
|
|
545
|
+
* reference descriptor) plus its payload columns (a `ClosedColumn` is
|
|
546
|
+
* structurally a `RelationField`). This is how {@link openDb}'s
|
|
547
|
+
* `offendingFactOf` reaches the open-time descriptors: the cited relation
|
|
548
|
+
* name resolves to the schema member, and the member carries them.
|
|
549
|
+
*/
|
|
550
|
+
function declaredFieldsOf(member: SchemaRelation): readonly RelationField[] {
|
|
551
|
+
if ("axioms" in member) {
|
|
552
|
+
return [{ name: "id", field: member.id }, ...member.data.columns]
|
|
553
|
+
}
|
|
554
|
+
return member.data.fields
|
|
555
|
+
}
|
|
556
|
+
|
|
528
557
|
/** The id-resolution tables one open builds: relation entries by name, statement slots by id. */
|
|
529
558
|
interface Tables {
|
|
530
559
|
readonly relations: ReadonlyMap<string, RelationEntry>
|
|
@@ -705,12 +734,20 @@ function openDb<Rels extends SchemaRelations>(handle: DbHandle, theory: Schema<R
|
|
|
705
734
|
}
|
|
706
735
|
|
|
707
736
|
function offendingFactOf(fact: WireViolationFact): OffendingFact<Rels> {
|
|
708
|
-
|
|
737
|
+
const entry = tables.relations.get(fact.relation)
|
|
738
|
+
if (entry === undefined || !isMemberName(fact.relation)) {
|
|
709
739
|
throw errors.new(`bumbledb violation cites unknown relation ${fact.relation}`)
|
|
710
740
|
}
|
|
741
|
+
const declared = declaredFieldsOf(entry.member)
|
|
711
742
|
const decoded: Record<string, FactValue> = {}
|
|
712
743
|
for (const cell of fact.fields) {
|
|
713
|
-
|
|
744
|
+
const cited = declared.find(function byName(candidate) {
|
|
745
|
+
return candidate.name === cell.name
|
|
746
|
+
})
|
|
747
|
+
decoded[cell.name] =
|
|
748
|
+
cited !== undefined && "closed" in cited.field
|
|
749
|
+
? handleOf(`violation fact ${fact.relation} field ${cell.name}`, cited.field.closed, cell.value)
|
|
750
|
+
: cell.value
|
|
714
751
|
}
|
|
715
752
|
return Object.freeze({ relation: fact.relation, fact: Object.freeze(decoded) })
|
|
716
753
|
}
|