ts-ioc-container 72.1.0 → 72.3.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 +169 -0
- package/cjm/container/Container.js +5 -2
- package/cjm/container/EmptyContainer.js +20 -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/feature/IFeatureFlags.js +6 -0
- package/cjm/index.js +14 -3
- package/cjm/provider/Provider.js +6 -6
- package/cjm/registration/Registration.js +2 -2
- package/cjm/token/BindableToken.js +63 -0
- package/cjm/token/ConstantToken.js +4 -4
- package/cjm/token/FunctionToken.js +1 -1
- package/cjm/token/GroupAliasToken.js +5 -50
- package/cjm/token/GroupInstanceToken.js +4 -4
- package/cjm/token/MultiVariantFeatureToken.js +84 -0
- package/cjm/token/SingleAliasToken.js +5 -50
- package/cjm/token/SingleToken.js +5 -50
- package/cjm/token/ToggleFeatureToken.js +10 -0
- 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 +5 -2
- package/esm/container/EmptyContainer.js +20 -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/feature/IFeatureFlags.js +3 -0
- package/esm/index.js +5 -0
- package/esm/provider/Provider.js +6 -6
- package/esm/registration/Registration.js +2 -2
- package/esm/token/BindableToken.js +59 -0
- package/esm/token/ConstantToken.js +4 -4
- package/esm/token/FunctionToken.js +1 -1
- package/esm/token/GroupAliasToken.js +5 -50
- package/esm/token/GroupInstanceToken.js +4 -4
- package/esm/token/MultiVariantFeatureToken.js +80 -0
- package/esm/token/SingleAliasToken.js +5 -50
- package/esm/token/SingleToken.js +5 -50
- package/esm/token/ToggleFeatureToken.js +6 -0
- 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 +315 -0
- package/lib/container/EmptyContainer.ts +170 -0
- package/lib/container/IContainer.ts +159 -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/feature/IFeatureFlags.ts +44 -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 +197 -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 +242 -0
- package/lib/registration/Registration.ts +122 -0
- package/lib/select.ts +33 -0
- package/lib/token/BindToken.ts +13 -0
- package/lib/token/BindableToken.ts +137 -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 +33 -0
- package/lib/token/GroupInstanceToken.ts +70 -0
- package/lib/token/InjectionToken.ts +46 -0
- package/lib/token/MultiVariantFeatureToken.ts +160 -0
- package/lib/token/SingleAliasToken.ts +28 -0
- package/lib/token/SingleToken.ts +35 -0
- package/lib/token/ToggleFeatureToken.ts +27 -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 +6 -4
- 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 +61 -0
- package/typings/container/Container.d.ts.map +1 -0
- package/typings/container/EmptyContainer.d.ts +52 -0
- package/typings/container/EmptyContainer.d.ts.map +1 -0
- package/typings/container/IContainer.d.ts +77 -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/feature/IFeatureFlags.d.ts +42 -0
- package/typings/feature/IFeatureFlags.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 +6 -0
- 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 +27 -0
- package/typings/metadata/class.d.ts.map +1 -0
- package/typings/metadata/method.d.ts +24 -0
- package/typings/metadata/method.d.ts.map +1 -0
- package/typings/metadata/parameter.d.ts +21 -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 +144 -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/BindableToken.d.ts +80 -0
- package/typings/token/BindableToken.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 +21 -22
- 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/MultiVariantFeatureToken.d.ts +79 -0
- package/typings/token/MultiVariantFeatureToken.d.ts.map +1 -0
- package/typings/token/SingleAliasToken.d.ts +16 -22
- package/typings/token/SingleAliasToken.d.ts.map +1 -0
- package/typings/token/SingleToken.d.ts +25 -21
- package/typings/token/SingleToken.d.ts.map +1 -0
- package/typings/token/ToggleFeatureToken.d.ts +25 -0
- package/typings/token/ToggleFeatureToken.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
package/lib/select.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type CreateScopeOptions } from './container/IContainer';
|
|
2
|
+
import { toGroupAlias } from './token/GroupAliasToken';
|
|
3
|
+
import { FunctionToken } from './token/FunctionToken';
|
|
4
|
+
import { GroupInstanceToken, InstancePredicate } from './token/GroupInstanceToken';
|
|
5
|
+
import { toToken } from './token/toToken';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shortcuts for common tokens.
|
|
9
|
+
*
|
|
10
|
+
* - `select.token(x)` - `toToken(x)`
|
|
11
|
+
* - `select.alias('Key')` - a {@link GroupAliasToken}
|
|
12
|
+
* - `select.instances(predicate)` - instances already created in the scope
|
|
13
|
+
* - `select.scope.current` - the resolving scope itself
|
|
14
|
+
* - `select.scope.create({ tags })` - a new child scope
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* class Handler {
|
|
18
|
+
* constructor(@inject(by(select.scope.current)) private scope: IContainer) {}
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
export const select = {
|
|
22
|
+
alias: toGroupAlias,
|
|
23
|
+
|
|
24
|
+
token: toToken,
|
|
25
|
+
|
|
26
|
+
instances: (predicate: InstancePredicate = () => true) => new GroupInstanceToken(predicate),
|
|
27
|
+
|
|
28
|
+
scope: {
|
|
29
|
+
current: new FunctionToken(({ scope }) => scope),
|
|
30
|
+
|
|
31
|
+
create: (options: CreateScopeOptions) => new FunctionToken(({ scope }) => scope.createScope(options)),
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IRegistration } from '../registration/IRegistration';
|
|
2
|
+
|
|
3
|
+
import { Is } from '../utils/basic';
|
|
4
|
+
|
|
5
|
+
/** A token that can bind a registration to itself (a key or an alias). */
|
|
6
|
+
export interface BindToken<T = any> {
|
|
7
|
+
bindTo(r: IRegistration<T>): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Narrows `token` to a {@link BindToken}. */
|
|
11
|
+
export function isBindToken(token: unknown): token is BindToken {
|
|
12
|
+
return !Is.nullish(token) && typeof token === 'object' && 'bindTo' in token;
|
|
13
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { type DependencyKey, type IContainer } from '../container/IContainer';
|
|
2
|
+
import { forwardArgs, InjectionToken } from './InjectionToken';
|
|
3
|
+
import { type BindToken } from './BindToken';
|
|
4
|
+
import { type IRegistration } from '../registration/IRegistration';
|
|
5
|
+
import { type ArgsFn, type ResolveOptions } from '../provider/IProvider';
|
|
6
|
+
import { type Serializable } from '../utils/basic';
|
|
7
|
+
import { type MultiVariantFeatureToken } from './MultiVariantFeatureToken';
|
|
8
|
+
|
|
9
|
+
// Blocks inference from `name` so it must be one of the feature's variants (TS < 5.4 has no NoInfer).
|
|
10
|
+
type NoInferVariant<V> = [V][V extends unknown ? 0 : never];
|
|
11
|
+
|
|
12
|
+
/** Internal state of a {@link BindableToken}, propagated by its modifiers. Never pass it from outside. */
|
|
13
|
+
export type BindableTokenOptions = {
|
|
14
|
+
getArgsFn?: ArgsFn;
|
|
15
|
+
isLazy?: boolean;
|
|
16
|
+
tags?: string[];
|
|
17
|
+
memberships?: DependencyKey[];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Base of every token a registration can be bound to (`SingleToken`,
|
|
22
|
+
* `SingleAliasToken`, `GroupAliasToken`): a key, the args / lazy / tags state
|
|
23
|
+
* every modifier copies, and the feature memberships the token declares.
|
|
24
|
+
*
|
|
25
|
+
* `T` is the type bound to the token, `R` the type resolving it returns (an
|
|
26
|
+
* alias group resolves `T[]`). A subclass says only how it binds (`bindKey`)
|
|
27
|
+
* and how it resolves.
|
|
28
|
+
*/
|
|
29
|
+
export abstract class BindableToken<T = any, R = T> extends InjectionToken<R> implements BindToken<T>, Serializable {
|
|
30
|
+
private readonly getArgsFn: ArgsFn;
|
|
31
|
+
private readonly isLazyByDefault: boolean;
|
|
32
|
+
private readonly memberships: DependencyKey[];
|
|
33
|
+
|
|
34
|
+
constructor(
|
|
35
|
+
readonly token: DependencyKey,
|
|
36
|
+
{ getArgsFn = forwardArgs, isLazy = false, tags = [], memberships = [] }: BindableTokenOptions = {},
|
|
37
|
+
) {
|
|
38
|
+
super(tags);
|
|
39
|
+
this.getArgsFn = getArgsFn;
|
|
40
|
+
this.isLazyByDefault = isLazy;
|
|
41
|
+
this.memberships = memberships;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Binds `r` under this token, and into every feature this token declared itself a variant or fallback of. */
|
|
45
|
+
bindTo(r: IRegistration<T>): void {
|
|
46
|
+
this.bindKey(r);
|
|
47
|
+
for (const alias of this.memberships) {
|
|
48
|
+
r.bindToAlias(alias);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Declares the dependency bound to this token the implementation of `feature`
|
|
54
|
+
* while the flag serves the variant `name`.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const BlueButtonToken = new SingleToken<IButton>('BlueButton').variantOf(CheckoutButtonToken, 'blue');
|
|
58
|
+
*
|
|
59
|
+
* @register(BlueButtonToken)
|
|
60
|
+
* class BlueButton implements IButton {}
|
|
61
|
+
*/
|
|
62
|
+
variantOf<V extends string>(feature: MultiVariantFeatureToken<T, V>, name: NoInferVariant<V>): this {
|
|
63
|
+
return this.copy({ memberships: [...this.memberships, feature.variantAlias(name)] });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Declares the dependency bound to this token the primary variant of
|
|
68
|
+
* `feature`: used while the flag is on and no named variant implementation
|
|
69
|
+
* matches. The enabled implementation of a `ToggleFeatureToken`.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* const StripeGatewayToken = new SingleToken<IPaymentGateway>('StripeGateway').primaryVariantOf(PaymentGatewayToken);
|
|
73
|
+
*
|
|
74
|
+
* @register(StripeGatewayToken)
|
|
75
|
+
* class StripeGateway implements IPaymentGateway {}
|
|
76
|
+
*/
|
|
77
|
+
primaryVariantOf(feature: MultiVariantFeatureToken<T, string>): this {
|
|
78
|
+
return this.copy({ memberships: [...this.memberships, feature.primaryAlias()] });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Declares the dependency bound to this token the fallback of `feature`: used
|
|
83
|
+
* whenever no variant implementation applies - the flag is off, serves
|
|
84
|
+
* nothing implemented, or its client throws.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* const LegacyGatewayToken = new SingleToken<IPaymentGateway>('LegacyGateway').fallbackOf(PaymentGatewayToken);
|
|
88
|
+
*
|
|
89
|
+
* @register(LegacyGatewayToken)
|
|
90
|
+
* class LegacyGateway implements IPaymentGateway {}
|
|
91
|
+
*/
|
|
92
|
+
fallbackOf(feature: MultiVariantFeatureToken<T, string>): this {
|
|
93
|
+
return this.copy({ memberships: [...this.memberships, feature.fallbackAlias()] });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
args(...newArgs: unknown[]): this {
|
|
97
|
+
const parentFn = this.getArgsFn;
|
|
98
|
+
return this.copy({ getArgsFn: (options) => [...parentFn(options), ...newArgs] });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
argsFn(fn: (s: IContainer) => unknown[]): this {
|
|
102
|
+
const parentFn = this.getArgsFn;
|
|
103
|
+
return this.copy({ getArgsFn: (options) => [...parentFn(options), ...fn(options.scope)] });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
lazy(): this {
|
|
107
|
+
return this.copy({ isLazy: true });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
addTags(...tags: string[]): this {
|
|
111
|
+
return this.copy({ tags: [...this.getTags(), ...tags] });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
toString(): string {
|
|
115
|
+
return this.token.toString();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Binds `r` under this token's own key or alias. */
|
|
119
|
+
protected abstract bindKey(r: IRegistration<T>): void;
|
|
120
|
+
|
|
121
|
+
/** The options a subclass resolves with: runtime args first, then this token's args; lazy if either asks. */
|
|
122
|
+
protected toResolveOptions(s: IContainer, { args = [], lazy }: ResolveOptions = {}): ResolveOptions {
|
|
123
|
+
// `lazy` stays undefined unless someone asked, so a provider's own `lazy()` pipe still applies.
|
|
124
|
+
return { args: this.getArgsFn({ scope: s, args }), lazy: this.isLazyByDefault || lazy };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private copy(overrides: BindableTokenOptions): this {
|
|
128
|
+
const Self = this.constructor as new (token: DependencyKey, options: BindableTokenOptions) => this;
|
|
129
|
+
return new Self(this.token, {
|
|
130
|
+
getArgsFn: this.getArgsFn,
|
|
131
|
+
isLazy: this.isLazyByDefault,
|
|
132
|
+
tags: this.getTags(),
|
|
133
|
+
memberships: this.memberships,
|
|
134
|
+
...overrides,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { IContainer } from '../container/IContainer';
|
|
2
|
+
import { forwardArgs, InjectionToken } from './InjectionToken';
|
|
3
|
+
import { type constructor, Serializable } from '../utils/basic';
|
|
4
|
+
import { ArgsFn, ResolveOptions } from '../provider/IProvider';
|
|
5
|
+
|
|
6
|
+
/** A token for a class: resolves it through the scope's injector. `toToken(Class)` creates one. */
|
|
7
|
+
export class ClassToken<T = any> extends InjectionToken<T> implements Serializable {
|
|
8
|
+
private readonly _getArgsFn: ArgsFn;
|
|
9
|
+
private readonly _isLazy: boolean;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly target: constructor<T>,
|
|
13
|
+
{
|
|
14
|
+
getArgsFn = forwardArgs,
|
|
15
|
+
isLazy = false,
|
|
16
|
+
tags = [],
|
|
17
|
+
}: { getArgsFn?: ArgsFn; isLazy?: boolean; tags?: string[] } = {},
|
|
18
|
+
) {
|
|
19
|
+
super(tags);
|
|
20
|
+
this._getArgsFn = getArgsFn;
|
|
21
|
+
this._isLazy = isLazy;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
select<R>(fn: (target: T) => R) {
|
|
25
|
+
return (s: IContainer) => fn(this.resolve(s));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
resolve(s: IContainer, { args = [], lazy }: ResolveOptions = {}): T {
|
|
29
|
+
return s.resolve(this.target, {
|
|
30
|
+
args: this._getArgsFn({ scope: s, args }),
|
|
31
|
+
lazy: this._isLazy || lazy,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
args(...newArgs: unknown[]) {
|
|
36
|
+
const parentFn = this._getArgsFn;
|
|
37
|
+
return new ClassToken<T>(this.target, {
|
|
38
|
+
getArgsFn: (options) => [...parentFn(options), ...newArgs],
|
|
39
|
+
isLazy: this._isLazy,
|
|
40
|
+
tags: this.getTags(),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
argsFn(fn: (s: IContainer) => unknown[]) {
|
|
45
|
+
const parentFn = this._getArgsFn;
|
|
46
|
+
return new ClassToken<T>(this.target, {
|
|
47
|
+
getArgsFn: (options) => [...parentFn(options), ...fn(options.scope)],
|
|
48
|
+
isLazy: this._isLazy,
|
|
49
|
+
tags: this.getTags(),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
lazy() {
|
|
54
|
+
return new ClassToken<T>(this.target, {
|
|
55
|
+
getArgsFn: this._getArgsFn,
|
|
56
|
+
isLazy: true,
|
|
57
|
+
tags: this.getTags(),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
addTags(...tags: string[]): ClassToken<T> {
|
|
62
|
+
return new ClassToken<T>(this.target, {
|
|
63
|
+
getArgsFn: this._getArgsFn,
|
|
64
|
+
isLazy: this._isLazy,
|
|
65
|
+
tags: [...this.getTags(), ...tags],
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
toString(): string {
|
|
70
|
+
return this.target.name;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { IContainer } from '../container/IContainer';
|
|
2
|
+
import { InjectionToken } from './InjectionToken';
|
|
3
|
+
import { MethodNotImplementedError } from '../errors/MethodNotImplementedError';
|
|
4
|
+
import { Serializable } from '../utils/basic';
|
|
5
|
+
|
|
6
|
+
/** A token that always resolves to a fixed value. It takes no args and cannot be lazy. */
|
|
7
|
+
export class ConstantToken<T = any> extends InjectionToken<T> implements Serializable {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly token: T,
|
|
10
|
+
{ tags = [] }: { tags?: string[] } = {},
|
|
11
|
+
) {
|
|
12
|
+
super(tags);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resolve(s: IContainer): T {
|
|
16
|
+
return this.token;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @throws {MethodNotImplementedError} always — a constant token cannot receive static args.
|
|
21
|
+
*/
|
|
22
|
+
args(...deps: unknown[]): InjectionToken<T> {
|
|
23
|
+
throw new MethodNotImplementedError('ConstantToken.args is not implemented');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @throws {MethodNotImplementedError} always — a constant token cannot receive resolved args.
|
|
28
|
+
*/
|
|
29
|
+
argsFn(getArgsFn: (s: IContainer) => unknown[]): InjectionToken<T> {
|
|
30
|
+
throw new MethodNotImplementedError('ConstantToken.argsFn is not implemented');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @throws {MethodNotImplementedError} always — a constant token cannot be made lazy.
|
|
35
|
+
*/
|
|
36
|
+
lazy(): InjectionToken<T> {
|
|
37
|
+
throw new MethodNotImplementedError('ConstantToken.lazy is not implemented');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
addTags(...tags: string[]): ConstantToken<T> {
|
|
41
|
+
return new ConstantToken<T>(this.token, { tags: [...this.getTags(), ...tags] });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @throws {MethodNotImplementedError} always — a constant token has no underlying key.
|
|
46
|
+
*/
|
|
47
|
+
toString(): string {
|
|
48
|
+
throw new MethodNotImplementedError('ConstantToken.toString is not implemented');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { IContainer } from '../container/IContainer';
|
|
2
|
+
import { forwardArgs, InjectionToken } from './InjectionToken';
|
|
3
|
+
import { InjectFn } from '../hooks/hook';
|
|
4
|
+
import { ArgsFn, ResolveOptions } from '../provider/IProvider';
|
|
5
|
+
import { MethodNotImplementedError } from '../errors/MethodNotImplementedError';
|
|
6
|
+
import { Serializable } from '../utils/basic';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A token that resolves by calling an `InjectFn` with the resolution context.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const CurrentScope = new FunctionToken(({ scope }) => scope);
|
|
13
|
+
*/
|
|
14
|
+
export class FunctionToken<T = any> extends InjectionToken<T> implements Serializable {
|
|
15
|
+
private readonly _getArgsFn: ArgsFn;
|
|
16
|
+
private readonly _isLazy: boolean;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly fn: InjectFn<T>,
|
|
20
|
+
{
|
|
21
|
+
getArgsFn = forwardArgs,
|
|
22
|
+
isLazy = false,
|
|
23
|
+
tags = [],
|
|
24
|
+
}: { getArgsFn?: ArgsFn; isLazy?: boolean; tags?: string[] } = {},
|
|
25
|
+
) {
|
|
26
|
+
super(tags);
|
|
27
|
+
this._getArgsFn = getArgsFn;
|
|
28
|
+
this._isLazy = isLazy;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
resolve(s: IContainer, { args = [], lazy }: ResolveOptions = {}): T {
|
|
32
|
+
return this.fn({
|
|
33
|
+
scope: s,
|
|
34
|
+
args: this._getArgsFn({ scope: s, args }),
|
|
35
|
+
lazy: this._isLazy || lazy,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
args(...newArgs: unknown[]): InjectionToken<T> {
|
|
40
|
+
const parentFn = this._getArgsFn;
|
|
41
|
+
return new FunctionToken<T>(this.fn, {
|
|
42
|
+
getArgsFn: (options) => [...parentFn(options), ...newArgs],
|
|
43
|
+
isLazy: this._isLazy,
|
|
44
|
+
tags: this.getTags(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
argsFn(fn: (s: IContainer) => unknown[]): InjectionToken<T> {
|
|
49
|
+
const parentFn = this._getArgsFn;
|
|
50
|
+
return new FunctionToken<T>(this.fn, {
|
|
51
|
+
getArgsFn: (options) => [...parentFn(options), ...fn(options.scope)],
|
|
52
|
+
isLazy: this._isLazy,
|
|
53
|
+
tags: this.getTags(),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
lazy(): InjectionToken<T> {
|
|
58
|
+
return new FunctionToken<T>(this.fn, {
|
|
59
|
+
getArgsFn: this._getArgsFn,
|
|
60
|
+
isLazy: true,
|
|
61
|
+
tags: this.getTags(),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
addTags(...tags: string[]): FunctionToken<T> {
|
|
66
|
+
return new FunctionToken<T>(this.fn, {
|
|
67
|
+
getArgsFn: this._getArgsFn,
|
|
68
|
+
isLazy: this._isLazy,
|
|
69
|
+
tags: [...this.getTags(), ...tags],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @throws {MethodNotImplementedError} always — a function token has no underlying key.
|
|
75
|
+
*/
|
|
76
|
+
toString(): string {
|
|
77
|
+
throw new MethodNotImplementedError('FunctionToken.toString is not implemented');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type DependencyKey, type IContainer } from '../container/IContainer';
|
|
2
|
+
import { BindableToken } from './BindableToken';
|
|
3
|
+
import { type IRegistration } from '../registration/IRegistration';
|
|
4
|
+
import { type ResolveOptions } from '../provider/IProvider';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A token for every dependency registered under an alias. As a `@register(...)`
|
|
8
|
+
* target it adds the alias; resolving returns all matches as an array.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const IMiddlewareToken = toGroupAlias<IMiddleware>('IMiddleware');
|
|
12
|
+
*
|
|
13
|
+
* @register(IMiddlewareToken)
|
|
14
|
+
* class Auth implements IMiddleware {}
|
|
15
|
+
*
|
|
16
|
+
* IMiddlewareToken.resolve(scope); // IMiddleware[]
|
|
17
|
+
*/
|
|
18
|
+
export class GroupAliasToken<T = any> extends BindableToken<T, T[]> {
|
|
19
|
+
select<R>(fn: (target: T[]) => R[]) {
|
|
20
|
+
return (s: IContainer) => fn(this.resolve(s));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
resolve(s: IContainer, options?: ResolveOptions): T[] {
|
|
24
|
+
return s.resolveByAlias(this.token, this.toResolveOptions(s, options));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected bindKey(r: IRegistration<T>) {
|
|
28
|
+
r.bindToAlias(this.token);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Creates a {@link GroupAliasToken} for `token`. */
|
|
33
|
+
export const toGroupAlias = <T>(token: DependencyKey) => new GroupAliasToken<T>(token);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { InjectionToken } from './InjectionToken';
|
|
2
|
+
import type { IContainer } from '../container/IContainer';
|
|
3
|
+
import { MethodNotImplementedError } from '../errors/MethodNotImplementedError';
|
|
4
|
+
|
|
5
|
+
import { Instance, Serializable } from '../utils/basic';
|
|
6
|
+
|
|
7
|
+
/** Selects instances for a {@link GroupInstanceToken}. */
|
|
8
|
+
export type InstancePredicate = (dep: unknown) => boolean;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the instances already created in a scope (and, by default, its
|
|
12
|
+
* child scopes) that match a predicate. Created by `select.instances(...)`.
|
|
13
|
+
*/
|
|
14
|
+
export class GroupInstanceToken extends InjectionToken<Instance[]> implements Serializable {
|
|
15
|
+
private isCascade: boolean;
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
private predicate: InstancePredicate,
|
|
19
|
+
{ tags = [], isCascade = true }: { tags?: string[]; isCascade?: boolean } = {},
|
|
20
|
+
) {
|
|
21
|
+
super(tags);
|
|
22
|
+
this.isCascade = isCascade;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
select<R>(fn: (target: Instance) => R) {
|
|
26
|
+
return (s: IContainer) => this.resolve(s).map(fn);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @throws {MethodNotImplementedError} always — a group instance token cannot receive static args.
|
|
31
|
+
*/
|
|
32
|
+
args(...deps: unknown[]): this {
|
|
33
|
+
throw new MethodNotImplementedError('GroupInstanceToken.args is not implemented');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @throws {MethodNotImplementedError} always — a group instance token cannot receive resolved args.
|
|
38
|
+
*/
|
|
39
|
+
argsFn(getArgsFn: (s: IContainer) => unknown[]): InjectionToken<Instance[]> {
|
|
40
|
+
throw new MethodNotImplementedError('GroupInstanceToken.argsFn is not implemented');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @throws {MethodNotImplementedError} always — a group instance token cannot be made lazy.
|
|
45
|
+
*/
|
|
46
|
+
lazy(): InjectionToken<Instance[]> {
|
|
47
|
+
throw new MethodNotImplementedError('GroupInstanceToken.lazy is not implemented');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Whether instances of child scopes are included (default `true`). */
|
|
51
|
+
cascade(isTrue: boolean): this {
|
|
52
|
+
this.isCascade = isTrue;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
resolve(c: IContainer): Instance[] {
|
|
57
|
+
return c.getInstances(this.isCascade).filter(this.predicate);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
addTags(...tags: string[]): GroupInstanceToken {
|
|
61
|
+
return new GroupInstanceToken(this.predicate, { tags: [...this.getTags(), ...tags], isCascade: this.isCascade });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @throws {MethodNotImplementedError} always — a group instance token has no underlying key.
|
|
66
|
+
*/
|
|
67
|
+
toString(): string {
|
|
68
|
+
throw new MethodNotImplementedError('GroupInstanceToken.toString is not implemented');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type IContainer, type Tag } from '../container/IContainer';
|
|
2
|
+
import { ArgsFn, ResolveOptions } from '../provider/IProvider';
|
|
3
|
+
import { Is } from '../utils/basic';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The default `getArgsFn` of every token: the runtime `args` a token is
|
|
7
|
+
* resolved with reach the provider as they are, and `token.args(...)` /
|
|
8
|
+
* `token.argsFn(...)` append after them.
|
|
9
|
+
*/
|
|
10
|
+
export const forwardArgs: ArgsFn = ({ args = [] }) => args;
|
|
11
|
+
|
|
12
|
+
/** Base class of every token: something resolvable from a scope that can be specialized with args, lazy and tags. */
|
|
13
|
+
export abstract class InjectionToken<T = any> {
|
|
14
|
+
private readonly tags: Set<Tag>;
|
|
15
|
+
|
|
16
|
+
protected constructor(tags: Tag[] = []) {
|
|
17
|
+
this.tags = new Set(tags);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
abstract resolve(s: IContainer, options?: ResolveOptions): T;
|
|
21
|
+
abstract args(...deps: unknown[]): InjectionToken<T>;
|
|
22
|
+
abstract argsFn(getArgsFn: (s: IContainer) => unknown[]): InjectionToken<T>;
|
|
23
|
+
abstract lazy(): InjectionToken<T>;
|
|
24
|
+
abstract addTags(...tags: Tag[]): InjectionToken<T>;
|
|
25
|
+
|
|
26
|
+
hasTag(tag: Tag): boolean {
|
|
27
|
+
return this.tags.has(tag);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected getTags(): Tag[] {
|
|
31
|
+
return [...this.tags];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Narrows `target` to an {@link InjectionToken}. */
|
|
36
|
+
export function isInjectionToken<T = any>(target: unknown): target is InjectionToken<T> {
|
|
37
|
+
return (
|
|
38
|
+
Is.object(target) &&
|
|
39
|
+
'resolve' in target &&
|
|
40
|
+
'args' in target &&
|
|
41
|
+
'argsFn' in target &&
|
|
42
|
+
'lazy' in target &&
|
|
43
|
+
'hasTag' in target &&
|
|
44
|
+
'addTags' in target
|
|
45
|
+
);
|
|
46
|
+
}
|