@bjornpagen/bumbledb 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capacity.d.ts +14 -1
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/order.d.ts +53 -2
- package/dist/order.d.ts.map +1 -1
- package/dist/order.js +30 -12
- package/dist/order.js.map +1 -1
- package/dist/query/atom.d.ts +100 -22
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +20 -7
- package/dist/query/atom.js.map +1 -1
- package/dist/query/find.d.ts +19 -8
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +6 -5
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +7 -10
- package/dist/query/lower.js.map +1 -1
- package/dist/statements.d.ts +7 -5
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +7 -0
- package/dist/statements.js.map +1 -1
- package/package.json +4 -4
- package/src/capacity.ts +24 -1
- package/src/index.ts +1 -1
- package/src/order.ts +83 -5
- package/src/query/atom.ts +186 -39
- package/src/query/find.ts +34 -16
- package/src/query/lower.ts +7 -11
- package/src/statements.ts +16 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bjornpagen/bumbledb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Type-theoretic TypeScript SDK for the bumbledb embedded relational engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -40,9 +40,6 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@superbuilders/errors": "^4.0.2"
|
|
42
42
|
},
|
|
43
|
-
"optionalDependencies": {
|
|
44
|
-
"@bjornpagen/bumbledb-darwin-arm64": "0.8.0"
|
|
45
|
-
},
|
|
46
43
|
"devDependencies": {
|
|
47
44
|
"@biomejs/biome": "^2.0.0-beta.5",
|
|
48
45
|
"@types/node": "^26.1.0",
|
|
@@ -55,6 +52,9 @@
|
|
|
55
52
|
"access": "public"
|
|
56
53
|
},
|
|
57
54
|
"private": false,
|
|
55
|
+
"optionalDependencies": {
|
|
56
|
+
"@bjornpagen/bumbledb-darwin-arm64": "0.10.0"
|
|
57
|
+
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "node scripts/build.ts",
|
|
60
60
|
"typecheck": "tsc --noEmit",
|
package/src/capacity.ts
CHANGED
|
@@ -196,6 +196,20 @@ type UnitWindowBan<W extends CapacityWindow> = W["window"] extends {
|
|
|
196
196
|
? BannedWindow<"`{1..*}` on the unit instance says only what the bare containment says — write contained(source, target)">
|
|
197
197
|
: unknown
|
|
198
198
|
|
|
199
|
+
/**
|
|
200
|
+
* The C18 dimension gate's ban row, unit instance (the engine's
|
|
201
|
+
* `CapacityDimensionMixing` twin, ruled 2026-07-24): a unit (count)
|
|
202
|
+
* window against a `duration()` bound counts facts against a span of
|
|
203
|
+
* time — a dimension error. Judged on the `capacity()` UNIT overload
|
|
204
|
+
* only: Duration weights pair with Duration-capable bounds, so the
|
|
205
|
+
* weighted overload takes the same window freely.
|
|
206
|
+
*/
|
|
207
|
+
type UnitDimensionBan<W extends CapacityWindow> = W["window"] extends {
|
|
208
|
+
readonly hi: { readonly kind: "durationField" }
|
|
209
|
+
}
|
|
210
|
+
? BannedWindow<"a count of facts bounded by a span of time mixes dimensions (C18) — weigh the source with weigh(duration(field)), or bound by a u64 field or literal">
|
|
211
|
+
: unknown
|
|
212
|
+
|
|
199
213
|
/** The projected kind of one field of a face's source, read off the schema type. */
|
|
200
214
|
type KindAt<S extends FaceSource, K extends string> =
|
|
201
215
|
ProjectedShape<S, K> extends readonly [infer Kind, ...unknown[]] ? Kind : undefined
|
|
@@ -450,5 +464,14 @@ function duration<const F extends string>(field: F & PathBan<F>): DurationRef<F>
|
|
|
450
464
|
return Object.freeze({ kind: "durationField", field: assertRowLocal(field, "Duration measure") }) as DurationRef<F>
|
|
451
465
|
}
|
|
452
466
|
|
|
453
|
-
export type {
|
|
467
|
+
export type {
|
|
468
|
+
BoundsOnTarget,
|
|
469
|
+
CapacityWeight,
|
|
470
|
+
CapacityWindow,
|
|
471
|
+
DurationRef,
|
|
472
|
+
FieldRef,
|
|
473
|
+
UnitDimensionBan,
|
|
474
|
+
UnitWindowBan,
|
|
475
|
+
WeightOnSource
|
|
476
|
+
}
|
|
454
477
|
export { duration, isCapacityWeight, isCapacityWindow, ref, unitWeight, weigh, within }
|
package/src/index.ts
CHANGED
|
@@ -119,7 +119,7 @@ export type {
|
|
|
119
119
|
Staleness,
|
|
120
120
|
StatementKindTag
|
|
121
121
|
} from "#native.ts"
|
|
122
|
-
export type { Desc, SortKey } from "#order.ts"
|
|
122
|
+
export type { Desc, EngineOrderable, SortKey } from "#order.ts"
|
|
123
123
|
export { by, desc } from "#order.ts"
|
|
124
124
|
|
|
125
125
|
export type {
|
package/src/order.ts
CHANGED
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
* `by(...)` into one row-typed comparator. Cross-type cells cannot arise
|
|
11
11
|
* within one column (one column, one domain), and the cell order is TOTAL
|
|
12
12
|
* anyway (the type-rank wall), so the comparator never throws.
|
|
13
|
+
*
|
|
14
|
+
* ZERO keys is the identity key (ruled 2026-07-25): `by()` / `desc()` are
|
|
15
|
+
* the same two spellings over BARE scalar arrays (`bigint[]` of ids, map
|
|
16
|
+
* keys) — with no column to name, the value itself is the key, there is
|
|
17
|
+
* nothing to fold, and the comparator IS the result. One ordering
|
|
18
|
+
* vocabulary, no sibling names. The identity arms are typed to EXACTLY the
|
|
19
|
+
* engine-orderable roster ({@link EngineOrderable}) because a bare scalar
|
|
20
|
+
* carries no decoded-row provenance — the type wall must carry the
|
|
21
|
+
* orderability law itself, where the row arm's cells arrive under their
|
|
22
|
+
* columns' law-typed proof.
|
|
13
23
|
*/
|
|
14
24
|
|
|
15
25
|
import type { FactValue } from "#native.ts"
|
|
@@ -23,6 +33,23 @@ interface Desc<K extends string> {
|
|
|
23
33
|
/** One sort key: a bare column name (ascending — the punning spelling) or `desc(name)`. */
|
|
24
34
|
type SortKey<K extends string> = K | Desc<K>
|
|
25
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The engine-orderable scalar roster in the SDK's representation — `bigint`
|
|
38
|
+
* (U64/I64) and `boolean` (false < true — the strict 0/1 encoding IS the
|
|
39
|
+
* order), EXACTLY as `docs/architecture/10-data-model.md` § "Orderability,
|
|
40
|
+
* complete" admits it (ruled 2026-07-23, R3/R4), and nothing more:
|
|
41
|
+
* `string` is deliberately absent (intern ids are meaningless to order — a
|
|
42
|
+
* typed refusal, not a gap) and `number` is not an engine scalar at all.
|
|
43
|
+
* The identity comparators `by()`/`desc()` constrain on this alias, so
|
|
44
|
+
* sorting a `string[]` or `number[]` through them is a COMPILE error naming
|
|
45
|
+
* this roster. ONE OWNER for scalar ordering semantics: both identity arms
|
|
46
|
+
* route through the same cell order the row arm uses, whose bigint and
|
|
47
|
+
* boolean arms mirror the engine's U64/I64/Bool order exactly — a host
|
|
48
|
+
* sort over these values can never disagree with an engine order judgment
|
|
49
|
+
* (`Lt`-family, `Min`/`Max`) over the same column.
|
|
50
|
+
*/
|
|
51
|
+
type EngineOrderable = bigint | boolean
|
|
52
|
+
|
|
26
53
|
/**
|
|
27
54
|
* The type-rank wall: boolean 0, bigint 1, string 2, bytes 3, interval 4.
|
|
28
55
|
* One column carries one domain, so a mixed pair never arises from decoded
|
|
@@ -116,8 +143,38 @@ function cellCmp(left: FactValue, right: FactValue): number {
|
|
|
116
143
|
return cellRank(left) - cellRank(right)
|
|
117
144
|
}
|
|
118
145
|
|
|
119
|
-
/**
|
|
120
|
-
|
|
146
|
+
/**
|
|
147
|
+
* The ascending identity comparator `by()` returns — one value, minted
|
|
148
|
+
* once (`by() === by()`), {@link cellCmp} narrowed to the engine-orderable
|
|
149
|
+
* roster (the one owner of the cell order).
|
|
150
|
+
*/
|
|
151
|
+
function identityAscending<T extends EngineOrderable>(left: T, right: T): number {
|
|
152
|
+
return cellCmp(left, right)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The descending identity comparator `desc()` returns — the same owner, sides flipped. */
|
|
156
|
+
function identityDescending<T extends EngineOrderable>(left: T, right: T): number {
|
|
157
|
+
return cellCmp(right, left)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The descending spelling — ONE name, both arities of one ordering
|
|
162
|
+
* vocabulary. `desc(name)` marks one sort key DESCENDING: plain data for
|
|
163
|
+
* {@link by} to fold (a bare name is already ascending). `desc()` — zero
|
|
164
|
+
* keys, the identity key (ruled 2026-07-25) — is the descending comparator
|
|
165
|
+
* over engine-orderable scalars themselves: a bare `bigint[]` or
|
|
166
|
+
* `boolean[]` has no column to name, so the value IS the key, there is
|
|
167
|
+
* nothing to fold, and the comparator is the result directly. The identity
|
|
168
|
+
* arm covers EXACTLY the {@link EngineOrderable} roster — `string` and
|
|
169
|
+
* `number` refuse at compile time, citing the orderability law
|
|
170
|
+
* (`10-data-model.md` § "Orderability, complete").
|
|
171
|
+
*/
|
|
172
|
+
function desc(): <T extends EngineOrderable>(left: T, right: T) => number
|
|
173
|
+
function desc<const K extends string>(key: K): Desc<K>
|
|
174
|
+
function desc<const K extends string>(key?: K): Desc<K> | (<T extends EngineOrderable>(left: T, right: T) => number) {
|
|
175
|
+
if (key === undefined) {
|
|
176
|
+
return identityDescending
|
|
177
|
+
}
|
|
121
178
|
const marker: Desc<K> = { key, desc: true }
|
|
122
179
|
return Object.freeze(marker)
|
|
123
180
|
}
|
|
@@ -130,12 +187,33 @@ function desc<const K extends string>(key: K): Desc<K> {
|
|
|
130
187
|
* carries that proof here (parse-don't-validate). The generic RETURN is the
|
|
131
188
|
* load-bearing trick: `rows.sort(by("rank"))` instantiates `Row` from the
|
|
132
189
|
* array's own element type and checks the key set right there.
|
|
190
|
+
*
|
|
191
|
+
* ZERO keys is the identity key (ruled 2026-07-25): `by()` is the
|
|
192
|
+
* ascending comparator over engine-orderable scalars themselves —
|
|
193
|
+
* `ids.sort(by())` — for the bare arrays (`bigint[]`, map keys) the
|
|
194
|
+
* row-typed arm cannot reach because they have no column to name. One
|
|
195
|
+
* vocabulary, both arities. The identity arm is typed to EXACTLY the
|
|
196
|
+
* {@link EngineOrderable} roster: with no law-typed column standing proof
|
|
197
|
+
* over the values, the type wall carries the orderability law itself —
|
|
198
|
+
* `string` ordering is deliberately refused and `number` is not an engine
|
|
199
|
+
* scalar (`10-data-model.md` § "Orderability, complete"), so both refuse
|
|
200
|
+
* at compile time and a host sort can never disagree with an engine order
|
|
201
|
+
* judgment.
|
|
133
202
|
*/
|
|
203
|
+
function by(): <T extends EngineOrderable>(left: T, right: T) => number
|
|
134
204
|
function by<const K extends string>(
|
|
135
205
|
first: SortKey<K>,
|
|
136
206
|
...rest: ReadonlyArray<SortKey<K>>
|
|
137
|
-
): <Row extends Readonly<Record<K, FactValue>>>(left: Row, right: Row) => number
|
|
138
|
-
|
|
207
|
+
): <Row extends Readonly<Record<K, FactValue>>>(left: Row, right: Row) => number
|
|
208
|
+
function by<const K extends string>(
|
|
209
|
+
...keys: ReadonlyArray<SortKey<K>>
|
|
210
|
+
):
|
|
211
|
+
| (<T extends EngineOrderable>(left: T, right: T) => number)
|
|
212
|
+
| (<Row extends Readonly<Record<K, FactValue>>>(left: Row, right: Row) => number) {
|
|
213
|
+
if (keys.length === 0) {
|
|
214
|
+
return identityAscending
|
|
215
|
+
}
|
|
216
|
+
const entries = keys.map(function normalizeKey(sortKey): { readonly key: K; readonly factor: 1 | -1 } {
|
|
139
217
|
if (typeof sortKey === "string") {
|
|
140
218
|
return { key: sortKey, factor: 1 }
|
|
141
219
|
}
|
|
@@ -152,5 +230,5 @@ function by<const K extends string>(
|
|
|
152
230
|
}
|
|
153
231
|
}
|
|
154
232
|
|
|
155
|
-
export type { Desc, SortKey }
|
|
233
|
+
export type { Desc, EngineOrderable, SortKey }
|
|
156
234
|
export { by, desc }
|
package/src/query/atom.ts
CHANGED
|
@@ -46,7 +46,7 @@ import type {
|
|
|
46
46
|
SetParam,
|
|
47
47
|
ShapeOf
|
|
48
48
|
} from "#query/scope.ts"
|
|
49
|
-
import { inferred, isTerm } from "#query/scope.ts"
|
|
49
|
+
import { inferred, isTerm, term } from "#query/scope.ts"
|
|
50
50
|
import type { FieldsShape } from "#relation.ts"
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -335,8 +335,8 @@ type EqRight = AnyVar | Param<string> | SetParam<string> | bigint | string | boo
|
|
|
335
335
|
/** What `ne`'s right side accepts. */
|
|
336
336
|
type NeRight = AnyVar | Param<string> | bigint | string | boolean | Uint8Array | IntervalValue
|
|
337
337
|
|
|
338
|
-
/** One side of an order comparison: orderable terms only (the IR's comparison rules). */
|
|
339
|
-
type OrderSide = AnyVar | Param<string> | Duration | bigint
|
|
338
|
+
/** One side of an order comparison: orderable terms only (the IR's comparison rules — bool orders, `false < true`, R3). */
|
|
339
|
+
type OrderSide = AnyVar | Param<string> | Duration | bigint | boolean
|
|
340
340
|
|
|
341
341
|
/** The point side of `pointIn`. */
|
|
342
342
|
type PointSide = AnyVar | Param<string> | bigint
|
|
@@ -350,14 +350,28 @@ function comparison<Op extends CmpKind, L, R, M>(op: Op, lhs: L, rhs: R, mask: M
|
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
/**
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
353
|
+
* Whether a comparison side is a VARIABLE side in the engine's sense — a
|
|
354
|
+
* bound variable or the measure. A param is a constant at execution, so a
|
|
355
|
+
* comparison whose every side is a param or literal is constant-valued:
|
|
356
|
+
* the engine's `ConstantComparison` conviction
|
|
357
|
+
* (`bumbledb/crates/bumbledb/src/ir/validate/context.rs`,
|
|
358
|
+
* `comparison_shape`'s last arm).
|
|
359
|
+
*/
|
|
360
|
+
function isVariableSide(value: unknown): boolean {
|
|
361
|
+
return isTerm(value) && (value[term] === "var" || value[term] === "duration")
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Rejects a comparison with no variable side: it is constant-valued —
|
|
366
|
+
* params included, a param is a constant the moment it binds — the
|
|
367
|
+
* engine's own validation refuses it (`ConstantComparison`), and the
|
|
368
|
+
* lowering has no anchored position to type the literals by — fail here
|
|
369
|
+
* with the same verdict.
|
|
356
370
|
*/
|
|
357
371
|
function assertTermSide(op: string, lhs: unknown, rhs: unknown): void {
|
|
358
|
-
if (!
|
|
372
|
+
if (!isVariableSide(lhs) && !isVariableSide(rhs)) {
|
|
359
373
|
throw errors.new(
|
|
360
|
-
`${op}: a comparison without a variable
|
|
374
|
+
`${op}: a comparison without a variable side is constant-valued (a parameter is a constant at execution) — write the query you mean`
|
|
361
375
|
)
|
|
362
376
|
}
|
|
363
377
|
}
|
|
@@ -388,7 +402,7 @@ function order<Op extends "lt" | "le" | "gt" | "ge", const L extends OrderSide,
|
|
|
388
402
|
return comparison(op, left, right, undefined)
|
|
389
403
|
}
|
|
390
404
|
|
|
391
|
-
/** Strict less-than (`ir::CmpOp::Lt`) — orderable sides only, never intervals/bytes/strings
|
|
405
|
+
/** Strict less-than (`ir::CmpOp::Lt`) — orderable sides only (bool included: `false < true`, R3), never intervals/bytes/strings. */
|
|
392
406
|
function lt<const L extends OrderSide, const R extends OrderSide>(left: L, right: R): Cmp<"lt", L, R> {
|
|
393
407
|
return order("lt", left, right)
|
|
394
408
|
}
|
|
@@ -531,34 +545,159 @@ function isRecTarget(value: MatchOwner | RecTarget): value is RecTarget {
|
|
|
531
545
|
}
|
|
532
546
|
|
|
533
547
|
/**
|
|
534
|
-
* Whether a variable's OWN field is
|
|
548
|
+
* Whether a variable's OWN field is NUMERIC (u64/i64) — the judgment the
|
|
549
|
+
* point side of `pointIn` and the `sum` input read: a point lives in the
|
|
550
|
+
* interval's element domain, and a quantifier is not an addition, so bool
|
|
551
|
+
* (orderable, never numeric) is exactly here refused. A CLOSED reference
|
|
535
552
|
* is excluded even though its kind is `u64`: a vocabulary's declaration-id
|
|
536
553
|
* order is an accident, not semantics (`docs/architecture/10-data-model.md`
|
|
537
554
|
* § orderability), so every order-comparison and fold position refuses
|
|
538
|
-
* closed-bound terms —
|
|
539
|
-
*
|
|
555
|
+
* closed-bound terms — the construction-time validations in
|
|
556
|
+
* `#query/lower.ts` are that ban's runtime twin.
|
|
540
557
|
*/
|
|
541
|
-
type
|
|
558
|
+
type NumericVarOk<V extends AnyVar> = V["field"] extends { readonly closed: ClosedRoster }
|
|
542
559
|
? false
|
|
543
560
|
: V["field"]["kind"] extends "u64" | "i64"
|
|
544
561
|
? true
|
|
545
562
|
: false
|
|
546
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Whether a variable's OWN field is ORDERABLE (u64/i64/bool) — the one gate
|
|
566
|
+
* every order-comparison side, `min`/`max` input, and Arg key reads. Bool
|
|
567
|
+
* orders: `false < true`, the strict 0/1 encoding IS the order (ruled R3),
|
|
568
|
+
* exactly the engine's operand screen
|
|
569
|
+
* (`bumbledb/crates/bumbledb/src/ir/validate/context.rs`); bool has no
|
|
570
|
+
* closed variant, so the closed exclusion rides in {@link NumericVarOk}.
|
|
571
|
+
*/
|
|
572
|
+
type OrderVarOk<V extends AnyVar> = V["field"]["kind"] extends "bool" ? true : NumericVarOk<V>
|
|
573
|
+
|
|
547
574
|
/** Whether a variable's OWN field is interval-typed. */
|
|
548
575
|
type IntervalVarOk<V extends AnyVar> = V["field"]["kind"] extends "interval" ? true : false
|
|
549
576
|
|
|
550
|
-
/**
|
|
551
|
-
|
|
552
|
-
|
|
577
|
+
/**
|
|
578
|
+
* Whether a comparison has a VARIABLE side (a var or the measure) — the
|
|
579
|
+
* type-level twin of the engine's `ConstantComparison` rule: a param is a
|
|
580
|
+
* constant at execution, so param-vs-param and param-vs-literal are
|
|
581
|
+
* constant-valued whatever their domains ({@link assertTermSide} is the
|
|
582
|
+
* runtime wall behind this one).
|
|
583
|
+
*/
|
|
584
|
+
type CmpVarSideOk<L, R> = L extends AnyVar | Duration ? true : R extends AnyVar | Duration ? true : false
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* One order side's DOMAIN — the type-level twin of the engine's resolved
|
|
588
|
+
* operand type (`ir/validate/context.rs`, `classify`'s `Ord*` arms): a
|
|
589
|
+
* variable names its own field kind, the measure is u64 by definition (and
|
|
590
|
+
* never meets another measure — `DurationBothSides`), a bigint literal
|
|
591
|
+
* spans the integer domains, a param is typed by its sibling (`"open"`).
|
|
592
|
+
* `never` marks a side that is itself unorderable: interval/bytes/string
|
|
593
|
+
* vars (the operand screen), closed-bound vars (R4), a measure over a
|
|
594
|
+
* non-interval var.
|
|
595
|
+
*/
|
|
596
|
+
type OrderDomain<T> = T extends AnyVar
|
|
597
|
+
? OrderVarOk<T> extends true
|
|
598
|
+
? T["field"]["kind"]
|
|
599
|
+
: never
|
|
553
600
|
: T extends Duration<infer V extends AnyVar>
|
|
554
|
-
? IntervalVarOk<V>
|
|
555
|
-
|
|
601
|
+
? IntervalVarOk<V> extends true
|
|
602
|
+
? "duration"
|
|
603
|
+
: never
|
|
604
|
+
: T extends Param<string>
|
|
605
|
+
? "open"
|
|
606
|
+
: T extends bigint
|
|
607
|
+
? "integer"
|
|
608
|
+
: T extends boolean
|
|
609
|
+
? "bool"
|
|
610
|
+
: never
|
|
556
611
|
|
|
557
|
-
/**
|
|
558
|
-
|
|
612
|
+
/**
|
|
613
|
+
* Whether two order domains may MEET under one operator — the engine's
|
|
614
|
+
* same-type rule judged pairwise, not per side: bool meets only bool (a
|
|
615
|
+
* bigint literal against a bool var is exactly `check_const`'s
|
|
616
|
+
* conviction; R3 made bool orderable, never cross-orderable), u64 and i64
|
|
617
|
+
* never meet, the measure's sibling must live in u64 (`OrdMeasureVar`:
|
|
618
|
+
* scalar ≠ U64 is `IllegalComparison`), two measures never meet, and an
|
|
619
|
+
* open side (param, integer literal) takes whatever its sibling names.
|
|
620
|
+
*/
|
|
621
|
+
type OrderDomainsOk<A, B> = [A] extends [never]
|
|
622
|
+
? false
|
|
623
|
+
: [B] extends [never]
|
|
624
|
+
? false
|
|
625
|
+
: A extends "open"
|
|
626
|
+
? true
|
|
627
|
+
: B extends "open"
|
|
628
|
+
? true
|
|
629
|
+
: A extends "duration"
|
|
630
|
+
? B extends "u64" | "integer"
|
|
631
|
+
? true
|
|
632
|
+
: false
|
|
633
|
+
: B extends "duration"
|
|
634
|
+
? A extends "u64" | "integer"
|
|
635
|
+
? true
|
|
636
|
+
: false
|
|
637
|
+
: A extends "bool"
|
|
638
|
+
? B extends "bool"
|
|
639
|
+
? true
|
|
640
|
+
: false
|
|
641
|
+
: B extends "bool"
|
|
642
|
+
? false
|
|
643
|
+
: A extends "integer"
|
|
644
|
+
? true
|
|
645
|
+
: B extends "integer"
|
|
646
|
+
? true
|
|
647
|
+
: A extends B
|
|
648
|
+
? true
|
|
649
|
+
: false
|
|
650
|
+
|
|
651
|
+
/** The whole order comparison's judgment: a variable side exists, and the sides' domains meet. */
|
|
652
|
+
type OrderPairOk<L, R> = CmpVarSideOk<L, R> extends true ? OrderDomainsOk<OrderDomain<L>, OrderDomain<R>> : false
|
|
559
653
|
|
|
560
|
-
/**
|
|
561
|
-
|
|
654
|
+
/**
|
|
655
|
+
* An interval side's ELEMENT domain: a variable names its field's element
|
|
656
|
+
* (`never` on a non-interval var), a param or bare interval literal is
|
|
657
|
+
* `"open"` — no type exists to name its element at this tier; the engine
|
|
658
|
+
* anchors it at prepare.
|
|
659
|
+
*/
|
|
660
|
+
type IntervalElementDomain<T> = T extends AnyVar
|
|
661
|
+
? T["field"] extends { readonly kind: "interval"; readonly element: infer E extends "u64" | "i64" }
|
|
662
|
+
? E
|
|
663
|
+
: never
|
|
664
|
+
: "open"
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* The point side's domain — a point lives in the interval's element
|
|
668
|
+
* domain: a numeric var names its kind (never bool, never closed), a
|
|
669
|
+
* param or bigint literal is open.
|
|
670
|
+
*/
|
|
671
|
+
type PointDomain<T> = T extends AnyVar ? (NumericVarOk<T> extends true ? T["field"]["kind"] : never) : "open"
|
|
672
|
+
|
|
673
|
+
/** Whether two element domains meet: both legal, and equal unless one is open. */
|
|
674
|
+
type ElementMeets<A, B> = [A] extends [never]
|
|
675
|
+
? false
|
|
676
|
+
: [B] extends [never]
|
|
677
|
+
? false
|
|
678
|
+
: A extends "open"
|
|
679
|
+
? true
|
|
680
|
+
: B extends "open"
|
|
681
|
+
? true
|
|
682
|
+
: A extends B
|
|
683
|
+
? true
|
|
684
|
+
: false
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* The whole `pointIn` judgment (operands already sealed interval-left):
|
|
688
|
+
* the point's domain must BE the interval's element domain — `pointIn(t,
|
|
689
|
+
* w)` with an i64-typed `t` against `interval(u64)` is the engine's
|
|
690
|
+
* `IllegalComparison`, judged here first.
|
|
691
|
+
*/
|
|
692
|
+
type PointInPairOk<L, R> =
|
|
693
|
+
CmpVarSideOk<L, R> extends true ? ElementMeets<IntervalElementDomain<L>, PointDomain<R>> : false
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* The whole `allen` judgment: two interval sides of ONE element domain
|
|
697
|
+
* (Q1 — widths meet freely, u64-vs-i64 stays illegal), judged pairwise.
|
|
698
|
+
*/
|
|
699
|
+
type AllenPairOk<L, R> =
|
|
700
|
+
CmpVarSideOk<L, R> extends true ? ElementMeets<IntervalElementDomain<L>, IntervalElementDomain<R>> : false
|
|
562
701
|
|
|
563
702
|
/** The `eq`/`ne` judgment: var-var joins by mint slot; var-literal is exact-typed by the var's own field. */
|
|
564
703
|
type EqOk<Classes extends SchemaClasses, L, R> = L extends AnyVar
|
|
@@ -632,10 +771,13 @@ type NotIdbOk<Classes extends SchemaClasses, Head, B> =
|
|
|
632
771
|
|
|
633
772
|
/**
|
|
634
773
|
* One condition's judgment — the type-level twin of the engine's comparison
|
|
635
|
-
* roster: class-equal joins (off the mint slots),
|
|
636
|
-
* interval var under a non-`pointIn` op is
|
|
637
|
-
*
|
|
638
|
-
*
|
|
774
|
+
* roster: class-equal joins (off the mint slots), PAIRWISE-judged order
|
|
775
|
+
* domains (an interval var under a non-`pointIn` op is refused per side;
|
|
776
|
+
* bool-vs-numeric, u64-vs-i64, and measure-vs-non-u64 are refused as
|
|
777
|
+
* pairs — the engine's same-type rule), element-matched `pointIn`/`allen`
|
|
778
|
+
* pairs, the no-variable-side (constant comparison) rule, and
|
|
779
|
+
* negated-atom class safety. The leading `[AnyTreeChild] extends [C]` arm
|
|
780
|
+
* is the recursion's base case.
|
|
639
781
|
*/
|
|
640
782
|
type CondOkBool<Classes extends SchemaClasses, C> = [AnyTreeChild] extends [C]
|
|
641
783
|
? true
|
|
@@ -643,17 +785,11 @@ type CondOkBool<Classes extends SchemaClasses, C> = [AnyTreeChild] extends [C]
|
|
|
643
785
|
? Op extends "eq" | "ne"
|
|
644
786
|
? EqOk<Classes, L, R>
|
|
645
787
|
: Op extends "lt" | "le" | "gt" | "ge"
|
|
646
|
-
?
|
|
647
|
-
? true
|
|
648
|
-
: false
|
|
788
|
+
? OrderPairOk<L, R>
|
|
649
789
|
: Op extends "pointIn"
|
|
650
|
-
?
|
|
651
|
-
? true
|
|
652
|
-
: false
|
|
790
|
+
? PointInPairOk<L, R>
|
|
653
791
|
: Op extends "allen"
|
|
654
|
-
?
|
|
655
|
-
? true
|
|
656
|
-
: false
|
|
792
|
+
? AllenPairOk<L, R>
|
|
657
793
|
: false
|
|
658
794
|
: C extends Tree<infer Ch extends readonly AnyTreeChild[]>
|
|
659
795
|
? false extends CondOkBool<Classes, Ch[number]>
|
|
@@ -677,8 +813,18 @@ type EqParams<L, R> = L extends AnyVar
|
|
|
677
813
|
: never
|
|
678
814
|
: never
|
|
679
815
|
|
|
680
|
-
/**
|
|
681
|
-
|
|
816
|
+
/**
|
|
817
|
+
* An order side's params contribution, typed by the SIBLING side — the
|
|
818
|
+
* runtime anchors a param to its sibling variable's field, so a bool var
|
|
819
|
+
* orders against a `boolean` param; every var-less pairing stays `bigint`.
|
|
820
|
+
*/
|
|
821
|
+
type OrderSideParams<T, Sib> =
|
|
822
|
+
T extends Param<infer P extends string>
|
|
823
|
+
? { readonly [Q in P]: Sib extends AnyVar ? Infer<Sib["field"]> : bigint }
|
|
824
|
+
: never
|
|
825
|
+
|
|
826
|
+
/** The point side's params contribution (a point is always `bigint`). */
|
|
827
|
+
type PointParams<T> = T extends Param<infer P extends string> ? { readonly [Q in P]: bigint } : never
|
|
682
828
|
|
|
683
829
|
/** An interval side's params contribution. */
|
|
684
830
|
type IntervalSideParams<T> = T extends Param<infer P extends string> ? { readonly [Q in P]: IntervalValue } : never
|
|
@@ -697,9 +843,9 @@ type CondParams<C> = [AnyTreeChild] extends [C]
|
|
|
697
843
|
? Op extends "eq" | "ne"
|
|
698
844
|
? EqParams<L, R>
|
|
699
845
|
: Op extends "lt" | "le" | "gt" | "ge"
|
|
700
|
-
? OrderSideParams<L> | OrderSideParams<R>
|
|
846
|
+
? OrderSideParams<L, R> | OrderSideParams<R, L>
|
|
701
847
|
: Op extends "pointIn"
|
|
702
|
-
? IntervalSideParams<L> |
|
|
848
|
+
? IntervalSideParams<L> | PointParams<R>
|
|
703
849
|
: Op extends "allen"
|
|
704
850
|
? IntervalSideParams<L> | IntervalSideParams<R> | MaskParams<M>
|
|
705
851
|
: never
|
|
@@ -749,6 +895,7 @@ export type {
|
|
|
749
895
|
MatchShape,
|
|
750
896
|
NotAtom,
|
|
751
897
|
NotIdbAtom,
|
|
898
|
+
NumericVarOk,
|
|
752
899
|
OrderSide,
|
|
753
900
|
OrderVarOk,
|
|
754
901
|
ParamUse,
|
package/src/query/find.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import type { Infer } from "#fields.ts"
|
|
23
23
|
import type { SchemaClasses } from "#law.ts"
|
|
24
|
-
import type { IntervalVarOk, OrderVarOk } from "#query/atom.ts"
|
|
24
|
+
import type { IntervalVarOk, NumericVarOk, OrderVarOk } from "#query/atom.ts"
|
|
25
25
|
import type { AnyVar, Duration, MintSlotOf } from "#query/scope.ts"
|
|
26
26
|
|
|
27
27
|
/** One aggregate operator name of the find vocabulary. */
|
|
@@ -71,20 +71,21 @@ function countDistinct<const V extends AnyVar>(over: V): Agg<"countDistinct", V>
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* Exact checked sum over
|
|
75
|
-
*
|
|
76
|
-
*
|
|
74
|
+
* Exact checked sum over a NUMERIC (u64/i64) variable — wide accumulator,
|
|
75
|
+
* one finalize range check; overflow is the engine's typed runtime error —
|
|
76
|
+
* or over the measure (`r.sum(r.duration(w))`). Bool stays refused: a
|
|
77
|
+
* quantifier is not an addition (R3).
|
|
77
78
|
*/
|
|
78
79
|
function sum<const O extends AnyVar | Duration>(over: O): Agg<"sum", O> {
|
|
79
80
|
return aggregate("sum", over, undefined)
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
/** Minimum over an orderable variable
|
|
83
|
+
/** Minimum over an orderable variable (u64/i64/bool — over bool, `Min` is the ALL quantifier; R3) or the measure. */
|
|
83
84
|
function min<const O extends AnyVar | Duration>(over: O): Agg<"min", O> {
|
|
84
85
|
return aggregate("min", over, undefined)
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
/** Maximum over an orderable variable
|
|
88
|
+
/** Maximum over an orderable variable (u64/i64/bool — over bool, `Max` is the ANY quantifier; R3) or the measure. */
|
|
88
89
|
function max<const O extends AnyVar | Duration>(over: O): Agg<"max", O> {
|
|
89
90
|
return aggregate("max", over, undefined)
|
|
90
91
|
}
|
|
@@ -116,7 +117,22 @@ function pack<const V extends AnyVar>(over: V): Agg<"pack", V> {
|
|
|
116
117
|
return aggregate("pack", over, undefined)
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* A `sum` input's judgment: a NUMERIC variable — Sum over bool stays
|
|
122
|
+
* refused, a quantifier is not an addition (R3) — or the measure of an
|
|
123
|
+
* interval variable.
|
|
124
|
+
*/
|
|
125
|
+
type SumOverOk<O> = O extends AnyVar
|
|
126
|
+
? NumericVarOk<O>
|
|
127
|
+
: O extends Duration<infer V extends AnyVar>
|
|
128
|
+
? IntervalVarOk<V>
|
|
129
|
+
: false
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A `min`/`max` input's judgment: an ORDERABLE variable — bool folds:
|
|
133
|
+
* `Max` is Any and `Min` is All, the two quantifiers as the 0/1 encoding's
|
|
134
|
+
* extremes (R3) — or the measure of an interval variable.
|
|
135
|
+
*/
|
|
120
136
|
type FoldOverOk<O> = O extends AnyVar
|
|
121
137
|
? OrderVarOk<O>
|
|
122
138
|
: O extends Duration<infer V extends AnyVar>
|
|
@@ -136,15 +152,17 @@ type FindEntryOk<E> = E extends AnyVar
|
|
|
136
152
|
? true
|
|
137
153
|
: E extends Agg<"countDistinct", AnyVar>
|
|
138
154
|
? true
|
|
139
|
-
: E extends Agg<"sum"
|
|
140
|
-
?
|
|
141
|
-
: E extends Agg<"
|
|
142
|
-
?
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
:
|
|
155
|
+
: E extends Agg<"sum", infer O>
|
|
156
|
+
? SumOverOk<O>
|
|
157
|
+
: E extends Agg<"min" | "max", infer O>
|
|
158
|
+
? FoldOverOk<O>
|
|
159
|
+
: E extends Agg<"argMax" | "argMin", AnyVar, infer K extends AnyVar>
|
|
160
|
+
? OrderVarOk<K> extends true
|
|
161
|
+
? true
|
|
162
|
+
: false
|
|
163
|
+
: E extends Agg<"pack", infer V extends AnyVar>
|
|
164
|
+
? IntervalVarOk<V>
|
|
165
|
+
: false
|
|
148
166
|
|
|
149
167
|
/** The validated find record (intersect with the inferred entries — errors land on the offending key). */
|
|
150
168
|
type CheckFind<F extends FindShape> = {
|
package/src/query/lower.ts
CHANGED
|
@@ -1327,7 +1327,12 @@ function renderParamAnchor(roster: ClosedRoster | undefined): string {
|
|
|
1327
1327
|
* Folds every rule's param uses (recs in declaration order first, output
|
|
1328
1328
|
* rules last — exactly the lowering walk) into the query's registry: first
|
|
1329
1329
|
* use mints the dense `ParamId`, the first FIELD-ANCHORED use types the
|
|
1330
|
-
* wire, and one name keeps one shape AND one closedness.
|
|
1330
|
+
* wire, and one name keeps one shape AND one closedness. The orderable
|
|
1331
|
+
* ban needs no registry arm: an order use always anchors its SIBLING's
|
|
1332
|
+
* domain (the no-variable-side spelling is refused at the comparison
|
|
1333
|
+
* constructor), so a closed-anchored param under an order op dies at the
|
|
1334
|
+
* one-domain wall here — and an order use whose sibling is itself
|
|
1335
|
+
* closed-bound dies at the comparison's own var-side wall first.
|
|
1331
1336
|
*/
|
|
1332
1337
|
function paramRegistryOf(recs: readonly RecData[], rules: readonly RuleData[]): readonly ParamEntry[] {
|
|
1333
1338
|
const order: string[] = []
|
|
@@ -1338,7 +1343,6 @@ function paramRegistryOf(recs: readonly RecData[], rules: readonly RuleData[]):
|
|
|
1338
1343
|
anchor: ParamEntry["anchor"]
|
|
1339
1344
|
op: ParamEntry["op"]
|
|
1340
1345
|
members: readonly string[] | undefined
|
|
1341
|
-
orderOp: "lt" | "le" | "gt" | "ge" | "pointIn" | undefined
|
|
1342
1346
|
}
|
|
1343
1347
|
>()
|
|
1344
1348
|
function fold(uses: readonly ParamUse[]): void {
|
|
@@ -1350,8 +1354,7 @@ function paramRegistryOf(recs: readonly RecData[], rules: readonly RuleData[]):
|
|
|
1350
1354
|
shape: use.shape,
|
|
1351
1355
|
anchor: use.anchor,
|
|
1352
1356
|
op: use.op,
|
|
1353
|
-
members: use.members
|
|
1354
|
-
orderOp: isOrderOp(use.op) ? use.op : undefined
|
|
1357
|
+
members: use.members
|
|
1355
1358
|
})
|
|
1356
1359
|
continue
|
|
1357
1360
|
}
|
|
@@ -1378,9 +1381,6 @@ function paramRegistryOf(recs: readonly RecData[], rules: readonly RuleData[]):
|
|
|
1378
1381
|
existing.anchor = use.anchor
|
|
1379
1382
|
existing.op = use.op
|
|
1380
1383
|
}
|
|
1381
|
-
if (existing.orderOp === undefined && isOrderOp(use.op)) {
|
|
1382
|
-
existing.orderOp = use.op
|
|
1383
|
-
}
|
|
1384
1384
|
}
|
|
1385
1385
|
}
|
|
1386
1386
|
for (const rec of recs) {
|
|
@@ -1397,10 +1397,6 @@ function paramRegistryOf(recs: readonly RecData[], rules: readonly RuleData[]):
|
|
|
1397
1397
|
if (entry === undefined) {
|
|
1398
1398
|
throw errors.new(`query param ${name} lost its registry entry`)
|
|
1399
1399
|
}
|
|
1400
|
-
const anchorRoster = anchorRosterOf(entry.anchor)
|
|
1401
|
-
if (entry.orderOp !== undefined && anchorRoster !== undefined) {
|
|
1402
|
-
throw closedOrderError(`query param ${name}`, `its ${entry.orderOp} use's anchor`, anchorRoster.name)
|
|
1403
|
-
}
|
|
1404
1400
|
/**
|
|
1405
1401
|
* A membership array's handle names are program constants, so the
|
|
1406
1402
|
* entry stores the resolved IMAGE: each name rides the one
|