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,255 @@
1
+ import { hashKey, notifyManager } from '@tanstack/query-core';
2
+ import { createFlushScheduler } from './realtime-flush.js';
3
+ import { openRealtimeStream } from './realtime-stream.js';
4
+ function tableQueryKey(api, table) {
5
+ return api[table]?.key?.({ type: 'query' }) ?? [[table], { type: 'query' }];
6
+ }
7
+ function detailQueryKey(api, table, id) {
8
+ return (api[table]?.get?.queryKey?.({ input: { id } }) ?? [
9
+ [table, 'get'],
10
+ { type: 'query', input: { id } },
11
+ ]);
12
+ }
13
+ function listQueryKey(api, table) {
14
+ return (api[table]?.list?.key?.({ type: 'query' }) ?? [
15
+ [table, 'list'],
16
+ { type: 'query' },
17
+ ]);
18
+ }
19
+ function isListSnapshot(value) {
20
+ return (typeof value === 'object' &&
21
+ value !== null &&
22
+ Array.isArray(value.items));
23
+ }
24
+ /** Dates arrive as Dates over the wire, so identity is not enough. */
25
+ function sameValue(a, b) {
26
+ if (a instanceof Date && b instanceof Date)
27
+ return a.getTime() === b.getTime();
28
+ return a === b;
29
+ }
30
+ /**
31
+ * Workerstack's filter contract is narrow by design — a scalar is `=`, an
32
+ * array is `IN`, and `null` is `IS NULL` — so membership is decidable against
33
+ * a single record without asking the server.
34
+ */
35
+ function matchesFilters(record, filters) {
36
+ for (const [column, expected] of Object.entries(filters ?? {})) {
37
+ if (expected === undefined)
38
+ continue;
39
+ const actual = record[column];
40
+ if (expected === null) {
41
+ if (actual !== null && actual !== undefined)
42
+ return false;
43
+ continue;
44
+ }
45
+ if (Array.isArray(expected)) {
46
+ if (!expected.some((value) => sameValue(value, actual)))
47
+ return false;
48
+ continue;
49
+ }
50
+ if (!sameValue(expected, actual))
51
+ return false;
52
+ }
53
+ return true;
54
+ }
55
+ function compareValues(a, b) {
56
+ if (a instanceof Date && b instanceof Date)
57
+ return a.getTime() - b.getTime();
58
+ if (typeof a === 'number' && typeof b === 'number')
59
+ return a - b;
60
+ if (typeof a === 'boolean' && typeof b === 'boolean')
61
+ return Number(a) - Number(b);
62
+ return String(a).localeCompare(String(b));
63
+ }
64
+ /**
65
+ * Where the row goes in an already-sorted page.
66
+ *
67
+ * Ties are common rather than exotic: a `timestamp` column stores seconds, so
68
+ * rows written in the same second share a sort value. SQL does not define an
69
+ * order within a tie, so the new row is placed as the most recent of its
70
+ * equals — first under `desc`, last under `asc` — which is where a reader
71
+ * expects a just-written row to appear.
72
+ */
73
+ function insertionIndex(items, record, sort, order) {
74
+ const value = record[sort];
75
+ const index = items.findIndex((item) => {
76
+ const comparison = compareValues(value, item[sort]);
77
+ return order === 'desc' ? comparison >= 0 : comparison < 0;
78
+ });
79
+ return index === -1 ? items.length : index;
80
+ }
81
+ /**
82
+ * A cached page can only absorb an insert when it already holds every row the
83
+ * query would return. A partial page cannot: the new row may belong to another
84
+ * page, and inserting would shift the boundary the server drew.
85
+ */
86
+ function holdsCompleteResult(snapshot) {
87
+ return (snapshot.hasMore === false &&
88
+ !snapshot.cursor &&
89
+ (snapshot.offset === undefined || snapshot.offset === 0));
90
+ }
91
+ function readInput(queryKey) {
92
+ const meta = queryKey[1];
93
+ const input = meta?.input;
94
+ return typeof input === 'object' && input !== null
95
+ ? input
96
+ : {};
97
+ }
98
+ export function syncRealtime(options) {
99
+ const scheduler = createFlushScheduler(options.notifyScheduler ?? 'frame', options.clock);
100
+ /** Changes waiting for the next flush, in arrival order. */
101
+ const buffered = [];
102
+ /**
103
+ * Query keys the flush must invalidate, deduplicated by their hash. Keyed
104
+ * this way rather than by table so a list that cannot absorb a change still
105
+ * invalidates only itself, while a burst against one key collapses to one
106
+ * call.
107
+ */
108
+ const stale = new Map();
109
+ const invalidateAll = async () => {
110
+ await Promise.all(options.tables.map((table) => options.queryClient.invalidateQueries({
111
+ queryKey: tableQueryKey(options.api, table),
112
+ })));
113
+ };
114
+ /**
115
+ * Write the change into every cached list for the table, marking the table
116
+ * stale for any list whose membership or ordering cannot be settled locally.
117
+ * A list is only patched when the answer is certain.
118
+ */
119
+ const patchLists = (change) => {
120
+ const prefix = listQueryKey(options.api, change.table);
121
+ const queries = options.queryClient
122
+ .getQueryCache()
123
+ .findAll({ queryKey: prefix });
124
+ const id = change.record['id'];
125
+ for (const query of queries) {
126
+ const snapshot = query.state.data;
127
+ if (!isListSnapshot(snapshot))
128
+ continue;
129
+ const queryKey = query.queryKey;
130
+ const items = snapshot.items;
131
+ const present = items.some((item) => sameValue(item['id'], id));
132
+ if (change.action === 'delete') {
133
+ if (!present)
134
+ continue;
135
+ options.queryClient.setQueryData(queryKey, {
136
+ ...snapshot,
137
+ items: items.filter((item) => !sameValue(item['id'], id)),
138
+ ...(typeof snapshot.total === 'number'
139
+ ? { total: Math.max(0, snapshot.total - 1) }
140
+ : {}),
141
+ });
142
+ continue;
143
+ }
144
+ const input = readInput(queryKey);
145
+ const belongs = matchesFilters(change.record, input['filters']);
146
+ // An update can move a row out of a filtered list.
147
+ if (!belongs) {
148
+ if (!present)
149
+ continue;
150
+ options.queryClient.setQueryData(queryKey, {
151
+ ...snapshot,
152
+ items: items.filter((item) => !sameValue(item['id'], id)),
153
+ ...(typeof snapshot.total === 'number'
154
+ ? { total: Math.max(0, snapshot.total - 1) }
155
+ : {}),
156
+ });
157
+ continue;
158
+ }
159
+ // Already here: replace in place and keep the row where it is.
160
+ if (present) {
161
+ options.queryClient.setQueryData(queryKey, {
162
+ ...snapshot,
163
+ items: items.map((item) => sameValue(item['id'], id) ? change.record : item),
164
+ });
165
+ continue;
166
+ }
167
+ // The row joins this list. Only safe on a page that holds everything,
168
+ // and only when `q` is absent — a text match needs the server's
169
+ // searchable columns to evaluate.
170
+ const searched = snapshot.q ?? input['q'];
171
+ const sort = snapshot.sort ?? input['sort'];
172
+ const order = snapshot.order ?? input['order'];
173
+ if (searched || !sort || !order || !holdsCompleteResult(snapshot)) {
174
+ stale.set(hashKey(queryKey), queryKey);
175
+ continue;
176
+ }
177
+ const next = items.slice();
178
+ next.splice(insertionIndex(items, change.record, sort, order), 0, change.record);
179
+ options.queryClient.setQueryData(queryKey, {
180
+ ...snapshot,
181
+ items: next,
182
+ ...(typeof snapshot.total === 'number'
183
+ ? { total: snapshot.total + 1 }
184
+ : {}),
185
+ });
186
+ }
187
+ };
188
+ const applyOne = (change) => {
189
+ // The runtime shape is the same either way; the generic only narrows what
190
+ // callers see.
191
+ options.onChange?.(change);
192
+ const id = change.record['id'];
193
+ if (id !== undefined) {
194
+ const queryKey = detailQueryKey(options.api, change.table, id);
195
+ if (change.action === 'delete')
196
+ options.queryClient.removeQueries({ queryKey });
197
+ else
198
+ options.queryClient.setQueryData(queryKey, change.record);
199
+ }
200
+ if (options.apply === 'patch') {
201
+ patchLists(change);
202
+ return;
203
+ }
204
+ const tableKey = tableQueryKey(options.api, change.table);
205
+ stale.set(hashKey(tableKey), tableKey);
206
+ };
207
+ /**
208
+ * One pass over the buffer, then one invalidation per stale table. The
209
+ * second part is the point: a burst of changes to a table used to issue one
210
+ * `invalidateQueries` each, and now issues one in total.
211
+ */
212
+ const flush = () => {
213
+ if (buffered.length === 0)
214
+ return;
215
+ const batch = buffered.splice(0, buffered.length);
216
+ notifyManager.batch(() => {
217
+ for (const change of batch)
218
+ applyOne(change);
219
+ for (const queryKey of stale.values())
220
+ void options.queryClient.invalidateQueries({ queryKey });
221
+ stale.clear();
222
+ });
223
+ };
224
+ const stream = openRealtimeStream({
225
+ subscribe: ({ signal, lastEventId }) => options.api.realtime.changes.call({ tables: options.tables }, { signal, lastEventId }),
226
+ onChange: (change) => {
227
+ buffered.push(change);
228
+ scheduler.schedule(flush);
229
+ },
230
+ onReconnect: async () => {
231
+ // A refetch of everything supersedes whatever was still buffered.
232
+ scheduler.cancel();
233
+ buffered.length = 0;
234
+ stale.clear();
235
+ await invalidateAll();
236
+ await options.onReconnect?.();
237
+ },
238
+ onError: options.onError,
239
+ onRetry: options.onRetry,
240
+ signal: options.signal ?? new AbortController().signal,
241
+ retryMs: options.retryMs,
242
+ maxRetryMs: options.maxRetryMs,
243
+ clock: options.clock,
244
+ });
245
+ return {
246
+ close: () => {
247
+ stream.close();
248
+ // Anything already delivered is still owed to the cache.
249
+ scheduler.cancel();
250
+ flush();
251
+ },
252
+ done: stream.done,
253
+ };
254
+ }
255
+ //# sourceMappingURL=realtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../src/query/realtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { QueryClient, QueryKey } from '@tanstack/query-core'\n\nimport { hashKey, notifyManager } from '@tanstack/query-core'\n\nimport type {\n AnyWorkerstackApp,\n InferSchema,\n InferSelect,\n InferTables,\n} from './infer'\nimport type { NotifyScheduler, RealtimeClock } from './realtime-flush'\nimport type {\n RealtimeAction,\n RealtimeChange,\n RealtimeProcedure,\n RealtimeSyncHandle,\n} from './realtime-stream'\n\nimport { createFlushScheduler } from './realtime-flush'\nimport { openRealtimeStream } from './realtime-stream'\n\n// The event types live with the stream that produces them; re-exported here so\n// the package's public surface is unchanged.\nexport type { NotifyScheduler, RealtimeClock } from './realtime-flush'\nexport type {\n RealtimeAction,\n RealtimeChange,\n RealtimeEvent,\n RealtimeHeartbeat,\n RealtimeProcedure,\n RealtimeSyncHandle,\n} from './realtime-stream'\n\n/**\n * The change for one table, with `record` typed as that table's row.\n *\n * Passing the app type to `syncRealtime` turns `tables` into a checked list of\n * exposed table names and `onChange` into a union discriminated by `table`, so\n * `change.table === 'todos'` narrows `change.record` to the todo row:\n *\n * ```ts\n * syncRealtime<App>({\n * api,\n * queryClient,\n * tables: ['todos'],\n * onChange: (change) => {\n * if (change.table === 'todos') console.log(change.record.done)\n * },\n * })\n * ```\n */\nexport type RealtimeChangeFor<\n TApp extends AnyWorkerstackApp,\n TTable extends InferTables<TApp>,\n> = {\n table: TTable\n action: RealtimeAction\n record: InferSelect<InferSchema<TApp>[TTable]>\n operationId?: string\n}\n\n/**\n * Untyped when no app type is supplied, so existing callers — and consumers\n * that only have a structural client — keep the loose `RealtimeChange`.\n */\nexport type RealtimeChangeOf<TApp, TTable extends string> = [TApp] extends [\n never,\n]\n ? RealtimeChange\n : TApp extends AnyWorkerstackApp\n ? TTable extends InferTables<TApp>\n ? RealtimeChangeFor<TApp, TTable>\n : RealtimeChange\n : RealtimeChange\n\n/** Every exposed table name, or any string when no app type is supplied. */\nexport type RealtimeTableName<TApp> = [TApp] extends [never]\n ? string\n : TApp extends AnyWorkerstackApp\n ? InferTables<TApp>\n : string\n\nexport type RealtimeQueryApi = {\n realtime: { changes: RealtimeProcedure }\n [table: string]: any\n}\n\n/**\n * How a change reaches the cache.\n *\n * `invalidate` (default) marks the table's queries stale and lets TanStack\n * Query refetch them — always correct, one request per change.\n *\n * `patch` writes the change into cached list results instead, so a write costs\n * no extra request. The event carries the action and the full row, and\n * Workerstack's list contract is narrow enough to decide membership locally:\n * filters are `=`, `IN`, or `IS NULL`, and ordering is a single column. Any\n * list where that is not decidable — a text search, or a page that is not the\n * complete result — falls back to invalidation.\n */\nexport type RealtimeApplyStrategy = 'invalidate' | 'patch'\n\nexport type RealtimeSyncOptions<\n TApp = never,\n TTable extends string = RealtimeTableName<TApp>,\n> = {\n api: RealtimeQueryApi\n queryClient: QueryClient\n tables: TTable[]\n signal?: AbortSignal\n /** Defaults to `'invalidate'`. */\n apply?: RealtimeApplyStrategy\n /**\n * When buffered changes reach the cache. Defaults to `'frame'`, which\n * collapses a burst into one cache write and one invalidation per table.\n * Pass `'sync'` to write as each change arrives, as versions before 0.18 did.\n */\n notifyScheduler?: NotifyScheduler\n /** Timer functions, injectable for tests. */\n clock?: RealtimeClock\n /** Initial reconnect delay before jitter. Defaults to 1 second. */\n retryMs?: number\n /** Maximum reconnect delay before jitter. Defaults to 30 seconds. */\n maxRetryMs?: number\n onChange?: (change: RealtimeChangeOf<TApp, TTable>) => void\n onReconnect?: () => void | Promise<void>\n onError?: (error: unknown) => void\n onRetry?: (retry: { attempt: number; delayMs: number }) => void\n}\n\nfunction tableQueryKey(api: RealtimeQueryApi, table: string): QueryKey {\n return api[table]?.key?.({ type: 'query' }) ?? [[table], { type: 'query' }]\n}\n\nfunction detailQueryKey(\n api: RealtimeQueryApi,\n table: string,\n id: unknown,\n): QueryKey {\n return (\n api[table]?.get?.queryKey?.({ input: { id } }) ?? [\n [table, 'get'],\n { type: 'query', input: { id } },\n ]\n )\n}\n\nfunction listQueryKey(api: RealtimeQueryApi, table: string): QueryKey {\n return (\n api[table]?.list?.key?.({ type: 'query' }) ?? [\n [table, 'list'],\n { type: 'query' },\n ]\n )\n}\n\n/** The shape every generated list endpoint returns, as far as patching cares. */\ntype ListSnapshot = {\n items: Record<string, unknown>[]\n hasMore?: boolean\n total?: number\n offset?: number\n cursor?: string\n q?: string\n sort?: string\n order?: 'asc' | 'desc'\n}\n\nfunction isListSnapshot(value: unknown): value is ListSnapshot {\n return (\n typeof value === 'object' &&\n value !== null &&\n Array.isArray((value as ListSnapshot).items)\n )\n}\n\n/** Dates arrive as Dates over the wire, so identity is not enough. */\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime()\n return a === b\n}\n\n/**\n * Workerstack's filter contract is narrow by design — a scalar is `=`, an\n * array is `IN`, and `null` is `IS NULL` — so membership is decidable against\n * a single record without asking the server.\n */\nfunction matchesFilters(\n record: Record<string, unknown>,\n filters: Record<string, unknown> | undefined,\n): boolean {\n for (const [column, expected] of Object.entries(filters ?? {})) {\n if (expected === undefined) continue\n const actual = record[column]\n if (expected === null) {\n if (actual !== null && actual !== undefined) return false\n continue\n }\n if (Array.isArray(expected)) {\n if (!expected.some((value) => sameValue(value, actual))) return false\n continue\n }\n if (!sameValue(expected, actual)) return false\n }\n return true\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime()\n if (typeof a === 'number' && typeof b === 'number') return a - b\n if (typeof a === 'boolean' && typeof b === 'boolean')\n return Number(a) - Number(b)\n return String(a).localeCompare(String(b))\n}\n\n/**\n * Where the row goes in an already-sorted page.\n *\n * Ties are common rather than exotic: a `timestamp` column stores seconds, so\n * rows written in the same second share a sort value. SQL does not define an\n * order within a tie, so the new row is placed as the most recent of its\n * equals — first under `desc`, last under `asc` — which is where a reader\n * expects a just-written row to appear.\n */\nfunction insertionIndex(\n items: Record<string, unknown>[],\n record: Record<string, unknown>,\n sort: string,\n order: 'asc' | 'desc',\n): number {\n const value = record[sort]\n const index = items.findIndex((item) => {\n const comparison = compareValues(value, item[sort])\n return order === 'desc' ? comparison >= 0 : comparison < 0\n })\n return index === -1 ? items.length : index\n}\n\n/**\n * A cached page can only absorb an insert when it already holds every row the\n * query would return. A partial page cannot: the new row may belong to another\n * page, and inserting would shift the boundary the server drew.\n */\nfunction holdsCompleteResult(snapshot: ListSnapshot): boolean {\n return (\n snapshot.hasMore === false &&\n !snapshot.cursor &&\n (snapshot.offset === undefined || snapshot.offset === 0)\n )\n}\n\nfunction readInput(queryKey: QueryKey): Record<string, unknown> {\n const meta = queryKey[1] as { input?: unknown } | undefined\n const input = meta?.input\n return typeof input === 'object' && input !== null\n ? (input as Record<string, unknown>)\n : {}\n}\n\nexport function syncRealtime<\n TApp = never,\n TTable extends string = RealtimeTableName<TApp>,\n>(options: RealtimeSyncOptions<TApp, TTable>): RealtimeSyncHandle {\n const scheduler = createFlushScheduler(\n options.notifyScheduler ?? 'frame',\n options.clock,\n )\n /** Changes waiting for the next flush, in arrival order. */\n const buffered: RealtimeChange[] = []\n /**\n * Query keys the flush must invalidate, deduplicated by their hash. Keyed\n * this way rather than by table so a list that cannot absorb a change still\n * invalidates only itself, while a burst against one key collapses to one\n * call.\n */\n const stale = new Map<string, QueryKey>()\n\n const invalidateAll = async () => {\n await Promise.all(\n options.tables.map((table) =>\n options.queryClient.invalidateQueries({\n queryKey: tableQueryKey(options.api, table),\n }),\n ),\n )\n }\n\n /**\n * Write the change into every cached list for the table, marking the table\n * stale for any list whose membership or ordering cannot be settled locally.\n * A list is only patched when the answer is certain.\n */\n const patchLists = (change: RealtimeChange) => {\n const prefix = listQueryKey(options.api, change.table)\n const queries = options.queryClient\n .getQueryCache()\n .findAll({ queryKey: prefix })\n const id = change.record['id']\n\n for (const query of queries) {\n const snapshot = query.state.data\n if (!isListSnapshot(snapshot)) continue\n\n const queryKey = query.queryKey\n const items = snapshot.items\n const present = items.some((item) => sameValue(item['id'], id))\n\n if (change.action === 'delete') {\n if (!present) continue\n options.queryClient.setQueryData(queryKey, {\n ...snapshot,\n items: items.filter((item) => !sameValue(item['id'], id)),\n ...(typeof snapshot.total === 'number'\n ? { total: Math.max(0, snapshot.total - 1) }\n : {}),\n })\n continue\n }\n\n const input = readInput(queryKey)\n const belongs = matchesFilters(\n change.record,\n input['filters'] as Record<string, unknown> | undefined,\n )\n\n // An update can move a row out of a filtered list.\n if (!belongs) {\n if (!present) continue\n options.queryClient.setQueryData(queryKey, {\n ...snapshot,\n items: items.filter((item) => !sameValue(item['id'], id)),\n ...(typeof snapshot.total === 'number'\n ? { total: Math.max(0, snapshot.total - 1) }\n : {}),\n })\n continue\n }\n\n // Already here: replace in place and keep the row where it is.\n if (present) {\n options.queryClient.setQueryData(queryKey, {\n ...snapshot,\n items: items.map((item) =>\n sameValue(item['id'], id) ? change.record : item,\n ),\n })\n continue\n }\n\n // The row joins this list. Only safe on a page that holds everything,\n // and only when `q` is absent — a text match needs the server's\n // searchable columns to evaluate.\n const searched = snapshot.q ?? (input['q'] as string | undefined)\n const sort = snapshot.sort ?? (input['sort'] as string | undefined)\n const order =\n snapshot.order ?? (input['order'] as 'asc' | 'desc' | undefined)\n\n if (searched || !sort || !order || !holdsCompleteResult(snapshot)) {\n stale.set(hashKey(queryKey), queryKey)\n continue\n }\n\n const next = items.slice()\n next.splice(\n insertionIndex(items, change.record, sort, order),\n 0,\n change.record,\n )\n options.queryClient.setQueryData(queryKey, {\n ...snapshot,\n items: next,\n ...(typeof snapshot.total === 'number'\n ? { total: snapshot.total + 1 }\n : {}),\n })\n }\n }\n\n const applyOne = (change: RealtimeChange) => {\n // The runtime shape is the same either way; the generic only narrows what\n // callers see.\n options.onChange?.(change as RealtimeChangeOf<TApp, TTable>)\n const id = change.record['id']\n if (id !== undefined) {\n const queryKey = detailQueryKey(options.api, change.table, id)\n if (change.action === 'delete')\n options.queryClient.removeQueries({ queryKey })\n else options.queryClient.setQueryData(queryKey, change.record)\n }\n\n if (options.apply === 'patch') {\n patchLists(change)\n return\n }\n\n const tableKey = tableQueryKey(options.api, change.table)\n stale.set(hashKey(tableKey), tableKey)\n }\n\n /**\n * One pass over the buffer, then one invalidation per stale table. The\n * second part is the point: a burst of changes to a table used to issue one\n * `invalidateQueries` each, and now issues one in total.\n */\n const flush = () => {\n if (buffered.length === 0) return\n const batch = buffered.splice(0, buffered.length)\n notifyManager.batch(() => {\n for (const change of batch) applyOne(change)\n for (const queryKey of stale.values())\n void options.queryClient.invalidateQueries({ queryKey })\n stale.clear()\n })\n }\n\n const stream = openRealtimeStream({\n subscribe: ({ signal, lastEventId }) =>\n options.api.realtime.changes.call(\n { tables: options.tables },\n { signal, lastEventId },\n ),\n onChange: (change) => {\n buffered.push(change)\n scheduler.schedule(flush)\n },\n onReconnect: async () => {\n // A refetch of everything supersedes whatever was still buffered.\n scheduler.cancel()\n buffered.length = 0\n stale.clear()\n await invalidateAll()\n await options.onReconnect?.()\n },\n onError: options.onError,\n onRetry: options.onRetry,\n signal: options.signal ?? new AbortController().signal,\n retryMs: options.retryMs,\n maxRetryMs: options.maxRetryMs,\n clock: options.clock,\n })\n\n return {\n close: () => {\n stream.close()\n // Anything already delivered is still owed to the cache.\n scheduler.cancel()\n flush()\n },\n done: stream.done,\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { type RateLimitStore } from './platform.js';
2
+ export type RateLimitConfig = {
3
+ windowMs?: number;
4
+ max?: number;
5
+ skip?: (req: Request) => boolean;
6
+ };
7
+ export declare function createRateLimiter(config: boolean | RateLimitConfig | undefined, store?: RateLimitStore): (req: Request) => Promise<Response | null>;
8
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AAE5E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CACjC,CAAA;AAkBD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,EAC7C,KAAK,GAAE,cAA6C,GACnD,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAgC5C"}
@@ -0,0 +1,46 @@
1
+ import { createMemoryRateLimitStore } from './platform.js';
2
+ function resolveConfig(config) {
3
+ if (!config)
4
+ return null;
5
+ if (config === true)
6
+ return {};
7
+ return config;
8
+ }
9
+ function clientKey(req) {
10
+ const cloudflare = req.headers.get('cf-connecting-ip');
11
+ if (cloudflare)
12
+ return cloudflare;
13
+ const forwarded = req.headers.get('x-forwarded-for');
14
+ if (forwarded)
15
+ return forwarded.split(',')[0].trim();
16
+ return req.headers.get('x-real-ip') ?? 'local';
17
+ }
18
+ export function createRateLimiter(config, store = createMemoryRateLimitStore()) {
19
+ const resolved = resolveConfig(config);
20
+ if (!resolved) {
21
+ return async (_req) => null;
22
+ }
23
+ const windowMs = resolved.windowMs ?? 60_000;
24
+ const max = resolved.max ?? 100;
25
+ return async (req) => {
26
+ if (resolved.skip?.(req))
27
+ return null;
28
+ const key = `${clientKey(req)}:${new URL(req.url).pathname}`;
29
+ const now = Date.now();
30
+ const { allowed, resetAt } = await store.hit(key, windowMs, max, now);
31
+ if (allowed)
32
+ return null;
33
+ const retryAfter = Math.max(1, Math.ceil((resetAt - now) / 1000));
34
+ return new Response(JSON.stringify({
35
+ error: 'Too many requests',
36
+ code: 'TOO_MANY_REQUESTS',
37
+ }), {
38
+ status: 429,
39
+ headers: {
40
+ 'Content-Type': 'application/json',
41
+ 'Retry-After': String(retryAfter),
42
+ },
43
+ });
44
+ };
45
+ }
46
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAuB,MAAM,YAAY,CAAA;AAQ5E,SAAS,aAAa,CACpB,MAA6C;IAE7C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACxB,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IAC9B,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,SAAS,CAAC,GAAY;IAC7B,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IACtD,IAAI,UAAU;QAAE,OAAO,UAAU,CAAA;IACjC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;IACpD,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;IACrD,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,OAAO,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAA6C,EAC7C,QAAwB,0BAA0B,EAAE;IAEpD,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,KAAK,EAAE,IAAa,EAAE,EAAE,CAAC,IAAI,CAAA;IACtC,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,MAAM,CAAA;IAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,IAAI,GAAG,CAAA;IAE/B,OAAO,KAAK,EAAE,GAAY,EAA4B,EAAE;QACtD,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAErC,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;QACrE,IAAI,OAAO;YAAE,OAAO,IAAI,CAAA;QAExB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;QACjE,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,mBAAmB;SAC1B,CAAC,EACF;YACE,MAAM,EAAE,GAAG;YACX,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC;aAClC;SACF,CACF,CAAA;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["import { createMemoryRateLimitStore, type RateLimitStore } from './platform'\n\nexport type RateLimitConfig = {\n windowMs?: number\n max?: number\n skip?: (req: Request) => boolean\n}\n\nfunction resolveConfig(\n config: boolean | RateLimitConfig | undefined,\n): RateLimitConfig | null {\n if (!config) return null\n if (config === true) return {}\n return config\n}\n\nfunction clientKey(req: Request): string {\n const cloudflare = req.headers.get('cf-connecting-ip')\n if (cloudflare) return cloudflare\n const forwarded = req.headers.get('x-forwarded-for')\n if (forwarded) return forwarded.split(',')[0]!.trim()\n return req.headers.get('x-real-ip') ?? 'local'\n}\n\nexport function createRateLimiter(\n config: boolean | RateLimitConfig | undefined,\n store: RateLimitStore = createMemoryRateLimitStore(),\n): (req: Request) => Promise<Response | null> {\n const resolved = resolveConfig(config)\n if (!resolved) {\n return async (_req: Request) => null\n }\n\n const windowMs = resolved.windowMs ?? 60_000\n const max = resolved.max ?? 100\n\n return async (req: Request): Promise<Response | null> => {\n if (resolved.skip?.(req)) return null\n\n const key = `${clientKey(req)}:${new URL(req.url).pathname}`\n const now = Date.now()\n const { allowed, resetAt } = await store.hit(key, windowMs, max, now)\n if (allowed) return null\n\n const retryAfter = Math.max(1, Math.ceil((resetAt - now) / 1000))\n return new Response(\n JSON.stringify({\n error: 'Too many requests',\n code: 'TOO_MANY_REQUESTS',\n }),\n {\n status: 429,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': String(retryAfter),\n },\n },\n )\n }\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { AnyDb } from './dialect.js';
2
+ import type { ReadinessProbes } from './readiness.js';
3
+ export declare function createReadinessProbes(db: AnyDb, now?: () => number): Pick<ReadinessProbes, 'probeDatabase' | 'countOverdueJobs'>;
4
+ //# sourceMappingURL=readiness-probes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness-probes.d.ts","sourceRoot":"","sources":["../src/readiness-probes.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAOlD,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,KAAK,EACT,GAAG,GAAE,MAAM,MAAiB,GAC3B,IAAI,CAAC,eAAe,EAAE,eAAe,GAAG,kBAAkB,CAAC,CAe7D"}
@@ -0,0 +1,23 @@
1
+ // src/readiness-probes.ts — the internal jobs table doubles as the liveness
2
+ // query: reaching it proves the connection, and a missing-relation error proves
3
+ // the schema was never provisioned.
4
+ import { and, count, eq, lt } from 'drizzle-orm';
5
+ import { workerstackJobs } from './internal-tables.js';
6
+ /** A pending job this far past its `runAt` means nothing is draining the queue. */
7
+ const OVERDUE_MS = 60_000;
8
+ export function createReadinessProbes(db, now = Date.now) {
9
+ const t = workerstackJobs;
10
+ return {
11
+ probeDatabase: async () => {
12
+ await db.select({ id: t.id }).from(t).limit(1);
13
+ },
14
+ countOverdueJobs: async () => {
15
+ const rows = await db
16
+ .select({ overdue: count() })
17
+ .from(t)
18
+ .where(and(eq(t.status, 'pending'), lt(t.runAt, now() - OVERDUE_MS)));
19
+ return Number(rows[0]?.overdue ?? 0);
20
+ },
21
+ };
22
+ }
23
+ //# sourceMappingURL=readiness-probes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness-probes.js","sourceRoot":"","sources":["../src/readiness-probes.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,gFAAgF;AAChF,oCAAoC;AACpC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAKhD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD,mFAAmF;AACnF,MAAM,UAAU,GAAG,MAAM,CAAA;AAEzB,MAAM,UAAU,qBAAqB,CACnC,EAAS,EACT,MAAoB,IAAI,CAAC,GAAG;IAE5B,MAAM,CAAC,GAAG,eAAe,CAAA;IAEzB,OAAO;QACL,aAAa,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAChD,CAAC;QACD,gBAAgB,EAAE,KAAK,IAAI,EAAE;YAC3B,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC;iBAC5B,IAAI,CAAC,CAAC,CAAC;iBACP,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;YACvE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,CAAA;QACtC,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["// src/readiness-probes.ts — the internal jobs table doubles as the liveness\n// query: reaching it proves the connection, and a missing-relation error proves\n// the schema was never provisioned.\nimport { and, count, eq, lt } from 'drizzle-orm'\n\nimport type { AnyDb } from './dialect'\nimport type { ReadinessProbes } from './readiness'\n\nimport { workerstackJobs } from './internal-tables'\n\n/** A pending job this far past its `runAt` means nothing is draining the queue. */\nconst OVERDUE_MS = 60_000\n\nexport function createReadinessProbes(\n db: AnyDb,\n now: () => number = Date.now,\n): Pick<ReadinessProbes, 'probeDatabase' | 'countOverdueJobs'> {\n const t = workerstackJobs\n\n return {\n probeDatabase: async () => {\n await db.select({ id: t.id }).from(t).limit(1)\n },\n countOverdueJobs: async () => {\n const rows = await db\n .select({ overdue: count() })\n .from(t)\n .where(and(eq(t.status, 'pending'), lt(t.runAt, now() - OVERDUE_MS)))\n return Number(rows[0]?.overdue ?? 0)\n },\n }\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import * as v from 'valibot';
2
+ export type ReadinessStatus = 'ok' | 'degraded' | 'error' | 'skipped';
3
+ export type ReadinessCode = 'unreachable' | 'not_provisioned' | 'backlog';
4
+ export type ReadinessCheckName = 'database' | 'schema' | 'background';
5
+ export type ReadinessCheck = {
6
+ name: ReadinessCheckName;
7
+ status: ReadinessStatus;
8
+ code?: ReadinessCode;
9
+ overdue?: number;
10
+ };
11
+ export type ReadinessReport = {
12
+ status: 'ok' | 'degraded' | 'error';
13
+ revision?: string;
14
+ checks: ReadinessCheck[];
15
+ };
16
+ export type ReadinessProbes = {
17
+ /** Deployed commit, when the platform injects `WORKERSTACK_REVISION`. */
18
+ revision?: string;
19
+ queueJobsDeclared: boolean;
20
+ probeDatabase: () => Promise<void>;
21
+ countOverdueJobs: () => Promise<number>;
22
+ };
23
+ export declare const readinessReportSchema: v.StrictObjectSchema<{
24
+ readonly status: v.PicklistSchema<["ok", "degraded", "error"], undefined>;
25
+ readonly revision: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
26
+ readonly checks: v.ArraySchema<v.StrictObjectSchema<{
27
+ readonly name: v.PicklistSchema<["database", "schema", "background"], undefined>;
28
+ readonly status: v.PicklistSchema<["ok", "degraded", "error", "skipped"], undefined>;
29
+ readonly code: v.OptionalSchema<v.PicklistSchema<["unreachable", "not_provisioned", "backlog"], undefined>, undefined>;
30
+ readonly overdue: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
31
+ }, undefined>, undefined>;
32
+ }, undefined>;
33
+ /**
34
+ * A query that fails this way reached the database and found no schema.
35
+ * Drizzle wraps the driver error, so the relation name lives on `cause`.
36
+ */
37
+ export declare function isMissingRelationError(error: unknown): boolean;
38
+ export declare function buildReadinessReport(probes: ReadinessProbes): Promise<ReadinessReport>;
39
+ //# sourceMappingURL=readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAA;AACrE,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,iBAAiB,GAAG,SAAS,CAAA;AACzE,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAA;AAErE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,kBAAkB,CAAA;IACxB,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,IAAI,GAAG,UAAU,GAAG,OAAO,CAAA;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,cAAc,EAAE,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,gBAAgB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACxC,CAAA;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;aAahC,CAAA;AAKF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAa9D;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,eAAe,CAAC,CAgD1B"}
@@ -0,0 +1,72 @@
1
+ // src/readiness.ts — machine-readable "did this release come up" report.
2
+ // Public endpoint: results carry a closed set of codes and never a driver
3
+ // message, a connection string, or a stack trace.
4
+ import * as v from 'valibot';
5
+ export const readinessReportSchema = v.strictObject({
6
+ status: v.picklist(['ok', 'degraded', 'error']),
7
+ revision: v.optional(v.string()),
8
+ checks: v.array(v.strictObject({
9
+ name: v.picklist(['database', 'schema', 'background']),
10
+ status: v.picklist(['ok', 'degraded', 'error', 'skipped']),
11
+ code: v.optional(v.picklist(['unreachable', 'not_provisioned', 'backlog'])),
12
+ overdue: v.optional(v.number()),
13
+ })),
14
+ });
15
+ const MISSING_RELATION = /no such table|does not exist|undefined table|unknown table/i;
16
+ /**
17
+ * A query that fails this way reached the database and found no schema.
18
+ * Drizzle wraps the driver error, so the relation name lives on `cause`.
19
+ */
20
+ export function isMissingRelationError(error) {
21
+ let current = error;
22
+ for (let depth = 0; current !== undefined && current !== null && depth < 8; depth += 1) {
23
+ const message = current instanceof Error ? current.message : String(current);
24
+ if (MISSING_RELATION.test(message))
25
+ return true;
26
+ if (!(current instanceof Error))
27
+ return false;
28
+ current = current.cause;
29
+ }
30
+ return false;
31
+ }
32
+ export async function buildReadinessReport(probes) {
33
+ const checks = [];
34
+ let queryable = false;
35
+ try {
36
+ await probes.probeDatabase();
37
+ checks.push({ name: 'database', status: 'ok' }, { name: 'schema', status: 'ok' });
38
+ queryable = true;
39
+ }
40
+ catch (error) {
41
+ checks.push(isMissingRelationError(error)
42
+ ? { name: 'database', status: 'ok' }
43
+ : { name: 'database', status: 'error', code: 'unreachable' }, isMissingRelationError(error)
44
+ ? { name: 'schema', status: 'error', code: 'not_provisioned' }
45
+ : { name: 'schema', status: 'skipped' });
46
+ }
47
+ if (!queryable || !probes.queueJobsDeclared) {
48
+ checks.push({ name: 'background', status: 'skipped' });
49
+ }
50
+ else {
51
+ try {
52
+ const overdue = await probes.countOverdueJobs();
53
+ checks.push(overdue > 0
54
+ ? { name: 'background', status: 'degraded', code: 'backlog', overdue }
55
+ : { name: 'background', status: 'ok' });
56
+ }
57
+ catch {
58
+ checks.push({ name: 'background', status: 'skipped' });
59
+ }
60
+ }
61
+ const status = checks.some((check) => check.status === 'error')
62
+ ? 'error'
63
+ : checks.some((check) => check.status === 'degraded')
64
+ ? 'degraded'
65
+ : 'ok';
66
+ return {
67
+ status,
68
+ ...(probes.revision === undefined ? {} : { revision: probes.revision }),
69
+ checks,
70
+ };
71
+ }
72
+ //# sourceMappingURL=readiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness.js","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"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","sourcesContent":["// src/readiness.ts — machine-readable \"did this release come up\" report.\n// Public endpoint: results carry a closed set of codes and never a driver\n// message, a connection string, or a stack trace.\nimport * as v from 'valibot'\n\nexport type ReadinessStatus = 'ok' | 'degraded' | 'error' | 'skipped'\nexport type ReadinessCode = 'unreachable' | 'not_provisioned' | 'backlog'\nexport type ReadinessCheckName = 'database' | 'schema' | 'background'\n\nexport type ReadinessCheck = {\n name: ReadinessCheckName\n status: ReadinessStatus\n code?: ReadinessCode\n overdue?: number\n}\n\nexport type ReadinessReport = {\n status: 'ok' | 'degraded' | 'error'\n revision?: string\n checks: ReadinessCheck[]\n}\n\nexport type ReadinessProbes = {\n /** Deployed commit, when the platform injects `WORKERSTACK_REVISION`. */\n revision?: string\n queueJobsDeclared: boolean\n probeDatabase: () => Promise<void>\n countOverdueJobs: () => Promise<number>\n}\n\nexport const readinessReportSchema = v.strictObject({\n status: v.picklist(['ok', 'degraded', 'error']),\n revision: v.optional(v.string()),\n checks: v.array(\n v.strictObject({\n name: v.picklist(['database', 'schema', 'background']),\n status: v.picklist(['ok', 'degraded', 'error', 'skipped']),\n code: v.optional(\n v.picklist(['unreachable', 'not_provisioned', 'backlog']),\n ),\n overdue: v.optional(v.number()),\n }),\n ),\n})\n\nconst MISSING_RELATION =\n /no such table|does not exist|undefined table|unknown table/i\n\n/**\n * A query that fails this way reached the database and found no schema.\n * Drizzle wraps the driver error, so the relation name lives on `cause`.\n */\nexport function isMissingRelationError(error: unknown): boolean {\n let current: unknown = error\n for (\n let depth = 0;\n current !== undefined && current !== null && depth < 8;\n depth += 1\n ) {\n const message = current instanceof Error ? current.message : String(current)\n if (MISSING_RELATION.test(message)) return true\n if (!(current instanceof Error)) return false\n current = current.cause\n }\n return false\n}\n\nexport async function buildReadinessReport(\n probes: ReadinessProbes,\n): Promise<ReadinessReport> {\n const checks: ReadinessCheck[] = []\n let queryable = false\n\n try {\n await probes.probeDatabase()\n checks.push(\n { name: 'database', status: 'ok' },\n { name: 'schema', status: 'ok' },\n )\n queryable = true\n } catch (error) {\n checks.push(\n isMissingRelationError(error)\n ? { name: 'database', status: 'ok' }\n : { name: 'database', status: 'error', code: 'unreachable' },\n isMissingRelationError(error)\n ? { name: 'schema', status: 'error', code: 'not_provisioned' }\n : { name: 'schema', status: 'skipped' },\n )\n }\n\n if (!queryable || !probes.queueJobsDeclared) {\n checks.push({ name: 'background', status: 'skipped' })\n } else {\n try {\n const overdue = await probes.countOverdueJobs()\n checks.push(\n overdue > 0\n ? { name: 'background', status: 'degraded', code: 'backlog', overdue }\n : { name: 'background', status: 'ok' },\n )\n } catch {\n checks.push({ name: 'background', status: 'skipped' })\n }\n }\n\n const status = checks.some((check) => check.status === 'error')\n ? 'error'\n : checks.some((check) => check.status === 'degraded')\n ? 'degraded'\n : 'ok'\n\n return {\n status,\n ...(probes.revision === undefined ? {} : { revision: probes.revision }),\n checks,\n }\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import { type InferSelectModel, type Table } from 'drizzle-orm';
2
+ import type { RealtimeAction, RealtimePublisher } from './publisher.js';
3
+ export type RealtimeTransport = 'disabled' | 'memory' | 'platform';
4
+ export type SchemaTable<TSchema extends Record<string, unknown>> = Extract<TSchema[keyof TSchema], Table>;
5
+ export interface RealtimeFacade<TSchema extends Record<string, unknown> = Record<string, unknown>> {
6
+ readonly enabled: boolean;
7
+ readonly transport: RealtimeTransport;
8
+ publish<TTable extends SchemaTable<TSchema>>(table: TTable, action: RealtimeAction, record: InferSelectModel<TTable>, metadata?: {
9
+ operationId?: string;
10
+ }): Promise<void>;
11
+ }
12
+ /**
13
+ * One name for a table everywhere: events, subscriptions, and the CRUD router
14
+ * all use the schema key. Pass the schema so a key like `creditBalances` is not
15
+ * published as its SQL name `credit_balances` — clients subscribe with the key
16
+ * they call procedures with. Without a schema, the SQL name is the only name
17
+ * available and is used as-is.
18
+ */
19
+ export declare function createRealtimeFacade<TSchema extends Record<string, unknown>>(publisher?: RealtimePublisher, transport?: RealtimeTransport, schema?: TSchema): RealtimeFacade<TSchema>;
20
+ //# sourceMappingURL=facade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facade.d.ts","sourceRoot":"","sources":["../../src/realtime/facade.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,KAAK,EACX,MAAM,aAAa,CAAA;AAEpB,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpE,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAA;AAElE,MAAM,MAAM,WAAW,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,OAAO,CACxE,OAAO,CAAC,MAAM,OAAO,CAAC,EACtB,KAAK,CACN,CAAA;AAED,MAAM,WAAW,cAAc,CAC7B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAA;IAErC,OAAO,CAAC,MAAM,SAAS,WAAW,CAAC,OAAO,CAAC,EACzC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAChC,QAAQ,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAClC,OAAO,CAAC,IAAI,CAAC,CAAA;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,SAAS,CAAC,EAAE,iBAAiB,EAC7B,SAAS,GAAE,iBAAqD,EAChE,MAAM,CAAC,EAAE,OAAO,GACf,cAAc,CAAC,OAAO,CAAC,CA+BzB"}
@@ -0,0 +1,37 @@
1
+ import { getTableName, isTable, } from 'drizzle-orm';
2
+ /**
3
+ * One name for a table everywhere: events, subscriptions, and the CRUD router
4
+ * all use the schema key. Pass the schema so a key like `creditBalances` is not
5
+ * published as its SQL name `credit_balances` — clients subscribe with the key
6
+ * they call procedures with. Without a schema, the SQL name is the only name
7
+ * available and is used as-is.
8
+ */
9
+ export function createRealtimeFacade(publisher, transport = publisher ? 'memory' : 'disabled', schema) {
10
+ if (!publisher && transport !== 'disabled') {
11
+ throw new Error('[workerstack] an enabled realtime transport requires a publisher');
12
+ }
13
+ if (publisher && transport === 'disabled') {
14
+ throw new Error('[workerstack] a realtime publisher cannot use the disabled transport');
15
+ }
16
+ const keyByTableName = new Map();
17
+ for (const [key, value] of Object.entries(schema ?? {})) {
18
+ if (isTable(value))
19
+ keyByTableName.set(getTableName(value), key);
20
+ }
21
+ return {
22
+ enabled: publisher !== undefined,
23
+ transport,
24
+ async publish(table, action, record, metadata) {
25
+ if (!publisher)
26
+ return;
27
+ const tableName = getTableName(table);
28
+ await publisher.publish('change', {
29
+ table: keyByTableName.get(tableName) ?? tableName,
30
+ action,
31
+ record: record,
32
+ ...(metadata?.operationId ? { operationId: metadata.operationId } : {}),
33
+ });
34
+ },
35
+ };
36
+ }
37
+ //# sourceMappingURL=facade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facade.js","sourceRoot":"","sources":["../../src/realtime/facade.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,GAGR,MAAM,aAAa,CAAA;AAyBpB;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAA6B,EAC7B,YAA+B,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAChE,MAAgB;IAEhB,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAA;IACH,CAAC;IACD,IAAI,SAAS,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAA;IACH,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAA;IAChD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,IAAI,OAAO,CAAC,KAAK,CAAC;YAAE,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAA;IAClE,CAAC;IAED,OAAO;QACL,OAAO,EAAE,SAAS,KAAK,SAAS;QAChC,SAAS;QACT,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ;YAC3C,IAAI,CAAC,SAAS;gBAAE,OAAM;YACtB,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;YACrC,MAAM,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE;gBAChC,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS;gBACjD,MAAM;gBACN,MAAM,EAAE,MAA4C;gBACpD,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxE,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import {\n getTableName,\n isTable,\n type InferSelectModel,\n type Table,\n} from 'drizzle-orm'\n\nimport type { RealtimeAction, RealtimePublisher } from './publisher'\n\nexport type RealtimeTransport = 'disabled' | 'memory' | 'platform'\n\nexport type SchemaTable<TSchema extends Record<string, unknown>> = Extract<\n TSchema[keyof TSchema],\n Table\n>\n\nexport interface RealtimeFacade<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n> {\n readonly enabled: boolean\n readonly transport: RealtimeTransport\n\n publish<TTable extends SchemaTable<TSchema>>(\n table: TTable,\n action: RealtimeAction,\n record: InferSelectModel<TTable>,\n metadata?: { operationId?: string },\n ): Promise<void>\n}\n\n/**\n * One name for a table everywhere: events, subscriptions, and the CRUD router\n * all use the schema key. Pass the schema so a key like `creditBalances` is not\n * published as its SQL name `credit_balances` — clients subscribe with the key\n * they call procedures with. Without a schema, the SQL name is the only name\n * available and is used as-is.\n */\nexport function createRealtimeFacade<TSchema extends Record<string, unknown>>(\n publisher?: RealtimePublisher,\n transport: RealtimeTransport = publisher ? 'memory' : 'disabled',\n schema?: TSchema,\n): RealtimeFacade<TSchema> {\n if (!publisher && transport !== 'disabled') {\n throw new Error(\n '[workerstack] an enabled realtime transport requires a publisher',\n )\n }\n if (publisher && transport === 'disabled') {\n throw new Error(\n '[workerstack] a realtime publisher cannot use the disabled transport',\n )\n }\n\n const keyByTableName = new Map<string, string>()\n for (const [key, value] of Object.entries(schema ?? {})) {\n if (isTable(value)) keyByTableName.set(getTableName(value), key)\n }\n\n return {\n enabled: publisher !== undefined,\n transport,\n async publish(table, action, record, metadata) {\n if (!publisher) return\n const tableName = getTableName(table)\n await publisher.publish('change', {\n table: keyByTableName.get(tableName) ?? tableName,\n action,\n record: record as unknown as Record<string, unknown>,\n ...(metadata?.operationId ? { operationId: metadata.operationId } : {}),\n })\n },\n }\n}\n"]}