ts-ioc-container 63.0.0 → 64.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +94 -90
  2. package/cjm/container/Container.js +3 -0
  3. package/cjm/container/EmptyContainer.js +3 -0
  4. package/cjm/hooks/AsyncHookExecutionStrategy.js +15 -0
  5. package/cjm/hooks/HookContext.js +4 -4
  6. package/cjm/hooks/HookExecutionStrategy.js +68 -0
  7. package/cjm/hooks/ParallelAsync.js +17 -0
  8. package/cjm/hooks/SequentialAsync.js +15 -0
  9. package/cjm/hooks/SequentialSync.js +14 -0
  10. package/cjm/hooks/onConstruct.js +7 -9
  11. package/cjm/hooks/onResolved.js +13 -9
  12. package/cjm/hooks/onScopeDisposed.js +5 -7
  13. package/cjm/hooks/resolveHooks.js +1 -18
  14. package/cjm/index.js +14 -11
  15. package/esm/container/Container.js +3 -0
  16. package/esm/container/EmptyContainer.js +3 -0
  17. package/esm/hooks/AsyncHookExecutionStrategy.js +11 -0
  18. package/esm/hooks/HookContext.js +2 -2
  19. package/esm/hooks/HookExecutionStrategy.js +62 -0
  20. package/esm/hooks/ParallelAsync.js +13 -0
  21. package/esm/hooks/SequentialAsync.js +11 -0
  22. package/esm/hooks/SequentialSync.js +10 -0
  23. package/esm/hooks/onConstruct.js +6 -8
  24. package/esm/hooks/onResolved.js +11 -8
  25. package/esm/hooks/onScopeDisposed.js +4 -6
  26. package/esm/hooks/resolveHooks.js +1 -14
  27. package/esm/index.js +10 -6
  28. package/package.json +1 -1
  29. package/typings/container/Container.d.ts +1 -0
  30. package/typings/container/EmptyContainer.d.ts +2 -0
  31. package/typings/container/IContainer.d.ts +2 -1
  32. package/typings/hooks/AsyncHookExecutionStrategy.d.ts +10 -0
  33. package/typings/hooks/HookContext.d.ts +3 -3
  34. package/typings/hooks/HookExecutionStrategy.d.ts +35 -0
  35. package/typings/hooks/ParallelAsync.d.ts +5 -0
  36. package/typings/hooks/SequentialAsync.d.ts +5 -0
  37. package/typings/hooks/SequentialSync.d.ts +4 -0
  38. package/typings/hooks/onConstruct.d.ts +7 -8
  39. package/typings/hooks/onResolved.d.ts +5 -5
  40. package/typings/hooks/onScopeDisposed.d.ts +4 -5
  41. package/typings/hooks/resolveHooks.d.ts +0 -6
  42. package/typings/index.d.ts +10 -6
  43. package/cjm/hooks/HooksRunner.js +0 -50
  44. package/esm/hooks/HooksRunner.js +0 -45
  45. package/typings/hooks/HooksRunner.d.ts +0 -19
package/README.md CHANGED
@@ -2833,22 +2833,39 @@ compensate for the bottom-up application order, so stacked decorators run in
2833
2833
  declaration order: `@onConstruct(h1) @onConstruct(h2)` runs `h1` before `h2`.
2834
2834
 
2835
2835
  Every hook may be sync or async — one decorator and one module take both, so
