@bjornpagen/bumbledb 0.1.0 → 0.2.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.
Files changed (103) hide show
  1. package/COOKBOOK.md +1394 -0
  2. package/README.md +4 -0
  3. package/dist/closed.d.ts +60 -37
  4. package/dist/closed.d.ts.map +1 -1
  5. package/dist/closed.js +159 -40
  6. package/dist/closed.js.map +1 -1
  7. package/dist/count.d.ts +64 -17
  8. package/dist/count.d.ts.map +1 -1
  9. package/dist/count.js +37 -14
  10. package/dist/count.js.map +1 -1
  11. package/dist/db.d.ts +15 -7
  12. package/dist/db.d.ts.map +1 -1
  13. package/dist/db.js +13 -5
  14. package/dist/db.js.map +1 -1
  15. package/dist/exhume.d.ts +12 -11
  16. package/dist/exhume.d.ts.map +1 -1
  17. package/dist/exhume.js +7 -6
  18. package/dist/exhume.js.map +1 -1
  19. package/dist/face.d.ts +79 -19
  20. package/dist/face.d.ts.map +1 -1
  21. package/dist/face.js +14 -15
  22. package/dist/face.js.map +1 -1
  23. package/dist/fields.d.ts +158 -129
  24. package/dist/fields.d.ts.map +1 -1
  25. package/dist/fields.js +80 -81
  26. package/dist/fields.js.map +1 -1
  27. package/dist/index.d.ts +21 -21
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +13 -12
  30. package/dist/index.js.map +1 -1
  31. package/dist/lower.d.ts +6 -3
  32. package/dist/lower.d.ts.map +1 -1
  33. package/dist/lower.js +48 -17
  34. package/dist/lower.js.map +1 -1
  35. package/dist/marshal.d.ts +36 -21
  36. package/dist/marshal.d.ts.map +1 -1
  37. package/dist/marshal.js +41 -25
  38. package/dist/marshal.js.map +1 -1
  39. package/dist/native.js +2 -2
  40. package/dist/query/atom.d.ts +332 -174
  41. package/dist/query/atom.d.ts.map +1 -1
  42. package/dist/query/atom.js +72 -172
  43. package/dist/query/atom.js.map +1 -1
  44. package/dist/query/lower.d.ts +295 -107
  45. package/dist/query/lower.d.ts.map +1 -1
  46. package/dist/query/lower.js +860 -388
  47. package/dist/query/lower.js.map +1 -1
  48. package/dist/query/predicate.d.ts +69 -88
  49. package/dist/query/predicate.d.ts.map +1 -1
  50. package/dist/query/predicate.js +118 -72
  51. package/dist/query/predicate.js.map +1 -1
  52. package/dist/query/run.d.ts +19 -16
  53. package/dist/query/run.d.ts.map +1 -1
  54. package/dist/query/run.js +31 -24
  55. package/dist/query/run.js.map +1 -1
  56. package/dist/query/scope.d.ts +139 -123
  57. package/dist/query/scope.d.ts.map +1 -1
  58. package/dist/query/scope.js +71 -115
  59. package/dist/query/scope.js.map +1 -1
  60. package/dist/query/select.d.ts +102 -80
  61. package/dist/query/select.d.ts.map +1 -1
  62. package/dist/query/select.js +39 -34
  63. package/dist/query/select.js.map +1 -1
  64. package/dist/relation.d.ts +33 -36
  65. package/dist/relation.d.ts.map +1 -1
  66. package/dist/relation.js +15 -16
  67. package/dist/relation.js.map +1 -1
  68. package/dist/schema.d.ts.map +1 -1
  69. package/dist/schema.js +19 -8
  70. package/dist/schema.js.map +1 -1
  71. package/dist/spec.d.ts +6 -5
  72. package/dist/spec.d.ts.map +1 -1
  73. package/dist/spec.js.map +1 -1
  74. package/dist/statements.d.ts +56 -31
  75. package/dist/statements.d.ts.map +1 -1
  76. package/dist/statements.js +38 -13
  77. package/dist/statements.js.map +1 -1
  78. package/package.json +4 -3
  79. package/src/closed.ts +271 -93
  80. package/src/count.ts +112 -18
  81. package/src/db.ts +22 -12
  82. package/src/exhume.ts +12 -11
  83. package/src/face.ts +114 -22
  84. package/src/fields.ts +261 -217
  85. package/src/index.ts +50 -59
  86. package/src/lower.ts +62 -20
  87. package/src/marshal.ts +48 -30
  88. package/src/native.ts +2 -2
  89. package/src/query/atom.ts +480 -376
  90. package/src/query/lower.ts +1341 -542
  91. package/src/query/predicate.ts +198 -161
  92. package/src/query/run.ts +35 -25
  93. package/src/query/scope.ts +188 -218
  94. package/src/query/select.ts +168 -93
  95. package/src/relation.ts +38 -44
  96. package/src/schema.ts +22 -12
  97. package/src/spec.ts +6 -5
  98. package/src/statements.ts +69 -26
  99. package/dist/brand.d.ts +0 -59
  100. package/dist/brand.d.ts.map +0 -1
  101. package/dist/brand.js +0 -47
  102. package/dist/brand.js.map +0 -1
  103. package/src/brand.ts +0 -82
