workerstack 0.1.0-beta.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 (605) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +138 -0
  4. package/dist/access.d.ts +131 -0
  5. package/dist/access.d.ts.map +1 -0
  6. package/dist/access.js +333 -0
  7. package/dist/access.js.map +1 -0
  8. package/dist/api/builder.d.ts +457 -0
  9. package/dist/api/builder.d.ts.map +1 -0
  10. package/dist/api/builder.js +45 -0
  11. package/dist/api/builder.js.map +1 -0
  12. package/dist/api/catalog.d.ts +8 -0
  13. package/dist/api/catalog.d.ts.map +1 -0
  14. package/dist/api/catalog.js +56 -0
  15. package/dist/api/catalog.js.map +1 -0
  16. package/dist/api/context.d.ts +47 -0
  17. package/dist/api/context.d.ts.map +1 -0
  18. package/dist/api/context.js +40 -0
  19. package/dist/api/context.js.map +1 -0
  20. package/dist/api/crud-router.d.ts +476 -0
  21. package/dist/api/crud-router.d.ts.map +1 -0
  22. package/dist/api/crud-router.js +354 -0
  23. package/dist/api/crud-router.js.map +1 -0
  24. package/dist/api/list-input-schema.d.ts +37 -0
  25. package/dist/api/list-input-schema.d.ts.map +1 -0
  26. package/dist/api/list-input-schema.js +63 -0
  27. package/dist/api/list-input-schema.js.map +1 -0
  28. package/dist/api/list-spec.d.ts +44 -0
  29. package/dist/api/list-spec.d.ts.map +1 -0
  30. package/dist/api/list-spec.js +49 -0
  31. package/dist/api/list-spec.js.map +1 -0
  32. package/dist/api/live-view.d.ts +14 -0
  33. package/dist/api/live-view.d.ts.map +1 -0
  34. package/dist/api/live-view.js +42 -0
  35. package/dist/api/live-view.js.map +1 -0
  36. package/dist/api/live-window.d.ts +40 -0
  37. package/dist/api/live-window.d.ts.map +1 -0
  38. package/dist/api/live-window.js +93 -0
  39. package/dist/api/live-window.js.map +1 -0
  40. package/dist/api/openapi.d.ts +6 -0
  41. package/dist/api/openapi.d.ts.map +1 -0
  42. package/dist/api/openapi.js +146 -0
  43. package/dist/api/openapi.js.map +1 -0
  44. package/dist/api/realtime-router.d.ts +94 -0
  45. package/dist/api/realtime-router.d.ts.map +1 -0
  46. package/dist/api/realtime-router.js +45 -0
  47. package/dist/api/realtime-router.js.map +1 -0
  48. package/dist/api/registry.d.ts +30 -0
  49. package/dist/api/registry.d.ts.map +1 -0
  50. package/dist/api/registry.js +236 -0
  51. package/dist/api/registry.js.map +1 -0
  52. package/dist/api/router.d.ts +15 -0
  53. package/dist/api/router.d.ts.map +1 -0
  54. package/dist/api/router.js +32 -0
  55. package/dist/api/router.js.map +1 -0
  56. package/dist/api/storage-router.d.ts +349 -0
  57. package/dist/api/storage-router.d.ts.map +1 -0
  58. package/dist/api/storage-router.js +155 -0
  59. package/dist/api/storage-router.js.map +1 -0
  60. package/dist/api/types.d.ts +55 -0
  61. package/dist/api/types.d.ts.map +1 -0
  62. package/dist/api/types.js +2 -0
  63. package/dist/api/types.js.map +1 -0
  64. package/dist/auth-password.d.ts +27 -0
  65. package/dist/auth-password.d.ts.map +1 -0
  66. package/dist/auth-password.js +60 -0
  67. package/dist/auth-password.js.map +1 -0
  68. package/dist/auth.d.ts +59 -0
  69. package/dist/auth.d.ts.map +1 -0
  70. package/dist/auth.js +146 -0
  71. package/dist/auth.js.map +1 -0
  72. package/dist/backend-internals.d.ts +35 -0
  73. package/dist/backend-internals.d.ts.map +1 -0
  74. package/dist/backend-internals.js +2 -0
  75. package/dist/backend-internals.js.map +1 -0
  76. package/dist/backend.d.ts +63 -0
  77. package/dist/backend.d.ts.map +1 -0
  78. package/dist/backend.js +59 -0
  79. package/dist/backend.js.map +1 -0
  80. package/dist/blueprint-generator.d.ts +21 -0
  81. package/dist/blueprint-generator.d.ts.map +1 -0
  82. package/dist/blueprint-generator.js +199 -0
  83. package/dist/blueprint-generator.js.map +1 -0
  84. package/dist/blueprint.d.ts +99 -0
  85. package/dist/blueprint.d.ts.map +1 -0
  86. package/dist/blueprint.js +257 -0
  87. package/dist/blueprint.js.map +1 -0
  88. package/dist/cli-skills.d.ts +16 -0
  89. package/dist/cli-skills.d.ts.map +1 -0
  90. package/dist/cli-skills.js +108 -0
  91. package/dist/cli-skills.js.map +1 -0
  92. package/dist/cli.d.ts +18 -0
  93. package/dist/cli.d.ts.map +1 -0
  94. package/dist/cli.js +205 -0
  95. package/dist/cli.js.map +1 -0
  96. package/dist/client/client.d.ts +53 -0
  97. package/dist/client/client.d.ts.map +1 -0
  98. package/dist/client/client.js +111 -0
  99. package/dist/client/client.js.map +1 -0
  100. package/dist/client/index.d.ts +4 -0
  101. package/dist/client/index.d.ts.map +1 -0
  102. package/dist/client/index.js +4 -0
  103. package/dist/client/index.js.map +1 -0
  104. package/dist/client/live-view.d.ts +49 -0
  105. package/dist/client/live-view.d.ts.map +1 -0
  106. package/dist/client/live-view.js +184 -0
  107. package/dist/client/live-view.js.map +1 -0
  108. package/dist/client/react.d.ts +4 -0
  109. package/dist/client/react.d.ts.map +1 -0
  110. package/dist/client/react.js +6 -0
  111. package/dist/client/react.js.map +1 -0
  112. package/dist/client/realtime.d.ts +54 -0
  113. package/dist/client/realtime.d.ts.map +1 -0
  114. package/dist/client/realtime.js +105 -0
  115. package/dist/client/realtime.js.map +1 -0
  116. package/dist/client/rest.d.ts +2 -0
  117. package/dist/client/rest.d.ts.map +1 -0
  118. package/dist/client/rest.js +2 -0
  119. package/dist/client/rest.js.map +1 -0
  120. package/dist/client/rpc-client.d.ts +38 -0
  121. package/dist/client/rpc-client.d.ts.map +1 -0
  122. package/dist/client/rpc-client.js +59 -0
  123. package/dist/client/rpc-client.js.map +1 -0
  124. package/dist/client/solid.d.ts +7 -0
  125. package/dist/client/solid.d.ts.map +1 -0
  126. package/dist/client/solid.js +21 -0
  127. package/dist/client/solid.js.map +1 -0
  128. package/dist/client/svelte.d.ts +6 -0
  129. package/dist/client/svelte.d.ts.map +1 -0
  130. package/dist/client/svelte.js +9 -0
  131. package/dist/client/svelte.js.map +1 -0
  132. package/dist/client/vue.d.ts +5 -0
  133. package/dist/client/vue.d.ts.map +1 -0
  134. package/dist/client/vue.js +11 -0
  135. package/dist/client/vue.js.map +1 -0
  136. package/dist/codegen.d.ts +36 -0
  137. package/dist/codegen.d.ts.map +1 -0
  138. package/dist/codegen.js +235 -0
  139. package/dist/codegen.js.map +1 -0
  140. package/dist/config.d.ts +129 -0
  141. package/dist/config.d.ts.map +1 -0
  142. package/dist/config.js +75 -0
  143. package/dist/config.js.map +1 -0
  144. package/dist/cron.d.ts +4 -0
  145. package/dist/cron.d.ts.map +1 -0
  146. package/dist/cron.js +3 -0
  147. package/dist/cron.js.map +1 -0
  148. package/dist/crud-operations.d.ts +43 -0
  149. package/dist/crud-operations.d.ts.map +1 -0
  150. package/dist/crud-operations.js +249 -0
  151. package/dist/crud-operations.js.map +1 -0
  152. package/dist/database/adapter.d.ts +26 -0
  153. package/dist/database/adapter.d.ts.map +1 -0
  154. package/dist/database/adapter.js +2 -0
  155. package/dist/database/adapter.js.map +1 -0
  156. package/dist/database/libsql.d.ts +3 -0
  157. package/dist/database/libsql.d.ts.map +1 -0
  158. package/dist/database/libsql.js +39 -0
  159. package/dist/database/libsql.js.map +1 -0
  160. package/dist/db.d.ts +15 -0
  161. package/dist/db.d.ts.map +1 -0
  162. package/dist/db.js +8 -0
  163. package/dist/db.js.map +1 -0
  164. package/dist/dev/binaries.d.ts +22 -0
  165. package/dist/dev/binaries.d.ts.map +1 -0
  166. package/dist/dev/binaries.js +119 -0
  167. package/dist/dev/binaries.js.map +1 -0
  168. package/dist/dev/env.d.ts +11 -0
  169. package/dist/dev/env.d.ts.map +1 -0
  170. package/dist/dev/env.js +71 -0
  171. package/dist/dev/env.js.map +1 -0
  172. package/dist/dev/index.d.ts +39 -0
  173. package/dist/dev/index.d.ts.map +1 -0
  174. package/dist/dev/index.js +273 -0
  175. package/dist/dev/index.js.map +1 -0
  176. package/dist/dev/processes.d.ts +37 -0
  177. package/dist/dev/processes.d.ts.map +1 -0
  178. package/dist/dev/processes.js +104 -0
  179. package/dist/dev/processes.js.map +1 -0
  180. package/dist/dev/push.d.ts +2 -0
  181. package/dist/dev/push.d.ts.map +1 -0
  182. package/dist/dev/push.js +30 -0
  183. package/dist/dev/push.js.map +1 -0
  184. package/dist/dialect.d.ts +17 -0
  185. package/dist/dialect.d.ts.map +1 -0
  186. package/dist/dialect.js +12 -0
  187. package/dist/dialect.js.map +1 -0
  188. package/dist/email/smtp.d.ts +20 -0
  189. package/dist/email/smtp.d.ts.map +1 -0
  190. package/dist/email/smtp.js +31 -0
  191. package/dist/email/smtp.js.map +1 -0
  192. package/dist/email.d.ts +26 -0
  193. package/dist/email.d.ts.map +1 -0
  194. package/dist/email.js +2 -0
  195. package/dist/email.js.map +1 -0
  196. package/dist/env-probe.d.ts +9 -0
  197. package/dist/env-probe.d.ts.map +1 -0
  198. package/dist/env-probe.js +72 -0
  199. package/dist/env-probe.js.map +1 -0
  200. package/dist/env.d.ts +62 -0
  201. package/dist/env.d.ts.map +1 -0
  202. package/dist/env.js +86 -0
  203. package/dist/env.js.map +1 -0
  204. package/dist/errors.d.ts +153 -0
  205. package/dist/errors.d.ts.map +1 -0
  206. package/dist/errors.js +106 -0
  207. package/dist/errors.js.map +1 -0
  208. package/dist/handler.d.ts +11 -0
  209. package/dist/handler.d.ts.map +1 -0
  210. package/dist/handler.js +21 -0
  211. package/dist/handler.js.map +1 -0
  212. package/dist/hosted-contract.d.ts +11 -0
  213. package/dist/hosted-contract.d.ts.map +1 -0
  214. package/dist/hosted-contract.js +50 -0
  215. package/dist/hosted-contract.js.map +1 -0
  216. package/dist/idempotency.d.ts +17 -0
  217. package/dist/idempotency.d.ts.map +1 -0
  218. package/dist/idempotency.js +65 -0
  219. package/dist/idempotency.js.map +1 -0
  220. package/dist/index.d.ts +41 -0
  221. package/dist/index.d.ts.map +1 -0
  222. package/dist/index.js +18 -0
  223. package/dist/index.js.map +1 -0
  224. package/dist/inspect.d.ts +15 -0
  225. package/dist/inspect.d.ts.map +1 -0
  226. package/dist/inspect.js +44 -0
  227. package/dist/inspect.js.map +1 -0
  228. package/dist/internal-tables.d.ts +1767 -0
  229. package/dist/internal-tables.d.ts.map +1 -0
  230. package/dist/internal-tables.js +114 -0
  231. package/dist/internal-tables.js.map +1 -0
  232. package/dist/jobs/cron.d.ts +15 -0
  233. package/dist/jobs/cron.d.ts.map +1 -0
  234. package/dist/jobs/cron.js +74 -0
  235. package/dist/jobs/cron.js.map +1 -0
  236. package/dist/jobs/define.d.ts +186 -0
  237. package/dist/jobs/define.d.ts.map +1 -0
  238. package/dist/jobs/define.js +99 -0
  239. package/dist/jobs/define.js.map +1 -0
  240. package/dist/jobs/index.d.ts +9 -0
  241. package/dist/jobs/index.d.ts.map +1 -0
  242. package/dist/jobs/index.js +7 -0
  243. package/dist/jobs/index.js.map +1 -0
  244. package/dist/jobs/queue.d.ts +13 -0
  245. package/dist/jobs/queue.d.ts.map +1 -0
  246. package/dist/jobs/queue.js +71 -0
  247. package/dist/jobs/queue.js.map +1 -0
  248. package/dist/jobs/slots.d.ts +30 -0
  249. package/dist/jobs/slots.d.ts.map +1 -0
  250. package/dist/jobs/slots.js +53 -0
  251. package/dist/jobs/slots.js.map +1 -0
  252. package/dist/jobs/worker.d.ts +45 -0
  253. package/dist/jobs/worker.d.ts.map +1 -0
  254. package/dist/jobs/worker.js +605 -0
  255. package/dist/jobs/worker.js.map +1 -0
  256. package/dist/lifecycle.d.ts +10 -0
  257. package/dist/lifecycle.d.ts.map +1 -0
  258. package/dist/lifecycle.js +37 -0
  259. package/dist/lifecycle.js.map +1 -0
  260. package/dist/list-query.d.ts +62 -0
  261. package/dist/list-query.d.ts.map +1 -0
  262. package/dist/list-query.js +190 -0
  263. package/dist/list-query.js.map +1 -0
  264. package/dist/live/apply.d.ts +13 -0
  265. package/dist/live/apply.d.ts.map +1 -0
  266. package/dist/live/apply.js +32 -0
  267. package/dist/live/apply.js.map +1 -0
  268. package/dist/live/index.d.ts +48 -0
  269. package/dist/live/index.d.ts.map +1 -0
  270. package/dist/live/index.js +108 -0
  271. package/dist/live/index.js.map +1 -0
  272. package/dist/live/protocol.d.ts +47 -0
  273. package/dist/live/protocol.d.ts.map +1 -0
  274. package/dist/live/protocol.js +12 -0
  275. package/dist/live/protocol.js.map +1 -0
  276. package/dist/live/sse.d.ts +22 -0
  277. package/dist/live/sse.d.ts.map +1 -0
  278. package/dist/live/sse.js +41 -0
  279. package/dist/live/sse.js.map +1 -0
  280. package/dist/logging.d.ts +8 -0
  281. package/dist/logging.d.ts.map +1 -0
  282. package/dist/logging.js +6 -0
  283. package/dist/logging.js.map +1 -0
  284. package/dist/manifest-diff.d.ts +6 -0
  285. package/dist/manifest-diff.d.ts.map +1 -0
  286. package/dist/manifest-diff.js +42 -0
  287. package/dist/manifest-diff.js.map +1 -0
  288. package/dist/manifest.d.ts +87 -0
  289. package/dist/manifest.d.ts.map +1 -0
  290. package/dist/manifest.js +237 -0
  291. package/dist/manifest.js.map +1 -0
  292. package/dist/messaging/email.d.ts +16 -0
  293. package/dist/messaging/email.d.ts.map +1 -0
  294. package/dist/messaging/email.js +8 -0
  295. package/dist/messaging/email.js.map +1 -0
  296. package/dist/messaging/index.d.ts +8 -0
  297. package/dist/messaging/index.d.ts.map +1 -0
  298. package/dist/messaging/index.js +4 -0
  299. package/dist/messaging/index.js.map +1 -0
  300. package/dist/messaging/journal.d.ts +4 -0
  301. package/dist/messaging/journal.d.ts.map +1 -0
  302. package/dist/messaging/journal.js +19 -0
  303. package/dist/messaging/journal.js.map +1 -0
  304. package/dist/messaging/runtime.d.ts +16 -0
  305. package/dist/messaging/runtime.d.ts.map +1 -0
  306. package/dist/messaging/runtime.js +213 -0
  307. package/dist/messaging/runtime.js.map +1 -0
  308. package/dist/messaging/standalone.d.ts +27 -0
  309. package/dist/messaging/standalone.d.ts.map +1 -0
  310. package/dist/messaging/standalone.js +18 -0
  311. package/dist/messaging/standalone.js.map +1 -0
  312. package/dist/messaging/telegram.d.ts +16 -0
  313. package/dist/messaging/telegram.d.ts.map +1 -0
  314. package/dist/messaging/telegram.js +5 -0
  315. package/dist/messaging/telegram.js.map +1 -0
  316. package/dist/messaging/types.d.ts +22 -0
  317. package/dist/messaging/types.d.ts.map +1 -0
  318. package/dist/messaging/types.js +17 -0
  319. package/dist/messaging/types.js.map +1 -0
  320. package/dist/platform.d.ts +25 -0
  321. package/dist/platform.d.ts.map +1 -0
  322. package/dist/platform.js +24 -0
  323. package/dist/platform.js.map +1 -0
  324. package/dist/provision-internals.d.ts +23 -0
  325. package/dist/provision-internals.d.ts.map +1 -0
  326. package/dist/provision-internals.js +7 -0
  327. package/dist/provision-internals.js.map +1 -0
  328. package/dist/provision-runtime.d.ts +6 -0
  329. package/dist/provision-runtime.d.ts.map +1 -0
  330. package/dist/provision-runtime.js +41 -0
  331. package/dist/provision-runtime.js.map +1 -0
  332. package/dist/provision-schema.d.ts +16 -0
  333. package/dist/provision-schema.d.ts.map +1 -0
  334. package/dist/provision-schema.js +59 -0
  335. package/dist/provision-schema.js.map +1 -0
  336. package/dist/provision.d.ts +8 -0
  337. package/dist/provision.d.ts.map +1 -0
  338. package/dist/provision.js +16 -0
  339. package/dist/provision.js.map +1 -0
  340. package/dist/query/api.d.ts +10 -0
  341. package/dist/query/api.d.ts.map +1 -0
  342. package/dist/query/api.js +7 -0
  343. package/dist/query/api.js.map +1 -0
  344. package/dist/query/client.d.ts +28 -0
  345. package/dist/query/client.d.ts.map +1 -0
  346. package/dist/query/client.js +87 -0
  347. package/dist/query/client.js.map +1 -0
  348. package/dist/query/fetch.d.ts +3 -0
  349. package/dist/query/fetch.d.ts.map +1 -0
  350. package/dist/query/fetch.js +4 -0
  351. package/dist/query/fetch.js.map +1 -0
  352. package/dist/query/index.d.ts +8 -0
  353. package/dist/query/index.d.ts.map +1 -0
  354. package/dist/query/index.js +4 -0
  355. package/dist/query/index.js.map +1 -0
  356. package/dist/query/infer.d.ts +25 -0
  357. package/dist/query/infer.d.ts.map +1 -0
  358. package/dist/query/infer.js +2 -0
  359. package/dist/query/infer.js.map +1 -0
  360. package/dist/query/react.d.ts +3 -0
  361. package/dist/query/react.d.ts.map +1 -0
  362. package/dist/query/react.js +2 -0
  363. package/dist/query/react.js.map +1 -0
  364. package/dist/query/realtime-flush.d.ts +26 -0
  365. package/dist/query/realtime-flush.d.ts.map +1 -0
  366. package/dist/query/realtime-flush.js +45 -0
  367. package/dist/query/realtime-flush.js.map +1 -0
  368. package/dist/query/realtime-stream.d.ts +3 -0
  369. package/dist/query/realtime-stream.d.ts.map +1 -0
  370. package/dist/query/realtime-stream.js +3 -0
  371. package/dist/query/realtime-stream.js.map +1 -0
  372. package/dist/query/realtime.d.ts +88 -0
  373. package/dist/query/realtime.d.ts.map +1 -0
  374. package/dist/query/realtime.js +255 -0
  375. package/dist/query/realtime.js.map +1 -0
  376. package/dist/rate-limit.d.ts +8 -0
  377. package/dist/rate-limit.d.ts.map +1 -0
  378. package/dist/rate-limit.js +46 -0
  379. package/dist/rate-limit.js.map +1 -0
  380. package/dist/readiness-probes.d.ts +4 -0
  381. package/dist/readiness-probes.d.ts.map +1 -0
  382. package/dist/readiness-probes.js +23 -0
  383. package/dist/readiness-probes.js.map +1 -0
  384. package/dist/readiness.d.ts +39 -0
  385. package/dist/readiness.d.ts.map +1 -0
  386. package/dist/readiness.js +72 -0
  387. package/dist/readiness.js.map +1 -0
  388. package/dist/realtime/facade.d.ts +20 -0
  389. package/dist/realtime/facade.d.ts.map +1 -0
  390. package/dist/realtime/facade.js +37 -0
  391. package/dist/realtime/facade.js.map +1 -0
  392. package/dist/realtime/filter.d.ts +42 -0
  393. package/dist/realtime/filter.d.ts.map +1 -0
  394. package/dist/realtime/filter.js +86 -0
  395. package/dist/realtime/filter.js.map +1 -0
  396. package/dist/realtime/heartbeat.d.ts +20 -0
  397. package/dist/realtime/heartbeat.d.ts.map +1 -0
  398. package/dist/realtime/heartbeat.js +64 -0
  399. package/dist/realtime/heartbeat.js.map +1 -0
  400. package/dist/realtime/publisher.d.ts +19 -0
  401. package/dist/realtime/publisher.d.ts.map +1 -0
  402. package/dist/realtime/publisher.js +8 -0
  403. package/dist/realtime/publisher.js.map +1 -0
  404. package/dist/runtime.d.ts +141 -0
  405. package/dist/runtime.d.ts.map +1 -0
  406. package/dist/runtime.js +463 -0
  407. package/dist/runtime.js.map +1 -0
  408. package/dist/schema-export.d.ts +2 -0
  409. package/dist/schema-export.d.ts.map +1 -0
  410. package/dist/schema-export.js +2 -0
  411. package/dist/schema-export.js.map +1 -0
  412. package/dist/scope.d.ts +4 -0
  413. package/dist/scope.d.ts.map +1 -0
  414. package/dist/scope.js +13 -0
  415. package/dist/scope.js.map +1 -0
  416. package/dist/standard-schema.d.ts +13 -0
  417. package/dist/standard-schema.d.ts.map +1 -0
  418. package/dist/standard-schema.js +34 -0
  419. package/dist/standard-schema.js.map +1 -0
  420. package/dist/start/auth-client.d.ts +11 -0
  421. package/dist/start/auth-client.d.ts.map +1 -0
  422. package/dist/start/auth-client.js +15 -0
  423. package/dist/start/auth-client.js.map +1 -0
  424. package/dist/start/create-start-worker.d.ts +16 -0
  425. package/dist/start/create-start-worker.d.ts.map +1 -0
  426. package/dist/start/create-start-worker.js +24 -0
  427. package/dist/start/create-start-worker.js.map +1 -0
  428. package/dist/start/index.d.ts +66 -0
  429. package/dist/start/index.d.ts.map +1 -0
  430. package/dist/start/index.js +49 -0
  431. package/dist/start/index.js.map +1 -0
  432. package/dist/start/isomorphic-fetch.d.ts +22 -0
  433. package/dist/start/isomorphic-fetch.d.ts.map +1 -0
  434. package/dist/start/isomorphic-fetch.js +49 -0
  435. package/dist/start/isomorphic-fetch.js.map +1 -0
  436. package/dist/start/server-entry.d.ts +7 -0
  437. package/dist/start/server-entry.d.ts.map +1 -0
  438. package/dist/start/server-entry.js +8 -0
  439. package/dist/start/server-entry.js.map +1 -0
  440. package/dist/storage/background.d.ts +3 -0
  441. package/dist/storage/background.d.ts.map +1 -0
  442. package/dist/storage/background.js +3 -0
  443. package/dist/storage/background.js.map +1 -0
  444. package/dist/storage/buckets.d.ts +78 -0
  445. package/dist/storage/buckets.d.ts.map +1 -0
  446. package/dist/storage/buckets.js +177 -0
  447. package/dist/storage/buckets.js.map +1 -0
  448. package/dist/storage/delete.d.ts +8 -0
  449. package/dist/storage/delete.d.ts.map +1 -0
  450. package/dist/storage/delete.js +10 -0
  451. package/dist/storage/delete.js.map +1 -0
  452. package/dist/storage/file-meta.d.ts +37 -0
  453. package/dist/storage/file-meta.d.ts.map +1 -0
  454. package/dist/storage/file-meta.js +166 -0
  455. package/dist/storage/file-meta.js.map +1 -0
  456. package/dist/storage/index.d.ts +28 -0
  457. package/dist/storage/index.d.ts.map +1 -0
  458. package/dist/storage/index.js +5 -0
  459. package/dist/storage/index.js.map +1 -0
  460. package/dist/storage/local.d.ts +15 -0
  461. package/dist/storage/local.d.ts.map +1 -0
  462. package/dist/storage/local.js +56 -0
  463. package/dist/storage/local.js.map +1 -0
  464. package/dist/storage/operations.d.ts +54 -0
  465. package/dist/storage/operations.d.ts.map +1 -0
  466. package/dist/storage/operations.js +212 -0
  467. package/dist/storage/operations.js.map +1 -0
  468. package/dist/storage/registry.d.ts +11 -0
  469. package/dist/storage/registry.d.ts.map +1 -0
  470. package/dist/storage/registry.js +23 -0
  471. package/dist/storage/registry.js.map +1 -0
  472. package/dist/storage/s3.d.ts +35 -0
  473. package/dist/storage/s3.d.ts.map +1 -0
  474. package/dist/storage/s3.js +125 -0
  475. package/dist/storage/s3.js.map +1 -0
  476. package/dist/storage/sigv4.d.ts +24 -0
  477. package/dist/storage/sigv4.d.ts.map +1 -0
  478. package/dist/storage/sigv4.js +107 -0
  479. package/dist/storage/sigv4.js.map +1 -0
  480. package/dist/storage/sweep.d.ts +9 -0
  481. package/dist/storage/sweep.d.ts.map +1 -0
  482. package/dist/storage/sweep.js +17 -0
  483. package/dist/storage/sweep.js.map +1 -0
  484. package/dist/sync/collection.d.ts +106 -0
  485. package/dist/sync/collection.d.ts.map +1 -0
  486. package/dist/sync/collection.js +258 -0
  487. package/dist/sync/collection.js.map +1 -0
  488. package/dist/sync/index.d.ts +8 -0
  489. package/dist/sync/index.d.ts.map +1 -0
  490. package/dist/sync/index.js +4 -0
  491. package/dist/sync/index.js.map +1 -0
  492. package/dist/sync/realtime-sync.d.ts +31 -0
  493. package/dist/sync/realtime-sync.d.ts.map +1 -0
  494. package/dist/sync/realtime-sync.js +46 -0
  495. package/dist/sync/realtime-sync.js.map +1 -0
  496. package/dist/sync/sync-client.d.ts +28 -0
  497. package/dist/sync/sync-client.d.ts.map +1 -0
  498. package/dist/sync/sync-client.js +52 -0
  499. package/dist/sync/sync-client.js.map +1 -0
  500. package/dist/sync/update-queue.d.ts +27 -0
  501. package/dist/sync/update-queue.d.ts.map +1 -0
  502. package/dist/sync/update-queue.js +66 -0
  503. package/dist/sync/update-queue.js.map +1 -0
  504. package/dist/testing/auth.d.ts +85 -0
  505. package/dist/testing/auth.d.ts.map +1 -0
  506. package/dist/testing/auth.js +140 -0
  507. package/dist/testing/auth.js.map +1 -0
  508. package/dist/testing/client.d.ts +6 -0
  509. package/dist/testing/client.d.ts.map +1 -0
  510. package/dist/testing/client.js +9 -0
  511. package/dist/testing/client.js.map +1 -0
  512. package/dist/testing/database.d.ts +6 -0
  513. package/dist/testing/database.d.ts.map +1 -0
  514. package/dist/testing/database.js +8 -0
  515. package/dist/testing/database.js.map +1 -0
  516. package/dist/testing/email.d.ts +15 -0
  517. package/dist/testing/email.d.ts.map +1 -0
  518. package/dist/testing/email.js +32 -0
  519. package/dist/testing/email.js.map +1 -0
  520. package/dist/testing/fixture.d.ts +51 -0
  521. package/dist/testing/fixture.d.ts.map +1 -0
  522. package/dist/testing/fixture.js +177 -0
  523. package/dist/testing/fixture.js.map +1 -0
  524. package/dist/testing/jobs.d.ts +40 -0
  525. package/dist/testing/jobs.d.ts.map +1 -0
  526. package/dist/testing/jobs.js +76 -0
  527. package/dist/testing/jobs.js.map +1 -0
  528. package/dist/testing/logs.d.ts +18 -0
  529. package/dist/testing/logs.d.ts.map +1 -0
  530. package/dist/testing/logs.js +53 -0
  531. package/dist/testing/logs.js.map +1 -0
  532. package/dist/testing/messaging.d.ts +21 -0
  533. package/dist/testing/messaging.d.ts.map +1 -0
  534. package/dist/testing/messaging.js +34 -0
  535. package/dist/testing/messaging.js.map +1 -0
  536. package/dist/testing/storage.d.ts +7 -0
  537. package/dist/testing/storage.d.ts.map +1 -0
  538. package/dist/testing/storage.js +29 -0
  539. package/dist/testing/storage.js.map +1 -0
  540. package/dist/testing/workers-fakes.d.ts +17 -0
  541. package/dist/testing/workers-fakes.d.ts.map +1 -0
  542. package/dist/testing/workers-fakes.js +98 -0
  543. package/dist/testing/workers-fakes.js.map +1 -0
  544. package/dist/testing.d.ts +12 -0
  545. package/dist/testing.d.ts.map +1 -0
  546. package/dist/testing.js +5 -0
  547. package/dist/testing.js.map +1 -0
  548. package/dist/typeid.d.ts +46 -0
  549. package/dist/typeid.d.ts.map +1 -0
  550. package/dist/typeid.js +130 -0
  551. package/dist/typeid.js.map +1 -0
  552. package/dist/vite.d.ts +26 -0
  553. package/dist/vite.d.ts.map +1 -0
  554. package/dist/vite.js +100 -0
  555. package/dist/vite.js.map +1 -0
  556. package/dist/worker-plan.d.ts +37 -0
  557. package/dist/worker-plan.d.ts.map +1 -0
  558. package/dist/worker-plan.js +63 -0
  559. package/dist/worker-plan.js.map +1 -0
  560. package/dist/workers/app.d.ts +19 -0
  561. package/dist/workers/app.d.ts.map +1 -0
  562. package/dist/workers/app.js +56 -0
  563. package/dist/workers/app.js.map +1 -0
  564. package/dist/workers/entry.d.ts +7 -0
  565. package/dist/workers/entry.d.ts.map +1 -0
  566. package/dist/workers/entry.js +8 -0
  567. package/dist/workers/entry.js.map +1 -0
  568. package/dist/workers/index.d.ts +23 -0
  569. package/dist/workers/index.d.ts.map +1 -0
  570. package/dist/workers/index.js +39 -0
  571. package/dist/workers/index.js.map +1 -0
  572. package/dist/workers/r2.d.ts +24 -0
  573. package/dist/workers/r2.d.ts.map +1 -0
  574. package/dist/workers/r2.js +76 -0
  575. package/dist/workers/r2.js.map +1 -0
  576. package/dist/workers/rate-limiter.d.ts +9 -0
  577. package/dist/workers/rate-limiter.d.ts.map +1 -0
  578. package/dist/workers/rate-limiter.js +22 -0
  579. package/dist/workers/rate-limiter.js.map +1 -0
  580. package/dist/workers/realtime-hub.d.ts +20 -0
  581. package/dist/workers/realtime-hub.d.ts.map +1 -0
  582. package/dist/workers/realtime-hub.js +117 -0
  583. package/dist/workers/realtime-hub.js.map +1 -0
  584. package/dist/workers/registry.d.ts +13 -0
  585. package/dist/workers/registry.d.ts.map +1 -0
  586. package/dist/workers/registry.js +19 -0
  587. package/dist/workers/registry.js.map +1 -0
  588. package/dist/workers/scheduler.d.ts +13 -0
  589. package/dist/workers/scheduler.d.ts.map +1 -0
  590. package/dist/workers/scheduler.js +71 -0
  591. package/dist/workers/scheduler.js.map +1 -0
  592. package/dist/workers/types.d.ts +55 -0
  593. package/dist/workers/types.d.ts.map +1 -0
  594. package/dist/workers/types.js +4 -0
  595. package/dist/workers/types.js.map +1 -0
  596. package/dist/workers/wrangler.d.ts +62 -0
  597. package/dist/workers/wrangler.d.ts.map +1 -0
  598. package/dist/workers/wrangler.js +90 -0
  599. package/dist/workers/wrangler.js.map +1 -0
  600. package/package.json +286 -0
  601. package/skills/creating-workerstack-apps/SKILL.md +45 -0
  602. package/skills/creating-workerstack-apps/agents/openai.yaml +4 -0
  603. package/skills/creating-workerstack-apps/references/application-structure.md +219 -0
  604. package/skills/creating-workerstack-apps/references/runtime-integrations.md +96 -0
  605. package/skills/creating-workerstack-apps/references/verification.md +29 -0
