@saasak/tool-env 1.3.0 → 1.4.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/bin/index.js +22 -5
- package/package.json +1 -1
- package/src/core.js +19 -7
- package/src/index.d.ts +8 -0
package/bin/index.js
CHANGED
|
@@ -16,10 +16,14 @@ import {
|
|
|
16
16
|
load,
|
|
17
17
|
} from "../src/core.js";
|
|
18
18
|
|
|
19
|
-
const args = minimist(process.argv.slice(2), {
|
|
19
|
+
const args = minimist(process.argv.slice(2), {
|
|
20
|
+
"--": true,
|
|
21
|
+
boolean: ["strict", "expose", "help", "h"],
|
|
22
|
+
});
|
|
20
23
|
const __root = process.cwd();
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
let command = args._[0] || "help";
|
|
26
|
+
if (args.help || args.h) command = "help";
|
|
23
27
|
|
|
24
28
|
const secret = resolveSecret(args.secret ? `file://${args.secret}` : '');
|
|
25
29
|
|
|
@@ -33,6 +37,15 @@ if (commandsRequiringEnvFile.includes(command) && !fs.existsSync(envFile)) {
|
|
|
33
37
|
process.exit(1);
|
|
34
38
|
}
|
|
35
39
|
|
|
40
|
+
if (args.strict && commandsRequiringEnvFile.includes(command)) {
|
|
41
|
+
const env = JSON.parse(fs.readFileSync(envFile, "utf8"));
|
|
42
|
+
const result = verifyCanDecrypt(env, secret);
|
|
43
|
+
if (!result.success) {
|
|
44
|
+
console.error(`Strict mode: ${result.error}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
async function showCommand() {
|
|
37
50
|
if (!secret) {
|
|
38
51
|
console.log(
|
|
@@ -295,7 +308,8 @@ async function runCommand() {
|
|
|
295
308
|
targetEnv: args.target,
|
|
296
309
|
envPath: args.env,
|
|
297
310
|
applyToProcess: false,
|
|
298
|
-
expose
|
|
311
|
+
expose,
|
|
312
|
+
strict: !!args.strict
|
|
299
313
|
});
|
|
300
314
|
} catch (error) {
|
|
301
315
|
console.error(`wrenv: ${error.message}`);
|
|
@@ -322,7 +336,7 @@ async function runCommand() {
|
|
|
322
336
|
function helpCommand() {
|
|
323
337
|
console.log("Usage: wrenv [command] [options]\n");
|
|
324
338
|
console.log("Commands:");
|
|
325
|
-
console.log(" write Write .env files for all packages
|
|
339
|
+
console.log(" write Write .env files for all packages");
|
|
326
340
|
console.log(
|
|
327
341
|
" show Show environment variables without writing files",
|
|
328
342
|
);
|
|
@@ -345,7 +359,10 @@ function helpCommand() {
|
|
|
345
359
|
" --target <env> Target environment (dev, staging, prod, all)",
|
|
346
360
|
);
|
|
347
361
|
console.log(
|
|
348
|
-
" --env <path> Path to env file (default: .env.json)
|
|
362
|
+
" --env <path> Path to env file (default: .env.json)",
|
|
363
|
+
);
|
|
364
|
+
console.log(
|
|
365
|
+
" --strict Error if encrypted values cannot be decrypted\n",
|
|
349
366
|
);
|
|
350
367
|
console.log("Environment Variables:");
|
|
351
368
|
console.log(" WRENV_SECRET Secret for encryption/decryption");
|
package/package.json
CHANGED
package/src/core.js
CHANGED
|
@@ -17,6 +17,7 @@ import { findMonorepoPackages } from "./utils-pkg.js";
|
|
|
17
17
|
* @property {string} [cwd] - Working directory (default: process.cwd())
|
|
18
18
|
* @property {boolean} [applyToProcess] - Whether to apply to process.env (default: true)
|
|
19
19
|
* @property {boolean} [expose] - wether to expose the secret or not
|
|
20
|
+
* @property {boolean} [strict] - Error if encrypted values cannot be decrypted
|
|
20
21
|
*/
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -26,6 +27,7 @@ import { findMonorepoPackages } from "./utils-pkg.js";
|
|
|
26
27
|
* @property {string} [envPath] - Path to env.json file (default: '.env.json')
|
|
27
28
|
* @property {string} [cwd] - Working directory (default: process.cwd())
|
|
28
29
|
* @property {boolean} [expose] - wether to expose the secret or not
|
|
30
|
+
* @property {boolean} [strict] - Error if encrypted values cannot be decrypted
|
|
29
31
|
*/
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -290,7 +292,8 @@ export function loadEnvJson(options = {}) {
|
|
|
290
292
|
secret: secretParam,
|
|
291
293
|
envPath = ".env.json",
|
|
292
294
|
cwd = process.cwd(),
|
|
293
|
-
expose = false
|
|
295
|
+
expose = false,
|
|
296
|
+
strict = false
|
|
294
297
|
} = options;
|
|
295
298
|
|
|
296
299
|
const secret = resolveSecret(secretParam);
|
|
@@ -306,6 +309,13 @@ export function loadEnvJson(options = {}) {
|
|
|
306
309
|
const envContent = fs.readFileSync(envFile, "utf8");
|
|
307
310
|
const env = JSON.parse(envContent);
|
|
308
311
|
|
|
312
|
+
if (strict) {
|
|
313
|
+
const result = verifyCanDecrypt(env, secret);
|
|
314
|
+
if (!result.success) {
|
|
315
|
+
throw new Error(`Strict mode: ${result.error}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
309
319
|
const allowedEnvs =
|
|
310
320
|
env && env.envs && Array.isArray(env.envs) ? env.envs : [DEFAULT_ENV];
|
|
311
321
|
const resolvedTarget = allowedEnvs.find((e) => e === targetEnv);
|
|
@@ -378,7 +388,8 @@ export function load(options = {}) {
|
|
|
378
388
|
envPath,
|
|
379
389
|
cwd = process.cwd(),
|
|
380
390
|
applyToProcess = true,
|
|
381
|
-
expose = false
|
|
391
|
+
expose = false,
|
|
392
|
+
strict = false
|
|
382
393
|
} = options;
|
|
383
394
|
|
|
384
395
|
// Resolve target first to determine if we're in dev mode
|
|
@@ -390,7 +401,8 @@ export function load(options = {}) {
|
|
|
390
401
|
targetEnv: resolvedTarget,
|
|
391
402
|
envPath,
|
|
392
403
|
cwd,
|
|
393
|
-
expose
|
|
404
|
+
expose,
|
|
405
|
+
strict
|
|
394
406
|
});
|
|
395
407
|
|
|
396
408
|
// Load .env.local overrides only in dev mode (security: prevent local overrides in production)
|
|
@@ -446,10 +458,6 @@ export function verifyCanDecrypt(env, secret) {
|
|
|
446
458
|
/** @type {EncryptedValueInfo[]} */
|
|
447
459
|
const encryptedValues = [];
|
|
448
460
|
|
|
449
|
-
if (!secret) {
|
|
450
|
-
return { success: false, path: null, error: "No secret provided" };
|
|
451
|
-
}
|
|
452
|
-
|
|
453
461
|
// Collect all encrypted values from variables
|
|
454
462
|
if (env.variables) {
|
|
455
463
|
for (const [varName, varValue] of Object.entries(env.variables)) {
|
|
@@ -485,6 +493,10 @@ export function verifyCanDecrypt(env, secret) {
|
|
|
485
493
|
return { success: true };
|
|
486
494
|
}
|
|
487
495
|
|
|
496
|
+
if (!secret) {
|
|
497
|
+
return { success: false, path: null, error: "No secret provided" };
|
|
498
|
+
}
|
|
499
|
+
|
|
488
500
|
// Try to decrypt each encrypted value
|
|
489
501
|
for (const { path, value } of encryptedValues) {
|
|
490
502
|
try {
|
package/src/index.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ export interface LoadOptions {
|
|
|
12
12
|
cwd?: string;
|
|
13
13
|
/** Whether to apply resolved vars to process.env (default: true) */
|
|
14
14
|
applyToProcess?: boolean;
|
|
15
|
+
/** Whether to expose WRENV_TARGET and WRENV_SECRET in the output (default: false) */
|
|
16
|
+
expose?: boolean;
|
|
17
|
+
/** Error if encrypted values cannot be decrypted (default: false) */
|
|
18
|
+
strict?: boolean;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
/**
|
|
@@ -28,6 +32,10 @@ export function loadEnvJson(options?: {
|
|
|
28
32
|
targetEnv?: string;
|
|
29
33
|
envPath?: string;
|
|
30
34
|
cwd?: string;
|
|
35
|
+
/** Whether to expose WRENV_TARGET and WRENV_SECRET in the output (default: false) */
|
|
36
|
+
expose?: boolean;
|
|
37
|
+
/** Error if encrypted values cannot be decrypted (default: false) */
|
|
38
|
+
strict?: boolean;
|
|
31
39
|
}): EnvRecord;
|
|
32
40
|
|
|
33
41
|
/** Load .env.local overrides if the file exists */
|