@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,142 @@
1
+ # Parallel Execution - Quick Start Guide
2
+
3
+ ## TL;DR
4
+
5
+ The `ParallelScheduler` now uses Effect's native concurrency primitives instead of broken Semaphore dependencies. Enable parallel execution with one config option.
6
+
7
+ ## Enable Parallel Execution
8
+
9
+ Add `parallelExecution` to your flow config:
10
+
11
+ ```typescript
12
+ const flow = yield* createFlowWithSchema({
13
+ flowId: 'my-flow',
14
+ name: 'My Flow',
15
+ nodes: [inputNode, processA, processB, mergeNode, outputNode],
16
+ edges: [
17
+ { source: 'input', target: 'processA' },
18
+ { source: 'input', target: 'processB' },
19
+ { source: 'processA', target: 'merge' },
20
+ { source: 'processB', target: 'merge' },
21
+ { source: 'merge', target: 'output' }
22
+ ],
23
+ inputSchema: myInputSchema,
24
+ outputSchema: myOutputSchema,
25
+ parallelExecution: {
26
+ enabled: true,
27
+ maxConcurrency: 4,
28
+ },
29
+ });
30
+ ```
31
+
32
+ ## How It Works
33
+
34
+ 1. **Dependency Analysis**: Nodes are automatically grouped by their dependencies
35
+ 2. **Level-Based Execution**:
36
+ - Nodes with no dependencies (Level 0)
37
+ - Nodes depending on Level 0 (Level 1)
38
+ - And so on...
39
+ 3. **Parallel Within Levels**: Nodes at the same level run in parallel
40
+ 4. **Sequential Between Levels**: Levels run one after another
41
+
42
+ ### Example Flow
43
+
44
+ ```
45
+ Input → [Resize ║ Optimize] → Merge → Output
46
+ └─ Level 1 (parallel) ─┘
47
+ ```
48
+
49
+ - Input runs first
50
+ - Resize and Optimize run in parallel
51
+ - Merge waits for both to complete
52
+ - Output runs last
53
+
54
+ ## Configuration
55
+
56
+ | Option | Default | Description |
57
+ |--------|---------|-------------|
58
+ | `enabled` | `false` | Enable/disable parallel execution |
59
+ | `maxConcurrency` | `4` | Max concurrent nodes per level |
60
+
61
+ ## What Changed
62
+
63
+ ### ParallelScheduler
64
+
65
+ **Before** (broken):
66
+ ```typescript
67
+ // This didn't work - Semaphore doesn't exist
68
+ const resourceSemaphore = semaphore(maxConcurrency); // ERROR
69
+ async executeNodesInParallel(executors) { ... }
70
+ ```
71
+
72
+ **After** (working):
73
+ ```typescript
74
+ // Uses Effect's Effect.all() with concurrency option
75
+ executeNodesInParallel<T, E, R>(
76
+ nodeExecutors: Array<() => Effect.Effect<T, E, R>>,
77
+ ): Effect.Effect<T[], E, R> {
78
+ return Effect.all(nodeExecutors.map((executor) => executor()), {
79
+ concurrency: this.maxConcurrency,
80
+ });
81
+ }
82
+ ```
83
+
84
+ ### Flow Engine
85
+
86
+ Automatic detection of `parallelExecution.enabled`:
87
+ - If `true`: Uses `ParallelScheduler` with level-based execution
88
+ - If `false` (default): Uses original sequential execution
89
+
90
+ ## Performance Example
91
+
92
+ ```
93
+ Sequential: Input(1s) → Resize(2s) → Optimize(2s) → Merge(1s) → Output(1s) = 7s
94
+ Parallel: Input(1s) → [Resize(2s) ║ Optimize(2s)] → Merge(1s) → Output(1s) = 5s
95
+ Speedup: 28% faster
96
+ ```
97
+
98
+ ## Debugging
99
+
100
+ Enable debug logs to see execution levels:
101
+
102
+ ```
103
+ Flow my-flow: Executing in parallel mode (maxConcurrency: 4)
104
+ Flow my-flow: Grouped nodes into 4 execution levels
105
+ Flow my-flow: Executing level 0 with nodes: input_1
106
+ Flow my-flow: Executing level 1 with nodes: resize_1, optimize_1
107
+ Flow my-flow: Executing level 2 with nodes: merge_1
108
+ Flow my-flow: Executing level 3 with nodes: output_1
109
+ ```
110
+
111
+ ## Introspection
112
+
113
+ ```typescript
114
+ const scheduler = new ParallelScheduler({ maxConcurrency: 4 });
115
+
116
+ // See how nodes are grouped
117
+ const levels = scheduler.groupNodesByExecutionLevel(nodes, edges);
118
+ levels.forEach(level => {
119
+ console.log(`Level ${level.level}: ${level.nodes.join(', ')}`);
120
+ });
121
+
122
+ // Get stats
123
+ const stats = scheduler.getStats();
124
+ console.log(`Max concurrency: ${stats.maxConcurrency}`);
125
+ ```
126
+
127
+ ## Backward Compatibility
128
+
129
+ ✅ Existing flows work unchanged (parallel execution disabled by default)
130
+ ✅ No breaking changes
131
+ ✅ Add config option to opt-in to parallel execution
132
+
133
+ ## Constraints
134
+
135
+ - **Dependencies enforced**: Nodes can't run before their inputs are ready
136
+ - **Level structure**: All nodes in a level must complete before next level starts
137
+ - **Concurrency limit**: Won't exceed `maxConcurrency` simultaneous executions
138
+ - **Error handling**: First error in a level stops the flow
139
+
140
+ ## For More Details
141
+
142
+ See `/src/flow/PARALLEL_EXECUTION.md` for comprehensive documentation.
@@ -0,0 +1,184 @@
1
+ # Parallel Execution Refactoring - Summary
2
+
3
+ ## Overview
4
+
5
+ Successfully refactored the `ParallelScheduler` from a Promise-based implementation with undefined `Semaphore` dependencies to a modern Effect-based pattern aligned with the flow engine's architecture.
6
+
7
+ ## Changes Made
8
+
9
+ ### 1. **ParallelScheduler Refactored** (`src/flow/parallel-scheduler.ts`)
10
+
11
+ #### Issues Fixed
12
+ - ❌ Removed undefined `Semaphore` import and usage
13
+ - ❌ Removed `semaphore()` function call (didn't exist)
14
+ - ✅ Replaced with Effect's native concurrency primitives
15
+
16
+ #### Key Changes
17
+ - Converted `executeNodesInParallel()` from Promise-based to Effect-based
18
+ - Now uses `Effect.all(nodeExecutors, { concurrency: maxConcurrency })`
19
+ - Improved documentation with comprehensive JSDoc comments
20
+ - All methods now properly typed with Effect generics
21
+
22
+ ```typescript
23
+ // Before (broken):
24
+ async executeNodesInParallel<T>(
25
+ nodeExecutors: Array<() => Promise<T>>,
26
+ ): Promise<T[]> {
27
+ const permit = await this.resourceSemaphore.acquire(); // ERROR: doesn't exist
28
+ // ...
29
+ }
30
+
31
+ // After (working):
32
+ executeNodesInParallel<T, E, R>(
33
+ nodeExecutors: Array<() => Effect.Effect<T, E, R>>,
34
+ ): Effect.Effect<T[], E, R> {
35
+ return Effect.all(nodeExecutors.map((executor) => executor()), {
36
+ concurrency: this.maxConcurrency,
37
+ });
38
+ }
39
+ ```
40
+
41
+ ### 2. **Flow Engine Integration** (`src/flow/flow.ts`)
42
+
43
+ #### Added Parallel Execution Support
44
+ - Imported `ParallelScheduler`
45
+ - Added parallel execution path in `executeFlow` function
46
+ - Maintains backward compatibility with sequential execution (default)
47
+
48
+ #### Execution Strategy
49
+ ```typescript
50
+ if (useParallelExecution) {
51
+ // Parallel execution using execution levels
52
+ const scheduler = new ParallelScheduler({ maxConcurrency: 4 });
53
+ const executionLevels = scheduler.groupNodesByExecutionLevel(nodes, edges);
54
+
55
+ // Execute each level sequentially, but nodes within level in parallel
56
+ for (const level of executionLevels) {
57
+ const levelResults = yield* scheduler.executeNodesInParallel(nodeExecutors);
58
+ // Process results...
59
+ }
60
+ } else {
61
+ // Sequential execution (original behavior)
62
+ for (let i = startIndex; i < executionOrder.length; i++) {
63
+ // ...
64
+ }
65
+ }
66
+ ```
67
+
68
+ #### Key Features
69
+ - ✅ Level-based execution (respects dependencies)
70
+ - ✅ Configurable concurrency via `maxConcurrency`
71
+ - ✅ Proper pause/resume handling in parallel mode
72
+ - ✅ Debug logging at each level
73
+ - ✅ Backward compatible (sequential is default)
74
+
75
+ ### 3. **Documentation Updated** (`src/flow/PARALLEL_EXECUTION.md`)
76
+
77
+ Comprehensive guide covering:
78
+ - ✅ Effect-based implementation details
79
+ - ✅ Configuration options
80
+ - ✅ Usage examples
81
+ - ✅ Performance characteristics
82
+ - ✅ Error handling
83
+ - ✅ Debugging and monitoring
84
+ - ✅ Future enhancements
85
+
86
+ ## Configuration
87
+
88
+ Parallel execution is **disabled by default** and can be enabled per flow:
89
+
90
+ ```typescript
91
+ const flow = yield* createFlowWithSchema({
92
+ flowId: 'image-pipeline',
93
+ name: 'Image Processing Pipeline',
94
+ nodes: [inputNode, resizeNode, optimizeNode, mergeNode, storageNode],
95
+ edges: [
96
+ { source: 'input', target: 'resize' },
97
+ { source: 'input', target: 'optimize' },
98
+ { source: 'resize', target: 'merge' },
99
+ { source: 'optimize', target: 'merge' },
100
+ { source: 'merge', target: 'storage' }
101
+ ],
102
+ inputSchema: myInputSchema,
103
+ outputSchema: myOutputSchema,
104
+ parallelExecution: {
105
+ enabled: true,
106
+ maxConcurrency: 4, // Limit concurrent nodes per level
107
+ },
108
+ });
109
+ ```
110
+
111
+ ## Architecture
112
+
113
+ ### Execution Levels
114
+ Nodes are automatically grouped into levels based on dependencies:
115
+
116
+ ```
117
+ Level 0: [input] ← No dependencies
118
+ Level 1: [resize, optimize] ← Both depend on input
119
+ Level 2: [merge] ← Depends on resize and optimize
120
+ Level 3: [storage] ← Depends on merge
121
+ ```
122
+
123
+ Within each level:
124
+ - All nodes execute **in parallel**
125
+ - Concurrency is limited by `maxConcurrency`
126
+ - Uses Effect's `Effect.all()` for safe concurrent execution
127
+
128
+ Between levels:
129
+ - Execution is **strictly sequential**
130
+ - Dependencies are guaranteed to complete before dependents run
131
+
132
+ ### Dependency Analysis
133
+ Uses **Kahn's algorithm** for topological sorting:
134
+ 1. Build dependency graph from edges
135
+ 2. Calculate in-degree for each node
136
+ 3. Find nodes with in-degree 0
137
+ 4. Group into levels and update in-degrees
138
+ 5. Repeat until all nodes are processed
139
+
140
+ ## Testing
141
+
142
+ ✅ **Build Status**: All 27 packages compile successfully
143
+ - `pnpm build` completes with 0 errors
144
+ - No TypeScript diagnostics
145
+ - ParallelScheduler and flow.ts both type-check cleanly
146
+
147
+ ## Backward Compatibility
148
+
149
+ ✅ **100% Backward Compatible**
150
+ - Existing flows work without any changes
151
+ - Sequential execution remains the default
152
+ - No breaking changes to public APIs
153
+ - `parallelExecution` is an optional config property
154
+
155
+ ## Migration Path
156
+
157
+ 1. **Current flows**: Continue working as-is (sequential by default)
158
+ 2. **Enable parallel execution**: Add `parallelExecution: { enabled: true }` to existing flows
159
+ 3. **Tune performance**: Adjust `maxConcurrency` based on your hardware and workload
160
+
161
+ ## Files Modified
162
+
163
+ 1. `src/flow/parallel-scheduler.ts` - Complete refactor to Effect-based API
164
+ 2. `src/flow/flow.ts` - Integrated parallel scheduler into executeFlow
165
+ 3. `src/flow/PARALLEL_EXECUTION.md` - Updated documentation
166
+ 4. ✅ All type definitions already in place (`src/flow/types/flow-types.ts`)
167
+
168
+ ## Benefits
169
+
170
+ - ✅ **No External Dependencies**: Uses Effect's built-in concurrency
171
+ - ✅ **Type Safe**: Full TypeScript support with proper Effect typing
172
+ - ✅ **Resource Safe**: Concurrency limits prevent resource exhaustion
173
+ - ✅ **Performance**: Independent nodes can run in parallel
174
+ - ✅ **Maintainable**: Clean, well-documented code following Effect patterns
175
+ - ✅ **Debuggable**: Built-in logging and scheduler introspection
176
+
177
+ ## Future Enhancements
178
+
179
+ Potential improvements for future releases:
180
+ - Dynamic concurrency adjustment based on resource availability
181
+ - Per-node concurrency limits for resource-intensive operations
182
+ - Parallel level visualization in flow builder UI
183
+ - Performance metrics collection and reporting
184
+ - Adaptive scheduling based on node execution times
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@uploadista/core",
3
+ "version": "0.0.2",
4
+ "description": "Core package of Uploadista",
5
+ "license": "MIT",
6
+ "author": "Uploadista",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./errors": {
15
+ "types": "./dist/errors/index.d.ts",
16
+ "import": "./dist/errors/index.js",
17
+ "default": "./dist/errors/index.js"
18
+ },
19
+ "./types": {
20
+ "types": "./dist/types/index.d.ts",
21
+ "import": "./dist/types/index.js",
22
+ "default": "./dist/types/index.js"
23
+ },
24
+ "./flow": {
25
+ "types": "./dist/flow/index.d.ts",
26
+ "import": "./dist/flow/index.js",
27
+ "default": "./dist/flow/index.js"
28
+ },
29
+ "./upload": {
30
+ "types": "./dist/upload/index.d.ts",
31
+ "import": "./dist/upload/index.js",
32
+ "default": "./dist/upload/index.js"
33
+ },
34
+ "./logger/*": {
35
+ "types": "./dist/logger/*.d.ts",
36
+ "import": "./dist/logger/*.js",
37
+ "default": "./dist/logger/*.js"
38
+ },
39
+ "./streams/*": {
40
+ "types": "./dist/streams/*.d.ts",
41
+ "import": "./dist/streams/*.js",
42
+ "default": "./dist/streams/*.js"
43
+ },
44
+ "./utils/*": {
45
+ "types": "./dist/utils/*.d.ts",
46
+ "import": "./dist/utils/*.js",
47
+ "default": "./dist/utils/*.js"
48
+ },
49
+ "./websocket": {
50
+ "types": "./dist/websocket/index.d.ts",
51
+ "import": "./dist/websocket/index.js",
52
+ "default": "./dist/websocket/index.js"
53
+ }
54
+ },
55
+ "dependencies": {
56
+ "effect": "3.18.4",
57
+ "zod": "4.1.12"
58
+ },
59
+ "devDependencies": {
60
+ "@types/node": "24.8.1",
61
+ "tsdown": "0.15.7",
62
+ "vitest": "3.2.4",
63
+ "@uploadista/typescript-config": "0.0.2"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public",
67
+ "registry": "https://registry.npmjs.org/"
68
+ },
69
+ "scripts": {
70
+ "build": "tsc -b",
71
+ "release": "tsdown && bumpp && pnpm publish --access restricted --no-git-checks",
72
+ "format": "biome format --write ./src",
73
+ "lint": "biome lint --write ./src",
74
+ "check": "biome check --write ./src",
75
+ "test": "vitest",
76
+ "test:run": "vitest run",
77
+ "test:watch": "vitest --watch",
78
+ "typecheck": "tsc --noEmit"
79
+ }
80
+ }
@@ -0,0 +1,251 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ ERROR_CATALOG,
5
+ httpFailure,
6
+ isUploadistaError,
7
+ UploadistaError,
8
+ type UploadistaErrorCode,
9
+ } from "../uploadista-error";
10
+ import { Effect } from "effect";
11
+
12
+ describe("ERROR_CATALOG", () => {
13
+ it("should contain all error codes with status and body", () => {
14
+ const errorCodes: UploadistaErrorCode[] = [
15
+ "MISSING_OFFSET",
16
+ "ABORTED",
17
+ "INVALID_TERMINATION",
18
+ "ERR_LOCK_TIMEOUT",
19
+ "INVALID_CONTENT_TYPE",
20
+ "FLOW_STRUCTURE_ERROR",
21
+ "FLOW_CYCLE_ERROR",
22
+ "FLOW_NODE_NOT_FOUND",
23
+ "FLOW_NODE_ERROR",
24
+ "DATASTORE_NOT_FOUND",
25
+ "FILE_NOT_FOUND",
26
+ "INVALID_OFFSET",
27
+ "FILE_NO_LONGER_EXISTS",
28
+ "ERR_SIZE_EXCEEDED",
29
+ "ERR_MAX_SIZE_EXCEEDED",
30
+ "INVALID_LENGTH",
31
+ "INVALID_METADATA",
32
+ "UNKNOWN_ERROR",
33
+ "FILE_WRITE_ERROR",
34
+ "UPLOAD_ID_NOT_FOUND",
35
+ "FLOW_OUTPUT_VALIDATION_ERROR",
36
+ "FLOW_INPUT_VALIDATION_ERROR",
37
+ ];
38
+
39
+ for (const code of errorCodes) {
40
+ expect(ERROR_CATALOG[code]).toBeDefined();
41
+ expect(ERROR_CATALOG[code]).toHaveProperty("status");
42
+ expect(ERROR_CATALOG[code]).toHaveProperty("body");
43
+ expect(typeof ERROR_CATALOG[code].status).toBe("number");
44
+ expect(typeof ERROR_CATALOG[code].body).toBe("string");
45
+ }
46
+ });
47
+
48
+ it("should have appropriate HTTP status codes", () => {
49
+ expect(ERROR_CATALOG.MISSING_OFFSET.status).toBe(403);
50
+ expect(ERROR_CATALOG.FILE_NOT_FOUND.status).toBe(404);
51
+ expect(ERROR_CATALOG.INVALID_OFFSET.status).toBe(409);
52
+ expect(ERROR_CATALOG.FILE_NO_LONGER_EXISTS.status).toBe(410);
53
+ expect(ERROR_CATALOG.ERR_SIZE_EXCEEDED.status).toBe(413);
54
+ expect(ERROR_CATALOG.UNKNOWN_ERROR.status).toBe(500);
55
+ });
56
+
57
+ it("should have meaningful error messages", () => {
58
+ expect(ERROR_CATALOG.MISSING_OFFSET.body).toContain("Upload-Offset");
59
+ expect(ERROR_CATALOG.FILE_NOT_FOUND.body).toContain("file");
60
+ expect(ERROR_CATALOG.FLOW_CYCLE_ERROR.body).toContain("cycle");
61
+ expect(ERROR_CATALOG.ERR_MAX_SIZE_EXCEEDED.body).toContain("Maximum size");
62
+ });
63
+ });
64
+
65
+ describe("UploadistaError", () => {
66
+ it("should create error with all properties", () => {
67
+ const error = new UploadistaError({
68
+ code: "FILE_NOT_FOUND",
69
+ status: 404,
70
+ body: "File not found",
71
+ cause: new Error("underlying error"),
72
+ details: { fileId: "123" },
73
+ });
74
+
75
+ expect(error.name).toBe("UploadistaError");
76
+ expect(error.code).toBe("FILE_NOT_FOUND");
77
+ expect(error.status).toBe(404);
78
+ expect(error.status_code).toBe(404); // legacy alias
79
+ expect(error.body).toBe("File not found");
80
+ expect(error.message).toBe("File not found");
81
+ expect(error.details).toEqual({ fileId: "123" });
82
+ expect((error as { cause?: unknown }).cause).toBeInstanceOf(Error);
83
+ });
84
+
85
+ it("should create error without optional properties", () => {
86
+ const error = new UploadistaError({
87
+ code: "UNKNOWN_ERROR",
88
+ status: 500,
89
+ body: "Something went wrong",
90
+ });
91
+
92
+ expect(error.code).toBe("UNKNOWN_ERROR");
93
+ expect(error.status).toBe(500);
94
+ expect(error.body).toBe("Something went wrong");
95
+ expect(error.details).toBeUndefined();
96
+ expect((error as { cause?: unknown }).cause).toBeUndefined();
97
+ });
98
+
99
+ it("should inherit from Error", () => {
100
+ const error = new UploadistaError({
101
+ code: "FILE_NOT_FOUND",
102
+ status: 404,
103
+ body: "File not found",
104
+ });
105
+
106
+ expect(error).toBeInstanceOf(Error);
107
+ expect(error).toBeInstanceOf(UploadistaError);
108
+ });
109
+
110
+ describe("fromCode", () => {
111
+ it("should create error from error code", () => {
112
+ const error = UploadistaError.fromCode("FILE_NOT_FOUND");
113
+
114
+ expect(error.code).toBe("FILE_NOT_FOUND");
115
+ expect(error.status).toBe(ERROR_CATALOG.FILE_NOT_FOUND.status);
116
+ expect(error.body).toBe(ERROR_CATALOG.FILE_NOT_FOUND.body);
117
+ });
118
+
119
+ it("should allow overriding status and body", () => {
120
+ const error = UploadistaError.fromCode("FILE_NOT_FOUND", {
121
+ status: 400,
122
+ body: "Custom message",
123
+ });
124
+
125
+ expect(error.code).toBe("FILE_NOT_FOUND");
126
+ expect(error.status).toBe(400);
127
+ expect(error.body).toBe("Custom message");
128
+ });
129
+
130
+ it("should include details and cause when provided", () => {
131
+ const cause = new Error("root cause");
132
+ const error = UploadistaError.fromCode("UNKNOWN_ERROR", {
133
+ details: { extra: "info" },
134
+ cause: cause,
135
+ });
136
+
137
+ expect(error.details).toEqual({ extra: "info" });
138
+ expect((error as { cause?: unknown }).cause).toBe(cause);
139
+ });
140
+
141
+ it("should work with all error codes", () => {
142
+ const errorCodes: UploadistaErrorCode[] = [
143
+ "MISSING_OFFSET",
144
+ "ABORTED",
145
+ "INVALID_TERMINATION",
146
+ "ERR_LOCK_TIMEOUT",
147
+ "INVALID_CONTENT_TYPE",
148
+ "FLOW_STRUCTURE_ERROR",
149
+ "FLOW_CYCLE_ERROR",
150
+ "FLOW_NODE_NOT_FOUND",
151
+ "FLOW_NODE_ERROR",
152
+ "DATASTORE_NOT_FOUND",
153
+ "FILE_NOT_FOUND",
154
+ "INVALID_OFFSET",
155
+ "FILE_NO_LONGER_EXISTS",
156
+ "ERR_SIZE_EXCEEDED",
157
+ "ERR_MAX_SIZE_EXCEEDED",
158
+ "INVALID_LENGTH",
159
+ "INVALID_METADATA",
160
+ "UNKNOWN_ERROR",
161
+ "FILE_WRITE_ERROR",
162
+ "UPLOAD_ID_NOT_FOUND",
163
+ "FLOW_OUTPUT_VALIDATION_ERROR",
164
+ "FLOW_INPUT_VALIDATION_ERROR",
165
+ ];
166
+
167
+ for (const code of errorCodes) {
168
+ const error = UploadistaError.fromCode(code);
169
+ expect(error.code).toBe(code);
170
+ expect(error.status).toBe(ERROR_CATALOG[code].status);
171
+ expect(error.body).toBe(ERROR_CATALOG[code].body);
172
+ }
173
+ });
174
+ });
175
+
176
+ describe("toFailure", () => {
177
+ it("should convert error to failure result", () => {
178
+ const error = UploadistaError.fromCode("FILE_NOT_FOUND");
179
+ const result = error.toEffect();
180
+
181
+ expect(Effect.isFailure(result)).toBe(true);
182
+ expect(Effect.isSuccess(result)).toBe(false);
183
+ });
184
+ });
185
+ });
186
+
187
+ describe("isUploadistaError", () => {
188
+ it("should return true for UploadistaError instances", () => {
189
+ const error = UploadistaError.fromCode("FILE_NOT_FOUND");
190
+ expect(isUploadistaError(error)).toBe(true);
191
+ });
192
+
193
+ it("should return false for regular Error instances", () => {
194
+ const error = new Error("regular error");
195
+ expect(isUploadistaError(error)).toBe(false);
196
+ });
197
+
198
+ it("should return false for non-error values", () => {
199
+ expect(isUploadistaError("string")).toBe(false);
200
+ expect(isUploadistaError(null)).toBe(false);
201
+ expect(isUploadistaError(undefined)).toBe(false);
202
+ expect(isUploadistaError({})).toBe(false);
203
+ expect(isUploadistaError(123)).toBe(false);
204
+ });
205
+
206
+ it("should work as type guard", () => {
207
+ const error: unknown = UploadistaError.fromCode("FILE_NOT_FOUND");
208
+
209
+ if (isUploadistaError(error)) {
210
+ // TypeScript should know error is UploadistaError
211
+ expect(error.code).toBe("FILE_NOT_FOUND");
212
+ expect(error.status).toBe(404);
213
+ } else {
214
+ expect.fail("Should be UploadistaError");
215
+ }
216
+ });
217
+ });
218
+
219
+ describe("httpFailure", () => {
220
+ it("should create failure result from error code", () => {
221
+ const result = httpFailure("FILE_NOT_FOUND");
222
+
223
+ expect(Effect.isFailure(result)).toBe(true);
224
+ expect(Effect.isSuccess(result)).toBe(false);
225
+ });
226
+
227
+ it("should allow overriding error properties", () => {
228
+ const result = httpFailure("UNKNOWN_ERROR", {
229
+ status: 503,
230
+ body: "Service unavailable",
231
+ details: { retryAfter: 60 },
232
+ });
233
+
234
+ expect(Effect.isFailure(result)).toBe(true);
235
+ expect(Effect.isSuccess(result)).toBe(false);
236
+ });
237
+
238
+ it("should work with all error codes", () => {
239
+ const errorCodes: UploadistaErrorCode[] = [
240
+ "MISSING_OFFSET",
241
+ "FLOW_NODE_ERROR",
242
+ "INVALID_METADATA",
243
+ ];
244
+
245
+ for (const code of errorCodes) {
246
+ const result = httpFailure(code);
247
+ expect(Effect.isFailure(result)).toBe(true);
248
+ expect(Effect.isSuccess(result)).toBe(false);
249
+ }
250
+ });
251
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./uploadista-error.js";
2
+ export { ERROR_CATALOG as ERRORS } from "./uploadista-error.js";