@entrinsik/vite-plugin-informer 2.3.0 → 2.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/deploy.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import dotenv from 'dotenv';
4
3
  import { readFile, writeFile } from 'node:fs/promises';
5
4
  import { resolve } from 'node:path';
6
5
  import { deploy } from '../src/deploy.js';
6
+ import { loadEnv, parseModeArg } from '../src/env.js';
7
7
 
8
- dotenv.config();
8
+ const mode = parseModeArg(process.argv);
9
+ loadEnv({ mode });
9
10
 
10
11
  const baseUrl = process.env.INFORMER_URL;
11
12
  const apiKey = process.env.INFORMER_API_KEY;
package/bin/workspace.js CHANGED
@@ -1,22 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import dotenv from 'dotenv';
4
3
  import { readFile } from 'node:fs/promises';
5
4
  import { resolve } from 'node:path';
6
5
  import { createClient } from '../src/client.js';
6
+ import { loadEnv, envWritePath, parseModeArg } from '../src/env.js';
7
7
  import { init, migrate, reset } from '../src/workspace.js';
8
8
 
9
- dotenv.config();
9
+ const mode = parseModeArg(process.argv);
10
+ loadEnv({ mode });
10
11
 
11
- const command = process.argv[2];
12
+ // Strip --mode <value> from argv before parsing the command
13
+ const args = process.argv.slice(2).filter((arg, i, arr) => arg !== '--mode' && arr[i - 1] !== '--mode');
14
+ const command = args[0];
12
15
 
13
16
  if (!command || !['init', 'migrate', 'reset'].includes(command)) {
14
- console.error('Usage: informer-workspace <init|migrate|reset>');
17
+ console.error('Usage: informer-workspace <init|migrate|reset> [--mode <name>]');
15
18
  console.error('');
16
19
  console.error('Commands:');
17
20
  console.error(' init Create a dev workspace datasource and run migrations');
18
21
  console.error(' migrate Run pending migrations against the dev workspace');
19
22
  console.error(' reset Drop all tables and re-run all migrations');
23
+ console.error('');
24
+ console.error('Options:');
25
+ console.error(' --mode <name> Load .env.<name> (e.g. --mode test, --mode production)');
20
26
  process.exit(1);
21
27
  }
22
28
 
