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
package/lib/index.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Containers
|
|
2
|
+
export {
|
|
3
|
+
type IContainer,
|
|
4
|
+
type Resolvable,
|
|
5
|
+
type IContainerModule,
|
|
6
|
+
type DependencyKey,
|
|
7
|
+
type Tag,
|
|
8
|
+
type Tagged,
|
|
9
|
+
type ResolveOneOptions,
|
|
10
|
+
type ResolveManyOptions,
|
|
11
|
+
type ScopeHook,
|
|
12
|
+
type RegisteredHook,
|
|
13
|
+
type AutoResolveOptions,
|
|
14
|
+
isDependencyKey,
|
|
15
|
+
} from './container/IContainer';
|
|
16
|
+
export { Container } from './container/Container';
|
|
17
|
+
export { AutoResolveModule } from './container/AutoResolveModule';
|
|
18
|
+
export { EmptyContainer } from './container/EmptyContainer';
|
|
19
|
+
|
|
20
|
+
// Injectors
|
|
21
|
+
export {
|
|
22
|
+
type IInjector,
|
|
23
|
+
type InjectOptions,
|
|
24
|
+
type WithScope,
|
|
25
|
+
type WithArgs,
|
|
26
|
+
type IInjectFnResolver,
|
|
27
|
+
type IInjectorModule,
|
|
28
|
+
type InjectorHook,
|
|
29
|
+
Injector,
|
|
30
|
+
} from './injector/IInjector';
|
|
31
|
+
export { MetadataInjector, inject, arg, args, argsFn, by, resolveArgs } from './injector/MetadataInjector';
|
|
32
|
+
export { SimpleInjector } from './injector/SimpleInjector';
|
|
33
|
+
export { ProxyInjector } from './injector/ProxyInjector';
|
|
34
|
+
|
|
35
|
+
// Providers
|
|
36
|
+
export {
|
|
37
|
+
type ResolveDependency,
|
|
38
|
+
type IProvider,
|
|
39
|
+
type DecorateFn,
|
|
40
|
+
type ArgsFn,
|
|
41
|
+
type ProviderOptions,
|
|
42
|
+
type ResolveOptions,
|
|
43
|
+
type GetCacheKey,
|
|
44
|
+
type ScopeAccessOptions,
|
|
45
|
+
type ScopeAccessRule,
|
|
46
|
+
type ProviderHook,
|
|
47
|
+
} from './provider/IProvider';
|
|
48
|
+
export { Provider } from './provider/Provider';
|
|
49
|
+
|
|
50
|
+
// Registrations
|
|
51
|
+
export {
|
|
52
|
+
type IRegistration,
|
|
53
|
+
type ReturnTypeOfRegistration,
|
|
54
|
+
type ScopeMatchRule,
|
|
55
|
+
type ProviderPipe,
|
|
56
|
+
type Bindable,
|
|
57
|
+
type ProviderMapper,
|
|
58
|
+
type RegistrationMapper,
|
|
59
|
+
isProviderPipe,
|
|
60
|
+
registerPipe,
|
|
61
|
+
toBindToken,
|
|
62
|
+
toProviderFn,
|
|
63
|
+
toRegistrationFn,
|
|
64
|
+
register,
|
|
65
|
+
bindTo,
|
|
66
|
+
scope,
|
|
67
|
+
scopeAccess,
|
|
68
|
+
lazy,
|
|
69
|
+
autoResolve,
|
|
70
|
+
singleton,
|
|
71
|
+
decorate,
|
|
72
|
+
appendArgs,
|
|
73
|
+
appendArgsFn,
|
|
74
|
+
onResolve,
|
|
75
|
+
} from './registration/IRegistration';
|
|
76
|
+
export { Registration } from './registration/Registration';
|
|
77
|
+
|
|
78
|
+
// Errors
|
|
79
|
+
export { ContainerError } from './errors/ContainerError';
|
|
80
|
+
export { DependencyNotFoundError } from './errors/DependencyNotFoundError';
|
|
81
|
+
export { ContainerNotFoundError } from './errors/ContainerNotFoundError';
|
|
82
|
+
export { DependencyMissingKeyError } from './errors/DependencyMissingKeyError';
|
|
83
|
+
export { MethodNotImplementedError } from './errors/MethodNotImplementedError';
|
|
84
|
+
export { ContainerDisposedError } from './errors/ContainerDisposedError';
|
|
85
|
+
export { ProviderDisposedError } from './errors/ProviderDisposedError';
|
|
86
|
+
export { CannotApplySingletonTwiceError } from './errors/CannotApplySingletonTwiceError';
|
|
87
|
+
export { CannonSingletonApplyTwiceError } from './errors/CannonSingletonApplyTwiceError';
|
|
88
|
+
export { UnsupportedTokenTypeError } from './errors/UnsupportedTokenTypeError';
|
|
89
|
+
export { TypedEventDisposedError } from './errors/TypedEventDisposedError';
|
|
90
|
+
export { ArgumentNotFoundError } from './errors/ArgumentNotFoundError';
|
|
91
|
+
|
|
92
|
+
// Hooks
|
|
93
|
+
export {
|
|
94
|
+
getHooks,
|
|
95
|
+
hook,
|
|
96
|
+
hasHooks,
|
|
97
|
+
toHookFn,
|
|
98
|
+
type HookFn,
|
|
99
|
+
type HookClass,
|
|
100
|
+
type HookType,
|
|
101
|
+
type InjectFn,
|
|
102
|
+
type HooksOfClass,
|
|
103
|
+
} from './hooks/hook';
|
|
104
|
+
export {
|
|
105
|
+
HookContext,
|
|
106
|
+
createHookContextFactory,
|
|
107
|
+
createHookExecutionContext,
|
|
108
|
+
type CreateHookExecutionContext,
|
|
109
|
+
type IHookContext,
|
|
110
|
+
} from './hooks/HookContext';
|
|
111
|
+
export { injectProp } from './hooks/injectProp';
|
|
112
|
+
export { sequential, parallel, oncePerInstance, type ResolvedObjectHook } from './hooks/combinators';
|
|
113
|
+
export {
|
|
114
|
+
HookCollector,
|
|
115
|
+
toTask,
|
|
116
|
+
type HookAction,
|
|
117
|
+
type HookCollectionContext,
|
|
118
|
+
type HookCollectorOptions,
|
|
119
|
+
type HookCollectorProps,
|
|
120
|
+
type MapHookExecutionContext,
|
|
121
|
+
} from './hooks/HookCollector';
|
|
122
|
+
|
|
123
|
+
// Tokens
|
|
124
|
+
export { InjectionToken, isInjectionToken, forwardArgs } from './token/InjectionToken';
|
|
125
|
+
export { type Injectable, toToken, argToToken } from './token/toToken';
|
|
126
|
+
export { GroupAliasToken, toGroupAlias } from './token/GroupAliasToken';
|
|
127
|
+
export { SingleAliasToken, toSingleAlias } from './token/SingleAliasToken';
|
|
128
|
+
export { ClassToken } from './token/ClassToken';
|
|
129
|
+
export { SingleToken } from './token/SingleToken';
|
|
130
|
+
export { FunctionToken } from './token/FunctionToken';
|
|
131
|
+
export { ConstantToken } from './token/ConstantToken';
|
|
132
|
+
export { type InstancePredicate, GroupInstanceToken } from './token/GroupInstanceToken';
|
|
133
|
+
|
|
134
|
+
// Metadata
|
|
135
|
+
export { resolveConstructor } from './metadata/target';
|
|
136
|
+
export {
|
|
137
|
+
addClassMeta,
|
|
138
|
+
getClassMeta,
|
|
139
|
+
addClassLabel,
|
|
140
|
+
getClassLabels,
|
|
141
|
+
addClassTag,
|
|
142
|
+
getClassTags,
|
|
143
|
+
createComposeClassDecorator,
|
|
144
|
+
} from './metadata/class';
|
|
145
|
+
export {
|
|
146
|
+
addParamMeta,
|
|
147
|
+
getParamMeta,
|
|
148
|
+
addParamLabel,
|
|
149
|
+
getParamLabels,
|
|
150
|
+
addParamTag,
|
|
151
|
+
getParamTags,
|
|
152
|
+
createComposeParameterDecorator,
|
|
153
|
+
} from './metadata/parameter';
|
|
154
|
+
export {
|
|
155
|
+
addMethodMeta,
|
|
156
|
+
getMethodMeta,
|
|
157
|
+
addMethodLabel,
|
|
158
|
+
getMethodLabels,
|
|
159
|
+
addMethodTag,
|
|
160
|
+
getMethodTags,
|
|
161
|
+
createComposeMethodDecorator,
|
|
162
|
+
} from './metadata/method';
|
|
163
|
+
export { handleError, handleAsyncError, type HandleErrorParams } from './utils/errorHandler';
|
|
164
|
+
export { runInOrder, runAtOnce, type Task } from './utils/task';
|
|
165
|
+
export { throttle } from './utils/throttle';
|
|
166
|
+
export { debounce } from './utils/debounce';
|
|
167
|
+
export { shallowCache } from './utils/shallowCache';
|
|
168
|
+
export { once } from './utils/once';
|
|
169
|
+
export { memoize } from './utils/memoize';
|
|
170
|
+
export { getConstructorChain } from './utils/getConstructorChain';
|
|
171
|
+
export { TypedEvent, type ITypedEvent, type TypedEventListener, type Unsubscribe } from './utils/TypedEvent';
|
|
172
|
+
|
|
173
|
+
// Execution
|
|
174
|
+
export { type ExecutionContext } from './ExecutionContext';
|
|
175
|
+
|
|
176
|
+
// Utils
|
|
177
|
+
export { select } from './select';
|
|
178
|
+
export { pipe, type MapFn } from './utils/fp';
|
|
179
|
+
export { findOrFail, type Predicate } from './utils/array';
|
|
180
|
+
export { ProxyRegistry, unwrapProxy, type IProxyRegistry } from './utils/ProxyRegistry';
|
|
181
|
+
export { type Branded, type constructor, type Instance, type Serializable, Is, isSerializable } from './utils/basic';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type IContainer, ResolveOneOptions } from '../container/IContainer';
|
|
2
|
+
import { ProviderOptions } from '../provider/IProvider';
|
|
3
|
+
import { type IProxyRegistry, ProxyRegistry } from '../utils/ProxyRegistry';
|
|
4
|
+
import { type constructor, Instance } from '../utils/basic';
|
|
5
|
+
|
|
6
|
+
export type WithScope = { scope: IContainer };
|
|
7
|
+
export type WithArgs = { args: unknown[] };
|
|
8
|
+
/**
|
|
9
|
+
* What an injector needs to build an instance: the scope it is built in, plus
|
|
10
|
+
* the runtime `args`. The scope travels inside the options rather than beside
|
|
11
|
+
* them, so every function handed a resolution context - `InjectFn`, `ArgsFn`,
|
|
12
|
+
* `ResolveDependency`, `IProvider.resolve`, `IInjector.resolve` - takes one
|
|
13
|
+
* object and destructures what it uses.
|
|
14
|
+
*/
|
|
15
|
+
export type InjectOptions = WithScope & Partial<WithArgs>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Injector hooks - the injector's own domain: an instance was constructed.
|
|
19
|
+
*
|
|
20
|
+
* Construction is the injector's business, so the hook list belongs to the
|
|
21
|
+
* injector rather than to a scope. One injector is shared by a container and
|
|
22
|
+
* every scope created from it, so a hook added here observes construction in
|
|
23
|
+
* all of them.
|
|
24
|
+
*/
|
|
25
|
+
export type InjectorHook = (instance: Instance, scope: IContainer) => void;
|
|
26
|
+
|
|
27
|
+
/** Builds instances of classes for a scope. Shared by a container and every scope created from it. */
|
|
28
|
+
export interface IInjector {
|
|
29
|
+
resolve<T>(Target: constructor<T>, options: ProviderOptions): T;
|
|
30
|
+
|
|
31
|
+
onConstructed(...hooks: InjectorHook[]): this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The injector counterpart of `IContainerModule`: an opt-in bundle of injector
|
|
36
|
+
* hooks, applied to the injector before it is handed to a container.
|
|
37
|
+
*/
|
|
38
|
+
export interface IInjectorModule {
|
|
39
|
+
applyTo(injector: IInjector): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Something that resolves a value from a scope. */
|
|
43
|
+
export interface IInjectFnResolver<T> {
|
|
44
|
+
resolve(s: IContainer, options?: ResolveOneOptions): T;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Base class of the bundled injectors: tracks the instance in the scope, runs
|
|
49
|
+
* `onConstructed` hooks and handles `lazy`. Subclass it and implement
|
|
50
|
+
* `createInstance` for a custom injection strategy.
|
|
51
|
+
*/
|
|
52
|
+
export abstract class Injector {
|
|
53
|
+
private readonly onConstructedHookList: InjectorHook[] = [];
|
|
54
|
+
|
|
55
|
+
constructor(private readonly proxyRegistry: IProxyRegistry = ProxyRegistry.getInstance()) {}
|
|
56
|
+
|
|
57
|
+
resolve<T>(Target: constructor<T>, { scope, args, lazy }: ProviderOptions): T {
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
return this.proxyRegistry.toLazyIf(() => {
|
|
60
|
+
const instance = this.createInstance(Target, { scope, args }) as Instance;
|
|
61
|
+
scope.addInstance(instance);
|
|
62
|
+
|
|
63
|
+
// Hooks run once the scope tracks the instance, so they observe a scope which owns it.
|
|
64
|
+
for (const onConstructed of this.onConstructedHookList) {
|
|
65
|
+
onConstructed(instance, scope);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return instance;
|
|
69
|
+
}, lazy);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Hooks run after the constructed instance is handed to the scope, and before
|
|
74
|
+
* it reaches the caller. A `lazy` resolve returns a proxy first, so its hooks
|
|
75
|
+
* run when the proxy is first touched.
|
|
76
|
+
*/
|
|
77
|
+
onConstructed(...hooks: InjectorHook[]): this {
|
|
78
|
+
this.onConstructedHookList.push(...hooks);
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
useModule(module: IInjectorModule): this {
|
|
83
|
+
module.applyTo(this);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
protected abstract createInstance<T>(Target: constructor<T>, options: InjectOptions): T;
|
|
88
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { IInjector, InjectOptions, Injector } from './IInjector';
|
|
2
|
+
import { type constructor, type Instance } from '../utils/basic';
|
|
3
|
+
import { resolveConstructor } from '../metadata/target';
|
|
4
|
+
import { addParamMeta, getParamMeta } from '../metadata/parameter';
|
|
5
|
+
import { ProviderOptions } from '../provider/IProvider';
|
|
6
|
+
import { InjectFn } from '../hooks/hook';
|
|
7
|
+
import { type DependencyKey } from '../container/IContainer';
|
|
8
|
+
import { type InjectionToken } from '../token/InjectionToken';
|
|
9
|
+
import { toToken } from '../token/toToken';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The default injector: builds a class by calling each constructor parameter's
|
|
13
|
+
* `@inject(...)` function. Parameters without `@inject` receive `undefined`.
|
|
14
|
+
* Needs `reflect-metadata` imported once at the entrypoint.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const container = new Container({ injector: new MetadataInjector() });
|
|
18
|
+
*/
|
|
19
|
+
export class MetadataInjector extends Injector implements IInjector {
|
|
20
|
+
protected createInstance<T>(Target: constructor<T>, { scope, args: deps = [] }: InjectOptions): T {
|
|
21
|
+
const args = resolveArgs(Target)({ scope, args: deps });
|
|
22
|
+
return new Target(...args);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const hookMetaKey = (methodName = 'constructor') => `inject:${methodName}`;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Injects a dependency into a constructor parameter.
|
|
30
|
+
*
|
|
31
|
+
* `fn` receives the resolution context - the `scope` the instance is built in and
|
|
32
|
+
* the runtime `args` it is built with - and returns the value to inject. That is
|
|
33
|
+
* the whole contract: a token, key or class is resolved with {@link by}, a
|
|
34
|
+
* runtime argument picked with {@link arg}, and a mapped value is
|
|
35
|
+
* `pipe(fn, ...mappers)`.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* class App {
|
|
39
|
+
* constructor(
|
|
40
|
+
* @inject(by(ILoggerToken)) private logger: ILogger,
|
|
41
|
+
* @inject(pipe(by(ConfigToken), (c) => c.apiUrl)) private apiUrl: string,
|
|
42
|
+
* @inject(arg(0)) private tenantId: string,
|
|
43
|
+
* ) {}
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
export function inject<T>(fn: InjectFn<T>): ParameterDecorator {
|
|
47
|
+
return (target, propertyKey, parameterIndex) => {
|
|
48
|
+
addParamMeta(hookMetaKey(propertyKey as string), () => fn)(resolveConstructor(target), propertyKey, parameterIndex);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The `InjectFn` which resolves `target` - an `InjectionToken`, a `DependencyKey`
|
|
54
|
+
* or a class - from the scope, handing it the runtime args of the class being
|
|
55
|
+
* constructed (see "Runtime args flow through tokens" in the README) and the
|
|
56
|
+
* `lazy` flag: `@inject(by(Token))`, `@inject(by('key'))`, `@inject(by(Logger))`.
|
|
57
|
+
* Being a function, it composes: `pipe(by(Config), (c) => c.apiUrl)`.
|
|
58
|
+
*
|
|
59
|
+
* @throws {UnsupportedTokenTypeError} when `target` is none of the three.
|
|
60
|
+
*/
|
|
61
|
+
export const by = <T>(target: InjectionToken<T> | DependencyKey | constructor<T>): InjectFn<T> => {
|
|
62
|
+
const token = toToken(target);
|
|
63
|
+
return ({ scope, ...options }) => token.resolve(scope, options);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Injects the first runtime arg matching `predicate`, or `undefined`.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* constructor(@inject(argsFn((value) => typeof value === 'string')) readonly name: string) {}
|
|
71
|
+
*/
|
|
72
|
+
export const argsFn =
|
|
73
|
+
<T = unknown>(predicate: (value: unknown, index: number) => boolean): InjectFn<T> =>
|
|
74
|
+
({ args = [] }): T =>
|
|
75
|
+
args.find((value, index) => predicate(value, index)) as T;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Injects the runtime arg at `index`, or `undefined`. Args arrive as passed:
|
|
79
|
+
* a token in the args list is not resolved.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* constructor(@inject(arg(0)) readonly baseUrl: string) {}
|
|
83
|
+
* // Token.args('https://api.example.com').resolve(scope)
|
|
84
|
+
*/
|
|
85
|
+
export const arg = <T = unknown>(index: number): InjectFn<T> => argsFn<T>((value, i) => i === index);
|
|
86
|
+
|
|
87
|
+
/** Injects the whole runtime args array. */
|
|
88
|
+
export const args: InjectFn<unknown[]> = ({ args = [] }) => args;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Resolves the arguments annotated with `@inject` on `target`'s constructor, or on
|
|
92
|
+
* its `methodName` method, by calling each parameter's `InjectFn` with `options`.
|
|
93
|
+
*
|
|
94
|
+
* `target` is the class or any instance of it - a proxy included, since it is
|
|
95
|
+
* unwrapped on the way to the metadata (see {@link resolveConstructor}).
|
|
96
|
+
*/
|
|
97
|
+
export const resolveArgs = (target: constructor<unknown> | Instance, methodName?: string) => {
|
|
98
|
+
const fns = getParamMeta(hookMetaKey(methodName), target) as InjectFn[];
|
|
99
|
+
return (options: ProviderOptions): unknown[] => fns.map((fn) => fn(options));
|
|
100
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { IInjector, InjectOptions, Injector } from './IInjector';
|
|
2
|
+
import { type constructor } from '../utils/basic';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Injector that passes one proxy object as the first constructor argument:
|
|
6
|
+
* reading `deps.someKey` resolves `'someKey'`, a property name containing
|
|
7
|
+
* "alias" resolves by alias, and `deps.args` returns the runtime args. Needs no
|
|
8
|
+
* decorators or `reflect-metadata`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* class App {
|
|
12
|
+
* constructor({ logger }: { logger: ILogger }) {}
|
|
13
|
+
* }
|
|
14
|
+
* new Container({ injector: new ProxyInjector() });
|
|
15
|
+
*/
|
|
16
|
+
export class ProxyInjector extends Injector implements IInjector {
|
|
17
|
+
protected createInstance<T>(Target: constructor<T>, { scope, args = [] }: InjectOptions): T {
|
|
18
|
+
const proxy = new Proxy(
|
|
19
|
+
{},
|
|
20
|
+
{
|
|
21
|
+
get(target: {}, prop: string | symbol): any {
|
|
22
|
+
if (prop === 'args') {
|
|
23
|
+
return args;
|
|
24
|
+
}
|
|
25
|
+
return prop.toString().search(/alias/gi) >= 0 ? scope.resolveByAlias(prop) : scope.resolve(prop);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
return new Target(proxy);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IInjector, InjectOptions, Injector } from './IInjector';
|
|
2
|
+
import { type constructor } from '../utils/basic';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Injector that passes the scope itself as the first constructor argument,
|
|
6
|
+
* followed by the runtime args. Needs no decorators or `reflect-metadata`.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* class App {
|
|
10
|
+
* constructor(scope: IContainer) {
|
|
11
|
+
* this.logger = scope.resolve('Logger');
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
* new Container({ injector: new SimpleInjector() });
|
|
15
|
+
*/
|
|
16
|
+
export class SimpleInjector extends Injector implements IInjector {
|
|
17
|
+
protected createInstance<T>(Target: constructor<T>, { scope, args = [] }: InjectOptions): T {
|
|
18
|
+
return new Target(scope, ...args);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { resolveConstructor } from './target';
|
|
2
|
+
|
|
3
|
+
/** Decorator that writes class metadata under `key`; `mapFn` receives the previous value. */
|
|
4
|
+
export const addClassMeta =
|
|
5
|
+
<T>(key: string | symbol, mapFn: (prev: T | undefined) => T): ClassDecorator =>
|
|
6
|
+
(target) => {
|
|
7
|
+
const value: T | undefined = Reflect.getOwnMetadata(key, target);
|
|
8
|
+
Reflect.defineMetadata(key, mapFn(value), target);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** Reads class metadata written by `addClassMeta`. */
|
|
12
|
+
export function getClassMeta<T>(target: object, key: string | symbol): T | undefined {
|
|
13
|
+
return Reflect.getOwnMetadata(key, resolveConstructor(target));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Decorator that attaches a `key` -> `label` pair to a class. */
|
|
17
|
+
export const addClassLabel = (key: string, label: string) =>
|
|
18
|
+
addClassMeta('label', (prev: Map<string, string> = new Map()) => prev.set(key, label));
|
|
19
|
+
/** Reads the labels written by `addClassLabel`. */
|
|
20
|
+
export const getClassLabels = (target: object): Map<string, string> => getClassMeta(target, 'label') ?? new Map();
|
|
21
|
+
|
|
22
|
+
/** Decorator that attaches a tag to a class. */
|
|
23
|
+
export const addClassTag = (tag: string) => addClassMeta('tag', (prev: Set<string> = new Set()) => prev.add(tag));
|
|
24
|
+
/** Reads the tags written by `addClassTag`. */
|
|
25
|
+
export const getClassTags = (target: object): Set<string> => getClassMeta(target, 'tag') ?? new Set();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Applies several class decorators as one, so a stack repeated on many classes
|
|
29
|
+
* can be given a name:
|
|
30
|
+
*
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const repository = <T>(token: SingleToken<T>, ...mappers: RegistrationMapper<T>[]) =>
|
|
33
|
+
* createComposeClassDecorator(
|
|
34
|
+
* register(IRepositoryToken, token, addMediator(token), ...mappers),
|
|
35
|
+
* addClassMeta('injection-token', () => token),
|
|
36
|
+
* );
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* Decorators are applied bottom-up, exactly as stacking them would be, so
|
|
40
|
+
* `@createComposeClassDecorator(a, b)` behaves like `@a @b` - moving a stack into one
|
|
41
|
+
* call never changes which decorator writes its metadata first.
|
|
42
|
+
*
|
|
43
|
+
* A decorator which returns a replacement class hands it to the next one, and
|
|
44
|
+
* the last replacement is returned - the same threading the runtime does for a
|
|
45
|
+
* stack.
|
|
46
|
+
*/
|
|
47
|
+
export const createComposeClassDecorator =
|
|
48
|
+
(...decorators: ClassDecorator[]): ClassDecorator =>
|
|
49
|
+
(target) =>
|
|
50
|
+
decorators.reduceRight((acc, decorate) => decorate(acc) ?? acc, target);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { resolveConstructor } from './target';
|
|
2
|
+
|
|
3
|
+
/** Decorator that writes method metadata under `key`; `mapFn` receives the previous value. */
|
|
4
|
+
export const addMethodMeta =
|
|
5
|
+
<T>(key: string, mapFn: (prev: T | undefined) => T): MethodDecorator =>
|
|
6
|
+
(target, propertyKey) => {
|
|
7
|
+
const metadata: T | undefined = Reflect.getMetadata(key, target.constructor, propertyKey);
|
|
8
|
+
Reflect.defineMetadata(key, mapFn(metadata), target.constructor, propertyKey);
|
|
9
|
+
};
|
|
10
|
+
/** Reads method metadata written by `addMethodMeta`. */
|
|
11
|
+
export const getMethodMeta = (key: string, target: object, propertyKey: string): unknown =>
|
|
12
|
+
Reflect.getMetadata(key, resolveConstructor(target), propertyKey);
|
|
13
|
+
|
|
14
|
+
/** Decorator that attaches a `key` -> `label` pair to a method. */
|
|
15
|
+
export const addMethodLabel = (key: string, label: string) =>
|
|
16
|
+
addMethodMeta('label', (prev: Map<string, string> = new Map()) => prev.set(key, label));
|
|
17
|
+
/** Reads the labels written by `addMethodLabel`. */
|
|
18
|
+
export const getMethodLabels = (target: object, propertyKey: string): Map<string, string> =>
|
|
19
|
+
(getMethodMeta('label', target, propertyKey) as Map<string, string> | undefined) ?? new Map();
|
|
20
|
+
|
|
21
|
+
/** Decorator that attaches a tag to a method. */
|
|
22
|
+
export const addMethodTag = (tag: string) => addMethodMeta('tag', (prev: Set<string> = new Set()) => prev.add(tag));
|
|
23
|
+
/** Reads the tags written by `addMethodTag`. */
|
|
24
|
+
export const getMethodTags = (target: object, propertyKey: string): Set<string> =>
|
|
25
|
+
(getMethodMeta('tag', target, propertyKey) as Set<string> | undefined) ?? new Set();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Applies several method decorators as one, so a stack repeated on many members
|
|
29
|
+
* can be given a name:
|
|
30
|
+
*
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const handler = (event: string) =>
|
|
33
|
+
* createComposeMethodDecorator(addMethodTag('handler'), addMethodMeta('event', () => event));
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Decorators are applied bottom-up, exactly as stacking them would be, so
|
|
37
|
+
* `@createComposeMethodDecorator(a, b)` behaves like `@a @b`.
|
|
38
|
+
*
|
|
39
|
+
* The property descriptor is threaded through the chain the way the runtime
|
|
40
|
+
* threads it: a decorator which returns a replacement descriptor hands it to the
|
|
41
|
+
* next one, and the last replacement is returned. That is what makes wrapping
|
|
42
|
+
* decorators such as `@once` or `@throttle` compose here.
|
|
43
|
+
*/
|
|
44
|
+
export const createComposeMethodDecorator =
|
|
45
|
+
(...decorators: MethodDecorator[]): MethodDecorator =>
|
|
46
|
+
(target, propertyKey, descriptor) =>
|
|
47
|
+
decorators.reduceRight((acc, decorate) => decorate(target, propertyKey, acc) ?? acc, descriptor);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { resolveConstructor } from './target';
|
|
2
|
+
|
|
3
|
+
/** Decorator that writes parameter metadata under `key`; `mapFn` receives the previous value. */
|
|
4
|
+
export const addParamMeta =
|
|
5
|
+
(key: string | symbol, mapFn: (prev: unknown) => unknown): ParameterDecorator =>
|
|
6
|
+
(target, _, parameterIndex) => {
|
|
7
|
+
const metadata: unknown[] = Reflect.getOwnMetadata(key, target) ?? [];
|
|
8
|
+
metadata[parameterIndex] = mapFn(metadata[parameterIndex]);
|
|
9
|
+
Reflect.defineMetadata(key, metadata, target);
|
|
10
|
+
};
|
|
11
|
+
/** Reads parameter metadata written by `addParamMeta`. */
|
|
12
|
+
export const getParamMeta = (key: string | symbol, target: object): unknown[] => {
|
|
13
|
+
return (Reflect.getOwnMetadata(key, resolveConstructor(target)) as unknown[]) ?? [];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** Decorator that attaches a `key` -> `label` pair to a parameter. */
|
|
17
|
+
export const addParamLabel = (key: string, label: string) =>
|
|
18
|
+
addParamMeta('label', (prev: unknown) => {
|
|
19
|
+
const map = (prev as Map<string, string> | undefined) ?? new Map<string, string>();
|
|
20
|
+
return map.set(key, label);
|
|
21
|
+
});
|
|
22
|
+
/** Reads the labels written by `addParamLabel`. */
|
|
23
|
+
export const getParamLabels = (target: object, parameterIndex: number): Map<string, string> => {
|
|
24
|
+
const all = getParamMeta('label', target);
|
|
25
|
+
return (all[parameterIndex] as Map<string, string> | undefined) ?? new Map();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Decorator that attaches a tag to a parameter. */
|
|
29
|
+
export const addParamTag = (tag: string) =>
|
|
30
|
+
addParamMeta('tag', (prev: unknown) => {
|
|
31
|
+
const set = (prev as Set<string> | undefined) ?? new Set<string>();
|
|
32
|
+
return set.add(tag);
|
|
33
|
+
});
|
|
34
|
+
/** Reads the tags written by `addParamTag`. */
|
|
35
|
+
export const getParamTags = (target: object, parameterIndex: number): Set<string> => {
|
|
36
|
+
const all = getParamMeta('tag', target);
|
|
37
|
+
return (all[parameterIndex] as Set<string> | undefined) ?? new Set();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Applies several parameter decorators as one, so a stack repeated on many
|
|
42
|
+
* parameters can be given a name:
|
|
43
|
+
*
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const fromConfig = <T>(key: string, map: MapFn<IConfig, T>) =>
|
|
46
|
+
* createComposeParameterDecorator(inject(pipe(by(ConfigToken), map)), addParamLabel('config', key));
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Decorators are applied bottom-up, exactly as stacking them would be, so
|
|
50
|
+
* `@createComposeParameterDecorator(a, b)` behaves like `@a @b`. A parameter
|
|
51
|
+
* decorator returns nothing, so there is nothing to thread - each one is called
|
|
52
|
+
* with the same target, property key and parameter index.
|
|
53
|
+
*/
|
|
54
|
+
export const createComposeParameterDecorator =
|
|
55
|
+
(...decorators: ParameterDecorator[]): ParameterDecorator =>
|
|
56
|
+
(target, propertyKey, parameterIndex) => {
|
|
57
|
+
for (let i = decorators.length - 1; i >= 0; i--) {
|
|
58
|
+
decorators[i](target, propertyKey, parameterIndex);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type constructor, type Instance, Is } from '../utils/basic';
|
|
2
|
+
import { unwrapProxy } from '../utils/ProxyRegistry';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The class behind `target`: `target` itself when it is already a constructor,
|
|
6
|
+
* otherwise the constructor of the instance.
|
|
7
|
+
*
|
|
8
|
+
* `target` may be a proxy (a `lazy()` provider hands one out), and it is
|
|
9
|
+
* unwrapped before either branch, because metadata is defined on the real class
|
|
10
|
+
* and a proxy is never that class. A proxied class misses the lookup outright,
|
|
11
|
+
* standing in for the very object metadata is keyed by; a proxied instance would
|
|
12
|
+
* only answer `constructor` correctly if its handler forwards the read, which a
|
|
13
|
+
* custom one need not do.
|
|
14
|
+
*
|
|
15
|
+
* The branch tests for a constructor rather than for an instance: `Is.instance`
|
|
16
|
+
* asks for an *own* `constructor` property, which a prototype has and an
|
|
17
|
+
* instance does not - its own `constructor` lives one level up the chain.
|
|
18
|
+
*
|
|
19
|
+
* Every metadata read goes through here, so callers pass whatever they hold - a
|
|
20
|
+
* class, an instance, or a proxy of either - and never unwrap by hand.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveConstructor(target: constructor<unknown> | Instance): constructor<unknown> {
|
|
23
|
+
const value = unwrapProxy(target);
|
|
24
|
+
return Is.constructor(value) ? value : (value.constructor as constructor<unknown>);
|
|
25
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { IContainer, Tagged } from '../container/IContainer';
|
|
2
|
+
import { InjectOptions, WithScope } from '../injector/IInjector';
|
|
3
|
+
import { Serializable } from '../utils/basic';
|
|
4
|
+
|
|
5
|
+
export type WithLazy = { lazy: boolean };
|
|
6
|
+
/**
|
|
7
|
+
* The resolution context a provider works in: the `scope` resolving, the runtime
|
|
8
|
+
* `args`, and whether the caller asked for a `lazy` instance. One object, so a
|
|
9
|
+
* function handed it destructures what it uses (`({ scope, args }) => ...`).
|
|
10
|
+
*/
|
|
11
|
+
export type ProviderOptions = InjectOptions & Partial<WithLazy>;
|
|
12
|
+
/**
|
|
13
|
+
* `ProviderOptions` as a caller which addresses the scope directly passes them -
|
|
14
|
+
* `scope.resolve(key, options)`, `token.resolve(scope, options)` - so the scope is
|
|
15
|
+
* not repeated inside.
|
|
16
|
+
*/
|
|
17
|
+
export type ResolveOptions = Omit<ProviderOptions, keyof WithScope>;
|
|
18
|
+
/** A factory: builds the dependency from the resolution context. What `Registration.fromFn` takes. */
|
|
19
|
+
export type ResolveDependency<T = unknown> = (options: ProviderOptions) => T;
|
|
20
|
+
/** What a {@link ScopeAccessRule} sees: the scope asking, the scope holding the provider, and the runtime args. */
|
|
21
|
+
export type ScopeAccessOptions = { invocationScope: Tagged; providerScope: Tagged; args: unknown[] };
|
|
22
|
+
/** Decides whether a provider may be resolved for an invocation; `prev` is the result of earlier rules. Set with `scopeAccess(...)`. */
|
|
23
|
+
export type ScopeAccessRule = (options: ScopeAccessOptions, prev: boolean) => boolean;
|
|
24
|
+
|
|
25
|
+
/** Computes args from the resolution context. */
|
|
26
|
+
export type ArgsFn = (options: InjectOptions) => unknown[];
|
|
27
|
+
|
|
28
|
+
/** Maps runtime args to a `singleton(...)` cache key: one instance per distinct key. */
|
|
29
|
+
export type GetCacheKey = (args: unknown[]) => string | Serializable;
|
|
30
|
+
/** Wraps or replaces a resolved dependency. Used by `decorate(...)`. */
|
|
31
|
+
export type DecorateFn<Instance = any> = (dep: Instance, scope: IContainer) => Instance;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Provider hooks - the provider's own domain: a dependency was resolved.
|
|
35
|
+
*
|
|
36
|
+
* Resolution is the provider's business, so the hook list belongs to the
|
|
37
|
+
* provider. A dependency is not necessarily a constructed instance - a provider
|
|
38
|
+
* can hand out a value the container never built - so the hook takes `unknown`.
|
|
39
|
+
*/
|
|
40
|
+
export type ProviderHook = (dependency: unknown, scope: IContainer) => void;
|
|
41
|
+
|
|
42
|
+
/** The factory behind a registration. See {@link Provider}. */
|
|
43
|
+
export interface IProvider<T = any> {
|
|
44
|
+
resolve(options: ProviderOptions): T;
|
|
45
|
+
|
|
46
|
+
hasAccess(options: ScopeAccessOptions): boolean;
|
|
47
|
+
|
|
48
|
+
map(...mappers: DecorateFn<T>[]): this;
|
|
49
|
+
|
|
50
|
+
addAccessRule(...rules: ScopeAccessRule[]): this;
|
|
51
|
+
|
|
52
|
+
addArgsFn(argsFn: ArgsFn): this;
|
|
53
|
+
|
|
54
|
+
lazy(): this;
|
|
55
|
+
|
|
56
|
+
autoResolve(): this;
|
|
57
|
+
|
|
58
|
+
isAutoResolvable(): boolean;
|
|
59
|
+
|
|
60
|
+
singleton(getCacheKey?: GetCacheKey): this;
|
|
61
|
+
|
|
62
|
+
dispose(): void;
|
|
63
|
+
|
|
64
|
+
onResolved(...hooks: ProviderHook[]): this;
|
|
65
|
+
}
|