ugcinc 4.1.9 → 4.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/automations/nodes/destructure.d.ts +4 -0
- package/dist/automations/nodes/destructure.js +24 -0
- package/dist/automations/nodes/image-composer.d.ts +20 -0
- package/dist/automations/nodes/image-composer.js +84 -0
- package/dist/automations/nodes/video-composer.d.ts +17 -1
- package/dist/automations/nodes/video-composer.js +176 -0
- package/dist/automations/selection.d.ts +24 -0
- package/dist/automations/selection.js +61 -0
- package/dist/automations/types.d.ts +19 -8
- package/dist/index.d.ts +7 -3
- package/dist/index.js +9 -1
- package/package.json +2 -2
|
@@ -24,3 +24,7 @@ declare const definition: import("./types").NodeDefinition<"generator", {
|
|
|
24
24
|
export default definition;
|
|
25
25
|
export type { DestructureSelection, IndexExpression };
|
|
26
26
|
export type DestructureNodeConfig = typeof definition.defaults;
|
|
27
|
+
/**
|
|
28
|
+
* Convert a structured IndexExpression to an array of indexes.
|
|
29
|
+
*/
|
|
30
|
+
export declare function indexExpressionToIndexes(expr: IndexExpression): number[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.indexExpressionToIndexes = indexExpressionToIndexes;
|
|
3
4
|
const types_1 = require("./types");
|
|
4
5
|
const definition = (0, types_1.defineNode)({
|
|
5
6
|
nodeId: 'destructure',
|
|
@@ -63,3 +64,26 @@ const definition = (0, types_1.defineNode)({
|
|
|
63
64
|
},
|
|
64
65
|
});
|
|
65
66
|
exports.default = definition;
|
|
67
|
+
// =============================================================================
|
|
68
|
+
// Utility Functions for Destructure Execution
|
|
69
|
+
// =============================================================================
|
|
70
|
+
/**
|
|
71
|
+
* Convert a structured IndexExpression to an array of indexes.
|
|
72
|
+
*/
|
|
73
|
+
function indexExpressionToIndexes(expr) {
|
|
74
|
+
switch (expr.type) {
|
|
75
|
+
case 'single':
|
|
76
|
+
return [expr.index];
|
|
77
|
+
case 'range': {
|
|
78
|
+
const result = [];
|
|
79
|
+
for (let i = expr.start; i <= expr.end; i++) {
|
|
80
|
+
result.push(i);
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
case 'list':
|
|
85
|
+
return expr.indexes;
|
|
86
|
+
default:
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -45,3 +45,23 @@ declare const definition: import("./types").NodeDefinition<"generator", {
|
|
|
45
45
|
}, false>;
|
|
46
46
|
export default definition;
|
|
47
47
|
export type ImageComposerNodeConfig = typeof definition.defaults;
|
|
48
|
+
/**
|
|
49
|
+
* Prepared input data for image composer render job
|
|
50
|
+
*/
|
|
51
|
+
export interface ImageComposerRenderInput {
|
|
52
|
+
/** Resolved editor config with crop boundaries resolved */
|
|
53
|
+
config: ImageEditorNodeConfig;
|
|
54
|
+
/** Map of inputId → image URL */
|
|
55
|
+
imageUrls: Record<string, string>;
|
|
56
|
+
/** Map of textInputId → text content */
|
|
57
|
+
textValues: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Prepare image composer data for rendering.
|
|
61
|
+
* Resolves inputRefs from runtime inputs and structures data for the render service.
|
|
62
|
+
*
|
|
63
|
+
* @param editorConfig - The image editor template config
|
|
64
|
+
* @param inputs - Runtime input values from upstream nodes
|
|
65
|
+
* @returns Prepared data ready for render submission
|
|
66
|
+
*/
|
|
67
|
+
export declare function prepareImageComposerInput(editorConfig: ImageEditorNodeConfig, inputs: Record<string, unknown>): ImageComposerRenderInput;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepareImageComposerInput = prepareImageComposerInput;
|
|
3
4
|
const types_1 = require("./types");
|
|
4
5
|
const definition = (0, types_1.defineNode)({
|
|
5
6
|
nodeId: 'image-composer',
|
|
@@ -133,3 +134,86 @@ const definition = (0, types_1.defineNode)({
|
|
|
133
134
|
},
|
|
134
135
|
});
|
|
135
136
|
exports.default = definition;
|
|
137
|
+
/**
|
|
138
|
+
* Prepare image composer data for rendering.
|
|
139
|
+
* Resolves inputRefs from runtime inputs and structures data for the render service.
|
|
140
|
+
*
|
|
141
|
+
* @param editorConfig - The image editor template config
|
|
142
|
+
* @param inputs - Runtime input values from upstream nodes
|
|
143
|
+
* @returns Prepared data ready for render submission
|
|
144
|
+
*/
|
|
145
|
+
function prepareImageComposerInput(editorConfig, inputs) {
|
|
146
|
+
// Build imageUrls map from inputs
|
|
147
|
+
const imageUrls = {};
|
|
148
|
+
const textValues = {};
|
|
149
|
+
// Add background URL if provided
|
|
150
|
+
if (inputs.background && typeof inputs.background === 'string') {
|
|
151
|
+
imageUrls.background = inputs.background;
|
|
152
|
+
}
|
|
153
|
+
// Process each element to extract image URLs and text values
|
|
154
|
+
for (const elem of editorConfig.elements) {
|
|
155
|
+
if (elem.type === 'image' && elem.inputId) {
|
|
156
|
+
const value = inputs[elem.inputId];
|
|
157
|
+
if (typeof value === 'string') {
|
|
158
|
+
imageUrls[elem.inputId] = value;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (elem.type === 'text' && elem.textInputId) {
|
|
162
|
+
const value = inputs[elem.textInputId];
|
|
163
|
+
if (typeof value === 'string') {
|
|
164
|
+
textValues[elem.textInputId] = value;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Resolve crop boundary inputRefs (clone config to avoid mutation)
|
|
169
|
+
const resolvedConfig = JSON.parse(JSON.stringify(editorConfig));
|
|
170
|
+
if (resolvedConfig.dynamicCrop) {
|
|
171
|
+
// Resolve vertical crop boundaries
|
|
172
|
+
if (resolvedConfig.dynamicCrop.vertical?.startBoundary?.inputRef) {
|
|
173
|
+
const inputRef = resolvedConfig.dynamicCrop.vertical.startBoundary.inputRef;
|
|
174
|
+
const elementId = inputs[inputRef];
|
|
175
|
+
if (typeof elementId === 'string' && elementId) {
|
|
176
|
+
resolvedConfig.dynamicCrop.vertical.startBoundary = { elementId };
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
resolvedConfig.dynamicCrop.vertical.startBoundary = undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (resolvedConfig.dynamicCrop.vertical?.endBoundary?.inputRef) {
|
|
183
|
+
const inputRef = resolvedConfig.dynamicCrop.vertical.endBoundary.inputRef;
|
|
184
|
+
const elementId = inputs[inputRef];
|
|
185
|
+
if (typeof elementId === 'string' && elementId) {
|
|
186
|
+
resolvedConfig.dynamicCrop.vertical.endBoundary = { elementId };
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
resolvedConfig.dynamicCrop.vertical.endBoundary = undefined;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Resolve horizontal crop boundaries
|
|
193
|
+
if (resolvedConfig.dynamicCrop.horizontal?.startBoundary?.inputRef) {
|
|
194
|
+
const inputRef = resolvedConfig.dynamicCrop.horizontal.startBoundary.inputRef;
|
|
195
|
+
const elementId = inputs[inputRef];
|
|
196
|
+
if (typeof elementId === 'string' && elementId) {
|
|
197
|
+
resolvedConfig.dynamicCrop.horizontal.startBoundary = { elementId };
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
resolvedConfig.dynamicCrop.horizontal.startBoundary = undefined;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (resolvedConfig.dynamicCrop.horizontal?.endBoundary?.inputRef) {
|
|
204
|
+
const inputRef = resolvedConfig.dynamicCrop.horizontal.endBoundary.inputRef;
|
|
205
|
+
const elementId = inputs[inputRef];
|
|
206
|
+
if (typeof elementId === 'string' && elementId) {
|
|
207
|
+
resolvedConfig.dynamicCrop.horizontal.endBoundary = { elementId };
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
resolvedConfig.dynamicCrop.horizontal.endBoundary = undefined;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
config: resolvedConfig,
|
|
216
|
+
imageUrls,
|
|
217
|
+
textValues,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { VideoEditorChannel, DimensionPresetKey } from '../../render/types';
|
|
1
|
+
import type { VideoEditorNodeConfig, VideoEditorChannel, DimensionPresetKey } from '../../render/types';
|
|
2
2
|
declare const definition: import("./types").NodeDefinition<"generator", {
|
|
3
3
|
videoEditor: {
|
|
4
4
|
width: number;
|
|
@@ -12,3 +12,19 @@ declare const definition: import("./types").NodeDefinition<"generator", {
|
|
|
12
12
|
}, false>;
|
|
13
13
|
export default definition;
|
|
14
14
|
export type VideoComposerNodeConfig = typeof definition.defaults;
|
|
15
|
+
/**
|
|
16
|
+
* Prepared input data for video composer render job
|
|
17
|
+
*/
|
|
18
|
+
export interface VideoComposerRenderInput {
|
|
19
|
+
/** Resolved editor config with segments transformed */
|
|
20
|
+
config: VideoEditorNodeConfig;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Prepare video composer data for rendering.
|
|
24
|
+
* Resolves inputRefs from runtime inputs and structures data for the render service.
|
|
25
|
+
*
|
|
26
|
+
* @param editorConfig - The video editor template config
|
|
27
|
+
* @param inputs - Runtime input values from upstream nodes
|
|
28
|
+
* @returns Prepared data ready for render submission
|
|
29
|
+
*/
|
|
30
|
+
export declare function prepareVideoComposerInput(editorConfig: VideoEditorNodeConfig, inputs: Record<string, unknown>): VideoComposerRenderInput;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepareVideoComposerInput = prepareVideoComposerInput;
|
|
3
4
|
const types_1 = require("./types");
|
|
4
5
|
const definition = (0, types_1.defineNode)({
|
|
5
6
|
nodeId: 'video-composer',
|
|
@@ -115,3 +116,178 @@ const definition = (0, types_1.defineNode)({
|
|
|
115
116
|
},
|
|
116
117
|
});
|
|
117
118
|
exports.default = definition;
|
|
119
|
+
/**
|
|
120
|
+
* Omit specified keys from an object
|
|
121
|
+
*/
|
|
122
|
+
function omit(obj, keys) {
|
|
123
|
+
const result = { ...obj };
|
|
124
|
+
for (const key of keys) {
|
|
125
|
+
delete result[key];
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Ensure all segments have explicit zIndex values for proper layering in the renderer.
|
|
131
|
+
*/
|
|
132
|
+
function normalizeZIndex(editor) {
|
|
133
|
+
return {
|
|
134
|
+
...editor,
|
|
135
|
+
channels: editor.channels.map(channel => ({
|
|
136
|
+
...channel,
|
|
137
|
+
segments: channel.segments.map(segment => {
|
|
138
|
+
// Audio segments don't have zIndex
|
|
139
|
+
if (segment.type === 'audio') {
|
|
140
|
+
return segment;
|
|
141
|
+
}
|
|
142
|
+
// If zIndex is already set, keep it
|
|
143
|
+
if (typeof segment.zIndex === 'number') {
|
|
144
|
+
return segment;
|
|
145
|
+
}
|
|
146
|
+
// Overlays get zIndex 1, base segments get 0
|
|
147
|
+
const isOverlay = segment.type === 'text' || segment.parentId !== undefined;
|
|
148
|
+
return { ...segment, zIndex: isOverlay ? 1 : 0 };
|
|
149
|
+
}),
|
|
150
|
+
})),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Transform a segment by resolving inputRefs to actual source URLs.
|
|
155
|
+
* Returns null if the segment references an optional input that was skipped.
|
|
156
|
+
*/
|
|
157
|
+
function transformSegment(segment, inputs) {
|
|
158
|
+
let result = { ...segment };
|
|
159
|
+
// Handle inputRef for media segments
|
|
160
|
+
if ('inputRef' in segment && segment.inputRef) {
|
|
161
|
+
const inputKey = segment.inputRef;
|
|
162
|
+
const value = inputs[inputKey];
|
|
163
|
+
if (value === undefined || value === null) {
|
|
164
|
+
if (segment.required === false) {
|
|
165
|
+
return null; // Optional segment with missing input
|
|
166
|
+
}
|
|
167
|
+
throw new Error(`Required segment input "${inputKey}" was not provided`);
|
|
168
|
+
}
|
|
169
|
+
// Image sequence segments expect array of URLs
|
|
170
|
+
if (segment.type === 'image-sequence') {
|
|
171
|
+
if (!Array.isArray(value)) {
|
|
172
|
+
throw new Error(`Image sequence expects string[] for "${inputKey}", got ${typeof value}`);
|
|
173
|
+
}
|
|
174
|
+
let items = value;
|
|
175
|
+
if (segment.itemCountMode === 'static' && segment.staticItemCount !== undefined) {
|
|
176
|
+
items = items.slice(0, segment.staticItemCount);
|
|
177
|
+
}
|
|
178
|
+
const keysToOmit = ['inputRef', 'timeMode', 'itemCountMode', 'staticItemCount'];
|
|
179
|
+
const transformed = {
|
|
180
|
+
...omit(segment, [...keysToOmit]),
|
|
181
|
+
type: 'image-sequence',
|
|
182
|
+
source: '',
|
|
183
|
+
items,
|
|
184
|
+
};
|
|
185
|
+
result = transformed;
|
|
186
|
+
}
|
|
187
|
+
else if (segment.type === 'video-sequence') {
|
|
188
|
+
if (!Array.isArray(value)) {
|
|
189
|
+
throw new Error(`Video sequence expects string[] for "${inputKey}", got ${typeof value}`);
|
|
190
|
+
}
|
|
191
|
+
let items = value;
|
|
192
|
+
if (segment.itemCountMode === 'static' && segment.staticItemCount !== undefined) {
|
|
193
|
+
items = items.slice(0, segment.staticItemCount);
|
|
194
|
+
}
|
|
195
|
+
const keysToOmit = ['inputRef', 'timeMode', 'itemCountMode', 'staticItemCount'];
|
|
196
|
+
const transformed = {
|
|
197
|
+
...omit(segment, [...keysToOmit]),
|
|
198
|
+
type: 'video-sequence',
|
|
199
|
+
source: '',
|
|
200
|
+
items,
|
|
201
|
+
};
|
|
202
|
+
result = transformed;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
if (typeof value !== 'string') {
|
|
206
|
+
throw new Error(`Segment expects string URL for "${inputKey}", got ${typeof value}`);
|
|
207
|
+
}
|
|
208
|
+
const keysToOmit = ['inputRef', 'timeMode'];
|
|
209
|
+
if (segment.type === 'video') {
|
|
210
|
+
result = { ...omit(segment, [...keysToOmit]), type: 'video', source: value };
|
|
211
|
+
}
|
|
212
|
+
else if (segment.type === 'audio') {
|
|
213
|
+
result = { ...omit(segment, [...keysToOmit]), type: 'audio', source: value };
|
|
214
|
+
}
|
|
215
|
+
else if (segment.type === 'image') {
|
|
216
|
+
result = { ...omit(segment, [...keysToOmit]), type: 'image', source: value };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Handle textInputRef for text segments
|
|
221
|
+
if (segment.type === 'text' && segment.textInputRef) {
|
|
222
|
+
const inputKey = segment.textInputRef;
|
|
223
|
+
const value = inputs[inputKey];
|
|
224
|
+
if (value === undefined || value === null) {
|
|
225
|
+
if (segment.required === false) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
throw new Error(`Required text segment input "${inputKey}" was not provided`);
|
|
229
|
+
}
|
|
230
|
+
if (typeof value !== 'string') {
|
|
231
|
+
throw new Error(`Text segment expects string for "${inputKey}", got ${typeof value}`);
|
|
232
|
+
}
|
|
233
|
+
result = { ...omit(segment, ['textInputRef', 'timeMode']), type: 'text', source: '', text: value };
|
|
234
|
+
}
|
|
235
|
+
// Handle trim inputRefs
|
|
236
|
+
if (segment.startTrimInputRef) {
|
|
237
|
+
const value = inputs[segment.startTrimInputRef];
|
|
238
|
+
if (value !== undefined) {
|
|
239
|
+
const numValue = typeof value === 'number' ? value : Number(value);
|
|
240
|
+
if (!isNaN(numValue)) {
|
|
241
|
+
result = { ...result, startTrim: numValue };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (segment.endTrimInputRef) {
|
|
246
|
+
const value = inputs[segment.endTrimInputRef];
|
|
247
|
+
if (value !== undefined) {
|
|
248
|
+
const numValue = typeof value === 'number' ? value : Number(value);
|
|
249
|
+
if (!isNaN(numValue)) {
|
|
250
|
+
result = { ...result, endTrim: numValue };
|
|
251
|
+
const startTrim = result.startTrim ?? 0;
|
|
252
|
+
if (numValue > startTrim) {
|
|
253
|
+
result = { ...result, duration: { type: 'absolute', value: numValue - startTrim } };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Remove UI-only fields
|
|
259
|
+
const uiOnlyKeys = ['timeMode', 'startTrimInputRef', 'endTrimInputRef'];
|
|
260
|
+
const resultWithUiFields = result;
|
|
261
|
+
for (const key of uiOnlyKeys) {
|
|
262
|
+
if (key in resultWithUiFields) {
|
|
263
|
+
delete resultWithUiFields[key];
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return resultWithUiFields;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Prepare video composer data for rendering.
|
|
270
|
+
* Resolves inputRefs from runtime inputs and structures data for the render service.
|
|
271
|
+
*
|
|
272
|
+
* @param editorConfig - The video editor template config
|
|
273
|
+
* @param inputs - Runtime input values from upstream nodes
|
|
274
|
+
* @returns Prepared data ready for render submission
|
|
275
|
+
*/
|
|
276
|
+
function prepareVideoComposerInput(editorConfig, inputs) {
|
|
277
|
+
// Transform all segments by resolving inputRefs
|
|
278
|
+
const transformedChannels = editorConfig.channels.map(channel => ({
|
|
279
|
+
...channel,
|
|
280
|
+
segments: channel.segments
|
|
281
|
+
.map(segment => transformSegment(segment, inputs))
|
|
282
|
+
.filter((segment) => segment !== null),
|
|
283
|
+
}));
|
|
284
|
+
const transformedConfig = {
|
|
285
|
+
...editorConfig,
|
|
286
|
+
channels: transformedChannels,
|
|
287
|
+
};
|
|
288
|
+
// Normalize zIndex for proper layering
|
|
289
|
+
const normalizedConfig = normalizeZIndex(transformedConfig);
|
|
290
|
+
return {
|
|
291
|
+
config: normalizedConfig,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selection Utilities
|
|
3
|
+
*
|
|
4
|
+
* Deterministic selection functions for source nodes.
|
|
5
|
+
* Used by executors to pick items from pools at runtime.
|
|
6
|
+
*/
|
|
7
|
+
import type { SelectionMode } from './nodes/types';
|
|
8
|
+
/**
|
|
9
|
+
* Select an item from a pool based on selection mode and executor index.
|
|
10
|
+
*
|
|
11
|
+
* For 'sequential' mode: picks items in order, cycling when exhausted.
|
|
12
|
+
* For 'random' mode: deterministically shuffles the pool using the seed,
|
|
13
|
+
* then picks by index. This ensures:
|
|
14
|
+
* - Same seed = same shuffle = reproducible results
|
|
15
|
+
* - Each executor gets a different item (until pool is exhausted)
|
|
16
|
+
* - When pool is exhausted, cycles through again
|
|
17
|
+
*
|
|
18
|
+
* @param pool - Array of items to select from
|
|
19
|
+
* @param mode - Selection mode ('random' or 'sequential')
|
|
20
|
+
* @param index - Executor index (0, 1, 2...)
|
|
21
|
+
* @param seed - Seed string for deterministic randomness (e.g., runId + nodeId)
|
|
22
|
+
* @returns The selected item, or undefined if pool is empty
|
|
23
|
+
*/
|
|
24
|
+
export declare function selectFromPool<T>(pool: T[], mode: SelectionMode, index: number, seed: string): T | undefined;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Selection Utilities
|
|
4
|
+
*
|
|
5
|
+
* Deterministic selection functions for source nodes.
|
|
6
|
+
* Used by executors to pick items from pools at runtime.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.selectFromPool = selectFromPool;
|
|
10
|
+
/**
|
|
11
|
+
* Simple hash function for deterministic pseudo-randomness
|
|
12
|
+
*/
|
|
13
|
+
function simpleHash(str) {
|
|
14
|
+
let hash = 0;
|
|
15
|
+
for (let i = 0; i < str.length; i++) {
|
|
16
|
+
const char = str.charCodeAt(i);
|
|
17
|
+
hash = ((hash << 5) - hash) + char;
|
|
18
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
19
|
+
}
|
|
20
|
+
return hash;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Deterministically shuffle an array using a seed.
|
|
24
|
+
* Same seed always produces the same shuffle order.
|
|
25
|
+
*/
|
|
26
|
+
function seededShuffle(array, seed) {
|
|
27
|
+
const result = [...array];
|
|
28
|
+
let currentSeed = simpleHash(seed);
|
|
29
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
30
|
+
currentSeed = simpleHash(seed + currentSeed.toString());
|
|
31
|
+
const j = Math.abs(currentSeed) % (i + 1);
|
|
32
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Select an item from a pool based on selection mode and executor index.
|
|
38
|
+
*
|
|
39
|
+
* For 'sequential' mode: picks items in order, cycling when exhausted.
|
|
40
|
+
* For 'random' mode: deterministically shuffles the pool using the seed,
|
|
41
|
+
* then picks by index. This ensures:
|
|
42
|
+
* - Same seed = same shuffle = reproducible results
|
|
43
|
+
* - Each executor gets a different item (until pool is exhausted)
|
|
44
|
+
* - When pool is exhausted, cycles through again
|
|
45
|
+
*
|
|
46
|
+
* @param pool - Array of items to select from
|
|
47
|
+
* @param mode - Selection mode ('random' or 'sequential')
|
|
48
|
+
* @param index - Executor index (0, 1, 2...)
|
|
49
|
+
* @param seed - Seed string for deterministic randomness (e.g., runId + nodeId)
|
|
50
|
+
* @returns The selected item, or undefined if pool is empty
|
|
51
|
+
*/
|
|
52
|
+
function selectFromPool(pool, mode, index, seed) {
|
|
53
|
+
if (pool.length === 0)
|
|
54
|
+
return undefined;
|
|
55
|
+
if (mode === 'sequential') {
|
|
56
|
+
return pool[index % pool.length];
|
|
57
|
+
}
|
|
58
|
+
// Random mode - shuffle deterministically then pick by index
|
|
59
|
+
const shuffled = seededShuffle(pool, seed);
|
|
60
|
+
return shuffled[index % shuffled.length];
|
|
61
|
+
}
|
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { ObjectSchemaField, SelectionMode, OutputMode, NodeCategory } from './nodes/types';
|
|
7
7
|
import type { NodeType, UserCreatableNodeType, WorkflowConfig, InternalNodeType } from './nodes';
|
|
8
|
+
/**
|
|
9
|
+
* Config type for internal node types (created dynamically at runtime).
|
|
10
|
+
* Internal nodes use generic config since they don't have static definitions.
|
|
11
|
+
*/
|
|
12
|
+
export type InternalWorkflowConfig = {
|
|
13
|
+
[K in InternalNodeType]: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Combined config lookup for all node types (user-creatable + internal).
|
|
17
|
+
*/
|
|
18
|
+
export type AllWorkflowConfig = WorkflowConfig & InternalWorkflowConfig;
|
|
8
19
|
/**
|
|
9
20
|
* Media type for automation nodes
|
|
10
21
|
*/
|
|
@@ -252,7 +263,7 @@ export interface FlowControlOutput {
|
|
|
252
263
|
* Context passed to executor-based node execution.
|
|
253
264
|
* Generic on node type - config is automatically derived from the node type.
|
|
254
265
|
*/
|
|
255
|
-
export interface ExecutorContext<TType extends
|
|
266
|
+
export interface ExecutorContext<TType extends NodeType = NodeType> {
|
|
256
267
|
/** Database ID of this executor */
|
|
257
268
|
executorId: string;
|
|
258
269
|
/** Run ID this executor belongs to */
|
|
@@ -264,7 +275,7 @@ export interface ExecutorContext<TType extends UserCreatableNodeType = UserCreat
|
|
|
264
275
|
/** Node type */
|
|
265
276
|
type: TType;
|
|
266
277
|
/** Node config - type is derived from the node type */
|
|
267
|
-
config:
|
|
278
|
+
config: AllWorkflowConfig[TType];
|
|
268
279
|
/** Input values read from incoming edges (portId -> value) */
|
|
269
280
|
inputs: Record<string, unknown>;
|
|
270
281
|
}
|
|
@@ -272,7 +283,7 @@ export interface ExecutorContext<TType extends UserCreatableNodeType = UserCreat
|
|
|
272
283
|
* Executor for synchronous nodes.
|
|
273
284
|
* Generic on node type - config type is automatically derived.
|
|
274
285
|
*/
|
|
275
|
-
export interface NodeExecutor<TType extends
|
|
286
|
+
export interface NodeExecutor<TType extends NodeType, TOutput = unknown> {
|
|
276
287
|
type: TType;
|
|
277
288
|
/**
|
|
278
289
|
* Execute the node and return a single output value.
|
|
@@ -296,7 +307,7 @@ export type ExecutorAsyncJobStatus<TOutput = unknown> = {
|
|
|
296
307
|
* Executor for asynchronous nodes (video-editor, generate-image, workflow).
|
|
297
308
|
* Uses submit/poll pattern for durable workflows.
|
|
298
309
|
*/
|
|
299
|
-
export interface AsyncNodeExecutor<TType extends
|
|
310
|
+
export interface AsyncNodeExecutor<TType extends NodeType, TOutput = unknown> extends Omit<NodeExecutor<TType, TOutput>, 'execute'> {
|
|
300
311
|
isAsync: true;
|
|
301
312
|
/** Submit job - returns job ID for polling */
|
|
302
313
|
submitJob: (ctx: ExecutorContext<TType>) => Promise<{
|
|
@@ -309,14 +320,14 @@ export interface AsyncNodeExecutor<TType extends UserCreatableNodeType, TOutput
|
|
|
309
320
|
* Any node executor - used for registries that hold executors of different types
|
|
310
321
|
*/
|
|
311
322
|
export type AnyNodeExecutor = {
|
|
312
|
-
[K in
|
|
313
|
-
}[
|
|
323
|
+
[K in NodeType]: NodeExecutor<K, unknown>;
|
|
324
|
+
}[NodeType];
|
|
314
325
|
/**
|
|
315
326
|
* Any async node executor - used for registries that hold executors of different types
|
|
316
327
|
*/
|
|
317
328
|
export type AnyAsyncNodeExecutor = {
|
|
318
|
-
[K in
|
|
319
|
-
}[
|
|
329
|
+
[K in NodeType]: AsyncNodeExecutor<K, unknown>;
|
|
330
|
+
}[NodeType];
|
|
320
331
|
/**
|
|
321
332
|
* Type guard to check if an executor is async
|
|
322
333
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export { areTypesCompatible, computePortsForNode, getAllNodes, getNodeByType, ge
|
|
|
17
17
|
export { nodeDefinitions, internalNodeTypes, isAsyncExecutor, formatPortType } from './automations/types';
|
|
18
18
|
export { isEditModel } from './automations/nodes/generate-image';
|
|
19
19
|
export { isImageToVideoModel } from './automations/nodes/generate-video';
|
|
20
|
+
export { selectFromPool } from './automations/selection';
|
|
20
21
|
export { portId, isValidPortId, portIdToTitle, normalizeToPortId, PortIdSchema } from './port-id';
|
|
21
22
|
export type { PortId } from './port-id';
|
|
22
23
|
export type { ClientConfig } from './base';
|
|
@@ -29,7 +30,8 @@ export type { UserMedia, MediaUse, SocialAudio, Media, GetMediaParams, GetSocial
|
|
|
29
30
|
export type { CommentStatus, Comment, CreateCommentParams, CreateCommentResponse, GetCommentsParams, } from './comments';
|
|
30
31
|
export type { RenderJobResponse, RenderJobStatus, SubmitImageRenderJobParams, SubmitVideoRenderJobParams, SubmitScreenshotAnimationRenderJobParams, SubmitInstagramDmRenderJobParams, SubmitIMessageDmRenderJobParams, IgDmMessage, ImDmMessage, RenderVideoEditorConfig, } from './render';
|
|
31
32
|
export type { VideoEditorNodeConfig, VideoEditorChannel, VideoEditorSegment, VideoEditorVideoSegment, VideoEditorAudioSegment, VideoEditorImageSegment, VideoEditorTextSegment, VideoEditorImageSequenceSegment, VideoEditorVideoSequenceSegment, TimeValue, TimeMode, SegmentTimelinePosition, DeduplicationInput, ImageEditorElement, DimensionPresetKey, } from './render/types';
|
|
32
|
-
export type { ImageEditorNodeConfig, ImageComposerNodeConfig } from './automations/nodes/image-composer';
|
|
33
|
+
export type { ImageEditorNodeConfig, ImageComposerNodeConfig, ImageComposerRenderInput } from './automations/nodes/image-composer';
|
|
34
|
+
export { prepareImageComposerInput } from './automations/nodes/image-composer';
|
|
33
35
|
export type { AccountNodeConfig } from './automations/nodes/account';
|
|
34
36
|
export type { AutoCaptionNodeConfig, AutoCaptionPreset, AutoCaptionFontWeight, AutoCaptionPosition, } from './automations/nodes/auto-caption';
|
|
35
37
|
export type { AutoPostNodeConfig, AutoPostMode, PostSchedulingMode, } from './automations/nodes/auto-post';
|
|
@@ -40,6 +42,7 @@ export type { CreateDmNodeConfig, CreateDmMessage, DmPlatform, } from './automat
|
|
|
40
42
|
export type { CustomModelNodeConfig, CustomModelOutputType, CustomModelInputParam, } from './automations/nodes/custom-model';
|
|
41
43
|
export type { DeduplicateNodeConfig } from './automations/nodes/deduplicate';
|
|
42
44
|
export type { DestructureNodeConfig, DestructureSelection, IndexExpression, } from './automations/nodes/destructure';
|
|
45
|
+
export { indexExpressionToIndexes } from './automations/nodes/destructure';
|
|
43
46
|
export type { ForEachNodeConfig, ForEachOutputProperty, ForEachInputPort, } from './automations/nodes/for-each';
|
|
44
47
|
export type { GenerateImageNodeConfig, ImageGenerationTextModel, ImageGenerationEditModel, ImageGenerationModel, } from './automations/nodes/generate-image';
|
|
45
48
|
export type { GenerateVideoNodeConfig, VideoGenerationTextToVideoModel, VideoGenerationImageToVideoModel, VideoGenerationModel, } from './automations/nodes/generate-video';
|
|
@@ -59,9 +62,10 @@ export type { ScreenshotAnimationNodeConfig } from './automations/nodes/screensh
|
|
|
59
62
|
export type { SocialAudioNodeConfig } from './automations/nodes/social-audio';
|
|
60
63
|
export type { TextNodeConfig } from './automations/nodes/text';
|
|
61
64
|
export type { TranscriptNodeConfig } from './automations/nodes/transcript';
|
|
62
|
-
export type { VideoComposerNodeConfig } from './automations/nodes/video-composer';
|
|
65
|
+
export type { VideoComposerNodeConfig, VideoComposerRenderInput } from './automations/nodes/video-composer';
|
|
66
|
+
export { prepareVideoComposerInput } from './automations/nodes/video-composer';
|
|
63
67
|
export type { VideoImportNodeConfig, VideoImportPlatform, VideoImportQuality, } from './automations/nodes/video-import';
|
|
64
|
-
export type { MediaType, BasePortType, EnumOption, NodeOutputValues, NodePort, ResolvedPort, ResolvedPorts, ComputedNode, OutputSchemaProperty, WorkflowNodeDefinition, CanvasState, WorkflowDefinition, TemplateNode, AutomationTemplate, AutomationRun, ExecutorNode, ExecutionEdge, AccountData, FlowControlOutput, ExecutorContext, NodeExecutor, AsyncNodeExecutor, AnyNodeExecutor, AnyAsyncNodeExecutor, ExecutorAsyncJobStatus, ValidationErrorType, ValidationError, ExportedNode, ExportedConnection, ExportedTemplate, AutomationExport, ExportedExecutor, ExportedEdge, ExportedRun, AutomationRunExport, } from './automations/types';
|
|
68
|
+
export type { MediaType, BasePortType, EnumOption, NodeOutputValues, NodePort, ResolvedPort, ResolvedPorts, ComputedNode, OutputSchemaProperty, WorkflowNodeDefinition, CanvasState, WorkflowDefinition, TemplateNode, AutomationTemplate, AutomationRun, ExecutorNode, ExecutionEdge, AccountData, FlowControlOutput, ExecutorContext, NodeExecutor, AsyncNodeExecutor, AnyNodeExecutor, AnyAsyncNodeExecutor, ExecutorAsyncJobStatus, ValidationErrorType, ValidationError, ExportedNode, ExportedConnection, ExportedTemplate, AutomationExport, ExportedExecutor, ExportedEdge, ExportedRun, AutomationRunExport, InternalWorkflowConfig, AllWorkflowConfig, } from './automations/types';
|
|
65
69
|
export type { NodeDefinition, NodeType, UserCreatableNodeType, NodeConfig, WorkflowConfig, InternalNodeType, } from './automations/nodes';
|
|
66
70
|
export type { SelectionMode, ExhaustionBehavior, SelectionConfig, SelectionState, OutputMode, NodeCategory, TriggerIterationMode, IterationExhaustionBehavior, CollectionSelectionMode, TriggerCollectionConfig, ObjectSchemaField, MediaItemType, MediaNodeSelectionType, ResolvedPreview, } from './automations/nodes/types';
|
|
67
71
|
export type { SuccessResponse, ErrorResponse, ApiResponse, } from './types';
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Official TypeScript/JavaScript client for the UGC Inc API
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.LLMProviders = exports.IfLogicOperators = exports.PortIdSchema = exports.normalizeToPortId = exports.portIdToTitle = exports.isValidPortId = exports.portId = exports.isImageToVideoModel = exports.isEditModel = exports.formatPortType = exports.isAsyncExecutor = exports.internalNodeTypes = exports.nodeDefinitions = exports.resolveNodePreview = exports.hasMissingTerminalError = exports.hasMissingTriggerError = exports.getPortErrorsForNode = exports.getErrorNodeIds = exports.validateWorkflow = exports.checkCrossContextViolation = exports.getForEachContext = exports.cleanupStaleConnections = exports.removeNodeConnections = exports.removeConnection = exports.addConnection = exports.deriveConnections = exports.getOutputSchema = exports.getNodeByType = exports.getAllNodes = exports.computePortsForNode = exports.areTypesCompatible = exports.CommentsClient = exports.MediaClient = exports.AutomationsClient = exports.RenderClient = exports.OrganizationClient = exports.StatsClient = exports.PostsClient = exports.TasksClient = exports.AccountsClient = exports.UGCClient = void 0;
|
|
8
|
+
exports.prepareVideoComposerInput = exports.LLMProviders = exports.IfLogicOperators = exports.indexExpressionToIndexes = exports.prepareImageComposerInput = exports.PortIdSchema = exports.normalizeToPortId = exports.portIdToTitle = exports.isValidPortId = exports.portId = exports.selectFromPool = exports.isImageToVideoModel = exports.isEditModel = exports.formatPortType = exports.isAsyncExecutor = exports.internalNodeTypes = exports.nodeDefinitions = exports.resolveNodePreview = exports.hasMissingTerminalError = exports.hasMissingTriggerError = exports.getPortErrorsForNode = exports.getErrorNodeIds = exports.validateWorkflow = exports.checkCrossContextViolation = exports.getForEachContext = exports.cleanupStaleConnections = exports.removeNodeConnections = exports.removeConnection = exports.addConnection = exports.deriveConnections = exports.getOutputSchema = exports.getNodeByType = exports.getAllNodes = exports.computePortsForNode = exports.areTypesCompatible = exports.CommentsClient = exports.MediaClient = exports.AutomationsClient = exports.RenderClient = exports.OrganizationClient = exports.StatsClient = exports.PostsClient = exports.TasksClient = exports.AccountsClient = exports.UGCClient = void 0;
|
|
9
9
|
// =============================================================================
|
|
10
10
|
// Client Exports
|
|
11
11
|
// =============================================================================
|
|
@@ -70,13 +70,21 @@ var generate_image_1 = require("./automations/nodes/generate-image");
|
|
|
70
70
|
Object.defineProperty(exports, "isEditModel", { enumerable: true, get: function () { return generate_image_1.isEditModel; } });
|
|
71
71
|
var generate_video_1 = require("./automations/nodes/generate-video");
|
|
72
72
|
Object.defineProperty(exports, "isImageToVideoModel", { enumerable: true, get: function () { return generate_video_1.isImageToVideoModel; } });
|
|
73
|
+
var selection_1 = require("./automations/selection");
|
|
74
|
+
Object.defineProperty(exports, "selectFromPool", { enumerable: true, get: function () { return selection_1.selectFromPool; } });
|
|
73
75
|
var port_id_1 = require("./port-id");
|
|
74
76
|
Object.defineProperty(exports, "portId", { enumerable: true, get: function () { return port_id_1.portId; } });
|
|
75
77
|
Object.defineProperty(exports, "isValidPortId", { enumerable: true, get: function () { return port_id_1.isValidPortId; } });
|
|
76
78
|
Object.defineProperty(exports, "portIdToTitle", { enumerable: true, get: function () { return port_id_1.portIdToTitle; } });
|
|
77
79
|
Object.defineProperty(exports, "normalizeToPortId", { enumerable: true, get: function () { return port_id_1.normalizeToPortId; } });
|
|
78
80
|
Object.defineProperty(exports, "PortIdSchema", { enumerable: true, get: function () { return port_id_1.PortIdSchema; } });
|
|
81
|
+
var image_composer_1 = require("./automations/nodes/image-composer");
|
|
82
|
+
Object.defineProperty(exports, "prepareImageComposerInput", { enumerable: true, get: function () { return image_composer_1.prepareImageComposerInput; } });
|
|
83
|
+
var destructure_1 = require("./automations/nodes/destructure");
|
|
84
|
+
Object.defineProperty(exports, "indexExpressionToIndexes", { enumerable: true, get: function () { return destructure_1.indexExpressionToIndexes; } });
|
|
79
85
|
var if_1 = require("./automations/nodes/if");
|
|
80
86
|
Object.defineProperty(exports, "IfLogicOperators", { enumerable: true, get: function () { return if_1.IfLogicOperators; } });
|
|
81
87
|
var llm_1 = require("./automations/nodes/llm");
|
|
82
88
|
Object.defineProperty(exports, "LLMProviders", { enumerable: true, get: function () { return llm_1.LLMProviders; } });
|
|
89
|
+
var video_composer_1 = require("./automations/nodes/video-composer");
|
|
90
|
+
Object.defineProperty(exports, "prepareVideoComposerInput", { enumerable: true, get: function () { return video_composer_1.prepareVideoComposerInput; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugcinc",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.10",
|
|
4
4
|
"description": "TypeScript/JavaScript client for the UGC Inc API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@remotion/player": "^4.0.0",
|
|
44
|
-
"ugcinc": "^4.1.
|
|
44
|
+
"ugcinc": "^4.1.9",
|
|
45
45
|
"zod": "^3.23.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|