@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,102 +1,143 @@
1
1
  /**
2
- * Query scope terms (PRD-08): typed variables and parameters, declared
3
- * inside a `query()` scope callback so identity is lexical. A `Var` is
4
- * typed by the field it is declared from and joins are brand-equal by
5
- * construction (the nominal join discipline); var identity is OBJECT
6
- * identity two `$.var` calls are two variables even from the same field,
7
- * and no name-collision machinery exists. Params are query-global, carry a
8
- * mandatory literal name (the key of `execute`'s params object), and lower
9
- * to dense positional `ParamId`s in declaration order. This module also
10
- * owns the type-level params extraction: every atom and condition value
11
- * carries its contributed params object in a phantom, and the query's
12
- * `Params` type is the flattened intersection over the returned rules
13
- * inference rides the return value, never a mutable type accumulator.
2
+ * Query scope terms, STRUCTURAL edition: string-named variables and
3
+ * parameters as plain frozen values. A `Var` is a NAME it is typed by the
4
+ * field it first binds (structurally, off the schema type, through the rule
5
+ * builder's environment), reuse of the name within one rule IS the join,
6
+ * and a domain-mismatched reuse is a compile error (the structural analog
7
+ * of the old brand-equal join now domain-equal, no value brands
8
+ * anywhere). Params are query-global by name and typed BY USE: the field
9
+ * position or comparison sibling that anchors a param types it, the
10
+ * query's inferred `Params` object is exactly the params the rules use,
11
+ * and a param value that no rule uses simply never registers — the query
12
+ * executes under its own inferred type (the bug-hunt law). This module
13
+ * also owns the environment/typing utilities the whole surface shares:
14
+ * the env shape (var name → field descriptor), the domain-equality
15
+ * judgment, and the record-folding helpers `Params` and `Row` inference
16
+ * ride.
14
17
  */
15
18
 
16
- import * as errors from "@superbuilders/errors"
17
- import { phantom } from "#brand.ts"
18
- import type { AnyClosed } from "#closed.ts"
19
- import type { FieldData } from "#fields.ts"
20
- import type { PredicateData } from "#query/predicate.ts"
21
- import type { AnySchema, SchemaRelation } from "#schema.ts"
19
+ import type { AnyField, Infer } from "#fields.ts"
22
20
 
23
21
  /**
24
22
  * The runtime discriminant of query term values. Host literals (bigints,
25
- * strings, interval objects, `oneOf` sets) never carry it, so "is this
26
- * binding a term or a literal" is one property probe, never a guess.
23
+ * strings, interval objects) never carry it, so "is this position a term
24
+ * or a literal" is one property probe, never a guess.
27
25
  */
28
26
  const term: unique symbol = Symbol("bumbledb.query.term")
29
27
 
30
28
  /**
31
- * A query variable, typed by the field it was declared from
32
- * (`$.var(Holder.fields.id)` `Var<Brand<bigint, "HolderId">>`). Usable in
33
- * any atom position whose field carries the same brand; a brand-mismatched
34
- * placement is a TYPE error. Identity is object identity.
29
+ * The carrier of a value's INFERRED types (a rule's row/params, a query's
30
+ * row/params, a rec's params). The property is never present at runtime —
31
+ * it exists so inference rides plain values without any brand on any
32
+ * field value.
35
33
  */
