@syncular/server 0.0.6-96 → 0.1.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 (597) hide show
  1. package/README.md +1 -2
  2. package/dist/auth-leases.d.ts +85 -0
  3. package/dist/auth-leases.d.ts.map +1 -0
  4. package/dist/auth-leases.js +368 -0
  5. package/dist/auth-leases.js.map +1 -0
  6. package/dist/better-sqlite3.d.ts +22 -0
  7. package/dist/better-sqlite3.d.ts.map +1 -0
  8. package/dist/better-sqlite3.js +16 -0
  9. package/dist/better-sqlite3.js.map +1 -0
  10. package/dist/blobs/access.d.ts +35 -0
  11. package/dist/blobs/access.d.ts.map +1 -0
  12. package/dist/blobs/access.js +149 -0
  13. package/dist/blobs/access.js.map +1 -0
  14. package/dist/blobs/adapters/database.d.ts +26 -9
  15. package/dist/blobs/adapters/database.d.ts.map +1 -1
  16. package/dist/blobs/adapters/database.js +68 -23
  17. package/dist/blobs/adapters/database.js.map +1 -1
  18. package/dist/blobs/index.d.ts +1 -0
  19. package/dist/blobs/index.d.ts.map +1 -1
  20. package/dist/blobs/index.js +1 -0
  21. package/dist/blobs/index.js.map +1 -1
  22. package/dist/blobs/manager.d.ts +69 -44
  23. package/dist/blobs/manager.d.ts.map +1 -1
  24. package/dist/blobs/manager.js +250 -65
  25. package/dist/blobs/manager.js.map +1 -1
  26. package/dist/blobs/migrate.d.ts.map +1 -1
  27. package/dist/blobs/migrate.js +16 -8
  28. package/dist/blobs/migrate.js.map +1 -1
  29. package/dist/blobs/types.d.ts +4 -0
  30. package/dist/blobs/types.d.ts.map +1 -1
  31. package/dist/bun-sqlite.d.ts +19 -0
  32. package/dist/bun-sqlite.d.ts.map +1 -0
  33. package/dist/bun-sqlite.js +19 -0
  34. package/dist/bun-sqlite.js.map +1 -0
  35. package/dist/clients.d.ts +1 -0
  36. package/dist/clients.d.ts.map +1 -1
  37. package/dist/clients.js.map +1 -1
  38. package/dist/cloudflare/durable-object.d.ts +93 -0
  39. package/dist/cloudflare/durable-object.d.ts.map +1 -0
  40. package/dist/cloudflare/durable-object.js +210 -0
  41. package/dist/cloudflare/durable-object.js.map +1 -0
  42. package/dist/cloudflare/index.d.ts +22 -0
  43. package/dist/cloudflare/index.d.ts.map +1 -0
  44. package/dist/cloudflare/index.js +22 -0
  45. package/dist/cloudflare/index.js.map +1 -0
  46. package/dist/cloudflare/r2.d.ts +180 -0
  47. package/dist/cloudflare/r2.d.ts.map +1 -0
  48. package/dist/cloudflare/r2.js +258 -0
  49. package/dist/cloudflare/r2.js.map +1 -0
  50. package/dist/cloudflare/scope-cache.d.ts +54 -0
  51. package/dist/cloudflare/scope-cache.d.ts.map +1 -0
  52. package/dist/cloudflare/scope-cache.js +223 -0
  53. package/dist/cloudflare/scope-cache.js.map +1 -0
  54. package/dist/cloudflare/sentry.d.ts +47 -0
  55. package/dist/cloudflare/sentry.d.ts.map +1 -0
  56. package/dist/cloudflare/sentry.js +163 -0
  57. package/dist/cloudflare/sentry.js.map +1 -0
  58. package/dist/cloudflare/worker.d.ts +46 -0
  59. package/dist/cloudflare/worker.d.ts.map +1 -0
  60. package/dist/cloudflare/worker.js +63 -0
  61. package/dist/cloudflare/worker.js.map +1 -0
  62. package/dist/commit-integrity.d.ts +38 -0
  63. package/dist/commit-integrity.d.ts.map +1 -0
  64. package/dist/commit-integrity.js +260 -0
  65. package/dist/commit-integrity.js.map +1 -0
  66. package/dist/compaction.js.map +1 -1
  67. package/dist/crdt-yjs/index.d.ts +99 -0
  68. package/dist/crdt-yjs/index.d.ts.map +1 -0
  69. package/dist/crdt-yjs/index.js +629 -0
  70. package/dist/crdt-yjs/index.js.map +1 -0
  71. package/dist/d1.d.ts +13 -0
  72. package/dist/d1.d.ts.map +1 -0
  73. package/dist/d1.js +14 -0
  74. package/dist/d1.js.map +1 -0
  75. package/dist/dialect/base.d.ts +11 -1
  76. package/dist/dialect/base.d.ts.map +1 -1
  77. package/dist/dialect/base.js +42 -5
  78. package/dist/dialect/base.js.map +1 -1
  79. package/dist/dialect/helpers.d.ts +6 -0
  80. package/dist/dialect/helpers.d.ts.map +1 -1
  81. package/dist/dialect/helpers.js +24 -0
  82. package/dist/dialect/helpers.js.map +1 -1
  83. package/dist/dialect/types.d.ts +40 -0
  84. package/dist/dialect/types.d.ts.map +1 -1
  85. package/dist/encrypted-crdt.d.ts +67 -0
  86. package/dist/encrypted-crdt.d.ts.map +1 -0
  87. package/dist/encrypted-crdt.js +426 -0
  88. package/dist/encrypted-crdt.js.map +1 -0
  89. package/dist/errors.d.ts +10 -0
  90. package/dist/errors.d.ts.map +1 -0
  91. package/dist/errors.js +13 -0
  92. package/dist/errors.js.map +1 -0
  93. package/dist/filesystem/index.d.ts +44 -0
  94. package/dist/filesystem/index.d.ts.map +1 -0
  95. package/dist/filesystem/index.js +164 -0
  96. package/dist/filesystem/index.js.map +1 -0
  97. package/dist/handlers/collection.d.ts +11 -3
  98. package/dist/handlers/collection.d.ts.map +1 -1
  99. package/dist/handlers/collection.js +34 -56
  100. package/dist/handlers/collection.js.map +1 -1
  101. package/dist/handlers/create-handler.d.ts +31 -7
  102. package/dist/handlers/create-handler.d.ts.map +1 -1
  103. package/dist/handlers/create-handler.js +332 -115
  104. package/dist/handlers/create-handler.js.map +1 -1
  105. package/dist/handlers/types.d.ts +86 -1
  106. package/dist/handlers/types.d.ts.map +1 -1
  107. package/dist/helpers/conflict.js.map +1 -1
  108. package/dist/helpers/emitted-change.js.map +1 -1
  109. package/dist/helpers/index.d.ts +2 -0
  110. package/dist/helpers/index.d.ts.map +1 -1
  111. package/dist/helpers/index.js +2 -0
  112. package/dist/helpers/index.js.map +1 -1
  113. package/dist/helpers/paginate.js.map +1 -1
  114. package/dist/helpers/scope-authorization.d.ts +7 -0
  115. package/dist/helpers/scope-authorization.d.ts.map +1 -0
  116. package/dist/helpers/scope-authorization.js +19 -0
  117. package/dist/helpers/scope-authorization.js.map +1 -0
  118. package/dist/helpers/scope-commit-index.d.ts +12 -0
  119. package/dist/helpers/scope-commit-index.d.ts.map +1 -0
  120. package/dist/helpers/scope-commit-index.js +35 -0
  121. package/dist/helpers/scope-commit-index.js.map +1 -0
  122. package/dist/helpers/scope-strings.js.map +1 -1
  123. package/dist/hono/api-key-auth.d.ts +49 -0
  124. package/dist/hono/api-key-auth.d.ts.map +1 -0
  125. package/dist/hono/api-key-auth.js +108 -0
  126. package/dist/hono/api-key-auth.js.map +1 -0
  127. package/dist/hono/audit-redaction.d.ts +20 -0
  128. package/dist/hono/audit-redaction.d.ts.map +1 -0
  129. package/dist/hono/audit-redaction.js +85 -0
  130. package/dist/hono/audit-redaction.js.map +1 -0
  131. package/dist/hono/blobs.d.ts +75 -0
  132. package/dist/hono/blobs.d.ts.map +1 -0
  133. package/dist/hono/blobs.js +586 -0
  134. package/dist/hono/blobs.js.map +1 -0
  135. package/dist/hono/console/gateway.d.ts +42 -0
  136. package/dist/hono/console/gateway.d.ts.map +1 -0
  137. package/dist/hono/console/gateway.js +2158 -0
  138. package/dist/hono/console/gateway.js.map +1 -0
  139. package/dist/hono/console/live-auth.d.ts +7 -0
  140. package/dist/hono/console/live-auth.d.ts.map +1 -0
  141. package/dist/hono/console/live-auth.js +39 -0
  142. package/dist/hono/console/live-auth.js.map +1 -0
  143. package/dist/hono/console/route-descriptor.d.ts +6 -0
  144. package/dist/hono/console/route-descriptor.d.ts.map +1 -0
  145. package/dist/hono/console/route-descriptor.js +16 -0
  146. package/dist/hono/console/route-descriptor.js.map +1 -0
  147. package/dist/hono/console/routes/api-keys.d.ts +8 -0
  148. package/dist/hono/console/routes/api-keys.d.ts.map +1 -0
  149. package/dist/hono/console/routes/api-keys.js +575 -0
  150. package/dist/hono/console/routes/api-keys.js.map +1 -0
  151. package/dist/hono/console/routes/clients.d.ts +8 -0
  152. package/dist/hono/console/routes/clients.d.ts.map +1 -0
  153. package/dist/hono/console/routes/clients.js +322 -0
  154. package/dist/hono/console/routes/clients.js.map +1 -0
  155. package/dist/hono/console/routes/commits.d.ts +8 -0
  156. package/dist/hono/console/routes/commits.d.ts.map +1 -0
  157. package/dist/hono/console/routes/commits.js +882 -0
  158. package/dist/hono/console/routes/commits.js.map +1 -0
  159. package/dist/hono/console/routes/context.d.ts +188 -0
  160. package/dist/hono/console/routes/context.d.ts.map +1 -0
  161. package/dist/hono/console/routes/context.js +633 -0
  162. package/dist/hono/console/routes/context.js.map +1 -0
  163. package/dist/hono/console/routes/events.d.ts +8 -0
  164. package/dist/hono/console/routes/events.d.ts.map +1 -0
  165. package/dist/hono/console/routes/events.js +501 -0
  166. package/dist/hono/console/routes/events.js.map +1 -0
  167. package/dist/hono/console/routes/maintenance.d.ts +8 -0
  168. package/dist/hono/console/routes/maintenance.d.ts.map +1 -0
  169. package/dist/hono/console/routes/maintenance.js +341 -0
  170. package/dist/hono/console/routes/maintenance.js.map +1 -0
  171. package/dist/hono/console/routes/shared.d.ts +174 -0
  172. package/dist/hono/console/routes/shared.d.ts.map +1 -0
  173. package/dist/hono/console/routes/shared.js +583 -0
  174. package/dist/hono/console/routes/shared.js.map +1 -0
  175. package/dist/hono/console/routes/stats.d.ts +8 -0
  176. package/dist/hono/console/routes/stats.d.ts.map +1 -0
  177. package/dist/hono/console/routes/stats.js +295 -0
  178. package/dist/hono/console/routes/stats.js.map +1 -0
  179. package/dist/hono/console/routes/storage.d.ts +8 -0
  180. package/dist/hono/console/routes/storage.d.ts.map +1 -0
  181. package/dist/hono/console/routes/storage.js +121 -0
  182. package/dist/hono/console/routes/storage.js.map +1 -0
  183. package/dist/hono/console/routes.d.ts +36 -0
  184. package/dist/hono/console/routes.d.ts.map +1 -0
  185. package/dist/hono/console/routes.js +112 -0
  186. package/dist/hono/console/routes.js.map +1 -0
  187. package/dist/hono/console/schema-errors.d.ts +2 -0
  188. package/dist/hono/console/schema-errors.d.ts.map +1 -0
  189. package/dist/hono/console/schema-errors.js +17 -0
  190. package/dist/hono/console/schema-errors.js.map +1 -0
  191. package/dist/hono/console/schemas.d.ts +1515 -0
  192. package/dist/hono/console/schemas.d.ts.map +1 -0
  193. package/dist/hono/console/schemas.js +661 -0
  194. package/dist/hono/console/schemas.js.map +1 -0
  195. package/dist/hono/console/types.d.ts +213 -0
  196. package/dist/hono/console/types.d.ts.map +1 -0
  197. package/dist/hono/console/types.js +2 -0
  198. package/dist/hono/console/types.js.map +1 -0
  199. package/dist/hono/console/ui.d.ts +38 -0
  200. package/dist/hono/console/ui.d.ts.map +1 -0
  201. package/dist/hono/console/ui.js +43 -0
  202. package/dist/hono/console/ui.js.map +1 -0
  203. package/dist/hono/create-server.d.ts +71 -0
  204. package/dist/hono/create-server.d.ts.map +1 -0
  205. package/dist/hono/create-server.js +121 -0
  206. package/dist/hono/create-server.js.map +1 -0
  207. package/dist/hono/errors.d.ts +13 -0
  208. package/dist/hono/errors.d.ts.map +1 -0
  209. package/dist/hono/errors.js +21 -0
  210. package/dist/hono/errors.js.map +1 -0
  211. package/dist/hono/index.d.ts +20 -0
  212. package/dist/hono/index.d.ts.map +1 -0
  213. package/dist/hono/index.js +31 -0
  214. package/dist/hono/index.js.map +1 -0
  215. package/dist/hono/openapi.d.ts +72 -0
  216. package/dist/hono/openapi.d.ts.map +1 -0
  217. package/dist/hono/openapi.js +99 -0
  218. package/dist/hono/openapi.js.map +1 -0
  219. package/dist/hono/proxy/connection-manager.d.ts +78 -0
  220. package/dist/hono/proxy/connection-manager.d.ts.map +1 -0
  221. package/dist/hono/proxy/connection-manager.js +251 -0
  222. package/dist/hono/proxy/connection-manager.js.map +1 -0
  223. package/dist/hono/proxy/index.d.ts +8 -0
  224. package/dist/hono/proxy/index.d.ts.map +1 -0
  225. package/dist/hono/proxy/index.js +8 -0
  226. package/dist/hono/proxy/index.js.map +1 -0
  227. package/dist/hono/proxy/routes.d.ts +86 -0
  228. package/dist/hono/proxy/routes.d.ts.map +1 -0
  229. package/dist/hono/proxy/routes.js +183 -0
  230. package/dist/hono/proxy/routes.js.map +1 -0
  231. package/dist/hono/rate-limit.d.ts +101 -0
  232. package/dist/hono/rate-limit.d.ts.map +1 -0
  233. package/dist/hono/rate-limit.js +184 -0
  234. package/dist/hono/rate-limit.js.map +1 -0
  235. package/dist/hono/realtime-sync-packs.d.ts +43 -0
  236. package/dist/hono/realtime-sync-packs.d.ts.map +1 -0
  237. package/dist/hono/realtime-sync-packs.js +219 -0
  238. package/dist/hono/realtime-sync-packs.js.map +1 -0
  239. package/dist/hono/routes/audit.d.ts +12 -0
  240. package/dist/hono/routes/audit.d.ts.map +1 -0
  241. package/dist/hono/routes/audit.js +385 -0
  242. package/dist/hono/routes/audit.js.map +1 -0
  243. package/dist/hono/routes/auth-leases.d.ts +8 -0
  244. package/dist/hono/routes/auth-leases.d.ts.map +1 -0
  245. package/dist/hono/routes/auth-leases.js +88 -0
  246. package/dist/hono/routes/auth-leases.js.map +1 -0
  247. package/dist/hono/routes/combined.d.ts +8 -0
  248. package/dist/hono/routes/combined.d.ts.map +1 -0
  249. package/dist/hono/routes/combined.js +392 -0
  250. package/dist/hono/routes/combined.js.map +1 -0
  251. package/dist/hono/routes/context.d.ts +314 -0
  252. package/dist/hono/routes/context.d.ts.map +1 -0
  253. package/dist/hono/routes/context.js +1147 -0
  254. package/dist/hono/routes/context.js.map +1 -0
  255. package/dist/hono/routes/health.d.ts +8 -0
  256. package/dist/hono/routes/health.d.ts.map +1 -0
  257. package/dist/hono/routes/health.js +16 -0
  258. package/dist/hono/routes/health.js.map +1 -0
  259. package/dist/hono/routes/realtime.d.ts +9 -0
  260. package/dist/hono/routes/realtime.d.ts.map +1 -0
  261. package/dist/hono/routes/realtime.js +626 -0
  262. package/dist/hono/routes/realtime.js.map +1 -0
  263. package/dist/hono/routes/shared.d.ts +689 -0
  264. package/dist/hono/routes/shared.d.ts.map +1 -0
  265. package/dist/hono/routes/shared.js +972 -0
  266. package/dist/hono/routes/shared.js.map +1 -0
  267. package/dist/hono/routes/snapshots.d.ts +10 -0
  268. package/dist/hono/routes/snapshots.d.ts.map +1 -0
  269. package/dist/hono/routes/snapshots.js +281 -0
  270. package/dist/hono/routes/snapshots.js.map +1 -0
  271. package/dist/hono/routes.d.ts +19 -0
  272. package/dist/hono/routes.d.ts.map +1 -0
  273. package/dist/hono/routes.js +37 -0
  274. package/dist/hono/routes.js.map +1 -0
  275. package/dist/hono/validation.d.ts +5 -0
  276. package/dist/hono/validation.d.ts.map +1 -0
  277. package/dist/hono/validation.js +47 -0
  278. package/dist/hono/validation.js.map +1 -0
  279. package/dist/hono/websocket-origin.d.ts +9 -0
  280. package/dist/hono/websocket-origin.d.ts.map +1 -0
  281. package/dist/hono/websocket-origin.js +96 -0
  282. package/dist/hono/websocket-origin.js.map +1 -0
  283. package/dist/hono/ws.d.ts +341 -0
  284. package/dist/hono/ws.d.ts.map +1 -0
  285. package/dist/hono/ws.js +711 -0
  286. package/dist/hono/ws.js.map +1 -0
  287. package/dist/index.d.ts +6 -0
  288. package/dist/index.d.ts.map +1 -1
  289. package/dist/index.js +6 -0
  290. package/dist/index.js.map +1 -1
  291. package/dist/libsql.d.ts +29 -0
  292. package/dist/libsql.d.ts.map +1 -0
  293. package/dist/libsql.js +25 -0
  294. package/dist/libsql.js.map +1 -0
  295. package/dist/migrate.js.map +1 -1
  296. package/dist/neon.d.ts +21 -0
  297. package/dist/neon.d.ts.map +1 -0
  298. package/dist/neon.js +22 -0
  299. package/dist/neon.js.map +1 -0
  300. package/dist/notify.d.ts +30 -0
  301. package/dist/notify.d.ts.map +1 -1
  302. package/dist/notify.js +160 -41
  303. package/dist/notify.js.map +1 -1
  304. package/dist/pglite.d.ts +37 -0
  305. package/dist/pglite.d.ts.map +1 -0
  306. package/dist/pglite.js +37 -0
  307. package/dist/pglite.js.map +1 -0
  308. package/dist/plugins/index.d.ts +2 -0
  309. package/dist/plugins/index.d.ts.map +1 -0
  310. package/dist/plugins/index.js +2 -0
  311. package/dist/plugins/index.js.map +1 -0
  312. package/dist/plugins/types.d.ts +73 -0
  313. package/dist/plugins/types.d.ts.map +1 -0
  314. package/dist/plugins/types.js +30 -0
  315. package/dist/plugins/types.js.map +1 -0
  316. package/dist/postgres/index.d.ts +68 -0
  317. package/dist/postgres/index.d.ts.map +1 -0
  318. package/dist/postgres/index.js +924 -0
  319. package/dist/postgres/index.js.map +1 -0
  320. package/dist/proxy/collection.d.ts +0 -2
  321. package/dist/proxy/collection.d.ts.map +1 -1
  322. package/dist/proxy/collection.js +2 -17
  323. package/dist/proxy/collection.js.map +1 -1
  324. package/dist/proxy/handler.d.ts +1 -1
  325. package/dist/proxy/handler.d.ts.map +1 -1
  326. package/dist/proxy/handler.js +1 -2
  327. package/dist/proxy/handler.js.map +1 -1
  328. package/dist/proxy/index.d.ts +1 -1
  329. package/dist/proxy/index.d.ts.map +1 -1
  330. package/dist/proxy/index.js +1 -1
  331. package/dist/proxy/index.js.map +1 -1
  332. package/dist/proxy/mutation-detector.js.map +1 -1
  333. package/dist/proxy/oplog.d.ts.map +1 -1
  334. package/dist/proxy/oplog.js +32 -8
  335. package/dist/proxy/oplog.js.map +1 -1
  336. package/dist/prune.d.ts +8 -1
  337. package/dist/prune.d.ts.map +1 -1
  338. package/dist/prune.js +68 -28
  339. package/dist/prune.js.map +1 -1
  340. package/dist/pull.d.ts +31 -0
  341. package/dist/pull.d.ts.map +1 -1
  342. package/dist/pull.js +1011 -471
  343. package/dist/pull.js.map +1 -1
  344. package/dist/push.d.ts +36 -2
  345. package/dist/push.d.ts.map +1 -1
  346. package/dist/push.js +603 -358
  347. package/dist/push.js.map +1 -1
  348. package/dist/realtime/in-memory.js.map +1 -1
  349. package/dist/realtime/index.d.ts +2 -1
  350. package/dist/realtime/index.d.ts.map +1 -1
  351. package/dist/realtime/index.js +1 -0
  352. package/dist/realtime/index.js.map +1 -1
  353. package/dist/realtime/types.d.ts +12 -0
  354. package/dist/realtime/types.d.ts.map +1 -1
  355. package/dist/realtime/types.js +13 -1
  356. package/dist/realtime/types.js.map +1 -1
  357. package/dist/relay/client-role/forward-engine.d.ts +63 -0
  358. package/dist/relay/client-role/forward-engine.d.ts.map +1 -0
  359. package/dist/relay/client-role/forward-engine.js +267 -0
  360. package/dist/relay/client-role/forward-engine.js.map +1 -0
  361. package/dist/relay/client-role/index.d.ts +9 -0
  362. package/dist/relay/client-role/index.d.ts.map +1 -0
  363. package/dist/relay/client-role/index.js +9 -0
  364. package/dist/relay/client-role/index.js.map +1 -0
  365. package/dist/relay/client-role/pull-engine.d.ts +72 -0
  366. package/dist/relay/client-role/pull-engine.d.ts.map +1 -0
  367. package/dist/relay/client-role/pull-engine.js +249 -0
  368. package/dist/relay/client-role/pull-engine.js.map +1 -0
  369. package/dist/relay/client-role/sequence-mapper.d.ts +65 -0
  370. package/dist/relay/client-role/sequence-mapper.d.ts.map +1 -0
  371. package/dist/relay/client-role/sequence-mapper.js +161 -0
  372. package/dist/relay/client-role/sequence-mapper.js.map +1 -0
  373. package/dist/relay/evaluation/relay-paths.d.ts +41 -0
  374. package/dist/relay/evaluation/relay-paths.d.ts.map +1 -0
  375. package/dist/relay/evaluation/relay-paths.js +504 -0
  376. package/dist/relay/evaluation/relay-paths.js.map +1 -0
  377. package/dist/relay/evaluation/rust-boundary.d.ts +47 -0
  378. package/dist/relay/evaluation/rust-boundary.d.ts.map +1 -0
  379. package/dist/relay/evaluation/rust-boundary.js +220 -0
  380. package/dist/relay/evaluation/rust-boundary.js.map +1 -0
  381. package/dist/relay/index.d.ts +37 -0
  382. package/dist/relay/index.d.ts.map +1 -0
  383. package/dist/relay/index.js +44 -0
  384. package/dist/relay/index.js.map +1 -0
  385. package/dist/relay/migrate.d.ts +18 -0
  386. package/dist/relay/migrate.d.ts.map +1 -0
  387. package/dist/relay/migrate.js +99 -0
  388. package/dist/relay/migrate.js.map +1 -0
  389. package/dist/relay/mode-manager.d.ts +60 -0
  390. package/dist/relay/mode-manager.d.ts.map +1 -0
  391. package/dist/relay/mode-manager.js +114 -0
  392. package/dist/relay/mode-manager.js.map +1 -0
  393. package/dist/relay/realtime.d.ts +90 -0
  394. package/dist/relay/realtime.d.ts.map +1 -0
  395. package/dist/relay/realtime.js +147 -0
  396. package/dist/relay/realtime.js.map +1 -0
  397. package/dist/relay/relay.d.ts +190 -0
  398. package/dist/relay/relay.d.ts.map +1 -0
  399. package/dist/relay/relay.js +320 -0
  400. package/dist/relay/relay.js.map +1 -0
  401. package/dist/relay/schema.d.ts +158 -0
  402. package/dist/relay/schema.d.ts.map +1 -0
  403. package/dist/relay/schema.js +7 -0
  404. package/dist/relay/schema.js.map +1 -0
  405. package/dist/relay/server-role/index.d.ts +56 -0
  406. package/dist/relay/server-role/index.d.ts.map +1 -0
  407. package/dist/relay/server-role/index.js +193 -0
  408. package/dist/relay/server-role/index.js.map +1 -0
  409. package/dist/relay/server-role/pull.d.ts +27 -0
  410. package/dist/relay/server-role/pull.d.ts.map +1 -0
  411. package/dist/relay/server-role/pull.js +24 -0
  412. package/dist/relay/server-role/pull.js.map +1 -0
  413. package/dist/relay/server-role/push.d.ts +29 -0
  414. package/dist/relay/server-role/push.d.ts.map +1 -0
  415. package/dist/relay/server-role/push.js +122 -0
  416. package/dist/relay/server-role/push.js.map +1 -0
  417. package/dist/s3/index.d.ts +83 -0
  418. package/dist/s3/index.d.ts.map +1 -0
  419. package/dist/s3/index.js +223 -0
  420. package/dist/s3/index.js.map +1 -0
  421. package/dist/schema.d.ts +137 -1
  422. package/dist/schema.d.ts.map +1 -1
  423. package/dist/schema.js +1 -0
  424. package/dist/schema.js.map +1 -1
  425. package/dist/service-worker/index.d.ts +114 -0
  426. package/dist/service-worker/index.d.ts.map +1 -0
  427. package/dist/service-worker/index.js +487 -0
  428. package/dist/service-worker/index.js.map +1 -0
  429. package/dist/snapshot-artifacts/sqlite-bun.d.ts +15 -0
  430. package/dist/snapshot-artifacts/sqlite-bun.d.ts.map +1 -0
  431. package/dist/snapshot-artifacts/sqlite-bun.js +128 -0
  432. package/dist/snapshot-artifacts/sqlite-bun.js.map +1 -0
  433. package/dist/snapshot-artifacts.d.ts +206 -0
  434. package/dist/snapshot-artifacts.d.ts.map +1 -0
  435. package/dist/snapshot-artifacts.js +545 -0
  436. package/dist/snapshot-artifacts.js.map +1 -0
  437. package/dist/snapshot-chunks/db-metadata.d.ts +18 -0
  438. package/dist/snapshot-chunks/db-metadata.d.ts.map +1 -1
  439. package/dist/snapshot-chunks/db-metadata.js +91 -63
  440. package/dist/snapshot-chunks/db-metadata.js.map +1 -1
  441. package/dist/snapshot-chunks/types.d.ts +2 -0
  442. package/dist/snapshot-chunks/types.d.ts.map +1 -1
  443. package/dist/snapshot-chunks.d.ts +33 -3
  444. package/dist/snapshot-chunks.d.ts.map +1 -1
  445. package/dist/snapshot-chunks.js +136 -55
  446. package/dist/snapshot-chunks.js.map +1 -1
  447. package/dist/sqlite/index.d.ts +53 -0
  448. package/dist/sqlite/index.d.ts.map +1 -0
  449. package/dist/sqlite/index.js +795 -0
  450. package/dist/sqlite/index.js.map +1 -0
  451. package/dist/sqlite3.d.ts +22 -0
  452. package/dist/sqlite3.d.ts.map +1 -0
  453. package/dist/sqlite3.js +99 -0
  454. package/dist/sqlite3.js.map +1 -0
  455. package/dist/stats.d.ts +8 -0
  456. package/dist/stats.d.ts.map +1 -1
  457. package/dist/stats.js +45 -13
  458. package/dist/stats.js.map +1 -1
  459. package/dist/subscriptions/cache.d.ts +3 -0
  460. package/dist/subscriptions/cache.d.ts.map +1 -1
  461. package/dist/subscriptions/cache.js +44 -0
  462. package/dist/subscriptions/cache.js.map +1 -1
  463. package/dist/subscriptions/resolve.d.ts +3 -1
  464. package/dist/subscriptions/resolve.d.ts.map +1 -1
  465. package/dist/subscriptions/resolve.js +90 -51
  466. package/dist/subscriptions/resolve.js.map +1 -1
  467. package/dist/sync.d.ts +3 -0
  468. package/dist/sync.d.ts.map +1 -1
  469. package/dist/sync.js +8 -4
  470. package/dist/sync.js.map +1 -1
  471. package/package.json +296 -7
  472. package/src/auth-leases.ts +649 -0
  473. package/src/better-sqlite3.ts +35 -0
  474. package/src/blobs/access.ts +244 -0
  475. package/src/blobs/adapters/database.ts +125 -43
  476. package/src/blobs/index.ts +1 -0
  477. package/src/blobs/manager.ts +366 -65
  478. package/src/blobs/migrate.ts +16 -8
  479. package/src/blobs/types.ts +4 -0
  480. package/src/bun-sqlite-ambient.d.ts +19 -0
  481. package/src/bun-sqlite.ts +27 -0
  482. package/src/clients.ts +1 -0
  483. package/src/cloudflare/durable-object.ts +289 -0
  484. package/src/cloudflare/index.ts +22 -0
  485. package/src/cloudflare/r2.ts +526 -0
  486. package/src/cloudflare/scope-cache.ts +341 -0
  487. package/src/cloudflare/sentry.ts +230 -0
  488. package/src/cloudflare/worker.ts +77 -0
  489. package/src/commit-integrity.ts +371 -0
  490. package/src/crdt-yjs/index.ts +931 -0
  491. package/src/d1.ts +16 -0
  492. package/src/dialect/base.ts +71 -5
  493. package/src/dialect/helpers.ts +31 -0
  494. package/src/dialect/types.ts +47 -0
  495. package/src/encrypted-crdt.ts +786 -0
  496. package/src/errors.ts +22 -0
  497. package/src/filesystem/index.ts +262 -0
  498. package/src/handlers/collection.ts +78 -78
  499. package/src/handlers/create-handler.ts +540 -147
  500. package/src/handlers/types.ts +116 -0
  501. package/src/helpers/index.ts +2 -0
  502. package/src/helpers/scope-authorization.ts +27 -0
  503. package/src/helpers/scope-commit-index.ts +52 -0
  504. package/src/hono/api-key-auth.ts +177 -0
  505. package/src/hono/audit-redaction.ts +135 -0
  506. package/src/hono/blobs.ts +851 -0
  507. package/src/hono/console/gateway.ts +3046 -0
  508. package/src/hono/console/live-auth.ts +46 -0
  509. package/src/hono/console/route-descriptor.ts +22 -0
  510. package/src/hono/console/routes/api-keys.ts +721 -0
  511. package/src/hono/console/routes/clients.ts +447 -0
  512. package/src/hono/console/routes/commits.ts +1137 -0
  513. package/src/hono/console/routes/context.ts +956 -0
  514. package/src/hono/console/routes/events.ts +669 -0
  515. package/src/hono/console/routes/maintenance.ts +461 -0
  516. package/src/hono/console/routes/shared.ts +816 -0
  517. package/src/hono/console/routes/stats.ts +392 -0
  518. package/src/hono/console/routes/storage.ts +159 -0
  519. package/src/hono/console/routes.ts +146 -0
  520. package/src/hono/console/schema-errors.ts +23 -0
  521. package/src/hono/console/schemas.ts +914 -0
  522. package/src/hono/console/types.ts +223 -0
  523. package/src/hono/console/ui.ts +100 -0
  524. package/src/hono/create-server.ts +230 -0
  525. package/src/hono/errors.ts +50 -0
  526. package/src/hono/index.ts +54 -0
  527. package/src/hono/openapi.ts +139 -0
  528. package/src/hono/proxy/connection-manager.ts +340 -0
  529. package/src/hono/proxy/index.ts +8 -0
  530. package/src/hono/proxy/routes.ts +272 -0
  531. package/src/hono/rate-limit.ts +319 -0
  532. package/src/hono/realtime-sync-packs.ts +354 -0
  533. package/src/hono/routes/audit.ts +499 -0
  534. package/src/hono/routes/auth-leases.ts +119 -0
  535. package/src/hono/routes/combined.ts +583 -0
  536. package/src/hono/routes/context.ts +1629 -0
  537. package/src/hono/routes/health.ts +26 -0
  538. package/src/hono/routes/realtime.ts +808 -0
  539. package/src/hono/routes/shared.ts +1626 -0
  540. package/src/hono/routes/snapshots.ts +345 -0
  541. package/src/hono/routes.ts +68 -0
  542. package/src/hono/validation.ts +81 -0
  543. package/src/hono/websocket-origin.ts +131 -0
  544. package/src/hono/ws.ts +1134 -0
  545. package/src/index.ts +6 -0
  546. package/src/libsql.ts +51 -0
  547. package/src/neon.ts +28 -0
  548. package/src/notify.ts +224 -44
  549. package/src/pglite.ts +68 -0
  550. package/src/plugins/index.ts +1 -0
  551. package/src/plugins/types.ts +144 -0
  552. package/src/postgres/index.ts +1291 -0
  553. package/src/proxy/collection.ts +5 -27
  554. package/src/proxy/handler.ts +2 -2
  555. package/src/proxy/index.ts +0 -2
  556. package/src/proxy/oplog.ts +41 -10
  557. package/src/prune.ts +137 -36
  558. package/src/pull.ts +1504 -591
  559. package/src/push.ts +881 -443
  560. package/src/realtime/index.ts +2 -0
  561. package/src/realtime/types.ts +35 -0
  562. package/src/relay/bun-types.d.ts +50 -0
  563. package/src/relay/client-role/forward-engine.ts +355 -0
  564. package/src/relay/client-role/index.ts +9 -0
  565. package/src/relay/client-role/pull-engine.ts +329 -0
  566. package/src/relay/client-role/sequence-mapper.ts +201 -0
  567. package/src/relay/evaluation/relay-paths.ts +699 -0
  568. package/src/relay/evaluation/rust-boundary.ts +464 -0
  569. package/src/relay/index.ts +50 -0
  570. package/src/relay/migrate.ts +113 -0
  571. package/src/relay/mode-manager.ts +142 -0
  572. package/src/relay/realtime.ts +207 -0
  573. package/src/relay/relay.ts +431 -0
  574. package/src/relay/schema.ts +171 -0
  575. package/src/relay/server-role/index.ts +338 -0
  576. package/src/relay/server-role/pull.ts +43 -0
  577. package/src/relay/server-role/push.ts +164 -0
  578. package/src/s3/index.ts +346 -0
  579. package/src/schema.ts +141 -1
  580. package/src/service-worker/index.ts +773 -0
  581. package/src/snapshot-artifacts/sqlite-bun.ts +168 -0
  582. package/src/snapshot-artifacts.ts +896 -0
  583. package/src/snapshot-chunks/db-metadata.ts +131 -72
  584. package/src/snapshot-chunks/types.ts +2 -0
  585. package/src/snapshot-chunks.ts +194 -70
  586. package/src/sqlite/index.ts +1064 -0
  587. package/src/sqlite3.ts +137 -0
  588. package/src/stats.ts +76 -13
  589. package/src/subscriptions/cache.ts +58 -0
  590. package/src/subscriptions/resolve.ts +99 -58
  591. package/src/sync.ts +16 -6
  592. package/src/blobs/adapters/database.test.ts +0 -67
  593. package/src/notify.test.ts +0 -520
  594. package/src/proxy/handler.test.ts +0 -126
  595. package/src/proxy/mutation-detector.test.ts +0 -71
  596. package/src/snapshot-chunks/db-metadata.test.ts +0 -172
  597. package/src/subscriptions/resolve.test.ts +0 -180
