@uploadista/flow-utility-nodes 0.0.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/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-check.log +5 -0
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/dist/conditional-node.d.ts +25 -0
- package/dist/conditional-node.d.ts.map +1 -0
- package/dist/conditional-node.js +34 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/merge-node.d.ts +12 -0
- package/dist/merge-node.d.ts.map +1 -0
- package/dist/merge-node.js +77 -0
- package/dist/multiplex-node.d.ts +42 -0
- package/dist/multiplex-node.d.ts.map +1 -0
- package/dist/multiplex-node.js +67 -0
- package/dist/nodes/conditional-node.d.ts +5 -0
- package/dist/nodes/conditional-node.d.ts.map +1 -0
- package/dist/nodes/conditional-node.js +19 -0
- package/dist/nodes/index.d.ts +5 -0
- package/dist/nodes/index.d.ts.map +1 -0
- package/dist/nodes/index.js +4 -0
- package/dist/nodes/merge-node.d.ts +7 -0
- package/dist/nodes/merge-node.d.ts.map +1 -0
- package/dist/nodes/merge-node.js +82 -0
- package/dist/nodes/multiplex-node.d.ts +7 -0
- package/dist/nodes/multiplex-node.d.ts.map +1 -0
- package/dist/nodes/multiplex-node.js +57 -0
- package/dist/nodes/zip-node.d.ts +8 -0
- package/dist/nodes/zip-node.d.ts.map +1 -0
- package/dist/nodes/zip-node.js +64 -0
- package/dist/types/conditional-node.d.ts +21 -0
- package/dist/types/conditional-node.d.ts.map +1 -0
- package/dist/types/conditional-node.js +13 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/merge-node.d.ts +11 -0
- package/dist/types/merge-node.d.ts.map +1 -0
- package/dist/types/merge-node.js +6 -0
- package/dist/types/multiplex-node.d.ts +10 -0
- package/dist/types/multiplex-node.d.ts.map +1 -0
- package/dist/types/multiplex-node.js +5 -0
- package/dist/types/zip-node.d.ts +8 -0
- package/dist/types/zip-node.d.ts.map +1 -0
- package/dist/types/zip-node.js +6 -0
- package/dist/zip-node.d.ts +9 -0
- package/dist/zip-node.d.ts.map +1 -0
- package/dist/zip-node.js +65 -0
- package/package.json +35 -0
- package/src/nodes/conditional-node.ts +28 -0
- package/src/nodes/index.ts +4 -0
- package/src/nodes/merge-node.ts +111 -0
- package/src/nodes/multiplex-node.ts +87 -0
- package/src/nodes/zip-node.ts +92 -0
- package/src/types/conditional-node.ts +16 -0
- package/src/types/index.ts +4 -0
- package/src/types/merge-node.ts +9 -0
- package/src/types/multiplex-node.ts +8 -0
- package/src/types/zip-node.ts +9 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 uploadista
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# @uploadista/flow-utility-nodes
|
|
2
|
+
|
|
3
|
+
Flow utility nodes for Uploadista. Provides conditional routing, merging, multiplexing, and data transformation operations in upload pipelines.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Utility nodes enable complex flow logic without custom code:
|
|
8
|
+
|
|
9
|
+
- **Conditional Node**: Route uploads based on file properties
|
|
10
|
+
- **Merge Node**: Combine multiple inputs into one
|
|
11
|
+
- **Multiplex Node**: Split single input across multiple outputs
|
|
12
|
+
- **Zip Node**: Archive multiple files together
|
|
13
|
+
|
|
14
|
+
Perfect for building sophisticated upload workflows.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @uploadista/flow-utility-nodes
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @uploadista/flow-utility-nodes
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { conditionalNode, mergeNode, multiplexNode, zipNode } from "@uploadista/flow-utility-nodes";
|
|
28
|
+
import { Effect } from "effect";
|
|
29
|
+
|
|
30
|
+
// Route based on file properties
|
|
31
|
+
const flow = {
|
|
32
|
+
nodes: [
|
|
33
|
+
{ id: "input", type: "input" },
|
|
34
|
+
{
|
|
35
|
+
id: "router",
|
|
36
|
+
type: "conditional",
|
|
37
|
+
params: {
|
|
38
|
+
field: "mimeType",
|
|
39
|
+
operator: "contains",
|
|
40
|
+
value: "image",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{ id: "output", type: "output" },
|
|
44
|
+
],
|
|
45
|
+
edges: [
|
|
46
|
+
{ from: "input", to: "router" },
|
|
47
|
+
{ from: "router", to: "output" },
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- ✅ **Conditional Routing**: Route based on file properties
|
|
55
|
+
- ✅ **Data Merging**: Combine multiple streams
|
|
56
|
+
- ✅ **Multiplexing**: Split to multiple outputs
|
|
57
|
+
- ✅ **Type Safe**: Full TypeScript support
|
|
58
|
+
- ✅ **No Custom Code**: Visual flow building
|
|
59
|
+
|
|
60
|
+
## Node Types
|
|
61
|
+
|
|
62
|
+
### Conditional Node
|
|
63
|
+
|
|
64
|
+
Route inputs based on file properties.
|
|
65
|
+
|
|
66
|
+
**Parameters**:
|
|
67
|
+
```typescript
|
|
68
|
+
{
|
|
69
|
+
field: "mimeType" | "size" | "width" | "height" | "extension",
|
|
70
|
+
operator: "equals" | "notEquals" | "greaterThan" | "lessThan" | "contains" | "startsWith",
|
|
71
|
+
value: string | number
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Example**: Route images to resize, documents to compress
|
|
76
|
+
```typescript
|
|
77
|
+
{
|
|
78
|
+
type: "conditional",
|
|
79
|
+
params: {
|
|
80
|
+
field: "mimeType",
|
|
81
|
+
operator: "contains",
|
|
82
|
+
value: "image",
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Merge Node
|
|
88
|
+
|
|
89
|
+
Combine multiple inputs into batch.
|
|
90
|
+
|
|
91
|
+
**Parameters**:
|
|
92
|
+
```typescript
|
|
93
|
+
{
|
|
94
|
+
strategy: "concat" | "batch",
|
|
95
|
+
separator?: string,
|
|
96
|
+
inputCount: 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Example**: Batch 5 uploads before processing
|
|
101
|
+
```typescript
|
|
102
|
+
{
|
|
103
|
+
type: "merge",
|
|
104
|
+
params: {
|
|
105
|
+
strategy: "batch",
|
|
106
|
+
inputCount: 5,
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Multiplex Node
|
|
112
|
+
|
|
113
|
+
Split input to multiple independent paths.
|
|
114
|
+
|
|
115
|
+
**Parameters**:
|
|
116
|
+
```typescript
|
|
117
|
+
{
|
|
118
|
+
outputCount: 2 | 3 | 4 | 5
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Example**: Send to S3 and archive simultaneously
|
|
123
|
+
```typescript
|
|
124
|
+
{
|
|
125
|
+
type: "multiplex",
|
|
126
|
+
params: {
|
|
127
|
+
outputCount: 2,
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Zip Node
|
|
133
|
+
|
|
134
|
+
Archive multiple files (see `@uploadista/flow-utility-zipjs`).
|
|
135
|
+
|
|
136
|
+
## Use Cases
|
|
137
|
+
|
|
138
|
+
### Case 1: Smart Routing
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
Input → Conditional
|
|
142
|
+
├─ Image → Resize
|
|
143
|
+
├─ PDF → Compress
|
|
144
|
+
└─ Document → Archive
|
|
145
|
+
→ Output
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Case 2: Batch Processing
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
Input 1 ┐
|
|
152
|
+
Input 2 ├─ Merge (batch 3) → Process → Output
|
|
153
|
+
Input 3 ┘
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Case 3: Multi-Destination
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
Input → Multiplex ├─ Store to S3
|
|
160
|
+
├─ Archive to GCS
|
|
161
|
+
└─ Notify Webhook
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## API Reference
|
|
165
|
+
|
|
166
|
+
All nodes exported from main entry point.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import {
|
|
170
|
+
conditionalNode,
|
|
171
|
+
mergeNode,
|
|
172
|
+
multiplexNode,
|
|
173
|
+
zipNode,
|
|
174
|
+
} from "@uploadista/flow-utility-nodes";
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Examples
|
|
178
|
+
|
|
179
|
+
### Example 1: Image/Document Routing
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const flow = {
|
|
183
|
+
nodes: [
|
|
184
|
+
{ id: "input", type: "input" },
|
|
185
|
+
{
|
|
186
|
+
id: "router",
|
|
187
|
+
type: "conditional",
|
|
188
|
+
params: {
|
|
189
|
+
field: "mimeType",
|
|
190
|
+
operator: "contains",
|
|
191
|
+
value: "image",
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
{ id: "resize", type: "resize", params: { width: 800 } },
|
|
195
|
+
{ id: "s3", type: "s3", params: { bucket: "images" } },
|
|
196
|
+
{ id: "pdf-store", type: "s3", params: { bucket: "documents" } },
|
|
197
|
+
{ id: "output", type: "output" },
|
|
198
|
+
],
|
|
199
|
+
edges: [
|
|
200
|
+
{ from: "input", to: "router" },
|
|
201
|
+
{ from: "router", true: "resize", false: "pdf-store" },
|
|
202
|
+
{ from: "resize", to: "s3" },
|
|
203
|
+
{ from: "s3", to: "output" },
|
|
204
|
+
{ from: "pdf-store", to: "output" },
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Example 2: Batch Processing
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const batchFlow = {
|
|
213
|
+
nodes: [
|
|
214
|
+
{ id: "input1", type: "input" },
|
|
215
|
+
{ id: "input2", type: "input" },
|
|
216
|
+
{ id: "input3", type: "input" },
|
|
217
|
+
{
|
|
218
|
+
id: "merge",
|
|
219
|
+
type: "merge",
|
|
220
|
+
params: { strategy: "batch", inputCount: 3 },
|
|
221
|
+
},
|
|
222
|
+
{ id: "process", type: "custom", params: {} },
|
|
223
|
+
{ id: "output", type: "output" },
|
|
224
|
+
],
|
|
225
|
+
edges: [
|
|
226
|
+
{ from: "input1", to: "merge" },
|
|
227
|
+
{ from: "input2", to: "merge" },
|
|
228
|
+
{ from: "input3", to: "merge" },
|
|
229
|
+
{ from: "merge", to: "process" },
|
|
230
|
+
{ from: "process", to: "output" },
|
|
231
|
+
],
|
|
232
|
+
};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Example 3: Multi-Path Distribution
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const multiPath = {
|
|
239
|
+
nodes: [
|
|
240
|
+
{ id: "input", type: "input" },
|
|
241
|
+
{ id: "split", type: "multiplex", params: { outputCount: 3 } },
|
|
242
|
+
{ id: "s3", type: "s3", params: { bucket: "primary" } },
|
|
243
|
+
{ id: "gcs", type: "gcs", params: { bucket: "backup" } },
|
|
244
|
+
{ id: "archive", type: "zip", params: {} },
|
|
245
|
+
{ id: "output", type: "output" },
|
|
246
|
+
],
|
|
247
|
+
edges: [
|
|
248
|
+
{ from: "input", to: "split" },
|
|
249
|
+
{ from: "split", index: 0, to: "s3" },
|
|
250
|
+
{ from: "split", index: 1, to: "gcs" },
|
|
251
|
+
{ from: "split", index: 2, to: "archive" },
|
|
252
|
+
{ from: "s3", to: "output" },
|
|
253
|
+
{ from: "gcs", to: "output" },
|
|
254
|
+
{ from: "archive", to: "output" },
|
|
255
|
+
],
|
|
256
|
+
};
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Configuration
|
|
260
|
+
|
|
261
|
+
Nodes configured via `params` object in flow definition:
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
{
|
|
265
|
+
id: "node-id",
|
|
266
|
+
type: "conditional",
|
|
267
|
+
params: {
|
|
268
|
+
field: "mimeType",
|
|
269
|
+
operator: "contains",
|
|
270
|
+
value: "image",
|
|
271
|
+
},
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Related Packages
|
|
276
|
+
|
|
277
|
+
- [@uploadista/core](../../core) - Core flow types
|
|
278
|
+
- [@uploadista/flow-utility-zipjs](../zipjs) - Archive node
|
|
279
|
+
- [@uploadista/flow-images-nodes](../images/nodes) - Image utilities
|
|
280
|
+
- [@uploadista/server](../../servers/server) - Upload server
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
See [LICENSE](../../../LICENSE) in the main repository.
|
|
285
|
+
|
|
286
|
+
## See Also
|
|
287
|
+
|
|
288
|
+
- [FLOW_NODES.md](../FLOW_NODES.md) - Complete node gallery
|
|
289
|
+
- [Server Setup Guide](../../../SERVER_SETUP.md) - Flow integration
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type FlowFile } from "@uploadista/flow-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export declare const conditionalParamsSchema: z.ZodObject<{
|
|
4
|
+
field: z.ZodEnum<{
|
|
5
|
+
mimeType: "mimeType";
|
|
6
|
+
size: "size";
|
|
7
|
+
width: "width";
|
|
8
|
+
height: "height";
|
|
9
|
+
extension: "extension";
|
|
10
|
+
}>;
|
|
11
|
+
operator: z.ZodEnum<{
|
|
12
|
+
equals: "equals";
|
|
13
|
+
notEquals: "notEquals";
|
|
14
|
+
greaterThan: "greaterThan";
|
|
15
|
+
lessThan: "lessThan";
|
|
16
|
+
contains: "contains";
|
|
17
|
+
startsWith: "startsWith";
|
|
18
|
+
}>;
|
|
19
|
+
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
20
|
+
trueBranch: z.ZodString;
|
|
21
|
+
falseBranch: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
export type ConditionalParams = z.infer<typeof conditionalParamsSchema>;
|
|
24
|
+
export declare function createConditionalNode(id: string, { field, operator, value, trueBranch, falseBranch }: ConditionalParams): import("@uploadista/flow-core").FlowNode<FlowFile, FlowFile>;
|
|
25
|
+
//# sourceMappingURL=conditional-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditional-node.d.ts","sourceRoot":"","sources":["../src/conditional-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;iBAalC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,iBAAiB,gEAmBvE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createFlowNode, flowFileSchema, NodeType, } from "@uploadista/flow-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const conditionalParamsSchema = z.object({
|
|
4
|
+
field: z.enum(["mimeType", "size", "width", "height", "extension"]),
|
|
5
|
+
operator: z.enum([
|
|
6
|
+
"equals",
|
|
7
|
+
"notEquals",
|
|
8
|
+
"greaterThan",
|
|
9
|
+
"lessThan",
|
|
10
|
+
"contains",
|
|
11
|
+
"startsWith",
|
|
12
|
+
]),
|
|
13
|
+
value: z.union([z.string(), z.number()]),
|
|
14
|
+
trueBranch: z.string(),
|
|
15
|
+
falseBranch: z.string().optional(),
|
|
16
|
+
});
|
|
17
|
+
// Define schemas for input and output
|
|
18
|
+
export function createConditionalNode(id, { field, operator, value, trueBranch, falseBranch }) {
|
|
19
|
+
return createFlowNode({
|
|
20
|
+
id,
|
|
21
|
+
name: "Conditional Router",
|
|
22
|
+
description: `Routes flow based on ${field} ${operator} ${value}`,
|
|
23
|
+
type: NodeType.conditional,
|
|
24
|
+
inputSchema: flowFileSchema,
|
|
25
|
+
outputSchema: flowFileSchema,
|
|
26
|
+
condition: { field, operator, value },
|
|
27
|
+
branches: [trueBranch, falseBranch].filter((branch) => branch !== undefined),
|
|
28
|
+
run: async ({ data }) => {
|
|
29
|
+
// The actual routing logic is handled by the flow engine
|
|
30
|
+
// This node just passes through the data
|
|
31
|
+
return data;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type FlowFile, type FlowFileBatch } from "@uploadista/flow-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export declare const mergeParamsSchema: z.ZodObject<{
|
|
4
|
+
strategy: z.ZodDefault<z.ZodEnum<{
|
|
5
|
+
concat: "concat";
|
|
6
|
+
batch: "batch";
|
|
7
|
+
}>>;
|
|
8
|
+
separator: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type MergeParams = z.infer<typeof mergeParamsSchema>;
|
|
11
|
+
export declare function createMergeNode(id: string, { strategy, separator: _separator }: MergeParams): import("@uploadista/flow-core").FlowNode<Record<string, FlowFile | FlowFileBatch>, FlowFile | FlowFileBatch>;
|
|
12
|
+
//# sourceMappingURL=merge-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-node.d.ts","sourceRoot":"","sources":["../src/merge-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,aAAa,EAInB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB;;;;;;iBAG5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAO5D,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,WAAW,gHAwEjD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createFlowNode, flowDataSchema, isFlowFile, NodeType, } from "@uploadista/flow-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const mergeParamsSchema = z.object({
|
|
4
|
+
strategy: z.enum(["concat", "batch"]).default("batch"),
|
|
5
|
+
separator: z.string().default("\n").optional(),
|
|
6
|
+
});
|
|
7
|
+
// Define schemas for input and output
|
|
8
|
+
const inputSchema = z.record(z.string(), flowDataSchema);
|
|
9
|
+
const outputSchema = flowDataSchema;
|
|
10
|
+
export function createMergeNode(id, { strategy, separator: _separator }) {
|
|
11
|
+
return createFlowNode({
|
|
12
|
+
id,
|
|
13
|
+
name: "Merge Files",
|
|
14
|
+
description: `Merges multiple files using ${strategy} strategy`,
|
|
15
|
+
type: NodeType.merge,
|
|
16
|
+
inputSchema,
|
|
17
|
+
outputSchema,
|
|
18
|
+
multiInput: true,
|
|
19
|
+
run: async ({ data: inputs }) => {
|
|
20
|
+
if (!inputs) {
|
|
21
|
+
throw new Error("No inputs provided to merge node");
|
|
22
|
+
}
|
|
23
|
+
const inputFiles = [];
|
|
24
|
+
// Collect all input files
|
|
25
|
+
for (const [_sourceId, input] of Object.entries(inputs)) {
|
|
26
|
+
if (isFlowFile(input)) {
|
|
27
|
+
// Handle single FlowFile
|
|
28
|
+
inputFiles.push(input);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Handle FlowFileBatch
|
|
32
|
+
inputFiles.push(...input.files);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (inputFiles.length === 0) {
|
|
36
|
+
throw new Error("No files to merge");
|
|
37
|
+
}
|
|
38
|
+
if (strategy === "batch") {
|
|
39
|
+
// Return as a batch
|
|
40
|
+
return {
|
|
41
|
+
files: inputFiles,
|
|
42
|
+
metadata: {
|
|
43
|
+
batchId: crypto.randomUUID(),
|
|
44
|
+
totalSize: inputFiles.reduce((sum, file) => sum + file.metadata.size, 0),
|
|
45
|
+
fileCount: inputFiles.length,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
else if (strategy === "concat") {
|
|
50
|
+
// Concatenate all files into one
|
|
51
|
+
const inputBytesArray = [];
|
|
52
|
+
let totalSize = 0;
|
|
53
|
+
for (const file of inputFiles) {
|
|
54
|
+
inputBytesArray.push(file.inputBytes);
|
|
55
|
+
totalSize += file.inputBytes.length;
|
|
56
|
+
}
|
|
57
|
+
const mergedInputBytes = new Uint8Array(totalSize);
|
|
58
|
+
let offset = 0;
|
|
59
|
+
for (const inputBytes of inputBytesArray) {
|
|
60
|
+
mergedInputBytes.set(inputBytes, offset);
|
|
61
|
+
offset += inputBytes.length;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
path: `merged_${crypto.randomUUID()}`,
|
|
65
|
+
inputBytes: mergedInputBytes,
|
|
66
|
+
metadata: {
|
|
67
|
+
mimeType: "application/octet-stream",
|
|
68
|
+
size: totalSize,
|
|
69
|
+
originalName: `merged_${inputFiles.length}_files`,
|
|
70
|
+
extension: "bin",
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`Unknown merge strategy: ${strategy}`);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const multiplexParamsSchema: z.ZodObject<{
|
|
3
|
+
outputCount: z.ZodNumber;
|
|
4
|
+
strategy: z.ZodDefault<z.ZodEnum<{
|
|
5
|
+
split: "split";
|
|
6
|
+
copy: "copy";
|
|
7
|
+
}>>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export type MultiplexParams = z.infer<typeof multiplexParamsSchema>;
|
|
10
|
+
export declare function createMultiplexNode(id: string, { outputCount, strategy }: MultiplexParams): import("@uploadista/flow-core").FlowNode<{
|
|
11
|
+
path: string;
|
|
12
|
+
inputBytes: Uint8Array<ArrayBufferLike>;
|
|
13
|
+
metadata: {
|
|
14
|
+
mimeType: string;
|
|
15
|
+
size: number;
|
|
16
|
+
width?: number | undefined;
|
|
17
|
+
height?: number | undefined;
|
|
18
|
+
format?: string | undefined;
|
|
19
|
+
originalName?: string | undefined;
|
|
20
|
+
extension?: string | undefined;
|
|
21
|
+
};
|
|
22
|
+
}, {
|
|
23
|
+
files: {
|
|
24
|
+
path: string;
|
|
25
|
+
inputBytes: Uint8Array<ArrayBufferLike>;
|
|
26
|
+
metadata: {
|
|
27
|
+
mimeType: string;
|
|
28
|
+
size: number;
|
|
29
|
+
width?: number | undefined;
|
|
30
|
+
height?: number | undefined;
|
|
31
|
+
format?: string | undefined;
|
|
32
|
+
originalName?: string | undefined;
|
|
33
|
+
extension?: string | undefined;
|
|
34
|
+
};
|
|
35
|
+
}[];
|
|
36
|
+
metadata?: {
|
|
37
|
+
batchId: string;
|
|
38
|
+
totalSize: number;
|
|
39
|
+
fileCount: number;
|
|
40
|
+
} | undefined;
|
|
41
|
+
}>;
|
|
42
|
+
//# sourceMappingURL=multiplex-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiplex-node.d.ts","sourceRoot":"","sources":["../src/multiplex-node.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,qBAAqB;;;;;;iBAGhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIpE,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,MAAM,EACV,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoE3C"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createFlowNode, flowFileBatchSchema, flowFileSchema, NodeType, } from "@uploadista/flow-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const multiplexParamsSchema = z.object({
|
|
4
|
+
outputCount: z.number().min(1).max(10),
|
|
5
|
+
strategy: z.enum(["copy", "split"]).default("copy"),
|
|
6
|
+
});
|
|
7
|
+
// Define schemas for input and output
|
|
8
|
+
export function createMultiplexNode(id, { outputCount, strategy }) {
|
|
9
|
+
return createFlowNode({
|
|
10
|
+
id,
|
|
11
|
+
name: "Multiplex",
|
|
12
|
+
description: `Splits input into ${outputCount} parallel outputs using ${strategy} strategy`,
|
|
13
|
+
type: NodeType.multiplex,
|
|
14
|
+
inputSchema: flowFileSchema,
|
|
15
|
+
outputSchema: flowFileBatchSchema,
|
|
16
|
+
multiOutput: true,
|
|
17
|
+
run: async ({ data }) => {
|
|
18
|
+
if (strategy === "copy") {
|
|
19
|
+
// Copy the same file to multiple outputs
|
|
20
|
+
const files = Array.from({ length: outputCount }, (_, index) => ({
|
|
21
|
+
path: `${data.path}_copy_${index}`,
|
|
22
|
+
inputBytes: data.inputBytes,
|
|
23
|
+
metadata: {
|
|
24
|
+
...data.metadata,
|
|
25
|
+
originalName: `${data.metadata.originalName || data.path}_copy_${index}`,
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
return {
|
|
29
|
+
files,
|
|
30
|
+
metadata: {
|
|
31
|
+
batchId: crypto.randomUUID(),
|
|
32
|
+
totalSize: files.length * data.metadata.size,
|
|
33
|
+
fileCount: files.length,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
else if (strategy === "split") {
|
|
38
|
+
// Split the file into chunks
|
|
39
|
+
const chunkSize = Math.ceil(data.inputBytes.length / outputCount);
|
|
40
|
+
const files = [];
|
|
41
|
+
for (let i = 0; i < outputCount; i++) {
|
|
42
|
+
const start = i * chunkSize;
|
|
43
|
+
const end = Math.min(start + chunkSize, data.inputBytes.length);
|
|
44
|
+
const chunk = data.inputBytes.subarray(start, end);
|
|
45
|
+
files.push({
|
|
46
|
+
path: `${data.path}_part_${i}`,
|
|
47
|
+
inputBytes: chunk,
|
|
48
|
+
metadata: {
|
|
49
|
+
...data.metadata,
|
|
50
|
+
size: chunk.length,
|
|
51
|
+
originalName: `${data.metadata.originalName || data.path}_part_${i}`,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
files,
|
|
57
|
+
metadata: {
|
|
58
|
+
batchId: crypto.randomUUID(),
|
|
59
|
+
totalSize: data.inputBytes.length,
|
|
60
|
+
fileCount: files.length,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`Unknown multiplex strategy: ${strategy}`);
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type UploadFile } from "@uploadista/core/types";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import type { ConditionalParams } from "@/types/conditional-node";
|
|
4
|
+
export declare function createConditionalNode(id: string, { field, operator, value }: ConditionalParams): Effect.Effect<import("@uploadista/core").FlowNode<UploadFile, UploadFile, import("@uploadista/core").UploadistaError>, never, never>;
|
|
5
|
+
//# sourceMappingURL=conditional-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditional-node.d.ts","sourceRoot":"","sources":["../../src/nodes/conditional-node.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,iBAAiB,wIAgB9C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { completeNodeExecution, createFlowNode, NodeType, } from "@uploadista/core/flow";
|
|
2
|
+
import { uploadFileSchema } from "@uploadista/core/types";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
export function createConditionalNode(id, { field, operator, value }) {
|
|
5
|
+
return createFlowNode({
|
|
6
|
+
id,
|
|
7
|
+
name: "Conditional Router",
|
|
8
|
+
description: `Routes flow based on ${field} ${operator} ${value}`,
|
|
9
|
+
type: NodeType.conditional,
|
|
10
|
+
inputSchema: uploadFileSchema,
|
|
11
|
+
outputSchema: uploadFileSchema,
|
|
12
|
+
condition: { field, operator, value },
|
|
13
|
+
run: ({ data }) => {
|
|
14
|
+
// The actual routing logic is handled by the flow engine
|
|
15
|
+
// This node just passes through the data
|
|
16
|
+
return Effect.succeed(completeNodeExecution(data));
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nodes/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { UploadistaError } from "@uploadista/core/errors";
|
|
2
|
+
import { type UploadFile } from "@uploadista/core/types";
|
|
3
|
+
import { UploadServer } from "@uploadista/core/upload";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
import type { MergeParams } from "@/types/merge-node";
|
|
6
|
+
export declare function createMergeNode(id: string, { strategy, separator: _separator }: MergeParams): Effect.Effect<import("@uploadista/core").FlowNode<Record<string, UploadFile>, UploadFile, UploadistaError>, never, UploadServer>;
|
|
7
|
+
//# sourceMappingURL=merge-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-node.d.ts","sourceRoot":"","sources":["../../src/nodes/merge-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAM1D,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKtD,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,WAAW,oIA6FjD"}
|