effect 4.0.0-beta.13 → 4.0.0-beta.15

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 (64) hide show
  1. package/dist/Channel.d.ts +6 -6
  2. package/dist/Channel.d.ts.map +1 -1
  3. package/dist/Channel.js +4 -4
  4. package/dist/Channel.js.map +1 -1
  5. package/dist/Effect.d.ts +7 -7
  6. package/dist/Effect.d.ts.map +1 -1
  7. package/dist/Effect.js.map +1 -1
  8. package/dist/Stream.d.ts +7 -7
  9. package/dist/Stream.d.ts.map +1 -1
  10. package/dist/Stream.js +8 -6
  11. package/dist/Stream.js.map +1 -1
  12. package/dist/Types.d.ts +70 -0
  13. package/dist/Types.d.ts.map +1 -1
  14. package/dist/internal/effect.js +4 -4
  15. package/dist/internal/effect.js.map +1 -1
  16. package/dist/unstable/ai/LanguageModel.d.ts +2 -0
  17. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  18. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  19. package/dist/unstable/cli/Command.d.ts +34 -4
  20. package/dist/unstable/cli/Command.d.ts.map +1 -1
  21. package/dist/unstable/cli/Command.js +75 -20
  22. package/dist/unstable/cli/Command.js.map +1 -1
  23. package/dist/unstable/cli/GlobalFlag.d.ts +25 -62
  24. package/dist/unstable/cli/GlobalFlag.d.ts.map +1 -1
  25. package/dist/unstable/cli/GlobalFlag.js +41 -87
  26. package/dist/unstable/cli/GlobalFlag.js.map +1 -1
  27. package/dist/unstable/cli/internal/command.d.ts +3 -0
  28. package/dist/unstable/cli/internal/command.d.ts.map +1 -1
  29. package/dist/unstable/cli/internal/command.js +2 -0
  30. package/dist/unstable/cli/internal/command.js.map +1 -1
  31. package/dist/unstable/cli/internal/help.d.ts +18 -4
  32. package/dist/unstable/cli/internal/help.d.ts.map +1 -1
  33. package/dist/unstable/cli/internal/help.js +62 -9
  34. package/dist/unstable/cli/internal/help.js.map +1 -1
  35. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +10 -4
  36. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  37. package/dist/unstable/httpapi/HttpApiBuilder.js +17 -6
  38. package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
  39. package/dist/unstable/httpapi/HttpApiEndpoint.d.ts +7 -2
  40. package/dist/unstable/httpapi/HttpApiEndpoint.d.ts.map +1 -1
  41. package/dist/unstable/httpapi/HttpApiEndpoint.js.map +1 -1
  42. package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
  43. package/dist/unstable/httpapi/OpenApi.js +3 -4
  44. package/dist/unstable/httpapi/OpenApi.js.map +1 -1
  45. package/dist/unstable/reactivity/AtomHttpApi.d.ts +2 -2
  46. package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
  47. package/dist/unstable/reactivity/AtomRegistry.js +2 -6
  48. package/dist/unstable/reactivity/AtomRegistry.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/Channel.ts +24 -14
  51. package/src/Effect.ts +30 -8
  52. package/src/Stream.ts +46 -22
  53. package/src/Types.ts +66 -0
  54. package/src/internal/effect.ts +41 -14
  55. package/src/unstable/ai/LanguageModel.ts +9 -6
  56. package/src/unstable/cli/Command.ts +122 -24
  57. package/src/unstable/cli/GlobalFlag.ts +98 -197
  58. package/src/unstable/cli/internal/command.ts +5 -0
  59. package/src/unstable/cli/internal/help.ts +104 -24
  60. package/src/unstable/httpapi/HttpApiBuilder.ts +68 -13
  61. package/src/unstable/httpapi/HttpApiEndpoint.ts +13 -4
  62. package/src/unstable/httpapi/OpenApi.ts +3 -4
  63. package/src/unstable/reactivity/AtomHttpApi.ts +2 -2
  64. package/src/unstable/reactivity/AtomRegistry.ts +2 -6
