ts-ioc-container 72.0.0 → 72.2.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/AGENTS.md +179 -0
- package/README.md +94 -0
- package/cjm/container/Container.js +2 -2
- package/cjm/container/EmptyContainer.js +17 -15
- package/cjm/errors/ArgumentNotFoundError.js +1 -0
- package/cjm/errors/CannonSingletonApplyTwiceError.js +2 -14
- package/cjm/errors/CannotApplySingletonTwiceError.js +18 -0
- package/cjm/errors/ContainerDisposedError.js +1 -0
- package/cjm/errors/ContainerError.js +1 -0
- package/cjm/errors/ContainerNotFoundError.js +1 -0
- package/cjm/errors/DependencyMissingKeyError.js +1 -0
- package/cjm/errors/DependencyNotFoundError.js +1 -0
- package/cjm/errors/MethodNotImplementedError.js +1 -0
- package/cjm/errors/ProviderDisposedError.js +1 -0
- package/cjm/errors/TypedEventDisposedError.js +1 -0
- package/cjm/errors/UnsupportedTokenTypeError.js +1 -0
- package/cjm/index.js +8 -3
- package/cjm/metadata/class.js +3 -1
- package/cjm/metadata/method.js +3 -1
- package/cjm/metadata/parameter.js +7 -1
- package/cjm/provider/Provider.js +6 -6
- package/cjm/registration/Registration.js +2 -2
- package/cjm/token/ConstantToken.js +4 -4
- package/cjm/token/FunctionToken.js +1 -1
- package/cjm/token/GroupInstanceToken.js +4 -4
- package/cjm/token/toToken.js +1 -1
- package/cjm/utils/TypedEvent.js +1 -1
- package/cjm/utils/array.js +1 -1
- package/esm/container/Container.js +2 -2
- package/esm/container/EmptyContainer.js +17 -15
- package/esm/errors/ArgumentNotFoundError.js +1 -0
- package/esm/errors/CannonSingletonApplyTwiceError.js +2 -13
- package/esm/errors/CannotApplySingletonTwiceError.js +14 -0
- package/esm/errors/ContainerDisposedError.js +1 -0
- package/esm/errors/ContainerError.js +1 -0
- package/esm/errors/ContainerNotFoundError.js +1 -0
- package/esm/errors/DependencyMissingKeyError.js +1 -0
- package/esm/errors/DependencyNotFoundError.js +1 -0
- package/esm/errors/MethodNotImplementedError.js +1 -0
- package/esm/errors/ProviderDisposedError.js +1 -0
- package/esm/errors/TypedEventDisposedError.js +1 -0
- package/esm/errors/UnsupportedTokenTypeError.js +1 -0
- package/esm/index.js +4 -3
- package/esm/metadata/class.js +1 -0
- package/esm/metadata/method.js +1 -0
- package/esm/metadata/parameter.js +5 -0
- package/esm/provider/Provider.js +6 -6
- package/esm/registration/Registration.js +2 -2
- package/esm/token/ConstantToken.js +4 -4
- package/esm/token/FunctionToken.js +1 -1
- package/esm/token/GroupInstanceToken.js +4 -4
- package/esm/token/toToken.js +1 -1
- package/esm/utils/TypedEvent.js +1 -1
- package/esm/utils/array.js +1 -1
- package/lib/ExecutionContext.ts +6 -0
- package/lib/container/AliasMap.ts +30 -0
- package/lib/container/AutoResolveModule.ts +24 -0
- package/lib/container/Container.ts +311 -0
- package/lib/container/EmptyContainer.ts +166 -0
- package/lib/container/IContainer.ts +156 -0
- package/lib/errors/ArgumentNotFoundError.ts +13 -0
- package/lib/errors/CannonSingletonApplyTwiceError.ts +6 -0
- package/lib/errors/CannotApplySingletonTwiceError.ts +22 -0
- package/lib/errors/ContainerDisposedError.ts +13 -0
- package/lib/errors/ContainerError.ts +15 -0
- package/lib/errors/ContainerNotFoundError.ts +13 -0
- package/lib/errors/DependencyMissingKeyError.ts +13 -0
- package/lib/errors/DependencyNotFoundError.ts +13 -0
- package/lib/errors/MethodNotImplementedError.ts +13 -0
- package/lib/errors/ProviderDisposedError.ts +22 -0
- package/lib/errors/TypedEventDisposedError.ts +22 -0
- package/lib/errors/UnsupportedTokenTypeError.ts +13 -0
- package/lib/hooks/HookCollector.ts +109 -0
- package/lib/hooks/HookContext.ts +85 -0
- package/lib/hooks/combinators.ts +51 -0
- package/lib/hooks/hook.ts +89 -0
- package/lib/hooks/injectProp.ts +14 -0
- package/lib/index.ts +181 -0
- package/lib/injector/IInjector.ts +88 -0
- package/lib/injector/MetadataInjector.ts +100 -0
- package/lib/injector/ProxyInjector.ts +31 -0
- package/lib/injector/SimpleInjector.ts +20 -0
- package/lib/metadata/class.ts +50 -0
- package/lib/metadata/method.ts +47 -0
- package/lib/metadata/parameter.ts +60 -0
- package/lib/metadata/target.ts +25 -0
- package/lib/provider/IProvider.ts +65 -0
- package/lib/provider/Provider.ts +174 -0
- package/lib/registration/IRegistration.ts +235 -0
- package/lib/registration/Registration.ts +122 -0
- package/lib/select.ts +33 -0
- package/lib/token/BindToken.ts +13 -0
- package/lib/token/ClassToken.ts +72 -0
- package/lib/token/ConstantToken.ts +50 -0
- package/lib/token/FunctionToken.ts +79 -0
- package/lib/token/GroupAliasToken.ts +92 -0
- package/lib/token/GroupInstanceToken.ts +70 -0
- package/lib/token/InjectionToken.ts +46 -0
- package/lib/token/SingleAliasToken.ts +84 -0
- package/lib/token/SingleToken.ts +93 -0
- package/lib/token/toToken.ts +46 -0
- package/lib/utils/ProxyRegistry.ts +149 -0
- package/lib/utils/TypedEvent.ts +96 -0
- package/lib/utils/array.ts +28 -0
- package/lib/utils/basic.ts +34 -0
- package/lib/utils/debounce.ts +18 -0
- package/lib/utils/errorHandler.ts +34 -0
- package/lib/utils/fp.ts +86 -0
- package/lib/utils/getConstructorChain.ts +17 -0
- package/lib/utils/memoize.ts +24 -0
- package/lib/utils/once.ts +24 -0
- package/lib/utils/shallowCache.ts +22 -0
- package/lib/utils/task.ts +33 -0
- package/lib/utils/throttle.ts +17 -0
- package/package.json +5 -3
- package/typings/ExecutionContext.d.ts +2 -0
- package/typings/ExecutionContext.d.ts.map +1 -0
- package/typings/container/AliasMap.d.ts +1 -0
- package/typings/container/AliasMap.d.ts.map +1 -0
- package/typings/container/AutoResolveModule.d.ts +16 -0
- package/typings/container/AutoResolveModule.d.ts.map +1 -0
- package/typings/container/Container.d.ts +60 -0
- package/typings/container/Container.d.ts.map +1 -0
- package/typings/container/EmptyContainer.d.ts +51 -0
- package/typings/container/EmptyContainer.d.ts.map +1 -0
- package/typings/container/IContainer.d.ts +75 -0
- package/typings/container/IContainer.d.ts.map +1 -0
- package/typings/errors/ArgumentNotFoundError.d.ts +3 -0
- package/typings/errors/ArgumentNotFoundError.d.ts.map +1 -0
- package/typings/errors/CannonSingletonApplyTwiceError.d.ts +6 -6
- package/typings/errors/CannonSingletonApplyTwiceError.d.ts.map +1 -0
- package/typings/errors/CannotApplySingletonTwiceError.d.ts +12 -0
- package/typings/errors/CannotApplySingletonTwiceError.d.ts.map +1 -0
- package/typings/errors/ContainerDisposedError.d.ts +3 -0
- package/typings/errors/ContainerDisposedError.d.ts.map +1 -0
- package/typings/errors/ContainerError.d.ts +7 -0
- package/typings/errors/ContainerError.d.ts.map +1 -0
- package/typings/errors/ContainerNotFoundError.d.ts +3 -0
- package/typings/errors/ContainerNotFoundError.d.ts.map +1 -0
- package/typings/errors/DependencyMissingKeyError.d.ts +3 -0
- package/typings/errors/DependencyMissingKeyError.d.ts.map +1 -0
- package/typings/errors/DependencyNotFoundError.d.ts +3 -0
- package/typings/errors/DependencyNotFoundError.d.ts.map +1 -0
- package/typings/errors/MethodNotImplementedError.d.ts +3 -0
- package/typings/errors/MethodNotImplementedError.d.ts.map +1 -0
- package/typings/errors/ProviderDisposedError.d.ts +6 -0
- package/typings/errors/ProviderDisposedError.d.ts.map +1 -0
- package/typings/errors/TypedEventDisposedError.d.ts +6 -0
- package/typings/errors/TypedEventDisposedError.d.ts.map +1 -0
- package/typings/errors/UnsupportedTokenTypeError.d.ts +3 -0
- package/typings/errors/UnsupportedTokenTypeError.d.ts.map +1 -0
- package/typings/hooks/HookCollector.d.ts +36 -0
- package/typings/hooks/HookCollector.d.ts.map +1 -0
- package/typings/hooks/HookContext.d.ts +10 -0
- package/typings/hooks/HookContext.d.ts.map +1 -0
- package/typings/hooks/combinators.d.ts +23 -0
- package/typings/hooks/combinators.d.ts.map +1 -0
- package/typings/hooks/hook.d.ts +39 -0
- package/typings/hooks/hook.d.ts.map +1 -0
- package/typings/hooks/injectProp.d.ts +9 -0
- package/typings/hooks/injectProp.d.ts.map +1 -0
- package/typings/index.d.ts +5 -3
- package/typings/index.d.ts.map +1 -0
- package/typings/injector/IInjector.d.ts +32 -0
- package/typings/injector/IInjector.d.ts.map +1 -0
- package/typings/injector/MetadataInjector.d.ts +58 -0
- package/typings/injector/MetadataInjector.d.ts.map +1 -0
- package/typings/injector/ProxyInjector.d.ts +13 -0
- package/typings/injector/ProxyInjector.d.ts.map +1 -0
- package/typings/injector/SimpleInjector.d.ts +13 -0
- package/typings/injector/SimpleInjector.d.ts.map +1 -0
- package/typings/metadata/class.d.ts +28 -0
- package/typings/metadata/class.d.ts.map +1 -0
- package/typings/metadata/method.d.ts +25 -0
- package/typings/metadata/method.d.ts.map +1 -0
- package/typings/metadata/parameter.d.ts +22 -0
- package/typings/metadata/parameter.d.ts.map +1 -0
- package/typings/metadata/target.d.ts +19 -0
- package/typings/metadata/target.d.ts.map +1 -0
- package/typings/provider/IProvider.d.ts +25 -0
- package/typings/provider/IProvider.d.ts.map +1 -0
- package/typings/provider/Provider.d.ts +30 -0
- package/typings/provider/Provider.d.ts.map +1 -0
- package/typings/registration/IRegistration.d.ts +137 -0
- package/typings/registration/IRegistration.d.ts.map +1 -0
- package/typings/registration/Registration.d.ts +23 -0
- package/typings/registration/Registration.d.ts.map +1 -0
- package/typings/select.d.ts +15 -0
- package/typings/select.d.ts.map +1 -0
- package/typings/token/BindToken.d.ts +3 -0
- package/typings/token/BindToken.d.ts.map +1 -0
- package/typings/token/ClassToken.d.ts +2 -0
- package/typings/token/ClassToken.d.ts.map +1 -0
- package/typings/token/ConstantToken.d.ts +14 -0
- package/typings/token/ConstantToken.d.ts.map +1 -0
- package/typings/token/FunctionToken.d.ts +10 -0
- package/typings/token/FunctionToken.d.ts.map +1 -0
- package/typings/token/GroupAliasToken.d.ts +14 -0
- package/typings/token/GroupAliasToken.d.ts.map +1 -0
- package/typings/token/GroupInstanceToken.d.ts +19 -0
- package/typings/token/GroupInstanceToken.d.ts.map +1 -0
- package/typings/token/InjectionToken.d.ts +8 -0
- package/typings/token/InjectionToken.d.ts.map +1 -0
- package/typings/token/SingleAliasToken.d.ts +6 -0
- package/typings/token/SingleAliasToken.d.ts.map +1 -0
- package/typings/token/SingleToken.d.ts +18 -0
- package/typings/token/SingleToken.d.ts.map +1 -0
- package/typings/token/toToken.d.ts +12 -0
- package/typings/token/toToken.d.ts.map +1 -0
- package/typings/utils/ProxyRegistry.d.ts +63 -0
- package/typings/utils/ProxyRegistry.d.ts.map +1 -0
- package/typings/utils/TypedEvent.d.ts +42 -0
- package/typings/utils/TypedEvent.d.ts.map +1 -0
- package/typings/utils/array.d.ts +7 -0
- package/typings/utils/array.d.ts.map +1 -0
- package/typings/utils/basic.d.ts +12 -0
- package/typings/utils/basic.d.ts.map +1 -0
- package/typings/utils/debounce.d.ts +2 -0
- package/typings/utils/debounce.d.ts.map +1 -0
- package/typings/utils/errorHandler.d.ts +4 -0
- package/typings/utils/errorHandler.d.ts.map +1 -0
- package/typings/utils/fp.d.ts +8 -0
- package/typings/utils/fp.d.ts.map +1 -0
- package/typings/utils/getConstructorChain.d.ts +9 -0
- package/typings/utils/getConstructorChain.d.ts.map +1 -0
- package/typings/utils/memoize.d.ts +10 -0
- package/typings/utils/memoize.d.ts.map +1 -0
- package/typings/utils/once.d.ts +2 -0
- package/typings/utils/once.d.ts.map +1 -0
- package/typings/utils/shallowCache.d.ts +2 -0
- package/typings/utils/shallowCache.d.ts.map +1 -0
- package/typings/utils/task.d.ts +13 -0
- package/typings/utils/task.d.ts.map +1 -0
- package/typings/utils/throttle.d.ts +2 -0
- package/typings/utils/throttle.d.ts.map +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type AutoResolveOptions, type IContainer, type IContainerModule } from './IContainer';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enables eager resolution of providers marked with the `autoResolve()` pipe.
|
|
5
|
+
*
|
|
6
|
+
* Every scope created after this module is applied resolves its own
|
|
7
|
+
* auto-resolvable providers as part of `createScope`, so the instances exist
|
|
8
|
+
* before anyone asks for them. Child scopes inherit the behaviour, so a single
|
|
9
|
+
* `useModule` call on the root covers the whole scope tree.
|
|
10
|
+
*
|
|
11
|
+
* `options.args` are forwarded to every eagerly resolved provider of every such
|
|
12
|
+
* scope. Pass a per-scope value by calling `scope.autoResolve({ args })`
|
|
13
|
+
* directly instead.
|
|
14
|
+
*
|
|
15
|
+
* The container the module is applied to is not a created scope — call
|
|
16
|
+
* `container.autoResolve()` explicitly to eagerly resolve its own providers.
|
|
17
|
+
*/
|
|
18
|
+
export class AutoResolveModule implements IContainerModule {
|
|
19
|
+
constructor(private readonly options: AutoResolveOptions = {}) {}
|
|
20
|
+
|
|
21
|
+
applyTo(container: IContainer): void {
|
|
22
|
+
container.scopeCreated.subscribe((scope) => scope.autoResolve(this.options));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AutoResolveOptions,
|
|
3
|
+
type CreateScopeOptions,
|
|
4
|
+
type DependencyKey,
|
|
5
|
+
type IContainer,
|
|
6
|
+
type IContainerModule,
|
|
7
|
+
type RegisterOptions,
|
|
8
|
+
ResolveManyOptions,
|
|
9
|
+
type ResolveOneOptions,
|
|
10
|
+
type Tag,
|
|
11
|
+
} from './IContainer';
|
|
12
|
+
import { type IInjector } from '../injector/IInjector';
|
|
13
|
+
import { type IProvider } from '../provider/IProvider';
|
|
14
|
+
import { EmptyContainer } from './EmptyContainer';
|
|
15
|
+
import { type IRegistration } from '../registration/IRegistration';
|
|
16
|
+
import { ContainerDisposedError } from '../errors/ContainerDisposedError';
|
|
17
|
+
import { MetadataInjector } from '../injector/MetadataInjector';
|
|
18
|
+
import { AliasMap } from './AliasMap';
|
|
19
|
+
import { unwrapProxy } from '../utils/ProxyRegistry';
|
|
20
|
+
import { DependencyNotFoundError } from '../errors/DependencyNotFoundError';
|
|
21
|
+
import { constructor, Instance, Is } from '../utils/basic';
|
|
22
|
+
import { Filter as F } from '../utils/array';
|
|
23
|
+
import { type ITypedEvent, TypedEvent } from '../utils/TypedEvent';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A dependency injection scope. Each container is a node in a parent chain:
|
|
27
|
+
* a key not found here (or denied by `scopeAccess`) is resolved from the parent,
|
|
28
|
+
* and the root's parent is an {@link EmptyContainer} that throws
|
|
29
|
+
* `DependencyNotFoundError`. Child scopes come from `createScope({ tags })`,
|
|
30
|
+
* which copies the matching registrations into the child at that moment.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const app = new Container({ tags: ['application'] })
|
|
34
|
+
* .addRegistration(Registration.fromClass(Logger))
|
|
35
|
+
* .addRegistration(Registration.fromValue(config).bindTo('Config'));
|
|
36
|
+
*
|
|
37
|
+
* const request = app.createScope({ tags: ['request'] });
|
|
38
|
+
* const logger = ILoggerToken.resolve(request);
|
|
39
|
+
* request.dispose();
|
|
40
|
+
*/
|
|
41
|
+
export class Container implements IContainer {
|
|
42
|
+
isDisposed = false;
|
|
43
|
+
private parent: IContainer;
|
|
44
|
+
private scopes: IContainer[] = [];
|
|
45
|
+
private readonly instances = new Set<Instance>();
|
|
46
|
+
private registrations: IRegistration[] = [];
|
|
47
|
+
private readonly tags: Set<Tag>;
|
|
48
|
+
private readonly providers = new Map<DependencyKey, IProvider>();
|
|
49
|
+
private readonly aliases = new AliasMap();
|
|
50
|
+
private readonly injector: IInjector;
|
|
51
|
+
|
|
52
|
+
// The concrete events stay private so `emit` is this container's alone; the
|
|
53
|
+
// public properties expose the subscriber's side only.
|
|
54
|
+
private readonly scopeCreatedEvent = new TypedEvent<[IContainer]>();
|
|
55
|
+
private readonly scopeDisposedEvent = new TypedEvent<[IContainer]>();
|
|
56
|
+
private readonly registeredEvent = new TypedEvent<[IProvider, DependencyKey, IContainer]>();
|
|
57
|
+
readonly scopeCreated: ITypedEvent<[IContainer]> = this.scopeCreatedEvent;
|
|
58
|
+
readonly scopeDisposed: ITypedEvent<[IContainer]> = this.scopeDisposedEvent;
|
|
59
|
+
readonly registered: ITypedEvent<[IProvider, DependencyKey, IContainer]> = this.registeredEvent;
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
options: {
|
|
63
|
+
injector?: IInjector;
|
|
64
|
+
parent?: IContainer;
|
|
65
|
+
tags?: Tag[];
|
|
66
|
+
} = {},
|
|
67
|
+
) {
|
|
68
|
+
this.injector = options.injector ?? new MetadataInjector();
|
|
69
|
+
this.parent = options.parent ?? new EmptyContainer();
|
|
70
|
+
this.tags = new Set(options.tags ?? []);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @throws {ContainerDisposedError} when the container has already been disposed.
|
|
75
|
+
*/
|
|
76
|
+
register(key: DependencyKey, provider: IProvider, { aliases = [] }: RegisterOptions = {}): this {
|
|
77
|
+
this.validateContainer();
|
|
78
|
+
this.providers.set(key, provider);
|
|
79
|
+
this.aliases.setAliasesByKey(key, aliases);
|
|
80
|
+
|
|
81
|
+
// Hooks run once the provider and its aliases are in place, so they observe a resolvable key.
|
|
82
|
+
this.registeredEvent.emit(provider, key, this);
|
|
83
|
+
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @throws {ContainerDisposedError} when the container has already been disposed.
|
|
89
|
+
* @throws {DependencyNotFoundError} when `target` cannot be resolved in this container or any parent scope.
|
|
90
|
+
*/
|
|
91
|
+
resolve<T>(target: constructor<T> | DependencyKey, { args = [], child = this, lazy }: ResolveOneOptions = {}): T {
|
|
92
|
+
this.validateContainer();
|
|
93
|
+
|
|
94
|
+
if (Is.constructor(target)) {
|
|
95
|
+
return this.injector.resolve(target, { scope: this, args, lazy });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const provider = this.providers.get(target) as IProvider<T> | undefined;
|
|
99
|
+
|
|
100
|
+
return provider?.hasAccess({ invocationScope: child, providerScope: this, args })
|
|
101
|
+
? provider.resolve({ scope: this, args, lazy })
|
|
102
|
+
: this.parent.resolve<T>(target, { args, child, lazy });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @throws {ContainerDisposedError} when the container has already been disposed.
|
|
107
|
+
* @throws {DependencyNotFoundError} when a key registered under `alias` has no matching provider.
|
|
108
|
+
*/
|
|
109
|
+
resolveByAlias<T>(
|
|
110
|
+
alias: DependencyKey,
|
|
111
|
+
{ args = [], child = this, lazy, excludedKeys = [] }: ResolveManyOptions = {},
|
|
112
|
+
): T[] {
|
|
113
|
+
this.validateContainer();
|
|
114
|
+
|
|
115
|
+
const keys: DependencyKey[] = [];
|
|
116
|
+
const deps: T[] = [];
|
|
117
|
+
for (const key of this.aliases.getKeysByAlias(alias).filter(F.exclude(excludedKeys))) {
|
|
118
|
+
const provider = this.findProviderByKeyOrFail<T>(key);
|
|
119
|
+
if (!provider.hasAccess({ invocationScope: child, providerScope: this, args })) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
keys.push(key);
|
|
123
|
+
deps.push(provider.resolve({ scope: this, args, lazy }));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const parentDeps = this.parent.resolveByAlias<T>(alias, {
|
|
127
|
+
args,
|
|
128
|
+
child,
|
|
129
|
+
lazy,
|
|
130
|
+
excludedKeys: [...excludedKeys, ...keys],
|
|
131
|
+
});
|
|
132
|
+
return [...deps, ...parentDeps];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @throws {ContainerDisposedError} when the container has already been disposed.
|
|
137
|
+
* @throws {DependencyNotFoundError} when `alias` cannot be resolved in this container or any parent scope.
|
|
138
|
+
*/
|
|
139
|
+
resolveOneByAlias<T>(alias: DependencyKey, { args = [], child = this, lazy }: ResolveOneOptions = {}): T {
|
|
140
|
+
this.validateContainer();
|
|
141
|
+
|
|
142
|
+
const [key] = this.aliases.getKeysByAlias(alias);
|
|
143
|
+
const provider = key ? this.findProviderByKeyOrFail<T>(key) : undefined;
|
|
144
|
+
|
|
145
|
+
return provider?.hasAccess({ invocationScope: child, providerScope: this, args })
|
|
146
|
+
? provider.resolve({ scope: this, args, lazy })
|
|
147
|
+
: this.parent.resolveOneByAlias<T>(alias, { args, child, lazy });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @throws {ContainerDisposedError} when the container has already been disposed.
|
|
152
|
+
*/
|
|
153
|
+
createScope({ tags }: CreateScopeOptions = {}): IContainer {
|
|
154
|
+
this.validateContainer();
|
|
155
|
+
|
|
156
|
+
// Injector hooks need no copying - the child shares this scope's injector, so it shares its hooks.
|
|
157
|
+
const scope = new Container({ injector: this.injector, parent: this, tags });
|
|
158
|
+
this.scopeCreatedEvent.getListeners().forEach((hook) => scope.scopeCreatedEvent.subscribe(hook));
|
|
159
|
+
this.scopeDisposedEvent.getListeners().forEach((hook) => scope.scopeDisposedEvent.subscribe(hook));
|
|
160
|
+
this.registeredEvent.getListeners().forEach((hook) => scope.registeredEvent.subscribe(hook));
|
|
161
|
+
|
|
162
|
+
for (const registration of this.getRegistrations()) {
|
|
163
|
+
registration.applyTo(scope);
|
|
164
|
+
}
|
|
165
|
+
this.scopes.push(scope);
|
|
166
|
+
|
|
167
|
+
// Hooks run once the scope is fully registered and attached, so they observe a usable scope.
|
|
168
|
+
this.scopeCreatedEvent.emit(scope);
|
|
169
|
+
|
|
170
|
+
return scope;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Eagerly resolves every provider of this scope which was marked with `autoResolve()`.
|
|
175
|
+
*
|
|
176
|
+
* `args` are forwarded to each of those providers, exactly as `resolve` forwards them.
|
|
177
|
+
*
|
|
178
|
+
* @throws {ContainerDisposedError} when the container has already been disposed.
|
|
179
|
+
*/
|
|
180
|
+
autoResolve({ args = [] }: AutoResolveOptions = {}): this {
|
|
181
|
+
this.validateContainer();
|
|
182
|
+
|
|
183
|
+
for (const provider of this.providers.values()) {
|
|
184
|
+
if (provider.isAutoResolvable() && provider.hasAccess({ invocationScope: this, providerScope: this, args })) {
|
|
185
|
+
provider.resolve({ scope: this, args });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @throws {ContainerDisposedError} when the container has already been disposed.
|
|
194
|
+
*/
|
|
195
|
+
dispose(): void {
|
|
196
|
+
this.validateContainer();
|
|
197
|
+
this.isDisposed = true;
|
|
198
|
+
|
|
199
|
+
this.scopeDisposedEvent.emit(this);
|
|
200
|
+
|
|
201
|
+
// Detach from parent
|
|
202
|
+
this.parent.removeScope(this);
|
|
203
|
+
this.parent = new EmptyContainer();
|
|
204
|
+
|
|
205
|
+
// Reset the state
|
|
206
|
+
for (const provider of this.providers.values()) {
|
|
207
|
+
provider.dispose();
|
|
208
|
+
}
|
|
209
|
+
this.providers.clear();
|
|
210
|
+
this.aliases.destroy();
|
|
211
|
+
this.instances.clear();
|
|
212
|
+
this.registrations = [];
|
|
213
|
+
|
|
214
|
+
// Dispose scope events. Injector hooks are not this scope's to clear - the injector outlives it.
|
|
215
|
+
this.scopeCreatedEvent.dispose();
|
|
216
|
+
this.scopeDisposedEvent.dispose();
|
|
217
|
+
this.registeredEvent.dispose();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
addRegistration(registration: IRegistration): this {
|
|
221
|
+
this.registrations.push(registration);
|
|
222
|
+
registration.applyTo(this);
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
getRegistrations(): IRegistration[] {
|
|
227
|
+
return [...this.parent.getRegistrations(), ...this.registrations];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
getInjector(): IInjector {
|
|
231
|
+
return this.injector;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
hasRegistration(key: DependencyKey): boolean {
|
|
235
|
+
return this.registrations.some((r) => r.getKeyOrFail() === key) || this.parent.hasRegistration(key);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
addInstance(instance: Instance) {
|
|
239
|
+
this.instances.add(instance);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
getScopes() {
|
|
243
|
+
return [...this.scopes];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Whether this scope created `instance`.
|
|
248
|
+
*
|
|
249
|
+
* A proxy stands for the object behind it, so it is unwrapped before the
|
|
250
|
+
* lookup - the container tracks real instances, and a caller asking about a
|
|
251
|
+
* proxy is asking about its target. Resolving a lazy proxy's target is what
|
|
252
|
+
* makes that answer possible, so a still-unresolved lazy proxy is resolved
|
|
253
|
+
* here.
|
|
254
|
+
*/
|
|
255
|
+
hasInstance(instance: Instance): boolean {
|
|
256
|
+
return this.instances.has(unwrapProxy(instance));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
removeScope(child: IContainer): void {
|
|
260
|
+
this.scopes = this.scopes.filter((s) => s !== child);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
useModule(module: IContainerModule): this {
|
|
264
|
+
module.applyTo(this);
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
getParent() {
|
|
269
|
+
return this.parent;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
getInstances(cascade: boolean = false) {
|
|
273
|
+
if (!cascade) {
|
|
274
|
+
return [...this.instances];
|
|
275
|
+
}
|
|
276
|
+
return [...this.instances, ...this.scopes.flatMap((s) => s.getInstances(true))];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
hasTag(tag: Tag) {
|
|
280
|
+
return this.tags.has(tag);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
addTags(...tags: Tag[]) {
|
|
284
|
+
for (const tag of tags) {
|
|
285
|
+
this.tags.add(tag);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @throws {ContainerDisposedError} when the container has already been disposed.
|
|
291
|
+
*/
|
|
292
|
+
private validateContainer(): void {
|
|
293
|
+
if (this.isDisposed) {
|
|
294
|
+
throw new ContainerDisposedError(
|
|
295
|
+
'Container is already disposed: a scope cannot be used after dispose(). Create a new scope.',
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @throws {DependencyNotFoundError} when no provider is registered under `key` in this container.
|
|
302
|
+
*/
|
|
303
|
+
private findProviderByKeyOrFail<T>(key: DependencyKey): IProvider<T> {
|
|
304
|
+
if (!this.providers.has(key)) {
|
|
305
|
+
throw new DependencyNotFoundError(
|
|
306
|
+
`Provider ${key.toString()} does not exist: an alias points at a key with no provider in this scope.`,
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
return this.providers.get(key)!;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AutoResolveOptions,
|
|
3
|
+
type DependencyKey,
|
|
4
|
+
type IContainer,
|
|
5
|
+
type IContainerModule,
|
|
6
|
+
type ResolveManyOptions,
|
|
7
|
+
type ResolveOneOptions,
|
|
8
|
+
type Tag,
|
|
9
|
+
} from './IContainer';
|
|
10
|
+
import { MethodNotImplementedError } from '../errors/MethodNotImplementedError';
|
|
11
|
+
import { DependencyNotFoundError } from '../errors/DependencyNotFoundError';
|
|
12
|
+
import { type IProvider } from '../provider/IProvider';
|
|
13
|
+
import { type IRegistration } from '../registration/IRegistration';
|
|
14
|
+
import { type constructor, type Instance } from '../utils/basic';
|
|
15
|
+
import { type IInjector } from '../injector/IInjector';
|
|
16
|
+
import { type ITypedEvent } from '../utils/TypedEvent';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Terminates the parent chain of every root {@link Container}: `resolve` throws
|
|
20
|
+
* `DependencyNotFoundError`, alias lookups return nothing, and every other
|
|
21
|
+
* method throws `MethodNotImplementedError`. You rarely use it directly.
|
|
22
|
+
*/
|
|
23
|
+
export class EmptyContainer implements IContainer {
|
|
24
|
+
/**
|
|
25
|
+
* @throws {MethodNotImplementedError} always — the empty container has no disposal state.
|
|
26
|
+
*/
|
|
27
|
+
get isDisposed(): boolean {
|
|
28
|
+
throw new MethodNotImplementedError('EmptyContainer.isDisposed is not implemented');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
addInstance(instance: Instance) {}
|
|
32
|
+
|
|
33
|
+
getParent() {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getScopes() {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getInstances() {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
hasInstance(instance: Instance): boolean {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @throws {MethodNotImplementedError} always — the empty container cannot create scopes.
|
|
51
|
+
*/
|
|
52
|
+
createScope(): IContainer {
|
|
53
|
+
throw new MethodNotImplementedError('EmptyContainer.createScope is not implemented');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @throws {MethodNotImplementedError} always — the empty container constructs nothing, so it has no injector.
|
|
58
|
+
*/
|
|
59
|
+
getInjector(): IInjector {
|
|
60
|
+
throw new MethodNotImplementedError('EmptyContainer.getInjector is not implemented');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @throws {MethodNotImplementedError} always — the empty container holds no providers to resolve.
|
|
65
|
+
*/
|
|
66
|
+
autoResolve(options?: AutoResolveOptions): this {
|
|
67
|
+
throw new MethodNotImplementedError('EmptyContainer.autoResolve is not implemented');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @throws {MethodNotImplementedError} always — the empty container cannot be disposed.
|
|
72
|
+
*/
|
|
73
|
+
dispose(): void {
|
|
74
|
+
throw new MethodNotImplementedError('EmptyContainer.dispose is not implemented');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @throws {MethodNotImplementedError} always — the empty container cannot hold registrations.
|
|
79
|
+
*/
|
|
80
|
+
register(key: DependencyKey, value: IProvider): this {
|
|
81
|
+
throw new MethodNotImplementedError('EmptyContainer.register is not implemented');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @throws {MethodNotImplementedError} always — the empty container has no tags.
|
|
86
|
+
*/
|
|
87
|
+
hasTag(tag: Tag): boolean {
|
|
88
|
+
throw new MethodNotImplementedError('EmptyContainer.hasTag is not implemented');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @throws {MethodNotImplementedError} always — the empty container has no tags.
|
|
93
|
+
*/
|
|
94
|
+
addTags(...tags: Tag[]): void {
|
|
95
|
+
throw new MethodNotImplementedError('EmptyContainer.addTags is not implemented');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getRegistrations() {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
hasRegistration(key: DependencyKey): boolean {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
removeScope(): void {}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @throws {MethodNotImplementedError} always — the empty container cannot use modules.
|
|
110
|
+
*/
|
|
111
|
+
useModule(module: IContainerModule): this {
|
|
112
|
+
throw new MethodNotImplementedError('EmptyContainer.useModule is not implemented');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @throws {MethodNotImplementedError} always — the empty container cannot hold registrations.
|
|
117
|
+
*/
|
|
118
|
+
addRegistration(registration: IRegistration): this {
|
|
119
|
+
throw new MethodNotImplementedError('EmptyContainer.addRegistration is not implemented');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @throws {DependencyNotFoundError} always — reaching the empty container means `key` was not found in any scope.
|
|
124
|
+
*/
|
|
125
|
+
resolve<T>(key: constructor<T> | DependencyKey, options?: ResolveOneOptions): T {
|
|
126
|
+
throw new DependencyNotFoundError(
|
|
127
|
+
`Cannot find ${key.toString()}: no provider for this key in the resolving scope or any parent. ` +
|
|
128
|
+
'Check that it is registered, that its scope(...) rule matches this scope, that its scopeAccess(...) rule allows it, ' +
|
|
129
|
+
'and that it was registered before createScope() (existing child scopes are not updated).',
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
resolveByAlias<T>(alias: DependencyKey, options?: ResolveManyOptions): T[] {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @throws {DependencyNotFoundError} always — reaching the empty container means `alias` was not found in any scope.
|
|
139
|
+
*/
|
|
140
|
+
resolveOneByAlias<T>(alias: DependencyKey, options?: ResolveOneOptions): T {
|
|
141
|
+
throw new DependencyNotFoundError(
|
|
142
|
+
`Cannot find alias ${alias.toString()}: no accessible registration carries this alias in the resolving scope or any parent.`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @throws {MethodNotImplementedError} always — the empty container cannot hold hooks.
|
|
148
|
+
*/
|
|
149
|
+
get scopeCreated(): ITypedEvent<[IContainer]> {
|
|
150
|
+
throw new MethodNotImplementedError('EmptyContainer.scopeCreated is not implemented');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @throws {MethodNotImplementedError} always — the empty container cannot hold hooks.
|
|
155
|
+
*/
|
|
156
|
+
get scopeDisposed(): ITypedEvent<[IContainer]> {
|
|
157
|
+
throw new MethodNotImplementedError('EmptyContainer.scopeDisposed is not implemented');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @throws {MethodNotImplementedError} always — the empty container cannot hold hooks.
|
|
162
|
+
*/
|
|
163
|
+
get registered(): ITypedEvent<[IProvider, DependencyKey, IContainer]> {
|
|
164
|
+
throw new MethodNotImplementedError('EmptyContainer.registered is not implemented');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { type IProvider, ResolveOptions } from '../provider/IProvider';
|
|
2
|
+
import { type IRegistration } from '../registration/IRegistration';
|
|
3
|
+
import { IInjector, type WithArgs } from '../injector/IInjector';
|
|
4
|
+
import { type constructor, Instance } from '../utils/basic';
|
|
5
|
+
import { type ITypedEvent } from '../utils/TypedEvent';
|
|
6
|
+
|
|
7
|
+
/** A registration key: a string or a symbol. */
|
|
8
|
+
export type DependencyKey = string | symbol;
|
|
9
|
+
/** Narrows `target` to a {@link DependencyKey}. */
|
|
10
|
+
export function isDependencyKey(target: unknown): target is DependencyKey {
|
|
11
|
+
return typeof target === 'symbol' || typeof target === 'string';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** A scope label (`'application'`, `'request'`, ...) matched by `scope(...)` rules. */
|
|
15
|
+
export type Tag = string;
|
|
16
|
+
type WithTags = { tags: Tag[] };
|
|
17
|
+
type WithChild = { child: Tagged };
|
|
18
|
+
type WithExcludedKeys = { excludedKeys: DependencyKey[] };
|
|
19
|
+
/** Something carrying tags: a scope or a token. */
|
|
20
|
+
export interface Tagged {
|
|
21
|
+
hasTag(tag: Tag): boolean;
|
|
22
|
+
addTags(...tags: Tag[]): void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Options of `resolve`: runtime `args`, `lazy`, and the invoking `child` scope. */
|
|
26
|
+
export type ResolveOneOptions = ResolveOptions & Partial<WithChild>;
|
|
27
|
+
/** Options of `resolveByAlias`: like {@link ResolveOneOptions}, plus keys to exclude. */
|
|
28
|
+
export type ResolveManyOptions = ResolveOneOptions & Partial<WithExcludedKeys>;
|
|
29
|
+
/** Something that resolves a key or a class. */
|
|
30
|
+
export interface Resolvable {
|
|
31
|
+
resolve<T>(key: constructor<T> | DependencyKey, options?: ResolveOneOptions): T;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A reusable bundle of registrations or event subscriptions applied with
|
|
36
|
+
* `container.useModule(module)`. A plain object with `applyTo` is enough.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const loggingModule: IContainerModule = {
|
|
40
|
+
* applyTo: (container) => container.addRegistration(Registration.fromClass(Logger)),
|
|
41
|
+
* };
|
|
42
|
+
*/
|
|
43
|
+
export interface IContainerModule {
|
|
44
|
+
applyTo(container: IContainer): void;
|
|
45
|
+
}
|
|
46
|
+
/** Options of `createScope`: the child scope's `tags`. */
|
|
47
|
+
export type CreateScopeOptions = Partial<WithTags>;
|
|
48
|
+
export type AutoResolveOptions = Partial<WithArgs>;
|
|
49
|
+
export type RegisterOptions = { aliases?: DependencyKey[] };
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Scope event hooks - the container's own domain: a scope was created, a scope
|
|
53
|
+
* was disposed. The listener type of `IContainer.scopeCreated` / `scopeDisposed`.
|
|
54
|
+
*
|
|
55
|
+
* The other two hook domains live with the abstraction which raises them:
|
|
56
|
+
* injector hooks on `IInjector` (`InjectorHook`), provider hooks on
|
|
57
|
+
* {@link IProvider} (`ProviderHook`). An injector is configured before it is
|
|
58
|
+
* passed to a container's constructor, so a container never hands its injector
|
|
59
|
+
* out.
|
|
60
|
+
*/
|
|
61
|
+
export type ScopeHook = (scope: IContainer) => void;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Scope event hook for a registration landing in a scope: a provider entered
|
|
65
|
+
* the provider map under `key`. Registration is always of a provider, so the
|
|
66
|
+
* name says only what varies. The listener type of `IContainer.registered`.
|
|
67
|
+
*/
|
|
68
|
+
export type RegisteredHook = (provider: IProvider, key: DependencyKey, scope: IContainer) => void;
|
|
69
|
+
|
|
70
|
+
/** A dependency injection scope. See {@link Container}. */
|
|
71
|
+
export interface IContainer extends Tagged {
|
|
72
|
+
readonly isDisposed: boolean;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Raised once a scope this container created is fully registered and attached.
|
|
76
|
+
* The subscriber's side only - a container raises its own scope events.
|
|
77
|
+
*/
|
|
78
|
+
readonly scopeCreated: ITypedEvent<[IContainer]>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Raised by this container as it disposes, before its providers and instances go.
|
|
82
|
+
*/
|
|
83
|
+
readonly scopeDisposed: ITypedEvent<[IContainer]>;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Raised once a provider is registered here under a resolvable key.
|
|
87
|
+
*/
|
|
88
|
+
readonly registered: ITypedEvent<[IProvider, DependencyKey, IContainer]>;
|
|
89
|
+
|
|
90
|
+
/** Low-level: puts `value` under `key` in this scope only. Prefer `addRegistration`. */
|
|
91
|
+
register(key: DependencyKey, value: IProvider, options?: RegisterOptions): this;
|
|
92
|
+
|
|
93
|
+
/** Registers here if the registration's scope rules match, and remembers it for future child scopes. */
|
|
94
|
+
addRegistration(registration: IRegistration): this;
|
|
95
|
+
|
|
96
|
+
/** Registrations added to this scope and its parents - what `createScope` copies. */
|
|
97
|
+
getRegistrations(): IRegistration[];
|
|
98
|
+
|
|
99
|
+
/** Whether a provider is registered under `key` in this scope (parents are not checked). */
|
|
100
|
+
hasRegistration(key: DependencyKey): boolean;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Resolves a key, or constructs a class with the injector. Takes a key or a
|
|
104
|
+
* class, not a token: for a token call `token.resolve(scope)`.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* scope.resolve<ILogger>('ILogger');
|
|
108
|
+
* scope.resolve(App, { args: ['tenant-1'] });
|
|
109
|
+
*/
|
|
110
|
+
resolve<T>(target: constructor<T> | DependencyKey, options?: ResolveOneOptions): T;
|
|
111
|
+
|
|
112
|
+
/** Resolves every accessible dependency registered under `alias`, in this scope and its parents. */
|
|
113
|
+
resolveByAlias<T>(alias: DependencyKey, options?: ResolveManyOptions): T[];
|
|
114
|
+
|
|
115
|
+
/** Resolves one dependency registered under `alias`, looking here first and then in parents. */
|
|
116
|
+
resolveOneByAlias<T>(alias: DependencyKey, options?: ResolveOneOptions): T;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Creates a child scope and copies into it the registrations whose scope
|
|
120
|
+
* rules match its tags. Registrations added later are not copied into it.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* const request = app.createScope({ tags: ['request'] });
|
|
124
|
+
*/
|
|
125
|
+
createScope(options?: CreateScopeOptions): IContainer;
|
|
126
|
+
|
|
127
|
+
/** Resolves every provider marked with `autoResolve()`. */
|
|
128
|
+
autoResolve(options?: AutoResolveOptions): this;
|
|
129
|
+
|
|
130
|
+
/** Direct child scopes that are still alive. */
|
|
131
|
+
getScopes(): IContainer[];
|
|
132
|
+
|
|
133
|
+
/** Detaches a child scope; called by the child on dispose. */
|
|
134
|
+
removeScope(child: IContainer): void;
|
|
135
|
+
|
|
136
|
+
/** Applies a module to this scope. */
|
|
137
|
+
useModule(module: IContainerModule): this;
|
|
138
|
+
|
|
139
|
+
/** The parent scope, if any. */
|
|
140
|
+
getParent(): IContainer | undefined;
|
|
141
|
+
|
|
142
|
+
/** Instances constructed in this scope, and in child scopes when `cascade` is true. */
|
|
143
|
+
getInstances(cascade?: boolean): Instance[];
|
|
144
|
+
|
|
145
|
+
/** Whether this scope tracks `instance`. */
|
|
146
|
+
hasInstance(instance: Instance): boolean;
|
|
147
|
+
|
|
148
|
+
/** Raises `scopeDisposed`, drops providers and instances, and detaches from the parent. Child scopes are not disposed. */
|
|
149
|
+
dispose(): void;
|
|
150
|
+
|
|
151
|
+
/** Tracks an instance in this scope; called by the injector. */
|
|
152
|
+
addInstance(instance: Instance): void;
|
|
153
|
+
|
|
154
|
+
/** The injector shared by this scope tree, e.g. to subscribe to `onConstructed`. */
|
|
155
|
+
getInjector(): IInjector;
|
|
156
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ContainerError } from './ContainerError';
|
|
2
|
+
|
|
3
|
+
/** Thrown by `findOrFail` when no runtime arg matches the predicate. Code `IOC_ARGUMENT_NOT_FOUND`. */
|
|
4
|
+
export class ArgumentNotFoundError extends ContainerError {
|
|
5
|
+
name = 'ArgumentNotFoundError';
|
|
6
|
+
readonly code = 'IOC_ARGUMENT_NOT_FOUND';
|
|
7
|
+
|
|
8
|
+
constructor(message = 'Argument not found') {
|
|
9
|
+
super(message);
|
|
10
|
+
|
|
11
|
+
Object.setPrototypeOf(this, ArgumentNotFoundError.prototype);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CannotApplySingletonTwiceError } from './CannotApplySingletonTwiceError';
|
|
2
|
+
|
|
3
|
+
/** @deprecated Misspelled name kept for compatibility. Use {@link CannotApplySingletonTwiceError}. */
|
|
4
|
+
export const CannonSingletonApplyTwiceError = CannotApplySingletonTwiceError;
|
|
5
|
+
/** @deprecated Misspelled name kept for compatibility. Use {@link CannotApplySingletonTwiceError}. */
|
|
6
|
+
export type CannonSingletonApplyTwiceError = CannotApplySingletonTwiceError;
|