1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.Injector = void 0;
|
4 | const constants_1 = require("@nestjs/common/constants");
|
5 | const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
|
6 | const iterare_1 = require("iterare");
|
7 | const runtime_exception_1 = require("../errors/exceptions/runtime.exception");
|
8 | const undefined_dependency_exception_1 = require("../errors/exceptions/undefined-dependency.exception");
|
9 | const unknown_dependencies_exception_1 = require("../errors/exceptions/unknown-dependencies.exception");
|
10 | const constants_2 = require("./constants");
|
11 | const inquirer_1 = require("./inquirer");
|
12 | const instance_wrapper_1 = require("./instance-wrapper");
|
13 | class Injector {
|
14 | loadPrototype({ token }, collection, contextId = constants_2.STATIC_CONTEXT) {
|
15 | if (!collection) {
|
16 | return;
|
17 | }
|
18 | const target = collection.get(token);
|
19 | const instance = target.createPrototype(contextId);
|
20 | if (instance) {
|
21 | const wrapper = new instance_wrapper_1.InstanceWrapper(Object.assign(Object.assign({}, target), { instance }));
|
22 | collection.set(token, wrapper);
|
23 | }
|
24 | }
|
25 | async loadInstance(wrapper, collection, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer) {
|
26 | const inquirerId = this.getInquirerId(inquirer);
|
27 | const instanceHost = wrapper.getInstanceByContextId(contextId, inquirerId);
|
28 | if (instanceHost.isPending) {
|
29 | return instanceHost.donePromise;
|
30 | }
|
31 | const done = this.applyDoneHook(instanceHost);
|
32 | const token = wrapper.token || wrapper.name;
|
33 | const { inject } = wrapper;
|
34 | const targetWrapper = collection.get(token);
|
35 | if (shared_utils_1.isUndefined(targetWrapper)) {
|
36 | throw new runtime_exception_1.RuntimeException();
|
37 | }
|
38 | if (instanceHost.isResolved) {
|
39 | return done();
|
40 | }
|
41 | const callback = async (instances) => {
|
42 | const properties = await this.resolveProperties(wrapper, moduleRef, inject, contextId, wrapper, inquirer);
|
43 | const instance = await this.instantiateClass(instances, wrapper, targetWrapper, contextId, inquirer);
|
44 | this.applyProperties(instance, properties);
|
45 | done();
|
46 | };
|
47 | await this.resolveConstructorParams(wrapper, moduleRef, inject, callback, contextId, wrapper, inquirer);
|
48 | }
|
49 | async loadMiddleware(wrapper, collection, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer) {
|
50 | const { metatype, token } = wrapper;
|
51 | const targetWrapper = collection.get(token);
|
52 | if (!shared_utils_1.isUndefined(targetWrapper.instance)) {
|
53 | return;
|
54 | }
|
55 | targetWrapper.instance = Object.create(metatype.prototype);
|
56 | await this.loadInstance(wrapper, collection, moduleRef, contextId, inquirer || wrapper);
|
57 | }
|
58 | async loadController(wrapper, moduleRef, contextId = constants_2.STATIC_CONTEXT) {
|
59 | const controllers = moduleRef.controllers;
|
60 | await this.loadInstance(wrapper, controllers, moduleRef, contextId, wrapper);
|
61 | await this.loadEnhancersPerContext(wrapper, contextId, wrapper);
|
62 | }
|
63 | async loadInjectable(wrapper, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer) {
|
64 | const injectables = moduleRef.injectables;
|
65 | await this.loadInstance(wrapper, injectables, moduleRef, contextId, inquirer);
|
66 | }
|
67 | async loadProvider(wrapper, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer) {
|
68 | const providers = moduleRef.providers;
|
69 | await this.loadInstance(wrapper, providers, moduleRef, contextId, inquirer);
|
70 | await this.loadEnhancersPerContext(wrapper, contextId, wrapper);
|
71 | }
|
72 | applyDoneHook(wrapper) {
|
73 | let done;
|
74 | wrapper.donePromise = new Promise((resolve, reject) => {
|
75 | done = resolve;
|
76 | });
|
77 | wrapper.isPending = true;
|
78 | return done;
|
79 | }
|
80 | async resolveConstructorParams(wrapper, moduleRef, inject, callback, contextId = constants_2.STATIC_CONTEXT, inquirer, parentInquirer) {
|
81 | const inquirerId = this.getInquirerId(inquirer);
|
82 | const metadata = wrapper.getCtorMetadata();
|
83 | if (metadata && contextId !== constants_2.STATIC_CONTEXT) {
|
84 | const deps = await this.loadCtorMetadata(metadata, contextId, inquirer, parentInquirer);
|
85 | return callback(deps);
|
86 | }
|
87 | const dependencies = shared_utils_1.isNil(inject)
|
88 | ? this.reflectConstructorParams(wrapper.metatype)
|
89 | : inject;
|
90 | const optionalDependenciesIds = shared_utils_1.isNil(inject)
|
91 | ? this.reflectOptionalParams(wrapper.metatype)
|
92 | : [];
|
93 | let isResolved = true;
|
94 | const resolveParam = async (param, index) => {
|
95 | try {
|
96 | if (this.isInquirer(param, parentInquirer)) {
|
97 | return parentInquirer && parentInquirer.instance;
|
98 | }
|
99 | const paramWrapper = await this.resolveSingleParam(wrapper, param, { index, dependencies }, moduleRef, contextId, inquirer, index);
|
100 | const instanceHost = paramWrapper.getInstanceByContextId(contextId, inquirerId);
|
101 | if (!instanceHost.isResolved && !paramWrapper.forwardRef) {
|
102 | isResolved = false;
|
103 | }
|
104 | return instanceHost === null || instanceHost === void 0 ? void 0 : instanceHost.instance;
|
105 | }
|
106 | catch (err) {
|
107 | const isOptional = optionalDependenciesIds.includes(index);
|
108 | if (!isOptional) {
|
109 | throw err;
|
110 | }
|
111 | return undefined;
|
112 | }
|
113 | };
|
114 | const instances = await Promise.all(dependencies.map(resolveParam));
|
115 | isResolved && (await callback(instances));
|
116 | }
|
117 | reflectConstructorParams(type) {
|
118 | const paramtypes = Reflect.getMetadata(constants_1.PARAMTYPES_METADATA, type) || [];
|
119 | const selfParams = this.reflectSelfParams(type);
|
120 | selfParams.forEach(({ index, param }) => (paramtypes[index] = param));
|
121 | return paramtypes;
|
122 | }
|
123 | reflectOptionalParams(type) {
|
124 | return Reflect.getMetadata(constants_1.OPTIONAL_DEPS_METADATA, type) || [];
|
125 | }
|
126 | reflectSelfParams(type) {
|
127 | return Reflect.getMetadata(constants_1.SELF_DECLARED_DEPS_METADATA, type) || [];
|
128 | }
|
129 | async resolveSingleParam(wrapper, param, dependencyContext, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex) {
|
130 | if (shared_utils_1.isUndefined(param)) {
|
131 | throw new undefined_dependency_exception_1.UndefinedDependencyException(wrapper.name, dependencyContext, moduleRef);
|
132 | }
|
133 | const token = this.resolveParamToken(wrapper, param);
|
134 | return this.resolveComponentInstance(moduleRef, token, dependencyContext, wrapper, contextId, inquirer, keyOrIndex);
|
135 | }
|
136 | resolveParamToken(wrapper, param) {
|
137 | if (!param.forwardRef) {
|
138 | return param;
|
139 | }
|
140 | wrapper.forwardRef = true;
|
141 | return param.forwardRef();
|
142 | }
|
143 | async resolveComponentInstance(moduleRef, token, dependencyContext, wrapper, contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex) {
|
144 | const providers = moduleRef.providers;
|
145 | const instanceWrapper = await this.lookupComponent(providers, moduleRef, Object.assign(Object.assign({}, dependencyContext), { name: token }), wrapper, contextId, inquirer, keyOrIndex);
|
146 | return this.resolveComponentHost(moduleRef, instanceWrapper, contextId, inquirer);
|
147 | }
|
148 | async resolveComponentHost(moduleRef, instanceWrapper, contextId = constants_2.STATIC_CONTEXT, inquirer) {
|
149 | const inquirerId = this.getInquirerId(inquirer);
|
150 | const instanceHost = instanceWrapper.getInstanceByContextId(contextId, inquirerId);
|
151 | if (!instanceHost.isResolved && !instanceWrapper.forwardRef) {
|
152 | await this.loadProvider(instanceWrapper, moduleRef, contextId, inquirer);
|
153 | }
|
154 | else if (!instanceHost.isResolved &&
|
155 | instanceWrapper.forwardRef &&
|
156 | (contextId !== constants_2.STATIC_CONTEXT || !!inquirerId)) {
|
157 | |
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | instanceHost.donePromise &&
|
165 | instanceHost.donePromise.then(() => this.loadProvider(instanceWrapper, moduleRef, contextId, inquirer));
|
166 | }
|
167 | if (instanceWrapper.async) {
|
168 | const host = instanceWrapper.getInstanceByContextId(contextId, inquirerId);
|
169 | host.instance = await host.instance;
|
170 | instanceWrapper.setInstanceByContextId(contextId, host, inquirerId);
|
171 | }
|
172 | return instanceWrapper;
|
173 | }
|
174 | async lookupComponent(providers, moduleRef, dependencyContext, wrapper, contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex) {
|
175 | const { name } = dependencyContext;
|
176 | if (wrapper && wrapper.name === name) {
|
177 | throw new unknown_dependencies_exception_1.UnknownDependenciesException(wrapper.name, dependencyContext, moduleRef);
|
178 | }
|
179 | if (providers.has(name)) {
|
180 | const instanceWrapper = providers.get(name);
|
181 | this.addDependencyMetadata(keyOrIndex, wrapper, instanceWrapper);
|
182 | return instanceWrapper;
|
183 | }
|
184 | return this.lookupComponentInParentModules(dependencyContext, moduleRef, wrapper, contextId, inquirer, keyOrIndex);
|
185 | }
|
186 | async lookupComponentInParentModules(dependencyContext, moduleRef, wrapper, contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex) {
|
187 | const instanceWrapper = await this.lookupComponentInImports(moduleRef, dependencyContext.name, wrapper, [], contextId, inquirer, keyOrIndex);
|
188 | if (shared_utils_1.isNil(instanceWrapper)) {
|
189 | throw new unknown_dependencies_exception_1.UnknownDependenciesException(wrapper.name, dependencyContext, moduleRef);
|
190 | }
|
191 | return instanceWrapper;
|
192 | }
|
193 | async lookupComponentInImports(moduleRef, name, wrapper, moduleRegistry = [], contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex, isTraversing) {
|
194 | let instanceWrapperRef = null;
|
195 | const imports = moduleRef.imports || new Set();
|
196 | const identity = (item) => item;
|
197 | let children = [...imports.values()].filter(identity);
|
198 | if (isTraversing) {
|
199 | const contextModuleExports = moduleRef.exports;
|
200 | children = children.filter(child => contextModuleExports.has(child.metatype));
|
201 | }
|
202 | for (const relatedModule of children) {
|
203 | if (moduleRegistry.includes(relatedModule.id)) {
|
204 | continue;
|
205 | }
|
206 | moduleRegistry.push(relatedModule.id);
|
207 | const { providers, exports } = relatedModule;
|
208 | if (!exports.has(name) || !providers.has(name)) {
|
209 | const instanceRef = await this.lookupComponentInImports(relatedModule, name, wrapper, moduleRegistry, contextId, inquirer, keyOrIndex, true);
|
210 | if (instanceRef) {
|
211 | this.addDependencyMetadata(keyOrIndex, wrapper, instanceRef);
|
212 | return instanceRef;
|
213 | }
|
214 | continue;
|
215 | }
|
216 | instanceWrapperRef = providers.get(name);
|
217 | this.addDependencyMetadata(keyOrIndex, wrapper, instanceWrapperRef);
|
218 | const inquirerId = this.getInquirerId(inquirer);
|
219 | const instanceHost = instanceWrapperRef.getInstanceByContextId(contextId, inquirerId);
|
220 | if (!instanceHost.isResolved && !instanceWrapperRef.forwardRef) {
|
221 | await this.loadProvider(instanceWrapperRef, relatedModule, contextId, wrapper);
|
222 | break;
|
223 | }
|
224 | }
|
225 | return instanceWrapperRef;
|
226 | }
|
227 | async resolveProperties(wrapper, moduleRef, inject, contextId = constants_2.STATIC_CONTEXT, inquirer, parentInquirer) {
|
228 | if (!shared_utils_1.isNil(inject)) {
|
229 | return [];
|
230 | }
|
231 | const metadata = wrapper.getPropertiesMetadata();
|
232 | if (metadata && contextId !== constants_2.STATIC_CONTEXT) {
|
233 | return this.loadPropertiesMetadata(metadata, contextId, inquirer);
|
234 | }
|
235 | const properties = this.reflectProperties(wrapper.metatype);
|
236 | const instances = await Promise.all(properties.map(async (item) => {
|
237 | try {
|
238 | const dependencyContext = {
|
239 | key: item.key,
|
240 | name: item.name,
|
241 | };
|
242 | if (this.isInquirer(item.name, parentInquirer)) {
|
243 | return parentInquirer && parentInquirer.instance;
|
244 | }
|
245 | const paramWrapper = await this.resolveSingleParam(wrapper, item.name, dependencyContext, moduleRef, contextId, inquirer, item.key);
|
246 | if (!paramWrapper) {
|
247 | return undefined;
|
248 | }
|
249 | const inquirerId = this.getInquirerId(inquirer);
|
250 | const instanceHost = paramWrapper.getInstanceByContextId(contextId, inquirerId);
|
251 | return instanceHost.instance;
|
252 | }
|
253 | catch (err) {
|
254 | if (!item.isOptional) {
|
255 | throw err;
|
256 | }
|
257 | return undefined;
|
258 | }
|
259 | }));
|
260 | return properties.map((item, index) => (Object.assign(Object.assign({}, item), { instance: instances[index] })));
|
261 | }
|
262 | reflectProperties(type) {
|
263 | const properties = Reflect.getMetadata(constants_1.PROPERTY_DEPS_METADATA, type) || [];
|
264 | const optionalKeys = Reflect.getMetadata(constants_1.OPTIONAL_PROPERTY_DEPS_METADATA, type) || [];
|
265 | return properties.map((item) => (Object.assign(Object.assign({}, item), { name: item.type, isOptional: optionalKeys.includes(item.key) })));
|
266 | }
|
267 | applyProperties(instance, properties) {
|
268 | if (!shared_utils_1.isObject(instance)) {
|
269 | return undefined;
|
270 | }
|
271 | iterare_1.iterate(properties)
|
272 | .filter(item => !shared_utils_1.isNil(item.instance))
|
273 | .forEach(item => (instance[item.key] = item.instance));
|
274 | }
|
275 | async instantiateClass(instances, wrapper, targetMetatype, contextId = constants_2.STATIC_CONTEXT, inquirer) {
|
276 | const { metatype, inject } = wrapper;
|
277 | const inquirerId = this.getInquirerId(inquirer);
|
278 | const instanceHost = targetMetatype.getInstanceByContextId(contextId, inquirerId);
|
279 | const isStatic = wrapper.isStatic(contextId, inquirer);
|
280 | const isInRequestScope = wrapper.isInRequestScope(contextId, inquirer);
|
281 | const isLazyTransient = wrapper.isLazyTransient(contextId, inquirer);
|
282 | const isExplicitlyRequested = wrapper.isExplicitlyRequested(contextId, inquirer);
|
283 | const isInContext = isStatic || isInRequestScope || isLazyTransient || isExplicitlyRequested;
|
284 | if (shared_utils_1.isNil(inject) && isInContext) {
|
285 | instanceHost.instance = wrapper.forwardRef
|
286 | ? Object.assign(instanceHost.instance, new metatype(...instances))
|
287 | : new metatype(...instances);
|
288 | }
|
289 | else if (isInContext) {
|
290 | const factoryReturnValue = targetMetatype.metatype(...instances);
|
291 | instanceHost.instance = await factoryReturnValue;
|
292 | }
|
293 | instanceHost.isResolved = true;
|
294 | return instanceHost.instance;
|
295 | }
|
296 | async loadPerContext(instance, moduleRef, collection, ctx, wrapper) {
|
297 | if (!wrapper) {
|
298 | const injectionToken = instance.constructor;
|
299 | wrapper = collection.get(injectionToken);
|
300 | }
|
301 | await this.loadInstance(wrapper, collection, moduleRef, ctx, wrapper);
|
302 | await this.loadEnhancersPerContext(wrapper, ctx, wrapper);
|
303 | const host = wrapper.getInstanceByContextId(ctx, wrapper.id);
|
304 | return host && host.instance;
|
305 | }
|
306 | async loadEnhancersPerContext(wrapper, ctx, inquirer) {
|
307 | const enhancers = wrapper.getEnhancersMetadata() || [];
|
308 | const loadEnhancer = (item) => {
|
309 | const hostModule = item.host;
|
310 | return this.loadInstance(item, hostModule.injectables, hostModule, ctx, inquirer);
|
311 | };
|
312 | await Promise.all(enhancers.map(loadEnhancer));
|
313 | }
|
314 | async loadCtorMetadata(metadata, contextId, inquirer, parentInquirer) {
|
315 | const hosts = await Promise.all(metadata.map(async (item) => this.resolveScopedComponentHost(item, contextId, inquirer, parentInquirer)));
|
316 | const inquirerId = this.getInquirerId(inquirer);
|
317 | return hosts.map(item => item.getInstanceByContextId(contextId, inquirerId).instance);
|
318 | }
|
319 | async loadPropertiesMetadata(metadata, contextId, inquirer) {
|
320 | const dependenciesHosts = await Promise.all(metadata.map(async ({ wrapper: item, key }) => ({
|
321 | key,
|
322 | host: await this.resolveComponentHost(item.host, item, contextId, inquirer),
|
323 | })));
|
324 | const inquirerId = this.getInquirerId(inquirer);
|
325 | return dependenciesHosts.map(({ key, host }) => ({
|
326 | key,
|
327 | name: key,
|
328 | instance: host.getInstanceByContextId(contextId, inquirerId).instance,
|
329 | }));
|
330 | }
|
331 | getInquirerId(inquirer) {
|
332 | return inquirer && inquirer.id;
|
333 | }
|
334 | resolveScopedComponentHost(item, contextId, inquirer, parentInquirer) {
|
335 | return this.isInquirerRequest(item, parentInquirer)
|
336 | ? parentInquirer
|
337 | : this.resolveComponentHost(item.host, item, contextId, inquirer);
|
338 | }
|
339 | isInquirerRequest(item, parentInquirer) {
|
340 | return item.isTransient && item.name === inquirer_1.INQUIRER && parentInquirer;
|
341 | }
|
342 | isInquirer(param, parentInquirer) {
|
343 | return param === inquirer_1.INQUIRER && parentInquirer;
|
344 | }
|
345 | addDependencyMetadata(keyOrIndex, hostWrapper, instanceWrapper) {
|
346 | shared_utils_1.isString(keyOrIndex)
|
347 | ? hostWrapper.addPropertiesMetadata(keyOrIndex, instanceWrapper)
|
348 | : hostWrapper.addCtorMetadata(keyOrIndex, instanceWrapper);
|
349 | }
|
350 | }
|
351 | exports.Injector = Injector;
|