All files Injectables.ts

62.5% Statements 10/16
25% Branches 1/4
55.55% Functions 5/9
66.66% Lines 10/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 1481x                                                                                                 1x           1x                                 24x                       9x                                                 2x               33x 1x 33x                 1x                                      
import {
  Injectable,
  InjectableRegistryImp,
  InjectablesRegistry,
} from "./registry";
import { InjectableOptions } from "./types";
 
/**
 * @description Central registry for managing injectable dependencies.
 * @summary Static class holding the access to the injectables functions. Provides methods for registering,
 * retrieving, and building injectable objects.
 * @template T Type of the injectable object
 *
 * @class Injectables
 *
 * @example
 * // Define an injectable class
 * @injectable()
 * class MyService {
 *   doSomething() {
 *     return 'Hello World';
 *   }
 * }
 *
 * // Inject the service into another class
 * class MyComponent {
 *   @inject()
 *   private service!: MyService;
 *
 *   useService() {
 *     return this.service.doSomething();
 *   }
 * }
 *
 * @mermaid
 * sequenceDiagram
 *   participant Client
 *   participant Injectables
 *   participant Registry
 *
 *   Client->>Injectables: register(MyService)
 *   Injectables->>Registry: register(MyService)
 *   Registry-->>Injectables: void
 *
 *   Client->>Injectables: get("MyService")
 *   Injectables->>Registry: get("MyService")
 *   Registry-->>Injectables: MyService instance
 *   Injectables-->>Client: MyService instance
 */
export class Injectables {
  /**
   * @description Holds the active registry implementation used by the Injectables facade.
   * @summary Internal static reference that can be swapped via setRegistry to customize how injectables are stored and retrieved.
   * @type {InjectablesRegistry | undefined}
   */
  private static actingInjectablesRegistry?: InjectablesRegistry = undefined;
 
  private constructor() {}
 
  /**
   * @description Fetches an injectable instance by its registered name.
   * @summary Retrieves the named {@link Injectable} from the registry. If the injectable is a singleton,
   * returns the existing instance. Otherwise, creates a new instance.
   * @template T Type of the injectable object to retrieve
   * @param {string} name The registered name of the injectable to retrieve
   * @param {any[]} args Constructor arguments to pass when instantiating the injectable
   * @return {Injectable<T> | undefined} The injectable instance or undefined if not found
   */
  static get<T>(
    name: symbol | string | { new (...args: any[]): T },
    ...args: any[]
  ): T | undefined {
    return Injectables.getRegistry().get(name, ...args);
  }
 
  /**
   * @description Adds a class or object to the injectable registry.
   * @summary Registers an injectable constructor or instance with the registry, making it available for injection.
   * @template T Type of the injectable object to register
   * @param {Injectable<T>} constructor The class constructor or object instance to register
   * @param {any[]} args Additional arguments for registration (category, singleton flag, etc.)
   * @return {void}
   */
  static register<T>(constructor: Injectable<T>, ...args: any[]): void {
    return Injectables.getRegistry().register(
      constructor,
      ...(args as [symbol, InjectableOptions<T>])
    );
  }
 
  /**
   * @description Creates a new instance of an injectable class.
   * @summary Instantiates an injectable class using its constructor and the provided arguments.
   * @template T Type of the object to build
   * @param {symbol} name symbol referencing the injectable
   * @param {any[]} args Constructor arguments to pass when instantiating the injectable
   * @return {T} The newly created instance
   */
  static build<T>(name: symbol, ...args: any[]): T {
    return Injectables.getRegistry().build(name, ...args);
  }
 
  /**
   * @description Replaces the current registry implementation.
   * @summary Sets a new {@link InjectablesRegistry} implementation, allowing for custom registry behavior.
   * @param {InjectablesRegistry} operationsRegistry The new implementation of Registry to use
   * @return {void}
   */
  static setRegistry(operationsRegistry: InjectablesRegistry): void {
    Injectables.actingInjectablesRegistry = operationsRegistry;
  }
  /**
   * @description Provides access to the current registry instance.
   * @summary Returns the current {@link InjectablesRegistry} or creates a default one if none exists.
   * @return {InjectablesRegistry} The current registry instance
   */
  private static getRegistry(): InjectablesRegistry {
    if (!Injectables.actingInjectablesRegistry)
      Injectables.actingInjectablesRegistry = new InjectableRegistryImp();
    return Injectables.actingInjectablesRegistry;
  }
 
  /**
   * @description Clears all registered injectables.
   * @summary Resets the registry to a clean state by creating a new empty registry instance.
   * @return {void}
   */
  static reset(): void {
    Injectables.setRegistry(new InjectableRegistryImp());
  }
 
  /**
   * @description Removes specific injectables from the registry based on a pattern.
   * @summary Selectively resets the registry by removing only the injectables whose names match the provided pattern.
   * @param {string | RegExp} match A string or regular expression pattern to match against injectable names
   * @return {void}
   */
  static selectiveReset(match: string | RegExp): void {
    const regexp = typeof match === "string" ? new RegExp(match) : match;
    (Injectables.actingInjectablesRegistry as any)["cache"] = Object.entries(
      (Injectables.actingInjectablesRegistry as any)["cache"]
    ).reduce((accum: Record<string, any>, [key, val]) => {
      Iif (!key.match(regexp)) accum[key] = val;
      return accum;
    }, {});
  }
}