@@ -5,6 +5,7 @@ import { getCurrentInjector, runInInjectionContext } from "../di/inject";
55import { Injector } from "../di/injector" ;
66import { IDictionary , IDashedOption , IErrors } from "../declarations" ;
77import { ICommand } from "../definitions/commands" ;
8+ import { COMMAND_CONTEXT } from "../contracts/command-context" ;
89import {
910 COMMAND_OWNER ,
1011 CommandRegistry ,
@@ -335,13 +336,17 @@ export function createCommandFromDefinition<
335336 // The state of one invocation. The command object itself is cached for the
336337 // process, so nothing invocation-scoped may live outside one of these.
337338 interface Invocation {
339+ /** The context of the stage that is running; COMMAND_CONTEXT reads it. */
340+ context : CommandContext < TSchema > ;
341+ injector : Injector ;
338342 setup : Promise < Awaited < TSetup > > ;
339343 hasRun : boolean ;
340344 runResult ?: Awaited < TResult > ;
341345 }
342346
343347 const startSetup = (
344348 context : CommandContext < TSchema > ,
349+ injector : Injector ,
345350 ) : Promise < Awaited < TSetup > > =>
346351 // The executor runs synchronously, so setup keeps its injection context
347352 // up to its first await, while a synchronous failure - ctx.fail() is one -
@@ -350,7 +355,7 @@ export function createCommandFromDefinition<
350355 resolve (
351356 definition . setup
352357 ? < any > (
353- runInInjectionContext ( targetInjector , ( ) =>
358+ runInInjectionContext ( injector , ( ) =>
354359 definition . setup . call ( definition , context ) ,
355360 )
356361 )
@@ -365,9 +370,25 @@ export function createCommandFromDefinition<
365370 let currentInvocation : Invocation = null ;
366371
367372 const beginInvocation = ( context : CommandContext < TSchema > ) : Invocation => {
368- currentInvocation = { setup : startSetup ( context ) , hasRun : false } ;
373+ const invocation : Invocation = {
374+ context,
375+ // Each entry point builds its own context object, so the token reads
376+ // the live one rather than a snapshot: a handler that injects it gets
377+ // the very context it was handed.
378+ injector : targetInjector . createChild ( [
379+ {
380+ provide : COMMAND_CONTEXT ,
381+ useFactory : ( ) => invocation . context ,
382+ shared : false ,
383+ } ,
384+ ] ) ,
385+ setup : undefined ,
386+ hasRun : false ,
387+ } ;
388+ invocation . setup = startSetup ( context , invocation . injector ) ;
389+ currentInvocation = invocation ;
369390
370- return currentInvocation ;
391+ return invocation ;
371392 } ;
372393
373394 /**
@@ -377,6 +398,7 @@ export function createCommandFromDefinition<
377398 * take the host's keys with it.
378399 */
379400 const attachShortcuts = (
401+ invocation : Invocation ,
380402 context : CommandContext < TSchema > ,
381403 setupResult : Awaited < TSetup > ,
382404 ) : void => {
@@ -392,8 +414,9 @@ export function createCommandFromDefinition<
392414 return ;
393415 }
394416
395- const shortcuts : KeyShortcut [ ] = runInInjectionContext ( targetInjector , ( ) =>
396- definition . shortcuts . call ( definition , context , setupResult ) ,
417+ const shortcuts : KeyShortcut [ ] = runInInjectionContext (
418+ invocation . injector ,
419+ ( ) => definition . shortcuts . call ( definition , context , setupResult ) ,
397420 ) ;
398421 if ( ! shortcuts || ! shortcuts . length ) {
399422 return ;
@@ -428,8 +451,9 @@ export function createCommandFromDefinition<
428451 postCommandAction : async ( args : string [ ] ) : Promise < void > => {
429452 const context = buildContext ( args ) ;
430453 const invocation = currentInvocation || beginInvocation ( context ) ;
454+ invocation . context = context ;
431455 const setupResult = await invocation . setup ;
432- await runInInjectionContext ( targetInjector , ( ) =>
456+ await runInInjectionContext ( invocation . injector , ( ) =>
433457 definition . postRun . call (
434458 definition ,
435459 context ,
@@ -446,7 +470,8 @@ export function createCommandFromDefinition<
446470 // arguments - so an argument validator can rely on it, and a command
447471 // run in the wrong place still reports that before complaining about
448472 // arity.
449- const setupResult = await beginInvocation ( context ) . setup ;
473+ const invocation = beginInvocation ( context ) ;
474+ const setupResult = await invocation . setup ;
450475
451476 await enforceArguments ( context ) ;
452477
@@ -457,7 +482,7 @@ export function createCommandFromDefinition<
457482
458483 // Same first-await rule as execute: runInInjectionContext is
459484 // synchronous, so inject() is available up to the first await.
460- return await runInInjectionContext ( targetInjector , ( ) =>
485+ return await runInInjectionContext ( invocation . injector , ( ) =>
461486 refine . call ( definition , context , setupResult ) ,
462487 ) ;
463488 } ,
@@ -467,15 +492,17 @@ export function createCommandFromDefinition<
467492 currentInvocation && ! currentInvocation . hasRun
468493 ? currentInvocation
469494 : beginInvocation ( context ) ;
495+ invocation . context = context ;
470496 invocation . hasRun = true ;
471497
472498 const setupResult = await invocation . setup ;
473- invocation . runResult = await runInInjectionContext ( targetInjector , ( ) =>
474- definition . run . call ( definition , context , setupResult ) ,
499+ invocation . runResult = await runInInjectionContext (
500+ invocation . injector ,
501+ ( ) => definition . run . call ( definition , context , setupResult ) ,
475502 ) ;
476503
477504 if ( definition . shortcuts ) {
478- attachShortcuts ( context , setupResult ) ;
505+ attachShortcuts ( invocation , context , setupResult ) ;
479506 }
480507 } ,
481508 } ;
0 commit comments