effect 4.0.0-beta.14 → 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.
- package/dist/Channel.d.ts +6 -6
- package/dist/Channel.d.ts.map +1 -1
- package/dist/Channel.js +4 -4
- package/dist/Channel.js.map +1 -1
- package/dist/Effect.d.ts +7 -7
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js.map +1 -1
- package/dist/Stream.d.ts +7 -7
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +8 -6
- package/dist/Stream.js.map +1 -1
- package/dist/Types.d.ts +70 -0
- package/dist/Types.d.ts.map +1 -1
- package/dist/internal/effect.js +4 -4
- package/dist/internal/effect.js.map +1 -1
- package/dist/unstable/ai/LanguageModel.d.ts +2 -0
- package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
- package/dist/unstable/ai/LanguageModel.js.map +1 -1
- package/dist/unstable/cli/Command.d.ts +34 -4
- package/dist/unstable/cli/Command.d.ts.map +1 -1
- package/dist/unstable/cli/Command.js +73 -24
- package/dist/unstable/cli/Command.js.map +1 -1
- package/dist/unstable/cli/GlobalFlag.d.ts +21 -56
- package/dist/unstable/cli/GlobalFlag.d.ts.map +1 -1
- package/dist/unstable/cli/GlobalFlag.js +9 -48
- package/dist/unstable/cli/GlobalFlag.js.map +1 -1
- package/dist/unstable/cli/internal/command.d.ts +3 -0
- package/dist/unstable/cli/internal/command.d.ts.map +1 -1
- package/dist/unstable/cli/internal/command.js +2 -0
- package/dist/unstable/cli/internal/command.js.map +1 -1
- package/dist/unstable/cli/internal/help.d.ts +18 -4
- package/dist/unstable/cli/internal/help.d.ts.map +1 -1
- package/dist/unstable/cli/internal/help.js +61 -7
- package/dist/unstable/cli/internal/help.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiBuilder.d.ts +10 -4
- package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiBuilder.js +17 -6
- package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiEndpoint.d.ts +7 -2
- package/dist/unstable/httpapi/HttpApiEndpoint.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiEndpoint.js.map +1 -1
- package/dist/unstable/reactivity/AtomHttpApi.d.ts +2 -2
- package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomRegistry.js +2 -6
- package/dist/unstable/reactivity/AtomRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/Channel.ts +24 -14
- package/src/Effect.ts +30 -8
- package/src/Stream.ts +46 -22
- package/src/Types.ts +66 -0
- package/src/internal/effect.ts +41 -14
- package/src/unstable/ai/LanguageModel.ts +9 -6
- package/src/unstable/cli/Command.ts +119 -31
- package/src/unstable/cli/GlobalFlag.ts +36 -114
- package/src/unstable/cli/internal/command.ts +5 -0
- package/src/unstable/cli/internal/help.ts +103 -22
- package/src/unstable/httpapi/HttpApiBuilder.ts +68 -13
- package/src/unstable/httpapi/HttpApiEndpoint.ts +13 -4
- package/src/unstable/reactivity/AtomHttpApi.ts +2 -2
- package/src/unstable/reactivity/AtomRegistry.ts +2 -6
|
@@ -6,7 +6,7 @@ import * as Console from "../../Console.ts"
|
|
|
6
6
|
import * as Effect from "../../Effect.ts"
|
|
7
7
|
import type * as FileSystem from "../../FileSystem.ts"
|
|
8
8
|
import { dual } from "../../Function.ts"
|
|
9
|
-
import * as Layer from "../../Layer.ts"
|
|
9
|
+
import type * as Layer from "../../Layer.ts"
|
|
10
10
|
import * as Option from "../../Option.ts"
|
|
11
11
|
import type * as Path from "../../Path.ts"
|
|
12
12
|
import type { Pipeable } from "../../Pipeable.ts"
|
|
@@ -22,7 +22,7 @@ import * as CliOutput from "./CliOutput.ts"
|
|
|
22
22
|
import * as GlobalFlag from "./GlobalFlag.ts"
|
|
23
23
|
import { checkForDuplicateFlags, makeCommand, toImpl, TypeId } from "./internal/command.ts"
|
|
24
24
|
import { parseConfig } from "./internal/config.ts"
|
|
25
|
-
import { getHelpForCommandPath } from "./internal/help.ts"
|
|
25
|
+
import { getGlobalFlagsForCommandPath, getGlobalFlagsForCommandTree, getHelpForCommandPath } from "./internal/help.ts"
|
|
26
26
|
import * as Lexer from "./internal/lexer.ts"
|
|
27
27
|
import * as Parser from "./internal/parser.ts"
|
|
28
28
|
import * as Param from "./Param.ts"
|
|
@@ -588,7 +588,7 @@ export const make: {
|
|
|
588
588
|
name: Name,
|
|
589
589
|
config: Config,
|
|
590
590
|
handler: (config: Command.Config.Infer<Config>) => Effect.Effect<void, E, R>
|
|
591
|
-
): Command<Name, Command.Config.Infer<Config>, E, R
|
|
591
|
+
): Command<Name, Command.Config.Infer<Config>, E, Exclude<R, GlobalFlag.BuiltInSettingContext>>
|
|
592
592
|
} = ((
|
|
593
593
|
name: string,
|
|
594
594
|
config?: Command.Config,
|
|
@@ -661,7 +661,7 @@ export const withHandler: {
|
|
|
661
661
|
*/
|
|
662
662
|
<A, R, E>(handler: (value: A) => Effect.Effect<void, E, R>): <Name extends string, XR, XE>(
|
|
663
663
|
self: Command<Name, A, XE, XR>
|
|
664
|
-
) => Command<Name, A, E, R
|
|
664
|
+
) => Command<Name, A, E, Exclude<R, GlobalFlag.BuiltInSettingContext>>
|
|
665
665
|
/* ========================================================================== */
|
|
666
666
|
/* Combinators */
|
|
667
667
|
/* ========================================================================== */
|
|
@@ -693,11 +693,12 @@ export const withHandler: {
|
|
|
693
693
|
<Name extends string, A, XR, XE, R, E>(
|
|
694
694
|
self: Command<Name, A, XE, XR>,
|
|
695
695
|
handler: (value: A) => Effect.Effect<void, E, R>
|
|
696
|
-
): Command<Name, A, E, R
|
|
696
|
+
): Command<Name, A, E, Exclude<R, GlobalFlag.BuiltInSettingContext>>
|
|
697
697
|
} = dual(2, <Name extends string, A, XR, XE, R, E>(
|
|
698
698
|
self: Command<Name, A, XE, XR>,
|
|
699
699
|
handler: (value: A) => Effect.Effect<void, E, R>
|
|
700
|
-
): Command<Name, A, E, R
|
|
700
|
+
): Command<Name, A, E, Exclude<R, GlobalFlag.BuiltInSettingContext>> =>
|
|
701
|
+
makeCommand({ ...toImpl(self), handle: handler }))
|
|
701
702
|
|
|
702
703
|
interface SubcommandGroupInternal {
|
|
703
704
|
readonly group: string | undefined
|
|
@@ -942,6 +943,7 @@ export const withSubcommands: {
|
|
|
942
943
|
shortDescription: impl.shortDescription,
|
|
943
944
|
alias: impl.alias,
|
|
944
945
|
annotations: impl.annotations,
|
|
946
|
+
globalFlags: impl.globalFlags,
|
|
945
947
|
examples: impl.examples,
|
|
946
948
|
service: impl.service,
|
|
947
949
|
subcommands: normalized.groups,
|
|
@@ -950,7 +952,50 @@ export const withSubcommands: {
|
|
|
950
952
|
})
|
|
951
953
|
})
|
|
952
954
|
|
|
955
|
+
/**
|
|
956
|
+
* Declares global flags for a command scope.
|
|
957
|
+
*
|
|
958
|
+
* Declared global flags apply to the command and all of its descendants.
|
|
959
|
+
*
|
|
960
|
+
* @since 4.0.0
|
|
961
|
+
* @category combinators
|
|
962
|
+
*/
|
|
963
|
+
export const withGlobalFlags: {
|
|
964
|
+
/**
|
|
965
|
+
* Declares global flags for a command scope.
|
|
966
|
+
*
|
|
967
|
+
* Declared global flags apply to the command and all of its descendants.
|
|
968
|
+
*
|
|
969
|
+
* @since 4.0.0
|
|
970
|
+
* @category combinators
|
|
971
|
+
*/
|
|
972
|
+
<const GlobalFlags extends ReadonlyArray<GlobalFlag.GlobalFlag<any>>>(globalFlags: GlobalFlags): <Name extends string, Input, E, R>(
|
|
973
|
+
self: Command<Name, Input, E, R>
|
|
974
|
+
) => Command<Name, Input, E, Exclude<R, ExtractGlobalFlagContext<GlobalFlags>>>
|
|
975
|
+
/**
|
|
976
|
+
* Declares global flags for a command scope.
|
|
977
|
+
*
|
|
978
|
+
* Declared global flags apply to the command and all of its descendants.
|
|
979
|
+
*
|
|
980
|
+
* @since 4.0.0
|
|
981
|
+
* @category combinators
|
|
982
|
+
*/
|
|
983
|
+
<Name extends string, Input, E, R, const GlobalFlags extends ReadonlyArray<GlobalFlag.GlobalFlag<any>>>(self: Command<Name, Input, E, R>, globalFlags: GlobalFlags): Command<Name, Input, E, Exclude<R, ExtractGlobalFlagContext<GlobalFlags>>>
|
|
984
|
+
} = dual(
|
|
985
|
+
2,
|
|
986
|
+
<Name extends string, Input, E, R, const GlobalFlags extends ReadonlyArray<GlobalFlag.GlobalFlag<any>>>(
|
|
987
|
+
self: Command<Name, Input, E, R>,
|
|
988
|
+
globalFlags: GlobalFlags
|
|
989
|
+
): Command<Name, Input, E, Exclude<R, ExtractGlobalFlagContext<GlobalFlags>>> => {
|
|
990
|
+
const impl = toImpl(self)
|
|
991
|
+
const next = Array.from(new Set([...impl.globalFlags, ...globalFlags]))
|
|
992
|
+
return makeCommand({ ...impl, globalFlags: next })
|
|
993
|
+
}
|
|
994
|
+
)
|
|
995
|
+
|
|
953
996
|
// Type extractors for subcommand arrays - T[number] gives union of all elements
|
|
997
|
+
type ExtractGlobalFlagContext<T extends ReadonlyArray<GlobalFlag.GlobalFlag<any>>> = T[number] extends
|
|
998
|
+
GlobalFlag.Setting<infer Id, any> ? GlobalFlag.Setting.Identifier<Id> : never
|
|
954
999
|
type ExtractSubcommand<T> = T extends Command<any, any, any, any> ? T
|
|
955
1000
|
: T extends Command.SubcommandGroup<infer Commands> ? Commands[number]
|
|
956
1001
|
: never
|
|
@@ -1534,6 +1579,45 @@ export const provideEffectDiscard: {
|
|
|
1534
1579
|
/* Execution */
|
|
1535
1580
|
/* ========================================================================== */
|
|
1536
1581
|
|
|
1582
|
+
const getOutOfScopeGlobalFlagErrors = (
|
|
1583
|
+
allFlags: ReadonlyArray<GlobalFlag.GlobalFlag<any>>,
|
|
1584
|
+
activeFlags: ReadonlyArray<GlobalFlag.GlobalFlag<any>>,
|
|
1585
|
+
flagMap: Record<string, ReadonlyArray<string>>,
|
|
1586
|
+
commandPath: ReadonlyArray<string>
|
|
1587
|
+
): ReadonlyArray<CliError.CliError> => {
|
|
1588
|
+
const activeSet = new Set(activeFlags)
|
|
1589
|
+
const errors: Array<CliError.CliError> = []
|
|
1590
|
+
const seen = new Set<string>()
|
|
1591
|
+
|
|
1592
|
+
for (const flag of allFlags) {
|
|
1593
|
+
if (activeSet.has(flag)) {
|
|
1594
|
+
continue
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
const singles = Param.extractSingleParams(flag.flag)
|
|
1598
|
+
for (const single of singles) {
|
|
1599
|
+
const entries = flagMap[single.name]
|
|
1600
|
+
if (!entries || entries.length === 0) {
|
|
1601
|
+
continue
|
|
1602
|
+
}
|
|
1603
|
+
const option = `--${single.name}`
|
|
1604
|
+
if (seen.has(option)) {
|
|
1605
|
+
continue
|
|
1606
|
+
}
|
|
1607
|
+
seen.add(option)
|
|
1608
|
+
errors.push(
|
|
1609
|
+
new CliError.UnrecognizedOption({
|
|
1610
|
+
option,
|
|
1611
|
+
suggestions: [],
|
|
1612
|
+
command: commandPath
|
|
1613
|
+
})
|
|
1614
|
+
)
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
return errors
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1537
1621
|
const showHelp = <Name extends string, Input, E, R>(
|
|
1538
1622
|
command: Command<Name, Input, E, R>,
|
|
1539
1623
|
commandPath: ReadonlyArray<string>,
|
|
@@ -1541,7 +1625,7 @@ const showHelp = <Name extends string, Input, E, R>(
|
|
|
1541
1625
|
): Effect.Effect<void, never, Environment> =>
|
|
1542
1626
|
Effect.gen(function*() {
|
|
1543
1627
|
const formatter = yield* CliOutput.Formatter
|
|
1544
|
-
const helpDoc = yield* getHelpForCommandPath(command, commandPath, GlobalFlag.
|
|
1628
|
+
const helpDoc = yield* getHelpForCommandPath(command, commandPath, GlobalFlag.BuiltIns)
|
|
1545
1629
|
yield* Console.log(formatter.formatHelpDoc(helpDoc))
|
|
1546
1630
|
if (errors && errors.length > 0) {
|
|
1547
1631
|
yield* Console.error(formatter.formatErrors(errors))
|
|
@@ -1698,11 +1782,11 @@ export const runWith = <const Name extends string, Input, E, R>(
|
|
|
1698
1782
|
function*(args: ReadonlyArray<string>) {
|
|
1699
1783
|
const { tokens, trailingOperands } = Lexer.lex(args)
|
|
1700
1784
|
|
|
1701
|
-
// 1.
|
|
1702
|
-
const
|
|
1785
|
+
// 1. Collect known global flags from the command tree
|
|
1786
|
+
const allFlags = getGlobalFlagsForCommandTree(command, GlobalFlag.BuiltIns)
|
|
1703
1787
|
|
|
1704
1788
|
// 2. Extract global flag tokens
|
|
1705
|
-
const allFlagParams =
|
|
1789
|
+
const allFlagParams = allFlags.flatMap((f) => Param.extractSingleParams(f.flag))
|
|
1706
1790
|
const globalRegistry = Parser.createFlagRegistry(allFlagParams.filter(Param.isFlagParam))
|
|
1707
1791
|
const { flagMap, remainder } = Parser.consumeKnownFlags(tokens, globalRegistry)
|
|
1708
1792
|
const emptyArgs: Param.ParsedArgs = { flags: flagMap, arguments: [] }
|
|
@@ -1711,9 +1795,17 @@ export const runWith = <const Name extends string, Input, E, R>(
|
|
|
1711
1795
|
const parsedArgs = yield* Parser.parseArgs({ tokens: remainder, trailingOperands }, command)
|
|
1712
1796
|
const commandPath = [command.name, ...Parser.getCommandPath(parsedArgs)] as const
|
|
1713
1797
|
const handlerCtx: GlobalFlag.HandlerContext = { command, commandPath, version: config.version }
|
|
1798
|
+
const activeFlags = getGlobalFlagsForCommandPath(command, commandPath, GlobalFlag.BuiltIns)
|
|
1714
1799
|
|
|
1715
|
-
// 4.
|
|
1716
|
-
|
|
1800
|
+
// 4. Reject globals that were passed outside the active command scope
|
|
1801
|
+
const outOfScopeErrors = getOutOfScopeGlobalFlagErrors(allFlags, activeFlags, flagMap, commandPath)
|
|
1802
|
+
if (outOfScopeErrors.length > 0) {
|
|
1803
|
+
const parseErrors = parsedArgs.errors ?? []
|
|
1804
|
+
return yield* showHelp(command, commandPath, [...outOfScopeErrors, ...parseErrors])
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
// 5. Process action flags — first present action wins, then exit
|
|
1808
|
+
for (const flag of activeFlags) {
|
|
1717
1809
|
if (flag._tag !== "Action") continue
|
|
1718
1810
|
const singles = Param.extractSingleParams(flag.flag)
|
|
1719
1811
|
const hasEntry = singles.some((s) => {
|
|
@@ -1726,7 +1818,7 @@ export const runWith = <const Name extends string, Input, E, R>(
|
|
|
1726
1818
|
return
|
|
1727
1819
|
}
|
|
1728
1820
|
|
|
1729
|
-
//
|
|
1821
|
+
// 6. Handle parsing errors
|
|
1730
1822
|
if (parsedArgs.errors && parsedArgs.errors.length > 0) {
|
|
1731
1823
|
return yield* showHelp(command, commandPath, parsedArgs.errors)
|
|
1732
1824
|
}
|
|
@@ -1735,29 +1827,25 @@ export const runWith = <const Name extends string, Input, E, R>(
|
|
|
1735
1827
|
return yield* showHelp(command, commandPath, [parseResult.failure])
|
|
1736
1828
|
}
|
|
1737
1829
|
|
|
1738
|
-
//
|
|
1739
|
-
let
|
|
1740
|
-
for (const flag of
|
|
1830
|
+
// 7. Provide setting values
|
|
1831
|
+
let program = commandImpl.handle(parseResult.success, [command.name])
|
|
1832
|
+
for (const flag of activeFlags) {
|
|
1741
1833
|
if (flag._tag !== "Setting") continue
|
|
1742
1834
|
const [, value] = yield* flag.flag.parse(emptyArgs)
|
|
1743
|
-
|
|
1835
|
+
program = Effect.provideService(program, flag, value)
|
|
1744
1836
|
}
|
|
1745
1837
|
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
})
|
|
1755
|
-
)
|
|
1756
|
-
}
|
|
1838
|
+
const [, logLevel] = yield* GlobalFlag.LogLevel.flag.parse(emptyArgs)
|
|
1839
|
+
program = Effect.provideService(program, GlobalFlag.LogLevel, logLevel)
|
|
1840
|
+
|
|
1841
|
+
// 8. Apply built-in setting behavior
|
|
1842
|
+
const services = Option.match(logLevel, {
|
|
1843
|
+
onNone: () => ServiceMap.empty(),
|
|
1844
|
+
onSome: (level) => ServiceMap.make(References.MinimumLogLevel, level)
|
|
1845
|
+
})
|
|
1757
1846
|
|
|
1758
|
-
//
|
|
1759
|
-
|
|
1760
|
-
yield* Effect.provide(program, contextLayer)
|
|
1847
|
+
// 9. Run command handler with composed context
|
|
1848
|
+
yield* Effect.provideServices(program, services)
|
|
1761
1849
|
},
|
|
1762
1850
|
Effect.catchIf(
|
|
1763
1851
|
((error: any) =>
|
|
@@ -4,7 +4,6 @@
|
|
|
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
7
|
import type { LogLevel as LogLevelType } from "../../LogLevel.ts"
|
|
9
8
|
import * as Option from "../../Option.ts"
|
|
10
9
|
import * as ServiceMap from "../../ServiceMap.ts"
|
|
@@ -49,18 +48,30 @@ export interface Action<A> {
|
|
|
49
48
|
* @since 4.0.0
|
|
50
49
|
* @category models
|
|
51
50
|
*/
|
|
52
|
-
export interface Setting<A> extends ServiceMap.
|
|
51
|
+
export interface Setting<Id extends string, A> extends ServiceMap.Service<Setting.Identifier<Id>, A> {
|
|
53
52
|
readonly _tag: "Setting"
|
|
53
|
+
readonly id: Id
|
|
54
54
|
readonly flag: Flag.Flag<A>
|
|
55
55
|
}
|
|
56
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}`
|
|
66
|
+
}
|
|
67
|
+
|
|
57
68
|
/**
|
|
58
69
|
* Global flag discriminated union.
|
|
59
70
|
*
|
|
60
71
|
* @since 4.0.0
|
|
61
72
|
* @category models
|
|
62
73
|
*/
|
|
63
|
-
export type GlobalFlag<A> = Action<A> | Setting<A>
|
|
74
|
+
export type GlobalFlag<A> = Action<A> | Setting<any, A>
|
|
64
75
|
|
|
65
76
|
/* ========================================================================== */
|
|
66
77
|
/* Constructors */
|
|
@@ -90,17 +101,19 @@ export const action = <A>(options: {
|
|
|
90
101
|
* @since 4.0.0
|
|
91
102
|
* @category constructors
|
|
92
103
|
*/
|
|
93
|
-
export const setting = <
|
|
104
|
+
export const setting = <const Id extends string>(
|
|
105
|
+
id: Id
|
|
106
|
+
) =>
|
|
107
|
+
<A>(options: {
|
|
94
108
|
readonly flag: Flag.Flag<A>
|
|
95
|
-
|
|
96
|
-
}): Setting<A> => {
|
|
109
|
+
}): Setting<Id, A> => {
|
|
97
110
|
settingIdCounter += 1
|
|
98
|
-
const ref = ServiceMap.
|
|
99
|
-
`effect/cli/GlobalFlag
|
|
100
|
-
{ defaultValue: options.defaultValue }
|
|
111
|
+
const ref = ServiceMap.Service<Setting.Identifier<Id>, A>(
|
|
112
|
+
`effect/unstable/cli/GlobalFlag/${id}/${settingIdCounter}`
|
|
101
113
|
)
|
|
102
114
|
return Object.assign(ref, {
|
|
103
115
|
_tag: "Setting" as const,
|
|
116
|
+
id,
|
|
104
117
|
flag: options.flag
|
|
105
118
|
})
|
|
106
119
|
}
|
|
@@ -130,7 +143,7 @@ export const Help: Action<boolean> = action({
|
|
|
130
143
|
run: (_, { command, commandPath }) =>
|
|
131
144
|
Effect.gen(function*() {
|
|
132
145
|
const formatter = yield* CliOutput.Formatter
|
|
133
|
-
const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath,
|
|
146
|
+
const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath, BuiltIns)
|
|
134
147
|
yield* Console.log(formatter.formatHelpDoc(helpDoc))
|
|
135
148
|
})
|
|
136
149
|
})
|
|
@@ -184,7 +197,7 @@ export const Completions: Action<Option.Option<"bash" | "zsh" | "fish">> = actio
|
|
|
184
197
|
* @since 4.0.0
|
|
185
198
|
* @category references
|
|
186
199
|
*/
|
|
187
|
-
export const LogLevel: Setting<Option.Option<LogLevelType>> = setting({
|
|
200
|
+
export const LogLevel: Setting<"log-level", Option.Option<LogLevelType>> = setting("log-level")({
|
|
188
201
|
flag: Flag.choiceWithValue(
|
|
189
202
|
"log-level",
|
|
190
203
|
[
|
|
@@ -201,121 +214,30 @@ export const LogLevel: Setting<Option.Option<LogLevelType>> = setting({
|
|
|
201
214
|
).pipe(
|
|
202
215
|
Flag.optional,
|
|
203
216
|
Flag.withDescription("Sets the minimum log level")
|
|
204
|
-
)
|
|
205
|
-
defaultValue: () => Option.none()
|
|
217
|
+
)
|
|
206
218
|
})
|
|
207
219
|
|
|
208
220
|
/* ========================================================================== */
|
|
209
|
-
/*
|
|
221
|
+
/* References */
|
|
210
222
|
/* ========================================================================== */
|
|
211
223
|
|
|
212
224
|
/**
|
|
213
|
-
*
|
|
214
|
-
* The parser iterates this set to know which flags to extract.
|
|
225
|
+
* Built-in global flags in default precedence order.
|
|
215
226
|
*
|
|
216
227
|
* @since 4.0.0
|
|
217
228
|
* @category references
|
|
218
229
|
*/
|
|
219
|
-
export const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Version,
|
|
226
|
-
Completions,
|
|
227
|
-
LogLevel
|
|
228
|
-
])
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
/* ========================================================================== */
|
|
232
|
-
/* Public API */
|
|
233
|
-
/* ========================================================================== */
|
|
230
|
+
export const BuiltIns: ReadonlyArray<GlobalFlag<any>> = [
|
|
231
|
+
Help,
|
|
232
|
+
Version,
|
|
233
|
+
Completions,
|
|
234
|
+
LogLevel
|
|
235
|
+
]
|
|
234
236
|
|
|
235
237
|
/**
|
|
236
|
-
*
|
|
238
|
+
* Built-in setting context identifiers.
|
|
237
239
|
*
|
|
238
240
|
* @since 4.0.0
|
|
239
|
-
* @category
|
|
240
|
-
*/
|
|
241
|
-
export const add: {
|
|
242
|
-
/* ========================================================================== */
|
|
243
|
-
/* Public API */
|
|
244
|
-
/* ========================================================================== */
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Adds a global flag to the registry.
|
|
248
|
-
*
|
|
249
|
-
* @since 4.0.0
|
|
250
|
-
* @category modifiers
|
|
251
|
-
*/
|
|
252
|
-
<A>(flag: GlobalFlag<A>): <B, E, R>(
|
|
253
|
-
self: Effect.Effect<B, E, R>
|
|
254
|
-
) => Effect.Effect<B, E, R>
|
|
255
|
-
/* ========================================================================== */
|
|
256
|
-
/* Public API */
|
|
257
|
-
/* ========================================================================== */
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Adds a global flag to the registry.
|
|
261
|
-
*
|
|
262
|
-
* @since 4.0.0
|
|
263
|
-
* @category modifiers
|
|
264
|
-
*/
|
|
265
|
-
<B, E, R, A>(self: Effect.Effect<B, E, R>, flag: GlobalFlag<A>): Effect.Effect<B, E, R>
|
|
266
|
-
} = dual(
|
|
267
|
-
2,
|
|
268
|
-
Effect.fnUntraced(function*<B, E, R, A>(
|
|
269
|
-
self: Effect.Effect<B, E, R>,
|
|
270
|
-
flag: GlobalFlag<A>
|
|
271
|
-
) {
|
|
272
|
-
const currentRegistry = yield* Registry
|
|
273
|
-
const nextRegistry = new Set([...currentRegistry, flag])
|
|
274
|
-
return yield* Effect.provideService(self, Registry, nextRegistry)
|
|
275
|
-
})
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Removes a global flag by its reference.
|
|
280
|
-
*
|
|
281
|
-
* @since 4.0.0
|
|
282
|
-
* @category modifiers
|
|
283
|
-
*/
|
|
284
|
-
export const remove: {
|
|
285
|
-
/**
|
|
286
|
-
* Removes a global flag by its reference.
|
|
287
|
-
*
|
|
288
|
-
* @since 4.0.0
|
|
289
|
-
* @category modifiers
|
|
290
|
-
*/
|
|
291
|
-
<A>(flag: GlobalFlag<A>): <B, E, R>(
|
|
292
|
-
self: Effect.Effect<B, E, R>
|
|
293
|
-
) => Effect.Effect<B, E, R>
|
|
294
|
-
/**
|
|
295
|
-
* Removes a global flag by its reference.
|
|
296
|
-
*
|
|
297
|
-
* @since 4.0.0
|
|
298
|
-
* @category modifiers
|
|
299
|
-
*/
|
|
300
|
-
<B, E, R, A>(self: Effect.Effect<B, E, R>, flag: GlobalFlag<A>): Effect.Effect<B, E, R>
|
|
301
|
-
} = dual(
|
|
302
|
-
2,
|
|
303
|
-
Effect.fnUntraced(function*<B, E, R, A>(
|
|
304
|
-
self: Effect.Effect<B, E, R>,
|
|
305
|
-
flag: GlobalFlag<A>
|
|
306
|
-
) {
|
|
307
|
-
const currentRegistry = yield* Registry
|
|
308
|
-
const nextRegistry = new Set(currentRegistry)
|
|
309
|
-
nextRegistry.delete(flag)
|
|
310
|
-
return yield* Effect.provideService(self, Registry, nextRegistry)
|
|
311
|
-
})
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Removes all global flags (built-in and user-defined).
|
|
316
|
-
*
|
|
317
|
-
* @since 4.0.0
|
|
318
|
-
* @category modifiers
|
|
241
|
+
* @category models
|
|
319
242
|
*/
|
|
320
|
-
export
|
|
321
|
-
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,46 +6,127 @@
|
|
|
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
|
|
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
|
-
*
|
|
19
|
-
*
|
|
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
|
|
35
|
+
export const getCommandsForCommandPath = <Name extends string, Input, E, R>(
|
|
23
36
|
command: Command<Name, Input, E, R>,
|
|
24
|
-
commandPath: ReadonlyArray<string
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
for (let i = 1; i < commandPath.length; i++) {
|
|
43
|
+
const subcommandName = commandPath[i]
|
|
44
|
+
let subcommand: Command.Any | undefined = undefined
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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 flags =
|
|
129
|
+
const flags = getGlobalFlagsForCommandPath(command, commandPath, builtIns)
|
|
49
130
|
const globalFlagDocs: Array<FlagDoc> = []
|
|
50
131
|
for (const flag of flags) {
|
|
51
132
|
const singles = Param.extractSingleParams(flag.flag)
|