@uploadista/core 0.0.2 → 0.0.4
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.
- package/README.md +6 -20
- package/dist/checksum-C5qE-5eg.js +2 -0
- package/dist/checksum-C5qE-5eg.js.map +1 -0
- package/dist/checksum-wSBuXX84.cjs +1 -0
- package/dist/errors/index.cjs +1 -1
- package/dist/errors/index.d.cts +2 -2
- package/dist/errors/index.d.ts +3 -3
- package/dist/errors/index.js +1 -2
- package/dist/flow/index.cjs +1 -1
- package/dist/flow/index.d.cts +5 -5
- package/dist/flow/index.d.ts +6 -24
- package/dist/flow/index.js +1 -24
- package/dist/flow-B0mMJM5Y.js +2 -0
- package/dist/flow-B0mMJM5Y.js.map +1 -0
- package/dist/flow-s5bgJsdb.cjs +1 -0
- package/dist/index-B46hb7yB.d.cts +36 -0
- package/dist/index-B46hb7yB.d.cts.map +1 -0
- package/dist/index-BnGZO47X.d.ts +3952 -0
- package/dist/index-BnGZO47X.d.ts.map +1 -0
- package/dist/{streams/stream-limiter.d.cts → index-C1mxuUxK.d.ts} +3 -3
- package/dist/index-C1mxuUxK.d.ts.map +1 -0
- package/dist/index-DM69UMgG.d.cts +4132 -0
- package/dist/index-DM69UMgG.d.cts.map +1 -0
- package/dist/index-DMJv8Tvo.d.ts +168 -0
- package/dist/index-DMJv8Tvo.d.ts.map +1 -0
- package/dist/index-GLPiXqj4.d.cts +168 -0
- package/dist/index-GLPiXqj4.d.cts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +1 -5
- package/dist/stream-limiter-CTuiXkcq.js +2 -0
- package/dist/{stream-limiter-CoWKv39w.js.map → stream-limiter-CTuiXkcq.js.map} +1 -1
- package/dist/stream-limiter-DYGG4t9f.cjs +1 -0
- package/dist/streams/index.cjs +1 -0
- package/dist/streams/index.d.cts +3 -0
- package/dist/streams/index.d.ts +3 -0
- package/dist/streams/index.js +1 -0
- package/dist/streams-BiD_pOPH.cjs +0 -0
- package/dist/streams-Bs3GDNKJ.js +1 -0
- package/dist/types/index.cjs +1 -1
- package/dist/types/index.d.cts +5 -5
- package/dist/types/index.d.ts +6 -10
- package/dist/types/index.js +1 -9
- package/dist/types-Dj9g8ocl.cjs +1 -0
- package/dist/types-m26wrG-Z.js +2 -0
- package/dist/types-m26wrG-Z.js.map +1 -0
- package/dist/upload/index.cjs +1 -1
- package/dist/upload/index.d.cts +5 -5
- package/dist/upload/index.d.ts +6 -4
- package/dist/upload/index.js +1 -3
- package/dist/upload-BzU7ifyH.js +2 -0
- package/dist/upload-BzU7ifyH.js.map +1 -0
- package/dist/upload-DvLp6TXO.cjs +1 -0
- package/dist/uploadista-error-CAtkQiAv.d.cts +221 -0
- package/dist/uploadista-error-CAtkQiAv.d.cts.map +1 -0
- package/dist/{uploadista-error-BVsVxqvz.js → uploadista-error-CjfcFnVa.js} +9 -2
- package/dist/uploadista-error-CjfcFnVa.js.map +1 -0
- package/dist/uploadista-error-D9SONF9K.d.ts +221 -0
- package/dist/uploadista-error-D9SONF9K.d.ts.map +1 -0
- package/dist/{uploadista-error-BB-Wdiz9.cjs → uploadista-error-DdTP-Rjx.cjs} +9 -2
- package/dist/utils/index.cjs +1 -0
- package/dist/utils/index.d.cts +3 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils-BILytQlb.js +2 -0
- package/dist/utils-BILytQlb.js.map +1 -0
- package/dist/utils-BLsIUd8c.cjs +1 -0
- package/package.json +13 -17
- package/src/index.ts +2 -0
- package/src/streams/index.ts +1 -0
- package/src/utils/index.ts +5 -0
- package/tsdown.config.ts +2 -10
- package/.turbo/turbo-build.log +0 -5
- package/.turbo/turbo-check.log +0 -231
- package/.turbo/turbo-format.log +0 -5
- package/dist/chunk-CUT6urMc.cjs +0 -1
- package/dist/debounce-C2SeqcxD.js +0 -2
- package/dist/debounce-C2SeqcxD.js.map +0 -1
- package/dist/debounce-LZK7yS7Z.cjs +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/uploadista-error.d.ts +0 -209
- package/dist/errors/uploadista-error.d.ts.map +0 -1
- package/dist/errors/uploadista-error.js +0 -322
- package/dist/flow/edge.d.ts +0 -47
- package/dist/flow/edge.d.ts.map +0 -1
- package/dist/flow/edge.js +0 -40
- package/dist/flow/event.d.ts +0 -206
- package/dist/flow/event.d.ts.map +0 -1
- package/dist/flow/event.js +0 -53
- package/dist/flow/flow-server.d.ts +0 -223
- package/dist/flow/flow-server.d.ts.map +0 -1
- package/dist/flow/flow-server.js +0 -614
- package/dist/flow/flow.d.ts +0 -238
- package/dist/flow/flow.d.ts.map +0 -1
- package/dist/flow/flow.js +0 -629
- package/dist/flow/index.d.ts.map +0 -1
- package/dist/flow/node.d.ts +0 -136
- package/dist/flow/node.d.ts.map +0 -1
- package/dist/flow/node.js +0 -153
- package/dist/flow/nodes/index.d.ts +0 -8
- package/dist/flow/nodes/index.d.ts.map +0 -1
- package/dist/flow/nodes/index.js +0 -7
- package/dist/flow/nodes/input-node.d.ts +0 -78
- package/dist/flow/nodes/input-node.d.ts.map +0 -1
- package/dist/flow/nodes/input-node.js +0 -233
- package/dist/flow/nodes/storage-node.d.ts +0 -67
- package/dist/flow/nodes/storage-node.d.ts.map +0 -1
- package/dist/flow/nodes/storage-node.js +0 -94
- package/dist/flow/nodes/streaming-input-node.d.ts +0 -69
- package/dist/flow/nodes/streaming-input-node.d.ts.map +0 -1
- package/dist/flow/nodes/streaming-input-node.js +0 -156
- package/dist/flow/nodes/transform-node.d.ts +0 -85
- package/dist/flow/nodes/transform-node.d.ts.map +0 -1
- package/dist/flow/nodes/transform-node.js +0 -107
- package/dist/flow/parallel-scheduler.d.ts +0 -175
- package/dist/flow/parallel-scheduler.d.ts.map +0 -1
- package/dist/flow/parallel-scheduler.js +0 -193
- package/dist/flow/plugins/credential-provider.d.ts +0 -47
- package/dist/flow/plugins/credential-provider.d.ts.map +0 -1
- package/dist/flow/plugins/credential-provider.js +0 -24
- package/dist/flow/plugins/image-ai-plugin.d.ts +0 -61
- package/dist/flow/plugins/image-ai-plugin.d.ts.map +0 -1
- package/dist/flow/plugins/image-ai-plugin.js +0 -21
- package/dist/flow/plugins/image-plugin.d.ts +0 -52
- package/dist/flow/plugins/image-plugin.d.ts.map +0 -1
- package/dist/flow/plugins/image-plugin.js +0 -22
- package/dist/flow/plugins/types/describe-image-node.d.ts +0 -16
- package/dist/flow/plugins/types/describe-image-node.d.ts.map +0 -1
- package/dist/flow/plugins/types/describe-image-node.js +0 -9
- package/dist/flow/plugins/types/index.d.ts +0 -9
- package/dist/flow/plugins/types/index.d.ts.map +0 -1
- package/dist/flow/plugins/types/index.js +0 -8
- package/dist/flow/plugins/types/optimize-node.d.ts +0 -20
- package/dist/flow/plugins/types/optimize-node.d.ts.map +0 -1
- package/dist/flow/plugins/types/optimize-node.js +0 -11
- package/dist/flow/plugins/types/remove-background-node.d.ts +0 -16
- package/dist/flow/plugins/types/remove-background-node.d.ts.map +0 -1
- package/dist/flow/plugins/types/remove-background-node.js +0 -9
- package/dist/flow/plugins/types/resize-node.d.ts +0 -21
- package/dist/flow/plugins/types/resize-node.d.ts.map +0 -1
- package/dist/flow/plugins/types/resize-node.js +0 -16
- package/dist/flow/plugins/zip-plugin.d.ts +0 -62
- package/dist/flow/plugins/zip-plugin.d.ts.map +0 -1
- package/dist/flow/plugins/zip-plugin.js +0 -21
- package/dist/flow/typed-flow.d.ts +0 -90
- package/dist/flow/typed-flow.d.ts.map +0 -1
- package/dist/flow/typed-flow.js +0 -59
- package/dist/flow/types/flow-file.d.ts +0 -45
- package/dist/flow/types/flow-file.d.ts.map +0 -1
- package/dist/flow/types/flow-file.js +0 -27
- package/dist/flow/types/flow-job.d.ts +0 -118
- package/dist/flow/types/flow-job.d.ts.map +0 -1
- package/dist/flow/types/flow-job.js +0 -11
- package/dist/flow/types/flow-types.d.ts +0 -321
- package/dist/flow/types/flow-types.d.ts.map +0 -1
- package/dist/flow/types/flow-types.js +0 -52
- package/dist/flow/types/index.d.ts +0 -4
- package/dist/flow/types/index.d.ts.map +0 -1
- package/dist/flow/types/index.js +0 -3
- package/dist/flow/types/run-args.d.ts +0 -38
- package/dist/flow/types/run-args.d.ts.map +0 -1
- package/dist/flow/types/run-args.js +0 -30
- package/dist/flow/types/type-validator.d.ts +0 -26
- package/dist/flow/types/type-validator.d.ts.map +0 -1
- package/dist/flow/types/type-validator.js +0 -134
- package/dist/flow/utils/resolve-upload-metadata.d.ts +0 -11
- package/dist/flow/utils/resolve-upload-metadata.d.ts.map +0 -1
- package/dist/flow/utils/resolve-upload-metadata.js +0 -28
- package/dist/flow-2zXnEiWL.cjs +0 -1
- package/dist/flow-CRaKy7Vj.js +0 -2
- package/dist/flow-CRaKy7Vj.js.map +0 -1
- package/dist/generate-id-Dm-Vboxq.d.ts +0 -34
- package/dist/generate-id-Dm-Vboxq.d.ts.map +0 -1
- package/dist/generate-id-LjJRLD6N.d.cts +0 -34
- package/dist/generate-id-LjJRLD6N.d.cts.map +0 -1
- package/dist/generate-id-xHp_Z7Cl.cjs +0 -1
- package/dist/generate-id-yohS1ZDk.js +0 -2
- package/dist/generate-id-yohS1ZDk.js.map +0 -1
- package/dist/index-BO8GZlbD.d.cts +0 -1040
- package/dist/index-BO8GZlbD.d.cts.map +0 -1
- package/dist/index-D-CoVpkZ.d.ts +0 -1004
- package/dist/index-D-CoVpkZ.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/logger/logger.cjs +0 -1
- package/dist/logger/logger.d.cts +0 -8
- package/dist/logger/logger.d.cts.map +0 -1
- package/dist/logger/logger.d.ts +0 -5
- package/dist/logger/logger.d.ts.map +0 -1
- package/dist/logger/logger.js +0 -10
- package/dist/logger/logger.js.map +0 -1
- package/dist/semaphore-0ZwjVpyF.js +0 -2
- package/dist/semaphore-0ZwjVpyF.js.map +0 -1
- package/dist/semaphore-BHprIjFI.d.cts +0 -37
- package/dist/semaphore-BHprIjFI.d.cts.map +0 -1
- package/dist/semaphore-DThupBkc.d.ts +0 -37
- package/dist/semaphore-DThupBkc.d.ts.map +0 -1
- package/dist/semaphore-DVrONiAV.cjs +0 -1
- package/dist/stream-limiter-CoWKv39w.js +0 -2
- package/dist/stream-limiter-JgOwmkMa.cjs +0 -1
- package/dist/streams/multi-stream.cjs +0 -1
- package/dist/streams/multi-stream.d.cts +0 -91
- package/dist/streams/multi-stream.d.cts.map +0 -1
- package/dist/streams/multi-stream.d.ts +0 -86
- package/dist/streams/multi-stream.d.ts.map +0 -1
- package/dist/streams/multi-stream.js +0 -149
- package/dist/streams/multi-stream.js.map +0 -1
- package/dist/streams/stream-limiter.cjs +0 -1
- package/dist/streams/stream-limiter.d.cts.map +0 -1
- package/dist/streams/stream-limiter.d.ts +0 -27
- package/dist/streams/stream-limiter.d.ts.map +0 -1
- package/dist/streams/stream-limiter.js +0 -49
- package/dist/streams/stream-splitter.cjs +0 -1
- package/dist/streams/stream-splitter.d.cts +0 -68
- package/dist/streams/stream-splitter.d.cts.map +0 -1
- package/dist/streams/stream-splitter.d.ts +0 -51
- package/dist/streams/stream-splitter.d.ts.map +0 -1
- package/dist/streams/stream-splitter.js +0 -175
- package/dist/streams/stream-splitter.js.map +0 -1
- package/dist/types/data-store-registry.d.ts +0 -13
- package/dist/types/data-store-registry.d.ts.map +0 -1
- package/dist/types/data-store-registry.js +0 -4
- package/dist/types/data-store.d.ts +0 -316
- package/dist/types/data-store.d.ts.map +0 -1
- package/dist/types/data-store.js +0 -157
- package/dist/types/event-broadcaster.d.ts +0 -28
- package/dist/types/event-broadcaster.d.ts.map +0 -1
- package/dist/types/event-broadcaster.js +0 -6
- package/dist/types/event-emitter.d.ts +0 -378
- package/dist/types/event-emitter.d.ts.map +0 -1
- package/dist/types/event-emitter.js +0 -223
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/input-file.d.ts +0 -104
- package/dist/types/input-file.d.ts.map +0 -1
- package/dist/types/input-file.js +0 -27
- package/dist/types/kv-store.d.ts +0 -281
- package/dist/types/kv-store.d.ts.map +0 -1
- package/dist/types/kv-store.js +0 -234
- package/dist/types/middleware.d.ts +0 -17
- package/dist/types/middleware.d.ts.map +0 -1
- package/dist/types/middleware.js +0 -21
- package/dist/types/upload-event.d.ts +0 -105
- package/dist/types/upload-event.d.ts.map +0 -1
- package/dist/types/upload-event.js +0 -71
- package/dist/types/upload-file.d.ts +0 -136
- package/dist/types/upload-file.d.ts.map +0 -1
- package/dist/types/upload-file.js +0 -34
- package/dist/types/websocket.d.ts +0 -144
- package/dist/types/websocket.d.ts.map +0 -1
- package/dist/types/websocket.js +0 -40
- package/dist/types-BT-cvi7T.cjs +0 -1
- package/dist/types-DhU2j-XF.js +0 -2
- package/dist/types-DhU2j-XF.js.map +0 -1
- package/dist/upload/convert-to-stream.d.ts +0 -38
- package/dist/upload/convert-to-stream.d.ts.map +0 -1
- package/dist/upload/convert-to-stream.js +0 -43
- package/dist/upload/convert-upload-to-flow-file.d.ts +0 -14
- package/dist/upload/convert-upload-to-flow-file.d.ts.map +0 -1
- package/dist/upload/convert-upload-to-flow-file.js +0 -21
- package/dist/upload/create-upload.d.ts +0 -68
- package/dist/upload/create-upload.d.ts.map +0 -1
- package/dist/upload/create-upload.js +0 -157
- package/dist/upload/index.d.ts.map +0 -1
- package/dist/upload/mime.d.ts +0 -24
- package/dist/upload/mime.d.ts.map +0 -1
- package/dist/upload/mime.js +0 -351
- package/dist/upload/upload-chunk.d.ts +0 -58
- package/dist/upload/upload-chunk.d.ts.map +0 -1
- package/dist/upload/upload-chunk.js +0 -277
- package/dist/upload/upload-server.d.ts +0 -221
- package/dist/upload/upload-server.d.ts.map +0 -1
- package/dist/upload/upload-server.js +0 -181
- package/dist/upload/upload-strategy-negotiator.d.ts +0 -148
- package/dist/upload/upload-strategy-negotiator.d.ts.map +0 -1
- package/dist/upload/upload-strategy-negotiator.js +0 -217
- package/dist/upload/upload-url.d.ts +0 -68
- package/dist/upload/upload-url.d.ts.map +0 -1
- package/dist/upload/upload-url.js +0 -142
- package/dist/upload/write-to-store.d.ts +0 -77
- package/dist/upload/write-to-store.d.ts.map +0 -1
- package/dist/upload/write-to-store.js +0 -147
- package/dist/upload-DLuICjpP.cjs +0 -1
- package/dist/upload-DaXO34dE.js +0 -2
- package/dist/upload-DaXO34dE.js.map +0 -1
- package/dist/uploadista-error-BVsVxqvz.js.map +0 -1
- package/dist/uploadista-error-CwxYs4EB.d.ts +0 -52
- package/dist/uploadista-error-CwxYs4EB.d.ts.map +0 -1
- package/dist/uploadista-error-kKlhLRhY.d.cts +0 -52
- package/dist/uploadista-error-kKlhLRhY.d.cts.map +0 -1
- package/dist/utils/checksum.d.ts +0 -22
- package/dist/utils/checksum.d.ts.map +0 -1
- package/dist/utils/checksum.js +0 -49
- package/dist/utils/debounce.cjs +0 -1
- package/dist/utils/debounce.d.cts +0 -38
- package/dist/utils/debounce.d.cts.map +0 -1
- package/dist/utils/debounce.d.ts +0 -36
- package/dist/utils/debounce.d.ts.map +0 -1
- package/dist/utils/debounce.js +0 -73
- package/dist/utils/generate-id.cjs +0 -1
- package/dist/utils/generate-id.d.cts +0 -2
- package/dist/utils/generate-id.d.ts +0 -32
- package/dist/utils/generate-id.d.ts.map +0 -1
- package/dist/utils/generate-id.js +0 -23
- package/dist/utils/md5.cjs +0 -1
- package/dist/utils/md5.d.cts +0 -73
- package/dist/utils/md5.d.cts.map +0 -1
- package/dist/utils/md5.d.ts +0 -71
- package/dist/utils/md5.d.ts.map +0 -1
- package/dist/utils/md5.js +0 -417
- package/dist/utils/md5.js.map +0 -1
- package/dist/utils/once.cjs +0 -1
- package/dist/utils/once.d.cts +0 -25
- package/dist/utils/once.d.cts.map +0 -1
- package/dist/utils/once.d.ts +0 -21
- package/dist/utils/once.d.ts.map +0 -1
- package/dist/utils/once.js +0 -54
- package/dist/utils/once.js.map +0 -1
- package/dist/utils/semaphore.cjs +0 -1
- package/dist/utils/semaphore.d.cts +0 -3
- package/dist/utils/semaphore.d.ts +0 -78
- package/dist/utils/semaphore.d.ts.map +0 -1
- package/dist/utils/semaphore.js +0 -134
- package/dist/utils/throttle.cjs +0 -1
- package/dist/utils/throttle.d.cts +0 -24
- package/dist/utils/throttle.d.cts.map +0 -1
- package/dist/utils/throttle.d.ts +0 -18
- package/dist/utils/throttle.d.ts.map +0 -1
- package/dist/utils/throttle.js +0 -20
- package/dist/utils/throttle.js.map +0 -1
- package/src/logger/logger.ts +0 -14
- /package/dist/{errors-CRm1FHHT.cjs → errors-D-K-vxsP.cjs} +0 -0
|
@@ -0,0 +1,3952 @@
|
|
|
1
|
+
import { n as UploadistaError$1 } from "./uploadista-error-D9SONF9K.js";
|
|
2
|
+
import { l as GenerateId, p as GenerateIdShape } from "./index-DMJv8Tvo.js";
|
|
3
|
+
import { Context, Effect, Layer, Stream } from "effect";
|
|
4
|
+
import z$1, { z } from "zod";
|
|
5
|
+
import { UploadistaError } from "@uploadista/core/errors";
|
|
6
|
+
|
|
7
|
+
//#region src/flow/node.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Defines the type of node in a flow, determining its role in the processing pipeline.
|
|
10
|
+
*/
|
|
11
|
+
declare enum NodeType {
|
|
12
|
+
/** Entry point for data into the flow */
|
|
13
|
+
input = "input",
|
|
14
|
+
/** Transforms data during flow execution */
|
|
15
|
+
process = "process",
|
|
16
|
+
/** Saves data to storage backends */
|
|
17
|
+
output = "output",
|
|
18
|
+
/** Routes data based on conditions */
|
|
19
|
+
conditional = "conditional",
|
|
20
|
+
/** Splits data to multiple outputs */
|
|
21
|
+
multiplex = "multiplex",
|
|
22
|
+
/** Combines multiple inputs into one output */
|
|
23
|
+
merge = "merge",
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fields that can be evaluated in conditional node conditions.
|
|
27
|
+
* These fields are typically found in file metadata.
|
|
28
|
+
*/
|
|
29
|
+
type ConditionField = "mimeType" | "size" | "width" | "height" | "extension";
|
|
30
|
+
/**
|
|
31
|
+
* Operators available for comparing values in conditional node conditions.
|
|
32
|
+
*/
|
|
33
|
+
type ConditionOperator = "equals" | "notEquals" | "greaterThan" | "lessThan" | "contains" | "startsWith";
|
|
34
|
+
/**
|
|
35
|
+
* Value used in conditional node comparisons.
|
|
36
|
+
* Can be either a string or number depending on the field being evaluated.
|
|
37
|
+
*/
|
|
38
|
+
type ConditionValue = string | number;
|
|
39
|
+
/**
|
|
40
|
+
* Creates a flow node with automatic input/output validation and retry logic.
|
|
41
|
+
*
|
|
42
|
+
* Flow nodes are the building blocks of processing pipelines. Each node:
|
|
43
|
+
* - Validates its input against a Zod schema
|
|
44
|
+
* - Executes its processing logic
|
|
45
|
+
* - Validates its output against a Zod schema
|
|
46
|
+
* - Can optionally retry on failure with exponential backoff
|
|
47
|
+
*
|
|
48
|
+
* @template Input - The expected input type for this node
|
|
49
|
+
* @template Output - The output type produced by this node
|
|
50
|
+
*
|
|
51
|
+
* @param config - Node configuration
|
|
52
|
+
* @param config.id - Unique identifier for this node in the flow
|
|
53
|
+
* @param config.name - Human-readable name for the node
|
|
54
|
+
* @param config.description - Description of what this node does
|
|
55
|
+
* @param config.type - The type of node (input, process, output, conditional, multiplex, merge)
|
|
56
|
+
* @param config.inputSchema - Zod schema for validating input data
|
|
57
|
+
* @param config.outputSchema - Zod schema for validating output data
|
|
58
|
+
* @param config.run - The processing function to execute for this node
|
|
59
|
+
* @param config.condition - Optional condition for conditional nodes to determine if they should execute
|
|
60
|
+
* @param config.multiInput - If true, node receives all inputs as a record instead of a single input
|
|
61
|
+
* @param config.multiOutput - If true, node can output to multiple targets
|
|
62
|
+
* @param config.pausable - If true, node can pause execution and wait for additional data
|
|
63
|
+
* @param config.retry - Optional retry configuration for handling transient failures
|
|
64
|
+
* @param config.retry.maxRetries - Maximum number of retry attempts (default: 0)
|
|
65
|
+
* @param config.retry.retryDelay - Base delay in milliseconds between retries (default: 1000)
|
|
66
|
+
* @param config.retry.exponentialBackoff - Whether to use exponential backoff for retries (default: true)
|
|
67
|
+
*
|
|
68
|
+
* @returns An Effect that succeeds with the created FlowNode
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const resizeNode = createFlowNode({
|
|
73
|
+
* id: "resize-1",
|
|
74
|
+
* name: "Resize Image",
|
|
75
|
+
* description: "Resizes images to 800x600",
|
|
76
|
+
* type: NodeType.process,
|
|
77
|
+
* inputSchema: z.object({
|
|
78
|
+
* stream: z.instanceof(Uint8Array),
|
|
79
|
+
* metadata: z.object({ width: z.number(), height: z.number() })
|
|
80
|
+
* }),
|
|
81
|
+
* outputSchema: z.object({
|
|
82
|
+
* stream: z.instanceof(Uint8Array),
|
|
83
|
+
* metadata: z.object({ width: z.literal(800), height: z.literal(600) })
|
|
84
|
+
* }),
|
|
85
|
+
* run: ({ data }) => Effect.gen(function* () {
|
|
86
|
+
* const resized = yield* resizeImage(data.stream, 800, 600);
|
|
87
|
+
* return {
|
|
88
|
+
* type: "complete",
|
|
89
|
+
* data: { stream: resized, metadata: { width: 800, height: 600 } }
|
|
90
|
+
* };
|
|
91
|
+
* }),
|
|
92
|
+
* retry: {
|
|
93
|
+
* maxRetries: 3,
|
|
94
|
+
* retryDelay: 1000,
|
|
95
|
+
* exponentialBackoff: true
|
|
96
|
+
* }
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function createFlowNode<Input, Output>({
|
|
101
|
+
id,
|
|
102
|
+
name,
|
|
103
|
+
description,
|
|
104
|
+
type,
|
|
105
|
+
inputSchema,
|
|
106
|
+
outputSchema,
|
|
107
|
+
run,
|
|
108
|
+
condition,
|
|
109
|
+
multiInput,
|
|
110
|
+
multiOutput,
|
|
111
|
+
pausable,
|
|
112
|
+
retry
|
|
113
|
+
}: {
|
|
114
|
+
id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
description: string;
|
|
117
|
+
type: NodeType;
|
|
118
|
+
inputSchema: z.ZodSchema<Input>;
|
|
119
|
+
outputSchema: z.ZodSchema<Output>;
|
|
120
|
+
run: (args: {
|
|
121
|
+
data: Input;
|
|
122
|
+
jobId: string;
|
|
123
|
+
storageId: string;
|
|
124
|
+
flowId: string;
|
|
125
|
+
clientId: string | null;
|
|
126
|
+
}) => Effect.Effect<NodeExecutionResult<Output>, UploadistaError$1>;
|
|
127
|
+
condition?: {
|
|
128
|
+
field: ConditionField;
|
|
129
|
+
operator: ConditionOperator;
|
|
130
|
+
value: ConditionValue;
|
|
131
|
+
};
|
|
132
|
+
multiInput?: boolean;
|
|
133
|
+
multiOutput?: boolean;
|
|
134
|
+
pausable?: boolean;
|
|
135
|
+
retry?: {
|
|
136
|
+
maxRetries?: number;
|
|
137
|
+
retryDelay?: number;
|
|
138
|
+
exponentialBackoff?: boolean;
|
|
139
|
+
};
|
|
140
|
+
}): Effect.Effect<FlowNode<Input, Output, UploadistaError$1>>;
|
|
141
|
+
/**
|
|
142
|
+
* Extracts serializable node metadata from a FlowNode instance.
|
|
143
|
+
*
|
|
144
|
+
* This function is useful for serializing flow configurations or
|
|
145
|
+
* transmitting node information over the network without including
|
|
146
|
+
* the executable run function or schemas.
|
|
147
|
+
*
|
|
148
|
+
* @param node - The flow node to extract data from
|
|
149
|
+
* @returns A plain object containing the node's metadata (id, name, description, type)
|
|
150
|
+
*/
|
|
151
|
+
declare const getNodeData: (node: FlowNode<any, any, UploadistaError$1>) => FlowNodeData;
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/flow/event.d.ts
|
|
154
|
+
/**
|
|
155
|
+
* Enumeration of all possible flow and node execution event types.
|
|
156
|
+
*
|
|
157
|
+
* Events follow a lifecycle pattern:
|
|
158
|
+
* - Job level: JobStart → ... → JobEnd
|
|
159
|
+
* - Flow level: FlowStart → ... → (FlowEnd | FlowError)
|
|
160
|
+
* - Node level: NodeStart → ... → (NodeEnd | NodePause | NodeError)
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* // Listen for flow completion
|
|
165
|
+
* if (event.eventType === EventType.FlowEnd) {
|
|
166
|
+
* console.log("Flow completed:", event.result);
|
|
167
|
+
* }
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
declare enum EventType {
|
|
171
|
+
/** Emitted when a job starts execution */
|
|
172
|
+
JobStart = "job-start",
|
|
173
|
+
/** Emitted when a job completes (success or failure) */
|
|
174
|
+
JobEnd = "job-end",
|
|
175
|
+
/** Emitted when a flow begins execution */
|
|
176
|
+
FlowStart = "flow-start",
|
|
177
|
+
/** Emitted when a flow completes successfully */
|
|
178
|
+
FlowEnd = "flow-end",
|
|
179
|
+
/** Emitted when a flow encounters an error */
|
|
180
|
+
FlowError = "flow-error",
|
|
181
|
+
/** Emitted when a node starts processing */
|
|
182
|
+
NodeStart = "node-start",
|
|
183
|
+
/** Emitted when a node completes successfully */
|
|
184
|
+
NodeEnd = "node-end",
|
|
185
|
+
/** Emitted when a node pauses (waiting for additional data) */
|
|
186
|
+
NodePause = "node-pause",
|
|
187
|
+
/** Emitted when a paused node resumes execution */
|
|
188
|
+
NodeResume = "node-resume",
|
|
189
|
+
/** Emitted when a node encounters an error */
|
|
190
|
+
NodeError = "node-error",
|
|
191
|
+
/** Emitted for streaming node data (e.g., progress updates) */
|
|
192
|
+
NodeStream = "node-stream",
|
|
193
|
+
/** Emitted for node response data */
|
|
194
|
+
NodeResponse = "node-response",
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Event emitted when a job starts execution.
|
|
198
|
+
*/
|
|
199
|
+
type FlowEventJobStart = {
|
|
200
|
+
jobId: string;
|
|
201
|
+
eventType: EventType.JobStart;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Event emitted when a job completes (either successfully or with failure).
|
|
205
|
+
*/
|
|
206
|
+
type FlowEventJobEnd = {
|
|
207
|
+
jobId: string;
|
|
208
|
+
eventType: EventType.JobEnd;
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Event emitted when a flow begins execution.
|
|
212
|
+
* This is the first event after JobStart in the execution lifecycle.
|
|
213
|
+
*/
|
|
214
|
+
type FlowEventFlowStart = {
|
|
215
|
+
jobId: string;
|
|
216
|
+
flowId: string;
|
|
217
|
+
eventType: EventType.FlowStart;
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Event emitted when a flow completes successfully.
|
|
221
|
+
*
|
|
222
|
+
* @property result - The final output from all output nodes in the flow
|
|
223
|
+
*/
|
|
224
|
+
type FlowEventFlowEnd = {
|
|
225
|
+
jobId: string;
|
|
226
|
+
flowId: string;
|
|
227
|
+
eventType: EventType.FlowEnd;
|
|
228
|
+
result?: unknown;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Event emitted when a flow encounters an unrecoverable error.
|
|
232
|
+
*
|
|
233
|
+
* @property error - Error message describing what went wrong
|
|
234
|
+
*/
|
|
235
|
+
type FlowEventFlowError = {
|
|
236
|
+
jobId: string;
|
|
237
|
+
flowId: string;
|
|
238
|
+
eventType: EventType.FlowError;
|
|
239
|
+
error: string;
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Event emitted when a node begins processing.
|
|
243
|
+
*
|
|
244
|
+
* @property nodeName - Human-readable node name
|
|
245
|
+
* @property nodeType - Type of node (input, transform, conditional, output, etc.)
|
|
246
|
+
*/
|
|
247
|
+
type FlowEventNodeStart = {
|
|
248
|
+
jobId: string;
|
|
249
|
+
flowId: string;
|
|
250
|
+
nodeId: string;
|
|
251
|
+
eventType: EventType.NodeStart;
|
|
252
|
+
nodeName: string;
|
|
253
|
+
nodeType: NodeType;
|
|
254
|
+
};
|
|
255
|
+
/**
|
|
256
|
+
* Event emitted when a node fails after all retry attempts.
|
|
257
|
+
*
|
|
258
|
+
* @property error - Error message from the failed execution
|
|
259
|
+
* @property retryCount - Number of retry attempts made before giving up
|
|
260
|
+
*/
|
|
261
|
+
type FlowEventNodeError = {
|
|
262
|
+
jobId: string;
|
|
263
|
+
flowId: string;
|
|
264
|
+
nodeId: string;
|
|
265
|
+
nodeName: string;
|
|
266
|
+
eventType: EventType.NodeError;
|
|
267
|
+
error: string;
|
|
268
|
+
retryCount?: number;
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* Event emitted when a node completes successfully.
|
|
272
|
+
*
|
|
273
|
+
* @property result - The output data produced by the node
|
|
274
|
+
*/
|
|
275
|
+
type FlowEventNodeEnd = {
|
|
276
|
+
jobId: string;
|
|
277
|
+
flowId: string;
|
|
278
|
+
nodeId: string;
|
|
279
|
+
eventType: EventType.NodeEnd;
|
|
280
|
+
nodeName: string;
|
|
281
|
+
result?: unknown;
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* Event emitted when a node pauses execution, waiting for additional data.
|
|
285
|
+
*
|
|
286
|
+
* This typically occurs with input nodes that need more chunks or nodes
|
|
287
|
+
* waiting for external services.
|
|
288
|
+
*
|
|
289
|
+
* @property partialData - Any partial result available before pausing
|
|
290
|
+
*/
|
|
291
|
+
type FlowEventNodePause = {
|
|
292
|
+
jobId: string;
|
|
293
|
+
flowId: string;
|
|
294
|
+
nodeId: string;
|
|
295
|
+
eventType: EventType.NodePause;
|
|
296
|
+
nodeName: string;
|
|
297
|
+
partialData?: unknown;
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* Event emitted when a paused node resumes execution.
|
|
301
|
+
*
|
|
302
|
+
* This occurs after providing the additional data needed by a paused node.
|
|
303
|
+
*/
|
|
304
|
+
type FlowEventNodeResume = {
|
|
305
|
+
jobId: string;
|
|
306
|
+
flowId: string;
|
|
307
|
+
nodeId: string;
|
|
308
|
+
eventType: EventType.NodeResume;
|
|
309
|
+
nodeName: string;
|
|
310
|
+
nodeType: NodeType;
|
|
311
|
+
};
|
|
312
|
+
/**
|
|
313
|
+
* Event emitted for node-specific response data.
|
|
314
|
+
*
|
|
315
|
+
* Used for streaming intermediate results or progress updates.
|
|
316
|
+
*/
|
|
317
|
+
type FlowEventNodeResponse = {
|
|
318
|
+
jobId: string;
|
|
319
|
+
flowId: string;
|
|
320
|
+
nodeId: string;
|
|
321
|
+
eventType: EventType.NodeResponse;
|
|
322
|
+
nodeName: string;
|
|
323
|
+
data: unknown;
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* Union of all possible flow execution events.
|
|
327
|
+
*
|
|
328
|
+
* This discriminated union allows type-safe event handling based on eventType.
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```typescript
|
|
332
|
+
* function handleFlowEvent(event: FlowEvent) {
|
|
333
|
+
* switch (event.eventType) {
|
|
334
|
+
* case EventType.FlowStart:
|
|
335
|
+
* console.log("Flow started:", event.flowId);
|
|
336
|
+
* break;
|
|
337
|
+
* case EventType.NodeEnd:
|
|
338
|
+
* console.log("Node completed:", event.nodeName, event.result);
|
|
339
|
+
* break;
|
|
340
|
+
* case EventType.FlowError:
|
|
341
|
+
* console.error("Flow failed:", event.error);
|
|
342
|
+
* break;
|
|
343
|
+
* }
|
|
344
|
+
* }
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
type FlowEvent = FlowEventJobStart | FlowEventJobEnd | FlowEventFlowStart | FlowEventFlowEnd | FlowEventFlowError | FlowEventNodeStart | FlowEventNodeEnd | FlowEventNodePause | FlowEventNodeResume | FlowEventNodeError;
|
|
348
|
+
//#endregion
|
|
349
|
+
//#region src/flow/types/flow-types.d.ts
|
|
350
|
+
/**
|
|
351
|
+
* Type mapping for node input/output schemas.
|
|
352
|
+
* Used for type-safe node connections in typed flows.
|
|
353
|
+
*/
|
|
354
|
+
type NodeTypeMap = Record<string, {
|
|
355
|
+
input: unknown;
|
|
356
|
+
output: unknown;
|
|
357
|
+
}>;
|
|
358
|
+
/**
|
|
359
|
+
* Minimal node data without execution logic.
|
|
360
|
+
* Used for serialization and UI display.
|
|
361
|
+
*
|
|
362
|
+
* @property id - Unique node identifier
|
|
363
|
+
* @property name - Human-readable node name
|
|
364
|
+
* @property description - Explanation of what the node does
|
|
365
|
+
* @property type - Node category (input, transform, conditional, output, etc.)
|
|
366
|
+
*/
|
|
367
|
+
type FlowNodeData = {
|
|
368
|
+
id: string;
|
|
369
|
+
name: string;
|
|
370
|
+
description: string;
|
|
371
|
+
type: NodeType;
|
|
372
|
+
};
|
|
373
|
+
/**
|
|
374
|
+
* Result of a node execution - either complete or waiting for more data.
|
|
375
|
+
*
|
|
376
|
+
* @template TOutput - Type of the node's output data
|
|
377
|
+
*
|
|
378
|
+
* @remarks
|
|
379
|
+
* Nodes can return "waiting" to pause flow execution when they need additional
|
|
380
|
+
* data (e.g., chunked uploads, external service responses). The flow can be
|
|
381
|
+
* resumed later with the missing data.
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* // Node completes immediately
|
|
386
|
+
* return completeNodeExecution({ processedData });
|
|
387
|
+
*
|
|
388
|
+
* // Node waits for more chunks
|
|
389
|
+
* if (needsMoreData) {
|
|
390
|
+
* return waitingNodeExecution({ receivedChunks: 3, totalChunks: 10 });
|
|
391
|
+
* }
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
type NodeExecutionResult<TOutput$1> = {
|
|
395
|
+
type: "complete";
|
|
396
|
+
data: TOutput$1;
|
|
397
|
+
} | {
|
|
398
|
+
type: "waiting";
|
|
399
|
+
partialData?: unknown;
|
|
400
|
+
};
|
|
401
|
+
/**
|
|
402
|
+
* Helper function to create a complete node execution result.
|
|
403
|
+
*
|
|
404
|
+
* @template TOutput - Type of the output data
|
|
405
|
+
* @param data - The output data from the node
|
|
406
|
+
* @returns A complete execution result
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* ```typescript
|
|
410
|
+
* return completeNodeExecution({
|
|
411
|
+
* url: uploadedFile.url,
|
|
412
|
+
* size: uploadedFile.size
|
|
413
|
+
* });
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
declare const completeNodeExecution: <TOutput>(data: TOutput) => {
|
|
417
|
+
type: "complete";
|
|
418
|
+
data: TOutput;
|
|
419
|
+
};
|
|
420
|
+
/**
|
|
421
|
+
* Helper function to create a waiting node execution result.
|
|
422
|
+
*
|
|
423
|
+
* @param partialData - Optional partial data available so far
|
|
424
|
+
* @returns A waiting execution result that pauses the flow
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```typescript
|
|
428
|
+
* // Wait for more upload chunks
|
|
429
|
+
* return waitingNodeExecution({
|
|
430
|
+
* receivedBytes: currentSize,
|
|
431
|
+
* totalBytes: expectedSize
|
|
432
|
+
* });
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
declare const waitingNodeExecution: (partialData?: unknown) => {
|
|
436
|
+
type: "waiting";
|
|
437
|
+
partialData: unknown;
|
|
438
|
+
};
|
|
439
|
+
/**
|
|
440
|
+
* A flow node represents a single processing step in the DAG.
|
|
441
|
+
*
|
|
442
|
+
* Nodes are the building blocks of flows. Each node has typed inputs/outputs,
|
|
443
|
+
* execution logic, and optional features like conditions, retries, and pausing.
|
|
444
|
+
*
|
|
445
|
+
* @template TInput - Type of data the node accepts
|
|
446
|
+
* @template TOutput - Type of data the node produces
|
|
447
|
+
* @template TError - Type of errors the node can throw
|
|
448
|
+
*
|
|
449
|
+
* @property inputSchema - Zod schema for validating input data
|
|
450
|
+
* @property outputSchema - Zod schema for validating output data
|
|
451
|
+
* @property run - Effect-based execution function
|
|
452
|
+
* @property condition - Optional conditional execution rule
|
|
453
|
+
* @property multiInput - Whether node accepts multiple inputs (default: false)
|
|
454
|
+
* @property multiOutput - Whether node produces multiple outputs (default: false)
|
|
455
|
+
* @property pausable - Whether node can pause execution (default: false)
|
|
456
|
+
* @property retry - Optional retry configuration
|
|
457
|
+
*
|
|
458
|
+
* @remarks
|
|
459
|
+
* - Nodes use Effect for composable error handling and dependency injection
|
|
460
|
+
* - Input/output schemas ensure type safety at runtime
|
|
461
|
+
* - Conditions are evaluated before execution
|
|
462
|
+
* - Retry logic supports exponential backoff
|
|
463
|
+
* - Pausable nodes can halt flow execution and resume later
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
* ```typescript
|
|
467
|
+
* const resizeNode: FlowNode<InputFile, UploadFile> = {
|
|
468
|
+
* id: "resize",
|
|
469
|
+
* name: "Resize Image",
|
|
470
|
+
* description: "Resize image to specified dimensions",
|
|
471
|
+
* type: NodeType.transform,
|
|
472
|
+
* inputSchema: inputFileSchema,
|
|
473
|
+
* outputSchema: uploadFileSchema,
|
|
474
|
+
* run: ({ data, storageId }) => Effect.gen(function* () {
|
|
475
|
+
* const resized = yield* resizeImage(data, { width: 1920, height: 1080 });
|
|
476
|
+
* return completeNodeExecution(resized);
|
|
477
|
+
* }),
|
|
478
|
+
* retry: {
|
|
479
|
+
* maxRetries: 3,
|
|
480
|
+
* retryDelay: 1000,
|
|
481
|
+
* exponentialBackoff: true
|
|
482
|
+
* }
|
|
483
|
+
* };
|
|
484
|
+
* ```
|
|
485
|
+
*
|
|
486
|
+
* @see {@link NodeExecutionResult} for return type options
|
|
487
|
+
* @see {@link FlowCondition} for conditional execution
|
|
488
|
+
*/
|
|
489
|
+
type FlowNode<TInput = unknown, TOutput$1 = unknown, TError$1 = UploadistaError$1> = FlowNodeData & {
|
|
490
|
+
inputSchema: z.ZodSchema<TInput>;
|
|
491
|
+
outputSchema: z.ZodSchema<TOutput$1>;
|
|
492
|
+
run: (args: {
|
|
493
|
+
data: TInput;
|
|
494
|
+
jobId: string;
|
|
495
|
+
storageId: string;
|
|
496
|
+
flowId: string;
|
|
497
|
+
inputs?: Record<string, unknown>;
|
|
498
|
+
clientId: string | null;
|
|
499
|
+
}) => Effect.Effect<NodeExecutionResult<TOutput$1>, TError$1>;
|
|
500
|
+
condition?: {
|
|
501
|
+
field: string;
|
|
502
|
+
operator: string;
|
|
503
|
+
value: unknown;
|
|
504
|
+
};
|
|
505
|
+
multiInput?: boolean;
|
|
506
|
+
multiOutput?: boolean;
|
|
507
|
+
pausable?: boolean;
|
|
508
|
+
retry?: {
|
|
509
|
+
maxRetries?: number;
|
|
510
|
+
retryDelay?: number;
|
|
511
|
+
exponentialBackoff?: boolean;
|
|
512
|
+
};
|
|
513
|
+
};
|
|
514
|
+
/**
|
|
515
|
+
* Represents a directed edge connecting two nodes in the flow graph.
|
|
516
|
+
*
|
|
517
|
+
* Edges define the data flow direction and can specify ports for multi-input/output nodes.
|
|
518
|
+
*
|
|
519
|
+
* @property source - ID of the source node
|
|
520
|
+
* @property target - ID of the target node
|
|
521
|
+
* @property sourcePort - Optional output port name for multi-output nodes
|
|
522
|
+
* @property targetPort - Optional input port name for multi-input nodes
|
|
523
|
+
*
|
|
524
|
+
* @remarks
|
|
525
|
+
* - Edges must not create cycles (DAG constraint)
|
|
526
|
+
* - Source node's output type should be compatible with target node's input type
|
|
527
|
+
* - Ports allow routing specific outputs to specific inputs
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```typescript
|
|
531
|
+
* // Simple edge
|
|
532
|
+
* const edge: FlowEdge = {
|
|
533
|
+
* source: "resize-node",
|
|
534
|
+
* target: "optimize-node"
|
|
535
|
+
* };
|
|
536
|
+
*
|
|
537
|
+
* // Edge with ports (for multiplex nodes)
|
|
538
|
+
* const multiplexEdge: FlowEdge = {
|
|
539
|
+
* source: "multiplex-node",
|
|
540
|
+
* target: "output-node",
|
|
541
|
+
* sourcePort: "image",
|
|
542
|
+
* targetPort: "primary"
|
|
543
|
+
* };
|
|
544
|
+
* ```
|
|
545
|
+
*/
|
|
546
|
+
type FlowEdge$1 = {
|
|
547
|
+
source: string;
|
|
548
|
+
target: string;
|
|
549
|
+
sourcePort?: string;
|
|
550
|
+
targetPort?: string;
|
|
551
|
+
};
|
|
552
|
+
/**
|
|
553
|
+
* Function type for checking schema compatibility between nodes.
|
|
554
|
+
*
|
|
555
|
+
* @param from - Source node's output schema
|
|
556
|
+
* @param to - Target node's input schema
|
|
557
|
+
* @returns true if schemas are compatible
|
|
558
|
+
*
|
|
559
|
+
* @remarks
|
|
560
|
+
* Custom type checkers can implement more sophisticated compatibility rules
|
|
561
|
+
* than the default checker.
|
|
562
|
+
*
|
|
563
|
+
* @see {@link FlowTypeValidator} for the default implementation
|
|
564
|
+
*/
|
|
565
|
+
type TypeCompatibilityChecker = (from: z.ZodSchema<any>, to: z.ZodSchema<any>) => boolean;
|
|
566
|
+
/**
|
|
567
|
+
* Interface for validating node connections and schema compatibility.
|
|
568
|
+
*
|
|
569
|
+
* @remarks
|
|
570
|
+
* Validators ensure that connected nodes have compatible types,
|
|
571
|
+
* preventing runtime type errors in flow execution.
|
|
572
|
+
*
|
|
573
|
+
* @see {@link FlowTypeValidator} for the implementation
|
|
574
|
+
*/
|
|
575
|
+
type NodeConnectionValidator = {
|
|
576
|
+
validateConnection: (sourceNode: FlowNode<any, any>, targetNode: FlowNode<any, any>, edge: FlowEdge$1) => boolean;
|
|
577
|
+
getCompatibleTypes: (sourceSchema: z.ZodSchema<any>, targetSchema: z.ZodSchema<any>) => boolean;
|
|
578
|
+
};
|
|
579
|
+
/**
|
|
580
|
+
* Configuration object for creating a new flow.
|
|
581
|
+
*
|
|
582
|
+
* FlowConfig defines all aspects of a flow including its nodes, connections,
|
|
583
|
+
* schemas, and optional features like event handlers and parallel execution.
|
|
584
|
+
*
|
|
585
|
+
* @template TFlowInputSchema - Zod schema for flow inputs
|
|
586
|
+
* @template TFlowOutputSchema - Zod schema for flow outputs
|
|
587
|
+
* @template TNodeError - Union of possible errors from node Effects
|
|
588
|
+
* @template TNodeRequirements - Union of requirements from node Effects
|
|
589
|
+
*
|
|
590
|
+
* @property flowId - Unique identifier for the flow
|
|
591
|
+
* @property name - Human-readable flow name
|
|
592
|
+
* @property nodes - Array of nodes (can be plain nodes or Effects resolving to nodes)
|
|
593
|
+
* @property edges - Array of edges connecting the nodes
|
|
594
|
+
* @property inputSchema - Zod schema for validating flow inputs
|
|
595
|
+
* @property outputSchema - Zod schema for validating flow outputs
|
|
596
|
+
* @property typeChecker - Optional custom type compatibility checker
|
|
597
|
+
* @property onEvent - Optional event handler for monitoring execution
|
|
598
|
+
* @property parallelExecution - Optional parallel execution configuration
|
|
599
|
+
*
|
|
600
|
+
* @remarks
|
|
601
|
+
* - Nodes can be provided as plain objects or Effect-wrapped for lazy initialization
|
|
602
|
+
* - Event handlers receive all flow and node events
|
|
603
|
+
* - Parallel execution is experimental and disabled by default
|
|
604
|
+
* - Type checker allows custom schema compatibility rules
|
|
605
|
+
*
|
|
606
|
+
* @example
|
|
607
|
+
* ```typescript
|
|
608
|
+
* const config: FlowConfig<
|
|
609
|
+
* z.ZodObject<{ file: z.ZodType<File> }>,
|
|
610
|
+
* z.ZodType<UploadFile>,
|
|
611
|
+
* never,
|
|
612
|
+
* never
|
|
613
|
+
* > = {
|
|
614
|
+
* flowId: "image-upload",
|
|
615
|
+
* name: "Image Upload Pipeline",
|
|
616
|
+
* nodes: [inputNode, resizeNode, optimizeNode, storageNode],
|
|
617
|
+
* edges: [
|
|
618
|
+
* { source: "input", target: "resize" },
|
|
619
|
+
* { source: "resize", target: "optimize" },
|
|
620
|
+
* { source: "optimize", target: "storage" }
|
|
621
|
+
* ],
|
|
622
|
+
* inputSchema: z.object({ file: z.instanceof(File) }),
|
|
623
|
+
* outputSchema: uploadFileSchema,
|
|
624
|
+
* onEvent: (event) => Effect.gen(function* () {
|
|
625
|
+
* yield* logEvent(event);
|
|
626
|
+
* return { eventId: event.jobId };
|
|
627
|
+
* })
|
|
628
|
+
* };
|
|
629
|
+
* ```
|
|
630
|
+
*
|
|
631
|
+
* @see {@link createFlowWithSchema} for creating flows from config
|
|
632
|
+
* @see {@link FlowNode} for node specifications
|
|
633
|
+
* @see {@link FlowEdge} for edge specifications
|
|
634
|
+
*/
|
|
635
|
+
type FlowConfig<TFlowInputSchema extends z.ZodSchema<any>, TFlowOutputSchema extends z.ZodSchema<any>, TNodeError = never, TNodeRequirements = never> = {
|
|
636
|
+
flowId: string;
|
|
637
|
+
name: string;
|
|
638
|
+
nodes: Array<FlowNode<any, any, UploadistaError$1> | Effect.Effect<FlowNode<any, any, UploadistaError$1>, TNodeError, TNodeRequirements>>;
|
|
639
|
+
edges: FlowEdge$1[];
|
|
640
|
+
inputSchema: TFlowInputSchema;
|
|
641
|
+
outputSchema: TFlowOutputSchema;
|
|
642
|
+
typeChecker?: TypeCompatibilityChecker;
|
|
643
|
+
onEvent?: (event: FlowEvent) => Effect.Effect<{
|
|
644
|
+
eventId: string | null;
|
|
645
|
+
}, UploadistaError$1>;
|
|
646
|
+
parallelExecution?: {
|
|
647
|
+
enabled?: boolean;
|
|
648
|
+
maxConcurrency?: number;
|
|
649
|
+
};
|
|
650
|
+
};
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region src/flow/edge.d.ts
|
|
653
|
+
/**
|
|
654
|
+
* Represents a connection between two nodes in a flow, defining the data flow direction.
|
|
655
|
+
*
|
|
656
|
+
* Edges connect the output of a source node to the input of a target node,
|
|
657
|
+
* enabling data to flow through the processing pipeline in a directed acyclic graph (DAG).
|
|
658
|
+
*/
|
|
659
|
+
type FlowEdge = FlowEdge$1;
|
|
660
|
+
/**
|
|
661
|
+
* Creates a flow edge connecting two nodes in a processing pipeline.
|
|
662
|
+
*
|
|
663
|
+
* Edges define how data flows between nodes. The data output from the source node
|
|
664
|
+
* becomes the input for the target node. For nodes with multiple inputs/outputs,
|
|
665
|
+
* ports can be specified to route data to specific connections.
|
|
666
|
+
*
|
|
667
|
+
* @param config - Edge configuration
|
|
668
|
+
* @param config.source - ID of the source node (data originates here)
|
|
669
|
+
* @param config.target - ID of the target node (data flows to here)
|
|
670
|
+
* @param config.sourcePort - Optional port name on the source node for multi-output nodes
|
|
671
|
+
* @param config.targetPort - Optional port name on the target node for multi-input nodes
|
|
672
|
+
*
|
|
673
|
+
* @returns A FlowEdge object representing the connection
|
|
674
|
+
*
|
|
675
|
+
* @example
|
|
676
|
+
* ```typescript
|
|
677
|
+
* // Simple edge connecting two nodes
|
|
678
|
+
* const edge = createFlowEdge({
|
|
679
|
+
* source: "input-1",
|
|
680
|
+
* target: "process-1"
|
|
681
|
+
* });
|
|
682
|
+
*
|
|
683
|
+
* // Edge with ports for multi-input/output nodes
|
|
684
|
+
* const portEdge = createFlowEdge({
|
|
685
|
+
* source: "multiplex-1",
|
|
686
|
+
* target: "merge-1",
|
|
687
|
+
* sourcePort: "out-a",
|
|
688
|
+
* targetPort: "in-1"
|
|
689
|
+
* });
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
declare function createFlowEdge({
|
|
693
|
+
source,
|
|
694
|
+
target,
|
|
695
|
+
sourcePort,
|
|
696
|
+
targetPort
|
|
697
|
+
}: {
|
|
698
|
+
source: string;
|
|
699
|
+
target: string;
|
|
700
|
+
sourcePort?: string;
|
|
701
|
+
targetPort?: string;
|
|
702
|
+
}): FlowEdge;
|
|
703
|
+
//#endregion
|
|
704
|
+
//#region src/flow/flow.d.ts
|
|
705
|
+
/**
|
|
706
|
+
* Serialized flow data for storage and transport.
|
|
707
|
+
* Contains the minimal information needed to reconstruct a flow.
|
|
708
|
+
*
|
|
709
|
+
* @property id - Unique flow identifier
|
|
710
|
+
* @property name - Human-readable flow name
|
|
711
|
+
* @property nodes - Array of node data (without execution logic)
|
|
712
|
+
* @property edges - Connections between nodes defining data flow
|
|
713
|
+
*/
|
|
714
|
+
type FlowData = {
|
|
715
|
+
id: string;
|
|
716
|
+
name: string;
|
|
717
|
+
nodes: FlowNodeData[];
|
|
718
|
+
edges: FlowEdge[];
|
|
719
|
+
};
|
|
720
|
+
/**
|
|
721
|
+
* Extracts serializable flow data from a Flow instance.
|
|
722
|
+
* Useful for storing flow definitions or sending them over the network.
|
|
723
|
+
*
|
|
724
|
+
* @template TRequirements - Effect requirements for the flow
|
|
725
|
+
* @param flow - Flow instance to extract data from
|
|
726
|
+
* @returns Serializable flow data without execution logic
|
|
727
|
+
*
|
|
728
|
+
* @example
|
|
729
|
+
* ```typescript
|
|
730
|
+
* const flowData = getFlowData(myFlow);
|
|
731
|
+
* // Store in database or send to client
|
|
732
|
+
* await db.flows.save(flowData);
|
|
733
|
+
* ```
|
|
734
|
+
*/
|
|
735
|
+
declare const getFlowData: <TRequirements>(flow: Flow<any, any, TRequirements>) => FlowData;
|
|
736
|
+
/**
|
|
737
|
+
* Result of a flow execution - either completed or paused.
|
|
738
|
+
*
|
|
739
|
+
* @template TOutput - Type of the flow's output data
|
|
740
|
+
*
|
|
741
|
+
* @remarks
|
|
742
|
+
* Flows can pause when a node needs additional data (e.g., waiting for user input
|
|
743
|
+
* or external service). The execution state allows resuming from where it paused.
|
|
744
|
+
*
|
|
745
|
+
* @example
|
|
746
|
+
* ```typescript
|
|
747
|
+
* const result = await Effect.runPromise(flow.run({ inputs, storageId, jobId }));
|
|
748
|
+
*
|
|
749
|
+
* if (result.type === "completed") {
|
|
750
|
+
* console.log("Flow completed:", result.result);
|
|
751
|
+
* } else {
|
|
752
|
+
* console.log("Flow paused at node:", result.nodeId);
|
|
753
|
+
* // Can resume later with: flow.resume({ jobId, executionState: result.executionState, ... })
|
|
754
|
+
* }
|
|
755
|
+
* ```
|
|
756
|
+
*/
|
|
757
|
+
type FlowExecutionResult<TOutput$1> = {
|
|
758
|
+
type: "completed";
|
|
759
|
+
result: TOutput$1;
|
|
760
|
+
} | {
|
|
761
|
+
type: "paused";
|
|
762
|
+
nodeId: string;
|
|
763
|
+
executionState: {
|
|
764
|
+
executionOrder: string[];
|
|
765
|
+
currentIndex: number;
|
|
766
|
+
inputs: Record<string, unknown>;
|
|
767
|
+
};
|
|
768
|
+
};
|
|
769
|
+
/**
|
|
770
|
+
* A Flow represents a directed acyclic graph (DAG) of processing nodes.
|
|
771
|
+
*
|
|
772
|
+
* Flows execute nodes in topological order, passing data between nodes through edges.
|
|
773
|
+
* They support conditional execution, retry logic, pausable nodes, and event emission.
|
|
774
|
+
*
|
|
775
|
+
* @template TFlowInputSchema - Zod schema defining the shape of input data
|
|
776
|
+
* @template TFlowOutputSchema - Zod schema defining the shape of output data
|
|
777
|
+
* @template TRequirements - Effect requirements (services/contexts) needed by nodes
|
|
778
|
+
*
|
|
779
|
+
* @property id - Unique flow identifier
|
|
780
|
+
* @property name - Human-readable flow name
|
|
781
|
+
* @property nodes - Array of nodes in the flow
|
|
782
|
+
* @property edges - Connections between nodes
|
|
783
|
+
* @property inputSchema - Zod schema for validating flow inputs
|
|
784
|
+
* @property outputSchema - Zod schema for validating flow outputs
|
|
785
|
+
* @property onEvent - Optional callback for flow execution events
|
|
786
|
+
* @property run - Executes the flow from the beginning
|
|
787
|
+
* @property resume - Resumes a paused flow execution
|
|
788
|
+
* @property validateTypes - Validates node type compatibility
|
|
789
|
+
* @property validateInputs - Validates input data against schema
|
|
790
|
+
* @property validateOutputs - Validates output data against schema
|
|
791
|
+
*
|
|
792
|
+
* @remarks
|
|
793
|
+
* Flows are created using {@link createFlowWithSchema}. The Effect-based design
|
|
794
|
+
* allows for composable error handling, resource management, and dependency injection.
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* ```typescript
|
|
798
|
+
* const flow = yield* createFlowWithSchema({
|
|
799
|
+
* flowId: "image-pipeline",
|
|
800
|
+
* name: "Image Processing Pipeline",
|
|
801
|
+
* nodes: [inputNode, resizeNode, optimizeNode, storageNode],
|
|
802
|
+
* edges: [
|
|
803
|
+
* { source: "input", target: "resize" },
|
|
804
|
+
* { source: "resize", target: "optimize" },
|
|
805
|
+
* { source: "optimize", target: "storage" }
|
|
806
|
+
* ],
|
|
807
|
+
* inputSchema: z.object({ file: z.instanceof(File) }),
|
|
808
|
+
* outputSchema: uploadFileSchema
|
|
809
|
+
* });
|
|
810
|
+
*
|
|
811
|
+
* const result = yield* flow.run({
|
|
812
|
+
* inputs: { input: { file: myFile } },
|
|
813
|
+
* storageId: "storage-1",
|
|
814
|
+
* jobId: "job-123"
|
|
815
|
+
* });
|
|
816
|
+
* ```
|
|
817
|
+
*/
|
|
818
|
+
type Flow<TFlowInputSchema extends z.ZodSchema<any>, TFlowOutputSchema extends z.ZodSchema<any>, TRequirements$1> = {
|
|
819
|
+
id: string;
|
|
820
|
+
name: string;
|
|
821
|
+
nodes: FlowNode<any, any, UploadistaError$1>[];
|
|
822
|
+
edges: FlowEdge[];
|
|
823
|
+
inputSchema: TFlowInputSchema;
|
|
824
|
+
outputSchema: TFlowOutputSchema;
|
|
825
|
+
onEvent?: FlowConfig<TFlowInputSchema, TFlowOutputSchema, TRequirements$1>["onEvent"];
|
|
826
|
+
run: (args: {
|
|
827
|
+
inputs?: Record<string, z.infer<TFlowInputSchema>>;
|
|
828
|
+
storageId: string;
|
|
829
|
+
jobId: string;
|
|
830
|
+
clientId: string | null;
|
|
831
|
+
}) => Effect.Effect<FlowExecutionResult<Record<string, z.infer<TFlowOutputSchema>>>, UploadistaError$1, TRequirements$1>;
|
|
832
|
+
resume: (args: {
|
|
833
|
+
jobId: string;
|
|
834
|
+
storageId: string;
|
|
835
|
+
nodeResults: Record<string, unknown>;
|
|
836
|
+
executionState: {
|
|
837
|
+
executionOrder: string[];
|
|
838
|
+
currentIndex: number;
|
|
839
|
+
inputs: Record<string, z.infer<TFlowInputSchema>>;
|
|
840
|
+
};
|
|
841
|
+
clientId: string | null;
|
|
842
|
+
}) => Effect.Effect<FlowExecutionResult<Record<string, z.infer<TFlowOutputSchema>>>, UploadistaError$1, TRequirements$1>;
|
|
843
|
+
validateTypes: () => {
|
|
844
|
+
isValid: boolean;
|
|
845
|
+
errors: string[];
|
|
846
|
+
};
|
|
847
|
+
validateInputs: (inputs: unknown) => {
|
|
848
|
+
isValid: boolean;
|
|
849
|
+
errors: string[];
|
|
850
|
+
};
|
|
851
|
+
validateOutputs: (outputs: unknown) => {
|
|
852
|
+
isValid: boolean;
|
|
853
|
+
errors: string[];
|
|
854
|
+
};
|
|
855
|
+
};
|
|
856
|
+
/**
|
|
857
|
+
* Creates a new Flow with Zod schema-based type validation.
|
|
858
|
+
*
|
|
859
|
+
* This is the primary way to create flows in Uploadista. It constructs a Flow
|
|
860
|
+
* instance that validates inputs/outputs, executes nodes in topological order,
|
|
861
|
+
* handles errors with retries, and emits events during execution.
|
|
862
|
+
*
|
|
863
|
+
* @template TFlowInputSchema - Zod schema for flow input validation
|
|
864
|
+
* @template TFlowOutputSchema - Zod schema for flow output validation
|
|
865
|
+
* @template TRequirements - Effect requirements/services needed by the flow
|
|
866
|
+
* @template TNodeError - Union of possible errors from nodes
|
|
867
|
+
* @template TNodeRequirements - Union of requirements from nodes
|
|
868
|
+
*
|
|
869
|
+
* @param config - Flow configuration object
|
|
870
|
+
* @param config.flowId - Unique identifier for the flow
|
|
871
|
+
* @param config.name - Human-readable flow name
|
|
872
|
+
* @param config.nodes - Array of nodes (can be plain nodes or Effects resolving to nodes)
|
|
873
|
+
* @param config.edges - Array of edges connecting nodes
|
|
874
|
+
* @param config.inputSchema - Zod schema for validating inputs
|
|
875
|
+
* @param config.outputSchema - Zod schema for validating outputs
|
|
876
|
+
* @param config.typeChecker - Optional custom type compatibility checker
|
|
877
|
+
* @param config.onEvent - Optional event callback for monitoring execution
|
|
878
|
+
*
|
|
879
|
+
* @returns Effect that resolves to a Flow instance
|
|
880
|
+
*
|
|
881
|
+
* @throws {UploadistaError} FLOW_CYCLE_ERROR if the graph contains cycles
|
|
882
|
+
* @throws {UploadistaError} FLOW_NODE_NOT_FOUND if a node is referenced but missing
|
|
883
|
+
* @throws {UploadistaError} FLOW_NODE_ERROR if node execution fails
|
|
884
|
+
* @throws {UploadistaError} FLOW_OUTPUT_VALIDATION_ERROR if outputs don't match schema
|
|
885
|
+
*
|
|
886
|
+
* @remarks
|
|
887
|
+
* - Nodes can be provided as plain objects or as Effects that resolve to nodes
|
|
888
|
+
* - The flow performs topological sorting to determine execution order
|
|
889
|
+
* - Conditional nodes are evaluated before execution
|
|
890
|
+
* - Nodes can specify retry configuration with exponential backoff
|
|
891
|
+
* - Pausable nodes can halt execution and resume later
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* ```typescript
|
|
895
|
+
* const flow = yield* createFlowWithSchema({
|
|
896
|
+
* flowId: "image-upload",
|
|
897
|
+
* name: "Image Upload with Processing",
|
|
898
|
+
* nodes: [
|
|
899
|
+
* inputNode,
|
|
900
|
+
* yield* createResizeNode({ width: 1920, height: 1080 }),
|
|
901
|
+
* optimizeNode,
|
|
902
|
+
* storageNode
|
|
903
|
+
* ],
|
|
904
|
+
* edges: [
|
|
905
|
+
* { source: "input", target: "resize" },
|
|
906
|
+
* { source: "resize", target: "optimize" },
|
|
907
|
+
* { source: "optimize", target: "storage" }
|
|
908
|
+
* ],
|
|
909
|
+
* inputSchema: z.object({
|
|
910
|
+
* file: z.instanceof(File),
|
|
911
|
+
* metadata: z.record(z.string(), z.any()).optional()
|
|
912
|
+
* }),
|
|
913
|
+
* outputSchema: uploadFileSchema,
|
|
914
|
+
* onEvent: (event) => Effect.gen(function* () {
|
|
915
|
+
* console.log("Flow event:", event);
|
|
916
|
+
* return { eventId: event.jobId };
|
|
917
|
+
* })
|
|
918
|
+
* });
|
|
919
|
+
* ```
|
|
920
|
+
*
|
|
921
|
+
* @see {@link Flow} for the returned flow type
|
|
922
|
+
* @see {@link FlowConfig} for configuration options
|
|
923
|
+
*/
|
|
924
|
+
declare function createFlowWithSchema<TFlowInputSchema extends z.ZodSchema<any>, TFlowOutputSchema extends z.ZodSchema<any>, TRequirements$1 = never, TNodeError = never, TNodeRequirements = never>(config: FlowConfig<TFlowInputSchema, TFlowOutputSchema, TNodeError, TNodeRequirements>): Effect.Effect<Flow<TFlowInputSchema, TFlowOutputSchema, TRequirements$1>, TNodeError, TNodeRequirements>;
|
|
925
|
+
//#endregion
|
|
926
|
+
//#region src/types/upload-file.d.ts
|
|
927
|
+
/**
|
|
928
|
+
* Zod schema for validating UploadFile objects.
|
|
929
|
+
*
|
|
930
|
+
* This schema defines the structure and validation rules for upload file metadata.
|
|
931
|
+
* Use this schema to parse and validate UploadFile data from external sources.
|
|
932
|
+
*
|
|
933
|
+
* @see {@link UploadFile} for the TypeScript type
|
|
934
|
+
*/
|
|
935
|
+
declare const uploadFileSchema: z.ZodObject<{
|
|
936
|
+
id: z.ZodString;
|
|
937
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
938
|
+
offset: z.ZodNumber;
|
|
939
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
940
|
+
creationDate: z.ZodOptional<z.ZodString>;
|
|
941
|
+
url: z.ZodOptional<z.ZodString>;
|
|
942
|
+
sizeIsDeferred: z.ZodOptional<z.ZodBoolean>;
|
|
943
|
+
checksum: z.ZodOptional<z.ZodString>;
|
|
944
|
+
checksumAlgorithm: z.ZodOptional<z.ZodString>;
|
|
945
|
+
storage: z.ZodObject<{
|
|
946
|
+
id: z.ZodString;
|
|
947
|
+
type: z.ZodString;
|
|
948
|
+
path: z.ZodOptional<z.ZodString>;
|
|
949
|
+
uploadId: z.ZodOptional<z.ZodString>;
|
|
950
|
+
bucket: z.ZodOptional<z.ZodString>;
|
|
951
|
+
}, z.core.$strip>;
|
|
952
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
953
|
+
flowId: z.ZodString;
|
|
954
|
+
nodeId: z.ZodString;
|
|
955
|
+
jobId: z.ZodString;
|
|
956
|
+
}, z.core.$strip>>;
|
|
957
|
+
}, z.core.$strip>;
|
|
958
|
+
/**
|
|
959
|
+
* Represents an uploaded file with its metadata and storage information.
|
|
960
|
+
*
|
|
961
|
+
* This is the core data structure that tracks file uploads throughout their lifecycle.
|
|
962
|
+
* It contains all metadata needed to resume uploads, track progress, and locate files
|
|
963
|
+
* in storage backends.
|
|
964
|
+
*
|
|
965
|
+
* @property id - Unique identifier for this upload
|
|
966
|
+
* @property offset - Current byte offset (how many bytes have been uploaded)
|
|
967
|
+
* @property storage - Storage backend information
|
|
968
|
+
* @property storage.id - Storage backend identifier (e.g., "s3-production")
|
|
969
|
+
* @property storage.type - Storage backend type (e.g., "s3", "azure", "gcs")
|
|
970
|
+
* @property storage.path - Optional path prefix within the storage backend
|
|
971
|
+
* @property storage.uploadId - Optional backend-specific upload ID (e.g., S3 multipart upload ID)
|
|
972
|
+
* @property storage.bucket - Optional bucket or container name
|
|
973
|
+
* @property flow - Optional flow processing information (when file is part of a flow)
|
|
974
|
+
* @property flow.flowId - ID of the flow processing this file
|
|
975
|
+
* @property flow.nodeId - ID of the flow node that created this file
|
|
976
|
+
* @property flow.jobId - ID of the flow job execution
|
|
977
|
+
* @property size - Total file size in bytes (undefined if deferred)
|
|
978
|
+
* @property metadata - Custom key-value metadata attached to the file
|
|
979
|
+
* @property creationDate - ISO 8601 timestamp when upload was created
|
|
980
|
+
* @property url - Optional public URL to access the file
|
|
981
|
+
* @property sizeIsDeferred - True if file size is not known at upload start
|
|
982
|
+
* @property checksum - Optional file checksum/hash value
|
|
983
|
+
* @property checksumAlgorithm - Algorithm used for checksum (e.g., "md5", "sha256")
|
|
984
|
+
*
|
|
985
|
+
* @example
|
|
986
|
+
* ```typescript
|
|
987
|
+
* // Create an UploadFile for a new upload
|
|
988
|
+
* const uploadFile: UploadFile = {
|
|
989
|
+
* id: "upload_abc123",
|
|
990
|
+
* offset: 0,
|
|
991
|
+
* size: 1024000,
|
|
992
|
+
* storage: {
|
|
993
|
+
* id: "s3-production",
|
|
994
|
+
* type: "s3",
|
|
995
|
+
* bucket: "my-uploads",
|
|
996
|
+
* path: "files/"
|
|
997
|
+
* },
|
|
998
|
+
* metadata: {
|
|
999
|
+
* fileName: "image.jpg",
|
|
1000
|
+
* contentType: "image/jpeg",
|
|
1001
|
+
* userId: "user_123"
|
|
1002
|
+
* },
|
|
1003
|
+
* creationDate: new Date().toISOString(),
|
|
1004
|
+
* checksum: "5d41402abc4b2a76b9719d911017c592",
|
|
1005
|
+
* checksumAlgorithm: "md5"
|
|
1006
|
+
* };
|
|
1007
|
+
*
|
|
1008
|
+
* // UploadFile with flow processing
|
|
1009
|
+
* const flowFile: UploadFile = {
|
|
1010
|
+
* id: "upload_xyz789",
|
|
1011
|
+
* offset: 0,
|
|
1012
|
+
* size: 2048000,
|
|
1013
|
+
* storage: {
|
|
1014
|
+
* id: "s3-temp",
|
|
1015
|
+
* type: "s3",
|
|
1016
|
+
* bucket: "temp-processing"
|
|
1017
|
+
* },
|
|
1018
|
+
* flow: {
|
|
1019
|
+
* flowId: "flow_resize_optimize",
|
|
1020
|
+
* nodeId: "input_1",
|
|
1021
|
+
* jobId: "job_456"
|
|
1022
|
+
* }
|
|
1023
|
+
* };
|
|
1024
|
+
*
|
|
1025
|
+
* // Resume an interrupted upload
|
|
1026
|
+
* const resumingFile: UploadFile = {
|
|
1027
|
+
* id: "upload_resume",
|
|
1028
|
+
* offset: 524288, // Already uploaded 512KB
|
|
1029
|
+
* size: 1024000,
|
|
1030
|
+
* storage: {
|
|
1031
|
+
* id: "s3-production",
|
|
1032
|
+
* type: "s3",
|
|
1033
|
+
* uploadId: "multipart_xyz" // S3 multipart upload ID
|
|
1034
|
+
* }
|
|
1035
|
+
* };
|
|
1036
|
+
* ```
|
|
1037
|
+
*/
|
|
1038
|
+
type UploadFile = {
|
|
1039
|
+
id: string;
|
|
1040
|
+
offset: number;
|
|
1041
|
+
storage: {
|
|
1042
|
+
id: string;
|
|
1043
|
+
type: string;
|
|
1044
|
+
path?: string | undefined;
|
|
1045
|
+
uploadId?: string | undefined;
|
|
1046
|
+
bucket?: string | undefined;
|
|
1047
|
+
};
|
|
1048
|
+
flow?: {
|
|
1049
|
+
flowId: string;
|
|
1050
|
+
nodeId: string;
|
|
1051
|
+
jobId: string;
|
|
1052
|
+
};
|
|
1053
|
+
size?: number | undefined;
|
|
1054
|
+
metadata?: Record<string, string | number | boolean> | undefined;
|
|
1055
|
+
creationDate?: string | undefined;
|
|
1056
|
+
url?: string | undefined;
|
|
1057
|
+
sizeIsDeferred?: boolean | undefined;
|
|
1058
|
+
checksum?: string | undefined;
|
|
1059
|
+
checksumAlgorithm?: string | undefined;
|
|
1060
|
+
};
|
|
1061
|
+
//#endregion
|
|
1062
|
+
//#region src/types/kv-store.d.ts
|
|
1063
|
+
/**
|
|
1064
|
+
* Base key-value store interface for raw string storage.
|
|
1065
|
+
*
|
|
1066
|
+
* This is the low-level interface that storage adapters implement.
|
|
1067
|
+
* It stores raw string values without type safety or serialization.
|
|
1068
|
+
*
|
|
1069
|
+
* @property get - Retrieves a value by key, returns null if not found
|
|
1070
|
+
* @property set - Stores a value with the given key
|
|
1071
|
+
* @property delete - Removes a value by key
|
|
1072
|
+
* @property list - Optional operation to list all keys with a given prefix
|
|
1073
|
+
*
|
|
1074
|
+
* @example
|
|
1075
|
+
* ```typescript
|
|
1076
|
+
* // Implement a BaseKvStore with Redis
|
|
1077
|
+
* const redisKvStore: BaseKvStore = {
|
|
1078
|
+
* get: (key) => Effect.tryPromise({
|
|
1079
|
+
* try: () => redis.get(key),
|
|
1080
|
+
* catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
|
|
1081
|
+
* }),
|
|
1082
|
+
*
|
|
1083
|
+
* set: (key, value) => Effect.tryPromise({
|
|
1084
|
+
* try: () => redis.set(key, value),
|
|
1085
|
+
* catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
|
|
1086
|
+
* }),
|
|
1087
|
+
*
|
|
1088
|
+
* delete: (key) => Effect.tryPromise({
|
|
1089
|
+
* try: () => redis.del(key),
|
|
1090
|
+
* catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
|
|
1091
|
+
* }),
|
|
1092
|
+
*
|
|
1093
|
+
* list: (prefix) => Effect.tryPromise({
|
|
1094
|
+
* try: () => redis.keys(`${prefix}*`),
|
|
1095
|
+
* catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
|
|
1096
|
+
* })
|
|
1097
|
+
* };
|
|
1098
|
+
* ```
|
|
1099
|
+
*/
|
|
1100
|
+
interface BaseKvStore {
|
|
1101
|
+
readonly get: (key: string) => Effect.Effect<string | null, UploadistaError$1>;
|
|
1102
|
+
readonly set: (key: string, value: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1103
|
+
readonly delete: (key: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1104
|
+
readonly list?: (keyPrefix: string) => Effect.Effect<Array<string>, UploadistaError$1>;
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Type-safe key-value store interface with automatic serialization.
|
|
1108
|
+
*
|
|
1109
|
+
* This wraps a BaseKvStore and handles JSON serialization/deserialization
|
|
1110
|
+
* for a specific data type, providing type safety and eliminating the need
|
|
1111
|
+
* for manual JSON.stringify/parse calls.
|
|
1112
|
+
*
|
|
1113
|
+
* @template TData - The type of data stored in this KV store
|
|
1114
|
+
*
|
|
1115
|
+
* @property get - Retrieves and deserializes a value, fails if not found
|
|
1116
|
+
* @property set - Serializes and stores a value
|
|
1117
|
+
* @property delete - Removes a value by key
|
|
1118
|
+
* @property list - Optional operation to list all keys (without prefix)
|
|
1119
|
+
*
|
|
1120
|
+
* @example
|
|
1121
|
+
* ```typescript
|
|
1122
|
+
* // Use a typed KV store
|
|
1123
|
+
* const uploadStore: KvStore<UploadFile> = new TypedKvStore(
|
|
1124
|
+
* baseStore,
|
|
1125
|
+
* "uploads:",
|
|
1126
|
+
* jsonSerializer.serialize,
|
|
1127
|
+
* jsonSerializer.deserialize
|
|
1128
|
+
* );
|
|
1129
|
+
*
|
|
1130
|
+
* // Store and retrieve typed data
|
|
1131
|
+
* const program = Effect.gen(function* () {
|
|
1132
|
+
* const file: UploadFile = {
|
|
1133
|
+
* id: "file123",
|
|
1134
|
+
* offset: 0,
|
|
1135
|
+
* storage: { id: "s3", type: "s3" }
|
|
1136
|
+
* };
|
|
1137
|
+
*
|
|
1138
|
+
* // Automatic serialization
|
|
1139
|
+
* yield* uploadStore.set("file123", file);
|
|
1140
|
+
*
|
|
1141
|
+
* // Automatic deserialization with type safety
|
|
1142
|
+
* const retrieved = yield* uploadStore.get("file123");
|
|
1143
|
+
* console.log(retrieved.offset); // TypeScript knows this is a number
|
|
1144
|
+
* });
|
|
1145
|
+
* ```
|
|
1146
|
+
*/
|
|
1147
|
+
type KvStore<TData> = {
|
|
1148
|
+
readonly get: (key: string) => Effect.Effect<TData, UploadistaError$1>;
|
|
1149
|
+
readonly set: (key: string, value: TData) => Effect.Effect<void, UploadistaError$1>;
|
|
1150
|
+
readonly delete: (key: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1151
|
+
readonly list?: () => Effect.Effect<Array<string>, UploadistaError$1>;
|
|
1152
|
+
};
|
|
1153
|
+
/**
|
|
1154
|
+
* Typed wrapper class that adds serialization to a BaseKvStore.
|
|
1155
|
+
*
|
|
1156
|
+
* This class implements the KvStore interface by wrapping a BaseKvStore
|
|
1157
|
+
* and handling serialization/deserialization for a specific type. It also
|
|
1158
|
+
* adds a key prefix to isolate different data types in the same store.
|
|
1159
|
+
*
|
|
1160
|
+
* @template TData - The type of data to store
|
|
1161
|
+
*
|
|
1162
|
+
* @example
|
|
1163
|
+
* ```typescript
|
|
1164
|
+
* // Create a typed store for UploadFile
|
|
1165
|
+
* const uploadFileStore = new TypedKvStore<UploadFile>(
|
|
1166
|
+
* baseKvStore,
|
|
1167
|
+
* "uploadista:upload-file:", // All keys will be prefixed
|
|
1168
|
+
* (data) => JSON.stringify(data),
|
|
1169
|
+
* (str) => JSON.parse(str) as UploadFile
|
|
1170
|
+
* );
|
|
1171
|
+
*
|
|
1172
|
+
* // Use the store
|
|
1173
|
+
* const effect = Effect.gen(function* () {
|
|
1174
|
+
* const file: UploadFile = { ... };
|
|
1175
|
+
* yield* uploadFileStore.set("abc123", file);
|
|
1176
|
+
* // Internally stores at key "uploadista:upload-file:abc123"
|
|
1177
|
+
*
|
|
1178
|
+
* const retrieved = yield* uploadFileStore.get("abc123");
|
|
1179
|
+
* return retrieved;
|
|
1180
|
+
* });
|
|
1181
|
+
*
|
|
1182
|
+
* // Custom serialization for binary data
|
|
1183
|
+
* const binaryStore = new TypedKvStore<Uint8Array>(
|
|
1184
|
+
* baseKvStore,
|
|
1185
|
+
* "binary:",
|
|
1186
|
+
* (data) => btoa(String.fromCharCode(...data)), // Base64 encode
|
|
1187
|
+
* (str) => Uint8Array.from(atob(str), c => c.charCodeAt(0)) // Base64 decode
|
|
1188
|
+
* );
|
|
1189
|
+
* ```
|
|
1190
|
+
*/
|
|
1191
|
+
declare class TypedKvStore<TData> implements KvStore<TData> {
|
|
1192
|
+
private baseStore;
|
|
1193
|
+
private keyPrefix;
|
|
1194
|
+
private serialize;
|
|
1195
|
+
private deserialize;
|
|
1196
|
+
constructor(baseStore: BaseKvStore, keyPrefix: string, serialize: (data: TData) => string, deserialize: (str: string) => TData);
|
|
1197
|
+
get: (key: string) => Effect.Effect<TData, UploadistaError$1>;
|
|
1198
|
+
set: (key: string, value: TData) => Effect.Effect<void, UploadistaError$1>;
|
|
1199
|
+
delete: (key: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1200
|
+
list: () => Effect.Effect<Array<string>, UploadistaError$1>;
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Default JSON serialization helpers.
|
|
1204
|
+
*
|
|
1205
|
+
* These functions provide standard JSON serialization for use with TypedKvStore.
|
|
1206
|
+
* They work with any JSON-serializable type.
|
|
1207
|
+
*
|
|
1208
|
+
* @example
|
|
1209
|
+
* ```typescript
|
|
1210
|
+
* const store = new TypedKvStore<MyType>(
|
|
1211
|
+
* baseStore,
|
|
1212
|
+
* "mydata:",
|
|
1213
|
+
* jsonSerializer.serialize,
|
|
1214
|
+
* jsonSerializer.deserialize
|
|
1215
|
+
* );
|
|
1216
|
+
* ```
|
|
1217
|
+
*/
|
|
1218
|
+
declare const jsonSerializer: {
|
|
1219
|
+
serialize: <T>(data: T) => string;
|
|
1220
|
+
deserialize: <T>(str: string) => T;
|
|
1221
|
+
};
|
|
1222
|
+
declare const BaseKvStoreService_base: Context.TagClass<BaseKvStoreService, "BaseKvStore", BaseKvStore>;
|
|
1223
|
+
/**
|
|
1224
|
+
* Effect-TS context tag for the base untyped KV store.
|
|
1225
|
+
*
|
|
1226
|
+
* This is the low-level store that storage adapter implementations provide.
|
|
1227
|
+
* Most application code should use typed stores like UploadFileKVStore instead.
|
|
1228
|
+
*
|
|
1229
|
+
* @example
|
|
1230
|
+
* ```typescript
|
|
1231
|
+
* // Provide a base store implementation
|
|
1232
|
+
* const baseStoreLayer = Layer.succeed(BaseKvStoreService, redisKvStore);
|
|
1233
|
+
*
|
|
1234
|
+
* // Use in an Effect
|
|
1235
|
+
* const effect = Effect.gen(function* () {
|
|
1236
|
+
* const baseStore = yield* BaseKvStoreService;
|
|
1237
|
+
* yield* baseStore.set("raw-key", "raw-value");
|
|
1238
|
+
* });
|
|
1239
|
+
* ```
|
|
1240
|
+
*/
|
|
1241
|
+
declare class BaseKvStoreService extends BaseKvStoreService_base {}
|
|
1242
|
+
declare const UploadFileKVStore_base: Context.TagClass<UploadFileKVStore, "UploadFileKVStore", KvStore<UploadFile>>;
|
|
1243
|
+
/**
|
|
1244
|
+
* Effect-TS context tag for the UploadFile typed KV store.
|
|
1245
|
+
*
|
|
1246
|
+
* This provides type-safe storage for UploadFile metadata. It's the primary
|
|
1247
|
+
* way to store and retrieve upload metadata in the system.
|
|
1248
|
+
*
|
|
1249
|
+
* @example
|
|
1250
|
+
* ```typescript
|
|
1251
|
+
* const uploadEffect = Effect.gen(function* () {
|
|
1252
|
+
* const kvStore = yield* UploadFileKVStore;
|
|
1253
|
+
*
|
|
1254
|
+
* // Store upload metadata
|
|
1255
|
+
* const file: UploadFile = {
|
|
1256
|
+
* id: "upload123",
|
|
1257
|
+
* offset: 0,
|
|
1258
|
+
* storage: { id: "s3", type: "s3" }
|
|
1259
|
+
* };
|
|
1260
|
+
* yield* kvStore.set("upload123", file);
|
|
1261
|
+
*
|
|
1262
|
+
* // Retrieve with type safety
|
|
1263
|
+
* const retrieved = yield* kvStore.get("upload123");
|
|
1264
|
+
* return retrieved;
|
|
1265
|
+
* });
|
|
1266
|
+
* ```
|
|
1267
|
+
*/
|
|
1268
|
+
declare class UploadFileKVStore extends UploadFileKVStore_base {}
|
|
1269
|
+
/**
|
|
1270
|
+
* Effect Layer that creates the UploadFileKVStore from a BaseKvStore.
|
|
1271
|
+
*
|
|
1272
|
+
* This layer automatically wires up JSON serialization for UploadFile objects
|
|
1273
|
+
* with the "uploadista:upload-file:" key prefix.
|
|
1274
|
+
*
|
|
1275
|
+
* @example
|
|
1276
|
+
* ```typescript
|
|
1277
|
+
* const program = Effect.gen(function* () {
|
|
1278
|
+
* const kvStore = yield* UploadFileKVStore;
|
|
1279
|
+
* // Use the store...
|
|
1280
|
+
* }).pipe(
|
|
1281
|
+
* Effect.provide(uploadFileKvStore),
|
|
1282
|
+
* Effect.provide(baseStoreLayer)
|
|
1283
|
+
* );
|
|
1284
|
+
* ```
|
|
1285
|
+
*/
|
|
1286
|
+
declare const uploadFileKvStore: Layer.Layer<UploadFileKVStore, never, BaseKvStoreService>;
|
|
1287
|
+
declare const FlowJobKVStore_base: Context.TagClass<FlowJobKVStore, "FlowJobKVStore", KvStore<FlowJob>>;
|
|
1288
|
+
/**
|
|
1289
|
+
* Effect-TS context tag for the FlowJob typed KV store.
|
|
1290
|
+
*
|
|
1291
|
+
* This provides type-safe storage for FlowJob metadata, tracking the
|
|
1292
|
+
* execution state of flow processing jobs.
|
|
1293
|
+
*
|
|
1294
|
+
* @example
|
|
1295
|
+
* ```typescript
|
|
1296
|
+
* const flowEffect = Effect.gen(function* () {
|
|
1297
|
+
* const jobStore = yield* FlowJobKVStore;
|
|
1298
|
+
*
|
|
1299
|
+
* // Store job state
|
|
1300
|
+
* const job: FlowJob = {
|
|
1301
|
+
* id: "job123",
|
|
1302
|
+
* flowId: "flow_resize",
|
|
1303
|
+
* status: "running",
|
|
1304
|
+
* tasks: [],
|
|
1305
|
+
* createdAt: new Date(),
|
|
1306
|
+
* updatedAt: new Date()
|
|
1307
|
+
* };
|
|
1308
|
+
* yield* jobStore.set("job123", job);
|
|
1309
|
+
*
|
|
1310
|
+
* // Retrieve and check status
|
|
1311
|
+
* const retrieved = yield* jobStore.get("job123");
|
|
1312
|
+
* return retrieved.status;
|
|
1313
|
+
* });
|
|
1314
|
+
* ```
|
|
1315
|
+
*/
|
|
1316
|
+
declare class FlowJobKVStore extends FlowJobKVStore_base {}
|
|
1317
|
+
/**
|
|
1318
|
+
* Effect Layer that creates the FlowJobKVStore from a BaseKvStore.
|
|
1319
|
+
*
|
|
1320
|
+
* This layer automatically wires up JSON serialization for FlowJob objects
|
|
1321
|
+
* with the "uploadista:flow-job:" key prefix.
|
|
1322
|
+
*
|
|
1323
|
+
* @example
|
|
1324
|
+
* ```typescript
|
|
1325
|
+
* const program = Effect.gen(function* () {
|
|
1326
|
+
* const jobStore = yield* FlowJobKVStore;
|
|
1327
|
+
* // Use the store...
|
|
1328
|
+
* }).pipe(
|
|
1329
|
+
* Effect.provide(flowJobKvStore),
|
|
1330
|
+
* Effect.provide(baseStoreLayer)
|
|
1331
|
+
* );
|
|
1332
|
+
* ```
|
|
1333
|
+
*/
|
|
1334
|
+
declare const flowJobKvStore: Layer.Layer<FlowJobKVStore, never, BaseKvStoreService>;
|
|
1335
|
+
//#endregion
|
|
1336
|
+
//#region src/types/data-store.d.ts
|
|
1337
|
+
/**
|
|
1338
|
+
* Options for writing data to a DataStore.
|
|
1339
|
+
*
|
|
1340
|
+
* @property file_id - Unique identifier for the file being written
|
|
1341
|
+
* @property stream - Stream of byte chunks to write to storage
|
|
1342
|
+
* @property offset - Byte offset where writing should begin (for resumable uploads)
|
|
1343
|
+
*/
|
|
1344
|
+
type DataStoreWriteOptions = {
|
|
1345
|
+
file_id: string;
|
|
1346
|
+
stream: Stream.Stream<Uint8Array, UploadistaError$1>;
|
|
1347
|
+
offset: number;
|
|
1348
|
+
};
|
|
1349
|
+
/**
|
|
1350
|
+
* Upload strategy type indicating how chunks are uploaded.
|
|
1351
|
+
*
|
|
1352
|
+
* - `single`: Upload file in a single request (traditional upload)
|
|
1353
|
+
* - `parallel`: Upload file chunks in parallel (for large files)
|
|
1354
|
+
*/
|
|
1355
|
+
type UploadStrategy = "single" | "parallel";
|
|
1356
|
+
/**
|
|
1357
|
+
* Capabilities and constraints of a DataStore implementation.
|
|
1358
|
+
*
|
|
1359
|
+
* This type describes what features a storage backend supports and what
|
|
1360
|
+
* limitations it has. Use this to determine the optimal upload strategy
|
|
1361
|
+
* and validate client requests.
|
|
1362
|
+
*
|
|
1363
|
+
* @property supportsParallelUploads - Can upload chunks in parallel (e.g., S3 multipart)
|
|
1364
|
+
* @property supportsConcatenation - Can concatenate multiple uploads into one file
|
|
1365
|
+
* @property supportsDeferredLength - Can start upload without knowing final size
|
|
1366
|
+
* @property supportsResumableUploads - Can resume interrupted uploads from last offset
|
|
1367
|
+
* @property supportsTransactionalUploads - Guarantees atomic upload success/failure
|
|
1368
|
+
* @property maxConcurrentUploads - Maximum parallel upload parts (if parallel supported)
|
|
1369
|
+
* @property minChunkSize - Minimum size in bytes for each chunk (except last)
|
|
1370
|
+
* @property maxChunkSize - Maximum size in bytes for each chunk
|
|
1371
|
+
* @property maxParts - Maximum number of parts in a multipart upload
|
|
1372
|
+
* @property optimalChunkSize - Recommended chunk size for best performance
|
|
1373
|
+
* @property requiresOrderedChunks - Must receive chunks in sequential order
|
|
1374
|
+
* @property requiresMimeTypeValidation - Validates file MIME type matches declaration
|
|
1375
|
+
* @property maxValidationSize - Maximum file size for MIME type validation
|
|
1376
|
+
*
|
|
1377
|
+
* @example
|
|
1378
|
+
* ```typescript
|
|
1379
|
+
* const capabilities = dataStore.getCapabilities();
|
|
1380
|
+
*
|
|
1381
|
+
* if (capabilities.supportsParallelUploads && fileSize > 10_000_000) {
|
|
1382
|
+
* // Use parallel upload for large files
|
|
1383
|
+
* const chunkSize = capabilities.optimalChunkSize || 5_242_880; // 5MB default
|
|
1384
|
+
* uploadInParallel(file, chunkSize);
|
|
1385
|
+
* } else {
|
|
1386
|
+
* // Use single upload
|
|
1387
|
+
* uploadAsSingleChunk(file);
|
|
1388
|
+
* }
|
|
1389
|
+
* ```
|
|
1390
|
+
*/
|
|
1391
|
+
type DataStoreCapabilities = {
|
|
1392
|
+
supportsParallelUploads: boolean;
|
|
1393
|
+
supportsConcatenation: boolean;
|
|
1394
|
+
supportsDeferredLength: boolean;
|
|
1395
|
+
supportsResumableUploads: boolean;
|
|
1396
|
+
supportsTransactionalUploads: boolean;
|
|
1397
|
+
maxConcurrentUploads?: number;
|
|
1398
|
+
minChunkSize?: number;
|
|
1399
|
+
maxChunkSize?: number;
|
|
1400
|
+
maxParts?: number;
|
|
1401
|
+
optimalChunkSize?: number;
|
|
1402
|
+
requiresOrderedChunks: boolean;
|
|
1403
|
+
requiresMimeTypeValidation?: boolean;
|
|
1404
|
+
maxValidationSize?: number;
|
|
1405
|
+
};
|
|
1406
|
+
/**
|
|
1407
|
+
* Core interface for all storage backend implementations.
|
|
1408
|
+
*
|
|
1409
|
+
* DataStore abstracts file storage operations across different backends
|
|
1410
|
+
* (S3, Azure Blob, GCS, local filesystem, etc.). All storage adapters
|
|
1411
|
+
* must implement this interface.
|
|
1412
|
+
*
|
|
1413
|
+
* @template TData - The data type stored (typically UploadFile)
|
|
1414
|
+
*
|
|
1415
|
+
* @property bucket - Optional storage bucket or container name
|
|
1416
|
+
* @property path - Optional base path prefix for all stored files
|
|
1417
|
+
* @property create - Creates a new file record in storage
|
|
1418
|
+
* @property remove - Deletes a file from storage
|
|
1419
|
+
* @property read - Reads complete file contents as bytes
|
|
1420
|
+
* @property write - Writes data stream to storage at specified offset
|
|
1421
|
+
* @property deleteExpired - Optional cleanup of expired files
|
|
1422
|
+
* @property getCapabilities - Returns storage backend capabilities
|
|
1423
|
+
* @property validateUploadStrategy - Validates if strategy is supported
|
|
1424
|
+
*
|
|
1425
|
+
* @example
|
|
1426
|
+
* ```typescript
|
|
1427
|
+
* // Implement a custom DataStore
|
|
1428
|
+
* const myDataStore: DataStore<UploadFile> = {
|
|
1429
|
+
* bucket: "my-uploads",
|
|
1430
|
+
* path: "files/",
|
|
1431
|
+
*
|
|
1432
|
+
* create: (file) => Effect.gen(function* () {
|
|
1433
|
+
* // Store file metadata
|
|
1434
|
+
* yield* saveMetadata(file);
|
|
1435
|
+
* return file;
|
|
1436
|
+
* }),
|
|
1437
|
+
*
|
|
1438
|
+
* write: ({ file_id, stream, offset }, { onProgress }) => Effect.gen(function* () {
|
|
1439
|
+
* // Write chunks to storage
|
|
1440
|
+
* let bytesWritten = offset;
|
|
1441
|
+
* yield* Stream.runForEach(stream, (chunk) => Effect.sync(() => {
|
|
1442
|
+
* writeChunk(file_id, chunk, bytesWritten);
|
|
1443
|
+
* bytesWritten += chunk.byteLength;
|
|
1444
|
+
* onProgress?.(chunk.byteLength);
|
|
1445
|
+
* }));
|
|
1446
|
+
* return bytesWritten;
|
|
1447
|
+
* }),
|
|
1448
|
+
*
|
|
1449
|
+
* read: (file_id) => Effect.gen(function* () {
|
|
1450
|
+
* // Read complete file
|
|
1451
|
+
* const data = yield* readFromStorage(file_id);
|
|
1452
|
+
* return data;
|
|
1453
|
+
* }),
|
|
1454
|
+
*
|
|
1455
|
+
* remove: (file_id) => Effect.gen(function* () {
|
|
1456
|
+
* yield* deleteFromStorage(file_id);
|
|
1457
|
+
* }),
|
|
1458
|
+
*
|
|
1459
|
+
* getCapabilities: () => ({
|
|
1460
|
+
* supportsParallelUploads: true,
|
|
1461
|
+
* supportsConcatenation: false,
|
|
1462
|
+
* supportsDeferredLength: true,
|
|
1463
|
+
* supportsResumableUploads: true,
|
|
1464
|
+
* supportsTransactionalUploads: false,
|
|
1465
|
+
* maxConcurrentUploads: 10,
|
|
1466
|
+
* optimalChunkSize: 5_242_880, // 5MB
|
|
1467
|
+
* requiresOrderedChunks: false,
|
|
1468
|
+
* }),
|
|
1469
|
+
*
|
|
1470
|
+
* validateUploadStrategy: (strategy) =>
|
|
1471
|
+
* Effect.succeed(strategy === "parallel" || strategy === "single"),
|
|
1472
|
+
* };
|
|
1473
|
+
* ```
|
|
1474
|
+
*/
|
|
1475
|
+
type DataStore<TData = unknown> = {
|
|
1476
|
+
readonly bucket?: string;
|
|
1477
|
+
readonly path?: string;
|
|
1478
|
+
readonly create: (file: TData) => Effect.Effect<TData, UploadistaError$1>;
|
|
1479
|
+
readonly remove: (file_id: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1480
|
+
readonly read: (file_id: string) => Effect.Effect<Uint8Array, UploadistaError$1>;
|
|
1481
|
+
readonly write: (options: DataStoreWriteOptions, dependencies: {
|
|
1482
|
+
onProgress?: (chunkSize: number) => void;
|
|
1483
|
+
}) => Effect.Effect<number, UploadistaError$1>;
|
|
1484
|
+
readonly deleteExpired?: Effect.Effect<number, UploadistaError$1>;
|
|
1485
|
+
readonly getCapabilities: () => DataStoreCapabilities;
|
|
1486
|
+
readonly validateUploadStrategy: (strategy: UploadStrategy) => Effect.Effect<boolean, never>;
|
|
1487
|
+
};
|
|
1488
|
+
declare const UploadFileDataStore_base: Context.TagClass<UploadFileDataStore, "UploadFileDataStore", DataStore<UploadFile>>;
|
|
1489
|
+
/**
|
|
1490
|
+
* Effect-TS context tag for UploadFile DataStore.
|
|
1491
|
+
*
|
|
1492
|
+
* Use this tag to access the primary DataStore in an Effect context.
|
|
1493
|
+
* This is the standard storage backend for uploaded files.
|
|
1494
|
+
*
|
|
1495
|
+
* @example
|
|
1496
|
+
* ```typescript
|
|
1497
|
+
* const uploadEffect = Effect.gen(function* () {
|
|
1498
|
+
* const dataStore = yield* UploadFileDataStore;
|
|
1499
|
+
* const file = yield* dataStore.create(uploadFile);
|
|
1500
|
+
* return file;
|
|
1501
|
+
* });
|
|
1502
|
+
* ```
|
|
1503
|
+
*/
|
|
1504
|
+
declare class UploadFileDataStore extends UploadFileDataStore_base {}
|
|
1505
|
+
declare const BufferedUploadFileDataStore_base: Context.TagClass<BufferedUploadFileDataStore, "BufferedUploadFileDataStore", DataStore<UploadFile>>;
|
|
1506
|
+
/**
|
|
1507
|
+
* Effect-TS context tag for buffered/temporary DataStore.
|
|
1508
|
+
*
|
|
1509
|
+
* This is an optional storage backend used for temporary or intermediate files
|
|
1510
|
+
* during flow processing. Not all implementations provide a buffered store.
|
|
1511
|
+
*
|
|
1512
|
+
* @example
|
|
1513
|
+
* ```typescript
|
|
1514
|
+
* const processEffect = Effect.gen(function* () {
|
|
1515
|
+
* const bufferedStore = yield* BufferedUploadFileDataStore;
|
|
1516
|
+
* // Store intermediate processing results
|
|
1517
|
+
* const tempFile = yield* bufferedStore.create(intermediateFile);
|
|
1518
|
+
* return tempFile;
|
|
1519
|
+
* });
|
|
1520
|
+
* ```
|
|
1521
|
+
*/
|
|
1522
|
+
declare class BufferedUploadFileDataStore extends BufferedUploadFileDataStore_base {}
|
|
1523
|
+
/**
|
|
1524
|
+
* Service interface for managing multiple DataStore instances.
|
|
1525
|
+
*
|
|
1526
|
+
* This allows routing files to different storage backends based on
|
|
1527
|
+
* storageId (e.g., different S3 buckets, Azure containers, or storage tiers).
|
|
1528
|
+
*
|
|
1529
|
+
* @property getDataStore - Retrieves the appropriate DataStore for a given storage ID
|
|
1530
|
+
* @property bufferedDataStore - Optional temporary storage for intermediate files
|
|
1531
|
+
*/
|
|
1532
|
+
type UploadFileDataStoresShape = {
|
|
1533
|
+
getDataStore: (storageId: string, clientId: string | null) => Effect.Effect<DataStore<UploadFile>, UploadistaError$1>;
|
|
1534
|
+
bufferedDataStore: Effect.Effect<DataStore<UploadFile> | undefined, UploadistaError$1>;
|
|
1535
|
+
};
|
|
1536
|
+
declare const UploadFileDataStores_base: Context.TagClass<UploadFileDataStores, "UploadFileDataStores", UploadFileDataStoresShape>;
|
|
1537
|
+
/**
|
|
1538
|
+
* Effect-TS context tag for the DataStore routing service.
|
|
1539
|
+
*
|
|
1540
|
+
* Provides access to multiple DataStore instances with routing logic.
|
|
1541
|
+
*
|
|
1542
|
+
* @example
|
|
1543
|
+
* ```typescript
|
|
1544
|
+
* const uploadEffect = Effect.gen(function* () {
|
|
1545
|
+
* const dataStores = yield* UploadFileDataStores;
|
|
1546
|
+
* // Route to specific storage based on storageId
|
|
1547
|
+
* const dataStore = yield* dataStores.getDataStore("s3-production", clientId);
|
|
1548
|
+
* const file = yield* dataStore.create(uploadFile);
|
|
1549
|
+
* return file;
|
|
1550
|
+
* });
|
|
1551
|
+
* ```
|
|
1552
|
+
*/
|
|
1553
|
+
declare class UploadFileDataStores extends UploadFileDataStores_base {}
|
|
1554
|
+
/**
|
|
1555
|
+
* Simplified DataStore configuration for easy setup.
|
|
1556
|
+
*
|
|
1557
|
+
* This type allows flexible configuration:
|
|
1558
|
+
* - Single DataStore instance
|
|
1559
|
+
* - Multiple named stores with routing
|
|
1560
|
+
* - Effect that resolves to a DataStore
|
|
1561
|
+
* - Pre-built Effect Layer
|
|
1562
|
+
*
|
|
1563
|
+
* @example
|
|
1564
|
+
* ```typescript
|
|
1565
|
+
* // Single store
|
|
1566
|
+
* const config: DataStoreConfig = s3DataStore;
|
|
1567
|
+
*
|
|
1568
|
+
* // Multiple stores with routing
|
|
1569
|
+
* const config: DataStoreConfig = {
|
|
1570
|
+
* stores: {
|
|
1571
|
+
* "s3-prod": s3ProdStore,
|
|
1572
|
+
* "s3-dev": s3DevStore,
|
|
1573
|
+
* "local": localFileStore,
|
|
1574
|
+
* },
|
|
1575
|
+
* default: "s3-prod"
|
|
1576
|
+
* };
|
|
1577
|
+
*
|
|
1578
|
+
* // Effect that creates a store
|
|
1579
|
+
* const config: DataStoreConfig = Effect.gen(function* () {
|
|
1580
|
+
* const kvStore = yield* UploadFileKVStore;
|
|
1581
|
+
* return createS3Store(kvStore);
|
|
1582
|
+
* });
|
|
1583
|
+
*
|
|
1584
|
+
* // Pre-built Layer
|
|
1585
|
+
* const config: DataStoreConfig = Layer.succeed(UploadFileDataStores, {...});
|
|
1586
|
+
* ```
|
|
1587
|
+
*/
|
|
1588
|
+
type DataStoreConfig = DataStore<UploadFile> | Effect.Effect<DataStore<UploadFile>, never, UploadFileKVStore> | {
|
|
1589
|
+
stores: Record<string, DataStore<UploadFile> | Effect.Effect<DataStore<UploadFile>, never, UploadFileKVStore>>;
|
|
1590
|
+
default?: string;
|
|
1591
|
+
} | Layer.Layer<UploadFileDataStores, never, UploadFileKVStore>;
|
|
1592
|
+
/**
|
|
1593
|
+
* Type guard to check if a value is a DataStore instance.
|
|
1594
|
+
*
|
|
1595
|
+
* @param config - The value to check
|
|
1596
|
+
* @returns True if the value is a DataStore
|
|
1597
|
+
*
|
|
1598
|
+
* @example
|
|
1599
|
+
* ```typescript
|
|
1600
|
+
* if (isDataStore(config)) {
|
|
1601
|
+
* const capabilities = config.getCapabilities();
|
|
1602
|
+
* }
|
|
1603
|
+
* ```
|
|
1604
|
+
*/
|
|
1605
|
+
declare const isDataStore: (config: DataStoreConfig) => config is DataStore<UploadFile>;
|
|
1606
|
+
/**
|
|
1607
|
+
* Creates an Effect Layer from simplified DataStoreConfig.
|
|
1608
|
+
*
|
|
1609
|
+
* This function converts any DataStoreConfig format into a proper Effect Layer
|
|
1610
|
+
* that can be provided to the UploadFileDataStores context tag.
|
|
1611
|
+
*
|
|
1612
|
+
* It handles:
|
|
1613
|
+
* - Single DataStore: Wraps in a Layer that always returns that store
|
|
1614
|
+
* - Multiple stores: Creates routing logic with optional default
|
|
1615
|
+
* - Effect<DataStore>: Executes the Effect and wraps the result
|
|
1616
|
+
* - Layer: Returns as-is
|
|
1617
|
+
*
|
|
1618
|
+
* @param config - The DataStore configuration
|
|
1619
|
+
* @returns A Layer that provides UploadFileDataStores service
|
|
1620
|
+
*
|
|
1621
|
+
* @example
|
|
1622
|
+
* ```typescript
|
|
1623
|
+
* // Create from single store
|
|
1624
|
+
* const layer = await createDataStoreLayer(s3DataStore);
|
|
1625
|
+
*
|
|
1626
|
+
* // Create from multiple stores
|
|
1627
|
+
* const layer = await createDataStoreLayer({
|
|
1628
|
+
* stores: {
|
|
1629
|
+
* "production": s3Store,
|
|
1630
|
+
* "development": localStore,
|
|
1631
|
+
* },
|
|
1632
|
+
* default: "development"
|
|
1633
|
+
* });
|
|
1634
|
+
*
|
|
1635
|
+
* // Use the layer
|
|
1636
|
+
* const program = Effect.gen(function* () {
|
|
1637
|
+
* const stores = yield* UploadFileDataStores;
|
|
1638
|
+
* const store = yield* stores.getDataStore("production", null);
|
|
1639
|
+
* return store;
|
|
1640
|
+
* }).pipe(Effect.provide(layer));
|
|
1641
|
+
* ```
|
|
1642
|
+
*/
|
|
1643
|
+
declare const createDataStoreLayer: (config: DataStoreConfig) => Promise<Layer.Layer<UploadFileDataStores, never, UploadFileKVStore>>;
|
|
1644
|
+
//#endregion
|
|
1645
|
+
//#region src/types/event-broadcaster.d.ts
|
|
1646
|
+
/**
|
|
1647
|
+
* Event broadcaster interface for pub/sub messaging across distributed instances.
|
|
1648
|
+
* Used by WebSocketManager to broadcast upload events to all connected instances.
|
|
1649
|
+
*/
|
|
1650
|
+
interface EventBroadcaster {
|
|
1651
|
+
/**
|
|
1652
|
+
* Publish a message to a channel
|
|
1653
|
+
*/
|
|
1654
|
+
readonly publish: (channel: string, message: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1655
|
+
/**
|
|
1656
|
+
* Subscribe to messages on a channel
|
|
1657
|
+
*/
|
|
1658
|
+
readonly subscribe: (channel: string, handler: (message: string) => void) => Effect.Effect<void, UploadistaError$1>;
|
|
1659
|
+
/**
|
|
1660
|
+
* Unsubscribe from a channel (optional - not all implementations may support)
|
|
1661
|
+
*/
|
|
1662
|
+
readonly unsubscribe?: (channel: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1663
|
+
}
|
|
1664
|
+
declare const EventBroadcasterService_base: Context.TagClass<EventBroadcasterService, "EventBroadcaster", EventBroadcaster>;
|
|
1665
|
+
/**
|
|
1666
|
+
* Context tag for EventBroadcaster service
|
|
1667
|
+
*/
|
|
1668
|
+
declare class EventBroadcasterService extends EventBroadcasterService_base {}
|
|
1669
|
+
//#endregion
|
|
1670
|
+
//#region src/types/upload-event.d.ts
|
|
1671
|
+
declare enum UploadEventType {
|
|
1672
|
+
UPLOAD_STARTED = "upload-started",
|
|
1673
|
+
UPLOAD_PROGRESS = "upload-progress",
|
|
1674
|
+
UPLOAD_COMPLETE = "upload-complete",
|
|
1675
|
+
UPLOAD_FAILED = "upload-failed",
|
|
1676
|
+
UPLOAD_VALIDATION_SUCCESS = "upload-validation-success",
|
|
1677
|
+
UPLOAD_VALIDATION_FAILED = "upload-validation-failed",
|
|
1678
|
+
UPLOAD_VALIDATION_WARNING = "upload-validation-warning",
|
|
1679
|
+
}
|
|
1680
|
+
declare const uploadEventSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
1681
|
+
type: z.ZodUnion<readonly [z.ZodLiteral<UploadEventType.UPLOAD_STARTED>, z.ZodLiteral<UploadEventType.UPLOAD_COMPLETE>]>;
|
|
1682
|
+
data: z.ZodObject<{
|
|
1683
|
+
id: z.ZodString;
|
|
1684
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
1685
|
+
offset: z.ZodNumber;
|
|
1686
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
1687
|
+
creationDate: z.ZodOptional<z.ZodString>;
|
|
1688
|
+
url: z.ZodOptional<z.ZodString>;
|
|
1689
|
+
sizeIsDeferred: z.ZodOptional<z.ZodBoolean>;
|
|
1690
|
+
checksum: z.ZodOptional<z.ZodString>;
|
|
1691
|
+
checksumAlgorithm: z.ZodOptional<z.ZodString>;
|
|
1692
|
+
storage: z.ZodObject<{
|
|
1693
|
+
id: z.ZodString;
|
|
1694
|
+
type: z.ZodString;
|
|
1695
|
+
path: z.ZodOptional<z.ZodString>;
|
|
1696
|
+
uploadId: z.ZodOptional<z.ZodString>;
|
|
1697
|
+
bucket: z.ZodOptional<z.ZodString>;
|
|
1698
|
+
}, z.core.$strip>;
|
|
1699
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
1700
|
+
flowId: z.ZodString;
|
|
1701
|
+
nodeId: z.ZodString;
|
|
1702
|
+
jobId: z.ZodString;
|
|
1703
|
+
}, z.core.$strip>>;
|
|
1704
|
+
}, z.core.$strip>;
|
|
1705
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
1706
|
+
flowId: z.ZodString;
|
|
1707
|
+
nodeId: z.ZodString;
|
|
1708
|
+
jobId: z.ZodString;
|
|
1709
|
+
}, z.core.$strip>>;
|
|
1710
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1711
|
+
type: z.ZodLiteral<UploadEventType.UPLOAD_PROGRESS>;
|
|
1712
|
+
data: z.ZodObject<{
|
|
1713
|
+
id: z.ZodString;
|
|
1714
|
+
progress: z.ZodNumber;
|
|
1715
|
+
total: z.ZodNumber;
|
|
1716
|
+
}, z.core.$strip>;
|
|
1717
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
1718
|
+
flowId: z.ZodString;
|
|
1719
|
+
nodeId: z.ZodString;
|
|
1720
|
+
jobId: z.ZodString;
|
|
1721
|
+
}, z.core.$strip>>;
|
|
1722
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1723
|
+
type: z.ZodLiteral<UploadEventType.UPLOAD_FAILED>;
|
|
1724
|
+
data: z.ZodObject<{
|
|
1725
|
+
id: z.ZodString;
|
|
1726
|
+
error: z.ZodString;
|
|
1727
|
+
}, z.core.$strip>;
|
|
1728
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
1729
|
+
flowId: z.ZodString;
|
|
1730
|
+
nodeId: z.ZodString;
|
|
1731
|
+
jobId: z.ZodString;
|
|
1732
|
+
}, z.core.$strip>>;
|
|
1733
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1734
|
+
type: z.ZodLiteral<UploadEventType.UPLOAD_VALIDATION_SUCCESS>;
|
|
1735
|
+
data: z.ZodObject<{
|
|
1736
|
+
id: z.ZodString;
|
|
1737
|
+
validationType: z.ZodEnum<{
|
|
1738
|
+
checksum: "checksum";
|
|
1739
|
+
mimetype: "mimetype";
|
|
1740
|
+
}>;
|
|
1741
|
+
algorithm: z.ZodOptional<z.ZodString>;
|
|
1742
|
+
}, z.core.$strip>;
|
|
1743
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
1744
|
+
flowId: z.ZodString;
|
|
1745
|
+
nodeId: z.ZodString;
|
|
1746
|
+
jobId: z.ZodString;
|
|
1747
|
+
}, z.core.$strip>>;
|
|
1748
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1749
|
+
type: z.ZodLiteral<UploadEventType.UPLOAD_VALIDATION_FAILED>;
|
|
1750
|
+
data: z.ZodObject<{
|
|
1751
|
+
id: z.ZodString;
|
|
1752
|
+
reason: z.ZodString;
|
|
1753
|
+
expected: z.ZodString;
|
|
1754
|
+
actual: z.ZodString;
|
|
1755
|
+
}, z.core.$strip>;
|
|
1756
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
1757
|
+
flowId: z.ZodString;
|
|
1758
|
+
nodeId: z.ZodString;
|
|
1759
|
+
jobId: z.ZodString;
|
|
1760
|
+
}, z.core.$strip>>;
|
|
1761
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1762
|
+
type: z.ZodLiteral<UploadEventType.UPLOAD_VALIDATION_WARNING>;
|
|
1763
|
+
data: z.ZodObject<{
|
|
1764
|
+
id: z.ZodString;
|
|
1765
|
+
message: z.ZodString;
|
|
1766
|
+
}, z.core.$strip>;
|
|
1767
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
1768
|
+
flowId: z.ZodString;
|
|
1769
|
+
nodeId: z.ZodString;
|
|
1770
|
+
jobId: z.ZodString;
|
|
1771
|
+
}, z.core.$strip>>;
|
|
1772
|
+
}, z.core.$strip>]>;
|
|
1773
|
+
type UploadEvent = z.infer<typeof uploadEventSchema>;
|
|
1774
|
+
//#endregion
|
|
1775
|
+
//#region src/types/websocket.d.ts
|
|
1776
|
+
/**
|
|
1777
|
+
* Platform-agnostic WebSocket connection interface
|
|
1778
|
+
*/
|
|
1779
|
+
interface WebSocketConnection {
|
|
1780
|
+
send(data: string): void;
|
|
1781
|
+
close(code?: number, reason?: string): void;
|
|
1782
|
+
readonly readyState: number;
|
|
1783
|
+
readonly id: string;
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* WebSocket message that can be sent/received
|
|
1787
|
+
*/
|
|
1788
|
+
declare const webSocketMessageSchema: z$1.ZodUnion<readonly [z$1.ZodObject<{
|
|
1789
|
+
type: z$1.ZodLiteral<"upload_event">;
|
|
1790
|
+
payload: z$1.ZodUnion<readonly [z$1.ZodObject<{
|
|
1791
|
+
type: z$1.ZodUnion<readonly [z$1.ZodLiteral<UploadEventType.UPLOAD_STARTED>, z$1.ZodLiteral<UploadEventType.UPLOAD_COMPLETE>]>;
|
|
1792
|
+
data: z$1.ZodObject<{
|
|
1793
|
+
id: z$1.ZodString;
|
|
1794
|
+
size: z$1.ZodOptional<z$1.ZodNumber>;
|
|
1795
|
+
offset: z$1.ZodNumber;
|
|
1796
|
+
metadata: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodUnion<readonly [z$1.ZodString, z$1.ZodNumber, z$1.ZodBoolean]>>>;
|
|
1797
|
+
creationDate: z$1.ZodOptional<z$1.ZodString>;
|
|
1798
|
+
url: z$1.ZodOptional<z$1.ZodString>;
|
|
1799
|
+
sizeIsDeferred: z$1.ZodOptional<z$1.ZodBoolean>;
|
|
1800
|
+
checksum: z$1.ZodOptional<z$1.ZodString>;
|
|
1801
|
+
checksumAlgorithm: z$1.ZodOptional<z$1.ZodString>;
|
|
1802
|
+
storage: z$1.ZodObject<{
|
|
1803
|
+
id: z$1.ZodString;
|
|
1804
|
+
type: z$1.ZodString;
|
|
1805
|
+
path: z$1.ZodOptional<z$1.ZodString>;
|
|
1806
|
+
uploadId: z$1.ZodOptional<z$1.ZodString>;
|
|
1807
|
+
bucket: z$1.ZodOptional<z$1.ZodString>;
|
|
1808
|
+
}, z$1.core.$strip>;
|
|
1809
|
+
flow: z$1.ZodOptional<z$1.ZodObject<{
|
|
1810
|
+
flowId: z$1.ZodString;
|
|
1811
|
+
nodeId: z$1.ZodString;
|
|
1812
|
+
jobId: z$1.ZodString;
|
|
1813
|
+
}, z$1.core.$strip>>;
|
|
1814
|
+
}, z$1.core.$strip>;
|
|
1815
|
+
flow: z$1.ZodOptional<z$1.ZodObject<{
|
|
1816
|
+
flowId: z$1.ZodString;
|
|
1817
|
+
nodeId: z$1.ZodString;
|
|
1818
|
+
jobId: z$1.ZodString;
|
|
1819
|
+
}, z$1.core.$strip>>;
|
|
1820
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1821
|
+
type: z$1.ZodLiteral<UploadEventType.UPLOAD_PROGRESS>;
|
|
1822
|
+
data: z$1.ZodObject<{
|
|
1823
|
+
id: z$1.ZodString;
|
|
1824
|
+
progress: z$1.ZodNumber;
|
|
1825
|
+
total: z$1.ZodNumber;
|
|
1826
|
+
}, z$1.core.$strip>;
|
|
1827
|
+
flow: z$1.ZodOptional<z$1.ZodObject<{
|
|
1828
|
+
flowId: z$1.ZodString;
|
|
1829
|
+
nodeId: z$1.ZodString;
|
|
1830
|
+
jobId: z$1.ZodString;
|
|
1831
|
+
}, z$1.core.$strip>>;
|
|
1832
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1833
|
+
type: z$1.ZodLiteral<UploadEventType.UPLOAD_FAILED>;
|
|
1834
|
+
data: z$1.ZodObject<{
|
|
1835
|
+
id: z$1.ZodString;
|
|
1836
|
+
error: z$1.ZodString;
|
|
1837
|
+
}, z$1.core.$strip>;
|
|
1838
|
+
flow: z$1.ZodOptional<z$1.ZodObject<{
|
|
1839
|
+
flowId: z$1.ZodString;
|
|
1840
|
+
nodeId: z$1.ZodString;
|
|
1841
|
+
jobId: z$1.ZodString;
|
|
1842
|
+
}, z$1.core.$strip>>;
|
|
1843
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1844
|
+
type: z$1.ZodLiteral<UploadEventType.UPLOAD_VALIDATION_SUCCESS>;
|
|
1845
|
+
data: z$1.ZodObject<{
|
|
1846
|
+
id: z$1.ZodString;
|
|
1847
|
+
validationType: z$1.ZodEnum<{
|
|
1848
|
+
checksum: "checksum";
|
|
1849
|
+
mimetype: "mimetype";
|
|
1850
|
+
}>;
|
|
1851
|
+
algorithm: z$1.ZodOptional<z$1.ZodString>;
|
|
1852
|
+
}, z$1.core.$strip>;
|
|
1853
|
+
flow: z$1.ZodOptional<z$1.ZodObject<{
|
|
1854
|
+
flowId: z$1.ZodString;
|
|
1855
|
+
nodeId: z$1.ZodString;
|
|
1856
|
+
jobId: z$1.ZodString;
|
|
1857
|
+
}, z$1.core.$strip>>;
|
|
1858
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1859
|
+
type: z$1.ZodLiteral<UploadEventType.UPLOAD_VALIDATION_FAILED>;
|
|
1860
|
+
data: z$1.ZodObject<{
|
|
1861
|
+
id: z$1.ZodString;
|
|
1862
|
+
reason: z$1.ZodString;
|
|
1863
|
+
expected: z$1.ZodString;
|
|
1864
|
+
actual: z$1.ZodString;
|
|
1865
|
+
}, z$1.core.$strip>;
|
|
1866
|
+
flow: z$1.ZodOptional<z$1.ZodObject<{
|
|
1867
|
+
flowId: z$1.ZodString;
|
|
1868
|
+
nodeId: z$1.ZodString;
|
|
1869
|
+
jobId: z$1.ZodString;
|
|
1870
|
+
}, z$1.core.$strip>>;
|
|
1871
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1872
|
+
type: z$1.ZodLiteral<UploadEventType.UPLOAD_VALIDATION_WARNING>;
|
|
1873
|
+
data: z$1.ZodObject<{
|
|
1874
|
+
id: z$1.ZodString;
|
|
1875
|
+
message: z$1.ZodString;
|
|
1876
|
+
}, z$1.core.$strip>;
|
|
1877
|
+
flow: z$1.ZodOptional<z$1.ZodObject<{
|
|
1878
|
+
flowId: z$1.ZodString;
|
|
1879
|
+
nodeId: z$1.ZodString;
|
|
1880
|
+
jobId: z$1.ZodString;
|
|
1881
|
+
}, z$1.core.$strip>>;
|
|
1882
|
+
}, z$1.core.$strip>]>;
|
|
1883
|
+
timestamp: z$1.ZodOptional<z$1.ZodString>;
|
|
1884
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1885
|
+
type: z$1.ZodLiteral<"flow_event">;
|
|
1886
|
+
payload: z$1.ZodAny;
|
|
1887
|
+
timestamp: z$1.ZodOptional<z$1.ZodString>;
|
|
1888
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1889
|
+
type: z$1.ZodLiteral<"subscribed">;
|
|
1890
|
+
payload: z$1.ZodObject<{
|
|
1891
|
+
eventKey: z$1.ZodString;
|
|
1892
|
+
}, z$1.core.$strip>;
|
|
1893
|
+
timestamp: z$1.ZodOptional<z$1.ZodString>;
|
|
1894
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1895
|
+
type: z$1.ZodLiteral<"error">;
|
|
1896
|
+
message: z$1.ZodOptional<z$1.ZodString>;
|
|
1897
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1898
|
+
type: z$1.ZodLiteral<"pong">;
|
|
1899
|
+
timestamp: z$1.ZodOptional<z$1.ZodString>;
|
|
1900
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1901
|
+
type: z$1.ZodLiteral<"ping">;
|
|
1902
|
+
timestamp: z$1.ZodOptional<z$1.ZodString>;
|
|
1903
|
+
}, z$1.core.$strip>, z$1.ZodObject<{
|
|
1904
|
+
type: z$1.ZodLiteral<"connection">;
|
|
1905
|
+
message: z$1.ZodOptional<z$1.ZodString>;
|
|
1906
|
+
uploadId: z$1.ZodOptional<z$1.ZodString>;
|
|
1907
|
+
timestamp: z$1.ZodOptional<z$1.ZodString>;
|
|
1908
|
+
}, z$1.core.$strip>]>;
|
|
1909
|
+
type WebSocketMessage<TEvent = unknown> = z$1.infer<typeof webSocketMessageSchema> | {
|
|
1910
|
+
type: "upload_event";
|
|
1911
|
+
payload: TEvent;
|
|
1912
|
+
timestamp?: string;
|
|
1913
|
+
} | {
|
|
1914
|
+
type: "flow_event";
|
|
1915
|
+
payload: TEvent;
|
|
1916
|
+
timestamp?: string;
|
|
1917
|
+
};
|
|
1918
|
+
//#endregion
|
|
1919
|
+
//#region src/types/event-emitter.d.ts
|
|
1920
|
+
/**
|
|
1921
|
+
* Base event emitter interface for raw string message broadcasting.
|
|
1922
|
+
*
|
|
1923
|
+
* This is the low-level interface that event broadcasting implementations
|
|
1924
|
+
* (WebSocket, Server-Sent Events, etc.) implement. It emits raw string messages
|
|
1925
|
+
* without type safety or serialization.
|
|
1926
|
+
*
|
|
1927
|
+
* @property subscribe - Registers a WebSocket connection to receive events for a key
|
|
1928
|
+
* @property unsubscribe - Removes subscription for a key
|
|
1929
|
+
* @property emit - Broadcasts a string message to all subscribers of a key
|
|
1930
|
+
*
|
|
1931
|
+
* @example
|
|
1932
|
+
* ```typescript
|
|
1933
|
+
* // Implement BaseEventEmitter with WebSocket broadcast
|
|
1934
|
+
* const websocketEmitter: BaseEventEmitter = {
|
|
1935
|
+
* subscribe: (key, connection) => Effect.sync(() => {
|
|
1936
|
+
* connections.set(key, [...(connections.get(key) || []), connection]);
|
|
1937
|
+
* }),
|
|
1938
|
+
*
|
|
1939
|
+
* unsubscribe: (key) => Effect.sync(() => {
|
|
1940
|
+
* connections.delete(key);
|
|
1941
|
+
* }),
|
|
1942
|
+
*
|
|
1943
|
+
* emit: (key, event) => Effect.sync(() => {
|
|
1944
|
+
* const subs = connections.get(key) || [];
|
|
1945
|
+
* subs.forEach(conn => conn.send(event));
|
|
1946
|
+
* })
|
|
1947
|
+
* };
|
|
1948
|
+
* ```
|
|
1949
|
+
*/
|
|
1950
|
+
interface BaseEventEmitter {
|
|
1951
|
+
readonly subscribe: (key: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError$1>;
|
|
1952
|
+
readonly unsubscribe: (key: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1953
|
+
readonly emit: (key: string, event: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Type-safe event emitter interface with automatic serialization.
|
|
1957
|
+
*
|
|
1958
|
+
* This wraps a BaseEventEmitter and handles event serialization to JSON messages,
|
|
1959
|
+
* providing type safety for events and ensuring consistent message format.
|
|
1960
|
+
*
|
|
1961
|
+
* @template TEvent - The type of events emitted by this emitter
|
|
1962
|
+
*
|
|
1963
|
+
* @property subscribe - Registers a WebSocket connection to receive typed events
|
|
1964
|
+
* @property unsubscribe - Removes subscription
|
|
1965
|
+
* @property emit - Serializes and broadcasts a typed event
|
|
1966
|
+
*
|
|
1967
|
+
* @example
|
|
1968
|
+
* ```typescript
|
|
1969
|
+
* // Use a typed event emitter
|
|
1970
|
+
* const uploadEmitter: EventEmitter<UploadEvent> = new TypedEventEmitter(
|
|
1971
|
+
* baseEmitter,
|
|
1972
|
+
* (event) => JSON.stringify({ type: 'upload', payload: event })
|
|
1973
|
+
* );
|
|
1974
|
+
*
|
|
1975
|
+
* // Emit type-safe events
|
|
1976
|
+
* const program = Effect.gen(function* () {
|
|
1977
|
+
* const event: UploadEvent = {
|
|
1978
|
+
* uploadId: "upload123",
|
|
1979
|
+
* type: "progress",
|
|
1980
|
+
* offset: 1024,
|
|
1981
|
+
* size: 2048
|
|
1982
|
+
* };
|
|
1983
|
+
*
|
|
1984
|
+
* // Automatic serialization
|
|
1985
|
+
* yield* uploadEmitter.emit("upload123", event);
|
|
1986
|
+
* });
|
|
1987
|
+
* ```
|
|
1988
|
+
*/
|
|
1989
|
+
type EventEmitter<TEvent> = {
|
|
1990
|
+
readonly subscribe: (key: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError$1>;
|
|
1991
|
+
readonly unsubscribe: (key: string) => Effect.Effect<void, UploadistaError$1>;
|
|
1992
|
+
readonly emit: (key: string, event: TEvent) => Effect.Effect<void, UploadistaError$1>;
|
|
1993
|
+
};
|
|
1994
|
+
/**
|
|
1995
|
+
* Typed wrapper class that adds event serialization to a BaseEventEmitter.
|
|
1996
|
+
*
|
|
1997
|
+
* This class implements the EventEmitter interface by wrapping a BaseEventEmitter
|
|
1998
|
+
* and handling serialization for a specific event type. It converts typed events
|
|
1999
|
+
* to JSON message strings before broadcasting.
|
|
2000
|
+
*
|
|
2001
|
+
* @template TEvent - The type of events to emit
|
|
2002
|
+
*
|
|
2003
|
+
* @example
|
|
2004
|
+
* ```typescript
|
|
2005
|
+
* // Create a typed emitter for UploadEvent
|
|
2006
|
+
* const uploadEmitter = new TypedEventEmitter<UploadEvent>(
|
|
2007
|
+
* baseEmitter,
|
|
2008
|
+
* (event) => JSON.stringify({
|
|
2009
|
+
* type: "upload_event",
|
|
2010
|
+
* payload: event,
|
|
2011
|
+
* timestamp: new Date().toISOString()
|
|
2012
|
+
* })
|
|
2013
|
+
* );
|
|
2014
|
+
*
|
|
2015
|
+
* // Use the emitter
|
|
2016
|
+
* const effect = Effect.gen(function* () {
|
|
2017
|
+
* // Subscribe a WebSocket connection
|
|
2018
|
+
* yield* uploadEmitter.subscribe("upload123", websocket);
|
|
2019
|
+
*
|
|
2020
|
+
* // Emit an event (automatically serialized)
|
|
2021
|
+
* yield* uploadEmitter.emit("upload123", {
|
|
2022
|
+
* uploadId: "upload123",
|
|
2023
|
+
* type: "completed",
|
|
2024
|
+
* offset: 2048,
|
|
2025
|
+
* size: 2048
|
|
2026
|
+
* });
|
|
2027
|
+
*
|
|
2028
|
+
* // Unsubscribe when done
|
|
2029
|
+
* yield* uploadEmitter.unsubscribe("upload123");
|
|
2030
|
+
* });
|
|
2031
|
+
*
|
|
2032
|
+
* // Custom message format
|
|
2033
|
+
* const customEmitter = new TypedEventEmitter<MyEvent>(
|
|
2034
|
+
* baseEmitter,
|
|
2035
|
+
* (event) => `EVENT:${event.type}:${JSON.stringify(event.data)}`
|
|
2036
|
+
* );
|
|
2037
|
+
* ```
|
|
2038
|
+
*/
|
|
2039
|
+
declare class TypedEventEmitter<TEvent> implements EventEmitter<TEvent> {
|
|
2040
|
+
private baseEmitter;
|
|
2041
|
+
private eventToMessage;
|
|
2042
|
+
constructor(baseEmitter: BaseEventEmitter, eventToMessage: (event: TEvent) => string);
|
|
2043
|
+
subscribe: (key: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError$1>;
|
|
2044
|
+
unsubscribe: (key: string) => Effect.Effect<void, UploadistaError$1>;
|
|
2045
|
+
emit: (key: string, event: TEvent) => Effect.Effect<void, UploadistaError$1>;
|
|
2046
|
+
}
|
|
2047
|
+
/**
|
|
2048
|
+
* Default event-to-message serialization helper.
|
|
2049
|
+
*
|
|
2050
|
+
* Creates a standardized JSON message format with type, payload, and timestamp.
|
|
2051
|
+
* This is the recommended way to serialize events for WebSocket transmission.
|
|
2052
|
+
*
|
|
2053
|
+
* @param messageType - The message type identifier ("upload_event" or "flow_event")
|
|
2054
|
+
* @returns An object with an eventToMessage function
|
|
2055
|
+
*
|
|
2056
|
+
* @example
|
|
2057
|
+
* ```typescript
|
|
2058
|
+
* // Create emitter with standard serialization
|
|
2059
|
+
* const emitter = new TypedEventEmitter<UploadEvent>(
|
|
2060
|
+
* baseEmitter,
|
|
2061
|
+
* eventToMessageSerializer("upload_event").eventToMessage
|
|
2062
|
+
* );
|
|
2063
|
+
*
|
|
2064
|
+
* // Messages will be formatted as:
|
|
2065
|
+
* // {
|
|
2066
|
+
* // "type": "upload_event",
|
|
2067
|
+
* // "payload": { ...event data... },
|
|
2068
|
+
* // "timestamp": "2024-01-15T10:30:00.000Z"
|
|
2069
|
+
* // }
|
|
2070
|
+
* ```
|
|
2071
|
+
*/
|
|
2072
|
+
declare const eventToMessageSerializer: (messageType: "upload_event" | "flow_event") => {
|
|
2073
|
+
eventToMessage: <T>(event: T) => string;
|
|
2074
|
+
};
|
|
2075
|
+
declare const BaseEventEmitterService_base: Context.TagClass<BaseEventEmitterService, "BaseEventEmitter", BaseEventEmitter>;
|
|
2076
|
+
/**
|
|
2077
|
+
* Effect-TS context tag for the base untyped event emitter.
|
|
2078
|
+
*
|
|
2079
|
+
* This is the low-level emitter that broadcasting implementations provide.
|
|
2080
|
+
* Most application code should use typed emitters like UploadEventEmitter instead.
|
|
2081
|
+
*
|
|
2082
|
+
* @example
|
|
2083
|
+
* ```typescript
|
|
2084
|
+
* // Provide a base emitter implementation
|
|
2085
|
+
* const baseEmitterLayer = Layer.succeed(BaseEventEmitterService, websocketEmitter);
|
|
2086
|
+
*
|
|
2087
|
+
* // Use in an Effect
|
|
2088
|
+
* const effect = Effect.gen(function* () {
|
|
2089
|
+
* const baseEmitter = yield* BaseEventEmitterService;
|
|
2090
|
+
* yield* baseEmitter.emit("channel1", "raw message");
|
|
2091
|
+
* });
|
|
2092
|
+
* ```
|
|
2093
|
+
*/
|
|
2094
|
+
declare class BaseEventEmitterService extends BaseEventEmitterService_base {}
|
|
2095
|
+
declare const UploadEventEmitter_base: Context.TagClass<UploadEventEmitter, "UploadEventEmitter", EventEmitter<{
|
|
2096
|
+
type: UploadEventType.UPLOAD_STARTED | UploadEventType.UPLOAD_COMPLETE;
|
|
2097
|
+
data: {
|
|
2098
|
+
id: string;
|
|
2099
|
+
offset: number;
|
|
2100
|
+
storage: {
|
|
2101
|
+
id: string;
|
|
2102
|
+
type: string;
|
|
2103
|
+
path?: string | undefined;
|
|
2104
|
+
uploadId?: string | undefined;
|
|
2105
|
+
bucket?: string | undefined;
|
|
2106
|
+
};
|
|
2107
|
+
size?: number | undefined;
|
|
2108
|
+
metadata?: Record<string, string | number | boolean> | undefined;
|
|
2109
|
+
creationDate?: string | undefined;
|
|
2110
|
+
url?: string | undefined;
|
|
2111
|
+
sizeIsDeferred?: boolean | undefined;
|
|
2112
|
+
checksum?: string | undefined;
|
|
2113
|
+
checksumAlgorithm?: string | undefined;
|
|
2114
|
+
flow?: {
|
|
2115
|
+
flowId: string;
|
|
2116
|
+
nodeId: string;
|
|
2117
|
+
jobId: string;
|
|
2118
|
+
} | undefined;
|
|
2119
|
+
};
|
|
2120
|
+
flow?: {
|
|
2121
|
+
flowId: string;
|
|
2122
|
+
nodeId: string;
|
|
2123
|
+
jobId: string;
|
|
2124
|
+
} | undefined;
|
|
2125
|
+
} | {
|
|
2126
|
+
type: UploadEventType.UPLOAD_PROGRESS;
|
|
2127
|
+
data: {
|
|
2128
|
+
id: string;
|
|
2129
|
+
progress: number;
|
|
2130
|
+
total: number;
|
|
2131
|
+
};
|
|
2132
|
+
flow?: {
|
|
2133
|
+
flowId: string;
|
|
2134
|
+
nodeId: string;
|
|
2135
|
+
jobId: string;
|
|
2136
|
+
} | undefined;
|
|
2137
|
+
} | {
|
|
2138
|
+
type: UploadEventType.UPLOAD_FAILED;
|
|
2139
|
+
data: {
|
|
2140
|
+
id: string;
|
|
2141
|
+
error: string;
|
|
2142
|
+
};
|
|
2143
|
+
flow?: {
|
|
2144
|
+
flowId: string;
|
|
2145
|
+
nodeId: string;
|
|
2146
|
+
jobId: string;
|
|
2147
|
+
} | undefined;
|
|
2148
|
+
} | {
|
|
2149
|
+
type: UploadEventType.UPLOAD_VALIDATION_SUCCESS;
|
|
2150
|
+
data: {
|
|
2151
|
+
id: string;
|
|
2152
|
+
validationType: "checksum" | "mimetype";
|
|
2153
|
+
algorithm?: string | undefined;
|
|
2154
|
+
};
|
|
2155
|
+
flow?: {
|
|
2156
|
+
flowId: string;
|
|
2157
|
+
nodeId: string;
|
|
2158
|
+
jobId: string;
|
|
2159
|
+
} | undefined;
|
|
2160
|
+
} | {
|
|
2161
|
+
type: UploadEventType.UPLOAD_VALIDATION_FAILED;
|
|
2162
|
+
data: {
|
|
2163
|
+
id: string;
|
|
2164
|
+
reason: string;
|
|
2165
|
+
expected: string;
|
|
2166
|
+
actual: string;
|
|
2167
|
+
};
|
|
2168
|
+
flow?: {
|
|
2169
|
+
flowId: string;
|
|
2170
|
+
nodeId: string;
|
|
2171
|
+
jobId: string;
|
|
2172
|
+
} | undefined;
|
|
2173
|
+
} | {
|
|
2174
|
+
type: UploadEventType.UPLOAD_VALIDATION_WARNING;
|
|
2175
|
+
data: {
|
|
2176
|
+
id: string;
|
|
2177
|
+
message: string;
|
|
2178
|
+
};
|
|
2179
|
+
flow?: {
|
|
2180
|
+
flowId: string;
|
|
2181
|
+
nodeId: string;
|
|
2182
|
+
jobId: string;
|
|
2183
|
+
} | undefined;
|
|
2184
|
+
}>>;
|
|
2185
|
+
/**
|
|
2186
|
+
* Effect-TS context tag for the UploadEvent typed emitter.
|
|
2187
|
+
*
|
|
2188
|
+
* This provides type-safe event emission for upload progress and lifecycle events.
|
|
2189
|
+
* It's the primary way to broadcast upload events to connected clients.
|
|
2190
|
+
*
|
|
2191
|
+
* @example
|
|
2192
|
+
* ```typescript
|
|
2193
|
+
* const uploadEffect = Effect.gen(function* () {
|
|
2194
|
+
* const emitter = yield* UploadEventEmitter;
|
|
2195
|
+
*
|
|
2196
|
+
* // Subscribe a client to upload events
|
|
2197
|
+
* yield* emitter.subscribe("upload123", websocketConnection);
|
|
2198
|
+
*
|
|
2199
|
+
* // Emit progress event
|
|
2200
|
+
* yield* emitter.emit("upload123", {
|
|
2201
|
+
* uploadId: "upload123",
|
|
2202
|
+
* type: "progress",
|
|
2203
|
+
* offset: 512000,
|
|
2204
|
+
* size: 1024000
|
|
2205
|
+
* });
|
|
2206
|
+
*
|
|
2207
|
+
* // Emit completion event
|
|
2208
|
+
* yield* emitter.emit("upload123", {
|
|
2209
|
+
* uploadId: "upload123",
|
|
2210
|
+
* type: "completed",
|
|
2211
|
+
* offset: 1024000,
|
|
2212
|
+
* size: 1024000
|
|
2213
|
+
* });
|
|
2214
|
+
* });
|
|
2215
|
+
* ```
|
|
2216
|
+
*/
|
|
2217
|
+
declare class UploadEventEmitter extends UploadEventEmitter_base {}
|
|
2218
|
+
/**
|
|
2219
|
+
* Effect Layer that creates the UploadEventEmitter from a BaseEventEmitter.
|
|
2220
|
+
*
|
|
2221
|
+
* This layer automatically wires up JSON serialization for UploadEvent objects
|
|
2222
|
+
* with the standard "upload_event" message format.
|
|
2223
|
+
*
|
|
2224
|
+
* @example
|
|
2225
|
+
* ```typescript
|
|
2226
|
+
* const program = Effect.gen(function* () {
|
|
2227
|
+
* const emitter = yield* UploadEventEmitter;
|
|
2228
|
+
* // Use the emitter...
|
|
2229
|
+
* }).pipe(
|
|
2230
|
+
* Effect.provide(uploadEventEmitter),
|
|
2231
|
+
* Effect.provide(baseEmitterLayer)
|
|
2232
|
+
* );
|
|
2233
|
+
* ```
|
|
2234
|
+
*/
|
|
2235
|
+
declare const uploadEventEmitter: Layer.Layer<UploadEventEmitter, never, BaseEventEmitterService>;
|
|
2236
|
+
declare const FlowEventEmitter_base: Context.TagClass<FlowEventEmitter, "FlowEventEmitter", EventEmitter<FlowEvent>>;
|
|
2237
|
+
/**
|
|
2238
|
+
* Effect-TS context tag for the FlowEvent typed emitter.
|
|
2239
|
+
*
|
|
2240
|
+
* This provides type-safe event emission for flow processing lifecycle events.
|
|
2241
|
+
* It's used to broadcast flow execution progress, node completion, and errors.
|
|
2242
|
+
*
|
|
2243
|
+
* @example
|
|
2244
|
+
* ```typescript
|
|
2245
|
+
* const flowEffect = Effect.gen(function* () {
|
|
2246
|
+
* const emitter = yield* FlowEventEmitter;
|
|
2247
|
+
*
|
|
2248
|
+
* // Subscribe a client to flow job events
|
|
2249
|
+
* yield* emitter.subscribe("job123", websocketConnection);
|
|
2250
|
+
*
|
|
2251
|
+
* // Emit node start event
|
|
2252
|
+
* yield* emitter.emit("job123", {
|
|
2253
|
+
* jobId: "job123",
|
|
2254
|
+
* eventType: "NodeStart",
|
|
2255
|
+
* flowId: "flow_resize",
|
|
2256
|
+
* nodeId: "resize_1"
|
|
2257
|
+
* });
|
|
2258
|
+
*
|
|
2259
|
+
* // Emit node completion event
|
|
2260
|
+
* yield* emitter.emit("job123", {
|
|
2261
|
+
* jobId: "job123",
|
|
2262
|
+
* eventType: "NodeEnd",
|
|
2263
|
+
* flowId: "flow_resize",
|
|
2264
|
+
* nodeId: "resize_1",
|
|
2265
|
+
* result: { width: 800, height: 600 }
|
|
2266
|
+
* });
|
|
2267
|
+
* });
|
|
2268
|
+
* ```
|
|
2269
|
+
*/
|
|
2270
|
+
declare class FlowEventEmitter extends FlowEventEmitter_base {}
|
|
2271
|
+
/**
|
|
2272
|
+
* Effect Layer that creates the FlowEventEmitter from a BaseEventEmitter.
|
|
2273
|
+
*
|
|
2274
|
+
* This layer automatically wires up JSON serialization for FlowEvent objects
|
|
2275
|
+
* with the standard "flow_event" message format.
|
|
2276
|
+
*
|
|
2277
|
+
* @example
|
|
2278
|
+
* ```typescript
|
|
2279
|
+
* const program = Effect.gen(function* () {
|
|
2280
|
+
* const emitter = yield* FlowEventEmitter;
|
|
2281
|
+
* // Use the emitter...
|
|
2282
|
+
* }).pipe(
|
|
2283
|
+
* Effect.provide(flowEventEmitter),
|
|
2284
|
+
* Effect.provide(baseEmitterLayer)
|
|
2285
|
+
* );
|
|
2286
|
+
* ```
|
|
2287
|
+
*/
|
|
2288
|
+
declare const flowEventEmitter: Layer.Layer<FlowEventEmitter, never, BaseEventEmitterService>;
|
|
2289
|
+
//#endregion
|
|
2290
|
+
//#region src/types/input-file.d.ts
|
|
2291
|
+
/**
|
|
2292
|
+
* Zod schema for validating InputFile objects.
|
|
2293
|
+
*
|
|
2294
|
+
* This schema defines the structure and validation rules for file upload requests.
|
|
2295
|
+
* Use this schema to parse and validate input data when creating new uploads.
|
|
2296
|
+
*
|
|
2297
|
+
* @see {@link InputFile} for the TypeScript type
|
|
2298
|
+
*/
|
|
2299
|
+
declare const inputFileSchema: z.ZodObject<{
|
|
2300
|
+
uploadLengthDeferred: z.ZodOptional<z.ZodBoolean>;
|
|
2301
|
+
storageId: z.ZodString;
|
|
2302
|
+
size: z.ZodNumber;
|
|
2303
|
+
type: z.ZodString;
|
|
2304
|
+
fileName: z.ZodOptional<z.ZodString>;
|
|
2305
|
+
lastModified: z.ZodOptional<z.ZodNumber>;
|
|
2306
|
+
metadata: z.ZodOptional<z.ZodString>;
|
|
2307
|
+
checksum: z.ZodOptional<z.ZodString>;
|
|
2308
|
+
checksumAlgorithm: z.ZodOptional<z.ZodString>;
|
|
2309
|
+
flow: z.ZodOptional<z.ZodObject<{
|
|
2310
|
+
flowId: z.ZodString;
|
|
2311
|
+
nodeId: z.ZodString;
|
|
2312
|
+
jobId: z.ZodString;
|
|
2313
|
+
}, z.core.$strip>>;
|
|
2314
|
+
}, z.core.$strip>;
|
|
2315
|
+
/**
|
|
2316
|
+
* Represents the input data for creating a new file upload.
|
|
2317
|
+
*
|
|
2318
|
+
* This type defines the information required to initiate an upload.
|
|
2319
|
+
* It's used by clients to provide upload metadata before sending file data.
|
|
2320
|
+
*
|
|
2321
|
+
* @property storageId - Target storage backend identifier (e.g., "s3-production", "azure-blob")
|
|
2322
|
+
* @property size - File size in bytes
|
|
2323
|
+
* @property type - MIME type of the file (e.g., "image/jpeg", "application/pdf")
|
|
2324
|
+
* @property uploadLengthDeferred - If true, file size is not known upfront (streaming upload)
|
|
2325
|
+
* @property fileName - Original filename from the client
|
|
2326
|
+
* @property lastModified - File's last modified timestamp in milliseconds since epoch
|
|
2327
|
+
* @property metadata - Base64-encoded metadata string (as per tus protocol)
|
|
2328
|
+
* @property checksum - Expected file checksum for validation
|
|
2329
|
+
* @property checksumAlgorithm - Algorithm used for checksum (e.g., "md5", "sha256")
|
|
2330
|
+
* @property flow - Optional flow processing configuration
|
|
2331
|
+
* @property flow.flowId - ID of the flow to execute on this file
|
|
2332
|
+
* @property flow.nodeId - Starting node ID in the flow
|
|
2333
|
+
* @property flow.jobId - Flow job execution ID
|
|
2334
|
+
*
|
|
2335
|
+
* @example
|
|
2336
|
+
* ```typescript
|
|
2337
|
+
* // Basic file upload
|
|
2338
|
+
* const inputFile: InputFile = {
|
|
2339
|
+
* storageId: "s3-production",
|
|
2340
|
+
* size: 1024000,
|
|
2341
|
+
* type: "image/jpeg",
|
|
2342
|
+
* fileName: "photo.jpg",
|
|
2343
|
+
* lastModified: Date.now()
|
|
2344
|
+
* };
|
|
2345
|
+
*
|
|
2346
|
+
* // Upload with metadata (base64 encoded as per tus protocol)
|
|
2347
|
+
* const metadata = btoa(JSON.stringify({
|
|
2348
|
+
* userId: "user_123",
|
|
2349
|
+
* albumId: "album_456"
|
|
2350
|
+
* }));
|
|
2351
|
+
* const inputWithMetadata: InputFile = {
|
|
2352
|
+
* storageId: "s3-production",
|
|
2353
|
+
* size: 2048000,
|
|
2354
|
+
* type: "image/png",
|
|
2355
|
+
* fileName: "screenshot.png",
|
|
2356
|
+
* metadata
|
|
2357
|
+
* };
|
|
2358
|
+
*
|
|
2359
|
+
* // Upload with checksum validation
|
|
2360
|
+
* const inputWithChecksum: InputFile = {
|
|
2361
|
+
* storageId: "s3-production",
|
|
2362
|
+
* size: 512000,
|
|
2363
|
+
* type: "application/pdf",
|
|
2364
|
+
* fileName: "document.pdf",
|
|
2365
|
+
* checksum: "5d41402abc4b2a76b9719d911017c592",
|
|
2366
|
+
* checksumAlgorithm: "md5"
|
|
2367
|
+
* };
|
|
2368
|
+
*
|
|
2369
|
+
* // Upload that triggers a flow
|
|
2370
|
+
* const inputWithFlow: InputFile = {
|
|
2371
|
+
* storageId: "s3-temp",
|
|
2372
|
+
* size: 4096000,
|
|
2373
|
+
* type: "image/jpeg",
|
|
2374
|
+
* fileName: "large-image.jpg",
|
|
2375
|
+
* flow: {
|
|
2376
|
+
* flowId: "resize-and-optimize",
|
|
2377
|
+
* nodeId: "input_1",
|
|
2378
|
+
* jobId: "job_789"
|
|
2379
|
+
* }
|
|
2380
|
+
* };
|
|
2381
|
+
*
|
|
2382
|
+
* // Streaming upload (size unknown)
|
|
2383
|
+
* const streamingInput: InputFile = {
|
|
2384
|
+
* storageId: "s3-production",
|
|
2385
|
+
* size: 0, // Will be updated as data arrives
|
|
2386
|
+
* type: "video/mp4",
|
|
2387
|
+
* uploadLengthDeferred: true,
|
|
2388
|
+
* fileName: "live-stream.mp4"
|
|
2389
|
+
* };
|
|
2390
|
+
* ```
|
|
2391
|
+
*/
|
|
2392
|
+
type InputFile = z.infer<typeof inputFileSchema>;
|
|
2393
|
+
//#endregion
|
|
2394
|
+
//#region src/types/middleware.d.ts
|
|
2395
|
+
type MiddlewareContext = {
|
|
2396
|
+
request: Request;
|
|
2397
|
+
uploadId?: string;
|
|
2398
|
+
metadata?: Record<string, string>;
|
|
2399
|
+
};
|
|
2400
|
+
type MiddlewareNext = () => Promise<Response>;
|
|
2401
|
+
type Middleware = (context: MiddlewareContext, next: MiddlewareNext) => Promise<Response>;
|
|
2402
|
+
declare const MiddlewareService_base: Context.TagClass<MiddlewareService, "MiddlewareService", {
|
|
2403
|
+
readonly execute: (middlewares: Middleware[], context: MiddlewareContext, handler: MiddlewareNext) => Effect.Effect<Response, UploadistaError$1>;
|
|
2404
|
+
}>;
|
|
2405
|
+
declare class MiddlewareService extends MiddlewareService_base {}
|
|
2406
|
+
declare const MiddlewareServiceLive: Layer.Layer<MiddlewareService, never, never>;
|
|
2407
|
+
//#endregion
|
|
2408
|
+
//#region src/upload/mime.d.ts
|
|
2409
|
+
/**
|
|
2410
|
+
* Detect MIME type from buffer using magic bytes (file signatures).
|
|
2411
|
+
* Supports a wide range of common file types including images, videos, audio, documents, and archives.
|
|
2412
|
+
*
|
|
2413
|
+
* @param buffer - File content as Uint8Array
|
|
2414
|
+
* @param filename - Optional filename for extension-based fallback
|
|
2415
|
+
* @returns Detected MIME type or "application/octet-stream" if unknown
|
|
2416
|
+
*/
|
|
2417
|
+
declare const detectMimeType: (buffer: Uint8Array, filename?: string) => string;
|
|
2418
|
+
/**
|
|
2419
|
+
* Compare two MIME types with lenient matching.
|
|
2420
|
+
* Matches on major type (e.g., "image/*") to allow for minor variations.
|
|
2421
|
+
*
|
|
2422
|
+
* @param declared - MIME type provided by client
|
|
2423
|
+
* @param detected - MIME type detected from file content
|
|
2424
|
+
* @returns true if MIME types are compatible
|
|
2425
|
+
*
|
|
2426
|
+
* @example
|
|
2427
|
+
* compareMimeTypes("image/png", "image/apng") // true
|
|
2428
|
+
* compareMimeTypes("image/jpeg", "image/png") // true (both images)
|
|
2429
|
+
* compareMimeTypes("image/png", "application/pdf") // false
|
|
2430
|
+
*/
|
|
2431
|
+
declare function compareMimeTypes(declared: string, detected: string): boolean;
|
|
2432
|
+
//#endregion
|
|
2433
|
+
//#region src/upload/upload-server.d.ts
|
|
2434
|
+
/**
|
|
2435
|
+
* Legacy configuration options for UploadServer.
|
|
2436
|
+
*
|
|
2437
|
+
* @deprecated Use Effect Layers instead of this configuration object.
|
|
2438
|
+
* This type is kept for backward compatibility.
|
|
2439
|
+
*
|
|
2440
|
+
* @property dataStore - DataStore instance or factory function
|
|
2441
|
+
* @property kvStore - KV store for upload metadata
|
|
2442
|
+
* @property eventEmitter - Event emitter for upload progress
|
|
2443
|
+
* @property generateId - Optional ID generator (defaults to UUID)
|
|
2444
|
+
* @property middlewares - Optional request middlewares
|
|
2445
|
+
* @property withTracing - Enable Effect tracing for debugging
|
|
2446
|
+
*/
|
|
2447
|
+
type UploadServerOptions = {
|
|
2448
|
+
dataStore: ((storageId: string) => Promise<DataStore<UploadFile>>) | DataStore<UploadFile>;
|
|
2449
|
+
kvStore: KvStore<UploadFile>;
|
|
2450
|
+
eventEmitter: EventEmitter<UploadEvent>;
|
|
2451
|
+
generateId?: GenerateIdShape;
|
|
2452
|
+
middlewares?: Middleware[];
|
|
2453
|
+
withTracing?: boolean;
|
|
2454
|
+
};
|
|
2455
|
+
/**
|
|
2456
|
+
* UploadServer service interface.
|
|
2457
|
+
*
|
|
2458
|
+
* This is the core upload handling service that provides all file upload operations.
|
|
2459
|
+
* It manages upload lifecycle, resumable uploads, progress tracking, and storage integration.
|
|
2460
|
+
*
|
|
2461
|
+
* All operations return Effect types for composable, type-safe error handling.
|
|
2462
|
+
*
|
|
2463
|
+
* @property createUpload - Initiates a new upload and returns metadata
|
|
2464
|
+
* @property uploadChunk - Uploads a chunk of data for an existing upload
|
|
2465
|
+
* @property getCapabilities - Returns storage backend capabilities
|
|
2466
|
+
* @property upload - Complete upload in one operation (create + upload data)
|
|
2467
|
+
* @property uploadFromUrl - Uploads a file from a remote URL
|
|
2468
|
+
* @property getUpload - Retrieves upload metadata by ID
|
|
2469
|
+
* @property read - Reads the complete uploaded file data
|
|
2470
|
+
* @property delete - Deletes an upload and its data
|
|
2471
|
+
* @property subscribeToUploadEvents - Subscribes WebSocket to upload progress events
|
|
2472
|
+
* @property unsubscribeFromUploadEvents - Unsubscribes from upload events
|
|
2473
|
+
*
|
|
2474
|
+
* @example
|
|
2475
|
+
* ```typescript
|
|
2476
|
+
* // Basic upload flow
|
|
2477
|
+
* const program = Effect.gen(function* () {
|
|
2478
|
+
* const server = yield* UploadServer;
|
|
2479
|
+
*
|
|
2480
|
+
* // 1. Create upload
|
|
2481
|
+
* const inputFile: InputFile = {
|
|
2482
|
+
* storageId: "s3-production",
|
|
2483
|
+
* size: 1024000,
|
|
2484
|
+
* type: "image/jpeg",
|
|
2485
|
+
* fileName: "photo.jpg"
|
|
2486
|
+
* };
|
|
2487
|
+
* const upload = yield* server.createUpload(inputFile, "client123");
|
|
2488
|
+
*
|
|
2489
|
+
* // 2. Upload chunks
|
|
2490
|
+
* const chunk = new ReadableStream(...);
|
|
2491
|
+
* const updated = yield* server.uploadChunk(upload.id, "client123", chunk);
|
|
2492
|
+
*
|
|
2493
|
+
* // 3. Read the uploaded file
|
|
2494
|
+
* const data = yield* server.read(upload.id, "client123");
|
|
2495
|
+
*
|
|
2496
|
+
* return upload;
|
|
2497
|
+
* });
|
|
2498
|
+
*
|
|
2499
|
+
* // Upload with WebSocket progress tracking
|
|
2500
|
+
* const uploadWithProgress = Effect.gen(function* () {
|
|
2501
|
+
* const server = yield* UploadServer;
|
|
2502
|
+
*
|
|
2503
|
+
* // Subscribe to progress events
|
|
2504
|
+
* yield* server.subscribeToUploadEvents(uploadId, websocket);
|
|
2505
|
+
*
|
|
2506
|
+
* // Upload (events will be emitted automatically)
|
|
2507
|
+
* const result = yield* server.upload(inputFile, clientId, stream);
|
|
2508
|
+
*
|
|
2509
|
+
* // Unsubscribe when done
|
|
2510
|
+
* yield* server.unsubscribeFromUploadEvents(uploadId);
|
|
2511
|
+
*
|
|
2512
|
+
* return result;
|
|
2513
|
+
* });
|
|
2514
|
+
*
|
|
2515
|
+
* // Upload from URL
|
|
2516
|
+
* const urlUpload = Effect.gen(function* () {
|
|
2517
|
+
* const server = yield* UploadServer;
|
|
2518
|
+
*
|
|
2519
|
+
* const inputFile: InputFile = {
|
|
2520
|
+
* storageId: "s3-production",
|
|
2521
|
+
* size: 0, // Unknown initially
|
|
2522
|
+
* type: "image/png",
|
|
2523
|
+
* fileName: "remote-image.png"
|
|
2524
|
+
* };
|
|
2525
|
+
*
|
|
2526
|
+
* const upload = yield* server.uploadFromUrl(
|
|
2527
|
+
* inputFile,
|
|
2528
|
+
* "client123",
|
|
2529
|
+
* "https://example.com/image.png"
|
|
2530
|
+
* );
|
|
2531
|
+
*
|
|
2532
|
+
* return upload;
|
|
2533
|
+
* });
|
|
2534
|
+
* ```
|
|
2535
|
+
*/
|
|
2536
|
+
type UploadServerShape = {
|
|
2537
|
+
createUpload: (inputFile: InputFile, clientId: string | null) => Effect.Effect<UploadFile, UploadistaError$1>;
|
|
2538
|
+
uploadChunk: (uploadId: string, clientId: string | null, chunk: ReadableStream) => Effect.Effect<UploadFile, UploadistaError$1>;
|
|
2539
|
+
getCapabilities: (storageId: string, clientId: string | null) => Effect.Effect<DataStoreCapabilities, UploadistaError$1>;
|
|
2540
|
+
upload: (file: InputFile, clientId: string | null, stream: ReadableStream) => Effect.Effect<UploadFile, UploadistaError$1>;
|
|
2541
|
+
uploadFromUrl: (inputFile: InputFile, clientId: string | null, url: string) => Effect.Effect<UploadFile, UploadistaError$1>;
|
|
2542
|
+
getUpload: (uploadId: string) => Effect.Effect<UploadFile, UploadistaError$1>;
|
|
2543
|
+
read: (uploadId: string, clientId: string | null) => Effect.Effect<Uint8Array, UploadistaError$1>;
|
|
2544
|
+
delete: (uploadId: string, clientId: string | null) => Effect.Effect<void, UploadistaError$1>;
|
|
2545
|
+
subscribeToUploadEvents: (uploadId: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError$1>;
|
|
2546
|
+
unsubscribeFromUploadEvents: (uploadId: string) => Effect.Effect<void, UploadistaError$1>;
|
|
2547
|
+
};
|
|
2548
|
+
declare const UploadServer_base: Context.TagClass<UploadServer, "UploadServer", UploadServerShape>;
|
|
2549
|
+
/**
|
|
2550
|
+
* Effect-TS context tag for the UploadServer service.
|
|
2551
|
+
*
|
|
2552
|
+
* Use this tag to access the UploadServer in an Effect context.
|
|
2553
|
+
* The server must be provided via a Layer or dependency injection.
|
|
2554
|
+
*
|
|
2555
|
+
* @example
|
|
2556
|
+
* ```typescript
|
|
2557
|
+
* // Access UploadServer in an Effect
|
|
2558
|
+
* const uploadEffect = Effect.gen(function* () {
|
|
2559
|
+
* const server = yield* UploadServer;
|
|
2560
|
+
* const upload = yield* server.createUpload(inputFile, clientId);
|
|
2561
|
+
* return upload;
|
|
2562
|
+
* });
|
|
2563
|
+
*
|
|
2564
|
+
* // Provide UploadServer layer
|
|
2565
|
+
* const program = uploadEffect.pipe(
|
|
2566
|
+
* Effect.provide(uploadServer),
|
|
2567
|
+
* Effect.provide(uploadFileKvStore),
|
|
2568
|
+
* Effect.provide(dataStoreLayer),
|
|
2569
|
+
* Effect.provide(eventEmitterLayer)
|
|
2570
|
+
* );
|
|
2571
|
+
* ```
|
|
2572
|
+
*/
|
|
2573
|
+
declare class UploadServer extends UploadServer_base {}
|
|
2574
|
+
/**
|
|
2575
|
+
* Creates the UploadServer implementation.
|
|
2576
|
+
*
|
|
2577
|
+
* This function constructs the UploadServer service by composing all required
|
|
2578
|
+
* dependencies (KV store, data stores, event emitter, ID generator). It implements
|
|
2579
|
+
* all upload operations defined in UploadServerShape.
|
|
2580
|
+
*
|
|
2581
|
+
* The server automatically handles:
|
|
2582
|
+
* - Upload lifecycle management (create, resume, complete)
|
|
2583
|
+
* - Progress tracking and event emission
|
|
2584
|
+
* - Storage backend routing based on storageId
|
|
2585
|
+
* - Error handling with proper UploadistaError types
|
|
2586
|
+
*
|
|
2587
|
+
* @returns An Effect that yields the UploadServerShape implementation
|
|
2588
|
+
*
|
|
2589
|
+
* @example
|
|
2590
|
+
* ```typescript
|
|
2591
|
+
* // Create a custom UploadServer layer
|
|
2592
|
+
* const myUploadServer = Layer.effect(
|
|
2593
|
+
* UploadServer,
|
|
2594
|
+
* createUploadServer()
|
|
2595
|
+
* );
|
|
2596
|
+
*
|
|
2597
|
+
* // Use in a program
|
|
2598
|
+
* const program = Effect.gen(function* () {
|
|
2599
|
+
* const server = yield* UploadServer;
|
|
2600
|
+
* // Use server operations...
|
|
2601
|
+
* }).pipe(Effect.provide(myUploadServer));
|
|
2602
|
+
* ```
|
|
2603
|
+
*/
|
|
2604
|
+
declare function createUploadServer(): Effect.Effect<{
|
|
2605
|
+
upload: (inputFile: InputFile, clientId: string | null, stream: ReadableStream) => Effect.Effect<UploadFile, UploadistaError$1, never>;
|
|
2606
|
+
uploadFromUrl: (inputFile: InputFile, clientId: string | null, url: string) => Effect.Effect<UploadFile, UploadistaError$1, never>;
|
|
2607
|
+
createUpload: (inputFile: InputFile, clientId: string | null) => Effect.Effect<UploadFile, UploadistaError$1, never>;
|
|
2608
|
+
uploadChunk: (uploadId: string, clientId: string | null, chunk: ReadableStream) => Effect.Effect<UploadFile, UploadistaError$1, never>;
|
|
2609
|
+
getUpload: (uploadId: string) => Effect.Effect<UploadFile, UploadistaError$1, never>;
|
|
2610
|
+
read: (uploadId: string, clientId: string | null) => Effect.Effect<Uint8Array<ArrayBufferLike>, UploadistaError$1, never>;
|
|
2611
|
+
delete: (uploadId: string, clientId: string | null) => Effect.Effect<void, UploadistaError$1, never>;
|
|
2612
|
+
getCapabilities: (storageId: string, clientId: string | null) => Effect.Effect<DataStoreCapabilities, UploadistaError$1, never>;
|
|
2613
|
+
subscribeToUploadEvents: (uploadId: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError$1, never>;
|
|
2614
|
+
unsubscribeFromUploadEvents: (uploadId: string) => Effect.Effect<void, UploadistaError$1, never>;
|
|
2615
|
+
}, never, GenerateId | UploadFileDataStores | UploadFileKVStore | UploadEventEmitter>;
|
|
2616
|
+
/**
|
|
2617
|
+
* Pre-built UploadServer Effect Layer.
|
|
2618
|
+
*
|
|
2619
|
+
* This layer provides a ready-to-use UploadServer implementation that can be
|
|
2620
|
+
* composed with other layers to build a complete upload system.
|
|
2621
|
+
*
|
|
2622
|
+
* Required dependencies:
|
|
2623
|
+
* - UploadFileKVStore: For storing upload metadata
|
|
2624
|
+
* - UploadFileDataStores: For routing to storage backends
|
|
2625
|
+
* - UploadEventEmitter: For progress events
|
|
2626
|
+
* - GenerateId: For creating upload IDs
|
|
2627
|
+
*
|
|
2628
|
+
* @example
|
|
2629
|
+
* ```typescript
|
|
2630
|
+
* // Compose a complete upload system
|
|
2631
|
+
* const fullUploadSystem = Layer.mergeAll(
|
|
2632
|
+
* uploadServer,
|
|
2633
|
+
* uploadFileKvStore,
|
|
2634
|
+
* dataStoreLayer,
|
|
2635
|
+
* uploadEventEmitter,
|
|
2636
|
+
* generateIdLayer
|
|
2637
|
+
* );
|
|
2638
|
+
*
|
|
2639
|
+
* // Use in application
|
|
2640
|
+
* const app = Effect.gen(function* () {
|
|
2641
|
+
* const server = yield* UploadServer;
|
|
2642
|
+
* // Perform uploads...
|
|
2643
|
+
* }).pipe(Effect.provide(fullUploadSystem));
|
|
2644
|
+
* ```
|
|
2645
|
+
*/
|
|
2646
|
+
declare const uploadServer: Layer.Layer<UploadServer, never, GenerateId | UploadFileDataStores | UploadFileKVStore | UploadEventEmitter>;
|
|
2647
|
+
//#endregion
|
|
2648
|
+
//#region src/upload/upload-strategy-negotiator.d.ts
|
|
2649
|
+
/**
|
|
2650
|
+
* Configuration options for upload strategy negotiation.
|
|
2651
|
+
*
|
|
2652
|
+
* @property fileSize - Size of the file to be uploaded in bytes
|
|
2653
|
+
* @property preferredStrategy - Preferred upload strategy (single, parallel, resumable)
|
|
2654
|
+
* @property preferredChunkSize - Preferred chunk size in bytes
|
|
2655
|
+
* @property parallelUploads - Number of parallel upload connections
|
|
2656
|
+
* @property minChunkSizeForParallel - Minimum file size to consider parallel uploads
|
|
2657
|
+
*/
|
|
2658
|
+
type UploadStrategyOptions = {
|
|
2659
|
+
fileSize: number;
|
|
2660
|
+
preferredStrategy?: UploadStrategy;
|
|
2661
|
+
preferredChunkSize?: number;
|
|
2662
|
+
parallelUploads?: number;
|
|
2663
|
+
minChunkSizeForParallel?: number;
|
|
2664
|
+
};
|
|
2665
|
+
/**
|
|
2666
|
+
* Result of upload strategy negotiation.
|
|
2667
|
+
*
|
|
2668
|
+
* @property strategy - The negotiated upload strategy
|
|
2669
|
+
* @property chunkSize - The negotiated chunk size in bytes
|
|
2670
|
+
* @property parallelUploads - The negotiated number of parallel uploads
|
|
2671
|
+
* @property reasoning - Array of reasoning strings explaining the decisions
|
|
2672
|
+
* @property warnings - Array of warning messages about adjustments made
|
|
2673
|
+
*/
|
|
2674
|
+
type NegotiatedStrategy = {
|
|
2675
|
+
strategy: UploadStrategy;
|
|
2676
|
+
chunkSize: number;
|
|
2677
|
+
parallelUploads: number;
|
|
2678
|
+
reasoning: string[];
|
|
2679
|
+
warnings: string[];
|
|
2680
|
+
};
|
|
2681
|
+
/**
|
|
2682
|
+
* Negotiates the optimal upload strategy based on data store capabilities and file characteristics.
|
|
2683
|
+
*
|
|
2684
|
+
* This class analyzes data store capabilities, file size, and user preferences to determine
|
|
2685
|
+
* the best upload strategy (single, parallel, resumable) and optimal parameters like chunk size
|
|
2686
|
+
* and parallel connection count.
|
|
2687
|
+
*
|
|
2688
|
+
* The negotiator considers:
|
|
2689
|
+
* - Data store capabilities (parallel uploads, resumable uploads, concatenation)
|
|
2690
|
+
* - File size and chunk size constraints
|
|
2691
|
+
* - User preferences and requirements
|
|
2692
|
+
* - Performance optimization opportunities
|
|
2693
|
+
*
|
|
2694
|
+
* @example
|
|
2695
|
+
* ```typescript
|
|
2696
|
+
* // Create negotiator for S3 data store
|
|
2697
|
+
* const negotiator = new UploadStrategyNegotiator(
|
|
2698
|
+
* s3Capabilities,
|
|
2699
|
+
* (strategy) => s3Capabilities.supportsStrategy(strategy)
|
|
2700
|
+
* );
|
|
2701
|
+
*
|
|
2702
|
+
* // Negotiate strategy for large file
|
|
2703
|
+
* const result = negotiator.negotiateStrategy({
|
|
2704
|
+
* fileSize: 100_000_000, // 100MB
|
|
2705
|
+
* preferredStrategy: "parallel",
|
|
2706
|
+
* preferredChunkSize: 5_000_000, // 5MB chunks
|
|
2707
|
+
* parallelUploads: 4
|
|
2708
|
+
* });
|
|
2709
|
+
*
|
|
2710
|
+
* console.log(result.strategy); // "parallel"
|
|
2711
|
+
* console.log(result.chunkSize); // 5_000_000
|
|
2712
|
+
* console.log(result.reasoning); // ["Using preferred strategy: parallel", ...]
|
|
2713
|
+
* ```
|
|
2714
|
+
*/
|
|
2715
|
+
declare class UploadStrategyNegotiator {
|
|
2716
|
+
private capabilities;
|
|
2717
|
+
private validateUploadStrategy;
|
|
2718
|
+
/**
|
|
2719
|
+
* Creates a new upload strategy negotiator.
|
|
2720
|
+
*
|
|
2721
|
+
* @param capabilities - Data store capabilities and constraints
|
|
2722
|
+
* @param validateUploadStrategy - Function to validate if a strategy is supported
|
|
2723
|
+
*/
|
|
2724
|
+
constructor(capabilities: DataStoreCapabilities, validateUploadStrategy: (strategy: UploadStrategy) => boolean);
|
|
2725
|
+
/**
|
|
2726
|
+
* Negotiates the optimal upload strategy based on options and data store capabilities.
|
|
2727
|
+
*
|
|
2728
|
+
* This method analyzes the provided options and data store capabilities to determine
|
|
2729
|
+
* the best upload strategy, chunk size, and parallel upload settings. It considers
|
|
2730
|
+
* user preferences, file size, and data store constraints to make optimal decisions.
|
|
2731
|
+
*
|
|
2732
|
+
* The negotiation process:
|
|
2733
|
+
* 1. Validates preferred strategy against data store capabilities
|
|
2734
|
+
* 2. Automatically selects strategy based on file size and capabilities
|
|
2735
|
+
* 3. Adjusts chunk size to fit within data store constraints
|
|
2736
|
+
* 4. Validates parallel upload settings
|
|
2737
|
+
* 5. Ensures final strategy is supported by the data store
|
|
2738
|
+
*
|
|
2739
|
+
* @param options - Upload strategy options including file size and preferences
|
|
2740
|
+
* @returns Negotiated strategy with reasoning and warnings
|
|
2741
|
+
*
|
|
2742
|
+
* @example
|
|
2743
|
+
* ```typescript
|
|
2744
|
+
* const result = negotiator.negotiateStrategy({
|
|
2745
|
+
* fileSize: 50_000_000, // 50MB
|
|
2746
|
+
* preferredStrategy: "parallel",
|
|
2747
|
+
* preferredChunkSize: 5_000_000, // 5MB
|
|
2748
|
+
* parallelUploads: 3
|
|
2749
|
+
* });
|
|
2750
|
+
*
|
|
2751
|
+
* console.log(result.strategy); // "parallel"
|
|
2752
|
+
* console.log(result.chunkSize); // 5_000_000
|
|
2753
|
+
* console.log(result.parallelUploads); // 3
|
|
2754
|
+
* console.log(result.reasoning); // ["Using preferred strategy: parallel", ...]
|
|
2755
|
+
* console.log(result.warnings); // [] (no warnings)
|
|
2756
|
+
* ```
|
|
2757
|
+
*/
|
|
2758
|
+
negotiateStrategy(options: UploadStrategyOptions): NegotiatedStrategy;
|
|
2759
|
+
/**
|
|
2760
|
+
* Gets the data store capabilities used by this negotiator.
|
|
2761
|
+
*
|
|
2762
|
+
* @returns The data store capabilities and constraints
|
|
2763
|
+
*/
|
|
2764
|
+
getDataStoreCapabilities(): DataStoreCapabilities;
|
|
2765
|
+
/**
|
|
2766
|
+
* Validates upload strategy configuration against data store capabilities.
|
|
2767
|
+
*
|
|
2768
|
+
* This method checks if the provided configuration is valid for the current
|
|
2769
|
+
* data store capabilities without performing the actual negotiation. It's
|
|
2770
|
+
* useful for pre-validation before attempting to negotiate a strategy.
|
|
2771
|
+
*
|
|
2772
|
+
* @param options - Upload strategy options to validate
|
|
2773
|
+
* @returns Validation result with validity flag and error messages
|
|
2774
|
+
*
|
|
2775
|
+
* @example
|
|
2776
|
+
* ```typescript
|
|
2777
|
+
* const validation = negotiator.validateConfiguration({
|
|
2778
|
+
* fileSize: 10_000_000,
|
|
2779
|
+
* preferredStrategy: "parallel",
|
|
2780
|
+
* preferredChunkSize: 1_000_000,
|
|
2781
|
+
* parallelUploads: 5
|
|
2782
|
+
* });
|
|
2783
|
+
*
|
|
2784
|
+
* if (!validation.valid) {
|
|
2785
|
+
* console.log("Configuration errors:", validation.errors);
|
|
2786
|
+
* // Handle validation errors
|
|
2787
|
+
* }
|
|
2788
|
+
* ```
|
|
2789
|
+
*/
|
|
2790
|
+
validateConfiguration(options: UploadStrategyOptions): {
|
|
2791
|
+
valid: boolean;
|
|
2792
|
+
errors: string[];
|
|
2793
|
+
};
|
|
2794
|
+
}
|
|
2795
|
+
//#endregion
|
|
2796
|
+
//#region src/flow/types/flow-job.d.ts
|
|
2797
|
+
/**
|
|
2798
|
+
* Flow job tracking and state management types.
|
|
2799
|
+
*
|
|
2800
|
+
* A FlowJob represents a single execution instance of a flow, tracking its progress,
|
|
2801
|
+
* node results, and execution state. Jobs can be paused and resumed, making them
|
|
2802
|
+
* suitable for long-running or interactive flows.
|
|
2803
|
+
*
|
|
2804
|
+
* @module flow/types/flow-job
|
|
2805
|
+
* @see {@link FlowServer} for job management operations
|
|
2806
|
+
*/
|
|
2807
|
+
/**
|
|
2808
|
+
* Status of an individual node within a flow job.
|
|
2809
|
+
*
|
|
2810
|
+
* Node tasks follow this lifecycle:
|
|
2811
|
+
* started → pending → running → (completed | paused | failed)
|
|
2812
|
+
*/
|
|
2813
|
+
type FlowJobTaskStatus = "started" | "pending" | "running" | "completed" | "paused" | "failed";
|
|
2814
|
+
/**
|
|
2815
|
+
* Represents a single node's execution within a flow job.
|
|
2816
|
+
*
|
|
2817
|
+
* Tasks track individual node execution, storing results, errors, and retry information.
|
|
2818
|
+
* They allow monitoring of which nodes have completed and accessing intermediate results.
|
|
2819
|
+
*
|
|
2820
|
+
* @property nodeId - Unique identifier of the node this task represents
|
|
2821
|
+
* @property status - Current execution status of the node
|
|
2822
|
+
* @property result - Output data from the node (if completed successfully)
|
|
2823
|
+
* @property error - Error message if the node failed
|
|
2824
|
+
* @property retryCount - Number of retry attempts made before success or final failure
|
|
2825
|
+
* @property createdAt - When the task was created
|
|
2826
|
+
* @property updatedAt - When the task was last updated
|
|
2827
|
+
*/
|
|
2828
|
+
type FlowJobTask = {
|
|
2829
|
+
nodeId: string;
|
|
2830
|
+
status: FlowJobTaskStatus;
|
|
2831
|
+
result?: unknown;
|
|
2832
|
+
error?: string;
|
|
2833
|
+
retryCount?: number;
|
|
2834
|
+
createdAt: Date;
|
|
2835
|
+
updatedAt: Date;
|
|
2836
|
+
};
|
|
2837
|
+
/**
|
|
2838
|
+
* Represents a flow execution job with full state tracking.
|
|
2839
|
+
*
|
|
2840
|
+
* Jobs are created when a flow is executed and track the entire execution lifecycle.
|
|
2841
|
+
* They store node results, handle paused states, and manage cleanup of intermediate files.
|
|
2842
|
+
*
|
|
2843
|
+
* @property id - Unique job identifier (UUID)
|
|
2844
|
+
* @property flowId - The flow being executed
|
|
2845
|
+
* @property storageId - Storage location for file outputs
|
|
2846
|
+
* @property clientId - Client that initiated the job (for authorization)
|
|
2847
|
+
* @property status - Overall job status
|
|
2848
|
+
* @property createdAt - When the job was created
|
|
2849
|
+
* @property updatedAt - When the job was last updated
|
|
2850
|
+
* @property tasks - Array of node execution tasks
|
|
2851
|
+
* @property error - Error message if the job failed
|
|
2852
|
+
* @property endedAt - When the job completed or failed
|
|
2853
|
+
* @property result - Final output from the flow (only set when completed)
|
|
2854
|
+
* @property pausedAt - Node ID where execution is paused (if applicable)
|
|
2855
|
+
* @property executionState - State needed to resume a paused flow
|
|
2856
|
+
* @property intermediateFiles - File IDs to cleanup after completion
|
|
2857
|
+
*
|
|
2858
|
+
* @remarks
|
|
2859
|
+
* - Jobs can be paused at nodes that return `{ type: "waiting" }`
|
|
2860
|
+
* - Paused jobs store execution state and can be resumed with new data
|
|
2861
|
+
* - Intermediate files from non-output nodes are automatically cleaned up
|
|
2862
|
+
* - Tasks are updated as nodes progress through their lifecycle
|
|
2863
|
+
*
|
|
2864
|
+
* @example
|
|
2865
|
+
* ```typescript
|
|
2866
|
+
* // Create and monitor a job
|
|
2867
|
+
* const job = yield* flowServer.runFlow({
|
|
2868
|
+
* flowId: "image-pipeline",
|
|
2869
|
+
* storageId: "storage-1",
|
|
2870
|
+
* inputs: { input: myFile }
|
|
2871
|
+
* });
|
|
2872
|
+
*
|
|
2873
|
+
* // Poll for status
|
|
2874
|
+
* const status = yield* flowServer.getJobStatus(job.id);
|
|
2875
|
+
* if (status.status === "completed") {
|
|
2876
|
+
* console.log("Final result:", status.result);
|
|
2877
|
+
* } else if (status.status === "paused") {
|
|
2878
|
+
* // Resume with additional data
|
|
2879
|
+
* yield* flowServer.continueFlow({
|
|
2880
|
+
* jobId: job.id,
|
|
2881
|
+
* nodeId: status.pausedAt,
|
|
2882
|
+
* newData: additionalChunk
|
|
2883
|
+
* });
|
|
2884
|
+
* }
|
|
2885
|
+
* ```
|
|
2886
|
+
*/
|
|
2887
|
+
type FlowJob = {
|
|
2888
|
+
id: string;
|
|
2889
|
+
flowId: string;
|
|
2890
|
+
storageId: string;
|
|
2891
|
+
clientId: string | null;
|
|
2892
|
+
status: FlowJobStatus;
|
|
2893
|
+
createdAt: Date;
|
|
2894
|
+
updatedAt: Date;
|
|
2895
|
+
tasks: FlowJobTask[];
|
|
2896
|
+
error?: string;
|
|
2897
|
+
endedAt?: Date;
|
|
2898
|
+
result?: unknown;
|
|
2899
|
+
pausedAt?: string;
|
|
2900
|
+
executionState?: {
|
|
2901
|
+
executionOrder: string[];
|
|
2902
|
+
currentIndex: number;
|
|
2903
|
+
inputs: Record<string, unknown>;
|
|
2904
|
+
};
|
|
2905
|
+
intermediateFiles?: string[];
|
|
2906
|
+
};
|
|
2907
|
+
/**
|
|
2908
|
+
* Overall status of a flow job.
|
|
2909
|
+
*
|
|
2910
|
+
* Job lifecycle: started → running → (completed | failed)
|
|
2911
|
+
* Or with pauses: started → running → paused → running → (completed | failed)
|
|
2912
|
+
*/
|
|
2913
|
+
type FlowJobStatus = "pending" | "running" | "completed" | "failed" | "started" | "paused";
|
|
2914
|
+
//#endregion
|
|
2915
|
+
//#region src/flow/flow-server.d.ts
|
|
2916
|
+
/**
|
|
2917
|
+
* Flow provider interface that applications must implement.
|
|
2918
|
+
*
|
|
2919
|
+
* This interface defines how the FlowServer retrieves flow definitions.
|
|
2920
|
+
* Applications provide their own implementation to load flows from a database,
|
|
2921
|
+
* configuration files, or any other source.
|
|
2922
|
+
*
|
|
2923
|
+
* @template TRequirements - Additional Effect requirements for flow execution
|
|
2924
|
+
*
|
|
2925
|
+
* @property getFlow - Retrieves a flow definition by ID with authorization check
|
|
2926
|
+
*
|
|
2927
|
+
* @example
|
|
2928
|
+
* ```typescript
|
|
2929
|
+
* // Implement a flow provider from database
|
|
2930
|
+
* const dbFlowProvider: FlowProviderShape = {
|
|
2931
|
+
* getFlow: (flowId, clientId) => Effect.gen(function* () {
|
|
2932
|
+
* // Load flow from database
|
|
2933
|
+
* const flowData = yield* db.getFlow(flowId);
|
|
2934
|
+
*
|
|
2935
|
+
* // Check authorization
|
|
2936
|
+
* if (flowData.ownerId !== clientId) {
|
|
2937
|
+
* return yield* Effect.fail(
|
|
2938
|
+
* UploadistaError.fromCode("FLOW_NOT_AUTHORIZED")
|
|
2939
|
+
* );
|
|
2940
|
+
* }
|
|
2941
|
+
*
|
|
2942
|
+
* // Create flow instance
|
|
2943
|
+
* return createFlow(flowData);
|
|
2944
|
+
* })
|
|
2945
|
+
* };
|
|
2946
|
+
*
|
|
2947
|
+
* // Provide to FlowServer
|
|
2948
|
+
* const flowProviderLayer = Layer.succeed(FlowProvider, dbFlowProvider);
|
|
2949
|
+
* ```
|
|
2950
|
+
*/
|
|
2951
|
+
type FlowProviderShape<TRequirements$1 = any> = {
|
|
2952
|
+
getFlow: (flowId: string, clientId: string | null) => Effect.Effect<Flow<any, any, TRequirements$1>, UploadistaError$1>;
|
|
2953
|
+
};
|
|
2954
|
+
declare const FlowProvider_base: Context.TagClass<FlowProvider, "FlowProvider", FlowProviderShape<any>>;
|
|
2955
|
+
/**
|
|
2956
|
+
* Effect-TS context tag for the FlowProvider service.
|
|
2957
|
+
*
|
|
2958
|
+
* Applications must provide an implementation of FlowProviderShape
|
|
2959
|
+
* to enable the FlowServer to retrieve flow definitions.
|
|
2960
|
+
*
|
|
2961
|
+
* @example
|
|
2962
|
+
* ```typescript
|
|
2963
|
+
* // Access FlowProvider in an Effect
|
|
2964
|
+
* const effect = Effect.gen(function* () {
|
|
2965
|
+
* const provider = yield* FlowProvider;
|
|
2966
|
+
* const flow = yield* provider.getFlow("flow123", "client456");
|
|
2967
|
+
* return flow;
|
|
2968
|
+
* });
|
|
2969
|
+
* ```
|
|
2970
|
+
*/
|
|
2971
|
+
declare class FlowProvider extends FlowProvider_base {}
|
|
2972
|
+
/**
|
|
2973
|
+
* FlowServer service interface.
|
|
2974
|
+
*
|
|
2975
|
+
* This is the core flow processing service that executes DAG-based file processing pipelines.
|
|
2976
|
+
* It manages flow execution, job tracking, node processing, pause/resume functionality,
|
|
2977
|
+
* and real-time event broadcasting.
|
|
2978
|
+
*
|
|
2979
|
+
* All operations return Effect types for composable, type-safe error handling.
|
|
2980
|
+
*
|
|
2981
|
+
* @property getFlow - Retrieves a flow definition by ID
|
|
2982
|
+
* @property getFlowData - Retrieves flow metadata (nodes, edges) without full flow instance
|
|
2983
|
+
* @property runFlow - Starts a new flow execution and returns immediately with job ID
|
|
2984
|
+
* @property continueFlow - Resumes a paused flow with new data for a specific node
|
|
2985
|
+
* @property getJobStatus - Retrieves current status and results of a flow job
|
|
2986
|
+
* @property subscribeToFlowEvents - Subscribes WebSocket to flow execution events
|
|
2987
|
+
* @property unsubscribeFromFlowEvents - Unsubscribes from flow events
|
|
2988
|
+
*
|
|
2989
|
+
* @example
|
|
2990
|
+
* ```typescript
|
|
2991
|
+
* // Execute a flow
|
|
2992
|
+
* const program = Effect.gen(function* () {
|
|
2993
|
+
* const server = yield* FlowServer;
|
|
2994
|
+
*
|
|
2995
|
+
* // Start flow execution (returns immediately)
|
|
2996
|
+
* const job = yield* server.runFlow({
|
|
2997
|
+
* flowId: "resize-optimize",
|
|
2998
|
+
* storageId: "s3-production",
|
|
2999
|
+
* clientId: "client123",
|
|
3000
|
+
* inputs: {
|
|
3001
|
+
* input_1: { uploadId: "upload_abc123" }
|
|
3002
|
+
* }
|
|
3003
|
+
* });
|
|
3004
|
+
*
|
|
3005
|
+
* // Subscribe to events
|
|
3006
|
+
* yield* server.subscribeToFlowEvents(job.id, websocket);
|
|
3007
|
+
*
|
|
3008
|
+
* // Poll for status
|
|
3009
|
+
* const status = yield* server.getJobStatus(job.id);
|
|
3010
|
+
* console.log(status.status); // "running", "paused", "completed", or "failed"
|
|
3011
|
+
*
|
|
3012
|
+
* return job;
|
|
3013
|
+
* });
|
|
3014
|
+
*
|
|
3015
|
+
* // Resume a paused flow
|
|
3016
|
+
* const resume = Effect.gen(function* () {
|
|
3017
|
+
* const server = yield* FlowServer;
|
|
3018
|
+
*
|
|
3019
|
+
* // Flow paused waiting for user input at node "approval_1"
|
|
3020
|
+
* const job = yield* server.continueFlow({
|
|
3021
|
+
* jobId: "job123",
|
|
3022
|
+
* nodeId: "approval_1",
|
|
3023
|
+
* newData: { approved: true },
|
|
3024
|
+
* clientId: "client123"
|
|
3025
|
+
* });
|
|
3026
|
+
*
|
|
3027
|
+
* return job;
|
|
3028
|
+
* });
|
|
3029
|
+
*
|
|
3030
|
+
* // Check flow structure before execution
|
|
3031
|
+
* const inspect = Effect.gen(function* () {
|
|
3032
|
+
* const server = yield* FlowServer;
|
|
3033
|
+
*
|
|
3034
|
+
* const flowData = yield* server.getFlowData("resize-optimize", "client123");
|
|
3035
|
+
* console.log("Nodes:", flowData.nodes);
|
|
3036
|
+
* console.log("Edges:", flowData.edges);
|
|
3037
|
+
*
|
|
3038
|
+
* return flowData;
|
|
3039
|
+
* });
|
|
3040
|
+
* ```
|
|
3041
|
+
*/
|
|
3042
|
+
type FlowServerShape = {
|
|
3043
|
+
getFlow: <TRequirements>(flowId: string, clientId: string | null) => Effect.Effect<Flow<any, any, TRequirements>, UploadistaError$1>;
|
|
3044
|
+
getFlowData: (flowId: string, clientId: string | null) => Effect.Effect<FlowData, UploadistaError$1>;
|
|
3045
|
+
runFlow: <TRequirements>({
|
|
3046
|
+
flowId,
|
|
3047
|
+
storageId,
|
|
3048
|
+
clientId,
|
|
3049
|
+
inputs
|
|
3050
|
+
}: {
|
|
3051
|
+
flowId: string;
|
|
3052
|
+
storageId: string;
|
|
3053
|
+
clientId: string | null;
|
|
3054
|
+
inputs: any;
|
|
3055
|
+
}) => Effect.Effect<FlowJob, UploadistaError$1, TRequirements>;
|
|
3056
|
+
continueFlow: <TRequirements>({
|
|
3057
|
+
jobId,
|
|
3058
|
+
nodeId,
|
|
3059
|
+
newData,
|
|
3060
|
+
clientId
|
|
3061
|
+
}: {
|
|
3062
|
+
jobId: string;
|
|
3063
|
+
nodeId: string;
|
|
3064
|
+
newData: unknown;
|
|
3065
|
+
clientId: string | null;
|
|
3066
|
+
}) => Effect.Effect<FlowJob, UploadistaError$1, TRequirements>;
|
|
3067
|
+
getJobStatus: (jobId: string) => Effect.Effect<FlowJob, UploadistaError$1>;
|
|
3068
|
+
subscribeToFlowEvents: (jobId: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError$1>;
|
|
3069
|
+
unsubscribeFromFlowEvents: (jobId: string) => Effect.Effect<void, UploadistaError$1>;
|
|
3070
|
+
};
|
|
3071
|
+
declare const FlowServer_base: Context.TagClass<FlowServer, "FlowServer", FlowServerShape>;
|
|
3072
|
+
/**
|
|
3073
|
+
* Effect-TS context tag for the FlowServer service.
|
|
3074
|
+
*
|
|
3075
|
+
* Use this tag to access the FlowServer in an Effect context.
|
|
3076
|
+
* The server must be provided via a Layer or dependency injection.
|
|
3077
|
+
*
|
|
3078
|
+
* @example
|
|
3079
|
+
* ```typescript
|
|
3080
|
+
* // Access FlowServer in an Effect
|
|
3081
|
+
* const flowEffect = Effect.gen(function* () {
|
|
3082
|
+
* const server = yield* FlowServer;
|
|
3083
|
+
* const job = yield* server.runFlow({
|
|
3084
|
+
* flowId: "my-flow",
|
|
3085
|
+
* storageId: "s3",
|
|
3086
|
+
* clientId: null,
|
|
3087
|
+
* inputs: {}
|
|
3088
|
+
* });
|
|
3089
|
+
* return job;
|
|
3090
|
+
* });
|
|
3091
|
+
*
|
|
3092
|
+
* // Provide FlowServer layer
|
|
3093
|
+
* const program = flowEffect.pipe(
|
|
3094
|
+
* Effect.provide(flowServer),
|
|
3095
|
+
* Effect.provide(flowProviderLayer),
|
|
3096
|
+
* Effect.provide(flowJobKvStore)
|
|
3097
|
+
* );
|
|
3098
|
+
* ```
|
|
3099
|
+
*/
|
|
3100
|
+
declare class FlowServer extends FlowServer_base {}
|
|
3101
|
+
/**
|
|
3102
|
+
* Legacy configuration options for FlowServer.
|
|
3103
|
+
*
|
|
3104
|
+
* @deprecated Use Effect Layers and FlowProvider instead.
|
|
3105
|
+
* This type is kept for backward compatibility.
|
|
3106
|
+
*
|
|
3107
|
+
* @property getFlow - Function to retrieve flow definitions
|
|
3108
|
+
* @property kvStore - KV store for flow job metadata
|
|
3109
|
+
*/
|
|
3110
|
+
type FlowServerOptions = {
|
|
3111
|
+
getFlow: <TRequirements>({
|
|
3112
|
+
flowId,
|
|
3113
|
+
storageId
|
|
3114
|
+
}: {
|
|
3115
|
+
flowId: string;
|
|
3116
|
+
storageId: string;
|
|
3117
|
+
}) => Promise<Flow<any, any, TRequirements>>;
|
|
3118
|
+
kvStore: KvStore<FlowJob>;
|
|
3119
|
+
};
|
|
3120
|
+
declare function createFlowServer(): Effect.Effect<{
|
|
3121
|
+
getFlow: <TRequirements>(flowId: string, clientId: string | null) => Effect.Effect<Flow<any, any, any>, UploadistaError$1, never>;
|
|
3122
|
+
getFlowData: (flowId: string, clientId: string | null) => Effect.Effect<FlowData, UploadistaError$1, never>;
|
|
3123
|
+
runFlow: ({
|
|
3124
|
+
flowId,
|
|
3125
|
+
storageId,
|
|
3126
|
+
clientId,
|
|
3127
|
+
inputs
|
|
3128
|
+
}: {
|
|
3129
|
+
flowId: string;
|
|
3130
|
+
storageId: string;
|
|
3131
|
+
clientId: string | null;
|
|
3132
|
+
inputs: unknown;
|
|
3133
|
+
}) => Effect.Effect<FlowJob, UploadistaError$1, any>;
|
|
3134
|
+
getJobStatus: (jobId: string) => Effect.Effect<FlowJob, UploadistaError$1, never>;
|
|
3135
|
+
continueFlow: ({
|
|
3136
|
+
jobId,
|
|
3137
|
+
nodeId,
|
|
3138
|
+
newData,
|
|
3139
|
+
clientId
|
|
3140
|
+
}: {
|
|
3141
|
+
jobId: string;
|
|
3142
|
+
nodeId: string;
|
|
3143
|
+
newData: unknown;
|
|
3144
|
+
clientId: string | null;
|
|
3145
|
+
}) => Effect.Effect<FlowJob, UploadistaError$1, any>;
|
|
3146
|
+
subscribeToFlowEvents: (jobId: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError$1, never>;
|
|
3147
|
+
unsubscribeFromFlowEvents: (jobId: string) => Effect.Effect<void, UploadistaError$1, never>;
|
|
3148
|
+
}, never, FlowEventEmitter | FlowJobKVStore | UploadServer | FlowProvider>;
|
|
3149
|
+
declare const flowServer: Layer.Layer<FlowServer, never, FlowEventEmitter | FlowJobKVStore | UploadServer | FlowProvider>;
|
|
3150
|
+
type FlowServerLayer = typeof flowServer;
|
|
3151
|
+
//#endregion
|
|
3152
|
+
//#region src/flow/nodes/input-node.d.ts
|
|
3153
|
+
/**
|
|
3154
|
+
* Union schema for all input operations.
|
|
3155
|
+
* Defines the possible input data structures for the input node.
|
|
3156
|
+
*/
|
|
3157
|
+
declare const inputDataSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
3158
|
+
operation: z.ZodLiteral<"init">;
|
|
3159
|
+
storageId: z.ZodString;
|
|
3160
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
3161
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
3162
|
+
operation: z.ZodLiteral<"finalize">;
|
|
3163
|
+
uploadId: z.ZodString;
|
|
3164
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
3165
|
+
operation: z.ZodLiteral<"url">;
|
|
3166
|
+
url: z.ZodString;
|
|
3167
|
+
storageId: z.ZodOptional<z.ZodString>;
|
|
3168
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
3169
|
+
}, z.core.$strip>]>;
|
|
3170
|
+
/**
|
|
3171
|
+
* Type representing the input data for the input node.
|
|
3172
|
+
* Can be one of three operation types: init, finalize, or url.
|
|
3173
|
+
*/
|
|
3174
|
+
type InputData = z.infer<typeof inputDataSchema>;
|
|
3175
|
+
/**
|
|
3176
|
+
* Schema for input node filtering parameters.
|
|
3177
|
+
* Defines validation rules for incoming files.
|
|
3178
|
+
*/
|
|
3179
|
+
declare const inputNodeParamsSchema: z.ZodObject<{
|
|
3180
|
+
allowedMimeTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
3181
|
+
minSize: z.ZodOptional<z.ZodNumber>;
|
|
3182
|
+
maxSize: z.ZodOptional<z.ZodNumber>;
|
|
3183
|
+
}, z.core.$strip>;
|
|
3184
|
+
/**
|
|
3185
|
+
* Parameters for configuring input node validation.
|
|
3186
|
+
* Controls which files are accepted based on type and size constraints.
|
|
3187
|
+
*/
|
|
3188
|
+
type InputNodeParams = z.infer<typeof inputNodeParamsSchema>;
|
|
3189
|
+
/**
|
|
3190
|
+
* Creates an input node for handling file input through multiple methods.
|
|
3191
|
+
*
|
|
3192
|
+
* The input node supports three operation types:
|
|
3193
|
+
* - `init`: Initialize a streaming upload session
|
|
3194
|
+
* - `finalize`: Complete a streaming upload after all chunks are uploaded
|
|
3195
|
+
* - `url`: Fetch a file directly from a URL
|
|
3196
|
+
*
|
|
3197
|
+
* @param id - Unique identifier for the node
|
|
3198
|
+
* @param params - Optional validation parameters for filtering incoming files
|
|
3199
|
+
* @returns An Effect that creates a flow node configured for file input
|
|
3200
|
+
*
|
|
3201
|
+
* @example
|
|
3202
|
+
* ```typescript
|
|
3203
|
+
* // Create input node with validation
|
|
3204
|
+
* const inputNode = yield* createInputNode("file-input", {
|
|
3205
|
+
* allowedMimeTypes: ["image/*", "application/pdf"],
|
|
3206
|
+
* maxSize: 10 * 1024 * 1024, // 10MB
|
|
3207
|
+
* });
|
|
3208
|
+
*
|
|
3209
|
+
* // Create input node without validation
|
|
3210
|
+
* const openInputNode = yield* createInputNode("open-input");
|
|
3211
|
+
* ```
|
|
3212
|
+
*/
|
|
3213
|
+
declare function createInputNode(id: string, params?: InputNodeParams): Effect.Effect<FlowNode<{
|
|
3214
|
+
operation: "init";
|
|
3215
|
+
storageId: string;
|
|
3216
|
+
metadata?: Record<string, any> | undefined;
|
|
3217
|
+
} | {
|
|
3218
|
+
operation: "finalize";
|
|
3219
|
+
uploadId: string;
|
|
3220
|
+
} | {
|
|
3221
|
+
operation: "url";
|
|
3222
|
+
url: string;
|
|
3223
|
+
storageId?: string | undefined;
|
|
3224
|
+
metadata?: Record<string, any> | undefined;
|
|
3225
|
+
}, UploadFile, UploadistaError$1>, never, UploadServer>;
|
|
3226
|
+
//#endregion
|
|
3227
|
+
//#region src/flow/nodes/storage-node.d.ts
|
|
3228
|
+
/**
|
|
3229
|
+
* Schema for storage node parameters.
|
|
3230
|
+
* Currently empty but can be extended for storage-specific configuration.
|
|
3231
|
+
*/
|
|
3232
|
+
declare const storageParamsSchema: z.ZodObject<{}, z.core.$strip>;
|
|
3233
|
+
/**
|
|
3234
|
+
* Parameters for the storage node.
|
|
3235
|
+
* Currently no parameters are required, but the schema is available for future extensions.
|
|
3236
|
+
*/
|
|
3237
|
+
type StorageParams = z.infer<typeof storageParamsSchema>;
|
|
3238
|
+
/**
|
|
3239
|
+
* Creates a storage node for storing files in the specified storage.
|
|
3240
|
+
*
|
|
3241
|
+
* The storage node handles the process of:
|
|
3242
|
+
* 1. Reading the input file from the upload server
|
|
3243
|
+
* 2. Checking if the file is already in the target storage
|
|
3244
|
+
* 3. If not, transferring the file to the target storage
|
|
3245
|
+
* 4. Applying optional post-processing
|
|
3246
|
+
* 5. Returning the final stored file
|
|
3247
|
+
*
|
|
3248
|
+
* @param id - Unique identifier for the node
|
|
3249
|
+
* @param postProcessFile - Optional function to process the file after storage
|
|
3250
|
+
* @returns An Effect that creates a flow node configured for file storage
|
|
3251
|
+
*
|
|
3252
|
+
* @example
|
|
3253
|
+
* ```typescript
|
|
3254
|
+
* // Create basic storage node
|
|
3255
|
+
* const storageNode = yield* createStorageNode("store-file");
|
|
3256
|
+
*
|
|
3257
|
+
* // Create storage node with post-processing
|
|
3258
|
+
* const storageWithProcessing = yield* createStorageNode("store-and-process", (file) => {
|
|
3259
|
+
* return Effect.succeed({
|
|
3260
|
+
* ...file,
|
|
3261
|
+
* metadata: { ...file.metadata, processed: true }
|
|
3262
|
+
* });
|
|
3263
|
+
* });
|
|
3264
|
+
* ```
|
|
3265
|
+
*/
|
|
3266
|
+
declare function createStorageNode(id: string, postProcessFile?: (file: UploadFile) => Effect.Effect<UploadFile>): Effect.Effect<FlowNode<{
|
|
3267
|
+
id: string;
|
|
3268
|
+
offset: number;
|
|
3269
|
+
storage: {
|
|
3270
|
+
id: string;
|
|
3271
|
+
type: string;
|
|
3272
|
+
path?: string | undefined;
|
|
3273
|
+
uploadId?: string | undefined;
|
|
3274
|
+
bucket?: string | undefined;
|
|
3275
|
+
};
|
|
3276
|
+
size?: number | undefined;
|
|
3277
|
+
metadata?: Record<string, string | number | boolean> | undefined;
|
|
3278
|
+
creationDate?: string | undefined;
|
|
3279
|
+
url?: string | undefined;
|
|
3280
|
+
sizeIsDeferred?: boolean | undefined;
|
|
3281
|
+
checksum?: string | undefined;
|
|
3282
|
+
checksumAlgorithm?: string | undefined;
|
|
3283
|
+
flow?: {
|
|
3284
|
+
flowId: string;
|
|
3285
|
+
nodeId: string;
|
|
3286
|
+
jobId: string;
|
|
3287
|
+
} | undefined;
|
|
3288
|
+
}, UploadFile, UploadistaError$1>, never, UploadServer>;
|
|
3289
|
+
//#endregion
|
|
3290
|
+
//#region src/flow/nodes/transform-node.d.ts
|
|
3291
|
+
/**
|
|
3292
|
+
* Configuration object for creating a transform node.
|
|
3293
|
+
*/
|
|
3294
|
+
interface TransformNodeConfig {
|
|
3295
|
+
/** Unique identifier for the node */
|
|
3296
|
+
id: string;
|
|
3297
|
+
/** Human-readable name for the node */
|
|
3298
|
+
name: string;
|
|
3299
|
+
/** Description of what the node does */
|
|
3300
|
+
description: string;
|
|
3301
|
+
/** Function that transforms file bytes */
|
|
3302
|
+
transform: (bytes: Uint8Array, file: UploadFile) => Effect.Effect<Uint8Array | {
|
|
3303
|
+
bytes: Uint8Array;
|
|
3304
|
+
type?: string;
|
|
3305
|
+
fileName?: string;
|
|
3306
|
+
}, UploadistaError$1>;
|
|
3307
|
+
}
|
|
3308
|
+
/**
|
|
3309
|
+
* Creates a transform node that handles the common pattern of:
|
|
3310
|
+
* 1. Reading bytes from an UploadFile
|
|
3311
|
+
* 2. Transforming the bytes
|
|
3312
|
+
* 3. Uploading the result as a new UploadFile
|
|
3313
|
+
*
|
|
3314
|
+
* This simplifies nodes that just need to transform file bytes without
|
|
3315
|
+
* worrying about upload server interactions.
|
|
3316
|
+
*
|
|
3317
|
+
* @param config - Configuration object for the transform node
|
|
3318
|
+
* @returns An Effect that creates a flow node configured for file transformation
|
|
3319
|
+
*
|
|
3320
|
+
* @example
|
|
3321
|
+
* ```typescript
|
|
3322
|
+
* // Create an image resize transform node
|
|
3323
|
+
* const resizeNode = yield* createTransformNode({
|
|
3324
|
+
* id: "resize-image",
|
|
3325
|
+
* name: "Resize Image",
|
|
3326
|
+
* description: "Resizes images to specified dimensions",
|
|
3327
|
+
* transform: (bytes, file) => {
|
|
3328
|
+
* // Your transformation logic here
|
|
3329
|
+
* return Effect.succeed(transformedBytes);
|
|
3330
|
+
* }
|
|
3331
|
+
* });
|
|
3332
|
+
*
|
|
3333
|
+
* // Create a transform node that changes file metadata
|
|
3334
|
+
* const metadataTransformNode = yield* createTransformNode({
|
|
3335
|
+
* id: "add-metadata",
|
|
3336
|
+
* name: "Add Metadata",
|
|
3337
|
+
* description: "Adds custom metadata to files",
|
|
3338
|
+
* transform: (bytes, file) => {
|
|
3339
|
+
* return Effect.succeed({
|
|
3340
|
+
* bytes,
|
|
3341
|
+
* type: "application/custom",
|
|
3342
|
+
* fileName: `processed-${file.fileName}`
|
|
3343
|
+
* });
|
|
3344
|
+
* }
|
|
3345
|
+
* });
|
|
3346
|
+
* ```
|
|
3347
|
+
*/
|
|
3348
|
+
declare function createTransformNode({
|
|
3349
|
+
id,
|
|
3350
|
+
name,
|
|
3351
|
+
description,
|
|
3352
|
+
transform
|
|
3353
|
+
}: TransformNodeConfig): Effect.Effect<FlowNode<{
|
|
3354
|
+
id: string;
|
|
3355
|
+
offset: number;
|
|
3356
|
+
storage: {
|
|
3357
|
+
id: string;
|
|
3358
|
+
type: string;
|
|
3359
|
+
path?: string | undefined;
|
|
3360
|
+
uploadId?: string | undefined;
|
|
3361
|
+
bucket?: string | undefined;
|
|
3362
|
+
};
|
|
3363
|
+
size?: number | undefined;
|
|
3364
|
+
metadata?: Record<string, string | number | boolean> | undefined;
|
|
3365
|
+
creationDate?: string | undefined;
|
|
3366
|
+
url?: string | undefined;
|
|
3367
|
+
sizeIsDeferred?: boolean | undefined;
|
|
3368
|
+
checksum?: string | undefined;
|
|
3369
|
+
checksumAlgorithm?: string | undefined;
|
|
3370
|
+
flow?: {
|
|
3371
|
+
flowId: string;
|
|
3372
|
+
nodeId: string;
|
|
3373
|
+
jobId: string;
|
|
3374
|
+
} | undefined;
|
|
3375
|
+
}, UploadFile, UploadistaError$1>, never, UploadServer>;
|
|
3376
|
+
//#endregion
|
|
3377
|
+
//#region src/flow/parallel-scheduler.d.ts
|
|
3378
|
+
/**
|
|
3379
|
+
* Represents a level in the execution hierarchy where all nodes can run in parallel.
|
|
3380
|
+
*
|
|
3381
|
+
* @property level - The execution level (0 = first to execute, higher = later)
|
|
3382
|
+
* @property nodes - Array of node IDs that can execute in parallel at this level
|
|
3383
|
+
*
|
|
3384
|
+
* @example
|
|
3385
|
+
* ```
|
|
3386
|
+
* Level 0: [input_node] (no dependencies)
|
|
3387
|
+
* Level 1: [resize, optimize] (all depend on level 0)
|
|
3388
|
+
* Level 2: [storage] (depends on level 1)
|
|
3389
|
+
* ```
|
|
3390
|
+
*/
|
|
3391
|
+
interface ExecutionLevel {
|
|
3392
|
+
level: number;
|
|
3393
|
+
nodes: string[];
|
|
3394
|
+
}
|
|
3395
|
+
/**
|
|
3396
|
+
* Configuration options for the ParallelScheduler.
|
|
3397
|
+
*
|
|
3398
|
+
* @property maxConcurrency - Maximum number of nodes to execute in parallel (default: 4)
|
|
3399
|
+
* Controls how many nodes run simultaneously within a level
|
|
3400
|
+
*
|
|
3401
|
+
* @example
|
|
3402
|
+
* ```typescript
|
|
3403
|
+
* const scheduler = new ParallelScheduler({ maxConcurrency: 8 });
|
|
3404
|
+
* ```
|
|
3405
|
+
*/
|
|
3406
|
+
interface ParallelSchedulerConfig {
|
|
3407
|
+
maxConcurrency?: number;
|
|
3408
|
+
}
|
|
3409
|
+
/**
|
|
3410
|
+
* Scheduler for executing flow nodes in parallel while respecting dependencies.
|
|
3411
|
+
*
|
|
3412
|
+
* The scheduler performs topological sorting to identify nodes that can run
|
|
3413
|
+
* concurrently, groups them into execution levels, and provides methods to
|
|
3414
|
+
* execute them with controlled concurrency using Effect.
|
|
3415
|
+
*
|
|
3416
|
+
* Key responsibilities:
|
|
3417
|
+
* - Analyze flow dependencies and detect cycles
|
|
3418
|
+
* - Group nodes into parallel execution levels
|
|
3419
|
+
* - Execute levels in parallel with concurrency limits
|
|
3420
|
+
* - Provide utilities to check parallel execution feasibility
|
|
3421
|
+
*/
|
|
3422
|
+
declare class ParallelScheduler {
|
|
3423
|
+
private maxConcurrency;
|
|
3424
|
+
/**
|
|
3425
|
+
* Creates a new ParallelScheduler instance.
|
|
3426
|
+
*
|
|
3427
|
+
* @param config - Configuration for the scheduler
|
|
3428
|
+
* @example
|
|
3429
|
+
* ```typescript
|
|
3430
|
+
* const scheduler = new ParallelScheduler({ maxConcurrency: 4 });
|
|
3431
|
+
* ```
|
|
3432
|
+
*/
|
|
3433
|
+
constructor(config?: ParallelSchedulerConfig);
|
|
3434
|
+
/**
|
|
3435
|
+
* Groups nodes into execution levels where nodes in the same level can run in parallel.
|
|
3436
|
+
*
|
|
3437
|
+
* Uses Kahn's algorithm to perform topological sorting with level identification.
|
|
3438
|
+
* Nodes are grouped by their distance from source nodes (input nodes with no dependencies).
|
|
3439
|
+
*
|
|
3440
|
+
* @param nodes - Array of flow nodes to analyze
|
|
3441
|
+
* @param edges - Array of edges defining dependencies between nodes
|
|
3442
|
+
* @returns Array of execution levels, ordered from 0 (no dependencies) onwards
|
|
3443
|
+
* @throws Error if a cycle is detected in the flow graph
|
|
3444
|
+
*
|
|
3445
|
+
* @example
|
|
3446
|
+
* ```typescript
|
|
3447
|
+
* const levels = scheduler.groupNodesByExecutionLevel(nodes, edges);
|
|
3448
|
+
* // levels = [
|
|
3449
|
+
* // { level: 0, nodes: ['input_1'] },
|
|
3450
|
+
* // { level: 1, nodes: ['resize_1', 'optimize_1'] },
|
|
3451
|
+
* // { level: 2, nodes: ['output_1'] }
|
|
3452
|
+
* // ]
|
|
3453
|
+
* ```
|
|
3454
|
+
*/
|
|
3455
|
+
groupNodesByExecutionLevel(nodes: FlowNode<unknown, unknown>[], edges: Array<{
|
|
3456
|
+
source: string;
|
|
3457
|
+
target: string;
|
|
3458
|
+
}>): ExecutionLevel[];
|
|
3459
|
+
/**
|
|
3460
|
+
* Executes a batch of Effect-based node executors in parallel with concurrency control.
|
|
3461
|
+
*
|
|
3462
|
+
* All executors are run in parallel, but the number of concurrent executions is limited
|
|
3463
|
+
* by maxConcurrency. This prevents resource exhaustion while maximizing parallelism.
|
|
3464
|
+
*
|
|
3465
|
+
* @template T - The return type of each executor
|
|
3466
|
+
* @template E - The error type of the Effects
|
|
3467
|
+
* @template R - The requirements type of the Effects
|
|
3468
|
+
*
|
|
3469
|
+
* @param nodeExecutors - Array of Effect-returning functions to execute in parallel
|
|
3470
|
+
* @returns Effect that resolves to array of results in the same order as input
|
|
3471
|
+
*
|
|
3472
|
+
* @example
|
|
3473
|
+
* ```typescript
|
|
3474
|
+
* const results = yield* scheduler.executeNodesInParallel([
|
|
3475
|
+
* () => executeNode("node1"),
|
|
3476
|
+
* () => executeNode("node2"),
|
|
3477
|
+
* () => executeNode("node3")
|
|
3478
|
+
* ]);
|
|
3479
|
+
* // results will be in order: [result1, result2, result3]
|
|
3480
|
+
* ```
|
|
3481
|
+
*/
|
|
3482
|
+
executeNodesInParallel<T, E, R>(nodeExecutors: Array<() => Effect.Effect<T, E, R>>): Effect.Effect<T[], E, R>;
|
|
3483
|
+
/**
|
|
3484
|
+
* Determines if a set of nodes can be safely executed in parallel.
|
|
3485
|
+
*
|
|
3486
|
+
* Nodes can execute in parallel if all their dependencies have been completed.
|
|
3487
|
+
* This is typically called to verify that nodes in an execution level are ready
|
|
3488
|
+
* to run given the current node results.
|
|
3489
|
+
*
|
|
3490
|
+
* @param nodeIds - Array of node IDs to check
|
|
3491
|
+
* @param nodeResults - Map of completed node IDs to their results
|
|
3492
|
+
* @param reverseGraph - Dependency graph mapping node IDs to their incoming dependencies
|
|
3493
|
+
* @returns true if all dependencies for all nodes are in nodeResults, false otherwise
|
|
3494
|
+
*
|
|
3495
|
+
* @example
|
|
3496
|
+
* ```typescript
|
|
3497
|
+
* const canRun = scheduler.canExecuteInParallel(
|
|
3498
|
+
* ['resize_1', 'optimize_1'],
|
|
3499
|
+
* nodeResults,
|
|
3500
|
+
* reverseGraph
|
|
3501
|
+
* );
|
|
3502
|
+
* ```
|
|
3503
|
+
*/
|
|
3504
|
+
canExecuteInParallel(nodeIds: string[], nodeResults: Map<string, unknown>, reverseGraph: Record<string, string[]>): boolean;
|
|
3505
|
+
/**
|
|
3506
|
+
* Gets execution statistics for monitoring and debugging.
|
|
3507
|
+
*
|
|
3508
|
+
* @returns Object containing current scheduler configuration
|
|
3509
|
+
*
|
|
3510
|
+
* @example
|
|
3511
|
+
* ```typescript
|
|
3512
|
+
* const stats = scheduler.getStats();
|
|
3513
|
+
* console.log(`Max concurrency: ${stats.maxConcurrency}`);
|
|
3514
|
+
* ```
|
|
3515
|
+
*/
|
|
3516
|
+
getStats(): {
|
|
3517
|
+
maxConcurrency: number;
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
//#endregion
|
|
3521
|
+
//#region src/flow/plugins/credential-provider.d.ts
|
|
3522
|
+
/**
|
|
3523
|
+
* Shape definition for the Credential Provider interface.
|
|
3524
|
+
* Defines the contract for retrieving credentials for various services.
|
|
3525
|
+
*/
|
|
3526
|
+
interface CredentialProviderShape {
|
|
3527
|
+
/**
|
|
3528
|
+
* Retrieves credentials for a specific service and client.
|
|
3529
|
+
*
|
|
3530
|
+
* @param params - Parameters for credential retrieval
|
|
3531
|
+
* @param params.clientId - Unique identifier for the client, or null if not available
|
|
3532
|
+
* @param params.serviceType - Optional service type to get specific credentials for
|
|
3533
|
+
* @returns An Effect that resolves to a record of credential key-value pairs
|
|
3534
|
+
* @throws {UploadistaError} When credential retrieval fails
|
|
3535
|
+
*/
|
|
3536
|
+
getCredential: (params: {
|
|
3537
|
+
clientId: string | null;
|
|
3538
|
+
serviceType?: string;
|
|
3539
|
+
}) => Effect.Effect<Record<string, unknown>, UploadistaError$1>;
|
|
3540
|
+
}
|
|
3541
|
+
declare const CredentialProvider_base: Context.TagClass<CredentialProvider, "CredentialProvider", CredentialProviderShape>;
|
|
3542
|
+
/**
|
|
3543
|
+
* Context tag for the Credential Provider.
|
|
3544
|
+
*
|
|
3545
|
+
* This tag provides a type-safe way to access credential functionality
|
|
3546
|
+
* throughout the application using Effect's dependency injection system.
|
|
3547
|
+
*
|
|
3548
|
+
* @example
|
|
3549
|
+
* ```typescript
|
|
3550
|
+
* import { CredentialProvider } from "@uploadista/core/flow/plugins";
|
|
3551
|
+
*
|
|
3552
|
+
* // In your flow node
|
|
3553
|
+
* const program = Effect.gen(function* () {
|
|
3554
|
+
* const credentialProvider = yield* CredentialProvider;
|
|
3555
|
+
* const credentials = yield* credentialProvider.getCredential({
|
|
3556
|
+
* clientId: "user123",
|
|
3557
|
+
* serviceType: "replicate"
|
|
3558
|
+
* });
|
|
3559
|
+
* return credentials;
|
|
3560
|
+
* });
|
|
3561
|
+
* ```
|
|
3562
|
+
*/
|
|
3563
|
+
declare class CredentialProvider extends CredentialProvider_base {}
|
|
3564
|
+
//#endregion
|
|
3565
|
+
//#region src/flow/plugins/image-ai-plugin.d.ts
|
|
3566
|
+
/**
|
|
3567
|
+
* Context information for AI image processing operations.
|
|
3568
|
+
* Contains client identification for tracking and billing purposes.
|
|
3569
|
+
*/
|
|
3570
|
+
type ImageAiContext = {
|
|
3571
|
+
/** Unique identifier for the client making the request, or null if not available */
|
|
3572
|
+
clientId: string | null;
|
|
3573
|
+
};
|
|
3574
|
+
/**
|
|
3575
|
+
* Shape definition for the Image AI Plugin interface.
|
|
3576
|
+
* Defines the contract that all image AI implementations must follow.
|
|
3577
|
+
*/
|
|
3578
|
+
type ImageAiPluginShape = {
|
|
3579
|
+
/**
|
|
3580
|
+
* Removes the background from an image using AI processing.
|
|
3581
|
+
*
|
|
3582
|
+
* @param inputUrl - The URL of the input image to process
|
|
3583
|
+
* @param context - Context information including client ID for tracking
|
|
3584
|
+
* @returns An Effect that resolves to an object containing the output image URL
|
|
3585
|
+
* @throws {UploadistaError} When the background removal fails
|
|
3586
|
+
*/
|
|
3587
|
+
removeBackground: (inputUrl: string, context: ImageAiContext) => Effect.Effect<{
|
|
3588
|
+
outputUrl: string;
|
|
3589
|
+
}, UploadistaError>;
|
|
3590
|
+
/**
|
|
3591
|
+
* Generates a textual description of an image using AI analysis.
|
|
3592
|
+
*
|
|
3593
|
+
* @param inputUrl - The URL of the input image to analyze
|
|
3594
|
+
* @param context - Context information including client ID for tracking
|
|
3595
|
+
* @returns An Effect that resolves to an object containing the image description
|
|
3596
|
+
* @throws {UploadistaError} When the image analysis fails
|
|
3597
|
+
*/
|
|
3598
|
+
describeImage: (inputUrl: string, context: ImageAiContext) => Effect.Effect<{
|
|
3599
|
+
description: string;
|
|
3600
|
+
}, UploadistaError>;
|
|
3601
|
+
};
|
|
3602
|
+
declare const ImageAiPlugin_base: Context.TagClass<ImageAiPlugin, "ImageAiPlugin", ImageAiPluginShape>;
|
|
3603
|
+
/**
|
|
3604
|
+
* Context tag for the Image AI Plugin.
|
|
3605
|
+
*
|
|
3606
|
+
* This tag provides a type-safe way to access image AI functionality
|
|
3607
|
+
* throughout the application using Effect's dependency injection system.
|
|
3608
|
+
*
|
|
3609
|
+
* @example
|
|
3610
|
+
* ```typescript
|
|
3611
|
+
* import { ImageAiPlugin } from "@uploadista/core/flow/plugins";
|
|
3612
|
+
*
|
|
3613
|
+
* // In your flow node
|
|
3614
|
+
* const program = Effect.gen(function* () {
|
|
3615
|
+
* const imageAi = yield* ImageAiPlugin;
|
|
3616
|
+
* const result = yield* imageAi.removeBackground(imageUrl, { clientId: "user123" });
|
|
3617
|
+
* return result.outputUrl;
|
|
3618
|
+
* });
|
|
3619
|
+
* ```
|
|
3620
|
+
*/
|
|
3621
|
+
declare class ImageAiPlugin extends ImageAiPlugin_base {}
|
|
3622
|
+
//#endregion
|
|
3623
|
+
//#region src/flow/plugins/types/optimize-node.d.ts
|
|
3624
|
+
/**
|
|
3625
|
+
* Zod schema for validating image optimization parameters.
|
|
3626
|
+
* Defines the structure and validation rules for image optimization requests.
|
|
3627
|
+
*/
|
|
3628
|
+
declare const optimizeParamsSchema: z.ZodObject<{
|
|
3629
|
+
quality: z.ZodNumber;
|
|
3630
|
+
format: z.ZodEnum<{
|
|
3631
|
+
jpeg: "jpeg";
|
|
3632
|
+
webp: "webp";
|
|
3633
|
+
png: "png";
|
|
3634
|
+
avif: "avif";
|
|
3635
|
+
}>;
|
|
3636
|
+
}, z.core.$strip>;
|
|
3637
|
+
/**
|
|
3638
|
+
* Parameters for the image optimization node.
|
|
3639
|
+
* Controls quality and format settings for image optimization.
|
|
3640
|
+
*/
|
|
3641
|
+
type OptimizeParams = z.infer<typeof optimizeParamsSchema>;
|
|
3642
|
+
//#endregion
|
|
3643
|
+
//#region src/flow/plugins/types/resize-node.d.ts
|
|
3644
|
+
/**
|
|
3645
|
+
* Zod schema for validating image resize parameters.
|
|
3646
|
+
* Defines the structure and validation rules for image resizing requests.
|
|
3647
|
+
* Requires at least one dimension (width or height) to be specified.
|
|
3648
|
+
*/
|
|
3649
|
+
declare const resizeParamsSchema: z.ZodObject<{
|
|
3650
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
3651
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
3652
|
+
fit: z.ZodEnum<{
|
|
3653
|
+
fill: "fill";
|
|
3654
|
+
contain: "contain";
|
|
3655
|
+
cover: "cover";
|
|
3656
|
+
}>;
|
|
3657
|
+
}, z.core.$strip>;
|
|
3658
|
+
/**
|
|
3659
|
+
* Parameters for the image resize node.
|
|
3660
|
+
* Controls the target dimensions and fitting behavior for image resizing.
|
|
3661
|
+
*/
|
|
3662
|
+
type ResizeParams = z.infer<typeof resizeParamsSchema>;
|
|
3663
|
+
//#endregion
|
|
3664
|
+
//#region src/flow/plugins/image-plugin.d.ts
|
|
3665
|
+
/**
|
|
3666
|
+
* Shape definition for the Image Plugin interface.
|
|
3667
|
+
* Defines the contract that all image processing implementations must follow.
|
|
3668
|
+
*/
|
|
3669
|
+
type ImagePluginShape = {
|
|
3670
|
+
/**
|
|
3671
|
+
* Optimizes an image by adjusting quality and format.
|
|
3672
|
+
*
|
|
3673
|
+
* @param input - The input image as a Uint8Array
|
|
3674
|
+
* @param options - Optimization parameters including quality and format
|
|
3675
|
+
* @returns An Effect that resolves to the optimized image as a Uint8Array
|
|
3676
|
+
* @throws {UploadistaError} When image optimization fails
|
|
3677
|
+
*/
|
|
3678
|
+
optimize: (input: Uint8Array, options: OptimizeParams) => Effect.Effect<Uint8Array, UploadistaError>;
|
|
3679
|
+
/**
|
|
3680
|
+
* Resizes an image to specified dimensions.
|
|
3681
|
+
*
|
|
3682
|
+
* @param input - The input image as a Uint8Array
|
|
3683
|
+
* @param options - Resize parameters including width, height, and fit mode
|
|
3684
|
+
* @returns An Effect that resolves to the resized image as a Uint8Array
|
|
3685
|
+
* @throws {UploadistaError} When image resizing fails
|
|
3686
|
+
*/
|
|
3687
|
+
resize: (input: Uint8Array, options: ResizeParams) => Effect.Effect<Uint8Array, UploadistaError>;
|
|
3688
|
+
};
|
|
3689
|
+
declare const ImagePlugin_base: Context.TagClass<ImagePlugin, "ImagePlugin", ImagePluginShape>;
|
|
3690
|
+
/**
|
|
3691
|
+
* Context tag for the Image Plugin.
|
|
3692
|
+
*
|
|
3693
|
+
* This tag provides a type-safe way to access image processing functionality
|
|
3694
|
+
* throughout the application using Effect's dependency injection system.
|
|
3695
|
+
*
|
|
3696
|
+
* @example
|
|
3697
|
+
* ```typescript
|
|
3698
|
+
* import { ImagePlugin } from "@uploadista/core/flow/plugins";
|
|
3699
|
+
*
|
|
3700
|
+
* // In your flow node
|
|
3701
|
+
* const program = Effect.gen(function* () {
|
|
3702
|
+
* const imagePlugin = yield* ImagePlugin;
|
|
3703
|
+
* const optimized = yield* imagePlugin.optimize(imageData, { quality: 80, format: "webp" });
|
|
3704
|
+
* const resized = yield* imagePlugin.resize(optimized, { width: 800, height: 600, fit: "cover" });
|
|
3705
|
+
* return resized;
|
|
3706
|
+
* });
|
|
3707
|
+
* ```
|
|
3708
|
+
*/
|
|
3709
|
+
declare class ImagePlugin extends ImagePlugin_base {}
|
|
3710
|
+
//#endregion
|
|
3711
|
+
//#region src/flow/plugins/types/describe-image-node.d.ts
|
|
3712
|
+
/**
|
|
3713
|
+
* Zod schema for validating describe image node parameters.
|
|
3714
|
+
* Defines the structure and validation rules for image description requests.
|
|
3715
|
+
*/
|
|
3716
|
+
declare const describeImageParamsSchema: z.ZodObject<{
|
|
3717
|
+
serviceType: z.ZodOptional<z.ZodEnum<{
|
|
3718
|
+
replicate: "replicate";
|
|
3719
|
+
}>>;
|
|
3720
|
+
}, z.core.$strip>;
|
|
3721
|
+
/**
|
|
3722
|
+
* Parameters for the describe image node.
|
|
3723
|
+
* Controls which AI service to use for generating image descriptions.
|
|
3724
|
+
*/
|
|
3725
|
+
type DescribeImageParams = z.infer<typeof describeImageParamsSchema>;
|
|
3726
|
+
//#endregion
|
|
3727
|
+
//#region src/flow/plugins/types/remove-background-node.d.ts
|
|
3728
|
+
/**
|
|
3729
|
+
* Zod schema for validating remove background node parameters.
|
|
3730
|
+
* Defines the structure and validation rules for background removal requests.
|
|
3731
|
+
*/
|
|
3732
|
+
declare const removeBackgroundParamsSchema: z.ZodObject<{
|
|
3733
|
+
serviceType: z.ZodOptional<z.ZodEnum<{
|
|
3734
|
+
replicate: "replicate";
|
|
3735
|
+
}>>;
|
|
3736
|
+
}, z.core.$strip>;
|
|
3737
|
+
/**
|
|
3738
|
+
* Parameters for the remove background node.
|
|
3739
|
+
* Controls which AI service to use for background removal processing.
|
|
3740
|
+
*/
|
|
3741
|
+
type RemoveBackgroundParams = z.infer<typeof removeBackgroundParamsSchema>;
|
|
3742
|
+
//#endregion
|
|
3743
|
+
//#region src/flow/plugins/zip-plugin.d.ts
|
|
3744
|
+
/**
|
|
3745
|
+
* Parameters for creating a ZIP archive.
|
|
3746
|
+
*/
|
|
3747
|
+
type ZipParams = {
|
|
3748
|
+
/** Name of the ZIP file to create */
|
|
3749
|
+
zipName: string;
|
|
3750
|
+
/** Whether to include file metadata in the ZIP archive */
|
|
3751
|
+
includeMetadata: boolean;
|
|
3752
|
+
};
|
|
3753
|
+
/**
|
|
3754
|
+
* Input data structure for ZIP operations.
|
|
3755
|
+
* Represents a single file to be included in the ZIP archive.
|
|
3756
|
+
*/
|
|
3757
|
+
type ZipInput = {
|
|
3758
|
+
/** Unique identifier for the file */
|
|
3759
|
+
id: string;
|
|
3760
|
+
/** Binary data of the file */
|
|
3761
|
+
data: Uint8Array;
|
|
3762
|
+
/** File metadata including name, size, type, etc. */
|
|
3763
|
+
metadata: UploadFile["metadata"];
|
|
3764
|
+
};
|
|
3765
|
+
/**
|
|
3766
|
+
* Shape definition for the ZIP Plugin interface.
|
|
3767
|
+
* Defines the contract that all ZIP implementations must follow.
|
|
3768
|
+
*/
|
|
3769
|
+
type ZipPluginShape = {
|
|
3770
|
+
/**
|
|
3771
|
+
* Creates a ZIP archive from multiple input files.
|
|
3772
|
+
*
|
|
3773
|
+
* @param inputs - Array of files to include in the ZIP archive
|
|
3774
|
+
* @param options - Configuration options for the ZIP creation
|
|
3775
|
+
* @returns An Effect that resolves to the ZIP file as a Uint8Array
|
|
3776
|
+
* @throws {UploadistaError} When ZIP creation fails
|
|
3777
|
+
*/
|
|
3778
|
+
zip: (inputs: ZipInput[], options: ZipParams) => Effect.Effect<Uint8Array, UploadistaError>;
|
|
3779
|
+
};
|
|
3780
|
+
declare const ZipPlugin_base: Context.TagClass<ZipPlugin, "ZipPlugin", ZipPluginShape>;
|
|
3781
|
+
/**
|
|
3782
|
+
* Context tag for the ZIP Plugin.
|
|
3783
|
+
*
|
|
3784
|
+
* This tag provides a type-safe way to access ZIP functionality
|
|
3785
|
+
* throughout the application using Effect's dependency injection system.
|
|
3786
|
+
*
|
|
3787
|
+
* @example
|
|
3788
|
+
* ```typescript
|
|
3789
|
+
* import { ZipPlugin } from "@uploadista/core/flow/plugins";
|
|
3790
|
+
*
|
|
3791
|
+
* // In your flow node
|
|
3792
|
+
* const program = Effect.gen(function* () {
|
|
3793
|
+
* const zipPlugin = yield* ZipPlugin;
|
|
3794
|
+
* const zipData = yield* zipPlugin.zip(files, { zipName: "archive.zip", includeMetadata: true });
|
|
3795
|
+
* return zipData;
|
|
3796
|
+
* });
|
|
3797
|
+
* ```
|
|
3798
|
+
*/
|
|
3799
|
+
declare class ZipPlugin extends ZipPlugin_base {}
|
|
3800
|
+
//#endregion
|
|
3801
|
+
//#region src/flow/typed-flow.d.ts
|
|
3802
|
+
type NodeDefinition<TNodeError = never, TNodeRequirements = never> = FlowNode<any, any, UploadistaError$1> | Effect.Effect<FlowNode<any, any, UploadistaError$1>, TNodeError, TNodeRequirements>;
|
|
3803
|
+
type NodeDefinitionsRecord = Record<string, NodeDefinition<any, any>>;
|
|
3804
|
+
type NodeDefinitionError<T$1> = T$1 extends Effect.Effect<FlowNode<any, any, UploadistaError$1>, infer TError, any> ? TError : never;
|
|
3805
|
+
type NodeDefinitionRequirements<T$1> = T$1 extends Effect.Effect<FlowNode<any, any, UploadistaError$1>, any, infer TRequirements> ? TRequirements : never;
|
|
3806
|
+
type NodesErrorUnion<TNodes extends NodeDefinitionsRecord> = { [K in keyof TNodes]: NodeDefinitionError<TNodes[K]> }[keyof TNodes];
|
|
3807
|
+
type NodesRequirementsUnion<TNodes extends NodeDefinitionsRecord> = { [K in keyof TNodes]: NodeDefinitionRequirements<TNodes[K]> }[keyof TNodes];
|
|
3808
|
+
type FlowRequirements<TNodes extends NodeDefinitionsRecord> = NodesRequirementsUnion<TNodes>;
|
|
3809
|
+
type FlowPluginRequirements<TNodes extends NodeDefinitionsRecord> = Exclude<FlowRequirements<TNodes>, UploadServer>;
|
|
3810
|
+
type InferNode<T$1> = T$1 extends FlowNode<any, any, UploadistaError$1> ? T$1 : T$1 extends Effect.Effect<infer R, any, any> ? R extends FlowNode<any, any, UploadistaError$1> ? R : never : never;
|
|
3811
|
+
type ExtractKeysByNodeType<TNodes extends NodeDefinitionsRecord, TType extends NodeType> = { [K in keyof TNodes]: InferNode<TNodes[K]>["type"] extends TType ? K : never }[keyof TNodes];
|
|
3812
|
+
type SchemaInfer<T$1> = T$1 extends z.ZodTypeAny ? z.infer<T$1> : never;
|
|
3813
|
+
type FlowInputMap<TNodes extends NodeDefinitionsRecord> = { [K in Extract<ExtractKeysByNodeType<TNodes, NodeType.input>, string>]: SchemaInfer<InferNode<TNodes[K]>["inputSchema"]> };
|
|
3814
|
+
type FlowOutputMap<TNodes extends NodeDefinitionsRecord> = { [K in Extract<ExtractKeysByNodeType<TNodes, NodeType.output>, string>]: SchemaInfer<InferNode<TNodes[K]>["outputSchema"]> };
|
|
3815
|
+
type FlowInputUnion<TNodes extends NodeDefinitionsRecord> = { [K in Extract<ExtractKeysByNodeType<TNodes, NodeType.input>, string>]: SchemaInfer<InferNode<TNodes[K]>["inputSchema"]> }[Extract<ExtractKeysByNodeType<TNodes, NodeType.input>, string>];
|
|
3816
|
+
type FlowOutputUnion<TNodes extends NodeDefinitionsRecord> = { [K in Extract<ExtractKeysByNodeType<TNodes, NodeType.output>, string>]: SchemaInfer<InferNode<TNodes[K]>["outputSchema"]> }[Extract<ExtractKeysByNodeType<TNodes, NodeType.output>, string>];
|
|
3817
|
+
type NodeKey<TNodes extends NodeDefinitionsRecord> = Extract<keyof TNodes, string>;
|
|
3818
|
+
type TypedFlowEdge<TNodes extends NodeDefinitionsRecord> = {
|
|
3819
|
+
source: NodeKey<TNodes>;
|
|
3820
|
+
target: NodeKey<TNodes>;
|
|
3821
|
+
sourcePort?: string;
|
|
3822
|
+
targetPort?: string;
|
|
3823
|
+
};
|
|
3824
|
+
type TypedFlowConfig<TNodes extends NodeDefinitionsRecord> = {
|
|
3825
|
+
flowId: string;
|
|
3826
|
+
name: string;
|
|
3827
|
+
nodes: TNodes;
|
|
3828
|
+
edges: Array<TypedFlowEdge<TNodes>>;
|
|
3829
|
+
typeChecker?: TypeCompatibilityChecker;
|
|
3830
|
+
onEvent?: (event: FlowEvent) => Effect.Effect<{
|
|
3831
|
+
eventId: string | null;
|
|
3832
|
+
}, UploadistaError$1>;
|
|
3833
|
+
parallelExecution?: {
|
|
3834
|
+
enabled?: boolean;
|
|
3835
|
+
maxConcurrency?: number;
|
|
3836
|
+
};
|
|
3837
|
+
inputSchema?: z.ZodTypeAny;
|
|
3838
|
+
outputSchema?: z.ZodTypeAny;
|
|
3839
|
+
};
|
|
3840
|
+
declare const typedFlowInputsSymbol: unique symbol;
|
|
3841
|
+
declare const typedFlowOutputsSymbol: unique symbol;
|
|
3842
|
+
declare const typedFlowPluginsSymbol: unique symbol;
|
|
3843
|
+
type TypedFlow<TNodes extends NodeDefinitionsRecord, TInputSchema extends z.ZodTypeAny, TOutputSchema extends z.ZodTypeAny> = Flow<TInputSchema, TOutputSchema, FlowRequirements<TNodes>> & {
|
|
3844
|
+
run: (args: {
|
|
3845
|
+
inputs?: Partial<FlowInputMap<TNodes>>;
|
|
3846
|
+
storageId: string;
|
|
3847
|
+
jobId: string;
|
|
3848
|
+
}) => Effect.Effect<FlowExecutionResult<FlowOutputMap<TNodes>>, UploadistaError$1, FlowRequirements<TNodes>>;
|
|
3849
|
+
resume: (args: {
|
|
3850
|
+
jobId: string;
|
|
3851
|
+
storageId: string;
|
|
3852
|
+
nodeResults: Record<string, unknown>;
|
|
3853
|
+
executionState: {
|
|
3854
|
+
executionOrder: string[];
|
|
3855
|
+
currentIndex: number;
|
|
3856
|
+
inputs: Partial<FlowInputMap<TNodes>>;
|
|
3857
|
+
};
|
|
3858
|
+
}) => Effect.Effect<FlowExecutionResult<FlowOutputMap<TNodes>>, UploadistaError$1, FlowRequirements<TNodes>>;
|
|
3859
|
+
readonly [typedFlowInputsSymbol]?: FlowInputMap<TNodes>;
|
|
3860
|
+
readonly [typedFlowOutputsSymbol]?: FlowOutputMap<TNodes>;
|
|
3861
|
+
readonly [typedFlowPluginsSymbol]?: FlowPluginRequirements<TNodes>;
|
|
3862
|
+
};
|
|
3863
|
+
declare function createFlow<TNodes extends NodeDefinitionsRecord>(config: TypedFlowConfig<TNodes>): Effect.Effect<TypedFlow<TNodes, z.ZodType<FlowInputUnion<TNodes>>, z.ZodType<FlowOutputUnion<TNodes>>>, NodesErrorUnion<TNodes> | UploadistaError$1, FlowRequirements<TNodes>>;
|
|
3864
|
+
//#endregion
|
|
3865
|
+
//#region src/flow/types/flow-file.d.ts
|
|
3866
|
+
/**
|
|
3867
|
+
* Conditional execution rules for flow nodes.
|
|
3868
|
+
*
|
|
3869
|
+
* Conditions allow nodes to execute conditionally based on file properties or metadata.
|
|
3870
|
+
* They are evaluated before node execution and can skip nodes that don't match.
|
|
3871
|
+
*
|
|
3872
|
+
* @module flow/types/flow-file
|
|
3873
|
+
* @see {@link FlowNode} for how conditions are used in nodes
|
|
3874
|
+
*
|
|
3875
|
+
* @example
|
|
3876
|
+
* ```typescript
|
|
3877
|
+
* // Only process images larger than 1MB
|
|
3878
|
+
* const condition: FlowCondition = {
|
|
3879
|
+
* field: "size",
|
|
3880
|
+
* operator: "greaterThan",
|
|
3881
|
+
* value: 1024 * 1024
|
|
3882
|
+
* };
|
|
3883
|
+
*
|
|
3884
|
+
* // Only process JPEG images
|
|
3885
|
+
* const jpegCondition: FlowCondition = {
|
|
3886
|
+
* field: "mimeType",
|
|
3887
|
+
* operator: "startsWith",
|
|
3888
|
+
* value: "image/jpeg"
|
|
3889
|
+
* };
|
|
3890
|
+
* ```
|
|
3891
|
+
*/
|
|
3892
|
+
/**
|
|
3893
|
+
* Represents a conditional rule for node execution.
|
|
3894
|
+
*
|
|
3895
|
+
* @property field - The file property to check
|
|
3896
|
+
* @property operator - The comparison operator to apply
|
|
3897
|
+
* @property value - The value to compare against
|
|
3898
|
+
*
|
|
3899
|
+
* @remarks
|
|
3900
|
+
* - Fields can check file metadata (mimeType, size) or image properties (width, height)
|
|
3901
|
+
* - String operators (contains, startsWith) work with string values
|
|
3902
|
+
* - Numeric operators (greaterThan, lessThan) work with numeric values
|
|
3903
|
+
* - The extension field checks the file extension without the dot
|
|
3904
|
+
*/
|
|
3905
|
+
type FlowCondition = {
|
|
3906
|
+
field: "mimeType" | "size" | "width" | "height" | "extension";
|
|
3907
|
+
operator: "equals" | "notEquals" | "greaterThan" | "lessThan" | "contains" | "startsWith";
|
|
3908
|
+
value: string | number;
|
|
3909
|
+
};
|
|
3910
|
+
//#endregion
|
|
3911
|
+
//#region src/flow/types/run-args.d.ts
|
|
3912
|
+
/**
|
|
3913
|
+
* Zod schema for validating flow run arguments.
|
|
3914
|
+
*
|
|
3915
|
+
* @property inputs - Record mapping input node IDs to their input data
|
|
3916
|
+
*
|
|
3917
|
+
* @example
|
|
3918
|
+
* ```typescript
|
|
3919
|
+
* const args = {
|
|
3920
|
+
* inputs: {
|
|
3921
|
+
* "input-node-1": { file: myFile, metadata: { ... } },
|
|
3922
|
+
* "input-node-2": { file: anotherFile }
|
|
3923
|
+
* }
|
|
3924
|
+
* };
|
|
3925
|
+
*
|
|
3926
|
+
* // Validate before running
|
|
3927
|
+
* const validated = runArgsSchema.parse(args);
|
|
3928
|
+
* ```
|
|
3929
|
+
*/
|
|
3930
|
+
declare const runArgsSchema: z.ZodObject<{
|
|
3931
|
+
inputs: z.ZodRecord<z.ZodString, z.ZodAny>;
|
|
3932
|
+
}, z.core.$strip>;
|
|
3933
|
+
/**
|
|
3934
|
+
* Type representing validated flow run arguments.
|
|
3935
|
+
*
|
|
3936
|
+
* This type is inferred from the runArgsSchema and ensures type safety
|
|
3937
|
+
* when passing inputs to flow execution.
|
|
3938
|
+
*/
|
|
3939
|
+
type RunArgs = z.infer<typeof runArgsSchema>;
|
|
3940
|
+
//#endregion
|
|
3941
|
+
//#region src/flow/utils/resolve-upload-metadata.d.ts
|
|
3942
|
+
type FileMetadata = UploadFile["metadata"];
|
|
3943
|
+
type ResolvedUploadMetadata = {
|
|
3944
|
+
type: string;
|
|
3945
|
+
fileName: string;
|
|
3946
|
+
metadata: FileMetadata;
|
|
3947
|
+
metadataJson: string | undefined;
|
|
3948
|
+
};
|
|
3949
|
+
declare function resolveUploadMetadata(metadata: FileMetadata): ResolvedUploadMetadata;
|
|
3950
|
+
//#endregion
|
|
3951
|
+
export { FlowServerShape as $, FlowJobKVStore as $t, ImageAiContext as A, FlowEventJobStart as An, flowEventEmitter as At, StorageParams as B, NodeType as Bn, BufferedUploadFileDataStore as Bt, describeImageParamsSchema as C, waitingNodeExecution as Cn, BaseEventEmitter as Ct, resizeParamsSchema as D, FlowEventFlowError as Dn, TypedEventEmitter as Dt, ResizeParams as E, FlowEventFlowEnd as En, FlowEventEmitter as Et, ExecutionLevel as F, FlowEventNodeResume as Fn, UploadEvent as Ft, createInputNode as G, UploadFileDataStore as Gt, storageParamsSchema as H, getNodeData as Hn, DataStoreCapabilities as Ht, ParallelScheduler as I, FlowEventNodeStart as In, UploadEventType as It, FlowProvider as J, UploadStrategy as Jt, inputDataSchema as K, UploadFileDataStores as Kt, ParallelSchedulerConfig as L, ConditionField as Ln, uploadEventSchema as Lt, ImageAiPluginShape as M, FlowEventNodeError as Mn, WebSocketConnection as Mt, CredentialProvider as N, FlowEventNodePause as Nn, WebSocketMessage as Nt, OptimizeParams as O, FlowEventFlowStart as On, UploadEventEmitter as Ot, CredentialProviderShape as P, FlowEventNodeResponse as Pn, webSocketMessageSchema as Pt, FlowServerOptions as Q, BaseKvStoreService as Qt, TransformNodeConfig as R, ConditionOperator as Rn, EventBroadcaster as Rt, DescribeImageParams as S, completeNodeExecution as Sn, inputFileSchema as St, ImagePluginShape as T, FlowEvent as Tn, EventEmitter as Tt, InputData as U, DataStoreConfig as Ut, createStorageNode as V, createFlowNode as Vn, DataStore as Vt, InputNodeParams as W, DataStoreWriteOptions as Wt, FlowServer as X, isDataStore as Xt, FlowProviderShape as Y, createDataStoreLayer as Yt, FlowServerLayer as Z, BaseKvStore as Zt, ZipParams as _, FlowNodeData as _n, MiddlewareContext as _t, FlowCondition as a, uploadFileKvStore as an, FlowJobTaskStatus as at, RemoveBackgroundParams as b, NodeTypeMap as bn, MiddlewareServiceLive as bt, FlowPluginRequirements as c, Flow as cn, UploadStrategyOptions as ct, NodeDefinitionsRecord as d, createFlowWithSchema as dn, UploadServerShape as dt, KvStore as en, createFlowServer as et, TypedFlow as f, getFlowData as fn, createUploadServer as ft, ZipInput as g, FlowNode as gn, Middleware as gt, createFlow as h, FlowConfig as hn, detectMimeType as ht, runArgsSchema as i, jsonSerializer as in, FlowJobTask as it, ImageAiPlugin as j, FlowEventNodeEnd as jn, uploadEventEmitter as jt, optimizeParamsSchema as k, FlowEventJobEnd as kn, eventToMessageSerializer as kt, FlowRequirements as l, FlowData as ln, UploadServer as lt, TypedFlowEdge as m, createFlowEdge as mn, compareMimeTypes as mt, resolveUploadMetadata as n, UploadFileKVStore as nn, FlowJob as nt, FlowInputMap as o, UploadFile as on, NegotiatedStrategy as ot, TypedFlowConfig as p, FlowEdge as pn, uploadServer as pt, inputNodeParamsSchema as q, UploadFileDataStoresShape as qt, RunArgs as r, flowJobKvStore as rn, FlowJobStatus as rt, FlowOutputMap as s, uploadFileSchema as sn, UploadStrategyNegotiator as st, ResolvedUploadMetadata as t, TypedKvStore as tn, flowServer as tt, NodeDefinition as u, FlowExecutionResult as un, UploadServerOptions as ut, ZipPlugin as v, NodeConnectionValidator as vn, MiddlewareNext as vt, ImagePlugin as w, EventType as wn, BaseEventEmitterService as wt, removeBackgroundParamsSchema as x, TypeCompatibilityChecker as xn, InputFile as xt, ZipPluginShape as y, NodeExecutionResult as yn, MiddlewareService as yt, createTransformNode as z, ConditionValue as zn, EventBroadcasterService as zt };
|
|
3952
|
+
//# sourceMappingURL=index-BnGZO47X.d.ts.map
|