2836
- there is no separate async form to reach for. A `HooksRunner` runs a hook chain
2837
- eagerly and stays synchronous until a hook returns a promise, then awaits the
2838
- rest of that chain. Sync hooks therefore finish before `resolve` (or `dispose`)
2839
- returns, exactly as before; async ones are started there and settle afterwards,
2840
- so `resolve` returns before they finish. Instances that must expose readiness
2841
- should publish it themselves, for example by storing the pending promise on the
2842
- instance.
2843
-
2844
- `HooksRunner.execute` mirrors that: it returns `undefined` when nothing went
2845
- async and a promise otherwise, so a caller running its own hooks can `await` the
2846
- result either way.
2847
-
2848
- Every module takes an optional `onException` handler, and it catches both kinds
2849
- of failure — what a sync hook threw and what an async hook rejected with.
2850
- Without one, a sync hook throws out of the call and an async hook surfaces as an
2851
- unhandled promise rejection.
2836
+ there is no separate async form to reach for. *How* the hooks run is a separate
2837
+ choice: each module takes a `HookExecutionStrategy`, keyed to the hooks it runs
2838
+ (`onConstruct`, `onScopeDisposed`, `onResolved`, or a custom key), and the
2839
+ strategy decides the order, what is awaited, and where a failure goes
2840
+ ([ADR 0014](../../adr/0014-hook-execution-strategy.md)):
2841
+
2842
+ | Strategy | Members (decorated methods) | Hooks of one member | Awaits |
2843
+ | ----------------- | --------------------------- | ------------------------------------------- | ------ |
2844
+ | `SequentialSync` | one after another | in declaration order | no |
2845
+ | `SequentialAsync` | one after another | in order, or all at once (`methodStrategy`) | yes |
2846
+ | `ParallelAsync` | all at once | in order, or all at once (`methodStrategy`) | yes |
2847
+
2848
+ ```typescript
2849
+ const container = new Container()
2850
+ .useModule(new OnConstructModule(new SequentialSync({ key: 'onConstruct' })))
2851
+ .useModule(new OnDisposeModule(new ParallelAsync({ key: 'onScopeDisposed' })));
2852
+ ```
2853
+
2854
+ Resolution and disposal stay synchronous under every strategy: a run stays
2855
+ synchronous until a hook returns a promise, so sync hooks finish before
2856
+ `resolve` (or `dispose`) returns, and async ones are started there and settle
2857
+ afterwards. Instances that must expose readiness should publish it themselves,
2858
+ for example by storing the pending promise on the instance. The sync strategy
2859
+ never awaits — a hook that returns a promise under it is started but not
2860
+ observed.
2861
+
2862
+ `strategy.execute(instance, { scope })` runs the hooks of a custom key by hand;
2863
+ `predicate`, `createExecutionContext` and `mapExecutionContext` can be set on
2864
+ the strategy or per call.
2865
+
2866
+ A strategy takes an optional `onError: (scope) => (error) => void`, which
2867
+ receives both kinds of failure — what a sync hook threw and what an async hook
2868
+ rejected with. Without one, failures are dropped.
2852
2869
 
2853
2870
  ### Hook domains
2854
2871
 
@@ -2863,51 +2880,41 @@ abstraction raises the event, one hook type per domain:
2863
2880
  | **Injector** | `IInjector` | `InjectorHook` | `onConstructed(...)` |
