@syncular/server 0.1.3 → 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,766 @@
1
+ import { syncError } from './errors.js';
2
+ import { asBytes, asNumber, } from './pg-executor.js';
3
+ import { commitWindowPageSql, deleteRowSql, layoutsOf, migratePayload, parseLayouts, rewritePlan, rewriteRowSql, rewriteValues, SCHEMA_META_DDL_POSTGRES, scanRowPageSql, schemaDdl, selectRowScopesSql, selectRowSql, selectRowsForRewriteSql, upsertSql, upsertValues, } from './relational-rows.js';
4
+ import { matchesEffective } from './scopes.js';
5
+ /**
6
+ * Schema DDL. Applied by `PostgresServerStorage.migrate()` (idempotent).
7
+ *
8
+ * Covering index design (the reason this file exists):
9
+ * - `sync_change_scopes_pk (partition, tbl, var, value, commit_seq)` — the
10
+ * candidate scan for `readCommitWindow` ranges on the (partition, tbl,
11
+ * var, value IN …) prefix and returns `commit_seq` already ascending;
12
+ * - `sync_row_scopes_pk (partition, tbl, var, value, row_id)` — same shape
13
+ * for `scanRows`, ordered by `row_id`.
14
+ * Both are the PRIMARY KEY, so they are the clustering/covering index for
15
+ * their table. No secondary index is needed for the hot path.
16
+ *
17
+ * Blob reference index (§5.9.4) — parity with the SQLite dialect's
18
+ * `sync_blob_refs`:
19
+ * - PRIMARY KEY `(partition, tbl, row_id, blob_id)` — the by-row prefix
20
+ * lets `setBlobRefs` replace a row's set with a single ranged DELETE and
21
+ * the delete path clear it by (partition, tbl, row_id);
22
+ * - the secondary `sync_blob_refs_by_blob (partition, blob_id)` index
23
+ * drives `listRowsReferencingBlob` (the download-authorization candidate
24
+ * set, §5.9.5) as an index range, never a scan. `postgres-explain
25
+ * .test.ts` asserts an `Index` node here too.
26
+ */
27
+ export const POSTGRES_DDL = `
28
+ CREATE TABLE IF NOT EXISTS sync_partitions(
29
+ partition TEXT PRIMARY KEY,
30
+ max_commit_seq BIGINT NOT NULL DEFAULT 0,
31
+ horizon_seq BIGINT NOT NULL DEFAULT 0
32
+ );
33
+ CREATE TABLE IF NOT EXISTS sync_row_scopes(
34
+ partition TEXT NOT NULL, tbl TEXT NOT NULL,
35
+ var TEXT NOT NULL, value TEXT NOT NULL, row_id TEXT NOT NULL,
36
+ PRIMARY KEY(partition, tbl, var, value, row_id)
37
+ );
38
+ CREATE TABLE IF NOT EXISTS sync_commits(
39
+ partition TEXT NOT NULL, commit_seq BIGINT NOT NULL,
40
+ client_id TEXT NOT NULL, client_commit_id TEXT NOT NULL,
41
+ actor_id TEXT NOT NULL, created_at_ms BIGINT NOT NULL,
42
+ PRIMARY KEY(partition, commit_seq)
43
+ );
44
+ CREATE INDEX IF NOT EXISTS sync_commits_by_time
45
+ ON sync_commits(partition, created_at_ms);
46
+ CREATE TABLE IF NOT EXISTS sync_changes(
47
+ partition TEXT NOT NULL, commit_seq BIGINT NOT NULL, idx INTEGER NOT NULL,
48
+ tbl TEXT NOT NULL, row_id TEXT NOT NULL, op SMALLINT NOT NULL,
49
+ row_version BIGINT, scopes JSONB NOT NULL, payload BYTEA,
50
+ PRIMARY KEY(partition, commit_seq, idx)
51
+ );
52
+ CREATE INDEX IF NOT EXISTS sync_changes_by_table
53
+ ON sync_changes(partition, commit_seq, tbl, idx);
54
+ CREATE TABLE IF NOT EXISTS sync_change_scopes(
55
+ partition TEXT NOT NULL, tbl TEXT NOT NULL,
56
+ var TEXT NOT NULL, value TEXT NOT NULL, commit_seq BIGINT NOT NULL,
57
+ PRIMARY KEY(partition, tbl, var, value, commit_seq)
58
+ );
59
+ CREATE TABLE IF NOT EXISTS sync_push_results(
60
+ partition TEXT NOT NULL, client_id TEXT NOT NULL,
61
+ client_commit_id TEXT NOT NULL, result JSONB NOT NULL,
62
+ PRIMARY KEY(partition, client_id, client_commit_id)
63
+ );
64
+ CREATE TABLE IF NOT EXISTS sync_clients(
65
+ partition TEXT NOT NULL, client_id TEXT NOT NULL, actor_id TEXT NOT NULL,
66
+ cursor BIGINT NOT NULL, subscriptions JSONB NOT NULL,
67
+ updated_at_ms BIGINT NOT NULL,
68
+ PRIMARY KEY(partition, client_id)
69
+ );
70
+ CREATE TABLE IF NOT EXISTS sync_blob_refs(
71
+ partition TEXT NOT NULL, tbl TEXT NOT NULL, row_id TEXT NOT NULL,
72
+ blob_id TEXT NOT NULL,
73
+ PRIMARY KEY(partition, tbl, row_id, blob_id)
74
+ );
75
+ CREATE INDEX IF NOT EXISTS sync_blob_refs_by_blob
76
+ ON sync_blob_refs(partition, blob_id);
77
+ `;
78
+ function toBase64(bytes) {
79
+ return Buffer.from(bytes).toString('base64');
80
+ }
81
+ function fromBase64(text) {
82
+ return new Uint8Array(Buffer.from(text, 'base64'));
83
+ }
84
+ /**
85
+ * Serialize a push result to a JSON-able object (stored in a JSONB column).
86
+ * `serverRow` bytes are base64-encoded — JSONB cannot hold raw bytes.
87
+ */
88
+ function serializePushResult(result) {
89
+ return {
90
+ status: result.status,
91
+ ...(result.commitSeq !== undefined ? { commitSeq: result.commitSeq } : {}),
92
+ results: result.results.map((record) => {
93
+ if (record.status === 'conflict') {
94
+ return {
95
+ opIndex: record.opIndex,
96
+ status: record.status,
97
+ code: record.code,
98
+ message: record.message,
99
+ serverVersion: record.serverVersion,
100
+ serverRow: toBase64(record.serverRow),
101
+ };
102
+ }
103
+ if (record.status === 'error') {
104
+ return {
105
+ opIndex: record.opIndex,
106
+ status: record.status,
107
+ code: record.code,
108
+ message: record.message,
109
+ retryable: record.retryable,
110
+ };
111
+ }
112
+ return { opIndex: record.opIndex, status: record.status };
113
+ }),
114
+ };
115
+ }
116
+ function deserializePushResult(value) {
117
+ const parsed = value;
118
+ const results = parsed.results.map((record) => {
119
+ if (record.status === 'conflict') {
120
+ return {
121
+ opIndex: record.opIndex,
122
+ status: 'conflict',
123
+ code: record.code ?? '',
124
+ message: record.message ?? '',
125
+ serverVersion: record.serverVersion ?? 0,
126
+ serverRow: fromBase64(record.serverRow ?? ''),
127
+ };
128
+ }
129
+ if (record.status === 'error') {
130
+ return {
131
+ opIndex: record.opIndex,
132
+ status: 'error',
133
+ code: record.code ?? '',
134
+ message: record.message ?? '',
135
+ retryable: record.retryable ?? false,
136
+ };
137
+ }
138
+ return { opIndex: record.opIndex, status: 'applied' };
139
+ });
140
+ return {
141
+ status: parsed.status,
142
+ ...(parsed.commitSeq !== undefined ? { commitSeq: parsed.commitSeq } : {}),
143
+ results,
144
+ };
145
+ }
146
+ /**
147
+ * Some drivers return a JSONB column already parsed (pglite, node-postgres);
148
+ * accept a string too for defensiveness.
149
+ */
150
+ function asJson(value) {
151
+ if (typeof value === 'string')
152
+ return JSON.parse(value);
153
+ return value;
154
+ }
155
+ function toStoredRow(record) {
156
+ return {
157
+ rowId: record.row_id,
158
+ serverVersion: asNumber(record.server_version),
159
+ scopes: asJson(record.scopes),
160
+ payload: asBytes(record.payload),
161
+ };
162
+ }
163
+ function toStoredChange(record) {
164
+ return {
165
+ table: record.tbl,
166
+ rowId: record.row_id,
167
+ op: asNumber(record.op) === 1 ? 'upsert' : 'delete',
168
+ ...(record.row_version !== null && record.row_version !== undefined
169
+ ? { rowVersion: asNumber(record.row_version) }
170
+ : {}),
171
+ scopes: asJson(record.scopes),
172
+ ...(record.payload !== null && record.payload !== undefined
173
+ ? { payload: asBytes(record.payload) }
174
+ : {}),
175
+ };
176
+ }
177
+ /**
178
+ * Migration rewrite (DESIGN "optional materialization"): keyset-paged walk
179
+ * of a row table inside the migration transaction; when `oldLayout` is
180
+ * given every payload re-encodes under the current columns, and the
181
+ * projection (when materialized) refreshes from the payload either way.
182
+ */
183
+ async function rewriteRowsOn(q, table, oldLayout) {
184
+ const select = selectRowsForRewriteSql(table, 'postgres');
185
+ const update = rewriteRowSql(table, 'postgres');
186
+ const BATCH = 500;
187
+ let afterPartition = '';
188
+ let afterRowId = '';
189
+ for (;;) {
190
+ const { rows } = await q.query(select, [afterPartition, afterRowId, BATCH]);
191
+ if (rows.length === 0)
192
+ break;
193
+ for (const row of rows) {
194
+ const bytes = asBytes(row.payload);
195
+ const payload = oldLayout !== undefined
196
+ ? migratePayload(oldLayout, table, bytes)
197
+ : bytes;
198
+ await q.query(update, rewriteValues(table, row.partition, row.row_id, payload, 'postgres'));
199
+ }
200
+ const last = rows[rows.length - 1];
201
+ if (last === undefined || rows.length < BATCH)
202
+ break;
203
+ afterPartition = last.partition;
204
+ afterRowId = last.row_id;
205
+ }
206
+ }
207
+ /** Shared read/write query logic, parameterized by the queryable in scope. */
208
+ async function getRowOn(q, compiled, partition, rowId) {
209
+ const { rows } = await q.query(selectRowSql(compiled, 'postgres'), [partition, rowId]);
210
+ const record = rows[0];
211
+ return record === undefined ? undefined : toStoredRow(record);
212
+ }
213
+ async function writeRowOn(q, compiled, partition, row) {
214
+ await q.query(upsertSql(compiled, 'postgres'), upsertValues(compiled, partition, row, 'postgres'));
215
+ await q.query('DELETE FROM sync_row_scopes WHERE partition=$1 AND tbl=$2 AND row_id=$3', [partition, compiled.name, row.rowId]);
216
+ for (const [variable, value] of Object.entries(row.scopes)) {
217
+ await q.query(`INSERT INTO sync_row_scopes(partition, tbl, var, value, row_id)
218
+ VALUES ($1,$2,$3,$4,$5) ON CONFLICT DO NOTHING`, [partition, compiled.name, variable, value, row.rowId]);
219
+ }
220
+ }
221
+ class PostgresTransaction {
222
+ #client;
223
+ #partition;
224
+ #resolveTable;
225
+ #open = true;
226
+ /** Resolves/rejects the `transaction(fn)` wrapper (see `begin`). */
227
+ #resolve;
228
+ #reject;
229
+ constructor(client, partition, resolveTable, resolve, reject) {
230
+ this.#client = client;
231
+ this.#partition = partition;
232
+ this.#resolveTable = resolveTable;
233
+ this.#resolve = resolve;
234
+ this.#reject = reject;
235
+ }
236
+ #assertOpen() {
237
+ if (!this.#open)
238
+ throw new Error('transaction already finished');
239
+ }
240
+ getRow(table, rowId) {
241
+ this.#assertOpen();
242
+ return getRowOn(this.#client, this.#resolveTable(table), this.#partition, rowId);
243
+ }
244
+ async upsertRow(table, row) {
245
+ this.#assertOpen();
246
+ await writeRowOn(this.#client, this.#resolveTable(table), this.#partition, row);
247
+ }
248
+ async deleteRow(table, rowId) {
249
+ this.#assertOpen();
250
+ await this.#client.query(deleteRowSql(this.#resolveTable(table), 'postgres'), [this.#partition, rowId]);
251
+ await this.#client.query('DELETE FROM sync_row_scopes WHERE partition=$1 AND tbl=$2 AND row_id=$3', [this.#partition, table, rowId]);
252
+ // §5.9.4: a deleted row references no blobs.
253
+ await this.#client.query('DELETE FROM sync_blob_refs WHERE partition=$1 AND tbl=$2 AND row_id=$3', [this.#partition, table, rowId]);
254
+ }
255
+ async setBlobRefs(table, rowId, blobIds) {
256
+ this.#assertOpen();
257
+ // Replace the row's reference set atomically inside the commit tx (§5.9.4).
258
+ await this.#client.query('DELETE FROM sync_blob_refs WHERE partition=$1 AND tbl=$2 AND row_id=$3', [this.#partition, table, rowId]);
259
+ for (const blobId of blobIds) {
260
+ await this.#client.query(`INSERT INTO sync_blob_refs(partition, tbl, row_id, blob_id)
261
+ VALUES ($1,$2,$3,$4) ON CONFLICT DO NOTHING`, [this.#partition, table, rowId, blobId]);
262
+ }
263
+ }
264
+ async appendCommit(commit) {
265
+ this.#assertOpen();
266
+ const q = this.#client;
267
+ const p = this.#partition;
268
+ // Allocate the next dense commitSeq under a per-partition row lock: the
269
+ // UPDATE … RETURNING serializes concurrent pushes to this partition and
270
+ // never leaves a gap on rollback (see the file header).
271
+ const { rows } = await q.query(`INSERT INTO sync_partitions(partition, max_commit_seq) VALUES ($1, 1)
272
+ ON CONFLICT (partition) DO UPDATE
273
+ SET max_commit_seq = sync_partitions.max_commit_seq + 1
274
+ RETURNING max_commit_seq`, [p]);
275
+ const commitSeq = asNumber(rows[0]?.max_commit_seq);
276
+ await q.query(`INSERT INTO sync_commits(partition, commit_seq, client_id, client_commit_id, actor_id, created_at_ms)
277
+ VALUES ($1,$2,$3,$4,$5,$6)`, [
278
+ p,
279
+ commitSeq,
280
+ commit.clientId,
281
+ commit.clientCommitId,
282
+ commit.actorId,
283
+ commit.createdAtMs,
284
+ ]);
285
+ for (let idx = 0; idx < commit.changes.length; idx++) {
286
+ const change = commit.changes[idx];
287
+ if (change === undefined)
288
+ continue;
289
+ await q.query(`INSERT INTO sync_changes(partition, commit_seq, idx, tbl, row_id, op, row_version, scopes, payload)
290
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`, [
291
+ p,
292
+ commitSeq,
293
+ idx,
294
+ change.table,
295
+ change.rowId,
296
+ change.op === 'upsert' ? 1 : 2,
297
+ change.rowVersion ?? null,
298
+ JSON.stringify(change.scopes),
299
+ change.payload ?? null,
300
+ ]);
301
+ for (const [variable, value] of Object.entries(change.scopes)) {
302
+ await q.query(`INSERT INTO sync_change_scopes(partition, tbl, var, value, commit_seq)
303
+ VALUES ($1,$2,$3,$4,$5) ON CONFLICT DO NOTHING`, [p, change.table, variable, value, commitSeq]);
304
+ }
305
+ }
306
+ return commitSeq;
307
+ }
308
+ async putPushResult(clientId, clientCommitId, result) {
309
+ this.#assertOpen();
310
+ await this.#client.query(`INSERT INTO sync_push_results(partition, client_id, client_commit_id, result)
311
+ VALUES ($1,$2,$3,$4)
312
+ ON CONFLICT (partition, client_id, client_commit_id) DO UPDATE
313
+ SET result=EXCLUDED.result`, [
314
+ this.#partition,
315
+ clientId,
316
+ clientCommitId,
317
+ JSON.stringify(serializePushResult(result)),
318
+ ]);
319
+ }
320
+ async commit() {
321
+ this.#assertOpen();
322
+ this.#open = false;
323
+ // Signal the transaction wrapper to COMMIT; wait for it to actually land.
324
+ this.#resolve();
325
+ await this.#done;
326
+ }
327
+ async rollback() {
328
+ if (!this.#open)
329
+ return;
330
+ this.#open = false;
331
+ this.#reject(new RollbackSignal());
332
+ // Swallow — a rollback is a normal outcome, not an error to the caller.
333
+ await this.#done.catch(() => { });
334
+ }
335
+ /** Set by `begin`: resolves when BEGIN…COMMIT/ROLLBACK has fully landed. */
336
+ #done;
337
+ _attachDone(done) {
338
+ this.#done = done;
339
+ }
340
+ }
341
+ /** Internal marker: a caller-requested rollback, not a real failure. */
342
+ class RollbackSignal extends Error {
343
+ constructor() {
344
+ super('rollback');
345
+ this.name = 'RollbackSignal';
346
+ }
347
+ }
348
+ export class PostgresServerStorage {
349
+ #exec;
350
+ /** Set by `ensureSchema`: app-table lookup for the relational row store. */
351
+ #tables;
352
+ #schemaVersion;
353
+ constructor(exec) {
354
+ this.#exec = exec;
355
+ }
356
+ /** Apply the schema DDL (idempotent). Call once before use. */
357
+ async migrate() {
358
+ // Split on the statement boundary so drivers that reject multi-statement
359
+ // query strings (node-postgres extended protocol) still apply each DDL.
360
+ const statements = POSTGRES_DDL.split(';')
361
+ .map((s) => s.trim())
362
+ .filter((s) => s.length > 0);
363
+ for (const statement of statements) {
364
+ await this.#exec.query(statement);
365
+ }
366
+ }
367
+ /** Resolve a table's compiled schema; row operations require `ensureSchema`. */
368
+ table(name) {
369
+ const table = this.#tables?.get(name);
370
+ if (table === undefined) {
371
+ throw new Error(`unknown table ${JSON.stringify(name)} — ensureSchema(schema) must run before row operations`);
372
+ }
373
+ return table;
374
+ }
375
+ async ensureSchema(schema) {
376
+ // Memoized fast path: same instance, same schema version.
377
+ if (this.#schemaVersion === schema.version)
378
+ return;
379
+ await this.migrate();
380
+ await this.#exec.query(SCHEMA_META_DDL_POSTGRES);
381
+ const marker = await this.#exec.query('SELECT schema_version, layouts FROM sync_schema_meta WHERE id=1');
382
+ const stored = marker.rows[0] === undefined
383
+ ? undefined
384
+ : asNumber(marker.rows[0].schema_version);
385
+ if (stored !== undefined && stored > schema.version) {
386
+ throw new Error(`stored schema version ${stored} is newer than the configured schema (${schema.version}) — refusing to run an older server against a migrated database`);
387
+ }
388
+ if (stored === undefined || stored < schema.version) {
389
+ // Introspect existing app tables, apply the migration subset
390
+ // (CREATE TABLE / ADD COLUMN / CREATE INDEX), then rewrite stored
391
+ // rows (payload re-encode for layout changes and/or projection
392
+ // backfill for flipped-on materialization) — all inside one
393
+ // transaction (Postgres DDL is transactional — a failed bump leaves
394
+ // no half-state).
395
+ const layouts = parseLayouts(typeof marker.rows[0]?.layouts === 'string'
396
+ ? marker.rows[0].layouts
397
+ : undefined);
398
+ await this.#exec.transaction(async (client) => {
399
+ const existing = new Map();
400
+ for (const table of schema.tables.values()) {
401
+ const { rows } = await client.query(`SELECT column_name FROM information_schema.columns
402
+ WHERE table_schema = current_schema() AND table_name = $1`, [table.name]);
403
+ if (rows.length > 0) {
404
+ existing.set(table.name, new Set(rows.map((r) => r.column_name)));
405
+ }
406
+ }
407
+ for (const statement of schemaDdl(schema, existing, 'postgres')) {
408
+ await client.query(statement);
409
+ }
410
+ for (const table of schema.tables.values()) {
411
+ const oldLayout = layouts[table.name];
412
+ const plan = rewritePlan(table, oldLayout, existing.get(table.name));
413
+ if (!plan.migrate && !plan.backfill)
414
+ continue;
415
+ await rewriteRowsOn(client, table, plan.migrate ? oldLayout : undefined);
416
+ }
417
+ await client.query(`INSERT INTO sync_schema_meta(id, schema_version, layouts) VALUES (1, $1, $2)
418
+ ON CONFLICT (id) DO UPDATE
419
+ SET schema_version=EXCLUDED.schema_version, layouts=EXCLUDED.layouts`, [schema.version, layoutsOf(schema)]);
420
+ });
421
+ }
422
+ this.#tables = schema.tables;
423
+ this.#schemaVersion = schema.version;
424
+ }
425
+ /**
426
+ * Open a real Postgres transaction. The push handler drives the returned
427
+ * `StorageTransaction` imperatively (getRow/upsert/…/commit), but the
428
+ * driver seam models a transaction as a callback scope. We bridge the two:
429
+ * `transaction(fn)` blocks inside `fn` on a promise that the imperative
430
+ * `commit()`/`rollback()` resolves/rejects, so BEGIN…COMMIT wraps exactly
431
+ * the handler's writes on one pinned connection.
432
+ */
433
+ async begin(partition) {
434
+ let resolveReady;
435
+ const ready = new Promise((r) => {
436
+ resolveReady = r;
437
+ });
438
+ let resolveScope;
439
+ let rejectScope;
440
+ const scope = new Promise((res, rej) => {
441
+ resolveScope = res;
442
+ rejectScope = rej;
443
+ });
444
+ const done = this.#exec
445
+ .transaction(async (client) => {
446
+ const tx = new PostgresTransaction(client, partition, (name) => this.table(name), resolveScope, rejectScope);
447
+ resolveReady(tx);
448
+ // Hold the transaction open until commit()/rollback() settles `scope`.
449
+ await scope;
450
+ })
451
+ .catch((error) => {
452
+ if (error instanceof RollbackSignal)
453
+ return;
454
+ throw error;
455
+ });
456
+ const tx = await ready;
457
+ tx._attachDone(done);
458
+ return tx;
459
+ }
460
+ async getMaxCommitSeq(partition) {
461
+ const { rows } = await this.#exec.query('SELECT max_commit_seq FROM sync_partitions WHERE partition=$1', [partition]);
462
+ return rows[0] === undefined ? 0 : asNumber(rows[0].max_commit_seq);
463
+ }
464
+ async getHorizonSeq(partition) {
465
+ const { rows } = await this.#exec.query('SELECT horizon_seq FROM sync_partitions WHERE partition=$1', [partition]);
466
+ return rows[0] === undefined ? 0 : asNumber(rows[0].horizon_seq);
467
+ }
468
+ async setHorizonSeq(partition, seq) {
469
+ await this.#exec.query(`INSERT INTO sync_partitions(partition, horizon_seq) VALUES ($1,$2)
470
+ ON CONFLICT (partition) DO UPDATE SET horizon_seq=EXCLUDED.horizon_seq`, [partition, seq]);
471
+ }
472
+ async pruneCommitsThrough(partition, seq) {
473
+ const removed = await this.#exec.query('DELETE FROM sync_commits WHERE partition=$1 AND commit_seq<=$2', [partition, seq]);
474
+ await this.#exec.query('DELETE FROM sync_changes WHERE partition=$1 AND commit_seq<=$2', [partition, seq]);
475
+ await this.#exec.query('DELETE FROM sync_change_scopes WHERE partition=$1 AND commit_seq<=$2', [partition, seq]);
476
+ return removed.rowCount;
477
+ }
478
+ async getCommitSeqBefore(partition, createdBeforeMs) {
479
+ const { rows } = await this.#exec.query('SELECT max(commit_seq) AS seq FROM sync_commits WHERE partition=$1 AND created_at_ms<$2', [partition, createdBeforeMs]);
480
+ const seq = rows[0]?.seq;
481
+ return seq === null || seq === undefined ? 0 : asNumber(seq);
482
+ }
483
+ getRow(partition, table, rowId) {
484
+ return getRowOn(this.#exec, this.table(table), partition, rowId);
485
+ }
486
+ async getPushResult(partition, clientId, clientCommitId) {
487
+ const { rows } = await this.#exec.query('SELECT result FROM sync_push_results WHERE partition=$1 AND client_id=$2 AND client_commit_id=$3', [partition, clientId, clientCommitId]);
488
+ if (rows[0] === undefined)
489
+ return undefined;
490
+ try {
491
+ return deserializePushResult(asJson(rows[0].result));
492
+ }
493
+ catch {
494
+ throw syncError('sync.idempotency_cache_miss', 'persisted push result unreadable (§6.3)');
495
+ }
496
+ }
497
+ async readCommitWindow(partition, query) {
498
+ const variables = Object.keys(query.scopeFilter).sort();
499
+ const firstVariable = variables[0];
500
+ if (firstVariable === undefined)
501
+ return [];
502
+ const firstValues = query.scopeFilter[firstVariable] ?? [];
503
+ if (firstValues.length === 0)
504
+ return [];
505
+ // Candidates via the inverted index (one variable): the (partition, tbl,
506
+ // var, value, commit_seq) PK makes the candidate subquery an index range
507
+ // scan returning commit_seq already ascending (see postgres-explain
508
+ // .test.ts), LEFT JOINed to the commit meta + the table's changes — one
509
+ // round trip per page, never two per candidate (see
510
+ // `commitWindowPageSql`). Exact multi-variable verification against the
511
+ // stored scope map happens below.
512
+ const sql = commitWindowPageSql(firstValues.length, 'postgres');
513
+ const commits = [];
514
+ let deliveredChanges = 0;
515
+ let afterSeq = query.afterSeq;
516
+ const batchSize = Math.max(64, query.limitChanges);
517
+ while (deliveredChanges < query.limitChanges) {
518
+ const { rows: records } = await this.#exec.query(sql, [
519
+ partition,
520
+ query.table,
521
+ firstVariable,
522
+ ...firstValues,
523
+ afterSeq,
524
+ query.throughSeq,
525
+ batchSize,
526
+ ]);
527
+ if (records.length === 0)
528
+ break;
529
+ // Fold the page: rows arrive ordered (commit_seq, idx); consecutive
530
+ // rows with the same commit_seq are one candidate. Every candidate
531
+ // advances the cursor — including vanished commits (NULL meta, LEFT
532
+ // JOIN contract) and commits whose changes all fail the exact
533
+ // multi-variable match.
534
+ let candidateCount = 0;
535
+ let i = 0;
536
+ while (i < records.length) {
537
+ const head = records[i];
538
+ if (head === undefined)
539
+ break;
540
+ const commitSeq = asNumber(head.commit_seq);
541
+ candidateCount += 1;
542
+ afterSeq = commitSeq;
543
+ const changes = [];
544
+ for (; i < records.length; i++) {
545
+ const record = records[i];
546
+ if (record === undefined || asNumber(record.commit_seq) !== commitSeq)
547
+ break;
548
+ // NULL tbl: a candidate with no change rows for the table.
549
+ if (record.tbl === null || record.row_id === null)
550
+ continue;
551
+ const change = toStoredChange({
552
+ tbl: record.tbl,
553
+ row_id: record.row_id,
554
+ op: record.op,
555
+ row_version: record.row_version,
556
+ scopes: record.scopes,
557
+ payload: record.payload,
558
+ });
559
+ if (matchesEffective(change.scopes, query.scopeFilter)) {
560
+ changes.push(change);
561
+ }
562
+ }
563
+ // NULL meta: the commit vanished — cursor advanced, nothing emitted.
564
+ if (head.actor_id === null)
565
+ continue;
566
+ if (changes.length === 0)
567
+ continue;
568
+ commits.push({
569
+ commitSeq,
570
+ createdAtMs: asNumber(head.created_at_ms),
571
+ actorId: head.actor_id,
572
+ changes,
573
+ });
574
+ deliveredChanges += changes.length;
575
+ if (deliveredChanges >= query.limitChanges)
576
+ break;
577
+ }
578
+ if (deliveredChanges >= query.limitChanges)
579
+ break;
580
+ if (candidateCount < batchSize)
581
+ break;
582
+ }
583
+ return commits;
584
+ }
585
+ async scanRows(partition, query) {
586
+ const variables = Object.keys(query.scopeFilter).sort();
587
+ const firstVariable = variables[0];
588
+ if (firstVariable === undefined)
589
+ return [];
590
+ const firstValues = query.scopeFilter[firstVariable] ?? [];
591
+ if (firstValues.length === 0)
592
+ return [];
593
+ // Candidates via the inverted index (ordered + LIMITed at the covering
594
+ // PK) LEFT JOINed to the row table — one round trip per page, never one
595
+ // per row (see `scanRowPageSql`). Exact multi-variable verification
596
+ // against the stored scope map happens below.
597
+ const sql = scanRowPageSql(this.table(query.table), firstValues.length, 'postgres');
598
+ const rows = [];
599
+ let afterRowId = query.afterRowId ?? '';
600
+ const batchSize = Math.max(64, query.limit);
601
+ while (rows.length < query.limit) {
602
+ const { rows: records } = await this.#exec.query(sql, [
603
+ partition,
604
+ query.table,
605
+ firstVariable,
606
+ ...firstValues,
607
+ afterRowId,
608
+ batchSize,
609
+ ]);
610
+ if (records.length === 0)
611
+ break;
612
+ for (const record of records) {
613
+ afterRowId = record.row_id;
614
+ // NULL payload: an index candidate whose row vanished — it still
615
+ // advances the keyset cursor (LEFT JOIN contract) but yields no row.
616
+ if (record.payload === null || record.payload === undefined)
617
+ continue;
618
+ const stored = toStoredRow(record);
619
+ if (!matchesEffective(stored.scopes, query.scopeFilter))
620
+ continue;
621
+ rows.push(stored);
622
+ if (rows.length >= query.limit)
623
+ break;
624
+ }
625
+ if (records.length < batchSize)
626
+ break;
627
+ }
628
+ return rows;
629
+ }
630
+ async getClientRecord(partition, clientId) {
631
+ const { rows } = await this.#exec.query('SELECT client_id, actor_id, cursor, subscriptions, updated_at_ms FROM sync_clients WHERE partition=$1 AND client_id=$2', [partition, clientId]);
632
+ const record = rows[0];
633
+ if (record === undefined)
634
+ return undefined;
635
+ return {
636
+ clientId: record.client_id,
637
+ actorId: record.actor_id,
638
+ cursor: asNumber(record.cursor),
639
+ updatedAtMs: asNumber(record.updated_at_ms),
640
+ subscriptions: asJson(record.subscriptions),
641
+ };
642
+ }
643
+ async putClientRecord(partition, record) {
644
+ await this.#exec.query(`INSERT INTO sync_clients(partition, client_id, actor_id, cursor, subscriptions, updated_at_ms)
645
+ VALUES ($1,$2,$3,$4,$5,$6)
646
+ ON CONFLICT (partition, client_id) DO UPDATE
647
+ SET actor_id=EXCLUDED.actor_id, cursor=EXCLUDED.cursor,
648
+ subscriptions=EXCLUDED.subscriptions,
649
+ updated_at_ms=EXCLUDED.updated_at_ms`, [
650
+ partition,
651
+ record.clientId,
652
+ record.actorId,
653
+ record.cursor,
654
+ JSON.stringify(record.subscriptions),
655
+ record.updatedAtMs,
656
+ ]);
657
+ }
658
+ async listClientCursors(partition) {
659
+ const { rows } = await this.#exec.query('SELECT client_id, cursor, updated_at_ms FROM sync_clients WHERE partition=$1', [partition]);
660
+ return rows.map((r) => ({
661
+ clientId: r.client_id,
662
+ cursor: asNumber(r.cursor),
663
+ updatedAtMs: asNumber(r.updated_at_ms),
664
+ }));
665
+ }
666
+ async listRowsReferencingBlob(partition, blobId) {
667
+ // Candidate rows via the by-blob index (§5.9.4/§5.9.5); each row's
668
+ // stored scopes come from sync_rows for the §3.4 authorization test.
669
+ const { rows: refs } = await this.#exec.query('SELECT tbl, row_id FROM sync_blob_refs WHERE partition=$1 AND blob_id=$2', [partition, blobId]);
670
+ const out = [];
671
+ for (const ref of refs) {
672
+ const compiled = this.#tables?.get(ref.tbl);
673
+ if (compiled === undefined)
674
+ continue; // table no longer in the schema
675
+ const { rows } = await this.#exec.query(selectRowScopesSql(compiled, 'postgres'), [partition, ref.row_id]);
676
+ const row = rows[0];
677
+ if (row === undefined)
678
+ continue;
679
+ out.push({
680
+ table: ref.tbl,
681
+ rowId: ref.row_id,
682
+ scopes: asJson(row.scopes),
683
+ });
684
+ }
685
+ return out;
686
+ }
687
+ async listReferencedBlobIds(partition) {
688
+ const { rows } = await this.#exec.query('SELECT DISTINCT blob_id FROM sync_blob_refs WHERE partition=$1', [partition]);
689
+ return rows.map((r) => r.blob_id);
690
+ }
691
+ // -- admin/console read surface (TODO §2.5) --------------------------------
692
+ async listClientRecords(partition) {
693
+ const { rows } = await this.#exec.query('SELECT client_id, actor_id, cursor, subscriptions, updated_at_ms FROM sync_clients WHERE partition=$1 ORDER BY updated_at_ms DESC', [partition]);
694
+ return rows.map((r) => ({
695
+ clientId: r.client_id,
696
+ actorId: r.actor_id,
697
+ cursor: asNumber(r.cursor),
698
+ updatedAtMs: asNumber(r.updated_at_ms),
699
+ subscriptions: (typeof r.subscriptions === 'string'
700
+ ? JSON.parse(r.subscriptions)
701
+ : r.subscriptions),
702
+ }));
703
+ }
704
+ async listCommitMetadata(partition, query) {
705
+ const { rows } = query.table
706
+ ? await this.#exec.query(`SELECT c.commit_seq, c.client_id, c.client_commit_id, c.actor_id, c.created_at_ms
707
+ FROM sync_commits c
708
+ WHERE c.partition=$1 AND c.commit_seq>$2
709
+ AND EXISTS (SELECT 1 FROM sync_changes ch
710
+ WHERE ch.partition=c.partition AND ch.commit_seq=c.commit_seq AND ch.tbl=$3)
711
+ ORDER BY c.commit_seq DESC LIMIT $4`, [partition, query.afterSeq, query.table, query.limit])
712
+ : await this.#exec.query(`SELECT commit_seq, client_id, client_commit_id, actor_id, created_at_ms
713
+ FROM sync_commits
714
+ WHERE partition=$1 AND commit_seq>$2
715
+ ORDER BY commit_seq DESC LIMIT $3`, [partition, query.afterSeq, query.limit]);
716
+ const out = [];
717
+ for (const row of rows) {
718
+ const commitSeq = asNumber(row.commit_seq);
719
+ const changes = await this.#exec.query('SELECT tbl, count(*) AS n FROM sync_changes WHERE partition=$1 AND commit_seq=$2 GROUP BY tbl', [partition, commitSeq]);
720
+ out.push({
721
+ commitSeq,
722
+ clientId: row.client_id,
723
+ clientCommitId: row.client_commit_id,
724
+ actorId: row.actor_id,
725
+ createdAtMs: asNumber(row.created_at_ms),
726
+ changeCount: changes.rows.reduce((sum, c) => sum + asNumber(c.n), 0),
727
+ tables: changes.rows.map((c) => c.tbl),
728
+ });
729
+ }
730
+ return out;
731
+ }
732
+ async scopeActivity(partition, query) {
733
+ const { rows } = await this.#exec.query(`SELECT DISTINCT commit_seq, tbl FROM sync_change_scopes
734
+ WHERE partition=$1 AND var=$2 AND value=$3
735
+ ORDER BY commit_seq DESC LIMIT $4`, [partition, query.variable, query.value, query.limit]);
736
+ const out = [];
737
+ for (const row of rows) {
738
+ const commitSeq = asNumber(row.commit_seq);
739
+ const meta = await this.#exec.query('SELECT actor_id, created_at_ms FROM sync_commits WHERE partition=$1 AND commit_seq=$2', [partition, commitSeq]);
740
+ const metaRow = meta.rows[0];
741
+ if (metaRow === undefined)
742
+ continue;
743
+ const count = await this.#exec.query('SELECT count(*) AS n FROM sync_changes WHERE partition=$1 AND commit_seq=$2 AND tbl=$3', [partition, commitSeq, row.tbl]);
744
+ out.push({
745
+ commitSeq,
746
+ table: row.tbl,
747
+ createdAtMs: asNumber(metaRow.created_at_ms),
748
+ actorId: metaRow.actor_id,
749
+ changeCount: count.rows[0] === undefined ? 0 : asNumber(count.rows[0].n),
750
+ });
751
+ }
752
+ return out;
753
+ }
754
+ async getRowScopes(partition, table, rowId) {
755
+ const { rows } = await this.#exec.query(selectRowScopesSql(this.table(table), 'postgres'), [partition, rowId]);
756
+ const row = rows[0];
757
+ if (row === undefined)
758
+ return undefined;
759
+ return {
760
+ serverVersion: asNumber(row.server_version),
761
+ scopes: (typeof row.scopes === 'string'
762
+ ? JSON.parse(row.scopes)
763
+ : row.scopes),
764
+ };
765
+ }
766
+ }