@uploadista/flow-images-nodes 0.0.17 → 0.0.18-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- let e=require(`@uploadista/core/errors`),t=require(`@uploadista/core/flow`),n=require(`@uploadista/core/types`),r=require(`effect`),i=require(`@uploadista/core/upload`);function a(t,n={}){let{maxWaitTime:i=1e4,retryDelay:a=500}=n;return r.Effect.gen(function*(){let n=Date.now();for(;Date.now()-n<i;){let e=yield*r.Effect.tryPromise(()=>fetch(t,{method:`HEAD`})).pipe(r.Effect.catchAll(()=>r.Effect.succeed(null)));if(e?.ok){yield*r.Effect.logInfo(`URL ${t} is now available`);return}e?yield*r.Effect.logDebug(`URL not ready yet (${e.status}), retrying...`):yield*r.Effect.logDebug(`URL check failed, retrying...`),yield*r.Effect.sleep(a)}return yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:`URL ${t} not available after ${i}ms`}).toEffect()})}function o(i,{credentialId:o,keepOutput:s}={}){return r.Effect.gen(function*(){let c=yield*t.ImageAiPlugin;return yield*(0,t.createFlowNode)({id:i,name:`Describe Image`,description:`Describes the image using AI`,type:t.NodeType.process,nodeTypeId:t.IMAGE_DESCRIPTION_OUTPUT_TYPE_ID,keepOutput:s,inputSchema:n.uploadFileSchema,outputSchema:t.imageDescriptionOutputSchema,run:({data:n,flowId:s,jobId:l,clientId:u})=>r.Effect.gen(function*(){let d={flowId:s,nodeId:i,jobId:l},f=n.url;if(!f)return yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:`URL is required for describe image operation`}).toEffect();yield*r.Effect.logInfo(`Describing image for file ${n.id} at URL: ${f}`),yield*a(f);let p={clientId:u,credentialId:o},m=yield*c.describeImage(f,p).pipe(r.Effect.catchAll(t=>r.Effect.gen(function*(){return yield*r.Effect.logError(`Failed to describe image`,t),yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to describe image`}).toEffect()})));return yield*r.Effect.logInfo(`Successfully described image for file ${n.id}`),(0,t.completeNodeExecution)({description:m.description,flow:d})})})})}const s={jpeg:`image/jpeg`,webp:`image/webp`,png:`image/png`,avif:`image/avif`},c={jpeg:`jpg`,webp:`webp`,png:`png`,avif:`avif`};function l(e,{quality:n,format:i},a){return r.Effect.gen(function*(){let o=yield*t.ImagePlugin;return yield*(0,t.createTransformNode)({id:e,name:`Optimize`,description:`Optimizes an image for web delivery`,nodeTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:a?.keepOutput,transform:(e,t)=>r.Effect.map(o.optimize(e,{quality:n,format:i}),e=>{let n=s[i],r=c[i],a=t.metadata?.fileName;return{bytes:e,type:n,fileName:a&&typeof a==`string`?a.replace(/\.[^.]+$/,`.${r}`):void 0}})})})}function u(o,{credentialId:s,keepOutput:c}={}){return r.Effect.gen(function*(){let l=yield*t.ImageAiPlugin,u=yield*i.UploadServer;return yield*(0,t.createFlowNode)({id:o,name:`Remove Background`,description:`Removes the background from an image`,type:t.NodeType.process,nodeTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:c,inputSchema:n.uploadFileSchema,outputSchema:n.uploadFileSchema,run:({data:n,flowId:i,jobId:c,storageId:d,clientId:f})=>r.Effect.gen(function*(){let p={flowId:i,nodeId:o,jobId:c},m=n.url;if(!m)return yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:`URL is required for remove background operation`}).toEffect();yield*r.Effect.logInfo(`Removing background for file ${n.id} at URL: ${n.url}`),yield*a(m);let h={clientId:f,credentialId:s},{outputUrl:g}=yield*l.removeBackground(m,h).pipe(r.Effect.catchAll(t=>r.Effect.gen(function*(){return yield*r.Effect.logError(`Failed to remove background`,t),yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to remove background from image`}).toEffect()}))),{type:_,fileName:v,metadata:y,metadataJson:b}=(0,t.resolveUploadMetadata)(n.metadata);yield*r.Effect.logInfo(`Uploading processed file to storage`);let x=yield*u.uploadFromUrl({storageId:d,size:0,type:_,fileName:v,lastModified:0,metadata:b,flow:p},f,g).pipe(r.Effect.catchAll(t=>r.Effect.gen(function*(){return yield*r.Effect.logError(`Failed to upload processed file`,t),yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to upload processed file`}).toEffect()})));return yield*r.Effect.logInfo(`Successfully removed background for file ${n.id}`),(0,t.completeNodeExecution)(y?{...x,metadata:y}:x)})})})}function d(e,{width:n,height:i,fit:a},o){return r.Effect.gen(function*(){let r=yield*t.ImagePlugin;return yield*(0,t.createTransformNode)({id:e,name:`Resize`,description:`Resizes an image to the specified dimensions`,nodeTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:o?.keepOutput,transform:e=>r.resize(e,{height:i,width:n,fit:a})})})}function f(e,t,n){return r.Effect.reduce(n,t,(t,n)=>e.transform(t,n))}function p(e,{transformations:n},i){return r.Effect.gen(function*(){let r=yield*t.ImagePlugin;return yield*(0,t.createTransformNode)({id:e,name:`Transform Image`,description:`Apply ${n.length} transformation${n.length===1?``:`s`} to the image`,nodeTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:i?.keepOutput,transform:e=>f(r,e,n)})})}exports.createDescribeImageNode=o,exports.createOptimizeNode=l,exports.createRemoveBackgroundNode=u,exports.createResizeNode=d,exports.createTransformImageNode=p,exports.waitForUrlAvailability=a;
1
+ let e=require(`@uploadista/core/errors`),t=require(`@uploadista/core/flow`),n=require(`@uploadista/core/types`),r=require(`effect`),i=require(`@uploadista/core/upload`);function a(t,n={}){let{maxWaitTime:i=1e4,retryDelay:a=500}=n;return r.Effect.gen(function*(){let n=Date.now();for(;Date.now()-n<i;){let e=yield*r.Effect.tryPromise(()=>fetch(t,{method:`HEAD`})).pipe(r.Effect.catchAll(()=>r.Effect.succeed(null)));if(e?.ok){yield*r.Effect.logInfo(`URL ${t} is now available`);return}e?yield*r.Effect.logDebug(`URL not ready yet (${e.status}), retrying...`):yield*r.Effect.logDebug(`URL check failed, retrying...`),yield*r.Effect.sleep(a)}return yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:`URL ${t} not available after ${i}ms`}).toEffect()})}function o(i,{credentialId:o,keepOutput:s}={}){return r.Effect.gen(function*(){let c=yield*t.ImageAiPlugin;return yield*(0,t.createFlowNode)({id:i,name:`Describe Image`,description:`Describes the image using AI`,type:t.NodeType.process,nodeTypeId:`describe-image`,outputTypeId:t.IMAGE_DESCRIPTION_OUTPUT_TYPE_ID,keepOutput:s,inputSchema:n.uploadFileSchema,outputSchema:t.imageDescriptionOutputSchema,circuitBreaker:{enabled:!0,failureThreshold:5,resetTimeout:6e4,fallback:{type:`skip`,passThrough:!0}},run:({data:n,flowId:s,jobId:l,clientId:u})=>r.Effect.gen(function*(){let d={flowId:s,nodeId:i,jobId:l},f=n.url;if(!f)return yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:`URL is required for describe image operation`}).toEffect();yield*r.Effect.logInfo(`Describing image for file ${n.id} at URL: ${f}`),yield*a(f);let p={clientId:u,credentialId:o},m=yield*c.describeImage(f,p).pipe(r.Effect.catchAll(t=>r.Effect.gen(function*(){return yield*r.Effect.logError(`Failed to describe image`,t),yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to describe image`}).toEffect()})));return yield*r.Effect.logInfo(`Successfully described image for file ${n.id}`),(0,t.completeNodeExecution)({description:m.description,flow:d})})})})}const s={jpeg:`image/jpeg`,webp:`image/webp`,png:`image/png`,avif:`image/avif`},c={jpeg:`jpg`,webp:`webp`,png:`png`,avif:`avif`};function l(e,{quality:n,format:i},a){return r.Effect.gen(function*(){let o=yield*t.ImagePlugin;return yield*(0,t.createTransformNode)({id:e,name:`Optimize`,description:`Optimizes an image for web delivery`,nodeTypeId:`optimize-image`,outputTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:a?.keepOutput,nodeType:`optimize`,namingVars:{format:i,quality:n},transform:(l,u)=>r.Effect.map(o.optimize(l,{quality:n,format:i}),r=>{let o=s[i],l=c[i],d=u.metadata?.fileName,f;if(d&&typeof d==`string`)if(a?.naming){let r={...a.naming,autoSuffix:a.naming.autoSuffix??(e=>e.format??i)};f=`${(0,t.getBaseName)((0,t.applyFileNaming)(u,(0,t.buildNamingContext)(u,{flowId:u.flow?.flowId??``,jobId:u.flow?.jobId??``,nodeId:e,nodeType:`optimize`},{format:i,quality:n}),r))}.${l}`}else f=d.replace(/\.[^.]+$/,`.${l}`);return{bytes:r,type:o,fileName:f}})})})}function u(o,{credentialId:s,keepOutput:c,naming:l}={}){return r.Effect.gen(function*(){let u=yield*t.ImageAiPlugin,d=yield*i.UploadServer;return yield*(0,t.createFlowNode)({id:o,name:`Remove Background`,description:`Removes the background from an image`,type:t.NodeType.process,nodeTypeId:`remove-background`,outputTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:c,inputSchema:n.uploadFileSchema,outputSchema:n.uploadFileSchema,circuitBreaker:{enabled:!0,failureThreshold:5,resetTimeout:6e4,fallback:{type:`skip`,passThrough:!0}},run:({data:n,flowId:i,jobId:c,storageId:f,clientId:p})=>r.Effect.gen(function*(){let m={flowId:i,nodeId:o,jobId:c},h=n.url;if(!h)return yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:`URL is required for remove background operation`}).toEffect();yield*r.Effect.logInfo(`Removing background for file ${n.id} at URL: ${n.url}`),yield*a(h);let g={clientId:p,credentialId:s},{outputUrl:_}=yield*u.removeBackground(h,g).pipe(r.Effect.catchAll(t=>r.Effect.gen(function*(){return yield*r.Effect.logError(`Failed to remove background`,t),yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to remove background from image`}).toEffect()}))),{type:v,fileName:y,metadata:b,metadataJson:x}=(0,t.resolveUploadMetadata)(n.metadata),S=y;if(l){let e={...l,autoSuffix:l.autoSuffix??(()=>`nobg`)};S=(0,t.applyFileNaming)(n,(0,t.buildNamingContext)(n,{flowId:i,jobId:c,nodeId:o,nodeType:`remove-background`}),e)}yield*r.Effect.logInfo(`Uploading processed file to storage`);let C=yield*d.uploadFromUrl({storageId:f,size:0,type:v,fileName:S,lastModified:0,metadata:x,flow:m},p,_).pipe(r.Effect.catchAll(t=>r.Effect.gen(function*(){return yield*r.Effect.logError(`Failed to upload processed file`,t),yield*e.UploadistaError.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to upload processed file`}).toEffect()})));yield*r.Effect.logInfo(`Successfully removed background for file ${n.id}`);let w=b?{...b,...S!==y&&{fileName:S,originalName:S,name:S,extension:S.split(`.`).pop()||b.extension}}:C.metadata;return(0,t.completeNodeExecution)(w?{...C,metadata:w}:C)})})})}function d(e,{width:n,height:i,fit:a},o){return r.Effect.gen(function*(){let r=yield*t.ImagePlugin,s=o?.naming?{...o.naming,autoSuffix:o.naming.autoSuffix??(e=>`${e.width??n}x${e.height??i}`)}:void 0;return yield*(0,t.createTransformNode)({id:e,name:`Resize`,description:`Resizes an image to the specified dimensions`,nodeTypeId:`resize-image`,outputTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:o?.keepOutput,naming:s,nodeType:`resize`,namingVars:{width:n,height:i},transform:e=>r.resize(e,{height:i,width:n,fit:a})})})}function f(e,t,n){return r.Effect.reduce(n,t,(t,n)=>e.transform(t,n))}function p(e,{transformations:n},i){return r.Effect.gen(function*(){let r=yield*t.ImagePlugin,a=i?.naming?{...i.naming,autoSuffix:i.naming.autoSuffix??(()=>`transformed`)}:void 0;return yield*(0,t.createTransformNode)({id:e,name:`Transform Image`,description:`Apply ${n.length} transformation${n.length===1?``:`s`} to the image`,nodeTypeId:`transform-image`,outputTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:i?.keepOutput,naming:a,nodeType:`transform-image`,transform:e=>f(r,e,n)})})}exports.createDescribeImageNode=o,exports.createOptimizeNode=l,exports.createRemoveBackgroundNode=u,exports.createResizeNode=d,exports.createTransformImageNode=p,exports.waitForUrlAvailability=a;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import * as _uploadista_core_flow10 from "@uploadista/core/flow";
2
- import { ImageAiPlugin, ImagePlugin, NodeType, OptimizeParams, ResizeParams, TransformImageParams } from "@uploadista/core/flow";
3
- import * as zod_v4_core7 from "zod/v4/core";
4
- import * as zod7 from "zod";
1
+ import * as _uploadista_core_flow0 from "@uploadista/core/flow";
2
+ import { FileNamingConfig, ImageAiPlugin, ImagePlugin, NodeType, OptimizeParams, ResizeParams, TransformImageParams } from "@uploadista/core/flow";
3
+ import * as zod_v4_core0 from "zod/v4/core";
4
+ import * as zod0 from "zod";
5
5
  import * as _uploadista_core_errors0 from "@uploadista/core/errors";
