effect 4.0.0-beta.98 → 4.0.0-beta.99

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.
Files changed (83) hide show
  1. package/README.md +1 -3
  2. package/dist/Array.js +1 -1
  3. package/dist/Array.js.map +1 -1
  4. package/dist/Graph.d.ts +1187 -196
  5. package/dist/Graph.d.ts.map +1 -1
  6. package/dist/Graph.js +1031 -283
  7. package/dist/Graph.js.map +1 -1
  8. package/dist/Iterable.d.ts.map +1 -1
  9. package/dist/Iterable.js +41 -1
  10. package/dist/Iterable.js.map +1 -1
  11. package/dist/Schema.js +1 -1
  12. package/dist/Schema.js.map +1 -1
  13. package/dist/internal/effect.js +9 -3
  14. package/dist/internal/effect.js.map +1 -1
  15. package/dist/unstable/ai/McpSchema.d.ts +128 -128
  16. package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
  17. package/dist/unstable/ai/Tool.d.ts.map +1 -1
  18. package/dist/unstable/ai/Tool.js +11 -13
  19. package/dist/unstable/ai/Tool.js.map +1 -1
  20. package/dist/unstable/cli/CliConfig.d.ts +76 -0
  21. package/dist/unstable/cli/CliConfig.d.ts.map +1 -0
  22. package/dist/unstable/cli/CliConfig.js +63 -0
  23. package/dist/unstable/cli/CliConfig.js.map +1 -0
  24. package/dist/unstable/cli/CliError.d.ts.map +1 -1
  25. package/dist/unstable/cli/CliError.js +3 -0
  26. package/dist/unstable/cli/CliError.js.map +1 -1
  27. package/dist/unstable/cli/Command.d.ts +36 -1
  28. package/dist/unstable/cli/Command.d.ts.map +1 -1
  29. package/dist/unstable/cli/Command.js +61 -4
  30. package/dist/unstable/cli/Command.js.map +1 -1
  31. package/dist/unstable/cli/GlobalFlag.d.ts +23 -2
  32. package/dist/unstable/cli/GlobalFlag.d.ts.map +1 -1
  33. package/dist/unstable/cli/GlobalFlag.js +20 -4
  34. package/dist/unstable/cli/GlobalFlag.js.map +1 -1
  35. package/dist/unstable/cli/Param.js +6 -2
  36. package/dist/unstable/cli/Param.js.map +1 -1
  37. package/dist/unstable/cli/index.d.ts +4 -0
  38. package/dist/unstable/cli/index.d.ts.map +1 -1
  39. package/dist/unstable/cli/index.js +4 -0
  40. package/dist/unstable/cli/index.js.map +1 -1
  41. package/dist/unstable/cli/internal/ansi.d.ts.map +1 -1
  42. package/dist/unstable/cli/internal/ansi.js +2 -0
  43. package/dist/unstable/cli/internal/ansi.js.map +1 -1
  44. package/dist/unstable/cli/internal/parser.js +11 -2
  45. package/dist/unstable/cli/internal/parser.js.map +1 -1
  46. package/dist/unstable/cli/internal/wizard.d.ts +13 -0
  47. package/dist/unstable/cli/internal/wizard.d.ts.map +1 -0
  48. package/dist/unstable/cli/internal/wizard.js +212 -0
  49. package/dist/unstable/cli/internal/wizard.js.map +1 -0
  50. package/dist/unstable/cluster/RunnerStorage.js +1 -1
  51. package/dist/unstable/cluster/RunnerStorage.js.map +1 -1
  52. package/dist/unstable/http/Multipart.d.ts.map +1 -1
  53. package/dist/unstable/http/Multipart.js +3 -1
  54. package/dist/unstable/http/Multipart.js.map +1 -1
  55. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +1 -1
  56. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  57. package/dist/unstable/persistence/Redis.d.ts.map +1 -1
  58. package/dist/unstable/persistence/Redis.js +5 -3
  59. package/dist/unstable/persistence/Redis.js.map +1 -1
  60. package/dist/unstable/sql/SqlClient.d.ts +1 -2
  61. package/dist/unstable/sql/SqlClient.d.ts.map +1 -1
  62. package/dist/unstable/sql/SqlClient.js.map +1 -1
  63. package/package.json +3 -3
  64. package/src/Array.ts +1 -1
  65. package/src/Graph.ts +2967 -457
  66. package/src/Iterable.ts +32 -2
  67. package/src/Schema.ts +1 -1
  68. package/src/internal/effect.ts +9 -3
  69. package/src/unstable/ai/Tool.ts +13 -16
  70. package/src/unstable/cli/CliConfig.ts +85 -0
  71. package/src/unstable/cli/CliError.ts +3 -0
  72. package/src/unstable/cli/Command.ts +83 -7
  73. package/src/unstable/cli/GlobalFlag.ts +33 -5
  74. package/src/unstable/cli/Param.ts +15 -3
  75. package/src/unstable/cli/index.ts +5 -0
  76. package/src/unstable/cli/internal/ansi.ts +3 -0
  77. package/src/unstable/cli/internal/parser.ts +14 -2
  78. package/src/unstable/cli/internal/wizard.ts +283 -0
  79. package/src/unstable/cluster/RunnerStorage.ts +1 -1
  80. package/src/unstable/http/Multipart.ts +3 -1
  81. package/src/unstable/httpapi/HttpApiBuilder.ts +1 -1
  82. package/src/unstable/persistence/Redis.ts +9 -4
  83. package/src/unstable/sql/SqlClient.ts +1 -2
