@bjornpagen/bumbledb 0.12.2 → 0.15.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 +35 -21
- package/README.md +82 -55
- package/dist/db.d.ts +173 -130
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +782 -371
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +7 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -9
- package/dist/index.js.map +1 -1
- package/dist/marshal.d.ts +5 -27
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js +4 -21
- package/dist/marshal.js.map +1 -1
- package/dist/native.d.ts +157 -168
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +46 -8
- package/dist/native.js.map +1 -1
- package/dist/query/find.d.ts +14 -9
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +2 -2
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +5 -4
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +11 -9
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/relation.d.ts +4 -25
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +3 -4
- package/dist/relation.js.map +1 -1
- package/package.json +3 -3
- package/src/db.ts +1151 -500
- package/src/index.ts +28 -24
- package/src/marshal.ts +5 -35
- package/src/native.ts +248 -175
- package/src/query/find.ts +19 -14
- package/src/query/lower.ts +7 -6
- package/src/query/parse-ir.ts +11 -9
- package/src/relation.ts +3 -28
- package/dist/exhume.d.ts +0 -143
- package/dist/exhume.d.ts.map +0 -1
- package/dist/exhume.js +0 -166
- package/dist/exhume.js.map +0 -1
- package/src/exhume.ts +0 -267
package/src/query/lower.ts
CHANGED
|
@@ -772,7 +772,7 @@ function advanceInterior(
|
|
|
772
772
|
}
|
|
773
773
|
|
|
774
774
|
/** Narrows a find entry to an aggregate value. */
|
|
775
|
-
function isAggregateEntry(value: unknown): value is { readonly agg: string; readonly over
|
|
775
|
+
function isAggregateEntry(value: unknown): value is { readonly agg: string; readonly over?: unknown } {
|
|
776
776
|
return typeof value === "object" && value !== null && "agg" in value
|
|
777
777
|
}
|
|
778
778
|
|
|
@@ -785,11 +785,12 @@ function asVarTerm(context: string, value: unknown): AnyVar {
|
|
|
785
785
|
}
|
|
786
786
|
|
|
787
787
|
/** Classifies one aggregate find entry into its runtime data (variables ride by reference). */
|
|
788
|
-
function aggDataOf(name: string, entry: { readonly agg: string; readonly over
|
|
788
|
+
function aggDataOf(name: string, entry: { readonly agg: string; readonly over?: unknown }): AggData {
|
|
789
|
+
if (entry.agg === "count") {
|
|
790
|
+
return Object.freeze({ op: "count" as const })
|
|
791
|
+
}
|
|
789
792
|
const over = entry.over
|
|
790
793
|
switch (entry.agg) {
|
|
791
|
-
case "count":
|
|
792
|
-
return Object.freeze({ op: "count" as const })
|
|
793
794
|
case "sum":
|
|
794
795
|
case "min":
|
|
795
796
|
case "max": {
|
|
@@ -2051,7 +2052,7 @@ function lowerFind(entry: FindEntryData, ids: VarIds): FindTermIr {
|
|
|
2051
2052
|
const agg = entry.agg
|
|
2052
2053
|
switch (agg.op) {
|
|
2053
2054
|
case "count":
|
|
2054
|
-
return { kind: "
|
|
2055
|
+
return { kind: "count" }
|
|
2055
2056
|
case "fold": {
|
|
2056
2057
|
if ("duration" in agg.over) {
|
|
2057
2058
|
return { kind: "aggregateMeasure", op: { kind: agg.fold }, over: ids.of(agg.over.duration) }
|
|
@@ -2059,7 +2060,7 @@ function lowerFind(entry: FindEntryData, ids: VarIds): FindTermIr {
|
|
|
2059
2060
|
return { kind: "aggregate", op: { kind: agg.fold }, over: ids.of(agg.over) }
|
|
2060
2061
|
}
|
|
2061
2062
|
case "pack":
|
|
2062
|
-
return { kind: "
|
|
2063
|
+
return { kind: "pack", over: ids.of(agg.over) }
|
|
2063
2064
|
}
|
|
2064
2065
|
}
|
|
2065
2066
|
|
package/src/query/parse-ir.ts
CHANGED
|
@@ -50,28 +50,30 @@ function align(context: string, head: readonly HeadTermIr[], rules: readonly Rul
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
/** Count
|
|
53
|
+
/** Count is nullary; pack and folds require `over`. */
|
|
54
54
|
function parseFind(context: string, find: FindTermIr): void {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (find.op.kind === "count") {
|
|
59
|
-
if ("over" in find) {
|
|
55
|
+
const raw = find as Record<string, unknown>
|
|
56
|
+
if (find.kind === "count") {
|
|
57
|
+
if ("over" in raw) {
|
|
60
58
|
throw errors.new(`${context}: Count carries no over`)
|
|
61
59
|
}
|
|
62
60
|
return
|
|
63
61
|
}
|
|
64
|
-
if (
|
|
65
|
-
|
|
62
|
+
if (find.kind === "pack" || find.kind === "aggregate" || find.kind === "aggregateMeasure") {
|
|
63
|
+
if (!("over" in raw)) {
|
|
64
|
+
throw errors.new(`${context}: ${find.kind} requires over`)
|
|
65
|
+
}
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
/** Head family of one find term: measure is a var slot;
|
|
69
|
+
/** Head family of one find term: measure is a var slot; count/pack/folds are aggregates. */
|
|
70
70
|
function findFamily(find: FindTermIr): "var" | "aggregate" {
|
|
71
71
|
switch (find.kind) {
|
|
72
72
|
case "var":
|
|
73
73
|
case "measure":
|
|
74
74
|
return "var"
|
|
75
|
+
case "count":
|
|
76
|
+
case "pack":
|
|
75
77
|
case "aggregate":
|
|
76
78
|
case "aggregateMeasure":
|
|
77
79
|
return "aggregate"
|
package/src/relation.ts
CHANGED
|
@@ -7,29 +7,15 @@
|
|
|
7
7
|
* verified against their roster at construction). Fields are addressed by
|
|
8
8
|
* NAME everywhere — statements (`on(R, "holder")`), selections, and match
|
|
9
9
|
* records all spell the field's own name, checked by type
|
|
10
|
-
* (`FaceFields`/`MatchShape`). `Fact
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* (resupply-to-preserve-identity), typed exactly.
|
|
10
|
+
* (`FaceFields`/`MatchShape`). `Fact<>` is the inferred row object type
|
|
11
|
+
* at BARE structural value types (no brands): every field is present,
|
|
12
|
+
* including fresh cells. Mint with `tx.reserve` before insert.
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
import * as errors from "@superbuilders/errors"
|
|
17
16
|
import { type AnyField, assertDeclarationOrderKey, assertDeclarationRecord, type Infer, literalOf } from "#fields.ts"
|
|
18
17
|
import { type LiteralSetSpec, type LiteralSpec, renderLiteral } from "#spec.ts"
|
|
19
18
|
|
|
20
|
-
/** Flattens an intersection into one displayed object type (hover legibility). */
|
|
21
|
-
type Flatten<T> = { [K in keyof T]: T[K] }
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* An optional property that may be omitted OR explicitly `undefined`.
|
|
25
|
-
* `Partial<T>` under `exactOptionalPropertyTypes` is omit-only (`key?: T`),
|
|
26
|
-
* which rejects `key: T | undefined` — insert must accept both (omit-to-mint,
|
|
27
|
-
* and a host binding that is already `bigint | undefined`).
|
|
28
|
-
*/
|
|
29
|
-
type ExactOptional<T> = {
|
|
30
|
-
[K in keyof T]?: T[K] | undefined
|
|
31
|
-
}
|
|
32
|
-
|
|
33
19
|
/**
|
|
34
20
|
* Resolves one selection entry to its lowered literal set: a plain ARRAY
|
|
35
21
|
* (detected by `Array.isArray` — no field's value type is an array;
|
|
@@ -187,16 +173,6 @@ type FreshKeys<R extends AnyRelation> = {
|
|
|
187
173
|
[K in keyof RelationFields<R>]: RelationFields<R>[K] extends { readonly fresh: true } ? K : never
|
|
188
174
|
}[keyof RelationFields<R>]
|
|
189
175
|
|
|
190
|
-
/**
|
|
191
|
-
* The inferred row object type of a relation as INSERTED: fresh fields
|
|
192
|
-
* optional — omitted, the engine mints; supplied, identity is preserved
|
|
193
|
-
* (the ETL resupply idiom). Explicit `undefined` is the same as omit
|
|
194
|
-
* (`exactOptionalPropertyTypes`: `id?: bigint | undefined`, not omit-only).
|
|
195
|
-
*/
|
|
196
|
-
type InsertFact<R extends AnyRelation> = Flatten<
|
|
197
|
-
Omit<Fact<R>, FreshKeys<R>> & ExactOptional<Pick<Fact<R>, FreshKeys<R>>>
|
|
198
|
-
>
|
|
199
|
-
|
|
200
176
|
/**
|
|
201
177
|
* Declares one relation: `relation("Account", { id: u64.fresh,
|
|
202
178
|
* holder: u64, kind: Kind.id, ... })` — every field is a pure-structure
|
|
@@ -241,7 +217,6 @@ export type {
|
|
|
241
217
|
Fact,
|
|
242
218
|
FieldsShape,
|
|
243
219
|
FreshKeys,
|
|
244
|
-
InsertFact,
|
|
245
220
|
Relation,
|
|
246
221
|
RelationData,
|
|
247
222
|
RelationField,
|
package/dist/exhume.d.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The exhume surface — the SDK's ONE schema-independent read path
|
|
3
|
-
* (docs/course-serialization/prd-02-sdk-exhume-surface.md; engine
|
|
4
|
-
* 70-api.md § exhume): a read-only, theory-less open returning the store's
|
|
5
|
-
* SELF-DESCRIBED relation shapes and raw facts by relation name. The
|
|
6
|
-
* sighting it exists for: a run store whose creating schema has since
|
|
7
|
-
* evolved — the record outlives the schema, and exhume is how the record
|
|
8
|
-
* is read back for rebirth (exhume the old store, create the successor
|
|
9
|
-
* under the new theory, copy by NAME, re-derive).
|
|
10
|
-
*
|
|
11
|
-
* DELIBERATELY SCHEMA-FREE: no schema type appears anywhere on this
|
|
12
|
-
* surface. The caller's schema is the wrong theory for an exhumed store BY
|
|
13
|
-
* DEFINITION (a store the current theory could open would never need
|
|
14
|
-
* exhuming), so every value crosses TYPED at its bare structural form
|
|
15
|
-
* ({@link FactValue} — bigint/string/boolean/bytes/interval, never
|
|
16
|
-
* `unknown`) and every fact is keyed by field NAME. The SDK never
|
|
17
|
-
* reconstructs a `Schema` value from the descriptor — that inverse mapping
|
|
18
|
-
* is deliberately out of scope; the rebirth tool keys by name.
|
|
19
|
-
*
|
|
20
|
-
* LIFETIMES ARE DISPOSABLES, never `close()` (ruled 2026-07-23, R12): the
|
|
21
|
-
* `Exhumed` value implements `Symbol.dispose` — teardown is synchronous
|
|
22
|
-
* (one environment close) — and `using` is the documented idiom:
|
|
23
|
-
*
|
|
24
|
-
* using exhumed = await Db.exhume(path)
|
|
25
|
-
*
|
|
26
|
-
* Disposal releases the engine handle AND the store's exclusive advisory
|
|
27
|
-
* lock deterministically, scope-shaped in the language's own syntax, so a
|
|
28
|
-
* same-path reopen (retry after a half-failed migration, a second forensic
|
|
29
|
-
* read) never waits on an unforceable GC finalizer. Every verb after
|
|
30
|
-
* disposal is a typed used-after-dispose refusal. The engine-side drop
|
|
31
|
-
* remains the reclamation-only backstop for a collected-but-undisposed
|
|
32
|
-
* value; the store is never written through this surface, so there is
|
|
33
|
-
* nothing to flush.
|
|
34
|
-
*/
|
|
35
|
-
import type { FactValue } from "./native.js";
|
|
36
|
-
import type { ValueTypeSpec } from "./spec.js";
|
|
37
|
-
/**
|
|
38
|
-
* The typed `descriptorMissing` refusal: the store predates self-describing
|
|
39
|
-
* stores and has not been adopted. The remedy is in the message — one
|
|
40
|
-
* fingerprint-matching `Db.open` under the creating schema back-fills the
|
|
41
|
-
* descriptor (engine 50-storage.md § the `_meta` block) and the store is
|
|
42
|
-
* self-describing forever. Match with `errors.is`.
|
|
43
|
-
*/
|
|
44
|
-
declare const ErrExhumeNoDescriptor: Error;
|
|
45
|
-
/**
|
|
46
|
-
* The typed `formatMismatch` refusal: the store's on-disk format version is
|
|
47
|
-
* not this engine's (no migration path exists, as everywhere). Match with
|
|
48
|
-
* `errors.is`.
|
|
49
|
-
*/
|
|
50
|
-
declare const ErrExhumeFormatMismatch: Error;
|
|
51
|
-
/**
|
|
52
|
-
* The typed `corruption` refusal: the persisted descriptor fails its
|
|
53
|
-
* integrity gates (the stored bytes hash to something other than the stored
|
|
54
|
-
* fingerprint, or the bytes do not decode and re-encode faithfully). Match
|
|
55
|
-
* with `errors.is`.
|
|
56
|
-
*/
|
|
57
|
-
declare const ErrExhumeCorruption: Error;
|
|
58
|
-
/**
|
|
59
|
-
* One exhumed fact as a plain name-keyed record of bare structural values
|
|
60
|
-
* — deliberately schema-free (module doc): `bigint` for u64/i64, `string`
|
|
61
|
-
* for str, `boolean` for bool, `Uint8Array` for bytes<N>, a `{ start, end }`
|
|
62
|
-
* bigint pair for intervals. Closed-relation rows open with the synthetic
|
|
63
|
-
* `id` field, exactly as the descriptor's sealed field list declares.
|
|
64
|
-
*/
|
|
65
|
-
type ExhumedFact = Readonly<Record<string, FactValue>>;
|
|
66
|
-
/**
|
|
67
|
-
* One field of an exhumed relation, exactly as the store's creator declared
|
|
68
|
-
* it: the name and the structural type tag (byte width on `fixedBytes`,
|
|
69
|
-
* element and optional width on `interval`).
|
|
70
|
-
*/
|
|
71
|
-
interface ExhumedField {
|
|
72
|
-
readonly name: string;
|
|
73
|
-
readonly valueType: ValueTypeSpec;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* One ground axiom of an exhumed closed relation: the handle (the row's
|
|
77
|
-
* identity, never a column), the declaration-order row id, and the declared
|
|
78
|
-
* payload columns by name.
|
|
79
|
-
*/
|
|
80
|
-
interface ExhumedAxiom {
|
|
81
|
-
readonly handle: string;
|
|
82
|
-
readonly id: bigint;
|
|
83
|
-
readonly values: ExhumedFact;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* One relation of an exhumed store: the name, the SEALED field list in
|
|
87
|
-
* declaration order (a closed relation opens with the synthetic (`id`,
|
|
88
|
-
* u64) handle field), and — exactly on closed relations — the roster of
|
|
89
|
-
* ground axioms. Scan rows come back in this field order, so pairing a
|
|
90
|
-
* row's positions against `fields` is the name-keyed reading `scan`
|
|
91
|
-
* performs.
|
|
92
|
-
*/
|
|
93
|
-
interface ExhumedRelation {
|
|
94
|
-
readonly name: string;
|
|
95
|
-
readonly fields: readonly ExhumedField[];
|
|
96
|
-
readonly roster: readonly ExhumedAxiom[] | undefined;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* The exhumed store's schema as declared, decoded from the store's own
|
|
100
|
-
* persisted descriptor: relations in engine-id order (declaration order
|
|
101
|
-
* mints every id), each with its ordered field descriptions and closed
|
|
102
|
-
* roster — enough for a caller to key facts by name and re-insert them
|
|
103
|
-
* into a differently-fingerprinted successor store. No schema type appears
|
|
104
|
-
* here (module doc: the surface is schema-free; rows are typed bare).
|
|
105
|
-
*/
|
|
106
|
-
interface ExhumedDescriptor {
|
|
107
|
-
readonly relations: readonly ExhumedRelation[];
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* One exhumed store: the self-described relation shapes and the raw facts
|
|
111
|
-
* by relation name. Read-only by construction — no write verb, no prepare
|
|
112
|
-
* verb. A DISPOSABLE lifetime (R12): `Symbol.dispose` releases the engine
|
|
113
|
-
* handle and the store's exclusive lock deterministically; `using` is the
|
|
114
|
-
* idiom, and a disposed value's verbs are typed refusals.
|
|
115
|
-
*/
|
|
116
|
-
interface Exhumed extends Disposable {
|
|
117
|
-
/** The store's own persisted schema, as its creator declared it. */
|
|
118
|
-
readonly descriptor: ExhumedDescriptor;
|
|
119
|
-
/**
|
|
120
|
-
* Full-relation export in row-id order, each row decoded per the
|
|
121
|
-
* STORED descriptor to a name-keyed record of natural values (str
|
|
122
|
-
* resolved through the engine's `_dict` before crossing; a closed
|
|
123
|
-
* relation scans its sealed roster). Each call reads one consistent
|
|
124
|
-
* snapshot. An unknown relation name is a typed error — the
|
|
125
|
-
* descriptor is the caller's roster.
|
|
126
|
-
*/
|
|
127
|
-
scan(relation: string): readonly ExhumedFact[];
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Opens one exhumed store at an ALREADY-CANONICAL path (`Db.exhume` is the
|
|
131
|
-
* public spelling — the path law lives in db.ts beside `open`/`create`).
|
|
132
|
-
* The bridge's three domain refusals become the typed error constants
|
|
133
|
-
* ({@link ErrExhumeNoDescriptor}, {@link ErrExhumeFormatMismatch},
|
|
134
|
-
* {@link ErrExhumeCorruption}), each carrying the engine's message in its
|
|
135
|
-
* wrap. The returned value is NOT cached: exhume is a forensic read whose
|
|
136
|
-
* lifetime is the caller's `using` scope — disposal releases the store's
|
|
137
|
-
* exclusive lock deterministically (R12), so the path is reusable the
|
|
138
|
-
* moment the scope exits.
|
|
139
|
-
*/
|
|
140
|
-
declare function exhumeStore(canonical: string): Exhumed;
|
|
141
|
-
export type { Exhumed, ExhumedAxiom, ExhumedDescriptor, ExhumedFact, ExhumedField, ExhumedRelation };
|
|
142
|
-
export { ErrExhumeCorruption, ErrExhumeFormatMismatch, ErrExhumeNoDescriptor, exhumeStore };
|
|
143
|
-
//# sourceMappingURL=exhume.d.ts.map
|
package/dist/exhume.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"exhume.d.ts","sourceRoot":"","sources":["../src/exhume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,YAAY,CAAA;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAE7C;;;;;;GAMG;AACH,QAAA,MAAM,qBAAqB,OAE1B,CAAA;AAED;;;;GAIG;AACH,QAAA,MAAM,uBAAuB,OAE5B,CAAA;AAED;;;;;GAKG;AACH,QAAA,MAAM,mBAAmB,OAA2F,CAAA;AAEpH;;;;;;GAMG;AACH,KAAK,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAA;AAEtD;;;;GAIG;AACH,UAAU,YAAY;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAA;CACjC;AAED;;;;GAIG;AACH,UAAU,YAAY;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,UAAU,eAAe;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,SAAS,CAAA;CACpD;AAED;;;;;;;GAOG;AACH,UAAU,iBAAiB;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAA;CAC9C;AAED;;;;;;GAMG;AACH,UAAU,OAAQ,SAAQ,UAAU;IACnC,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAA;IACtC;;;;;;;OAOG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,WAAW,EAAE,CAAA;CAC9C;AA+BD;;;;;;;;;;GAUG;AACH,iBAAS,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CA8E/C;AAED,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,CAAA;AACpG,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,WAAW,EAAE,CAAA"}
|
package/dist/exhume.js
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The exhume surface — the SDK's ONE schema-independent read path
|
|
3
|
-
* (docs/course-serialization/prd-02-sdk-exhume-surface.md; engine
|
|
4
|
-
* 70-api.md § exhume): a read-only, theory-less open returning the store's
|
|
5
|
-
* SELF-DESCRIBED relation shapes and raw facts by relation name. The
|
|
6
|
-
* sighting it exists for: a run store whose creating schema has since
|
|
7
|
-
* evolved — the record outlives the schema, and exhume is how the record
|
|
8
|
-
* is read back for rebirth (exhume the old store, create the successor
|
|
9
|
-
* under the new theory, copy by NAME, re-derive).
|
|
10
|
-
*
|
|
11
|
-
* DELIBERATELY SCHEMA-FREE: no schema type appears anywhere on this
|
|
12
|
-
* surface. The caller's schema is the wrong theory for an exhumed store BY
|
|
13
|
-
* DEFINITION (a store the current theory could open would never need
|
|
14
|
-
* exhuming), so every value crosses TYPED at its bare structural form
|
|
15
|
-
* ({@link FactValue} — bigint/string/boolean/bytes/interval, never
|
|
16
|
-
* `unknown`) and every fact is keyed by field NAME. The SDK never
|
|
17
|
-
* reconstructs a `Schema` value from the descriptor — that inverse mapping
|
|
18
|
-
* is deliberately out of scope; the rebirth tool keys by name.
|
|
19
|
-
*
|
|
20
|
-
* LIFETIMES ARE DISPOSABLES, never `close()` (ruled 2026-07-23, R12): the
|
|
21
|
-
* `Exhumed` value implements `Symbol.dispose` — teardown is synchronous
|
|
22
|
-
* (one environment close) — and `using` is the documented idiom:
|
|
23
|
-
*
|
|
24
|
-
* using exhumed = await Db.exhume(path)
|
|
25
|
-
*
|
|
26
|
-
* Disposal releases the engine handle AND the store's exclusive advisory
|
|
27
|
-
* lock deterministically, scope-shaped in the language's own syntax, so a
|
|
28
|
-
* same-path reopen (retry after a half-failed migration, a second forensic
|
|
29
|
-
* read) never waits on an unforceable GC finalizer. Every verb after
|
|
30
|
-
* disposal is a typed used-after-dispose refusal. The engine-side drop
|
|
31
|
-
* remains the reclamation-only backstop for a collected-but-undisposed
|
|
32
|
-
* value; the store is never written through this surface, so there is
|
|
33
|
-
* nothing to flush.
|
|
34
|
-
*/
|
|
35
|
-
import * as errors from "@superbuilders/errors";
|
|
36
|
-
import { bridged, native } from "#native.ts";
|
|
37
|
-
/**
|
|
38
|
-
* The typed `descriptorMissing` refusal: the store predates self-describing
|
|
39
|
-
* stores and has not been adopted. The remedy is in the message — one
|
|
40
|
-
* fingerprint-matching `Db.open` under the creating schema back-fills the
|
|
41
|
-
* descriptor (engine 50-storage.md § the `_meta` block) and the store is
|
|
42
|
-
* self-describing forever. Match with `errors.is`.
|
|
43
|
-
*/
|
|
44
|
-
const ErrExhumeNoDescriptor = errors.new("bumbledb exhume: the store carries no schema descriptor (not yet adopted) — open it once under its creating schema (one fingerprint-matching Db.open back-fills the descriptor; engine 50-storage.md)");
|
|
45
|
-
/**
|
|
46
|
-
* The typed `formatMismatch` refusal: the store's on-disk format version is
|
|
47
|
-
* not this engine's (no migration path exists, as everywhere). Match with
|
|
48
|
-
* `errors.is`.
|
|
49
|
-
*/
|
|
50
|
-
const ErrExhumeFormatMismatch = errors.new("bumbledb exhume: storage format version mismatch — the store was written by a different engine format");
|
|
51
|
-
/**
|
|
52
|
-
* The typed `corruption` refusal: the persisted descriptor fails its
|
|
53
|
-
* integrity gates (the stored bytes hash to something other than the stored
|
|
54
|
-
* fingerprint, or the bytes do not decode and re-encode faithfully). Match
|
|
55
|
-
* with `errors.is`.
|
|
56
|
-
*/
|
|
57
|
-
const ErrExhumeCorruption = errors.new("bumbledb exhume: the persisted schema descriptor fails its integrity gates");
|
|
58
|
-
/**
|
|
59
|
-
* Shapes the bridge's manifest rendering of the stored descriptor into the
|
|
60
|
-
* SDK's {@link ExhumedDescriptor}: same relations in the same engine-id
|
|
61
|
-
* order, extension rows re-keyed by column name. Pure reshaping — no
|
|
62
|
-
* re-validation of engine-decoded values happens here (the bridge stays
|
|
63
|
-
* dumb and so does this).
|
|
64
|
-
*/
|
|
65
|
-
function descriptorOf(manifest) {
|
|
66
|
-
const relations = manifest.relations.map(function relationOf(relation) {
|
|
67
|
-
const fields = relation.fields.map(function fieldOf(field) {
|
|
68
|
-
return Object.freeze({ name: field.name, valueType: field.valueType });
|
|
69
|
-
});
|
|
70
|
-
let roster;
|
|
71
|
-
if (relation.extension !== undefined) {
|
|
72
|
-
roster = Object.freeze(relation.extension.map(function axiomOf(row) {
|
|
73
|
-
const values = {};
|
|
74
|
-
for (const cell of row.values) {
|
|
75
|
-
values[cell.name] = cell.value;
|
|
76
|
-
}
|
|
77
|
-
return Object.freeze({ handle: row.handle, id: row.id, values: Object.freeze(values) });
|
|
78
|
-
}));
|
|
79
|
-
}
|
|
80
|
-
return Object.freeze({ name: relation.name, fields: Object.freeze(fields), roster });
|
|
81
|
-
});
|
|
82
|
-
return Object.freeze({ relations: Object.freeze(relations) });
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Opens one exhumed store at an ALREADY-CANONICAL path (`Db.exhume` is the
|
|
86
|
-
* public spelling — the path law lives in db.ts beside `open`/`create`).
|
|
87
|
-
* The bridge's three domain refusals become the typed error constants
|
|
88
|
-
* ({@link ErrExhumeNoDescriptor}, {@link ErrExhumeFormatMismatch},
|
|
89
|
-
* {@link ErrExhumeCorruption}), each carrying the engine's message in its
|
|
90
|
-
* wrap. The returned value is NOT cached: exhume is a forensic read whose
|
|
91
|
-
* lifetime is the caller's `using` scope — disposal releases the store's
|
|
92
|
-
* exclusive lock deterministically (R12), so the path is reusable the
|
|
93
|
-
* moment the scope exits.
|
|
94
|
-
*/
|
|
95
|
-
function exhumeStore(canonical) {
|
|
96
|
-
const outcome = bridged(`exhume bumbledb store at ${canonical}`, function callExhume() {
|
|
97
|
-
return native.dbExhume(canonical);
|
|
98
|
-
});
|
|
99
|
-
if (!outcome.ok) {
|
|
100
|
-
switch (outcome.kind) {
|
|
101
|
-
case "descriptorMissing":
|
|
102
|
-
throw errors.wrap(ErrExhumeNoDescriptor, `exhume ${canonical}: ${outcome.message}`);
|
|
103
|
-
case "formatMismatch":
|
|
104
|
-
throw errors.wrap(ErrExhumeFormatMismatch, `exhume ${canonical}: ${outcome.message}`);
|
|
105
|
-
case "corruption":
|
|
106
|
-
throw errors.wrap(ErrExhumeCorruption, `exhume ${canonical}: ${outcome.message}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const handle = outcome.exhume;
|
|
110
|
-
const manifest = bridged("read bumbledb exhumed descriptor", function callDescriptor() {
|
|
111
|
-
return native.exhumeDescriptor(handle);
|
|
112
|
-
});
|
|
113
|
-
const descriptor = descriptorOf(manifest);
|
|
114
|
-
const fieldNames = new Map();
|
|
115
|
-
for (const relation of descriptor.relations) {
|
|
116
|
-
fieldNames.set(relation.name, relation.fields.map(function nameOf(field) {
|
|
117
|
-
return field.name;
|
|
118
|
-
}));
|
|
119
|
-
}
|
|
120
|
-
/** The lifetime record: flipped once by {@link dispose}, judged by every verb. */
|
|
121
|
-
const lifetime = { live: true };
|
|
122
|
-
/**
|
|
123
|
-
* The `Symbol.dispose` teardown (R12): idempotent — a manual call inside
|
|
124
|
-
* a `using` scope must not double-close — and deterministic: the engine
|
|
125
|
-
* handle and the store's exclusive lock release HERE, never on a GC
|
|
126
|
-
* schedule.
|
|
127
|
-
*/
|
|
128
|
-
function dispose() {
|
|
129
|
-
if (!lifetime.live) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
lifetime.live = false;
|
|
133
|
-
bridged(`close bumbledb exhumed store at ${canonical}`, function close() {
|
|
134
|
-
native.exhumeClose(handle);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
function scan(relation) {
|
|
138
|
-
if (!lifetime.live) {
|
|
139
|
-
throw errors.new("bumbledb exhumed store is disposed — its using scope already exited");
|
|
140
|
-
}
|
|
141
|
-
const names = fieldNames.get(relation);
|
|
142
|
-
if (names === undefined) {
|
|
143
|
-
throw errors.new(`bumbledb exhume: the store's descriptor declares no relation ${relation}`);
|
|
144
|
-
}
|
|
145
|
-
const rows = bridged(`scan bumbledb exhumed relation ${relation}`, function callScan() {
|
|
146
|
-
return native.exhumeScan(handle, relation);
|
|
147
|
-
});
|
|
148
|
-
return Object.freeze(rows.map(function factOf(row) {
|
|
149
|
-
if (row.length !== names.length) {
|
|
150
|
-
throw errors.new(`bumbledb exhume drift: relation ${relation} row arity ${row.length} does not match the ${names.length} descriptor fields`);
|
|
151
|
-
}
|
|
152
|
-
const fact = {};
|
|
153
|
-
names.forEach(function pair(name, index) {
|
|
154
|
-
const cell = row[index];
|
|
155
|
-
if (cell === undefined) {
|
|
156
|
-
throw errors.new(`bumbledb exhume drift: relation ${relation} row has no value at position ${index} (${name})`);
|
|
157
|
-
}
|
|
158
|
-
fact[name] = cell;
|
|
159
|
-
});
|
|
160
|
-
return Object.freeze(fact);
|
|
161
|
-
}));
|
|
162
|
-
}
|
|
163
|
-
return Object.freeze({ descriptor, scan, [Symbol.dispose]: dispose });
|
|
164
|
-
}
|
|
165
|
-
export { ErrExhumeCorruption, ErrExhumeFormatMismatch, ErrExhumeNoDescriptor, exhumeStore };
|
|
166
|
-
//# sourceMappingURL=exhume.js.map
|
package/dist/exhume.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"exhume.js","sourceRoot":"","sources":["../src/exhume.ts"],"names":[],"mappings":"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"}
|