@transloadit/node 4.2.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +116 -4
  2. package/dist/Transloadit.d.ts +45 -4
  3. package/dist/Transloadit.d.ts.map +1 -1
  4. package/dist/Transloadit.js +104 -21
  5. package/dist/Transloadit.js.map +1 -1
  6. package/dist/alphalib/assembly-linter.d.ts +123 -0
  7. package/dist/alphalib/assembly-linter.d.ts.map +1 -0
  8. package/dist/alphalib/assembly-linter.js +1142 -0
  9. package/dist/alphalib/assembly-linter.js.map +1 -0
  10. package/dist/alphalib/assembly-linter.lang.en.d.ts +87 -0
  11. package/dist/alphalib/assembly-linter.lang.en.d.ts.map +1 -0
  12. package/dist/alphalib/assembly-linter.lang.en.js +326 -0
  13. package/dist/alphalib/assembly-linter.lang.en.js.map +1 -0
  14. package/dist/alphalib/goldenTemplates.d.ts +52 -0
  15. package/dist/alphalib/goldenTemplates.d.ts.map +1 -0
  16. package/dist/alphalib/goldenTemplates.js +46 -0
  17. package/dist/alphalib/goldenTemplates.js.map +1 -0
  18. package/dist/alphalib/object.d.ts +20 -0
  19. package/dist/alphalib/object.d.ts.map +1 -0
  20. package/dist/alphalib/object.js +23 -0
  21. package/dist/alphalib/object.js.map +1 -0
  22. package/dist/alphalib/stepParsing.d.ts +93 -0
  23. package/dist/alphalib/stepParsing.d.ts.map +1 -0
  24. package/dist/alphalib/stepParsing.js +1154 -0
  25. package/dist/alphalib/stepParsing.js.map +1 -0
  26. package/dist/alphalib/templateMerge.d.ts +4 -0
  27. package/dist/alphalib/templateMerge.d.ts.map +1 -0
  28. package/dist/alphalib/templateMerge.js +22 -0
  29. package/dist/alphalib/templateMerge.js.map +1 -0
  30. package/dist/cli/commands/assemblies.d.ts +20 -1
  31. package/dist/cli/commands/assemblies.d.ts.map +1 -1
  32. package/dist/cli/commands/assemblies.js +137 -2
  33. package/dist/cli/commands/assemblies.js.map +1 -1
  34. package/dist/cli/commands/auth.d.ts.map +1 -1
  35. package/dist/cli/commands/auth.js +19 -19
  36. package/dist/cli/commands/auth.js.map +1 -1
  37. package/dist/cli/commands/index.d.ts.map +1 -1
  38. package/dist/cli/commands/index.js +2 -1
  39. package/dist/cli/commands/index.js.map +1 -1
  40. package/dist/cli/docs/assemblyLintingExamples.d.ts +2 -0
  41. package/dist/cli/docs/assemblyLintingExamples.d.ts.map +1 -0
  42. package/dist/cli/docs/assemblyLintingExamples.js +10 -0
  43. package/dist/cli/docs/assemblyLintingExamples.js.map +1 -0
  44. package/dist/cli/helpers.d.ts +11 -0
  45. package/dist/cli/helpers.d.ts.map +1 -1
  46. package/dist/cli/helpers.js +29 -0
  47. package/dist/cli/helpers.js.map +1 -1
  48. package/dist/inputFiles.d.ts +41 -0
  49. package/dist/inputFiles.d.ts.map +1 -0
  50. package/dist/inputFiles.js +214 -0
  51. package/dist/inputFiles.js.map +1 -0
  52. package/dist/lintAssemblyInput.d.ts +10 -0
  53. package/dist/lintAssemblyInput.d.ts.map +1 -0
  54. package/dist/lintAssemblyInput.js +73 -0
  55. package/dist/lintAssemblyInput.js.map +1 -0
  56. package/dist/lintAssemblyInstructions.d.ts +29 -0
  57. package/dist/lintAssemblyInstructions.d.ts.map +1 -0
  58. package/dist/lintAssemblyInstructions.js +33 -0
  59. package/dist/lintAssemblyInstructions.js.map +1 -0
  60. package/dist/robots.d.ts +38 -0
  61. package/dist/robots.d.ts.map +1 -0
  62. package/dist/robots.js +230 -0
  63. package/dist/robots.js.map +1 -0
  64. package/dist/tus.d.ts +5 -1
  65. package/dist/tus.d.ts.map +1 -1
  66. package/dist/tus.js +80 -6
  67. package/dist/tus.js.map +1 -1
  68. package/package.json +5 -2
  69. package/src/Transloadit.ts +170 -26
  70. package/src/alphalib/assembly-linter.lang.en.ts +393 -0
  71. package/src/alphalib/assembly-linter.ts +1475 -0
  72. package/src/alphalib/goldenTemplates.ts +53 -0
  73. package/src/alphalib/object.ts +27 -0
  74. package/src/alphalib/stepParsing.ts +1465 -0
  75. package/src/alphalib/templateMerge.ts +32 -0
  76. package/src/alphalib/typings/json-to-ast.d.ts +34 -0
  77. package/src/cli/commands/assemblies.ts +161 -2
  78. package/src/cli/commands/auth.ts +19 -22
  79. package/src/cli/commands/index.ts +2 -0
  80. package/src/cli/docs/assemblyLintingExamples.ts +9 -0
  81. package/src/cli/helpers.ts +50 -0
  82. package/src/inputFiles.ts +278 -0
  83. package/src/lintAssemblyInput.ts +89 -0
  84. package/src/lintAssemblyInstructions.ts +72 -0
  85. package/src/robots.ts +317 -0
  86. package/src/tus.ts +91 -5
