@bjornpagen/bumbledb 1.3.0 → 1.3.1

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 (86) hide show
  1. package/COOKBOOK.md +72 -19
  2. package/README.md +9 -6
  3. package/dist/changes.d.ts +0 -1
  4. package/dist/changes.d.ts.map +1 -1
  5. package/dist/changes.js +2 -8
  6. package/dist/changes.js.map +1 -1
  7. package/dist/codec.d.ts +8 -1
  8. package/dist/codec.d.ts.map +1 -1
  9. package/dist/codec.js +29 -2
  10. package/dist/codec.js.map +1 -1
  11. package/dist/db-native.d.ts +2 -9
  12. package/dist/db-native.d.ts.map +1 -1
  13. package/dist/db-native.js.map +1 -1
  14. package/dist/index.d.ts +2 -3
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/internal/log.d.ts +4 -4
  19. package/dist/internal/log.d.ts.map +1 -1
  20. package/dist/internal/log.js +2 -2
  21. package/dist/internal/log.js.map +1 -1
  22. package/dist/native.d.ts +9 -9
  23. package/dist/native.d.ts.map +1 -1
  24. package/dist/query/compute.d.ts +3 -3
  25. package/dist/query/compute.d.ts.map +1 -1
  26. package/dist/query/compute.js +3 -12
  27. package/dist/query/compute.js.map +1 -1
  28. package/dist/query/description.js +4 -4
  29. package/dist/query/description.js.map +1 -1
  30. package/dist/query/lower.d.ts +11 -2
  31. package/dist/query/lower.d.ts.map +1 -1
  32. package/dist/query/lower.js +31 -26
  33. package/dist/query/lower.js.map +1 -1
  34. package/dist/query/parse-ir.d.ts.map +1 -1
  35. package/dist/query/parse-ir.js +4 -4
  36. package/dist/query/parse-ir.js.map +1 -1
  37. package/dist/query/scope.d.ts.map +1 -1
  38. package/dist/query/scope.js +6 -1
  39. package/dist/query/scope.js.map +1 -1
  40. package/dist/runtime-codes.d.ts +1 -1
  41. package/dist/runtime-codes.d.ts.map +1 -1
  42. package/dist/runtime-codes.js +1 -0
  43. package/dist/runtime-codes.js.map +1 -1
  44. package/dist/runtime-errors.d.ts +23 -3
  45. package/dist/runtime-errors.d.ts.map +1 -1
  46. package/dist/runtime-errors.js +16 -3
  47. package/dist/runtime-errors.js.map +1 -1
  48. package/dist/runtime-native.d.ts +3 -8
  49. package/dist/runtime-native.d.ts.map +1 -1
  50. package/dist/runtime-native.js.map +1 -1
  51. package/dist/runtime.d.ts +0 -23
  52. package/dist/runtime.d.ts.map +1 -1
  53. package/dist/runtime.js +0 -36
  54. package/dist/runtime.js.map +1 -1
  55. package/dist/scalar.d.ts +34 -104
  56. package/dist/scalar.d.ts.map +1 -1
  57. package/dist/scalar.js +12 -177
  58. package/dist/scalar.js.map +1 -1
  59. package/dist/schema-file.d.ts +7 -0
  60. package/dist/schema-file.d.ts.map +1 -0
  61. package/dist/schema-file.js +24 -0
  62. package/dist/schema-file.js.map +1 -0
  63. package/pack-provenance.json +4 -4
  64. package/package.json +4 -4
  65. package/src/changes.ts +2 -8
  66. package/src/codec.ts +43 -3
  67. package/src/db-native.ts +2 -10
  68. package/src/index.ts +11 -5
  69. package/src/internal/log.ts +2 -4
  70. package/src/native.ts +7 -7
  71. package/src/query/compute.ts +6 -15
  72. package/src/query/description.ts +4 -4
  73. package/src/query/lower.ts +56 -25
  74. package/src/query/parse-ir.ts +6 -4
  75. package/src/query/scope.ts +8 -1
  76. package/src/runtime-codes.ts +1 -0
  77. package/src/runtime-errors.ts +16 -3
  78. package/src/runtime-native.ts +1 -9
  79. package/src/runtime.ts +1 -70
  80. package/src/scalar.ts +49 -329
  81. package/src/schema-file.ts +38 -0
  82. package/dist/migration.d.ts +0 -31
  83. package/dist/migration.d.ts.map +0 -1
  84. package/dist/migration.js +0 -47
  85. package/dist/migration.js.map +0 -1
  86. package/src/migration.ts +0 -61
