scenv 0.2.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.
package/dist/index.cjs CHANGED
@@ -235,26 +235,6 @@ function writeToContext(contextName, key, value) {
235
235
  (0, import_node_fs2.writeFileSync)(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
236
236
  }
237
237
 
238
- // src/prompt-default.ts
239
- var import_node_readline = require("readline");
240
- function defaultPrompt(name, defaultValue) {
241
- const defaultStr = defaultValue !== void 0 && defaultValue !== null ? String(defaultValue) : "";
242
- const message = defaultStr ? `Enter ${name} [${defaultStr}]: ` : `Enter ${name}: `;
243
- const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
244
- return new Promise((resolve, reject) => {
245
- rl.question(message, (answer) => {
246
- rl.close();
247
- const trimmed = answer.trim();
248
- const value = trimmed !== "" ? trimmed : defaultStr;
249
- resolve(value);
250
- });
251
- rl.on("error", (err) => {
252
- rl.close();
253
- reject(err);
254
- });
255
- });
256
- }
257
-
258
238
  // src/variable.ts
259
239
  function defaultKeyFromName(name) {
260
240
  return name.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/gi, "");
@@ -296,22 +276,29 @@ function scenv(name, options = {}) {
296
276
  if (mode === "no-env") return !hadEnv;
297
277
  return false;
298
278
  }
299
- async function getResolvedValue() {
279
+ async function getResolvedValue(overrides) {
300
280
  const config = loadConfig();
301
281
  const raw = await resolveRaw();
302
282
  const hadEnv = !config.ignoreEnv && process.env[envKey] !== void 0 && process.env[envKey] !== "";
303
283
  const hadValue = raw !== void 0;
284
+ const effectiveDefault = overrides?.default !== void 0 ? overrides.default : defaultValue;
304
285
  let wasPrompted = false;
305
286
  let value;
306
287
  if (shouldPrompt(config, hadValue, hadEnv)) {
307
- const defaultForPrompt = raw !== void 0 ? raw : defaultValue;
308
- const fn = promptFn ?? defaultPrompt;
288
+ const callbacks = getCallbacks();
289
+ const fn = overrides?.prompt ?? promptFn ?? callbacks.defaultPrompt;
290
+ if (typeof fn !== "function") {
291
+ throw new Error(
292
+ `Prompt required for variable "${name}" (key: ${key}) but no prompt was supplied and no defaultPrompt callback is configured. Set a prompt on the variable or configure({ callbacks: { defaultPrompt: ... } }).`
293
+ );
294
+ }
295
+ const defaultForPrompt = raw !== void 0 ? raw : effectiveDefault;
309
296
  value = await Promise.resolve(fn(name, defaultForPrompt));
310
297
  wasPrompted = true;
311
298
  } else if (raw !== void 0) {
312
299
  value = raw;
313
- } else if (defaultValue !== void 0) {
314
- value = defaultValue;
300
+ } else if (effectiveDefault !== void 0) {
301
+ value = effectiveDefault;
315
302
  } else {
316
303
  throw new Error(`Missing value for variable "${name}" (key: ${key})`);
317
304
  }
@@ -330,8 +317,8 @@ function scenv(name, options = {}) {
330
317
  error: "error" in normalized ? normalized.error : void 0
331
318
  };
332
319
  }
333
- async function get() {
334
- const { value, wasPrompted } = await getResolvedValue();
320
+ async function get(options2) {
321
+ const { value, wasPrompted } = await getResolvedValue(options2);
335
322
  const validated = validate(value);
336
323
  if (!validated.success) {
337
324
  throw new Error(
@@ -345,7 +332,12 @@ function scenv(name, options = {}) {
345
332
  const shouldAskSave = savePrompt === "always" || savePrompt === "ask" && wasPrompted;
346
333
  if (shouldAskSave) {
347
334
  const callbacks = getCallbacks();
348
- const ctxToSave = callbacks.onAskSaveAfterPrompt && await callbacks.onAskSaveAfterPrompt(
335
+ if (typeof callbacks.onAskSaveAfterPrompt !== "function") {
336
+ throw new Error(
337
+ `savePrompt is "${savePrompt}" but onAskSaveAfterPrompt callback is not set. Configure callbacks via configure({ callbacks: { onAskSaveAfterPrompt: ... } }).`
338
+ );
339
+ }
340
+ const ctxToSave = await callbacks.onAskSaveAfterPrompt(
349
341
  name,
350
342
  final,
351
343
  config.contexts ?? []
@@ -355,9 +347,9 @@ function scenv(name, options = {}) {
355
347
  }
356
348
  return final;
357
349
  }
358
- async function safeGet() {
350
+ async function safeGet(options2) {
359
351
  try {
360
- const v = await get();
352
+ const v = await get(options2);
361
353
  return { success: true, value: v };
362
354
  } catch (err) {
363
355
  return { success: false, error: err };
@@ -375,14 +367,15 @@ function scenv(name, options = {}) {
375
367
  let contextName = config.saveContextTo;
376
368
  if (contextName === "ask") {
377
369
  const callbacks = getCallbacks();
378
- if (typeof callbacks.onAskContext === "function") {
379
- contextName = await callbacks.onAskContext(
380
- name,
381
- config.contexts ?? []
370
+ if (typeof callbacks.onAskContext !== "function") {
371
+ throw new Error(
372
+ `saveContextTo is "ask" but onAskContext callback is not set. Configure callbacks via configure({ callbacks: { onAskContext: ... } }).`
382
373
  );
383
- } else {
384
- contextName = config.contexts?.[0] ?? "default";
385
374
  }
375
+ contextName = await callbacks.onAskContext(
376
+ name,
377
+ config.contexts ?? []
378
+ );
386
379
  }
387
380
  if (!contextName) contextName = config.contexts?.[0] ?? "default";
388
381
  writeToContext(contextName, key, String(validated.data));
package/dist/index.d.cts CHANGED
@@ -20,7 +20,11 @@ interface ScenvConfig {
20
20
  /** Root directory for config/context search (default: cwd) */
21
21
  root?: string;
22
22
  }
23
+ /** (name, defaultValue) => value; used when a variable has no prompt option. Overridable per variable. */
24
+ type DefaultPromptFn = (name: string, defaultValue: unknown) => unknown | Promise<unknown>;
23
25
  interface ScenvCallbacks {
26
+ /** Default prompt when a variable does not provide its own `prompt`. Variable's `prompt` overrides this. */
27
+ defaultPrompt?: DefaultPromptFn;
24
28
  /** When user was just prompted for a value and savePrompt is ask/always: (variableName, value, contextNames) => context name to save to, or null to skip */
25
29
  onAskSaveAfterPrompt?: (name: string, value: unknown, contextNames: string[]) => Promise<string | null>;
26
30
  /** When saveContextTo is "ask": (variableName, contextNames) => context name to save to */
@@ -66,9 +70,16 @@ interface ScenvVariableOptions<T> {
66
70
  validator?: (val: T) => ValidatorResult<T>;
67
71
  prompt?: PromptFn<T>;
68
72
  }
73
+ /** Overrides for a single .get() or .safeGet() call. */
74
+ interface GetOptions<T> {
75
+ /** Use this prompt for this call instead of the variable's prompt or callbacks.defaultPrompt. */
76
+ prompt?: PromptFn<T>;
77
+ /** Use this as the default for this call if no value from set/env/context. */
78
+ default?: T;
79
+ }
69
80
  interface ScenvVariable<T> {
70
- get(): Promise<T>;
71
- safeGet(): Promise<{
81
+ get(options?: GetOptions<T>): Promise<T>;
82
+ safeGet(options?: GetOptions<T>): Promise<{
72
83
  success: true;
73
84
  value: T;
74
85
  } | {
@@ -86,4 +97,4 @@ declare function scenv<T>(name: string, options?: ScenvVariableOptions<T>): Scen
86
97
  */
87
98
  declare function parseScenvArgs(argv: string[]): Partial<ScenvConfig>;
88
99
 
89
- export { type PromptMode, type SavePromptMode, type ScenvCallbacks, type ScenvConfig, type ScenvVariable, configure, discoverContextPaths, getCallbacks, getContextValues, loadConfig, parseScenvArgs, resetConfig, scenv };
100
+ export { type DefaultPromptFn, type GetOptions, type PromptMode, type SavePromptMode, type ScenvCallbacks, type ScenvConfig, type ScenvVariable, configure, discoverContextPaths, getCallbacks, getContextValues, loadConfig, parseScenvArgs, resetConfig, scenv };
package/dist/index.d.ts CHANGED
@@ -20,7 +20,11 @@ interface ScenvConfig {
20
20
  /** Root directory for config/context search (default: cwd) */
21
21
  root?: string;
22
22
  }
23
+ /** (name, defaultValue) => value; used when a variable has no prompt option. Overridable per variable. */
24
+ type DefaultPromptFn = (name: string, defaultValue: unknown) => unknown | Promise<unknown>;
23
25
  interface ScenvCallbacks {
26
+ /** Default prompt when a variable does not provide its own `prompt`. Variable's `prompt` overrides this. */
27
+ defaultPrompt?: DefaultPromptFn;
24
28
  /** When user was just prompted for a value and savePrompt is ask/always: (variableName, value, contextNames) => context name to save to, or null to skip */
25
29
  onAskSaveAfterPrompt?: (name: string, value: unknown, contextNames: string[]) => Promise<string | null>;
26
30
  /** When saveContextTo is "ask": (variableName, contextNames) => context name to save to */
@@ -66,9 +70,16 @@ interface ScenvVariableOptions<T> {
66
70
  validator?: (val: T) => ValidatorResult<T>;
67
71
  prompt?: PromptFn<T>;
68
72
  }
73
+ /** Overrides for a single .get() or .safeGet() call. */
74
+ interface GetOptions<T> {
75
+ /** Use this prompt for this call instead of the variable's prompt or callbacks.defaultPrompt. */
76
+ prompt?: PromptFn<T>;
77
+ /** Use this as the default for this call if no value from set/env/context. */
78
+ default?: T;
79
+ }
69
80
  interface ScenvVariable<T> {
70
- get(): Promise<T>;
71
- safeGet(): Promise<{
81
+ get(options?: GetOptions<T>): Promise<T>;
82
+ safeGet(options?: GetOptions<T>): Promise<{
72
83
  success: true;
73
84
  value: T;
74
85
  } | {
@@ -86,4 +97,4 @@ declare function scenv<T>(name: string, options?: ScenvVariableOptions<T>): Scen
86
97
  */
87
98
  declare function parseScenvArgs(argv: string[]): Partial<ScenvConfig>;
88
99
 
89
- export { type PromptMode, type SavePromptMode, type ScenvCallbacks, type ScenvConfig, type ScenvVariable, configure, discoverContextPaths, getCallbacks, getContextValues, loadConfig, parseScenvArgs, resetConfig, scenv };
100
+ export { type DefaultPromptFn, type GetOptions, type PromptMode, type SavePromptMode, type ScenvCallbacks, type ScenvConfig, type ScenvVariable, configure, discoverContextPaths, getCallbacks, getContextValues, loadConfig, parseScenvArgs, resetConfig, scenv };
package/dist/index.js CHANGED
@@ -208,26 +208,6 @@ function writeToContext(contextName, key, value) {
208
208
  writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
209
209
  }
210
210
 
211
- // src/prompt-default.ts
212
- import { createInterface } from "readline";
213
- function defaultPrompt(name, defaultValue) {
214
- const defaultStr = defaultValue !== void 0 && defaultValue !== null ? String(defaultValue) : "";
215
- const message = defaultStr ? `Enter ${name} [${defaultStr}]: ` : `Enter ${name}: `;
216
- const rl = createInterface({ input: process.stdin, output: process.stdout });
217
- return new Promise((resolve, reject) => {
218
- rl.question(message, (answer) => {
219
- rl.close();
220
- const trimmed = answer.trim();
221
- const value = trimmed !== "" ? trimmed : defaultStr;
222
- resolve(value);
223
- });
224
- rl.on("error", (err) => {
225
- rl.close();
226
- reject(err);
227
- });
228
- });
229
- }
230
-
231
211
  // src/variable.ts
232
212
  function defaultKeyFromName(name) {
233
213
  return name.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/gi, "");
@@ -269,22 +249,29 @@ function scenv(name, options = {}) {
269
249
  if (mode === "no-env") return !hadEnv;
270
250
  return false;
271
251
  }
272
- async function getResolvedValue() {
252
+ async function getResolvedValue(overrides) {
273
253
  const config = loadConfig();
274
254
  const raw = await resolveRaw();
275
255
  const hadEnv = !config.ignoreEnv && process.env[envKey] !== void 0 && process.env[envKey] !== "";
276
256
  const hadValue = raw !== void 0;
257
+ const effectiveDefault = overrides?.default !== void 0 ? overrides.default : defaultValue;
277
258
  let wasPrompted = false;
278
259
  let value;
279
260
  if (shouldPrompt(config, hadValue, hadEnv)) {
280
- const defaultForPrompt = raw !== void 0 ? raw : defaultValue;
281
- const fn = promptFn ?? defaultPrompt;
261
+ const callbacks = getCallbacks();
262
+ const fn = overrides?.prompt ?? promptFn ?? callbacks.defaultPrompt;
263
+ if (typeof fn !== "function") {
264
+ throw new Error(
265
+ `Prompt required for variable "${name}" (key: ${key}) but no prompt was supplied and no defaultPrompt callback is configured. Set a prompt on the variable or configure({ callbacks: { defaultPrompt: ... } }).`
266
+ );
267
+ }
268
+ const defaultForPrompt = raw !== void 0 ? raw : effectiveDefault;
282
269
  value = await Promise.resolve(fn(name, defaultForPrompt));
283
270
  wasPrompted = true;
284
271
  } else if (raw !== void 0) {
285
272
  value = raw;
286
- } else if (defaultValue !== void 0) {
287
- value = defaultValue;
273
+ } else if (effectiveDefault !== void 0) {
274
+ value = effectiveDefault;
288
275
  } else {
289
276
  throw new Error(`Missing value for variable "${name}" (key: ${key})`);
290
277
  }
@@ -303,8 +290,8 @@ function scenv(name, options = {}) {
303
290
  error: "error" in normalized ? normalized.error : void 0
304
291
  };
305
292
  }
306
- async function get() {
307
- const { value, wasPrompted } = await getResolvedValue();
293
+ async function get(options2) {
294
+ const { value, wasPrompted } = await getResolvedValue(options2);
308
295
  const validated = validate(value);
309
296
  if (!validated.success) {
310
297
  throw new Error(
@@ -318,7 +305,12 @@ function scenv(name, options = {}) {
318
305
  const shouldAskSave = savePrompt === "always" || savePrompt === "ask" && wasPrompted;
319
306
  if (shouldAskSave) {
320
307
  const callbacks = getCallbacks();
321
- const ctxToSave = callbacks.onAskSaveAfterPrompt && await callbacks.onAskSaveAfterPrompt(
308
+ if (typeof callbacks.onAskSaveAfterPrompt !== "function") {
309
+ throw new Error(
310
+ `savePrompt is "${savePrompt}" but onAskSaveAfterPrompt callback is not set. Configure callbacks via configure({ callbacks: { onAskSaveAfterPrompt: ... } }).`
311
+ );
312
+ }
313
+ const ctxToSave = await callbacks.onAskSaveAfterPrompt(
322
314
  name,
323
315
  final,
324
316
  config.contexts ?? []
@@ -328,9 +320,9 @@ function scenv(name, options = {}) {
328
320
  }
329
321
  return final;
330
322
  }
331
- async function safeGet() {
323
+ async function safeGet(options2) {
332
324
  try {
333
- const v = await get();
325
+ const v = await get(options2);
334
326
  return { success: true, value: v };
335
327
  } catch (err) {
336
328
  return { success: false, error: err };
@@ -348,14 +340,15 @@ function scenv(name, options = {}) {
348
340
  let contextName = config.saveContextTo;
349
341
  if (contextName === "ask") {
350
342
  const callbacks = getCallbacks();
351
- if (typeof callbacks.onAskContext === "function") {
352
- contextName = await callbacks.onAskContext(
353
- name,
354
- config.contexts ?? []
343
+ if (typeof callbacks.onAskContext !== "function") {
344
+ throw new Error(
345
+ `saveContextTo is "ask" but onAskContext callback is not set. Configure callbacks via configure({ callbacks: { onAskContext: ... } }).`
355
346
  );
356
- } else {
357
- contextName = config.contexts?.[0] ?? "default";
358
347
  }
348
+ contextName = await callbacks.onAskContext(
349
+ name,
350
+ config.contexts ?? []
351
+ );
359
352
  }
360
353
  if (!contextName) contextName = config.contexts?.[0] ?? "default";
361
354
  writeToContext(contextName, key, String(validated.data));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scenv",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Environment and context variables with runtime-configurable resolution",
5
5
  "repository": {
6
6
  "type": "git",