2864
2881
  | **Provider** | `IProvider` | `ProviderHook` | `onResolved(...)`, or the [`onResolve`](#on-resolve) pipe |
2865
2882
 
2866
- A container never hands its injector out — the injector is configured first and
2867
- passed in at construction:
2883
+ A container passes its injector to every scope it creates, so **one injector**
2884
+ backs the whole scope tree, and `container.getInjector()` hands it out: an
2885
+ `onConstructed` hook registered through it covers every scope, whenever it was
2886
+ added. Scope hooks, by contrast, are copied into a child at `createScope` time,
2887
+ so a child inherits what its parent held then and later additions to either
2888
+ stay local.
2868
2889
 
2869
2890
  ```typescript
2870
- // Construction is the injector's event, not a scope's
2871
- const injector = new MetadataInjector().onConstructed((instance, scope) => metrics.built(instance, scope));
2872
-
2873
- const container = new Container({ injector, tags: ['application'] })
2891
+ const container = new Container({ tags: ['application'] })
2874
2892
  .onScopeCreated((scope) => audit.scopeOpened(scope))
2875
2893
  .onScopeDisposed((scope) => audit.scopeClosed(scope))
2876
2894
  .onRegistered((provider, key) => audit.registered(key));
2877
- ```
2878
-
2879
- A container passes its injector to every scope it creates, so **one injector**
2880
- backs the whole scope tree and an `onConstructed` hook covers all of it, whenever
2881
- it was added. Scope hooks, by contrast, are copied into a child at `createScope`
2882
- time, so a child inherits what its parent held then and later additions to either
2883
- stay local.
2884
-
2885
- The built-in modules follow the same split. `OnConstructModule` is an
2886
- **injector** module (`IInjectorModule`), applied with `injector.useModule(...)`:
2887
2895
 
2888
- ```typescript
2889
- const injector = new MetadataInjector().useModule(new OnConstructModule());
2890
- const container = new Container({ injector });
2896
+ // Construction is the injector's event, not a scope's
2897
+ container.getInjector().onConstructed((instance, scope) => metrics.built(instance, scope));
2891
2898
  ```
2892
2899
 
2893
- `OnDisposeModule` is a container module hooking `onScopeDisposed`, and
2894
- `OnResolvedModule` is a container module reaching every provider through
2895
- `onRegistered`.
2900
+ The built-in modules are all container modules: `OnConstructModule` reaches the
2901
+ injector through `getInjector()`, `OnDisposeModule` hooks `onScopeDisposed`, and
2902
+ `OnResolvedModule` reaches every provider through `onRegistered`.
2896
2903
 
2897
2904
  ### OnConstruct
2898
2905
 
2899
2906
  ```typescript
2900
2907
  import 'reflect-metadata';
2901
2908
  import {
2902
- MetadataInjector,
2903
2909
  OnConstructModule,
2904
2910
  Container,
2905
- type ExecutionContext,
2906
2911
  type HookFn,
2907
2912
  type IContainer,
2908
2913
  inject,
2909
2914
  onConstruct,
2910
2915
  Registration as R,
2916
+ SequentialAsync,
2917
+ SequentialSync,
2911
2918
  } from 'ts-ioc-container';
2912
2919
 
2913
2920
  const execute: HookFn = (ctx) => {
@@ -2931,9 +2938,10 @@ describe('onConstruct', function () {
2931
2938
  }
2932
2939
  }
2933
2940
 
2934
- const container = new Container({
2935
- injector: new MetadataInjector().useModule(new OnConstructModule()),
2936
- }).addRegistration(R.fromValue('postgres://localhost:5432').bindTo('ConnectionString'));
2941
+ // The module takes a strategy for how the hooks run; the strategy is keyed to the hooks it runs.
2942
+ const container = new Container()
2943
+ .useModule(new OnConstructModule(new SequentialSync({ key: 'onConstruct' })))
2944
+ .addRegistration(R.fromValue('postgres://localhost:5432').bindTo('ConnectionString'));
2937
2945
 
2938
2946
  const db = container.resolve(DatabaseConnection);
2939
2947
 
@@ -2941,7 +2949,7 @@ describe('onConstruct', function () {
2941
2949
  expect(db.connectionString).toBe('postgres://localhost:5432');
2942
2950
  });
2943
2951
 
2944
- it('should forward hook exceptions to the onException handler with the execution context', function () {
2952
+ it('should forward hook exceptions to the onError handler with the scope', function () {
2945
2953
  const failure = new Error('boom');
2946
2954
 
2947
2955
  class BrokenService {
@@ -2951,36 +2959,24 @@ describe('onConstruct', function () {
2951
2959
  init() {}
2952
2960
  }
2953
2961
 
2954
- let captured: { ex: unknown; context: ExecutionContext } | undefined;
2955
- const container = new Container({
2956
- injector: new MetadataInjector().useModule(
2957
- new OnConstructModule((ex, context) => {
2958
- captured = { ex, context };
2962
+ let captured: { ex: unknown; scope: IContainer } | undefined;
2963
+ const container = new Container().useModule(
2964
+ new OnConstructModule(
2965
+ new SequentialSync({
2966
+ key: 'onConstruct',
2967
+ onError: (scope) => (ex) => {
2968
+ captured = { ex, scope };
2969
+ },
2959
2970
  }),
2960
2971
  ),
2961
- });
2972
+ );
2962
2973
 
2963
2974
  expect(() => container.resolve(BrokenService)).not.toThrow();
2964
2975
  expect(captured?.ex).toBe(failure);
2965
- expect(captured?.context.scope).toBe(container);
2966
- });
2967
-
2968
- it('should rethrow hook exceptions when no onException handler is provided', function () {
2969
- const failure = new Error('boom');
2970
-
2971
- class BrokenService {
2972
- @onConstruct(() => {
2973
- throw failure;
2974
- })
2975
- init() {}
2976
- }
2977
-
2978
- const container = new Container({ injector: new MetadataInjector().useModule(new OnConstructModule()) });
2979
-
2980
- expect(() => container.resolve(BrokenService)).toThrow(failure);
2976
+ expect(captured?.scope).toBe(container);
2981
2977
  });
2982
2978
 
2983
- it('should expose the resolving scope through the execution context', function () {
2979
+ it('should expose the resolving scope to the onError handler', function () {
2984
2980
  class BrokenService {
2985
2981
  @onConstruct(() => {
2986
2982
  throw new Error('boom');
@@ -2989,13 +2985,16 @@ describe('onConstruct', function () {
2989
2985
  }
2990
2986
 
2991
2987
  let scope: IContainer | undefined;
2992
- const container = new Container({
2993
- injector: new MetadataInjector().useModule(
2994
- new OnConstructModule((_ex, context) => {
2995
- scope = context.scope;
2988
+ const container = new Container().useModule(
2989
+ new OnConstructModule(
2990
+ new SequentialSync({
2991
+ key: 'onConstruct',
2992
+ onError: (s) => () => {
2993
+ scope = s;
2994
+ },
2996
2995
  }),
2997
2996
  ),
2998
- });
2997
+ );
2999
2998
  const child = container.createScope();
3000
2999
 
3001
3000
  child.resolve(BrokenService);
@@ -3026,9 +3025,10 @@ describe('onConstruct', function () {
3026
3025
  }
3027
3026
  }
3028
3027
 
3029
- const container = new Container({
3030
- injector: new MetadataInjector().useModule(new OnConstructModule()),
3031
- }).addRegistration(R.fromValue('postgres://localhost:5432').bindTo('ConnectionString'));
3028
+ // An async strategy awaits the hooks; resolution itself still does not wait for them.
3029
+ const container = new Container()
3030
+ .useModule(new OnConstructModule(new SequentialAsync({ key: 'onConstruct' })))
3031
+ .addRegistration(R.fromValue('postgres://localhost:5432').bindTo('ConnectionString'));
3032
3032
 
3033
3033
  const db = container.resolve(DatabaseConnection);
3034
3034
 
@@ -3041,7 +3041,7 @@ describe('onConstruct', function () {
3041
3041
  expect(db.connectionString).toBe('postgres://localhost:5432');
3042
3042
  });
3043
3043
 
3044
- it('should forward rejected hooks to the onException handler with the execution context', async function () {
3044
+ it('should forward rejected hooks to the onError handler with the scope', async function () {
3045
3045
  const failure = new Error('boom');
3046
3046
 
3047
3047
  class BrokenService {
@@ -3049,14 +3049,17 @@ describe('onConstruct', function () {
3049
3049
  init() {}
3050
3050
  }
3051
3051
 
3052
- let captured: { ex: unknown; context: ExecutionContext } | undefined;
3053
- const container = new Container({
3054
- injector: new MetadataInjector().useModule(
3055
- new OnConstructModule((ex, context) => {
3056
- captured = { ex, context };
3052
+ let captured: { ex: unknown; scope: IContainer } | undefined;
3053
+ const container = new Container().useModule(
3054
+ new OnConstructModule(
3055
+ new SequentialAsync({
3056
+ key: 'onConstruct',
3057
+ onError: (scope) => (ex) => {
3058
+ captured = { ex, scope };
3059
+ },
3057
3060
  }),
3058
3061
  ),
3059
- });
3062
+ );
3060
3063
 
3061
3064
  const child = container.createScope();
3062
3065
  child.resolve(BrokenService);
@@ -3064,7 +3067,7 @@ describe('onConstruct', function () {
3064
3067
  await vi.waitFor(() => expect(captured).toBeDefined());
3065
3068
 
3066
3069
  expect(captured?.ex).toBe(failure);
3067
- expect(captured?.context.scope).toBe(child);
3070
+ expect(captured?.scope).toBe(child);
3068
3071
  });
3069
3072
  });
3070
3073
 
@@ -3083,6 +3086,7 @@ import {
3083
3086
  onScopeDisposed,
3084
3087
  register,
3085
3088
  Registration as R,
3089
+ SequentialSync,
3086
3090
  singleton,
3087
3091
  } from 'ts-ioc-container';
3088
3092
 
@@ -3118,7 +3122,7 @@ class Logger {
3118
3122
  describe('onScopeDisposed', function () {
3119
3123
  it('should invoke hooks on all instances when container is disposed', function () {
3120
3124
  const container = new Container()
3121
- .useModule(new OnDisposeModule())
3125
+ .useModule(new OnDisposeModule(new SequentialSync({ key: 'onScopeDisposed' })))
3122
3126
  .addRegistration(R.fromClass(Logger))
3123
3127
  .addRegistration(R.fromClass(LogsRepo));
3124
3128
 
@@ -3138,7 +3142,7 @@ describe('onScopeDisposed', function () {
3138
3142
 
3139
3143
  ```typescript
3140
3144
  import 'reflect-metadata';
3141
- import { append, Container, hook, HooksRunner, injectProp, Registration } from 'ts-ioc-container';
3145
+ import { append, Container, hook, SequentialSync, injectProp, Registration } from 'ts-ioc-container';
3142
3146
 
3143
3147
  /**
3144
3148
  * UI Components - Property Injection
@@ -3153,8 +3157,8 @@ import { append, Container, hook, HooksRunner, injectProp, Registration } from '
3153
3157
 
3154
3158
  describe('inject property', () => {
3155
3159
  it('should inject property', () => {
3156
- // Runner for the 'onInit' lifecycle hook
3157
- const onInitHookRunner = new HooksRunner('onInit');
3160
+ // Strategy for the 'onInit' lifecycle hook
3161
+ const onInitStrategy = new SequentialSync({ key: 'onInit' });
3158
3162
 
3159
3163
  class UserViewModel {
3160
3164
  // Inject 'GreetingService' into 'greeting' property during 'onInit'
@@ -3172,14 +3176,14 @@ describe('inject property', () => {
3172
3176
  const viewModel = container.resolve(UserViewModel);
3173
3177
 
3174
3178
  // 2. Run lifecycle hooks to inject properties
3175
- onInitHookRunner.execute(viewModel, { scope: container });
3179
+ onInitStrategy.execute(viewModel, { scope: container });
3176
3180
 
3177
3181
  expect(viewModel.greetingService).toBe('Hello');
3178
3182
  expect(viewModel.display()).toBe('Hello User');
3179
3183
  });
3180
3184
 
3181
3185
  it('should read the applied instance property via getProperty', () => {
3182
- const onInitHookRunner = new HooksRunner('onInit');
3186
+ const onInitStrategy = new SequentialSync({ key: 'onInit' });
3183
3187
 
3184
3188
  let injectedValue: unknown;
3185
3189
 
@@ -3196,7 +3200,7 @@ describe('inject property', () => {
3196
3200
  const container = new Container().addRegistration(Registration.fromValue('Hello').bindToKey('GreetingService'));
3197
3201
 
3198
3202
  const viewModel = container.resolve(UserViewModel);
3199
- onInitHookRunner.execute(viewModel, { scope: container });
3203
+ onInitStrategy.execute(viewModel, { scope: container });
3200
3204
 
3201
3205
  expect(injectedValue).toBe('Hello');
3202
3206
  });
@@ -125,6 +125,9 @@ class Container {
125
125
  getRegistrations() {
126
126
  return [...this.parent.getRegistrations(), ...this.registrations];
127
127
  }
128
+ getInjector() {
129
+ return this.injector;
130
+ }
128
131
  hasRegistration(key) {
129
132
  return this.registrations.some((r) => r.getKeyOrFail() === key) || this.parent.hasRegistration(key);
130
133
  }
@@ -23,6 +23,9 @@ class EmptyContainer {
23
23
  createScope() {
24
24
  throw new MethodNotImplementedError_1.MethodNotImplementedError();
25
25
  }
26
+ getInjector() {
27
+ throw new MethodNotImplementedError_1.MethodNotImplementedError();
28
+ }
26
29
  autoResolve(options) {
27
30
  throw new MethodNotImplementedError_1.MethodNotImplementedError();
28
31
  }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AsyncHookExecutionStrategy = void 0;
4
+ const HookExecutionStrategy_1 = require("./HookExecutionStrategy");
5
+ class AsyncHookExecutionStrategy extends HookExecutionStrategy_1.HookExecutionStrategy {
6
+ methodStrategy;
7
+ constructor({ methodStrategy = 'sequential', ...props }) {
8
+ super(props);
9
+ this.methodStrategy = methodStrategy;
10
+ }
11
+ runMember({ hooks, context }) {
12
+ return this.methodStrategy === 'parallel' ? (0, HookExecutionStrategy_1.runAtOnce)(hooks, context) : (0, HookExecutionStrategy_1.runInOrder)(hooks, context);
13
+ }
14
+ }
15
+ exports.AsyncHookExecutionStrategy = AsyncHookExecutionStrategy;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createHookContextFactory = exports.createHookContext = exports.HookContext = void 0;
3
+ exports.createHookContextFactory = exports.createHookExecutionContext = exports.HookContext = void 0;
4
4
  const MetadataInjector_1 = require("../injector/MetadataInjector");
5
5
  class HookContext {
6
6
  instance;
@@ -35,7 +35,7 @@ class HookContext {
35
35
  }
36
36
  }
37
37
  exports.HookContext = HookContext;
38
- const createHookContext = (Target, scope, methodName = 'constructor') => new HookContext(Target, scope, methodName);
39
- exports.createHookContext = createHookContext;
40
- const createHookContextFactory = ({ args = [] } = {}) => (Target, scope, methodName) => (0, exports.createHookContext)(Target, scope, methodName).setInitialArgs(...args);
38
+ const createHookExecutionContext = (Target, scope, methodName = 'constructor') => new HookContext(Target, scope, methodName);
39
+ exports.createHookExecutionContext = createHookExecutionContext;
40
+ const createHookContextFactory = ({ args = [] } = {}) => (Target, scope, methodName) => (0, exports.createHookExecutionContext)(Target, scope, methodName).setInitialArgs(...args);
41
41
  exports.createHookContextFactory = createHookContextFactory;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HookExecutionStrategy = exports.runAtOnce = exports.runInOrder = void 0;
4
+ const target_1 = require("../metadata/target");
5
+ const hook_1 = require("./hook");
6
+ const HookContext_1 = require("./HookContext");
7
+ const runInOrder = (hooks, context, from = 0) => {
8
+ for (let i = from; i < hooks.length; i++) {
9
+ const result = hooks[i](context);
10
+ if (result instanceof Promise) {
11
+ return result.then(() => (0, exports.runInOrder)(hooks, context, i + 1));
12
+ }
13
+ }
14
+ };
15
+ exports.runInOrder = runInOrder;
16
+ const runAtOnce = (hooks, context) => {
17
+ const pending = [];
18
+ for (const hook of hooks) {
19
+ const result = hook(context);
20
+ if (result instanceof Promise) {
21
+ pending.push(result);
22
+ }
23
+ }
24
+ return pending.length > 0 ? Promise.all(pending).then(() => undefined) : undefined;
25
+ };
26
+ exports.runAtOnce = runAtOnce;
27
+ class HookExecutionStrategy {
28
+ key;
29
+ onError;
30
+ options;
31
+ hooksByClass = new WeakMap();
32
+ constructor({ key, onError, createExecutionContext = HookContext_1.createHookExecutionContext, mapExecutionContext = (context) => context, predicate = () => true, }) {
33
+ this.key = key;
34
+ this.onError = onError;
35
+ this.options = { createExecutionContext, mapExecutionContext, predicate };
36
+ }
37
+ hasHooks(target) {
38
+ return (0, hook_1.hasHooks)(target, this.key);
39
+ }
40
+ execute(target, { scope, ...overrides }) {
41
+ const report = (ex) => this.onError?.(scope)(ex);
42
+ try {
43
+ this.processHooks(this.collect(target, scope, overrides))?.catch(report);
44
+ }
45
+ catch (ex) {
46
+ report(ex);
47
+ }
48
+ }
49
+ collect(target, scope, { createExecutionContext = this.options.createExecutionContext, mapExecutionContext = this.options.mapExecutionContext, predicate = this.options.predicate, }) {
50
+ const members = [];
51
+ for (const { methodName, hooks } of this.hooksOf(target)) {
52
+ if (predicate(methodName)) {
53
+ members.push({ hooks, context: mapExecutionContext(createExecutionContext(target, scope, methodName)) });
54
+ }
55
+ }
56
+ return members;
57
+ }
58
+ hooksOf(target) {
59
+ const Target = (0, target_1.resolveConstructor)(target);
60
+ let hooks = this.hooksByClass.get(Target);
61
+ if (!hooks) {
62
+ hooks = Array.from((0, hook_1.getHooks)(Target, this.key), ([methodName, fns]) => ({ methodName, hooks: fns.map(hook_1.toHookFn) }));
63
+ this.hooksByClass.set(Target, hooks);
64
+ }
65
+ return hooks;
66
+ }
67
+ }
68
+ exports.HookExecutionStrategy = HookExecutionStrategy;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParallelAsync = void 0;
4
+ const AsyncHookExecutionStrategy_1 = require("./AsyncHookExecutionStrategy");
5
+ class ParallelAsync extends AsyncHookExecutionStrategy_1.AsyncHookExecutionStrategy {
6
+ processHooks(members) {
7
+ const pending = [];
8
+ for (const member of members) {
9
+ const result = this.runMember(member);
10
+ if (result) {
11
+ pending.push(result);
12
+ }
13
+ }
14
+ return pending.length > 0 ? Promise.all(pending).then(() => undefined) : undefined;
15
+ }
16
+ }
17
+ exports.ParallelAsync = ParallelAsync;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SequentialAsync = void 0;
4
+ const AsyncHookExecutionStrategy_1 = require("./AsyncHookExecutionStrategy");
5
+ class SequentialAsync extends AsyncHookExecutionStrategy_1.AsyncHookExecutionStrategy {
6
+ processHooks(members, from = 0) {
7
+ for (let i = from; i < members.length; i++) {
8
+ const result = this.runMember(members[i]);
9
+ if (result) {
10
+ return result.then(() => this.processHooks(members, i + 1));
11
+ }
12
+ }
13
+ }
14
+ }
15
+ exports.SequentialAsync = SequentialAsync;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SequentialSync = void 0;
4
+ const HookExecutionStrategy_1 = require("./HookExecutionStrategy");
5
+ class SequentialSync extends HookExecutionStrategy_1.HookExecutionStrategy {
6
+ processHooks(members) {
7
+ for (const { hooks, context } of members) {
8
+ for (const hook of hooks) {
9
+ hook(context);
10
+ }
11
+ }
12
+ }
13
+ }
14
+ exports.SequentialSync = SequentialSync;
@@ -1,19 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OnConstructModule = exports.onConstruct = exports.onConstructHooksRunner = void 0;
3
+ exports.OnConstructModule = exports.onConstruct = void 0;
4
4
  const hook_1 = require("./hook");
5
- const HooksRunner_1 = require("./HooksRunner");
6
- exports.onConstructHooksRunner = new HooksRunner_1.HooksRunner('onConstruct');
7
5
  const onConstruct = (...fns) => (0, hook_1.hook)('onConstruct', (0, hook_1.prependHooks)(...fns));
8
6
  exports.onConstruct = onConstruct;
9
7
  class OnConstructModule {
10
- onException;
11
- constructor(onException) {
12
- this.onException = onException;
8
+ strategy;
9
+ constructor(strategy) {
10
+ this.strategy = strategy;
13
11
  }
14
- applyTo(injector) {
15
- injector.onConstructed((instance, scope) => {
16
- (0, HooksRunner_1.runHooks)(exports.onConstructHooksRunner, instance, scope, this.onException);
12
+ applyTo(container) {
13
+ container.getInjector().onConstructed((instance, scope) => {
14
+ this.strategy.execute(instance, { scope });
17
15
  });
18
16
  }
19
17
  }
@@ -1,16 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolved = exports.OnResolvedModule = exports.onResolved = exports.onResolvedHooksRunner = void 0;
4
- const HooksRunner_1 = require("./HooksRunner");
3
+ exports.resolved = exports.OnResolvedModule = exports.onResolved = void 0;
5
4
  const IRegistration_1 = require("../registration/IRegistration");
6
- const resolveHooks_1 = require("./resolveHooks");
7
- exports.onResolvedHooksRunner = new HooksRunner_1.HooksRunner('onResolved');
8
- exports.onResolved = (0, resolveHooks_1.resolvedHook)('onResolved');
9
- const runHooks = (onException) => (0, resolveHooks_1.forEachResolvedObject)((0, resolveHooks_1.executeHooks)(exports.onResolvedHooksRunner, onException));
5
+ const basic_1 = require("../utils/basic");
6
+ const hook_1 = require("./hook");
7
+ const onResolved = (...hooks) => (0, hook_1.hook)('onResolved', (0, hook_1.prependHooks)(...hooks));
8
+ exports.onResolved = onResolved;
9
+ const runHooks = (strategy) => (dependency, scope) => {
10
+ if (basic_1.Is.object(dependency)) {
11
+ strategy.execute(dependency, { scope });
12
+ }
13
+ };
10
14
  class OnResolvedModule {
11
15
  runHooks;
12
- constructor(onException) {
13
- this.runHooks = runHooks(onException);
16
+ constructor(strategy) {
17
+ this.runHooks = runHooks(strategy);
14
18
  }
15
19
  applyTo(container) {
16
20
  container.onRegistered((provider) => {
@@ -19,5 +23,5 @@ class OnResolvedModule {
19
23
  }
20
24
  }
21
25
  exports.OnResolvedModule = OnResolvedModule;
22
- const resolved = (onException) => (0, IRegistration_1.registerPipe)((p) => p.onResolved(runHooks(onException)));
26
+ const resolved = (strategy) => (0, IRegistration_1.registerPipe)((p) => p.onResolved(runHooks(strategy)));
23
27
  exports.resolved = resolved;
@@ -1,20 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OnDisposeModule = exports.onScopeDisposed = exports.onScopeDisposedHooksRunner = void 0;
3
+ exports.OnDisposeModule = exports.onScopeDisposed = void 0;
4
4
  const hook_1 = require("./hook");
5
- const HooksRunner_1 = require("./HooksRunner");
6
- exports.onScopeDisposedHooksRunner = new HooksRunner_1.HooksRunner('onScopeDisposed');
7
5
  const onScopeDisposed = (...fns) => (0, hook_1.hook)('onScopeDisposed', (0, hook_1.prependHooks)(...fns));
8
6
  exports.onScopeDisposed = onScopeDisposed;
9
7
  class OnDisposeModule {
10
- onException;
11
- constructor(onException) {
12
- this.onException = onException;
8
+ strategy;
9
+ constructor(strategy) {
10
+ this.strategy = strategy;
13
11
  }
14
12
  applyTo(container) {
15
13
  container.onScopeDisposed((scope) => {
16
14
  for (const instance of scope.getInstances()) {
17
- (0, HooksRunner_1.runHooks)(exports.onScopeDisposedHooksRunner, instance, scope, this.onException);
15
+ this.strategy.execute(instance, { scope });
18
16
  }
19
17
  });
20
18
  }
@@ -1,14 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.executeHooks = exports.forEachResolvedObject = exports.resolvedHook = exports.oncePerInstance = exports.invokeMethod = void 0;
4
- const HooksRunner_1 = require("./HooksRunner");
3
+ exports.oncePerInstance = void 0;
5
4
  const hook_1 = require("./hook");
6
- const basic_1 = require("../utils/basic");
7
- const invokeMethod = (context) => {
8
- context.invokeMethod();
9
- };
10
- exports.invokeMethod = invokeMethod;
11
- const toHooks = (hooks) => (hooks.length > 0 ? hooks : [exports.invokeMethod]);
12
5
  const oncePerInstance = (execute) => {
13
6
  const invokedInstances = new WeakSet();
14
7
  return (context) => {
@@ -20,13 +13,3 @@ const oncePerInstance = (execute) => {
20
13
  };
21
14
  };
22
15
  exports.oncePerInstance = oncePerInstance;
23
- const resolvedHook = (key) => (...hooks) => (0, hook_1.hook)(key, (0, hook_1.prependHooks)(...toHooks(hooks)));
24
- exports.resolvedHook = resolvedHook;
25
- const forEachResolvedObject = (run) => (dependency, scope) => {
26
- if (basic_1.Is.object(dependency)) {
27
- run(dependency, scope);
28
- }
29
- };
30
- exports.forEachResolvedObject = forEachResolvedObject;
31
- const executeHooks = (runner, onException) => (dependency, scope) => (0, HooksRunner_1.runHooks)(runner, dependency, scope, onException);
32
- exports.executeHooks = executeHooks;