@raisenow/tamaro-cli 1.6.1 → 1.7.0-dev.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 (122) hide show
  1. package/.history/.eslintignore_20220609190420 +4 -0
  2. package/.history/.eslintignore_20230911190227 +6 -0
  3. package/.history/.eslintignore_20230911211247 +7 -0
  4. package/.history/.eslintignore_20230911211250 +7 -0
  5. package/.history/.eslintrc_20230911201601.js +49 -0
  6. package/.history/.eslintrc_20230911201643.js +49 -0
  7. package/.history/.eslintrc_20230911201735.js +49 -0
  8. package/.history/.eslintrc_20230911201913.js +64 -0
  9. package/.history/.eslintrc_20230911202002.js +62 -0
  10. package/.history/.eslintrc_20230911202138.js +133 -0
  11. package/.history/.eslintrc_20230911202140.js +133 -0
  12. package/.history/.eslintrc_20230911202145.js +133 -0
  13. package/.history/.eslintrc_20230911202147.js +133 -0
  14. package/.history/.eslintrc_20230911202406.js +133 -0
  15. package/.history/.eslintrc_20230911202447.js +133 -0
  16. package/.history/.eslintrc_20230911202453.js +133 -0
  17. package/.history/.eslintrc_20230911202511.js +133 -0
  18. package/.history/.eslintrc_20230911202527.js +133 -0
  19. package/.history/.eslintrc_20230911202639.js +125 -0
  20. package/.history/.eslintrc_20230911202721.js +149 -0
  21. package/.history/.eslintrc_20230911202746.js +149 -0
  22. package/.history/.eslintrc_20240423125702.cjs +148 -0
  23. package/.history/.gitignore_20220609190420 +14 -0
  24. package/.history/.gitignore_20230911185959 +4 -0
  25. package/.history/.gitignore_20230911190006 +4 -0
  26. package/.history/.gitignore_20230911190007 +4 -0
  27. package/.history/.nmprc_20241004144025 +0 -0
  28. package/.history/.nmprc_20241004144041 +1 -0
  29. package/.history/.npmrc_20241004144040 +1 -0
  30. package/.history/.npmrc_20241004144152 +1 -0
  31. package/.history/.npmrc_20241004145639 +2 -0
  32. package/.history/.npmrc_20241004145649 +1 -0
  33. package/.history/.nvmrc_20250729181039 +1 -0
  34. package/.history/.prettierignore_20220609190420 +5 -0
  35. package/.history/.prettierignore_20230911190242 +7 -0
  36. package/.history/.prettierignore_20230911190245 +7 -0
  37. package/.history/.prettierignore_20230911211301 +8 -0
  38. package/.history/CHANGELOG_20250729195139.md +28 -0
  39. package/.history/CHANGELOG_20250729195148.md +29 -0
  40. package/.history/CHANGELOG_20250729195156.md +28 -0
  41. package/.history/CHANGELOG_20250729195218.md +29 -0
  42. package/.history/CHANGELOG_20250729195223.md +27 -0
  43. package/.history/CHANGELOG_20250729195506.md +29 -0
  44. package/.history/CHANGELOG_20250729224404.md +30 -0
  45. package/.history/CHANGELOG_20250729224411.md +30 -0
  46. package/.history/CHANGELOG_20250730124538.md +41 -0
  47. package/.history/CHANGELOG_20250730124603.md +38 -0
  48. package/.history/CHANGELOG_20250730124627.md +36 -0
  49. package/.history/CHANGELOG_20250730124638.md +36 -0
  50. package/.history/CHANGELOG_20250730124640.md +36 -0
  51. package/.history/README_20230911145033.md +245 -0
  52. package/.history/README_20230911151749.md +245 -0
  53. package/.history/README_20230911151844.md +245 -0
  54. package/.history/README_20230911153040.md +245 -0
  55. package/.history/README_20230911202959.md +245 -0
  56. package/.history/bitbucket-pipelines_20250729111540.yml +46 -0
  57. package/.history/bitbucket-pipelines_20250729210338.yml +46 -0
  58. package/.history/bitbucket-pipelines_20250729210340.yml +46 -0
  59. package/.history/bitbucket-pipelines_20250729210342.yml +46 -0
  60. package/.history/bitbucket-pipelines_20250729222234.yml +47 -0
  61. package/.history/bitbucket-pipelines_20250729222554.yml +47 -0
  62. package/.history/bitbucket-pipelines_20250729222555.yml +47 -0
  63. package/.history/bitbucket-pipelines_20250729223022.yml +46 -0
  64. package/.history/bitbucket-pipelines_20250729223024.yml +46 -0
  65. package/.history/cli_20241005014539 +3 -0
  66. package/.history/cli_20241005014542 +3 -0
  67. package/.history/cli_20241005014848 +3 -0
  68. package/.history/dist/chunk-FICM3HUI_20230912183408.js +302 -0
  69. package/{dist/chunk-5D6F3T2J.js → .history/dist/chunk-FICM3HUI_20230912201610.js} +45 -98
  70. package/.history/package_20250729182417.json +111 -0
  71. package/.history/package_20250729182419.json +111 -0
  72. package/.history/package_20250729182618.json +110 -0
  73. package/.history/package_20250729182834.json +110 -0
  74. package/.history/package_20250729183032.json +110 -0
  75. package/.history/package_20250729183033.json +110 -0
  76. package/.history/package_20250729185750.json +110 -0
  77. package/.history/package_20250729185815.json +110 -0
  78. package/.history/package_20250729185846.json +110 -0
  79. package/.history/package_20250729185854.json +110 -0
  80. package/.history/package_20250729185943.json +110 -0
  81. package/.history/package_20250729190645.json +110 -0
  82. package/.history/package_20250729195934.json +110 -0
  83. package/.history/package_20250729200013.json +110 -0
  84. package/.history/package_20250729200056.json +110 -0
  85. package/.history/package_20250729200434.json +110 -0
  86. package/.history/package_20250729200808.json +110 -0
  87. package/.history/package_20250729200944.json +110 -0
  88. package/.history/package_20250729214420.json +114 -0
  89. package/.history/package_20250729223112.json +110 -0
  90. package/.history/package_20250729223127.json +110 -0
  91. package/.history/package_20250729223134.json +110 -0
  92. package/.history/package_20250730124647.json +114 -0
  93. package/.history/tsconfig_20241005001442.json +34 -0
  94. package/.history/tsconfig_20241005001500.json +34 -0
  95. package/.history/tsconfig_20241005001540.json +34 -0
  96. package/.history/tsconfig_20241005014429.json +42 -0
  97. package/.history/tsconfig_20241005014436.json +42 -0
  98. package/.history/tsconfig_20241005014439.json +38 -0
  99. package/.history/tsconfig_20241005014455.json +38 -0
  100. package/.history/tsconfig_20241005014521.json +38 -0
  101. package/.history/vitest.config_20250729214539.ts +0 -0
  102. package/.history/vitest.config_20250729214659.ts +24 -0
  103. package/.history/vitest.config_20250729214748.ts +21 -0
  104. package/.history/vitest.config_20250729214825.ts +21 -0
  105. package/.history/vitest.config_20250729214902.ts +21 -0
  106. package/.history/vitest.config_20250729222125.ts +20 -0
  107. package/.history/vitest.config_20250729222213.ts +20 -0
  108. package/.idea/codeStyles/Project.xml +131 -0
  109. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  110. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  111. package/.idea/misc.xml +17 -0
  112. package/.idea/modules.xml +8 -0
  113. package/.idea/prettier.xml +6 -0
  114. package/.idea/tamaro-cli.iml +12 -0
  115. package/.idea/vcs.xml +6 -0
  116. package/.nvmrc +1 -1
  117. package/bitbucket-pipelines.yml +1 -1
  118. package/dist/cli.js +754 -1160
  119. package/dist/env-C_Jkayak.js +261 -0
  120. package/dist/webpack.config.js +310 -441
  121. package/package.json +18 -22
  122. package/tsdown.config.ts +11 -0
package/dist/cli.js CHANGED
@@ -1,442 +1,361 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- AWS_CLOUDFRONT_DISTRIBUTION_ID,
4
- AWS_S3_BUCKET_TAMARO,
5
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD,
6
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
7
- CORE_CONFIG_NAME,
8
- DEFAULT_PORT,
9
- DEFAULT_TAG,
10
- HTTPS_CRT_FILE,
11
- HTTPS_KEY_FILE,
12
- applyEnv,
13
- assertEnvValid,
14
- getIfCoreFns,
15
- getPaths,
16
- getWidgetUuid,
17
- halt,
18
- logCommand,
19
- logDataTable,
20
- logError,
21
- logSuccess,
22
- logTitle,
23
- promptConfirmation,
24
- resolveBin,
25
- resolveOwn,
26
- runCommandSync
27
- } from "./chunk-5D6F3T2J.js";
28
-
29
- // src/cli.ts
2
+ import { C as promptConfirmation, D as logError, E as logDataTable, O as logSuccess, S as halt, T as logCommand, _ as getWidgetUuid, a as AWS_S3_BUCKET_TAMARO, c as CORE_CONFIG_NAME, d as HTTPS_CRT_FILE, f as HTTPS_KEY_FILE, h as getPaths, i as AWS_CLOUDFRONT_DISTRIBUTION_ID, k as logTitle, l as DEFAULT_PORT, m as getIfCoreFns, n as assertEnvValid, o as AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD, s as AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE, t as applyEnv, u as DEFAULT_TAG, w as runCommandSync, x as resolveOwn, y as resolveBin } from "./env-C_Jkayak.js";
30
3
  import { createCommand } from "commander";
31
-
32
- // src/commands/archive.ts
33
4
  import { existsSync, mkdirSync, renameSync } from "fs";
34
5
  import { basename, dirname, resolve } from "path";
35
-
36
- // src/lib/aws.ts
37
6
  import { execaCommandSync } from "execa";
38
7
  import prompts from "prompts";
39
8
  import stripIndent from "strip-indent";
