@uploadista/core 0.0.18-beta.8 → 0.0.18

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 (60) hide show
  1. package/dist/{checksum-YLW4hVY7.cjs → checksum-p3NmuAky.cjs} +1 -1
  2. package/dist/errors/index.cjs +1 -1
  3. package/dist/flow/index.cjs +1 -1
  4. package/dist/flow/index.d.cts +2 -2
  5. package/dist/flow/index.d.mts +2 -2
  6. package/dist/flow/index.mjs +1 -1
  7. package/dist/flow-DKCp_0Y1.mjs +2 -0
  8. package/dist/flow-DKCp_0Y1.mjs.map +1 -0
  9. package/dist/flow-NHkTGTxu.cjs +1 -0
  10. package/dist/index-9gyMMEIB.d.cts.map +1 -1
  11. package/dist/index-B9V5SSxl.d.mts.map +1 -1
  12. package/dist/{index-CDyRMCkp.d.cts → index-BOic6-Cg.d.cts} +71 -6
  13. package/dist/index-BOic6-Cg.d.cts.map +1 -0
  14. package/dist/{index-gZBo_Phe.d.mts → index-TokXRAZ5.d.mts} +71 -6
  15. package/dist/index-TokXRAZ5.d.mts.map +1 -0
  16. package/dist/index.cjs +1 -1
  17. package/dist/index.d.cts +2 -2
  18. package/dist/index.d.mts +2 -2
  19. package/dist/index.mjs +1 -1
  20. package/dist/{stream-limiter-DyWOdil4.cjs → stream-limiter-Cem7Zvaw.cjs} +1 -1
  21. package/dist/streams/index.cjs +1 -1
  22. package/dist/testing/index.cjs +1 -1
  23. package/dist/testing/index.d.cts +1 -1
  24. package/dist/testing/index.d.mts +1 -1
  25. package/dist/testing/index.mjs +1 -1
  26. package/dist/types/index.cjs +1 -1
  27. package/dist/types/index.d.cts +2 -2
  28. package/dist/types/index.d.mts +2 -2
  29. package/dist/types/index.mjs +1 -1
  30. package/dist/types-CHbyV8e6.mjs +2 -0
  31. package/dist/types-CHbyV8e6.mjs.map +1 -0
  32. package/dist/types-D3_rWxD0.cjs +1 -0
  33. package/dist/upload/index.cjs +1 -1
  34. package/dist/upload/index.d.cts +1 -1
  35. package/dist/upload/index.d.mts +1 -1
  36. package/dist/upload/index.mjs +1 -1
  37. package/dist/upload-5l3utoc7.cjs +1 -0
  38. package/dist/upload-B2RDFkTe.mjs +2 -0
  39. package/dist/upload-B2RDFkTe.mjs.map +1 -0
  40. package/dist/{uploadista-error-BxBLmQtX.cjs → uploadista-error-BfpQ4mOO.cjs} +1 -1
  41. package/dist/utils/index.cjs +1 -1
  42. package/dist/{utils-Dhq3vPqp.cjs → utils-QJOPnlmt.cjs} +1 -1
  43. package/package.json +4 -3
  44. package/src/flow/flow-server.ts +114 -37
  45. package/src/flow/flow.ts +19 -1
  46. package/src/flow/types/flow-job.ts +20 -0
  47. package/src/types/upload-file.ts +29 -0
  48. package/src/upload/create-upload.ts +97 -49
  49. package/src/upload/upload-chunk.ts +115 -47
  50. package/dist/flow-BLGpxdEm.mjs +0 -2
  51. package/dist/flow-BLGpxdEm.mjs.map +0 -1
  52. package/dist/flow-DaBzRGmY.cjs +0 -1
  53. package/dist/index-CDyRMCkp.d.cts.map +0 -1
  54. package/dist/index-gZBo_Phe.d.mts.map +0 -1
  55. package/dist/types-CH0BgiJN.mjs +0 -2
  56. package/dist/types-CH0BgiJN.mjs.map +0 -1
  57. package/dist/types-DUYVoR13.cjs +0 -1
  58. package/dist/upload-CFT-dWPB.cjs +0 -1
  59. package/dist/upload-ggK-0ZBM.mjs +0 -2
  60. package/dist/upload-ggK-0ZBM.mjs.map +0 -1
@@ -8,6 +8,15 @@ import { z } from "zod";
8
8
  *
9
9
  * @see {@link UploadFile} for the TypeScript type
10
10
  */
11
+ /**
12
+ * Zod schema for trace context used in distributed tracing.
13
+ */
14
+ export const traceContextSchema = z.object({
15
+ traceId: z.string(),
16
+ spanId: z.string(),
17
+ traceFlags: z.number(),
18
+ });
19
+
11
20
  export const uploadFileSchema = z.object({
12
21
  id: z.string(),
13
22
  size: z.number().optional(),
@@ -43,6 +52,7 @@ export const uploadFileSchema = z.object({
43
52
  jobId: z.string(),
44
53
  })
45
54
  .optional(),
55
+ traceContext: traceContextSchema.optional(),
46
56
  });
47
57
 
48
58
  /**
@@ -126,6 +136,19 @@ export const uploadFileSchema = z.object({
126
136
  * };
127
137
  * ```
128
138
  */
