@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.
Files changed (103) hide show
  1. package/COOKBOOK.md +33 -49
  2. package/README.md +3 -3
  3. package/dist/capacity.d.ts +24 -136
  4. package/dist/capacity.d.ts.map +1 -1
  5. package/dist/capacity.js +18 -40
  6. package/dist/capacity.js.map +1 -1
  7. package/dist/closed.d.ts +0 -156
  8. package/dist/closed.d.ts.map +1 -1
  9. package/dist/closed.js +0 -104
  10. package/dist/closed.js.map +1 -1
  11. package/dist/db.d.ts +7 -223
  12. package/dist/db.d.ts.map +1 -1
  13. package/dist/db.js +147 -396
  14. package/dist/db.js.map +1 -1
  15. package/dist/face.d.ts +0 -133
  16. package/dist/face.d.ts.map +1 -1
  17. package/dist/face.js +0 -33
  18. package/dist/face.js.map +1 -1
  19. package/dist/fields.d.ts +1 -145
  20. package/dist/fields.d.ts.map +1 -1
  21. package/dist/fields.js +2 -91
  22. package/dist/fields.js.map +1 -1
  23. package/dist/index.d.ts +11 -15
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +9 -13
  26. package/dist/index.js.map +1 -1
  27. package/dist/law.d.ts +111 -93
  28. package/dist/law.d.ts.map +1 -1
  29. package/dist/law.js +23 -27
  30. package/dist/law.js.map +1 -1
  31. package/dist/lower.d.ts +9 -35
  32. package/dist/lower.d.ts.map +1 -1
  33. package/dist/lower.js +8 -53
  34. package/dist/lower.js.map +1 -1
  35. package/dist/marshal.d.ts +0 -65
  36. package/dist/marshal.d.ts.map +1 -1
  37. package/dist/marshal.js +0 -72
  38. package/dist/marshal.js.map +1 -1
  39. package/dist/native.d.ts +25 -290
  40. package/dist/native.d.ts.map +1 -1
  41. package/dist/native.js +6 -66
  42. package/dist/native.js.map +1 -1
  43. package/dist/query/atom.d.ts +10 -276
  44. package/dist/query/atom.d.ts.map +1 -1
  45. package/dist/query/atom.js +1 -96
  46. package/dist/query/atom.js.map +1 -1
  47. package/dist/query/find.d.ts +10 -76
  48. package/dist/query/find.d.ts.map +1 -1
  49. package/dist/query/find.js +0 -30
  50. package/dist/query/find.js.map +1 -1
  51. package/dist/query/lower.d.ts +64 -146
  52. package/dist/query/lower.d.ts.map +1 -1
  53. package/dist/query/lower.js +19 -256
  54. package/dist/query/lower.js.map +1 -1
  55. package/dist/query/parse-ir.d.ts +0 -7
  56. package/dist/query/parse-ir.d.ts.map +1 -1
  57. package/dist/query/parse-ir.js +1 -13
  58. package/dist/query/parse-ir.js.map +1 -1
  59. package/dist/query/run.d.ts +0 -36
  60. package/dist/query/run.d.ts.map +1 -1
  61. package/dist/query/run.js +0 -44
  62. package/dist/query/run.js.map +1 -1
  63. package/dist/query/scope.d.ts +24 -180
  64. package/dist/query/scope.d.ts.map +1 -1
  65. package/dist/query/scope.js +2 -66
  66. package/dist/query/scope.js.map +1 -1
  67. package/dist/relation.d.ts +2 -50
  68. package/dist/relation.d.ts.map +1 -1
  69. package/dist/relation.js +2 -37
  70. package/dist/relation.js.map +1 -1
  71. package/dist/schema.d.ts +13 -63
  72. package/dist/schema.d.ts.map +1 -1
  73. package/dist/schema.js +118 -92
  74. package/dist/schema.js.map +1 -1
  75. package/dist/spec.d.ts +1 -140
  76. package/dist/spec.d.ts.map +1 -1
  77. package/dist/spec.js +1 -68
  78. package/dist/spec.js.map +1 -1
  79. package/dist/statements.d.ts +6 -137
  80. package/dist/statements.d.ts.map +1 -1
  81. package/dist/statements.js +16 -119
  82. package/dist/statements.js.map +1 -1
  83. package/package.json +2 -2
  84. package/src/capacity.ts +26 -140
  85. package/src/closed.ts +5 -206
  86. package/src/db.ts +203 -692
  87. package/src/face.ts +0 -142
  88. package/src/fields.ts +4 -172
  89. package/src/index.ts +9 -15
  90. package/src/law.ts +201 -129
  91. package/src/lower.ts +8 -53
  92. package/src/marshal.ts +1 -85
  93. package/src/native.ts +47 -323
  94. package/src/query/atom.ts +26 -313
  95. package/src/query/find.ts +24 -110
  96. package/src/query/lower.ts +132 -377
  97. package/src/query/parse-ir.ts +1 -14
  98. package/src/query/run.ts +0 -45
  99. package/src/query/scope.ts +25 -186
  100. package/src/relation.ts +2 -66
  101. package/src/schema.ts +143 -122
  102. package/src/spec.ts +1 -160
  103. package/src/statements.ts +22 -174
package/src/db.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * A store read is one callback: `db.read((instance, witness) => …)`. The
9
9
  * instance is invalid the moment the callback returns; the witness is a
10
10
  * cloneable token and may escape. There is no handle-shaped read and no
11
- * `using snap = db.read()`. Builder, owned instance, and witness
11
+ * `using snap = db.read`. Builder, owned instance, and witness
12
12
  * implement `Symbol.dispose`. Prepared plans are plain values whose
13
13
  * engine-side half is reclaimed by a GC finalizer — reclamation only,
14
14
  * never correctness.
@@ -48,9 +48,9 @@ import type {
48
48
  PreparedHandle,
49
49
  TxHandle,
50
50
  WireFreshRange,
51
+ WireMutationReport,
51
52
  Violation as WireViolation,
52
53
  ViolationFact as WireViolationFact,
53
- WireMutationReport,
54
54
  WitnessHandle
55
55
  } from "#native.ts"
56
56
  import { bridged, bridgedAsync, errorFromThrow, native } from "#native.ts"
@@ -63,86 +63,60 @@ import type { AnyRelation, Fact, FreshKeys } from "#relation.ts"
63
63
  import type { AnySchema, Schema, SchemaRelation, SchemaRelations } from "#schema.ts"
64
64
  import { isStatement, type KeyStatement, type Statement } from "#statements.ts"
65
65
 
66
- /**
67
- * The ordinary (writable, scannable) relations of a schema's record — the
68
- * only values the runtime methods accept: closed relations lack the
69
- * relation shape entirely, so passing one is a type error.
70
- */
71
66
  type MemberRelation<Rels extends SchemaRelations> = Extract<Rels[keyof Rels], AnyRelation>
72
67
 
73
- /**
74
- * Facts consumed vs facts that changed the in-memory final-state view.
75
- * The length-1 report is `{ submitted: 1n, changed: 0n | 1n }`.
76
- */
77
68
  interface MutationReport {
78
69
  readonly submitted: bigint
79
70
  readonly changed: bigint
80
71
  }
81
72
 
82
- /**
83
- * Column-major collection write: one array per declared field, every
84
- * column the same length. The second transport of `load` / `insert` —
85
- * objects and columns are two ways to spell the same batch.
86
- */
87
- type ColumnBatch<R extends AnyRelation> = {
88
- readonly [K in keyof Fact<R> & string]: readonly Fact<R>[K][]
89
- }
90
-
91
- type CollectionWrite<R extends AnyRelation> = Iterable<Fact<R>> | ColumnBatch<R>
92
-
93
- function isColumnBatch(value: object): boolean {
94
- return !(Symbol.iterator in value)
95
- }
73
+ type CollectionWrite<R extends AnyRelation> = Iterable<Fact<R>>
96
74
 