package/src/native.ts CHANGED
@@ -82,7 +82,7 @@ type HeadOpIr = "sum" | "mean" | "min" | "max" | "count" | "pack"
82
82
 
83
83
  /**
84
84
  * The computed-find scalar expression (C05 `FindTerm::Compute(ScalarExpr)`):
85
- * exactly the frozen core roster, spelled as the migration plan JSON spells
85
+ * exactly the frozen core roster, spelled as the canonical scalar JSON spells
86
86
  * the same expressions (one grammar, no second evaluator). `var` binds a
87
87
  * rule variable ordinal on this lane.
88
88
  */
@@ -294,7 +294,7 @@ type LogAccess =
294
294
  readonly operation: string
295
295
  readonly intent:
296
296
  | { readonly kind: "erasure" }
297
- | { readonly kind: "migration"; readonly planSetDigest: Uint8Array; readonly target: string }
297
+ | { readonly kind: "transition"; readonly contractDigest: Uint8Array; readonly target: string }
298
298
  }
299
299
 
300
300
  type LogLifecycle =
@@ -311,10 +311,10 @@ type LogLifecycle =
311
311
  readonly reason:
312
312
  | { readonly kind: "erasure" }
313
313
  | {
314
- readonly kind: "migrationAborted"
314
+ readonly kind: "transitionAborted"
315
315
  readonly sourceDatabase: string
316
316
  readonly sourceIncarnation: string
317
- readonly planSetDigest: Uint8Array
317
+ readonly contractDigest: Uint8Array
318
318
  }
319
319
  }
320
320
 
@@ -327,7 +327,7 @@ type LogActivation =
327
327
  readonly cause:
328
328
  | { readonly kind: "create" }
329
329
  | { readonly kind: "restore" }
330
- | { readonly kind: "migration"; readonly planSetDigest: Uint8Array }
330
+ | { readonly kind: "transition"; readonly contractDigest: Uint8Array }
331
331
  }
332
332
 
