@bjornpagen/bumbledb 1.3.0 → 2.0.0-rc.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 (290) hide show
  1. package/COOKBOOK.md +224 -95
  2. package/README.md +114 -45
  3. package/dist/alternatives.d.ts +5 -6
  4. package/dist/alternatives.d.ts.map +1 -1
  5. package/dist/alternatives.js +5 -6
  6. package/dist/alternatives.js.map +1 -1
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +85 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/capacity.d.ts +14 -17
  12. package/dist/capacity.d.ts.map +1 -1
  13. package/dist/capacity.js +7 -7
  14. package/dist/capacity.js.map +1 -1
  15. package/dist/changes.d.ts +32 -41
  16. package/dist/changes.d.ts.map +1 -1
  17. package/dist/changes.js +86 -110
  18. package/dist/changes.js.map +1 -1
  19. package/dist/closed.d.ts +2 -2
  20. package/dist/closed.d.ts.map +1 -1
  21. package/dist/closed.js +5 -5
  22. package/dist/closed.js.map +1 -1
  23. package/dist/codec.d.ts +20 -27
  24. package/dist/codec.d.ts.map +1 -1
  25. package/dist/codec.js +48 -22
  26. package/dist/codec.js.map +1 -1
  27. package/dist/compile.d.ts +26 -50
  28. package/dist/compile.d.ts.map +1 -1
  29. package/dist/compile.js +75 -64
  30. package/dist/compile.js.map +1 -1
  31. package/dist/database/database.d.ts +98 -0
  32. package/dist/database/database.d.ts.map +1 -0
  33. package/dist/database/database.js +249 -0
  34. package/dist/database/database.js.map +1 -0
  35. package/dist/database/driver.d.ts +40 -0
  36. package/dist/database/driver.d.ts.map +1 -0
  37. package/dist/database/driver.js +68 -0
  38. package/dist/database/driver.js.map +1 -0
  39. package/dist/database/fs.d.ts +12 -0
  40. package/dist/database/fs.d.ts.map +1 -0
  41. package/dist/database/fs.js +120 -0
  42. package/dist/database/fs.js.map +1 -0
  43. package/dist/database/generate.d.ts +26 -0
  44. package/dist/database/generate.d.ts.map +1 -0
  45. package/dist/database/generate.js +102 -0
  46. package/dist/database/generate.js.map +1 -0
  47. package/dist/database/hosted.d.ts +32 -0
  48. package/dist/database/hosted.d.ts.map +1 -0
  49. package/dist/database/hosted.js +44 -0
  50. package/dist/database/hosted.js.map +1 -0
  51. package/dist/database/io.d.ts +94 -0
  52. package/dist/database/io.d.ts.map +1 -0
  53. package/dist/database/io.js +35 -0
  54. package/dist/database/io.js.map +1 -0
  55. package/dist/database/mem.d.ts +26 -0
  56. package/dist/database/mem.d.ts.map +1 -0
  57. package/dist/database/mem.js +67 -0
  58. package/dist/database/mem.js.map +1 -0
  59. package/dist/database/migration.d.ts +55 -0
  60. package/dist/database/migration.d.ts.map +1 -0
  61. package/dist/database/migration.js +46 -0
  62. package/dist/database/migration.js.map +1 -0
  63. package/dist/database/s3.d.ts +36 -0
  64. package/dist/database/s3.d.ts.map +1 -0
  65. package/dist/database/s3.js +141 -0
  66. package/dist/database/s3.js.map +1 -0
  67. package/dist/db.d.ts +44 -128
  68. package/dist/db.d.ts.map +1 -1
  69. package/dist/db.js +163 -287
  70. package/dist/db.js.map +1 -1
  71. package/dist/engine.d.ts +7 -0
  72. package/dist/engine.d.ts.map +1 -0
  73. package/dist/engine.js +2 -0
  74. package/dist/engine.js.map +1 -0
  75. package/dist/errors.d.ts +152 -46
  76. package/dist/errors.d.ts.map +1 -1
  77. package/dist/errors.js +107 -19
  78. package/dist/errors.js.map +1 -1
  79. package/dist/face.d.ts +5 -5
  80. package/dist/face.d.ts.map +1 -1
  81. package/dist/face.js +6 -6
  82. package/dist/face.js.map +1 -1
  83. package/dist/fields.d.ts +4 -5
  84. package/dist/fields.d.ts.map +1 -1
  85. package/dist/fields.js +3 -3
  86. package/dist/fields.js.map +1 -1
  87. package/dist/index.d.ts +60 -58
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +27 -29
  90. package/dist/index.js.map +1 -1
  91. package/dist/judgment.d.ts +1 -1
  92. package/dist/judgment.js +1 -1
  93. package/dist/law.d.ts +16 -68
  94. package/dist/law.d.ts.map +1 -1
  95. package/dist/law.js +3 -3
  96. package/dist/law.js.map +1 -1
  97. package/dist/lower.d.ts +3 -3
  98. package/dist/lower.d.ts.map +1 -1
  99. package/dist/lower.js +35 -39
  100. package/dist/lower.js.map +1 -1
  101. package/dist/native/addon.d.ts +23 -0
  102. package/dist/native/addon.d.ts.map +1 -0
  103. package/dist/native/addon.js +15 -0
  104. package/dist/native/addon.js.map +1 -0
  105. package/dist/native/binding.d.ts +996 -0
  106. package/dist/native/json.d.ts +14 -0
  107. package/dist/native/json.d.ts.map +1 -0
  108. package/dist/native/json.js +73 -0
  109. package/dist/native/json.js.map +1 -0
  110. package/dist/native/load.d.ts +9 -0
  111. package/dist/native/load.d.ts.map +1 -0
  112. package/dist/native/load.js +30 -0
  113. package/dist/native/load.js.map +1 -0
  114. package/dist/native/op.d.ts +29 -0
  115. package/dist/native/op.d.ts.map +1 -0
  116. package/dist/native/op.js +50 -0
  117. package/dist/native/op.js.map +1 -0
  118. package/dist/query/atom.d.ts +13 -14
  119. package/dist/query/atom.d.ts.map +1 -1
  120. package/dist/query/atom.js +2 -2
  121. package/dist/query/atom.js.map +1 -1
  122. package/dist/query/compute.d.ts +7 -7
  123. package/dist/query/compute.d.ts.map +1 -1
  124. package/dist/query/compute.js +7 -16
  125. package/dist/query/compute.js.map +1 -1
  126. package/dist/query/find.d.ts +8 -8
  127. package/dist/query/find.d.ts.map +1 -1
  128. package/dist/query/find.js +1 -1
  129. package/dist/query/lower.d.ts +31 -17
  130. package/dist/query/lower.d.ts.map +1 -1
  131. package/dist/query/lower.js +260 -368
  132. package/dist/query/lower.js.map +1 -1
  133. package/dist/query/run.d.ts +5 -5
  134. package/dist/query/run.d.ts.map +1 -1
  135. package/dist/query/run.js +32 -15
  136. package/dist/query/run.js.map +1 -1
  137. package/dist/query/scope.d.ts +20 -28
  138. package/dist/query/scope.d.ts.map +1 -1
  139. package/dist/query/scope.js +13 -8
  140. package/dist/query/scope.js.map +1 -1
  141. package/dist/query/segments.d.ts +2 -2
  142. package/dist/query/segments.d.ts.map +1 -1
  143. package/dist/query/segments.js +3 -3
  144. package/dist/query/segments.js.map +1 -1
  145. package/dist/relation.d.ts +1 -1
  146. package/dist/relation.d.ts.map +1 -1
  147. package/dist/relation.js +4 -4
  148. package/dist/relation.js.map +1 -1
  149. package/dist/result.d.ts +15 -22
  150. package/dist/result.d.ts.map +1 -1
  151. package/dist/result.js +30 -39
  152. package/dist/result.js.map +1 -1
  153. package/dist/rows.d.ts +9 -33
  154. package/dist/rows.d.ts.map +1 -1
  155. package/dist/rows.js +8 -12
  156. package/dist/rows.js.map +1 -1
  157. package/dist/runtime.d.ts +17 -43
  158. package/dist/runtime.d.ts.map +1 -1
  159. package/dist/runtime.js +41 -241
  160. package/dist/runtime.js.map +1 -1
  161. package/dist/scalar.d.ts +36 -106
  162. package/dist/scalar.d.ts.map +1 -1
  163. package/dist/scalar.js +36 -185
  164. package/dist/scalar.js.map +1 -1
  165. package/dist/schema.d.ts +7 -18
  166. package/dist/schema.d.ts.map +1 -1
  167. package/dist/schema.js +18 -228
  168. package/dist/schema.js.map +1 -1
  169. package/dist/selection.d.ts +4 -4
  170. package/dist/selection.d.ts.map +1 -1
  171. package/dist/selection.js +5 -5
  172. package/dist/selection.js.map +1 -1
  173. package/dist/shape.d.ts +5 -5
  174. package/dist/shape.d.ts.map +1 -1
  175. package/dist/spec.d.ts +7 -79
  176. package/dist/spec.d.ts.map +1 -1
  177. package/dist/spec.js.map +1 -1
  178. package/dist/statements.d.ts +3 -3
  179. package/dist/statements.d.ts.map +1 -1
  180. package/dist/statements.js +10 -10
  181. package/dist/statements.js.map +1 -1
  182. package/dist/uuid.d.ts +1 -1
  183. package/dist/uuid.d.ts.map +1 -1
  184. package/dist/uuid.js +1 -1
  185. package/dist/uuid.js.map +1 -1
  186. package/dist/values.d.ts +2 -2
  187. package/dist/values.d.ts.map +1 -1
  188. package/dist/values.js +2 -2
  189. package/dist/values.js.map +1 -1
  190. package/package.json +18 -18
  191. package/src/alternatives.ts +7 -8
  192. package/src/bin.ts +82 -0
  193. package/src/capacity.ts +20 -23
  194. package/src/changes.ts +134 -197
  195. package/src/closed.ts +5 -5
  196. package/src/codec.ts +77 -51
  197. package/src/compile.ts +91 -113
  198. package/src/database/database.ts +413 -0
  199. package/src/database/driver.ts +134 -0
  200. package/src/database/fs.ts +119 -0
  201. package/src/database/generate.ts +116 -0
  202. package/src/database/hosted.ts +102 -0
  203. package/src/database/io.ts +134 -0
  204. package/src/database/mem.ts +113 -0
  205. package/src/database/migration.ts +107 -0
  206. package/src/database/s3.ts +231 -0
  207. package/src/db.ts +290 -547
  208. package/src/engine.ts +14 -0
  209. package/src/errors.ts +118 -39
  210. package/src/face.ts +9 -9
  211. package/src/fields.ts +6 -7
  212. package/src/index.ts +102 -109
  213. package/src/judgment.ts +1 -1
  214. package/src/law.ts +18 -70
  215. package/src/lower.ts +61 -72
  216. package/src/native/addon.ts +54 -0
  217. package/src/native/binding.d.ts +996 -0
  218. package/src/native/json.ts +100 -0
  219. package/src/native/load.ts +31 -0
  220. package/src/native/op.ts +73 -0
  221. package/src/query/atom.ts +15 -16
  222. package/src/query/compute.ts +13 -22
  223. package/src/query/find.ts +8 -8
  224. package/src/query/lower.ts +323 -414
  225. package/src/query/run.ts +42 -21
  226. package/src/query/scope.ts +37 -38
  227. package/src/query/segments.ts +4 -4
  228. package/src/relation.ts +4 -4
  229. package/src/result.ts +53 -80
  230. package/src/rows.ts +18 -46
  231. package/src/runtime.ts +50 -346
  232. package/src/scalar.ts +76 -336
  233. package/src/schema.ts +23 -284
  234. package/src/selection.ts +6 -6
  235. package/src/shape.ts +5 -5
  236. package/src/spec.ts +7 -85
  237. package/src/statements.ts +11 -11
  238. package/src/uuid.ts +1 -1
  239. package/src/values.ts +4 -4
  240. package/dist/close.d.ts +0 -24
  241. package/dist/close.d.ts.map +0 -1
  242. package/dist/close.js +0 -34
  243. package/dist/close.js.map +0 -1
  244. package/dist/db-native.d.ts +0 -196
  245. package/dist/db-native.d.ts.map +0 -1
  246. package/dist/db-native.js +0 -13
  247. package/dist/db-native.js.map +0 -1
  248. package/dist/internal/log.d.ts +0 -26
  249. package/dist/internal/log.d.ts.map +0 -1
  250. package/dist/internal/log.js +0 -12
  251. package/dist/internal/log.js.map +0 -1
  252. package/dist/migration.d.ts +0 -31
  253. package/dist/migration.d.ts.map +0 -1
  254. package/dist/migration.js +0 -47
  255. package/dist/migration.js.map +0 -1
  256. package/dist/native.d.ts +0 -562
  257. package/dist/native.d.ts.map +0 -1
  258. package/dist/native.js +0 -123
  259. package/dist/native.js.map +0 -1
  260. package/dist/query/description.d.ts +0 -41
  261. package/dist/query/description.d.ts.map +0 -1
  262. package/dist/query/description.js +0 -311
  263. package/dist/query/description.js.map +0 -1
  264. package/dist/query/parse-ir.d.ts +0 -10
  265. package/dist/query/parse-ir.d.ts.map +0 -1
  266. package/dist/query/parse-ir.js +0 -280
  267. package/dist/query/parse-ir.js.map +0 -1
  268. package/dist/runtime-codes.d.ts +0 -2
  269. package/dist/runtime-codes.d.ts.map +0 -1
  270. package/dist/runtime-codes.js +0 -18
  271. package/dist/runtime-codes.js.map +0 -1
  272. package/dist/runtime-errors.d.ts +0 -118
  273. package/dist/runtime-errors.d.ts.map +0 -1
  274. package/dist/runtime-errors.js +0 -98
  275. package/dist/runtime-errors.js.map +0 -1
  276. package/dist/runtime-native.d.ts +0 -141
  277. package/dist/runtime-native.d.ts.map +0 -1
  278. package/dist/runtime-native.js +0 -5
  279. package/dist/runtime-native.js.map +0 -1
  280. package/pack-provenance.json +0 -7
  281. package/src/close.ts +0 -53
  282. package/src/db-native.ts +0 -243
  283. package/src/internal/log.ts +0 -48
  284. package/src/migration.ts +0 -61
  285. package/src/native.ts +0 -736
  286. package/src/query/description.ts +0 -424
  287. package/src/query/parse-ir.ts +0 -291
  288. package/src/runtime-codes.ts +0 -17
  289. package/src/runtime-errors.ts +0 -109
  290. package/src/runtime-native.ts +0 -189
