@raisenow/tamaro-cli 1.6.0-beta.0 → 1.6.0-dev.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/README.md CHANGED
@@ -249,7 +249,6 @@ the CloudFront cache. Always asks for confirmation unless `-y` is passed.
249
249
  - `--tag <tag>` – Tag which should be undeployed (default: "latest")
250
250
  - `--all` – Undeploy all tags
251
251
  - `--dryrun` – Displays the operations that would be performed without actually running them
252
- - `-y, --yes` – Skip confirmation prompt
253
252
 
254
253
  ### Undeploy the "latest" tag
255
254
 
@@ -274,13 +273,12 @@ npx -y @raisenow/tamaro-cli undeploy --all
274
273
 
275
274
  ## `undeploy-email-config`
276
275
 
277
- Undeploy a widget's email configuration from AWS S3. Always asks for confirmation unless `-y` is passed.
276
+ Undeploy a widget's email configuration from AWS S3.
278
277
 
279
278
  ### Options
280
279
 
281
280
  - `--bucket <bucket>` – AWS bucket for email configs
282
281
  - `--dryrun` – Displays the operations that would be performed without actually running them
283
- - `-y, --yes` – Skip confirmation prompt
284
282
 
285
283
  ### Example
286
284
 
@@ -291,14 +289,11 @@ npx -y @raisenow/tamaro-cli undeploy-email-config
291
289
 
292
290
  ## `archive`
293
291
 
294
- Archive a customer configuration. This moves the configuration folder into the `_archived` folder, undeploys all tags
295
- from S3, and undeploys the email configuration from both stage and prod. Archived widgets cannot be archived again.
296
- Always asks for confirmation unless `-y` is passed.
292
+ Archive a customer configuration. This moves the configuration folder into the `_archived` folder.
297
293
 
298
294
  ### Options
299
295
 
300
296
  - `--dryrun` – Displays the operations that would be performed without actually running them
301
- - `-y, --yes` – Skip confirmation prompt
302
297
 
303
298
  ### Example
304
299
 
@@ -309,14 +304,11 @@ npx -y @raisenow/tamaro-cli archive
309
304
 
310
305
  ## `unarchive`
311
306
 
312
- Unarchive a previously archived customer configuration. This moves the configuration out of the `_archived` folder,
313
- builds the bundle, deploys it to the "latest" tag, and deploys the email configuration. Only archived widgets can be
314
- unarchived. Always asks for confirmation unless `-y` is passed.
307
+ Unarchive a previously archived customer configuration. This moves the configuration out of the `_archived` folder.
315
308
 
316
309
  ### Options
317
310
 
318
311
  - `--dryrun` – Displays the operations that would be performed without actually running them
319
- - `-y, --yes` – Skip confirmation prompt
320
312
 
321
313
  ### Example
322
314
 
