ts-ioc-container 60.3.0 → 62.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 +111 -65
- package/cjm/container/Container.js +16 -26
- package/cjm/container/EmptyContainer.js +3 -6
- package/cjm/hooks/HooksRunner.js +30 -22
- package/cjm/hooks/onConstruct.js +3 -11
- package/cjm/hooks/onResolved.js +8 -4
- package/cjm/hooks/onScopeDisposed.js +22 -0
- package/cjm/hooks/resolveHooks.js +3 -14
- package/cjm/index.js +8 -19
- package/cjm/injector/IInjector.js +12 -0
- package/cjm/provider/Provider.js +6 -6
- package/cjm/registration/IRegistration.js +1 -1
- package/esm/container/Container.js +16 -26
- package/esm/container/EmptyContainer.js +3 -6
- package/esm/hooks/HooksRunner.js +28 -21
- package/esm/hooks/onConstruct.js +4 -12
- package/esm/hooks/onResolved.js +8 -4
- package/esm/hooks/onScopeDisposed.js +17 -0
- package/esm/hooks/resolveHooks.js +2 -12
- package/esm/index.js +3 -6
- package/esm/injector/IInjector.js +12 -0
- package/esm/provider/Provider.js +6 -6
- package/esm/registration/IRegistration.js +2 -2
- package/package.json +1 -1
- package/typings/container/Container.d.ts +5 -8
- package/typings/container/EmptyContainer.d.ts +3 -5
- package/typings/container/IContainer.d.ts +3 -7
- package/typings/hooks/HooksRunner.d.ts +4 -2
- package/typings/hooks/onConstruct.d.ts +4 -6
- package/typings/hooks/onResolved.d.ts +4 -2
- package/typings/hooks/onScopeDisposed.d.ts +10 -0
- package/typings/hooks/resolveHooks.d.ts +6 -7
- package/typings/index.d.ts +7 -10
- package/typings/injector/IInjector.d.ts +9 -1
- package/typings/provider/IProvider.d.ts +3 -2
- package/typings/provider/Provider.d.ts +4 -4
- package/typings/registration/IRegistration.d.ts +3 -3
- package/cjm/errors/UnexpectedHookResultError.js +0 -12
- package/cjm/hooks/onConstructAsync.js +0 -28
- package/cjm/hooks/onContainerDisposed.js +0 -18
- package/cjm/hooks/onResolvedAsync.js +0 -24
- package/cjm/utils/promise.js +0 -5
- package/esm/errors/UnexpectedHookResultError.js +0 -8
- package/esm/hooks/onConstructAsync.js +0 -23
- package/esm/hooks/onContainerDisposed.js +0 -13
- package/esm/hooks/onResolvedAsync.js +0 -19
- package/esm/utils/promise.js +0 -1
- package/typings/errors/UnexpectedHookResultError.d.ts +0 -5
- package/typings/hooks/onConstructAsync.d.ts +0 -11
- package/typings/hooks/onContainerDisposed.d.ts +0 -9
- package/typings/hooks/onResolvedAsync.d.ts +0 -12
- package/typings/utils/promise.d.ts +0 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ provider pipelines, aliases, and custom injector strategies.
|
|
|
18
18
|
- clean API for classes, keys, tokens, aliases, and scopes
|
|
19
19
|
- no global container object; pass containers and scopes explicitly
|
|
20
20
|
- supports tagged application, request, transaction, page, and widget scopes
|
|
21
|
-
- decorator support with `@register`, `@inject`, `@onConstruct`, and `@
|
|
21
|
+
- decorator support with `@register`, `@inject`, `@onConstruct`, and `@onScopeDisposed`
|
|
22
22
|
- can [inject properties](#inject-property)
|
|
23
23
|
- can inject [lazy dependencies](#lazy)
|
|
24
24
|
- composable provider and registration pipelines
|
|
@@ -54,9 +54,9 @@ provider pipelines, aliases, and custom injector strategies.
|
|
|
54
54
|
- [Scope](#scope) `scope`
|
|
55
55
|
- [Module](#module)
|
|
56
56
|
- [Hook](#hook) `@hook`
|
|
57
|
+
- [Hook domains](#hook-domains) `ScopeHook` `InjectorHook` `ProviderHook`
|
|
57
58
|
- [OnConstruct](#onconstruct) `@onConstruct`
|
|
58
|
-
- [
|
|
59
|
-
- [OnContainerDisposed](#oncontainerdisposed) `@onContainerDisposed`
|
|
59
|
+
- [OnScopeDisposed](#onscopedisposed) `@onScopeDisposed`
|
|
60
60
|
- [Inject Property](#inject-property)
|
|
61
61
|
- [Inject Method](#inject-method)
|
|
62
62
|
- [Mock](#mock)
|
|
@@ -107,7 +107,7 @@ bundlers tree-shake unused exports.
|
|
|
107
107
|
|
|
108
108
|
> [!NOTE]
|
|
109
109
|
> The default `MetadataInjector` (and the `@inject` / `@onConstruct` /
|
|
110
|
-
> `@
|
|
110
|
+
> `@onScopeDisposed` decorators) rely on `reflect-metadata`. It is declared as an
|
|
111
111
|
> optional peer dependency — install it and import it once at your entrypoint.
|
|
112
112
|
> `SimpleInjector` and `ProxyInjector` do not need it.
|
|
113
113
|
|
|
@@ -454,7 +454,7 @@ describe('Instances', function () {
|
|
|
454
454
|
Sometimes you want to dispose a container or scope. For example, when a request, page, widget, or other local lifecycle ends.
|
|
455
455
|
|
|
456
456
|
- container can be disposed
|
|
457
|
-
- when container is disposed then it runs its `
|
|
457
|
+
- when container is disposed then it runs its `onScopeDisposed` hooks, unregisters its providers, removes its local instances, and detaches from its parent
|
|
458
458
|
|
|
459
459
|
> [!IMPORTANT]
|
|
460
460
|
> Dispose is local to the container being disposed. Child scopes are not disposed automatically; dispose them explicitly when their own lifecycle ends.
|
|
@@ -473,7 +473,7 @@ import { bindTo, Container, ContainerDisposedError, register, Registration as R,
|
|
|
473
473
|
* - Cache entries cleared
|
|
474
474
|
*
|
|
475
475
|
* The container.dispose() method:
|
|
476
|
-
* 1. Executes all
|
|
476
|
+
* 1. Executes all onScopeDisposed hooks
|
|
477
477
|
* 2. Clears all instances and registrations
|
|
478
478
|
* 3. Detaches from parent scope
|
|
479
479
|
* 4. Prevents further resolution
|
|
@@ -2379,7 +2379,7 @@ describe('Decorator Pattern', () => {
|
|
|
2379
2379
|
|
|
2380
2380
|
### On resolve
|
|
2381
2381
|
|
|
2382
|
-
Sometimes you don't want to change the dependency, only to react to it. Use the `onResolve(...)` pipe — it appends a `
|
|
2382
|
+
Sometimes you don't want to change the dependency, only to react to it. Use the `onResolve(...)` pipe — it appends a `ProviderHook` that receives the resolved dependency and the resolving scope.
|
|
2383
2383
|
|
|
2384
2384
|
- `provider(onResolve((instance, scope) => tracker.track(instance)))`
|
|
2385
2385
|
|
|
@@ -2828,16 +2828,78 @@ Decorators are applied bottom-up, so `authorize` runs first, then `validate` and
|
|
|
2828
2828
|
`(...prev) => [...prev].reverse()` to reorder, or `() => [onlyThisOne]` to
|
|
2829
2829
|
replace the accumulated hooks.
|
|
2830
2830
|
|
|
2831
|
-
`@onConstruct
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2831
|
+
`@onConstruct` and `@onScopeDisposed` keep their variadic signature and
|
|
2832
|
+
compensate for the bottom-up application order, so stacked decorators run in
|
|
2833
|
+
declaration order: `@onConstruct(h1) @onConstruct(h2)` runs `h1` before `h2`.
|
|
2834
|
+
|
|
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.
|
|
2852
|
+
|
|
2853
|
+
### Hook domains
|
|
2854
|
+
|
|
2855
|
+
The decorators above declare hook *metadata* on a class. The imperative side —
|
|
2856
|
+
the callbacks the container machinery runs — is registered on whichever
|
|
2857
|
+
abstraction raises the event, one hook type per domain:
|
|
2858
|
+
|
|
2859
|
+
| Domain | Registered on | Type | Methods |
|
|
2860
|
+
| ------------ | ------------- | --------------- | -------------------------------------------------------- |
|
|
2861
|
+
| **Scope** | `IContainer` | `ScopeHook` | `onScopeCreated(...)`, `onScopeDisposed(...)` |
|
|
2862
|
+
| **Scope** | `IContainer` | `RegisteredHook`| `onRegistered(...)` |
|
|
2863
|
+
| **Injector** | `IInjector` | `InjectorHook` | `onConstructed(...)` |
|
|
2864
|
+
| **Provider** | `IProvider` | `ProviderHook` | `onResolved(...)`, or the [`onResolve`](#on-resolve) pipe |
|
|
2865
|
+
|
|
2866
|
+
A container never hands its injector out — the injector is configured first and
|
|
2867
|
+
passed in at construction:
|
|
2868
|
+
|
|
2869
|
+
```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'] })
|
|
2874
|
+
.onScopeCreated((scope) => audit.scopeOpened(scope))
|
|
2875
|
+
.onScopeDisposed((scope) => audit.scopeClosed(scope))
|
|
2876
|
+
.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
|
+
|
|
2888
|
+
```typescript
|
|
2889
|
+
const injector = new MetadataInjector().useModule(new OnConstructModule());
|
|
2890
|
+
const container = new Container({ injector });
|
|
2891
|
+
```
|
|
2892
|
+
|
|
2893
|
+
`OnDisposeModule` is a container module hooking `onScopeDisposed`, and
|
|
2894
|
+
`OnResolvedModule` is a container module reaching every provider through
|
|
2895
|
+
`onRegistered`.
|
|
2835
2896
|
|
|
2836
2897
|
### OnConstruct
|
|
2837
2898
|
|
|
2838
2899
|
```typescript
|
|
2839
2900
|
import 'reflect-metadata';
|
|
2840
2901
|
import {
|
|
2902
|
+
MetadataInjector,
|
|
2841
2903
|
OnConstructModule,
|
|
2842
2904
|
Container,
|
|
2843
2905
|
type ExecutionContext,
|
|
@@ -2852,6 +2914,10 @@ const execute: HookFn = (ctx) => {
|
|
|
2852
2914
|
ctx.invokeMethod({ args: ctx.resolveArgs() });
|
|
2853
2915
|
};
|
|
2854
2916
|
|
|
2917
|
+
const executeAsync: HookFn = async (ctx) => {
|
|
2918
|
+
await ctx.invokeMethod({ args: ctx.resolveArgs() });
|
|
2919
|
+
};
|
|
2920
|
+
|
|
2855
2921
|
describe('onConstruct', function () {
|
|
2856
2922
|
it('should run initialization method after dependencies are resolved', function () {
|
|
2857
2923
|
class DatabaseConnection {
|
|
@@ -2865,9 +2931,9 @@ describe('onConstruct', function () {
|
|
|
2865
2931
|
}
|
|
2866
2932
|
}
|
|
2867
2933
|
|
|
2868
|
-
const container = new Container(
|
|
2869
|
-
.useModule(new OnConstructModule())
|
|
2870
|
-
|
|
2934
|
+
const container = new Container({
|
|
2935
|
+
injector: new MetadataInjector().useModule(new OnConstructModule()),
|
|
2936
|
+
}).addRegistration(R.fromValue('postgres://localhost:5432').bindTo('ConnectionString'));
|
|
2871
2937
|
|
|
2872
2938
|
const db = container.resolve(DatabaseConnection);
|
|
2873
2939
|
|
|
@@ -2886,11 +2952,13 @@ describe('onConstruct', function () {
|
|
|
2886
2952
|
}
|
|
2887
2953
|
|
|
2888
2954
|
let captured: { ex: unknown; context: ExecutionContext } | undefined;
|
|
2889
|
-
const container = new Container(
|
|
2890
|
-
new
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2955
|
+
const container = new Container({
|
|
2956
|
+
injector: new MetadataInjector().useModule(
|
|
2957
|
+
new OnConstructModule((ex, context) => {
|
|
2958
|
+
captured = { ex, context };
|
|
2959
|
+
}),
|
|
2960
|
+
),
|
|
2961
|
+
});
|
|
2894
2962
|
|
|
2895
2963
|
expect(() => container.resolve(BrokenService)).not.toThrow();
|
|
2896
2964
|
expect(captured?.ex).toBe(failure);
|
|
@@ -2907,7 +2975,7 @@ describe('onConstruct', function () {
|
|
|
2907
2975
|
init() {}
|
|
2908
2976
|
}
|
|
2909
2977
|
|
|
2910
|
-
const container = new Container().useModule(new OnConstructModule());
|
|
2978
|
+
const container = new Container({ injector: new MetadataInjector().useModule(new OnConstructModule()) });
|
|
2911
2979
|
|
|
2912
2980
|
expect(() => container.resolve(BrokenService)).toThrow(failure);
|
|
2913
2981
|
});
|
|
@@ -2921,44 +2989,20 @@ describe('onConstruct', function () {
|
|
|
2921
2989
|
}
|
|
2922
2990
|
|
|
2923
2991
|
let scope: IContainer | undefined;
|
|
2924
|
-
const container = new Container(
|
|
2925
|
-
new
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2992
|
+
const container = new Container({
|
|
2993
|
+
injector: new MetadataInjector().useModule(
|
|
2994
|
+
new OnConstructModule((_ex, context) => {
|
|
2995
|
+
scope = context.scope;
|
|
2996
|
+
}),
|
|
2997
|
+
),
|
|
2998
|
+
});
|
|
2929
2999
|
const child = container.createScope();
|
|
2930
3000
|
|
|
2931
3001
|
child.resolve(BrokenService);
|
|
2932
3002
|
|
|
2933
3003
|
expect(scope).toBe(child);
|
|
2934
3004
|
});
|
|
2935
|
-
});
|
|
2936
|
-
|
|
2937
|
-
```
|
|
2938
|
-
|
|
2939
|
-
### OnConstructAsync
|
|
2940
|
-
|
|
2941
|
-
`@onConstructAsync` runs promise-returning initialization. Resolution stays
|
|
2942
|
-
synchronous: `OnConstructAsyncModule` starts the hooks when the instance
|
|
2943
|
-
is created and they settle afterwards, so `resolve` returns before they finish.
|
|
2944
|
-
|
|
2945
|
-
```typescript
|
|
2946
|
-
import 'reflect-metadata';
|
|
2947
|
-
import {
|
|
2948
|
-
OnConstructAsyncModule,
|
|
2949
|
-
Container,
|
|
2950
|
-
type ExecutionContext,
|
|
2951
|
-
type HookFn,
|
|
2952
|
-
inject,
|
|
2953
|
-
onConstructAsync,
|
|
2954
|
-
Registration as R,
|
|
2955
|
-
} from 'ts-ioc-container';
|
|
2956
|
-
|
|
2957
|
-
const execute: HookFn = async (ctx) => {
|
|
2958
|
-
await ctx.invokeMethod({ args: ctx.resolveArgs() });
|
|
2959
|
-
};
|
|
2960
3005
|
|
|
2961
|
-
describe('onConstructAsync', function () {
|
|
2962
3006
|
it('should run an async initialization method after the instance is created', async function () {
|
|
2963
3007
|
class DatabaseConnection {
|
|
2964
3008
|
isConnected = false;
|
|
@@ -2973,7 +3017,7 @@ describe('onConstructAsync', function () {
|
|
|
2973
3017
|
});
|
|
2974
3018
|
}
|
|
2975
3019
|
|
|
2976
|
-
@
|
|
3020
|
+
@onConstruct(executeAsync)
|
|
2977
3021
|
async connect(@inject('ConnectionString') connectionString: string) {
|
|
2978
3022
|
await Promise.resolve();
|
|
2979
3023
|
this.connectionString = connectionString;
|
|
@@ -2982,9 +3026,9 @@ describe('onConstructAsync', function () {
|
|
|
2982
3026
|
}
|
|
2983
3027
|
}
|
|
2984
3028
|
|
|
2985
|
-
const container = new Container(
|
|
2986
|
-
.useModule(new
|
|
2987
|
-
|
|
3029
|
+
const container = new Container({
|
|
3030
|
+
injector: new MetadataInjector().useModule(new OnConstructModule()),
|
|
3031
|
+
}).addRegistration(R.fromValue('postgres://localhost:5432').bindTo('ConnectionString'));
|
|
2988
3032
|
|
|
2989
3033
|
const db = container.resolve(DatabaseConnection);
|
|
2990
3034
|
|
|
@@ -3001,16 +3045,18 @@ describe('onConstructAsync', function () {
|
|
|
3001
3045
|
const failure = new Error('boom');
|
|
3002
3046
|
|
|
3003
3047
|
class BrokenService {
|
|
3004
|
-
@
|
|
3048
|
+
@onConstruct(() => Promise.reject(failure))
|
|
3005
3049
|
init() {}
|
|
3006
3050
|
}
|
|
3007
3051
|
|
|
3008
3052
|
let captured: { ex: unknown; context: ExecutionContext } | undefined;
|
|
3009
|
-
const container = new Container(
|
|
3010
|
-
new
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3053
|
+
const container = new Container({
|
|
3054
|
+
injector: new MetadataInjector().useModule(
|
|
3055
|
+
new OnConstructModule((ex, context) => {
|
|
3056
|
+
captured = { ex, context };
|
|
3057
|
+
}),
|
|
3058
|
+
),
|
|
3059
|
+
});
|
|
3014
3060
|
|
|
3015
3061
|
const child = container.createScope();
|
|
3016
3062
|
child.resolve(BrokenService);
|
|
@@ -3024,7 +3070,7 @@ describe('onConstructAsync', function () {
|
|
|
3024
3070
|
|
|
3025
3071
|
```
|
|
3026
3072
|
|
|
3027
|
-
###
|
|
3073
|
+
### OnScopeDisposed
|
|
3028
3074
|
|
|
3029
3075
|
```typescript
|
|
3030
3076
|
import 'reflect-metadata';
|
|
@@ -3034,7 +3080,7 @@ import {
|
|
|
3034
3080
|
Container,
|
|
3035
3081
|
type HookFn,
|
|
3036
3082
|
inject,
|
|
3037
|
-
|
|
3083
|
+
onScopeDisposed,
|
|
3038
3084
|
register,
|
|
3039
3085
|
Registration as R,
|
|
3040
3086
|
singleton,
|
|
@@ -3063,13 +3109,13 @@ class Logger {
|
|
|
3063
3109
|
this.messages.push(message);
|
|
3064
3110
|
}
|
|
3065
3111
|
|
|
3066
|
-
@
|
|
3112
|
+
@onScopeDisposed(execute)
|
|
3067
3113
|
save() {
|
|
3068
3114
|
this.logsRepo.saveLogs(this.messages);
|
|
3069
3115
|
}
|
|
3070
3116
|
}
|
|
3071
3117
|
|
|
3072
|
-
describe('
|
|
3118
|
+
describe('onScopeDisposed', function () {
|
|
3073
3119
|
it('should invoke hooks on all instances when container is disposed', function () {
|
|
3074
3120
|
const container = new Container()
|
|
3075
3121
|
.useModule(new OnDisposeModule())
|
|
@@ -19,10 +19,9 @@ class Container {
|
|
|
19
19
|
providers = new Map();
|
|
20
20
|
aliases = new AliasMap_1.AliasMap();
|
|
21
21
|
injector;
|
|
22
|
-
onConstructHookList = [];
|
|
23
|
-
onDisposeHookList = [];
|
|
24
22
|
onScopeCreatedHookList = [];
|
|
25
|
-
|
|
23
|
+
onScopeDisposedHookList = [];
|
|
24
|
+
onRegisteredHookList = [];
|
|
26
25
|
constructor(options = {}) {
|
|
27
26
|
this.injector = options.injector ?? new MetadataInjector_1.MetadataInjector();
|
|
28
27
|
this.parent = options.parent ?? new EmptyContainer_1.EmptyContainer();
|
|
@@ -32,8 +31,8 @@ class Container {
|
|
|
32
31
|
this.validateContainer();
|
|
33
32
|
this.providers.set(key, provider);
|
|
34
33
|
this.aliases.setAliasesByKey(key, aliases);
|
|
35
|
-
for (const
|
|
36
|
-
|
|
34
|
+
for (const onRegistered of this.onRegisteredHookList) {
|
|
35
|
+
onRegistered(provider, key, this);
|
|
37
36
|
}
|
|
38
37
|
return this;
|
|
39
38
|
}
|
|
@@ -78,10 +77,9 @@ class Container {
|
|
|
78
77
|
createScope({ tags } = {}) {
|
|
79
78
|
this.validateContainer();
|
|
80
79
|
const scope = new Container({ injector: this.injector, parent: this, tags })
|
|
81
|
-
.onConstruct(...this.onConstructHookList)
|
|
82
|
-
.onInstanceDisposed(...this.onDisposeHookList)
|
|
83
80
|
.onScopeCreated(...this.onScopeCreatedHookList)
|
|
84
|
-
.
|
|
81
|
+
.onScopeDisposed(...this.onScopeDisposedHookList)
|
|
82
|
+
.onRegistered(...this.onRegisteredHookList);
|
|
85
83
|
for (const registration of this.getRegistrations()) {
|
|
86
84
|
registration.applyTo(scope);
|
|
87
85
|
}
|
|
@@ -103,8 +101,8 @@ class Container {
|
|
|
103
101
|
dispose() {
|
|
104
102
|
this.validateContainer();
|
|
105
103
|
this.isDisposed = true;
|
|
106
|
-
for (const
|
|
107
|
-
|
|
104
|
+
for (const onScopeDisposed of this.onScopeDisposedHookList) {
|
|
105
|
+
onScopeDisposed(this);
|
|
108
106
|
}
|
|
109
107
|
this.parent.removeScope(this);
|
|
110
108
|
this.parent = new EmptyContainer_1.EmptyContainer();
|
|
@@ -115,10 +113,9 @@ class Container {
|
|
|
115
113
|
this.aliases.destroy();
|
|
116
114
|
this.instances.clear();
|
|
117
115
|
this.registrations = [];
|
|
118
|
-
this.onConstructHookList.length = 0;
|
|
119
|
-
this.onDisposeHookList.length = 0;
|
|
120
116
|
this.onScopeCreatedHookList.length = 0;
|
|
121
|
-
this.
|
|
117
|
+
this.onScopeDisposedHookList.length = 0;
|
|
118
|
+
this.onRegisteredHookList.length = 0;
|
|
122
119
|
}
|
|
123
120
|
addRegistration(registration) {
|
|
124
121
|
this.registrations.push(registration);
|
|
@@ -131,27 +128,20 @@ class Container {
|
|
|
131
128
|
hasRegistration(key) {
|
|
132
129
|
return this.registrations.some((r) => r.getKeyOrFail() === key) || this.parent.hasRegistration(key);
|
|
133
130
|
}
|
|
134
|
-
onConstruct(...hooks) {
|
|
135
|
-
this.onConstructHookList.push(...hooks);
|
|
136
|
-
return this;
|
|
137
|
-
}
|
|
138
|
-
onInstanceDisposed(...hooks) {
|
|
139
|
-
this.onDisposeHookList.push(...hooks);
|
|
140
|
-
return this;
|
|
141
|
-
}
|
|
142
131
|
onScopeCreated(...hooks) {
|
|
143
132
|
this.onScopeCreatedHookList.push(...hooks);
|
|
144
133
|
return this;
|
|
145
134
|
}
|
|
146
|
-
|
|
147
|
-
this.
|
|
135
|
+
onScopeDisposed(...hooks) {
|
|
136
|
+
this.onScopeDisposedHookList.push(...hooks);
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
onRegistered(...hooks) {
|
|
140
|
+
this.onRegisteredHookList.push(...hooks);
|
|
148
141
|
return this;
|
|
149
142
|
}
|
|
150
143
|
addInstance(instance) {
|
|
151
144
|
this.instances.add(instance);
|
|
152
|
-
for (const onConstruct of this.onConstructHookList) {
|
|
153
|
-
onConstruct(instance, this);
|
|
154
|
-
}
|
|
155
145
|
}
|
|
156
146
|
getScopes() {
|
|
157
147
|
return [...this.scopes];
|
|
@@ -60,16 +60,13 @@ class EmptyContainer {
|
|
|
60
60
|
resolveOneByAlias(alias, options) {
|
|
61
61
|
throw new DependencyNotFoundError_1.DependencyNotFoundError(`Cannot find alias ${alias.toString()}`);
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
65
|
-
}
|
|
66
|
-
onConstruct(...hooks) {
|
|
63
|
+
onScopeCreated(...hooks) {
|
|
67
64
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
68
65
|
}
|
|
69
|
-
|
|
66
|
+
onScopeDisposed(...hooks) {
|
|
70
67
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
71
68
|
}
|
|
72
|
-
|
|
69
|
+
onRegistered(...hooks) {
|
|
73
70
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
74
71
|
}
|
|
75
72
|
}
|
package/cjm/hooks/HooksRunner.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HooksRunner = void 0;
|
|
3
|
+
exports.runHooks = exports.HooksRunner = void 0;
|
|
4
4
|
const HookContext_1 = require("./HookContext");
|
|
5
5
|
const hook_1 = require("./hook");
|
|
6
|
-
const
|
|
7
|
-
|
|
6
|
+
const runFrom = (fns, index, context) => {
|
|
7
|
+
for (let i = index; i < fns.length; i++) {
|
|
8
|
+
const result = fns[i](context);
|
|
9
|
+
if (result instanceof Promise) {
|
|
10
|
+
return result.then(() => runFrom(fns, i + 1, context));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
8
14
|
class HooksRunner {
|
|
9
15
|
key;
|
|
10
16
|
constructor(key) {
|
|
@@ -15,28 +21,30 @@ class HooksRunner {
|
|
|
15
21
|
}
|
|
16
22
|
execute(target, { scope, createContext = HookContext_1.createHookContext, mapContext = (context) => context, predicate = () => true, }) {
|
|
17
23
|
const hooks = Array.from((0, hook_1.getHooks)(target, this.key).entries()).filter(([methodName]) => predicate(methodName));
|
|
18
|
-
const
|
|
19
|
-
const context = mapContext(createContext(target, scope, methodName));
|
|
20
|
-
for (const execute of executions) {
|
|
21
|
-
const result = execute(context);
|
|
22
|
-
if (result instanceof Promise) {
|
|
23
|
-
throw new UnexpectedHookResultError_1.UnexpectedHookResultError(`Hook ${methodName} returned a promise, use runHooksAsync instead`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
};
|
|
24
|
+
const pending = [];
|
|
27
25
|
for (const [methodName, executions] of hooks) {
|
|
28
|
-
runMethodHooks(methodName, executions.map(hook_1.toHookFn));
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
async executeAsync(target, { scope, createContext = HookContext_1.createHookContext, mapContext = (context) => context, predicate = () => true, }) {
|
|
32
|
-
const hooks = Array.from((0, hook_1.getHooks)(target, this.key).entries()).filter(([methodName]) => predicate(methodName));
|
|
33
|
-
const runMethodHooks = async (methodName, executions) => {
|
|
34
26
|
const context = mapContext(createContext(target, scope, methodName));
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
const result = runFrom(executions.map(hook_1.toHookFn), 0, context);
|
|
28
|
+
if (result) {
|
|
29
|
+
pending.push(result);
|
|
37
30
|
}
|
|
38
|
-
}
|
|
39
|
-
return Promise.all(
|
|
31
|
+
}
|
|
32
|
+
return pending.length > 0 ? Promise.all(pending).then(() => undefined) : undefined;
|
|
40
33
|
}
|
|
41
34
|
}
|
|
42
35
|
exports.HooksRunner = HooksRunner;
|
|
36
|
+
const runHooks = (runner, target, scope, onException) => {
|
|
37
|
+
const report = (ex) => {
|
|
38
|
+
if (!onException) {
|
|
39
|
+
throw ex;
|
|
40
|
+
}
|
|
41
|
+
onException(ex, { scope });
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
runner.execute(target, { scope })?.catch(report);
|
|
45
|
+
}
|
|
46
|
+
catch (ex) {
|
|
47
|
+
report(ex);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
exports.runHooks = runHooks;
|
package/cjm/hooks/onConstruct.js
CHANGED
|
@@ -11,17 +11,9 @@ class OnConstructModule {
|
|
|
11
11
|
constructor(onException) {
|
|
12
12
|
this.onException = onException;
|
|
13
13
|
}
|
|
14
|
-
applyTo(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
exports.onConstructHooksRunner.execute(instance, { scope });
|
|
18
|
-
}
|
|
19
|
-
catch (ex) {
|
|
20
|
-
if (!this.onException) {
|
|
21
|
-
throw ex;
|
|
22
|
-
}
|
|
23
|
-
this.onException(ex, { scope });
|
|
24
|
-
}
|
|
14
|
+
applyTo(injector) {
|
|
15
|
+
injector.onConstructed((instance, scope) => {
|
|
16
|
+
(0, HooksRunner_1.runHooks)(exports.onConstructHooksRunner, instance, scope, this.onException);
|
|
25
17
|
});
|
|
26
18
|
}
|
|
27
19
|
}
|
package/cjm/hooks/onResolved.js
CHANGED
|
@@ -7,14 +7,18 @@ const resolveHooks_1 = require("./resolveHooks");
|
|
|
7
7
|
exports.onResolvedHooksRunner = new HooksRunner_1.HooksRunner('onResolved');
|
|
8
8
|
exports.onResolved = (0, resolveHooks_1.resolvedHook)('onResolved');
|
|
9
9
|
exports.onceResolved = (0, resolveHooks_1.onceResolvedHook)('onResolved');
|
|
10
|
-
const runHooks = (0, resolveHooks_1.forEachResolvedObject)((0, resolveHooks_1.executeHooks)(exports.onResolvedHooksRunner));
|
|
10
|
+
const runHooks = (onException) => (0, resolveHooks_1.forEachResolvedObject)((0, resolveHooks_1.executeHooks)(exports.onResolvedHooksRunner, onException));
|
|
11
11
|
class OnResolvedModule {
|
|
12
|
+
runHooks;
|
|
13
|
+
constructor(onException) {
|
|
14
|
+
this.runHooks = runHooks(onException);
|
|
15
|
+
}
|
|
12
16
|
applyTo(container) {
|
|
13
|
-
container.
|
|
14
|
-
provider.
|
|
17
|
+
container.onRegistered((provider) => {
|
|
18
|
+
provider.onResolved(this.runHooks);
|
|
15
19
|
});
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
22
|
exports.OnResolvedModule = OnResolvedModule;
|
|
19
|
-
const resolved = () => (0, IRegistration_1.registerPipe)((p) => p.
|
|
23
|
+
const resolved = (onException) => (0, IRegistration_1.registerPipe)((p) => p.onResolved(runHooks(onException)));
|
|
20
24
|
exports.resolved = resolved;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OnDisposeModule = exports.onScopeDisposed = exports.onScopeDisposedHooksRunner = void 0;
|
|
4
|
+
const hook_1 = require("./hook");
|
|
5
|
+
const HooksRunner_1 = require("./HooksRunner");
|
|
6
|
+
exports.onScopeDisposedHooksRunner = new HooksRunner_1.HooksRunner('onScopeDisposed');
|
|
7
|
+
const onScopeDisposed = (...fns) => (0, hook_1.hook)('onScopeDisposed', (0, hook_1.prependHooks)(...fns));
|
|
8
|
+
exports.onScopeDisposed = onScopeDisposed;
|
|
9
|
+
class OnDisposeModule {
|
|
10
|
+
onException;
|
|
11
|
+
constructor(onException) {
|
|
12
|
+
this.onException = onException;
|
|
13
|
+
}
|
|
14
|
+
applyTo(container) {
|
|
15
|
+
container.onScopeDisposed((scope) => {
|
|
16
|
+
for (const instance of scope.getInstances()) {
|
|
17
|
+
(0, HooksRunner_1.runHooks)(exports.onScopeDisposedHooksRunner, instance, scope, this.onException);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.OnDisposeModule = OnDisposeModule;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.executeHooks = exports.forEachResolvedObject = exports.onceResolvedHook = exports.resolvedHook = exports.invokeMethod = void 0;
|
|
4
|
+
const HooksRunner_1 = require("./HooksRunner");
|
|
4
5
|
const hook_1 = require("./hook");
|
|
5
6
|
const basic_1 = require("../utils/basic");
|
|
6
7
|
const invokeMethod = (context) => {
|
|
@@ -28,17 +29,5 @@ const forEachResolvedObject = (run) => (dependency, scope) => {
|
|
|
28
29
|
}
|
|
29
30
|
};
|
|
30
31
|
exports.forEachResolvedObject = forEachResolvedObject;
|
|
31
|
-
const executeHooks = (runner) => (dependency, scope) =>
|
|
32
|
+
const executeHooks = (runner, onException) => (dependency, scope) => (0, HooksRunner_1.runHooks)(runner, dependency, scope, onException);
|
|
32
33
|
exports.executeHooks = executeHooks;
|
|
33
|
-
const executeHooksAsync = (runner, onException) => (dependency, scope) => {
|
|
34
|
-
if (!runner.hasHooks(dependency)) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
runner.executeAsync(dependency, { scope }).catch((ex) => {
|
|
38
|
-
if (!onException) {
|
|
39
|
-
throw ex;
|
|
40
|
-
}
|
|
41
|
-
onException(ex, { scope });
|
|
42
|
-
});
|
|
43
|
-
};
|
|
44
|
-
exports.executeHooksAsync = executeHooksAsync;
|