nestjs-cls
Version:
A continuation-local storage module compatible with NestJS's dependency injection.
136 lines • 6.53 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProxyProvidersResolver = void 0;
const unknown_dependencies_exception_1 = require("@nestjs/core/errors/exceptions/unknown-dependencies.exception");
const proxy_provider_functions_1 = require("./proxy-provider.functions");
const proxy_provider_exceptions_1 = require("./proxy-provider.exceptions");
const proxy_provider_constants_1 = require("./proxy-provider.constants");
const get_proxy_provider_symbol_1 = require("./get-proxy-provider-symbol");
const promise_with_resolvers_polyfill_1 = require("../../utils/promise-with-resolvers.polyfill");
const CLS_PROXY_PROVIDER_PROMISES_MAP = Symbol('CLS_PROVIDER_PROMISES_MAP');
class ProxyProvidersResolver {
constructor(cls, proxyProviderMap) {
this.cls = cls;
this.proxyProviderMap = proxyProviderMap;
this.proxyProviderDependenciesMap = new Map();
this.proxyProviderMap.forEach((provider) => {
// find each Proxy Providers' dependencies that are also Proxy Providers
// and store those in a separate map
this.proxyProviderDependenciesMap.set(provider.symbol, provider.dependencies
.map(get_proxy_provider_symbol_1.getProxyProviderSymbol)
.filter((symbol) => !proxy_provider_constants_1.defaultProxyProviderTokens.has(symbol))
.filter((symbol) => proxyProviderMap.has(symbol)));
});
}
/**
* Resolves all Proxy Providers that have been registered,
* or only the ones that are passed as an argument and their dependencies.
*/
async resolve(providerSymbols) {
const providerSymbolsToResolve = providerSymbols?.length
? providerSymbols
: Array.from(this.proxyProviderMap.keys());
const resolutionPromisesMap = this.getOrCreateCurrentProxyPromisesMap();
const allNeededProviderSymbols = this.getAllNeededProviderSymbols(providerSymbolsToResolve);
const promiseWithResolversMap = new Map();
for (const dep of allNeededProviderSymbols) {
const isBeingResolved = resolutionPromisesMap.has(dep);
if (!isBeingResolved) {
const promiseWithResolvers = (0, promise_with_resolvers_polyfill_1.Promise_withResolvers)();
promiseWithResolversMap.set(dep, promiseWithResolvers);
resolutionPromisesMap.set(dep, promiseWithResolvers.promise);
}
}
for (const [dep, promiseWithResolvers] of promiseWithResolversMap) {
void this.resolveProxyProvider(dep, promiseWithResolvers, resolutionPromisesMap);
}
const timeout = 10_000;
const timeoutPromise = (0, promise_with_resolvers_polyfill_1.Promise_withResolvers)();
const timeoutHandle = setTimeout(() => {
timeoutPromise.reject(proxy_provider_exceptions_1.ProxyProvidersResolutionTimeoutException.create(timeout));
}, timeout);
try {
await Promise.race([
timeoutPromise.promise,
Promise.all(resolutionPromisesMap.values()),
]);
}
finally {
clearTimeout(timeoutHandle);
}
}
/**
* ProxyProviderResolutionPromisesMap is a map scoped to the
* current CLS context that holds reference to the Promises of
* all Proxy Providers (those that have been resolved and those
* that are being resolved)
*
* It is used to prevent multiple concurrent resolutions of the same
* Proxy Provider and also to ensure that all Proxy Providers are
* resolved in the correct order (dependencies first)
*/
getOrCreateCurrentProxyPromisesMap() {
const resolutionPromisesMap = this.cls.get(CLS_PROXY_PROVIDER_PROMISES_MAP) ?? new Map();
this.cls.setIfUndefined(CLS_PROXY_PROVIDER_PROMISES_MAP, resolutionPromisesMap);
return resolutionPromisesMap;
}
/**
* Gets a set of all Proxy Provider symbols that need to be resolved
* and symbols of their dependencies (that have not been resolved yet)
*/
getAllNeededProviderSymbols(providerSymbols) {
return new Set(providerSymbols
.filter((providerSymbol) => !this.cls.get(providerSymbol))
.map((providerSymbol) => {
const deps = this.proxyProviderDependenciesMap.get(providerSymbol) ??
[];
return [providerSymbol, ...deps];
})
.flat());
}
async resolveProxyProvider(providerSymbol, ownPromise, dependencyPromises) {
try {
const providerDefinition = this.proxyProviderMap.get(providerSymbol);
if (!providerDefinition) {
throw proxy_provider_exceptions_1.ProxyProviderNotRegisteredException.create(providerSymbol);
}
const ownDependencySymbols = this.proxyProviderDependenciesMap.get(providerSymbol) ?? [];
const ownDependencyPromises = ownDependencySymbols.map((dep) => dependencyPromises.get(dep));
await Promise.all(ownDependencyPromises);
const providerInstance = await this.resolveProxyProviderInstance(providerDefinition);
this.cls.set(providerSymbol, providerInstance);
return ownPromise.resolve();
}
catch (e) {
return ownPromise.reject(e);
}
}
resolveProxyProviderInstance(provider) {
let resolution;
if ((0, proxy_provider_functions_1.isProxyClassProvider)(provider)) {
resolution = this.resolveProxyClassProviderInstance(provider);
}
else {
resolution = this.resolveProxyFactoryProviderInstance(provider);
}
return resolution;
}
async resolveProxyClassProviderInstance(provider) {
const moduleRef = provider.moduleRef;
const Provider = provider.useClass;
try {
return await moduleRef.create(Provider);
}
catch (error) {
if (!(error instanceof unknown_dependencies_exception_1.UnknownDependenciesException))
throw error;
throw proxy_provider_exceptions_1.UnknownProxyDependenciesException.create(error, Provider);
}
}
async resolveProxyFactoryProviderInstance(provider) {
const injected = provider.injected;
return await provider.useFactory.apply(null, injected);
}
}
exports.ProxyProvidersResolver = ProxyProvidersResolver;
//# sourceMappingURL=proxy-provider-resolver.js.map
;