@@ -4,11 +4,8 @@
4
4
 
5
5
  import * as Console from "../../Console.ts"
6
6
  import * as Effect from "../../Effect.ts"
7
- import { dual } from "../../Function.ts"
8
- import * as Layer from "../../Layer.ts"
9
- import type { LogLevel } from "../../LogLevel.ts"
7
+ import type { LogLevel as LogLevelType } from "../../LogLevel.ts"
10
8
  import * as Option from "../../Option.ts"
11
- import * as References from "../../References.ts"
12
9
  import * as ServiceMap from "../../ServiceMap.ts"
13
10
  import * as CliOutput from "./CliOutput.ts"
14
11
  import type * as Command from "./Command.ts"
@@ -51,10 +48,21 @@ export interface Action<A> {
51
48
  * @since 4.0.0
52
49
  * @category models
53
50
  */
54
- export interface Setting<A> {
51
+ export interface Setting<Id extends string, A> extends ServiceMap.Service<Setting.Identifier<Id>, A> {
55
52
  readonly _tag: "Setting"
53
+ readonly id: Id
56
54
  readonly flag: Flag.Flag<A>
57
- readonly layer: (value: A) => Layer.Layer<never>
55
+ }
56
+
57
+ /**
58
+ * @since 4.0.0
59
+ */
60
+ export declare namespace Setting {
61
+ /**
62
+ * @since 4.0.0
63
+ * @category models
64
+ */
65
+ export type Identifier<Id extends string> = `effect/unstable/cli/GlobalFlag/${Id}`
58
66
  }
59
67
 
60
68
  /**
@@ -63,7 +71,7 @@ export interface Setting<A> {
63
71
  * @since 4.0.0
64
72
  * @category models
65
73
  */
66
- export type GlobalFlag<A> = Action<A> | Setting<A>
74
+ export type GlobalFlag<A> = Action<A> | Setting<any, A>
67
75
 
68
76
  /* ========================================================================== */
69
77
  /* Constructors */
@@ -93,21 +101,31 @@ export const action = <A>(options: {
93
101
  * @since 4.0.0
94
102
  * @category constructors
95
103
  */
96
- export const setting = <A>(options: {
104
+ export const setting = <const Id extends string>(
105
+ id: Id
106
+ ) =>
107
+ <A>(options: {
97
108
  readonly flag: Flag.Flag<A>
98
- readonly layer: (value: A) => Layer.Layer<never>
99
- }): Setting<A> => ({
100
- _tag: "Setting",
101
- flag: options.flag,
102
- layer: options.layer
103
- })
109
+ }): Setting<Id, A> => {
110
+ settingIdCounter += 1
111
+ const ref = ServiceMap.Service<Setting.Identifier<Id>, A>(
112
+ `effect/unstable/cli/GlobalFlag/${id}/${settingIdCounter}`
113
+ )
114
+ return Object.assign(ref, {
115
+ _tag: "Setting" as const,
116
+ id,
117
+ flag: options.flag
118
+ })
119
+ }
120
+
121
+ let settingIdCounter = 0
104
122
 
105
123
  /* ========================================================================== */
106
124
  /* Built-in Flag References */
107
125
  /* ========================================================================== */
108
126
 
109
127
  import * as CommandDescriptor from "./internal/completions/CommandDescriptor.ts"
110
- import * as Completions from "./internal/completions/Completions.ts"
128
+ import * as CompletionsInternal from "./internal/completions/Completions.ts"
111
129
  import * as HelpInternal from "./internal/help.ts"
112
130
 
113
131
  /**
@@ -117,24 +135,18 @@ import * as HelpInternal from "./internal/help.ts"
117
135
  * @since 4.0.0
118
136
  * @category references
119
137
  */
120
- export const Help: ServiceMap.Reference<Action<boolean>> = ServiceMap.Reference(
121
- "effect/cli/GlobalFlag/Help",
122
- {
123
- defaultValue: () =>
124
- action({
125
- flag: Flag.boolean("help").pipe(
126
- Flag.withAlias("h"),
127
- Flag.withDescription("Show help information")
128
- ),
129
- run: (_, { command, commandPath }) =>
130
- Effect.gen(function*() {
131
- const formatter = yield* CliOutput.Formatter
132
- const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath, Registry)
133
- yield* Console.log(formatter.formatHelpDoc(helpDoc))
134
- })
135
- })
136
- }
137
- )
138
+ export const Help: Action<boolean> = action({
139
+ flag: Flag.boolean("help").pipe(
140
+ Flag.withAlias("h"),
141
+ Flag.withDescription("Show help information")
142
+ ),
143
+ run: (_, { command, commandPath }) =>
144
+ Effect.gen(function*() {
145
+ const formatter = yield* CliOutput.Formatter
146
+ const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath, BuiltIns)
147
+ yield* Console.log(formatter.formatHelpDoc(helpDoc))
148
+ })
149
+ })
138
150
 
139
151
  /**
140
152
  * The `--version` global flag.
@@ -143,22 +155,16 @@ export const Help: ServiceMap.Reference<Action<boolean>> = ServiceMap.Reference(
143
155
  * @since 4.0.0
144
156
  * @category references
145
157
  */
146
- export const Version: ServiceMap.Reference<Action<boolean>> = ServiceMap.Reference(
147
- "effect/cli/GlobalFlag/Version",
148
- {
149
- defaultValue: () =>
150
- action({
151
- flag: Flag.boolean("version").pipe(
152
- Flag.withDescription("Show version information")
153
- ),
154
- run: (_, { command, version }) =>
155
- Effect.gen(function*() {
156
- const formatter = yield* CliOutput.Formatter
157
- yield* Console.log(formatter.formatVersion(command.name, version))
158
- })
159
- })
160
- }
161
- )
158
+ export const Version: Action<boolean> = action({
159
+ flag: Flag.boolean("version").pipe(
160
+ Flag.withDescription("Show version information")
161
+ ),
162
+ run: (_, { command, version }) =>
163
+ Effect.gen(function*() {
164
+ const formatter = yield* CliOutput.Formatter
165
+ yield* Console.log(formatter.formatVersion(command.name, version))
166
+ })
167
+ })
162
168
 
163
169
  /**
164
170
  * The `--completions` global flag.
@@ -167,25 +173,20 @@ export const Version: ServiceMap.Reference<Action<boolean>> = ServiceMap.Referen
167
173
  * @since 4.0.0
168
174
  * @category references
169
175
  */
170
- export const CompletionsFlag: ServiceMap.Reference<
171
- Action<Option.Option<"bash" | "zsh" | "fish">>
172
- > = ServiceMap.Reference("effect/cli/GlobalFlag/Completions", {
173
- defaultValue: () =>
174
- action({
175
- flag: Flag.choice("completions", ["bash", "zsh", "fish", "sh"] as const)
176
- .pipe(
177
- Flag.optional,
178
- Flag.map((v) => Option.map(v, (s) => s === "sh" ? "bash" : s)),
179
- Flag.withDescription("Print shell completion script")
180
- ),
181
- run: (shell, { command }) =>
182
- Effect.gen(function*() {
183
- if (Option.isNone(shell)) return
184
- const descriptor = CommandDescriptor.fromCommand(command)
185
- yield* Console.log(
186
- Completions.generate(command.name, shell.value, descriptor)
187
- )
188
- })
176
+ export const Completions: Action<Option.Option<"bash" | "zsh" | "fish">> = action({
177
+ flag: Flag.choice("completions", ["bash", "zsh", "fish", "sh"] as const)
178
+ .pipe(
179
+ Flag.optional,
180
+ Flag.map((v) => Option.map(v, (s) => s === "sh" ? "bash" : s)),
181
+ Flag.withDescription("Print shell completion script")
182
+ ),
183
+ run: (shell, { command }) =>
184
+ Effect.gen(function*() {
185
+ if (Option.isNone(shell)) return
186
+ const descriptor = CommandDescriptor.fromCommand(command)
187
+ yield* Console.log(
188
+ CompletionsInternal.generate(command.name, shell.value, descriptor)
189
+ )
189
190
  })
190
191
  })
191
192
 
@@ -196,147 +197,47 @@ export const CompletionsFlag: ServiceMap.Reference<
196
197
  * @since 4.0.0
197
198
  * @category references
198
199
  */
199
- export const LogLevelFlag: ServiceMap.Reference<
200
- Setting<Option.Option<LogLevel>>
201
- > = ServiceMap.Reference("effect/cli/GlobalFlag/LogLevel", {
202
- defaultValue: () =>
203
- setting({
204
- flag: Flag.choiceWithValue(
205
- "log-level",
206
- [
207
- ["all", "All"],
208
- ["trace", "Trace"],
209
- ["debug", "Debug"],
210
- ["info", "Info"],
211
- ["warn", "Warn"],
212
- ["warning", "Warn"],
213
- ["error", "Error"],
214
- ["fatal", "Fatal"],
215
- ["none", "None"]
216
- ] as const
217
- ).pipe(
218
- Flag.optional,
219
- Flag.withDescription("Sets the minimum log level")
220
- ),
221
- layer: (value) =>
222
- Option.match(value, {
223
- onNone: () => Layer.empty,
224
- onSome: (level) => Layer.succeed(References.MinimumLogLevel, level)
225
- })
226
- })
200
+ export const LogLevel: Setting<"log-level", Option.Option<LogLevelType>> = setting("log-level")({
201
+ flag: Flag.choiceWithValue(
202
+ "log-level",
203
+ [
204
+ ["all", "All"],
205
+ ["trace", "Trace"],
206
+ ["debug", "Debug"],
207
+ ["info", "Info"],
208
+ ["warn", "Warn"],
209
+ ["warning", "Warn"],
210
+ ["error", "Error"],
211
+ ["fatal", "Fatal"],
212
+ ["none", "None"]
213
+ ] as const
214
+ ).pipe(
215
+ Flag.optional,
216
+ Flag.withDescription("Sets the minimum log level")
217
+ )
227
218
  })
228
219
 
229
220
  /* ========================================================================== */
230
- /* Registry */
221
+ /* References */
231
222
  /* ========================================================================== */
232
223
 
233
224
  /**
234
- * The ordered set of global flag references.
235
- * The parser iterates this set to know which flags to extract.
225
+ * Built-in global flags in default precedence order.
236
226
  *
237
227
  * @since 4.0.0
238
228
  * @category references
239
229
  */
240
- export const Registry: ServiceMap.Reference<
241
- Set<ServiceMap.Reference<GlobalFlag<any>>>
242
- > = ServiceMap.Reference("effect/cli/GlobalFlag/Registry", {
243
- defaultValue: () =>
244
- new Set([
245
- Help as ServiceMap.Reference<GlobalFlag<any>>,
246
- Version as ServiceMap.Reference<GlobalFlag<any>>,
247
- CompletionsFlag as ServiceMap.Reference<GlobalFlag<any>>,
248
- LogLevelFlag as ServiceMap.Reference<GlobalFlag<any>>
249
- ])
250
- })
251
-
252
- /* ========================================================================== */
253
- /* Public API */
254
- /* ========================================================================== */
230
+ export const BuiltIns: ReadonlyArray<GlobalFlag<any>> = [
231
+ Help,
232
+ Version,
233
+ Completions,
234
+ LogLevel
235
+ ]
255
236
 
256
237
  /**
257
- * Adds a global flag to the registry.
238
+ * Built-in setting context identifiers.
258
239
  *
259
240
  * @since 4.0.0
260
- * @category modifiers
261
- */
262
- export const add: {
263
- /* ========================================================================== */
264
- /* Public API */
265
- /* ========================================================================== */
266
-
267
- /**
268
- * Adds a global flag to the registry.
269
- *
270
- * @since 4.0.0
271
- * @category modifiers
272
- */
273
- <A>(ref: ServiceMap.Reference<GlobalFlag<A>>): <B, E, R>(
274
- self: Effect.Effect<B, E, R>
275
- ) => Effect.Effect<B, E, R>
276
- /* ========================================================================== */
277
- /* Public API */
278
- /* ========================================================================== */
279
-
280
- /**
281
- * Adds a global flag to the registry.
282
- *
283
- * @since 4.0.0
284
- * @category modifiers
285
- */
286
- <B, E, R, A>(self: Effect.Effect<B, E, R>, ref: ServiceMap.Reference<GlobalFlag<A>>): Effect.Effect<B, E, R>
287
- } = dual(
288
- 2,
289
- Effect.fnUntraced(function*<B, E, R, A>(
290
- self: Effect.Effect<B, E, R>,
291
- ref: ServiceMap.Reference<GlobalFlag<A>>
292
- ) {
293
- const currentRegistry = yield* Registry
294
- const nextRegistry = new Set([...currentRegistry, ref])
295
- return yield* Effect.provideService(self, Registry, nextRegistry)
296
- })
297
- )
298
-
299
- /**
300
- * Removes a global flag by its reference.
301
- *
302
- * @since 4.0.0
303
- * @category modifiers
304
- */
305
- export const remove: {
306
- /**
307
- * Removes a global flag by its reference.
308
- *
309
- * @since 4.0.0
310
- * @category modifiers
311
- */
312
- <A>(ref: ServiceMap.Reference<GlobalFlag<A>>): <B, E, R>(
313
- self: Effect.Effect<B, E, R>
314
- ) => Effect.Effect<B, E, R>
315
- /**
316
- * Removes a global flag by its reference.
317
- *
318
- * @since 4.0.0
319
- * @category modifiers
320
- */
321
- <B, E, R, A>(self: Effect.Effect<B, E, R>, ref: ServiceMap.Reference<GlobalFlag<A>>): Effect.Effect<B, E, R>
322
- } = dual(
323
- 2,
324
- Effect.fnUntraced(function*<B, E, R, A>(
325
- self: Effect.Effect<B, E, R>,
326
- ref: ServiceMap.Reference<GlobalFlag<A>>
327
- ) {
328
- const currentRegistry = yield* Registry
329
- const nextRegistry = new Set(currentRegistry)
330
- nextRegistry.delete(ref)
331
- return yield* Effect.provideService(self, Registry, nextRegistry)
332
- })
333
- )
334
-
335
- /**
336
- * Removes all global flags (built-in and user-defined).
337
- *
338
- * @since 4.0.0
339
- * @category modifiers
241
+ * @category models
340
242
  */
341
- export const clear = <B, E, R>(self: Effect.Effect<B, E, R>): Effect.Effect<B, E, R> =>
342
- Effect.provideService(self, Registry, new Set())
243
+ export type BuiltInSettingContext = Setting.Identifier<"log-level">
@@ -12,6 +12,7 @@ import { pipeArguments } from "../../../Pipeable.ts"
12
12
  import * as Predicate from "../../../Predicate.ts"
13
13
  import * as ServiceMap from "../../../ServiceMap.ts"
14
14
  import * as CliError from "../CliError.ts"
15
+ import type * as GlobalFlag from "../GlobalFlag.ts"
15
16
  import type { ArgDoc, ExampleDoc, FlagDoc, HelpDoc, SubcommandGroupDoc } from "../HelpDoc.ts"
16
17
  import * as Param from "../Param.ts"
17
18
  import * as Primitive from "../Primitive.ts"
@@ -36,6 +37,7 @@ export interface CommandInternal<Name extends string, Input, E, R> extends Comma
36
37
  readonly config: ConfigInternal
37
38
  readonly service: ServiceMap.Service<CommandContext<Name>, Input>
38
39
  readonly annotations: ServiceMap.ServiceMap<never>
40
+ readonly globalFlags: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
39
41
  readonly parse: (input: ParsedTokens) => Effect.Effect<Input, CliError.CliError, Environment>
40
42
  readonly handle: (
41
43
  input: Input,
@@ -88,6 +90,7 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
88
90
  readonly config: ConfigInternal
89
91
  readonly service?: ServiceMap.Service<CommandContext<Name>, Input> | undefined
90
92
  readonly annotations?: ServiceMap.ServiceMap<never> | undefined
93
+ readonly globalFlags?: ReadonlyArray<GlobalFlag.GlobalFlag<any>> | undefined
91
94
  readonly description?: string | undefined
92
95
  readonly shortDescription?: string | undefined
93
96
  readonly alias?: string | undefined
@@ -101,6 +104,7 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
101
104
  const service = options.service ?? ServiceMap.Service<CommandContext<Name>, Input>(`${TypeId}/${options.name}`)
102
105
  const config = options.config
103
106
  const annotations = options.annotations ?? ServiceMap.empty()
107
+ const globalFlags = options.globalFlags ?? []
104
108
  const subcommands = options.subcommands ?? []
105
109
 
106
110
  const handle = (
@@ -191,6 +195,7 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
191
195
  name: options.name,
192
196
  examples: options.examples ?? [],
193
197
  annotations,
198
+ globalFlags,
194
199
  subcommands,
195
200
  config,
196
201
  service,
@@ -6,49 +6,129 @@
6
6
  * Extracted from command.ts to avoid circular dependencies.
7
7
  */
8
8
  import * as Effect from "../../../Effect.ts"
9
- import type * as ServiceMap from "../../../ServiceMap.ts"
10
9
  import type { Command } from "../Command.ts"
11
- import type { GlobalFlag } from "../GlobalFlag.ts"
10
+ import type * as GlobalFlag from "../GlobalFlag.ts"
12
11
  import type { FlagDoc, HelpDoc } from "../HelpDoc.ts"
13
12
  import * as Param from "../Param.ts"
14
13
  import * as Primitive from "../Primitive.ts"
15
14
  import { toImpl } from "./command.ts"
16
15
 
16
+ const dedupeGlobalFlags = (
17
+ flags: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
18
+ ): ReadonlyArray<GlobalFlag.GlobalFlag<any>> => {
19
+ const seen = new Set<GlobalFlag.GlobalFlag<any>>()
20
+ const deduped: Array<GlobalFlag.GlobalFlag<any>> = []
21
+ for (const flag of flags) {
22
+ if (seen.has(flag)) {
23
+ continue
24
+ }
25
+ seen.add(flag)
26
+ deduped.push(flag)
27
+ }
28
+ return deduped
29
+ }
30
+
17
31
  /**
18
- * Helper function to get help documentation for a specific command path.
19
- * Navigates through the command hierarchy to find the right command.
20
- * Reads global flags from the registry and includes them in the help doc.
32
+ * Returns the resolved command lineage for the provided path.
33
+ * Includes the root command as the first element.
21
34
  */
22
- export const getHelpForCommandPath = <Name extends string, Input, E, R>(
35
+ export const getCommandsForCommandPath = <Name extends string, Input, E, R>(
23
36
  command: Command<Name, Input, E, R>,
24
- commandPath: ReadonlyArray<string>,
25
- registry: ServiceMap.Reference<Set<ServiceMap.Reference<GlobalFlag<unknown>>>>
26
- ): Effect.Effect<HelpDoc, never, never> =>
27
- Effect.gen(function*() {
28
- let currentCommand: Command.Any = command
37
+ commandPath: ReadonlyArray<string>
38
+ ): ReadonlyArray<Command.Any> => {
39
+ const commands: Array<Command.Any> = [command]
40
+ let currentCommand: Command.Any = command
29
41
 
30
- for (let i = 1; i < commandPath.length; i++) {
31
- const subcommandName = commandPath[i]
32
- let subcommand: Command.Any | undefined = undefined
42
+ for (let i = 1; i < commandPath.length; i++) {
43
+ const subcommandName = commandPath[i]
44
+ let subcommand: Command.Any | undefined = undefined
33
45
 
34
- for (const group of currentCommand.subcommands) {
35
- subcommand = group.commands.find((sub) => sub.name === subcommandName)
36
- if (subcommand) {
37
- break
38
- }
46
+ for (const group of currentCommand.subcommands) {
47
+ subcommand = group.commands.find((sub) => sub.name === subcommandName)
48
+ if (subcommand) {
49
+ break
39
50
  }
51
+ }
40
52
 
41
- if (subcommand) {
42
- currentCommand = subcommand
53
+ if (!subcommand) {
54
+ break
55
+ }
56
+
57
+ commands.push(subcommand)
58
+ currentCommand = subcommand
59
+ }
60
+
61
+ return commands
62
+ }
63
+
64
+ /**
65
+ * Returns active global flags for a command path.
66
+ * Built-ins are prepended and declarations are collected root -> leaf.
67
+ */
68
+ export const getGlobalFlagsForCommandPath = <Name extends string, Input, E, R>(
69
+ command: Command<Name, Input, E, R>,
70
+ commandPath: ReadonlyArray<string>,
71
+ builtIns: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
72
+ ): ReadonlyArray<GlobalFlag.GlobalFlag<any>> => {
73
+ const commands = getCommandsForCommandPath(command, commandPath)
74
+ const declared = commands.flatMap((current) => toImpl(current).globalFlags)
75
+ return dedupeGlobalFlags([
76
+ ...builtIns,
77
+ ...declared
78
+ ])
79
+ }
80
+
81
+ const collectDeclaredGlobalFlags = (command: Command.Any): ReadonlyArray<GlobalFlag.GlobalFlag<any>> => {
82
+ const collected: Array<GlobalFlag.GlobalFlag<any>> = []
83
+
84
+ const visit = (current: Command.Any): void => {
85
+ const impl = toImpl(current)
86
+ for (const flag of impl.globalFlags) {
87
+ collected.push(flag)
88
+ }
89
+ for (const group of current.subcommands) {
90
+ for (const subcommand of group.commands) {
91
+ visit(subcommand)
43
92
  }
44
93
  }
94
+ }
95
+
96
+ visit(command)
97
+ return dedupeGlobalFlags(collected)
98
+ }
99
+
100
+ /**
101
+ * Returns all global flags declared in a command tree.
102
+ * Built-ins are prepended and command declarations are deduplicated by identity.
103
+ */
104
+ export const getGlobalFlagsForCommandTree = <Name extends string, Input, E, R>(
105
+ command: Command<Name, Input, E, R>,
106
+ builtIns: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
107
+ ): ReadonlyArray<GlobalFlag.GlobalFlag<any>> =>
108
+ dedupeGlobalFlags([
109
+ ...builtIns,
110
+ ...collectDeclaredGlobalFlags(command)
111
+ ])
112
+
113
+ /**
114
+ * Helper function to get help documentation for a specific command path.
115
+ * Navigates through the command hierarchy to find the right command.
116
+ * Reads active global flags for the path and includes them in the help doc.
117
+ */
118
+ export const getHelpForCommandPath = <Name extends string, Input, E, R>(
119
+ command: Command<Name, Input, E, R>,
120
+ commandPath: ReadonlyArray<string>,
121
+ builtIns: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
122
+ ): Effect.Effect<HelpDoc, never, never> =>
123
+ Effect.gen(function*() {
124
+ const commands = getCommandsForCommandPath(command, commandPath)
125
+ const currentCommand = commands.length > 0 ? commands[commands.length - 1] : command
45
126
 
46
127
  const baseDoc = toImpl(currentCommand).buildHelpDoc(commandPath)
47
128
 
48
- const refs = yield* registry
129
+ const flags = getGlobalFlagsForCommandPath(command, commandPath, builtIns)
49
130
  const globalFlagDocs: Array<FlagDoc> = []
50
- for (const ref of refs) {
51
- const flag = yield* ref
131
+ for (const flag of flags) {
52
132
  const singles = Param.extractSingleParams(flag.flag)
53
133
  for (const single of singles) {
54
134
  const formattedAliases = single.aliases.map((alias) => alias.length === 1 ? `-${alias}` : `--${alias}`)