@raisenow/tamaro-cli 3.0.0-dev.4 → 3.1.0-dev.0

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/dist/cli.js CHANGED
@@ -14,7 +14,8 @@ import open from "open";
14
14
  import { globSync } from "glob";
15
15
  import Handlebars from "handlebars";
16
16
  import helpers from "handlebars-helpers";
17
- import { config } from "dotenv";
17
+ import { codeFrameColumns } from "@babel/code-frame";
18
+ import { config, parse } from "dotenv";
18
19
  import { getPortPromise } from "portfinder";
19
20
  import prompts from "prompts";
20
21
  import columnify from "columnify";
@@ -502,7 +503,7 @@ const assureOnlySupportedHelpers = (template) => {
502
503
  return usedHelpers;
503
504
  };
504
505
  const usedHelpers = extractHelpers(Handlebars.parse(template));
505
- const supportedHelpers = new Set([
506
+ const supportedHelpers = /* @__PURE__ */ new Set([
506
507
  "if",
507
508
  "unless",
508
509
  "each",
@@ -545,6 +546,103 @@ const compileEmailTemplate = (template) => {
545
546
  Handlebars.compile(template)({});
546
547
  };
547
548
  //#endregion
549
+ //#region src/lib/validators/validateEnv.ts
550
+ const envSchema = z.strictObject({
551
+ CORE_URL: z.url().optional(),
552
+ CORE_URL_PATTERN: z.string().includes("{{version}}", { message: "Must contain the \"{{version}}\" placeholder" }).optional(),
553
+ CORE_VERSION: z.string().refine((version) => version === version.trim(), { message: "There must be no leading or trailing whitespace in the version string" }).refine((version) => version === version.toLowerCase(), { message: "The version string must be lowercase" }).optional(),
554
+ DISABLE_URL_VERSION_OVERRIDE: z.enum([
555
+ "true",
556
+ "false",
557
+ "1",
558
+ "0"
559
+ ]).optional(),
560
+ LOCAL_CORE: z.enum(["true", "false"]).optional()
561
+ });
562
+ const TAMARO_CORE_REGISTRY_URL = "https://registry.npmjs.org/@raisenow/tamaro-core";
563
+ /**
564
+ * Check that the configured version exists for "@raisenow/tamaro-core" on npm:
565
+ * as a dist-tag (e.g. "latest", "lts_2026"), an exact version, or a partial
566
+ * version ("2", "2.15") matching at least one published version.
567
+ *
568
+ * Returns undefined when the registry is unreachable, so offline builds are
569
+ * not blocked by this check.
570
+ */
571
+ const coreVersionExists = async (version) => {
572
+ try {
573
+ const response = await fetch(TAMARO_CORE_REGISTRY_URL, { headers: { accept: "application/vnd.npm.install-v1+json" } });
574
+ if (!response.ok) return;
575
+ const metadata = await response.json();
576
+ const distTags = Object.keys(metadata["dist-tags"] ?? {});
577
+ const versions = Object.keys(metadata.versions ?? {});
578
+ return distTags.includes(version) || versions.includes(version) || versions.some((existing) => existing.startsWith(`${version}.`));
579
+ } catch {
580
+ return;
581
+ }
582
+ };
583
+ /**
584
+ * Locate the line of a `KEY=` assignment in the .env source, so the error can
585
+ * be rendered as a code frame pointing at the value.
586
+ */
587
+ const findKeyLocation = (source, key) => {
588
+ const lines = source.split("\n");
589
+ const assignment = new RegExp(String.raw`^\s*${key}\s*=`);
590
+ const index = lines.findIndex((line) => assignment.test(line));
591
+ if (index === -1) return;
592
+ const line = index + 1;
593
+ const valueStart = lines[index].indexOf("=") + 1;
594
+ return {
595
+ end: {
596
+ column: lines[index].length,
597
+ line
598
+ },
599
+ start: {
600
+ column: valueStart,
601
+ line
602
+ }
603
+ };
604
+ };
605
+ /**
606
+ * Format an error for a `KEY=` assignment as a code frame pointing at the
607
+ * value, so it looks fancy like this:
608
+ *
609
+ * .env:2
610
+ * 1 | CORE_VERSION=LTS_2026
611
+ * 2 | CORE_URL_PATTERN=adsf
612
+ * | ^^^^ Must contain the "{{version}}" placeholder
613
+ */
614
+ const formatError = (dotEnvPath, source, key, message) => {
615
+ const location = findKeyLocation(source, key);
616
+ if (!location) return `${dotEnvPath}: ${key}: ${message}`;
617
+ const frame = codeFrameColumns(source, location, {
618
+ forceColor: true,
619
+ highlightCode: true,
620
+ message
621
+ });
622
+ return `\n${dotEnvPath}:${location.start.line}\n${frame}`;
623
+ };
624
+ /**
625
+ * Validate the environment variables in the config's .env file.
626
+ */
627
+ const validateEnv = async (dotEnvPath) => {
628
+ if (!fs.existsSync(dotEnvPath)) return [];
629
+ const source = fs.readFileSync(dotEnvPath, "utf8");
630
+ const parsed = parse(source);
631
+ const result = envSchema.safeParse(parsed);
632
+ if (!result.success) {
633
+ const errors = [];
634
+ for (const issue of result.error.issues) {
635
+ const keys = issue.code === "unrecognized_keys" ? issue.keys : [String(issue.path[0] ?? "")];
636
+ for (const key of keys) errors.push(formatError(dotEnvPath, source, key, issue.message));
637
+ }
638
+ return errors;
639
+ }
640
+ if (parsed.CORE_VERSION) {
641
+ if (await coreVersionExists(parsed.CORE_VERSION) === false) return [formatError(dotEnvPath, source, "CORE_VERSION", `Version "${parsed.CORE_VERSION}" does not exist. See all versions at https://www.npmjs.com/package/@raisenow/tamaro-core?activeTab=versions`)];
642
+ }
643
+ return [];
644
+ };
645
+ //#endregion
548
646
  //#region src/commands/validate.ts
549
647
  /**
550
648
  * Validates the current Tamaro configuration to avoid common errors.
@@ -554,8 +652,12 @@ const compileEmailTemplate = (template) => {
554
652
  * deployed.
555
653
  */
556
654
  const validate = async () => {
557
- const errors = validateEmailTemplates(globSync("email-config/templates/**/*.hbs").toSorted());
558
- if (errors.length > 0) halt(errors.map((el) => el.toString()).join("\n"));
655
+ const errors = [...await validateEnv(".env"), ...validateEmailTemplates(globSync("email-config/templates/**/*.hbs").toSorted())];
656
+ if (errors.length > 0) {
657
+ logError("❌ Validation failed with the following errors:");
658
+ console.log(errors.map((el) => el.toString()).join("\n"));
659
+ halt();
660
+ }
559
661
  logTitle("Validated successfully");
560
662
  };
561
663
  //#endregion
@@ -631,6 +733,7 @@ const getOutDir = (configName) => {
631
733
  //#region src/commands/update-epms.ts
632
734
  const updateEpms = async (options) => {
633
735
  const configName = getConfigName();
736
+ await validate();
634
737
  if (!options.profile && !options.ci) {
635
738
  assertAwsProfilesPresent();
636
739
  options.profile = await promptAwsProfile();
@@ -912,6 +1015,7 @@ const dev = async (options) => {
912
1015
  const configName = getConfigName();
913
1016
  const configsPackageRoot = getConfigsPackageRoot();
914
1017
  assertCrtExists(configsPackageRoot);
1018
+ await validate();
915
1019
  const port = await getPortPromise({ port: Number(DEFAULT_PORT) });
916
1020
  logTitle(`\nStarting Vite dev server for "${configName}" configuration...`);
917
1021
  runCommandSync(`pnpm vite dev --port ${port}`, {
@@ -1225,7 +1329,7 @@ const promptBucketTamaroEmailConfig = async () => {
1225
1329
  //#endregion
1226
1330
  //#region package.json
1227
1331
  var name = "@raisenow/tamaro-cli";
1228
- var version = "3.0.0-dev.4";
1332
+ var version = "3.1.0-dev.0";
1229
1333
  //#endregion
1230
1334
  //#region src/cli.ts
1231
1335
  const cli = createCommand().name(name).description("CLI for Tamaro Customer Configurations development").version(version, "-v, --version");
package/package.json CHANGED
@@ -1,20 +1,21 @@
1
1
  {
2
2
  "name": "@raisenow/tamaro-cli",
3
- "version": "3.0.0-dev.4",
3
+ "version": "3.1.0-dev.0",
4
4
  "author": {
5
5
  "name": "RaiseNow",
6
6
  "email": "development@raisenow.com"
7
7
  },
8
8
  "type": "module",
9
9
  "engines": {
10
- "node": ">=24.16.0",
11
- "pnpm": ">=11.7.0",
10
+ "node": ">=24.18.0",
11
+ "pnpm": ">=11.10.0",
12
12
  "npm": "please-use-pnpm",
13
13
  "yarn": "please-use-pnpm"
14
14
  },
15
15
  "dependencies": {
16
- "@aws-sdk/client-s3": "^3.1070.0",
17
- "@rnw-npm/cli-helpers": "^2.0.0",
16
+ "@aws-sdk/client-s3": "^3.1081.0",
17
+ "@babel/code-frame": "^8.0.0",
18
+ "@rnw-npm/cli-helpers": "^2.0.1",
18
19
  "columnify": "^1.6.0",
19
20
  "commander": "^15.0.0",
20
21
  "dedent": "^1.7.2",
@@ -24,7 +25,7 @@
24
25
  "glob": "^13.0.6",
25
26
  "handlebars": "^4.7.9",
26
27
  "handlebars-helpers": "^0.10.0",
27
- "js-yaml": "^4.2.0",
28
+ "js-yaml": "^4.3.0",
28
29
  "ky": "^1.14.3",
29
30
  "open": "^11.0.0",
30
31
  "portfinder": "^1.0.38",
@@ -32,20 +33,20 @@
32
33
  "zod": "^4.4.3"
33
34
  },
34
35
  "devDependencies": {
35
- "@rnw-npm/eslint-config": "^2.0.0",
36
- "@rnw-npm/prettier-config": "^1.0.0",
36
+ "@rnw-npm/eslint-config": "^2.0.1",
37
+ "@rnw-npm/prettier-config": "^1.0.1",
37
38
  "@types/columnify": "^1.5.4",
38
39
  "@types/handlebars-helpers": "^0.5.6",
39
40
  "@types/js-yaml": "^4.0.9",
40
41
  "@types/node": "^24.13.2",
41
42
  "@types/prompts": "^2.4.9",
42
- "eslint": "^10.5.0",
43
+ "eslint": "^10.6.0",
43
44
  "husky": "^9.1.7",
44
- "lint-staged": "^17.0.7",
45
- "prettier": "^3.8.4",
46
- "tsdown": "^0.22.2",
45
+ "lint-staged": "^17.0.8",
46
+ "prettier": "^3.9.4",
47
+ "tsdown": "^0.22.3",
47
48
  "typescript": "^6.0.3",
48
- "vitest": "^4.1.9"
49
+ "vitest": "^4.1.10"
49
50
  },
50
51
  "prettier": "@rnw-npm/prettier-config",
51
52
  "lint-staged": {
@@ -1,6 +0,0 @@
1
- <component name="InspectionProjectProfileManager">
2
- <profile version="1.0">
3
- <option name="myName" value="Project Default" />
4
- <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
5
- </profile>
6
- </component>
package/.idea/modules.xml DELETED
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectModuleManager">
4
- <modules>
5
- <module fileurl="file://$PROJECT_DIR$/.idea/tamaro-cli.iml" filepath="$PROJECT_DIR$/.idea/tamaro-cli.iml" />
6
- </modules>
7
- </component>
8
- </project>
@@ -1,12 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="WEB_MODULE" version="4">
3
- <component name="NewModuleRootManager">
4
- <content url="file://$MODULE_DIR$">
5
- <excludeFolder url="file://$MODULE_DIR$/temp" />
6
- <excludeFolder url="file://$MODULE_DIR$/.tmp" />
7
- <excludeFolder url="file://$MODULE_DIR$/tmp" />
8
- </content>
9
- <orderEntry type="inheritedJdk" />
10
- <orderEntry type="sourceFolder" forTests="false" />
11
- </component>
12
- </module>
package/.idea/vcs.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
- </component>
6
- </project>
@@ -1,101 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ChangeListManager">
4
- <list default="true" id="03cc3f97-55ca-4737-9fe2-779ac8457ab2" name="Changes" comment="">
5
- <change afterPath="$PROJECT_DIR$/src/commands/format.ts" afterDir="false" />
6
- <change afterPath="$PROJECT_DIR$/src/commands/lint.ts" afterDir="false" />
7
- <change afterPath="$PROJECT_DIR$/src/commands/preview.ts" afterDir="false" />
8
- <change afterPath="$PROJECT_DIR$/src/commands/typecheck.ts" afterDir="false" />
9
- <change afterPath="$PROJECT_DIR$/src/lib/paths.ts" afterDir="false" />
10
- <change beforePath="$PROJECT_DIR$/.nvmrc" beforeDir="false" afterPath="$PROJECT_DIR$/.nvmrc" afterDir="false" />
11
- <change beforePath="$PROJECT_DIR$/CHANGELOG.md" beforeDir="false" afterPath="$PROJECT_DIR$/CHANGELOG.md" afterDir="false" />
12
- <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
13
- <change beforePath="$PROJECT_DIR$/bitbucket-pipelines.yml" beforeDir="false" afterPath="$PROJECT_DIR$/bitbucket-pipelines.yml" afterDir="false" />
14
- <change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
15
- <change beforePath="$PROJECT_DIR$/pnpm-lock.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pnpm-lock.yaml" afterDir="false" />
16
- <change beforePath="$PROJECT_DIR$/pnpm-workspace.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pnpm-workspace.yaml" afterDir="false" />
17
- <change beforePath="$PROJECT_DIR$/src/cli.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/cli.ts" afterDir="false" />
18
- <change beforePath="$PROJECT_DIR$/src/commands/archive.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/archive.ts" afterDir="false" />
19
- <change beforePath="$PROJECT_DIR$/src/commands/build.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/build.ts" afterDir="false" />
20
- <change beforePath="$PROJECT_DIR$/src/commands/deploy-email-config.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/deploy-email-config.ts" afterDir="false" />
21
- <change beforePath="$PROJECT_DIR$/src/commands/deploy.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/deploy.ts" afterDir="false" />
22
- <change beforePath="$PROJECT_DIR$/src/commands/dev.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/dev.ts" afterDir="false" />
23
- <change beforePath="$PROJECT_DIR$/src/commands/list-deployed.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/list-deployed.ts" afterDir="false" />
24
- <change beforePath="$PROJECT_DIR$/src/commands/serve.ts" beforeDir="false" />
25
- <change beforePath="$PROJECT_DIR$/src/commands/unarchive.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/unarchive.ts" afterDir="false" />
26
- <change beforePath="$PROJECT_DIR$/src/commands/undeploy-email-config.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/undeploy-email-config.ts" afterDir="false" />
27
- <change beforePath="$PROJECT_DIR$/src/commands/undeploy.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/undeploy.ts" afterDir="false" />
28
- <change beforePath="$PROJECT_DIR$/src/commands/update-epms.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/update-epms.ts" afterDir="false" />
29
- <change beforePath="$PROJECT_DIR$/src/commands/validate.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/commands/validate.ts" afterDir="false" />
30
- <change beforePath="$PROJECT_DIR$/src/lib/InterpolateHtmlPlugin.ts" beforeDir="false" />
31
- <change beforePath="$PROJECT_DIR$/src/lib/aws.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/aws.ts" afterDir="false" />
32
- <change beforePath="$PROJECT_DIR$/src/lib/command.ts" beforeDir="false" />
33
- <change beforePath="$PROJECT_DIR$/src/lib/constants.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/constants.ts" afterDir="false" />
34
- <change beforePath="$PROJECT_DIR$/src/lib/env.ts" beforeDir="false" />
35
- <change beforePath="$PROJECT_DIR$/src/lib/epms/auth/browser.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/epms/auth/browser.ts" afterDir="false" />
36
- <change beforePath="$PROJECT_DIR$/src/lib/epms/auth/util.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/epms/auth/util.ts" afterDir="false" />
37
- <change beforePath="$PROJECT_DIR$/src/lib/epms/helpers.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/epms/helpers.ts" afterDir="false" />
38
- <change beforePath="$PROJECT_DIR$/src/lib/https.ts" beforeDir="false" />
39
- <change beforePath="$PROJECT_DIR$/src/lib/logging.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/logging.ts" afterDir="false" />
40
- <change beforePath="$PROJECT_DIR$/src/lib/notifier.ts" beforeDir="false" />
41
- <change beforePath="$PROJECT_DIR$/src/lib/resolve.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/resolve.ts" afterDir="false" />
42
- <change beforePath="$PROJECT_DIR$/src/lib/validators/assertTagValid.ts" beforeDir="false" />
43
- <change beforePath="$PROJECT_DIR$/src/webpack.config.ts" beforeDir="false" />
44
- <change beforePath="$PROJECT_DIR$/tsconfig.base.json" beforeDir="false" afterPath="$PROJECT_DIR$/tsconfig.base.json" afterDir="false" />
45
- <change beforePath="$PROJECT_DIR$/tsdown.config.ts" beforeDir="false" afterPath="$PROJECT_DIR$/tsdown.config.ts" afterDir="false" />
46
- </list>
47
- <option name="SHOW_DIALOG" value="false" />
48
- <option name="HIGHLIGHT_CONFLICTS" value="true" />
49
- <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
50
- <option name="LAST_RESOLUTION" value="IGNORE" />
51
- </component>
52
- <component name="Git.Settings">
53
- <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
54
- </component>
55
- <component name="MarkdownSettingsMigration">
56
- <option name="stateVersion" value="1" />
57
- </component>
58
- <component name="ProjectId" id="3BwvHZOW2N4A0f0JXo7q8qDzYwY" />
59
- <component name="ProjectViewState">
60
- <option name="autoscrollFromSource" value="true" />
61
- <option name="autoscrollToSource" value="true" />
62
- <option name="hideEmptyMiddlePackages" value="true" />
63
- <option name="showLibraryContents" value="true" />
64
- <option name="showMembers" value="true" />
65
- </component>
66
- <component name="PropertiesComponent">{
67
- &quot;keyToString&quot;: {
68
- &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
69
- &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
70
- &quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
71
- &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
72
- &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
73
- &quot;nodejs_package_manager_path&quot;: &quot;pnpm&quot;,
74
- &quot;prettierjs.PrettierConfiguration.Package&quot;: &quot;/Users/sigerello/dev/_raisenow/tools/tamaro-cli/node_modules/prettier&quot;,
75
- &quot;ts.external.directory.path&quot;: &quot;/Users/sigerello/dev/_raisenow/tools/tamaro-cli/node_modules/typescript/lib&quot;,
76
- &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
77
- }
78
- }</component>
79
- <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
80
- <component name="TaskManager">
81
- <task active="true" id="Default" summary="Default task">
82
- <changelist id="03cc3f97-55ca-4737-9fe2-779ac8457ab2" name="Changes" comment="" />
83
- <created>1775412859839</created>
84
- <option name="number" value="Default" />
85
- <option name="presentableId" value="Default" />
86
- <updated>1775412859839</updated>
87
- <workItem from="1775412861312" duration="2060000" />
88
- <workItem from="1776174639969" duration="1525000" />
89
- <workItem from="1776736351878" duration="598000" />
90
- <workItem from="1777934504965" duration="82000" />
91
- <workItem from="1777936008103" duration="88000" />
92
- <workItem from="1778477038620" duration="2530000" />
93
- <workItem from="1780398242165" duration="1962000" />
94
- <workItem from="1781661280180" duration="949000" />
95
- </task>
96
- <servers />
97
- </component>
98
- <component name="TypeScriptGeneratedFilesManager">
99
- <option name="version" value="3" />
100
- </component>
101
- </project>