UNPKG

@athenna/ioc

Version:

Global Ioc helper for Athenna ecosystem. Built on top of awilix.

136 lines (135 loc) 3.77 kB
/** * @athenna/ioc * * (c) João Lenon <lenon@athenna.io> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { debug } from '#src/debug'; import { Is, Module, Macroable } from '@athenna/common'; import { PROTECTED_FACADE_METHODS } from '#src/constants/ProtectedFacadeMethods'; const athennaTest = await Module.safeImport('@athenna/test'); const Mock = athennaTest?.Mock; export class FacadeProxyHandler extends Macroable { /** * Creates a new instance of FacadeProxyHandler. */ constructor(facadeAccessor) { super(); /** * The service instance. */ this.provider = null; this.facadeAccessor = facadeAccessor; } /** * Resolve and return the service instance * of the facade. */ getProvider() { return ioc.safeUse(this.facadeAccessor); } /** * Returns the service instance registered * inside the facade if exists. */ getFreezedProvider() { return this.provider; } /** * Freezes the service instance of the * facade until calling `unfreeze()` method. */ freeze() { this.provider = this.getProvider(); } /** * Release the service instance of the * facade so new instances can be created. */ unfreeze() { this.provider = null; } /** * Resolves a service instance of the * facade and save it to be used as stub. * * The stub will be used instead of resolving * the service. */ stub() { this.freeze(); return Mock.stub(this.provider); } /** * Resolve a service instance of the facade * and save it to be */ spy() { this.freeze(); return Mock.spy(this.provider); } /** * Create a mock builder instance for the given method * of the facade. */ when(method) { this.freeze(); return Mock.when(this.provider, method); } /** * Restore the mocked facade to the original state. */ restore() { if (!this.provider) { return; } Mock.restore(this.provider); this.unfreeze(); } /** * Method called by Proxy every time that a value is changed. * Returns the provider method with a Proxy applied in apply. * This way we guarantee that we are working with * the same instance when a Facade method returns this. */ set(_, key, value) { if (this.provider) { this.provider[key] = value; return true; } const provider = this.getProvider(); provider[key] = value; return true; } /** * Method called by Proxy every time a new property is called. * Returns the provider method with a Proxy applied in apply. * This way we guarantee that we are working with * the same instance when a Facade method returns this. */ get(_, key) { if (PROTECTED_FACADE_METHODS.includes(key)) { debug('restricted facade method executed: %s', key); const method = this[key]; if (!Is.Function(method)) { return method; } return this[key].bind(this); } if (this.provider) { return this.provider[key]; } const provider = this.getProvider(); if (provider[key] === undefined) { return undefined; } if (!Is.Function(provider[key])) { return provider[key]; } return new Proxy(provider[key], { apply: (method, _this, args) => method.bind(provider)(...args) }); } }