333
333
  interface LogAuthority {
@@ -341,10 +341,10 @@ type LogGenesisProvenance =
341
341
  | { readonly kind: "create" }
342
342
  | { readonly kind: "restore"; readonly sourceEvidence: Uint8Array }
343
343
  | {
344
- readonly kind: "migration"
344
+ readonly kind: "transition"
345
345
  readonly sourceDatabase: string
346
346
  readonly sourceIncarnation: string
347
- readonly planSetDigest: Uint8Array
347
+ readonly contractDigest: Uint8Array
348
348
  }
349
349
 
350
350
  interface LogGenesis {
@@ -1,13 +1,5 @@
1
- /**
2
- * Query-scoped scalar expressions (C05 `FindTerm::Compute`): the
3
- * variable-addressed leaf scope over the ONE shared operator roster in
4
- * `#scalar.ts`. Leaves are bound query variables that lower to `VarId`s.
5
- * Operators are the shared constructors — this module only admits known
6
- * schema kinds at query-var leaves.
7
- *
8
- * `ComputeExpr<K>` is a `ScalarNode<"query-var", K>`. Distinct I64 and U64
9
- * survive host inference; they do not collapse to plain `bigint`.
10
- */
1
+ /** Query scalar expressions use the same nodes that native preparation reads.
2
+ * Distinct i64 and u64 kinds survive TypeScript's shared bigint carrier. */
11
3
  import { AuthoringError } from "#errors.ts"
12
4
  import type { AnyField } from "#fields.ts"
13
5
  import { bool as boolField, f64 as f64Field, i64 as i64Field, rosterOf, u64 as u64Field } from "#fields.ts"
@@ -32,13 +24,13 @@ import {
32
24
  scalarNegate
33
25
  } from "#scalar.ts"
34
26
 
35
- /** Query-var tree: the shared AST restricted to this leaf scope. */
36
- type QueryNode = ScalarNode<"query-var">
27
+ /** Query expression, including interval leaves consumed by measure. */
28
+ type QueryNode = ScalarNode
37
29
 
38
30
  /** Host value for a derived query compute kind. */
39
31
  type ComputeValue<K extends ScalarKind> = K extends "f64" ? number : K extends "bool" ? boolean : bigint
40
32
 
41
- type ComputeExpr<K extends ScalarKind> = ScalarNode<"query-var", K>
33
+ type ComputeExpr<K extends ScalarKind> = ScalarNode<K>
42
34
 
43
35
  type AnyComputeExpr = ComputeExpr<"u64"> | ComputeExpr<"i64"> | ComputeExpr<"f64"> | ComputeExpr<"bool">
44
36
 
@@ -65,7 +57,6 @@ type OperandKind<O> =
65
57
  function isComputeExpr(value: unknown): value is AnyComputeExpr {
66
58
  return (
67
59
  isScalarNode(value) &&
68
- value.scope === "query-var" &&
69
60
  (value.result === "u64" || value.result === "i64" || value.result === "f64" || value.result === "bool")
70
61
  )
71
62
  }
@@ -99,7 +90,7 @@ function asQueryNode(where: string, operand: Operand): QueryNode {
99
90
  }
100
91
 
101
92
  function literal<K extends ScalarKind>(value: ScalarLiteral): ComputeExpr<K> {
102
- return scalarLiteral("query-var", value) as ComputeExpr<K>
93
+ return scalarLiteral(value) as ComputeExpr<K>
103
94
  }
104
95
 
105
96
  function u64(value: bigint): ComputeExpr<"u64"> {
@@ -269,13 +269,13 @@ function replayRule(
269
269
  const value = input.value
270
270
  switch (value.kind) {
271
271
  case "u64":
272
- return scalarLiteral("query-var", { u64: value.value })
272
+ return scalarLiteral({ u64: value.value })
273
273
  case "i64":
274
- return scalarLiteral("query-var", { i64: value.value })
274
+ return scalarLiteral({ i64: value.value })
275
275
  case "f64":
276
- return scalarLiteral("query-var", { f64: value.value })
276
+ return scalarLiteral({ f64: value.value })
277
277
  case "bool":
278
- return scalarLiteral("query-var", { bool: value.value })
278
+ return scalarLiteral({ bool: value.value })
279
279
  default:
280
280
  return refused("query compute", "requires a scalar literal")
281
281
  }
@@ -13,6 +13,7 @@ import {
13
13
  u64 as u64Field
14
14
  } from "#fields.ts"
15
15
  import { snapshotData } from "#immutable.ts"
16
+ import type { Same } from "#judgment.ts"
16
17
  import type { ClassRecordOf, SchemaClasses } from "#law.ts"
17
18
  import type {
18
19
  AtomIr,
@@ -103,6 +104,25 @@ type HeadShape = Readonly<Record<string, ClassedField>> | undefined
103
104
 
104
105
  type HeadOf<T> = InferredOf<T> extends { readonly head: infer H extends HeadShape } ? H : HeadShape
105
106
 
107
+ type HeadFieldMeet<A extends AnyField, B extends AnyField> = A extends IntervalField
108
+ ? B extends IntervalField
109
+ ? IntervalField<A["element"], Same<A["width"], B["width"]> extends true ? A["width"] : undefined>
110
+ : A
111
+ : A
112
+
113
+ /** The common refinements of the same output column across union arms. */
114
+ type HeadMeet<A extends HeadShape, B extends HeadShape> =
115
+ A extends Readonly<Record<string, ClassedField>>
116
+ ? B extends Readonly<Record<string, ClassedField>>
117
+ ? {
118
+ readonly [K in keyof A & keyof B]: {
119
+ readonly field: HeadFieldMeet<A[K]["field"], B[K]["field"]>
120
+ readonly class: Same<A[K]["class"], B[K]["class"]> extends true ? A[K]["class"] : undefined
121
+ }
122
+ }
123
+ : HeadShape
124
+ : HeadShape
125
+
106
126
  interface RuleValue<Row, P extends ParamsRecord, Head extends HeadShape = undefined> {
107
127
  readonly rule: RuleData
108
128
  readonly [inferred]?: { readonly row: Row; readonly params: P; readonly head: Head }
@@ -412,7 +432,7 @@ interface Query<
412
432
 
413
433
  rule<RV extends AnyRuleValue>(
414
434
  build: (r: QueryRuleScope<Rels, Classes>) => RV
415
- ): Query<Rels, Row | RowOf<RV>, Flatten<Params & ParamsOf<RV>>, Classes, Head | HeadOf<RV>>
435
+ ): Query<Rels, Row | RowOf<RV>, Flatten<Params & ParamsOf<RV>>, Classes, HeadMeet<Head, HeadOf<RV>>>
416
436
 
417
437
  /** A diagnostic name for composition/tracing — never a schema relation. */
418
438
  named(label: string): Query<Rels, Row, Params, Classes, Head>
@@ -1542,20 +1562,8 @@ function renderClosedSlice(closed: AnyClosedRoster | undefined): string {
1542
1562
  return closed === undefined ? "a bare value" : `a ${closed.name} reference`
1543
1563
  }
1544
1564
 
1545
- function headSignature(column: FindColumn): string {
1546
- const entry = column.entry
1547
- if (entry.kind === "segments") return `${column.name}:compute`
1548
- if (entry.kind === "var") {
1549
- return `${column.name}:var`
1550
- }
1551
- if (entry.kind === "compute") {
1552
- return `${column.name}:compute`
1553
- }
1554
- const agg = entry.agg
1555
- if (agg.op === "fold") {
1556
- return `${column.name}:${agg.fold}`
1557
- }
1558
- return `${column.name}:${agg.op}`
1565
+ function headOperation(column: FindColumn): HeadOpIr | undefined {
1566
+ return column.entry.kind === "aggregate" ? headOpOf(column.entry.agg) : undefined
1559
1567
  }
1560
1568
 
1561
1569
  function renderParamAnchor(roster: AnyClosedRoster | undefined): string {
@@ -1692,12 +1700,22 @@ function alignedHeadOf(label: string, rules: readonly RuleData[]): readonly Find
1692
1700
  if (first === undefined) {
1693
1701
  throw new AuthoringError({ message: `${label} needs at least one rule` })
1694
1702
  }
1695
- const signature = first.finds.map(headSignature).join(", ")
1703
+ // Bare u64 admits either carrier, but does not make two different
1704
+ // carriers compatible. Retain one original classed slot per column;
1705
+ // checking only the first arm (or its weakened meet) loses that proof.
1706
+ const classed = new Map<number, { readonly slot: ClassedField; readonly rule: number }>()
1696
1707
  rules.forEach(function verifyHead(rule, index) {
1697
- const candidate = rule.finds.map(headSignature).join(", ")
1698
- if (candidate !== signature) {
1708
+ if (
1709
+ rule.finds.length !== first.finds.length ||
1710
+ rule.finds.some((column, position) => {
1711
+ const lead = first.finds[position]
1712
+ return lead === undefined || lead.name !== column.name || headOperation(lead) !== headOperation(column)
1713
+ })
1714
+ ) {
1715
+ const render = (columns: readonly FindColumn[]) =>
1716
+ columns.map((column) => `${column.name}:${headOperation(column) ?? "project"}`).join(", ")
1699
1717
  throw new AuthoringError({
1700
- message: `every rule of ${label} derives the same head — rule 0 finds (${signature}), rule ${index} finds (${candidate})`
1718
+ message: `every rule of ${label} derives the same head — rule 0 finds (${render(first.finds)}), rule ${index} finds (${render(rule.finds)})`
1701
1719
  })
1702
1720
  }
1703
1721
  rule.finds.forEach(function verifyClosedSlice(column, position) {
@@ -1710,30 +1728,44 @@ function alignedHeadOf(label: string, rules: readonly RuleData[]): readonly Find
1710
1728
  if (lead === undefined) {
1711
1729
  return
1712
1730
  }
1713
- if (lead.slot !== undefined && column.slot !== undefined && !headFieldJoins(lead.slot, column.slot)) {
1731
+ const prior = classed.get(position) ?? (lead.slot === undefined ? undefined : { slot: lead.slot, rule: 0 })
1732
+ if (prior !== undefined && column.slot !== undefined && !headFieldJoins(prior.slot, column.slot)) {
1714
1733
  throw new AuthoringError({
1715
- message: `every rule of ${label} derives the same head — the head column ${lead.name} is bound at ${renderFieldKind(lead.slot)} in rule 0 but at ${renderFieldKind(column.slot)} in rule ${index} (a head column joins class-equal slots; same-wire u64 admits a bare side, and the merged head is bare)`
1734
+ message: `every rule of ${label} derives the same head — the head column ${lead.name} is bound at ${renderFieldKind(prior.slot)} in rule ${prior.rule} but at ${renderFieldKind(column.slot)} in rule ${index} (a head column joins class-equal slots; same-wire u64 admits a bare side, and the merged head is bare)`
1716
1735
  })
1717
1736
  }
1737
+ if (column.slot?.class !== undefined && !classed.has(position)) {
1738
+ classed.set(position, { slot: column.slot, rule: index })
1739
+ }
1718
1740
  })
1719
1741
  })
1720
1742
  const merged = first.finds.map(function meetColumn(lead, position) {
1721
1743
  const slot = lead.slot
1722
- if (slot === undefined || slot.class === undefined) {
1744
+ if (slot === undefined) {
1723
1745
  return lead
1724
1746
  }
1725
1747
  const demoted = rules.some(function bareElsewhere(rule) {
1726
1748
  const column = rule.finds[position]
1727
1749
  return column !== undefined && column.slot !== undefined && column.slot.class !== slot.class
1728
1750
  })
1729
- if (!demoted) {
1751
+ const field = slot.field
1752
+ const widened =
1753
+ field.kind === "interval" &&
1754
+ rules.some((rule) => {
1755
+ const other = rule.finds[position]?.slot?.field
1756
+ return other?.kind === "interval" && other.width !== field.width
1757
+ })
1758
+ if (!demoted && !widened) {
1730
1759
  return lead
1731
1760
  }
1732
1761
  return Object.freeze({
1733
1762
  name: lead.name,
1734
1763
  entry: lead.entry,
1735
1764
  closed: lead.closed,
1736
- slot: Object.freeze({ field: slot.field, class: undefined })
1765
+ slot: Object.freeze({
1766
+ field: widened ? Object.freeze({ ...field, width: undefined }) : field,
1767
+ class: demoted ? undefined : slot.class
1768
+ })
1737
1769
  })
1738
1770
  })
1739
1771
  return Object.freeze(merged)
@@ -2211,7 +2243,6 @@ function lowerComputeNode(node: QueryNode, ids: VarIds): ScalarExprIr {
2211
2243
 
2212
2244
  function lowerComputeGrammar(node: QueryNode, ids: VarIds): ScalarExprIr {
2213
2245
  return scalarWire(node, (leaf) => {
2214
- if (leaf.kind !== "var") throw new AuthoringError({ message: "query expressions require bound variables" })
2215
2246
  return ids.of(leaf.ref as AnyVar)
2216
2247
  })
2217
2248
  }
@@ -231,9 +231,8 @@ function align(context: string, head: readonly HeadTermIr[], rules: readonly Rul
231
231
  if (rule.finds.length !== head.length) fail(`${context}.rules[${index}]`, "finds width does not match head width")
232
232
  for (const [position, find] of rule.finds.entries()) {
233
233
  const term = head[position]
234
- const plainFamily = find.kind === "var" || find.kind === "compute" ? find.kind : "aggregate"
235
- const family = find.kind === "segments" ? "compute" : plainFamily
236
- if (term?.kind !== family)
234
+ const projects = find.kind === "var" || find.kind === "compute" || find.kind === "segments"
235
+ if (term === undefined || (term.kind === "aggregate") === projects)
237
236
  fail(`${context}.rules[${index}].finds[${position}]`, "find family does not match head")
238
237
  if (term.kind === "aggregate") {
239
238
  const op = find.kind === "aggregate" ? find.op.kind : find.kind
@@ -280,7 +279,10 @@ function parseQueryIr(input: unknown): ParsedQuery {
280
279
  if (rec.length === 0) return fail("parseQueryIr", "rec step is empty")
281
280
  align("rec base", headRec, base)
282
281
  align("rec step", headRec, rec)
283
- if (headRec.some((term) => term.kind !== "var") || [...base, ...rec].some((rule) => rule.negated.length !== 0))
282
+ if (
283
+ headRec.some((term) => term.kind !== "var") ||
284
+ [...base, ...rec].some((rule) => rule.negated.length !== 0 || rule.finds.some((find) => find.kind !== "var"))
285
+ )
284
286
  return fail("query.rec", "recursion requires projection-only heads and no negation")
285
287
  owned = { kind: raw.kind, interiors, head, rules, rec: Object.freeze({ head: headRec, base, rec }) }
286
288
  }
@@ -369,7 +369,14 @@ function fieldAntiJoins(a: ClassedField, b: ClassedField): boolean {
369
369
  * joins cannot inherit provenance a rule never proved.
370
370
  */
371
371
  function headFieldJoins(a: ClassedField, b: ClassedField): boolean {
372
- return fieldJoins(a, b) || u64BareWirePair(a, b)
372
+ return (
373
+ fieldJoins(a, b) ||
374
+ u64BareWirePair(a, b) ||
375
+ (a.class === b.class &&
376
+ a.field.kind === "interval" &&
377
+ b.field.kind === "interval" &&
378
+ a.field.element === b.field.element)
379
+ )
373
380
  }
374
381
 
375
382
  function renderFieldKind(slot: ClassedField): string {
@@ -3,6 +3,7 @@ export const runtimeErrorCodes = [
3
3
  "RuntimeAlreadyLive",
4
4
  "ForeignRuntime",
5
5
  "ClosedHandle",
6
+ "HandleBusy",
6
7
  "SpentHandle",
7
8
  "QueueFull",
8
9
  "InvalidArgument",
@@ -32,11 +32,18 @@ const Io = Schema.Struct({
32
32
  kind: Schema.String,
33
33
  osCode: Schema.optional(Schema.Number)
34
34
  })
35
- /** A typed engine refusal crossing the executor: core family tag + message. */
35
+ const StatementDiagnostic = Schema.Struct({ id: Schema.Number, descriptor: Schema.String })
36
+ const SchemaDiagnostic = Schema.Struct({
37
+ statement: StatementDiagnostic,
38
+ conflict: Schema.optional(StatementDiagnostic)
39
+ })
40
+
41
+ /** A typed engine refusal, including native statement coordinates when known. */
36
42
  const Engine = Schema.Struct({
37
43
  _tag: Schema.Literal("Engine"),
38
44
  kind: Schema.String,
39
- message: Schema.String
45
+ message: Schema.String,
46
+ diagnostic: Schema.optional(SchemaDiagnostic)
40
47
  })
41
48
  export const DbReason = Schema.Union([ResourceLimit, Io, Engine, InvalidArgument, PlainReason])
42
49
  export class DbError extends Schema.TaggedError<DbError>()("DbError", {
@@ -68,7 +75,13 @@ export function dbError(operation: string, cause: unknown): DbError {
68
75
  "message" in cause &&
69
76
  typeof cause.message === "string"
70
77
  ) {
71
- return new DbError({ operation, reason: { _tag: "Engine", kind: cause.kind, message: cause.message } })
78
+ const reason = decodeReason({
79
+ _tag: "Engine",
80
+ kind: cause.kind,
81
+ message: cause.message,
82
+ diagnostic: "diagnostic" in cause ? cause.diagnostic : undefined
83
+ })
84
+ if (reason._tag === "Some") return new DbError({ operation, reason: reason.value })
72
85
  }
73
86
  return new DbError({ operation, reason: { _tag: "Internal" } })
74
87
  }
@@ -28,11 +28,6 @@ export interface DirectoryHandle {
28
28
  readonly __directory: unique symbol
29
29
  }
30
30
 
31
- /** Stamped `NativeKind::RepositoryLock` mint — not a directory-owner twin. */
32
- export interface RepositoryLockHandle {
33
- readonly __repositoryLock: unique symbol
34
- }
35
-
36
31
  /** Resource kinds installed in native worker tables. */
37
32
  export type NativeKind = "snapshot" | "prepared" | "result" | "cursor" | "draft" | "changes" | "repository-lock"
38
33
 
@@ -55,6 +50,7 @@ export type ManagedDbOutcome =
55
50
  readonly tag: "refused"
56
51
  readonly kind: "schemaError" | "newtypeMismatch" | "fingerprintMismatch" | "destinationExists"
57
52
  readonly message: string
53
+ readonly diagnostic?: Extract<import("#runtime-errors.ts").DbError["reason"], { _tag: "Engine" }>["diagnostic"]
58
54
  }
59
55
 
60
56
  export type LogTakeWire =
@@ -179,10 +175,6 @@ interface RuntimeNative {
179
175
  callback: () => void
180
176
  ): OperationHandle
181
177
  runtimeLogTake(operation: OperationHandle): LogTakeWire
182
- /** L14 mint: `Runtime::mint_repository_lock` stamps `NativeKind::RepositoryLock` at take. */
183
- logRepositoryLockAcquire(runtime: RuntimeHandle, directory: string, callback: () => void): OperationHandle
184
- logRepositoryLockTake(operation: OperationHandle): RepositoryLockHandle
185
- logRepositoryLockRelease(owner: RepositoryLockHandle, callback: (report: CloseWire) => void): void
186
178
  }
187
179
 
188
180
  // The checked source/fresh-addon roster test pins this private declaration.
package/src/runtime.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Context, Duration, Effect, Exit, Layer } from "effect"
2
2
  import type { CloseReport, OutstandingWork } from "#runtime-errors.ts"
3
3
  import { CloseFailure, DbError, dbError } from "#runtime-errors.ts"
4
- import type { CloseWire, OperationHandle, OptionsWire, RepositoryLockHandle, RuntimeHandle } from "#runtime-native.ts"
4
+ import type { CloseWire, OperationHandle, OptionsWire, RuntimeHandle } from "#runtime-native.ts"
5
5
  import { runtimeNative } from "#runtime-native.ts"
6
6
 
7
7
  export interface NativeRuntimeOptions {
@@ -283,75 +283,6 @@ export const runtimeHandle = Effect.fn("NativeRuntime.handle")(function* () {
283
283
  return handle
284
284
  })
285
285
 
286
- /**
287
- * Opaque kernel fence (C8). Backed by L14 `log_repository_lock_*` and
288
- * L12 `mint_repository_lock` (`NativeKind::RepositoryLock`). Native
289
- * exclusion is the owner — there is no JS occupancy table. `release`
290
- * is the one close: it joins `logRepositoryLockRelease` then clears
291
- * `slot.owner` so the Scope finalizer is a no-op. Callers join host
292
- * I/O first (`joinPendingIo.pipe(Effect.andThen(lock.release))`).
293
- */
294
- export interface RepositoryLock {
295
- readonly directory: string
296
- readonly release: Effect.Effect<void>
297
- }
298
-
299
- function joinLockRelease(operation: string, owner: RepositoryLockHandle): Effect.Effect<void> {
300
- return drain(operation, (callback) => runtimeNative.logRepositoryLockRelease(owner, callback)).pipe(
301
- Effect.flatMap((report) => finalizeClose(operation, report))
302
- )
303
- }
304
-
305
- /**
306
- * Internal log seam: stamped mint only. Cleanup is registered before
307
- * the interruptible acquire: `acquireRelease({ interruptible })`
308
- * installs the finalizer only after acquire succeeds. Interrupt, defect,
309
- * and acquire failure all run the same slot finalizer — empty slot is
310
- * a no-op. No `runtimeDirectoryAcquire`, no core `Db`, no JS bookkeeping.
311
- */
312
- export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositoryLock")(function* (
313
- operation: string,
314
- directory: string
315
- ) {
316
- if (directory.length === 0) {
317
- return yield* Effect.fail(invalid(operation))
318
- }
319
- const runtime = yield* runtimeHandle()
320
- const closeOp = `${operation}.repositoryLock`
321
- return yield* Effect.uninterruptibleMask((restore) =>
322
- Effect.gen(function* () {
323
- const slot: { owner: RepositoryLockHandle | undefined } = { owner: undefined }
324
- const release = Effect.suspend(() => {
325
- const held = slot.owner
326
- if (held === undefined) {
327
- return Effect.void
328
- }
329
- return joinLockRelease(closeOp, held).pipe(
330
- Effect.ensuring(
331
- Effect.sync(() => {
332
- slot.owner = undefined
333
- })
334
- )
335
- )
336
- })
337
- yield* Effect.addFinalizer(() => release)
338
- const owner = yield* restore(
339
- nativeOperationWith(
340
- operation,
341
- (callback) => runtimeNative.logRepositoryLockAcquire(runtime, directory, callback),
342
- runtimeNative.logRepositoryLockTake,
343
- (value) => value
344
- )
345
- )
346
- slot.owner = owner
347
- return Object.freeze({
348
- directory,
349
- release
350
- })
351
- })
352
- )
353
- })
354
-
355
286
  /** Internal first executor consumer; not a replacement row codec or public hash API. */
356
287
  export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8Array) {
357
288
  const runtime = yield* NativeRuntime