@statewalker/cellb 0.0.0-stage → 0.1.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 (365) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +323 -2
  3. package/dist/adapters/files-backup.d.ts +16 -0
  4. package/dist/adapters/files-backup.js +32 -0
  5. package/dist/adapters/files-backup.js.map +1 -0
  6. package/dist/adapters/files-changelog.d.ts +33 -0
  7. package/dist/adapters/files-changelog.js +96 -0
  8. package/dist/adapters/files-changelog.js.map +1 -0
  9. package/dist/adapters/files-ltx.d.ts +35 -0
  10. package/dist/adapters/files-ltx.js +9 -0
  11. package/dist/adapters/files-ltx.js.map +1 -0
  12. package/dist/adapters/ltx-vfs.d.ts +21 -0
  13. package/dist/adapters/ltx-vfs.js +286 -0
  14. package/dist/adapters/ltx-vfs.js.map +1 -0
  15. package/dist/adapters/node-sqlite.d.ts +45 -0
  16. package/dist/adapters/node-sqlite.js +254 -0
  17. package/dist/adapters/node-sqlite.js.map +1 -0
  18. package/dist/adapters/wasm-page-hook.d.ts +26 -0
  19. package/dist/adapters/wasm-page-hook.js +55 -0
  20. package/dist/adapters/wasm-page-hook.js.map +1 -0
  21. package/dist/adapters/wasm-session.d.ts +11 -0
  22. package/dist/adapters/wasm-session.js +76 -0
  23. package/dist/adapters/wasm-session.js.map +1 -0
  24. package/dist/adapters/wasm-sqlite.d.ts +89 -0
  25. package/dist/adapters/wasm-sqlite.js +258 -0
  26. package/dist/adapters/wasm-sqlite.js.map +1 -0
  27. package/dist/bindings/assets.d.ts +26 -0
  28. package/dist/bindings/assets.js +260 -0
  29. package/dist/bindings/assets.js.map +1 -0
  30. package/dist/bindings/cache.d.ts +16 -0
  31. package/dist/bindings/cache.js +34 -0
  32. package/dist/bindings/cache.js.map +1 -0
  33. package/dist/bindings/celld-kv.d.ts +26 -0
  34. package/dist/bindings/celld-kv.js +81 -0
  35. package/dist/bindings/celld-kv.js.map +1 -0
  36. package/dist/bindings/celld-queues.d.ts +56 -0
  37. package/dist/bindings/celld-queues.js +223 -0
  38. package/dist/bindings/celld-queues.js.map +1 -0
  39. package/dist/bindings/celld-workflows.d.ts +22 -0
  40. package/dist/bindings/celld-workflows.js +54 -0
  41. package/dist/bindings/celld-workflows.js.map +1 -0
  42. package/dist/bindings/cron.d.ts +27 -0
  43. package/dist/bindings/cron.js +232 -0
  44. package/dist/bindings/cron.js.map +1 -0
  45. package/dist/bindings/d1-migrations.d.ts +22 -0
  46. package/dist/bindings/d1-migrations.js +32 -0
  47. package/dist/bindings/d1-migrations.js.map +1 -0
  48. package/dist/bindings/html-rewriter.d.ts +8 -0
  49. package/dist/bindings/html-rewriter.js +75 -0
  50. package/dist/bindings/html-rewriter.js.map +1 -0
  51. package/dist/bindings/r2-celld.d.ts +3 -0
  52. package/dist/bindings/r2-celld.js +113 -0
  53. package/dist/bindings/r2-celld.js.map +1 -0
  54. package/dist/bindings/r2.d.ts +236 -0
  55. package/dist/bindings/r2.js +419 -0
  56. package/dist/bindings/r2.js.map +1 -0
  57. package/dist/bindings/worker-loader.d.ts +83 -0
  58. package/dist/bindings/worker-loader.js +183 -0
  59. package/dist/bindings/worker-loader.js.map +1 -0
  60. package/dist/bindings/workers-ai.d.ts +39 -0
  61. package/dist/bindings/workers-ai.js +175 -0
  62. package/dist/bindings/workers-ai.js.map +1 -0
  63. package/dist/browser.d.ts +7 -0
  64. package/dist/browser.js +8 -0
  65. package/dist/browser.js.map +1 -0
  66. package/dist/bucket/celld-cells.d.ts +86 -0
  67. package/dist/bucket/celld-cells.js +273 -0
  68. package/dist/bucket/celld-cells.js.map +1 -0
  69. package/dist/bucket/celld-format.d.ts +10 -0
  70. package/dist/bucket/celld-format.js +27 -0
  71. package/dist/bucket/celld-format.js.map +1 -0
  72. package/dist/bucket/idb-object-store.d.ts +9 -0
  73. package/dist/bucket/idb-object-store.js +73 -0
  74. package/dist/bucket/idb-object-store.js.map +1 -0
  75. package/dist/bucket/ltx-log.d.ts +34 -0
  76. package/dist/bucket/ltx-log.js +289 -0
  77. package/dist/bucket/ltx-log.js.map +1 -0
  78. package/dist/bucket/object-store.d.ts +51 -0
  79. package/dist/bucket/object-store.js +114 -0
  80. package/dist/bucket/object-store.js.map +1 -0
  81. package/dist/bucket/s3-object-store.d.ts +25 -0
  82. package/dist/bucket/s3-object-store.js +151 -0
  83. package/dist/bucket/s3-object-store.js.map +1 -0
  84. package/dist/bucket/sqlite-object-store.d.ts +10 -0
  85. package/dist/bucket/sqlite-object-store.js +107 -0
  86. package/dist/bucket/sqlite-object-store.js.map +1 -0
  87. package/dist/core/alarms.d.ts +27 -0
  88. package/dist/core/alarms.js +88 -0
  89. package/dist/core/alarms.js.map +1 -0
  90. package/dist/core/async-context.d.ts +34 -0
  91. package/dist/core/async-context.js +50 -0
  92. package/dist/core/async-context.js.map +1 -0
  93. package/dist/core/celld-schema.d.ts +14 -0
  94. package/dist/core/celld-schema.js +34 -0
  95. package/dist/core/celld-schema.js.map +1 -0
  96. package/dist/core/durable-object.d.ts +28 -0
  97. package/dist/core/durable-object.js +31 -0
  98. package/dist/core/durable-object.js.map +1 -0
  99. package/dist/core/gates.d.ts +33 -0
  100. package/dist/core/gates.js +89 -0
  101. package/dist/core/gates.js.map +1 -0
  102. package/dist/core/handles.d.ts +90 -0
  103. package/dist/core/handles.js +224 -0
  104. package/dist/core/handles.js.map +1 -0
  105. package/dist/core/id.d.ts +19 -0
  106. package/dist/core/id.js +64 -0
  107. package/dist/core/id.js.map +1 -0
  108. package/dist/core/namespace.d.ts +38 -0
  109. package/dist/core/namespace.js +141 -0
  110. package/dist/core/namespace.js.map +1 -0
  111. package/dist/core/object-host.d.ts +91 -0
  112. package/dist/core/object-host.js +616 -0
  113. package/dist/core/object-host.js.map +1 -0
  114. package/dist/core/request-body.d.ts +9 -0
  115. package/dist/core/request-body.js +55 -0
  116. package/dist/core/request-body.js.map +1 -0
  117. package/dist/core/runtime.d.ts +121 -0
  118. package/dist/core/runtime.js +366 -0
  119. package/dist/core/runtime.js.map +1 -0
  120. package/dist/core/shard.d.ts +7 -0
  121. package/dist/core/shard.js +10 -0
  122. package/dist/core/shard.js.map +1 -0
  123. package/dist/core/sql-split.d.ts +15 -0
  124. package/dist/core/sql-split.js +101 -0
  125. package/dist/core/sql-split.js.map +1 -0
  126. package/dist/core/sql-storage.d.ts +53 -0
  127. package/dist/core/sql-storage.js +407 -0
  128. package/dist/core/sql-storage.js.map +1 -0
  129. package/dist/core/state.d.ts +44 -0
  130. package/dist/core/state.js +93 -0
  131. package/dist/core/state.js.map +1 -0
  132. package/dist/core/storage.d.ts +103 -0
  133. package/dist/core/storage.js +351 -0
  134. package/dist/core/storage.js.map +1 -0
  135. package/dist/core/worker-rejections.d.ts +21 -0
  136. package/dist/core/worker-rejections.js +55 -0
  137. package/dist/core/worker-rejections.js.map +1 -0
  138. package/dist/index.d.ts +22 -0
  139. package/dist/index.js +23 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/isolate/browser.d.ts +16 -0
  142. package/dist/isolate/browser.js +37 -0
  143. package/dist/isolate/browser.js.map +1 -0
  144. package/dist/isolate/host.d.ts +24 -0
  145. package/dist/isolate/host.js +74 -0
  146. package/dist/isolate/host.js.map +1 -0
  147. package/dist/isolate/isolate-worker.d.ts +1 -0
  148. package/dist/isolate/isolate-worker.js +53 -0
  149. package/dist/isolate/isolate-worker.js.map +1 -0
  150. package/dist/isolate/isolate.d.ts +10 -0
  151. package/dist/isolate/isolate.js +169 -0
  152. package/dist/isolate/isolate.js.map +1 -0
  153. package/dist/isolate/node-entry.d.ts +1 -0
  154. package/dist/isolate/node-entry.js +12 -0
  155. package/dist/isolate/node-entry.js.map +1 -0
  156. package/dist/isolate/protocol.d.ts +66 -0
  157. package/dist/isolate/protocol.js +205 -0
  158. package/dist/isolate/protocol.js.map +1 -0
  159. package/dist/ltx/crc64.d.ts +6 -0
  160. package/dist/ltx/crc64.js +46 -0
  161. package/dist/ltx/crc64.js.map +1 -0
  162. package/dist/ltx/ltx-page-server.d.ts +65 -0
  163. package/dist/ltx/ltx-page-server.js +309 -0
  164. package/dist/ltx/ltx-page-server.js.map +1 -0
  165. package/dist/ltx/ltx-pages.d.ts +81 -0
  166. package/dist/ltx/ltx-pages.js +315 -0
  167. package/dist/ltx/ltx-pages.js.map +1 -0
  168. package/dist/ltx/ltx-store-worker.d.ts +1 -0
  169. package/dist/ltx/ltx-store-worker.js +38 -0
  170. package/dist/ltx/ltx-store-worker.js.map +1 -0
  171. package/dist/ltx/ltx.d.ts +45 -0
  172. package/dist/ltx/ltx.js +203 -0
  173. package/dist/ltx/ltx.js.map +1 -0
  174. package/dist/ltx/lz4.d.ts +9 -0
  175. package/dist/ltx/lz4.js +156 -0
  176. package/dist/ltx/lz4.js.map +1 -0
  177. package/dist/ltx/opfs-sync-files.d.ts +23 -0
  178. package/dist/ltx/opfs-sync-files.js +191 -0
  179. package/dist/ltx/opfs-sync-files.js.map +1 -0
  180. package/dist/ltx/xxh32.d.ts +1 -0
  181. package/dist/ltx/xxh32.js +43 -0
  182. package/dist/ltx/xxh32.js.map +1 -0
  183. package/dist/miniflare-compat/blob-service.d.ts +13 -0
  184. package/dist/miniflare-compat/blob-service.js +66 -0
  185. package/dist/miniflare-compat/blob-service.js.map +1 -0
  186. package/dist/miniflare-compat/kv-binding.d.ts +44 -0
  187. package/dist/miniflare-compat/kv-binding.js +99 -0
  188. package/dist/miniflare-compat/kv-binding.js.map +1 -0
  189. package/dist/miniflare-compat/shims/assert.d.ts +15 -0
  190. package/dist/miniflare-compat/shims/assert.js +24 -0
  191. package/dist/miniflare-compat/shims/assert.js.map +1 -0
  192. package/dist/miniflare-compat/shims/workerd-cloudflare.d.ts +7 -0
  193. package/dist/miniflare-compat/shims/workerd-cloudflare.js +6 -0
  194. package/dist/miniflare-compat/shims/workerd-cloudflare.js.map +1 -0
  195. package/dist/miniflare-compat/shims/workerd-globals.d.ts +1 -0
  196. package/dist/miniflare-compat/shims/workerd-globals.js +30 -0
  197. package/dist/miniflare-compat/shims/workerd-globals.js.map +1 -0
  198. package/dist/miniflare-compat/shims/workerd-internal.d.ts +20 -0
  199. package/dist/miniflare-compat/shims/workerd-internal.js +23 -0
  200. package/dist/miniflare-compat/shims/workerd-internal.js.map +1 -0
  201. package/dist/miniflare-compat/shims/workerd-performance.d.ts +4 -0
  202. package/dist/miniflare-compat/shims/workerd-performance.js +17 -0
  203. package/dist/miniflare-compat/shims/workerd-performance.js.map +1 -0
  204. package/dist/miniflare-compat/simulator-fetcher.d.ts +9 -0
  205. package/dist/miniflare-compat/simulator-fetcher.js +14 -0
  206. package/dist/miniflare-compat/simulator-fetcher.js.map +1 -0
  207. package/dist/miniflare-compat/vendored.d.ts +15 -0
  208. package/dist/miniflare-compat/vendored.js +27 -0
  209. package/dist/miniflare-compat/vendored.js.map +1 -0
  210. package/dist/node-compat/async_hooks.d.ts +22 -0
  211. package/dist/node-compat/async_hooks.js +176 -0
  212. package/dist/node-compat/async_hooks.js.map +1 -0
  213. package/dist/node-compat/browser-registry.d.ts +3 -0
  214. package/dist/node-compat/browser-registry.js +39 -0
  215. package/dist/node-compat/browser-registry.js.map +1 -0
  216. package/dist/node-compat/crypto.d.ts +60 -0
  217. package/dist/node-compat/crypto.js +143 -0
  218. package/dist/node-compat/crypto.js.map +1 -0
  219. package/dist/node-compat/diagnostics_channel.d.ts +27 -0
  220. package/dist/node-compat/diagnostics_channel.js +46 -0
  221. package/dist/node-compat/diagnostics_channel.js.map +1 -0
  222. package/dist/node-compat/fs.d.ts +35 -0
  223. package/dist/node-compat/fs.js +40 -0
  224. package/dist/node-compat/fs.js.map +1 -0
  225. package/dist/node-compat/globals.d.ts +3 -0
  226. package/dist/node-compat/globals.js +4 -0
  227. package/dist/node-compat/globals.js.map +1 -0
  228. package/dist/node-compat/install.d.ts +5 -0
  229. package/dist/node-compat/install.js +12 -0
  230. package/dist/node-compat/install.js.map +1 -0
  231. package/dist/node-compat/module.d.ts +12 -0
  232. package/dist/node-compat/module.js +36 -0
  233. package/dist/node-compat/module.js.map +1 -0
  234. package/dist/node-compat/modules.d.ts +8 -0
  235. package/dist/node-compat/modules.js +23 -0
  236. package/dist/node-compat/modules.js.map +1 -0
  237. package/dist/node-compat/os.d.ts +36 -0
  238. package/dist/node-compat/os.js +36 -0
  239. package/dist/node-compat/os.js.map +1 -0
  240. package/dist/node-compat/process.d.ts +14 -0
  241. package/dist/node-compat/process.js +16 -0
  242. package/dist/node-compat/process.js.map +1 -0
  243. package/dist/node-compat/shims.d.ts +4 -0
  244. package/dist/node.d.ts +5 -0
  245. package/dist/node.js +6 -0
  246. package/dist/node.js.map +1 -0
  247. package/dist/peer/celld-peer.d.ts +53 -0
  248. package/dist/peer/celld-peer.js +555 -0
  249. package/dist/peer/celld-peer.js.map +1 -0
  250. package/dist/ports.d.ts +183 -0
  251. package/dist/ports.js +10 -0
  252. package/dist/ports.js.map +1 -0
  253. package/dist/project/browser-bundle.d.ts +17 -0
  254. package/dist/project/browser-bundle.js +125 -0
  255. package/dist/project/browser-bundle.js.map +1 -0
  256. package/dist/project/browser-cookies.d.ts +12 -0
  257. package/dist/project/browser-cookies.js +90 -0
  258. package/dist/project/browser-cookies.js.map +1 -0
  259. package/dist/project/browser-host.d.ts +173 -0
  260. package/dist/project/browser-host.js +200 -0
  261. package/dist/project/browser-host.js.map +1 -0
  262. package/dist/project/browser-modules.d.ts +17 -0
  263. package/dist/project/browser-modules.js +89 -0
  264. package/dist/project/browser-modules.js.map +1 -0
  265. package/dist/project/browser-shards.d.ts +62 -0
  266. package/dist/project/browser-shards.js +210 -0
  267. package/dist/project/browser-shards.js.map +1 -0
  268. package/dist/project/browser-tabs.d.ts +103 -0
  269. package/dist/project/browser-tabs.js +418 -0
  270. package/dist/project/browser-tabs.js.map +1 -0
  271. package/dist/project/cloudflare-email.d.ts +8 -0
  272. package/dist/project/cloudflare-email.js +11 -0
  273. package/dist/project/cloudflare-email.js.map +1 -0
  274. package/dist/project/cloudflare-workers.d.ts +12 -0
  275. package/dist/project/cloudflare-workers.js +16 -0
  276. package/dist/project/cloudflare-workers.js.map +1 -0
  277. package/dist/project/host-modules.d.ts +4 -0
  278. package/dist/project/host-modules.js +40 -0
  279. package/dist/project/host-modules.js.map +1 -0
  280. package/dist/project/jsonc.d.ts +2 -0
  281. package/dist/project/jsonc.js +27 -0
  282. package/dist/project/jsonc.js.map +1 -0
  283. package/dist/project/lower-await.d.ts +2 -0
  284. package/dist/project/lower-await.js +151 -0
  285. package/dist/project/lower-await.js.map +1 -0
  286. package/dist/project/node-project.d.ts +38 -0
  287. package/dist/project/node-project.js +362 -0
  288. package/dist/project/node-project.js.map +1 -0
  289. package/dist/project/own-module.d.ts +6 -0
  290. package/dist/project/own-module.js +13 -0
  291. package/dist/project/own-module.js.map +1 -0
  292. package/dist/project/scheduler.d.ts +2 -0
  293. package/dist/project/scheduler.js +3 -0
  294. package/dist/project/scheduler.js.map +1 -0
  295. package/dist/project/site.d.ts +74 -0
  296. package/dist/project/site.js +206 -0
  297. package/dist/project/site.js.map +1 -0
  298. package/dist/project/virtual-modules.d.ts +3 -0
  299. package/dist/project/worker-scope.d.ts +27 -0
  300. package/dist/project/worker-scope.js +46 -0
  301. package/dist/project/worker-scope.js.map +1 -0
  302. package/dist/project/workerd-message-channel.d.ts +13 -0
  303. package/dist/project/workerd-message-channel.js +137 -0
  304. package/dist/project/workerd-message-channel.js.map +1 -0
  305. package/dist/project/workers-global-names.d.ts +6 -0
  306. package/dist/project/workers-global-names.js +15 -0
  307. package/dist/project/workers-global-names.js.map +1 -0
  308. package/dist/project/workers-globals-module.d.ts +8 -0
  309. package/dist/project/workers-globals-module.js +9 -0
  310. package/dist/project/workers-globals-module.js.map +1 -0
  311. package/dist/project/workers-globals.d.ts +9 -0
  312. package/dist/project/workers-globals.js +19 -0
  313. package/dist/project/workers-globals.js.map +1 -0
  314. package/dist/project/wrangler.d.ts +116 -0
  315. package/dist/project/wrangler.js +342 -0
  316. package/dist/project/wrangler.js.map +1 -0
  317. package/dist/router/cell-router.d.ts +72 -0
  318. package/dist/router/cell-router.js +247 -0
  319. package/dist/router/cell-router.js.map +1 -0
  320. package/dist/router/shard-worker.d.ts +44 -0
  321. package/dist/router/shard-worker.js +163 -0
  322. package/dist/router/shard-worker.js.map +1 -0
  323. package/dist/transport/cells-http.d.ts +25 -0
  324. package/dist/transport/cells-http.js +141 -0
  325. package/dist/transport/cells-http.js.map +1 -0
  326. package/dist/transport/cells-link.d.ts +27 -0
  327. package/dist/transport/cells-link.js +71 -0
  328. package/dist/transport/cells-link.js.map +1 -0
  329. package/dist/transport/port.d.ts +26 -0
  330. package/dist/transport/port.js +100 -0
  331. package/dist/transport/port.js.map +1 -0
  332. package/dist/v8/codec.d.ts +6 -0
  333. package/dist/v8/codec.js +9 -0
  334. package/dist/v8/codec.js.map +1 -0
  335. package/dist/v8/serialize.d.ts +10 -0
  336. package/dist/v8/serialize.js +601 -0
  337. package/dist/v8/serialize.js.map +1 -0
  338. package/dist/ws/client.d.ts +25 -0
  339. package/dist/ws/client.js +203 -0
  340. package/dist/ws/client.js.map +1 -0
  341. package/dist/ws/control.d.ts +48 -0
  342. package/dist/ws/control.js +44 -0
  343. package/dist/ws/control.js.map +1 -0
  344. package/dist/ws/frames.d.ts +38 -0
  345. package/dist/ws/frames.js +39 -0
  346. package/dist/ws/frames.js.map +1 -0
  347. package/dist/ws/server.d.ts +99 -0
  348. package/dist/ws/server.js +295 -0
  349. package/dist/ws/server.js.map +1 -0
  350. package/dist/ws/sw-connector.d.ts +10 -0
  351. package/dist/ws/sw-connector.js +25 -0
  352. package/dist/ws/sw-connector.js.map +1 -0
  353. package/dist/ws/upgrade.d.ts +28 -0
  354. package/dist/ws/upgrade.js +75 -0
  355. package/dist/ws/upgrade.js.map +1 -0
  356. package/package.json +111 -3
  357. package/vendor/celld/LICENSE +202 -0
  358. package/vendor/celld/kv.js +1131 -0
  359. package/vendor/celld/queues.js +1656 -0
  360. package/vendor/celld/workflows.js +1610 -0
  361. package/vendor/html-rewriter-wasm/html_rewriter.js +925 -0
  362. package/vendor/html-rewriter-wasm/html_rewriter_bg.wasm +0 -0
  363. package/vendor/miniflare/d1-api.js +738 -0
  364. package/vendor/miniflare/d1-database.js +6978 -0
  365. package/vendor/miniflare/kv-namespace.js +3061 -0
