@martel/calyx 1.11.0 → 1.13.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/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/src/cache/cache.interceptor.ts +4 -2
- package/src/cache/decorators.ts +4 -0
- package/src/cache/index.ts +1 -0
- package/src/cli/index.ts +7 -1
- package/src/config/config.module.ts +16 -2
- package/src/config/config.service.ts +20 -6
- package/src/core/container.ts +559 -140
- package/src/core/index.ts +2 -0
- package/src/core/lazy-module-loader.ts +29 -0
- package/src/core/metadata.ts +6 -1
- package/src/core/testing-module.ts +123 -0
- package/src/cqrs/cqrs.ts +264 -0
- package/src/database/sequelize.module.ts +239 -0
- package/src/event-emitter/decorators.ts +2 -2
- package/src/event-emitter/event-emitter.ts +3 -0
- package/src/graphql/decorators.ts +16 -0
- package/src/graphql/graphql.module.ts +16 -0
- package/src/http/application.ts +261 -21
- package/src/http/decorators.ts +25 -1
- package/src/http/exceptions.ts +97 -0
- package/src/http/factory.ts +3 -0
- package/src/http/router.ts +27 -4
- package/src/index.ts +3 -0
- package/src/microservices/clients.module.ts +47 -0
- package/src/microservices/exceptions.ts +10 -0
- package/src/microservices/index.ts +2 -0
- package/src/microservices/microservice.ts +1 -1
- package/src/queue/queue.module.ts +73 -5
- package/src/schedule/decorators.ts +10 -6
- package/src/schedule/index.ts +1 -0
- package/src/schedule/schedule.module.ts +3 -2
- package/src/schedule/scheduler-registry.ts +50 -0
- package/src/security/index.ts +1 -0
- package/src/security/throttler.module.ts +108 -0
- package/src/terminus/terminus.ts +134 -0
- package/src/validation/compiler.ts +133 -10
- package/src/validation/decorators.ts +164 -2
- package/src/validation/http-pipes.ts +128 -0
- package/src/validation/index.ts +1 -0
- package/src/websockets/decorators.ts +12 -2
- package/src/websockets/exceptions.ts +10 -0
- package/src/websockets/index.ts +1 -0
- package/tests/circular-di.test.ts +151 -0
- package/tests/di.test.ts +10 -2
- package/tests/nestjs-parity.test.ts +527 -0
package/src/core/container.ts
CHANGED
|
@@ -47,14 +47,14 @@ class ContainerModuleRef extends ModuleRef {
|
|
|
47
47
|
const requestContext = contextId instanceof Map ? contextId : new Map<any, any>();
|
|
48
48
|
const strict = options?.strict ?? true;
|
|
49
49
|
if (strict) {
|
|
50
|
-
return this.container.
|
|
50
|
+
return await this.container.resolveTokenInModuleContextAsync(this.moduleClass, token, requestContext);
|
|
51
51
|
} else {
|
|
52
|
-
return this.container.
|
|
52
|
+
return await this.container.resolveTokenGloballyAsync(token, requestContext);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
async create<T>(type: Type<T>): Promise<T> {
|
|
57
|
-
return this.container.
|
|
57
|
+
return await this.container.instantiateClassAsync(type, this.moduleClass);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -65,6 +65,7 @@ export class CalyxContainer {
|
|
|
65
65
|
private providerScopes = new Map<InjectionToken, Scope>();
|
|
66
66
|
private controllerScopes = new Map<any, Scope>();
|
|
67
67
|
private compiledControllerFactories = new Map<any, (requestContext: Map<any, any>) => any>();
|
|
68
|
+
private deferredProxies: { moduleClass: any; token: any; proxyTarget: { instance: any } }[] = [];
|
|
68
69
|
|
|
69
70
|
getModuleRecord(moduleClass: any): ModuleRecord | undefined {
|
|
70
71
|
return this.modules.get(resolveForwardRef(moduleClass));
|
|
@@ -124,29 +125,9 @@ export class CalyxContainer {
|
|
|
124
125
|
this.addModule(actualModule);
|
|
125
126
|
record.imports.add(actualModule);
|
|
126
127
|
|
|
127
|
-
// If it is dynamic module, merge its extra
|
|
128
|
+
// If it is dynamic module, merge its extra metadata recursively
|
|
128
129
|
if (isDynamicModule(resolvedImp)) {
|
|
129
|
-
|
|
130
|
-
if (resolvedImp.providers) {
|
|
131
|
-
for (const prov of resolvedImp.providers) {
|
|
132
|
-
const token = this.getProviderToken(prov);
|
|
133
|
-
dynamicRecord.providers.set(token, prov);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (resolvedImp.controllers) {
|
|
137
|
-
for (const ctrl of resolvedImp.controllers) {
|
|
138
|
-
dynamicRecord.controllers.add(ctrl);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (resolvedImp.exports) {
|
|
142
|
-
for (const exp of resolvedImp.exports) {
|
|
143
|
-
const resolvedExp = resolveForwardRef(exp);
|
|
144
|
-
dynamicRecord.exports.add(resolvedExp);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (resolvedImp.global) {
|
|
148
|
-
this.globalModules.add(actualModule);
|
|
149
|
-
}
|
|
130
|
+
this.mergeDynamicModuleMetadata(resolvedImp);
|
|
150
131
|
}
|
|
151
132
|
}
|
|
152
133
|
|
|
@@ -171,6 +152,45 @@ export class CalyxContainer {
|
|
|
171
152
|
}
|
|
172
153
|
}
|
|
173
154
|
|
|
155
|
+
private mergeDynamicModuleMetadata(dynModule: DynamicModule) {
|
|
156
|
+
const actualModule = dynModule.module;
|
|
157
|
+
const record = this.modules.get(actualModule)!;
|
|
158
|
+
|
|
159
|
+
if (dynModule.imports) {
|
|
160
|
+
for (const imp of dynModule.imports) {
|
|
161
|
+
const resolvedImp = resolveForwardRef(imp);
|
|
162
|
+
const actualImp = isDynamicModule(resolvedImp) ? resolvedImp.module : resolvedImp;
|
|
163
|
+
|
|
164
|
+
this.addModule(actualImp);
|
|
165
|
+
record.imports.add(actualImp);
|
|
166
|
+
|
|
167
|
+
if (isDynamicModule(resolvedImp)) {
|
|
168
|
+
this.mergeDynamicModuleMetadata(resolvedImp);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (dynModule.providers) {
|
|
173
|
+
for (const prov of dynModule.providers) {
|
|
174
|
+
const token = this.getProviderToken(prov);
|
|
175
|
+
record.providers.set(token, prov);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (dynModule.controllers) {
|
|
179
|
+
for (const ctrl of dynModule.controllers) {
|
|
180
|
+
record.controllers.add(ctrl);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (dynModule.exports) {
|
|
184
|
+
for (const exp of dynModule.exports) {
|
|
185
|
+
const resolvedExp = resolveForwardRef(exp);
|
|
186
|
+
record.exports.add(resolvedExp);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (dynModule.global) {
|
|
190
|
+
this.globalModules.add(actualModule);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
174
194
|
private getProviderToken(provider: Provider): InjectionToken {
|
|
175
195
|
if (typeof provider === 'function') {
|
|
176
196
|
return provider;
|
|
@@ -178,19 +198,82 @@ export class CalyxContainer {
|
|
|
178
198
|
return provider.provide;
|
|
179
199
|
}
|
|
180
200
|
|
|
201
|
+
private resolveDeferredProxies() {
|
|
202
|
+
for (const dp of this.deferredProxies) {
|
|
203
|
+
if (!dp.proxyTarget.instance) {
|
|
204
|
+
const record = this.modules.get(dp.moduleClass);
|
|
205
|
+
if (record && record.instances.has(dp.token)) {
|
|
206
|
+
dp.proxyTarget.instance = record.instances.get(dp.token);
|
|
207
|
+
} else {
|
|
208
|
+
try {
|
|
209
|
+
dp.proxyTarget.instance = this.getGlobalOrAnyInstance(dp.token);
|
|
210
|
+
} catch {
|
|
211
|
+
// ignore
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
181
218
|
resolveTokenInModuleContext<T>(moduleClass: any, token: InjectionToken, requestContext?: Map<any, any>): T {
|
|
219
|
+
const resolvedToken = resolveForwardRef(token);
|
|
182
220
|
// 1. Check circular dependency
|
|
183
221
|
const isResolving = this.resolvingStack.some(
|
|
184
|
-
(item) => item.moduleClass === moduleClass && item.token ===
|
|
222
|
+
(item) => item.moduleClass === moduleClass && item.token === resolvedToken
|
|
185
223
|
);
|
|
186
224
|
if (isResolving) {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
225
|
+
const container = this;
|
|
226
|
+
const proxyTarget = { instance: null as any };
|
|
227
|
+
this.deferredProxies.push({
|
|
228
|
+
moduleClass,
|
|
229
|
+
token: resolvedToken,
|
|
230
|
+
proxyTarget,
|
|
231
|
+
});
|
|
232
|
+
return new Proxy(proxyTarget, {
|
|
233
|
+
get(target, prop) {
|
|
234
|
+
if (prop === '__isCalyxProxy') return true;
|
|
235
|
+
if (prop === '__proxyTarget') return target;
|
|
236
|
+
if (!target.instance) {
|
|
237
|
+
const record = container.modules.get(moduleClass);
|
|
238
|
+
if (record && record.instances.has(resolvedToken)) {
|
|
239
|
+
target.instance = record.instances.get(resolvedToken);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (!target.instance) {
|
|
243
|
+
try {
|
|
244
|
+
target.instance = container.getGlobalOrAnyInstance(resolvedToken);
|
|
245
|
+
} catch {
|
|
246
|
+
// ignore
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (!target.instance) {
|
|
250
|
+
if (typeof prop === 'symbol' || prop === 'then' || prop === 'toJSON') {
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
throw new Error(`Calyx circular proxy DI error: Accessing property "${String(prop)}" on circular dependency ${resolvedToken.name || resolvedToken} before it is instantiated.`);
|
|
254
|
+
}
|
|
255
|
+
const value = Reflect.get(target.instance, prop);
|
|
256
|
+
if (typeof value === 'function') {
|
|
257
|
+
return value.bind(target.instance);
|
|
258
|
+
}
|
|
259
|
+
return value;
|
|
260
|
+
},
|
|
261
|
+
set(target, prop, value) {
|
|
262
|
+
if (!target.instance) {
|
|
263
|
+
const record = container.modules.get(moduleClass);
|
|
264
|
+
if (record && record.instances.has(resolvedToken)) {
|
|
265
|
+
target.instance = record.instances.get(resolvedToken);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (!target.instance) {
|
|
269
|
+
throw new Error(`Calyx circular proxy DI error: Setting property "${String(prop)}" on circular dependency ${resolvedToken.name || resolvedToken} before it is instantiated.`);
|
|
270
|
+
}
|
|
271
|
+
return Reflect.set(target.instance, prop, value);
|
|
272
|
+
}
|
|
273
|
+
}) as any;
|
|
191
274
|
}
|
|
192
275
|
|
|
193
|
-
this.resolvingStack.push({ moduleClass, token });
|
|
276
|
+
this.resolvingStack.push({ moduleClass, token: resolvedToken });
|
|
194
277
|
|
|
195
278
|
try {
|
|
196
279
|
const record = this.modules.get(moduleClass);
|
|
@@ -199,14 +282,14 @@ export class CalyxContainer {
|
|
|
199
282
|
}
|
|
200
283
|
|
|
201
284
|
// Special resolution cases
|
|
202
|
-
if (
|
|
285
|
+
if (resolvedToken === REQUEST) {
|
|
203
286
|
if (!requestContext || !requestContext.has(REQUEST)) {
|
|
204
287
|
throw new Error(`calyx DI: REQUEST token resolved outside of a request context`);
|
|
205
288
|
}
|
|
206
289
|
return requestContext.get(REQUEST);
|
|
207
290
|
}
|
|
208
291
|
|
|
209
|
-
if (
|
|
292
|
+
if (resolvedToken === ModuleRef) {
|
|
210
293
|
const instance = record.instances.get(ModuleRef);
|
|
211
294
|
if (instance) return instance;
|
|
212
295
|
const moduleRefInstance = new ContainerModuleRef(this, moduleClass);
|
|
@@ -214,16 +297,17 @@ export class CalyxContainer {
|
|
|
214
297
|
return moduleRefInstance as any;
|
|
215
298
|
}
|
|
216
299
|
|
|
217
|
-
if (
|
|
300
|
+
if (resolvedToken === moduleClass) {
|
|
218
301
|
const instance = record.instances.get(moduleClass);
|
|
219
302
|
if (instance) return instance;
|
|
220
303
|
const instantiatedModule = this.instantiateClass(moduleClass, moduleClass, requestContext);
|
|
221
304
|
record.instances.set(moduleClass, instantiatedModule);
|
|
305
|
+
this.resolveDeferredProxies();
|
|
222
306
|
return instantiatedModule;
|
|
223
307
|
}
|
|
224
308
|
|
|
225
309
|
// Find where the token is defined
|
|
226
|
-
const resolution = this.findProviderDefinition(moduleClass,
|
|
310
|
+
const resolution = this.findProviderDefinition(moduleClass, resolvedToken);
|
|
227
311
|
if (!resolution) {
|
|
228
312
|
// Not found. Check if the token is optional
|
|
229
313
|
const currentResolving = this.resolvingStack[this.resolvingStack.length - 2];
|
|
@@ -238,7 +322,7 @@ export class CalyxContainer {
|
|
|
238
322
|
for (let i = 0; i < paramTypes.length; i++) {
|
|
239
323
|
const pToken = injectTokens.get(i) ?? paramTypes[i];
|
|
240
324
|
const resolvedPToken = resolveForwardRef(pToken);
|
|
241
|
-
if (resolvedPToken ===
|
|
325
|
+
if (resolvedPToken === resolvedToken && optionalParams.has(i)) {
|
|
242
326
|
isOptional = true;
|
|
243
327
|
break;
|
|
244
328
|
}
|
|
@@ -248,23 +332,24 @@ export class CalyxContainer {
|
|
|
248
332
|
}
|
|
249
333
|
}
|
|
250
334
|
}
|
|
251
|
-
throw new Error(`calyx DI: Cannot resolve dependency "${String(
|
|
335
|
+
throw new Error(`calyx DI: Cannot resolve dependency "${String(resolvedToken.name ?? resolvedToken)}" in module ${moduleClass.name || moduleClass}`);
|
|
252
336
|
}
|
|
253
337
|
|
|
254
338
|
const { targetModuleClass, provider } = resolution;
|
|
255
339
|
const targetRecord = this.modules.get(targetModuleClass)!;
|
|
256
340
|
|
|
257
|
-
const scope = this.providerScopes.get(
|
|
341
|
+
const scope = this.providerScopes.get(resolvedToken) ?? Scope.DEFAULT;
|
|
258
342
|
|
|
259
343
|
if (scope === Scope.REQUEST) {
|
|
260
344
|
if (!requestContext) {
|
|
261
|
-
throw new Error(`calyx DI: Cannot resolve request-scoped provider "${String(
|
|
345
|
+
throw new Error(`calyx DI: Cannot resolve request-scoped provider "${String(resolvedToken.name ?? resolvedToken)}" without request context.`);
|
|
262
346
|
}
|
|
263
|
-
if (requestContext.has(
|
|
264
|
-
return requestContext.get(
|
|
347
|
+
if (requestContext.has(resolvedToken)) {
|
|
348
|
+
return requestContext.get(resolvedToken);
|
|
265
349
|
}
|
|
266
350
|
const instance = this.createInstanceFromProvider(provider, targetModuleClass, requestContext);
|
|
267
|
-
requestContext.set(
|
|
351
|
+
requestContext.set(resolvedToken, instance);
|
|
352
|
+
this.resolveDeferredProxies();
|
|
268
353
|
return instance;
|
|
269
354
|
}
|
|
270
355
|
|
|
@@ -273,12 +358,13 @@ export class CalyxContainer {
|
|
|
273
358
|
}
|
|
274
359
|
|
|
275
360
|
// Check if instance already exists in target module (Singleton)
|
|
276
|
-
if (targetRecord.instances.has(
|
|
277
|
-
return targetRecord.instances.get(
|
|
361
|
+
if (targetRecord.instances.has(resolvedToken)) {
|
|
362
|
+
return targetRecord.instances.get(resolvedToken);
|
|
278
363
|
}
|
|
279
364
|
|
|
280
365
|
const instance = this.createInstanceFromProvider(provider, targetModuleClass, requestContext);
|
|
281
|
-
targetRecord.instances.set(
|
|
366
|
+
targetRecord.instances.set(resolvedToken, instance);
|
|
367
|
+
this.resolveDeferredProxies();
|
|
282
368
|
return instance;
|
|
283
369
|
} finally {
|
|
284
370
|
this.resolvingStack.pop();
|
|
@@ -288,6 +374,8 @@ export class CalyxContainer {
|
|
|
288
374
|
private createInstanceFromProvider(provider: Provider, targetModuleClass: any, requestContext?: Map<any, any>): any {
|
|
289
375
|
if (typeof provider !== 'function' && 'useValue' in provider) {
|
|
290
376
|
return provider.useValue;
|
|
377
|
+
} else if (typeof provider !== 'function' && 'useExisting' in provider) {
|
|
378
|
+
return this.resolveTokenInModuleContext(targetModuleClass, resolveForwardRef(provider.useExisting), requestContext);
|
|
291
379
|
} else if (typeof provider !== 'function' && 'useClass' in provider) {
|
|
292
380
|
return this.instantiateClass(provider.useClass, targetModuleClass, requestContext);
|
|
293
381
|
} else if (typeof provider !== 'function' && 'useFactory' in provider) {
|
|
@@ -400,7 +488,7 @@ export class CalyxContainer {
|
|
|
400
488
|
const propertyInjects: Map<string | symbol, InjectionToken> =
|
|
401
489
|
Reflect.getOwnMetadata(METADATA_KEYS.PROPERTY_INJECTS, Class) || new Map();
|
|
402
490
|
for (const [propertyKey, token] of propertyInjects.entries()) {
|
|
403
|
-
instance[propertyKey] = this.resolveTokenInModuleContext(moduleClass, token, requestContext);
|
|
491
|
+
instance[propertyKey] = this.resolveTokenInModuleContext(moduleClass, resolveForwardRef(token), requestContext);
|
|
404
492
|
}
|
|
405
493
|
|
|
406
494
|
return instance;
|
|
@@ -415,13 +503,31 @@ export class CalyxContainer {
|
|
|
415
503
|
throw new Error(`calyx DI: Instance of "${String(token.name ?? token)}" not found in any module context`);
|
|
416
504
|
}
|
|
417
505
|
|
|
418
|
-
bootstrap(rootModule: any) {
|
|
506
|
+
bootstrap(rootModule: any): void | Promise<void> {
|
|
419
507
|
this.addModule(rootModule);
|
|
420
|
-
|
|
421
|
-
// Compute scopes of all providers and controllers (including bubble-up)
|
|
422
508
|
this.resolveProviderAndControllerScopes();
|
|
423
509
|
|
|
424
|
-
|
|
510
|
+
let hasAsync = false;
|
|
511
|
+
for (const record of this.modules.values()) {
|
|
512
|
+
for (const provider of record.providers.values()) {
|
|
513
|
+
if (typeof provider !== 'function' && 'useFactory' in provider) {
|
|
514
|
+
if (provider.useFactory.constructor.name === 'AsyncFunction') {
|
|
515
|
+
hasAsync = true;
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (hasAsync) break;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (hasAsync) {
|
|
524
|
+
return this.bootstrapAsync(rootModule);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
this.bootstrapSync(rootModule);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private bootstrapSync(rootModule: any) {
|
|
425
531
|
for (const [moduleClass, record] of this.modules.entries()) {
|
|
426
532
|
for (const token of record.providers.keys()) {
|
|
427
533
|
const scope = this.providerScopes.get(token) ?? Scope.DEFAULT;
|
|
@@ -431,7 +537,6 @@ export class CalyxContainer {
|
|
|
431
537
|
}
|
|
432
538
|
}
|
|
433
539
|
|
|
434
|
-
// Instantiate all singleton controllers
|
|
435
540
|
for (const [moduleClass, record] of this.modules.entries()) {
|
|
436
541
|
for (const controllerClass of record.controllers) {
|
|
437
542
|
const scope = this.controllerScopes.get(controllerClass) ?? Scope.DEFAULT;
|
|
@@ -441,14 +546,46 @@ export class CalyxContainer {
|
|
|
441
546
|
}
|
|
442
547
|
}
|
|
443
548
|
|
|
444
|
-
// Instantiate module classes themselves
|
|
445
549
|
for (const [moduleClass, record] of this.modules.entries()) {
|
|
446
550
|
if (record.instances.get(moduleClass) === null) {
|
|
447
551
|
const instance = this.instantiateClass(moduleClass, moduleClass);
|
|
448
552
|
record.instances.set(moduleClass, instance);
|
|
553
|
+
this.resolveDeferredProxies();
|
|
449
554
|
}
|
|
450
555
|
}
|
|
451
556
|
|
|
557
|
+
this.resolveDeferredProxies();
|
|
558
|
+
this.compileControllerFactories();
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private async bootstrapAsync(rootModule: any) {
|
|
562
|
+
for (const [moduleClass, record] of this.modules.entries()) {
|
|
563
|
+
for (const token of record.providers.keys()) {
|
|
564
|
+
const scope = this.providerScopes.get(token) ?? Scope.DEFAULT;
|
|
565
|
+
if (scope === Scope.DEFAULT) {
|
|
566
|
+
await this.resolveTokenInModuleContextAsync(moduleClass, token);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
for (const [moduleClass, record] of this.modules.entries()) {
|
|
572
|
+
for (const controllerClass of record.controllers) {
|
|
573
|
+
const scope = this.controllerScopes.get(controllerClass) ?? Scope.DEFAULT;
|
|
574
|
+
if (scope === Scope.DEFAULT) {
|
|
575
|
+
await this.resolveControllerAsync(moduleClass, controllerClass);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
for (const [moduleClass, record] of this.modules.entries()) {
|
|
581
|
+
if (record.instances.get(moduleClass) === null) {
|
|
582
|
+
const instance = await this.instantiateClassAsync(moduleClass, moduleClass);
|
|
583
|
+
record.instances.set(moduleClass, instance);
|
|
584
|
+
this.resolveDeferredProxies();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
this.resolveDeferredProxies();
|
|
452
589
|
this.compileControllerFactories();
|
|
453
590
|
}
|
|
454
591
|
|
|
@@ -459,6 +596,18 @@ export class CalyxContainer {
|
|
|
459
596
|
}
|
|
460
597
|
const instance = this.instantiateClass(controllerClass, moduleClass);
|
|
461
598
|
record.instances.set(controllerClass, instance);
|
|
599
|
+
this.resolveDeferredProxies();
|
|
600
|
+
return instance;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
private async resolveControllerAsync(moduleClass: any, controllerClass: any): Promise<any> {
|
|
604
|
+
const record = this.modules.get(moduleClass)!;
|
|
605
|
+
if (record.instances.has(controllerClass)) {
|
|
606
|
+
return record.instances.get(controllerClass);
|
|
607
|
+
}
|
|
608
|
+
const instance = await this.instantiateClassAsync(controllerClass, moduleClass);
|
|
609
|
+
record.instances.set(controllerClass, instance);
|
|
610
|
+
this.resolveDeferredProxies();
|
|
462
611
|
return instance;
|
|
463
612
|
}
|
|
464
613
|
|
|
@@ -475,6 +624,229 @@ export class CalyxContainer {
|
|
|
475
624
|
return this.getGlobalOrAnyInstance(token);
|
|
476
625
|
}
|
|
477
626
|
|
|
627
|
+
async resolveTokenGloballyAsync<T>(token: InjectionToken, requestContext?: Map<any, any>): Promise<T> {
|
|
628
|
+
const scope = this.providerScopes.get(token) ?? Scope.DEFAULT;
|
|
629
|
+
if (scope === Scope.REQUEST && requestContext?.has(token)) {
|
|
630
|
+
return requestContext.get(token);
|
|
631
|
+
}
|
|
632
|
+
for (const [moduleClass, record] of this.modules.entries()) {
|
|
633
|
+
if (record.providers.has(token)) {
|
|
634
|
+
return await this.resolveTokenInModuleContextAsync(moduleClass, token, requestContext);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return this.getGlobalOrAnyInstance(token);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
async resolveTokenInModuleContextAsync<T>(moduleClass: any, token: InjectionToken, requestContext?: Map<any, any>): Promise<T> {
|
|
641
|
+
const resolvedToken = resolveForwardRef(token);
|
|
642
|
+
const isResolving = this.resolvingStack.some(
|
|
643
|
+
(item) => item.moduleClass === moduleClass && item.token === resolvedToken
|
|
644
|
+
);
|
|
645
|
+
if (isResolving) {
|
|
646
|
+
const container = this;
|
|
647
|
+
const proxyTarget = { instance: null as any };
|
|
648
|
+
this.deferredProxies.push({
|
|
649
|
+
moduleClass,
|
|
650
|
+
token: resolvedToken,
|
|
651
|
+
proxyTarget,
|
|
652
|
+
});
|
|
653
|
+
return new Proxy(proxyTarget, {
|
|
654
|
+
get(target, prop) {
|
|
655
|
+
if (prop === '__isCalyxProxy') return true;
|
|
656
|
+
if (prop === '__proxyTarget') return target;
|
|
657
|
+
if (!target.instance) {
|
|
658
|
+
const record = container.modules.get(moduleClass);
|
|
659
|
+
if (record && record.instances.has(resolvedToken)) {
|
|
660
|
+
target.instance = record.instances.get(resolvedToken);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (!target.instance) {
|
|
664
|
+
try {
|
|
665
|
+
target.instance = container.getGlobalOrAnyInstance(resolvedToken);
|
|
666
|
+
} catch {
|
|
667
|
+
// ignore
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (!target.instance) {
|
|
671
|
+
if (typeof prop === 'symbol' || prop === 'then' || prop === 'toJSON') {
|
|
672
|
+
return undefined;
|
|
673
|
+
}
|
|
674
|
+
throw new Error(`Calyx circular proxy DI error: Accessing property "${String(prop)}" on circular dependency ${resolvedToken.name || resolvedToken} before it is instantiated.`);
|
|
675
|
+
}
|
|
676
|
+
const value = Reflect.get(target.instance, prop);
|
|
677
|
+
if (typeof value === 'function') {
|
|
678
|
+
return value.bind(target.instance);
|
|
679
|
+
}
|
|
680
|
+
return value;
|
|
681
|
+
},
|
|
682
|
+
set(target, prop, value) {
|
|
683
|
+
if (!target.instance) {
|
|
684
|
+
const record = container.modules.get(moduleClass);
|
|
685
|
+
if (record && record.instances.has(resolvedToken)) {
|
|
686
|
+
target.instance = record.instances.get(resolvedToken);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if (!target.instance) {
|
|
690
|
+
throw new Error(`Calyx circular proxy DI error: Setting property "${String(prop)}" on circular dependency ${resolvedToken.name || resolvedToken} before it is instantiated.`);
|
|
691
|
+
}
|
|
692
|
+
return Reflect.set(target.instance, prop, value);
|
|
693
|
+
}
|
|
694
|
+
}) as any;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
this.resolvingStack.push({ moduleClass, token: resolvedToken });
|
|
698
|
+
|
|
699
|
+
try {
|
|
700
|
+
const record = this.modules.get(moduleClass);
|
|
701
|
+
if (!record) {
|
|
702
|
+
throw new Error(`Module ${moduleClass.name || moduleClass} is not registered in the container`);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (resolvedToken === REQUEST) {
|
|
706
|
+
if (!requestContext || !requestContext.has(REQUEST)) {
|
|
707
|
+
throw new Error(`calyx DI: REQUEST token resolved outside of a request context`);
|
|
708
|
+
}
|
|
709
|
+
return requestContext.get(REQUEST);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (resolvedToken === ModuleRef) {
|
|
713
|
+
const instance = record.instances.get(ModuleRef);
|
|
714
|
+
if (instance) return instance;
|
|
715
|
+
const moduleRefInstance = new ContainerModuleRef(this, moduleClass);
|
|
716
|
+
record.instances.set(ModuleRef, moduleRefInstance);
|
|
717
|
+
return moduleRefInstance as any;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (resolvedToken === moduleClass) {
|
|
721
|
+
const instance = record.instances.get(moduleClass);
|
|
722
|
+
if (instance) return instance;
|
|
723
|
+
const instantiatedModule = await this.instantiateClassAsync(moduleClass, moduleClass, requestContext);
|
|
724
|
+
record.instances.set(moduleClass, instantiatedModule);
|
|
725
|
+
this.resolveDeferredProxies();
|
|
726
|
+
return instantiatedModule;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const resolution = this.findProviderDefinition(moduleClass, resolvedToken);
|
|
730
|
+
if (!resolution) {
|
|
731
|
+
const currentResolving = this.resolvingStack[this.resolvingStack.length - 2];
|
|
732
|
+
if (currentResolving) {
|
|
733
|
+
const parentClass = currentResolving.token;
|
|
734
|
+
if (typeof parentClass === 'function') {
|
|
735
|
+
const optionalParams: Set<number> = Reflect.getMetadata(METADATA_KEYS.OPTIONAL_PARAMS, parentClass) || new Set();
|
|
736
|
+
const injectTokens: Map<number, InjectionToken> = Reflect.getMetadata(METADATA_KEYS.INJECT_TOKENS, parentClass) || new Map();
|
|
737
|
+
const paramTypes = Reflect.getMetadata('design:paramtypes', parentClass) || [];
|
|
738
|
+
|
|
739
|
+
let isOptional = false;
|
|
740
|
+
for (let i = 0; i < paramTypes.length; i++) {
|
|
741
|
+
const pToken = injectTokens.get(i) ?? paramTypes[i];
|
|
742
|
+
const resolvedPToken = resolveForwardRef(pToken);
|
|
743
|
+
if (resolvedPToken === resolvedToken && optionalParams.has(i)) {
|
|
744
|
+
isOptional = true;
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (isOptional) {
|
|
749
|
+
return undefined as any;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
throw new Error(`calyx DI: Cannot resolve dependency "${String(resolvedToken.name ?? resolvedToken)}" in module ${moduleClass.name || moduleClass}`);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
const { targetModuleClass, provider } = resolution;
|
|
757
|
+
const targetRecord = this.modules.get(targetModuleClass)!;
|
|
758
|
+
|
|
759
|
+
const scope = this.providerScopes.get(resolvedToken) ?? Scope.DEFAULT;
|
|
760
|
+
|
|
761
|
+
if (scope === Scope.REQUEST) {
|
|
762
|
+
if (!requestContext) {
|
|
763
|
+
throw new Error(`calyx DI: Cannot resolve request-scoped provider "${String(resolvedToken.name ?? resolvedToken)}" without request context.`);
|
|
764
|
+
}
|
|
765
|
+
if (requestContext.has(resolvedToken)) {
|
|
766
|
+
return requestContext.get(resolvedToken);
|
|
767
|
+
}
|
|
768
|
+
const instance = await this.createInstanceFromProviderAsync(provider, targetModuleClass, requestContext);
|
|
769
|
+
requestContext.set(resolvedToken, instance);
|
|
770
|
+
this.resolveDeferredProxies();
|
|
771
|
+
return instance;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (scope === Scope.TRANSIENT) {
|
|
775
|
+
return await this.createInstanceFromProviderAsync(provider, targetModuleClass, requestContext);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (targetRecord.instances.has(resolvedToken)) {
|
|
779
|
+
return targetRecord.instances.get(resolvedToken);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const instance = await this.createInstanceFromProviderAsync(provider, targetModuleClass, requestContext);
|
|
783
|
+
targetRecord.instances.set(resolvedToken, instance);
|
|
784
|
+
this.resolveDeferredProxies();
|
|
785
|
+
return instance;
|
|
786
|
+
} finally {
|
|
787
|
+
this.resolvingStack.pop();
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
private async createInstanceFromProviderAsync(provider: Provider, targetModuleClass: any, requestContext?: Map<any, any>): Promise<any> {
|
|
792
|
+
if (typeof provider !== 'function' && 'useValue' in provider) {
|
|
793
|
+
return provider.useValue;
|
|
794
|
+
} else if (typeof provider !== 'function' && 'useExisting' in provider) {
|
|
795
|
+
return await this.resolveTokenInModuleContextAsync(targetModuleClass, resolveForwardRef(provider.useExisting), requestContext);
|
|
796
|
+
} else if (typeof provider !== 'function' && 'useClass' in provider) {
|
|
797
|
+
return await this.instantiateClassAsync(provider.useClass, targetModuleClass, requestContext);
|
|
798
|
+
} else if (typeof provider !== 'function' && 'useFactory' in provider) {
|
|
799
|
+
const injectTokens = provider.inject || [];
|
|
800
|
+
const args = await Promise.all(
|
|
801
|
+
injectTokens.map((t) => this.resolveTokenInModuleContextAsync(targetModuleClass, resolveForwardRef(t), requestContext))
|
|
802
|
+
);
|
|
803
|
+
const res = provider.useFactory(...args);
|
|
804
|
+
return res instanceof Promise ? await res : res;
|
|
805
|
+
} else {
|
|
806
|
+
return await this.instantiateClassAsync(provider as Type<any>, targetModuleClass, requestContext);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
public async instantiateClassAsync(Class: Type<any>, moduleClass: any, requestContext?: Map<any, any>): Promise<any> {
|
|
811
|
+
if (
|
|
812
|
+
!Reflect.hasMetadata(METADATA_KEYS.INJECTABLE, Class) &&
|
|
813
|
+
!Reflect.hasMetadata(METADATA_KEYS.CONTROLLER, Class) &&
|
|
814
|
+
!Reflect.hasMetadata(METADATA_KEYS.MODULE, Class)
|
|
815
|
+
) {
|
|
816
|
+
throw new Error(`Class ${Class.name} is missing @Injectable(), @Controller() or @Module() decorator`);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const paramTypes: any[] = Reflect.getMetadata('design:paramtypes', Class) || [];
|
|
820
|
+
const injectTokens: Map<number, InjectionToken> = Reflect.getMetadata(METADATA_KEYS.INJECT_TOKENS, Class) || new Map();
|
|
821
|
+
const optionalParams: Set<number> = Reflect.getMetadata(METADATA_KEYS.OPTIONAL_PARAMS, Class) || new Set();
|
|
822
|
+
|
|
823
|
+
const args = await Promise.all(
|
|
824
|
+
paramTypes.map(async (paramType, i) => {
|
|
825
|
+
const token = injectTokens.get(i) ?? paramType;
|
|
826
|
+
const resolvedToken = resolveForwardRef(token);
|
|
827
|
+
|
|
828
|
+
try {
|
|
829
|
+
return await this.resolveTokenInModuleContextAsync(moduleClass, resolvedToken, requestContext);
|
|
830
|
+
} catch (err) {
|
|
831
|
+
if (optionalParams.has(i)) {
|
|
832
|
+
return undefined;
|
|
833
|
+
}
|
|
834
|
+
throw err;
|
|
835
|
+
}
|
|
836
|
+
})
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
const instance = new Class(...args);
|
|
840
|
+
|
|
841
|
+
const propertyInjects: Map<string | symbol, InjectionToken> =
|
|
842
|
+
Reflect.getOwnMetadata(METADATA_KEYS.PROPERTY_INJECTS, Class) || new Map();
|
|
843
|
+
for (const [propertyKey, token] of propertyInjects.entries()) {
|
|
844
|
+
instance[propertyKey] = await this.resolveTokenInModuleContextAsync(moduleClass, resolveForwardRef(token), requestContext);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
return instance;
|
|
848
|
+
}
|
|
849
|
+
|
|
478
850
|
resolveControllerInRequestContext(moduleClass: any, controllerClass: any, requestContext: Map<any, any>): any {
|
|
479
851
|
const scope = this.controllerScopes.get(controllerClass) ?? Scope.DEFAULT;
|
|
480
852
|
if (scope === Scope.REQUEST) {
|
|
@@ -634,123 +1006,170 @@ export class CalyxContainer {
|
|
|
634
1006
|
return idx;
|
|
635
1007
|
};
|
|
636
1008
|
|
|
1009
|
+
const getLazyRequestProxy = (token: any, requestContext: Map<any, any>) => {
|
|
1010
|
+
const proxyTarget = { instance: null as any };
|
|
1011
|
+
return new Proxy(proxyTarget, {
|
|
1012
|
+
get(target, prop) {
|
|
1013
|
+
if (prop === '__isCalyxProxy') return true;
|
|
1014
|
+
if (prop === '__proxyTarget') return target;
|
|
1015
|
+
if (!target.instance) {
|
|
1016
|
+
target.instance = requestContext.get(token);
|
|
1017
|
+
}
|
|
1018
|
+
if (!target.instance) {
|
|
1019
|
+
if (typeof prop === 'symbol' || prop === 'then' || prop === 'toJSON') {
|
|
1020
|
+
return undefined;
|
|
1021
|
+
}
|
|
1022
|
+
throw new Error(`Calyx circular request proxy error: Accessing property "${String(prop)}" before it is instantiated.`);
|
|
1023
|
+
}
|
|
1024
|
+
const val = Reflect.get(target.instance, prop);
|
|
1025
|
+
if (typeof val === 'function') {
|
|
1026
|
+
return val.bind(target.instance);
|
|
1027
|
+
}
|
|
1028
|
+
return val;
|
|
1029
|
+
},
|
|
1030
|
+
set(target, prop, value) {
|
|
1031
|
+
if (!target.instance) {
|
|
1032
|
+
target.instance = requestContext.get(token);
|
|
1033
|
+
}
|
|
1034
|
+
if (!target.instance) {
|
|
1035
|
+
throw new Error(`Calyx circular request proxy error: Setting property "${String(prop)}" before it is instantiated.`);
|
|
1036
|
+
}
|
|
1037
|
+
return Reflect.set(target.instance, prop, value);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
const lazyProxyHelperIdx = getClosureIdx(getLazyRequestProxy);
|
|
1043
|
+
|
|
637
1044
|
const instantiations: string[] = [];
|
|
638
1045
|
const compiledTokens = new Set<string>();
|
|
1046
|
+
const compilingTokens = new Set<string>();
|
|
639
1047
|
|
|
640
1048
|
const compileToken = (token: any, currentModuleClass: any): string => {
|
|
641
|
-
|
|
1049
|
+
const resolvedToken = resolveForwardRef(token);
|
|
1050
|
+
if (resolvedToken === REQUEST) {
|
|
642
1051
|
return `requestContext.get(c[${getClosureIdx(REQUEST)}])`;
|
|
643
1052
|
}
|
|
644
|
-
if (
|
|
1053
|
+
if (resolvedToken === ModuleRef) {
|
|
645
1054
|
const record = this.modules.get(currentModuleClass)!;
|
|
646
1055
|
return `c[${getClosureIdx(record.instances.get(ModuleRef))}]`;
|
|
647
1056
|
}
|
|
648
1057
|
|
|
649
|
-
const scope = this.providerScopes.get(
|
|
1058
|
+
const scope = this.providerScopes.get(resolvedToken) ?? Scope.DEFAULT;
|
|
650
1059
|
if (scope === Scope.DEFAULT) {
|
|
651
|
-
const instance = this.resolveTokenInModuleContext(currentModuleClass,
|
|
1060
|
+
const instance = this.resolveTokenInModuleContext(currentModuleClass, resolvedToken);
|
|
652
1061
|
return `c[${getClosureIdx(instance)}]`;
|
|
653
1062
|
}
|
|
654
1063
|
|
|
655
|
-
const tokenKey = `${currentModuleClass.name || currentModuleClass}::${String(
|
|
1064
|
+
const tokenKey = `${currentModuleClass.name || currentModuleClass}::${String(resolvedToken.name || resolvedToken)}`;
|
|
656
1065
|
if (compiledTokens.has(tokenKey)) {
|
|
657
|
-
return `requestContext.get(c[${getClosureIdx(
|
|
1066
|
+
return `requestContext.get(c[${getClosureIdx(resolvedToken)}])`;
|
|
658
1067
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
if (!resolution) {
|
|
662
|
-
const optional = this.isTokenOptional(token, currentModuleClass);
|
|
663
|
-
return optional ? `undefined` : `(() => { throw new Error('calyx JIT DI: Cannot resolve dependency ' + String(c[${getClosureIdx(token)}])); })()`;
|
|
1068
|
+
if (compilingTokens.has(tokenKey)) {
|
|
1069
|
+
return `c[${lazyProxyHelperIdx}](c[${getClosureIdx(resolvedToken)}], requestContext)`;
|
|
664
1070
|
}
|
|
665
1071
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
1072
|
+
compilingTokens.add(tokenKey);
|
|
1073
|
+
try {
|
|
1074
|
+
const resolution = this.findProviderDefinition(currentModuleClass, resolvedToken);
|
|
1075
|
+
if (!resolution) {
|
|
1076
|
+
const optional = this.isTokenOptional(resolvedToken, currentModuleClass);
|
|
1077
|
+
return optional ? `undefined` : `(() => { throw new Error('calyx JIT DI: Cannot resolve dependency ' + String(c[${getClosureIdx(resolvedToken)}])); })()`;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
const { targetModuleClass, provider } = resolution;
|
|
1081
|
+
|
|
1082
|
+
let valExpr = '';
|
|
1083
|
+
if (typeof provider !== 'function' && 'useValue' in provider) {
|
|
1084
|
+
valExpr = `c[${getClosureIdx(provider.useValue)}]`;
|
|
1085
|
+
} else if (typeof provider !== 'function' && 'useExisting' in provider) {
|
|
1086
|
+
return compileToken(resolveForwardRef(provider.useExisting), targetModuleClass);
|
|
1087
|
+
} else {
|
|
1088
|
+
const ClassToInstantiate = typeof provider === 'function'
|
|
1089
|
+
? provider
|
|
1090
|
+
: ('useClass' in provider ? provider.useClass : null);
|
|
1091
|
+
|
|
1092
|
+
if (ClassToInstantiate) {
|
|
1093
|
+
const paramTypes: any[] = Reflect.getMetadata('design:paramtypes', ClassToInstantiate) || [];
|
|
1094
|
+
const injectTokens: Map<number, any> = Reflect.getMetadata(METADATA_KEYS.INJECT_TOKENS, ClassToInstantiate) || new Map();
|
|
1095
|
+
const optionalParams: Set<number> = Reflect.getMetadata(METADATA_KEYS.OPTIONAL_PARAMS, ClassToInstantiate) || new Set();
|
|
1096
|
+
|
|
1097
|
+
const argsExprs = paramTypes.map((paramType, i) => {
|
|
1098
|
+
const depToken = injectTokens.get(i) ?? paramType;
|
|
1099
|
+
const resolvedDepToken = resolveForwardRef(depToken);
|
|
1100
|
+
try {
|
|
1101
|
+
return compileToken(resolvedDepToken, targetModuleClass);
|
|
1102
|
+
} catch (err) {
|
|
1103
|
+
if (optionalParams.has(i)) {
|
|
1104
|
+
return `undefined`;
|
|
1105
|
+
}
|
|
1106
|
+
throw err;
|
|
689
1107
|
}
|
|
690
|
-
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
const propertyInjects: Map<string | symbol, any> =
|
|
1111
|
+
Reflect.getOwnMetadata(METADATA_KEYS.PROPERTY_INJECTS, ClassToInstantiate) || new Map();
|
|
1112
|
+
|
|
1113
|
+
const propAssignments: string[] = [];
|
|
1114
|
+
for (const [propKey, propToken] of propertyInjects.entries()) {
|
|
1115
|
+
const resolvedPropToken = resolveForwardRef(propToken);
|
|
1116
|
+
const propValExpr = compileToken(resolvedPropToken, targetModuleClass);
|
|
1117
|
+
propAssignments.push(`inst.${String(propKey)} = ${propValExpr};`);
|
|
691
1118
|
}
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
const propertyInjects: Map<string | symbol, any> =
|
|
695
|
-
Reflect.getOwnMetadata(METADATA_KEYS.PROPERTY_INJECTS, ClassToInstantiate) || new Map();
|
|
696
|
-
|
|
697
|
-
const propAssignments: string[] = [];
|
|
698
|
-
for (const [propKey, propToken] of propertyInjects.entries()) {
|
|
699
|
-
const resolvedPropToken = resolveForwardRef(propToken);
|
|
700
|
-
const propValExpr = compileToken(resolvedPropToken, targetModuleClass);
|
|
701
|
-
propAssignments.push(`inst.${String(propKey)} = ${propValExpr};`);
|
|
702
|
-
}
|
|
703
1119
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
1120
|
+
const classIdx = getClosureIdx(ClassToInstantiate);
|
|
1121
|
+
const instantiateExpr = `new c[${classIdx}](${argsExprs.join(', ')})`;
|
|
1122
|
+
|
|
1123
|
+
if (scope === Scope.REQUEST) {
|
|
1124
|
+
compiledTokens.add(tokenKey);
|
|
1125
|
+
const tokenIdx = getClosureIdx(resolvedToken);
|
|
1126
|
+
instantiations.push(`
|
|
1127
|
+
if (!requestContext.has(c[${tokenIdx}])) {
|
|
1128
|
+
const inst = ${instantiateExpr};
|
|
1129
|
+
${propAssignments.join('\n')}
|
|
1130
|
+
requestContext.set(c[${tokenIdx}], inst);
|
|
1131
|
+
}
|
|
1132
|
+
`);
|
|
1133
|
+
valExpr = `requestContext.get(c[${tokenIdx}])`;
|
|
1134
|
+
} else {
|
|
1135
|
+
if (propAssignments.length > 0) {
|
|
1136
|
+
const tempVar = `transient_${compiledTokens.size}`;
|
|
1137
|
+
compiledTokens.add(tempVar);
|
|
1138
|
+
instantiations.push(`
|
|
1139
|
+
const ${tempVar} = ${instantiateExpr};
|
|
1140
|
+
${propAssignments.map(line => line.replace('inst.', `${tempVar}.`)).join('\n')}
|
|
1141
|
+
`);
|
|
1142
|
+
valExpr = tempVar;
|
|
1143
|
+
} else {
|
|
1144
|
+
valExpr = instantiateExpr;
|
|
715
1145
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1146
|
+
}
|
|
1147
|
+
} else if (provider && typeof provider === 'object' && 'useFactory' in provider) {
|
|
1148
|
+
const injectTokens = provider.inject || [];
|
|
1149
|
+
const argsExprs = injectTokens.map((t) => compileToken(resolveForwardRef(t), targetModuleClass));
|
|
1150
|
+
|
|
1151
|
+
const factoryIdx = getClosureIdx(provider.useFactory);
|
|
1152
|
+
const factoryCallExpr = `c[${factoryIdx}](${argsExprs.join(', ')})`;
|
|
1153
|
+
|
|
1154
|
+
if (scope === Scope.REQUEST) {
|
|
1155
|
+
compiledTokens.add(tokenKey);
|
|
1156
|
+
const tokenIdx = getClosureIdx(resolvedToken);
|
|
722
1157
|
instantiations.push(`
|
|
723
|
-
|
|
724
|
-
|
|
1158
|
+
if (!requestContext.has(c[${tokenIdx}])) {
|
|
1159
|
+
requestContext.set(c[${tokenIdx}], ${factoryCallExpr});
|
|
1160
|
+
}
|
|
725
1161
|
`);
|
|
726
|
-
valExpr =
|
|
1162
|
+
valExpr = `requestContext.get(c[${tokenIdx}])`;
|
|
727
1163
|
} else {
|
|
728
|
-
valExpr =
|
|
1164
|
+
valExpr = factoryCallExpr;
|
|
729
1165
|
}
|
|
730
1166
|
}
|
|
731
|
-
} else if (provider && typeof provider === 'object' && 'useFactory' in provider) {
|
|
732
|
-
const injectTokens = provider.inject || [];
|
|
733
|
-
const argsExprs = injectTokens.map((t) => compileToken(resolveForwardRef(t), targetModuleClass));
|
|
734
|
-
|
|
735
|
-
const factoryIdx = getClosureIdx(provider.useFactory);
|
|
736
|
-
const factoryCallExpr = `c[${factoryIdx}](${argsExprs.join(', ')})`;
|
|
737
|
-
|
|
738
|
-
if (scope === Scope.REQUEST) {
|
|
739
|
-
compiledTokens.add(tokenKey);
|
|
740
|
-
const tokenIdx = getClosureIdx(token);
|
|
741
|
-
instantiations.push(`
|
|
742
|
-
if (!requestContext.has(c[${tokenIdx}])) {
|
|
743
|
-
requestContext.set(c[${tokenIdx}], ${factoryCallExpr});
|
|
744
|
-
}
|
|
745
|
-
`);
|
|
746
|
-
valExpr = `requestContext.get(c[${tokenIdx}])`;
|
|
747
|
-
} else {
|
|
748
|
-
valExpr = factoryCallExpr;
|
|
749
|
-
}
|
|
750
1167
|
}
|
|
751
|
-
}
|
|
752
1168
|
|
|
753
|
-
|
|
1169
|
+
return valExpr;
|
|
1170
|
+
} finally {
|
|
1171
|
+
compilingTokens.delete(tokenKey);
|
|
1172
|
+
}
|
|
754
1173
|
};
|
|
755
1174
|
|
|
756
1175
|
const paramTypes: any[] = Reflect.getMetadata('design:paramtypes', controllerClass) || [];
|