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.
Files changed (255) hide show
  1. package/AGENTS.md +179 -0
  2. package/README.md +169 -0
  3. package/cjm/container/Container.js +5 -2
  4. package/cjm/container/EmptyContainer.js +20 -15
  5. package/cjm/errors/ArgumentNotFoundError.js +1 -0
  6. package/cjm/errors/CannonSingletonApplyTwiceError.js +2 -14
  7. package/cjm/errors/CannotApplySingletonTwiceError.js +18 -0
  8. package/cjm/errors/ContainerDisposedError.js +1 -0
  9. package/cjm/errors/ContainerError.js +1 -0
  10. package/cjm/errors/ContainerNotFoundError.js +1 -0
  11. package/cjm/errors/DependencyMissingKeyError.js +1 -0
  12. package/cjm/errors/DependencyNotFoundError.js +1 -0
  13. package/cjm/errors/MethodNotImplementedError.js +1 -0
  14. package/cjm/errors/ProviderDisposedError.js +1 -0
  15. package/cjm/errors/TypedEventDisposedError.js +1 -0
  16. package/cjm/errors/UnsupportedTokenTypeError.js +1 -0
  17. package/cjm/feature/IFeatureFlags.js +6 -0
  18. package/cjm/index.js +14 -3
  19. package/cjm/provider/Provider.js +6 -6
  20. package/cjm/registration/Registration.js +2 -2
  21. package/cjm/token/BindableToken.js +63 -0
  22. package/cjm/token/ConstantToken.js +4 -4
  23. package/cjm/token/FunctionToken.js +1 -1
  24. package/cjm/token/GroupAliasToken.js +5 -50
  25. package/cjm/token/GroupInstanceToken.js +4 -4
  26. package/cjm/token/MultiVariantFeatureToken.js +84 -0
  27. package/cjm/token/SingleAliasToken.js +5 -50
  28. package/cjm/token/SingleToken.js +5 -50
  29. package/cjm/token/ToggleFeatureToken.js +10 -0
  30. package/cjm/token/toToken.js +1 -1
  31. package/cjm/utils/TypedEvent.js +1 -1
  32. package/cjm/utils/array.js +1 -1
  33. package/esm/container/Container.js +5 -2
  34. package/esm/container/EmptyContainer.js +20 -15
  35. package/esm/errors/ArgumentNotFoundError.js +1 -0
  36. package/esm/errors/CannonSingletonApplyTwiceError.js +2 -13
  37. package/esm/errors/CannotApplySingletonTwiceError.js +14 -0
  38. package/esm/errors/ContainerDisposedError.js +1 -0
  39. package/esm/errors/ContainerError.js +1 -0
  40. package/esm/errors/ContainerNotFoundError.js +1 -0
  41. package/esm/errors/DependencyMissingKeyError.js +1 -0
  42. package/esm/errors/DependencyNotFoundError.js +1 -0
  43. package/esm/errors/MethodNotImplementedError.js +1 -0
  44. package/esm/errors/ProviderDisposedError.js +1 -0
  45. package/esm/errors/TypedEventDisposedError.js +1 -0
  46. package/esm/errors/UnsupportedTokenTypeError.js +1 -0
  47. package/esm/feature/IFeatureFlags.js +3 -0
  48. package/esm/index.js +5 -0
  49. package/esm/provider/Provider.js +6 -6
  50. package/esm/registration/Registration.js +2 -2
  51. package/esm/token/BindableToken.js +59 -0
  52. package/esm/token/ConstantToken.js +4 -4
  53. package/esm/token/FunctionToken.js +1 -1
  54. package/esm/token/GroupAliasToken.js +5 -50
  55. package/esm/token/GroupInstanceToken.js +4 -4
  56. package/esm/token/MultiVariantFeatureToken.js +80 -0
  57. package/esm/token/SingleAliasToken.js +5 -50
  58. package/esm/token/SingleToken.js +5 -50
  59. package/esm/token/ToggleFeatureToken.js +6 -0
  60. package/esm/token/toToken.js +1 -1
  61. package/esm/utils/TypedEvent.js +1 -1
  62. package/esm/utils/array.js +1 -1
  63. package/lib/ExecutionContext.ts +6 -0
  64. package/lib/container/AliasMap.ts +30 -0
  65. package/lib/container/AutoResolveModule.ts +24 -0
  66. package/lib/container/Container.ts +315 -0
  67. package/lib/container/EmptyContainer.ts +170 -0
  68. package/lib/container/IContainer.ts +159 -0
  69. package/lib/errors/ArgumentNotFoundError.ts +13 -0
  70. package/lib/errors/CannonSingletonApplyTwiceError.ts +6 -0
  71. package/lib/errors/CannotApplySingletonTwiceError.ts +22 -0
  72. package/lib/errors/ContainerDisposedError.ts +13 -0
  73. package/lib/errors/ContainerError.ts +15 -0
  74. package/lib/errors/ContainerNotFoundError.ts +13 -0
  75. package/lib/errors/DependencyMissingKeyError.ts +13 -0
  76. package/lib/errors/DependencyNotFoundError.ts +13 -0
  77. package/lib/errors/MethodNotImplementedError.ts +13 -0
  78. package/lib/errors/ProviderDisposedError.ts +22 -0
  79. package/lib/errors/TypedEventDisposedError.ts +22 -0
  80. package/lib/errors/UnsupportedTokenTypeError.ts +13 -0
  81. package/lib/feature/IFeatureFlags.ts +44 -0
  82. package/lib/hooks/HookCollector.ts +109 -0
  83. package/lib/hooks/HookContext.ts +85 -0
  84. package/lib/hooks/combinators.ts +51 -0
  85. package/lib/hooks/hook.ts +89 -0
  86. package/lib/hooks/injectProp.ts +14 -0
  87. package/lib/index.ts +197 -0
  88. package/lib/injector/IInjector.ts +88 -0
  89. package/lib/injector/MetadataInjector.ts +100 -0
  90. package/lib/injector/ProxyInjector.ts +31 -0
  91. package/lib/injector/SimpleInjector.ts +20 -0
  92. package/lib/metadata/class.ts +50 -0
  93. package/lib/metadata/method.ts +47 -0
  94. package/lib/metadata/parameter.ts +60 -0
  95. package/lib/metadata/target.ts +25 -0
  96. package/lib/provider/IProvider.ts +65 -0
  97. package/lib/provider/Provider.ts +174 -0
  98. package/lib/registration/IRegistration.ts +242 -0
  99. package/lib/registration/Registration.ts +122 -0
  100. package/lib/select.ts +33 -0
  101. package/lib/token/BindToken.ts +13 -0
  102. package/lib/token/BindableToken.ts +137 -0
  103. package/lib/token/ClassToken.ts +72 -0
  104. package/lib/token/ConstantToken.ts +50 -0
  105. package/lib/token/FunctionToken.ts +79 -0
  106. package/lib/token/GroupAliasToken.ts +33 -0
  107. package/lib/token/GroupInstanceToken.ts +70 -0
  108. package/lib/token/InjectionToken.ts +46 -0
  109. package/lib/token/MultiVariantFeatureToken.ts +160 -0
  110. package/lib/token/SingleAliasToken.ts +28 -0
  111. package/lib/token/SingleToken.ts +35 -0
  112. package/lib/token/ToggleFeatureToken.ts +27 -0
  113. package/lib/token/toToken.ts +46 -0
  114. package/lib/utils/ProxyRegistry.ts +149 -0
  115. package/lib/utils/TypedEvent.ts +96 -0
  116. package/lib/utils/array.ts +28 -0
  117. package/lib/utils/basic.ts +34 -0
  118. package/lib/utils/debounce.ts +18 -0
  119. package/lib/utils/errorHandler.ts +34 -0
  120. package/lib/utils/fp.ts +86 -0
  121. package/lib/utils/getConstructorChain.ts +17 -0
  122. package/lib/utils/memoize.ts +24 -0
  123. package/lib/utils/once.ts +24 -0
  124. package/lib/utils/shallowCache.ts +22 -0
  125. package/lib/utils/task.ts +33 -0
  126. package/lib/utils/throttle.ts +17 -0
  127. package/package.json +6 -4
  128. package/typings/ExecutionContext.d.ts +2 -0
  129. package/typings/ExecutionContext.d.ts.map +1 -0
  130. package/typings/container/AliasMap.d.ts +1 -0
  131. package/typings/container/AliasMap.d.ts.map +1 -0
  132. package/typings/container/AutoResolveModule.d.ts +16 -0
  133. package/typings/container/AutoResolveModule.d.ts.map +1 -0
  134. package/typings/container/Container.d.ts +61 -0
  135. package/typings/container/Container.d.ts.map +1 -0
  136. package/typings/container/EmptyContainer.d.ts +52 -0
  137. package/typings/container/EmptyContainer.d.ts.map +1 -0
  138. package/typings/container/IContainer.d.ts +77 -0
  139. package/typings/container/IContainer.d.ts.map +1 -0
  140. package/typings/errors/ArgumentNotFoundError.d.ts +3 -0
  141. package/typings/errors/ArgumentNotFoundError.d.ts.map +1 -0
  142. package/typings/errors/CannonSingletonApplyTwiceError.d.ts +6 -6
  143. package/typings/errors/CannonSingletonApplyTwiceError.d.ts.map +1 -0
  144. package/typings/errors/CannotApplySingletonTwiceError.d.ts +12 -0
  145. package/typings/errors/CannotApplySingletonTwiceError.d.ts.map +1 -0
  146. package/typings/errors/ContainerDisposedError.d.ts +3 -0
  147. package/typings/errors/ContainerDisposedError.d.ts.map +1 -0
  148. package/typings/errors/ContainerError.d.ts +7 -0
  149. package/typings/errors/ContainerError.d.ts.map +1 -0
  150. package/typings/errors/ContainerNotFoundError.d.ts +3 -0
  151. package/typings/errors/ContainerNotFoundError.d.ts.map +1 -0
  152. package/typings/errors/DependencyMissingKeyError.d.ts +3 -0
  153. package/typings/errors/DependencyMissingKeyError.d.ts.map +1 -0
  154. package/typings/errors/DependencyNotFoundError.d.ts +3 -0
  155. package/typings/errors/DependencyNotFoundError.d.ts.map +1 -0
  156. package/typings/errors/MethodNotImplementedError.d.ts +3 -0
  157. package/typings/errors/MethodNotImplementedError.d.ts.map +1 -0
  158. package/typings/errors/ProviderDisposedError.d.ts +6 -0
  159. package/typings/errors/ProviderDisposedError.d.ts.map +1 -0
  160. package/typings/errors/TypedEventDisposedError.d.ts +6 -0
  161. package/typings/errors/TypedEventDisposedError.d.ts.map +1 -0
  162. package/typings/errors/UnsupportedTokenTypeError.d.ts +3 -0
  163. package/typings/errors/UnsupportedTokenTypeError.d.ts.map +1 -0
  164. package/typings/feature/IFeatureFlags.d.ts +42 -0
  165. package/typings/feature/IFeatureFlags.d.ts.map +1 -0
  166. package/typings/hooks/HookCollector.d.ts +36 -0
  167. package/typings/hooks/HookCollector.d.ts.map +1 -0
  168. package/typings/hooks/HookContext.d.ts +10 -0
  169. package/typings/hooks/HookContext.d.ts.map +1 -0
  170. package/typings/hooks/combinators.d.ts +23 -0
  171. package/typings/hooks/combinators.d.ts.map +1 -0
  172. package/typings/hooks/hook.d.ts +39 -0
  173. package/typings/hooks/hook.d.ts.map +1 -0
  174. package/typings/hooks/injectProp.d.ts +9 -0
  175. package/typings/hooks/injectProp.d.ts.map +1 -0
  176. package/typings/index.d.ts +6 -0
  177. package/typings/index.d.ts.map +1 -0
  178. package/typings/injector/IInjector.d.ts +32 -0
  179. package/typings/injector/IInjector.d.ts.map +1 -0
  180. package/typings/injector/MetadataInjector.d.ts +58 -0
  181. package/typings/injector/MetadataInjector.d.ts.map +1 -0
  182. package/typings/injector/ProxyInjector.d.ts +13 -0
  183. package/typings/injector/ProxyInjector.d.ts.map +1 -0
  184. package/typings/injector/SimpleInjector.d.ts +13 -0
  185. package/typings/injector/SimpleInjector.d.ts.map +1 -0
  186. package/typings/metadata/class.d.ts +27 -0
  187. package/typings/metadata/class.d.ts.map +1 -0
  188. package/typings/metadata/method.d.ts +24 -0
  189. package/typings/metadata/method.d.ts.map +1 -0
  190. package/typings/metadata/parameter.d.ts +21 -0
  191. package/typings/metadata/parameter.d.ts.map +1 -0
  192. package/typings/metadata/target.d.ts +19 -0
  193. package/typings/metadata/target.d.ts.map +1 -0
  194. package/typings/provider/IProvider.d.ts +25 -0
  195. package/typings/provider/IProvider.d.ts.map +1 -0
  196. package/typings/provider/Provider.d.ts +30 -0
  197. package/typings/provider/Provider.d.ts.map +1 -0
  198. package/typings/registration/IRegistration.d.ts +144 -0
  199. package/typings/registration/IRegistration.d.ts.map +1 -0
  200. package/typings/registration/Registration.d.ts +23 -0
  201. package/typings/registration/Registration.d.ts.map +1 -0
  202. package/typings/select.d.ts +15 -0
  203. package/typings/select.d.ts.map +1 -0
  204. package/typings/token/BindToken.d.ts +3 -0
  205. package/typings/token/BindToken.d.ts.map +1 -0
  206. package/typings/token/BindableToken.d.ts +80 -0
  207. package/typings/token/BindableToken.d.ts.map +1 -0
  208. package/typings/token/ClassToken.d.ts +2 -0
  209. package/typings/token/ClassToken.d.ts.map +1 -0
  210. package/typings/token/ConstantToken.d.ts +14 -0
  211. package/typings/token/ConstantToken.d.ts.map +1 -0
  212. package/typings/token/FunctionToken.d.ts +10 -0
  213. package/typings/token/FunctionToken.d.ts.map +1 -0
  214. package/typings/token/GroupAliasToken.d.ts +21 -22
  215. package/typings/token/GroupAliasToken.d.ts.map +1 -0
  216. package/typings/token/GroupInstanceToken.d.ts +19 -0
  217. package/typings/token/GroupInstanceToken.d.ts.map +1 -0
  218. package/typings/token/InjectionToken.d.ts +8 -0
  219. package/typings/token/InjectionToken.d.ts.map +1 -0
  220. package/typings/token/MultiVariantFeatureToken.d.ts +79 -0
  221. package/typings/token/MultiVariantFeatureToken.d.ts.map +1 -0
  222. package/typings/token/SingleAliasToken.d.ts +16 -22
  223. package/typings/token/SingleAliasToken.d.ts.map +1 -0
  224. package/typings/token/SingleToken.d.ts +25 -21
  225. package/typings/token/SingleToken.d.ts.map +1 -0
  226. package/typings/token/ToggleFeatureToken.d.ts +25 -0
  227. package/typings/token/ToggleFeatureToken.d.ts.map +1 -0
  228. package/typings/token/toToken.d.ts +12 -0
  229. package/typings/token/toToken.d.ts.map +1 -0
  230. package/typings/utils/ProxyRegistry.d.ts +63 -0
  231. package/typings/utils/ProxyRegistry.d.ts.map +1 -0
  232. package/typings/utils/TypedEvent.d.ts +42 -0
  233. package/typings/utils/TypedEvent.d.ts.map +1 -0
  234. package/typings/utils/array.d.ts +7 -0
  235. package/typings/utils/array.d.ts.map +1 -0
  236. package/typings/utils/basic.d.ts +12 -0
  237. package/typings/utils/basic.d.ts.map +1 -0
  238. package/typings/utils/debounce.d.ts +2 -0
  239. package/typings/utils/debounce.d.ts.map +1 -0
  240. package/typings/utils/errorHandler.d.ts +4 -0
  241. package/typings/utils/errorHandler.d.ts.map +1 -0
  242. package/typings/utils/fp.d.ts +8 -0
  243. package/typings/utils/fp.d.ts.map +1 -0
  244. package/typings/utils/getConstructorChain.d.ts +9 -0
  245. package/typings/utils/getConstructorChain.d.ts.map +1 -0
  246. package/typings/utils/memoize.d.ts +10 -0
  247. package/typings/utils/memoize.d.ts.map +1 -0
  248. package/typings/utils/once.d.ts +2 -0
  249. package/typings/utils/once.d.ts.map +1 -0
  250. package/typings/utils/shallowCache.d.ts +2 -0
  251. package/typings/utils/shallowCache.d.ts.map +1 -0
  252. package/typings/utils/task.d.ts +13 -0
  253. package/typings/utils/task.d.ts.map +1 -0
  254. package/typings/utils/throttle.d.ts +2 -0
  255. package/typings/utils/throttle.d.ts.map +1 -0
