@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/native.ts
CHANGED
|
@@ -22,28 +22,46 @@ import type { SchemaSpec, ValueSpec, ValueTypeSpec } from "#spec.ts"
|
|
|
22
22
|
/** The opaque database handle (owns the LMDB environment + exclusive lock). */
|
|
23
23
|
type DbHandle = { readonly __brand: "bumbledb.db" }
|
|
24
24
|
|
|
25
|
-
/** One live
|
|
26
|
-
type
|
|
25
|
+
/** One live borrowed instance valid only inside a read callback. */
|
|
26
|
+
type InstanceHandle = { readonly __brand: "bumbledb.instance" }
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
type ExhumeHandle = { readonly __brand: "bumbledb.exhume" }
|
|
28
|
+
/** One cloneable generation witness. May outlive the read that minted it. */
|
|
29
|
+
type WitnessHandle = { readonly __brand: "bumbledb.witness" }
|
|
30
|
+
|
|
31
|
+
/** One unproved heap builder. Spent by `instanceBuilderAdmit` / close. */
|
|
32
|
+
type BuilderHandle = { readonly __brand: "bumbledb.builder" }
|
|
33
|
+
|
|
34
|
+
/** One admitted heap instance. */
|
|
35
|
+
type OwnedHandle = { readonly __brand: "bumbledb.owned" }
|
|
37
36
|
|
|
38
37
|
/**
|
|
39
38
|
* One live write transaction — the submitted delta with the engine's
|
|
40
|
-
* final-state point-read view.
|
|
39
|
+
* final-state point-read view. Valid only inside a write callback.
|
|
41
40
|
*/
|
|
42
41
|
type TxHandle = { readonly __brand: "bumbledb.tx" }
|
|
43
42
|
|
|
44
43
|
/** One prepared query (plan pinned at prepare). */
|
|
45
44
|
type PreparedHandle = { readonly __brand: "bumbledb.prepared" }
|
|
46
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Engine mutation report as it crosses napi: both counts are engine
|
|
48
|
+
* values, never reconstructed from JS length.
|
|
49
|
+
*/
|
|
50
|
+
interface WireMutationReport {
|
|
51
|
+
readonly submitted: bigint
|
|
52
|
+
readonly changed: bigint
|
|
53
|
+
}
|
|
54
|
+
|
|
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
|
+
type WireFreshRange =
|
|
62
|
+
| { readonly empty: true }
|
|
63
|
+
| { readonly empty: false; readonly start: bigint; readonly endExclusive: bigint }
|
|
64
|
+
|
|
47
65
|
/** A half-open interval `[start, end)` as it crosses the boundary. */
|
|
48
66
|
interface IntervalValue {
|
|
49
67
|
readonly start: bigint
|
|
@@ -118,15 +136,14 @@ interface RuleIr {
|
|
|
118
136
|
readonly conditions: readonly ConditionTreeIr[]
|
|
119
137
|
}
|
|
120
138
|
|
|
121
|
-
/** One find term (mirrors `ir::FindTerm`). Count
|
|
139
|
+
/** One find term (mirrors `ir::FindTerm`). Count is nullary; pack and folds carry `over`. */
|
|
122
140
|
type FoldOpIr = { readonly kind: "sum" } | { readonly kind: "min" } | { readonly kind: "max" }
|
|
123
141
|
|
|
124
|
-
type ArgOpIr = FoldOpIr | { readonly kind: "pack" }
|
|
125
|
-
|
|
126
142
|
type FindTermIr =
|
|
127
143
|
| { readonly kind: "var"; readonly var: number }
|
|
128
|
-
| { readonly kind: "
|
|
129
|
-
| { readonly kind: "aggregate"; readonly op:
|
|
144
|
+
| { readonly kind: "count" }
|
|
145
|
+
| { readonly kind: "aggregate"; readonly op: FoldOpIr; readonly over: number }
|
|
146
|
+
| { readonly kind: "pack"; readonly over: number }
|
|
130
147
|
| { readonly kind: "measure"; readonly var: number }
|
|
131
148
|
| { readonly kind: "aggregateMeasure"; readonly op: FoldOpIr; readonly over: number }
|
|
132
149
|
|
|
@@ -278,14 +295,22 @@ type Violation =
|
|
|
278
295
|
}
|
|
279
296
|
|
|
280
297
|
/**
|
|
281
|
-
* `dbCreate
|
|
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
|
+
type CreateResult =
|
|
303
|
+
| { readonly tag: "accepted"; readonly db: DbHandle }
|
|
304
|
+
| { readonly tag: "rejected"; readonly violations: readonly Violation[] }
|
|
305
|
+
| { readonly tag: "schemaError"; readonly message: string }
|
|
306
|
+
| { readonly tag: "newtypeMismatch"; readonly message: string }
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* `dbOpen`'s domain outcome. `schemaError` spans both spec
|
|
282
310
|
* resolution (unresolvable names, banned spellings — every issue in one
|
|
283
311
|
* message) and schema validation at the declaration boundary;
|
|
284
|
-
* `newtypeMismatch` is the coherence wall's own kind
|
|
285
|
-
*
|
|
286
|
-
* of the schema-level class wall; unreachable through the typed builder,
|
|
287
|
-
* which computes every label from the laws, so only a raw spec can reach
|
|
288
|
-
* it); `fingerprintMismatch` is `dbOpen`'s stored-theory refusal.
|
|
312
|
+
* `newtypeMismatch` is the coherence wall's own kind; `fingerprintMismatch`
|
|
313
|
+
* is `dbOpen`'s stored-theory refusal.
|
|
289
314
|
*/
|
|
290
315
|
type DbOpenResult =
|
|
291
316
|
| { readonly ok: true; readonly db: DbHandle }
|
|
@@ -296,46 +321,23 @@ type DbOpenResult =
|
|
|
296
321
|
}
|
|
297
322
|
|
|
298
323
|
/**
|
|
299
|
-
* `
|
|
300
|
-
*
|
|
301
|
-
* self-describing stores and has not been adopted; the remedy is one
|
|
302
|
-
* fingerprint-matching `dbOpen` under the creating schema),
|
|
303
|
-
* `formatMismatch`, and `corruption` (the persisted descriptor fails its
|
|
304
|
-
* integrity gates). Genuine failures — a missing path, a held exclusive
|
|
305
|
-
* lock — throw.
|
|
306
|
-
*/
|
|
307
|
-
type ExhumeResult =
|
|
308
|
-
| { readonly ok: true; readonly exhume: ExhumeHandle }
|
|
309
|
-
| {
|
|
310
|
-
readonly ok: false
|
|
311
|
-
readonly kind: "descriptorMissing" | "formatMismatch" | "corruption"
|
|
312
|
-
readonly message: string
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* `dbWriteFrom`'s domain outcome: the live witnessed transaction, or the
|
|
317
|
-
* typed stale-premise verdict (a state-changing commit landed after the
|
|
318
|
-
* witness snapshot; retry policy is host-side).
|
|
324
|
+
* `dbWrite` / `dbWriteFrom` native outcome. The SDK attaches the callback
|
|
325
|
+
* return onto the accepted arm. Moved is data, never an error kind.
|
|
319
326
|
*/
|
|
320
|
-
type
|
|
321
|
-
| { readonly
|
|
322
|
-
| {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
readonly witnessed: bigint
|
|
326
|
-
readonly current: bigint
|
|
327
|
-
}
|
|
327
|
+
type NativeWriteOutcome =
|
|
328
|
+
| { readonly tag: "accepted"; readonly generation: bigint }
|
|
329
|
+
| { readonly tag: "rejected"; readonly violations: readonly Violation[] }
|
|
330
|
+
| { readonly tag: "abandoned" }
|
|
331
|
+
| { readonly tag: "moved"; readonly witnessed: bigint; readonly current: bigint }
|
|
328
332
|
|
|
329
333
|
/**
|
|
330
|
-
* `
|
|
331
|
-
* violation set (every violated statement cited once, per direction for a
|
|
332
|
-
* containment, in materialized statement order).
|
|
334
|
+
* Builder `admit` native outcome.
|
|
333
335
|
*/
|
|
334
|
-
type
|
|
335
|
-
| { readonly
|
|
336
|
-
| { readonly
|
|
336
|
+
type AdmitResult =
|
|
337
|
+
| { readonly tag: "accepted"; readonly value: OwnedHandle }
|
|
338
|
+
| { readonly tag: "rejected"; readonly violations: readonly Violation[] }
|
|
337
339
|
|
|
338
|
-
/** `dbPrepare`'s domain outcome (IR roster errors are data). */
|
|
340
|
+
/** `dbPrepare`/`instancePrepare`'s domain outcome (IR roster errors are data). */
|
|
339
341
|
type PrepareResult =
|
|
340
342
|
| { readonly ok: true; readonly prepared: PreparedHandle }
|
|
341
343
|
| { readonly ok: false; readonly kind: "irError"; readonly message: string }
|
|
@@ -357,13 +359,37 @@ interface Staleness {
|
|
|
357
359
|
readonly maxRatio: number
|
|
358
360
|
}
|
|
359
361
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
362
|
+
type ErrorFamilyKind =
|
|
363
|
+
| "formatMismatch"
|
|
364
|
+
| "schemaMismatch"
|
|
365
|
+
| "alreadyInitialized"
|
|
366
|
+
| "destinationExists"
|
|
367
|
+
| "publishedButUnsynced"
|
|
368
|
+
| "environmentLocked"
|
|
369
|
+
| "io"
|
|
370
|
+
| "lmdb"
|
|
371
|
+
| "readersFull"
|
|
372
|
+
| "schema"
|
|
373
|
+
| "validation"
|
|
374
|
+
| "factShape"
|
|
375
|
+
| "freshExhausted"
|
|
376
|
+
| "closedRelationWrite"
|
|
377
|
+
| "commitSync"
|
|
378
|
+
| "transactionPoisoned"
|
|
379
|
+
| "foreignPrepared"
|
|
380
|
+
| "foreignWitness"
|
|
381
|
+
| "param"
|
|
382
|
+
| "measureOfRay"
|
|
383
|
+
| "capacityRayMeasure"
|
|
384
|
+
| "derivedBudgetExceeded"
|
|
385
|
+
| "overflow"
|
|
386
|
+
| "resultBytesOverflow"
|
|
387
|
+
| "corruption"
|
|
388
|
+
|
|
389
|
+
type AdmissionTag = "accepted" | "rejected"
|
|
390
|
+
type WriteTag = "accepted" | "rejected" | "abandoned" | "moved"
|
|
391
|
+
type OpenKind = "schemaError" | "newtypeMismatch" | "fingerprintMismatch"
|
|
392
|
+
type PrepareKind = "irError"
|
|
367
393
|
|
|
368
394
|
/**
|
|
369
395
|
* The plan-as-data report (ruled 2026-07-23, R13): the engine's
|
|
@@ -394,16 +420,15 @@ interface Native {
|
|
|
394
420
|
engineVersion(): string
|
|
395
421
|
|
|
396
422
|
/**
|
|
397
|
-
* Creates a fresh
|
|
398
|
-
*
|
|
399
|
-
* (throws); schema failures return as data.
|
|
423
|
+
* Creates a fresh durable store at `path`. Refuses an already-initialized
|
|
424
|
+
* directory (throws); schema failures return as data.
|
|
400
425
|
*/
|
|
401
|
-
dbCreate(path: string, spec: SchemaSpec):
|
|
426
|
+
dbCreate(path: string, spec: SchemaSpec): Promise<CreateResult>
|
|
402
427
|
/**
|
|
403
|
-
* Opens an existing durable store, verifying format version
|
|
404
|
-
*
|
|
428
|
+
* Opens an existing durable store, verifying format version and
|
|
429
|
+
* schema fingerprint (`fingerprintMismatch` as data).
|
|
405
430
|
*/
|
|
406
|
-
dbOpen(path: string, spec: SchemaSpec): DbOpenResult
|
|
431
|
+
dbOpen(path: string, spec: SchemaSpec): Promise<DbOpenResult>
|
|
407
432
|
/**
|
|
408
433
|
* Closes the handle. Dependent handles each hold the engine alive; the
|
|
409
434
|
* environment (and its exclusive lock) releases when the last closes.
|
|
@@ -422,87 +447,59 @@ interface Native {
|
|
|
422
447
|
dbFingerprint(db: DbHandle): string
|
|
423
448
|
/**
|
|
424
449
|
* The current committed generation — diagnostics only. The write-side
|
|
425
|
-
* witness is always
|
|
426
|
-
* integer: an integer witness would be a claim a caller could fabricate
|
|
427
|
-
* or stale-cache (the engine's recorded refusal).
|
|
450
|
+
* witness is always a {@link WitnessHandle}, never this integer.
|
|
428
451
|
*/
|
|
429
452
|
dbGeneration(db: DbHandle): bigint
|
|
453
|
+
/** Publishes an admitted heap instance at `path` without re-judgment. */
|
|
454
|
+
dbFromInstance(path: string, instance: OwnedHandle): Promise<DbHandle>
|
|
430
455
|
|
|
431
456
|
/**
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
* genuine failures throw. The handle's deterministic teardown is
|
|
436
|
-
* `exhumeClose` (R12); GC reclamation remains the backstop only.
|
|
437
|
-
*/
|
|
438
|
-
dbExhume(path: string): ExhumeResult
|
|
439
|
-
/**
|
|
440
|
-
* Closes the exhume handle, releasing its environment (and the store's
|
|
441
|
-
* exclusive lock) deterministically — the native teardown under the
|
|
442
|
-
* SDK's `Symbol.dispose` (ruled 2026-07-23, R12: lifetimes are
|
|
443
|
-
* disposables, never `close()` methods to remember).
|
|
444
|
-
*/
|
|
445
|
-
exhumeClose(exhume: ExhumeHandle): void
|
|
446
|
-
/**
|
|
447
|
-
* The exhumed store's persisted schema as manifest-shaped data — the
|
|
448
|
-
* engine's own manifest rendering of the STORED descriptor: relations
|
|
449
|
-
* in engine-id order, sealed field lists (a closed relation opens with
|
|
450
|
-
* the synthetic (`id`, u64) handle field) with structural value types,
|
|
451
|
-
* and closed-relation rosters.
|
|
457
|
+
* Runs `callback` synchronously inside the engine read lease. The
|
|
458
|
+
* instance handle is invalid after the callback returns; the witness
|
|
459
|
+
* handle is a clone and may escape.
|
|
452
460
|
*/
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
*/
|
|
460
|
-
exhumeScan(exhume: ExhumeHandle, relationName: string): FactValue[][]
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Opens one MVCC read snapshot as a live handle, returned WITH its
|
|
464
|
-
* witnessed generation — one crossing carries both (finding 016), so
|
|
465
|
-
* no separate `dbGeneration` call (with its own transient read
|
|
466
|
-
* transaction and fault-pairing close branch) exists on this path.
|
|
467
|
-
*/
|
|
468
|
-
dbSnapshot(db: DbHandle): SnapshotOpened
|
|
469
|
-
/** Closes the snapshot, releasing its LMDB reader slot. */
|
|
470
|
-
snapshotClose(snap: SnapshotHandle): void
|
|
471
|
-
/** Full-relation export in row-id order (one row per fact). */
|
|
472
|
-
snapshotScan(snap: SnapshotHandle, relationId: number): FactValue[][]
|
|
473
|
-
/** Committed-state membership of one fact (sealed field order). */
|
|
474
|
-
snapshotContains(snap: SnapshotHandle, relationId: number, values: readonly FactValue[]): boolean
|
|
475
|
-
/**
|
|
476
|
-
* Committed-state point lookup through a key statement (`keyValues` in
|
|
477
|
-
* the statement's projection order); `null` on a miss.
|
|
478
|
-
*/
|
|
479
|
-
snapshotGet(
|
|
480
|
-
snap: SnapshotHandle,
|
|
461
|
+
dbRead<R>(db: DbHandle, callback: (instance: InstanceHandle, witness: WitnessHandle) => R): R
|
|
462
|
+
instanceGeneration(instance: InstanceHandle): bigint
|
|
463
|
+
instanceScan(instance: InstanceHandle, relationId: number): FactValue[][]
|
|
464
|
+
instanceContains(instance: InstanceHandle, relationId: number, values: readonly FactValue[]): boolean
|
|
465
|
+
instanceGet(
|
|
466
|
+
instance: InstanceHandle,
|
|
481
467
|
relationId: number,
|
|
482
468
|
keyStatementId: number,
|
|
483
469
|
keyValues: readonly FactValue[]
|
|
484
470
|
): FactValue[] | null
|
|
471
|
+
instancePrepare(instance: InstanceHandle, query: ParsedQuery): PrepareResult
|
|
472
|
+
witnessClose(witness: WitnessHandle): void
|
|
485
473
|
|
|
486
474
|
/**
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
* engine; a second begin throws rather than deadlocking the process).
|
|
475
|
+
* Runs `callback` synchronously inside the engine write region.
|
|
476
|
+
* Return `true` to commit, `false` to abandon. Nested writes throw.
|
|
490
477
|
*/
|
|
491
|
-
|
|
478
|
+
dbWrite(db: DbHandle, callback: (tx: TxHandle) => boolean): NativeWriteOutcome
|
|
492
479
|
/**
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
* `generationMoved` as data otherwise (the optimistic
|
|
496
|
-
* read-compute-write loop's entry; retry policy stays host-side).
|
|
480
|
+
* Witnessed write: `moved` is data when the store advanced since the
|
|
481
|
+
* witness was minted. The callback does not run on that arm.
|
|
497
482
|
*/
|
|
498
|
-
dbWriteFrom(db: DbHandle,
|
|
483
|
+
dbWriteFrom(db: DbHandle, witness: WitnessHandle, callback: (tx: TxHandle) => boolean): NativeWriteOutcome
|
|
499
484
|
/**
|
|
500
|
-
* Records
|
|
501
|
-
*
|
|
485
|
+
* Records a collection of inserts into the delta; returns the engine
|
|
486
|
+
* `{ submitted, changed }` report. `rows` is an array of value-arrays
|
|
487
|
+
* in sealed field order. Empty is lawful and still a mutation (poison
|
|
488
|
+
* is observed). Nothing is judged until commit; shape violations throw typed.
|
|
502
489
|
*/
|
|
503
|
-
txInsert(tx: TxHandle, relationId: number,
|
|
504
|
-
/**
|
|
505
|
-
|
|
490
|
+
txInsert(tx: TxHandle, relationId: number, rows: readonly (readonly FactValue[])[]): WireMutationReport
|
|
491
|
+
/**
|
|
492
|
+
* Records a collection of inserts from per-column arrays in sealed
|
|
493
|
+
* field order — the column transport, same parse-all-first batch as
|
|
494
|
+
* {@link Native.txInsert}.
|
|
495
|
+
*/
|
|
496
|
+
txInsertColumns(
|
|
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
|
|
506
503
|
/**
|
|
507
504
|
* Final-state membership (base + pending delta — the exact view the
|
|
508
505
|
* commit judgment judges; check-then-act is race-free by construction).
|
|
@@ -511,20 +508,10 @@ interface Native {
|
|
|
511
508
|
/** Final-state point lookup through a key statement; `null` on a miss. */
|
|
512
509
|
txGet(tx: TxHandle, relationId: number, keyStatementId: number, keyValues: readonly FactValue[]): FactValue[] | null
|
|
513
510
|
/**
|
|
514
|
-
* Mints
|
|
515
|
-
*
|
|
516
|
-
* insert-with-omitted-fields spelling; include the minted id in the
|
|
517
|
-
* full row).
|
|
518
|
-
*/
|
|
519
|
-
txAlloc(tx: TxHandle, relationId: number, fieldId: number): bigint
|
|
520
|
-
/**
|
|
521
|
-
* Commits the delta: every dependency statement judged against the
|
|
522
|
-
* final state; a rejection carries the complete violation rendering.
|
|
523
|
-
* The handle is spent either way.
|
|
511
|
+
* Mints `count` consecutive fresh values for `(relationId, fieldId)`.
|
|
512
|
+
* `count === 0n` is empty and does not yield a start.
|
|
524
513
|
*/
|
|
525
|
-
|
|
526
|
-
/** Aborts the delta (LMDB was never touched). The handle is spent. */
|
|
527
|
-
txAbort(tx: TxHandle): void
|
|
514
|
+
txReserve(tx: TxHandle, relationId: number, fieldId: number, count: bigint): WireFreshRange
|
|
528
515
|
|
|
529
516
|
/**
|
|
530
517
|
* Prepares a query (IR as data, ids only; plan pinned at prepare).
|
|
@@ -532,24 +519,64 @@ interface Native {
|
|
|
532
519
|
*/
|
|
533
520
|
dbPrepare(db: DbHandle, query: ParsedQuery): PrepareResult
|
|
534
521
|
/**
|
|
535
|
-
* Executes against a
|
|
522
|
+
* Executes against a live instance with positional params. One-copy owned
|
|
536
523
|
* rows out, column order = the query's head order; answers are a set
|
|
537
524
|
* — the host sorts.
|
|
538
525
|
*/
|
|
539
|
-
preparedExecute(prepared: PreparedHandle,
|
|
526
|
+
preparedExecute(prepared: PreparedHandle, instance: InstanceHandle, params: readonly QueryParam[]): FactValue[][]
|
|
540
527
|
/**
|
|
541
528
|
* Plan introspection as data (ruled 2026-07-23, R13): runs the prepared
|
|
542
|
-
* query against
|
|
543
|
-
*
|
|
544
|
-
* structured stats — plan sections and counters as plain values.
|
|
545
|
-
* Scalar params only (the engine's profile entry has no param-set
|
|
546
|
-
* spelling).
|
|
529
|
+
* query against a store read with counting instrumentation and returns
|
|
530
|
+
* the structured stats. Store-read only.
|
|
547
531
|
*/
|
|
548
|
-
preparedExplain(prepared: PreparedHandle,
|
|
549
|
-
/** The pull-based plan-drift signal against a
|
|
550
|
-
preparedStaleness(prepared: PreparedHandle,
|
|
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
|
|
551
535
|
/** Releases the prepared query. */
|
|
552
536
|
preparedClose(prepared: PreparedHandle): void
|
|
537
|
+
|
|
538
|
+
instanceBuilderNew(spec: SchemaSpec): BuilderHandle
|
|
539
|
+
instanceBuilderLoad(
|
|
540
|
+
builder: BuilderHandle,
|
|
541
|
+
relationId: number,
|
|
542
|
+
rows: readonly (readonly FactValue[])[]
|
|
543
|
+
): WireMutationReport
|
|
544
|
+
instanceBuilderLoadColumns(
|
|
545
|
+
builder: BuilderHandle,
|
|
546
|
+
relationId: number,
|
|
547
|
+
columns: readonly (readonly FactValue[])[]
|
|
548
|
+
): WireMutationReport
|
|
549
|
+
instanceBuilderDelete(
|
|
550
|
+
builder: BuilderHandle,
|
|
551
|
+
relationId: number,
|
|
552
|
+
rows: readonly (readonly FactValue[])[]
|
|
553
|
+
): WireMutationReport
|
|
554
|
+
instanceBuilderReserve(
|
|
555
|
+
builder: BuilderHandle,
|
|
556
|
+
relationId: number,
|
|
557
|
+
fieldId: number,
|
|
558
|
+
count: bigint
|
|
559
|
+
): WireFreshRange
|
|
560
|
+
instanceBuilderContains(builder: BuilderHandle, relationId: number, values: readonly FactValue[]): boolean
|
|
561
|
+
instanceBuilderGet(
|
|
562
|
+
builder: BuilderHandle,
|
|
563
|
+
relationId: number,
|
|
564
|
+
keyStatementId: number,
|
|
565
|
+
keyValues: readonly FactValue[]
|
|
566
|
+
): FactValue[] | null
|
|
567
|
+
instanceBuilderClose(builder: BuilderHandle): void
|
|
568
|
+
instanceBuilderAdmit(builder: BuilderHandle): Promise<AdmitResult>
|
|
569
|
+
ownedInstanceClose(instance: OwnedHandle): void
|
|
570
|
+
ownedScan(instance: OwnedHandle, relationId: number): FactValue[][]
|
|
571
|
+
ownedContains(instance: OwnedHandle, relationId: number, values: readonly FactValue[]): boolean
|
|
572
|
+
ownedGet(
|
|
573
|
+
instance: OwnedHandle,
|
|
574
|
+
relationId: number,
|
|
575
|
+
keyStatementId: number,
|
|
576
|
+
keyValues: readonly FactValue[]
|
|
577
|
+
): FactValue[] | null
|
|
578
|
+
ownedPrepare(instance: OwnedHandle, query: ParsedQuery): PrepareResult
|
|
579
|
+
ownedExecute(prepared: PreparedHandle, instance: OwnedHandle, params: readonly QueryParam[]): FactValue[][]
|
|
553
580
|
}
|
|
554
581
|
|
|
555
582
|
/**
|
|
@@ -626,38 +653,79 @@ function loadNativeBinding(platform: string, arch: string): Native {
|
|
|
626
653
|
const native: Native = loadNativeBinding(process.platform, process.arch)
|
|
627
654
|
|
|
628
655
|
/**
|
|
629
|
-
*
|
|
630
|
-
*
|
|
656
|
+
* Engine throw identity: a real `Error` carrying `kind` from the
|
|
657
|
+
* `ErrorFamily` table, or a leftover `{ kind, message }` object.
|
|
658
|
+
*/
|
|
659
|
+
function isEngineThrow(value: unknown): value is { kind: ErrorFamilyKind; message: string } {
|
|
660
|
+
if (typeof value !== "object" || value === null) {
|
|
661
|
+
return false
|
|
662
|
+
}
|
|
663
|
+
const rec = value as { kind?: unknown; message?: unknown }
|
|
664
|
+
return typeof rec.kind === "string" && typeof rec.message === "string"
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function errorFromThrow(caught: unknown): Error {
|
|
668
|
+
if (caught instanceof Error) {
|
|
669
|
+
return caught
|
|
670
|
+
}
|
|
671
|
+
if (isEngineThrow(caught)) {
|
|
672
|
+
const error = errors.new(`bumbledb ${caught.kind}: ${caught.message}`)
|
|
673
|
+
Object.defineProperty(error, "kind", { value: caught.kind, enumerable: true })
|
|
674
|
+
return error
|
|
675
|
+
}
|
|
676
|
+
return errors.new(String(caught))
|
|
677
|
+
}
|
|
678
|
+
|
|
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
|
|
631
682
|
* throws, so marshal-shape refusals and handle-lifecycle refusals cross as
|
|
632
|
-
* genuine typed failures, never bare foreign errors.
|
|
683
|
+
* genuine typed failures, never bare foreign errors. Engine throws keep
|
|
684
|
+
* their forced kind.
|
|
633
685
|
*/
|
|
634
686
|
function bridged<T>(context: string, run: () => T): T {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
687
|
+
try {
|
|
688
|
+
return run()
|
|
689
|
+
} catch (caught) {
|
|
690
|
+
const inner = errorFromThrow(caught)
|
|
691
|
+
throw errors.wrap(inner, `${context}: ${inner.message}`)
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
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
|
+
async function bridgedAsync<T>(context: string, run: () => Promise<T>): Promise<T> {
|
|
700
|
+
try {
|
|
701
|
+
return await run()
|
|
702
|
+
} catch (caught) {
|
|
703
|
+
const inner = errorFromThrow(caught)
|
|
704
|
+
throw errors.wrap(inner, `${context}: ${inner.message}`)
|
|
638
705
|
}
|
|
639
|
-
return result.data
|
|
640
706
|
}
|
|
641
707
|
|
|
642
708
|
export type {
|
|
709
|
+
AdmissionTag,
|
|
710
|
+
AdmitResult,
|
|
643
711
|
AggOpIr,
|
|
644
|
-
ArgOpIr,
|
|
645
712
|
AtomIr,
|
|
646
713
|
AtomSourceIr,
|
|
714
|
+
BuilderHandle,
|
|
647
715
|
CmpOpIr,
|
|
648
|
-
CommitResult,
|
|
649
716
|
ComparisonIr,
|
|
650
717
|
ConditionTreeIr,
|
|
718
|
+
CreateResult,
|
|
651
719
|
DbHandle,
|
|
652
720
|
DbOpenResult,
|
|
653
|
-
|
|
654
|
-
ExhumeResult,
|
|
721
|
+
ErrorFamilyKind,
|
|
655
722
|
Explain,
|
|
656
723
|
FactValue,
|
|
657
724
|
FindTermIr,
|
|
658
725
|
FoldOpIr,
|
|
659
726
|
HeadOpIr,
|
|
660
727
|
HeadTermIr,
|
|
728
|
+
InstanceHandle,
|
|
661
729
|
InteriorIr,
|
|
662
730
|
IntervalValue,
|
|
663
731
|
Manifest,
|
|
@@ -666,16 +734,18 @@ export type {
|
|
|
666
734
|
ManifestRow,
|
|
667
735
|
ManifestStatement,
|
|
668
736
|
Native,
|
|
737
|
+
NativeWriteOutcome,
|
|
669
738
|
OccurrenceDrift,
|
|
739
|
+
OpenKind,
|
|
740
|
+
OwnedHandle,
|
|
670
741
|
ParsedQuery,
|
|
671
742
|
PreparedHandle,
|
|
743
|
+
PrepareKind,
|
|
672
744
|
PrepareResult,
|
|
673
745
|
QueryIr,
|
|
674
746
|
QueryParam,
|
|
675
747
|
RecIr,
|
|
676
748
|
RuleIr,
|
|
677
|
-
SnapshotHandle,
|
|
678
|
-
SnapshotOpened,
|
|
679
749
|
Staleness,
|
|
680
750
|
StatementKindTag,
|
|
681
751
|
TaggedValue,
|
|
@@ -683,6 +753,9 @@ export type {
|
|
|
683
753
|
TxHandle,
|
|
684
754
|
Violation,
|
|
685
755
|
ViolationFact,
|
|
686
|
-
|
|
756
|
+
WireFreshRange,
|
|
757
|
+
WireMutationReport,
|
|
758
|
+
WitnessHandle,
|
|
759
|
+
WriteTag
|
|
687
760
|
}
|
|
688
|
-
export { bridged, loadNativeBinding, native, SHIPPED_PLATFORMS }
|
|
761
|
+
export { bridged, bridgedAsync, errorFromThrow, loadNativeBinding, native, SHIPPED_PLATFORMS }
|
package/src/query/find.ts
CHANGED
|
@@ -24,19 +24,25 @@ import type { IntervalVarOk, NumericVarOk, OrderVarOk } from "#query/atom.ts"
|
|
|
24
24
|
import type { AnyVar, Duration, MintSlotOf } from "#query/scope.ts"
|
|
25
25
|
|
|
26
26
|
/** One aggregate operator name of the find vocabulary. */
|
|
27
|
-
type
|
|
27
|
+
type FoldOpName = "sum" | "min" | "max" | "pack"
|
|
28
|
+
type AggOpName = "count" | FoldOpName
|
|
29
|
+
|
|
30
|
+
/** Nullary count: no `over` exists to inhabit. */
|
|
31
|
+
interface CountAgg {
|
|
32
|
+
readonly agg: "count"
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
/**
|
|
30
|
-
* One aggregate
|
|
31
|
-
* BY REFERENCE.
|
|
36
|
+
* One fold aggregate: the op and the variable (or measure) it folds
|
|
37
|
+
* BY REFERENCE. Count is [`CountAgg`], not this type with `undefined`.
|
|
32
38
|
*/
|
|
33
|
-
interface Agg<Op extends
|
|
39
|
+
interface Agg<Op extends FoldOpName, Over extends AnyVar | Duration> {
|
|
34
40
|
readonly agg: Op
|
|
35
41
|
readonly over: Over
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
/** Any aggregate find value. */
|
|
39
|
-
type AnyAgg = Agg<
|
|
45
|
+
type AnyAgg = CountAgg | Agg<FoldOpName, AnyVar | Duration>
|
|
40
46
|
|
|
41
47
|
/** One find entry: a projected variable, the measure, or an aggregate. */
|
|
42
48
|
type FindEntry = AnyVar | Duration | AnyAgg
|
|
@@ -44,17 +50,14 @@ type FindEntry = AnyVar | Duration | AnyAgg
|
|
|
44
50
|
/** The `find` record: column name → find entry. Keys ARE the answer columns. */
|
|
45
51
|
type FindShape = Readonly<Record<string, FindEntry>>
|
|
46
52
|
|
|
47
|
-
/** Builds one aggregate value. */
|
|
48
|
-
function aggregate<Op extends
|
|
49
|
-
op: Op,
|
|
50
|
-
over: Over
|
|
51
|
-
): Agg<Op, Over> {
|
|
53
|
+
/** Builds one fold aggregate value. */
|
|
54
|
+
function aggregate<Op extends FoldOpName, Over extends AnyVar | Duration>(op: Op, over: Over): Agg<Op, Over> {
|
|
52
55
|
return Object.freeze({ agg: op, over })
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
/** Nullary count: |the group's set of distinct full bindings|, `bigint`. */
|
|
56
|
-
function count():
|
|
57
|
-
return
|
|
59
|
+
function count(): CountAgg {
|
|
60
|
+
return Object.freeze({ agg: "count" })
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
/**
|
|
@@ -119,7 +122,7 @@ type FindEntryOk<E> = E extends AnyVar
|
|
|
119
122
|
? true
|
|
120
123
|
: E extends Duration<infer V extends AnyVar>
|
|
121
124
|
? IntervalVarOk<V>
|
|
122
|
-
: E extends
|
|
125
|
+
: E extends CountAgg
|
|
123
126
|
? true
|
|
124
127
|
: E extends Agg<"sum", infer O>
|
|
125
128
|
? SumOverOk<O>
|
|
@@ -151,7 +154,7 @@ type FindValue<E> = E extends AnyVar
|
|
|
151
154
|
? Infer<E["field"]>
|
|
152
155
|
: E extends Duration<AnyVar>
|
|
153
156
|
? bigint
|
|
154
|
-
: E extends
|
|
157
|
+
: E extends CountAgg
|
|
155
158
|
? bigint
|
|
156
159
|
: E extends Agg<"sum" | "min" | "max", infer O>
|
|
157
160
|
? O extends AnyVar
|
|
@@ -179,10 +182,12 @@ export type {
|
|
|
179
182
|
AnyAgg,
|
|
180
183
|
CheckFind,
|
|
181
184
|
CheckRecFind,
|
|
185
|
+
CountAgg,
|
|
182
186
|
FindEntry,
|
|
183
187
|
FindEntryOk,
|
|
184
188
|
FindShape,
|
|
185
189
|
FindValue,
|
|
190
|
+
FoldOpName,
|
|
186
191
|
HeadRecordOf,
|
|
187
192
|
RowOfFind
|
|
188
193
|
}
|