package/src/Iterable.ts CHANGED
@@ -5066,8 +5066,38 @@ export const cartesianWith: {
5066
5066
  <A, B, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C>
5067
5067
  } = dual(
5068
5068
  3,
5069
- <A, B, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C> =>
5070
- flatMap(self, (a) => map(that, (b) => f(a, b)))
5069
+ <A, B, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C> => ({
5070
+ [Symbol.iterator]() {
5071
+ const cache: Array<B> = []
5072
+ let iterator: Iterator<B> | undefined
5073
+ let done = false
5074
+ const replay: Iterable<B> = {
5075
+ [Symbol.iterator]() {
5076
+ let index = 0
5077
+ return {
5078
+ next(): IteratorResult<B> {
5079
+ if (index < cache.length) {
5080
+ return { done: false, value: cache[index++] }
5081
+ }
5082
+ if (done) {
5083
+ return { done: true, value: undefined }
5084
+ }
5085
+ iterator ??= that[Symbol.iterator]()
5086
+ const result = iterator.next()
5087
+ if (result.done) {
5088
+ done = true
5089
+ return { done: true, value: undefined }
5090
+ }
5091
+ cache.push(result.value)
5092
+ index++
5093
+ return result
5094
+ }
5095
+ }
5096
+ }
5097
+ }
5098
+ return flatMap(self, (a) => map(replay, (b) => f(a, b)))[Symbol.iterator]()
5099
+ }
5100
+ })
5071
5101
  )
5072
5102
 
5073
5103
  /**
package/src/Schema.ts CHANGED
@@ -7552,7 +7552,7 @@ export function isInt(annotations?: Annotations.Filter) {
7552
7552
  export function isInt32(annotations?: Annotations.Filter) {
7553
7553
  return new SchemaAST.FilterGroup(
7554
7554
  [
7555
- isInt(annotations),
7555
+ isInt(),
7556
7556
  isBetween({ minimum: -2147483648, maximum: 2147483647 })
7557
7557
  ],
7558
7558
  {
@@ -851,10 +851,12 @@ export const fiberInterruptAll = <A extends Iterable<Fiber.Fiber<any, any>>>(
851
851
  ): Effect.Effect<void> =>
852
852
  withFiber((parent) => {
853
853
  const annotations = fiberStackAnnotations(parent)
854
+ let fiberArr = Arr.empty<Fiber.Fiber<any, any>>()
854
855
  for (const fiber of fibers) {
855
856
  fiber.interruptUnsafe(parent.id, annotations)
857
+ fiberArr.push(fiber)
856
858
  }
857
- return asVoid(fiberAwaitAll(fibers))
859
+ return asVoid(fiberAwaitAll(fiberArr))
858
860
  })
859
861
 
860
862
  /** @internal */