@@ -1,106 +1,87 @@
1
1
  /**
2
- * Select entries and aggregates (PRD-08) — the head vocabulary, mirroring
3
- * the IR's aggregate roster exactly (`bumbledb/crates/bumbledb/src/ir.rs`
4
- * `AggOp`/`FindTerm`; `docs/architecture/20-query-ir.md` § aggregation):
5
- * `count` (nullary), `countDistinct`, `sum`/`min`/`max` (over a u64/i64
6
- * variable or the measure), `argmax`/`argmin` (arg-restriction: carried
7
- * value + orderable key; a tie yields every attaining row), and `pack`
8
- * (the coalescing fold RELATION-SHAPED: one answer row per (group,
9
- * maximal segment), the result position interval-typed). Grouping is
2
+ * Select entries and aggregates, STRUCTURAL edition — the head vocabulary,
3
+ * mirroring the IR's aggregate roster exactly
4
+ * (`bumbledb/crates/bumbledb/src/ir.rs` `AggOp`/`FindTerm`;
5
+ * `docs/architecture/20-query-ir.md` § aggregation): `count` (nullary),
6
+ * `countDistinct`, `sum`/`min`/`max` (over an orderable variable or the
7
+ * measure), `argMax`/`argMin` (arg-restriction: carried value + orderable
8
+ * key; a tie yields every attaining row), and `pack` (the coalescing fold —
9
+ * RELATION-SHAPED: one answer row per (group, maximal segment), the result
10
+ * position interval-typed). Aggregates name their variables — `r.sum("m")`
11
+ * — and are typed by the rule environment at `.select`. Grouping is
10
12
  * implicit: the non-aggregate select entries are the group key; over empty
11
13
  * input an all-aggregate select yields the EMPTY SET, never a zero row.
14
+ * The creation quarantine is representational: a head position is a var
15
+ * name, the measure, or an aggregate — no minting or arithmetic term
16
+ * exists to spell (permanent law).
12
17
  */
13
18
 
14
- import type { IntervalValue } from "#brand.ts"
15
- import { phantom } from "#brand.ts"
16
- import type { Duration } from "#query/atom.ts"
17
- import type { AnyVar, Flatten, Var } from "#query/scope.ts"
18
- import { term } from "#query/scope.ts"
19
-
20
- /** The three folds the measure admits (and the plain-variable folds). */
21
- type FoldOp = "sum" | "min" | "max"
22
-
23
- /** One aggregate's runtime description. */
24
- type AggregateData =
25
- | { readonly op: "count" }
26
- | { readonly op: "countDistinct"; readonly over: AnyVar }
27
- | { readonly op: "fold"; readonly fold: FoldOp; readonly over: AnyVar | Duration }
28
- | {
29
- readonly op: "arg"
30
- readonly direction: "argMax" | "argMin"
31
- readonly key: AnyVar
32
- readonly over: AnyVar
33
- }
34
- | { readonly op: "pack"; readonly over: AnyVar }
19
+ import type { Infer } from "#fields.ts"
20
+ import type { IntervalVarOk, OrderVarOk } from "#query/atom.ts"
21
+ import type { Duration, EnvShape, ShapeOf } from "#query/scope.ts"
22
+
23
+ /** One aggregate operator name of the select vocabulary. */
24
+ type AggOpName = "count" | "countDistinct" | "sum" | "min" | "max" | "argMax" | "argMin" | "pack"
35
25
 
36
26
  /**
37
- * One aggregate select value; the phantom carries the answer column's
38
- * host type (`count`/`countDistinct` `bigint` whatever they counted;
39
- * folds carry their input's type; the Arg forms the carried payload's
40
- * type; `pack` its interval type).
27
+ * One aggregate select VALUE: the op, the variable name (or measure) it
28
+ * folds, and for the Arg forms — the orderable key's variable name. The
29
+ * runtime representation carries the types; the rule environment types the
30
+ * result at `.select`.
41
31
  */
