@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,614 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { UploadistaError } from "../errors";
3
+ import { createFlowWithSchema, EventType, getFlowData, runArgsSchema, } from "../flow";
4
+ import { FlowEventEmitter, FlowJobKVStore } from "../types";
5
+ import { UploadServer } from "../upload";
6
+ /**
7
+ * Effect-TS context tag for the FlowProvider service.
8
+ *
9
+ * Applications must provide an implementation of FlowProviderShape
10
+ * to enable the FlowServer to retrieve flow definitions.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // Access FlowProvider in an Effect
15
+ * const effect = Effect.gen(function* () {
16
+ * const provider = yield* FlowProvider;
17
+ * const flow = yield* provider.getFlow("flow123", "client456");
18
+ * return flow;
19
+ * });
20
+ * ```
21
+ */
22
+ export class FlowProvider extends Context.Tag("FlowProvider")() {
23
+ }
24
+ /**
25
+ * Effect-TS context tag for the FlowServer service.
26
+ *
27
+ * Use this tag to access the FlowServer in an Effect context.
28
+ * The server must be provided via a Layer or dependency injection.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // Access FlowServer in an Effect
33
+ * const flowEffect = Effect.gen(function* () {
34
+ * const server = yield* FlowServer;
35
+ * const job = yield* server.runFlow({
36
+ * flowId: "my-flow",
37
+ * storageId: "s3",
38
+ * clientId: null,
39
+ * inputs: {}
40
+ * });
41
+ * return job;
42
+ * });
43
+ *
44
+ * // Provide FlowServer layer
45
+ * const program = flowEffect.pipe(
46
+ * Effect.provide(flowServer),
47
+ * Effect.provide(flowProviderLayer),
48
+ * Effect.provide(flowJobKvStore)
49
+ * );
50
+ * ```
51
+ */
52
+ export class FlowServer extends Context.Tag("FlowServer")() {
53
+ }
54
+ const isResultUploadFile = (result) => {
55
+ return typeof result === "object" && result !== null && "id" in result;
56
+ };
57
+ // Function to enhance a flow with event emission capabilities
58
+ function withFlowEvents(flow, eventEmitter, kvStore) {
59
+ // Shared helper to create onEvent callback for a given jobId
60
+ const createOnEventCallback = (executionJobId) => {
61
+ // Helper to update job in KV store
62
+ const updateJobInStore = (updates) => Effect.gen(function* () {
63
+ const job = yield* kvStore.get(executionJobId);
64
+ if (job) {
65
+ yield* kvStore.set(executionJobId, {
66
+ ...job,
67
+ ...updates,
68
+ updatedAt: new Date(),
69
+ });
70
+ }
71
+ });
72
+ // Create the onEvent callback that calls original onEvent, emits to eventEmitter, and updates job
73
+ return (event) => Effect.gen(function* () {
74
+ // Call the original onEvent from the flow if it exists
75
+ // Catch errors to prevent them from blocking flow execution
76
+ if (flow.onEvent) {
77
+ yield* Effect.catchAll(flow.onEvent(event), (error) => {
78
+ // Log the error but don't fail the flow
79
+ Effect.logError("Original onEvent failed", error);
80
+ return Effect.succeed({ eventId: null });
81
+ });
82
+ }
83
+ // Emit event
84
+ yield* eventEmitter.emit(executionJobId, event);
85
+ Effect.logInfo(`Updating job ${executionJobId} with event ${event.eventType}`);
86
+ // Update job based on event type
87
+ switch (event.eventType) {
88
+ case EventType.FlowStart:
89
+ yield* updateJobInStore({ status: "running" });
90
+ break;
91
+ case EventType.FlowEnd:
92
+ // Flow end is handled by executeFlowInBackground
93
+ // This case ensures the event is still emitted
94
+ break;
95
+ case EventType.FlowError:
96
+ yield* updateJobInStore({
97
+ status: "failed",
98
+ error: event.error,
99
+ });
100
+ break;
101
+ case EventType.NodeStart:
102
+ yield* Effect.gen(function* () {
103
+ const job = yield* kvStore.get(executionJobId);
104
+ if (job) {
105
+ const existingTask = job.tasks.find((t) => t.nodeId === event.nodeId);
106
+ const updatedTasks = existingTask
107
+ ? job.tasks.map((t) => t.nodeId === event.nodeId
108
+ ? {
109
+ ...t,
110
+ status: "running",
111
+ updatedAt: new Date(),
112
+ }
113
+ : t)
114
+ : [
115
+ ...job.tasks,
116
+ {
117
+ nodeId: event.nodeId,
118
+ status: "running",
119
+ createdAt: new Date(),
120
+ updatedAt: new Date(),
121
+ },
122
+ ];
123
+ yield* kvStore.set(executionJobId, {
124
+ ...job,
125
+ tasks: updatedTasks,
126
+ updatedAt: new Date(),
127
+ });
128
+ }
129
+ });
130
+ break;
131
+ case EventType.NodePause:
132
+ yield* Effect.gen(function* () {
133
+ const job = yield* kvStore.get(executionJobId);
134
+ if (job) {
135
+ const existingTask = job.tasks.find((t) => t.nodeId === event.nodeId);
136
+ const updatedTasks = existingTask
137
+ ? job.tasks.map((t) => t.nodeId === event.nodeId
138
+ ? {
139
+ ...t,
140
+ status: "paused",
141
+ result: event.partialData,
142
+ updatedAt: new Date(),
143
+ }
144
+ : t)
145
+ : [
146
+ ...job.tasks,
147
+ {
148
+ nodeId: event.nodeId,
149
+ status: "paused",
150
+ result: event.partialData,
151
+ createdAt: new Date(),
152
+ updatedAt: new Date(),
153
+ },
154
+ ];
155
+ yield* kvStore.set(executionJobId, {
156
+ ...job,
157
+ tasks: updatedTasks,
158
+ updatedAt: new Date(),
159
+ });
160
+ }
161
+ });
162
+ break;
163
+ case EventType.NodeResume:
164
+ yield* Effect.gen(function* () {
165
+ const job = yield* kvStore.get(executionJobId);
166
+ if (job) {
167
+ const updatedTasks = job.tasks.map((t) => t.nodeId === event.nodeId
168
+ ? {
169
+ ...t,
170
+ status: "running",
171
+ updatedAt: new Date(),
172
+ }
173
+ : t);
174
+ yield* kvStore.set(executionJobId, {
175
+ ...job,
176
+ tasks: updatedTasks,
177
+ updatedAt: new Date(),
178
+ });
179
+ }
180
+ });
181
+ break;
182
+ case EventType.NodeEnd:
183
+ yield* Effect.gen(function* () {
184
+ const job = yield* kvStore.get(executionJobId);
185
+ if (job) {
186
+ const updatedTasks = job.tasks.map((t) => t.nodeId === event.nodeId
187
+ ? {
188
+ ...t,
189
+ status: "completed",
190
+ result: event.result,
191
+ updatedAt: new Date(),
192
+ }
193
+ : t);
194
+ // Track intermediate files for cleanup
195
+ // Check if result is an UploadFile and node is not an output node
196
+ const node = flow.nodes.find((n) => n.id === event.nodeId);
197
+ const isOutputNode = node?.type === "output";
198
+ const result = event.result;
199
+ let intermediateFiles = job.intermediateFiles || [];
200
+ if (isOutputNode && isResultUploadFile(result) && result.id) {
201
+ // If this is an output node and it returns a file that was an intermediate file,
202
+ // remove it from the intermediate files list (it's now the final output)
203
+ intermediateFiles = intermediateFiles.filter((fileId) => fileId !== result.id);
204
+ }
205
+ else if (!isOutputNode &&
206
+ isResultUploadFile(result) &&
207
+ result.id) {
208
+ // Only add to intermediate files if it's not an output node
209
+ if (!intermediateFiles.includes(result.id)) {
210
+ intermediateFiles.push(result.id);
211
+ }
212
+ }
213
+ yield* kvStore.set(executionJobId, {
214
+ ...job,
215
+ tasks: updatedTasks,
216
+ intermediateFiles,
217
+ updatedAt: new Date(),
218
+ });
219
+ }
220
+ });
221
+ break;
222
+ case EventType.NodeError:
223
+ yield* Effect.gen(function* () {
224
+ const job = yield* kvStore.get(executionJobId);
225
+ if (job) {
226
+ const updatedTasks = job.tasks.map((t) => t.nodeId === event.nodeId
227
+ ? {
228
+ ...t,
229
+ status: "failed",
230
+ error: event.error,
231
+ retryCount: event.retryCount,
232
+ updatedAt: new Date(),
233
+ }
234
+ : t);
235
+ yield* kvStore.set(executionJobId, {
236
+ ...job,
237
+ tasks: updatedTasks,
238
+ error: event.error,
239
+ updatedAt: new Date(),
240
+ });
241
+ }
242
+ });
243
+ break;
244
+ }
245
+ return { eventId: executionJobId };
246
+ });
247
+ };
248
+ return {
249
+ ...flow,
250
+ run: (args) => {
251
+ return Effect.gen(function* () {
252
+ // Use provided jobId or generate a new one
253
+ const executionJobId = args.jobId || crypto.randomUUID();
254
+ const onEventCallback = createOnEventCallback(executionJobId);
255
+ // Create a new flow with the same configuration but with onEvent callback
256
+ const flowWithEvents = yield* createFlowWithSchema({
257
+ flowId: flow.id,
258
+ name: flow.name,
259
+ nodes: flow.nodes,
260
+ edges: flow.edges,
261
+ inputSchema: flow.inputSchema,
262
+ outputSchema: flow.outputSchema,
263
+ onEvent: onEventCallback,
264
+ });
265
+ // Run the enhanced flow with consistent jobId
266
+ const result = yield* flowWithEvents.run({
267
+ ...args,
268
+ jobId: executionJobId,
269
+ clientId: args.clientId,
270
+ });
271
+ // Return the result directly (can be completed or paused)
272
+ return result;
273
+ });
274
+ },
275
+ resume: (args) => {
276
+ return Effect.gen(function* () {
277
+ const executionJobId = args.jobId;
278
+ const onEventCallback = createOnEventCallback(executionJobId);
279
+ // Create a new flow with the same configuration but with onEvent callback
280
+ const flowWithEvents = yield* createFlowWithSchema({
281
+ flowId: flow.id,
282
+ name: flow.name,
283
+ nodes: flow.nodes,
284
+ edges: flow.edges,
285
+ inputSchema: flow.inputSchema,
286
+ outputSchema: flow.outputSchema,
287
+ onEvent: onEventCallback,
288
+ });
289
+ // Resume the enhanced flow
290
+ const result = yield* flowWithEvents.resume(args);
291
+ // Return the result directly (can be completed or paused)
292
+ return result;
293
+ });
294
+ },
295
+ };
296
+ }
297
+ // Core FlowServer implementation
298
+ export function createFlowServer() {
299
+ return Effect.gen(function* () {
300
+ const flowProvider = yield* FlowProvider;
301
+ const eventEmitter = yield* FlowEventEmitter;
302
+ const kvStore = yield* FlowJobKVStore;
303
+ const uploadServer = yield* UploadServer;
304
+ const updateJob = (jobId, updates) => Effect.gen(function* () {
305
+ const job = yield* kvStore.get(jobId);
306
+ if (!job) {
307
+ return yield* Effect.fail(UploadistaError.fromCode("FLOW_JOB_NOT_FOUND", {
308
+ cause: `Job ${jobId} not found`,
309
+ }));
310
+ }
311
+ return yield* kvStore.set(jobId, { ...job, ...updates });
312
+ });
313
+ // Helper function to cleanup intermediate files
314
+ const cleanupIntermediateFiles = (jobId, clientId) => Effect.gen(function* () {
315
+ const job = yield* kvStore.get(jobId);
316
+ if (!job ||
317
+ !job.intermediateFiles ||
318
+ job.intermediateFiles.length === 0) {
319
+ return;
320
+ }
321
+ yield* Effect.logInfo(`Cleaning up ${job.intermediateFiles.length} intermediate files for job ${jobId}`);
322
+ // Delete each intermediate file
323
+ yield* Effect.all(job.intermediateFiles.map((fileId) => Effect.gen(function* () {
324
+ yield* uploadServer.delete(fileId, clientId);
325
+ yield* Effect.logDebug(`Deleted intermediate file ${fileId}`);
326
+ }).pipe(Effect.catchAll((error) => Effect.gen(function* () {
327
+ yield* Effect.logWarning(`Failed to delete intermediate file ${fileId}: ${error}`);
328
+ return Effect.succeed(undefined);
329
+ })))), { concurrency: 5 });
330
+ // Clear the intermediateFiles array
331
+ yield* updateJob(jobId, {
332
+ intermediateFiles: [],
333
+ });
334
+ });
335
+ // Helper function to execute flow in background
336
+ const executeFlowInBackground = ({ jobId, flow, storageId, clientId, inputs, }) => Effect.gen(function* () {
337
+ // Update job status to running
338
+ yield* updateJob(jobId, {
339
+ status: "running",
340
+ });
341
+ const flowWithEvents = withFlowEvents(flow, eventEmitter, kvStore);
342
+ // Run the flow with the consistent jobId
343
+ const result = yield* flowWithEvents.run({
344
+ inputs,
345
+ storageId,
346
+ jobId,
347
+ clientId,
348
+ });
349
+ // Handle result based on type
350
+ if (result.type === "paused") {
351
+ // Update job as paused (node results are in tasks, not executionState)
352
+ yield* updateJob(jobId, {
353
+ status: "paused",
354
+ pausedAt: result.nodeId,
355
+ executionState: result.executionState,
356
+ updatedAt: new Date(),
357
+ });
358
+ }
359
+ else {
360
+ // Update job as completed with final result
361
+ yield* updateJob(jobId, {
362
+ status: "completed",
363
+ result: result.result,
364
+ updatedAt: new Date(),
365
+ endedAt: new Date(),
366
+ });
367
+ // Cleanup intermediate files
368
+ yield* cleanupIntermediateFiles(jobId, clientId);
369
+ }
370
+ return result;
371
+ }).pipe(Effect.catchAll((error) => Effect.gen(function* () {
372
+ yield* Effect.logError("Flow execution failed", error);
373
+ // Convert error to a proper message
374
+ const errorMessage = error instanceof UploadistaError ? error.body : String(error);
375
+ yield* Effect.logInfo(`Updating job ${jobId} to failed status with error: ${errorMessage}`);
376
+ // Update job as failed - do this FIRST before cleanup
377
+ yield* updateJob(jobId, {
378
+ status: "failed",
379
+ error: errorMessage,
380
+ updatedAt: new Date(),
381
+ }).pipe(Effect.catchAll((updateError) => Effect.gen(function* () {
382
+ yield* Effect.logError(`Failed to update job ${jobId}`, updateError);
383
+ return Effect.succeed(undefined);
384
+ })));
385
+ // Emit FlowError event to notify client
386
+ const job = yield* kvStore.get(jobId);
387
+ if (job) {
388
+ yield* eventEmitter
389
+ .emit(jobId, {
390
+ jobId,
391
+ eventType: EventType.FlowError,
392
+ flowId: job.flowId,
393
+ error: errorMessage,
394
+ })
395
+ .pipe(Effect.catchAll((emitError) => Effect.gen(function* () {
396
+ yield* Effect.logError(`Failed to emit FlowError event for job ${jobId}`, emitError);
397
+ return Effect.succeed(undefined);
398
+ })));
399
+ }
400
+ // Cleanup intermediate files even on failure (don't let this fail the error handling)
401
+ yield* cleanupIntermediateFiles(jobId, clientId).pipe(Effect.catchAll((cleanupError) => Effect.gen(function* () {
402
+ yield* Effect.logWarning(`Failed to cleanup intermediate files for job ${jobId}`, cleanupError);
403
+ return Effect.succeed(undefined);
404
+ })));
405
+ return Effect.fail(error);
406
+ })));
407
+ return {
408
+ getFlow: (flowId, clientId) => Effect.gen(function* () {
409
+ const flow = yield* flowProvider.getFlow(flowId, clientId);
410
+ return flow;
411
+ }),
412
+ getFlowData: (flowId, clientId) => Effect.gen(function* () {
413
+ const flow = yield* flowProvider.getFlow(flowId, clientId);
414
+ return getFlowData(flow);
415
+ }),
416
+ runFlow: ({ flowId, storageId, clientId, inputs, }) => Effect.gen(function* () {
417
+ const parsedParams = yield* Effect.try({
418
+ try: () => runArgsSchema.parse({ inputs }),
419
+ catch: (error) => UploadistaError.fromCode("FLOW_INPUT_VALIDATION_ERROR", {
420
+ cause: error,
421
+ }),
422
+ });
423
+ // Generate a unique jobId
424
+ const jobId = crypto.randomUUID();
425
+ const createdAt = new Date();
426
+ // Store initial job metadata
427
+ const job = {
428
+ id: jobId,
429
+ flowId,
430
+ storageId,
431
+ clientId,
432
+ status: "started",
433
+ createdAt,
434
+ updatedAt: createdAt,
435
+ tasks: [],
436
+ };
437
+ yield* kvStore.set(jobId, job);
438
+ // Get the flow and start background execution
439
+ const flow = yield* flowProvider.getFlow(flowId, clientId);
440
+ // Fork the flow execution to run in background as daemon
441
+ yield* Effect.forkDaemon(executeFlowInBackground({
442
+ jobId,
443
+ flow,
444
+ storageId,
445
+ clientId,
446
+ inputs: parsedParams.inputs,
447
+ }).pipe(Effect.tapErrorCause((cause) => Effect.logError("Flow execution failed", cause))));
448
+ // Return immediately with jobId
449
+ return job;
450
+ }),
451
+ getJobStatus: (jobId) => Effect.gen(function* () {
452
+ const job = yield* kvStore.get(jobId);
453
+ if (!job) {
454
+ return yield* Effect.fail(UploadistaError.fromCode("FLOW_JOB_NOT_FOUND", {
455
+ cause: `Job ${jobId} not found`,
456
+ }));
457
+ }
458
+ return job;
459
+ }),
460
+ continueFlow: ({ jobId, nodeId, newData, clientId, }) => Effect.gen(function* () {
461
+ console.log("continueFlow", jobId, nodeId, newData);
462
+ // Get the current job
463
+ const job = yield* kvStore.get(jobId);
464
+ if (!job) {
465
+ console.error("Job not found");
466
+ return yield* Effect.fail(UploadistaError.fromCode("FLOW_JOB_NOT_FOUND", {
467
+ cause: `Job ${jobId} not found`,
468
+ }));
469
+ }
470
+ // Verify job is paused
471
+ if (job.status !== "paused") {
472
+ console.error("Job is not paused");
473
+ return yield* Effect.fail(UploadistaError.fromCode("FLOW_JOB_ERROR", {
474
+ cause: `Job ${jobId} is not paused (status: ${job.status})`,
475
+ }));
476
+ }
477
+ // Verify it's paused at the expected node
478
+ if (job.pausedAt !== nodeId) {
479
+ console.error("Job is not paused at the expected node");
480
+ return yield* Effect.fail(UploadistaError.fromCode("FLOW_JOB_ERROR", {
481
+ cause: `Job ${jobId} is paused at node ${job.pausedAt}, not ${nodeId}`,
482
+ }));
483
+ }
484
+ // Verify we have execution state
485
+ if (!job.executionState) {
486
+ console.error("Job has no execution state");
487
+ return yield* Effect.fail(UploadistaError.fromCode("FLOW_JOB_ERROR", {
488
+ cause: `Job ${jobId} has no execution state`,
489
+ }));
490
+ }
491
+ // Reconstruct nodeResults from tasks
492
+ const nodeResults = job.tasks.reduce((acc, task) => {
493
+ if (task.result !== undefined) {
494
+ acc[task.nodeId] = task.result;
495
+ }
496
+ return acc;
497
+ }, {});
498
+ // Update with new data
499
+ const updatedNodeResults = {
500
+ ...nodeResults,
501
+ [nodeId]: newData,
502
+ };
503
+ const updatedInputs = {
504
+ ...job.executionState.inputs,
505
+ [nodeId]: newData,
506
+ };
507
+ // Update job status to running BEFORE forking background execution
508
+ // This ensures the status is updated synchronously before events start firing
509
+ yield* updateJob(jobId, {
510
+ status: "running",
511
+ });
512
+ // Get the flow
513
+ const flow = yield* flowProvider.getFlow(job.flowId, job.clientId);
514
+ // Helper to resume flow in background
515
+ const resumeFlowInBackground = Effect.gen(function* () {
516
+ const flowWithEvents = withFlowEvents(flow, eventEmitter, kvStore);
517
+ if (!job.executionState) {
518
+ return yield* Effect.fail(UploadistaError.fromCode("FLOW_JOB_ERROR", {
519
+ cause: `Job ${jobId} has no execution state`,
520
+ }));
521
+ }
522
+ // Resume the flow with updated state
523
+ const result = yield* flowWithEvents.resume({
524
+ jobId,
525
+ storageId: job.storageId,
526
+ nodeResults: updatedNodeResults,
527
+ executionState: {
528
+ ...job.executionState,
529
+ inputs: updatedInputs,
530
+ },
531
+ clientId: job.clientId,
532
+ });
533
+ // Handle result based on type
534
+ if (result.type === "paused") {
535
+ // Update job as paused again (node results are in tasks, not executionState)
536
+ yield* updateJob(jobId, {
537
+ status: "paused",
538
+ pausedAt: result.nodeId,
539
+ executionState: result.executionState,
540
+ updatedAt: new Date(),
541
+ });
542
+ }
543
+ else {
544
+ // Update job as completed with final result
545
+ yield* updateJob(jobId, {
546
+ status: "completed",
547
+ pausedAt: undefined,
548
+ executionState: undefined,
549
+ result: result.result,
550
+ updatedAt: new Date(),
551
+ endedAt: new Date(),
552
+ });
553
+ // Cleanup intermediate files
554
+ yield* cleanupIntermediateFiles(jobId, clientId);
555
+ }
556
+ return result;
557
+ }).pipe(Effect.catchAll((error) => Effect.gen(function* () {
558
+ yield* Effect.logError("Flow resume failed", error);
559
+ // Convert error to a proper message
560
+ const errorMessage = error instanceof UploadistaError ? error.body : String(error);
561
+ yield* Effect.logInfo(`Updating job ${jobId} to failed status with error: ${errorMessage}`);
562
+ // Update job as failed - do this FIRST before cleanup
563
+ yield* updateJob(jobId, {
564
+ status: "failed",
565
+ error: errorMessage,
566
+ updatedAt: new Date(),
567
+ }).pipe(Effect.catchAll((updateError) => Effect.gen(function* () {
568
+ yield* Effect.logError(`Failed to update job ${jobId}`, updateError);
569
+ return Effect.succeed(undefined);
570
+ })));
571
+ // Emit FlowError event to notify client
572
+ const currentJob = yield* kvStore.get(jobId);
573
+ if (currentJob) {
574
+ yield* eventEmitter
575
+ .emit(jobId, {
576
+ jobId,
577
+ eventType: EventType.FlowError,
578
+ flowId: currentJob.flowId,
579
+ error: errorMessage,
580
+ })
581
+ .pipe(Effect.catchAll((emitError) => Effect.gen(function* () {
582
+ yield* Effect.logError(`Failed to emit FlowError event for job ${jobId}`, emitError);
583
+ return Effect.succeed(undefined);
584
+ })));
585
+ }
586
+ // Cleanup intermediate files even on failure (don't let this fail the error handling)
587
+ yield* cleanupIntermediateFiles(jobId, clientId).pipe(Effect.catchAll((cleanupError) => Effect.gen(function* () {
588
+ yield* Effect.logWarning(`Failed to cleanup intermediate files for job ${jobId}`, cleanupError);
589
+ return Effect.succeed(undefined);
590
+ })));
591
+ return Effect.fail(error);
592
+ })));
593
+ // Fork the resume execution to run in background as daemon
594
+ yield* Effect.forkDaemon(resumeFlowInBackground.pipe(Effect.tapErrorCause((cause) => Effect.logError("Flow resume failed", cause))));
595
+ // Return immediately with updated job
596
+ const updatedJob = yield* kvStore.get(jobId);
597
+ if (!updatedJob) {
598
+ return yield* Effect.fail(UploadistaError.fromCode("FLOW_JOB_NOT_FOUND", {
599
+ cause: `Job ${jobId} not found after update`,
600
+ }));
601
+ }
602
+ return updatedJob;
603
+ }),
604
+ subscribeToFlowEvents: (jobId, connection) => Effect.gen(function* () {
605
+ yield* eventEmitter.subscribe(jobId, connection);
606
+ }),
607
+ unsubscribeFromFlowEvents: (jobId) => Effect.gen(function* () {
608
+ yield* eventEmitter.unsubscribe(jobId);
609
+ }),
610
+ };
611
+ });
612
+ }
613
+ // Export the FlowServer layer with job store dependency
614
+ export const flowServer = Layer.effect(FlowServer, createFlowServer());