morphix-env 0.1.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +38 -34
  2. package/dist/cli.js +64 -32
  3. package/package.json +6 -5
package/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # mx-env
1
+ <p align="center">
2
+ <img src="https://morphix.app/brand/logo-rounded.png" width="80" alt="MorphixAI" />
3
+ </p>
4
+
5
+ # morphix-env
2
6
 
3
7
  Environment variable toolkit for multi-project architectures. Combines [Infisical](https://infisical.com) secret management with local override files and client-side runtime injection.
4
8
 
@@ -6,7 +10,7 @@ Environment variable toolkit for multi-project architectures. Combines [Infisica
6
10
 
7
11
  | Problem | Solution |
8
12
  |---------|----------|
9
- | `NEXT_PUBLIC_*` / `VITE_*` baked at build time | `mx-env generate` creates `__env.js` for runtime injection |
13
+ | `NEXT_PUBLIC_*` / `VITE_*` baked at build time | `morphix-env generate` creates `__env.js` for runtime injection |
10
14
  | Scattered env vars across hosting platforms | Single source of truth in Infisical, pulled at startup |
11
15
  | No local override when using remote config | `.env.local` always wins — edit one file, restart |
12
16
  | Different tools for different needs (dotenv, cross-env, infisical CLI) | One tool, one command |
@@ -14,42 +18,42 @@ Environment variable toolkit for multi-project architectures. Combines [Infisica
14
18
  ## Install
15
19
 
16
20
  ```bash
17
- pnpm add -D mx-env
21
+ pnpm add -D morphix-env
18
22
  # or
19
- npm install -D mx-env
23
+ npm install -D morphix-env
20
24
  ```
21
25
 
22
26
  ## Quick Start
23
27
 
24
28
  ```bash
25
29
  # Run a command with .env.local overrides
26
- mx-env run -- next dev
30
+ morphix-env run -- next dev
27
31
 
28
32
  # Generate client-side __env.js
29
- mx-env generate --out public/__env.js
33
+ morphix-env generate --out public/__env.js
30
34
 
31
35
  # Debug: see what's loaded
32
- mx-env inspect
36
+ morphix-env inspect
33
37
  ```
34
38
 
35
39
  ## Commands
36
40
 
37
- ### `mx-env run [options] -- <command>`
41
+ ### `morphix-env run [options] -- <command>`
38
42
 
39
43
  Load environment variables, then execute a command. The child process inherits all injected vars.
40
44
 
41
45
  ```bash
42
46
  # Basic: load .env.local, run dev server
43
- mx-env run -- next dev --turbo -p 3004
47
+ morphix-env run -- next dev --turbo -p 3004
44
48
 
45
49
  # Custom env file
46
- mx-env run -f .env.staging -- npm start
50
+ morphix-env run -f .env.staging -- npm start
47
51
 
48
52
  # Skip Infisical (use only local files)
49
- mx-env run --no-infisical -- npm start
53
+ morphix-env run --no-infisical -- npm start
50
54
 
51
55
  # Verbose: show which vars were loaded
52
- mx-env run -v -- node server.js
56
+ morphix-env run -v -- node server.js
53
57
  ```
54
58
 
55
59
  **Loading priority (high to low):**
@@ -58,19 +62,19 @@ mx-env run -v -- node server.js
58
62
  2. Infisical secrets — pulled via SDK
59
63
  3. Existing `process.env` — Docker ENV, CI vars, etc.
60
64
 
61
- ### `mx-env generate [options]`
65
+ ### `morphix-env generate [options]`
62
66
 
63
67
  Extract public environment variables (`NEXT_PUBLIC_*`, `VITE_*`, `EXPO_PUBLIC_*`) and write them to a JS file for browser runtime injection.
64
68
 
65
69
  ```bash
66
70
  # Default: public/__env.js
67
- mx-env generate
71
+ morphix-env generate
68
72
 
69
73
  # Custom output path (Vite projects)
70
- mx-env generate --out dist/__env.js
74
+ morphix-env generate --out dist/__env.js
71
75
 
72
76
  # Only include specific prefix
73
- mx-env generate --filter NEXT_PUBLIC_
77
+ morphix-env generate --filter NEXT_PUBLIC_
74
78
  ```
75
79
 
76
80
  Output file content:
@@ -94,14 +98,14 @@ function getEnv(key: string, fallback = ''): string {
94
98
  }
95
99
  ```
96
100
 
97
- ### `mx-env inspect [options]`
101
+ ### `morphix-env inspect [options]`
98
102
 
99
103
  Print env var values for debugging. Secrets are masked (first 4 chars shown).
100
104
 
101
105
  ```bash
102
- mx-env inspect
103
- mx-env inspect --filter NEXT_PUBLIC_
104
- mx-env inspect -f .env.production
106
+ morphix-env inspect
107
+ morphix-env inspect --filter NEXT_PUBLIC_
108
+ morphix-env inspect -f .env.production
105
109
  ```
106
110
 
107
111
  ## Options
@@ -116,7 +120,7 @@ mx-env inspect -f .env.production
116
120
 
117
121
  ## Config File
118
122
 
119
- Create `mx-env.config.json` in your project root to declare project-level settings. This file is committed to git.
123
+ Create `morphix-env.config.json` in your project root to declare project-level settings. This file is committed to git.
120
124
 
121
125
  ```json
122
126
  {
@@ -156,14 +160,14 @@ Create `mx-env.config.json` in your project root to declare project-level settin
156
160
  ```json
157
161
  {
158
162
  "scripts": {
159
- "dev": "mx-env run -- next dev --turbo -p 3004",
160
- "build": "mx-env run -- next build",
161
- "start": "mx-env run -- node server.js"
163
+ "dev": "morphix-env run -- next dev --turbo -p 3004",
164
+ "build": "morphix-env run -- next build",
165
+ "start": "morphix-env run -- node server.js"
162
166
  }
163
167
  }
164
168
  ```
165
169
 
166
- `mx-env.config.json`:
170
+ `morphix-env.config.json`:
167
171
 
168
172
  ```json
169
173
  {
@@ -185,8 +189,8 @@ Create `mx-env.config.json` in your project root to declare project-level settin
185
189
  ```json
186
190
  {
187
191
  "scripts": {
188
- "dev": "mx-env run -- vite",
189
- "build": "mx-env run -- vite build"
192
+ "dev": "morphix-env run -- vite",
193
+ "build": "morphix-env run -- vite build"
190
194
  }
191
195
  }
192
196
  ```
@@ -210,8 +214,8 @@ Create `mx-env.config.json` in your project root to declare project-level settin
210
214
  ```json
211
215
  {
212
216
  "scripts": {
213
- "dev": "mx-env run -- tsx watch src/index.ts",
214
- "start": "mx-env run -- node dist/index.js"
217
+ "dev": "morphix-env run -- tsx watch src/index.ts",
218
+ "start": "morphix-env run -- node dist/index.js"
215
219
  }
216
220
  }
217
221
  ```
@@ -241,19 +245,19 @@ ENV INFISICAL_CLIENT_ID=""
241
245
  ENV INFISICAL_CLIENT_SECRET=""
242
246
  ENV DEPLOY_ENV="prod"
243
247
 
244
- CMD ["npx", "mx-env", "run", "--", "node", "server.js"]
248
+ CMD ["npx", "morphix-env", "run", "--", "node", "server.js"]
245
249
  ```
246
250
 
247
251
  No Infisical CLI binary needed in the image.
248
252
 
249
253
  ### Local Development with Infisical CLI
250
254
 
251
- If you already use `infisical run` locally, mx-env still adds value as the override layer:
255
+ If you already use `infisical run` locally, morphix-env still adds value as the override layer:
252
256
 
253
257
  ```json
254
258
  {
255
- "dev": "infisical run --path=/ai --env=dev -- mx-env run -- next dev",
256
- "dev:local": "infisical run --path=/ai --env=dev -- mx-env run -- next dev"
259
+ "dev": "infisical run --path=/ai --env=dev -- morphix-env run -- next dev",
260
+ "dev:local": "infisical run --path=/ai --env=dev -- morphix-env run -- next dev"
257
261
  }
258
262
  ```
259
263
 
@@ -261,7 +265,7 @@ If you already use `infisical run` locally, mx-env still adds value as the overr
261
265
 
262
266
  ## How It Works
263
267
 
264
- 1. Read `mx-env.config.json` for project settings
268
+ 1. Read `morphix-env.config.json` for project settings
265
269
  2. If `INFISICAL_CLIENT_ID` + `INFISICAL_CLIENT_SECRET` exist → fetch secrets via SDK, inject into `process.env` (does not overwrite existing vars)
266
270
  3. Read `.env.local` → inject into `process.env` (overwrites everything, highest priority)
267
271
  4. If `generate` is configured → write `__env.js` with public vars
package/dist/cli.js CHANGED
@@ -26,32 +26,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  // src/env.ts
27
27
  var import_fs = require("fs");
28
28
  var import_path = require("path");
29
+ var import_dotenv = require("dotenv");
29
30
  function parseEnvFile(filePath) {
30
31
  const absPath = (0, import_path.resolve)(filePath);
31
32
  if (!(0, import_fs.existsSync)(absPath)) return {};
32
- const vars = {};
33
- const content = (0, import_fs.readFileSync)(absPath, "utf8");
34
- for (const raw of content.split("\n")) {
35
- const line = raw.trim();
36
- if (!line || line.startsWith("#")) continue;
37
- const eq = line.indexOf("=");
38
- if (eq === -1) continue;
39
- const key = line.slice(0, eq).trim();
40
- let value = line.slice(eq + 1).trim();
41
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
42
- value = value.slice(1, -1);
43
- }
44
- vars[key] = value;
45
- }
46
- return vars;
33
+ const result = (0, import_dotenv.config)({ path: absPath, override: false });
34
+ return result.parsed || {};
47
35
  }
48
36
  function loadEnvFiles(files) {
49
37
  const overrides = [];
50
38
  for (const file of files) {
51
- const vars = parseEnvFile(file);
52
- for (const [key, value] of Object.entries(vars)) {
53
- process.env[key] = value;
54
- overrides.push({ key, source: file });
39
+ const absPath = (0, import_path.resolve)(file);
40
+ if (!(0, import_fs.existsSync)(absPath)) continue;
41
+ const result = (0, import_dotenv.config)({ path: absPath, override: true });
42
+ if (result.parsed) {
43
+ for (const key of Object.keys(result.parsed)) {
44
+ overrides.push({ key, source: file });
45
+ }
55
46
  }
56
47
  }
57
48
  return overrides;
@@ -69,6 +60,8 @@ function extractPublicVars() {
69
60
 
70
61
  // src/infisical.ts
71
62
  var import_sdk = require("@infisical/sdk");
63
+ var import_child_process = require("child_process");
64
+ var import_dotenv2 = require("dotenv");
72
65
  function getInfisicalConfig() {
73
66
  const clientId = process.env.INFISICAL_CLIENT_ID;
74
67
  const clientSecret = process.env.INFISICAL_CLIENT_SECRET;
@@ -107,6 +100,32 @@ async function fetchInfisicalSecrets(config) {
107
100
  }
108
101
  return count;
109
102
  }
103
+ function hasInfisicalCLI() {
104
+ try {
105
+ (0, import_child_process.execSync)("infisical --version", { stdio: "pipe" });
106
+ return true;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+ function fetchSecretsViaCLI(environment, paths) {
112
+ let count = 0;
113
+ for (const secretPath of paths) {
114
+ try {
115
+ const output = (0, import_child_process.execSync)(
116
+ `infisical export --path=${secretPath} --env=${environment} --format=dotenv`,
117
+ { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }
118
+ );
119
+ const vars = (0, import_dotenv2.parse)(output);
120
+ for (const [key, value] of Object.entries(vars)) {
121
+ process.env[key] = value;
122
+ count++;
123
+ }
124
+ } catch {
125
+ }
126
+ }
127
+ return count;
128
+ }
110
129
 
111
130
  // src/config.ts
112
131
  var import_fs2 = require("fs");
@@ -137,7 +156,7 @@ function loadConfig() {
137
156
  var import_cross_spawn = __toESM(require("cross-spawn"));
138
157
  var import_fs3 = require("fs");
139
158
  var import_path3 = require("path");
140
- var VERSION = "0.1.0";
159
+ var VERSION = "0.3.0";
141
160
  var DEFAULT_ENV_FILE = ".env.local";
142
161
  function parseArgs(argv) {
143
162
  const args = {
@@ -197,28 +216,41 @@ function mergeWithConfig(args, config) {
197
216
  }
198
217
  async function loadAllEnv(args, config) {
199
218
  if (!args.noInfisical) {
219
+ const env = config.infisical?.env || process.env.DEPLOY_ENV || process.env.INFISICAL_ENV || "dev";
220
+ const paths = config.infisical?.paths || ["/"];
200
221
  const infisicalConfig = config.infisical ? {
201
222
  clientId: process.env.INFISICAL_CLIENT_ID || "",
202
223
  clientSecret: process.env.INFISICAL_CLIENT_SECRET || "",
203
224
  projectId: config.infisical.projectId,
204
- environment: config.infisical.env || "dev",
205
- paths: config.infisical.paths || ["/"],
225
+ environment: env,
226
+ paths,
206
227
  siteUrl: config.infisical.siteUrl
207
228
  } : getInfisicalConfig();
208
229
  if (infisicalConfig && infisicalConfig.clientId && infisicalConfig.clientSecret) {
209
230
  try {
210
231
  const count = await fetchInfisicalSecrets(infisicalConfig);
211
- console.log(`[mx-env] Infisical: loaded ${count} secrets (${infisicalConfig.environment}: ${infisicalConfig.paths.join(", ")})`);
232
+ console.log(`[morphix-env] Infisical SDK: loaded ${count} secrets (${env}: ${paths.join(", ")})`);
233
+ } catch (e) {
234
+ console.warn(`[morphix-env] Infisical SDK: failed - ${e.message}`);
235
+ }
236
+ } else if (hasInfisicalCLI()) {
237
+ try {
238
+ const count = fetchSecretsViaCLI(env, paths);
239
+ if (count > 0) {
240
+ console.log(`[morphix-env] Infisical CLI: loaded ${count} secrets (${env}: ${paths.join(", ")})`);
241
+ } else {
242
+ console.log(`[morphix-env] Infisical CLI: no secrets found (run 'infisical login' first?)`);
243
+ }
212
244
  } catch (e) {
213
- console.warn(`[mx-env] Infisical: failed - ${e.message}`);
245
+ console.warn(`[morphix-env] Infisical CLI: failed - ${e.message}`);
214
246
  }
215
247
  } else if (args.verbose) {
216
- console.log("[mx-env] Infisical: skipped (no credentials)");
248
+ console.log("[morphix-env] Infisical: skipped (no SDK credentials, no CLI)");
217
249
  }
218
250
  }
219
251
  const overrides = loadEnvFiles(args.envFiles);
220
252
  if (overrides.length > 0) {
221
- console.log(`[mx-env] Loaded ${overrides.length} overrides from ${args.envFiles.join(", ")}`);
253
+ console.log(`[morphix-env] Loaded ${overrides.length} overrides from ${args.envFiles.join(", ")}`);
222
254
  if (args.verbose) {
223
255
  for (const o of overrides) {
224
256
  console.log(` ${o.key} (from ${o.source})`);
@@ -228,7 +260,7 @@ async function loadAllEnv(args, config) {
228
260
  }
229
261
  async function cmdRun(args, config) {
230
262
  if (args.subArgs.length === 0) {
231
- console.error("[mx-env] No command specified. Usage: mx-env run -- <command>");
263
+ console.error("[morphix-env] No command specified. Usage: mx-env run -- <command>");
232
264
  process.exit(1);
233
265
  }
234
266
  await loadAllEnv(args, config);
@@ -241,7 +273,7 @@ async function cmdRun(args, config) {
241
273
  env: process.env
242
274
  });
243
275
  if (result.error) {
244
- console.error(`[mx-env] Failed to execute: ${cmd}`, result.error.message);
276
+ console.error(`[morphix-env] Failed to execute: ${cmd}`, result.error.message);
245
277
  process.exit(1);
246
278
  }
247
279
  process.exit(result.status ?? 1);
@@ -256,7 +288,7 @@ function generateClientEnv(outFile, filter) {
256
288
  const js = `window.__ENV=${JSON.stringify(vars)};`;
257
289
  (0, import_fs3.mkdirSync)((0, import_path3.dirname)(outFile), { recursive: true });
258
290
  (0, import_fs3.writeFileSync)(outFile, js);
259
- console.log(`[mx-env] Generated ${outFile} (${Object.keys(vars).length} client vars)`);
291
+ console.log(`[morphix-env] Generated ${outFile} (${Object.keys(vars).length} client vars)`);
260
292
  }
261
293
  async function cmdGenerate(args, config) {
262
294
  const outFile = args.outFile || "public/__env.js";
@@ -297,7 +329,7 @@ process.env public vars: (${filtered.length})`);
297
329
  }
298
330
  function showHelp() {
299
331
  console.log(`
300
- mx-env v${VERSION} \u2014 MorphixAI environment variable toolkit
332
+ morphix-env v${VERSION} \u2014 MorphixAI environment variable toolkit
301
333
 
302
334
  Usage:
303
335
  mx-env run [options] -- <command> Load env + exec command
@@ -364,13 +396,13 @@ async function main() {
364
396
  break;
365
397
  default:
366
398
  if (args.command) {
367
- console.error(`[mx-env] Unknown command: ${args.command}`);
399
+ console.error(`[morphix-env] Unknown command: ${args.command}`);
368
400
  }
369
401
  showHelp();
370
402
  process.exit(args.command ? 1 : 0);
371
403
  }
372
404
  }
373
405
  main().catch((e) => {
374
- console.error("[mx-env] Fatal:", e.message);
406
+ console.error("[morphix-env] Fatal:", e.message);
375
407
  process.exit(1);
376
408
  });
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "morphix-env",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Environment variable toolkit — Infisical integration, local overrides, and client-side runtime injection for Next.js / Vite / Express.",
5
5
  "author": "MorphixAI <dev@morphixai.com>",
6
- "homepage": "https://github.com/Morphicai/mx-env#readme",
6
+ "homepage": "https://github.com/Morphicai/morphix-env#readme",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/Morphicai/mx-env.git"
9
+ "url": "git+https://github.com/Morphicai/morphix-env.git"
10
10
  },
11
11
  "bugs": {
12
- "url": "https://github.com/Morphicai/mx-env/issues"
12
+ "url": "https://github.com/Morphicai/morphix-env/issues"
13
13
  },
14
14
  "bin": {
15
15
  "morphix-env": "./dist/cli.js"
@@ -49,6 +49,7 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@infisical/sdk": "^5.0.0",
52
- "cross-spawn": "^7.0.6"
52
+ "cross-spawn": "^7.0.6",
53
+ "dotenv": "^17.3.1"
53
54
  }
54
55
  }