@@ -0,0 +1,286 @@
1
+ /**
2
+ * A synchronous SQLite VFS (SQLite WASM, in a worker) over an ASYNCHRONOUS page store in another
3
+ * thread: the database is a cell's LTX files on a FilesApi (src/ltx/ltx-pages.ts, served by
4
+ * ltxPageServer). Reads block on a SharedArrayBuffer (Atomics.wait) while the store reads the
5
+ * page by range; pages are cached here (LRU). Writes stay in this worker until the commit
6
+ * (xSync of the main file), which sends the transaction's pages to the store: one LTX file.
7
+ * The rollback journal lives in memory (journal_mode=MEMORY): only the main file is a file.
8
+ *
9
+ * Needs cross-origin isolation (SharedArrayBuffer). One writer per database.
10
+ */
11
+ const SQLITE_OK = 0;
12
+ const SQLITE_IOERR = 10;
13
+ const SQLITE_IOERR_SHORT_READ = 522;
14
+ const SQLITE_OPEN_MAIN_DB = 0x100;
15
+ export function installLtxVfs(sqlite3, channel, opts = {}) {
16
+ const { capi, wasm } = sqlite3;
17
+ const name = opts.name ?? "ltx";
18
+ const cacheMax = opts.cachePages ?? 2048;
19
+ // A miss fetches this many consecutive pages at once (SQLite scans walk pages in order).
20
+ const readAhead = Math.max(1, opts.readAhead ?? 16);
21
+ const ctl = new Int32Array(channel.sab, 0, 2);
22
+ const payload = new Uint8Array(channel.sab, 8);
23
+ const stats = { reads: 0, cacheHits: 0, storeReads: 0, commits: 0, blockedMs: 0 };
24
+ /** A request answered synchronously: blocks this worker until the store notifies. */
25
+ const call = (msg) => {
26
+ Atomics.store(ctl, 0, 0);
27
+ const t = performance.now();
28
+ channel.post(msg);
29
+ // A store that never answers (a worker that failed to load) must not freeze this worker.
30
+ if (Atomics.wait(ctl, 0, 0, 30_000) === "timed-out")
31
+ throw new Error(`ltx store: no answer to ${String(msg.op)} in 30 s`);
32
+ stats.blockedMs += performance.now() - t;
33
+ if (ctl[0] === 2)
34
+ throw new Error(new TextDecoder().decode(payload.slice(0, ctl[1])));
35
+ return payload.slice(0, ctl[1]);
36
+ };
37
+ const dbs = new Map();
38
+ const dbOf = (cell) => {
39
+ let d = dbs.get(cell);
40
+ if (!d) {
41
+ const meta = JSON.parse(new TextDecoder().decode(call({ op: "meta", cell })));
42
+ d = {
43
+ cell,
44
+ pageSize: meta.pageSize || 4096,
45
+ pageCount: meta.pageCount,
46
+ cache: new Map(),
47
+ dirty: new Map(),
48
+ };
49
+ dbs.set(cell, d);
50
+ }
51
+ return d;
52
+ };
53
+ const page = (d, pgno) => {
54
+ const w = d.dirty.get(pgno);
55
+ if (w)
56
+ return w;
57
+ stats.reads++;
58
+ const c = d.cache.get(pgno);
59
+ if (c) {
60
+ stats.cacheHits++;
61
+ d.cache.delete(pgno);
62
+ d.cache.set(pgno, c);
63
+ return c;
64
+ }
65
+ stats.storeReads++;
66
+ if (pgno > d.pageCount)
67
+ return new Uint8Array(d.pageSize);
68
+ const count = Math.min(readAhead, d.pageCount - pgno + 1, Math.floor((channel.sab.byteLength - 8) / d.pageSize));
69
+ const run = call({ op: "readRun", cell: d.cell, pgno, count });
70
+ for (let i = 0; i * d.pageSize < run.length; i++) {
71
+ const q = pgno + i;
72
+ if (d.dirty.has(q))
73
+ continue;
74
+ d.cache.set(q, run.subarray(i * d.pageSize, (i + 1) * d.pageSize));
75
+ if (d.cache.size > cacheMax)
76
+ d.cache.delete(d.cache.keys().next().value);
77
+ }
78
+ return d.cache.get(pgno) ?? new Uint8Array(d.pageSize);
79
+ };
80
+ // Journal and other non-main files: plain memory.
81
+ const memFiles = new Map();
82
+ const open = new Map();
83
+ const io = new capi.sqlite3_io_methods();
84
+ io.$iVersion = 1;
85
+ const vfs = new capi.sqlite3_vfs();
86
+ vfs.$iVersion = 1;
87
+ vfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
88
+ vfs.$mxPathname = 512;
89
+ sqlite3.vfs.installVfs({
90
+ io: {
91
+ struct: io,
92
+ methods: {
93
+ xClose(pFile) {
94
+ const d = open.get(pFile)?.db;
95
+ if (d)
96
+ dbs.delete(d.cell); // its cache goes with it
97
+ open.delete(pFile);
98
+ return SQLITE_OK;
99
+ },
100
+ xRead(pFile, pDest, n, off) {
101
+ const f = open.get(pFile);
102
+ const at = Number(off);
103
+ const heap = wasm.heap8u();
104
+ if (!f?.main) {
105
+ const b = memFiles.get(f?.path ?? "") ?? new Uint8Array();
106
+ const got = b.subarray(at, at + n);
107
+ heap.set(got, pDest);
108
+ if (got.length < n) {
109
+ heap.fill(0, pDest + got.length, pDest + n);
110
+ return SQLITE_IOERR_SHORT_READ;
111
+ }
112
+ return SQLITE_OK;
113
+ }
114
+ try {
115
+ const d = f.db;
116
+ const { pageSize } = d;
117
+ const size = d.pageCount * pageSize;
118
+ let o = 0;
119
+ while (o < n && at + o < size) {
120
+ const pos = at + o;
121
+ const pgno = Math.floor(pos / pageSize) + 1;
122
+ const inPage = pos % pageSize;
123
+ const take = Math.min(pageSize - inPage, n - o);
124
+ heap.set(page(d, pgno).subarray(inPage, inPage + take), pDest + o);
125
+ o += take;
126
+ }
127
+ if (o < n) {
128
+ heap.fill(0, pDest + o, pDest + n);
129
+ return SQLITE_IOERR_SHORT_READ;
130
+ }
131
+ return SQLITE_OK;
132
+ }
133
+ catch (err) {
134
+ // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.
135
+ console.error(`cellb ltx-vfs: ${err?.message ?? err}`);
136
+ return SQLITE_IOERR;
137
+ }
138
+ },
139
+ xWrite(pFile, pSrc, n, off) {
140
+ const f = open.get(pFile);
141
+ const at = Number(off);
142
+ const src = wasm.heap8u().subarray(pSrc, pSrc + n);
143
+ if (!f?.main) {
144
+ const cur = memFiles.get(f?.path ?? "") ?? new Uint8Array();
145
+ const next = cur.length >= at + n ? cur : new Uint8Array(at + n);
146
+ if (next !== cur)
147
+ next.set(cur);
148
+ next.set(src, at);
149
+ memFiles.set(f?.path ?? "", next);
150
+ return SQLITE_OK;
151
+ }
152
+ const d = f.db;
153
+ if (d.pageCount === 0 && at === 0 && n >= 18) {
154
+ const ps = (src[16] << 8) | src[17];
155
+ d.pageSize = ps === 1 ? 65536 : ps;
156
+ }
157
+ const { pageSize } = d;
158
+ for (let o = 0; o < n;) {
159
+ const pos = at + o;
160
+ const pgno = Math.floor(pos / pageSize) + 1;
161
+ const inPage = pos % pageSize;
162
+ const take = Math.min(pageSize - inPage, n - o);
163
+ let p = d.dirty.get(pgno);
164
+ if (!p) {
165
+ p = take === pageSize ? new Uint8Array(pageSize) : page(d, pgno).slice();
166
+ d.dirty.set(pgno, p);
167
+ }
168
+ p.set(src.subarray(o, o + take), inPage);
169
+ o += take;
170
+ if (pgno > d.pageCount)
171
+ d.pageCount = pgno;
172
+ }
173
+ return SQLITE_OK;
174
+ },
175
+ xTruncate(pFile, size) {
176
+ const f = open.get(pFile);
177
+ if (!f?.main) {
178
+ memFiles.set(f?.path ?? "", (memFiles.get(f?.path ?? "") ?? new Uint8Array()).slice(0, Number(size)));
179
+ return SQLITE_OK;
180
+ }
181
+ const d = f.db;
182
+ d.pageCount = Math.ceil(Number(size) / d.pageSize);
183
+ for (const k of [...d.dirty.keys()])
184
+ if (k > d.pageCount)
185
+ d.dirty.delete(k);
186
+ return SQLITE_OK;
187
+ },
188
+ xSync(pFile) {
189
+ const d = open.get(pFile)?.db;
190
+ if (!d || !d.dirty.size)
191
+ return SQLITE_OK;
192
+ // The commit: this transaction's pages become one LTX file in the store.
193
+ try {
194
+ const pages = [...d.dirty].sort((a, b) => a[0] - b[0]);
195
+ call({
196
+ op: "commit",
197
+ cell: d.cell,
198
+ pageSize: d.pageSize,
199
+ pageCount: d.pageCount,
200
+ pages,
201
+ });
202
+ stats.commits++;
203
+ for (const [pgno, p] of pages)
204
+ d.cache.set(pgno, p);
205
+ d.dirty.clear();
206
+ return SQLITE_OK;
207
+ }
208
+ catch (err) {
209
+ // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.
210
+ console.error(`cellb ltx-vfs: ${err?.message ?? err}`);
211
+ return SQLITE_IOERR;
212
+ }
213
+ },
214
+ xFileSize(pFile, pOut) {
215
+ const f = open.get(pFile);
216
+ const size = f?.db
217
+ ? f.db.pageCount * f.db.pageSize
218
+ : (memFiles.get(f?.path ?? "")?.length ?? 0);
219
+ wasm.poke64(pOut, BigInt(size));
220
+ return SQLITE_OK;
221
+ },
222
+ xLock: () => SQLITE_OK,
223
+ xUnlock: () => SQLITE_OK,
224
+ xCheckReservedLock(_p, pOut) {
225
+ wasm.poke32(pOut, 0);
226
+ return SQLITE_OK;
227
+ },
228
+ xFileControl: () => 12, // SQLITE_NOTFOUND
229
+ xSectorSize: () => 4096,
230
+ xDeviceCharacteristics: () => 0,
231
+ },
232
+ },
233
+ vfs: {
234
+ struct: vfs,
235
+ name,
236
+ methods: {
237
+ xOpen(_pVfs, zName, pFile, flags, pOutFlags) {
238
+ const path = zName ? wasm.cstrToJs(zName) : `tmp-${pFile}`;
239
+ const main = !!(flags & SQLITE_OPEN_MAIN_DB);
240
+ try {
241
+ open.set(pFile, { main, path, db: main ? dbOf(path) : undefined });
242
+ }
243
+ catch (err) {
244
+ // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.
245
+ console.error(`cellb ltx-vfs: ${err?.message ?? err}`);
246
+ return SQLITE_IOERR;
247
+ }
248
+ const f = new capi.sqlite3_file(pFile);
249
+ f.$pMethods = io.pointer;
250
+ f.dispose();
251
+ if (pOutFlags)
252
+ wasm.poke32(pOutFlags, flags);
253
+ return SQLITE_OK;
254
+ },
255
+ xDelete(_p, zName) {
256
+ memFiles.delete(wasm.cstrToJs(zName));
257
+ return SQLITE_OK;
258
+ },
259
+ xAccess(_p, zName, _flags, pOut) {
260
+ wasm.poke32(pOut, memFiles.has(wasm.cstrToJs(zName)) ? 1 : 0);
261
+ return SQLITE_OK;
262
+ },
263
+ xFullPathname(_p, zName, nOut, pOut) {
264
+ wasm.cstrncpy(pOut, zName, nOut);
265
+ return SQLITE_OK;
266
+ },
267
+ xRandomness(_p, n, pOut) {
268
+ crypto.getRandomValues(wasm.heap8u().subarray(pOut, pOut + n));
269
+ return n;
270
+ },
271
+ xSleep: () => SQLITE_OK,
272
+ xCurrentTime(_p, pOut) {
273
+ wasm.poke64f(pOut, 2440587.5 + Date.now() / 86400000);
274
+ return SQLITE_OK;
275
+ },
276
+ xCurrentTimeInt64(_p, pOut) {
277
+ wasm.poke64(pOut, BigInt(Math.floor(2440587.5 * 86400000 + Date.now())));
278
+ return SQLITE_OK;
279
+ },
280
+ xGetLastError: () => 0,
281
+ },
282
+ },
283
+ });
284
+ return { name, stats };
285
+ }
286
+ //# sourceMappingURL=ltx-vfs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ltx-vfs.js","sourceRoot":"","sources":["../../src/adapters/ltx-vfs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A synchronous SQLite VFS (SQLite WASM, in a worker) over an ASYNCHRONOUS page store in another\n * thread: the database is a cell's LTX files on a FilesApi (src/ltx/ltx-pages.ts, served by\n * ltxPageServer). Reads block on a SharedArrayBuffer (Atomics.wait) while the store reads the\n * page by range; pages are cached here (LRU). Writes stay in this worker until the commit\n * (xSync of the main file), which sends the transaction's pages to the store: one LTX file.\n * The rollback journal lives in memory (journal_mode=MEMORY): only the main file is a file.\n *\n * Needs cross-origin isolation (SharedArrayBuffer). One writer per database.\n */\nconst SQLITE_OK = 0;\nconst SQLITE_IOERR = 10;\nconst SQLITE_IOERR_SHORT_READ = 522;\nconst SQLITE_OPEN_MAIN_DB = 0x100;\n\nexport interface LtxVfsChannel {\n /** Shared control words [state, length] + payload (≥ one page). */\n sab: SharedArrayBuffer;\n /** Posts a request to the store (it answers into `sab`, then notifies). */\n post(msg: unknown): void;\n}\n\nexport interface LtxVfsStats {\n reads: number;\n cacheHits: number;\n storeReads: number;\n commits: number;\n blockedMs: number;\n}\n\nexport function installLtxVfs(\n sqlite3: any,\n channel: LtxVfsChannel,\n opts: { name?: string; cachePages?: number; readAhead?: number } = {},\n): { name: string; stats: LtxVfsStats } {\n const { capi, wasm } = sqlite3;\n const name = opts.name ?? \"ltx\";\n const cacheMax = opts.cachePages ?? 2048;\n // A miss fetches this many consecutive pages at once (SQLite scans walk pages in order).\n const readAhead = Math.max(1, opts.readAhead ?? 16);\n const ctl = new Int32Array(channel.sab, 0, 2);\n const payload = new Uint8Array(channel.sab, 8);\n const stats: LtxVfsStats = { reads: 0, cacheHits: 0, storeReads: 0, commits: 0, blockedMs: 0 };\n\n /** A request answered synchronously: blocks this worker until the store notifies. */\n const call = (msg: Record<string, unknown>): Uint8Array => {\n Atomics.store(ctl, 0, 0);\n const t = performance.now();\n channel.post(msg);\n // A store that never answers (a worker that failed to load) must not freeze this worker.\n if (Atomics.wait(ctl, 0, 0, 30_000) === \"timed-out\")\n throw new Error(`ltx store: no answer to ${String(msg.op)} in 30 s`);\n stats.blockedMs += performance.now() - t;\n if (ctl[0] === 2) throw new Error(new TextDecoder().decode(payload.slice(0, ctl[1])));\n return payload.slice(0, ctl[1]);\n };\n /** One open database (a cell): its size, its page cache, the current transaction's pages. */\n interface Db {\n cell: string;\n pageSize: number;\n pageCount: number;\n cache: Map<number, Uint8Array>; // LRU: insertion order\n dirty: Map<number, Uint8Array>;\n }\n const dbs = new Map<string, Db>();\n const dbOf = (cell: string): Db => {\n let d = dbs.get(cell);\n if (!d) {\n const meta = JSON.parse(new TextDecoder().decode(call({ op: \"meta\", cell }))) as {\n pageSize: number;\n pageCount: number;\n };\n d = {\n cell,\n pageSize: meta.pageSize || 4096,\n pageCount: meta.pageCount,\n cache: new Map(),\n dirty: new Map(),\n };\n dbs.set(cell, d);\n }\n return d;\n };\n const page = (d: Db, pgno: number): Uint8Array => {\n const w = d.dirty.get(pgno);\n if (w) return w;\n stats.reads++;\n const c = d.cache.get(pgno);\n if (c) {\n stats.cacheHits++;\n d.cache.delete(pgno);\n d.cache.set(pgno, c);\n return c;\n }\n stats.storeReads++;\n if (pgno > d.pageCount) return new Uint8Array(d.pageSize);\n const count = Math.min(\n readAhead,\n d.pageCount - pgno + 1,\n Math.floor((channel.sab.byteLength - 8) / d.pageSize),\n );\n const run = call({ op: \"readRun\", cell: d.cell, pgno, count });\n for (let i = 0; i * d.pageSize < run.length; i++) {\n const q = pgno + i;\n if (d.dirty.has(q)) continue;\n d.cache.set(q, run.subarray(i * d.pageSize, (i + 1) * d.pageSize));\n if (d.cache.size > cacheMax) d.cache.delete(d.cache.keys().next().value as number);\n }\n return d.cache.get(pgno) ?? new Uint8Array(d.pageSize);\n };\n\n // Journal and other non-main files: plain memory.\n const memFiles = new Map<string, Uint8Array>();\n const open = new Map<number, { main: boolean; path: string; db?: Db }>();\n\n const io = new capi.sqlite3_io_methods();\n io.$iVersion = 1;\n const vfs = new capi.sqlite3_vfs();\n vfs.$iVersion = 1;\n vfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;\n vfs.$mxPathname = 512;\n\n sqlite3.vfs.installVfs({\n io: {\n struct: io,\n methods: {\n xClose(pFile: number) {\n const d = open.get(pFile)?.db;\n if (d) dbs.delete(d.cell); // its cache goes with it\n open.delete(pFile);\n return SQLITE_OK;\n },\n xRead(pFile: number, pDest: number, n: number, off: bigint) {\n const f = open.get(pFile);\n const at = Number(off);\n const heap = wasm.heap8u();\n if (!f?.main) {\n const b = memFiles.get(f?.path ?? \"\") ?? new Uint8Array();\n const got = b.subarray(at, at + n);\n heap.set(got, pDest);\n if (got.length < n) {\n heap.fill(0, pDest + got.length, pDest + n);\n return SQLITE_IOERR_SHORT_READ;\n }\n return SQLITE_OK;\n }\n try {\n const d = f.db as Db;\n const { pageSize } = d;\n const size = d.pageCount * pageSize;\n let o = 0;\n while (o < n && at + o < size) {\n const pos = at + o;\n const pgno = Math.floor(pos / pageSize) + 1;\n const inPage = pos % pageSize;\n const take = Math.min(pageSize - inPage, n - o);\n heap.set(page(d, pgno).subarray(inPage, inPage + take), pDest + o);\n o += take;\n }\n if (o < n) {\n heap.fill(0, pDest + o, pDest + n);\n return SQLITE_IOERR_SHORT_READ;\n }\n return SQLITE_OK;\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n },\n xWrite(pFile: number, pSrc: number, n: number, off: bigint) {\n const f = open.get(pFile);\n const at = Number(off);\n const src = wasm.heap8u().subarray(pSrc, pSrc + n);\n if (!f?.main) {\n const cur = memFiles.get(f?.path ?? \"\") ?? new Uint8Array();\n const next = cur.length >= at + n ? cur : new Uint8Array(at + n);\n if (next !== cur) next.set(cur);\n next.set(src, at);\n memFiles.set(f?.path ?? \"\", next);\n return SQLITE_OK;\n }\n const d = f.db as Db;\n if (d.pageCount === 0 && at === 0 && n >= 18) {\n const ps = (src[16] << 8) | src[17];\n d.pageSize = ps === 1 ? 65536 : ps;\n }\n const { pageSize } = d;\n for (let o = 0; o < n; ) {\n const pos = at + o;\n const pgno = Math.floor(pos / pageSize) + 1;\n const inPage = pos % pageSize;\n const take = Math.min(pageSize - inPage, n - o);\n let p = d.dirty.get(pgno);\n if (!p) {\n p = take === pageSize ? new Uint8Array(pageSize) : page(d, pgno).slice();\n d.dirty.set(pgno, p);\n }\n p.set(src.subarray(o, o + take), inPage);\n o += take;\n if (pgno > d.pageCount) d.pageCount = pgno;\n }\n return SQLITE_OK;\n },\n xTruncate(pFile: number, size: bigint) {\n const f = open.get(pFile);\n if (!f?.main) {\n memFiles.set(\n f?.path ?? \"\",\n (memFiles.get(f?.path ?? \"\") ?? new Uint8Array()).slice(0, Number(size)),\n );\n return SQLITE_OK;\n }\n const d = f.db as Db;\n d.pageCount = Math.ceil(Number(size) / d.pageSize);\n for (const k of [...d.dirty.keys()]) if (k > d.pageCount) d.dirty.delete(k);\n return SQLITE_OK;\n },\n xSync(pFile: number) {\n const d = open.get(pFile)?.db;\n if (!d || !d.dirty.size) return SQLITE_OK;\n // The commit: this transaction's pages become one LTX file in the store.\n try {\n const pages = [...d.dirty].sort((a, b) => a[0] - b[0]);\n call({\n op: \"commit\",\n cell: d.cell,\n pageSize: d.pageSize,\n pageCount: d.pageCount,\n pages,\n });\n stats.commits++;\n for (const [pgno, p] of pages) d.cache.set(pgno, p);\n d.dirty.clear();\n return SQLITE_OK;\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n },\n xFileSize(pFile: number, pOut: number) {\n const f = open.get(pFile);\n const size = f?.db\n ? f.db.pageCount * f.db.pageSize\n : (memFiles.get(f?.path ?? \"\")?.length ?? 0);\n wasm.poke64(pOut, BigInt(size));\n return SQLITE_OK;\n },\n xLock: () => SQLITE_OK,\n xUnlock: () => SQLITE_OK,\n xCheckReservedLock(_p: number, pOut: number) {\n wasm.poke32(pOut, 0);\n return SQLITE_OK;\n },\n xFileControl: () => 12, // SQLITE_NOTFOUND\n xSectorSize: () => 4096,\n xDeviceCharacteristics: () => 0,\n },\n },\n vfs: {\n struct: vfs,\n name,\n methods: {\n xOpen(_pVfs: number, zName: number, pFile: number, flags: number, pOutFlags: number) {\n const path = zName ? wasm.cstrToJs(zName) : `tmp-${pFile}`;\n const main = !!(flags & SQLITE_OPEN_MAIN_DB);\n try {\n open.set(pFile, { main, path, db: main ? dbOf(path) : undefined });\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n const f = new capi.sqlite3_file(pFile);\n f.$pMethods = io.pointer;\n f.dispose();\n if (pOutFlags) wasm.poke32(pOutFlags, flags);\n return SQLITE_OK;\n },\n xDelete(_p: number, zName: number) {\n memFiles.delete(wasm.cstrToJs(zName));\n return SQLITE_OK;\n },\n xAccess(_p: number, zName: number, _flags: number, pOut: number) {\n wasm.poke32(pOut, memFiles.has(wasm.cstrToJs(zName)) ? 1 : 0);\n return SQLITE_OK;\n },\n xFullPathname(_p: number, zName: number, nOut: number, pOut: number) {\n wasm.cstrncpy(pOut, zName, nOut);\n return SQLITE_OK;\n },\n xRandomness(_p: number, n: number, pOut: number) {\n crypto.getRandomValues(wasm.heap8u().subarray(pOut, pOut + n));\n return n;\n },\n xSleep: () => SQLITE_OK,\n xCurrentTime(_p: number, pOut: number) {\n wasm.poke64f(pOut, 2440587.5 + Date.now() / 86400000);\n return SQLITE_OK;\n },\n xCurrentTimeInt64(_p: number, pOut: number) {\n wasm.poke64(pOut, BigInt(Math.floor(2440587.5 * 86400000 + Date.now())));\n return SQLITE_OK;\n },\n xGetLastError: () => 0,\n },\n },\n });\n return { name, stats };\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import type { OpenCellSql, SqlResult, SyncSqlDriver } from "../ports.js";
2
+ /** Server-side SyncSqlDriver over node:sqlite (Node >= 22.13). */
3
+ export declare class NodeSqliteDriver implements SyncSqlDriver {
4
+ #private;
5
+ constructor(path?: string, opts?: {
6
+ exclusive?: boolean;
7
+ pageCapture?: boolean;
8
+ });
9
+ /** The pages of the transactions committed since the previous call (WAL frames). */
10
+ takeDirtyPages(): {
11
+ pageSize: number;
12
+ pageCount: number;
13
+ pages: Map<number, Uint8Array>;
14
+ } | undefined;
15
+ exec(sql: string, params: unknown[]): SqlResult;
16
+ inTransaction(): boolean;
17
+ databaseSize(): number;
18
+ close(): void;
19
+ /** node:sqlite exposes no ROWID session option: PK-less tables are logged as statements. */
20
+ readonly captureCoversRowidTables = false;
21
+ captureStart(): void;
22
+ captureTake(): Uint8Array;
23
+ captureDiscard(): void;
24
+ applyChangeset(changeset: Uint8Array): void;
25
+ /** Known extensions only: "sqlite_vec" → the sqlite-vec npm package's loadable library. */
26
+ loadExtension(name: string): void;
27
+ /**
28
+ * The database file as SQLite wrote it (page for page), for page-level logs; an in-memory
29
+ * database falls back to image(). Valid between transactions (rollback journal mode).
30
+ */
31
+ pageImage(): Uint8Array;
32
+ /** `VACUUM INTO` a temp file: a consistent, compact image (no open transaction allowed). */
33
+ image(): Uint8Array;
34
+ /** Replaces the database file with `image` and reopens it (an in-memory DB becomes file-backed). */
35
+ restore(image: Uint8Array): void;
36
+ }
37
+ /** One file per cell in `dir`, or in-memory when `dir` is omitted. */
38
+ /**
39
+ * `pageCapture` (file-backed only): WAL mode, and each commit's changed pages read from the WAL
40
+ * (takeDirtyPages) — LTX logs then cost O(changed pages) on Node as with SQLite WASM.
41
+ */
42
+ export declare function nodeSqliteOpener(dir?: string, opts?: {
43
+ exclusive?: boolean;
44
+ pageCapture?: boolean;
45
+ }): OpenCellSql;
@@ -0,0 +1,254 @@
1
+ import { closeSync, fstatSync, mkdtempSync, openSync, readFileSync, readSync, rmSync, writeFileSync, } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ // Loaded via require: bundlers (vite/vitest) strip the "node:" prefix of the
6
+ // still-experimental node:sqlite builtin and fail to resolve "sqlite".
7
+ const { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
8
+ import { splitSqlStatements } from "../core/sql-split.js";
9
+ const requireCjs = createRequire(import.meta.url);
10
+ const KNOWN_EXTENSIONS = {
11
+ sqlite_vec: () => requireCjs("sqlite-vec").getLoadablePath(),
12
+ };
13
+ /** Server-side SyncSqlDriver over node:sqlite (Node >= 22.13). */
14
+ export class NodeSqliteDriver {
15
+ #db;
16
+ #path;
17
+ #exclusive;
18
+ #pageCapture;
19
+ constructor(path = ":memory:", opts = {}) {
20
+ this.#path = path;
21
+ this.#exclusive = !!opts.exclusive;
22
+ this.#pageCapture = !!opts.pageCapture && path !== ":memory:";
23
+ // As celld and workerd: a double-quoted name that is no column is a string literal.
24
+ this.#db = new DatabaseSync(path, {
25
+ allowExtension: true,
26
+ enableDoubleQuotedStringLiterals: true,
27
+ });
28
+ this.#db.enableLoadExtension(false); // only loadExtension() below may load, by name
29
+ // EXCLUSIVE locking: the first write takes an OS lock on the file that is
30
+ // kept until close (or process death) — a second process can neither read
31
+ // nor write the cell. Storage-level fencing on one host, no lease needed.
32
+ if (opts.exclusive)
33
+ this.#db.exec("PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;");
34
+ this.#walSetup();
35
+ }
36
+ // ---- page capture through the WAL (what the WASM driver does with its xWrite hook) ----
37
+ // In WAL mode every commit appends its changed pages to `<db>-wal` as frames (24-byte frame
38
+ // header + page; a commit frame carries the database size in pages). Reading the frames
39
+ // appended since the last read gives exactly the committed transaction's pages, O(changed),
40
+ // instead of reading the whole file. Automatic checkpoints are off; take() checkpoints
41
+ // (TRUNCATE) itself once the WAL grows, and restarts from its start.
42
+ #walOffset = 0;
43
+ #walSetup() {
44
+ if (!this.#pageCapture)
45
+ return;
46
+ this.#db.exec("PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0;");
47
+ this.#db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
48
+ this.#walOffset = 0;
49
+ }
50
+ /** The pages of the transactions committed since the previous call (WAL frames). */
51
+ takeDirtyPages() {
52
+ if (!this.#pageCapture)
53
+ return undefined;
54
+ const walPath = `${this.#path}-wal`;
55
+ let fd;
56
+ try {
57
+ fd = openSync(walPath, "r");
58
+ }
59
+ catch {
60
+ return undefined;
61
+ }
62
+ try {
63
+ const size = fstatSync(fd).size;
64
+ if (size < 32)
65
+ return undefined;
66
+ const header = Buffer.alloc(32);
67
+ readSync(fd, header, 0, 32, 0);
68
+ const pageSize = header.readUInt32BE(8) === 1 ? 65536 : header.readUInt32BE(8);
69
+ const [salt1, salt2] = [header.readUInt32BE(16), header.readUInt32BE(20)];
70
+ const frameSize = 24 + pageSize;
71
+ let offset = Math.max(32, this.#walOffset);
72
+ const pending = new Map();
73
+ const pages = new Map();
74
+ let pageCount = 0;
75
+ let committedTo = offset;
76
+ const frame = Buffer.alloc(frameSize);
77
+ while (offset + frameSize <= size) {
78
+ readSync(fd, frame, 0, frameSize, offset);
79
+ if (frame.readUInt32BE(8) !== salt1 || frame.readUInt32BE(12) !== salt2)
80
+ break; // stale frames
81
+ pending.set(frame.readUInt32BE(0), new Uint8Array(frame.subarray(24)));
82
+ offset += frameSize;
83
+ const commit = frame.readUInt32BE(4);
84
+ if (commit) {
85
+ for (const [p, d] of pending)
86
+ pages.set(p, d);
87
+ pending.clear();
88
+ pageCount = commit;
89
+ committedTo = offset;
90
+ }
91
+ }
92
+ this.#walOffset = committedTo;
93
+ if (!pageCount)
94
+ return {
95
+ pageSize,
96
+ pageCount: Number(this.exec("PRAGMA page_count", []).rows[0][0]),
97
+ pages,
98
+ };
99
+ for (const p of [...pages.keys()])
100
+ if (p > pageCount)
101
+ pages.delete(p);
102
+ // keep the WAL bounded: fold it into the database and start over
103
+ if (committedTo > 4 * 1024 * 1024) {
104
+ this.#db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
105
+ this.#walOffset = 0;
106
+ }
107
+ return { pageSize, pageCount, pages };
108
+ }
109
+ finally {
110
+ closeSync(fd);
111
+ }
112
+ }
113
+ exec(sql, params) {
114
+ const statements = params.length === 0 ? splitSqlStatements(sql) : [sql];
115
+ // Several statements: the result of the last one, with the rows written by all of them.
116
+ let last = { columnNames: [], rows: [], rowsWritten: 0 };
117
+ let written = 0;
118
+ for (const text of statements) {
119
+ last = this.#one(text, params);
120
+ written += last.rowsWritten;
121
+ }
122
+ return { ...last, rowsWritten: written };
123
+ }
124
+ #one(sql, params) {
125
+ const stmt = this.#db.prepare(sql);
126
+ const columnNames = stmt.columns().map((c) => c.name);
127
+ const args = params.map(toSqlite);
128
+ if (columnNames.length === 0) {
129
+ const r = stmt.run(...args);
130
+ return { columnNames, rows: [], rowsWritten: Number(r.changes) };
131
+ }
132
+ stmt.setReturnArrays(true);
133
+ const rows = stmt.all(...args).map((row) => row.map(fromSqlite));
134
+ return { columnNames, rows, rowsWritten: 0 };
135
+ }
136
+ inTransaction() {
137
+ return this.#db.isTransaction;
138
+ }
139
+ databaseSize() {
140
+ const [pc] = this.exec("PRAGMA page_count", []).rows[0];
141
+ const [ps] = this.exec("PRAGMA page_size", []).rows[0];
142
+ return Number(pc) * Number(ps);
143
+ }
144
+ close() {
145
+ this.#db.close();
146
+ }
147
+ // ---- change capture (session extension) for transaction logs -------------------------
148
+ #session;
149
+ /** node:sqlite exposes no ROWID session option: PK-less tables are logged as statements. */
150
+ captureCoversRowidTables = false;
151
+ captureStart() {
152
+ this.#session?.close();
153
+ this.#session = this.#db.createSession();
154
+ }
155
+ captureTake() {
156
+ const s = this.#session;
157
+ this.#session = undefined;
158
+ if (!s)
159
+ return new Uint8Array();
160
+ try {
161
+ return s.changeset();
162
+ }
163
+ finally {
164
+ s.close();
165
+ }
166
+ }
167
+ captureDiscard() {
168
+ this.#session?.close();
169
+ this.#session = undefined;
170
+ }
171
+ applyChangeset(changeset) {
172
+ if (!changeset.byteLength)
173
+ return;
174
+ const SQLITE_CHANGESET_REPLACE = 1;
175
+ this.#db.applyChangeset(changeset, {
176
+ onConflict: () => SQLITE_CHANGESET_REPLACE,
177
+ });
178
+ }
179
+ /** Known extensions only: "sqlite_vec" → the sqlite-vec npm package's loadable library. */
180
+ loadExtension(name) {
181
+ const path = KNOWN_EXTENSIONS[name]?.();
182
+ if (!path)
183
+ throw new Error(`Unknown SQLite extension "${name}"`);
184
+ this.#db.enableLoadExtension(true);
185
+ try {
186
+ this.#db.loadExtension(path);
187
+ }
188
+ finally {
189
+ this.#db.enableLoadExtension(false);
190
+ }
191
+ }
192
+ /**
193
+ * The database file as SQLite wrote it (page for page), for page-level logs; an in-memory
194
+ * database falls back to image(). Valid between transactions (rollback journal mode).
195
+ */
196
+ pageImage() {
197
+ if (this.#path === ":memory:")
198
+ return this.image();
199
+ // A database restored from celld's files is in WAL mode (litestream needs it): its last
200
+ // commits are in the -wal file until a checkpoint moves them into the main file.
201
+ const mode = String(this.#db.prepare("PRAGMA journal_mode").get()?.journal_mode ?? "");
202
+ if (mode.toLowerCase() === "wal") {
203
+ this.#db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
204
+ this.#walOffset = 0; // the WAL starts over
205
+ }
206
+ return new Uint8Array(readFileSync(this.#path));
207
+ }
208
+ /** `VACUUM INTO` a temp file: a consistent, compact image (no open transaction allowed). */
209
+ image() {
210
+ const tmp = join(mkdtempSync(join(tmpdir(), "cell-snap-")), "db.sqlite");
211
+ try {
212
+ this.#db.exec(`VACUUM INTO '${tmp.replace(/'/g, "''")}'`);
213
+ return new Uint8Array(readFileSync(tmp));
214
+ }
215
+ finally {
216
+ rmSync(dirname(tmp), { recursive: true, force: true });
217
+ }
218
+ }
219
+ /** Replaces the database file with `image` and reopens it (an in-memory DB becomes file-backed). */
220
+ restore(image) {
221
+ this.#db.close();
222
+ if (this.#path === ":memory:")
223
+ this.#path = join(mkdtempSync(join(tmpdir(), "cell-mem-")), "db.sqlite");
224
+ for (const suffix of ["-wal", "-shm", "-journal"])
225
+ rmSync(this.#path + suffix, { force: true });
226
+ writeFileSync(this.#path, image);
227
+ this.#db = new DatabaseSync(this.#path, { enableDoubleQuotedStringLiterals: true });
228
+ if (this.#exclusive)
229
+ this.#db.exec("PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;");
230
+ this.#walSetup(); // a new file: a new WAL
231
+ }
232
+ }
233
+ function toSqlite(v) {
234
+ if (v instanceof ArrayBuffer)
235
+ return new Uint8Array(v);
236
+ if (typeof v === "boolean")
237
+ return v ? 1 : 0;
238
+ return v === undefined ? null : v;
239
+ }
240
+ /** Workers return BLOBs as ArrayBuffer. */
241
+ function fromSqlite(v) {
242
+ if (v instanceof Uint8Array)
243
+ return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);
244
+ return v;
245
+ }
246
+ /** One file per cell in `dir`, or in-memory when `dir` is omitted. */
247
+ /**
248
+ * `pageCapture` (file-backed only): WAL mode, and each commit's changed pages read from the WAL
249
+ * (takeDirtyPages) — LTX logs then cost O(changed pages) on Node as with SQLite WASM.
250
+ */
251
+ export function nodeSqliteOpener(dir, opts = {}) {
252
+ return (cellKey) => new NodeSqliteDriver(dir ? `${dir}/${cellKey.replace(/[^\w.-]/g, "_")}.sqlite` : ":memory:", opts);
253
+ }
254
+ //# sourceMappingURL=node-sqlite.js.map