@@ -0,0 +1,272 @@
1
+ /**
2
+ * @syncular/server/hono - Proxy Routes
3
+ *
4
+ * WebSocket endpoint for database proxy.
5
+ */
6
+
7
+ import type {
8
+ ProxyHandshake,
9
+ ProxyHandshakeAck,
10
+ ProxyMessage,
11
+ ProxyResponse,
12
+ } from '@syncular/core';
13
+ import { createSyncularErrorResponse, logSyncEvent } from '@syncular/core';
14
+ import type {
15
+ ProxyHandlerCollection,
16
+ ServerSyncDialect,
17
+ SyncCoreDb,
18
+ } from '@syncular/server';
19
+ import type { Context } from 'hono';
20
+ import { Hono } from 'hono';
21
+ import type { UpgradeWebSocket, WSContext } from 'hono/ws';
22
+ import type { Kysely } from 'kysely';
23
+ import { isWebSocketOriginAllowed } from '../websocket-origin';
24
+ import { ProxyConnectionManager } from './connection-manager';
25
+
26
+ /**
27
+ * WeakMap for storing proxy connection manager per Hono instance.
28
+ */
29
+ interface ProxyConnectionManagerHandle {
30
+ canAccept(): boolean;
31
+ getConnectionCount(): number;
32
+ register(ws: WSContext, handshake: ProxyHandshake): unknown;
33
+ handleMessage(ws: WSContext, message: ProxyMessage): Promise<ProxyResponse>;
34
+ unregister(ws: WSContext): Promise<void>;
35
+ }
36
+
37
+ const proxyConnectionManagerMap = new WeakMap<
38
+ Hono,
39
+ ProxyConnectionManagerHandle
40
+ >();
41
+
42
+ interface ProxyAuthResult {
43
+ /** Actor ID for oplog tracking */
44
+ actorId: string;
45
+ }
46
+
47
+ interface CreateProxyRoutesConfig<DB extends SyncCoreDb = SyncCoreDb> {
48
+ /** Database connection */
49
+ db: Kysely<DB>;
50
+ /** Server sync dialect */
51
+ dialect: ServerSyncDialect;
52
+ /** Proxy table handlers for oplog generation */
53
+ handlers: ProxyHandlerCollection;
54
+ /** Authenticate the request and return actor info */
55
+ authenticate: (c: Context) => Promise<ProxyAuthResult | null>;
56
+ /** WebSocket upgrade function from Hono */
57
+ upgradeWebSocket: UpgradeWebSocket;
58
+ /** Maximum concurrent connections (default: 100) */
59
+ maxConnections?: number;
60
+ /** Idle connection timeout in ms (default: 30000) */
61
+ idleTimeoutMs?: number;
62
+ /**
63
+ * Maximum inbound websocket message size in bytes.
64
+ * Default: 1 MiB.
65
+ */
66
+ maxMessageBytes?: number;
67
+ /**
68
+ * Optional list of allowed websocket origins.
69
+ * - undefined: allow same-origin browser upgrades and origin-less non-browser clients
70
+ * - '*': allow all origins
71
+ * - string[]: exact origin match (scheme + host + port)
72
+ */
73
+ allowedOrigins?: string[] | '*';
74
+ }
75
+
76
+ /**
77
+ * Create Hono routes for the proxy WebSocket endpoint.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * import { Hono } from 'hono';
82
+ * import { createBunWebSocket } from 'hono/bun';
83
+ * import { createProxyRoutes } from '@syncular/server/hono/proxy';
84
+ *
85
+ * const { upgradeWebSocket, websocket } = createBunWebSocket();
86
+ *
87
+ * const app = new Hono();
88
+ *
89
+ * app.route('/proxy', createProxyRoutes({
90
+ * db,
91
+ * handlers: proxyHandlers,
92
+ * authenticate: async (c) => {
93
+ * // Verify admin auth
94
+ * return { actorId: 'admin:123' };
95
+ * },
96
+ * upgradeWebSocket,
97
+ * }));
98
+ *
99
+ * export default { fetch: app.fetch, websocket };
100
+ * ```
101
+ */
102
+ export function createProxyRoutes<DB extends SyncCoreDb>(
103
+ config: CreateProxyRoutesConfig<DB>
104
+ ): Hono {
105
+ const app = new Hono();
106
+ const maxMessageBytes = config.maxMessageBytes ?? 1024 * 1024;
107
+
108
+ const manager = new ProxyConnectionManager({
109
+ db: config.db,
110
+ dialect: config.dialect,
111
+ handlers: config.handlers,
112
+ maxConnections: config.maxConnections,
113
+ idleTimeoutMs: config.idleTimeoutMs,
114
+ });
115
+
116
+ // Store manager for external access if needed
117
+ proxyConnectionManagerMap.set(app, manager);
118
+
119
+ // WebSocket upgrade endpoint - using regular route since WebSocket doesn't fit OpenAPI well
120
+ app.get('/', async (c) => {
121
+ if (!isWebSocketOriginAllowed(c, config.allowedOrigins)) {
122
+ return c.json(createSyncularErrorResponse('proxy.forbidden_origin'), 403);
123
+ }
124
+
125
+ // Authenticate before upgrade
126
+ const auth = await config.authenticate(c);
127
+ if (!auth) {
128
+ return c.json(createSyncularErrorResponse('proxy.auth_required'), 401);
129
+ }
130
+
131
+ // Check connection limit
132
+ if (!manager.canAccept()) {
133
+ logSyncEvent({
134
+ event: 'proxy.rejected',
135
+ userId: auth.actorId,
136
+ reason: 'max_connections',
137
+ });
138
+ return c.json(createSyncularErrorResponse('proxy.connection_limit'), 429);
139
+ }
140
+
141
+ logSyncEvent({
142
+ event: 'proxy.connect',
143
+ userId: auth.actorId,
144
+ });
145
+
146
+ return config.upgradeWebSocket(c, {
147
+ onOpen(_evt, _ws) {
148
+ // Connection opened, wait for handshake message
149
+ },
150
+
151
+ async onMessage(evt, ws) {
152
+ try {
153
+ const messageBytes = measureWebSocketMessageBytes(evt.data);
154
+ if (messageBytes > maxMessageBytes) {
155
+ ws.send(
156
+ JSON.stringify({
157
+ type: 'error',
158
+ error: `Message exceeds max size (${maxMessageBytes} bytes)`,
159
+ })
160
+ );
161
+ ws.close(1009, 'Message too large');
162
+ return;
163
+ }
164
+
165
+ const data = decodeWebSocketData(evt.data);
166
+
167
+ const message = JSON.parse(data);
168
+
169
+ // Handle handshake
170
+ if (message.type === 'handshake') {
171
+ const handshake = message as ProxyHandshake;
172
+
173
+ // Validate that the handshake actor matches authenticated actor
174
+ if (handshake.actorId !== auth.actorId) {
175
+ const ack: ProxyHandshakeAck = {
176
+ type: 'handshake_ack',
177
+ ok: false,
178
+ error: 'Actor ID mismatch',
179
+ };
180
+ ws.send(JSON.stringify(ack));
181
+ ws.close(4001, 'Unauthorized');
182
+ return;
183
+ }
184
+
185
+ manager.register(ws, handshake);
186
+
187
+ const ack: ProxyHandshakeAck = {
188
+ type: 'handshake_ack',
189
+ ok: true,
190
+ };
191
+ ws.send(JSON.stringify(ack));
192
+ return;
193
+ }
194
+
195
+ // Handle proxy messages
196
+ const proxyMessage = message as ProxyMessage;
197
+ const response = await manager.handleMessage(ws, proxyMessage);
198
+ ws.send(JSON.stringify(response));
199
+ } catch (err) {
200
+ // Send error response if we can parse the message ID
201
+ try {
202
+ const parsed = JSON.parse(decodeWebSocketData(evt.data));
203
+ if (parsed.id) {
204
+ ws.send(
205
+ JSON.stringify({
206
+ id: parsed.id,
207
+ type: 'error',
208
+ error: err instanceof Error ? err.message : 'Unknown error',
209
+ })
210
+ );
211
+ }
212
+ } catch {
213
+ // Ignore parse errors
214
+ }
215
+ }
216
+ },
217
+
218
+ async onClose(_evt, ws) {
219
+ await manager.unregister(ws);
220
+ logSyncEvent({
221
+ event: 'proxy.disconnect',
222
+ userId: auth.actorId,
223
+ });
224
+ },
225
+
226
+ async onError(_evt, ws) {
227
+ await manager.unregister(ws);
228
+ logSyncEvent({
229
+ event: 'proxy.error',
230
+ userId: auth.actorId,
231
+ });
232
+ },
233
+ });
234
+ });
235
+
236
+ return app;
237
+ }
238
+
239
+ function measureWebSocketMessageBytes(data: unknown): number {
240
+ if (typeof data === 'string') {
241
+ return new TextEncoder().encode(data).byteLength;
242
+ }
243
+ if (data instanceof ArrayBuffer) {
244
+ return data.byteLength;
245
+ }
246
+ if (ArrayBuffer.isView(data)) {
247
+ return data.byteLength;
248
+ }
249
+ if (typeof Blob !== 'undefined' && data instanceof Blob) {
250
+ return data.size;
251
+ }
252
+ return new TextEncoder().encode(String(data)).byteLength;
253
+ }
254
+
255
+ function decodeWebSocketData(data: unknown): string {
256
+ if (typeof data === 'string') return data;
257
+ if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
258
+ if (ArrayBuffer.isView(data)) {
259
+ const view = data as Uint8Array;
260
+ return new TextDecoder().decode(view);
261
+ }
262
+ return String(data);
263
+ }
264
+
265
+ /**
266
+ * Get the ProxyConnectionManager from a proxy routes instance.
267
+ */
268
+ export function getProxyConnectionManager(
269
+ routes: Hono
270
+ ): ProxyConnectionManagerHandle | undefined {
271
+ return proxyConnectionManagerMap.get(routes);
272
+ }
@@ -0,0 +1,319 @@
1
+ /**
2
+ * @syncular/server/hono - Rate limiting middleware for sync endpoints
3
+ *
4
+ * Provides per-user rate limiting to prevent DoS attacks and excessive
5
+ * server load from misbehaving clients.
6
+ */
7
+
8
+ import { createSyncularErrorResponse, logSyncEvent } from '@syncular/core';
9
+ import type { Context, MiddlewareHandler } from 'hono';
10
+
11
+ /**
12
+ * Rate limit configuration
13
+ */
14
+ export interface RateLimitConfig {
15
+ /**
16
+ * Maximum requests per window (default: 60)
17
+ */
18
+ maxRequests: number;
19
+
20
+ /**
21
+ * Time window in milliseconds (default: 60000 = 1 minute)
22
+ */
23
+ windowMs: number;
24
+
25
+ /**
26
+ * Function to extract the rate limit key from a request.
27
+ * Typically returns userId, deviceId, or IP address.
28
+ * Return null to skip rate limiting for this request.
29
+ */
30
+ keyGenerator: (c: Context) => string | null | Promise<string | null>;
31
+
32
+ /**
33
+ * Whether to include rate limit headers in responses (default: true)
34
+ */
35
+ includeHeaders?: boolean;
36
+
37
+ /**
38
+ * Custom handler for rate-limited requests (optional)
39
+ * If not provided, returns a 429 JSON response
40
+ */
41
+ onRateLimited?: (
42
+ c: Context,
43
+ retryAfterMs: number
44
+ ) => Response | Promise<Response>;
45
+
46
+ /**
47
+ * Whether to skip rate limiting in test environments (default: false)
48
+ */
49
+ skipInTest?: boolean;
50
+ }
51
+
52
+ /**
53
+ * Default rate limit configuration
54
+ */
55
+ const DEFAULT_RATE_LIMIT_CONFIG: Omit<RateLimitConfig, 'keyGenerator'> = {
56
+ maxRequests: 60,
57
+ windowMs: 60_000,
58
+ includeHeaders: true,
59
+ skipInTest: false,
60
+ };
61
+
62
+ /**
63
+ * Rate limit entry for tracking request counts
64
+ */
65
+ interface RateLimitEntry {
66
+ /** Request count in current window */
67
+ count: number;
68
+ /** Window start timestamp */
69
+ windowStart: number;
70
+ }
71
+
72
+ /**
73
+ * In-memory rate limiter store
74
+ *
75
+ * Note: This is suitable for single-instance deployments.
76
+ * For distributed deployments, use Redis or similar.
77
+ */
78
+ class RateLimitStore {
79
+ private entries = new Map<string, RateLimitEntry>();
80
+ private cleanupInterval: ReturnType<typeof setInterval> | null = null;
81
+
82
+ constructor(private windowMs: number) {
83
+ // Clean up expired entries periodically
84
+ this.cleanupInterval = setInterval(
85
+ () => this.cleanup(),
86
+ Math.max(windowMs, 60_000)
87
+ );
88
+ }
89
+
90
+ /**
91
+ * Check and increment the rate limit counter for a key.
92
+ *
93
+ * @param key - Rate limit key (e.g., userId)
94
+ * @param maxRequests - Maximum requests allowed
95
+ * @returns Rate limit check result
96
+ */
97
+ check(
98
+ key: string,
99
+ maxRequests: number
100
+ ): {
101
+ allowed: boolean;
102
+ current: number;
103
+ remaining: number;
104
+ resetAt: number;
105
+ } {
106
+ const now = Date.now();
107
+ let entry = this.entries.get(key);
108
+
109
+ // Check if window has expired
110
+ if (!entry || now - entry.windowStart >= this.windowMs) {
111
+ entry = { count: 0, windowStart: now };
112
+ this.entries.set(key, entry);
113
+ }
114
+
115
+ const resetAt = entry.windowStart + this.windowMs;
116
+ const allowed = entry.count < maxRequests;
117
+
118
+ if (allowed) {
119
+ entry.count++;
120
+ }
121
+
122
+ return {
123
+ allowed,
124
+ current: entry.count,
125
+ remaining: Math.max(0, maxRequests - entry.count),
126
+ resetAt,
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Clean up expired entries
132
+ */
133
+ cleanup(): void {
134
+ const now = Date.now();
135
+ for (const [key, entry] of this.entries) {
136
+ if (now - entry.windowStart >= this.windowMs) {
137
+ this.entries.delete(key);
138
+ }
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Stop the cleanup interval
144
+ */
145
+ stop(): void {
146
+ if (this.cleanupInterval) {
147
+ clearInterval(this.cleanupInterval);
148
+ this.cleanupInterval = null;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Clear all entries (for testing)
154
+ */
155
+ clear(): void {
156
+ this.entries.clear();
157
+ }
158
+
159
+ /**
160
+ * Get current entry count (for monitoring)
161
+ */
162
+ get size(): number {
163
+ return this.entries.size;
164
+ }
165
+ }
166
+
167
+ // Track created stores so tests can reset state deterministically.
168
+ const activeStores = new Set<RateLimitStore>();
169
+
170
+ /**
171
+ * Reset the global store (for testing)
172
+ */
173
+ export function resetRateLimitStore(): void {
174
+ for (const store of activeStores) {
175
+ store.stop();
176
+ }
177
+ activeStores.clear();
178
+ }
179
+
180
+ /**
181
+ * Create a rate limiting middleware for Hono.
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * import { createRateLimiter } from '@syncular/server/hono';
186
+ *
187
+ * const rateLimiter = createRateLimiter({
188
+ * maxRequests: 60,
189
+ * windowMs: 60_000,
190
+ * keyGenerator: async (c) => {
191
+ * const auth = await authenticate(c);
192
+ * return auth?.userId ?? null;
193
+ * },
194
+ * });
195
+ *
196
+ * app.use('/sync/*', rateLimiter);
197
+ * ```
198
+ */
199
+ export function createRateLimiter(
200
+ config: Partial<RateLimitConfig> & Pick<RateLimitConfig, 'keyGenerator'>
201
+ ): MiddlewareHandler {
202
+ const {
203
+ maxRequests = DEFAULT_RATE_LIMIT_CONFIG.maxRequests,
204
+ windowMs = DEFAULT_RATE_LIMIT_CONFIG.windowMs,
205
+ keyGenerator,
206
+ includeHeaders = DEFAULT_RATE_LIMIT_CONFIG.includeHeaders,
207
+ onRateLimited,
208
+ skipInTest = DEFAULT_RATE_LIMIT_CONFIG.skipInTest,
209
+ } = config;
210
+
211
+ const store = new RateLimitStore(windowMs);
212
+ activeStores.add(store);
213
+
214
+ return async (c, next) => {
215
+ // Skip in test environment if configured
216
+ if (skipInTest && process.env.NODE_ENV === 'test') {
217
+ return next();
218
+ }
219
+
220
+ // Get the rate limit key
221
+ const key = await keyGenerator(c);
222
+ if (key === null) {
223
+ // Skip rate limiting for this request
224
+ return next();
225
+ }
226
+
227
+ const result = store.check(key, maxRequests);
228
+
229
+ // Add rate limit headers if configured
230
+ if (includeHeaders) {
231
+ c.header('X-RateLimit-Limit', String(maxRequests));
232
+ c.header('X-RateLimit-Remaining', String(result.remaining));
233
+ c.header('X-RateLimit-Reset', String(Math.ceil(result.resetAt / 1000)));
234
+ }
235
+
236
+ if (!result.allowed) {
237
+ const retryAfterMs = result.resetAt - Date.now();
238
+ const retryAfterSec = Math.ceil(retryAfterMs / 1000);
239
+
240
+ // Log rate limit event
241
+ logSyncEvent({
242
+ event: 'sync.rate_limit',
243
+ key,
244
+ current: result.current,
245
+ maxRequests,
246
+ retryAfterMs,
247
+ });
248
+
249
+ // Add Retry-After header
250
+ c.header('Retry-After', String(retryAfterSec));
251
+
252
+ // Use custom handler or default response
253
+ if (onRateLimited) {
254
+ return onRateLimited(c, retryAfterMs);
255
+ }
256
+
257
+ return c.json(
258
+ createSyncularErrorResponse('sync.rate_limited', {
259
+ message: 'Too many requests. Please try again later.',
260
+ details: { retryAfterMs, retryAfterSec },
261
+ }),
262
+ 429
263
+ );
264
+ }
265
+
266
+ return next();
267
+ };
268
+ }
269
+
270
+ /**
271
+ * Create a rate limiter that uses userId from auth context.
272
+ *
273
+ * This is a convenience function for the common case of rate limiting
274
+ * by authenticated user.
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * const syncRoutes = createSyncRoutes({
279
+ * db,
280
+ * handlers: [tasksHandler],
281
+ * authenticate,
282
+ * sync: {
283
+ * rateLimit: {
284
+ * pull: { maxRequests: 120, windowMs: 60_000 },
285
+ * push: { maxRequests: 60, windowMs: 60_000 },
286
+ * },
287
+ * },
288
+ * });
289
+ * ```
290
+ */
291
+ export interface SyncRateLimitConfig {
292
+ /**
293
+ * Rate limit config for pull requests.
294
+ * Set to false to disable rate limiting for pulls.
295
+ */
296
+ pull?: Omit<RateLimitConfig, 'keyGenerator'> | false;
297
+
298
+ /**
299
+ * Rate limit config for push requests.
300
+ * Set to false to disable rate limiting for pushes.
301
+ */
302
+ push?: Omit<RateLimitConfig, 'keyGenerator'> | false;
303
+ }
304
+
305
+ /**
306
+ * Default sync rate limit configuration
307
+ */
308
+ export const DEFAULT_SYNC_RATE_LIMITS: SyncRateLimitConfig = {
309
+ pull: {
310
+ maxRequests: 120, // 2 requests per second average
311
+ windowMs: 60_000,
312
+ includeHeaders: true,
313
+ },
314
+ push: {
315
+ maxRequests: 60, // 1 request per second average
316
+ windowMs: 60_000,
317
+ includeHeaders: true,
318
+ },
319
+ };