@crustjs/core 0.4.1 → 0.5.1

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.
@@ -223,7 +223,7 @@ type ValidateContextNames<Ctx extends ContextMap, Cs extends readonly AnyContext
223
223
  type InstanceNames<P extends readonly unknown[]> = P extends readonly [infer H, ...infer T extends readonly unknown[]] ? DefName<H> | InstanceNames<T> : never;
224
224
  /** Statically known names of an Extension's provided Contexts; widened Extensions opt out. */
225
225
  type ExtensionProvidedNames<E> = DefiningOf<E> extends {
226
- readonly provides?: infer P extends readonly unknown[];
226
+ readonly provide?: infer P extends readonly unknown[];
227
227
  } ? InstanceNames<P> : never;
228
228
  type ExtensionContextBrand<E, Existing extends string> = CollisionBrand<ExtensionProvidedNames<E>, Existing, "FIX_DUPLICATE_CONTEXT", "Extension-provided Context ", " is already provided on this command path">;
229
229
  type ValidateExtensionProvidesWorker<Es extends readonly unknown[], Existing extends string> = Es extends readonly [infer H, ...infer T extends readonly unknown[]] ? readonly [H & ExtensionContextBrand<H, Existing>, ...ValidateExtensionProvidesWorker<T, Existing | ExtensionProvidedNames<H>>] : Es;
@@ -246,9 +246,10 @@ type MismatchedDependencyBrand<C, KnownValues> = MismatchedDependencyNames<Provi
246
246
  type ValidateContextDeps<Ctx extends ContextMap, Cs extends readonly AnyContextInstance[], Known extends string = (keyof Ctx & string) | DefName<Cs[number]>, KnownValues extends ContextMap = Ctx & ContextsOutput<Cs>> = { [I in keyof Cs]: Cs[I] & MissingDependencyBrand<Cs[I], Known> & MismatchedDependencyBrand<Cs[I], KnownValues>; };
247
247
  /** Dependency closure carried by command definitions and Extensions. */
248
248
  type IsAny<T> = 0 extends 1 & T ? true : false;