6
6
  import { UploadistaError } from "@uploadista/core/errors";
7
7
  import { Effect } from "effect";
@@ -16,8 +16,8 @@ declare function createDescribeImageNode(id: string, {
16
16
  }?: {
17
17
  credentialId?: string;
18
18
  keepOutput?: boolean;
19
- }): Effect.Effect<_uploadista_core_flow10.FlowNodeData & {
20
- inputSchema: zod7.ZodType<{
19
+ }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
20
+ inputSchema: zod0.ZodType<{
21
21
  id: string;
22
22
  offset: number;
23
23
  storage: {
@@ -44,7 +44,7 @@ declare function createDescribeImageNode(id: string, {
44
44
  nodeId: string;
45
45
  jobId: string;
46
46
  } | undefined;
47
- }, unknown, zod_v4_core7.$ZodTypeInternals<{
47
+ }, unknown, zod_v4_core0.$ZodTypeInternals<{
48
48
  id: string;
49
49
  offset: number;
50
50
  storage: {
@@ -72,11 +72,11 @@ declare function createDescribeImageNode(id: string, {
72
72
  jobId: string;
73
73
  } | undefined;
74
74
  }, unknown>>;
75
- outputSchema: zod7.ZodType<{
75
+ outputSchema: zod0.ZodType<{
76
76
  description: string;
77
77
  confidence?: number | undefined;
78
78
  metadata?: Record<string, unknown> | undefined;
79
- }, unknown, zod_v4_core7.$ZodTypeInternals<{
79
+ }, unknown, zod_v4_core0.$ZodTypeInternals<{
80
80
  description: string;
81
81
  confidence?: number | undefined;
82
82
  metadata?: Record<string, unknown> | undefined;
@@ -115,7 +115,7 @@ declare function createDescribeImageNode(id: string, {
115
115
  flowId: string;
116
116
  inputs?: Record<string, unknown>;
117
117
  clientId: string | null;
118
- }) => Effect.Effect<_uploadista_core_flow10.NodeExecutionResult<{
118
+ }) => Effect.Effect<_uploadista_core_flow0.NodeExecutionResult<{
119
119
  description: string;
120
120
  confidence?: number | undefined;
121
121
  metadata?: Record<string, unknown> | undefined;
@@ -133,19 +133,38 @@ declare function createDescribeImageNode(id: string, {
133
133
  retryDelay?: number;
134
134
  exponentialBackoff?: boolean;
135
135
  };
136
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
136
137
  } & {
137
138
  type: NodeType.process;
138
139
  }, UploadistaError, ImageAiPlugin>;
139
140
  //#endregion
140
141
  //#region src/optimize-node.d.ts
142
+ /**
143
+ * Creates an optimize node that optimizes images for web delivery.
144
+ *
145
+ * @param id - Unique node identifier
146
+ * @param params - Optimize parameters (quality, format)
147
+ * @param options - Optional configuration
148
+ * @param options.keepOutput - Whether to keep output in flow results
149
+ * @param options.naming - File naming configuration (auto suffix: `${format}`)
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * // With auto-naming: "photo.jpg" -> "photo-webp.webp"
154
+ * const optimize = yield* createOptimizeNode("opt-1", { quality: 80, format: "webp" }, {
155
+ * naming: { mode: "auto" }
156
+ * });
157
+ * ```
158
+ */
141
159
  declare function createOptimizeNode(id: string, {
142
160
  quality,
143
161
  format
144
162
  }: OptimizeParams, options?: {
145
163
  keepOutput?: boolean;
146
- }): Effect.Effect<_uploadista_core_flow10.FlowNodeData & {
147
- inputSchema: zod7.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core7.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
148
- outputSchema: zod7.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core7.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
164
+ naming?: FileNamingConfig;
165
+ }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
166
+ inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
167
+ outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
149
168
  run: (args: {
150
169
  data: _uploadista_core_types0.UploadFile;
151
170
  jobId: string;
@@ -153,7 +172,7 @@ declare function createOptimizeNode(id: string, {
153
172
  flowId: string;
154
173
  inputs?: Record<string, unknown>;
155
174
  clientId: string | null;
156
- }) => Effect.Effect<_uploadista_core_flow10.NodeExecutionResult<_uploadista_core_types0.UploadFile>, _uploadista_core_errors0.UploadistaError, never>;
175
+ }) => Effect.Effect<_uploadista_core_flow0.NodeExecutionResult<_uploadista_core_types0.UploadFile>, _uploadista_core_errors0.UploadistaError, never>;
157
176
  condition?: {
158
177
  field: string;
159
178
  operator: string;
@@ -167,19 +186,39 @@ declare function createOptimizeNode(id: string, {
167
186
  retryDelay?: number;
168
187
  exponentialBackoff?: boolean;
169
188
  };
189
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
170
190
  } & {
171
- type: _uploadista_core_flow10.NodeType;
191
+ type: _uploadista_core_flow0.NodeType;
172
192
  }, _uploadista_core_errors0.UploadistaError, ImagePlugin | _uploadista_core_upload0.UploadServer>;
173
193
  //#endregion
174
194
  //#region src/remove-background-node.d.ts
195
+ /**
196
+ * Creates a remove-background node that removes backgrounds from images using AI.
197
+ *
198
+ * @param id - Unique node identifier
199
+ * @param options - Optional configuration
200
+ * @param options.credentialId - Optional credential ID for AI service
201
+ * @param options.keepOutput - Whether to keep output in flow results
202
+ * @param options.naming - File naming configuration (auto suffix: `nobg`)
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * // With auto-naming: "photo.jpg" -> "photo-nobg.jpg"
207
+ * const node = yield* createRemoveBackgroundNode("remove-bg-1", {
208
+ * naming: { mode: "auto" }
209
+ * });
210
+ * ```
211
+ */
175
212
  declare function createRemoveBackgroundNode(id: string, {
176
213
  credentialId,
177
- keepOutput
214
+ keepOutput,
215
+ naming
178
216
  }?: {
179
217
  credentialId?: string;
180
218
  keepOutput?: boolean;
181
- }): Effect.Effect<_uploadista_core_flow10.FlowNodeData & {
182
- inputSchema: zod7.ZodType<{
219
+ naming?: FileNamingConfig;
220
+ }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
221
+ inputSchema: zod0.ZodType<{
183
222
  id: string;
184
223
  offset: number;
185
224
  storage: {
@@ -206,7 +245,7 @@ declare function createRemoveBackgroundNode(id: string, {
206
245
  nodeId: string;
207
246
  jobId: string;
208
247
  } | undefined;
209
- }, unknown, zod_v4_core7.$ZodTypeInternals<{
248
+ }, unknown, zod_v4_core0.$ZodTypeInternals<{
210
249
  id: string;
211
250
  offset: number;
212
251
  storage: {
@@ -234,7 +273,7 @@ declare function createRemoveBackgroundNode(id: string, {
234
273
  jobId: string;
235
274
  } | undefined;
236
275
  }, unknown>>;
237
- outputSchema: zod7.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core7.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
276
+ outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
238
277
  run: (args: {
239
278
  data: {
240
279
  id: string;
@@ -269,7 +308,7 @@ declare function createRemoveBackgroundNode(id: string, {
269
308
  flowId: string;
270
309
  inputs?: Record<string, unknown>;
271
310
  clientId: string | null;
272
- }) => Effect.Effect<_uploadista_core_flow10.NodeExecutionResult<_uploadista_core_types0.UploadFile>, UploadistaError, never>;
311
+ }) => Effect.Effect<_uploadista_core_flow0.NodeExecutionResult<_uploadista_core_types0.UploadFile>, UploadistaError, never>;
273
312
  condition?: {
274
313
  field: string;
275
314
  operator: string;
@@ -283,20 +322,39 @@ declare function createRemoveBackgroundNode(id: string, {
283
322
  retryDelay?: number;
284
323
  exponentialBackoff?: boolean;
285
324
  };
325
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
286
326
  } & {
287
327
  type: NodeType.process;
288
328
  }, UploadistaError, ImageAiPlugin | UploadServer>;
289
329
  //#endregion
290
330
  //#region src/resize-node.d.ts
331
+ /**
332
+ * Creates a resize node that resizes images to specified dimensions.
333
+ *
334
+ * @param id - Unique node identifier
335
+ * @param params - Resize parameters (width, height, fit)
336
+ * @param options - Optional configuration
337
+ * @param options.keepOutput - Whether to keep output in flow results
338
+ * @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)
339
+ *
340
+ * @example
341
+ * ```typescript
342
+ * // With auto-naming: "photo.jpg" -> "photo-800x600.jpg"
343
+ * const resize = yield* createResizeNode("resize-1", { width: 800, height: 600 }, {
344
+ * naming: { mode: "auto" }
345
+ * });
346
+ * ```
347
+ */
291
348
  declare function createResizeNode(id: string, {
292
349
  width,
293
350
  height,
294
351
  fit
295
352
  }: ResizeParams, options?: {
296
353
  keepOutput?: boolean;
297
- }): Effect.Effect<_uploadista_core_flow10.FlowNodeData & {
298
- inputSchema: zod7.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core7.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
299
- outputSchema: zod7.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core7.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
354
+ naming?: FileNamingConfig;
355
+ }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
356
+ inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
357
+ outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
300
358
  run: (args: {
301
359
  data: _uploadista_core_types0.UploadFile;
302
360
  jobId: string;
@@ -304,7 +362,7 @@ declare function createResizeNode(id: string, {
304
362
  flowId: string;
305
363
  inputs?: Record<string, unknown>;
306
364
  clientId: string | null;
307
- }) => Effect.Effect<_uploadista_core_flow10.NodeExecutionResult<_uploadista_core_types0.UploadFile>, _uploadista_core_errors0.UploadistaError, never>;
365
+ }) => Effect.Effect<_uploadista_core_flow0.NodeExecutionResult<_uploadista_core_types0.UploadFile>, _uploadista_core_errors0.UploadistaError, never>;
308
366
  condition?: {
309
367
  field: string;
310
368
  operator: string;
@@ -318,8 +376,9 @@ declare function createResizeNode(id: string, {
318
376
  retryDelay?: number;
319
377
  exponentialBackoff?: boolean;
320
378
  };
379
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
321
380
  } & {
322
- type: _uploadista_core_flow10.NodeType;
381
+ type: _uploadista_core_flow0.NodeType;
323
382
  }, _uploadista_core_errors0.UploadistaError, ImagePlugin | _uploadista_core_upload0.UploadServer>;
324
383
  //#endregion
325
384
  //#region src/transform-image-node.d.ts
@@ -341,21 +400,20 @@ declare function createResizeNode(id: string, {
341
400
  *
342
401
  * @param id - Unique identifier for this node
343
402
  * @param params - Parameters including the transformations array
344
- * @returns Effect that resolves to a transform node
403
+ * @param options - Optional configuration
404
+ * @param options.keepOutput - Whether to keep output in flow results
405
+ * @param options.naming - File naming configuration (auto suffix: `transformed`)
345
406
  *
346
407
  * @example
347
408
  * ```typescript
348
- * const node = createTransformImageNode("transform-1", {
409
+ * // With auto-naming: "photo.jpg" -> "photo-transformed.jpg"
410
+ * const node = yield* createTransformImageNode("transform-1", {
349
411
  * transformations: [
350
412
  * { type: 'resize', width: 800, height: 600, fit: 'cover' },
351
- * { type: 'brightness', value: 20 },
352
- * {
353
- * type: 'watermark',
354
- * imagePath: 'https://cdn.example.com/watermark.png',
355
- * position: 'bottom-right',
356
- * opacity: 0.5
357
- * }
413
+ * { type: 'brightness', value: 20 }
358
414
  * ]
415
+ * }, {
416
+ * naming: { mode: "auto" }
359
417
  * });
360
418
  * ```
361
419
  */
@@ -363,9 +421,10 @@ declare function createTransformImageNode(id: string, {
363
421
  transformations
364
422
  }: TransformImageParams, options?: {
365
423
  keepOutput?: boolean;
366
- }): Effect.Effect<_uploadista_core_flow10.FlowNodeData & {
367
- inputSchema: zod7.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core7.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
368
- outputSchema: zod7.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core7.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
424
+ naming?: FileNamingConfig;
425
+ }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
426
+ inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
427
+ outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
369
428
  run: (args: {
370
429
  data: _uploadista_core_types0.UploadFile;
371
430
  jobId: string;
@@ -373,7 +432,7 @@ declare function createTransformImageNode(id: string, {
373
432
  flowId: string;
374
433
  inputs?: Record<string, unknown>;
375
434
  clientId: string | null;
376
- }) => Effect.Effect<_uploadista_core_flow10.NodeExecutionResult<_uploadista_core_types0.UploadFile>, _uploadista_core_errors0.UploadistaError, never>;
435
+ }) => Effect.Effect<_uploadista_core_flow0.NodeExecutionResult<_uploadista_core_types0.UploadFile>, _uploadista_core_errors0.UploadistaError, never>;
377
436
  condition?: {
378
437
  field: string;
379
438
  operator: string;
@@ -387,8 +446,9 @@ declare function createTransformImageNode(id: string, {
387
446
  retryDelay?: number;
388
447
  exponentialBackoff?: boolean;
389
448
  };
449
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
390
450
  } & {
391
- type: _uploadista_core_flow10.NodeType;
451
+ type: _uploadista_core_flow0.NodeType;
392
452
  }, _uploadista_core_errors0.UploadistaError, ImagePlugin | _uploadista_core_upload0.UploadServer>;
393
453
  //#endregion
394
454
  //#region src/wait-for-url.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/describe-image-node.ts","../src/optimize-node.ts","../src/remove-background-node.ts","../src/resize-node.ts","../src/transform-image-node.ts","../src/wait-for-url.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/describe-image-node.ts","../src/optimize-node.ts","../src/remove-background-node.ts","../src/resize-node.ts","../src/transform-image-node.ts","../src/wait-for-url.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _uploadista_core_errors0 from "@uploadista/core/errors";
2
2
  import { UploadistaError } from "@uploadista/core/errors";
3
3
  import * as _uploadista_core_flow0 from "@uploadista/core/flow";
4
- import { ImageAiPlugin, ImagePlugin, NodeType, OptimizeParams, ResizeParams, TransformImageParams } from "@uploadista/core/flow";
4
+ import { FileNamingConfig, ImageAiPlugin, ImagePlugin, NodeType, OptimizeParams, ResizeParams, TransformImageParams } from "@uploadista/core/flow";
5
5
  import * as _uploadista_core_types0 from "@uploadista/core/types";
6
6
  import { Effect } from "effect";
7
7
  import * as _uploadista_core_upload0 from "@uploadista/core/upload";
@@ -133,16 +133,35 @@ declare function createDescribeImageNode(id: string, {
133
133
  retryDelay?: number;
134
134
  exponentialBackoff?: boolean;
135
135
  };
136
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
136
137
  } & {
137
138
  type: NodeType.process;
138
139
  }, UploadistaError, ImageAiPlugin>;
139
140
  //#endregion
140
141
  //#region src/optimize-node.d.ts
142
+ /**
143
+ * Creates an optimize node that optimizes images for web delivery.
144
+ *
145
+ * @param id - Unique node identifier
146
+ * @param params - Optimize parameters (quality, format)
147
+ * @param options - Optional configuration
148
+ * @param options.keepOutput - Whether to keep output in flow results
149
+ * @param options.naming - File naming configuration (auto suffix: `${format}`)
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * // With auto-naming: "photo.jpg" -> "photo-webp.webp"
154
+ * const optimize = yield* createOptimizeNode("opt-1", { quality: 80, format: "webp" }, {
155
+ * naming: { mode: "auto" }
156
+ * });
157
+ * ```
158
+ */
141
159
  declare function createOptimizeNode(id: string, {
142
160
  quality,
143
161
  format
144
162
  }: OptimizeParams, options?: {
145
163
  keepOutput?: boolean;
164
+ naming?: FileNamingConfig;
146
165
  }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
147
166
  inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
148
167
  outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
@@ -167,17 +186,37 @@ declare function createOptimizeNode(id: string, {
167
186
  retryDelay?: number;
168
187
  exponentialBackoff?: boolean;
169
188
  };
189
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
170
190
  } & {
171
191
  type: _uploadista_core_flow0.NodeType;
172
192
  }, _uploadista_core_errors0.UploadistaError, ImagePlugin | _uploadista_core_upload0.UploadServer>;
173
193
  //#endregion
174
194
  //#region src/remove-background-node.d.ts
195
+ /**
196
+ * Creates a remove-background node that removes backgrounds from images using AI.
197
+ *
198
+ * @param id - Unique node identifier
199
+ * @param options - Optional configuration
200
+ * @param options.credentialId - Optional credential ID for AI service
201
+ * @param options.keepOutput - Whether to keep output in flow results
202
+ * @param options.naming - File naming configuration (auto suffix: `nobg`)
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * // With auto-naming: "photo.jpg" -> "photo-nobg.jpg"
207
+ * const node = yield* createRemoveBackgroundNode("remove-bg-1", {
208
+ * naming: { mode: "auto" }
209
+ * });
210
+ * ```
211
+ */
175
212
  declare function createRemoveBackgroundNode(id: string, {
176
213
  credentialId,
177
- keepOutput
214
+ keepOutput,
215
+ naming
178
216
  }?: {
179
217
  credentialId?: string;
180
218
  keepOutput?: boolean;
219
+ naming?: FileNamingConfig;
181
220
  }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
182
221
  inputSchema: zod0.ZodType<{
183
222
  id: string;
@@ -283,17 +322,36 @@ declare function createRemoveBackgroundNode(id: string, {
283
322
  retryDelay?: number;
284
323
  exponentialBackoff?: boolean;
285
324
  };
325
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
286
326
  } & {
287
327
  type: NodeType.process;
288
328
  }, UploadistaError, ImageAiPlugin | UploadServer>;
289
329
  //#endregion
290
330
  //#region src/resize-node.d.ts
331
+ /**
332
+ * Creates a resize node that resizes images to specified dimensions.
333
+ *
334
+ * @param id - Unique node identifier
335
+ * @param params - Resize parameters (width, height, fit)
336
+ * @param options - Optional configuration
337
+ * @param options.keepOutput - Whether to keep output in flow results
338
+ * @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)
339
+ *
340
+ * @example
341
+ * ```typescript
342
+ * // With auto-naming: "photo.jpg" -> "photo-800x600.jpg"
343
+ * const resize = yield* createResizeNode("resize-1", { width: 800, height: 600 }, {
344
+ * naming: { mode: "auto" }
345
+ * });
346
+ * ```
347
+ */
291
348
  declare function createResizeNode(id: string, {
292
349
  width,
293
350
  height,
294
351
  fit
295
352
  }: ResizeParams, options?: {
296
353
  keepOutput?: boolean;
354
+ naming?: FileNamingConfig;
297
355
  }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
298
356
  inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
299
357
  outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
@@ -318,6 +376,7 @@ declare function createResizeNode(id: string, {
318
376
  retryDelay?: number;
319
377
  exponentialBackoff?: boolean;
320
378
  };
379
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
321
380
  } & {
322
381
  type: _uploadista_core_flow0.NodeType;
323
382
  }, _uploadista_core_errors0.UploadistaError, ImagePlugin | _uploadista_core_upload0.UploadServer>;
@@ -341,21 +400,20 @@ declare function createResizeNode(id: string, {
341
400
  *
342
401
  * @param id - Unique identifier for this node
343
402
  * @param params - Parameters including the transformations array
344
- * @returns Effect that resolves to a transform node
403
+ * @param options - Optional configuration
404
+ * @param options.keepOutput - Whether to keep output in flow results
405
+ * @param options.naming - File naming configuration (auto suffix: `transformed`)
345
406
  *
346
407
  * @example
347
408
  * ```typescript
348
- * const node = createTransformImageNode("transform-1", {
409
+ * // With auto-naming: "photo.jpg" -> "photo-transformed.jpg"
410
+ * const node = yield* createTransformImageNode("transform-1", {
349
411
  * transformations: [
350
412
  * { type: 'resize', width: 800, height: 600, fit: 'cover' },
351
- * { type: 'brightness', value: 20 },
352
- * {
353
- * type: 'watermark',
354
- * imagePath: 'https://cdn.example.com/watermark.png',
355
- * position: 'bottom-right',
356
- * opacity: 0.5
357
- * }
413
+ * { type: 'brightness', value: 20 }
358
414
  * ]
415
+ * }, {
416
+ * naming: { mode: "auto" }
359
417
  * });
360
418
  * ```
361
419
  */
@@ -363,6 +421,7 @@ declare function createTransformImageNode(id: string, {
363
421
  transformations
364
422
  }: TransformImageParams, options?: {
365
423
  keepOutput?: boolean;
424
+ naming?: FileNamingConfig;
366
425
  }): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
367
426
  inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
368
427
  outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
@@ -387,6 +446,7 @@ declare function createTransformImageNode(id: string, {
387
446
  retryDelay?: number;
388
447
  exponentialBackoff?: boolean;
389
448
  };
449
+ circuitBreaker?: _uploadista_core_flow0.FlowCircuitBreakerConfig;
390
450
  } & {
391
451
  type: _uploadista_core_flow0.NodeType;
392
452
  }, _uploadista_core_errors0.UploadistaError, ImagePlugin | _uploadista_core_upload0.UploadServer>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/describe-image-node.ts","../src/optimize-node.ts","../src/remove-background-node.ts","../src/resize-node.ts","../src/transform-image-node.ts","../src/wait-for-url.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/describe-image-node.ts","../src/optimize-node.ts","../src/remove-background-node.ts","../src/resize-node.ts","../src/transform-image-node.ts","../src/wait-for-url.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{UploadistaError as e}from"@uploadista/core/errors";import{IMAGE_DESCRIPTION_OUTPUT_TYPE_ID as t,ImageAiPlugin as n,ImagePlugin as r,NodeType as i,STORAGE_OUTPUT_TYPE_ID as a,completeNodeExecution as o,createFlowNode as s,createTransformNode as c,imageDescriptionOutputSchema as l,resolveUploadMetadata as u}from"@uploadista/core/flow";import{uploadFileSchema as d}from"@uploadista/core/types";import{Effect as f}from"effect";import{UploadServer as p}from"@uploadista/core/upload";function m(t,n={}){let{maxWaitTime:r=1e4,retryDelay:i=500}=n;return f.gen(function*(){let n=Date.now();for(;Date.now()-n<r;){let e=yield*f.tryPromise(()=>fetch(t,{method:`HEAD`})).pipe(f.catchAll(()=>f.succeed(null)));if(e?.ok){yield*f.logInfo(`URL ${t} is now available`);return}e?yield*f.logDebug(`URL not ready yet (${e.status}), retrying...`):yield*f.logDebug(`URL check failed, retrying...`),yield*f.sleep(i)}return yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:`URL ${t} not available after ${r}ms`}).toEffect()})}function h(r,{credentialId:a,keepOutput:c}={}){return f.gen(function*(){let u=yield*n;return yield*s({id:r,name:`Describe Image`,description:`Describes the image using AI`,type:i.process,nodeTypeId:t,keepOutput:c,inputSchema:d,outputSchema:l,run:({data:t,flowId:n,jobId:i,clientId:s})=>f.gen(function*(){let c={flowId:n,nodeId:r,jobId:i},l=t.url;if(!l)return yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:`URL is required for describe image operation`}).toEffect();yield*f.logInfo(`Describing image for file ${t.id} at URL: ${l}`),yield*m(l);let d={clientId:s,credentialId:a},p=yield*u.describeImage(l,d).pipe(f.catchAll(t=>f.gen(function*(){return yield*f.logError(`Failed to describe image`,t),yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to describe image`}).toEffect()})));return yield*f.logInfo(`Successfully described image for file ${t.id}`),o({description:p.description,flow:c})})})})}const g={jpeg:`image/jpeg`,webp:`image/webp`,png:`image/png`,avif:`image/avif`},_={jpeg:`jpg`,webp:`webp`,png:`png`,avif:`avif`};function v(e,{quality:t,format:n},i){return f.gen(function*(){let o=yield*r;return yield*c({id:e,name:`Optimize`,description:`Optimizes an image for web delivery`,nodeTypeId:a,keepOutput:i?.keepOutput,transform:(e,r)=>f.map(o.optimize(e,{quality:t,format:n}),e=>{let t=g[n],i=_[n],a=r.metadata?.fileName;return{bytes:e,type:t,fileName:a&&typeof a==`string`?a.replace(/\.[^.]+$/,`.${i}`):void 0}})})})}function y(t,{credentialId:r,keepOutput:c}={}){return f.gen(function*(){let l=yield*n,h=yield*p;return yield*s({id:t,name:`Remove Background`,description:`Removes the background from an image`,type:i.process,nodeTypeId:a,keepOutput:c,inputSchema:d,outputSchema:d,run:({data:n,flowId:i,jobId:a,storageId:s,clientId:c})=>f.gen(function*(){let d={flowId:i,nodeId:t,jobId:a},p=n.url;if(!p)return yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:`URL is required for remove background operation`}).toEffect();yield*f.logInfo(`Removing background for file ${n.id} at URL: ${n.url}`),yield*m(p);let g={clientId:c,credentialId:r},{outputUrl:_}=yield*l.removeBackground(p,g).pipe(f.catchAll(t=>f.gen(function*(){return yield*f.logError(`Failed to remove background`,t),yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to remove background from image`}).toEffect()}))),{type:v,fileName:y,metadata:b,metadataJson:x}=u(n.metadata);yield*f.logInfo(`Uploading processed file to storage`);let S=yield*h.uploadFromUrl({storageId:s,size:0,type:v,fileName:y,lastModified:0,metadata:x,flow:d},c,_).pipe(f.catchAll(t=>f.gen(function*(){return yield*f.logError(`Failed to upload processed file`,t),yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to upload processed file`}).toEffect()})));return yield*f.logInfo(`Successfully removed background for file ${n.id}`),o(b?{...S,metadata:b}:S)})})})}function b(e,{width:t,height:n,fit:i},o){return f.gen(function*(){let s=yield*r;return yield*c({id:e,name:`Resize`,description:`Resizes an image to the specified dimensions`,nodeTypeId:a,keepOutput:o?.keepOutput,transform:e=>s.resize(e,{height:n,width:t,fit:i})})})}function x(e,t,n){return f.reduce(n,t,(t,n)=>e.transform(t,n))}function S(e,{transformations:t},n){return f.gen(function*(){let i=yield*r;return yield*c({id:e,name:`Transform Image`,description:`Apply ${t.length} transformation${t.length===1?``:`s`} to the image`,nodeTypeId:a,keepOutput:n?.keepOutput,transform:e=>x(i,e,t)})})}export{h as createDescribeImageNode,v as createOptimizeNode,y as createRemoveBackgroundNode,b as createResizeNode,S as createTransformImageNode,m as waitForUrlAvailability};
1
+ import{UploadistaError as e}from"@uploadista/core/errors";import{IMAGE_DESCRIPTION_OUTPUT_TYPE_ID as t,ImageAiPlugin as n,ImagePlugin as r,NodeType as i,STORAGE_OUTPUT_TYPE_ID as a,applyFileNaming as o,buildNamingContext as s,completeNodeExecution as c,createFlowNode as l,createTransformNode as u,getBaseName as d,imageDescriptionOutputSchema as f,resolveUploadMetadata as p}from"@uploadista/core/flow";import{uploadFileSchema as m}from"@uploadista/core/types";import{Effect as h}from"effect";import{UploadServer as g}from"@uploadista/core/upload";function _(t,n={}){let{maxWaitTime:r=1e4,retryDelay:i=500}=n;return h.gen(function*(){let n=Date.now();for(;Date.now()-n<r;){let e=yield*h.tryPromise(()=>fetch(t,{method:`HEAD`})).pipe(h.catchAll(()=>h.succeed(null)));if(e?.ok){yield*h.logInfo(`URL ${t} is now available`);return}e?yield*h.logDebug(`URL not ready yet (${e.status}), retrying...`):yield*h.logDebug(`URL check failed, retrying...`),yield*h.sleep(i)}return yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:`URL ${t} not available after ${r}ms`}).toEffect()})}function v(r,{credentialId:a,keepOutput:o}={}){return h.gen(function*(){let s=yield*n;return yield*l({id:r,name:`Describe Image`,description:`Describes the image using AI`,type:i.process,nodeTypeId:`describe-image`,outputTypeId:t,keepOutput:o,inputSchema:m,outputSchema:f,circuitBreaker:{enabled:!0,failureThreshold:5,resetTimeout:6e4,fallback:{type:`skip`,passThrough:!0}},run:({data:t,flowId:n,jobId:i,clientId:o})=>h.gen(function*(){let l={flowId:n,nodeId:r,jobId:i},u=t.url;if(!u)return yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:`URL is required for describe image operation`}).toEffect();yield*h.logInfo(`Describing image for file ${t.id} at URL: ${u}`),yield*_(u);let d={clientId:o,credentialId:a},f=yield*s.describeImage(u,d).pipe(h.catchAll(t=>h.gen(function*(){return yield*h.logError(`Failed to describe image`,t),yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to describe image`}).toEffect()})));return yield*h.logInfo(`Successfully described image for file ${t.id}`),c({description:f.description,flow:l})})})})}const y={jpeg:`image/jpeg`,webp:`image/webp`,png:`image/png`,avif:`image/avif`},b={jpeg:`jpg`,webp:`webp`,png:`png`,avif:`avif`};function x(e,{quality:t,format:n},i){return h.gen(function*(){let c=yield*r;return yield*u({id:e,name:`Optimize`,description:`Optimizes an image for web delivery`,nodeTypeId:`optimize-image`,outputTypeId:a,keepOutput:i?.keepOutput,nodeType:`optimize`,namingVars:{format:n,quality:t},transform:(r,a)=>h.map(c.optimize(r,{quality:t,format:n}),r=>{let c=y[n],l=b[n],u=a.metadata?.fileName,f;if(u&&typeof u==`string`)if(i?.naming){let r={...i.naming,autoSuffix:i.naming.autoSuffix??(e=>e.format??n)};f=`${d(o(a,s(a,{flowId:a.flow?.flowId??``,jobId:a.flow?.jobId??``,nodeId:e,nodeType:`optimize`},{format:n,quality:t}),r))}.${l}`}else f=u.replace(/\.[^.]+$/,`.${l}`);return{bytes:r,type:c,fileName:f}})})})}function S(t,{credentialId:r,keepOutput:u,naming:d}={}){return h.gen(function*(){let f=yield*n,v=yield*g;return yield*l({id:t,name:`Remove Background`,description:`Removes the background from an image`,type:i.process,nodeTypeId:`remove-background`,outputTypeId:a,keepOutput:u,inputSchema:m,outputSchema:m,circuitBreaker:{enabled:!0,failureThreshold:5,resetTimeout:6e4,fallback:{type:`skip`,passThrough:!0}},run:({data:n,flowId:i,jobId:a,storageId:l,clientId:u})=>h.gen(function*(){let m={flowId:i,nodeId:t,jobId:a},g=n.url;if(!g)return yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:`URL is required for remove background operation`}).toEffect();yield*h.logInfo(`Removing background for file ${n.id} at URL: ${n.url}`),yield*_(g);let y={clientId:u,credentialId:r},{outputUrl:b}=yield*f.removeBackground(g,y).pipe(h.catchAll(t=>h.gen(function*(){return yield*h.logError(`Failed to remove background`,t),yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to remove background from image`}).toEffect()}))),{type:x,fileName:S,metadata:C,metadataJson:w}=p(n.metadata),T=S;if(d){let e={...d,autoSuffix:d.autoSuffix??(()=>`nobg`)};T=o(n,s(n,{flowId:i,jobId:a,nodeId:t,nodeType:`remove-background`}),e)}yield*h.logInfo(`Uploading processed file to storage`);let E=yield*v.uploadFromUrl({storageId:l,size:0,type:x,fileName:T,lastModified:0,metadata:w,flow:m},u,b).pipe(h.catchAll(t=>h.gen(function*(){return yield*h.logError(`Failed to upload processed file`,t),yield*e.fromCode(`FLOW_NODE_ERROR`,{cause:t instanceof Error?t.message:`Failed to upload processed file`}).toEffect()})));yield*h.logInfo(`Successfully removed background for file ${n.id}`);let D=C?{...C,...T!==S&&{fileName:T,originalName:T,name:T,extension:T.split(`.`).pop()||C.extension}}:E.metadata;return c(D?{...E,metadata:D}:E)})})})}function C(e,{width:t,height:n,fit:i},o){return h.gen(function*(){let s=yield*r,c=o?.naming?{...o.naming,autoSuffix:o.naming.autoSuffix??(e=>`${e.width??t}x${e.height??n}`)}:void 0;return yield*u({id:e,name:`Resize`,description:`Resizes an image to the specified dimensions`,nodeTypeId:`resize-image`,outputTypeId:a,keepOutput:o?.keepOutput,naming:c,nodeType:`resize`,namingVars:{width:t,height:n},transform:e=>s.resize(e,{height:n,width:t,fit:i})})})}function w(e,t,n){return h.reduce(n,t,(t,n)=>e.transform(t,n))}function T(e,{transformations:t},n){return h.gen(function*(){let i=yield*r,o=n?.naming?{...n.naming,autoSuffix:n.naming.autoSuffix??(()=>`transformed`)}:void 0;return yield*u({id:e,name:`Transform Image`,description:`Apply ${t.length} transformation${t.length===1?``:`s`} to the image`,nodeTypeId:`transform-image`,outputTypeId:a,keepOutput:n?.keepOutput,naming:o,nodeType:`transform-image`,transform:e=>w(i,e,t)})})}export{v as createDescribeImageNode,x as createOptimizeNode,S as createRemoveBackgroundNode,C as createResizeNode,T as createTransformImageNode,_ as waitForUrlAvailability};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["formatToMimeType: Record<OptimizeParams[\"format\"], string>","formatToExtension: Record<OptimizeParams[\"format\"], string>"],"sources":["../src/wait-for-url.ts","../src/describe-image-node.ts","../src/optimize-node.ts","../src/remove-background-node.ts","../src/resize-node.ts","../src/transform-image-node.ts"],"sourcesContent":["import { UploadistaError } from \"@uploadista/core/errors\";\nimport { Effect } from \"effect\";\n\n/**\n * Waits for a URL to become available by periodically checking its accessibility.\n * This is useful when a file has just been uploaded and may not be immediately\n * accessible due to CDN propagation or storage consistency delays.\n *\n * @param url - The URL to check for availability\n * @param options - Configuration options\n * @param options.maxWaitTime - Maximum time to wait in milliseconds (default: 10000)\n * @param options.retryDelay - Delay between retries in milliseconds (default: 500)\n * @returns Effect that succeeds when URL is available or fails with UploadistaError\n */\nexport function waitForUrlAvailability(\n url: string,\n options: {\n maxWaitTime?: number;\n retryDelay?: number;\n } = {},\n): Effect.Effect<void, UploadistaError> {\n const { maxWaitTime = 10000, retryDelay = 500 } = options;\n\n return Effect.gen(function* () {\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWaitTime) {\n const response = yield* Effect.tryPromise(() =>\n fetch(url, { method: \"HEAD\" }),\n ).pipe(Effect.catchAll(() => Effect.succeed(null)));\n\n if (response?.ok) {\n yield* Effect.logInfo(`URL ${url} is now available`);\n return;\n }\n\n if (response) {\n yield* Effect.logDebug(\n `URL not ready yet (${response.status}), retrying...`,\n );\n } else {\n yield* Effect.logDebug(`URL check failed, retrying...`);\n }\n\n yield* Effect.sleep(retryDelay);\n }\n\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause: `URL ${url} not available after ${maxWaitTime}ms`,\n }).toEffect();\n });\n}\n","import { UploadistaError } from \"@uploadista/core/errors\";\nimport {\n completeNodeExecution,\n createFlowNode,\n IMAGE_DESCRIPTION_OUTPUT_TYPE_ID,\n ImageAiPlugin,\n imageDescriptionOutputSchema,\n NodeType,\n} from \"@uploadista/core/flow\";\nimport { uploadFileSchema } from \"@uploadista/core/types\";\n\nimport { Effect } from \"effect\";\nimport { waitForUrlAvailability } from \"./wait-for-url\";\n\nexport function createDescribeImageNode(\n id: string,\n { credentialId, keepOutput }: { credentialId?: string; keepOutput?: boolean } = {},\n) {\n return Effect.gen(function* () {\n const imageAiService = yield* ImageAiPlugin;\n\n return yield* createFlowNode({\n id,\n name: \"Describe Image\",\n description: \"Describes the image using AI\",\n type: NodeType.process,\n nodeTypeId: IMAGE_DESCRIPTION_OUTPUT_TYPE_ID,\n keepOutput,\n inputSchema: uploadFileSchema,\n outputSchema: imageDescriptionOutputSchema,\n run: ({ data: file, flowId, jobId, clientId }) => {\n return Effect.gen(function* () {\n const flow = {\n flowId,\n nodeId: id,\n jobId,\n };\n\n const fileUrl = file.url;\n\n // Validate input\n if (!fileUrl) {\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause: \"URL is required for describe image operation\",\n }).toEffect();\n }\n\n yield* Effect.logInfo(\n `Describing image for file ${file.id} at URL: ${fileUrl}`,\n );\n\n // Wait for URL to be available with retry mechanism\n yield* waitForUrlAvailability(fileUrl);\n\n // Build context for ImageAI plugin\n const context = {\n clientId,\n credentialId,\n };\n\n // Describe image with error handling\n const result = yield* imageAiService\n .describeImage(fileUrl, context)\n .pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* Effect.logError(\"Failed to describe image\", error);\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause:\n error instanceof Error\n ? error.message\n : \"Failed to describe image\",\n }).toEffect();\n }),\n ),\n );\n\n yield* Effect.logInfo(\n `Successfully described image for file ${file.id}`,\n );\n\n // Return structured image description output (not UploadFile)\n return completeNodeExecution({\n description: result.description,\n flow,\n });\n });\n },\n });\n });\n}\n","import {\n createTransformNode,\n ImagePlugin,\n type OptimizeParams,\n STORAGE_OUTPUT_TYPE_ID,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n// Map image format to MIME type\nconst formatToMimeType: Record<OptimizeParams[\"format\"], string> = {\n jpeg: \"image/jpeg\",\n webp: \"image/webp\",\n png: \"image/png\",\n avif: \"image/avif\",\n};\n\n// Map image format to file extension\nconst formatToExtension: Record<OptimizeParams[\"format\"], string> = {\n jpeg: \"jpg\",\n webp: \"webp\",\n png: \"png\",\n avif: \"avif\",\n};\n\nexport function createOptimizeNode(\n id: string,\n { quality, format }: OptimizeParams,\n options?: { keepOutput?: boolean },\n) {\n return Effect.gen(function* () {\n const imageService = yield* ImagePlugin;\n\n return yield* createTransformNode({\n id,\n name: \"Optimize\",\n description: \"Optimizes an image for web delivery\",\n nodeTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n transform: (inputBytes, file) =>\n Effect.map(\n imageService.optimize(inputBytes, { quality, format }),\n (optimizedBytes) => {\n // Return bytes with updated metadata if format changes\n const newType = formatToMimeType[format];\n const newExtension = formatToExtension[format];\n\n // Update file extension if format changed\n const fileName = file.metadata?.fileName;\n const newFileName =\n fileName && typeof fileName === \"string\"\n ? fileName.replace(/\\.[^.]+$/, `.${newExtension}`)\n : undefined;\n\n return {\n bytes: optimizedBytes,\n type: newType,\n fileName: newFileName,\n } as\n | Uint8Array\n | { bytes: Uint8Array; type: string; fileName?: string };\n },\n ),\n });\n });\n}\n","import { UploadistaError } from \"@uploadista/core/errors\";\nimport {\n completeNodeExecution,\n createFlowNode,\n ImageAiPlugin,\n NodeType,\n resolveUploadMetadata,\n STORAGE_OUTPUT_TYPE_ID,\n} from \"@uploadista/core/flow\";\nimport { uploadFileSchema } from \"@uploadista/core/types\";\nimport { UploadServer } from \"@uploadista/core/upload\";\nimport { Effect } from \"effect\";\nimport { waitForUrlAvailability } from \"./wait-for-url\";\n\nexport function createRemoveBackgroundNode(\n id: string,\n { credentialId, keepOutput }: { credentialId?: string; keepOutput?: boolean } = {},\n) {\n return Effect.gen(function* () {\n const imageAiService = yield* ImageAiPlugin;\n const uploadServer = yield* UploadServer;\n\n return yield* createFlowNode({\n id,\n name: \"Remove Background\",\n description: \"Removes the background from an image\",\n type: NodeType.process,\n nodeTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput,\n inputSchema: uploadFileSchema,\n outputSchema: uploadFileSchema,\n run: ({ data: file, flowId, jobId, storageId, clientId }) => {\n return Effect.gen(function* () {\n const flow = {\n flowId,\n nodeId: id,\n jobId,\n };\n\n const fileUrl = file.url;\n\n // Validate input\n if (!fileUrl) {\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause: \"URL is required for remove background operation\",\n }).toEffect();\n }\n\n yield* Effect.logInfo(\n `Removing background for file ${file.id} at URL: ${file.url}`,\n );\n\n // Wait for URL to be available with retry mechanism\n yield* waitForUrlAvailability(fileUrl);\n\n // Build context for ImageAI plugin\n const context = {\n clientId,\n credentialId,\n };\n\n // Remove background with error handling\n const backgroundRemovalResult = yield* imageAiService\n .removeBackground(fileUrl, context)\n .pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* Effect.logError(\"Failed to remove background\", error);\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause:\n error instanceof Error\n ? error.message\n : \"Failed to remove background from image\",\n }).toEffect();\n }),\n ),\n );\n\n const { outputUrl } = backgroundRemovalResult;\n const { type, fileName, metadata, metadataJson } =\n resolveUploadMetadata(file.metadata);\n\n yield* Effect.logInfo(`Uploading processed file to storage`);\n\n // Upload the transformed bytes back to the upload server with error handling\n const result = yield* uploadServer\n .uploadFromUrl(\n {\n storageId,\n size: 0,\n type,\n fileName,\n lastModified: 0,\n metadata: metadataJson,\n flow,\n },\n clientId,\n outputUrl,\n )\n .pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* Effect.logError(\n \"Failed to upload processed file\",\n error,\n );\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause:\n error instanceof Error\n ? error.message\n : \"Failed to upload processed file\",\n }).toEffect();\n }),\n ),\n );\n\n yield* Effect.logInfo(\n `Successfully removed background for file ${file.id}`,\n );\n\n return completeNodeExecution(\n metadata\n ? {\n ...result,\n metadata,\n }\n : result,\n );\n });\n },\n });\n });\n}\n","import {\n createTransformNode,\n ImagePlugin,\n type ResizeParams,\n STORAGE_OUTPUT_TYPE_ID,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\nexport function createResizeNode(\n id: string,\n { width, height, fit }: ResizeParams,\n options?: { keepOutput?: boolean },\n) {\n return Effect.gen(function* () {\n const imageService = yield* ImagePlugin;\n\n return yield* createTransformNode({\n id,\n name: \"Resize\",\n description: \"Resizes an image to the specified dimensions\",\n nodeTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n transform: (inputBytes) =>\n imageService.resize(inputBytes, { height, width, fit }),\n });\n });\n}\n","import {\n createTransformNode,\n ImagePlugin,\n STORAGE_OUTPUT_TYPE_ID,\n type TransformImageParams,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Apply a chain of transformations to an image by reducing over the transformations array.\n * Each transformation receives the output of the previous transformation as input.\n *\n * @param imageService - The image plugin service to use for transformations\n * @param inputBytes - The input image bytes\n * @param transformations - Array of transformations to apply in sequence\n * @returns Effect that resolves to the final transformed image bytes\n */\nfunction applyTransformationChain(\n imageService: ReturnType<typeof ImagePlugin.of>,\n inputBytes: Uint8Array,\n transformations: TransformImageParams[\"transformations\"],\n) {\n return Effect.reduce(transformations, inputBytes, (bytes, transformation) =>\n imageService.transform(bytes, transformation),\n );\n}\n\n/**\n * Creates a transform image node that applies multiple transformations sequentially.\n *\n * This node enables complex image processing workflows by chaining multiple transformations\n * together. Each transformation is applied to the output of the previous transformation,\n * allowing for powerful image manipulation pipelines.\n *\n * Supported transformations include:\n * - Basic: resize, blur, rotate, flip\n * - Filters: grayscale, sepia, brightness, contrast\n * - Effects: sharpen\n * - Advanced: watermark, logo, text\n *\n * Note: Watermark and logo transformations require imagePath to be a valid URL.\n * Images will be fetched from the provided URL during transformation.\n *\n * @param id - Unique identifier for this node\n * @param params - Parameters including the transformations array\n * @returns Effect that resolves to a transform node\n *\n * @example\n * ```typescript\n * const node = createTransformImageNode(\"transform-1\", {\n * transformations: [\n * { type: 'resize', width: 800, height: 600, fit: 'cover' },\n * { type: 'brightness', value: 20 },\n * {\n * type: 'watermark',\n * imagePath: 'https://cdn.example.com/watermark.png',\n * position: 'bottom-right',\n * opacity: 0.5\n * }\n * ]\n * });\n * ```\n */\nexport function createTransformImageNode(\n id: string,\n { transformations }: TransformImageParams,\n options?: { keepOutput?: boolean },\n) {\n return Effect.gen(function* () {\n const imageService = yield* ImagePlugin;\n\n return yield* createTransformNode({\n id,\n name: \"Transform Image\",\n description: `Apply ${transformations.length} transformation${transformations.length === 1 ? \"\" : \"s\"} to the image`,\n nodeTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n transform: (inputBytes) =>\n applyTransformationChain(imageService, inputBytes, transformations),\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["formatToMimeType: Record<OptimizeParams[\"format\"], string>","formatToExtension: Record<OptimizeParams[\"format\"], string>","newFileName: string | undefined","namingConfig: FileNamingConfig","namingConfig: FileNamingConfig","namingConfig: FileNamingConfig | undefined","namingConfig: FileNamingConfig | undefined"],"sources":["../src/wait-for-url.ts","../src/describe-image-node.ts","../src/optimize-node.ts","../src/remove-background-node.ts","../src/resize-node.ts","../src/transform-image-node.ts"],"sourcesContent":["import { UploadistaError } from \"@uploadista/core/errors\";\nimport { Effect } from \"effect\";\n\n/**\n * Waits for a URL to become available by periodically checking its accessibility.\n * This is useful when a file has just been uploaded and may not be immediately\n * accessible due to CDN propagation or storage consistency delays.\n *\n * @param url - The URL to check for availability\n * @param options - Configuration options\n * @param options.maxWaitTime - Maximum time to wait in milliseconds (default: 10000)\n * @param options.retryDelay - Delay between retries in milliseconds (default: 500)\n * @returns Effect that succeeds when URL is available or fails with UploadistaError\n */\nexport function waitForUrlAvailability(\n url: string,\n options: {\n maxWaitTime?: number;\n retryDelay?: number;\n } = {},\n): Effect.Effect<void, UploadistaError> {\n const { maxWaitTime = 10000, retryDelay = 500 } = options;\n\n return Effect.gen(function* () {\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWaitTime) {\n const response = yield* Effect.tryPromise(() =>\n fetch(url, { method: \"HEAD\" }),\n ).pipe(Effect.catchAll(() => Effect.succeed(null)));\n\n if (response?.ok) {\n yield* Effect.logInfo(`URL ${url} is now available`);\n return;\n }\n\n if (response) {\n yield* Effect.logDebug(\n `URL not ready yet (${response.status}), retrying...`,\n );\n } else {\n yield* Effect.logDebug(`URL check failed, retrying...`);\n }\n\n yield* Effect.sleep(retryDelay);\n }\n\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause: `URL ${url} not available after ${maxWaitTime}ms`,\n }).toEffect();\n });\n}\n","import { UploadistaError } from \"@uploadista/core/errors\";\nimport {\n completeNodeExecution,\n createFlowNode,\n IMAGE_DESCRIPTION_OUTPUT_TYPE_ID,\n ImageAiPlugin,\n imageDescriptionOutputSchema,\n NodeType,\n} from \"@uploadista/core/flow\";\nimport { uploadFileSchema } from \"@uploadista/core/types\";\n\nimport { Effect } from \"effect\";\nimport { waitForUrlAvailability } from \"./wait-for-url\";\n\nexport function createDescribeImageNode(\n id: string,\n { credentialId, keepOutput }: { credentialId?: string; keepOutput?: boolean } = {},\n) {\n return Effect.gen(function* () {\n const imageAiService = yield* ImageAiPlugin;\n\n return yield* createFlowNode({\n id,\n name: \"Describe Image\",\n description: \"Describes the image using AI\",\n type: NodeType.process,\n nodeTypeId: \"describe-image\",\n outputTypeId: IMAGE_DESCRIPTION_OUTPUT_TYPE_ID,\n keepOutput,\n inputSchema: uploadFileSchema,\n outputSchema: imageDescriptionOutputSchema,\n // AI service - enable circuit breaker with skip fallback\n circuitBreaker: {\n enabled: true,\n failureThreshold: 5,\n resetTimeout: 60000,\n fallback: { type: \"skip\", passThrough: true },\n },\n run: ({ data: file, flowId, jobId, clientId }) => {\n return Effect.gen(function* () {\n const flow = {\n flowId,\n nodeId: id,\n jobId,\n };\n\n const fileUrl = file.url;\n\n // Validate input\n if (!fileUrl) {\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause: \"URL is required for describe image operation\",\n }).toEffect();\n }\n\n yield* Effect.logInfo(\n `Describing image for file ${file.id} at URL: ${fileUrl}`,\n );\n\n // Wait for URL to be available with retry mechanism\n yield* waitForUrlAvailability(fileUrl);\n\n // Build context for ImageAI plugin\n const context = {\n clientId,\n credentialId,\n };\n\n // Describe image with error handling\n const result = yield* imageAiService\n .describeImage(fileUrl, context)\n .pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* Effect.logError(\"Failed to describe image\", error);\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause:\n error instanceof Error\n ? error.message\n : \"Failed to describe image\",\n }).toEffect();\n }),\n ),\n );\n\n yield* Effect.logInfo(\n `Successfully described image for file ${file.id}`,\n );\n\n // Return structured image description output (not UploadFile)\n return completeNodeExecution({\n description: result.description,\n flow,\n });\n });\n },\n });\n });\n}\n","import {\n applyFileNaming,\n buildNamingContext,\n createTransformNode,\n type FileNamingConfig,\n getBaseName,\n ImagePlugin,\n type OptimizeParams,\n STORAGE_OUTPUT_TYPE_ID,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n// Map image format to MIME type\nconst formatToMimeType: Record<OptimizeParams[\"format\"], string> = {\n jpeg: \"image/jpeg\",\n webp: \"image/webp\",\n png: \"image/png\",\n avif: \"image/avif\",\n};\n\n// Map image format to file extension\nconst formatToExtension: Record<OptimizeParams[\"format\"], string> = {\n jpeg: \"jpg\",\n webp: \"webp\",\n png: \"png\",\n avif: \"avif\",\n};\n\n/**\n * Creates an optimize node that optimizes images for web delivery.\n *\n * @param id - Unique node identifier\n * @param params - Optimize parameters (quality, format)\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `${format}`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"photo.jpg\" -> \"photo-webp.webp\"\n * const optimize = yield* createOptimizeNode(\"opt-1\", { quality: 80, format: \"webp\" }, {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createOptimizeNode(\n id: string,\n { quality, format }: OptimizeParams,\n options?: { keepOutput?: boolean; naming?: FileNamingConfig },\n) {\n return Effect.gen(function* () {\n const imageService = yield* ImagePlugin;\n\n return yield* createTransformNode({\n id,\n name: \"Optimize\",\n description: \"Optimizes an image for web delivery\",\n nodeTypeId: \"optimize-image\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n // Note: naming is handled in transform since format changes extension\n nodeType: \"optimize\",\n namingVars: { format, quality },\n transform: (inputBytes, file) =>\n Effect.map(\n imageService.optimize(inputBytes, { quality, format }),\n (optimizedBytes) => {\n // Return bytes with updated metadata if format changes\n const newType = formatToMimeType[format];\n const newExtension = formatToExtension[format];\n\n // Get original fileName\n const fileName = file.metadata?.fileName;\n let newFileName: string | undefined;\n\n if (fileName && typeof fileName === \"string\") {\n // Apply naming if configured\n if (options?.naming) {\n const namingConfig: FileNamingConfig = {\n ...options.naming,\n autoSuffix:\n options.naming.autoSuffix ?? ((ctx) => ctx.format ?? format),\n };\n const namingContext = buildNamingContext(\n file,\n {\n flowId: file.flow?.flowId ?? \"\",\n jobId: file.flow?.jobId ?? \"\",\n nodeId: id,\n nodeType: \"optimize\",\n },\n { format, quality },\n );\n // Apply naming to get base name with suffix\n const namedFile = applyFileNaming(file, namingContext, namingConfig);\n // Replace extension with new format extension\n newFileName = `${getBaseName(namedFile)}.${newExtension}`;\n } else {\n // No naming config, just update extension\n newFileName = fileName.replace(/\\.[^.]+$/, `.${newExtension}`);\n }\n }\n\n return {\n bytes: optimizedBytes,\n type: newType,\n fileName: newFileName,\n } as\n | Uint8Array\n | { bytes: Uint8Array; type: string; fileName?: string };\n },\n ),\n });\n });\n}\n","import { UploadistaError } from \"@uploadista/core/errors\";\nimport {\n applyFileNaming,\n buildNamingContext,\n completeNodeExecution,\n createFlowNode,\n type FileNamingConfig,\n ImageAiPlugin,\n NodeType,\n resolveUploadMetadata,\n STORAGE_OUTPUT_TYPE_ID,\n} from \"@uploadista/core/flow\";\nimport { uploadFileSchema } from \"@uploadista/core/types\";\nimport { UploadServer } from \"@uploadista/core/upload\";\nimport { Effect } from \"effect\";\nimport { waitForUrlAvailability } from \"./wait-for-url\";\n\n/**\n * Creates a remove-background node that removes backgrounds from images using AI.\n *\n * @param id - Unique node identifier\n * @param options - Optional configuration\n * @param options.credentialId - Optional credential ID for AI service\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `nobg`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"photo.jpg\" -> \"photo-nobg.jpg\"\n * const node = yield* createRemoveBackgroundNode(\"remove-bg-1\", {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createRemoveBackgroundNode(\n id: string,\n { credentialId, keepOutput, naming }: { credentialId?: string; keepOutput?: boolean; naming?: FileNamingConfig } = {},\n) {\n return Effect.gen(function* () {\n const imageAiService = yield* ImageAiPlugin;\n const uploadServer = yield* UploadServer;\n\n return yield* createFlowNode({\n id,\n name: \"Remove Background\",\n description: \"Removes the background from an image\",\n type: NodeType.process,\n nodeTypeId: \"remove-background\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput,\n inputSchema: uploadFileSchema,\n outputSchema: uploadFileSchema,\n // AI service - enable circuit breaker with skip fallback\n circuitBreaker: {\n enabled: true,\n failureThreshold: 5,\n resetTimeout: 60000,\n fallback: { type: \"skip\", passThrough: true },\n },\n run: ({ data: file, flowId, jobId, storageId, clientId }) => {\n return Effect.gen(function* () {\n const flow = {\n flowId,\n nodeId: id,\n jobId,\n };\n\n const fileUrl = file.url;\n\n // Validate input\n if (!fileUrl) {\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause: \"URL is required for remove background operation\",\n }).toEffect();\n }\n\n yield* Effect.logInfo(\n `Removing background for file ${file.id} at URL: ${file.url}`,\n );\n\n // Wait for URL to be available with retry mechanism\n yield* waitForUrlAvailability(fileUrl);\n\n // Build context for ImageAI plugin\n const context = {\n clientId,\n credentialId,\n };\n\n // Remove background with error handling\n const backgroundRemovalResult = yield* imageAiService\n .removeBackground(fileUrl, context)\n .pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* Effect.logError(\"Failed to remove background\", error);\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause:\n error instanceof Error\n ? error.message\n : \"Failed to remove background from image\",\n }).toEffect();\n }),\n ),\n );\n\n const { outputUrl } = backgroundRemovalResult;\n const { type, fileName, metadata, metadataJson } =\n resolveUploadMetadata(file.metadata);\n\n // Apply file naming if configured\n let outputFileName = fileName;\n if (naming) {\n const namingConfig: FileNamingConfig = {\n ...naming,\n autoSuffix: naming.autoSuffix ?? (() => \"nobg\"),\n };\n const namingContext = buildNamingContext(\n file,\n {\n flowId,\n jobId,\n nodeId: id,\n nodeType: \"remove-background\",\n },\n );\n outputFileName = applyFileNaming(file, namingContext, namingConfig);\n }\n\n yield* Effect.logInfo(`Uploading processed file to storage`);\n\n // Upload the transformed bytes back to the upload server with error handling\n const result = yield* uploadServer\n .uploadFromUrl(\n {\n storageId,\n size: 0,\n type,\n fileName: outputFileName,\n lastModified: 0,\n metadata: metadataJson,\n flow,\n },\n clientId,\n outputUrl,\n )\n .pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* Effect.logError(\n \"Failed to upload processed file\",\n error,\n );\n return yield* UploadistaError.fromCode(\"FLOW_NODE_ERROR\", {\n cause:\n error instanceof Error\n ? error.message\n : \"Failed to upload processed file\",\n }).toEffect();\n }),\n ),\n );\n\n yield* Effect.logInfo(\n `Successfully removed background for file ${file.id}`,\n );\n\n // Update metadata with new filename if naming was applied\n const updatedMetadata = metadata\n ? {\n ...metadata,\n ...(outputFileName !== fileName && {\n fileName: outputFileName,\n originalName: outputFileName,\n name: outputFileName,\n extension: outputFileName.split(\".\").pop() || metadata.extension,\n }),\n }\n : result.metadata;\n\n return completeNodeExecution(\n updatedMetadata\n ? {\n ...result,\n metadata: updatedMetadata,\n }\n : result,\n );\n });\n },\n });\n });\n}\n","import {\n createTransformNode,\n type FileNamingConfig,\n ImagePlugin,\n type ResizeParams,\n STORAGE_OUTPUT_TYPE_ID,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Creates a resize node that resizes images to specified dimensions.\n *\n * @param id - Unique node identifier\n * @param params - Resize parameters (width, height, fit)\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"photo.jpg\" -> \"photo-800x600.jpg\"\n * const resize = yield* createResizeNode(\"resize-1\", { width: 800, height: 600 }, {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createResizeNode(\n id: string,\n { width, height, fit }: ResizeParams,\n options?: { keepOutput?: boolean; naming?: FileNamingConfig },\n) {\n return Effect.gen(function* () {\n const imageService = yield* ImagePlugin;\n\n // Build naming config with auto suffix for resize\n const namingConfig: FileNamingConfig | undefined = options?.naming\n ? {\n ...options.naming,\n // Provide default auto suffix generator for resize nodes\n autoSuffix:\n options.naming.autoSuffix ??\n ((ctx) => `${ctx.width ?? width}x${ctx.height ?? height}`),\n }\n : undefined;\n\n return yield* createTransformNode({\n id,\n name: \"Resize\",\n description: \"Resizes an image to the specified dimensions\",\n nodeTypeId: \"resize-image\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n naming: namingConfig,\n nodeType: \"resize\",\n namingVars: { width, height },\n transform: (inputBytes) =>\n imageService.resize(inputBytes, { height, width, fit }),\n });\n });\n}\n","import {\n createTransformNode,\n type FileNamingConfig,\n ImagePlugin,\n STORAGE_OUTPUT_TYPE_ID,\n type TransformImageParams,\n} from \"@uploadista/core/flow\";\nimport { Effect } from \"effect\";\n\n/**\n * Apply a chain of transformations to an image by reducing over the transformations array.\n * Each transformation receives the output of the previous transformation as input.\n *\n * @param imageService - The image plugin service to use for transformations\n * @param inputBytes - The input image bytes\n * @param transformations - Array of transformations to apply in sequence\n * @returns Effect that resolves to the final transformed image bytes\n */\nfunction applyTransformationChain(\n imageService: ReturnType<typeof ImagePlugin.of>,\n inputBytes: Uint8Array,\n transformations: TransformImageParams[\"transformations\"],\n) {\n return Effect.reduce(transformations, inputBytes, (bytes, transformation) =>\n imageService.transform(bytes, transformation),\n );\n}\n\n/**\n * Creates a transform image node that applies multiple transformations sequentially.\n *\n * This node enables complex image processing workflows by chaining multiple transformations\n * together. Each transformation is applied to the output of the previous transformation,\n * allowing for powerful image manipulation pipelines.\n *\n * Supported transformations include:\n * - Basic: resize, blur, rotate, flip\n * - Filters: grayscale, sepia, brightness, contrast\n * - Effects: sharpen\n * - Advanced: watermark, logo, text\n *\n * Note: Watermark and logo transformations require imagePath to be a valid URL.\n * Images will be fetched from the provided URL during transformation.\n *\n * @param id - Unique identifier for this node\n * @param params - Parameters including the transformations array\n * @param options - Optional configuration\n * @param options.keepOutput - Whether to keep output in flow results\n * @param options.naming - File naming configuration (auto suffix: `transformed`)\n *\n * @example\n * ```typescript\n * // With auto-naming: \"photo.jpg\" -> \"photo-transformed.jpg\"\n * const node = yield* createTransformImageNode(\"transform-1\", {\n * transformations: [\n * { type: 'resize', width: 800, height: 600, fit: 'cover' },\n * { type: 'brightness', value: 20 }\n * ]\n * }, {\n * naming: { mode: \"auto\" }\n * });\n * ```\n */\nexport function createTransformImageNode(\n id: string,\n { transformations }: TransformImageParams,\n options?: { keepOutput?: boolean; naming?: FileNamingConfig },\n) {\n return Effect.gen(function* () {\n const imageService = yield* ImagePlugin;\n\n // Build naming config with auto suffix for transform-image\n const namingConfig: FileNamingConfig | undefined = options?.naming\n ? {\n ...options.naming,\n autoSuffix: options.naming.autoSuffix ?? (() => \"transformed\"),\n }\n : undefined;\n\n return yield* createTransformNode({\n id,\n name: \"Transform Image\",\n description: `Apply ${transformations.length} transformation${transformations.length === 1 ? \"\" : \"s\"} to the image`,\n nodeTypeId: \"transform-image\",\n outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n naming: namingConfig,\n nodeType: \"transform-image\",\n transform: (inputBytes) =>\n applyTransformationChain(imageService, inputBytes, transformations),\n });\n });\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@uploadista/flow-images-nodes",
3
3
  "type": "module",
4
- "version": "0.0.17",
4
+ "version": "0.0.18-beta.10",
5
5
  "description": "Image processing nodes for Uploadista Flow",
6
6
  "license": "MIT",
7
7
  "author": "Uploadista",
@@ -14,16 +14,20 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "effect": "3.19.6",
18
- "zod": "4.1.13",
19
- "@uploadista/core": "0.0.17"
17
+ "@uploadista/core": "0.0.18-beta.10"
18
+ },
19
+ "peerDependencies": {
20
+ "effect": "^3.0.0",
21
+ "zod": "^4.0.0"
20
22
  },
21
23
  "devDependencies": {
22
24
  "@effect/vitest": "0.27.0",
23
25
  "@types/node": "24.10.1",
24
- "tsdown": "0.16.6",
25
- "vitest": "4.0.13",
26
- "@uploadista/typescript-config": "0.0.17"
26
+ "effect": "3.19.8",
27
+ "tsdown": "0.16.8",
28
+ "vitest": "4.0.14",
29
+ "zod": "4.1.13",
30
+ "@uploadista/typescript-config": "0.0.18-beta.10"
27
31
  },
28
32
  "scripts": {
29
33
  "build": "tsdown",
@@ -24,10 +24,18 @@ export function createDescribeImageNode(
24
24
  name: "Describe Image",
25
25
  description: "Describes the image using AI",
26
26
  type: NodeType.process,
27
- nodeTypeId: IMAGE_DESCRIPTION_OUTPUT_TYPE_ID,
27
+ nodeTypeId: "describe-image",
28
+ outputTypeId: IMAGE_DESCRIPTION_OUTPUT_TYPE_ID,
28
29
  keepOutput,
29
30
  inputSchema: uploadFileSchema,
30
31
  outputSchema: imageDescriptionOutputSchema,
32
+ // AI service - enable circuit breaker with skip fallback
33
+ circuitBreaker: {
34
+ enabled: true,
35
+ failureThreshold: 5,
36
+ resetTimeout: 60000,
37
+ fallback: { type: "skip", passThrough: true },
38
+ },
31
39
  run: ({ data: file, flowId, jobId, clientId }) => {
32
40
  return Effect.gen(function* () {
33
41
  const flow = {
@@ -1,5 +1,9 @@
1
1
  import {
2
+ applyFileNaming,
3
+ buildNamingContext,
2
4
  createTransformNode,
5
+ type FileNamingConfig,
6
+ getBaseName,
3
7
  ImagePlugin,
4
8
  type OptimizeParams,
5
9
  STORAGE_OUTPUT_TYPE_ID,
@@ -22,10 +26,27 @@ const formatToExtension: Record<OptimizeParams["format"], string> = {
22
26
  avif: "avif",
23
27
  };
24
28
 
29
+ /**
30
+ * Creates an optimize node that optimizes images for web delivery.
31
+ *
32
+ * @param id - Unique node identifier
33
+ * @param params - Optimize parameters (quality, format)
34
+ * @param options - Optional configuration
35
+ * @param options.keepOutput - Whether to keep output in flow results
36
+ * @param options.naming - File naming configuration (auto suffix: `${format}`)
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * // With auto-naming: "photo.jpg" -> "photo-webp.webp"
41
+ * const optimize = yield* createOptimizeNode("opt-1", { quality: 80, format: "webp" }, {
42
+ * naming: { mode: "auto" }
43
+ * });
44
+ * ```
45
+ */
25
46
  export function createOptimizeNode(
26
47
  id: string,
27
48
  { quality, format }: OptimizeParams,
28
- options?: { keepOutput?: boolean },
49
+ options?: { keepOutput?: boolean; naming?: FileNamingConfig },
29
50
  ) {
30
51
  return Effect.gen(function* () {
31
52
  const imageService = yield* ImagePlugin;
@@ -34,8 +55,12 @@ export function createOptimizeNode(
34
55
  id,
35
56
  name: "Optimize",
36
57
  description: "Optimizes an image for web delivery",
37
- nodeTypeId: STORAGE_OUTPUT_TYPE_ID,
58
+ nodeTypeId: "optimize-image",
59
+ outputTypeId: STORAGE_OUTPUT_TYPE_ID,
38
60
  keepOutput: options?.keepOutput,
61
+ // Note: naming is handled in transform since format changes extension
62
+ nodeType: "optimize",
63
+ namingVars: { format, quality },
39
64
  transform: (inputBytes, file) =>
40
65
  Effect.map(
41
66
  imageService.optimize(inputBytes, { quality, format }),
@@ -44,12 +69,37 @@ export function createOptimizeNode(
44
69
  const newType = formatToMimeType[format];
45
70
  const newExtension = formatToExtension[format];
46
71
 
47
- // Update file extension if format changed
72
+ // Get original fileName
48
73
  const fileName = file.metadata?.fileName;
49
- const newFileName =
50
- fileName && typeof fileName === "string"
51
- ? fileName.replace(/\.[^.]+$/, `.${newExtension}`)
52
- : undefined;
74
+ let newFileName: string | undefined;
75
+
76
+ if (fileName && typeof fileName === "string") {
77
+ // Apply naming if configured
78
+ if (options?.naming) {
79
+ const namingConfig: FileNamingConfig = {
80
+ ...options.naming,
81
+ autoSuffix:
82
+ options.naming.autoSuffix ?? ((ctx) => ctx.format ?? format),
83
+ };
84
+ const namingContext = buildNamingContext(
85
+ file,
86
+ {
87
+ flowId: file.flow?.flowId ?? "",
88
+ jobId: file.flow?.jobId ?? "",
89
+ nodeId: id,
90
+ nodeType: "optimize",
91
+ },
92
+ { format, quality },
93
+ );
94
+ // Apply naming to get base name with suffix
95
+ const namedFile = applyFileNaming(file, namingContext, namingConfig);
96
+ // Replace extension with new format extension
97
+ newFileName = `${getBaseName(namedFile)}.${newExtension}`;
98
+ } else {
99
+ // No naming config, just update extension
100
+ newFileName = fileName.replace(/\.[^.]+$/, `.${newExtension}`);
101
+ }
102
+ }
53
103
 
54
104
  return {
55
105
  bytes: optimizedBytes,
@@ -1,7 +1,10 @@
1
1
  import { UploadistaError } from "@uploadista/core/errors";
2
2
  import {
3
+ applyFileNaming,
4
+ buildNamingContext,
3
5
  completeNodeExecution,
4
6
  createFlowNode,
7
+ type FileNamingConfig,
5
8
  ImageAiPlugin,
6
9
  NodeType,
7
10
  resolveUploadMetadata,
@@ -12,9 +15,26 @@ import { UploadServer } from "@uploadista/core/upload";
12
15
  import { Effect } from "effect";
13
16
  import { waitForUrlAvailability } from "./wait-for-url";
14
17
 
18
+ /**
19
+ * Creates a remove-background node that removes backgrounds from images using AI.
20
+ *
21
+ * @param id - Unique node identifier
22
+ * @param options - Optional configuration
23
+ * @param options.credentialId - Optional credential ID for AI service
24
+ * @param options.keepOutput - Whether to keep output in flow results
25
+ * @param options.naming - File naming configuration (auto suffix: `nobg`)
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * // With auto-naming: "photo.jpg" -> "photo-nobg.jpg"
30
+ * const node = yield* createRemoveBackgroundNode("remove-bg-1", {
31
+ * naming: { mode: "auto" }
32
+ * });
33
+ * ```
34
+ */
15
35
  export function createRemoveBackgroundNode(
16
36
  id: string,
17
- { credentialId, keepOutput }: { credentialId?: string; keepOutput?: boolean } = {},
37
+ { credentialId, keepOutput, naming }: { credentialId?: string; keepOutput?: boolean; naming?: FileNamingConfig } = {},
18
38
  ) {
19
39
  return Effect.gen(function* () {
20
40
  const imageAiService = yield* ImageAiPlugin;
@@ -25,10 +45,18 @@ export function createRemoveBackgroundNode(
25
45
  name: "Remove Background",
26
46
  description: "Removes the background from an image",
27
47
  type: NodeType.process,
28
- nodeTypeId: STORAGE_OUTPUT_TYPE_ID,
48
+ nodeTypeId: "remove-background",
49
+ outputTypeId: STORAGE_OUTPUT_TYPE_ID,
29
50
  keepOutput,
30
51
  inputSchema: uploadFileSchema,
31
52
  outputSchema: uploadFileSchema,
53
+ // AI service - enable circuit breaker with skip fallback
54
+ circuitBreaker: {
55
+ enabled: true,
56
+ failureThreshold: 5,
57
+ resetTimeout: 60000,
58
+ fallback: { type: "skip", passThrough: true },
59
+ },
32
60
  run: ({ data: file, flowId, jobId, storageId, clientId }) => {
33
61
  return Effect.gen(function* () {
34
62
  const flow = {
@@ -80,6 +108,25 @@ export function createRemoveBackgroundNode(
80
108
  const { type, fileName, metadata, metadataJson } =
81
109
  resolveUploadMetadata(file.metadata);
82
110
 
111
+ // Apply file naming if configured
112
+ let outputFileName = fileName;
113
+ if (naming) {
114
+ const namingConfig: FileNamingConfig = {
115
+ ...naming,
116
+ autoSuffix: naming.autoSuffix ?? (() => "nobg"),
117
+ };
118
+ const namingContext = buildNamingContext(
119
+ file,
120
+ {
121
+ flowId,
122
+ jobId,
123
+ nodeId: id,
124
+ nodeType: "remove-background",
125
+ },
126
+ );
127
+ outputFileName = applyFileNaming(file, namingContext, namingConfig);
128
+ }
129
+
83
130
  yield* Effect.logInfo(`Uploading processed file to storage`);
84
131
 
85
132
  // Upload the transformed bytes back to the upload server with error handling
@@ -89,7 +136,7 @@ export function createRemoveBackgroundNode(
89
136
  storageId,
90
137
  size: 0,
91
138
  type,
92
- fileName,
139
+ fileName: outputFileName,
93
140
  lastModified: 0,
94
141
  metadata: metadataJson,
95
142
  flow,
@@ -118,11 +165,24 @@ export function createRemoveBackgroundNode(
118
165
  `Successfully removed background for file ${file.id}`,
119
166
  );
120
167
 
168
+ // Update metadata with new filename if naming was applied
169
+ const updatedMetadata = metadata
170
+ ? {
171
+ ...metadata,
172
+ ...(outputFileName !== fileName && {
173
+ fileName: outputFileName,
174
+ originalName: outputFileName,
175
+ name: outputFileName,
176
+ extension: outputFileName.split(".").pop() || metadata.extension,
177
+ }),
178
+ }
179
+ : result.metadata;
180
+
121
181
  return completeNodeExecution(
122
- metadata
182
+ updatedMetadata
123
183
  ? {
124
184
  ...result,
125
- metadata,
185
+ metadata: updatedMetadata,
126
186
  }
127
187
  : result,
128
188
  );
@@ -1,25 +1,58 @@
1
1
  import {
2
2
  createTransformNode,
3
+ type FileNamingConfig,
3
4
  ImagePlugin,
4
5
  type ResizeParams,
5
6
  STORAGE_OUTPUT_TYPE_ID,
6
7
  } from "@uploadista/core/flow";
7
8
  import { Effect } from "effect";
8
9
 
10
+ /**
11
+ * Creates a resize node that resizes images to specified dimensions.
12
+ *
13
+ * @param id - Unique node identifier
14
+ * @param params - Resize parameters (width, height, fit)
15
+ * @param options - Optional configuration
16
+ * @param options.keepOutput - Whether to keep output in flow results
17
+ * @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // With auto-naming: "photo.jpg" -> "photo-800x600.jpg"
22
+ * const resize = yield* createResizeNode("resize-1", { width: 800, height: 600 }, {
23
+ * naming: { mode: "auto" }
24
+ * });
25
+ * ```
26
+ */
9
27
  export function createResizeNode(
10
28
  id: string,
11
29
  { width, height, fit }: ResizeParams,
12
- options?: { keepOutput?: boolean },
30
+ options?: { keepOutput?: boolean; naming?: FileNamingConfig },
13
31
  ) {
14
32
  return Effect.gen(function* () {
15
33
  const imageService = yield* ImagePlugin;
16
34
 
35
+ // Build naming config with auto suffix for resize
36
+ const namingConfig: FileNamingConfig | undefined = options?.naming
37
+ ? {
38
+ ...options.naming,
39
+ // Provide default auto suffix generator for resize nodes
40
+ autoSuffix:
41
+ options.naming.autoSuffix ??
42
+ ((ctx) => `${ctx.width ?? width}x${ctx.height ?? height}`),
43
+ }
44
+ : undefined;
45
+
17
46
  return yield* createTransformNode({
18
47
  id,
19
48
  name: "Resize",
20
49
  description: "Resizes an image to the specified dimensions",
21
- nodeTypeId: STORAGE_OUTPUT_TYPE_ID,
50
+ nodeTypeId: "resize-image",
51
+ outputTypeId: STORAGE_OUTPUT_TYPE_ID,
22
52
  keepOutput: options?.keepOutput,
53
+ naming: namingConfig,
54
+ nodeType: "resize",
55
+ namingVars: { width, height },
23
56
  transform: (inputBytes) =>
24
57
  imageService.resize(inputBytes, { height, width, fit }),
25
58
  });
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  createTransformNode,
3
+ type FileNamingConfig,
3
4
  ImagePlugin,
4
5
  STORAGE_OUTPUT_TYPE_ID,
5
6
  type TransformImageParams,
@@ -43,38 +44,48 @@ function applyTransformationChain(
43
44
  *
44
45
  * @param id - Unique identifier for this node
45
46
  * @param params - Parameters including the transformations array
46
- * @returns Effect that resolves to a transform node
47
+ * @param options - Optional configuration
48
+ * @param options.keepOutput - Whether to keep output in flow results
49
+ * @param options.naming - File naming configuration (auto suffix: `transformed`)
47
50
  *
48
51
  * @example
49
52
  * ```typescript
50
- * const node = createTransformImageNode("transform-1", {
53
+ * // With auto-naming: "photo.jpg" -> "photo-transformed.jpg"
54
+ * const node = yield* createTransformImageNode("transform-1", {
51
55
  * transformations: [
52
56
  * { type: 'resize', width: 800, height: 600, fit: 'cover' },
53
- * { type: 'brightness', value: 20 },
54
- * {
55
- * type: 'watermark',
56
- * imagePath: 'https://cdn.example.com/watermark.png',
57
- * position: 'bottom-right',
58
- * opacity: 0.5
59
- * }
57
+ * { type: 'brightness', value: 20 }
60
58
  * ]
59
+ * }, {
60
+ * naming: { mode: "auto" }
61
61
  * });
62
62
  * ```
63
63
  */
64
64
  export function createTransformImageNode(
65
65
  id: string,
66
66
  { transformations }: TransformImageParams,
67
- options?: { keepOutput?: boolean },
67
+ options?: { keepOutput?: boolean; naming?: FileNamingConfig },
68
68
  ) {
69
69
  return Effect.gen(function* () {
70
70
  const imageService = yield* ImagePlugin;
71
71
 
72
+ // Build naming config with auto suffix for transform-image
73
+ const namingConfig: FileNamingConfig | undefined = options?.naming
74
+ ? {
75
+ ...options.naming,
76
+ autoSuffix: options.naming.autoSuffix ?? (() => "transformed"),
77
+ }
78
+ : undefined;
79
+
72
80
  return yield* createTransformNode({
73
81
  id,
74
82
  name: "Transform Image",
75
83
  description: `Apply ${transformations.length} transformation${transformations.length === 1 ? "" : "s"} to the image`,
76
- nodeTypeId: STORAGE_OUTPUT_TYPE_ID,
84
+ nodeTypeId: "transform-image",
85
+ outputTypeId: STORAGE_OUTPUT_TYPE_ID,
77
86
  keepOutput: options?.keepOutput,
87
+ naming: namingConfig,
88
+ nodeType: "transform-image",
78
89
  transform: (inputBytes) =>
79
90
  applyTransformationChain(imageService, inputBytes, transformations),
80
91
  });