ts-ioc-container 64.0.0 → 65.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 +21 -9
- package/cjm/container/AutoResolveModule.js +1 -1
- package/cjm/container/Container.js +17 -31
- package/cjm/container/EmptyContainer.js +3 -3
- package/cjm/errors/TypedEventDisposedError.js +17 -0
- package/cjm/hooks/onResolved.js +1 -1
- package/cjm/hooks/onScopeDisposed.js +1 -1
- package/cjm/index.js +7 -3
- package/cjm/utils/TypedEvent.js +35 -0
- package/esm/container/AutoResolveModule.js +1 -1
- package/esm/container/Container.js +17 -31
- package/esm/container/EmptyContainer.js +3 -3
- package/esm/errors/TypedEventDisposedError.js +13 -0
- package/esm/hooks/onResolved.js +1 -1
- package/esm/hooks/onScopeDisposed.js +1 -1
- package/esm/index.js +2 -0
- package/esm/utils/TypedEvent.js +31 -0
- package/package.json +1 -1
- package/typings/container/Container.d.ts +8 -7
- package/typings/container/EmptyContainer.d.ts +5 -4
- package/typings/container/IContainer.d.ts +4 -3
- package/typings/errors/TypedEventDisposedError.d.ts +6 -0
- package/typings/index.d.ts +2 -0
- package/typings/utils/TypedEvent.d.ts +17 -0
package/README.md
CHANGED
|
@@ -2873,10 +2873,10 @@ The decorators above declare hook *metadata* on a class. The imperative side —
|
|
|
2873
2873
|
the callbacks the container machinery runs — is registered on whichever
|
|
2874
2874
|
abstraction raises the event, one hook type per domain:
|
|
2875
2875
|
|
|
2876
|
-
| Domain | Registered on | Type |
|
|
2876
|
+
| Domain | Registered on | Type | Where |
|
|
2877
2877
|
| ------------ | ------------- | --------------- | -------------------------------------------------------- |
|
|
2878
|
-
| **Scope** | `IContainer` | `ScopeHook` | `
|
|
2879
|
-
| **Scope** | `IContainer` | `RegisteredHook`| `
|
|
2878
|
+
| **Scope** | `IContainer` | `ScopeHook` | `scopeCreated.subscribe(...)`, `scopeDisposed.subscribe(...)` |
|
|
2879
|
+
| **Scope** | `IContainer` | `RegisteredHook`| `registered.subscribe(...)` |
|
|
2880
2880
|
| **Injector** | `IInjector` | `InjectorHook` | `onConstructed(...)` |
|
|
2881
2881
|
| **Provider** | `IProvider` | `ProviderHook` | `onResolved(...)`, or the [`onResolve`](#on-resolve) pipe |
|
|
2882
2882
|
|
|
@@ -2888,18 +2888,30 @@ so a child inherits what its parent held then and later additions to either
|
|
|
2888
2888
|
stay local.
|
|
2889
2889
|
|
|
2890
2890
|
```typescript
|
|
2891
|
-
const container = new Container({ tags: ['application'] })
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2891
|
+
const container = new Container({ tags: ['application'] });
|
|
2892
|
+
|
|
2893
|
+
// Scope events are typed events on the container itself
|
|
2894
|
+
const stop = container.scopeCreated.subscribe((scope) => audit.scopeOpened(scope));
|
|
2895
|
+
container.scopeDisposed.subscribe((scope) => audit.scopeClosed(scope));
|
|
2896
|
+
container.registered.subscribe((provider, key) => audit.registered(key));
|
|
2897
|
+
|
|
2898
|
+
stop(); // detached; `container.scopeCreated.unsubscribe(fn)` does the same by reference
|
|
2895
2899
|
|
|
2896
2900
|
// Construction is the injector's event, not a scope's
|
|
2897
2901
|
container.getInjector().onConstructed((instance, scope) => metrics.built(instance, scope));
|
|
2898
2902
|
```
|
|
2899
2903
|
|
|
2904
|
+
The scope events are `scopeCreated`, `scopeDisposed` (`ITypedEvent<[IContainer]>`)
|
|
2905
|
+
and `registered` (`ITypedEvent<[IProvider, DependencyKey, IContainer]>`);
|
|
2906
|
+
`subscribe` returns the unsubscribe function. The exposed events carry no
|
|
2907
|
+
`emit` — only the container raises its own events.
|
|
2908
|
+
|
|
2909
|
+
`TypedEvent` itself is exported for your own events: `subscribe` / `unsubscribe`
|
|
2910
|
+
/ `emit` / `dispose`, with `ITypedEvent` as the subscriber-only view to hand out.
|
|
2911
|
+
|
|
2900
2912
|
The built-in modules are all container modules: `OnConstructModule` reaches the
|
|
2901
|
-
injector through `getInjector()`, `OnDisposeModule`
|
|
2902
|
-
`OnResolvedModule` reaches every provider through `
|
|
2913
|
+
injector through `getInjector()`, `OnDisposeModule` subscribes to `scopeDisposed`,
|
|
2914
|
+
and `OnResolvedModule` reaches every provider through `registered`.
|
|
2903
2915
|
|
|
2904
2916
|
### OnConstruct
|
|
2905
2917
|
|
|
@@ -7,7 +7,7 @@ class AutoResolveModule {
|
|
|
7
7
|
this.options = options;
|
|
8
8
|
}
|
|
9
9
|
applyTo(container) {
|
|
10
|
-
container.
|
|
10
|
+
container.scopeCreated.subscribe((scope) => scope.autoResolve(this.options));
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
exports.AutoResolveModule = AutoResolveModule;
|
|
@@ -9,6 +9,7 @@ const ProxyRegistry_1 = require("../utils/ProxyRegistry");
|
|
|
9
9
|
const DependencyNotFoundError_1 = require("../errors/DependencyNotFoundError");
|
|
10
10
|
const basic_1 = require("../utils/basic");
|
|
11
11
|
const array_1 = require("../utils/array");
|
|
12
|
+
const TypedEvent_1 = require("../utils/TypedEvent");
|
|
12
13
|
class Container {
|
|
13
14
|
isDisposed = false;
|
|
14
15
|
parent;
|
|
@@ -19,9 +20,12 @@ class Container {
|
|
|
19
20
|
providers = new Map();
|
|
20
21
|
aliases = new AliasMap_1.AliasMap();
|
|
21
22
|
injector;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
scopeCreatedEvent = new TypedEvent_1.TypedEvent();
|
|
24
|
+
scopeDisposedEvent = new TypedEvent_1.TypedEvent();
|
|
25
|
+
registeredEvent = new TypedEvent_1.TypedEvent();
|
|
26
|
+
scopeCreated = this.scopeCreatedEvent;
|
|
27
|
+
scopeDisposed = this.scopeDisposedEvent;
|
|
28
|
+
registered = this.registeredEvent;
|
|
25
29
|
constructor(options = {}) {
|
|
26
30
|
this.injector = options.injector ?? new MetadataInjector_1.MetadataInjector();
|
|
27
31
|
this.parent = options.parent ?? new EmptyContainer_1.EmptyContainer();
|
|
@@ -31,9 +35,7 @@ class Container {
|
|
|
31
35
|
this.validateContainer();
|
|
32
36
|
this.providers.set(key, provider);
|
|
33
37
|
this.aliases.setAliasesByKey(key, aliases);
|
|
34
|
-
|
|
35
|
-
onRegistered(provider, key, this);
|
|
36
|
-
}
|
|
38
|
+
this.registeredEvent.emit(provider, key, this);
|
|
37
39
|
return this;
|
|
38
40
|
}
|
|
39
41
|
resolve(target, { args = [], child = this, lazy } = {}) {
|
|
@@ -76,17 +78,15 @@ class Container {
|
|
|
76
78
|
}
|
|
77
79
|
createScope({ tags } = {}) {
|
|
78
80
|
this.validateContainer();
|
|
79
|
-
const scope = new Container({ injector: this.injector, parent: this, tags })
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
const scope = new Container({ injector: this.injector, parent: this, tags });
|
|
82
|
+
this.scopeCreatedEvent.getListeners().forEach((hook) => scope.scopeCreatedEvent.subscribe(hook));
|
|
83
|
+
this.scopeDisposedEvent.getListeners().forEach((hook) => scope.scopeDisposedEvent.subscribe(hook));
|
|
84
|
+
this.registeredEvent.getListeners().forEach((hook) => scope.registeredEvent.subscribe(hook));
|
|
83
85
|
for (const registration of this.getRegistrations()) {
|
|
84
86
|
registration.applyTo(scope);
|
|
85
87
|
}
|
|
86
88
|
this.scopes.push(scope);
|
|
87
|
-
|
|
88
|
-
onScopeCreated(scope);
|
|
89
|
-
}
|
|
89
|
+
this.scopeCreatedEvent.emit(scope);
|
|
90
90
|
return scope;
|
|
91
91
|
}
|
|
92
92
|
autoResolve({ args = [] } = {}) {
|
|
@@ -101,9 +101,7 @@ class Container {
|
|
|
101
101
|
dispose() {
|
|
102
102
|
this.validateContainer();
|
|
103
103
|
this.isDisposed = true;
|
|
104
|
-
|
|
105
|
-
onScopeDisposed(this);
|
|
106
|
-
}
|
|
104
|
+
this.scopeDisposedEvent.emit(this);
|
|
107
105
|
this.parent.removeScope(this);
|
|
108
106
|
this.parent = new EmptyContainer_1.EmptyContainer();
|
|
109
107
|
for (const provider of this.providers.values()) {
|
|
@@ -113,9 +111,9 @@ class Container {
|
|
|
113
111
|
this.aliases.destroy();
|
|
114
112
|
this.instances.clear();
|
|
115
113
|
this.registrations = [];
|
|
116
|
-
this.
|
|
117
|
-
this.
|
|
118
|
-
this.
|
|
114
|
+
this.scopeCreatedEvent.dispose();
|
|
115
|
+
this.scopeDisposedEvent.dispose();
|
|
116
|
+
this.registeredEvent.dispose();
|
|
119
117
|
}
|
|
120
118
|
addRegistration(registration) {
|
|
121
119
|
this.registrations.push(registration);
|
|
@@ -131,18 +129,6 @@ class Container {
|
|
|
131
129
|
hasRegistration(key) {
|
|
132
130
|
return this.registrations.some((r) => r.getKeyOrFail() === key) || this.parent.hasRegistration(key);
|
|
133
131
|
}
|
|
134
|
-
onScopeCreated(...hooks) {
|
|
135
|
-
this.onScopeCreatedHookList.push(...hooks);
|
|
136
|
-
return this;
|
|
137
|
-
}
|
|
138
|
-
onScopeDisposed(...hooks) {
|
|
139
|
-
this.onScopeDisposedHookList.push(...hooks);
|
|
140
|
-
return this;
|
|
141
|
-
}
|
|
142
|
-
onRegistered(...hooks) {
|
|
143
|
-
this.onRegisteredHookList.push(...hooks);
|
|
144
|
-
return this;
|
|
145
|
-
}
|
|
146
132
|
addInstance(instance) {
|
|
147
133
|
this.instances.add(instance);
|
|
148
134
|
}
|
|
@@ -63,13 +63,13 @@ class EmptyContainer {
|
|
|
63
63
|
resolveOneByAlias(alias, options) {
|
|
64
64
|
throw new DependencyNotFoundError_1.DependencyNotFoundError(`Cannot find alias ${alias.toString()}`);
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
get scopeCreated() {
|
|
67
67
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
get scopeDisposed() {
|
|
70
70
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
get registered() {
|
|
73
73
|
throw new MethodNotImplementedError_1.MethodNotImplementedError();
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypedEventDisposedError = void 0;
|
|
4
|
+
const ContainerError_1 = require("./ContainerError");
|
|
5
|
+
class TypedEventDisposedError extends ContainerError_1.ContainerError {
|
|
6
|
+
name = 'TypedEventDisposedError';
|
|
7
|
+
static assert(isTrue, failMessage) {
|
|
8
|
+
if (!isTrue) {
|
|
9
|
+
throw new TypedEventDisposedError(failMessage);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
Object.setPrototypeOf(this, TypedEventDisposedError.prototype);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.TypedEventDisposedError = TypedEventDisposedError;
|
package/cjm/hooks/onResolved.js
CHANGED
|
@@ -10,7 +10,7 @@ class OnDisposeModule {
|
|
|
10
10
|
this.strategy = strategy;
|
|
11
11
|
}
|
|
12
12
|
applyTo(container) {
|
|
13
|
-
container.
|
|
13
|
+
container.scopeDisposed.subscribe((scope) => {
|
|
14
14
|
for (const instance of scope.getInstances()) {
|
|
15
15
|
this.strategy.execute(instance, { scope });
|
|
16
16
|
}
|
package/cjm/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
5
|
-
exports.Is = exports.unwrapProxy = exports.ProxyRegistry = exports.pipe = exports.select = exports.once = exports.shallowCache = void 0;
|
|
3
|
+
exports.createHookContextFactory = exports.HookContext = exports.prepend = exports.append = exports.prependHooks = exports.appendHooks = exports.hasHooks = exports.hook = exports.getHooks = exports.TypedEventDisposedError = exports.UnsupportedTokenTypeError = exports.CannonSingletonApplyTwiceError = exports.ProviderDisposedError = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyMissingKeyError = exports.ContainerNotFoundError = exports.DependencyNotFoundError = exports.ContainerError = exports.Registration = exports.onResolve = exports.appendArgsFn = exports.appendArgs = exports.decorate = exports.singleton = exports.autoResolve = exports.lazy = exports.scopeAccess = exports.scope = exports.bindTo = exports.register = exports.toRegistrationFn = exports.toProviderFn = exports.toBindToken = exports.registerPipe = exports.isProviderPipe = exports.Provider = exports.ProxyInjector = exports.SimpleInjector = exports.resolveArgs = exports.argsFn = exports.args = exports.arg = exports.inject = exports.MetadataInjector = exports.Injector = exports.EmptyContainer = exports.AutoResolveModule = exports.Container = exports.isDependencyKey = void 0;
|
|
4
|
+
exports.throttle = exports.handleAsyncError = exports.handleError = exports.getMethodTags = exports.addMethodTag = exports.getMethodLabels = exports.addMethodLabel = exports.getMethodMeta = exports.addMethodMeta = exports.getParamTags = exports.addParamTag = exports.getParamLabels = exports.addParamLabel = exports.getParamMeta = exports.addParamMeta = exports.getClassTags = exports.addClassTag = exports.getClassLabels = exports.addClassLabel = exports.getClassMeta = exports.addClassMeta = exports.resolveConstructor = exports.GroupInstanceToken = exports.ConstantToken = exports.FunctionToken = exports.SingleToken = exports.ClassToken = exports.toSingleAlias = exports.SingleAliasToken = exports.toGroupAlias = exports.GroupAliasToken = exports.argToToken = exports.toMappedToken = exports.toToken = exports.InjectionToken = exports.ParallelAsync = exports.SequentialAsync = exports.SequentialSync = exports.AsyncHookExecutionStrategy = exports.HookExecutionStrategy = exports.oncePerInstance = exports.resolved = exports.OnResolvedModule = exports.onResolved = exports.OnDisposeModule = exports.onScopeDisposed = exports.OnConstructModule = exports.onConstruct = exports.injectProp = exports.createHookExecutionContext = void 0;
|
|
5
|
+
exports.Is = exports.unwrapProxy = exports.ProxyRegistry = exports.pipe = exports.select = exports.TypedEvent = exports.once = exports.shallowCache = exports.debounce = void 0;
|
|
6
6
|
var IContainer_1 = require("./container/IContainer");
|
|
7
7
|
Object.defineProperty(exports, "isDependencyKey", { enumerable: true, get: function () { return IContainer_1.isDependencyKey; } });
|
|
8
8
|
var Container_1 = require("./container/Container");
|
|
@@ -63,6 +63,8 @@ var CannonSingletonApplyTwiceError_1 = require("./errors/CannonSingletonApplyTwi
|
|
|
63
63
|
Object.defineProperty(exports, "CannonSingletonApplyTwiceError", { enumerable: true, get: function () { return CannonSingletonApplyTwiceError_1.CannonSingletonApplyTwiceError; } });
|
|
64
64
|
var UnsupportedTokenTypeError_1 = require("./errors/UnsupportedTokenTypeError");
|
|
65
65
|
Object.defineProperty(exports, "UnsupportedTokenTypeError", { enumerable: true, get: function () { return UnsupportedTokenTypeError_1.UnsupportedTokenTypeError; } });
|
|
66
|
+
var TypedEventDisposedError_1 = require("./errors/TypedEventDisposedError");
|
|
67
|
+
Object.defineProperty(exports, "TypedEventDisposedError", { enumerable: true, get: function () { return TypedEventDisposedError_1.TypedEventDisposedError; } });
|
|
66
68
|
var hook_1 = require("./hooks/hook");
|
|
67
69
|
Object.defineProperty(exports, "getHooks", { enumerable: true, get: function () { return hook_1.getHooks; } });
|
|
68
70
|
Object.defineProperty(exports, "hook", { enumerable: true, get: function () { return hook_1.hook; } });
|
|
@@ -155,6 +157,8 @@ var shallowCache_1 = require("./utils/shallowCache");
|
|
|
155
157
|
Object.defineProperty(exports, "shallowCache", { enumerable: true, get: function () { return shallowCache_1.shallowCache; } });
|
|
156
158
|
var once_1 = require("./utils/once");
|
|
157
159
|
Object.defineProperty(exports, "once", { enumerable: true, get: function () { return once_1.once; } });
|
|
160
|
+
var TypedEvent_1 = require("./utils/TypedEvent");
|
|
161
|
+
Object.defineProperty(exports, "TypedEvent", { enumerable: true, get: function () { return TypedEvent_1.TypedEvent; } });
|
|
158
162
|
var select_1 = require("./select");
|
|
159
163
|
Object.defineProperty(exports, "select", { enumerable: true, get: function () { return select_1.select; } });
|
|
160
164
|
var fp_1 = require("./utils/fp");
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypedEvent = void 0;
|
|
4
|
+
const TypedEventDisposedError_1 = require("../errors/TypedEventDisposedError");
|
|
5
|
+
class TypedEvent {
|
|
6
|
+
listeners = new Set();
|
|
7
|
+
isDisposed = false;
|
|
8
|
+
subscribe(listener) {
|
|
9
|
+
this.validate();
|
|
10
|
+
this.listeners.add(listener);
|
|
11
|
+
return () => this.unsubscribe(listener);
|
|
12
|
+
}
|
|
13
|
+
unsubscribe(listener) {
|
|
14
|
+
this.listeners.delete(listener);
|
|
15
|
+
}
|
|
16
|
+
emit(...args) {
|
|
17
|
+
this.validate();
|
|
18
|
+
for (const listener of [...this.listeners]) {
|
|
19
|
+
if (this.listeners.has(listener)) {
|
|
20
|
+
listener(...args);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
getListeners() {
|
|
25
|
+
return [...this.listeners];
|
|
26
|
+
}
|
|
27
|
+
dispose() {
|
|
28
|
+
this.listeners.clear();
|
|
29
|
+
this.isDisposed = true;
|
|
30
|
+
}
|
|
31
|
+
validate() {
|
|
32
|
+
TypedEventDisposedError_1.TypedEventDisposedError.assert(!this.isDisposed, 'TypedEvent is already disposed');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.TypedEvent = TypedEvent;
|
|
@@ -6,6 +6,7 @@ import { unwrapProxy } from '../utils/ProxyRegistry.js';
|
|
|
6
6
|
import { DependencyNotFoundError } from '../errors/DependencyNotFoundError.js';
|
|
7
7
|
import { Is } from '../utils/basic.js';
|
|
8
8
|
import { Filter as F } from '../utils/array.js';
|
|
9
|
+
import { TypedEvent } from '../utils/TypedEvent.js';
|
|
9
10
|
export class Container {
|
|
10
11
|
isDisposed = false;
|
|
11
12
|
parent;
|
|
@@ -16,9 +17,12 @@ export class Container {
|
|
|
16
17
|
providers = new Map();
|
|
17
18
|
aliases = new AliasMap();
|
|
18
19
|
injector;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
scopeCreatedEvent = new TypedEvent();
|
|
21
|
+
scopeDisposedEvent = new TypedEvent();
|
|
22
|
+
registeredEvent = new TypedEvent();
|
|
23
|
+
scopeCreated = this.scopeCreatedEvent;
|
|
24
|
+
scopeDisposed = this.scopeDisposedEvent;
|
|
25
|
+
registered = this.registeredEvent;
|
|
22
26
|
constructor(options = {}) {
|
|
23
27
|
this.injector = options.injector ?? new MetadataInjector();
|
|
24
28
|
this.parent = options.parent ?? new EmptyContainer();
|
|
@@ -28,9 +32,7 @@ export class Container {
|
|
|
28
32
|
this.validateContainer();
|
|
29
33
|
this.providers.set(key, provider);
|
|
30
34
|
this.aliases.setAliasesByKey(key, aliases);
|
|
31
|
-
|
|
32
|
-
onRegistered(provider, key, this);
|
|
33
|
-
}
|
|
35
|
+
this.registeredEvent.emit(provider, key, this);
|
|
34
36
|
return this;
|
|
35
37
|
}
|
|
36
38
|
resolve(target, { args = [], child = this, lazy } = {}) {
|
|
@@ -73,17 +75,15 @@ export class Container {
|
|
|
73
75
|
}
|
|
74
76
|
createScope({ tags } = {}) {
|
|
75
77
|
this.validateContainer();
|
|
76
|
-
const scope = new Container({ injector: this.injector, parent: this, tags })
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
const scope = new Container({ injector: this.injector, parent: this, tags });
|
|
79
|
+
this.scopeCreatedEvent.getListeners().forEach((hook) => scope.scopeCreatedEvent.subscribe(hook));
|
|
80
|
+
this.scopeDisposedEvent.getListeners().forEach((hook) => scope.scopeDisposedEvent.subscribe(hook));
|
|
81
|
+
this.registeredEvent.getListeners().forEach((hook) => scope.registeredEvent.subscribe(hook));
|
|
80
82
|
for (const registration of this.getRegistrations()) {
|
|
81
83
|
registration.applyTo(scope);
|
|
82
84
|
}
|
|
83
85
|
this.scopes.push(scope);
|
|
84
|
-
|
|
85
|
-
onScopeCreated(scope);
|
|
86
|
-
}
|
|
86
|
+
this.scopeCreatedEvent.emit(scope);
|
|
87
87
|
return scope;
|
|
88
88
|
}
|
|
89
89
|
autoResolve({ args = [] } = {}) {
|
|
@@ -98,9 +98,7 @@ export class Container {
|
|
|
98
98
|
dispose() {
|
|
99
99
|
this.validateContainer();
|
|
100
100
|
this.isDisposed = true;
|
|
101
|
-
|
|
102
|
-
onScopeDisposed(this);
|
|
103
|
-
}
|
|
101
|
+
this.scopeDisposedEvent.emit(this);
|
|
104
102
|
this.parent.removeScope(this);
|
|
105
103
|
this.parent = new EmptyContainer();
|
|
106
104
|
for (const provider of this.providers.values()) {
|
|
@@ -110,9 +108,9 @@ export class Container {
|
|
|
110
108
|
this.aliases.destroy();
|
|
111
109
|
this.instances.clear();
|
|
112
110
|
this.registrations = [];
|
|
113
|
-
this.
|
|
114
|
-
this.
|
|
115
|
-
this.
|
|
111
|
+
this.scopeCreatedEvent.dispose();
|
|
112
|
+
this.scopeDisposedEvent.dispose();
|
|
113
|
+
this.registeredEvent.dispose();
|
|
116
114
|
}
|
|
117
115
|
addRegistration(registration) {
|
|
118
116
|
this.registrations.push(registration);
|
|
@@ -128,18 +126,6 @@ export class Container {
|
|
|
128
126
|
hasRegistration(key) {
|
|
129
127
|
return this.registrations.some((r) => r.getKeyOrFail() === key) || this.parent.hasRegistration(key);
|
|
130
128
|
}
|
|
131
|
-
onScopeCreated(...hooks) {
|
|
132
|
-
this.onScopeCreatedHookList.push(...hooks);
|
|
133
|
-
return this;
|
|
134
|
-
}
|
|
135
|
-
onScopeDisposed(...hooks) {
|
|
136
|
-
this.onScopeDisposedHookList.push(...hooks);
|
|
137
|
-
return this;
|
|
138
|
-
}
|
|
139
|
-
onRegistered(...hooks) {
|
|
140
|
-
this.onRegisteredHookList.push(...hooks);
|
|
141
|
-
return this;
|
|
142
|
-
}
|
|
143
129
|
addInstance(instance) {
|
|
144
130
|
this.instances.add(instance);
|
|
145
131
|
}
|
|
@@ -60,13 +60,13 @@ export class EmptyContainer {
|
|
|
60
60
|
resolveOneByAlias(alias, options) {
|
|
61
61
|
throw new DependencyNotFoundError(`Cannot find alias ${alias.toString()}`);
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
get scopeCreated() {
|
|
64
64
|
throw new MethodNotImplementedError();
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
get scopeDisposed() {
|
|
67
67
|
throw new MethodNotImplementedError();
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
get registered() {
|
|
70
70
|
throw new MethodNotImplementedError();
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError.js';
|
|
2
|
+
export class TypedEventDisposedError extends ContainerError {
|
|
3
|
+
name = 'TypedEventDisposedError';
|
|
4
|
+
static assert(isTrue, failMessage) {
|
|
5
|
+
if (!isTrue) {
|
|
6
|
+
throw new TypedEventDisposedError(failMessage);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
Object.setPrototypeOf(this, TypedEventDisposedError.prototype);
|
|
12
|
+
}
|
|
13
|
+
}
|
package/esm/hooks/onResolved.js
CHANGED
|
@@ -6,7 +6,7 @@ export class OnDisposeModule {
|
|
|
6
6
|
this.strategy = strategy;
|
|
7
7
|
}
|
|
8
8
|
applyTo(container) {
|
|
9
|
-
container.
|
|
9
|
+
container.scopeDisposed.subscribe((scope) => {
|
|
10
10
|
for (const instance of scope.getInstances()) {
|
|
11
11
|
this.strategy.execute(instance, { scope });
|
|
12
12
|
}
|
package/esm/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export { ContainerDisposedError } from './errors/ContainerDisposedError.js';
|
|
|
18
18
|
export { ProviderDisposedError } from './errors/ProviderDisposedError.js';
|
|
19
19
|
export { CannonSingletonApplyTwiceError } from './errors/CannonSingletonApplyTwiceError.js';
|
|
20
20
|
export { UnsupportedTokenTypeError } from './errors/UnsupportedTokenTypeError.js';
|
|
21
|
+
export { TypedEventDisposedError } from './errors/TypedEventDisposedError.js';
|
|
21
22
|
export { getHooks, hook, hasHooks, appendHooks, prependHooks, append, prepend, } from './hooks/hook.js';
|
|
22
23
|
export { HookContext, createHookContextFactory, createHookExecutionContext, } from './hooks/HookContext.js';
|
|
23
24
|
export { injectProp } from './hooks/injectProp.js';
|
|
@@ -48,6 +49,7 @@ export { throttle } from './utils/throttle.js';
|
|
|
48
49
|
export { debounce } from './utils/debounce.js';
|
|
49
50
|
export { shallowCache } from './utils/shallowCache.js';
|
|
50
51
|
export { once } from './utils/once.js';
|
|
52
|
+
export { TypedEvent } from './utils/TypedEvent.js';
|
|
51
53
|
export { select } from './select.js';
|
|
52
54
|
export { pipe } from './utils/fp.js';
|
|
53
55
|
export { ProxyRegistry, unwrapProxy } from './utils/ProxyRegistry.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { TypedEventDisposedError } from '../errors/TypedEventDisposedError.js';
|
|
2
|
+
export class TypedEvent {
|
|
3
|
+
listeners = new Set();
|
|
4
|
+
isDisposed = false;
|
|
5
|
+
subscribe(listener) {
|
|
6
|
+
this.validate();
|
|
7
|
+
this.listeners.add(listener);
|
|
8
|
+
return () => this.unsubscribe(listener);
|
|
9
|
+
}
|
|
10
|
+
unsubscribe(listener) {
|
|
11
|
+
this.listeners.delete(listener);
|
|
12
|
+
}
|
|
13
|
+
emit(...args) {
|
|
14
|
+
this.validate();
|
|
15
|
+
for (const listener of [...this.listeners]) {
|
|
16
|
+
if (this.listeners.has(listener)) {
|
|
17
|
+
listener(...args);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
getListeners() {
|
|
22
|
+
return [...this.listeners];
|
|
23
|
+
}
|
|
24
|
+
dispose() {
|
|
25
|
+
this.listeners.clear();
|
|
26
|
+
this.isDisposed = true;
|
|
27
|
+
}
|
|
28
|
+
validate() {
|
|
29
|
+
TypedEventDisposedError.assert(!this.isDisposed, 'TypedEvent is already disposed');
|
|
30
|
+
}
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ioc-container",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "65.0.0",
|
|
4
4
|
"description": "Fast, lightweight TypeScript dependency injection container with a clean API, scoped lifecycles, decorators, tokens, hooks, lazy injection, customizable providers, and no global container objects.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { type AutoResolveOptions, type CreateScopeOptions, type DependencyKey, type IContainer, type IContainerModule, type
|
|
1
|
+
import { type AutoResolveOptions, type CreateScopeOptions, type DependencyKey, type IContainer, type IContainerModule, type RegisterOptions, ResolveManyOptions, type ResolveOneOptions, type Tag } from './IContainer.js';
|
|
2
2
|
import { type IInjector } from '../injector/IInjector.js';
|
|
3
3
|
import { type IProvider } from '../provider/IProvider.js';
|
|
4
4
|
import { type IRegistration } from '../registration/IRegistration.js';
|
|
5
5
|
import { constructor, Instance } from '../utils/basic.js';
|
|
6
|
+
import { type ITypedEvent } from '../utils/TypedEvent.js';
|
|
6
7
|
export declare class Container implements IContainer {
|
|
7
8
|
isDisposed: boolean;
|
|
8
9
|
private parent;
|
|
@@ -13,9 +14,12 @@ export declare class Container implements IContainer {
|
|
|
13
14
|
private readonly providers;
|
|
14
15
|
private readonly aliases;
|
|
15
16
|
private readonly injector;
|
|
16
|
-
private readonly
|
|
17
|
-
private readonly
|
|
18
|
-
private readonly
|
|
17
|
+
private readonly scopeCreatedEvent;
|
|
18
|
+
private readonly scopeDisposedEvent;
|
|
19
|
+
private readonly registeredEvent;
|
|
20
|
+
readonly scopeCreated: ITypedEvent<[IContainer]>;
|
|
21
|
+
readonly scopeDisposed: ITypedEvent<[IContainer]>;
|
|
22
|
+
readonly registered: ITypedEvent<[IProvider, DependencyKey, IContainer]>;
|
|
19
23
|
constructor(options?: {
|
|
20
24
|
injector?: IInjector;
|
|
21
25
|
parent?: IContainer;
|
|
@@ -32,9 +36,6 @@ export declare class Container implements IContainer {
|
|
|
32
36
|
getRegistrations(): IRegistration[];
|
|
33
37
|
getInjector(): IInjector;
|
|
34
38
|
hasRegistration(key: DependencyKey): boolean;
|
|
35
|
-
onScopeCreated(...hooks: ScopeHook[]): this;
|
|
36
|
-
onScopeDisposed(...hooks: ScopeHook[]): this;
|
|
37
|
-
onRegistered(...hooks: RegisteredHook[]): this;
|
|
38
39
|
addInstance(instance: Instance): void;
|
|
39
40
|
getScopes(): IContainer[];
|
|
40
41
|
hasInstance(instance: Instance): boolean;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { type AutoResolveOptions, type DependencyKey, type IContainer, type IContainerModule, type
|
|
1
|
+
import { type AutoResolveOptions, type DependencyKey, type IContainer, type IContainerModule, type ResolveManyOptions, type ResolveOneOptions, type Tag } from './IContainer.js';
|
|
2
2
|
import { type IProvider } from '../provider/IProvider.js';
|
|
3
3
|
import { type IRegistration } from '../registration/IRegistration.js';
|
|
4
4
|
import { type constructor, type Instance } from '../utils/basic.js';
|
|
5
5
|
import { type IInjector } from '../injector/IInjector.js';
|
|
6
|
+
import { type ITypedEvent } from '../utils/TypedEvent.js';
|
|
6
7
|
export declare class EmptyContainer implements IContainer {
|
|
7
8
|
get isDisposed(): boolean;
|
|
8
9
|
addInstance(instance: Instance): void;
|
|
@@ -25,7 +26,7 @@ export declare class EmptyContainer implements IContainer {
|
|
|
25
26
|
resolve<T>(key: constructor<T> | DependencyKey, options?: ResolveOneOptions): T;
|
|
26
27
|
resolveByAlias<T>(alias: DependencyKey, options?: ResolveManyOptions): T[];
|
|
27
28
|
resolveOneByAlias<T>(alias: DependencyKey, options?: ResolveOneOptions): T;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
get scopeCreated(): ITypedEvent<[IContainer]>;
|
|
30
|
+
get scopeDisposed(): ITypedEvent<[IContainer]>;
|
|
31
|
+
get registered(): ITypedEvent<[IProvider, DependencyKey, IContainer]>;
|
|
31
32
|
}
|
|
@@ -2,6 +2,7 @@ import { type IProvider, ProviderOptions } from '../provider/IProvider.js';
|
|
|
2
2
|
import { type IRegistration } from '../registration/IRegistration.js';
|
|
3
3
|
import { IInjector, type WithArgs } from '../injector/IInjector.js';
|
|
4
4
|
import { type constructor, Instance } from '../utils/basic.js';
|
|
5
|
+
import { type ITypedEvent } from '../utils/TypedEvent.js';
|
|
5
6
|
export type DependencyKey = string | symbol;
|
|
6
7
|
export declare function isDependencyKey(target: unknown): target is DependencyKey;
|
|
7
8
|
export type Tag = string;
|
|
@@ -35,9 +36,9 @@ export type ScopeHook = (scope: IContainer) => void;
|
|
|
35
36
|
export type RegisteredHook = (provider: IProvider, key: DependencyKey, scope: IContainer) => void;
|
|
36
37
|
export interface IContainer extends Tagged {
|
|
37
38
|
readonly isDisposed: boolean;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
readonly scopeCreated: ITypedEvent<[IContainer]>;
|
|
40
|
+
readonly scopeDisposed: ITypedEvent<[IContainer]>;
|
|
41
|
+
readonly registered: ITypedEvent<[IProvider, DependencyKey, IContainer]>;
|
|
41
42
|
register(key: DependencyKey, value: IProvider, options?: RegisterOptions): this;
|
|
42
43
|
addRegistration(registration: IRegistration): this;
|
|
43
44
|
getRegistrations(): IRegistration[];
|
package/typings/index.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { ContainerDisposedError } from './errors/ContainerDisposedError.js';
|
|
|
19
19
|
export { ProviderDisposedError } from './errors/ProviderDisposedError.js';
|
|
20
20
|
export { CannonSingletonApplyTwiceError } from './errors/CannonSingletonApplyTwiceError.js';
|
|
21
21
|
export { UnsupportedTokenTypeError } from './errors/UnsupportedTokenTypeError.js';
|
|
22
|
+
export { TypedEventDisposedError } from './errors/TypedEventDisposedError.js';
|
|
22
23
|
export { getHooks, hook, hasHooks, appendHooks, prependHooks, append, prepend, type HookFn, type HookClass, type HookType, type MapHooksFn, type InjectFn, type HooksOfClass, } from './hooks/hook.js';
|
|
23
24
|
export { HookContext, createHookContextFactory, createHookExecutionContext, type CreateHookExecutionContext, type IHookContext, } from './hooks/HookContext.js';
|
|
24
25
|
export { injectProp } from './hooks/injectProp.js';
|
|
@@ -49,6 +50,7 @@ export { throttle } from './utils/throttle.js';
|
|
|
49
50
|
export { debounce } from './utils/debounce.js';
|
|
50
51
|
export { shallowCache } from './utils/shallowCache.js';
|
|
51
52
|
export { once } from './utils/once.js';
|
|
53
|
+
export { TypedEvent, type ITypedEvent, type TypedEventListener, type Unsubscribe } from './utils/TypedEvent.js';
|
|
52
54
|
export { type ExecutionContext } from './ExecutionContext.js';
|
|
53
55
|
export { select } from './select.js';
|
|
54
56
|
export { pipe, type MapFn } from './utils/fp.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type TypedEventListener<TArgs extends unknown[]> = (...args: TArgs) => void;
|
|
2
|
+
export type Unsubscribe = () => void;
|
|
3
|
+
export interface ITypedEvent<TArgs extends unknown[]> {
|
|
4
|
+
subscribe(listener: TypedEventListener<TArgs>): Unsubscribe;
|
|
5
|
+
unsubscribe(listener: TypedEventListener<TArgs>): void;
|
|
6
|
+
dispose(): void;
|
|
7
|
+
}
|
|
8
|
+
export declare class TypedEvent<TArgs extends unknown[] = []> implements ITypedEvent<TArgs> {
|
|
9
|
+
private readonly listeners;
|
|
10
|
+
private isDisposed;
|
|
11
|
+
subscribe(listener: TypedEventListener<TArgs>): Unsubscribe;
|
|
12
|
+
unsubscribe(listener: TypedEventListener<TArgs>): void;
|
|
13
|
+
emit(...args: TArgs): void;
|
|
14
|
+
getListeners(): TypedEventListener<TArgs>[];
|
|
15
|
+
dispose(): void;
|
|
16
|
+
private validate;
|
|
17
|
+
}
|