All files / core/src/linker linker.ts

100% Statements 98/98
90% Branches 9/10
100% Functions 3/3
100% Lines 98/98

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 991x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 180x 180x 22x 22x 180x 180x 180x 180x 1x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 58x 58x 58x 8x 58x 58x 58x 58x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 93x 93x 93x 93x 206x 115x 206x 206x 93x 93x 93x 1x 1x  
import { Singleton } from '../utility/singleton';
import { Target } from './target';
 
/**
 * The {@link Singleton} **Linker** class provides the means to lookup and
 * retrieve instances of {@link Target}ed constructors. The **Linker** is used
 * throughout the [SGRUD](https://sgrud.github.io) client libraries, e.g., by
 * the {@link Factor} decorator, to provide and retrieve different centrally
 * provisioned class instances.
 *
 * @decorator {@link Singleton}
 * @typeParam K - The {@link Target}ed constructor type.
 * @typeParam V - The {@link Target}ed {@link InstanceType}.
 *
 * @example
 * Preemptively link an instance:
 * ```ts
 * import { Linker } from '@sgrud/core';
 * import { Service } from './service';
 *
 * new Linker<typeof Service>([
 *   [Service, new Service('linked')]
 * ]);
 * ```
 */
@Singleton((linker, [tuples]) => {
  if (tuples) {
    for (const [key, value] of tuples) {
      linker.set(key, value);
    }
  }
 
  return linker;
})
export class Linker<
  K extends abstract new () => V,
  V = InstanceType<K>
> extends Map<K, V> {
 
  /**
   * Overridden **get** method. Calling this method looks up the linked instance
   * based on the supplied `target` constructor. If no linked instance is found,
   * one is created by calling the `new` operator on the `target` constructor.
   * Therefor the `target` constructors must not require parameters.
   *
   * @param target - The `target` constructor for which to retrieve an instance.
   * @returns The already linked or a newly constructed and linked instance.
   *
   * @example
   * Retrieve a linked instance:
   * ```ts
   * import { Linker } from '@sgrud/core';
   * import { Service } from './service';
   *
   * new Linker<typeof Service>().get(Service);
   * ```
   */
  public override get(target: K): V {
    let instance = super.get(target);
 
    if (!instance) {
      this.set(target, instance = new (target as unknown as Target<V>)());
    }
 
    return instance;
  }
 
  /**
   * The **getAll** method returns all linked instances, which satisfy
   * `instanceof target`. Use this method when multiple linked `target`
   * constructors extend the same base class and are to be retrieved.
   *
   * @param target - The `target` constructor for which to retrieve instances.
   * @returns All already linked instance of the `target` constructor.
   *
   * @example
   * Retrieve all linked instances of a `Service`:
   * ```ts
   * import { Linker } from '@sgrud/core';
   * import { Service } from './service';
   *
   * new Linker<typeof Service>().getAll(Service);
   * ```
   */
  public getAll(target: K): V[] {
    const instances = [];
    const values = this.values();
 
    for (const instance of values) {
      if (instance instanceof target) {
        instances.push(instance);
      }
    }
 
    return instances;
  }
 
}