@squiz/dxp-cli-next 5.19.0-develop.1 → 5.19.0-develop.2

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/lib/cmp/deploy.js CHANGED
@@ -50,6 +50,7 @@ const deployCommand = new commander_1.Command()
50
50
  .argument('<source>', 'folder containing a manifest.json file')
51
51
  .addOption(new commander_1.Option('-cu, --component-service-url <string>', 'Override the component service url from login').env('COMPONENT_SERVICE_URL'))
52
52
  .addOption(new commander_1.Option('-t, --tenant <string>', 'Tenant ID to deploy to. If not provided will use configured tenant from login'))
53
+ .addOption(new commander_1.Option('--dry-run', 'Run all pre-deployment processes without deploying').default(false))
53
54
  .action((source, options) => __awaiter(void 0, void 0, void 0, function* () {
54
55
  var _a, _b;
55
56
  if (process.env.COMPONENT_SERVICE_URL !== undefined) {
@@ -64,27 +65,35 @@ const deployCommand = new commander_1.Command()
64
65
  console.warn(cli_color_1.default.yellow('WARN: Component service URL no longer requires /v1/ suffix'));
65
66
  }
66
67
  try {
68
+ const dxpCacheDirPath = path.resolve(source, '.dxp');
69
+ yield fs.mkdir(dxpCacheDirPath, { recursive: true });
70
+ const outputDir = yield fs.mkdtemp(path.resolve(dxpCacheDirPath, 'cmp-'));
67
71
  const def = yield definitions_1.ComponentDefinition.load(source);
68
72
  if (!def) {
69
- throw new Error('Invalid definition');
73
+ throw new Error('Failed to load component for deployment');
70
74
  }
71
75
  const componentServiceUrl = ((_b = options.componentServiceUrl) === null || _b === void 0 ? void 0 : _b.replace(/v1\/?$/, '')) ||
72
76
  (yield buildComponentServiceUrl(options.tenant));
73
77
  if (def.type === 'server') {
74
- return yield (0, component_cli_lib_1.uploadComponentFolder)(apiService.client, componentServiceUrl, source);
78
+ if (options.dryRun) {
79
+ console.info(cli_color_1.default.yellow('INFO: Cancelling deployment due to --dry-run flag'));
80
+ return;
81
+ }
82
+ return yield (0, component_cli_lib_1.uploadComponentFolder)(apiService.client, componentServiceUrl, source, dxpCacheDirPath);
75
83
  }
76
84
  if (def.type === 'edge') {
77
85
  if (process.env.FEATURE_EDGE_COMPONENTS !== 'true') {
78
86
  throw new Error('Component type "edge" is still in development. Run with environment variable "FEATURE_EDGE_COMPONENTS=true" to enable');
79
87
  }
80
- const dxpCacheDirPath = path.resolve(source, '.dxp');
81
- yield fs.mkdir(dxpCacheDirPath, { recursive: true });
82
- const outputDir = yield fs.mkdtemp(path.resolve(dxpCacheDirPath, 'cmp-'));
83
- yield (0, compiler_1.compileUserCode)(def.mainEntryFilePath, path.join(outputDir, 'userCode.js'));
88
+ yield (0, compiler_1.compileUserCode)(def.mainEntryFilePath, path.join(outputDir, definitions_1.COMPILED_ENTRY_FILE));
84
89
  yield def.copyManifest(outputDir);
85
90
  yield def.copyPreviewFiles(outputDir);
86
91
  yield def.copyMockedUriFiles(outputDir);
87
- return yield (0, component_cli_lib_1.uploadComponentFolder)(apiService.client, componentServiceUrl, outputDir);
92
+ if (options.dryRun) {
93
+ console.info(cli_color_1.default.yellow('INFO: Cancelling deployment due to --dry-run flag'));
94
+ return;
95
+ }
96
+ return yield (0, component_cli_lib_1.uploadComponentFolder)(apiService.client, componentServiceUrl, outputDir, dxpCacheDirPath);
88
97
  }
89
98
  }
90
99
  catch (error) {
@@ -92,18 +101,18 @@ const deployCommand = new commander_1.Command()
92
101
  deployCommand.error(error.stack);
93
102
  }
94
103
  if (error instanceof ApiService_1.InvalidLoginSessionError) {
95
- deployCommand.error(cli_color_1.default.red('Login session expired. Please login again.'));
104
+ deployCommand.error(cli_color_1.default.red('ERROR:', 'Login session expired. Please login again.'));
96
105
  return;
97
106
  }
98
107
  if (error instanceof ApiService_1.InvalidTenantError) {
99
- deployCommand.error(cli_color_1.default.red('Cannot deploy to specified tenant'));
108
+ deployCommand.error(cli_color_1.default.red('ERROR:', 'Cannot deploy to specified tenant'));
100
109
  return;
101
110
  }
102
111
  if (error.message) {
103
- deployCommand.error(cli_color_1.default.red(error.message));
112
+ deployCommand.error(cli_color_1.default.red('ERROR:', error.message));
104
113
  }
105
114
  else {
106
- deployCommand.error(cli_color_1.default.red('An unknown error occurred'));
115
+ deployCommand.error(cli_color_1.default.red('ERROR:', 'An unknown error occurred'));
107
116
  }
108
117
  }
109
118
  }));
@@ -1,6 +1,7 @@
1
+ import { z } from 'zod';
1
2
  export declare class ComponentDefinition {
2
- private baseDir;
3
- private definition;
3
+ baseDir: string;
4
+ definition: BasicDefinition;
4
5
  static load(componentDirectory: string): Promise<ComponentDefinition | undefined>;
5
6
  private constructor();
6
7
  get mainEntryFilePath(): string;
@@ -9,8 +10,469 @@ export declare class ComponentDefinition {
9
10
  entry: string;
10
11
  };
11
12
  get manifestPath(): string;
12
- get type(): "edge" | "server";
13
+ get type(): "server" | "edge";
13
14
  copyManifest(copyDir: string): Promise<void>;
14
15
  copyPreviewFiles(copyDir: string): Promise<void>;
15
16
  copyMockedUriFiles(copyDir: string): Promise<void>;
16
17
  }
