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.
- package/README.md +94 -90
- package/cjm/container/Container.js +3 -0
- package/cjm/container/EmptyContainer.js +3 -0
- package/cjm/hooks/AsyncHookExecutionStrategy.js +15 -0
- package/cjm/hooks/HookContext.js +4 -4
- package/cjm/hooks/HookExecutionStrategy.js +68 -0
- package/cjm/hooks/ParallelAsync.js +17 -0
- package/cjm/hooks/SequentialAsync.js +15 -0
- package/cjm/hooks/SequentialSync.js +14 -0
- package/cjm/hooks/onConstruct.js +7 -9
- package/cjm/hooks/onResolved.js +13 -9
- package/cjm/hooks/onScopeDisposed.js +5 -7
- package/cjm/hooks/resolveHooks.js +1 -18
- package/cjm/index.js +14 -11
- package/esm/container/Container.js +3 -0
- package/esm/container/EmptyContainer.js +3 -0
- package/esm/hooks/AsyncHookExecutionStrategy.js +11 -0
- package/esm/hooks/HookContext.js +2 -2
- package/esm/hooks/HookExecutionStrategy.js +62 -0
- package/esm/hooks/ParallelAsync.js +13 -0
- package/esm/hooks/SequentialAsync.js +11 -0
- package/esm/hooks/SequentialSync.js +10 -0
- package/esm/hooks/onConstruct.js +6 -8
- package/esm/hooks/onResolved.js +11 -8
- package/esm/hooks/onScopeDisposed.js +4 -6
- package/esm/hooks/resolveHooks.js +1 -14
- package/esm/index.js +10 -6
- package/package.json +1 -1
- package/typings/container/Container.d.ts +1 -0
- package/typings/container/EmptyContainer.d.ts +2 -0
- package/typings/container/IContainer.d.ts +2 -1
- package/typings/hooks/AsyncHookExecutionStrategy.d.ts +10 -0
- package/typings/hooks/HookContext.d.ts +3 -3
- package/typings/hooks/HookExecutionStrategy.d.ts +35 -0
- package/typings/hooks/ParallelAsync.d.ts +5 -0
- package/typings/hooks/SequentialAsync.d.ts +5 -0
- package/typings/hooks/SequentialSync.d.ts +4 -0
- package/typings/hooks/onConstruct.d.ts +7 -8
- package/typings/hooks/onResolved.d.ts +5 -5
- package/typings/hooks/onScopeDisposed.d.ts +4 -5
- package/typings/hooks/resolveHooks.d.ts +0 -6
- package/typings/index.d.ts +10 -6
- package/cjm/hooks/HooksRunner.js +0 -50
- package/esm/hooks/HooksRunner.js +0 -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.
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
`
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
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
|
|
2867
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2889
|
-
|
|
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
|
-
|
|
2894
|
-
|
|
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
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
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
|
|
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;
|
|
2955
|
-
const container = new Container(
|
|
2956
|
-
|
|
2957
|
-
new
|
|
2958
|
-
|
|
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?.
|
|
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
|
|
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
|
-
|
|
2994
|
-
new
|
|
2995
|
-
|
|
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
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
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
|
|
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;
|
|
3053
|
-
const container = new Container(
|
|
3054
|
-
|
|
3055
|
-
new
|
|
3056
|
-
|
|
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?.
|
|
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,
|
|
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
|
-
//
|
|
3157
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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;
|
package/cjm/hooks/HookContext.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createHookContextFactory = exports.
|
|
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
|
|
39
|
-
exports.
|
|
40
|
-
const createHookContextFactory = ({ args = [] } = {}) => (Target, scope, methodName) => (0, exports.
|
|
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;
|
package/cjm/hooks/onConstruct.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OnConstructModule = exports.onConstruct =
|
|
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
|
-
|
|
11
|
-
constructor(
|
|
12
|
-
this.
|
|
8
|
+
strategy;
|
|
9
|
+
constructor(strategy) {
|
|
10
|
+
this.strategy = strategy;
|
|
13
11
|
}
|
|
14
|
-
applyTo(
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
applyTo(container) {
|
|
13
|
+
container.getInjector().onConstructed((instance, scope) => {
|
|
14
|
+
this.strategy.execute(instance, { scope });
|
|
17
15
|
});
|
|
18
16
|
}
|
|
19
17
|
}
|
package/cjm/hooks/onResolved.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolved = exports.OnResolvedModule = exports.onResolved =
|
|
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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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(
|
|
13
|
-
this.runHooks = runHooks(
|
|
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 = (
|
|
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 =
|
|
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
|
-
|
|
11
|
-
constructor(
|
|
12
|
-
this.
|
|
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
|
-
|
|
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.
|
|
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;
|