@@ -33,7 +39,7 @@ if (!baseUrl || (!apiKey && (!user || !pass))) {
33
39
 
34
40
  const api = createClient({ baseUrl, apiKey, user, pass });
35
41
  const migrationsDir = resolve('migrations');
36
- const envPath = resolve('.env');
42
+ const envPath = envWritePath({ mode });
37
43
 
38
44
  try {
39
45
  if (command === 'init') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entrinsik/vite-plugin-informer",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Vite plugin and deploy tool for Informer App development",
5
5
  "repository": {
6
6
  "type": "git",
package/src/agent-dev.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createHmac } from 'node:crypto';
1
2
  import { readFile, readdir, stat, access } from 'node:fs/promises';
2
3
  import { join } from 'node:path';
3
4
  import { parse as parseUrl } from 'node:url';
@@ -192,6 +193,33 @@ export function createAgentMiddleware(viteServer, { serverOrigin, authHeader, de
192
193
  return { ok: true };
193
194
  }
194
195
 
196
+ // crypto helper — mirrors the sandbox's crypto.hmac()
197
+ const cryptoHelper = {
198
+ hmac(algorithm, key, data, encoding) {
199
+ return createHmac(algorithm, key).update(data).digest(encoding || 'hex');
200
+ }
201
+ };
202
+
203
+ // log helper — mirrors sandbox log(message, data) + log.info/warn/error/debug
204
+ const logCall = (level, message, data) => {
205
+ const msg = typeof message === 'string' ? message : JSON.stringify(message);
206
+ const args = [`[app-log] [${level}] ${msg}`];
207
+ if (data) args.push(data);
208
+ console.log(...args);
209
+ };
210
+ const log = Object.assign(
211
+ (message, data) => logCall('info', message, data),
212
+ {
213
+ debug: (message, data) => logCall('debug', message, data),
214
+ info: (message, data) => logCall('info', message, data),
215
+ warn: (message, data) => logCall('warn', message, data),
216
+ error: (message, data) => logCall('error', message, data)
217
+ }
218
+ );
219
+
220
+ // markdown helper — passthrough in dev (production uses `marked`)
221
+ const markdown = (text) => text;
222
+
195
223
  return async function agentDevMiddleware(req, res, next) {
196
224
  try {
197
225
  const parsed = parseUrl(req.url, true);
@@ -366,7 +394,7 @@ export function createAgentMiddleware(viteServer, { serverOrigin, authHeader, de
366
394
 
367
395
  if (tool) {
368
396
  try {
369
- result = await tool.handler(tc.input, { query, fetch: apiFetch, emit, context: triggerEvent });
397
+ result = await tool.handler(tc.input, { query, fetch: apiFetch, emit, crypto: cryptoHelper, markdown, log, context: triggerEvent });
370
398
  } catch (err) {
371
399
  console.error(`[agent-dev] Tool "${tc.name}" failed:`, err.message);
372
400
  result = { error: err.message };
package/src/env.js ADDED
@@ -0,0 +1,86 @@
1
+ import dotenv from 'dotenv';
2
+ import { existsSync } from 'node:fs';
3
+ import { resolve, dirname, parse as parsePath } from 'node:path';
4
+
5
+ /**
6
+ * Build an ordered list of .env file paths for dotenv to load.
7
+ *
8
+ * Priority (first match wins in dotenv):
9
+ * 1. <cwd>/.env.<mode> (mode-specific, e.g. .env.test)
10
+ * 2. <cwd>/.env (local defaults)
11
+ * 3. <parent>/.env.<mode> (walk up for monorepo shared config)
12
+ * 4. <parent>/.env
13
+ * ... continues up to first parent that contains a .env file
14
+ *
15
+ * When mode is 'development' (Vite default), mode-specific files are skipped
16
+ * since .env already serves that purpose.
17
+ *
18
+ * @param {{ mode?: string, cwd?: string }} options
19
+ * @returns {string[]} Ordered file paths (may include non-existent files — dotenv ignores those)
20
+ */
21
+ export function buildEnvPaths({ mode, cwd } = {}) {
22
+ const dir = cwd || process.cwd();
23
+ const useMode = mode && mode !== 'development';
24
+
25
+ // Local project .env files (highest priority)
26
+ const paths = [];
27
+ if (useMode) paths.push(resolve(dir, `.env.${mode}`));
28
+ paths.push(resolve(dir, '.env'));
29
+
30
+ // Walk up to parent directories for monorepo fallback
31
+ let current = dirname(dir);
32
+ const { root } = parsePath(dir);
33
+
34
+ while (current !== root) {
35
+ if (useMode) paths.push(resolve(current, `.env.${mode}`));
36
+ paths.push(resolve(current, '.env'));
37
+
38
+ // Stop at the first ancestor that has any .env file
39
+ const hasEnv = useMode
40
+ ? existsSync(resolve(current, `.env.${mode}`)) || existsSync(resolve(current, '.env'))
41
+ : existsSync(resolve(current, '.env'));
42
+
43
+ if (hasEnv) break;
44
+
45
+ current = dirname(current);
46
+ }
47
+
48
+ return paths;
49
+ }
50
+
51
+ /**
52
+ * Load environment variables using mode-aware, parent-walking dotenv paths.
53
+ *
54
+ * @param {{ mode?: string, cwd?: string }} options
55
+ */
56
+ export function loadEnv({ mode, cwd } = {}) {
57
+ const paths = buildEnvPaths({ mode, cwd });
58
+ dotenv.config({ path: paths });
59
+ }
60
+
61
+ /**
62
+ * Determine the .env file path to write workspace IDs into.
63
+ * Writes to .env.<mode> when a non-default mode is active, otherwise .env.
64
+ *
65
+ * @param {{ mode?: string, cwd?: string }} options
66
+ * @returns {string}
67
+ */
68
+ export function envWritePath({ mode, cwd } = {}) {
69
+ const dir = cwd || process.cwd();
70
+ const useMode = mode && mode !== 'development';
71
+ return resolve(dir, useMode ? `.env.${mode}` : '.env');
72
+ }
73
+
74
+ /**
75
+ * Parse --mode <name> from a process.argv array.
76
+ *
77
+ * @param {string[]} argv
78
+ * @returns {string|null}
79
+ */
80
+ export function parseModeArg(argv) {
81
+ const idx = argv.indexOf('--mode');
82
+ if (idx !== -1 && idx + 1 < argv.length) {
83
+ return argv[idx + 1];
84
+ }
85
+ return null;
86
+ }
package/src/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import dotenv from 'dotenv';
2
1
  import { existsSync } from 'node:fs';
3
2
  import { readFile } from 'node:fs/promises';
4
3
  import { resolve } from 'node:path';
5
4
  import { createClient } from './client.js';
5
+ import { loadEnv, envWritePath } from './env.js';
6
6
  import { createMiddleware as createServerRoutes } from './server-routes.js';
7
7
  import { createAgentMiddleware } from './agent-dev.js';
8
8
  import { init, migrate } from './workspace.js';
@@ -23,12 +23,14 @@ export default function informer(options = {}) {
23
23
  let authHeader = null;
24
24
  let serverOrigin = null;
25
25
  let devWorkspaceId = null;
26
+ let activeMode = null;
26
27
 
27
28
  return {
28
29
  name: 'vite-plugin-informer',
29
30
 
30
- config(_, { command }) {
31
- dotenv.config();
31
+ config(_, { command, mode }) {
32
+ activeMode = mode;
33
+ loadEnv({ mode });
32
34
 
33
35
  isDev = command === 'serve';
34
36
 
@@ -57,7 +59,8 @@ export default function informer(options = {}) {
57
59
  changeOrigin: true,
58
60
  headers: {
59
61
  Authorization: authHeader
60
- }
62
+ },
63
+ ...options.proxy
61
64
  }
62
65
  }
63
66
  };
@@ -106,7 +109,7 @@ export default function informer(options = {}) {
106
109
  api,
107
110
  slug,
108
111
  migrationsDir,
109
- envPath: resolve(projectRoot, '.env')
112
+ envPath: envWritePath({ mode: activeMode })
110
113
  });
111
114
  devWorkspaceId = result.workspaceId;
112
115
  }
@@ -237,6 +237,32 @@ export function createMiddleware(viteServer, { serverOrigin, authHeader, devWork
237
237
  }
238
238
  };
239
239
 
240
+ // log helper — mirrors sandbox log(message, data) + log.info/warn/error/debug
241
+ const logCall = (level, message, data) => {
242
+ const msg = typeof message === 'string' ? message : JSON.stringify(message);
243
+ const args = [`[app-log] [${level}] ${msg}`];
244
+ if (data) args.push(data);
245
+ console.log(...args);
246
+ };
247
+ const log = Object.assign(
248
+ (message, data) => logCall('info', message, data),
249
+ {
250
+ debug: (message, data) => logCall('debug', message, data),
251
+ info: (message, data) => logCall('info', message, data),
252
+ warn: (message, data) => logCall('warn', message, data),
253
+ error: (message, data) => logCall('error', message, data)
254
+ }
255
+ );
256
+
257
+ // markdown helper — passthrough in dev (production uses `marked`)
258
+ const markdown = (text) => text;
259
+
260
+ // emit helper — no-op in dev (logs to console)
261
+ const emit = (event, payload) => {
262
+ console.log(`[app-event] emit("${event}",`, JSON.stringify(payload), ')');
263
+ return { ok: true };
264
+ };
265
+
240
266
  // Build request context
241
267
  const request = {
242
268
  method: req.method.toUpperCase(),
@@ -266,7 +292,7 @@ export function createMiddleware(viteServer, { serverOrigin, authHeader, devWork
266
292
  }
267
293
 
268
294
  // Call handler
269
- const result = await handler({ query, fetch: apiFetch, respond, crypto: cryptoHelper, env: {}, request });
295
+ const result = await handler({ query, fetch: apiFetch, respond, emit, crypto: cryptoHelper, markdown, log, env: {}, request });
270
296
 
271
297
  // If respond() was already called, the response is already sent
272
298
  if (responded) return;
package/src/workspace.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { readdir, readFile, writeFile, access } from 'node:fs/promises';
2
- import { join } from 'node:path';
2
+ import { join, basename } from 'node:path';
3
3
 
4
4
  /**
5
5
  * Execute raw SQL against a workspace datasource via the _sql route.
@@ -122,7 +122,7 @@ export async function init({ api, slug, migrationsDir, envPath }) {
122
122
  }
123
123
 
124
124
  await writeFile(envPath, envContent);
125
- console.log(`Saved INFORMER_DEV_WORKSPACE=${workspaceId} to .env`);
125
+ console.log(`Saved INFORMER_DEV_WORKSPACE=${workspaceId} to ${basename(envPath)}`);
126
126
 
127
127
  return { workspaceId, ...result };
128
128
  }