@@ -0,0 +1,31 @@
1
+ import { type RealtimeProcedure, type RealtimeSyncHandle } from '../client/realtime.js';
2
+ type SyncableCollection = {
3
+ utils: {
4
+ writeUpsert(item: unknown): void;
5
+ writeDelete(key: unknown): void;
6
+ refetch(): Promise<void>;
7
+ };
8
+ };
9
+ export type SyncRealtimeTarget = {
10
+ applyRealtimeEvent(action: 'create' | 'update' | 'delete', record: Record<string, unknown>): void;
11
+ refetchAll(): Promise<void>;
12
+ };
13
+ export type SyncRealtimeConfig = {
14
+ api: {
15
+ realtime: {
16
+ changes: RealtimeProcedure;
17
+ };
18
+ };
19
+ tables: string[];
20
+ collections?: Record<string, SyncableCollection>;
21
+ resolve?: (table: string) => SyncRealtimeTarget | undefined;
22
+ resolveAll?: () => Iterable<SyncRealtimeTarget>;
23
+ signal?: AbortSignal;
24
+ retryMs?: number;
25
+ maxRetryMs?: number;
26
+ onError?: (error: unknown) => void;
27
+ };
28
+ /** TanStack DB consumes the shared raw stream without a QueryClient detour. */
29
+ export declare function createSyncRealtimeClient(config: SyncRealtimeConfig): RealtimeSyncHandle;
30
+ export {};
31
+ //# sourceMappingURL=realtime-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-sync.d.ts","sourceRoot":"","sources":["../../src/sync/realtime-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAA;AAE3B,KAAK,kBAAkB,GAAG;IACxB,KAAK,EAAE;QACL,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;QAChC,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;QAC/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KACzB,CAAA;CACF,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kBAAkB,CAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI,CAAA;IACP,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE;QAAE,QAAQ,EAAE;YAAE,OAAO,EAAE,iBAAiB,CAAA;SAAE,CAAA;KAAE,CAAA;IACjD,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAA;IAC3D,UAAU,CAAC,EAAE,MAAM,QAAQ,CAAC,kBAAkB,CAAC,CAAA;IAC/C,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACnC,CAAA;AAED,+EAA+E;AAC/E,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,GACzB,kBAAkB,CAoDpB"}
@@ -0,0 +1,46 @@
1
+ import { openRealtimeStream, } from '../client/realtime.js';
2
+ /** TanStack DB consumes the shared raw stream without a QueryClient detour. */
3
+ export function createSyncRealtimeClient(config) {
4
+ const collections = config.collections ?? {};
5
+ const caller = new AbortController();
6
+ const signal = config.signal
7
+ ? AbortSignal.any([caller.signal, config.signal])
8
+ : caller.signal;
9
+ const stream = openRealtimeStream({
10
+ signal,
11
+ retryMs: config.retryMs,
12
+ maxRetryMs: config.maxRetryMs,
13
+ onError: config.onError,
14
+ subscribe: ({ signal, lastEventId }) => config.api.realtime.changes.call({ tables: config.tables }, { signal, lastEventId }),
15
+ onChange: (event) => {
16
+ if (config.resolve) {
17
+ config
18
+ .resolve(event.table)
19
+ ?.applyRealtimeEvent(event.action, event.record);
20
+ return;
21
+ }
22
+ const collection = collections[event.table];
23
+ if (!collection)
24
+ return;
25
+ if (event.action === 'delete')
26
+ collection.utils.writeDelete(event.record['id']);
27
+ else
28
+ collection.utils.writeUpsert(event.record);
29
+ },
30
+ onReconnect: async () => {
31
+ if (config.resolveAll) {
32
+ await Promise.all([...config.resolveAll()].map((target) => target.refetchAll()));
33
+ return;
34
+ }
35
+ await Promise.all(Object.values(collections).map((collection) => collection.utils.refetch()));
36
+ },
37
+ });
38
+ return {
39
+ close() {
40
+ caller.abort();
41
+ stream.close();
42
+ },
43
+ done: stream.done,
44
+ };
45
+ }
46
+ //# sourceMappingURL=realtime-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-sync.js","sourceRoot":"","sources":["../../src/sync/realtime-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAGnB,MAAM,oBAAoB,CAAA;AA8B3B,+EAA+E;AAC/E,MAAM,UAAU,wBAAwB,CACtC,MAA0B;IAE1B,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAA;IAC5C,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM;QAC1B,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;IAEjB,MAAM,MAAM,GAAG,kBAAkB,CAAC;QAChC,MAAM;QACN,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,CACrC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAC9B,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EACzB,EAAE,MAAM,EAAE,WAAW,EAAE,CACxB;QACH,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;YAClB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,MAAM;qBACH,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;oBACrB,EAAE,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;gBAClD,OAAM;YACR,CAAC;YACD,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAC3C,IAAI,CAAC,UAAU;gBAAE,OAAM;YACvB,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;gBAC3B,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;;gBAC7C,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QACjD,CAAC;QACD,WAAW,EAAE,KAAK,IAAI,EAAE;YACtB,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACtB,MAAM,OAAO,CAAC,GAAG,CACf,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAC9D,CAAA;gBACD,OAAM;YACR,CAAC;YACD,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAC5C,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,CAC3B,CACF,CAAA;QACH,CAAC;KACF,CAAC,CAAA;IAEF,OAAO;QACL,KAAK;YACH,MAAM,CAAC,KAAK,EAAE,CAAA;YACd,MAAM,CAAC,KAAK,EAAE,CAAA;QAChB,CAAC;QACD,IAAI,EAAE,MAAM,CAAC,IAAI;KAClB,CAAA;AACH,CAAC","sourcesContent":["import {\n openRealtimeStream,\n type RealtimeProcedure,\n type RealtimeSyncHandle,\n} from '../client/realtime'\n\ntype SyncableCollection = {\n utils: {\n writeUpsert(item: unknown): void\n writeDelete(key: unknown): void\n refetch(): Promise<void>\n }\n}\n\nexport type SyncRealtimeTarget = {\n applyRealtimeEvent(\n action: 'create' | 'update' | 'delete',\n record: Record<string, unknown>,\n ): void\n refetchAll(): Promise<void>\n}\n\nexport type SyncRealtimeConfig = {\n api: { realtime: { changes: RealtimeProcedure } }\n tables: string[]\n collections?: Record<string, SyncableCollection>\n resolve?: (table: string) => SyncRealtimeTarget | undefined\n resolveAll?: () => Iterable<SyncRealtimeTarget>\n signal?: AbortSignal\n retryMs?: number\n maxRetryMs?: number\n onError?: (error: unknown) => void\n}\n\n/** TanStack DB consumes the shared raw stream without a QueryClient detour. */\nexport function createSyncRealtimeClient(\n config: SyncRealtimeConfig,\n): RealtimeSyncHandle {\n const collections = config.collections ?? {}\n const caller = new AbortController()\n const signal = config.signal\n ? AbortSignal.any([caller.signal, config.signal])\n : caller.signal\n\n const stream = openRealtimeStream({\n signal,\n retryMs: config.retryMs,\n maxRetryMs: config.maxRetryMs,\n onError: config.onError,\n subscribe: ({ signal, lastEventId }) =>\n config.api.realtime.changes.call(\n { tables: config.tables },\n { signal, lastEventId },\n ),\n onChange: (event) => {\n if (config.resolve) {\n config\n .resolve(event.table)\n ?.applyRealtimeEvent(event.action, event.record)\n return\n }\n const collection = collections[event.table]\n if (!collection) return\n if (event.action === 'delete')\n collection.utils.writeDelete(event.record['id'])\n else collection.utils.writeUpsert(event.record)\n },\n onReconnect: async () => {\n if (config.resolveAll) {\n await Promise.all(\n [...config.resolveAll()].map((target) => target.refetchAll()),\n )\n return\n }\n await Promise.all(\n Object.values(collections).map((collection) =>\n collection.utils.refetch(),\n ),\n )\n },\n })\n\n return {\n close() {\n caller.abort()\n stream.close()\n },\n done: stream.done,\n }\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import type { QueryClient } from '@tanstack/react-query';
2
+ import { type AnyWorkerstackApp, type WorkerstackClient, type InferInsert, type InferSchema, type InferSelect, type InferTables } from '../query/index.js';
3
+ import { type TableCollection } from './collection.js';
4
+ export type RowFor<TSchema extends Record<string, unknown>, K extends keyof TSchema> = [InferSelect<TSchema[K]>] extends [never] ? {
5
+ id: string | number;
6
+ } : InferSelect<TSchema[K]> extends {
7
+ id: string | number;
8
+ } ? InferSelect<TSchema[K]> : {
9
+ id: string | number;
10
+ };
11
+ export type CreateFor<TSchema extends Record<string, unknown>, K extends keyof TSchema> = [InferInsert<TSchema[K]>] extends [never] ? Partial<RowFor<TSchema, K>> : InferInsert<TSchema[K]>;
12
+ export type SyncClientOptions = {
13
+ baseUrl?: string;
14
+ fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
15
+ queryClient: QueryClient;
16
+ realtime?: boolean;
17
+ };
18
+ export type WorkerstackSyncClient<TApp extends AnyWorkerstackApp> = {
19
+ [K in InferTables<TApp>]: TableCollection<RowFor<InferSchema<TApp>, K>, CreateFor<InferSchema<TApp>, K>, Partial<RowFor<InferSchema<TApp>, K>>>;
20
+ } & {
21
+ files: WorkerstackClient<TApp>['files'];
22
+ realtime: {
23
+ close(): void;
24
+ subscribe(tables: string[]): Promise<void>;
25
+ } | undefined;
26
+ };
27
+ export declare function createSyncClient<TApp extends AnyWorkerstackApp>(options: SyncClientOptions): WorkerstackSyncClient<TApp>;
28
+ //# sourceMappingURL=sync-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-client.d.ts","sourceRoot":"","sources":["../../src/sync/sync-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAM1E,MAAM,MAAM,MAAM,CAChB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,CAAC,SAAS,MAAM,OAAO,IACrB,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACzC;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GACvB,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GACrD,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GACvB;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAA;AAE7B,MAAM,MAAM,SAAS,CACnB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,CAAC,SAAS,MAAM,OAAO,IACrB,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACzC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,GAC3B,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;AAE3B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC3E,WAAW,EAAE,WAAW,CAAA;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,qBAAqB,CAAC,IAAI,SAAS,iBAAiB,IAAI;KACjE,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,eAAe,CACvC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAC5B,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAC/B,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CACtC;CACF,GAAG;IACF,KAAK,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAA;IACvC,QAAQ,EACJ;QAAE,KAAK,IAAI,IAAI,CAAC;QAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,GAC7D,SAAS,CAAA;CACd,CAAA;AAED,wBAAgB,gBAAgB,CAAC,IAAI,SAAS,iBAAiB,EAC7D,OAAO,EAAE,iBAAiB,GACzB,qBAAqB,CAAC,IAAI,CAAC,CA+C7B"}
@@ -0,0 +1,52 @@
1
+ import { createClient, } from '../query/index.js';
2
+ import { createTableCollection } from './collection.js';
3
+ import { createSyncRealtimeClient, } from './realtime-sync.js';
4
+ export function createSyncClient(options) {
5
+ const api = createClient(options);
6
+ const materialized = new Map();
7
+ const realtimeHandles = new Map();
8
+ const tables = new Map();
9
+ const realtimeEnabled = options.realtime ?? typeof window !== 'undefined';
10
+ const result = new Proxy({}, {
11
+ get(_target, property) {
12
+ if (typeof property !== 'string')
13
+ return undefined;
14
+ if (property === 'files')
15
+ return api.files;
16
+ if (property === 'realtime') {
17
+ return realtimeEnabled
18
+ ? {
19
+ close: () => realtimeHandles.forEach((handle) => handle.close()),
20
+ subscribe: async (names) => {
21
+ for (const name of names)
22
+ void result[name];
23
+ },
24
+ }
25
+ : undefined;
26
+ }
27
+ if (['then', 'toJSON', 'constructor', '$$typeof'].includes(property))
28
+ return undefined;
29
+ const cached = tables.get(property);
30
+ if (cached)
31
+ return cached;
32
+ const collection = createTableCollection({
33
+ tableName: property,
34
+ procedures: api[property],
35
+ queryClient: options.queryClient,
36
+ });
37
+ tables.set(property, collection);
38
+ materialized.set(property, collection);
39
+ if (realtimeEnabled) {
40
+ realtimeHandles.set(property, createSyncRealtimeClient({
41
+ api: api,
42
+ tables: [property],
43
+ resolve: (table) => materialized.get(table),
44
+ resolveAll: () => materialized.values(),
45
+ }));
46
+ }
47
+ return collection;
48
+ },
49
+ });
50
+ return result;
51
+ }
52
+ //# sourceMappingURL=sync-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-client.js","sourceRoot":"","sources":["../../src/sync/sync-client.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,GAOb,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,qBAAqB,EAAwB,MAAM,cAAc,CAAA;AAC1E,OAAO,EACL,wBAAwB,GAEzB,MAAM,iBAAiB,CAAA;AAsCxB,MAAM,UAAU,gBAAgB,CAC9B,OAA0B;IAE1B,MAAM,GAAG,GAAG,YAAY,CAAO,OAAO,CAAC,CAAA;IACvC,MAAM,YAAY,GAAG,IAAI,GAAG,EAA8B,CAAA;IAC1D,MAAM,eAAe,GAAG,IAAI,GAAG,EAA6B,CAAA;IAC5D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAA;IACzC,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,MAAM,KAAK,WAAW,CAAA;IAEzE,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,EAAiC,EAAE;QAC1D,GAAG,CAAC,OAAO,EAAE,QAAQ;YACnB,IAAI,OAAO,QAAQ,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAA;YAClD,IAAI,QAAQ,KAAK,OAAO;gBAAE,OAAO,GAAG,CAAC,KAAK,CAAA;YAC1C,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;gBAC5B,OAAO,eAAe;oBACpB,CAAC,CAAC;wBACE,KAAK,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;wBAChE,SAAS,EAAE,KAAK,EAAE,KAAe,EAAE,EAAE;4BACnC,KAAK,MAAM,IAAI,IAAI,KAAK;gCAAE,KAAM,MAAc,CAAC,IAAI,CAAC,CAAA;wBACtD,CAAC;qBACF;oBACH,CAAC,CAAC,SAAS,CAAA;YACf,CAAC;YACD,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAClE,OAAO,SAAS,CAAA;YAClB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACnC,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAA;YACzB,MAAM,UAAU,GAAG,qBAAqB,CAAC;gBACvC,SAAS,EAAE,QAAQ;gBACnB,UAAU,EAAG,GAAW,CAAC,QAAQ,CAAC;gBAClC,WAAW,EAAE,OAAO,CAAC,WAAW;aACjC,CAAC,CAAA;YACF,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;YAChC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;YACtC,IAAI,eAAe,EAAE,CAAC;gBACpB,eAAe,CAAC,GAAG,CACjB,QAAQ,EACR,wBAAwB,CAAC;oBACvB,GAAG,EAAE,GAAU;oBACf,MAAM,EAAE,CAAC,QAAQ,CAAC;oBAClB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;oBAC3C,UAAU,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE;iBACxC,CAAC,CACH,CAAA;YACH,CAAC;YACD,OAAO,UAAU,CAAA;QACnB,CAAC;KACF,CAAC,CAAA;IACF,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import type { QueryClient } from '@tanstack/react-query'\n\nimport {\n createClient,\n type AnyWorkerstackApp,\n type WorkerstackClient,\n type InferInsert,\n type InferSchema,\n type InferSelect,\n type InferTables,\n} from '../query/index'\nimport { createTableCollection, type TableCollection } from './collection'\nimport {\n createSyncRealtimeClient,\n type SyncRealtimeTarget,\n} from './realtime-sync'\n\nexport type RowFor<\n TSchema extends Record<string, unknown>,\n K extends keyof TSchema,\n> = [InferSelect<TSchema[K]>] extends [never]\n ? { id: string | number }\n : InferSelect<TSchema[K]> extends { id: string | number }\n ? InferSelect<TSchema[K]>\n : { id: string | number }\n\nexport type CreateFor<\n TSchema extends Record<string, unknown>,\n K extends keyof TSchema,\n> = [InferInsert<TSchema[K]>] extends [never]\n ? Partial<RowFor<TSchema, K>>\n : InferInsert<TSchema[K]>\n\nexport type SyncClientOptions = {\n baseUrl?: string\n fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n queryClient: QueryClient\n realtime?: boolean\n}\n\nexport type WorkerstackSyncClient<TApp extends AnyWorkerstackApp> = {\n [K in InferTables<TApp>]: TableCollection<\n RowFor<InferSchema<TApp>, K>,\n CreateFor<InferSchema<TApp>, K>,\n Partial<RowFor<InferSchema<TApp>, K>>\n >\n} & {\n files: WorkerstackClient<TApp>['files']\n realtime:\n | { close(): void; subscribe(tables: string[]): Promise<void> }\n | undefined\n}\n\nexport function createSyncClient<TApp extends AnyWorkerstackApp>(\n options: SyncClientOptions,\n): WorkerstackSyncClient<TApp> {\n const api = createClient<TApp>(options)\n const materialized = new Map<string, SyncRealtimeTarget>()\n const realtimeHandles = new Map<string, { close(): void }>()\n const tables = new Map<string, unknown>()\n const realtimeEnabled = options.realtime ?? typeof window !== 'undefined'\n\n const result = new Proxy({} as WorkerstackSyncClient<TApp>, {\n get(_target, property) {\n if (typeof property !== 'string') return undefined\n if (property === 'files') return api.files\n if (property === 'realtime') {\n return realtimeEnabled\n ? {\n close: () => realtimeHandles.forEach((handle) => handle.close()),\n subscribe: async (names: string[]) => {\n for (const name of names) void (result as any)[name]\n },\n }\n : undefined\n }\n if (['then', 'toJSON', 'constructor', '$$typeof'].includes(property))\n return undefined\n const cached = tables.get(property)\n if (cached) return cached\n const collection = createTableCollection({\n tableName: property,\n procedures: (api as any)[property],\n queryClient: options.queryClient,\n })\n tables.set(property, collection)\n materialized.set(property, collection)\n if (realtimeEnabled) {\n realtimeHandles.set(\n property,\n createSyncRealtimeClient({\n api: api as any,\n tables: [property],\n resolve: (table) => materialized.get(table),\n resolveAll: () => materialized.values(),\n }),\n )\n }\n return collection\n },\n })\n return result\n}\n"]}
@@ -0,0 +1,27 @@
1
+ /** Per-key coalescing queue for high-frequency row updates.
2
+ *
3
+ * Cursor-shaped workloads call `collection.update()` many times a second. Sent
4
+ * one-for-one, each call is a request, a database write and a broadcast. This
5
+ * queue holds a single merged slot per row key: while a request is in flight,
6
+ * further updates for that key merge into the slot and go out as one follow-up
7
+ * once it lands. Request frequency settles at `min(update rate, 1/RTT)` without
8
+ * a tuned throttle constant, and the optimistic UI is untouched — only the
9
+ * network is coalesced.
10
+ */
11
+ export type UpdateQueueConfig<TKey, TRow> = {
12
+ /** Sends one merged batch of changes and resolves with the canonical row. */
13
+ send: (key: TKey, changes: Record<string, unknown>) => Promise<TRow>;
14
+ /** Called with the server's row before the batch's waiters resolve. Awaited,
15
+ * so a handler that needs to recover (refetch) finishes first. */
16
+ onResult: (row: TRow) => void | Promise<void>;
17
+ };
18
+ export type UpdateQueue<TKey> = {
19
+ /** Queues changes for `key`. Resolves once a request carrying them lands. */
20
+ enqueue: (key: TKey, changes: Record<string, unknown>) => Promise<void>;
21
+ /** Drops changes still queued for `key` (resolving their waiters) and waits
22
+ * for any in-flight request to land. For operations that supersede pending
23
+ * updates and must not race them — a delete of the same row. */
24
+ settle: (key: TKey) => Promise<void>;
25
+ };
26
+ export declare function createUpdateQueue<TKey, TRow>(config: UpdateQueueConfig<TKey, TRow>): UpdateQueue<TKey>;
27
+ //# sourceMappingURL=update-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-queue.d.ts","sourceRoot":"","sources":["../../src/sync/update-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAcH,MAAM,MAAM,iBAAiB,CAAC,IAAI,EAAE,IAAI,IAAI;IAC1C,6EAA6E;IAC7E,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpE;sEACkE;IAClE,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9C,CAAA;AAED,MAAM,MAAM,WAAW,CAAC,IAAI,IAAI;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACvE;;oEAEgE;IAChE,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC,CAAA;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAC1C,MAAM,EAAE,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,GACpC,WAAW,CAAC,IAAI,CAAC,CAkDnB"}
@@ -0,0 +1,66 @@
1
+ /** Per-key coalescing queue for high-frequency row updates.
2
+ *
3
+ * Cursor-shaped workloads call `collection.update()` many times a second. Sent
4
+ * one-for-one, each call is a request, a database write and a broadcast. This
5
+ * queue holds a single merged slot per row key: while a request is in flight,
6
+ * further updates for that key merge into the slot and go out as one follow-up
7
+ * once it lands. Request frequency settles at `min(update rate, 1/RTT)` without
8
+ * a tuned throttle constant, and the optimistic UI is untouched — only the
9
+ * network is coalesced.
10
+ */
11
+ export function createUpdateQueue(config) {
12
+ const slots = new Map();
13
+ async function drain(key, slot) {
14
+ while (Object.keys(slot.changes).length > 0) {
15
+ // Taking changes and waiters as one snapshot is what guarantees a waiter
16
+ // resolves only after a request that carried its own changes.
17
+ const changes = slot.changes;
18
+ const waiters = slot.waiters;
19
+ slot.changes = {};
20
+ slot.waiters = [];
21
+ try {
22
+ await config.onResult(await config.send(key, changes));
23
+ for (const waiter of waiters)
24
+ waiter.resolve();
25
+ }
26
+ catch (error) {
27
+ // Replaying queued changes on top of a failed base would diverge from
28
+ // the server silently, so the whole key is rolled back instead.
29
+ const queued = slot.waiters;
30
+ slot.changes = {};
31
+ slot.waiters = [];
32
+ for (const waiter of [...waiters, ...queued])
33
+ waiter.reject(error);
34
+ }
35
+ }
36
+ slots.delete(key);
37
+ }
38
+ return {
39
+ enqueue(key, changes) {
40
+ let slot = slots.get(key);
41
+ if (!slot) {
42
+ slot = { changes: {}, waiters: [], done: null };
43
+ slots.set(key, slot);
44
+ }
45
+ Object.assign(slot.changes, changes);
46
+ const promise = new Promise((resolve, reject) => {
47
+ slot.waiters.push({ resolve, reject });
48
+ });
49
+ if (!slot.done)
50
+ slot.done = drain(key, slot);
51
+ return promise;
52
+ },
53
+ settle(key) {
54
+ const slot = slots.get(key);
55
+ if (!slot)
56
+ return Promise.resolve();
57
+ const waiters = slot.waiters;
58
+ slot.changes = {};
59
+ slot.waiters = [];
60
+ for (const waiter of waiters)
61
+ waiter.resolve();
62
+ return slot.done ?? Promise.resolve();
63
+ },
64
+ };
65
+ }
66
+ //# sourceMappingURL=update-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-queue.js","sourceRoot":"","sources":["../../src/sync/update-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA+BH,MAAM,UAAU,iBAAiB,CAC/B,MAAqC;IAErC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAc,CAAA;IAEnC,KAAK,UAAU,KAAK,CAAC,GAAS,EAAE,IAAU;QACxC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,yEAAyE;YACzE,8DAA8D;YAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;YAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;YAC5B,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;YACjB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;YACjB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;gBACtD,KAAK,MAAM,MAAM,IAAI,OAAO;oBAAE,MAAM,CAAC,OAAO,EAAE,CAAA;YAChD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,sEAAsE;gBACtE,gEAAgE;gBAChE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAA;gBAC3B,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;gBACjB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;gBACjB,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC;oBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACpE,CAAC;QACH,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACnB,CAAC;IAED,OAAO;QACL,OAAO,CAAC,GAAG,EAAE,OAAO;YAClB,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YACzB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,IAAI,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;gBAC/C,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACtB,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YACpC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACpD,IAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;YACzC,CAAC,CAAC,CAAA;YACF,IAAI,CAAC,IAAI,CAAC,IAAI;gBAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC5C,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,CAAC,GAAG;YACR,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC3B,IAAI,CAAC,IAAI;gBAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;YAC5B,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;YACjB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;YACjB,KAAK,MAAM,MAAM,IAAI,OAAO;gBAAE,MAAM,CAAC,OAAO,EAAE,CAAA;YAC9C,OAAO,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAA;QACvC,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["/** Per-key coalescing queue for high-frequency row updates.\n *\n * Cursor-shaped workloads call `collection.update()` many times a second. Sent\n * one-for-one, each call is a request, a database write and a broadcast. This\n * queue holds a single merged slot per row key: while a request is in flight,\n * further updates for that key merge into the slot and go out as one follow-up\n * once it lands. Request frequency settles at `min(update rate, 1/RTT)` without\n * a tuned throttle constant, and the optimistic UI is untouched — only the\n * network is coalesced.\n */\n\ntype Waiter = {\n resolve: () => void\n reject: (error: unknown) => void\n}\n\ntype Slot = {\n changes: Record<string, unknown>\n waiters: Waiter[]\n /** Promise of the running drain loop; never rejects. */\n done: Promise<void> | null\n}\n\nexport type UpdateQueueConfig<TKey, TRow> = {\n /** Sends one merged batch of changes and resolves with the canonical row. */\n send: (key: TKey, changes: Record<string, unknown>) => Promise<TRow>\n /** Called with the server's row before the batch's waiters resolve. Awaited,\n * so a handler that needs to recover (refetch) finishes first. */\n onResult: (row: TRow) => void | Promise<void>\n}\n\nexport type UpdateQueue<TKey> = {\n /** Queues changes for `key`. Resolves once a request carrying them lands. */\n enqueue: (key: TKey, changes: Record<string, unknown>) => Promise<void>\n /** Drops changes still queued for `key` (resolving their waiters) and waits\n * for any in-flight request to land. For operations that supersede pending\n * updates and must not race them — a delete of the same row. */\n settle: (key: TKey) => Promise<void>\n}\n\nexport function createUpdateQueue<TKey, TRow>(\n config: UpdateQueueConfig<TKey, TRow>,\n): UpdateQueue<TKey> {\n const slots = new Map<TKey, Slot>()\n\n async function drain(key: TKey, slot: Slot) {\n while (Object.keys(slot.changes).length > 0) {\n // Taking changes and waiters as one snapshot is what guarantees a waiter\n // resolves only after a request that carried its own changes.\n const changes = slot.changes\n const waiters = slot.waiters\n slot.changes = {}\n slot.waiters = []\n try {\n await config.onResult(await config.send(key, changes))\n for (const waiter of waiters) waiter.resolve()\n } catch (error) {\n // Replaying queued changes on top of a failed base would diverge from\n // the server silently, so the whole key is rolled back instead.\n const queued = slot.waiters\n slot.changes = {}\n slot.waiters = []\n for (const waiter of [...waiters, ...queued]) waiter.reject(error)\n }\n }\n slots.delete(key)\n }\n\n return {\n enqueue(key, changes) {\n let slot = slots.get(key)\n if (!slot) {\n slot = { changes: {}, waiters: [], done: null }\n slots.set(key, slot)\n }\n Object.assign(slot.changes, changes)\n const promise = new Promise<void>((resolve, reject) => {\n slot!.waiters.push({ resolve, reject })\n })\n if (!slot.done) slot.done = drain(key, slot)\n return promise\n },\n settle(key) {\n const slot = slots.get(key)\n if (!slot) return Promise.resolve()\n const waiters = slot.waiters\n slot.changes = {}\n slot.waiters = []\n for (const waiter of waiters) waiter.resolve()\n return slot.done ?? Promise.resolve()\n },\n }\n}\n"]}
@@ -0,0 +1,85 @@
1
+ export type TestUser = {
2
+ id: string;
3
+ email: string;
4
+ name: string;
5
+ emailVerified?: boolean;
6
+ image?: string | null;
7
+ createdAt?: string | Date;
8
+ updatedAt?: string | Date;
9
+ };
10
+ export type TestIdentity = {
11
+ user: TestUser;
12
+ headers: Headers;
13
+ };
14
+ export declare class TestAuthError extends Error {
15
+ readonly status: number;
16
+ readonly body: unknown;
17
+ constructor(status: number, body: unknown);
18
+ }
19
+ export type SignUpEmailInput = {
20
+ email: string;
21
+ name: string;
22
+ password?: string;
23
+ };
24
+ export type SignInEmailInput = {
25
+ email: string;
26
+ password?: string;
27
+ };
28
+ export type TestSession = {
29
+ user: TestUser;
30
+ session: {
31
+ activeOrganizationId?: string | null;
32
+ } | null;
33
+ };
34
+ export type TestAuth = {
35
+ signUpEmail(input: SignUpEmailInput): Promise<TestIdentity>;
36
+ signInEmail(input: SignInEmailInput): Promise<TestIdentity>;
37
+ getSession(identity: TestIdentity): Promise<TestSession | null>;
38
+ signOut(identity: TestIdentity): Promise<void>;
39
+ verifyEmail(identity: TestIdentity): Promise<TestIdentity>;
40
+ mockSession<TUser extends TestUser>(user: TUser, session?: {
41
+ activeOrganizationId?: string | null;
42
+ } | null): TestIdentity & {
43
+ user: TUser;
44
+ };
45
+ };
46
+ export type AuthSessionResolverLike = {
47
+ api: {
48
+ getSession: (opts: {
49
+ headers: Headers;
50
+ }) => Promise<unknown>;
51
+ };
52
+ };
53
+ export type WorkerstackAppLike = {
54
+ auth: unknown;
55
+ };
56
+ export type TestSessionRegistry = {
57
+ readonly resolver: AuthSessionResolver;
58
+ mocked(headers: Headers): TestSession | undefined;
59
+ mock<TUser extends TestUser>(user: TUser, session?: {
60
+ activeOrganizationId?: string | null;
61
+ } | null): TestIdentity & {
62
+ user: TUser;
63
+ };
64
+ };
65
+ export declare function createTestSessionRegistry(): TestSessionRegistry;
66
+ export declare function mockAuthSession<TUser extends {
67
+ id: string;
68
+ email: string;
69
+ name?: string;
70
+ role?: string;
71
+ }>(app: WorkerstackAppLike, resolver: (opts: {
72
+ headers: Headers;
73
+ }) => Promise<{
74
+ user: TUser;
75
+ session?: {
76
+ activeOrganizationId?: string | null;
77
+ } | null;
78
+ } | null>): void;
79
+ export declare function createTestAuth(app: {
80
+ handler(request: Request): Promise<Response>;
81
+ auth: unknown;
82
+ }, email: TestEmail, sessions: TestSessionRegistry): TestAuth;
83
+ import type { AuthSessionResolver } from '../access.js';
84
+ import type { TestEmail } from './email.js';
85
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/testing/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ export class TestAuthError extends Error {
2
+ status;
3
+ body;
4
+ constructor(status, body) {
5
+ super(`Better Auth request failed (${status})`);
6
+ this.name = 'TestAuthError';
7
+ this.status = status;
8
+ this.body = body;
9
+ }
10
+ }
11
+ const TEST_SESSION_HEADER = 'x-workerstack-test-session';
12
+ export function createTestSessionRegistry() {
13
+ const sessions = new Map();
14
+ return {
15
+ resolver: {
16
+ api: {
17
+ async getSession({ headers }) {
18
+ const token = headers.get(TEST_SESSION_HEADER);
19
+ if (token)
20
+ return sessions.get(token) ?? null;
21
+ return null;
22
+ },
23
+ },
24
+ },
25
+ mocked(headers) {
26
+ const token = headers.get(TEST_SESSION_HEADER);
27
+ return token ? sessions.get(token) : undefined;
28
+ },
29
+ mock(user, session = null) {
30
+ const token = crypto.randomUUID();
31
+ sessions.set(token, { user, session });
32
+ return {
33
+ user,
34
+ headers: new Headers({ [TEST_SESSION_HEADER]: token }),
35
+ };
36
+ },
37
+ };
38
+ }
39
+ export function mockAuthSession(app, resolver) {
40
+ const auth = app.auth;
41
+ if (auth?.api) {
42
+ auth.api.getSession = resolver;
43
+ }
44
+ }
45
+ function cookieHeaders(response) {
46
+ const values = response.headers.getSetCookie();
47
+ const cookie = values
48
+ .map((value) => value.split(';', 1)[0])
49
+ .filter((value) => Boolean(value))
50
+ .join('; ');
51
+ return new Headers(cookie ? { cookie } : undefined);
52
+ }
53
+ export function createTestAuth(app, email, sessions) {
54
+ const request = async (path, init = {}, headers) => {
55
+ const mergedHeaders = new Headers(headers);
56
+ if (init.body !== undefined && !mergedHeaders.has('content-type')) {
57
+ mergedHeaders.set('content-type', 'application/json');
58
+ }
59
+ return app.handler(new Request(new URL(path, 'http://workerstack.test'), {
60
+ ...init,
61
+ headers: mergedHeaders,
62
+ }));
63
+ };
64
+ const identityFromResponse = async (response) => {
65
+ const body = await response.json().catch(() => null);
66
+ if (!response.ok)
67
+ throw new TestAuthError(response.status, body);
68
+ const user = body?.user;
69
+ if (!user)
70
+ throw new TestAuthError(response.status, body);
71
+ return { user, headers: cookieHeaders(response) };
72
+ };
73
+ const currentSession = async (identity) => {
74
+ const mocked = sessions.mocked(identity.headers);
75
+ if (mocked)
76
+ return mocked;
77
+ const auth = app.auth;
78
+ const result = await auth.api.getSession({ headers: identity.headers });
79
+ if (!result || typeof result !== 'object' || !('user' in result)) {
80
+ return null;
81
+ }
82
+ const value = result;
83
+ return value.user
84
+ ? { user: value.user, session: value.session ?? null }
85
+ : null;
86
+ };
87
+ return {
88
+ async signUpEmail(input) {
89
+ return identityFromResponse(await request('/api/auth/sign-up/email', {
90
+ method: 'POST',
91
+ body: JSON.stringify({
92
+ email: input.email,
93
+ name: input.name,
94
+ password: input.password ?? 'password-123',
95
+ }),
96
+ }));
97
+ },
98
+ async signInEmail(input) {
99
+ return identityFromResponse(await request('/api/auth/sign-in/email', {
100
+ method: 'POST',
101
+ body: JSON.stringify({
102
+ email: input.email,
103
+ password: input.password ?? 'password-123',
104
+ }),
105
+ }));
106
+ },
107
+ async getSession(identity) {
108
+ return currentSession(identity);
109
+ },
110
+ async signOut(identity) {
111
+ const response = await request('/api/auth/sign-out', { method: 'POST' }, identity.headers);
112
+ if (!response.ok) {
113
+ throw new TestAuthError(response.status, await response.json().catch(() => null));
114
+ }
115
+ },
116
+ async verifyEmail(identity) {
117
+ const message = [...email.sent]
118
+ .reverse()
119
+ .find((candidate) => candidate.to.includes(identity.user.email));
120
+ const content = `${message?.text ?? ''}\n${message?.html ?? ''}`;
121
+ const url = content.match(/https?:\/\/[^\s<>"']+\/api\/auth\/verify-email\?[^\s<>"']+/)?.[0];
122
+ if (!url) {
123
+ throw new Error(`[workerstack] no verification link captured for ${identity.user.email}`);
124
+ }
125
+ const response = await request(url, {}, identity.headers);
126
+ if (response.status >= 400) {
127
+ throw new TestAuthError(response.status, await response.text());
128
+ }
129
+ const user = (await currentSession(identity))?.user;
130
+ if (!user) {
131
+ throw new Error('[workerstack] verified identity has no active session');
132
+ }
133
+ return { user, headers: identity.headers };
134
+ },
135
+ mockSession(user, session) {
136
+ return sessions.mock(user, session);
137
+ },
138
+ };
139
+ }
140
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/testing/auth.ts"],"names":[],"mappings":"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","sourcesContent":["export type TestUser = {\n id: string\n email: string\n name: string\n emailVerified?: boolean\n image?: string | null\n createdAt?: string | Date\n updatedAt?: string | Date\n}\n\nexport type TestIdentity = {\n user: TestUser\n headers: Headers\n}\n\nexport class TestAuthError extends Error {\n readonly status: number\n readonly body: unknown\n\n constructor(status: number, body: unknown) {\n super(`Better Auth request failed (${status})`)\n this.name = 'TestAuthError'\n this.status = status\n this.body = body\n }\n}\n\nexport type SignUpEmailInput = {\n email: string\n name: string\n password?: string\n}\n\nexport type SignInEmailInput = {\n email: string\n password?: string\n}\n\nexport type TestSession = {\n user: TestUser\n session: { activeOrganizationId?: string | null } | null\n}\n\nexport type TestAuth = {\n signUpEmail(input: SignUpEmailInput): Promise<TestIdentity>\n signInEmail(input: SignInEmailInput): Promise<TestIdentity>\n getSession(identity: TestIdentity): Promise<TestSession | null>\n signOut(identity: TestIdentity): Promise<void>\n verifyEmail(identity: TestIdentity): Promise<TestIdentity>\n mockSession<TUser extends TestUser>(\n user: TUser,\n session?: { activeOrganizationId?: string | null } | null,\n ): TestIdentity & { user: TUser }\n}\n\nexport type AuthSessionResolverLike = {\n api: {\n getSession: (opts: { headers: Headers }) => Promise<unknown>\n }\n}\n\nexport type WorkerstackAppLike = {\n auth: unknown\n}\n\nconst TEST_SESSION_HEADER = 'x-workerstack-test-session'\n\nexport type TestSessionRegistry = {\n readonly resolver: AuthSessionResolver\n mocked(headers: Headers): TestSession | undefined\n mock<TUser extends TestUser>(\n user: TUser,\n session?: { activeOrganizationId?: string | null } | null,\n ): TestIdentity & { user: TUser }\n}\n\nexport function createTestSessionRegistry(): TestSessionRegistry {\n const sessions = new Map<string, TestSession>()\n return {\n resolver: {\n api: {\n async getSession({ headers }) {\n const token = headers.get(TEST_SESSION_HEADER)\n if (token) return sessions.get(token) ?? null\n return null\n },\n },\n },\n mocked(headers) {\n const token = headers.get(TEST_SESSION_HEADER)\n return token ? sessions.get(token) : undefined\n },\n mock(user, session = null) {\n const token = crypto.randomUUID()\n sessions.set(token, { user, session })\n return {\n user,\n headers: new Headers({ [TEST_SESSION_HEADER]: token }),\n }\n },\n }\n}\n\nexport function mockAuthSession<\n TUser extends { id: string; email: string; name?: string; role?: string },\n>(\n app: WorkerstackAppLike,\n resolver: (opts: { headers: Headers }) => Promise<{\n user: TUser\n session?: { activeOrganizationId?: string | null } | null\n } | null>,\n): void {\n const auth = app.auth as unknown as AuthSessionResolverLike\n if (auth?.api) {\n auth.api.getSession = resolver as unknown as (opts: {\n headers: Headers\n }) => Promise<unknown>\n }\n}\n\nfunction cookieHeaders(response: Response): Headers {\n const values = response.headers.getSetCookie()\n const cookie = values\n .map((value) => value.split(';', 1)[0])\n .filter((value): value is string => Boolean(value))\n .join('; ')\n return new Headers(cookie ? { cookie } : undefined)\n}\n\nexport function createTestAuth(\n app: {\n handler(request: Request): Promise<Response>\n auth: unknown\n },\n email: TestEmail,\n sessions: TestSessionRegistry,\n): TestAuth {\n const request = async (\n path: string,\n init: RequestInit = {},\n headers?: Headers,\n ) => {\n const mergedHeaders = new Headers(headers)\n if (init.body !== undefined && !mergedHeaders.has('content-type')) {\n mergedHeaders.set('content-type', 'application/json')\n }\n return app.handler(\n new Request(new URL(path, 'http://workerstack.test'), {\n ...init,\n headers: mergedHeaders,\n }),\n )\n }\n\n const identityFromResponse = async (response: Response) => {\n const body = await response.json().catch(() => null)\n if (!response.ok) throw new TestAuthError(response.status, body)\n const user = (body as { user?: TestUser } | null)?.user\n if (!user) throw new TestAuthError(response.status, body)\n return { user, headers: cookieHeaders(response) }\n }\n\n const currentSession = async (\n identity: TestIdentity,\n ): Promise<TestSession | null> => {\n const mocked = sessions.mocked(identity.headers)\n if (mocked) return mocked\n const auth = app.auth as {\n api: {\n getSession(options: { headers: Headers }): Promise<unknown>\n }\n }\n const result = await auth.api.getSession({ headers: identity.headers })\n if (!result || typeof result !== 'object' || !('user' in result)) {\n return null\n }\n const value = result as {\n user?: TestUser\n session?: TestSession['session']\n }\n return value.user\n ? { user: value.user, session: value.session ?? null }\n : null\n }\n\n return {\n async signUpEmail(input) {\n return identityFromResponse(\n await request('/api/auth/sign-up/email', {\n method: 'POST',\n body: JSON.stringify({\n email: input.email,\n name: input.name,\n password: input.password ?? 'password-123',\n }),\n }),\n )\n },\n async signInEmail(input) {\n return identityFromResponse(\n await request('/api/auth/sign-in/email', {\n method: 'POST',\n body: JSON.stringify({\n email: input.email,\n password: input.password ?? 'password-123',\n }),\n }),\n )\n },\n async getSession(identity) {\n return currentSession(identity)\n },\n async signOut(identity) {\n const response = await request(\n '/api/auth/sign-out',\n { method: 'POST' },\n identity.headers,\n )\n if (!response.ok) {\n throw new TestAuthError(\n response.status,\n await response.json().catch(() => null),\n )\n }\n },\n async verifyEmail(identity) {\n const message = [...email.sent]\n .reverse()\n .find((candidate) => candidate.to.includes(identity.user.email))\n const content = `${message?.text ?? ''}\\n${message?.html ?? ''}`\n const url = content.match(\n /https?:\\/\\/[^\\s<>\"']+\\/api\\/auth\\/verify-email\\?[^\\s<>\"']+/,\n )?.[0]\n if (!url) {\n throw new Error(\n `[workerstack] no verification link captured for ${identity.user.email}`,\n )\n }\n const response = await request(url, {}, identity.headers)\n if (response.status >= 400) {\n throw new TestAuthError(response.status, await response.text())\n }\n const user = (await currentSession(identity))?.user\n if (!user) {\n throw new Error('[workerstack] verified identity has no active session')\n }\n return { user, headers: identity.headers }\n },\n mockSession(user, session) {\n return sessions.mock(user, session)\n },\n }\n}\nimport type { AuthSessionResolver } from '../access'\nimport type { TestEmail } from './email'\n"]}
@@ -0,0 +1,6 @@
1
+ import type { AnyWorkerstackApp, WorkerstackClient } from '../client/rpc-client.js';
2
+ import type { TestIdentity } from './auth.js';
3
+ export declare function testClient<TApp extends AnyWorkerstackApp>(app: TApp & {
4
+ handler(request: Request): Promise<Response>;
5
+ }, identity?: TestIdentity): WorkerstackClient<TApp>;
6
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/testing/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAI1C,wBAAgB,UAAU,CAAC,IAAI,SAAS,iBAAiB,EACvD,GAAG,EAAE,IAAI,GAAG;IAAE,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAAE,EAC5D,QAAQ,CAAC,EAAE,YAAY,GACtB,iBAAiB,CAAC,IAAI,CAAC,CAMzB"}
@@ -0,0 +1,9 @@
1
+ import { createClient } from '../client/rpc-client.js';
2
+ export function testClient(app, identity) {
3
+ return createClient({
4
+ baseUrl: 'http://workerstack.test/api',
5
+ fetch: (input, init) => app.handler(new Request(input, init)),
6
+ headers: identity?.headers,
7
+ });
8
+ }
9
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/testing/client.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD,MAAM,UAAU,UAAU,CACxB,GAA4D,EAC5D,QAAuB;IAEvB,OAAO,YAAY,CAAO;QACxB,OAAO,EAAE,6BAA6B;QACtC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7D,OAAO,EAAE,QAAQ,EAAE,OAAO;KAC3B,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type { AnyWorkerstackApp, WorkerstackClient } from '../client/rpc-client'\nimport type { TestIdentity } from './auth'\n\nimport { createClient } from '../client/rpc-client'\n\nexport function testClient<TApp extends AnyWorkerstackApp>(\n app: TApp & { handler(request: Request): Promise<Response> },\n identity?: TestIdentity,\n): WorkerstackClient<TApp> {\n return createClient<TApp>({\n baseUrl: 'http://workerstack.test/api',\n fetch: (input, init) => app.handler(new Request(input, init)),\n headers: identity?.headers,\n })\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { DatabaseAdapter, TestDatabaseStrategy, TestDatabaseTarget } from '../database/adapter.js';
2
+ export declare function createTestDatabaseTarget(adapter: DatabaseAdapter, options: {
3
+ mode: 'memory' | 'temporary';
4
+ strategy?: TestDatabaseStrategy;
5
+ }): Promise<TestDatabaseTarget>;
6
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/testing/database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,qBAAqB,CAAA;AAE5B,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE;IACP,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAA;IAC5B,QAAQ,CAAC,EAAE,oBAAoB,CAAA;CAChC,GACA,OAAO,CAAC,kBAAkB,CAAC,CAQ7B"}
@@ -0,0 +1,8 @@
1
+ export async function createTestDatabaseTarget(adapter, options) {
2
+ const strategy = options.strategy ?? adapter.testing;
3
+ if (!strategy) {
4
+ throw new Error(`[workerstack] ${adapter.driver} requires an explicit test database strategy`);
5
+ }
6
+ return strategy.createTarget({ mode: options.mode });
7
+ }
8
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/testing/database.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAwB,EACxB,OAGC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAA;IACpD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,iBAAiB,OAAO,CAAC,MAAM,8CAA8C,CAC9E,CAAA;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;AACtD,CAAC","sourcesContent":["import type {\n DatabaseAdapter,\n TestDatabaseStrategy,\n TestDatabaseTarget,\n} from '../database/adapter'\n\nexport async function createTestDatabaseTarget(\n adapter: DatabaseAdapter,\n options: {\n mode: 'memory' | 'temporary'\n strategy?: TestDatabaseStrategy\n },\n): Promise<TestDatabaseTarget> {\n const strategy = options.strategy ?? adapter.testing\n if (!strategy) {\n throw new Error(\n `[workerstack] ${adapter.driver} requires an explicit test database strategy`,\n )\n }\n return strategy.createTarget({ mode: options.mode })\n}\n"]}