ultraenv 1.0.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/LICENSE +21 -0
- package/README.md +2058 -0
- package/bin/ultraenv.mjs +3 -0
- package/dist/chunk-2USZPWLZ.js +288 -0
- package/dist/chunk-3UV2QNJL.js +270 -0
- package/dist/chunk-3VYXPTYV.js +179 -0
- package/dist/chunk-4XUYMRK5.js +366 -0
- package/dist/chunk-5G2DU52U.js +189 -0
- package/dist/chunk-6KS56D6E.js +172 -0
- package/dist/chunk-AWN6ADV7.js +328 -0
- package/dist/chunk-CHVO6NWI.js +203 -0
- package/dist/chunk-CIFMBJ4H.js +3975 -0
- package/dist/chunk-GC7RXHLA.js +253 -0
- package/dist/chunk-HFXQGJY3.js +445 -0
- package/dist/chunk-IGFVP24Q.js +91 -0
- package/dist/chunk-IKPTKALB.js +78 -0
- package/dist/chunk-JB7RKV3C.js +66 -0
- package/dist/chunk-MNVFG7H4.js +611 -0
- package/dist/chunk-MSXMESFP.js +1910 -0
- package/dist/chunk-N5PAV4NM.js +127 -0
- package/dist/chunk-NBOABPHM.js +158 -0
- package/dist/chunk-OMAOROL4.js +49 -0
- package/dist/chunk-R7PZRSZ7.js +105 -0
- package/dist/chunk-TE7HPLA6.js +73 -0
- package/dist/chunk-TMT5KCO3.js +101 -0
- package/dist/chunk-UEWYFN6A.js +189 -0
- package/dist/chunk-WMHN5RW2.js +128 -0
- package/dist/chunk-XC65ORJ5.js +70 -0
- package/dist/chunk-YMMP4VQL.js +118 -0
- package/dist/chunk-YN2KGTCB.js +33 -0
- package/dist/chunk-YTICOB5M.js +65 -0
- package/dist/chunk-YVWLXFUT.js +107 -0
- package/dist/ci-check-sync-VBMSVWIV.js +48 -0
- package/dist/ci-scan-24MT5XGS.js +41 -0
- package/dist/ci-setup-C2NKEFRD.js +135 -0
- package/dist/ci-validate-7AW24LSQ.js +57 -0
- package/dist/cli/index.cjs +9217 -0
- package/dist/cli/index.d.cts +9 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +339 -0
- package/dist/comparator-RDKX3OI7.js +13 -0
- package/dist/completion-MW35C2XO.js +168 -0
- package/dist/config-O5YRQP5Z.js +13 -0
- package/dist/debug-PTPXAF3K.js +131 -0
- package/dist/declaration-LEME4AFZ.js +10 -0
- package/dist/doctor-FZAUPKHS.js +129 -0
- package/dist/envs-compare-5K3HESX5.js +49 -0
- package/dist/envs-create-2XXHXMGA.js +58 -0
- package/dist/envs-list-NQM5252B.js +59 -0
- package/dist/envs-switch-6L2AQYID.js +50 -0
- package/dist/envs-validate-FL73Q76T.js +89 -0
- package/dist/fs-VH7ATUS3.js +31 -0
- package/dist/generator-LFZBMZZS.js +14 -0
- package/dist/git-BZS4DPAI.js +30 -0
- package/dist/help-3XJBXEHE.js +121 -0
- package/dist/index.cjs +12907 -0
- package/dist/index.d.cts +2562 -0
- package/dist/index.d.ts +2562 -0
- package/dist/index.js +3212 -0
- package/dist/init-Y7JQ2KYJ.js +146 -0
- package/dist/install-hook-SKXIV6NV.js +111 -0
- package/dist/json-schema-I26YNQBH.js +10 -0
- package/dist/key-manager-O3G55WPU.js +25 -0
- package/dist/middleware/express.cjs +103 -0
- package/dist/middleware/express.d.cts +115 -0
- package/dist/middleware/express.d.ts +115 -0
- package/dist/middleware/express.js +8 -0
- package/dist/middleware/fastify.cjs +91 -0
- package/dist/middleware/fastify.d.cts +111 -0
- package/dist/middleware/fastify.d.ts +111 -0
- package/dist/middleware/fastify.js +8 -0
- package/dist/module-IDIZPP4M.js +10 -0
- package/dist/protect-NCWPM6VC.js +161 -0
- package/dist/scan-TRLY36TT.js +58 -0
- package/dist/schema/index.cjs +4074 -0
- package/dist/schema/index.d.cts +1244 -0
- package/dist/schema/index.d.ts +1244 -0
- package/dist/schema/index.js +152 -0
- package/dist/sync-TMHMTLH2.js +186 -0
- package/dist/typegen-SQOSXBWM.js +80 -0
- package/dist/validate-IOAM5HWS.js +100 -0
- package/dist/vault-decrypt-U6HJZNBV.js +111 -0
- package/dist/vault-diff-B3ZOQTWI.js +132 -0
- package/dist/vault-encrypt-GUSLCSKS.js +112 -0
- package/dist/vault-init-GUBOTOUL.js +106 -0
- package/dist/vault-rekey-DAHT7JCN.js +132 -0
- package/dist/vault-status-GDLRU2OK.js +90 -0
- package/dist/vault-verify-CD76FJSF.js +102 -0
- package/package.json +106 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseEnvFile
|
|
3
|
+
} from "./chunk-HFXQGJY3.js";
|
|
4
|
+
import {
|
|
5
|
+
readFile
|
|
6
|
+
} from "./chunk-3VYXPTYV.js";
|
|
7
|
+
|
|
8
|
+
// src/sync/comparator.ts
|
|
9
|
+
function parseToVars(content) {
|
|
10
|
+
const parsed = parseEnvFile(content);
|
|
11
|
+
const vars = {};
|
|
12
|
+
const keys = [];
|
|
13
|
+
for (const envVar of parsed.vars) {
|
|
14
|
+
if (!(envVar.key in vars)) {
|
|
15
|
+
vars[envVar.key] = envVar.value;
|
|
16
|
+
keys.push(envVar.key);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { vars, keys };
|
|
20
|
+
}
|
|
21
|
+
async function compareSync(envPath, examplePath) {
|
|
22
|
+
const envContent = await readFile(envPath);
|
|
23
|
+
const exampleContent = await readFile(examplePath);
|
|
24
|
+
const envParsed = parseToVars(envContent);
|
|
25
|
+
const exampleParsed = parseToVars(exampleContent);
|
|
26
|
+
return compareValues(envParsed.vars, exampleParsed.vars);
|
|
27
|
+
}
|
|
28
|
+
function compareValues(envVars, exampleVars) {
|
|
29
|
+
const envKeys = new Set(Object.keys(envVars));
|
|
30
|
+
const exampleKeys = new Set(Object.keys(exampleVars));
|
|
31
|
+
const missing = [];
|
|
32
|
+
const extra = [];
|
|
33
|
+
const different = [];
|
|
34
|
+
const same = [];
|
|
35
|
+
for (const key of exampleKeys) {
|
|
36
|
+
if (!envKeys.has(key)) {
|
|
37
|
+
missing.push(key);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const key of envKeys) {
|
|
41
|
+
if (!exampleKeys.has(key)) {
|
|
42
|
+
extra.push(key);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const key of envKeys) {
|
|
46
|
+
if (!exampleKeys.has(key)) continue;
|
|
47
|
+
const envValue = envVars[key];
|
|
48
|
+
const exampleValue = exampleVars[key];
|
|
49
|
+
if (envValue === exampleValue) {
|
|
50
|
+
same.push(key);
|
|
51
|
+
} else {
|
|
52
|
+
different.push(key);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
missing.sort();
|
|
56
|
+
extra.sort();
|
|
57
|
+
different.sort();
|
|
58
|
+
same.sort();
|
|
59
|
+
const inSync = missing.length === 0 && extra.length === 0;
|
|
60
|
+
return {
|
|
61
|
+
inSync,
|
|
62
|
+
missing,
|
|
63
|
+
extra,
|
|
64
|
+
different,
|
|
65
|
+
same
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function formatSyncDiff(diff) {
|
|
69
|
+
const lines = [];
|
|
70
|
+
if (diff.inSync) {
|
|
71
|
+
lines.push("\u2713 .env is in sync with .env.example");
|
|
72
|
+
} else {
|
|
73
|
+
lines.push("\u2717 .env is out of sync with .env.example");
|
|
74
|
+
}
|
|
75
|
+
if (diff.missing.length > 0) {
|
|
76
|
+
lines.push("");
|
|
77
|
+
lines.push(`Missing (${diff.missing.length}):`);
|
|
78
|
+
for (const key of diff.missing) {
|
|
79
|
+
lines.push(` - ${key}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (diff.extra.length > 0) {
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push(`Extra (${diff.extra.length}):`);
|
|
85
|
+
for (const key of diff.extra) {
|
|
86
|
+
lines.push(` + ${key}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (diff.different.length > 0) {
|
|
90
|
+
lines.push("");
|
|
91
|
+
lines.push(`Different (${diff.different.length}):`);
|
|
92
|
+
for (const key of diff.different) {
|
|
93
|
+
lines.push(` ~ ${key}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (diff.same.length > 0 && diff.inSync) {
|
|
97
|
+
lines.push("");
|
|
98
|
+
lines.push(`${diff.same.length} variable(s) matched.`);
|
|
99
|
+
}
|
|
100
|
+
return lines.join("\n");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
compareSync,
|
|
105
|
+
compareValues,
|
|
106
|
+
formatSyncDiff
|
|
107
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareSync
|
|
3
|
+
} from "./chunk-YVWLXFUT.js";
|
|
4
|
+
import "./chunk-HFXQGJY3.js";
|
|
5
|
+
import {
|
|
6
|
+
writeJson
|
|
7
|
+
} from "./chunk-YN2KGTCB.js";
|
|
8
|
+
import {
|
|
9
|
+
exists
|
|
10
|
+
} from "./chunk-3VYXPTYV.js";
|
|
11
|
+
import "./chunk-5G2DU52U.js";
|
|
12
|
+
|
|
13
|
+
// src/cli/commands/ci-check-sync.ts
|
|
14
|
+
import { resolve, join } from "path";
|
|
15
|
+
async function run(args, ctx) {
|
|
16
|
+
try {
|
|
17
|
+
const cwd = args.flags["--cwd"] ?? ctx.cwd;
|
|
18
|
+
const baseDir = resolve(cwd);
|
|
19
|
+
const envPath = args.flags["--file"] ?? join(baseDir, ".env");
|
|
20
|
+
const examplePath = args.flags["--example"] ?? join(baseDir, ".env.example");
|
|
21
|
+
const envExists = await exists(envPath);
|
|
22
|
+
const exampleExists = await exists(examplePath);
|
|
23
|
+
if (!envExists) {
|
|
24
|
+
writeJson({ inSync: false, error: ".env not found" });
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
if (!exampleExists) {
|
|
28
|
+
writeJson({ inSync: false, error: ".env.example not found" });
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
const diff = await compareSync(envPath, examplePath);
|
|
32
|
+
writeJson({
|
|
33
|
+
inSync: diff.inSync,
|
|
34
|
+
missing: diff.missing,
|
|
35
|
+
extra: diff.extra,
|
|
36
|
+
different: diff.different,
|
|
37
|
+
same: diff.same
|
|
38
|
+
});
|
|
39
|
+
return diff.inSync ? 0 : 1;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
42
|
+
writeJson({ inSync: false, error: msg });
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
run
|
|
48
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatScanResult,
|
|
3
|
+
scan
|
|
4
|
+
} from "./chunk-MSXMESFP.js";
|
|
5
|
+
import "./chunk-R7PZRSZ7.js";
|
|
6
|
+
import "./chunk-TE7HPLA6.js";
|
|
7
|
+
|
|
8
|
+
// src/cli/commands/ci-scan.ts
|
|
9
|
+
import { resolve } from "path";
|
|
10
|
+
async function run(args, ctx) {
|
|
11
|
+
const cwd = args.flags["--cwd"] ?? ctx.cwd;
|
|
12
|
+
const baseDir = resolve(cwd);
|
|
13
|
+
const outputFormat = args.flags["--format"] ?? "sarif";
|
|
14
|
+
try {
|
|
15
|
+
const result = await scan({
|
|
16
|
+
cwd: baseDir,
|
|
17
|
+
paths: args.positional.length > 0 ? args.positional : void 0,
|
|
18
|
+
scanGitHistory: args.flags["--git-history"] ?? false,
|
|
19
|
+
failFast: args.flags["--fail-fast"] ?? true,
|
|
20
|
+
outputFormat
|
|
21
|
+
});
|
|
22
|
+
const formatted = formatScanResult(
|
|
23
|
+
result,
|
|
24
|
+
outputFormat
|
|
25
|
+
);
|
|
26
|
+
process.stdout.write(formatted + "\n");
|
|
27
|
+
return result.found ? 1 : 0;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
30
|
+
if (outputFormat === "sarif" || outputFormat === "json") {
|
|
31
|
+
process.stdout.write(JSON.stringify({ error: msg }) + "\n");
|
|
32
|
+
} else {
|
|
33
|
+
process.stderr.write(`Error: ${msg}
|
|
34
|
+
`);
|
|
35
|
+
}
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
run
|
|
41
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bold,
|
|
3
|
+
cyan,
|
|
4
|
+
green,
|
|
5
|
+
red,
|
|
6
|
+
yellow
|
|
7
|
+
} from "./chunk-OMAOROL4.js";
|
|
8
|
+
import {
|
|
9
|
+
writeError,
|
|
10
|
+
writeLine
|
|
11
|
+
} from "./chunk-YN2KGTCB.js";
|
|
12
|
+
import {
|
|
13
|
+
ensureDir,
|
|
14
|
+
exists,
|
|
15
|
+
writeFile
|
|
16
|
+
} from "./chunk-3VYXPTYV.js";
|
|
17
|
+
import "./chunk-5G2DU52U.js";
|
|
18
|
+
|
|
19
|
+
// src/cli/commands/ci-setup.ts
|
|
20
|
+
import { resolve, join } from "path";
|
|
21
|
+
var GITHUB_ACTIONS_TEMPLATE = `name: Ultraenv CI
|
|
22
|
+
|
|
23
|
+
on:
|
|
24
|
+
push:
|
|
25
|
+
branches: [main]
|
|
26
|
+
pull_request:
|
|
27
|
+
branches: [main]
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
validate:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
|
|
35
|
+
- name: Setup Node.js
|
|
36
|
+
uses: actions/setup-node@v4
|
|
37
|
+
with:
|
|
38
|
+
node-version: '20'
|
|
39
|
+
|
|
40
|
+
- name: Install ultraenv
|
|
41
|
+
run: npm install -g ultraenv
|
|
42
|
+
|
|
43
|
+
- name: Validate environment
|
|
44
|
+
run: ultraenv ci validate --strict
|
|
45
|
+
|
|
46
|
+
- name: Check .env sync
|
|
47
|
+
run: ultraenv ci check-sync
|
|
48
|
+
|
|
49
|
+
- name: Scan for secrets
|
|
50
|
+
run: ultraenv ci scan --format sarif --output results.sarif
|
|
51
|
+
continue-on-error: true
|
|
52
|
+
|
|
53
|
+
- name: Upload SARIF results
|
|
54
|
+
if: always()
|
|
55
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
56
|
+
with:
|
|
57
|
+
sarif_file: results.sarif
|
|
58
|
+
continue-on-error: true
|
|
59
|
+
`;
|
|
60
|
+
var GITLAB_CI_TEMPLATE = `stages:
|
|
61
|
+
- validate
|
|
62
|
+
- scan
|
|
63
|
+
|
|
64
|
+
variables:
|
|
65
|
+
NODE_VERSION: "20"
|
|
66
|
+
|
|
67
|
+
.validate-env:
|
|
68
|
+
stage: validate
|
|
69
|
+
image: node:\${NODE_VERSION}
|
|
70
|
+
before_script:
|
|
71
|
+
- npm install -g ultraenv
|
|
72
|
+
script:
|
|
73
|
+
- ultraenv ci validate --strict
|
|
74
|
+
- ultraenv ci check-sync
|
|
75
|
+
|
|
76
|
+
.scan-secrets:
|
|
77
|
+
stage: scan
|
|
78
|
+
image: node:\${NODE_VERSION}
|
|
79
|
+
before_script:
|
|
80
|
+
- npm install -g ultraenv
|
|
81
|
+
script:
|
|
82
|
+
- ultraenv ci scan --format json > scan-results.json
|
|
83
|
+
artifacts:
|
|
84
|
+
paths:
|
|
85
|
+
- scan-results.json
|
|
86
|
+
when: always
|
|
87
|
+
`;
|
|
88
|
+
async function run(args, ctx) {
|
|
89
|
+
try {
|
|
90
|
+
const cwd = args.flags["--cwd"] ?? ctx.cwd;
|
|
91
|
+
const baseDir = resolve(cwd);
|
|
92
|
+
const platform = args.flags["--platform"] ?? "github";
|
|
93
|
+
writeLine(bold("\u{1F527} Generating CI configuration"));
|
|
94
|
+
writeLine("");
|
|
95
|
+
if (platform === "github") {
|
|
96
|
+
const workflowsDir = join(baseDir, ".github", "workflows");
|
|
97
|
+
await ensureDir(workflowsDir);
|
|
98
|
+
const workflowPath = join(workflowsDir, "ultraenv.yml");
|
|
99
|
+
if (await exists(workflowPath) && !args.flags["--force"]) {
|
|
100
|
+
writeLine(yellow(` ${workflowPath} already exists. Use --force to overwrite.`));
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
await writeFile(workflowPath, GITHUB_ACTIONS_TEMPLATE);
|
|
104
|
+
writeLine(green(` \u2713 Created ${workflowPath}`));
|
|
105
|
+
} else if (platform === "gitlab") {
|
|
106
|
+
const gitlabPath = join(baseDir, ".gitlab-ci.yml");
|
|
107
|
+
if (await exists(gitlabPath) && !args.flags["--force"]) {
|
|
108
|
+
writeLine(yellow(` ${gitlabPath} already exists. Use --force to overwrite.`));
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
await writeFile(gitlabPath, GITLAB_CI_TEMPLATE);
|
|
112
|
+
writeLine(green(` \u2713 Created ${gitlabPath}`));
|
|
113
|
+
} else {
|
|
114
|
+
writeError(red(` Unknown platform: ${platform}`));
|
|
115
|
+
writeError(yellow(" Supported: github, gitlab"));
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
writeLine("");
|
|
119
|
+
writeLine(green(bold(" \u2705 CI configuration generated!")));
|
|
120
|
+
writeLine("");
|
|
121
|
+
writeLine(cyan(" The CI pipeline will:"));
|
|
122
|
+
writeLine(" 1. Validate environment variables");
|
|
123
|
+
writeLine(" 2. Check .env \u2194 .env.example sync");
|
|
124
|
+
writeLine(" 3. Scan for leaked secrets");
|
|
125
|
+
writeLine("");
|
|
126
|
+
return 0;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
129
|
+
writeError(red(` Error: ${msg}`));
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
run
|
|
135
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadWithResult
|
|
3
|
+
} from "./chunk-MNVFG7H4.js";
|
|
4
|
+
import "./chunk-HFXQGJY3.js";
|
|
5
|
+
import {
|
|
6
|
+
writeJson
|
|
7
|
+
} from "./chunk-YN2KGTCB.js";
|
|
8
|
+
import "./chunk-3VYXPTYV.js";
|
|
9
|
+
import "./chunk-XC65ORJ5.js";
|
|
10
|
+
import "./chunk-5G2DU52U.js";
|
|
11
|
+
|
|
12
|
+
// src/cli/commands/ci-validate.ts
|
|
13
|
+
async function run(args, ctx) {
|
|
14
|
+
try {
|
|
15
|
+
const strict = args.flags["--strict"];
|
|
16
|
+
const envDir = args.flags["--env-dir"] ?? ctx.config.envDir;
|
|
17
|
+
const result = loadWithResult({
|
|
18
|
+
envDir,
|
|
19
|
+
expandVariables: ctx.config.expandVariables,
|
|
20
|
+
overrideProcessEnv: false,
|
|
21
|
+
mergeStrategy: ctx.config.mergeStrategy
|
|
22
|
+
});
|
|
23
|
+
const warnings = [];
|
|
24
|
+
const errors = [];
|
|
25
|
+
if (result.metadata.totalVars === 0) {
|
|
26
|
+
errors.push("No environment variables loaded");
|
|
27
|
+
}
|
|
28
|
+
for (const [key, value] of Object.entries(result.env)) {
|
|
29
|
+
if (value === "" && strict) {
|
|
30
|
+
errors.push(`Empty value: ${key}`);
|
|
31
|
+
} else if (value === "") {
|
|
32
|
+
warnings.push(`Empty value: ${key}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const valid = errors.length === 0;
|
|
36
|
+
const output = {
|
|
37
|
+
valid,
|
|
38
|
+
errors,
|
|
39
|
+
warnings,
|
|
40
|
+
metadata: {
|
|
41
|
+
totalVars: result.metadata.totalVars,
|
|
42
|
+
filesParsed: result.metadata.filesParsed,
|
|
43
|
+
loadTimeMs: result.metadata.loadTimeMs
|
|
44
|
+
},
|
|
45
|
+
variables: Object.keys(result.env).length
|
|
46
|
+
};
|
|
47
|
+
writeJson(output);
|
|
48
|
+
return valid ? 0 : 1;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
51
|
+
writeJson({ valid: false, errors: [msg], warnings: [] });
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
run
|
|
57
|
+
};
|