@bjornpagen/bumbledb 0.2.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 +503 -427
- package/README.md +84 -36
- package/dist/closed.d.ts +111 -38
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +94 -99
- package/dist/closed.js.map +1 -1
- package/dist/db.d.ts +16 -2
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +46 -8
- package/dist/db.js.map +1 -1
- package/dist/face.d.ts +114 -69
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +38 -21
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +72 -87
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +35 -67
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +18 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/law.d.ts +224 -0
- package/dist/law.d.ts.map +1 -0
- package/dist/law.js +224 -0
- package/dist/law.js.map +1 -0
- package/dist/lower.d.ts +17 -10
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +34 -23
- package/dist/lower.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/native.d.ts +6 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +139 -56
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +7 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/lower.d.ts +71 -56
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +341 -69
- package/dist/query/lower.js.map +1 -1
- package/dist/query/predicate.d.ts +10 -9
- package/dist/query/predicate.d.ts.map +1 -1
- package/dist/query/predicate.js +2 -2
- package/dist/query/predicate.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 +100 -43
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +89 -30
- package/dist/query/scope.js.map +1 -1
- package/dist/query/select.d.ts +5 -5
- package/dist/query/select.d.ts.map +1 -1
- package/dist/relation.d.ts +29 -15
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +45 -17
- package/dist/relation.js.map +1 -1
- package/dist/schema.d.ts +41 -3
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +16 -2
- package/dist/schema.js.map +1 -1
- package/dist/spec.d.ts +10 -8
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +67 -31
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +97 -20
- package/dist/statements.js.map +1 -1
- package/package.json +2 -2
- package/src/closed.ts +214 -146
- package/src/db.ts +65 -10
- package/src/face.ts +169 -102
- package/src/fields.ts +97 -164
- package/src/index.ts +43 -18
- package/src/law.ts +519 -0
- package/src/lower.ts +36 -23
- package/src/marshal.ts +74 -7
- package/src/native.ts +6 -2
- package/src/query/atom.ts +155 -65
- package/src/query/lower.ts +572 -167
- package/src/query/predicate.ts +43 -33
- package/src/query/run.ts +26 -6
- package/src/query/scope.ts +161 -51
- package/src/query/select.ts +5 -5
- package/src/relation.ts +60 -26
- package/src/schema.ts +48 -7
- package/src/spec.ts +10 -8
- package/src/statements.ts +165 -46
package/src/query/predicate.ts
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import * as errors from "@superbuilders/errors"
|
|
31
|
+
import type { SchemaClasses } from "#law.ts"
|
|
31
32
|
import type { RecData } from "#query/atom.ts"
|
|
32
33
|
import type {
|
|
33
34
|
AnyRuleValue,
|
|
@@ -61,42 +62,47 @@ interface Rec<
|
|
|
61
62
|
Rels extends SchemaRelations,
|
|
62
63
|
Name extends string,
|
|
63
64
|
P extends ParamsRecord,
|
|
64
|
-
Head extends HeadShape = undefined
|
|
65
|
+
Head extends HeadShape = undefined,
|
|
66
|
+
Classes extends SchemaClasses = SchemaClasses
|
|
65
67
|
> extends RecRef<Name, P, Head> {
|
|
66
68
|
rule<RV extends AnyRuleValue>(
|
|
67
|
-
build: (r: RecRuleScope<Rels, Name>) => RV
|
|
68
|
-
): Rec<Rels, Name, Flatten<P & ParamsOf<RV>>, Head extends undefined ? HeadOf<RV> : Head>
|
|
69
|
+
build: (r: RecRuleScope<Rels, Name, Classes>) => RV
|
|
70
|
+
): Rec<Rels, Name, Flatten<P & ParamsOf<RV>>, Head extends undefined ? HeadOf<RV> : Head, Classes>
|
|
69
71
|
readonly [inferred]?: { readonly params: P; readonly head: Head }
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
/** One output-rule builder function. */
|
|
73
|
-
type OutputBuild<Rels extends SchemaRelations
|
|
75
|
+
type OutputBuild<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> = (
|
|
76
|
+
r: OutputRuleScope<Rels, Classes>
|
|
77
|
+
) => AnyRuleValue
|
|
74
78
|
|
|
75
79
|
/** A build function's rule value. */
|
|
76
80
|
type BuiltRule<F> = F extends (r: never) => infer RV ? RV : never
|
|
77
81
|
|
|
78
82
|
/** The union row of a tuple of output builds. */
|
|
79
|
-
type OutputRow<Builds extends readonly OutputBuild<SchemaRelations>[]> = RowOf<BuiltRule<Builds[number]>>
|
|
83
|
+
type OutputRow<Builds extends readonly OutputBuild<SchemaRelations, SchemaClasses>[]> = RowOf<BuiltRule<Builds[number]>>
|
|
80
84
|
|
|
81
85
|
/** The intersected params record of a tuple of output builds. */
|
|
82
|
-
type OutputParams<Builds extends readonly OutputBuild<SchemaRelations>[]> = ShapeOf<
|
|
86
|
+
type OutputParams<Builds extends readonly OutputBuild<SchemaRelations, SchemaClasses>[]> = ShapeOf<
|
|
87
|
+
ParamsOf<BuiltRule<Builds[number]>>
|
|
88
|
+
>
|
|
83
89
|
|
|
84
90
|
/**
|
|
85
91
|
* The program scope: declare recs, attach their rules, then declare the
|
|
86
92
|
* output — which seals the recs (a later `rec`/`rule` is a construction
|
|
87
93
|
* error) and returns the program as an ordinary query value.
|
|
88
94
|
*/
|
|
89
|
-
interface ProgramScope<Rels extends SchemaRelations> {
|
|
95
|
+
interface ProgramScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> {
|
|
90
96
|
/** Declares one recursive predicate; declaration order = its dense `PredId`. */
|
|
91
|
-
rec<const Name extends string>(name: Name): Rec<Rels, Name, Record<never, never
|
|
97
|
+
rec<const Name extends string>(name: Name): Rec<Rels, Name, Record<never, never>, undefined, Classes>
|
|
92
98
|
/**
|
|
93
99
|
* Declares the output predicate (one rule per build; multiple rules =
|
|
94
100
|
* set union) and seals the program. Must be what the `program()`
|
|
95
101
|
* callback returns.
|
|
96
102
|
*/
|
|
97
|
-
output<const Builds extends readonly OutputBuild<Rels>[]>(
|
|
103
|
+
output<const Builds extends readonly OutputBuild<Rels, Classes>[]>(
|
|
98
104
|
...builds: Builds
|
|
99
|
-
): Query<Rels, OutputRow<Builds>, OutputParams<Builds
|
|
105
|
+
): Query<Rels, OutputRow<Builds>, OutputParams<Builds>, Classes>
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
/** The runtime rec-handle shape beneath the typed `Rec` face. */
|
|
@@ -117,7 +123,7 @@ function makeRawRec<Name extends string>(state: ProgramState, name: Name, data:
|
|
|
117
123
|
`rec ${name}: the program's output is already declared — recursive rules attach before p.output`
|
|
118
124
|
)
|
|
119
125
|
}
|
|
120
|
-
const built = build(makeRawScope({ kind: "rec", self: data }))
|
|
126
|
+
const built = build(makeRawScope({ kind: "rec", self: data, classes: state.classes }))
|
|
121
127
|
const head = data.rules[0]
|
|
122
128
|
if (head !== undefined) {
|
|
123
129
|
const declared = head.select.map(function columnName(column) {
|
|
@@ -145,21 +151,24 @@ function makeRawRec<Name extends string>(state: ProgramState, name: Name, data:
|
|
|
145
151
|
* the checkable fact — the handle owns exactly the rec data it names — is
|
|
146
152
|
* verified before the raw handle is admitted at its typed face.
|
|
147
153
|
*/
|
|
148
|
-
function isRecHandle<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
function isRecHandle<
|
|
155
|
+
Rels extends SchemaRelations,
|
|
156
|
+
Name extends string,
|
|
157
|
+
P extends ParamsRecord,
|
|
158
|
+
Classes extends SchemaClasses
|
|
159
|
+
>(data: RecData, rec: RawRec<Name>): rec is RawRec<Name> & Rec<Rels, Name, P, undefined, Classes> {
|
|
152
160
|
return rec.data === data
|
|
153
161
|
}
|
|
154
162
|
|
|
155
163
|
/** Builds one typed rec handle over shared rec data. */
|
|
156
|
-
function makeRec<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
function makeRec<
|
|
165
|
+
Rels extends SchemaRelations,
|
|
166
|
+
Name extends string,
|
|
167
|
+
P extends ParamsRecord,
|
|
168
|
+
Classes extends SchemaClasses
|
|
169
|
+
>(state: ProgramState, name: Name, data: RecData): Rec<Rels, Name, P, undefined, Classes> {
|
|
161
170
|
const raw = makeRawRec<Name>(state, name, data)
|
|
162
|
-
if (!isRecHandle<Rels, Name, P>(data, raw)) {
|
|
171
|
+
if (!isRecHandle<Rels, Name, P, Classes>(data, raw)) {
|
|
163
172
|
throw errors.new(`rec ${name}: handle construction incomplete`)
|
|
164
173
|
}
|
|
165
174
|
return raw
|
|
@@ -172,15 +181,16 @@ function makeRec<Rels extends SchemaRelations, Name extends string, P extends Pa
|
|
|
172
181
|
* the one `ProgramIr` shape the engine executes under the per-stratum
|
|
173
182
|
* fixpoint driver.
|
|
174
183
|
*/
|
|
175
|
-
function program<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
184
|
+
function program<
|
|
185
|
+
Rels extends SchemaRelations,
|
|
186
|
+
Classes extends SchemaClasses,
|
|
187
|
+
Q extends Query<Rels, unknown, ParamsRecord, Classes>
|
|
188
|
+
>(theory: Schema<Rels, Classes>, build: (p: ProgramScope<Rels, Classes>) => Q): Q {
|
|
189
|
+
const state: ProgramState = { recs: [], classes: theory.classes, sealed: false }
|
|
180
190
|
const names = new Set<string>()
|
|
181
191
|
const made: { query: unknown } = { query: undefined }
|
|
182
|
-
const scope: ProgramScope<Rels> = {
|
|
183
|
-
rec<const Name extends string>(name: Name): Rec<Rels, Name, Record<never, never
|
|
192
|
+
const scope: ProgramScope<Rels, Classes> = {
|
|
193
|
+
rec<const Name extends string>(name: Name): Rec<Rels, Name, Record<never, never>, undefined, Classes> {
|
|
184
194
|
if (state.sealed) {
|
|
185
195
|
throw errors.new(`program: the output is already declared — rec ${name} would be unreachable`)
|
|
186
196
|
}
|
|
@@ -192,11 +202,11 @@ function program<Rels extends SchemaRelations, Q extends Query<Rels, unknown, Pa
|
|
|
192
202
|
names.add(name)
|
|
193
203
|
const data: RecData = { name, rules: [] }
|
|
194
204
|
state.recs.push(data)
|
|
195
|
-
return makeRec<Rels, Name, Record<never, never
|
|
205
|
+
return makeRec<Rels, Name, Record<never, never>, Classes>(state, name, data)
|
|
196
206
|
},
|
|
197
|
-
output<const Builds extends readonly OutputBuild<Rels>[]>(
|
|
207
|
+
output<const Builds extends readonly OutputBuild<Rels, Classes>[]>(
|
|
198
208
|
...builds: Builds
|
|
199
|
-
): Query<Rels, OutputRow<Builds>, OutputParams<Builds
|
|
209
|
+
): Query<Rels, OutputRow<Builds>, OutputParams<Builds>, Classes> {
|
|
200
210
|
if (state.sealed) {
|
|
201
211
|
throw errors.new("program: output is declared once — multiple rules are multiple builds of the one output")
|
|
202
212
|
}
|
|
@@ -213,9 +223,9 @@ function program<Rels extends SchemaRelations, Q extends Query<Rels, unknown, Pa
|
|
|
213
223
|
throw errors.new("program: the output needs at least one rule")
|
|
214
224
|
}
|
|
215
225
|
const rules = builds.map(function buildRule(buildOne) {
|
|
216
|
-
return buildOne(makeOutputRuleScope<Rels>(state)).rule
|
|
226
|
+
return buildOne(makeOutputRuleScope<Rels, Classes>(state)).rule
|
|
217
227
|
})
|
|
218
|
-
const q = makeQuery<Rels, OutputRow<Builds>, OutputParams<Builds
|
|
228
|
+
const q = makeQuery<Rels, OutputRow<Builds>, OutputParams<Builds>, Classes>(theory, state.recs, rules)
|
|
219
229
|
made.query = q
|
|
220
230
|
return q
|
|
221
231
|
}
|
package/src/query/run.ts
CHANGED
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
* values — the marshal boundary is pure both ways: the engine computed
|
|
9
9
|
* the answer under the prepared head, so a decoded row that carries every
|
|
10
10
|
* select column IS a row (the trusted read seam), and nothing is asserted
|
|
11
|
-
* on any value.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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 `SelectColumn.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`.
|
|
14
17
|
*/
|
|
15
18
|
|
|
16
19
|
import * as errors from "@superbuilders/errors"
|
|
20
|
+
import { handleOf } from "#marshal.ts"
|
|
17
21
|
import type { FactValue, QueryParam, TaggedValue } from "#native.ts"
|
|
18
22
|
import type { SelectColumn } from "#query/atom.ts"
|
|
19
23
|
import { taggedCmpLiteral } from "#query/lower.ts"
|
|
@@ -45,10 +49,23 @@ function wireValue(entry: ParamEntry, context: string, value: unknown): TaggedVa
|
|
|
45
49
|
* in registry order (= the lowering's dense `ParamId`s). A missing entry
|
|
46
50
|
* is a typed error naming the param; values tag by the anchoring use's
|
|
47
51
|
* structural type; a set param takes a readonly array (the empty set is
|
|
48
|
-
* legal and matches nothing — the engine's rule).
|
|
52
|
+
* legal and matches nothing — the engine's rule). A MEMBERSHIP-ARRAY
|
|
53
|
+
* entry (`members` present — a literal set folded into the program) is
|
|
54
|
+
* supplied by the SDK itself: each handle name rides the one
|
|
55
|
+
* roster-verification point (`taggedHandleId`, through `wireValue`) and
|
|
56
|
+
* crosses as the same `{ kind: "set", values }` a bound `r.inSet` param
|
|
57
|
+
* crosses as — the host's params object is never consulted for it.
|
|
49
58
|
*/
|
|
50
59
|
function wireParams(entries: readonly ParamEntry[], supplied: Readonly<Record<string, unknown>>): QueryParam[] {
|
|
51
60
|
return entries.map(function wireOne(entry): QueryParam {
|
|
61
|
+
if (entry.members !== undefined) {
|
|
62
|
+
return {
|
|
63
|
+
kind: "set",
|
|
64
|
+
values: entry.members.map(function wireMember(member, index) {
|
|
65
|
+
return wireValue(entry, `membership array ${entry.name}[${index}]`, member)
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
52
69
|
const value = supplied[entry.name]
|
|
53
70
|
if (value === undefined) {
|
|
54
71
|
throw errors.new(`execute params object is missing param ${entry.name}`)
|
|
@@ -90,7 +107,9 @@ function isAnswerRow<Row>(
|
|
|
90
107
|
/**
|
|
91
108
|
* Decodes positional answer rows (column order = the program's head order
|
|
92
109
|
* = the select's written order) to named, frozen row objects of bare
|
|
93
|
-
* structural values.
|
|
110
|
+
* structural values. A closed column lifts its row id back to the handle
|
|
111
|
+
* NAME through the marshal's bijection — an out-of-roster id is the same
|
|
112
|
+
* pointed throw a fact decode gives, never a silent fallback.
|
|
94
113
|
*/
|
|
95
114
|
function decodeAnswers<Row>(select: readonly SelectColumn[], rows: FactValue[][]): Row[] {
|
|
96
115
|
return rows.map(function decodeRow(row) {
|
|
@@ -103,7 +122,8 @@ function decodeAnswers<Row>(select: readonly SelectColumn[], rows: FactValue[][]
|
|
|
103
122
|
if (cell === undefined) {
|
|
104
123
|
throw errors.new(`query answer cell ${ordinal} (${column.name}) is absent`)
|
|
105
124
|
}
|
|
106
|
-
decoded[column.name] =
|
|
125
|
+
decoded[column.name] =
|
|
126
|
+
column.closed === undefined ? cell : handleOf(`query answer column ${column.name}`, column.closed, cell)
|
|
107
127
|
})
|
|
108
128
|
Object.freeze(decoded)
|
|
109
129
|
if (!isAnswerRow<Row>(select, decoded)) {
|
package/src/query/scope.ts
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Query scope terms,
|
|
2
|
+
* Query scope terms, LAW-TYPED edition: string-named variables and
|
|
3
3
|
* parameters as plain frozen values. A `Var` is a NAME — it is typed by the
|
|
4
|
-
* field it first binds (
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* and
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
4
|
+
* field slot it first binds (the descriptor AND the slot's law-computed
|
|
5
|
+
* CLASS, read off the schema's class map through the rule builder's
|
|
6
|
+
* environment), reuse of the name within one rule IS the join, and a
|
|
7
|
+
* class-mismatched reuse is a compile error: a var joins only class-equal
|
|
8
|
+
* slots, and BARE PAIRS ONLY WITH BARE (ruling 3 — a slot in no law has no
|
|
9
|
+
* class and never joins a classed slot; the deliberate sum-domain pointer
|
|
10
|
+
* stays legal against other bare slots). Params are query-global by name
|
|
11
|
+
* and typed BY USE: the field position or comparison sibling that anchors
|
|
12
|
+
* a param types it, the query's inferred `Params` object is exactly the
|
|
13
|
+
* params the rules use, and a param value that no rule uses simply never
|
|
14
|
+
* registers — the query executes under its own inferred type (the
|
|
15
|
+
* bug-hunt law). This module also owns the environment/typing utilities
|
|
16
|
+
* the whole surface shares: the env shape (var name → classed slot), the
|
|
17
|
+
* class-equality judgment {@link JoinOk} with its runtime twin
|
|
18
|
+
* {@link fieldJoins}, and the record-folding helpers `Params` and `Row`
|
|
19
|
+
* inference ride.
|
|
17
20
|
*/
|
|
18
21
|
|
|
22
|
+
import * as errors from "@superbuilders/errors"
|
|
19
23
|
import type { AnyField, Infer } from "#fields.ts"
|
|
20
24
|
|
|
21
25
|
/**
|
|
@@ -106,6 +110,57 @@ function makeVar<const Name extends string>(name: Name): Var<Name> {
|
|
|
106
110
|
return Object.freeze(value)
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
/** The record `makeVars` mints: one own frozen `Var<Name>` per requested name, each typed exactly. */
|
|
114
|
+
type VarsRecord<Names extends string> = { readonly [N in Names]: Var<N> }
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The trusted seam of the vars mint: every requested name reads back as an
|
|
118
|
+
* own var term of exactly that name — verified before the record is
|
|
119
|
+
* admitted at the {@link VarsRecord} type (a name riding the
|
|
120
|
+
* object-protocol accessor instead of an own definition would fail exactly
|
|
121
|
+
* this check).
|
|
122
|
+
*/
|
|
123
|
+
function varsMinted<Names extends string>(
|
|
124
|
+
record: Readonly<Record<string, unknown>>,
|
|
125
|
+
names: readonly Names[]
|
|
126
|
+
): record is Readonly<Record<string, unknown>> & VarsRecord<Names> {
|
|
127
|
+
return names.every(function varMinted(name) {
|
|
128
|
+
if (!Object.hasOwn(record, name)) {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
const value = record[name]
|
|
132
|
+
return isTerm(value) && value[term] === "var" && value.name === name
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Mints several variables at once — `const { service, w } = r.vars("service",
|
|
138
|
+
* "w")`: tuple-to-object, each name typed exactly (`Var<"service">`),
|
|
139
|
+
* inference identical to the one-at-a-time `r.var` spelling (one lowering,
|
|
140
|
+
* two entry flavors). Each key is defined as an OWN property (a name like
|
|
141
|
+
* `"__proto__"` is a record key like any other, never a prototype write),
|
|
142
|
+
* and a duplicate name in one call is a construction error: each name mints
|
|
143
|
+
* one variable — write it once and reuse the binding.
|
|
144
|
+
*/
|
|
145
|
+
function makeVars<const Names extends readonly string[]>(...names: Names): VarsRecord<Names[number]> {
|
|
146
|
+
const out: Record<string, unknown> = {}
|
|
147
|
+
const seen = new Set<string>()
|
|
148
|
+
for (const name of names) {
|
|
149
|
+
if (seen.has(name)) {
|
|
150
|
+
throw errors.new(
|
|
151
|
+
`vars: duplicate name ${name} — each name mints one variable; write it once and reuse the binding`
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
seen.add(name)
|
|
155
|
+
Object.defineProperty(out, name, { value: makeVar(name), enumerable: true })
|
|
156
|
+
}
|
|
157
|
+
Object.freeze(out)
|
|
158
|
+
if (!varsMinted<Names[number]>(out, names)) {
|
|
159
|
+
throw errors.new("vars: variable minting incomplete")
|
|
160
|
+
}
|
|
161
|
+
return out
|
|
162
|
+
}
|
|
163
|
+
|
|
109
164
|
/** Builds one scalar-parameter term. */
|
|
110
165
|
function makeParam<const Name extends string>(name: Name): Param<Name> {
|
|
111
166
|
const value: Param<Name> = { [term]: "param", name }
|
|
@@ -131,11 +186,24 @@ function makeDuration<const Name extends string>(name: Name): Duration<Name> {
|
|
|
131
186
|
}
|
|
132
187
|
|
|
133
188
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
189
|
+
* One bound field slot: the field's descriptor plus the slot's
|
|
190
|
+
* law-computed CLASS (`undefined` = bare — the slot is in no law). The one
|
|
191
|
+
* shape the rule environment carries per variable, at the TYPE level (env
|
|
192
|
+
* entries hold the schema type's class-map lookups) and at RUNTIME alike
|
|
193
|
+
* (the rule's `varFields` record holds exactly this shape, read off the
|
|
194
|
+
* schema value's frozen class map) — one shape, two tiers, one walk.
|
|
195
|
+
*/
|
|
196
|
+
interface ClassedField {
|
|
197
|
+
readonly field: AnyField
|
|
198
|
+
readonly class: string | undefined
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* A rule's typing environment: variable name → the classed slot it first
|
|
203
|
+
* bound. Purely a TYPE — the runtime twin is the rule's `varFields`
|
|
136
204
|
* record, and the two are built by the same walk.
|
|
137
205
|
*/
|
|
138
|
-
type EnvShape = Record<string,
|
|
206
|
+
type EnvShape = Record<string, ClassedField>
|
|
139
207
|
|
|
140
208
|
/** A params object type — what `execute` takes and inference carries. */
|
|
141
209
|
type ParamsRecord = Readonly<Record<string, unknown>>
|
|
@@ -154,66 +222,102 @@ type UnionToIntersection<U> = (U extends unknown ? (member: U) => void : never)
|
|
|
154
222
|
*/
|
|
155
223
|
type ShapeOf<U> = [U] extends [never] ? Record<never, never> : Flatten<UnionToIntersection<U>>
|
|
156
224
|
|
|
157
|
-
/** Reads a field descriptor's domain label (S1: the label IS the domain check). */
|
|
158
|
-
type DomainOf<F extends AnyField> = F["domain"]
|
|
159
|
-
|
|
160
225
|
/** Reads a field descriptor's width label (`bytes<N>`, `interval<E, W>`); `undefined` when the kind carries none. */
|
|
161
226
|
type WidthOf<F extends AnyField> = F extends { readonly width: infer W } ? W : undefined
|
|
162
227
|
|
|
163
228
|
/** Reads an interval descriptor's element kind; `undefined` on scalar kinds. */
|
|
164
229
|
type ElementOf<F extends AnyField> = F extends { readonly element: infer E } ? E : undefined
|
|
165
230
|
|
|
231
|
+
/** Reads a closed reference's handle union; `undefined` on every non-closed kind (the roster IS descriptor structure). */
|
|
232
|
+
type RosterOf<F extends AnyField> = F extends {
|
|
233
|
+
readonly closed: { readonly handles: readonly (infer H extends string)[] }
|
|
234
|
+
}
|
|
235
|
+
? H
|
|
236
|
+
: undefined
|
|
237
|
+
|
|
166
238
|
/**
|
|
167
|
-
* The
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
239
|
+
* The join judgment: two bound slots join iff their descriptors' structure
|
|
240
|
+
* agrees (kind, width label, interval element, and the closed ROSTER — a
|
|
241
|
+
* closed reference pairs only with the same vocabulary, never with a bare
|
|
242
|
+
* u64: the roster keys every closed judgment downstream, so a join across
|
|
243
|
+
* it would decode/order/translate incoherently by binding order) AND their
|
|
244
|
+
* law-computed classes agree — same class name joins, and bare
|
|
245
|
+
* (`undefined`) pairs only with bare (ruling 3: a field in no law has no
|
|
246
|
+
* class; a bare↔classed pairing refuses). The class names come off the
|
|
247
|
+
* SCHEMA type's class map — the statements are the typing; no descriptor
|
|
248
|
+
* label beyond the roster exists to compare.
|
|
171
249
|
*/
|
|
172
|
-
type JoinOk<A extends
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
WidthOf<
|
|
176
|
-
ElementOf<
|
|
177
|
-
]
|
|
178
|
-
|
|
250
|
+
type JoinOk<A extends ClassedField, B extends ClassedField> = [
|
|
251
|
+
A["field"]["kind"],
|
|
252
|
+
A["class"],
|
|
253
|
+
WidthOf<A["field"]>,
|
|
254
|
+
ElementOf<A["field"]>,
|
|
255
|
+
RosterOf<A["field"]>
|
|
256
|
+
] extends [B["field"]["kind"], B["class"], WidthOf<B["field"]>, ElementOf<B["field"]>, RosterOf<B["field"]>]
|
|
257
|
+
? [B["field"]["kind"], B["class"], WidthOf<B["field"]>, ElementOf<B["field"]>, RosterOf<B["field"]>] extends [
|
|
258
|
+
A["field"]["kind"],
|
|
259
|
+
A["class"],
|
|
260
|
+
WidthOf<A["field"]>,
|
|
261
|
+
ElementOf<A["field"]>,
|
|
262
|
+
RosterOf<A["field"]>
|
|
263
|
+
]
|
|
179
264
|
? true
|
|
180
265
|
: false
|
|
181
266
|
: false
|
|
182
267
|
|
|
183
268
|
/**
|
|
184
|
-
* The runtime twin of {@link JoinOk}: two
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
269
|
+
* The runtime twin of {@link JoinOk}: two bound slots join iff descriptor
|
|
270
|
+
* structure and class agree — the same comparison the type tier makes,
|
|
271
|
+
* judged on the honest runtime values (the descriptor, the roster by VALUE
|
|
272
|
+
* IDENTITY — vocabulary identity is value identity — and the schema
|
|
273
|
+
* value's frozen class map). The rule builders throw through this on a
|
|
274
|
+
* class-unequal variable reuse, so the wall holds for untyped callers too,
|
|
275
|
+
* not only where the compiler can see.
|
|
190
276
|
*/
|
|
191
|
-
function fieldJoins(a:
|
|
192
|
-
const widthA = "width" in a ? a.width : undefined
|
|
193
|
-
const widthB = "width" in b ? b.width : undefined
|
|
194
|
-
const elementA = "element" in a ? a.element : undefined
|
|
195
|
-
const elementB = "element" in b ? b.element : undefined
|
|
196
|
-
|
|
277
|
+
function fieldJoins(a: ClassedField, b: ClassedField): boolean {
|
|
278
|
+
const widthA = "width" in a.field ? a.field.width : undefined
|
|
279
|
+
const widthB = "width" in b.field ? b.field.width : undefined
|
|
280
|
+
const elementA = "element" in a.field ? a.field.element : undefined
|
|
281
|
+
const elementB = "element" in b.field ? b.field.element : undefined
|
|
282
|
+
const rosterA = "closed" in a.field ? a.field.closed : undefined
|
|
283
|
+
const rosterB = "closed" in b.field ? b.field.closed : undefined
|
|
284
|
+
return (
|
|
285
|
+
a.field.kind === b.field.kind &&
|
|
286
|
+
a.class === b.class &&
|
|
287
|
+
widthA === widthB &&
|
|
288
|
+
elementA === elementB &&
|
|
289
|
+
rosterA === rosterB
|
|
290
|
+
)
|
|
197
291
|
}
|
|
198
292
|
|
|
199
293
|
/**
|
|
200
|
-
* Renders one
|
|
201
|
-
* grammar's
|
|
294
|
+
* Renders one bound slot for join-mismatch diagnostics — the structural
|
|
295
|
+
* kind in the schema grammar's spelling (a closed reference names its
|
|
296
|
+
* vocabulary: the roster is part of the structure being compared) plus the
|
|
297
|
+
* slot's law-computed class (`u64 in class Holder.id`; a lawless slot
|
|
298
|
+
* renders `bare`).
|
|
202
299
|
*/
|
|
203
|
-
function renderFieldKind(
|
|
300
|
+
function renderFieldKind(slot: ClassedField): string {
|
|
301
|
+
const field = slot.field
|
|
204
302
|
let base: string = field.kind
|
|
303
|
+
if ("closed" in field) {
|
|
304
|
+
base = `u64 referencing ${field.closed.name}`
|
|
305
|
+
}
|
|
205
306
|
if (field.kind === "bytes") {
|
|
206
307
|
base = `bytes<${field.width}>`
|
|
207
308
|
}
|
|
208
309
|
if (field.kind === "interval") {
|
|
209
310
|
base = field.width === undefined ? `interval<${field.element}>` : `interval<${field.element}, ${field.width}>`
|
|
210
311
|
}
|
|
211
|
-
return
|
|
312
|
+
return slot.class === undefined ? `${base} (bare)` : `${base} in class ${slot.class}`
|
|
212
313
|
}
|
|
213
314
|
|
|
214
315
|
/**
|
|
215
316
|
* What a PARAM anchored at field `F` accepts at execution: the field's
|
|
216
|
-
* bare value type, exactly
|
|
317
|
+
* bare value type, exactly — at a CLOSED-reference field that is the
|
|
318
|
+
* handle-name union (`"DirectPass" | "Failed"`), translated name → row id
|
|
319
|
+
* at execute through the one roster-verification point
|
|
320
|
+
* (`taggedHandleId`). At an interval field the engine resolves the
|
|
217
321
|
* bivalent anchor to the INTERVAL reading (value equality) — the point
|
|
218
322
|
* reading of a param is spelled `pointIn(r.param(...), w)`, whose sibling
|
|
219
323
|
* anchors it element-typed.
|
|
@@ -230,18 +334,22 @@ type InferredOf<T> = T extends { readonly [inferred]?: infer S } ? Exclude<S, un
|
|
|
230
334
|
* positions) — the op keeps literal tagging op-aware at `pointIn`
|
|
231
335
|
* (the bug-hunt fix, preserved). `anchor` is `undefined` only on a query
|
|
232
336
|
* built but not yet anchored by any rule; lowering and the wire both refuse
|
|
233
|
-
* that state typed.
|
|
337
|
+
* that state typed. `members` is present exactly on a MEMBERSHIP-ARRAY
|
|
338
|
+
* entry (a literal set at a closed field, folded into the program): the
|
|
339
|
+
* SDK itself translates and supplies the set at every execute — the entry
|
|
340
|
+
* is never read from, and never demanded of, the host's params object.
|
|
234
341
|
*/
|
|
235
342
|
interface ParamEntry {
|
|
236
343
|
readonly name: string
|
|
237
344
|
readonly shape: "value" | "set" | "mask"
|
|
238
345
|
readonly anchor: AnyField | "measure" | undefined
|
|
239
346
|
readonly op: "binding" | "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "pointIn" | "allen"
|
|
347
|
+
readonly members: readonly string[] | undefined
|
|
240
348
|
}
|
|
241
349
|
|
|
242
350
|
export type {
|
|
243
351
|
AnyTerm,
|
|
244
|
-
|
|
352
|
+
ClassedField,
|
|
245
353
|
Duration,
|
|
246
354
|
EnvShape,
|
|
247
355
|
Flatten,
|
|
@@ -255,7 +363,8 @@ export type {
|
|
|
255
363
|
SetParam,
|
|
256
364
|
ShapeOf,
|
|
257
365
|
UnionToIntersection,
|
|
258
|
-
Var
|
|
366
|
+
Var,
|
|
367
|
+
VarsRecord
|
|
259
368
|
}
|
|
260
369
|
export {
|
|
261
370
|
fieldJoins,
|
|
@@ -266,6 +375,7 @@ export {
|
|
|
266
375
|
makeParam,
|
|
267
376
|
makeSetParam,
|
|
268
377
|
makeVar,
|
|
378
|
+
makeVars,
|
|
269
379
|
renderFieldKind,
|
|
270
380
|
term
|
|
271
381
|
}
|
package/src/query/select.ts
CHANGED
|
@@ -166,7 +166,7 @@ type CheckNameSelect<Env extends EnvShape, S> = {
|
|
|
166
166
|
* type.
|
|
167
167
|
*/
|
|
168
168
|
type SelectEntryRow<Env extends EnvShape, E> = E extends string
|
|
169
|
-
? { readonly [K in E]: Infer<Env[E & keyof Env]> }
|
|
169
|
+
? { readonly [K in E]: Infer<Env[E & keyof Env]["field"]> }
|
|
170
170
|
: E extends Duration<infer N extends string>
|
|
171
171
|
? { readonly [K in N]: bigint }
|
|
172
172
|
: E extends Agg<"count", undefined>
|
|
@@ -175,14 +175,14 @@ type SelectEntryRow<Env extends EnvShape, E> = E extends string
|
|
|
175
175
|
? { readonly [K in O]: bigint }
|
|
176
176
|
: E extends Agg<"sum" | "min" | "max", infer O>
|
|
177
177
|
? O extends string
|
|
178
|
-
? { readonly [K in O]: Infer<Env[O & keyof Env]> }
|
|
178
|
+
? { readonly [K in O]: Infer<Env[O & keyof Env]["field"]> }
|
|
179
179
|
: O extends Duration<infer N extends string>
|
|
180
180
|
? { readonly [K in N]: bigint }
|
|
181
181
|
: never
|
|
182
182
|
: E extends Agg<"argMax" | "argMin", infer O extends string, string>
|
|
183
|
-
? { readonly [K in O]: Infer<Env[O & keyof Env]> }
|
|
183
|
+
? { readonly [K in O]: Infer<Env[O & keyof Env]["field"]> }
|
|
184
184
|
: E extends Agg<"pack", infer O extends string>
|
|
185
|
-
? { readonly [K in O]: Infer<Env[O & keyof Env]> }
|
|
185
|
+
? { readonly [K in O]: Infer<Env[O & keyof Env]["field"]> }
|
|
186
186
|
: never
|
|
187
187
|
|
|
188
188
|
/** The inferred answer-row object type of a select tuple. */
|
|
@@ -194,7 +194,7 @@ type RowOfSelect<Env extends EnvShape, S extends readonly SelectEntry[]> = Shape
|
|
|
194
194
|
* smears into one column's type), mirroring {@link SelectEntryRow}.
|
|
195
195
|
*/
|
|
196
196
|
type NameSelectRow<Env extends EnvShape, N> = N extends string
|
|
197
|
-
? { readonly [K in N]: Infer<Env[K & keyof Env]> }
|
|
197
|
+
? { readonly [K in N]: Infer<Env[K & keyof Env]["field"]> }
|
|
198
198
|
: never
|
|
199
199
|
|
|
200
200
|
/** The inferred answer-row object type of a names-only (recursive-rule) select tuple. */
|