@@ -692,9 +692,8 @@ func (c *Client) evaluate(
692692 // ensure that the same provider & hooks are used across this transaction to avoid unexpected behaviour
693693 provider , globalHooks , globalEvalCtx := c .api .ForEvaluation (c .metadata .domain )
694694
695- evalCtx = mergeContexts (evalCtx , c .evaluationContext , TransactionContext (ctx ), globalEvalCtx ) // API (global) -> transaction -> client -> invocation
696- apiClientInvocationProviderHooks := slices .Concat (globalHooks , c .hooks , options .hooks , provider .Hooks ()) // API, Client, Invocation, Provider
697- providerInvocationClientAPIHooks := slices .Concat (provider .Hooks (), options .hooks , c .hooks , globalHooks ) // Provider, Invocation, Client, API
695+ evalCtx = mergeContexts (evalCtx , c .evaluationContext , TransactionContext (ctx ), globalEvalCtx ) // API (global) -> transaction -> client -> invocation
696+ hooks := slices .Concat (globalHooks , c .hooks , options .hooks , provider .Hooks ()) // API, Client, Invocation, Provider
698697
699698 var err error
700699 hookCtx := HookContext {
@@ -707,29 +706,29 @@ func (c *Client) evaluate(
707706 }
708707
709708 defer func () {
710- c .finallyHooks (ctx , hookCtx , providerInvocationClientAPIHooks , evalDetails , options )
709+ c .finallyHooks (ctx , hookCtx , hooks , evalDetails , options )
711710 }()
712711
713712 // bypass short-circuit logic for the Noop provider; it is essentially stateless and a "special case"
714713 if _ , ok := provider .(NoopProvider ); ! ok {
715714 // short circuit if provider is in NOT READY state
716715 if c .State () == NotReadyState {
717- c .errorHooks (ctx , hookCtx , providerInvocationClientAPIHooks , ProviderNotReadyError , options )
716+ c .errorHooks (ctx , hookCtx , hooks , ProviderNotReadyError , options )
718717 return evalDetails , ProviderNotReadyError
719718 }
720719
721720 // short circuit if provider is in FATAL state
722721 if c .State () == FatalState {
723- c .errorHooks (ctx , hookCtx , providerInvocationClientAPIHooks , ProviderFatalError , options )
722+ c .errorHooks (ctx , hookCtx , hooks , ProviderFatalError , options )
724723 return evalDetails , ProviderFatalError
725724 }
726725 }
727726
728- evalCtx , err = c .beforeHooks (ctx , hookCtx , apiClientInvocationProviderHooks , evalCtx , options )
727+ evalCtx , err = c .beforeHooks (ctx , hookCtx , hooks , evalCtx , options )
729728 hookCtx .evaluationContext = evalCtx
730729 if err != nil {
731730 err = fmt .Errorf ("before hook: %w" , err )
732- c .errorHooks (ctx , hookCtx , providerInvocationClientAPIHooks , err , options )
731+ c .errorHooks (ctx , hookCtx , hooks , err , options )
733732 return evalDetails , err
734733 }
735734
@@ -763,17 +762,17 @@ func (c *Client) evaluate(
763762 err = resolution .Error ()
764763 if err != nil {
765764 err = fmt .Errorf ("error code: %w" , err )
766- c .errorHooks (ctx , hookCtx , providerInvocationClientAPIHooks , err , options )
765+ c .errorHooks (ctx , hookCtx , hooks , err , options )
767766 evalDetails .ResolutionDetail = resolution .ResolutionDetail ()
768767 evalDetails .Reason = ErrorReason
769768 return evalDetails , err
770769 }
771770 evalDetails .Value = resolution .Value
772771 evalDetails .ResolutionDetail = resolution .ResolutionDetail ()
773772
774- if err := c .afterHooks (ctx , hookCtx , providerInvocationClientAPIHooks , evalDetails , options ); err != nil {
773+ if err := c .afterHooks (ctx , hookCtx , hooks , evalDetails , options ); err != nil {
775774 err = fmt .Errorf ("after hook: %w" , err )
776- c .errorHooks (ctx , hookCtx , providerInvocationClientAPIHooks , err , options )
775+ c .errorHooks (ctx , hookCtx , hooks , err , options )
777776 return evalDetails , err
778777 }
779778
@@ -791,6 +790,8 @@ func flattenContext(evalCtx EvaluationContext) FlattenedContext {
791790 return flatCtx
792791}
793792
793+ // beforeHooks executes the Before hook for each hook in the collection.
794+ // Hooks are executed in forward order: API → Client → Invocation → Provider.
794795func (c * Client ) beforeHooks (
795796 ctx context.Context , hookCtx HookContext , hooks []Hook , evalCtx EvaluationContext , options EvaluationOptions ,
796797) (EvaluationContext , error ) {
@@ -807,10 +808,13 @@ func (c *Client) beforeHooks(
807808 return mergeContexts (hookCtx .evaluationContext , evalCtx ), nil
808809}
809810
811+ // afterHooks executes the After hook for each hook in the collection.
812+ // Hooks are executed in reverse order: Provider → Invocation → Client → API.
810813func (c * Client ) afterHooks (
811814 ctx context.Context , hookCtx HookContext , hooks []Hook , evalDetails InterfaceEvaluationDetails , options EvaluationOptions ,
812815) error {
813- for _ , hook := range hooks {
816+ // reverse order
817+ for _ , hook := range slices .Backward (hooks ) {
814818 if err := hook .After (ctx , hookCtx , evalDetails , options .hookHints ); err != nil {
815819 return err
816820 }
@@ -819,14 +823,20 @@ func (c *Client) afterHooks(
819823 return nil
820824}
821825
826+ // errorHooks executes the Error hook for each hook in the collection.
827+ // Hooks are executed in reverse order: Provider → Invocation → Client → API.
822828func (c * Client ) errorHooks (ctx context.Context , hookCtx HookContext , hooks []Hook , err error , options EvaluationOptions ) {
823- for _ , hook := range hooks {
829+ // reverse order
830+ for _ , hook := range slices .Backward (hooks ) {
824831 hook .Error (ctx , hookCtx , err , options .hookHints )
825832 }
826833}
827834
835+ // finallyHooks executes the Finally hook for each hook in the collection.
836+ // Hooks are executed in reverse order: Provider → Invocation → Client → API.
828837func (c * Client ) finallyHooks (ctx context.Context , hookCtx HookContext , hooks []Hook , evalDetails InterfaceEvaluationDetails , options EvaluationOptions ) {
829- for _ , hook := range hooks {
838+ // reverse order
839+ for _ , hook := range slices .Backward (hooks ) {
830840 hook .Finally (ctx , hookCtx , evalDetails , options .hookHints )
831841 }
832842}
0 commit comments