node-safe-env 0.1.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 +381 -0
- package/dist/cli.js +1019 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +938 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +196 -0
- package/dist/index.d.ts +196 -0
- package/dist/index.js +892 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1019 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/cli/index.ts
|
|
5
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
6
|
+
|
|
7
|
+
// src/cli/utils/parseArgs.ts
|
|
8
|
+
function parseArgs(argv) {
|
|
9
|
+
const [command, ...rest] = argv;
|
|
10
|
+
const flags = {};
|
|
11
|
+
const positionals = [];
|
|
12
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
13
|
+
const token = rest[index];
|
|
14
|
+
if (!token.startsWith("--")) {
|
|
15
|
+
positionals.push(token);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const flagName = token.slice(2);
|
|
19
|
+
const next = rest[index + 1];
|
|
20
|
+
if (!next || next.startsWith("--")) {
|
|
21
|
+
flags[flagName] = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
flags[flagName] = next;
|
|
25
|
+
index += 1;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
command,
|
|
29
|
+
flags,
|
|
30
|
+
positionals
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/errors/EnvValidationError.ts
|
|
35
|
+
var EnvValidationError = class extends Error {
|
|
36
|
+
constructor(issues) {
|
|
37
|
+
super(
|
|
38
|
+
[
|
|
39
|
+
"Environment validation failed:",
|
|
40
|
+
...issues.map((issue) => `- [${issue.code}] ${issue.message}`)
|
|
41
|
+
].join("\n")
|
|
42
|
+
);
|
|
43
|
+
this.name = "EnvValidationError";
|
|
44
|
+
this.issues = issues;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/createEnv.ts
|
|
49
|
+
import path2 from "path";
|
|
50
|
+
|
|
51
|
+
// src/flattenSchema.ts
|
|
52
|
+
function isEnvRule(value) {
|
|
53
|
+
return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
54
|
+
}
|
|
55
|
+
function toEnvKey(path4) {
|
|
56
|
+
return path4.map((segment) => segment.toUpperCase()).join("_");
|
|
57
|
+
}
|
|
58
|
+
function flattenSchema(schema, parentPath = []) {
|
|
59
|
+
const entries = [];
|
|
60
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
61
|
+
const currentPath = [...parentPath, key];
|
|
62
|
+
if (isEnvRule(value)) {
|
|
63
|
+
entries.push({
|
|
64
|
+
path: currentPath,
|
|
65
|
+
envKey: toEnvKey(currentPath),
|
|
66
|
+
rule: value
|
|
67
|
+
});
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (typeof value === "object" && value !== null) {
|
|
71
|
+
entries.push(
|
|
72
|
+
...flattenSchema(value, currentPath)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return entries;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/findUnknownEnvKeys.ts
|
|
80
|
+
function findUnknownEnvKeys(schema, source) {
|
|
81
|
+
const knownKeys = new Set(flattenSchema(schema).map((entry) => entry.envKey));
|
|
82
|
+
const issues = [];
|
|
83
|
+
for (const key of Object.keys(source)) {
|
|
84
|
+
if (source[key] === void 0) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (knownKeys.has(key)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
issues.push({
|
|
91
|
+
key,
|
|
92
|
+
code: "unknown_key",
|
|
93
|
+
message: `Environment variable "${key}" is not defined in the schema.`
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return issues;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/loadEnvFiles.ts
|
|
100
|
+
import fs from "fs";
|
|
101
|
+
import path from "path";
|
|
102
|
+
var ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
103
|
+
function stripInlineComment(input) {
|
|
104
|
+
let inSingleQuote = false;
|
|
105
|
+
let inDoubleQuote = false;
|
|
106
|
+
let isEscaped = false;
|
|
107
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
108
|
+
const char = input[index];
|
|
109
|
+
if (isEscaped) {
|
|
110
|
+
isEscaped = false;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (char === "\\") {
|
|
114
|
+
isEscaped = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (char === '"' && !inSingleQuote) {
|
|
118
|
+
inDoubleQuote = !inDoubleQuote;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (char === "'" && !inDoubleQuote) {
|
|
122
|
+
inSingleQuote = !inSingleQuote;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (char === "#" && !inSingleQuote && !inDoubleQuote && (index === 0 || /\s/.test(input[index - 1] ?? ""))) {
|
|
126
|
+
return input.slice(0, index).trimEnd();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return input.trimEnd();
|
|
130
|
+
}
|
|
131
|
+
function parseEnvFile(filePath) {
|
|
132
|
+
if (!fs.existsSync(filePath)) {
|
|
133
|
+
return /* @__PURE__ */ Object.create(null);
|
|
134
|
+
}
|
|
135
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
136
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
137
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
138
|
+
let line = rawLine.trim();
|
|
139
|
+
if (!line || line.startsWith("#")) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (line.startsWith("export ")) {
|
|
143
|
+
line = line.slice("export ".length).trim();
|
|
144
|
+
if (!line) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const equalIndex = line.indexOf("=");
|
|
149
|
+
if (equalIndex <= 0) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const key = line.slice(0, equalIndex).trim();
|
|
153
|
+
if (!ENV_KEY_PATTERN.test(key)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
let value = stripInlineComment(line.slice(equalIndex + 1)).trim();
|
|
157
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
158
|
+
value = value.slice(1, -1);
|
|
159
|
+
}
|
|
160
|
+
result[key] = value;
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
function loadEnvFiles(options = {}) {
|
|
165
|
+
const cwd = options.cwd ?? process.cwd();
|
|
166
|
+
const nodeEnv = options.nodeEnv ?? process.env.NODE_ENV ?? "development";
|
|
167
|
+
const base = parseEnvFile(path.join(cwd, ".env"));
|
|
168
|
+
const local = parseEnvFile(path.join(cwd, ".env.local"));
|
|
169
|
+
const environment = parseEnvFile(path.join(cwd, `.env.${nodeEnv}`));
|
|
170
|
+
const custom = options.envFile ? parseEnvFile(path.resolve(cwd, options.envFile)) : /* @__PURE__ */ Object.create(null);
|
|
171
|
+
return {
|
|
172
|
+
base,
|
|
173
|
+
local,
|
|
174
|
+
environment,
|
|
175
|
+
custom
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/mergeSources.ts
|
|
180
|
+
function mergeSources(files, runtimeValues = process.env) {
|
|
181
|
+
return Object.assign(
|
|
182
|
+
/* @__PURE__ */ Object.create(null),
|
|
183
|
+
files.base,
|
|
184
|
+
files.local,
|
|
185
|
+
files.environment,
|
|
186
|
+
files.custom,
|
|
187
|
+
runtimeValues
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/applyTransform.ts
|
|
192
|
+
function applyTransform(key, rule, result) {
|
|
193
|
+
if (result.issue || result.value === void 0 || !rule.transform) {
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
return {
|
|
198
|
+
value: rule.transform(result.value)
|
|
199
|
+
};
|
|
200
|
+
} catch (error) {
|
|
201
|
+
const message = error instanceof Error && error.message ? error.message : `Environment variable "${key}" failed transform.`;
|
|
202
|
+
return {
|
|
203
|
+
issue: {
|
|
204
|
+
key,
|
|
205
|
+
code: "invalid_custom",
|
|
206
|
+
message
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/validators/boolean.ts
|
|
213
|
+
var TRUE_VALUES = /* @__PURE__ */ new Set(["true", "1", "yes", "on"]);
|
|
214
|
+
var FALSE_VALUES = /* @__PURE__ */ new Set(["false", "0", "no", "off"]);
|
|
215
|
+
var validateBoolean = ({ key, rawValue }) => {
|
|
216
|
+
const normalized = rawValue.trim().toLowerCase();
|
|
217
|
+
if (TRUE_VALUES.has(normalized)) {
|
|
218
|
+
return { value: true };
|
|
219
|
+
}
|
|
220
|
+
if (FALSE_VALUES.has(normalized)) {
|
|
221
|
+
return { value: false };
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
issue: {
|
|
225
|
+
key,
|
|
226
|
+
code: "invalid_boolean",
|
|
227
|
+
message: `Environment variable "${key}" must be a valid boolean.`
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/validators/enum.ts
|
|
233
|
+
var validateEnum = ({ key, rawValue, rule }) => {
|
|
234
|
+
const enumRule = rule;
|
|
235
|
+
if (!enumRule.values.includes(rawValue)) {
|
|
236
|
+
return {
|
|
237
|
+
issue: {
|
|
238
|
+
key,
|
|
239
|
+
code: "invalid_enum",
|
|
240
|
+
message: `Environment variable "${key}" must be one of: ${enumRule.values.join(", ")}.`
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return { value: rawValue };
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// src/validators/json.ts
|
|
248
|
+
var validateJson = ({ key, rawValue }) => {
|
|
249
|
+
try {
|
|
250
|
+
const parsed = JSON.parse(rawValue);
|
|
251
|
+
return { value: parsed };
|
|
252
|
+
} catch {
|
|
253
|
+
return {
|
|
254
|
+
issue: {
|
|
255
|
+
key,
|
|
256
|
+
code: "invalid_json",
|
|
257
|
+
message: `Environment variable "${key}" must contain valid JSON.`
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/validators/number.ts
|
|
264
|
+
var validateNumber = ({ key, rawValue }) => {
|
|
265
|
+
const parsed = Number(rawValue);
|
|
266
|
+
if (!Number.isFinite(parsed)) {
|
|
267
|
+
return {
|
|
268
|
+
issue: {
|
|
269
|
+
key,
|
|
270
|
+
code: "invalid_number",
|
|
271
|
+
message: `Environment variable "${key}" must be a valid number.`
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return { value: parsed };
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// src/validators/port.ts
|
|
279
|
+
var validatePort = ({ key, rawValue }) => {
|
|
280
|
+
const num = Number(rawValue);
|
|
281
|
+
if (!Number.isInteger(num) || num < 1 || num > 65535) {
|
|
282
|
+
return {
|
|
283
|
+
issue: {
|
|
284
|
+
key,
|
|
285
|
+
code: "invalid_port",
|
|
286
|
+
message: `Environment variable "${key}" must be a valid port (1\u201365535).`
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return { value: num };
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// src/validators/string.ts
|
|
294
|
+
var validateString = ({ rawValue }) => {
|
|
295
|
+
return { value: rawValue };
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// src/validators/url.ts
|
|
299
|
+
var validateUrl = ({ key, rawValue }) => {
|
|
300
|
+
try {
|
|
301
|
+
new URL(rawValue);
|
|
302
|
+
return { value: rawValue };
|
|
303
|
+
} catch {
|
|
304
|
+
return {
|
|
305
|
+
issue: {
|
|
306
|
+
key,
|
|
307
|
+
code: "invalid_url",
|
|
308
|
+
message: `Environment variable "${key}" must be a valid URL.`
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// src/validators/int.ts
|
|
315
|
+
var validateInt = ({ key, rawValue }) => {
|
|
316
|
+
const parsed = Number(rawValue);
|
|
317
|
+
if (!Number.isInteger(parsed)) {
|
|
318
|
+
return {
|
|
319
|
+
issue: {
|
|
320
|
+
key,
|
|
321
|
+
code: "invalid_number",
|
|
322
|
+
message: `Environment variable "${key}" must be a valid integer.`
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return { value: parsed };
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// src/validators/float.ts
|
|
330
|
+
var validateFloat = ({ key, rawValue }) => {
|
|
331
|
+
const parsed = Number(rawValue);
|
|
332
|
+
if (!Number.isFinite(parsed)) {
|
|
333
|
+
return {
|
|
334
|
+
issue: {
|
|
335
|
+
key,
|
|
336
|
+
code: "invalid_number",
|
|
337
|
+
message: `Environment variable "${key}" must be a valid float.`
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
return { value: parsed };
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// src/validators/array.ts
|
|
345
|
+
var validateArray = ({ key, rawValue, rule }) => {
|
|
346
|
+
const arrayRule = rule;
|
|
347
|
+
const separator = arrayRule.separator ?? ",";
|
|
348
|
+
const trimItems = arrayRule.trimItems ?? true;
|
|
349
|
+
const allowEmptyItems = arrayRule.allowEmptyItems ?? false;
|
|
350
|
+
const splitValues = rawValue.split(separator);
|
|
351
|
+
const parsed = trimItems ? splitValues.map((item) => item.trim()) : splitValues;
|
|
352
|
+
if (!allowEmptyItems && parsed.some((item) => item === "")) {
|
|
353
|
+
const issue = {
|
|
354
|
+
key,
|
|
355
|
+
code: "invalid_array",
|
|
356
|
+
message: `Environment variable "${key}" cannot contain empty array items`
|
|
357
|
+
};
|
|
358
|
+
return { issue };
|
|
359
|
+
}
|
|
360
|
+
return { value: parsed };
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// src/validators/custom.ts
|
|
364
|
+
var validateCustom = ({ key, rawValue, rule }) => {
|
|
365
|
+
const customRule = rule;
|
|
366
|
+
try {
|
|
367
|
+
const parsed = customRule.parse(rawValue);
|
|
368
|
+
return { value: parsed };
|
|
369
|
+
} catch (error) {
|
|
370
|
+
const message = error instanceof Error && error.message ? error.message : `Environment variable "${key}" failed custom validation.`;
|
|
371
|
+
return {
|
|
372
|
+
issue: {
|
|
373
|
+
key,
|
|
374
|
+
code: "invalid_custom",
|
|
375
|
+
message
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// src/validators/email.ts
|
|
382
|
+
var validateEmail = ({ key, rawValue, rule }) => {
|
|
383
|
+
const emailRule = rule;
|
|
384
|
+
void emailRule;
|
|
385
|
+
const value = rawValue.trim();
|
|
386
|
+
if (!value) {
|
|
387
|
+
const issue = {
|
|
388
|
+
key,
|
|
389
|
+
code: "invalid_email",
|
|
390
|
+
message: `Environment variable "${key}" must be a valid email address`
|
|
391
|
+
};
|
|
392
|
+
return { issue };
|
|
393
|
+
}
|
|
394
|
+
if (/\s/.test(value)) {
|
|
395
|
+
const issue = {
|
|
396
|
+
key,
|
|
397
|
+
code: "invalid_email",
|
|
398
|
+
message: `Environment variable "${key}" must be a valid email address`
|
|
399
|
+
};
|
|
400
|
+
return { issue };
|
|
401
|
+
}
|
|
402
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
403
|
+
if (!emailRegex.test(value)) {
|
|
404
|
+
const issue = {
|
|
405
|
+
key,
|
|
406
|
+
code: "invalid_email",
|
|
407
|
+
message: `Environment variable "${key}" must be a valid email address`
|
|
408
|
+
};
|
|
409
|
+
return { issue };
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
value
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// src/validators/date.ts
|
|
417
|
+
function isValidDate(date) {
|
|
418
|
+
return !Number.isNaN(date.getTime());
|
|
419
|
+
}
|
|
420
|
+
var validateDate = ({ key, rawValue, rule }) => {
|
|
421
|
+
const dateRule = rule;
|
|
422
|
+
void dateRule;
|
|
423
|
+
const value = rawValue.trim();
|
|
424
|
+
if (!value) {
|
|
425
|
+
const issue = {
|
|
426
|
+
key,
|
|
427
|
+
code: "invalid_date",
|
|
428
|
+
message: `Environment variable "${key}" must be a valid ISO date`
|
|
429
|
+
};
|
|
430
|
+
return { issue };
|
|
431
|
+
}
|
|
432
|
+
const isoDateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
433
|
+
const isoDateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,3})?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
434
|
+
if (!isoDateRegex.test(value) && !isoDateTimeRegex.test(value)) {
|
|
435
|
+
const issue = {
|
|
436
|
+
key,
|
|
437
|
+
code: "invalid_date",
|
|
438
|
+
message: `Environment variable "${key}" must be a valid ISO date string`
|
|
439
|
+
};
|
|
440
|
+
return { issue };
|
|
441
|
+
}
|
|
442
|
+
const parsed = new Date(value);
|
|
443
|
+
if (!isValidDate(parsed)) {
|
|
444
|
+
const issue = {
|
|
445
|
+
key,
|
|
446
|
+
code: "invalid_date",
|
|
447
|
+
message: `Environment variable "${key}" must be a valid date`
|
|
448
|
+
};
|
|
449
|
+
return { issue };
|
|
450
|
+
}
|
|
451
|
+
if (isoDateRegex.test(value)) {
|
|
452
|
+
const [year, month, day] = value.split("-").map(Number);
|
|
453
|
+
if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() + 1 !== month || parsed.getUTCDate() !== day) {
|
|
454
|
+
const issue = {
|
|
455
|
+
key,
|
|
456
|
+
code: "invalid_date",
|
|
457
|
+
message: `Environment variable "${key}" must be a real calendar date`
|
|
458
|
+
};
|
|
459
|
+
return { issue };
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
value: parsed
|
|
464
|
+
};
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// src/validators/index.ts
|
|
468
|
+
var validators = {
|
|
469
|
+
string: validateString,
|
|
470
|
+
number: validateNumber,
|
|
471
|
+
boolean: validateBoolean,
|
|
472
|
+
enum: validateEnum,
|
|
473
|
+
url: validateUrl,
|
|
474
|
+
port: validatePort,
|
|
475
|
+
json: validateJson,
|
|
476
|
+
int: validateInt,
|
|
477
|
+
float: validateFloat,
|
|
478
|
+
array: validateArray,
|
|
479
|
+
custom: validateCustom,
|
|
480
|
+
email: validateEmail,
|
|
481
|
+
date: validateDate
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// src/parseValue.ts
|
|
485
|
+
function parseValue(key, rawValue, rule) {
|
|
486
|
+
const validator = validators[rule.type];
|
|
487
|
+
const result = validator({
|
|
488
|
+
key,
|
|
489
|
+
rawValue,
|
|
490
|
+
rule
|
|
491
|
+
});
|
|
492
|
+
return applyTransform(key, rule, result);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/setNestedValue.ts
|
|
496
|
+
function setNestedValue(target, path4, value) {
|
|
497
|
+
let current = target;
|
|
498
|
+
for (let index = 0; index < path4.length - 1; index += 1) {
|
|
499
|
+
const segment = path4[index];
|
|
500
|
+
const existing = current[segment];
|
|
501
|
+
if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
|
|
502
|
+
current[segment] = {};
|
|
503
|
+
}
|
|
504
|
+
current = current[segment];
|
|
505
|
+
}
|
|
506
|
+
current[path4[path4.length - 1]] = value;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/createEnv.ts
|
|
510
|
+
function isEmptyString(value) {
|
|
511
|
+
return value.trim() === "";
|
|
512
|
+
}
|
|
513
|
+
function defaultToRawValue(value) {
|
|
514
|
+
if (typeof value === "string") {
|
|
515
|
+
return value;
|
|
516
|
+
}
|
|
517
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
518
|
+
return String(value);
|
|
519
|
+
}
|
|
520
|
+
if (value instanceof Date) {
|
|
521
|
+
return value.toISOString();
|
|
522
|
+
}
|
|
523
|
+
return JSON.stringify(value);
|
|
524
|
+
}
|
|
525
|
+
function resolveDefaultValue(value) {
|
|
526
|
+
return typeof value === "function" ? value() : value;
|
|
527
|
+
}
|
|
528
|
+
function parseDefaultValue(envKey, rule) {
|
|
529
|
+
const defaultKind = typeof rule.default === "function" ? "function" : "static";
|
|
530
|
+
let resolvedDefault;
|
|
531
|
+
try {
|
|
532
|
+
resolvedDefault = resolveDefaultValue(rule.default);
|
|
533
|
+
} catch (err) {
|
|
534
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
535
|
+
return {
|
|
536
|
+
issue: {
|
|
537
|
+
key: envKey,
|
|
538
|
+
code: "invalid_default",
|
|
539
|
+
message: `Default function for "${envKey}" threw an error: ${message}`
|
|
540
|
+
},
|
|
541
|
+
defaultKind
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
const rawDefault = defaultToRawValue(resolvedDefault);
|
|
545
|
+
return {
|
|
546
|
+
...parseValue(envKey, rawDefault, rule),
|
|
547
|
+
defaultKind,
|
|
548
|
+
rawDefault
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function countKeys(source) {
|
|
552
|
+
return Object.values(source).filter((value) => typeof value === "string").length;
|
|
553
|
+
}
|
|
554
|
+
function buildLoadedFileReport(loadedFiles, cwd, nodeEnv, envFile) {
|
|
555
|
+
return [
|
|
556
|
+
{
|
|
557
|
+
source: ".env",
|
|
558
|
+
path: path2.join(cwd, ".env"),
|
|
559
|
+
keyCount: countKeys(loadedFiles.base)
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
source: ".env.local",
|
|
563
|
+
path: path2.join(cwd, ".env.local"),
|
|
564
|
+
keyCount: countKeys(loadedFiles.local)
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
source: ".env.environment",
|
|
568
|
+
path: path2.join(cwd, `.env.${nodeEnv}`),
|
|
569
|
+
keyCount: countKeys(loadedFiles.environment)
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
source: "custom",
|
|
573
|
+
path: envFile ? path2.resolve(cwd, envFile) : void 0,
|
|
574
|
+
keyCount: countKeys(loadedFiles.custom)
|
|
575
|
+
}
|
|
576
|
+
];
|
|
577
|
+
}
|
|
578
|
+
function buildSourceTrace(loadedFiles, runtimeValues) {
|
|
579
|
+
const trace = /* @__PURE__ */ Object.create(null);
|
|
580
|
+
const applySource = (source, label) => {
|
|
581
|
+
for (const [key, raw] of Object.entries(source)) {
|
|
582
|
+
if (typeof raw === "string") {
|
|
583
|
+
trace[key] = { source: label, raw };
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
applySource(loadedFiles.base, ".env");
|
|
588
|
+
applySource(loadedFiles.local, ".env.local");
|
|
589
|
+
applySource(loadedFiles.environment, ".env.environment");
|
|
590
|
+
applySource(loadedFiles.custom, "custom");
|
|
591
|
+
applySource(runtimeValues, "process.env");
|
|
592
|
+
return trace;
|
|
593
|
+
}
|
|
594
|
+
function maskDebugValue(value, sensitive) {
|
|
595
|
+
if (value === void 0) {
|
|
596
|
+
return void 0;
|
|
597
|
+
}
|
|
598
|
+
return sensitive ? "***" : value;
|
|
599
|
+
}
|
|
600
|
+
function resolveDebugLogger(debug) {
|
|
601
|
+
if (debug === true) {
|
|
602
|
+
return (report) => {
|
|
603
|
+
console.info(report);
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
if (debug && typeof debug === "object" && debug.logger) {
|
|
607
|
+
return debug.logger;
|
|
608
|
+
}
|
|
609
|
+
return void 0;
|
|
610
|
+
}
|
|
611
|
+
function createEnv(schema, options = {}) {
|
|
612
|
+
const debugEnabled = options.debug !== void 0 && options.debug !== false;
|
|
613
|
+
const debugLogger = debugEnabled ? resolveDebugLogger(options.debug) : void 0;
|
|
614
|
+
const debugKeys = [];
|
|
615
|
+
let source;
|
|
616
|
+
let loadedFileReport = [];
|
|
617
|
+
let sourceTrace = /* @__PURE__ */ Object.create(null);
|
|
618
|
+
if (options.source) {
|
|
619
|
+
source = options.source;
|
|
620
|
+
if (debugEnabled) {
|
|
621
|
+
for (const [key, raw] of Object.entries(source)) {
|
|
622
|
+
if (typeof raw === "string") {
|
|
623
|
+
sourceTrace[key] = { source: "process.env", raw };
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
} else {
|
|
628
|
+
const loadedFiles = loadEnvFiles({
|
|
629
|
+
cwd: options.cwd,
|
|
630
|
+
nodeEnv: options.nodeEnv,
|
|
631
|
+
envFile: options.envFile
|
|
632
|
+
});
|
|
633
|
+
source = mergeSources(loadedFiles, process.env);
|
|
634
|
+
if (debugEnabled) {
|
|
635
|
+
const cwd = options.cwd ?? process.cwd();
|
|
636
|
+
const nodeEnv = options.nodeEnv ?? process.env.NODE_ENV ?? "development";
|
|
637
|
+
loadedFileReport = buildLoadedFileReport(
|
|
638
|
+
loadedFiles,
|
|
639
|
+
cwd,
|
|
640
|
+
nodeEnv,
|
|
641
|
+
options.envFile
|
|
642
|
+
);
|
|
643
|
+
sourceTrace = buildSourceTrace(loadedFiles, process.env);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
const issues = [];
|
|
647
|
+
const result = {};
|
|
648
|
+
const flattenedSchema = flattenSchema(schema);
|
|
649
|
+
if (options.strict) {
|
|
650
|
+
issues.push(
|
|
651
|
+
...findUnknownEnvKeys(schema, source)
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
for (const entry of flattenedSchema) {
|
|
655
|
+
const { path: path4, envKey, rule } = entry;
|
|
656
|
+
const currentValue = source[envKey];
|
|
657
|
+
const sourceInfo = sourceTrace[envKey];
|
|
658
|
+
const sensitive = rule.sensitive === true;
|
|
659
|
+
const defaultKind = rule.default === void 0 ? void 0 : typeof rule.default === "function" ? "function" : "static";
|
|
660
|
+
const pushDebugEntry = (status, values) => {
|
|
661
|
+
if (!debugEnabled) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
debugKeys.push({
|
|
665
|
+
key: envKey,
|
|
666
|
+
ruleType: rule.type,
|
|
667
|
+
source: values.source,
|
|
668
|
+
usedDefault: values.usedDefault,
|
|
669
|
+
defaultKind: values.defaultKind,
|
|
670
|
+
raw: maskDebugValue(values.raw, sensitive),
|
|
671
|
+
parsed: maskDebugValue(values.parsed, sensitive),
|
|
672
|
+
status,
|
|
673
|
+
issue: values.issue
|
|
674
|
+
});
|
|
675
|
+
};
|
|
676
|
+
if (typeof currentValue !== "string") {
|
|
677
|
+
if (rule.default !== void 0) {
|
|
678
|
+
const parsedDefault = parseDefaultValue(envKey, rule);
|
|
679
|
+
if (parsedDefault.issue) {
|
|
680
|
+
issues.push(parsedDefault.issue);
|
|
681
|
+
pushDebugEntry("issue", {
|
|
682
|
+
source: "default",
|
|
683
|
+
usedDefault: true,
|
|
684
|
+
defaultKind: parsedDefault.defaultKind,
|
|
685
|
+
raw: parsedDefault.rawDefault,
|
|
686
|
+
issue: parsedDefault.issue
|
|
687
|
+
});
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
pushDebugEntry("defaulted", {
|
|
691
|
+
source: "default",
|
|
692
|
+
usedDefault: true,
|
|
693
|
+
defaultKind: parsedDefault.defaultKind,
|
|
694
|
+
raw: parsedDefault.rawDefault,
|
|
695
|
+
parsed: parsedDefault.value
|
|
696
|
+
});
|
|
697
|
+
setNestedValue(result, path4, parsedDefault.value);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
if (rule.required) {
|
|
701
|
+
const issue = {
|
|
702
|
+
key: envKey,
|
|
703
|
+
code: "missing",
|
|
704
|
+
message: `Missing required environment variable "${envKey}".`
|
|
705
|
+
};
|
|
706
|
+
issues.push(issue);
|
|
707
|
+
pushDebugEntry("missing", {
|
|
708
|
+
source: "missing",
|
|
709
|
+
usedDefault: false,
|
|
710
|
+
defaultKind,
|
|
711
|
+
issue
|
|
712
|
+
});
|
|
713
|
+
} else {
|
|
714
|
+
pushDebugEntry("missing", {
|
|
715
|
+
source: "missing",
|
|
716
|
+
usedDefault: false,
|
|
717
|
+
defaultKind
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
const rawValue = currentValue;
|
|
723
|
+
if (!rule.allowEmpty && isEmptyString(rawValue)) {
|
|
724
|
+
if (rule.default !== void 0) {
|
|
725
|
+
const parsedDefault = parseDefaultValue(envKey, rule);
|
|
726
|
+
if (parsedDefault.issue) {
|
|
727
|
+
issues.push(parsedDefault.issue);
|
|
728
|
+
pushDebugEntry("issue", {
|
|
729
|
+
source: "default",
|
|
730
|
+
usedDefault: true,
|
|
731
|
+
defaultKind: parsedDefault.defaultKind,
|
|
732
|
+
raw: parsedDefault.rawDefault,
|
|
733
|
+
issue: parsedDefault.issue
|
|
734
|
+
});
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
pushDebugEntry("defaulted", {
|
|
738
|
+
source: "default",
|
|
739
|
+
usedDefault: true,
|
|
740
|
+
defaultKind: parsedDefault.defaultKind,
|
|
741
|
+
raw: parsedDefault.rawDefault,
|
|
742
|
+
parsed: parsedDefault.value
|
|
743
|
+
});
|
|
744
|
+
setNestedValue(result, path4, parsedDefault.value);
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
const issue = {
|
|
748
|
+
key: envKey,
|
|
749
|
+
code: "empty",
|
|
750
|
+
message: `Environment variable "${envKey}" cannot be empty.`
|
|
751
|
+
};
|
|
752
|
+
issues.push(issue);
|
|
753
|
+
pushDebugEntry("empty", {
|
|
754
|
+
source: sourceInfo?.source ?? "process.env",
|
|
755
|
+
usedDefault: false,
|
|
756
|
+
defaultKind,
|
|
757
|
+
raw: rawValue,
|
|
758
|
+
issue
|
|
759
|
+
});
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const parsed = parseValue(envKey, rawValue, rule);
|
|
763
|
+
if (parsed.issue) {
|
|
764
|
+
issues.push(parsed.issue);
|
|
765
|
+
pushDebugEntry("issue", {
|
|
766
|
+
source: sourceInfo?.source ?? "process.env",
|
|
767
|
+
usedDefault: false,
|
|
768
|
+
defaultKind,
|
|
769
|
+
raw: rawValue,
|
|
770
|
+
issue: parsed.issue
|
|
771
|
+
});
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
pushDebugEntry("parsed", {
|
|
775
|
+
source: sourceInfo?.source ?? "process.env",
|
|
776
|
+
usedDefault: false,
|
|
777
|
+
defaultKind,
|
|
778
|
+
raw: rawValue,
|
|
779
|
+
parsed: parsed.value
|
|
780
|
+
});
|
|
781
|
+
setNestedValue(result, path4, parsed.value);
|
|
782
|
+
}
|
|
783
|
+
if (debugLogger) {
|
|
784
|
+
debugLogger({
|
|
785
|
+
loadedFiles: loadedFileReport,
|
|
786
|
+
keys: debugKeys
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
if (issues.length > 0) {
|
|
790
|
+
throw new EnvValidationError(issues);
|
|
791
|
+
}
|
|
792
|
+
return result;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/cli/utils/formatIssues.ts
|
|
796
|
+
function formatIssues(issues) {
|
|
797
|
+
return issues.map((issue) => `- ${issue.key}: ${issue.message}`).join("\n");
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// src/cli/utils/loadSchemaModule.ts
|
|
801
|
+
import path3 from "path";
|
|
802
|
+
import { pathToFileURL } from "url";
|
|
803
|
+
function isObject(value) {
|
|
804
|
+
return typeof value === "object" && value !== null;
|
|
805
|
+
}
|
|
806
|
+
async function loadSchemaModule(schemaPath) {
|
|
807
|
+
const absolutePath = path3.resolve(schemaPath);
|
|
808
|
+
const moduleUrl = pathToFileURL(absolutePath).href;
|
|
809
|
+
const mod = await import(moduleUrl);
|
|
810
|
+
const schemaCandidate = mod.default ?? mod.schema;
|
|
811
|
+
if (!isObject(schemaCandidate)) {
|
|
812
|
+
throw new Error(
|
|
813
|
+
`Schema module "${schemaPath}" must export a schema as default export or named export "schema".`
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
return schemaCandidate;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/cli/commands/validate.ts
|
|
820
|
+
async function runValidateCommand(options) {
|
|
821
|
+
try {
|
|
822
|
+
const schema = await loadSchemaModule(options.schemaPath);
|
|
823
|
+
createEnv(schema, {
|
|
824
|
+
cwd: options.cwd,
|
|
825
|
+
envFile: options.envFile,
|
|
826
|
+
nodeEnv: options.nodeEnv,
|
|
827
|
+
strict: options.strict
|
|
828
|
+
});
|
|
829
|
+
console.log("\u2713 Environment validation passed.");
|
|
830
|
+
return 0;
|
|
831
|
+
} catch (error) {
|
|
832
|
+
if (error instanceof EnvValidationError) {
|
|
833
|
+
console.error("Environment validation failed:\n");
|
|
834
|
+
console.error(formatIssues(error.issues));
|
|
835
|
+
return 1;
|
|
836
|
+
}
|
|
837
|
+
const message = error instanceof Error ? error.message : "Unknown CLI error";
|
|
838
|
+
console.error(`CLI error: ${message}`);
|
|
839
|
+
return 1;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/readEnvFileSource.ts
|
|
844
|
+
import { existsSync, readFileSync } from "fs";
|
|
845
|
+
import { resolve } from "path";
|
|
846
|
+
function stripQuotes(value) {
|
|
847
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
848
|
+
return value.slice(1, -1);
|
|
849
|
+
}
|
|
850
|
+
return value;
|
|
851
|
+
}
|
|
852
|
+
function readEnvFileSource(filePath) {
|
|
853
|
+
if (!existsSync(filePath)) {
|
|
854
|
+
return {};
|
|
855
|
+
}
|
|
856
|
+
const content = readFileSync(filePath, "utf8");
|
|
857
|
+
const source = {};
|
|
858
|
+
for (const line of content.split(/\r?\n/)) {
|
|
859
|
+
const trimmed = line.trim();
|
|
860
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
const equalsIndex = trimmed.indexOf("=");
|
|
864
|
+
if (equalsIndex === -1) {
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
const key = trimmed.slice(0, equalsIndex).trim();
|
|
868
|
+
const rawValue = trimmed.slice(equalsIndex + 1).trim();
|
|
869
|
+
if (!key) {
|
|
870
|
+
continue;
|
|
871
|
+
}
|
|
872
|
+
source[key] = stripQuotes(rawValue);
|
|
873
|
+
}
|
|
874
|
+
return source;
|
|
875
|
+
}
|
|
876
|
+
function resolveExampleEnvPath(cwd = process.cwd(), exampleFile = ".env.example") {
|
|
877
|
+
return resolve(cwd, exampleFile);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// src/validateExampleEnv.ts
|
|
881
|
+
function validateExampleEnv(schema, exampleSource) {
|
|
882
|
+
const issues = [];
|
|
883
|
+
const flattenedSchema = flattenSchema(schema);
|
|
884
|
+
const expectedKeys = new Set(flattenedSchema.map((entry) => entry.envKey));
|
|
885
|
+
for (const entry of flattenedSchema) {
|
|
886
|
+
const { envKey } = entry;
|
|
887
|
+
if (!(envKey in exampleSource)) {
|
|
888
|
+
issues.push({
|
|
889
|
+
key: envKey,
|
|
890
|
+
code: "missing_example_key",
|
|
891
|
+
message: `Environment variable "${envKey}" is missing from .env.example.`
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
for (const key of Object.keys(exampleSource)) {
|
|
896
|
+
if (exampleSource[key] === void 0) {
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
if (expectedKeys.has(key)) {
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
issues.push({
|
|
903
|
+
key,
|
|
904
|
+
code: "unknown_example_key",
|
|
905
|
+
message: `Environment variable "${key}" in .env.example is not defined in the schema.`
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
return issues;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// src/validateExampleEnvFile.ts
|
|
912
|
+
function validateExampleEnvFile(schema, options = {}) {
|
|
913
|
+
const filePath = resolveExampleEnvPath(options.cwd, options.exampleFile);
|
|
914
|
+
const exampleSource = readEnvFileSource(filePath);
|
|
915
|
+
return validateExampleEnv(schema, exampleSource);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/cli/commands/validateExample.ts
|
|
919
|
+
async function runValidateExampleCommand(options) {
|
|
920
|
+
try {
|
|
921
|
+
const schema = await loadSchemaModule(options.schemaPath);
|
|
922
|
+
const issues = validateExampleEnvFile(schema, {
|
|
923
|
+
cwd: options.cwd,
|
|
924
|
+
exampleFile: options.exampleFile
|
|
925
|
+
});
|
|
926
|
+
if (issues.length > 0) {
|
|
927
|
+
console.error(".env.example validation failed:\n");
|
|
928
|
+
console.error(formatIssues(issues));
|
|
929
|
+
return 1;
|
|
930
|
+
}
|
|
931
|
+
console.log("\u2713 .env.example validation passed.");
|
|
932
|
+
return 0;
|
|
933
|
+
} catch (error) {
|
|
934
|
+
const message = error instanceof Error ? error.message : "Unknown CLI error";
|
|
935
|
+
console.error(`CLI error: ${message}`);
|
|
936
|
+
return 1;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// src/cli/index.ts
|
|
941
|
+
function printHelp(io) {
|
|
942
|
+
io.log(`node-safe-env CLI
|
|
943
|
+
|
|
944
|
+
Usage:
|
|
945
|
+
node-safe-env validate --schema <path> [--cwd <path>] [--env-file <path>] [--node-env <value>] [--strict]
|
|
946
|
+
node-safe-env validate-example --schema <path> [--cwd <path>] [--example-file <path>]
|
|
947
|
+
|
|
948
|
+
Commands:
|
|
949
|
+
validate Validate environment variables using a schema
|
|
950
|
+
validate-example Validate a .env.example file against a schema
|
|
951
|
+
|
|
952
|
+
Options:
|
|
953
|
+
--schema <path> Path to schema module
|
|
954
|
+
--cwd <path> Working directory for env file loading
|
|
955
|
+
--env-file <path> Custom env file path for validate
|
|
956
|
+
--node-env <value> NODE_ENV override for validate
|
|
957
|
+
--strict Enable strict mode for unknown keys
|
|
958
|
+
--example-file <path> Custom example file path for validate-example
|
|
959
|
+
--help Show this help
|
|
960
|
+
`);
|
|
961
|
+
}
|
|
962
|
+
function getStringFlag(flags, name) {
|
|
963
|
+
const value = flags[name];
|
|
964
|
+
return typeof value === "string" ? value : void 0;
|
|
965
|
+
}
|
|
966
|
+
async function runCli(argv, io = {
|
|
967
|
+
log: console.log,
|
|
968
|
+
error: console.error
|
|
969
|
+
}) {
|
|
970
|
+
const parsed = parseArgs(argv);
|
|
971
|
+
if (!parsed.command || parsed.flags.help || parsed.command === "help") {
|
|
972
|
+
printHelp(io);
|
|
973
|
+
return 0;
|
|
974
|
+
}
|
|
975
|
+
const schemaPath = getStringFlag(parsed.flags, "schema");
|
|
976
|
+
if (!schemaPath) {
|
|
977
|
+
io.error('Missing required flag "--schema".\n');
|
|
978
|
+
printHelp(io);
|
|
979
|
+
return 1;
|
|
980
|
+
}
|
|
981
|
+
if (parsed.command === "validate") {
|
|
982
|
+
return runValidateCommand({
|
|
983
|
+
schemaPath,
|
|
984
|
+
cwd: getStringFlag(parsed.flags, "cwd"),
|
|
985
|
+
envFile: getStringFlag(parsed.flags, "env-file"),
|
|
986
|
+
nodeEnv: getStringFlag(parsed.flags, "node-env"),
|
|
987
|
+
strict: parsed.flags.strict === true
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
if (parsed.command === "validate-example") {
|
|
991
|
+
return runValidateExampleCommand({
|
|
992
|
+
schemaPath,
|
|
993
|
+
cwd: getStringFlag(parsed.flags, "cwd"),
|
|
994
|
+
exampleFile: getStringFlag(parsed.flags, "example-file")
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
io.error(`Unknown command "${parsed.command}".
|
|
998
|
+
`);
|
|
999
|
+
printHelp(io);
|
|
1000
|
+
return 1;
|
|
1001
|
+
}
|
|
1002
|
+
async function main() {
|
|
1003
|
+
const exitCode = await runCli(process.argv.slice(2));
|
|
1004
|
+
process.exitCode = exitCode;
|
|
1005
|
+
}
|
|
1006
|
+
function isDirectRun() {
|
|
1007
|
+
const entry = process.argv[1];
|
|
1008
|
+
if (!entry) {
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
return import.meta.url === pathToFileURL2(entry).href;
|
|
1012
|
+
}
|
|
1013
|
+
if (isDirectRun()) {
|
|
1014
|
+
void main();
|
|
1015
|
+
}
|
|
1016
|
+
export {
|
|
1017
|
+
runCli
|
|
1018
|
+
};
|
|
1019
|
+
//# sourceMappingURL=cli.js.map
|