@optique/inquirer 1.0.0-dev.921 → 1.0.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/README.md CHANGED
@@ -9,7 +9,7 @@ when no CLI value is provided. The fallback priority is:
9
9
  CLI arguments > interactive prompt.
10
10
 
11
11
  Because interactive prompts are inherently asynchronous, the returned parser
12
- always has `$mode: "async"`.
12
+ always has `mode: "async"`.
13
13
 
14
14
  [Optique]: https://optique.dev/
15
15
  [Inquirer.js]: https://github.com/SBoudrias/Inquirer.js
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  //#endregion
24
24
  const __inquirer_prompts = __toESM(require("@inquirer/prompts"));
25
25
  const __optique_core_annotations = __toESM(require("@optique/core/annotations"));
26
+ const __optique_core_extension = __toESM(require("@optique/core/extension"));
26
27
  const __optique_core_message = __toESM(require("@optique/core/message"));
27
28
 
28
29
  //#region src/index.ts
@@ -69,41 +70,34 @@ function getPromptFunctions() {
69
70
  function isExitPromptError(error) {
70
71
  return typeof error === "object" && error != null && "name" in error && error.name === "ExitPromptError";
71
72
  }
72
- const deferredPromptValueKey = Symbol.for("@optique/inquirer/deferredPromptValue");
73
- const deferPromptUntilConfigResolvesKey = Symbol.for("@optique/config/deferPromptUntilResolved");
74
- const inheritParentAnnotationsKey = Symbol.for("@optique/core/inheritParentAnnotations");
75
- var DeferredPromptValue = class {
76
- [deferredPromptValueKey] = true;
77
- };
78
- function shouldDeferPrompt(parser, state) {
79
- const maybeShouldDefer = Reflect.get(parser, deferPromptUntilConfigResolvesKey);
80
- return typeof maybeShouldDefer === "function" && maybeShouldDefer(state) === true;
73
+ function shouldDeferPrompt(parser, state, exec) {
74
+ return typeof parser.shouldDeferCompletion === "function" && parser.shouldDeferCompletion(state, exec) === true;
81
75
  }
82
- function deferredPromptResult() {
76
+ function deferredPromptResult(placeholderValue) {
77
+ if (placeholderValue == null || typeof placeholderValue !== "object") return {
78
+ success: true,
79
+ value: placeholderValue,
80
+ deferred: true
81
+ };
82
+ const isArray = Array.isArray(placeholderValue);
83
+ const keys = /* @__PURE__ */ new Map();
84
+ for (const key of Reflect.ownKeys(placeholderValue)) {
85
+ if (isArray && key === "length") continue;
86
+ keys.set(key, null);
87
+ }
83
88
  return {
84
89
  success: true,
85
- value: new DeferredPromptValue()
90
+ value: placeholderValue,
91
+ deferred: true,
92
+ deferredKeys: keys
86
93
  };
87
94
  }
88
- function withAnnotationView(state, annotations, run) {
89
- const annotatedState = new Proxy(state, {
90
- get(target, key) {
91
- if (key === __optique_core_annotations.annotationKey) return annotations;
92
- const value = Reflect.get(target, key, target);
93
- return typeof value === "function" ? value.bind(target) : value;
94
- },
95
- has(target, key) {
96
- return key === __optique_core_annotations.annotationKey || Reflect.has(target, key);
97
- }
98
- });
99
- return run(annotatedState);
100
- }
101
95
  function withAnnotatedInnerState(sourceState, innerState, run) {
102
96
  const annotations = (0, __optique_core_annotations.getAnnotations)(sourceState);
103
- if (annotations == null || innerState == null || typeof innerState !== "object" || typeof innerState === "object" && __optique_core_annotations.annotationKey in innerState) return run(innerState);
104
- const inheritedState = (0, __optique_core_annotations.inheritAnnotations)(sourceState, innerState);
97
+ if (annotations == null || innerState == null || typeof innerState !== "object" || (0, __optique_core_annotations.getAnnotations)(innerState) != null) return run(innerState);
98
+ const inheritedState = (0, __optique_core_extension.inheritAnnotations)(sourceState, innerState);
105
99
  if (inheritedState !== innerState) return run(inheritedState);
106
- return withAnnotationView(innerState, annotations, (annotatedState) => run(annotatedState));
100
+ return run((0, __optique_core_extension.withAnnotationView)(innerState, annotations));
107
101
  }
108
102
  /**
109
103
  * Wraps a parser with an interactive Inquirer.js prompt fallback.
@@ -112,7 +106,7 @@ function withAnnotatedInnerState(sourceState, innerState, run) {
112
106
  * that value is used directly. When no CLI value is found, an interactive
113
107
  * prompt is shown to the user.
114
108
  *
115
- * The returned parser always has `$mode: "async"` because Inquirer.js prompts
109
+ * The returned parser always has `mode: "async"` because Inquirer.js prompts
116
110
  * are inherently asynchronous.
117
111
  *
118
112
  * Example:
@@ -141,21 +135,26 @@ function prompt(parser, config) {
141
135
  return value != null && typeof value === "object" && promptBindStateKey in value;
142
136
  }
143
137
  const cfg = config;
144
- const PromptBindInitialStateClass = class {
145
- [promptBindStateKey] = true;
146
- hasCliValue = false;
147
- };
148
- let promptCache = null;
149
138
  function shouldAttemptInnerCompletion(cliState, state) {
150
- if (cliState == null || cliState instanceof PromptBindInitialStateClass) return false;
151
- const cliStateHasAnnotations = typeof cliState === "object" && __optique_core_annotations.annotationKey in cliState;
139
+ if (cliState == null) return false;
140
+ const cliStateHasAnnotations = (0, __optique_core_annotations.getAnnotations)(cliState) != null;
152
141
  if (cliStateHasAnnotations) return true;
153
142
  if ((0, __optique_core_annotations.getAnnotations)(state) == null || typeof cliState !== "object") return false;
154
143
  if ("hasCliValue" in cliState) return true;
155
- if (Array.isArray(cliState)) return typeof Reflect.get(parser, deferPromptUntilConfigResolvesKey) === "function";
144
+ if (Array.isArray(cliState)) return typeof parser.shouldDeferCompletion === "function";
156
145
  const prototype = Object.getPrototypeOf(cliState);
157
146
  return prototype !== Object.prototype && prototype !== null;
158
147
  }
148
+ function hasSourceBindingMarker(state) {
149
+ return state != null && typeof state === "object" && "hasCliValue" in state && Object.getOwnPropertySymbols(state).length > 0;
150
+ }
151
+ function shouldCompleteFromSourceBinding(cliState, state) {
152
+ const cliStateIsInjectedAnnotationWrapper = cliState != null && typeof cliState === "object" && (0, __optique_core_extension.unwrapInjectedAnnotationState)(cliState) !== cliState;
153
+ const requiresSourceBindingForAnnotationWrapper = (0, __optique_core_extension.getTraits)(parser).requiresSourceBinding === true;
154
+ const hasNestedSourceBinding = hasSourceBindingMarker(cliState) || Array.isArray(cliState) && cliState.length === 1 && (hasSourceBindingMarker(cliState[0]) || cliState[0] != null && typeof cliState[0] === "object" && (0, __optique_core_annotations.getAnnotations)(cliState[0]) != null);
155
+ if (cliStateIsInjectedAnnotationWrapper && requiresSourceBindingForAnnotationWrapper) return hasNestedSourceBinding;
156
+ return shouldAttemptInnerCompletion(cliState, state) || hasNestedSourceBinding;
157
+ }
159
158
  /**
160
159
  * Executes the configured prompt and normalizes its result.
161
160
  *
@@ -166,9 +165,24 @@ function prompt(parser, config) {
166
165
  * @throws {Error} Rethrows unexpected prompt failures after converting
167
166
  * `ExitPromptError` cancellations into parse failures.
168
167
  */
169
- async function executePrompt() {
168
+ function validatePromptedValue(result) {
169
+ return result;
170
+ }
171
+ const validPromptTypes = new Set([
172
+ "confirm",
173
+ "number",
174
+ "input",
175
+ "password",
176
+ "editor",
177
+ "select",
178
+ "rawlist",
179
+ "expand",
180
+ "checkbox"
181
+ ]);
182
+ async function executePromptRaw() {
170
183
  const prompts = getPromptFunctions();
171
184
  try {
185
+ if (!validPromptTypes.has(cfg.type)) throw new TypeError(`Unsupported prompt type: ${cfg.type}`);
172
186
  if ("prompter" in cfg && cfg.prompter != null) {
173
187
  const value = await cfg.prompter();
174
188
  if (cfg.type === "number" && value === void 0) return {
@@ -269,22 +283,31 @@ function prompt(parser, config) {
269
283
  throw error;
270
284
  }
271
285
  }
272
- function usePromptOrDefer(state, result) {
273
- if (result.success) return Promise.resolve(result);
274
- return shouldDeferPrompt(parser, state) ? Promise.resolve(deferredPromptResult()) : executePrompt();
286
+ async function executePrompt() {
287
+ const result = await executePromptRaw();
288
+ return validatePromptedValue(result);
275
289
  }
276
290
  const promptedParser = {
277
- $mode: "async",
291
+ mode: "async",
278
292
  $valueType: parser.$valueType,
279
293
  $stateType: parser.$stateType,
280
294
  priority: parser.priority,
281
- [inheritParentAnnotationsKey]: true,
282
- usage: [{
295
+ usage: parser.usage.length === 1 && parser.usage[0].type === "optional" ? parser.usage : [{
283
296
  type: "optional",
284
297
  terms: parser.usage
285
298
  }],
286
- get initialState() {
287
- return new PromptBindInitialStateClass();
299
+ leadingNames: parser.leadingNames,
300
+ acceptingAnyToken: parser.acceptingAnyToken,
301
+ shouldDeferCompletion(state) {
302
+ return !isPromptBindState(state) || !state.hasCliValue;
303
+ },
304
+ getSuggestRuntimeNodes(state, path) {
305
+ const innerState = isPromptBindState(state) ? state.cliState === void 0 ? parser.initialState : state.cliState : state;
306
+ return (0, __optique_core_extension.delegateSuggestNodes)(parser, promptedParser, state, path, innerState, "prepend");
307
+ },
308
+ initialState: {
309
+ [promptBindStateKey]: true,
310
+ hasCliValue: false
288
311
  },
289
312
  parse: (context) => {
290
313
  const annotations = (0, __optique_core_annotations.getAnnotations)(context.state);
@@ -293,17 +316,19 @@ function prompt(parser, config) {
293
316
  ...context,
294
317
  state: innerState
295
318
  } : context;
319
+ const effectiveInnerState = annotations != null && innerState == null && (0, __optique_core_extension.getTraits)(parser).inheritsAnnotations === true ? (0, __optique_core_extension.injectAnnotations)(innerState, annotations) : innerState;
296
320
  const processResult = (result$1) => {
297
321
  if (result$1.success) {
322
+ const cliState = annotations != null && result$1.next.state != null && typeof result$1.next.state === "object" && (0, __optique_core_annotations.getAnnotations)(result$1.next.state) !== annotations ? (0, __optique_core_extension.injectAnnotations)(result$1.next.state, annotations) : result$1.next.state;
298
323
  const cliConsumed = result$1.consumed.length > 0;
299
- const nextState$1 = {
324
+ const nextState$1 = (0, __optique_core_extension.injectAnnotations)({
300
325
  [promptBindStateKey]: true,
301
326
  hasCliValue: cliConsumed,
302
- cliState: result$1.next.state,
303
- ...annotations != null ? { [__optique_core_annotations.annotationKey]: annotations } : {}
304
- };
327
+ cliState
328
+ }, annotations);
305
329
  return {
306
330
  success: true,
331
+ ...result$1.provisional ? { provisional: true } : {},
307
332
  next: {
308
333
  ...result$1.next,
309
334
  state: nextState$1
@@ -312,11 +337,10 @@ function prompt(parser, config) {
312
337
  };
313
338
  }
314
339
  if (result$1.consumed > 0) return result$1;
315
- const nextState = {
340
+ const nextState = (0, __optique_core_extension.injectAnnotations)({
316
341
  [promptBindStateKey]: true,
317
- hasCliValue: false,
318
- ...annotations != null ? { [__optique_core_annotations.annotationKey]: annotations } : {}
319
- };
342
+ hasCliValue: false
343
+ }, annotations);
320
344
  return {
321
345
  success: true,
322
346
  next: {
@@ -326,7 +350,7 @@ function prompt(parser, config) {
326
350
  consumed: []
327
351
  };
328
352
  };
329
- const result = withAnnotatedInnerState(context.state, innerState, (annotatedInnerState) => {
353
+ const result = withAnnotatedInnerState(context.state, effectiveInnerState, (annotatedInnerState) => {
330
354
  const innerContext = annotatedInnerState !== context.state ? {
331
355
  ...context,
332
356
  state: annotatedInnerState
@@ -336,85 +360,71 @@ function prompt(parser, config) {
336
360
  if (result instanceof Promise) return result.then(processResult);
337
361
  return Promise.resolve(processResult(result));
338
362
  },
339
- complete: (state) => {
363
+ complete: (state, exec) => {
340
364
  if (isPromptBindState(state) && state.hasCliValue) {
341
- const r = withAnnotatedInnerState(state, state.cliState, (annotatedInnerState) => parser.complete(annotatedInnerState));
365
+ const r = withAnnotatedInnerState(state, state.cliState, (annotatedInnerState) => parser.complete(annotatedInnerState, exec));
342
366
  if (r instanceof Promise) return r;
343
367
  return Promise.resolve(r);
344
368
  }
345
- if (state instanceof PromptBindInitialStateClass) {
346
- if (promptCache?.state === state) {
347
- const cached = promptCache.result;
348
- promptCache = null;
349
- return cached;
369
+ const isProbe = exec != null && exec.phase !== "complete";
370
+ const annotations = (0, __optique_core_annotations.getAnnotations)(state);
371
+ const innerInitialState = parser.initialState;
372
+ const shouldInheritInitialStateAnnotations = annotations != null && (innerInitialState == null || typeof innerInitialState === "object");
373
+ const effectiveInitialState = shouldInheritInitialStateAnnotations ? (0, __optique_core_extension.inheritAnnotations)(state, innerInitialState) : innerInitialState;
374
+ const readPlaceholder = () => {
375
+ try {
376
+ return "placeholder" in parser ? parser.placeholder : void 0;
377
+ } catch {
378
+ return void 0;
350
379
  }
351
- const hasDeferHook = typeof Reflect.get(parser, deferPromptUntilConfigResolvesKey) === "function";
352
- const annotations = (0, __optique_core_annotations.getAnnotations)(state);
353
- const innerInitialState = parser.initialState;
354
- const effectiveInitialState = annotations != null && innerInitialState == null ? (0, __optique_core_annotations.injectAnnotations)(innerInitialState, annotations) : innerInitialState;
355
- if (hasDeferHook) {
356
- const annotatedR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => parser.complete(annotatedInnerState));
357
- const usePromptOrDeferSentinel = (res) => {
358
- if (res.success && res.value === void 0) return usePromptOrDefer(state, {
359
- success: false,
360
- error: []
361
- });
362
- return usePromptOrDefer(state, res);
363
- };
364
- const cachedResult$1 = annotatedR instanceof Promise ? annotatedR.then(usePromptOrDeferSentinel) : usePromptOrDeferSentinel(annotatedR);
365
- promptCache = {
366
- state,
367
- result: cachedResult$1
368
- };
369
- return cachedResult$1;
370
- }
371
- const simParseR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedState) => parser.parse({
372
- buffer: [],
373
- state: annotatedState,
374
- optionsTerminated: false,
375
- usage: parser.usage
376
- }));
377
- const decideFromParse = (parseResult) => {
378
- const consumed = parseResult.success ? parseResult.consumed.length : 0;
379
- const cliState$1 = parseResult.success && consumed === 0 ? parseResult.next.state : void 0;
380
- const hasSourceBindingMarker = (s) => s != null && typeof s === "object" && "hasCliValue" in s && Object.getOwnPropertySymbols(s).length > 0;
381
- const cliStateIsPassthrough = cliState$1 != null && typeof cliState$1 === "object" && (0, __optique_core_annotations.unwrapInjectedAnnotationWrapper)(cliState$1) !== cliState$1;
382
- const isSourceBinding = shouldAttemptInnerCompletion(cliState$1, state) && !cliStateIsPassthrough || hasSourceBindingMarker(cliState$1) || Array.isArray(cliState$1) && cliState$1.length === 1 && (hasSourceBindingMarker(cliState$1[0]) || typeof cliState$1[0] === "object" && cliState$1[0] != null && __optique_core_annotations.annotationKey in cliState$1[0]);
383
- if (isSourceBinding) {
384
- const cliStateIsInjected = cliState$1 != null && typeof cliState$1 === "object" && (0, __optique_core_annotations.unwrapInjectedAnnotationWrapper)(cliState$1) !== cliState$1;
385
- const handleCompleteResult = (res) => {
386
- if (res.success && res.value === void 0 && cliStateIsInjected) return executePrompt();
387
- return usePromptOrDefer(state, res);
388
- };
389
- const completeState = parseResult.success ? parseResult.next.state : effectiveInitialState;
390
- const completeR = parser.complete(completeState);
391
- if (completeR instanceof Promise) return completeR.then(handleCompleteResult);
392
- return handleCompleteResult(completeR);
393
- }
394
- return executePrompt();
395
- };
396
- const cachedResult = simParseR instanceof Promise ? simParseR.then(decideFromParse) : decideFromParse(simParseR);
397
- promptCache = {
398
- state,
399
- result: cachedResult
380
+ };
381
+ const finalizePrompt = () => {
382
+ const shouldDefer = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => shouldDeferPrompt(parser, annotatedInnerState, exec));
383
+ if (shouldDefer) return Promise.resolve(deferredPromptResult(readPlaceholder()));
384
+ if (isProbe) return Promise.resolve({
385
+ success: true,
386
+ value: readPlaceholder()
387
+ });
388
+ return executePrompt();
389
+ };
390
+ const hasDeferHook = typeof parser.shouldDeferCompletion === "function";
391
+ const decideFromParse = (parseResult) => {
392
+ const consumed = parseResult.success ? parseResult.consumed.length : 0;
393
+ const cliState = parseResult.success && consumed === 0 ? parseResult.next.state : void 0;
394
+ const cliStateIsInjected = cliState != null && typeof cliState === "object" && (0, __optique_core_extension.unwrapInjectedAnnotationState)(cliState) !== cliState;
395
+ const isSourceBinding = shouldCompleteFromSourceBinding(cliState, state);
396
+ if (!isSourceBinding) return finalizePrompt();
397
+ const completeState = parseResult.success ? parseResult.next.state : effectiveInitialState;
398
+ const innerR = parser.complete(completeState, exec);
399
+ const handleCompleteResult = (res) => {
400
+ if (res.success && res.value === void 0 && cliStateIsInjected) return finalizePrompt();
401
+ if (!res.success) return finalizePrompt();
402
+ return Promise.resolve(res);
400
403
  };
401
- return cachedResult;
402
- }
403
- const cliState = isPromptBindState(state) ? state.cliState : void 0;
404
- const cliStateIsInjectedAnnotationWrapper = cliState != null && typeof cliState === "object" && (0, __optique_core_annotations.unwrapInjectedAnnotationWrapper)(cliState) !== cliState;
405
- if (shouldAttemptInnerCompletion(cliState, state)) {
406
- const useCompleteResultOrPrompt = (result) => {
407
- if (result.success && result.value === void 0 && cliStateIsInjectedAnnotationWrapper) return executePrompt();
408
- return usePromptOrDefer(state, result);
404
+ if (innerR instanceof Promise) return innerR.then(handleCompleteResult);
405
+ return handleCompleteResult(innerR);
406
+ };
407
+ if (hasDeferHook) {
408
+ const innerR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => parser.complete(annotatedInnerState, exec));
409
+ const handleDeferHookResult = (res) => {
410
+ if (res.success && res.value === void 0) return finalizePrompt();
411
+ if (!res.success) return finalizePrompt();
412
+ return Promise.resolve(res);
409
413
  };
410
- const r = withAnnotatedInnerState(state, cliState, (annotatedInnerState) => parser.complete(annotatedInnerState));
411
- if (r instanceof Promise) return r.then(useCompleteResultOrPrompt);
412
- return useCompleteResultOrPrompt(r);
414
+ if (innerR instanceof Promise) return innerR.then(handleDeferHookResult);
415
+ return handleDeferHookResult(innerR);
413
416
  }
414
- return shouldDeferPrompt(parser, state) ? Promise.resolve(deferredPromptResult()) : executePrompt();
417
+ const simParseR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedState) => parser.parse({
418
+ buffer: [],
419
+ state: annotatedState,
420
+ optionsTerminated: false,
421
+ usage: parser.usage
422
+ }));
423
+ if (simParseR instanceof Promise) return simParseR.then(decideFromParse);
424
+ return decideFromParse(simParseR);
415
425
  },
416
426
  suggest: (context, prefix) => {
417
- const innerState = isPromptBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
427
+ const innerState = isPromptBindState(context.state) ? context.state.cliState === void 0 ? parser.initialState : context.state.cliState : context.state;
418
428
  const innerContext = innerState !== context.state ? {
419
429
  ...context,
420
430
  state: innerState
@@ -430,6 +440,35 @@ function prompt(parser, config) {
430
440
  return parser.getDocFragments(state, defaultValue);
431
441
  }
432
442
  };
443
+ (0, __optique_core_extension.defineTraits)(promptedParser, { inheritsAnnotations: true });
444
+ if ("placeholder" in parser) Object.defineProperty(promptedParser, "placeholder", {
445
+ get() {
446
+ try {
447
+ return parser.placeholder;
448
+ } catch {
449
+ return void 0;
450
+ }
451
+ },
452
+ configurable: true,
453
+ enumerable: false
454
+ });
455
+ if (typeof parser.normalizeValue === "function") Object.defineProperty(promptedParser, "normalizeValue", {
456
+ value: parser.normalizeValue.bind(parser),
457
+ configurable: true,
458
+ enumerable: false
459
+ });
460
+ const dependencyMetadata = (0, __optique_core_extension.mapSourceMetadata)(parser, (source) => ({
461
+ ...source,
462
+ extractSourceValue: (state) => {
463
+ if (!isPromptBindState(state)) return source.extractSourceValue(state);
464
+ return source.extractSourceValue(state.cliState ?? state);
465
+ }
466
+ }));
467
+ if (dependencyMetadata != null) Object.defineProperty(promptedParser, "dependencyMetadata", {
468
+ value: dependencyMetadata,
469
+ configurable: true,
470
+ enumerable: false
471
+ });
433
472
  return promptedParser;
434
473
  }
435
474
  /** Normalize choices to the format Inquirer.js expects. */
package/dist/index.d.cts CHANGED
@@ -328,7 +328,7 @@ type BasePromptConfig<T> = T extends boolean ? ConfirmConfig : T extends number
328
328
  * that value is used directly. When no CLI value is found, an interactive
329
329
  * prompt is shown to the user.
330
330
  *
331
- * The returned parser always has `$mode: "async"` because Inquirer.js prompts
331
+ * The returned parser always has `mode: "async"` because Inquirer.js prompts
332
332
  * are inherently asynchronous.
333
333
  *
334
334
  * Example:
package/dist/index.d.ts CHANGED
@@ -328,7 +328,7 @@ type BasePromptConfig<T> = T extends boolean ? ConfirmConfig : T extends number
328
328
  * that value is used directly. When no CLI value is found, an interactive
329
329
  * prompt is shown to the user.
330
330
  *
331
- * The returned parser always has `$mode: "async"` because Inquirer.js prompts
331
+ * The returned parser always has `mode: "async"` because Inquirer.js prompts
332
332
  * are inherently asynchronous.
333
333
  *
334
334
  * Example:
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Separator, checkbox, confirm, editor, expand, input, number, password, rawlist, select } from "@inquirer/prompts";
2
- import { annotationKey, getAnnotations, inheritAnnotations, injectAnnotations, unwrapInjectedAnnotationWrapper } from "@optique/core/annotations";
2
+ import { getAnnotations } from "@optique/core/annotations";
3
+ import { defineTraits, delegateSuggestNodes, getTraits, inheritAnnotations, injectAnnotations, mapSourceMetadata, unwrapInjectedAnnotationState, withAnnotationView } from "@optique/core/extension";
3
4
  import { message } from "@optique/core/message";
4
5
 
5
6
  //#region src/index.ts
@@ -46,41 +47,34 @@ function getPromptFunctions() {
46
47
  function isExitPromptError(error) {
47
48
  return typeof error === "object" && error != null && "name" in error && error.name === "ExitPromptError";
48
49
  }
49
- const deferredPromptValueKey = Symbol.for("@optique/inquirer/deferredPromptValue");
50
- const deferPromptUntilConfigResolvesKey = Symbol.for("@optique/config/deferPromptUntilResolved");
51
- const inheritParentAnnotationsKey = Symbol.for("@optique/core/inheritParentAnnotations");
52
- var DeferredPromptValue = class {
53
- [deferredPromptValueKey] = true;
54
- };
55
- function shouldDeferPrompt(parser, state) {
56
- const maybeShouldDefer = Reflect.get(parser, deferPromptUntilConfigResolvesKey);
57
- return typeof maybeShouldDefer === "function" && maybeShouldDefer(state) === true;
50
+ function shouldDeferPrompt(parser, state, exec) {
51
+ return typeof parser.shouldDeferCompletion === "function" && parser.shouldDeferCompletion(state, exec) === true;
58
52
  }
59
- function deferredPromptResult() {
53
+ function deferredPromptResult(placeholderValue) {
54
+ if (placeholderValue == null || typeof placeholderValue !== "object") return {
55
+ success: true,
56
+ value: placeholderValue,
57
+ deferred: true
58
+ };
59
+ const isArray = Array.isArray(placeholderValue);
60
+ const keys = /* @__PURE__ */ new Map();
61
+ for (const key of Reflect.ownKeys(placeholderValue)) {
62
+ if (isArray && key === "length") continue;
63
+ keys.set(key, null);
64
+ }
60
65
  return {
61
66
  success: true,
62
- value: new DeferredPromptValue()
67
+ value: placeholderValue,
68
+ deferred: true,
69
+ deferredKeys: keys
63
70
  };
64
71
  }
65
- function withAnnotationView(state, annotations, run) {
66
- const annotatedState = new Proxy(state, {
67
- get(target, key) {
68
- if (key === annotationKey) return annotations;
69
- const value = Reflect.get(target, key, target);
70
- return typeof value === "function" ? value.bind(target) : value;
71
- },
72
- has(target, key) {
73
- return key === annotationKey || Reflect.has(target, key);
74
- }
75
- });
76
- return run(annotatedState);
77
- }
78
72
  function withAnnotatedInnerState(sourceState, innerState, run) {
79
73
  const annotations = getAnnotations(sourceState);
80
- if (annotations == null || innerState == null || typeof innerState !== "object" || typeof innerState === "object" && annotationKey in innerState) return run(innerState);
74
+ if (annotations == null || innerState == null || typeof innerState !== "object" || getAnnotations(innerState) != null) return run(innerState);
81
75
  const inheritedState = inheritAnnotations(sourceState, innerState);
82
76
  if (inheritedState !== innerState) return run(inheritedState);
83
- return withAnnotationView(innerState, annotations, (annotatedState) => run(annotatedState));
77
+ return run(withAnnotationView(innerState, annotations));
84
78
  }
85
79
  /**
86
80
  * Wraps a parser with an interactive Inquirer.js prompt fallback.
@@ -89,7 +83,7 @@ function withAnnotatedInnerState(sourceState, innerState, run) {
89
83
  * that value is used directly. When no CLI value is found, an interactive
90
84
  * prompt is shown to the user.
91
85
  *
92
- * The returned parser always has `$mode: "async"` because Inquirer.js prompts
86
+ * The returned parser always has `mode: "async"` because Inquirer.js prompts
93
87
  * are inherently asynchronous.
94
88
  *
95
89
  * Example:
@@ -118,21 +112,26 @@ function prompt(parser, config) {
118
112
  return value != null && typeof value === "object" && promptBindStateKey in value;
119
113
  }
120
114
  const cfg = config;
121
- const PromptBindInitialStateClass = class {
122
- [promptBindStateKey] = true;
123
- hasCliValue = false;
124
- };
125
- let promptCache = null;
126
115
  function shouldAttemptInnerCompletion(cliState, state) {
127
- if (cliState == null || cliState instanceof PromptBindInitialStateClass) return false;
128
- const cliStateHasAnnotations = typeof cliState === "object" && annotationKey in cliState;
116
+ if (cliState == null) return false;
117
+ const cliStateHasAnnotations = getAnnotations(cliState) != null;
129
118
  if (cliStateHasAnnotations) return true;
130
119
  if (getAnnotations(state) == null || typeof cliState !== "object") return false;
131
120
  if ("hasCliValue" in cliState) return true;
132
- if (Array.isArray(cliState)) return typeof Reflect.get(parser, deferPromptUntilConfigResolvesKey) === "function";
121
+ if (Array.isArray(cliState)) return typeof parser.shouldDeferCompletion === "function";
133
122
  const prototype = Object.getPrototypeOf(cliState);
134
123
  return prototype !== Object.prototype && prototype !== null;
135
124
  }
125
+ function hasSourceBindingMarker(state) {
126
+ return state != null && typeof state === "object" && "hasCliValue" in state && Object.getOwnPropertySymbols(state).length > 0;
127
+ }
128
+ function shouldCompleteFromSourceBinding(cliState, state) {
129
+ const cliStateIsInjectedAnnotationWrapper = cliState != null && typeof cliState === "object" && unwrapInjectedAnnotationState(cliState) !== cliState;
130
+ const requiresSourceBindingForAnnotationWrapper = getTraits(parser).requiresSourceBinding === true;
131
+ const hasNestedSourceBinding = hasSourceBindingMarker(cliState) || Array.isArray(cliState) && cliState.length === 1 && (hasSourceBindingMarker(cliState[0]) || cliState[0] != null && typeof cliState[0] === "object" && getAnnotations(cliState[0]) != null);
132
+ if (cliStateIsInjectedAnnotationWrapper && requiresSourceBindingForAnnotationWrapper) return hasNestedSourceBinding;
133
+ return shouldAttemptInnerCompletion(cliState, state) || hasNestedSourceBinding;
134
+ }
136
135
  /**
137
136
  * Executes the configured prompt and normalizes its result.
138
137
  *
@@ -143,9 +142,24 @@ function prompt(parser, config) {
143
142
  * @throws {Error} Rethrows unexpected prompt failures after converting
144
143
  * `ExitPromptError` cancellations into parse failures.
145
144
  */
146
- async function executePrompt() {
145
+ function validatePromptedValue(result) {
146
+ return result;
147
+ }
148
+ const validPromptTypes = new Set([
149
+ "confirm",
150
+ "number",
151
+ "input",
152
+ "password",
153
+ "editor",
154
+ "select",
155
+ "rawlist",
156
+ "expand",
157
+ "checkbox"
158
+ ]);
159
+ async function executePromptRaw() {
147
160
  const prompts = getPromptFunctions();
148
161
  try {
162
+ if (!validPromptTypes.has(cfg.type)) throw new TypeError(`Unsupported prompt type: ${cfg.type}`);
149
163
  if ("prompter" in cfg && cfg.prompter != null) {
150
164
  const value = await cfg.prompter();
151
165
  if (cfg.type === "number" && value === void 0) return {
@@ -246,22 +260,31 @@ function prompt(parser, config) {
246
260
  throw error;
247
261
  }
248
262
  }
249
- function usePromptOrDefer(state, result) {
250
- if (result.success) return Promise.resolve(result);
251
- return shouldDeferPrompt(parser, state) ? Promise.resolve(deferredPromptResult()) : executePrompt();
263
+ async function executePrompt() {
264
+ const result = await executePromptRaw();
265
+ return validatePromptedValue(result);
252
266
  }
253
267
  const promptedParser = {
254
- $mode: "async",
268
+ mode: "async",
255
269
  $valueType: parser.$valueType,
256
270
  $stateType: parser.$stateType,
257
271
  priority: parser.priority,
258
- [inheritParentAnnotationsKey]: true,
259
- usage: [{
272
+ usage: parser.usage.length === 1 && parser.usage[0].type === "optional" ? parser.usage : [{
260
273
  type: "optional",
261
274
  terms: parser.usage
262
275
  }],
263
- get initialState() {
264
- return new PromptBindInitialStateClass();
276
+ leadingNames: parser.leadingNames,
277
+ acceptingAnyToken: parser.acceptingAnyToken,
278
+ shouldDeferCompletion(state) {
279
+ return !isPromptBindState(state) || !state.hasCliValue;
280
+ },
281
+ getSuggestRuntimeNodes(state, path) {
282
+ const innerState = isPromptBindState(state) ? state.cliState === void 0 ? parser.initialState : state.cliState : state;
283
+ return delegateSuggestNodes(parser, promptedParser, state, path, innerState, "prepend");
284
+ },
285
+ initialState: {
286
+ [promptBindStateKey]: true,
287
+ hasCliValue: false
265
288
  },
266
289
  parse: (context) => {
267
290
  const annotations = getAnnotations(context.state);
@@ -270,17 +293,19 @@ function prompt(parser, config) {
270
293
  ...context,
271
294
  state: innerState
272
295
  } : context;
296
+ const effectiveInnerState = annotations != null && innerState == null && getTraits(parser).inheritsAnnotations === true ? injectAnnotations(innerState, annotations) : innerState;
273
297
  const processResult = (result$1) => {
274
298
  if (result$1.success) {
299
+ const cliState = annotations != null && result$1.next.state != null && typeof result$1.next.state === "object" && getAnnotations(result$1.next.state) !== annotations ? injectAnnotations(result$1.next.state, annotations) : result$1.next.state;
275
300
  const cliConsumed = result$1.consumed.length > 0;
276
- const nextState$1 = {
301
+ const nextState$1 = injectAnnotations({
277
302
  [promptBindStateKey]: true,
278
303
  hasCliValue: cliConsumed,
279
- cliState: result$1.next.state,
280
- ...annotations != null ? { [annotationKey]: annotations } : {}
281
- };
304
+ cliState
305
+ }, annotations);
282
306
  return {
283
307
  success: true,
308
+ ...result$1.provisional ? { provisional: true } : {},
284
309
  next: {
285
310
  ...result$1.next,
286
311
  state: nextState$1
@@ -289,11 +314,10 @@ function prompt(parser, config) {
289
314
  };
290
315
  }
291
316
  if (result$1.consumed > 0) return result$1;
292
- const nextState = {
317
+ const nextState = injectAnnotations({
293
318
  [promptBindStateKey]: true,
294
- hasCliValue: false,
295
- ...annotations != null ? { [annotationKey]: annotations } : {}
296
- };
319
+ hasCliValue: false
320
+ }, annotations);
297
321
  return {
298
322
  success: true,
299
323
  next: {
@@ -303,7 +327,7 @@ function prompt(parser, config) {
303
327
  consumed: []
304
328
  };
305
329
  };
306
- const result = withAnnotatedInnerState(context.state, innerState, (annotatedInnerState) => {
330
+ const result = withAnnotatedInnerState(context.state, effectiveInnerState, (annotatedInnerState) => {
307
331
  const innerContext = annotatedInnerState !== context.state ? {
308
332
  ...context,
309
333
  state: annotatedInnerState
@@ -313,85 +337,71 @@ function prompt(parser, config) {
313
337
  if (result instanceof Promise) return result.then(processResult);
314
338
  return Promise.resolve(processResult(result));
315
339
  },
316
- complete: (state) => {
340
+ complete: (state, exec) => {
317
341
  if (isPromptBindState(state) && state.hasCliValue) {
318
- const r = withAnnotatedInnerState(state, state.cliState, (annotatedInnerState) => parser.complete(annotatedInnerState));
342
+ const r = withAnnotatedInnerState(state, state.cliState, (annotatedInnerState) => parser.complete(annotatedInnerState, exec));
319
343
  if (r instanceof Promise) return r;
320
344
  return Promise.resolve(r);
321
345
  }
322
- if (state instanceof PromptBindInitialStateClass) {
323
- if (promptCache?.state === state) {
324
- const cached = promptCache.result;
325
- promptCache = null;
326
- return cached;
346
+ const isProbe = exec != null && exec.phase !== "complete";
347
+ const annotations = getAnnotations(state);
348
+ const innerInitialState = parser.initialState;
349
+ const shouldInheritInitialStateAnnotations = annotations != null && (innerInitialState == null || typeof innerInitialState === "object");
350
+ const effectiveInitialState = shouldInheritInitialStateAnnotations ? inheritAnnotations(state, innerInitialState) : innerInitialState;
351
+ const readPlaceholder = () => {
352
+ try {
353
+ return "placeholder" in parser ? parser.placeholder : void 0;
354
+ } catch {
355
+ return void 0;
327
356
  }
328
- const hasDeferHook = typeof Reflect.get(parser, deferPromptUntilConfigResolvesKey) === "function";
329
- const annotations = getAnnotations(state);
330
- const innerInitialState = parser.initialState;
331
- const effectiveInitialState = annotations != null && innerInitialState == null ? injectAnnotations(innerInitialState, annotations) : innerInitialState;
332
- if (hasDeferHook) {
333
- const annotatedR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => parser.complete(annotatedInnerState));
334
- const usePromptOrDeferSentinel = (res) => {
335
- if (res.success && res.value === void 0) return usePromptOrDefer(state, {
336
- success: false,
337
- error: []
338
- });
339
- return usePromptOrDefer(state, res);
340
- };
341
- const cachedResult$1 = annotatedR instanceof Promise ? annotatedR.then(usePromptOrDeferSentinel) : usePromptOrDeferSentinel(annotatedR);
342
- promptCache = {
343
- state,
344
- result: cachedResult$1
345
- };
346
- return cachedResult$1;
347
- }
348
- const simParseR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedState) => parser.parse({
349
- buffer: [],
350
- state: annotatedState,
351
- optionsTerminated: false,
352
- usage: parser.usage
353
- }));
354
- const decideFromParse = (parseResult) => {
355
- const consumed = parseResult.success ? parseResult.consumed.length : 0;
356
- const cliState$1 = parseResult.success && consumed === 0 ? parseResult.next.state : void 0;
357
- const hasSourceBindingMarker = (s) => s != null && typeof s === "object" && "hasCliValue" in s && Object.getOwnPropertySymbols(s).length > 0;
358
- const cliStateIsPassthrough = cliState$1 != null && typeof cliState$1 === "object" && unwrapInjectedAnnotationWrapper(cliState$1) !== cliState$1;
359
- const isSourceBinding = shouldAttemptInnerCompletion(cliState$1, state) && !cliStateIsPassthrough || hasSourceBindingMarker(cliState$1) || Array.isArray(cliState$1) && cliState$1.length === 1 && (hasSourceBindingMarker(cliState$1[0]) || typeof cliState$1[0] === "object" && cliState$1[0] != null && annotationKey in cliState$1[0]);
360
- if (isSourceBinding) {
361
- const cliStateIsInjected = cliState$1 != null && typeof cliState$1 === "object" && unwrapInjectedAnnotationWrapper(cliState$1) !== cliState$1;
362
- const handleCompleteResult = (res) => {
363
- if (res.success && res.value === void 0 && cliStateIsInjected) return executePrompt();
364
- return usePromptOrDefer(state, res);
365
- };
366
- const completeState = parseResult.success ? parseResult.next.state : effectiveInitialState;
367
- const completeR = parser.complete(completeState);
368
- if (completeR instanceof Promise) return completeR.then(handleCompleteResult);
369
- return handleCompleteResult(completeR);
370
- }
371
- return executePrompt();
372
- };
373
- const cachedResult = simParseR instanceof Promise ? simParseR.then(decideFromParse) : decideFromParse(simParseR);
374
- promptCache = {
375
- state,
376
- result: cachedResult
357
+ };
358
+ const finalizePrompt = () => {
359
+ const shouldDefer = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => shouldDeferPrompt(parser, annotatedInnerState, exec));
360
+ if (shouldDefer) return Promise.resolve(deferredPromptResult(readPlaceholder()));
361
+ if (isProbe) return Promise.resolve({
362
+ success: true,
363
+ value: readPlaceholder()
364
+ });
365
+ return executePrompt();
366
+ };
367
+ const hasDeferHook = typeof parser.shouldDeferCompletion === "function";
368
+ const decideFromParse = (parseResult) => {
369
+ const consumed = parseResult.success ? parseResult.consumed.length : 0;
370
+ const cliState = parseResult.success && consumed === 0 ? parseResult.next.state : void 0;
371
+ const cliStateIsInjected = cliState != null && typeof cliState === "object" && unwrapInjectedAnnotationState(cliState) !== cliState;
372
+ const isSourceBinding = shouldCompleteFromSourceBinding(cliState, state);
373
+ if (!isSourceBinding) return finalizePrompt();
374
+ const completeState = parseResult.success ? parseResult.next.state : effectiveInitialState;
375
+ const innerR = parser.complete(completeState, exec);
376
+ const handleCompleteResult = (res) => {
377
+ if (res.success && res.value === void 0 && cliStateIsInjected) return finalizePrompt();
378
+ if (!res.success) return finalizePrompt();
379
+ return Promise.resolve(res);
377
380
  };
378
- return cachedResult;
379
- }
380
- const cliState = isPromptBindState(state) ? state.cliState : void 0;
381
- const cliStateIsInjectedAnnotationWrapper = cliState != null && typeof cliState === "object" && unwrapInjectedAnnotationWrapper(cliState) !== cliState;
382
- if (shouldAttemptInnerCompletion(cliState, state)) {
383
- const useCompleteResultOrPrompt = (result) => {
384
- if (result.success && result.value === void 0 && cliStateIsInjectedAnnotationWrapper) return executePrompt();
385
- return usePromptOrDefer(state, result);
381
+ if (innerR instanceof Promise) return innerR.then(handleCompleteResult);
382
+ return handleCompleteResult(innerR);
383
+ };
384
+ if (hasDeferHook) {
385
+ const innerR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => parser.complete(annotatedInnerState, exec));
386
+ const handleDeferHookResult = (res) => {
387
+ if (res.success && res.value === void 0) return finalizePrompt();
388
+ if (!res.success) return finalizePrompt();
389
+ return Promise.resolve(res);
386
390
  };
387
- const r = withAnnotatedInnerState(state, cliState, (annotatedInnerState) => parser.complete(annotatedInnerState));
388
- if (r instanceof Promise) return r.then(useCompleteResultOrPrompt);
389
- return useCompleteResultOrPrompt(r);
391
+ if (innerR instanceof Promise) return innerR.then(handleDeferHookResult);
392
+ return handleDeferHookResult(innerR);
390
393
  }
391
- return shouldDeferPrompt(parser, state) ? Promise.resolve(deferredPromptResult()) : executePrompt();
394
+ const simParseR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedState) => parser.parse({
395
+ buffer: [],
396
+ state: annotatedState,
397
+ optionsTerminated: false,
398
+ usage: parser.usage
399
+ }));
400
+ if (simParseR instanceof Promise) return simParseR.then(decideFromParse);
401
+ return decideFromParse(simParseR);
392
402
  },
393
403
  suggest: (context, prefix) => {
394
- const innerState = isPromptBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
404
+ const innerState = isPromptBindState(context.state) ? context.state.cliState === void 0 ? parser.initialState : context.state.cliState : context.state;
395
405
  const innerContext = innerState !== context.state ? {
396
406
  ...context,
397
407
  state: innerState
@@ -407,6 +417,35 @@ function prompt(parser, config) {
407
417
  return parser.getDocFragments(state, defaultValue);
408
418
  }
409
419
  };
420
+ defineTraits(promptedParser, { inheritsAnnotations: true });
421
+ if ("placeholder" in parser) Object.defineProperty(promptedParser, "placeholder", {
422
+ get() {
423
+ try {
424
+ return parser.placeholder;
425
+ } catch {
426
+ return void 0;
427
+ }
428
+ },
429
+ configurable: true,
430
+ enumerable: false
431
+ });
432
+ if (typeof parser.normalizeValue === "function") Object.defineProperty(promptedParser, "normalizeValue", {
433
+ value: parser.normalizeValue.bind(parser),
434
+ configurable: true,
435
+ enumerable: false
436
+ });
437
+ const dependencyMetadata = mapSourceMetadata(parser, (source) => ({
438
+ ...source,
439
+ extractSourceValue: (state) => {
440
+ if (!isPromptBindState(state)) return source.extractSourceValue(state);
441
+ return source.extractSourceValue(state.cliState ?? state);
442
+ }
443
+ }));
444
+ if (dependencyMetadata != null) Object.defineProperty(promptedParser, "dependencyMetadata", {
445
+ value: dependencyMetadata,
446
+ configurable: true,
447
+ enumerable: false
448
+ });
410
449
  return promptedParser;
411
450
  }
412
451
  /** Normalize choices to the format Inquirer.js expects. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/inquirer",
3
- "version": "1.0.0-dev.921+754748bd",
3
+ "version": "1.0.1",
4
4
  "description": "Interactive prompt support for Optique via Inquirer.js",
5
5
  "keywords": [
6
6
  "CLI",
@@ -56,13 +56,13 @@
56
56
  "sideEffects": false,
57
57
  "dependencies": {
58
58
  "@inquirer/prompts": "^8.3.0",
59
- "@optique/core": "1.0.0-dev.921+754748bd"
59
+ "@optique/core": "1.0.1"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^20.19.9",
63
63
  "tsdown": "^0.13.0",
64
64
  "typescript": "^5.8.3",
65
- "@optique/env": "1.0.0-dev.921+754748bd"
65
+ "@optique/env": "1.0.1"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsdown",