@syncular/server 0.1.2 → 0.2.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 (758) hide show
  1. package/README.md +773 -13
  2. package/dist/admin.d.ts +136 -0
  3. package/dist/admin.js +168 -0
  4. package/dist/blob-handlers.d.ts +69 -0
  5. package/dist/blob-handlers.js +245 -0
  6. package/dist/blob-store.d.ts +111 -0
  7. package/dist/blob-store.js +0 -0
  8. package/dist/content-encoding.d.ts +27 -0
  9. package/dist/content-encoding.js +69 -0
  10. package/dist/context.d.ts +151 -0
  11. package/dist/context.js +21 -0
  12. package/dist/crdt-merger.d.ts +28 -0
  13. package/dist/crdt-merger.js +11 -0
  14. package/dist/d1-storage.d.ts +50 -0
  15. package/dist/d1-storage.js +0 -0
  16. package/dist/errors.d.ts +35 -9
  17. package/dist/errors.js +220 -11
  18. package/dist/events-ring.d.ts +55 -0
  19. package/dist/events-ring.js +96 -0
  20. package/dist/events.d.ts +231 -0
  21. package/dist/events.js +22 -0
  22. package/dist/frame-bytes.d.ts +20 -0
  23. package/dist/frame-bytes.js +75 -0
  24. package/dist/handler.d.ts +9 -0
  25. package/dist/handler.js +490 -0
  26. package/dist/index.d.ts +42 -33
  27. package/dist/index.js +45 -27
  28. package/dist/lease-store.d.ts +50 -0
  29. package/dist/lease-store.js +0 -0
  30. package/dist/pg-executor-pglite.d.ts +27 -0
  31. package/dist/pg-executor-pglite.js +33 -0
  32. package/dist/pg-executor.d.ts +67 -0
  33. package/dist/pg-executor.js +56 -0
  34. package/dist/postgres-fanout.d.ts +83 -0
  35. package/dist/postgres-fanout.js +71 -0
  36. package/dist/postgres-storage.d.ts +69 -0
  37. package/dist/postgres-storage.js +766 -0
  38. package/dist/prune.d.ts +24 -39
  39. package/dist/prune.js +44 -146
  40. package/dist/pull.d.ts +42 -67
  41. package/dist/pull.js +387 -1187
  42. package/dist/push.d.ts +31 -66
  43. package/dist/push.js +406 -655
  44. package/dist/realtime.d.ts +197 -0
  45. package/dist/realtime.js +848 -0
  46. package/dist/relational-rows.d.ts +229 -0
  47. package/dist/relational-rows.js +514 -0
  48. package/dist/s3-blob-store.d.ts +142 -0
  49. package/dist/s3-blob-store.js +458 -0
  50. package/dist/s3-segment-store.d.ts +95 -0
  51. package/dist/s3-segment-store.js +380 -0
  52. package/dist/schema.d.ts +82 -286
  53. package/dist/schema.js +133 -9
  54. package/dist/scopes.d.ts +48 -0
  55. package/dist/scopes.js +98 -0
  56. package/dist/segment-download.d.ts +14 -0
  57. package/dist/segment-download.js +125 -0
  58. package/dist/segment-store.d.ts +81 -0
  59. package/dist/segment-store.js +63 -0
  60. package/dist/signed-url.d.ts +139 -0
  61. package/dist/signed-url.js +143 -0
  62. package/dist/sigv4.d.ts +97 -0
  63. package/dist/sigv4.js +161 -0
  64. package/dist/sqlite-blob-store.d.ts +22 -0
  65. package/dist/sqlite-blob-store.js +86 -0
  66. package/dist/sqlite-dialect.d.ts +101 -0
  67. package/dist/sqlite-dialect.js +250 -0
  68. package/dist/sqlite-image.d.ts +27 -0
  69. package/dist/sqlite-image.js +83 -0
  70. package/dist/sqlite-lease-store.d.ts +21 -0
  71. package/dist/sqlite-lease-store.js +94 -0
  72. package/dist/sqlite-segment-store.d.ts +25 -0
  73. package/dist/sqlite-segment-store.js +115 -0
  74. package/dist/sqlite-storage.d.ts +47 -0
  75. package/dist/sqlite-storage.js +477 -0
  76. package/dist/storage.d.ts +252 -0
  77. package/dist/storage.js +1 -0
  78. package/dist/validate.d.ts +83 -0
  79. package/dist/validate.js +44 -0
  80. package/package.json +17 -325
  81. package/src/admin.ts +312 -0
  82. package/src/blob-handlers.ts +340 -0
  83. package/src/blob-store.ts +0 -0
  84. package/src/content-encoding.ts +94 -0
  85. package/src/context.ts +176 -0
  86. package/src/crdt-merger.ts +33 -0
  87. package/src/d1-storage.ts +0 -0
  88. package/src/errors.ts +241 -19
  89. package/src/events-ring.ts +116 -0
  90. package/src/events.ts +291 -0
  91. package/src/frame-bytes.ts +94 -0
  92. package/src/handler.ts +678 -0
  93. package/src/index.ts +44 -27
  94. package/src/lease-store.ts +0 -0
  95. package/src/pg-executor-pglite.ts +63 -0
  96. package/src/pg-executor.ts +87 -0
  97. package/src/postgres-fanout.ts +143 -0
  98. package/src/postgres-storage.ts +1182 -0
  99. package/src/prune.ts +63 -289
  100. package/src/pull.ts +505 -1805
  101. package/src/push.ts +623 -1006
  102. package/src/realtime.ts +1082 -0
  103. package/src/relational-rows.ts +633 -0
  104. package/src/s3-blob-store.ts +616 -0
  105. package/src/s3-segment-store.ts +519 -0
  106. package/src/schema.ts +242 -281
  107. package/src/scopes.ts +127 -0
  108. package/src/segment-download.ts +169 -0
  109. package/src/segment-store.ts +154 -0
  110. package/src/signed-url.ts +308 -0
  111. package/src/sigv4.ts +267 -0
  112. package/src/sqlite-blob-store.ts +125 -0
  113. package/src/sqlite-dialect.ts +337 -0
  114. package/src/sqlite-image.ts +124 -0
  115. package/src/sqlite-lease-store.ts +144 -0
  116. package/src/sqlite-segment-store.ts +204 -0
  117. package/src/sqlite-storage.ts +792 -0
  118. package/src/storage.ts +316 -0
  119. package/src/validate.ts +123 -0
  120. package/dist/auth-leases.d.ts +0 -85
  121. package/dist/auth-leases.d.ts.map +0 -1
  122. package/dist/auth-leases.js +0 -368
  123. package/dist/auth-leases.js.map +0 -1
  124. package/dist/better-sqlite3.d.ts +0 -22
  125. package/dist/better-sqlite3.d.ts.map +0 -1
  126. package/dist/better-sqlite3.js +0 -16
  127. package/dist/better-sqlite3.js.map +0 -1
  128. package/dist/blobs/access.d.ts +0 -35
  129. package/dist/blobs/access.d.ts.map +0 -1
  130. package/dist/blobs/access.js +0 -149
  131. package/dist/blobs/access.js.map +0 -1
  132. package/dist/blobs/adapters/database.d.ts +0 -100
  133. package/dist/blobs/adapters/database.d.ts.map +0 -1
  134. package/dist/blobs/adapters/database.js +0 -247
  135. package/dist/blobs/adapters/database.js.map +0 -1
  136. package/dist/blobs/index.d.ts +0 -9
  137. package/dist/blobs/index.d.ts.map +0 -1
  138. package/dist/blobs/index.js +0 -9
  139. package/dist/blobs/index.js.map +0 -1
  140. package/dist/blobs/manager.d.ts +0 -220
  141. package/dist/blobs/manager.d.ts.map +0 -1
  142. package/dist/blobs/manager.js +0 -625
  143. package/dist/blobs/manager.js.map +0 -1
  144. package/dist/blobs/migrate.d.ts +0 -27
  145. package/dist/blobs/migrate.d.ts.map +0 -1
  146. package/dist/blobs/migrate.js +0 -127
  147. package/dist/blobs/migrate.js.map +0 -1
  148. package/dist/blobs/types.d.ts +0 -58
  149. package/dist/blobs/types.d.ts.map +0 -1
  150. package/dist/blobs/types.js +0 -5
  151. package/dist/blobs/types.js.map +0 -1
  152. package/dist/bun-sqlite.d.ts +0 -19
  153. package/dist/bun-sqlite.d.ts.map +0 -1
  154. package/dist/bun-sqlite.js +0 -19
  155. package/dist/bun-sqlite.js.map +0 -1
  156. package/dist/clients.d.ts +0 -15
  157. package/dist/clients.d.ts.map +0 -1
  158. package/dist/clients.js +0 -7
  159. package/dist/clients.js.map +0 -1
  160. package/dist/cloudflare/durable-object.d.ts +0 -93
  161. package/dist/cloudflare/durable-object.d.ts.map +0 -1
  162. package/dist/cloudflare/durable-object.js +0 -210
  163. package/dist/cloudflare/durable-object.js.map +0 -1
  164. package/dist/cloudflare/index.d.ts +0 -22
  165. package/dist/cloudflare/index.d.ts.map +0 -1
  166. package/dist/cloudflare/index.js +0 -22
  167. package/dist/cloudflare/index.js.map +0 -1
  168. package/dist/cloudflare/r2.d.ts +0 -180
  169. package/dist/cloudflare/r2.d.ts.map +0 -1
  170. package/dist/cloudflare/r2.js +0 -258
  171. package/dist/cloudflare/r2.js.map +0 -1
  172. package/dist/cloudflare/scope-cache.d.ts +0 -54
  173. package/dist/cloudflare/scope-cache.d.ts.map +0 -1
  174. package/dist/cloudflare/scope-cache.js +0 -223
  175. package/dist/cloudflare/scope-cache.js.map +0 -1
  176. package/dist/cloudflare/sentry.d.ts +0 -47
  177. package/dist/cloudflare/sentry.d.ts.map +0 -1
  178. package/dist/cloudflare/sentry.js +0 -163
  179. package/dist/cloudflare/sentry.js.map +0 -1
  180. package/dist/cloudflare/worker.d.ts +0 -46
  181. package/dist/cloudflare/worker.d.ts.map +0 -1
  182. package/dist/cloudflare/worker.js +0 -63
  183. package/dist/cloudflare/worker.js.map +0 -1
  184. package/dist/commit-integrity.d.ts +0 -38
  185. package/dist/commit-integrity.d.ts.map +0 -1
  186. package/dist/commit-integrity.js +0 -260
  187. package/dist/commit-integrity.js.map +0 -1
  188. package/dist/compaction.d.ts +0 -27
  189. package/dist/compaction.d.ts.map +0 -1
  190. package/dist/compaction.js +0 -49
  191. package/dist/compaction.js.map +0 -1
  192. package/dist/crdt-yjs/index.d.ts +0 -99
  193. package/dist/crdt-yjs/index.d.ts.map +0 -1
  194. package/dist/crdt-yjs/index.js +0 -629
  195. package/dist/crdt-yjs/index.js.map +0 -1
  196. package/dist/d1.d.ts +0 -13
  197. package/dist/d1.d.ts.map +0 -1
  198. package/dist/d1.js +0 -14
  199. package/dist/d1.js.map +0 -1
  200. package/dist/dialect/base.d.ts +0 -93
  201. package/dist/dialect/base.d.ts.map +0 -1
  202. package/dist/dialect/base.js +0 -181
  203. package/dist/dialect/base.js.map +0 -1
  204. package/dist/dialect/helpers.d.ts +0 -16
  205. package/dist/dialect/helpers.d.ts.map +0 -1
  206. package/dist/dialect/helpers.js +0 -83
  207. package/dist/dialect/helpers.js.map +0 -1
  208. package/dist/dialect/index.d.ts +0 -7
  209. package/dist/dialect/index.d.ts.map +0 -1
  210. package/dist/dialect/index.js +0 -7
  211. package/dist/dialect/index.js.map +0 -1
  212. package/dist/dialect/types.d.ts +0 -187
  213. package/dist/dialect/types.d.ts.map +0 -1
  214. package/dist/dialect/types.js +0 -8
  215. package/dist/dialect/types.js.map +0 -1
  216. package/dist/encrypted-crdt.d.ts +0 -67
  217. package/dist/encrypted-crdt.d.ts.map +0 -1
  218. package/dist/encrypted-crdt.js +0 -426
  219. package/dist/encrypted-crdt.js.map +0 -1
  220. package/dist/errors.d.ts.map +0 -1
  221. package/dist/errors.js.map +0 -1
  222. package/dist/filesystem/index.d.ts +0 -44
  223. package/dist/filesystem/index.d.ts.map +0 -1
  224. package/dist/filesystem/index.js +0 -164
  225. package/dist/filesystem/index.js.map +0 -1
  226. package/dist/handlers/collection.d.ts +0 -20
  227. package/dist/handlers/collection.d.ts.map +0 -1
  228. package/dist/handlers/collection.js +0 -42
  229. package/dist/handlers/collection.js.map +0 -1
  230. package/dist/handlers/create-handler.d.ts +0 -156
  231. package/dist/handlers/create-handler.d.ts.map +0 -1
  232. package/dist/handlers/create-handler.js +0 -626
  233. package/dist/handlers/create-handler.js.map +0 -1
  234. package/dist/handlers/index.d.ts +0 -4
  235. package/dist/handlers/index.d.ts.map +0 -1
  236. package/dist/handlers/index.js +0 -4
  237. package/dist/handlers/index.js.map +0 -1
  238. package/dist/handlers/types.d.ts +0 -295
  239. package/dist/handlers/types.d.ts.map +0 -1
  240. package/dist/handlers/types.js +0 -2
  241. package/dist/handlers/types.js.map +0 -1
  242. package/dist/helpers/conflict.d.ts +0 -52
  243. package/dist/helpers/conflict.d.ts.map +0 -1
  244. package/dist/helpers/conflict.js +0 -49
  245. package/dist/helpers/conflict.js.map +0 -1
  246. package/dist/helpers/emitted-change.d.ts +0 -56
  247. package/dist/helpers/emitted-change.d.ts.map +0 -1
  248. package/dist/helpers/emitted-change.js +0 -46
  249. package/dist/helpers/emitted-change.js.map +0 -1
  250. package/dist/helpers/index.d.ts +0 -12
  251. package/dist/helpers/index.d.ts.map +0 -1
  252. package/dist/helpers/index.js +0 -12
  253. package/dist/helpers/index.js.map +0 -1
  254. package/dist/helpers/paginate.d.ts +0 -49
  255. package/dist/helpers/paginate.d.ts.map +0 -1
  256. package/dist/helpers/paginate.js +0 -54
  257. package/dist/helpers/paginate.js.map +0 -1
  258. package/dist/helpers/scope-authorization.d.ts +0 -7
  259. package/dist/helpers/scope-authorization.d.ts.map +0 -1
  260. package/dist/helpers/scope-authorization.js +0 -19
  261. package/dist/helpers/scope-authorization.js.map +0 -1
  262. package/dist/helpers/scope-commit-index.d.ts +0 -12
  263. package/dist/helpers/scope-commit-index.d.ts.map +0 -1
  264. package/dist/helpers/scope-commit-index.js +0 -35
  265. package/dist/helpers/scope-commit-index.js.map +0 -1
  266. package/dist/helpers/scope-strings.d.ts +0 -74
  267. package/dist/helpers/scope-strings.d.ts.map +0 -1
  268. package/dist/helpers/scope-strings.js +0 -82
  269. package/dist/helpers/scope-strings.js.map +0 -1
  270. package/dist/hono/api-key-auth.d.ts +0 -49
  271. package/dist/hono/api-key-auth.d.ts.map +0 -1
  272. package/dist/hono/api-key-auth.js +0 -108
  273. package/dist/hono/api-key-auth.js.map +0 -1
  274. package/dist/hono/audit-redaction.d.ts +0 -20
  275. package/dist/hono/audit-redaction.d.ts.map +0 -1
  276. package/dist/hono/audit-redaction.js +0 -85
  277. package/dist/hono/audit-redaction.js.map +0 -1
  278. package/dist/hono/blobs.d.ts +0 -75
  279. package/dist/hono/blobs.d.ts.map +0 -1
  280. package/dist/hono/blobs.js +0 -586
  281. package/dist/hono/blobs.js.map +0 -1
  282. package/dist/hono/console/gateway.d.ts +0 -42
  283. package/dist/hono/console/gateway.d.ts.map +0 -1
  284. package/dist/hono/console/gateway.js +0 -2158
  285. package/dist/hono/console/gateway.js.map +0 -1
  286. package/dist/hono/console/live-auth.d.ts +0 -7
  287. package/dist/hono/console/live-auth.d.ts.map +0 -1
  288. package/dist/hono/console/live-auth.js +0 -39
  289. package/dist/hono/console/live-auth.js.map +0 -1
  290. package/dist/hono/console/route-descriptor.d.ts +0 -6
  291. package/dist/hono/console/route-descriptor.d.ts.map +0 -1
  292. package/dist/hono/console/route-descriptor.js +0 -16
  293. package/dist/hono/console/route-descriptor.js.map +0 -1
  294. package/dist/hono/console/routes/api-keys.d.ts +0 -8
  295. package/dist/hono/console/routes/api-keys.d.ts.map +0 -1
  296. package/dist/hono/console/routes/api-keys.js +0 -575
  297. package/dist/hono/console/routes/api-keys.js.map +0 -1
  298. package/dist/hono/console/routes/clients.d.ts +0 -8
  299. package/dist/hono/console/routes/clients.d.ts.map +0 -1
  300. package/dist/hono/console/routes/clients.js +0 -322
  301. package/dist/hono/console/routes/clients.js.map +0 -1
  302. package/dist/hono/console/routes/commits.d.ts +0 -8
  303. package/dist/hono/console/routes/commits.d.ts.map +0 -1
  304. package/dist/hono/console/routes/commits.js +0 -882
  305. package/dist/hono/console/routes/commits.js.map +0 -1
  306. package/dist/hono/console/routes/context.d.ts +0 -188
  307. package/dist/hono/console/routes/context.d.ts.map +0 -1
  308. package/dist/hono/console/routes/context.js +0 -633
  309. package/dist/hono/console/routes/context.js.map +0 -1
  310. package/dist/hono/console/routes/events.d.ts +0 -8
  311. package/dist/hono/console/routes/events.d.ts.map +0 -1
  312. package/dist/hono/console/routes/events.js +0 -501
  313. package/dist/hono/console/routes/events.js.map +0 -1
  314. package/dist/hono/console/routes/maintenance.d.ts +0 -8
  315. package/dist/hono/console/routes/maintenance.d.ts.map +0 -1
  316. package/dist/hono/console/routes/maintenance.js +0 -341
  317. package/dist/hono/console/routes/maintenance.js.map +0 -1
  318. package/dist/hono/console/routes/shared.d.ts +0 -174
  319. package/dist/hono/console/routes/shared.d.ts.map +0 -1
  320. package/dist/hono/console/routes/shared.js +0 -583
  321. package/dist/hono/console/routes/shared.js.map +0 -1
  322. package/dist/hono/console/routes/stats.d.ts +0 -8
  323. package/dist/hono/console/routes/stats.d.ts.map +0 -1
  324. package/dist/hono/console/routes/stats.js +0 -295
  325. package/dist/hono/console/routes/stats.js.map +0 -1
  326. package/dist/hono/console/routes/storage.d.ts +0 -8
  327. package/dist/hono/console/routes/storage.d.ts.map +0 -1
  328. package/dist/hono/console/routes/storage.js +0 -121
  329. package/dist/hono/console/routes/storage.js.map +0 -1
  330. package/dist/hono/console/routes.d.ts +0 -36
  331. package/dist/hono/console/routes.d.ts.map +0 -1
  332. package/dist/hono/console/routes.js +0 -112
  333. package/dist/hono/console/routes.js.map +0 -1
  334. package/dist/hono/console/schema-errors.d.ts +0 -2
  335. package/dist/hono/console/schema-errors.d.ts.map +0 -1
  336. package/dist/hono/console/schema-errors.js +0 -17
  337. package/dist/hono/console/schema-errors.js.map +0 -1
  338. package/dist/hono/console/schemas.d.ts +0 -1515
  339. package/dist/hono/console/schemas.d.ts.map +0 -1
  340. package/dist/hono/console/schemas.js +0 -661
  341. package/dist/hono/console/schemas.js.map +0 -1
  342. package/dist/hono/console/types.d.ts +0 -213
  343. package/dist/hono/console/types.d.ts.map +0 -1
  344. package/dist/hono/console/types.js +0 -2
  345. package/dist/hono/console/types.js.map +0 -1
  346. package/dist/hono/console/ui.d.ts +0 -38
  347. package/dist/hono/console/ui.d.ts.map +0 -1
  348. package/dist/hono/console/ui.js +0 -43
  349. package/dist/hono/console/ui.js.map +0 -1
  350. package/dist/hono/create-server.d.ts +0 -71
  351. package/dist/hono/create-server.d.ts.map +0 -1
  352. package/dist/hono/create-server.js +0 -121
  353. package/dist/hono/create-server.js.map +0 -1
  354. package/dist/hono/errors.d.ts +0 -13
  355. package/dist/hono/errors.d.ts.map +0 -1
  356. package/dist/hono/errors.js +0 -21
  357. package/dist/hono/errors.js.map +0 -1
  358. package/dist/hono/index.d.ts +0 -20
  359. package/dist/hono/index.d.ts.map +0 -1
  360. package/dist/hono/index.js +0 -31
  361. package/dist/hono/index.js.map +0 -1
  362. package/dist/hono/openapi.d.ts +0 -72
  363. package/dist/hono/openapi.d.ts.map +0 -1
  364. package/dist/hono/openapi.js +0 -99
  365. package/dist/hono/openapi.js.map +0 -1
  366. package/dist/hono/proxy/connection-manager.d.ts +0 -78
  367. package/dist/hono/proxy/connection-manager.d.ts.map +0 -1
  368. package/dist/hono/proxy/connection-manager.js +0 -251
  369. package/dist/hono/proxy/connection-manager.js.map +0 -1
  370. package/dist/hono/proxy/index.d.ts +0 -8
  371. package/dist/hono/proxy/index.d.ts.map +0 -1
  372. package/dist/hono/proxy/index.js +0 -8
  373. package/dist/hono/proxy/index.js.map +0 -1
  374. package/dist/hono/proxy/routes.d.ts +0 -86
  375. package/dist/hono/proxy/routes.d.ts.map +0 -1
  376. package/dist/hono/proxy/routes.js +0 -183
  377. package/dist/hono/proxy/routes.js.map +0 -1
  378. package/dist/hono/rate-limit.d.ts +0 -101
  379. package/dist/hono/rate-limit.d.ts.map +0 -1
  380. package/dist/hono/rate-limit.js +0 -184
  381. package/dist/hono/rate-limit.js.map +0 -1
  382. package/dist/hono/realtime-sync-packs.d.ts +0 -43
  383. package/dist/hono/realtime-sync-packs.d.ts.map +0 -1
  384. package/dist/hono/realtime-sync-packs.js +0 -219
  385. package/dist/hono/realtime-sync-packs.js.map +0 -1
  386. package/dist/hono/routes/audit.d.ts +0 -12
  387. package/dist/hono/routes/audit.d.ts.map +0 -1
  388. package/dist/hono/routes/audit.js +0 -385
  389. package/dist/hono/routes/audit.js.map +0 -1
  390. package/dist/hono/routes/auth-leases.d.ts +0 -8
  391. package/dist/hono/routes/auth-leases.d.ts.map +0 -1
  392. package/dist/hono/routes/auth-leases.js +0 -88
  393. package/dist/hono/routes/auth-leases.js.map +0 -1
  394. package/dist/hono/routes/combined.d.ts +0 -8
  395. package/dist/hono/routes/combined.d.ts.map +0 -1
  396. package/dist/hono/routes/combined.js +0 -392
  397. package/dist/hono/routes/combined.js.map +0 -1
  398. package/dist/hono/routes/context.d.ts +0 -314
  399. package/dist/hono/routes/context.d.ts.map +0 -1
  400. package/dist/hono/routes/context.js +0 -1147
  401. package/dist/hono/routes/context.js.map +0 -1
  402. package/dist/hono/routes/health.d.ts +0 -8
  403. package/dist/hono/routes/health.d.ts.map +0 -1
  404. package/dist/hono/routes/health.js +0 -16
  405. package/dist/hono/routes/health.js.map +0 -1
  406. package/dist/hono/routes/realtime.d.ts +0 -9
  407. package/dist/hono/routes/realtime.d.ts.map +0 -1
  408. package/dist/hono/routes/realtime.js +0 -626
  409. package/dist/hono/routes/realtime.js.map +0 -1
  410. package/dist/hono/routes/shared.d.ts +0 -689
  411. package/dist/hono/routes/shared.d.ts.map +0 -1
  412. package/dist/hono/routes/shared.js +0 -972
  413. package/dist/hono/routes/shared.js.map +0 -1
  414. package/dist/hono/routes/snapshots.d.ts +0 -10
  415. package/dist/hono/routes/snapshots.d.ts.map +0 -1
  416. package/dist/hono/routes/snapshots.js +0 -281
  417. package/dist/hono/routes/snapshots.js.map +0 -1
  418. package/dist/hono/routes.d.ts +0 -19
  419. package/dist/hono/routes.d.ts.map +0 -1
  420. package/dist/hono/routes.js +0 -37
  421. package/dist/hono/routes.js.map +0 -1
  422. package/dist/hono/validation.d.ts +0 -5
  423. package/dist/hono/validation.d.ts.map +0 -1
  424. package/dist/hono/validation.js +0 -47
  425. package/dist/hono/validation.js.map +0 -1
  426. package/dist/hono/websocket-origin.d.ts +0 -9
  427. package/dist/hono/websocket-origin.d.ts.map +0 -1
  428. package/dist/hono/websocket-origin.js +0 -96
  429. package/dist/hono/websocket-origin.js.map +0 -1
  430. package/dist/hono/ws.d.ts +0 -341
  431. package/dist/hono/ws.d.ts.map +0 -1
  432. package/dist/hono/ws.js +0 -711
  433. package/dist/hono/ws.js.map +0 -1
  434. package/dist/index.d.ts.map +0 -1
  435. package/dist/index.js.map +0 -1
  436. package/dist/libsql.d.ts +0 -29
  437. package/dist/libsql.d.ts.map +0 -1
  438. package/dist/libsql.js +0 -25
  439. package/dist/libsql.js.map +0 -1
  440. package/dist/migrate.d.ts +0 -14
  441. package/dist/migrate.d.ts.map +0 -1
  442. package/dist/migrate.js +0 -13
  443. package/dist/migrate.js.map +0 -1
  444. package/dist/neon.d.ts +0 -21
  445. package/dist/neon.d.ts.map +0 -1
  446. package/dist/neon.js +0 -22
  447. package/dist/neon.js.map +0 -1
  448. package/dist/notify.d.ts +0 -77
  449. package/dist/notify.d.ts.map +0 -1
  450. package/dist/notify.js +0 -233
  451. package/dist/notify.js.map +0 -1
  452. package/dist/pglite.d.ts +0 -37
  453. package/dist/pglite.d.ts.map +0 -1
  454. package/dist/pglite.js +0 -37
  455. package/dist/pglite.js.map +0 -1
  456. package/dist/plugins/index.d.ts +0 -2
  457. package/dist/plugins/index.d.ts.map +0 -1
  458. package/dist/plugins/index.js +0 -2
  459. package/dist/plugins/index.js.map +0 -1
  460. package/dist/plugins/types.d.ts +0 -73
  461. package/dist/plugins/types.d.ts.map +0 -1
  462. package/dist/plugins/types.js +0 -30
  463. package/dist/plugins/types.js.map +0 -1
  464. package/dist/postgres/index.d.ts +0 -68
  465. package/dist/postgres/index.d.ts.map +0 -1
  466. package/dist/postgres/index.js +0 -924
  467. package/dist/postgres/index.js.map +0 -1
  468. package/dist/proxy/collection.d.ts +0 -7
  469. package/dist/proxy/collection.d.ts.map +0 -1
  470. package/dist/proxy/collection.js +0 -6
  471. package/dist/proxy/collection.js.map +0 -1
  472. package/dist/proxy/handler.d.ts +0 -42
  473. package/dist/proxy/handler.d.ts.map +0 -1
  474. package/dist/proxy/handler.js +0 -102
  475. package/dist/proxy/handler.js.map +0 -1
  476. package/dist/proxy/index.d.ts +0 -9
  477. package/dist/proxy/index.d.ts.map +0 -1
  478. package/dist/proxy/index.js +0 -14
  479. package/dist/proxy/index.js.map +0 -1
  480. package/dist/proxy/mutation-detector.d.ts +0 -35
  481. package/dist/proxy/mutation-detector.d.ts.map +0 -1
  482. package/dist/proxy/mutation-detector.js +0 -246
  483. package/dist/proxy/mutation-detector.js.map +0 -1
  484. package/dist/proxy/oplog.d.ts +0 -30
  485. package/dist/proxy/oplog.d.ts.map +0 -1
  486. package/dist/proxy/oplog.js +0 -137
  487. package/dist/proxy/oplog.js.map +0 -1
  488. package/dist/proxy/types.d.ts +0 -44
  489. package/dist/proxy/types.d.ts.map +0 -1
  490. package/dist/proxy/types.js +0 -7
  491. package/dist/proxy/types.js.map +0 -1
  492. package/dist/prune.d.ts.map +0 -1
  493. package/dist/prune.js.map +0 -1
  494. package/dist/pull.d.ts.map +0 -1
  495. package/dist/pull.js.map +0 -1
  496. package/dist/push.d.ts.map +0 -1
  497. package/dist/push.js.map +0 -1
  498. package/dist/realtime/in-memory.d.ts +0 -13
  499. package/dist/realtime/in-memory.d.ts.map +0 -1
  500. package/dist/realtime/in-memory.js +0 -28
  501. package/dist/realtime/in-memory.js.map +0 -1
  502. package/dist/realtime/index.d.ts +0 -4
  503. package/dist/realtime/index.d.ts.map +0 -1
  504. package/dist/realtime/index.js +0 -3
  505. package/dist/realtime/index.js.map +0 -1
  506. package/dist/realtime/types.d.ts +0 -62
  507. package/dist/realtime/types.d.ts.map +0 -1
  508. package/dist/realtime/types.js +0 -19
  509. package/dist/realtime/types.js.map +0 -1
  510. package/dist/relay/client-role/forward-engine.d.ts +0 -63
  511. package/dist/relay/client-role/forward-engine.d.ts.map +0 -1
  512. package/dist/relay/client-role/forward-engine.js +0 -267
  513. package/dist/relay/client-role/forward-engine.js.map +0 -1
  514. package/dist/relay/client-role/index.d.ts +0 -9
  515. package/dist/relay/client-role/index.d.ts.map +0 -1
  516. package/dist/relay/client-role/index.js +0 -9
  517. package/dist/relay/client-role/index.js.map +0 -1
  518. package/dist/relay/client-role/pull-engine.d.ts +0 -72
  519. package/dist/relay/client-role/pull-engine.d.ts.map +0 -1
  520. package/dist/relay/client-role/pull-engine.js +0 -249
  521. package/dist/relay/client-role/pull-engine.js.map +0 -1
  522. package/dist/relay/client-role/sequence-mapper.d.ts +0 -65
  523. package/dist/relay/client-role/sequence-mapper.d.ts.map +0 -1
  524. package/dist/relay/client-role/sequence-mapper.js +0 -161
  525. package/dist/relay/client-role/sequence-mapper.js.map +0 -1
  526. package/dist/relay/evaluation/relay-paths.d.ts +0 -41
  527. package/dist/relay/evaluation/relay-paths.d.ts.map +0 -1
  528. package/dist/relay/evaluation/relay-paths.js +0 -504
  529. package/dist/relay/evaluation/relay-paths.js.map +0 -1
  530. package/dist/relay/evaluation/rust-boundary.d.ts +0 -47
  531. package/dist/relay/evaluation/rust-boundary.d.ts.map +0 -1
  532. package/dist/relay/evaluation/rust-boundary.js +0 -220
  533. package/dist/relay/evaluation/rust-boundary.js.map +0 -1
  534. package/dist/relay/index.d.ts +0 -37
  535. package/dist/relay/index.d.ts.map +0 -1
  536. package/dist/relay/index.js +0 -44
  537. package/dist/relay/index.js.map +0 -1
  538. package/dist/relay/migrate.d.ts +0 -18
  539. package/dist/relay/migrate.d.ts.map +0 -1
  540. package/dist/relay/migrate.js +0 -99
  541. package/dist/relay/migrate.js.map +0 -1
  542. package/dist/relay/mode-manager.d.ts +0 -60
  543. package/dist/relay/mode-manager.d.ts.map +0 -1
  544. package/dist/relay/mode-manager.js +0 -114
  545. package/dist/relay/mode-manager.js.map +0 -1
  546. package/dist/relay/realtime.d.ts +0 -90
  547. package/dist/relay/realtime.d.ts.map +0 -1
  548. package/dist/relay/realtime.js +0 -147
  549. package/dist/relay/realtime.js.map +0 -1
  550. package/dist/relay/relay.d.ts +0 -190
  551. package/dist/relay/relay.d.ts.map +0 -1
  552. package/dist/relay/relay.js +0 -320
  553. package/dist/relay/relay.js.map +0 -1
  554. package/dist/relay/schema.d.ts +0 -158
  555. package/dist/relay/schema.d.ts.map +0 -1
  556. package/dist/relay/schema.js +0 -7
  557. package/dist/relay/schema.js.map +0 -1
  558. package/dist/relay/server-role/index.d.ts +0 -56
  559. package/dist/relay/server-role/index.d.ts.map +0 -1
  560. package/dist/relay/server-role/index.js +0 -193
  561. package/dist/relay/server-role/index.js.map +0 -1
  562. package/dist/relay/server-role/pull.d.ts +0 -27
  563. package/dist/relay/server-role/pull.d.ts.map +0 -1
  564. package/dist/relay/server-role/pull.js +0 -24
  565. package/dist/relay/server-role/pull.js.map +0 -1
  566. package/dist/relay/server-role/push.d.ts +0 -29
  567. package/dist/relay/server-role/push.d.ts.map +0 -1
  568. package/dist/relay/server-role/push.js +0 -122
  569. package/dist/relay/server-role/push.js.map +0 -1
  570. package/dist/s3/index.d.ts +0 -83
  571. package/dist/s3/index.d.ts.map +0 -1
  572. package/dist/s3/index.js +0 -223
  573. package/dist/s3/index.js.map +0 -1
  574. package/dist/schema.d.ts.map +0 -1
  575. package/dist/schema.js.map +0 -1
  576. package/dist/service-worker/index.d.ts +0 -114
  577. package/dist/service-worker/index.d.ts.map +0 -1
  578. package/dist/service-worker/index.js +0 -487
  579. package/dist/service-worker/index.js.map +0 -1
  580. package/dist/snapshot-artifacts/sqlite-bun.d.ts +0 -15
  581. package/dist/snapshot-artifacts/sqlite-bun.d.ts.map +0 -1
  582. package/dist/snapshot-artifacts/sqlite-bun.js +0 -128
  583. package/dist/snapshot-artifacts/sqlite-bun.js.map +0 -1
  584. package/dist/snapshot-artifacts.d.ts +0 -206
  585. package/dist/snapshot-artifacts.d.ts.map +0 -1
  586. package/dist/snapshot-artifacts.js +0 -545
  587. package/dist/snapshot-artifacts.js.map +0 -1
  588. package/dist/snapshot-chunks/db-metadata.d.ts +0 -56
  589. package/dist/snapshot-chunks/db-metadata.d.ts.map +0 -1
  590. package/dist/snapshot-chunks/db-metadata.js +0 -360
  591. package/dist/snapshot-chunks/db-metadata.js.map +0 -1
  592. package/dist/snapshot-chunks/index.d.ts +0 -8
  593. package/dist/snapshot-chunks/index.d.ts.map +0 -1
  594. package/dist/snapshot-chunks/index.js +0 -8
  595. package/dist/snapshot-chunks/index.js.map +0 -1
  596. package/dist/snapshot-chunks/types.d.ts +0 -80
  597. package/dist/snapshot-chunks/types.d.ts.map +0 -1
  598. package/dist/snapshot-chunks/types.js +0 -8
  599. package/dist/snapshot-chunks/types.js.map +0 -1
  600. package/dist/snapshot-chunks.d.ts +0 -90
  601. package/dist/snapshot-chunks.d.ts.map +0 -1
  602. package/dist/snapshot-chunks.js +0 -304
  603. package/dist/snapshot-chunks.js.map +0 -1
  604. package/dist/sqlite/index.d.ts +0 -53
  605. package/dist/sqlite/index.d.ts.map +0 -1
  606. package/dist/sqlite/index.js +0 -795
  607. package/dist/sqlite/index.js.map +0 -1
  608. package/dist/sqlite3.d.ts +0 -22
  609. package/dist/sqlite3.d.ts.map +0 -1
  610. package/dist/sqlite3.js +0 -99
  611. package/dist/sqlite3.js.map +0 -1
  612. package/dist/stats.d.ts +0 -28
  613. package/dist/stats.d.ts.map +0 -1
  614. package/dist/stats.js +0 -93
  615. package/dist/stats.js.map +0 -1
  616. package/dist/subscriptions/cache.d.ts +0 -58
  617. package/dist/subscriptions/cache.d.ts.map +0 -1
  618. package/dist/subscriptions/cache.js +0 -250
  619. package/dist/subscriptions/cache.js.map +0 -1
  620. package/dist/subscriptions/index.d.ts +0 -3
  621. package/dist/subscriptions/index.d.ts.map +0 -1
  622. package/dist/subscriptions/index.js +0 -3
  623. package/dist/subscriptions/index.js.map +0 -1
  624. package/dist/subscriptions/resolve.d.ts +0 -40
  625. package/dist/subscriptions/resolve.d.ts.map +0 -1
  626. package/dist/subscriptions/resolve.js +0 -275
  627. package/dist/subscriptions/resolve.js.map +0 -1
  628. package/dist/sync.d.ts +0 -24
  629. package/dist/sync.d.ts.map +0 -1
  630. package/dist/sync.js +0 -27
  631. package/dist/sync.js.map +0 -1
  632. package/src/auth-leases.ts +0 -649
  633. package/src/better-sqlite3.ts +0 -35
  634. package/src/blobs/access.ts +0 -244
  635. package/src/blobs/adapters/database.ts +0 -397
  636. package/src/blobs/index.ts +0 -9
  637. package/src/blobs/manager.ts +0 -901
  638. package/src/blobs/migrate.ts +0 -158
  639. package/src/blobs/types.ts +0 -74
  640. package/src/bun-sqlite-ambient.d.ts +0 -19
  641. package/src/bun-sqlite.ts +0 -27
  642. package/src/clients.ts +0 -22
  643. package/src/cloudflare/durable-object.ts +0 -289
  644. package/src/cloudflare/index.ts +0 -22
  645. package/src/cloudflare/r2.ts +0 -526
  646. package/src/cloudflare/scope-cache.ts +0 -341
  647. package/src/cloudflare/sentry.ts +0 -230
  648. package/src/cloudflare/worker.ts +0 -77
  649. package/src/commit-integrity.ts +0 -371
  650. package/src/compaction.ts +0 -77
  651. package/src/crdt-yjs/index.ts +0 -931
  652. package/src/d1.ts +0 -16
  653. package/src/dialect/base.ts +0 -360
  654. package/src/dialect/helpers.ts +0 -92
  655. package/src/dialect/index.ts +0 -7
  656. package/src/dialect/types.ts +0 -247
  657. package/src/encrypted-crdt.ts +0 -786
  658. package/src/filesystem/index.ts +0 -262
  659. package/src/handlers/collection.ts +0 -121
  660. package/src/handlers/create-handler.ts +0 -1134
  661. package/src/handlers/index.ts +0 -3
  662. package/src/handlers/types.ts +0 -403
  663. package/src/helpers/conflict.ts +0 -64
  664. package/src/helpers/emitted-change.ts +0 -69
  665. package/src/helpers/index.ts +0 -12
  666. package/src/helpers/paginate.ts +0 -82
  667. package/src/helpers/scope-authorization.ts +0 -27
  668. package/src/helpers/scope-commit-index.ts +0 -52
  669. package/src/helpers/scope-strings.ts +0 -101
  670. package/src/hono/api-key-auth.ts +0 -177
  671. package/src/hono/audit-redaction.ts +0 -135
  672. package/src/hono/blobs.ts +0 -851
  673. package/src/hono/console/gateway.ts +0 -3046
  674. package/src/hono/console/live-auth.ts +0 -46
  675. package/src/hono/console/route-descriptor.ts +0 -22
  676. package/src/hono/console/routes/api-keys.ts +0 -721
  677. package/src/hono/console/routes/clients.ts +0 -447
  678. package/src/hono/console/routes/commits.ts +0 -1137
  679. package/src/hono/console/routes/context.ts +0 -956
  680. package/src/hono/console/routes/events.ts +0 -669
  681. package/src/hono/console/routes/maintenance.ts +0 -461
  682. package/src/hono/console/routes/shared.ts +0 -816
  683. package/src/hono/console/routes/stats.ts +0 -392
  684. package/src/hono/console/routes/storage.ts +0 -159
  685. package/src/hono/console/routes.ts +0 -146
  686. package/src/hono/console/schema-errors.ts +0 -23
  687. package/src/hono/console/schemas.ts +0 -914
  688. package/src/hono/console/types.ts +0 -223
  689. package/src/hono/console/ui.ts +0 -100
  690. package/src/hono/create-server.ts +0 -230
  691. package/src/hono/errors.ts +0 -50
  692. package/src/hono/index.ts +0 -54
  693. package/src/hono/openapi.ts +0 -139
  694. package/src/hono/proxy/connection-manager.ts +0 -340
  695. package/src/hono/proxy/index.ts +0 -8
  696. package/src/hono/proxy/routes.ts +0 -272
  697. package/src/hono/rate-limit.ts +0 -319
  698. package/src/hono/realtime-sync-packs.ts +0 -354
  699. package/src/hono/routes/audit.ts +0 -499
  700. package/src/hono/routes/auth-leases.ts +0 -119
  701. package/src/hono/routes/combined.ts +0 -583
  702. package/src/hono/routes/context.ts +0 -1629
  703. package/src/hono/routes/health.ts +0 -26
  704. package/src/hono/routes/realtime.ts +0 -808
  705. package/src/hono/routes/shared.ts +0 -1626
  706. package/src/hono/routes/snapshots.ts +0 -345
  707. package/src/hono/routes.ts +0 -68
  708. package/src/hono/validation.ts +0 -81
  709. package/src/hono/websocket-origin.ts +0 -131
  710. package/src/hono/ws.ts +0 -1134
  711. package/src/libsql.ts +0 -51
  712. package/src/migrate.ts +0 -20
  713. package/src/neon.ts +0 -28
  714. package/src/notify.ts +0 -341
  715. package/src/pglite.ts +0 -68
  716. package/src/plugins/index.ts +0 -1
  717. package/src/plugins/types.ts +0 -144
  718. package/src/postgres/index.ts +0 -1291
  719. package/src/proxy/collection.ts +0 -17
  720. package/src/proxy/handler.ts +0 -159
  721. package/src/proxy/index.ts +0 -21
  722. package/src/proxy/mutation-detector.ts +0 -281
  723. package/src/proxy/oplog.ts +0 -181
  724. package/src/proxy/types.ts +0 -46
  725. package/src/realtime/in-memory.ts +0 -33
  726. package/src/realtime/index.ts +0 -7
  727. package/src/realtime/types.ts +0 -90
  728. package/src/relay/bun-types.d.ts +0 -50
  729. package/src/relay/client-role/forward-engine.ts +0 -355
  730. package/src/relay/client-role/index.ts +0 -9
  731. package/src/relay/client-role/pull-engine.ts +0 -329
  732. package/src/relay/client-role/sequence-mapper.ts +0 -201
  733. package/src/relay/evaluation/relay-paths.ts +0 -699
  734. package/src/relay/evaluation/rust-boundary.ts +0 -464
  735. package/src/relay/index.ts +0 -50
  736. package/src/relay/migrate.ts +0 -113
  737. package/src/relay/mode-manager.ts +0 -142
  738. package/src/relay/realtime.ts +0 -207
  739. package/src/relay/relay.ts +0 -431
  740. package/src/relay/schema.ts +0 -171
  741. package/src/relay/server-role/index.ts +0 -338
  742. package/src/relay/server-role/pull.ts +0 -43
  743. package/src/relay/server-role/push.ts +0 -164
  744. package/src/s3/index.ts +0 -346
  745. package/src/service-worker/index.ts +0 -773
  746. package/src/snapshot-artifacts/sqlite-bun.ts +0 -168
  747. package/src/snapshot-artifacts.ts +0 -896
  748. package/src/snapshot-chunks/db-metadata.ts +0 -537
  749. package/src/snapshot-chunks/index.ts +0 -8
  750. package/src/snapshot-chunks/types.ts +0 -105
  751. package/src/snapshot-chunks.ts +0 -453
  752. package/src/sqlite/index.ts +0 -1064
  753. package/src/sqlite3.ts +0 -137
  754. package/src/stats.ts +0 -180
  755. package/src/subscriptions/cache.ts +0 -376
  756. package/src/subscriptions/index.ts +0 -2
  757. package/src/subscriptions/resolve.ts +0 -357
  758. package/src/sync.ts +0 -111
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Signed-URL segment delivery (SPEC.md §5.4): the native HMAC token
3
+ * scheme, plus the delegated-presign alternative (S3/R2/GCS) behind one
4
+ * issuance seam.
5
+ *
6
+ * Native: `st = base64url(payloadJson) + "." +
7
+ * base64url(HMAC-SHA256(key, payloadJson))` with claims
8
+ * `{v, seg, sd, aud, exp}`; `exp` is unix seconds. Issuance happens inside
9
+ * the pull immediately after scope resolution; verification checks MAC,
10
+ * expiry (≤ 60 s skew), and `seg`/`sd`/`aud` equality. Actual CDN serving
11
+ * is out of scope.
12
+ *
13
+ * Delegated presign: the provider mints the URL (§5.4 equivalence rule —
14
+ * the signed object key embeds the `segmentId`, the expiry obeys the same
15
+ * TTL guidance); `issueSegmentUrl` routes to whichever config the host
16
+ * supplied. Both paths issue only after scope resolution, so a URL is
17
+ * never minted for scopes the actor did not hold at that moment.
18
+ */
19
+ import { syncError } from './errors';
20
+
21
+ export interface SignedUrlConfig {
22
+ /** HMAC key (raw bytes or UTF-8 string). */
23
+ readonly key: string | Uint8Array;
24
+ /** URL prefix; the segment URL is `${baseUrl}/${segmentId}?st=${token}`. */
25
+ readonly baseUrl: string;
26
+ /** Token TTL; SHOULD be ≤ 15 minutes (§5.4). Default 900. */
27
+ readonly ttlSeconds?: number;
28
+ /**
29
+ * Opaque per-partition audience (§5.4): stable per partition, MUST NOT
30
+ * disclose the internal partition id.
31
+ */
32
+ readonly audience: (partition: string) => string;
33
+ }
34
+
35
+ /** What descriptor emission needs: the §5.4 `url`/`urlExpiresAtMs` pair. */
36
+ export interface SegmentUrlIssue {
37
+ readonly url: string;
38
+ readonly urlExpiresAtMs: number;
39
+ }
40
+
41
+ export interface SegmentPresignArgs {
42
+ readonly segmentId: string;
43
+ readonly partition: string;
44
+ readonly scopeDigest: string;
45
+ readonly nowMs: number;
46
+ /** Resolved TTL (config value or the 900 s default) — already applied. */
47
+ readonly ttlSeconds: number;
48
+ }
49
+
50
+ /**
51
+ * Delegated presign (§5.4): the provider signs the URL. The callback MUST
52
+ * satisfy the equivalence rule — the signed object key embeds exactly one
53
+ * `segmentId`, and `urlExpiresAtMs` reflects the real provider expiry.
54
+ * `S3SegmentStore.presignSegmentGet` (via `s3PresignedUrls`) is the
55
+ * in-tree implementation.
56
+ */
57
+ export interface DelegatedPresignConfig {
58
+ readonly presign: (
59
+ args: SegmentPresignArgs,
60
+ ) => SegmentUrlIssue | Promise<SegmentUrlIssue>;
61
+ /** URL TTL; SHOULD be ≤ 15 minutes (§5.4). Default 900. */
62
+ readonly ttlSeconds?: number;
63
+ }
64
+
65
+ /** Either §5.4 scheme; the pull emits descriptors identically for both. */
66
+ export type SegmentUrlConfig = SignedUrlConfig | DelegatedPresignConfig;
67
+
68
+ /**
69
+ * Issue the §5.4 `url`/`urlExpiresAtMs` pair for one segment descriptor,
70
+ * routing to the native HMAC token or the delegated presigner. Callers
71
+ * (the pull) invoke this only after scope resolution and only when the
72
+ * client advertised accept bit 3.
73
+ */
74
+ export async function issueSegmentUrl(
75
+ config: SegmentUrlConfig,
76
+ args: {
77
+ readonly segmentId: string;
78
+ readonly partition: string;
79
+ readonly scopeDigest: string;
80
+ readonly nowMs: number;
81
+ },
82
+ ): Promise<SegmentUrlIssue> {
83
+ const ttlSeconds = config.ttlSeconds ?? 900;
84
+ if ('presign' in config) {
85
+ return config.presign({ ...args, ttlSeconds });
86
+ }
87
+ const exp = Math.floor(args.nowMs / 1000) + ttlSeconds;
88
+ const token = await signSegmentToken(config.key, {
89
+ v: 1,
90
+ seg: args.segmentId,
91
+ sd: args.scopeDigest,
92
+ aud: config.audience(args.partition),
93
+ exp,
94
+ });
95
+ return {
96
+ url: `${config.baseUrl}/${args.segmentId}?st=${token}`,
97
+ urlExpiresAtMs: exp * 1000,
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Delegated presign for BLOB downloads (§5.9.5). The blob twin of
103
+ * `DelegatedPresignConfig`: the provider signs the URL and the signed object
104
+ * key embeds the `blobId`. Issuance happens ONLY after the §5.9.5
105
+ * row-derived authorization check (never before), so the URL is a short-TTL
106
+ * bearer grant to exactly those immutable bytes — the SPEC's "authorization
107
+ * was resolved against the referencing rows at issuance". Unlike segments,
108
+ * there is no native-HMAC blob token scheme wired this rung; presign is the
109
+ * shipped path (`S3BlobStore.presignBlobGet` via `s3PresignedBlobUrls`).
110
+ */
111
+ export interface BlobPresignArgs {
112
+ readonly partition: string;
113
+ readonly blobId: string;
114
+ readonly nowMs: number;
115
+ /** Resolved TTL (config value or the 900 s default) — already applied. */
116
+ readonly ttlSeconds: number;
117
+ }
118
+
119
+ export interface BlobPresignConfig {
120
+ readonly presign: (
121
+ args: BlobPresignArgs,
122
+ ) => SegmentUrlIssue | Promise<SegmentUrlIssue>;
123
+ /** URL TTL; SHOULD be ≤ 15 minutes (§5.9.5). Default 900. */
124
+ readonly ttlSeconds?: number;
125
+ }
126
+
127
+ /**
128
+ * Issue the §5.9.5 `url`/`urlExpiresAtMs` pair for one blob download. The
129
+ * caller (blob-handlers `downloadBlob`) invokes this only after the
130
+ * row-derived authorization check passes.
131
+ */
132
+ export async function issueBlobUrl(
133
+ config: BlobPresignConfig,
134
+ args: {
135
+ readonly partition: string;
136
+ readonly blobId: string;
137
+ readonly nowMs: number;
138
+ },
139
+ ): Promise<SegmentUrlIssue> {
140
+ const ttlSeconds = config.ttlSeconds ?? 900;
141
+ return config.presign({ ...args, ttlSeconds });
142
+ }
143
+
144
+ /**
145
+ * Delegated presign for BLOB UPLOADS (§5.9.3 direct-to-storage). The upload
146
+ * twin of `BlobPresignConfig`: the provider signs a single PUT whose object
147
+ * key embeds the `blobId`. Issuance happens ONLY after host authentication in
148
+ * the upload-grant handler (uploading is host-auth-only, not scope-bearing —
149
+ * §5.9.3). The `byteLength` rides through so a provider MAY bind
150
+ * `Content-Length` (S3 conditional) to the granted size. Never a multipart or
151
+ * chunk protocol — single PUT only, this rung and the next (§5.9.3 non-goal).
152
+ * `S3BlobStore.presignBlobPut` (via `s3PresignedBlobUploads`) is the in-tree
153
+ * implementation.
154
+ */
155
+ export interface BlobUploadPresignArgs {
156
+ readonly partition: string;
157
+ readonly blobId: string;
158
+ readonly byteLength: number;
159
+ readonly nowMs: number;
160
+ /** Resolved TTL (config value or the 900 s default) — already applied. */
161
+ readonly ttlSeconds: number;
162
+ }
163
+
164
+ export interface BlobUploadPresignConfig {
165
+ readonly presign: (
166
+ args: BlobUploadPresignArgs,
167
+ ) => SegmentUrlIssue | Promise<SegmentUrlIssue>;
168
+ /** URL TTL; SHOULD be ≤ 15 minutes (§5.9.3). Default 900. */
169
+ readonly ttlSeconds?: number;
170
+ }
171
+
172
+ /**
173
+ * Issue the §5.9.3 `url`/`urlExpiresAtMs` pair for one presigned blob upload.
174
+ * The caller (blob-handlers `handleBlobUploadGrant`) invokes this only after
175
+ * host authentication and the size-cap check against the declared byteLength.
176
+ */
177
+ export async function issueBlobUploadUrl(
178
+ config: BlobUploadPresignConfig,
179
+ args: {
180
+ readonly partition: string;
181
+ readonly blobId: string;
182
+ readonly byteLength: number;
183
+ readonly nowMs: number;
184
+ },
185
+ ): Promise<SegmentUrlIssue> {
186
+ const ttlSeconds = config.ttlSeconds ?? 900;
187
+ return config.presign({ ...args, ttlSeconds });
188
+ }
189
+
190
+ export interface SegmentTokenClaims {
191
+ readonly v: 1;
192
+ readonly seg: string;
193
+ readonly sd: string;
194
+ readonly aud: string;
195
+ /** Unix seconds — the sole non-millisecond timestamp in the spec. */
196
+ readonly exp: number;
197
+ }
198
+
199
+ const encoder = new TextEncoder();
200
+
201
+ // Runtime-neutral base64url (no `Buffer` — Workers/Deno/browser safe; TODO
202
+ // §4.2): `btoa`/`atob` operate on binary strings, present in every runtime.
203
+ function base64url(bytes: Uint8Array): string {
204
+ let binary = '';
205
+ for (const byte of bytes) binary += String.fromCharCode(byte);
206
+ return btoa(binary)
207
+ .replaceAll('+', '-')
208
+ .replaceAll('/', '_')
209
+ .replace(/=+$/, '');
210
+ }
211
+
212
+ function fromBase64url(text: string): Uint8Array {
213
+ const binary = atob(text.replaceAll('-', '+').replaceAll('_', '/'));
214
+ const bytes = new Uint8Array(binary.length);
215
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
216
+ return bytes;
217
+ }
218
+
219
+ async function hmac(
220
+ key: string | Uint8Array,
221
+ payload: Uint8Array,
222
+ ): Promise<Uint8Array> {
223
+ const keyBytes = typeof key === 'string' ? encoder.encode(key) : key;
224
+ const cryptoKey = await crypto.subtle.importKey(
225
+ 'raw',
226
+ keyBytes.slice().buffer as ArrayBuffer,
227
+ { name: 'HMAC', hash: 'SHA-256' },
228
+ false,
229
+ ['sign'],
230
+ );
231
+ const mac = await crypto.subtle.sign(
232
+ 'HMAC',
233
+ cryptoKey,
234
+ payload.slice().buffer as ArrayBuffer,
235
+ );
236
+ return new Uint8Array(mac);
237
+ }
238
+
239
+ function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {
240
+ if (a.length !== b.length) return false;
241
+ let diff = 0;
242
+ for (let i = 0; i < a.length; i++) diff |= (a[i] ?? 0) ^ (b[i] ?? 0);
243
+ return diff === 0;
244
+ }
245
+
246
+ export async function signSegmentToken(
247
+ key: string | Uint8Array,
248
+ claims: SegmentTokenClaims,
249
+ ): Promise<string> {
250
+ const payloadJson = JSON.stringify({
251
+ v: claims.v,
252
+ seg: claims.seg,
253
+ sd: claims.sd,
254
+ aud: claims.aud,
255
+ exp: claims.exp,
256
+ });
257
+ const payload = encoder.encode(payloadJson);
258
+ const mac = await hmac(key, payload);
259
+ return `${base64url(payload)}.${base64url(mac)}`;
260
+ }
261
+
262
+ const DEFAULT_SKEW_SECONDS = 60;
263
+
264
+ /**
265
+ * Verify a segment token per §5.4. Throws `SyncError sync.forbidden` on any
266
+ * failure (MAC, expiry, or claim mismatch).
267
+ */
268
+ export async function verifySegmentToken(
269
+ key: string | Uint8Array,
270
+ token: string,
271
+ expected: {
272
+ readonly segmentId: string;
273
+ readonly scopeDigest: string;
274
+ readonly audience: string;
275
+ readonly nowMs: number;
276
+ readonly skewSeconds?: number;
277
+ },
278
+ ): Promise<SegmentTokenClaims> {
279
+ const forbidden = (reason: string) =>
280
+ syncError('sync.forbidden', `segment token rejected: ${reason}`);
281
+ const dot = token.indexOf('.');
282
+ if (dot < 0) throw forbidden('malformed token');
283
+ const payload = fromBase64url(token.slice(0, dot));
284
+ const mac = fromBase64url(token.slice(dot + 1));
285
+ const computed = await hmac(key, payload);
286
+ if (!constantTimeEqual(mac, computed)) throw forbidden('bad MAC');
287
+ let claims: SegmentTokenClaims;
288
+ try {
289
+ claims = JSON.parse(
290
+ new TextDecoder().decode(payload),
291
+ ) as SegmentTokenClaims;
292
+ } catch {
293
+ throw forbidden('unparseable claims');
294
+ }
295
+ if (claims.v !== 1) throw forbidden('unsupported token version');
296
+ const skew = expected.skewSeconds ?? DEFAULT_SKEW_SECONDS;
297
+ if (
298
+ typeof claims.exp !== 'number' ||
299
+ claims.exp < expected.nowMs / 1000 - skew
300
+ ) {
301
+ throw forbidden('token expired');
302
+ }
303
+ if (claims.seg !== expected.segmentId) throw forbidden('segment mismatch');
304
+ if (claims.sd !== expected.scopeDigest)
305
+ throw forbidden('scope-digest mismatch');
306
+ if (claims.aud !== expected.audience) throw forbidden('audience mismatch');
307
+ return claims;
308
+ }
package/src/sigv4.ts ADDED
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Minimal AWS Signature Version 4 — exactly the subset the S3 segment
3
+ * store needs (header-signed requests and presigned GET URLs), hand-rolled
4
+ * per the dependency-light doctrine (REVISE): SigV4 is small and
5
+ * well-specified, so no SDK.
6
+ *
7
+ * Runtime-neutral (TODO §4.2): the crypto primitives are Web Crypto
8
+ * (`crypto.subtle`), not `node:crypto`, so this module runs unchanged on
9
+ * Cloudflare Workers / Deno / browsers as well as Bun/Node. `crypto.subtle`
10
+ * is async, so signing is async throughout; the `S3SegmentStore` consumers
11
+ * are already async, and the `DelegatedPresignConfig.presign` seam already
12
+ * accepts a `Promise<SegmentUrlIssue>`.
13
+ *
14
+ * Pinned by the published AWS SigV4 example vectors in
15
+ * `test/sigv4.test.ts`; the hermetic S3 stub re-derives every signature
16
+ * from the incoming request, so an asymmetric signing bug fails tests.
17
+ */
18
+
19
+ export interface SigV4Credentials {
20
+ readonly accessKeyId: string;
21
+ readonly secretAccessKey: string;
22
+ /** Optional STS session token (signed as `x-amz-security-token`). */
23
+ readonly sessionToken?: string;
24
+ }
25
+
26
+ /** Hex SHA-256 of an empty body — the payload hash for GET/HEAD/DELETE. */
27
+ export const EMPTY_PAYLOAD_SHA256 =
28
+ 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
29
+
30
+ /** The presigned-URL payload sentinel (S3 presigns never hash the body). */
31
+ export const UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD';
32
+
33
+ const textEncoder = new TextEncoder();
34
+
35
+ function toBytes(data: Uint8Array | string): Uint8Array {
36
+ return typeof data === 'string' ? textEncoder.encode(data) : data;
37
+ }
38
+
39
+ function toHex(bytes: Uint8Array): string {
40
+ let out = '';
41
+ for (const b of bytes) out += b.toString(16).padStart(2, '0');
42
+ return out;
43
+ }
44
+
45
+ /** Web Crypto wants an `ArrayBuffer`; slice() gives an owned, exact-length one. */
46
+ function bufferOf(bytes: Uint8Array): ArrayBuffer {
47
+ return bytes.slice().buffer as ArrayBuffer;
48
+ }
49
+
50
+ export async function sha256Hex(data: Uint8Array | string): Promise<string> {
51
+ const digest = await crypto.subtle.digest('SHA-256', bufferOf(toBytes(data)));
52
+ return toHex(new Uint8Array(digest));
53
+ }
54
+
55
+ async function hmacSha256(
56
+ key: Uint8Array | string,
57
+ data: string,
58
+ ): Promise<Uint8Array> {
59
+ const cryptoKey = await crypto.subtle.importKey(
60
+ 'raw',
61
+ bufferOf(toBytes(key)),
62
+ { name: 'HMAC', hash: 'SHA-256' },
63
+ false,
64
+ ['sign'],
65
+ );
66
+ const mac = await crypto.subtle.sign(
67
+ 'HMAC',
68
+ cryptoKey,
69
+ bufferOf(toBytes(data)),
70
+ );
71
+ return new Uint8Array(mac);
72
+ }
73
+
74
+ /**
75
+ * AWS canonical URI-encoding: RFC 3986 unreserved characters only —
76
+ * stricter than `encodeURIComponent` (which leaves `!'()*` alone).
77
+ */
78
+ export function uriEncode(value: string, encodeSlash: boolean): string {
79
+ const encoded = encodeURIComponent(value).replace(
80
+ /[!'()*]/g,
81
+ (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`,
82
+ );
83
+ return encodeSlash ? encoded : encoded.replaceAll('%2F', '/');
84
+ }
85
+
86
+ /** `YYYYMMDD'T'HHMMSS'Z'` plus its date-only prefix, from epoch ms. */
87
+ export function amzTimestamps(nowMs: number): {
88
+ amzDate: string;
89
+ dateStamp: string;
90
+ } {
91
+ const iso = new Date(nowMs).toISOString();
92
+ const amzDate = `${iso.slice(0, 4)}${iso.slice(5, 7)}${iso.slice(8, 10)}T${iso.slice(11, 13)}${iso.slice(14, 16)}${iso.slice(17, 19)}Z`;
93
+ return { amzDate, dateStamp: amzDate.slice(0, 8) };
94
+ }
95
+
96
+ /** Sorted, fully re-encoded canonical query string from *decoded* pairs. */
97
+ export function canonicalQuery(
98
+ params: Iterable<readonly [string, string]>,
99
+ ): string {
100
+ return [...params]
101
+ .map(([k, v]) => [uriEncode(k, true), uriEncode(v, true)] as const)
102
+ .sort(([ak, av], [bk, bv]) =>
103
+ ak < bk ? -1 : ak > bk ? 1 : av < bv ? -1 : av > bv ? 1 : 0,
104
+ )
105
+ .map(([k, v]) => `${k}=${v}`)
106
+ .join('&');
107
+ }
108
+
109
+ export interface CanonicalRequestArgs {
110
+ readonly method: string;
111
+ /** The URI-encoded absolute path exactly as sent on the request line. */
112
+ readonly canonicalPath: string;
113
+ /** Decoded query pairs; `X-Amz-Signature` must already be excluded. */
114
+ readonly query: Iterable<readonly [string, string]>;
115
+ /** Only the headers being signed (name → raw value). */
116
+ readonly headers: Readonly<Record<string, string>>;
117
+ readonly payloadHash: string;
118
+ }
119
+
120
+ export function canonicalRequest(args: CanonicalRequestArgs): {
121
+ text: string;
122
+ signedHeaders: string;
123
+ } {
124
+ const entries = Object.entries(args.headers)
125
+ .map(
126
+ ([name, value]) =>
127
+ [name.toLowerCase(), value.trim().replace(/\s+/g, ' ')] as const,
128
+ )
129
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
130
+ const signedHeaders = entries.map(([name]) => name).join(';');
131
+ const text = [
132
+ args.method.toUpperCase(),
133
+ args.canonicalPath,
134
+ canonicalQuery(args.query),
135
+ ...entries.map(([name, value]) => `${name}:${value}`),
136
+ '',
137
+ signedHeaders,
138
+ args.payloadHash,
139
+ ].join('\n');
140
+ return { text, signedHeaders };
141
+ }
142
+
143
+ export async function stringToSign(
144
+ amzDate: string,
145
+ scope: string,
146
+ canonicalRequestText: string,
147
+ ): Promise<string> {
148
+ return `AWS4-HMAC-SHA256\n${amzDate}\n${scope}\n${await sha256Hex(canonicalRequestText)}`;
149
+ }
150
+
151
+ export async function sigV4Signature(args: {
152
+ readonly secretAccessKey: string;
153
+ readonly dateStamp: string;
154
+ readonly region: string;
155
+ readonly service: string;
156
+ readonly stringToSign: string;
157
+ }): Promise<string> {
158
+ const kDate = await hmacSha256(`AWS4${args.secretAccessKey}`, args.dateStamp);
159
+ const kRegion = await hmacSha256(kDate, args.region);
160
+ const kService = await hmacSha256(kRegion, args.service);
161
+ const kSigning = await hmacSha256(kService, 'aws4_request');
162
+ return toHex(await hmacSha256(kSigning, args.stringToSign));
163
+ }
164
+
165
+ export interface SignRequestArgs {
166
+ readonly method: string;
167
+ /** Full request URL (host is taken from here and always signed). */
168
+ readonly url: URL;
169
+ readonly region: string;
170
+ /** Defaults to `s3`. */
171
+ readonly service?: string;
172
+ readonly credentials: SigV4Credentials;
173
+ readonly nowMs: number;
174
+ /** Hex SHA-256 of the request body (`EMPTY_PAYLOAD_SHA256` if none). */
175
+ readonly payloadHash: string;
176
+ /** Extra headers to send *and* sign (e.g. content-type, x-amz-meta-*). */
177
+ readonly headers?: Readonly<Record<string, string>>;
178
+ }
179
+
180
+ /**
181
+ * Headers for a header-authenticated request: the caller's headers plus
182
+ * `x-amz-date`, `x-amz-content-sha256`, optional `x-amz-security-token`,
183
+ * and `authorization`. `host` is signed but not returned — fetch derives
184
+ * it from the URL.
185
+ */
186
+ export async function signRequest(
187
+ args: SignRequestArgs,
188
+ ): Promise<Record<string, string>> {
189
+ const service = args.service ?? 's3';
190
+ const { amzDate, dateStamp } = amzTimestamps(args.nowMs);
191
+ const added: Record<string, string> = {
192
+ 'x-amz-content-sha256': args.payloadHash,
193
+ 'x-amz-date': amzDate,
194
+ ...(args.credentials.sessionToken !== undefined
195
+ ? { 'x-amz-security-token': args.credentials.sessionToken }
196
+ : {}),
197
+ };
198
+ const { text, signedHeaders } = canonicalRequest({
199
+ method: args.method,
200
+ canonicalPath: args.url.pathname,
201
+ query: args.url.searchParams,
202
+ headers: { ...args.headers, ...added, host: args.url.host },
203
+ payloadHash: args.payloadHash,
204
+ });
205
+ const scope = `${dateStamp}/${args.region}/${service}/aws4_request`;
206
+ const signature = await sigV4Signature({
207
+ secretAccessKey: args.credentials.secretAccessKey,
208
+ dateStamp,
209
+ region: args.region,
210
+ service,
211
+ stringToSign: await stringToSign(amzDate, scope, text),
212
+ });
213
+ return {
214
+ ...args.headers,
215
+ ...added,
216
+ authorization: `AWS4-HMAC-SHA256 Credential=${args.credentials.accessKeyId}/${scope},SignedHeaders=${signedHeaders},Signature=${signature}`,
217
+ };
218
+ }
219
+
220
+ export interface PresignArgs {
221
+ /** Defaults to `GET`. */
222
+ readonly method?: string;
223
+ /** Object URL without any `X-Amz-*` query parameters. */
224
+ readonly url: URL;
225
+ readonly region: string;
226
+ /** Defaults to `s3`. */
227
+ readonly service?: string;
228
+ readonly credentials: SigV4Credentials;
229
+ readonly nowMs: number;
230
+ readonly expiresSeconds: number;
231
+ }
232
+
233
+ /** Query-authenticated (presigned) URL; only the `host` header is signed. */
234
+ export async function presignUrl(args: PresignArgs): Promise<URL> {
235
+ const service = args.service ?? 's3';
236
+ const method = args.method ?? 'GET';
237
+ const { amzDate, dateStamp } = amzTimestamps(args.nowMs);
238
+ const scope = `${dateStamp}/${args.region}/${service}/aws4_request`;
239
+ const url = new URL(args.url);
240
+ url.searchParams.set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256');
241
+ url.searchParams.set(
242
+ 'X-Amz-Credential',
243
+ `${args.credentials.accessKeyId}/${scope}`,
244
+ );
245
+ url.searchParams.set('X-Amz-Date', amzDate);
246
+ url.searchParams.set('X-Amz-Expires', String(args.expiresSeconds));
247
+ url.searchParams.set('X-Amz-SignedHeaders', 'host');
248
+ if (args.credentials.sessionToken !== undefined) {
249
+ url.searchParams.set('X-Amz-Security-Token', args.credentials.sessionToken);
250
+ }
251
+ const { text } = canonicalRequest({
252
+ method,
253
+ canonicalPath: url.pathname,
254
+ query: url.searchParams,
255
+ headers: { host: url.host },
256
+ payloadHash: UNSIGNED_PAYLOAD,
257
+ });
258
+ const signature = await sigV4Signature({
259
+ secretAccessKey: args.credentials.secretAccessKey,
260
+ dateStamp,
261
+ region: args.region,
262
+ service,
263
+ stringToSign: await stringToSign(amzDate, scope, text),
264
+ });
265
+ url.searchParams.set('X-Amz-Signature', signature);
266
+ return url;
267
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * SQLite-backed blob store via `bun:sqlite` (dev/bench convenience,
3
+ * dependency-free). Bun-specific by design (top-level `bun:sqlite` import),
4
+ * so it lives in its own module — the runtime-neutral `BlobStore` interface,
5
+ * `MemoryBlobStore`, `blobIdFor`, and `isBlobId` stay in `blob-store.ts` for
6
+ * the Workers/edge core (TODO §4.2 neutrality discipline; enforced by
7
+ * `test/runtime-neutrality.test.ts`).
8
+ */
9
+ import { Database } from 'bun:sqlite';
10
+ import type { BlobRecord, BlobStore, BlobStoreStats } from './blob-store';
11
+
12
+ export class SqliteBlobStore implements BlobStore {
13
+ readonly db: Database;
14
+
15
+ constructor(db: Database | string = ':memory:') {
16
+ this.db = typeof db === 'string' ? new Database(db) : db;
17
+ this.db.exec(`
18
+ CREATE TABLE IF NOT EXISTS sync_blobs(
19
+ partition TEXT NOT NULL, blob_id TEXT NOT NULL,
20
+ byte_length INTEGER NOT NULL, media_type TEXT,
21
+ created_at_ms INTEGER NOT NULL, bytes BLOB NOT NULL,
22
+ PRIMARY KEY (partition, blob_id)
23
+ );
24
+ `);
25
+ }
26
+
27
+ async put(
28
+ partition: string,
29
+ blobId: string,
30
+ bytes: Uint8Array,
31
+ nowMs: number,
32
+ mediaType?: string,
33
+ ): Promise<BlobRecord> {
34
+ const existing = await this.get(partition, blobId);
35
+ if (existing !== undefined) return existing.record;
36
+ const record: BlobRecord = {
37
+ blobId,
38
+ partition,
39
+ byteLength: bytes.length,
40
+ ...(mediaType !== undefined ? { mediaType } : {}),
41
+ createdAtMs: nowMs,
42
+ };
43
+ this.db
44
+ .query(
45
+ `INSERT OR IGNORE INTO sync_blobs(
46
+ partition, blob_id, byte_length, media_type, created_at_ms, bytes
47
+ ) VALUES (?,?,?,?,?,?)`,
48
+ )
49
+ .run(partition, blobId, bytes.length, mediaType ?? null, nowMs, bytes);
50
+ return record;
51
+ }
52
+
53
+ async has(partition: string, blobId: string): Promise<boolean> {
54
+ const row = this.db
55
+ .query<{ n: number }, [string, string]>(
56
+ 'SELECT 1 AS n FROM sync_blobs WHERE partition=? AND blob_id=?',
57
+ )
58
+ .get(partition, blobId);
59
+ return row !== null;
60
+ }
61
+
62
+ async get(
63
+ partition: string,
64
+ blobId: string,
65
+ ): Promise<{ record: BlobRecord; bytes: Uint8Array } | undefined> {
66
+ const row = this.db
67
+ .query<
68
+ {
69
+ byte_length: number;
70
+ media_type: string | null;
71
+ created_at_ms: number;
72
+ bytes: Uint8Array;
73
+ },
74
+ [string, string]
75
+ >(
76
+ `SELECT byte_length, media_type, created_at_ms, bytes
77
+ FROM sync_blobs WHERE partition=? AND blob_id=?`,
78
+ )
79
+ .get(partition, blobId);
80
+ if (row === null) return undefined;
81
+ return {
82
+ record: {
83
+ blobId,
84
+ partition,
85
+ byteLength: row.byte_length,
86
+ ...(row.media_type !== null ? { mediaType: row.media_type } : {}),
87
+ createdAtMs: row.created_at_ms,
88
+ },
89
+ bytes: new Uint8Array(row.bytes),
90
+ };
91
+ }
92
+
93
+ async sweepOrphans(
94
+ partition: string,
95
+ olderThanMs: number,
96
+ referencedBlobIds: ReadonlySet<string>,
97
+ ): Promise<string[]> {
98
+ const candidates = this.db
99
+ .query<{ blob_id: string }, [string, number]>(
100
+ `SELECT blob_id FROM sync_blobs
101
+ WHERE partition=? AND created_at_ms < ?`,
102
+ )
103
+ .all(partition, olderThanMs);
104
+ const swept: string[] = [];
105
+ const del = this.db.query(
106
+ 'DELETE FROM sync_blobs WHERE partition=? AND blob_id=?',
107
+ );
108
+ for (const { blob_id } of candidates) {
109
+ if (!referencedBlobIds.has(blob_id)) {
110
+ del.run(partition, blob_id);
111
+ swept.push(blob_id);
112
+ }
113
+ }
114
+ return swept;
115
+ }
116
+
117
+ async stats(partition: string): Promise<BlobStoreStats> {
118
+ const row = this.db
119
+ .query<{ count: number; bytes: number | null }, [string]>(
120
+ 'SELECT count(*) AS count, sum(byte_length) AS bytes FROM sync_blobs WHERE partition=?',
121
+ )
122
+ .get(partition);
123
+ return { count: row?.count ?? 0, bytes: row?.bytes ?? 0 };
124
+ }
125
+ }