UNPKG

awilix-manager

Version:

Wrapper over awilix to support more complex use-cases, such as async init and eager injection

204 lines (203 loc) 7.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AwilixManager = void 0; exports.asMockClass = asMockClass; exports.asMockValue = asMockValue; exports.asMockFunction = asMockFunction; exports.asyncInit = asyncInit; exports.eagerInject = eagerInject; exports.getWithTags = getWithTags; exports.getByPredicate = getByPredicate; exports.asyncDispose = asyncDispose; const awilix_1 = require("awilix"); function asMockClass(Type, opts) { return (0, awilix_1.asClass)(Type, opts); } function asMockValue(value) { return (0, awilix_1.asValue)(value); } function asMockFunction(fn, opts) { return (0, awilix_1.asFunction)(fn, opts); } class AwilixManager { config; constructor(config) { this.config = config; if (config.strictBooleanEnforced) { for (const entry of Object.entries(config.diContainer.registrations)) { const [dependencyName, config] = entry; if ('enabled' in config && config.enabled !== true && config.enabled !== false) { throw new Error(`Invalid config for ${dependencyName}. "enabled" field can only be set to true or false, or omitted`); } } } } async executeInit() { if (this.config.eagerInject) { eagerInject(this.config.diContainer); } if (this.config.asyncInit) { await asyncInit(this.config.diContainer, { enableDebugLogging: this.config.enableDebugLogging, loggerFn: this.config.loggerFn, }); } } async executeDispose() { await asyncDispose(this.config.diContainer); } getWithTags(tags) { return getWithTags(this.config.diContainer, tags); } getByPredicate(predicate) { return getByPredicate(this.config.diContainer, predicate); } } exports.AwilixManager = AwilixManager; function isAsyncInitConfig(value) { return (typeof value === 'object' && value !== null && ('method' in value || 'nonBlocking' in value)); } function getAsyncInitMethod(asyncInit) { if (isAsyncInitConfig(asyncInit)) { // If method is not specified, default to true (use default asyncInit method) return asyncInit.method ?? true; } return asyncInit; } function isNonBlocking(asyncInit) { return isAsyncInitConfig(asyncInit) && asyncInit.nonBlocking === true; } async function asyncInit(diContainer, options = {}) { const { enableDebugLogging, loggerFn = console.log } = options; const dependenciesWithAsyncInit = Object.entries(diContainer.registrations) .filter((entry) => { return entry[1].asyncInit && entry[1].enabled !== false; }) .sort((entry1, entry2) => { const [key1, resolver1] = entry1; const [key2, resolver2] = entry2; const asyncInitPriority1 = resolver1.asyncInitPriority ?? 1; const asyncInitPriority2 = resolver2.asyncInitPriority ?? 1; if (asyncInitPriority1 !== asyncInitPriority2) { return asyncInitPriority1 - asyncInitPriority2; } return key1.localeCompare(key2); }); for (const [key, description] of dependenciesWithAsyncInit) { if (enableDebugLogging) { loggerFn(`asyncInit: ${key} - started`); } const resolvedValue = diContainer.resolve(key); const method = getAsyncInitMethod(description.asyncInit); const nonBlocking = isNonBlocking(description.asyncInit); // Validate method existence synchronously before starting async init validateAsyncInitMethod(resolvedValue, method, key); const initPromise = executeAsyncInitMethod(resolvedValue, method, key, diContainer); if (nonBlocking) { // Fire-and-forget: don't await the promise initPromise.then(() => { if (enableDebugLogging) { loggerFn(`asyncInit: ${key} - finished (non-blocking)`); } }); } else { await initPromise; if (enableDebugLogging) { loggerFn(`asyncInit: ${key} - finished`); } } } } function validateAsyncInitMethod(resolvedValue, method, key) { if (method === true) { if (!('asyncInit' in resolvedValue)) { throw new Error(`Method asyncInit does not exist on dependency ${key}`); } } else if (typeof method === 'string') { // custom method name if (!(method in resolvedValue)) { throw new Error(`Method ${method} for asyncInit does not exist on dependency ${key}`); } } } async function executeAsyncInitMethod(resolvedValue, method, _key, diContainer) { // use default asyncInit method if (method === true) { await resolvedValue.asyncInit(diContainer.cradle); } else if (typeof method === 'function') { // use function asyncInit await method(resolvedValue, diContainer); } else if (typeof method === 'string') { // use custom method name await resolvedValue[method](diContainer.cradle); } } function eagerInject(diContainer) { const dependenciesWithEagerInject = Object.entries(diContainer.registrations).filter(([_key, description]) => { return description.eagerInject && description.enabled !== false; }); for (const [key, description] of dependenciesWithEagerInject) { const resolvedComponent = diContainer.resolve(key); if (typeof description.eagerInject === 'string') { resolvedComponent[description.eagerInject](); } } } function getWithTags(diContainer, tags) { const dependenciesWithTags = Object.entries(diContainer.registrations).filter(([_key, description]) => { return (description.enabled !== false && tags.every((v) => description.tags && description.tags.includes(v))); }); const resolvedComponents = {}; for (const [key] of dependenciesWithTags) { resolvedComponents[key] = diContainer.resolve(key); } return resolvedComponents; } function getByPredicate(diContainer, predicate) { const enabledDependencies = Object.entries(diContainer.registrations).filter(([_key, description]) => { return description.enabled !== false; }); const resolvedComponents = {}; for (const [key] of enabledDependencies) { const resolvedElement = diContainer.resolve(key); if (predicate(resolvedElement)) { resolvedComponents[key] = resolvedElement; } } return resolvedComponents; } async function asyncDispose(diContainer) { const dependenciesWithAsyncDispose = Object.entries(diContainer.registrations) .filter(([_key, description]) => { return description.asyncDispose && description.enabled !== false; }) .sort((entry1, entry2) => { const [key1, resolver1] = entry1; const [key2, resolver2] = entry2; const asyncDisposePriority1 = resolver1.asyncDisposePriority ?? 1; const asyncDisposePriority2 = resolver2.asyncDisposePriority ?? 1; if (asyncDisposePriority1 !== asyncDisposePriority2) { return asyncDisposePriority1 - asyncDisposePriority2; } return key1.localeCompare(key2); }); for (const [key, description] of dependenciesWithAsyncDispose) { const resolvedValue = diContainer.resolve(key); const asyncDispose = description.asyncDispose; if (typeof asyncDispose === 'function') { await asyncDispose(resolvedValue); continue; } if (asyncDispose === true) { await resolvedValue.asyncDispose(); continue; } // @ts-expect-error await resolvedValue[asyncDispose](); } }