@bjornpagen/bumbledb 0.15.0 → 0.17.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 +33 -49
- package/README.md +3 -3
- package/dist/capacity.d.ts +24 -136
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +18 -40
- package/dist/capacity.js.map +1 -1
- package/dist/closed.d.ts +0 -156
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +0 -104
- package/dist/closed.js.map +1 -1
- package/dist/db.d.ts +7 -223
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +147 -396
- package/dist/db.js.map +1 -1
- package/dist/face.d.ts +0 -133
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +0 -33
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +1 -145
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +2 -91
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +11 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -13
- package/dist/index.js.map +1 -1
- package/dist/law.d.ts +111 -93
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +23 -27
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts +9 -35
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +8 -53
- package/dist/lower.js.map +1 -1
- package/dist/marshal.d.ts +0 -65
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js +0 -72
- package/dist/marshal.js.map +1 -1
- package/dist/native.d.ts +25 -290
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +6 -66
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +10 -276
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +1 -96
- package/dist/query/atom.js.map +1 -1
- package/dist/query/find.d.ts +10 -76
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +0 -30
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +64 -146
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +19 -256
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +0 -7
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +1 -13
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts +0 -36
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +0 -44
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +24 -180
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +2 -66
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +2 -50
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +2 -37
- package/dist/relation.js.map +1 -1
- package/dist/schema.d.ts +13 -63
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +118 -92
- package/dist/schema.js.map +1 -1
- package/dist/spec.d.ts +1 -140
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js +1 -68
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +6 -137
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +16 -119
- package/dist/statements.js.map +1 -1
- package/package.json +2 -2
- package/src/capacity.ts +26 -140
- package/src/closed.ts +5 -206
- package/src/db.ts +203 -692
- package/src/face.ts +0 -142
- package/src/fields.ts +4 -172
- package/src/index.ts +9 -15
- package/src/law.ts +201 -129
- package/src/lower.ts +8 -53
- package/src/marshal.ts +1 -85
- package/src/native.ts +47 -323
- package/src/query/atom.ts +26 -313
- package/src/query/find.ts +24 -110
- package/src/query/lower.ts +132 -377
- package/src/query/parse-ir.ts +1 -14
- package/src/query/run.ts +0 -45
- package/src/query/scope.ts +25 -186
- package/src/relation.ts +2 -66
- package/src/schema.ts +143 -122
- package/src/spec.ts +1 -160
- package/src/statements.ts +22 -174
package/src/native.ts
CHANGED
|
@@ -2,98 +2,41 @@ import { createRequire } from "node:module"
|
|
|
2
2
|
import * as errors from "@superbuilders/errors"
|
|
3
3
|
import type { SchemaSpec, ValueSpec, ValueTypeSpec } from "#spec.ts"
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* The complete typed surface of the bumbledb-node napi bridge. ALL FFI
|
|
7
|
-
* typing lives in this one file — no other module may know the `.node`
|
|
8
|
-
* artifact exists. The bridge is a dumb bridge (PRD-04): descriptor in as
|
|
9
|
-
* data, queries in as IR data, facts in/out as value rows, rejections out as
|
|
10
|
-
* structured violation sets; anything smart lives in the SDK or the engine.
|
|
11
|
-
*
|
|
12
|
-
* Marshaling law: fact rows cross as NATURAL JS values, schema-directed by
|
|
13
|
-
* the engine descriptor (`boolean ⇄ bool`, `bigint ⇄ u64/i64`,
|
|
14
|
-
* `string ⇄ str`, `Uint8Array ⇄ bytes<N>`, `{ start, end }` ⇄ interval);
|
|
15
|
-
* IR, spec, and query params cross as TAGGED plain objects mirroring the
|
|
16
|
-
* engine's own data enums 1:1. Every u64/i64 crosses as `bigint`, never
|
|
17
|
-
* `number`. Domain outcomes (schema errors, fingerprint mismatches,
|
|
18
|
-
* rejections, generation moves, IR errors) are DATA; marshaling/shape
|
|
19
|
-
* violations and use-after-close THROW.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
5
|
/** The opaque database handle (owns the LMDB environment + exclusive lock). */
|
|
23
6
|
type DbHandle = { readonly __brand: "bumbledb.db" }
|
|
24
7
|
|
|
25
|
-
/** One live borrowed instance valid only inside a read callback. */
|
|
26
8
|
type InstanceHandle = { readonly __brand: "bumbledb.instance" }
|
|
27
9
|
|
|
28
|
-
/** One cloneable generation witness. May outlive the read that minted it. */
|
|
29
10
|
type WitnessHandle = { readonly __brand: "bumbledb.witness" }
|
|
30
11
|
|
|
31
|
-
/** One unproved heap builder. Spent by `instanceBuilderAdmit` / close. */
|
|
32
12
|
type BuilderHandle = { readonly __brand: "bumbledb.builder" }
|
|
33
13
|
|
|
34
|
-
/** One admitted heap instance. */
|
|
35
14
|
type OwnedHandle = { readonly __brand: "bumbledb.owned" }
|
|
36
15
|
|
|
37
|
-
/**
|
|
38
|
-
* One live write transaction — the submitted delta with the engine's
|
|
39
|
-
* final-state point-read view. Valid only inside a write callback.
|
|
40
|
-
*/
|
|
41
16
|
type TxHandle = { readonly __brand: "bumbledb.tx" }
|
|
42
17
|
|
|
43
|
-
/** One prepared query (plan pinned at prepare). */
|
|
44
18
|
type PreparedHandle = { readonly __brand: "bumbledb.prepared" }
|
|
45
19
|
|
|
46
|
-
/**
|
|
47
|
-
* Engine mutation report as it crosses napi: both counts are engine
|
|
48
|
-
* values, never reconstructed from JS length.
|
|
49
|
-
*/
|
|
50
20
|
interface WireMutationReport {
|
|
51
21
|
readonly submitted: bigint
|
|
52
22
|
readonly changed: bigint
|
|
53
23
|
}
|
|
54
24
|
|
|
55
|
-
/**
|
|
56
|
-
* Engine fresh-id range as it crosses napi. Empty cannot yield a start —
|
|
57
|
-
* `start` is a minted id only on the nonempty arm. (C wires empty as
|
|
58
|
-
* `BDB_FRESH_RANGE_TAG_EMPTY`. The JS wire is `{ empty: true }`, not that
|
|
59
|
-
* C sentinel.)
|
|
60
|
-
*/
|
|
61
25
|
type WireFreshRange =
|
|
62
26
|
| { readonly empty: true }
|
|
63
27
|
| { readonly empty: false; readonly start: bigint; readonly endExclusive: bigint }
|
|
64
28
|
|
|
65
|
-
/** A half-open interval `[start, end)` as it crosses the boundary. */
|
|
66
29
|
interface IntervalValue {
|
|
67
30
|
readonly start: bigint
|
|
68
31
|
readonly end: bigint
|
|
69
32
|
}
|
|
70
33
|
|
|
71
|
-
/**
|
|
72
|
-
* One fact-row cell as a natural JS value. The expected engine type comes
|
|
73
|
-
* from the schema descriptor (marshaling is schema-directed, never guessed):
|
|
74
|
-
* `boolean` for bool, `bigint` for u64/i64, `string` for str, `Uint8Array`
|
|
75
|
-
* for bytes<N> (width-checked), `{ start, end }` for intervals.
|
|
76
|
-
*/
|
|
77
34
|
type FactValue = boolean | bigint | string | Uint8Array | IntervalValue
|
|
78
35
|
|
|
79
|
-
/**
|
|
80
|
-
* One tagged engine value — the 1:1 mirror of `bumbledb::Value` for the
|
|
81
|
-
* positions no schema field directs (IR literals, query params).
|
|
82
|
-
*/
|
|
83
36
|
type TaggedValue = ValueSpec
|
|
84
37
|
|
|
85
|
-
/**
|
|
86
|
-
* One positional execution argument: a tagged scalar, or a param SET
|
|
87
|
-
* (`Term.paramSet` positions) as `{ kind: "set", values }`.
|
|
88
|
-
*/
|
|
89
38
|
type QueryParam = TaggedValue | { readonly kind: "set"; readonly values: readonly TaggedValue[] }
|
|
90
39
|
|
|
91
|
-
/**
|
|
92
|
-
* The IR mirror (`bumbledb::ir`, 1:1): relations, fields, interiors, and
|
|
93
|
-
* params by NUMERIC id — the SDK resolves names through the manifest and
|
|
94
|
-
* sends ids; the bridge never sees names in queries. Q1 is a tagged sum:
|
|
95
|
-
* CQ carries no rec; Reach carries `rec` by value.
|
|
96
|
-
*/
|
|
97
40
|
type QueryIr =
|
|
98
41
|
| {
|
|
99
42
|
readonly kind: "cq"
|
|
@@ -109,26 +52,21 @@ type QueryIr =
|
|
|
109
52
|
readonly rules: readonly RuleIr[]
|
|
110
53
|
}
|
|
111
54
|
|
|
112
|
-
/** One named interior: the head shape its rules align against, and the rules. */
|
|
113
55
|
interface InteriorIr {
|
|
114
56
|
readonly head: readonly HeadTermIr[]
|
|
115
57
|
readonly rules: readonly RuleIr[]
|
|
116
58
|
}
|
|
117
59
|
|
|
118
|
-
/** The linear rec on a Reach query: shared head, base arms, rec arms. */
|
|
119
60
|
interface RecIr {
|
|
120
61
|
readonly head: readonly HeadTermIr[]
|
|
121
62
|
readonly base: readonly RuleIr[]
|
|
122
63
|
readonly rec: readonly RuleIr[]
|
|
123
64
|
}
|
|
124
65
|
|
|
125
|
-
/** One head position: a plain variable slot or an aggregate-op kind. */
|
|
126
66
|
type HeadTermIr = { readonly kind: "var" } | { readonly kind: "aggregate"; readonly op: HeadOpIr }
|
|
127
67
|
|
|
128
|
-
/** The var-free aggregate-op kind at a head position. */
|
|
129
68
|
type HeadOpIr = "sum" | "min" | "max" | "count" | "pack"
|
|
130
69
|
|
|
131
|
-
/** One rule: conjunctive body, anti-join atoms, condition trees. */
|
|
132
70
|
interface RuleIr {
|
|
133
71
|
readonly finds: readonly FindTermIr[]
|
|
134
72
|
readonly atoms: readonly AtomIr[]
|
|
@@ -136,7 +74,6 @@ interface RuleIr {
|
|
|
136
74
|
readonly conditions: readonly ConditionTreeIr[]
|
|
137
75
|
}
|
|
138
76
|
|
|
139
|
-
/** One find term (mirrors `ir::FindTerm`). Count is nullary; pack and folds carry `over`. */
|
|
140
77
|
type FoldOpIr = { readonly kind: "sum" } | { readonly kind: "min" } | { readonly kind: "max" }
|
|
141
78
|
|
|
142
79
|
type FindTermIr =
|
|
@@ -144,16 +81,11 @@ type FindTermIr =
|
|
|
144
81
|
| { readonly kind: "count" }
|
|
145
82
|
| { readonly kind: "aggregate"; readonly op: FoldOpIr; readonly over: number }
|
|
146
83
|
| { readonly kind: "pack"; readonly over: number }
|
|
147
|
-
| { readonly kind: "measure"; readonly var: number }
|
|
148
|
-
| { readonly kind: "aggregateMeasure"; readonly op: FoldOpIr; readonly over: number }
|
|
149
84
|
|
|
150
|
-
/** Host brand: only {@link parseQueryIr} and `lowerQuery` inhabit this. Phantom — not a runtime key. */
|
|
151
85
|
declare const parsedQueryBrand: unique symbol
|
|
152
86
|
|
|
153
|
-
/** A `QueryIr` that passed the host shape parse (rec/main nonempty, aggregate finds split). */
|
|
154
87
|
type ParsedQuery = QueryIr & { readonly [parsedQueryBrand]: true }
|
|
155
88
|
|
|
156
|
-
/** One aggregate operator (mirrors `ir::AggOp`; Arg ops carry their key). */
|
|
157
89
|
type AggOpIr =
|
|
158
90
|
| { readonly kind: "sum" }
|
|
159
91
|
| { readonly kind: "min" }
|
|
@@ -161,29 +93,21 @@ type AggOpIr =
|
|
|
161
93
|
| { readonly kind: "count" }
|
|
162
94
|
| { readonly kind: "pack" }
|
|
163
95
|
|
|
164
|
-
/** Where an atom draws its facts: a stored relation or a derived table. */
|
|
165
96
|
type AtomSourceIr =
|
|
166
97
|
| { readonly kind: "edb"; readonly relation: number }
|
|
167
98
|
| { readonly kind: "interior"; readonly interior: number }
|
|
168
99
|
|
|
169
|
-
/**
|
|
170
|
-
* One atom: named-field bindings as `[fieldId, term]` pairs; absence of a
|
|
171
|
-
* field is the wildcard.
|
|
172
|
-
*/
|
|
173
100
|
interface AtomIr {
|
|
174
101
|
readonly source: AtomSourceIr
|
|
175
102
|
readonly bindings: ReadonlyArray<readonly [number, TermIr]>
|
|
176
103
|
}
|
|
177
104
|
|
|
178
|
-
/** One term of an atom binding or comparison (mirrors `ir::Term`). */
|
|
179
105
|
type TermIr =
|
|
180
106
|
| { readonly kind: "var"; readonly var: number }
|
|
181
107
|
| { readonly kind: "param"; readonly param: number }
|
|
182
108
|
| { readonly kind: "paramSet"; readonly param: number }
|
|
183
109
|
| { readonly kind: "literal"; readonly value: TaggedValue }
|
|
184
|
-
| { readonly kind: "measure"; readonly var: number }
|
|
185
110
|
|
|
186
|
-
/** One comparison operator (mirrors `ir::CmpOp`). */
|
|
187
111
|
type CmpOpIr =
|
|
188
112
|
| { readonly kind: "eq" }
|
|
189
113
|
| { readonly kind: "ne" }
|
|
@@ -194,46 +118,31 @@ type CmpOpIr =
|
|
|
194
118
|
| { readonly kind: "allen"; readonly mask: number }
|
|
195
119
|
| { readonly kind: "pointIn" }
|
|
196
120
|
|
|
197
|
-
/** One comparison condition. */
|
|
198
121
|
interface ComparisonIr {
|
|
199
122
|
readonly op: CmpOpIr
|
|
200
123
|
readonly lhs: TermIr
|
|
201
124
|
readonly rhs: TermIr
|
|
202
125
|
}
|
|
203
126
|
|
|
204
|
-
/**
|
|
205
|
-
* The input condition grammar: any boolean combination of comparisons
|
|
206
|
-
* (validation distributes to DNF engine-side).
|
|
207
|
-
*/
|
|
208
127
|
type ConditionTreeIr =
|
|
209
128
|
| { readonly kind: "leaf"; readonly cmp: ComparisonIr }
|
|
210
129
|
| { readonly kind: "and"; readonly children: readonly ConditionTreeIr[] }
|
|
211
130
|
| { readonly kind: "or"; readonly children: readonly ConditionTreeIr[] }
|
|
212
131
|
|
|
213
|
-
/** A statement's form tag. */
|
|
214
132
|
type StatementKindTag = "functionality" | "containment" | "capacity"
|
|
215
133
|
|
|
216
|
-
/** One field's name, dense id, and structural type. */
|
|
217
134
|
interface ManifestField {
|
|
218
135
|
readonly name: string
|
|
219
136
|
readonly id: number
|
|
220
137
|
readonly valueType: ValueTypeSpec
|
|
221
138
|
}
|
|
222
139
|
|
|
223
|
-
/**
|
|
224
|
-
* One closed-relation ground axiom as manifest data: handle →
|
|
225
|
-
* declaration-order id → (column, value) pairs.
|
|
226
|
-
*/
|
|
227
140
|
interface ManifestRow {
|
|
228
141
|
readonly handle: string
|
|
229
142
|
readonly id: bigint
|
|
230
143
|
readonly values: ReadonlyArray<{ readonly name: string; readonly value: FactValue }>
|
|
231
144
|
}
|
|
232
145
|
|
|
233
|
-
/**
|
|
234
|
-
* One relation's names and ids; a closed relation's sealed field list opens
|
|
235
|
-
* with the synthetic (`id`, u64) handle field and carries its extension.
|
|
236
|
-
*/
|
|
237
146
|
interface ManifestRelation {
|
|
238
147
|
readonly name: string
|
|
239
148
|
readonly id: number
|
|
@@ -241,37 +150,22 @@ interface ManifestRelation {
|
|
|
241
150
|
readonly extension?: readonly ManifestRow[]
|
|
242
151
|
}
|
|
243
152
|
|
|
244
|
-
/** One statement's identity, form tag, and canonical spelling. */
|
|
245
153
|
interface ManifestStatement {
|
|
246
154
|
readonly id: number
|
|
247
155
|
readonly kind: StatementKindTag
|
|
248
156
|
readonly spelling: string
|
|
249
157
|
}
|
|
250
158
|
|
|
251
|
-
/**
|
|
252
|
-
* The theory's manifest: every name → id pairing as plain data (PRD-02's
|
|
253
|
-
* tables, one JS object) — called once per open by the SDK.
|
|
254
|
-
*/
|
|
255
159
|
interface Manifest {
|
|
256
160
|
readonly relations: readonly ManifestRelation[]
|
|
257
161
|
readonly statements: readonly ManifestStatement[]
|
|
258
162
|
}
|
|
259
163
|
|
|
260
|
-
/** One offending fact of a violation, decoded to named natural values. */
|
|
261
164
|
interface ViolationFact {
|
|
262
165
|
readonly relation: string
|
|
263
166
|
readonly fields: ReadonlyArray<{ readonly name: string; readonly value: FactValue }>
|
|
264
167
|
}
|
|
265
168
|
|
|
266
|
-
/**
|
|
267
|
-
* One violated statement of a rejected commit, rendered to plain data: the
|
|
268
|
-
* statement id (materialized order), form tag, CANONICAL spelling (the
|
|
269
|
-
* engine's one renderer — a bijection on legal statements, paste-back-able),
|
|
270
|
-
* the form's direction/measure payloads, and the decoded offending facts.
|
|
271
|
-
* `measure` is the capacity form's witnessed group total — the engine
|
|
272
|
-
* accumulates in u128 and the value crosses WHOLE as bigint (C3:
|
|
273
|
-
* truncation is unrepresentable).
|
|
274
|
-
*/
|
|
275
169
|
type Violation =
|
|
276
170
|
| {
|
|
277
171
|
readonly statementId: number
|
|
@@ -294,11 +188,6 @@ type Violation =
|
|
|
294
188
|
readonly facts: readonly ViolationFact[]
|
|
295
189
|
}
|
|
296
190
|
|
|
297
|
-
/**
|
|
298
|
-
* `dbCreate`'s domain outcome. Admission is `accepted` / `rejected`.
|
|
299
|
-
* Declaration-boundary refusals ride as their own tags (not theory
|
|
300
|
-
* admission) and the SDK throws them.
|
|
301
|
-
*/
|
|
302
191
|
type CreateResult =
|
|
303
192
|
| { readonly tag: "accepted"; readonly db: DbHandle }
|
|
304
193
|
| { readonly tag: "rejected"; readonly violations: readonly Violation[] }
|
|
@@ -320,45 +209,20 @@ type DbOpenResult =
|
|
|
320
209
|
readonly message: string
|
|
321
210
|
}
|
|
322
211
|
|
|
323
|
-
/**
|
|
324
|
-
* `dbWrite` / `dbWriteFrom` native outcome. The SDK attaches the callback
|
|
325
|
-
* return onto the accepted arm. Moved is data, never an error kind.
|
|
326
|
-
*/
|
|
327
212
|
type NativeWriteOutcome =
|
|
328
213
|
| { readonly tag: "accepted"; readonly generation: bigint }
|
|
329
214
|
| { readonly tag: "rejected"; readonly violations: readonly Violation[] }
|
|
330
215
|
| { readonly tag: "abandoned" }
|
|
331
216
|
| { readonly tag: "moved"; readonly witnessed: bigint; readonly current: bigint }
|
|
332
217
|
|
|
333
|
-
/**
|
|
334
|
-
* Builder `admit` native outcome.
|
|
335
|
-
*/
|
|
336
218
|
type AdmitResult =
|
|
337
219
|
| { readonly tag: "accepted"; readonly value: OwnedHandle }
|
|
338
220
|
| { readonly tag: "rejected"; readonly violations: readonly Violation[] }
|
|
339
221
|
|
|
340
|
-
/** `dbPrepare`/`instancePrepare`'s domain outcome (IR roster errors are data). */
|
|
341
222
|
type PrepareResult =
|
|
342
223
|
| { readonly ok: true; readonly prepared: PreparedHandle }
|
|
343
224
|
| { readonly ok: false; readonly kind: "irError"; readonly message: string }
|
|
344
225
|
|
|
345
|
-
/** One occurrence's plan drift (pinned vs live row counts). */
|
|
346
|
-
interface OccurrenceDrift {
|
|
347
|
-
readonly relation: number
|
|
348
|
-
readonly pinned: bigint
|
|
349
|
-
readonly live: bigint
|
|
350
|
-
readonly ratio: number
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* The pull-based plan-drift report: engine-policy-free — no threshold
|
|
355
|
-
* exists engine-side; the host owns reprepare policy.
|
|
356
|
-
*/
|
|
357
|
-
interface Staleness {
|
|
358
|
-
readonly perOccurrence: readonly OccurrenceDrift[]
|
|
359
|
-
readonly maxRatio: number
|
|
360
|
-
}
|
|
361
|
-
|
|
362
226
|
type ErrorFamilyKind =
|
|
363
227
|
| "formatMismatch"
|
|
364
228
|
| "schemaMismatch"
|
|
@@ -379,7 +243,6 @@ type ErrorFamilyKind =
|
|
|
379
243
|
| "foreignPrepared"
|
|
380
244
|
| "foreignWitness"
|
|
381
245
|
| "param"
|
|
382
|
-
| "measureOfRay"
|
|
383
246
|
| "capacityRayMeasure"
|
|
384
247
|
| "derivedBudgetExceeded"
|
|
385
248
|
| "overflow"
|
|
@@ -391,66 +254,19 @@ type WriteTag = "accepted" | "rejected" | "abandoned" | "moved"
|
|
|
391
254
|
type OpenKind = "schemaError" | "newtypeMismatch" | "fingerprintMismatch"
|
|
392
255
|
type PrepareKind = "irError"
|
|
393
256
|
|
|
394
|
-
/**
|
|
395
|
-
* The plan-as-data report (ruled 2026-07-23, R13): the engine's
|
|
396
|
-
* `ExecutionStats` rendered to plain objects — camelCase keys, u64
|
|
397
|
-
* counters as `bigint`. A diagnostic surface, EXPLICITLY UNFROZEN: the
|
|
398
|
-
* shape follows the plan representation wherever it goes and no
|
|
399
|
-
* compatibility claim attaches, so this typing names the stable spine
|
|
400
|
-
* (version, emits, the plan sections) and leaves each section's leaves
|
|
401
|
-
* open for the host to introspect.
|
|
402
|
-
*/
|
|
403
|
-
interface Explain {
|
|
404
|
-
readonly introspectionVersion: number
|
|
405
|
-
readonly emits: bigint
|
|
406
|
-
readonly disjointRules?: Readonly<Record<string, unknown>>
|
|
407
|
-
readonly subsumed: ReadonlyArray<Readonly<Record<string, unknown>>>
|
|
408
|
-
readonly dead: ReadonlyArray<Readonly<Record<string, unknown>>>
|
|
409
|
-
readonly rules: ReadonlyArray<Readonly<Record<string, unknown>>>
|
|
410
|
-
readonly interiors: ReadonlyArray<Readonly<Record<string, unknown>>>
|
|
411
|
-
readonly reach?: Readonly<Record<string, unknown>>
|
|
412
|
-
}
|
|
413
|
-
|
|
414
257
|
interface Native {
|
|
415
|
-
/**
|
|
416
|
-
* Proof-of-life export (PRD-03): a non-empty string naming the bridge
|
|
417
|
-
* crate version and the engine's storage format version — evidence the
|
|
418
|
-
* cargo path dependency compiled, linked, and loaded through Node-API.
|
|
419
|
-
*/
|
|
420
258
|
engineVersion(): string
|
|
421
259
|
|
|
422
|
-
/**
|
|
423
|
-
* Creates a fresh durable store at `path`. Refuses an already-initialized
|
|
424
|
-
* directory (throws); schema failures return as data.
|
|
425
|
-
*/
|
|
426
260
|
dbCreate(path: string, spec: SchemaSpec): Promise<CreateResult>
|
|
427
|
-
|
|
428
|
-
* Opens an existing durable store, verifying format version and
|
|
429
|
-
* schema fingerprint (`fingerprintMismatch` as data).
|
|
430
|
-
*/
|
|
261
|
+
|
|
431
262
|
dbOpen(path: string, spec: SchemaSpec): Promise<DbOpenResult>
|
|
432
|
-
|
|
433
|
-
* Closes the handle. Dependent handles each hold the engine alive; the
|
|
434
|
-
* environment (and its exclusive lock) releases when the last closes.
|
|
435
|
-
*/
|
|
436
|
-
dbClose(db: DbHandle): void
|
|
437
|
-
/** The PRD-02 manifest — every name → id table, one plain object. */
|
|
263
|
+
|
|
438
264
|
dbManifest(db: DbHandle): Manifest
|
|
439
|
-
|
|
440
|
-
* The open store's schema fingerprint, 64 lowercase hex chars — the
|
|
441
|
-
* cross-host identity readback (`dbCreate` stored this exact value,
|
|
442
|
-
* `dbOpen` verified it). The engine computes; the bridge hex-encodes.
|
|
443
|
-
* Test-facing (the cross-host fingerprint lock); the SDK surface stays
|
|
444
|
-
* bijective with the Rust surface, which exposes no fingerprint
|
|
445
|
-
* accessor on `Db` — so no `Db` method wraps this.
|
|
446
|
-
*/
|
|
265
|
+
|
|
447
266
|
dbFingerprint(db: DbHandle): string
|
|
448
|
-
|
|
449
|
-
* The current committed generation — diagnostics only. The write-side
|
|
450
|
-
* witness is always a {@link WitnessHandle}, never this integer.
|
|
451
|
-
*/
|
|
267
|
+
|
|
452
268
|
dbGeneration(db: DbHandle): bigint
|
|
453
|
-
|
|
269
|
+
|
|
454
270
|
dbFromInstance(path: string, instance: OwnedHandle): Promise<DbHandle>
|
|
455
271
|
|
|
456
272
|
/**
|
|
@@ -461,6 +277,8 @@ interface Native {
|
|
|
461
277
|
dbRead<R>(db: DbHandle, callback: (instance: InstanceHandle, witness: WitnessHandle) => R): R
|
|
462
278
|
instanceGeneration(instance: InstanceHandle): bigint
|
|
463
279
|
instanceScan(instance: InstanceHandle, relationId: number): FactValue[][]
|
|
280
|
+
|
|
281
|
+
instanceCount(instance: InstanceHandle, relationId: number): bigint
|
|
464
282
|
instanceContains(instance: InstanceHandle, relationId: number, values: readonly FactValue[]): boolean
|
|
465
283
|
instanceGet(
|
|
466
284
|
instance: InstanceHandle,
|
|
@@ -471,92 +289,54 @@ interface Native {
|
|
|
471
289
|
instancePrepare(instance: InstanceHandle, query: ParsedQuery): PrepareResult
|
|
472
290
|
witnessClose(witness: WitnessHandle): void
|
|
473
291
|
|
|
474
|
-
/**
|
|
475
|
-
* Runs `callback` synchronously inside the engine write region.
|
|
476
|
-
* Return `true` to commit, `false` to abandon. Nested writes throw.
|
|
477
|
-
*/
|
|
478
292
|
dbWrite(db: DbHandle, callback: (tx: TxHandle) => boolean): NativeWriteOutcome
|
|
479
|
-
|
|
480
|
-
* Witnessed write: `moved` is data when the store advanced since the
|
|
481
|
-
* witness was minted. The callback does not run on that arm.
|
|
482
|
-
*/
|
|
293
|
+
|
|
483
294
|
dbWriteFrom(db: DbHandle, witness: WitnessHandle, callback: (tx: TxHandle) => boolean): NativeWriteOutcome
|
|
484
295
|
/**
|
|
485
296
|
* Records a collection of inserts into the delta; returns the engine
|
|
486
|
-
* `{ submitted, changed }` report. `
|
|
487
|
-
* in sealed field order
|
|
488
|
-
*
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
tx: TxHandle,
|
|
498
|
-
relationId: number,
|
|
499
|
-
columns: readonly (readonly FactValue[])[]
|
|
500
|
-
): WireMutationReport
|
|
501
|
-
/** Records a collection of deletes; returns the engine `{ submitted, changed }` report. */
|
|
502
|
-
txDelete(tx: TxHandle, relationId: number, rows: readonly (readonly FactValue[])[]): WireMutationReport
|
|
503
|
-
/**
|
|
504
|
-
* Final-state membership (base + pending delta — the exact view the
|
|
505
|
-
* commit judgment judges; check-then-act is race-free by construction).
|
|
297
|
+
* `{ submitted, changed }` report. `cells` is ONE flat row-major array
|
|
298
|
+
* (length rows×arity) in sealed field order, and `rows` is the EXPLICIT
|
|
299
|
+
* row count the caller states — the one collection crossing
|
|
300
|
+
* (proposals/one-representation/20): the JS side alone knows N when the
|
|
301
|
+
* roster is fieldless (N nullary facts project to 0 cells, so no
|
|
302
|
+
* derivation can recover N), and the bridge verifies
|
|
303
|
+
* `cells.length === rows × arity` exactly against its resident sealed
|
|
304
|
+
* roster before building the engine's shape-proved collection in a
|
|
305
|
+
* single pass. Empty (`rows === 0n`, no cells) is lawful and still a
|
|
306
|
+
* mutation. Nothing is judged until commit; shape violations throw
|
|
307
|
+
* typed, naming relation and field.
|
|
506
308
|
*/
|
|
309
|
+
txInsert(tx: TxHandle, relationId: number, rows: bigint, cells: readonly FactValue[]): WireMutationReport
|
|
310
|
+
|
|
311
|
+
txDelete(tx: TxHandle, relationId: number, rows: bigint, cells: readonly FactValue[]): WireMutationReport
|
|
312
|
+
|
|
507
313
|
txContains(tx: TxHandle, relationId: number, values: readonly FactValue[]): boolean
|
|
508
|
-
|
|
314
|
+
|
|
509
315
|
txGet(tx: TxHandle, relationId: number, keyStatementId: number, keyValues: readonly FactValue[]): FactValue[] | null
|
|
510
|
-
|
|
511
|
-
* Mints `count` consecutive fresh values for `(relationId, fieldId)`.
|
|
512
|
-
* `count === 0n` is empty and does not yield a start.
|
|
513
|
-
*/
|
|
316
|
+
|
|
514
317
|
txReserve(tx: TxHandle, relationId: number, fieldId: number, count: bigint): WireFreshRange
|
|
515
318
|
|
|
516
|
-
/**
|
|
517
|
-
* Prepares a query (IR as data, ids only; plan pinned at prepare).
|
|
518
|
-
* Roster errors return as data.
|
|
519
|
-
*/
|
|
520
319
|
dbPrepare(db: DbHandle, query: ParsedQuery): PrepareResult
|
|
521
|
-
|
|
522
|
-
* Executes against a live instance with positional params. One-copy owned
|
|
523
|
-
* rows out, column order = the query's head order; answers are a set
|
|
524
|
-
* — the host sorts.
|
|
525
|
-
*/
|
|
320
|
+
|
|
526
321
|
preparedExecute(prepared: PreparedHandle, instance: InstanceHandle, params: readonly QueryParam[]): FactValue[][]
|
|
527
|
-
|
|
528
|
-
* Plan introspection as data (ruled 2026-07-23, R13): runs the prepared
|
|
529
|
-
* query against a store read with counting instrumentation and returns
|
|
530
|
-
* the structured stats. Store-read only.
|
|
531
|
-
*/
|
|
532
|
-
preparedExplain(prepared: PreparedHandle, instance: InstanceHandle, params: readonly QueryParam[]): Explain
|
|
533
|
-
/** The pull-based plan-drift signal against a store read. */
|
|
534
|
-
preparedStaleness(prepared: PreparedHandle, instance: InstanceHandle): Staleness
|
|
535
|
-
/** Releases the prepared query. */
|
|
322
|
+
|
|
536
323
|
preparedClose(prepared: PreparedHandle): void
|
|
537
324
|
|
|
538
325
|
instanceBuilderNew(spec: SchemaSpec): BuilderHandle
|
|
326
|
+
|
|
539
327
|
instanceBuilderLoad(
|
|
540
328
|
builder: BuilderHandle,
|
|
541
329
|
relationId: number,
|
|
542
|
-
rows:
|
|
543
|
-
|
|
544
|
-
instanceBuilderLoadColumns(
|
|
545
|
-
builder: BuilderHandle,
|
|
546
|
-
relationId: number,
|
|
547
|
-
columns: readonly (readonly FactValue[])[]
|
|
330
|
+
rows: bigint,
|
|
331
|
+
cells: readonly FactValue[]
|
|
548
332
|
): WireMutationReport
|
|
549
333
|
instanceBuilderDelete(
|
|
550
334
|
builder: BuilderHandle,
|
|
551
335
|
relationId: number,
|
|
552
|
-
rows:
|
|
336
|
+
rows: bigint,
|
|
337
|
+
cells: readonly FactValue[]
|
|
553
338
|
): WireMutationReport
|
|
554
|
-
instanceBuilderReserve(
|
|
555
|
-
builder: BuilderHandle,
|
|
556
|
-
relationId: number,
|
|
557
|
-
fieldId: number,
|
|
558
|
-
count: bigint
|
|
559
|
-
): WireFreshRange
|
|
339
|
+
instanceBuilderReserve(builder: BuilderHandle, relationId: number, fieldId: number, count: bigint): WireFreshRange
|
|
560
340
|
instanceBuilderContains(builder: BuilderHandle, relationId: number, values: readonly FactValue[]): boolean
|
|
561
341
|
instanceBuilderGet(
|
|
562
342
|
builder: BuilderHandle,
|
|
@@ -568,6 +348,8 @@ interface Native {
|
|
|
568
348
|
instanceBuilderAdmit(builder: BuilderHandle): Promise<AdmitResult>
|
|
569
349
|
ownedInstanceClose(instance: OwnedHandle): void
|
|
570
350
|
ownedScan(instance: OwnedHandle, relationId: number): FactValue[][]
|
|
351
|
+
|
|
352
|
+
ownedCount(instance: OwnedHandle, relationId: number): bigint
|
|
571
353
|
ownedContains(instance: OwnedHandle, relationId: number, values: readonly FactValue[]): boolean
|
|
572
354
|
ownedGet(
|
|
573
355
|
instance: OwnedHandle,
|
|
@@ -579,54 +361,17 @@ interface Native {
|
|
|
579
361
|
ownedExecute(prepared: PreparedHandle, instance: OwnedHandle, params: readonly QueryParam[]): FactValue[][]
|
|
580
362
|
}
|
|
581
363
|
|
|
582
|
-
/**
|
|
583
|
-
* The sole platform this release ships (PRD-03 ruling 1: prebuilt-only,
|
|
584
|
-
* darwin-arm64). The per-platform-package structure below makes adding
|
|
585
|
-
* `darwin-x64`/`linux-*`/`win32-*` pure addition — one more `os`/`cpu`-gated
|
|
586
|
-
* package plus a CI matrix — never a redesign. This constant names the
|
|
587
|
-
* shipped set for the unsupported-platform message; the build's
|
|
588
|
-
* `PUBLISH_PLATFORM` (`scripts/platform.ts` — src cannot import scripts,
|
|
589
|
-
* the packaging boundary) and the `ts/.gitignore` carve-out spell the same
|
|
590
|
-
* target, and the single-source pin in `test/build-platform.test.ts` holds
|
|
591
|
-
* all three in lockstep.
|
|
592
|
-
*/
|
|
593
364
|
const SHIPPED_PLATFORMS = "darwin-arm64"
|
|
594
365
|
|
|
595
|
-
/**
|
|
596
|
-
* CommonJS require anchored to this module, the only mechanism ESM has for
|
|
597
|
-
* loading a Node-API addon without an experimental flag (static `import` of
|
|
598
|
-
* `.node` files still sits behind `--experimental-addon-modules` on Node 24).
|
|
599
|
-
* It resolves the per-platform binary package by name (see
|
|
600
|
-
* {@link loadNativeBinding}); the addon never crosses as a relative path.
|
|
601
|
-
* createRequire is the only unflagged Node-API addon loader in ESM, and this
|
|
602
|
-
* file is the package's single sanctioned FFI boundary (the arch-split
|
|
603
|
-
* packaging ruling).
|
|
604
|
-
*/
|
|
605
366
|
const requireNative = createRequire(import.meta.url)
|
|
606
367
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
* resolves nothing. The two failure modes are distinct and both typed:
|
|
613
|
-
*
|
|
614
|
-
* - the platform package is ABSENT (the expected state on any
|
|
615
|
-
* non-darwin-arm64 host, and on a foreign `platform`/`arch` passed under
|
|
616
|
-
* test) — an actionable unsupported-platform error naming the running
|
|
617
|
-
* `platform-arch` and the shipped set;
|
|
618
|
-
* - the platform package is PRESENT but its `bumbledb.node` will not load
|
|
619
|
-
* (a genuine ABI/corruption fault) — the wrapped loader error.
|
|
620
|
-
*
|
|
621
|
-
* Parameterized on `platform`/`arch` so the resolution law is exercised for
|
|
622
|
-
* foreign hosts as a unit, without spawning a foreign process.
|
|
623
|
-
*/
|
|
624
|
-
function loadNativeBinding(platform: string, arch: string): Native {
|
|
368
|
+
interface NativeBinding extends Native {
|
|
369
|
+
dbClose(db: DbHandle): void
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function loadNativeBinding(platform: string, arch: string): NativeBinding {
|
|
625
373
|
const platformPackage = `@bjornpagen/bumbledb-${platform}-${arch}`
|
|
626
374
|
|
|
627
|
-
// Presence probe: the platform package's OWN manifest resolves iff the
|
|
628
|
-
// matching optional dependency was installed. Its absence is the
|
|
629
|
-
// expected, benign "unsupported platform" — never a corruption signal.
|
|
630
375
|
const present = errors.trySync(() => requireNative.resolve(`${platformPackage}/package.json`))
|
|
631
376
|
if (present.error) {
|
|
632
377
|
throw errors.wrap(
|
|
@@ -635,8 +380,6 @@ function loadNativeBinding(platform: string, arch: string): Native {
|
|
|
635
380
|
)
|
|
636
381
|
}
|
|
637
382
|
|
|
638
|
-
// The package is present; load its addon (its `main` is `bumbledb.node`).
|
|
639
|
-
// A failure HERE is corruption or an ABI mismatch, not an absent platform.
|
|
640
383
|
const loaded = errors.trySync(() => requireNative(platformPackage))
|
|
641
384
|
if (loaded.error) {
|
|
642
385
|
throw errors.wrap(loaded.error, `load the ${platformPackage} native binary (package present but unloadable)`)
|
|
@@ -644,18 +387,13 @@ function loadNativeBinding(platform: string, arch: string): Native {
|
|
|
644
387
|
return loaded.data
|
|
645
388
|
}
|
|
646
389
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const native: Native = loadNativeBinding(process.platform, process.arch)
|
|
390
|
+
const binding: NativeBinding = loadNativeBinding(process.platform, process.arch)
|
|
391
|
+
const native: Native = binding
|
|
392
|
+
|
|
393
|
+
function dbClose(db: DbHandle): void {
|
|
394
|
+
binding.dbClose(db)
|
|
395
|
+
}
|
|
654
396
|
|
|
655
|
-
/**
|
|
656
|
-
* Engine throw identity: a real `Error` carrying `kind` from the
|
|
657
|
-
* `ErrorFamily` table, or a leftover `{ kind, message }` object.
|
|
658
|
-
*/
|
|
659
397
|
function isEngineThrow(value: unknown): value is { kind: ErrorFamilyKind; message: string } {
|
|
660
398
|
if (typeof value !== "object" || value === null) {
|
|
661
399
|
return false
|
|
@@ -676,13 +414,6 @@ function errorFromThrow(caught: unknown): Error {
|
|
|
676
414
|
return errors.new(String(caught))
|
|
677
415
|
}
|
|
678
416
|
|
|
679
|
-
/**
|
|
680
|
-
* The bridge guard — THE one wrapper every native call crosses (db.ts
|
|
681
|
-
* imports it): runs one native call and wraps anything it
|
|
682
|
-
* throws, so marshal-shape refusals and handle-lifecycle refusals cross as
|
|
683
|
-
* genuine typed failures, never bare foreign errors. Engine throws keep
|
|
684
|
-
* their forced kind.
|
|
685
|
-
*/
|
|
686
417
|
function bridged<T>(context: string, run: () => T): T {
|
|
687
418
|
try {
|
|
688
419
|
return run()
|
|
@@ -692,10 +423,6 @@ function bridged<T>(context: string, run: () => T): T {
|
|
|
692
423
|
}
|
|
693
424
|
}
|
|
694
425
|
|
|
695
|
-
/**
|
|
696
|
-
* The async twin of {@link bridged}: every control-plane native is an
|
|
697
|
-
* `AsyncTask` Promise, and this is the one wrapper those awaits cross.
|
|
698
|
-
*/
|
|
699
426
|
async function bridgedAsync<T>(context: string, run: () => Promise<T>): Promise<T> {
|
|
700
427
|
try {
|
|
701
428
|
return await run()
|
|
@@ -719,7 +446,6 @@ export type {
|
|
|
719
446
|
DbHandle,
|
|
720
447
|
DbOpenResult,
|
|
721
448
|
ErrorFamilyKind,
|
|
722
|
-
Explain,
|
|
723
449
|
FactValue,
|
|
724
450
|
FindTermIr,
|
|
725
451
|
FoldOpIr,
|
|
@@ -735,7 +461,6 @@ export type {
|
|
|
735
461
|
ManifestStatement,
|
|
736
462
|
Native,
|
|
737
463
|
NativeWriteOutcome,
|
|
738
|
-
OccurrenceDrift,
|
|
739
464
|
OpenKind,
|
|
740
465
|
OwnedHandle,
|
|
741
466
|
ParsedQuery,
|
|
@@ -746,7 +471,6 @@ export type {
|
|
|
746
471
|
QueryParam,
|
|
747
472
|
RecIr,
|
|
748
473
|
RuleIr,
|
|
749
|
-
Staleness,
|
|
750
474
|
StatementKindTag,
|
|
751
475
|
TaggedValue,
|
|
752
476
|
TermIr,
|
|
@@ -758,4 +482,4 @@ export type {
|
|
|
758
482
|
WitnessHandle,
|
|
759
483
|
WriteTag
|
|
760
484
|
}
|
|
761
|
-
export { bridged, bridgedAsync, errorFromThrow, loadNativeBinding, native, SHIPPED_PLATFORMS }
|
|
485
|
+
export { bridged, bridgedAsync, dbClose, errorFromThrow, loadNativeBinding, native, SHIPPED_PLATFORMS }
|