@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.
Files changed (46) hide show
  1. package/COOKBOOK.md +35 -21
  2. package/README.md +82 -55
  3. package/dist/db.d.ts +173 -130
  4. package/dist/db.d.ts.map +1 -1
  5. package/dist/db.js +782 -371
  6. package/dist/db.js.map +1 -1
  7. package/dist/index.d.ts +7 -13
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +4 -9
  10. package/dist/index.js.map +1 -1
  11. package/dist/marshal.d.ts +5 -27
  12. package/dist/marshal.d.ts.map +1 -1
  13. package/dist/marshal.js +4 -21
  14. package/dist/marshal.js.map +1 -1
  15. package/dist/native.d.ts +157 -168
  16. package/dist/native.d.ts.map +1 -1
  17. package/dist/native.js +46 -8
  18. package/dist/native.js.map +1 -1
  19. package/dist/query/find.d.ts +14 -9
  20. package/dist/query/find.d.ts.map +1 -1
  21. package/dist/query/find.js +2 -2
  22. package/dist/query/find.js.map +1 -1
  23. package/dist/query/lower.d.ts.map +1 -1
  24. package/dist/query/lower.js +5 -4
  25. package/dist/query/lower.js.map +1 -1
  26. package/dist/query/parse-ir.d.ts.map +1 -1
  27. package/dist/query/parse-ir.js +11 -9
  28. package/dist/query/parse-ir.js.map +1 -1
  29. package/dist/relation.d.ts +4 -25
  30. package/dist/relation.d.ts.map +1 -1
  31. package/dist/relation.js +3 -4
  32. package/dist/relation.js.map +1 -1
  33. package/package.json +3 -3
  34. package/src/db.ts +1151 -500
  35. package/src/index.ts +28 -24
  36. package/src/marshal.ts +5 -35
  37. package/src/native.ts +248 -175
  38. package/src/query/find.ts +19 -14
  39. package/src/query/lower.ts +7 -6
  40. package/src/query/parse-ir.ts +11 -9
  41. package/src/relation.ts +3 -28
  42. package/dist/exhume.d.ts +0 -143
  43. package/dist/exhume.d.ts.map +0 -1
  44. package/dist/exhume.js +0 -166
  45. package/dist/exhume.js.map +0 -1
  46. package/src/exhume.ts +0 -267
