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,177 @@
1
+ // ---------------------------------------------------------------------------
2
+ // parseSize
3
+ // ---------------------------------------------------------------------------
4
+ const SIZE_UNITS = {
5
+ b: 1,
6
+ kb: 1024,
7
+ mb: 1024 * 1024,
8
+ gb: 1024 * 1024 * 1024,
9
+ };
10
+ export function parseSize(value) {
11
+ if (typeof value === 'number') {
12
+ if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) {
13
+ throw new Error(`[workerstack] invalid size "${value}"`);
14
+ }
15
+ return value;
16
+ }
17
+ const trimmed = value.trim();
18
+ if (trimmed === '')
19
+ throw new Error(`[workerstack] invalid size "${value}"`);
20
+ const match = /^(\d+(?:\.\d+)?)\s*([a-z]*)$/i.exec(trimmed);
21
+ if (!match)
22
+ throw new Error(`[workerstack] invalid size "${value}"`);
23
+ const numStr = match[1];
24
+ if (numStr === undefined)
25
+ throw new Error(`[workerstack] invalid size "${value}"`);
26
+ const num = parseFloat(numStr);
27
+ const unit = (match[2] ?? '').toLowerCase();
28
+ if (unit === '')
29
+ return Math.floor(num);
30
+ const multiplier = SIZE_UNITS[unit];
31
+ if (multiplier === undefined)
32
+ throw new Error(`[workerstack] invalid size "${value}"`);
33
+ return Math.floor(num * multiplier);
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Platform override (Bunderhost & co.)
37
+ // ---------------------------------------------------------------------------
38
+ /**
39
+ * A deployment platform that injects WORKERSTACK_S3_ENDPOINT forces every
40
+ * bucket onto that backend — code-level `local`/per-bucket `s3` blocks are
41
+ * ignored so apps deploy unchanged. Logical buckets already prefix object
42
+ * keys ("<bucket>/<uuid>"), so one physical bucket per environment suffices.
43
+ */
44
+ function platformS3Backend(env) {
45
+ const endpoint = env['WORKERSTACK_S3_ENDPOINT'];
46
+ if (!endpoint)
47
+ return undefined;
48
+ return {
49
+ type: 's3',
50
+ bucket: env['WORKERSTACK_S3_BUCKET'] ?? '',
51
+ region: env['WORKERSTACK_S3_REGION'] ?? 'auto',
52
+ endpoint,
53
+ accessKeyId: env['WORKERSTACK_S3_ACCESS_KEY_ID'] ?? '',
54
+ secretAccessKey: env['WORKERSTACK_S3_SECRET_ACCESS_KEY'] ?? '',
55
+ publicUrl: env['WORKERSTACK_S3_PUBLIC_URL'],
56
+ };
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // Shared backend resolution
60
+ // ---------------------------------------------------------------------------
61
+ function resolveSharedBackend(input, env) {
62
+ if (!input)
63
+ return { type: 'local', path: './uploads' };
64
+ if ('local' in input && input.local !== undefined) {
65
+ return {
66
+ type: 'local',
67
+ path: input.local === true ? './uploads' : input.local,
68
+ };
69
+ }
70
+ if ('s3' in input && input.s3 !== undefined) {
71
+ const s3Cfg = typeof input.s3 === 'object' ? input.s3 : {};
72
+ return {
73
+ type: 's3',
74
+ bucket: env['S3_BUCKET'] ?? '',
75
+ region: env['S3_REGION'] ?? 'us-east-1',
76
+ endpoint: s3Cfg.endpoint ?? env['S3_ENDPOINT'],
77
+ accessKeyId: env['S3_ACCESS_KEY_ID'] ?? '',
78
+ secretAccessKey: env['S3_SECRET_ACCESS_KEY'] ?? '',
79
+ };
80
+ }
81
+ return { type: 'local', path: './uploads' };
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Per-bucket backend resolution
85
+ // ---------------------------------------------------------------------------
86
+ function resolveBucketBackend(bucketInput, sharedBackend, env) {
87
+ // Platform override active → sharedBackend IS the platform backend and
88
+ // code-level per-bucket backends are ignored.
89
+ if (env['WORKERSTACK_S3_ENDPOINT'])
90
+ return sharedBackend;
91
+ if ('s3' in bucketInput && bucketInput.s3 !== undefined) {
92
+ const block = bucketInput.s3;
93
+ return {
94
+ type: 's3',
95
+ bucket: block.bucket,
96
+ region: block.region ?? env['S3_REGION'] ?? 'us-east-1',
97
+ endpoint: block.endpoint ?? env['S3_ENDPOINT'],
98
+ accessKeyId: env['S3_ACCESS_KEY_ID'] ?? '',
99
+ secretAccessKey: env['S3_SECRET_ACCESS_KEY'] ?? '',
100
+ publicUrl: block.publicUrl,
101
+ };
102
+ }
103
+ if ('local' in bucketInput && bucketInput.local !== undefined) {
104
+ return { type: 'local', path: bucketInput.local };
105
+ }
106
+ return sharedBackend;
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // Bucket resolution
110
+ // ---------------------------------------------------------------------------
111
+ function resolveSingleBucket(name, input, sharedBackend, env) {
112
+ const visibility = input.visibility ?? 'private';
113
+ const backend = resolveBucketBackend(input, sharedBackend, env);
114
+ const defaultGet = visibility === 'public' ? 'public' : 'owner';
115
+ const access = {
116
+ create: input.access?.create ?? 'authenticated',
117
+ get: input.access?.get ?? defaultGet,
118
+ delete: input.access?.delete ?? 'owner',
119
+ };
120
+ let upload = undefined;
121
+ if (input.upload !== undefined) {
122
+ upload = {};
123
+ if (input.upload.maxSize !== undefined) {
124
+ upload.maxSizeBytes = parseSize(input.upload.maxSize);
125
+ }
126
+ if (input.upload.accept !== undefined) {
127
+ upload.accept = input.upload.accept;
128
+ }
129
+ }
130
+ let quota = undefined;
131
+ if (input.quota !== undefined) {
132
+ quota = {};
133
+ if (input.quota.perUser !== undefined) {
134
+ quota.perUserBytes = parseSize(input.quota.perUser);
135
+ }
136
+ if (input.quota.perScope !== undefined) {
137
+ quota.perScopeBytes = parseSize(input.quota.perScope);
138
+ }
139
+ }
140
+ return {
141
+ name,
142
+ backend,
143
+ visibility,
144
+ access,
145
+ upload,
146
+ readScope: input.scope?.read,
147
+ writeScope: input.scope?.write,
148
+ quota,
149
+ };
150
+ }
151
+ // ---------------------------------------------------------------------------
152
+ // resolveBuckets
153
+ // ---------------------------------------------------------------------------
154
+ export function resolveBuckets(input, env = process.env) {
155
+ const sharedBackend = platformS3Backend(env) ?? resolveSharedBackend(input, env);
156
+ const bucketsInput = input?.buckets;
157
+ const declaredNames = bucketsInput ? Object.keys(bucketsInput) : [];
158
+ const defaultBucketName = input?.defaultBucket ??
159
+ // A single declared bucket is unambiguous — treat it as the default.
160
+ (declaredNames.length === 1 ? declaredNames[0] : 'default');
161
+ const buckets = new Map();
162
+ if (!bucketsInput || Object.keys(bucketsInput).length === 0) {
163
+ // Synthesize a single default bucket
164
+ const defaultBucket = resolveSingleBucket(defaultBucketName, {}, sharedBackend, env);
165
+ buckets.set(defaultBucketName, defaultBucket);
166
+ return { defaultBucket: defaultBucketName, buckets };
167
+ }
168
+ // Validate defaultBucket is declared
169
+ if (!(defaultBucketName in bucketsInput)) {
170
+ throw new Error(`[workerstack] defaultBucket "${defaultBucketName}" is not a declared bucket`);
171
+ }
172
+ for (const [name, bucketInput] of Object.entries(bucketsInput)) {
173
+ buckets.set(name, resolveSingleBucket(name, bucketInput, sharedBackend, env));
174
+ }
175
+ return { defaultBucket: defaultBucketName, buckets };
176
+ }
177
+ //# sourceMappingURL=buckets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buckets.js","sourceRoot":"","sources":["../../src/storage/buckets.ts"],"names":[],"mappings":"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","sourcesContent":["// src/storage/buckets.ts\nimport type { OperationRule, ScopeResolver } from '../access'\n\n// ---------------------------------------------------------------------------\n// Input types (developer-facing config)\n// ---------------------------------------------------------------------------\n\nexport type BucketBackendInput =\n | {\n s3: {\n bucket: string\n region?: string\n endpoint?: string\n publicUrl?: string\n }\n }\n | { local: string }\n\nexport type BucketConfigInput = {\n visibility?: 'public' | 'private'\n access?: {\n create?: OperationRule\n get?: OperationRule\n delete?: OperationRule\n }\n upload?: { maxSize?: string | number; accept?: string[] }\n scope?: {\n read?: ScopeResolver\n write?: ScopeResolver\n }\n quota?: { perUser?: string | number; perScope?: string | number }\n} & Partial<BucketBackendInput>\n\nexport type StorageConfigInput = {\n s3?: true | { endpoint?: string }\n local?: true | string\n defaultBucket?: string\n buckets?: Record<string, BucketConfigInput>\n}\n\n// ---------------------------------------------------------------------------\n// Resolved types (internal)\n// ---------------------------------------------------------------------------\n\nexport type ResolvedBackend =\n | { type: 'local'; path: string }\n | {\n type: 's3'\n bucket: string\n region: string\n endpoint?: string\n accessKeyId: string\n secretAccessKey: string\n publicUrl?: string\n }\n\nexport type ResolvedBucket = {\n name: string\n backend: ResolvedBackend\n visibility: 'public' | 'private'\n access: { create: OperationRule; get: OperationRule; delete: OperationRule }\n upload?: { maxSizeBytes?: number; accept?: string[] }\n readScope?: ScopeResolver\n writeScope?: ScopeResolver\n quota?: { perUserBytes?: number; perScopeBytes?: number }\n}\n\nexport type ResolvedStorageBuckets = {\n defaultBucket: string\n buckets: Map<string, ResolvedBucket>\n}\n\n// ---------------------------------------------------------------------------\n// parseSize\n// ---------------------------------------------------------------------------\n\nconst SIZE_UNITS: Record<string, number> = {\n b: 1,\n kb: 1024,\n mb: 1024 * 1024,\n gb: 1024 * 1024 * 1024,\n}\n\nexport function parseSize(value: string | number): number {\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) {\n throw new Error(`[workerstack] invalid size \"${value}\"`)\n }\n return value\n }\n\n const trimmed = value.trim()\n if (trimmed === '') throw new Error(`[workerstack] invalid size \"${value}\"`)\n\n const match = /^(\\d+(?:\\.\\d+)?)\\s*([a-z]*)$/i.exec(trimmed)\n if (!match) throw new Error(`[workerstack] invalid size \"${value}\"`)\n\n const numStr = match[1]\n if (numStr === undefined)\n throw new Error(`[workerstack] invalid size \"${value}\"`)\n const num = parseFloat(numStr)\n const unit = (match[2] ?? '').toLowerCase()\n\n if (unit === '') return Math.floor(num)\n\n const multiplier = SIZE_UNITS[unit]\n if (multiplier === undefined)\n throw new Error(`[workerstack] invalid size \"${value}\"`)\n\n return Math.floor(num * multiplier)\n}\n\n// ---------------------------------------------------------------------------\n// Platform override (Bunderhost & co.)\n// ---------------------------------------------------------------------------\n\n/**\n * A deployment platform that injects WORKERSTACK_S3_ENDPOINT forces every\n * bucket onto that backend — code-level `local`/per-bucket `s3` blocks are\n * ignored so apps deploy unchanged. Logical buckets already prefix object\n * keys (\"<bucket>/<uuid>\"), so one physical bucket per environment suffices.\n */\nfunction platformS3Backend(\n env: Record<string, string | undefined>,\n): ResolvedBackend | undefined {\n const endpoint = env['WORKERSTACK_S3_ENDPOINT']\n if (!endpoint) return undefined\n return {\n type: 's3',\n bucket: env['WORKERSTACK_S3_BUCKET'] ?? '',\n region: env['WORKERSTACK_S3_REGION'] ?? 'auto',\n endpoint,\n accessKeyId: env['WORKERSTACK_S3_ACCESS_KEY_ID'] ?? '',\n secretAccessKey: env['WORKERSTACK_S3_SECRET_ACCESS_KEY'] ?? '',\n publicUrl: env['WORKERSTACK_S3_PUBLIC_URL'],\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared backend resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveSharedBackend(\n input: StorageConfigInput | undefined,\n env: Record<string, string | undefined>,\n): ResolvedBackend {\n if (!input) return { type: 'local', path: './uploads' }\n\n if ('local' in input && input.local !== undefined) {\n return {\n type: 'local',\n path: input.local === true ? './uploads' : input.local,\n }\n }\n\n if ('s3' in input && input.s3 !== undefined) {\n const s3Cfg = typeof input.s3 === 'object' ? input.s3 : {}\n return {\n type: 's3',\n bucket: env['S3_BUCKET'] ?? '',\n region: env['S3_REGION'] ?? 'us-east-1',\n endpoint: s3Cfg.endpoint ?? env['S3_ENDPOINT'],\n accessKeyId: env['S3_ACCESS_KEY_ID'] ?? '',\n secretAccessKey: env['S3_SECRET_ACCESS_KEY'] ?? '',\n }\n }\n\n return { type: 'local', path: './uploads' }\n}\n\n// ---------------------------------------------------------------------------\n// Per-bucket backend resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveBucketBackend(\n bucketInput: BucketConfigInput,\n sharedBackend: ResolvedBackend,\n env: Record<string, string | undefined>,\n): ResolvedBackend {\n // Platform override active → sharedBackend IS the platform backend and\n // code-level per-bucket backends are ignored.\n if (env['WORKERSTACK_S3_ENDPOINT']) return sharedBackend\n if ('s3' in bucketInput && bucketInput.s3 !== undefined) {\n const block = bucketInput.s3\n return {\n type: 's3',\n bucket: block.bucket,\n region: block.region ?? env['S3_REGION'] ?? 'us-east-1',\n endpoint: block.endpoint ?? env['S3_ENDPOINT'],\n accessKeyId: env['S3_ACCESS_KEY_ID'] ?? '',\n secretAccessKey: env['S3_SECRET_ACCESS_KEY'] ?? '',\n publicUrl: block.publicUrl,\n }\n }\n\n if ('local' in bucketInput && bucketInput.local !== undefined) {\n return { type: 'local', path: bucketInput.local }\n }\n\n return sharedBackend\n}\n\n// ---------------------------------------------------------------------------\n// Bucket resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveSingleBucket(\n name: string,\n input: BucketConfigInput,\n sharedBackend: ResolvedBackend,\n env: Record<string, string | undefined>,\n): ResolvedBucket {\n const visibility = input.visibility ?? 'private'\n const backend = resolveBucketBackend(input, sharedBackend, env)\n\n const defaultGet: OperationRule = visibility === 'public' ? 'public' : 'owner'\n\n const access: ResolvedBucket['access'] = {\n create: input.access?.create ?? 'authenticated',\n get: input.access?.get ?? defaultGet,\n delete: input.access?.delete ?? 'owner',\n }\n\n let upload: ResolvedBucket['upload'] | undefined = undefined\n if (input.upload !== undefined) {\n upload = {}\n if (input.upload.maxSize !== undefined) {\n upload.maxSizeBytes = parseSize(input.upload.maxSize)\n }\n if (input.upload.accept !== undefined) {\n upload.accept = input.upload.accept\n }\n }\n\n let quota: ResolvedBucket['quota'] | undefined = undefined\n if (input.quota !== undefined) {\n quota = {}\n if (input.quota.perUser !== undefined) {\n quota.perUserBytes = parseSize(input.quota.perUser)\n }\n if (input.quota.perScope !== undefined) {\n quota.perScopeBytes = parseSize(input.quota.perScope)\n }\n }\n\n return {\n name,\n backend,\n visibility,\n access,\n upload,\n readScope: input.scope?.read,\n writeScope: input.scope?.write,\n quota,\n }\n}\n\n// ---------------------------------------------------------------------------\n// resolveBuckets\n// ---------------------------------------------------------------------------\n\nexport function resolveBuckets(\n input: StorageConfigInput | undefined,\n env: Record<string, string | undefined> = process.env,\n): ResolvedStorageBuckets {\n const sharedBackend =\n platformS3Backend(env) ?? resolveSharedBackend(input, env)\n const bucketsInput = input?.buckets\n\n const declaredNames = bucketsInput ? Object.keys(bucketsInput) : []\n const defaultBucketName =\n input?.defaultBucket ??\n // A single declared bucket is unambiguous — treat it as the default.\n (declaredNames.length === 1 ? declaredNames[0]! : 'default')\n\n const buckets = new Map<string, ResolvedBucket>()\n\n if (!bucketsInput || Object.keys(bucketsInput).length === 0) {\n // Synthesize a single default bucket\n const defaultBucket = resolveSingleBucket(\n defaultBucketName,\n {},\n sharedBackend,\n env,\n )\n buckets.set(defaultBucketName, defaultBucket)\n return { defaultBucket: defaultBucketName, buckets }\n }\n\n // Validate defaultBucket is declared\n if (!(defaultBucketName in bucketsInput)) {\n throw new Error(\n `[workerstack] defaultBucket \"${defaultBucketName}\" is not a declared bucket`,\n )\n }\n\n for (const [name, bucketInput] of Object.entries(bucketsInput)) {\n buckets.set(\n name,\n resolveSingleBucket(name, bucketInput, sharedBackend, env),\n )\n }\n\n return { defaultBucket: defaultBucketName, buckets }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import type { AnyDb } from '../dialect.js';
2
+ import type { StorageAdapter } from './index.js';
3
+ /**
4
+ * Delete a logical file: the object and its file-meta row. All deletion goes
5
+ * through this helper — never inline `adapter.delete`.
6
+ */
7
+ export declare function deleteStoredFile(adapter: StorageAdapter, db: AnyDb, fileId: string): Promise<void>;
8
+ //# sourceMappingURL=delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/storage/delete.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAI7C;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,KAAK,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAGf"}
@@ -0,0 +1,10 @@
1
+ import { deleteFileMetaRow } from './file-meta.js';
2
+ /**
3
+ * Delete a logical file: the object and its file-meta row. All deletion goes
4
+ * through this helper — never inline `adapter.delete`.
5
+ */
6
+ export async function deleteStoredFile(adapter, db, fileId) {
7
+ await adapter.delete(fileId);
8
+ await deleteFileMetaRow(db, fileId);
9
+ }
10
+ //# sourceMappingURL=delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/storage/delete.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAuB,EACvB,EAAS,EACT,MAAc;IAEd,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC5B,MAAM,iBAAiB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;AACrC,CAAC","sourcesContent":["// src/storage/delete.ts\nimport type { AnyDb } from '../dialect'\nimport type { StorageAdapter } from './index'\n\nimport { deleteFileMetaRow } from './file-meta'\n\n/**\n * Delete a logical file: the object and its file-meta row. All deletion goes\n * through this helper — never inline `adapter.delete`.\n */\nexport async function deleteStoredFile(\n adapter: StorageAdapter,\n db: AnyDb,\n fileId: string,\n): Promise<void> {\n await adapter.delete(fileId)\n await deleteFileMetaRow(db, fileId)\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import type { ScopeMap } from '../access.js';
2
+ import type { AnyDb } from '../dialect.js';
3
+ import { workerstackFiles } from '../internal-tables.js';
4
+ export type FileMetaRow = typeof workerstackFiles.$inferSelect;
5
+ export declare function insertPendingFile(db: AnyDb, input: {
6
+ fileId: string;
7
+ bucket: string;
8
+ ownerId: string | null;
9
+ scopeJson: string | null;
10
+ filename: string | null;
11
+ contentType: string | null;
12
+ }): Promise<void>;
13
+ export declare function insertReadyFile(db: AnyDb, input: {
14
+ fileId: string;
15
+ bucket: string;
16
+ ownerId: string | null;
17
+ scopeJson: string | null;
18
+ filename: string | null;
19
+ contentType: string | null;
20
+ size: number | null;
21
+ }): Promise<void>;
22
+ export declare function markFileReady(db: AnyDb, fileId: string, patch: {
23
+ size: number | null;
24
+ contentType: string | null;
25
+ }): Promise<void>;
26
+ export declare function getFileMeta(db: AnyDb, fileId: string): Promise<FileMetaRow | null>;
27
+ export declare function deleteFileMetaRow(db: AnyDb, fileId: string): Promise<void>;
28
+ export declare function listStalePendingFiles(db: AnyDb, olderThanMs: number): Promise<FileMetaRow[]>;
29
+ export declare function sumReadySize(db: AnyDb, q: {
30
+ bucket: string;
31
+ ownerId?: string;
32
+ scopeJson?: string;
33
+ }): Promise<number>;
34
+ export declare function scopeToJson(scope: ScopeMap | undefined | null): string | null;
35
+ export declare function parseScopeJson(json: string | null): ScopeMap | null;
36
+ export declare function fileMatchesScope(row: FileMetaRow, requesterScope: ScopeMap | undefined): boolean;
37
+ //# sourceMappingURL=file-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-meta.d.ts","sourceRoot":"","sources":["../../src/storage/file-meta.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAGvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,MAAM,MAAM,WAAW,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAA;AAI9D,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,KAAK,EACT,KAAK,EAAE;IACL,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B,GACA,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,KAAK,EACT,KAAK,EAAE;IACL,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB,GACA,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,KAAK,EACT,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACzD,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,WAAW,CAC/B,EAAE,EAAE,KAAK,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAQ7B;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,KAAK,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAGf;AAID,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,KAAK,EACT,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,EAAE,CAAC,CAMxB;AAID,wBAAsB,YAAY,CAChC,EAAE,EAAE,KAAK,EACT,CAAC,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAID,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAW7E;AAeD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,CASnE;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,EAChB,cAAc,EAAE,QAAQ,GAAG,SAAS,GACnC,OAAO,CAOT"}
@@ -0,0 +1,166 @@
1
+ /*
2
+ * DEFERRED — logical/physical blob/file split
3
+ *
4
+ * The current model is 1:1 logical-file = physical-object: one row in
5
+ * bunderstack_file_meta maps to exactly one object in storage. A future
6
+ * blob/file split would introduce:
7
+ *
8
+ * workerstackBlob { key, bucket, size, contentType, refcount, createdAt }
9
+ * workerstackFile { id, blobKey, ownerId, scopeJson, status, filename,
10
+ * createdAt, confirmedAt, deletedAt }
11
+ *
12
+ * This would enable:
13
+ * - Soft/shadow delete (file.deletedAt + per-bucket retention policy)
14
+ * - Transfer ownership/scope (mutate file.ownerId / scopeJson, no byte movement)
15
+ * - Duplicate/share to many users or orgs by reference (refcount++) so sharing
16
+ * to N users stores bytes once; or by-copy via S3 CopyObject.
17
+ *
18
+ * Deletion would decrement refcount; bytes removed only at 0; orphan sweep also
19
+ * reaps refcount=0 blobs (self-heals crash mid-decrement).
20
+ *
21
+ * Deferred because refcount correctness is a real cost vs. current need.
22
+ *
23
+ * Two forward-compat invariants keep this addable without breaking the public API:
24
+ * 1. fileId is opaque — clients never parse it.
25
+ * 2. All deletion goes through the storage delete helper, never inline.
26
+ *
27
+ * See docs/plans/2026-06-28-multi-bucket-storage-design.md §6 for full rationale.
28
+ */
29
+ import { eq, and, lt, sql } from 'drizzle-orm';
30
+ import { rowMatchesScope } from '../access.js';
31
+ import { workerstackFiles } from '../internal-tables.js';
32
+ // ─── CRUD ─────────────────────────────────────────────────────────────────────
33
+ export async function insertPendingFile(db, input) {
34
+ const files = workerstackFiles;
35
+ await db.insert(files).values({
36
+ fileId: input.fileId,
37
+ bucket: input.bucket,
38
+ ownerId: input.ownerId,
39
+ scopeJson: input.scopeJson,
40
+ filename: input.filename,
41
+ contentType: input.contentType,
42
+ status: 'pending',
43
+ createdAt: Date.now(),
44
+ confirmedAt: null,
45
+ size: null,
46
+ });
47
+ }
48
+ export async function insertReadyFile(db, input) {
49
+ const files = workerstackFiles;
50
+ const now = Date.now();
51
+ await db.insert(files).values({
52
+ fileId: input.fileId,
53
+ bucket: input.bucket,
54
+ ownerId: input.ownerId,
55
+ scopeJson: input.scopeJson,
56
+ filename: input.filename,
57
+ contentType: input.contentType,
58
+ size: input.size,
59
+ status: 'ready',
60
+ createdAt: now,
61
+ confirmedAt: now,
62
+ });
63
+ }
64
+ export async function markFileReady(db, fileId, patch) {
65
+ const files = workerstackFiles;
66
+ await db
67
+ .update(files)
68
+ .set({
69
+ status: 'ready',
70
+ confirmedAt: Date.now(),
71
+ size: patch.size,
72
+ contentType: patch.contentType,
73
+ })
74
+ .where(eq(files.fileId, fileId));
75
+ }
76
+ export async function getFileMeta(db, fileId) {
77
+ const files = workerstackFiles;
78
+ const rows = await db
79
+ .select()
80
+ .from(files)
81
+ .where(eq(files.fileId, fileId))
82
+ .limit(1);
83
+ return rows[0] ?? null;
84
+ }
85
+ export async function deleteFileMetaRow(db, fileId) {
86
+ const files = workerstackFiles;
87
+ await db.delete(files).where(eq(files.fileId, fileId));
88
+ }
89
+ // ─── Sweep ────────────────────────────────────────────────────────────────────
90
+ export async function listStalePendingFiles(db, olderThanMs) {
91
+ const files = workerstackFiles;
92
+ return db
93
+ .select()
94
+ .from(files)
95
+ .where(and(eq(files.status, 'pending'), lt(files.createdAt, olderThanMs)));
96
+ }
97
+ // ─── Quota ────────────────────────────────────────────────────────────────────
98
+ export async function sumReadySize(db, q) {
99
+ const files = workerstackFiles;
100
+ const conditions = [eq(files.status, 'ready'), eq(files.bucket, q.bucket)];
101
+ if (q.ownerId !== undefined) {
102
+ conditions.push(eq(files.ownerId, q.ownerId));
103
+ }
104
+ if (q.scopeJson !== undefined) {
105
+ conditions.push(eq(files.scopeJson, q.scopeJson));
106
+ }
107
+ const rows = await db
108
+ .select({
109
+ total: sql `coalesce(sum(${files.size}), 0)`,
110
+ })
111
+ .from(files)
112
+ .where(and(...conditions));
113
+ const raw = rows[0]?.total ?? 0;
114
+ return Number(raw);
115
+ }
116
+ // ─── Scope helpers ────────────────────────────────────────────────────────────
117
+ export function scopeToJson(scope) {
118
+ if (scope == null)
119
+ return null;
120
+ const keys = Object.keys(scope);
121
+ if (keys.length === 0)
122
+ return null;
123
+ const sorted = keys.sort();
124
+ const ordered = {};
125
+ for (const k of sorted) {
126
+ const value = scope[k];
127
+ if (value !== undefined)
128
+ ordered[k] = value;
129
+ }
130
+ return JSON.stringify(ordered);
131
+ }
132
+ function isScopeMap(value) {
133
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
134
+ return false;
135
+ }
136
+ for (const item of Object.values(value)) {
137
+ if (typeof item === 'string')
138
+ continue;
139
+ if (Array.isArray(item) && item.every((entry) => typeof entry === 'string'))
140
+ continue;
141
+ return false;
142
+ }
143
+ return true;
144
+ }
145
+ export function parseScopeJson(json) {
146
+ if (json == null)
147
+ return null;
148
+ try {
149
+ const parsed = JSON.parse(json);
150
+ return isScopeMap(parsed) ? parsed : null;
151
+ }
152
+ catch {
153
+ // Corrupt/legacy scope_json should not crash a read; treat as no scope.
154
+ return null;
155
+ }
156
+ }
157
+ export function fileMatchesScope(row, requesterScope) {
158
+ if (requesterScope == null || Object.keys(requesterScope).length === 0) {
159
+ return true;
160
+ }
161
+ if (row.scopeJson == null)
162
+ return false;
163
+ const parsed = parseScopeJson(row.scopeJson) ?? {};
164
+ return rowMatchesScope(parsed, requesterScope);
165
+ }
166
+ //# sourceMappingURL=file-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-meta.js","sourceRoot":"","sources":["../../src/storage/file-meta.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * DEFERRED — logical/physical blob/file split\n *\n * The current model is 1:1 logical-file = physical-object: one row in\n * bunderstack_file_meta maps to exactly one object in storage. A future\n * blob/file split would introduce:\n *\n * workerstackBlob { key, bucket, size, contentType, refcount, createdAt }\n * workerstackFile { id, blobKey, ownerId, scopeJson, status, filename,\n * createdAt, confirmedAt, deletedAt }\n *\n * This would enable:\n * - Soft/shadow delete (file.deletedAt + per-bucket retention policy)\n * - Transfer ownership/scope (mutate file.ownerId / scopeJson, no byte movement)\n * - Duplicate/share to many users or orgs by reference (refcount++) so sharing\n * to N users stores bytes once; or by-copy via S3 CopyObject.\n *\n * Deletion would decrement refcount; bytes removed only at 0; orphan sweep also\n * reaps refcount=0 blobs (self-heals crash mid-decrement).\n *\n * Deferred because refcount correctness is a real cost vs. current need.\n *\n * Two forward-compat invariants keep this addable without breaking the public API:\n * 1. fileId is opaque — clients never parse it.\n * 2. All deletion goes through the storage delete helper, never inline.\n *\n * See docs/plans/2026-06-28-multi-bucket-storage-design.md §6 for full rationale.\n */\n\nimport { eq, and, lt, sql } from 'drizzle-orm'\n\nimport type { ScopeMap } from '../access'\nimport type { AnyDb } from '../dialect'\n\nimport { rowMatchesScope } from '../access'\nimport { workerstackFiles } from '../internal-tables'\n\nexport type FileMetaRow = typeof workerstackFiles.$inferSelect\n\n// ─── CRUD ─────────────────────────────────────────────────────────────────────\n\nexport async function insertPendingFile(\n db: AnyDb,\n input: {\n fileId: string\n bucket: string\n ownerId: string | null\n scopeJson: string | null\n filename: string | null\n contentType: string | null\n },\n): Promise<void> {\n const files = workerstackFiles\n await db.insert(files).values({\n fileId: input.fileId,\n bucket: input.bucket,\n ownerId: input.ownerId,\n scopeJson: input.scopeJson,\n filename: input.filename,\n contentType: input.contentType,\n status: 'pending',\n createdAt: Date.now(),\n confirmedAt: null,\n size: null,\n })\n}\n\nexport async function insertReadyFile(\n db: AnyDb,\n input: {\n fileId: string\n bucket: string\n ownerId: string | null\n scopeJson: string | null\n filename: string | null\n contentType: string | null\n size: number | null\n },\n): Promise<void> {\n const files = workerstackFiles\n const now = Date.now()\n await db.insert(files).values({\n fileId: input.fileId,\n bucket: input.bucket,\n ownerId: input.ownerId,\n scopeJson: input.scopeJson,\n filename: input.filename,\n contentType: input.contentType,\n size: input.size,\n status: 'ready',\n createdAt: now,\n confirmedAt: now,\n })\n}\n\nexport async function markFileReady(\n db: AnyDb,\n fileId: string,\n patch: { size: number | null; contentType: string | null },\n): Promise<void> {\n const files = workerstackFiles\n await db\n .update(files)\n .set({\n status: 'ready',\n confirmedAt: Date.now(),\n size: patch.size,\n contentType: patch.contentType,\n })\n .where(eq(files.fileId, fileId))\n}\n\nexport async function getFileMeta(\n db: AnyDb,\n fileId: string,\n): Promise<FileMetaRow | null> {\n const files = workerstackFiles\n const rows = await db\n .select()\n .from(files)\n .where(eq(files.fileId, fileId))\n .limit(1)\n return (rows[0] as FileMetaRow | undefined) ?? null\n}\n\nexport async function deleteFileMetaRow(\n db: AnyDb,\n fileId: string,\n): Promise<void> {\n const files = workerstackFiles\n await db.delete(files).where(eq(files.fileId, fileId))\n}\n\n// ─── Sweep ────────────────────────────────────────────────────────────────────\n\nexport async function listStalePendingFiles(\n db: AnyDb,\n olderThanMs: number,\n): Promise<FileMetaRow[]> {\n const files = workerstackFiles\n return db\n .select()\n .from(files)\n .where(and(eq(files.status, 'pending'), lt(files.createdAt, olderThanMs)))\n}\n\n// ─── Quota ────────────────────────────────────────────────────────────────────\n\nexport async function sumReadySize(\n db: AnyDb,\n q: { bucket: string; ownerId?: string; scopeJson?: string },\n): Promise<number> {\n const files = workerstackFiles\n const conditions = [eq(files.status, 'ready'), eq(files.bucket, q.bucket)]\n if (q.ownerId !== undefined) {\n conditions.push(eq(files.ownerId, q.ownerId))\n }\n if (q.scopeJson !== undefined) {\n conditions.push(eq(files.scopeJson, q.scopeJson))\n }\n\n const rows = await db\n .select({\n total: sql<number>`coalesce(sum(${files.size}), 0)`,\n })\n .from(files)\n .where(and(...conditions))\n\n const raw = rows[0]?.total ?? 0\n return Number(raw)\n}\n\n// ─── Scope helpers ────────────────────────────────────────────────────────────\n\nexport function scopeToJson(scope: ScopeMap | undefined | null): string | null {\n if (scope == null) return null\n const keys = Object.keys(scope)\n if (keys.length === 0) return null\n const sorted = keys.sort()\n const ordered: Record<string, string | string[]> = {}\n for (const k of sorted) {\n const value = scope[k]\n if (value !== undefined) ordered[k] = value\n }\n return JSON.stringify(ordered)\n}\n\nfunction isScopeMap(value: unknown): value is ScopeMap {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n for (const item of Object.values(value)) {\n if (typeof item === 'string') continue\n if (Array.isArray(item) && item.every((entry) => typeof entry === 'string'))\n continue\n return false\n }\n return true\n}\n\nexport function parseScopeJson(json: string | null): ScopeMap | null {\n if (json == null) return null\n try {\n const parsed = JSON.parse(json)\n return isScopeMap(parsed) ? parsed : null\n } catch {\n // Corrupt/legacy scope_json should not crash a read; treat as no scope.\n return null\n }\n}\n\nexport function fileMatchesScope(\n row: FileMetaRow,\n requesterScope: ScopeMap | undefined,\n): boolean {\n if (requesterScope == null || Object.keys(requesterScope).length === 0) {\n return true\n }\n if (row.scopeJson == null) return false\n const parsed = parseScopeJson(row.scopeJson) ?? {}\n return rowMatchesScope(parsed, requesterScope)\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { LocalStorageAdapter } from './local.js';
2
+ import { S3StorageAdapter } from './s3.js';
3
+ export type { LocalStorageAdapter, S3StorageAdapter };
4
+ export { createStorageOperations } from './operations.js';
5
+ export type { PrepareUploadResult, StorageDownload, StorageExecutionContext, StorageOperations, StorageOperationsOptions, } from './operations.js';
6
+ export interface PresignPutOptions {
7
+ contentType?: string;
8
+ expiresIn: number;
9
+ }
10
+ export interface PresignGetOptions {
11
+ expiresIn: number;
12
+ }
13
+ export interface StorageAdapter {
14
+ upload(fileId: string, data: Blob | ArrayBuffer, contentType: string): Promise<void>;
15
+ get(fileId: string): Promise<Response>;
16
+ delete(fileId: string): Promise<void>;
17
+ exists(fileId: string): Promise<boolean>;
18
+ presignPut?(key: string, opts: PresignPutOptions): Promise<string>;
19
+ presignGet?(key: string, opts: PresignGetOptions): Promise<string>;
20
+ stat?(key: string): Promise<{
21
+ size: number;
22
+ contentType: string;
23
+ } | null>;
24
+ publicUrlFor?(key: string): string | undefined;
25
+ /** List full object keys under a prefix (used to reap transform derivatives). */
26
+ list?(prefix: string): Promise<string[]>;
27
+ }
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAEvC,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,CAAA;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AACtD,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACvB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,cAAc,CAAA;AAErB,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB;AACD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CACJ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,GAAG,WAAW,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAAA;IAChB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACtC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAExC,UAAU,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE,UAAU,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACzE,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAC9C,iFAAiF;IACjF,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;CACzC"}
@@ -0,0 +1,5 @@
1
+ // src/storage/index.ts
2
+ import { LocalStorageAdapter } from './local.js';
3
+ import { S3StorageAdapter } from './s3.js';
4
+ export { createStorageOperations } from './operations.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAGvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA","sourcesContent":["// src/storage/index.ts\nimport { LocalStorageAdapter } from './local'\nimport { S3StorageAdapter } from './s3'\n\nexport type { LocalStorageAdapter, S3StorageAdapter }\nexport { createStorageOperations } from './operations'\nexport type {\n PrepareUploadResult,\n StorageDownload,\n StorageExecutionContext,\n StorageOperations,\n StorageOperationsOptions,\n} from './operations'\n\nexport interface PresignPutOptions {\n contentType?: string\n expiresIn: number\n}\nexport interface PresignGetOptions {\n expiresIn: number\n}\n\nexport interface StorageAdapter {\n upload(\n fileId: string,\n data: Blob | ArrayBuffer,\n contentType: string,\n ): Promise<void>\n get(fileId: string): Promise<Response>\n delete(fileId: string): Promise<void>\n exists(fileId: string): Promise<boolean>\n // Optional — present on S3, absent on local (router uses proxy path for local)\n presignPut?(key: string, opts: PresignPutOptions): Promise<string>\n presignGet?(key: string, opts: PresignGetOptions): Promise<string>\n stat?(key: string): Promise<{ size: number; contentType: string } | null>\n publicUrlFor?(key: string): string | undefined\n /** List full object keys under a prefix (used to reap transform derivatives). */\n list?(prefix: string): Promise<string[]>\n}\n"]}
@@ -0,0 +1,15 @@
1
+ export declare class LocalStorageAdapter {
2
+ private readonly basePath;
3
+ constructor(basePath: string);
4
+ private filePath;
5
+ upload(fileId: string, data: Blob | ArrayBuffer, contentType: string): Promise<void>;
6
+ get(fileId: string): Promise<Response>;
7
+ delete(fileId: string): Promise<void>;
8
+ exists(fileId: string): Promise<boolean>;
9
+ stat(key: string): Promise<{
10
+ size: number;
11
+ contentType: string;
12
+ } | null>;
13
+ list(prefix: string): Promise<string[]>;
14
+ }
15
+ //# sourceMappingURL=local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"AAIA,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;gBACrB,QAAQ,EAAE,MAAM;IAI5B,OAAO,CAAC,QAAQ;IAIV,MAAM,CACV,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,GAAG,WAAW,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IASV,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYtC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC,IAAI,CACR,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAMlD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAY9C"}
@@ -0,0 +1,56 @@
1
+ // src/storage/local.ts
2
+ import { mkdir, readdir } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ export class LocalStorageAdapter {
5
+ basePath;
6
+ constructor(basePath) {
7
+ this.basePath = basePath;
8
+ }
9
+ filePath(fileId) {
10
+ return join(this.basePath, fileId);
11
+ }
12
+ async upload(fileId, data, contentType) {
13
+ await mkdir(this.basePath, { recursive: true });
14
+ const bytes = data instanceof Blob ? await data.arrayBuffer() : data;
15
+ await Bun.write(Bun.file(this.filePath(fileId), { type: contentType }), bytes);
16
+ }
17
+ async get(fileId) {
18
+ const file = Bun.file(this.filePath(fileId));
19
+ if (!(await file.exists()))
20
+ return new Response('Not found', { status: 404 });
21
+ return new Response(file, {
22
+ headers: {
23
+ 'Content-Type': file.type,
24
+ 'Cache-Control': 'public, max-age=31536000',
25
+ },
26
+ });
27
+ }
28
+ async delete(fileId) {
29
+ const file = Bun.file(this.filePath(fileId));
30
+ if (await file.exists())
31
+ await file.unlink();
32
+ }
33
+ async exists(fileId) {
34
+ return Bun.file(this.filePath(fileId)).exists();
35
+ }
36
+ async stat(key) {
37
+ const file = Bun.file(this.filePath(key));
38
+ if (!(await file.exists()))
39
+ return null;
40
+ return { size: file.size, contentType: file.type };
41
+ }
42
+ async list(prefix) {
43
+ const cleanPrefix = prefix.replace(/\/$/, '');
44
+ const dir = join(this.basePath, cleanPrefix);
45
+ let entries;
46
+ try {
47
+ entries = await readdir(dir);
48
+ }
49
+ catch {
50
+ // Missing dir (or prefix isn't a directory) → no derivatives.
51
+ return [];
52
+ }
53
+ return entries.map((name) => `${cleanPrefix}/${name}`);
54
+ }
55
+ }
56
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/storage/local.ts"],"names":[],"mappings":"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","sourcesContent":["// src/storage/local.ts\nimport { mkdir, readdir } from 'node:fs/promises'\nimport { join } from 'node:path'\n\nexport class LocalStorageAdapter {\n private readonly basePath: string\n constructor(basePath: string) {\n this.basePath = basePath\n }\n\n private filePath(fileId: string) {\n return join(this.basePath, fileId)\n }\n\n async upload(\n fileId: string,\n data: Blob | ArrayBuffer,\n contentType: string,\n ): Promise<void> {\n await mkdir(this.basePath, { recursive: true })\n const bytes = data instanceof Blob ? await data.arrayBuffer() : data\n await Bun.write(\n Bun.file(this.filePath(fileId), { type: contentType }),\n bytes,\n )\n }\n\n async get(fileId: string): Promise<Response> {\n const file = Bun.file(this.filePath(fileId))\n if (!(await file.exists()))\n return new Response('Not found', { status: 404 })\n return new Response(file, {\n headers: {\n 'Content-Type': file.type,\n 'Cache-Control': 'public, max-age=31536000',\n },\n })\n }\n\n async delete(fileId: string): Promise<void> {\n const file = Bun.file(this.filePath(fileId))\n if (await file.exists()) await file.unlink()\n }\n\n async exists(fileId: string): Promise<boolean> {\n return Bun.file(this.filePath(fileId)).exists()\n }\n\n async stat(\n key: string,\n ): Promise<{ size: number; contentType: string } | null> {\n const file = Bun.file(this.filePath(key))\n if (!(await file.exists())) return null\n return { size: file.size, contentType: file.type }\n }\n\n async list(prefix: string): Promise<string[]> {\n const cleanPrefix = prefix.replace(/\\/$/, '')\n const dir = join(this.basePath, cleanPrefix)\n let entries: string[]\n try {\n entries = await readdir(dir)\n } catch {\n // Missing dir (or prefix isn't a directory) → no derivatives.\n return []\n }\n return entries.map((name) => `${cleanPrefix}/${name}`)\n }\n}\n"]}