@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/query/parse-ir.ts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Host shape parse for the wire `QueryIr`: CQ/Reach eliminator, rec/main
|
|
3
|
-
* nonempty, aggregate finds split (Count has no `over`; folds require
|
|
4
|
-
* it), head/find alignment. The engine validator remains the one roster
|
|
5
|
-
* authority — this parse refuses only shape the host type can see.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import * as errors from "@superbuilders/errors"
|
|
9
2
|
import type { FindTermIr, HeadTermIr, ParsedQuery, QueryIr, RuleIr } from "#native.ts"
|
|
10
3
|
|
|
11
|
-
/** Brands a shape-checked wire query so {@link Native.dbPrepare} will accept it. */
|
|
12
4
|
function parseQueryIr(ir: QueryIr): ParsedQuery {
|
|
13
5
|
if (ir.rules.length === 0) {
|
|
14
6
|
throw errors.new("parseQueryIr: main rules are empty")
|
|
@@ -30,7 +22,6 @@ function parseQueryIr(ir: QueryIr): ParsedQuery {
|
|
|
30
22
|
return ir as ParsedQuery
|
|
31
23
|
}
|
|
32
24
|
|
|
33
|
-
/** One rule list must share the head's width and var/aggregate family. */
|
|
34
25
|
function align(context: string, head: readonly HeadTermIr[], rules: readonly RuleIr[]): void {
|
|
35
26
|
for (const [ruleIndex, rule] of rules.entries()) {
|
|
36
27
|
if (rule.finds.length !== head.length) {
|
|
@@ -50,7 +41,6 @@ function align(context: string, head: readonly HeadTermIr[], rules: readonly Rul
|
|
|
50
41
|
}
|
|
51
42
|
}
|
|
52
43
|
|
|
53
|
-
/** Count is nullary; pack and folds require `over`. */
|
|
54
44
|
function parseFind(context: string, find: FindTermIr): void {
|
|
55
45
|
const raw = find as Record<string, unknown>
|
|
56
46
|
if (find.kind === "count") {
|
|
@@ -59,23 +49,20 @@ function parseFind(context: string, find: FindTermIr): void {
|
|
|
59
49
|
}
|
|
60
50
|
return
|
|
61
51
|
}
|
|
62
|
-
if (find.kind === "pack" || find.kind === "aggregate"
|
|
52
|
+
if (find.kind === "pack" || find.kind === "aggregate") {
|
|
63
53
|
if (!("over" in raw)) {
|
|
64
54
|
throw errors.new(`${context}: ${find.kind} requires over`)
|
|
65
55
|
}
|
|
66
56
|
}
|
|
67
57
|
}
|
|
68
58
|
|
|
69
|
-
/** Head family of one find term: measure is a var slot; count/pack/folds are aggregates. */
|
|
70
59
|
function findFamily(find: FindTermIr): "var" | "aggregate" {
|
|
71
60
|
switch (find.kind) {
|
|
72
61
|
case "var":
|
|
73
|
-
case "measure":
|
|
74
62
|
return "var"
|
|
75
63
|
case "count":
|
|
76
64
|
case "pack":
|
|
77
65
|
case "aggregate":
|
|
78
|
-
case "aggregateMeasure":
|
|
79
66
|
return "aggregate"
|
|
80
67
|
}
|
|
81
68
|
}
|
package/src/query/run.ts
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Prepared-query marshaling seams, the two rides every execution takes —
|
|
3
|
-
* the typed params object down to the bridge's positional `QueryParam[]`
|
|
4
|
-
* (registry order = dense `ParamId`s, values tagged by each param's
|
|
5
|
-
* ANCHORING use: the field position or comparison sibling that typed it,
|
|
6
|
-
* op-aware exactly as comparison literals tag), and answer rows
|
|
7
|
-
* (positional, head order) back up to plain objects of BARE structural
|
|
8
|
-
* values — the marshal boundary is pure both ways: the engine computed
|
|
9
|
-
* the answer under the prepared head, so a decoded row that carries every
|
|
10
|
-
* select column IS a row (the trusted read seam), and nothing is asserted
|
|
11
|
-
* on any value. A CLOSED answer column decodes id → handle NAME through
|
|
12
|
-
* the marshal's one bijection (`handleOf` — the same read half every fact
|
|
13
|
-
* decode rides; the column's roster rides `FindColumn.closed`), so query
|
|
14
|
-
* rows speak the vocabulary exactly as scans and gets do. Answers are
|
|
15
|
-
* SETS — no order or limit exists anywhere; hosts sort. The `Prepared`
|
|
16
|
-
* VALUE itself (no lifecycle, GC-reclaimed plan) lives in `#db.ts`.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
1
|
import * as errors from "@superbuilders/errors"
|
|
20
2
|
import { handleOf } from "#marshal.ts"
|
|
21
3
|
import type { FactValue, QueryParam, TaggedValue } from "#native.ts"
|
|
@@ -23,7 +5,6 @@ import type { FindColumn } from "#query/atom.ts"
|
|
|
23
5
|
import { taggedCmpLiteral } from "#query/lower.ts"
|
|
24
6
|
import type { ParamEntry } from "#query/scope.ts"
|
|
25
7
|
|
|
26
|
-
/** Tags one supplied value-param cell by its anchoring use. */
|
|
27
8
|
function wireValue(entry: ParamEntry, context: string, value: unknown): TaggedValue {
|
|
28
9
|
if (entry.anchor === undefined) {
|
|
29
10
|
throw errors.new(
|
|
@@ -33,18 +14,6 @@ function wireValue(entry: ParamEntry, context: string, value: unknown): TaggedVa
|
|
|
33
14
|
return taggedCmpLiteral(context, entry.anchor, value, entry.op)
|
|
34
15
|
}
|
|
35
16
|
|
|
36
|
-
/**
|
|
37
|
-
* Marshals the typed params object to the bridge's positional arguments,
|
|
38
|
-
* in registry order (= the lowering's dense `ParamId`s). A missing entry
|
|
39
|
-
* is a typed error naming the param; values tag by the anchoring use's
|
|
40
|
-
* structural type; a set param takes a readonly array (the empty set is
|
|
41
|
-
* legal and matches nothing — the engine's rule). A MEMBERSHIP-ARRAY
|
|
42
|
-
* entry (`membership` present — a literal set folded into the query) is
|
|
43
|
-
* a program constant the registry already resolved through the one
|
|
44
|
-
* roster-verification point at BUILD time: it crosses as its prebuilt
|
|
45
|
-
* frozen `{ kind: "set", values }` by reference — the host's params object
|
|
46
|
-
* is never consulted for it, and no per-execute work exists.
|
|
47
|
-
*/
|
|
48
17
|
function wireParams(entries: readonly ParamEntry[], supplied: Readonly<Record<string, unknown>>): QueryParam[] {
|
|
49
18
|
return entries.map(function wireOne(entry): QueryParam {
|
|
50
19
|
if (entry.membership !== undefined) {
|
|
@@ -69,13 +38,6 @@ function wireParams(entries: readonly ParamEntry[], supplied: Readonly<Record<st
|
|
|
69
38
|
})
|
|
70
39
|
}
|
|
71
40
|
|
|
72
|
-
/**
|
|
73
|
-
* The read-side trusted seam of answers: a decoded row carrying every
|
|
74
|
-
* select column IS a `Row` — the engine computed it under the prepared
|
|
75
|
-
* head, and the values are BARE structural values, so nothing is asserted
|
|
76
|
-
* beyond presence (the store is the proof carrier; no brand exists to
|
|
77
|
-
* re-derive).
|
|
78
|
-
*/
|
|
79
41
|
function isAnswerRow<Row>(
|
|
80
42
|
finds: readonly FindColumn[],
|
|
81
43
|
decoded: Readonly<Record<string, FactValue>>
|
|
@@ -85,13 +47,6 @@ function isAnswerRow<Row>(
|
|
|
85
47
|
})
|
|
86
48
|
}
|
|
87
49
|
|
|
88
|
-
/**
|
|
89
|
-
* Decodes positional answer rows (column order = the query's head order
|
|
90
|
-
* = the select's written order) to named, frozen row objects of bare
|
|
91
|
-
* structural values. A closed column lifts its row id back to the handle
|
|
92
|
-
* NAME through the marshal's bijection — an out-of-roster id is the same
|
|
93
|
-
* pointed throw a fact decode gives, never a silent fallback.
|
|
94
|
-
*/
|
|
95
50
|
function decodeAnswers<Row>(finds: readonly FindColumn[], rows: FactValue[][]): Row[] {
|
|
96
51
|
return rows.map(function decodeRow(row) {
|
|
97
52
|
if (row.length !== finds.length) {
|
package/src/query/scope.ts
CHANGED
|
@@ -1,35 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Query scope terms, REFERENCE-IDENTITY edition: a query variable is an
|
|
3
|
-
* OBJECT, minted fresh by {@link v} over a relation's statically-known
|
|
4
|
-
* columns. `v(relation)` returns a record of fresh variables — one per
|
|
5
|
-
* column, each typed at mint by its column's descriptor AND the mint
|
|
6
|
-
* coordinate (the owner relation name and the column name), so
|
|
7
|
-
* destructuring preserves every literal and every class
|
|
8
|
-
* (`const { id, holder } = v(Account)`). Variable IDENTITY is the object
|
|
9
|
-
* reference: reusing the same var value across binding positions IS the
|
|
10
|
-
* join, and a name-collision join is unrepresentable (two `v()` calls mint
|
|
11
|
-
* two distinct batches, so two same-named vars are two variables). Params
|
|
12
|
-
* stay STRING-named — their names are the execute() params object's runtime
|
|
13
|
-
* keys, an honest load-bearing channel, not a lie.
|
|
14
|
-
*
|
|
15
|
-
* THE DESIGN THEOREM. {@link JoinOk} is an EQUALITY (kind, class, width,
|
|
16
|
-
* element, roster), so judging every binding position against the
|
|
17
|
-
* variable's MINT slot ({@link MintSlotOf}) makes all cross-binding joins
|
|
18
|
-
* mutually class-equal by transitivity — the env/sibling checks the
|
|
19
|
-
* name-keyed edition needed are subsumed, deleted rather than ported. The
|
|
20
|
-
* one check representation cannot carry is BOUNDNESS (is this var positively
|
|
21
|
-
* bound in this rule): TypeScript types cannot see object identity, so
|
|
22
|
-
* boundness moves from the type tier to construction-time walls only — an
|
|
23
|
-
* explicit essential-vs-accidental concession; every runtime twin is
|
|
24
|
-
* preserved.
|
|
25
|
-
*
|
|
26
|
-
* This module also owns the environment/typing utilities the whole surface
|
|
27
|
-
* shares: the join descriptor {@link ClassedField}, the mint-slot machinery
|
|
28
|
-
* ({@link MintSlotOf}/{@link MintClassOf}), the class-equality judgment
|
|
29
|
-
* {@link JoinOk} with its runtime twin {@link fieldJoins}, and the
|
|
30
|
-
* record-folding helpers `Params` and `Row` inference ride.
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
1
|
import * as errors from "@superbuilders/errors"
|
|
34
2
|
import type { AnyClosed } from "#closed.ts"
|
|
35
3
|
import { sealedFieldsOf } from "#closed.ts"
|
|
@@ -39,52 +7,18 @@ import type { ClassLookup, ClassRecordOf, SchemaClasses } from "#law.ts"
|
|
|
39
7
|
import type { QueryParam } from "#native.ts"
|
|
40
8
|
import type { AnyRelation, RelationFields } from "#relation.ts"
|
|
41
9
|
|
|
42
|
-
/**
|
|
43
|
-
* The runtime discriminant of query term values. Host literals (bigints,
|
|
44
|
-
* strings, interval objects) never carry it, so "is this position a term
|
|
45
|
-
* or a literal" is one property probe, never a guess.
|
|
46
|
-
*/
|
|
47
10
|
const term: unique symbol = Symbol("bumbledb.query.term")
|
|
48
11
|
|
|
49
|
-
/**
|
|
50
|
-
* The carrier of a value's INFERRED types (a rule's row/params, a query's
|
|
51
|
-
* row/params, a rec's params). The property is never present at runtime —
|
|
52
|
-
* it exists so inference rides plain values without any brand on any
|
|
53
|
-
* field value.
|
|
54
|
-
*/
|
|
55
12
|
const inferred: unique symbol = Symbol("bumbledb.query.inferred")
|
|
56
13
|
|
|
57
|
-
/**
|
|
58
|
-
* What a query atom matches over: an ordinary relation or a CLOSED
|
|
59
|
-
* vocabulary (ψ query atoms — the engine folds a resolvable closed atom
|
|
60
|
-
* into a plan-constant member set at prepare, or joins the L1-resident
|
|
61
|
-
* virtual image when the shape does not fold; the SDK never pre-folds and
|
|
62
|
-
* never knows which — transparency is the contract).
|
|
63
|
-
*/
|
|
64
14
|
type MatchOwner = AnyRelation | AnyClosed
|
|
65
15
|
|
|
66
|
-
/**
|
|
67
|
-
* The matchable field block of an atom owner: a relation's declared
|
|
68
|
-
* fields; a closed relation's SEALED shape — the synthetic `id` (the
|
|
69
|
-
* value's OWN roster-carrying descriptor, at its precise type) first, then
|
|
70
|
-
* the declared payload columns read through the typed `columns` carrier.
|
|
71
|
-
* The runtime twin is `sealedFieldsOf` in `#closed.ts`.
|
|
72
|
-
*/
|
|
73
16
|
type MatchFields<R extends MatchOwner> = R extends AnyClosed
|
|
74
17
|
? { readonly id: R["id"] } & R["columns"]
|
|
75
18
|
: R extends AnyRelation
|
|
76
19
|
? RelationFields<R>
|
|
77
20
|
: never
|
|
78
21
|
|
|
79
|
-
/**
|
|
80
|
-
* A query variable — an OBJECT minted by {@link v}. Identity is the object
|
|
81
|
-
* reference: reuse of the same value across binding positions is the join,
|
|
82
|
-
* strictly rule-scoped (each rule numbers its own dense `VarId`s). The type
|
|
83
|
-
* carries the mint COORDINATE — `RN` the owner relation name literal, `K`
|
|
84
|
-
* the column name literal — and `F` the mint descriptor, so the mint slot
|
|
85
|
-
* (descriptor + law-computed class) is recoverable at every binding
|
|
86
|
-
* position for the join judgment.
|
|
87
|
-
*/
|
|
88
22
|
interface Var<F extends AnyField = AnyField, RN extends string = string, K extends string = string> {
|
|
89
23
|
readonly [term]: "var"
|
|
90
24
|
readonly owner: MatchOwner & { readonly name: RN }
|
|
@@ -93,60 +27,48 @@ interface Var<F extends AnyField = AnyField, RN extends string = string, K exten
|
|
|
93
27
|
readonly label: string
|
|
94
28
|
}
|
|
95
29
|
|
|
96
|
-
/** Any query variable, whatever its descriptor and mint coordinate. */
|
|
97
30
|
type AnyVar = Var
|
|
98
31
|
|
|
99
|
-
/**
|
|
100
|
-
* A scalar query parameter — `r.param("root")`. The name is the key of the
|
|
101
|
-
* typed params object `execute` takes; the type is the element type of the
|
|
102
|
-
* position that anchors it (a field binding, or the bound-variable side of
|
|
103
|
-
* a comparison).
|
|
104
|
-
*/
|
|
105
32
|
interface Param<Name extends string = string> {
|
|
106
33
|
readonly [term]: "param"
|
|
107
34
|
readonly name: Name
|
|
108
35
|
}
|
|
109
36
|
|
|
110
|
-
/**
|
|
111
|
-
* A set-valued query parameter (the IR's `ParamSet` term) — `r.inSet("frontier")`:
|
|
112
|
-
* bound at execution to a readonly ARRAY of values of the anchoring field's
|
|
113
|
-
* type; a binding position matches iff the field value is in the set. Legal
|
|
114
|
-
* in atom bindings (positive and negated) and as the right side of `eq` —
|
|
115
|
-
* nowhere else, exactly as the IR rules it.
|
|
116
|
-
*/
|
|
117
37
|
interface SetParam<Name extends string = string> {
|
|
118
38
|
readonly [term]: "setParam"
|
|
119
39
|
readonly name: Name
|
|
120
40
|
}
|
|
121
41
|
|
|
122
|
-
|
|
123
|
-
* The measure of an interval-typed variable (`ir::Term::Measure`):
|
|
124
|
-
* `|[s, e)| = e − s`, u64 — legal as one side of an order comparison, as a
|
|
125
|
-
* find entry, and as the input of `sum`/`min`/`max`; every other position
|
|
126
|
-
* is unwritable, exactly as the IR rejects it typed. Carries the interval
|
|
127
|
-
* variable it measures BY REFERENCE.
|
|
128
|
-
*/
|
|
129
|
-
interface Duration<V extends AnyVar = AnyVar> {
|
|
130
|
-
readonly [term]: "duration"
|
|
131
|
-
readonly over: V
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Any scope term value. */
|
|
135
|
-
type AnyTerm = Var | Param | SetParam | Duration
|
|
42
|
+
type AnyTerm = Var | Param | SetParam
|
|
136
43
|
|
|
137
|
-
/** Narrows an unknown position value to a scope term (vs a host literal). */
|
|
138
44
|
function isTerm(value: unknown): value is AnyTerm {
|
|
139
45
|
return typeof value === "object" && value !== null && term in value
|
|
140
46
|
}
|
|
141
47
|
|
|
142
|
-
/**
|
|
143
|
-
* The record of fresh variables `v(owner)` mints — one per statically-known
|
|
144
|
-
* column, each typed by its column's descriptor and mint coordinate.
|
|
145
|
-
*/
|
|
146
48
|
type VarsOf<R extends MatchOwner> = {
|
|
147
49
|
readonly [K in keyof MatchFields<R> & string]: Var<MatchFields<R>[K], R["name"], K>
|
|
148
50
|
}
|
|
149
51
|
|
|
52
|
+
/**
|
|
53
|
+
* The exactness judgment of the six full-binding `match` forms (`#query/
|
|
54
|
+
* lower.ts` intersects it into the `bindings` parameter): a mapped type
|
|
55
|
+
* over the INFERRED record `B` requiring every entry to be a variable
|
|
56
|
+
* whose mint COLUMN is the entry's own key restricted to `R`'s matchable
|
|
57
|
+
* fields — for a column of `R` that is just the {@link VarsOf} entry's own
|
|
58
|
+
* shape, and for a FOREIGN key the column type is `never`, which no
|
|
59
|
+
* mintable variable inhabits. So an aliased or function-returned record
|
|
60
|
+
* carrying an extra key (`{...v(Account), extra: otherVar }` — shapes
|
|
61
|
+
* excess-property checking never sees, it covers inline literals only)
|
|
62
|
+
* fails the intersection and falls to the general form's judgment: the
|
|
63
|
+
* pre-0.16.0 compile refusal, restored. The identity record `v(rel)` still
|
|
64
|
+
* unifies for GENERIC `R` — the judgment is intersections and index
|
|
65
|
+
* constraints only, no deferred conditional anywhere, so the full-binding
|
|
66
|
+
* law (50-generic-binding.md, "The ruling") stands untouched.
|
|
67
|
+
*/
|
|
68
|
+
type ExactVars<R extends MatchOwner, B> = {
|
|
69
|
+
readonly [K in keyof B]: Var<AnyField, R["name"], K & keyof MatchFields<R> & string>
|
|
70
|
+
}
|
|
71
|
+
|
|
150
72
|
/**
|
|
151
73
|
* The trusted admission seam of the variable-record mint (the pattern's
|
|
152
74
|
* home is `isTypedScope` in `#query/lower.ts`): the checkable fact — one own
|
|
@@ -164,7 +86,7 @@ function varsMinted<R extends MatchOwner>(owner: R, record: Readonly<Record<stri
|
|
|
164
86
|
* statically-known columns — one variable per sealed column
|
|
165
87
|
* (`sealedFieldsOf`: a closed owner mints `id` first, then payload columns),
|
|
166
88
|
* each frozen and each defined by OWN-property definition (object-protocol
|
|
167
|
-
* column names must work, the `closed
|
|
89
|
+
* column names must work, the `closed` precedent). Every `v` call mints
|
|
168
90
|
* new objects, so two batches are two variables; property access within one
|
|
169
91
|
* batch is stable by construction (the record is an eager frozen record,
|
|
170
92
|
* never a Proxy). Variable identity is the object reference: destructure
|
|
@@ -190,102 +112,51 @@ function v<R extends MatchOwner>(owner: R): VarsOf<R> {
|
|
|
190
112
|
return record
|
|
191
113
|
}
|
|
192
114
|
|
|
193
|
-
/** Builds one scalar-parameter term. */
|
|
194
115
|
function makeParam<const Name extends string>(name: Name): Param<Name> {
|
|
195
116
|
const value: Param<Name> = { [term]: "param", name }
|
|
196
117
|
return Object.freeze(value)
|
|
197
118
|
}
|
|
198
119
|
|
|
199
|
-
/** Builds one set-parameter term. */
|
|
200
120
|
function makeSetParam<const Name extends string>(name: Name): SetParam<Name> {
|
|
201
121
|
const value: SetParam<Name> = { [term]: "setParam", name }
|
|
202
122
|
return Object.freeze(value)
|
|
203
123
|
}
|
|
204
124
|
|
|
205
|
-
/** Builds one measure term over an interval-typed variable reference. */
|
|
206
|
-
function makeDuration<const V extends AnyVar>(over: V): Duration<V> {
|
|
207
|
-
const value: Duration<V> = { [term]: "duration", over }
|
|
208
|
-
return Object.freeze(value)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* One bound field slot: the field's descriptor plus the slot's
|
|
213
|
-
* law-computed CLASS (`undefined` = bare — the slot is in no law). The one
|
|
214
|
-
* shape every join judgment compares, at the TYPE level (a variable's mint
|
|
215
|
-
* slot, a binding position's slot) and at RUNTIME alike.
|
|
216
|
-
*/
|
|
217
125
|
interface ClassedField {
|
|
218
126
|
readonly field: AnyField
|
|
219
127
|
readonly class: string | undefined
|
|
220
128
|
}
|
|
221
129
|
|
|
222
|
-
/**
|
|
223
|
-
* A variable's law-computed CLASS at the TYPE level: its column's class,
|
|
224
|
-
* read off the schema type's class map through the mint coordinate the
|
|
225
|
-
* variable carries (`RN.K`). `undefined` = bare.
|
|
226
|
-
*/
|
|
227
130
|
type MintClassOf<Classes extends SchemaClasses, V> =
|
|
228
131
|
V extends Var<AnyField, infer RN extends string, infer K extends string>
|
|
229
132
|
? ClassLookup<ClassRecordOf<Classes, RN>, K>
|
|
230
133
|
: never
|
|
231
134
|
|
|
232
|
-
/**
|
|
233
|
-
* A variable's MINT slot: the descriptor it was minted at plus its
|
|
234
|
-
* law-computed class. The one slot every binding position judges against —
|
|
235
|
-
* because {@link JoinOk} is an equality, judging each position against the
|
|
236
|
-
* mint slot makes every cross-binding join transitively class-equal.
|
|
237
|
-
*/
|
|
238
135
|
type MintSlotOf<Classes extends SchemaClasses, V extends AnyVar> = {
|
|
239
136
|
readonly field: V["field"]
|
|
240
137
|
readonly class: MintClassOf<Classes, V>
|
|
241
138
|
}
|
|
242
139
|
|
|
243
|
-
/** A params object type — what `execute` takes and inference carries. */
|
|
244
140
|
type ParamsRecord = Readonly<Record<string, unknown>>
|
|
245
141
|
|
|
246
|
-
/** Flattens an intersection into one displayed object type (hover legibility). */
|
|
247
142
|
type Flatten<T> = { [K in keyof T]: T[K] }
|
|
248
143
|
|
|
249
|
-
/** The standard union-to-intersection fold (distributes over `U`). */
|
|
250
144
|
type UnionToIntersection<U> = (U extends unknown ? (member: U) => void : never) extends (member: infer I) => void
|
|
251
145
|
? I
|
|
252
146
|
: never
|
|
253
147
|
|
|
254
|
-
/**
|
|
255
|
-
* Folds a union of per-position record fragments into one flattened record
|
|
256
|
-
* (the machinery both `Params` and `Row` inference ride).
|
|
257
|
-
*/
|
|
258
148
|
type ShapeOf<U> = [U] extends [never] ? Record<never, never> : Flatten<UnionToIntersection<U>>
|
|
259
149
|
|
|
260
|
-
/** Reads a field descriptor's width label (`bytes<N>`, `interval<E, W>`); `undefined` when the kind carries none. */
|
|
261
150
|
type WidthOf<F extends AnyField> = F extends { readonly width: infer W } ? W : undefined
|
|
262
151
|
|
|
263
|
-
/** Reads an interval descriptor's element kind; `undefined` on scalar kinds. */
|
|
264
152
|
type ElementOf<F extends AnyField> = F extends { readonly element: infer E } ? E : undefined
|
|
265
153
|
|
|
266
|
-
/**
|
|
267
|
-
* Reads a closed reference's vocabulary name literal paired with its handle
|
|
268
|
-
* union; `undefined` on every non-closed kind (the roster IS descriptor
|
|
269
|
-
* structure). The name literal is the type-tier encoding of the runtime's
|
|
270
|
-
* roster VALUE-IDENTITY judgment ({@link fieldJoins}): two same-shaped
|
|
271
|
-
* vocabularies are distinct rosters, so they mismatch here exactly as they
|
|
272
|
-
* refuse at runtime.
|
|
273
|
-
*/
|
|
274
154
|
type RosterOf<F extends AnyField> = F extends {
|
|
275
155
|
readonly closed: { readonly name: infer N extends string; readonly handles: readonly (infer H extends string)[] }
|
|
276
156
|
}
|
|
277
157
|
? readonly [N, H]
|
|
278
158
|
: undefined
|
|
279
159
|
|
|
280
|
-
/**
|
|
281
|
-
* The join judgment: two bound slots join iff their descriptors' structure
|
|
282
|
-
* agrees (kind, width label, interval element, and the closed ROSTER — a
|
|
283
|
-
* closed reference pairs only with the same vocabulary, never with a bare
|
|
284
|
-
* u64) AND their law-computed classes agree — same class name joins, and
|
|
285
|
-
* bare (`undefined`) pairs only with bare (ruling 3). The class names come
|
|
286
|
-
* off the SCHEMA type's class map; no descriptor label beyond the roster
|
|
287
|
-
* exists to compare.
|
|
288
|
-
*/
|
|
289
160
|
type JoinOk<A extends ClassedField, B extends ClassedField> = [
|
|
290
161
|
A["field"]["kind"],
|
|
291
162
|
A["class"],
|
|
@@ -304,14 +175,6 @@ type JoinOk<A extends ClassedField, B extends ClassedField> = [
|
|
|
304
175
|
: false
|
|
305
176
|
: false
|
|
306
177
|
|
|
307
|
-
/**
|
|
308
|
-
* The runtime twin of {@link JoinOk}: two bound slots join iff descriptor
|
|
309
|
-
* structure and class agree — the same comparison the type tier makes,
|
|
310
|
-
* judged on the honest runtime values (the descriptor, the roster by VALUE
|
|
311
|
-
* IDENTITY, and the schema value's frozen class map). The rule builders
|
|
312
|
-
* throw through this on a class-unequal reuse, so the wall holds for untyped
|
|
313
|
-
* callers too.
|
|
314
|
-
*/
|
|
315
178
|
function fieldJoins(a: ClassedField, b: ClassedField): boolean {
|
|
316
179
|
const widthA = "width" in a.field ? a.field.width : undefined
|
|
317
180
|
const widthB = "width" in b.field ? b.field.width : undefined
|
|
@@ -328,12 +191,6 @@ function fieldJoins(a: ClassedField, b: ClassedField): boolean {
|
|
|
328
191
|
)
|
|
329
192
|
}
|
|
330
193
|
|
|
331
|
-
/**
|
|
332
|
-
* Renders one bound slot for join-mismatch diagnostics — the structural
|
|
333
|
-
* kind in the schema grammar's spelling (a closed reference names its
|
|
334
|
-
* vocabulary) plus the slot's law-computed class (`u64 in class Holder.id`;
|
|
335
|
-
* a lawless slot renders `bare`).
|
|
336
|
-
*/
|
|
337
194
|
function renderFieldKind(slot: ClassedField): string {
|
|
338
195
|
const field = slot.field
|
|
339
196
|
let base: string = field.kind
|
|
@@ -350,32 +207,14 @@ function renderFieldKind(slot: ClassedField): string {
|
|
|
350
207
|
return slot.class === undefined ? `${base} (bare)` : `${base} in class ${slot.class}`
|
|
351
208
|
}
|
|
352
209
|
|
|
353
|
-
/**
|
|
354
|
-
* What a PARAM anchored at field `F` accepts at execution: the field's
|
|
355
|
-
* bare value type, exactly — at a CLOSED-reference field that is the
|
|
356
|
-
* handle-name union (`"DirectPass" | "Failed"`), translated name → row id
|
|
357
|
-
* at execute through the one roster-verification point (`taggedHandleId`).
|
|
358
|
-
*/
|
|
359
210
|
type ParamValueAt<F extends AnyField> = Infer<F>
|
|
360
211
|
|
|
361
|
-
/** Reads a value's inferred-types carrier (rules, queries, recs). */
|
|
362
212
|
type InferredOf<T> = T extends { readonly [inferred]?: infer S } ? Exclude<S, undefined> : never
|
|
363
213
|
|
|
364
|
-
/**
|
|
365
|
-
* One registered parameter of a query, as the wire marshal reads it: the
|
|
366
|
-
* name, the wire shape, the field descriptor (or the measure) that anchored
|
|
367
|
-
* it, and the comparison op the anchor came from (`"binding"` for atom
|
|
368
|
-
* positions). `anchor` is `undefined` only on a query built but not yet
|
|
369
|
-
* anchored by any rule. `membership` is present exactly on a
|
|
370
|
-
* MEMBERSHIP-ARRAY entry: the FROZEN wire param, already resolved through
|
|
371
|
-
* the one roster-verification point at BUILD time (the entry stores the
|
|
372
|
-
* image, never the pre-image — no per-execute re-translation exists, and an
|
|
373
|
-
* out-of-roster handle name fails where the mistake was made).
|
|
374
|
-
*/
|
|
375
214
|
interface ParamEntry {
|
|
376
215
|
readonly name: string
|
|
377
216
|
readonly shape: "value" | "set"
|
|
378
|
-
readonly anchor: AnyField |
|
|
217
|
+
readonly anchor: AnyField | undefined
|
|
379
218
|
readonly op: "binding" | "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "pointIn" | "allen"
|
|
380
219
|
readonly membership: QueryParam | undefined
|
|
381
220
|
}
|
|
@@ -384,7 +223,7 @@ export type {
|
|
|
384
223
|
AnyTerm,
|
|
385
224
|
AnyVar,
|
|
386
225
|
ClassedField,
|
|
387
|
-
|
|
226
|
+
ExactVars,
|
|
388
227
|
Flatten,
|
|
389
228
|
InferredOf,
|
|
390
229
|
JoinOk,
|
|
@@ -402,4 +241,4 @@ export type {
|
|
|
402
241
|
Var,
|
|
403
242
|
VarsOf
|
|
404
243
|
}
|
|
405
|
-
export { fieldJoins, inferred, isTerm,
|
|
244
|
+
export { fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term, v }
|
package/src/relation.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `relation
|
|
2
|
+
* `relation` — the ordinary-relation half of the theory's signature. A
|
|
3
3
|
* relation value is a frozen plain object carrying its name, its ordered
|
|
4
4
|
* field descriptors (declaration order = ordinal ids, the macro's law),
|
|
5
|
-
* and — since selections are the relation's own vocabulary — `where
|
|
5
|
+
* and — since selections are the relation's own vocabulary — `where`,
|
|
6
6
|
* which resolves a selection into lowered bindings eagerly (handles
|
|
7
7
|
* verified against their roster at construction). Fields are addressed by
|
|
8
8
|
* NAME everywhere — statements (`on(R, "holder")`), selections, and match
|
|
@@ -16,22 +16,6 @@ import * as errors from "@superbuilders/errors"
|
|
|
16
16
|
import { type AnyField, assertDeclarationOrderKey, assertDeclarationRecord, type Infer, literalOf } from "#fields.ts"
|
|
17
17
|
import { type LiteralSetSpec, type LiteralSpec, renderLiteral } from "#spec.ts"
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* Resolves one selection entry to its lowered literal set: a plain ARRAY
|
|
21
|
-
* (detected by `Array.isArray` — no field's value type is an array;
|
|
22
|
-
* `Uint8Array` is not one) becomes a disjunctive set, anything else the
|
|
23
|
-
* bare literal. The degenerate sets are construction errors, each
|
|
24
|
-
* self-locating (`context` names the relation and field) — the empty set
|
|
25
|
-
* selects nothing, the one-element set is the bare literal respelled, and
|
|
26
|
-
* a DUPLICATE literal (judged on the canonical rendering — the engine's
|
|
27
|
-
* own duplicate test, reached here first so its index-speak twin at
|
|
28
|
-
* `Db.create` stays unreachable from this surface) is the same respelling
|
|
29
|
-
* in disguise (the canonical-utterance law; the old set combinator's
|
|
30
|
-
* signature made the length degenerates unwritable, and the refusals here
|
|
31
|
-
* are that law's runtime seat). The lowered set — `{ kind: "many",
|
|
32
|
-
* literals }` — is byte-identical to what the combinator produced, so no
|
|
33
|
-
* fingerprint moves.
|
|
34
|
-
*/
|
|
35
19
|
function resolveEntry(context: string, field: AnyField, entry: unknown): LiteralSetSpec {
|
|
36
20
|
if (Array.isArray(entry)) {
|
|
37
21
|
if (entry.length < 2) {
|
|
@@ -58,15 +42,6 @@ function resolveEntry(context: string, field: AnyField, entry: unknown): Literal
|
|
|
58
42
|
return Object.freeze({ kind: "one", literal: Object.freeze(literalOf(field, entry)) })
|
|
59
43
|
}
|
|
60
44
|
|
|
61
|
-
/**
|
|
62
|
-
* Resolves a whole `where()` selection against the declared fields, in the
|
|
63
|
-
* selection's written order (macro parity: σ is spelled, not sorted). An
|
|
64
|
-
* empty selection is the bare relation respelled and rejected (the
|
|
65
|
-
* canonical-utterance law). THE one selection resolver — `closed()`'s
|
|
66
|
-
* `where()` resolves its payload columns through this same machine (a
|
|
67
|
-
* `ClosedColumn` is structurally a {@link RelationField}), so both surfaces
|
|
68
|
-
* share one vocabulary and one error voice.
|
|
69
|
-
*/
|
|
70
45
|
function resolveSelection(
|
|
71
46
|
name: string,
|
|
72
47
|
ordered: readonly RelationField[],
|
|
@@ -95,94 +70,55 @@ function resolveSelection(
|
|
|
95
70
|
return Object.freeze(bindings)
|
|
96
71
|
}
|
|
97
72
|
|
|
98
|
-
/** The field block of a relation: field name to field descriptor. */
|
|
99
73
|
type FieldsShape = Record<string, AnyField>
|
|
100
74
|
|
|
101
|
-
/** One declared field: name plus its descriptor, in declaration order. */
|
|
102
75
|
interface RelationField {
|
|
103
76
|
readonly name: string
|
|
104
77
|
readonly field: AnyField
|
|
105
78
|
}
|
|
106
79
|
|
|
107
|
-
/** A relation's runtime description. */
|
|
108
80
|
interface RelationData {
|
|
109
81
|
readonly name: string
|
|
110
82
|
readonly fields: readonly RelationField[]
|
|
111
83
|
}
|
|
112
84
|
|
|
113
|
-
/**
|
|
114
|
-
* One resolved σ binding: the field name and its lowered literal set —
|
|
115
|
-
* handles already resolved to names, values already tagged by structural
|
|
116
|
-
* type.
|
|
117
|
-
*/
|
|
118
85
|
interface SelectionBinding {
|
|
119
86
|
readonly field: string
|
|
120
87
|
readonly set: LiteralSetSpec
|
|
121
88
|
}
|
|
122
89
|
|
|
123
|
-
/**
|
|
124
|
-
* The `where()` argument: per field, a bare structural literal of that
|
|
125
|
-
* field's value type (a closed reference's literal IS its handle name —
|
|
126
|
-
* `"Savings"`, verified against the roster at construction), a plain
|
|
127
|
-
* ARRAY of such literals read disjunctively — `kind: ["Checking",
|
|
128
|
-
* "Savings"]` — or a `span(start, end)` interval literal. Membership is
|
|
129
|
-
* an array, never an operator (the drizzle law); equality-only by
|
|
130
|
-
* construction: no operator parameter exists anywhere.
|
|
131
|
-
*/
|
|
132
90
|
type SelectionInput<Fields extends FieldsShape> = {
|
|
133
91
|
readonly [K in keyof Fields]?: Infer<Fields[K]> | readonly Infer<Fields[K]>[]
|
|
134
92
|
}
|
|
135
93
|
|
|
136
|
-
/** A relation with a selection applied — what `on()` consumes as a σ-carrying source. */
|
|
137
94
|
interface Selected<Name extends string, Fields extends FieldsShape> {
|
|
138
95
|
readonly relation: Relation<Name, Fields>
|
|
139
96
|
readonly selection: readonly SelectionBinding[]
|
|
140
97
|
}
|
|
141
98
|
|
|
142
|
-
/** A relation value. */
|
|
143
99
|
interface Relation<Name extends string, Fields extends FieldsShape> {
|
|
144
100
|
readonly name: Name
|
|
145
101
|
readonly data: RelationData
|
|
146
102
|
where(selection: SelectionInput<Fields>): Selected<Name, Fields>
|
|
147
103
|
}
|
|
148
104
|
|
|
149
|
-
/** Any relation value, whatever its name and field block. */
|
|
150
105
|
type AnyRelation = Relation<string, FieldsShape>
|
|
151
106
|
|
|
152
|
-
/** Any selected relation value. */
|
|
153
107
|
interface AnySelected {
|
|
154
108
|
readonly relation: AnyRelation
|
|
155
109
|
readonly selection: readonly SelectionBinding[]
|
|
156
110
|
}
|
|
157
111
|
|
|
158
|
-
/** Extracts a relation's field block type. */
|
|
159
112
|
type RelationFields<R extends AnyRelation> = R extends Relation<string, infer F extends FieldsShape> ? F : never
|
|
160
113
|
|
|
161
|
-
/**
|
|
162
|
-
* The inferred row object type of a relation as READ: every field present,
|
|
163
|
-
* at its BARE structural value type ({@link Infer}). Closed relations have
|
|
164
|
-
* no `Fact` — they are unwritable, and the type constraint refuses them
|
|
165
|
-
* because a closed value lacks the relation shape.
|
|
166
|
-
*/
|
|
167
114
|
type Fact<R extends AnyRelation> = {
|
|
168
115
|
[K in keyof RelationFields<R>]: Infer<RelationFields<R>[K]>
|
|
169
116
|
}
|
|
170
117
|
|
|
171
|
-
/** The field names of `R` whose descriptor type carries the fresh mint mark. */
|
|
172
118
|
type FreshKeys<R extends AnyRelation> = {
|
|
173
119
|
[K in keyof RelationFields<R>]: RelationFields<R>[K] extends { readonly fresh: true } ? K : never
|
|
174
120
|
}[keyof RelationFields<R>]
|
|
175
121
|
|
|
176
|
-
/**
|
|
177
|
-
* Declares one relation: `relation("Account", { id: u64.fresh,
|
|
178
|
-
* holder: u64, kind: Kind.id, ... })` — every field is a pure-structure
|
|
179
|
-
* descriptor (the constructor values themselves; domains are never
|
|
180
|
-
* declared: `schema()` computes them from the statements). Field
|
|
181
|
-
* declaration order is ordinal-id order (macro parity), carried at BOTH
|
|
182
|
-
* levels: the type level by the fields object, the value level by the
|
|
183
|
-
* frozen `data.fields` list — the law the schema-level class naming leans
|
|
184
|
-
* on. The returned value is frozen and side-effect free.
|
|
185
|
-
*/
|
|
186
122
|
function relation<const Name extends string, Fields extends FieldsShape>(
|
|
187
123
|
name: Name,
|
|
188
124
|
fields: Fields
|