@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.
Files changed (103) hide show
  1. package/COOKBOOK.md +33 -49
  2. package/README.md +3 -3
  3. package/dist/capacity.d.ts +24 -136
  4. package/dist/capacity.d.ts.map +1 -1
  5. package/dist/capacity.js +18 -40
  6. package/dist/capacity.js.map +1 -1
  7. package/dist/closed.d.ts +0 -156
  8. package/dist/closed.d.ts.map +1 -1
  9. package/dist/closed.js +0 -104
  10. package/dist/closed.js.map +1 -1
  11. package/dist/db.d.ts +7 -223
  12. package/dist/db.d.ts.map +1 -1
  13. package/dist/db.js +147 -396
  14. package/dist/db.js.map +1 -1
  15. package/dist/face.d.ts +0 -133
  16. package/dist/face.d.ts.map +1 -1
  17. package/dist/face.js +0 -33
  18. package/dist/face.js.map +1 -1
  19. package/dist/fields.d.ts +1 -145
  20. package/dist/fields.d.ts.map +1 -1
  21. package/dist/fields.js +2 -91
  22. package/dist/fields.js.map +1 -1
  23. package/dist/index.d.ts +12 -15
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +10 -13
  26. package/dist/index.js.map +1 -1
  27. package/dist/law.d.ts +111 -93
  28. package/dist/law.d.ts.map +1 -1
  29. package/dist/law.js +23 -27
  30. package/dist/law.js.map +1 -1
  31. package/dist/lower.d.ts +9 -35
  32. package/dist/lower.d.ts.map +1 -1
  33. package/dist/lower.js +8 -53
  34. package/dist/lower.js.map +1 -1
  35. package/dist/marshal.d.ts +0 -65
  36. package/dist/marshal.d.ts.map +1 -1
  37. package/dist/marshal.js +0 -72
  38. package/dist/marshal.js.map +1 -1
  39. package/dist/native.d.ts +31 -289
  40. package/dist/native.d.ts.map +1 -1
  41. package/dist/native.js +15 -64
  42. package/dist/native.js.map +1 -1
  43. package/dist/query/atom.d.ts +10 -276
  44. package/dist/query/atom.d.ts.map +1 -1
  45. package/dist/query/atom.js +1 -96
  46. package/dist/query/atom.js.map +1 -1
  47. package/dist/query/find.d.ts +10 -76
  48. package/dist/query/find.d.ts.map +1 -1
  49. package/dist/query/find.js +0 -30
  50. package/dist/query/find.js.map +1 -1
  51. package/dist/query/lower.d.ts +58 -146
  52. package/dist/query/lower.d.ts.map +1 -1
  53. package/dist/query/lower.js +19 -256
  54. package/dist/query/lower.js.map +1 -1
  55. package/dist/query/parse-ir.d.ts +0 -7
  56. package/dist/query/parse-ir.d.ts.map +1 -1
  57. package/dist/query/parse-ir.js +1 -13
  58. package/dist/query/parse-ir.js.map +1 -1
  59. package/dist/query/run.d.ts +0 -36
  60. package/dist/query/run.d.ts.map +1 -1
  61. package/dist/query/run.js +0 -44
  62. package/dist/query/run.js.map +1 -1
  63. package/dist/query/scope.d.ts +24 -180
  64. package/dist/query/scope.d.ts.map +1 -1
  65. package/dist/query/scope.js +2 -66
  66. package/dist/query/scope.js.map +1 -1
  67. package/dist/relation.d.ts +2 -50
  68. package/dist/relation.d.ts.map +1 -1
  69. package/dist/relation.js +2 -37
  70. package/dist/relation.js.map +1 -1
  71. package/dist/schema.d.ts +13 -63
  72. package/dist/schema.d.ts.map +1 -1
  73. package/dist/schema.js +118 -92
  74. package/dist/schema.js.map +1 -1
  75. package/dist/spec.d.ts +1 -140
  76. package/dist/spec.d.ts.map +1 -1
  77. package/dist/spec.js +1 -68
  78. package/dist/spec.js.map +1 -1
  79. package/dist/statements.d.ts +6 -137
  80. package/dist/statements.d.ts.map +1 -1
  81. package/dist/statements.js +16 -119
  82. package/dist/statements.js.map +1 -1
  83. package/package.json +2 -2
  84. package/src/capacity.ts +26 -140
  85. package/src/closed.ts +5 -206
  86. package/src/db.ts +203 -692
  87. package/src/face.ts +0 -142
  88. package/src/fields.ts +4 -172
  89. package/src/index.ts +10 -15
  90. package/src/law.ts +201 -129
  91. package/src/lower.ts +8 -53
  92. package/src/marshal.ts +1 -85
  93. package/src/native.ts +58 -321
  94. package/src/query/atom.ts +26 -313
  95. package/src/query/find.ts +24 -110
  96. package/src/query/lower.ts +126 -377
  97. package/src/query/parse-ir.ts +1 -14
  98. package/src/query/run.ts +0 -45
  99. package/src/query/scope.ts +25 -186
  100. package/src/relation.ts +2 -66
  101. package/src/schema.ts +143 -122
  102. package/src/spec.ts +1 -160
  103. package/src/statements.ts +22 -174
@@ -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" || find.kind === "aggregateMeasure") {
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) {
@@ -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()` precedent). Every `v()` call mints
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 | "measure" | undefined
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
- Duration,
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, makeDuration, makeParam, makeSetParam, renderFieldKind, term, v }
244
+ export { fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term, v }
package/src/relation.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * `relation()` — the ordinary-relation half of the theory's signature. A
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