@optique/core 1.2.0-dev.2232 → 1.2.0-dev.2236

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.
@@ -913,9 +913,89 @@ function makeFallbackDeferredValue(fallback, memoize) {
913
913
  };
914
914
  return brandDeferredValue(call, "fallback");
915
915
  }
916
+ /**
917
+ * Wraps a parser so its value is resolved by the command handler instead of
918
+ * during parsing.
919
+ *
920
+ * The parsed field becomes a {@link DeferredValue}: a value-producing function.
921
+ * When the wrapped parser produced a value, calling the function returns that
922
+ * value and {@link DeferredValue.source} is `"specified"`. A value resolved
923
+ * from a source such as `bindEnv()`/`bindConfig()` counts as produced. When
924
+ * the wrapped parser did not produce a value, calling the function runs
925
+ * `fallback` and `source` is `"fallback"`. Because a missing value is reported
926
+ * as `undefined`, a wrapped parser that yields `undefined` is treated as having
927
+ * produced no value and selects the fallback. The fallback runs at handler
928
+ * time, so its errors are handler errors rather than parser errors. A value
929
+ * that is specified but invalid still fails during parsing.
930
+ *
931
+ * Like {@link optional}, the wrapped parser keeps its place in usage and help;
932
+ * only the result type changes. The fallback context type `C` is inferred from
933
+ * the `fallback` parameter.
934
+ *
935
+ * @example
936
+ * ```typescript
937
+ * const parser = object({
938
+ * serviceName: option("--service-name", string()),
939
+ * apiToken: deferredValue(
940
+ * option("--api-token", string()),
941
+ * ({ serviceName }: { readonly serviceName: string }) =>
942
+ * promptForApiToken(serviceName),
943
+ * ),
944
+ * });
945
+ *
946
+ * const parsed = parse(parser, argv);
947
+ * // The prompt only runs if this branch is reached.
948
+ * const apiToken = await parsed.apiToken({
949
+ * serviceName: parsed.serviceName,
950
+ * });
951
+ * ```
952
+ *
953
+ * @template M The execution mode of the wrapped parser.
954
+ * @template T The resolved value type.
955
+ * @template S The state type of the wrapped parser.
956
+ * @template C The handler-time context type, inferred from `fallback`. A
957
+ * fallback without a parameter yields a no-argument
958
+ * {@link DeferredValue}; one whose parameter is optional or includes
959
+ * `undefined` stays callable with no argument.
960
+ * @param parser The parser that reads the CLI value.
961
+ * @param fallback A resolver run at handler time when no value was specified.
962
+ * @param options Optional {@link DeferredValueOptions}.
963
+ * @returns A parser whose value is a {@link DeferredValue}.
964
+ * @since 1.2.0
965
+ */
916
966
  function deferredValue(parser, fallback, options) {
917
967
  const memoize = options?.memoize ?? false;
918
- return map(optional(map(parser, (value) => ({ value }))), (matched) => matched === void 0 ? makeFallbackDeferredValue(fallback, memoize) : makeSpecifiedDeferredValue(matched.value));
968
+ const base = optional(parser);
969
+ const complete = (state, exec) => require_mode_dispatch.wrapForMode(base.mode, require_mode_dispatch.mapModeValue(base.mode, base.complete(state, exec), (result) => {
970
+ if (!result.success) return result;
971
+ const value = result.value === void 0 ? makeFallbackDeferredValue(fallback, memoize) : makeSpecifiedDeferredValue(result.value);
972
+ return result.deferred ? {
973
+ success: true,
974
+ value,
975
+ deferred: true
976
+ } : {
977
+ success: true,
978
+ value
979
+ };
980
+ }));
981
+ const descriptors = Object.getOwnPropertyDescriptors(base);
982
+ descriptors.complete = {
983
+ value: complete,
984
+ writable: true,
985
+ enumerable: true,
986
+ configurable: true
987
+ };
988
+ descriptors.$valueType = {
989
+ value: [],
990
+ writable: true,
991
+ enumerable: true,
992
+ configurable: true
993
+ };
994
+ delete descriptors.normalizeValue;
995
+ delete descriptors.validateValue;
996
+ delete descriptors[fluentParserMarker];
997
+ const deferredParser = Object.create(Object.getPrototypeOf(base), descriptors);
998
+ return fluent(deferredParser);
919
999
  }
