@bjornpagen/bumbledb 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COOKBOOK.md +155 -136
- package/README.md +3 -7
- package/dist/capacity.d.ts +14 -1
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js.map +1 -1
- package/dist/db.d.ts +77 -109
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +121 -339
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +11 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -10
- package/dist/index.js.map +1 -1
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +8 -1
- package/dist/lower.js.map +1 -1
- package/dist/native.d.ts +69 -50
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +162 -122
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +26 -22
- package/dist/query/atom.js.map +1 -1
- package/dist/query/find.d.ts +18 -35
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +13 -32
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +113 -122
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +336 -260
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +12 -0
- package/dist/query/parse-ir.d.ts.map +1 -0
- package/dist/query/parse-ir.js +71 -0
- package/dist/query/parse-ir.js.map +1 -0
- package/dist/query/run.d.ts +2 -2
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +2 -13
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +4 -16
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +1 -6
- package/dist/query/scope.js.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/statements.d.ts +15 -9
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +14 -9
- package/dist/statements.js.map +1 -1
- package/package.json +2 -2
- package/src/capacity.ts +24 -1
- package/src/db.ts +182 -443
- package/src/index.ts +9 -23
- package/src/lower.ts +8 -1
- package/src/native.ts +69 -42
- package/src/query/atom.ts +255 -165
- package/src/query/find.ts +39 -80
- package/src/query/lower.ts +578 -432
- package/src/query/parse-ir.ts +82 -0
- package/src/query/run.ts +2 -14
- package/src/query/scope.ts +3 -21
- package/src/schema.ts +2 -2
- package/src/statements.ts +33 -17
- package/dist/order.d.ts +0 -87
- package/dist/order.d.ts.map +0 -1
- package/dist/order.js +0 -153
- package/dist/order.js.map +0 -1
- package/dist/query/predicate.d.ts +0 -91
- package/dist/query/predicate.d.ts.map +0 -1
- package/dist/query/predicate.js +0 -156
- package/dist/query/predicate.js.map +0 -1
- package/src/order.ts +0 -234
- package/src/query/predicate.ts +0 -269
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stratified recursion — engine recursion as values, mirroring the IR's
|
|
3
|
-
* cut exactly (`ir::Program`/`PredicateDef`/`AtomSource::Idb`;
|
|
4
|
-
* `docs/architecture/20-query-ir.md` § engine recursion):
|
|
5
|
-
*
|
|
6
|
-
* program(S, (p) => {
|
|
7
|
-
* const reach = p.rec("reach")
|
|
8
|
-
* reach.rule((r) => {
|
|
9
|
-
* const n = v(Node)
|
|
10
|
-
* return r.match(Node, { id: n.id }).where(r.eq(n.id, r.param("root"))).find({ c: n.id })
|
|
11
|
-
* })
|
|
12
|
-
* reach.rule((r) => {
|
|
13
|
-
* const e = v(Parent)
|
|
14
|
-
* return r.match(Parent, { child: e.child, parent: e.parent }).idb(reach, { c: e.parent }).find({ c: e.child })
|
|
15
|
-
* })
|
|
16
|
-
* return p.output((r) => {
|
|
17
|
-
* const post = v(Posting)
|
|
18
|
-
* return r.match(Posting, { account: post.account, minor: post.minor })
|
|
19
|
-
* .idb(reach, { c: post.account }).find({ total: r.sum(post.minor) })
|
|
20
|
-
* })
|
|
21
|
-
* })
|
|
22
|
-
*
|
|
23
|
-
* `p.rec(name)` declares one recursive predicate (declaration order = its
|
|
24
|
-
* dense `PredId`); `rec.rule(...)` attaches one clause — its builder's
|
|
25
|
-
* `idb` accepts ONLY the rec itself (the self-recursion cut as a
|
|
26
|
-
* type-level boundary: mutual recursion is unwritable) and its `find` head
|
|
27
|
-
* projects bound variables only (aggregation/measure through a cycle is
|
|
28
|
-
* unrepresentable — the strata judge's roster, made unwritable);
|
|
29
|
-
* `p.output(...)` seals the recs and builds the output rules, whose `idb`
|
|
30
|
-
* folds any FINISHED stratum by NAMED record over its head keys (recipe
|
|
31
|
-
* 25's form). The rec value `.rule` returns carries the params its rules
|
|
32
|
-
* used — thread it into the output's `idb` and the program's inferred
|
|
33
|
-
* `Params` stays exactly the params the rules use. Everything deeper —
|
|
34
|
-
* strata legality, signature sealing, the three oracles — is the ENGINE's
|
|
35
|
-
* judge, surfacing typed at prepare.
|
|
36
|
-
*/
|
|
37
|
-
import type { SchemaClasses } from "#law.ts";
|
|
38
|
-
import type { AnyRuleValue, HeadOf, HeadShape, OutputRuleScope, ParamsOf, Query, RecRef, RecRuleScope, RowOf } from "#query/lower.ts";
|
|
39
|
-
import type { Flatten, ParamsRecord, ShapeOf } from "#query/scope.ts";
|
|
40
|
-
import { inferred } from "#query/scope.ts";
|
|
41
|
-
import type { Schema, SchemaRelations } from "#schema.ts";
|
|
42
|
-
/**
|
|
43
|
-
* One recursive predicate HANDLE: `rec.rule(...)` attaches a clause and
|
|
44
|
-
* returns the SAME rec under a widened params type (the runtime data is
|
|
45
|
-
* shared — either handle is the self-reference; the returned one carries
|
|
46
|
-
* the rules' params for the output to thread) and under the head signature
|
|
47
|
-
* the FIRST rule sealed (every later rule derives the same head — the
|
|
48
|
-
* runtime alignment check's law), so a threaded handle's `idb` joins are
|
|
49
|
-
* arity- and domain-checked against the head.
|
|
50
|
-
*/
|
|
51
|
-
interface Rec<Rels extends SchemaRelations, Name extends string, P extends ParamsRecord, Head extends HeadShape = undefined, Classes extends SchemaClasses = SchemaClasses> extends RecRef<Name, P, Head> {
|
|
52
|
-
rule<RV extends AnyRuleValue>(build: (r: RecRuleScope<Rels, Name, Classes>) => RV): Rec<Rels, Name, Flatten<P & ParamsOf<RV>>, Head extends undefined ? HeadOf<RV> : Head, Classes>;
|
|
53
|
-
readonly [inferred]?: {
|
|
54
|
-
readonly params: P;
|
|
55
|
-
readonly head: Head;
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
/** One output-rule builder function. */
|
|
59
|
-
type OutputBuild<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> = (r: OutputRuleScope<Rels, Classes>) => AnyRuleValue;
|
|
60
|
-
/** A build function's rule value. */
|
|
61
|
-
type BuiltRule<F> = F extends (r: never) => infer RV ? RV : never;
|
|
62
|
-
/** The union row of a tuple of output builds. */
|
|
63
|
-
type OutputRow<Builds extends readonly OutputBuild<SchemaRelations, SchemaClasses>[]> = RowOf<BuiltRule<Builds[number]>>;
|
|
64
|
-
/** The intersected params record of a tuple of output builds. */
|
|
65
|
-
type OutputParams<Builds extends readonly OutputBuild<SchemaRelations, SchemaClasses>[]> = ShapeOf<ParamsOf<BuiltRule<Builds[number]>>>;
|
|
66
|
-
/**
|
|
67
|
-
* The program scope: declare recs, attach their rules, then declare the
|
|
68
|
-
* output — which seals the recs (a later `rec`/`rule` is a construction
|
|
69
|
-
* error) and returns the program as an ordinary query value.
|
|
70
|
-
*/
|
|
71
|
-
interface ProgramScope<Rels extends SchemaRelations, Classes extends SchemaClasses = SchemaClasses> {
|
|
72
|
-
/** Declares one recursive predicate; declaration order = its dense `PredId`. */
|
|
73
|
-
rec<const Name extends string>(name: Name): Rec<Rels, Name, Record<never, never>, undefined, Classes>;
|
|
74
|
-
/**
|
|
75
|
-
* Declares the output predicate (one rule per build; multiple rules =
|
|
76
|
-
* set union) and seals the program. Must be what the `program()`
|
|
77
|
-
* callback returns.
|
|
78
|
-
*/
|
|
79
|
-
output<const Builds extends readonly OutputBuild<Rels, Classes>[]>(...builds: Builds): Query<Rels, OutputRow<Builds>, OutputParams<Builds>, Classes>;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Builds a stratified program over a schema. The callback declares recs
|
|
83
|
-
* and their rules through the scope and MUST return `p.output(...)` — the
|
|
84
|
-
* sealed program is an ordinary query value: `db.prepare` lowers it to
|
|
85
|
-
* the one `ProgramIr` shape the engine executes under the per-stratum
|
|
86
|
-
* fixpoint driver.
|
|
87
|
-
*/
|
|
88
|
-
declare function program<Rels extends SchemaRelations, Classes extends SchemaClasses, Q extends Query<Rels, unknown, ParamsRecord, Classes>>(theory: Schema<Rels, Classes>, build: (p: ProgramScope<Rels, Classes>) => Q): Q;
|
|
89
|
-
export type { OutputBuild, ProgramScope, Rec };
|
|
90
|
-
export { program };
|
|
91
|
-
//# sourceMappingURL=predicate.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"predicate.d.ts","sourceRoot":"","sources":["../../src/query/predicate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,OAAO,KAAK,EACX,YAAY,EACZ,MAAM,EACN,SAAS,EACT,eAAe,EACf,QAAQ,EAER,KAAK,EAEL,MAAM,EACN,YAAY,EACZ,KAAK,EAEL,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAc,QAAQ,EAAmB,MAAM,iBAAiB,CAAA;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEzD;;;;;;;;GAQG;AACH,UAAU,GAAG,CACZ,IAAI,SAAS,eAAe,EAC5B,IAAI,SAAS,MAAM,EACnB,CAAC,SAAS,YAAY,EACtB,IAAI,SAAS,SAAS,GAAG,SAAS,EAClC,OAAO,SAAS,aAAa,GAAG,aAAa,CAC5C,SAAQ,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC;IAC9B,IAAI,CAAC,EAAE,SAAS,YAAY,EAC3B,KAAK,EAAE,CAAC,CAAC,EAAE,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,GACjD,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,SAAS,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;IAClG,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAA;CACjE;AAED,wCAAwC;AACxC,KAAK,WAAW,CAAC,IAAI,SAAS,eAAe,EAAE,OAAO,SAAS,aAAa,GAAG,aAAa,IAAI,CAC/F,CAAC,EAAE,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,KAC7B,YAAY,CAAA;AAEjB,qCAAqC;AACrC,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,KAAK,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAA;AAEjE,iDAAiD;AACjD,KAAK,SAAS,CAAC,MAAM,SAAS,SAAS,WAAW,CAAC,eAAe,EAAE,aAAa,CAAC,EAAE,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;AAExH,iEAAiE;AACjE,KAAK,YAAY,CAAC,MAAM,SAAS,SAAS,WAAW,CAAC,eAAe,EAAE,aAAa,CAAC,EAAE,IAAI,OAAO,CACjG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CACnC,CAAA;AAED;;;;GAIG;AACH,UAAU,YAAY,CAAC,IAAI,SAAS,eAAe,EAAE,OAAO,SAAS,aAAa,GAAG,aAAa;IACjG,gFAAgF;IAChF,GAAG,CAAC,KAAK,CAAC,IAAI,SAAS,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAA;IACrG;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,SAAS,SAAS,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAChE,GAAG,MAAM,EAAE,MAAM,GACf,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAA;CAChE;AA0FD;;;;;;GAMG;AACH,iBAAS,OAAO,CACf,IAAI,SAAS,eAAe,EAC5B,OAAO,SAAS,aAAa,EAC7B,CAAC,SAAS,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EACpD,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAmDhF;AAED,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,EAAE,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,CAAA"}
|
package/dist/query/predicate.js
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stratified recursion — engine recursion as values, mirroring the IR's
|
|
3
|
-
* cut exactly (`ir::Program`/`PredicateDef`/`AtomSource::Idb`;
|
|
4
|
-
* `docs/architecture/20-query-ir.md` § engine recursion):
|
|
5
|
-
*
|
|
6
|
-
* program(S, (p) => {
|
|
7
|
-
* const reach = p.rec("reach")
|
|
8
|
-
* reach.rule((r) => {
|
|
9
|
-
* const n = v(Node)
|
|
10
|
-
* return r.match(Node, { id: n.id }).where(r.eq(n.id, r.param("root"))).find({ c: n.id })
|
|
11
|
-
* })
|
|
12
|
-
* reach.rule((r) => {
|
|
13
|
-
* const e = v(Parent)
|
|
14
|
-
* return r.match(Parent, { child: e.child, parent: e.parent }).idb(reach, { c: e.parent }).find({ c: e.child })
|
|
15
|
-
* })
|
|
16
|
-
* return p.output((r) => {
|
|
17
|
-
* const post = v(Posting)
|
|
18
|
-
* return r.match(Posting, { account: post.account, minor: post.minor })
|
|
19
|
-
* .idb(reach, { c: post.account }).find({ total: r.sum(post.minor) })
|
|
20
|
-
* })
|
|
21
|
-
* })
|
|
22
|
-
*
|
|
23
|
-
* `p.rec(name)` declares one recursive predicate (declaration order = its
|
|
24
|
-
* dense `PredId`); `rec.rule(...)` attaches one clause — its builder's
|
|
25
|
-
* `idb` accepts ONLY the rec itself (the self-recursion cut as a
|
|
26
|
-
* type-level boundary: mutual recursion is unwritable) and its `find` head
|
|
27
|
-
* projects bound variables only (aggregation/measure through a cycle is
|
|
28
|
-
* unrepresentable — the strata judge's roster, made unwritable);
|
|
29
|
-
* `p.output(...)` seals the recs and builds the output rules, whose `idb`
|
|
30
|
-
* folds any FINISHED stratum by NAMED record over its head keys (recipe
|
|
31
|
-
* 25's form). The rec value `.rule` returns carries the params its rules
|
|
32
|
-
* used — thread it into the output's `idb` and the program's inferred
|
|
33
|
-
* `Params` stays exactly the params the rules use. Everything deeper —
|
|
34
|
-
* strata legality, signature sealing, the three oracles — is the ENGINE's
|
|
35
|
-
* judge, surfacing typed at prepare.
|
|
36
|
-
*/
|
|
37
|
-
import * as errors from "@superbuilders/errors";
|
|
38
|
-
import { makeOutputRuleScope, makeQuery, makeRawScope } from "#query/lower.ts";
|
|
39
|
-
import { fieldJoins, inferred, renderFieldKind } from "#query/scope.ts";
|
|
40
|
-
/** Builds the runtime rec handle over shared rec data. */
|
|
41
|
-
function makeRawRec(state, name, data) {
|
|
42
|
-
const rec = {
|
|
43
|
-
name,
|
|
44
|
-
data,
|
|
45
|
-
rule(build) {
|
|
46
|
-
if (state.sealed) {
|
|
47
|
-
throw errors.new(`rec ${name}: the program's output is already declared — recursive rules attach before p.output`);
|
|
48
|
-
}
|
|
49
|
-
const built = build(makeRawScope({ kind: "rec", self: data, classes: state.classes, theory: state.theory }));
|
|
50
|
-
const head = data.rules[0];
|
|
51
|
-
if (head !== undefined) {
|
|
52
|
-
const declared = head.finds.map(function columnName(column) {
|
|
53
|
-
return column.name;
|
|
54
|
-
});
|
|
55
|
-
const candidate = built.rule.finds.map(function columnName(column) {
|
|
56
|
-
return column.name;
|
|
57
|
-
});
|
|
58
|
-
if (declared.join(", ") !== candidate.join(", ")) {
|
|
59
|
-
throw errors.new(`rec ${name}: every rule derives the same head — rule 0 projects (${declared.join(", ")}), this rule projects (${candidate.join(", ")})`);
|
|
60
|
-
}
|
|
61
|
-
// The law-class wall on the sealed head: names alone do not
|
|
62
|
-
// align value spaces. Every rule must bind each head column
|
|
63
|
-
// at a classed mint slot that JOINS rule 0's (the sealing rule
|
|
64
|
-
// — the one slot every downstream idb pairing class-checks
|
|
65
|
-
// against), under the same fieldJoins judgment every reuse
|
|
66
|
-
// site enforces; otherwise a later rule feeds (say) bare
|
|
67
|
-
// weights into a column the idb joins as Node ids.
|
|
68
|
-
built.rule.finds.forEach(function verifyHeadSlot(column, position) {
|
|
69
|
-
const lead = head.finds[position];
|
|
70
|
-
if (lead === undefined) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
if (lead.slot !== undefined && column.slot !== undefined && !fieldJoins(lead.slot, column.slot)) {
|
|
74
|
-
throw errors.new(`rec ${name}: every rule derives the same head — the head column ${lead.name} is bound at ${renderFieldKind(lead.slot)} in rule 0 but at ${renderFieldKind(column.slot)} in this rule (a head column joins only class-equal slots; bare pairs only with bare)`);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
data.rules.push(built.rule);
|
|
79
|
-
return makeRawRec(state, name, data);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
Object.freeze(rec);
|
|
83
|
-
return rec;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* The rec handles' trusted admission seam (the pattern's home is
|
|
87
|
-
* `isTypedScope` in query/lower.ts):
|
|
88
|
-
* the checkable fact — the handle owns exactly the rec data it names — is
|
|
89
|
-
* verified before the raw handle is admitted at its typed face.
|
|
90
|
-
*/
|
|
91
|
-
function isRecHandle(data, rec) {
|
|
92
|
-
return rec.data === data;
|
|
93
|
-
}
|
|
94
|
-
/** Builds one typed rec handle over shared rec data. */
|
|
95
|
-
function makeRec(state, name, data) {
|
|
96
|
-
const raw = makeRawRec(state, name, data);
|
|
97
|
-
if (!isRecHandle(data, raw)) {
|
|
98
|
-
throw errors.new(`rec ${name}: handle construction incomplete`);
|
|
99
|
-
}
|
|
100
|
-
return raw;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Builds a stratified program over a schema. The callback declares recs
|
|
104
|
-
* and their rules through the scope and MUST return `p.output(...)` — the
|
|
105
|
-
* sealed program is an ordinary query value: `db.prepare` lowers it to
|
|
106
|
-
* the one `ProgramIr` shape the engine executes under the per-stratum
|
|
107
|
-
* fixpoint driver.
|
|
108
|
-
*/
|
|
109
|
-
function program(theory, build) {
|
|
110
|
-
const state = { recs: [], classes: theory.classes, theory, sealed: false };
|
|
111
|
-
const names = new Set();
|
|
112
|
-
const made = { query: undefined };
|
|
113
|
-
const scope = {
|
|
114
|
-
rec(name) {
|
|
115
|
-
if (state.sealed) {
|
|
116
|
-
throw errors.new(`program: the output is already declared — rec ${name} would be unreachable`);
|
|
117
|
-
}
|
|
118
|
-
if (names.has(name)) {
|
|
119
|
-
throw errors.new(`program: a rec named ${name} is already declared — rec names are the self-recursion cut's identity`);
|
|
120
|
-
}
|
|
121
|
-
names.add(name);
|
|
122
|
-
const data = { name, rules: [] };
|
|
123
|
-
state.recs.push(data);
|
|
124
|
-
return makeRec(state, name, data);
|
|
125
|
-
},
|
|
126
|
-
output(...builds) {
|
|
127
|
-
if (state.sealed) {
|
|
128
|
-
throw errors.new("program: output is declared once — multiple rules are multiple builds of the one output");
|
|
129
|
-
}
|
|
130
|
-
state.sealed = true;
|
|
131
|
-
for (const rec of state.recs) {
|
|
132
|
-
if (rec.rules.length === 0) {
|
|
133
|
-
throw errors.new(`program: rec ${rec.name} has no rules — a predicate with no defining clause seals no signature`);
|
|
134
|
-
}
|
|
135
|
-
Object.freeze(rec.rules);
|
|
136
|
-
}
|
|
137
|
-
if (builds.length === 0) {
|
|
138
|
-
throw errors.new("program: the output needs at least one rule");
|
|
139
|
-
}
|
|
140
|
-
const rules = builds.map(function buildRule(buildOne) {
|
|
141
|
-
return buildOne(makeOutputRuleScope(state)).rule;
|
|
142
|
-
});
|
|
143
|
-
const q = makeQuery(theory, state.recs, rules);
|
|
144
|
-
made.query = q;
|
|
145
|
-
return q;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
Object.freeze(scope);
|
|
149
|
-
const result = build(scope);
|
|
150
|
-
if (made.query !== result) {
|
|
151
|
-
throw errors.new("program: the build callback must return p.output(...) — the sealed program IS the query value");
|
|
152
|
-
}
|
|
153
|
-
return result;
|
|
154
|
-
}
|
|
155
|
-
export { program };
|
|
156
|
-
//# sourceMappingURL=predicate.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"predicate.js","sourceRoot":"","sources":["../../src/query/predicate.ts"],"names":[],"mappings":"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"}
|
package/src/order.ts
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Host-side answer ordering — the census-fired convenience
|
|
3
|
-
* (`docs/architecture/70-api.md` § the freeze ledger). Answers are SETS and
|
|
4
|
-
* the ENGINE NEVER ORDERS; the language owns the sort (`rows.sort(...)`)
|
|
5
|
-
* and the limit (`.slice(0, n)`) — the drizzle law. What JS lacks is a
|
|
6
|
-
* number-returning comparator over the SDK's bigint-bearing cells, so the
|
|
7
|
-
* SDK ships exactly that: sort keys are DATA — a bare column name is
|
|
8
|
-
* ascending (the punning spelling; no `asc` wrapper exists — one spelling
|
|
9
|
-
* per meaning) and `desc(name)` is the one descending spelling — folded by
|
|
10
|
-
* `by(...)` into one row-typed comparator. Cross-type cells cannot arise
|
|
11
|
-
* within one column (one column, one domain), and the cell order is TOTAL
|
|
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.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import type { FactValue } from "#native.ts"
|
|
26
|
-
|
|
27
|
-
/** One DESCENDING sort key, plain data — built by {@link desc}. */
|
|
28
|
-
interface Desc<K extends string> {
|
|
29
|
-
readonly key: K
|
|
30
|
-
readonly desc: true
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** One sort key: a bare column name (ascending — the punning spelling) or `desc(name)`. */
|
|
34
|
-
type SortKey<K extends string> = K | Desc<K>
|
|
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
|
-
|
|
53
|
-
/**
|
|
54
|
-
* The type-rank wall: boolean 0, bigint 1, string 2, bytes 3, interval 4.
|
|
55
|
-
* One column carries one domain, so a mixed pair never arises from decoded
|
|
56
|
-
* answer rows — the wall exists to keep the cell order TOTAL (never a
|
|
57
|
-
* throw), not to be reached.
|
|
58
|
-
*/
|
|
59
|
-
function cellRank(value: FactValue): number {
|
|
60
|
-
if (typeof value === "boolean") {
|
|
61
|
-
return 0
|
|
62
|
-
}
|
|
63
|
-
if (typeof value === "bigint") {
|
|
64
|
-
return 1
|
|
65
|
-
}
|
|
66
|
-
if (typeof value === "string") {
|
|
67
|
-
return 2
|
|
68
|
-
}
|
|
69
|
-
if (value instanceof Uint8Array) {
|
|
70
|
-
return 3
|
|
71
|
-
}
|
|
72
|
-
return 4
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* One cell against one cell. Same-type arms: boolean orders false < true;
|
|
77
|
-
* bigint by `<`/`>`; string by the host language's own `<`/`>` (flavor,
|
|
78
|
-
* recorded); bytes bytewise over the shared prefix, then by length;
|
|
79
|
-
* intervals by start, then end. A mixed pair falls through to the
|
|
80
|
-
* type-rank wall.
|
|
81
|
-
*/
|
|
82
|
-
function cellCmp(left: FactValue, right: FactValue): number {
|
|
83
|
-
if (typeof left === "boolean" && typeof right === "boolean") {
|
|
84
|
-
if (left === right) {
|
|
85
|
-
return 0
|
|
86
|
-
}
|
|
87
|
-
if (left) {
|
|
88
|
-
return 1
|
|
89
|
-
}
|
|
90
|
-
return -1
|
|
91
|
-
}
|
|
92
|
-
if (typeof left === "bigint" && typeof right === "bigint") {
|
|
93
|
-
if (left < right) {
|
|
94
|
-
return -1
|
|
95
|
-
}
|
|
96
|
-
if (left > right) {
|
|
97
|
-
return 1
|
|
98
|
-
}
|
|
99
|
-
return 0
|
|
100
|
-
}
|
|
101
|
-
if (typeof left === "string" && typeof right === "string") {
|
|
102
|
-
if (left < right) {
|
|
103
|
-
return -1
|
|
104
|
-
}
|
|
105
|
-
if (left > right) {
|
|
106
|
-
return 1
|
|
107
|
-
}
|
|
108
|
-
return 0
|
|
109
|
-
}
|
|
110
|
-
if (left instanceof Uint8Array && right instanceof Uint8Array) {
|
|
111
|
-
const shared = Math.min(left.length, right.length)
|
|
112
|
-
for (let index = 0; index < shared; index += 1) {
|
|
113
|
-
const leftByte = left[index]
|
|
114
|
-
const rightByte = right[index]
|
|
115
|
-
// `index < shared` keeps both reads in bounds; the `undefined`
|
|
116
|
-
// arms are the checker's indexed-access tax, never taken.
|
|
117
|
-
if (leftByte !== undefined && rightByte !== undefined && leftByte !== rightByte) {
|
|
118
|
-
return leftByte - rightByte
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return left.length - right.length
|
|
122
|
-
}
|
|
123
|
-
if (
|
|
124
|
-
typeof left === "object" &&
|
|
125
|
-
!(left instanceof Uint8Array) &&
|
|
126
|
-
typeof right === "object" &&
|
|
127
|
-
!(right instanceof Uint8Array)
|
|
128
|
-
) {
|
|
129
|
-
if (left.start < right.start) {
|
|
130
|
-
return -1
|
|
131
|
-
}
|
|
132
|
-
if (left.start > right.start) {
|
|
133
|
-
return 1
|
|
134
|
-
}
|
|
135
|
-
if (left.end < right.end) {
|
|
136
|
-
return -1
|
|
137
|
-
}
|
|
138
|
-
if (left.end > right.end) {
|
|
139
|
-
return 1
|
|
140
|
-
}
|
|
141
|
-
return 0
|
|
142
|
-
}
|
|
143
|
-
return cellRank(left) - cellRank(right)
|
|
144
|
-
}
|
|
145
|
-
|
|
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
|
-
}
|
|
178
|
-
const marker: Desc<K> = { key, desc: true }
|
|
179
|
-
return Object.freeze(marker)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Folds sort keys into ONE comparator typed against the row —
|
|
184
|
-
* `Row extends Readonly<Record<K, FactValue>>` — so a key the row lacks, or
|
|
185
|
-
* a column typed `number` (outside the cell domain), is a COMPILE error at
|
|
186
|
-
* the `.sort` call site: the laws typed the columns, and the row type
|
|
187
|
-
* carries that proof here (parse-don't-validate). The generic RETURN is the
|
|
188
|
-
* load-bearing trick: `rows.sort(by("rank"))` instantiates `Row` from the
|
|
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.
|
|
202
|
-
*/
|
|
203
|
-
function by(): <T extends EngineOrderable>(left: T, right: T) => number
|
|
204
|
-
function by<const K extends string>(
|
|
205
|
-
first: SortKey<K>,
|
|
206
|
-
...rest: ReadonlyArray<SortKey<K>>
|
|
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 } {
|
|
217
|
-
if (typeof sortKey === "string") {
|
|
218
|
-
return { key: sortKey, factor: 1 }
|
|
219
|
-
}
|
|
220
|
-
return { key: sortKey.key, factor: -1 }
|
|
221
|
-
})
|
|
222
|
-
return function compare<Row extends Readonly<Record<K, FactValue>>>(left: Row, right: Row): number {
|
|
223
|
-
for (const entry of entries) {
|
|
224
|
-
const order = cellCmp(left[entry.key], right[entry.key]) * entry.factor
|
|
225
|
-
if (order !== 0) {
|
|
226
|
-
return order
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return 0
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export type { Desc, EngineOrderable, SortKey }
|
|
234
|
-
export { by, desc }
|