All files / bus/src/handler stream.ts

100% Statements 115/115
100% Branches 4/4
100% Functions 3/3
100% Lines 115/115

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 1161x 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 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 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 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 2x 2x 2x 1x 1x  
import { Bus } from '../bus/bus';
 
/**
 * Prototype property decorator factory. Applying this decorator replaces the
 * decorated property with a getter returning a {@link Bus}, thereby allowing to
 * duplex **Stream** values designated by the supplied `handle`. Depending on
 * the value of the `suffix` parameter, this {@link Bus} is either assigned
 * directly to the prototype using the supplied `handle`, or, if a truthy value
 * is supplied for the `suffix` parameter, this value is assumed to reference
 * another property of the class containing this decorated property. The first
 * truthy value assigned to this `suffix` property on an instance of the class
 * containing this **Stream** decorator will then be used to suffix the supplied
 * `handle` upon construction of the {@link Bus}, which is assigned to the
 * decorated instance property.
 *
 * Through these two different modes of operation, a {@link Bus} can be assigned
 * statically to the prototype of the class containing the decorated property,
 * or this assignment can be deferred until an instance of the class containing
 * the decorated property is constructed and a truthy value is assigned to its
 * `suffix` property.
 *
 * @param handle - The {@link Bus.Handle} to **Stream**.
 * @param suffix - An optional `suffix` property for the `handle`.
 * @returns A prototype property decorator.
 *
 * @example
 * **Stream** `'io.github.sgrud.example'`:
 * ```ts
 * import { type Bus, Stream } from '@sgrud/bus';
 *
 * export class Streamer {
 *
 *   ⁠@Stream('io.github.sgrud.example')
 *   public readonly stream!: Bus<unknown, unknown>;
 *
 * }
 *
 * Streamer.prototype.stream.next('value');
 * Streamer.prototype.stream.complete();
 *
 * Streamer.prototype.stream.subscribe({
 *   next: console.log
 * });
 * ```
 *
 * @example
 * **Stream** `'io.github.sgrud.example'`:
 * ```ts
 * import { type Bus, Stream } from '@sgrud/bus';
 *
 * export class Streamer {
 *
 *   ⁠@Stream('io.github.sgrud', 'suffix')
 *   public readonly stream!: Bus<unknown>;
 *
 *   public constructor(
 *     public readonly suffix: string
 *   ) { }
 *
 * }
 *
 * const streamer = new Streamer('example');
 * streamer.stream.next('value');
 * streamer.stream.complete();
 *
 * streamer.stream.subscribe({
 *   next: console.log
 * });
 * ```
 *
 * @see {@link BusHandler}
 * @see {@link Observe}
 * @see {@link Publish}
 */
export function Stream(handle: Bus.Handle, suffix?: PropertyKey) {
 
  /**
   * @param prototype - The `prototype` to be decorated.
   * @param propertyKey - The `prototype` property to be decorated.
   */
  return function(prototype: object, propertyKey: PropertyKey): void {
    if (!suffix) {
      const stream = new Bus<unknown, unknown>(handle);
 
      Object.defineProperty(prototype, propertyKey, {
        enumerable: true,
        get: (): Bus<unknown, unknown> => stream,
        set: Function.prototype as (...args: any[]) => any
      });
    } else {
      Object.defineProperty(prototype, suffix, {
        enumerable: true,
        set(this: object, value: string): void {
          if (value) {
            const scoped = `${handle}.${value}` as Bus.Handle;
            const stream = new Bus(scoped);
 
            Object.defineProperties(this, {
              [suffix]: {
                enumerable: true,
                value
              },
              [propertyKey]: {
                enumerable: true,
                get: (): Bus<unknown, unknown> => stream,
                set: Function.prototype as (...args: any[]) => any
              }
            });
          }
        }
      });
    }
  };
 
}