package/src/runtime.ts CHANGED
@@ -1,10 +1,13 @@
1
- import { Context, Duration, Effect, Exit, Layer } from "effect"
2
- import type { CloseReport, OutstandingWork } from "#runtime-errors.ts"
3
- import { CloseFailure, DbError, dbError } from "#runtime-errors.ts"
4
- import type { CloseWire, OperationHandle, OptionsWire, RepositoryLockHandle, RuntimeHandle } from "#runtime-native.ts"
5
- import { runtimeNative } from "#runtime-native.ts"
6
-
7
- export interface NativeRuntimeOptions {
1
+ import { Context, Duration, Effect, Layer } from "effect"
2
+ import type { OutstandingWork } from "./errors.ts"
3
+ import { DbError, dbError } from "./errors.ts"
4
+ import type { RuntimeRef } from "./native/addon.ts"
5
+ import { addon } from "./native/addon.ts"
6
+ import type { RuntimeOptionsIn } from "./native/binding.d.ts"
7
+ import { call, scoped } from "./native/op.ts"
8
+
9
+ /** Sizing for the native runtime. The addon validates every count and picks the defaults. */
10
+ export interface BumbleOptions {
8
11
  readonly workers?: number
9
12
  readonly queueCapacity?: number
10
13
  readonly cleanupCapacity?: number
@@ -13,359 +16,60 @@ export interface NativeRuntimeOptions {
13
16
  readonly cleanupTimeout?: Duration.Input
14
17
  }
15
18
 
16
- interface RuntimeService {
17
- readonly close: () => Effect.Effect<CloseReport>
18
- readonly inspect: () => Effect.Effect<OutstandingWork, DbError>
19
- }
20
-
21
- const owners = new WeakMap<RuntimeService, RuntimeHandle>()
22
-
23
- function invalid(operation: string): DbError {
24
- return new DbError({ operation, reason: { _tag: "InvalidArgument" } })
25
- }
26
-
27
- function count(value: number, operation: string): number {
28
- if (!Number.isSafeInteger(value) || value <= 0 || value > 0xffffffff) throw invalid(operation)
29
- return value
30
- }
31
-
32
- function millis(value: Duration.Input, operation: string): number {
33
- const duration = Duration.toMillis(value)
34
- if (!Number.isFinite(duration) || duration < 0 || duration > 0xffffffff) throw invalid(operation)
35
- return Math.ceil(duration)
36
- }
37
-
38
- function options(value: NativeRuntimeOptions): OptionsWire {
39
- const operation = "NativeRuntime.acquire"
40
- return {
41
- workers: value.workers === undefined ? undefined : count(value.workers, operation),
42
- queueCapacity: value.queueCapacity === undefined ? undefined : count(value.queueCapacity, operation),
43
- cleanupCapacity: value.cleanupCapacity === undefined ? undefined : count(value.cleanupCapacity, operation),
44
- ownerCapacity: value.ownerCapacity === undefined ? undefined : count(value.ownerCapacity, operation),
45
- nativeHandleCapacity:
46
- value.nativeHandleCapacity === undefined ? undefined : count(value.nativeHandleCapacity, operation),
47
- cleanupTimeoutMs:
48
- value.cleanupTimeout === undefined ? undefined : count(millis(value.cleanupTimeout, operation), operation)
19
+ function wire(options: BumbleOptions): string {
20
+ const timeout = options.cleanupTimeout === undefined ? undefined : Duration.toMillis(options.cleanupTimeout)
21
+ if (timeout !== undefined && !Number.isFinite(timeout)) {
22
+ throw new DbError({ operation: "Bumble.layer", reason: { _tag: "InvalidArgument" } })
49
23
  }
50
- }
51
-
52
- function failure(operation: string, cause: unknown): DbError {
53
- return cause instanceof DbError ? cause : dbError(operation, cause)
54
- }
55
-
56
- function closeReport(operation: string, report: CloseWire): CloseReport {
57
- // A payload-less native "failed" drain (cleanup capacity exhausted, or
58
- // owner teardown failed) decodes as the core `Internal` reason — the
59
- // same mapping the log bridge applies, so one wire arm has one meaning.
60
- // It is deliberately NOT `QueueFull`: a failed drain is never retryable
61
- // submit backpressure.
62
- return report.kind === "failed" ? { kind: "failed", error: dbError(operation, { _tag: "Internal" }) } : report
63
- }
64
-
65
- function drain(operation: string, start: (callback: (report: CloseWire) => void) => void): Effect.Effect<CloseReport> {
66
- return Effect.callback<CloseReport>((resume) => {
67
- try {
68
- start((report) => resume(Effect.succeed(closeReport(operation, report))))
69
- } catch (cause) {
70
- resume(Effect.succeed({ kind: "failed", error: failure(operation, cause) }))
71
- }
72
- }).pipe(Effect.uninterruptible)
73
- }
74
-
75
- export function finalizeClose(operation: string, report: CloseReport): Effect.Effect<void> {
76
- return report.kind === "closed" ? Effect.void : Effect.die(new CloseFailure({ operation, report }))
77
- }
78
-
79
- /**
80
- * Effect.callback's returned finalizer is interruption cleanup only
81
- * (`asyncFinalizer` runs solely on interrupt Cause). If that finalizer
82
- * dies, the original interrupt is dropped. Join the native drain here and
83
- * always succeed so the interrupt Cause is kept; attach CloseFailure via
84
- * `onExit` so both appear in the final Cause.
85
- */
86
- function joinInterruptDrain(
87
- operation: string,
88
- start: (callback: (report: CloseWire) => void) => void,
89
- stash: { report?: CloseReport }
90
- ): Effect.Effect<void> {
91
- return drain(operation, start).pipe(
92
- Effect.tap((report) => {
93
- stash.report = report
94
- return Effect.void
95
- }),
96
- Effect.asVoid
97
- )
98
- }
99
-
100
- function attachInterruptClose(
101
- operation: string,
102
- stash: { report?: CloseReport }
103
- ): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> {
104
- return (effect) =>
105
- effect.pipe(
106
- Effect.onExit((exit) => {
107
- if (!Exit.hasInterrupts(exit)) {
108
- return Effect.void
109
- }
110
- const report = stash.report
111
- if (report === undefined || report.kind === "closed") {
112
- return Effect.void
113
- }
114
- return Effect.die(new CloseFailure({ operation, report }))
115
- })
116
- )
117
- }
118
-
119
- function afterClose<A, E, R>(
120
- operation: string,
121
- report: CloseReport,
122
- effect: Effect.Effect<A, E, R>
123
- ): Effect.Effect<A, E, R> {
124
- if (report.kind === "closed") {
125
- return effect
24
+ const input: RuntimeOptionsIn = {
25
+ ...(options.workers === undefined ? {} : { workers: options.workers }),
26
+ ...(options.queueCapacity === undefined ? {} : { queueCapacity: options.queueCapacity }),
27
+ ...(options.cleanupCapacity === undefined ? {} : { cleanupCapacity: options.cleanupCapacity }),
28
+ ...(options.ownerCapacity === undefined ? {} : { ownerCapacity: options.ownerCapacity }),
29
+ ...(options.nativeHandleCapacity === undefined ? {} : { nativeHandleCapacity: options.nativeHandleCapacity }),
30
+ ...(timeout === undefined ? {} : { cleanupTimeoutMs: Math.ceil(timeout) })
126
31
  }
127
- return effect.pipe(Effect.onExit(() => Effect.die(new CloseFailure({ operation, report }))))
32
+ return JSON.stringify(input)
128
33
  }
129
34
 
130
- function close(handle: RuntimeHandle): Effect.Effect<CloseReport> {
131
- return drain("NativeRuntime.close", (callback) => runtimeNative.runtimeClose(handle, callback))
132
- }
35
+ /** The live native runtime: one worker pool and handle registry per process. */
36
+ export class Runtime {
37
+ readonly #handle: RuntimeRef
38
+ /** The runtime's outstanding native work, after every operation queued before it settles. */
39
+ readonly inspect: () => Effect.Effect<OutstandingWork, DbError>
133
40
 
134
- /** Kick `runtimeCancel` without joining. Used when the completion
135
- * callback arrives after abort: JS must not `take`; L12 drain reclaims
136
- * queued Page/Rows. A second call is native-idempotent (Committed
137
- * mutations stay; only unpublished delivery is dropped).
138
- */
139
- function startCancelDrain(operation: string, lease: OperationHandle, stash: { report?: CloseReport }): void {
140
- try {
141
- runtimeNative.runtimeCancel(lease, (report) => {
142
- stash.report = closeReport(operation, report)
41
+ constructor(handle: RuntimeRef) {
42
+ this.#handle = handle
43
+ this.inspect = Effect.fn("Bumble.inspect")(function* () {
44
+ yield* call("Bumble.inspect", (done) => addon.runtimeReady(handle, done), addon.runtimeTake)
45
+ return addon.runtimeInspect(handle) as OutstandingWork
143
46
  })
144
- } catch (cause) {
145
- stash.report = { kind: "failed", error: failure(operation, cause) }
146
47
  }
147
- }
148
-
149
- /** Registration returns the native lease before any completion can run in JS.
150
- * Interruption cancels and joins that lease, including a late successful result.
151
- * No Promise or libuv job is created for ordinary native work.
152
- */
153
- export function nativeOperation<A>(
154
- operation: string,
155
- start: (callback: () => void) => OperationHandle,
156
- accept: (value: Uint8Array | null) => A
157
- ): Effect.Effect<A, DbError> {
158
- return nativeOperationWith(operation, start, runtimeNative.runtimeTake, accept)
159
- }
160
48
 
161
- export function nativeOperationWith<A, Value>(
162
- operation: string,
163
- start: (callback: () => void) => OperationHandle,
164
- take: (operation: OperationHandle) => Value,
165
- accept: (value: Value) => A
166
- ): Effect.Effect<A, DbError> {
167
- const stash: { report?: CloseReport } = {}
168
- const cancelOp = `${operation}.cancel`
169
- return attachInterruptClose(
170
- cancelOp,
171
- stash
172
- )(
173
- Effect.callback((resume, signal) => {
174
- let lease: OperationHandle
175
- try {
176
- lease = start(() => {
177
- if (signal.aborted) {
178
- startCancelDrain(cancelOp, lease, stash)
179
- return
180
- }
181
- try {
182
- resume(Effect.succeed(accept(take(lease))))
183
- } catch (cause) {
184
- resume(Effect.fail(failure(operation, cause)))
185
- }
186
- })
187
- } catch (cause) {
188
- resume(Effect.fail(failure(operation, cause)))
189
- return
190
- }
191
- return joinInterruptDrain(cancelOp, (callback) => runtimeNative.runtimeCancel(lease, callback), stash)
192
- })
193
- )
49
+ static handle(runtime: Runtime): RuntimeRef {
50
+ return runtime.#handle
51
+ }
194
52
  }
195
53
 
196
- const acquire = Effect.fn("NativeRuntime.acquire")(function* (configuration: NativeRuntimeOptions) {
197
- return yield* Effect.acquireRelease(
198
- Effect.suspend(() => {
199
- const stash: { report?: CloseReport } = {}
200
- return attachInterruptClose(
201
- "NativeRuntime.acquire.cancel",
202
- stash
203
- )(
204
- Effect.callback<RuntimeService, DbError>((resume, signal) => {
205
- let handle: RuntimeHandle | undefined
206
- try {
207
- const wire = options(configuration)
208
- handle = runtimeNative.runtimeOpen(wire)
209
- const owner = handle
210
- const lease = runtimeNative.runtimeReady(owner, () => {
211
- if (signal.aborted) {
212
- try {
213
- runtimeNative.runtimeClose(owner, (report) => {
214
- stash.report = closeReport("NativeRuntime.acquire.cancel", report)
215
- })
216
- } catch (cause) {
217
- stash.report = { kind: "failed", error: failure("NativeRuntime.acquire.cancel", cause) }
218
- }
219
- return
220
- }
221
- try {
222
- runtimeNative.runtimeTake(lease)
223
- const service: RuntimeService = {
224
- close: () => close(owner),
225
- inspect: Effect.fn("NativeRuntime.inspect")(function* () {
226
- yield* nativeOperation(
227
- "NativeRuntime.inspect",
228
- (callback) => runtimeNative.runtimeReady(owner, callback),
229
- () => undefined
230
- )
231
- return runtimeNative.runtimeInspect(owner)
232
- })
233
- }
234
- owners.set(service, owner)
235
- resume(Effect.succeed(service))
236
- } catch (cause) {
237
- const error = failure("NativeRuntime.acquire", cause)
238
- resume(
239
- close(owner).pipe(
240
- Effect.flatMap((report) => afterClose("NativeRuntime.acquire", report, Effect.fail(error)))
241
- )
242
- )
243
- }
244
- })
245
- } catch (cause) {
246
- const error = failure("NativeRuntime.acquire", cause)
247
- resume(
248
- handle === undefined
249
- ? Effect.fail(error)
250
- : close(handle).pipe(
251
- Effect.flatMap((report) => afterClose("NativeRuntime.acquire", report, Effect.fail(error)))
252
- )
253
- )
254
- }
255
- return handle === undefined
256
- ? Effect.void
257
- : joinInterruptDrain(
258
- "NativeRuntime.acquire.cancel",
259
- (callback) => runtimeNative.runtimeClose(handle, callback),
260
- stash
261
- )
262
- })
263
- )
264
- }),
265
- (service) => service.close().pipe(Effect.flatMap((report) => finalizeClose("NativeRuntime.release", report))),
266
- { interruptible: true }
54
+ const acquire = Effect.fn("Bumble.layer")(function* (options: BumbleOptions) {
55
+ const input = yield* Effect.try({ try: () => wire(options), catch: (cause) => dbError("Bumble.layer", cause) })
56
+ const handle = yield* scoped(
57
+ "Bumble.release",
58
+ Effect.try({ try: () => addon.runtimeOpen(input), catch: (cause) => dbError("Bumble.layer", cause) }),
59
+ (owner) => (done) => addon.runtimeClose(owner, done)
267
60
  )
61
+ yield* call("Bumble.layer", (done) => addon.runtimeReady(handle, done), addon.runtimeTake)
62
+ return new Runtime(handle)
268
63
  })
269
64
 
270
- export class NativeRuntime extends Context.Service<NativeRuntime, RuntimeService>()(
271
- "@bjornpagen/bumbledb/NativeRuntime"
272
- ) {
273
- static layer(options: NativeRuntimeOptions = {}): Layer.Layer<NativeRuntime, DbError> {
274
- return Layer.effect(NativeRuntime, acquire(options))
65
+ /** The native runtime as an Effect service. Provide `Bumble.layer()` once in the application graph. */
66
+ export class Bumble extends Context.Service<Bumble, Runtime>()("@bjornpagen/bumbledb/Bumble") {
67
+ static layer(options: BumbleOptions = {}): Layer.Layer<Bumble, DbError> {
68
+ return Layer.effect(Bumble, acquire(options))
275
69
  }
276
70
  }
277
71
 
278
- /** Private core/log integration: captures the already acquired shared service. */
279
- export const runtimeHandle = Effect.fn("NativeRuntime.handle")(function* () {
280
- const runtime = yield* NativeRuntime
281
- const handle = owners.get(runtime)
282
- if (handle === undefined) return yield* Effect.fail(invalid("NativeRuntime.handle"))
283
- return handle
284
- })
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
- /** Internal first executor consumer; not a replacement row codec or public hash API. */
356
- export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8Array) {
357
- const runtime = yield* NativeRuntime
358
- const handle = owners.get(runtime)
359
- if (handle === undefined) return yield* Effect.fail(invalid("hashChunk"))
360
- return yield* nativeOperation(
361
- "hashChunk",
362
- (callback) => {
363
- if (!(input instanceof Uint8Array) || !(input.buffer instanceof ArrayBuffer)) throw invalid("hashChunk")
364
- return runtimeNative.runtimeHash(handle, input, callback)
365
- },
366
- (value) => {
367
- if (value === null) throw dbError("hashChunk", { _tag: "Internal" })
368
- return value
369
- }
370
- )
72
+ /** The provided runtime's native handle. */
73
+ export const runtimeHandle: Effect.Effect<RuntimeRef, never, Bumble> = Effect.gen(function* () {
74
+ return Runtime.handle(yield* Bumble)
371
75
  })