97
- function rowsOf<R extends AnyRelation>(relation: R, facts: Iterable<Fact<R>>): FactValue[][] {
98
- const rows: FactValue[][] = []
99
- for (const fact of facts) {
100
- rows.push(rowOf(relation.data, recordOf(fact)))
101
- }
102
- return rows
75
+ interface FlatCollection {
76
+ readonly rows: bigint
77
+ readonly cells: readonly FactValue[]
103
78
  }
104
79
 
105
80
  /**
106
- * Lowers a column batch to per-field wire arrays in sealed field order.
107
- * Allocates one array per field — never a JS array per row.
81
+ * The flat projector: every fact's cells land in ONE row-major
82
+ * `FactValue` array (length rows×arity) — no JS array per fact exists
83
+ * anywhere between the caller's objects and the native crossing
84
+ * (proposals/one-representation/20, V1) — and the row count is counted
85
+ * while projecting (the {@link FlatCollection} law: the stated count is
86
+ * what the bridge verifies against `rows × arity`, exactly, for every
87
+ * arity). The per-cell judgment is `cellOf` — the one cell judge `rowOf`
88
+ * also speaks (closed handle→id, well-formedness, interval shape) — and
89
+ * the missing-field refusal is `rowOf`'s, byte for byte; only the output
90
+ * form differs (flat, never per-row).
108
91
  */
109
- function columnsOf(relation: AnyRelation, batch: object): FactValue[][] {
110
- const record = recordOf(batch)
111
- let count: number | undefined
112
- return relation.data.fields.map(function marshalColumn(declared) {
113
- const raw = record[declared.name]
114
- if (!Array.isArray(raw)) {
115
- throw errors.new(`relation ${relation.name}: column ${declared.name} is not an array`)
116
- }
117
- if (count === undefined) {
118
- count = raw.length
119
- } else if (raw.length !== count) {
120
- throw errors.new(
121
- `relation ${relation.name}: column ${declared.name} has length ${raw.length}, expected ${count}`
122
- )
92
+ function rowsOf<R extends AnyRelation>(relation: R, facts: Iterable<Fact<R>>): FlatCollection {
93
+ const data = relation.data
94
+ const cells: FactValue[] = []
95
+ let rows = 0n
96
+ for (const fact of facts) {
97
+ rows += 1n
98
+ const record = recordOf(fact)
99
+ for (const declared of data.fields) {
100
+ const value = record[declared.name]
101
+ if (value === undefined) {
102
+ throw errors.new(`relation ${data.name}: fact is missing field ${declared.name}`)
103
+ }
104
+ cells.push(cellOf(`relation ${data.name} field ${declared.name}`, declared.field, value))
123
105
  }
124
- return raw.map(function marshalCell(value: unknown) {
125
- return cellOf(`relation ${relation.name} field ${declared.name}`, declared.field, value)
126
- })
127
- })
106
+ }
107
+ return { rows, cells }
128
108
  }
129
109
 
130
110
  function mutateCollection<R extends AnyRelation>(
131
111
  relation: R,
132
112
  facts: CollectionWrite<R>,
133
- applyRows: (rows: readonly FactValue[][]) => WireMutationReport,
134
- applyColumns: (columns: readonly FactValue[][]) => WireMutationReport
113
+ apply: (rows: bigint, cells: readonly FactValue[]) => WireMutationReport
135
114
  ): MutationReport {
136
- const report = isColumnBatch(facts)
137
- ? applyColumns(columnsOf(relation, facts))
138
- : applyRows(rowsOf(relation, facts as Iterable<Fact<R>>))
115
+ const flat = rowsOf(relation, facts)
116
+ const report = apply(flat.rows, flat.cells)
139
117
  return Object.freeze({ submitted: report.submitted, changed: report.changed })
140
118
  }
141
119
 
142
- /**
143
- * Half-open fresh-id range from one `reserve`. Empty cannot yield a
144
- * minted id — `start` exists only on the nonempty arm.
145
- */
146
120
  type FreshRange =
147
121
  | {
148
122
  readonly empty: true
@@ -194,80 +168,36 @@ function freshRangeOf(wire: WireFreshRange): FreshRange {
194
168
  })
195
169
  }
196
170
 
197
- /**
198
- * The key object of a key-statement-selected `get`: exactly the selected
199
- * `key()` statement's projection fields, each at the relation's own BARE
200
- * structural value type — the {@link KeyFact} rule generalized from the
201
- * primary key to ANY declared key statement.
202
- */
203
171
  type DeclaredKeyFact<R extends AnyRelation, Projection extends readonly string[]> = {
204
172
  readonly [K in Projection[number] & keyof Fact<R>]: Fact<R>[K]
205
173
  }
206
174
 
207
- /**
208
- * One offending fact of a violation: the cited relation's name (a member
209
- * of the schema's record) and the fact decoded to a named natural-value
210
- * object — partial exactly as the engine cites it. Closed-referencing
211
- * cells arrive as handle NAMES (the marshal bijection's read half), so the
212
- * record and the violation's `canonical` string — which the engine already
213
- * renders with handle names — agree on the one spelling.
214
- */
215
175
  interface OffendingFact<Rels extends SchemaRelations> {
216
176
  readonly relation: keyof Rels & string
217
177
  readonly fact: Readonly<Record<string, FactValue>>
218
178
  }
219
179
 
220
- /**
221
- * Shared body of every violation arm: the engine's canonical rendering and
222
- * the cited facts. `statement` is NOT here — its presence is the
223
- * discriminant. Implied auto-keys have no SDK spelling (`statement` is
224
- * the value `undefined`); every declared form carries the IDENTICAL
225
- * statement value the schema declared (consumers `===`-match it).
226
- */
227
180
  type ViolationBody<Rels extends SchemaRelations> = {
228
181
  readonly canonical: string
229
182
  readonly facts: readonly OffendingFact<Rels>[]
230
183
  }
231
184
 
232
- /**
233
- * A functionality violation of an engine-materialized fresh-implied or
234
- * closed auto-key. These slots have no declared spelling (`schema()`
235
- * rejects an explicit duplicate); `statement` is present and `undefined`.
236
- */
237
185
  type ImpliedKeyViolation<Rels extends SchemaRelations> = ViolationBody<Rels> & {
238
186
  readonly kind: "functionality"
239
187
  readonly statement: undefined
240
188
  }
241
189
 
242
- /**
243
- * A functionality violation of a declared `key()` statement. `statement`
244
- * is the IDENTICAL SDK value the schema declared.
245
- */
246
190
  type DeclaredKeyViolation<Rels extends SchemaRelations> = ViolationBody<Rels> & {
247
191
  readonly kind: "functionality"
248
192
  readonly statement: Statement
249
193
  }
250
194
 
251
- /**
252
- * A containment violation of a declared `contained()` statement (no
253
- * `orientation` — that property exists exactly on {@link MirrorViolation}).
254
- */
255
195
  type ContainmentViolation<Rels extends SchemaRelations> = ViolationBody<Rels> & {
256
196
  readonly kind: "containment"
257
197
  readonly statement: Statement
258
198
  readonly direction: "sourceUnsatisfied" | "targetRequired"
259
199
  }
260
200
 
261
- /**
262
- * A containment violation of one slot of a declared `mirrors()` statement.
263
- * BOTH materialized slots render as the one `==` utterance in the written
264
- * orientation (identical `canonical` strings; the engine's `render.rs`
265
- * never emits a bare `<=` for a mirrored pair). `direction` is relative
266
- * to the violated SLOT's own orientation, so it alone cannot say which
267
- * side of the `==` was violated: `written` is the `source <= target` slot
268
- * as the statement was spelled, `mirrored` the engine-materialized
269
- * `target <= source` partner.
270
- */
271
201
  type MirrorViolation<Rels extends SchemaRelations> = ViolationBody<Rels> & {
272
202
  readonly kind: "containment"
273
203
  readonly statement: Statement
@@ -275,25 +205,12 @@ type MirrorViolation<Rels extends SchemaRelations> = ViolationBody<Rels> & {
275
205
  readonly orientation: "written" | "mirrored"
276
206
  }
277
207
 
278
- /**
279
- * A capacity violation of a declared `capacity()` statement. `measure` is
280
- * the engine's witnessed group total — u128-wide, crossing whole as
281
- * bigint (C3: truncation is unrepresentable).
282
- */
283
208
  type CapacityViolation<Rels extends SchemaRelations> = ViolationBody<Rels> & {
284
209
  readonly kind: "capacity"
285
210
  readonly statement: Statement
286
211
  readonly measure: bigint
287
212
  }
288
213
 
289
- /**
290
- * One violated statement of a rejected commit, as a typed value. The
291
- * arms are a true discriminant: `statement === undefined` is exactly the
292
- * implied-auto-key arm; every declared form carries `Statement` (not
293
- * `Statement | undefined`, not an omit-optional). `canonical` is the
294
- * ENGINE's rendering. `direction` / `measure` pass through from the
295
- * engine VERBATIM.
296
- */
297
214
  type Violation<Rels extends SchemaRelations> =
298
215
  | ImpliedKeyViolation<Rels>
299
216
  | DeclaredKeyViolation<Rels>
@@ -310,7 +227,6 @@ type Violation<Rels extends SchemaRelations> =
310
227
  */
311
228
  type AbandonedArm<R> = R extends Abandon<infer P> ? { readonly tag: "abandoned"; readonly abandoned: P } : never
312
229
 
313
- /** A callback return that is not Promise-like. TypeScript `never` is not a runtime boundary. */
314
230
  type SyncResult<R> = R extends PromiseLike<unknown> ? never : R
315
231
 
316
232
  interface Committed<T> {
@@ -322,10 +238,6 @@ type Admission<Rels extends SchemaRelations, T> =
322
238
  | { readonly tag: "accepted"; readonly value: T }
323
239
  | { readonly tag: "rejected"; readonly violations: readonly Violation<Rels>[] }
324
240
 
325
- /**
326
- * A write's domain outcome. One discriminant: narrow on `tag`. The
327
- * abandoned arm is present exactly when the callback can abandon.
328
- */
329
241
  type WriteOutcome<Rels extends SchemaRelations, R> =
330
242
  | { readonly tag: "accepted"; readonly value: Committed<Exclude<R, Abandon<unknown>>> }
331
243
  | { readonly tag: "rejected"; readonly violations: readonly Violation<Rels>[] }
@@ -335,19 +247,8 @@ type WriteFromOutcome<Rels extends SchemaRelations, R> =
335
247
  | WriteOutcome<Rels, R>
336
248
  | { readonly tag: "moved"; readonly witnessed: bigint; readonly current: bigint }
337
249
 
338
- /**
339
- * The delta-building callback of a write: runs synchronously against the
340
- * live transaction. Returning {@link abandon}`(payload)` rolls the
341
- * transaction back (R10) — the result type carries the payload arm exactly
342
- * then.
343
- */
344
250
  type DeltaBuild<Rels extends SchemaRelations, R = void> = (tx: WriteTx<Rels>) => R
345
251
 
346
- /**
347
- * The runtime discriminant of {@link Abandon} values — a property probe is
348
- * how `write`/`writeFrom` distinguish "abort without committing" from an
349
- * ordinary callback result, never a guess about the host's own value shapes.
350
- */
351
252
  const abandonMark: unique symbol = Symbol("bumbledb.abandon")
352
253
 
353
254
  /**
@@ -362,43 +263,16 @@ interface Abandon<P> {
362
263
  readonly payload: P
363
264
  }
364
265
 
365
- /**
366
- * Wraps a payload in the {@link Abandon} sentinel — the one way a write
367
- * callback declines to commit: `return abandon(payload)` aborts the delta
368
- * (nothing is committed, not even an empty commit) and the write resolves
369
- * to `{ tag: "abandoned", abandoned: payload }`, from `write` and `writeFrom`
370
- * alike (R10).
371
- */
372
266
  function abandon<P>(payload: P): Abandon<P> {
373
267
  return Object.freeze({ [abandonMark]: true as const, payload })
374
268
  }
375
269
 
376
- /**
377
- * The abandon payload type a write callback's return type implies: the
378
- * payload of its `Abandon` arm, `never` when the callback can never
379
- * abandon (the `abandoned` outcome is then statically unreachable and
380
- * {@link AbandonedArm} erases it from the sum).
381
- */
382
270
  type AbandonedPayload<R> = R extends Abandon<infer P> ? P : never
383
271
 
384
- /**
385
- * Narrows a write callback result to the abandon sentinel. The probe is
386
- * the private {@link abandonMark} symbol only {@link abandon} sets, and
387
- * `R`'s `Abandon` arm is the only way a sentinel can flow out of the
388
- * callback — so the narrowed payload type is sound by construction.
389
- */
390
272
  function isAbandon<R>(value: R): value is R & Abandon<AbandonedPayload<R>> {
391
273
  return typeof value === "object" && value !== null && abandonMark in value
392
274
  }
393
275
 
394
- /**
395
- * The abandon outcome's trusted admission seam: the value's shape is the
396
- * checkable half (the sentinel mark only {@link abandon} mints, and the
397
- * outcome carrying that sentinel's own payload), and the sentinel's
398
- * existence IS the proof `R` carries an `Abandon` arm — so the outcome is
399
- * admitted at the conditional {@link AbandonedArm} face the type tier
400
- * cannot resolve over an open `R`.
401
- */
402
276
  function isAbandonedOutcome<Rels extends SchemaRelations, R>(
403
277
  outcome: { readonly tag: "abandoned"; readonly abandoned: AbandonedPayload<R> },
404
278
  sentinel: Abandon<AbandonedPayload<R>>
@@ -406,7 +280,6 @@ function isAbandonedOutcome<Rels extends SchemaRelations, R>(
406
280
  return isAbandon(sentinel) && outcome.abandoned === sentinel.payload
407
281
  }
408
282
 
409
- /** Builds the abandoned write outcome from the callback's own sentinel (the R10 arm's one mint). */
410
283
  function abandonedOutcome<Rels extends SchemaRelations, R>(
411
284
  sentinel: Abandon<AbandonedPayload<R>>
412
285
  ): WriteOutcome<Rels, R> {
@@ -417,43 +290,16 @@ function abandonedOutcome<Rels extends SchemaRelations, R>(
417
290
  return outcome
418
291
  }
419
292
 
420
- /**
421
- * One live write transaction: the submitted delta with the engine's
422
- * FINAL-STATE point-read view (base + pending delta — the exact state the
423
- * commit judgment judges, so check-then-act is race-free by construction).
424
- * Spent when its owning `write`/`writeFrom` call resolves the attempt;
425
- * any later use throws.
426
- */
427
293
  interface WriteTx<Rels extends SchemaRelations> {
428
- /**
429
- * Records a collection of inserts. Singleton is `[fact]`. Empty is
430
- * lawful. Returns how many facts were consumed and how many changed
431
- * the in-memory final-state view. Every fact is complete — omitted
432
- * fresh cells are a type error; mint first with {@link WriteTx.reserve}.
433
- */
434
294
  insert<R extends MemberRelation<Rels>>(relation: R, facts: CollectionWrite<R>): MutationReport
435
- /**
436
- * Records a collection of deletes. Singleton is `[fact]`. Returns
437
- * how many facts were consumed and how many changed the view.
438
- */
295
+
439
296
  delete<R extends MemberRelation<Rels>>(relation: R, facts: Iterable<Fact<R>>): MutationReport
440
- /**
441
- * Mints `count` consecutive fresh values for a `.fresh` field.
442
- * `count === 0n` is empty and does not yield a start.
443
- */
297
+
444
298
  reserve<R extends MemberRelation<Rels>>(relation: R, field: FreshKeys<R> & string, count: bigint): FreshRange
445
- /** Final-state membership of one complete fact. */
299
+
446
300
  contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean
447
- /**
448
- * Final-state point lookup through the relation's primary key (the
449
- * {@link KeyFact} rule); `undefined` on a miss.
450
- */
301
+
451
302
  get<R extends MemberRelation<Rels>>(relation: R, key: KeyFact<R>): Fact<R> | undefined
452
- /**
453
- * Final-state point lookup through a DECLARED `key()` statement of this
454
- * schema — the key object is typed by the statement's own projection;
455
- * `undefined` on a miss.
456
- */
457
303
  get<R extends MemberRelation<Rels>, const P extends readonly string[]>(
458
304
  relation: R,
459
305
  keyStatement: KeyStatement<R, P>,
@@ -461,74 +307,40 @@ interface WriteTx<Rels extends SchemaRelations> {
461
307
  ): Fact<R> | undefined
462
308
  }
463
309
 
464
- type Tx<Rels extends SchemaRelations> = WriteTx<Rels>
465
-
466
310
  const witnessTypes: unique symbol = Symbol("bumbledb.witness.types")
467
311
 
468
- /**
469
- * Cloneable generation evidence from one store read. May cross `await`.
470
- * Disposal is idempotent; later use throws {@link ErrSpentHandle}.
471
- */
472
312
  interface Witness<Rels extends SchemaRelations> extends Disposable {
473
313
  readonly [witnessTypes]?: Rels
474
314
  }
475
315
 
476
- /**
477
- * The borrowed instance one `db.read((instance, witness) => …)` callback
478
- * receives. Invalid the moment the callback returns. A stashed value
479
- * throws {@link ErrUseAfterScope}. Not a handle: there is no `db.read()`.
480
- */
481
316
  interface ReadInstance<Rels extends SchemaRelations> {
482
- /**
483
- * The committed generation this instance witnessed — read inside the
484
- * lease's own transaction.
485
- */
486
317
  readonly generation: bigint
487
- /** Full-relation export in row-id order, decoded to bare structural facts. */
318
+
488
319
  scan<R extends MemberRelation<Rels>>(relation: R): Fact<R>[]
489
- /**
490
- * Committed-state point lookup through the relation's primary key
491
- * (the {@link KeyFact} rule); `undefined` on a miss.
492
- */
320
+
321
+ count<R extends MemberRelation<Rels>>(relation: R): bigint
322
+
493
323
  get<R extends MemberRelation<Rels>>(relation: R, key: KeyFact<R>): Fact<R> | undefined
494
- /**
495
- * Committed-state point lookup through a DECLARED `key()` statement of
496
- * this schema — the key object is typed by the statement's own
497
- * projection; `undefined` on a miss.
498
- */
324
+
499
325
  get<R extends MemberRelation<Rels>, const P extends readonly string[]>(
500
326
  relation: R,
501
327
  keyStatement: KeyStatement<R, P>,
502
328
  key: DeclaredKeyFact<R, P>
503
329
  ): Fact<R> | undefined
504
- /** Committed-state membership of one complete fact. */
330
+
505
331
  contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean
506
- /**
507
- * Executes a prepared query against this instance with the typed
508
- * params object; returns the answer SET as plain rows with bare
509
- * structural values (no order — the host sorts). This is the ONE
510
- * execution spelling ({@link Prepared} carries no `execute`).
511
- */
332
+
512
333
  execute<Row, Params extends ParamsRecord>(prepared: Prepared<Rels, Row, Params>, params: Params): Row[]
513
334
  prepare<Row, Params extends ParamsRecord>(q: Query<Rels, Row, Params>): Prepared<Rels, Row, Params>
514
335
  }
515
336
 
516
- /**
517
- * The module-private inference slot of {@link Prepared}: an optional symbol
518
- * property (never set at runtime) that keeps the prepared value's `Row` and
519
- * `Params` type arguments load-bearing, so `execute` infers the typed rows
520
- * and the typed params object from the value alone — the query module's
521
- * `inferred` pattern, local to this module. A type-level carrier only:
522
- * values stay bare, nothing is asserted.
523
- */
524
337
  const preparedTypes: unique symbol = Symbol("bumbledb.prepared.types")
525
338
 
526
339
  /**
527
340
  * One prepared query as a plain VALUE: explicit visible compilation
528
341
  * (`db.prepare(q)` lowers, pins the plan, and surfaces every engine roster
529
342
  * refusal), no lifecycle. Execution happens ONLY through
530
- * `instance.execute(prepared, params)` / `db.execute(prepared, params)` — the
531
- * symmetry rule's one spelling. The engine-side plan is reclaimed by a GC
343
+ * `instance.execute(prepared, params)`. The engine-side plan is reclaimed by a GC
532
344
  * finalizer when this value becomes unreachable (reclamation only, never
533
345
  * correctness — an unreclaimed plan is idle memory, and process exit frees
534
346
  * everything).
@@ -537,38 +349,10 @@ interface Prepared<Rels extends SchemaRelations, Row, Params extends ParamsRecor
537
349
  readonly [preparedTypes]?: { readonly rels: Rels; readonly row: Row; readonly params: Params }
538
350
  }
539
351
 
540
- /**
541
- * An open store. There is no close: read through `read`/the read sugar,
542
- * write through `write`/`writeFrom`, and let the process own the
543
- * environment's lifetime (the engine fsyncs every commit, so durability
544
- * never waits on a close). A second `open`/`create` of the same path
545
- * while this handle lives is the engine's `EnvironmentLocked`.
546
- */
547
352
  interface Db<Rels extends SchemaRelations> {
548
- /** The theory this store was opened with (fingerprint-verified by the engine). */
549
353
  readonly schema: Schema<Rels>
550
- /**
551
- * One store read: runs `body` SYNCHRONOUSLY inside the engine lease
552
- * and returns its result. The {@link ReadInstance} is invalidated
553
- * when `body` returns — a stashed use throws {@link ErrUseAfterScope}.
554
- * The {@link Witness} is a clone and may escape. A thenable return
555
- * throws {@link ErrAsyncCallback}.
556
- */
354
+
557
355
  read<R>(body: (instance: ReadInstance<Rels>, witness: Witness<Rels>) => SyncResult<R>): SyncResult<R>
558
- /** `db.scan(r)` === `db.read(instance => instance.scan(r))` — the symmetry rule. */
559
- scan<R extends MemberRelation<Rels>>(relation: R): Fact<R>[]
560
- /** `db.get(r, k)` === `db.read(instance => instance.get(r, k))` — the symmetry rule. */
561
- get<R extends MemberRelation<Rels>>(relation: R, key: KeyFact<R>): Fact<R> | undefined
562
- /** `db.get(r, s, k)` === `db.read(instance => instance.get(r, s, k))` — the symmetry rule, keyed form. */
563
- get<R extends MemberRelation<Rels>, const P extends readonly string[]>(
564
- relation: R,
565
- keyStatement: KeyStatement<R, P>,
566
- key: DeclaredKeyFact<R, P>
567
- ): Fact<R> | undefined
568
- /** `db.contains(r, f)` === `db.read(instance => instance.contains(r, f))` — the symmetry rule. */
569
- contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean
570
- /** `db.execute(p, params)` === `db.read(instance => instance.execute(p, params))` — the symmetry rule. */
571
- execute<Row, Params extends ParamsRecord>(prepared: Prepared<Rels, Row, Params>, params: Params): Row[]
572
356
  /**
573
357
  * One delta transaction: builds the delta synchronously through `fn`,
574
358
  * commits, and returns the domain outcome. A throw from `fn` aborts
@@ -578,11 +362,7 @@ interface Db<Rels extends SchemaRelations> {
578
362
  * outcome is `{ tag: "abandoned", abandoned: payload }`.
579
363
  */
580
364
  write<R>(fn: (tx: WriteTx<Rels>) => SyncResult<R>): WriteOutcome<Rels, SyncResult<R>>
581
- /**
582
- * Witnessed write: commits only if no state-changing commit landed
583
- * since `witness` was minted. A moved generation is
584
- * `{ tag: "moved" }` — retry is host policy; this method never loops.
585
- */
365
+
586
366
  writeFrom<R>(witness: Witness<Rels>, fn: (tx: WriteTx<Rels>) => SyncResult<R>): WriteFromOutcome<Rels, SyncResult<R>>
587
367
  /**
588
368
  * Prepares a query value built against THIS schema (identity is the
@@ -594,7 +374,6 @@ interface Db<Rels extends SchemaRelations> {
594
374
  prepare<Row, Params extends ParamsRecord>(q: Query<Rels, Row, Params>): Prepared<Rels, Row, Params>
595
375
  }
596
376
 
597
- /** One relation's runtime tables: engine id, the identical schema member, field ids, primary key. */
598
377
  interface RelationEntry {
599
378
  readonly id: number
600
379
  readonly member: SchemaRelation
@@ -602,19 +381,11 @@ interface RelationEntry {
602
381
  readonly primaryKey: PrimaryKey | undefined
603
382
  }
604
383
 
605
- /** One relation's primary candidate key: its materialized statement id and projection. */
606
384
  interface PrimaryKey {
607
385
  readonly statementId: number
608
386
  readonly projection: readonly string[]
609
387
  }
610
388
 
611
- /**
612
- * One materialized-statement slot as the SDK mirrors it. Implied auto-keys
613
- * omit `statement` (the engine owns those slots); every declared form
614
- * carries the SDK value that lowered to it. Functionality forms also
615
- * carry the key's owner and projection (what keyed point reads resolve
616
- * through).
617
- */
618
389
  type ImpliedKeyEntry = {
619
390
  readonly kind: "functionality"
620
391
  readonly owner: string
@@ -635,17 +406,72 @@ type StatementEntry =
635
406
  | { readonly kind: "mirrors"; readonly statement: Statement; readonly orientation: "written" | "mirrored" }
636
407
  | { readonly kind: "capacity"; readonly statement: Statement }
637
408
 
638
- /**
639
- * Mirrors the engine's materialized statement order
640
- * (`SchemaDescriptor::materialized_statements`, pinned by the fingerprint):
641
- * one auto-key per fresh field (relation declaration order, then field
642
- * order), one closed auto-key per closed relation (declaration order),
643
- * then the declared statements in declaration order — a `mirrors`
644
- * statement occupying TWO adjacent slots (the engine lowers `==` to two
645
- * containments, `source <= target` first), both owned by the one SDK
646
- * value. This positional match is how statement ids resolve back to SDK
647
- * statement values without the engine ever learning a wire format.
648
- */
409
+ function decodeOffendingFact<Rels extends SchemaRelations>(
410
+ member: SchemaRelation,
411
+ relation: keyof Rels & string,
412
+ fact: WireViolationFact
413
+ ): OffendingFact<Rels> {
414
+ const declared = sealedFieldsOf(member)
415
+ const decoded: Record<string, FactValue> = {}
416
+ for (const cell of fact.fields) {
417
+ const cited = declared.find(function byName(candidate) {
418
+ return candidate.name === cell.name
419
+ })
420
+ const roster = rosterOf(cited?.field)
421
+ decoded[cell.name] =
422
+ roster !== undefined
423
+ ? handleOf(`violation fact ${fact.relation} field ${cell.name}`, roster, cell.value)
424
+ : cell.value
425
+ }
426
+ return Object.freeze({ relation, fact: Object.freeze(decoded) })
427
+ }
428
+
429
+ function violationFromEntry<Rels extends SchemaRelations>(
430
+ entry: StatementEntry,
431
+ wire: WireViolation,
432
+ facts: readonly OffendingFact<Rels>[]
433
+ ): Violation<Rels> {
434
+ const canonical = wire.canonical
435
+ if (entry.kind === "functionality") {
436
+ if (!("statement" in entry)) {
437
+ return Object.freeze({ kind: "functionality", statement: undefined, canonical, facts })
438
+ }
439
+ return Object.freeze({ kind: "functionality", statement: entry.statement, canonical, facts })
440
+ }
441
+ if (entry.kind === "capacity") {
442
+ if (wire.kind !== "capacity") {
443
+ throw errors.new(`bumbledb violation ${wire.statementId} is a capacity slot without a measure`)
444
+ }
445
+ return Object.freeze({
446
+ kind: "capacity",
447
+ statement: entry.statement,
448
+ canonical,
449
+ measure: wire.measure,
450
+ facts
451
+ })
452
+ }
453
+ if (wire.kind !== "containment") {
454
+ throw errors.new(`bumbledb violation ${wire.statementId} is a containment slot without a direction`)
455
+ }
456
+ if (entry.kind === "mirrors") {
457
+ return Object.freeze({
458
+ kind: "containment",
459
+ statement: entry.statement,
460
+ canonical,
461
+ direction: wire.direction,
462
+ orientation: entry.orientation,
463
+ facts
464
+ })
465
+ }
466
+ return Object.freeze({
467
+ kind: "containment",
468
+ statement: entry.statement,
469
+ canonical,
470
+ direction: wire.direction,
471
+ facts
472
+ })
473
+ }
474
+
649
475
  function materializedEntries(theory: AnySchema): StatementEntry[] {
650
476
  const entries = impliedKeyEntries(theory)
651
477
  for (const statement of theory.statements) {
@@ -654,13 +480,6 @@ function materializedEntries(theory: AnySchema): StatementEntry[] {
654
480
  return entries
655
481
  }
656
482
 
657
- /**
658
- * The engine-materialized implied keys, in the engine's pinned order: one
659
- * auto-key per fresh field (relation declaration order, then field order),
660
- * then one closed auto-key `R(id) -> R` per closed relation (declaration
661
- * order). These slots carry no SDK statement value — the engine owns them
662
- * (`schema()` rejects an explicit duplicate).
663
- */
664
483
  function impliedKeyEntries(theory: AnySchema): StatementEntry[] {
665
484
  const entries: StatementEntry[] = []
666
485
  for (const member of Object.values(theory.relations)) {
@@ -689,12 +508,6 @@ function impliedKeyEntries(theory: AnySchema): StatementEntry[] {
689
508
  return entries
690
509
  }
691
510
 
692
- /**
693
- * One declared statement's materialized slots: a key or capacity statement
694
- * occupies one, a `mirrors` occupies two adjacent slots (the engine lowers
695
- * `==` to two containments, `source <= target` first), both owned by the
696
- * one SDK value.
697
- */
698
511
  function declaredEntries(statement: Statement): StatementEntry[] {
699
512
  const data = statement.data
700
513
  switch (data.kind) {
@@ -733,29 +546,12 @@ function isThenable(value: unknown): boolean {
733
546
  return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function"
734
547
  }
735
548
 
736
- /**
737
- * Narrows a keyed-get middle argument to a statement value (vs a key
738
- * object) through the statement module's admission brand — a
739
- * REPRESENTATION, never a shape probe: fact cell shapes are structurally
740
- * OPEN (an interval value carrying an excess `kind` property is a legal
741
- * cell), so no property probe could ever be sound here, but no host-built
742
- * key object can spell the module-private brand symbol.
743
- */
744
549
  function isStatementValue<R extends AnyRelation, P extends readonly string[]>(
745
550
  value: KeyFact<R> | KeyStatement<R, P>
746
551
  ): value is KeyStatement<R, P> {
747
552
  return isStatement(value)
748
553
  }
749
554
 
750
- /**
751
- * THE one selector dispatch of the `get` overload pair (primary-key vs
752
- * key-statement, `docs/architecture/70-api.md` § the freeze): judges the
753
- * middle argument once and hands the narrowed pieces to the chosen
754
- * continuation. `Db.get` and the read scope's `get` both dispatch through
755
- * here, so the two mismatch refusals speak with one voice and the symmetry
756
- * rule (`db.get(...) === db.read(snap => snap.get(...))`) holds by
757
- * construction.
758
- */
759
555
  function selectKeyRead<R extends AnyRelation, P extends readonly string[], T>(
760
556
  keyOrStatement: KeyFact<R> | KeyStatement<R, P>,
761
557
  declaredKey: DeclaredKeyFact<R, P> | undefined,
@@ -774,22 +570,11 @@ function selectKeyRead<R extends AnyRelation, P extends readonly string[], T>(
774
570
  return byPrimary(keyOrStatement)
775
571
  }
776
572
 
777
- /** The id-resolution tables one open builds: relation entries by name, statement slots by id. */
778
573
  interface Tables {
779
574
  readonly relations: ReadonlyMap<string, RelationEntry>
780
575
  readonly statements: readonly StatementEntry[]
781
576
  }
782
577
 
783
- /**
784
- * Builds the id-resolution tables from the manifest, verifying the SDK's
785
- * positional mirror against the engine's reported order — any drift
786
- * (count, kind, id, or membership) is a construction-time failure, never a
787
- * silent misattribution of a violation to the wrong statement value. The
788
- * declaration-ordinal law the query lowering leans on is verified in the
789
- * same walks: relation ids and sealed field ids both equal declaration
790
- * order, so a constructed `Tables` IS the proof and `prepare` inherits it
791
- * structurally — never a silently misaddressed query.
792
- */
793
578
  function tablesOf(theory: AnySchema, manifest: Manifest): Tables {
794
579
  const entries = materializedEntries(theory)
795
580
  if (entries.length !== manifest.statements.length) {
@@ -873,16 +658,11 @@ function tablesFromTheory(theory: AnySchema): Tables {
873
658
  return Object.freeze({ relations, statements: Object.freeze(entries) })
874
659
  }
875
660
 
876
- /** The point-read half a transaction and a read scope share, over their own handle. */
877
661
  interface PointReads {
878
662
  contains(relationId: number, row: readonly FactValue[]): boolean
879
663
  get(relationId: number, statementId: number, key: readonly FactValue[]): FactValue[] | null
880
664
  }
881
665
 
882
- /**
883
- * One borrowed instance's PRIVATE lifetime record. Held in
884
- * {@link instanceStates} — the native handle is never a public value.
885
- */
886
666
  interface InstanceState {
887
667
  readonly handle: InstanceHandle
888
668
  live: boolean
@@ -908,12 +688,6 @@ const witnessReclaimer = new FinalizationRegistry<WitnessHandle>(function reclai
908
688
  }
909
689
  })
910
690
 
911
- /**
912
- * One prepared value's PRIVATE engine half: the pinned plan handle, the
913
- * owning store's identity token, and the query's marshaling tables (params
914
- * in declaration order, select columns in head order). Held in
915
- * {@link preparedPlans} — the plan handle is never a public value.
916
- */
917
691
  interface PreparedPlan {
918
692
  readonly handle: PreparedHandle
919
693
  readonly owner: object
@@ -921,15 +695,8 @@ interface PreparedPlan {
921
695
  readonly finds: readonly FindColumn[]
922
696
  }
923
697
 
924
- /** The private engine halves of this module's prepared values. */
925
698
  const preparedPlans = new WeakMap<object, PreparedPlan>()
926
699
 
927
- /**
928
- * Reclaims the engine-side plan of a garbage-collected {@link Prepared}
929
- * value. RECLAMATION ONLY, never correctness: a plan the collector never
930
- * visits is idle engine memory until process exit, and a failure to close
931
- * is swallowed (there is no one left to care — the owning value is gone).
932
- */
933
700
  const planReclaimer = new FinalizationRegistry<PreparedHandle>(function reclaimPlan(handle) {
934
701
  const closed = errors.trySync(function closePlan() {
935
702
  native.preparedClose(handle)
@@ -949,13 +716,9 @@ const ErrUseAfterScope = errors.new(
949
716
  const ErrForeignPrepared = errors.new("bumbledb foreignPrepared: a prepared query met a foreign instance")
950
717
  const ErrForeignWitness = errors.new("bumbledb foreignWitness: a witness met a foreign store")
951
718
 
952
- /**
953
- * The shared typed read surface: store leases and owned instances both
954
- * expose scan/get/contains/execute/prepare. The native ops are the only
955
- * difference — one way to read, two handle kinds.
956
- */
957
719
  interface CatalogNative {
958
720
  scan(relationId: number): FactValue[][]
721
+ count(relationId: number): bigint
959
722
  contains(relationId: number, values: readonly FactValue[]): boolean
960
723
  get(relationId: number, statementId: number, keyValues: readonly FactValue[]): FactValue[] | null
961
724
  prepare(query: ReturnType<typeof lowerQuery>): ReturnType<typeof native.instancePrepare>
@@ -968,39 +731,7 @@ function catalogMethods<Rels extends SchemaRelations>(
968
731
  owner: object,
969
732
  assertLive: () => void,
970
733
  ops: CatalogNative
971
- ): Pick<ReadInstance<Rels>, "scan" | "get" | "contains" | "execute" | "prepare"> {
972
- function resolveOrdinary(relation: AnyRelation): RelationEntry {
973
- const entry = tables.relations.get(relation.name)
974
- if (entry === undefined || entry.member !== relation) {
975
- throw errors.new(`relation ${relation.name} is not a member of schema ${theory.name}`)
976
- }
977
- if (isClosedMember(relation)) {
978
- throw errors.new(
979
- `relation ${relation.name} is closed — its extension is schema data (axioms), never scanned or written`
980
- )
981
- }
982
- return entry
983
- }
984
- function declaredKeyOf(relation: AnyRelation, statement: Statement): PrimaryKey {
985
- const statementId = tables.statements.findIndex(function byIdentity(candidate) {
986
- return "statement" in candidate && candidate.statement === statement
987
- })
988
- const entry = tables.statements[statementId]
989
- if (entry === undefined) {
990
- throw errors.new(
991
- `keyed get statement is not a declared statement of schema ${theory.name} — statement identity is the membership rule`
992
- )
993
- }
994
- if (entry.kind !== "functionality") {
995
- throw errors.new("keyed get takes a key() statement — containments and capacity statements key nothing")
996
- }
997
- if (entry.owner !== relation.name) {
998
- throw errors.new(
999
- `keyed get statement keys ${entry.owner}, not ${relation.name} — the statement must be a declared key of the relation it reads`
1000
- )
1001
- }
1002
- return Object.freeze({ statementId, projection: entry.projection })
1003
- }
734
+ ): Pick<ReadInstance<Rels>, "scan" | "count" | "get" | "contains" | "execute" | "prepare"> {
1004
735
  function planOf(prepared: object): PreparedPlan {
1005
736
  const plan = preparedPlans.get(prepared)
1006
737
  if (plan === undefined) {
@@ -1016,7 +747,7 @@ function catalogMethods<Rels extends SchemaRelations>(
1016
747
  }
1017
748
  function contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean {
1018
749
  assertLive()
1019
- const entry = resolveOrdinary(relation)
750
+ const entry = ordinaryEntry(tables, theory, relation)
1020
751
  return bridged("bumbledb instance contains", function readContains() {
1021
752
  return ops.contains(entry.id, rowOf(relation.data, recordOf(fact)))
1022
753
  })
@@ -1027,12 +758,12 @@ function catalogMethods<Rels extends SchemaRelations>(
1027
758
  declaredKey?: DeclaredKeyFact<R, P>
1028
759
  ): Fact<R> | undefined {
1029
760
  assertLive()
1030
- const entry = resolveOrdinary(relation)
761
+ const entry = ordinaryEntry(tables, theory, relation)
1031
762
  return selectKeyRead(
1032
763
  keyOrStatement,
1033
764
  declaredKey,
1034
765
  function byStatement(statement, key) {
1035
- const selected = declaredKeyOf(relation, statement)
766
+ const selected = declaredKeyOf(tables, theory, relation, statement)
1036
767
  const row = bridged("bumbledb instance get", function readGet() {
1037
768
  return ops.get(entry.id, selected.statementId, keyRowOf(relation.data, selected.projection, recordOf(key)))
1038
769
  })
@@ -1058,7 +789,7 @@ function catalogMethods<Rels extends SchemaRelations>(
1058
789
  }
1059
790
  function scan<R extends MemberRelation<Rels>>(relation: R): Fact<R>[] {
1060
791
  assertLive()
1061
- const entry = resolveOrdinary(relation)
792
+ const entry = ordinaryEntry(tables, theory, relation)
1062
793
  const rows = bridged("bumbledb instance scan", function readScan() {
1063
794
  return ops.scan(entry.id)
1064
795
  })
@@ -1066,6 +797,13 @@ function catalogMethods<Rels extends SchemaRelations>(
1066
797
  return factOf(relation, row)
1067
798
  })
1068
799
  }
800
+ function count<R extends MemberRelation<Rels>>(relation: R): bigint {
801
+ assertLive()
802
+ const entry = ordinaryEntry(tables, theory, relation)
803
+ return bridged("bumbledb instance count", function readCount() {
804
+ return ops.count(entry.id)
805
+ })
806
+ }
1069
807
  function execute<Row, Params extends ParamsRecord>(prepared: Prepared<Rels, Row, Params>, params: Params): Row[] {
1070
808
  assertLive()
1071
809
  const plan = planOf(prepared)
@@ -1102,7 +840,7 @@ function catalogMethods<Rels extends SchemaRelations>(
1102
840
  planReclaimer.register(prepared, outcome.prepared)
1103
841
  return prepared
1104
842
  }
1105
- return { scan, get, contains, execute, prepare }
843
+ return { scan, count, get, contains, execute, prepare }
1106
844
  }
1107
845
 
1108
846
  function ordinaryEntry(tables: Tables, theory: AnySchema, relation: AnyRelation): RelationEntry {
@@ -1118,32 +856,33 @@ function ordinaryEntry(tables: Tables, theory: AnySchema, relation: AnyRelation)
1118
856
  return entry
1119
857
  }
1120
858
 
859
+ function declaredKeyOf(tables: Tables, theory: AnySchema, relation: AnyRelation, statement: Statement): PrimaryKey {
860
+ const statementId = tables.statements.findIndex(function byIdentity(candidate) {
861
+ return "statement" in candidate && candidate.statement === statement
862
+ })
863
+ const entry = tables.statements[statementId]
864
+ if (entry === undefined) {
865
+ throw errors.new(
866
+ `keyed get statement is not a declared statement of schema ${theory.name} — statement identity is the membership rule`
867
+ )
868
+ }
869
+ if (entry.kind !== "functionality") {
870
+ throw errors.new("keyed get takes a key() statement — containments and capacity statements key nothing")
871
+ }
872
+ if (entry.owner !== relation.name) {
873
+ throw errors.new(
874
+ `keyed get statement keys ${entry.owner}, not ${relation.name} — the statement must be a declared key of the relation it reads`
875
+ )
876
+ }
877
+ return Object.freeze({ statementId, projection: entry.projection })
878
+ }
879
+
1121
880
  function overlayMethods<Rels extends SchemaRelations>(
1122
881
  theory: Schema<Rels>,
1123
882
  tables: Tables,
1124
883
  assertLive: () => void,
1125
884
  reads: PointReads
1126
885
  ): Pick<WriteTx<Rels>, "contains" | "get"> {
1127
- function declaredKeyOf(relation: AnyRelation, statement: Statement): PrimaryKey {
1128
- const statementId = tables.statements.findIndex(function byIdentity(candidate) {
1129
- return "statement" in candidate && candidate.statement === statement
1130
- })
1131
- const entry = tables.statements[statementId]
1132
- if (entry === undefined) {
1133
- throw errors.new(
1134
- `keyed get statement is not a declared statement of schema ${theory.name} — statement identity is the membership rule`
1135
- )
1136
- }
1137
- if (entry.kind !== "functionality") {
1138
- throw errors.new("keyed get takes a key() statement — containments and capacity statements key nothing")
1139
- }
1140
- if (entry.owner !== relation.name) {
1141
- throw errors.new(
1142
- `keyed get statement keys ${entry.owner}, not ${relation.name} — the statement must be a declared key of the relation it reads`
1143
- )
1144
- }
1145
- return Object.freeze({ statementId, projection: entry.projection })
1146
- }
1147
886
  function contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean {
1148
887
  assertLive()
1149
888
  const entry = ordinaryEntry(tables, theory, relation)
@@ -1178,7 +917,7 @@ function overlayMethods<Rels extends SchemaRelations>(
1178
917
  keyOrStatement,
1179
918
  declaredKey,
1180
919
  function byStatement(statement, key) {
1181
- return readThroughKey(relation, entry, declaredKeyOf(relation, statement), recordOf(key))
920
+ return readThroughKey(relation, entry, declaredKeyOf(tables, theory, relation, statement), recordOf(key))
1182
921
  },
1183
922
  function byPrimary(key) {
1184
923
  const primaryKey = entry.primaryKey
@@ -1213,6 +952,9 @@ function createReadInstance<Rels extends SchemaRelations>(
1213
952
  scan(relationId) {
1214
953
  return native.instanceScan(state.handle, relationId)
1215
954
  },
955
+ count(relationId) {
956
+ return native.instanceCount(state.handle, relationId)
957
+ },
1216
958
  contains(relationId, values) {
1217
959
  return native.instanceContains(state.handle, relationId, values)
1218
960
  },
@@ -1239,12 +981,6 @@ function createReadInstance<Rels extends SchemaRelations>(
1239
981
  return instance
1240
982
  }
1241
983
 
1242
- /**
1243
- * Constructs one open `Db` over an already-admitted handle: builds the
1244
- * id-resolution tables once and closes over them — the `Db` owns handle
1245
- * and tables and nothing else. Handle lifetime is the process's: the store
1246
- * cache holds the environment handle until the exit hook closes it.
1247
- */
1248
984
  function openDb<Rels extends SchemaRelations>(handle: DbHandle, theory: Schema<Rels>, manifest: Manifest): Db<Rels> {
1249
985
  const tables = tablesOf(theory, manifest)
1250
986
  /** This store's identity token: read scopes and prepared values carry it, so cross-store use is a typed refusal. */
@@ -1254,121 +990,30 @@ function openDb<Rels extends SchemaRelations>(handle: DbHandle, theory: Schema<R
1254
990
  return tables.relations.has(name)
1255
991
  }
1256
992
 
1257
- function resolveOrdinary(relation: AnyRelation): RelationEntry {
1258
- const entry = tables.relations.get(relation.name)
1259
- if (entry === undefined || entry.member !== relation) {
1260
- throw errors.new(`relation ${relation.name} is not a member of schema ${theory.name}`)
1261
- }
1262
- if (isClosedMember(relation)) {
1263
- throw errors.new(
1264
- `relation ${relation.name} is closed — its extension is schema data (axioms), never scanned or written`
1265
- )
1266
- }
1267
- return entry
1268
- }
1269
-
1270
- function offendingFactOf(fact: WireViolationFact): OffendingFact<Rels> {
1271
- const entry = tables.relations.get(fact.relation)
1272
- if (entry === undefined || !isMemberName(fact.relation)) {
1273
- throw errors.new(`bumbledb violation cites unknown relation ${fact.relation}`)
1274
- }
1275
- const declared = sealedFieldsOf(entry.member)
1276
- const decoded: Record<string, FactValue> = {}
1277
- for (const cell of fact.fields) {
1278
- const cited = declared.find(function byName(candidate) {
1279
- return candidate.name === cell.name
1280
- })
1281
- const roster = rosterOf(cited?.field)
1282
- decoded[cell.name] =
1283
- roster !== undefined
1284
- ? handleOf(`violation fact ${fact.relation} field ${cell.name}`, roster, cell.value)
1285
- : cell.value
1286
- }
1287
- return Object.freeze({ relation: fact.relation, fact: Object.freeze(decoded) })
1288
- }
1289
-
1290
993
  function violationOf(wire: WireViolation): Violation<Rels> {
1291
994
  const entry = tables.statements[wire.statementId]
1292
995
  if (entry === undefined) {
1293
996
  throw errors.new(`bumbledb violation cites unknown statement id ${wire.statementId}`)
1294
997
  }
1295
- const facts = Object.freeze(wire.facts.map(offendingFactOf))
1296
- const canonical = wire.canonical
1297
- if (entry.kind === "functionality") {
1298
- if (!("statement" in entry)) {
1299
- return Object.freeze({ kind: "functionality", statement: undefined, canonical, facts })
1300
- }
1301
- return Object.freeze({ kind: "functionality", statement: entry.statement, canonical, facts })
1302
- }
1303
- if (entry.kind === "capacity") {
1304
- if (wire.kind !== "capacity") {
1305
- throw errors.new(`bumbledb violation ${wire.statementId} is a capacity slot without a measure`)
1306
- }
1307
- return Object.freeze({
1308
- kind: "capacity",
1309
- statement: entry.statement,
1310
- canonical,
1311
- measure: wire.measure,
1312
- facts
1313
- })
1314
- }
1315
- if (wire.kind !== "containment") {
1316
- throw errors.new(`bumbledb violation ${wire.statementId} is a containment slot without a direction`)
1317
- }
1318
- if (entry.kind === "mirrors") {
1319
- return Object.freeze({
1320
- kind: "containment",
1321
- statement: entry.statement,
1322
- canonical,
1323
- direction: wire.direction,
1324
- orientation: entry.orientation,
1325
- facts
998
+ const facts = Object.freeze(
999
+ wire.facts.map(function offending(fact) {
1000
+ const rel = tables.relations.get(fact.relation)
1001
+ if (rel === undefined || !isMemberName(fact.relation)) {
1002
+ throw errors.new(`bumbledb violation cites unknown relation ${fact.relation}`)
1003
+ }
1004
+ return decodeOffendingFact<Rels>(rel.member, fact.relation, fact)
1326
1005
  })
1327
- }
1328
- return Object.freeze({
1329
- kind: "containment",
1330
- statement: entry.statement,
1331
- canonical,
1332
- direction: wire.direction,
1333
- facts
1334
- })
1335
- }
1336
-
1337
- /**
1338
- * Resolves a key-statement-selected read: the statement must be the
1339
- * IDENTICAL `key()` value this schema declared (identity is the
1340
- * membership rule) and must key `relation` — its materialized statement
1341
- * id comes from the positional mirror, so the engine point-reads through
1342
- * exactly the declared projection.
1343
- */
1344
- function declaredKeyOf(relation: AnyRelation, statement: Statement): PrimaryKey {
1345
- const statementId = tables.statements.findIndex(function byIdentity(candidate) {
1346
- return "statement" in candidate && candidate.statement === statement
1347
- })
1348
- const entry = tables.statements[statementId]
1349
- if (entry === undefined) {
1350
- throw errors.new(
1351
- `keyed get statement is not a declared statement of schema ${theory.name} — statement identity is the membership rule`
1352
- )
1353
- }
1354
- if (entry.kind !== "functionality") {
1355
- throw errors.new("keyed get takes a key() statement — containments and capacity statements key nothing")
1356
- }
1357
- if (entry.owner !== relation.name) {
1358
- throw errors.new(
1359
- `keyed get statement keys ${entry.owner}, not ${relation.name} — the statement must be a declared key of the relation it reads`
1360
- )
1361
- }
1362
- return Object.freeze({ statementId, projection: entry.projection })
1006
+ )
1007
+ return violationFromEntry(entry, wire, facts)
1363
1008
  }
1364
1009
 
1365
1010
  function pointReadsOf(assertLive: () => void, reads: PointReads) {
1366
1011
  function contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean {
1367
1012
  assertLive()
1368
- const entry = resolveOrdinary(relation)
1013
+ const entry = ordinaryEntry(tables, theory, relation)
1369
1014
  return reads.contains(entry.id, rowOf(relation.data, recordOf(fact)))
1370
1015
  }
1371
- /** One keyed point read through an already-resolved key, decoded to a fact (`undefined` on a miss). */
1016
+
1372
1017
  function readThroughKey<R extends MemberRelation<Rels>>(
1373
1018
  relation: R,
1374
1019
  entry: RelationEntry,
@@ -1393,12 +1038,12 @@ function openDb<Rels extends SchemaRelations>(handle: DbHandle, theory: Schema<R
1393
1038
  declaredKey?: DeclaredKeyFact<R, P>
1394
1039
  ): Fact<R> | undefined {
1395
1040
  assertLive()
1396
- const entry = resolveOrdinary(relation)
1041
+ const entry = ordinaryEntry(tables, theory, relation)
1397
1042
  return selectKeyRead(
1398
1043
  keyOrStatement,
1399
1044
  declaredKey,
1400
1045
  function byStatement(statement, key) {
1401
- return readThroughKey(relation, entry, declaredKeyOf(relation, statement), recordOf(key))
1046
+ return readThroughKey(relation, entry, declaredKeyOf(tables, theory, relation, statement), recordOf(key))
1402
1047
  },
1403
1048
  function byPrimary(key) {
1404
1049
  const primaryKey = entry.primaryKey
@@ -1475,51 +1120,6 @@ function openDb<Rels extends SchemaRelations>(handle: DbHandle, theory: Schema<R
1475
1120
  return (captured ?? result) as SyncResult<R>
1476
1121
  }
1477
1122
 
1478
- function scan<R extends MemberRelation<Rels>>(relation: R): Fact<R>[] {
1479
- return read(function scanInScope(instance) {
1480
- return instance.scan(relation)
1481
- })
1482
- }
1483
-
1484
- function get<R extends MemberRelation<Rels>>(relation: R, key: KeyFact<R>): Fact<R> | undefined
1485
- function get<R extends MemberRelation<Rels>, const P extends readonly string[]>(
1486
- relation: R,
1487
- keyStatement: KeyStatement<R, P>,
1488
- key: DeclaredKeyFact<R, P>
1489
- ): Fact<R> | undefined
1490
- function get<R extends MemberRelation<Rels>, const P extends readonly string[]>(
1491
- relation: R,
1492
- keyOrStatement: KeyFact<R> | KeyStatement<R, P>,
1493
- declaredKey?: DeclaredKeyFact<R, P>
1494
- ): Fact<R> | undefined {
1495
- let found: Fact<R> | undefined
1496
- read(function getInScope(instance) {
1497
- found = selectKeyRead(
1498
- keyOrStatement,
1499
- declaredKey,
1500
- function byStatement(statement, key) {
1501
- return instance.get(relation, statement, key)
1502
- },
1503
- function byPrimary(key) {
1504
- return instance.get(relation, key)
1505
- }
1506
- )
1507
- })
1508
- return found
1509
- }
1510
-
1511
- function contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean {
1512
- return read(function containsInScope(instance) {
1513
- return instance.contains(relation, fact)
1514
- })
1515
- }
1516
-
1517
- function execute<Row, Params extends ParamsRecord>(prepared: Prepared<Rels, Row, Params>, params: Params): Row[] {
1518
- return read(function executeInScope(instance) {
1519
- return instance.execute(prepared, params)
1520
- })
1521
- }
1522
-
1523
1123
  function makeTx(resolveTx: () => TxHandle): { readonly tx: WriteTx<Rels>; spend(): void } {
1524
1124
  const txState = { spent: false }
1525
1125
  function assertLive(): void {
@@ -1543,29 +1143,21 @@ function openDb<Rels extends SchemaRelations>(handle: DbHandle, theory: Schema<R
1543
1143
  })
1544
1144
  function insert<R extends MemberRelation<Rels>>(relation: R, facts: CollectionWrite<R>): MutationReport {
1545
1145
  assertLive()
1546
- const entry = resolveOrdinary(relation)
1146
+ const entry = ordinaryEntry(tables, theory, relation)
1547
1147
  const txHandle = resolveTx()
1548
- return mutateCollection(
1549
- relation,
1550
- facts,
1551
- function applyRows(rows) {
1552
- return bridged("bumbledb tx insert", function record() {
1553
- return native.txInsert(txHandle, entry.id, rows)
1554
- })
1555
- },
1556
- function applyColumns(columns) {
1557
- return bridged("bumbledb tx insert", function recordColumns() {
1558
- return native.txInsertColumns(txHandle, entry.id, columns)
1559
- })
1560
- }
1561
- )
1148
+ return mutateCollection(relation, facts, function applyCells(rows, cells) {
1149
+ return bridged("bumbledb tx insert", function record() {
1150
+ return native.txInsert(txHandle, entry.id, rows, cells)
1151
+ })
1152
+ })
1562
1153
  }
1563
1154
  function remove<R extends MemberRelation<Rels>>(relation: R, facts: Iterable<Fact<R>>): MutationReport {
1564
1155
  assertLive()
1565
- const entry = resolveOrdinary(relation)
1156
+ const entry = ordinaryEntry(tables, theory, relation)
1566
1157
  const txHandle = resolveTx()
1158
+ const flat = rowsOf(relation, facts)
1567
1159
  const report = bridged("bumbledb tx delete", function record() {
1568
- return native.txDelete(txHandle, entry.id, rowsOf(relation, facts))
1160
+ return native.txDelete(txHandle, entry.id, flat.rows, flat.cells)
1569
1161
  })
1570
1162
  return Object.freeze({ submitted: report.submitted, changed: report.changed })
1571
1163
  }
@@ -1575,7 +1167,7 @@ function openDb<Rels extends SchemaRelations>(handle: DbHandle, theory: Schema<R
1575
1167
  count: bigint
1576
1168
  ): FreshRange {
1577
1169
  assertLive()
1578
- const entry = resolveOrdinary(relation)
1170
+ const entry = ordinaryEntry(tables, theory, relation)
1579
1171
  const declared = relation.data.fields.find(function byName(candidate) {
1580
1172
  return candidate.name === field
1581
1173
  })
@@ -1712,33 +1304,17 @@ function openDb<Rels extends SchemaRelations>(handle: DbHandle, theory: Schema<R
1712
1304
  return Object.freeze({
1713
1305
  schema: theory,
1714
1306
  read,
1715
- scan,
1716
- get,
1717
- contains,
1718
- execute,
1719
1307
  write,
1720
1308
  writeFrom,
1721
1309
  prepare
1722
1310
  })
1723
1311
  }
1724
1312
 
1725
- /**
1726
- * The engine twin of the schema-level class wall, as a matchable value
1727
- * (`errors.is`): the shared lowering rejected a spec whose statement pairs
1728
- * faces with disagreeing newtype labels — the faces of a dependency agree
1729
- * on their newtype, or neither carries one. UNREACHABLE through the typed
1730
- * builder (the SDK computes every label from the laws, so its lowered
1731
- * specs cohere by construction); a raw spec handed to the bridge is the
1732
- * one road here, and the runtime referee that proves the engine judges
1733
- * what the types claim.
1734
- */
1735
1313
  const ErrNewtypeMismatch = errors.new(
1736
1314
  "bumbledb newtypeMismatch: a statement pairs faces whose newtypes disagree — the faces of a dependency agree on their newtype, or neither carries one"
1737
1315
  )
1738
1316
  const ErrSchemaError = errors.new("bumbledb schemaError: the declaration failed validation")
1739
- const ErrFingerprintMismatch = errors.new(
1740
- "bumbledb fingerprintMismatch: the store's schema does not match this theory"
1741
- )
1317
+ const ErrFingerprintMismatch = errors.new("bumbledb fingerprintMismatch: the store's schema does not match this theory")
1742
1318
  const ErrIrError = errors.new("bumbledb irError: the query failed validation")
1743
1319
 
1744
1320
  function throwOpenRefusal(
@@ -1802,71 +1378,19 @@ function mapViolationWithoutStore<Rels extends SchemaRelations>(
1802
1378
  if (entry === undefined) {
1803
1379
  throw errors.new(`bumbledb violation cites unknown statement id ${wire.statementId}`)
1804
1380
  }
1805
- function offending(fact: WireViolationFact): OffendingFact<Rels> {
1806
- const member = theory.relations[fact.relation]
1807
- if (member === undefined || !(fact.relation in theory.relations)) {
1808
- throw errors.new(`bumbledb violation cites unknown relation ${fact.relation}`)
1809
- }
1810
- const declared = sealedFieldsOf(member)
1811
- const decoded: Record<string, FactValue> = {}
1812
- for (const cell of fact.fields) {
1813
- const cited = declared.find(function byName(candidate) {
1814
- return candidate.name === cell.name
1815
- })
1816
- const roster = rosterOf(cited?.field)
1817
- decoded[cell.name] =
1818
- roster !== undefined
1819
- ? handleOf(`violation fact ${fact.relation} field ${cell.name}`, roster, cell.value)
1820
- : cell.value
1821
- }
1822
- return Object.freeze({ relation: fact.relation as keyof Rels & string, fact: Object.freeze(decoded) })
1823
- }
1824
- const facts = Object.freeze(wire.facts.map(offending))
1825
- const canonical = wire.canonical
1826
- if (entry.kind === "functionality") {
1827
- if (!("statement" in entry)) {
1828
- return Object.freeze({ kind: "functionality", statement: undefined, canonical, facts })
1829
- }
1830
- return Object.freeze({ kind: "functionality", statement: entry.statement, canonical, facts })
1831
- }
1832
- if (entry.kind === "capacity") {
1833
- if (wire.kind !== "capacity") {
1834
- throw errors.new(`bumbledb violation ${wire.statementId} is a capacity slot without a measure`)
1835
- }
1836
- return Object.freeze({
1837
- kind: "capacity",
1838
- statement: entry.statement,
1839
- canonical,
1840
- measure: wire.measure,
1841
- facts
1842
- })
1843
- }
1844
- if (wire.kind !== "containment") {
1845
- throw errors.new(`bumbledb violation ${wire.statementId} is a containment slot without a direction`)
1846
- }
1847
- if (entry.kind === "mirrors") {
1848
- return Object.freeze({
1849
- kind: "containment",
1850
- statement: entry.statement,
1851
- canonical,
1852
- direction: wire.direction,
1853
- orientation: entry.orientation,
1854
- facts
1381
+ const facts = Object.freeze(
1382
+ wire.facts.map(function offending(fact) {
1383
+ const member = theory.relations[fact.relation]
1384
+ if (member === undefined || !(fact.relation in theory.relations)) {
1385
+ throw errors.new(`bumbledb violation cites unknown relation ${fact.relation}`)
1386
+ }
1387
+ return decodeOffendingFact<Rels>(member, fact.relation as keyof Rels & string, fact)
1855
1388
  })
1856
- }
1857
- return Object.freeze({
1858
- kind: "containment",
1859
- statement: entry.statement,
1860
- canonical,
1861
- direction: wire.direction,
1862
- facts
1863
- })
1389
+ )
1390
+ return violationFromEntry(entry, wire, facts)
1864
1391
  }
1865
1392
 
1866
- async function openStore<Rels extends SchemaRelations>(
1867
- storePath: string,
1868
- theory: Schema<Rels>
1869
- ): Promise<Db<Rels>> {
1393
+ async function openStore<Rels extends SchemaRelations>(storePath: string, theory: Schema<Rels>): Promise<Db<Rels>> {
1870
1394
  const canonical = path.resolve(storePath)
1871
1395
  const spec = lower(theory)
1872
1396
  const opened = await bridgedAsync(`open bumbledb store at ${canonical}`, function callBridge() {
@@ -1882,7 +1406,9 @@ interface OwnedInstance<Rels extends SchemaRelations> extends Disposable {
1882
1406
  prepare<Row, Params extends ParamsRecord>(q: Query<Rels, Row, Params>): Prepared<Rels, Row, Params>
1883
1407
  execute<Row, Params extends ParamsRecord>(prepared: Prepared<Rels, Row, Params>, params: Params): Row[]
1884
1408
  scan<R extends MemberRelation<Rels>>(relation: R): Fact<R>[]
1409
+ count<R extends MemberRelation<Rels>>(relation: R): bigint
1885
1410
  contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean
1411
+
1886
1412
  get<R extends MemberRelation<Rels>>(relation: R, key: KeyFact<R>): Fact<R> | undefined
1887
1413
  get<R extends MemberRelation<Rels>, const P extends readonly string[]>(
1888
1414
  relation: R,
@@ -1896,6 +1422,7 @@ interface InstanceBuilder<Rels extends SchemaRelations> extends Disposable {
1896
1422
  delete<R extends MemberRelation<Rels>>(relation: R, facts: Iterable<Fact<R>>): MutationReport
1897
1423
  reserve<R extends MemberRelation<Rels>>(relation: R, field: FreshKeys<R> & string, count: bigint): FreshRange
1898
1424
  contains<R extends MemberRelation<Rels>>(relation: R, fact: Fact<R>): boolean
1425
+
1899
1426
  get<R extends MemberRelation<Rels>>(relation: R, key: KeyFact<R>): Fact<R> | undefined
1900
1427
  get<R extends MemberRelation<Rels>, const P extends readonly string[]>(
1901
1428
  relation: R,
@@ -1939,6 +1466,9 @@ function wrapOwned<Rels extends SchemaRelations>(nativeHandle: OwnedHandle, theo
1939
1466
  scan(relationId) {
1940
1467
  return native.ownedScan(nativeHandle, relationId)
1941
1468
  },
1469
+ count(relationId) {
1470
+ return native.ownedCount(nativeHandle, relationId)
1471
+ },
1942
1472
  contains(relationId, values) {
1943
1473
  return native.ownedContains(nativeHandle, relationId, values)
1944
1474
  },
@@ -2003,34 +1533,22 @@ function wrapBuilder<Rels extends SchemaRelations>(
2003
1533
  load<R extends MemberRelation<Rels>>(relation: R, facts: CollectionWrite<R>): MutationReport {
2004
1534
  assertLive()
2005
1535
  const entry = ordinaryEntry(tables, theory, relation)
2006
- return mutateCollection(
2007
- relation,
2008
- facts,
2009
- function applyRows(rows) {
2010
- return bridged("bumbledb builder load", function loadRows() {
2011
- return native.instanceBuilderLoad(nativeHandle, entry.id, rows)
2012
- })
2013
- },
2014
- function applyColumns(columns) {
2015
- return bridged("bumbledb builder load", function loadColumns() {
2016
- return native.instanceBuilderLoadColumns(nativeHandle, entry.id, columns)
2017
- })
2018
- }
2019
- )
1536
+ return mutateCollection(relation, facts, function applyCells(rows, cells) {
1537
+ return bridged("bumbledb builder load", function loadCells() {
1538
+ return native.instanceBuilderLoad(nativeHandle, entry.id, rows, cells)
1539
+ })
1540
+ })
2020
1541
  },
2021
1542
  delete<R extends MemberRelation<Rels>>(relation: R, facts: Iterable<Fact<R>>): MutationReport {
2022
1543
  assertLive()
2023
1544
  const entry = ordinaryEntry(tables, theory, relation)
1545
+ const flat = rowsOf(relation, facts)
2024
1546
  const report = bridged("bumbledb builder delete", function remove() {
2025
- return native.instanceBuilderDelete(nativeHandle, entry.id, rowsOf(relation, facts))
1547
+ return native.instanceBuilderDelete(nativeHandle, entry.id, flat.rows, flat.cells)
2026
1548
  })
2027
1549
  return Object.freeze({ submitted: report.submitted, changed: report.changed })
2028
1550
  },
2029
- reserve<R extends MemberRelation<Rels>>(
2030
- relation: R,
2031
- field: FreshKeys<R> & string,
2032
- count: bigint
2033
- ): FreshRange {
1551
+ reserve<R extends MemberRelation<Rels>>(relation: R, field: FreshKeys<R> & string, count: bigint): FreshRange {
2034
1552
  assertLive()
2035
1553
  const entry = ordinaryEntry(tables, theory, relation)
2036
1554
  const declared = relation.data.fields.find(function byName(candidate) {
@@ -2113,18 +1631,13 @@ const InstanceBuilder = Object.freeze({
2113
1631
  * hold the `Db` this process opened.
2114
1632
  */
2115
1633
  const Db = Object.freeze({
2116
- /** Creates a fresh durable store at `path` from the schema. */
2117
1634
  async create<Rels extends SchemaRelations>(
2118
1635
  storePath: string,
2119
1636
  theory: Schema<Rels>
2120
1637
  ): Promise<Admission<Rels, Db<Rels>>> {
2121
1638
  return createStore(storePath, theory)
2122
1639
  },
2123
- /**
2124
- * Opens an existing durable store at `path` with the same theory.
2125
- * Format 8 open never back-fills a descriptor. A second open of a
2126
- * still-live path is `EnvironmentLocked`.
2127
- */
1640
+
2128
1641
  async open<Rels extends SchemaRelations>(storePath: string, theory: Schema<Rels>): Promise<Db<Rels>> {
2129
1642
  return openStore(storePath, theory)
2130
1643
  },
@@ -2152,7 +1665,6 @@ export type {
2152
1665
  AbandonedArm,
2153
1666
  Admission,
2154
1667
  CapacityViolation,
2155
- ColumnBatch,
2156
1668
  Committed,
2157
1669
  ContainmentViolation,
2158
1670
  DeclaredKeyFact,
@@ -2168,7 +1680,6 @@ export type {
2168
1680
  Prepared,
2169
1681
  ReadInstance,
2170
1682
  SyncResult,
2171
- Tx,
2172
1683
  Violation,
2173
1684
  Witness,
2174
1685
  WriteFromOutcome,
@@ -2179,9 +1690,9 @@ export {
2179
1690
  abandon,
2180
1691
  Db,
2181
1692
  ErrAsyncCallback,
1693
+ ErrFingerprintMismatch,
2182
1694
  ErrForeignPrepared,
2183
1695
  ErrForeignWitness,
2184
- ErrFingerprintMismatch,
2185
1696
  ErrIrError,
2186
1697
  ErrNewtypeMismatch,
2187
1698
  ErrSchemaError,