@@ -0,0 +1,159 @@
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
+ /** Whether a provider carrying `alias` is registered in this scope or a parent. Never throws for a missing alias. */
103
+ hasAlias(alias: DependencyKey): boolean;
104
+
105
+ /**
106
+ * Resolves a key, or constructs a class with the injector. Takes a key or a
107
+ * class, not a token: for a token call `token.resolve(scope)`.
108
+ *
109
+ * @example
110
+ * scope.resolve<ILogger>('ILogger');
111
+ * scope.resolve(App, { args: ['tenant-1'] });
112
+ */
113
+ resolve<T>(target: constructor<T> | DependencyKey, options?: ResolveOneOptions): T;
114
+
115
+ /** Resolves every accessible dependency registered under `alias`, in this scope and its parents. */
116
+ resolveByAlias<T>(alias: DependencyKey, options?: ResolveManyOptions): T[];
117
+
118
+ /** Resolves one dependency registered under `alias`, looking here first and then in parents. */
119
+ resolveOneByAlias<T>(alias: DependencyKey, options?: ResolveOneOptions): T;
120
+
121
+ /**
122
+ * Creates a child scope and copies into it the registrations whose scope
123
+ * rules match its tags. Registrations added later are not copied into it.
124
+ *
125
+ * @example
126
+ * const request = app.createScope({ tags: ['request'] });
127
+ */
128
+ createScope(options?: CreateScopeOptions): IContainer;
129
+
130
+ /** Resolves every provider marked with `autoResolve()`. */
131
+ autoResolve(options?: AutoResolveOptions): this;
132
+
133
+ /** Direct child scopes that are still alive. */
134
+ getScopes(): IContainer[];
135
+
136
+ /** Detaches a child scope; called by the child on dispose. */
137
+ removeScope(child: IContainer): void;
138
+
139
+ /** Applies a module to this scope. */
140
+ useModule(module: IContainerModule): this;
141
+
142
+ /** The parent scope, if any. */
143
+ getParent(): IContainer | undefined;
144
+
145
+ /** Instances constructed in this scope, and in child scopes when `cascade` is true. */
146
+ getInstances(cascade?: boolean): Instance[];
147
+
148
+ /** Whether this scope tracks `instance`. */
149
+ hasInstance(instance: Instance): boolean;
150
+
151
+ /** Raises `scopeDisposed`, drops providers and instances, and detaches from the parent. Child scopes are not disposed. */
152
+ dispose(): void;
153
+
154
+ /** Tracks an instance in this scope; called by the injector. */
155
+ addInstance(instance: Instance): void;
156
+
157
+ /** The injector shared by this scope tree, e.g. to subscribe to `onConstructed`. */
158
+ getInjector(): IInjector;
159
+ }
@@ -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;
@@ -0,0 +1,22 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Thrown when `singleton()` is applied twice to one provider. Code `IOC_SINGLETON_APPLIED_TWICE`. */
4
+ export class CannotApplySingletonTwiceError extends ContainerError {
5
+ name = 'CannotApplySingletonTwiceError';
6
+ readonly code = 'IOC_SINGLETON_APPLIED_TWICE';
7
+
8
+ constructor(message?: string) {
9
+ super(message);
10
+
11
+ Object.setPrototypeOf(this, CannotApplySingletonTwiceError.prototype);
12
+ }
13
+
14
+ /**
15
+ * @throws {CannotApplySingletonTwiceError} when `isTrue` is falsy.
16
+ */
17
+ static assert(isTrue: boolean, failMessage: string) {
18
+ if (!isTrue) {
19
+ throw new CannotApplySingletonTwiceError(failMessage);
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,13 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Thrown when a disposed scope is used. Code `IOC_CONTAINER_DISPOSED`. */
4
+ export class ContainerDisposedError extends ContainerError {
5
+ name = 'ContainerDisposedError';
6
+ readonly code = 'IOC_CONTAINER_DISPOSED';
7
+
8
+ constructor(message: string) {
9
+ super(message);
10
+
11
+ Object.setPrototypeOf(this, ContainerDisposedError.prototype);
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Base class of every error the container throws. `code` is stable across
3
+ * versions (unlike `message`), so match on it — or on the subclass — rather
4
+ * than on the text. The codes are listed in the package's AGENTS.md.
5
+ */
6
+ export abstract class ContainerError extends Error {
7
+ name = 'ContainerError';
8
+ readonly code: string = 'IOC_CONTAINER_ERROR';
9
+
10
+ protected constructor(message?: string) {
11
+ super(message);
12
+
13
+ Object.setPrototypeOf(this, ContainerError.prototype);
14
+ }
15
+ }
@@ -0,0 +1,13 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Signals that no container is associated with a target. Code `IOC_CONTAINER_NOT_FOUND`. */
4
+ export class ContainerNotFoundError extends ContainerError {
5
+ name = 'ContainerNotFoundError';
6
+ readonly code = 'IOC_CONTAINER_NOT_FOUND';
7
+
8
+ constructor(message: string) {
9
+ super(message);
10
+
11
+ Object.setPrototypeOf(this, ContainerNotFoundError.prototype);
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Thrown when a registration has no binding key. Code `IOC_DEPENDENCY_MISSING_KEY`. */
4
+ export class DependencyMissingKeyError extends ContainerError {
5
+ name = 'DependencyMissingKeyError';
6
+ readonly code = 'IOC_DEPENDENCY_MISSING_KEY';
7
+
8
+ constructor(message: string) {
9
+ super(message);
10
+
11
+ Object.setPrototypeOf(this, DependencyMissingKeyError.prototype);
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Thrown when a key or alias is not found in the scope or any parent. Code `IOC_DEPENDENCY_NOT_FOUND`. */
4
+ export class DependencyNotFoundError extends ContainerError {
5
+ name = 'DependencyNotFoundError';
6
+ readonly code = 'IOC_DEPENDENCY_NOT_FOUND';
7
+
8
+ constructor(message: string) {
9
+ super(message);
10
+
11
+ Object.setPrototypeOf(this, DependencyNotFoundError.prototype);
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Thrown by a method the object does not support; the message names the class and method. Code `IOC_METHOD_NOT_IMPLEMENTED`. */
4
+ export class MethodNotImplementedError extends ContainerError {
5
+ name = 'MethodNotImplementedError';
6
+ readonly code = 'IOC_METHOD_NOT_IMPLEMENTED';
7
+
8
+ constructor(message?: string) {
9
+ super(message);
10
+
11
+ Object.setPrototypeOf(this, MethodNotImplementedError.prototype);
12
+ }
13
+ }
@@ -0,0 +1,22 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Thrown when a provider of a disposed scope is used. Code `IOC_PROVIDER_DISPOSED`. */
4
+ export class ProviderDisposedError extends ContainerError {
5
+ name = 'ProviderDisposedError';
6
+ readonly code = 'IOC_PROVIDER_DISPOSED';
7
+
8
+ /**
9
+ * @throws {ProviderDisposedError} when `isTrue` is falsy.
10
+ */
11
+ static assert(isTrue: boolean, failMessage: string) {
12
+ if (!isTrue) {
13
+ throw new ProviderDisposedError(failMessage);
14
+ }
15
+ }
16
+
17
+ constructor(message: string) {
18
+ super(message);
19
+
20
+ Object.setPrototypeOf(this, ProviderDisposedError.prototype);
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Thrown when subscribing to an event of a disposed scope. Code `IOC_EVENT_DISPOSED`. */
4
+ export class TypedEventDisposedError extends ContainerError {
5
+ name = 'TypedEventDisposedError';
6
+ readonly code = 'IOC_EVENT_DISPOSED';
7
+
8
+ /**
9
+ * @throws {TypedEventDisposedError} when `isTrue` is falsy.
10
+ */
11
+ static assert(isTrue: boolean, failMessage: string) {
12
+ if (!isTrue) {
13
+ throw new TypedEventDisposedError(failMessage);
14
+ }
15
+ }
16
+
17
+ constructor(message: string) {
18
+ super(message);
19
+
20
+ Object.setPrototypeOf(this, TypedEventDisposedError.prototype);
21
+ }
22
+ }
@@ -0,0 +1,13 @@
1
+ import { ContainerError } from './ContainerError';
2
+
3
+ /** Thrown by `toToken` for a value that is not a token, key, class or function. Code `IOC_UNSUPPORTED_TOKEN_TYPE`. */
4
+ export class UnsupportedTokenTypeError extends ContainerError {
5
+ name = 'UnsupportedTokenTypeError';
6
+ readonly code = 'IOC_UNSUPPORTED_TOKEN_TYPE';
7
+
8
+ constructor(message: string) {
9
+ super(message);
10
+
11
+ Object.setPrototypeOf(this, UnsupportedTokenTypeError.prototype);
12
+ }
13
+ }
@@ -0,0 +1,44 @@
1
+ import { SingleToken } from '../token/SingleToken';
2
+
3
+ /**
4
+ * What a flag is evaluated for - usually the current user. Shaped after the
5
+ * Unleash context, so an Unleash client accepts it as is.
6
+ */
7
+ export type FeatureContext = {
8
+ userId?: string;
9
+ sessionId?: string;
10
+ remoteAddress?: string;
11
+ environment?: string;
12
+ properties?: Record<string, string | number | undefined>;
13
+ [key: string]: unknown;
14
+ };
15
+
16
+ /** A variant served for a flag. Unleash reports `{ name: 'disabled', enabled: false }` for a flag that is off. */
17
+ export type FeatureVariant = { name: string; enabled: boolean };
18
+
19
+ /**
20
+ * The flag client feature tokens ask: `getVariant` for a
21
+ * `MultiVariantFeatureToken`, `isEnabled` for a `ToggleFeatureToken`.
22
+ * Synchronous, because resolution is. An Unleash client satisfies it
23
+ * structurally - register the instance itself; an asynchronous client
24
+ * (OpenFeature server SDK) needs an adapter which evaluates ahead of time and
25
+ * answers from that snapshot.
26
+ *
27
+ * @example
28
+ * container.addRegistration(Registration.fromValue(unleash).bindTo(IFeatureFlagsToken));
29
+ */
30
+ export interface IFeatureFlags {
31
+ isEnabled(name: string, context?: FeatureContext): boolean;
32
+
33
+ getVariant(name: string, context?: FeatureContext): FeatureVariant;
34
+ }
35
+
36
+ /** Where feature tokens find the flag client. */
37
+ export const IFeatureFlagsToken = new SingleToken<IFeatureFlags>('IFeatureFlags');
38
+
39
+ /**
40
+ * Where feature tokens find the context to evaluate a flag for. Register it
41
+ * in the scope that knows the user (e.g. `request`); register `{}` for flags
42
+ * that do not depend on one.
43
+ */
44
+ export const IFeatureContextToken = new SingleToken<FeatureContext>('IFeatureContext');
@@ -0,0 +1,109 @@
1
+ import type { ExecutionContext } from '../ExecutionContext';
2
+ import { resolveConstructor } from '../metadata/target';
3
+ import { type Task } from '../utils/task';
4
+ import { type Instance } from '../utils/basic';
5
+ import { memoize } from '../utils/memoize';
6
+ import { getHooks, hasHooks, type HookFn, toHookFn } from './hook';
7
+ import { createHookExecutionContext, type CreateHookExecutionContext, type IHookContext } from './HookContext';
8
+
9
+ /** Transforms each hook context before it is handed to its hook. */
10
+ export type MapHookExecutionContext = (context: IHookContext) => IHookContext;
11
+
12
+ /**
13
+ * One decorated member's hook, resolved to a function and bound to the context
14
+ * it runs against — the model the library hands back, and the whole of what it
15
+ * says about a hook. The member it came from is `context.methodName`.
16
+ * Performing it is the caller's business (ADR 0016).
17
+ */
18
+ export type HookAction = {
19
+ hook: HookFn;
20
+ context: IHookContext;
21
+ };
22
+
23
+ /**
24
+ * Shapes what is collected: how each action's hook context is built, and which
25
+ * members take part. A collector carries defaults for all three, so a call
26
+ * names only what it overrides.
27
+ */
28
+ export type HookCollectorOptions = {
29
+ createExecutionContext?: CreateHookExecutionContext;
30
+ mapExecutionContext?: MapHookExecutionContext;
31
+ predicate?: (methodName: string) => boolean;
32
+ };
33
+
34
+ /** What one collection runs in: the scope, plus any per-call overrides of the collector's options. */
35
+ export type HookCollectionContext = ExecutionContext & HookCollectorOptions;
36
+
37
+ /** Options of `new HookCollector(...)`. */
38
+ export type HookCollectorProps = HookCollectorOptions & {
39
+ /** The hook key this collector reads: `onConstruct`, `onScopeDisposed`, `onResolved`, or a custom one. */
40
+ key: string | symbol;
41
+ };
42
+
43
+ /**
44
+ * Reads the hooks declared under one key off a target and hands them back as
45
+ * {@link HookAction}s, in declaration order. It runs nothing: what order the
46
+ * actions are performed in, what is awaited and where a failure goes are
47
+ * answered by the caller (ADR 0016).
48
+ *
49
+ * Wire it to whichever event should collect — the injector's `onConstructed`,
50
+ * the scope's `scopeDisposed`, a provider's `onResolved` — the library ships no
51
+ * module which does that for you (ADR 0018).
52
+ *
53
+ * ```typescript
54
+ * const collector = new HookCollector({ key: 'onStart' });
55
+ *
56
+ * container.getInjector().onConstructed((instance, scope) => {
57
+ * runInOrder(collector.getActions(instance, { scope }).map(toTask))?.catch(report);
58
+ * });
59
+ * ```
60
+ */
61
+ export class HookCollector {
62
+ private readonly key: string | symbol;
63
+ private readonly options: Required<HookCollectorOptions>;
64
+ // Hook metadata is fixed once a class is defined - decorators have all run by the time
65
+ // there is an instance to read them off - so the merge runs once per class and key.
66
+ private readonly hooksOf = memoize(getHooks);
67
+
68
+ constructor({
69
+ key,
70
+ createExecutionContext = createHookExecutionContext,
71
+ mapExecutionContext = (context) => context,
72
+ predicate = () => true,
73
+ }: HookCollectorProps) {
74
+ this.key = key;
75
+ this.options = { createExecutionContext, mapExecutionContext, predicate };
76
+ }
77
+
78
+ hasHooks(target: Instance): boolean {
79
+ return hasHooks(target, this.key);
80
+ }
81
+
82
+ /** The actions `target` declares under this collector's key — empty when it declares none. */
83
+ getActions(
84
+ target: Instance,
85
+ {
86
+ scope,
87
+ createExecutionContext = this.options.createExecutionContext,
88
+ mapExecutionContext = this.options.mapExecutionContext,
89
+ predicate = this.options.predicate,
90
+ }: HookCollectionContext,
91
+ ): HookAction[] {
92
+ const actions: HookAction[] = [];
93
+ for (const [methodName, fn] of this.hooksOf(resolveConstructor(target), this.key)) {
94
+ if (predicate(methodName)) {
95
+ actions.push({
96
+ hook: toHookFn(fn),
97
+ context: mapExecutionContext(createExecutionContext(target, scope, methodName)),
98
+ });
99
+ }
100
+ }
101
+ return actions;
102
+ }
103
+ }
104
+
105
+ /** The {@link Task} an action runs as: `runInOrder(actions.map(toTask))`, `runAtOnce(actions.map(toTask))`. */
106
+ export const toTask =
107
+ ({ hook, context }: HookAction): Task =>
108
+ () =>
109
+ hook(context);
@@ -0,0 +1,85 @@
1
+ import type { IContainer } from '../container/IContainer';
2
+
3
+ import { type Instance } from '../utils/basic';
4
+ import { resolveArgs } from '../injector/MetadataInjector';
5
+ import { type InjectFn } from './hook';
6
+
7
+ /**
8
+ * What a hook receives: the `instance`, the `scope` and the decorated member's
9
+ * name. `invokeMethod({ args: resolveArgs() })` calls the member with its
10
+ * `@inject` parameters; `setProperty(fn)` assigns it (see `injectProp`).
11
+ */
12
+ export interface IHookContext {
13
+ instance: Instance;
14
+ scope: IContainer;
15
+ methodName?: string;
16
+
17
+ resolveArgs(...args: unknown[]): unknown[];
18
+
19
+ invokeMethod(options?: { args?: unknown[] }): unknown;
20
+
21
+ setProperty(fn: InjectFn): void;
22
+
23
+ getProperty(): unknown;
24
+
25
+ setInitialArgs(...args: unknown[]): this;
26
+
27
+ getInitialArgs(): unknown[];
28
+ }
29
+
30
+ /** The default {@link IHookContext}. */
31
+ export class HookContext implements IHookContext {
32
+ private initialArgs: unknown[] = [];
33
+
34
+ constructor(
35
+ readonly instance: Instance,
36
+ readonly scope: IContainer,
37
+ readonly methodName?: string,
38
+ ) {}
39
+
40
+ resolveArgs(...args: unknown[]): unknown[] {
41
+ return resolveArgs(
42
+ this.instance,
43
+ this.methodName,
44
+ )({
45
+ scope: this.scope,
46
+ args: [...this.initialArgs, ...args],
47
+ });
48
+ }
49
+
50
+ invokeMethod({ args = this.resolveArgs() }: { args?: unknown[] } = {}): unknown {
51
+ // @ts-ignore
52
+ return this.instance[this.methodName](...args);
53
+ }
54
+
55
+ setProperty(fn: InjectFn): void {
56
+ // @ts-ignore
57
+ this.instance[this.methodName] = fn({ scope: this.scope });
58
+ }
59
+
60
+ getProperty(): unknown {
61
+ // @ts-ignore
62
+ return this.instance[this.methodName];
63
+ }
64
+
65
+ setInitialArgs(...args: unknown[]): this {
66
+ this.initialArgs = args;
67
+ return this;
68
+ }
69
+
70
+ getInitialArgs(): unknown[] {
71
+ return this.initialArgs;
72
+ }
73
+ }
74
+
75
+ /** Builds the context a hook receives. */
76
+ export type CreateHookExecutionContext = (Target: Instance, scope: IContainer, methodName?: string) => IHookContext;
77
+ /** The default {@link CreateHookExecutionContext}: a {@link HookContext}. */
78
+ export const createHookExecutionContext: CreateHookExecutionContext = (Target, scope, methodName = 'constructor') =>
79
+ new HookContext(Target, scope, methodName);
80
+
81
+ /** A {@link CreateHookExecutionContext} whose contexts start with `args` as initial args. */
82
+ export const createHookContextFactory =
83
+ ({ args = [] }: { args?: unknown[] } = {}): CreateHookExecutionContext =>
84
+ (Target, scope, methodName) =>
85
+ createHookExecutionContext(Target, scope, methodName).setInitialArgs(...args);
@@ -0,0 +1,51 @@
1
+ import type { IContainer } from '../container/IContainer';
2
+ import { runAtOnce, runInOrder } from '../utils/task';
3
+ import { type HookFn, type HookType, toHookFn } from './hook';
4
+
5
+ /**
6
+ * A provider hook narrowed to the dependencies that can carry hook metadata.
7
+ */
8
+ export type ResolvedObjectHook = (dependency: object, scope: IContainer) => void;
9
+
10
+ /**
11
+ * Combines hooks into one which runs them in declaration order, staying
12
+ * synchronous until one returns a promise and awaiting the rest from that point.
13
+ *
14
+ * A member carries a single hook, so this is how several are declared together:
15
+ * `@hook('onConstruct', sequential(validate, persist))`.
16
+ */
17
+ export const sequential = (...hooks: HookType[]): HookFn => {
18
+ const fns = hooks.map(toHookFn);
19
+ return (context) => runInOrder(fns.map((fn) => () => fn(context)));
20
+ };
21
+
22
+ /**
23
+ * Combines hooks into one which starts them all at once. Settles when every
24
+ * hook has; a fully synchronous run returns nothing, like {@link sequential}.
25
+ *
26
+ * Use it for hooks of one member which do not depend on each other:
27
+ * `@hook('onScopeDisposed', parallel(flush, closeSocket))`.
28
+ */
29
+ export const parallel = (...hooks: HookType[]): HookFn => {
30
+ const fns = hooks.map(toHookFn);
31
+ return (context) => runAtOnce(fns.map((fn) => () => fn(context)));
32
+ };
33
+
34
+ /**
35
+ * Wraps a hook so it runs a single time per instance, however often the event
36
+ * it is declared under fires. Composes with the combinators above —
37
+ * `oncePerInstance(sequential(connect, warmUp))` runs the whole sequence once.
38
+ */
39
+ export const oncePerInstance = (execute: HookType): HookFn => {
40
+ const invokedInstances = new WeakSet<object>();
41
+ const fn = toHookFn(execute);
42
+
43
+ return (context) => {
44
+ if (invokedInstances.has(context.instance)) {
45
+ return;
46
+ }
47
+
48
+ invokedInstances.add(context.instance);
49
+ return fn(context);
50
+ };
51
+ };