@uploadista/core 0.0.2

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 (359) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/.turbo/turbo-check.log +231 -0
  3. package/.turbo/turbo-format.log +5 -0
  4. package/LICENSE +21 -0
  5. package/README.md +1120 -0
  6. package/dist/chunk-CUT6urMc.cjs +1 -0
  7. package/dist/debounce-C2SeqcxD.js +2 -0
  8. package/dist/debounce-C2SeqcxD.js.map +1 -0
  9. package/dist/debounce-LZK7yS7Z.cjs +1 -0
  10. package/dist/errors/index.cjs +1 -0
  11. package/dist/errors/index.d.cts +3 -0
  12. package/dist/errors/index.d.ts +3 -0
  13. package/dist/errors/index.d.ts.map +1 -0
  14. package/dist/errors/index.js +2 -0
  15. package/dist/errors/uploadista-error.d.ts +209 -0
  16. package/dist/errors/uploadista-error.d.ts.map +1 -0
  17. package/dist/errors/uploadista-error.js +322 -0
  18. package/dist/errors-8i_aMxOE.js +1 -0
  19. package/dist/errors-CRm1FHHT.cjs +0 -0
  20. package/dist/flow/edge.d.ts +47 -0
  21. package/dist/flow/edge.d.ts.map +1 -0
  22. package/dist/flow/edge.js +40 -0
  23. package/dist/flow/event.d.ts +206 -0
  24. package/dist/flow/event.d.ts.map +1 -0
  25. package/dist/flow/event.js +53 -0
  26. package/dist/flow/flow-server.d.ts +223 -0
  27. package/dist/flow/flow-server.d.ts.map +1 -0
  28. package/dist/flow/flow-server.js +614 -0
  29. package/dist/flow/flow.d.ts +238 -0
  30. package/dist/flow/flow.d.ts.map +1 -0
  31. package/dist/flow/flow.js +629 -0
  32. package/dist/flow/index.cjs +1 -0
  33. package/dist/flow/index.d.cts +6 -0
  34. package/dist/flow/index.d.ts +24 -0
  35. package/dist/flow/index.d.ts.map +1 -0
  36. package/dist/flow/index.js +24 -0
  37. package/dist/flow/node.d.ts +136 -0
  38. package/dist/flow/node.d.ts.map +1 -0
  39. package/dist/flow/node.js +153 -0
  40. package/dist/flow/nodes/index.d.ts +8 -0
  41. package/dist/flow/nodes/index.d.ts.map +1 -0
  42. package/dist/flow/nodes/index.js +7 -0
  43. package/dist/flow/nodes/input-node.d.ts +78 -0
  44. package/dist/flow/nodes/input-node.d.ts.map +1 -0
  45. package/dist/flow/nodes/input-node.js +233 -0
  46. package/dist/flow/nodes/storage-node.d.ts +67 -0
  47. package/dist/flow/nodes/storage-node.d.ts.map +1 -0
  48. package/dist/flow/nodes/storage-node.js +94 -0
  49. package/dist/flow/nodes/streaming-input-node.d.ts +69 -0
  50. package/dist/flow/nodes/streaming-input-node.d.ts.map +1 -0
  51. package/dist/flow/nodes/streaming-input-node.js +156 -0
  52. package/dist/flow/nodes/transform-node.d.ts +85 -0
  53. package/dist/flow/nodes/transform-node.d.ts.map +1 -0
  54. package/dist/flow/nodes/transform-node.js +107 -0
  55. package/dist/flow/parallel-scheduler.d.ts +175 -0
  56. package/dist/flow/parallel-scheduler.d.ts.map +1 -0
  57. package/dist/flow/parallel-scheduler.js +193 -0
  58. package/dist/flow/plugins/credential-provider.d.ts +47 -0
  59. package/dist/flow/plugins/credential-provider.d.ts.map +1 -0
  60. package/dist/flow/plugins/credential-provider.js +24 -0
  61. package/dist/flow/plugins/image-ai-plugin.d.ts +61 -0
  62. package/dist/flow/plugins/image-ai-plugin.d.ts.map +1 -0
  63. package/dist/flow/plugins/image-ai-plugin.js +21 -0
  64. package/dist/flow/plugins/image-plugin.d.ts +52 -0
  65. package/dist/flow/plugins/image-plugin.d.ts.map +1 -0
  66. package/dist/flow/plugins/image-plugin.js +22 -0
  67. package/dist/flow/plugins/types/describe-image-node.d.ts +16 -0
  68. package/dist/flow/plugins/types/describe-image-node.d.ts.map +1 -0
  69. package/dist/flow/plugins/types/describe-image-node.js +9 -0
  70. package/dist/flow/plugins/types/index.d.ts +9 -0
  71. package/dist/flow/plugins/types/index.d.ts.map +1 -0
  72. package/dist/flow/plugins/types/index.js +8 -0
  73. package/dist/flow/plugins/types/optimize-node.d.ts +20 -0
  74. package/dist/flow/plugins/types/optimize-node.d.ts.map +1 -0
  75. package/dist/flow/plugins/types/optimize-node.js +11 -0
  76. package/dist/flow/plugins/types/remove-background-node.d.ts +16 -0
  77. package/dist/flow/plugins/types/remove-background-node.d.ts.map +1 -0
  78. package/dist/flow/plugins/types/remove-background-node.js +9 -0
  79. package/dist/flow/plugins/types/resize-node.d.ts +21 -0
  80. package/dist/flow/plugins/types/resize-node.d.ts.map +1 -0
  81. package/dist/flow/plugins/types/resize-node.js +16 -0
  82. package/dist/flow/plugins/zip-plugin.d.ts +62 -0
  83. package/dist/flow/plugins/zip-plugin.d.ts.map +1 -0
  84. package/dist/flow/plugins/zip-plugin.js +21 -0
  85. package/dist/flow/typed-flow.d.ts +90 -0
  86. package/dist/flow/typed-flow.d.ts.map +1 -0
  87. package/dist/flow/typed-flow.js +59 -0
  88. package/dist/flow/types/flow-file.d.ts +45 -0
  89. package/dist/flow/types/flow-file.d.ts.map +1 -0
  90. package/dist/flow/types/flow-file.js +27 -0
  91. package/dist/flow/types/flow-job.d.ts +118 -0
  92. package/dist/flow/types/flow-job.d.ts.map +1 -0
  93. package/dist/flow/types/flow-job.js +11 -0
  94. package/dist/flow/types/flow-types.d.ts +321 -0
  95. package/dist/flow/types/flow-types.d.ts.map +1 -0
  96. package/dist/flow/types/flow-types.js +52 -0
  97. package/dist/flow/types/index.d.ts +4 -0
  98. package/dist/flow/types/index.d.ts.map +1 -0
  99. package/dist/flow/types/index.js +3 -0
  100. package/dist/flow/types/run-args.d.ts +38 -0
  101. package/dist/flow/types/run-args.d.ts.map +1 -0
  102. package/dist/flow/types/run-args.js +30 -0
  103. package/dist/flow/types/type-validator.d.ts +26 -0
  104. package/dist/flow/types/type-validator.d.ts.map +1 -0
  105. package/dist/flow/types/type-validator.js +134 -0
  106. package/dist/flow/utils/resolve-upload-metadata.d.ts +11 -0
  107. package/dist/flow/utils/resolve-upload-metadata.d.ts.map +1 -0
  108. package/dist/flow/utils/resolve-upload-metadata.js +28 -0
  109. package/dist/flow-2zXnEiWL.cjs +1 -0
  110. package/dist/flow-CRaKy7Vj.js +2 -0
  111. package/dist/flow-CRaKy7Vj.js.map +1 -0
  112. package/dist/generate-id-Dm-Vboxq.d.ts +34 -0
  113. package/dist/generate-id-Dm-Vboxq.d.ts.map +1 -0
  114. package/dist/generate-id-LjJRLD6N.d.cts +34 -0
  115. package/dist/generate-id-LjJRLD6N.d.cts.map +1 -0
  116. package/dist/generate-id-xHp_Z7Cl.cjs +1 -0
  117. package/dist/generate-id-yohS1ZDk.js +2 -0
  118. package/dist/generate-id-yohS1ZDk.js.map +1 -0
  119. package/dist/index-BO8GZlbD.d.cts +1040 -0
  120. package/dist/index-BO8GZlbD.d.cts.map +1 -0
  121. package/dist/index-BoGG5KAY.d.ts +1 -0
  122. package/dist/index-BtBZHVmz.d.cts +1 -0
  123. package/dist/index-D-CoVpkZ.d.ts +1004 -0
  124. package/dist/index-D-CoVpkZ.d.ts.map +1 -0
  125. package/dist/index.cjs +1 -0
  126. package/dist/index.d.cts +6 -0
  127. package/dist/index.d.ts +5 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +5 -0
  130. package/dist/logger/logger.cjs +1 -0
  131. package/dist/logger/logger.d.cts +8 -0
  132. package/dist/logger/logger.d.cts.map +1 -0
  133. package/dist/logger/logger.d.ts +5 -0
  134. package/dist/logger/logger.d.ts.map +1 -0
  135. package/dist/logger/logger.js +10 -0
  136. package/dist/logger/logger.js.map +1 -0
  137. package/dist/semaphore-0ZwjVpyF.js +2 -0
  138. package/dist/semaphore-0ZwjVpyF.js.map +1 -0
  139. package/dist/semaphore-BHprIjFI.d.cts +37 -0
  140. package/dist/semaphore-BHprIjFI.d.cts.map +1 -0
  141. package/dist/semaphore-DThupBkc.d.ts +37 -0
  142. package/dist/semaphore-DThupBkc.d.ts.map +1 -0
  143. package/dist/semaphore-DVrONiAV.cjs +1 -0
  144. package/dist/stream-limiter-CoWKv39w.js +2 -0
  145. package/dist/stream-limiter-CoWKv39w.js.map +1 -0
  146. package/dist/stream-limiter-JgOwmkMa.cjs +1 -0
  147. package/dist/streams/multi-stream.cjs +1 -0
  148. package/dist/streams/multi-stream.d.cts +91 -0
  149. package/dist/streams/multi-stream.d.cts.map +1 -0
  150. package/dist/streams/multi-stream.d.ts +86 -0
  151. package/dist/streams/multi-stream.d.ts.map +1 -0
  152. package/dist/streams/multi-stream.js +149 -0
  153. package/dist/streams/multi-stream.js.map +1 -0
  154. package/dist/streams/stream-limiter.cjs +1 -0
  155. package/dist/streams/stream-limiter.d.cts +36 -0
  156. package/dist/streams/stream-limiter.d.cts.map +1 -0
  157. package/dist/streams/stream-limiter.d.ts +27 -0
  158. package/dist/streams/stream-limiter.d.ts.map +1 -0
  159. package/dist/streams/stream-limiter.js +49 -0
  160. package/dist/streams/stream-splitter.cjs +1 -0
  161. package/dist/streams/stream-splitter.d.cts +68 -0
  162. package/dist/streams/stream-splitter.d.cts.map +1 -0
  163. package/dist/streams/stream-splitter.d.ts +51 -0
  164. package/dist/streams/stream-splitter.d.ts.map +1 -0
  165. package/dist/streams/stream-splitter.js +175 -0
  166. package/dist/streams/stream-splitter.js.map +1 -0
  167. package/dist/types/data-store-registry.d.ts +13 -0
  168. package/dist/types/data-store-registry.d.ts.map +1 -0
  169. package/dist/types/data-store-registry.js +4 -0
  170. package/dist/types/data-store.d.ts +316 -0
  171. package/dist/types/data-store.d.ts.map +1 -0
  172. package/dist/types/data-store.js +157 -0
  173. package/dist/types/event-broadcaster.d.ts +28 -0
  174. package/dist/types/event-broadcaster.d.ts.map +1 -0
  175. package/dist/types/event-broadcaster.js +6 -0
  176. package/dist/types/event-emitter.d.ts +378 -0
  177. package/dist/types/event-emitter.d.ts.map +1 -0
  178. package/dist/types/event-emitter.js +223 -0
  179. package/dist/types/index.cjs +1 -0
  180. package/dist/types/index.d.cts +6 -0
  181. package/dist/types/index.d.ts +10 -0
  182. package/dist/types/index.d.ts.map +1 -0
  183. package/dist/types/index.js +9 -0
  184. package/dist/types/input-file.d.ts +104 -0
  185. package/dist/types/input-file.d.ts.map +1 -0
  186. package/dist/types/input-file.js +27 -0
  187. package/dist/types/kv-store.d.ts +281 -0
  188. package/dist/types/kv-store.d.ts.map +1 -0
  189. package/dist/types/kv-store.js +234 -0
  190. package/dist/types/middleware.d.ts +17 -0
  191. package/dist/types/middleware.d.ts.map +1 -0
  192. package/dist/types/middleware.js +21 -0
  193. package/dist/types/upload-event.d.ts +105 -0
  194. package/dist/types/upload-event.d.ts.map +1 -0
  195. package/dist/types/upload-event.js +71 -0
  196. package/dist/types/upload-file.d.ts +136 -0
  197. package/dist/types/upload-file.d.ts.map +1 -0
  198. package/dist/types/upload-file.js +34 -0
  199. package/dist/types/websocket.d.ts +144 -0
  200. package/dist/types/websocket.d.ts.map +1 -0
  201. package/dist/types/websocket.js +40 -0
  202. package/dist/types-BT-cvi7T.cjs +1 -0
  203. package/dist/types-DhU2j-XF.js +2 -0
  204. package/dist/types-DhU2j-XF.js.map +1 -0
  205. package/dist/upload/convert-to-stream.d.ts +38 -0
  206. package/dist/upload/convert-to-stream.d.ts.map +1 -0
  207. package/dist/upload/convert-to-stream.js +43 -0
  208. package/dist/upload/convert-upload-to-flow-file.d.ts +14 -0
  209. package/dist/upload/convert-upload-to-flow-file.d.ts.map +1 -0
  210. package/dist/upload/convert-upload-to-flow-file.js +21 -0
  211. package/dist/upload/create-upload.d.ts +68 -0
  212. package/dist/upload/create-upload.d.ts.map +1 -0
  213. package/dist/upload/create-upload.js +157 -0
  214. package/dist/upload/index.cjs +1 -0
  215. package/dist/upload/index.d.cts +6 -0
  216. package/dist/upload/index.d.ts +4 -0
  217. package/dist/upload/index.d.ts.map +1 -0
  218. package/dist/upload/index.js +3 -0
  219. package/dist/upload/mime.d.ts +24 -0
  220. package/dist/upload/mime.d.ts.map +1 -0
  221. package/dist/upload/mime.js +351 -0
  222. package/dist/upload/upload-chunk.d.ts +58 -0
  223. package/dist/upload/upload-chunk.d.ts.map +1 -0
  224. package/dist/upload/upload-chunk.js +277 -0
  225. package/dist/upload/upload-server.d.ts +221 -0
  226. package/dist/upload/upload-server.d.ts.map +1 -0
  227. package/dist/upload/upload-server.js +181 -0
  228. package/dist/upload/upload-strategy-negotiator.d.ts +148 -0
  229. package/dist/upload/upload-strategy-negotiator.d.ts.map +1 -0
  230. package/dist/upload/upload-strategy-negotiator.js +217 -0
  231. package/dist/upload/upload-url.d.ts +68 -0
  232. package/dist/upload/upload-url.d.ts.map +1 -0
  233. package/dist/upload/upload-url.js +142 -0
  234. package/dist/upload/write-to-store.d.ts +77 -0
  235. package/dist/upload/write-to-store.d.ts.map +1 -0
  236. package/dist/upload/write-to-store.js +147 -0
  237. package/dist/upload-DLuICjpP.cjs +1 -0
  238. package/dist/upload-DaXO34dE.js +2 -0
  239. package/dist/upload-DaXO34dE.js.map +1 -0
  240. package/dist/uploadista-error-BB-Wdiz9.cjs +22 -0
  241. package/dist/uploadista-error-BVsVxqvz.js +23 -0
  242. package/dist/uploadista-error-BVsVxqvz.js.map +1 -0
  243. package/dist/uploadista-error-CwxYs4EB.d.ts +52 -0
  244. package/dist/uploadista-error-CwxYs4EB.d.ts.map +1 -0
  245. package/dist/uploadista-error-kKlhLRhY.d.cts +52 -0
  246. package/dist/uploadista-error-kKlhLRhY.d.cts.map +1 -0
  247. package/dist/utils/checksum.d.ts +22 -0
  248. package/dist/utils/checksum.d.ts.map +1 -0
  249. package/dist/utils/checksum.js +49 -0
  250. package/dist/utils/debounce.cjs +1 -0
  251. package/dist/utils/debounce.d.cts +38 -0
  252. package/dist/utils/debounce.d.cts.map +1 -0
  253. package/dist/utils/debounce.d.ts +36 -0
  254. package/dist/utils/debounce.d.ts.map +1 -0
  255. package/dist/utils/debounce.js +73 -0
  256. package/dist/utils/generate-id.cjs +1 -0
  257. package/dist/utils/generate-id.d.cts +2 -0
  258. package/dist/utils/generate-id.d.ts +32 -0
  259. package/dist/utils/generate-id.d.ts.map +1 -0
  260. package/dist/utils/generate-id.js +23 -0
  261. package/dist/utils/md5.cjs +1 -0
  262. package/dist/utils/md5.d.cts +73 -0
  263. package/dist/utils/md5.d.cts.map +1 -0
  264. package/dist/utils/md5.d.ts +71 -0
  265. package/dist/utils/md5.d.ts.map +1 -0
  266. package/dist/utils/md5.js +417 -0
  267. package/dist/utils/md5.js.map +1 -0
  268. package/dist/utils/once.cjs +1 -0
  269. package/dist/utils/once.d.cts +25 -0
  270. package/dist/utils/once.d.cts.map +1 -0
  271. package/dist/utils/once.d.ts +21 -0
  272. package/dist/utils/once.d.ts.map +1 -0
  273. package/dist/utils/once.js +54 -0
  274. package/dist/utils/once.js.map +1 -0
  275. package/dist/utils/semaphore.cjs +1 -0
  276. package/dist/utils/semaphore.d.cts +3 -0
  277. package/dist/utils/semaphore.d.ts +78 -0
  278. package/dist/utils/semaphore.d.ts.map +1 -0
  279. package/dist/utils/semaphore.js +134 -0
  280. package/dist/utils/throttle.cjs +1 -0
  281. package/dist/utils/throttle.d.cts +24 -0
  282. package/dist/utils/throttle.d.cts.map +1 -0
  283. package/dist/utils/throttle.d.ts +18 -0
  284. package/dist/utils/throttle.d.ts.map +1 -0
  285. package/dist/utils/throttle.js +20 -0
  286. package/dist/utils/throttle.js.map +1 -0
  287. package/docs/PARALLEL_EXECUTION.md +206 -0
  288. package/docs/PARALLEL_EXECUTION_QUICKSTART.md +142 -0
  289. package/docs/PARALLEL_EXECUTION_REFACTOR.md +184 -0
  290. package/package.json +80 -0
  291. package/src/errors/__tests__/uploadista-error.test.ts +251 -0
  292. package/src/errors/index.ts +2 -0
  293. package/src/errors/uploadista-error.ts +394 -0
  294. package/src/flow/README.md +352 -0
  295. package/src/flow/edge.test.ts +146 -0
  296. package/src/flow/edge.ts +60 -0
  297. package/src/flow/event.ts +229 -0
  298. package/src/flow/flow-server.ts +1089 -0
  299. package/src/flow/flow.ts +1050 -0
  300. package/src/flow/index.ts +28 -0
  301. package/src/flow/node.ts +249 -0
  302. package/src/flow/nodes/index.ts +8 -0
  303. package/src/flow/nodes/input-node.ts +296 -0
  304. package/src/flow/nodes/storage-node.ts +128 -0
  305. package/src/flow/nodes/transform-node.ts +154 -0
  306. package/src/flow/parallel-scheduler.ts +259 -0
  307. package/src/flow/plugins/credential-provider.ts +48 -0
  308. package/src/flow/plugins/image-ai-plugin.ts +66 -0
  309. package/src/flow/plugins/image-plugin.ts +60 -0
  310. package/src/flow/plugins/types/describe-image-node.ts +16 -0
  311. package/src/flow/plugins/types/index.ts +9 -0
  312. package/src/flow/plugins/types/optimize-node.ts +18 -0
  313. package/src/flow/plugins/types/remove-background-node.ts +18 -0
  314. package/src/flow/plugins/types/resize-node.ts +26 -0
  315. package/src/flow/plugins/zip-plugin.ts +69 -0
  316. package/src/flow/typed-flow.ts +279 -0
  317. package/src/flow/types/flow-file.ts +51 -0
  318. package/src/flow/types/flow-job.ts +138 -0
  319. package/src/flow/types/flow-types.ts +353 -0
  320. package/src/flow/types/index.ts +6 -0
  321. package/src/flow/types/run-args.ts +40 -0
  322. package/src/flow/types/type-validator.ts +204 -0
  323. package/src/flow/utils/resolve-upload-metadata.ts +48 -0
  324. package/src/index.ts +5 -0
  325. package/src/logger/logger.ts +14 -0
  326. package/src/streams/stream-limiter.test.ts +150 -0
  327. package/src/streams/stream-limiter.ts +75 -0
  328. package/src/types/data-store.ts +427 -0
  329. package/src/types/event-broadcaster.ts +39 -0
  330. package/src/types/event-emitter.ts +349 -0
  331. package/src/types/index.ts +9 -0
  332. package/src/types/input-file.ts +107 -0
  333. package/src/types/kv-store.ts +375 -0
  334. package/src/types/middleware.ts +54 -0
  335. package/src/types/upload-event.ts +75 -0
  336. package/src/types/upload-file.ts +139 -0
  337. package/src/types/websocket.ts +65 -0
  338. package/src/upload/convert-to-stream.ts +48 -0
  339. package/src/upload/create-upload.ts +214 -0
  340. package/src/upload/index.ts +3 -0
  341. package/src/upload/mime.ts +436 -0
  342. package/src/upload/upload-chunk.ts +364 -0
  343. package/src/upload/upload-server.ts +390 -0
  344. package/src/upload/upload-strategy-negotiator.ts +316 -0
  345. package/src/upload/upload-url.ts +173 -0
  346. package/src/upload/write-to-store.ts +211 -0
  347. package/src/utils/checksum.ts +61 -0
  348. package/src/utils/debounce.test.ts +126 -0
  349. package/src/utils/debounce.ts +89 -0
  350. package/src/utils/generate-id.ts +35 -0
  351. package/src/utils/md5.ts +475 -0
  352. package/src/utils/once.test.ts +83 -0
  353. package/src/utils/once.ts +63 -0
  354. package/src/utils/throttle.test.ts +101 -0
  355. package/src/utils/throttle.ts +29 -0
  356. package/tsconfig.json +20 -0
  357. package/tsconfig.tsbuildinfo +1 -0
  358. package/tsdown.config.ts +25 -0
  359. package/vitest.config.ts +15 -0