@@ -0,0 +1,1154 @@
1
+ import { z } from 'zod';
2
+ import { entries } from "./object.js";
3
+ import { robotsSchema } from "./types/robots/_index.js";
4
+ import { useParamArrayOfStringsSchema, useParamArrayOfUseParamObjectSchema, useParamObjectOfStepsSchema, useParamStringSchema, } from "./types/robots/_instructions-primitives.js";
5
+ import { assemblyInstructionsSchema } from "./types/template.js";
6
+ import { zodParseWithContext } from "./zodParseWithContext.js";
7
+ function isRecord(value) {
8
+ return typeof value === 'object' && value !== null;
9
+ }
10
+ export function doesRobotSupportUse(robot) {
11
+ return (robot !== '/upload/handle' &&
12
+ robot !== '/google/import' &&
13
+ robot !== '/dropbox/import' &&
14
+ robot !== '/supabase/import' &&
15
+ robot !== '/swift/import' &&
16
+ robot !== '/backblaze/import' &&
17
+ robot !== '/ftp/import' &&
18
+ robot !== '/cloudfiles/import' &&
19
+ robot !== '/cloudflare/import' &&
20
+ robot !== '/digitalocean/import' &&
21
+ robot !== '/http/import' &&
22
+ robot !== '/s3/import' &&
23
+ robot !== '/azure/import' &&
24
+ robot !== '/minio/import' &&
25
+ robot !== '/wasabi/import' &&
26
+ robot !== '/edgly/deliver' &&
27
+ robot !== '/tlcdn/deliver' &&
28
+ robot !== '/sftp/import');
29
+ }
30
+ export function doesStepRobotSupportUse(step) {
31
+ return 'robot' in step && doesRobotSupportUse(step.robot);
32
+ }
33
+ export function nonSignedSmartCDNUrl(argWorkspaceSlug, argTemplateSlug, argInputField, params = {}) {
34
+ const workspaceSlug = encodeURIComponent(argWorkspaceSlug);
35
+ const templateSlug = encodeURIComponent(argTemplateSlug);
36
+ const inputField = encodeURIComponent(argInputField);
37
+ const queryParams = new URLSearchParams(Object.fromEntries(entries(params).map(([key, value]) => [key, String(value)])));
38
+ queryParams.sort();
39
+ const nonSignedUrl = `https://${workspaceSlug}.tlcdn.com/${templateSlug}/${inputField}?${queryParams}`;
40
+ return nonSignedUrl;
41
+ }
42
+ export function simplifyUse(step) {
43
+ if (!doesStepRobotSupportUse(step)) {
44
+ return step;
45
+ }
46
+ if (!('use' in step)) {
47
+ return step;
48
+ }
49
+ // const zodRes1 = useParamStringSchema.safeParse(step.use)
50
+ const zodRes2 = useParamArrayOfStringsSchema.safeParse(step.use);
51
+ // const zodRes3 = useParamArrayOfUseParamObjectSchema.safeParse(step.use)
52
+ // const zodRes4 = useParamObjectOfStepsSchema.safeParse(step.use)
53
+ if (zodRes2.success) {
54
+ // Turn single element array into a string
55
+ if (zodRes2.data.length === 1) {
56
+ step.use = zodRes2.data[0];
57
+ }
58
+ }
59
+ return step;
60
+ }
61
+ export function getLastUsedStepName(step) {
62
+ if (!doesStepRobotSupportUse(step)) {
63
+ return undefined;
64
+ }
65
+ if (!('use' in step)) {
66
+ return undefined;
67
+ }
68
+ const zodRes1 = useParamStringSchema.safeParse(step.use);
69
+ const zodRes2 = useParamArrayOfStringsSchema.safeParse(step.use);
70
+ const zodRes3 = useParamArrayOfUseParamObjectSchema.safeParse(step.use);
71
+ const zodRes4 = useParamObjectOfStepsSchema.safeParse(step.use);
72
+ if (zodRes1.success) {
73
+ return zodRes1.data;
74
+ }
75
+ if (zodRes2.success) {
76
+ return zodRes2.data[zodRes2.data.length - 1];
77
+ }
78
+ if (zodRes3.success) {
79
+ return zodRes3.data[zodRes3.data.length - 1].name;
80
+ }
81
+ if (zodRes4.success) {
82
+ const zodRes41 = useParamStringSchema.safeParse(zodRes4.data.steps);
83
+ const zodRes42 = useParamArrayOfStringsSchema.safeParse(zodRes4.data.steps);
84
+ const zodRes43 = useParamArrayOfUseParamObjectSchema.safeParse(zodRes4.data.steps);
85
+ if (zodRes41.success) {
86
+ return zodRes41.data;
87
+ }
88
+ if (zodRes42.success) {
89
+ return zodRes42.data[zodRes42.data.length - 1];
90
+ }
91
+ if (zodRes43.success) {
92
+ return zodRes43.data[zodRes43.data.length - 1].name;
93
+ }
94
+ throw new Error('Invalid use value');
95
+ }
96
+ return undefined;
97
+ }
98
+ export function addUseReference(step, newName, opts) {
99
+ // const step = structuredClone(step)
100
+ const { leading = false } = opts ?? {};
101
+ if (!doesStepRobotSupportUse(step)) {
102
+ return step;
103
+ }
104
+ if (!('use' in step)) {
105
+ // TypeScript now knows this step supports 'use' due to the doesStepRobotSupportUse check
106
+ // We need to return a new object with the use property
107
+ return { ...step, use: newName };
108
+ }
109
+ const zodRes1 = useParamStringSchema.safeParse(step.use);
110
+ const zodRes2 = useParamArrayOfStringsSchema.safeParse(step.use);
111
+ const zodRes3 = useParamArrayOfUseParamObjectSchema.safeParse(step.use);
112
+ const zodRes4 = useParamObjectOfStepsSchema.safeParse(step.use);
113
+ if (zodRes1.success) {
114
+ step.use = leading ? [newName, zodRes1.data] : [zodRes1.data, newName];
115
+ }
116
+ else if (zodRes2.success) {
117
+ step.use = leading ? [newName, ...zodRes2.data] : [...zodRes2.data, newName];
118
+ }
119
+ else if (zodRes3.success) {
120
+ step.use = leading ? [{ name: newName }, ...zodRes3.data] : [...zodRes3.data, { name: newName }];
121
+ }
122
+ else if (zodRes4.success) {
123
+ const zodRes41 = useParamStringSchema.safeParse(zodRes4.data.steps);
124
+ const zodRes42 = useParamArrayOfStringsSchema.safeParse(zodRes4.data.steps);
125
+ const zodRes43 = useParamArrayOfUseParamObjectSchema.safeParse(zodRes4.data.steps);
126
+ if (zodRes41.success) {
127
+ step.use = leading
128
+ ? { ...zodRes4.data, steps: [newName, zodRes41.data] }
129
+ : { ...zodRes4.data, steps: [zodRes41.data, newName] };
130
+ }
131
+ else if (zodRes42.success) {
132
+ step.use = leading
133
+ ? { ...zodRes4.data, steps: [newName, ...zodRes42.data] }
134
+ : { ...zodRes4.data, steps: [...zodRes42.data, newName] };
135
+ }
136
+ else if (zodRes43.success) {
137
+ step.use = leading
138
+ ? { ...zodRes4.data, steps: [{ name: newName }, ...zodRes43.data] }
139
+ : { ...zodRes4.data, steps: [...zodRes43.data, { name: newName }] };
140
+ }
141
+ else {
142
+ throw new Error('Invalid use value');
143
+ }
144
+ }
145
+ return simplifyUse(step);
146
+ }
147
+ // Helper function to update 'use' references
148
+ export function renameUseReferences(step, oldName, newName) {
149
+ if (!('use' in step)) {
150
+ return step;
151
+ }
152
+ const zodRes1 = useParamStringSchema.safeParse(step.use);
153
+ const zodRes2 = useParamArrayOfStringsSchema.safeParse(step.use);
154
+ const zodRes3 = useParamArrayOfUseParamObjectSchema.safeParse(step.use);
155
+ const zodRes4 = useParamObjectOfStepsSchema.safeParse(step.use);
156
+ if (zodRes1.success) {
157
+ step.use = step.use === oldName ? newName : step.use;
158
+ }
159
+ else if (zodRes2.success) {
160
+ const newUse = [];
161
+ for (const currentName of zodRes2.data) {
162
+ if (currentName === oldName) {
163
+ if (!newUse.includes(newName)) {
164
+ newUse.push(newName);
165
+ }
166
+ }
167
+ else if (!newUse.includes(currentName)) {
168
+ newUse.push(currentName);
169
+ }
170
+ }
171
+ step.use = newUse;
172
+ }
173
+ else if (zodRes3.success) {
174
+ step.use = zodRes3.data.map((u) => ({
175
+ ...u,
176
+ name: u.name === oldName ? newName : u.name,
177
+ }));
178
+ }
179
+ else if (zodRes4.success) {
180
+ const zodRes41 = useParamStringSchema.safeParse(zodRes4.data.steps);
181
+ const zodRes42 = useParamArrayOfStringsSchema.safeParse(zodRes4.data.steps);
182
+ const zodRes43 = useParamArrayOfUseParamObjectSchema.safeParse(zodRes4.data.steps);
183
+ if (zodRes41.success) {
184
+ step.use = {
185
+ ...zodRes4.data,
186
+ steps: zodRes41.data === oldName ? newName : zodRes41.data,
187
+ };
188
+ }
189
+ else if (zodRes42.success) {
190
+ step.use = {
191
+ ...zodRes4.data,
192
+ steps: zodRes42.data.map((u) => (u === oldName ? newName : u)),
193
+ };
194
+ }
195
+ else if (zodRes43.success) {
196
+ step.use = {
197
+ ...zodRes4.data,
198
+ steps: zodRes43.data.map((u) => ({
199
+ ...u,
200
+ name: u.name === oldName ? newName : u.name,
201
+ })),
202
+ };
203
+ }
204
+ else {
205
+ throw new Error('Invalid use value');
206
+ }
207
+ }
208
+ return simplifyUse(step);
209
+ }
210
+ export function removeUseReference(step, nameToRemove) {
211
+ if (!('use' in step)) {
212
+ return step;
213
+ }
214
+ const zodRes1 = useParamStringSchema.safeParse(step.use);
215
+ const zodRes2 = useParamArrayOfStringsSchema.safeParse(step.use);
216
+ const zodRes3 = useParamArrayOfUseParamObjectSchema.safeParse(step.use);
217
+ const zodRes4 = useParamObjectOfStepsSchema.safeParse(step.use);
218
+ if (zodRes1.success) {
219
+ if (step.use === nameToRemove) {
220
+ return { ...step, use: [] };
221
+ }
222
+ return step;
223
+ }
224
+ if (zodRes2.success) {
225
+ const newUse = zodRes2.data.filter((u) => u !== nameToRemove);
226
+ return simplifyUse({ ...step, use: newUse });
227
+ }
228
+ if (zodRes3.success) {
229
+ const newUse = zodRes3.data.filter((u) => u.name !== nameToRemove);
230
+ return simplifyUse({ ...step, use: newUse });
231
+ }
232
+ if (zodRes4.success) {
233
+ const zodRes41 = useParamStringSchema.safeParse(zodRes4.data.steps);
234
+ const zodRes42 = useParamArrayOfStringsSchema.safeParse(zodRes4.data.steps);
235
+ const zodRes43 = useParamArrayOfUseParamObjectSchema.safeParse(zodRes4.data.steps);
236
+ if (zodRes41.success) {
237
+ if (zodRes41.data === nameToRemove) {
238
+ return { ...step, use: { ...zodRes4.data, steps: [] } };
239
+ }
240
+ return step;
241
+ }
242
+ if (zodRes42.success) {
243
+ const newSteps = zodRes42.data.filter((u) => u !== nameToRemove);
244
+ return simplifyUse({ ...step, use: { ...zodRes4.data, steps: newSteps } });
245
+ }
246
+ if (zodRes43.success) {
247
+ const newSteps = zodRes43.data.filter((u) => u.name !== nameToRemove);
248
+ return simplifyUse({ ...step, use: { ...zodRes4.data, steps: newSteps } });
249
+ }
250
+ throw new Error('Invalid use value');
251
+ }
252
+ return step;
253
+ }
254
+ function isZodObject(schema) {
255
+ return 'shape' in schema;
256
+ }
257
+ function getSchemaForRobot(rName) {
258
+ // Extract all robot schemas from the union and convert readonly array to regular array
259
+ const schemas = [...robotsSchema._def.options];
260
+ // Find the matching schema based on the robot name
261
+ return schemas.find((schema) => {
262
+ if (!isZodObject(schema))
263
+ return false;
264
+ const shape = schema.shape;
265
+ if (!('robot' in shape))
266
+ return false;
267
+ const robotField = shape.robot;
268
+ if (!(robotField instanceof z.ZodLiteral))
269
+ return false;
270
+ return robotField.value === rName;
271
+ });
272
+ }
273
+ export const getIndentation = (templateContent) => {
274
+ const lines = templateContent.split('\n');
275
+ // Find the first line with content and its indentation level
276
+ let baseIndent = '';
277
+ let firstContentLine = '';
278
+ for (const line of lines) {
279
+ const trimmed = line.trim();
280
+ if (trimmed.length > 0) {
281
+ firstContentLine = line;
282
+ baseIndent = line.slice(0, line.length - trimmed.length);
283
+ break;
284
+ }
285
+ }
286
+ // Find the first nested line's indentation
287
+ let nestedIndent = '';
288
+ let foundNested = false;
289
+ for (const line of lines) {
290
+ const trimmed = line.trim();
291
+ if (trimmed.length === 0)
292
+ continue;
293
+ // Skip the first content line
294
+ if (line === firstContentLine)
295
+ continue;
296
+ const currentIndent = line.slice(0, line.length - trimmed.length);
297
+ if (currentIndent.length > baseIndent.length) {
298
+ nestedIndent = currentIndent.slice(baseIndent.length);
299
+ foundNested = true;
300
+ break;
301
+ }
302
+ }
303
+ // If we found nested indentation, use that
304
+ if (foundNested) {
305
+ // For tabs, return the actual number of tabs used
306
+ if (nestedIndent.includes('\t')) {
307
+ return '\t'.repeat(nestedIndent.split('\t').length - 1);
308
+ }
309
+ // For spaces, return the actual number of spaces used
310
+ return nestedIndent;
311
+ }
312
+ // Default to 2 spaces if no nested indentation found
313
+ return ' ';
314
+ };
315
+ export class StepParsingError extends Error {
316
+ zodIssuesWithContext;
317
+ humanReadable;
318
+ constructor(message, zodIssuesWithContext, humanReadable) {
319
+ super(message);
320
+ this.zodIssuesWithContext = zodIssuesWithContext;
321
+ this.humanReadable = humanReadable;
322
+ }
323
+ }
324
+ function formatZodIssuesForLog(issues) {
325
+ return issues.map((issue) => ({
326
+ path: issue.path,
327
+ code: issue.code,
328
+ message: issue.message,
329
+ }));
330
+ }
331
+ export const parseSafeTemplate = (templateContent, opts) => {
332
+ const silent = opts?.silent ?? (process.env.NODE_ENV ?? 'production') === 'production';
333
+ let parsed;
334
+ let indent = ' ';
335
+ try {
336
+ parsed = JSON.parse(templateContent);
337
+ indent = getIndentation(templateContent);
338
+ }
339
+ catch (error) {
340
+ if (!silent) {
341
+ console.error('templateContent', { length: templateContent.length });
342
+ }
343
+ return [
344
+ new StepParsingError(`Error parsing valid type from Template. ${error}. Input length: ${templateContent.length}`, [], ''),
345
+ null,
346
+ ];
347
+ }
348
+ const { success, errors: zodIssuesWithContext, humanReadable, } = zodParseWithContext(assemblyInstructionsSchema, parsed);
349
+ if (!success) {
350
+ if (!silent) {
351
+ console.error('zodIssuesWithContext', formatZodIssuesForLog(zodIssuesWithContext));
352
+ }
353
+ return [
354
+ new StepParsingError('Error validating Template against assemblyInstructionsSchema. ', zodIssuesWithContext, humanReadable),
355
+ null,
356
+ ];
357
+ }
358
+ // We won't return zodRes.data because that will add all the defaults to the
359
+ // original input. So if we know it will pass, we return the original input.
360
+ // One of the few cases where `as` is safe and allowed.
361
+ const safe = parsed;
362
+ return [null, safe, indent];
363
+ };
364
+ export function botNeedsInput(robotName, stepName, step) {
365
+ if (robotName.endsWith('/import')) {
366
+ return false;
367
+ }
368
+ if (robotName === '/upload/handle') {
369
+ return false;
370
+ }
371
+ if (robotName === '/html/convert') {
372
+ if (step && 'url' in step && typeof step.url === 'string' && step.url) {
373
+ return false;
374
+ }
375
+ }
376
+ if (robotName === '/text/speak') {
377
+ if (step && 'prompt' in step && typeof step.prompt === 'string' && step.prompt) {
378
+ return false;
379
+ }
380
+ }
381
+ if (robotName === '/image/generate') {
382
+ return false;
383
+ }
384
+ if (stepName === ':original') {
385
+ return false;
386
+ }
387
+ return true;
388
+ }
389
+ export function getFirstStepNameThatDoesNotNeedInput(templateContent, excludeBots = []) {
390
+ // Used by functions that fix missing use parameters, which is
391
+ // a violation of our schema so we cannot use parseSafeTemplate
392
+ // here.
393
+ let parsed;
394
+ try {
395
+ parsed = JSON.parse(templateContent);
396
+ }
397
+ catch (_e) {
398
+ return '';
399
+ }
400
+ if (!isRecord(parsed) || !('steps' in parsed)) {
401
+ return '';
402
+ }
403
+ const stepsValue = parsed.steps;
404
+ if (!isRecord(stepsValue)) {
405
+ return '';
406
+ }
407
+ const stepsRecord = stepsValue;
408
+ return getFirstStepNameThatDoesNotNeedInputFromSteps(stepsRecord, excludeBots);
409
+ }
410
+ const getFirstStepNameThatDoesNotNeedInputFromSteps = (steps, excludeBots = []) => {
411
+ return (Object.keys(steps).find((stepName) => {
412
+ const step = steps[stepName];
413
+ return (typeof step === 'object' &&
414
+ step !== null &&
415
+ typeof step.robot === 'string' &&
416
+ !botNeedsInput(step.robot, stepName, step) &&
417
+ !excludeBots.includes(step.robot));
418
+ }) ?? '');
419
+ };
420
+ function hasRobotInSteps(steps, rName) {
421
+ return Object.values(steps).some((step) => {
422
+ return typeof rName === 'string' ? step.robot === rName : rName.test(step.robot);
423
+ });
424
+ }
425
+ const hasSteps = (template) => {
426
+ return typeof template.steps === 'object' && template.steps !== null;
427
+ };
428
+ export const hasRobot = (templateContent, rName, silent) => {
429
+ const parseOpts = silent === undefined ? undefined : { silent };
430
+ const [templateError, template] = parseSafeTemplate(templateContent, parseOpts);
431
+ if (templateError) {
432
+ return false;
433
+ }
434
+ return Object.values(template.steps ?? {}).some((step) => {
435
+ return typeof rName === 'string' ? step.robot === rName : rName.test(step.robot);
436
+ });
437
+ };
438
+ export const doesContentRequireUpload = (templateContent, opts) => {
439
+ const hasFileUploadRobot = hasRobot(templateContent, '/file/upload', opts?.silent);
440
+ const hasOriginalStepName = templateContent.includes(':original');
441
+ if (hasFileUploadRobot || hasOriginalStepName) {
442
+ return true;
443
+ }
444
+ return false;
445
+ };
446
+ export const canAssemblyJustRun = (templateContent) => {
447
+ const firstStepNameThatDoesNotNeedInput = getFirstStepNameThatDoesNotNeedInput(templateContent, [
448
+ '/upload/handle',
449
+ ]);
450
+ if (firstStepNameThatDoesNotNeedInput !== '') {
451
+ return true;
452
+ }
453
+ return false;
454
+ };
455
+ export function addOptimizeRobots(templateContent) {
456
+ const [templateError, template, indent] = parseSafeTemplate(templateContent);
457
+ if (templateError) {
458
+ return templateContent;
459
+ }
460
+ const newSteps = {};
461
+ let hasResizeStep = false;
462
+ for (const [stepName, step] of entries(template.steps)) {
463
+ newSteps[stepName] = step;
464
+ if (step.robot === '/image/resize') {
465
+ hasResizeStep = true;
466
+ const optimizeStepName = `${stepName}_optimized`;
467
+ const optimizeStep = {
468
+ robot: '/image/optimize',
469
+ use: [],
470
+ };
471
+ addUseReference(optimizeStep, stepName);
472
+ newSteps[optimizeStepName] = optimizeStep;
473
+ // Update subsequent steps to use the new optimize step
474
+ for (const [, nextStep] of entries(template.steps)) {
475
+ renameUseReferences(nextStep, stepName, optimizeStepName);
476
+ }
477
+ }
478
+ }
479
+ if (!hasResizeStep) {
480
+ return templateContent;
481
+ }
482
+ template.steps = newSteps;
483
+ return JSON.stringify(template, null, indent);
484
+ }
485
+ export function removeRobots(templateContent, robots = []) {
486
+ const [templateError, template, indent] = parseSafeTemplate(templateContent);
487
+ if (templateError) {
488
+ return templateContent;
489
+ }
490
+ if (!('steps' in template) || typeof template.steps !== 'object') {
491
+ return templateContent;
492
+ }
493
+ const newSteps = {};
494
+ const firstImportStepName = Object.keys(template.steps).find((stepName) => template.steps?.[stepName]?.robot?.endsWith('/import'));
495
+ let usedBefore = '';
496
+ for (const [stepName, step] of entries(template.steps)) {
497
+ if (Array.isArray(robots) ? robots.includes(step.robot) : robots.test(step.robot)) {
498
+ usedBefore = stepName;
499
+ continue;
500
+ }
501
+ if (step.robot === '/file/serve' && firstImportStepName && usedBefore) {
502
+ // Copy step before modifying
503
+ const updatedStep = { ...step };
504
+ renameUseReferences(updatedStep, usedBefore, firstImportStepName);
505
+ newSteps[stepName] = updatedStep;
506
+ }
507
+ else {
508
+ newSteps[stepName] = step;
509
+ }
510
+ }
511
+ return JSON.stringify({ ...template, steps: newSteps }, null, indent);
512
+ }
513
+ export function removeFileServeRobots(templateContent) {
514
+ return removeRobots(templateContent, ['/file/serve']);
515
+ }
516
+ export function removeUploading(templateContent) {
517
+ const [templateError, template, indent] = parseSafeTemplate(templateContent);
518
+ if (templateError) {
519
+ return templateContent;
520
+ }
521
+ if (!('steps' in template) || typeof template.steps !== 'object') {
522
+ return templateContent;
523
+ }
524
+ const newSteps = {};
525
+ const firstImportStepName = Object.keys(template.steps).find((stepName) => template.steps?.[stepName]?.robot?.endsWith('/import'));
526
+ for (const [stepName, step] of entries(template.steps)) {
527
+ if (step.robot !== '/upload/handle') {
528
+ newSteps[stepName] = firstImportStepName
529
+ ? renameUseReferences(step, ':original', firstImportStepName)
530
+ : step;
531
+ }
532
+ }
533
+ template.steps = newSteps;
534
+ return JSON.stringify(template, null, indent);
535
+ }
536
+ export function getRobotList(templateContent) {
537
+ const [templateError, template] = parseSafeTemplate(templateContent);
538
+ if (templateError) {
539
+ return [];
540
+ }
541
+ return entries(template.steps).map(([, step]) => step.robot);
542
+ }
543
+ export function addStorageRobot(content) {
544
+ const [templateError2, template2, indent] = parseSafeTemplate(content);
545
+ if (templateError2) {
546
+ return content;
547
+ }
548
+ if (!('steps' in template2) || typeof template2.steps !== 'object') {
549
+ return content;
550
+ }
551
+ // Find the last non-storage step
552
+ let lastNonStorageStep = '';
553
+ for (const [stepName, step] of entries(template2.steps)) {
554
+ if (!step.robot.endsWith('/store')) {
555
+ lastNonStorageStep = stepName;
556
+ }
557
+ }
558
+ const storedStep = {
559
+ robot: '/s3/store',
560
+ credentials: 'YOUR_S3_CREDENTIALS',
561
+ path: '/uploads/${file.id}/${file.name}',
562
+ use: [],
563
+ };
564
+ // Add references in order
565
+ if (lastNonStorageStep) {
566
+ addUseReference(storedStep, lastNonStorageStep);
567
+ }
568
+ if (template2.steps[':original']) {
569
+ addUseReference(storedStep, ':original');
570
+ }
571
+ template2.steps.stored = storedStep;
572
+ return JSON.stringify(template2, null, indent);
573
+ }
574
+ export function addFilePreviewRobot(templateContent) {
575
+ const [templateError, template, indent] = parseSafeTemplate(templateContent);
576
+ if (templateError) {
577
+ return templateContent;
578
+ }
579
+ if (!('steps' in template) || typeof template.steps !== 'object') {
580
+ return templateContent;
581
+ }
582
+ const steps = template.steps;
583
+ const newSteps = {};
584
+ let importStepName = null;
585
+ let serveStep = null;
586
+ let width;
587
+ let height;
588
+ let format;
589
+ // Identify steps and extract width
590
+ for (const [stepName, step] of entries(steps)) {
591
+ if (!botNeedsInput(step.robot)) {
592
+ importStepName = stepName;
593
+ }
594
+ else if (['/image/resize', '/video/thumb'].includes(step.robot)) {
595
+ if ('width' in step && (typeof step.width === 'number' || typeof step.width === 'string')) {
596
+ width = step.width;
597
+ }
598
+ if ('height' in step &&
599
+ (typeof step.height === 'number' || typeof step.height === 'string')) {
600
+ height = step.height;
601
+ }
602
+ if ('format' in step) {
603
+ if (step.format === 'png') {
604
+ format = 'png';
605
+ }
606
+ else if (step.format === 'jpg') {
607
+ format = 'jpg';
608
+ }
609
+ else if (step.format === 'gif') {
610
+ format = 'gif';
611
+ }
612
+ }
613
+ }
614
+ else if (step.robot === '/file/serve') {
615
+ serveStep = stepName;
616
+ }
617
+ }
618
+ // Add import step
619
+ if (importStepName) {
620
+ newSteps[importStepName] = steps[importStepName];
621
+ }
622
+ if (!width) {
623
+ if (serveStep) {
624
+ width = '${fields.w}';
625
+ }
626
+ else {
627
+ width = 500;
628
+ }
629
+ }
630
+ if (!height) {
631
+ if (serveStep) {
632
+ height = '${fields.h}';
633
+ }
634
+ else {
635
+ height = 250;
636
+ }
637
+ }
638
+ if (!format) {
639
+ format = 'png';
640
+ }
641
+ // Add preview step
642
+ newSteps.previewed = {
643
+ robot: '/file/preview',
644
+ use: [],
645
+ format,
646
+ width,
647
+ height,
648
+ resize_strategy: 'min_fit',
649
+ };
650
+ if (importStepName) {
651
+ addUseReference(newSteps.previewed, importStepName);
652
+ }
653
+ // Update serve step
654
+ if (serveStep) {
655
+ newSteps[serveStep] = {
656
+ robot: '/file/serve',
657
+ use: [],
658
+ };
659
+ // Always use 'previewed' as the source for serve step
660
+ addUseReference(newSteps[serveStep], 'previewed');
661
+ }
662
+ template.steps = newSteps;
663
+ return JSON.stringify(template, null, indent);
664
+ }
665
+ export function getRecommendations(templateContent, silent) {
666
+ const parseOpts = silent === undefined ? undefined : { silent };
667
+ const [templateError, template] = parseSafeTemplate(templateContent, parseOpts);
668
+ if (templateError) {
669
+ return [];
670
+ }
671
+ if (!hasSteps(template)) {
672
+ return [];
673
+ }
674
+ const recommendations = [];
675
+ const steps = template.steps;
676
+ const stepCount = Object.keys(steps).length;
677
+ const canJustRun = getFirstStepNameThatDoesNotNeedInputFromSteps(steps, ['/upload/handle']) !== '';
678
+ const hasFileServe = hasRobotInSteps(steps, '/file/serve');
679
+ const hasStorageRobot = hasRobotInSteps(steps, /\/store$/);
680
+ const hasImageResizeStep = hasRobotInSteps(steps, '/image/resize');
681
+ const hasVideoThumbStep = hasRobotInSteps(steps, '/video/thumb');
682
+ const hasImageResizeOrVideoThumb = hasImageResizeStep || hasVideoThumbStep;
683
+ // Add the storage recommendation if needed
684
+ if (!hasStorageRobot && !hasFileServe) {
685
+ recommendations.push({
686
+ id: 'ADD_STORAGE',
687
+ robotName: 'Add Storage Robot',
688
+ description: 'Add a storage Robot to permanently save your processed files.',
689
+ applyFunction: (templateContent2) => addStorageRobot(templateContent2),
690
+ iconSrc: '/assets/images/robots/s3-store.png',
691
+ });
692
+ }
693
+ if (stepCount === 2 && canJustRun && hasFileServe) {
694
+ recommendations.push({
695
+ id: 'ADD_FILE_PREVIEW',
696
+ robotName: 'Add /file/preview',
697
+ description: 'You are serving assets directly to users. Consider adding a preview Step to reduce sizes and bandwidth usage.',
698
+ applyFunction: (templateContent2) => addFilePreviewRobot(templateContent2),
699
+ iconSrc: '/assets/images/robots/file-preview.png',
700
+ });
701
+ }
702
+ else if (stepCount === 3 && canJustRun && hasFileServe && hasImageResizeOrVideoThumb) {
703
+ const robotToSwap = hasImageResizeStep ? '/image/resize' : '/video/thumb';
704
+ const imageOrVideo = hasImageResizeStep ? 'image' : 'video';
705
+ const negative = hasImageResizeStep ? 'video' : 'image';
706
+ recommendations.push({
707
+ id: 'REPLACE_THUMBESQUE_WITH_FILE_PREVIEW',
708
+ robotName: 'Use /file/preview',
709
+ description: `If you swap out the ${robotToSwap} Step for /file/preview, you can serve previews of not only ${imageOrVideo}s but also ${negative}s, audio, PDFs, and more.`,
710
+ applyFunction: (templateContent2) => addFilePreviewRobot(templateContent2),
711
+ iconSrc: '/assets/images/robots/file-preview.png',
712
+ });
713
+ }
714
+ else if (hasRobotInSteps(steps, '/image/resize') &&
715
+ !hasRobotInSteps(steps, '/image/optimize')) {
716
+ recommendations.push({
717
+ id: 'ADD_IMAGE_OPTIMIZE',
718
+ robotName: 'Add /image/optimize',
719
+ description: 'Optimize your resized images to reduce file size without losing quality.',
720
+ applyFunction: (templateContent2) => addOptimizeRobots(templateContent2),
721
+ iconSrc: '/assets/images/robots/image-optimize.png',
722
+ });
723
+ }
724
+ // Sort recommendations by id to ensure consistent order
725
+ return recommendations.sort((a, b) => a.id.localeCompare(b.id));
726
+ }
727
+ export function addFileServeRobot(templateContent) {
728
+ const [templateError, template, indent] = parseSafeTemplate(templateContent);
729
+ if (templateError) {
730
+ return templateContent;
731
+ }
732
+ if (!('steps' in template) || typeof template.steps !== 'object') {
733
+ return templateContent;
734
+ }
735
+ const { steps } = template;
736
+ let lastNonImportStepName = '';
737
+ let lastExportStepName = '';
738
+ // Find the last non-import step and any export step
739
+ for (const [stepName, step] of entries(steps)) {
740
+ if (!step.robot.endsWith('/import')) {
741
+ lastNonImportStepName = stepName;
742
+ }
743
+ if (step.robot.includes('/store')) {
744
+ lastExportStepName = stepName;
745
+ }
746
+ }
747
+ // If there's an export step, replace it with /file/serve
748
+ if (lastExportStepName) {
749
+ const lastExportStep = steps[lastExportStepName];
750
+ delete steps[lastExportStepName];
751
+ const finalUse = getLastUsedStepName(lastExportStep);
752
+ if (finalUse) {
753
+ steps.served = {
754
+ robot: '/file/serve',
755
+ use: [],
756
+ };
757
+ addUseReference(steps.served, finalUse);
758
+ }
759
+ }
760
+ else {
761
+ // If no export step, append /file/serve as a new step
762
+ steps.served = {
763
+ robot: '/file/serve',
764
+ use: [],
765
+ };
766
+ addUseReference(steps.served, lastNonImportStepName);
767
+ }
768
+ return JSON.stringify(template, null, indent);
769
+ }
770
+ export function addUploadHandleRobot(templateContent) {
771
+ const [templateError, template, indent] = parseSafeTemplate(templateContent);
772
+ if (templateError) {
773
+ return templateContent;
774
+ }
775
+ if (!('steps' in template) || typeof template.steps !== 'object') {
776
+ return templateContent;
777
+ }
778
+ const steps = template.steps;
779
+ const stepsOrder = Object.keys(steps);
780
+ let firstImportStepName = null;
781
+ let firstImportStepIndex = -1;
782
+ // Find the first import step
783
+ for (let i = 0; i < stepsOrder.length; i++) {
784
+ const stepName = stepsOrder[i];
785
+ const step = steps[stepName];
786
+ if (step.robot.endsWith('/import')) {
787
+ firstImportStepName = stepName;
788
+ firstImportStepIndex = i;
789
+ break;
790
+ }
791
+ }
792
+ const newSteps = {};
793
+ if (firstImportStepName !== null && firstImportStepIndex >= 0) {
794
+ // Replace the first import step with ':original' at the same position
795
+ const stepsOrderNew = [...stepsOrder];
796
+ stepsOrderNew[firstImportStepIndex] = ':original';
797
+ for (const stepName of stepsOrderNew) {
798
+ if (stepName === ':original') {
799
+ newSteps[stepName] = { robot: '/upload/handle' };
800
+ }
801
+ else {
802
+ let step = steps[stepName];
803
+ step = renameUseReferences(step, firstImportStepName, ':original');
804
+ newSteps[stepName] = step;
805
+ }
806
+ }
807
+ }
808
+ else {
809
+ // No import step, insert ':original' before the first step
810
+ const stepsOrderNew = [':original', ...stepsOrder];
811
+ let isFirstStep = true;
812
+ for (const stepName of stepsOrderNew) {
813
+ if (stepName === ':original') {
814
+ newSteps[stepName] = { robot: '/upload/handle' };
815
+ }
816
+ else {
817
+ const step = steps[stepName];
818
+ if (isFirstStep &&
819
+ (!('use' in step) || (Array.isArray(step.use) && step.use.length === 0)) &&
820
+ doesStepRobotSupportUse(step)) {
821
+ step.use = ':original';
822
+ isFirstStep = false;
823
+ }
824
+ newSteps[stepName] = step;
825
+ }
826
+ }
827
+ }
828
+ template.steps = newSteps;
829
+ return JSON.stringify(template, null, indent);
830
+ }
831
+ export function addImportRobot(templateContent) {
832
+ const [templateError, template, indent] = parseSafeTemplate(templateContent);
833
+ if (templateError) {
834
+ return templateContent;
835
+ }
836
+ if (!('steps' in template) || typeof template.steps !== 'object') {
837
+ return templateContent;
838
+ }
839
+ const steps = template.steps;
840
+ const newSteps = {};
841
+ // Check if an import or html/convert robot already exists
842
+ const hasImportRobot = Boolean(getFirstStepNameThatDoesNotNeedInput(templateContent)) &&
843
+ !hasRobot(templateContent, '/upload/handle');
844
+ if (hasImportRobot) {
845
+ return templateContent; // No changes needed
846
+ }
847
+ const uploadHandleStepName = Object.keys(steps).find((stepName) => steps[stepName].robot === '/upload/handle');
848
+ // Find the first non-import-non-export step to determine media type
849
+ let firstNonImportExportStep;
850
+ for (const [, step] of entries(steps)) {
851
+ if (!firstNonImportExportStep &&
852
+ !step.robot.endsWith('/import') &&
853
+ !step.robot.includes('/store') &&
854
+ step.robot !== '/file/serve') {
855
+ firstNonImportExportStep = step;
856
+ }
857
+ }
858
+ // Add the import step, replacing the upload/handle step if it exists
859
+ const importStepName = 'imported';
860
+ let url;
861
+ // Set URL based on the type of media being processed
862
+ const robotName = firstNonImportExportStep?.robot || '';
863
+ if (robotName.startsWith('/image/') || robotName === '/file/preview') {
864
+ url = 'https://demos.transloadit.com/inputs/prinsengracht.jpg';
865
+ }
866
+ else if (robotName.startsWith('/video/')) {
867
+ url = 'https://demos.transloadit.com/inputs/wave10.mp4';
868
+ }
869
+ else if (robotName.startsWith('/audio/')) {
870
+ url = 'https://demos.transloadit.com/inputs/joakim_karud-rock_angel.mp3';
871
+ }
872
+ else if (robotName.startsWith('/document/')) {
873
+ url = 'https://demos.transloadit.com/inputs/aws-cloud-best-practices.pdf';
874
+ }
875
+ else {
876
+ url = 'https://demos.transloadit.com/inputs/prinsengracht.jpg';
877
+ }
878
+ newSteps[importStepName] = {
879
+ robot: '/http/import',
880
+ url,
881
+ };
882
+ // Update references and add other steps
883
+ for (const [stepName, step] of entries(steps)) {
884
+ if (stepName !== uploadHandleStepName) {
885
+ const updatedStep = { ...step };
886
+ if (uploadHandleStepName) {
887
+ renameUseReferences(updatedStep, uploadHandleStepName, importStepName);
888
+ }
889
+ else if (!('use' in updatedStep) ||
890
+ (Array.isArray(updatedStep.use) && updatedStep.use.length === 0)) {
891
+ addUseReference(updatedStep, importStepName);
892
+ }
893
+ newSteps[stepName] = updatedStep;
894
+ }
895
+ }
896
+ template.steps = newSteps;
897
+ return JSON.stringify(template, null, indent);
898
+ }
899
+ export function addFieldsInput(templateContent) {
900
+ let parsed;
901
+ let indent = ' ';
902
+ try {
903
+ parsed = JSON.parse(templateContent);
904
+ indent = getIndentation(templateContent);
905
+ }
906
+ catch (_e) {
907
+ return templateContent;
908
+ }
909
+ if (!isRecord(parsed)) {
910
+ return templateContent;
911
+ }
912
+ const parsedRecord = parsed;
913
+ const stepsValue = parsedRecord.steps;
914
+ if (!isRecord(stepsValue)) {
915
+ return templateContent;
916
+ }
917
+ const steps = stepsValue;
918
+ for (const [, step] of entries(steps)) {
919
+ if (step.robot === '/http/import') {
920
+ if ('url' in step && typeof step.url === 'string') {
921
+ if (!step.url.includes('${fields.input}')) {
922
+ let url;
923
+ try {
924
+ url = new URL(step.url);
925
+ url.pathname = '${fields.input}';
926
+ step.url = url.toString().replaceAll('%7B', '{').replaceAll('%7D', '}');
927
+ }
928
+ catch (_e) {
929
+ step.url = 'https://demos.transloadit.com/${fields.input}';
930
+ }
931
+ }
932
+ }
933
+ else {
934
+ step.url = 'https://demos.transloadit.com/${fields.input}';
935
+ }
936
+ }
937
+ else if (step.robot.endsWith('/import')) {
938
+ if ('path' in step && typeof step.path === 'string') {
939
+ if (!step.path.includes('${fields.input}')) {
940
+ let url;
941
+ try {
942
+ url = new URL(step.path);
943
+ url.pathname = '${fields.input}';
944
+ step.path = url.toString().replaceAll('%7B', '{').replaceAll('%7D', '}');
945
+ }
946
+ catch (_e) {
947
+ step.path = '${fields.input}';
948
+ }
949
+ }
950
+ }
951
+ else {
952
+ step.path = '${fields.input}';
953
+ }
954
+ break;
955
+ }
956
+ }
957
+ parsedRecord.steps = steps;
958
+ return JSON.stringify(parsedRecord, null, indent);
959
+ }
960
+ function getExampleValueForField(rName, paramName) {
961
+ if (rName === '/http/import' && paramName === 'url') {
962
+ return ['inputs/prinsengracht.jpg'];
963
+ }
964
+ if (rName.endsWith('/import') && paramName === 'path') {
965
+ return ['inputs/prinsengracht.jpg'];
966
+ }
967
+ if (paramName === 'width') {
968
+ return [400];
969
+ }
970
+ if (paramName === 'height') {
971
+ return [180];
972
+ }
973
+ return [];
974
+ }
975
+ function getMostCommonExampleValue(occurrences) {
976
+ const exampleValues = occurrences.map((occurrence) => occurrence.exampleValues[0]);
977
+ const exampleValueCounts = new Map();
978
+ for (const value of exampleValues) {
979
+ exampleValueCounts.set(value, (exampleValueCounts.get(value) || 0) + 1);
980
+ }
981
+ let mostCommonValue;
982
+ let maxCount = 0;
983
+ for (const [value, count] of exampleValueCounts.entries()) {
984
+ if (count > maxCount) {
985
+ maxCount = count;
986
+ mostCommonValue = value;
987
+ }
988
+ }
989
+ if (mostCommonValue === undefined) {
990
+ return '';
991
+ }
992
+ return mostCommonValue;
993
+ }
994
+ // Modify the extractFieldNamesFromTemplate function
995
+ export function extractFieldNamesFromTemplate(templateContent) {
996
+ const [templateError, template] = parseSafeTemplate(templateContent);
997
+ if (templateError) {
998
+ return [];
999
+ }
1000
+ const fieldsMap = new Map();
1001
+ function traverse(value, path, stepName, rName) {
1002
+ if (typeof value === 'string') {
1003
+ const matches = value.match(/\${fields\.([a-zA-Z0-9_]+)}/g);
1004
+ if (matches) {
1005
+ for (const match of matches) {
1006
+ const fieldName = match.slice(9, -1);
1007
+ let field = fieldsMap.get(fieldName);
1008
+ if (!field) {
1009
+ field = {
1010
+ fieldName,
1011
+ occurrences: [],
1012
+ mostCommonExampleValue: '',
1013
+ };
1014
+ fieldsMap.set(fieldName, field);
1015
+ }
1016
+ const parts = value.split(match);
1017
+ const [leader, trailer] = parts;
1018
+ const paramName = String(path[0]);
1019
+ field.occurrences.push({
1020
+ stepName,
1021
+ exampleValues: getExampleValueForField(rName, paramName),
1022
+ rName,
1023
+ paramName,
1024
+ leader,
1025
+ trailer,
1026
+ requiresDenoEval: false,
1027
+ errors: [],
1028
+ path: [stepName, ...path],
1029
+ });
1030
+ }
1031
+ }
1032
+ }
1033
+ else if (Array.isArray(value)) {
1034
+ for (const [index, item] of value.entries()) {
1035
+ traverse(item, [...path, index], stepName, rName);
1036
+ }
1037
+ }
1038
+ else if (typeof value === 'object' && value !== null) {
1039
+ for (const [key, childValue] of entries(value)) {
1040
+ traverse(childValue, [...path, key], stepName, rName);
1041
+ }
1042
+ }
1043
+ }
1044
+ for (const [stepName, step] of entries(template.steps || {})) {
1045
+ if (typeof step !== 'object' || step === null) {
1046
+ continue;
1047
+ }
1048
+ if (!('robot' in step)) {
1049
+ continue;
1050
+ }
1051
+ if (typeof step.robot !== 'string') {
1052
+ continue;
1053
+ }
1054
+ const rName = step.robot || '';
1055
+ traverse(step, [], stepName, rName);
1056
+ }
1057
+ for (const field of fieldsMap.values()) {
1058
+ field.mostCommonExampleValue = getMostCommonExampleValue(field.occurrences);
1059
+ }
1060
+ return Array.from(fieldsMap.values());
1061
+ }
1062
+ function getFinalType(schema) {
1063
+ if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {
1064
+ return getFinalType(schema.unwrap());
1065
+ }
1066
+ if (schema instanceof z.ZodDefault) {
1067
+ return getFinalType(schema._def.innerType);
1068
+ }
1069
+ return schema;
1070
+ }
1071
+ export function interpolateFieldsInTemplate(templateContent, allFields, opts) {
1072
+ const [templateError, template] = parseSafeTemplate(templateContent, opts);
1073
+ if (templateError) {
1074
+ return { steps: {} };
1075
+ }
1076
+ const { steps } = template;
1077
+ const newSteps = {};
1078
+ for (const [stepName, step] of entries(steps)) {
1079
+ const newStep = { ...step };
1080
+ for (const [paramName, paramValue] of entries(step)) {
1081
+ if (typeof paramValue === 'string') {
1082
+ let newValue = paramValue;
1083
+ for (const field of allFields) {
1084
+ for (const occurrence of field.occurrences) {
1085
+ if (occurrence.stepName === stepName && occurrence.paramName === paramName) {
1086
+ const compiledValue = `${occurrence.leader}${field.value}${occurrence.trailer}`;
1087
+ newValue = compiledValue;
1088
+ }
1089
+ }
1090
+ }
1091
+ // Convert to number if expected
1092
+ const schema = getSchemaForRobot(step.robot);
1093
+ if (schema && isZodObject(schema)) {
1094
+ const paramSchema = schema.shape[paramName];
1095
+ const finalParamSchema = getFinalType(paramSchema);
1096
+ if (finalParamSchema instanceof z.ZodNumber) {
1097
+ const num = Number(newValue);
1098
+ if (!Number.isNaN(num)) {
1099
+ newValue = num;
1100
+ }
1101
+ }
1102
+ }
1103
+ Object.assign(newStep, { [paramName]: newValue });
1104
+ }
1105
+ }
1106
+ newSteps[stepName] = newStep;
1107
+ }
1108
+ return { ...template, steps: newSteps };
1109
+ }
1110
+ export function validateInterpolatedTemplate(template, fieldsWithValues, fieldNameToValidate) {
1111
+ const errors = [];
1112
+ for (const [stepName, step] of entries(template.steps)) {
1113
+ const schema = getSchemaForRobot(step.robot);
1114
+ if (!schema) {
1115
+ console.error('No schema linked up for', step.robot);
1116
+ continue;
1117
+ }
1118
+ const zodRes = schema.safeParse(step);
1119
+ if (!zodRes.success) {
1120
+ for (const err of zodRes.error.errors) {
1121
+ if (err.path.length !== 1) {
1122
+ continue;
1123
+ }
1124
+ if (typeof err.path[0] !== 'string') {
1125
+ continue;
1126
+ }
1127
+ const fieldNames = [];
1128
+ for (const field of fieldsWithValues) {
1129
+ if (field.fieldName === fieldNameToValidate || fieldNameToValidate === undefined) {
1130
+ for (const occurrence of field.occurrences) {
1131
+ if (occurrence.stepName === stepName && occurrence.paramName === err.path[0]) {
1132
+ fieldNames.push(field.fieldName);
1133
+ }
1134
+ }
1135
+ }
1136
+ }
1137
+ if (fieldNames.length > 0) {
1138
+ const paramName = err.path[0];
1139
+ const value = Object.entries(step).find(([key]) => key === paramName)?.[1];
1140
+ errors.push({
1141
+ stepName,
1142
+ robotName: step.robot,
1143
+ paramName,
1144
+ value,
1145
+ fieldNames,
1146
+ message: err.message,
1147
+ });
1148
+ }
1149
+ }
1150
+ }
1151
+ }
1152
+ return errors;
1153
+ }
1154
+ //# sourceMappingURL=stepParsing.js.map