18
+ export declare const BasicDefinition: z.ZodIntersection<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
19
+ type: z.ZodLiteral<"server">;
20
+ mainFunction: z.ZodString;
21
+ functions: z.ZodEffects<z.ZodArray<z.ZodObject<{
22
+ name: z.ZodString;
23
+ entry: z.ZodString;
24
+ }, "strip", z.ZodTypeAny, {
25
+ name: string;
26
+ entry: string;
27
+ }, {
28
+ name: string;
29
+ entry: string;
30
+ }>, "many">, {
31
+ name: string;
32
+ entry: string;
33
+ }[], {
34
+ name: string;
35
+ entry: string;
36
+ }[]>;
37
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
38
+ type: z.ZodLiteral<"server">;
39
+ mainFunction: z.ZodString;
40
+ functions: z.ZodEffects<z.ZodArray<z.ZodObject<{
41
+ name: z.ZodString;
42
+ entry: z.ZodString;
43
+ }, "strip", z.ZodTypeAny, {
44
+ name: string;
45
+ entry: string;
46
+ }, {
47
+ name: string;
48
+ entry: string;
49
+ }>, "many">, {
50
+ name: string;
51
+ entry: string;
52
+ }[], {
53
+ name: string;
54
+ entry: string;
55
+ }[]>;
56
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
57
+ type: z.ZodLiteral<"server">;
58
+ mainFunction: z.ZodString;
59
+ functions: z.ZodEffects<z.ZodArray<z.ZodObject<{
60
+ name: z.ZodString;
61
+ entry: z.ZodString;
62
+ }, "strip", z.ZodTypeAny, {
63
+ name: string;
64
+ entry: string;
65
+ }, {
66
+ name: string;
67
+ entry: string;
68
+ }>, "many">, {
69
+ name: string;
70
+ entry: string;
71
+ }[], {
72
+ name: string;
73
+ entry: string;
74
+ }[]>;
75
+ }, z.ZodTypeAny, "passthrough">>, z.ZodObject<{
76
+ type: z.ZodLiteral<"edge">;
77
+ mainFunction: z.ZodString;
78
+ functions: z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodObject<{
79
+ name: z.ZodString;
80
+ entry: z.ZodString;
81
+ input: z.ZodRecord<z.ZodString, z.ZodAny>;
82
+ output: z.ZodDiscriminatedUnion<"responseType", [z.ZodObject<{
83
+ responseType: z.ZodLiteral<"html">;
84
+ staticFiles: z.ZodOptional<z.ZodNever>;
85
+ }, "strip", z.ZodTypeAny, {
86
+ responseType: "html";
87
+ staticFiles?: undefined;
88
+ }, {
89
+ responseType: "html";
90
+ staticFiles?: undefined;
91
+ }>, z.ZodObject<{
92
+ responseType: z.ZodLiteral<"json">;
93
+ }, "strip", z.ZodTypeAny, {
94
+ responseType: "json";
95
+ }, {
96
+ responseType: "json";
97
+ }>]>;
98
+ }, "strip", z.ZodTypeAny, {
99
+ name: string;
100
+ input: Record<string, any>;
101
+ entry: string;
102
+ output: {
103
+ responseType: "html";
104
+ staticFiles?: undefined;
105
+ } | {
106
+ responseType: "json";
107
+ };
108
+ }, {
109
+ name: string;
110
+ input: Record<string, any>;
111
+ entry: string;
112
+ output: {
113
+ responseType: "html";
114
+ staticFiles?: undefined;
115
+ } | {
116
+ responseType: "json";
117
+ };
118
+ }>, "many">, {
119
+ name: string;
120
+ input: Record<string, any>;
121
+ entry: string;
122
+ output: {
123
+ responseType: "html";
124
+ staticFiles?: undefined;
125
+ } | {
126
+ responseType: "json";
127
+ };
128
+ }[], {
129
+ name: string;
130
+ input: Record<string, any>;
131
+ entry: string;
132
+ output: {
133
+ responseType: "html";
134
+ staticFiles?: undefined;
135
+ } | {
136
+ responseType: "json";
137
+ };
138
+ }[]>, {
139
+ name: string;
140
+ input: Record<string, any>;
141
+ entry: string;
142
+ output: {
143
+ responseType: "html";
144
+ staticFiles?: undefined;
145
+ } | {
146
+ responseType: "json";
147
+ };
148
+ }[], {
149
+ name: string;
150
+ input: Record<string, any>;
151
+ entry: string;
152
+ output: {
153
+ responseType: "html";
154
+ staticFiles?: undefined;
155
+ } | {
156
+ responseType: "json";
157
+ };
158
+ }[]>;
159
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
160
+ type: z.ZodLiteral<"edge">;
161
+ mainFunction: z.ZodString;
162
+ functions: z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodObject<{
163
+ name: z.ZodString;
164
+ entry: z.ZodString;
165
+ input: z.ZodRecord<z.ZodString, z.ZodAny>;
166
+ output: z.ZodDiscriminatedUnion<"responseType", [z.ZodObject<{
167
+ responseType: z.ZodLiteral<"html">;
168
+ staticFiles: z.ZodOptional<z.ZodNever>;
169
+ }, "strip", z.ZodTypeAny, {
170
+ responseType: "html";
171
+ staticFiles?: undefined;
172
+ }, {
173
+ responseType: "html";
174
+ staticFiles?: undefined;
175
+ }>, z.ZodObject<{
176
+ responseType: z.ZodLiteral<"json">;
177
+ }, "strip", z.ZodTypeAny, {
178
+ responseType: "json";
179
+ }, {
180
+ responseType: "json";
181
+ }>]>;
182
+ }, "strip", z.ZodTypeAny, {
183
+ name: string;
184
+ input: Record<string, any>;
185
+ entry: string;
186
+ output: {
187
+ responseType: "html";
188
+ staticFiles?: undefined;
189
+ } | {
190
+ responseType: "json";
191
+ };
192
+ }, {
193
+ name: string;
194
+ input: Record<string, any>;
195
+ entry: string;
196
+ output: {
197
+ responseType: "html";
198
+ staticFiles?: undefined;
199
+ } | {
200
+ responseType: "json";
201
+ };
202
+ }>, "many">, {
203
+ name: string;
204
+ input: Record<string, any>;
205
+ entry: string;
206
+ output: {
207
+ responseType: "html";
208
+ staticFiles?: undefined;
209
+ } | {
210
+ responseType: "json";
211
+ };
212
+ }[], {
213
+ name: string;
214
+ input: Record<string, any>;
215
+ entry: string;
216
+ output: {
217
+ responseType: "html";
218
+ staticFiles?: undefined;
219
+ } | {
220
+ responseType: "json";
221
+ };
222
+ }[]>, {
223
+ name: string;
224
+ input: Record<string, any>;
225
+ entry: string;
226
+ output: {
227
+ responseType: "html";
228
+ staticFiles?: undefined;
229
+ } | {
230
+ responseType: "json";
231
+ };
232
+ }[], {
233
+ name: string;
234
+ input: Record<string, any>;
235
+ entry: string;
236
+ output: {
237
+ responseType: "html";
238
+ staticFiles?: undefined;
239
+ } | {
240
+ responseType: "json";
241
+ };
242
+ }[]>;
243
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
244
+ type: z.ZodLiteral<"edge">;
245
+ mainFunction: z.ZodString;
246
+ functions: z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodObject<{
247
+ name: z.ZodString;
248
+ entry: z.ZodString;
249
+ input: z.ZodRecord<z.ZodString, z.ZodAny>;
250
+ output: z.ZodDiscriminatedUnion<"responseType", [z.ZodObject<{
251
+ responseType: z.ZodLiteral<"html">;
252
+ staticFiles: z.ZodOptional<z.ZodNever>;
253
+ }, "strip", z.ZodTypeAny, {
254
+ responseType: "html";
255
+ staticFiles?: undefined;
256
+ }, {
257
+ responseType: "html";
258
+ staticFiles?: undefined;
259
+ }>, z.ZodObject<{
260
+ responseType: z.ZodLiteral<"json">;
261
+ }, "strip", z.ZodTypeAny, {
262
+ responseType: "json";
263
+ }, {
264
+ responseType: "json";
265
+ }>]>;
266
+ }, "strip", z.ZodTypeAny, {
267
+ name: string;
268
+ input: Record<string, any>;
269
+ entry: string;
270
+ output: {
271
+ responseType: "html";
272
+ staticFiles?: undefined;
273
+ } | {
274
+ responseType: "json";
275
+ };
276
+ }, {
277
+ name: string;
278
+ input: Record<string, any>;
279
+ entry: string;
280
+ output: {
281
+ responseType: "html";
282
+ staticFiles?: undefined;
283
+ } | {
284
+ responseType: "json";
285
+ };
286
+ }>, "many">, {
287
+ name: string;
288
+ input: Record<string, any>;
289
+ entry: string;
290
+ output: {
291
+ responseType: "html";
292
+ staticFiles?: undefined;
293
+ } | {
294
+ responseType: "json";
295
+ };
296
+ }[], {
297
+ name: string;
298
+ input: Record<string, any>;
299
+ entry: string;
300
+ output: {
301
+ responseType: "html";
302
+ staticFiles?: undefined;
303
+ } | {
304
+ responseType: "json";
305
+ };
306
+ }[]>, {
307
+ name: string;
308
+ input: Record<string, any>;
309
+ entry: string;
310
+ output: {
311
+ responseType: "html";
312
+ staticFiles?: undefined;
313
+ } | {
314
+ responseType: "json";
315
+ };
316
+ }[], {
317
+ name: string;
318
+ input: Record<string, any>;
319
+ entry: string;
320
+ output: {
321
+ responseType: "html";
322
+ staticFiles?: undefined;
323
+ } | {
324
+ responseType: "json";
325
+ };
326
+ }[]>;
327
+ }, z.ZodTypeAny, "passthrough">>]>, z.ZodObject<{
328
+ mockedUris: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
329
+ type: z.ZodLiteral<"inline">;
330
+ value: z.ZodRecord<z.ZodString, z.ZodAny>;
331
+ }, "strip", z.ZodTypeAny, {
332
+ type: "inline";
333
+ value: Record<string, any>;
334
+ }, {
335
+ type: "inline";
336
+ value: Record<string, any>;
337
+ }>, z.ZodObject<{
338
+ type: z.ZodLiteral<"file">;
339
+ path: z.ZodString;
340
+ }, "strip", z.ZodTypeAny, {
341
+ type: "file";
342
+ path: string;
343
+ }, {
344
+ type: "file";
345
+ path: string;
346
+ }>]>>>;
347
+ previews: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
348
+ functionData: z.ZodRecord<z.ZodString, z.ZodObject<{
349
+ inputData: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
350
+ type: z.ZodLiteral<"inline">;
351
+ value: z.ZodRecord<z.ZodString, z.ZodAny>;
352
+ }, "strip", z.ZodTypeAny, {
353
+ type: "inline";
354
+ value: Record<string, any>;
355
+ }, {
356
+ type: "inline";
357
+ value: Record<string, any>;
358
+ }>, z.ZodObject<{
359
+ type: z.ZodLiteral<"file">;
360
+ path: z.ZodString;
361
+ }, "strip", z.ZodTypeAny, {
362
+ type: "file";
363
+ path: string;
364
+ }, {
365
+ type: "file";
366
+ path: string;
367
+ }>]>;
368
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
369
+ wrapper: z.ZodOptional<z.ZodObject<{
370
+ path: z.ZodString;
371
+ }, "strip", z.ZodTypeAny, {
372
+ path: string;
373
+ }, {
374
+ path: string;
375
+ }>>;
376
+ }, "strip", z.ZodTypeAny, {
377
+ inputData: {
378
+ type: "inline";
379
+ value: Record<string, any>;
380
+ } | {
381
+ type: "file";
382
+ path: string;
383
+ };
384
+ headers?: Record<string, string> | undefined;
385
+ wrapper?: {
386
+ path: string;
387
+ } | undefined;
388
+ }, {
389
+ inputData: {
390
+ type: "inline";
391
+ value: Record<string, any>;
392
+ } | {
393
+ type: "file";
394
+ path: string;
395
+ };
396
+ headers?: Record<string, string> | undefined;
397
+ wrapper?: {
398
+ path: string;
399
+ } | undefined;
400
+ }>>;
401
+ }, "strip", z.ZodTypeAny, {
402
+ functionData: Record<string, {
403
+ inputData: {
404
+ type: "inline";
405
+ value: Record<string, any>;
406
+ } | {
407
+ type: "file";
408
+ path: string;
409
+ };
410
+ headers?: Record<string, string> | undefined;
411
+ wrapper?: {
412
+ path: string;
413
+ } | undefined;
414
+ }>;
415
+ }, {
416
+ functionData: Record<string, {
417
+ inputData: {
418
+ type: "inline";
419
+ value: Record<string, any>;
420
+ } | {
421
+ type: "file";
422
+ path: string;
423
+ };
424
+ headers?: Record<string, string> | undefined;
425
+ wrapper?: {
426
+ path: string;
427
+ } | undefined;
428
+ }>;
429
+ }>>>;
430
+ }, "strip", z.ZodTypeAny, {
431
+ mockedUris?: Record<string, {
432
+ type: "inline";
433
+ value: Record<string, any>;
434
+ } | {
435
+ type: "file";
436
+ path: string;
437
+ }> | undefined;
438
+ previews?: Record<string, {
439
+ functionData: Record<string, {
440
+ inputData: {
441
+ type: "inline";
442
+ value: Record<string, any>;
443
+ } | {
444
+ type: "file";
445
+ path: string;
446
+ };
447
+ headers?: Record<string, string> | undefined;
448
+ wrapper?: {
449
+ path: string;
450
+ } | undefined;
451
+ }>;
452
+ }> | undefined;
453
+ }, {
454
+ mockedUris?: Record<string, {
455
+ type: "inline";
456
+ value: Record<string, any>;
457
+ } | {
458
+ type: "file";
459
+ path: string;
460
+ }> | undefined;
461
+ previews?: Record<string, {
462
+ functionData: Record<string, {
463
+ inputData: {
464
+ type: "inline";
465
+ value: Record<string, any>;
466
+ } | {
467
+ type: "file";
468
+ path: string;
469
+ };
470
+ headers?: Record<string, string> | undefined;
471
+ wrapper?: {
472
+ path: string;
473
+ } | undefined;
474
+ }>;
475
+ }> | undefined;
476
+ }>>;
477
+ export declare type BasicDefinition = z.infer<typeof BasicDefinition>;
478
+ export declare const COMPILED_ENTRY_FILE = "userCode.js";
@@ -31,25 +31,51 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
31
31
  step((generator = generator.apply(thisArg, _arguments || [])).next());
