@uploadista/core 0.2.0 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/{checksum-BjP9nb5b.mjs → checksum-BRjFmTRk.mjs} +2 -2
  2. package/dist/{checksum-BjP9nb5b.mjs.map → checksum-BRjFmTRk.mjs.map} +1 -1
  3. package/dist/{checksum-B7RDiO7V.cjs → checksum-BrjQ8GJL.cjs} +1 -1
  4. package/dist/errors/index.cjs +1 -1
  5. package/dist/errors/index.d.cts +1 -1
  6. package/dist/errors/index.d.mts +1 -1
  7. package/dist/errors/index.mjs +1 -1
  8. package/dist/flow/index.cjs +1 -1
  9. package/dist/flow/index.d.cts +3 -2
  10. package/dist/flow/index.d.mts +8 -5
  11. package/dist/flow/index.mjs +1 -1
  12. package/dist/generate-id-BAMRQzMr.d.cts +34 -0
  13. package/dist/generate-id-BAMRQzMr.d.cts.map +1 -0
  14. package/dist/generate-id-DuZwLm4m.d.mts +34 -0
  15. package/dist/generate-id-DuZwLm4m.d.mts.map +1 -0
  16. package/dist/index.cjs +1 -1
  17. package/dist/index.d.cts +8 -5
  18. package/dist/index.d.mts +8 -5
  19. package/dist/index.mjs +1 -1
  20. package/dist/middleware-BlrOGKrp.d.cts +4129 -0
  21. package/dist/middleware-BlrOGKrp.d.cts.map +1 -0
  22. package/dist/middleware-BmRmwme_.d.mts +4129 -0
  23. package/dist/middleware-BmRmwme_.d.mts.map +1 -0
  24. package/dist/resolve-upload-metadata-B2C5e1y1.d.cts +4533 -0
  25. package/dist/resolve-upload-metadata-B2C5e1y1.d.cts.map +1 -0
  26. package/dist/resolve-upload-metadata-DbkBzxm8.d.mts +4533 -0
  27. package/dist/resolve-upload-metadata-DbkBzxm8.d.mts.map +1 -0
  28. package/dist/run-args-C4no7Ny4.cjs +1 -0
  29. package/dist/run-args-CIqI4Zc7.mjs +2 -0
  30. package/dist/run-args-CIqI4Zc7.mjs.map +1 -0
  31. package/dist/{stream-limiter-BCFULdAM.d.cts → stream-limiter-7wkBVLWT.d.mts} +2 -2
  32. package/dist/{stream-limiter-BCFULdAM.d.cts.map → stream-limiter-7wkBVLWT.d.mts.map} +1 -1
  33. package/dist/{stream-limiter-DZ22uIqf.cjs → stream-limiter-B-Y0DTgA.cjs} +1 -1
  34. package/dist/{stream-limiter-CTJPEJqE.mjs → stream-limiter-CvDuNIyd.mjs} +2 -2
  35. package/dist/{stream-limiter-CTJPEJqE.mjs.map → stream-limiter-CvDuNIyd.mjs.map} +1 -1
  36. package/dist/{stream-limiter-Bi7OTbRp.d.mts → stream-limiter-D1KC-6pK.d.cts} +2 -2
  37. package/dist/{stream-limiter-Bi7OTbRp.d.mts.map → stream-limiter-D1KC-6pK.d.cts.map} +1 -1
  38. package/dist/streams/index.cjs +1 -1
  39. package/dist/streams/index.d.cts +1 -1
  40. package/dist/streams/index.d.mts +2 -2
  41. package/dist/streams/index.mjs +1 -1
  42. package/dist/testing/index.cjs +1 -1
  43. package/dist/testing/index.d.cts +2 -1
  44. package/dist/testing/index.d.cts.map +1 -1
  45. package/dist/testing/index.d.mts +7 -4
  46. package/dist/testing/index.d.mts.map +1 -1
  47. package/dist/testing/index.mjs +1 -1
  48. package/dist/{throttle-Da0OA8JT.d.cts → throttle-3FRcr7MU.d.mts} +4 -34
  49. package/dist/throttle-3FRcr7MU.d.mts.map +1 -0
  50. package/dist/{throttle-ibiT6E4U.d.mts → throttle-BlH27EGu.d.cts} +4 -34
  51. package/dist/throttle-BlH27EGu.d.cts.map +1 -0
  52. package/dist/{throttle-KnkRgZPi.cjs → throttle-Dp59f37i.cjs} +1 -1
  53. package/dist/{throttle-CnDa3v1k.mjs → throttle-TFY-V41R.mjs} +2 -2
  54. package/dist/{throttle-CnDa3v1k.mjs.map → throttle-TFY-V41R.mjs.map} +1 -1
  55. package/dist/types/index.cjs +1 -1
  56. package/dist/types/index.d.cts +2 -2
  57. package/dist/types/index.d.mts +3 -5
  58. package/dist/types/index.mjs +1 -1
  59. package/dist/upload/index.cjs +1 -1
  60. package/dist/upload/index.d.cts +1 -1
  61. package/dist/upload/index.d.mts +4 -4
  62. package/dist/upload/index.mjs +1 -1
  63. package/dist/{upload-strategy-negotiator-DfiQ0Fy0.cjs → upload-strategy-negotiator-5da9ZySO.cjs} +1 -1
  64. package/dist/{upload-strategy-negotiator-BuxPf1sa.mjs → upload-strategy-negotiator-ChKvppnA.mjs} +2 -2
  65. package/dist/{upload-strategy-negotiator-BuxPf1sa.mjs.map → upload-strategy-negotiator-ChKvppnA.mjs.map} +1 -1
  66. package/dist/upload-strategy-negotiator-EmOrc2bn.d.cts +455 -0
  67. package/dist/upload-strategy-negotiator-EmOrc2bn.d.cts.map +1 -0
  68. package/dist/upload-strategy-negotiator-a2O28qPf.d.mts +455 -0
  69. package/dist/upload-strategy-negotiator-a2O28qPf.d.mts.map +1 -0
  70. package/dist/{uploadista-error-B-geDgi8.cjs → uploadista-error-CZx1JU_L.cjs} +3 -1
  71. package/dist/{uploadista-error-Fsfvr2Bb.mjs → uploadista-error-DQ7V1FlX.mjs} +3 -1
  72. package/dist/uploadista-error-DQ7V1FlX.mjs.map +1 -0
  73. package/dist/{uploadista-error-BragVhIs.d.mts → uploadista-error-LtiZn-R_.d.mts} +2 -2
  74. package/dist/{uploadista-error-BragVhIs.d.mts.map → uploadista-error-LtiZn-R_.d.mts.map} +1 -1
  75. package/dist/{uploadista-error-Cj_pAFck.d.cts → uploadista-error-eZtG4iyf.d.cts} +2 -2
  76. package/dist/{uploadista-error-Cj_pAFck.d.cts.map → uploadista-error-eZtG4iyf.d.cts.map} +1 -1
  77. package/dist/utils/index.cjs +1 -1
  78. package/dist/utils/index.d.cts +2 -1
  79. package/dist/utils/index.d.mts +3 -2
  80. package/dist/utils/index.mjs +1 -1
  81. package/dist/websocket-Br0ijEZA.cjs +1 -0
  82. package/dist/websocket-DftnHFfN.mjs +2 -0
  83. package/dist/websocket-DftnHFfN.mjs.map +1 -0
  84. package/package.json +3 -3
  85. package/src/errors/uploadista-error.ts +11 -1
  86. package/src/flow/README.md +115 -0
  87. package/src/flow/flow-engine.ts +34 -2
  88. package/src/flow/flow-queue-store.ts +155 -0
  89. package/src/flow/flow-queue.ts +640 -0
  90. package/src/flow/index.ts +4 -0
  91. package/src/flow/types/flow-queue-item.ts +154 -0
  92. package/src/types/kv-store.ts +31 -1
  93. package/tests/flow-queue-store.test.ts +150 -0
  94. package/tests/flow-queue.test.ts +308 -0
  95. package/dist/resolve-upload-metadata-BUVl1LoS.d.cts +0 -8723
  96. package/dist/resolve-upload-metadata-BUVl1LoS.d.cts.map +0 -1
  97. package/dist/resolve-upload-metadata-MPDmDfOZ.d.mts +0 -8723
  98. package/dist/resolve-upload-metadata-MPDmDfOZ.d.mts.map +0 -1
  99. package/dist/run-args-WD1otVrz.mjs +0 -2
  100. package/dist/run-args-WD1otVrz.mjs.map +0 -1
  101. package/dist/run-args-g74p8pEZ.cjs +0 -1
  102. package/dist/throttle-Da0OA8JT.d.cts.map +0 -1
  103. package/dist/throttle-ibiT6E4U.d.mts.map +0 -1
  104. package/dist/uploadista-error-Fsfvr2Bb.mjs.map +0 -1
  105. package/dist/websocket-Avz4T8YB.cjs +0 -1
  106. package/dist/websocket-CdgVhVJs.mjs +0 -2
  107. package/dist/websocket-CdgVhVJs.mjs.map +0 -1
