@saasak/tool-env 1.2.2 → 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 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), { "--": true });
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
- const command = args._[0] || "write";
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(
@@ -280,6 +293,7 @@ async function addCommand() {
280
293
  async function runCommand() {
281
294
  const commandParts = args["--"] || [];
282
295
  const [cmd, ...cmdArgs] = commandParts;
296
+ const expose = !!args.expose
283
297
 
284
298
  if (!cmd) {
285
299
  console.error("Usage: wrenv run [options] -- <command> [args...]");
@@ -294,6 +308,8 @@ async function runCommand() {
294
308
  targetEnv: args.target,
295
309
  envPath: args.env,
296
310
  applyToProcess: false,
311
+ expose,
312
+ strict: !!args.strict
297
313
  });
298
314
  } catch (error) {
299
315
  console.error(`wrenv: ${error.message}`);
@@ -320,7 +336,7 @@ async function runCommand() {
320
336
  function helpCommand() {
321
337
  console.log("Usage: wrenv [command] [options]\n");
322
338
  console.log("Commands:");
323
- console.log(" write Write .env files for all packages (default)");
339
+ console.log(" write Write .env files for all packages");
324
340
  console.log(
325
341
  " show Show environment variables without writing files",
326
342
  );
@@ -343,7 +359,10 @@ function helpCommand() {
343
359
  " --target <env> Target environment (dev, staging, prod, all)",
344
360
  );
345
361
  console.log(
346
- " --env <path> Path to env file (default: .env.json)\n",
362
+ " --env <path> Path to env file (default: .env.json)",
363
+ );
364
+ console.log(
365
+ " --strict Error if encrypted values cannot be decrypted\n",
347
366
  );
348
367
  console.log("Environment Variables:");
349
368
  console.log(" WRENV_SECRET Secret for encryption/decryption");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@saasak/tool-env",
3
3
  "license": "MIT",
4
- "version": "1.2.2",
4
+ "version": "1.4.0",
5
5
  "author": "dev@saasak.studio",
6
6
  "description": "A small util to manage environment variables for your monorepo",
7
7
  "keywords": [
package/src/core.js CHANGED
@@ -16,6 +16,8 @@ import { findMonorepoPackages } from "./utils-pkg.js";
16
16
  * @property {string} [envPath] - Path to env.json file (default: '.env.json')
17
17
  * @property {string} [cwd] - Working directory (default: process.cwd())
18
18
  * @property {boolean} [applyToProcess] - Whether to apply to process.env (default: true)
19
+ * @property {boolean} [expose] - wether to expose the secret or not
20
+ * @property {boolean} [strict] - Error if encrypted values cannot be decrypted
19
21
  */
20
22
 
21
23
  /**
@@ -24,6 +26,8 @@ import { findMonorepoPackages } from "./utils-pkg.js";
24
26
  * @property {string} [targetEnv] - Target environment
25
27
  * @property {string} [envPath] - Path to env.json file (default: '.env.json')
26
28
  * @property {string} [cwd] - Working directory (default: process.cwd())
29
+ * @property {boolean} [expose] - wether to expose the secret or not
30
+ * @property {boolean} [strict] - Error if encrypted values cannot be decrypted
27
31
  */
28
32
 
29
33
  /**
@@ -288,6 +292,8 @@ export function loadEnvJson(options = {}) {
288
292
  secret: secretParam,
289
293
  envPath = ".env.json",
290
294
  cwd = process.cwd(),
295
+ expose = false,
296
+ strict = false
291
297
  } = options;
292
298
 
293
299
  const secret = resolveSecret(secretParam);
@@ -303,6 +309,13 @@ export function loadEnvJson(options = {}) {
303
309
  const envContent = fs.readFileSync(envFile, "utf8");
304
310
  const env = JSON.parse(envContent);
305
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
+
306
319
  const allowedEnvs =
307
320
  env && env.envs && Array.isArray(env.envs) ? env.envs : [DEFAULT_ENV];
308
321
  const resolvedTarget = allowedEnvs.find((e) => e === targetEnv);
@@ -341,7 +354,13 @@ export function loadEnvJson(options = {}) {
341
354
  const currentPackageName = findNearestPackageName(cwd, scope);
342
355
 
343
356
  // Return env for current package (with fallback to root)
344
- return allEnvs[currentPackageName] || allEnvs.root || {};
357
+ const finalEnv = allEnvs[currentPackageName] || allEnvs.root || {};
358
+
359
+ return expose ? {
360
+ ...finalEnv,
361
+ WRENV_TARGET: resolvedTarget,
362
+ WRENV_SECRET: secret
363
+ } : finalEnv;
345
364
  }
346
365
 
347
366
  /**
@@ -369,6 +388,8 @@ export function load(options = {}) {
369
388
  envPath,
370
389
  cwd = process.cwd(),
371
390
  applyToProcess = true,
391
+ expose = false,
392
+ strict = false
372
393
  } = options;
373
394
 
374
395
  // Resolve target first to determine if we're in dev mode
@@ -380,6 +401,8 @@ export function load(options = {}) {
380
401
  targetEnv: resolvedTarget,
381
402
  envPath,
382
403
  cwd,
404
+ expose,
405
+ strict
383
406
  });
384
407
 
385
408
  // Load .env.local overrides only in dev mode (security: prevent local overrides in production)
@@ -398,10 +421,12 @@ export function load(options = {}) {
398
421
  }
399
422
 
400
423
  // - Reserved vars (WRENV_*, TARGET_*) are never injected
401
- // - NODE_ENV is only injected if not already set
402
- for (const key of RESERVED_ENV_VARS) {
403
- delete finalConfig[key];
424
+ if (!expose) {
425
+ for (const key of RESERVED_ENV_VARS) {
426
+ delete finalConfig[key];
427
+ }
404
428
  }
429
+ // - NODE_ENV is only injected if not already set
405
430
  if (process.env.NODE_ENV) {
406
431
  delete finalConfig.NODE_ENV;
407
432
  }
@@ -433,10 +458,6 @@ export function verifyCanDecrypt(env, secret) {
433
458
  /** @type {EncryptedValueInfo[]} */
434
459
  const encryptedValues = [];
435
460
 
436
- if (!secret) {
437
- return { success: false, path: null, error: "No secret provided" };
438
- }
439
-
440
461
  // Collect all encrypted values from variables
441
462
  if (env.variables) {
442
463
  for (const [varName, varValue] of Object.entries(env.variables)) {
@@ -472,6 +493,10 @@ export function verifyCanDecrypt(env, secret) {
472
493
  return { success: true };
473
494
  }
474
495
 
496
+ if (!secret) {
497
+ return { success: false, path: null, error: "No secret provided" };
498
+ }
499
+
475
500
  // Try to decrypt each encrypted value
476
501
  for (const { path, value } of encryptedValues) {
477
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 */