36
- interface Var<V> {
34
+ const inferred: unique symbol = Symbol("bumbledb.query.inferred")
35
+
36
+ /**
37
+ * A query variable — a NAME. Its type comes from the field it first binds
38
+ * in the rule (the builder's environment); reusing the name joins, and a
39
+ * cross-domain reuse is a compile error. Identity is the name, strictly
40
+ * rule-scoped: the same name in two rules names two unrelated variables
41
+ * (exactly as the IR scopes `VarId`).
42
+ */
43
+ interface Var<Name extends string = string> {
37
44
  readonly [term]: "var"
38
- readonly relation: string
39
- readonly field: string
40
- readonly data: FieldData
41
- readonly [phantom]?: V
45
+ readonly name: Name
42
46
  }
43
47
 
44
48
  /**
45
- * A scalar query parameter — `$.param("root", Holder.fields.id)`. The name
46
- * literal is the key of the typed params object `execute` takes; the value
47
- * is marshaled by the declaring field's structural type at bind.
49
+ * A scalar query parameter — `r.param("root")`. The name is the key of the
50
+ * typed params object `execute` takes; the type is the element type of the
51
+ * position that anchors it (a field binding, or the bound-variable side of
52
+ * a comparison).
48
53
  */
49
- interface Param<Name extends string, V> {
54
+ interface Param<Name extends string = string> {
50
55
  readonly [term]: "param"
51
56
  readonly name: Name
52
- readonly relation: string
53
- readonly field: string
54
- readonly data: FieldData
55
- readonly [phantom]?: V
56
57
  }
57
58
 
58
59
  /**
59
- * A set-valued query parameter (the IR's `ParamSet` term): bound at
60
- * execution to an ARRAY of values of the declaring field's type; a binding
61
- * position matches iff the field value is in the set. Legal in atom
62
- * bindings (positive and negated) and as the right side of `is` — nowhere
63
- * else, exactly as the IR rules it.
60
+ * A set-valued query parameter (the IR's `ParamSet` term) `r.inSet("frontier")`:
61
+ * bound at execution to a readonly ARRAY of values of the anchoring field's
62
+ * type; a binding position matches iff the field value is in the set. Legal
63
+ * in atom bindings (positive and negated) and as the right side of `eq` —
64
+ * nowhere else, exactly as the IR rules it.
64
65
  */
65
- interface ParamSet<Name extends string, V> {
66
- readonly [term]: "paramSet"
66
+ interface SetParam<Name extends string = string> {
67
+ readonly [term]: "setParam"
67
68
  readonly name: Name
68
- readonly relation: string
69
- readonly field: string
70
- readonly data: FieldData
71
- readonly [phantom]?: V
72
69
  }
73
70
 
74
71
  /**
75
- * An Allen-mask parameter (the IR's `MaskTerm::Param`): the temporal
76
- * relation as a bind-time argument one prepared query answers any of the
72
+ * An Allen-mask parameter (the IR's `MaskTerm::Param`) the temporal
73
+ * relation as a bind-time argument: one prepared query answers any of the
77
74
  * mask questions per execution. Bound to a 13-bit mask number built from
78
75
  * the `ALLEN` constants.
79
76
  */
80
- interface MaskParam<Name extends string> {
77
+ interface MaskParam<Name extends string = string> {
81
78
  readonly [term]: "maskParam"
82
79
  readonly name: Name
83
80
  }
84
81
 
85
- /** Any query variable, whatever its brand. */
86
- type AnyVar = Var<unknown>
87
-
88
- /** Any parameter term of the scope, whatever its name and brand. */
89
- type AnyParamTerm = Param<string, unknown> | ParamSet<string, unknown> | MaskParam<string>
82
+ /**
83
+ * The measure of an interval-typed variable (`ir::Term::Measure`):
84
+ * `|[s, e)| = e − s`, u64 — legal as one side of an order comparison, as a
85
+ * select entry, and as the input of `sum`/`min`/`max`; every other position
86
+ * is unwritable, exactly as the IR rejects it typed. A ray has no finite
87
+ * measure — the engine's `MeasureOfRay` execution error; exclude rays first
88
+ * (`allen` against a bounded window).
89
+ */
90
+ interface Duration<Name extends string = string> {
91
+ readonly [term]: "duration"
92
+ readonly name: Name
93
+ }
90
94
 
91
95
  /** Any scope term value. */
92
- type AnyTerm = AnyVar | AnyParamTerm
96
+ type AnyTerm = Var | Param | SetParam | MaskParam | Duration
93
97
 
94
- /** Narrows an unknown binding value to a scope term (vs a host literal). */
98
+ /** Narrows an unknown position value to a scope term (vs a host literal). */
95
99
  function isTerm(value: unknown): value is AnyTerm {
96
100
  return typeof value === "object" && value !== null && term in value
97
101
  }
98
102
 
99
- /** A params object type — what `execute` takes and the phantoms carry. */
103
+ /** Builds one variable term. */
104
+ function makeVar<const Name extends string>(name: Name): Var<Name> {
105
+ const value: Var<Name> = { [term]: "var", name }
106
+ return Object.freeze(value)
107
+ }
108
+
109
+ /** Builds one scalar-parameter term. */
110
+ function makeParam<const Name extends string>(name: Name): Param<Name> {
111
+ const value: Param<Name> = { [term]: "param", name }
112
+ return Object.freeze(value)
113
+ }
114
+
115
+ /** Builds one set-parameter term. */
116
+ function makeSetParam<const Name extends string>(name: Name): SetParam<Name> {
117
+ const value: SetParam<Name> = { [term]: "setParam", name }
118
+ return Object.freeze(value)
119
+ }
120
+
121
+ /** Builds one Allen-mask-parameter term. */
122
+ function makeMaskParam<const Name extends string>(name: Name): MaskParam<Name> {
123
+ const value: MaskParam<Name> = { [term]: "maskParam", name }
124
+ return Object.freeze(value)
125
+ }
126
+
127
+ /** Builds one measure term over an interval-typed variable's name. */
128
+ function makeDuration<const Name extends string>(name: Name): Duration<Name> {
129
+ const value: Duration<Name> = { [term]: "duration", name }
130
+ return Object.freeze(value)
131
+ }
132
+
133
+ /**
134
+ * A rule's typing environment: variable name → the field descriptor it
135
+ * first bound. Purely a TYPE — the runtime twin is the rule's `varFields`
136
+ * record, and the two are built by the same walk.
137
+ */
138
+ type EnvShape = Record<string, AnyField>
139
+
140
+ /** A params object type — what `execute` takes and inference carries. */
100
141
  type ParamsRecord = Readonly<Record<string, unknown>>
101
142
 
102
143
  /** Flattens an intersection into one displayed object type (hover legibility). */
@@ -108,194 +149,123 @@ type UnionToIntersection<U> = (U extends unknown ? (member: U) => void : never)
108
149
  : never
109
150
 
110
151
  /**
111
- * One term's contribution to the query's params object type: a `Param`
112
- * contributes its value type under its name, a `ParamSet` the readonly
113
- * array of it, a `MaskParam` a mask number; everything else contributes
114
- * nothing.
152
+ * Folds a union of per-position record fragments into one flattened record
153
+ * (the machinery both `Params` and `Row` inference ride).
115
154
  */
116
- type TermContribution<T> = T extends {
117
- readonly [term]: "param"
118
- readonly name: infer N extends string
119
- readonly [phantom]?: infer V
120
- }
121
- ? { readonly [K in N]: Exclude<V, undefined> }
122
- : T extends {
123
- readonly [term]: "paramSet"
124
- readonly name: infer N extends string
125
- readonly [phantom]?: infer V
126
- }
127
- ? { readonly [K in N]: readonly Exclude<V, undefined>[] }
128
- : T extends { readonly [term]: "maskParam"; readonly name: infer N extends string }
129
- ? { readonly [K in N]: number }
130
- : Record<never, never>
155
+ type ShapeOf<U> = [U] extends [never] ? Record<never, never> : Flatten<UnionToIntersection<U>>
131
156
 
132
- /**
133
- * Folds a union of per-term/per-item params objects into the one flattened
134
- * params record (the query's `Params` type).
135
- */
136
- type ParamsShape<U> = [U] extends [never] ? Record<never, never> : Flatten<UnionToIntersection<U>>
157
+ /** Reads a field descriptor's domain label (S1: the label IS the domain check). */
158
+ type DomainOf<F extends AnyField> = F["domain"]
137
159
 
138
- /** Reads an atom's/condition's contributed params object off its phantom. */
139
- type ItemParams<T> = T extends { readonly [phantom]?: infer P } ? Exclude<P, undefined> : Record<never, never>
160
+ /** Reads a field descriptor's width label (`bytes<N>`, `interval<E, W>`); `undefined` when the kind carries none. */
161
+ type WidthOf<F extends AnyField> = F extends { readonly width: infer W } ? W : undefined
140
162
 
141
- /** One registered parameter: name, wire shape, and the declaring field. */
142
- interface ParamEntry {
143
- readonly name: string
144
- readonly shape: "value" | "set" | "mask"
145
- readonly data: FieldData | undefined
146
- }
163
+ /** Reads an interval descriptor's element kind; `undefined` on scalar kinds. */
164
+ type ElementOf<F extends AnyField> = F extends { readonly element: infer E } ? E : undefined
147
165
 
148
166
  /**
149
- * The mutable build-time registry one `query()` scope owns: declared vars
150
- * (membership polices cross-scope smuggling), params in declaration order
151
- * (= dense `ParamId`s), and declared predicates in declaration order
152
- * (= dense `PredId`s; the output predicate is appended by lowering).
167
+ * The structural join judgment: two field descriptors join iff kind,
168
+ * domain label, width label, and interval element all agree — the
169
+ * string-literal comparison of descriptor shapes that replaced the value
170
+ * brand (design ruling 3).
153
171
  */
154
- interface QueryRegistry {
155
- readonly theory: AnySchema
156
- readonly vars: Set<AnyVar>
157
- readonly params: ParamEntry[]
158
- readonly paramIndex: Map<AnyTerm, number>
159
- readonly predicates: PredicateData[]
160
- }
161
-
162
- /** Creates one empty scope registry over the query's theory. */
163
- function createRegistry(theory: AnySchema): QueryRegistry {
164
- return {
165
- theory,
166
- vars: new Set(),
167
- params: [],
168
- paramIndex: new Map(),
169
- predicates: []
170
- }
171
- }
172
+ type JoinOk<A extends AnyField, B extends AnyField> = [A["kind"], DomainOf<A>, WidthOf<A>, ElementOf<A>] extends [
173
+ B["kind"],
174
+ DomainOf<B>,
175
+ WidthOf<B>,
176
+ ElementOf<B>
177
+ ]
178
+ ? [B["kind"], DomainOf<B>, WidthOf<B>, ElementOf<B>] extends [A["kind"], DomainOf<A>, WidthOf<A>, ElementOf<A>]
179
+ ? true
180
+ : false
181
+ : false
172
182
 
173
183
  /**
174
- * The relation-kind discriminant: a closed relation's runtime description
175
- * carries its handle roster, an ordinary relation's never does.
184
+ * The runtime twin of {@link JoinOk}: two field descriptors join iff kind,
185
+ * domain label, width label, and interval element all agree — the same
186
+ * structural comparison the type tier makes, judged on the descriptor
187
+ * VALUES (S1 descriptors are honest at runtime). The rule builders throw
188
+ * through this on a domain-unequal variable reuse, so the wall holds for
189
+ * untyped callers too, not only where the compiler can see.
176
190
  */
177
- function isClosedMember(member: SchemaRelation): member is AnyClosed {
178
- return "handles" in member.data
191
+ function fieldJoins(a: AnyField, b: AnyField): boolean {
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
+ return a.kind === b.kind && a.domain === b.domain && widthA === widthB && elementA === elementB
179
197
  }
180
198
 
181
199
  /**
182
- * Resolves a field reference's runtime description through the schema
183
- * the seam that types `$.var`/`$.param` declarations at runtime (the type
184
- * level already carries the brand; this recovers the structural type the
185
- * lowering and the param marshaler direct by).
200
+ * Renders one field descriptor for join-mismatch diagnostics the schema
201
+ * grammar's own spelling (`u64 as HolderId`, `interval<i64, 7> as Window`).
186
202
  */
187
- function resolveFieldData(theory: AnySchema, relationName: string, fieldName: string): FieldData {
188
- const member: SchemaRelation | undefined = theory.relations[relationName]
189
- if (member === undefined) {
190
- throw errors.new(`schema ${theory.name} has no relation ${relationName}`)
191
- }
192
- if (isClosedMember(member)) {
193
- if (fieldName === "id") {
194
- return member.id.data
195
- }
196
- const column = member.data.columns.find(function byName(candidate) {
197
- return candidate.name === fieldName
198
- })
199
- if (column === undefined) {
200
- throw errors.new(`closed relation ${relationName} has no column ${fieldName}`)
201
- }
202
- return column.field
203
- }
204
- const declared = member.data.fields.find(function byName(candidate) {
205
- return candidate.name === fieldName
206
- })
207
- if (declared === undefined) {
208
- throw errors.new(`relation ${relationName} has no field ${fieldName}`)
203
+ function renderFieldKind(field: AnyField): string {
204
+ let base: string = field.kind
205
+ if (field.kind === "bytes") {
206
+ base = `bytes<${field.width}>`
209
207
  }
210
- return declared.field
211
- }
212
-
213
- /** A field reference's runtime half, as the scope factories consume it. */
214
- interface RefNames {
215
- readonly relation: string
216
- readonly field: string
217
- }
218
-
219
- /** Declares one variable in the scope (the `$.var` implementation). */
220
- function scopeVar<V>(registry: QueryRegistry, ref: RefNames): Var<V> {
221
- const value: Var<V> = Object.freeze({
222
- [term]: "var" as const,
223
- relation: ref.relation,
224
- field: ref.field,
225
- data: resolveFieldData(registry.theory, ref.relation, ref.field)
226
- })
227
- registry.vars.add(value)
228
- return value
229
- }
230
-
231
- /** Rejects a second parameter under an already-taken name. */
232
- function assertFreshParamName(registry: QueryRegistry, name: string): void {
233
- const taken = registry.params.some(function byName(entry) {
234
- return entry.name === name
235
- })
236
- if (taken) {
237
- throw errors.new(
238
- `query scope already declares a param named ${name} — param names key the execute params object, one declaration each`
239
- )
208
+ if (field.kind === "interval") {
209
+ base = field.width === undefined ? `interval<${field.element}>` : `interval<${field.element}, ${field.width}>`
240
210
  }
211
+ return field.domain === undefined ? base : `${base} as ${field.domain}`
241
212
  }
242
213
 
243
- /** Declares one scalar parameter (the `$.param` implementation). */
244
- function scopeParam<Name extends string, V>(registry: QueryRegistry, name: Name, ref: RefNames): Param<Name, V> {
245
- assertFreshParamName(registry, name)
246
- const data = resolveFieldData(registry.theory, ref.relation, ref.field)
247
- const value: Param<Name, V> = Object.freeze({
248
- [term]: "param" as const,
249
- name,
250
- relation: ref.relation,
251
- field: ref.field,
252
- data
253
- })
254
- registry.paramIndex.set(value, registry.params.length)
255
- registry.params.push(Object.freeze({ name, shape: "value" as const, data }))
256
- return value
257
- }
214
+ /**
215
+ * What a PARAM anchored at field `F` accepts at execution: the field's
216
+ * bare value type, exactly. At an interval field the engine resolves the
217
+ * bivalent anchor to the INTERVAL reading (value equality) — the point
218
+ * reading of a param is spelled `pointIn(r.param(...), w)`, whose sibling
219
+ * anchors it element-typed.
220
+ */
221
+ type ParamValueAt<F extends AnyField> = Infer<F>
258
222
 
259
- /** Declares one set parameter (the `$.paramSet` implementation). */
260
- function scopeParamSet<Name extends string, V>(registry: QueryRegistry, name: Name, ref: RefNames): ParamSet<Name, V> {
261
- assertFreshParamName(registry, name)
262
- const data = resolveFieldData(registry.theory, ref.relation, ref.field)
263
- const value: ParamSet<Name, V> = Object.freeze({
264
- [term]: "paramSet" as const,
265
- name,
266
- relation: ref.relation,
267
- field: ref.field,
268
- data
269
- })
270
- registry.paramIndex.set(value, registry.params.length)
271
- registry.params.push(Object.freeze({ name, shape: "set" as const, data }))
272
- return value
273
- }
223
+ /** Reads a value's inferred-types carrier (rules, queries, recs). */
224
+ type InferredOf<T> = T extends { readonly [inferred]?: infer S } ? Exclude<S, undefined> : never
274
225
 
275
- /** Declares one Allen-mask parameter (the `$.allenParam` implementation). */
276
- function scopeAllenParam<Name extends string>(registry: QueryRegistry, name: Name): MaskParam<Name> {
277
- assertFreshParamName(registry, name)
278
- const value: MaskParam<Name> = Object.freeze({ [term]: "maskParam" as const, name })
279
- registry.paramIndex.set(value, registry.params.length)
280
- registry.params.push(Object.freeze({ name, shape: "mask" as const, data: undefined }))
281
- return value
226
+ /**
227
+ * One registered parameter of a query, as the wire marshal reads it: the
228
+ * name, the wire shape, the field descriptor (or the measure) that anchored
229
+ * it, and the comparison op the anchor came from (`"binding"` for atom
230
+ * positions) — the op keeps literal tagging op-aware at `pointIn`
231
+ * (the bug-hunt fix, preserved). `anchor` is `undefined` only on a query
232
+ * built but not yet anchored by any rule; lowering and the wire both refuse
233
+ * that state typed.
234
+ */
235
+ interface ParamEntry {
236
+ readonly name: string
237
+ readonly shape: "value" | "set" | "mask"
238
+ readonly anchor: AnyField | "measure" | undefined
239
+ readonly op: "binding" | "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "pointIn" | "allen"
282
240
  }
283
241
 
284
242
  export type {
285
- AnyParamTerm,
286
243
  AnyTerm,
287
- AnyVar,
244
+ DomainOf,
245
+ Duration,
246
+ EnvShape,
288
247
  Flatten,
289
- ItemParams,
248
+ InferredOf,
249
+ JoinOk,
290
250
  MaskParam,
291
251
  Param,
292
252
  ParamEntry,
293
- ParamSet,
294
253
  ParamsRecord,
295
- ParamsShape,
296
- QueryRegistry,
297
- TermContribution,
254
+ ParamValueAt,
255
+ SetParam,
256
+ ShapeOf,
298
257
  UnionToIntersection,
299
258
  Var
300
259
  }
301
- export { createRegistry, isTerm, resolveFieldData, scopeAllenParam, scopeParam, scopeParamSet, scopeVar, term }
260
+ export {
261
+ fieldJoins,
262
+ inferred,
263
+ isTerm,
264
+ makeDuration,
265
+ makeMaskParam,
266
+ makeParam,
267
+ makeSetParam,
268
+ makeVar,
269
+ renderFieldKind,
270
+ term
271
+ }