scenv 0.7.0 → 0.8.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/index.cjs +47 -54
- package/dist/index.d.cts +19 -11
- package/dist/index.d.ts +19 -11
- package/dist/index.js +47 -54
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -48,12 +48,12 @@ function defaultPrompt(name, defaultValue) {
|
|
|
48
48
|
const defaultStr = defaultValue !== void 0 && defaultValue !== null ? String(defaultValue) : "";
|
|
49
49
|
const message = defaultStr ? `Enter ${name} [${defaultStr}]: ` : `Enter ${name}: `;
|
|
50
50
|
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
51
|
-
return new Promise((
|
|
51
|
+
return new Promise((resolve2, reject) => {
|
|
52
52
|
rl.question(message, (answer) => {
|
|
53
53
|
rl.close();
|
|
54
54
|
const trimmed = answer.trim();
|
|
55
55
|
const value = trimmed !== "" ? trimmed : defaultStr;
|
|
56
|
-
|
|
56
|
+
resolve2(value);
|
|
57
57
|
});
|
|
58
58
|
rl.on("error", (err) => {
|
|
59
59
|
rl.close();
|
|
@@ -72,7 +72,7 @@ var envKeyMap = {
|
|
|
72
72
|
SCENV_IGNORE_ENV: "ignoreEnv",
|
|
73
73
|
SCENV_IGNORE_CONTEXT: "ignoreContext",
|
|
74
74
|
SCENV_SAVE_CONTEXT_TO: "saveContextTo",
|
|
75
|
-
|
|
75
|
+
SCENV_SAVE_MODE: "saveMode",
|
|
76
76
|
SCENV_LOG_LEVEL: "logLevel"
|
|
77
77
|
};
|
|
78
78
|
var programmaticConfig = {};
|
|
@@ -108,8 +108,10 @@ function configFromEnv() {
|
|
|
108
108
|
out[configKey] = v;
|
|
109
109
|
} else if (configKey === "saveContextTo") {
|
|
110
110
|
out.saveContextTo = val;
|
|
111
|
-
} else if (configKey === "
|
|
112
|
-
|
|
111
|
+
} else if (configKey === "saveMode") {
|
|
112
|
+
const v = val.toLowerCase();
|
|
113
|
+
if (v === "all" || v === "prompts-only")
|
|
114
|
+
out[configKey] = v;
|
|
113
115
|
} else if (configKey === "logLevel") {
|
|
114
116
|
const v = val.toLowerCase();
|
|
115
117
|
if (LOG_LEVELS.includes(v)) out.logLevel = v;
|
|
@@ -149,8 +151,8 @@ function loadConfigFile(configDir) {
|
|
|
149
151
|
out.set = parsed.set;
|
|
150
152
|
if (typeof parsed.saveContextTo === "string")
|
|
151
153
|
out.saveContextTo = parsed.saveContextTo;
|
|
152
|
-
if (typeof parsed.
|
|
153
|
-
|
|
154
|
+
if (typeof parsed.saveMode === "string" && ["all", "prompts-only"].includes(parsed.saveMode))
|
|
155
|
+
out.saveMode = parsed.saveMode;
|
|
154
156
|
if (typeof parsed.root === "string") out.root = parsed.root;
|
|
155
157
|
if (typeof parsed.logLevel === "string" && LOG_LEVELS.includes(parsed.logLevel))
|
|
156
158
|
out.logLevel = parsed.logLevel;
|
|
@@ -238,7 +240,7 @@ var CONFIG_LOG_KEYS = [
|
|
|
238
240
|
"ignoreContext",
|
|
239
241
|
"set",
|
|
240
242
|
"saveContextTo",
|
|
241
|
-
"
|
|
243
|
+
"saveMode",
|
|
242
244
|
"logLevel"
|
|
243
245
|
];
|
|
244
246
|
function configForLog(config) {
|
|
@@ -259,7 +261,7 @@ function logConfigLoaded(config) {
|
|
|
259
261
|
if (config.ignoreEnv === true) parts.push("ignoreEnv=true");
|
|
260
262
|
if (config.ignoreContext === true) parts.push("ignoreContext=true");
|
|
261
263
|
if (config.saveContextTo !== void 0) parts.push("saveContextTo=" + config.saveContextTo);
|
|
262
|
-
if (config.
|
|
264
|
+
if (config.saveMode !== void 0) parts.push("saveMode=" + config.saveMode);
|
|
263
265
|
log("info", "config loaded", parts.join(" "));
|
|
264
266
|
}
|
|
265
267
|
if (levelNum >= LEVEL_NUM.debug) {
|
|
@@ -328,10 +330,23 @@ function discoverContextPaths(dir, found = /* @__PURE__ */ new Map()) {
|
|
|
328
330
|
return found;
|
|
329
331
|
}
|
|
330
332
|
function getContext(contextName, root) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
let filePath;
|
|
334
|
+
if (root !== void 0) {
|
|
335
|
+
const paths = discoverContextPaths(root);
|
|
336
|
+
filePath = paths.get(contextName);
|
|
337
|
+
} else {
|
|
338
|
+
const cwd = process.cwd();
|
|
339
|
+
const paths = discoverContextPaths(cwd);
|
|
340
|
+
filePath = paths.get(contextName);
|
|
341
|
+
if (!filePath) {
|
|
342
|
+
const config = loadConfig();
|
|
343
|
+
const projectRoot = config.root ?? cwd;
|
|
344
|
+
if (projectRoot !== cwd) {
|
|
345
|
+
const rootPaths = discoverContextPaths(projectRoot);
|
|
346
|
+
filePath = rootPaths.get(contextName);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
335
350
|
if (!filePath) {
|
|
336
351
|
log("trace", `getContext: context "${contextName}" not found`);
|
|
337
352
|
return {};
|
|
@@ -356,17 +371,9 @@ function getMergedContextValues() {
|
|
|
356
371
|
const config = loadConfig();
|
|
357
372
|
logConfigLoaded(config);
|
|
358
373
|
if (config.ignoreContext) return {};
|
|
359
|
-
const
|
|
360
|
-
const paths = discoverContextPaths(
|
|
374
|
+
const searchRoot = process.cwd();
|
|
375
|
+
const paths = discoverContextPaths(searchRoot);
|
|
361
376
|
const out = {};
|
|
362
|
-
if (config.saveContextTo) {
|
|
363
|
-
const savePath = resolveSaveContextPath(config.saveContextTo);
|
|
364
|
-
const saveCtx = getContextAtPath(savePath);
|
|
365
|
-
for (const [k, v] of Object.entries(saveCtx)) out[k] = v;
|
|
366
|
-
if (Object.keys(saveCtx).length > 0) {
|
|
367
|
-
log("debug", `saveContextTo "${config.saveContextTo}" loaded keys=${JSON.stringify(Object.keys(saveCtx))}`);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
377
|
for (const contextName of config.context ?? []) {
|
|
371
378
|
const filePath = paths.get(contextName);
|
|
372
379
|
if (!filePath) {
|
|
@@ -398,32 +405,11 @@ function getContextWritePath(contextName) {
|
|
|
398
405
|
return contextName.endsWith(CONTEXT_SUFFIX) ? contextName : contextName + CONTEXT_SUFFIX;
|
|
399
406
|
}
|
|
400
407
|
const config = loadConfig();
|
|
401
|
-
const
|
|
402
|
-
const paths = discoverContextPaths(root);
|
|
408
|
+
const paths = discoverContextPaths(process.cwd());
|
|
403
409
|
const existing = paths.get(contextName);
|
|
404
410
|
if (existing) return existing;
|
|
405
|
-
const
|
|
406
|
-
return (0, import_node_path2.join)(
|
|
407
|
-
}
|
|
408
|
-
function resolveSaveContextPath(nameOrPath) {
|
|
409
|
-
if ((0, import_node_path2.isAbsolute)(nameOrPath) || nameOrPath.includes(import_node_path2.sep)) {
|
|
410
|
-
return nameOrPath.endsWith(CONTEXT_SUFFIX) ? nameOrPath : nameOrPath + CONTEXT_SUFFIX;
|
|
411
|
-
}
|
|
412
|
-
return getContextWritePath(nameOrPath);
|
|
413
|
-
}
|
|
414
|
-
function getContextAtPath(filePath) {
|
|
415
|
-
if (!(0, import_node_fs2.existsSync)(filePath)) return {};
|
|
416
|
-
try {
|
|
417
|
-
const raw = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
|
|
418
|
-
const data = JSON.parse(raw);
|
|
419
|
-
const out = {};
|
|
420
|
-
for (const [k, v] of Object.entries(data)) {
|
|
421
|
-
if (typeof v === "string") out[k] = v;
|
|
422
|
-
}
|
|
423
|
-
return out;
|
|
424
|
-
} catch {
|
|
425
|
-
return {};
|
|
426
|
-
}
|
|
411
|
+
const projectRoot = config.root ?? process.cwd();
|
|
412
|
+
return (0, import_node_path2.join)(projectRoot, `${contextName}${CONTEXT_SUFFIX}`);
|
|
427
413
|
}
|
|
428
414
|
function writeToContext(contextName, key, value) {
|
|
429
415
|
const path = getContextWritePath(contextName);
|
|
@@ -610,10 +596,11 @@ function scenv(name, options = {}) {
|
|
|
610
596
|
throw new Error(errMsg);
|
|
611
597
|
}
|
|
612
598
|
const final = validated.data;
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
599
|
+
const config = loadConfig();
|
|
600
|
+
if (wasPrompted) setInMemoryContext(key, String(final));
|
|
601
|
+
if (config.saveContextTo) {
|
|
602
|
+
const saveMode = config.saveMode ?? "all";
|
|
603
|
+
if (saveMode === "all" || wasPrompted) {
|
|
617
604
|
writeToContext(config.saveContextTo, key, String(final));
|
|
618
605
|
log("info", `Saved key=${key} to saveContextTo ${config.saveContextTo}`);
|
|
619
606
|
}
|
|
@@ -647,12 +634,15 @@ function scenv(name, options = {}) {
|
|
|
647
634
|
}
|
|
648
635
|
|
|
649
636
|
// src/cli-args.ts
|
|
637
|
+
var import_node_path3 = require("path");
|
|
650
638
|
function parseScenvArgs(argv) {
|
|
651
639
|
const config = {};
|
|
652
640
|
let i = 0;
|
|
653
641
|
while (i < argv.length) {
|
|
654
642
|
const arg = argv[i];
|
|
655
|
-
if (arg === "--
|
|
643
|
+
if (arg === "--root" && argv[i + 1] !== void 0) {
|
|
644
|
+
config.root = (0, import_node_path3.resolve)(argv[++i]);
|
|
645
|
+
} else if (arg === "--context" && argv[i + 1] !== void 0) {
|
|
656
646
|
config.context = argv[++i].split(",").map((s) => s.trim()).filter(Boolean);
|
|
657
647
|
} else if (arg === "--add-context" && argv[i + 1] !== void 0) {
|
|
658
648
|
config.addContext = argv[++i].split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -674,8 +664,11 @@ function parseScenvArgs(argv) {
|
|
|
674
664
|
}
|
|
675
665
|
} else if (arg === "--save-context-to" && argv[i + 1] !== void 0) {
|
|
676
666
|
config.saveContextTo = argv[++i];
|
|
677
|
-
} else if (arg === "--
|
|
678
|
-
|
|
667
|
+
} else if (arg === "--save-mode" && argv[i + 1] !== void 0) {
|
|
668
|
+
const v = argv[++i].toLowerCase();
|
|
669
|
+
if (v === "all" || v === "prompts-only") {
|
|
670
|
+
config.saveMode = v;
|
|
671
|
+
}
|
|
679
672
|
} else if ((arg === "--log-level" || arg === "--log") && argv[i + 1] !== void 0) {
|
|
680
673
|
const v = argv[++i].toLowerCase();
|
|
681
674
|
if (LOG_LEVELS.includes(v)) {
|
package/dist/index.d.cts
CHANGED
|
@@ -13,6 +13,12 @@ type PromptMode = "always" | "never" | "fallback" | "no-env";
|
|
|
13
13
|
declare const LOG_LEVELS: readonly ["none", "trace", "debug", "info", "warn", "error"];
|
|
14
14
|
/** Log level type. `"none"` disables logging; higher values are more verbose. */
|
|
15
15
|
type LogLevel = (typeof LOG_LEVELS)[number];
|
|
16
|
+
/**
|
|
17
|
+
* When to write resolved values to saveContextTo during get().
|
|
18
|
+
* - `"all"` – Save every resolved variable (from set, env, context, or prompt). Useful for re-running with the same values.
|
|
19
|
+
* - `"prompts-only"` – Save only when the user was prompted. Default is `"all"`.
|
|
20
|
+
*/
|
|
21
|
+
type SaveMode = "all" | "prompts-only";
|
|
16
22
|
/**
|
|
17
23
|
* Full scenv configuration. Built from file (scenv.config.json), environment (SCENV_*),
|
|
18
24
|
* and programmatic config (configure()), with programmatic > env > file precedence.
|
|
@@ -33,9 +39,9 @@ interface ScenvConfig {
|
|
|
33
39
|
set?: Record<string, string>;
|
|
34
40
|
/** Optional path or context name (without .context.json) where to save resolved values. If set, all saves go here and this context is used before prompting. If unset, values are saved to an in-memory context only (same process). */
|
|
35
41
|
saveContextTo?: string;
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
/**
|
|
42
|
+
/** When to write to saveContextTo during get(): "all" (default) saves every resolved variable; "prompts-only" saves only when the user was prompted. */
|
|
43
|
+
saveMode?: SaveMode;
|
|
44
|
+
/** Project root: where to search for scenv.config.json and where new context files are saved. Defaults to the directory containing scenv.config.json (when found) or cwd. Context files are discovered from cwd. */
|
|
39
45
|
root?: string;
|
|
40
46
|
/** Logging level. Default is `"none"`. Messages go to stderr. */
|
|
41
47
|
logLevel?: LogLevel;
|
|
@@ -120,17 +126,18 @@ declare function discoverContextPaths(dir: string, found?: Map<string, string>):
|
|
|
120
126
|
/**
|
|
121
127
|
* Loads key-value pairs from a single context file. Used when resolving @context:key references.
|
|
122
128
|
* Does not depend on config.context or ignoreContext; the context file is read if it exists
|
|
123
|
-
* under the
|
|
129
|
+
* under the search directory.
|
|
124
130
|
*
|
|
125
131
|
* @param contextName - Name of the context (e.g. "prod", "dev") — file is contextName.context.json.
|
|
126
|
-
* @param root - Optional
|
|
132
|
+
* @param root - Optional directory to search. If omitted, searches from process.cwd() then from project root (config.root) if the context is not found under cwd.
|
|
127
133
|
* @returns A flat record of key → string value from that context file. Empty if file not found or invalid.
|
|
128
134
|
*/
|
|
129
135
|
declare function getContext(contextName: string, root?: string): Record<string, string>;
|
|
130
136
|
/**
|
|
131
|
-
* Loads and merges context values from the current config
|
|
132
|
-
* is
|
|
133
|
-
*
|
|
137
|
+
* Loads and merges context values from the current config from {@link ScenvConfig.context} only.
|
|
138
|
+
* saveContextTo is not used for resolution; it is only a write target. To use the same context
|
|
139
|
+
* for reading, add it explicitly via context or addContext.
|
|
140
|
+
* Respects {@link ScenvConfig.ignoreContext}. Later contexts overwrite earlier for the same key.
|
|
134
141
|
* Used during variable resolution (set > env > in-memory > merged context > default).
|
|
135
142
|
*
|
|
136
143
|
* @returns A flat record of key → string value. Empty if ignoreContext is true or no context loaded.
|
|
@@ -139,7 +146,7 @@ declare function getMergedContextValues(): Record<string, string>;
|
|
|
139
146
|
/**
|
|
140
147
|
* Returns the file path used for a context name or path when saving.
|
|
141
148
|
* - If contextName is path-like (absolute or contains path separator), returns that path with .context.json appended if not already present.
|
|
142
|
-
* - Otherwise, if that context was already discovered under
|
|
149
|
+
* - Otherwise, if that context was already discovered under cwd, returns its path; else saves under project root (config.root or cwd).
|
|
143
150
|
*
|
|
144
151
|
* @param contextName - Context name (e.g. "dev", "prod") or file path without suffix (e.g. "/path/to/myfile" → myfile.context.json).
|
|
145
152
|
* @returns Absolute path to the context JSON file.
|
|
@@ -263,6 +270,7 @@ declare function scenv<T>(name: string, options?: ScenvVariableOptions<T>): Scen
|
|
|
263
270
|
* Typical use: `configure(parseScenvArgs(process.argv.slice(2)))`. Unrecognized flags are ignored.
|
|
264
271
|
*
|
|
265
272
|
* Supported flags:
|
|
273
|
+
* - `--root path` – Project root (where to find scenv.config.json and where new context files are saved). Relative paths are resolved from cwd.
|
|
266
274
|
* - `--context a,b,c` – Set context list (replace).
|
|
267
275
|
* - `--add-context x,y` – Add context names.
|
|
268
276
|
* - `--prompt always|never|fallback|no-env` – Prompt mode.
|
|
@@ -270,7 +278,7 @@ declare function scenv<T>(name: string, options?: ScenvVariableOptions<T>): Scen
|
|
|
270
278
|
* - `--ignore-context` – Set ignoreContext to true.
|
|
271
279
|
* - `--set key=value` or `--set=key=value` – Add to set overrides (multiple allowed).
|
|
272
280
|
* - `--save-context-to pathOrName` – saveContextTo (path or context name without .context.json).
|
|
273
|
-
* - `--
|
|
281
|
+
* - `--save-mode all|prompts-only` – When to write to saveContextTo during get(); default is all.
|
|
274
282
|
* - `--log-level level`, `--log level`, `--log=level` – logLevel.
|
|
275
283
|
*
|
|
276
284
|
* @param argv - Array of CLI arguments (e.g. process.argv.slice(2)).
|
|
@@ -278,4 +286,4 @@ declare function scenv<T>(name: string, options?: ScenvVariableOptions<T>): Scen
|
|
|
278
286
|
*/
|
|
279
287
|
declare function parseScenvArgs(argv: string[]): Partial<ScenvConfig>;
|
|
280
288
|
|
|
281
|
-
export { type DefaultPromptFn, type GetOptions, LOG_LEVELS, type LogLevel, type PromptMode, type ScenvCallbacks, type ScenvConfig, type ScenvVariable, configure, discoverContextPaths, getCallbacks, getContext, getContextWritePath, getInMemoryContext, getMergedContextValues, loadConfig, parseScenvArgs, resetConfig, resetInMemoryContext, resetLogState, scenv, setInMemoryContext };
|
|
289
|
+
export { type DefaultPromptFn, type GetOptions, LOG_LEVELS, type LogLevel, type PromptMode, type SaveMode, type ScenvCallbacks, type ScenvConfig, type ScenvVariable, configure, discoverContextPaths, getCallbacks, getContext, getContextWritePath, getInMemoryContext, getMergedContextValues, loadConfig, parseScenvArgs, resetConfig, resetInMemoryContext, resetLogState, scenv, setInMemoryContext };
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,12 @@ type PromptMode = "always" | "never" | "fallback" | "no-env";
|
|
|
13
13
|
declare const LOG_LEVELS: readonly ["none", "trace", "debug", "info", "warn", "error"];
|
|
14
14
|
/** Log level type. `"none"` disables logging; higher values are more verbose. */
|
|
15
15
|
type LogLevel = (typeof LOG_LEVELS)[number];
|
|
16
|
+
/**
|
|
17
|
+
* When to write resolved values to saveContextTo during get().
|
|
18
|
+
* - `"all"` – Save every resolved variable (from set, env, context, or prompt). Useful for re-running with the same values.
|
|
19
|
+
* - `"prompts-only"` – Save only when the user was prompted. Default is `"all"`.
|
|
20
|
+
*/
|
|
21
|
+
type SaveMode = "all" | "prompts-only";
|
|
16
22
|
/**
|
|
17
23
|
* Full scenv configuration. Built from file (scenv.config.json), environment (SCENV_*),
|
|
18
24
|
* and programmatic config (configure()), with programmatic > env > file precedence.
|
|
@@ -33,9 +39,9 @@ interface ScenvConfig {
|
|
|
33
39
|
set?: Record<string, string>;
|
|
34
40
|
/** Optional path or context name (without .context.json) where to save resolved values. If set, all saves go here and this context is used before prompting. If unset, values are saved to an in-memory context only (same process). */
|
|
35
41
|
saveContextTo?: string;
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
/**
|
|
42
|
+
/** When to write to saveContextTo during get(): "all" (default) saves every resolved variable; "prompts-only" saves only when the user was prompted. */
|
|
43
|
+
saveMode?: SaveMode;
|
|
44
|
+
/** Project root: where to search for scenv.config.json and where new context files are saved. Defaults to the directory containing scenv.config.json (when found) or cwd. Context files are discovered from cwd. */
|
|
39
45
|
root?: string;
|
|
40
46
|
/** Logging level. Default is `"none"`. Messages go to stderr. */
|
|
41
47
|
logLevel?: LogLevel;
|
|
@@ -120,17 +126,18 @@ declare function discoverContextPaths(dir: string, found?: Map<string, string>):
|
|
|
120
126
|
/**
|
|
121
127
|
* Loads key-value pairs from a single context file. Used when resolving @context:key references.
|
|
122
128
|
* Does not depend on config.context or ignoreContext; the context file is read if it exists
|
|
123
|
-
* under the
|
|
129
|
+
* under the search directory.
|
|
124
130
|
*
|
|
125
131
|
* @param contextName - Name of the context (e.g. "prod", "dev") — file is contextName.context.json.
|
|
126
|
-
* @param root - Optional
|
|
132
|
+
* @param root - Optional directory to search. If omitted, searches from process.cwd() then from project root (config.root) if the context is not found under cwd.
|
|
127
133
|
* @returns A flat record of key → string value from that context file. Empty if file not found or invalid.
|
|
128
134
|
*/
|
|
129
135
|
declare function getContext(contextName: string, root?: string): Record<string, string>;
|
|
130
136
|
/**
|
|
131
|
-
* Loads and merges context values from the current config
|
|
132
|
-
* is
|
|
133
|
-
*
|
|
137
|
+
* Loads and merges context values from the current config from {@link ScenvConfig.context} only.
|
|
138
|
+
* saveContextTo is not used for resolution; it is only a write target. To use the same context
|
|
139
|
+
* for reading, add it explicitly via context or addContext.
|
|
140
|
+
* Respects {@link ScenvConfig.ignoreContext}. Later contexts overwrite earlier for the same key.
|
|
134
141
|
* Used during variable resolution (set > env > in-memory > merged context > default).
|
|
135
142
|
*
|
|
136
143
|
* @returns A flat record of key → string value. Empty if ignoreContext is true or no context loaded.
|
|
@@ -139,7 +146,7 @@ declare function getMergedContextValues(): Record<string, string>;
|
|
|
139
146
|
/**
|
|
140
147
|
* Returns the file path used for a context name or path when saving.
|
|
141
148
|
* - If contextName is path-like (absolute or contains path separator), returns that path with .context.json appended if not already present.
|
|
142
|
-
* - Otherwise, if that context was already discovered under
|
|
149
|
+
* - Otherwise, if that context was already discovered under cwd, returns its path; else saves under project root (config.root or cwd).
|
|
143
150
|
*
|
|
144
151
|
* @param contextName - Context name (e.g. "dev", "prod") or file path without suffix (e.g. "/path/to/myfile" → myfile.context.json).
|
|
145
152
|
* @returns Absolute path to the context JSON file.
|
|
@@ -263,6 +270,7 @@ declare function scenv<T>(name: string, options?: ScenvVariableOptions<T>): Scen
|
|
|
263
270
|
* Typical use: `configure(parseScenvArgs(process.argv.slice(2)))`. Unrecognized flags are ignored.
|
|
264
271
|
*
|
|
265
272
|
* Supported flags:
|
|
273
|
+
* - `--root path` – Project root (where to find scenv.config.json and where new context files are saved). Relative paths are resolved from cwd.
|
|
266
274
|
* - `--context a,b,c` – Set context list (replace).
|
|
267
275
|
* - `--add-context x,y` – Add context names.
|
|
268
276
|
* - `--prompt always|never|fallback|no-env` – Prompt mode.
|
|
@@ -270,7 +278,7 @@ declare function scenv<T>(name: string, options?: ScenvVariableOptions<T>): Scen
|
|
|
270
278
|
* - `--ignore-context` – Set ignoreContext to true.
|
|
271
279
|
* - `--set key=value` or `--set=key=value` – Add to set overrides (multiple allowed).
|
|
272
280
|
* - `--save-context-to pathOrName` – saveContextTo (path or context name without .context.json).
|
|
273
|
-
* - `--
|
|
281
|
+
* - `--save-mode all|prompts-only` – When to write to saveContextTo during get(); default is all.
|
|
274
282
|
* - `--log-level level`, `--log level`, `--log=level` – logLevel.
|
|
275
283
|
*
|
|
276
284
|
* @param argv - Array of CLI arguments (e.g. process.argv.slice(2)).
|
|
@@ -278,4 +286,4 @@ declare function scenv<T>(name: string, options?: ScenvVariableOptions<T>): Scen
|
|
|
278
286
|
*/
|
|
279
287
|
declare function parseScenvArgs(argv: string[]): Partial<ScenvConfig>;
|
|
280
288
|
|
|
281
|
-
export { type DefaultPromptFn, type GetOptions, LOG_LEVELS, type LogLevel, type PromptMode, type ScenvCallbacks, type ScenvConfig, type ScenvVariable, configure, discoverContextPaths, getCallbacks, getContext, getContextWritePath, getInMemoryContext, getMergedContextValues, loadConfig, parseScenvArgs, resetConfig, resetInMemoryContext, resetLogState, scenv, setInMemoryContext };
|
|
289
|
+
export { type DefaultPromptFn, type GetOptions, LOG_LEVELS, type LogLevel, type PromptMode, type SaveMode, type ScenvCallbacks, type ScenvConfig, type ScenvVariable, configure, discoverContextPaths, getCallbacks, getContext, getContextWritePath, getInMemoryContext, getMergedContextValues, loadConfig, parseScenvArgs, resetConfig, resetInMemoryContext, resetLogState, scenv, setInMemoryContext };
|
package/dist/index.js
CHANGED
|
@@ -8,12 +8,12 @@ function defaultPrompt(name, defaultValue) {
|
|
|
8
8
|
const defaultStr = defaultValue !== void 0 && defaultValue !== null ? String(defaultValue) : "";
|
|
9
9
|
const message = defaultStr ? `Enter ${name} [${defaultStr}]: ` : `Enter ${name}: `;
|
|
10
10
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
11
|
-
return new Promise((
|
|
11
|
+
return new Promise((resolve2, reject) => {
|
|
12
12
|
rl.question(message, (answer) => {
|
|
13
13
|
rl.close();
|
|
14
14
|
const trimmed = answer.trim();
|
|
15
15
|
const value = trimmed !== "" ? trimmed : defaultStr;
|
|
16
|
-
|
|
16
|
+
resolve2(value);
|
|
17
17
|
});
|
|
18
18
|
rl.on("error", (err) => {
|
|
19
19
|
rl.close();
|
|
@@ -32,7 +32,7 @@ var envKeyMap = {
|
|
|
32
32
|
SCENV_IGNORE_ENV: "ignoreEnv",
|
|
33
33
|
SCENV_IGNORE_CONTEXT: "ignoreContext",
|
|
34
34
|
SCENV_SAVE_CONTEXT_TO: "saveContextTo",
|
|
35
|
-
|
|
35
|
+
SCENV_SAVE_MODE: "saveMode",
|
|
36
36
|
SCENV_LOG_LEVEL: "logLevel"
|
|
37
37
|
};
|
|
38
38
|
var programmaticConfig = {};
|
|
@@ -68,8 +68,10 @@ function configFromEnv() {
|
|
|
68
68
|
out[configKey] = v;
|
|
69
69
|
} else if (configKey === "saveContextTo") {
|
|
70
70
|
out.saveContextTo = val;
|
|
71
|
-
} else if (configKey === "
|
|
72
|
-
|
|
71
|
+
} else if (configKey === "saveMode") {
|
|
72
|
+
const v = val.toLowerCase();
|
|
73
|
+
if (v === "all" || v === "prompts-only")
|
|
74
|
+
out[configKey] = v;
|
|
73
75
|
} else if (configKey === "logLevel") {
|
|
74
76
|
const v = val.toLowerCase();
|
|
75
77
|
if (LOG_LEVELS.includes(v)) out.logLevel = v;
|
|
@@ -109,8 +111,8 @@ function loadConfigFile(configDir) {
|
|
|
109
111
|
out.set = parsed.set;
|
|
110
112
|
if (typeof parsed.saveContextTo === "string")
|
|
111
113
|
out.saveContextTo = parsed.saveContextTo;
|
|
112
|
-
if (typeof parsed.
|
|
113
|
-
|
|
114
|
+
if (typeof parsed.saveMode === "string" && ["all", "prompts-only"].includes(parsed.saveMode))
|
|
115
|
+
out.saveMode = parsed.saveMode;
|
|
114
116
|
if (typeof parsed.root === "string") out.root = parsed.root;
|
|
115
117
|
if (typeof parsed.logLevel === "string" && LOG_LEVELS.includes(parsed.logLevel))
|
|
116
118
|
out.logLevel = parsed.logLevel;
|
|
@@ -198,7 +200,7 @@ var CONFIG_LOG_KEYS = [
|
|
|
198
200
|
"ignoreContext",
|
|
199
201
|
"set",
|
|
200
202
|
"saveContextTo",
|
|
201
|
-
"
|
|
203
|
+
"saveMode",
|
|
202
204
|
"logLevel"
|
|
203
205
|
];
|
|
204
206
|
function configForLog(config) {
|
|
@@ -219,7 +221,7 @@ function logConfigLoaded(config) {
|
|
|
219
221
|
if (config.ignoreEnv === true) parts.push("ignoreEnv=true");
|
|
220
222
|
if (config.ignoreContext === true) parts.push("ignoreContext=true");
|
|
221
223
|
if (config.saveContextTo !== void 0) parts.push("saveContextTo=" + config.saveContextTo);
|
|
222
|
-
if (config.
|
|
224
|
+
if (config.saveMode !== void 0) parts.push("saveMode=" + config.saveMode);
|
|
223
225
|
log("info", "config loaded", parts.join(" "));
|
|
224
226
|
}
|
|
225
227
|
if (levelNum >= LEVEL_NUM.debug) {
|
|
@@ -295,10 +297,23 @@ function discoverContextPaths(dir, found = /* @__PURE__ */ new Map()) {
|
|
|
295
297
|
return found;
|
|
296
298
|
}
|
|
297
299
|
function getContext(contextName, root) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
300
|
+
let filePath;
|
|
301
|
+
if (root !== void 0) {
|
|
302
|
+
const paths = discoverContextPaths(root);
|
|
303
|
+
filePath = paths.get(contextName);
|
|
304
|
+
} else {
|
|
305
|
+
const cwd = process.cwd();
|
|
306
|
+
const paths = discoverContextPaths(cwd);
|
|
307
|
+
filePath = paths.get(contextName);
|
|
308
|
+
if (!filePath) {
|
|
309
|
+
const config = loadConfig();
|
|
310
|
+
const projectRoot = config.root ?? cwd;
|
|
311
|
+
if (projectRoot !== cwd) {
|
|
312
|
+
const rootPaths = discoverContextPaths(projectRoot);
|
|
313
|
+
filePath = rootPaths.get(contextName);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
302
317
|
if (!filePath) {
|
|
303
318
|
log("trace", `getContext: context "${contextName}" not found`);
|
|
304
319
|
return {};
|
|
@@ -323,17 +338,9 @@ function getMergedContextValues() {
|
|
|
323
338
|
const config = loadConfig();
|
|
324
339
|
logConfigLoaded(config);
|
|
325
340
|
if (config.ignoreContext) return {};
|
|
326
|
-
const
|
|
327
|
-
const paths = discoverContextPaths(
|
|
341
|
+
const searchRoot = process.cwd();
|
|
342
|
+
const paths = discoverContextPaths(searchRoot);
|
|
328
343
|
const out = {};
|
|
329
|
-
if (config.saveContextTo) {
|
|
330
|
-
const savePath = resolveSaveContextPath(config.saveContextTo);
|
|
331
|
-
const saveCtx = getContextAtPath(savePath);
|
|
332
|
-
for (const [k, v] of Object.entries(saveCtx)) out[k] = v;
|
|
333
|
-
if (Object.keys(saveCtx).length > 0) {
|
|
334
|
-
log("debug", `saveContextTo "${config.saveContextTo}" loaded keys=${JSON.stringify(Object.keys(saveCtx))}`);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
344
|
for (const contextName of config.context ?? []) {
|
|
338
345
|
const filePath = paths.get(contextName);
|
|
339
346
|
if (!filePath) {
|
|
@@ -365,32 +372,11 @@ function getContextWritePath(contextName) {
|
|
|
365
372
|
return contextName.endsWith(CONTEXT_SUFFIX) ? contextName : contextName + CONTEXT_SUFFIX;
|
|
366
373
|
}
|
|
367
374
|
const config = loadConfig();
|
|
368
|
-
const
|
|
369
|
-
const paths = discoverContextPaths(root);
|
|
375
|
+
const paths = discoverContextPaths(process.cwd());
|
|
370
376
|
const existing = paths.get(contextName);
|
|
371
377
|
if (existing) return existing;
|
|
372
|
-
const
|
|
373
|
-
return join2(
|
|
374
|
-
}
|
|
375
|
-
function resolveSaveContextPath(nameOrPath) {
|
|
376
|
-
if (isAbsolute(nameOrPath) || nameOrPath.includes(sep)) {
|
|
377
|
-
return nameOrPath.endsWith(CONTEXT_SUFFIX) ? nameOrPath : nameOrPath + CONTEXT_SUFFIX;
|
|
378
|
-
}
|
|
379
|
-
return getContextWritePath(nameOrPath);
|
|
380
|
-
}
|
|
381
|
-
function getContextAtPath(filePath) {
|
|
382
|
-
if (!existsSync2(filePath)) return {};
|
|
383
|
-
try {
|
|
384
|
-
const raw = readFileSync2(filePath, "utf-8");
|
|
385
|
-
const data = JSON.parse(raw);
|
|
386
|
-
const out = {};
|
|
387
|
-
for (const [k, v] of Object.entries(data)) {
|
|
388
|
-
if (typeof v === "string") out[k] = v;
|
|
389
|
-
}
|
|
390
|
-
return out;
|
|
391
|
-
} catch {
|
|
392
|
-
return {};
|
|
393
|
-
}
|
|
378
|
+
const projectRoot = config.root ?? process.cwd();
|
|
379
|
+
return join2(projectRoot, `${contextName}${CONTEXT_SUFFIX}`);
|
|
394
380
|
}
|
|
395
381
|
function writeToContext(contextName, key, value) {
|
|
396
382
|
const path = getContextWritePath(contextName);
|
|
@@ -577,10 +563,11 @@ function scenv(name, options = {}) {
|
|
|
577
563
|
throw new Error(errMsg);
|
|
578
564
|
}
|
|
579
565
|
const final = validated.data;
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
566
|
+
const config = loadConfig();
|
|
567
|
+
if (wasPrompted) setInMemoryContext(key, String(final));
|
|
568
|
+
if (config.saveContextTo) {
|
|
569
|
+
const saveMode = config.saveMode ?? "all";
|
|
570
|
+
if (saveMode === "all" || wasPrompted) {
|
|
584
571
|
writeToContext(config.saveContextTo, key, String(final));
|
|
585
572
|
log("info", `Saved key=${key} to saveContextTo ${config.saveContextTo}`);
|
|
586
573
|
}
|
|
@@ -614,12 +601,15 @@ function scenv(name, options = {}) {
|
|
|
614
601
|
}
|
|
615
602
|
|
|
616
603
|
// src/cli-args.ts
|
|
604
|
+
import { resolve } from "path";
|
|
617
605
|
function parseScenvArgs(argv) {
|
|
618
606
|
const config = {};
|
|
619
607
|
let i = 0;
|
|
620
608
|
while (i < argv.length) {
|
|
621
609
|
const arg = argv[i];
|
|
622
|
-
if (arg === "--
|
|
610
|
+
if (arg === "--root" && argv[i + 1] !== void 0) {
|
|
611
|
+
config.root = resolve(argv[++i]);
|
|
612
|
+
} else if (arg === "--context" && argv[i + 1] !== void 0) {
|
|
623
613
|
config.context = argv[++i].split(",").map((s) => s.trim()).filter(Boolean);
|
|
624
614
|
} else if (arg === "--add-context" && argv[i + 1] !== void 0) {
|
|
625
615
|
config.addContext = argv[++i].split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -641,8 +631,11 @@ function parseScenvArgs(argv) {
|
|
|
641
631
|
}
|
|
642
632
|
} else if (arg === "--save-context-to" && argv[i + 1] !== void 0) {
|
|
643
633
|
config.saveContextTo = argv[++i];
|
|
644
|
-
} else if (arg === "--
|
|
645
|
-
|
|
634
|
+
} else if (arg === "--save-mode" && argv[i + 1] !== void 0) {
|
|
635
|
+
const v = argv[++i].toLowerCase();
|
|
636
|
+
if (v === "all" || v === "prompts-only") {
|
|
637
|
+
config.saveMode = v;
|
|
638
|
+
}
|
|
646
639
|
} else if ((arg === "--log-level" || arg === "--log") && argv[i + 1] !== void 0) {
|
|
647
640
|
const v = argv[++i].toLowerCase();
|
|
648
641
|
if (LOG_LEVELS.includes(v)) {
|