@@ -0,0 +1,4533 @@
1
+ import { n as UploadistaError } from "./uploadista-error-LtiZn-R_.mjs";
2
+ import { $ as BaseKvStoreService, Bt as TypedOutput, Dn as NodeType, Dt as FlowCircuitBreakerConfig, G as StreamingConfig, In as DeadLetterCleanupOptions, Ln as DeadLetterCleanupResult, Lt as NodeExecutionResult, Mt as FlowNode, Nt as FlowNodeData, O as WebSocketConnection, Pt as NamingContext, Q as BaseKvStore, Qt as RetryPolicy, S as FlowEventEmitter, Tt as FileNamingConfig, Un as DeadLetterQueueStats, Vn as DeadLetterListOptions, Xn as CircuitBreakerStoreService, Yn as CircuitBreakerStore, et as DeadLetterQueueKVStore, ft as FlowQueueConfig, gt as FlowJob, ht as FlowQueueStats, in as FlowEvent, jn as UploadFile, jt as FlowEdge$1, kt as FlowConfig, mt as FlowQueueItemStatus, nt as FlowQueueKVStore, pt as FlowQueueItem, q as UploadFileDataStores, qn as CircuitBreakerStateValue, rt as KvStore, tt as FlowJobKVStore, zn as DeadLetterItem, zt as TypeCompatibilityChecker } from "./middleware-BmRmwme_.mjs";
3
+ import { i as UploadEngine } from "./upload-strategy-negotiator-a2O28qPf.mjs";
4
+ import { Context, Effect, Layer, Option, Stream } from "effect";
5
+ import * as zod from "zod";
6
+ import { z } from "zod";
7
+ import * as zod_v4_core0 from "zod/v4/core";
8
+
9
+ //#region src/flow/circuit-breaker.d.ts
10
+ /**
11
+ * Circuit breaker state machine states.
12
+ *
13
+ * - `closed`: Normal operation, tracking failures in sliding window
14
+ * - `open`: Rejecting all requests immediately, waiting for reset timeout
15
+ * - `half-open`: Allowing limited test requests to probe service health
16
+ */
17
+ type CircuitBreakerState = "closed" | "open" | "half-open";
18
+ /**
19
+ * Configuration for a circuit breaker.
20
+ *
21
+ * @property enabled - Whether circuit breaker is active (default: false for backward compatibility)
22
+ * @property failureThreshold - Number of failures within window to trip circuit (default: 5)
23
+ * @property resetTimeout - Milliseconds to wait in open state before half-open (default: 30000)
24
+ * @property halfOpenRequests - Number of successful requests in half-open to close (default: 3)
25
+ * @property windowDuration - Sliding window duration in milliseconds (default: 60000)
26
+ * @property fallback - Behavior when circuit is open
27
+ */
28
+ interface CircuitBreakerConfig {
29
+ /** Whether circuit breaker is active (default: false) */
30
+ enabled?: boolean;
31
+ /** Number of failures within window to trip circuit (default: 5) */
32
+ failureThreshold?: number;
33
+ /** Milliseconds to wait in open state before half-open (default: 30000) */
34
+ resetTimeout?: number;
35
+ /** Number of successful requests in half-open to close (default: 3) */
36
+ halfOpenRequests?: number;
37
+ /** Sliding window duration in milliseconds (default: 60000) */
38
+ windowDuration?: number;
39
+ /** Behavior when circuit is open */
40
+ fallback?: CircuitBreakerFallback;
41
+ }
42
+ /**
43
+ * Fallback behavior when circuit is open.
44
+ *
45
+ * - `fail`: Fail immediately with CIRCUIT_BREAKER_OPEN error (default)
46
+ * - `skip`: Skip node, pass input through as output
47
+ * - `default`: Return a configured default value
48
+ */
49
+ type CircuitBreakerFallback = {
50
+ type: "fail";
51
+ } | {
52
+ type: "skip";
53
+ passThrough: true;
54
+ } | {
55
+ type: "default";
56
+ value: unknown;
57
+ };
58
+ /**
59
+ * Event emitted when circuit state changes.
60
+ */
61
+ interface CircuitBreakerEvent {
62
+ nodeType: string;
63
+ previousState: CircuitBreakerState;
64
+ newState: CircuitBreakerState;
65
+ timestamp: number;
66
+ failureCount?: number;
67
+ }
68
+ /**
69
+ * Callback type for circuit state change events.
70
+ */
71
+ type CircuitBreakerEventHandler = (event: CircuitBreakerEvent) => Effect.Effect<void, never, never>;
72
+ /**
73
+ * Default circuit breaker configuration values.
74
+ */
75
+ declare const DEFAULT_CIRCUIT_BREAKER_CONFIG: Required<Omit<CircuitBreakerConfig, "fallback">> & {
76
+ fallback: CircuitBreakerFallback;
77
+ };
78
+ //#endregion
79
+ //#region src/flow/circuit-breaker-store.d.ts
80
+ /**
81
+ * Creates a CircuitBreakerStore backed by any BaseKvStore.
82
+ *
83
+ * This adapter wraps a generic KV store to provide circuit breaker state
84
+ * storage. It handles:
85
+ * - JSON serialization of state data
86
+ * - Sliding window expiry (checked on read/increment)
87
+ * - Read-modify-write for increment operations
88
+ *
89
+ * Note: This implementation uses read-modify-write for increments, which
90
+ * may have race conditions under high concurrency. This is acceptable for
91
+ * circuit breakers as they tolerate eventual consistency.
92
+ *
93
+ * @param baseStore - The underlying KV store
94
+ * @returns A CircuitBreakerStore implementation
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const baseStore = makeRedisBaseKvStore({ redis: redisClient });
99
+ * const cbStore = makeKvCircuitBreakerStore(baseStore);
100
+ *
101
+ * // Use the store
102
+ * yield* cbStore.incrementFailures("describe-image", 60000);
103
+ * ```
104
+ */
105
+ declare function makeKvCircuitBreakerStore(baseStore: BaseKvStore): CircuitBreakerStore;
106
+ /**
107
+ * Creates an in-memory CircuitBreakerStore.
108
+ *
109
+ * This implementation keeps all state in memory and is suitable for:
110
+ * - Single-instance deployments
111
+ * - Development and testing
112
+ * - Serverless functions (where state is ephemeral anyway)
113
+ *
114
+ * @returns A CircuitBreakerStore backed by in-memory Map
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const cbStore = makeMemoryCircuitBreakerStore();
119
+ *
120
+ * // Use for testing
121
+ * yield* cbStore.incrementFailures("test-node", 60000);
122
+ * const state = yield* cbStore.getState("test-node");
123
+ * ```
124
+ */
125
+ declare function makeMemoryCircuitBreakerStore(): CircuitBreakerStore;
126
+ /**
127
+ * Effect Layer that provides a CircuitBreakerStore backed by the BaseKvStore.
128
+ *
129
+ * Use this layer when you want circuit breaker state to be distributed
130
+ * across multiple instances (e.g., in a cluster).
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * const program = Effect.gen(function* () {
135
+ * const cbStore = yield* CircuitBreakerStoreService;
136
+ * // ...
137
+ * }).pipe(
138
+ * Effect.provide(kvCircuitBreakerStoreLayer),
139
+ * Effect.provide(redisKvStore({ redis: redisClient }))
140
+ * );
141
+ * ```
142
+ */
143
+ declare const kvCircuitBreakerStoreLayer: Layer.Layer<CircuitBreakerStoreService, never, BaseKvStoreService>;
144
+ /**
145
+ * Effect Layer that provides an in-memory CircuitBreakerStore.
146
+ *
147
+ * Use this layer for single-instance deployments, development, or testing.
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const program = Effect.gen(function* () {
152
+ * const cbStore = yield* CircuitBreakerStoreService;
153
+ * // ...
154
+ * }).pipe(
155
+ * Effect.provide(memoryCircuitBreakerStoreLayer)
156
+ * );
157
+ * ```
158
+ */
159
+ declare const memoryCircuitBreakerStoreLayer: Layer.Layer<CircuitBreakerStoreService, never, never>;
160
+ //#endregion
161
+ //#region src/flow/distributed-circuit-breaker.d.ts
162
+ /**
163
+ * Result of checking if a request is allowed.
164
+ */
165
+ interface AllowRequestResult {
166
+ allowed: boolean;
167
+ state: CircuitBreakerStateValue;
168
+ failureCount: number;
169
+ }
170
+ /**
171
+ * Distributed circuit breaker that uses a store for state persistence.
172
+ *
173
+ * Unlike the in-memory CircuitBreaker, this implementation stores all state
174
+ * in a CircuitBreakerStore, allowing multiple instances to share circuit state.
175
+ *
176
+ * All operations are Effect-based since they may involve I/O.
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * const breaker = new DistributedCircuitBreaker(
181
+ * "describe-image",
182
+ * { enabled: true, failureThreshold: 5 },
183
+ * store
184
+ * );
185
+ *
186
+ * // Check if request is allowed
187
+ * const { allowed, state } = yield* breaker.allowRequest();
188
+ * if (!allowed) {
189
+ * // Handle circuit open
190
+ * }
191
+ *
192
+ * // Record result
193
+ * try {
194
+ * const result = yield* executeNode();
195
+ * yield* breaker.recordSuccess();
196
+ * return result;
197
+ * } catch (error) {
198
+ * yield* breaker.recordFailure(error.message);
199
+ * throw error;
200
+ * }
201
+ * ```
202
+ */
203
+ declare class DistributedCircuitBreaker {
204
+ private eventHandler?;
205
+ readonly nodeType: string;
206
+ readonly config: Required<Omit<CircuitBreakerConfig, "fallback">> & {
207
+ fallback: CircuitBreakerFallback;
208
+ };
209
+ readonly store: CircuitBreakerStore;
210
+ constructor(nodeType: string, config: CircuitBreakerConfig, store: CircuitBreakerStore);
211
+ /**
212
+ * Sets the event handler for state change notifications.
213
+ */
214
+ setEventHandler(handler: CircuitBreakerEventHandler): void;
215
+ /**
216
+ * Checks if a request is allowed through the circuit.
217
+ *
218
+ * This method reads state from the store, checks for time-based transitions,
219
+ * and returns whether the request should proceed.
220
+ */
221
+ allowRequest(): Effect.Effect<AllowRequestResult, UploadistaError>;
222
+ /**
223
+ * Gets the current circuit state from the store.
224
+ */
225
+ getState(): Effect.Effect<CircuitBreakerStateValue, UploadistaError>;
226
+ /**
227
+ * Gets the current failure count from the store.
228
+ */
229
+ getFailureCount(): Effect.Effect<number, UploadistaError>;
230
+ /**
231
+ * Records a successful execution.
232
+ *
233
+ * In half-open state, tracks successes toward closing the circuit.
234
+ * In closed state, resets the failure count.
235
+ */
236
+ recordSuccess(): Effect.Effect<void, UploadistaError>;
237
+ /**
238
+ * Records a failed execution.
239
+ *
240
+ * In closed state, increments failure count and may trip the circuit.
241
+ * In half-open state, immediately reopens the circuit.
242
+ */
243
+ recordFailure(_errorMessage: string): Effect.Effect<void, UploadistaError>;
244
+ /**
245
+ * Gets the fallback configuration.
246
+ */
247
+ getFallback(): CircuitBreakerFallback;
248
+ /**
249
+ * Resets the circuit breaker to closed state.
250
+ */
251
+ reset(): Effect.Effect<void, UploadistaError>;
252
+ /**
253
+ * Transitions to a new state.
254
+ */
255
+ private transitionTo;
256
+ /**
257
+ * Emits a state change event if handler is set.
258
+ */
259
+ private emitEvent;
260
+ }
261
+ /**
262
+ * Registry for managing distributed circuit breakers.
263
+ *
264
+ * Unlike the in-memory CircuitBreakerRegistry, this registry creates
265
+ * DistributedCircuitBreaker instances that share state via a store.
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * const store = makeKvCircuitBreakerStore(baseKvStore);
270
+ * const registry = new DistributedCircuitBreakerRegistry(store);
271
+ *
272
+ * const breaker = registry.getOrCreate("describe-image", {
273
+ * enabled: true,
274
+ * failureThreshold: 5
275
+ * });
276
+ * ```
277
+ */
278
+ declare class DistributedCircuitBreakerRegistry {
279
+ readonly store: CircuitBreakerStore;
280
+ private breakers;
281
+ private eventHandler?;
282
+ constructor(store: CircuitBreakerStore);
283
+ /**
284
+ * Sets a global event handler for all circuit breakers.
285
+ */
286
+ setEventHandler(handler: CircuitBreakerEventHandler): void;
287
+ /**
288
+ * Gets an existing circuit breaker or creates a new one.
289
+ */
290
+ getOrCreate(nodeType: string, config: CircuitBreakerConfig): DistributedCircuitBreaker;
291
+ /**
292
+ * Gets an existing circuit breaker if it exists.
293
+ */
294
+ get(nodeType: string): DistributedCircuitBreaker | undefined;
295
+ /**
296
+ * Gets statistics for all circuit breakers from the store.
297
+ */
298
+ getAllStats(): Effect.Effect<Map<string, {
299
+ state: CircuitBreakerStateValue;
300
+ failureCount: number;
301
+ }>, UploadistaError>;
302
+ /**
303
+ * Resets all circuit breakers.
304
+ */
305
+ resetAll(): Effect.Effect<void, UploadistaError>;
306
+ /**
307
+ * Clears all circuit breakers from the local cache.
308
+ * Note: This does not clear state from the store.
309
+ */
310
+ clear(): void;
311
+ }
312
+ //#endregion
313
+ //#region src/flow/edge.d.ts
314
+ /**
315
+ * Represents a connection between two nodes in a flow, defining the data flow direction.
316
+ *
317
+ * Edges connect the output of a source node to the input of a target node,
318
+ * enabling data to flow through the processing pipeline in a directed acyclic graph (DAG).
319
+ */
320
+ type FlowEdge = FlowEdge$1;
321
+ /**
322
+ * Creates a flow edge connecting two nodes in a processing pipeline.
323
+ *
324
+ * Edges define how data flows between nodes. The data output from the source node
325
+ * becomes the input for the target node. For nodes with multiple inputs/outputs,
326
+ * ports can be specified to route data to specific connections.
327
+ *
328
+ * @param config - Edge configuration
329
+ * @param config.source - ID of the source node (data originates here)
330
+ * @param config.target - ID of the target node (data flows to here)
331
+ * @param config.sourcePort - Optional port name on the source node for multi-output nodes
332
+ * @param config.targetPort - Optional port name on the target node for multi-input nodes
333
+ *
334
+ * @returns A FlowEdge object representing the connection
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * // Simple edge connecting two nodes
339
+ * const edge = createFlowEdge({
340
+ * source: "input-1",
341
+ * target: "process-1"
342
+ * });
343
+ *
344
+ * // Edge with ports for multi-input/output nodes
345
+ * const portEdge = createFlowEdge({
346
+ * source: "multiplex-1",
347
+ * target: "merge-1",
348
+ * sourcePort: "out-a",
349
+ * targetPort: "in-1"
350
+ * });
351
+ * ```
352
+ */
353
+ declare function createFlowEdge({
354
+ source,
355
+ target,
356
+ sourcePort,
357
+ targetPort
358
+ }: {
359
+ source: string;
360
+ target: string;
361
+ sourcePort?: string;
362
+ targetPort?: string;
363
+ }): FlowEdge;
364
+ //#endregion
365
+ //#region src/flow/flow.d.ts
366
+ /**
367
+ * Serialized flow data for storage and transport.
368
+ * Contains the minimal information needed to reconstruct a flow.
369
+ *
370
+ * @property id - Unique flow identifier
371
+ * @property name - Human-readable flow name
372
+ * @property nodes - Array of node data (without execution logic)
373
+ * @property edges - Connections between nodes defining data flow
374
+ */
375
+ type FlowData = {
376
+ id: string;
377
+ name: string;
378
+ nodes: FlowNodeData[];
379
+ edges: FlowEdge[];
380
+ };
381
+ /**
382
+ * Extracts serializable flow data from a Flow instance.
383
+ * Useful for storing flow definitions or sending them over the network.
384
+ *
385
+ * @template TRequirements - Effect requirements for the flow
386
+ * @param flow - Flow instance to extract data from
387
+ * @returns Serializable flow data without execution logic
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * const flowData = getFlowData(myFlow);
392
+ * // Store in database or send to client
393
+ * await db.flows.save(flowData);
394
+ * ```
395
+ */
396
+ declare const getFlowData: <TRequirements>(flow: Flow<any, any, TRequirements>) => FlowData;
397
+ /**
398
+ * Result of a flow execution - either completed or paused.
399
+ *
400
+ * @template TOutput - Type of the flow's output data
401
+ *
402
+ * @remarks
403
+ * Flows can pause when a node needs additional data (e.g., waiting for user input
404
+ * or external service). The execution state allows resuming from where it paused.
405
+ *
406
+ * @example
407
+ * ```typescript
408
+ * const result = await Effect.runPromise(flow.run({ inputs, storageId, jobId }));
409
+ *
410
+ * if (result.type === "completed") {
411
+ * console.log("Flow completed:", result.result);
412
+ * } else {
413
+ * console.log("Flow paused at node:", result.nodeId);
414
+ * // Can resume later with: flow.resume({ jobId, executionState: result.executionState, ... })
415
+ * }
416
+ * ```
417
+ */
418
+ type FlowExecutionResult<TOutput> = {
419
+ type: "completed";
420
+ result: TOutput;
421
+ outputs?: TypedOutput[];
422
+ } | {
423
+ type: "paused";
424
+ nodeId: string;
425
+ executionState: {
426
+ executionOrder: string[];
427
+ currentIndex: number;
428
+ inputs: Record<string, unknown>;
429
+ };
430
+ };
431
+ /**
432
+ * A Flow represents a directed acyclic graph (DAG) of processing nodes.
433
+ *
434
+ * Flows execute nodes in topological order, passing data between nodes through edges.
435
+ * They support conditional execution, retry logic, pausable nodes, and event emission.
436
+ *
437
+ * @template TFlowInputSchema - Zod schema defining the shape of input data
438
+ * @template TFlowOutputSchema - Zod schema defining the shape of output data
439
+ * @template TRequirements - Effect requirements (services/contexts) needed by nodes
440
+ *
441
+ * @property id - Unique flow identifier
442
+ * @property name - Human-readable flow name
443
+ * @property nodes - Array of nodes in the flow
444
+ * @property edges - Connections between nodes
445
+ * @property inputSchema - Zod schema for validating flow inputs
446
+ * @property outputSchema - Zod schema for validating flow outputs
447
+ * @property onEvent - Optional callback for flow execution events
448
+ * @property run - Executes the flow from the beginning
449
+ * @property resume - Resumes a paused flow execution
450
+ * @property validateTypes - Validates node type compatibility
451
+ * @property validateInputs - Validates input data against schema
452
+ * @property validateOutputs - Validates output data against schema
453
+ *
454
+ * @remarks
455
+ * Flows are created using {@link createFlowWithSchema}. The Effect-based design
456
+ * allows for composable error handling, resource management, and dependency injection.
457
+ *
458
+ * @example
459
+ * ```typescript
460
+ * const flow = yield* createFlowWithSchema({
461
+ * flowId: "image-pipeline",
462
+ * name: "Image Processing Pipeline",
463
+ * nodes: [inputNode, resizeNode, optimizeNode, storageNode],
464
+ * edges: [
465
+ * { source: "input", target: "resize" },
466
+ * { source: "resize", target: "optimize" },
467
+ * { source: "optimize", target: "storage" }
468
+ * ],
469
+ * inputSchema: z.object({ file: z.instanceof(File) }),
470
+ * outputSchema: uploadFileSchema
471
+ * });
472
+ *
473
+ * const result = yield* flow.run({
474
+ * inputs: { input: { file: myFile } },
475
+ * storageId: "storage-1",
476
+ * jobId: "job-123"
477
+ * });
478
+ * ```
479
+ */
480
+ type Flow<TFlowInputSchema extends z.ZodSchema<any>, TFlowOutputSchema extends z.ZodSchema<any>, TRequirements> = {
481
+ id: string;
482
+ name: string;
483
+ nodes: FlowNode<any, any, UploadistaError>[];
484
+ edges: FlowEdge[];
485
+ inputSchema: TFlowInputSchema;
486
+ outputSchema: TFlowOutputSchema;
487
+ onEvent?: FlowConfig<TFlowInputSchema, TFlowOutputSchema, TRequirements>["onEvent"];
488
+ checkJobStatus?: FlowConfig<TFlowInputSchema, TFlowOutputSchema, TRequirements>["checkJobStatus"];
489
+ hooks?: FlowConfig<TFlowInputSchema, TFlowOutputSchema, TRequirements>["hooks"];
490
+ run: (args: {
491
+ inputs?: Record<string, z.infer<TFlowInputSchema>>;
492
+ storageId: string;
493
+ jobId: string;
494
+ clientId: string | null;
495
+ }) => Effect.Effect<FlowExecutionResult<Record<string, z.infer<TFlowOutputSchema>>>, UploadistaError, TRequirements | UploadFileDataStores>;
496
+ resume: (args: {
497
+ jobId: string;
498
+ storageId: string;
499
+ nodeResults: Record<string, unknown>;
500
+ executionState: {
501
+ executionOrder: string[];
502
+ currentIndex: number;
503
+ inputs: Record<string, z.infer<TFlowInputSchema>>;
504
+ };
505
+ clientId: string | null;
506
+ }) => Effect.Effect<FlowExecutionResult<Record<string, z.infer<TFlowOutputSchema>>>, UploadistaError, TRequirements | UploadFileDataStores>;
507
+ validateTypes: () => {
508
+ isValid: boolean;
509
+ errors: string[];
510
+ };
511
+ validateInputs: (inputs: unknown) => {
512
+ isValid: boolean;
513
+ errors: string[];
514
+ };
515
+ validateOutputs: (outputs: unknown) => {
516
+ isValid: boolean;
517
+ errors: string[];
518
+ };
519
+ };
520
+ /**
521
+ * Creates a new Flow with Zod schema-based type validation.
522
+ *
523
+ * This is the primary way to create flows in Uploadista. It constructs a Flow
524
+ * instance that validates inputs/outputs, executes nodes in topological order,
525
+ * handles errors with retries, and emits events during execution.
526
+ *
527
+ * @template TFlowInputSchema - Zod schema for flow input validation
528
+ * @template TFlowOutputSchema - Zod schema for flow output validation
529
+ * @template TRequirements - Effect requirements/services needed by the flow
530
+ * @template TNodeError - Union of possible errors from nodes
531
+ * @template TNodeRequirements - Union of requirements from nodes
532
+ *
533
+ * @param config - Flow configuration object
534
+ * @param config.flowId - Unique identifier for the flow
535
+ * @param config.name - Human-readable flow name
536
+ * @param config.nodes - Array of nodes (can be plain nodes or Effects resolving to nodes)
537
+ * @param config.edges - Array of edges connecting nodes
538
+ * @param config.inputSchema - Zod schema for validating inputs
539
+ * @param config.outputSchema - Zod schema for validating outputs
540
+ * @param config.typeChecker - Optional custom type compatibility checker
541
+ * @param config.onEvent - Optional event callback for monitoring execution
542
+ *
543
+ * @returns Effect that resolves to a Flow instance
544
+ *
545
+ * @throws {UploadistaError} FLOW_CYCLE_ERROR if the graph contains cycles
546
+ * @throws {UploadistaError} FLOW_NODE_NOT_FOUND if a node is referenced but missing
547
+ * @throws {UploadistaError} FLOW_NODE_ERROR if node execution fails
548
+ * @throws {UploadistaError} FLOW_OUTPUT_VALIDATION_ERROR if outputs don't match schema
549
+ *
550
+ * @remarks
551
+ * - Nodes can be provided as plain objects or as Effects that resolve to nodes
552
+ * - The flow performs topological sorting to determine execution order
553
+ * - Conditional nodes are evaluated before execution
554
+ * - Nodes can specify retry configuration with exponential backoff
555
+ * - Pausable nodes can halt execution and resume later
556
+ *
557
+ * @example
558
+ * ```typescript
559
+ * const flow = yield* createFlowWithSchema({
560
+ * flowId: "image-upload",
561
+ * name: "Image Upload with Processing",
562
+ * nodes: [
563
+ * inputNode,
564
+ * yield* createResizeNode({ width: 1920, height: 1080 }),
565
+ * optimizeNode,
566
+ * storageNode
567
+ * ],
568
+ * edges: [
569
+ * { source: "input", target: "resize" },
570
+ * { source: "resize", target: "optimize" },
571
+ * { source: "optimize", target: "storage" }
572
+ * ],
573
+ * inputSchema: z.object({
574
+ * file: z.instanceof(File),
575
+ * metadata: z.record(z.string(), z.any()).optional()
576
+ * }),
577
+ * outputSchema: uploadFileSchema,
578
+ * onEvent: (event) => Effect.gen(function* () {
579
+ * console.log("Flow event:", event);
580
+ * return { eventId: event.jobId };
581
+ * })
582
+ * });
583
+ * ```
584
+ *
585
+ * @see {@link Flow} for the returned flow type
586
+ * @see {@link FlowConfig} for configuration options
587
+ */
588
+ declare function createFlowWithSchema<TFlowInputSchema extends z.ZodSchema<any>, TFlowOutputSchema extends z.ZodSchema<any>, TRequirements = never, TNodeError = never, TNodeRequirements = never>(config: FlowConfig<TFlowInputSchema, TFlowOutputSchema, TNodeError, TNodeRequirements>): Effect.Effect<Flow<TFlowInputSchema, TFlowOutputSchema, TRequirements>, TNodeError, TNodeRequirements>;
589
+ //#endregion
590
+ //#region src/flow/input-type-registry.d.ts
591
+ /**
592
+ * Defines a registered input type with its schema and metadata.
593
+ *
594
+ * Input type definitions describe how external clients interact with input nodes.
595
+ * Unlike output types, input types define the external interface (e.g., init/finalize
596
+ * operations for streaming uploads).
597
+ *
598
+ * @template TSchema - The Zod schema type for this input's data
599
+ *
600
+ * @property id - Unique identifier (e.g., "streaming-input-v1")
601
+ * @property schema - Zod schema for validating input data from clients
602
+ * @property version - Semantic version (e.g., "1.0.0") for tracking type evolution
603
+ * @property description - Human-readable explanation of what this input type does
604
+ */
605
+ interface InputTypeDefinition<TSchema = unknown> {
606
+ id: string;
607
+ schema: z.ZodSchema<TSchema>;
608
+ version: string;
609
+ description: string;
610
+ }
611
+ /**
612
+ * Result type for input validation operations.
613
+ *
614
+ * @template T - The expected type on successful validation
615
+ */
616
+ type InputValidationResult<T> = {
617
+ success: true;
618
+ data: T;
619
+ } | {
620
+ success: false;
621
+ error: UploadistaError;
622
+ };
623
+ /**
624
+ * Registry for input node type definitions.
625
+ *
626
+ * The InputTypeRegistry maintains a global registry of input types with their schemas
627
+ * and metadata. Input types describe how data enters the flow from external sources.
628
+ *
629
+ * @remarks
630
+ * - Use the exported `inputTypeRegistry` singleton instance
631
+ * - Types cannot be unregistered or modified after registration
632
+ * - Duplicate type IDs are rejected
633
+ *
634
+ * @example
635
+ * ```typescript
636
+ * // Register a new input type
637
+ * inputTypeRegistry.register({
638
+ * id: "form-input-v1",
639
+ * schema: formInputSchema,
640
+ * version: "1.0.0",
641
+ * description: "Form-based file input",
642
+ * });
643
+ *
644
+ * // Check if type exists
645
+ * if (inputTypeRegistry.has("streaming-input-v1")) {
646
+ * const def = inputTypeRegistry.get("streaming-input-v1");
647
+ * }
648
+ * ```
649
+ */
650
+ declare class InputTypeRegistry {
651
+ private readonly types;
652
+ constructor();
653
+ /**
654
+ * Register a new input type in the registry.
655
+ *
656
+ * @template T - The TypeScript type inferred from the Zod schema
657
+ * @param definition - The complete type definition including schema and metadata
658
+ * @throws {UploadistaError} If a type with the same ID is already registered
659
+ */
660
+ register<T>(definition: InputTypeDefinition<T>): void;
661
+ /**
662
+ * Retrieve a registered type definition by its ID.
663
+ *
664
+ * @param id - The unique type identifier (e.g., "streaming-input-v1")
665
+ * @returns The type definition if found, undefined otherwise
666
+ */
667
+ get(id: string): InputTypeDefinition<unknown> | undefined;
668
+ /**
669
+ * List all registered input types.
670
+ *
671
+ * @returns Array of all input type definitions
672
+ */
673
+ list(): InputTypeDefinition<unknown>[];
674
+ /**
675
+ * Validate data against a registered type's schema.
676
+ *
677
+ * @template T - The expected TypeScript type after validation
678
+ * @param typeId - The ID of the registered type to validate against
679
+ * @param data - The data to validate
680
+ * @returns A result object with either the validated data or an error
681
+ */
682
+ validate<T>(typeId: string, data: unknown): InputValidationResult<T>;
683
+ /**
684
+ * Check if a type is registered.
685
+ *
686
+ * @param id - The unique type identifier to check
687
+ * @returns True if the type is registered, false otherwise
688
+ */
689
+ has(id: string): boolean;
690
+ /**
691
+ * Get the total number of registered types.
692
+ *
693
+ * @returns The count of registered types
694
+ */
695
+ size(): number;
696
+ }
697
+ /**
698
+ * Global singleton instance of the input type registry.
699
+ *
700
+ * Use this instance to register and access input node type definitions.
701
+ * Input types describe how data enters the flow from external sources.
702
+ *
703
+ * @example
704
+ * ```typescript
705
+ * import { inputTypeRegistry } from "@uploadista/core/flow";
706
+ *
707
+ * // Register a type
708
+ * inputTypeRegistry.register({
709
+ * id: "my-input-v1",
710
+ * schema: myInputSchema,
711
+ * version: "1.0.0",
712
+ * description: "My custom input type",
713
+ * });
714
+ *
715
+ * // Validate data
716
+ * const result = inputTypeRegistry.validate("my-input-v1", data);
717
+ * ```
718
+ */
719
+ declare const inputTypeRegistry: InputTypeRegistry;
720
+ /**
721
+ * Validates flow input data against a registered input type.
722
+ *
723
+ * @param typeId - The registered type ID (e.g., "streaming-input-v1")
724
+ * @param data - The input data to validate
725
+ * @returns A validation result with either the typed data or an error
726
+ */
727
+ declare function validateFlowInput<T = unknown>(typeId: string, data: unknown): InputValidationResult<T>;
728
+ //#endregion
729
+ //#region src/flow/output-type-registry.d.ts
730
+ /**
731
+ * Defines a registered output type with its schema and metadata.
732
+ *
733
+ * Output type definitions describe the data shapes produced by nodes. This enables
734
+ * type-safe result consumption where clients can narrow types based on the
735
+ * `nodeType` field in results.
736
+ *
737
+ * @template TSchema - The Zod schema type for this output's data
738
+ *
739
+ * @property id - Unique identifier (e.g., "storage-output-v1", "ocr-output-v1")
740
+ * @property schema - Zod schema for validating output data
741
+ * @property version - Semantic version (e.g., "1.0.0") for tracking type evolution
742
+ * @property description - Human-readable explanation of what this output type contains
743
+ */
744
+ interface OutputTypeDefinition<TSchema = unknown> {
745
+ id: string;
746
+ schema: z.ZodSchema<TSchema>;
747
+ version: string;
748
+ description: string;
749
+ }
750
+ /**
751
+ * Result type for output validation operations.
752
+ *
753
+ * @template T - The expected type on successful validation
754
+ */
755
+ type OutputValidationResult<T> = {
756
+ success: true;
757
+ data: T;
758
+ } | {
759
+ success: false;
760
+ error: UploadistaError;
761
+ };
762
+ /**
763
+ * Registry for output node type definitions.
764
+ *
765
+ * The OutputTypeRegistry maintains a global registry of output types with their schemas
766
+ * and metadata. Output types describe the data shapes that flow through the system
767
+ * and appear in results.
768
+ *
769
+ * @remarks
770
+ * - Use the exported `outputTypeRegistry` singleton instance
771
+ * - Types cannot be unregistered or modified after registration
772
+ * - Duplicate type IDs are rejected
773
+ *
774
+ * @example
775
+ * ```typescript
776
+ * // Register a new output type
777
+ * outputTypeRegistry.register({
778
+ * id: "metadata-output-v1",
779
+ * schema: metadataSchema,
780
+ * version: "1.0.0",
781
+ * description: "File metadata extraction output",
782
+ * });
783
+ *
784
+ * // Validate result data
785
+ * const result = outputTypeRegistry.validate("storage-output-v1", data);
786
+ * if (result.success) {
787
+ * console.log(result.data.url);
788
+ * }
789
+ * ```
790
+ */
791
+ declare class OutputTypeRegistry {
792
+ private readonly types;
793
+ constructor();
794
+ /**
795
+ * Register a new output type in the registry.
796
+ *
797
+ * @template T - The TypeScript type inferred from the Zod schema
798
+ * @param definition - The complete type definition including schema and metadata
799
+ * @throws {UploadistaError} If a type with the same ID is already registered
800
+ */
801
+ register<T>(definition: OutputTypeDefinition<T>): void;
802
+ /**
803
+ * Retrieve a registered type definition by its ID.
804
+ *
805
+ * @param id - The unique type identifier (e.g., "storage-output-v1")
806
+ * @returns The type definition if found, undefined otherwise
807
+ */
808
+ get(id: string): OutputTypeDefinition<unknown> | undefined;
809
+ /**
810
+ * List all registered output types.
811
+ *
812
+ * @returns Array of all output type definitions
813
+ */
814
+ list(): OutputTypeDefinition<unknown>[];
815
+ /**
816
+ * Validate data against a registered type's schema.
817
+ *
818
+ * @template T - The expected TypeScript type after validation
819
+ * @param typeId - The ID of the registered type to validate against
820
+ * @param data - The data to validate
821
+ * @returns A result object with either the validated data or an error
822
+ */
823
+ validate<T>(typeId: string, data: unknown): OutputValidationResult<T>;
824
+ /**
825
+ * Check if a type is registered.
826
+ *
827
+ * @param id - The unique type identifier to check
828
+ * @returns True if the type is registered, false otherwise
829
+ */
830
+ has(id: string): boolean;
831
+ /**
832
+ * Get the total number of registered types.
833
+ *
834
+ * @returns The count of registered types
835
+ */
836
+ size(): number;
837
+ }
838
+ /**
839
+ * Global singleton instance of the output type registry.
840
+ *
841
+ * Use this instance to register and access output node type definitions.
842
+ * Output types describe the data shapes produced by nodes and used in results.
843
+ *
844
+ * @example
845
+ * ```typescript
846
+ * import { outputTypeRegistry } from "@uploadista/core/flow";
847
+ *
848
+ * // Register a type
849
+ * outputTypeRegistry.register({
850
+ * id: "my-output-v1",
851
+ * schema: myOutputSchema,
852
+ * version: "1.0.0",
853
+ * description: "My custom output type",
854
+ * });
855
+ *
856
+ * // Validate result data
857
+ * const result = outputTypeRegistry.validate("my-output-v1", data);
858
+ * ```
859
+ */
860
+ declare const outputTypeRegistry: OutputTypeRegistry;
861
+ /**
862
+ * Validates flow output data against a registered output type.
863
+ *
864
+ * @param typeId - The registered type ID (e.g., "storage-output-v1")
865
+ * @param data - The output data to validate
866
+ * @returns A validation result with either the typed data or an error
867
+ */
868
+ declare function validateFlowOutput<T = unknown>(typeId: string, data: unknown): OutputValidationResult<T>;
869
+ //#endregion
870
+ //#region src/flow/node-types/index.d.ts
871
+ /**
872
+ * Type ID constants for built-in node types.
873
+ *
874
+ * Use these constants when creating nodes with type information to ensure
875
+ * consistency and avoid typos.
876
+ *
877
+ * @example
878
+ * ```typescript
879
+ * import { STREAMING_INPUT_TYPE_ID, STORAGE_OUTPUT_TYPE_ID } from "@uploadista/core/flow";
880
+ *
881
+ * const inputNode = createFlowNode({
882
+ * // ... other config
883
+ * inputTypeId: STREAMING_INPUT_TYPE_ID,
884
+ * outputTypeId: STORAGE_OUTPUT_TYPE_ID,
885
+ * });
886
+ * ```
887
+ */
888
+ declare const STORAGE_OUTPUT_TYPE_ID = "storage-output-v1";
889
+ declare const OCR_OUTPUT_TYPE_ID = "ocr-output-v1";
890
+ declare const IMAGE_DESCRIPTION_OUTPUT_TYPE_ID = "image-description-output-v1";
891
+ declare const STREAMING_INPUT_TYPE_ID = "streaming-input-v1";
892
+ /**
893
+ * OCR output schema - structured text extraction result.
894
+ *
895
+ * @property extractedText - The text extracted from the document
896
+ * @property format - Output format (text, markdown, or JSON)
897
+ * @property taskType - Type of OCR task performed
898
+ * @property confidence - Optional confidence score (0-1)
899
+ */
900
+ declare const ocrOutputSchema: z.ZodObject<{
901
+ extractedText: z.ZodString;
902
+ format: z.ZodEnum<{
903
+ markdown: "markdown";
904
+ plain: "plain";
905
+ structured: "structured";
906
+ }>;
907
+ taskType: z.ZodEnum<{
908
+ convertToMarkdown: "convertToMarkdown";
909
+ freeOcr: "freeOcr";
910
+ parseFigure: "parseFigure";
911
+ locateObject: "locateObject";
912
+ }>;
913
+ confidence: z.ZodOptional<z.ZodNumber>;
914
+ }, z.core.$strip>;
915
+ type OcrOutput = z.infer<typeof ocrOutputSchema>;
916
+ /**
917
+ * Image description output schema - AI-generated image analysis result.
918
+ *
919
+ * @property description - Human-readable description of the image
920
+ * @property confidence - Confidence score for the description (0-1)
921
+ * @property metadata - Additional metadata about the description
922
+ */
923
+ declare const imageDescriptionOutputSchema: z.ZodObject<{
924
+ description: z.ZodString;
925
+ confidence: z.ZodOptional<z.ZodNumber>;
926
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
927
+ }, z.core.$strip>;
928
+ type ImageDescriptionOutput = z.infer<typeof imageDescriptionOutputSchema>;
929
+ //#endregion
930
+ //#region src/flow/dead-letter-queue.d.ts
931
+ /**
932
+ * Shape of the Dead Letter Queue service.
933
+ *
934
+ * Provides all operations for managing failed flow jobs including
935
+ * adding items, querying, retrying, and cleanup.
936
+ */
937
+ interface DeadLetterQueueServiceShape {
938
+ /**
939
+ * Add a failed job to the DLQ with full failure context.
940
+ *
941
+ * @param job - The failed flow job
942
+ * @param error - The error that caused the failure
943
+ * @param retryPolicy - Optional retry policy (uses default if not provided)
944
+ * @returns The created DLQ item
945
+ */
946
+ add(job: FlowJob, error: UploadistaError, retryPolicy?: RetryPolicy): Effect.Effect<DeadLetterItem, UploadistaError>;
947
+ /**
948
+ * Get a specific DLQ item by ID.
949
+ *
950
+ * @param itemId - The DLQ item ID
951
+ * @returns The DLQ item
952
+ */
953
+ get(itemId: string): Effect.Effect<DeadLetterItem, UploadistaError>;
954
+ /**
955
+ * Get a DLQ item by ID, returning None if not found.
956
+ *
957
+ * @param itemId - The DLQ item ID
958
+ * @returns Option of the DLQ item
959
+ */
960
+ getOption(itemId: string): Effect.Effect<Option.Option<DeadLetterItem>, UploadistaError>;
961
+ /**
962
+ * Delete a DLQ item.
963
+ *
964
+ * @param itemId - The DLQ item ID to delete
965
+ */
966
+ delete(itemId: string): Effect.Effect<void, UploadistaError>;
967
+ /**
968
+ * List DLQ items with optional filtering and pagination.
969
+ *
970
+ * @param options - Filter and pagination options
971
+ * @returns List of items and total count
972
+ */
973
+ list(options?: DeadLetterListOptions): Effect.Effect<{
974
+ items: DeadLetterItem[];
975
+ total: number;
976
+ }, UploadistaError>;
977
+ /**
978
+ * Update a DLQ item.
979
+ *
980
+ * @param itemId - The DLQ item ID
981
+ * @param updates - Partial updates to apply
982
+ * @returns The updated item
983
+ */
984
+ update(itemId: string, updates: Partial<DeadLetterItem>): Effect.Effect<DeadLetterItem, UploadistaError>;
985
+ /**
986
+ * Mark a DLQ item as being retried.
987
+ *
988
+ * @param itemId - The DLQ item ID
989
+ * @returns The updated item with status "retrying"
990
+ */
991
+ markRetrying(itemId: string): Effect.Effect<DeadLetterItem, UploadistaError>;
992
+ /**
993
+ * Record a failed retry attempt.
994
+ *
995
+ * @param itemId - The DLQ item ID
996
+ * @param error - Error message from the failed retry
997
+ * @param durationMs - Duration of the retry attempt
998
+ * @returns The updated item
999
+ */
1000
+ recordRetryFailure(itemId: string, error: string, durationMs: number): Effect.Effect<DeadLetterItem, UploadistaError>;
1001
+ /**
1002
+ * Mark a DLQ item as resolved (successfully retried or manually resolved).
1003
+ *
1004
+ * @param itemId - The DLQ item ID
1005
+ * @returns The updated item with status "resolved"
1006
+ */
1007
+ markResolved(itemId: string): Effect.Effect<DeadLetterItem, UploadistaError>;
1008
+ /**
1009
+ * Get items that are due for scheduled retry.
1010
+ *
1011
+ * @param limit - Maximum number of items to return
1012
+ * @returns List of items ready for retry
1013
+ */
1014
+ getScheduledRetries(limit?: number): Effect.Effect<DeadLetterItem[], UploadistaError>;
1015
+ /**
1016
+ * Cleanup old DLQ items based on options.
1017
+ *
1018
+ * @param options - Cleanup criteria
1019
+ * @returns Number of items deleted
1020
+ */
1021
+ cleanup(options?: DeadLetterCleanupOptions): Effect.Effect<DeadLetterCleanupResult, UploadistaError>;
1022
+ /**
1023
+ * Get DLQ statistics.
1024
+ *
1025
+ * @returns Aggregate statistics about the DLQ
1026
+ */
1027
+ getStats(): Effect.Effect<DeadLetterQueueStats, UploadistaError>;
1028
+ }
1029
+ declare const DeadLetterQueueService_base: Context.TagClass<DeadLetterQueueService, "DeadLetterQueueService", DeadLetterQueueServiceShape>;
1030
+ /**
1031
+ * Effect-TS context tag for the Dead Letter Queue service.
1032
+ *
1033
+ * @example
1034
+ * ```typescript
1035
+ * const effect = Effect.gen(function* () {
1036
+ * const dlq = yield* DeadLetterQueueService;
1037
+ * const stats = yield* dlq.getStats();
1038
+ * console.log(`DLQ has ${stats.totalItems} items`);
1039
+ * });
1040
+ * ```
1041
+ */
1042
+ declare class DeadLetterQueueService extends DeadLetterQueueService_base {
1043
+ /**
1044
+ * Access the DLQ service optionally (for integration in FlowServer).
1045
+ * Returns Option.none if the service is not provided.
1046
+ */
1047
+ static optional: Effect.Effect<Option.Option<DeadLetterQueueServiceShape>, never, never>;
1048
+ }
1049
+ /**
1050
+ * Creates the Dead Letter Queue service implementation.
1051
+ *
1052
+ * @returns Effect that creates the DLQ service
1053
+ */
1054
+ declare function createDeadLetterQueueService(): Effect.Effect<DeadLetterQueueServiceShape, never, DeadLetterQueueKVStore>;
1055
+ /**
1056
+ * Effect Layer that creates the DeadLetterQueueService.
1057
+ *
1058
+ * @example
1059
+ * ```typescript
1060
+ * const program = Effect.gen(function* () {
1061
+ * const dlq = yield* DeadLetterQueueService;
1062
+ * const stats = yield* dlq.getStats();
1063
+ * return stats;
1064
+ * }).pipe(
1065
+ * Effect.provide(deadLetterQueueService),
1066
+ * Effect.provide(deadLetterQueueKvStore),
1067
+ * Effect.provide(baseStoreLayer)
1068
+ * );
1069
+ * ```
1070
+ */
1071
+ declare const deadLetterQueueService: Layer.Layer<DeadLetterQueueService, never, DeadLetterQueueKVStore>;
1072
+ //#endregion
1073
+ //#region src/flow/flow-queue-store.d.ts
1074
+ /**
1075
+ * Adapter interface for flow queue item persistence.
1076
+ *
1077
+ * Implementations must provide CRUD operations for FlowQueueItems and an
1078
+ * efficient status-based listing. Redis implementations use sorted sets and
1079
+ * sets for O(log n) lookups; the memory implementation uses a plain Map.
1080
+ *
1081
+ * @example
1082
+ * ```typescript
1083
+ * // Provide a custom store via the Effect layer
1084
+ * const customStore: FlowQueueStore = { ... };
1085
+ * ```
1086
+ */
1087
+ interface FlowQueueStore {
1088
+ /**
1089
+ * Persist a new queue item.
1090
+ *
1091
+ * @param item - The fully-constructed FlowQueueItem to store
1092
+ * @returns The stored item
1093
+ */
1094
+ createItem(item: FlowQueueItem): Effect.Effect<FlowQueueItem, UploadistaError>;
1095
+ /**
1096
+ * Retrieve a queue item by ID.
1097
+ *
1098
+ * @param id - The queue item ID
1099
+ * @returns The item, or null if not found
1100
+ */
1101
+ getItem(id: string): Effect.Effect<FlowQueueItem | null, UploadistaError>;
1102
+ /**
1103
+ * Apply partial updates to an existing queue item.
1104
+ *
1105
+ * The implementation must atomically update the item and any status-based
1106
+ * indexes (e.g., Redis sorted sets) when status changes.
1107
+ *
1108
+ * @param id - The queue item ID
1109
+ * @param updates - Fields to update (merged over the existing item)
1110
+ * @returns The fully-updated item
1111
+ */
1112
+ updateItem(id: string, updates: Partial<FlowQueueItem>): Effect.Effect<FlowQueueItem, UploadistaError>;
1113
+ /**
1114
+ * List all items with a specific status.
1115
+ *
1116
+ * For "pending" items, results SHOULD be returned in FIFO order (oldest first)
1117
+ * so that the worker loop dispatches items in enqueuedAt order.
1118
+ *
1119
+ * @param status - The status to filter by
1120
+ * @returns Array of matching items
1121
+ */
1122
+ listByStatus(status: FlowQueueItemStatus): Effect.Effect<FlowQueueItem[], UploadistaError>;
1123
+ /**
1124
+ * Remove a queue item from the store.
1125
+ *
1126
+ * @param id - The queue item ID to remove
1127
+ */
1128
+ deleteItem(id: string): Effect.Effect<void, UploadistaError>;
1129
+ }
1130
+ /**
1131
+ * In-memory implementation of FlowQueueStore.
1132
+ *
1133
+ * Uses a plain Map for storage. Items are not persisted across process restarts.
1134
+ * Suitable for single-process deployments or development/testing.
1135
+ *
1136
+ * For durability across restarts, use RedisFlowQueueStore or IoRedisFlowQueueStore.
1137
+ *
1138
+ * @example
1139
+ * ```typescript
1140
+ * const store = new MemoryFlowQueueStore();
1141
+ * // Pass to FlowQueueService.make(config, store)
1142
+ * ```
1143
+ */
1144
+ declare class MemoryFlowQueueStore implements FlowQueueStore {
1145
+ private readonly items;
1146
+ createItem(item: FlowQueueItem): Effect.Effect<FlowQueueItem, UploadistaError>;
1147
+ getItem(id: string): Effect.Effect<FlowQueueItem | null, UploadistaError>;
1148
+ updateItem(id: string, updates: Partial<FlowQueueItem>): Effect.Effect<FlowQueueItem, UploadistaError>;
1149
+ listByStatus(status: FlowQueueItemStatus): Effect.Effect<FlowQueueItem[], UploadistaError>;
1150
+ deleteItem(id: string): Effect.Effect<void, UploadistaError>;
1151
+ }
1152
+ //#endregion
1153
+ //#region src/flow/flow-engine.d.ts
1154
+ /**
1155
+ * WaitUntil callback type for keeping background tasks alive.
1156
+ * Used in serverless environments like Cloudflare Workers to prevent
1157
+ * premature termination of background operations.
1158
+ *
1159
+ * @param promise - Promise representing the background task to keep alive
1160
+ */
1161
+ type WaitUntilCallback = (promise: Promise<unknown>) => void;
1162
+ declare const FlowWaitUntil_base: Context.TagClass<FlowWaitUntil, "FlowWaitUntil", WaitUntilCallback>;
1163
+ /**
1164
+ * Optional WaitUntil service for background task management.
1165
+ * When provided, allows flows to execute beyond the HTTP response lifecycle.
1166
+ *
1167
+ * In Cloudflare Workers, use `ctx.executionCtx.waitUntil()`.
1168
+ * In other environments, this can be undefined (flows execute normally with Effect.fork).
1169
+ *
1170
+ * This service uses Effect's optional service pattern. Access it via:
1171
+ * ```typescript
1172
+ * const waitUntil = yield* FlowWaitUntil.optional;
1173
+ * if (Option.isSome(waitUntil)) {
1174
+ * // Use waitUntil.value
1175
+ * }
1176
+ * ```
1177
+ *
1178
+ * @see https://effect.website/docs/requirements-management/services/#optional-services
1179
+ */
1180
+ declare class FlowWaitUntil extends FlowWaitUntil_base {
1181
+ static optional: Effect.Effect<Option.Option<WaitUntilCallback>, never, never>;
1182
+ }
1183
+ declare const FlowLifecycleHook_base: Context.TagClass<FlowLifecycleHook, "FlowLifecycleHook", {
1184
+ readonly onComplete: (ctx: {
1185
+ jobId: string;
1186
+ flowId: string;
1187
+ clientId: string | null;
1188
+ status: "completed" | "failed";
1189
+ }) => Effect.Effect<void>;
1190
+ }>;
1191
+ /**
1192
+ * Optional lifecycle hook for flow execution events.
1193
+ * Called when a flow completes or fails, enabling usage tracking,
1194
+ * billing, and other post-processing directly in the execution pipeline.
1195
+ *
1196
+ * This follows the same optional service pattern as {@link DeadLetterQueueService}.
1197
+ * When provided, the hook fires reliably from the flow daemon — not from
1198
+ * polling endpoints — ensuring it always runs exactly once per flow execution.
1199
+ */
1200
+ declare class FlowLifecycleHook extends FlowLifecycleHook_base {
1201
+ static optional: Effect.Effect<Option.Option<{
1202
+ readonly onComplete: (ctx: {
1203
+ jobId: string;
1204
+ flowId: string;
1205
+ clientId: string | null;
1206
+ status: "completed" | "failed";
1207
+ }) => Effect.Effect<void>;
1208
+ }>, never, never>;
1209
+ }
1210
+ /**
1211
+ * Flow provider interface that applications must implement.
1212
+ *
1213
+ * This interface defines how the FlowEngine retrieves flow definitions.
1214
+ * Applications provide their own implementation to load flows from a database,
1215
+ * configuration files, or any other source.
1216
+ *
1217
+ * @template TRequirements - Additional Effect requirements for flow execution
1218
+ *
1219
+ * @property getFlow - Retrieves a flow definition by ID with authorization check
1220
+ *
1221
+ * @example
1222
+ * ```typescript
1223
+ * // Implement a flow provider from database
1224
+ * const dbFlowProvider: FlowProviderShape = {
1225
+ * getFlow: (flowId, clientId) => Effect.gen(function* () {
1226
+ * // Load flow from database
1227
+ * const flowData = yield* db.getFlow(flowId);
1228
+ *
1229
+ * // Check authorization
1230
+ * if (flowData.ownerId !== clientId) {
1231
+ * return yield* Effect.fail(
1232
+ * UploadistaError.fromCode("FLOW_NOT_AUTHORIZED")
1233
+ * );
1234
+ * }
1235
+ *
1236
+ * // Create flow instance
1237
+ * return createFlow(flowData);
1238
+ * })
1239
+ * };
1240
+ *
1241
+ * // Provide to FlowEngine
1242
+ * const flowProviderLayer = Layer.succeed(FlowProvider, dbFlowProvider);
1243
+ * ```
1244
+ */
1245
+ type FlowProviderShape<TRequirements = any> = {
1246
+ getFlow: (flowId: string, clientId: string | null) => Effect.Effect<Flow<any, any, TRequirements>, UploadistaError>;
1247
+ };
1248
+ declare const FlowProvider_base: Context.TagClass<FlowProvider, "FlowProvider", FlowProviderShape<any>>;
1249
+ /**
1250
+ * Effect-TS context tag for the FlowProvider service.
1251
+ *
1252
+ * Applications must provide an implementation of FlowProviderShape
1253
+ * to enable the FlowEngine to retrieve flow definitions.
1254
+ *
1255
+ * @example
1256
+ * ```typescript
1257
+ * // Access FlowProvider in an Effect
1258
+ * const effect = Effect.gen(function* () {
1259
+ * const provider = yield* FlowProvider;
1260
+ * const flow = yield* provider.getFlow("flow123", "client456");
1261
+ * return flow;
1262
+ * });
1263
+ * ```
1264
+ */
1265
+ declare class FlowProvider extends FlowProvider_base {}
1266
+ /**
1267
+ * FlowServer service interface.
1268
+ *
1269
+ * This is the core flow processing service that executes DAG-based file processing pipelines.
1270
+ * It manages flow execution, job tracking, node processing, pause/resume functionality,
1271
+ * and real-time event broadcasting.
1272
+ *
1273
+ * All operations return Effect types for composable, type-safe error handling.
1274
+ *
1275
+ * @property getFlow - Retrieves a flow definition by ID
1276
+ * @property getFlowData - Retrieves flow metadata (nodes, edges) without full flow instance
1277
+ * @property runFlow - Starts a new flow execution and returns immediately with job ID
1278
+ * @property resumeFlow - Resumes a paused flow with new data for a specific node
1279
+ * @property pauseFlow - Pauses a running flow (user-initiated pause)
1280
+ * @property cancelFlow - Cancels a running or paused flow and cleans up resources
1281
+ * @property getJobStatus - Retrieves current status and results of a flow job
1282
+ * @property subscribeToFlowEvents - Subscribes WebSocket to flow execution events
1283
+ * @property unsubscribeFromFlowEvents - Unsubscribes from flow events
1284
+ *
1285
+ * @example
1286
+ * ```typescript
1287
+ * // Execute a flow
1288
+ * const program = Effect.gen(function* () {
1289
+ * const server = yield* FlowEngine;
1290
+ *
1291
+ * // Start flow execution (returns immediately)
1292
+ * const job = yield* server.runFlow({
1293
+ * flowId: "resize-optimize",
1294
+ * storageId: "s3-production",
1295
+ * clientId: "client123",
1296
+ * inputs: {
1297
+ * input_1: { uploadId: "upload_abc123" }
1298
+ * }
1299
+ * });
1300
+ *
1301
+ * // Subscribe to events
1302
+ * yield* server.subscribeToFlowEvents(job.id, websocket);
1303
+ *
1304
+ * // Poll for status
1305
+ * const status = yield* server.getJobStatus(job.id);
1306
+ * console.log(status.status); // "running", "paused", "completed", "failed", or "cancelled"
1307
+ *
1308
+ * // User can pause the flow
1309
+ * yield* server.pauseFlow(job.id, "client123");
1310
+ *
1311
+ * return job;
1312
+ * });
1313
+ *
1314
+ * // Resume a paused flow
1315
+ * const resume = Effect.gen(function* () {
1316
+ * const server = yield* FlowEngine;
1317
+ *
1318
+ * // Flow paused waiting for user input at node "approval_1"
1319
+ * const job = yield* server.resumeFlow({
1320
+ * jobId: "job123",
1321
+ * nodeId: "approval_1",
1322
+ * newData: { approved: true },
1323
+ * clientId: "client123"
1324
+ * });
1325
+ *
1326
+ * return job;
1327
+ * });
1328
+ *
1329
+ * // Cancel a flow
1330
+ * const cancel = Effect.gen(function* () {
1331
+ * const server = yield* FlowEngine;
1332
+ *
1333
+ * // Cancel flow and cleanup intermediate files
1334
+ * const job = yield* server.cancelFlow("job123", "client123");
1335
+ *
1336
+ * return job;
1337
+ * });
1338
+ *
1339
+ * // Check flow structure before execution
1340
+ * const inspect = Effect.gen(function* () {
1341
+ * const server = yield* FlowEngine;
1342
+ *
1343
+ * const flowData = yield* server.getFlowData("resize-optimize", "client123");
1344
+ * console.log("Nodes:", flowData.nodes);
1345
+ * console.log("Edges:", flowData.edges);
1346
+ *
1347
+ * return flowData;
1348
+ * });
1349
+ * ```
1350
+ */
1351
+ type FlowEngineShape = {
1352
+ getFlow: <TRequirements>(flowId: string, clientId: string | null) => Effect.Effect<Flow<any, any, TRequirements>, UploadistaError>;
1353
+ getFlowData: (flowId: string, clientId: string | null) => Effect.Effect<FlowData, UploadistaError>;
1354
+ runFlow: <TRequirements>({
1355
+ flowId,
1356
+ storageId,
1357
+ clientId,
1358
+ inputs,
1359
+ jobId
1360
+ }: {
1361
+ flowId: string;
1362
+ storageId: string;
1363
+ clientId: string | null;
1364
+ inputs: any; /** Optional job ID to use instead of generating a new UUID. Used by the queue worker to keep the queue item ID and flow job ID in sync. */
1365
+ jobId?: string;
1366
+ }) => Effect.Effect<FlowJob, UploadistaError, TRequirements>;
1367
+ resumeFlow: <TRequirements>({
1368
+ jobId,
1369
+ nodeId,
1370
+ newData,
1371
+ clientId
1372
+ }: {
1373
+ jobId: string;
1374
+ nodeId: string;
1375
+ newData: unknown;
1376
+ clientId: string | null;
1377
+ }) => Effect.Effect<FlowJob, UploadistaError, TRequirements>;
1378
+ pauseFlow: (jobId: string, clientId: string | null) => Effect.Effect<FlowJob, UploadistaError>;
1379
+ cancelFlow: (jobId: string, clientId: string | null) => Effect.Effect<FlowJob, UploadistaError>;
1380
+ getJobStatus: (jobId: string) => Effect.Effect<FlowJob, UploadistaError>;
1381
+ subscribeToFlowEvents: (jobId: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError>;
1382
+ unsubscribeFromFlowEvents: (jobId: string) => Effect.Effect<void, UploadistaError>;
1383
+ };
1384
+ declare const FlowEngine_base: Context.TagClass<FlowEngine, "FlowEngine", FlowEngineShape>;
1385
+ /**
1386
+ * Effect-TS context tag for the FlowEngine service.
1387
+ *
1388
+ * Use this tag to access the FlowEngine in an Effect context.
1389
+ * The server must be provided via a Layer or dependency injection.
1390
+ *
1391
+ * @example
1392
+ * ```typescript
1393
+ * // Access FlowEngine in an Effect
1394
+ * const flowEffect = Effect.gen(function* () {
1395
+ * const server = yield* FlowEngine;
1396
+ * const job = yield* server.runFlow({
1397
+ * flowId: "my-flow",
1398
+ * storageId: "s3",
1399
+ * clientId: null,
1400
+ * inputs: {}
1401
+ * });
1402
+ * return job;
1403
+ * });
1404
+ *
1405
+ * // Provide FlowEngine layer
1406
+ * const program = flowEffect.pipe(
1407
+ * Effect.provide(flowServer),
1408
+ * Effect.provide(flowProviderLayer),
1409
+ * Effect.provide(flowJobKvStore)
1410
+ * );
1411
+ * ```
1412
+ */
1413
+ declare class FlowEngine extends FlowEngine_base {}
1414
+ /**
1415
+ * Legacy configuration options for FlowEngine.
1416
+ *
1417
+ * @deprecated Use Effect Layers and FlowProvider instead.
1418
+ * This type is kept for backward compatibility.
1419
+ *
1420
+ * @property getFlow - Function to retrieve flow definitions
1421
+ * @property kvStore - KV store for flow job metadata
1422
+ */
1423
+ type FlowEngineOptions = {
1424
+ getFlow: <TRequirements>({
1425
+ flowId,
1426
+ storageId
1427
+ }: {
1428
+ flowId: string;
1429
+ storageId: string;
1430
+ }) => Promise<Flow<any, any, TRequirements>>;
1431
+ kvStore: KvStore<FlowJob>;
1432
+ };
1433
+ declare function createFlowEngine(): Effect.Effect<{
1434
+ getFlow: <TRequirements>(flowId: string, clientId: string | null) => Effect.Effect<Flow<any, any, any>, UploadistaError, never>;
1435
+ getFlowData: (flowId: string, clientId: string | null) => Effect.Effect<FlowData, UploadistaError, never>;
1436
+ runFlow: ({
1437
+ flowId,
1438
+ storageId,
1439
+ clientId,
1440
+ inputs,
1441
+ jobId: providedJobId
1442
+ }: {
1443
+ flowId: string;
1444
+ storageId: string;
1445
+ clientId: string | null;
1446
+ inputs: unknown;
1447
+ jobId?: string;
1448
+ }) => Effect.Effect<FlowJob, UploadistaError, never>;
1449
+ getJobStatus: (jobId: string) => Effect.Effect<FlowJob, UploadistaError, never>;
1450
+ resumeFlow: ({
1451
+ jobId,
1452
+ nodeId,
1453
+ newData,
1454
+ clientId
1455
+ }: {
1456
+ jobId: string;
1457
+ nodeId: string;
1458
+ newData: unknown;
1459
+ clientId: string | null;
1460
+ }) => Effect.Effect<FlowJob, UploadistaError, never>;
1461
+ pauseFlow: (jobId: string, clientId: string | null) => Effect.Effect<FlowJob, UploadistaError, never>;
1462
+ cancelFlow: (jobId: string, clientId: string | null) => Effect.Effect<FlowJob, UploadistaError, never>;
1463
+ subscribeToFlowEvents: (jobId: string, connection: WebSocketConnection) => Effect.Effect<void, UploadistaError, never>;
1464
+ unsubscribeFromFlowEvents: (jobId: string) => Effect.Effect<void, UploadistaError, never>;
1465
+ }, never, UploadEngine | FlowProvider | FlowJobKVStore | FlowEventEmitter>;
1466
+ declare const flowEngine: Layer.Layer<FlowEngine, never, UploadEngine | FlowProvider | FlowJobKVStore | FlowEventEmitter>;
1467
+ type FlowEngineLayer = typeof flowEngine;
1468
+ //#endregion
1469
+ //#region src/flow/flow-queue.d.ts
1470
+ declare const FlowQueueDispatchMarker_base: Context.TagClass<FlowQueueDispatchMarker, "FlowQueueDispatchMarker", true>;
1471
+ /**
1472
+ * Context marker that signals the current Effect is running inside the
1473
+ * FlowQueueService worker dispatch loop.
1474
+ *
1475
+ * When this marker is present in the Effect context, FlowEngine.runFlow()
1476
+ * skips the FlowQueueService delegation and executes directly via forkDaemon.
1477
+ * This prevents infinite re-enqueue cycles when the worker calls runFlow.
1478
+ *
1479
+ * @internal
1480
+ */
1481
+ declare class FlowQueueDispatchMarker extends FlowQueueDispatchMarker_base {}
1482
+ /**
1483
+ * Shape of the FlowQueueService.
1484
+ *
1485
+ * All operations return Effect types for composable, type-safe error handling.
1486
+ */
1487
+ interface FlowQueueServiceShape {
1488
+ /**
1489
+ * Enqueue a flow for execution.
1490
+ *
1491
+ * Returns immediately with a FlowQueueItem in "pending" state.
1492
+ * The worker loop will dispatch the flow when a concurrency slot is available.
1493
+ *
1494
+ * @param params - Flow execution parameters
1495
+ * @returns The created queue item with status "pending"
1496
+ */
1497
+ enqueue(params: {
1498
+ flowId: string;
1499
+ storageId: string;
1500
+ input: unknown;
1501
+ clientId: string | null;
1502
+ dlqItemId?: string;
1503
+ }): Effect.Effect<FlowQueueItem, UploadistaError>;
1504
+ /**
1505
+ * Retrieve the current status of a queue item by ID.
1506
+ *
1507
+ * @param itemId - The queue item ID
1508
+ * @returns The queue item
1509
+ * @throws QUEUE_ITEM_NOT_FOUND if the ID is unknown
1510
+ */
1511
+ getStatus(itemId: string): Effect.Effect<FlowQueueItem, UploadistaError>;
1512
+ /**
1513
+ * Cancel a pending queue item before it starts executing.
1514
+ *
1515
+ * @param itemId - The queue item ID
1516
+ * @throws QUEUE_ITEM_ALREADY_RUNNING if the item is already running
1517
+ */
1518
+ cancel(itemId: string): Effect.Effect<void, UploadistaError>;
1519
+ /**
1520
+ * List queue items, optionally filtered by status.
1521
+ *
1522
+ * @param options - Optional filter options
1523
+ * @returns Array of matching queue items
1524
+ */
1525
+ list(options?: {
1526
+ status?: FlowQueueItem["status"];
1527
+ }): Effect.Effect<FlowQueueItem[], UploadistaError>;
1528
+ /**
1529
+ * Get aggregate queue statistics for monitoring.
1530
+ *
1531
+ * @returns Current queue stats including counts and concurrency info
1532
+ */
1533
+ getStats(): Effect.Effect<FlowQueueStats, UploadistaError>;
1534
+ }
1535
+ declare const FlowQueueService_base: Context.TagClass<FlowQueueService, "FlowQueueService", FlowQueueServiceShape>;
1536
+ /**
1537
+ * Effect-TS context tag for the FlowQueueService.
1538
+ *
1539
+ * Use `FlowQueueService.optional` to resolve it optionally — this is the
1540
+ * pattern used in FlowEngine to preserve backward compatibility.
1541
+ *
1542
+ * @example
1543
+ * ```typescript
1544
+ * // In FlowEngine.runFlow()
1545
+ * const queueOption = yield* FlowQueueService.optional;
1546
+ * if (Option.isSome(queueOption)) {
1547
+ * return yield* queueOption.value.enqueue({ flowId, storageId, input, clientId });
1548
+ * }
1549
+ * // ... existing fork path
1550
+ *
1551
+ * // From application code
1552
+ * const queue = yield* FlowQueueService;
1553
+ * const item = yield* queue.enqueue({ flowId: "my-flow", storageId: "s3", input: {}, clientId: null });
1554
+ * ```
1555
+ */
1556
+ declare class FlowQueueService extends FlowQueueService_base {
1557
+ /**
1558
+ * Access the FlowQueueService optionally.
1559
+ * Returns Option.none() if the service is not present in the layer.
1560
+ *
1561
+ * Use this in FlowEngine to remain backward-compatible.
1562
+ */
1563
+ static readonly optional: Effect.Effect<Option.Option<FlowQueueServiceShape>, never, never>;
1564
+ /**
1565
+ * Create a FlowQueueService Layer using the default in-memory store.
1566
+ *
1567
+ * @param config - Optional configuration overrides
1568
+ * @returns A Layer providing FlowQueueService
1569
+ */
1570
+ static Default(config?: FlowQueueConfig): Layer.Layer<FlowQueueService, never, FlowEngine>;
1571
+ /**
1572
+ * Create a FlowQueueService Layer with a custom store.
1573
+ *
1574
+ * @param config - Configuration (maxConcurrency, dlqRetryIntervalMs, dlqRetryBatchSize)
1575
+ * @param store - The FlowQueueStore implementation to use
1576
+ * @returns A Layer providing FlowQueueService
1577
+ */
1578
+ static make(config: FlowQueueConfig, store: FlowQueueStore): Layer.Layer<FlowQueueService, never, FlowEngine>;
1579
+ /**
1580
+ * Create a FlowQueueService Layer backed by the application's BaseKvStoreService.
1581
+ *
1582
+ * Items are persisted under the "uploadista:queue-item:" key prefix, using the
1583
+ * same KV store already configured for the server (Redis, Cloudflare KV, etc.).
1584
+ * This is the recommended factory for most deployments — no separate store
1585
+ * dependency is needed beyond the kvStore already wired at server level.
1586
+ *
1587
+ * @param config - Optional queue configuration (maxConcurrency, retry intervals…)
1588
+ * @returns A Layer providing FlowQueueService, requiring FlowEngine and BaseKvStoreService
1589
+ *
1590
+ * @example
1591
+ * ```typescript
1592
+ * // In createUploadistaServer — flowQueue: true uses this automatically
1593
+ * FlowQueueService.fromKvStore({ maxConcurrency: 8 })
1594
+ * .pipe(Layer.provide(flowEngineLayer), Layer.provide(kvStore))
1595
+ * ```
1596
+ */
1597
+ static fromKvStore(config?: FlowQueueConfig): Layer.Layer<FlowQueueService, never, FlowEngine | FlowQueueKVStore>;
1598
+ /**
1599
+ * Shorthand for fromKvStore — creates the full layer including the KV store
1600
+ * sub-layer, requiring only FlowEngine and BaseKvStoreService.
1601
+ */
1602
+ static fromBaseKvStore(config?: FlowQueueConfig): Layer.Layer<FlowQueueService, never, FlowEngine | BaseKvStoreService>;
1603
+ }
1604
+ //#endregion
1605
+ //#region src/flow/nodes/input-node.d.ts
1606
+ /**
1607
+ * Union schema for all input operations.
1608
+ * Defines the possible input data structures for the input node.
1609
+ */
1610
+ declare const inputDataSchema: z.ZodUnion<readonly [z.ZodObject<{
1611
+ operation: z.ZodLiteral<"init">;
1612
+ storageId: z.ZodString;
1613
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
1614
+ }, z.core.$strip>, z.ZodObject<{
1615
+ operation: z.ZodLiteral<"finalize">;
1616
+ uploadId: z.ZodString;
1617
+ }, z.core.$strip>, z.ZodObject<{
1618
+ operation: z.ZodLiteral<"url">;
1619
+ url: z.ZodString;
1620
+ storageId: z.ZodOptional<z.ZodString>;
1621
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
1622
+ }, z.core.$strip>]>;
1623
+ /**
1624
+ * Type representing the input data for the input node.
1625
+ * Can be one of three operation types: init, finalize, or url.
1626
+ */
1627
+ type InputData = z.infer<typeof inputDataSchema>;
1628
+ /**
1629
+ * Schema for input node filtering parameters.
1630
+ * Defines validation rules for incoming files.
1631
+ */
1632
+ declare const inputNodeParamsSchema: z.ZodObject<{
1633
+ allowedMimeTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1634
+ minSize: z.ZodOptional<z.ZodNumber>;
1635
+ maxSize: z.ZodOptional<z.ZodNumber>;
1636
+ }, z.core.$strip>;
1637
+ /**
1638
+ * Parameters for configuring input node validation.
1639
+ * Controls which files are accepted based on type and size constraints.
1640
+ */
1641
+ type InputNodeParams = z.infer<typeof inputNodeParamsSchema>;
1642
+ /**
1643
+ * Creates an input node for handling file input through multiple methods.
1644
+ *
1645
+ * The input node supports three operation types:
1646
+ * - `init`: Initialize a streaming upload session
1647
+ * - `finalize`: Complete a streaming upload after all chunks are uploaded
1648
+ * - `url`: Fetch a file directly from a URL
1649
+ *
1650
+ * @param id - Unique identifier for the node
1651
+ * @param params - Optional validation parameters for filtering incoming files
1652
+ * @returns An Effect that creates a flow node configured for file input
1653
+ *
1654
+ * @example
1655
+ * ```typescript
1656
+ * // Create input node with validation
1657
+ * const inputNode = yield* createInputNode("file-input", {
1658
+ * allowedMimeTypes: ["image/*", "application/pdf"],
1659
+ * maxSize: 10 * 1024 * 1024, // 10MB
1660
+ * });
1661
+ *
1662
+ * // Create input node without validation
1663
+ * const openInputNode = yield* createInputNode("open-input");
1664
+ * ```
1665
+ */
1666
+ declare function createInputNode(id: string, params?: InputNodeParams, options?: {
1667
+ keepOutput?: boolean;
1668
+ }): Effect.Effect<FlowNodeData & {
1669
+ inputSchema: z.ZodType<{
1670
+ operation: "init";
1671
+ storageId: string;
1672
+ metadata?: Record<string, any> | undefined;
1673
+ } | {
1674
+ operation: "finalize";
1675
+ uploadId: string;
1676
+ } | {
1677
+ operation: "url";
1678
+ url: string;
1679
+ storageId?: string | undefined;
1680
+ metadata?: Record<string, any> | undefined;
1681
+ }, unknown, z.core.$ZodTypeInternals<{
1682
+ operation: "init";
1683
+ storageId: string;
1684
+ metadata?: Record<string, any> | undefined;
1685
+ } | {
1686
+ operation: "finalize";
1687
+ uploadId: string;
1688
+ } | {
1689
+ operation: "url";
1690
+ url: string;
1691
+ storageId?: string | undefined;
1692
+ metadata?: Record<string, any> | undefined;
1693
+ }, unknown>>;
1694
+ outputSchema: z.ZodType<UploadFile, unknown, z.core.$ZodTypeInternals<UploadFile, unknown>>;
1695
+ run: (args: {
1696
+ data: {
1697
+ operation: "init";
1698
+ storageId: string;
1699
+ metadata?: Record<string, any> | undefined;
1700
+ } | {
1701
+ operation: "finalize";
1702
+ uploadId: string;
1703
+ } | {
1704
+ operation: "url";
1705
+ url: string;
1706
+ storageId?: string | undefined;
1707
+ metadata?: Record<string, any> | undefined;
1708
+ };
1709
+ jobId: string;
1710
+ storageId: string;
1711
+ flowId: string;
1712
+ inputs?: Record<string, unknown>;
1713
+ clientId: string | null;
1714
+ }) => Effect.Effect<NodeExecutionResult<UploadFile>, UploadistaError, never>;
1715
+ condition?: {
1716
+ field: string;
1717
+ operator: string;
1718
+ value: unknown;
1719
+ };
1720
+ multiInput?: boolean;
1721
+ multiOutput?: boolean;
1722
+ pausable?: boolean;
1723
+ retry?: {
1724
+ maxRetries?: number;
1725
+ retryDelay?: number;
1726
+ exponentialBackoff?: boolean;
1727
+ };
1728
+ circuitBreaker?: FlowCircuitBreakerConfig;
1729
+ } & {
1730
+ type: NodeType.input;
1731
+ }, UploadistaError, UploadEngine>;
1732
+ //#endregion
1733
+ //#region src/flow/types/flow-file.d.ts
1734
+ /**
1735
+ * Conditional execution rules for flow nodes.
1736
+ *
1737
+ * Conditions allow nodes to execute conditionally based on file properties or metadata.
1738
+ * They are evaluated before node execution and can skip nodes that don't match.
1739
+ *
1740
+ * @module flow/types/flow-file
1741
+ * @see {@link FlowNode} for how conditions are used in nodes
1742
+ *
1743
+ * @example
1744
+ * ```typescript
1745
+ * // Only process images larger than 1MB
1746
+ * const condition: FlowCondition = {
1747
+ * field: "size",
1748
+ * operator: "greaterThan",
1749
+ * value: 1024 * 1024
1750
+ * };
1751
+ *
1752
+ * // Only process JPEG images
1753
+ * const jpegCondition: FlowCondition = {
1754
+ * field: "mimeType",
1755
+ * operator: "startsWith",
1756
+ * value: "image/jpeg"
1757
+ * };
1758
+ * ```
1759
+ */
1760
+ /**
1761
+ * Represents a conditional rule for node execution.
1762
+ *
1763
+ * @property field - The file property to check
1764
+ * @property operator - The comparison operator to apply
1765
+ * @property value - The value to compare against
1766
+ *
1767
+ * @remarks
1768
+ * - Fields can check file metadata (mimeType, size) or image properties (width, height)
1769
+ * - String operators (contains, startsWith) work with string values
1770
+ * - Numeric operators (greaterThan, lessThan) work with numeric values
1771
+ * - The extension field checks the file extension without the dot
1772
+ */
1773
+ type FlowCondition = {
1774
+ field: "mimeType" | "size" | "width" | "height" | "extension";
1775
+ operator: "equals" | "notEquals" | "greaterThan" | "lessThan" | "contains" | "startsWith";
1776
+ value: string | number;
1777
+ };
1778
+ //#endregion
1779
+ //#region src/flow/types/type-utils.d.ts
1780
+ /**
1781
+ * Extracts the service type from an Effect Layer.
1782
+ *
1783
+ * Given a Layer that provides a service, this type utility extracts
1784
+ * the service type from the Layer's type signature.
1785
+ *
1786
+ * @template T - The Layer type to extract from
1787
+ * @returns The service type provided by the layer, or never if T is not a Layer
1788
+ *
1789
+ * @example
1790
+ * ```typescript
1791
+ * type MyLayer = Layer.Layer<ServiceA, never, never>;
1792
+ * type Service = ExtractLayerService<MyLayer>;
1793
+ * // Service = ServiceA
1794
+ * ```
1795
+ *
1796
+ * @example
1797
+ * ```typescript
1798
+ * import { ImagePluginLayer } from '@uploadista/core';
1799
+ *
1800
+ * type ImageService = ExtractLayerService<ImagePluginLayer>;
1801
+ * // ImageService = ImagePlugin
1802
+ * ```
1803
+ */
1804
+ type ExtractLayerService<T, TError = never, TRequirements = never> = T extends Layer.Layer<infer S, TError, TRequirements> ? S : never;
1805
+ /**
1806
+ * Extracts all service types from a tuple of layers and returns them as a union.
1807
+ *
1808
+ * This type recursively processes a tuple of Layer types and extracts all
1809
+ * the services they provide, combining them into a single union type.
1810
+ *
1811
+ * @template T - A readonly tuple of Layer types
1812
+ * @returns A union of all service types provided by the layers, or never for empty tuples
1813
+ *
1814
+ * @example
1815
+ * ```typescript
1816
+ * type Layers = [
1817
+ * Layer.Layer<ServiceA, never, never>,
1818
+ * Layer.Layer<ServiceB, never, never>,
1819
+ * Layer.Layer<ServiceC, never, never>
1820
+ * ];
1821
+ * type Services = ExtractLayerServices<Layers>;
1822
+ * // Services = ServiceA | ServiceB | ServiceC
1823
+ * ```
1824
+ *
1825
+ * @example
1826
+ * ```typescript
1827
+ * import { ImagePluginLayer, ZipPluginLayer } from '@uploadista/core';
1828
+ *
1829
+ * type PluginLayers = [ImagePluginLayer, ZipPluginLayer];
1830
+ * type AllServices = ExtractLayerServices<PluginLayers>;
1831
+ * // AllServices = ImagePlugin | ZipPlugin
1832
+ * ```
1833
+ *
1834
+ * @example
1835
+ * ```typescript
1836
+ * type EmptyLayers = [];
1837
+ * type NoServices = ExtractLayerServices<EmptyLayers>;
1838
+ * // NoServices = never
1839
+ * ```
1840
+ */
1841
+ type ExtractLayerServices<T extends readonly Layer.Layer<any, any, any>[]> = T extends readonly [] ? never : { [K in keyof T]: T[K] extends Layer.Layer<infer S, any, any> ? S : never }[number];
1842
+ /**
1843
+ * Unwraps an Effect type to extract its success value type.
1844
+ *
1845
+ * If the input type is an Effect, this extracts the success type (first type parameter).
1846
+ * If the input is not an Effect, it returns the type unchanged.
1847
+ *
1848
+ * @template T - The type to resolve, potentially an Effect
1849
+ * @returns The success type if T is an Effect, otherwise T
1850
+ *
1851
+ * @example
1852
+ * ```typescript
1853
+ * type MyEffect = Effect.Effect<string, Error, never>;
1854
+ * type Result = ResolveEffect<MyEffect>;
1855
+ * // Result = string
1856
+ * ```
1857
+ *
1858
+ * @example
1859
+ * ```typescript
1860
+ * type NonEffect = { data: string };
1861
+ * type Result = ResolveEffect<NonEffect>;
1862
+ * // Result = { data: string }
1863
+ * ```
1864
+ */
1865
+ type ResolveEffect<T> = T extends Effect.Effect<infer S, any, any> ? S : T;
1866
+ /**
1867
+ * Extracts the error type from an Effect.
1868
+ *
1869
+ * Given an Effect type, this utility extracts the error type
1870
+ * (second type parameter) from the Effect's type signature.
1871
+ *
1872
+ * @template T - The Effect type to extract from
1873
+ * @returns The error type of the Effect, or never if T is not an Effect
1874
+ *
1875
+ * @example
1876
+ * ```typescript
1877
+ * type MyEffect = Effect.Effect<string, ValidationError, never>;
1878
+ * type ErrorType = ExtractEffectError<MyEffect>;
1879
+ * // ErrorType = ValidationError
1880
+ * ```
1881
+ *
1882
+ * @example
1883
+ * ```typescript
1884
+ * type SafeEffect = Effect.Effect<number, never, SomeService>;
1885
+ * type ErrorType = ExtractEffectError<SafeEffect>;
1886
+ * // ErrorType = never (no errors possible)
1887
+ * ```
1888
+ */
1889
+ type ExtractEffectError<T> = T extends Effect.Effect<any, infer E, any> ? E : never;
1890
+ /**
1891
+ * Extracts the requirements (context) type from an Effect.
1892
+ *
1893
+ * Given an Effect type, this utility extracts the requirements type
1894
+ * (third type parameter) from the Effect's type signature. This represents
1895
+ * the services that must be provided for the Effect to run.
1896
+ *
1897
+ * @template T - The Effect type to extract from
1898
+ * @returns The requirements type of the Effect, or never if T is not an Effect
1899
+ *
1900
+ * @example
1901
+ * ```typescript
1902
+ * type MyEffect = Effect.Effect<string, Error, Database | Logger>;
1903
+ * type Requirements = ExtractEffectRequirements<MyEffect>;
1904
+ * // Requirements = Database | Logger
1905
+ * ```
1906
+ *
1907
+ * @example
1908
+ * ```typescript
1909
+ * import { ImagePlugin, ZipPlugin } from '@uploadista/core';
1910
+ *
1911
+ * type ProcessEffect = Effect.Effect<
1912
+ * ProcessedImage,
1913
+ * ProcessError,
1914
+ * ImagePlugin | ZipPlugin
1915
+ * >;
1916
+ * type Needed = ExtractEffectRequirements<ProcessEffect>;
1917
+ * // Needed = ImagePlugin | ZipPlugin
1918
+ * ```
1919
+ */
1920
+ type ExtractEffectRequirements<T> = T extends Effect.Effect<any, any, infer R> ? R : never;
1921
+ //#endregion
1922
+ //#region src/flow/nodes/transform-node.d.ts
1923
+ /**
1924
+ * Transform mode for controlling how file data is processed.
1925
+ *
1926
+ * - `buffered`: Always load entire file into memory before transforming (default, backward compatible)
1927
+ * - `streaming`: Process file as a stream of chunks for memory efficiency
1928
+ * - `auto`: Automatically select mode based on file size and DataStore capabilities
1929
+ */
1930
+ type TransformMode = "buffered" | "streaming" | "auto";
1931
+ /**
1932
+ * Result type for streaming transforms.
1933
+ * Can return just the transformed stream, or include metadata changes.
1934
+ */
1935
+ type StreamingTransformResult = Stream.Stream<Uint8Array, UploadistaError> | {
1936
+ stream: Stream.Stream<Uint8Array, UploadistaError>;
1937
+ type?: string;
1938
+ fileName?: string; /** Estimated output size in bytes (for progress tracking) */
1939
+ estimatedSize?: number;
1940
+ };
1941
+ /**
1942
+ * Function type for streaming transforms.
1943
+ * Receives an input stream and file metadata, returns a transformed stream.
1944
+ */
1945
+ type StreamingTransformFn = (stream: Stream.Stream<Uint8Array, UploadistaError>, file: UploadFile) => Effect.Effect<StreamingTransformResult, UploadistaError>;
1946
+ /**
1947
+ * Configuration object for creating a transform node.
1948
+ */
1949
+ interface TransformNodeConfig {
1950
+ /** Unique identifier for the node */
1951
+ id: string;
1952
+ /** Human-readable name for the node */
1953
+ name: string;
1954
+ /** Description of what the node does */
1955
+ description: string;
1956
+ /** Optional output type ID from outputTypeRegistry for result type registration */
1957
+ outputTypeId?: string;
1958
+ /**
1959
+ * Whether to keep this node's output as a flow result even if it has outgoing edges.
1960
+ * When true, the node's output will be included in the final flow outputs alongside topology sinks.
1961
+ * Defaults to false.
1962
+ */
1963
+ keepOutput?: boolean;
1964
+ /**
1965
+ * Optional file naming configuration.
1966
+ * - undefined: Preserve original filename (backward compatible)
1967
+ * - mode: 'auto': Generate smart suffix based on node type
1968
+ * - mode: 'custom': Use template pattern or rename function
1969
+ */
1970
+ naming?: FileNamingConfig;
1971
+ /**
1972
+ * Node type identifier used for auto-naming context.
1973
+ * Defaults to "transform" if not specified.
1974
+ */
1975
+ nodeType?: string;
1976
+ /**
1977
+ * Stable node type identifier for circuit breaker configuration.
1978
+ * Used to share circuit breaker state across nodes of the same type
1979
+ * and for nodeTypeOverrides in flow config.
1980
+ * Example: "describe-image", "remove-background", "scan-virus"
1981
+ */
1982
+ nodeTypeId?: string;
1983
+ /**
1984
+ * Additional variables to include in the naming context.
1985
+ * These are merged with the base context (flowId, jobId, etc.)
1986
+ * and can be used in templates.
1987
+ */
1988
+ namingVars?: Record<string, string | number | undefined>;
1989
+ /**
1990
+ * Circuit breaker configuration for resilience against external service failures.
1991
+ * Overrides flow-level circuit breaker defaults for this node.
1992
+ */
1993
+ circuitBreaker?: FlowCircuitBreakerConfig;
1994
+ /**
1995
+ * Transform mode controlling how file data is processed.
1996
+ * - `buffered`: Always load entire file into memory
1997
+ * - `streaming`: Process file as a stream of chunks
1998
+ * - `auto`: Select mode based on file size and DataStore capabilities (default)
1999
+ *
2000
+ * @default "auto"
2001
+ */
2002
+ mode?: TransformMode;
2003
+ /**
2004
+ * Configuration for streaming mode (file size threshold, chunk size).
2005
+ * Only used when mode is "streaming" or "auto".
2006
+ */
2007
+ streamingConfig?: StreamingConfig;
2008
+ /**
2009
+ * Function that transforms file bytes (buffered mode).
2010
+ * Required unless streamingTransform is provided and mode is "streaming".
2011
+ */
2012
+ transform?: (bytes: Uint8Array, file: UploadFile) => Effect.Effect<Uint8Array | {
2013
+ bytes: Uint8Array;
2014
+ type?: string;
2015
+ fileName?: string;
2016
+ metadata?: Record<string, unknown>;
2017
+ }, UploadistaError>;
2018
+ /**
2019
+ * Function that transforms file as a stream (streaming mode).
2020
+ * For memory-efficient processing of large files.
2021
+ * Used when mode is "streaming" or when "auto" selects streaming.
2022
+ */
2023
+ streamingTransform?: StreamingTransformFn;
2024
+ }
2025
+ /**
2026
+ * Creates a transform node that handles the common pattern of:
2027
+ * 1. Reading bytes from an UploadFile
2028
+ * 2. Transforming the bytes
2029
+ * 3. Uploading the result as a new UploadFile
2030
+ *
2031
+ * This simplifies nodes that just need to transform file bytes without
2032
+ * worrying about upload server interactions.
2033
+ *
2034
+ * Supports both buffered and streaming modes:
2035
+ * - **Buffered mode**: Loads entire file into memory, transforms, uploads
2036
+ * - **Streaming mode**: Processes file as chunks for memory efficiency with large files
2037
+ * - **Auto mode** (default): Selects mode based on file size and DataStore capabilities
2038
+ *
2039
+ * @param config - Configuration object for the transform node
2040
+ * @returns An Effect that creates a flow node configured for file transformation
2041
+ *
2042
+ * @example
2043
+ * ```typescript
2044
+ * // Create a transform node with auto mode (default) - uses streaming for large files
2045
+ * const resizeNode = yield* createTransformNode({
2046
+ * id: "resize-image",
2047
+ * name: "Resize Image",
2048
+ * description: "Resizes images to specified dimensions",
2049
+ * transform: (bytes, file) => {
2050
+ * // Your transformation logic here
2051
+ * return Effect.succeed(transformedBytes);
2052
+ * },
2053
+ * streamingTransform: (stream, file) => {
2054
+ * const transformed = Stream.map(stream, (chunk) => processChunk(chunk));
2055
+ * return Effect.succeed(transformed);
2056
+ * }
2057
+ * });
2058
+ *
2059
+ * // Force buffered mode for specific use cases
2060
+ * const bufferedNode = yield* createTransformNode({
2061
+ * id: "optimize-small",
2062
+ * name: "Optimize Small Files",
2063
+ * description: "Optimizes small files with buffered mode",
2064
+ * mode: "buffered",
2065
+ * transform: (bytes, file) => Effect.succeed(transformBytes(bytes)),
2066
+ * });
2067
+ *
2068
+ * // Force streaming mode for memory efficiency
2069
+ * const streamingNode = yield* createTransformNode({
2070
+ * id: "optimize-large",
2071
+ * name: "Optimize Large Files",
2072
+ * description: "Optimizes large files with streaming",
2073
+ * mode: "streaming",
2074
+ * streamingTransform: (stream, file) => {
2075
+ * const transformed = Stream.map(stream, (chunk) => processChunk(chunk));
2076
+ * return Effect.succeed(transformed);
2077
+ * }
2078
+ * });
2079
+ * ```
2080
+ */
2081
+ declare function createTransformNode({
2082
+ id,
2083
+ name,
2084
+ description,
2085
+ outputTypeId,
2086
+ keepOutput,
2087
+ naming,
2088
+ nodeType: namingNodeType,
2089
+ nodeTypeId,
2090
+ namingVars,
2091
+ circuitBreaker,
2092
+ mode,
2093
+ streamingConfig,
2094
+ transform,
2095
+ streamingTransform
2096
+ }: TransformNodeConfig): Effect.Effect<FlowNodeData & {
2097
+ inputSchema: zod.ZodType<UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<UploadFile, unknown>>;
2098
+ outputSchema: zod.ZodType<UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<UploadFile, unknown>>;
2099
+ run: (args: {
2100
+ data: UploadFile;
2101
+ jobId: string;
2102
+ storageId: string;
2103
+ flowId: string;
2104
+ inputs?: Record<string, unknown>;
2105
+ clientId: string | null;
2106
+ }) => Effect.Effect<NodeExecutionResult<UploadFile>, UploadistaError, never>;
2107
+ condition?: {
2108
+ field: string;
2109
+ operator: string;
2110
+ value: unknown;
2111
+ };
2112
+ multiInput?: boolean;
2113
+ multiOutput?: boolean;
2114
+ pausable?: boolean;
2115
+ retry?: {
2116
+ maxRetries?: number;
2117
+ retryDelay?: number;
2118
+ exponentialBackoff?: boolean;
2119
+ };
2120
+ circuitBreaker?: FlowCircuitBreakerConfig;
2121
+ } & {
2122
+ type: NodeType;
2123
+ }, UploadistaError, UploadEngine>;
2124
+ //#endregion
2125
+ //#region src/flow/parallel-scheduler.d.ts
2126
+ /**
2127
+ * Represents a level in the execution hierarchy where all nodes can run in parallel.
2128
+ *
2129
+ * @property level - The execution level (0 = first to execute, higher = later)
2130
+ * @property nodes - Array of node IDs that can execute in parallel at this level
2131
+ *
2132
+ * @example
2133
+ * ```
2134
+ * Level 0: [input_node] (no dependencies)
2135
+ * Level 1: [resize, optimize] (all depend on level 0)
2136
+ * Level 2: [storage] (depends on level 1)
2137
+ * ```
2138
+ */
2139
+ interface ExecutionLevel {
2140
+ level: number;
2141
+ nodes: string[];
2142
+ }
2143
+ /**
2144
+ * Configuration options for the ParallelScheduler.
2145
+ *
2146
+ * @property maxConcurrency - Maximum number of nodes to execute in parallel (default: 4)
2147
+ * Controls how many nodes run simultaneously within a level
2148
+ *
2149
+ * @example
2150
+ * ```typescript
2151
+ * const scheduler = new ParallelScheduler({ maxConcurrency: 8 });
2152
+ * ```
2153
+ */
2154
+ interface ParallelSchedulerConfig {
2155
+ maxConcurrency?: number;
2156
+ }
2157
+ /**
2158
+ * Scheduler for executing flow nodes in parallel while respecting dependencies.
2159
+ *
2160
+ * The scheduler performs topological sorting to identify nodes that can run
2161
+ * concurrently, groups them into execution levels, and provides methods to
2162
+ * execute them with controlled concurrency using Effect.
2163
+ *
2164
+ * Key responsibilities:
2165
+ * - Analyze flow dependencies and detect cycles
2166
+ * - Group nodes into parallel execution levels
2167
+ * - Execute levels in parallel with concurrency limits
2168
+ * - Provide utilities to check parallel execution feasibility
2169
+ */
2170
+ declare class ParallelScheduler {
2171
+ private maxConcurrency;
2172
+ /**
2173
+ * Creates a new ParallelScheduler instance.
2174
+ *
2175
+ * @param config - Configuration for the scheduler
2176
+ * @example
2177
+ * ```typescript
2178
+ * const scheduler = new ParallelScheduler({ maxConcurrency: 4 });
2179
+ * ```
2180
+ */
2181
+ constructor(config?: ParallelSchedulerConfig);
2182
+ /**
2183
+ * Groups nodes into execution levels where nodes in the same level can run in parallel.
2184
+ *
2185
+ * Uses Kahn's algorithm to perform topological sorting with level identification.
2186
+ * Nodes are grouped by their distance from source nodes (input nodes with no dependencies).
2187
+ *
2188
+ * @param nodes - Array of flow nodes to analyze
2189
+ * @param edges - Array of edges defining dependencies between nodes
2190
+ * @returns Array of execution levels, ordered from 0 (no dependencies) onwards
2191
+ * @throws Error if a cycle is detected in the flow graph
2192
+ *
2193
+ * @example
2194
+ * ```typescript
2195
+ * const levels = scheduler.groupNodesByExecutionLevel(nodes, edges);
2196
+ * // levels = [
2197
+ * // { level: 0, nodes: ['input_1'] },
2198
+ * // { level: 1, nodes: ['resize_1', 'optimize_1'] },
2199
+ * // { level: 2, nodes: ['output_1'] }
2200
+ * // ]
2201
+ * ```
2202
+ */
2203
+ groupNodesByExecutionLevel(nodes: FlowNode<unknown, unknown>[], edges: Array<{
2204
+ source: string;
2205
+ target: string;
2206
+ }>): ExecutionLevel[];
2207
+ /**
2208
+ * Executes a batch of Effect-based node executors in parallel with concurrency control.
2209
+ *
2210
+ * All executors are run in parallel, but the number of concurrent executions is limited
2211
+ * by maxConcurrency. This prevents resource exhaustion while maximizing parallelism.
2212
+ *
2213
+ * @template T - The return type of each executor
2214
+ * @template E - The error type of the Effects
2215
+ * @template R - The requirements type of the Effects
2216
+ *
2217
+ * @param nodeExecutors - Array of Effect-returning functions to execute in parallel
2218
+ * @returns Effect that resolves to array of results in the same order as input
2219
+ *
2220
+ * @example
2221
+ * ```typescript
2222
+ * const results = yield* scheduler.executeNodesInParallel([
2223
+ * () => executeNode("node1"),
2224
+ * () => executeNode("node2"),
2225
+ * () => executeNode("node3")
2226
+ * ]);
2227
+ * // results will be in order: [result1, result2, result3]
2228
+ * ```
2229
+ */
2230
+ executeNodesInParallel<T, E, R>(nodeExecutors: Array<() => Effect.Effect<T, E, R>>): Effect.Effect<T[], E, R>;
2231
+ /**
2232
+ * Determines if a set of nodes can be safely executed in parallel.
2233
+ *
2234
+ * Nodes can execute in parallel if all their dependencies have been completed.
2235
+ * This is typically called to verify that nodes in an execution level are ready
2236
+ * to run given the current node results.
2237
+ *
2238
+ * @param nodeIds - Array of node IDs to check
2239
+ * @param nodeResults - Map of completed node IDs to their results
2240
+ * @param reverseGraph - Dependency graph mapping node IDs to their incoming dependencies
2241
+ * @returns true if all dependencies for all nodes are in nodeResults, false otherwise
2242
+ *
2243
+ * @example
2244
+ * ```typescript
2245
+ * const canRun = scheduler.canExecuteInParallel(
2246
+ * ['resize_1', 'optimize_1'],
2247
+ * nodeResults,
2248
+ * reverseGraph
2249
+ * );
2250
+ * ```
2251
+ */
2252
+ canExecuteInParallel(nodeIds: string[], nodeResults: Map<string, unknown>, reverseGraph: Record<string, string[]>): boolean;
2253
+ /**
2254
+ * Gets execution statistics for monitoring and debugging.
2255
+ *
2256
+ * @returns Object containing current scheduler configuration
2257
+ *
2258
+ * @example
2259
+ * ```typescript
2260
+ * const stats = scheduler.getStats();
2261
+ * console.log(`Max concurrency: ${stats.maxConcurrency}`);
2262
+ * ```
2263
+ */
2264
+ getStats(): {
2265
+ maxConcurrency: number;
2266
+ };
2267
+ }
2268
+ //#endregion
2269
+ //#region src/flow/plugins/credential-provider.d.ts
2270
+ /**
2271
+ * Shape definition for the Credential Provider interface.
2272
+ * Defines the contract for retrieving credentials for various services.
2273
+ */
2274
+ interface CredentialProviderShape {
2275
+ /**
2276
+ * Retrieves credentials for a specific service and client.
2277
+ *
2278
+ * @param params - Parameters for credential retrieval
2279
+ * @param params.clientId - Unique identifier for the client, or null if not available
2280
+ * @param params.serviceType - Optional service type to get specific credentials for
2281
+ * @returns An Effect that resolves to a record of credential key-value pairs
2282
+ * @throws {UploadistaError} When credential retrieval fails
2283
+ */
2284
+ getCredential: (params: {
2285
+ clientId: string | null;
2286
+ serviceType?: string;
2287
+ }) => Effect.Effect<Record<string, unknown>, UploadistaError>;
2288
+ }
2289
+ declare const CredentialProvider_base: Context.TagClass<CredentialProvider, "CredentialProvider", CredentialProviderShape>;
2290
+ /**
2291
+ * Context tag for the Credential Provider.
2292
+ *
2293
+ * This tag provides a type-safe way to access credential functionality
2294
+ * throughout the application using Effect's dependency injection system.
2295
+ *
2296
+ * @example
2297
+ * ```typescript
2298
+ * import { CredentialProvider } from "@uploadista/core/flow/plugins";
2299
+ *
2300
+ * // In your flow node
2301
+ * const program = Effect.gen(function* () {
2302
+ * const credentialProvider = yield* CredentialProvider;
2303
+ * const credentials = yield* credentialProvider.getCredential({
2304
+ * clientId: "user123",
2305
+ * serviceType: "replicate"
2306
+ * });
2307
+ * return credentials;
2308
+ * });
2309
+ * ```
2310
+ */
2311
+ declare class CredentialProvider extends CredentialProvider_base {}
2312
+ type CredentialProviderLayer = Layer.Layer<CredentialProvider, never, never>;
2313
+ //#endregion
2314
+ //#region src/flow/plugins/document-ai-plugin.d.ts
2315
+ /**
2316
+ * Context information for AI document processing operations.
2317
+ * Contains client identification and credentials for tracking and billing purposes.
2318
+ */
2319
+ type DocumentAiContext = {
2320
+ /** Unique identifier for the client making the request, or null if not available */clientId: string | null; /** Credential ID for accessing the AI service (e.g., Replicate API key) */
2321
+ credentialId?: string;
2322
+ };
2323
+ /**
2324
+ * Task types supported by OCR operations.
2325
+ */
2326
+ type OcrTaskType = "convertToMarkdown" | "freeOcr" | "parseFigure" | "locateObject";
2327
+ /**
2328
+ * Resolution options for OCR processing.
2329
+ * Higher resolutions provide better accuracy but slower processing.
2330
+ */
2331
+ type OcrResolution = "tiny" | "small" | "base" | "gundam" | "large";
2332
+ /**
2333
+ * Parameters for OCR operations.
2334
+ */
2335
+ type OcrParams = {
2336
+ /**
2337
+ * Type of OCR task to perform.
2338
+ * - "convertToMarkdown": Convert document to structured Markdown
2339
+ * - "freeOcr": Extract all visible text without structure
2340
+ * - "parseFigure": Analyze charts and diagrams
2341
+ * - "locateObject": Find specific content using reference text
2342
+ */
2343
+ taskType: OcrTaskType;
2344
+ /**
2345
+ * Resolution size for processing.
2346
+ * Affects speed/accuracy tradeoff.
2347
+ * Default: "gundam" (recommended)
2348
+ */
2349
+ resolution?: OcrResolution;
2350
+ /**
2351
+ * Reference text for object location tasks.
2352
+ * Only used when taskType is "locateObject".
2353
+ */
2354
+ referenceText?: string;
2355
+ };
2356
+ /**
2357
+ * Result of an OCR operation.
2358
+ */
2359
+ type OcrResult = {
2360
+ /**
2361
+ * The extracted text content.
2362
+ */
2363
+ extractedText: string;
2364
+ /**
2365
+ * Format of the extracted text.
2366
+ * - "markdown": Structured markdown format
2367
+ * - "plain": Unstructured plain text
2368
+ * - "structured": Structured analysis (for figures)
2369
+ */
2370
+ format: "markdown" | "plain" | "structured";
2371
+ /**
2372
+ * Confidence score (0-1) if provided by the service.
2373
+ */
2374
+ confidence?: number;
2375
+ };
2376
+ /**
2377
+ * Shape definition for the Document AI Plugin interface.
2378
+ * Defines the contract that all document AI implementations must follow.
2379
+ */
2380
+ type DocumentAiPluginShape = {
2381
+ /**
2382
+ * Performs OCR on a document image or scanned PDF using AI.
2383
+ *
2384
+ * @param inputUrl - The URL of the input document/image to process
2385
+ * @param params - OCR parameters including task type and resolution
2386
+ * @param context - Context information including client ID for tracking
2387
+ * @returns An Effect that resolves to OcrResult with extracted text
2388
+ * @throws {UploadistaError} When OCR operation fails
2389
+ */
2390
+ performOCR: (inputUrl: string, params: OcrParams, context: DocumentAiContext) => Effect.Effect<OcrResult, UploadistaError>;
2391
+ };
2392
+ declare const DocumentAiPlugin_base: Context.TagClass<DocumentAiPlugin, "DocumentAiPlugin", DocumentAiPluginShape>;
2393
+ /**
2394
+ * Context tag for the Document AI Plugin.
2395
+ *
2396
+ * This tag provides a type-safe way to access document AI functionality
2397
+ * throughout the application using Effect's dependency injection system.
2398
+ *
2399
+ * @example
2400
+ * ```typescript
2401
+ * import { DocumentAiPlugin } from "@uploadista/core/flow/plugins";
2402
+ *
2403
+ * // In your flow node
2404
+ * const program = Effect.gen(function* () {
2405
+ * const documentAi = yield* DocumentAiPlugin;
2406
+ * const result = yield* documentAi.performOCR(
2407
+ * documentUrl,
2408
+ * { taskType: "convertToMarkdown", resolution: "gundam" },
2409
+ * { clientId: "user123" }
2410
+ * );
2411
+ * return result.extractedText;
2412
+ * });
2413
+ * ```
2414
+ */
2415
+ declare class DocumentAiPlugin extends DocumentAiPlugin_base {}
2416
+ type DocumentAiPluginLayer = Layer.Layer<DocumentAiPlugin, never, never>;
2417
+ //#endregion
2418
+ //#region src/flow/plugins/document-plugin.d.ts
2419
+ /**
2420
+ * Parameters for splitting a PDF document.
2421
+ */
2422
+ type SplitPdfParams = {
2423
+ /**
2424
+ * Mode of split operation.
2425
+ * - "range": Extract a contiguous range of pages
2426
+ * - "individual": Split into individual single-page PDFs
2427
+ */
2428
+ mode: "range" | "individual";
2429
+ /**
2430
+ * Starting page number (1-indexed).
2431
+ * Only used in "range" mode.
2432
+ */
2433
+ startPage?: number;
2434
+ /**
2435
+ * Ending page number (1-indexed, inclusive).
2436
+ * Only used in "range" mode.
2437
+ */
2438
+ endPage?: number;
2439
+ };
2440
+ /**
2441
+ * Result of a split PDF operation.
2442
+ * In "range" mode, returns a single PDF.
2443
+ * In "individual" mode, returns an array of single-page PDFs.
2444
+ */
2445
+ type SplitPdfResult = {
2446
+ mode: "range";
2447
+ pdf: Uint8Array;
2448
+ } | {
2449
+ mode: "individual";
2450
+ pdfs: Uint8Array[];
2451
+ };
2452
+ /**
2453
+ * Parameters for merging multiple PDF documents.
2454
+ */
2455
+ type MergePdfParams = {
2456
+ /**
2457
+ * Array of PDF documents to merge (in order).
2458
+ */
2459
+ pdfs: Uint8Array[];
2460
+ };
2461
+ /**
2462
+ * Metadata extracted from a PDF document.
2463
+ */
2464
+ type DocumentMetadata = {
2465
+ /**
2466
+ * Total number of pages in the document.
2467
+ */
2468
+ pageCount: number;
2469
+ /**
2470
+ * Document format (e.g., "pdf").
2471
+ */
2472
+ format: string;
2473
+ /**
2474
+ * Author of the document (if available).
2475
+ */
2476
+ author: string | null;
2477
+ /**
2478
+ * Title of the document (if available).
2479
+ */
2480
+ title: string | null;
2481
+ /**
2482
+ * Subject of the document (if available).
2483
+ */
2484
+ subject: string | null;
2485
+ /**
2486
+ * Creator application (if available).
2487
+ */
2488
+ creator: string | null;
2489
+ /**
2490
+ * Creation date in ISO 8601 format (if available).
2491
+ */
2492
+ creationDate: string | null;
2493
+ /**
2494
+ * Last modification date in ISO 8601 format (if available).
2495
+ */
2496
+ modifiedDate: string | null;
2497
+ /**
2498
+ * File size in bytes.
2499
+ */
2500
+ fileSize: number;
2501
+ };
2502
+ /**
2503
+ * Shape definition for the Document Plugin interface.
2504
+ * Defines the contract that all document processing implementations must follow.
2505
+ */
2506
+ type DocumentPluginShape = {
2507
+ /**
2508
+ * Extracts plain text from a searchable PDF document.
2509
+ *
2510
+ * @param input - The input PDF as a Uint8Array
2511
+ * @returns An Effect that resolves to the extracted text as a string
2512
+ * @throws {UploadistaError} When text extraction fails (e.g., PDF_ENCRYPTED, PDF_CORRUPTED)
2513
+ */
2514
+ extractText: (input: Uint8Array) => Effect.Effect<string, UploadistaError>;
2515
+ /**
2516
+ * Splits a PDF document by page range or into individual pages.
2517
+ *
2518
+ * @param input - The input PDF as a Uint8Array
2519
+ * @param options - Split parameters including mode and page range
2520
+ * @returns An Effect that resolves to either a single PDF or array of PDFs
2521
+ * @throws {UploadistaError} When splitting fails (e.g., PAGE_RANGE_INVALID)
2522
+ */
2523
+ splitPdf: (input: Uint8Array, options: SplitPdfParams) => Effect.Effect<SplitPdfResult, UploadistaError>;
2524
+ /**
2525
+ * Merges multiple PDF documents into a single document.
2526
+ *
2527
+ * @param options - Merge parameters including array of PDFs to merge
2528
+ * @returns An Effect that resolves to the merged PDF as a Uint8Array
2529
+ * @throws {UploadistaError} When merging fails
2530
+ */
2531
+ mergePdfs: (options: MergePdfParams) => Effect.Effect<Uint8Array, UploadistaError>;
2532
+ /**
2533
+ * Extracts metadata from a PDF document.
2534
+ *
2535
+ * @param input - The input PDF as a Uint8Array
2536
+ * @returns An Effect that resolves to DocumentMetadata with comprehensive document information
2537
+ * @throws {UploadistaError} When metadata extraction fails
2538
+ */
2539
+ getMetadata: (input: Uint8Array) => Effect.Effect<DocumentMetadata, UploadistaError>;
2540
+ };
2541
+ declare const DocumentPlugin_base: Context.TagClass<DocumentPlugin, "DocumentPlugin", DocumentPluginShape>;
2542
+ /**
2543
+ * Context tag for the Document Plugin.
2544
+ *
2545
+ * This tag provides a type-safe way to access document processing functionality
2546
+ * throughout the application using Effect's dependency injection system.
2547
+ *
2548
+ * @example
2549
+ * ```typescript
2550
+ * import { DocumentPlugin } from "@uploadista/core/flow/plugins";
2551
+ *
2552
+ * // In your flow node
2553
+ * const program = Effect.gen(function* () {
2554
+ * const documentPlugin = yield* DocumentPlugin;
2555
+ * const text = yield* documentPlugin.extractText(pdfData);
2556
+ * const metadata = yield* documentPlugin.getMetadata(pdfData);
2557
+ * return { text, metadata };
2558
+ * });
2559
+ * ```
2560
+ */
2561
+ declare class DocumentPlugin extends DocumentPlugin_base {}
2562
+ type DocumentPluginLayer = Layer.Layer<DocumentPlugin, never, never>;
2563
+ //#endregion
2564
+ //#region src/flow/plugins/image-ai-plugin.d.ts
2565
+ /**
2566
+ * Context information for AI image processing operations.
2567
+ * Contains client identification for tracking and billing purposes.
2568
+ */
2569
+ type ImageAiContext = {
2570
+ /** Unique identifier for the client making the request, or null if not available */clientId: string | null;
2571
+ };
2572
+ /**
2573
+ * Shape definition for the Image AI Plugin interface.
2574
+ * Defines the contract that all image AI implementations must follow.
2575
+ */
2576
+ type ImageAiPluginShape = {
2577
+ /**
2578
+ * Removes the background from an image using AI processing.
2579
+ *
2580
+ * @param inputUrl - The URL of the input image to process
2581
+ * @param context - Context information including client ID for tracking
2582
+ * @returns An Effect that resolves to an object containing the output image URL
2583
+ * @throws {UploadistaError} When the background removal fails
2584
+ */
2585
+ removeBackground: (inputUrl: string, context: ImageAiContext) => Effect.Effect<{
2586
+ outputUrl: string;
2587
+ }, UploadistaError>;
2588
+ /**
2589
+ * Generates a textual description of an image using AI analysis.
2590
+ *
2591
+ * @param inputUrl - The URL of the input image to analyze
2592
+ * @param context - Context information including client ID for tracking
2593
+ * @returns An Effect that resolves to an object containing the image description
2594
+ * @throws {UploadistaError} When the image analysis fails
2595
+ */
2596
+ describeImage: (inputUrl: string, context: ImageAiContext) => Effect.Effect<{
2597
+ description: string;
2598
+ }, UploadistaError>;
2599
+ };
2600
+ declare const ImageAiPlugin_base: Context.TagClass<ImageAiPlugin, "ImageAiPlugin", ImageAiPluginShape>;
2601
+ /**
2602
+ * Context tag for the Image AI Plugin.
2603
+ *
2604
+ * This tag provides a type-safe way to access image AI functionality
2605
+ * throughout the application using Effect's dependency injection system.
2606
+ *
2607
+ * @example
2608
+ * ```typescript
2609
+ * import { ImageAiPlugin } from "@uploadista/core/flow/plugins";
2610
+ *
2611
+ * // In your flow node
2612
+ * const program = Effect.gen(function* () {
2613
+ * const imageAi = yield* ImageAiPlugin;
2614
+ * const result = yield* imageAi.removeBackground(imageUrl, { clientId: "user123" });
2615
+ * return result.outputUrl;
2616
+ * });
2617
+ * ```
2618
+ */
2619
+ declare class ImageAiPlugin extends ImageAiPlugin_base {}
2620
+ type ImageAiPluginLayer = Layer.Layer<ImageAiPlugin, never, never>;
2621
+ //#endregion
2622
+ //#region src/flow/plugins/types/optimize-node.d.ts
2623
+ /**
2624
+ * Zod schema for validating image optimization parameters.
2625
+ * Defines the structure and validation rules for image optimization requests.
2626
+ */
2627
+ declare const optimizeParamsSchema: z.ZodObject<{
2628
+ quality: z.ZodNumber;
2629
+ format: z.ZodEnum<{
2630
+ jpeg: "jpeg";
2631
+ webp: "webp";
2632
+ png: "png";
2633
+ avif: "avif";
2634
+ }>;
2635
+ }, z.core.$strip>;
2636
+ /**
2637
+ * Parameters for the image optimization node.
2638
+ * Controls quality and format settings for image optimization.
2639
+ */
2640
+ type OptimizeParams = z.infer<typeof optimizeParamsSchema>;
2641
+ //#endregion
2642
+ //#region src/flow/plugins/types/resize-node.d.ts
2643
+ /**
2644
+ * Zod schema for validating image resize parameters.
2645
+ * Defines the structure and validation rules for image resizing requests.
2646
+ * Requires at least one dimension (width or height) to be specified.
2647
+ */
2648
+ declare const resizeParamsSchema: z.ZodObject<{
2649
+ width: z.ZodOptional<z.ZodNumber>;
2650
+ height: z.ZodOptional<z.ZodNumber>;
2651
+ fit: z.ZodEnum<{
2652
+ fill: "fill";
2653
+ contain: "contain";
2654
+ cover: "cover";
2655
+ }>;
2656
+ }, z.core.$strip>;
2657
+ /**
2658
+ * Parameters for the image resize node.
2659
+ * Controls the target dimensions and fitting behavior for image resizing.
2660
+ */
2661
+ type ResizeParams = z.infer<typeof resizeParamsSchema>;
2662
+ //#endregion
2663
+ //#region src/flow/plugins/types/transform-image-node.d.ts
2664
+ /**
2665
+ * Type of transformation to apply to an image.
2666
+ */
2667
+ type TransformationType = "resize" | "blur" | "rotate" | "flip" | "grayscale" | "sepia" | "brightness" | "contrast" | "sharpen" | "watermark" | "logo" | "text";
2668
+ /**
2669
+ * Resize transformation parameters.
2670
+ * Resizes the image to the specified dimensions with the given fit mode.
2671
+ */
2672
+ declare const resizeTransformSchema: z.ZodObject<{
2673
+ type: z.ZodLiteral<"resize">;
2674
+ width: z.ZodOptional<z.ZodNumber>;
2675
+ height: z.ZodOptional<z.ZodNumber>;
2676
+ fit: z.ZodEnum<{
2677
+ fill: "fill";
2678
+ contain: "contain";
2679
+ cover: "cover";
2680
+ }>;
2681
+ }, z.core.$strip>;
2682
+ type ResizeTransform = z.infer<typeof resizeTransformSchema>;
2683
+ /**
2684
+ * Blur transformation parameters.
2685
+ * Applies Gaussian blur to the image.
2686
+ */
2687
+ declare const blurTransformSchema: z.ZodObject<{
2688
+ type: z.ZodLiteral<"blur">;
2689
+ sigma: z.ZodNumber;
2690
+ }, z.core.$strip>;
2691
+ type BlurTransform = z.infer<typeof blurTransformSchema>;
2692
+ /**
2693
+ * Rotate transformation parameters.
2694
+ * Rotates the image by the specified angle.
2695
+ */
2696
+ declare const rotateTransformSchema: z.ZodObject<{
2697
+ type: z.ZodLiteral<"rotate">;
2698
+ angle: z.ZodNumber;
2699
+ background: z.ZodOptional<z.ZodString>;
2700
+ }, z.core.$strip>;
2701
+ type RotateTransform = z.infer<typeof rotateTransformSchema>;
2702
+ /**
2703
+ * Flip transformation parameters.
2704
+ * Flips the image horizontally or vertically.
2705
+ */
2706
+ declare const flipTransformSchema: z.ZodObject<{
2707
+ type: z.ZodLiteral<"flip">;
2708
+ direction: z.ZodEnum<{
2709
+ horizontal: "horizontal";
2710
+ vertical: "vertical";
2711
+ }>;
2712
+ }, z.core.$strip>;
2713
+ type FlipTransform = z.infer<typeof flipTransformSchema>;
2714
+ /**
2715
+ * Grayscale transformation parameters.
2716
+ * Converts the image to grayscale.
2717
+ */
2718
+ declare const grayscaleTransformSchema: z.ZodObject<{
2719
+ type: z.ZodLiteral<"grayscale">;
2720
+ }, z.core.$strip>;
2721
+ type GrayscaleTransform = z.infer<typeof grayscaleTransformSchema>;
2722
+ /**
2723
+ * Sepia transformation parameters.
2724
+ * Applies a sepia tone effect to the image.
2725
+ */
2726
+ declare const sepiaTransformSchema: z.ZodObject<{
2727
+ type: z.ZodLiteral<"sepia">;
2728
+ }, z.core.$strip>;
2729
+ type SepiaTransform = z.infer<typeof sepiaTransformSchema>;
2730
+ /**
2731
+ * Brightness transformation parameters.
2732
+ * Adjusts the brightness of the image.
2733
+ */
2734
+ declare const brightnessTransformSchema: z.ZodObject<{
2735
+ type: z.ZodLiteral<"brightness">;
2736
+ value: z.ZodNumber;
2737
+ }, z.core.$strip>;
2738
+ type BrightnessTransform = z.infer<typeof brightnessTransformSchema>;
2739
+ /**
2740
+ * Contrast transformation parameters.
2741
+ * Adjusts the contrast of the image.
2742
+ */
2743
+ declare const contrastTransformSchema: z.ZodObject<{
2744
+ type: z.ZodLiteral<"contrast">;
2745
+ value: z.ZodNumber;
2746
+ }, z.core.$strip>;
2747
+ type ContrastTransform = z.infer<typeof contrastTransformSchema>;
2748
+ /**
2749
+ * Sharpen transformation parameters.
2750
+ * Applies sharpening to the image.
2751
+ */
2752
+ declare const sharpenTransformSchema: z.ZodObject<{
2753
+ type: z.ZodLiteral<"sharpen">;
2754
+ sigma: z.ZodOptional<z.ZodNumber>;
2755
+ }, z.core.$strip>;
2756
+ type SharpenTransform = z.infer<typeof sharpenTransformSchema>;
2757
+ /**
2758
+ * Position for overlays (watermarks, logos, text).
2759
+ */
2760
+ type OverlayPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
2761
+ /**
2762
+ * Watermark transformation parameters.
2763
+ * Overlays a watermark image on the main image.
2764
+ */
2765
+ declare const watermarkTransformSchema: z.ZodObject<{
2766
+ type: z.ZodLiteral<"watermark">;
2767
+ imagePath: z.ZodString;
2768
+ position: z.ZodEnum<{
2769
+ "top-left": "top-left";
2770
+ "top-right": "top-right";
2771
+ "bottom-left": "bottom-left";
2772
+ "bottom-right": "bottom-right";
2773
+ center: "center";
2774
+ }>;
2775
+ opacity: z.ZodNumber;
2776
+ offsetX: z.ZodOptional<z.ZodNumber>;
2777
+ offsetY: z.ZodOptional<z.ZodNumber>;
2778
+ }, z.core.$strip>;
2779
+ type WatermarkTransform = z.infer<typeof watermarkTransformSchema>;
2780
+ /**
2781
+ * Logo transformation parameters.
2782
+ * Overlays a logo image on the main image with scaling.
2783
+ */
2784
+ declare const logoTransformSchema: z.ZodObject<{
2785
+ type: z.ZodLiteral<"logo">;
2786
+ imagePath: z.ZodString;
2787
+ position: z.ZodEnum<{
2788
+ "top-left": "top-left";
2789
+ "top-right": "top-right";
2790
+ "bottom-left": "bottom-left";
2791
+ "bottom-right": "bottom-right";
2792
+ center: "center";
2793
+ }>;
2794
+ scale: z.ZodNumber;
2795
+ offsetX: z.ZodOptional<z.ZodNumber>;
2796
+ offsetY: z.ZodOptional<z.ZodNumber>;
2797
+ }, z.core.$strip>;
2798
+ type LogoTransform = z.infer<typeof logoTransformSchema>;
2799
+ /**
2800
+ * Text transformation parameters.
2801
+ * Overlays text on the image.
2802
+ */
2803
+ declare const textTransformSchema: z.ZodObject<{
2804
+ type: z.ZodLiteral<"text">;
2805
+ text: z.ZodString;
2806
+ position: z.ZodEnum<{
2807
+ "top-left": "top-left";
2808
+ "top-right": "top-right";
2809
+ "bottom-left": "bottom-left";
2810
+ "bottom-right": "bottom-right";
2811
+ center: "center";
2812
+ }>;
2813
+ fontSize: z.ZodNumber;
2814
+ color: z.ZodString;
2815
+ fontFamily: z.ZodOptional<z.ZodString>;
2816
+ offsetX: z.ZodOptional<z.ZodNumber>;
2817
+ offsetY: z.ZodOptional<z.ZodNumber>;
2818
+ }, z.core.$strip>;
2819
+ type TextTransform = z.infer<typeof textTransformSchema>;
2820
+ /**
2821
+ * Schema for validating any transformation type.
2822
+ * This is a discriminated union of all transformation schemas.
2823
+ */
2824
+ declare const transformationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
2825
+ type: z.ZodLiteral<"resize">;
2826
+ width: z.ZodOptional<z.ZodNumber>;
2827
+ height: z.ZodOptional<z.ZodNumber>;
2828
+ fit: z.ZodEnum<{
2829
+ fill: "fill";
2830
+ contain: "contain";
2831
+ cover: "cover";
2832
+ }>;
2833
+ }, z.core.$strip>, z.ZodObject<{
2834
+ type: z.ZodLiteral<"blur">;
2835
+ sigma: z.ZodNumber;
2836
+ }, z.core.$strip>, z.ZodObject<{
2837
+ type: z.ZodLiteral<"rotate">;
2838
+ angle: z.ZodNumber;
2839
+ background: z.ZodOptional<z.ZodString>;
2840
+ }, z.core.$strip>, z.ZodObject<{
2841
+ type: z.ZodLiteral<"flip">;
2842
+ direction: z.ZodEnum<{
2843
+ horizontal: "horizontal";
2844
+ vertical: "vertical";
2845
+ }>;
2846
+ }, z.core.$strip>, z.ZodObject<{
2847
+ type: z.ZodLiteral<"grayscale">;
2848
+ }, z.core.$strip>, z.ZodObject<{
2849
+ type: z.ZodLiteral<"sepia">;
2850
+ }, z.core.$strip>, z.ZodObject<{
2851
+ type: z.ZodLiteral<"brightness">;
2852
+ value: z.ZodNumber;
2853
+ }, z.core.$strip>, z.ZodObject<{
2854
+ type: z.ZodLiteral<"contrast">;
2855
+ value: z.ZodNumber;
2856
+ }, z.core.$strip>, z.ZodObject<{
2857
+ type: z.ZodLiteral<"sharpen">;
2858
+ sigma: z.ZodOptional<z.ZodNumber>;
2859
+ }, z.core.$strip>, z.ZodObject<{
2860
+ type: z.ZodLiteral<"watermark">;
2861
+ imagePath: z.ZodString;
2862
+ position: z.ZodEnum<{
2863
+ "top-left": "top-left";
2864
+ "top-right": "top-right";
2865
+ "bottom-left": "bottom-left";
2866
+ "bottom-right": "bottom-right";
2867
+ center: "center";
2868
+ }>;
2869
+ opacity: z.ZodNumber;
2870
+ offsetX: z.ZodOptional<z.ZodNumber>;
2871
+ offsetY: z.ZodOptional<z.ZodNumber>;
2872
+ }, z.core.$strip>, z.ZodObject<{
2873
+ type: z.ZodLiteral<"logo">;
2874
+ imagePath: z.ZodString;
2875
+ position: z.ZodEnum<{
2876
+ "top-left": "top-left";
2877
+ "top-right": "top-right";
2878
+ "bottom-left": "bottom-left";
2879
+ "bottom-right": "bottom-right";
2880
+ center: "center";
2881
+ }>;
2882
+ scale: z.ZodNumber;
2883
+ offsetX: z.ZodOptional<z.ZodNumber>;
2884
+ offsetY: z.ZodOptional<z.ZodNumber>;
2885
+ }, z.core.$strip>, z.ZodObject<{
2886
+ type: z.ZodLiteral<"text">;
2887
+ text: z.ZodString;
2888
+ position: z.ZodEnum<{
2889
+ "top-left": "top-left";
2890
+ "top-right": "top-right";
2891
+ "bottom-left": "bottom-left";
2892
+ "bottom-right": "bottom-right";
2893
+ center: "center";
2894
+ }>;
2895
+ fontSize: z.ZodNumber;
2896
+ color: z.ZodString;
2897
+ fontFamily: z.ZodOptional<z.ZodString>;
2898
+ offsetX: z.ZodOptional<z.ZodNumber>;
2899
+ offsetY: z.ZodOptional<z.ZodNumber>;
2900
+ }, z.core.$strip>], "type">;
2901
+ /**
2902
+ * A single image transformation operation.
2903
+ * This is a discriminated union type that can represent any transformation.
2904
+ */
2905
+ type Transformation = z.infer<typeof transformationSchema>;
2906
+ /**
2907
+ * Parameters for the transform image node.
2908
+ * Contains an ordered array of transformations to apply sequentially.
2909
+ */
2910
+ declare const transformImageParamsSchema: z.ZodObject<{
2911
+ transformations: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
2912
+ type: z.ZodLiteral<"resize">;
2913
+ width: z.ZodOptional<z.ZodNumber>;
2914
+ height: z.ZodOptional<z.ZodNumber>;
2915
+ fit: z.ZodEnum<{
2916
+ fill: "fill";
2917
+ contain: "contain";
2918
+ cover: "cover";
2919
+ }>;
2920
+ }, z.core.$strip>, z.ZodObject<{
2921
+ type: z.ZodLiteral<"blur">;
2922
+ sigma: z.ZodNumber;
2923
+ }, z.core.$strip>, z.ZodObject<{
2924
+ type: z.ZodLiteral<"rotate">;
2925
+ angle: z.ZodNumber;
2926
+ background: z.ZodOptional<z.ZodString>;
2927
+ }, z.core.$strip>, z.ZodObject<{
2928
+ type: z.ZodLiteral<"flip">;
2929
+ direction: z.ZodEnum<{
2930
+ horizontal: "horizontal";
2931
+ vertical: "vertical";
2932
+ }>;
2933
+ }, z.core.$strip>, z.ZodObject<{
2934
+ type: z.ZodLiteral<"grayscale">;
2935
+ }, z.core.$strip>, z.ZodObject<{
2936
+ type: z.ZodLiteral<"sepia">;
2937
+ }, z.core.$strip>, z.ZodObject<{
2938
+ type: z.ZodLiteral<"brightness">;
2939
+ value: z.ZodNumber;
2940
+ }, z.core.$strip>, z.ZodObject<{
2941
+ type: z.ZodLiteral<"contrast">;
2942
+ value: z.ZodNumber;
2943
+ }, z.core.$strip>, z.ZodObject<{
2944
+ type: z.ZodLiteral<"sharpen">;
2945
+ sigma: z.ZodOptional<z.ZodNumber>;
2946
+ }, z.core.$strip>, z.ZodObject<{
2947
+ type: z.ZodLiteral<"watermark">;
2948
+ imagePath: z.ZodString;
2949
+ position: z.ZodEnum<{
2950
+ "top-left": "top-left";
2951
+ "top-right": "top-right";
2952
+ "bottom-left": "bottom-left";
2953
+ "bottom-right": "bottom-right";
2954
+ center: "center";
2955
+ }>;
2956
+ opacity: z.ZodNumber;
2957
+ offsetX: z.ZodOptional<z.ZodNumber>;
2958
+ offsetY: z.ZodOptional<z.ZodNumber>;
2959
+ }, z.core.$strip>, z.ZodObject<{
2960
+ type: z.ZodLiteral<"logo">;
2961
+ imagePath: z.ZodString;
2962
+ position: z.ZodEnum<{
2963
+ "top-left": "top-left";
2964
+ "top-right": "top-right";
2965
+ "bottom-left": "bottom-left";
2966
+ "bottom-right": "bottom-right";
2967
+ center: "center";
2968
+ }>;
2969
+ scale: z.ZodNumber;
2970
+ offsetX: z.ZodOptional<z.ZodNumber>;
2971
+ offsetY: z.ZodOptional<z.ZodNumber>;
2972
+ }, z.core.$strip>, z.ZodObject<{
2973
+ type: z.ZodLiteral<"text">;
2974
+ text: z.ZodString;
2975
+ position: z.ZodEnum<{
2976
+ "top-left": "top-left";
2977
+ "top-right": "top-right";
2978
+ "bottom-left": "bottom-left";
2979
+ "bottom-right": "bottom-right";
2980
+ center: "center";
2981
+ }>;
2982
+ fontSize: z.ZodNumber;
2983
+ color: z.ZodString;
2984
+ fontFamily: z.ZodOptional<z.ZodString>;
2985
+ offsetX: z.ZodOptional<z.ZodNumber>;
2986
+ offsetY: z.ZodOptional<z.ZodNumber>;
2987
+ }, z.core.$strip>], "type">>;
2988
+ }, z.core.$strip>;
2989
+ /**
2990
+ * Parameters for the transform image node.
2991
+ */
2992
+ type TransformImageParams = z.infer<typeof transformImageParamsSchema>;
2993
+ //#endregion
2994
+ //#region src/flow/plugins/image-plugin.d.ts
2995
+ /**
2996
+ * Shape definition for the Image Plugin interface.
2997
+ * Defines the contract that all image processing implementations must follow.
2998
+ */
2999
+ type ImagePluginShape = {
3000
+ /**
3001
+ * Optimizes an image by adjusting quality and format.
3002
+ *
3003
+ * @param input - The input image as a Uint8Array
3004
+ * @param options - Optimization parameters including quality and format
3005
+ * @returns An Effect that resolves to the optimized image as a Uint8Array
3006
+ * @throws {UploadistaError} When image optimization fails
3007
+ */
3008
+ optimize: (input: Uint8Array, options: OptimizeParams) => Effect.Effect<Uint8Array, UploadistaError>;
3009
+ /**
3010
+ * Resizes an image to specified dimensions.
3011
+ *
3012
+ * @param input - The input image as a Uint8Array
3013
+ * @param options - Resize parameters including width, height, and fit mode
3014
+ * @returns An Effect that resolves to the resized image as a Uint8Array
3015
+ * @throws {UploadistaError} When image resizing fails
3016
+ */
3017
+ resize: (input: Uint8Array, options: ResizeParams) => Effect.Effect<Uint8Array, UploadistaError>;
3018
+ /**
3019
+ * Applies a single transformation to an image.
3020
+ *
3021
+ * This method is used by the transform image node to apply individual transformations
3022
+ * in a chain. Each transformation receives the output of the previous transformation.
3023
+ *
3024
+ * @param input - The input image as a Uint8Array
3025
+ * @param transformation - The transformation to apply (discriminated union)
3026
+ * @returns An Effect that resolves to the transformed image as a Uint8Array
3027
+ * @throws {UploadistaError} When transformation fails or is unsupported by the plugin
3028
+ *
3029
+ * @example
3030
+ * ```typescript
3031
+ * const program = Effect.gen(function* () {
3032
+ * const imagePlugin = yield* ImagePlugin;
3033
+ *
3034
+ * // Apply a single transformation
3035
+ * const blurred = yield* imagePlugin.transform(imageData, {
3036
+ * type: 'blur',
3037
+ * sigma: 5.0
3038
+ * });
3039
+ *
3040
+ * // Chain multiple transformations
3041
+ * const resized = yield* imagePlugin.transform(blurred, {
3042
+ * type: 'resize',
3043
+ * width: 800,
3044
+ * height: 600,
3045
+ * fit: 'cover'
3046
+ * });
3047
+ *
3048
+ * return resized;
3049
+ * });
3050
+ * ```
3051
+ */
3052
+ transform: (input: Uint8Array, transformation: Transformation) => Effect.Effect<Uint8Array, UploadistaError>;
3053
+ /**
3054
+ * Optimizes an image using streaming for memory-efficient processing of large files.
3055
+ *
3056
+ * This method processes image data as a stream, which is beneficial for large images
3057
+ * where loading the entire file into memory would be problematic.
3058
+ *
3059
+ * Note: Image processing inherently requires decoding the full image, so memory
3060
+ * savings are primarily from avoiding double-buffering. The streaming interface
3061
+ * allows better pipeline integration with DataStore streaming reads.
3062
+ *
3063
+ * @param input - The input image as an Effect Stream of Uint8Array chunks
3064
+ * @param options - Optimization parameters including quality and format
3065
+ * @returns An Effect that resolves to a Stream of the optimized image bytes
3066
+ * @throws {UploadistaError} When image optimization fails
3067
+ *
3068
+ * @example
3069
+ * ```typescript
3070
+ * const program = Effect.gen(function* () {
3071
+ * const imagePlugin = yield* ImagePlugin;
3072
+ * const inputStream = yield* dataStore.readStream(fileId);
3073
+ * const outputStream = yield* imagePlugin.optimizeStream(inputStream, {
3074
+ * quality: 80,
3075
+ * format: "webp"
3076
+ * });
3077
+ * return outputStream;
3078
+ * });
3079
+ * ```
3080
+ */
3081
+ optimizeStream?: (input: Stream.Stream<Uint8Array, UploadistaError>, options: OptimizeParams) => Effect.Effect<Stream.Stream<Uint8Array, UploadistaError>, UploadistaError>;
3082
+ /**
3083
+ * Resizes an image using streaming for memory-efficient processing of large files.
3084
+ *
3085
+ * This method processes image data as a stream. Like other image operations,
3086
+ * the full image must be decoded before processing, but the streaming interface
3087
+ * avoids double-buffering when combined with streaming DataStore reads and writes.
3088
+ *
3089
+ * @param input - The input image as an Effect Stream of Uint8Array chunks
3090
+ * @param options - Resize parameters including width, height, and fit mode
3091
+ * @returns An Effect that resolves to a Stream of the resized image bytes
3092
+ * @throws {UploadistaError} When image resizing fails
3093
+ *
3094
+ * @example
3095
+ * ```typescript
3096
+ * const program = Effect.gen(function* () {
3097
+ * const imagePlugin = yield* ImagePlugin;
3098
+ * const inputStream = yield* dataStore.readStream(fileId);
3099
+ * const outputStream = yield* imagePlugin.resizeStream(inputStream, {
3100
+ * width: 800,
3101
+ * height: 600,
3102
+ * fit: "cover"
3103
+ * });
3104
+ * return outputStream;
3105
+ * });
3106
+ * ```
3107
+ */
3108
+ resizeStream?: (input: Stream.Stream<Uint8Array, UploadistaError>, options: ResizeParams) => Effect.Effect<Stream.Stream<Uint8Array, UploadistaError>, UploadistaError>;
3109
+ /**
3110
+ * Applies a single transformation using streaming for memory-efficient processing.
3111
+ *
3112
+ * This method processes image data as a stream. The streaming interface
3113
+ * allows better pipeline integration with DataStore streaming reads and writes,
3114
+ * reducing peak memory usage for large files.
3115
+ *
3116
+ * @param input - The input image as an Effect Stream of Uint8Array chunks
3117
+ * @param transformation - The transformation to apply
3118
+ * @returns An Effect that resolves to a Stream of the transformed image bytes
3119
+ * @throws {UploadistaError} When transformation fails
3120
+ *
3121
+ * @example
3122
+ * ```typescript
3123
+ * const program = Effect.gen(function* () {
3124
+ * const imagePlugin = yield* ImagePlugin;
3125
+ * const inputStream = yield* dataStore.readStream(fileId);
3126
+ * const outputStream = yield* imagePlugin.transformStream(inputStream, {
3127
+ * type: 'blur',
3128
+ * sigma: 5.0
3129
+ * });
3130
+ * return outputStream;
3131
+ * });
3132
+ * ```
3133
+ */
3134
+ transformStream?: (input: Stream.Stream<Uint8Array, UploadistaError>, transformation: Transformation) => Effect.Effect<Stream.Stream<Uint8Array, UploadistaError>, UploadistaError>;
3135
+ /**
3136
+ * Indicates whether this plugin supports streaming operations.
3137
+ * Returns true if streaming methods (optimizeStream, resizeStream, transformStream) are available.
3138
+ */
3139
+ supportsStreaming?: boolean;
3140
+ };
3141
+ declare const ImagePlugin_base: Context.TagClass<ImagePlugin, "ImagePlugin", ImagePluginShape>;
3142
+ /**
3143
+ * Context tag for the Image Plugin.
3144
+ *
3145
+ * This tag provides a type-safe way to access image processing functionality
3146
+ * throughout the application using Effect's dependency injection system.
3147
+ *
3148
+ * @example
3149
+ * ```typescript
3150
+ * import { ImagePlugin } from "@uploadista/core/flow/plugins";
3151
+ *
3152
+ * // In your flow node
3153
+ * const program = Effect.gen(function* () {
3154
+ * const imagePlugin = yield* ImagePlugin;
3155
+ * const optimized = yield* imagePlugin.optimize(imageData, { quality: 80, format: "webp" });
3156
+ * const resized = yield* imagePlugin.resize(optimized, { width: 800, height: 600, fit: "cover" });
3157
+ * return resized;
3158
+ * });
3159
+ * ```
3160
+ */
3161
+ declare class ImagePlugin extends ImagePlugin_base {}
3162
+ type ImagePluginLayer = Layer.Layer<ImagePlugin, never, never>;
3163
+ //#endregion
3164
+ //#region src/flow/plugins/types/describe-video-node.d.ts
3165
+ /**
3166
+ * Zod schema for video metadata extracted by the describe operation.
3167
+ * Defines the structure and validation rules for video metadata.
3168
+ */
3169
+ declare const describeVideoMetadataSchema: z.ZodObject<{
3170
+ duration: z.ZodNumber;
3171
+ width: z.ZodNumber;
3172
+ height: z.ZodNumber;
3173
+ codec: z.ZodString;
3174
+ format: z.ZodString;
3175
+ bitrate: z.ZodNumber;
3176
+ frameRate: z.ZodNumber;
3177
+ aspectRatio: z.ZodString;
3178
+ hasAudio: z.ZodBoolean;
3179
+ audioCodec: z.ZodOptional<z.ZodString>;
3180
+ audioBitrate: z.ZodOptional<z.ZodNumber>;
3181
+ size: z.ZodNumber;
3182
+ }, z.core.$strip>;
3183
+ /**
3184
+ * Video metadata extracted by the describe operation.
3185
+ * Contains comprehensive information about video properties, codecs, and audio.
3186
+ */
3187
+ type DescribeVideoMetadata = z.infer<typeof describeVideoMetadataSchema>;
3188
+ //#endregion
3189
+ //#region src/flow/plugins/types/extract-frame-video-node.d.ts
3190
+ /**
3191
+ * Zod schema for validating video frame extraction parameters.
3192
+ * Defines the structure and validation rules for extracting a single frame from video.
3193
+ */
3194
+ declare const extractFrameVideoParamsSchema: z.ZodObject<{
3195
+ timestamp: z.ZodNumber;
3196
+ format: z.ZodOptional<z.ZodEnum<{
3197
+ jpeg: "jpeg";
3198
+ png: "png";
3199
+ }>>;
3200
+ quality: z.ZodOptional<z.ZodNumber>;
3201
+ }, z.core.$strip>;
3202
+ /**
3203
+ * Parameters for the video frame extraction node.
3204
+ * Controls the timestamp and output format for extracting a single frame from video.
3205
+ */
3206
+ type ExtractFrameVideoParams = z.infer<typeof extractFrameVideoParamsSchema>;
3207
+ //#endregion
3208
+ //#region src/flow/plugins/types/resize-video-node.d.ts
3209
+ /**
3210
+ * Zod schema for validating video resize parameters.
3211
+ * Defines the structure and validation rules for video resolution changes.
3212
+ * Requires at least one dimension (width or height) to be specified.
3213
+ */
3214
+ declare const resizeVideoParamsSchema: z.ZodObject<{
3215
+ width: z.ZodOptional<z.ZodNumber>;
3216
+ height: z.ZodOptional<z.ZodNumber>;
3217
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
3218
+ keep: "keep";
3219
+ ignore: "ignore";
3220
+ }>>;
3221
+ scaling: z.ZodOptional<z.ZodEnum<{
3222
+ bicubic: "bicubic";
3223
+ bilinear: "bilinear";
3224
+ lanczos: "lanczos";
3225
+ }>>;
3226
+ }, z.core.$strip>;
3227
+ /**
3228
+ * Parameters for the video resize node.
3229
+ * Controls the target dimensions and aspect ratio handling for video resizing.
3230
+ */
3231
+ type ResizeVideoParams = z.infer<typeof resizeVideoParamsSchema>;
3232
+ //#endregion
3233
+ //#region src/flow/plugins/types/transcode-video-node.d.ts
3234
+ /**
3235
+ * Zod schema for validating video transcode parameters.
3236
+ * Defines the structure and validation rules for video format and codec conversion.
3237
+ */
3238
+ declare const transcodeVideoParamsSchema: z.ZodObject<{
3239
+ format: z.ZodEnum<{
3240
+ mp4: "mp4";
3241
+ webm: "webm";
3242
+ mov: "mov";
3243
+ avi: "avi";
3244
+ }>;
3245
+ codec: z.ZodOptional<z.ZodEnum<{
3246
+ h264: "h264";
3247
+ h265: "h265";
3248
+ vp9: "vp9";
3249
+ av1: "av1";
3250
+ }>>;
3251
+ videoBitrate: z.ZodOptional<z.ZodString>;
3252
+ audioBitrate: z.ZodOptional<z.ZodString>;
3253
+ audioCodec: z.ZodOptional<z.ZodEnum<{
3254
+ aac: "aac";
3255
+ mp3: "mp3";
3256
+ opus: "opus";
3257
+ vorbis: "vorbis";
3258
+ }>>;
3259
+ }, z.core.$strip>;
3260
+ /**
3261
+ * Parameters for the video transcode node.
3262
+ * Controls output format, codecs, and quality settings for video transcoding.
3263
+ */
3264
+ type TranscodeVideoParams = z.infer<typeof transcodeVideoParamsSchema>;
3265
+ //#endregion
3266
+ //#region src/flow/plugins/types/trim-video-node.d.ts
3267
+ /**
3268
+ * Zod schema for validating video trim parameters.
3269
+ * Defines the structure and validation rules for extracting video segments.
3270
+ */
3271
+ declare const trimVideoParamsSchema: z.ZodObject<{
3272
+ startTime: z.ZodNumber;
3273
+ endTime: z.ZodOptional<z.ZodNumber>;
3274
+ duration: z.ZodOptional<z.ZodNumber>;
3275
+ }, z.core.$strip>;
3276
+ /**
3277
+ * Parameters for the video trim node.
3278
+ * Controls the time range for extracting video segments.
3279
+ */
3280
+ type TrimVideoParams = z.infer<typeof trimVideoParamsSchema>;
3281
+ //#endregion
3282
+ //#region src/flow/plugins/video-plugin.d.ts
3283
+ /**
3284
+ * Input type for streaming video operations.
3285
+ * Accepts either buffered input (Uint8Array) or streaming input (Effect Stream).
3286
+ * Streaming input is only supported for specific formats like MPEG-TS.
3287
+ */
3288
+ type VideoStreamInput = Uint8Array | Stream.Stream<Uint8Array, UploadistaError>;
3289
+ /**
3290
+ * Options for streaming video operations.
3291
+ */
3292
+ type VideoStreamOptions = {
3293
+ /**
3294
+ * Hint for input format to help determine if streaming input is possible.
3295
+ * MPEG-TS format supports true streaming input; other formats require buffering.
3296
+ */
3297
+ inputFormat?: string;
3298
+ };
3299
+ /**
3300
+ * Shape definition for the Video Plugin interface.
3301
+ * Defines the contract that all video processing implementations must follow.
3302
+ */
3303
+ type VideoPluginShape = {
3304
+ /**
3305
+ * Transcodes a video to a different format/codec.
3306
+ *
3307
+ * @param input - The input video as a Uint8Array
3308
+ * @param options - Transcode parameters including format, codec, and bitrates
3309
+ * @returns An Effect that resolves to the transcoded video as a Uint8Array
3310
+ * @throws {UploadistaError} When video transcoding fails
3311
+ */
3312
+ transcode: (input: Uint8Array, options: TranscodeVideoParams) => Effect.Effect<Uint8Array, UploadistaError>;
3313
+ /**
3314
+ * Resizes a video to specified dimensions.
3315
+ *
3316
+ * @param input - The input video as a Uint8Array
3317
+ * @param options - Resize parameters including width, height, and aspect ratio handling
3318
+ * @returns An Effect that resolves to the resized video as a Uint8Array
3319
+ * @throws {UploadistaError} When video resizing fails
3320
+ */
3321
+ resize: (input: Uint8Array, options: ResizeVideoParams) => Effect.Effect<Uint8Array, UploadistaError>;
3322
+ /**
3323
+ * Trims a video to extract a segment by time range.
3324
+ *
3325
+ * @param input - The input video as a Uint8Array
3326
+ * @param options - Trim parameters including start time and end time/duration
3327
+ * @returns An Effect that resolves to the trimmed video as a Uint8Array
3328
+ * @throws {UploadistaError} When video trimming fails
3329
+ */
3330
+ trim: (input: Uint8Array, options: TrimVideoParams) => Effect.Effect<Uint8Array, UploadistaError>;
3331
+ /**
3332
+ * Extracts a single frame from the video at a specific timestamp.
3333
+ *
3334
+ * @param input - The input video as a Uint8Array
3335
+ * @param options - Frame extraction parameters including timestamp and format
3336
+ * @returns An Effect that resolves to the extracted frame as a Uint8Array (image)
3337
+ * @throws {UploadistaError} When frame extraction fails
3338
+ */
3339
+ extractFrame: (input: Uint8Array, options: ExtractFrameVideoParams) => Effect.Effect<Uint8Array, UploadistaError>;
3340
+ /**
3341
+ * Extracts metadata from a video file.
3342
+ *
3343
+ * @param input - The input video as a Uint8Array
3344
+ * @returns An Effect that resolves to VideoMetadata with comprehensive video information
3345
+ * @throws {UploadistaError} When metadata extraction fails
3346
+ */
3347
+ describe: (input: Uint8Array) => Effect.Effect<DescribeVideoMetadata, UploadistaError>;
3348
+ /**
3349
+ * Transcodes a video using streaming for memory-efficient processing of large files.
3350
+ *
3351
+ * This method outputs the transcoded video as a stream, reducing peak memory usage.
3352
+ * For input, it accepts either a buffered Uint8Array or a Stream. Streaming input
3353
+ * is only supported for MPEG-TS format; other formats will be buffered internally.
3354
+ *
3355
+ * @param input - The input video as Uint8Array or Stream (MPEG-TS only for streaming)
3356
+ * @param options - Transcode parameters including format, codec, and bitrates
3357
+ * @param streamOptions - Optional streaming configuration including input format hint
3358
+ * @returns An Effect that resolves to a Stream of the transcoded video bytes
3359
+ * @throws {UploadistaError} When video transcoding fails
3360
+ *
3361
+ * @example
3362
+ * ```typescript
3363
+ * const program = Effect.gen(function* () {
3364
+ * const videoPlugin = yield* VideoPlugin;
3365
+ * const inputStream = yield* dataStore.readStream(fileId);
3366
+ * const outputStream = yield* videoPlugin.transcodeStream(inputStream, {
3367
+ * format: "mp4",
3368
+ * codec: "h264"
3369
+ * }, { inputFormat: "video/mp2t" });
3370
+ * return outputStream;
3371
+ * });
3372
+ * ```
3373
+ */
3374
+ transcodeStream?: (input: VideoStreamInput, options: TranscodeVideoParams, streamOptions?: VideoStreamOptions) => Effect.Effect<Stream.Stream<Uint8Array, UploadistaError>, UploadistaError>;
3375
+ /**
3376
+ * Resizes a video using streaming for memory-efficient processing of large files.
3377
+ *
3378
+ * This method outputs the resized video as a stream, reducing peak memory usage.
3379
+ * For input, it accepts either a buffered Uint8Array or a Stream. Streaming input
3380
+ * is only supported for MPEG-TS format; other formats will be buffered internally.
3381
+ *
3382
+ * @param input - The input video as Uint8Array or Stream (MPEG-TS only for streaming)
3383
+ * @param options - Resize parameters including width, height, and aspect ratio
3384
+ * @param streamOptions - Optional streaming configuration including input format hint
3385
+ * @returns An Effect that resolves to a Stream of the resized video bytes
3386
+ * @throws {UploadistaError} When video resizing fails
3387
+ *
3388
+ * @example
3389
+ * ```typescript
3390
+ * const program = Effect.gen(function* () {
3391
+ * const videoPlugin = yield* VideoPlugin;
3392
+ * const inputStream = yield* dataStore.readStream(fileId);
3393
+ * const outputStream = yield* videoPlugin.resizeStream(inputStream, {
3394
+ * width: 1280,
3395
+ * height: 720,
3396
+ * aspectRatio: "keep"
3397
+ * });
3398
+ * return outputStream;
3399
+ * });
3400
+ * ```
3401
+ */
3402
+ resizeStream?: (input: VideoStreamInput, options: ResizeVideoParams, streamOptions?: VideoStreamOptions) => Effect.Effect<Stream.Stream<Uint8Array, UploadistaError>, UploadistaError>;
3403
+ /**
3404
+ * Trims a video using streaming for memory-efficient processing of large files.
3405
+ *
3406
+ * This method outputs the trimmed video as a stream, reducing peak memory usage.
3407
+ * For input, it accepts either a buffered Uint8Array or a Stream. Streaming input
3408
+ * is only supported for MPEG-TS format; other formats will be buffered internally.
3409
+ *
3410
+ * @param input - The input video as Uint8Array or Stream (MPEG-TS only for streaming)
3411
+ * @param options - Trim parameters including start time and end time/duration
3412
+ * @param streamOptions - Optional streaming configuration including input format hint
3413
+ * @returns An Effect that resolves to a Stream of the trimmed video bytes
3414
+ * @throws {UploadistaError} When video trimming fails
3415
+ *
3416
+ * @example
3417
+ * ```typescript
3418
+ * const program = Effect.gen(function* () {
3419
+ * const videoPlugin = yield* VideoPlugin;
3420
+ * const inputStream = yield* dataStore.readStream(fileId);
3421
+ * const outputStream = yield* videoPlugin.trimStream(inputStream, {
3422
+ * startTime: 10,
3423
+ * endTime: 30
3424
+ * });
3425
+ * return outputStream;
3426
+ * });
3427
+ * ```
3428
+ */
3429
+ trimStream?: (input: VideoStreamInput, options: TrimVideoParams, streamOptions?: VideoStreamOptions) => Effect.Effect<Stream.Stream<Uint8Array, UploadistaError>, UploadistaError>;
3430
+ /**
3431
+ * Indicates whether this plugin supports streaming operations.
3432
+ * Returns true if streaming methods are available and functional.
3433
+ */
3434
+ supportsStreaming?: boolean;
3435
+ };
3436
+ declare const VideoPlugin_base: Context.TagClass<VideoPlugin, "VideoPlugin", VideoPluginShape>;
3437
+ /**
3438
+ * Context tag for the Video Plugin.
3439
+ *
3440
+ * This tag provides a type-safe way to access video processing functionality
3441
+ * throughout the application using Effect's dependency injection system.
3442
+ *
3443
+ * @example
3444
+ * ```typescript
3445
+ * import { VideoPlugin } from "@uploadista/core/flow/plugins";
3446
+ *
3447
+ * // In your flow node
3448
+ * const program = Effect.gen(function* () {
3449
+ * const videoPlugin = yield* VideoPlugin;
3450
+ * const transcoded = yield* videoPlugin.transcode(videoData, { format: "webm", codec: "vp9" });
3451
+ * const resized = yield* videoPlugin.resize(transcoded, { width: 1280, height: 720, aspectRatio: "keep" });
3452
+ * return resized;
3453
+ * });
3454
+ * ```
3455
+ */
3456
+ declare class VideoPlugin extends VideoPlugin_base {}
3457
+ type VideoPluginLayer = Layer.Layer<VideoPlugin, never, never>;
3458
+ //#endregion
3459
+ //#region src/flow/plugins/virus-scan-plugin.d.ts
3460
+ /**
3461
+ * Result of a virus scan operation.
3462
+ */
3463
+ type ScanResult = {
3464
+ /**
3465
+ * Whether the file is clean (no viruses detected)
3466
+ */
3467
+ isClean: boolean;
3468
+ /**
3469
+ * Array of detected virus/malware names (empty if clean)
3470
+ */
3471
+ detectedViruses: string[];
3472
+ };
3473
+ /**
3474
+ * Comprehensive metadata about a virus scan operation.
3475
+ */
3476
+ type ScanMetadata = {
3477
+ /**
3478
+ * Whether the file was scanned
3479
+ */
3480
+ scanned: boolean;
3481
+ /**
3482
+ * Whether the file is clean (no viruses detected)
3483
+ */
3484
+ isClean: boolean;
3485
+ /**
3486
+ * Array of detected virus/malware names (empty if clean)
3487
+ */
3488
+ detectedViruses: string[];
3489
+ /**
3490
+ * ISO 8601 timestamp of when the scan was performed
3491
+ */
3492
+ scanDate: string;
3493
+ /**
3494
+ * Version of the antivirus engine used
3495
+ */
3496
+ engineVersion: string;
3497
+ /**
3498
+ * ISO 8601 timestamp of when virus definitions were last updated
3499
+ */
3500
+ definitionsDate: string;
3501
+ };
3502
+ /**
3503
+ * Shape definition for the Virus Scan Plugin interface.
3504
+ * Defines the contract that all virus scanning implementations must follow.
3505
+ */
3506
+ type VirusScanPluginShape = {
3507
+ /**
3508
+ * Scans a file for viruses and malware.
3509
+ *
3510
+ * @param input - The input file as a Uint8Array
3511
+ * @returns An Effect that resolves to ScanResult with detection information
3512
+ * @throws {UploadistaError} When virus scanning fails or ClamAV is unavailable
3513
+ *
3514
+ * @example
3515
+ * ```typescript
3516
+ * const program = Effect.gen(function* () {
3517
+ * const virusScanPlugin = yield* VirusScanPlugin;
3518
+ * const result = yield* virusScanPlugin.scan(fileData);
3519
+ * if (!result.isClean) {
3520
+ * console.log('Viruses detected:', result.detectedViruses);
3521
+ * }
3522
+ * });
3523
+ * ```
3524
+ */
3525
+ scan: (input: Uint8Array) => Effect.Effect<ScanResult, UploadistaError>;
3526
+ /**
3527
+ * Retrieves the version of the antivirus engine.
3528
+ *
3529
+ * @returns An Effect that resolves to the engine version string
3530
+ * @throws {UploadistaError} When version retrieval fails
3531
+ *
3532
+ * @example
3533
+ * ```typescript
3534
+ * const program = Effect.gen(function* () {
3535
+ * const virusScanPlugin = yield* VirusScanPlugin;
3536
+ * const version = yield* virusScanPlugin.getVersion();
3537
+ * console.log('ClamAV version:', version);
3538
+ * });
3539
+ * ```
3540
+ */
3541
+ getVersion: () => Effect.Effect<string, UploadistaError>;
3542
+ };
3543
+ declare const VirusScanPlugin_base: Context.TagClass<VirusScanPlugin, "VirusScanPlugin", VirusScanPluginShape>;
3544
+ /**
3545
+ * Context tag for the Virus Scan Plugin.
3546
+ *
3547
+ * This tag provides a type-safe way to access virus scanning functionality
3548
+ * throughout the application using Effect's dependency injection system.
3549
+ *
3550
+ * @example
3551
+ * ```typescript
3552
+ * import { VirusScanPlugin } from "@uploadista/core/flow/plugins";
3553
+ *
3554
+ * // In your flow node
3555
+ * const program = Effect.gen(function* () {
3556
+ * const virusScanPlugin = yield* VirusScanPlugin;
3557
+ * const result = yield* virusScanPlugin.scan(fileData);
3558
+ *
3559
+ * if (!result.isClean) {
3560
+ * // Handle infected file
3561
+ * return Effect.fail(new UploadistaError({
3562
+ * code: "VIRUS_DETECTED",
3563
+ * message: `Viruses detected: ${result.detectedViruses.join(', ')}`
3564
+ * }));
3565
+ * }
3566
+ *
3567
+ * return fileData;
3568
+ * });
3569
+ * ```
3570
+ */
3571
+ declare class VirusScanPlugin extends VirusScanPlugin_base {}
3572
+ type VirusScanPluginLayer = Layer.Layer<VirusScanPlugin, never, never>;
3573
+ //#endregion
3574
+ //#region src/flow/plugins/zip-plugin.d.ts
3575
+ /**
3576
+ * Parameters for creating a ZIP archive.
3577
+ */
3578
+ type ZipParams = {
3579
+ /** Name of the ZIP file to create */zipName: string; /** Whether to include file metadata in the ZIP archive */
3580
+ includeMetadata: boolean;
3581
+ };
3582
+ /**
3583
+ * Input data structure for ZIP operations.
3584
+ * Represents a single file to be included in the ZIP archive.
3585
+ */
3586
+ type ZipInput = {
3587
+ /** Unique identifier for the file */id: string; /** Binary data of the file */
3588
+ data: Uint8Array; /** File metadata including name, size, type, etc. */
3589
+ metadata: UploadFile["metadata"];
3590
+ };
3591
+ /**
3592
+ * Shape definition for the ZIP Plugin interface.
3593
+ * Defines the contract that all ZIP implementations must follow.
3594
+ */
3595
+ type ZipPluginShape = {
3596
+ /**
3597
+ * Creates a ZIP archive from multiple input files.
3598
+ *
3599
+ * @param inputs - Array of files to include in the ZIP archive
3600
+ * @param options - Configuration options for the ZIP creation
3601
+ * @returns An Effect that resolves to the ZIP file as a Uint8Array
3602
+ * @throws {UploadistaError} When ZIP creation fails
3603
+ */
3604
+ zip: (inputs: ZipInput[], options: ZipParams) => Effect.Effect<Uint8Array, UploadistaError>;
3605
+ };
3606
+ declare const ZipPlugin_base: Context.TagClass<ZipPlugin, "ZipPlugin", ZipPluginShape>;
3607
+ /**
3608
+ * Context tag for the ZIP Plugin.
3609
+ *
3610
+ * This tag provides a type-safe way to access ZIP functionality
3611
+ * throughout the application using Effect's dependency injection system.
3612
+ *
3613
+ * @example
3614
+ * ```typescript
3615
+ * import { ZipPlugin } from "@uploadista/core/flow/plugins";
3616
+ *
3617
+ * // In your flow node
3618
+ * const program = Effect.gen(function* () {
3619
+ * const zipPlugin = yield* ZipPlugin;
3620
+ * const zipData = yield* zipPlugin.zip(files, { zipName: "archive.zip", includeMetadata: true });
3621
+ * return zipData;
3622
+ * });
3623
+ * ```
3624
+ */
3625
+ declare class ZipPlugin extends ZipPlugin_base {}
3626
+ type ZipPluginLayer = Layer.Layer<ZipPlugin, never, never>;
3627
+ //#endregion
3628
+ //#region src/flow/plugins/plugins.d.ts
3629
+ type Plugin = ImagePlugin | ImageAiPlugin | VideoPlugin | DocumentPlugin | DocumentAiPlugin | VirusScanPlugin | CredentialProvider | ZipPlugin;
3630
+ type PluginLayer = ImagePluginLayer | ImageAiPluginLayer | VideoPluginLayer | DocumentPluginLayer | DocumentAiPluginLayer | VirusScanPluginLayer | CredentialProviderLayer | ZipPluginLayer;
3631
+ //#endregion
3632
+ //#region src/flow/plugins/types/describe-image-node.d.ts
3633
+ /**
3634
+ * Zod schema for validating describe image node parameters.
3635
+ * Defines the structure and validation rules for image description requests.
3636
+ */
3637
+ declare const describeImageParamsSchema: z.ZodObject<{
3638
+ serviceType: z.ZodOptional<z.ZodEnum<{
3639
+ replicate: "replicate";
3640
+ }>>;
3641
+ }, z.core.$strip>;
3642
+ /**
3643
+ * Parameters for the describe image node.
3644
+ * Controls which AI service to use for generating image descriptions.
3645
+ */
3646
+ type DescribeImageParams = z.infer<typeof describeImageParamsSchema>;
3647
+ //#endregion
3648
+ //#region src/flow/plugins/types/remove-background-node.d.ts
3649
+ /**
3650
+ * Zod schema for validating remove background node parameters.
3651
+ * Defines the structure and validation rules for background removal requests.
3652
+ */
3653
+ declare const removeBackgroundParamsSchema: z.ZodObject<{
3654
+ serviceType: z.ZodOptional<z.ZodEnum<{
3655
+ replicate: "replicate";
3656
+ }>>;
3657
+ }, z.core.$strip>;
3658
+ /**
3659
+ * Parameters for the remove background node.
3660
+ * Controls which AI service to use for background removal processing.
3661
+ */
3662
+ type RemoveBackgroundParams = z.infer<typeof removeBackgroundParamsSchema>;
3663
+ //#endregion
3664
+ //#region src/flow/type-guards.d.ts
3665
+ /**
3666
+ * A narrowed typed output with a specific node type and data type.
3667
+ * Unlike TypedOutput<T>, this type has a required nodeType field and
3668
+ * excludes BuiltInTypedOutput from the union, providing better type narrowing.
3669
+ *
3670
+ * @template T - The TypeScript type of the output data
3671
+ * @template TNodeType - The literal string type of the node type ID
3672
+ */
3673
+ type NarrowedTypedOutput<T, TNodeType extends string = string> = {
3674
+ nodeType: TNodeType;
3675
+ data: T;
3676
+ nodeId: string;
3677
+ timestamp: string;
3678
+ };
3679
+ /**
3680
+ * Factory function to create type guards for specific node types.
3681
+ *
3682
+ * Creates a TypeScript type guard that validates both the type tag and
3683
+ * the data structure against the registered schema. This enables type-safe
3684
+ * narrowing of TypedOutput objects in TypeScript.
3685
+ *
3686
+ * @template T - The expected TypeScript type after narrowing
3687
+ * @template TNodeType - The literal string type of the node type ID
3688
+ * @param typeId - The registered type ID to check against (e.g., "storage-output-v1")
3689
+ * @returns A type guard function that narrows TypedOutput to NarrowedTypedOutput<T, TNodeType>
3690
+ *
3691
+ * @example
3692
+ * ```typescript
3693
+ * import { createTypeGuard } from "@uploadista/core/flow";
3694
+ * import { z } from "zod";
3695
+ *
3696
+ * const descriptionSchema = z.object({
3697
+ * description: z.string(),
3698
+ * confidence: z.number(),
3699
+ * });
3700
+ *
3701
+ * type DescriptionOutput = z.infer<typeof descriptionSchema>;
3702
+ *
3703
+ * const isDescriptionOutput = createTypeGuard<DescriptionOutput>(
3704
+ * "description-output-v1"
3705
+ * );
3706
+ *
3707
+ * // Use in code
3708
+ * if (isDescriptionOutput(output)) {
3709
+ * // output.data is typed as DescriptionOutput
3710
+ * console.log(output.data.description);
3711
+ * }
3712
+ * ```
3713
+ */
3714
+ declare function createTypeGuard<T, TNodeType extends string = string>(typeId: TNodeType): (output: TypedOutput) => output is NarrowedTypedOutput<T, TNodeType>;
3715
+ /**
3716
+ * Type guard for UploadFile objects.
3717
+ *
3718
+ * Validates that a value is a valid UploadFile by checking its structure against the schema.
3719
+ * This is useful for determining if a node result is an UploadFile, which affects
3720
+ * auto-persistence and intermediate file tracking.
3721
+ *
3722
+ * @param value - The value to check
3723
+ * @returns True if the value is a valid UploadFile
3724
+ *
3725
+ * @example
3726
+ * ```typescript
3727
+ * import { isUploadFile } from "@uploadista/core/flow";
3728
+ *
3729
+ * if (isUploadFile(nodeResult)) {
3730
+ * // nodeResult is typed as UploadFile
3731
+ * console.log("File ID:", nodeResult.id);
3732
+ * console.log("Storage:", nodeResult.storage.id);
3733
+ * }
3734
+ * ```
3735
+ */
3736
+ declare function isUploadFile(value: unknown): value is UploadFile;
3737
+ /**
3738
+ * Type guard for storage output nodes.
3739
+ *
3740
+ * Validates that an output is from a storage node and contains valid UploadFile data.
3741
+ *
3742
+ * @param output - The output to check
3743
+ * @returns True if the output is a storage output with valid UploadFile data
3744
+ *
3745
+ * @example
3746
+ * ```typescript
3747
+ * import { isStorageOutput } from "@uploadista/core/flow";
3748
+ *
3749
+ * if (isStorageOutput(output)) {
3750
+ * // output.data is typed as UploadFile
3751
+ * console.log("File URL:", output.data.url);
3752
+ * console.log("File size:", output.data.size);
3753
+ * }
3754
+ * ```
3755
+ */
3756
+ declare const isStorageOutput: (output: TypedOutput) => output is NarrowedTypedOutput<UploadFile, string>;
3757
+ /**
3758
+ * Type guard for OCR output nodes.
3759
+ *
3760
+ * Validates that an output is from an OCR node and contains valid structured OCR data.
3761
+ *
3762
+ * @param output - The output to check
3763
+ * @returns True if the output is an OCR output with valid structured text data
3764
+ *
3765
+ * @example
3766
+ * ```typescript
3767
+ * import { isOcrOutput } from "@uploadista/core/flow";
3768
+ *
3769
+ * if (isOcrOutput(output)) {
3770
+ * // output.data is typed as OcrOutput
3771
+ * console.log("Extracted text:", output.data.extractedText);
3772
+ * console.log("Format:", output.data.format);
3773
+ * console.log("Task type:", output.data.taskType);
3774
+ * }
3775
+ * ```
3776
+ */
3777
+ declare const isOcrOutput: (output: TypedOutput) => output is NarrowedTypedOutput<{
3778
+ extractedText: string;
3779
+ format: "markdown" | "plain" | "structured";
3780
+ taskType: "convertToMarkdown" | "freeOcr" | "parseFigure" | "locateObject";
3781
+ confidence?: number | undefined;
3782
+ }, string>;
3783
+ /**
3784
+ * Type guard for image description output nodes.
3785
+ *
3786
+ * Validates that an output is from an image description node and contains valid description data.
3787
+ *
3788
+ * @param output - The output to check
3789
+ * @returns True if the output is an image description output with valid description data
3790
+ *
3791
+ * @example
3792
+ * ```typescript
3793
+ * import { isImageDescriptionOutput } from "@uploadista/core/flow";
3794
+ *
3795
+ * if (isImageDescriptionOutput(output)) {
3796
+ * // output.data is typed as ImageDescriptionOutput
3797
+ * console.log("Description:", output.data.description);
3798
+ * console.log("Confidence:", output.data.confidence);
3799
+ * }
3800
+ * ```
3801
+ */
3802
+ declare const isImageDescriptionOutput: (output: TypedOutput) => output is NarrowedTypedOutput<{
3803
+ description: string;
3804
+ confidence?: number | undefined;
3805
+ metadata?: Record<string, unknown> | undefined;
3806
+ }, string>;
3807
+ /**
3808
+ * Filter an array of outputs to only those matching a specific type.
3809
+ *
3810
+ * This helper function filters outputs using a type guard and returns a
3811
+ * properly typed array of results. It's useful for extracting specific
3812
+ * output types from multi-output flows.
3813
+ *
3814
+ * @template TOutput - The expected narrowed output type
3815
+ * @param outputs - Array of typed outputs to filter
3816
+ * @param typeGuard - Type guard function to use for filtering
3817
+ * @returns Array of outputs that match the type guard, properly typed
3818
+ *
3819
+ * @example
3820
+ * ```typescript
3821
+ * import { filterOutputsByType, isStorageOutput } from "@uploadista/core/flow";
3822
+ *
3823
+ * // Get all storage outputs from a multi-output flow
3824
+ * const storageOutputs = filterOutputsByType(
3825
+ * flowResult.outputs,
3826
+ * isStorageOutput
3827
+ * );
3828
+ *
3829
+ * for (const output of storageOutputs) {
3830
+ * // Each output.data is typed as UploadFile
3831
+ * console.log("Saved file:", output.data.url);
3832
+ * }
3833
+ * ```
3834
+ */
3835
+ declare function filterOutputsByType<TOutput extends TypedOutput>(outputs: TypedOutput[], typeGuard: (output: TypedOutput) => output is TOutput): TOutput[];
3836
+ /**
3837
+ * Get a single output of a specific type from an array of outputs.
3838
+ *
3839
+ * This helper function finds exactly one output matching the type guard.
3840
+ * It throws an error if no outputs match or if multiple outputs match,
3841
+ * ensuring the caller receives exactly the expected result.
3842
+ *
3843
+ * @template TOutput - The expected narrowed output type
3844
+ * @param outputs - Array of typed outputs to search
3845
+ * @param typeGuard - Type guard function to use for matching
3846
+ * @returns The single matching output, properly typed
3847
+ * @throws {UploadistaError} If no outputs match (OUTPUT_NOT_FOUND)
3848
+ * @throws {UploadistaError} If multiple outputs match (MULTIPLE_OUTPUTS_FOUND)
3849
+ *
3850
+ * @example
3851
+ * ```typescript
3852
+ * import { getSingleOutputByType, isStorageOutput } from "@uploadista/core/flow";
3853
+ *
3854
+ * try {
3855
+ * const storageOutput = getSingleOutputByType(
3856
+ * flowResult.outputs,
3857
+ * isStorageOutput
3858
+ * );
3859
+ * // storageOutput.data is typed as UploadFile
3860
+ * console.log("File saved at:", storageOutput.data.url);
3861
+ * } catch (error) {
3862
+ * if (error.code === "OUTPUT_NOT_FOUND") {
3863
+ * console.error("No storage output found");
3864
+ * } else if (error.code === "MULTIPLE_OUTPUTS_FOUND") {
3865
+ * console.error("Multiple storage outputs found, expected one");
3866
+ * }
3867
+ * }
3868
+ * ```
3869
+ */
3870
+ declare function getSingleOutputByType<TOutput extends TypedOutput>(outputs: TypedOutput[], typeGuard: (output: TypedOutput) => output is TOutput): Effect.Effect<TOutput, UploadistaError>;
3871
+ /**
3872
+ * Get the first output of a specific type, if any exists.
3873
+ *
3874
+ * Unlike getSingleOutputByType, this function returns undefined if no outputs
3875
+ * match, and returns the first match if multiple outputs exist. This is useful
3876
+ * when you want a more lenient matching strategy.
3877
+ *
3878
+ * @template TOutput - The expected narrowed output type
3879
+ * @param outputs - Array of typed outputs to search
3880
+ * @param typeGuard - Type guard function to use for matching
3881
+ * @returns The first matching output, or undefined if none match
3882
+ *
3883
+ * @example
3884
+ * ```typescript
3885
+ * import { getFirstOutputByType, isStorageOutput } from "@uploadista/core/flow";
3886
+ *
3887
+ * const storageOutput = getFirstOutputByType(
3888
+ * flowResult.outputs,
3889
+ * isStorageOutput
3890
+ * );
3891
+ *
3892
+ * if (storageOutput) {
3893
+ * console.log("First storage output:", storageOutput.data.url);
3894
+ * } else {
3895
+ * console.log("No storage outputs found");
3896
+ * }
3897
+ * ```
3898
+ */
3899
+ declare function getFirstOutputByType<TOutput extends TypedOutput>(outputs: TypedOutput[], typeGuard: (output: TypedOutput) => output is TOutput): TOutput | undefined;
3900
+ /**
3901
+ * Get an output by its node ID.
3902
+ *
3903
+ * This helper finds an output produced by a specific node instance,
3904
+ * regardless of its type. Useful when you know the specific node ID
3905
+ * you're looking for.
3906
+ *
3907
+ * @param outputs - Array of typed outputs to search
3908
+ * @param nodeId - The node ID to match
3909
+ * @returns The output from the specified node, or undefined if not found
3910
+ *
3911
+ * @example
3912
+ * ```typescript
3913
+ * import { getOutputByNodeId } from "@uploadista/core/flow";
3914
+ *
3915
+ * const cdnOutput = getOutputByNodeId(flowResult.outputs, "cdn-storage");
3916
+ * if (cdnOutput) {
3917
+ * console.log("CDN output:", cdnOutput.data);
3918
+ * }
3919
+ * ```
3920
+ */
3921
+ declare function getOutputByNodeId(outputs: TypedOutput[], nodeId: string): TypedOutput | undefined;
3922
+ /**
3923
+ * Check if any outputs match a specific type.
3924
+ *
3925
+ * Simple predicate function to check if at least one output of a given
3926
+ * type exists in the results.
3927
+ *
3928
+ * @template TOutput - The expected narrowed output type
3929
+ * @param outputs - Array of typed outputs to check
3930
+ * @param typeGuard - Type guard function to use for checking
3931
+ * @returns True if at least one output matches the type guard
3932
+ *
3933
+ * @example
3934
+ * ```typescript
3935
+ * import { hasOutputOfType, isStorageOutput } from "@uploadista/core/flow";
3936
+ *
3937
+ * if (hasOutputOfType(flowResult.outputs, isStorageOutput)) {
3938
+ * console.log("Flow produced at least one storage output");
3939
+ * } else {
3940
+ * console.log("No storage outputs in this flow");
3941
+ * }
3942
+ * ```
3943
+ */
3944
+ declare function hasOutputOfType<TOutput extends TypedOutput>(outputs: TypedOutput[], typeGuard: (output: TypedOutput) => output is TOutput): boolean;
3945
+ /**
3946
+ * Type guard for init operation (streaming file upload initialization).
3947
+ *
3948
+ * Checks if the input data is an init operation that starts a streaming
3949
+ * file upload session.
3950
+ *
3951
+ * @param data - Input data to check
3952
+ * @returns True if data is an init operation
3953
+ *
3954
+ * @example
3955
+ * ```typescript
3956
+ * if (isInitOperation(inputData)) {
3957
+ * console.log("Storage ID:", inputData.storageId);
3958
+ * console.log("Metadata:", inputData.metadata);
3959
+ * }
3960
+ * ```
3961
+ */
3962
+ declare function isInitOperation(data: InputData): data is Extract<InputData, {
3963
+ operation: "init";
3964
+ }>;
3965
+ /**
3966
+ * Type guard for finalize operation (complete streaming upload).
3967
+ *
3968
+ * Checks if the input data is a finalize operation that completes a
3969
+ * previously initialized streaming upload.
3970
+ *
3971
+ * @param data - Input data to check
3972
+ * @returns True if data is a finalize operation
3973
+ *
3974
+ * @example
3975
+ * ```typescript
3976
+ * if (isFinalizeOperation(inputData)) {
3977
+ * console.log("Upload ID:", inputData.uploadId);
3978
+ * }
3979
+ * ```
3980
+ */
3981
+ declare function isFinalizeOperation(data: InputData): data is Extract<InputData, {
3982
+ operation: "finalize";
3983
+ }>;
3984
+ /**
3985
+ * Type guard for URL operation (direct file fetch from URL).
3986
+ *
3987
+ * Checks if the input data is a URL operation that fetches a file
3988
+ * directly from an external URL.
3989
+ *
3990
+ * @param data - Input data to check
3991
+ * @returns True if data is a URL operation
3992
+ *
3993
+ * @example
3994
+ * ```typescript
3995
+ * if (isUrlOperation(inputData)) {
3996
+ * console.log("Fetching from:", inputData.url);
3997
+ * console.log("Optional storage:", inputData.storageId);
3998
+ * }
3999
+ * ```
4000
+ */
4001
+ declare function isUrlOperation(data: InputData): data is Extract<InputData, {
4002
+ operation: "url";
4003
+ }>;
4004
+ /**
4005
+ * Type guard for upload operations (init or url).
4006
+ *
4007
+ * Checks if the input data is either an init or URL operation (i.e., operations
4008
+ * that trigger new uploads, as opposed to finalize which completes an existing upload).
4009
+ *
4010
+ * @param data - Input data to check
4011
+ * @returns True if data is an init or URL operation
4012
+ *
4013
+ * @example
4014
+ * ```typescript
4015
+ * if (isUploadOperation(inputData)) {
4016
+ * // This is a new upload, not a finalization
4017
+ * if (isInitOperation(inputData)) {
4018
+ * console.log("Streaming upload");
4019
+ * } else {
4020
+ * console.log("URL fetch");
4021
+ * }
4022
+ * }
4023
+ * ```
4024
+ */
4025
+ declare function isUploadOperation(data: InputData): data is Extract<InputData, {
4026
+ operation: "init" | "url";
4027
+ }>;
4028
+ //#endregion
4029
+ //#region src/flow/typed-flow.d.ts
4030
+ /**
4031
+ * Defines a node that can be used in a typed flow.
4032
+ *
4033
+ * A node definition can be either:
4034
+ * - A plain FlowNode object
4035
+ * - An Effect that resolves to a FlowNode (for nodes requiring dependencies)
4036
+ *
4037
+ * @template TNodeError - The error types that the node can produce
4038
+ * @template TNodeRequirements - The services/dependencies the node requires
4039
+ */
4040
+ type NodeDefinition<TNodeError = never, TNodeRequirements = never> = FlowNode<any, any, UploadistaError> | Effect.Effect<FlowNode<any, any, UploadistaError>, TNodeError, TNodeRequirements>;
4041
+ /**
4042
+ * A record mapping node IDs to their definitions.
4043
+ *
4044
+ * This is the primary type used for defining the nodes in a typed flow,
4045
+ * allowing TypeScript to infer input/output schemas and requirements.
4046
+ *
4047
+ * @example
4048
+ * ```typescript
4049
+ * const nodes = {
4050
+ * input: fileInputNode,
4051
+ * resize: Effect.succeed(imageResizeNode),
4052
+ * output: s3OutputNode
4053
+ * } satisfies NodeDefinitionsRecord;
4054
+ * ```
4055
+ */
4056
+ type NodeDefinitionsRecord = Record<string, NodeDefinition<any, any>>;
4057
+ /**
4058
+ * Extracts the error type from a NodeDefinition.
4059
+ *
4060
+ * If the node is an Effect, extracts its error type.
4061
+ * If the node is a plain FlowNode, returns never (no errors).
4062
+ */
4063
+ type NodeDefinitionError<T> = T extends Effect.Effect<FlowNode<any, any, UploadistaError>, infer TError, any> ? TError : never;
4064
+ /**
4065
+ * Extracts the requirements (dependencies) from a NodeDefinition.
4066
+ *
4067
+ * Uses the shared ExtractEffectRequirements utility for consistency.
4068
+ */
4069
+ type NodeDefinitionRequirements<T> = ExtractEffectRequirements<T>;
4070
+ /**
4071
+ * Extracts all possible errors from all nodes in a flow as a union.
4072
+ *
4073
+ * This iterates through all nodes in the record and combines their
4074
+ * error types into a single union type.
4075
+ */
4076
+ type NodesErrorUnion<TNodes extends NodeDefinitionsRecord> = { [K in keyof TNodes]: NodeDefinitionError<TNodes[K]> }[keyof TNodes];
4077
+ /**
4078
+ * Extracts all service requirements from all nodes in a flow as a union.
4079
+ *
4080
+ * This iterates through all nodes in the record and combines their
4081
+ * requirement types into a single union type representing all services
4082
+ * needed by the flow.
4083
+ *
4084
+ * @template TNodes - The record of node definitions
4085
+ *
4086
+ * @example
4087
+ * ```typescript
4088
+ * const nodes = {
4089
+ * resize: imageResizeNode, // requires ImagePlugin
4090
+ * zip: zipNode, // requires ZipPlugin
4091
+ * };
4092
+ * type Requirements = NodesRequirementsUnion<typeof nodes>;
4093
+ * // Requirements = ImagePlugin | ZipPlugin
4094
+ * ```
4095
+ */
4096
+ type NodesRequirementsUnion<TNodes extends NodeDefinitionsRecord> = { [K in keyof TNodes]: NodeDefinitionRequirements<TNodes[K]> }[keyof TNodes];
4097
+ /**
4098
+ * Extracts all service requirements from a flow's nodes.
4099
+ *
4100
+ * This includes all services required by any node in the flow,
4101
+ * including UploadEngine (which is provided by the runtime).
4102
+ *
4103
+ * @template TNodes - The record of node definitions
4104
+ *
4105
+ * @example
4106
+ * ```typescript
4107
+ * const myFlow = createFlow({
4108
+ * nodes: {
4109
+ * input: fileInputNode,
4110
+ * process: imageProcessNode, // requires ImagePlugin
4111
+ * },
4112
+ * edges: [...]
4113
+ * });
4114
+ * type AllRequirements = FlowRequirements<typeof myFlow.nodes>;
4115
+ * // AllRequirements = ImagePlugin | UploadEngine
4116
+ * ```
4117
+ */
4118
+ type FlowRequirements<TNodes extends NodeDefinitionsRecord> = NodesRequirementsUnion<TNodes>;
4119
+ /**
4120
+ * Extracts plugin service requirements from a flow, excluding UploadEngine.
4121
+ *
4122
+ * This type is useful for determining which plugin layers need to be
4123
+ * provided when creating a server, as UploadEngine is automatically
4124
+ * provided by the runtime.
4125
+ *
4126
+ * @template TNodes - The record of node definitions
4127
+ *
4128
+ * @example
4129
+ * ```typescript
4130
+ * const myFlow = createFlow({
4131
+ * nodes: {
4132
+ * resize: imageResizeNode, // requires ImagePlugin
4133
+ * upload: s3OutputNode, // requires UploadEngine
4134
+ * },
4135
+ * edges: [...]
4136
+ * });
4137
+ * type PluginRequirements = FlowPluginRequirements<typeof myFlow.nodes>;
4138
+ * // PluginRequirements = ImagePlugin (UploadEngine excluded)
4139
+ * ```
4140
+ */
4141
+ type FlowPluginRequirements<TNodes extends NodeDefinitionsRecord> = Exclude<FlowRequirements<TNodes>, UploadEngine>;
4142
+ /**
4143
+ * Infers the concrete FlowNode type from a NodeDefinition.
4144
+ *
4145
+ * If the definition is already a FlowNode, returns it as-is.
4146
+ * If the definition is an Effect, extracts the FlowNode from the Effect's success type.
4147
+ *
4148
+ * Uses the shared ResolveEffect utility for consistency.
4149
+ */
4150
+ type InferNode<T> = T extends FlowNode<any, any, UploadistaError> ? T : ResolveEffect<T> extends FlowNode<any, any, UploadistaError> ? ResolveEffect<T> : never;
4151
+ type ExtractKeysByNodeType<TNodes extends NodeDefinitionsRecord, TType extends NodeType> = { [K in keyof TNodes]: InferNode<TNodes[K]>["type"] extends TType ? K : never }[keyof TNodes];
4152
+ type SchemaInfer<T> = T extends z.ZodTypeAny ? z.infer<T> : never;
4153
+ type FlowInputMap<TNodes extends NodeDefinitionsRecord> = { [K in Extract<ExtractKeysByNodeType<TNodes, NodeType.input>, string>]: SchemaInfer<InferNode<TNodes[K]>["inputSchema"]> };
4154
+ type FlowOutputMap<TNodes extends NodeDefinitionsRecord> = { [K in Extract<keyof TNodes, string>]: SchemaInfer<InferNode<TNodes[K]>["outputSchema"]> };
4155
+ type FlowInputUnion<TNodes extends NodeDefinitionsRecord> = { [K in Extract<ExtractKeysByNodeType<TNodes, NodeType.input>, string>]: SchemaInfer<InferNode<TNodes[K]>["inputSchema"]> }[Extract<ExtractKeysByNodeType<TNodes, NodeType.input>, string>];
4156
+ type FlowOutputUnion<TNodes extends NodeDefinitionsRecord> = { [K in Extract<keyof TNodes, string>]: SchemaInfer<InferNode<TNodes[K]>["outputSchema"]> }[Extract<keyof TNodes, string>];
4157
+ type NodeKey<TNodes extends NodeDefinitionsRecord> = Extract<keyof TNodes, string>;
4158
+ type TypedFlowEdge<TNodes extends NodeDefinitionsRecord> = {
4159
+ source: NodeKey<TNodes>;
4160
+ target: NodeKey<TNodes>;
4161
+ sourcePort?: string;
4162
+ targetPort?: string;
4163
+ };
4164
+ type TypedFlowConfig<TNodes extends NodeDefinitionsRecord> = {
4165
+ flowId: string;
4166
+ name: string;
4167
+ nodes: TNodes;
4168
+ edges: Array<TypedFlowEdge<TNodes>>;
4169
+ typeChecker?: TypeCompatibilityChecker;
4170
+ onEvent?: (event: FlowEvent) => Effect.Effect<{
4171
+ eventId: string | null;
4172
+ }, UploadistaError>;
4173
+ parallelExecution?: {
4174
+ enabled?: boolean;
4175
+ maxConcurrency?: number;
4176
+ };
4177
+ inputSchema?: z.ZodTypeAny;
4178
+ outputSchema?: z.ZodTypeAny;
4179
+ hooks?: {
4180
+ /**
4181
+ * Called when a sink node (terminal node with no outgoing edges) produces an output.
4182
+ * This hook runs after auto-persistence for UploadFile outputs.
4183
+ *
4184
+ * Use this hook to perform additional post-processing such as:
4185
+ * - Saving output metadata to a database
4186
+ * - Tracking outputs in external systems
4187
+ * - Adding custom metadata to outputs
4188
+ * - Triggering downstream workflows
4189
+ *
4190
+ * **Important**: The hook must not have any service requirements (Effect requirements must be `never`).
4191
+ * All necessary services should be captured in the closure when defining the hook.
4192
+ *
4193
+ * @example
4194
+ * ```typescript
4195
+ * // Using Promise (simpler for most users)
4196
+ * hooks: {
4197
+ * onNodeOutput: async ({ output }) => {
4198
+ * await db.save(output);
4199
+ * return output;
4200
+ * }
4201
+ * }
4202
+ * ```
4203
+ */
4204
+ onNodeOutput?: <TOutput>(context: {
4205
+ output: TOutput;
4206
+ nodeId: string;
4207
+ flowId: string;
4208
+ jobId: string;
4209
+ storageId: string;
4210
+ clientId: string | null;
4211
+ }) => Effect.Effect<TOutput, UploadistaError, never> | Promise<TOutput>;
4212
+ };
4213
+ /**
4214
+ * Circuit breaker configuration for resilience against external service failures.
4215
+ *
4216
+ * @example
4217
+ * ```typescript
4218
+ * circuitBreaker: {
4219
+ * defaults: { enabled: false },
4220
+ * nodeTypeOverrides: {
4221
+ * "Describe Image": {
4222
+ * enabled: true,
4223
+ * failureThreshold: 5,
4224
+ * resetTimeout: 60000,
4225
+ * fallback: { type: "skip", passThrough: true }
4226
+ * }
4227
+ * }
4228
+ * }
4229
+ * ```
4230
+ */
4231
+ circuitBreaker?: {
4232
+ /** Default circuit breaker config for all nodes */defaults?: FlowCircuitBreakerConfig; /** Override circuit breaker config per node type (node name) */
4233
+ nodeTypeOverrides?: Record<string, FlowCircuitBreakerConfig>;
4234
+ };
4235
+ };
4236
+ declare const typedFlowInputsSymbol: unique symbol;
4237
+ declare const typedFlowOutputsSymbol: unique symbol;
4238
+ declare const typedFlowPluginsSymbol: unique symbol;
4239
+ /**
4240
+ * A type-safe Flow that infers input/output types and requirements from its nodes.
4241
+ *
4242
+ * TypedFlow extends the base Flow type with additional type information that
4243
+ * allows TypeScript to verify inputs, outputs, and plugin requirements at compile time.
4244
+ *
4245
+ * The phantom type properties (using unique symbols) enable type-level metadata
4246
+ * without affecting runtime behavior, allowing other type utilities to extract
4247
+ * this information for validation purposes.
4248
+ *
4249
+ * @template TNodes - Record of node definitions used in the flow
4250
+ * @template TInputSchema - Zod schema for flow inputs (inferred from input nodes)
4251
+ * @template TOutputSchema - Zod schema for flow outputs (inferred from output nodes)
4252
+ *
4253
+ * @example
4254
+ * ```typescript
4255
+ * const myFlow = createFlow({
4256
+ * nodes: {
4257
+ * input: fileInputNode,
4258
+ * resize: imageResizeNode,
4259
+ * output: s3OutputNode
4260
+ * },
4261
+ * edges: [
4262
+ * { source: 'input', target: 'resize' },
4263
+ * { source: 'resize', target: 'output' }
4264
+ * ]
4265
+ * });
4266
+ *
4267
+ * // TypeScript infers:
4268
+ * // - Input types from fileInputNode.inputSchema
4269
+ * // - Output types from s3OutputNode.outputSchema
4270
+ * // - Requirements: ImagePlugin (from resize node)
4271
+ * ```
4272
+ */
4273
+ type TypedFlow<TNodes extends NodeDefinitionsRecord, TInputSchema extends z.ZodTypeAny, TOutputSchema extends z.ZodTypeAny> = Flow<TInputSchema, TOutputSchema, FlowRequirements<TNodes>> & {
4274
+ run: (args: {
4275
+ inputs?: Partial<FlowInputMap<TNodes>>;
4276
+ storageId: string;
4277
+ jobId: string;
4278
+ }) => Effect.Effect<FlowExecutionResult<FlowOutputMap<TNodes>>, UploadistaError, FlowRequirements<TNodes>>;
4279
+ resume: (args: {
4280
+ jobId: string;
4281
+ storageId: string;
4282
+ nodeResults: Record<string, unknown>;
4283
+ executionState: {
4284
+ executionOrder: string[];
4285
+ currentIndex: number;
4286
+ inputs: Partial<FlowInputMap<TNodes>>;
4287
+ };
4288
+ }) => Effect.Effect<FlowExecutionResult<FlowOutputMap<TNodes>>, UploadistaError, FlowRequirements<TNodes>>;
4289
+ readonly [typedFlowInputsSymbol]?: FlowInputMap<TNodes>;
4290
+ readonly [typedFlowOutputsSymbol]?: FlowOutputMap<TNodes>;
4291
+ readonly [typedFlowPluginsSymbol]?: FlowPluginRequirements<TNodes>;
4292
+ };
4293
+ declare function createFlow<TNodes extends NodeDefinitionsRecord>(config: TypedFlowConfig<TNodes>): Effect.Effect<TypedFlow<TNodes, z.ZodType<FlowInputUnion<TNodes>>, z.ZodType<FlowOutputUnion<TNodes>>>, NodesErrorUnion<TNodes> | UploadistaError, FlowRequirements<TNodes>>;
4294
+ //#endregion
4295
+ //#region src/flow/types/run-args.d.ts
4296
+ /**
4297
+ * Zod schema for validating flow run arguments.
4298
+ *
4299
+ * @property inputs - Record mapping input node IDs to their input data
4300
+ *
4301
+ * @example
4302
+ * ```typescript
4303
+ * const args = {
4304
+ * inputs: {
4305
+ * "input-node-1": { file: myFile, metadata: { ... } },
4306
+ * "input-node-2": { file: anotherFile }
4307
+ * }
4308
+ * };
4309
+ *
4310
+ * // Validate before running
4311
+ * const validated = runArgsSchema.parse(args);
4312
+ * ```
4313
+ */
4314
+ declare const runArgsSchema: z.ZodObject<{
4315
+ inputs: z.ZodRecord<z.ZodString, z.ZodAny>;
4316
+ }, z.core.$strip>;
4317
+ /**
4318
+ * Type representing validated flow run arguments.
4319
+ *
4320
+ * This type is inferred from the runArgsSchema and ensures type safety
4321
+ * when passing inputs to flow execution.
4322
+ */
4323
+ type RunArgs = z.infer<typeof runArgsSchema>;
4324
+ //#endregion
4325
+ //#region src/flow/utils/file-naming.d.ts
4326
+ /**
4327
+ * Extracts the base name (without extension) from a filename.
4328
+ *
4329
+ * @param fileName - The full filename
4330
+ * @returns The filename without extension
4331
+ *
4332
+ * @example
4333
+ * ```typescript
4334
+ * getBaseName("photo.jpg") // "photo"
4335
+ * getBaseName("document.tar.gz") // "document.tar"
4336
+ * getBaseName("noextension") // "noextension"
4337
+ * ```
4338
+ */
4339
+ declare function getBaseName(fileName: string): string;
4340
+ /**
4341
+ * Extracts the extension (without dot) from a filename.
4342
+ *
4343
+ * @param fileName - The full filename
4344
+ * @returns The extension without leading dot, or empty string if none
4345
+ *
4346
+ * @example
4347
+ * ```typescript
4348
+ * getExtension("photo.jpg") // "jpg"
4349
+ * getExtension("document.tar.gz") // "gz"
4350
+ * getExtension("noextension") // ""
4351
+ * ```
4352
+ */
4353
+ declare function getExtension(fileName: string): string;
4354
+ /**
4355
+ * Builds a naming context from file and flow execution information.
4356
+ *
4357
+ * @param file - The UploadFile being processed
4358
+ * @param flowContext - Flow execution context (flowId, jobId, nodeId, nodeType)
4359
+ * @param extraVars - Additional variables to include (width, height, format, etc.)
4360
+ * @returns Complete naming context for template interpolation
4361
+ *
4362
+ * @example
4363
+ * ```typescript
4364
+ * const context = buildNamingContext(
4365
+ * uploadFile,
4366
+ * { flowId: "flow-1", jobId: "job-1", nodeId: "resize-1", nodeType: "resize" },
4367
+ * { width: 800, height: 600 }
4368
+ * );
4369
+ * // context.baseName = "photo"
4370
+ * // context.extension = "jpg"
4371
+ * // context.width = 800
4372
+ * // context.height = 600
4373
+ * ```
4374
+ */
4375
+ declare function buildNamingContext(file: UploadFile, flowContext: {
4376
+ flowId: string;
4377
+ jobId: string;
4378
+ nodeId: string;
4379
+ nodeType: string;
4380
+ }, extraVars?: Record<string, string | number | undefined>): NamingContext;
4381
+ /**
4382
+ * Interpolates a mustache-style template with the given context.
4383
+ *
4384
+ * Uses micromustache for fast, secure template rendering.
4385
+ * Unknown variables are preserved as-is (e.g., {{unknown}} stays {{unknown}}).
4386
+ *
4387
+ * @param pattern - Mustache-style template string
4388
+ * @param context - Variables to interpolate
4389
+ * @returns Interpolated string
4390
+ *
4391
+ * @example
4392
+ * ```typescript
4393
+ * interpolateFileName(
4394
+ * "{{baseName}}-{{width}}x{{height}}.{{extension}}",
4395
+ * { baseName: "photo", width: 800, height: 600, extension: "jpg" }
4396
+ * );
4397
+ * // Returns: "photo-800x600.jpg"
4398
+ * ```
4399
+ */
4400
+ declare function interpolateFileName(pattern: string, context: NamingContext): string;
4401
+ /**
4402
+ * Applies file naming configuration to generate a new filename.
4403
+ *
4404
+ * Handles three modes:
4405
+ * - No config: Returns original filename (backward compatible)
4406
+ * - Auto mode: Appends auto-generated suffix based on node type
4407
+ * - Custom mode: Uses template pattern or rename function
4408
+ *
4409
+ * On any error, falls back to the original filename to prevent flow failures.
4410
+ *
4411
+ * @param file - The UploadFile being processed
4412
+ * @param context - Naming context with all available variables
4413
+ * @param config - Optional naming configuration
4414
+ * @returns The new filename (or original on error/no config)
4415
+ *
4416
+ * @example
4417
+ * ```typescript
4418
+ * // Auto mode
4419
+ * applyFileNaming(file, context, {
4420
+ * mode: 'auto',
4421
+ * autoSuffix: (ctx) => `${ctx.width}x${ctx.height}`
4422
+ * });
4423
+ * // Returns: "photo-800x600.jpg"
4424
+ *
4425
+ * // Custom mode with template
4426
+ * applyFileNaming(file, context, {
4427
+ * mode: 'custom',
4428
+ * pattern: '{{baseName}}-processed.{{extension}}'
4429
+ * });
4430
+ * // Returns: "photo-processed.jpg"
4431
+ *
4432
+ * // Custom mode with function
4433
+ * applyFileNaming(file, context, {
4434
+ * mode: 'custom',
4435
+ * rename: (file, ctx) => `${ctx.flowId}-${ctx.fileName}`
4436
+ * });
4437
+ * // Returns: "flow-1-photo.jpg"
4438
+ * ```
4439
+ */
4440
+ declare function applyFileNaming(file: UploadFile, context: NamingContext, config?: FileNamingConfig): string;
4441
+ /**
4442
+ * Validates a template pattern for common issues.
4443
+ *
4444
+ * Checks for:
4445
+ * - Balanced braces
4446
+ * - Non-empty pattern
4447
+ * - Valid variable names
4448
+ *
4449
+ * @param pattern - Template pattern to validate
4450
+ * @returns Object with isValid flag and optional error message
4451
+ *
4452
+ * @example
4453
+ * ```typescript
4454
+ * validatePattern("{{baseName}}.{{extension}}");
4455
+ * // { isValid: true }
4456
+ *
4457
+ * validatePattern("{{baseName");
4458
+ * // { isValid: false, error: "Unbalanced braces: missing closing }}" }
4459
+ * ```
4460
+ */
4461
+ declare function validatePattern(pattern: string): {
4462
+ isValid: boolean;
4463
+ error?: string;
4464
+ };
4465
+ /**
4466
+ * List of available template variables for documentation and UI.
4467
+ */
4468
+ declare const AVAILABLE_TEMPLATE_VARIABLES: readonly [{
4469
+ readonly name: "baseName";
4470
+ readonly description: "Filename without extension";
4471
+ readonly example: "photo";
4472
+ }, {
4473
+ readonly name: "extension";
4474
+ readonly description: "File extension without dot";
4475
+ readonly example: "jpg";
4476
+ }, {
4477
+ readonly name: "fileName";
4478
+ readonly description: "Full original filename";
4479
+ readonly example: "photo.jpg";
4480
+ }, {
4481
+ readonly name: "nodeType";
4482
+ readonly description: "Type of processing node";
4483
+ readonly example: "resize";
4484
+ }, {
4485
+ readonly name: "nodeId";
4486
+ readonly description: "Specific node instance ID";
4487
+ readonly example: "resize-1";
4488
+ }, {
4489
+ readonly name: "flowId";
4490
+ readonly description: "Flow identifier";
4491
+ readonly example: "flow-abc";
4492
+ }, {
4493
+ readonly name: "jobId";
4494
+ readonly description: "Execution job ID";
4495
+ readonly example: "job-123";
4496
+ }, {
4497
+ readonly name: "timestamp";
4498
+ readonly description: "ISO 8601 processing time";
4499
+ readonly example: "2024-01-15T10:30:00Z";
4500
+ }, {
4501
+ readonly name: "width";
4502
+ readonly description: "Output width (image/video)";
4503
+ readonly example: "800";
4504
+ }, {
4505
+ readonly name: "height";
4506
+ readonly description: "Output height (image/video)";
4507
+ readonly example: "600";
4508
+ }, {
4509
+ readonly name: "format";
4510
+ readonly description: "Output format";
4511
+ readonly example: "webp";
4512
+ }, {
4513
+ readonly name: "quality";
4514
+ readonly description: "Quality setting";
4515
+ readonly example: "80";
4516
+ }, {
4517
+ readonly name: "pageNumber";
4518
+ readonly description: "Page number (documents)";
4519
+ readonly example: "1";
4520
+ }];
4521
+ //#endregion
4522
+ //#region src/flow/utils/resolve-upload-metadata.d.ts
4523
+ type FileMetadata = UploadFile["metadata"];
4524
+ type ResolvedUploadMetadata = {
4525
+ type: string;
4526
+ fileName: string;
4527
+ metadata: FileMetadata;
4528
+ metadataJson: string | undefined;
4529
+ };
4530
+ declare function resolveUploadMetadata(metadata: FileMetadata): ResolvedUploadMetadata;
4531
+ //#endregion
4532
+ export { VideoPlugin as $, MemoryFlowQueueStore as $n, ImageAiPluginLayer as $t, isImageDescriptionOutput as A, ExtractLayerServices as An, DistributedCircuitBreaker as Ar, Transformation as At, describeImageParamsSchema as B, FlowQueueServiceShape as Bn, CircuitBreakerState as Br, rotateTransformSchema as Bt, createTypeGuard as C, StreamingTransformResult as Cn, FlowData as Cr, OverlayPosition as Ct, getSingleOutputByType as D, ExtractEffectError as Dn, FlowEdge as Dr, SharpenTransform as Dt, getOutputByNodeId as E, createTransformNode as En, getFlowData as Er, SepiaTransform as Et, isUploadOperation as F, createInputNode as Fn, memoryCircuitBreakerStoreLayer as Fr, contrastTransformSchema as Ft, ZipPlugin as G, FlowLifecycleHook as Gn, transformationSchema as Gt, PluginLayer as H, FlowEngineLayer as Hn, sharpenTransformSchema as Ht, isUrlOperation as I, inputDataSchema as In, CircuitBreakerConfig as Ir, flipTransformSchema as It, ScanMetadata as J, FlowWaitUntil as Jn, resizeParamsSchema as Jt, ZipPluginLayer as K, FlowProvider as Kn, watermarkTransformSchema as Kt, RemoveBackgroundParams as L, inputNodeParamsSchema as Ln, CircuitBreakerEvent as Lr, grayscaleTransformSchema as Lt, isOcrOutput as M, FlowCondition as Mn, kvCircuitBreakerStoreLayer as Mr, WatermarkTransform as Mt, isStorageOutput as N, InputData as Nn, makeKvCircuitBreakerStore as Nr, blurTransformSchema as Nt, hasOutputOfType as O, ExtractEffectRequirements as On, createFlowEdge as Or, TextTransform as Ot, isUploadFile as P, InputNodeParams as Pn, makeMemoryCircuitBreakerStore as Pr, brightnessTransformSchema as Pt, VirusScanPluginShape as Q, FlowQueueStore as Qn, ImageAiPlugin as Qt, removeBackgroundParamsSchema as R, FlowQueueDispatchMarker as Rn, CircuitBreakerEventHandler as Rr, logoTransformSchema as Rt, NarrowedTypedOutput as S, StreamingTransformFn as Sn, Flow as Sr, LogoTransform as St, getFirstOutputByType as T, TransformNodeConfig as Tn, createFlowWithSchema as Tr, RotateTransform as Tt, ZipInput as U, FlowEngineOptions as Un, textTransformSchema as Ut, Plugin as V, FlowEngine as Vn, DEFAULT_CIRCUIT_BREAKER_CONFIG as Vr, sepiaTransformSchema as Vt, ZipParams as W, FlowEngineShape as Wn, transformImageParamsSchema as Wt, VirusScanPlugin as X, createFlowEngine as Xn, optimizeParamsSchema as Xt, ScanResult as Y, WaitUntilCallback as Yn, OptimizeParams as Yt, VirusScanPluginLayer as Z, flowEngine as Zn, ImageAiContext as Zt, NodeDefinitionsRecord as _, CredentialProviderLayer as _n, InputTypeDefinition as _r, BlurTransform as _t, buildNamingContext as a, MergePdfParams as an, ImageDescriptionOutput as ar, trimVideoParamsSchema as at, TypedFlowEdge as b, ParallelScheduler as bn, inputTypeRegistry as br, FlipTransform as bt, interpolateFileName as c, DocumentAiContext as cn, STORAGE_OUTPUT_TYPE_ID as cr, ResizeVideoParams as ct, runArgsSchema as d, DocumentAiPluginShape as dn, ocrOutputSchema as dr, extractFrameVideoParamsSchema as dt, ImageAiPluginShape as en, DeadLetterQueueService as er, VideoPluginLayer as et, FlowInputMap as f, OcrParams as fn, OutputTypeDefinition as fr, DescribeVideoMetadata as ft, NodeDefinition as g, CredentialProvider as gn, validateFlowOutput as gr, ImagePluginShape as gt, FlowRequirements as h, OcrTaskType as hn, outputTypeRegistry as hr, ImagePluginLayer as ht, applyFileNaming as i, DocumentPluginShape as in, IMAGE_DESCRIPTION_OUTPUT_TYPE_ID as ir, TrimVideoParams as it, isInitOperation as j, ResolveEffect as jn, DistributedCircuitBreakerRegistry as jr, TransformationType as jt, isFinalizeOperation as k, ExtractLayerService as kn, AllowRequestResult as kr, TransformImageParams as kt, validatePattern as l, DocumentAiPlugin as ln, STREAMING_INPUT_TYPE_ID as lr, resizeVideoParamsSchema as lt, FlowPluginRequirements as m, OcrResult as mn, OutputValidationResult as mr, ImagePlugin as mt, resolveUploadMetadata as n, DocumentPlugin as nn, createDeadLetterQueueService as nr, VideoStreamInput as nt, getBaseName as o, SplitPdfParams as on, OCR_OUTPUT_TYPE_ID as or, TranscodeVideoParams as ot, FlowOutputMap as p, OcrResolution as pn, OutputTypeRegistry as pr, describeVideoMetadataSchema as pt, ZipPluginShape as q, FlowProviderShape as qn, ResizeParams as qt, AVAILABLE_TEMPLATE_VARIABLES as r, DocumentPluginLayer as rn, deadLetterQueueService as rr, VideoStreamOptions as rt, getExtension as s, SplitPdfResult as sn, OcrOutput as sr, transcodeVideoParamsSchema as st, ResolvedUploadMetadata as t, DocumentMetadata as tn, DeadLetterQueueServiceShape as tr, VideoPluginShape as tt, RunArgs as u, DocumentAiPluginLayer as un, imageDescriptionOutputSchema as ur, ExtractFrameVideoParams as ut, TypedFlow as v, CredentialProviderShape as vn, InputTypeRegistry as vr, BrightnessTransform as vt, filterOutputsByType as w, TransformMode as wn, FlowExecutionResult as wr, ResizeTransform as wt, createFlow as x, ParallelSchedulerConfig as xn, validateFlowInput as xr, GrayscaleTransform as xt, TypedFlowConfig as y, ExecutionLevel as yn, InputValidationResult as yr, ContrastTransform as yt, DescribeImageParams as z, FlowQueueService as zn, CircuitBreakerFallback as zr, resizeTransformSchema as zt };
4533
+ //# sourceMappingURL=resolve-upload-metadata-DbkBzxm8.d.mts.map