@@ -34,6 +34,7 @@ var logDataTable = (data) => {
34
34
 
35
35
  // src/lib/command.ts
36
36
  import { execaCommandSync } from "execa";
37
+ import prompts from "prompts";
37
38
  var halt = (message) => {
38
39
  logError(message);
39
40
  process.exit(1);
@@ -44,17 +45,27 @@ var prepareCommand = (cmd) => {
44
45
  var runCommandSync = (cmd, options) => {
45
46
  return execaCommandSync(prepareCommand(cmd), options);
46
47
  };
47
-
48
- // src/lib/constants.ts
49
- var DEFAULT_TAG = "latest";
50
- var DEFAULT_PORT = 1234;
51
- var AWS_S3_BUCKET_TAMARO = "tamaro.raisenow.com";
52
- var AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE = "rnw-stage-email-service";
53
- var AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD = "rnw-email-service";
54
- var CORE_CONFIG_NAME = "tamaro-core";
55
- var AWS_CLOUDFRONT_DISTRIBUTION_ID = "EHJ1OM458YQ0I";
56
- var HTTPS_CRT_FILE = "localhost.crt";
57
- var HTTPS_KEY_FILE = "localhost.key";
48
+ var promptConfirmation = async (message, skipConfirmation) => {
49
+ if (skipConfirmation) {
50
+ return;
51
+ }
52
+ const { confirmed } = await prompts(
53
+ [
54
+ {
55
+ type: "confirm",
56
+ name: "confirmed",
57
+ message,
58
+ initial: false
59
+ }
60
+ ],
61
+ {
62
+ onCancel: () => halt()
63
+ }
64
+ );
65
+ if (!confirmed) {
66
+ halt("Operation cancelled.");
67
+ }
68
+ };
58
69
 
59
70
  // src/lib/resolve.ts
60
71
  import { existsSync, realpathSync } from "fs";
@@ -63,6 +74,7 @@ import { basename, dirname, join, relative, resolve } from "path";
63
74
  import chalk2 from "chalk";
64
75
  import stripIndent2 from "strip-indent";
65
76
  import { getIfUtils } from "webpack-config-utils";
77
+ import { z } from "zod";
66
78
  var require2 = createRequire(import.meta.url);
67
79
  var moduleFileExtensions = ["ts", "tsx", "js", "jsx"];
68
80
  var extensions = moduleFileExtensions.map((v) => `.${v}`);
@@ -128,6 +140,7 @@ var getPaths = (ifCore) => {
128
140
  appTsConfig: resolveApp("tsconfig.json"),
129
141
  appHtml: resolveApp("*.html"),
130
142
  appEnv: resolveApp(".env*"),
143
+ configYml: resolveApp("config.yml"),
131
144
  appTailwindConfig: void 0
132
145
  }
133
146
  );
@@ -174,6 +187,55 @@ var getIfCoreFns = ({
174
187
  );
175
188
  process.exit(1);
176
189
  };
190
+ var resolveAccountUuidFromConfig = (rawConfig, field) => {
191
+ const epmsConfig = z.object({
192
+ [field]: z.object()
193
+ }).safeParse(rawConfig);
194
+ if (!epmsConfig.success) {
195
+ return void 0;
196
+ }
197
+ const simple = z.object({
198
+ [field]: z.object({
199
+ account_uuid: z.uuid()
200
+ })
201
+ }).safeParse(rawConfig);
202
+ if (simple.success) {
203
+ return simple.data[field].account_uuid;
204
+ }
205
+ const accountMapping = z.object({
206
+ [field]: z.object({
207
+ account_mapping: z.uuid()
208
+ })
209
+ }).safeParse(rawConfig);
210
+ if (accountMapping.success) {
211
+ return accountMapping.data[field].account_mapping;
212
+ }
213
+ halt(
214
+ stripIndent2(
215
+ `Could not extract EPMS account UUID(s) from config.yml. Please ensure that the "${field}" field is correctly formatted. Expected formats:
216
+ 1. ${field}:
217
+ account_uuid: <UUID>
218
+
219
+ 2. ${field}:
220
+ account_mapping: <UUID>
221
+ `
222
+ )
223
+ );
224
+ };
225
+
226
+ // src/lib/constants.ts
227
+ import envPaths from "env-paths";
228
+ var DEFAULT_TAG = "latest";
229
+ var DEFAULT_PORT = 1234;
230
+ var AWS_S3_BUCKET_TAMARO = "tamaro.raisenow.com";
231
+ var AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE = "rnw-stage-email-service";
232
+ var AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD = "rnw-email-service";
233
+ var EPMS_API_BASE_URL_STAGE = "https://api.stage.mesos.raisenow.net";
234
+ var CORE_CONFIG_NAME = "tamaro-core";
235
+ var AWS_CLOUDFRONT_DISTRIBUTION_ID = "EHJ1OM458YQ0I";
236
+ var HTTPS_CRT_FILE = "localhost.crt";
237
+ var HTTPS_KEY_FILE = "localhost.key";
238
+ var CACHE_DIR = envPaths("tamaro-cli", { suffix: "" }).cache;
177
239
 
178
240
  // src/lib/env.ts
179
241
  import { createRequire as createRequire2 } from "module";
@@ -308,15 +370,7 @@ export {
308
370
  logDataTable,
309
371
  halt,
310
372
  runCommandSync,
311
- DEFAULT_TAG,
312
- DEFAULT_PORT,
313
- AWS_S3_BUCKET_TAMARO,
314
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
315
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD,
316
- CORE_CONFIG_NAME,
317
- AWS_CLOUDFRONT_DISTRIBUTION_ID,
318
- HTTPS_CRT_FILE,
319
- HTTPS_KEY_FILE,
373
+ promptConfirmation,
320
374
  extensions,
321
375
  resolveApp,
322
376
  resolveOwn,
@@ -326,6 +380,18 @@ export {
326
380
  getPaths,
327
381
  getRelativePaths,
328
382
  getIfCoreFns,
383
+ resolveAccountUuidFromConfig,
384
+ DEFAULT_TAG,
385
+ DEFAULT_PORT,
386
+ AWS_S3_BUCKET_TAMARO,
387
+ AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
388
+ AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD,
389
+ EPMS_API_BASE_URL_STAGE,
390
+ CORE_CONFIG_NAME,
391
+ AWS_CLOUDFRONT_DISTRIBUTION_ID,
392
+ HTTPS_CRT_FILE,
393
+ HTTPS_KEY_FILE,
394
+ CACHE_DIR,
329
395
  getEnvVars,
330
396
  assertEnvValid,
331
397
  applyEnv
package/dist/cli.js CHANGED
@@ -4,9 +4,11 @@ import {
4
4
  AWS_S3_BUCKET_TAMARO,
5
5
  AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD,
6
6
  AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
7
+ CACHE_DIR,
7
8
  CORE_CONFIG_NAME,
8
9
  DEFAULT_PORT,
9
10
  DEFAULT_TAG,
11
+ EPMS_API_BASE_URL_STAGE,
10
12
  HTTPS_CRT_FILE,
11
13
  HTTPS_KEY_FILE,
12
14
  applyEnv,
@@ -20,10 +22,12 @@ import {
20
22
  logError,
21
23
  logSuccess,
22
24
  logTitle,
25
+ promptConfirmation,
26
+ resolveAccountUuidFromConfig,
23
27
  resolveBin,
24
28
  resolveOwn,
25
29
  runCommandSync
26
- } from "./chunk-MCUAYEGK.js";
30
+ } from "./chunk-FI7KZAG6.js";
27
31
 
28
32
  // src/cli.ts
29
33
  import { createCommand } from "commander";
@@ -31,7 +35,6 @@ import { createCommand } from "commander";
31
35
  // src/commands/archive.ts
32
36
  import { existsSync, mkdirSync, renameSync } from "fs";
33
37
  import { basename, dirname, resolve } from "path";
34
- import chalk from "chalk";
35
38
 
36
39
  // src/lib/aws.ts
37
40
  import { execaCommandSync } from "execa";
@@ -113,35 +116,6 @@ var promptProfile = async () => {
113
116
  );
114
117
  return profile;
115
118
  };
116
- var promptConfirmation = async (message, skipConfirmation) => {
117
- if (skipConfirmation) {
118
- return;
119
- }
120
- const { confirmed } = await prompts(
121
- [
122
- {
123
- type: "confirm",
124
- name: "confirmed",
125
- message,
126
- initial: false
127
- }
128
- ],
129
- {
130
- onCancel: () => halt()
131
- }
132
- );
133
- if (!confirmed) {
134
- halt("Operation cancelled.");
135
- }
136
- };
137
- var assertTagValid = (tag) => {
138
- const regex = /^[a-zA-Z0-9-_.]+$/;
139
- if (!regex.test(tag)) {
140
- halt(
141
- `Flag "--tag" has forbidden format. Allowed format: ${regex.toString()}.`
142
- );
143
- }
144
- };
145
119
 
146
120
  // src/lib/notifier.ts
147
121
  import notifier from "node-notifier";
@@ -156,186 +130,10 @@ var notify = (args) => {
156
130
  });
157
131
  };
158
132
 
159
- // src/commands/undeploy.ts
160
- var undeploy = async (options) => {
161
- const { ifCore } = getIfCoreFns();
162
- const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
163
- if (!options.profile && !options.ci) {
164
- assertProfilesPresent();
165
- options.profile = await promptProfile();
166
- }
167
- assertOptionsValid(options);
168
- awsAuthenticate(options);
169
- const flags = prepareFlags(options);
170
- const { tag, all, dryrun } = options;
171
- const dryRunFlag = dryrun ? `--dryrun` : "";
172
- const deployUrl = all ? `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/` : `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/`;
173
- const description = all ? `all tags of "${configName}"` : `tag "${tag}" of "${configName}"`;
174
- if (dryRunFlag) {
175
- logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
176
- }
177
- await promptConfirmation(
178
- `Are you sure you want to undeploy ${description}?`,
179
- options.yes
180
- );
181
- const cmd = `
182
- aws s3 rm ${deployUrl}
183
- --recursive
184
- ${flags}
185
- ${dryRunFlag}
186
- `;
187
- logTitle(`Undeploying ${description} from AWS S3 \u2026`);
188
- logCommand(cmd);
189
- try {
190
- runCommandSync(cmd, { stdout: "inherit" });
191
- } catch (error) {
192
- halt(error.stderr);
193
- }
194
- if (!dryRunFlag) {
195
- const invalidationPath = all ? `/${configName}/*` : `/${configName}/${tag}/*`;
196
- const cmdInvalidateCache = `
197
- aws cloudfront create-invalidation
198
- --distribution-id ${AWS_CLOUDFRONT_DISTRIBUTION_ID}
199
- --paths ${invalidationPath}
200
- ${flags}
201
- `;
202
- logTitle("\nInvalidating edge cache \u2026");
203
- logCommand(cmdInvalidateCache);
204
- try {
205
- runCommandSync(cmdInvalidateCache);
206
- } catch (error) {
207
- halt(error.stderr);
208
- }
209
- }
210
- logTitle(`
211
- ${description} has been undeployed.`);
212
- logDataTable({ "Removed URL:": deployUrl });
213
- process.on("exit", () => {
214
- notify({
215
- title: "undeploy",
216
- message: `${description} has been undeployed.`
217
- });
218
- });
219
- };
220
- var assertOptionsValid = (options) => {
221
- const { profile, tag, all } = options;
222
- const { ifCore } = getIfCoreFns();
223
- if (ifCore() && all) {
224
- halt('Flag "--all" must not be used in Tamaro Core context.');
225
- }
226
- if (ifCore() && tag === DEFAULT_TAG) {
227
- halt("You cannot undeploy the default tag for Tamaro Core.");
228
- }
229
- if (profile) {
230
- assertProfileValid(profile);
231
- }
232
- if (all && tag) {
233
- halt('Flags "--tag" and "--all" must not be used together.');
234
- }
235
- if (!all && tag) {
236
- assertTagValid(tag);
237
- }
238
- };
239
-
240
- // src/commands/undeploy-email-config.ts
241
- import prompts2 from "prompts";
242
- var undeployEmailConfig = async (options) => {
243
- const { ifCore } = getIfCoreFns();
244
- assertIsNotCore(ifCore);
245
- if (!options.profile && !options.ci) {
246
- assertProfilesPresent();
247
- options.profile = await promptProfile();
248
- }
249
- if (!options.bucket) {
250
- options.bucket = options.ci ? AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD : await promptBucketTamaroEmailConfig();
251
- }
252
- assertOptionsValid2(options);
253
- awsAuthenticate(options);
254
- const flags = prepareFlags(options);
255
- const dryRunFlag = options.dryrun ? `--dryrun` : "";
256
- const configName = getWidgetUuid();
257
- const deployUrl = `s3://${options.bucket}/${configName}/`;
258
- await promptConfirmation(
259
- `Are you sure you want to undeploy the email configuration for "${configName}" from "${options.bucket}"?`,
260
- options.yes
261
- );
262
- if (dryRunFlag) {
263
- logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
264
- }
265
- const cmd = `
266
- aws s3 rm ${deployUrl}
267
- --recursive
268
- ${flags}
269
- ${dryRunFlag}
270
- `;
271
- logTitle(
272
- `Undeploying email configuration for "${configName}" from AWS S3 \u2026`
273
- );
274
- logCommand(cmd);
275
- try {
276
- runCommandSync(cmd, { stdout: "inherit" });
277
- } catch (error) {
278
- halt(error.stderr);
279
- }
280
- logTitle(
281
- `Email configuration for "${configName}" has been undeployed from "${options.bucket}".`
282
- );
283
- logDataTable({ "Removed URL:": deployUrl });
284
- process.on("exit", () => {
285
- notify({
286
- title: "undeploy-email-config",
287
- message: `Email configuration for "${configName}" has been undeployed.`
288
- });
289
- });
290
- };
291
- var assertOptionsValid2 = (options) => {
292
- const { profile, bucket } = options;
293
- if (profile) {
294
- assertProfileValid(profile);
295
- }
296
- if (bucket) {
297
- assertBucketValid(bucket);
298
- }
299
- };
300
- var assertIsNotCore = (ifCore) => {
301
- if (ifCore()) {
302
- halt("You cannot undeploy widget email configuration for Tamaro Core.");
303
- }
304
- };
305
- var assertBucketValid = (bucket) => {
306
- const buckets = [
307
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
308
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
309
- ];
310
- if (!buckets.includes(bucket)) {
311
- halt("Invalid bucket name.");
312
- }
313
- };
314
- var promptBucketTamaroEmailConfig = async () => {
315
- const buckets = [
316
- { title: "stage", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE },
317
- { title: "prod", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD }
318
- ];
319
- const { bucket } = await prompts2(
320
- [
321
- {
322
- type: "select",
323
- name: "bucket",
324
- message: "Select environment",
325
- choices: buckets
326
- }
327
- ],
328
- {
329
- onCancel: () => halt()
330
- }
331
- );
332
- return bucket;
333
- };
334
-
335
133
  // src/commands/archive.ts
336
- var archive = async (options) => {
134
+ var archive = (options) => {
337
135
  const { ifCore } = getIfCoreFns();
338
- assertIsNotCore2(ifCore);
136
+ assertIsNotCore(ifCore);
339
137
  assertIsNotArchived();
340
138
  const configName = getWidgetUuid();
341
139
  const cwd = process.cwd();
@@ -343,56 +141,20 @@ var archive = async (options) => {
343
141
  const archiveDir = resolve(parentDir, "_archived");
344
142
  const archiveTarget = resolve(archiveDir, configName);
345
143
  assertArchiveTargetDoesNotExist(archiveTarget);
346
- if (!options.profile && !options.ci) {
347
- assertProfilesPresent();
348
- options.profile = await promptProfile();
349
- }
350
- assertOptionsValid3(options);
144
+ assertOptionsValid(options);
351
145
  if (options.dryrun) {
352
146
  logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
353
147
  }
354
- await promptConfirmation(
355
- `Are you sure you want to archive "${configName}"? This will ${chalk.red(`undeploy all tags and email configurations of "${configName}"`)}.`,
356
- options.yes
357
- );
358
- logTitle(`
359
- Undeploying all tags for "${configName}" \u2026`);
360
- await undeploy({
361
- all: true,
362
- yes: true,
363
- profile: options.profile,
364
- ci: options.ci,
365
- dryrun: options.dryrun
366
- });
367
- logSuccess(`
368
- \u2705 Undeployed all tags for "${configName}" \u2026`);
369
- logTitle(`
370
- Undeploying email configuration for "${configName}" \u2026`);
371
- await undeployEmailConfig({
372
- yes: true,
373
- profile: options.profile,
374
- ci: options.ci,
375
- dryrun: options.dryrun,
376
- bucket: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
377
- });
378
- logSuccess(`
379
- \u2705 Undeployed email configuration for "${configName}" \u2026`);
380
- logTitle(`
381
- Archiving "${configName}" \u2026`);
382
148
  if (!options.dryrun) {
383
149
  if (!existsSync(archiveDir)) {
384
150
  mkdirSync(archiveDir, { recursive: true });
385
151
  }
386
152
  renameSync(cwd, archiveTarget);
387
- logSuccess(`\u2705 Moved to ${archiveTarget}`);
388
153
  } else {
389
154
  logTitle(`Would move ${cwd} \u2192 ${archiveTarget}`);
390
155
  }
391
156
  logSuccess(`
392
- "\u2705 ${configName}" has been archived.`);
393
- logDataTable({
394
- "Archived to:": archiveTarget
395
- });
157
+ \u2705 "${configName}" has been archived and moved to ${archiveTarget}`);
396
158
  process.on("exit", () => {
397
159
  notify({
398
160
  title: "archive",
@@ -400,13 +162,13 @@ Archiving "${configName}" \u2026`);
400
162
  });
401
163
  });
