@syncular/server 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,514 @@
1
+ /**
2
+ * Relational current-row storage (DESIGN-relational-server-storage.md).
3
+ *
4
+ * Every synced table is a REAL table in the server database — the app's
5
+ * columns with proper type affinities, queryable with plain SQL/joins/BI —
6
+ * plus five `_sync_*` meta columns:
7
+ *
8
+ * _sync_partition TEXT partition key (multi-partition servers
9
+ * share one database; same app PK in two
10
+ * partitions must coexist)
11
+ * _sync_row_id TEXT the change rowId (§2.2) — the string
12
+ * rendering of the app PK. The storage key
13
+ * stays TEXT so lookup and scan-pagination
14
+ * semantics are byte-identical to the old
15
+ * generic store regardless of the PK's type.
16
+ * _sync_server_version INTEGER §2.2 server version
17
+ * _sync_scopes TEXT/JSONB the stored-scope map the §3.4 authz read
18
+ * consumes (also present as real columns;
19
+ * the map is cheaper to read back)
20
+ * _sync_payload BLOB/BYTEA the verbatim row-codec bytes — the wire
21
+ * source of truth. The serve path reads
22
+ * THIS, never re-encodes from typed columns,
23
+ * so sync round-trips are byte-identical by
24
+ * construction. Typed columns are a
25
+ * queryable projection.
26
+ *
27
+ * PRIMARY KEY (_sync_partition, _sync_row_id).
28
+ *
29
+ * The typed columns are written from `decodeRow(payload)` in the same
30
+ * statement as the payload, so projection and payload cannot drift except
31
+ * through a codec bug — which the round-trip contract tests would catch.
32
+ *
33
+ * Dialect notes:
34
+ * - postgres maps `json` → JSONB (queryable: `->>`, GIN). JSONB normalizes
35
+ * bytes, which is safe because the wire never reads typed columns.
36
+ * A json value that fails to parse binds NULL on postgres (the payload
37
+ * still holds it verbatim); sqlite stores the text as-is.
38
+ * - D1's JSON transport represents integers as JS doubles; app integer
39
+ * columns beyond 2^53 lose precision in the PROJECTION only — the
40
+ * payload is exact, sync is unaffected.
41
+ * - Migration-added columns (ALTER TABLE ADD COLUMN) are always nullable at
42
+ * the DB layer: SQLite cannot add a NOT NULL column without a default, and
43
+ * the row codec — not the DB — is the type authority. Fresh CREATEs carry
44
+ * NOT NULL per the schema.
45
+ */
46
+ import { decodeRow, encodeRow, } from '@syncular/core';
47
+ export const SYNC_PARTITION_COLUMN = '_sync_partition';
48
+ export const SYNC_ROW_ID_COLUMN = '_sync_row_id';
49
+ export const SYNC_VERSION_COLUMN = '_sync_server_version';
50
+ export const SYNC_SCOPES_COLUMN = '_sync_scopes';
51
+ export const SYNC_PAYLOAD_COLUMN = '_sync_payload';
52
+ /** SQL-standard identifier quoting (both dialects). */
53
+ export function quoteIdent(name) {
54
+ return `"${name.replaceAll('"', '""')}"`;
55
+ }
56
+ /** §5.3-style type affinities, per dialect (DESIGN type-mapping table). */
57
+ export function columnSqlType(column, dialect) {
58
+ if (dialect === 'postgres') {
59
+ switch (column.type) {
60
+ case 'string':
61
+ case 'blob_ref':
62
+ return 'TEXT';
63
+ case 'json':
64
+ return 'JSONB';
65
+ case 'integer':
66
+ return 'BIGINT';
67
+ case 'float':
68
+ return 'DOUBLE PRECISION';
69
+ case 'boolean':
70
+ return 'BOOLEAN';
71
+ case 'bytes':
72
+ case 'crdt':
73
+ return 'BYTEA';
74
+ default:
75
+ throw new Error(`unsupported column type: ${String(column.type)}`);
76
+ }
77
+ }
78
+ switch (column.type) {
79
+ case 'string':
80
+ case 'json':
81
+ case 'blob_ref':
82
+ return 'TEXT';
83
+ case 'integer':
84
+ return 'INTEGER';
85
+ case 'float':
86
+ return 'REAL';
87
+ case 'boolean':
88
+ return 'INTEGER';
89
+ case 'bytes':
90
+ case 'crdt':
91
+ return 'BLOB';
92
+ default:
93
+ throw new Error(`unsupported column type: ${String(column.type)}`);
94
+ }
95
+ }
96
+ /**
97
+ * The full column list of a relational row table, in INSERT order:
98
+ * partition, row id, app columns (schema order; only when the table is
99
+ * materialized), version, scopes, payload.
100
+ */
101
+ export function tableColumnNames(table) {
102
+ return [
103
+ SYNC_PARTITION_COLUMN,
104
+ SYNC_ROW_ID_COLUMN,
105
+ ...(table.materialize ? table.columns.map((column) => column.name) : []),
106
+ SYNC_VERSION_COLUMN,
107
+ SYNC_SCOPES_COLUMN,
108
+ SYNC_PAYLOAD_COLUMN,
109
+ ];
110
+ }
111
+ /** CREATE TABLE IF NOT EXISTS for one app table. */
112
+ export function createTableDdl(table, dialect) {
113
+ const versionType = dialect === 'postgres' ? 'BIGINT' : 'INTEGER';
114
+ const scopesType = dialect === 'postgres' ? 'JSONB' : 'TEXT';
115
+ const payloadType = dialect === 'postgres' ? 'BYTEA' : 'BLOB';
116
+ const defs = [
117
+ `${quoteIdent(SYNC_PARTITION_COLUMN)} TEXT NOT NULL`,
118
+ `${quoteIdent(SYNC_ROW_ID_COLUMN)} TEXT NOT NULL`,
119
+ ...(table.materialize
120
+ ? table.columns.map((column) => {
121
+ const notNull = column.nullable ? '' : ' NOT NULL';
122
+ return `${quoteIdent(column.name)} ${columnSqlType(column, dialect)}${notNull}`;
123
+ })
124
+ : []),
125
+ `${quoteIdent(SYNC_VERSION_COLUMN)} ${versionType} NOT NULL`,
126
+ `${quoteIdent(SYNC_SCOPES_COLUMN)} ${scopesType} NOT NULL`,
127
+ `${quoteIdent(SYNC_PAYLOAD_COLUMN)} ${payloadType} NOT NULL`,
128
+ `PRIMARY KEY (${quoteIdent(SYNC_PARTITION_COLUMN)}, ${quoteIdent(SYNC_ROW_ID_COLUMN)})`,
129
+ ];
130
+ return `CREATE TABLE IF NOT EXISTS ${quoteIdent(table.name)} (${defs.join(', ')})`;
131
+ }
132
+ /**
133
+ * ALTER TABLE ADD COLUMN statements for schema columns missing from
134
+ * `existingColumns` (introspected). Added columns are nullable (header note).
135
+ */
136
+ export function addColumnDdl(table, existingColumns, dialect) {
137
+ if (!table.materialize)
138
+ return [];
139
+ const out = [];
140
+ for (const column of table.columns) {
141
+ if (existingColumns.has(column.name))
142
+ continue;
143
+ out.push(`ALTER TABLE ${quoteIdent(table.name)} ADD COLUMN ${quoteIdent(column.name)} ${columnSqlType(column, dialect)}`);
144
+ }
145
+ return out;
146
+ }
147
+ /**
148
+ * CREATE INDEX IF NOT EXISTS for the table's user-declared indexes
149
+ * (DESIGN "user indexes" — the same names/columns the client materializes;
150
+ * cross-table index-name uniqueness is the user's schema concern, exactly
151
+ * as it is client-side).
152
+ */
153
+ export function createIndexDdl(table) {
154
+ // User indexes name app columns — nothing to index without the projection.
155
+ if (!table.materialize)
156
+ return [];
157
+ return table.indexes.map((index) => {
158
+ const unique = index.unique ? 'UNIQUE ' : '';
159
+ const columns = index.columns
160
+ .map((column) => quoteIdent(column))
161
+ .join(', ');
162
+ return `CREATE ${unique}INDEX IF NOT EXISTS ${quoteIdent(index.name)} ON ${quoteIdent(table.name)} (${columns})`;
163
+ });
164
+ }
165
+ /**
166
+ * Convert one decoded row value to its SQL bind value.
167
+ * - boolean → 0/1 on sqlite (INTEGER affinity), native on postgres;
168
+ * - json → the raw string on sqlite; on postgres, NULL if unparseable
169
+ * (JSONB would reject the INSERT — the payload keeps the verbatim value);
170
+ * - everything else binds naturally (Uint8Array → BLOB/BYTEA).
171
+ */
172
+ export function toSqlValue(column, value, dialect) {
173
+ if (value === null)
174
+ return null;
175
+ if (column.type === 'boolean' && dialect === 'sqlite') {
176
+ return value === true ? 1 : 0;
177
+ }
178
+ if (column.type === 'json' && dialect === 'postgres') {
179
+ if (typeof value !== 'string')
180
+ return null;
181
+ try {
182
+ JSON.parse(value);
183
+ return value;
184
+ }
185
+ catch {
186
+ return null;
187
+ }
188
+ }
189
+ return value;
190
+ }
191
+ /**
192
+ * Bind values for an upsert, in `tableColumnNames` order. Decodes the
193
+ * payload once; the typed columns and the verbatim payload land in one
194
+ * statement, so they cannot drift.
195
+ */
196
+ export function upsertValues(table, partition, row, dialect) {
197
+ // A non-materialized table skips the decode entirely — the upsert is a
198
+ // five-column meta write (the old blob-store cost).
199
+ const projection = table.materialize
200
+ ? decodeRow(table.columns, row.payload)
201
+ : undefined;
202
+ return [
203
+ partition,
204
+ row.rowId,
205
+ ...(projection !== undefined
206
+ ? table.columns.map((column, index) => toSqlValue(column, projection[index] ?? null, dialect))
207
+ : []),
208
+ row.serverVersion,
209
+ JSON.stringify(row.scopes),
210
+ row.payload,
211
+ ];
212
+ }
213
+ function placeholderList(count, dialect) {
214
+ if (dialect === 'sqlite')
215
+ return new Array(count).fill('?').join(',');
216
+ const out = [];
217
+ for (let i = 1; i <= count; i++)
218
+ out.push(`$${i}`);
219
+ return out.join(',');
220
+ }
221
+ /** Upsert statement text (bind with `upsertValues`). */
222
+ export function upsertSql(table, dialect) {
223
+ const names = tableColumnNames(table);
224
+ const quoted = names.map((name) => quoteIdent(name)).join(', ');
225
+ const values = placeholderList(names.length, dialect);
226
+ if (dialect === 'sqlite') {
227
+ // INSERT OR REPLACE keys on the (_sync_partition, _sync_row_id) PK.
228
+ return `INSERT OR REPLACE INTO ${quoteIdent(table.name)} (${quoted}) VALUES (${values})`;
229
+ }
230
+ const updates = names
231
+ .slice(2) // partition + row id are the conflict key
232
+ .map((name) => `${quoteIdent(name)}=EXCLUDED.${quoteIdent(name)}`)
233
+ .join(', ');
234
+ return `INSERT INTO ${quoteIdent(table.name)} (${quoted}) VALUES (${values})
235
+ ON CONFLICT (${quoteIdent(SYNC_PARTITION_COLUMN)}, ${quoteIdent(SYNC_ROW_ID_COLUMN)}) DO UPDATE SET ${updates}`;
236
+ }
237
+ /**
238
+ * SELECT one stored row. Aliased to the legacy record shape
239
+ * (`row_id`/`server_version`/`scopes`/`payload`) so the existing
240
+ * `toStoredRow` converters keep working. Params: [partition, rowId].
241
+ */
242
+ export function selectRowSql(table, dialect) {
243
+ const p = dialect === 'sqlite' ? ['?', '?'] : ['$1', '$2'];
244
+ return `SELECT ${quoteIdent(SYNC_ROW_ID_COLUMN)} AS row_id, ${quoteIdent(SYNC_VERSION_COLUMN)} AS server_version, ${quoteIdent(SYNC_SCOPES_COLUMN)} AS scopes, ${quoteIdent(SYNC_PAYLOAD_COLUMN)} AS payload FROM ${quoteIdent(table.name)} WHERE ${quoteIdent(SYNC_PARTITION_COLUMN)}=${p[0]} AND ${quoteIdent(SYNC_ROW_ID_COLUMN)}=${p[1]}`;
245
+ }
246
+ /**
247
+ * One-round-trip page scan for `scanRows`: candidates from the inverted
248
+ * scope index (ordered + LIMITed at the covering `sync_row_scopes` PK —
249
+ * exactly the old candidate query, so the index-first posture is unchanged)
250
+ * LEFT JOINed to the row table, so a whole page arrives in ONE statement
251
+ * instead of one lookup per candidate. That per-candidate lookup was the
252
+ * cold-bootstrap hot path: a 100k-row snapshot on Postgres paid ~100k
253
+ * network round-trips (~10 s at 0.1 ms each) before this join.
254
+ *
255
+ * LEFT JOIN (not INNER): a candidate whose row vanished (index entry
256
+ * without a row) must still reach the caller — its `row_id` advances the
257
+ * keyset cursor — so it comes back with NULL payload rather than
258
+ * disappearing from the page.
259
+ *
260
+ * Bind order:
261
+ * - postgres: [partition, tbl, var, ...values, afterRowId, limit]
262
+ * (the join reuses `$1` for the partition);
263
+ * - sqlite: [partition, tbl, var, ...values, afterRowId, limit,
264
+ * partition] (positional `?` — the partition binds again for the join).
265
+ */
266
+ export function scanRowPageSql(table, valueCount, dialect) {
267
+ const p = (n) => (dialect === 'sqlite' ? '?' : `$${n}`);
268
+ const values = [];
269
+ for (let i = 0; i < valueCount; i++)
270
+ values.push(p(4 + i));
271
+ const after = p(4 + valueCount);
272
+ const limit = p(5 + valueCount);
273
+ const joinPartition = dialect === 'sqlite' ? '?' : '$1';
274
+ return `SELECT c.row_id AS row_id, r.${quoteIdent(SYNC_VERSION_COLUMN)} AS server_version, r.${quoteIdent(SYNC_SCOPES_COLUMN)} AS scopes, r.${quoteIdent(SYNC_PAYLOAD_COLUMN)} AS payload
275
+ FROM (SELECT DISTINCT row_id FROM sync_row_scopes
276
+ WHERE partition=${p(1)} AND tbl=${p(2)} AND var=${p(3)} AND value IN (${values.join(',')})
277
+ AND row_id>${after}
278
+ ORDER BY row_id LIMIT ${limit}) c
279
+ LEFT JOIN ${quoteIdent(table.name)} r
280
+ ON r.${quoteIdent(SYNC_PARTITION_COLUMN)}=${joinPartition} AND r.${quoteIdent(SYNC_ROW_ID_COLUMN)}=c.row_id
281
+ ORDER BY c.row_id`;
282
+ }
283
+ /**
284
+ * One-round-trip page read for `readCommitWindow` (sync_* tables only; it
285
+ * lives here beside `scanRowPageSql` because it is the same join-the-index
286
+ * posture, dialect-parameterized the same way): candidates from the inverted
287
+ * change-scope index (ordered + LIMITed at the covering `sync_change_scopes`
288
+ * PK — exactly the old candidate query, so the index-first invariant of
289
+ * `CommitWindowQuery` is unchanged) LEFT JOINed to the commit metadata and
290
+ * to the commit's changes for the table, so a whole window page arrives in
291
+ * ONE statement instead of 1 + 2×candidates round trips. This is the
292
+ * incremental-pull hot path — it runs on every sync round; before the join a
293
+ * 500-candidate catch-up window on Postgres paid ~1000 network round trips.
294
+ *
295
+ * LEFT JOIN (not INNER): a candidate whose commit vanished (scope-index
296
+ * entry without a commit/changes row) must still reach the caller — its
297
+ * `commit_seq` advances the window cursor — so it comes back with NULL
298
+ * meta/change columns rather than disappearing from the page.
299
+ *
300
+ * Rows arrive ordered (commit_seq, idx): oldest-first commits, each commit's
301
+ * change rows consecutive in `idx` order — the caller groups consecutive
302
+ * rows and verifies the full multi-variable scope match in JS.
303
+ *
304
+ * Bind order:
305
+ * - postgres: [partition, tbl, var, ...values, afterSeq, throughSeq,
306
+ * limit] (the joins reuse `$1`/`$2`);
307
+ * - sqlite: [partition, tbl, var, ...values, afterSeq, throughSeq,
308
+ * limit, partition, partition, tbl] (positional `?` — partition/tbl bind
309
+ * again for the joins).
310
+ */
311
+ export function commitWindowPageSql(valueCount, dialect) {
312
+ const p = (n) => (dialect === 'sqlite' ? '?' : `$${n}`);
313
+ const values = [];
314
+ for (let i = 0; i < valueCount; i++)
315
+ values.push(p(4 + i));
316
+ const after = p(4 + valueCount);
317
+ const through = p(5 + valueCount);
318
+ const limit = p(6 + valueCount);
319
+ const joinPartition = dialect === 'sqlite' ? '?' : '$1';
320
+ const joinTbl = dialect === 'sqlite' ? '?' : '$2';
321
+ return `SELECT c.commit_seq AS commit_seq, m.actor_id AS actor_id, m.created_at_ms AS created_at_ms,
322
+ ch.tbl AS tbl, ch.row_id AS row_id, ch.op AS op, ch.row_version AS row_version, ch.scopes AS scopes, ch.payload AS payload
323
+ FROM (SELECT DISTINCT commit_seq FROM sync_change_scopes
324
+ WHERE partition=${p(1)} AND tbl=${p(2)} AND var=${p(3)} AND value IN (${values.join(',')})
325
+ AND commit_seq>${after} AND commit_seq<=${through}
326
+ ORDER BY commit_seq LIMIT ${limit}) c
327
+ LEFT JOIN sync_commits m
328
+ ON m.partition=${joinPartition} AND m.commit_seq=c.commit_seq
329
+ LEFT JOIN sync_changes ch
330
+ ON ch.partition=${joinPartition} AND ch.commit_seq=c.commit_seq AND ch.tbl=${joinTbl}
331
+ ORDER BY c.commit_seq, ch.idx`;
332
+ }
333
+ /** SELECT a row's version + scope map (admin/blob authz). Params: [partition, rowId]. */
334
+ export function selectRowScopesSql(table, dialect) {
335
+ const p = dialect === 'sqlite' ? ['?', '?'] : ['$1', '$2'];
336
+ return `SELECT ${quoteIdent(SYNC_VERSION_COLUMN)} AS server_version, ${quoteIdent(SYNC_SCOPES_COLUMN)} AS scopes FROM ${quoteIdent(table.name)} WHERE ${quoteIdent(SYNC_PARTITION_COLUMN)}=${p[0]} AND ${quoteIdent(SYNC_ROW_ID_COLUMN)}=${p[1]}`;
337
+ }
338
+ /** DELETE one stored row. Params: [partition, rowId]. */
339
+ export function deleteRowSql(table, dialect) {
340
+ const p = dialect === 'sqlite' ? ['?', '?'] : ['$1', '$2'];
341
+ return `DELETE FROM ${quoteIdent(table.name)} WHERE ${quoteIdent(SYNC_PARTITION_COLUMN)}=${p[0]} AND ${quoteIdent(SYNC_ROW_ID_COLUMN)}=${p[1]}`;
342
+ }
343
+ /**
344
+ * The schema-version marker table gating DDL work (DESIGN "server-side
345
+ * schema migration"): `ensureSchema` compares the stored version and skips
346
+ * all introspection/DDL when it matches — one cheap read per storage
347
+ * instance (relevant for D1's per-request instantiation).
348
+ *
349
+ * `layouts` persists each table's column layout (name/type/nullable, the
350
+ * exact inputs the row codec's byte layout depends on) as of the LAST
351
+ * applied schema version. The codec is strict — a payload only decodes
352
+ * under the column list it was encoded with — so a version bump MUST
353
+ * re-encode stored payloads (append trailing NULLs for added columns);
354
+ * decoding the old bytes requires the old layout, and this column is where
355
+ * it lives.
356
+ */
357
+ export const SCHEMA_META_DDL_SQLITE = `CREATE TABLE IF NOT EXISTS sync_schema_meta(
358
+ id INTEGER PRIMARY KEY CHECK (id = 1),
359
+ schema_version INTEGER NOT NULL,
360
+ layouts TEXT NOT NULL DEFAULT '{}'
361
+ )`;
362
+ export const SCHEMA_META_DDL_POSTGRES = `CREATE TABLE IF NOT EXISTS sync_schema_meta(
363
+ id INTEGER PRIMARY KEY CHECK (id = 1),
364
+ schema_version BIGINT NOT NULL,
365
+ layouts TEXT NOT NULL DEFAULT '{}'
366
+ )`;
367
+ /** The layouts JSON persisted alongside the schema version marker. */
368
+ export function layoutsOf(schema) {
369
+ const out = {};
370
+ for (const table of schema.tables.values()) {
371
+ out[table.name] = table.columns.map((column) => ({
372
+ name: column.name,
373
+ type: column.type,
374
+ nullable: column.nullable,
375
+ }));
376
+ }
377
+ return JSON.stringify(out);
378
+ }
379
+ export function parseLayouts(json) {
380
+ if (json === null || json === undefined || json.length === 0)
381
+ return {};
382
+ return JSON.parse(json);
383
+ }
384
+ /**
385
+ * Enforce the migration subset on a table's column list: the old layout
386
+ * must be an exact prefix (same name, type, nullability) of the new one,
387
+ * and appended columns must be nullable (there is no default to backfill).
388
+ * Returns `true` iff columns were appended.
389
+ */
390
+ export function assertAppendOnlyMigration(tableName, oldLayout, table) {
391
+ if (oldLayout.length > table.columns.length) {
392
+ throw new Error(`table ${JSON.stringify(tableName)}: the schema removed columns — only CREATE TABLE / ADD COLUMN / CREATE INDEX migrations are supported`);
393
+ }
394
+ for (let i = 0; i < oldLayout.length; i++) {
395
+ const before = oldLayout[i];
396
+ const after = table.columns[i];
397
+ if (before === undefined ||
398
+ after === undefined ||
399
+ before.name !== after.name ||
400
+ before.type !== after.type ||
401
+ before.nullable !== after.nullable) {
402
+ throw new Error(`table ${JSON.stringify(tableName)}: column ${i} changed (${JSON.stringify(before)} → ${JSON.stringify({ name: after?.name, type: after?.type, nullable: after?.nullable })}) — only appending nullable columns is supported`);
403
+ }
404
+ }
405
+ for (let i = oldLayout.length; i < table.columns.length; i++) {
406
+ const added = table.columns[i];
407
+ if (added !== undefined && !added.nullable) {
408
+ throw new Error(`table ${JSON.stringify(tableName)}: added column ${JSON.stringify(added.name)} must be nullable — existing rows have no value to backfill`);
409
+ }
410
+ }
411
+ return oldLayout.length < table.columns.length;
412
+ }
413
+ /**
414
+ * Re-encode an old-layout payload under the current columns: decode with
415
+ * the layout it was written under, append NULLs for the added columns,
416
+ * encode with the current column list. The write path (§3.4 scope-strip,
417
+ * CRDT merge, conflict serverRow) and the bootstrap serve path both decode
418
+ * stored payloads under the CURRENT schema, so this migration is a
419
+ * correctness requirement of the version bump, not an optimization.
420
+ */
421
+ export function migratePayload(oldLayout, table, payload) {
422
+ const values = decodeRow(oldLayout, payload);
423
+ while (values.length < table.columns.length)
424
+ values.push(null);
425
+ return encodeRow(table.columns, values);
426
+ }
427
+ /**
428
+ * Keyset-paged scan of a row table for the migration rewrite.
429
+ * Params: [afterPartition, afterRowId, limit].
430
+ */
431
+ export function selectRowsForRewriteSql(table, dialect) {
432
+ const p = dialect === 'sqlite' ? ['?', '?', '?'] : ['$1', '$2', '$3'];
433
+ return `SELECT ${quoteIdent(SYNC_PARTITION_COLUMN)} AS partition, ${quoteIdent(SYNC_ROW_ID_COLUMN)} AS row_id, ${quoteIdent(SYNC_PAYLOAD_COLUMN)} AS payload
434
+ FROM ${quoteIdent(table.name)}
435
+ WHERE (${quoteIdent(SYNC_PARTITION_COLUMN)}, ${quoteIdent(SYNC_ROW_ID_COLUMN)}) > (${p[0]}, ${p[1]})
436
+ ORDER BY ${quoteIdent(SYNC_PARTITION_COLUMN)}, ${quoteIdent(SYNC_ROW_ID_COLUMN)}
437
+ LIMIT ${p[2]}`;
438
+ }
439
+ /**
440
+ * Rewrite one row during a migration: refresh the projection columns (when
441
+ * materialized) and the payload. Bind with `rewriteValues`.
442
+ */
443
+ export function rewriteRowSql(table, dialect) {
444
+ const sets = [];
445
+ let i = 1;
446
+ if (table.materialize) {
447
+ for (const column of table.columns) {
448
+ sets.push(`${quoteIdent(column.name)}=${dialect === 'sqlite' ? '?' : `$${i}`}`);
449
+ i++;
450
+ }
451
+ }
452
+ sets.push(`${quoteIdent(SYNC_PAYLOAD_COLUMN)}=${dialect === 'sqlite' ? '?' : `$${i}`}`);
453
+ i++;
454
+ const wherePartition = dialect === 'sqlite' ? '?' : `$${i}`;
455
+ const whereRowId = dialect === 'sqlite' ? '?' : `$${i + 1}`;
456
+ return `UPDATE ${quoteIdent(table.name)} SET ${sets.join(', ')} WHERE ${quoteIdent(SYNC_PARTITION_COLUMN)}=${wherePartition} AND ${quoteIdent(SYNC_ROW_ID_COLUMN)}=${whereRowId}`;
457
+ }
458
+ /** Bind values for `rewriteRowSql` from the (already migrated) payload. */
459
+ export function rewriteValues(table, partition, rowId, payload, dialect) {
460
+ const out = [];
461
+ if (table.materialize) {
462
+ const values = decodeRow(table.columns, payload);
463
+ for (let i = 0; i < table.columns.length; i++) {
464
+ const column = table.columns[i];
465
+ if (column === undefined)
466
+ continue;
467
+ out.push(toSqlValue(column, values[i] ?? null, dialect));
468
+ }
469
+ }
470
+ out.push(payload, partition, rowId);
471
+ return out;
472
+ }
473
+ /**
474
+ * What the migration rewrite phase must do for one table, derived from the
475
+ * persisted layouts + the physical columns present before this run.
476
+ *
477
+ * - `migrate`: the layout gained columns — every stored payload re-encodes
478
+ * under the new column list (correctness; see `migratePayload`).
479
+ * - `backfill`: the table just gained physical projection columns whose
480
+ * values exist in stored payloads (materialization flipped on) — the
481
+ * projection refreshes from the payload, no re-encode.
482
+ */
483
+ export function rewritePlan(table, oldLayout, physicalColumnsBefore) {
484
+ if (oldLayout === undefined || physicalColumnsBefore === undefined) {
485
+ // Fresh table (or a pre-layout database, which cannot be reasoned
486
+ // about): nothing stored to rewrite.
487
+ return { migrate: false, backfill: false };
488
+ }
489
+ const migrate = assertAppendOnlyMigration(table.name, oldLayout, table);
490
+ const backfill = table.materialize &&
491
+ table.columns.some((column, index) => index < oldLayout.length && // value exists in old payloads
492
+ !physicalColumnsBefore.has(column.name));
493
+ return { migrate, backfill };
494
+ }
495
+ /**
496
+ * Every DDL statement to bring a database from `existingColumnsByTable`
497
+ * (introspected; a missing key = table absent) to `schema`: CREATE TABLE
498
+ * for absent tables, ADD COLUMN for missing columns, CREATE INDEX for the
499
+ * declared user indexes — exactly the migration subset.
500
+ */
501
+ export function schemaDdl(schema, existingColumnsByTable, dialect) {
502
+ const out = [];
503
+ for (const table of schema.tables.values()) {
504
+ const existing = existingColumnsByTable.get(table.name);
505
+ if (existing === undefined) {
506
+ out.push(createTableDdl(table, dialect));
507
+ }
508
+ else {
509
+ out.push(...addColumnDdl(table, existing, dialect));
510
+ }
511
+ out.push(...createIndexDdl(table));
512
+ }
513
+ return out;
514
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * S3-compatible blob store (SPEC.md §5.9.2 durable objects, §5.9.5 delegated
3
+ * presign) — AWS S3, Cloudflare R2, MinIO. The blob twin of
4
+ * `s3-segment-store.ts`: same hand-rolled SigV4 over `fetch` (sigv4.ts), same
5
+ * content-addressed key layout, same LIST-free-on-the-hot-path CAS stats
6
+ * accumulator. Zero dependencies.
7
+ *
8
+ * Key layout (deterministic — every point lookup is a GET/HEAD, never a LIST):
9
+ *
10
+ * {keyPrefix}blob/sha256/{hex}
11
+ * The blob bytes, VERBATIM (the object body MUST be exactly the
12
+ * content-addressed bytes so a presigned GET serves them directly and the
13
+ * client's §5.9.1 hash check passes — blobs are never re-compressed at
14
+ * rest, §5.8/§5.9.5). The `byteLength` + optional `mediaType` ride along
15
+ * as object user metadata `x-amz-meta-syncular-blob` = base64url(JSON), so
16
+ * `get` is a single GET. `createdAtMs` is stored there too so the orphan
17
+ * sweep (§5.9.2) can read the upload age without a separate index.
18
+ *
19
+ * DURABILITY vs SEGMENT TTL — the honest interface difference (§5.9.2): a
20
+ * blob referenced by a live row must stay downloadable INDEFINITELY. So,
21
+ * unlike `S3SegmentStore`, this store writes **no `expiresAtMs`, no S3
22
+ * lifecycle-expiration mapping, and no TTL config**. Expiry is
23
+ * REFERENCE-driven, not time-driven: reclamation is the host-scheduled
24
+ * `sweepOrphanBlobs` (blob-store.ts) deleting only objects no live row
25
+ * references after a grace period — never a bucket lifecycle rule. Do NOT
26
+ * put a lifecycle expiration on the `blob/` prefix; it would delete
27
+ * still-referenced attachments.
28
+ *
29
+ * The orphan sweep is the ONLY operation that LISTs: `sweepOrphans` pages
30
+ * `ListObjectsV2` over the `blob/` prefix (an admin/GC path, off the hot
31
+ * path) to find candidates, reads each object's `createdAtMs` from its
32
+ * metadata for the grace check, and DELETEs the unreferenced-and-old ones.
33
+ */
34
+ import type { BlobRecord, BlobStore, BlobStoreStats } from './blob-store.js';
35
+ import type { BlobPresignConfig, BlobUploadPresignConfig, SegmentUrlIssue } from './signed-url.js';
36
+ export interface S3BlobStoreConfig {
37
+ /**
38
+ * Endpoint origin, no bucket, no trailing slash:
39
+ * AWS `https://s3.<region>.amazonaws.com`,
40
+ * R2 `https://<account-id>.r2.cloudflarestorage.com`,
41
+ * MinIO `http://127.0.0.1:9000`. Requests are path-style
42
+ * (`{endpoint}/{bucket}/{key}`), which all three accept.
43
+ */
44
+ readonly endpoint: string;
45
+ /** AWS region; R2 uses `auto`. */
46
+ readonly region: string;
47
+ readonly bucket: string;
48
+ readonly accessKeyId: string;
49
+ readonly secretAccessKey: string;
50
+ /** Optional STS session token. */
51
+ readonly sessionToken?: string;
52
+ /** Key namespace inside the bucket, e.g. `syncular/`. Default: none. */
53
+ readonly keyPrefix?: string;
54
+ }
55
+ export declare class S3BlobStore implements BlobStore {
56
+ #private;
57
+ constructor(config: S3BlobStoreConfig);
58
+ /**
59
+ * `{keyPrefix}blob/{partition}/sha256/{hex}` — the §5.9.5 "signed key embeds
60
+ * the blobId". Blobs are content-addressed, but download authorization and
61
+ * the reference index are per-partition, so the same bytes uploaded under
62
+ * two partitions are two objects (a partition MUST NOT read another's
63
+ * attachment by guessing a content address). The partition is folded into
64
+ * the key as a path segment.
65
+ */
66
+ objectKeyFor(partition: string, blobId: string): string;
67
+ put(partition: string, blobId: string, bytes: Uint8Array, nowMs: number, mediaType?: string): Promise<BlobRecord>;
68
+ has(partition: string, blobId: string): Promise<boolean>;
69
+ get(partition: string, blobId: string): Promise<{
70
+ record: BlobRecord;
71
+ bytes: Uint8Array;
72
+ } | undefined>;
73
+ /**
74
+ * §5.9.2 orphan sweep — the ONLY LISTing operation. Page `ListObjectsV2`
75
+ * over `{prefix}blob/{partition}/`, HEAD each object for its `createdAtMs`,
76
+ * and DELETE the ones both older than `olderThanMs` AND absent from
77
+ * `referencedBlobIds`. Returns the deleted blobIds. Never deletes a
78
+ * referenced blob (§5.9.2). Off the hot path — a host-scheduled GC job.
79
+ */
80
+ sweepOrphans(partition: string, olderThanMs: number, referencedBlobIds: ReadonlySet<string>): Promise<string[]>;
81
+ /**
82
+ * Store-wide counters from the pointer-object accumulator (never a LIST on
83
+ * the read path). `approximate: true` for the same reasons `S3SegmentStore`
84
+ * documents: best-effort ETag-CAS maintenance and sweep deletes that may
85
+ * miss the decrement. Exact within a single writer; a health gauge under
86
+ * concurrency. Note this is store-WIDE, not partition-scoped, so a shared
87
+ * bucket reports the whole bucket — the parameter is accepted for interface
88
+ * parity but the accumulator is one object per store.
89
+ */
90
+ stats(_partition: string): Promise<BlobStoreStats>;
91
+ /**
92
+ * SigV4 presigned GET for a blob object (§5.9.5 delegated presign). The
93
+ * signed key embeds the `blobId`; `urlExpiresAtMs` is the provider-enforced
94
+ * expiry (`X-Amz-Date + X-Amz-Expires`). Issued ONLY after the row-derived
95
+ * authorization check (§5.9.5) — the caller (blob-handlers) resolves scopes
96
+ * and tests referencing rows first; the URL is then a short-TTL bearer
97
+ * grant to exactly those immutable bytes. TTL SHOULD be ≤ 15 minutes;
98
+ * default 900 s.
99
+ */
100
+ presignBlobGet(partition: string, blobId: string, options?: {
101
+ readonly ttlSeconds?: number;
102
+ readonly nowMs?: number;
103
+ }): Promise<SegmentUrlIssue>;
104
+ /**
105
+ * SigV4 presigned PUT for a blob object (§5.9.3 direct-to-storage upload).
106
+ * The blob twin of `presignBlobGet`: the signed key embeds the `blobId`, so
107
+ * the grant places bytes at exactly the content-addressed key. The store
108
+ * does NOT recompute the SHA-256 (it is the object store, not the sync
109
+ * server); integrity is enforced at REFERENCE time — the §5.9.6 push
110
+ * existence check verifies the object exists (`has`) and every consumer
111
+ * re-verifies the content address over the received bytes (§5.9.5/§5.1). A
112
+ * client PUTting bytes that do not hash to `{blobId}` poisons only its own
113
+ * upload; no honest reference resolves to it. Issued by the upload-grant
114
+ * handler after host authentication; TTL SHOULD be ≤ 15 min (default 900).
115
+ */
116
+ presignBlobPut(partition: string, blobId: string, options?: {
117
+ readonly ttlSeconds?: number;
118
+ readonly nowMs?: number;
119
+ }): Promise<SegmentUrlIssue>;
120
+ }
121
+ /**
122
+ * Wire an `S3BlobStore` into `SyncServerConfig.blobSignedUrls` as the §5.9.5
123
+ * delegated-presign scheme for blob downloads:
124
+ * `blobSignedUrls: s3PresignedBlobUrls(blobStore, { ttlSeconds: 900 })`.
125
+ * The presigned URL is issued only after the row-derived authorization check
126
+ * (blob-handlers) — never as a bearer capability minted from the id alone.
127
+ */
128
+ export declare function s3PresignedBlobUrls(store: S3BlobStore, options?: {
129
+ readonly ttlSeconds?: number;
130
+ }): BlobPresignConfig;
131
+ /**
132
+ * Wire an `S3BlobStore` into `SyncServerConfig.blobUploadUrls` as the §5.9.3
133
+ * presigned-upload (direct-to-storage) scheme:
134
+ * `blobUploadUrls: s3PresignedBlobUploads(blobStore, { ttlSeconds: 900 })`.
135
+ * The upload-grant handler issues the presigned PUT only after host
136
+ * authentication + the size-cap check; the client PUTs bytes straight to the
137
+ * object store with no host auth (§5.9.3). Integrity stays the content-address
138
+ * check at reference/download time — never a store-side hash recompute.
139
+ */
140
+ export declare function s3PresignedBlobUploads(store: S3BlobStore, options?: {
141
+ readonly ttlSeconds?: number;
142
+ }): BlobUploadPresignConfig;