@uploadista/flow-images-nodes 0.0.18-beta.2 → 0.0.18-beta.3
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 +1 -1
- package/dist/index.d.cts +66 -11
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +66 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/optimize-node.ts +55 -6
- package/src/remove-background-node.ts +56 -4
- package/src/resize-node.ts +33 -1
- package/src/transform-image-node.ts +20 -10
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,outputTypeId: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`,outputTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:a?.keepOutput,transform:(
|
|
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,outputTypeId: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`,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,outputTypeId:t.STORAGE_OUTPUT_TYPE_ID,keepOutput:c,inputSchema:n.uploadFileSchema,outputSchema:n.uploadFileSchema,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`,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`,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,5 +1,5 @@
|
|
|
1
1
|
import * as _uploadista_core_flow0 from "@uploadista/core/flow";
|
|
2
|
-
import { ImageAiPlugin, ImagePlugin, NodeType, OptimizeParams, ResizeParams, TransformImageParams } from "@uploadista/core/flow";
|
|
2
|
+
import { FileNamingConfig, ImageAiPlugin, ImagePlugin, NodeType, OptimizeParams, ResizeParams, TransformImageParams } from "@uploadista/core/flow";
|
|
3
3
|
import * as zod_v4_core0 from "zod/v4/core";
|
|
4
4
|
import * as zod0 from "zod";
|
|
5
5
|
import * as _uploadista_core_errors0 from "@uploadista/core/errors";
|
|
@@ -138,11 +138,29 @@ declare function createDescribeImageNode(id: string, {
|
|
|
138
138
|
}, UploadistaError, ImageAiPlugin>;
|
|
139
139
|
//#endregion
|
|
140
140
|
//#region src/optimize-node.d.ts
|
|
141
|
+
/**
|
|
142
|
+
* Creates an optimize node that optimizes images for web delivery.
|
|
143
|
+
*
|
|
144
|
+
* @param id - Unique node identifier
|
|
145
|
+
* @param params - Optimize parameters (quality, format)
|
|
146
|
+
* @param options - Optional configuration
|
|
147
|
+
* @param options.keepOutput - Whether to keep output in flow results
|
|
148
|
+
* @param options.naming - File naming configuration (auto suffix: `${format}`)
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* // With auto-naming: "photo.jpg" -> "photo-webp.webp"
|
|
153
|
+
* const optimize = yield* createOptimizeNode("opt-1", { quality: 80, format: "webp" }, {
|
|
154
|
+
* naming: { mode: "auto" }
|
|
155
|
+
* });
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
141
158
|
declare function createOptimizeNode(id: string, {
|
|
142
159
|
quality,
|
|
143
160
|
format
|
|
144
161
|
}: OptimizeParams, options?: {
|
|
145
162
|
keepOutput?: boolean;
|
|
163
|
+
naming?: FileNamingConfig;
|
|
146
164
|
}): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
|
|
147
165
|
inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
148
166
|
outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
@@ -172,12 +190,31 @@ declare function createOptimizeNode(id: string, {
|
|
|
172
190
|
}, _uploadista_core_errors0.UploadistaError, ImagePlugin | _uploadista_core_upload0.UploadServer>;
|
|
173
191
|
//#endregion
|
|
174
192
|
//#region src/remove-background-node.d.ts
|
|
193
|
+
/**
|
|
194
|
+
* Creates a remove-background node that removes backgrounds from images using AI.
|
|
195
|
+
*
|
|
196
|
+
* @param id - Unique node identifier
|
|
197
|
+
* @param options - Optional configuration
|
|
198
|
+
* @param options.credentialId - Optional credential ID for AI service
|
|
199
|
+
* @param options.keepOutput - Whether to keep output in flow results
|
|
200
|
+
* @param options.naming - File naming configuration (auto suffix: `nobg`)
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* // With auto-naming: "photo.jpg" -> "photo-nobg.jpg"
|
|
205
|
+
* const node = yield* createRemoveBackgroundNode("remove-bg-1", {
|
|
206
|
+
* naming: { mode: "auto" }
|
|
207
|
+
* });
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
175
210
|
declare function createRemoveBackgroundNode(id: string, {
|
|
176
211
|
credentialId,
|
|
177
|
-
keepOutput
|
|
212
|
+
keepOutput,
|
|
213
|
+
naming
|
|
178
214
|
}?: {
|
|
179
215
|
credentialId?: string;
|
|
180
216
|
keepOutput?: boolean;
|
|
217
|
+
naming?: FileNamingConfig;
|
|
181
218
|
}): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
|
|
182
219
|
inputSchema: zod0.ZodType<{
|
|
183
220
|
id: string;
|
|
@@ -288,12 +325,30 @@ declare function createRemoveBackgroundNode(id: string, {
|
|
|
288
325
|
}, UploadistaError, ImageAiPlugin | UploadServer>;
|
|
289
326
|
//#endregion
|
|
290
327
|
//#region src/resize-node.d.ts
|
|
328
|
+
/**
|
|
329
|
+
* Creates a resize node that resizes images to specified dimensions.
|
|
330
|
+
*
|
|
331
|
+
* @param id - Unique node identifier
|
|
332
|
+
* @param params - Resize parameters (width, height, fit)
|
|
333
|
+
* @param options - Optional configuration
|
|
334
|
+
* @param options.keepOutput - Whether to keep output in flow results
|
|
335
|
+
* @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```typescript
|
|
339
|
+
* // With auto-naming: "photo.jpg" -> "photo-800x600.jpg"
|
|
340
|
+
* const resize = yield* createResizeNode("resize-1", { width: 800, height: 600 }, {
|
|
341
|
+
* naming: { mode: "auto" }
|
|
342
|
+
* });
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
291
345
|
declare function createResizeNode(id: string, {
|
|
292
346
|
width,
|
|
293
347
|
height,
|
|
294
348
|
fit
|
|
295
349
|
}: ResizeParams, options?: {
|
|
296
350
|
keepOutput?: boolean;
|
|
351
|
+
naming?: FileNamingConfig;
|
|
297
352
|
}): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
|
|
298
353
|
inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
299
354
|
outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
@@ -341,21 +396,20 @@ declare function createResizeNode(id: string, {
|
|
|
341
396
|
*
|
|
342
397
|
* @param id - Unique identifier for this node
|
|
343
398
|
* @param params - Parameters including the transformations array
|
|
344
|
-
* @
|
|
399
|
+
* @param options - Optional configuration
|
|
400
|
+
* @param options.keepOutput - Whether to keep output in flow results
|
|
401
|
+
* @param options.naming - File naming configuration (auto suffix: `transformed`)
|
|
345
402
|
*
|
|
346
403
|
* @example
|
|
347
404
|
* ```typescript
|
|
348
|
-
*
|
|
405
|
+
* // With auto-naming: "photo.jpg" -> "photo-transformed.jpg"
|
|
406
|
+
* const node = yield* createTransformImageNode("transform-1", {
|
|
349
407
|
* transformations: [
|
|
350
408
|
* { 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
|
-
* }
|
|
409
|
+
* { type: 'brightness', value: 20 }
|
|
358
410
|
* ]
|
|
411
|
+
* }, {
|
|
412
|
+
* naming: { mode: "auto" }
|
|
359
413
|
* });
|
|
360
414
|
* ```
|
|
361
415
|
*/
|
|
@@ -363,6 +417,7 @@ declare function createTransformImageNode(id: string, {
|
|
|
363
417
|
transformations
|
|
364
418
|
}: TransformImageParams, options?: {
|
|
365
419
|
keepOutput?: boolean;
|
|
420
|
+
naming?: FileNamingConfig;
|
|
366
421
|
}): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
|
|
367
422
|
inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
368
423
|
outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
package/dist/index.d.cts.map
CHANGED
|
@@ -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":";;;;;;;;;;;;iBAcgB,uBAAA;;;;;;IAEoE,MAAA,CAAA,OAAxD,sBAAA,CAAwD,YAAA;;;;;;MAFpE,IAAA,EAAA,MAAA;MAEZ,IAAA,CAAA,EAAA,MAAA,GAAA,SAAA;MAAc,QAAA,CAAA,EAAA,MAAA,GAAA,SAAA;MAAU,MAAA,CAAA,EAAA,MAAwD,GAAA,SAAA;;;;;;;;;;;;;;;;;;IAAA,CAAA,GAAA,SAAA;EAAA,CAAA,EAAA,OAAA,gCAAA,CAAA;;;;
|
|
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";
|
|
@@ -138,11 +138,29 @@ declare function createDescribeImageNode(id: string, {
|
|
|
138
138
|
}, UploadistaError, ImageAiPlugin>;
|
|
139
139
|
//#endregion
|
|
140
140
|
//#region src/optimize-node.d.ts
|
|
141
|
+
/**
|
|
142
|
+
* Creates an optimize node that optimizes images for web delivery.
|
|
143
|
+
*
|
|
144
|
+
* @param id - Unique node identifier
|
|
145
|
+
* @param params - Optimize parameters (quality, format)
|
|
146
|
+
* @param options - Optional configuration
|
|
147
|
+
* @param options.keepOutput - Whether to keep output in flow results
|
|
148
|
+
* @param options.naming - File naming configuration (auto suffix: `${format}`)
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* // With auto-naming: "photo.jpg" -> "photo-webp.webp"
|
|
153
|
+
* const optimize = yield* createOptimizeNode("opt-1", { quality: 80, format: "webp" }, {
|
|
154
|
+
* naming: { mode: "auto" }
|
|
155
|
+
* });
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
141
158
|
declare function createOptimizeNode(id: string, {
|
|
142
159
|
quality,
|
|
143
160
|
format
|
|
144
161
|
}: OptimizeParams, options?: {
|
|
145
162
|
keepOutput?: boolean;
|
|
163
|
+
naming?: FileNamingConfig;
|
|
146
164
|
}): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
|
|
147
165
|
inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
148
166
|
outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
@@ -172,12 +190,31 @@ declare function createOptimizeNode(id: string, {
|
|
|
172
190
|
}, _uploadista_core_errors0.UploadistaError, ImagePlugin | _uploadista_core_upload0.UploadServer>;
|
|
173
191
|
//#endregion
|
|
174
192
|
//#region src/remove-background-node.d.ts
|
|
193
|
+
/**
|
|
194
|
+
* Creates a remove-background node that removes backgrounds from images using AI.
|
|
195
|
+
*
|
|
196
|
+
* @param id - Unique node identifier
|
|
197
|
+
* @param options - Optional configuration
|
|
198
|
+
* @param options.credentialId - Optional credential ID for AI service
|
|
199
|
+
* @param options.keepOutput - Whether to keep output in flow results
|
|
200
|
+
* @param options.naming - File naming configuration (auto suffix: `nobg`)
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* // With auto-naming: "photo.jpg" -> "photo-nobg.jpg"
|
|
205
|
+
* const node = yield* createRemoveBackgroundNode("remove-bg-1", {
|
|
206
|
+
* naming: { mode: "auto" }
|
|
207
|
+
* });
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
175
210
|
declare function createRemoveBackgroundNode(id: string, {
|
|
176
211
|
credentialId,
|
|
177
|
-
keepOutput
|
|
212
|
+
keepOutput,
|
|
213
|
+
naming
|
|
178
214
|
}?: {
|
|
179
215
|
credentialId?: string;
|
|
180
216
|
keepOutput?: boolean;
|
|
217
|
+
naming?: FileNamingConfig;
|
|
181
218
|
}): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
|
|
182
219
|
inputSchema: zod0.ZodType<{
|
|
183
220
|
id: string;
|
|
@@ -288,12 +325,30 @@ declare function createRemoveBackgroundNode(id: string, {
|
|
|
288
325
|
}, UploadistaError, ImageAiPlugin | UploadServer>;
|
|
289
326
|
//#endregion
|
|
290
327
|
//#region src/resize-node.d.ts
|
|
328
|
+
/**
|
|
329
|
+
* Creates a resize node that resizes images to specified dimensions.
|
|
330
|
+
*
|
|
331
|
+
* @param id - Unique node identifier
|
|
332
|
+
* @param params - Resize parameters (width, height, fit)
|
|
333
|
+
* @param options - Optional configuration
|
|
334
|
+
* @param options.keepOutput - Whether to keep output in flow results
|
|
335
|
+
* @param options.naming - File naming configuration (auto suffix: `${width}x${height}`)
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```typescript
|
|
339
|
+
* // With auto-naming: "photo.jpg" -> "photo-800x600.jpg"
|
|
340
|
+
* const resize = yield* createResizeNode("resize-1", { width: 800, height: 600 }, {
|
|
341
|
+
* naming: { mode: "auto" }
|
|
342
|
+
* });
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
291
345
|
declare function createResizeNode(id: string, {
|
|
292
346
|
width,
|
|
293
347
|
height,
|
|
294
348
|
fit
|
|
295
349
|
}: ResizeParams, options?: {
|
|
296
350
|
keepOutput?: boolean;
|
|
351
|
+
naming?: FileNamingConfig;
|
|
297
352
|
}): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
|
|
298
353
|
inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
299
354
|
outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
@@ -341,21 +396,20 @@ declare function createResizeNode(id: string, {
|
|
|
341
396
|
*
|
|
342
397
|
* @param id - Unique identifier for this node
|
|
343
398
|
* @param params - Parameters including the transformations array
|
|
344
|
-
* @
|
|
399
|
+
* @param options - Optional configuration
|
|
400
|
+
* @param options.keepOutput - Whether to keep output in flow results
|
|
401
|
+
* @param options.naming - File naming configuration (auto suffix: `transformed`)
|
|
345
402
|
*
|
|
346
403
|
* @example
|
|
347
404
|
* ```typescript
|
|
348
|
-
*
|
|
405
|
+
* // With auto-naming: "photo.jpg" -> "photo-transformed.jpg"
|
|
406
|
+
* const node = yield* createTransformImageNode("transform-1", {
|
|
349
407
|
* transformations: [
|
|
350
408
|
* { 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
|
-
* }
|
|
409
|
+
* { type: 'brightness', value: 20 }
|
|
358
410
|
* ]
|
|
411
|
+
* }, {
|
|
412
|
+
* naming: { mode: "auto" }
|
|
359
413
|
* });
|
|
360
414
|
* ```
|
|
361
415
|
*/
|
|
@@ -363,6 +417,7 @@ declare function createTransformImageNode(id: string, {
|
|
|
363
417
|
transformations
|
|
364
418
|
}: TransformImageParams, options?: {
|
|
365
419
|
keepOutput?: boolean;
|
|
420
|
+
naming?: FileNamingConfig;
|
|
366
421
|
}): Effect.Effect<_uploadista_core_flow0.FlowNodeData & {
|
|
367
422
|
inputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
|
368
423
|
outputSchema: zod0.ZodType<_uploadista_core_types0.UploadFile, unknown, zod_v4_core0.$ZodTypeInternals<_uploadista_core_types0.UploadFile, unknown>>;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -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":";;;;;;;;;;;;iBAcgB,uBAAA;;;;;;IAEoE,MAAA,CAAA,OAAxD,sBAAA,CAAwD,YAAA;;;;;;MAFpE,IAAA,EAAA,MAAA;MAEZ,IAAA,CAAA,EAAA,MAAA,GAAA,SAAA;MAAc,QAAA,CAAA,EAAA,MAAA,GAAA,SAAA;MAAU,MAAA,CAAA,EAAA,MAAwD,GAAA,SAAA;;;;;;;;;;;;;;;;;;IAAA,CAAA,GAAA,SAAA;EAAA,CAAA,EAAA,OAAA,gCAAA,CAAA;;;;
|
|
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,
|
|
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,outputTypeId:t,keepOutput:o,inputSchema:m,outputSchema:f,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`,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,outputTypeId:a,keepOutput:u,inputSchema:m,outputSchema:m,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`,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`,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
|
package/dist/index.mjs.map
CHANGED
|
@@ -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 outputTypeId: 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 outputTypeId: 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 outputTypeId: 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 outputTypeId: 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 outputTypeId: STORAGE_OUTPUT_TYPE_ID,\n keepOutput: options?.keepOutput,\n transform: (inputBytes) =>\n applyTransformationChain(imageService, inputBytes, transformations),\n });\n });\n}\n"],"mappings":"ueAcA,SAAgB,EACd,EACA,EAGI,EAAE,CACgC,CACtC,GAAM,CAAE,cAAc,IAAO,aAAa,KAAQ,EAElD,OAAO,EAAO,IAAI,WAAa,CAC7B,IAAM,EAAY,KAAK,KAAK,CAE5B,KAAO,KAAK,KAAK,CAAG,EAAY,GAAa,CAC3C,IAAM,EAAW,MAAO,EAAO,eAC7B,MAAM,EAAK,CAAE,OAAQ,OAAQ,CAAC,CAC/B,CAAC,KAAK,EAAO,aAAe,EAAO,QAAQ,KAAK,CAAC,CAAC,CAEnD,GAAI,GAAU,GAAI,CAChB,MAAO,EAAO,QAAQ,OAAO,EAAI,mBAAmB,CACpD,OAGE,EACF,MAAO,EAAO,SACZ,sBAAsB,EAAS,OAAO,gBACvC,CAED,MAAO,EAAO,SAAS,gCAAgC,CAGzD,MAAO,EAAO,MAAM,EAAW,CAGjC,OAAO,MAAO,EAAgB,SAAS,kBAAmB,CACxD,MAAO,OAAO,EAAI,uBAAuB,EAAY,IACtD,CAAC,CAAC,UAAU,EACb,CCpCJ,SAAgB,EACd,EACA,CAAE,eAAc,cAAgE,EAAE,CAClF,CACA,OAAO,EAAO,IAAI,WAAa,CAC7B,IAAM,EAAiB,MAAO,EAE9B,OAAO,MAAO,EAAe,CAC3B,KACA,KAAM,iBACN,YAAa,+BACb,KAAM,EAAS,QACf,aAAc,EACd,aACA,YAAa,EACb,aAAc,EACd,KAAM,CAAE,KAAM,EAAM,SAAQ,QAAO,cAC1B,EAAO,IAAI,WAAa,CAC7B,IAAM,EAAO,CACX,SACA,OAAQ,EACR,QACD,CAEK,EAAU,EAAK,IAGrB,GAAI,CAAC,EACH,OAAO,MAAO,EAAgB,SAAS,kBAAmB,CACxD,MAAO,+CACR,CAAC,CAAC,UAAU,CAGf,MAAO,EAAO,QACZ,6BAA6B,EAAK,GAAG,WAAW,IACjD,CAGD,MAAO,EAAuB,EAAQ,CAGtC,IAAM,EAAU,CACd,WACA,eACD,CAGK,EAAS,MAAO,EACnB,cAAc,EAAS,EAAQ,CAC/B,KACC,EAAO,SAAU,GACf,EAAO,IAAI,WAAa,CAEtB,OADA,MAAO,EAAO,SAAS,2BAA4B,EAAM,CAClD,MAAO,EAAgB,SAAS,kBAAmB,CACxD,MACE,aAAiB,MACb,EAAM,QACN,2BACP,CAAC,CAAC,UAAU,EACb,CACH,CACF,CAOH,OALA,MAAO,EAAO,QACZ,yCAAyC,EAAK,KAC/C,CAGM,EAAsB,CAC3B,YAAa,EAAO,YACpB,OACD,CAAC,EACF,CAEL,CAAC,EACF,CChFJ,MAAMA,EAA6D,CACjE,KAAM,aACN,KAAM,aACN,IAAK,YACL,KAAM,aACP,CAGKC,EAA8D,CAClE,KAAM,MACN,KAAM,OACN,IAAK,MACL,KAAM,OACP,CAED,SAAgB,EACd,EACA,CAAE,UAAS,UACX,EACA,CACA,OAAO,EAAO,IAAI,WAAa,CAC7B,IAAM,EAAe,MAAO,EAE5B,OAAO,MAAO,EAAoB,CAChC,KACA,KAAM,WACN,YAAa,sCACb,aAAc,EACd,WAAY,GAAS,WACrB,WAAY,EAAY,IACtB,EAAO,IACL,EAAa,SAAS,EAAY,CAAE,UAAS,SAAQ,CAAC,CACrD,GAAmB,CAElB,IAAM,EAAU,EAAiB,GAC3B,EAAe,EAAkB,GAGjC,EAAW,EAAK,UAAU,SAMhC,MAAO,CACL,MAAO,EACP,KAAM,EACN,SAPA,GAAY,OAAO,GAAa,SAC5B,EAAS,QAAQ,WAAY,IAAI,IAAe,CAChD,IAAA,GAML,EAIJ,CACJ,CAAC,EACF,CCjDJ,SAAgB,EACd,EACA,CAAE,eAAc,cAAgE,EAAE,CAClF,CACA,OAAO,EAAO,IAAI,WAAa,CAC7B,IAAM,EAAiB,MAAO,EACxB,EAAe,MAAO,EAE5B,OAAO,MAAO,EAAe,CAC3B,KACA,KAAM,oBACN,YAAa,uCACb,KAAM,EAAS,QACf,aAAc,EACd,aACA,YAAa,EACb,aAAc,EACd,KAAM,CAAE,KAAM,EAAM,SAAQ,QAAO,YAAW,cACrC,EAAO,IAAI,WAAa,CAC7B,IAAM,EAAO,CACX,SACA,OAAQ,EACR,QACD,CAEK,EAAU,EAAK,IAGrB,GAAI,CAAC,EACH,OAAO,MAAO,EAAgB,SAAS,kBAAmB,CACxD,MAAO,kDACR,CAAC,CAAC,UAAU,CAGf,MAAO,EAAO,QACZ,gCAAgC,EAAK,GAAG,WAAW,EAAK,MACzD,CAGD,MAAO,EAAuB,EAAQ,CAGtC,IAAM,EAAU,CACd,WACA,eACD,CAmBK,CAAE,aAhBwB,MAAO,EACpC,iBAAiB,EAAS,EAAQ,CAClC,KACC,EAAO,SAAU,GACf,EAAO,IAAI,WAAa,CAEtB,OADA,MAAO,EAAO,SAAS,8BAA+B,EAAM,CACrD,MAAO,EAAgB,SAAS,kBAAmB,CACxD,MACE,aAAiB,MACb,EAAM,QACN,yCACP,CAAC,CAAC,UAAU,EACb,CACH,CACF,CAGG,CAAE,OAAM,WAAU,WAAU,gBAChC,EAAsB,EAAK,SAAS,CAEtC,MAAO,EAAO,QAAQ,sCAAsC,CAG5D,IAAM,EAAS,MAAO,EACnB,cACC,CACE,YACA,KAAM,EACN,OACA,WACA,aAAc,EACd,SAAU,EACV,OACD,CACD,EACA,EACD,CACA,KACC,EAAO,SAAU,GACf,EAAO,IAAI,WAAa,CAKtB,OAJA,MAAO,EAAO,SACZ,kCACA,EACD,CACM,MAAO,EAAgB,SAAS,kBAAmB,CACxD,MACE,aAAiB,MACb,EAAM,QACN,kCACP,CAAC,CAAC,UAAU,EACb,CACH,CACF,CAMH,OAJA,MAAO,EAAO,QACZ,4CAA4C,EAAK,KAClD,CAEM,EACL,EACI,CACE,GAAG,EACH,WACD,CACD,EACL,EACD,CAEL,CAAC,EACF,CC3HJ,SAAgB,EACd,EACA,CAAE,QAAO,SAAQ,OACjB,EACA,CACA,OAAO,EAAO,IAAI,WAAa,CAC7B,IAAM,EAAe,MAAO,EAE5B,OAAO,MAAO,EAAoB,CAChC,KACA,KAAM,SACN,YAAa,+CACb,aAAc,EACd,WAAY,GAAS,WACrB,UAAY,GACV,EAAa,OAAO,EAAY,CAAE,SAAQ,QAAO,MAAK,CAAC,CAC1D,CAAC,EACF,CCRJ,SAAS,EACP,EACA,EACA,EACA,CACA,OAAO,EAAO,OAAO,EAAiB,GAAa,EAAO,IACxD,EAAa,UAAU,EAAO,EAAe,CAC9C,CAuCH,SAAgB,EACd,EACA,CAAE,mBACF,EACA,CACA,OAAO,EAAO,IAAI,WAAa,CAC7B,IAAM,EAAe,MAAO,EAE5B,OAAO,MAAO,EAAoB,CAChC,KACA,KAAM,kBACN,YAAa,SAAS,EAAgB,OAAO,iBAAiB,EAAgB,SAAW,EAAI,GAAK,IAAI,eACtG,aAAc,EACd,WAAY,GAAS,WACrB,UAAY,GACV,EAAyB,EAAc,EAAY,EAAgB,CACtE,CAAC,EACF"}
|
|
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 outputTypeId: 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 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 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 outputTypeId: 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 // 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 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 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.18-beta.
|
|
4
|
+
"version": "0.0.18-beta.3",
|
|
5
5
|
"description": "Image processing nodes for Uploadista Flow",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Uploadista",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@uploadista/core": "0.0.18-beta.
|
|
17
|
+
"@uploadista/core": "0.0.18-beta.3"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"effect": "^3.0.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"tsdown": "0.16.6",
|
|
28
28
|
"vitest": "4.0.13",
|
|
29
29
|
"zod": "4.1.13",
|
|
30
|
-
"@uploadista/typescript-config": "0.0.18-beta.
|
|
30
|
+
"@uploadista/typescript-config": "0.0.18-beta.3"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsdown",
|
package/src/optimize-node.ts
CHANGED
|
@@ -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;
|
|
@@ -36,6 +57,9 @@ export function createOptimizeNode(
|
|
|
36
57
|
description: "Optimizes an image for web delivery",
|
|
37
58
|
outputTypeId: STORAGE_OUTPUT_TYPE_ID,
|
|
38
59
|
keepOutput: options?.keepOutput,
|
|
60
|
+
// Note: naming is handled in transform since format changes extension
|
|
61
|
+
nodeType: "optimize",
|
|
62
|
+
namingVars: { format, quality },
|
|
39
63
|
transform: (inputBytes, file) =>
|
|
40
64
|
Effect.map(
|
|
41
65
|
imageService.optimize(inputBytes, { quality, format }),
|
|
@@ -44,12 +68,37 @@ export function createOptimizeNode(
|
|
|
44
68
|
const newType = formatToMimeType[format];
|
|
45
69
|
const newExtension = formatToExtension[format];
|
|
46
70
|
|
|
47
|
-
//
|
|
71
|
+
// Get original fileName
|
|
48
72
|
const fileName = file.metadata?.fileName;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
let newFileName: string | undefined;
|
|
74
|
+
|
|
75
|
+
if (fileName && typeof fileName === "string") {
|
|
76
|
+
// Apply naming if configured
|
|
77
|
+
if (options?.naming) {
|
|
78
|
+
const namingConfig: FileNamingConfig = {
|
|
79
|
+
...options.naming,
|
|
80
|
+
autoSuffix:
|
|
81
|
+
options.naming.autoSuffix ?? ((ctx) => ctx.format ?? format),
|
|
82
|
+
};
|
|
83
|
+
const namingContext = buildNamingContext(
|
|
84
|
+
file,
|
|
85
|
+
{
|
|
86
|
+
flowId: file.flow?.flowId ?? "",
|
|
87
|
+
jobId: file.flow?.jobId ?? "",
|
|
88
|
+
nodeId: id,
|
|
89
|
+
nodeType: "optimize",
|
|
90
|
+
},
|
|
91
|
+
{ format, quality },
|
|
92
|
+
);
|
|
93
|
+
// Apply naming to get base name with suffix
|
|
94
|
+
const namedFile = applyFileNaming(file, namingContext, namingConfig);
|
|
95
|
+
// Replace extension with new format extension
|
|
96
|
+
newFileName = `${getBaseName(namedFile)}.${newExtension}`;
|
|
97
|
+
} else {
|
|
98
|
+
// No naming config, just update extension
|
|
99
|
+
newFileName = fileName.replace(/\.[^.]+$/, `.${newExtension}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
53
102
|
|
|
54
103
|
return {
|
|
55
104
|
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;
|
|
@@ -80,6 +100,25 @@ export function createRemoveBackgroundNode(
|
|
|
80
100
|
const { type, fileName, metadata, metadataJson } =
|
|
81
101
|
resolveUploadMetadata(file.metadata);
|
|
82
102
|
|
|
103
|
+
// Apply file naming if configured
|
|
104
|
+
let outputFileName = fileName;
|
|
105
|
+
if (naming) {
|
|
106
|
+
const namingConfig: FileNamingConfig = {
|
|
107
|
+
...naming,
|
|
108
|
+
autoSuffix: naming.autoSuffix ?? (() => "nobg"),
|
|
109
|
+
};
|
|
110
|
+
const namingContext = buildNamingContext(
|
|
111
|
+
file,
|
|
112
|
+
{
|
|
113
|
+
flowId,
|
|
114
|
+
jobId,
|
|
115
|
+
nodeId: id,
|
|
116
|
+
nodeType: "remove-background",
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
outputFileName = applyFileNaming(file, namingContext, namingConfig);
|
|
120
|
+
}
|
|
121
|
+
|
|
83
122
|
yield* Effect.logInfo(`Uploading processed file to storage`);
|
|
84
123
|
|
|
85
124
|
// Upload the transformed bytes back to the upload server with error handling
|
|
@@ -89,7 +128,7 @@ export function createRemoveBackgroundNode(
|
|
|
89
128
|
storageId,
|
|
90
129
|
size: 0,
|
|
91
130
|
type,
|
|
92
|
-
fileName,
|
|
131
|
+
fileName: outputFileName,
|
|
93
132
|
lastModified: 0,
|
|
94
133
|
metadata: metadataJson,
|
|
95
134
|
flow,
|
|
@@ -118,11 +157,24 @@ export function createRemoveBackgroundNode(
|
|
|
118
157
|
`Successfully removed background for file ${file.id}`,
|
|
119
158
|
);
|
|
120
159
|
|
|
160
|
+
// Update metadata with new filename if naming was applied
|
|
161
|
+
const updatedMetadata = metadata
|
|
162
|
+
? {
|
|
163
|
+
...metadata,
|
|
164
|
+
...(outputFileName !== fileName && {
|
|
165
|
+
fileName: outputFileName,
|
|
166
|
+
originalName: outputFileName,
|
|
167
|
+
name: outputFileName,
|
|
168
|
+
extension: outputFileName.split(".").pop() || metadata.extension,
|
|
169
|
+
}),
|
|
170
|
+
}
|
|
171
|
+
: result.metadata;
|
|
172
|
+
|
|
121
173
|
return completeNodeExecution(
|
|
122
|
-
|
|
174
|
+
updatedMetadata
|
|
123
175
|
? {
|
|
124
176
|
...result,
|
|
125
|
-
metadata,
|
|
177
|
+
metadata: updatedMetadata,
|
|
126
178
|
}
|
|
127
179
|
: result,
|
|
128
180
|
);
|
package/src/resize-node.ts
CHANGED
|
@@ -1,25 +1,57 @@
|
|
|
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
50
|
outputTypeId: STORAGE_OUTPUT_TYPE_ID,
|
|
22
51
|
keepOutput: options?.keepOutput,
|
|
52
|
+
naming: namingConfig,
|
|
53
|
+
nodeType: "resize",
|
|
54
|
+
namingVars: { width, height },
|
|
23
55
|
transform: (inputBytes) =>
|
|
24
56
|
imageService.resize(inputBytes, { height, width, fit }),
|
|
25
57
|
});
|
|
@@ -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,47 @@ function applyTransformationChain(
|
|
|
43
44
|
*
|
|
44
45
|
* @param id - Unique identifier for this node
|
|
45
46
|
* @param params - Parameters including the transformations array
|
|
46
|
-
* @
|
|
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
|
-
*
|
|
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
84
|
outputTypeId: STORAGE_OUTPUT_TYPE_ID,
|
|
77
85
|
keepOutput: options?.keepOutput,
|
|
86
|
+
naming: namingConfig,
|
|
87
|
+
nodeType: "transform-image",
|
|
78
88
|
transform: (inputBytes) =>
|
|
79
89
|
applyTransformationChain(imageService, inputBytes, transformations),
|
|
80
90
|
});
|