42
- interface Aggregate<R> {
43
- readonly aggregate: AggregateData
44
- readonly [phantom]?: R
32
+ interface Agg<
33
+ Op extends AggOpName,
34
+ Over extends string | Duration<string> | undefined,
35
+ Key extends string | undefined = undefined
36
+ > {
37
+ readonly agg: Op
38
+ readonly over: Over
39
+ readonly key: Key
45
40
  }
46
41
 
47
- /** What a select record's values may be: a projection, a measure, or an aggregate. */
48
- type SelectEntryInput = AnyVar | Duration | Aggregate<unknown>
49
-
50
- /** The select record: answer column name to entry, written order = column order. */
51
- type SelectShape = Readonly<Record<string, SelectEntryInput>>
42
+ /** Any aggregate select value. */
43
+ type AnyAgg = Agg<AggOpName, string | Duration<string> | undefined, string | undefined>
52
44
 
53
- /** One select entry's answer type. */
54
- type SelectValue<T> = T extends { readonly aggregate: AggregateData; readonly [phantom]?: infer R }
55
- ? Exclude<R, undefined>
56
- : T extends { readonly measure: AnyVar }
57
- ? bigint
58
- : T extends { readonly [term]: "var"; readonly [phantom]?: infer V }
59
- ? Exclude<V, undefined>
60
- : never
45
+ /** One select entry: a projected var name, the measure, or an aggregate. */
46
+ type SelectEntry = string | Duration<string> | AnyAgg
61
47
 
62
- /** The inferred answer-row object type of a select record. */
63
- type RowOf<Sel extends SelectShape> = Flatten<{ [K in keyof Sel]: SelectValue<Sel[K]> }>
48
+ /** Builds one aggregate value. */
49
+ function aggregate<
50
+ Op extends AggOpName,
51
+ Over extends string | Duration<string> | undefined,
52
+ Key extends string | undefined
53
+ >(op: Op, over: Over, key: Key): Agg<Op, Over, Key> {
54
+ return Object.freeze({ agg: op, over, key })
55
+ }
64
56
 
65
- /** Nullary count: |the group's set of distinct full bindings|, `bigint`. */
66
- function count(): Aggregate<bigint> {
67
- return Object.freeze({ aggregate: Object.freeze({ op: "count" as const }) })
57
+ /** Nullary count: |the group's set of distinct full bindings|, `bigint`; the answer column is named `count`. */
58
+ function count(): Agg<"count", undefined> {
59
+ return aggregate("count", undefined, undefined)
68
60
  }
69
61
 
70
- /** |the distinct values of `over` across the group|, `bigint`; legal over every type. */
71
- function countDistinct<V>(over: Var<V>): Aggregate<bigint> {
72
- return Object.freeze({ aggregate: Object.freeze({ op: "countDistinct" as const, over }) })
62
+ /** |the distinct values of the named variable across the group|, `bigint`; legal over every type. */
63
+ function countDistinct<const N extends string>(over: N): Agg<"countDistinct", N> {
64
+ return aggregate("countDistinct", over, undefined)
73
65
  }
74
66
 
75
67
  /**
76
- * Exact checked sum over a u64/i64 variable (wide accumulator, one
77
- * finalize range check overflow is the engine's typed runtime error,
78
- * never a wrap), or over the measure (`sum(duration(v))`).
68
+ * Exact checked sum over an orderable (u64/i64) variable wide
69
+ * accumulator, one finalize range check; overflow is the engine's typed
70
+ * runtime error, never a wrap or over the measure
71
+ * (`r.sum(r.duration("w"))`).
79
72
  */
80
- function sum<V extends bigint>(over: Var<V>): Aggregate<V>
81
- function sum(over: Duration): Aggregate<bigint>
82
- function sum(over: Var<bigint> | Duration): Aggregate<bigint> {
83
- return Object.freeze({
84
- aggregate: Object.freeze({ op: "fold" as const, fold: "sum" as const, over })
85
- })
73
+ function sum<const N extends string | Duration<string>>(over: N): Agg<"sum", N> {
74
+ return aggregate("sum", over, undefined)
86
75
  }
87
76
 
88
- /** Minimum over a u64/i64 variable or the measure (orderable types only). */
89
- function min<V extends bigint>(over: Var<V>): Aggregate<V>
90
- function min(over: Duration): Aggregate<bigint>
91
- function min(over: Var<bigint> | Duration): Aggregate<bigint> {
92
- return Object.freeze({
93
- aggregate: Object.freeze({ op: "fold" as const, fold: "min" as const, over })
94
- })
77
+ /** Minimum over an orderable variable or the measure (orderable types only). */
78
+ function min<const N extends string | Duration<string>>(over: N): Agg<"min", N> {
79
+ return aggregate("min", over, undefined)
95
80
  }
96
81
 
97
- /** Maximum over a u64/i64 variable or the measure (orderable types only). */
98
- function max<V extends bigint>(over: Var<V>): Aggregate<V>
99
- function max(over: Duration): Aggregate<bigint>
100
- function max(over: Var<bigint> | Duration): Aggregate<bigint> {
101
- return Object.freeze({
102
- aggregate: Object.freeze({ op: "fold" as const, fold: "max" as const, over })
103
- })
82
+ /** Maximum over an orderable variable or the measure (orderable types only). */
83
+ function max<const N extends string | Duration<string>>(over: N): Agg<"max", N> {
84
+ return aggregate("max", over, undefined)
104
85
  }
105
86
 
106
87
  /**
@@ -111,17 +92,13 @@ function max(over: Var<bigint> | Duration): Aggregate<bigint> {
111
92
  * direction; Arg and fold aggregates never mix (both the engine's typed
112
93
  * rules).
113
94
  */
114
- function argmax<K extends bigint, V>(key: Var<K>, value: Var<V>): Aggregate<V> {
115
- return Object.freeze({
116
- aggregate: Object.freeze({ op: "arg" as const, direction: "argMax" as const, key, over: value })
117
- })
95
+ function argMax<const V extends string, const K extends string>(value: V, key: K): Agg<"argMax", V, K> {
96
+ return aggregate("argMax", value, key)
118
97
  }
119
98
 
120
- /** Arg-restriction toward the minimum of `key`; rules as {@link argmax}. */
121
- function argmin<K extends bigint, V>(key: Var<K>, value: Var<V>): Aggregate<V> {
122
- return Object.freeze({
123
- aggregate: Object.freeze({ op: "arg" as const, direction: "argMin" as const, key, over: value })
124
- })
99
+ /** Arg-restriction toward the minimum of `key`; rules as {@link argMax}. */
100
+ function argMin<const V extends string, const K extends string>(value: V, key: K): Agg<"argMin", V, K> {
101
+ return aggregate("argMin", value, key)
125
102
  }
126
103
 
127
104
  /**
@@ -132,9 +109,107 @@ function argmin<K extends bigint, V>(key: Var<K>, value: Var<V>): Aggregate<V> {
132
109
  * input's element type. At most one `pack` per select, never beside a
133
110
  * fold or an Arg entry (the engine's typed rules).
134
111
  */
135
- function pack<IV extends IntervalValue>(over: Var<IV>): Aggregate<IV> {
136
- return Object.freeze({ aggregate: Object.freeze({ op: "pack" as const, over }) })
112
+ function pack<const N extends string>(over: N): Agg<"pack", N> {
113
+ return aggregate("pack", over, undefined)
114
+ }
115
+
116
+ /** A fold input's judgment: an orderable variable, or the measure of an interval variable. */
117
+ type FoldOverOk<Env extends EnvShape, O> = O extends string
118
+ ? OrderVarOk<Env, O>
119
+ : O extends Duration<infer N extends string>
120
+ ? IntervalVarOk<Env, N>
121
+ : false
122
+
123
+ /**
124
+ * One select entry's judgment against the rule environment: projected
125
+ * names must be bound, the measure and `pack` demand interval-typed
126
+ * variables, folds and Arg keys demand orderable ones.
127
+ */
128
+ type SelectEntryOk<Env extends EnvShape, E> = E extends string
129
+ ? E extends keyof Env
130
+ ? true
131
+ : false
132
+ : E extends Duration<infer N extends string>
133
+ ? IntervalVarOk<Env, N>
134
+ : E extends Agg<"count", undefined>
135
+ ? true
136
+ : E extends Agg<"countDistinct", infer O extends string>
137
+ ? O extends keyof Env
138
+ ? true
139
+ : false
140
+ : E extends Agg<"sum" | "min" | "max", infer O>
141
+ ? FoldOverOk<Env, O>
142
+ : E extends Agg<"argMax" | "argMin", infer O extends string, infer K extends string>
143
+ ? [O extends keyof Env ? true : false, OrderVarOk<Env, K>] extends [true, true]
144
+ ? true
145
+ : false
146
+ : E extends Agg<"pack", infer O extends string>
147
+ ? IntervalVarOk<Env, O>
148
+ : false
149
+
150
+ /** The validated select tuple (intersect with the inferred entries — errors land on the offending argument). */
151
+ type CheckSelect<Env extends EnvShape, S> = {
152
+ readonly [I in keyof S]: SelectEntryOk<Env, S[I]> extends true ? S[I] : never
137
153
  }
138
154
 
139
- export type { Aggregate, AggregateData, FoldOp, RowOf, SelectEntryInput, SelectShape, SelectValue }
140
- export { argmax, argmin, count, countDistinct, max, min, pack, sum }
155
+ /** The validated names-only select tuple of a RECURSIVE rule (aggregates and the measure are unwritable there). */
156
+ type CheckNameSelect<Env extends EnvShape, S> = {
157
+ readonly [I in keyof S]: S[I] extends keyof Env ? S[I] : never
158
+ }
159
+
160
+ /**
161
+ * One select entry's answer-column fragment: the column is named by the
162
+ * variable it projects or folds (`count` names its column `count`), and
163
+ * its type reflects the entry — `count`/`countDistinct` are `bigint`
164
+ * whatever they counted, folds carry their input's type, the measure is
165
+ * `bigint`, the Arg forms carry the payload's type, `pack` its interval
166
+ * type.
167
+ */
168
+ type SelectEntryRow<Env extends EnvShape, E> = E extends string
169
+ ? { readonly [K in E]: Infer<Env[E & keyof Env]> }
170
+ : E extends Duration<infer N extends string>
171
+ ? { readonly [K in N]: bigint }
172
+ : E extends Agg<"count", undefined>
173
+ ? { readonly count: bigint }
174
+ : E extends Agg<"countDistinct", infer O extends string>
175
+ ? { readonly [K in O]: bigint }
176
+ : E extends Agg<"sum" | "min" | "max", infer O>
177
+ ? O extends string
178
+ ? { readonly [K in O]: Infer<Env[O & keyof Env]> }
179
+ : O extends Duration<infer N extends string>
180
+ ? { readonly [K in N]: bigint }
181
+ : never
182
+ : E extends Agg<"argMax" | "argMin", infer O extends string, string>
183
+ ? { readonly [K in O]: Infer<Env[O & keyof Env]> }
184
+ : E extends Agg<"pack", infer O extends string>
185
+ ? { readonly [K in O]: Infer<Env[O & keyof Env]> }
186
+ : never
187
+
188
+ /** The inferred answer-row object type of a select tuple. */
189
+ type RowOfSelect<Env extends EnvShape, S extends readonly SelectEntry[]> = ShapeOf<SelectEntryRow<Env, S[number]>>
190
+
191
+ /**
192
+ * One projected name's answer-column fragment — a NAKED parameter, so the
193
+ * judgment distributes per name (the union of a multi-name select never
194
+ * smears into one column's type), mirroring {@link SelectEntryRow}.
195
+ */
196
+ type NameSelectRow<Env extends EnvShape, N> = N extends string
197
+ ? { readonly [K in N]: Infer<Env[K & keyof Env]> }
198
+ : never
199
+
200
+ /** The inferred answer-row object type of a names-only (recursive-rule) select tuple. */
201
+ type RowOfNameSelect<Env extends EnvShape, S extends readonly string[]> = ShapeOf<NameSelectRow<Env, S[number]>>
202
+
203
+ export type {
204
+ Agg,
205
+ AggOpName,
206
+ AnyAgg,
207
+ CheckNameSelect,
208
+ CheckSelect,
209
+ RowOfNameSelect,
210
+ RowOfSelect,
211
+ SelectEntry,
212
+ SelectEntryOk,
213
+ SelectEntryRow
214
+ }
215
+ export { argMax, argMin, count, countDistinct, max, min, pack, sum }
package/src/relation.ts CHANGED
@@ -1,31 +1,30 @@
1
1
  /**
2
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
- * field metadata (declaration order = ordinal ids, the macro's law), typed
5
- * field references (`R.fields.holder`), and — since selections are the
6
- * relation's own vocabulary — `where()`, which resolves a selection into
7
- * lowered bindings eagerly (handles re-verified against their roster at
4
+ * field descriptors (declaration order = ordinal ids, the macro's law),
5
+ * typed field references (`R.fields.holder`), and — since selections are
6
+ * the relation's own vocabulary — `where()`, which resolves a selection
7
+ * into lowered bindings eagerly (handles verified against their roster at
8
8
  * construction). `Fact<>`/`InsertFact<>` are the inferred row object
9
- * types: fresh fields are optional on insert input (omit-to-mint) and
10
- * present on read (resupply-to-preserve-identity), typed exactly.
9
+ * types at BARE structural value types (no brands): fresh fields are
10
+ * optional on insert input (omit-to-mint) and present on read
11
+ * (resupply-to-preserve-identity), typed exactly.
11
12
  */
12
13
 
13
14
  import * as errors from "@superbuilders/errors"
14
- import { phantom } from "#brand.ts"
15
15
  import type { OneOf } from "#face.ts"
16
- import { type AnyField, assertDeclarationOrderKey, type FieldData, type FieldValue, literalOf } from "#fields.ts"
16
+ import { type AnyField, assertDeclarationOrderKey, type Infer, literalOf } from "#fields.ts"
17
17
  import type { LiteralSetSpec, LiteralSpec } from "#spec.ts"
18
18
 
19
19
  /** Flattens an intersection into one displayed object type (hover legibility). */
20
20
  type Flatten<T> = { [K in keyof T]: T[K] }
21
21
 
22
22
  /**
23
- * The one nominal step of `relation()`: the reference record is built by
23
+ * The one trusted seam of `relation()`: the reference record is built by
24
24
  * iterating the declared fields, and this guard verifies the checkable
25
25
  * facts — one reference per declared field, each carrying its own name —
26
- * before the record is admitted as the typed {@link FieldRefs}. The
27
- * phantom halves (brands) are carried by construction; this is the
28
- * module's single trusted seam, the macro-emission analog.
26
+ * before the record is admitted as the typed {@link FieldRefs} (the
27
+ * macro-emission analog).
29
28
  */
30
29
  function refsComplete<RName extends string, Fields extends FieldsShape>(
31
30
  refs: Record<string, unknown>,
@@ -44,7 +43,7 @@ function refsComplete<RName extends string, Fields extends FieldsShape>(
44
43
  * `oneOf` signature — the one-element set is unwritable); anything else is
45
44
  * the bare literal.
46
45
  */
47
- function resolveEntry(field: FieldData, entry: unknown): LiteralSetSpec {
46
+ function resolveEntry(field: AnyField, entry: unknown): LiteralSetSpec {
48
47
  if (typeof entry === "object" && entry !== null && "literals" in entry && Array.isArray(entry.literals)) {
49
48
  const literals: LiteralSpec[] = entry.literals.map(function lowerSetLiteral(literal: unknown) {
50
49
  return Object.freeze(literalOf(field, literal))
@@ -86,29 +85,29 @@ function resolveSelection(
86
85
  return Object.freeze(bindings)
87
86
  }
88
87
 
89
- /** The field block of a relation: field name to field constructor value. */
88
+ /** The field block of a relation: field name to field descriptor. */
90
89
  type FieldsShape = Record<string, AnyField>
91
90
 
92
91
  /**
93
92
  * A typed field reference (`Account.fields.holder`) — the value statements,
94
- * selections, and queries address a field through; its hover shows the
95
- * field's brand in the phantom position.
93
+ * selections, and queries address a field through. Purely positional
94
+ * (relation name + field name); the field's descriptor (domain label
95
+ * included) is read off the relation's schema type structurally.
96
96
  */
97
- interface FieldRef<Rel extends string, Name extends string, V> {
97
+ interface FieldRef<Rel extends string, Name extends string> {
98
98
  readonly relation: Rel
99
99
  readonly field: Name
100
- readonly [phantom]?: V
101
100
  }
102
101
 
103
102
  /** The typed field-reference record of a relation. */
104
103
  type FieldRefs<RName extends string, Fields extends FieldsShape> = {
105
- readonly [K in keyof Fields & string]: FieldRef<RName, K, FieldValue<Fields[K]>>
104
+ readonly [K in keyof Fields & string]: FieldRef<RName, K>
106
105
  }
107
106
 
108
- /** One declared field: name plus its runtime description, in declaration order. */
107
+ /** One declared field: name plus its descriptor, in declaration order. */
109
108
  interface RelationField {
110
109
  readonly name: string
111
- readonly field: FieldData
110
+ readonly field: AnyField
112
111
  }
113
112
 
114
113
  /** A relation's runtime description. */
@@ -128,15 +127,14 @@ interface SelectionBinding {
128
127
  }
129
128
 
130
129
  /**
131
- * The `where()` argument: per field, a branded literal of that field's
132
- * type (a closed handle constant IS such a literal — it carries the closed
133
- * relation's brand, so it is legal exactly where the field is that closed
134
- * relation's id type), an `oneOf(a, b, ...)` literal set, or a `span(start,
135
- * end)` interval literal. Equality-only by construction: no operator
136
- * parameter exists anywhere.
130
+ * The `where()` argument: per field, a bare structural literal of that
131
+ * field's value type (a closed handle constant IS such a literal — a
132
+ * bigint verified against the roster at construction), an `oneOf(a, b,
133
+ * ...)` literal set, or a `span(start, end)` interval literal.
134
+ * Equality-only by construction: no operator parameter exists anywhere.
137
135
  */
138
136
  type SelectionInput<Fields extends FieldsShape> = {
139
- readonly [K in keyof Fields]?: FieldValue<Fields[K]> | OneOf<FieldValue<Fields[K]>>
137
+ readonly [K in keyof Fields]?: Infer<Fields[K]> | OneOf<Infer<Fields[K]>>
140
138
  }
141
139
 
142
140
  /** A relation with a selection applied — what `on()` consumes as a σ-carrying source. */
@@ -163,25 +161,21 @@ interface AnySelected {
163
161
  }
164
162
 
165
163
  /** Extracts a relation's field block type. */
166
- type RelationFields<R extends AnyRelation> = R extends Relation<string, infer F> ? F : never
164
+ type RelationFields<R extends AnyRelation> = R extends Relation<string, infer F extends FieldsShape> ? F : never
167
165
 
168
166
  /**
169
167
  * The inferred row object type of a relation as READ: every field present,
170
- * branded. Closed relations have no `Fact` they are unwritable, and the
171
- * type constraint refuses them because a closed value lacks the relation
172
- * shape.
168
+ * at its BARE structural value type ({@link Infer}). Closed relations have
169
+ * no `Fact` they are unwritable, and the type constraint refuses them
170
+ * because a closed value lacks the relation shape.
173
171
  */
174
172
  type Fact<R extends AnyRelation> = {
175
- [K in keyof RelationFields<R>]: FieldValue<RelationFields<R>[K]>
173
+ [K in keyof RelationFields<R>]: Infer<RelationFields<R>[K]>
176
174
  }
177
175
 
178
- /** The field names of `R` that carry the fresh mint mark. */
176
+ /** The field names of `R` whose descriptor type carries the fresh mint mark. */
179
177
  type FreshKeys<R extends AnyRelation> = {
180
- [K in keyof RelationFields<R>]: RelationFields<R>[K] extends {
181
- readonly data: { readonly minted: true }
182
- }
183
- ? K
184
- : never
178
+ [K in keyof RelationFields<R>]: RelationFields<R>[K] extends { readonly fresh: true } ? K : never
185
179
  }[keyof RelationFields<R>]
186
180
 
187
181
  /**
@@ -193,10 +187,10 @@ type InsertFact<R extends AnyRelation> = Flatten<Omit<Fact<R>, FreshKeys<R>> & P
193
187
 
194
188
  /**
195
189
  * Declares one relation: `relation("Account", { id: AccountId.fresh,
196
- * holder: HolderId, ... })` — every field references a declared newtype
197
- * (`const AccountId = u64.newtype("AccountId")`) or a bare constructor.
198
- * Field declaration order is ordinal-id order (macro parity); the returned
199
- * value is frozen and side-effect free.
190
+ * holder: HolderId, ... })` — every field references a declared
191
+ * domain-labeled descriptor (`const AccountId = u64.as("AccountId")`) or a
192
+ * bare constructor. Field declaration order is ordinal-id order (macro
193
+ * parity); the returned value is frozen and side-effect free.
200
194
  */
201
195
  function relation<const Name extends string, Fields extends FieldsShape>(
202
196
  name: Name,
@@ -205,7 +199,7 @@ function relation<const Name extends string, Fields extends FieldsShape>(
205
199
  const ordered: RelationField[] = []
206
200
  for (const [fieldName, field] of Object.entries(fields)) {
207
201
  assertDeclarationOrderKey(`relation ${name} field`, fieldName)
208
- ordered.push(Object.freeze({ name: fieldName, field: field.data }))
202
+ ordered.push(Object.freeze({ name: fieldName, field }))
209
203
  }
210
204
  const data: RelationData = Object.freeze({ name, fields: Object.freeze(ordered) })
211
205
  const refs: Record<string, unknown> = {}
package/src/schema.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  import * as errors from "@superbuilders/errors"
13
13
  import type { AnyClosed } from "#closed.ts"
14
14
  import type { FaceData } from "#face.ts"
15
- import { assertDeclarationOrderKey, type FieldData } from "#fields.ts"
15
+ import { type AnyField, assertDeclarationOrderKey, type ClosedRoster } from "#fields.ts"
16
16
  import type { AnyRelation } from "#relation.ts"
17
17
  import type { LiteralSetSpec, LiteralSpec } from "#spec.ts"
18
18
  import { renderStatement, type Statement } from "#statements.ts"
@@ -38,7 +38,7 @@ function collectImplied(name: string, relations: SchemaRelations): Set<string> {
38
38
  continue
39
39
  }
40
40
  for (const declared of member.data.fields) {
41
- if (declared.field.minted) {
41
+ if ("fresh" in declared.field && declared.field.fresh === true) {
42
42
  implied.add(`${member.name}(${declared.name}) -> ${member.name}`)
43
43
  }
44
44
  }
@@ -74,8 +74,8 @@ function verifyMembership(name: string, relations: SchemaRelations, statement: S
74
74
  }
75
75
  }
76
76
 
77
- /** Finds a face's field description by name, across both relation kinds. */
78
- function faceField(face: FaceData, fieldName: string): FieldData | undefined {
77
+ /** Finds a face's field descriptor by name, across both relation kinds. */
78
+ function faceField(face: FaceData, fieldName: string): AnyField | undefined {
79
79
  const data = face.owner.data
80
80
  if ("handles" in data) {
81
81
  const column = data.columns.find(function byName(candidate) {
@@ -89,6 +89,18 @@ function faceField(face: FaceData, fieldName: string): FieldData | undefined {
89
89
  return declared?.field
90
90
  }
91
91
 
92
+ /**
93
+ * The roster a field resolves handles through: present exactly on a closed
94
+ * reference descriptor (the structural `closed` property — S1's
95
+ * `ClosedIdField`), absent on every other field kind.
96
+ */
97
+ function rosterOf(field: AnyField | undefined): ClosedRoster | undefined {
98
+ if (field !== undefined && "closed" in field) {
99
+ return field.closed
100
+ }
101
+ return undefined
102
+ }
103
+
92
104
  /** Flattens one binding's literal set into its literals. */
93
105
  function bindingLiterals(set: LiteralSetSpec): readonly LiteralSpec[] {
94
106
  if (set.kind === "one") {
@@ -109,20 +121,18 @@ function verifyBindingHandles(
109
121
  binding: { readonly field: string; readonly set: LiteralSetSpec },
110
122
  rendered: string
111
123
  ): void {
112
- const field = faceField(face, binding.field)
124
+ const roster = rosterOf(faceField(face, binding.field))
113
125
  for (const literal of bindingLiterals(binding.set)) {
114
126
  if (literal.kind !== "handle") {
115
127
  continue
116
128
  }
117
- if (field?.closed === undefined) {
129
+ if (roster === undefined) {
118
130
  throw errors.new(
119
- `schema ${name}: ${face.owner.name}.${binding.field} is not a closed-relation reference — the handle literal ${literal.handle} is legal only on a field whose newtype is a closed relation's handle newtype — ${rendered}`
131
+ `schema ${name}: ${face.owner.name}.${binding.field} is not a closed-relation reference — the handle literal ${literal.handle} is legal only on a field whose domain is a closed relation's handle domain — ${rendered}`
120
132
  )
121
133
  }
122
- if (!field.closed.handles.includes(literal.handle)) {
123
- throw errors.new(
124
- `schema ${name}: closed relation ${field.closed.name} has no handle ${literal.handle} — ${rendered}`
125
- )
134
+ if (!roster.handles.includes(literal.handle)) {
135
+ throw errors.new(`schema ${name}: closed relation ${roster.name} has no handle ${literal.handle} — ${rendered}`)
126
136
  }
127
137
  }
128
138
  }
@@ -216,7 +226,7 @@ function verifyClosedReferenceBinding(
216
226
  if (!spellsHandle) {
217
227
  return
218
228
  }
219
- const roster = faceField(face, binding.field)?.closed
229
+ const roster = rosterOf(faceField(face, binding.field))
220
230
  if (roster === undefined) {
221
231
  return
222
232
  }
package/src/spec.ts CHANGED
@@ -95,9 +95,10 @@ type WindowSpec =
95
95
  | { readonly kind: "floor"; readonly lo: bigint }
96
96
 
97
97
  /**
98
- * One field: name, structural type, host newtype name (carried for handle
99
- * resolution only dropped by the engine at descriptor lowering), and the
100
- * `fresh` mint mark.
98
+ * One field: name, structural type, host newtype name the descriptor's
99
+ * DOMAIN label (the macro's `as NewType`; the SDK's `.as("Domain")`),
100
+ * carried for handle resolution only, dropped by the engine at descriptor
101
+ * lowering and never fingerprinted — and the `fresh` mint mark.
101
102
  */
102
103
  interface FieldSpec {
103
104
  readonly name: string
@@ -120,8 +121,8 @@ interface RowSpec {
120
121
  * kind); a closed relation's `fields` are its declared intrinsic columns
121
122
  * only — the synthetic (`id`, u64) handle field is materialized by the
122
123
  * engine's schema validation. `newtype` is the handle newtype of a closed
123
- * relation (the SDK uses the closed relation's own name), undefined on an
124
- * ordinary one.
124
+ * relation (the SDK emits its handle DOMAIN, `` `${name}Id` `` the same
125
+ * label every referencing field carries), undefined on an ordinary one.
125
126
  */
126
127
  interface RelationSpec {
127
128
  readonly name: string