@@ -0,0 +1,375 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { UploadistaError } from "../errors";
3
+ import type { FlowJob } from "../flow";
4
+ import type { UploadFile } from "./upload-file";
5
+
6
+ /**
7
+ * Base key-value store interface for raw string storage.
8
+ *
9
+ * This is the low-level interface that storage adapters implement.
10
+ * It stores raw string values without type safety or serialization.
11
+ *
12
+ * @property get - Retrieves a value by key, returns null if not found
13
+ * @property set - Stores a value with the given key
14
+ * @property delete - Removes a value by key
15
+ * @property list - Optional operation to list all keys with a given prefix
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Implement a BaseKvStore with Redis
20
+ * const redisKvStore: BaseKvStore = {
21
+ * get: (key) => Effect.tryPromise({
22
+ * try: () => redis.get(key),
23
+ * catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
24
+ * }),
25
+ *
26
+ * set: (key, value) => Effect.tryPromise({
27
+ * try: () => redis.set(key, value),
28
+ * catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
29
+ * }),
30
+ *
31
+ * delete: (key) => Effect.tryPromise({
32
+ * try: () => redis.del(key),
33
+ * catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
34
+ * }),
35
+ *
36
+ * list: (prefix) => Effect.tryPromise({
37
+ * try: () => redis.keys(`${prefix}*`),
38
+ * catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
39
+ * })
40
+ * };
41
+ * ```
42
+ */
43
+ export interface BaseKvStore {
44
+ readonly get: (key: string) => Effect.Effect<string | null, UploadistaError>;
45
+ readonly set: (
46
+ key: string,
47
+ value: string,
48
+ ) => Effect.Effect<void, UploadistaError>;
49
+ readonly delete: (key: string) => Effect.Effect<void, UploadistaError>;
50
+ readonly list?: (
51
+ keyPrefix: string,
52
+ ) => Effect.Effect<Array<string>, UploadistaError>;
53
+ }
54
+
55
+ /**
56
+ * Type-safe key-value store interface with automatic serialization.
57
+ *
58
+ * This wraps a BaseKvStore and handles JSON serialization/deserialization
59
+ * for a specific data type, providing type safety and eliminating the need
60
+ * for manual JSON.stringify/parse calls.
61
+ *
62
+ * @template TData - The type of data stored in this KV store
63
+ *
64
+ * @property get - Retrieves and deserializes a value, fails if not found
65
+ * @property set - Serializes and stores a value
66
+ * @property delete - Removes a value by key
67
+ * @property list - Optional operation to list all keys (without prefix)
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // Use a typed KV store
72
+ * const uploadStore: KvStore<UploadFile> = new TypedKvStore(
73
+ * baseStore,
74
+ * "uploads:",
75
+ * jsonSerializer.serialize,
76
+ * jsonSerializer.deserialize
77
+ * );
78
+ *
79
+ * // Store and retrieve typed data
80
+ * const program = Effect.gen(function* () {
81
+ * const file: UploadFile = {
82
+ * id: "file123",
83
+ * offset: 0,
84
+ * storage: { id: "s3", type: "s3" }
85
+ * };
86
+ *
87
+ * // Automatic serialization
88
+ * yield* uploadStore.set("file123", file);
89
+ *
90
+ * // Automatic deserialization with type safety
91
+ * const retrieved = yield* uploadStore.get("file123");
92
+ * console.log(retrieved.offset); // TypeScript knows this is a number
93
+ * });
94
+ * ```
95
+ */
96
+ export type KvStore<TData> = {
97
+ readonly get: (key: string) => Effect.Effect<TData, UploadistaError>;
98
+ readonly set: (
99
+ key: string,
100
+ value: TData,
101
+ ) => Effect.Effect<void, UploadistaError>;
102
+ readonly delete: (key: string) => Effect.Effect<void, UploadistaError>;
103
+ readonly list?: () => Effect.Effect<Array<string>, UploadistaError>;
104
+ };
105
+
106
+ /**
107
+ * Typed wrapper class that adds serialization to a BaseKvStore.
108
+ *
109
+ * This class implements the KvStore interface by wrapping a BaseKvStore
110
+ * and handling serialization/deserialization for a specific type. It also
111
+ * adds a key prefix to isolate different data types in the same store.
112
+ *
113
+ * @template TData - The type of data to store
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * // Create a typed store for UploadFile
118
+ * const uploadFileStore = new TypedKvStore<UploadFile>(
119
+ * baseKvStore,
120
+ * "uploadista:upload-file:", // All keys will be prefixed
121
+ * (data) => JSON.stringify(data),
122
+ * (str) => JSON.parse(str) as UploadFile
123
+ * );
124
+ *
125
+ * // Use the store
126
+ * const effect = Effect.gen(function* () {
127
+ * const file: UploadFile = { ... };
128
+ * yield* uploadFileStore.set("abc123", file);
129
+ * // Internally stores at key "uploadista:upload-file:abc123"
130
+ *
131
+ * const retrieved = yield* uploadFileStore.get("abc123");
132
+ * return retrieved;
133
+ * });
134
+ *
135
+ * // Custom serialization for binary data
136
+ * const binaryStore = new TypedKvStore<Uint8Array>(
137
+ * baseKvStore,
138
+ * "binary:",
139
+ * (data) => btoa(String.fromCharCode(...data)), // Base64 encode
140
+ * (str) => Uint8Array.from(atob(str), c => c.charCodeAt(0)) // Base64 decode
141
+ * );
142
+ * ```
143
+ */
144
+ export class TypedKvStore<TData> implements KvStore<TData> {
145
+ constructor(
146
+ private baseStore: BaseKvStore,
147
+ private keyPrefix: string,
148
+ private serialize: (data: TData) => string,
149
+ private deserialize: (str: string) => TData,
150
+ ) {}
151
+
152
+ get = (key: string): Effect.Effect<TData, UploadistaError> =>
153
+ this.baseStore.get(this.keyPrefix + key).pipe(
154
+ Effect.flatMap((value) => {
155
+ if (value === null) {
156
+ return Effect.fail(
157
+ UploadistaError.fromCode("FILE_NOT_FOUND", {
158
+ cause: `Key "${key}" not found`,
159
+ }),
160
+ );
161
+ }
162
+ try {
163
+ return Effect.succeed(this.deserialize(value));
164
+ } catch (error) {
165
+ return Effect.fail(
166
+ new UploadistaError({
167
+ code: "VALIDATION_ERROR",
168
+ status: 400,
169
+ body: `Failed to deserialize value for key "${key}"`,
170
+ cause: error,
171
+ }),
172
+ );
173
+ }
174
+ }),
175
+ );
176
+
177
+ set = (key: string, value: TData): Effect.Effect<void, UploadistaError> => {
178
+ try {
179
+ const serialized = this.serialize(value);
180
+ return this.baseStore.set(this.keyPrefix + key, serialized);
181
+ } catch (error) {
182
+ return Effect.fail(
183
+ new UploadistaError({
184
+ code: "VALIDATION_ERROR",
185
+ status: 400,
186
+ body: `Failed to serialize value for key "${key}"`,
187
+ cause: error,
188
+ }),
189
+ );
190
+ }
191
+ };
192
+
193
+ delete = (key: string): Effect.Effect<void, UploadistaError> =>
194
+ this.baseStore.delete(this.keyPrefix + key);
195
+
196
+ list = (): Effect.Effect<Array<string>, UploadistaError> => {
197
+ if (this.baseStore.list) {
198
+ return this.baseStore.list(this.keyPrefix);
199
+ }
200
+ return Effect.fail(
201
+ new UploadistaError({
202
+ code: "UNKNOWN_ERROR",
203
+ status: 501,
204
+ body: "List operation not supported by this store",
205
+ }),
206
+ );
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Default JSON serialization helpers.
212
+ *
213
+ * These functions provide standard JSON serialization for use with TypedKvStore.
214
+ * They work with any JSON-serializable type.
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * const store = new TypedKvStore<MyType>(
219
+ * baseStore,
220
+ * "mydata:",
221
+ * jsonSerializer.serialize,
222
+ * jsonSerializer.deserialize
223
+ * );
224
+ * ```
225
+ */
226
+ export const jsonSerializer = {
227
+ serialize: <T>(data: T): string => JSON.stringify(data),
228
+ deserialize: <T>(str: string): T => JSON.parse(str),
229
+ };
230
+
231
+ /**
232
+ * Effect-TS context tag for the base untyped KV store.
233
+ *
234
+ * This is the low-level store that storage adapter implementations provide.
235
+ * Most application code should use typed stores like UploadFileKVStore instead.
236
+ *
237
+ * @example
238
+ * ```typescript
239
+ * // Provide a base store implementation
240
+ * const baseStoreLayer = Layer.succeed(BaseKvStoreService, redisKvStore);
241
+ *
242
+ * // Use in an Effect
243
+ * const effect = Effect.gen(function* () {
244
+ * const baseStore = yield* BaseKvStoreService;
245
+ * yield* baseStore.set("raw-key", "raw-value");
246
+ * });
247
+ * ```
248
+ */
249
+ export class BaseKvStoreService extends Context.Tag("BaseKvStore")<
250
+ BaseKvStoreService,
251
+ BaseKvStore
252
+ >() {}
253
+
254
+ /**
255
+ * Effect-TS context tag for the UploadFile typed KV store.
256
+ *
257
+ * This provides type-safe storage for UploadFile metadata. It's the primary
258
+ * way to store and retrieve upload metadata in the system.
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const uploadEffect = Effect.gen(function* () {
263
+ * const kvStore = yield* UploadFileKVStore;
264
+ *
265
+ * // Store upload metadata
266
+ * const file: UploadFile = {
267
+ * id: "upload123",
268
+ * offset: 0,
269
+ * storage: { id: "s3", type: "s3" }
270
+ * };
271
+ * yield* kvStore.set("upload123", file);
272
+ *
273
+ * // Retrieve with type safety
274
+ * const retrieved = yield* kvStore.get("upload123");
275
+ * return retrieved;
276
+ * });
277
+ * ```
278
+ */
279
+ export class UploadFileKVStore extends Context.Tag("UploadFileKVStore")<
280
+ UploadFileKVStore,
281
+ KvStore<UploadFile>
282
+ >() {}
283
+
284
+ /**
285
+ * Effect Layer that creates the UploadFileKVStore from a BaseKvStore.
286
+ *
287
+ * This layer automatically wires up JSON serialization for UploadFile objects
288
+ * with the "uploadista:upload-file:" key prefix.
289
+ *
290
+ * @example
291
+ * ```typescript
292
+ * const program = Effect.gen(function* () {
293
+ * const kvStore = yield* UploadFileKVStore;
294
+ * // Use the store...
295
+ * }).pipe(
296
+ * Effect.provide(uploadFileKvStore),
297
+ * Effect.provide(baseStoreLayer)
298
+ * );
299
+ * ```
300
+ */
301
+ export const uploadFileKvStore = Layer.effect(
302
+ UploadFileKVStore,
303
+ Effect.gen(function* () {
304
+ const baseStore = yield* BaseKvStoreService;
305
+ return new TypedKvStore<UploadFile>(
306
+ baseStore,
307
+ "uploadista:upload-file:",
308
+ jsonSerializer.serialize,
309
+ jsonSerializer.deserialize,
310
+ );
311
+ }),
312
+ );
313
+
314
+ /**
315
+ * Effect-TS context tag for the FlowJob typed KV store.
316
+ *
317
+ * This provides type-safe storage for FlowJob metadata, tracking the
318
+ * execution state of flow processing jobs.
319
+ *
320
+ * @example
321
+ * ```typescript
322
+ * const flowEffect = Effect.gen(function* () {
323
+ * const jobStore = yield* FlowJobKVStore;
324
+ *
325
+ * // Store job state
326
+ * const job: FlowJob = {
327
+ * id: "job123",
328
+ * flowId: "flow_resize",
329
+ * status: "running",
330
+ * tasks: [],
331
+ * createdAt: new Date(),
332
+ * updatedAt: new Date()
333
+ * };
334
+ * yield* jobStore.set("job123", job);
335
+ *
336
+ * // Retrieve and check status
337
+ * const retrieved = yield* jobStore.get("job123");
338
+ * return retrieved.status;
339
+ * });
340
+ * ```
341
+ */
342
+ export class FlowJobKVStore extends Context.Tag("FlowJobKVStore")<
343
+ FlowJobKVStore,
344
+ KvStore<FlowJob>
345
+ >() {}
346
+
347
+ /**
348
+ * Effect Layer that creates the FlowJobKVStore from a BaseKvStore.
349
+ *
350
+ * This layer automatically wires up JSON serialization for FlowJob objects
351
+ * with the "uploadista:flow-job:" key prefix.
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * const program = Effect.gen(function* () {
356
+ * const jobStore = yield* FlowJobKVStore;
357
+ * // Use the store...
358
+ * }).pipe(
359
+ * Effect.provide(flowJobKvStore),
360
+ * Effect.provide(baseStoreLayer)
361
+ * );
362
+ * ```
363
+ */
364
+ export const flowJobKvStore = Layer.effect(
365
+ FlowJobKVStore,
366
+ Effect.gen(function* () {
367
+ const baseStore = yield* BaseKvStoreService;
368
+ return new TypedKvStore<FlowJob>(
369
+ baseStore,
370
+ "uploadista:flow-job:",
371
+ jsonSerializer.serialize,
372
+ jsonSerializer.deserialize,
373
+ );
374
+ }),
375
+ );
@@ -0,0 +1,54 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import type { UploadistaError } from "../errors";
3
+
4
+ export type MiddlewareContext = {
5
+ request: Request;
6
+ uploadId?: string;
7
+ metadata?: Record<string, string>;
8
+ };
9
+
10
+ export type MiddlewareNext = () => Promise<Response>;
11
+
12
+ export type Middleware = (
13
+ context: MiddlewareContext,
14
+ next: MiddlewareNext,
15
+ ) => Promise<Response>;
16
+
17
+ // Effect-based Middleware service
18
+ export class MiddlewareService extends Context.Tag("MiddlewareService")<
19
+ MiddlewareService,
20
+ {
21
+ readonly execute: (
22
+ middlewares: Middleware[],
23
+ context: MiddlewareContext,
24
+ handler: MiddlewareNext,
25
+ ) => Effect.Effect<Response, UploadistaError>;
26
+ }
27
+ >() {}
28
+
29
+ export const MiddlewareServiceLive = Layer.succeed(
30
+ MiddlewareService,
31
+ MiddlewareService.of({
32
+ execute: (middlewares, context, handler) =>
33
+ Effect.gen(function* () {
34
+ if (middlewares.length === 0) {
35
+ return yield* Effect.tryPromise({
36
+ try: () => handler(),
37
+ catch: (error) => error as UploadistaError,
38
+ });
39
+ }
40
+
41
+ const chain = middlewares.reduceRight(
42
+ (next: MiddlewareNext, middleware: Middleware) => {
43
+ return () => middleware(context, next);
44
+ },
45
+ handler,
46
+ );
47
+
48
+ return yield* Effect.tryPromise({
49
+ try: () => chain(),
50
+ catch: (error) => error as UploadistaError,
51
+ });
52
+ }),
53
+ }),
54
+ );
@@ -0,0 +1,75 @@
1
+ import { z } from "zod";
2
+ import { uploadFileSchema } from "./upload-file";
3
+
4
+ export enum UploadEventType {
5
+ UPLOAD_STARTED = "upload-started",
6
+ UPLOAD_PROGRESS = "upload-progress",
7
+ UPLOAD_COMPLETE = "upload-complete",
8
+ UPLOAD_FAILED = "upload-failed",
9
+ UPLOAD_VALIDATION_SUCCESS = "upload-validation-success",
10
+ UPLOAD_VALIDATION_FAILED = "upload-validation-failed",
11
+ UPLOAD_VALIDATION_WARNING = "upload-validation-warning",
12
+ }
13
+
14
+ const flowContextSchema = z.object({
15
+ flowId: z.string(),
16
+ nodeId: z.string(),
17
+ jobId: z.string(),
18
+ }).optional();
19
+
20
+ export const uploadEventSchema = z.union([
21
+ z.object({
22
+ type: z.union([
23
+ z.literal(UploadEventType.UPLOAD_STARTED),
24
+ z.literal(UploadEventType.UPLOAD_COMPLETE),
25
+ ]),
26
+ data: uploadFileSchema,
27
+ flow: flowContextSchema,
28
+ }),
29
+ z.object({
30
+ type: z.literal(UploadEventType.UPLOAD_PROGRESS),
31
+ data: z.object({
32
+ id: z.string(),
33
+ progress: z.number(),
34
+ total: z.number(),
35
+ }),
36
+ flow: flowContextSchema,
37
+ }),
38
+ z.object({
39
+ type: z.literal(UploadEventType.UPLOAD_FAILED),
40
+ data: z.object({
41
+ id: z.string(),
42
+ error: z.string(),
43
+ }),
44
+ flow: flowContextSchema,
45
+ }),
46
+ z.object({
47
+ type: z.literal(UploadEventType.UPLOAD_VALIDATION_SUCCESS),
48
+ data: z.object({
49
+ id: z.string(),
50
+ validationType: z.enum(["checksum", "mimetype"]),
51
+ algorithm: z.string().optional(),
52
+ }),
53
+ flow: flowContextSchema,
54
+ }),
55
+ z.object({
56
+ type: z.literal(UploadEventType.UPLOAD_VALIDATION_FAILED),
57
+ data: z.object({
58
+ id: z.string(),
59
+ reason: z.string(),
60
+ expected: z.string(),
61
+ actual: z.string(),
62
+ }),
63
+ flow: flowContextSchema,
64
+ }),
65
+ z.object({
66
+ type: z.literal(UploadEventType.UPLOAD_VALIDATION_WARNING),
67
+ data: z.object({
68
+ id: z.string(),
69
+ message: z.string(),
70
+ }),
71
+ flow: flowContextSchema,
72
+ }),
73
+ ]);
74
+
75
+ export type UploadEvent = z.infer<typeof uploadEventSchema>;
@@ -0,0 +1,139 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Zod schema for validating UploadFile objects.
5
+ *
6
+ * This schema defines the structure and validation rules for upload file metadata.
7
+ * Use this schema to parse and validate UploadFile data from external sources.
8
+ *
9
+ * @see {@link UploadFile} for the TypeScript type
10
+ */
11
+ export const uploadFileSchema = z.object({
12
+ id: z.string(),
13
+ size: z.number().optional(),
14
+ offset: z.number(),
15
+ metadata: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
16
+ creationDate: z.string().optional(),
17
+ url: z.string().optional(),
18
+ sizeIsDeferred: z.boolean().optional(),
19
+ checksum: z.string().optional(),
20
+ checksumAlgorithm: z.string().optional(),
21
+ storage: z.object({
22
+ id: z.string(),
23
+ type: z.string(),
24
+ path: z.string().optional(),
25
+ uploadId: z.string().optional(),
26
+ bucket: z.string().optional(),
27
+ }),
28
+ flow: z
29
+ .object({
30
+ flowId: z.string(),
31
+ nodeId: z.string(),
32
+ jobId: z.string(),
33
+ })
34
+ .optional(),
35
+ });
36
+
37
+ /**
38
+ * Represents an uploaded file with its metadata and storage information.
39
+ *
40
+ * This is the core data structure that tracks file uploads throughout their lifecycle.
41
+ * It contains all metadata needed to resume uploads, track progress, and locate files
42
+ * in storage backends.
43
+ *
44
+ * @property id - Unique identifier for this upload
45
+ * @property offset - Current byte offset (how many bytes have been uploaded)
46
+ * @property storage - Storage backend information
47
+ * @property storage.id - Storage backend identifier (e.g., "s3-production")
48
+ * @property storage.type - Storage backend type (e.g., "s3", "azure", "gcs")
49
+ * @property storage.path - Optional path prefix within the storage backend
50
+ * @property storage.uploadId - Optional backend-specific upload ID (e.g., S3 multipart upload ID)
51
+ * @property storage.bucket - Optional bucket or container name
52
+ * @property flow - Optional flow processing information (when file is part of a flow)
53
+ * @property flow.flowId - ID of the flow processing this file
54
+ * @property flow.nodeId - ID of the flow node that created this file
55
+ * @property flow.jobId - ID of the flow job execution
56
+ * @property size - Total file size in bytes (undefined if deferred)
57
+ * @property metadata - Custom key-value metadata attached to the file
58
+ * @property creationDate - ISO 8601 timestamp when upload was created
59
+ * @property url - Optional public URL to access the file
60
+ * @property sizeIsDeferred - True if file size is not known at upload start
61
+ * @property checksum - Optional file checksum/hash value
62
+ * @property checksumAlgorithm - Algorithm used for checksum (e.g., "md5", "sha256")
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * // Create an UploadFile for a new upload
67
+ * const uploadFile: UploadFile = {
68
+ * id: "upload_abc123",
69
+ * offset: 0,
70
+ * size: 1024000,
71
+ * storage: {
72
+ * id: "s3-production",
73
+ * type: "s3",
74
+ * bucket: "my-uploads",
75
+ * path: "files/"
76
+ * },
77
+ * metadata: {
78
+ * fileName: "image.jpg",
79
+ * contentType: "image/jpeg",
80
+ * userId: "user_123"
81
+ * },
82
+ * creationDate: new Date().toISOString(),
83
+ * checksum: "5d41402abc4b2a76b9719d911017c592",
84
+ * checksumAlgorithm: "md5"
85
+ * };
86
+ *
87
+ * // UploadFile with flow processing
88
+ * const flowFile: UploadFile = {
89
+ * id: "upload_xyz789",
90
+ * offset: 0,
91
+ * size: 2048000,
92
+ * storage: {
93
+ * id: "s3-temp",
94
+ * type: "s3",
95
+ * bucket: "temp-processing"
96
+ * },
97
+ * flow: {
98
+ * flowId: "flow_resize_optimize",
99
+ * nodeId: "input_1",
100
+ * jobId: "job_456"
101
+ * }
102
+ * };
103
+ *
104
+ * // Resume an interrupted upload
105
+ * const resumingFile: UploadFile = {
106
+ * id: "upload_resume",
107
+ * offset: 524288, // Already uploaded 512KB
108
+ * size: 1024000,
109
+ * storage: {
110
+ * id: "s3-production",
111
+ * type: "s3",
112
+ * uploadId: "multipart_xyz" // S3 multipart upload ID
113
+ * }
114
+ * };
115
+ * ```
116
+ */
117
+ export type UploadFile = {
118
+ id: string;
119
+ offset: number;
120
+ storage: {
121
+ id: string;
122
+ type: string;
123
+ path?: string | undefined;
124
+ uploadId?: string | undefined;
125
+ bucket?: string | undefined;
126
+ };
127
+ flow?: {
128
+ flowId: string;
129
+ nodeId: string;
130
+ jobId: string;
131
+ };
132
+ size?: number | undefined;
133
+ metadata?: Record<string, string | number | boolean> | undefined;
134
+ creationDate?: string | undefined;
135
+ url?: string | undefined;
136
+ sizeIsDeferred?: boolean | undefined;
137
+ checksum?: string | undefined;
138
+ checksumAlgorithm?: string | undefined;
139
+ };
@@ -0,0 +1,65 @@
1
+ import z from "zod";
2
+ import { uploadEventSchema } from "./upload-event";
3
+
4
+ /**
5
+ * Platform-agnostic WebSocket connection interface
6
+ */
7
+ export interface WebSocketConnection {
8
+ send(data: string): void;
9
+ close(code?: number, reason?: string): void;
10
+ readonly readyState: number;
11
+ readonly id: string;
12
+ }
13
+
14
+ /**
15
+ * WebSocket message that can be sent/received
16
+ */
17
+
18
+ export const webSocketMessageSchema = z.union([
19
+ z.object({
20
+ type: z.literal("upload_event"),
21
+ payload: uploadEventSchema,
22
+ timestamp: z.string().optional(),
23
+ }),
24
+ z.object({
25
+ type: z.literal("flow_event"),
26
+ payload: z.any(), // FlowEvent doesn't have a zod schema, using z.any() for now
27
+ timestamp: z.string().optional(),
28
+ }),
29
+ z.object({
30
+ type: z.literal("subscribed"),
31
+ payload: z.object({ eventKey: z.string() }),
32
+ timestamp: z.string().optional(),
33
+ }),
34
+ z.object({
35
+ type: z.literal("error"),
36
+ message: z.string().optional(),
37
+ }),
38
+ z.object({
39
+ type: z.literal("pong"),
40
+ timestamp: z.string().optional(),
41
+ }),
42
+ z.object({
43
+ type: z.literal("ping"),
44
+ timestamp: z.string().optional(),
45
+ }),
46
+ z.object({
47
+ type: z.literal("connection"),
48
+ message: z.string().optional(),
49
+ uploadId: z.string().optional(),
50
+ timestamp: z.string().optional(),
51
+ }),
52
+ ]);
53
+
54
+ export type WebSocketMessage<TEvent = unknown> =
55
+ | z.infer<typeof webSocketMessageSchema>
56
+ | {
57
+ type: "upload_event";
58
+ payload: TEvent;
59
+ timestamp?: string;
60
+ }
61
+ | {
62
+ type: "flow_event";
63
+ payload: TEvent;
64
+ timestamp?: string;
65
+ };