@@ -867,8 +869,12 @@ export const fiberInterruptAllAs: {
867
869
  ): Effect.Effect<void> =>
868
870
  withFiber((parent) => {
869
871
  const annotations = fiberStackAnnotations(parent)
870
- for (const fiber of fibers) fiber.interruptUnsafe(fiberId, annotations)
871
- return asVoid(fiberAwaitAll(fibers))
872
+ const fiberArr = Arr.empty<Fiber.Fiber<any, any>>()
873
+ for (const fiber of fibers) {
874
+ fiber.interruptUnsafe(fiberId, annotations)
875
+ fiberArr.push(fiber)
876
+ }
877
+ return asVoid(fiberAwaitAll(fiberArr))
872
878
  }))
873
879
 
874
880
  /** @internal */
@@ -1028,37 +1028,34 @@ export type RequiresHandler<Tool extends Any> = Tool extends ProviderDefined<
1028
1028
  // Constructors
1029
1029
  // =============================================================================
1030
1030
 
1031
+ // Clones a tool while preserving its prototype (and thus its kind, e.g.
1032
+ // user-defined vs. provider-defined vs. dynamic) and its own properties such
1033
+ // as `id`. Optional `overrides` replace individual fields on the clone.
1034
+ const clone = (self: Any, overrides?: Record<string, unknown>): any =>
1035
+ Object.assign(Object.create(Object.getPrototypeOf(self)), self, overrides)
1036
+
1031
1037
  const Proto = {
1032
1038
  [TypeId]: { _Requirements: identity },
1033
1039
  pipe() {
1034
1040
  return pipeArguments(this, arguments)
1035
1041
  },
1036
1042
  addDependency(this: Any) {
1037
- return userDefinedProto({ ...this })
1043
+ return clone(this)
1038
1044
  },
1039
1045
  setParameters(this: Any, parametersSchema: Schema.Constraint) {
1040
- return userDefinedProto({
1041
- ...this,
1042
- parametersSchema
1043
- })
1046
+ return clone(this, { parametersSchema })
1044
1047
  },
1045
1048
  setSuccess(this: Any, successSchema: Schema.Constraint) {
1046
- return userDefinedProto({ ...this, successSchema })
1049
+ return clone(this, { successSchema })
1047
1050
  },
1048
1051
  setFailure(this: Any, failureSchema: Schema.Constraint) {
1049
- return userDefinedProto({ ...this, failureSchema })
1052
+ return clone(this, { failureSchema })
1050
1053
  },
1051
1054
  annotate<I, S>(this: Any, tag: Context.Key<I, S>, value: S) {
1052
- return userDefinedProto({
1053
- ...this,
1054
- annotations: Context.add(this.annotations, tag, value)
1055
- })
1055
+ return clone(this, { annotations: Context.add(this.annotations, tag, value) })
1056
1056
  },
1057
1057
  annotateMerge<I>(this: Any, context: Context.Context<I>) {
1058
- return userDefinedProto({
1059
- ...this,
1060
- annotations: Context.merge(this.annotations, context)
1061
- })
1058
+ return clone(this, { annotations: Context.merge(this.annotations, context) })
1062
1059
  }
1063
1060
  }
1064
1061
 
@@ -1132,7 +1129,7 @@ const providerDefinedProto = <
1132
1129
  readonly failureMode: Mode
1133
1130
  },
1134
1131
  RequiresHandler
1135
- > => Object.assign(Object.create(ProviderDefinedProto), { ...options })
1132
+ > => Object.assign(Object.create(ProviderDefinedProto), { annotations: Context.empty(), ...options })
1136
1133
 
1137
1134
  const dynamicProto = <