402
164
  };
403
- var assertOptionsValid3 = (options) => {
165
+ var assertOptionsValid = (options) => {
404
166
  const { profile } = options;
405
167
  if (profile) {
406
168
  assertProfileValid(profile);
407
169
  }
408
170
  };
409
- var assertIsNotCore2 = (ifCore) => {
171
+ var assertIsNotCore = (ifCore) => {
410
172
  if (ifCore()) {
411
173
  halt("You cannot archive Tamaro Core.");
412
174
  }
@@ -575,7 +337,7 @@ var build = async (options) => {
575
337
  assertProfilesPresent();
576
338
  options.profile = await promptProfile();
577
339
  }
578
- assertOptionsValid4(options);
340
+ assertOptionsValid2(options);
579
341
  await validate();
580
342
  const flags = prepareFlags2(options);
581
343
  const { ifCore } = getIfCoreFns();
@@ -607,7 +369,7 @@ Bundle for "${configName}" customer configuration is done.`);
607
369
  });
608
370
  });
609
371
  };
610
- var assertOptionsValid4 = (options) => {
372
+ var assertOptionsValid2 = (options) => {
611
373
  const { localCore, https, deploy: deploy2, env, profile } = options;
612
374
  const { ifCore } = getIfCoreFns();
613
375
  if (ifCore() && localCore) {
@@ -645,6 +407,18 @@ var prepareFlags2 = (options) => {
645
407
  // src/commands/deploy.ts
646
408
  import { existsSync as existsSync3 } from "fs";
647
409
  import stripIndent4 from "strip-indent";
410
+
411
+ // src/lib/validators/assertTagValid.ts
412
+ var assertTagValid = (tag) => {
413
+ const regex = /^[a-zA-Z0-9-_.]+$/;
414
+ if (!regex.test(tag)) {
415
+ halt(
416
+ `Flag "--tag" has forbidden format. Allowed format: ${regex.toString()}.`
417
+ );
418
+ }
419
+ };
420
+
421
+ // src/commands/deploy.ts
648
422
  var deploy = async (options) => {
649
423
  const { ifCore } = getIfCoreFns();
650
424
  const paths = getPaths(ifCore);
@@ -653,7 +427,7 @@ var deploy = async (options) => {
653
427
  assertProfilesPresent();
654
428
  options.profile = await promptProfile();
655
429
  }
656
- assertOptionsValid5(options);
430
+ assertOptionsValid3(options);
657
431
  await validate();
658
432
  awsAuthenticate(options);
659
433
  const flags = prepareFlags(options);
@@ -727,7 +501,7 @@ Bundle for "${configName}" is deployed with tag "${tag}".`);
727
501
  });
728
502
  });
729
503
  };
730
- var assertOptionsValid5 = (options) => {
504
+ var assertOptionsValid3 = (options) => {
731
505
  const { profile, tag } = options;
732
506
  if (profile) {
733
507
  assertProfileValid(profile);
@@ -747,21 +521,21 @@ var assertDistPathExists = (distPath) => {
747
521
 
748
522
  // src/commands/deploy-email-config.ts
749
523
  import { existsSync as existsSync4 } from "fs";
750
- import prompts3 from "prompts";
524
+ import prompts2 from "prompts";
751
525
  var deployEmailConfig = async (options) => {
752
526
  const { ifCore } = getIfCoreFns();
753
527
  const paths = getPaths(ifCore);
754
528
  const emailConfigPath = `${paths.app}/email-config`;
755
- assertIsNotCore3();
529
+ assertIsNotCore2();
756
530
  assertEmailConfigPathExists(emailConfigPath);
757
531
  if (!options.profile && !options.ci) {
758
532
  assertProfilesPresent();
759
533
  options.profile = await promptProfile();
760
534
  }
761
535
  if (!options.bucket) {
762
- options.bucket = options.ci ? AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD : await promptBucketTamaroEmailConfig2();
536
+ options.bucket = options.ci ? AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD : await promptBucketTamaroEmailConfig();
763
537
  }
764
- assertOptionsValid6(options);
538
+ assertOptionsValid4(options);
765
539
  await validate();
766
540
  awsAuthenticate(options);
767
541
  const flags = prepareFlags(options);
@@ -798,13 +572,13 @@ var deployEmailConfig = async (options) => {
798
572
  });
799
573
  });
800
574
  };
801
- var assertOptionsValid6 = (options) => {
575
+ var assertOptionsValid4 = (options) => {
802
576
  const { profile, bucket } = options;
803
577
  if (profile) {
804
578
  assertProfileValid(profile);
805
579
  }
806
580
  if (bucket) {
807
- assertBucketValid2(bucket);
581
+ assertBucketValid(bucket);
808
582
  }
809
583
  };
810
584
  var assertEmailConfigPathExists = (distPath) => {
@@ -812,13 +586,13 @@ var assertEmailConfigPathExists = (distPath) => {
812
586
  halt("Email config folder does not exists. Nothing to deploy.");
813
587
  }
814
588
  };
815
- var assertIsNotCore3 = () => {
589
+ var assertIsNotCore2 = () => {
816
590
  const { ifCore } = getIfCoreFns();
817
591
  if (ifCore()) {
818
592
  halt("You cannot deploy widget email configuration for Tamaro Core.");
819
593
  }
820
594
  };
821
- var assertBucketValid2 = (bucket) => {
595
+ var assertBucketValid = (bucket) => {
822
596
  const buckets = [
823
597
  AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
824
598
  AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
@@ -827,12 +601,12 @@ var assertBucketValid2 = (bucket) => {
827
601
  halt("Invalid bucket name.");
828
602
  }
829
603
  };
830
- var promptBucketTamaroEmailConfig2 = async () => {
604
+ var promptBucketTamaroEmailConfig = async () => {
831
605
  const buckets = [
832
606
  { title: "stage", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE },
833
607
  { title: "prod", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD }
834
608
  ];
835
- const { bucket } = await prompts3(
609
+ const { bucket } = await prompts2(
836
610
  [
837
611
  {
838
612
  type: "select",
@@ -853,7 +627,7 @@ import { getPortPromise } from "portfinder";
853
627
  var dev = async (options) => {
854
628
  const { env } = options;
855
629
  applyEnv(env);
856
- assertOptionsValid7(options);
630
+ assertOptionsValid5(options);
857
631
  const flags = await prepareFlags3(options);
858
632
  const { ifCore } = getIfCoreFns();
859
633
  const title = ifCore(
@@ -871,7 +645,7 @@ var dev = async (options) => {
871
645
  logCommand(cmd);
872
646
  runCommandSync(cmd, { stdio: "inherit" });
873
647
  };
874
- var assertOptionsValid7 = (options) => {
648
+ var assertOptionsValid5 = (options) => {
875
649
  const { localCore, https, port, env } = options;
876
650
  const { ifCore } = getIfCoreFns();
877
651
  if (ifCore() && localCore) {
@@ -908,7 +682,7 @@ var listDeployed = async (options) => {
908
682
  assertProfilesPresent();
909
683
  options.profile = await promptProfile();
910
684
  }
911
- assertOptionsValid8(options);
685
+ assertOptionsValid6(options);
912
686
  awsAuthenticate(options);
913
687
  const flags = prepareFlags(options);
914
688
  const { config } = options;
@@ -942,7 +716,7 @@ var listDeployed = async (options) => {
942
716
  console.log(`${text}
943
717
  `);
944
718
  };
945
- var assertOptionsValid8 = (options) => {
719
+ var assertOptionsValid6 = (options) => {
946
720
  const { config, profile } = options;
947
721
  assertConfigValid(config);
948
722
  if (profile) {
@@ -969,7 +743,7 @@ var parseTags = (lines) => {
969
743
  import { resolve as resolve3 } from "path";
970
744
  import { getPortPromise as getPortPromise2 } from "portfinder";
971
745
  var serve = async (options) => {
972
- assertOptionsValid9(options);
746
+ assertOptionsValid7(options);
973
747
  const flags = await prepareFlags4(options);
974
748
  const { ifCore } = getIfCoreFns();
975
749
  const paths = getPaths(ifCore);
@@ -982,7 +756,7 @@ var serve = async (options) => {
982
756
  logCommand(cmd);
983
757
  runCommandSync(cmd, { stdio: "inherit" });
984
758
  };
985
- var assertOptionsValid9 = (options) => {
759
+ var assertOptionsValid7 = (options) => {
986
760
  const { port, https } = options;
987
761
  if (isNaN(Number(port))) {
988
762
  halt('Flag "--port" should be a number.');
@@ -1010,9 +784,9 @@ var prepareFlags4 = async (options) => {
1010
784
  // src/commands/unarchive.ts
1011
785
  import { existsSync as existsSync5, renameSync as renameSync2 } from "fs";
1012
786
  import { basename as basename2, dirname as dirname2, resolve as resolve4 } from "path";
1013
- var unarchive = async (options) => {
787
+ var unarchive = (options) => {
1014
788
  const { ifCore } = getIfCoreFns({ allowArchived: true });
1015
- assertIsNotCore4(ifCore);
789
+ assertIsNotCore3(ifCore);
1016
790
  assertIsArchived();
1017
791
  const configName = getWidgetUuid();
1018
792
  const cwd = process.cwd();
@@ -1020,77 +794,25 @@ var unarchive = async (options) => {
1020
794
  const configsDir = dirname2(archivedDir);
1021
795
  const restoreTarget = resolve4(configsDir, configName);
1022
796
  assertRestoreTargetDoesNotExist(restoreTarget);
1023
- if (!options.profile && !options.ci) {
1024
- assertProfilesPresent();
1025
- options.profile = await promptProfile();
1026
- }
1027
- assertOptionsValid10(options);
1028
- await promptConfirmation(
1029
- `Are you sure you want to unarchive "${configName}"? This will restore, build, and deploy the configuration.`,
1030
- options.yes
1031
- );
797
+ assertOptionsValid8(options);
1032
798
  if (options.dryrun) {
1033
799
  logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
1034
800
  }
1035
- logTitle(`
1036
- Unarchiving "${configName}" \u2026`);
1037
801
  if (!options.dryrun) {
1038
802
  renameSync2(cwd, restoreTarget);
1039
803
  process.chdir(restoreTarget);
1040
- logTitle(`Moved to ${restoreTarget}`);
1041
804
  } else {
1042
805
  logTitle(`Would move ${cwd} \u2192 ${restoreTarget}`);
1043
806
  }
1044
- if (!options.dryrun) {
1045
- logTitle(`
1046
- Building "${configName}" \u2026`);
1047
- await build({
1048
- localCore: false,
1049
- analyze: false,
1050
- https: false,
1051
- serve: false,
1052
- deploy: false,
1053
- nolint: false,
1054
- tag: DEFAULT_TAG,
1055
- profile: options.profile,
1056
- ci: options.ci,
1057
- dryrun: options.dryrun
1058
- });
1059
- logSuccess(`
1060
- \u2705 Built "${configName}" \u2026`);
1061
- logTitle(`
1062
- Deploying "${configName}" to tag "${DEFAULT_TAG}" \u2026`);
1063
- await deploy({
1064
- tag: DEFAULT_TAG,
1065
- profile: options.profile,
1066
- ci: options.ci,
1067
- dryrun: options.dryrun
1068
- });
1069
- logSuccess(`
1070
- \u2705 Deployed "${configName}" to tag "${DEFAULT_TAG}" \u2026`);
1071
- logTitle(`
1072
- Deploying email configuration for "${configName}" \u2026`);
1073
- await deployEmailConfig({
1074
- profile: options.profile,
1075
- ci: options.ci,
1076
- dryrun: options.dryrun,
1077
- bucket: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
1078
- });
1079
- logSuccess(`
1080
- \u2705 Deployed email configuration for "${configName}" \u2026`);
1081
- } else {
1082
- logTitle('Would build, deploy to "latest", and deploy email configuration.');
1083
- }
1084
- logSuccess(`
1085
- \u2705 "${configName}" has been unarchived and deployed.`);
807
+ logTitle(`\u2705 "${configName}" has been unarchived and moved to ${restoreTarget}`);
1086
808
  process.on("exit", () => {
1087
809
  notify({
1088
810
  title: "unarchive",
1089
- message: `"${configName}" has been unarchived and deployed.`
811
+ message: `"${configName}" has been unarchived.`
1090
812
  });
1091
813
  });
1092
814
  };
1093
- var assertOptionsValid10 = (options) => {
815
+ var assertOptionsValid8 = (options) => {
1094
816
  const { profile } = options;
1095
817
  if (profile) {
1096
818
  assertProfileValid(profile);
@@ -1104,7 +826,7 @@ var assertIsArchived = () => {
1104
826
  );
1105
827
  }
1106
828
  };
1107
- var assertIsNotCore4 = (ifCore) => {
829
+ var assertIsNotCore3 = (ifCore) => {
1108
830
  if (ifCore()) {
1109
831
  halt("You cannot unarchive Tamaro Core.");
1110
832
  }
@@ -1117,10 +839,410 @@ var assertRestoreTargetDoesNotExist = (restoreTarget) => {
1117
839
  }
1118
840
  };
1119
841
 
842
+ // src/commands/undeploy.ts
843
+ var undeploy = async (options) => {
844
+ const { ifCore } = getIfCoreFns();
845
+ const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
846
+ if (!options.profile && !options.ci) {
847
+ assertProfilesPresent();
848
+ options.profile = await promptProfile();
849
+ }
850
+ assertOptionsValid9(options);
851
+ awsAuthenticate(options);
852
+ const flags = prepareFlags(options);
853
+ const { tag, all, dryrun } = options;
854
+ const dryRunFlag = dryrun ? `--dryrun` : "";
855
+ const deployUrl = all ? `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/` : `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/`;
856
+ const description = all ? `all tags of "${configName}"` : `tag "${tag}" of "${configName}"`;
857
+ if (dryRunFlag) {
858
+ logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
859
+ }
860
+ await promptConfirmation(
861
+ `Are you sure you want to undeploy ${description}?`,
862
+ options.ci
863
+ );
864
+ const cmd = `
865
+ aws s3 rm ${deployUrl}
866
+ --recursive
867
+ ${flags}
868
+ ${dryRunFlag}
869
+ `;
870
+ logTitle(`Undeploying ${description} from AWS S3 \u2026`);
871
+ logCommand(cmd);
872
+ try {
873
+ runCommandSync(cmd, { stdout: "inherit" });
874
+ } catch (error) {
875
+ halt(error.stderr);
876
+ }
877
+ if (!dryRunFlag) {
878
+ const invalidationPath = all ? `/${configName}/*` : `/${configName}/${tag}/*`;
879
+ const cmdInvalidateCache = `
880
+ aws cloudfront create-invalidation
881
+ --distribution-id ${AWS_CLOUDFRONT_DISTRIBUTION_ID}
882
+ --paths ${invalidationPath}
883
+ ${flags}
884
+ `;
885
+ logTitle("\nInvalidating edge cache \u2026");
886
+ logCommand(cmdInvalidateCache);
887
+ try {
888
+ runCommandSync(cmdInvalidateCache);
889
+ } catch (error) {
890
+ halt(error.stderr);
891
+ }
892
+ }
893
+ logTitle(`
894
+ ${description} has been undeployed.`);
895
+ logDataTable({ "Removed URL:": deployUrl });
896
+ process.on("exit", () => {
897
+ notify({
898
+ title: "undeploy",
899
+ message: `${description} has been undeployed.`
900
+ });
901
+ });
902
+ };
903
+ var assertOptionsValid9 = (options) => {
904
+ const { profile, tag, all } = options;
905
+ const { ifCore } = getIfCoreFns();
906
+ if (ifCore() && all) {
907
+ halt('Flag "--all" must not be used in Tamaro Core context.');
908
+ }
909
+ if (ifCore() && tag === DEFAULT_TAG) {
910
+ halt("You cannot undeploy the default tag for Tamaro Core.");
911
+ }
912
+ if (profile) {
913
+ assertProfileValid(profile);
914
+ }
915
+ if (all && tag) {
916
+ halt('Flags "--tag" and "--all" must not be used together.');
917
+ }
918
+ if (!all && tag) {
919
+ assertTagValid(tag);
920
+ }
921
+ };
922
+
923
+ // src/commands/undeploy-email-config.ts
924
+ import prompts3 from "prompts";
925
+ var undeployEmailConfig = async (options) => {
926
+ const { ifCore } = getIfCoreFns();
927
+ assertIsNotCore4(ifCore);
928
+ if (!options.profile && !options.ci) {
929
+ assertProfilesPresent();
930
+ options.profile = await promptProfile();
931
+ }
932
+ if (!options.bucket) {
933
+ options.bucket = options.ci ? AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD : await promptBucketTamaroEmailConfig2();
934
+ }
935
+ assertOptionsValid10(options);
936
+ awsAuthenticate(options);
937
+ const flags = prepareFlags(options);
938
+ const dryRunFlag = options.dryrun ? `--dryrun` : "";
939
+ const configName = getWidgetUuid();
940
+ const deployUrl = `s3://${options.bucket}/${configName}/`;
941
+ await promptConfirmation(
942
+ `Are you sure you want to undeploy the email configuration for "${configName}" from "${options.bucket}"?`,
943
+ options.ci
944
+ );
945
+ if (dryRunFlag) {
946
+ logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
947
+ }
948
+ const cmd = `
949
+ aws s3 rm ${deployUrl}
950
+ --recursive
951
+ ${flags}
952
+ ${dryRunFlag}
953
+ `;
954
+ logTitle(`Undeploying email configuration for "${configName}" from AWS S3 \u2026`);
955
+ logCommand(cmd);
956
+ try {
957
+ runCommandSync(cmd, { stdout: "inherit" });
958
+ } catch (error) {
959
+ halt(error.stderr);
960
+ }
961
+ logTitle(
962
+ `Email configuration for "${configName}" has been undeployed from "${options.bucket}".`
963
+ );
964
+ logDataTable({ "Removed URL:": deployUrl });
965
+ process.on("exit", () => {
966
+ notify({
967
+ title: "undeploy-email-config",
968
+ message: `Email configuration for "${configName}" has been undeployed.`
969
+ });
970
+ });
971
+ };
972
+ var assertOptionsValid10 = (options) => {
973
+ const { profile, bucket } = options;
974
+ if (profile) {
975
+ assertProfileValid(profile);
976
+ }
977
+ if (bucket) {
978
+ assertBucketValid2(bucket);
979
+ }
980
+ };
981
+ var assertIsNotCore4 = (ifCore) => {
982
+ if (ifCore()) {
983
+ halt("You cannot undeploy widget email configuration for Tamaro Core.");
984
+ }
985
+ };
986
+ var assertBucketValid2 = (bucket) => {
987
+ const buckets = [
988
+ AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
989
+ AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
990
+ ];
991
+ if (!buckets.includes(bucket)) {
992
+ halt("Invalid bucket name.");
993
+ }
994
+ };
995
+ var promptBucketTamaroEmailConfig2 = async () => {
996
+ const buckets = [
997
+ { title: "stage", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE },
998
+ { title: "prod", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD }
999
+ ];
1000
+ const { bucket } = await prompts3(
1001
+ [
1002
+ {
1003
+ type: "select",
1004
+ name: "bucket",
1005
+ message: "Select environment",
1006
+ choices: buckets
1007
+ }
1008
+ ],
1009
+ {
1010
+ onCancel: () => halt()
1011
+ }
1012
+ );
1013
+ return bucket;
1014
+ };
1015
+
1016
+ // src/commands/update-epms.ts
1017
+ import { readFileSync as readFileSync3 } from "fs";
1018
+ import yaml2 from "js-yaml";
1019
+
1020
+ // src/lib/epms/assert.ts
1021
+ import { existsSync as existsSync6, readFileSync } from "fs";
1022
+ import yaml from "js-yaml";
1023
+ import stripIndent5 from "strip-indent";
1024
+ var assertEpmsCredentials = (configPath) => {
1025
+ if (!existsSync6(configPath)) {
1026
+ throw new Error(`Config file not found at path: ${configPath}`);
1027
+ }
1028
+ try {
1029
+ const configContent = yaml.load(readFileSync(configPath, "utf8"));
1030
+ assertEpmsCredentialsPresentStage(configContent);
1031
+ assertEpmsCredentialsPresentProd(configContent);
1032
+ } catch (e) {
1033
+ throw new Error(
1034
+ `Error parsing config file at path: ${configPath}. Details: ${e}`
1035
+ );
1036
+ }
1037
+ };
1038
+ var assertEpmsCredentialsPresentStage = (rawConfig) => {
1039
+ const accountsUuidsStage = resolveAccountUuidFromConfig(
1040
+ rawConfig,
1041
+ "epms_stage"
1042
+ );
1043
+ if (!accountsUuidsStage) {
1044
+ return;
1045
+ }
1046
+ if (!process.env.EPMS_CLIENT_ID_STAGE || !process.env.EPMS_CLIENT_SECRET_STAGE) {
1047
+ halt(
1048
+ stripIndent5(
1049
+ `EPMS credentials for stage environment are missing.
1050
+ - Please set EPMS_CLIENT_ID_STAGE and EPMS_CLIENT_SECRET_STAGE in your environment variables.
1051
+ - Alternatively, remove the "epms_stage" fields from your config.yml
1052
+ `
1053
+ )
1054
+ );
1055
+ }
1056
+ };
1057
+ var assertEpmsCredentialsPresentProd = (rawConfig) => {
1058
+ const accountsUuidsProd = resolveAccountUuidFromConfig(rawConfig, "epms");
1059
+ if (!accountsUuidsProd) {
1060
+ return;
1061
+ }
1062
+ if (!process.env.EPMS_CLIENT_ID || !process.env.EPMS_CLIENT_SECRET) {
1063
+ halt(
1064
+ stripIndent5(
1065
+ `EPMS credentials for prod environment are missing.
1066
+ - Please set EPMS_CLIENT_ID and EPMS_CLIENT_SECRET in your environment variables.
1067
+ - Alternatively, remove the "epms" fields from your config.yml
1068
+ `
1069
+ )
1070
+ );
1071
+ }
1072
+ };
1073
+
1074
+ // src/lib/epms/auth.ts
1075
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
1076
+ import { dirname as dirname3, join } from "path";
1077
+ import ky from "ky";
1078
+ var DEFAULT_TIMEOUT = 1e4;
1079
+ function getCacheFilePath() {
1080
+ return join(CACHE_DIR, "token.json");
1081
+ }
1082
+ function readCachedToken() {
1083
+ try {
1084
+ const filePath = getCacheFilePath();
1085
+ if (!existsSync7(filePath)) {
1086
+ return void 0;
1087
+ }
1088
+ const data = JSON.parse(readFileSync2(filePath, "utf-8"));
1089
+ if (typeof data.token === "string" && typeof data.expirationTime === "number") {
1090
+ return data;
1091
+ }
1092
+ } catch {
1093
+ }
1094
+ return void 0;
1095
+ }
1096
+ function writeCachedToken(cached) {
1097
+ try {
1098
+ const filePath = getCacheFilePath();
1099
+ mkdirSync2(dirname3(filePath), { recursive: true });
1100
+ writeFileSync(filePath, JSON.stringify(cached), { mode: 384 });
1101
+ } catch {
1102
+ }
1103
+ }
1104
+ var createAuthorizer = (baseUrl, clientId, clientSecret) => {
1105
+ const apiUnauthorized = ky.extend({
1106
+ headers: {
1107
+ "Access-Control-Allow-Origin": "*"
1108
+ },
1109
+ prefixUrl: baseUrl,
1110
+ timeout: DEFAULT_TIMEOUT
1111
+ });
1112
+ let expirationTime = void 0;
1113
+ let token = void 0;
1114
+ const cached = readCachedToken();
1115
+ if (cached) {
1116
+ token = cached.token;
1117
+ expirationTime = cached.expirationTime;
1118
+ }
1119
+ const authorize = async () => {
1120
+ const isExpired = (expirationTime ?? 0) - Date.now() < 5 * 60 * 1e3;
1121
+ if (token && !isExpired) {
1122
+ return token;
1123
+ }
1124
+ const data = await apiUnauthorized.post("oauth2/token", {
1125
+ json: {
1126
+ client_id: clientId,
1127
+ client_secret: clientSecret,
1128
+ grant_type: "client_credentials"
1129
+ }
1130
+ }).json();
1131
+ token = `${data.token_type} ${data.access_token}`;
1132
+ expirationTime = Date.now() + data.expires_in * 1e3;
1133
+ writeCachedToken({ token, expirationTime });
1134
+ return token;
1135
+ };
1136
+ return apiUnauthorized.extend({
1137
+ hooks: {
1138
+ beforeRequest: [
1139
+ async (request) => {
1140
+ request.headers.set("Authorization", await authorize());
1141
+ }
1142
+ ]
1143
+ }
1144
+ });
1145
+ };
1146
+
1147
+ // src/lib/epms/client.ts
1148
+ var EpmsClient = class {
1149
+ #epmsClient;
1150
+ constructor(baseUrl, clientId, clientSecret) {
1151
+ this.#epmsClient = createAuthorizer(baseUrl, clientId, clientSecret);
1152
+ }
1153
+ async getOrganisationIdByAccountUuid(accountUuid) {
1154
+ const query = {
1155
+ query: {
1156
+ $and: [
1157
+ { $term: { object_uuid: accountUuid } },
1158
+ { $term: { object: "account" } }
1159
+ ]
1160
+ },
1161
+ size: 1,
1162
+ from: 0
1163
+ };
1164
+ const response = await this.#epmsClient.post("search/events", {
1165
+ json: query
1166
+ }).json();
1167
+ if (response.hits.length === 0) {
1168
+ throw new Error(`No events found for account UUID: ${accountUuid}`);
1169
+ }
1170
+ const organisationId = response.hits[0].organisation_uuid;
1171
+ if (!organisationId) {
1172
+ throw new Error(
1173
+ `Organisation UUID not found for account UUID: ${accountUuid}`
1174
+ );
1175
+ }
1176
+ return organisationId;
1177
+ }
1178
+ async updateTamaro(configName, tag, json) {
1179
+ return this.#epmsClient.put(`products/tamaro/${configName}/tags/${tag}`, {
1180
+ json
1181
+ }).json();
1182
+ }
1183
+ };
1184
+
1185
+ // src/commands/update-epms.ts
1186
+ var updateEpms = async (options) => {
1187
+ const { ifCore } = getIfCoreFns();
1188
+ assertIsNotCore5(ifCore);
1189
+ const paths = getPaths(ifCore);
1190
+ const { tag, dryrun } = options;
1191
+ const dryRunFlag = dryrun ? `--dryrun` : "";
1192
+ const configName = getWidgetUuid();
1193
+ if (!("configYml" in paths)) {
1194
+ halt("Fatal error, this should never happen");
1195
+ throw new Error();
1196
+ }
1197
+ if (dryRunFlag) {
1198
+ logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
1199
+ }
1200
+ assertTagValid(tag);
1201
+ assertEpmsCredentials(paths.configYml);
1202
+ const configContent = yaml2.load(readFileSync3(paths.configYml, "utf8"));
1203
+ const accountUuidStage = resolveAccountUuidFromConfig(
1204
+ configContent,
1205
+ "epms_stage"
1206
+ );
1207
+ if (dryrun) {
1208
+ logTitle(
1209
+ `[DRY RUN] Would update EPMS for "${configName}" with tag "${tag}"`
1210
+ );
1211
+ } else {
1212
+ if (accountUuidStage) {
1213
+ const epmsClient = new EpmsClient(
1214
+ EPMS_API_BASE_URL_STAGE,
1215
+ process.env.EPMS_CLIENT_ID_STAGE,
1216
+ process.env.EPMS_CLIENT_SECRET_STAGE
1217
+ );
1218
+ const organisationUuid = await epmsClient.getOrganisationIdByAccountUuid(accountUuidStage);
1219
+ logTitle("Updating EPMS with the following details:");
1220
+ const body = {
1221
+ account_uuid: accountUuidStage,
1222
+ organisation_uuid: organisationUuid,
1223
+ configured_version: tag,
1224
+ read_only: false
1225
+ };
1226
+ logDataTable({
1227
+ name: configName,
1228
+ tag,
1229
+ ...body
1230
+ });
1231
+ await epmsClient.updateTamaro(configName, tag, body);
1232
+ logSuccess("\u2705 EPMS Stage is updated.");
1233
+ }
1234
+ }
1235
+ };
1236
+ var assertIsNotCore5 = (ifCore) => {
1237
+ if (ifCore()) {
1238
+ halt("You cannot update EPMS in the context of Tamaro Core.");
1239
+ }
1240
+ };
1241
+
1120
1242
  // package.json
1121
1243
  var package_default = {
1122
1244
  name: "@raisenow/tamaro-cli",
1123
- version: "1.6.0-beta.0",
1245
+ version: "1.6.0-dev.2",
1124
1246
  author: {
1125
1247
  name: "RaiseNow",
1126
1248
  email: "development@raisenow.com"
@@ -1183,6 +1305,7 @@ var package_default = {
1183
1305
  "css-loader": "^7.1.3",
1184
1306
  "css-minimizer-webpack-plugin": "^7.0.4",
1185
1307
  dotenv: "^17.2.3",
1308
+ "env-paths": "^4.0.0",
1186
1309
  "escape-string-regexp": "^5.0.0",
1187
1310
  eslint: "^8.57.1",
1188
1311
  "eslint-config-prettier": "^10.1.8",
@@ -1202,7 +1325,9 @@ var package_default = {
1202
1325
  "handlebars-helpers": "^0.10.0",
1203
1326
  "html-loader": "^5.1.0",
1204
1327
  "html-webpack-plugin": "^5.6.6",
1328
+ "js-yaml": "^4.1.1",
1205
1329
  "json-loader": "^0.5.7",
1330
+ ky: "^1.14.3",
1206
1331
  lodash: "^4.17.23",
1207
1332
  "mini-css-extract-plugin": "^2.10.0",
1208
1333
  "node-notifier": "^10.0.1",
@@ -1230,7 +1355,11 @@ var package_default = {
1230
1355
  "webpack-cli": "^6.0.1",
1231
1356
  "webpack-config-utils": "^2.3.1",
1232
1357
  "webpack-dev-server": "^5.2.3",
1233
- "yaml-loader": "^0.9.0"
1358
+ "yaml-loader": "^0.9.0",
1359
+ zod: "^4.3.6"
1360
+ },
1361
+ devDependencies: {
1362
+ "@types/js-yaml": "^4.0.9"
1234
1363
  }
1235
1364
  };
1236
1365
 
@@ -1319,41 +1448,44 @@ cli.command("deploy-email-config").description("Deploy a widget's email configur
1319
1448
  });
1320
1449
  cli.command("undeploy").description(
1321
1450
  "Undeploy a Tamaro Core or customer configuration bundle from AWS S3"
1322
- ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option(
1323
- "--tag <tag>",
1324
- "Tag which should be undeployed",
1325
- DEFAULT_TAG
1326
- ).option("--all", "Undeploy all tags", false).option(
1451
+ ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--tag <tag>", "Tag which should be undeployed", DEFAULT_TAG).option("--all", "Undeploy all tags", false).option(
1327
1452
  "--dryrun",
1328
1453
  "Displays the operations that would be performed without actually running them",
1329
1454
  false
1330
- ).option("-y, --yes", "Skip confirmation prompt", false).action(async (options) => {
1455
+ ).action(async (options) => {
1331
1456
  await undeploy(options);
1332
1457
  });
1333
1458
  cli.command("undeploy-email-config").description("Undeploy a widget's email configuration from AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--bucket <bucket>", "AWS bucket for email configs").option(
1334
1459
  "--dryrun",
1335
1460
  "Displays the operations that would be performed without actually running them",
1336
1461
  false
1337
- ).option("-y, --yes", "Skip confirmation prompt", false).action(async (options) => {
1462
+ ).action(async (options) => {
1338
1463
  await undeployEmailConfig(options);
1339
1464
  });
1340
1465
  cli.command("archive").description(
1341
- "Archive a customer configuration: moves to _archived folder and undeploys all tags and email configurations"
1466
+ "Archive a customer configuration by moving it to the _archived folder"
1342
1467
  ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option(
1343
1468
  "--dryrun",
1344
1469
  "Displays the operations that would be performed without actually running them",
1345
1470
  false
1346
- ).option("-y, --yes", "Skip confirmation prompt", false).action(async (options) => {
1347
- await archive(options);
1471
+ ).action((options) => {
1472
+ archive(options);
1348
1473
  });
1349
1474
  cli.command("unarchive").description(
1350
- "Unarchive a customer configuration: restores from _archived folder, builds and deploys"
1475
+ "Unarchive a customer configuration by restoring it from the _archived folder"
1351
1476
  ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option(
1352
1477
  "--dryrun",
1353
1478
  "Displays the operations that would be performed without actually running them",
1354
1479
  false
1355
- ).option("-y, --yes", "Skip confirmation prompt", false).action(async (options) => {
1356
- await unarchive(options);
1480
+ ).action((options) => {
1481
+ unarchive(options);
1482
+ });
1483
+ cli.command("update-epms").description("Update EPMS with the currently configured widget version.").option("--tag <tag>", "Tag which should be used for the update", DEFAULT_TAG).option(
1484
+ "--dryrun",
1485
+ "Displays the operations that would be performed without actually running them",
1486
+ false
1487
+ ).action(async (options) => {
1488
+ await updateEpms(options);
1357
1489
  });
1358
1490
  cli.command("validate").description(
1359
1491
  "Validate the current Tamaro configuration to avoid common errors"
@@ -10,7 +10,7 @@ import {
10
10
  logTitle,
11
11
  resolveApp,
12
12
  resolveEslintPluginConfig
13
- } from "./chunk-MCUAYEGK.js";
13
+ } from "./chunk-FI7KZAG6.js";
14
14
 
15
15
  // src/webpack.config.ts
16
16
  import { existsSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raisenow/tamaro-cli",
3
- "version": "1.6.0-beta.0",
3
+ "version": "1.6.0-dev.2",
4
4
  "author": {
5
5
  "name": "RaiseNow",
6
6
  "email": "development@raisenow.com"
@@ -48,6 +48,7 @@
48
48
  "css-loader": "^7.1.3",
49
49
  "css-minimizer-webpack-plugin": "^7.0.4",
50
50
  "dotenv": "^17.2.3",
51
+ "env-paths": "^4.0.0",
51
52
  "escape-string-regexp": "^5.0.0",
52
53
  "eslint": "^8.57.1",
53
54
  "eslint-config-prettier": "^10.1.8",
@@ -67,7 +68,9 @@
67
68
  "handlebars-helpers": "^0.10.0",
68
69
  "html-loader": "^5.1.0",
69
70
  "html-webpack-plugin": "^5.6.6",
71
+ "js-yaml": "^4.1.1",
70
72
  "json-loader": "^0.5.7",
73
+ "ky": "^1.14.3",
71
74
  "lodash": "^4.17.23",
72
75
  "mini-css-extract-plugin": "^2.10.0",
73
76
  "node-notifier": "^10.0.1",
@@ -95,7 +98,11 @@
95
98
  "webpack-cli": "^6.0.1",
96
99
  "webpack-config-utils": "^2.3.1",
97
100
  "webpack-dev-server": "^5.2.3",
98
- "yaml-loader": "^0.9.0"
101
+ "yaml-loader": "^0.9.0",
102
+ "zod": "^4.3.6"
103
+ },
104
+ "devDependencies": {
105
+ "@types/js-yaml": "^4.0.9"
99
106
  },
100
107
  "scripts": {
101
108
  "typecheck": "tsc --noEmit",