@optique/inquirer 1.0.0-dev.908 → 1.0.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/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,28 @@ 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
+ getSuggestRuntimeNodes(state, path) {
302
+ const innerState = isPromptBindState(state) ? state.cliState === void 0 ? parser.initialState : state.cliState : state;
303
+ return (0, __optique_core_extension.delegateSuggestNodes)(parser, promptedParser, state, path, innerState, "prepend");
304
+ },
305
+ initialState: {
306
+ [promptBindStateKey]: true,
307
+ hasCliValue: false
288
308
  },
289
309
  parse: (context) => {
290
310
  const annotations = (0, __optique_core_annotations.getAnnotations)(context.state);
@@ -293,17 +313,19 @@ function prompt(parser, config) {
293
313
  ...context,
294
314
  state: innerState
295
315
  } : context;
316
+ const effectiveInnerState = annotations != null && innerState == null && (0, __optique_core_extension.getTraits)(parser).inheritsAnnotations === true ? (0, __optique_core_extension.injectAnnotations)(innerState, annotations) : innerState;
296
317
  const processResult = (result$1) => {
297
318
  if (result$1.success) {
319
+ 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
320
  const cliConsumed = result$1.consumed.length > 0;
299
- const nextState$1 = {
321
+ const nextState$1 = (0, __optique_core_extension.injectAnnotations)({
300
322
  [promptBindStateKey]: true,
301
323
  hasCliValue: cliConsumed,
302
- cliState: result$1.next.state,
303
- ...annotations != null ? { [__optique_core_annotations.annotationKey]: annotations } : {}
304
- };
324
+ cliState
325
+ }, annotations);
305
326
  return {
306
327
  success: true,
328
+ ...result$1.provisional ? { provisional: true } : {},
307
329
  next: {
308
330
  ...result$1.next,
309
331
  state: nextState$1
@@ -312,11 +334,10 @@ function prompt(parser, config) {
312
334
  };
313
335
  }
314
336
  if (result$1.consumed > 0) return result$1;
315
- const nextState = {
337
+ const nextState = (0, __optique_core_extension.injectAnnotations)({
316
338
  [promptBindStateKey]: true,
317
- hasCliValue: false,
318
- ...annotations != null ? { [__optique_core_annotations.annotationKey]: annotations } : {}
319
- };
339
+ hasCliValue: false
340
+ }, annotations);
320
341
  return {
321
342
  success: true,
322
343
  next: {
@@ -326,7 +347,7 @@ function prompt(parser, config) {
326
347
  consumed: []
327
348
  };
328
349
  };
329
- const result = withAnnotatedInnerState(context.state, innerState, (annotatedInnerState) => {
350
+ const result = withAnnotatedInnerState(context.state, effectiveInnerState, (annotatedInnerState) => {
330
351
  const innerContext = annotatedInnerState !== context.state ? {
331
352
  ...context,
332
353
  state: annotatedInnerState
@@ -336,85 +357,71 @@ function prompt(parser, config) {
336
357
  if (result instanceof Promise) return result.then(processResult);
337
358
  return Promise.resolve(processResult(result));
338
359
  },
339
- complete: (state) => {
360
+ complete: (state, exec) => {
340
361
  if (isPromptBindState(state) && state.hasCliValue) {
341
- const r = withAnnotatedInnerState(state, state.cliState, (annotatedInnerState) => parser.complete(annotatedInnerState));
362
+ const r = withAnnotatedInnerState(state, state.cliState, (annotatedInnerState) => parser.complete(annotatedInnerState, exec));
342
363
  if (r instanceof Promise) return r;
343
364
  return Promise.resolve(r);
344
365
  }
345
- if (state instanceof PromptBindInitialStateClass) {
346
- if (promptCache?.state === state) {
347
- const cached = promptCache.result;
348
- promptCache = null;
349
- return cached;
366
+ const isProbe = exec != null && exec.phase !== "complete";
367
+ const annotations = (0, __optique_core_annotations.getAnnotations)(state);
368
+ const innerInitialState = parser.initialState;
369
+ const shouldInheritInitialStateAnnotations = annotations != null && (innerInitialState == null || typeof innerInitialState === "object");
370
+ const effectiveInitialState = shouldInheritInitialStateAnnotations ? (0, __optique_core_extension.inheritAnnotations)(state, innerInitialState) : innerInitialState;
371
+ const readPlaceholder = () => {
372
+ try {
373
+ return "placeholder" in parser ? parser.placeholder : void 0;
374
+ } catch {
375
+ return void 0;
350
376
  }
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
377
+ };
378
+ const finalizePrompt = () => {
379
+ const shouldDefer = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => shouldDeferPrompt(parser, annotatedInnerState, exec));
380
+ if (shouldDefer) return Promise.resolve(deferredPromptResult(readPlaceholder()));
381
+ if (isProbe) return Promise.resolve({
382
+ success: true,
383
+ value: readPlaceholder()
384
+ });
385
+ return executePrompt();
386
+ };
387
+ const hasDeferHook = typeof parser.shouldDeferCompletion === "function";
388
+ const decideFromParse = (parseResult) => {
389
+ const consumed = parseResult.success ? parseResult.consumed.length : 0;
390
+ const cliState = parseResult.success && consumed === 0 ? parseResult.next.state : void 0;
391
+ const cliStateIsInjected = cliState != null && typeof cliState === "object" && (0, __optique_core_extension.unwrapInjectedAnnotationState)(cliState) !== cliState;
392
+ const isSourceBinding = shouldCompleteFromSourceBinding(cliState, state);
393
+ if (!isSourceBinding) return finalizePrompt();
394
+ const completeState = parseResult.success ? parseResult.next.state : effectiveInitialState;
395
+ const innerR = parser.complete(completeState, exec);
396
+ const handleCompleteResult = (res) => {
397
+ if (res.success && res.value === void 0 && cliStateIsInjected) return finalizePrompt();
398
+ if (!res.success) return finalizePrompt();
399
+ return Promise.resolve(res);
400
400
  };
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);
401
+ if (innerR instanceof Promise) return innerR.then(handleCompleteResult);
402
+ return handleCompleteResult(innerR);
403
+ };
404
+ if (hasDeferHook) {
405
+ const innerR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => parser.complete(annotatedInnerState, exec));
406
+ const handleDeferHookResult = (res) => {
407
+ if (res.success && res.value === void 0) return finalizePrompt();
408
+ if (!res.success) return finalizePrompt();
409
+ return Promise.resolve(res);
409
410
  };
410
- const r = withAnnotatedInnerState(state, cliState, (annotatedInnerState) => parser.complete(annotatedInnerState));
411
- if (r instanceof Promise) return r.then(useCompleteResultOrPrompt);
412
- return useCompleteResultOrPrompt(r);
411
+ if (innerR instanceof Promise) return innerR.then(handleDeferHookResult);
412
+ return handleDeferHookResult(innerR);
413
413
  }
414
- return shouldDeferPrompt(parser, state) ? Promise.resolve(deferredPromptResult()) : executePrompt();
414
+ const simParseR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedState) => parser.parse({
415
+ buffer: [],
416
+ state: annotatedState,
417
+ optionsTerminated: false,
418
+ usage: parser.usage
419
+ }));
420
+ if (simParseR instanceof Promise) return simParseR.then(decideFromParse);
421
+ return decideFromParse(simParseR);
415
422
  },
416
423
  suggest: (context, prefix) => {
417
- const innerState = isPromptBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
424
+ const innerState = isPromptBindState(context.state) ? context.state.cliState === void 0 ? parser.initialState : context.state.cliState : context.state;
418
425
  const innerContext = innerState !== context.state ? {
419
426
  ...context,
420
427
  state: innerState
@@ -430,6 +437,35 @@ function prompt(parser, config) {
430
437
  return parser.getDocFragments(state, defaultValue);
431
438
  }
432
439
  };
440
+ (0, __optique_core_extension.defineTraits)(promptedParser, { inheritsAnnotations: true });
441
+ if ("placeholder" in parser) Object.defineProperty(promptedParser, "placeholder", {
442
+ get() {
443
+ try {
444
+ return parser.placeholder;
445
+ } catch {
446
+ return void 0;
447
+ }
448
+ },
449
+ configurable: true,
450
+ enumerable: false
451
+ });
452
+ if (typeof parser.normalizeValue === "function") Object.defineProperty(promptedParser, "normalizeValue", {
453
+ value: parser.normalizeValue.bind(parser),
454
+ configurable: true,
455
+ enumerable: false
456
+ });
457
+ const dependencyMetadata = (0, __optique_core_extension.mapSourceMetadata)(parser, (source) => ({
458
+ ...source,
459
+ extractSourceValue: (state) => {
460
+ if (!isPromptBindState(state)) return source.extractSourceValue(state);
461
+ return source.extractSourceValue(state.cliState ?? state);
462
+ }
463
+ }));
464
+ if (dependencyMetadata != null) Object.defineProperty(promptedParser, "dependencyMetadata", {
465
+ value: dependencyMetadata,
466
+ configurable: true,
467
+ enumerable: false
468
+ });
433
469
  return promptedParser;
434
470
  }
435
471
  /** 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,28 @@ 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
+ getSuggestRuntimeNodes(state, path) {
279
+ const innerState = isPromptBindState(state) ? state.cliState === void 0 ? parser.initialState : state.cliState : state;
280
+ return delegateSuggestNodes(parser, promptedParser, state, path, innerState, "prepend");
281
+ },
282
+ initialState: {
283
+ [promptBindStateKey]: true,
284
+ hasCliValue: false
265
285
  },
266
286
  parse: (context) => {
267
287
  const annotations = getAnnotations(context.state);
@@ -270,17 +290,19 @@ function prompt(parser, config) {
270
290
  ...context,
271
291
  state: innerState
272
292
  } : context;
293
+ const effectiveInnerState = annotations != null && innerState == null && getTraits(parser).inheritsAnnotations === true ? injectAnnotations(innerState, annotations) : innerState;
273
294
  const processResult = (result$1) => {
274
295
  if (result$1.success) {
296
+ 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
297
  const cliConsumed = result$1.consumed.length > 0;
276
- const nextState$1 = {
298
+ const nextState$1 = injectAnnotations({
277
299
  [promptBindStateKey]: true,
278
300
  hasCliValue: cliConsumed,
279
- cliState: result$1.next.state,
280
- ...annotations != null ? { [annotationKey]: annotations } : {}
281
- };
301
+ cliState
302
+ }, annotations);
282
303
  return {
283
304
  success: true,
305
+ ...result$1.provisional ? { provisional: true } : {},
284
306
  next: {
285
307
  ...result$1.next,
286
308
  state: nextState$1
@@ -289,11 +311,10 @@ function prompt(parser, config) {
289
311
  };
290
312
  }
291
313
  if (result$1.consumed > 0) return result$1;
292
- const nextState = {
314
+ const nextState = injectAnnotations({
293
315
  [promptBindStateKey]: true,
294
- hasCliValue: false,
295
- ...annotations != null ? { [annotationKey]: annotations } : {}
296
- };
316
+ hasCliValue: false
317
+ }, annotations);
297
318
  return {
298
319
  success: true,
299
320
  next: {
@@ -303,7 +324,7 @@ function prompt(parser, config) {
303
324
  consumed: []
304
325
  };
305
326
  };
306
- const result = withAnnotatedInnerState(context.state, innerState, (annotatedInnerState) => {
327
+ const result = withAnnotatedInnerState(context.state, effectiveInnerState, (annotatedInnerState) => {
307
328
  const innerContext = annotatedInnerState !== context.state ? {
308
329
  ...context,
309
330
  state: annotatedInnerState
@@ -313,85 +334,71 @@ function prompt(parser, config) {
313
334
  if (result instanceof Promise) return result.then(processResult);
314
335
  return Promise.resolve(processResult(result));
315
336
  },
316
- complete: (state) => {
337
+ complete: (state, exec) => {
317
338
  if (isPromptBindState(state) && state.hasCliValue) {
318
- const r = withAnnotatedInnerState(state, state.cliState, (annotatedInnerState) => parser.complete(annotatedInnerState));
339
+ const r = withAnnotatedInnerState(state, state.cliState, (annotatedInnerState) => parser.complete(annotatedInnerState, exec));
319
340
  if (r instanceof Promise) return r;
320
341
  return Promise.resolve(r);
321
342
  }
322
- if (state instanceof PromptBindInitialStateClass) {
323
- if (promptCache?.state === state) {
324
- const cached = promptCache.result;
325
- promptCache = null;
326
- return cached;
343
+ const isProbe = exec != null && exec.phase !== "complete";
344
+ const annotations = getAnnotations(state);
345
+ const innerInitialState = parser.initialState;
346
+ const shouldInheritInitialStateAnnotations = annotations != null && (innerInitialState == null || typeof innerInitialState === "object");
347
+ const effectiveInitialState = shouldInheritInitialStateAnnotations ? inheritAnnotations(state, innerInitialState) : innerInitialState;
348
+ const readPlaceholder = () => {
349
+ try {
350
+ return "placeholder" in parser ? parser.placeholder : void 0;
351
+ } catch {
352
+ return void 0;
327
353
  }
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
354
+ };
355
+ const finalizePrompt = () => {
356
+ const shouldDefer = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => shouldDeferPrompt(parser, annotatedInnerState, exec));
357
+ if (shouldDefer) return Promise.resolve(deferredPromptResult(readPlaceholder()));
358
+ if (isProbe) return Promise.resolve({
359
+ success: true,
360
+ value: readPlaceholder()
361
+ });
362
+ return executePrompt();
363
+ };
364
+ const hasDeferHook = typeof parser.shouldDeferCompletion === "function";
365
+ const decideFromParse = (parseResult) => {
366
+ const consumed = parseResult.success ? parseResult.consumed.length : 0;
367
+ const cliState = parseResult.success && consumed === 0 ? parseResult.next.state : void 0;
368
+ const cliStateIsInjected = cliState != null && typeof cliState === "object" && unwrapInjectedAnnotationState(cliState) !== cliState;
369
+ const isSourceBinding = shouldCompleteFromSourceBinding(cliState, state);
370
+ if (!isSourceBinding) return finalizePrompt();
371
+ const completeState = parseResult.success ? parseResult.next.state : effectiveInitialState;
372
+ const innerR = parser.complete(completeState, exec);
373
+ const handleCompleteResult = (res) => {
374
+ if (res.success && res.value === void 0 && cliStateIsInjected) return finalizePrompt();
375
+ if (!res.success) return finalizePrompt();
376
+ return Promise.resolve(res);
377
377
  };
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);
378
+ if (innerR instanceof Promise) return innerR.then(handleCompleteResult);
379
+ return handleCompleteResult(innerR);
380
+ };
381
+ if (hasDeferHook) {
382
+ const innerR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedInnerState) => parser.complete(annotatedInnerState, exec));
383
+ const handleDeferHookResult = (res) => {
384
+ if (res.success && res.value === void 0) return finalizePrompt();
385
+ if (!res.success) return finalizePrompt();
386
+ return Promise.resolve(res);
386
387
  };
387
- const r = withAnnotatedInnerState(state, cliState, (annotatedInnerState) => parser.complete(annotatedInnerState));
388
- if (r instanceof Promise) return r.then(useCompleteResultOrPrompt);
389
- return useCompleteResultOrPrompt(r);
388
+ if (innerR instanceof Promise) return innerR.then(handleDeferHookResult);
389
+ return handleDeferHookResult(innerR);
390
390
  }
391
- return shouldDeferPrompt(parser, state) ? Promise.resolve(deferredPromptResult()) : executePrompt();
391
+ const simParseR = withAnnotatedInnerState(state, effectiveInitialState, (annotatedState) => parser.parse({
392
+ buffer: [],
393
+ state: annotatedState,
394
+ optionsTerminated: false,
395
+ usage: parser.usage
396
+ }));
397
+ if (simParseR instanceof Promise) return simParseR.then(decideFromParse);
398
+ return decideFromParse(simParseR);
392
399
  },
393
400
  suggest: (context, prefix) => {
394
- const innerState = isPromptBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
401
+ const innerState = isPromptBindState(context.state) ? context.state.cliState === void 0 ? parser.initialState : context.state.cliState : context.state;
395
402
  const innerContext = innerState !== context.state ? {
396
403
  ...context,
397
404
  state: innerState
@@ -407,6 +414,35 @@ function prompt(parser, config) {
407
414
  return parser.getDocFragments(state, defaultValue);
408
415
  }
409
416
  };
417
+ defineTraits(promptedParser, { inheritsAnnotations: true });
418
+ if ("placeholder" in parser) Object.defineProperty(promptedParser, "placeholder", {
419
+ get() {
420
+ try {
421
+ return parser.placeholder;
422
+ } catch {
423
+ return void 0;
424
+ }
425
+ },
426
+ configurable: true,
427
+ enumerable: false
428
+ });
429
+ if (typeof parser.normalizeValue === "function") Object.defineProperty(promptedParser, "normalizeValue", {
430
+ value: parser.normalizeValue.bind(parser),
431
+ configurable: true,
432
+ enumerable: false
433
+ });
434
+ const dependencyMetadata = mapSourceMetadata(parser, (source) => ({
435
+ ...source,
436
+ extractSourceValue: (state) => {
437
+ if (!isPromptBindState(state)) return source.extractSourceValue(state);
438
+ return source.extractSourceValue(state.cliState ?? state);
439
+ }
440
+ }));
441
+ if (dependencyMetadata != null) Object.defineProperty(promptedParser, "dependencyMetadata", {
442
+ value: dependencyMetadata,
443
+ configurable: true,
444
+ enumerable: false
445
+ });
410
446
  return promptedParser;
411
447
  }
412
448
  /** 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.908+f60f037d",
3
+ "version": "1.0.0",
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.908+f60f037d"
59
+ "@optique/core": "1.0.0"
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.908+f60f037d"
65
+ "@optique/env": "1.0.0"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsdown",