@bjornpagen/bumbledb 0.1.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 (108) hide show
  1. package/LICENSE +12 -0
  2. package/README.md +84 -0
  3. package/dist/brand.d.ts +59 -0
  4. package/dist/brand.d.ts.map +1 -0
  5. package/dist/brand.js +47 -0
  6. package/dist/brand.js.map +1 -0
  7. package/dist/closed.d.ts +97 -0
  8. package/dist/closed.d.ts.map +1 -0
  9. package/dist/closed.js +107 -0
  10. package/dist/closed.js.map +1 -0
  11. package/dist/count.d.ts +55 -0
  12. package/dist/count.d.ts.map +1 -0
  13. package/dist/count.js +92 -0
  14. package/dist/count.js.map +1 -0
  15. package/dist/db.d.ts +341 -0
  16. package/dist/db.d.ts.map +1 -0
  17. package/dist/db.js +1016 -0
  18. package/dist/db.js.map +1 -0
  19. package/dist/exhume.d.ts +130 -0
  20. package/dist/exhume.d.ts.map +1 -0
  21. package/dist/exhume.js +145 -0
  22. package/dist/exhume.js.map +1 -0
  23. package/dist/face.d.ts +91 -0
  24. package/dist/face.d.ts.map +1 -0
  25. package/dist/face.js +69 -0
  26. package/dist/face.js.map +1 -0
  27. package/dist/fields.d.ts +187 -0
  28. package/dist/fields.d.ts.map +1 -0
  29. package/dist/fields.js +204 -0
  30. package/dist/fields.js.map +1 -0
  31. package/dist/index.d.ts +49 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +31 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/lower.d.ts +34 -0
  36. package/dist/lower.d.ts.map +1 -0
  37. package/dist/lower.js +105 -0
  38. package/dist/lower.js.map +1 -0
  39. package/dist/marshal.d.ts +79 -0
  40. package/dist/marshal.d.ts.map +1 -0
  41. package/dist/marshal.js +167 -0
  42. package/dist/marshal.js.map +1 -0
  43. package/dist/native.d.ts +532 -0
  44. package/dist/native.d.ts.map +1 -0
  45. package/dist/native.js +64 -0
  46. package/dist/native.js.map +1 -0
  47. package/dist/query/atom.d.ts +285 -0
  48. package/dist/query/atom.d.ts.map +1 -0
  49. package/dist/query/atom.js +281 -0
  50. package/dist/query/atom.js.map +1 -0
  51. package/dist/query/lower.d.ts +145 -0
  52. package/dist/query/lower.d.ts.map +1 -0
  53. package/dist/query/lower.js +604 -0
  54. package/dist/query/lower.js.map +1 -0
  55. package/dist/query/predicate.d.ts +101 -0
  56. package/dist/query/predicate.d.ts.map +1 -0
  57. package/dist/query/predicate.js +85 -0
  58. package/dist/query/predicate.js.map +1 -0
  59. package/dist/query/run.d.ts +30 -0
  60. package/dist/query/run.d.ts.map +1 -0
  61. package/dist/query/run.js +94 -0
  62. package/dist/query/run.js.map +1 -0
  63. package/dist/query/scope.d.ts +168 -0
  64. package/dist/query/scope.d.ts.map +1 -0
  65. package/dist/query/scope.js +134 -0
  66. package/dist/query/scope.js.map +1 -0
  67. package/dist/query/select.d.ts +106 -0
  68. package/dist/query/select.d.ts.map +1 -0
  69. package/dist/query/select.js +69 -0
  70. package/dist/query/select.js.map +1 -0
  71. package/dist/relation.d.ts +120 -0
  72. package/dist/relation.d.ts.map +1 -0
  73. package/dist/relation.js +108 -0
  74. package/dist/relation.js.map +1 -0
  75. package/dist/schema.d.ts +50 -0
  76. package/dist/schema.d.ts.map +1 -0
  77. package/dist/schema.js +235 -0
  78. package/dist/schema.js.map +1 -0
  79. package/dist/spec.d.ts +218 -0
  80. package/dist/spec.d.ts.map +1 -0
  81. package/dist/spec.js +154 -0
  82. package/dist/spec.js.map +1 -0
  83. package/dist/statements.d.ts +91 -0
  84. package/dist/statements.d.ts.map +1 -0
  85. package/dist/statements.js +101 -0
  86. package/dist/statements.js.map +1 -0
  87. package/package.json +66 -0
  88. package/src/brand.ts +82 -0
  89. package/src/closed.ts +228 -0
  90. package/src/count.ts +117 -0
  91. package/src/db.ts +1519 -0
  92. package/src/exhume.ts +243 -0
  93. package/src/face.ts +161 -0
  94. package/src/fields.ts +385 -0
  95. package/src/index.ts +185 -0
  96. package/src/lower.ts +118 -0
  97. package/src/marshal.ts +220 -0
  98. package/src/native.ts +576 -0
  99. package/src/query/atom.ts +556 -0
  100. package/src/query/lower.ts +855 -0
  101. package/src/query/predicate.ts +195 -0
  102. package/src/query/run.ts +106 -0
  103. package/src/query/scope.ts +301 -0
  104. package/src/query/select.ts +140 -0
  105. package/src/relation.ts +252 -0
  106. package/src/schema.ts +297 -0
  107. package/src/spec.ts +325 -0
  108. package/src/statements.ts +148 -0
package/LICENSE ADDED
@@ -0,0 +1,12 @@
1
+ Copyright © 2026 Bjorn Pagen <11238136+bjornpagen@users.noreply.github.com>
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any
4
+ purpose with or without fee is hereby granted.
5
+
6
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
7
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
8
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
9
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
10
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
11
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
12
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @bjornpagen/bumbledb
2
+
3
+ Type-theoretic TypeScript SDK for the [bumbledb](https://github.com/bjornpagen/bumbledb) embedded relational engine.
4
+
5
+ bumbledb models data as relations judged by statements (functionality, containment, cardinality) and queried with Datalog expressed as plain values — no SQL, no query-string parser. The SDK is a thin, fully typed surface over an in-process native engine (LMDB storage, MVCC snapshots, a single-writer witnessed write loop).
6
+
7
+ > **Research-grade, one platform.** This is a `0.x` release of an embedded engine under active development. It targets a single platform today (below), the API is not yet frozen across `0.x`, and the FFI ABI is pinned exactly per version. Treat it as an early adopter's tool, not a production datastore.
8
+
9
+ ## Platform support
10
+
11
+ This release targets **darwin-arm64 (macOS Apple Silicon) only**. The native binary ships as the optional platform package `@bjornpagen/bumbledb-darwin-arm64`, resolved automatically at install on a matching host. Installs on other platforms succeed (the main package is pure JS) but throw a typed, actionable error at first load naming the running platform and that only `darwin-arm64` ships today. More targets are pure addition — one more `os`/`cpu`-gated package plus a CI matrix — not a redesign.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ pnpm add @bjornpagen/bumbledb
17
+ ```
18
+
19
+ ## Quick start
20
+
21
+ Declare a schema, write facts through a transaction, and query with Datalog as
22
+ values. Everything is typed end to end — branded ids, inferred query rows, and
23
+ rejections that arrive as data rather than exceptions.
24
+
25
+ ```ts
26
+ import { Db, relation, schema, contained, on, query, match, u64, str, type Brand, type Scope } from "@bjornpagen/bumbledb"
27
+
28
+ // Branded, fresh-minted id types.
29
+ const HolderId = u64.newtype("HolderId")
30
+ const AccountId = u64.newtype("AccountId")
31
+
32
+ // Relations. `.fresh` marks an engine-minted primary key.
33
+ const Holder = relation("Holder", { id: HolderId.fresh, name: str })
34
+ const Account = relation("Account", { id: AccountId.fresh, holder: HolderId })
35
+
36
+ // A theory: every Account.holder must reference an existing Holder.id.
37
+ const Ledger = schema("Ledger", { Holder, Account }, [contained(on(Account, "holder"), on(Holder, "id"))])
38
+
39
+ const db = await Db.create("./ledger.db", Ledger)
40
+
41
+ // Write. The delta is judged against every statement at commit.
42
+ let adaId: Brand<bigint, "HolderId"> | undefined
43
+ const result = db.write((tx) => {
44
+ const ada = tx.insert(Holder, { name: "ada" }) // ada.id is a branded HolderId
45
+ adaId = ada.id
46
+ tx.insert(Account, { holder: ada.id })
47
+ })
48
+
49
+ // Rejection-as-data: no throw — a rejected commit is a typed value carrying
50
+ // every violated statement, cited once, with its canonical spelling and facts.
51
+ if (!result.ok) {
52
+ for (const v of result.violations) {
53
+ console.error(v.kind, v.canonical, v.facts)
54
+ }
55
+ }
56
+
57
+ // Query: Datalog as values. Rows are typed from the `select` shape.
58
+ const accountsOf = query(Ledger, ($: Scope<(typeof Ledger)["relations"]>) => {
59
+ const acct = $.var(Account.fields.id)
60
+ const holder = $.param("holder", Holder.fields.id)
61
+ return { rules: [[match(Account, { id: acct, holder })]], select: { acct } }
62
+ })
63
+
64
+ const prepared = db.prepare(accountsOf)
65
+ const rows = db.execute(prepared, { holder: adaId }) // rows: { acct: AccountId }[]
66
+ ```
67
+
68
+ ## Surface
69
+
70
+ - The type kernel — brands, fields, `relation()`, `closed()`.
71
+ - The statement algebra — `schema()`, `key`, `contained`, `mirrors`, `window`.
72
+ - The `Db` runtime — path-cached stores, transactions, typed violations, scoped snapshot reads, the witnessed write loop.
73
+ - The query surface — Datalog as values: scoped vars/params, atoms, negation, conditions, aggregates, engine recursion via predicates, `db.prepare`.
74
+ - The exhume surface — `Db.exhume`, the schema-independent read path: a store's self-described shapes and raw facts by name.
75
+
76
+ ## Architecture
77
+
78
+ The SDK is a typed surface over the native engine; the model (relations,
79
+ statement-based judgment, Datalog evaluation, MVCC storage, the witnessed
80
+ write loop) is documented in the [bumbledb engine repository](https://github.com/bjornpagen/bumbledb).
81
+
82
+ ## License
83
+
84
+ 0BSD
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Nominal branding — the TS analog of the Rust macro's host newtypes
3
+ * (`docs/architecture/10-data-model.md`, the nominal-safety layer). A brand
4
+ * is a phantom: it exists only in the type, tsc polices the wall exactly as
5
+ * rustc polices newtype domains, and nothing is allocated or wrapped at
6
+ * runtime.
7
+ */
8
+ /**
9
+ * The brand key. A real runtime symbol (so modules can import it without a
10
+ * `declare`-only lie), but no branded value ever carries the property — the
11
+ * brand is purely a typing device.
12
+ */
13
+ declare const brand: unique symbol;
14
+ /**
15
+ * The phantom-value key used by field values, field references, and faces
16
+ * to carry their value type without any runtime representation.
17
+ */
18
+ declare const phantom: unique symbol;
19
+ /**
20
+ * A branded scalar: `T` walled off under the literal name `Name`. A
21
+ * `Brand<bigint, "HolderId">` is not assignable where a
22
+ * `Brand<bigint, "AccountId">` is expected — the Rust newtype wall,
23
+ * verbatim. Scalars brand as `bigint` (u64/i64), `Uint8Array` (bytes), and
24
+ * whole interval objects (`Interval<Name>`); `bool` and `str` take no
25
+ * newtype, exactly as the macro's `as` grammar refuses them.
26
+ */
27
+ type Brand<T, Name extends string> = T & {
28
+ readonly [brand]: Name;
29
+ };
30
+ /**
31
+ * A half-open interval `[start, end)` as a plain value object. The ray is
32
+ * representable (`end` = the element domain's MAX_END); widths and
33
+ * signedness are NOT modeled here — the engine judges widths at the typed
34
+ * write boundary, the brand blocks cross-field assignment, and nothing
35
+ * else is TS's business. Interval newtypes derive no order (the Rust
36
+ * refusal, `docs/architecture/10-data-model.md`), so no comparators exist.
37
+ */
38
+ interface IntervalValue {
39
+ readonly start: bigint;
40
+ readonly end: bigint;
41
+ }
42
+ /**
43
+ * A branded interval: the whole `{ start, end }` object walled under
44
+ * `Name` — the `interval<i64> as ActiveDuring` analog.
45
+ */
46
+ type Interval<Name extends string> = Brand<IntervalValue, Name>;
47
+ /**
48
+ * Constructs an interval literal — the `start..end` spelling. Half-open
49
+ * and nonempty by construction: `start >= end` is a typed construction
50
+ * error (parse, don't validate — the same invariant Rust's
51
+ * `Interval::new` enforces at the host boundary). The default `never`
52
+ * brand makes a fresh literal assignable to any interval field or brand,
53
+ * the wrap-at-construction idiom; pass the brand explicitly
54
+ * (`span<"ActiveDuring">(0n, 10n)`) to pin it.
55
+ */
56
+ declare function span<Name extends string = never>(start: bigint, end: bigint): Interval<Name>;
57
+ export type { Brand, Interval, IntervalValue };
58
+ export { brand, phantom, span };
59
+ //# sourceMappingURL=brand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH;;;;GAIG;AACH,QAAA,MAAM,KAAK,EAAE,OAAO,MAAiC,CAAA;AAErD;;;GAGG;AACH,QAAA,MAAM,OAAO,EAAE,OAAO,MAAmC,CAAA;AAEzD;;;;;;;GAOG;AACH,KAAK,KAAK,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,CAAA;CAAE,CAAA;AAEnE;;;;;;;GAOG;AACH,UAAU,aAAa;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,KAAK,QAAQ,CAAC,IAAI,SAAS,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;AAE/D;;;;;;;;GAQG;AACH,iBAAS,IAAI,CAAC,IAAI,SAAS,MAAM,GAAG,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAMrF;AAED,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;AAC9C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA"}
package/dist/brand.js ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Nominal branding — the TS analog of the Rust macro's host newtypes
3
+ * (`docs/architecture/10-data-model.md`, the nominal-safety layer). A brand
4
+ * is a phantom: it exists only in the type, tsc polices the wall exactly as
5
+ * rustc polices newtype domains, and nothing is allocated or wrapped at
6
+ * runtime.
7
+ */
8
+ import * as errors from "@superbuilders/errors";
9
+ /**
10
+ * The brand-minting guard behind {@link span} — the one nominal step, as a
11
+ * type guard carrying the interval's REAL invariant (`start < end`, the
12
+ * same check Rust's `Interval::new` runs): a value that passes IS a legal
13
+ * interval of any brand, exactly as a Rust newtype wraps a checked
14
+ * `Interval<T>` at construction.
15
+ */
16
+ function isNonemptyInterval(value) {
17
+ return value.start < value.end;
18
+ }
19
+ /**
20
+ * The brand key. A real runtime symbol (so modules can import it without a
21
+ * `declare`-only lie), but no branded value ever carries the property — the
22
+ * brand is purely a typing device.
23
+ */
24
+ const brand = Symbol("bumbledb.brand");
25
+ /**
26
+ * The phantom-value key used by field values, field references, and faces
27
+ * to carry their value type without any runtime representation.
28
+ */
29
+ const phantom = Symbol("bumbledb.phantom");
30
+ /**
31
+ * Constructs an interval literal — the `start..end` spelling. Half-open
32
+ * and nonempty by construction: `start >= end` is a typed construction
33
+ * error (parse, don't validate — the same invariant Rust's
34
+ * `Interval::new` enforces at the host boundary). The default `never`
35
+ * brand makes a fresh literal assignable to any interval field or brand,
36
+ * the wrap-at-construction idiom; pass the brand explicitly
37
+ * (`span<"ActiveDuring">(0n, 10n)`) to pin it.
38
+ */
39
+ function span(start, end) {
40
+ const value = Object.freeze({ start, end });
41
+ if (!isNonemptyInterval(value)) {
42
+ throw errors.new(`interval is half-open and nonempty: start must be < end (got ${start}..${end})`);
43
+ }
44
+ return value;
45
+ }
46
+ export { brand, phantom, span };
47
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAA;AAE/C;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAsB,KAAoB;IACpE,OAAO,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAA;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,KAAK,GAAkB,MAAM,CAAC,gBAAgB,CAAC,CAAA;AAErD;;;GAGG;AACH,MAAM,OAAO,GAAkB,MAAM,CAAC,kBAAkB,CAAC,CAAA;AA+BzD;;;;;;;;GAQG;AACH,SAAS,IAAI,CAA8B,KAAa,EAAE,GAAW;IACpE,MAAM,KAAK,GAAkB,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;IAC1D,IAAI,CAAC,kBAAkB,CAAO,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,MAAM,CAAC,GAAG,CAAC,gEAAgE,KAAK,KAAK,GAAG,GAAG,CAAC,CAAA;IACnG,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAGD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Closed relations (`docs/architecture/10-data-model.md` § closed
3
+ * relations): a vocabulary whose extension is declared in the schema — two
4
+ * tiers, one function. The emission per closed relation mirrors the macro's
5
+ * (host-enum analog): handle CONSTANTS on the value (`Kind.Checking`, ids =
6
+ * declaration order), the `fromId` weld, an `id` field constructor
7
+ * pre-branded with the handle newtype for other relations' field blocks
8
+ * (`kind: Kind.id`), and payload readback (`Kind.axioms`). No fact type and
9
+ * no insert surface exist — closed relations are unwritable by
10
+ * construction: the value simply lacks the writable relation shape.
11
+ */
12
+ import type { Brand } from "#brand.ts";
13
+ import { type AnyField, type ClosedIdField, type FieldData, type FieldValue } from "#fields.ts";
14
+ import type { LiteralSpec } from "#spec.ts";
15
+ /**
16
+ * A payload column of a closed relation: any field constructor except a
17
+ * fresh-marked one (a vocabulary's rows are ground axioms, never minted).
18
+ */
19
+ type PayloadField = AnyField & {
20
+ readonly data: FieldData<false>;
21
+ };
22
+ /** One declared payload column: name plus its field description. */
23
+ interface ClosedColumn {
24
+ readonly name: string;
25
+ readonly field: FieldData;
26
+ }
27
+ /**
28
+ * One ground axiom, already lowered: the handle plus one wire literal per
29
+ * declared column in column-declaration order (row id = index). Lowered
30
+ * EAGERLY at construction so axiom literals ride the same selection-literal
31
+ * machine as `where()` bindings, with the same errors (the macro's rule).
32
+ */
33
+ interface ClosedRow {
34
+ readonly handle: string;
35
+ readonly values: readonly LiteralSpec[];
36
+ }
37
+ /** A closed relation's runtime description. */
38
+ interface ClosedData {
39
+ readonly name: string;
40
+ readonly handles: readonly string[];
41
+ readonly columns: readonly ClosedColumn[];
42
+ readonly rows: readonly ClosedRow[];
43
+ }
44
+ /** One axiom row as the host writes and reads it: column name to host value. */
45
+ type AxiomRow<Cols> = {
46
+ readonly [C in keyof Cols]: FieldValue<Cols[C]>;
47
+ };
48
+ /**
49
+ * The whole axiom record: every handle exactly once, every column exactly
50
+ * once per row — a missing or extra axiom, column, or handle is a TYPE
51
+ * error (mapped over the handle tuple).
52
+ */
53
+ type Axioms<Handles extends readonly string[], Cols> = {
54
+ readonly [H in Handles[number]]: AxiomRow<Cols>;
55
+ };
56
+ /**
57
+ * The named surface of a closed relation value, minus the handle constants
58
+ * (which {@link Closed} intersects in).
59
+ */
60
+ interface ClosedCore<Name extends string, Handles extends readonly string[], Cols> {
61
+ readonly name: Name;
62
+ /**
63
+ * The pre-branded u64 field constructor: `kind: Kind.id` in another
64
+ * relation's field block is the reference through which bare handles
65
+ * become legal in that relation's selections.
66
+ */
67
+ readonly id: ClosedIdField<Name>;
68
+ readonly data: ClosedData;
69
+ /** Payload readback: handle to its declared column values. */
70
+ readonly axioms: Axioms<Handles, Cols>;
71
+ /** The weld: declaration-order id back to its handle, or undefined beyond the roster. */
72
+ fromId(id: Brand<bigint, Name>): Handles[number] | undefined;
73
+ }
74
+ /**
75
+ * A closed relation value: the core surface plus one branded constant per
76
+ * handle (`Kind.Checking: Brand<bigint, "Kind">`, ids = declaration order).
77
+ */
78
+ type Closed<Name extends string, Handles extends readonly string[], Cols> = ClosedCore<Name, Handles, Cols> & {
79
+ readonly [H in Handles[number]]: Brand<bigint, Name>;
80
+ };
81
+ /** Any closed relation value, whatever its roster and columns. */
82
+ interface AnyClosed {
83
+ readonly name: string;
84
+ readonly id: ClosedIdField<string>;
85
+ readonly data: ClosedData;
86
+ readonly axioms: Readonly<Record<string, object>>;
87
+ }
88
+ /** Bare tier: `closed("Kind", ["Checking", "Savings"])` — handles only. */
89
+ declare function closed<const Name extends string, const Handles extends readonly [string, ...string[]]>(name: Name, handles: Handles): Closed<Name, Handles, Record<never, never>>;
90
+ /**
91
+ * Payload tier: declared columns plus ground axioms, every handle with
92
+ * every column exactly once (type-enforced by {@link Axioms}).
93
+ */
94
+ declare function closed<const Name extends string, const Handles extends readonly [string, ...string[]], const Cols extends Record<string, PayloadField>>(name: Name, handles: Handles, columns: Cols, axioms: Axioms<Handles, Cols>): Closed<Name, Handles, Cols>;
95
+ export type { AnyClosed, AxiomRow, Axioms, Closed, ClosedColumn, ClosedCore, ClosedData, ClosedRow, PayloadField };
96
+ export { closed };
97
+ //# sourceMappingURL=closed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closed.d.ts","sourceRoot":"","sources":["../src/closed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EACN,KAAK,QAAQ,EAEb,KAAK,aAAa,EAElB,KAAK,SAAS,EACd,KAAK,UAAU,EAGf,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAsC3C;;;GAGG;AACH,KAAK,YAAY,GAAG,QAAQ,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,CAAA;CAAE,CAAA;AAElE,oEAAoE;AACpE,UAAU,YAAY;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CACzB;AAED;;;;;GAKG;AACH,UAAU,SAAS;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;CACvC;AAED,+CAA+C;AAC/C,UAAU,UAAU;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IACzC,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,CAAA;CACnC;AAED,gFAAgF;AAChF,KAAK,QAAQ,CAAC,IAAI,IAAI;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;CAAE,CAAA;AAEzE;;;;GAIG;AACH,KAAK,MAAM,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE,EAAE,IAAI,IAAI;IACtD,QAAQ,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC;CAC/C,CAAA;AAED;;;GAGG;AACH,UAAU,UAAU,CAAC,IAAI,SAAS,MAAM,EAAE,OAAO,SAAS,SAAS,MAAM,EAAE,EAAE,IAAI;IAChF,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,IAAI,CAAC,CAAA;IAChC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IACtC,yFAAyF;IACzF,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;CAC5D;AAED;;;GAGG;AACH,KAAK,MAAM,CAAC,IAAI,SAAS,MAAM,EAAE,OAAO,SAAS,SAAS,MAAM,EAAE,EAAE,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG;IAC7G,QAAQ,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;CACpD,CAAA;AAED,kEAAkE;AAClE,UAAU,SAAS;IAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAClC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACjD;AAED,2EAA2E;AAC3E,iBAAS,MAAM,CAAC,KAAK,CAAC,IAAI,SAAS,MAAM,EAAE,KAAK,CAAC,OAAO,SAAS,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,EAC9F,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,GACd,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;AAE9C;;;GAGG;AACH,iBAAS,MAAM,CACd,KAAK,CAAC,IAAI,SAAS,MAAM,EACzB,KAAK,CAAC,OAAO,SAAS,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,EACpD,KAAK,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EAC9C,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;AAwE1G,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,CAAA;AAClH,OAAO,EAAE,MAAM,EAAE,CAAA"}
package/dist/closed.js ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Closed relations (`docs/architecture/10-data-model.md` § closed
3
+ * relations): a vocabulary whose extension is declared in the schema — two
4
+ * tiers, one function. The emission per closed relation mirrors the macro's
5
+ * (host-enum analog): handle CONSTANTS on the value (`Kind.Checking`, ids =
6
+ * declaration order), the `fromId` weld, an `id` field constructor
7
+ * pre-branded with the handle newtype for other relations' field blocks
8
+ * (`kind: Kind.id`), and payload readback (`Kind.axioms`). No fact type and
9
+ * no insert surface exist — closed relations are unwritable by
10
+ * construction: the value simply lacks the writable relation shape.
11
+ */
12
+ import * as errors from "@superbuilders/errors";
13
+ import { assertDeclarationOrderKey, fieldData, literalOf } from "#fields.ts";
14
+ /**
15
+ * The value-surface property names a handle may not shadow — the macro's
16
+ * name-collision diagnostic, here over the closed value's own properties
17
+ * (`relation`/`selection` are reserved so a closed value can never be
18
+ * mistaken for a selected relation by `on()`'s discriminant).
19
+ */
20
+ const reservedHandleNames = Object.freeze([
21
+ "name",
22
+ "id",
23
+ "data",
24
+ "axioms",
25
+ "fromId",
26
+ "relation",
27
+ "selection"
28
+ ]);
29
+ /**
30
+ * Reads one handle's axiom row, refusing absence loudly: the payload tier's
31
+ * overload types the record exhaustively, so a missing row is ill-typed
32
+ * input, and the bare tier never reaches here (it declares no columns).
33
+ */
34
+ function axiomRow(name, axioms, handle) {
35
+ if (axioms === undefined) {
36
+ throw errors.new(`closed relation ${name}: payload columns declared without ground axioms`);
37
+ }
38
+ const row = axioms[handle];
39
+ if (row === undefined) {
40
+ throw errors.new(`closed relation ${name}: no ground axiom for handle ${handle}`);
41
+ }
42
+ return row;
43
+ }
44
+ function closed(name, handles, columns, axioms) {
45
+ const seen = new Set();
46
+ for (const handle of handles) {
47
+ if (seen.has(handle)) {
48
+ throw errors.new(`closed relation ${name}: duplicate handle ${handle}`);
49
+ }
50
+ seen.add(handle);
51
+ if (reservedHandleNames.includes(handle)) {
52
+ throw errors.new(`closed relation ${name}: handle ${handle} collides with the closed value's own surface (${reservedHandleNames.join(", ")})`);
53
+ }
54
+ }
55
+ const roster = Object.freeze({ name, handles: Object.freeze([...handles]) });
56
+ const cols = [];
57
+ if (columns !== undefined) {
58
+ for (const [columnName, field] of Object.entries(columns)) {
59
+ assertDeclarationOrderKey(`closed relation ${name} column`, columnName);
60
+ cols.push(Object.freeze({ name: columnName, field: field.data }));
61
+ }
62
+ }
63
+ const rows = handles.map(function lowerRow(handle) {
64
+ const values = cols.map(function lowerAxiomLiteral(column) {
65
+ const row = axiomRow(name, axioms, handle);
66
+ return Object.freeze(literalOf(column.field, row[column.name]));
67
+ });
68
+ return Object.freeze({ handle, values: Object.freeze(values) });
69
+ });
70
+ const data = Object.freeze({
71
+ name,
72
+ handles: roster.handles,
73
+ columns: Object.freeze(cols),
74
+ rows: Object.freeze(rows)
75
+ });
76
+ const id = Object.freeze({
77
+ data: fieldData({ kind: "u64" }, name, false, roster)
78
+ });
79
+ /**
80
+ * Handle names are arbitrary identifiers, so rows and constants are
81
+ * minted with OWN-property definition, never assignment: a handle named
82
+ * "__proto__" would otherwise ride the Object.prototype accessor —
83
+ * silently swapping the record's prototype instead of creating the row,
84
+ * and no-oping the constant (a primitive through the setter) — minting a
85
+ * value whose type claims Brand<bigint, Name> but reads back an object.
86
+ */
87
+ const axiomsOut = {};
88
+ for (const handle of handles) {
89
+ const row = axioms === undefined ? Object.freeze({}) : Object.freeze({ ...axiomRow(name, axioms, handle) });
90
+ Object.defineProperty(axiomsOut, handle, { value: row, enumerable: true });
91
+ }
92
+ const value = {
93
+ name,
94
+ id,
95
+ data,
96
+ axioms: Object.freeze(axiomsOut),
97
+ fromId(idValue) {
98
+ return roster.handles[Number(idValue)];
99
+ }
100
+ };
101
+ handles.forEach(function mintHandleConstant(handle, index) {
102
+ Object.defineProperty(value, handle, { value: BigInt(index), enumerable: true });
103
+ });
104
+ return Object.freeze(value);
105
+ }
106
+ export { closed };
107
+ //# sourceMappingURL=closed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closed.js","sourceRoot":"","sources":["../src/closed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Cardinality-window counts — exactly five constructors, and nothing else
3
+ * (`docs/architecture/70-api.md` § the canonical-utterance law). The ban
4
+ * table is enforced REPRESENTATIONALLY, stronger than Rust's expansion
5
+ * errors: `{1..*}`, `{n..n}`, `{0..0}`, `{0..*}`, and inverted windows have
6
+ * NO constructor — the spellings that could produce them are construction
7
+ * errors naming the canonical form, and no other spelling exists at all.
8
+ * Bounds are `bigint` (u64 crosses as bigint always, PRD-04's law).
9
+ */
10
+ import type { WindowSpec } from "#spec.ts";
11
+ /**
12
+ * The admission brand — a module-private symbol, deliberately unexported:
13
+ * `WindowSpec` is a public wire type, so without this brand every banned
14
+ * spelling in the ban table would be writable as a plain object literal
15
+ * (`{ window: { kind: "floor", lo: 1n } }` typechecks structurally). The
16
+ * symbol makes the five constructors the ONLY producers of a `Count`
17
+ * value, which is what "the ban table is unwritable" means.
18
+ */
19
+ declare const admitted: unique symbol;
20
+ /**
21
+ * An admitted window count — opaque and inert: a fact about the theory,
22
+ * not a builder. Only the five constructors below produce one (the
23
+ * module-private {@link admitted} brand forecloses structural literals).
24
+ */
25
+ interface Count {
26
+ readonly window: WindowSpec;
27
+ readonly [admitted]: true;
28
+ }
29
+ /**
30
+ * `{n}` — THE exact-count spelling, n ≥ 1. `exactly(0)` is the exclusion
31
+ * respelled and rejected naming `none`.
32
+ */
33
+ declare function exactly(n: bigint): Count;
34
+ /** `{0}` — the exclusion: no source fact may pair with the target group. */
35
+ declare const none: Count;
36
+ /**
37
+ * `{lo..hi}` — both bounds explicit, 0 ≤ lo < hi. `lo === hi` is the exact
38
+ * count respelled (rejected naming `exactly(n)`, or `none` at 0); an
39
+ * inverted window is unsatisfiable and rejected.
40
+ */
41
+ declare function between(lo: bigint, hi: bigint): Count;
42
+ /**
43
+ * `{lo..*}` — a floor with no ceiling, lo ≥ 2: `atLeast(1)` says only what
44
+ * the bare containment says (rejected naming `contained`), and
45
+ * `atLeast(0)` is vacuous (rejected naming deletion).
46
+ */
47
+ declare function atLeast(lo: bigint): Count;
48
+ /**
49
+ * `{0..hi}` — a ceiling, hi ≥ 1: `atMost(0)` is the exclusion respelled
50
+ * and rejected naming `none`.
51
+ */
52
+ declare function atMost(hi: bigint): Count;
53
+ export type { Count };
54
+ export { atLeast, atMost, between, exactly, none };
55
+ //# sourceMappingURL=count.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count.d.ts","sourceRoot":"","sources":["../src/count.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAK1C;;;;;;;GAOG;AACH,QAAA,MAAM,QAAQ,EAAE,OAAO,MAA0C,CAAA;AAEjE;;;;GAIG;AACH,UAAU,KAAK;IACd,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;CACzB;AAOD;;;GAGG;AACH,iBAAS,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAQjC;AAED,4EAA4E;AAC5E,QAAA,MAAM,IAAI,EAAE,KAAwB,CAAA;AAEpC;;;;GAIG;AACH,iBAAS,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CAgB9C;AAED;;;;GAIG;AACH,iBAAS,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAalC;AAED;;;GAGG;AACH,iBAAS,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAQjC;AAED,YAAY,EAAE,KAAK,EAAE,CAAA;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA"}
package/dist/count.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Cardinality-window counts — exactly five constructors, and nothing else
3
+ * (`docs/architecture/70-api.md` § the canonical-utterance law). The ban
4
+ * table is enforced REPRESENTATIONALLY, stronger than Rust's expansion
5
+ * errors: `{1..*}`, `{n..n}`, `{0..0}`, `{0..*}`, and inverted windows have
6
+ * NO constructor — the spellings that could produce them are construction
7
+ * errors naming the canonical form, and no other spelling exists at all.
8
+ * Bounds are `bigint` (u64 crosses as bigint always, PRD-04's law).
9
+ */
10
+ import * as errors from "@superbuilders/errors";
11
+ /** The exclusion's one spelling, shared by `none`. */
12
+ const exclusion = Object.freeze({ kind: "exact", n: 0n });
13
+ /**
14
+ * The admission brand — a module-private symbol, deliberately unexported:
15
+ * `WindowSpec` is a public wire type, so without this brand every banned
16
+ * spelling in the ban table would be writable as a plain object literal
17
+ * (`{ window: { kind: "floor", lo: 1n } }` typechecks structurally). The
18
+ * symbol makes the five constructors the ONLY producers of a `Count`
19
+ * value, which is what "the ban table is unwritable" means.
20
+ */
21
+ const admitted = Symbol("bumbledb.count.admitted");
22
+ /** Stamps one admitted window as a frozen `Count` value. */
23
+ function admit(window) {
24
+ return Object.freeze({ window, [admitted]: true });
25
+ }
26
+ /**
27
+ * `{n}` — THE exact-count spelling, n ≥ 1. `exactly(0)` is the exclusion
28
+ * respelled and rejected naming `none`.
29
+ */
30
+ function exactly(n) {
31
+ if (n < 0n) {
32
+ throw errors.new(`window counts are u64: exactly(${n}) is out of domain`);
33
+ }
34
+ if (n === 0n) {
35
+ throw errors.new("`{0..0}`-shaped spelling: the exclusion is written `{0}` — use none");
36
+ }
37
+ return admit(Object.freeze({ kind: "exact", n }));
38
+ }
39
+ /** `{0}` — the exclusion: no source fact may pair with the target group. */
40
+ const none = admit(exclusion);
41
+ /**
42
+ * `{lo..hi}` — both bounds explicit, 0 ≤ lo < hi. `lo === hi` is the exact
43
+ * count respelled (rejected naming `exactly(n)`, or `none` at 0); an
44
+ * inverted window is unsatisfiable and rejected.
45
+ */
46
+ function between(lo, hi) {
47
+ if (lo < 0n || hi < 0n) {
48
+ throw errors.new(`window counts are u64: between(${lo}, ${hi}) is out of domain`);
49
+ }
50
+ if (hi < lo) {
51
+ throw errors.new(`the window \`{${lo}..${hi}}\` is inverted — no count satisfies it; bounds are \`{lo..hi}\` with lo < hi (an exact count is \`{n}\`: exactly(n))`);
52
+ }
53
+ if (lo === hi) {
54
+ if (lo === 0n) {
55
+ throw errors.new("`{0..0}` — the exclusion is written `{0}`: use none");
56
+ }
57
+ throw errors.new(`\`{${lo}..${lo}}\` — an exact count is written \`{${lo}}\`: use exactly(${lo})`);
58
+ }
59
+ return admit(Object.freeze({ kind: "range", lo, hi }));
60
+ }
61
+ /**
62
+ * `{lo..*}` — a floor with no ceiling, lo ≥ 2: `atLeast(1)` says only what
63
+ * the bare containment says (rejected naming `contained`), and
64
+ * `atLeast(0)` is vacuous (rejected naming deletion).
65
+ */
66
+ function atLeast(lo) {
67
+ if (lo < 0n) {
68
+ throw errors.new(`window counts are u64: atLeast(${lo}) is out of domain`);
69
+ }
70
+ if (lo === 0n) {
71
+ throw errors.new("the `{0..*}` window is vacuous — it provably says nothing; delete the statement");
72
+ }
73
+ if (lo === 1n) {
74
+ throw errors.new("`{1..*}` says only what the bare containment says — drop the annotation and write the containment: contained(source, target)");
75
+ }
76
+ return admit(Object.freeze({ kind: "floor", lo }));
77
+ }
78
+ /**
79
+ * `{0..hi}` — a ceiling, hi ≥ 1: `atMost(0)` is the exclusion respelled
80
+ * and rejected naming `none`.
81
+ */
82
+ function atMost(hi) {
83
+ if (hi < 0n) {
84
+ throw errors.new(`window counts are u64: atMost(${hi}) is out of domain`);
85
+ }
86
+ if (hi === 0n) {
87
+ throw errors.new("`{0..0}` — the exclusion is written `{0}`: use none");
88
+ }
89
+ return admit(Object.freeze({ kind: "range", lo: 0n, hi }));
90
+ }
91
+ export { atLeast, atMost, between, exactly, none };
92
+ //# sourceMappingURL=count.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count.js","sourceRoot":"","sources":["../src/count.ts"],"names":[],"mappings":"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"}