package/src/exhume.ts DELETED
@@ -1,267 +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
-
36
- import * as errors from "@superbuilders/errors"
37
- import type { FactValue, Manifest } from "#native.ts"
38
- import { bridged, native } from "#native.ts"
39
- import type { ValueTypeSpec } from "#spec.ts"
40
-
41
- /**
42
- * The typed `descriptorMissing` refusal: the store predates self-describing
43
- * stores and has not been adopted. The remedy is in the message — one
44
- * fingerprint-matching `Db.open` under the creating schema back-fills the
45
- * descriptor (engine 50-storage.md § the `_meta` block) and the store is
46
- * self-describing forever. Match with `errors.is`.
47
- */
48
- const ErrExhumeNoDescriptor = errors.new(
49
- "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)"
50
- )
51
-
52
- /**
53
- * The typed `formatMismatch` refusal: the store's on-disk format version is
54
- * not this engine's (no migration path exists, as everywhere). Match with
55
- * `errors.is`.
56
- */
57
- const ErrExhumeFormatMismatch = errors.new(
58
- "bumbledb exhume: storage format version mismatch — the store was written by a different engine format"
59
- )
60
-
61
- /**
62
- * The typed `corruption` refusal: the persisted descriptor fails its
63
- * integrity gates (the stored bytes hash to something other than the stored
64
- * fingerprint, or the bytes do not decode and re-encode faithfully). Match
65
- * with `errors.is`.
66
- */
67
- const ErrExhumeCorruption = errors.new("bumbledb exhume: the persisted schema descriptor fails its integrity gates")
68
-
69
- /**
70
- * One exhumed fact as a plain name-keyed record of bare structural values
71
- * — deliberately schema-free (module doc): `bigint` for u64/i64, `string`
72
- * for str, `boolean` for bool, `Uint8Array` for bytes<N>, a `{ start, end }`
73
- * bigint pair for intervals. Closed-relation rows open with the synthetic
74
- * `id` field, exactly as the descriptor's sealed field list declares.
75
- */
76
- type ExhumedFact = Readonly<Record<string, FactValue>>
77
-
78
- /**
79
- * One field of an exhumed relation, exactly as the store's creator declared
80
- * it: the name and the structural type tag (byte width on `fixedBytes`,
81
- * element and optional width on `interval`).
82
- */
83
- interface ExhumedField {
84
- readonly name: string
85
- readonly valueType: ValueTypeSpec
86
- }
87
-
88
- /**
89
- * One ground axiom of an exhumed closed relation: the handle (the row's
90
- * identity, never a column), the declaration-order row id, and the declared
91
- * payload columns by name.
92
- */
93
- interface ExhumedAxiom {
94
- readonly handle: string
95
- readonly id: bigint
96
- readonly values: ExhumedFact
97
- }
98
-
99
- /**
100
- * One relation of an exhumed store: the name, the SEALED field list in
101
- * declaration order (a closed relation opens with the synthetic (`id`,
102
- * u64) handle field), and — exactly on closed relations — the roster of
103
- * ground axioms. Scan rows come back in this field order, so pairing a
104
- * row's positions against `fields` is the name-keyed reading `scan`
105
- * performs.
106
- */
107
- interface ExhumedRelation {
108
- readonly name: string
109
- readonly fields: readonly ExhumedField[]
110
- readonly roster: readonly ExhumedAxiom[] | undefined
111
- }
112
-
113
- /**
114
- * The exhumed store's schema as declared, decoded from the store's own
115
- * persisted descriptor: relations in engine-id order (declaration order
116
- * mints every id), each with its ordered field descriptions and closed
117
- * roster — enough for a caller to key facts by name and re-insert them
118
- * into a differently-fingerprinted successor store. No schema type appears
119
- * here (module doc: the surface is schema-free; rows are typed bare).
120
- */
121
- interface ExhumedDescriptor {
122
- readonly relations: readonly ExhumedRelation[]
123
- }
124
-
125
- /**
126
- * One exhumed store: the self-described relation shapes and the raw facts
127
- * by relation name. Read-only by construction — no write verb, no prepare
128
- * verb. A DISPOSABLE lifetime (R12): `Symbol.dispose` releases the engine
129
- * handle and the store's exclusive lock deterministically; `using` is the
130
- * idiom, and a disposed value's verbs are typed refusals.
131
- */
132
- interface Exhumed extends Disposable {
133
- /** The store's own persisted schema, as its creator declared it. */
134
- readonly descriptor: ExhumedDescriptor
135
- /**
136
- * Full-relation export in row-id order, each row decoded per the
137
- * STORED descriptor to a name-keyed record of natural values (str
138
- * resolved through the engine's `_dict` before crossing; a closed
139
- * relation scans its sealed roster). Each call reads one consistent
140
- * snapshot. An unknown relation name is a typed error — the
141
- * descriptor is the caller's roster.
142
- */
143
- scan(relation: string): readonly ExhumedFact[]
144
- }
145
-
146
- /**
147
- * Shapes the bridge's manifest rendering of the stored descriptor into the
148
- * SDK's {@link ExhumedDescriptor}: same relations in the same engine-id
149
- * order, extension rows re-keyed by column name. Pure reshaping — no
150
- * re-validation of engine-decoded values happens here (the bridge stays
151
- * dumb and so does this).
152
- */
153
- function descriptorOf(manifest: Manifest): ExhumedDescriptor {
154
- const relations = manifest.relations.map(function relationOf(relation): ExhumedRelation {
155
- const fields = relation.fields.map(function fieldOf(field): ExhumedField {
156
- return Object.freeze({ name: field.name, valueType: field.valueType })
157
- })
158
- let roster: readonly ExhumedAxiom[] | undefined
159
- if (relation.extension !== undefined) {
160
- roster = Object.freeze(
161
- relation.extension.map(function axiomOf(row): ExhumedAxiom {
162
- const values: Record<string, FactValue> = {}
163
- for (const cell of row.values) {
164
- values[cell.name] = cell.value
165
- }
166
- return Object.freeze({ handle: row.handle, id: row.id, values: Object.freeze(values) })
167
- })
168
- )
169
- }
170
- return Object.freeze({ name: relation.name, fields: Object.freeze(fields), roster })
171
- })
172
- return Object.freeze({ relations: Object.freeze(relations) })
173
- }
174
-
175
- /**
176
- * Opens one exhumed store at an ALREADY-CANONICAL path (`Db.exhume` is the
177
- * public spelling — the path law lives in db.ts beside `open`/`create`).
178
- * The bridge's three domain refusals become the typed error constants
179
- * ({@link ErrExhumeNoDescriptor}, {@link ErrExhumeFormatMismatch},
180
- * {@link ErrExhumeCorruption}), each carrying the engine's message in its
181
- * wrap. The returned value is NOT cached: exhume is a forensic read whose
182
- * lifetime is the caller's `using` scope — disposal releases the store's
183
- * exclusive lock deterministically (R12), so the path is reusable the
184
- * moment the scope exits.
185
- */
186
- function exhumeStore(canonical: string): Exhumed {
187
- const outcome = bridged(`exhume bumbledb store at ${canonical}`, function callExhume() {
188
- return native.dbExhume(canonical)
189
- })
190
- if (!outcome.ok) {
191
- switch (outcome.kind) {
192
- case "descriptorMissing":
193
- throw errors.wrap(ErrExhumeNoDescriptor, `exhume ${canonical}: ${outcome.message}`)
194
- case "formatMismatch":
195
- throw errors.wrap(ErrExhumeFormatMismatch, `exhume ${canonical}: ${outcome.message}`)
196
- case "corruption":
197
- throw errors.wrap(ErrExhumeCorruption, `exhume ${canonical}: ${outcome.message}`)
198
- }
199
- }
200
- const handle = outcome.exhume
201
- const manifest = bridged("read bumbledb exhumed descriptor", function callDescriptor() {
202
- return native.exhumeDescriptor(handle)
203
- })
204
- const descriptor = descriptorOf(manifest)
205
- const fieldNames = new Map<string, readonly string[]>()
206
- for (const relation of descriptor.relations) {
207
- fieldNames.set(
208
- relation.name,
209
- relation.fields.map(function nameOf(field) {
210
- return field.name
211
- })
212
- )
213
- }
214
- /** The lifetime record: flipped once by {@link dispose}, judged by every verb. */
215
- const lifetime = { live: true }
216
- /**
217
- * The `Symbol.dispose` teardown (R12): idempotent — a manual call inside
218
- * a `using` scope must not double-close — and deterministic: the engine
219
- * handle and the store's exclusive lock release HERE, never on a GC
220
- * schedule.
221
- */
222
- function dispose(): void {
223
- if (!lifetime.live) {
224
- return
225
- }
226
- lifetime.live = false
227
- bridged(`close bumbledb exhumed store at ${canonical}`, function close() {
228
- native.exhumeClose(handle)
229
- })
230
- }
231
- function scan(relation: string): readonly ExhumedFact[] {
232
- if (!lifetime.live) {
233
- throw errors.new("bumbledb exhumed store is disposed — its using scope already exited")
234
- }
235
- const names = fieldNames.get(relation)
236
- if (names === undefined) {
237
- throw errors.new(`bumbledb exhume: the store's descriptor declares no relation ${relation}`)
238
- }
239
- const rows = bridged(`scan bumbledb exhumed relation ${relation}`, function callScan() {
240
- return native.exhumeScan(handle, relation)
241
- })
242
- return Object.freeze(
243
- rows.map(function factOf(row): ExhumedFact {
244
- if (row.length !== names.length) {
245
- throw errors.new(
246
- `bumbledb exhume drift: relation ${relation} row arity ${row.length} does not match the ${names.length} descriptor fields`
247
- )
248
- }
249
- const fact: Record<string, FactValue> = {}
250
- names.forEach(function pair(name, index) {
251
- const cell = row[index]
252
- if (cell === undefined) {
253
- throw errors.new(
254
- `bumbledb exhume drift: relation ${relation} row has no value at position ${index} (${name})`
255
- )
256
- }
257
- fact[name] = cell
258
- })
259
- return Object.freeze(fact)
260
- })
261
- )
262
- }
263
- return Object.freeze({ descriptor, scan, [Symbol.dispose]: dispose })
264
- }
265
-
266
- export type { Exhumed, ExhumedAxiom, ExhumedDescriptor, ExhumedFact, ExhumedField, ExhumedRelation }
267
- export { ErrExhumeCorruption, ErrExhumeFormatMismatch, ErrExhumeNoDescriptor, exhumeStore }