1138
1135
  const Name extends string,
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Configuration for Effect CLI command execution.
3
+ *
4
+ * @since 4.0.0
5
+ */
6
+ import * as Context from "../../Context.ts"
7
+ import * as Layer from "../../Layer.ts"
8
+ import * as GlobalFlag from "./GlobalFlag.ts"
9
+
10
+ /**
11
+ * Context reference for configuration shared by CLI parsing, help generation,
12
+ * and command execution.
13
+ *
14
+ * **When to use**
15
+ *
16
+ * Use when you need to customize runner-wide CLI behavior, such as which
17
+ * built-in global flags are available.
18
+ *
19
+ * @category services
20
+ * @since 4.0.0
21
+ */
22
+ export class CliConfig extends Context.Reference<CliConfig.Service>("effect/unstable/cli/CliConfig", {
23
+ defaultValue: () => defaults
24
+ }) {}
25
+
26
+ /**
27
+ * Types used by the `CliConfig` context reference.
28
+ *
29
+ * @since 4.0.0
30
+ */
31
+ export declare namespace CliConfig {
32
+ /**
33
+ * Configuration values used while running a CLI command.
34
+ *
35
+ * @category models
36
+ * @since 4.0.0
37
+ */
38
+ export interface Service {
39
+ /** Ordered built-in global flags, with earlier action flags taking precedence. */
40
+ readonly builtIns: ReadonlyArray<GlobalFlag.BuiltIn>
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Default CLI configuration containing every built-in global flag.
46
+ *
47
+ * @category defaults
48
+ * @since 4.0.0
49
+ */
50
+ export const defaults: CliConfig.Service = {
51
+ builtIns: GlobalFlag.BuiltIns
52
+ }
53
+
54
+ /**
55
+ * Creates CLI configuration by merging the provided options over `defaults`.
56
+ *
57
+ * **When to use**
58
+ *
59
+ * Use when you need a configuration value to provide directly through the
60
+ * `CliConfig` context reference.
61
+ *
62
+ * @see {@link layer} for providing configuration as a layer
63
+ *
64
+ * @category constructors
65
+ * @since 4.0.0
66
+ */
67
+ export const make = (options?: Partial<CliConfig.Service>): CliConfig.Service => ({
68
+ ...defaults,
69
+ ...options
70
+ })
71
+
72
+ /**
73
+ * Creates a layer that provides CLI configuration merged over `defaults`.
74
+ *
75
+ * **When to use**
76
+ *
77
+ * Use when wiring customized CLI behavior into an application layer.
78
+ *
79
+ * @see {@link make} for creating a configuration value directly
80
+ *
81
+ * @category layers
82
+ * @since 4.0.0
83
+ */
84
+ export const layer = (options?: Partial<CliConfig.Service>): Layer.Layer<never> =>
85
+ Layer.succeed(CliConfig, make(options))
@@ -365,6 +365,9 @@ export class InvalidValue extends Schema.TaggedErrorClass<InvalidValue>(
365
365
  if (this.kind === "argument") {
366
366
  return `Invalid value for argument <${this.option}>: "${this.value}". Expected: ${this.expected}`
367
367
  }
368
+ if (this.value.length === 0) {
369
+ return `Missing value for flag --${this.option}. Expected: ${this.expected}`
370
+ }
368
371
  return `Invalid value for flag --${this.option}: "${this.value}". Expected: ${this.expected}`
369
372
  }
370
373
  }
@@ -26,6 +26,7 @@ import * as Stdio from "../../Stdio.ts"
26
26
  import * as Terminal from "../../Terminal.ts"
27
27
  import type { Contravariant, Covariant, NoInfer, Simplify } from "../../Types.ts"
28
28
  import type { ChildProcessSpawner } from "../process/ChildProcessSpawner.ts"
29
+ import * as CliConfig from "./CliConfig.ts"
29
30
  import * as CliError from "./CliError.ts"
30
31
  import * as CliOutput from "./CliOutput.ts"
31
32
  import * as GlobalFlag from "./GlobalFlag.ts"
@@ -34,7 +35,9 @@ import { mergeConfig, parseConfig } from "./internal/config.ts"
34
35
  import { getGlobalFlagsForCommandPath, getGlobalFlagsForCommandTree, getHelpForCommandPath } from "./internal/help.ts"
35
36
  import * as Lexer from "./internal/lexer.ts"
36
37
  import * as Parser from "./internal/parser.ts"
38
+ import * as Wizard from "./internal/wizard.ts"
37
39
  import * as Param from "./Param.ts"
40
+ import * as Prompt from "./Prompt.ts"
38
41
 
39
42
  /* ========================================================================== */
40
43
  /* Public Types */
@@ -369,6 +372,21 @@ export type Error<C> = C extends Command<
369
372
  > ? _Error :
370
373
  never
371
374
 
375
+ /**
376
+ * A utility type to extract the required services type from a `Command`.
377
+ *
378
+ * @category utility types
379
+ * @since 4.0.0
380
+ */
381
+ export type Services<C> = C extends Command<
382
+ infer _Name,
383
+ infer _Input,
384
+ infer _ContextInput,
385
+ infer _Error,
386
+ infer _Requirements
387
+ > ? _Requirements :
388
+ never
389
+
372
390
  /**
373
391
  * Service context for a specific command, enabling subcommands to access their parent's parsed configuration.
374
392
  *
@@ -1277,8 +1295,7 @@ type ExtractSubcommand<T> = T extends Command<infer _Name, infer _Input, infer _
1277
1295
  : T extends Command.SubcommandGroup<infer Commands> ? Commands[number]
1278
1296
  : never
1279
1297
  type ExtractSubcommandErrors<T extends ReadonlyArray<Command.SubcommandEntry>> = Error<ExtractSubcommand<T[number]>>
1280
- type ExtractSubcommandContext<T extends ReadonlyArray<Command.SubcommandEntry>> = ExtractSubcommand<T[number]> extends
1281
- Command<infer _Name, infer _Input, infer _CI, infer _E, infer _R> ? _R : never
1298
+ type ExtractSubcommandContext<T extends ReadonlyArray<Command.SubcommandEntry>> = Services<ExtractSubcommand<T[number]>>
1282
1299
 
1283
1300
  /**
1284
1301
  * Sets the description for a command.
@@ -2066,6 +2083,38 @@ export const provideEffectDiscard: {
2066
2083
  /* Execution */
2067
2084
  /* ========================================================================== */
2068
2085
 
2086
+ /**
2087
+ * Interactively constructs command-line arguments for a command.
2088
+ *
2089
+ * **Details**
2090
+ *
2091
+ * The returned arguments include the command name and can be inspected,
2092
+ * modified, or passed to another command runner by the caller.
2093
+ *
2094
+ * **Example** (Constructing command arguments)
2095
+ *
2096
+ * ```ts
2097
+ * import { Console, Effect } from "effect"
2098
+ * import { Command } from "effect/unstable/cli"
2099
+ *
2100
+ * const command = Command.make("app")
2101
+ *
2102
+ * const program = Effect.gen(function*() {
2103
+ * const args = yield* Command.wizard(command)
2104
+ * yield* Console.log(args.join(" "))
2105
+ * })
2106
+ * ```
2107
+ *
2108
+ * @category command execution
2109
+ * @since 4.0.0
2110
+ */
2111
+ export const wizard = <Name extends string, Input, E, R, ContextInput>(
2112
+ command: Command<Name, Input, ContextInput, E, R>,
2113
+ options?: {
2114
+ readonly prefix?: ReadonlyArray<string> | undefined
2115
+ } | undefined
2116
+ ): Effect.Effect<Array<string>, CliError.CliError | Terminal.QuitError, Environment> => Wizard.run(command, options)
2117
+
2069
2118
  const getOutOfScopeGlobalFlagErrors = (
2070
2119
  allFlags: ReadonlyArray<GlobalFlag.GlobalFlag<any>>,
2071
2120
  activeFlags: ReadonlyArray<GlobalFlag.GlobalFlag<any>>,
@@ -2110,8 +2159,9 @@ const showHelp = <Name extends string, Input, E, R, ContextInput>(
2110
2159
  error: CliError.ShowHelp
2111
2160
  ): Effect.Effect<void, CliError.CliError, Environment> =>
2112
2161
  Effect.gen(function*() {
2162
+ const { builtIns } = yield* CliConfig.CliConfig
2113
2163
  const formatter = yield* CliOutput.Formatter
2114
- const helpDoc = yield* getHelpForCommandPath(command, error.commandPath, GlobalFlag.BuiltIns)
2164
+ const helpDoc = yield* getHelpForCommandPath(command, error.commandPath, builtIns)
2115
2165
  yield* Console.log(formatter.formatHelpDoc(helpDoc))
2116
2166
  if (error.errors.length > 0) {
2117
2167
  yield* Console.error(formatter.formatErrors(error.errors as any))
@@ -2294,10 +2344,11 @@ export const runWith = <const Name extends string, Input, E, R, ContextInput>(
2294
2344
  const commandImpl = toImpl(command)
2295
2345
  return Effect.fnUntraced(
2296
2346
  function*(args: ReadonlyArray<string>) {
2347
+ const { builtIns } = yield* CliConfig.CliConfig
2297
2348
  const { tokens, trailingOperands } = Lexer.lex(args)
2298
2349
 
2299
2350
  // 1. Collect known global flags from the command tree
2300
- const allFlags = getGlobalFlagsForCommandTree(command, GlobalFlag.BuiltIns)
2351
+ const allFlags = getGlobalFlagsForCommandTree(command, builtIns)
2301
2352
 
2302
2353
  // 2. Extract global flag tokens
2303
2354
  const allFlagParams = allFlags.flatMap((f) => Param.extractSingleParams(f.flag))
@@ -2312,8 +2363,8 @@ export const runWith = <const Name extends string, Input, E, R, ContextInput>(
2312
2363
  // 3. Parse command arguments from remaining tokens
2313
2364
  const parsedArgs = yield* Parser.parseArgs({ tokens: remainder, trailingOperands }, command)
2314
2365
  const commandPath = [command.name, ...Parser.getCommandPath(parsedArgs)] as const
2315
- const handlerCtx: GlobalFlag.HandlerContext = { command, commandPath, version: config.version }
2316
- const activeFlags = getGlobalFlagsForCommandPath(command, commandPath, GlobalFlag.BuiltIns)
2366
+ const handlerCtx: GlobalFlag.HandlerContext = { builtIns, command, commandPath, version: config.version }
2367
+ const activeFlags = getGlobalFlagsForCommandPath(command, commandPath, builtIns)
2317
2368
 
2318
2369
  // 4. Reject globals that were passed outside the active command scope
2319
2370
  const outOfScopeErrors = getOutOfScopeGlobalFlagErrors(allFlags, activeFlags, flagMap, commandPath)
@@ -2335,6 +2386,29 @@ export const runWith = <const Name extends string, Input, E, R, ContextInput>(
2335
2386
  })
2336
2387
  if (!hasEntry) continue
2337
2388
  const [, value] = yield* flag.flag.parse(emptyArgs)
2389
+ if (flag === GlobalFlag.Wizard) {
2390
+ return yield* Effect.gen(function*() {
2391
+ yield* Console.log(Wizard.renderIntroduction(command.name, config.version, command.description))
2392
+ const prefix = [
2393
+ command.name,
2394
+ ...args.filter((arg) => arg !== "--wizard" && !arg.startsWith("--wizard="))
2395
+ ]
2396
+ const wizardArgs = yield* Wizard.run(command, { commandPath, prefix })
2397
+ yield* Console.log(Wizard.renderCompletion(wizardArgs))
2398
+ const shouldRun = yield* Prompt.run(Prompt.toggle({
2399
+ message: "Run this command?",
2400
+ initial: true,
2401
+ active: "yes",
2402
+ inactive: "no"
2403
+ }))
2404
+ if (shouldRun) {
2405
+ yield* Console.log()
2406
+ yield* runWith(command, config)(wizardArgs.slice(1))
2407
+ }
2408
+ }).pipe(
2409
+ Effect.catchTag("QuitError", () => Console.log(Wizard.renderQuit()))
2410
+ )
2411
+ }
2338
2412
  yield* flag.run(value, handlerCtx)
2339
2413
  return
2340
2414
  }
@@ -2353,7 +2427,9 @@ export const runWith = <const Name extends string, Input, E, R, ContextInput>(
2353
2427
 
2354
2428
  // 7. Provide setting values
2355
2429
  let program = commandImpl.handle(parseResult.success, [command.name])
2356
- const [, logLevel] = yield* GlobalFlag.LogLevel.flag.parse(emptyArgs)
2430
+ const logLevel = activeFlags.includes(GlobalFlag.LogLevel)
2431
+ ? (yield* GlobalFlag.LogLevel.flag.parse(emptyArgs))[1]
2432
+ : Option.none()
2357
2433
  program = Effect.provideService(program, GlobalFlag.LogLevel, logLevel)
2358
2434
  for (const flag of activeFlags) {
2359
2435
  if (flag._tag !== "Setting" || flag === GlobalFlag.LogLevel) continue
@@ -5,7 +5,7 @@
5
5
  * This module defines two kinds of global flags: action flags, which run an
6
6
  * effect and stop normal command execution, and setting flags, which provide a
7
7
  * parsed value to the command handler through the Effect context. It also
8
- * defines the built-in help, version, shell-completion, and log-level flags
8
+ * defines the built-in help, version, wizard, shell-completion, and log-level flags
9
9
  * used by `Command.run` and `Command.runWith`.
10
10
  *
11
11
  * @since 4.0.0
@@ -37,6 +37,7 @@ export interface HandlerContext {
37
37
  readonly command: Command.Command.Any
38
38
  readonly commandPath: ReadonlyArray<string>
39
39
  readonly version: string
40
+ readonly builtIns: ReadonlyArray<BuiltIn>
40
41
  }
41
42
 
42
43
  /**
@@ -156,9 +157,9 @@ export const Help: Action<boolean> = action({
156
157
  Flag.withAlias("h"),
157
158
  Flag.withDescription("Show help information")
158
159
  ),
159
- run: Effect.fnUntraced(function*(_, { command, commandPath }) {
160
+ run: Effect.fnUntraced(function*(_, { builtIns, command, commandPath }) {
160
161
  const formatter = yield* CliOutput.Formatter
161
- const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath, BuiltIns)
162
+ const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath, builtIns)
162
163
  yield* Console.log(formatter.formatHelpDoc(helpDoc))
163
164
  })
164
165
  })
@@ -184,6 +185,24 @@ export const Version: Action<boolean> = action({
184
185
  })
185
186
  })
186
187
 
188
+ /**
189
+ * Defines the global action flag for starting interactive wizard mode.
190
+ *
191
+ * **Details**
192
+ *
193
+ * `Command.run` and `Command.runWith` handle this action specially so the
194
+ * generated arguments can be passed back through the command parser.
195
+ *
196
+ * @category references
197
+ * @since 4.0.0
198
+ */
199
+ export const Wizard: Action<boolean> = action({
200
+ flag: Flag.boolean("wizard").pipe(
201
+ Flag.withDescription("Start wizard mode for a command")
202
+ ),
203
+ run: () => Effect.void
204
+ })
205
+
187
206
  /**
188
207
  * Defines the `--completions` global flag, which prints a shell completion script for
189
208
  * the given shell.
@@ -259,7 +278,7 @@ export const LogLevel: Setting<"log-level", Option.Option<LogLevelType>> = setti
259
278
  *
260
279
  * **Details**
261
280
  *
262
- * The built-ins are `Help`, `Version`, `Completions`, and `LogLevel`.
281
+ * The built-ins are `Help`, `Version`, `Wizard`, `Completions`, and `LogLevel`.
263
282
  * `Command.runWith` prepends these built-ins when collecting and parsing global
264
283
  * flags.
265
284
  *
@@ -277,8 +296,17 @@ export const LogLevel: Setting<"log-level", Option.Option<LogLevelType>> = setti
277
296
  * @since 4.0.0
278
297
  */
279
298
  export const BuiltIns: readonly [
299
+ Action<boolean>,
280
300
  Action<boolean>,
281
301
  Action<boolean>,
282
302
  Action<Option.Option<"bash" | "zsh" | "fish">>,
283
303
  Setting<"log-level", Option.Option<LogLevelType>>
284
- ] = [Help, Version, Completions, LogLevel]
304
+ ] = [Help, Version, Wizard, Completions, LogLevel]
305
+
306
+ /**
307
+ * Global flag included in the default command-runner configuration.
308
+ *
309
+ * @category models
310
+ * @since 4.0.0
311
+ */
312
+ export type BuiltIn = typeof BuiltIns[number]
@@ -2898,9 +2898,19 @@ export const getUnderlyingSingleOrThrow = <Kind extends ParamKind, A>(
2898
2898
  */
2899
2899
  export const getParamMetadata = <Kind extends ParamKind, A>(
2900
2900
  param: Param<Kind, A>
2901
- ): { isOptional: boolean; isVariadic: boolean } => {
2901
+ ): {
2902
+ readonly isOptional: boolean
2903
+ readonly isVariadic: boolean
2904
+ readonly variadicMin: Option.Option<number>
2905
+ readonly variadicMax: Option.Option<number>
2906
+ } => {
2902
2907
  return matchParam(param, {
2903
- Single: () => ({ isOptional: false, isVariadic: false }),
2908
+ Single: () => ({
2909
+ isOptional: false,
2910
+ isVariadic: false,
2911
+ variadicMin: Option.none(),
2912
+ variadicMax: Option.none()
2913
+ }),
2904
2914
  Map: (mapped) => getParamMetadata(mapped.param),
2905
2915
  Transform: (mapped) => getParamMetadata(mapped.param),
2906
2916
  Optional: (optional) => ({
@@ -2909,7 +2919,9 @@ export const getParamMetadata = <Kind extends ParamKind, A>(
2909
2919
  }),
2910
2920
  Variadic: (variadic) => ({
2911
2921
  ...getParamMetadata(variadic.param),
2912
- isVariadic: true
2922
+ isVariadic: true,
2923
+ variadicMin: variadic.min,
2924
+ variadicMax: variadic.max
2913
2925
  })
2914
2926
  })
2915
2927
  }
@@ -9,6 +9,11 @@
9
9
  */
10
10
  export * as Argument from "./Argument.ts"
11
11
 
12
+ /**
13
+ * @since 4.0.0
14
+ */
15
+ export * as CliConfig from "./CliConfig.ts"
16
+
12
17
  /**
13
18
  * @since 4.0.0
14
19
  */
@@ -57,6 +57,9 @@ export const red = `${ESC}31m`
57
57
  /** @internal */
58
58
  export const green = `${ESC}32m`
59
59
 
60
+ /** @internal */
61
+ export const magenta = `${ESC}35m`
62
+
60
63
  /** @internal */
61
64
  export const white = `${ESC}37m`
62
65
 
@@ -391,6 +391,18 @@ const invalidNegatedFlagValue = (
391
391
  kind: "flag"
392
392
  })
393
393
 
394
+ const missingFlagValue = (spec: FlagParam): CliError.InvalidValue => {
395
+ const choices = Primitive.getChoiceKeys(spec.primitiveType)
396
+ return new CliError.InvalidValue({
397
+ option: spec.name,
398
+ value: "",
399
+ expected: choices === undefined
400
+ ? spec.typeName ?? Primitive.getTypeName(spec.primitiveType)
401
+ : choices.join(" | "),
402
+ kind: "flag"
403
+ })
404
+ }
405
+
394
406
  /**
395
407
  * Checks whether a token is a boolean literal value.
396
408
  * Recognizes: true/false, yes/no, on/off, 1/0
@@ -497,8 +509,8 @@ const consumeFlagValueWithTokens = (
497
509
  }
498
510
 
499
511
  return {
500
- _tag: "Value",
501
- value: undefined,
512
+ _tag: "Error",
513
+ error: missingFlagValue(spec),
502
514
  tokens: []
503
515
  }
504
516
  }