32
32
  });
33
33
  };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
34
37
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.ComponentDefinition = void 0;
38
+ exports.COMPILED_ENTRY_FILE = exports.BasicDefinition = exports.ComponentDefinition = void 0;
36
39
  const fs = __importStar(require("node:fs/promises"));
37
40
  const path = __importStar(require("node:path"));
41
+ const cli_color_1 = __importDefault(require("cli-color"));
38
42
  const zod_1 = require("zod");
43
+ const zod_validation_error_1 = require("zod-validation-error");
39
44
  class ComponentDefinition {
40
45
  constructor(baseDir, definition) {
41
46
  this.baseDir = baseDir;
42
47
  this.definition = definition;
43
48
  }
44
49
  static load(componentDirectory) {
50
+ var _a;
45
51
  return __awaiter(this, void 0, void 0, function* () {
46
52
  try {
47
53
  const manifest = yield fs.readFile(path.resolve(componentDirectory, 'manifest.json'), { encoding: 'utf-8' });
48
- const definition = yield BasicDefinition.parseAsync(JSON.parse(manifest));
54
+ const definition = yield exports.BasicDefinition.parseAsync(JSON.parse(manifest));
49
55
  return new ComponentDefinition(componentDirectory, definition);
50
56
  }
51
57
  catch (e) {
52
- console.error(e);
58
+ if (e instanceof SyntaxError) {
59
+ console.error('"manifest.json" is not valid JSON');
60
+ }
61
+ else if (e instanceof zod_1.ZodError) {
62
+ let message;
63
+ if ((message = (_a = e.issues.find(i => i.path.includes('staticFiles'))) === null || _a === void 0 ? void 0 : _a.message)) {
64
+ console.error(cli_color_1.default.yellowBright(message));
65
+ }
66
+ else {
67
+ console.error(cli_color_1.default.redBright('"manifest.json" does not have the required format'));
68
+ console.error(cli_color_1.default.redBright((0, zod_validation_error_1.fromError)(e).toString()));
69
+ }
70
+ }
71
+ else if (e instanceof Error) {
72
+ if (e.message.includes('no such file or directory')) {
73
+ console.error(cli_color_1.default.redBright('"manifest.json" could not be found'));
74
+ }
75
+ else {
76
+ console.error('Failed to load component: %s', e);
77
+ }
78
+ }
53
79
  }
54
80
  });
55
81
  }
@@ -67,15 +93,11 @@ class ComponentDefinition {
67
93
  }
68
94
  copyManifest(copyDir) {
69
95
  return __awaiter(this, void 0, void 0, function* () {
70
- yield fs.copyFile(this.manifestPath, path.resolve(copyDir, 'manifest.json'));
71
- const manifest = yield fs.readFile(path.resolve(copyDir, 'manifest.json'), {
72
- encoding: 'utf-8',
73
- });
74
- const definition = JSON.parse(manifest);
75
- definition.functions = definition.functions.map((func) => {
76
- return Object.assign(Object.assign({}, func), { entry: 'userCode.js' });
96
+ const copy = yield exports.BasicDefinition.parseAsync(this.definition);
97
+ copy.functions = this.definition.functions.map(func => {
98
+ return Object.assign(Object.assign({}, func), { entry: exports.COMPILED_ENTRY_FILE });
77
99
  });
78
- return yield fs.writeFile(path.resolve(copyDir, 'manifest.json'), JSON.stringify(definition));
100
+ return yield fs.writeFile(path.resolve(copyDir, 'manifest.json'), JSON.stringify(copy));
79
101
  });
80
102
  }
81
103
  copyPreviewFiles(copyDir) {
@@ -87,9 +109,9 @@ class ComponentDefinition {
87
109
  for (const [_funcName, funcInput] of Object.entries(value.functionData)) {
88
110
  if (funcInput.inputData.type === 'file') {
89
111
  yield fs.copyFile(path.resolve(this.baseDir, funcInput.inputData.path), path.resolve(copyDir, funcInput.inputData.path));
90
- if (funcInput.wrapper) {
91
- yield fs.copyFile(path.resolve(this.baseDir, funcInput.wrapper.path), path.resolve(copyDir, funcInput.wrapper.path));
92
- }
112
+ }
113
+ if (funcInput.wrapper) {
114
+ yield fs.copyFile(path.resolve(this.baseDir, funcInput.wrapper.path), path.resolve(copyDir, funcInput.wrapper.path));
93
115
  }
94
116
  }
95
117
  }
@@ -109,7 +131,9 @@ class ComponentDefinition {
109
131
  }
110
132
  }
111
133
  exports.ComponentDefinition = ComponentDefinition;
112
- const BasicDefinition = zod_1.z.object({
134
+ const ServerDefinition = zod_1.z
135
+ .object({
136
+ type: zod_1.z.literal('server'),
113
137
  mainFunction: zod_1.z.string(),
114
138
  functions: zod_1.z
115
139
  .array(zod_1.z.object({
@@ -117,7 +141,44 @@ const BasicDefinition = zod_1.z.object({
117
141
  entry: zod_1.z.string(),
118
142
  }))
119
143
  .refine(fns => new Set(fns.map(f => f.name)).size === fns.length),
120
- type: zod_1.z.enum(['edge', 'server']).optional().default('server'),
144
+ })
145
+ .passthrough();
146
+ const EdgeDefinition = zod_1.z
147
+ .object({
148
+ type: zod_1.z.literal('edge'),
149
+ mainFunction: zod_1.z.string(),
150
+ functions: zod_1.z
151
+ .array(zod_1.z.object({
152
+ name: zod_1.z.string(),
153
+ entry: zod_1.z.string(),
154
+ input: zod_1.z.record(zod_1.z.any()),
155
+ output: zod_1.z.discriminatedUnion('responseType', [
156
+ zod_1.z.object({
157
+ responseType: zod_1.z.literal('html'),
158
+ staticFiles: zod_1.z
159
+ .never({
160
+ invalid_type_error: 'Static files are not supported in edge and have been removed',
161
+ })
162
+ .optional(),
163
+ }),
164
+ zod_1.z.object({
165
+ responseType: zod_1.z.literal('json'),
166
+ }),
167
+ ]),
168
+ }))
169
+ // All names should be unique
170
+ .refine(fns => new Set(fns.map(f => f.name)).size === fns.length, {
171
+ message: 'Function names must be unique',
172
+ })
173
+ // All entry files should be the same
174
+ .refine(fns => new Set(fns.map(f => f.entry)).size === 1, {
175
+ message: 'Function entries must be the same',
176
+ }),
177
+ })
178
+ .passthrough();
179
+ exports.BasicDefinition = zod_1.z
180
+ .discriminatedUnion('type', [ServerDefinition, EdgeDefinition])
181
+ .and(zod_1.z.object({
121
182
  mockedUris: zod_1.z
122
183
  .record(zod_1.z.string(), zod_1.z.discriminatedUnion('type', [
123
184
  zod_1.z.object({
@@ -152,4 +213,5 @@ const BasicDefinition = zod_1.z.object({
152
213
  })),
153
214
  }))
154
215
  .optional(),
155
- });
216
+ }));
217
+ exports.COMPILED_ENTRY_FILE = 'userCode.js';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,427 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ const fs = __importStar(require("node:fs/promises"));
36
+ const path = __importStar(require("node:path"));
37
+ const definitions_1 = require("./definitions");
38
+ const tempRootDir = path.join('.testing', 'definitions');
39
+ function makeTempDirectory() {
40
+ return __awaiter(this, void 0, void 0, function* () {
41
+ yield fs.mkdir(tempRootDir, { recursive: true });
42
+ const tempDir = yield fs.mkdtemp(tempRootDir + '/', {
43
+ encoding: 'utf-8',
44
+ });
45
+ return tempDir;
46
+ });
47
+ }
48
+ function writeManifest(dir, content) {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ content = typeof content !== 'string' ? JSON.stringify(content) : content;
51
+ yield fs.writeFile(path.join(dir, 'manifest.json'), content);
52
+ });
53
+ }
54
+ function createManifest(manifestContent = {}) {
55
+ return Object.assign({ functions: [
56
+ {
57
+ name: 'main',
58
+ entry: 'main.entry.js',
59
+ input: {},
60
+ output: {
61
+ responseType: 'html',
62
+ },
63
+ },
64
+ ], mainFunction: 'main', type: 'edge' }, manifestContent);
65
+ }
66
+ function createComponentDefinition(manifest = {}) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ const tempDir = yield makeTempDirectory();
69
+ yield writeManifest(tempDir, createManifest(manifest));
70
+ return (yield definitions_1.ComponentDefinition.load(tempDir));
71
+ });
72
+ }
73
+ describe('cmp > utils > definitions', () => {
74
+ afterAll(() => __awaiter(void 0, void 0, void 0, function* () {
75
+ yield fs.rm(tempRootDir, { recursive: true, force: true });
76
+ }));
77
+ describe('BasicDefinition', () => {
78
+ //////////////////////////////
79
+ // STATIC FILES DEPRECATION //
80
+ //////////////////////////////
81
+ test('error when staticFiles are defined in an edge component', () => {
82
+ var _a;
83
+ const manifest = createManifest({ type: 'edge' });
84
+ manifest.functions = manifest.functions.map(f => (Object.assign(Object.assign({}, f), { output: { responseType: 'html', staticFiles: {} } })));
85
+ const result = definitions_1.BasicDefinition.safeParse(manifest);
86
+ expect(result).toMatchObject({ success: false });
87
+ expect((_a = result.error) === null || _a === void 0 ? void 0 : _a.issues).toContainEqual(expect.objectContaining({
88
+ message: 'Static files are not supported in edge and have been removed',
89
+ }));
90
+ });
91
+ test('no error when staticFiles are defined in a server component', () => {
92
+ const manifest = createManifest({ type: 'server' });
93
+ manifest.functions = manifest.functions.map(f => (Object.assign(Object.assign({}, f), { output: { responseType: 'html', staticFiles: {} } })));
94
+ const result = definitions_1.BasicDefinition.safeParse(manifest);
95
+ expect(result).toMatchObject({ success: true });
96
+ });
97
+ //////////////
98
+ // PREVIEWS //
99
+ //////////////
100
+ test.each(['server', 'edge'])('%s components correctly parse preview properties', () => {
101
+ const manifest = createManifest({
102
+ previews: {
103
+ 'inline-preview': {
104
+ functionData: {
105
+ main: {
106
+ inputData: {
107
+ type: 'inline',
108
+ value: {
109
+ message: 'test-message',
110
+ },
111
+ },
112
+ headers: {
113
+ 'cache-control': '10',
114
+ },
115
+ wrapper: {
116
+ path: 'test-wrapper.html',
117
+ },
118
+ },
119
+ },
120
+ },
121
+ },
122
+ });
123
+ const result = definitions_1.BasicDefinition.safeParse(manifest);
124
+ expect(result).toMatchObject({ success: true });
125
+ });
126
+ test.each(['server', 'edge'])('%s components correctly parse mockedUri properties', () => {
127
+ const manifest = createManifest({
128
+ mockedUris: {
129
+ 'test-uri': {
130
+ type: 'inline',
131
+ value: {
132
+ result: 'test-uri-result',
133
+ },
134
+ },
135
+ },
136
+ });
137
+ const result = definitions_1.BasicDefinition.safeParse(manifest);
138
+ expect(result).toMatchObject({ success: true });
139
+ });
140
+ test.each(['server', 'edge'])('%s components do not remove extra properties', type => {
141
+ const manifest = createManifest({
142
+ type,
143
+ extraProperty: 'my-extra-property',
144
+ });
145
+ const result = definitions_1.BasicDefinition.safeParse(manifest);
146
+ expect(result.data).toMatchObject({
147
+ extraProperty: 'my-extra-property',
148
+ });
149
+ });
150
+ });
151
+ describe('ComponentDefinition.load()', () => {
152
+ /////////////////
153
+ // ERROR CASES //
154
+ /////////////////
155
+ test('does not load a definition when no manifest.json is found', () => __awaiter(void 0, void 0, void 0, function* () {
156
+ const dir = yield makeTempDirectory();
157
+ yield expect(definitions_1.ComponentDefinition.load(dir)).resolves.toBeUndefined();
158
+ }));
159
+ test('does not load a definition when the manifest.json is not json', () => __awaiter(void 0, void 0, void 0, function* () {
160
+ const dir = yield makeTempDirectory();
161
+ yield writeManifest(dir, 'not json');
162
+ yield expect(definitions_1.ComponentDefinition.load(dir)).resolves.toBeUndefined();
163
+ }));
164
+ test('does not load when the manifest.json is not a valid definition', () => __awaiter(void 0, void 0, void 0, function* () {
165
+ const dir = yield makeTempDirectory();
166
+ yield writeManifest(dir, { wrongKey: '' });
167
+ yield expect(definitions_1.ComponentDefinition.load(dir)).resolves.toBeUndefined();
168
+ }));
169
+ ///////////////////
170
+ // SUCCESS CASES //
171
+ ///////////////////
172
+ test('successfully returns a ComponentDefinition instance with a valid manifest.json', () => __awaiter(void 0, void 0, void 0, function* () {
173
+ const dir = yield makeTempDirectory();
174
+ yield writeManifest(dir, createManifest());
175
+ yield expect(definitions_1.ComponentDefinition.load(dir)).resolves.toBeInstanceOf(definitions_1.ComponentDefinition);
176
+ }));
177
+ });
178
+ describe('ComponentDefinition properties', () => {
179
+ let componentDirectory;
180
+ let definition;
181
+ const manifest = createManifest();
182
+ beforeAll(() => __awaiter(void 0, void 0, void 0, function* () {
183
+ componentDirectory = yield makeTempDirectory();
184
+ yield writeManifest(componentDirectory, manifest);
185
+ definition = (yield definitions_1.ComponentDefinition.load(componentDirectory));
186
+ }));
187
+ test('.manifestPath returns the full path to manifest.json', () => __awaiter(void 0, void 0, void 0, function* () {
188
+ expect(definition.manifestPath).toEqual(path.resolve(componentDirectory, 'manifest.json'));
189
+ }));
190
+ test('.mainEntryFilePath returns the full path to the entry file', () => __awaiter(void 0, void 0, void 0, function* () {
191
+ const mainEntryFileName = manifest.functions.find(f => f.name === manifest.mainFunction).entry;
192
+ expect(definition.mainEntryFilePath).toEqual(path.resolve(componentDirectory, mainEntryFileName));
193
+ }));
194
+ test('.mainFunction returns the function set as mainFunction', () => __awaiter(void 0, void 0, void 0, function* () {
195
+ expect(definition.mainFunction).toEqual(manifest.functions.find(f => f.name === manifest.mainFunction));
196
+ }));
197
+ test('.type return the component type', () => __awaiter(void 0, void 0, void 0, function* () {
198
+ expect(definition.type).toEqual(manifest.type);
199
+ }));
200
+ });
201
+ describe('ComponentDefinition#copyManifest', () => {
202
+ test('copies the manifest.json to target directory', () => __awaiter(void 0, void 0, void 0, function* () {
203
+ const definition = yield createComponentDefinition();
204
+ const copyTarget = yield makeTempDirectory();
205
+ yield definition.copyManifest(copyTarget);
206
+ yield expect(fs.access(path.join(copyTarget, 'manifest.json'))).resolves.toBeUndefined();
207
+ }));
208
+ test('copied manifest.json sets file entry to compiled file name', () => __awaiter(void 0, void 0, void 0, function* () {
209
+ const definition = yield createComponentDefinition();
210
+ const copyTarget = yield makeTempDirectory();
211
+ yield definition.copyManifest(copyTarget);
212
+ const copiedDefinition = yield definitions_1.ComponentDefinition.load(copyTarget);
213
+ expect(copiedDefinition === null || copiedDefinition === void 0 ? void 0 : copiedDefinition.mainEntryFilePath).toMatch(`${definitions_1.COMPILED_ENTRY_FILE}`);
214
+ }));
215
+ test('does not remove extraneous properties in manifest', () => __awaiter(void 0, void 0, void 0, function* () {
216
+ const definition = yield createComponentDefinition({
217
+ extraValue: 'string',
218
+ });
219
+ const copyTarget = yield makeTempDirectory();
220
+ yield definition.copyManifest(copyTarget);
221
+ const copiedDefinition = yield definitions_1.ComponentDefinition.load(copyTarget);
222
+ expect(copiedDefinition === null || copiedDefinition === void 0 ? void 0 : copiedDefinition.definition).toMatchObject({
223
+ extraValue: 'string',
224
+ });
225
+ }));
226
+ });
227
+ describe('ComponentDefinition#copyPreviewFiles', () => {
228
+ test('preview data in files are copied to target', () => __awaiter(void 0, void 0, void 0, function* () {
229
+ const definition = yield createComponentDefinition({
230
+ previews: {
231
+ 'file-preview': {
232
+ functionData: {
233
+ main: {
234
+ inputData: {
235
+ type: 'file',
236
+ path: 'file-preview.data.json',
237
+ },
238
+ wrapper: {
239
+ path: 'preview-wrapper.html',
240
+ },
241
+ },
242
+ },
243
+ },
244
+ },
245
+ });
246
+ yield fs.writeFile(path.join(definition.baseDir, 'file-preview.data.json'), '{}');
247
+ yield fs.writeFile(path.join(definition.baseDir, 'preview-wrapper.html'), '<html></html>');
248
+ const copyTarget = yield makeTempDirectory();
249
+ yield definition.copyPreviewFiles(copyTarget);
250
+ yield expect(fs.access(path.join(copyTarget, 'file-preview.data.json'))).resolves.toBe(undefined);
251
+ yield expect(fs.access(path.join(copyTarget, 'preview-wrapper.html'))).resolves.toBe(undefined);
252
+ }));
253
+ test('multiple previews are copied to target', () => __awaiter(void 0, void 0, void 0, function* () {
254
+ const definition = yield createComponentDefinition({
255
+ previews: {
256
+ 'file-preview': {
257
+ functionData: {
258
+ main: {
259
+ inputData: {
260
+ type: 'file',
261
+ path: 'file-preview.data.json',
262
+ },
263
+ },
264
+ },
265
+ },
266
+ 'another-file-preview': {
267
+ functionData: {
268
+ main: {
269
+ inputData: {
270
+ type: 'file',
271
+ path: 'another-file-preview.data.json',
272
+ },
273
+ },
274
+ },
275
+ },
276
+ },
277
+ });
278
+ yield fs.writeFile(path.join(definition.baseDir, 'file-preview.data.json'), '{}');
279
+ yield fs.writeFile(path.join(definition.baseDir, 'another-file-preview.data.json'), '{}');
280
+ const copyTarget = yield makeTempDirectory();
281
+ yield definition.copyPreviewFiles(copyTarget);
282
+ yield expect(fs.access(path.join(copyTarget, 'file-preview.data.json'))).resolves.toBeUndefined();
283
+ yield expect(fs.access(path.join(copyTarget, 'another-file-preview.data.json'))).resolves.toBeUndefined();
284
+ }));
285
+ test('multiple functions in a preview are copied to target', () => __awaiter(void 0, void 0, void 0, function* () {
286
+ const definition = yield createComponentDefinition({
287
+ previews: {
288
+ 'file-preview': {
289
+ functionData: {
290
+ main: {
291
+ inputData: {
292
+ type: 'file',
293
+ path: 'file-preview.data.json',
294
+ },
295
+ },
296
+ other: {
297
+ inputData: {
298
+ type: 'file',
299
+ path: 'another-file-preview.data.json',
300
+ },
301
+ },
302
+ },
303
+ },
304
+ },
305
+ });
306
+ yield fs.writeFile(path.join(definition.baseDir, 'file-preview.data.json'), '{}');
307
+ yield fs.writeFile(path.join(definition.baseDir, 'another-file-preview.data.json'), '{}');
308
+ const copyTarget = yield makeTempDirectory();
309
+ yield definition.copyPreviewFiles(copyTarget);
310
+ yield expect(fs.access(path.join(copyTarget, 'file-preview.data.json'))).resolves.toBeUndefined();
311
+ yield expect(fs.access(path.join(copyTarget, 'another-file-preview.data.json'))).resolves.toBeUndefined();
312
+ }));
313
+ test('preview wrapper is copied when inputData is inline', () => __awaiter(void 0, void 0, void 0, function* () {
314
+ const definition = yield createComponentDefinition({
315
+ previews: {
316
+ 'file-wrapper': {
317
+ functionData: {
318
+ main: {
319
+ inputData: {
320
+ type: 'inline',
321
+ value: {
322
+ message: 'inline-preview',
323
+ },
324
+ },
325
+ wrapper: {
326
+ path: 'preview-wrapper.html',
327
+ },
328
+ },
329
+ },
330
+ },
331
+ },
332
+ });
333
+ yield fs.writeFile(path.join(definition.baseDir, 'preview-wrapper.html'), '<html></html>');
334
+ const copyTarget = yield makeTempDirectory();
335
+ yield definition.copyPreviewFiles(copyTarget);
336
+ yield expect(fs.access(path.join(copyTarget, 'preview-wrapper.html'))).resolves.toBeUndefined();
337
+ }));
338
+ test('files not defined in previews are not copied to target', () => __awaiter(void 0, void 0, void 0, function* () {
339
+ const definition = yield createComponentDefinition({
340
+ previews: {
341
+ 'file-preview': {
342
+ functionData: {
343
+ main: {
344
+ inputData: {
345
+ type: 'file',
346
+ path: 'file-preview.data.json',
347
+ },
348
+ },
349
+ },
350
+ },
351
+ },
352
+ });
353
+ yield fs.writeFile(path.join(definition.baseDir, 'file-preview.data.json'), '{}');
354
+ yield fs.writeFile(path.join(definition.baseDir, 'not-preview.data-json'), '{}');
355
+ const copyTarget = yield makeTempDirectory();
356
+ yield definition.copyPreviewFiles(copyTarget);
357
+ expect(fs.access(path.join(copyTarget, 'not-preview.data.json'))).rejects.toBeTruthy();
358
+ }));
359
+ test('inline preview data is not attempted to copy', () => __awaiter(void 0, void 0, void 0, function* () {
360
+ const definition = yield createComponentDefinition({
361
+ previews: {
362
+ 'inline-preview': {
363
+ functionData: {
364
+ main: {
365
+ inputData: {
366
+ type: 'inline',
367
+ value: {
368
+ message: 'inline-preview',
369
+ },
370
+ },
371
+ },
372
+ },
373
+ },
374
+ },
375
+ });
376
+ const copyTarget = yield makeTempDirectory();
377
+ yield definition.copyPreviewFiles(copyTarget);
378
+ yield expect(fs.readdir(copyTarget)).resolves.toEqual([]);
379
+ }));
380
+ test('does not error when a component has no previews', () => __awaiter(void 0, void 0, void 0, function* () {
381
+ const definition = yield createComponentDefinition({});
382
+ const copyTarget = yield makeTempDirectory();
383
+ yield expect(definition.copyPreviewFiles(copyTarget)).resolves.toBeUndefined();
384
+ }));
385
+ });
386
+ describe('ComponentDefinition#copyMockedUriFiles', () => {
387
+ test('mockedUri defined in a file is copied to target', () => __awaiter(void 0, void 0, void 0, function* () {
388
+ const definition = yield createComponentDefinition({
389
+ mockedUris: {
390
+ 'test-uri': {
391
+ type: 'file',
392
+ path: 'test-uri.data.json',
393
+ },
394
+ },
395
+ });
396
+ yield fs.writeFile(path.join(definition.baseDir, 'test-uri.data.json'), '{}');
397
+ const copyTarget = yield makeTempDirectory();
398
+ yield definition.copyMockedUriFiles(copyTarget);
399
+ yield expect(fs.access(path.join(copyTarget, 'test-uri.data.json'))).resolves.toBeUndefined();
400
+ }));
401
+ test('multiple mockedUri files are copied to target', () => __awaiter(void 0, void 0, void 0, function* () {
402
+ const definition = yield createComponentDefinition({
403
+ mockedUris: {
404
+ 'test-uri': {
405
+ type: 'file',
406
+ path: 'test-uri.data.json',
407
+ },
408
+ 'another-test-uri': {
409
+ type: 'file',
410
+ path: 'another-test-uri.data.json',
411
+ },
412
+ },
413
+ });
414
+ yield fs.writeFile(path.join(definition.baseDir, 'test-uri.data.json'), '{}');
415
+ yield fs.writeFile(path.join(definition.baseDir, 'another-test-uri.data.json'), '{}');
416
+ const copyTarget = yield makeTempDirectory();
417
+ yield definition.copyMockedUriFiles(copyTarget);
418
+ yield expect(fs.access(path.join(copyTarget, 'test-uri.data.json'))).resolves.toBeUndefined();
419
+ yield expect(fs.access(path.join(copyTarget, 'another-test-uri.data.json'))).resolves.toBeUndefined();
420
+ }));
421
+ test('no error when no mockedUris defined', () => __awaiter(void 0, void 0, void 0, function* () {
422
+ const definition = yield createComponentDefinition();
423
+ const copyTarget = yield makeTempDirectory();
424
+ yield expect(definition.copyMockedUriFiles(copyTarget)).resolves.toBeUndefined();
425
+ }));
426
+ });
427
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/dxp-cli-next",
3
- "version": "5.19.0-develop.1",
3
+ "version": "5.19.0-develop.2",
4
4
  "repository": {
5
5
  "url": "https://gitlab.squiz.net/dxp/dxp-cli-next"
6
6
  },
@@ -57,7 +57,8 @@
57
57
  "tough-cookie": "4.1.2",
58
58
  "update-notifier": "5.1.0",
59
59
  "yaml": "^2.3.4",
60
- "zod": "^3.23.8"
60
+ "zod": "^3.23.8",
61
+ "zod-validation-error": "^3.3.1"
61
62
  },
62
63
  "devDependencies": {
63
64
  "@semantic-release/git": "10.0.1",