@raisenow/tamaro-cli 1.8.0 → 1.9.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.
- package/README.md +11 -0
- package/dist/cli.js +371 -29
- package/dist/{env-CSg7BrvF.js → env-JzPIlctP.js} +58 -12
- package/dist/webpack.config.js +1 -1
- package/package.json +9 -2
- package/.husky/pre-commit +0 -6
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/tamaro-cli.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/.idea/workspace.xml +0 -55
- package/.nvmrc +0 -1
- package/.prettierignore +0 -8
- package/bitbucket-pipelines.yml +0 -40
- package/out-tsc/lib/tsconfig.lib.tsbuildinfo +0 -1
- package/out-tsc/node/eslint.config.d.ts +0 -5
- package/out-tsc/node/eslint.config.d.ts.map +0 -1
- package/out-tsc/node/tsconfig.node.tsbuildinfo +0 -1
- package/out-tsc/node/tsdown.config.d.ts +0 -3
- package/out-tsc/node/tsdown.config.d.ts.map +0 -1
- package/out-tsc/node/vitest.config.d.ts +0 -3
- package/out-tsc/node/vitest.config.d.ts.map +0 -1
- package/out-tsc/spec/tsconfig.spec.tsbuildinfo +0 -1
- package/renovate.json +0 -3
- package/tsconfig.base.json +0 -25
- package/tsconfig.lib.json +0 -25
- package/tsconfig.node.json +0 -18
- package/tsconfig.spec.json +0 -29
- package/tsdown.config.ts +0 -13
- package/vitest.config.ts +0 -20
package/README.md
CHANGED
|
@@ -185,7 +185,9 @@ Deploy a pre-built _Tamaro Core_ or customer configuration bundle to AWS S3.
|
|
|
185
185
|
|
|
186
186
|
### Options
|
|
187
187
|
|
|
188
|
+
- `--dryrun` – Displays the operations that would be performed without actually running them
|
|
188
189
|
- `--tag <tag>` – Tag which should be used for the deployment of the bundle (default: "latest")
|
|
190
|
+
- `--skip-epms-sync-only-use-in-emergencies-or-you-will-be-fired` – Skip the EPMS sync entirely. Only use this in emergencies.
|
|
189
191
|
|
|
190
192
|
Make sure you have built the bundle before deploying it with this command, otherwise you may mistakenly deploy the
|
|
191
193
|
bundle from a previous build.
|
|
@@ -287,6 +289,15 @@ cd /path/to/tamaro-configurations/configs/example-02-typical-customisations
|
|
|
287
289
|
npx -y @raisenow/tamaro-cli undeploy-email-config
|
|
288
290
|
```
|
|
289
291
|
|
|
292
|
+
## `update-epms`
|
|
293
|
+
|
|
294
|
+
Sync the Tamaro configuration with EPMS (without deploying it).
|
|
295
|
+
|
|
296
|
+
## Options
|
|
297
|
+
|
|
298
|
+
- `--dryrun` – Displays the operations that would be performed without actually running them
|
|
299
|
+
- `--tag <tag>` – Tag which should be used for the deployment of the bundle (default: "latest")
|
|
300
|
+
|
|
290
301
|
## `archive`
|
|
291
302
|
|
|
292
303
|
Archive a customer configuration. This moves the configuration folder into the `_archived` folder.
|
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as
|
|
2
|
+
import { A as EPMS_AUTH_CLIENT_ID_STAGE, B as logSuccess, C as DEFAULT_PORT, D as EPMS_AUTH_BASE_URL_PROD, E as EPMS_API_BASE_URL_STAGE, F as runCommandSync, I as createTerminalLink, L as logCommand, M as HTTPS_KEY_FILE, N as halt, O as EPMS_AUTH_BASE_URL_STAGE, P as promptConfirmation, R as logDataTable, S as DEFAULT_HTTP_TIMEOUT, T as EPMS_API_BASE_URL_PROD, V as logTitle, _ as AWS_S3_BUCKET_TAMARO, a as getIfCoreFns, b as CACHE_DIR, c as getWidgetUuid, d as resolveBin, f as resolveCoreVersion, g as AWS_CLOUDFRONT_DISTRIBUTION_ID, h as AUTH_PORT, j as HTTPS_CRT_FILE, k as EPMS_AUTH_CLIENT_ID_PROD, l as resolveAccountUuidFromConfig, m as resolveOwn, n as assertEnvValid, o as getPaths, t as applyEnv, v as AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD, w as DEFAULT_TAG, x as CORE_CONFIG_NAME, y as AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE, z as logError } from "./env-JzPIlctP.js";
|
|
3
3
|
import { createCommand } from "commander";
|
|
4
|
-
import fs, { existsSync, mkdirSync, renameSync } from "node:fs";
|
|
4
|
+
import fs, { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { S3 } from "@aws-sdk/client-s3";
|
|
6
7
|
import { execaCommandSync } from "execa";
|
|
7
8
|
import prompts from "prompts";
|
|
8
9
|
import stripIndent from "strip-indent";
|
|
10
|
+
import columnify from "columnify";
|
|
9
11
|
import notifier from "node-notifier";
|
|
12
|
+
import { z } from "zod";
|
|
10
13
|
import { globSync } from "glob";
|
|
11
14
|
import Handlebars from "handlebars";
|
|
12
15
|
import helpers from "handlebars-helpers";
|
|
16
|
+
import { config } from "dotenv";
|
|
17
|
+
import * as yaml from "js-yaml";
|
|
18
|
+
import ky from "ky";
|
|
19
|
+
import { createServer } from "node:http";
|
|
20
|
+
import open from "open";
|
|
13
21
|
import { getPortPromise } from "portfinder";
|
|
14
22
|
//#region src/lib/aws.ts
|
|
15
23
|
const awsAuthenticate = (options) => {
|
|
@@ -83,6 +91,28 @@ const promptProfile = async () => {
|
|
|
83
91
|
}], { onCancel: () => halt() });
|
|
84
92
|
return profile;
|
|
85
93
|
};
|
|
94
|
+
const s3 = new S3({});
|
|
95
|
+
const getConfigDeployments = async (configName) => {
|
|
96
|
+
const widgetJs = (await s3.listObjectsV2({
|
|
97
|
+
Bucket: "tamaro.raisenow.com",
|
|
98
|
+
Prefix: `${configName}/`
|
|
99
|
+
})).Contents?.filter((file) => file.Key?.endsWith(`/index.html`)) ?? [];
|
|
100
|
+
const deployments = [];
|
|
101
|
+
for (const file of widgetJs) {
|
|
102
|
+
const key = file.Key ?? "";
|
|
103
|
+
const parts = key.split("/");
|
|
104
|
+
if (parts.length < 2 || !file.LastModified) continue;
|
|
105
|
+
const tag = parts.at(-2);
|
|
106
|
+
const date = file.LastModified.toISOString();
|
|
107
|
+
const fullUrl = `https://${AWS_S3_BUCKET_TAMARO}/${key}`;
|
|
108
|
+
deployments.push({
|
|
109
|
+
date,
|
|
110
|
+
fullUrl,
|
|
111
|
+
tag
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return deployments;
|
|
115
|
+
};
|
|
86
116
|
//#endregion
|
|
87
117
|
//#region src/lib/notifier.ts
|
|
88
118
|
const notify = (args) => {
|
|
@@ -98,7 +128,7 @@ const notify = (args) => {
|
|
|
98
128
|
//#region src/commands/archive.ts
|
|
99
129
|
const archive = (options) => {
|
|
100
130
|
const { ifCore } = getIfCoreFns();
|
|
101
|
-
assertIsNotCore$
|
|
131
|
+
assertIsNotCore$4(ifCore);
|
|
102
132
|
assertIsNotArchived();
|
|
103
133
|
const configName = getWidgetUuid();
|
|
104
134
|
const cwd = process.cwd();
|
|
@@ -124,7 +154,7 @@ const assertOptionsValid$9 = (options) => {
|
|
|
124
154
|
const { profile } = options;
|
|
125
155
|
if (profile) assertProfileValid(profile);
|
|
126
156
|
};
|
|
127
|
-
const assertIsNotCore$
|
|
157
|
+
const assertIsNotCore$4 = (ifCore) => {
|
|
128
158
|
if (ifCore()) halt("You cannot archive Tamaro Core.");
|
|
129
159
|
};
|
|
130
160
|
const assertIsNotArchived = () => {
|
|
@@ -324,6 +354,311 @@ const assertTagValid = (tag) => {
|
|
|
324
354
|
if (!regex.test(tag)) halt(`Flag "--tag" has forbidden format. Allowed format: ${regex.toString()}.`);
|
|
325
355
|
};
|
|
326
356
|
//#endregion
|
|
357
|
+
//#region src/lib/epms/auth/util.ts
|
|
358
|
+
const CachedTokenSchema = z.object({
|
|
359
|
+
expirationTime: z.number(),
|
|
360
|
+
token: z.string()
|
|
361
|
+
});
|
|
362
|
+
const AuthResponseSchema = z.object({
|
|
363
|
+
access_token: z.string(),
|
|
364
|
+
expires_in: z.number(),
|
|
365
|
+
token_type: z.string()
|
|
366
|
+
});
|
|
367
|
+
const getCacheFilePath = (baseUrl, clientId) => {
|
|
368
|
+
const cacheKey = Buffer.from(`${baseUrl}|${clientId}`).toString("base64url");
|
|
369
|
+
return path.join(CACHE_DIR, `token-${cacheKey}.json`);
|
|
370
|
+
};
|
|
371
|
+
const readCachedToken = (baseUrl, clientId) => {
|
|
372
|
+
try {
|
|
373
|
+
const filePath = getCacheFilePath(baseUrl, clientId);
|
|
374
|
+
if (!existsSync(filePath)) return;
|
|
375
|
+
const data = JSON.parse(readFileSync(filePath, "utf8"));
|
|
376
|
+
const parsed = CachedTokenSchema.safeParse(data);
|
|
377
|
+
if (parsed.success) return parsed.data;
|
|
378
|
+
} catch {}
|
|
379
|
+
};
|
|
380
|
+
const writeCachedToken = (baseUrl, clientId, cached) => {
|
|
381
|
+
try {
|
|
382
|
+
const filePath = getCacheFilePath(baseUrl, clientId);
|
|
383
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
384
|
+
writeFileSync(filePath, JSON.stringify(cached), { mode: 384 });
|
|
385
|
+
} catch {}
|
|
386
|
+
};
|
|
387
|
+
const removeCachedToken = (baseUrl, clientId) => {
|
|
388
|
+
try {
|
|
389
|
+
const filePath = getCacheFilePath(baseUrl, clientId);
|
|
390
|
+
if (existsSync(filePath)) writeFileSync(filePath, "", { flag: "w" });
|
|
391
|
+
} catch {}
|
|
392
|
+
};
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region src/lib/epms/auth/apiClient.ts
|
|
395
|
+
const createAuthorizer = (baseUrl, clientId, clientSecret) => {
|
|
396
|
+
const apiUnauthorized = ky.extend({
|
|
397
|
+
headers: { "Access-Control-Allow-Origin": "*" },
|
|
398
|
+
prefixUrl: baseUrl,
|
|
399
|
+
timeout: DEFAULT_HTTP_TIMEOUT
|
|
400
|
+
});
|
|
401
|
+
let expirationTime = void 0;
|
|
402
|
+
let token = void 0;
|
|
403
|
+
const cached = readCachedToken(baseUrl, clientId);
|
|
404
|
+
if (cached) {
|
|
405
|
+
token = cached.token;
|
|
406
|
+
expirationTime = cached.expirationTime;
|
|
407
|
+
}
|
|
408
|
+
const authorize = async () => {
|
|
409
|
+
const isExpired = (expirationTime ?? 0) - Date.now() < 300 * 1e3;
|
|
410
|
+
if (token && !isExpired) return token;
|
|
411
|
+
const data = await apiUnauthorized.post("oauth2/token", { json: {
|
|
412
|
+
client_id: clientId,
|
|
413
|
+
client_secret: clientSecret,
|
|
414
|
+
grant_type: "client_credentials"
|
|
415
|
+
} }).json();
|
|
416
|
+
token = `${data.token_type} ${data.access_token}`;
|
|
417
|
+
expirationTime = Date.now() + data.expires_in * 1e3;
|
|
418
|
+
writeCachedToken(baseUrl, clientId, {
|
|
419
|
+
expirationTime,
|
|
420
|
+
token
|
|
421
|
+
});
|
|
422
|
+
return token;
|
|
423
|
+
};
|
|
424
|
+
return apiUnauthorized.extend({ hooks: { beforeRequest: [async (request) => {
|
|
425
|
+
const authToken = await authorize();
|
|
426
|
+
request.headers.set("Authorization", authToken);
|
|
427
|
+
}] } });
|
|
428
|
+
};
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/lib/epms/auth/authCallback.html
|
|
431
|
+
var authCallback_default = "<!doctype html>\n<html>\n <head\n ><title>Authenticating</title></head\n >\n <body>\n <p>Authenticating</p>\n <script>\n const hash = window.location.hash.substring(1)\n const params = new URLSearchParams(hash)\n const data = {\n access_token: params.get('access_token'),\n token_type: params.get('token_type'),\n expires_in: parseInt(params.get('expires_in') || '0', 10),\n }\n fetch('/token', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify(data),\n })\n .then(() => {\n document.querySelector('p').textContent =\n 'Authentication successful! You can close this tab.'\n })\n .catch(() => {\n document.querySelector('p').textContent =\n 'Authentication failed. Please try again.'\n })\n <\/script>\n </body>\n</html>\n";
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region src/lib/epms/auth/browser.ts
|
|
434
|
+
/**
|
|
435
|
+
* The following authentication method is a browser-based OAuth flow.
|
|
436
|
+
* A very similar flow is used by tools like AWS CLI when running `aws sso login` or Claude Code.
|
|
437
|
+
*
|
|
438
|
+
* The flow works as follows:
|
|
439
|
+
* 1. The CLI starts a temporary local HTTP server that listens for the OAuth callback.
|
|
440
|
+
* 2. The CLI opens the user's default web browser and navigates to the EPMS authorization URL, passing the local server's callback URL as the redirect_uri.
|
|
441
|
+
* 3. The user authenticates in the browser, and the OAuth server redirects back to the local server with the authorization token.
|
|
442
|
+
* 4. The local server captures the token and resolves the promise, completing the authentication flow.
|
|
443
|
+
*
|
|
444
|
+
*
|
|
445
|
+
* We also implement a simple caching mechanism that stores the token in a file in the user's home directory.
|
|
446
|
+
* This allows us to reuse the token for subsequent requests until it expires, at which point we automatically trigger a new authentication flow.
|
|
447
|
+
*/
|
|
448
|
+
const doBrowserOAuthFlow = (authBaseUrl, clientId, port) => {
|
|
449
|
+
return new Promise((resolve, reject) => {
|
|
450
|
+
const server = createServer((req, res) => {
|
|
451
|
+
if (req.method === "POST" && req.url === "/token") {
|
|
452
|
+
let body = "";
|
|
453
|
+
req.on("data", (chunk) => {
|
|
454
|
+
body += chunk.toString();
|
|
455
|
+
});
|
|
456
|
+
req.on("end", () => {
|
|
457
|
+
try {
|
|
458
|
+
const data = AuthResponseSchema.parse(JSON.parse(body));
|
|
459
|
+
res.writeHead(200);
|
|
460
|
+
res.end();
|
|
461
|
+
server.close();
|
|
462
|
+
resolve(data);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
res.writeHead(400);
|
|
465
|
+
res.end();
|
|
466
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
} else {
|
|
470
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
471
|
+
res.end(authCallback_default);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
server.on("error", reject);
|
|
475
|
+
server.listen(port, () => {
|
|
476
|
+
const redirectUri = `http://localhost:${port}`;
|
|
477
|
+
const authUrl = `${authBaseUrl}/oauth2/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=token`;
|
|
478
|
+
console.log(`Opening browser for authentication: ${authUrl}`);
|
|
479
|
+
open(authUrl).catch((error) => {
|
|
480
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
};
|
|
485
|
+
const createBrowserAuthorizer = (authBaseUrl, clientId, epmsBaseUrl) => {
|
|
486
|
+
const apiBase = ky.extend({
|
|
487
|
+
prefixUrl: epmsBaseUrl,
|
|
488
|
+
timeout: DEFAULT_HTTP_TIMEOUT
|
|
489
|
+
});
|
|
490
|
+
let expirationTime = void 0;
|
|
491
|
+
let token = void 0;
|
|
492
|
+
const cacheBaseUrl = `browser|${authBaseUrl}`;
|
|
493
|
+
const cached = readCachedToken(cacheBaseUrl, clientId);
|
|
494
|
+
if (cached) {
|
|
495
|
+
token = cached.token;
|
|
496
|
+
expirationTime = cached.expirationTime;
|
|
497
|
+
}
|
|
498
|
+
const resetAuth = () => {
|
|
499
|
+
token = void 0;
|
|
500
|
+
expirationTime = void 0;
|
|
501
|
+
removeCachedToken(cacheBaseUrl, clientId);
|
|
502
|
+
};
|
|
503
|
+
const authorize = async () => {
|
|
504
|
+
const isExpired = (expirationTime ?? 0) - Date.now() < 300 * 1e3;
|
|
505
|
+
if (token && !isExpired) return token;
|
|
506
|
+
const data = await doBrowserOAuthFlow(authBaseUrl, clientId, AUTH_PORT);
|
|
507
|
+
token = `${data.token_type} ${data.access_token}`;
|
|
508
|
+
expirationTime = Date.now() + data.expires_in * 1e3;
|
|
509
|
+
writeCachedToken(cacheBaseUrl, clientId, {
|
|
510
|
+
expirationTime,
|
|
511
|
+
token
|
|
512
|
+
});
|
|
513
|
+
return token;
|
|
514
|
+
};
|
|
515
|
+
return apiBase.extend({ hooks: {
|
|
516
|
+
afterResponse: [async (request, options, response) => {
|
|
517
|
+
if (response.status === 401) {
|
|
518
|
+
resetAuth();
|
|
519
|
+
const newToken = await authorize();
|
|
520
|
+
const newRequest = new Request(request, { headers: new Headers(request.headers) });
|
|
521
|
+
newRequest.headers.set("Authorization", newToken);
|
|
522
|
+
return ky(newRequest, options);
|
|
523
|
+
}
|
|
524
|
+
return response;
|
|
525
|
+
}],
|
|
526
|
+
beforeRequest: [async (request) => {
|
|
527
|
+
const authToken = await authorize();
|
|
528
|
+
request.headers.set("Authorization", authToken);
|
|
529
|
+
}]
|
|
530
|
+
} });
|
|
531
|
+
};
|
|
532
|
+
//#endregion
|
|
533
|
+
//#region src/lib/epms/client.ts
|
|
534
|
+
var EpmsClient = class EpmsClient {
|
|
535
|
+
#epmsClient;
|
|
536
|
+
constructor(epmsClient) {
|
|
537
|
+
this.#epmsClient = epmsClient;
|
|
538
|
+
}
|
|
539
|
+
static fromClientCredentials(baseUrl, clientId, clientSecret) {
|
|
540
|
+
return new EpmsClient(createAuthorizer(baseUrl, clientId, clientSecret));
|
|
541
|
+
}
|
|
542
|
+
static fromBrowserAuth(epmsBaseUrl, authBaseUrl, clientId) {
|
|
543
|
+
return new EpmsClient(createBrowserAuthorizer(authBaseUrl, clientId, epmsBaseUrl));
|
|
544
|
+
}
|
|
545
|
+
async getOrganisationIdByAccountUuid(accountUuid) {
|
|
546
|
+
const query = {
|
|
547
|
+
from: 0,
|
|
548
|
+
query: { $and: [{ $term: { object_uuid: accountUuid } }, { $term: { object: "account" } }] },
|
|
549
|
+
size: 1
|
|
550
|
+
};
|
|
551
|
+
const response = await this.#epmsClient.post("search/events", { json: query }).json();
|
|
552
|
+
if (response.hits.length === 0) throw new Error(`No events found for account UUID: ${accountUuid}`);
|
|
553
|
+
const organisationId = response.hits[0].organisation_uuid;
|
|
554
|
+
if (!organisationId) throw new Error(`Organisation UUID not found for account UUID: ${accountUuid}`);
|
|
555
|
+
return organisationId;
|
|
556
|
+
}
|
|
557
|
+
async updateTamaro(configName, tag, json) {
|
|
558
|
+
return this.#epmsClient.put(`products/tamaro/${configName}/tags/${tag}`, { json }).json();
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
//#endregion
|
|
562
|
+
//#region src/commands/update-epms.ts
|
|
563
|
+
const updateEpms = async (options) => {
|
|
564
|
+
const { ifCore } = getIfCoreFns();
|
|
565
|
+
assertIsNotCore$3(ifCore);
|
|
566
|
+
if (!options.profile && !options.ci) {
|
|
567
|
+
assertProfilesPresent();
|
|
568
|
+
options.profile = await promptProfile();
|
|
569
|
+
}
|
|
570
|
+
if (options.profile) {
|
|
571
|
+
assertProfileValid(options.profile);
|
|
572
|
+
process.env.AWS_PROFILE = options.profile;
|
|
573
|
+
}
|
|
574
|
+
awsAuthenticate(options);
|
|
575
|
+
const paths = getPaths(ifCore);
|
|
576
|
+
const { dryrun, tag } = options;
|
|
577
|
+
const configName = getWidgetUuid();
|
|
578
|
+
loadEnv(paths);
|
|
579
|
+
const { accountUuidsProd, accountUuidsStage } = loadAccountUuidsFromConfig(paths);
|
|
580
|
+
if (dryrun) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
|
|
581
|
+
const configuredVersion = resolveCoreVersion();
|
|
582
|
+
if (!configuredVersion) {
|
|
583
|
+
logTitle("⚠️ Could not determine the configured Tamaro version. Please ensure that the CORE_VERSION environment variable is set or that the CORE_URL environment variable contains a valid Tamaro version.");
|
|
584
|
+
logTitle("This may be expected behaviour if you are using a custom build of Tamaro Core. If so, you can safely ignore this warning. ⚠️");
|
|
585
|
+
}
|
|
586
|
+
const baseInfo = {
|
|
587
|
+
configured_version: configuredVersion ?? "unknown",
|
|
588
|
+
last_deployment: Math.trunc(Date.now() / 1e3),
|
|
589
|
+
read_only: false
|
|
590
|
+
};
|
|
591
|
+
for (const accountUuidStage of accountUuidsStage) if (accountUuidStage) {
|
|
592
|
+
const epmsClient = epmsAuthenticate("stage");
|
|
593
|
+
const organisationUuid = await epmsClient.getOrganisationIdByAccountUuid(accountUuidStage);
|
|
594
|
+
const body = {
|
|
595
|
+
...baseInfo,
|
|
596
|
+
account_uuid: accountUuidStage,
|
|
597
|
+
organisation_uuid: organisationUuid
|
|
598
|
+
};
|
|
599
|
+
logTitle(`Updating EPMS Stage for tag "${tag}":`);
|
|
600
|
+
logDataTable({
|
|
601
|
+
name: configName,
|
|
602
|
+
tag,
|
|
603
|
+
...body
|
|
604
|
+
});
|
|
605
|
+
if (!dryrun) {
|
|
606
|
+
await epmsClient.updateTamaro(configName, tag, body);
|
|
607
|
+
logSuccess("✅ EPMS Stage is updated.");
|
|
608
|
+
} else logSuccess("⏭️ EPMS Stage would be updated (dry run).");
|
|
609
|
+
}
|
|
610
|
+
for (const accountUuidProd of accountUuidsProd) if (accountUuidProd) {
|
|
611
|
+
const epmsClient = epmsAuthenticate("prod");
|
|
612
|
+
const organisationUuid = await epmsClient.getOrganisationIdByAccountUuid(accountUuidProd);
|
|
613
|
+
logTitle(`Updating EPMS Prod for tag "${tag}":`);
|
|
614
|
+
const body = {
|
|
615
|
+
...baseInfo,
|
|
616
|
+
account_uuid: accountUuidProd,
|
|
617
|
+
organisation_uuid: organisationUuid
|
|
618
|
+
};
|
|
619
|
+
logDataTable({
|
|
620
|
+
name: configName,
|
|
621
|
+
tag,
|
|
622
|
+
...body
|
|
623
|
+
});
|
|
624
|
+
if (!dryrun) {
|
|
625
|
+
await epmsClient.updateTamaro(configName, tag, body);
|
|
626
|
+
logSuccess("✅ EPMS Prod is updated.");
|
|
627
|
+
} else logSuccess("⏭️ EPMS Prod would be updated (dry run).");
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
const epmsAuthenticate = (env) => {
|
|
631
|
+
if (env === "stage") {
|
|
632
|
+
if (process.env.EPMS_CLIENT_ID_STAGE && process.env.EPMS_CLIENT_SECRET_STAGE) return EpmsClient.fromClientCredentials(EPMS_API_BASE_URL_STAGE, process.env.EPMS_CLIENT_ID_STAGE, process.env.EPMS_CLIENT_SECRET_STAGE);
|
|
633
|
+
return EpmsClient.fromBrowserAuth(EPMS_API_BASE_URL_STAGE, EPMS_AUTH_BASE_URL_STAGE, EPMS_AUTH_CLIENT_ID_STAGE);
|
|
634
|
+
}
|
|
635
|
+
if (process.env.EPMS_CLIENT_ID && process.env.EPMS_CLIENT_SECRET) return EpmsClient.fromClientCredentials(EPMS_API_BASE_URL_PROD, process.env.EPMS_CLIENT_ID, process.env.EPMS_CLIENT_SECRET);
|
|
636
|
+
return EpmsClient.fromBrowserAuth(EPMS_API_BASE_URL_PROD, EPMS_AUTH_BASE_URL_PROD, EPMS_AUTH_CLIENT_ID_PROD);
|
|
637
|
+
};
|
|
638
|
+
const loadAccountUuidsFromConfig = (paths) => {
|
|
639
|
+
if (!("configYml" in paths)) {
|
|
640
|
+
halt("Fatal error, this should never happen");
|
|
641
|
+
throw new Error("Fatal error");
|
|
642
|
+
}
|
|
643
|
+
const configContent = yaml.load(readFileSync(paths.configYml, "utf8"));
|
|
644
|
+
const accountUuidsStage = resolveAccountUuidFromConfig(configContent, "epms_stage");
|
|
645
|
+
return {
|
|
646
|
+
accountUuidsProd: resolveAccountUuidFromConfig(configContent, "epms"),
|
|
647
|
+
accountUuidsStage
|
|
648
|
+
};
|
|
649
|
+
};
|
|
650
|
+
const loadEnv = (paths) => {
|
|
651
|
+
const filePath = globSync(paths.appEnv).find((file) => path.basename(file) === ".env");
|
|
652
|
+
if (!filePath) halt("No \".env\" file found. Are you in the folder of the Tamaro configuration?");
|
|
653
|
+
config({
|
|
654
|
+
path: filePath,
|
|
655
|
+
quiet: true
|
|
656
|
+
});
|
|
657
|
+
};
|
|
658
|
+
const assertIsNotCore$3 = (ifCore) => {
|
|
659
|
+
if (ifCore()) halt("You cannot update EPMS in the context of Tamaro Core.");
|
|
660
|
+
};
|
|
661
|
+
//#endregion
|
|
327
662
|
//#region src/commands/deploy.ts
|
|
328
663
|
const deploy = async (options) => {
|
|
329
664
|
const { ifCore } = getIfCoreFns();
|
|
@@ -395,6 +730,8 @@ const deploy = async (options) => {
|
|
|
395
730
|
halt(error.stderr);
|
|
396
731
|
}
|
|
397
732
|
}
|
|
733
|
+
if (options.skipEpmsSyncOnlyUseInEmergenciesOrYouWillBeFired) logTitle("⚠️ EPMS sync skipped. You better know what you are doing.");
|
|
734
|
+
else await updateEpms(options);
|
|
398
735
|
const demoPage = `https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/index.html`;
|
|
399
736
|
const entryPoint = `https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/${entryFilename}`;
|
|
400
737
|
logTitle(`\nBundle for "${configName}" is deployed with tag "${tag}".`);
|
|
@@ -543,26 +880,32 @@ const listDeployed = async (options) => {
|
|
|
543
880
|
}
|
|
544
881
|
assertOptionsValid$4(options);
|
|
545
882
|
awsAuthenticate(options);
|
|
546
|
-
const
|
|
547
|
-
const { config } = options;
|
|
883
|
+
const { config, profile } = options;
|
|
548
884
|
const { ifCore } = getIfCoreFns();
|
|
549
885
|
const configName = config ?? ifCore("tamaro-core", getWidgetUuid());
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
out = runCommandSync(cmd).stdout;
|
|
557
|
-
} catch (error) {
|
|
558
|
-
out = error.stdout;
|
|
559
|
-
if (error.stderr) halt(error.stderr);
|
|
886
|
+
logTitle(!config && ifCore() ? `Listing deployments of Tamaro Core …` : `Listing deployments of "${configName}" customer configuration …`);
|
|
887
|
+
if (profile) process.env.AWS_PROFILE = profile;
|
|
888
|
+
const deployments = await getConfigDeployments(configName);
|
|
889
|
+
if (deployments.length === 0) {
|
|
890
|
+
console.log("\nNo deployments found.\n");
|
|
891
|
+
return;
|
|
560
892
|
}
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
893
|
+
const table = columnify(deployments.map((deployment, idx) => ({
|
|
894
|
+
"#": `${idx + 1}`,
|
|
895
|
+
"Deployed At": deployment.date,
|
|
896
|
+
Tag: deployment.tag,
|
|
897
|
+
Preview: createTerminalLink("🔍 Live Preview", deployment.fullUrl)
|
|
898
|
+
})), {
|
|
899
|
+
columnSplitter: " | ",
|
|
900
|
+
config: {
|
|
901
|
+
"#": { minWidth: 2 },
|
|
902
|
+
"Deployed At": { minWidth: 20 },
|
|
903
|
+
Preview: { minWidth: 16 },
|
|
904
|
+
Tag: { minWidth: 14 }
|
|
905
|
+
},
|
|
906
|
+
minWidth: 10
|
|
907
|
+
});
|
|
908
|
+
console.log(`\n${table}\n`);
|
|
566
909
|
};
|
|
567
910
|
const assertOptionsValid$4 = (options) => {
|
|
568
911
|
const { config, profile } = options;
|
|
@@ -574,10 +917,6 @@ const assertConfigValid = (config) => {
|
|
|
574
917
|
const regex = /^[\w-]+$/;
|
|
575
918
|
if (!regex.test(config)) halt(`Flag "--config" has forbidden format. Allowed format: ${regex.toString()}.`);
|
|
576
919
|
};
|
|
577
|
-
const parseTags = (lines) => {
|
|
578
|
-
const regex = /^\s*PRE\s*/;
|
|
579
|
-
return lines.filter((line) => regex.test(line)).map((line) => line.replace(regex, "").replace(/\/$/, ""));
|
|
580
|
-
};
|
|
581
920
|
//#endregion
|
|
582
921
|
//#region src/commands/serve.ts
|
|
583
922
|
const serve = async (options) => {
|
|
@@ -669,7 +1008,7 @@ const undeploy = async (options) => {
|
|
|
669
1008
|
const deployUrl = all ? `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/` : `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/`;
|
|
670
1009
|
const description = all ? `all tags of "${configName}"` : `tag "${tag}" of "${configName}"`;
|
|
671
1010
|
if (dryRunFlag) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
|
|
672
|
-
await promptConfirmation(`Are you sure you want to undeploy ${description}?`, options.
|
|
1011
|
+
await promptConfirmation(`Are you sure you want to undeploy ${description}?`, options.ci);
|
|
673
1012
|
const cmd = `
|
|
674
1013
|
aws s3 rm ${deployUrl}
|
|
675
1014
|
--recursive
|
|
@@ -789,7 +1128,7 @@ const promptBucketTamaroEmailConfig = async () => {
|
|
|
789
1128
|
//#endregion
|
|
790
1129
|
//#region package.json
|
|
791
1130
|
var name = "@raisenow/tamaro-cli";
|
|
792
|
-
var version = "1.
|
|
1131
|
+
var version = "1.9.0-dev.1";
|
|
793
1132
|
//#endregion
|
|
794
1133
|
//#region src/cli.ts
|
|
795
1134
|
/**
|
|
@@ -807,7 +1146,7 @@ const cli = createCommand().name(name).version(version, "-v, --version");
|
|
|
807
1146
|
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) => {
|
|
808
1147
|
await dev(options);
|
|
809
1148
|
});
|
|
810
|
-
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) => {
|
|
1149
|
+
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("--skip-epms-sync-only-use-in-emergencies-or-you-will-be-fired", "Skip the EPMS sync entirely. Only use this in emergencies.", 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) => {
|
|
811
1150
|
await build(options);
|
|
812
1151
|
if (options.serve) await serve(options);
|
|
813
1152
|
if (options.deploy) await deploy(options);
|
|
@@ -815,7 +1154,7 @@ cli.command("build").description("Build an optimised (minified) bundle of Tamaro
|
|
|
815
1154
|
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) => {
|
|
816
1155
|
await serve(options);
|
|
817
1156
|
});
|
|
818
|
-
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) => {
|
|
1157
|
+
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).option("--skip-epms-sync-only-use-in-emergencies-or-you-will-be-fired", "Skip the EPMS sync entirely. Only use this in emergencies.", false).action(async (options) => {
|
|
819
1158
|
await deploy(options);
|
|
820
1159
|
});
|
|
821
1160
|
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) => {
|
|
@@ -836,6 +1175,9 @@ cli.command("archive").description("Archive a customer configuration by moving i
|
|
|
836
1175
|
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) => {
|
|
837
1176
|
unarchive(options);
|
|
838
1177
|
});
|
|
1178
|
+
cli.command("update-epms").description("Update EPMS with the currently deployed widget tags.").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--tag <tag>", "Tag which should be set for the deployment", DEFAULT_TAG).action(async (options) => {
|
|
1179
|
+
await updateEpms(options);
|
|
1180
|
+
});
|
|
839
1181
|
cli.command("validate").description("Validate the current Tamaro configuration to avoid common errors").action(async () => {
|
|
840
1182
|
await validate();
|
|
841
1183
|
});
|
|
@@ -6,8 +6,10 @@ import prompts from "prompts";
|
|
|
6
6
|
import stripIndent from "strip-indent";
|
|
7
7
|
import chalk from "chalk";
|
|
8
8
|
import columnify from "columnify";
|
|
9
|
+
import envPaths from "env-paths";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
11
|
import { getIfUtils } from "webpack-config-utils";
|
|
12
|
+
import { z } from "zod";
|
|
11
13
|
import { globSync } from "glob";
|
|
12
14
|
import { config } from "dotenv";
|
|
13
15
|
//#region src/lib/logging.ts
|
|
@@ -27,6 +29,9 @@ const logDataTable = (data) => {
|
|
|
27
29
|
console.log(columnify(data, { showHeaders: false }));
|
|
28
30
|
console.log("");
|
|
29
31
|
};
|
|
32
|
+
const createTerminalLink = (text, url) => {
|
|
33
|
+
return `\u001B]8;;${url}\u001B\\${text}\u001B]8;;\u001B\\`;
|
|
34
|
+
};
|
|
30
35
|
//#endregion
|
|
31
36
|
//#region src/lib/command.ts
|
|
32
37
|
const halt = (message) => {
|
|
@@ -50,6 +55,26 @@ const promptConfirmation = async (message, skipConfirmation) => {
|
|
|
50
55
|
if (!confirmed) halt("Operation cancelled.");
|
|
51
56
|
};
|
|
52
57
|
//#endregion
|
|
58
|
+
//#region src/lib/constants.ts
|
|
59
|
+
const DEFAULT_TAG = "latest";
|
|
60
|
+
const DEFAULT_PORT = 1234;
|
|
61
|
+
const DEFAULT_HTTP_TIMEOUT = 1e4;
|
|
62
|
+
const AWS_S3_BUCKET_TAMARO = "tamaro.raisenow.com";
|
|
63
|
+
const AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE = "rnw-stage-email-service";
|
|
64
|
+
const AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD = "rnw-email-service";
|
|
65
|
+
const EPMS_API_BASE_URL_STAGE = "https://api.stage.mesos.raisenow.net";
|
|
66
|
+
const EPMS_API_BASE_URL_PROD = "https://api.raisenow.io";
|
|
67
|
+
const EPMS_AUTH_BASE_URL_STAGE = "https://login.stage.mesos.raisenow.net";
|
|
68
|
+
const EPMS_AUTH_BASE_URL_PROD = "https://login.raisenow.com";
|
|
69
|
+
const EPMS_AUTH_CLIENT_ID_STAGE = "luna-stage-local";
|
|
70
|
+
const EPMS_AUTH_CLIENT_ID_PROD = "luna-local";
|
|
71
|
+
const CORE_CONFIG_NAME = "tamaro-core";
|
|
72
|
+
const AUTH_PORT = 8080;
|
|
73
|
+
const AWS_CLOUDFRONT_DISTRIBUTION_ID = "EHJ1OM458YQ0I";
|
|
74
|
+
const HTTPS_CRT_FILE = "localhost.crt";
|
|
75
|
+
const HTTPS_KEY_FILE = "localhost.key";
|
|
76
|
+
const CACHE_DIR = envPaths("tamaro-cli", { suffix: "" }).cache;
|
|
77
|
+
//#endregion
|
|
53
78
|
//#region node_modules/.pnpm/tsdown@0.21.8_synckit@0.11.11_typescript@6.0.2/node_modules/tsdown/esm-shims.js
|
|
54
79
|
const getFilename = () => fileURLToPath(import.meta.url);
|
|
55
80
|
const getDirname = () => path.dirname(getFilename());
|
|
@@ -145,6 +170,7 @@ const getPaths = (ifCore) => {
|
|
|
145
170
|
appTsConfig: resolveApp("tsconfig.json"),
|
|
146
171
|
appHtml: resolveApp("*.html"),
|
|
147
172
|
appEnv: resolveApp(".env*"),
|
|
173
|
+
configYml: resolveApp("config.yml"),
|
|
148
174
|
appTailwindConfig: void 0
|
|
149
175
|
});
|
|
150
176
|
};
|
|
@@ -171,17 +197,37 @@ const getIfCoreFns = ({ allowArchived = false } = {}) => {
|
|
|
171
197
|
`));
|
|
172
198
|
process.exit(1);
|
|
173
199
|
};
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
200
|
+
const resolveAccountUuidFromConfig = (rawConfig, field) => {
|
|
201
|
+
const epmsConfig = z.object({ [field]: z.record(z.string(), z.unknown()) }).safeParse(rawConfig);
|
|
202
|
+
if (!epmsConfig.success) return [];
|
|
203
|
+
const configContent = epmsConfig.data[field];
|
|
204
|
+
const accountUuid = configContent.account_mapping ?? configContent.account_uuid;
|
|
205
|
+
const accountUuidValidation = z.uuid().safeParse(accountUuid);
|
|
206
|
+
const accontUuidArrayValidation = z.array(z.uuid()).safeParse(accountUuid);
|
|
207
|
+
if (!accountUuidValidation.success && !accontUuidArrayValidation.success) halt(stripIndent(`Could not extract EPMS account UUID(s) from config.yml. Please ensure that the "${field}" field is correctly formatted. Expected formats:
|
|
208
|
+
1. ${field}:
|
|
209
|
+
account_uuid: <UUID>
|
|
210
|
+
|
|
211
|
+
Alternatively, if the account_uuid is conditional using "if" / "then" / "else" statements, it must be provided using the "account_mapping" format:
|
|
212
|
+
2. ${field}:
|
|
213
|
+
account_mapping:
|
|
214
|
+
- <UUID_1>
|
|
215
|
+
- <UUID_2>
|
|
216
|
+
- <UUID_N>
|
|
217
|
+
`));
|
|
218
|
+
if (accontUuidArrayValidation.success) return accontUuidArrayValidation.data;
|
|
219
|
+
if (accountUuidValidation.success) return [accountUuidValidation.data];
|
|
220
|
+
throw new Error("This should never happen");
|
|
221
|
+
};
|
|
222
|
+
const TAMARO_VERSION_IN_URL_REGEX = /tamaro-core\/(.*)\/index.js/;
|
|
223
|
+
const extractTamaroVersionFromUrl = (url) => {
|
|
224
|
+
const match = url.match(TAMARO_VERSION_IN_URL_REGEX);
|
|
225
|
+
if (match) return match[1].toString();
|
|
226
|
+
};
|
|
227
|
+
const resolveCoreVersion = () => {
|
|
228
|
+
if (process.env.CORE_VERSION) return process.env.CORE_VERSION;
|
|
229
|
+
if (process.env.CORE_URL) return extractTamaroVersionFromUrl(process.env.CORE_URL);
|
|
230
|
+
};
|
|
185
231
|
//#endregion
|
|
186
232
|
//#region src/lib/env.ts
|
|
187
233
|
const require = createRequire(import.meta.url);
|
|
@@ -297,4 +343,4 @@ const applyEnv = (env) => {
|
|
|
297
343
|
if (filePath) config({ path: filePath });
|
|
298
344
|
};
|
|
299
345
|
//#endregion
|
|
300
|
-
export {
|
|
346
|
+
export { EPMS_AUTH_CLIENT_ID_STAGE as A, logSuccess as B, DEFAULT_PORT as C, EPMS_AUTH_BASE_URL_PROD as D, EPMS_API_BASE_URL_STAGE as E, runCommandSync as F, createTerminalLink as I, logCommand as L, HTTPS_KEY_FILE as M, halt as N, EPMS_AUTH_BASE_URL_STAGE as O, promptConfirmation as P, logDataTable as R, DEFAULT_HTTP_TIMEOUT as S, EPMS_API_BASE_URL_PROD as T, logTitle as V, AWS_S3_BUCKET_TAMARO as _, getIfCoreFns as a, CACHE_DIR as b, getWidgetUuid as c, resolveBin as d, resolveCoreVersion as f, AWS_CLOUDFRONT_DISTRIBUTION_ID as g, AUTH_PORT as h, extensions as i, HTTPS_CRT_FILE as j, EPMS_AUTH_CLIENT_ID_PROD as k, resolveAccountUuidFromConfig as l, resolveOwn as m, assertEnvValid as n, getPaths as o, resolveEslintPluginConfig as p, getEnvVars as r, getRelativePaths as s, applyEnv as t, resolveApp as u, AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD as v, DEFAULT_TAG as w, CORE_CONFIG_NAME as x, AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE as y, logError as z };
|
package/dist/webpack.config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { M as HTTPS_KEY_FILE, R as logDataTable, V as logTitle, a as getIfCoreFns, i as extensions, j as HTTPS_CRT_FILE, o as getPaths, p as resolveEslintPluginConfig, r as getEnvVars, s as getRelativePaths, u as resolveApp } from "./env-JzPIlctP.js";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|