920
1000
  /**
921
1001
  * Checks whether a value is a {@link DeferredValue} produced by
@@ -232,11 +232,14 @@ interface DeferredValueOptions {
232
232
  *
233
233
  * The parsed field becomes a {@link DeferredValue}: a value-producing function.
234
234
  * When the wrapped parser produced a value, calling the function returns that
235
- * value and {@link DeferredValue.source} is `"specified"`. When the wrapped
236
- * parser did not produce a value, calling the function runs `fallback` and
237
- * `source` is `"fallback"`. The fallback runs at handler time, so its errors
238
- * are handler errors rather than parser errors. A value that is specified but
239
- * invalid still fails during parsing.
235
+ * value and {@link DeferredValue.source} is `"specified"`. A value resolved
236
+ * from a source such as `bindEnv()`/`bindConfig()` counts as produced. When
237
+ * the wrapped parser did not produce a value, calling the function runs
238
+ * `fallback` and `source` is `"fallback"`. Because a missing value is reported
239
+ * as `undefined`, a wrapped parser that yields `undefined` is treated as having
240
+ * produced no value and selects the fallback. The fallback runs at handler
241
+ * time, so its errors are handler errors rather than parser errors. A value
242
+ * that is specified but invalid still fails during parsing.
240
243
  *
241
244
  * Like {@link optional}, the wrapped parser keeps its place in usage and help;
242
245
  * only the result type changes. The fallback context type `C` is inferred from
@@ -263,14 +266,17 @@ interface DeferredValueOptions {
263
266
  * @template M The execution mode of the wrapped parser.
264
267
  * @template T The resolved value type.
265
268
  * @template S The state type of the wrapped parser.
269
+ * @template C The handler-time context type, inferred from `fallback`. A
270
+ * fallback without a parameter yields a no-argument
271
+ * {@link DeferredValue}; one whose parameter is optional or includes
272
+ * `undefined` stays callable with no argument.
266
273
  * @param parser The parser that reads the CLI value.
267
274
  * @param fallback A resolver run at handler time when no value was specified.
268
275
  * @param options Optional {@link DeferredValueOptions}.
269
276
  * @returns A parser whose value is a {@link DeferredValue}.
270
277
  * @since 1.2.0
271
278
  */
