envlock-next 0.5.0 → 0.6.1

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
@@ -4,18 +4,149 @@
4
4
  import { pathToFileURL as pathToFileURL2 } from "url";
5
5
  import { realpathSync } from "fs";
6
6
  import { Command } from "commander";
7
- import { ENVIRONMENTS, runWithSecrets, validateEnvFilePath, validateOnePasswordEnvId as validateOnePasswordEnvId2 } from "envlock-core";
8
- import { log as log2, setVerbose } from "envlock-core";
7
+
8
+ // ../../node_modules/.pnpm/envlock-core@0.6.0/node_modules/envlock-core/dist/index.js
9
+ import { spawnSync } from "child_process";
10
+ import { execFileSync } from "child_process";
11
+ import { isAbsolute, relative, resolve } from "path";
12
+ import { createServer } from "net";
13
+ var verbose = false;
14
+ function setVerbose(flag) {
15
+ verbose = flag;
16
+ }
17
+ var log = {
18
+ debug: (msg) => {
19
+ if (verbose) process.stderr.write(`[envlock:debug] ${msg}
20
+ `);
21
+ },
22
+ info: (msg) => {
23
+ process.stderr.write(`[envlock] ${msg}
24
+ `);
25
+ },
26
+ warn: (msg) => {
27
+ process.stderr.write(`[envlock] Warning: ${msg}
28
+ `);
29
+ },
30
+ error: (msg) => {
31
+ process.stderr.write(`[envlock] Error: ${msg}
32
+ `);
33
+ }
34
+ };
35
+ var WHICH = process.platform === "win32" ? "where" : "which";
36
+ function hasBinary(name) {
37
+ try {
38
+ execFileSync(WHICH, [name], { stdio: "pipe" });
39
+ return true;
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+ function checkBinary(name, installHint) {
45
+ if (!hasBinary(name)) {
46
+ throw new Error(`[envlock] '${name}' not found in PATH.
47
+ ${installHint}`);
48
+ }
49
+ log.debug(`Binary check: ${name} found`);
50
+ }
51
+ function runWithSecrets(options) {
52
+ const { envFile, environment, onePasswordEnvId, command, args } = options;
53
+ checkBinary(
54
+ "dotenvx",
55
+ "Install dotenvx: npm install -g @dotenvx/dotenvx\nOr add it as a dev dependency."
56
+ );
57
+ const privateKeyVar = `DOTENV_PRIVATE_KEY_${environment.toUpperCase()}`;
58
+ const keyAlreadyInjected = !!process.env[privateKeyVar];
59
+ let result;
60
+ if (keyAlreadyInjected) {
61
+ log.debug(`Spawning: dotenvx run -f ${envFile} -- ${command} ${args.join(" ")}`);
62
+ result = spawnSync(
63
+ "dotenvx",
64
+ ["run", "-f", envFile, "--", command, ...args],
65
+ { stdio: "inherit" }
66
+ );
67
+ if (result.error) {
68
+ throw new Error(`[envlock] Failed to spawn 'dotenvx': ${result.error.message}`);
69
+ }
70
+ } else {
71
+ checkBinary(
72
+ "op",
73
+ "Install 1Password CLI: brew install --cask 1password-cli@beta\nThen sign in: op signin"
74
+ );
75
+ log.debug(`Spawning: op run --environment ${onePasswordEnvId} -- dotenvx run -f ${envFile} -- ${command} ${args.join(" ")}`);
76
+ result = spawnSync(
77
+ "op",
78
+ [
79
+ "run",
80
+ "--environment",
81
+ onePasswordEnvId,
82
+ "--",
83
+ "dotenvx",
84
+ "run",
85
+ "-f",
86
+ envFile,
87
+ "--",
88
+ command,
89
+ ...args
90
+ ],
91
+ { stdio: "inherit" }
92
+ );
93
+ if (result.error) {
94
+ throw new Error(`[envlock] Failed to spawn 'op': ${result.error.message}`);
95
+ }
96
+ }
97
+ process.exit(result.status ?? 1);
98
+ }
99
+ var OP_ENV_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
100
+ function validateOnePasswordEnvId(id) {
101
+ if (!id || !OP_ENV_ID_PATTERN.test(id)) {
102
+ throw new Error(
103
+ `[envlock] Invalid onePasswordEnvId: "${id}". Must be a lowercase alphanumeric string (hyphens allowed), e.g. 'ca6uypwvab5mevel44gqdc2zae'.`
104
+ );
105
+ }
106
+ }
107
+ function validateEnvFilePath(envFile, cwd) {
108
+ if (envFile.includes("\0")) {
109
+ throw new Error(`[envlock] Invalid env file path: null bytes are not allowed.`);
110
+ }
111
+ const resolved = resolve(cwd, envFile);
112
+ const base = resolve(cwd);
113
+ const rel = relative(base, resolved);
114
+ if (rel.startsWith("..") || isAbsolute(rel)) {
115
+ throw new Error(
116
+ `[envlock] Invalid env file path: "${envFile}" resolves outside the project directory.`
117
+ );
118
+ }
119
+ }
120
+ var ENVIRONMENTS = {
121
+ development: "development",
122
+ staging: "staging",
123
+ production: "production"
124
+ };
125
+ function isPortFree(port) {
126
+ return new Promise((resolve22) => {
127
+ const server = createServer();
128
+ server.once("error", () => resolve22(false));
129
+ server.once("listening", () => server.close(() => resolve22(true)));
130
+ server.listen(port, "127.0.0.1");
131
+ });
132
+ }
133
+ async function findFreePort(preferred) {
134
+ for (let port = preferred; port <= preferred + 10; port++) {
135
+ if (await isPortFree(port)) return port;
136
+ }
137
+ throw new Error(
138
+ `[envlock] No free port found in range ${preferred}\u2013${preferred + 10}.`
139
+ );
140
+ }
9
141
 
10
142
  // src/cli/resolve-config.ts
11
143
  import { existsSync } from "fs";
12
- import { resolve } from "path";
144
+ import { resolve as resolve2 } from "path";
13
145
  import { pathToFileURL } from "url";
14
- import { validateOnePasswordEnvId, log } from "envlock-core";
15
- var CONFIG_CANDIDATES = ["next.config.js", "next.config.mjs"];
146
+ var CONFIG_CANDIDATES = ["next.config.ts", "next.config.js", "next.config.mjs"];
16
147
  async function resolveConfig(cwd) {
17
148
  for (const candidate of CONFIG_CANDIDATES) {
18
- const fullPath = resolve(cwd, candidate);
149
+ const fullPath = resolve2(cwd, candidate);
19
150
  if (!existsSync(fullPath)) continue;
20
151
  try {
21
152
  const mod = await import(pathToFileURL(fullPath).href);
@@ -62,16 +193,31 @@ var ARGUMENT_FLAGS = {
62
193
  async function runNextCommand(subcommand, environment, passthroughArgs) {
63
194
  const config = await resolveConfig(process.cwd());
64
195
  const envFile = config.envFiles?.[environment] ?? DEFAULT_ENV_FILES[environment];
65
- log2.debug(`Environment: ${environment}`);
66
- log2.debug(`Env file: ${envFile}`);
67
- log2.debug(`Command: next ${subcommand} ${passthroughArgs.join(" ")}`);
68
196
  validateEnvFilePath(envFile, process.cwd());
197
+ let finalArgs = [...passthroughArgs];
198
+ if (subcommand === "dev") {
199
+ const portFlagIndex = finalArgs.findIndex(
200
+ (a) => a === "--port" || a === "-p"
201
+ );
202
+ const requestedPort = portFlagIndex !== -1 ? parseInt(finalArgs[portFlagIndex + 1] ?? "3000", 10) : 3e3;
203
+ const freePort = await findFreePort(requestedPort);
204
+ if (freePort !== requestedPort) {
205
+ log.warn(`Port ${requestedPort} in use, switching to ${freePort}`);
206
+ }
207
+ if (portFlagIndex !== -1) {
208
+ finalArgs.splice(portFlagIndex, 2);
209
+ }
210
+ finalArgs = ["-p", String(freePort), ...finalArgs];
211
+ }
212
+ log.debug(`Environment: ${environment}`);
213
+ log.debug(`Env file: ${envFile}`);
214
+ log.debug(`Command: next ${subcommand} ${finalArgs.join(" ")}`);
69
215
  runWithSecrets({
70
216
  envFile,
71
217
  environment,
72
218
  onePasswordEnvId: config.onePasswordEnvId,
73
219
  command: "next",
74
- args: [subcommand, ...passthroughArgs]
220
+ args: [subcommand, ...finalArgs]
75
221
  });
76
222
  }
77
223
  function addEnvFlags(cmd) {
@@ -96,12 +242,12 @@ async function handleRunCommand(cmd, cmdArgs, opts) {
96
242
  "[envlock] No onePasswordEnvId found. Set it in envlock.config.js or via ENVLOCK_OP_ENV_ID env var."
97
243
  );
98
244
  }
99
- validateOnePasswordEnvId2(onePasswordEnvId);
245
+ validateOnePasswordEnvId(onePasswordEnvId);
100
246
  const envFile = config.envFiles?.[environment] ?? DEFAULT_ENV_FILES[environment];
101
247
  validateEnvFilePath(envFile, process.cwd());
102
- log2.debug(`Environment: ${environment}`);
103
- log2.debug(`Env file: ${envFile}`);
104
- log2.debug(`Command: ${cmd} ${cmdArgs.join(" ")}`);
248
+ log.debug(`Environment: ${environment}`);
249
+ log.debug(`Env file: ${envFile}`);
250
+ log.debug(`Command: ${cmd} ${cmdArgs.join(" ")}`);
105
251
  runWithSecrets({
106
252
  envFile,
107
253
  environment,
@@ -130,7 +276,7 @@ addEnvFlags(runCmd).action(
130
276
  try {
131
277
  await handleRunCommand(cmd, cmdArgs, opts);
132
278
  } catch (err) {
133
- log2.error(err instanceof Error ? err.message : String(err));
279
+ log.error(err instanceof Error ? err.message : String(err));
134
280
  process.exit(1);
135
281
  }
136
282
  }
@@ -147,9 +293,10 @@ if (import.meta.url === pathToFileURL2(_resolvedArgv1Next).href) {
147
293
  if (process.argv.includes("--debug") || process.argv.includes("-d")) {
148
294
  setVerbose(true);
149
295
  }
150
- log2.debug(`Resolved argv[1]: ${_resolvedArgv1Next}`);
296
+ log.debug(`Resolved argv[1]: ${_resolvedArgv1Next}`);
151
297
  program.parse(process.argv);
152
298
  }
153
299
  export {
154
- handleRunCommand
300
+ handleRunCommand,
301
+ runNextCommand
155
302
  };
package/dist/index.cjs ADDED
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ withEnvlock: () => withEnvlock
24
+ });
25
+ module.exports = __toCommonJS(src_exports);
26
+
27
+ // src/plugin.ts
28
+ var import_envlock_core = require("envlock-core");
29
+ function withEnvlock(nextConfig, options) {
30
+ if (!options?.onePasswordEnvId) {
31
+ console.warn(
32
+ "[envlock] No onePasswordEnvId provided to withEnvlock(). Set it to your 1Password Environment ID for automatic secret injection. Alternatively, set ENVLOCK_OP_ENV_ID in your environment."
33
+ );
34
+ } else {
35
+ (0, import_envlock_core.validateOnePasswordEnvId)(options.onePasswordEnvId);
36
+ }
37
+ return {
38
+ ...nextConfig,
39
+ __envlock: options ?? { onePasswordEnvId: "" }
40
+ };
41
+ }
42
+ // Annotate the CommonJS export names for ESM import in node:
43
+ 0 && (module.exports = {
44
+ withEnvlock
45
+ });
@@ -0,0 +1,10 @@
1
+ import { NextConfig } from 'next';
2
+ import { EnvlockOptions } from 'envlock-core';
3
+ export { EnvlockOptions } from 'envlock-core';
4
+
5
+ type EnvlockNextConfig = NextConfig & {
6
+ __envlock: EnvlockOptions;
7
+ };
8
+ declare function withEnvlock(nextConfig: NextConfig, options?: EnvlockOptions): EnvlockNextConfig;
9
+
10
+ export { type EnvlockNextConfig, withEnvlock };
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/postinstall.ts
21
+ var postinstall_exports = {};
22
+ __export(postinstall_exports, {
23
+ rewriteScripts: () => rewriteScripts
24
+ });
25
+ module.exports = __toCommonJS(postinstall_exports);
26
+ var import_node_fs = require("fs");
27
+ var import_node_path = require("path");
28
+ var REWRITES = [
29
+ { from: /(?<![envlock\s])next dev/, to: "envlock dev", cmd: "dev" },
30
+ { from: /(?<![envlock\s])next build/, to: "envlock build", cmd: "build" },
31
+ { from: /(?<![envlock\s])next start/, to: "envlock start", cmd: "start" }
32
+ ];
33
+ function rewriteScripts(projectRoot2) {
34
+ const pkgPath = (0, import_node_path.join)(projectRoot2, "package.json");
35
+ if (!(0, import_node_fs.existsSync)(pkgPath)) return;
36
+ let pkg;
37
+ try {
38
+ pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf8"));
39
+ } catch {
40
+ return;
41
+ }
42
+ if (!pkg.scripts) return;
43
+ let changed = false;
44
+ for (const { from, to, cmd } of REWRITES) {
45
+ const script = pkg.scripts[cmd];
46
+ if (!script) continue;
47
+ if (script.includes("envlock")) continue;
48
+ if (!from.test(script)) continue;
49
+ pkg.scripts[cmd] = script.replace(from, to);
50
+ console.log(`[envlock] Updated scripts.${cmd}: "${script}" \u2192 "${pkg.scripts[cmd]}"`);
51
+ changed = true;
52
+ }
53
+ if (changed) {
54
+ (0, import_node_fs.writeFileSync)(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
55
+ }
56
+ }
57
+ var projectRoot = process.env["INIT_CWD"];
58
+ if (projectRoot) {
59
+ rewriteScripts(projectRoot);
60
+ }
61
+ // Annotate the CommonJS export names for ESM import in node:
62
+ 0 && (module.exports = {
63
+ rewriteScripts
64
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envlock-next",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "description": "Next.js plugin and CLI for envlock",
6
6
  "license": "MIT",
@@ -22,10 +22,12 @@
22
22
  },
23
23
  "exports": {
24
24
  ".": {
25
- "import": "./dist/index.js",
26
- "types": "./dist/index.d.ts"
25
+ "types": "./dist/index.d.ts",
26
+ "require": "./dist/index.cjs",
27
+ "import": "./dist/index.js"
27
28
  }
28
29
  },
30
+ "main": "./dist/index.cjs",
29
31
  "files": [
30
32
  "dist"
31
33
  ],
@@ -33,15 +35,20 @@
33
35
  "build": "tsup",
34
36
  "dev": "tsup --watch",
35
37
  "test": "vitest run",
36
- "test:watch": "vitest"
38
+ "test:watch": "vitest",
39
+ "postinstall": "node -e \"const f='./dist/postinstall.cjs';require('fs').existsSync(f)&&require(f)\""
37
40
  },
38
41
  "dependencies": {
39
- "envlock-core": "^0.5.0",
42
+ "envlock-core": "^0.6.0",
40
43
  "commander": "^12.0.0"
41
44
  },
42
45
  "peerDependencies": {
46
+ "@dotenvx/dotenvx": ">=1.0.0",
43
47
  "next": ">=14.0.0"
44
48
  },
49
+ "peerDependenciesMeta": {
50
+ "@dotenvx/dotenvx": { "optional": false }
51
+ },
45
52
  "devDependencies": {
46
53
  "envlock-core": "workspace:*",
47
54
  "@types/node": "^20.14.10",