UNPKG

@aedart/support

Version:

The Ion support package

331 lines (325 loc) 7.87 kB
/** * @aedart/support * * BSD-3-Clause, Copyright (c) 2023-present Alin Eugen Deac <aedart@gmail.com>. */ 'use strict'; var container = require('@aedart/contracts/container'); var misc = require('@aedart/support/misc'); var exceptions = require('@aedart/support/exceptions'); /** * Facade * * Adaptation of Laravel's Facade abstraction. * * @see https://github.com/laravel/framework/blob/master/src/Illuminate/Support/Facades/Facade.php * * @abstract */ class Facade { /** * The facade's service container instance * * @type {Container|undefined} * * @protected * @static */ static container = undefined; /** * The "type" of the resolved object instance. * * **Note**: _This property is not used for anything other * than to provide a TypeScript return type for the `obtain()` * method._ * * @protected * @static */ static type; /** * Resolved instances * * @type {Map<Identifier, any>} * * @protected * @static */ static resolved = new Map(); /** * Registered object spies * * @type {Set<Identifier>} * * @protected * @static */ static spies = new Set(); /** * Facade Constructor * * @protected */ constructor() { /* @ts-expect-error TS2345 Facade constructor is abstract */ throw new exceptions.AbstractClassError(this.constructor); } /** * Returns identifier to be used for resolving facade's underlying object instance * * @return {Identifier} * * @abstract * * @static */ static getIdentifier() { throw new exceptions.LogicalError('Facade does not implement the getIdentifier() method'); } /** * Obtain the underlying object instance, or a "spy" (for testing) * * @see spy * * @return {any} * * @throws {NotFoundException} * @throws {ContainerException} * @throws {LogicalError} * * @abstract * * @static */ static obtain() { return this.resolve(this.getIdentifier()); } /** * Register a "spy" (e.g. object mock) for this facade's identifier * * @template T = any * * @param {SpyFactoryCallback} callback Callback to be used for creating some kind of object spy * or mock, with appropriate configuration and expectations for testing * purposes. * * @return {T} Object instance (spy / object mock) to be registered in service container. * * @static */ static spy(callback) { const identifier = this.getIdentifier(); const spy = callback(this.getContainer(), identifier); this.swap(spy); this.spies.add(identifier); return spy; } /** * Determine if a spy has been registered for this facade's identifier * * @return {boolean} * * @static */ static isSpy() { return this.spies.has(this.getIdentifier()); } /** * Removes registered spy for this facade's identifier * * **Warning**: _Method does NOT perform any actual "spy" cleanup logic. * It only removes the reference to the "spy" object._ * * @return {boolean} * * @static */ static forgetSpy() { const identifier = this.getIdentifier(); return this.forgetResolved(identifier) && this.spies.delete(identifier); } /** * Removes all registered spies * * @return {void} * * @static */ static forgetAllSpies() { for (const identifier of this.spies) { this.forgetResolved(identifier); } this.spies.clear(); } /** * Swap the facade's underlying instance * * @param {any} instance * * @return {void} * * @static */ static swap(instance /* eslint-disable-line @typescript-eslint/no-explicit-any */) { const identifier = this.getIdentifier(); this.resolved.set(identifier, instance); if (this.hasContainer()) { this.getContainer().instance(identifier, instance); } } /** * Set this facade's service container instance * * @param {Container | undefined} container * * @return {this} * * @static */ static setContainer(container) { this.container = container; return this; } /** * Get this facade's service container instance * * @return {Container | undefined} * * @static */ static getContainer() { return this.container; } /** * Determine if facade's has a service container instance set * * @return {boolean} * * @static */ static hasContainer() { return misc.isset(this.container); } /** * Removes this facade's service container instance * * @return {this} * * @static */ static forgetContainer() { this.container = undefined; return this; } /** * Determine if resolved facade instance exists * * @param {Identifier} identifier * * @reurn {boolean} * * @static */ static hasResolved(identifier) { return this.resolved.has(identifier); } /** * Forget resolved facade instance * * @param {Identifier} identifier * * @return {boolean} * * @static */ static forgetResolved(identifier) { return this.resolved.delete(identifier); } /** * Forget all resolved facade instances * * @return {void} * * @static */ static forgetAllResolved() { this.resolved.clear(); } /** * Clears all resolved instances, service container and evt. spies. * * @return {void} */ static destroy() { this.forgetAllSpies(); this.forgetAllResolved(); this.forgetContainer(); } /** * Resolves the facade's underlying object instance from the service container, * which matches given identifier. * * **Note**: _If a "spy" has been registered for given identifier, then that spy * object is returned instead._ * * @template T = any * * @param {Identifier} identifier * * @return {T} * * @throws {NotFoundException} * @throws {ContainerException} * @throws {LogicalError} * * @protected * * @static */ static resolve(identifier) { if (!this.hasContainer()) { throw new exceptions.LogicalError('Facade has no service container instance set'); } if (this.hasResolved(identifier)) { return this.resolved.get(identifier); } const resolved = this.getContainer().make(identifier); this.resolved.set(identifier, resolved); return resolved; } } /** * Container Facade */ class Container extends Facade { /** * The "type" of the resolved object instance. * * **Note**: _This property is not used for anything other * than to provide a TypeScript return type for the `obtain()` * method._ * * @type {import('@aedart/contracts/container').Container} * * @protected * @static */ static type; /** * Returns identifier to be used for resolving facade's underlying object instance * * @return {Identifier} * * @abstract * * @static */ static getIdentifier() { return container.CONTAINER; } } exports.Container = Container; exports.Facade = Facade; module.exports = Object.assign(exports.default, exports); //# sourceMappingURL=facades.cjs.map