@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,128 @@
1
+ import { Effect } from "effect";
2
+ import { z } from "zod";
3
+ import { UploadistaError } from "../../errors";
4
+ import { type UploadFile, uploadFileSchema } from "../../types";
5
+ import { UploadServer } from "../../upload";
6
+ import { createFlowNode, NodeType } from "../node";
7
+ import { completeNodeExecution } from "../types";
8
+ import { resolveUploadMetadata } from "../utils/resolve-upload-metadata";
9
+
10
+ /**
11
+ * Schema for storage node parameters.
12
+ * Currently empty but can be extended for storage-specific configuration.
13
+ */
14
+ export const storageParamsSchema = z.object({});
15
+
16
+ /**
17
+ * Parameters for the storage node.
18
+ * Currently no parameters are required, but the schema is available for future extensions.
19
+ */
20
+ export type StorageParams = z.infer<typeof storageParamsSchema>;
21
+
22
+ /**
23
+ * Creates a storage node for storing files in the specified storage.
24
+ *
25
+ * The storage node handles the process of:
26
+ * 1. Reading the input file from the upload server
27
+ * 2. Checking if the file is already in the target storage
28
+ * 3. If not, transferring the file to the target storage
29
+ * 4. Applying optional post-processing
30
+ * 5. Returning the final stored file
31
+ *
32
+ * @param id - Unique identifier for the node
33
+ * @param postProcessFile - Optional function to process the file after storage
34
+ * @returns An Effect that creates a flow node configured for file storage
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * // Create basic storage node
39
+ * const storageNode = yield* createStorageNode("store-file");
40
+ *
41
+ * // Create storage node with post-processing
42
+ * const storageWithProcessing = yield* createStorageNode("store-and-process", (file) => {
43
+ * return Effect.succeed({
44
+ * ...file,
45
+ * metadata: { ...file.metadata, processed: true }
46
+ * });
47
+ * });
48
+ * ```
49
+ */
50
+ export function createStorageNode(
51
+ id: string,
52
+ postProcessFile: (file: UploadFile) => Effect.Effect<UploadFile> = (file) =>
53
+ Effect.succeed(file)
54
+ ) {
55
+ return Effect.gen(function* () {
56
+ const uploadServer = yield* UploadServer;
57
+ return yield* createFlowNode({
58
+ id,
59
+ name: "Storage",
60
+ description: "Stores a file in the storage",
61
+ type: NodeType.output,
62
+ inputSchema: uploadFileSchema,
63
+ outputSchema: uploadFileSchema,
64
+ run: ({ data: file, storageId, flowId, jobId, clientId }) => {
65
+ return Effect.gen(function* () {
66
+ const { type, fileName, metadata, metadataJson } =
67
+ resolveUploadMetadata(file.metadata);
68
+ const flow = {
69
+ flowId,
70
+ nodeId: id,
71
+ jobId,
72
+ };
73
+ const normalizedFile = metadata ? { ...file, metadata } : file;
74
+
75
+ const upload = yield* uploadServer.getUpload(file.id);
76
+ if (!upload.id) {
77
+ return yield* Effect.fail(
78
+ UploadistaError.fromCode(
79
+ "FILE_READ_ERROR",
80
+ new Error("Upload Key is undefined")
81
+ )
82
+ );
83
+ }
84
+ // If the upload is already in the correct storage, return the file, just update the flow
85
+ if (upload.storage.id === storageId) {
86
+ return completeNodeExecution(
87
+ yield* postProcessFile({ ...normalizedFile, flow })
88
+ );
89
+ }
90
+
91
+ const inputBytes = yield* uploadServer.read(file.id, clientId);
92
+ const stream = new ReadableStream({
93
+ start(controller) {
94
+ controller.enqueue(inputBytes);
95
+ controller.close();
96
+ },
97
+ });
98
+
99
+ const uploadResult = yield* uploadServer.upload(
100
+ {
101
+ storageId,
102
+ size: inputBytes.byteLength,
103
+ type,
104
+ fileName,
105
+ lastModified: 0,
106
+ metadata: metadataJson,
107
+ flow,
108
+ },
109
+ clientId,
110
+ stream
111
+ );
112
+
113
+ const resolvedUploadResult = resolveUploadMetadata(
114
+ uploadResult.metadata
115
+ );
116
+
117
+ const postProcessed = yield* postProcessFile(
118
+ resolvedUploadResult.metadata
119
+ ? { ...uploadResult, metadata: resolvedUploadResult.metadata }
120
+ : uploadResult
121
+ );
122
+
123
+ return completeNodeExecution(postProcessed);
124
+ });
125
+ },
126
+ });
127
+ });
128
+ }
@@ -0,0 +1,154 @@
1
+ import { Effect } from "effect";
2
+ import type { UploadistaError } from "../../errors";
3
+ import type { UploadFile } from "../../types";
4
+ import { uploadFileSchema } from "../../types";
5
+ import { UploadServer } from "../../upload";
6
+ import { createFlowNode, NodeType } from "../node";
7
+ import { completeNodeExecution } from "../types";
8
+ import { resolveUploadMetadata } from "../utils/resolve-upload-metadata";
9
+
10
+ /**
11
+ * Configuration object for creating a transform node.
12
+ */
13
+ export interface TransformNodeConfig {
14
+ /** Unique identifier for the node */
15
+ id: string;
16
+ /** Human-readable name for the node */
17
+ name: string;
18
+ /** Description of what the node does */
19
+ description: string;
20
+ /** Function that transforms file bytes */
21
+ transform: (
22
+ bytes: Uint8Array,
23
+ file: UploadFile
24
+ ) => Effect.Effect<
25
+ Uint8Array | { bytes: Uint8Array; type?: string; fileName?: string },
26
+ UploadistaError
27
+ >;
28
+ }
29
+
30
+ /**
31
+ * Creates a transform node that handles the common pattern of:
32
+ * 1. Reading bytes from an UploadFile
33
+ * 2. Transforming the bytes
34
+ * 3. Uploading the result as a new UploadFile
35
+ *
36
+ * This simplifies nodes that just need to transform file bytes without
37
+ * worrying about upload server interactions.
38
+ *
39
+ * @param config - Configuration object for the transform node
40
+ * @returns An Effect that creates a flow node configured for file transformation
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * // Create an image resize transform node
45
+ * const resizeNode = yield* createTransformNode({
46
+ * id: "resize-image",
47
+ * name: "Resize Image",
48
+ * description: "Resizes images to specified dimensions",
49
+ * transform: (bytes, file) => {
50
+ * // Your transformation logic here
51
+ * return Effect.succeed(transformedBytes);
52
+ * }
53
+ * });
54
+ *
55
+ * // Create a transform node that changes file metadata
56
+ * const metadataTransformNode = yield* createTransformNode({
57
+ * id: "add-metadata",
58
+ * name: "Add Metadata",
59
+ * description: "Adds custom metadata to files",
60
+ * transform: (bytes, file) => {
61
+ * return Effect.succeed({
62
+ * bytes,
63
+ * type: "application/custom",
64
+ * fileName: `processed-${file.fileName}`
65
+ * });
66
+ * }
67
+ * });
68
+ * ```
69
+ */
70
+ export function createTransformNode({
71
+ id,
72
+ name,
73
+ description,
74
+ transform,
75
+ }: TransformNodeConfig) {
76
+ return Effect.gen(function* () {
77
+ const uploadServer = yield* UploadServer;
78
+
79
+ return yield* createFlowNode({
80
+ id,
81
+ name,
82
+ description,
83
+ type: NodeType.process,
84
+ inputSchema: uploadFileSchema,
85
+ outputSchema: uploadFileSchema,
86
+ run: ({ data: file, storageId, flowId, jobId, clientId }) => {
87
+ return Effect.gen(function* () {
88
+ const flow = {
89
+ flowId,
90
+ nodeId: id,
91
+ jobId,
92
+ };
93
+ // Read input bytes from upload server
94
+ const inputBytes = yield* uploadServer.read(file.id, clientId);
95
+
96
+ // Transform the bytes using the provided function
97
+ const transformResult = yield* transform(inputBytes, file);
98
+
99
+ // Handle both simple Uint8Array and object with metadata
100
+ const outputBytes =
101
+ transformResult instanceof Uint8Array
102
+ ? transformResult
103
+ : transformResult.bytes;
104
+
105
+ const outputType =
106
+ transformResult instanceof Uint8Array
107
+ ? undefined
108
+ : transformResult.type;
109
+
110
+ const outputFileName =
111
+ transformResult instanceof Uint8Array
112
+ ? undefined
113
+ : transformResult.fileName;
114
+
115
+ // Create a stream from the output bytes
116
+ const stream = new ReadableStream({
117
+ start(controller) {
118
+ controller.enqueue(outputBytes);
119
+ controller.close();
120
+ },
121
+ });
122
+
123
+ const { type, fileName, metadata, metadataJson } =
124
+ resolveUploadMetadata(file.metadata);
125
+
126
+ // Upload the transformed bytes back to the upload server
127
+ // Use output metadata if provided, otherwise fall back to original
128
+ const result = yield* uploadServer.upload(
129
+ {
130
+ storageId,
131
+ size: outputBytes.byteLength,
132
+ type: outputType ?? type,
133
+ fileName: outputFileName ?? fileName,
134
+ lastModified: 0,
135
+ metadata: metadataJson,
136
+ flow,
137
+ },
138
+ clientId,
139
+ stream
140
+ );
141
+
142
+ return completeNodeExecution(
143
+ metadata
144
+ ? {
145
+ ...result,
146
+ metadata,
147
+ }
148
+ : result
149
+ );
150
+ });
151
+ },
152
+ });
153
+ });
154
+ }
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Parallel execution scheduler for flow nodes.
3
+ *
4
+ * The ParallelScheduler analyzes flow dependencies and groups nodes into execution
5
+ * levels where nodes at the same level can run in parallel. It manages concurrency
6
+ * using Effect's built-in concurrency control to prevent resource exhaustion.
7
+ *
8
+ * @module flow/parallel-scheduler
9
+ * @see {@link ParallelScheduler} for the main scheduler class
10
+ *
11
+ * @remarks
12
+ * This scheduler groups nodes by execution level (respecting dependencies) and executes
13
+ * each level in parallel with controlled concurrency. Levels are executed sequentially
14
+ * to ensure dependencies are satisfied before dependent nodes execute.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const scheduler = new ParallelScheduler({ maxConcurrency: 4 });
19
+ *
20
+ * // Group nodes by execution level
21
+ * const levels = scheduler.groupNodesByExecutionLevel(nodes, edges);
22
+ *
23
+ * // Execute nodes in a level with Effect
24
+ * const results = yield* scheduler.executeNodesInParallel([
25
+ * () => executeNode("node1"),
26
+ * () => executeNode("node2"),
27
+ * () => executeNode("node3")
28
+ * ]);
29
+ * ```
30
+ */
31
+
32
+ import { Effect } from "effect";
33
+ import type { FlowNode } from "./types/flow-types";
34
+
35
+ /**
36
+ * Represents a level in the execution hierarchy where all nodes can run in parallel.
37
+ *
38
+ * @property level - The execution level (0 = first to execute, higher = later)
39
+ * @property nodes - Array of node IDs that can execute in parallel at this level
40
+ *
41
+ * @example
42
+ * ```
43
+ * Level 0: [input_node] (no dependencies)
44
+ * Level 1: [resize, optimize] (all depend on level 0)
45
+ * Level 2: [storage] (depends on level 1)
46
+ * ```
47
+ */
48
+ export interface ExecutionLevel {
49
+ level: number;
50
+ nodes: string[];
51
+ }
52
+
53
+ /**
54
+ * Configuration options for the ParallelScheduler.
55
+ *
56
+ * @property maxConcurrency - Maximum number of nodes to execute in parallel (default: 4)
57
+ * Controls how many nodes run simultaneously within a level
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const scheduler = new ParallelScheduler({ maxConcurrency: 8 });
62
+ * ```
63
+ */
64
+ export interface ParallelSchedulerConfig {
65
+ maxConcurrency?: number;
66
+ }
67
+
68
+ /**
69
+ * Scheduler for executing flow nodes in parallel while respecting dependencies.
70
+ *
71
+ * The scheduler performs topological sorting to identify nodes that can run
72
+ * concurrently, groups them into execution levels, and provides methods to
73
+ * execute them with controlled concurrency using Effect.
74
+ *
75
+ * Key responsibilities:
76
+ * - Analyze flow dependencies and detect cycles
77
+ * - Group nodes into parallel execution levels
78
+ * - Execute levels in parallel with concurrency limits
79
+ * - Provide utilities to check parallel execution feasibility
80
+ */
81
+ export class ParallelScheduler {
82
+ private maxConcurrency: number;
83
+
84
+ /**
85
+ * Creates a new ParallelScheduler instance.
86
+ *
87
+ * @param config - Configuration for the scheduler
88
+ * @example
89
+ * ```typescript
90
+ * const scheduler = new ParallelScheduler({ maxConcurrency: 4 });
91
+ * ```
92
+ */
93
+ constructor(config: ParallelSchedulerConfig = {}) {
94
+ this.maxConcurrency = config.maxConcurrency ?? 4;
95
+ }
96
+
97
+ /**
98
+ * Groups nodes into execution levels where nodes in the same level can run in parallel.
99
+ *
100
+ * Uses Kahn's algorithm to perform topological sorting with level identification.
101
+ * Nodes are grouped by their distance from source nodes (input nodes with no dependencies).
102
+ *
103
+ * @param nodes - Array of flow nodes to analyze
104
+ * @param edges - Array of edges defining dependencies between nodes
105
+ * @returns Array of execution levels, ordered from 0 (no dependencies) onwards
106
+ * @throws Error if a cycle is detected in the flow graph
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const levels = scheduler.groupNodesByExecutionLevel(nodes, edges);
111
+ * // levels = [
112
+ * // { level: 0, nodes: ['input_1'] },
113
+ * // { level: 1, nodes: ['resize_1', 'optimize_1'] },
114
+ * // { level: 2, nodes: ['output_1'] }
115
+ * // ]
116
+ * ```
117
+ */
118
+ groupNodesByExecutionLevel(
119
+ nodes: FlowNode<unknown, unknown>[],
120
+ edges: Array<{ source: string; target: string }>,
121
+ ): ExecutionLevel[] {
122
+ // Build dependency graph
123
+ const graph: Record<string, string[]> = {};
124
+ const inDegree: Record<string, number> = {};
125
+
126
+ // Initialize graph structure
127
+ nodes.forEach((node) => {
128
+ graph[node.id] = [];
129
+ inDegree[node.id] = 0;
130
+ });
131
+
132
+ // Build edges and calculate in-degrees
133
+ edges.forEach((edge) => {
134
+ graph[edge.source]?.push(edge.target);
135
+ inDegree[edge.target] = (inDegree[edge.target] || 0) + 1;
136
+ });
137
+
138
+ const levels: ExecutionLevel[] = [];
139
+ const processedNodes = new Set<string>();
140
+ let levelIndex = 0;
141
+
142
+ // Use Kahn's algorithm to group nodes by level
143
+ while (processedNodes.size < nodes.length) {
144
+ // Find all nodes with zero in-degree that haven't been processed
145
+ const currentLevelNodes = Object.keys(inDegree).filter(
146
+ (nodeId) => inDegree[nodeId] === 0 && !processedNodes.has(nodeId),
147
+ );
148
+
149
+ if (currentLevelNodes.length === 0) {
150
+ throw new Error(
151
+ "Cycle detected in flow graph - cannot execute in parallel",
152
+ );
153
+ }
154
+
155
+ levels.push({
156
+ level: levelIndex++,
157
+ nodes: currentLevelNodes,
158
+ });
159
+
160
+ // Remove current level nodes and update in-degrees for dependent nodes
161
+ currentLevelNodes.forEach((nodeId) => {
162
+ processedNodes.add(nodeId);
163
+ delete inDegree[nodeId];
164
+
165
+ // Decrease in-degree for all nodes that depend on this node
166
+ graph[nodeId]?.forEach((dependentId) => {
167
+ if (inDegree[dependentId] !== undefined) {
168
+ inDegree[dependentId]--;
169
+ }
170
+ });
171
+ });
172
+ }
173
+
174
+ return levels;
175
+ }
176
+
177
+ /**
178
+ * Executes a batch of Effect-based node executors in parallel with concurrency control.
179
+ *
180
+ * All executors are run in parallel, but the number of concurrent executions is limited
181
+ * by maxConcurrency. This prevents resource exhaustion while maximizing parallelism.
182
+ *
183
+ * @template T - The return type of each executor
184
+ * @template E - The error type of the Effects
185
+ * @template R - The requirements type of the Effects
186
+ *
187
+ * @param nodeExecutors - Array of Effect-returning functions to execute in parallel
188
+ * @returns Effect that resolves to array of results in the same order as input
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * const results = yield* scheduler.executeNodesInParallel([
193
+ * () => executeNode("node1"),
194
+ * () => executeNode("node2"),
195
+ * () => executeNode("node3")
196
+ * ]);
197
+ * // results will be in order: [result1, result2, result3]
198
+ * ```
199
+ */
200
+ executeNodesInParallel<T, E, R>(
201
+ nodeExecutors: Array<() => Effect.Effect<T, E, R>>,
202
+ ): Effect.Effect<T[], E, R> {
203
+ return Effect.all(
204
+ nodeExecutors.map((executor) => executor()),
205
+ {
206
+ concurrency: this.maxConcurrency,
207
+ },
208
+ );
209
+ }
210
+
211
+ /**
212
+ * Determines if a set of nodes can be safely executed in parallel.
213
+ *
214
+ * Nodes can execute in parallel if all their dependencies have been completed.
215
+ * This is typically called to verify that nodes in an execution level are ready
216
+ * to run given the current node results.
217
+ *
218
+ * @param nodeIds - Array of node IDs to check
219
+ * @param nodeResults - Map of completed node IDs to their results
220
+ * @param reverseGraph - Dependency graph mapping node IDs to their incoming dependencies
221
+ * @returns true if all dependencies for all nodes are in nodeResults, false otherwise
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * const canRun = scheduler.canExecuteInParallel(
226
+ * ['resize_1', 'optimize_1'],
227
+ * nodeResults,
228
+ * reverseGraph
229
+ * );
230
+ * ```
231
+ */
232
+ canExecuteInParallel(
233
+ nodeIds: string[],
234
+ nodeResults: Map<string, unknown>,
235
+ reverseGraph: Record<string, string[]>,
236
+ ): boolean {
237
+ return nodeIds.every((nodeId) => {
238
+ const dependencies = reverseGraph[nodeId] || [];
239
+ return dependencies.every((depId) => nodeResults.has(depId));
240
+ });
241
+ }
242
+
243
+ /**
244
+ * Gets execution statistics for monitoring and debugging.
245
+ *
246
+ * @returns Object containing current scheduler configuration
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * const stats = scheduler.getStats();
251
+ * console.log(`Max concurrency: ${stats.maxConcurrency}`);
252
+ * ```
253
+ */
254
+ getStats() {
255
+ return {
256
+ maxConcurrency: this.maxConcurrency,
257
+ };
258
+ }
259
+ }
@@ -0,0 +1,48 @@
1
+ import { Context, type Effect } from "effect";
2
+ import type { UploadistaError } from "@/errors";
3
+
4
+ /**
5
+ * Shape definition for the Credential Provider interface.
6
+ * Defines the contract for retrieving credentials for various services.
7
+ */
8
+ export interface CredentialProviderShape {
9
+ /**
10
+ * Retrieves credentials for a specific service and client.
11
+ *
12
+ * @param params - Parameters for credential retrieval
13
+ * @param params.clientId - Unique identifier for the client, or null if not available
14
+ * @param params.serviceType - Optional service type to get specific credentials for
15
+ * @returns An Effect that resolves to a record of credential key-value pairs
16
+ * @throws {UploadistaError} When credential retrieval fails
17
+ */
18
+ getCredential: (params: {
19
+ clientId: string | null;
20
+ serviceType?: string;
21
+ }) => Effect.Effect<Record<string, unknown>, UploadistaError>;
22
+ }
23
+
24
+ /**
25
+ * Context tag for the Credential Provider.
26
+ *
27
+ * This tag provides a type-safe way to access credential functionality
28
+ * throughout the application using Effect's dependency injection system.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { CredentialProvider } from "@uploadista/core/flow/plugins";
33
+ *
34
+ * // In your flow node
35
+ * const program = Effect.gen(function* () {
36
+ * const credentialProvider = yield* CredentialProvider;
37
+ * const credentials = yield* credentialProvider.getCredential({
38
+ * clientId: "user123",
39
+ * serviceType: "replicate"
40
+ * });
41
+ * return credentials;
42
+ * });
43
+ * ```
44
+ */
45
+ export class CredentialProvider extends Context.Tag("CredentialProvider")<
46
+ CredentialProvider,
47
+ CredentialProviderShape
48
+ >() {}
@@ -0,0 +1,66 @@
1
+ import type { UploadistaError } from "@uploadista/core/errors";
2
+ import { Context, type Effect } from "effect";
3
+
4
+ /**
5
+ * Context information for AI image processing operations.
6
+ * Contains client identification for tracking and billing purposes.
7
+ */
8
+ export type ImageAiContext = {
9
+ /** Unique identifier for the client making the request, or null if not available */
10
+ clientId: string | null;
11
+ };
12
+
13
+ /**
14
+ * Shape definition for the Image AI Plugin interface.
15
+ * Defines the contract that all image AI implementations must follow.
16
+ */
17
+ export type ImageAiPluginShape = {
18
+ /**
19
+ * Removes the background from an image using AI processing.
20
+ *
21
+ * @param inputUrl - The URL of the input image to process
22
+ * @param context - Context information including client ID for tracking
23
+ * @returns An Effect that resolves to an object containing the output image URL
24
+ * @throws {UploadistaError} When the background removal fails
25
+ */
26
+ removeBackground: (
27
+ inputUrl: string,
28
+ context: ImageAiContext
29
+ ) => Effect.Effect<{ outputUrl: string }, UploadistaError>;
30
+
31
+ /**
32
+ * Generates a textual description of an image using AI analysis.
33
+ *
34
+ * @param inputUrl - The URL of the input image to analyze
35
+ * @param context - Context information including client ID for tracking
36
+ * @returns An Effect that resolves to an object containing the image description
37
+ * @throws {UploadistaError} When the image analysis fails
38
+ */
39
+ describeImage: (
40
+ inputUrl: string,
41
+ context: ImageAiContext
42
+ ) => Effect.Effect<{ description: string }, UploadistaError>;
43
+ };
44
+
45
+ /**
46
+ * Context tag for the Image AI Plugin.
47
+ *
48
+ * This tag provides a type-safe way to access image AI functionality
49
+ * throughout the application using Effect's dependency injection system.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * import { ImageAiPlugin } from "@uploadista/core/flow/plugins";
54
+ *
55
+ * // In your flow node
56
+ * const program = Effect.gen(function* () {
57
+ * const imageAi = yield* ImageAiPlugin;
58
+ * const result = yield* imageAi.removeBackground(imageUrl, { clientId: "user123" });
59
+ * return result.outputUrl;
60
+ * });
61
+ * ```
62
+ */
63
+ export class ImageAiPlugin extends Context.Tag("ImageAiPlugin")<
64
+ ImageAiPlugin,
65
+ ImageAiPluginShape
66
+ >() {}