envlock-core 0.3.0 → 0.5.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/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/cli/index.ts
4
4
  import { pathToFileURL as pathToFileURL2 } from "url";
5
+ import { realpathSync } from "fs";
5
6
 
6
7
  // src/types.ts
7
8
  var ENVIRONMENTS = {
@@ -15,6 +16,32 @@ import { spawnSync } from "child_process";
15
16
 
16
17
  // src/detect.ts
17
18
  import { execFileSync } from "child_process";
19
+
20
+ // src/logger.ts
21
+ var verbose = false;
22
+ function setVerbose(flag) {
23
+ verbose = flag;
24
+ }
25
+ var log = {
26
+ debug: (msg) => {
27
+ if (verbose) process.stderr.write(`[envlock:debug] ${msg}
28
+ `);
29
+ },
30
+ info: (msg) => {
31
+ process.stderr.write(`[envlock] ${msg}
32
+ `);
33
+ },
34
+ warn: (msg) => {
35
+ process.stderr.write(`[envlock] Warning: ${msg}
36
+ `);
37
+ },
38
+ error: (msg) => {
39
+ process.stderr.write(`[envlock] Error: ${msg}
40
+ `);
41
+ }
42
+ };
43
+
44
+ // src/detect.ts
18
45
  var WHICH = process.platform === "win32" ? "where" : "which";
19
46
  function hasBinary(name) {
20
47
  try {
@@ -26,10 +53,10 @@ function hasBinary(name) {
26
53
  }
27
54
  function checkBinary(name, installHint) {
28
55
  if (!hasBinary(name)) {
29
- console.error(`[envlock] '${name}' not found in PATH.
56
+ throw new Error(`[envlock] '${name}' not found in PATH.
30
57
  ${installHint}`);
31
- process.exit(1);
32
58
  }
59
+ log.debug(`Binary check: ${name} found`);
33
60
  }
34
61
 
35
62
  // src/invoke.ts
@@ -43,16 +70,21 @@ function runWithSecrets(options) {
43
70
  const keyAlreadyInjected = !!process.env[privateKeyVar];
44
71
  let result;
45
72
  if (keyAlreadyInjected) {
73
+ log.debug(`Spawning: dotenvx run -f ${envFile} -- ${command} ${args.join(" ")}`);
46
74
  result = spawnSync(
47
75
  "dotenvx",
48
76
  ["run", "-f", envFile, "--", command, ...args],
49
77
  { stdio: "inherit" }
50
78
  );
79
+ if (result.error) {
80
+ throw new Error(`[envlock] Failed to spawn 'dotenvx': ${result.error.message}`);
81
+ }
51
82
  } else {
52
83
  checkBinary(
53
84
  "op",
54
85
  "Install 1Password CLI: brew install --cask 1password-cli@beta\nThen sign in: op signin"
55
86
  );
87
+ log.debug(`Spawning: op run --environment ${onePasswordEnvId} -- dotenvx run -f ${envFile} -- ${command} ${args.join(" ")}`);
56
88
  result = spawnSync(
57
89
  "op",
58
90
  [
@@ -70,6 +102,9 @@ function runWithSecrets(options) {
70
102
  ],
71
103
  { stdio: "inherit" }
72
104
  );
105
+ if (result.error) {
106
+ throw new Error(`[envlock] Failed to spawn 'op': ${result.error.message}`);
107
+ }
73
108
  }
74
109
  process.exit(result.status ?? 1);
75
110
  }
@@ -114,12 +149,12 @@ async function resolveConfig(cwd) {
114
149
  const mod = await import(pathToFileURL(fullPath).href);
115
150
  const config = mod.default ?? mod;
116
151
  if (config && typeof config === "object") {
152
+ log.debug(`Config loaded from ${candidate}`);
117
153
  return config;
118
154
  }
119
155
  } catch (err) {
120
- console.warn(
121
- `[envlock] Failed to load ${candidate}: ${err instanceof Error ? err.message : String(err)}`
122
- );
156
+ log.warn(`Failed to load ${candidate}: ${err instanceof Error ? err.message : String(err)}`);
157
+ log.debug(`Stack: ${err instanceof Error ? err.stack ?? "" : ""}`);
123
158
  }
124
159
  }
125
160
  return null;
@@ -145,6 +180,11 @@ function splitCommand(cmd) {
145
180
  return parts;
146
181
  }
147
182
  async function run(argv, cwd = process.cwd()) {
183
+ const debugIdx = argv.findIndex((a) => a === "--debug" || a === "-d");
184
+ if (debugIdx !== -1) {
185
+ setVerbose(true);
186
+ argv = argv.filter((_, i) => i !== debugIdx);
187
+ }
148
188
  const environment = argv.includes(ARGUMENT_FLAGS.production) ? ENVIRONMENTS.production : argv.includes(ARGUMENT_FLAGS.staging) ? ENVIRONMENTS.staging : ENVIRONMENTS.development;
149
189
  const passthrough = argv.filter(
150
190
  (f) => f !== ARGUMENT_FLAGS.staging && f !== ARGUMENT_FLAGS.production
@@ -157,7 +197,21 @@ async function run(argv, cwd = process.cwd()) {
157
197
  const available = config?.commands ? Object.keys(config.commands).join(", ") : "none";
158
198
  throw new Error(`[envlock] No command specified. Available commands: ${available}`);
159
199
  }
160
- if (config?.commands && firstArg in config.commands) {
200
+ if (firstArg === "run") {
201
+ if (config?.commands?.["run"]) {
202
+ log.warn(
203
+ '"run" is a reserved subcommand. The config command named "run" is ignored.\nRename it in envlock.config.js to use it as a named command.'
204
+ );
205
+ }
206
+ const runArgs = passthrough.slice(1);
207
+ if (runArgs.length === 0) {
208
+ throw new Error(
209
+ "[envlock] Usage: envlock run <command> [args...]\nExample: envlock run node server.js --port 4000"
210
+ );
211
+ }
212
+ command = runArgs[0];
213
+ args = runArgs.slice(1);
214
+ } else if (config?.commands && firstArg in config.commands) {
161
215
  const cmdString = config.commands[firstArg];
162
216
  if (!cmdString || cmdString.trim() === "") {
163
217
  throw new Error(`[envlock] Command "${firstArg}" is empty in envlock.config.js.`);
@@ -182,11 +236,25 @@ async function run(argv, cwd = process.cwd()) {
182
236
  validateOnePasswordEnvId(onePasswordEnvId);
183
237
  const envFile = config?.envFiles?.[environment] ?? DEFAULT_ENV_FILES[environment];
184
238
  validateEnvFilePath(envFile, cwd);
239
+ log.debug(`Environment: ${environment}`);
240
+ log.debug(`Env file: ${envFile}`);
241
+ log.debug(`Command: ${command} ${args.join(" ")}`);
185
242
  runWithSecrets({ envFile, environment, onePasswordEnvId, command, args });
186
243
  }
187
- if (import.meta.url === pathToFileURL2(process.argv[1] ?? "").href) {
244
+ var _resolvedArgv1 = (() => {
245
+ try {
246
+ return realpathSync(process.argv[1] ?? "");
247
+ } catch {
248
+ return process.argv[1] ?? "";
249
+ }
250
+ })();
251
+ if (import.meta.url === pathToFileURL2(_resolvedArgv1).href) {
252
+ if (process.argv.includes("--debug") || process.argv.includes("-d")) {
253
+ setVerbose(true);
254
+ }
255
+ log.debug(`Resolved argv[1]: ${_resolvedArgv1}`);
188
256
  run(process.argv.slice(2)).catch((err) => {
189
- console.error(err instanceof Error ? err.message : String(err));
257
+ log.error(err instanceof Error ? err.message : String(err));
190
258
  process.exit(1);
191
259
  });
192
260
  }
package/dist/index.d.ts CHANGED
@@ -33,4 +33,12 @@ declare function checkBinary(name: string, installHint: string): void;
33
33
  declare function validateOnePasswordEnvId(id: string): void;
34
34
  declare function validateEnvFilePath(envFile: string, cwd: string): void;
35
35
 
36
- export { ENVIRONMENTS, type Environment, type EnvlockConfig, type EnvlockOptions, type RunWithSecretsOptions, checkBinary, hasBinary, runWithSecrets, validateEnvFilePath, validateOnePasswordEnvId };
36
+ declare function setVerbose(flag: boolean): void;
37
+ declare const log: {
38
+ debug: (msg: string) => void;
39
+ info: (msg: string) => void;
40
+ warn: (msg: string) => void;
41
+ error: (msg: string) => void;
42
+ };
43
+
44
+ export { ENVIRONMENTS, type Environment, type EnvlockConfig, type EnvlockOptions, type RunWithSecretsOptions, checkBinary, hasBinary, log, runWithSecrets, setVerbose, validateEnvFilePath, validateOnePasswordEnvId };
package/dist/index.js CHANGED
@@ -3,6 +3,32 @@ import { spawnSync } from "child_process";
3
3
 
4
4
  // src/detect.ts
5
5
  import { execFileSync } from "child_process";
6
+
7
+ // src/logger.ts
8
+ var verbose = false;
9
+ function setVerbose(flag) {
10
+ verbose = flag;
11
+ }
12
+ var log = {
13
+ debug: (msg) => {
14
+ if (verbose) process.stderr.write(`[envlock:debug] ${msg}
15
+ `);
16
+ },
17
+ info: (msg) => {
18
+ process.stderr.write(`[envlock] ${msg}
19
+ `);
20
+ },
21
+ warn: (msg) => {
22
+ process.stderr.write(`[envlock] Warning: ${msg}
23
+ `);
24
+ },
25
+ error: (msg) => {
26
+ process.stderr.write(`[envlock] Error: ${msg}
27
+ `);
28
+ }
29
+ };
30
+
31
+ // src/detect.ts
6
32
  var WHICH = process.platform === "win32" ? "where" : "which";
7
33
  function hasBinary(name) {
8
34
  try {
@@ -14,10 +40,10 @@ function hasBinary(name) {
14
40
  }
15
41
  function checkBinary(name, installHint) {
16
42
  if (!hasBinary(name)) {
17
- console.error(`[envlock] '${name}' not found in PATH.
43
+ throw new Error(`[envlock] '${name}' not found in PATH.
18
44
  ${installHint}`);
19
- process.exit(1);
20
45
  }
46
+ log.debug(`Binary check: ${name} found`);
21
47
  }
22
48
 
23
49
  // src/invoke.ts
@@ -31,16 +57,21 @@ function runWithSecrets(options) {
31
57
  const keyAlreadyInjected = !!process.env[privateKeyVar];
32
58
  let result;
33
59
  if (keyAlreadyInjected) {
60
+ log.debug(`Spawning: dotenvx run -f ${envFile} -- ${command} ${args.join(" ")}`);
34
61
  result = spawnSync(
35
62
  "dotenvx",
36
63
  ["run", "-f", envFile, "--", command, ...args],
37
64
  { stdio: "inherit" }
38
65
  );
66
+ if (result.error) {
67
+ throw new Error(`[envlock] Failed to spawn 'dotenvx': ${result.error.message}`);
68
+ }
39
69
  } else {
40
70
  checkBinary(
41
71
  "op",
42
72
  "Install 1Password CLI: brew install --cask 1password-cli@beta\nThen sign in: op signin"
43
73
  );
74
+ log.debug(`Spawning: op run --environment ${onePasswordEnvId} -- dotenvx run -f ${envFile} -- ${command} ${args.join(" ")}`);
44
75
  result = spawnSync(
45
76
  "op",
46
77
  [
@@ -58,6 +89,9 @@ function runWithSecrets(options) {
58
89
  ],
59
90
  { stdio: "inherit" }
60
91
  );
92
+ if (result.error) {
93
+ throw new Error(`[envlock] Failed to spawn 'op': ${result.error.message}`);
94
+ }
61
95
  }
62
96
  process.exit(result.status ?? 1);
63
97
  }
@@ -96,7 +130,9 @@ export {
96
130
  ENVIRONMENTS,
97
131
  checkBinary,
98
132
  hasBinary,
133
+ log,
99
134
  runWithSecrets,
135
+ setVerbose,
100
136
  validateEnvFilePath,
101
137
  validateOnePasswordEnvId
102
138
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envlock-core",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Core 1Password + dotenvx secret injection logic for envlock",
6
6
  "license": "MIT",
@@ -29,16 +29,16 @@
29
29
  "files": [
30
30
  "dist"
31
31
  ],
32
- "devDependencies": {
33
- "@types/node": "^20.14.10",
34
- "tsup": "^8.0.0",
35
- "typescript": "^5.8.2",
36
- "vitest": "^3.0.0"
37
- },
38
32
  "scripts": {
39
33
  "build": "tsup",
40
34
  "dev": "tsup --watch",
41
35
  "test": "vitest run",
42
36
  "test:watch": "vitest"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.14.10",
40
+ "tsup": "^8.0.0",
41
+ "typescript": "^5.8.2",
42
+ "vitest": "^3.0.0"
43
43
  }
44
- }
44
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Benjamin Davies
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.