272
- declare function deferredValue<M extends Mode, T, S>(parser: Parser<M, T, S>, fallback: () => T | Promise<T>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<T>, [S] | undefined>;
273
- declare function deferredValue<M extends Mode, T, S, C>(parser: Parser<M, T, S>, fallback: (ctx: C) => T | Promise<T>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<T, C>, [S] | undefined>;
279
+ declare function deferredValue<M extends Mode, T, S, C = void>(parser: Parser<M, T, S>, fallback: (ctx: C) => T | Promise<T>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<T, C>, [S] | undefined>;
274
280
  /**
275
281
  * Checks whether a value is a {@link DeferredValue} produced by
276
282
  * {@link deferredValue}.
@@ -413,12 +419,13 @@ interface ParserModifiers<M extends Mode = "sync", TValue = unknown, TState = un
413
419
  /**
414
420
  * Defers this parser's value so the command handler resolves it.
415
421
  *
422
+ * @template C The handler-time context type, inferred from `fallback`.
416
423
  * @param fallback A resolver run at handler time when no value was specified.
417
424
  * @param options Optional {@link DeferredValueOptions}.
418
425
  * @returns A parser whose value is a {@link DeferredValue}.
426
+ * @since 1.2.0
419
427
  */
420
- deferredValue(fallback: () => TValue | Promise<TValue>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<TValue>, [TState] | undefined>;
421
- deferredValue<C>(fallback: (ctx: C) => TValue | Promise<TValue>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<TValue, C>, [TState] | undefined>;
428
+ deferredValue<C = void>(fallback: (ctx: C) => TValue | Promise<TValue>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<TValue, C>, [TState] | undefined>;
422
429
  /**
423
430
  * Allows this parser to match multiple times.
424
431
  *
@@ -232,11 +232,14 @@ interface DeferredValueOptions {
232
232
  *
233
233
  * The parsed field becomes a {@link DeferredValue}: a value-producing function.
234
234
  * When the wrapped parser produced a value, calling the function returns that
235
- * value and {@link DeferredValue.source} is `"specified"`. When the wrapped
236
- * parser did not produce a value, calling the function runs `fallback` and
237
- * `source` is `"fallback"`. The fallback runs at handler time, so its errors
238
- * are handler errors rather than parser errors. A value that is specified but
239
- * invalid still fails during parsing.
235
+ * value and {@link DeferredValue.source} is `"specified"`. A value resolved
236
+ * from a source such as `bindEnv()`/`bindConfig()` counts as produced. When
237
+ * the wrapped parser did not produce a value, calling the function runs
238
+ * `fallback` and `source` is `"fallback"`. Because a missing value is reported
239
+ * as `undefined`, a wrapped parser that yields `undefined` is treated as having
240
+ * produced no value and selects the fallback. The fallback runs at handler
241
+ * time, so its errors are handler errors rather than parser errors. A value
242
+ * that is specified but invalid still fails during parsing.
240
243
  *
241
244
  * Like {@link optional}, the wrapped parser keeps its place in usage and help;
242
245
  * only the result type changes. The fallback context type `C` is inferred from
@@ -263,14 +266,17 @@ interface DeferredValueOptions {
263
266
  * @template M The execution mode of the wrapped parser.
264
267
  * @template T The resolved value type.
265
268
  * @template S The state type of the wrapped parser.
269
+ * @template C The handler-time context type, inferred from `fallback`. A
270
+ * fallback without a parameter yields a no-argument
271
+ * {@link DeferredValue}; one whose parameter is optional or includes
272
+ * `undefined` stays callable with no argument.
266
273
  * @param parser The parser that reads the CLI value.
267
274
  * @param fallback A resolver run at handler time when no value was specified.
268
275
  * @param options Optional {@link DeferredValueOptions}.
269
276
  * @returns A parser whose value is a {@link DeferredValue}.
270
277
  * @since 1.2.0
271
278
  */
272
- declare function deferredValue<M extends Mode, T, S>(parser: Parser<M, T, S>, fallback: () => T | Promise<T>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<T>, [S] | undefined>;
273
- declare function deferredValue<M extends Mode, T, S, C>(parser: Parser<M, T, S>, fallback: (ctx: C) => T | Promise<T>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<T, C>, [S] | undefined>;
279
+ declare function deferredValue<M extends Mode, T, S, C = void>(parser: Parser<M, T, S>, fallback: (ctx: C) => T | Promise<T>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<T, C>, [S] | undefined>;
274
280
  /**
275
281
  * Checks whether a value is a {@link DeferredValue} produced by
276
282
  * {@link deferredValue}.
@@ -413,12 +419,13 @@ interface ParserModifiers<M extends Mode = "sync", TValue = unknown, TState = un
413
419
  /**
414
420
  * Defers this parser's value so the command handler resolves it.
415
421
  *
422
+ * @template C The handler-time context type, inferred from `fallback`.
416
423
  * @param fallback A resolver run at handler time when no value was specified.
417
424
  * @param options Optional {@link DeferredValueOptions}.
418
425
  * @returns A parser whose value is a {@link DeferredValue}.
426
+ * @since 1.2.0
419
427
  */
420
- deferredValue(fallback: () => TValue | Promise<TValue>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<TValue>, [TState] | undefined>;
421
- deferredValue<C>(fallback: (ctx: C) => TValue | Promise<TValue>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<TValue, C>, [TState] | undefined>;
428
+ deferredValue<C = void>(fallback: (ctx: C) => TValue | Promise<TValue>, options?: DeferredValueOptions): FluentParser<M, DeferredValue<TValue, C>, [TState] | undefined>;
422
429
  /**
423
430
  * Allows this parser to match multiple times.
424
431
  *
package/dist/modifiers.js CHANGED
@@ -913,9 +913,89 @@ function makeFallbackDeferredValue(fallback, memoize) {
913
913
  };
914
914
  return brandDeferredValue(call, "fallback");
915
915
  }
916
+ /**
917
+ * Wraps a parser so its value is resolved by the command handler instead of
918
+ * during parsing.
919
+ *
920
+ * The parsed field becomes a {@link DeferredValue}: a value-producing function.
921
+ * When the wrapped parser produced a value, calling the function returns that
922
+ * value and {@link DeferredValue.source} is `"specified"`. A value resolved
923
+ * from a source such as `bindEnv()`/`bindConfig()` counts as produced. When
924
+ * the wrapped parser did not produce a value, calling the function runs
925
+ * `fallback` and `source` is `"fallback"`. Because a missing value is reported
926
+ * as `undefined`, a wrapped parser that yields `undefined` is treated as having
927
+ * produced no value and selects the fallback. The fallback runs at handler
928
+ * time, so its errors are handler errors rather than parser errors. A value
929
+ * that is specified but invalid still fails during parsing.
930
+ *
931
+ * Like {@link optional}, the wrapped parser keeps its place in usage and help;
932
+ * only the result type changes. The fallback context type `C` is inferred from
933
+ * the `fallback` parameter.
934
+ *
935
+ * @example
936
+ * ```typescript
937
+ * const parser = object({
938
+ * serviceName: option("--service-name", string()),
939
+ * apiToken: deferredValue(
940
+ * option("--api-token", string()),
941
+ * ({ serviceName }: { readonly serviceName: string }) =>
942
+ * promptForApiToken(serviceName),
943
+ * ),
944
+ * });
945
+ *
946
+ * const parsed = parse(parser, argv);
947
+ * // The prompt only runs if this branch is reached.
948
+ * const apiToken = await parsed.apiToken({
949
+ * serviceName: parsed.serviceName,
950
+ * });
951
+ * ```
952
+ *
953
+ * @template M The execution mode of the wrapped parser.
954
+ * @template T The resolved value type.
955
+ * @template S The state type of the wrapped parser.
956
+ * @template C The handler-time context type, inferred from `fallback`. A
957
+ * fallback without a parameter yields a no-argument
958
+ * {@link DeferredValue}; one whose parameter is optional or includes
959
+ * `undefined` stays callable with no argument.
960
+ * @param parser The parser that reads the CLI value.
961
+ * @param fallback A resolver run at handler time when no value was specified.
962
+ * @param options Optional {@link DeferredValueOptions}.
963
+ * @returns A parser whose value is a {@link DeferredValue}.
964
+ * @since 1.2.0
965
+ */
916
966
  function deferredValue(parser, fallback, options) {
917
967
  const memoize = options?.memoize ?? false;
918
- return map(optional(map(parser, (value) => ({ value }))), (matched) => matched === void 0 ? makeFallbackDeferredValue(fallback, memoize) : makeSpecifiedDeferredValue(matched.value));
968
+ const base = optional(parser);
969
+ const complete = (state, exec) => wrapForMode(base.mode, mapModeValue(base.mode, base.complete(state, exec), (result) => {
970
+ if (!result.success) return result;
971
+ const value = result.value === void 0 ? makeFallbackDeferredValue(fallback, memoize) : makeSpecifiedDeferredValue(result.value);
972
+ return result.deferred ? {
973
+ success: true,
974
+ value,
975
+ deferred: true
976
+ } : {
977
+ success: true,
978
+ value
979
+ };
980
+ }));
981
+ const descriptors = Object.getOwnPropertyDescriptors(base);
982
+ descriptors.complete = {
983
+ value: complete,
984
+ writable: true,
985
+ enumerable: true,
986
+ configurable: true
987
+ };
988
+ descriptors.$valueType = {
989
+ value: [],
990
+ writable: true,
991
+ enumerable: true,
992
+ configurable: true
993
+ };
994
+ delete descriptors.normalizeValue;
995
+ delete descriptors.validateValue;
996
+ delete descriptors[fluentParserMarker];
997
+ const deferredParser = Object.create(Object.getPrototypeOf(base), descriptors);
998
+ return fluent(deferredParser);
919
999
  }
920
1000
  /**
921
1001
  * Checks whether a value is a {@link DeferredValue} produced by
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.2.0-dev.2232",
3
+ "version": "1.2.0-dev.2236",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -208,7 +208,7 @@
208
208
  "fast-check": "^4.7.0",
209
209
  "tsdown": "^0.13.0",
210
210
  "typescript": "^5.8.3",
211
- "@optique/env": "1.2.0-dev.2232+9fc28f44"
211
+ "@optique/env": "1.2.0-dev.2236+88a40b64"
212
212
  },
213
213
  "scripts": {
214
214
  "build": "tsdown",