40
- var awsAuthenticate = (options) => {
41
- if (options.ci) {
42
- return;
43
- }
44
- try {
45
- checkIdentity(options);
46
- } catch (error) {
47
- login(options);
48
- }
49
- };
50
- var assertProfilesPresent = () => {
51
- const profiles = getAvailableProfiles();
52
- if (profiles.length === 0) {
53
- halt(
54
- stripIndent(`
9
+ import notifier from "node-notifier";
10
+ import { globSync } from "glob";
11
+ import fs from "node:fs";
12
+ import Handlebars from "handlebars";
13
+ import helpers from "handlebars-helpers";
14
+ import { getPortPromise } from "portfinder";
15
+
16
+ //#region src/lib/aws.ts
17
+ const awsAuthenticate = (options) => {
18
+ if (options.ci)
19
+ /**
20
+ * Don't try to authenticate the user.
21
+ * This is done when CLI is running within an automated system like pipeline,
22
+ * In this case "AWS_ACCESS_KEY_ID" and "AWS_SECRET_ACCESS_KEY" are used instead of "AWS_PROFILE".
23
+ */
24
+ return;
25
+ try {
26
+ checkIdentity(options);
27
+ } catch (error) {
28
+ login(options);
29
+ }
30
+ };
31
+ const assertProfilesPresent = () => {
32
+ if (getAvailableProfiles().length === 0) halt(stripIndent(`
55
33
  No AWS profiles found.
56
34
  Run "aws configure sso" to set up SSO-enabled profile.
57
35
  Check the wiki for more information:
58
36
  https://raisenow.atlassian.net/wiki/x/lIrWvg
59
- `)
60
- );
61
- }
62
- };
63
- var getAvailableProfiles = () => {
64
- const { stdout } = execaCommandSync("aws configure list-profiles");
65
- return stdout.split("\n");
66
- };
67
- var checkIdentity = (options) => {
68
- const flags = prepareFlags(options);
69
- execaCommandSync(`aws sts get-caller-identity ${flags}`);
70
- };
71
- var login = (options) => {
72
- const flags = prepareFlags(options);
73
- try {
74
- execaCommandSync(`aws sso login ${flags}`, { stdio: "inherit" });
75
- console.log("");
76
- } catch (error) {
77
- halt("Login failed.");
78
- }
79
- };
80
- var prepareFlags = (options) => {
81
- const flags = [];
82
- const { profile } = options;
83
- if (profile) {
84
- flags.push(`--profile ${profile}`);
85
- }
86
- return flags.join(" ");
87
- };
88
- var assertProfileValid = (profile) => {
89
- const profiles = getAvailableProfiles();
90
- if (!profiles.includes(profile)) {
91
- halt(
92
- stripIndent(`
37
+ `));
38
+ };
39
+ const getAvailableProfiles = () => {
40
+ const { stdout } = execaCommandSync("aws configure list-profiles");
41
+ return stdout.split("\n");
42
+ };
43
+ /**
44
+ * Returns caller identity data (output is ignored here).
45
+ * Fails in case of expired SSO session.
46
+ */
47
+ const checkIdentity = (options) => {
48
+ execaCommandSync(`aws sts get-caller-identity ${prepareFlags$3(options)}`);
49
+ };
50
+ /**
51
+ * Opens SSO authentication page in the browser.
52
+ * Fails if user canceled authentication process.
53
+ */
54
+ const login = (options) => {
55
+ const flags = prepareFlags$3(options);
56
+ try {
57
+ execaCommandSync(`aws sso login ${flags}`, { stdio: "inherit" });
58
+ console.log("");
59
+ } catch (error) {
60
+ halt("Login failed.");
61
+ }
62
+ };
63
+ const prepareFlags$3 = (options) => {
64
+ const flags = [];
65
+ const { profile } = options;
66
+ if (profile) flags.push(`--profile ${profile}`);
67
+ return flags.join(" ");
68
+ };
69
+ const assertProfileValid = (profile) => {
70
+ const profiles = getAvailableProfiles();
71
+ if (!profiles.includes(profile)) halt(stripIndent(`
93
72
  AWS profile "${profile}" is not found.
94
73
  Available profiles are: ${profiles.map((v) => `"${v}"`).join(", ")}.
95
- `)
96
- );
97
- }
98
- };
99
- var promptProfile = async () => {
100
- const profiles = getAvailableProfiles().filter((v) => !!v).map((v) => ({ title: v, value: v }));
101
- const { profile } = await prompts(
102
- [
103
- {
104
- type: "select",
105
- name: "profile",
106
- message: "Select AWS profile",
107
- choices: profiles
108
- }
109
- ],
110
- {
111
- onCancel: () => halt()
112
- }
113
- );
114
- return profile;
74
+ `));
75
+ };
76
+ const promptProfile = async () => {
77
+ const { profile } = await prompts([{
78
+ type: "select",
79
+ name: "profile",
80
+ message: "Select AWS profile",
81
+ choices: getAvailableProfiles().filter((v) => !!v).map((v) => ({
82
+ title: v,
83
+ value: v
84
+ }))
85
+ }], { onCancel: () => halt() });
86
+ return profile;
115
87
  };
116
88
 
117
- // src/lib/notifier.ts
118
- import notifier from "node-notifier";
119
- import stripIndent2 from "strip-indent";
120
- var notify = (args) => {
121
- const { title, message = "" } = args;
122
- notifier.notify({
123
- title,
124
- message: stripIndent2(message).trim(),
125
- contentImage: "https://assets.raisenow.io/favicon.png",
126
- sound: "Funk"
127
- });
89
+ //#endregion
90
+ //#region src/lib/notifier.ts
91
+ const notify = (args) => {
92
+ const { title, message = "" } = args;
93
+ notifier.notify({
94
+ title,
95
+ message: stripIndent(message).trim(),
96
+ contentImage: "https://assets.raisenow.io/favicon.png",
97
+ sound: "Funk"
98
+ });
128
99
  };
129
100
 
130
- // src/commands/archive.ts
131
- var archive = (options) => {
132
- const { ifCore } = getIfCoreFns();
133
- assertIsNotCore(ifCore);
134
- assertIsNotArchived();
135
- const configName = getWidgetUuid();
136
- const cwd = process.cwd();
137
- const parentDir = dirname(cwd);
138
- const archiveDir = resolve(parentDir, "_archived");
139
- const archiveTarget = resolve(archiveDir, configName);
140
- assertArchiveTargetDoesNotExist(archiveTarget);
141
- assertOptionsValid(options);
142
- if (options.dryrun) {
143
- logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
144
- }
145
- if (!options.dryrun) {
146
- if (!existsSync(archiveDir)) {
147
- mkdirSync(archiveDir, { recursive: true });
148
- }
149
- renameSync(cwd, archiveTarget);
150
- } else {
151
- logTitle(`Would move ${cwd} \u2192 ${archiveTarget}`);
152
- }
153
- logSuccess(`
154
- \u2705 "${configName}" has been archived and moved to ${archiveTarget}`);
155
- process.on("exit", () => {
156
- notify({
157
- title: "archive",
158
- message: `"${configName}" has been archived.`
159
- });
160
- });
161
- };
162
- var assertOptionsValid = (options) => {
163
- const { profile } = options;
164
- if (profile) {
165
- assertProfileValid(profile);
166
- }
167
- };
168
- var assertIsNotCore = (ifCore) => {
169
- if (ifCore()) {
170
- halt("You cannot archive Tamaro Core.");
171
- }
172
- };
173
- var assertIsNotArchived = () => {
174
- const parentDirName = basename(dirname(process.cwd()));
175
- if (parentDirName === "_archived") {
176
- halt("This configuration is already archived.");
177
- }
178
- };
179
- var assertArchiveTargetDoesNotExist = (archiveTarget) => {
180
- if (existsSync(archiveTarget)) {
181
- halt(
182
- `Archive target already exists: ${archiveTarget}. Please remove it first.`
183
- );
184
- }
101
+ //#endregion
102
+ //#region src/commands/archive.ts
103
+ const archive = (options) => {
104
+ const { ifCore } = getIfCoreFns();
105
+ assertIsNotCore$3(ifCore);
106
+ assertIsNotArchived();
107
+ const configName = getWidgetUuid();
108
+ const cwd = process.cwd();
109
+ const archiveDir = resolve(dirname(cwd), "_archived");
110
+ const archiveTarget = resolve(archiveDir, configName);
111
+ assertArchiveTargetDoesNotExist(archiveTarget);
112
+ assertOptionsValid$9(options);
113
+ if (options.dryrun) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
114
+ if (!options.dryrun) {
115
+ if (!existsSync(archiveDir)) mkdirSync(archiveDir, { recursive: true });
116
+ renameSync(cwd, archiveTarget);
117
+ } else logTitle(`Would move ${cwd} → ${archiveTarget}`);
118
+ logSuccess(`\n✅ "${configName}" has been archived and moved to ${archiveTarget}`);
119
+ process.on("exit", () => {
120
+ notify({
121
+ title: "archive",
122
+ message: `"${configName}" has been archived.`
123
+ });
124
+ });
125
+ };
126
+ const assertOptionsValid$9 = (options) => {
127
+ const { profile } = options;
128
+ if (profile) assertProfileValid(profile);
129
+ };
130
+ const assertIsNotCore$3 = (ifCore) => {
131
+ if (ifCore()) halt("You cannot archive Tamaro Core.");
132
+ };
133
+ const assertIsNotArchived = () => {
134
+ if (basename(dirname(process.cwd())) === "_archived") halt("This configuration is already archived.");
135
+ };
136
+ const assertArchiveTargetDoesNotExist = (archiveTarget) => {
137
+ if (existsSync(archiveTarget)) halt(`Archive target already exists: ${archiveTarget}. Please remove it first.`);
185
138
  };
186
139
 
187
- // src/commands/validate.ts
188
- import { globSync } from "glob";
189
-
190
- // src/lib/validators/validateEmailTemplates.ts
191
- import fs from "fs";
192
- import Handlebars from "handlebars";
193
- import helpers from "handlebars-helpers";
194
- helpers.comparison({
195
- handlebars: Handlebars
196
- });
197
- var validateEmailTemplates = (templatePaths) => {
198
- const errors = [];
199
- for (const templatePath of templatePaths) {
200
- try {
201
- const template = fs.readFileSync(templatePath, "utf-8");
202
- assureOnlySupportedHelpers(template);
203
- compileEmailTemplate(template);
204
- console.log(`${templatePath} is a valid email template`);
205
- } catch (e) {
206
- errors.push(`${templatePath} is invalid`);
207
- errors.push(e);
208
- }
209
- }
210
- return errors;
211
- };
212
- var assureOnlySupportedHelpers = (template) => {
213
- const extractHelpers = (ast2) => {
214
- const usedHelpers2 = /* @__PURE__ */ new Set();
215
- const extractHelperName = (node) => {
216
- if (node.path?.type === "PathExpression" && (node.params?.length > 0 || node.hash?.pairs?.length > 0)) {
217
- return node.path?.original;
218
- }
219
- };
220
- const visit = (node) => {
221
- if (!node) {
222
- return;
223
- }
224
- switch (node.type) {
225
- case "MustacheStatement":
226
- case "BlockStatement": {
227
- const name = extractHelperName(node);
228
- if (name) {
229
- usedHelpers2.add(name);
230
- }
231
- break;
232
- }
233
- }
234
- for (const key in node) {
235
- const value = node[key];
236
- if (Array.isArray(value)) {
237
- value.forEach(visit);
238
- } else if (value && typeof value === "object" && value.type) {
239
- visit(value);
240
- }
241
- }
242
- };
243
- visit(ast2);
244
- return usedHelpers2;
245
- };
246
- const ast = Handlebars.parse(template);
247
- const usedHelpers = extractHelpers(ast);
248
- const supportedHelpers = /* @__PURE__ */ new Set([
249
- // Built-in Handlebars helpers
250
- "if",
251
- "unless",
252
- "each",
253
- "with",
254
- "log",
255
- "lookup",
256
- // Comparison helpers, provided by "handlebars-helpers" lib
257
- "and",
258
- "compare",
259
- "contains",
260
- "default",
261
- "eq",
262
- "gt",
263
- "gte",
264
- "has",
265
- "isFalsey",
266
- "isTruthy",
267
- "ifEven",
268
- "ifNth",
269
- "ifOdd",
270
- "is",
271
- "isnt",
272
- "lt",
273
- "lte",
274
- "neither",
275
- "not",
276
- "or",
277
- "unlessEq",
278
- "unlessGt",
279
- "unlessLt",
280
- "unlessGteq",
281
- "unlessLteq"
282
- ]);
283
- const unsupportedHelpers = Array.from(usedHelpers).filter(
284
- (h) => !supportedHelpers.has(h)
285
- );
286
- if (unsupportedHelpers.length > 0) {
287
- throw new Error(
288
- `Email template uses unsupported Handlebars helpers: ${unsupportedHelpers.join(", ")}`
289
- );
290
- }
291
- };
292
- var compileEmailTemplate = (template) => {
293
- const compiled = Handlebars.compile(template);
294
- compiled({});
140
+ //#endregion
141
+ //#region src/lib/validators/validateEmailTemplates.ts
142
+ /**
143
+ * Register custom Handlebars helpers - the same, which are used in the Email Service.
144
+ * Make sure to update it if new helpers are added in the Email Service.
145
+ */
146
+ helpers.comparison({ handlebars: Handlebars });
147
+ /**
148
+ * Recurse through the expected email templates folder and attempt to parse
149
+ * handlebars template to validate their syntax.
150
+ */
151
+ const validateEmailTemplates = (templatePaths) => {
152
+ const errors = [];
153
+ for (const templatePath of templatePaths) try {
154
+ const template = fs.readFileSync(templatePath, "utf-8");
155
+ assureOnlySupportedHelpers(template);
156
+ compileEmailTemplate(template);
157
+ console.log(`${templatePath} is a valid email template`);
158
+ } catch (e) {
159
+ errors.push(`${templatePath} is invalid`);
160
+ errors.push(e);
161
+ }
162
+ return errors;
163
+ };
164
+ /**
165
+ * Validate that only supported Handlebars helpers are used in the template.
166
+ * This function extracts helpers from the template AST and checks them
167
+ * against a predefined set of supported helpers.
168
+ */
169
+ const assureOnlySupportedHelpers = (template) => {
170
+ /**
171
+ * Recursively extract helpers from the AST
172
+ */
173
+ const extractHelpers = (ast) => {
174
+ const usedHelpers = /* @__PURE__ */ new Set();
175
+ const extractHelperName = (node) => {
176
+ if (node.path?.type === "PathExpression" && (node.params?.length > 0 || node.hash?.pairs?.length > 0)) return node.path?.original;
177
+ };
178
+ const visit = (node) => {
179
+ if (!node) return;
180
+ switch (node.type) {
181
+ case "MustacheStatement":
182
+ case "BlockStatement": {
183
+ const name = extractHelperName(node);
184
+ if (name) usedHelpers.add(name);
185
+ break;
186
+ }
187
+ }
188
+ /**
189
+ * Recursively visit children of the AST node.
190
+ */
191
+ for (const key in node) {
192
+ const value = node[key];
193
+ if (Array.isArray(value)) value.forEach(visit);
194
+ else if (value && typeof value === "object" && value.type) visit(value);
195
+ }
196
+ };
197
+ visit(ast);
198
+ return usedHelpers;
199
+ };
200
+ const usedHelpers = extractHelpers(Handlebars.parse(template));
201
+ const supportedHelpers = new Set([
202
+ "if",
203
+ "unless",
204
+ "each",
205
+ "with",
206
+ "log",
207
+ "lookup",
208
+ "and",
209
+ "compare",
210
+ "contains",
211
+ "default",
212
+ "eq",
213
+ "gt",
214
+ "gte",
215
+ "has",
216
+ "isFalsey",
217
+ "isTruthy",
218
+ "ifEven",
219
+ "ifNth",
220
+ "ifOdd",
221
+ "is",
222
+ "isnt",
223
+ "lt",
224
+ "lte",
225
+ "neither",
226
+ "not",
227
+ "or",
228
+ "unlessEq",
229
+ "unlessGt",
230
+ "unlessLt",
231
+ "unlessGteq",
232
+ "unlessLteq"
233
+ ]);
234
+ const unsupportedHelpers = Array.from(usedHelpers).filter((h) => !supportedHelpers.has(h));
235
+ if (unsupportedHelpers.length > 0) throw new Error(`Email template uses unsupported Handlebars helpers: ${unsupportedHelpers.join(", ")}`);
236
+ };
237
+ /**
238
+ * Compile Email template and pass an empty context to validate syntax.
239
+ */
240
+ const compileEmailTemplate = (template) => {
241
+ Handlebars.compile(template)({});
295
242
  };
296
243
 
297
- // src/commands/validate.ts
298
- var validate = async () => {
299
- const errors = validateEmailTemplates(
300
- globSync("email-config/templates/**/*.hbs").sort()
301
- );
302
- if (errors.length) {
303
- halt(errors.map((el) => el.toString()).join("\n"));
304
- }
305
- logTitle("Validated successfully");
244
+ //#endregion
245
+ //#region src/commands/validate.ts
246
+ /**
247
+ * Validates the current Tamaro configuration to avoid common errors.
248
+ *
249
+ * The command description is kept generic on purpose. We check for a number
250
+ * of mistakes observed when tamaro configurations are manually edited and
251
+ * deployed.
252
+ */
253
+ const validate = async () => {
254
+ const errors = validateEmailTemplates(globSync("email-config/templates/**/*.hbs").sort());
255
+ if (errors.length) halt(errors.map((el) => el.toString()).join("\n"));
256
+ logTitle("Validated successfully");
306
257
  };
307
258
 
308
- // src/lib/https.ts
309
- import { existsSync as existsSync2 } from "fs";
310
- import { resolve as resolve2 } from "path";
311
- import stripIndent3 from "strip-indent";
312
- var assertCrtExists = () => {
313
- const { ifCore } = getIfCoreFns();
314
- const paths = getPaths(ifCore);
315
- const certFile = resolve2(paths.root, HTTPS_CRT_FILE);
316
- const keyFile = resolve2(paths.root, HTTPS_KEY_FILE);
317
- if (existsSync2(certFile) && existsSync2(keyFile)) {
318
- return;
319
- }
320
- halt(
321
- stripIndent3(`
259
+ //#endregion
260
+ //#region src/lib/https.ts
261
+ const assertCrtExists = () => {
262
+ const { ifCore } = getIfCoreFns();
263
+ const paths = getPaths(ifCore);
264
+ const certFile = resolve(paths.root, HTTPS_CRT_FILE);
265
+ const keyFile = resolve(paths.root, HTTPS_KEY_FILE);
266
+ if (existsSync(certFile) && existsSync(keyFile)) return;
267
+ halt(stripIndent(`
322
268
  Flag "--https" is used, but "${HTTPS_CRT_FILE}" and/or "${HTTPS_KEY_FILE}" files are not found.
323
269
  You need to generate certificate first.
324
270
  Check docs: https://unpkg.com/@raisenow/tamaro-cli/readme.html#/?id=use-transport-level-security-https
325
- `)
326
- );
271
+ `));
327
272
  };
328
273
 
329
- // src/commands/build.ts
330
- var build = async (options) => {
331
- const { env } = options;
332
- applyEnv(env);
333
- if (options.deploy && !options.profile && !options.ci) {
334
- assertProfilesPresent();
335
- options.profile = await promptProfile();
336
- }
337
- assertOptionsValid2(options);
338
- await validate();
339
- const flags = prepareFlags2(options);
340
- const { ifCore } = getIfCoreFns();
341
- const paths = getPaths(ifCore);
342
- const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
343
- const title = ifCore(
344
- "Building optimised (minified) bundle of Tamaro Core \u2026",
345
- `Building optimised (minified) bundle of "${configName}" customer configuration \u2026`
346
- );
347
- const wpBin = resolveBin("webpack");
348
- const wpConfig = resolveOwn("dist/webpack.config.js");
349
- const cmd = `
350
- ${wpBin}
351
- --config ${wpConfig}
274
+ //#endregion
275
+ //#region src/commands/build.ts
276
+ const build = async (options) => {
277
+ const { env } = options;
278
+ applyEnv(env);
279
+ if (options.deploy && !options.profile && !options.ci) {
280
+ assertProfilesPresent();
281
+ options.profile = await promptProfile();
282
+ }
283
+ assertOptionsValid$8(options);
284
+ await validate();
285
+ const flags = prepareFlags$2(options);
286
+ const { ifCore } = getIfCoreFns();
287
+ const paths = getPaths(ifCore);
288
+ const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
289
+ const title = ifCore("Building optimised (minified) bundle of Tamaro Core ", `Building optimised (minified) bundle of "${configName}" customer configuration …`);
290
+ const cmd = `
291
+ ${resolveBin("webpack")}
292
+ --config ${resolveOwn("dist/webpack.config.js")}
352
293
  --env min
353
294
  ${flags}
354
295
  `;
355
- logTitle(title);
356
- logCommand(cmd);
357
- runCommandSync(cmd, { stdio: "inherit" });
358
- logTitle(`
359
- Bundle for "${configName}" customer configuration is done.`);
360
- console.log(`Path: ${paths.appDist}
361
- `);
362
- process.on("exit", () => {
363
- notify({
364
- title: "build",
365
- message: `Bundle for "${configName}" customer configuration is done.`
366
- });
367
- });
368
- };
369
- var assertOptionsValid2 = (options) => {
370
- const { localCore, https, deploy: deploy2, env, profile } = options;
371
- const { ifCore } = getIfCoreFns();
372
- if (ifCore() && localCore) {
373
- halt('Flag "--local-core" is redundant if running in Tamaro Core context.');
374
- }
375
- if (localCore && deploy2) {
376
- halt('Flags "--local-core" and "--deploy" must not be used together.');
377
- }
378
- if (https) {
379
- assertCrtExists();
380
- }
381
- if (profile) {
382
- assertProfileValid(profile);
383
- }
384
- assertEnvValid(env);
385
- };
386
- var prepareFlags2 = (options) => {
387
- const flags = [];
388
- const { localCore, analyze, https, nolint } = options;
389
- if (localCore) {
390
- flags.push("--env localCore");
391
- }
392
- if (analyze) {
393
- flags.push("--env analyze");
394
- }
395
- if (https) {
396
- flags.push("--env https");
397
- }
398
- if (nolint) {
399
- flags.push("--env nolint");
400
- }
401
- return flags.join(" ");
296
+ logTitle(title);
297
+ logCommand(cmd);
298
+ runCommandSync(cmd, { stdio: "inherit" });
299
+ logTitle(`\nBundle for "${configName}" customer configuration is done.`);
300
+ console.log(`Path: ${paths.appDist}\n`);
301
+ process.on("exit", () => {
302
+ notify({
303
+ title: "build",
304
+ message: `Bundle for "${configName}" customer configuration is done.`
305
+ });
306
+ });
307
+ };
308
+ const assertOptionsValid$8 = (options) => {
309
+ const { localCore, https, deploy, env, profile } = options;
310
+ const { ifCore } = getIfCoreFns();
311
+ if (ifCore() && localCore) halt("Flag \"--local-core\" is redundant if running in Tamaro Core context.");
312
+ if (localCore && deploy) halt("Flags \"--local-core\" and \"--deploy\" must not be used together.");
313
+ if (https) assertCrtExists();
314
+ if (profile) assertProfileValid(profile);
315
+ assertEnvValid(env);
316
+ };
317
+ const prepareFlags$2 = (options) => {
318
+ const flags = [];
319
+ const { localCore, analyze, https, nolint, debug } = options;
320
+ if (localCore) flags.push("--env localCore");
321
+ if (analyze) flags.push("--env analyze");
322
+ if (https) flags.push("--env https");
323
+ if (nolint) flags.push("--env nolint");
324
+ if (debug) flags.push("--env debug");
325
+ return flags.join(" ");
402
326
  };
403
327
 
404
- // src/commands/deploy.ts
405
- import { existsSync as existsSync3 } from "fs";
406
- import stripIndent4 from "strip-indent";
407
-
408
- // src/lib/validators/assertTagValid.ts
409
- var assertTagValid = (tag) => {
410
- const regex = /^[a-zA-Z0-9-_.]+$/;
411
- if (!regex.test(tag)) {
412
- halt(
413
- `Flag "--tag" has forbidden format. Allowed format: ${regex.toString()}.`
414
- );
415
- }
328
+ //#endregion
329
+ //#region src/lib/validators/assertTagValid.ts
330
+ const assertTagValid = (tag) => {
331
+ const regex = /^[a-zA-Z0-9-_.]+$/;
332
+ if (!regex.test(tag)) halt(`Flag "--tag" has forbidden format. Allowed format: ${regex.toString()}.`);
416
333
  };
417
334
 
418
- // src/commands/deploy.ts
419
- var deploy = async (options) => {
420
- const { ifCore } = getIfCoreFns();
421
- const paths = getPaths(ifCore);
422
- assertDistPathExists(paths.appDist);
423
- if (!options.profile && !options.ci) {
424
- assertProfilesPresent();
425
- options.profile = await promptProfile();
426
- }
427
- assertOptionsValid3(options);
428
- await validate();
429
- awsAuthenticate(options);
430
- const flags = prepareFlags(options);
431
- const { tag, dryrun } = options;
432
- const dryRunFlag = dryrun ? `--dryrun` : "";
433
- const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
434
- const deployUrl = `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}`;
435
- const entryFilename = ifCore("index.js", "widget.js");
436
- if (dryRunFlag) {
437
- logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
438
- }
439
- const cmdSyncAll = `
335
+ //#endregion
336
+ //#region src/commands/deploy.ts
337
+ const deploy = async (options) => {
338
+ const { ifCore } = getIfCoreFns();
339
+ const paths = getPaths(ifCore);
340
+ assertDistPathExists(paths.appDist);
341
+ if (!options.profile && !options.ci) {
342
+ assertProfilesPresent();
343
+ options.profile = await promptProfile();
344
+ }
345
+ assertOptionsValid$7(options);
346
+ await validate();
347
+ awsAuthenticate(options);
348
+ const flags = prepareFlags$3(options);
349
+ const { tag, dryrun } = options;
350
+ const dryRunFlag = dryrun ? `--dryrun` : "";
351
+ const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
352
+ const deployUrl = `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}`;
353
+ const entryFilename = ifCore("index.js", "widget.js");
354
+ if (dryRunFlag) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
355
+ /**
356
+ * Sync all files except entrypoint (without deleting old files)
357
+ */
358
+ const cmdSyncAll = `
440
359
  aws s3 sync ${paths.appDist} ${deployUrl}
441
360
  --acl public-read
442
361
  --exclude ${paths.appDist}/${entryFilename}
@@ -444,837 +363,512 @@ var deploy = async (options) => {
444
363
  ${flags}
445
364
  ${dryRunFlag}
446
365
  `;
447
- logTitle("Deploying to AWS S3 \u2026");
448
- logCommand(cmdSyncAll);
449
- try {
450
- runCommandSync(cmdSyncAll, { stdout: "inherit" });
451
- } catch (error) {
452
- halt(error.stderr);
453
- }
454
- const cmdCpEntry = `
366
+ logTitle("Deploying to AWS S3 ");
367
+ logCommand(cmdSyncAll);
368
+ try {
369
+ runCommandSync(cmdSyncAll, { stdout: "inherit" });
370
+ } catch (error) {
371
+ halt(error.stderr);
372
+ }
373
+ /**
374
+ * Copy entrypoint
375
+ */
376
+ const cmdCpEntry = `
455
377
  aws s3 cp ${paths.appDist}/${entryFilename} ${deployUrl}/${entryFilename}
456
378
  --acl public-read
457
379
  --cache-control max-age=64800
458
380
  ${flags}
459
381
  ${dryRunFlag}
460
382
  `;
461
- logCommand(cmdCpEntry);
462
- try {
463
- runCommandSync(cmdCpEntry, { stdout: "inherit" });
464
- } catch (error) {
465
- halt(error.stderr);
466
- }
467
- if (!dryRunFlag) {
468
- const cmdInvalidateCache = `
383
+ logCommand(cmdCpEntry);
384
+ try {
385
+ runCommandSync(cmdCpEntry, { stdout: "inherit" });
386
+ } catch (error) {
387
+ halt(error.stderr);
388
+ }
389
+ if (!dryRunFlag) {
390
+ /**
391
+ * Invalidate cloudfront cache
392
+ */
393
+ const cmdInvalidateCache = `
469
394
  aws cloudfront create-invalidation
470
395
  --distribution-id ${AWS_CLOUDFRONT_DISTRIBUTION_ID}
471
396
  --paths /${configName}/${tag}/*
472
397
  ${flags}
473
398
  `;
474
- logTitle("\nInvalidating edge cache \u2026");
475
- logCommand(cmdInvalidateCache);
476
- try {
477
- runCommandSync(cmdInvalidateCache);
478
- } catch (error) {
479
- halt(error.stderr);
480
- }
481
- }
482
- const demoPage = `https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/index.html`;
483
- const entryPoint = `https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/${entryFilename}`;
484
- logTitle(`
485
- Bundle for "${configName}" is deployed with tag "${tag}".`);
486
- logDataTable({
487
- "Demo page:": demoPage,
488
- "Entry point:": entryPoint
489
- });
490
- process.on("exit", () => {
491
- notify({
492
- title: "deploy",
493
- message: `
399
+ logTitle("\nInvalidating edge cache ");
400
+ logCommand(cmdInvalidateCache);
401
+ try {
402
+ runCommandSync(cmdInvalidateCache);
403
+ } catch (error) {
404
+ halt(error.stderr);
405
+ }
406
+ }
407
+ const demoPage = `https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/index.html`;
408
+ const entryPoint = `https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/${entryFilename}`;
409
+ logTitle(`\nBundle for "${configName}" is deployed with tag "${tag}".`);
410
+ logDataTable({
411
+ "Demo page:": demoPage,
412
+ "Entry point:": entryPoint
413
+ });
414
+ process.on("exit", () => {
415
+ notify({
416
+ title: "deploy",
417
+ message: `
494
418
  Bundle for "${configName}" is deployed with tag "${tag}".
495
419
  Demo page: ${demoPage}
496
420
  Entry point: ${entryPoint}
497
421
  `
498
- });
499
- });
422
+ });
423
+ });
500
424
  };
501
- var assertOptionsValid3 = (options) => {
502
- const { profile, tag } = options;
503
- if (profile) {
504
- assertProfileValid(profile);
505
- }
506
- assertTagValid(tag);
425
+ const assertOptionsValid$7 = (options) => {
426
+ const { profile, tag } = options;
427
+ if (profile) assertProfileValid(profile);
428
+ assertTagValid(tag);
507
429
  };
508
- var assertDistPathExists = (distPath) => {
509
- if (!existsSync3(distPath)) {
510
- halt(
511
- stripIndent4(`
430
+ const assertDistPathExists = (distPath) => {
431
+ if (!existsSync(distPath)) halt(stripIndent(`
512
432
  Dist folder does not exists.
513
433
  Make sure you have built the bundle before trying to deploy it.
514
- `)
515
- );
516
- }
434
+ `));
517
435
  };
518
436
 
519
- // src/commands/deploy-email-config.ts
520
- import { existsSync as existsSync4 } from "fs";
521
- import prompts2 from "prompts";
522
- var deployEmailConfig = async (options) => {
523
- const { ifCore } = getIfCoreFns();
524
- const paths = getPaths(ifCore);
525
- const emailConfigPath = `${paths.app}/email-config`;
526
- assertIsNotCore2();
527
- assertEmailConfigPathExists(emailConfigPath);
528
- if (!options.profile && !options.ci) {
529
- assertProfilesPresent();
530
- options.profile = await promptProfile();
531
- }
532
- if (!options.bucket) {
533
- options.bucket = options.ci ? AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD : await promptBucketTamaroEmailConfig();
534
- }
535
- assertOptionsValid4(options);
536
- await validate();
537
- awsAuthenticate(options);
538
- const flags = prepareFlags(options);
539
- const dryRunFlag = options.dryrun ? `--dryrun` : "";
540
- const configName = getWidgetUuid();
541
- const deployUrl = `s3://${options.bucket}/${configName}`;
542
- if (dryRunFlag) {
543
- logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
544
- }
545
- const cmd = `
437
+ //#endregion
438
+ //#region src/commands/deploy-email-config.ts
439
+ const deployEmailConfig = async (options) => {
440
+ const { ifCore } = getIfCoreFns();
441
+ const emailConfigPath = `${getPaths(ifCore).app}/email-config`;
442
+ assertIsNotCore$2();
443
+ assertEmailConfigPathExists(emailConfigPath);
444
+ if (!options.profile && !options.ci) {
445
+ assertProfilesPresent();
446
+ options.profile = await promptProfile();
447
+ }
448
+ if (!options.bucket) options.bucket = options.ci ? AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD : await promptBucketTamaroEmailConfig$1();
449
+ assertOptionsValid$6(options);
450
+ await validate();
451
+ awsAuthenticate(options);
452
+ const flags = prepareFlags$3(options);
453
+ const dryRunFlag = options.dryrun ? `--dryrun` : "";
454
+ const configName = getWidgetUuid();
455
+ const deployUrl = `s3://${options.bucket}/${configName}`;
456
+ if (dryRunFlag) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
457
+ const cmd = `
546
458
  aws s3 sync ${emailConfigPath} ${deployUrl}
547
459
  --delete
548
460
  --exact-timestamps
549
461
  ${flags}
550
462
  ${dryRunFlag}
551
463
  `;
552
- logTitle(
553
- `Deploying email configuration for "${configName}" customer configuration to AWS S3 \u2026`
554
- );
555
- logCommand(cmd);
556
- try {
557
- runCommandSync(cmd, { stdout: "inherit" });
558
- } catch (error) {
559
- halt(error.stderr);
560
- }
561
- logTitle(
562
- `Email configuration for "${configName}" customer configuration is deployed.`
563
- );
564
- logDataTable({ "Deploy URL:": deployUrl });
565
- process.on("exit", () => {
566
- notify({
567
- title: "deploy-email-config",
568
- message: `Email configuration for "${configName}" customer configuration is deployed.`
569
- });
570
- });
571
- };
572
- var assertOptionsValid4 = (options) => {
573
- const { profile, bucket } = options;
574
- if (profile) {
575
- assertProfileValid(profile);
576
- }
577
- if (bucket) {
578
- assertBucketValid(bucket);
579
- }
580
- };
581
- var assertEmailConfigPathExists = (distPath) => {
582
- if (!existsSync4(distPath)) {
583
- halt("Email config folder does not exists. Nothing to deploy.");
584
- }
585
- };
586
- var assertIsNotCore2 = () => {
587
- const { ifCore } = getIfCoreFns();
588
- if (ifCore()) {
589
- halt("You cannot deploy widget email configuration for Tamaro Core.");
590
- }
591
- };
592
- var assertBucketValid = (bucket) => {
593
- const buckets = [
594
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
595
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
596
- ];
597
- if (!buckets.includes(bucket)) {
598
- halt("Invalid bucket name.");
599
- }
600
- };
601
- var promptBucketTamaroEmailConfig = async () => {
602
- const buckets = [
603
- { title: "stage", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE },
604
- { title: "prod", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD }
605
- ];
606
- const { bucket } = await prompts2(
607
- [
608
- {
609
- type: "select",
610
- name: "bucket",
611
- message: "Select environment",
612
- choices: buckets
613
- }
614
- ],
615
- {
616
- onCancel: () => halt()
617
- }
618
- );
619
- return bucket;
464
+ logTitle(`Deploying email configuration for "${configName}" customer configuration to AWS S3 …`);
465
+ logCommand(cmd);
466
+ try {
467
+ runCommandSync(cmd, { stdout: "inherit" });
468
+ } catch (error) {
469
+ halt(error.stderr);
470
+ }
471
+ logTitle(`Email configuration for "${configName}" customer configuration is deployed.`);
472
+ logDataTable({ "Deploy URL:": deployUrl });
473
+ process.on("exit", () => {
474
+ notify({
475
+ title: "deploy-email-config",
476
+ message: `Email configuration for "${configName}" customer configuration is deployed.`
477
+ });
478
+ });
479
+ };
480
+ const assertOptionsValid$6 = (options) => {
481
+ const { profile, bucket } = options;
482
+ if (profile) assertProfileValid(profile);
483
+ if (bucket) assertBucketValid$1(bucket);
484
+ };
485
+ const assertEmailConfigPathExists = (distPath) => {
486
+ if (!existsSync(distPath)) halt("Email config folder does not exists. Nothing to deploy.");
487
+ };
488
+ const assertIsNotCore$2 = () => {
489
+ const { ifCore } = getIfCoreFns();
490
+ if (ifCore()) halt("You cannot deploy widget email configuration for Tamaro Core.");
491
+ };
492
+ const assertBucketValid$1 = (bucket) => {
493
+ if (![AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE, AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD].includes(bucket)) halt("Invalid bucket name.");
494
+ };
495
+ const promptBucketTamaroEmailConfig$1 = async () => {
496
+ const { bucket } = await prompts([{
497
+ type: "select",
498
+ name: "bucket",
499
+ message: "Select environment",
500
+ choices: [{
501
+ title: "stage",
502
+ value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE
503
+ }, {
504
+ title: "prod",
505
+ value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
506
+ }]
507
+ }], { onCancel: () => halt() });
508
+ return bucket;
620
509
  };
621
510
 
622
- // src/commands/dev.ts
623
- import { getPortPromise } from "portfinder";
624
- var dev = async (options) => {
625
- const { env } = options;
626
- applyEnv(env);
627
- assertOptionsValid5(options);
628
- const flags = await prepareFlags3(options);
629
- const { ifCore } = getIfCoreFns();
630
- const title = ifCore(
631
- "Running dev web-server for Tamaro Core \u2026",
632
- "Running dev web-server for customer configuration \u2026"
633
- );
634
- const wpBin = resolveBin("webpack");
635
- const wpConfig = resolveOwn("dist/webpack.config.js");
636
- const cmd = `
637
- ${wpBin} serve
638
- --config ${wpConfig}
511
+ //#endregion
512
+ //#region src/commands/dev.ts
513
+ const dev = async (options) => {
514
+ const { env } = options;
515
+ applyEnv(env);
516
+ assertOptionsValid$5(options);
517
+ const flags = await prepareFlags$1(options);
518
+ const { ifCore } = getIfCoreFns();
519
+ const title = ifCore("Running dev web-server for Tamaro Core …", "Running dev web-server for customer configuration …");
520
+ const cmd = `
521
+ ${resolveBin("webpack")} serve
522
+ --config ${resolveOwn("dist/webpack.config.js")}
639
523
  ${flags}
640
524
  `;
641
- logTitle(title);
642
- logCommand(cmd);
643
- runCommandSync(cmd, { stdio: "inherit" });
644
- };
645
- var assertOptionsValid5 = (options) => {
646
- const { localCore, https, port, env } = options;
647
- const { ifCore } = getIfCoreFns();
648
- if (ifCore() && localCore) {
649
- halt('Flag "--local-core" is redundant if running in Tamaro Core context.');
650
- }
651
- if (isNaN(Number(port))) {
652
- halt('Flag "--port" should be a number.');
653
- }
654
- if (https) {
655
- assertCrtExists();
656
- }
657
- assertEnvValid(env);
658
- };
659
- var prepareFlags3 = async (options) => {
660
- const flags = [];
661
- const { localCore, port: defaultPort, https, nolint } = options;
662
- const port = await getPortPromise({ port: Number(defaultPort) });
663
- if (localCore) {
664
- flags.push("--env localCore");
665
- }
666
- flags.push(`--port ${port}`);
667
- if (https) {
668
- flags.push("--env https");
669
- }
670
- if (nolint) {
671
- flags.push("--env nolint");
672
- }
673
- return flags.join(" ");
525
+ logTitle(title);
526
+ logCommand(cmd);
527
+ runCommandSync(cmd, { stdio: "inherit" });
528
+ };
529
+ const assertOptionsValid$5 = (options) => {
530
+ const { localCore, https, port, env } = options;
531
+ const { ifCore } = getIfCoreFns();
532
+ if (ifCore() && localCore) halt("Flag \"--local-core\" is redundant if running in Tamaro Core context.");
533
+ if (isNaN(Number(port))) halt("Flag \"--port\" should be a number.");
534
+ if (https) assertCrtExists();
535
+ assertEnvValid(env);
536
+ };
537
+ const prepareFlags$1 = async (options) => {
538
+ const flags = [];
539
+ const { localCore, port: defaultPort, https, nolint, debug } = options;
540
+ const port = await getPortPromise({ port: Number(defaultPort) });
541
+ if (localCore) flags.push("--env localCore");
542
+ flags.push(`--port ${port}`);
543
+ if (https) flags.push("--env https");
544
+ if (nolint) flags.push("--env nolint");
545
+ if (debug) flags.push("--env debug");
546
+ return flags.join(" ");
674
547
  };
675
548
 
676
- // src/commands/list-deployed.ts
677
- var listDeployed = async (options) => {
678
- if (!options.profile && !options.ci) {
679
- assertProfilesPresent();
680
- options.profile = await promptProfile();
681
- }
682
- assertOptionsValid6(options);
683
- awsAuthenticate(options);
684
- const flags = prepareFlags(options);
685
- const { config } = options;
686
- const { ifCore } = getIfCoreFns();
687
- const configName = config ?? ifCore(CORE_CONFIG_NAME, getWidgetUuid());
688
- const title = !config && ifCore() ? `Listing deployments of Tamaro Core \u2026` : `Listing deployments of "${configName}" customer configuration \u2026`;
689
- const deployUrl = `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/`;
690
- const cmd = `aws s3 ls ${deployUrl} ${flags}`;
691
- let out = "";
692
- logTitle(title);
693
- logCommand(cmd);
694
- try {
695
- const result = runCommandSync(cmd);
696
- out = result.stdout;
697
- } catch (error) {
698
- out = error.stdout;
699
- if (error.stderr) {
700
- halt(error.stderr);
701
- }
702
- }
703
- const lines = out.split("\n");
704
- const tags = parseTags(lines);
705
- let text = "";
706
- if (tags.length === 0) {
707
- text = "No deployments found.";
708
- } else {
709
- text = tags.map((tag, idx) => {
710
- return `${idx + 1}. https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/index.html`;
711
- }).join("\n");
712
- }
713
- console.log(`${text}
714
- `);
715
- };
716
- var assertOptionsValid6 = (options) => {
717
- const { config, profile } = options;
718
- assertConfigValid(config);
719
- if (profile) {
720
- assertProfileValid(profile);
721
- }
722
- };
723
- var assertConfigValid = (config) => {
724
- if (!config) {
725
- return;
726
- }
727
- const regex = /^[a-zA-Z0-9-_]+$/;
728
- if (!regex.test(config)) {
729
- halt(
730
- `Flag "--config" has forbidden format. Allowed format: ${regex.toString()}.`
731
- );
732
- }
733
- };
734
- var parseTags = (lines) => {
735
- const regex = /^\s*PRE\s*/;
736
- return lines.filter((line) => regex.test(line)).map((line) => line.replace(regex, "").replace(/\/$/, ""));
549
+ //#endregion
550
+ //#region src/commands/list-deployed.ts
551
+ const listDeployed = async (options) => {
552
+ if (!options.profile && !options.ci) {
553
+ assertProfilesPresent();
554
+ options.profile = await promptProfile();
555
+ }
556
+ assertOptionsValid$4(options);
557
+ awsAuthenticate(options);
558
+ const flags = prepareFlags$3(options);
559
+ const { config } = options;
560
+ const { ifCore } = getIfCoreFns();
561
+ const configName = config ?? ifCore(CORE_CONFIG_NAME, getWidgetUuid());
562
+ const title = !config && ifCore() ? `Listing deployments of Tamaro Core …` : `Listing deployments of "${configName}" customer configuration …`;
563
+ const cmd = `aws s3 ls ${`s3://${AWS_S3_BUCKET_TAMARO}/${configName}/`} ${flags}`;
564
+ let out = "";
565
+ logTitle(title);
566
+ logCommand(cmd);
567
+ try {
568
+ out = runCommandSync(cmd).stdout;
569
+ } catch (error) {
570
+ out = error.stdout;
571
+ if (error.stderr) halt(error.stderr);
572
+ }
573
+ const tags = parseTags(out.split("\n"));
574
+ let text = "";
575
+ if (tags.length === 0) text = "No deployments found.";
576
+ else text = tags.map((tag, idx) => {
577
+ return `${idx + 1}. https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/index.html`;
578
+ }).join("\n");
579
+ console.log(`${text}\n`);
580
+ };
581
+ const assertOptionsValid$4 = (options) => {
582
+ const { config, profile } = options;
583
+ assertConfigValid(config);
584
+ if (profile) assertProfileValid(profile);
585
+ };
586
+ const assertConfigValid = (config) => {
587
+ if (!config) return;
588
+ const regex = /^[a-zA-Z0-9-_]+$/;
589
+ if (!regex.test(config)) halt(`Flag "--config" has forbidden format. Allowed format: ${regex.toString()}.`);
590
+ };
591
+ const parseTags = (lines) => {
592
+ const regex = /^\s*PRE\s*/;
593
+ return lines.filter((line) => regex.test(line)).map((line) => line.replace(regex, "").replace(/\/$/, ""));
737
594
  };
738
595
 
739
- // src/commands/serve.ts
740
- import { resolve as resolve3 } from "path";
741
- import { getPortPromise as getPortPromise2 } from "portfinder";
742
- var serve = async (options) => {
743
- assertOptionsValid7(options);
744
- const flags = await prepareFlags4(options);
745
- const { ifCore } = getIfCoreFns();
746
- const paths = getPaths(ifCore);
747
- const cmd = `
748
- npx -y serve ${paths.appDist}
596
+ //#endregion
597
+ //#region src/commands/serve.ts
598
+ const serve = async (options) => {
599
+ assertOptionsValid$3(options);
600
+ const flags = await prepareFlags(options);
601
+ const { ifCore } = getIfCoreFns();
602
+ const cmd = `
603
+ npx -y serve ${getPaths(ifCore).appDist}
749
604
  --cors
750
605
  ${flags}
751
606
  `;
752
- logTitle(`Running web-server for pre-built bundle \u2026`);
753
- logCommand(cmd);
754
- runCommandSync(cmd, { stdio: "inherit" });
755
- };
756
- var assertOptionsValid7 = (options) => {
757
- const { port, https } = options;
758
- if (isNaN(Number(port))) {
759
- halt('Flag "--port" should be a number.');
760
- }
761
- if (https) {
762
- assertCrtExists();
763
- }
764
- };
765
- var prepareFlags4 = async (options) => {
766
- const flags = [];
767
- const { port: defaultPort, https } = options;
768
- const port = await getPortPromise2({ port: Number(defaultPort) });
769
- flags.push(`-p ${port}`);
770
- if (https) {
771
- const { ifCore } = getIfCoreFns();
772
- const paths = getPaths(ifCore);
773
- const certFile = resolve3(paths.root, HTTPS_CRT_FILE);
774
- const keyFile = resolve3(paths.root, HTTPS_KEY_FILE);
775
- flags.push(`--ssl-cert ${certFile}`);
776
- flags.push(`--ssl-key ${keyFile}`);
777
- }
778
- return flags.join(" ");
607
+ logTitle(`Running web-server for pre-built bundle …`);
608
+ logCommand(cmd);
609
+ runCommandSync(cmd, { stdio: "inherit" });
610
+ };
611
+ const assertOptionsValid$3 = (options) => {
612
+ const { port, https } = options;
613
+ if (isNaN(Number(port))) halt("Flag \"--port\" should be a number.");
614
+ if (https) assertCrtExists();
615
+ };
616
+ const prepareFlags = async (options) => {
617
+ const flags = [];
618
+ const { port: defaultPort, https } = options;
619
+ const port = await getPortPromise({ port: Number(defaultPort) });
620
+ flags.push(`-p ${port}`);
621
+ if (https) {
622
+ const { ifCore } = getIfCoreFns();
623
+ const paths = getPaths(ifCore);
624
+ const certFile = resolve(paths.root, HTTPS_CRT_FILE);
625
+ const keyFile = resolve(paths.root, HTTPS_KEY_FILE);
626
+ flags.push(`--ssl-cert ${certFile}`);
627
+ flags.push(`--ssl-key ${keyFile}`);
628
+ }
629
+ return flags.join(" ");
779
630
  };
780
631
 
781
- // src/commands/unarchive.ts
782
- import { existsSync as existsSync5, renameSync as renameSync2 } from "fs";
783
- import { basename as basename2, dirname as dirname2, resolve as resolve4 } from "path";
784
- var unarchive = (options) => {
785
- const { ifCore } = getIfCoreFns({ allowArchived: true });
786
- assertIsNotCore3(ifCore);
787
- assertIsArchived();
788
- const configName = getWidgetUuid();
789
- const cwd = process.cwd();
790
- const archivedDir = dirname2(cwd);
791
- const configsDir = dirname2(archivedDir);
792
- const restoreTarget = resolve4(configsDir, configName);
793
- assertRestoreTargetDoesNotExist(restoreTarget);
794
- assertOptionsValid8(options);
795
- if (options.dryrun) {
796
- logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
797
- }
798
- if (!options.dryrun) {
799
- renameSync2(cwd, restoreTarget);
800
- process.chdir(restoreTarget);
801
- } else {
802
- logTitle(`Would move ${cwd} \u2192 ${restoreTarget}`);
803
- }
804
- logTitle(`\u2705 "${configName}" has been unarchived and moved to ${restoreTarget}`);
805
- process.on("exit", () => {
806
- notify({
807
- title: "unarchive",
808
- message: `"${configName}" has been unarchived.`
809
- });
810
- });
811
- };
812
- var assertOptionsValid8 = (options) => {
813
- const { profile } = options;
814
- if (profile) {
815
- assertProfileValid(profile);
816
- }
817
- };
818
- var assertIsArchived = () => {
819
- const parentDirName = basename2(dirname2(process.cwd()));
820
- if (parentDirName !== "_archived") {
821
- halt(
822
- "This configuration is not archived. Only archived configurations can be unarchived."
823
- );
824
- }
825
- };
826
- var assertIsNotCore3 = (ifCore) => {
827
- if (ifCore()) {
828
- halt("You cannot unarchive Tamaro Core.");
829
- }
830
- };
831
- var assertRestoreTargetDoesNotExist = (restoreTarget) => {
832
- if (existsSync5(restoreTarget)) {
833
- halt(
834
- `Restore target already exists: ${restoreTarget}. Please remove it first.`
835
- );
836
- }
632
+ //#endregion
633
+ //#region src/commands/unarchive.ts
634
+ const unarchive = (options) => {
635
+ const { ifCore } = getIfCoreFns({ allowArchived: true });
636
+ assertIsNotCore$1(ifCore);
637
+ assertIsArchived();
638
+ const configName = getWidgetUuid();
639
+ const cwd = process.cwd();
640
+ const restoreTarget = resolve(dirname(dirname(cwd)), configName);
641
+ assertRestoreTargetDoesNotExist(restoreTarget);
642
+ assertOptionsValid$2(options);
643
+ if (options.dryrun) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
644
+ if (!options.dryrun) {
645
+ renameSync(cwd, restoreTarget);
646
+ process.chdir(restoreTarget);
647
+ } else logTitle(`Would move ${cwd} ${restoreTarget}`);
648
+ logTitle(`✅ "${configName}" has been unarchived and moved to ${restoreTarget}`);
649
+ process.on("exit", () => {
650
+ notify({
651
+ title: "unarchive",
652
+ message: `"${configName}" has been unarchived.`
653
+ });
654
+ });
655
+ };
656
+ const assertOptionsValid$2 = (options) => {
657
+ const { profile } = options;
658
+ if (profile) assertProfileValid(profile);
659
+ };
660
+ const assertIsArchived = () => {
661
+ if (basename(dirname(process.cwd())) !== "_archived") halt("This configuration is not archived. Only archived configurations can be unarchived.");
662
+ };
663
+ const assertIsNotCore$1 = (ifCore) => {
664
+ if (ifCore()) halt("You cannot unarchive Tamaro Core.");
665
+ };
666
+ const assertRestoreTargetDoesNotExist = (restoreTarget) => {
667
+ if (existsSync(restoreTarget)) halt(`Restore target already exists: ${restoreTarget}. Please remove it first.`);
837
668
  };
838
669
 
839
- // src/commands/undeploy.ts
840
- var undeploy = async (options) => {
841
- const { ifCore } = getIfCoreFns();
842
- const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
843
- if (!options.profile && !options.ci) {
844
- assertProfilesPresent();
845
- options.profile = await promptProfile();
846
- }
847
- assertOptionsValid9(options);
848
- awsAuthenticate(options);
849
- const flags = prepareFlags(options);
850
- const { tag = DEFAULT_TAG, all, dryrun } = options;
851
- const dryRunFlag = dryrun ? `--dryrun` : "";
852
- const deployUrl = all ? `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/` : `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/`;
853
- const description = all ? `all tags of "${configName}"` : `tag "${tag}" of "${configName}"`;
854
- if (dryRunFlag) {
855
- logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
856
- }
857
- await promptConfirmation(
858
- `Are you sure you want to undeploy ${description}?`,
859
- options.yes
860
- );
861
- const cmd = `
670
+ //#endregion
671
+ //#region src/commands/undeploy.ts
672
+ const undeploy = async (options) => {
673
+ const { ifCore } = getIfCoreFns();
674
+ const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
675
+ if (!options.profile && !options.ci) {
676
+ assertProfilesPresent();
677
+ options.profile = await promptProfile();
678
+ }
679
+ assertOptionsValid$1(options);
680
+ awsAuthenticate(options);
681
+ const flags = prepareFlags$3(options);
682
+ const { tag = DEFAULT_TAG, all, dryrun } = options;
683
+ const dryRunFlag = dryrun ? `--dryrun` : "";
684
+ const deployUrl = all ? `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/` : `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/`;
685
+ const description = all ? `all tags of "${configName}"` : `tag "${tag}" of "${configName}"`;
686
+ if (dryRunFlag) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
687
+ await promptConfirmation(`Are you sure you want to undeploy ${description}?`, options.yes);
688
+ const cmd = `
862
689
  aws s3 rm ${deployUrl}
863
690
  --recursive
864
691
  ${flags}
865
692
  ${dryRunFlag}
866
693
  `;
867
- logTitle(`Undeploying ${description} from AWS S3 \u2026`);
868
- logCommand(cmd);
869
- try {
870
- runCommandSync(cmd, { stdout: "inherit" });
871
- } catch (error) {
872
- halt(error.stderr);
873
- }
874
- if (!dryRunFlag) {
875
- const invalidationPath = all ? `/${configName}/*` : `/${configName}/${tag}/*`;
876
- const cmdInvalidateCache = `
694
+ logTitle(`Undeploying ${description} from AWS S3 …`);
695
+ logCommand(cmd);
696
+ try {
697
+ runCommandSync(cmd, { stdout: "inherit" });
698
+ } catch (error) {
699
+ halt(error.stderr);
700
+ }
701
+ if (!dryRunFlag) {
702
+ const cmdInvalidateCache = `
877
703
  aws cloudfront create-invalidation
878
704
  --distribution-id ${AWS_CLOUDFRONT_DISTRIBUTION_ID}
879
- --paths ${invalidationPath}
705
+ --paths ${all ? `/${configName}/*` : `/${configName}/${tag}/*`}
880
706
  ${flags}
881
707
  `;
882
- logTitle("\nInvalidating edge cache \u2026");
883
- logCommand(cmdInvalidateCache);
884
- try {
885
- runCommandSync(cmdInvalidateCache);
886
- } catch (error) {
887
- halt(error.stderr);
888
- }
889
- }
890
- logTitle(`
891
- ${description} has been undeployed.`);
892
- logDataTable({ "Removed URL:": deployUrl });
893
- process.on("exit", () => {
894
- notify({
895
- title: "undeploy",
896
- message: `${description} has been undeployed.`
897
- });
898
- });
899
- };
900
- var assertOptionsValid9 = (options) => {
901
- const { profile, tag, all } = options;
902
- const { ifCore } = getIfCoreFns();
903
- if (ifCore() && all) {
904
- halt('Flag "--all" must not be used in Tamaro Core context.');
905
- }
906
- if (ifCore() && tag === DEFAULT_TAG) {
907
- halt("You cannot undeploy the default tag for Tamaro Core.");
908
- }
909
- if (profile) {
910
- assertProfileValid(profile);
911
- }
912
- if (all && tag) {
913
- halt('Flags "--tag" and "--all" must not be used together.');
914
- }
915
- if (!all && tag) {
916
- assertTagValid(tag);
917
- }
708
+ logTitle("\nInvalidating edge cache ");
709
+ logCommand(cmdInvalidateCache);
710
+ try {
711
+ runCommandSync(cmdInvalidateCache);
712
+ } catch (error) {
713
+ halt(error.stderr);
714
+ }
715
+ }
716
+ logTitle(`\n${description} has been undeployed.`);
717
+ logDataTable({ "Removed URL:": deployUrl });
718
+ process.on("exit", () => {
719
+ notify({
720
+ title: "undeploy",
721
+ message: `${description} has been undeployed.`
722
+ });
723
+ });
724
+ };
725
+ const assertOptionsValid$1 = (options) => {
726
+ const { profile, tag, all } = options;
727
+ const { ifCore } = getIfCoreFns();
728
+ if (ifCore() && all) halt("Flag \"--all\" must not be used in Tamaro Core context.");
729
+ if (ifCore() && tag === DEFAULT_TAG) halt("You cannot undeploy the default tag for Tamaro Core.");
730
+ if (profile) assertProfileValid(profile);
731
+ if (all && tag) halt("Flags \"--tag\" and \"--all\" must not be used together.");
732
+ if (!all && tag) assertTagValid(tag);
918
733
  };
919
734
 
920
- // src/commands/undeploy-email-config.ts
921
- import prompts3 from "prompts";
922
- var undeployEmailConfig = async (options) => {
923
- const { ifCore } = getIfCoreFns();
924
- assertIsNotCore4(ifCore);
925
- if (!options.profile && !options.ci) {
926
- assertProfilesPresent();
927
- options.profile = await promptProfile();
928
- }
929
- if (options.ci) {
930
- options.bucket = AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD;
931
- }
932
- if (!options.bucket) {
933
- if (options.ci) {
934
- options.bucket = AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD;
935
- } else if (options.prod) {
936
- options.bucket = AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD;
937
- } else if (options.stage) {
938
- options.bucket = AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE;
939
- } else {
940
- options.bucket = await promptBucketTamaroEmailConfig2();
941
- }
942
- }
943
- assertOptionsValid10(options);
944
- awsAuthenticate(options);
945
- const flags = prepareFlags(options);
946
- const dryRunFlag = options.dryrun ? `--dryrun` : "";
947
- const configName = getWidgetUuid();
948
- const deployUrl = `s3://${options.bucket}/${configName}/`;
949
- await promptConfirmation(
950
- `Are you sure you want to undeploy the email configuration for "${configName}" from "${options.bucket}"?`,
951
- options.ci
952
- );
953
- if (dryRunFlag) {
954
- logTitle("\u{1F9EA} DRY RUN MODE - No actual changes will be made \u{1F9EA}");
955
- }
956
- const cmd = `
735
+ //#endregion
736
+ //#region src/commands/undeploy-email-config.ts
737
+ const undeployEmailConfig = async (options) => {
738
+ const { ifCore } = getIfCoreFns();
739
+ assertIsNotCore(ifCore);
740
+ if (!options.profile && !options.ci) {
741
+ assertProfilesPresent();
742
+ options.profile = await promptProfile();
743
+ }
744
+ if (options.ci) options.bucket = AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD;
745
+ if (!options.bucket) if (options.ci) options.bucket = AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD;
746
+ else if (options.prod) options.bucket = AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD;
747
+ else if (options.stage) options.bucket = AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE;
748
+ else options.bucket = await promptBucketTamaroEmailConfig();
749
+ assertOptionsValid(options);
750
+ awsAuthenticate(options);
751
+ const flags = prepareFlags$3(options);
752
+ const dryRunFlag = options.dryrun ? `--dryrun` : "";
753
+ const configName = getWidgetUuid();
754
+ const deployUrl = `s3://${options.bucket}/${configName}/`;
755
+ await promptConfirmation(`Are you sure you want to undeploy the email configuration for "${configName}" from "${options.bucket}"?`, options.ci);
756
+ if (dryRunFlag) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
757
+ const cmd = `
957
758
  aws s3 rm ${deployUrl}
958
759
  --recursive
959
760
  ${flags}
960
761
  ${dryRunFlag}
961
762
  `;
962
- logTitle(`Undeploying email configuration for "${configName}" from AWS S3 \u2026`);
963
- logCommand(cmd);
964
- try {
965
- runCommandSync(cmd, { stdout: "inherit" });
966
- } catch (error) {
967
- halt(error.stderr);
968
- }
969
- logTitle(
970
- `Email configuration for "${configName}" has been undeployed from "${options.bucket}".`
971
- );
972
- logDataTable({ "Removed URL:": deployUrl });
973
- process.on("exit", () => {
974
- notify({
975
- title: "undeploy-email-config",
976
- message: `Email configuration for "${configName}" has been undeployed.`
977
- });
978
- });
979
- };
980
- var assertOptionsValid10 = (options) => {
981
- const { profile, bucket } = options;
982
- if (profile) {
983
- assertProfileValid(profile);
984
- }
985
- if (bucket) {
986
- assertBucketValid2(bucket);
987
- }
988
- };
989
- var assertIsNotCore4 = (ifCore) => {
990
- if (ifCore()) {
991
- halt("You cannot undeploy widget email configuration for Tamaro Core.");
992
- }
993
- };
994
- var assertBucketValid2 = (bucket) => {
995
- const buckets = [
996
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE,
997
- AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
998
- ];
999
- if (!buckets.includes(bucket)) {
1000
- halt("Invalid bucket name.");
1001
- }
1002
- };
1003
- var promptBucketTamaroEmailConfig2 = async () => {
1004
- const buckets = [
1005
- { title: "stage", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE },
1006
- { title: "prod", value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD }
1007
- ];
1008
- const { bucket } = await prompts3(
1009
- [
1010
- {
1011
- type: "select",
1012
- name: "bucket",
1013
- message: "Select environment",
1014
- choices: buckets
1015
- }
1016
- ],
1017
- {
1018
- onCancel: () => halt()
1019
- }
1020
- );
1021
- return bucket;
763
+ logTitle(`Undeploying email configuration for "${configName}" from AWS S3 …`);
764
+ logCommand(cmd);
765
+ try {
766
+ runCommandSync(cmd, { stdout: "inherit" });
767
+ } catch (error) {
768
+ halt(error.stderr);
769
+ }
770
+ logTitle(`Email configuration for "${configName}" has been undeployed from "${options.bucket}".`);
771
+ logDataTable({ "Removed URL:": deployUrl });
772
+ process.on("exit", () => {
773
+ notify({
774
+ title: "undeploy-email-config",
775
+ message: `Email configuration for "${configName}" has been undeployed.`
776
+ });
777
+ });
778
+ };
779
+ const assertOptionsValid = (options) => {
780
+ const { profile, bucket } = options;
781
+ if (profile) assertProfileValid(profile);
782
+ if (bucket) assertBucketValid(bucket);
783
+ };
784
+ const assertIsNotCore = (ifCore) => {
785
+ if (ifCore()) halt("You cannot undeploy widget email configuration for Tamaro Core.");
786
+ };
787
+ const assertBucketValid = (bucket) => {
788
+ if (![AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE, AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD].includes(bucket)) halt("Invalid bucket name.");
789
+ };
790
+ const promptBucketTamaroEmailConfig = async () => {
791
+ const { bucket } = await prompts([{
792
+ type: "select",
793
+ name: "bucket",
794
+ message: "Select environment",
795
+ choices: [{
796
+ title: "stage",
797
+ value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE
798
+ }, {
799
+ title: "prod",
800
+ value: AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD
801
+ }]
802
+ }], { onCancel: () => halt() });
803
+ return bucket;
1022
804
  };
1023
805
 
1024
- // package.json
1025
- var package_default = {
1026
- name: "@raisenow/tamaro-cli",
1027
- version: "1.6.1",
1028
- author: {
1029
- name: "RaiseNow",
1030
- email: "development@raisenow.com"
1031
- },
1032
- type: "module",
1033
- bin: "dist/cli.js",
1034
- packageManager: "pnpm@10.28.2",
1035
- engines: {
1036
- node: ">=22.22.0",
1037
- pnpm: ">=10.28.2",
1038
- npm: "please-use-pnpm",
1039
- yarn: "please-use-pnpm"
1040
- },
1041
- scripts: {
1042
- prepare: "pnpm build",
1043
- typecheck: "tsc --noEmit",
1044
- build: "tsup src/cli.ts src/webpack.config.ts --clean --shims --format esm",
1045
- "build:watch": "pnpm build --watch --ignore-watch .history --ignore-watch .idea",
1046
- dev: "pnpm build:watch",
1047
- readme: "pnpx http-server . --cors -o /readme.html",
1048
- lint: "DEBUG=eslint:cli-engine eslint .",
1049
- "lint:fix": "DEBUG=eslint:cli-engine eslint --fix .",
1050
- format: "prettier --write .",
1051
- test: "vitest",
1052
- "test:watch": "vitest --watch",
1053
- "test:coverage": "vitest --coverage"
1054
- },
1055
- dependencies: {
1056
- "@babel/cli": "^7.28.6",
1057
- "@babel/core": "^7.29.0",
1058
- "@babel/plugin-proposal-decorators": "^7.29.0",
1059
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
1060
- "@babel/plugin-transform-runtime": "^7.29.0",
1061
- "@babel/preset-env": "^7.29.0",
1062
- "@babel/preset-react": "^7.28.5",
1063
- "@babel/preset-typescript": "^7.28.5",
1064
- "@babel/runtime-corejs3": "^7.29.0",
1065
- "@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
1066
- "@statoscope/webpack-plugin": "^5.29.0",
1067
- "@types/columnify": "^1.5.4",
1068
- "@types/handlebars-helpers": "^0.5.6",
1069
- "@types/lodash": "^4.17.23",
1070
- "@types/node": "^22.19.8",
1071
- "@types/node-notifier": "^8.0.5",
1072
- "@types/prompts": "^2.4.9",
1073
- "@types/resolve-bin": "^0.4.3",
1074
- "@types/webpack-config-utils": "^2.3.4",
1075
- "@typescript-eslint/eslint-plugin": "^7.18.0",
1076
- "@typescript-eslint/parser": "^7.18.0",
1077
- autoprefixer: "^10.4.24",
1078
- "babel-loader": "^10.0.0",
1079
- "babel-plugin-istanbul": "^7.0.1",
1080
- "babel-plugin-lodash": "^3.3.4",
1081
- chalk: "^5.6.2",
1082
- "clean-webpack-plugin": "^4.0.0",
1083
- columnify: "^1.6.0",
1084
- commander: "^14.0.3",
1085
- "copy-webpack-plugin": "^13.0.1",
1086
- "core-js": "^3.48.0",
1087
- "css-loader": "^7.1.3",
1088
- "css-minimizer-webpack-plugin": "^7.0.4",
1089
- dotenv: "^17.2.3",
1090
- "escape-string-regexp": "^5.0.0",
1091
- eslint: "^8.57.1",
1092
- "eslint-config-prettier": "^10.1.8",
1093
- "eslint-import-resolver-typescript": "^3.10.1",
1094
- "eslint-plugin-deprecation": "^3.0.0",
1095
- "eslint-plugin-import": "^2.32.0",
1096
- "eslint-plugin-markdownlint": "^0.9.0",
1097
- "eslint-plugin-promise": "^7.2.1",
1098
- "eslint-plugin-unused-imports": "^3.2.0",
1099
- "eslint-plugin-yml": "^1.19.1",
1100
- "eslint-webpack-plugin": "^5.0.2",
1101
- execa: "^9.6.1",
1102
- "file-loader": "^6.2.0",
1103
- "fork-ts-checker-webpack-plugin": "^9.1.0",
1104
- glob: "^13.0.1",
1105
- handlebars: "^4.7.8",
1106
- "handlebars-helpers": "^0.10.0",
1107
- "html-loader": "^5.1.0",
1108
- "html-webpack-plugin": "^5.6.6",
1109
- "json-loader": "^0.5.7",
1110
- lodash: "^4.17.23",
1111
- "mini-css-extract-plugin": "^2.10.0",
1112
- "node-notifier": "^10.0.1",
1113
- portfinder: "^1.0.38",
1114
- postcss: "^8.5.6",
1115
- "postcss-custom-properties": "^14.0.6",
1116
- "postcss-import": "^16.1.1",
1117
- "postcss-loader": "^8.2.0",
1118
- "postcss-nested": "^7.0.2",
1119
- prettier: "^3.8.1",
1120
- prompts: "^2.4.2",
1121
- "react-refresh": "^0.18.0",
1122
- "resolve-url-loader": "^5.0.0",
1123
- sass: "^1.97.3",
1124
- "sass-loader": "^16.0.6",
1125
- "strip-indent": "^4.1.1",
1126
- "style-loader": "^4.0.0",
1127
- "terser-webpack-plugin": "^5.3.16",
1128
- "tsconfig-paths-webpack-plugin": "^4.2.0",
1129
- tsup: "^8.5.1",
1130
- typescript: "^5.9.3",
1131
- "url-loader": "^4.1.1",
1132
- vitest: "^4.0.18",
1133
- webpack: "^5.105.0",
1134
- "webpack-cli": "^6.0.1",
1135
- "webpack-config-utils": "^2.3.1",
1136
- "webpack-dev-server": "^5.2.3",
1137
- "yaml-loader": "^0.9.0"
1138
- }
1139
- };
806
+ //#endregion
807
+ //#region package.json
808
+ var name = "@raisenow/tamaro-cli";
809
+ var version = "1.7.0-dev.1";
1140
810
 
1141
- // src/cli.ts
811
+ //#endregion
812
+ //#region src/cli.ts
813
+ /**
814
+ * Makes the script crash on unhandled rejections instead of silently
815
+ * ignoring them. In the future, promise rejections that are not handled will
816
+ * terminate the Node.js process with a non-zero exit code.
817
+ */
1142
818
  process.on("unhandledRejection", (error) => {
1143
- throw error;
819
+ throw error;
1144
820
  });
1145
821
  process.on("SIGINT", () => {
1146
- halt();
822
+ halt();
1147
823
  });
1148
- var cli = createCommand().name(package_default.name).version(package_default.version, "-v, --version");
1149
- cli.command("dev").description(
1150
- "Run the local development server for Tamaro Core or a particular customer configuration"
1151
- ).option(
1152
- "--local-core",
1153
- "Load Tamaro Core from localhost:1234 instead of the CDN",
1154
- false
1155
- ).option(
1156
- "--https",
1157
- "Let local web server serve Tamaro with SSL encryption",
1158
- false
1159
- ).option("--port <port>", "Web server port", `${DEFAULT_PORT}`).option("--env <env>", "Environment (dev, stage, prod)").option("--nolint", "Disable ESLint", false).action(async (options) => {
1160
- await dev(options);
824
+ const cli = createCommand().name(name).version(version, "-v, --version");
825
+ cli.command("dev").description("Run the local development server for Tamaro Core or a particular customer configuration").option("--local-core", "Load Tamaro Core from localhost:1234 instead of the CDN", false).option("--https", "Let local web server serve Tamaro with SSL encryption", false).option("--port <port>", "Web server port", `${DEFAULT_PORT}`).option("--env <env>", "Environment (dev, stage, prod)").option("--nolint", "Disable ESLint", false).option("--debug", "Enable debug mode", false).action(async (options) => {
826
+ await dev(options);
1161
827
  });
1162
- cli.command("build").description(
1163
- "Build an optimised (minified) bundle of Tamaro Core or a customer configuration"
1164
- ).option(
1165
- "--local-core",
1166
- "Load Tamaro Core from localhost:1234 instead of the CDN",
1167
- false
1168
- ).option("--analyze", 'Generate bundle statistics to "reports" folder', false).option("--serve", "Run the generated bundle with a local web server", false).option(
1169
- "--https",
1170
- "Let local web server serve Tamaro with SSL encryption",
1171
- false
1172
- ).option("--port <port>", "Web server port", `${DEFAULT_PORT}`).option("--env <env>", "Environment (dev, stage, prod)").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option(
1173
- "--deploy",
1174
- "Deploy Tamaro Core or a customer configuration bundle to AWS S3",
1175
- false
1176
- ).option(
1177
- "--tag <tag>",
1178
- "Tag which should be used for the deployment of the bundle",
1179
- DEFAULT_TAG
1180
- ).option("--nolint", "Disable ESLint", false).action(async (options) => {
1181
- await build(options);
1182
- if (options.serve) {
1183
- await serve(options);
1184
- }
1185
- if (options.deploy) {
1186
- await deploy(options);
1187
- }
828
+ cli.command("build").description("Build an optimised (minified) bundle of Tamaro Core or a customer configuration").option("--local-core", "Load Tamaro Core from localhost:1234 instead of the CDN", false).option("--analyze", "Generate bundle statistics to \"reports\" folder", false).option("--serve", "Run the generated bundle with a local web server", false).option("--https", "Let local web server serve Tamaro with SSL encryption", false).option("--port <port>", "Web server port", `${DEFAULT_PORT}`).option("--env <env>", "Environment (dev, stage, prod)").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--deploy", "Deploy Tamaro Core or a customer configuration bundle to AWS S3", false).option("--tag <tag>", "Tag which should be used for the deployment of the bundle", DEFAULT_TAG).option("--nolint", "Disable ESLint", false).option("--debug", "Enable debug mode", false).action(async (options) => {
829
+ await build(options);
830
+ if (options.serve) await serve(options);
831
+ if (options.deploy) await deploy(options);
1188
832
  });
1189
- cli.command("serve").description("Run a local web server for pre-built bundle").option("--port <port>", "Web server port", `${DEFAULT_PORT}`).option(
1190
- "--https",
1191
- "Let local web server serve Tamaro with SSL encryption",
1192
- false
1193
- ).action(async (options) => {
1194
- await serve(options);
833
+ cli.command("serve").description("Run a local web server for pre-built bundle").option("--port <port>", "Web server port", `${DEFAULT_PORT}`).option("--https", "Let local web server serve Tamaro with SSL encryption", false).action(async (options) => {
834
+ await serve(options);
1195
835
  });
1196
- cli.command("deploy").description(
1197
- "Deploy a pre-built Tamaro Core or customer configuration bundle to AWS S3"
1198
- ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option(
1199
- "--tag <tag>",
1200
- "Tag which should be used for the deployment of the bundle",
1201
- DEFAULT_TAG
1202
- ).option(
1203
- "--dryrun",
1204
- "Displays the operations that would be performed without actually running them",
1205
- false
1206
- ).action(async (options) => {
1207
- await deploy(options);
836
+ cli.command("deploy").description("Deploy a pre-built Tamaro Core or customer configuration bundle to AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--tag <tag>", "Tag which should be used for the deployment of the bundle", DEFAULT_TAG).option("--dryrun", "Displays the operations that would be performed without actually running them", false).action(async (options) => {
837
+ await deploy(options);
1208
838
  });
1209
- cli.command("list-deployed").description(
1210
- "List deployments of Tamaro Core or a particular customer configuration"
1211
- ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option(
1212
- "--config <config>",
1213
- "Configuration name (default: current customer configuration)"
1214
- ).action(async (options) => {
1215
- await listDeployed(options);
839
+ cli.command("list-deployed").description("List deployments of Tamaro Core or a particular customer configuration").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--config <config>", "Configuration name (default: current customer configuration)").action(async (options) => {
840
+ await listDeployed(options);
1216
841
  });
1217
- cli.command("deploy-email-config").description("Deploy a widget's email configuration to AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--bucket <bucket>", "AWS bucket for email configs").option(
1218
- "--dryrun",
1219
- "Displays the operations that would be performed without actually running them",
1220
- false
1221
- ).action(async (options) => {
1222
- await deployEmailConfig(options);
842
+ cli.command("deploy-email-config").description("Deploy a widget's email configuration to AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--bucket <bucket>", "AWS bucket for email configs").option("--dryrun", "Displays the operations that would be performed without actually running them", false).action(async (options) => {
843
+ await deployEmailConfig(options);
1223
844
  });
1224
- cli.command("undeploy").description(
1225
- "Undeploy a Tamaro Core or customer configuration bundle from AWS S3"
1226
- ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--tag <tag>", "Tag which should be undeployed").option("--all", "Undeploy all tags", false).option(
1227
- "--dryrun",
1228
- "Displays the operations that would be performed without actually running them",
1229
- false
1230
- ).action(async (options) => {
1231
- await undeploy(options);
845
+ cli.command("undeploy").description("Undeploy a Tamaro Core or customer configuration bundle from AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--tag <tag>", "Tag which should be undeployed").option("--all", "Undeploy all tags", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).action(async (options) => {
846
+ await undeploy(options);
1232
847
  });
1233
- 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(
1234
- "--stage",
1235
- "Undeploy from stage environment (alternative to --bucket)"
1236
- ).option("--prod", "Undeploy from prod environment (alternative to --bucket)").option(
1237
- "--dryrun",
1238
- "Displays the operations that would be performed without actually running them",
1239
- false
1240
- ).action(async (options) => {
1241
- await undeployEmailConfig(options);
848
+ 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("--stage", "Undeploy from stage environment (alternative to --bucket)").option("--prod", "Undeploy from prod environment (alternative to --bucket)").option("--dryrun", "Displays the operations that would be performed without actually running them", false).action(async (options) => {
849
+ await undeployEmailConfig(options);
1242
850
  });
1243
- cli.command("archive").description(
1244
- "Archive a customer configuration by moving it to the _archived folder"
1245
- ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option(
1246
- "--dryrun",
1247
- "Displays the operations that would be performed without actually running them",
1248
- false
1249
- ).action((options) => {
1250
- archive(options);
851
+ cli.command("archive").description("Archive a customer configuration by moving it to the _archived folder").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).action((options) => {
852
+ archive(options);
1251
853
  });
1252
- cli.command("unarchive").description(
1253
- "Unarchive a customer configuration by restoring it from the _archived folder"
1254
- ).option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option(
1255
- "--dryrun",
1256
- "Displays the operations that would be performed without actually running them",
1257
- false
1258
- ).action((options) => {
1259
- unarchive(options);
854
+ cli.command("unarchive").description("Unarchive a customer configuration by restoring it from the _archived folder").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).action((options) => {
855
+ unarchive(options);
1260
856
  });
1261
- cli.command("validate").description(
1262
- "Validate the current Tamaro configuration to avoid common errors"
1263
- ).action(async () => {
1264
- await validate();
857
+ cli.command("validate").description("Validate the current Tamaro configuration to avoid common errors").action(async () => {
858
+ await validate();
1265
859
  });
1266
860
  (async () => {
1267
- try {
1268
- logTitle(`${package_default.name} ${package_default.version}
1269
- `);
1270
- await cli.parseAsync(process.argv);
1271
- } catch (error) {
1272
- if (error.signal === "SIGINT" && error.signalDescription) {
1273
- logError(`
1274
- ${error.signalDescription}`);
1275
- } else {
1276
- console.log("");
1277
- logError(error.stack);
1278
- }
1279
- }
861
+ try {
862
+ logTitle(`${name} ${version}\n`);
863
+ await cli.parseAsync(process.argv);
864
+ } catch (error) {
865
+ if (error.signal === "SIGINT" && error.signalDescription) logError(`\n${error.signalDescription}`);
866
+ else {
867
+ console.log("");
868
+ logError(error.stack);
869
+ }
870
+ }
1280
871
  })();
872
+
873
+ //#endregion
874
+ export { };