Code coverage report for core/src/providers.ts

Statements: 90.48% (38 / 42)      Branches: 74.07% (40 / 54)      Functions: 91.67% (11 / 12)      Lines: 90.48% (38 / 42)      Ignored: none     

All files » core/src/ » providers.ts
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 1261 1 1                 1 1         129 125   129         1         1 1         1         108 1   108           43     43 42     1   43 43 22   43 43         2           83     83 83     83 83 83 1 1 1 1           83         83                                           12        
import {InternalLog, logger, BASE_ROOT} from './internal/log';
import {Log} from './log';
import {joinFqn, Supplier} from './util';
 
 
export type Constructor<T> = new (...args: any[]) => T;
export type Named<T> = string | { name: string } | Constructor<T>;
 
let app: Providers;
 
 
export class Providers {
  private providers: Record<string, any> = {};
  private _log: InternalLog | Log | undefined;
 
 
  get log(): InternalLog | Log | undefined {
    if (this._log) {
      this._log = logger('Providers');
    }
    return this._log;
  }
 
 
  table(): void {
    this.log?.table(Object.entries(this.providers));
  }
 
 
  reset(): void {
    this.log?.debug('Resetting all entries!');
    this.providers = {};
  }
 
 
  static reset(): void {
    Providers.get().reset();
  }
 
 
  static get(): Providers {
    if (!app) {
      app = new Providers();
    }
    return app;
  }
 
 
  register<T extends Record<string, any>>(instance: T, name?: Named<T>): T {
    let key: string;
    Iif (typeof name === 'string') {
      key = name;
    }
    else if (name) {
      key = name.name;
    }
    else {
      key = instance.constructor.name;
    }
    this.providers[key] = instance;
    if (key === Log.name && instance instanceof Log) {
      this._log = instance.copy(joinFqn('Providers', BASE_ROOT));
    }
    this.log?.debug('Registered %s as %s', key, instance.constructor.name);
    return instance;
  }
 
 
  static register<T, I extends T>(instance: I, name?: Named<T>): T {
    return Providers.get().register(instance, name);
  }
 
 
  provide<T>(providerName: Named<T>, orRegister?: Supplier<T>): T {
    let key;
    Iif (typeof providerName === 'string') {
      key = providerName;
    }
    else Eif (providerName) {
      key = providerName.name;
    }
 
    let result = this.providers[key];
    this.log?.debug('Initial lookup for %s was %s', key, result?.constructor.name);
    if (!result && orRegister) {
      result = orRegister();
      Eif (result) {
        this.log?.debug('Fallback lookup for %s was %s', key, result?.constructor.name);
        this.register(result, providerName);
      }
      else {
        throw new Error(`Provider ${key} does not exist!`);
      }
    }
    return result;
  }
 
 
  static provide<T>(providerName: Named<T>, orRegister?: Supplier<T>): T {
    return Providers.get().provide(providerName, orRegister);
  }
 
 
  /**
   * Prefer registered instance but fallback to using `fallback` without registering.
   * @param providerName
   * @param fallback
   * @see useOrProvide
   */
  static provideOrUse<T>(fallback: T, providerName: Named<T>): T {
    return Providers.provide(providerName) || fallback;
  }
 
 
  /**
   * Prefer `preferred` but fallback to registered `providerName`.
   * @param providerName
   * @param preferred
   * @see provideOrUse
   */
  static useOrProvide<T>(providerName: Named<T>, preferred?: T): T {
    return preferred || Providers.provide(providerName);
  }
 
}