139
+ /**
140
+ * Trace context for distributed tracing.
141
+ * Allows upload operations to be linked under a single trace.
142
+ */
143
+ export type UploadFileTraceContext = {
144
+ /** 128-bit trace identifier (32 hex characters) */
145
+ traceId: string;
146
+ /** 64-bit span identifier (16 hex characters) */
147
+ spanId: string;
148
+ /** Trace flags (1 = sampled) */
149
+ traceFlags: number;
150
+ };
151
+
129
152
  export type UploadFile = {
130
153
  id: string;
131
154
  offset: number;
@@ -155,4 +178,10 @@ export type UploadFile = {
155
178
  sizeIsDeferred?: boolean | undefined;
156
179
  checksum?: string | undefined;
157
180
  checksumAlgorithm?: string | undefined;
181
+ /**
182
+ * OpenTelemetry trace context for distributed tracing.
183
+ * When set, subsequent upload operations (chunks, validation) will be
184
+ * linked as children of this trace context.
185
+ */
186
+ traceContext?: UploadFileTraceContext | undefined;
158
187
  };
@@ -1,4 +1,4 @@
1
- import { Effect, Metric, MetricBoundaries } from "effect";
1
+ import { Effect, Metric, MetricBoundaries, Option } from "effect";
2
2
  import {
3
3
  type EventEmitter,
4
4
  type InputFile,
@@ -7,9 +7,34 @@ import {
7
7
  UploadEventType,
8
8
  type UploadFile,
9
9
  type UploadFileDataStoresShape,
10
+ type UploadFileTraceContext,
10
11
  } from "../types";
11
12
  import type { GenerateIdShape } from "../utils/generate-id";
12
13
 
14
+ /**
15
+ * Captures the current Effect trace context for distributed tracing.
16
+ *
17
+ * Uses Effect's `currentSpan` to get the active span, which is more reliable
18
+ * than OpenTelemetry's `trace.getActiveSpan()` when using @effect/opentelemetry
19
+ * because Effect manages its own span context that may not be synchronized
20
+ * with OpenTelemetry's global context.
21
+ *
22
+ * @returns Effect that yields TraceContext if there's an active span, undefined otherwise
23
+ */
24
+ const captureTraceContextEffect: Effect.Effect<
25
+ UploadFileTraceContext | undefined
26
+ > = Effect.gen(function* () {
27
+ const spanOption = yield* Effect.currentSpan.pipe(Effect.option);
28
+ return Option.match(spanOption, {
29
+ onNone: () => undefined,
30
+ onSome: (span) => ({
31
+ traceId: span.traceId,
32
+ spanId: span.spanId,
33
+ traceFlags: span.sampled ? 1 : 0,
34
+ }),
35
+ });
36
+ });
37
+
13
38
  /**
14
39
  * Creates a new upload and initializes it in the storage system.
15
40
  *
@@ -84,66 +109,89 @@ export const createUpload = (
84
109
  },
85
110
  ) =>
86
111
  Effect.gen(function* () {
87
- // Get datastore using Effect
88
- const dataStore = yield* dataStoreService.getDataStore(
89
- inputFile.storageId,
90
- clientId,
91
- );
112
+ // Capture the parent "upload" span's trace context FIRST
113
+ // This allows subsequent chunk uploads to be siblings of upload-create
114
+ // under the same parent "upload" span
115
+ const traceContext = yield* captureTraceContextEffect;
116
+ const creationDate = new Date().toISOString();
92
117
 
93
- const id = yield* generateId.generateId();
94
- const { size, type, fileName, lastModified, metadata, flow } = inputFile;
118
+ // Now run the actual upload creation inside a child span
119
+ const fileCreated = yield* Effect.gen(function* () {
120
+ // Get datastore using Effect
121
+ const dataStore = yield* dataStoreService.getDataStore(
122
+ inputFile.storageId,
123
+ clientId,
124
+ );
95
125
 
96
- let parsedMetadata: Record<string, string> = {};
97
- if (metadata) {
98
- try {
99
- parsedMetadata = JSON.parse(metadata) as Record<string, string>;
100
- } catch {
101
- parsedMetadata = {};
126
+ const id = yield* generateId.generateId();
127
+ const { size, type, fileName, lastModified, metadata, flow } = inputFile;
128
+
129
+ let parsedMetadata: Record<string, string> = {};
130
+ if (metadata) {
131
+ try {
132
+ parsedMetadata = JSON.parse(metadata) as Record<string, string>;
133
+ } catch {
134
+ parsedMetadata = {};
135
+ }
102
136
  }
103
- }
104
137
 
105
- const metadataObject: Record<string, string> = {
106
- ...parsedMetadata,
107
- type,
108
- fileName: fileName ?? "",
109
- };
110
- if (lastModified) {
111
- metadataObject.lastModified = lastModified.toString();
112
- }
138
+ const metadataObject: Record<string, string> = {
139
+ ...parsedMetadata,
140
+ type,
141
+ fileName: fileName ?? "",
142
+ };
143
+ if (lastModified) {
144
+ metadataObject.lastModified = lastModified.toString();
145
+ }
113
146
 
114
- const file: UploadFile = {
115
- id,
116
- size,
117
- metadata: metadataObject,
118
- offset: 0,
119
- creationDate: new Date().toISOString(),
120
- storage: {
121
- id: inputFile.storageId,
147
+ const file: UploadFile = {
148
+ id,
149
+ size,
150
+ metadata: metadataObject,
151
+ offset: 0,
152
+ creationDate,
153
+ storage: {
154
+ id: inputFile.storageId,
155
+ type,
156
+ path: "",
157
+ bucket: dataStore.bucket,
158
+ },
159
+ flow,
160
+ traceContext,
161
+ };
122
162
 
123
- type,
124
- path: "",
125
- bucket: dataStore.bucket,
126
- },
127
- flow,
128
- };
163
+ // Create file using Effect
164
+ const created = yield* dataStore.create(file);
129
165
 
130
- // Create file using Effect
131
- const fileCreated = yield* dataStore.create(file);
166
+ // Store in KV store
167
+ yield* kvStore.set(id, created);
132
168
 
133
- // Store in KV store
134
- yield* kvStore.set(id, fileCreated);
169
+ // Emit event
170
+ yield* eventEmitter.emit(id, {
171
+ type: UploadEventType.UPLOAD_STARTED,
172
+ data: created,
173
+ flow: created.flow,
174
+ });
135
175
 
136
- // Emit event
137
- yield* eventEmitter.emit(id, {
138
- type: UploadEventType.UPLOAD_STARTED,
139
- data: fileCreated,
140
- flow: fileCreated.flow,
141
- });
176
+ return created;
177
+ }).pipe(
178
+ // upload-create is a CHILD span of the parent "upload" span
179
+ Effect.withSpan("upload-create", {
180
+ attributes: {
181
+ "upload.file_name": inputFile.fileName ?? "unknown",
182
+ "upload.file_size": inputFile.size?.toString() ?? "0",
183
+ "upload.storage_id": inputFile.storageId,
184
+ "upload.mime_type": inputFile.type,
185
+ "upload.has_flow": inputFile.flow ? "true" : "false",
186
+ },
187
+ }),
188
+ );
142
189
 
143
190
  return fileCreated;
144
191
  }).pipe(
145
- // Add tracing span for the entire create operation
146
- Effect.withSpan("upload-create", {
192
+ // Parent "upload" span wraps the entire upload lifecycle
193
+ // upload-create and upload-chunk will be children of this span
194
+ Effect.withSpan("upload", {
147
195
  attributes: {
148
196
  "upload.file_name": inputFile.fileName ?? "unknown",
149
197
  "upload.file_size": inputFile.size?.toString() ?? "0",
@@ -1,4 +1,4 @@
1
- import { Effect, Metric, MetricBoundaries } from "effect";
1
+ import { Effect, Metric, MetricBoundaries, Tracer } from "effect";
2
2
  import { UploadistaError } from "../errors/uploadista-error";
3
3
  import {
4
4
  type DataStore,
@@ -8,11 +8,58 @@ import {
8
8
  UploadEventType,
9
9
  type UploadFile,
10
10
  type UploadFileDataStoresShape,
11
+ type UploadFileTraceContext,
11
12
  } from "../types";
12
13
  import { computeChecksum } from "../utils/checksum";
13
14
  import { compareMimeTypes, detectMimeType } from "./mime";
14
15
  import { writeToStore } from "./write-to-store";
15
16
 
17
+ /**
18
+ * Creates an ExternalSpan from stored trace context.
19
+ * Used for linking chunk uploads to the original upload trace.
20
+ */
21
+ function createExternalSpan(traceContext: UploadFileTraceContext) {
22
+ return Tracer.externalSpan({
23
+ traceId: traceContext.traceId,
24
+ spanId: traceContext.spanId,
25
+ sampled: traceContext.traceFlags === 1,
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Creates an "upload-complete" span Effect that captures the full upload duration.
31
+ * This span is a sibling of upload-create and upload-chunk under the parent "upload" span.
32
+ *
33
+ * Note: The span's visual duration in tracing UIs will be short (instant), but the
34
+ * actual upload duration is captured in the "upload.total_duration_ms" attribute.
35
+ *
36
+ * @param file - The completed upload file
37
+ * @param parentSpan - The parent span to link to
38
+ * @returns Effect that creates and completes the span
39
+ */
40
+ const createUploadCompleteSpanEffect = (
41
+ file: UploadFile,
42
+ parentSpan: Tracer.ExternalSpan,
43
+ ): Effect.Effect<void> => {
44
+ const creationTime = new Date(file.creationDate as string).getTime();
45
+ const totalDurationMs = Date.now() - creationTime;
46
+
47
+ return Effect.void.pipe(
48
+ Effect.withSpan("upload-complete", {
49
+ attributes: {
50
+ "upload.id": file.id,
51
+ "upload.size": file.size ?? 0,
52
+ "upload.total_duration_ms": totalDurationMs,
53
+ "upload.storage_id": file.storage.id,
54
+ "upload.file_name": file.metadata?.fileName ?? "unknown",
55
+ "upload.creation_date": file.creationDate as string,
56
+ "upload.completion_date": new Date().toISOString(),
57
+ },
58
+ parent: parentSpan,
59
+ }),
60
+ );
61
+ };
62
+
16
63
  /**
17
64
  * Uploads a chunk of data for an existing upload.
18
65
  *
@@ -77,64 +124,85 @@ export const uploadChunk = (
77
124
  },
78
125
  ) =>
79
126
  Effect.gen(function* () {
80
- // Get file from KV store
127
+ // Get file from KV store first to check for trace context
81
128
  const file = yield* kvStore.get(uploadId);
82
129
 
83
- // Get datastore
84
- const dataStore = yield* dataStoreService.getDataStore(
85
- file.storage.id,
86
- clientId,
87
- );
130
+ // Create external span from stored trace context if available
131
+ // This links chunk uploads to the original upload trace
132
+ const parentSpan = file.traceContext
133
+ ? createExternalSpan(file.traceContext)
134
+ : undefined;
88
135
 
89
- // Note: AbortController could be used for cancellation if needed
136
+ // Core chunk processing logic
137
+ const processChunk = Effect.gen(function* () {
138
+ // Get datastore
139
+ const dataStore = yield* dataStoreService.getDataStore(
140
+ file.storage.id,
141
+ clientId,
142
+ );
90
143
 
91
- // Write to store using writeToStore Effect
92
- const controller = new AbortController();
144
+ // Note: AbortController could be used for cancellation if needed
93
145
 
94
- const chunkSize = yield* writeToStore({
95
- dataStore,
96
- data: chunk,
97
- upload: file,
98
- maxFileSize: 100_000_000,
99
- controller,
100
- uploadProgressInterval: 200,
101
- eventEmitter,
102
- });
146
+ // Write to store using writeToStore Effect
147
+ const controller = new AbortController();
103
148
 
104
- file.offset = chunkSize;
149
+ const chunkSize = yield* writeToStore({
150
+ dataStore,
151
+ data: chunk,
152
+ upload: file,
153
+ maxFileSize: 100_000_000,
154
+ controller,
155
+ uploadProgressInterval: 200,
156
+ eventEmitter,
157
+ });
105
158
 
106
- // Update KV store
107
- yield* kvStore.set(uploadId, file);
159
+ file.offset = chunkSize;
108
160
 
109
- // Emit progress event
110
- yield* eventEmitter.emit(file.id, {
111
- type: UploadEventType.UPLOAD_PROGRESS,
112
- data: {
113
- id: file.id,
114
- progress: file.offset,
115
- total: file.size ?? 0,
116
- },
117
- flow: file.flow,
118
- });
161
+ // Update KV store
162
+ yield* kvStore.set(uploadId, file);
119
163
 
120
- // Check if upload is complete and run validation
121
- if (file.size && file.offset === file.size) {
122
- yield* validateUpload({
123
- file,
124
- dataStore,
125
- eventEmitter,
164
+ // Emit progress event
165
+ yield* eventEmitter.emit(file.id, {
166
+ type: UploadEventType.UPLOAD_PROGRESS,
167
+ data: {
168
+ id: file.id,
169
+ progress: file.offset,
170
+ total: file.size ?? 0,
171
+ },
172
+ flow: file.flow,
126
173
  });
127
- }
128
174
 
129
- return file;
175
+ // Check if upload is complete and run validation
176
+ if (file.size && file.offset === file.size) {
177
+ yield* validateUpload({
178
+ file,
179
+ dataStore,
180
+ eventEmitter,
181
+ });
182
+
183
+ // Create "upload-complete" span that captures the full upload duration
184
+ // This span shows the total time from upload creation to completion
185
+ if (file.traceContext) {
186
+ const completeParentSpan = createExternalSpan(file.traceContext);
187
+ yield* createUploadCompleteSpanEffect(file, completeParentSpan);
188
+ }
189
+ }
190
+
191
+ return file;
192
+ }).pipe(
193
+ // Add tracing span for chunk upload with parent from stored trace context
194
+ Effect.withSpan("upload-chunk", {
195
+ attributes: {
196
+ "upload.id": uploadId,
197
+ "chunk.upload_id": uploadId,
198
+ "upload.has_trace_context": file.traceContext ? "true" : "false",
199
+ },
200
+ parent: parentSpan,
201
+ }),
202
+ );
203
+
204
+ return yield* processChunk;
130
205
  }).pipe(
131
- // Add tracing span for chunk upload
132
- Effect.withSpan("upload-chunk", {
133
- attributes: {
134
- "upload.id": uploadId,
135
- "chunk.upload_id": uploadId,
136
- },
137
- }),
138
206
  // Track chunk upload metrics
139
207
  Effect.tap((file) =>
140
208
  Effect.gen(function* () {
@@ -1,2 +0,0 @@
1
- import{n as e}from"./uploadista-error-CkSxSyNo.mjs";import{C as t,D as n,E as r,N as i,O as a,T as o,b as s,d as c,w as l}from"./types-CH0BgiJN.mjs";import{a as u,n as d,o as f}from"./upload-ggK-0ZBM.mjs";import{Context as p,Effect as m,Layer as h,Option as g,Runtime as _,Stream as v}from"effect";import{z as y}from"zod";import{render as b}from"micromustache";const x={enabled:!1,failureThreshold:5,resetTimeout:3e4,halfOpenRequests:3,windowDuration:6e4,fallback:{type:`fail`}},S=`uploadista:circuit-breaker:`;function C(e){let t=e=>`${S}${e}`,n=n=>m.gen(function*(){let r=t(n),a=yield*e.get(r);if(a===null)return null;try{return i.deserialize(a)}catch{return yield*e.delete(r),null}}),r=(n,r)=>{let a=t(n),o=i.serialize(r);return e.set(a,o)};return{getState:n,setState:r,incrementFailures:(e,t)=>m.gen(function*(){let i=Date.now(),a=yield*n(e);return a===null&&(a=o({failureThreshold:5,resetTimeout:3e4,halfOpenRequests:3,windowDuration:t})),a=i-a.windowStart>t?{...a,failureCount:1,windowStart:i}:{...a,failureCount:a.failureCount+1},yield*r(e,a),a.failureCount}),resetFailures:e=>m.gen(function*(){let t=yield*n(e);t!==null&&(yield*r(e,{...t,failureCount:0,windowStart:Date.now()}))}),incrementHalfOpenSuccesses:e=>m.gen(function*(){let t=yield*n(e);if(t===null)return 1;let i={...t,halfOpenSuccesses:t.halfOpenSuccesses+1};return yield*r(e,i),i.halfOpenSuccesses}),getAllStats:()=>m.gen(function*(){let t=new Map;if(!e.list)return t;let r=yield*e.list(S),i=Date.now();for(let e of r){let r=e,a=yield*n(r);if(a!==null){let e=i-a.lastStateChange;t.set(r,{nodeType:r,state:a.state,failureCount:a.failureCount,halfOpenSuccesses:a.halfOpenSuccesses,timeSinceLastStateChange:e,timeUntilHalfOpen:a.state===`open`?Math.max(0,a.config.resetTimeout-e):void 0})}}return t}),delete:n=>e.delete(t(n))}}function w(){let e=new Map;return{getState:t=>m.succeed(e.get(t)??null),setState:(t,n)=>m.sync(()=>{e.set(t,n)}),incrementFailures:(t,n)=>m.sync(()=>{let r=Date.now(),i=e.get(t);return i===void 0&&(i=o({failureThreshold:5,resetTimeout:3e4,halfOpenRequests:3,windowDuration:n})),i=r-i.windowStart>n?{...i,failureCount:1,windowStart:r}:{...i,failureCount:i.failureCount+1},e.set(t,i),i.failureCount}),resetFailures:t=>m.sync(()=>{let n=e.get(t);n!==void 0&&e.set(t,{...n,failureCount:0,windowStart:Date.now()})}),incrementHalfOpenSuccesses:t=>m.sync(()=>{let n=e.get(t);if(n===void 0)return 1;let r={...n,halfOpenSuccesses:n.halfOpenSuccesses+1};return e.set(t,r),r.halfOpenSuccesses}),getAllStats:()=>m.sync(()=>{let t=new Map,n=Date.now();for(let[r,i]of e){let e=n-i.lastStateChange;t.set(r,{nodeType:r,state:i.state,failureCount:i.failureCount,halfOpenSuccesses:i.halfOpenSuccesses,timeSinceLastStateChange:e,timeUntilHalfOpen:i.state===`open`?Math.max(0,i.config.resetTimeout-e):void 0})}return t}),delete:t=>m.sync(()=>{e.delete(t)})}}const T=h.effect(l,m.gen(function*(){return C(yield*r)})),E=h.succeed(l,w());var D=class{eventHandler;nodeType;config;store;constructor(e,t,n){this.nodeType=e,this.config={enabled:t.enabled??x.enabled,failureThreshold:t.failureThreshold??x.failureThreshold,resetTimeout:t.resetTimeout??x.resetTimeout,halfOpenRequests:t.halfOpenRequests??x.halfOpenRequests,windowDuration:t.windowDuration??x.windowDuration,fallback:t.fallback??x.fallback},this.store=n}setEventHandler(e){this.eventHandler=e}allowRequest(){let e=this;return m.gen(function*(){if(!e.config.enabled)return{allowed:!0,state:`closed`,failureCount:0};let t=yield*e.store.getState(e.nodeType),n=Date.now();if(t===null&&(t=o({failureThreshold:e.config.failureThreshold,resetTimeout:e.config.resetTimeout,halfOpenRequests:e.config.halfOpenRequests,windowDuration:e.config.windowDuration}),yield*e.store.setState(e.nodeType,t)),t.state===`open`&&n-t.lastStateChange>=e.config.resetTimeout){let r=t.state;t={...t,state:`half-open`,halfOpenSuccesses:0,lastStateChange:n},yield*e.store.setState(e.nodeType,t),yield*e.emitEvent(r,`half-open`,t.failureCount)}return{allowed:t.state!==`open`,state:t.state,failureCount:t.failureCount}})}getState(){let e=this;return m.gen(function*(){return(yield*e.store.getState(e.nodeType))?.state??`closed`})}getFailureCount(){let e=this;return m.gen(function*(){return(yield*e.store.getState(e.nodeType))?.failureCount??0})}recordSuccess(){let e=this;return m.gen(function*(){if(!e.config.enabled)return;let t=yield*e.store.getState(e.nodeType);t!==null&&(t.state===`half-open`?(yield*e.store.incrementHalfOpenSuccesses(e.nodeType))>=e.config.halfOpenRequests&&(yield*e.transitionTo(`closed`,t.failureCount)):t.state===`closed`&&(yield*e.store.resetFailures(e.nodeType)))})}recordFailure(e){let t=this;return m.gen(function*(){if(!t.config.enabled)return;let e=yield*t.store.getState(t.nodeType);if(e===null||e.state===`closed`){let e=yield*t.store.incrementFailures(t.nodeType,t.config.windowDuration);e>=t.config.failureThreshold&&(yield*t.transitionTo(`open`,e))}else e.state===`half-open`&&(yield*t.transitionTo(`open`,e.failureCount))})}getFallback(){return this.config.fallback}reset(){let e=this;return m.gen(function*(){let t=(yield*e.store.getState(e.nodeType))?.state??`closed`;yield*e.store.setState(e.nodeType,o({failureThreshold:e.config.failureThreshold,resetTimeout:e.config.resetTimeout,halfOpenRequests:e.config.halfOpenRequests,windowDuration:e.config.windowDuration})),t!==`closed`&&(yield*e.emitEvent(t,`closed`,0))})}transitionTo(e,t){let n=this;return m.gen(function*(){let r=yield*n.store.getState(n.nodeType),i=r?.state??`closed`;if(i===e)return;let a=Date.now(),o={state:e,failureCount:e===`closed`?0:t,lastStateChange:a,halfOpenSuccesses:0,windowStart:e===`closed`?a:r?.windowStart??a,config:{failureThreshold:n.config.failureThreshold,resetTimeout:n.config.resetTimeout,halfOpenRequests:n.config.halfOpenRequests,windowDuration:n.config.windowDuration}};yield*n.store.setState(n.nodeType,o),yield*n.emitEvent(i,e,t)})}emitEvent(e,t,n){let r=this;return m.gen(function*(){r.eventHandler&&(yield*r.eventHandler({nodeType:r.nodeType,previousState:e,newState:t,timestamp:Date.now(),failureCount:n}))})}},ee=class{breakers=new Map;eventHandler;constructor(e){this.store=e}setEventHandler(e){this.eventHandler=e;for(let t of this.breakers.values())t.setEventHandler(e)}getOrCreate(e,t){let n=this.breakers.get(e);return n||(n=new D(e,t,this.store),this.eventHandler&&n.setEventHandler(this.eventHandler),this.breakers.set(e,n)),n}get(e){return this.breakers.get(e)}getAllStats(){return this.store.getAllStats()}resetAll(){let e=this;return m.gen(function*(){for(let t of e.breakers.values())yield*t.reset()})}clear(){this.breakers.clear()}};function te({source:e,target:t,sourcePort:n,targetPort:r}){return{source:e,target:t,sourcePort:n,targetPort:r}}let O=function(e){return e.JobStart=`job-start`,e.JobEnd=`job-end`,e.FlowStart=`flow-start`,e.FlowEnd=`flow-end`,e.FlowError=`flow-error`,e.FlowPause=`flow-pause`,e.FlowCancel=`flow-cancel`,e.NodeStart=`node-start`,e.NodeEnd=`node-end`,e.NodePause=`node-pause`,e.NodeResume=`node-resume`,e.NodeError=`node-error`,e.NodeStream=`node-stream`,e.NodeResponse=`node-response`,e.DlqItemAdded=`dlq-item-added`,e.DlqRetryStart=`dlq-retry-start`,e.DlqRetrySuccess=`dlq-retry-success`,e.DlqRetryFailed=`dlq-retry-failed`,e.DlqItemExhausted=`dlq-item-exhausted`,e.DlqItemResolved=`dlq-item-resolved`,e}({});var k=class{types;constructor(){this.types=new Map}register(t){if(this.types.has(t.id))throw e.fromCode(`VALIDATION_ERROR`,{body:`Input type "${t.id}" is already registered. Types cannot be modified or re-registered.`,details:{typeId:t.id}});this.types.set(t.id,t)}get(e){return this.types.get(e)}list(){return Array.from(this.types.values())}validate(t,n){let r=this.types.get(t);if(!r)return{success:!1,error:e.fromCode(`VALIDATION_ERROR`,{body:`Input type "${t}" is not registered`,details:{typeId:t}})};try{return{success:!0,data:r.schema.parse(n)}}catch(n){return{success:!1,error:e.fromCode(`VALIDATION_ERROR`,{body:`Data validation failed for input type "${t}"`,cause:n,details:{typeId:t,validationErrors:n}})}}}has(e){return this.types.has(e)}size(){return this.types.size}};const A=new k;function j(e,t){return A.validate(e,t)}var M=class{types;constructor(){this.types=new Map}register(t){if(this.types.has(t.id))throw e.fromCode(`VALIDATION_ERROR`,{body:`Output type "${t.id}" is already registered. Types cannot be modified or re-registered.`,details:{typeId:t.id}});this.types.set(t.id,t)}get(e){return this.types.get(e)}list(){return Array.from(this.types.values())}validate(t,n){let r=this.types.get(t);if(!r)return{success:!1,error:e.fromCode(`VALIDATION_ERROR`,{body:`Output type "${t}" is not registered`,details:{typeId:t}})};try{return{success:!0,data:r.schema.parse(n)}}catch(n){return{success:!1,error:e.fromCode(`VALIDATION_ERROR`,{body:`Data validation failed for output type "${t}"`,cause:n,details:{typeId:t,validationErrors:n}})}}}has(e){return this.types.has(e)}size(){return this.types.size}};const N=new M;function ne(e,t){return N.validate(e,t)}let P=function(e){return e.input=`input`,e.process=`process`,e.conditional=`conditional`,e.multiplex=`multiplex`,e.merge=`merge`,e}({});function F({id:t,name:n,description:r,type:i,inputSchema:a,outputSchema:o,run:s,condition:c,multiInput:l=!1,multiOutput:u=!1,pausable:d=!1,retry:f,inputTypeId:p,outputTypeId:h,keepOutput:g=!1,circuitBreaker:_,nodeTypeId:v}){return m.gen(function*(){return p&&!A.get(p)?yield*e.fromCode(`INVALID_INPUT_TYPE`,{body:`Input type "${p}" is not registered in inputTypeRegistry`,details:{inputTypeId:p,nodeId:t}}).toEffect():h&&!N.get(h)?yield*e.fromCode(`INVALID_OUTPUT_TYPE`,{body:`Output type "${h}" is not registered in outputTypeRegistry`,details:{outputTypeId:h,nodeId:t}}).toEffect():{id:t,name:n,description:r,type:i,inputTypeId:p,outputTypeId:h,keepOutput:g,inputSchema:a,outputSchema:o,pausable:d,run:({data:r,jobId:i,flowId:c,storageId:l,clientId:u})=>m.gen(function*(){let d=yield*s({data:yield*m.try({try:()=>a.parse(r),catch:r=>{let i=r instanceof Error?r.message:String(r);return e.fromCode(`FLOW_INPUT_VALIDATION_ERROR`,{body:`Node '${n}' (${t}) input validation failed: ${i}`,cause:r})}}),jobId:i,storageId:l,flowId:c,clientId:u});return d.type===`waiting`?{type:`waiting`,partialData:d.partialData,nodeType:h,nodeId:t}:{type:`complete`,data:yield*m.try({try:()=>o.parse(d.data),catch:r=>{let i=r instanceof Error?r.message:String(r);return e.fromCode(`FLOW_OUTPUT_VALIDATION_ERROR`,{body:`Node '${n}' (${t}) output validation failed: ${i}`,cause:r})}}),nodeType:h,nodeId:t}}),condition:c,multiInput:l,multiOutput:u,retry:f,circuitBreaker:_,nodeTypeId:v}})}const re=e=>({id:e.id,name:e.name,description:e.description,type:e.type,inputTypeId:e.inputTypeId,outputTypeId:e.outputTypeId,nodeTypeId:e.nodeTypeId}),I=e=>({type:`complete`,data:e}),L=e=>({type:`waiting`,partialData:e}),ie=(e,t)=>{if(e===t)return!0;try{return!!(e&&t&&typeof e==`object`&&typeof t==`object`)}catch{return!0}};var ae=class{typeChecker;constructor(e=ie){this.typeChecker=e}validateConnection(e,t,n){return this.getCompatibleTypes(e.outputSchema,t.inputSchema)}getCompatibleTypes(e,t){return this.typeChecker(e,t)}validateFlow(e,t){let n=[],r=new Map(e.map(e=>[e.id,e]));for(let e of t){let t=r.get(e.source),i=r.get(e.target);if(!t){n.push(`Source node ${e.source} not found`);continue}if(!i){n.push(`Target node ${e.target} not found`);continue}this.validateConnection(t,i,e)||n.push(`Schema mismatch: ${t.id} output schema incompatible with ${i.id} input schema`)}return{isValid:n.length===0,errors:n}}getExpectedInputSchemas(e,t,n){let r=new Map(t.map(e=>[e.id,e])),i={};for(let t of n)if(t.target===e){let e=r.get(t.source);if(e){let n=t.sourcePort||t.source;i[n]=e.outputSchema}}return i}getActualOutputSchemas(e,t,n){let r=new Map(t.map(e=>[e.id,e])),i={};for(let t of n)if(t.source===e){let e=r.get(t.target);if(e){let n=t.targetPort||t.target;i[n]=e.inputSchema}}return i}validateData(e,t){try{return t.parse(e),{isValid:!0,errors:[]}}catch(e){return e instanceof Error&&`errors`in e?{isValid:!1,errors:e.errors.map(e=>`${e.path.join(`.`)}: ${e.message}`)}:{isValid:!1,errors:[e instanceof Error?e.message:`Validation failed`]}}}};function R(e){if(!e)return{type:``,fileName:``,metadata:void 0,metadataJson:void 0};let t={...e},n=String(t.type||t.mimeType||t[`content-type`]||``);n&&(t.type||=n,t.mimeType||=n);let r=String(t.fileName||t.originalName||t.name||``);return r&&(t.fileName||=r,t.originalName||=r,t.name||=r),{type:n,fileName:r,metadata:t,metadataJson:JSON.stringify(t)}}const oe=y.object({operation:y.literal(`init`),storageId:y.string(),metadata:y.record(y.string(),y.any()).optional()}),se=y.object({operation:y.literal(`finalize`),uploadId:y.string()}),ce=y.object({operation:y.literal(`url`),url:y.string(),storageId:y.string().optional(),metadata:y.record(y.string(),y.any()).optional()}),z=y.union([oe,se,ce]),le=y.object({allowedMimeTypes:y.array(y.string()).optional(),minSize:y.number().positive().optional(),maxSize:y.number().positive().optional()});function ue(t,n){return m.gen(function*(){if(n){if(n.allowedMimeTypes&&n.allowedMimeTypes.length>0&&!n.allowedMimeTypes.some(e=>{if(e.endsWith(`/*`)){let n=e.slice(0,-2);return t.type.startsWith(n)}return t.type===e}))throw yield*e.fromCode(`VALIDATION_ERROR`,{cause:Error(`File type "${t.type}" is not allowed. Allowed types: ${n.allowedMimeTypes.join(`, `)}`)}).toEffect();if(n.minSize!==void 0&&t.size<n.minSize)throw yield*e.fromCode(`VALIDATION_ERROR`,{cause:Error(`File size (${t.size} bytes) is below minimum (${n.minSize} bytes)`)}).toEffect();if(n.maxSize!==void 0&&t.size>n.maxSize)throw yield*e.fromCode(`VALIDATION_ERROR`,{cause:Error(`File size (${t.size} bytes) exceeds maximum (${n.maxSize} bytes)`)}).toEffect()}})}function de(n,r,i){let a=i?.keepOutput??!1;return m.gen(function*(){let i=yield*d;return yield*F({id:n,name:`Input`,description:`Handles file input through multiple methods - streaming upload (init/finalize) or direct URL fetch`,type:P.input,nodeTypeId:`input`,inputSchema:z,outputSchema:t,keepOutput:a,inputTypeId:U,outputTypeId:B,run:({data:t,flowId:a,jobId:o,clientId:s})=>m.gen(function*(){switch(t.operation){case`init`:{let e={storageId:t.storageId,size:t.metadata?.size||0,type:t.metadata?.mimeType||`application/octet-stream`,fileName:t.metadata?.originalName,lastModified:t.metadata?.size?Date.now():void 0,metadata:t.metadata?JSON.stringify(t.metadata):void 0,flow:{flowId:a,nodeId:n,jobId:o}};return L(yield*i.createUpload(e,s))}case`finalize`:{let e=yield*i.getUpload(t.uploadId),{type:n}=R(e.metadata);return yield*ue({type:n,size:e.size||0},r),I(e)}case`url`:{let e=yield*f(t.url),c=yield*u(e),l=t.metadata?.mimeType||e.headers.get(`content-type`)||`application/octet-stream`,d=t.metadata?.size||Number(e.headers.get(`content-length`)||0),p=t.metadata?.originalName||t.url.split(`/`).pop()||`file`;yield*ue({type:l,size:d},r);let m=new ReadableStream({start(e){e.enqueue(new Uint8Array(c)),e.close()}}),h={storageId:t.storageId||`buffer`,size:d,type:l,fileName:p,lastModified:Date.now(),metadata:t.metadata?JSON.stringify(t.metadata):void 0};return I({...yield*i.upload(h,s,m),flow:{flowId:a,nodeId:n,jobId:o}})}default:throw yield*e.fromCode(`VALIDATION_ERROR`,{cause:Error(`Invalid operation`)}).toEffect()}})})})}const B=`storage-output-v1`,V=`ocr-output-v1`,H=`image-description-output-v1`,U=`streaming-input-v1`,W=y.object({extractedText:y.string(),format:y.enum([`markdown`,`plain`,`structured`]),taskType:y.enum([`convertToMarkdown`,`freeOcr`,`parseFigure`,`locateObject`]),confidence:y.number().min(0).max(1).optional()}),G=y.object({description:y.string(),confidence:y.number().min(0).max(1).optional(),metadata:y.record(y.string(),y.unknown()).optional()});A.register({id:U,schema:z,version:`1.0.0`,description:`Streaming file input with init/finalize/url operations for flexible file ingestion`}),N.register({id:B,schema:t,version:`1.0.0`,description:`Storage output node that saves files to configured storage backend`}),N.register({id:V,schema:W,version:`1.0.0`,description:`OCR output node that extracts structured text from documents using AI`}),N.register({id:H,schema:G,version:`1.0.0`,description:`Image description output node that generates AI-powered descriptions of images`});var fe=class{maxConcurrency;constructor(e={}){this.maxConcurrency=e.maxConcurrency??4}groupNodesByExecutionLevel(e,t){let n={},r={};e.forEach(e=>{n[e.id]=[],r[e.id]=0}),t.forEach(e=>{n[e.source]?.push(e.target),r[e.target]=(r[e.target]||0)+1});let i=[],a=new Set,o=0;for(;a.size<e.length;){let e=Object.keys(r).filter(e=>r[e]===0&&!a.has(e));if(e.length===0)throw Error(`Cycle detected in flow graph - cannot execute in parallel`);i.push({level:o++,nodes:e}),e.forEach(e=>{a.add(e),delete r[e],n[e]?.forEach(e=>{r[e]!==void 0&&r[e]--})})}return i}executeNodesInParallel(e){return m.all(e.map(e=>e()),{concurrency:this.maxConcurrency})}canExecuteInParallel(e,t,n){return e.every(e=>(n[e]||[]).every(e=>t.has(e)))}getStats(){return{maxConcurrency:this.maxConcurrency}}};function K(e){return t=>{if(t.nodeType!==e)return!1;let n=N.get(e);return n?n.schema.safeParse(t.data).success:!1}}function pe(e){return!e||typeof e!=`object`?!1:t.safeParse(e).success}const me=K(`storage-output-v1`),he=K(V),ge=K(H);function q(e,t){return e.filter(t)}function _e(t,n){return m.gen(function*(){let r=q(t,n);return r.length===0?yield*e.fromCode(`OUTPUT_NOT_FOUND`,{body:`No output of the specified type was found in the flow results`}).toEffect():r.length>1?yield*e.fromCode(`MULTIPLE_OUTPUTS_FOUND`,{body:`Found ${r.length} outputs of the specified type, expected exactly one`,details:{foundCount:r.length,nodeIds:r.map(e=>e.nodeId)}}).toEffect():r[0]})}function ve(e,t){return q(e,t)[0]}function ye(e,t){return e.find(e=>e.nodeId===t)}function be(e,t){return e.some(t)}function xe(e){return e.operation===`init`}function Se(e){return e.operation===`finalize`}function Ce(e){return e.operation===`url`}function we(e){return e.operation===`init`||e.operation===`url`}const Te=e=>({id:e.id,name:e.name,nodes:e.nodes.map(re),edges:e.edges});function J(t){return m.gen(function*(){let n=yield*m.all(t.nodes.map(e=>m.isEffect(e)?e:m.succeed(e))),{flowId:r,name:i,onEvent:a,checkJobStatus:o,edges:c,inputSchema:u,outputSchema:d,typeChecker:f,circuitBreaker:p}=t,h=n,g=new ae(f),_=e=>{let t=e.circuitBreaker,n=e.nodeTypeId?p?.nodeTypeOverrides?.[e.nodeTypeId]:void 0,r=p?.defaults;if(!(!t&&!n&&!r))return{...r,...n,...t}},b=()=>{let e={},t={},n={};return h.forEach(r=>{e[r.id]=[],n[r.id]=[],t[r.id]=0}),c.forEach(r=>{e[r.source]?.push(r.target),n[r.target]?.push(r.source),t[r.target]=(t[r.target]||0)+1}),{graph:e,reverseGraph:n,inDegree:t}},x=()=>{let{graph:e,inDegree:t}=b(),n=[],r=[];for(Object.keys(t).forEach(e=>{t[e]===0&&n.push(e)});n.length>0;){let i=n.shift();if(!i)throw Error(`No current node found`);r.push(i),e[i]?.forEach(e=>{t[e]=(t[e]||0)-1,t[e]===0&&n.push(e)})}return r},S=(e,t)=>{if(!e.condition)return m.succeed(!0);let{field:n,operator:r,value:i}=e.condition,a=t,o=a?.metadata?.[n]||a?.[n],s=(()=>{switch(r){case`equals`:return o===i;case`notEquals`:return o!==i;case`greaterThan`:return Number(o)>Number(i);case`lessThan`:return Number(o)<Number(i);case`contains`:return String(o).includes(String(i));case`startsWith`:return String(o).startsWith(String(i));default:return!0}})();return m.succeed(s)},C=(e,t)=>{let{reverseGraph:n}=b(),r=n[e]||[],i={};return r.forEach(e=>{let n=t.get(e);n!==void 0&&(i[e]=n)}),i},w=e=>{let t=h.filter(e=>e.type===`input`),n={};return t.forEach(t=>{e&&typeof e==`object`&&t.id in e&&(n[t.id]=u.parse(e[t.id]))}),n},T=e=>!c.some(t=>t.source===e),E=e=>{let t=h.find(t=>t.id===e);return T(e)||t?.keepOutput===!0},D=e=>{let t=h.filter(e=>E(e.id)),n={};return t.forEach(t=>{let r=e.get(t.id);r!==void 0&&(n[t.id]=r)}),n},te=(e,t)=>{let n=h.filter(e=>E(e.id)),r=[];return n.forEach(n=>{let i=e.get(n.id);if(i!==void 0){let e=t.get(n.id);r.push({nodeId:n.id,nodeType:e,data:i,timestamp:new Date().toISOString()})}}),r},k=(e,t,n)=>m.gen(function*(){if(e.storage.id===t)return e;let r=yield*s,i=yield*r.getDataStore(e.storage.id,n),a=yield*r.getDataStore(t,n),o=yield*i.read(e.id),c=v.make(o),l={...e,storage:{id:t,type:e.storage.type}},u=yield*a.create(l);return yield*a.write({file_id:u.id,stream:c,offset:0},{}),u}),A=(n,i,s,c,l,u,d,f)=>m.gen(function*(){let p=l.get(n);if(!p)return yield*e.fromCode(`FLOW_NODE_NOT_FOUND`).toEffect();if(o){let t=yield*o(u);if(t===`paused`)return yield*e.fromCode(`FLOW_PAUSED`,{cause:`Flow ${r} was paused by user at job ${u}`}).toEffect();if(t===`cancelled`)return yield*e.fromCode(`FLOW_CANCELLED`,{cause:`Flow ${r} was cancelled by user at job ${u}`}).toEffect()}a&&(yield*a({jobId:u,flowId:r,nodeId:n,eventType:O.NodeStart,nodeName:p.name,nodeType:p.type}));let h=p.retry?.maxRetries??0,g=p.retry?.retryDelay??1e3,v=p.retry?.exponentialBackoff??!0,y=_(p),b=y?.enabled&&p.nodeTypeId&&f?f.getOrCreate(p.nodeTypeId,y):null;if(b){let{allowed:t,state:i,failureCount:o}=yield*b.allowRequest();if(!t){let t=b.getFallback();return yield*m.logWarning(`Circuit breaker OPEN for node type "${p.nodeTypeId}" - applying fallback`),t.type===`skip`?(a&&(yield*a({jobId:u,flowId:r,nodeId:n,eventType:O.NodeEnd,nodeName:p.name})),{nodeId:n,result:s[n],success:!0,waiting:!1}):t.type===`default`?(a&&(yield*a({jobId:u,flowId:r,nodeId:n,eventType:O.NodeEnd,nodeName:p.name,result:t.value})),{nodeId:n,result:t.value,success:!0,waiting:!1}):yield*e.fromCode(`CIRCUIT_BREAKER_OPEN`,{body:`Circuit breaker is open for node type "${p.name}"`,details:{nodeType:p.name,nodeId:n,state:i,failureCount:o}}).toEffect()}}let x=0,w=null;for(;x<=h;)try{let o,l={};if(p.type===`input`){if(o=s[n],o===void 0)return yield*m.logError(`Input node ${n} has no input data`),yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:Error(`Input node ${n} has no input data`)}).toEffect()}else{if(l=C(n,c),Object.keys(l).length===0)return yield*m.logError(`Node ${n} has no input data`),yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:Error(`Node ${n} has no input data`)}).toEffect();if(p.multiInput)o=l;else{let t=Object.keys(l)[0];if(!t)return yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:Error(`Node ${n} has no input data`)}).toEffect();o=l[t]}}if(p.type===`conditional`&&!(yield*S(p,o)))return a&&(yield*a({jobId:u,flowId:r,nodeId:n,eventType:O.NodeEnd,nodeName:p.name})),{nodeId:n,result:o,success:!0,waiting:!1};let f=yield*p.run({data:o,inputs:l,jobId:u,flowId:r,storageId:i,clientId:d});if(f.type===`waiting`){let e=f.partialData;return a&&(yield*a({jobId:u,flowId:r,nodeId:n,eventType:O.NodePause,nodeName:p.name,partialData:e})),{nodeId:n,result:e,success:!0,waiting:!0,nodeType:f.nodeType}}let h=f.data;if(E(n)&&(pe(h)&&h.storage.id!==i&&(yield*m.logDebug(`Auto-persisting output node ${n} output from ${h.storage.id} to ${i}`),h=yield*k(h,i,d)),t.hooks?.onNodeOutput)){yield*m.logDebug(`Calling onNodeOutput hook for sink node ${n}`);let e=t.hooks.onNodeOutput({output:h,nodeId:n,flowId:r,jobId:u,storageId:i,clientId:d});h=yield*m.isEffect(e)?e:m.promise(()=>e)}return b&&(yield*b.recordSuccess()),a&&(yield*a({jobId:u,flowId:r,nodeId:n,eventType:O.NodeEnd,nodeName:p.name,result:h})),{nodeId:n,result:h,success:!0,waiting:!1,nodeType:f.nodeType}}catch(t){if(w=t instanceof e?t:e.fromCode(`FLOW_NODE_ERROR`,{cause:t}),b&&(yield*b.recordFailure(w.body)),x<h){x++;let e=v?g*2**(x-1):g;yield*m.logWarning(`Node ${n} (${p.name}) failed, retrying (${x}/${h}) after ${e}ms`),yield*m.sleep(e);continue}return a&&(yield*a({jobId:u,flowId:r,nodeId:n,eventType:O.NodeError,nodeName:p.name,error:w.body,retryCount:x})),yield*w.toEffect()}return w?yield*w.toEffect():yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:Error(`Unexpected error in retry loop`)}).toEffect()}),j=({inputs:n,storageId:i,jobId:o,resumeFrom:s,clientId:u})=>m.gen(function*(){let f=yield*m.serviceOption(l),p=f._tag===`Some`?new ee(f.value):null;!s&&a&&(yield*a({jobId:o,eventType:O.FlowStart,flowId:r}));let g=w(n||{}),_,v,b;s?(_=s.executionOrder,v=s.nodeResults,b=s.currentIndex):(_=x(),v=new Map,b=0);let S=new Map;if(_.length!==h.length)return yield*e.fromCode(`FLOW_CYCLE_ERROR`).toEffect();let C=new Map(h.map(e=>[e.id,e]));if(t.parallelExecution?.enabled??!1){yield*m.logDebug(`Flow ${r}: Executing in parallel mode (maxConcurrency: ${t.parallelExecution?.maxConcurrency??4})`);let e=new fe({maxConcurrency:t.parallelExecution?.maxConcurrency??4}),n=e.groupNodesByExecutionLevel(h,c);yield*m.logDebug(`Flow ${r}: Grouped nodes into ${n.length} execution levels`);let l={};h.forEach(e=>{l[e.id]=[]}),c.forEach(e=>{l[e.target]?.push(e.source)});for(let t of n){yield*m.logDebug(`Flow ${r}: Executing level ${t.level} with nodes: ${t.nodes.join(`, `)}`);let n=t.nodes.map(e=>()=>m.gen(function*(){if(s&&e===s.executionOrder[b]&&a){let t=C.get(e);t&&(yield*a({jobId:o,flowId:r,nodeId:e,eventType:O.NodeResume,nodeName:t.name,nodeType:t.type}))}return{nodeId:e,nodeResult:yield*A(e,i,g,v,C,o,u,p)}})),c=yield*e.executeNodesInParallel(n);for(let{nodeId:e,nodeResult:t}of c){if(t.waiting)return t.result!==void 0&&(v.set(e,t.result),t.nodeType&&S.set(e,t.nodeType)),{type:`paused`,nodeId:e,executionState:{executionOrder:_,currentIndex:_.indexOf(e),inputs:g}};t.success&&(v.set(e,t.result),t.nodeType&&S.set(e,t.nodeType))}}}else{yield*m.logDebug(`Flow ${r}: Executing in sequential mode`);for(let t=b;t<_.length;t++){let n=_[t];if(!n)return yield*e.fromCode(`FLOW_NODE_NOT_FOUND`).toEffect();if(s&&t===b&&a){let e=C.get(n);e&&(yield*a({jobId:o,flowId:r,nodeId:n,eventType:O.NodeResume,nodeName:e.name,nodeType:e.type}))}let c=yield*A(n,i,g,v,C,o,u,p);if(c.waiting)return c.result!==void 0&&(v.set(c.nodeId,c.result),c.nodeType&&S.set(c.nodeId,c.nodeType)),{type:`paused`,nodeId:c.nodeId,executionState:{executionOrder:_,currentIndex:t,inputs:g}};c.success&&(v.set(c.nodeId,c.result),c.nodeType&&S.set(c.nodeId,c.nodeType))}}let T=D(v),k=te(v,S),j=y.record(y.string(),d).safeParse(T);if(!j.success){let t=`Flow output validation failed: ${j.error.message}. Expected outputs: ${JSON.stringify(Object.keys(D(v)))}. Output nodes (sinks + keepOutput): ${h.filter(e=>E(e.id)).map(e=>e.id).join(`, `)}`;return a&&(yield*a({jobId:o,eventType:O.FlowError,flowId:r,error:t})),yield*e.fromCode(`FLOW_OUTPUT_VALIDATION_ERROR`,{body:t,cause:j.error}).toEffect()}let M=j.data;return a&&(yield*a({jobId:o,eventType:O.FlowEnd,flowId:r,outputs:k,result:M})),{type:`completed`,result:M,outputs:k}});return{id:r,name:i,nodes:h,edges:c,inputSchema:u,outputSchema:d,onEvent:a,checkJobStatus:o,hooks:t.hooks,run:({inputs:e,storageId:t,jobId:n,clientId:r})=>j({inputs:e,storageId:t,jobId:n,clientId:r}),resume:({jobId:e,storageId:t,nodeResults:n,executionState:r,clientId:i})=>j({inputs:r.inputs,storageId:t,jobId:e,resumeFrom:{executionOrder:r.executionOrder,nodeResults:new Map(Object.entries(n)),currentIndex:r.currentIndex},clientId:i}),validateTypes:()=>{let e=h;return g.validateFlow(e,c)},validateInputs:e=>g.validateData(e,u),validateOutputs:e=>g.validateData(e,d)}})}const Y={enabled:!0,maxRetries:3,backoff:{type:`exponential`,initialDelayMs:1e3,maxDelayMs:3e5,multiplier:2,jitter:!0},ttlMs:6048e5};function X(e,t){switch(e.type){case`immediate`:return 0;case`fixed`:return e.delayMs;case`exponential`:{let n=e.initialDelayMs*e.multiplier**+t,r=Math.min(n,e.maxDelayMs);if(e.jitter){let e=.5+Math.random();return Math.floor(r*e)}return r}default:return 0}}function Ee(e,t){return!t.enabled||t.nonRetryableErrors?.includes(e)?!1:t.retryableErrors&&t.retryableErrors.length>0?t.retryableErrors.includes(e):!0}function De(e,t){if(!(t===void 0||t<=0))return new Date(e.getTime()+t)}var Z=class e extends p.Tag(`DeadLetterQueueService`)(){static optional=m.serviceOption(e)};function Oe(){return m.gen(function*(){let e=yield*n,t=()=>`dlq_${crypto.randomUUID()}`,r=e=>({...e,createdAt:new Date(e.createdAt),updatedAt:new Date(e.updatedAt),expiresAt:e.expiresAt?new Date(e.expiresAt):void 0,nextRetryAt:e.nextRetryAt?new Date(e.nextRetryAt):void 0,retryHistory:e.retryHistory.map(e=>({...e,attemptedAt:new Date(e.attemptedAt)}))}),i=()=>m.gen(function*(){if(!e.list)return[];let t=yield*e.list(),n=[];for(let i of t){let t=yield*m.catchAll(e.get(i),()=>m.succeed(null));t&&n.push(r(t))}return n});return{add:(n,r,i=Y)=>m.gen(function*(){let a=t(),o=new Date,s={code:r.code||`UNKNOWN_ERROR`,message:r.body||r.message||`Unknown error`,nodeId:void 0,stack:r.stack},c=n.tasks.find(e=>e.status===`failed`);c&&(s.nodeId=c.nodeId);let l={};for(let e of n.tasks)e.result!==void 0&&(l[e.nodeId]=e.result);let u=Ee(s.code,i),d;if(i.enabled&&u&&i.maxRetries>0){let e=X(i.backoff,0);d=new Date(o.getTime()+e)}let f={id:a,jobId:n.id,flowId:n.flowId,storageId:n.storageId,clientId:n.clientId,error:s,inputs:n.executionState?.inputs||{},nodeResults:l,failedAtNodeId:s.nodeId,retryCount:0,maxRetries:i.maxRetries,nextRetryAt:d,retryHistory:[],createdAt:o,updatedAt:o,expiresAt:De(o,i.ttlMs),status:u&&i.enabled?`pending`:`exhausted`};return yield*e.set(a,f),f}),get:t=>m.gen(function*(){return r(yield*e.get(t))}),getOption:t=>m.gen(function*(){let n=yield*m.either(e.get(t));return n._tag===`Left`?n.left.code===`FILE_NOT_FOUND`?g.none():yield*m.fail(n.left):g.some(r(n.right))}),delete:t=>e.delete(t),list:(e={})=>m.gen(function*(){let t=yield*i(),{status:n,flowId:r,clientId:a,limit:o=50,offset:s=0}=e,c=t;n&&(c=c.filter(e=>e.status===n)),r&&(c=c.filter(e=>e.flowId===r)),a&&(c=c.filter(e=>e.clientId===a)),c.sort((e,t)=>t.createdAt.getTime()-e.createdAt.getTime());let l=c.length;return{items:c.slice(s,s+o),total:l}}),update:(t,n)=>m.gen(function*(){let i={...r(yield*e.get(t)),...n,updatedAt:new Date};return yield*e.set(t,i),i}),markRetrying:t=>m.gen(function*(){let n={...r(yield*e.get(t)),status:`retrying`,updatedAt:new Date};return yield*e.set(t,n),n}),recordRetryFailure:(t,n,i)=>m.gen(function*(){let a=r(yield*e.get(t)),o=new Date,s=a.retryCount+1,c=[...a.retryHistory,{attemptedAt:o,error:n,durationMs:i}],l=`pending`,u;if(s>=a.maxRetries)l=`exhausted`,u=void 0;else{let e=X(Y.backoff,s);u=new Date(o.getTime()+e)}let d={...a,retryCount:s,retryHistory:c,status:l,nextRetryAt:u,updatedAt:o};return yield*e.set(t,d),d}),markResolved:t=>m.gen(function*(){let n={...r(yield*e.get(t)),status:`resolved`,nextRetryAt:void 0,updatedAt:new Date};return yield*e.set(t,n),n}),getScheduledRetries:(e=100)=>m.gen(function*(){let t=yield*i(),n=new Date;return t.filter(e=>e.status===`pending`&&e.nextRetryAt&&e.nextRetryAt<=n).sort((e,t)=>(e.nextRetryAt?.getTime()||0)-(t.nextRetryAt?.getTime()||0)).slice(0,e)}),cleanup:(t={})=>m.gen(function*(){let n=yield*i(),{olderThan:r,status:a}=t,o=new Date,s=0;for(let t of n){let n=!1;t.expiresAt&&t.expiresAt<=o&&(n=!0),r&&t.createdAt<=r&&(a?n=t.status===a:(t.status===`exhausted`||t.status===`resolved`)&&(n=!0)),n&&(yield*m.catchAll(e.delete(t.id),()=>m.succeed(void 0)),s++)}return{deleted:s}}),getStats:()=>m.gen(function*(){let e=yield*i(),t={pending:0,retrying:0,exhausted:0,resolved:0},n={},r,a=0;for(let i of e)t[i.status]++,n[i.flowId]=(n[i.flowId]||0)+1,(!r||i.createdAt<r)&&(r=i.createdAt),a+=i.retryCount;let o=e.length>0?a/e.length:0;return{totalItems:e.length,byStatus:t,byFlow:n,oldestItem:r,averageRetryCount:o}})}})}const ke=h.effect(Z,Oe());var Q=class e extends p.Tag(`FlowWaitUntil`)(){static optional=m.serviceOption(e)},Ae=class extends p.Tag(`FlowProvider`)(){},je=class extends p.Tag(`FlowServer`)(){};const Me=e=>typeof e==`object`&&!!e&&`id`in e,Ne=e=>typeof e==`object`&&e&&`nodeId`in e&&`data`in e&&`timestamp`in e?e.data:e;function Pe(t,n,r){let i=e=>{let i=t=>m.gen(function*(){let n=yield*r.get(e);n&&(yield*r.set(e,{...n,...t,updatedAt:new Date}))});return a=>m.gen(function*(){switch(t.onEvent&&(yield*m.catchAll(t.onEvent(a),e=>(m.logError(`Original onEvent failed`,e),m.succeed({eventId:null})))),yield*n.emit(e,a),m.logInfo(`Updating job ${e} with event ${a.eventType}`),a.eventType){case O.FlowStart:yield*i({status:`running`});break;case O.FlowEnd:yield*m.gen(function*(){let t=yield*r.get(e);t&&a.outputs&&(yield*r.set(e,{...t,result:a.outputs,updatedAt:new Date}))});break;case O.FlowError:yield*i({status:`failed`,error:a.error});break;case O.NodeStart:yield*m.gen(function*(){let t=yield*r.get(e);if(t){let n=t.tasks.find(e=>e.nodeId===a.nodeId)?t.tasks.map(e=>e.nodeId===a.nodeId?{...e,status:`running`,updatedAt:new Date}:e):[...t.tasks,{nodeId:a.nodeId,status:`running`,createdAt:new Date,updatedAt:new Date}];yield*r.set(e,{...t,tasks:n,updatedAt:new Date})}});break;case O.NodePause:yield*m.gen(function*(){let t=yield*r.get(e);if(t){let n=t.tasks.find(e=>e.nodeId===a.nodeId)?t.tasks.map(e=>e.nodeId===a.nodeId?{...e,status:`paused`,result:a.partialData,updatedAt:new Date}:e):[...t.tasks,{nodeId:a.nodeId,status:`paused`,result:a.partialData,createdAt:new Date,updatedAt:new Date}];yield*r.set(e,{...t,tasks:n,updatedAt:new Date})}});break;case O.NodeResume:yield*m.gen(function*(){let t=yield*r.get(e);if(t){let n=t.tasks.map(e=>e.nodeId===a.nodeId?{...e,status:`running`,updatedAt:new Date}:e);yield*r.set(e,{...t,tasks:n,updatedAt:new Date})}});break;case O.NodeEnd:yield*m.gen(function*(){let n=yield*r.get(e);if(n){let i=n.tasks.map(e=>e.nodeId===a.nodeId?{...e,status:`completed`,result:a.result,updatedAt:new Date}:e),o=!t.edges.some(e=>e.source===a.nodeId),s=t.nodes.find(e=>e.id===a.nodeId)?.keepOutput===!0,c=a.result,l=Ne(c),u=n.intermediateFiles||[],d=o||s;d&&Me(l)&&l.id?(u=u.filter(e=>e!==l.id),s&&!o&&m.logInfo(`Preserving output from node ${a.nodeId} due to keepOutput flag`)):!d&&Me(l)&&l.id&&(u.includes(l.id)||u.push(l.id)),yield*r.set(e,{...n,tasks:i,intermediateFiles:u,updatedAt:new Date})}});break;case O.NodeError:yield*m.gen(function*(){let t=yield*r.get(e);if(t){let n=t.tasks.map(e=>e.nodeId===a.nodeId?{...e,status:`failed`,error:a.error,retryCount:a.retryCount,updatedAt:new Date}:e);yield*r.set(e,{...t,tasks:n,error:a.error,updatedAt:new Date})}});break}return{eventId:e}})},a=t=>t=>m.gen(function*(){let n=yield*r.get(t);return n?n.status===`paused`?`paused`:n.status===`cancelled`?`cancelled`:`running`:yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${t} not found`}))});return{...t,run:e=>m.gen(function*(){let n=e.jobId||crypto.randomUUID(),r=i(n),o=a(n);return yield*(yield*J({flowId:t.id,name:t.name,nodes:t.nodes,edges:t.edges,inputSchema:t.inputSchema,outputSchema:t.outputSchema,onEvent:r,checkJobStatus:o})).run({...e,jobId:n,clientId:e.clientId})}),resume:e=>m.gen(function*(){let n=e.jobId,r=i(n),o=a(n);return yield*(yield*J({flowId:t.id,name:t.name,nodes:t.nodes,edges:t.edges,inputSchema:t.inputSchema,outputSchema:t.outputSchema,onEvent:r,checkJobStatus:o})).resume(e)})}}function Fe(){return m.gen(function*(){let t=yield*Ae,n=yield*c,r=yield*a,i=yield*d,o=yield*Z.optional,s=(t,n)=>m.gen(function*(){let i=yield*r.get(t);return i?yield*r.set(t,{...i,...n}):yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${t} not found`}))}),l=(e,t)=>m.gen(function*(){let n=yield*r.get(e);!n||!n.intermediateFiles||n.intermediateFiles.length===0||(yield*m.logInfo(`Cleaning up ${n.intermediateFiles.length} intermediate files for job ${e}`),yield*m.all(n.intermediateFiles.map(e=>m.gen(function*(){yield*i.delete(e,t),yield*m.logDebug(`Deleted intermediate file ${e}`)}).pipe(m.catchAll(t=>m.gen(function*(){return yield*m.logWarning(`Failed to delete intermediate file ${e}: ${t}`),m.succeed(void 0)})))),{concurrency:5}),yield*s(e,{intermediateFiles:[]}))}),u=(e,t)=>m.gen(function*(){if(g.isNone(o)){yield*m.logDebug(`[FlowServer] DLQ not configured, skipping for job: ${e}`);return}let n=o.value,i=yield*m.catchAll(r.get(e),()=>m.succeed(null));if(!i){yield*m.logWarning(`[FlowServer] Job ${e} not found when adding to DLQ`);return}yield*m.catchAll(n.add(i,t),t=>m.gen(function*(){return yield*m.logError(`[FlowServer] Failed to add job ${e} to DLQ`,t),m.succeed(void 0)})),yield*m.logInfo(`[FlowServer] Added job ${e} to Dead Letter Queue`)}),f=({jobId:t,flow:i,storageId:a,clientId:o,inputs:c})=>m.gen(function*(){console.log(`[FlowServer] executeFlowInBackground started for job: ${t}`),yield*s(t,{status:`running`}),console.log(`[FlowServer] Creating flowWithEvents for job: ${t}`);let e=Pe(i,n,r);console.log(`[FlowServer] Running flow for job: ${t}`);let u=yield*e.run({inputs:c,storageId:a,jobId:t,clientId:o});return console.log(`[FlowServer] Flow completed for job: ${t}, result type: ${u.type}`),u.type===`paused`?yield*s(t,{status:`paused`,pausedAt:u.nodeId,executionState:u.executionState,updatedAt:new Date}):(yield*s(t,{status:`completed`,updatedAt:new Date,endedAt:new Date}),yield*l(t,o)),u}).pipe(m.catchAll(i=>m.gen(function*(){yield*m.logError(`Flow execution failed`,i);let a=i instanceof e?i.body:String(i);yield*m.logInfo(`Updating job ${t} to failed status with error: ${a}`),yield*s(t,{status:`failed`,error:a,updatedAt:new Date}).pipe(m.catchAll(e=>m.gen(function*(){return yield*m.logError(`Failed to update job ${t}`,e),m.succeed(void 0)})));let c=yield*r.get(t);throw c&&(yield*n.emit(t,{jobId:t,eventType:O.FlowError,flowId:c.flowId,error:a}).pipe(m.catchAll(e=>m.gen(function*(){return yield*m.logError(`Failed to emit FlowError event for job ${t}`,e),m.succeed(void 0)})))),yield*l(t,o).pipe(m.catchAll(e=>m.gen(function*(){return yield*m.logWarning(`Failed to cleanup intermediate files for job ${t}`,e),m.succeed(void 0)}))),yield*u(t,i instanceof e?i:new e({code:`UNKNOWN_ERROR`,status:500,body:String(i),cause:i})),i})));return{getFlow:(e,n)=>m.gen(function*(){return yield*t.getFlow(e,n)}),getFlowData:(e,n)=>m.gen(function*(){return Te(yield*t.getFlow(e,n))}),runFlow:({flowId:n,storageId:i,clientId:a,inputs:o})=>m.gen(function*(){let s=yield*Q.optional,c=yield*m.try({try:()=>Ct.parse({inputs:o}),catch:t=>e.fromCode(`FLOW_INPUT_VALIDATION_ERROR`,{cause:t})}),l=crypto.randomUUID(),u=new Date,d={id:l,flowId:n,storageId:i,clientId:a,status:`started`,createdAt:u,updatedAt:u,tasks:[]};yield*r.set(l,d);let p=yield*t.getFlow(n,a);console.log(`[FlowServer] About to fork flow execution for job: ${l}`);let h=f({jobId:l,flow:p,storageId:i,clientId:a,inputs:c.inputs}).pipe(m.tapErrorCause(e=>m.logError(`Flow execution failed`,e)));if(g.isSome(s)){console.log(`[FlowServer] Using waitUntil for job: ${l}`);let e=yield*m.runtime(),t=_.runPromise(e)(h);s.value(t)}else console.log(`[FlowServer] Using Effect.forkDaemon for job: ${l}`),yield*m.forkDaemon(h);return console.log(`[FlowServer] Flow execution started for job: ${l}`),d}),getJobStatus:t=>m.gen(function*(){return(yield*r.get(t))||(yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${t} not found`})))}),resumeFlow:({jobId:i,nodeId:a,newData:o,clientId:c})=>m.gen(function*(){let d=yield*Q.optional,f=yield*r.get(i);if(!f)return console.error(`Job not found`),yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${i} not found`}));if(f.status!==`paused`)return console.error(`Job is not paused`),yield*m.fail(e.fromCode(`FLOW_JOB_ERROR`,{cause:`Job ${i} is not paused (status: ${f.status})`}));if(f.pausedAt!==a)return console.error(`Job is not paused at the expected node`),yield*m.fail(e.fromCode(`FLOW_JOB_ERROR`,{cause:`Job ${i} is paused at node ${f.pausedAt}, not ${a}`}));if(!f.executionState)return console.error(`Job has no execution state`),yield*m.fail(e.fromCode(`FLOW_JOB_ERROR`,{cause:`Job ${i} has no execution state`}));let p={...f.tasks.reduce((e,t)=>(t.result!==void 0&&(e[t.nodeId]=t.result),e),{}),[a]:o},h={...f.executionState.inputs,[a]:o};yield*s(i,{status:`running`});let v=yield*t.getFlow(f.flowId,f.clientId),y=m.gen(function*(){let t=Pe(v,n,r);if(!f.executionState)return yield*m.fail(e.fromCode(`FLOW_JOB_ERROR`,{cause:`Job ${i} has no execution state`}));let a=yield*t.resume({jobId:i,storageId:f.storageId,nodeResults:p,executionState:{...f.executionState,inputs:h},clientId:f.clientId});return a.type===`paused`?yield*s(i,{status:`paused`,pausedAt:a.nodeId,executionState:a.executionState,updatedAt:new Date}):(yield*s(i,{status:`completed`,pausedAt:void 0,executionState:void 0,updatedAt:new Date,endedAt:new Date}),yield*l(i,c)),a}).pipe(m.catchAll(t=>m.gen(function*(){yield*m.logError(`Flow resume failed`,t);let a=t instanceof e?t.body:String(t);yield*m.logInfo(`Updating job ${i} to failed status with error: ${a}`),yield*s(i,{status:`failed`,error:a,updatedAt:new Date}).pipe(m.catchAll(e=>m.gen(function*(){return yield*m.logError(`Failed to update job ${i}`,e),m.succeed(void 0)})));let o=yield*r.get(i);throw o&&(yield*n.emit(i,{jobId:i,eventType:O.FlowError,flowId:o.flowId,error:a}).pipe(m.catchAll(e=>m.gen(function*(){return yield*m.logError(`Failed to emit FlowError event for job ${i}`,e),m.succeed(void 0)})))),yield*l(i,c).pipe(m.catchAll(e=>m.gen(function*(){return yield*m.logWarning(`Failed to cleanup intermediate files for job ${i}`,e),m.succeed(void 0)}))),yield*u(i,t instanceof e?t:new e({code:`UNKNOWN_ERROR`,status:500,body:String(t),cause:t})),t}))).pipe(m.tapErrorCause(e=>m.logError(`Flow resume failed`,e)));if(g.isSome(d)){console.log(`[FlowServer] Using waitUntil for resume job: ${i}`);let e=yield*m.runtime(),t=_.runPromise(e)(y);d.value(t)}else console.log(`[FlowServer] Using Effect.forkDaemon for resume job: ${i}`),yield*m.forkDaemon(y);return(yield*r.get(i))||(yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${i} not found after update`})))}),pauseFlow:(t,i)=>m.gen(function*(){let a=yield*r.get(t);if(!a)return yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${t} not found`}));if(i!==null&&a.clientId!==i)return yield*m.fail(e.fromCode(`FLOW_NOT_AUTHORIZED`,{cause:`Client ${i} is not authorized to pause job ${t}`}));if(a.status!==`running`)return yield*m.fail(e.fromCode(`FLOW_JOB_ERROR`,{cause:`Job ${t} cannot be paused (current status: ${a.status})`}));let o=a.tasks.find(e=>e.status===`running`)?.nodeId;return yield*s(t,{status:`paused`,pausedAt:o,updatedAt:new Date}),yield*n.emit(t,{jobId:t,flowId:a.flowId,eventType:O.FlowPause,pausedAt:o}),(yield*r.get(t))||(yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${t} not found after pause`})))}),cancelFlow:(t,i)=>m.gen(function*(){let a=yield*r.get(t);return a?i!==null&&a.clientId!==i?yield*m.fail(e.fromCode(`FLOW_NOT_AUTHORIZED`,{cause:`Client ${i} is not authorized to cancel job ${t}`})):a.status!==`running`&&a.status!==`paused`&&a.status!==`started`?yield*m.fail(e.fromCode(`FLOW_JOB_ERROR`,{cause:`Job ${t} cannot be cancelled (current status: ${a.status})`})):(yield*s(t,{status:`cancelled`,updatedAt:new Date,endedAt:new Date}),yield*n.emit(t,{jobId:t,flowId:a.flowId,eventType:O.FlowCancel}),yield*l(t,i),(yield*r.get(t))||(yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${t} not found after cancellation`})))):yield*m.fail(e.fromCode(`FLOW_JOB_NOT_FOUND`,{cause:`Job ${t} not found`}))}),subscribeToFlowEvents:(e,t)=>m.gen(function*(){yield*n.subscribe(e,t)}),unsubscribeFromFlowEvents:e=>m.gen(function*(){yield*n.unsubscribe(e)})}})}const Ie=h.effect(je,Fe());function Le(e){let t=e.lastIndexOf(`.`);return t===-1||t===0?e:e.substring(0,t)}function Re(e){let t=e.lastIndexOf(`.`);return t===-1||t===0?``:e.substring(t+1)}function ze(e,t,n){let r=e.metadata??{},i=r.fileName??r.originalName??r.name??`unnamed`;return{baseName:Le(i),extension:Re(i),fileName:i,nodeType:t.nodeType,nodeId:t.nodeId,flowId:t.flowId,jobId:t.jobId,timestamp:new Date().toISOString(),...n}}function Be(e,t){try{let n={};for(let[e,r]of Object.entries(t))r!==void 0&&(n[e]=String(r));return b(e,n)}catch{return e}}function Ve(e,t,n){let r=t.fileName;if(!n)return r;try{if(n.mode===`auto`){if(n.autoSuffix){let e=n.autoSuffix(t);if(e){let{baseName:n,extension:r}=t;return r?`${n}-${e}.${r}`:`${n}-${e}`}}return r}if(n.mode===`custom`){if(n.rename)return n.rename(e,t)||r;if(n.pattern)return Be(n.pattern,t)||r}return r}catch{return r}}function He(e){if(!e||e.trim()===``)return{isValid:!1,error:`Pattern cannot be empty`};let t=(e.match(/\{\{/g)||[]).length,n=(e.match(/\}\}/g)||[]).length;if(t!==n)return{isValid:!1,error:`Unbalanced braces: ${t} opening, ${n} closing`};let r=e.match(/\{\{[^}]*[^a-zA-Z0-9_}][^}]*\}\}/g);return r?{isValid:!1,error:`Invalid variable syntax: ${r[0]}`}:{isValid:!0}}const Ue=[{name:`baseName`,description:`Filename without extension`,example:`photo`},{name:`extension`,description:`File extension without dot`,example:`jpg`},{name:`fileName`,description:`Full original filename`,example:`photo.jpg`},{name:`nodeType`,description:`Type of processing node`,example:`resize`},{name:`nodeId`,description:`Specific node instance ID`,example:`resize-1`},{name:`flowId`,description:`Flow identifier`,example:`flow-abc`},{name:`jobId`,description:`Execution job ID`,example:`job-123`},{name:`timestamp`,description:`ISO 8601 processing time`,example:`2024-01-15T10:30:00Z`},{name:`width`,description:`Output width (image/video)`,example:`800`},{name:`height`,description:`Output height (image/video)`,example:`600`},{name:`format`,description:`Output format`,example:`webp`},{name:`quality`,description:`Quality setting`,example:`80`},{name:`pageNumber`,description:`Page number (documents)`,example:`1`}];function We({id:e,name:n,description:r,outputTypeId:i,keepOutput:a,naming:o,nodeType:s=`transform`,nodeTypeId:c,namingVars:l,circuitBreaker:u,transform:f}){return m.gen(function*(){let p=yield*d;return yield*F({id:e,name:n,description:r,type:P.process,outputTypeId:i,keepOutput:a,nodeTypeId:c,circuitBreaker:u,inputSchema:t,outputSchema:t,run:({data:t,storageId:n,flowId:r,jobId:i,clientId:a})=>m.gen(function*(){let c={flowId:r,nodeId:e,jobId:i},u=yield*f(yield*p.read(t.id,a),t),d=u instanceof Uint8Array?u:u.bytes,m=u instanceof Uint8Array?void 0:u.type,h=u instanceof Uint8Array?void 0:u.fileName;!h&&o&&(h=Ve(t,ze(t,{flowId:r,jobId:i,nodeId:e,nodeType:s},l),o));let g=new ReadableStream({start(e){e.enqueue(d),e.close()}}),{type:_,fileName:v,metadata:y,metadataJson:b}=R(t.metadata),x=yield*p.upload({storageId:n,size:d.byteLength,type:m??_,fileName:h??v,lastModified:0,metadata:b,flow:c},a,g),S=y?{...y,...m&&{mimeType:m,type:m,"content-type":m},...h&&{fileName:h,originalName:h,name:h,extension:h.split(`.`).pop()||y.extension}}:x.metadata;return I(S?{...x,metadata:S}:x)})})})}var Ge=class extends p.Tag(`CredentialProvider`)(){},Ke=class extends p.Tag(`DocumentAiPlugin`)(){},qe=class extends p.Tag(`DocumentPlugin`)(){},Je=class extends p.Tag(`ImageAiPlugin`)(){},Ye=class extends p.Tag(`ImagePlugin`)(){};const Xe=y.object({serviceType:y.enum([`replicate`]).optional()}),Ze=y.object({duration:y.number().nonnegative(),width:y.number().positive(),height:y.number().positive(),codec:y.string(),format:y.string(),bitrate:y.number().nonnegative(),frameRate:y.number().positive(),aspectRatio:y.string(),hasAudio:y.boolean(),audioCodec:y.string().optional(),audioBitrate:y.number().nonnegative().optional(),size:y.number().nonnegative()}),Qe=y.object({timestamp:y.number().nonnegative(),format:y.enum([`png`,`jpeg`]).optional(),quality:y.number().min(1).max(100).optional()}),$e=y.object({quality:y.number().min(0).max(100),format:y.enum([`jpeg`,`webp`,`png`,`avif`])}),et=y.object({serviceType:y.enum([`replicate`]).optional()}),tt=y.object({width:y.number().positive().optional(),height:y.number().positive().optional(),fit:y.enum([`contain`,`cover`,`fill`])}).refine(e=>e.width||e.height,`Either width or height must be specified for resize`),nt=y.object({width:y.number().positive().optional(),height:y.number().positive().optional(),aspectRatio:y.enum([`keep`,`ignore`]).optional(),scaling:y.enum([`bicubic`,`bilinear`,`lanczos`]).optional()}).refine(e=>e.width||e.height,`Either width or height must be specified for video resize`),rt=y.object({format:y.enum([`mp4`,`webm`,`mov`,`avi`]),codec:y.enum([`h264`,`h265`,`vp9`,`av1`]).optional(),videoBitrate:y.string().optional(),audioBitrate:y.string().optional(),audioCodec:y.enum([`aac`,`mp3`,`opus`,`vorbis`]).optional()}),it=y.object({type:y.literal(`resize`),width:y.number().positive().optional(),height:y.number().positive().optional(),fit:y.enum([`contain`,`cover`,`fill`])}),$=y.object({type:y.literal(`blur`),sigma:y.number().min(.3).max(1e3)}),at=y.object({type:y.literal(`rotate`),angle:y.number(),background:y.string().optional()}),ot=y.object({type:y.literal(`flip`),direction:y.enum([`horizontal`,`vertical`])}),st=y.object({type:y.literal(`grayscale`)}),ct=y.object({type:y.literal(`sepia`)}),lt=y.object({type:y.literal(`brightness`),value:y.number().min(-100).max(100)}),ut=y.object({type:y.literal(`contrast`),value:y.number().min(-100).max(100)}),dt=y.object({type:y.literal(`sharpen`),sigma:y.number().positive().optional()}),ft=y.object({type:y.literal(`watermark`),imagePath:y.string().min(1).url(),position:y.enum([`top-left`,`top-right`,`bottom-left`,`bottom-right`,`center`]),opacity:y.number().min(0).max(1),offsetX:y.number().optional(),offsetY:y.number().optional()}),pt=y.object({type:y.literal(`logo`),imagePath:y.string().min(1).url(),position:y.enum([`top-left`,`top-right`,`bottom-left`,`bottom-right`,`center`]),scale:y.number().min(.1).max(2),offsetX:y.number().optional(),offsetY:y.number().optional()}),mt=y.object({type:y.literal(`text`),text:y.string().min(1),position:y.enum([`top-left`,`top-right`,`bottom-left`,`bottom-right`,`center`]),fontSize:y.number().positive(),color:y.string().min(1),fontFamily:y.string().optional(),offsetX:y.number().optional(),offsetY:y.number().optional()}),ht=y.discriminatedUnion(`type`,[it,$,at,ot,st,ct,lt,ut,dt,ft,pt,mt]),gt=y.object({transformations:y.array(ht).min(1)}),_t=y.object({startTime:y.number().nonnegative(),endTime:y.number().positive().optional(),duration:y.number().positive().optional()}).refine(e=>!e.endTime||!e.duration,`Cannot specify both endTime and duration`).refine(e=>!e.endTime||e.endTime>e.startTime,`endTime must be greater than startTime`);var vt=class extends p.Tag(`VideoPlugin`)(){},yt=class extends p.Tag(`VirusScanPlugin`)(){},bt=class extends p.Tag(`ZipPlugin`)(){};const xt=(e,t)=>{if(e.length===0)return t;let[n,...r]=e;return r.reduce((e,t)=>y.union([e,t]),n)};function St(t){return m.gen(function*(){let n=Object.entries(t.nodes),r=e=>m.isEffect(e)?e:m.succeed(e),i=yield*m.forEach(n,([t,n])=>m.flatMap(r(n),n=>n.id===t?m.succeed([t,n]):m.fail(e.fromCode(`FLOW_NODE_ERROR`,{cause:Error(`Node key ${t} does not match node id ${n.id}`)})))),a=Object.fromEntries(i),o=i.map(([,e])=>e),s=i.filter(([,e])=>e.type===P.input).map(([,e])=>e.inputSchema),c=t.edges.map(e=>({source:a[e.source]?.id??e.source,target:a[e.target]?.id??e.target,sourcePort:e.sourcePort,targetPort:e.targetPort})),l=new Set(i.map(([e])=>a[e]?.id).filter(e=>e&&!c.some(t=>t.source===e))),u=i.filter(([,e])=>l.has(e.id)).map(([,e])=>e.outputSchema),d=t.inputSchema??xt(s,y.unknown()),f=t.outputSchema??xt(u,y.unknown());return yield*J({flowId:t.flowId,name:t.name,nodes:o,edges:c,inputSchema:d,outputSchema:f,typeChecker:t.typeChecker,onEvent:t.onEvent,parallelExecution:t.parallelExecution,hooks:t.hooks,circuitBreaker:t.circuitBreaker})})}const Ct=y.object({inputs:y.record(y.string(),y.any())});export{Ee as $,Je as A,F as At,Be as B,D as Bt,tt as C,de as Ct,Ze as D,I as Dt,Qe as E,R as Et,Ue as F,k as Ft,Fe as G,E as Gt,Ae as H,T as Ht,Ve as I,A as It,Oe as J,Ie as K,x as Kt,ze as L,j as Lt,Ke as M,M as Mt,Ge as N,N as Nt,Xe as O,L as Ot,We as P,ne as Pt,De as Q,Le as R,O as Rt,nt as S,W as St,$e as T,le as Tt,je as U,C as Ut,He as V,ee as Vt,Q as W,w as Wt,Y as X,ke as Y,X as Z,mt as _,H as _t,vt as a,ye as at,ft as b,U as bt,lt as c,Se as ct,st as d,he as dt,J as et,pt as f,me as ft,dt as g,fe as gt,ct as h,Ce as ht,yt as i,ve as it,qe as j,re as jt,Ye as k,P as kt,ut as l,ge as lt,at as m,we as mt,St as n,K as nt,_t as o,_e as ot,it as p,pe as pt,Z as q,bt as r,q as rt,$ as s,be as st,Ct as t,Te as tt,ot as u,xe as ut,gt as v,V as vt,et as w,z as wt,rt as x,G as xt,ht as y,B as yt,Re as z,te as zt};
2
- //# sourceMappingURL=flow-BLGpxdEm.mjs.map