249
- type DeclaredDepsOf<T> = IsAny<T> extends true ? Record<string, ContextValue> : CommandDefinitionData<DefiningOf<T>> extends {
249
+ type DeclaredDepsOfMember<T> = IsAny<T> extends true ? Record<string, ContextValue> : CommandDefinitionData<DefiningOf<T>> extends {
250
250
  readonly _deps?: infer D extends ContextMap;
251
251
  } ? IsAny<D> extends true ? Record<string, ContextValue> : D : {};
252
+ type DeclaredDepsOf<T> = (T extends unknown ? (deps: DeclaredDepsOfMember<T>) => void : never) extends ((deps: infer Deps extends ContextMap) => void) ? Deps : {};
252
253
  /** Missing-dependency brand shared by `ValidateDeclaredDeps` and inline `.command()`. */
253
254
  type MissingDeclaredDependencyBrand<T, Known extends string> = string extends keyof DeclaredDepsOf<T> ? {} : Exclude<keyof DeclaredDepsOf<T> & string, Known> extends (infer Missing extends string) ? [Missing] extends [never] ? {} : {
254
255
  readonly FIX_MISSING_DEPENDENCY: `Uses Context "${Missing}" which is not provided`;
@@ -341,7 +342,7 @@ type ExtensionFlagDefsOf<E> = [E] extends [never] ? readonly [] : DefiningOf<E>
341
342
  type ProvidedContextSpellings<P extends readonly unknown[]> = P extends readonly [infer H, ...infer T extends readonly unknown[]] ? LocalSpellingsOf<ContextOwnedFlags<H>> | ProvidedContextSpellings<T> : [P[number]] extends [never] ? never : LocalSpellingsOf<ContextOwnedFlags<P[number]>>;
342
343
  /** All statically known flag spellings an Extension contributes: declared flags plus provided Context-owned flags. */
343
344
  type ExtensionSpellings<E> = AttachedSpellings<ExtensionFlagDefsOf<E>> | ([E] extends [never] ? never : DefiningOf<E> extends {
344
- readonly provides?: infer P extends readonly unknown[];
345
+ readonly provide?: infer P extends readonly unknown[];
345
346
  } ? ProvidedContextSpellings<P> : never);
346
347
  type ExtensionFlagCollisionBrand<E, Existing extends string> = CollisionBrand<ExtensionSpellings<E>, Existing, "FIX_ALIAS_COLLISION", "Extension flag spelling ", " collides with an existing flag">;
347
348
  /**
@@ -578,12 +579,13 @@ type RunOutcome<Result> = {
578
579
  type CommandTree = Record<string, CommandShape>;
579
580
  /** Every valid path through a command tree, including the root path (`[]`). */
580
581
  type CommandPath<Tree extends object, Depth extends readonly unknown[] = readonly []> = Depth["length"] extends 15 ? readonly string[] : string extends keyof Tree ? readonly string[] : readonly [] | { [K in keyof Tree & string]: Tree[K] extends CommandShape ? readonly [K, ...CommandPath<Tree[K]["children"], readonly [...Depth, unknown]>] : never; }[keyof Tree & string];
581
- type KnownCommandPath<Path extends readonly string[], Tree> = string extends keyof Tree ? Path : IsStaticTuple<Path> extends true ? string extends Path[number] ? never : Path : never;
582
+ type CommandPathContinuations<Path extends readonly string[], Tree extends object, Prefix extends readonly string[] = readonly []> = Path extends readonly [infer Head extends string, ...infer Tail extends readonly string[]] ? readonly [...Prefix, Head] extends CommandPath<Tree> ? CommandPathContinuations<Tail, Tree, readonly [...Prefix, Head]> : Extract<CommandPath<Tree>, readonly [...Prefix, ...string[]]> : Extract<CommandPath<Tree>, readonly [...Prefix, ...string[]]>;
583
+ type KnownCommandPath<Path extends readonly string[], Tree extends object> = string extends keyof Tree ? Path : IsStaticTuple<Path> extends true ? string extends Path[number] ? never : [Path] extends [CommandPath<Tree>] ? Path : CommandPathContinuations<Path, Tree> : number extends Path["length"] ? CommandPath<Tree> : never;
582
584
  /** Resolve the command shape at a typed path. */
583
585
  type CommandShapeAt<Shape extends CommandShape, Path extends readonly string[]> = Path extends readonly [infer Head, ...infer Tail extends readonly string[]] ? Head extends keyof Shape["children"] ? Shape["children"][Head] extends (infer Child extends CommandShape) ? CommandShapeAt<Child, Tail> : never : string extends Head ? CommandShape : never : Path extends readonly [] ? Shape : CommandShape;
584
- type RunSection<Name extends string, Values> = keyof Values extends never ? { [K in Name]?: never; } : {} extends Values ? { [K in Name]?: Values; } : { [K in Name]: Values; };
586
+ type RunSection<Name extends string, Values> = keyof Values extends never ? {} : {} extends Values ? { [K in Name]?: Values; } : { [K in Name]: Values; };
585
587
  /** Structured values bound directly against the selected command's definitions; no argv is produced. */
586
- type RunInput<Shape extends CommandShape> = RunSection<"args", ArgsDef extends Shape["args"] ? NonNullable<RunInputPayload["args"]> : InputArgs<Shape["args"]>> & RunSection<"flags", FlagsDef extends Shape["flags"] ? NonNullable<RunInputPayload["flags"]> : InputFlags<Shape["flags"]>> & {
588
+ type RunInput<Shape extends CommandShape> = RunSection<"args", ArgsDef extends Shape["args"] ? NonNullable<RunInputPayload["args"]> : InputArgs<Shape["args"]>> & RunSection<"flags", FlagsDef extends Shape["flags"] ? [keyof Shape["flags"]] extends [never] ? {} : NonNullable<RunInputPayload["flags"]> : InputFlags<Shape["flags"]>> & {
587
589
  readonly raw?: readonly string[];
588
590
  };
589
591
  type CompatibleRunValue<Expected, Actual> = Actual extends Expected ? Actual extends object ? Expected extends unknown ? Actual extends Expected ? Actual & { [K in Exclude<keyof Actual, keyof Expected>]: never; } & { [K in keyof Actual & keyof Expected]: CompatibleRunValue<Expected[K], Actual[K]>; } : never : never : Actual : JsonValue extends Expected ? Actual extends JsonCompatible<Actual> ? Actual : never : Expected extends unknown ? CompatibleRunBranch<Expected, Actual> : never;
@@ -591,6 +593,14 @@ type CompatibleRunBranch<Expected, Actual> = Actual extends Expected ? Actual :
591
593
  type CompatibleRunInput<Shape extends CommandShape, Input> = CompatibleRunValue<RunInput<Shape>, Input>;
592
594
  type RunInputArguments<Shape extends CommandShape> = {} extends RunInput<Shape> ? readonly [input?: RunInput<Shape>] : readonly [input: RunInput<Shape>];
593
595
  type RunArguments<Shape extends CommandShape> = readonly [...RunInputArguments<Shape>, options?: InvocationOptions];
596
+ type KeysOf<T> = T extends unknown ? keyof T : never;
597
+ type UnknownRunKeys<Actual, Known> = { [K in keyof Actual as string extends K ? never : number extends K ? never : K extends Known ? never : K]: Actual[K] extends undefined ? Actual[K] : never; };
598
+ type ClosedRunInput<Shape extends CommandShape, Input> = Input extends unknown ? UnknownRunKeys<Input, keyof RunInput<Shape>> & { [K in keyof Input as K extends ("args" | "flags") & keyof RunInput<Shape> ? K : never]: UnknownRunKeys<NonNullable<Input[K]>, KeysOf<NonNullable<RunInput<Shape>[K & keyof RunInput<Shape>]>>>; } : never;
599
+ type CheckedRunInput<Shape extends CommandShape, Input> = [Input] extends [ClosedRunInput<Shape, Input>] ? NonNullable<RunInputArguments<Shape>[0]> : Input & NoInfer<ClosedRunInput<Shape, Input>>;
600
+ type OmittableRunInput<Shape extends CommandShape> = {} extends RunInput<Shape> ? undefined : never;
601
+ type RunWithoutInputThis<Shape extends CommandShape, This> = [Shape] extends [never] ? This : {} extends RunInput<Shape> ? This : This & {
602
+ readonly FIX_MISSING_INPUT: "Pass the command's required arguments or flags";
603
+ };
594
604
  /**
595
605
  * Typed invoker bound to one command in an app, returned by {@link Crust.at}.
596
606
  *
@@ -600,7 +610,8 @@ type RunArguments<Shape extends CommandShape> = readonly [...RunInputArguments<S
600
610
  interface CommandHandle<Shape extends CommandShape> {
601
611
  /** The typed path this handle was created with (`[]` selects the root). */
602
612
  readonly path: readonly string[];
603
- run(...args: RunArguments<Shape>): Promise<RunOutcome<Shape["result"]>>;
613
+ run(this: RunWithoutInputThis<Shape, unknown>): Promise<RunOutcome<Shape["result"]>>;
614
+ run<const Input extends RunInput<Shape> | OmittableRunInput<Shape> = RunInput<Shape>>(input: CheckedRunInput<Shape, Input> | OmittableRunInput<Shape>, options?: InvocationOptions): Promise<RunOutcome<Shape["result"]>>;
604
615
  run<const Input>(input: Input, ...validation: [Input] extends [CompatibleRunInput<Shape, Input>] ? readonly [options?: InvocationOptions] : readonly [invalidInput: never]): Promise<RunOutcome<Shape["result"]>>;
605
616
  }
606
617
  /** Static configuration for a reusable command definition. */
@@ -668,7 +679,7 @@ type KnownDefinitionTree<Ds extends readonly unknown[], CF extends FlagsDef> = I
668
679
  type ExtensionCheckAt<Checks, I> = I extends keyof Checks ? Checks[I] : never;
669
680
  type ExtensionCommands<Es extends readonly AnyExtension[]> = Es extends readonly [infer H, ...infer T extends readonly AnyExtension[]] ? readonly [...ExtensionCommandDefs<H>, ...ExtensionCommands<T>] : ExtensionCommandDefs<Es[number]>[number] extends never ? readonly [] : readonly CommandDefinition<any, any, any, any>[];
670
681
  type ExtensionProviders<E> = [E] extends [never] ? [] : DefiningOf<E> extends {
671
- provides?: infer P extends readonly AnyContextInstance[];
682
+ provide?: infer P extends readonly AnyContextInstance[];
672
683
  } ? P : [];
673
684
  type ExtensionOwnDefs<E> = [E] extends [never] ? [] : DefiningOf<E> extends {
674
685
  _flagDefs?: infer F extends readonly NamedFlagDef[];
@@ -763,9 +774,8 @@ type AfterAction<Flags extends FlagsDef, A extends ArgsDef, Ctx extends ContextM
763
774
  }, Awaited<R>, Meta, string, Caps>;
764
775
  type AfterExtend<Flags extends FlagsDef, A extends ArgsDef, Ctx extends ContextMap, Sibs extends string, Sp extends string, Tree extends object, CtxFlags extends FlagsDef, CollisionSp extends AnyCollisionSpellings, Result, Es extends readonly AnyExtension[], Meta extends RootCommandMeta | undefined, Caps extends "app" | "recipe"> = Crust<MergeFlags<Flags, ExtensionFlags<Es>>, A, MergeProviders<Ctx, ExtensionsProvidesOutput<Es>>, Sibs | ExtensionsCommandSpellings<Es>, Sp | ExtensionsSpellings<Es>, ExtendedTree<Tree, Es, RecursiveExtensionFlags<Es>, CtxFlags>, MergeFlags<CtxFlags, RecursiveExtensionFlags<Es>>, CollisionSpellings<CollisionSp["extension"] | ExtensionsSpellings<Es>, CollisionSp["tree"] | DefinitionTreeSpellings<ExtensionCommands<Es>>, CollisionSp["demands"] & ExtensionDemandValues<Es>, CollisionSp["pending"] | DefinitionTreeSpellings<ExtensionCommands<Es>>, RecipeDepsOf<CollisionSp>, MergeProviders<ProvidersOf<CollisionSp>, ExtensionsProvidesOutput<Es>>, ActionDepsOf<CollisionSp>>, Result, Meta, string, Caps>;
765
776
  type AfterAdd<Flags extends FlagsDef, A extends ArgsDef, Ctx extends ContextMap, Sibs extends string, Sp extends string, Tree extends object, CtxFlags extends FlagsDef, CollisionSp extends AnyCollisionSpellings, Result, Ds extends readonly CommandDefinition<any, any, any, any>[], Meta extends RootCommandMeta | undefined, Caps extends "app" | "recipe"> = Crust<Flags, A, Ctx, Sibs | AttachedCommandSpellings<Ds>, Sp, Tree & DefinitionsTree<Ds, CtxFlags>, CtxFlags, CollisionSpellings<CollisionSp["extension"], CollisionSp["tree"] | DefinitionTreeSpellings<Ds>, CollisionSp["demands"], CollisionSp["pending"], RecipeDepsOf<CollisionSp>, ProvidersOf<CollisionSp>, ActionDepsOf<CollisionSp>>, Result, Meta, string, Caps>;
766
- type ExtensionDemandValues<Es extends readonly AnyExtension[]> = UnionToIntersection<DefiningOf<Es[number]> extends {
767
- readonly _hookDeps?: infer H extends ContextMap;
768
- } ? H : Record<string, ContextValue>> extends (infer D extends ContextMap) ? D : {};
777
+ type ExtensionHookDemand<E> = E extends AnyExtension ? (deps: NonNullable<DefiningOf<E>["_hookDeps"]>) => void : never;
778
+ type ExtensionDemandValues<Es extends readonly AnyExtension[]> = ExtensionHookDemand<Es[number]> extends ((deps: infer D extends ContextMap) => void) ? D : {};
769
779
  type DescendantShapeValuesBrand<S, Deps> = S extends CommandShape ? DeclaredDependencyValuesBrand<Deps, NonNullable<S[typeof commandProviders]>> & DescendantValuesBrand<S["children"], Deps> : {};
770
780
  type DescendantValuesBrand<Tree, Deps> = keyof Deps extends never ? {} : UnionToIntersection<Tree extends unknown ? keyof Tree extends never ? {} : { [K in keyof Tree]: DescendantShapeValuesBrand<Tree[K], Deps>; }[keyof Tree] : never>;
771
781
  type DefinitionDescendantValuesBrand<Ds extends readonly CommandDefinition<any, any, any, any>[], Deps> = DescendantValuesBrand<DefinitionsTree<Ds>, Deps>;
@@ -851,6 +861,8 @@ declare class Crust<Flags extends FlagsDef = {}, A extends ArgsDef = [], Ctx ext
851
861
  * reverse construction order after post-run hooks. TypeScript rejects known Context-owned flag collisions, including pending
852
862
  * Extension commands. Consuming operations throw `DEFINITION` for actual collisions.
853
863
  *
864
+ * Context `sections` document only this command, never descendants. Preparation throws
865
+ * `DEFINITION` when one repeats a section title already on this command.
854
866
  */
855
867
  provide<const Cs extends readonly AnyContextInstance[] = never>(...instances: KnownContextInstances<Cs> & ProvideChecks<Sp | CollisionSp["pending"], Cs> & ValidateContextNames<Caps extends "recipe" ? ProvidersOf<CollisionSp> : Ctx, Cs> & ValidateContextDeps<Ctx, Cs> & DeclaredDependencyValuesBrand<CollisionSp["demands"] & RecipeDepsOf<CollisionSp> & ActionDepsOf<CollisionSp>, ContextsOutput<NoInfer<Cs>>>): AfterProvide<Flags, A, Ctx, Sibs, Sp, Tree, CtxFlags, CollisionSp, Result, Cs, Meta, Caps>;
856
868
  /**
@@ -897,21 +909,17 @@ declare class Crust<Flags extends FlagsDef = {}, A extends ArgsDef = [], Ctx ext
897
909
  */
898
910
  add<const Ds extends readonly CommandDefinition<any, any, any, any>[] = never>(...definitions: Ds & ValidateCommandDefinitions<Ds, Sibs> & ValidateDeclaredDeps<Ctx, Ds> & DefinitionDescendantValuesBrand<Ds, CollisionSp["demands"]> & ValidateDefinitionFlags<Ds, CollisionSp["extension"] | LocalSpellingsOf<CtxFlags>>): AfterAdd<Flags, A, Ctx, Sibs, Sp, Tree, CtxFlags, CollisionSp, Result, Ds, Meta, Caps>;
899
911
  /**
900
- * Define an app-local leaf subcommand inline (root-only sugar for
901
- * `.add(defineCommand(name, recipe))`).
912
+ * Define a subcommand inline (sugar for `.add(defineCommand(name, recipe))`),
913
+ * on the root or inside another command's recipe.
902
914
  *
903
915
  * The recipe builder is seeded with the Contexts and Context-owned flags
904
- * accumulated on this builder so far — the call site. Contexts provided
905
- * after `.command()` are not visible to it, matching the positional runtime
906
- * semantics of `.provide()`. Extract to `defineCommand` when a command needs
907
- * its own file, reuse, or a package. Recipe builders cannot call this
908
- * root-only method.
916
+ * accumulated on this builder so far — the call site. In a recipe, those are
917
+ * its `.use()` and `.provide()` Contexts. Contexts provided after `.command()`
918
+ * are not visible to it, matching the positional runtime semantics of
919
+ * `.provide()`. Extract to `defineCommand` when a command needs its own file
920
+ * or reuse.
909
921
  */
910
- command<const N extends string, B extends AnyCommandDefinitionBuilder>(this: {
911
- readonly _types: {
912
- readonly caps: "app";
913
- };
914
- }, name: N & CommandNameBrand<N> & CommandCollisionBrand<N, Sibs>, recipe: ((command: CommandDefinitionBuilder<{}, [], Ctx, never, LocalSpellingsOf<CtxFlags>, {}, CtxFlags>) => B & ShapeFlagCollisionBrand<ShapeOfBuilder<B>, CollisionSp["extension"]> & ValidateInlineCommandDeps<Ctx, NoInfer<B>>) & NoInfer<DescendantValuesBrand<{
922
+ command<const N extends string, B extends AnyCommandDefinitionBuilder>(name: N & CommandNameBrand<N> & CommandCollisionBrand<N, Sibs>, recipe: ((command: CommandDefinitionBuilder<{}, [], Ctx, never, LocalSpellingsOf<CtxFlags>, {}, CtxFlags>) => B & ShapeFlagCollisionBrand<ShapeOfBuilder<B>, CollisionSp["extension"]> & ValidateInlineCommandDeps<Ctx, NoInfer<B>>) & NoInfer<DescendantValuesBrand<{
915
923
  child: ShapeOfBuilder<B>;
916
924
  }, CollisionSp["demands"]>>): AfterAdd<Flags, A, Ctx, Sibs, Sp, Tree, CtxFlags, CollisionSp, Result, readonly [CommandDefinition<N, readonly [], ShapeOfBuilder<B>, DepsOfBuilder<B>>], Meta, Caps>;
917
925
  private _addDefinitions;
@@ -938,16 +946,21 @@ declare class Crust<Flags extends FlagsDef = {}, A extends ArgsDef = [], Ctx ext
938
946
  * exposed to Command Actions and Extensions) and an
939
947
  * `AbortSignal` that cancels the invocation
940
948
  */
941
- run<const Path extends CommandPath<Tree>>(this: {
949
+ run<const Path extends readonly string[]>(this: RunWithoutInputThis<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>, {
942
950
  readonly _types: {
943
951
  readonly caps: "app";
944
952
  };
945
- }, path: Path & KnownCommandPath<Path, Tree>, ...args: RunArguments<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>>): Promise<RunOutcome<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>["result"]>>;
946
- run<const Path extends CommandPath<Tree>, const Input>(this: {
953
+ }>, path: KnownCommandPath<Path, Tree>): Promise<RunOutcome<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>["result"]>>;
954
+ run<const Path extends readonly string[], const Input extends RunInput<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>> | OmittableRunInput<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>> = RunInput<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>>>(this: {
947
955
  readonly _types: {
948
956
  readonly caps: "app";
949
957
  };
950
- }, path: Path & KnownCommandPath<Path, Tree>, input: Input, ...validation: [Input] extends [CompatibleRunInput<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, NoInfer<Path>>, Input>] ? readonly [options?: InvocationOptions] : readonly [invalidInput: never]): Promise<RunOutcome<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>["result"]>>;
958
+ }, path: KnownCommandPath<Path, Tree>, input: CheckedRunInput<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>, Input> | OmittableRunInput<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>>, options?: InvocationOptions): Promise<RunOutcome<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>["result"]>>;
959
+ run<const Path extends readonly string[], const Input>(this: {
960
+ readonly _types: {
961
+ readonly caps: "app";
962
+ };
963
+ }, path: KnownCommandPath<Path, Tree>, input: Input, ...validation: [Input] extends [CompatibleRunInput<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, NoInfer<Path>>, Input>] ? readonly [options?: InvocationOptions] : readonly [invalidInput: never]): Promise<RunOutcome<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>["result"]>>;
951
964
  /**
952
965
  * Bind a typed command path into a reusable {@link CommandHandle}.
953
966
  *
@@ -959,11 +972,11 @@ declare class Crust<Flags extends FlagsDef = {}, A extends ArgsDef = [], Ctx ext
959
972
  * @param path - Typed path to the command to bind (`[]` selects the root)
960
973
  * @throws {CrustError} COMMAND_NOT_FOUND when the path does not name a command
961
974
  */
962
- at<const Path extends CommandPath<Tree>>(this: {
975
+ at<const Path extends readonly string[]>(this: {
963
976
  readonly _types: {
964
977
  readonly caps: "app";
965
978
  };
966
- }, path: Path & KnownCommandPath<Path, Tree>): CommandHandle<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>>;
979
+ }, path: KnownCommandPath<Path, Tree>): CommandHandle<CommandShapeAt<CommandShape<A, Flags, Tree, Result>, Path>>;
967
980
  /**
968
981
  * Parse `process.argv`, resolve subcommands, run Extension hooks, and
969
982
  * execute the matched Command Action.
@@ -1021,11 +1034,25 @@ interface DefinitionErrorDetails {
1021
1034
  readonly name?: string;
1022
1035
  readonly reason?: string;
1023
1036
  }
1037
+ /** One environment variable that failed; its value is never recorded. */
1038
+ interface EnvIssue {
1039
+ /** Variable name as declared. */
1040
+ readonly name: string;
1041
+ /** Declared expectation, e.g. `"number"` or `"one of: debug, info"`. */
1042
+ readonly expected: string;
1043
+ /** Whether the variable was unset or held a value that failed parsing or validation. */
1044
+ readonly received: "missing" | "invalid";
1045
+ }
1046
+ /** Every missing or invalid variable of one `@crustjs/env` definition. */
1047
+ interface EnvErrorDetails {
1048
+ readonly issues: readonly EnvIssue[];
1049
+ }
1024
1050
  interface CrustErrorDetailsMap {
1025
1051
  DEFINITION: DefinitionErrorDetails | undefined;
1026
1052
  VALIDATION: ValidationErrorDetails | undefined;
1027
1053
  PARSE: ParseErrorDetails | undefined;
1028
1054
  COMMAND_NOT_FOUND: CommandNotFoundErrorDetails;
1055
+ ENV: EnvErrorDetails;
1029
1056
  }
1030
1057
  /**
1031
1058
  * All possible error codes emitted by Crust.
@@ -1034,6 +1061,7 @@ interface CrustErrorDetailsMap {
1034
1061
  * - `VALIDATION` — Missing required arguments or flags
1035
1062
  * - `PARSE` — Argv parsing failures (unknown flags, type coercion)
1036
1063
  * - `COMMAND_NOT_FOUND` — Unrecognised subcommand at the current level
1064
+ * - `ENV` — Missing or invalid `@crustjs/env` variables (values redacted)
1037
1065
  *
1038
1066
  * @example
1039
1067
  * ```ts
@@ -1284,67 +1312,100 @@ type ExtensionSectionContribution = RuntimeCommandSectionInput & {
1284
1312
  readonly command: readonly string[];
1285
1313
  };
1286
1314
  type CommandDefinitionsDependencies<Commands extends readonly CommandDefinition<any, any, any, any>[]> = Commands extends readonly [infer H extends CommandDefinition<any, any, any, any>, ...infer T extends readonly CommandDefinition<any, any, any, any>[]] ? ([H] extends [never] ? {} : DeclaredDepsOf<H>) & CommandDefinitionsDependencies<T> : Commands extends readonly [] ? {} : Record<string, ContextValue>;
1287
- interface ExtensionConfig<Defs extends readonly NamedExtensionFlagDef[] = readonly NamedExtensionFlagDef[], Uses extends readonly AnyContextFactory[] = readonly AnyContextFactory[], Provides extends readonly AnyContextInstance[] = readonly AnyContextInstance[], Commands extends readonly CommandDefinition<any, any, any, any>[] = readonly CommandDefinition<any, any, any, any>[], MetaKeys extends RootMetaKey = never> {
1288
- readonly flags?: Defs;
1289
- readonly commands?: Commands;
1290
- readonly uses?: Uses;
1291
- readonly provides?: Provides;
1292
- readonly sections?: (snapshot: RootCommandSnapshot<MetaKeys>) => readonly ExtensionSectionContribution[];
1293
- readonly build?: (ctx: ExtensionBuildContext<MetaKeys>) => Awaitable<BuildArtifacts>;
1294
- readonly hooks?: ExtensionHooks<Defs, ContextDependencies<Uses>, MetaKeys>;
1295
- }
1296
- type ValidateExtensionConfig<Defs extends readonly NamedExtensionFlagDef[], Provides extends readonly AnyContextInstance[], Commands extends readonly CommandDefinition<any, any, any, any>[], Uses extends readonly AnyContextFactory[]> = {
1297
- readonly uses?: Uses;
1298
- readonly commands?: ValidateCommandDefinitions<Commands>;
1299
- readonly flags?: ValidateLocalFlagDefs<Defs, ProvidedContextSpellings<Provides>>;
1300
- readonly provides?: KnownContextInstances<Provides> & ProvideChecks<never, Provides>;
1301
- };
1315
+ type ExtensionDeps<Use extends readonly AnyContextFactory[], Provide extends readonly AnyContextInstance[], Commands extends readonly CommandDefinition<any, any, any, any>[]> = ContextDependencies<Use> & ContextsDependencies<Provide> & CommandDefinitionsDependencies<Commands>;
1316
+ /** Flag spellings an Extension already owns: declared flags plus provided Context-owned flags. */
1317
+ type DeclaredSpellings<Defs extends readonly NamedExtensionFlagDef[], Provide extends readonly AnyContextInstance[]> = AttachedSpellings<Defs> | ProvidedContextSpellings<Provide>;
1302
1318
  declare const extensionHookProof: unique symbol;
1303
- interface Extension<Deps extends ContextMap = ContextMap, Provides extends readonly AnyContextInstance[] = readonly AnyContextInstance[], FlagDefs extends readonly NamedExtensionFlagDef[] = readonly NamedExtensionFlagDef[], Commands extends readonly CommandDefinition<any, any, any, any>[] = readonly CommandDefinition<any, any, any, any>[], out MetaKeys extends RootMetaKey = never, HookDeps extends ContextMap = Deps> extends Defining<Extension<Deps, Provides, FlagDefs, Commands, MetaKeys, HookDeps>> {
1319
+ /**
1320
+ * @internal Sealed runtime record behind an {@link Extension} handle. Runtime
1321
+ * consumers read it through `definingOf`, so structural copies of a handle keep
1322
+ * the declaration proofs `.extend()` checks.
1323
+ */
1324
+ interface ExtensionData<Deps extends ContextMap = ContextMap, Provide extends readonly AnyContextInstance[] = readonly AnyContextInstance[], FlagDefs extends readonly NamedExtensionFlagDef[] = readonly NamedExtensionFlagDef[], Commands extends readonly CommandDefinition<any, any, any, any>[] = readonly CommandDefinition<any, any, any, any>[], out MetaKeys extends RootMetaKey = never, HookDeps extends ContextMap = Deps> {
1304
1325
  /** @internal Hook demands are distinct from command/provider attachment dependencies. */
1305
1326
  readonly _hookDeps?: HookDeps;
1306
1327
  readonly [extensionHookProof]?: (deps: HookDeps) => void;
1307
1328
  readonly id: ExtensionId;
1308
- readonly flags?: Readonly<Record<string, ExtensionFlagDef>>;
1329
+ readonly flags: Readonly<Record<string, ExtensionFlagDef>>;
1309
1330
  /** @internal — phantom carrying declared flag literals for extend-time collision checks */
1310
1331
  readonly _flagDefs?: FlagDefs;
1311
- readonly commands?: Commands;
1312
- readonly uses: readonly AnyContextFactory[];
1313
- readonly provides?: Provides;
1332
+ readonly commands: Commands;
1333
+ readonly use: readonly AnyContextFactory[];
1334
+ readonly provide: Provide;
1314
1335
  readonly sections?: (snapshot: RootCommandSnapshot<MetaKeys>) => readonly ExtensionSectionContribution[];
1315
1336
  readonly build?: (ctx: ExtensionBuildContext<MetaKeys>) => Awaitable<BuildArtifacts>;
1316
- readonly hooks?: ExtensionHooks<any, Deps, MetaKeys>;
1337
+ readonly hooks: ExtensionHooks<any, Deps, MetaKeys>;
1317
1338
  readonly _deps?: Deps;
1318
1339
  }
1340
+ /**
1341
+ * An Extension handle accepted by `.extend()`. Contribution parameters default to
1342
+ * open sets; holders keep the private dependency and hook-demand proofs.
1343
+ */
1344
+ interface Extension<Deps extends ContextMap = ContextMap, Provide extends readonly AnyContextInstance[] = readonly AnyContextInstance[], FlagDefs extends readonly NamedExtensionFlagDef[] = readonly NamedExtensionFlagDef[], Commands extends readonly CommandDefinition<any, any, any, any>[] = readonly CommandDefinition<any, any, any, any>[], out MetaKeys extends RootMetaKey = never, HookDeps extends ContextMap = Deps> extends Defining<ExtensionData<Deps, Provide, FlagDefs, Commands, MetaKeys, HookDeps>> {
1345
+ readonly id: ExtensionId;
1346
+ }
1319
1347
  /** @internal Broad Extension constraint; contravariance requires the full metadata key set. */
1320
1348
  type AnyExtension = Extension<any, any, any, any, RootMetaKey>;
1321
- type ExtensionProvidesOutput<E> = DefiningOf<E> extends Extension<any, infer Provides, any, any, RootMetaKey> ? ContextsOutput<Provides> : {};
1322
- type ExtensionsProvidesOutput<Es extends readonly AnyExtension[]> = Es extends readonly [infer H, ...infer T extends readonly AnyExtension[]] ? MergeProviders<ExtensionProvidesOutput<H>, ExtensionsProvidesOutput<T>> : Es extends readonly [] ? {} : Es[number] extends Extension<any, infer P, any, any, RootMetaKey> ? P[number] extends never ? {} : Record<string, ContextValue> : {};
1349
+ type ExtensionProvidesOutput<E> = DefiningOf<E> extends ExtensionData<any, infer Provide, any, any, RootMetaKey> ? ContextsOutput<Provide> : {};
1350
+ type ExtensionsProvidesOutput<Es extends readonly AnyExtension[]> = Es extends readonly [infer H, ...infer T extends readonly AnyExtension[]] ? MergeProviders<ExtensionProvidesOutput<H>, ExtensionsProvidesOutput<T>> : Es extends readonly [] ? {} : DefiningOf<Es[number]> extends ExtensionData<any, infer P, any, any, RootMetaKey> ? P[number] extends never ? {} : Record<string, ContextValue> : {};
1323
1351
  /**
1324
1352
  * A callable Extension constructor whose identity is also a section consumer.
1325
1353
  * Contribution parameters default to closed sets. Use `ContextMap`,
1326
1354
  * `readonly AnyContextInstance[]`, `readonly NamedExtensionFlagDef[]`, or
1327
1355
  * `readonly CommandDefinition<any, any, any, any>[]` to keep a namespace open.
1328
1356
  */
1329
- type ExtensionFactory<Args extends readonly unknown[] = [], Deps extends ContextMap = {}, Provides extends readonly AnyContextInstance[] = [], Defs extends readonly NamedExtensionFlagDef[] = [], Commands extends readonly CommandDefinition<any, any, any, any>[] = [], MetaKeys extends RootMetaKey = never, HookDeps extends ContextMap = Deps> = ((...args: Args) => Extension<Deps, Provides, Defs, Commands, MetaKeys, HookDeps>) & {
1357
+ type ExtensionFactory<Args extends readonly unknown[] = [], Deps extends ContextMap = {}, Provide extends readonly AnyContextInstance[] = [], Defs extends readonly NamedExtensionFlagDef[] = [], Commands extends readonly CommandDefinition<any, any, any, any>[] = [], MetaKeys extends RootMetaKey = never, HookDeps extends ContextMap = Deps> = ((...args: Args) => Extension<Deps, Provide, Defs, Commands, MetaKeys, HookDeps>) & {
1330
1358
  readonly id: ExtensionId;
1331
1359
  };
1332
- /** Curried Extension definer: explicit metadata keys leave all other types inferred. */
1333
- interface DefineExtensionWith<MetaKeys extends RootMetaKey> {
1334
- <Args extends readonly unknown[], const Defs extends readonly NamedExtensionFlagDef[] = [], const Uses extends readonly AnyContextFactory[] = [], const Provides extends readonly AnyContextInstance[] = [], const Commands extends readonly CommandDefinition<any, any, any, any>[] = []>(id: ExtensionId, factory: (...args: Args) => ExtensionConfig<Defs, Uses, Provides, Commands, MetaKeys> & ValidateExtensionConfig<Defs, Provides, Commands, Uses>): ExtensionFactory<Args, ContextDependencies<Uses> & ContextsDependencies<Provides> & CommandDefinitionsDependencies<Commands>, Provides, Defs, Commands, MetaKeys, ContextDependencies<Uses>>;
1335
- <const Defs extends readonly NamedExtensionFlagDef[] = [], const Uses extends readonly AnyContextFactory[] = [], const Provides extends readonly AnyContextInstance[] = [], const Commands extends readonly CommandDefinition<any, any, any, any>[] = []>(id: ExtensionId, config?: ExtensionConfig<Defs, Uses, Provides, Commands, MetaKeys> & ValidateExtensionConfig<Defs, Provides, Commands, Uses>): Extension<ContextDependencies<Uses> & ContextsDependencies<Provides> & CommandDefinitionsDependencies<Commands>, Provides, Defs, Commands, MetaKeys, ContextDependencies<Uses>>;
1360
+ type ExtensionFactoryOf<Args extends readonly unknown[], E> = E extends Defining<ExtensionData<infer Deps, infer Provide, infer Defs, infer Commands, infer MetaKeys, infer HookDeps>> ? ExtensionFactory<Args, Deps, Provide, Defs, Commands, MetaKeys, HookDeps> : never;
1361
+ type ExtensionHook<Name extends keyof ExtensionHooks, Use extends readonly AnyContextFactory[], Defs extends readonly NamedExtensionFlagDef[], MetaKeys extends RootMetaKey> = NonNullable<ExtensionHooks<Defs, ContextDependencies<Use>, MetaKeys>[Name]>;
1362
+ /**
1363
+ * Immutable fluent Extension authoring handle returned by {@link defineExtension}.
1364
+ * Every method returns a new handle. Collection methods (`use`, `provide`,
1365
+ * `flags`, `add`) append; lifecycle setters (`preRun`, `postRun`, `onError`,
1366
+ * `sections`, `build`) replace the previous callback. Callbacks are typed by the
1367
+ * declarations made before them.
1368
+ */
1369
+ interface ExtensionBuilder<Use extends readonly AnyContextFactory[] = [], Provide extends readonly AnyContextInstance[] = [], Defs extends readonly NamedExtensionFlagDef[] = [], Commands extends readonly CommandDefinition<any, any, any, any>[] = [], MetaKeys extends RootMetaKey = never> extends Extension<ExtensionDeps<Use, Provide, Commands>, Provide, Defs, Commands, MetaKeys, ContextDependencies<Use>> {
1370
+ /**
1371
+ * Declare Contexts the hooks read from `ctx`. This consumes only: the
1372
+ * application (or `.provide()`) must supply each Context.
1373
+ */
1374
+ use<const Fs extends readonly [AnyContextFactory, ...AnyContextFactory[]]>(...factories: Fs): ExtensionBuilder<readonly [...Use, ...Fs], Provide, Defs, Commands, MetaKeys>;
1375
+ /**
1376
+ * Provide Contexts application-wide, regardless of chain position. Provided
1377
+ * Contexts are not exposed to this Extension's hooks unless also declared with `.use()`.
1378
+ */
1379
+ provide<const Cs extends readonly AnyContextInstance[]>(...instances: KnownContextInstances<Cs> & ProvideChecks<DeclaredSpellings<Defs, Provide>, Cs>): ExtensionBuilder<Use, readonly [...Provide, ...Cs], Defs, Commands, MetaKeys>;
1380
+ /** Own flags; `recursive` (default `true`) contributes a flag to every command. */
1381
+ flags<const Fs extends readonly NamedExtensionFlagDef[]>(...defs: ValidateLocalFlagDefs<Fs, DeclaredSpellings<Defs, Provide>>): ExtensionBuilder<Use, Provide, readonly [...Defs, ...Fs], Commands, MetaKeys>;
1382
+ /** Contribute root commands; they replace same-named application commands. */
1383
+ add<const Ds extends readonly CommandDefinition<any, any, any, any>[]>(...definitions: Ds & ValidateCommandDefinitions<Ds, AttachedCommandSpellings<Commands>>): ExtensionBuilder<Use, Provide, Defs, readonly [...Commands, ...Ds], MetaKeys>;
1384
+ /** See {@link ExtensionHooks.preRun}. */
1385
+ preRun(hook: ExtensionHook<"preRun", Use, Defs, MetaKeys>): ExtensionBuilder<Use, Provide, Defs, Commands, MetaKeys>;
1386
+ /** See {@link ExtensionHooks.postRun}. */
1387
+ postRun(hook: ExtensionHook<"postRun", Use, Defs, MetaKeys>): ExtensionBuilder<Use, Provide, Defs, Commands, MetaKeys>;
1388
+ /** See {@link ExtensionHooks.onError}. */
1389
+ onError(hook: ExtensionHook<"onError", Use, Defs, MetaKeys>): ExtensionBuilder<Use, Provide, Defs, Commands, MetaKeys>;
1390
+ /** Contribute documentation sections computed from the prepared root snapshot. */
1391
+ sections(contribute: (snapshot: RootCommandSnapshot<MetaKeys>) => readonly ExtensionSectionContribution[]): ExtensionBuilder<Use, Provide, Defs, Commands, MetaKeys>;
1392
+ /** Generate build artifacts; build tooling writes the returned files. */
1393
+ build(generate: (ctx: ExtensionBuildContext<MetaKeys>) => Awaitable<BuildArtifacts>): ExtensionBuilder<Use, Provide, Defs, Commands, MetaKeys>;
1394
+ /**
1395
+ * Make a configurable Extension: each call passes this handle and the call
1396
+ * arguments to `define`, which must return an Extension with this handle's id.
1397
+ */
1398
+ factory<Args extends readonly unknown[], E extends AnyExtension>(define: (extension: ExtensionBuilder<Use, Provide, Defs, Commands, MetaKeys>, ...args: Args) => E): ExtensionFactoryOf<Args, E>;
1336
1399
  }
1337
1400
  /**
1338
- * Define an Extension, or a factory that builds one from config on each call.
1401
+ * Start an immutable fluent Extension definition.
1339
1402
  *
1340
1403
  * Extensions apply to the whole application and own the flags and commands
1341
- * they contribute. Factories expose the same identity for section audiences.
1342
- * `defineExtension<"version">()(id, configOrFactory)` declares required root
1343
- * metadata keys without preventing inference of flags, Contexts, or commands.
1404
+ * they contribute. `defineExtension<"version">(id)` requires root metadata keys
1405
+ * without preventing inference of flags, Contexts, or commands; chain
1406
+ * `.factory()` for a configurable Extension whose identity is a section consumer.
1344
1407
  */
1345
- declare function defineExtension<MetaKeys extends RootMetaKey = never>(): DefineExtensionWith<MetaKeys>;
1346
- declare function defineExtension<Args extends readonly unknown[], const Defs extends readonly NamedExtensionFlagDef[] = [], const Uses extends readonly AnyContextFactory[] = [], const Provides extends readonly AnyContextInstance[] = [], const Commands extends readonly CommandDefinition<any, any, any, any>[] = []>(id: ExtensionId, factory: (...args: Args) => ExtensionConfig<Defs, Uses, Provides, Commands> & ValidateExtensionConfig<Defs, Provides, Commands, Uses>): ExtensionFactory<Args, ContextDependencies<Uses> & ContextsDependencies<Provides> & CommandDefinitionsDependencies<Commands>, Provides, Defs, Commands, never, ContextDependencies<Uses>>;
1347
- declare function defineExtension<const Defs extends readonly NamedExtensionFlagDef[] = [], const Uses extends readonly AnyContextFactory[] = [], const Provides extends readonly AnyContextInstance[] = [], const Commands extends readonly CommandDefinition<any, any, any, any>[] = []>(id: ExtensionId, config?: ExtensionConfig<Defs, Uses, Provides, Commands> & ValidateExtensionConfig<Defs, Provides, Commands, Uses>): Extension<ContextDependencies<Uses> & ContextsDependencies<Provides> & CommandDefinitionsDependencies<Commands>, Provides, Defs, Commands, never, ContextDependencies<Uses>>;
1408
+ declare function defineExtension<MetaKeys extends RootMetaKey = never>(id: ExtensionId): ExtensionBuilder<[], [], [], [], MetaKeys>;
1348
1409
  //#endregion
1349
1410
  //#region src/parsing/spellings.d.ts
1350
1411
  /**
@@ -1392,10 +1453,12 @@ interface CommandNode {
1392
1453
  subCommands: Record<string, CommandNode>;
1393
1454
  /** Contexts available to this command in provide order (construction order is pull-driven). */
1394
1455
  contexts: CommandContext[];
1456
+ /** Contexts `.provide()`d on this command itself; preparation installs their sections here. */
1457
+ providedContexts: readonly AnyContextInstance[];
1395
1458
  /** Declared command demands; validated when recipes are materialized. */
1396
1459
  demands: readonly AnyContextFactory[];
1397
1460
  /** Extensions registered via `.extend()` (root builder only) */
1398
- extensions: Extension[];
1461
+ extensions: ExtensionData[];
1399
1462
  /** The Command Action */
1400
1463
  run?: CommandAction;
1401
1464
  }
@@ -1407,6 +1470,22 @@ interface RunInputPayload {
1407
1470
  readonly flags?: Readonly<Record<string, RunInputValue | undefined>>;
1408
1471
  readonly raw?: readonly string[];
1409
1472
  }
1473
+ /** Environment variables consulted by `FlagDef.env`; `process.env` on the terminal path. */
1474
+ type FlagEnvironment = Readonly<Record<string, string | undefined>>;
1475
+ /**
1476
+ * Resolve flags from their `env` bindings alone, as {@link parseArgs} does for an
1477
+ * empty argv: `env.delimiter` splitting, `choices`, `parse`, built-in coercion,
1478
+ * defaults, requiredness, then Standard Schemas. Flags without `env` resolve
1479
+ * to their defaults.
1480
+ *
1481
+ * @param flags - Flag definitions keyed by result name; each `env.name` selects its variable
1482
+ * @param env - Raw variable text; only own properties are read
1483
+ * @throws {CrustError} `DEFINITION` for a definition `defineFlag` rejects (e.g. a
1484
+ * `__proto__` key or a default outside `choices`); `PARSE` or `VALIDATION` on the first
1485
+ * coercion or requiredness failure, or `VALIDATION` aggregating schema issues. Messages
1486
+ * may quote raw values.
1487
+ */
1488
+ declare function parseFlagValues<F extends FlagsDef>(flags: F, env: FlagEnvironment): Promise<InferFlags<F>>;
1410
1489
  //#endregion
1411
1490
  //#region src/types.d.ts
1412
1491
  /** Injectable output callbacks threaded through one invocation. */
@@ -2048,29 +2127,17 @@ declare const contextSources: unique symbol;
2048
2127
  type ContextBag<Deps extends ContextMap = {}> = { readonly [K in keyof Deps]: Promise<Deps[K]>; } & {
2049
2128
  readonly [contextSources]?: readonly (AnyContextFactory | AnyContextInstance)[];
2050
2129
  };
2051
- interface ContextConfig {
2052
- readonly flags?: readonly NamedFlagDef[];
2053
- readonly uses?: readonly AnyContextFactory[];
2054
- }
2055
- type ValidateContextConfig<R extends ContextConfig> = {
2056
- readonly flags?: R["flags"] extends readonly NamedFlagDef[] ? ValidateLocalFlagDefs<R["flags"], never> : "flags" extends keyof R ? {} : never;
2057
- readonly uses?: R["uses"] extends readonly AnyContextFactory[] ? R["uses"] : "uses" extends keyof R ? {} : never;
2058
- };
2059
- interface ContextSetupInput<OF extends FlagsDef = FlagsDef> extends InvocationIO {
2060
- readonly signal: AbortSignal;
2061
- readonly flags: InferFlags<OF>;
2062
- readonly ctx: ContextBag<ContextMap>;
2063
- readonly defer: (cleanup: () => void | PromiseLike<void>) => void;
2064
- }
2065
2130
  interface ContextInstance<Name extends string = string, Value = unknown, OF extends FlagsDef = FlagsDef, Deps extends ContextMap = Record<string, ContextValue>> extends Defining<ContextInstance<Name, Value, OF, Deps>> {
2066
2131
  readonly [contextProof]?: string extends keyof OF | keyof Deps ? unknown : (state: [OF, Deps]) => void;
2067
2132
  readonly name: Name;
2068
2133
  readonly ownedFlags: FlagsDef;
2134
+ /** Validated {@link ContextBuilder.sections}, frozen at declaration. */
2135
+ readonly sections: readonly CommandSection[];
2069
2136
  /** @internal — declared direct dependency factories */
2070
- readonly uses: readonly AnyContextFactory[];
2137
+ readonly use: readonly AnyContextFactory[];
2071
2138
  /** @internal defining factory, for adapters that identify Contexts by factory */
2072
2139
  readonly factory: AnyContextFactory;
2073
- setup(input: ContextSetupInput<OF>): Awaitable<Value>;
2140
+ setup(input: ContextSetup<OF, ContextMap>): Awaitable<Value>;
2074
2141
  readonly _ownedFlags?: OF;
2075
2142
  /** @internal — phantom carrying the transitive dependency closure */
2076
2143
  readonly _deps?: Deps;
@@ -2079,8 +2146,8 @@ interface ContextInstance<Name extends string = string, Value = unknown, OF exte
2079
2146
  type AnyContextInstance = ContextInstance<string, unknown, any, any>;
2080
2147
  /** Whatever a Context setup produces, erased at the runtime registry. */
2081
2148
  type ContextValue = Awaited<ReturnType<AnyContextInstance["setup"]>>;
2082
- interface ContextSetup<Options, OF extends FlagsDef = {}, Deps extends ContextMap = {}> extends InvocationIO {
2083
- readonly options: Options;
2149
+ /** Invocation input passed to a Context's `.setup()` callback; factory options arrive as its second parameter. */
2150
+ interface ContextSetup<OF extends FlagsDef = {}, Deps extends ContextMap = {}> extends InvocationIO {
2084
2151
  /** The invocation's cancellation signal; pass it to cancellable setup work. */
2085
2152
  readonly signal: AbortSignal;
2086
2153
  readonly flags: InferFlags<OF>;
@@ -2092,10 +2159,11 @@ interface ContextSetup<Options, OF extends FlagsDef = {}, Deps extends ContextMa
2092
2159
  readonly defer: (cleanup: () => void | PromiseLike<void>) => void;
2093
2160
  }
2094
2161
  interface ContextFactory<Name extends string, Options, Value, OF extends FlagsDef = {}, Deps extends ContextMap = {}> extends Defining<ContextFactory<Name, Options, Value, OF, Deps>> {
2095
- (options: Options): ContextInstance<Name, Value, OF, Deps>;
2162
+ /** Options may be omitted when their type accepts `undefined` (including no-option `void`). */
2163
+ (...options: undefined extends Options ? [options?: Options] : [options: Options]): ContextInstance<Name, Value, OF, Deps>;
2096
2164
  readonly contextName: Name;
2097
2165
  /** @internal — declared direct dependency factories */
2098
- readonly uses: readonly AnyContextFactory[];
2166
+ readonly use: readonly AnyContextFactory[];
2099
2167
  of(value: Value): ContextInstance<Name, Value, OF, {}>;
2100
2168
  readonly _deps?: Deps;
2101
2169
  }
@@ -2108,20 +2176,40 @@ type FactoryOutput<F> = F extends AnyContextFactory ? DefiningOf<F> extends Cont
2108
2176
  type FactoriesOutput<Fs extends readonly AnyContextFactory[]> = Fs extends readonly [infer H, ...infer T extends readonly AnyContextFactory[]] ? FactoryOutput<H> & FactoriesOutput<T> : {};
2109
2177
  type FactoryDeps<F> = F extends AnyContextFactory ? DefiningOf<F> extends ContextFactory<any, any, any, any, infer Deps> ? Deps : {} : {};
2110
2178
  type FactoriesDeps<Fs extends readonly AnyContextFactory[]> = Fs extends readonly [infer H, ...infer T extends readonly AnyContextFactory[]] ? FactoryDeps<H> & FactoriesDeps<T> : {};
2111
- type ContextDependencies<Uses extends readonly AnyContextFactory[]> = IsStaticTuple<Uses> extends true ? FactoriesOutput<Uses> & FactoriesDeps<Uses> : Record<string, ContextValue>;
2179
+ type ContextDependencies<Use extends readonly AnyContextFactory[]> = IsStaticTuple<Use> extends true ? FactoriesOutput<Use> & FactoriesDeps<Use> : Record<string, ContextValue>;
2112
2180
  type ContextDepsOf<C> = C extends AnyContextInstance ? DefiningOf<C> extends {
2113
2181
  readonly _deps?: infer Deps extends ContextMap;
2114
2182
  } ? Deps : {} : {};
2115
2183
  type ContextsDependencies<Cs extends readonly AnyContextInstance[]> = Cs extends readonly [infer H, ...infer T extends readonly AnyContextInstance[]] ? ContextDepsOf<H> & ContextsDependencies<T> : {};
2116
- type OwnedFlagsOf<R extends ContextConfig> = R extends {
2117
- flags: infer F extends readonly NamedFlagDef[];
2118
- } ? AttachedFlags<F> : "flags" extends keyof R ? FlagsDef : {};
2119
- type UsesOf<R extends ContextConfig> = R extends {
2120
- uses: infer Uses extends readonly AnyContextFactory[];
2121
- } ? Uses : "uses" extends keyof R ? readonly AnyContextFactory[] : readonly [];
2122
- /** Define a named, lazy command dependency. Declared `uses` are exposed on `ctx`. */
2123
- declare function defineContext<Name extends string, Value, Options = void>(name: Name, setup: (input: ContextSetup<Options>) => Awaitable<Value>): ContextFactory<Name, Options, Value>;
2124
- declare function defineContext<Name extends string, const R extends ContextConfig, Value, Options = void>(name: Name, config: R & ValidateContextConfig<R> & ContextConfig, setup: (input: ContextSetup<Options, OwnedFlagsOf<R>, ContextDependencies<UsesOf<R>>>) => Awaitable<Value>): ContextFactory<Name, Options, Value, OwnedFlagsOf<R>, ContextDependencies<UsesOf<R>>>;
2184
+ /** Factory options inferred from a setup callback's optional second parameter. */
2185
+ type ContextOptionsOf<Args extends readonly unknown[]> = Args extends readonly [] ? void : Args[0];
2186
+ /**
2187
+ * Immutable fluent Context authoring handle returned by {@link defineContext}.
2188
+ * `use`, `flags`, and `sections` append and return a new handle; `setup` ends the chain and
2189
+ * returns the callable {@link ContextFactory}. Callbacks are typed by the
2190
+ * declarations made before them.
2191
+ */
2192
+ interface ContextBuilder<Name extends string, Use extends readonly AnyContextFactory[] = [], Defs extends readonly NamedFlagDef[] = []> {
2193
+ /** Declare Contexts setup reads from `ctx`; consumers must provide their transitive closure. */
2194
+ use<const Fs extends readonly AnyContextFactory[]>(...factories: Fs): ContextBuilder<Name, readonly [...Use, ...Fs], Defs>;
2195
+ /** Own flags parsed wherever this Context is provided; setup reads them from `flags`. */
2196
+ flags<const Fs extends readonly NamedFlagDef[]>(...defs: ValidateLocalFlagDefs<Fs, AttachedSpellings<Defs>>): ContextBuilder<Name, Use, readonly [...Defs, ...Fs]>;
2197
+ /** Document only the providing command (the root for Extension-provided Contexts). */
2198
+ sections<const S extends readonly RuntimeCommandSectionInput[]>(...sections: S & LocalSectionsBrand<{
2199
+ sections: S;
2200
+ }>["sections"]): ContextBuilder<Name, Use, Defs>;
2201
+ /**
2202
+ * Finish the definition. `setup` runs lazily, once per invocation, when a
2203
+ * consumer first reads the Context. Annotate its optional second parameter to
2204
+ * accept factory options: `.setup((input, options: { url: string }) => …)`.
2205
+ */
2206
+ setup<Value, Args extends readonly [options?: unknown] = []>(setup: (input: ContextSetup<AttachedFlags<Defs>, ContextDependencies<Use>>, ...args: Args) => Awaitable<Value>): ContextFactory<Name, ContextOptionsOf<Args>, Value, AttachedFlags<Defs>, ContextDependencies<Use>>;
2207
+ }
2208
+ /**
2209
+ * Start an immutable fluent Context definition: a named, lazy command
2210
+ * dependency. Chain `.use()`, `.flags()`, and `.sections()`, then `.setup()` for the factory.
2211
+ */
2212
+ declare function defineContext<Name extends string>(name: Name): ContextBuilder<Name>;
2125
2213
  type FactoryValueOf<F extends AnyContextFactory> = F extends ContextFactory<any, any, infer Value, any, any> ? Awaited<Value> : never;
2126
2214
  //#endregion
2127
- export { CrustErrorCode as $, SectionConsumer as A, MergeContext as At, ExtensionContext as B, InvocationOptions as C, ArgSnapshot as Ct, ParsedArgValue as D, LocalFlagNameBrand as Dt, ParseResult as E, LocalFlagBrand as Et, BuildReport as F, Finished as G, ExtensionFlagDef as H, DefineExtensionWith as I, NamedExtensionFlagDef as J, InferExtensionFlags as K, Extension as L, ValueType as M, defineExtensionId as Mt, BuildArtifacts as N, ParsedFlagValue as O, EmptyArgNameBrand as Ot, BuildFile as P, CrustError as Q, ExtensionBuildContext as R, InvocationIO as S, defineCommand as St, NamedFlagDef as T, FlagSnapshot as Tt, ExtensionHooks as U, ExtensionFactory as V, ExtensionSectionContribution as W, defineExtension as X, RootMetaKey as Y, CommandNotFoundErrorDetails as Z, ExecuteOptions as _, RootCommandMeta as _t, ContextInstance as a, ValidationErrorDetails as at, InputArgs as b, RunInputArguments as bt, FactoryValueOf as c, CommandDefinition as ct, ArgDef as d, CommandPath as dt, CrustErrorDetails as et, ArgsDef as f, CommandShape as ft, DeclaredDefault as g, CrustCommandContext as gt, CommandSectionInput as h, Crust as ht, ContextFactory as i, ParseErrorDetails as it, ValidatedInput as j, ExtensionId as jt, SectionAudience as k, LocalValueBrand as kt, contextSources as l, CommandDefinitionBuilder as lt, CommandSection as m, CommandTree as mt, ContextBag as n, CrustErrorJson as nt, ContextMap as o, AnyCrust as ot, CommandMeta as p, CommandShapeAt as pt, InvocationOutcome as q, ContextConfig as r, DefinitionErrorDetails as rt, ContextSetup as s, CommandConfig as st, AnyContextFactory as t, CrustErrorDetailsMap as tt, defineContext as u, CommandHandle as ut, FlagDef as v, RunArguments as vt, MergeFlags as w, CommandSnapshot as wt, InputFlags as x, RunOutcome as xt, FlagsDef as y, RunInput as yt, ExtensionConfig as z };
2215
+ export { CrustError as $, SectionConsumer as A, LocalFlagNameBrand as At, ExtensionBuilder as B, InvocationOptions as C, RunInputArguments as Ct, ParsedArgValue as D, CommandSnapshot as Dt, ParseResult as E, ArgSnapshot as Et, BuildArtifacts as F, defineExtensionId as Ft, ExtensionSectionContribution as G, ExtensionFactory as H, BuildFile as I, InvocationOutcome as J, Finished as K, BuildReport as L, ValueType as M, LocalValueBrand as Mt, FlagEnvironment as N, MergeContext as Nt, ParsedFlagValue as O, FlagSnapshot as Ot, parseFlagValues as P, ExtensionId as Pt, CommandNotFoundErrorDetails as Q, Extension as R, InvocationIO as S, RunInput as St, NamedFlagDef as T, defineCommand as Tt, ExtensionFlagDef as U, ExtensionContext as V, ExtensionHooks as W, RootMetaKey as X, NamedExtensionFlagDef as Y, defineExtension as Z, ExecuteOptions as _, CommandTree as _t, ContextInstance as a, EnvErrorDetails as at, InputArgs as b, RootCommandMeta as bt, FactoryValueOf as c, ValidationErrorDetails as ct, ArgDef as d, CommandDefinition as dt, CrustErrorCode as et, ArgsDef as f, CommandDefinitionBuilder as ft, DeclaredDefault as g, CommandShapeAt as gt, CommandSectionInput as h, CommandShape as ht, ContextFactory as i, DefinitionErrorDetails as it, ValidatedInput as j, EmptyArgNameBrand as jt, SectionAudience as k, LocalFlagBrand as kt, contextSources as l, AnyCrust as lt, CommandSection as m, CommandPath as mt, ContextBag as n, CrustErrorDetailsMap as nt, ContextMap as o, EnvIssue as ot, CommandMeta as p, CommandHandle as pt, InferExtensionFlags as q, ContextBuilder as r, CrustErrorJson as rt, ContextSetup as s, ParseErrorDetails as st, AnyContextFactory as t, CrustErrorDetails as tt, defineContext as u, CommandConfig as ut, FlagDef as v, Crust as vt, MergeFlags as w, RunOutcome as wt, InputFlags as x, RunArguments as xt, FlagsDef as y, CrustCommandContext as yt, ExtensionBuildContext as z };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as CrustErrorCode, A as SectionConsumer, At as MergeContext, B as ExtensionContext, C as InvocationOptions, Ct as ArgSnapshot, D as ParsedArgValue, Dt as LocalFlagNameBrand, E as ParseResult, Et as LocalFlagBrand, F as BuildReport, G as Finished, H as ExtensionFlagDef, I as DefineExtensionWith, J as NamedExtensionFlagDef, K as InferExtensionFlags, L as Extension, M as ValueType, Mt as defineExtensionId, N as BuildArtifacts, O as ParsedFlagValue, Ot as EmptyArgNameBrand, P as BuildFile, Q as CrustError, R as ExtensionBuildContext, S as InvocationIO, St as defineCommand, T as NamedFlagDef, Tt as FlagSnapshot, U as ExtensionHooks, V as ExtensionFactory, W as ExtensionSectionContribution, X as defineExtension, Y as RootMetaKey, Z as CommandNotFoundErrorDetails, _ as ExecuteOptions, _t as RootCommandMeta, a as ContextInstance, at as ValidationErrorDetails, b as InputArgs, bt as RunInputArguments, c as FactoryValueOf, ct as CommandDefinition, d as ArgDef, dt as CommandPath, et as CrustErrorDetails, f as ArgsDef, ft as CommandShape, gt as CrustCommandContext, h as CommandSectionInput, ht as Crust, i as ContextFactory, it as ParseErrorDetails, j as ValidatedInput, jt as ExtensionId, k as SectionAudience, kt as LocalValueBrand, l as contextSources, lt as CommandDefinitionBuilder, m as CommandSection, mt as CommandTree, n as ContextBag, nt as CrustErrorJson, o as ContextMap, ot as AnyCrust, p as CommandMeta, pt as CommandShapeAt, q as InvocationOutcome, r as ContextConfig, rt as DefinitionErrorDetails, s as ContextSetup, st as CommandConfig, t as AnyContextFactory, tt as CrustErrorDetailsMap, u as defineContext, ut as CommandHandle, v as FlagDef, vt as RunArguments, w as MergeFlags, wt as CommandSnapshot, x as InputFlags, xt as RunOutcome, y as FlagsDef, yt as RunInput, z as ExtensionConfig } from "./context-B1ck_WZ-.js";
1
+ import { $ as CrustError, A as SectionConsumer, At as LocalFlagNameBrand, B as ExtensionBuilder, C as InvocationOptions, Ct as RunInputArguments, D as ParsedArgValue, Dt as CommandSnapshot, E as ParseResult, Et as ArgSnapshot, F as BuildArtifacts, Ft as defineExtensionId, G as ExtensionSectionContribution, H as ExtensionFactory, I as BuildFile, J as InvocationOutcome, K as Finished, L as BuildReport, M as ValueType, Mt as LocalValueBrand, Nt as MergeContext, O as ParsedFlagValue, Ot as FlagSnapshot, Pt as ExtensionId, Q as CommandNotFoundErrorDetails, R as Extension, S as InvocationIO, St as RunInput, T as NamedFlagDef, Tt as defineCommand, U as ExtensionFlagDef, V as ExtensionContext, W as ExtensionHooks, X as RootMetaKey, Y as NamedExtensionFlagDef, Z as defineExtension, _ as ExecuteOptions, _t as CommandTree, a as ContextInstance, at as EnvErrorDetails, b as InputArgs, bt as RootCommandMeta, c as FactoryValueOf, ct as ValidationErrorDetails, d as ArgDef, dt as CommandDefinition, et as CrustErrorCode, f as ArgsDef, ft as CommandDefinitionBuilder, gt as CommandShapeAt, h as CommandSectionInput, ht as CommandShape, i as ContextFactory, it as DefinitionErrorDetails, j as ValidatedInput, jt as EmptyArgNameBrand, k as SectionAudience, kt as LocalFlagBrand, l as contextSources, lt as AnyCrust, m as CommandSection, mt as CommandPath, n as ContextBag, nt as CrustErrorDetailsMap, o as ContextMap, ot as EnvIssue, p as CommandMeta, pt as CommandHandle, q as InferExtensionFlags, r as ContextBuilder, rt as CrustErrorJson, s as ContextSetup, st as ParseErrorDetails, t as AnyContextFactory, tt as CrustErrorDetails, u as defineContext, ut as CommandConfig, v as FlagDef, vt as Crust, w as MergeFlags, wt as RunOutcome, x as InputFlags, xt as RunArguments, y as FlagsDef, yt as CrustCommandContext, z as ExtensionBuildContext } from "./context-9ee4iljp.js";
2
2
  import { resolveArtifactDir } from "@crustjs/utils/artifacts";
3
3
  //#region src/api/flags.d.ts
4
4
  /** Distribute `Omit<_, "name">` over the {@link ArgDef} union. */
@@ -20,4 +20,4 @@ export declare function defineFlag<const N extends string, const D extends FlagD
20
20
  /** Define and own one positional argument; layout belongs to its consuming command. */
21
21
  export declare function defineArg<const N extends string, const D extends UnnamedArgDef>(name: N & EmptyArgNameBrand<N>, def: D & LocalValueBrand<D>): Named<N, D>;
22
22
  //#endregion
23
- export { type AnyContextFactory, type AnyCrust, type ArgDef, type ArgSnapshot, type ArgsDef, type BuildArtifacts, type BuildFile, type BuildReport, type CommandConfig, type CommandDefinition, type CommandDefinitionBuilder, type CommandHandle, type CommandMeta, type CommandNotFoundErrorDetails, type CommandPath, type CommandSection, type CommandSectionInput, type CommandShape, type CommandShapeAt, type CommandSnapshot, type CommandTree, type ContextBag, type ContextConfig, type ContextFactory, type ContextInstance, type ContextMap, type ContextSetup, Crust, type CrustCommandContext, CrustError, type CrustErrorCode, type CrustErrorDetails, type CrustErrorDetailsMap, type CrustErrorJson, type DefineExtensionWith, type DefinitionErrorDetails, type ExecuteOptions, type Extension, type ExtensionBuildContext, type ExtensionConfig, type ExtensionContext, type ExtensionFactory, type ExtensionFlagDef, type ExtensionHooks, type ExtensionId, type ExtensionSectionContribution, type FactoryValueOf, type Finished, type FlagDef, type FlagSnapshot, type FlagsDef, type InferExtensionFlags, type InputArgs, type InputFlags, type InvocationIO, type InvocationOptions, type InvocationOutcome, type MergeContext, type MergeFlags, type NamedExtensionFlagDef, type NamedFlagDef, type ParseErrorDetails, type ParseResult, type ParsedArgValue, type ParsedFlagValue, type RootCommandMeta, type RootMetaKey, type RunArguments, type RunInput, type RunInputArguments, type RunOutcome, type SectionAudience, type SectionConsumer, type UnnamedArgDef, type ValidatedInput, type ValidationErrorDetails, type ValueType, contextSources, defineCommand, defineContext, defineExtension, defineExtensionId, resolveArtifactDir };
23
+ export { type AnyContextFactory, type AnyCrust, type ArgDef, type ArgSnapshot, type ArgsDef, type BuildArtifacts, type BuildFile, type BuildReport, type CommandConfig, type CommandDefinition, type CommandDefinitionBuilder, type CommandHandle, type CommandMeta, type CommandNotFoundErrorDetails, type CommandPath, type CommandSection, type CommandSectionInput, type CommandShape, type CommandShapeAt, type CommandSnapshot, type CommandTree, type ContextBag, type ContextBuilder, type ContextFactory, type ContextInstance, type ContextMap, type ContextSetup, Crust, type CrustCommandContext, CrustError, type CrustErrorCode, type CrustErrorDetails, type CrustErrorDetailsMap, type CrustErrorJson, type DefinitionErrorDetails, type EnvErrorDetails, type EnvIssue, type ExecuteOptions, type Extension, type ExtensionBuildContext, type ExtensionBuilder, type ExtensionContext, type ExtensionFactory, type ExtensionFlagDef, type ExtensionHooks, type ExtensionId, type ExtensionSectionContribution, type FactoryValueOf, type Finished, type FlagDef, type FlagSnapshot, type FlagsDef, type InferExtensionFlags, type InputArgs, type InputFlags, type InvocationIO, type InvocationOptions, type InvocationOutcome, type MergeContext, type MergeFlags, type NamedExtensionFlagDef, type NamedFlagDef, type ParseErrorDetails, type ParseResult, type ParsedArgValue, type ParsedFlagValue, type RootCommandMeta, type RootMetaKey, type RunArguments, type RunInput, type RunInputArguments, type RunOutcome, type SectionAudience, type SectionConsumer, type UnnamedArgDef, type ValidatedInput, type ValidationErrorDetails, type ValueType, contextSources, defineCommand, defineContext, defineExtension, defineExtensionId, resolveArtifactDir };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as CrustError, S as normalizeFlag, _ as contextSources, a as runInvocation, b as validateContextAvailability, d as cloneFlagRegistry, f as installExtensionContexts, g as defineExtension, h as registerFlag, i as resolveTypedPath, m as createCommandNode, n as executeInvocation, o as snapshotCommand, p as validateCommandSections, r as prepareInvocation, u as cloneCommandNode, v as defineContext, x as normalizeArg, y as definingOf } from "./invocation-ka-BNJKZ.js";
1
+ import { C as normalizeFlag, S as normalizeArg, _ as validateContextAvailability, a as runInvocation, b as validateCommandSections, c as cloneCommandNode, d as createCommandNode, f as registerFlag, g as definingOf, h as defineContext, i as resolveTypedPath, l as cloneFlagRegistry, m as contextSources, n as executeInvocation, o as snapshotCommand, p as defineExtension, r as prepareInvocation, u as installExtensionContexts, w as CrustError } from "./invocation-CD299Gic.js";
2
2
  import { resolveArtifactDir } from "@crustjs/utils/artifacts";
3
3
  //#region src/identity.ts
4
4
  /** Mint an Extension identity from any non-blank, trimmed string. */
@@ -161,7 +161,8 @@ var Crust = class {
161
161
  validateContextAvailability([...this._node.contexts.map(({ instance }) => instance), ...instances], instances);
162
162
  const cloned = this._clone({
163
163
  ...cloneFlagRegistry(this._node),
164
- contexts: [...this._node.contexts, ...instances.map((instance) => ({ instance }))]
164
+ contexts: [...this._node.contexts, ...instances.map((instance) => ({ instance }))],
165
+ providedContexts: [...this._node.providedContexts, ...instances]
165
166
  });
166
167
  for (const instance of instances) for (const [name, definition] of Object.entries(instance.ownedFlags)) registerFlag(cloned._node, name, definition, "owned");
167
168
  return cloned;
@@ -187,7 +188,7 @@ var Crust = class {
187
188
  const extensions = inputs.map(definingOf);
188
189
  const activeExtensions = dedupeExtensions([...this._node.extensions, ...extensions]);
189
190
  const node = installExtensionContexts(this._node, activeExtensions, new Set(extensions.map((extension) => extension.id)));
190
- validateContextAvailability(node.contexts.map(({ instance }) => instance), activeExtensions.flatMap((extension) => [...extension.uses, ...extension.provides ?? []]));
191
+ validateContextAvailability(node.contexts.map(({ instance }) => instance), activeExtensions.flatMap((extension) => [...extension.use, ...extension.provide]));
191
192
  return this._clone({
192
193
  ...node,
193
194
  extensions: activeExtensions