All files / core/src/thread spawn.ts

100% Statements 80/80
93.75% Branches 15/16
100% Functions 3/3
100% Lines 80/80

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 811x 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 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 8x 8x 1x 1x 1x 8x 8x 6x 3x 3x 3x 3x 3x 3x 3x 1x 2x 2x 2x 2x 2x 2x 2x 1x 8x 8x 8x 9x 9x 9x 9x 9x 9x  
import { Endpoint, Remote, wrap } from 'comlink';
import { NodeEndpoint } from 'comlink/dist/umd/node-adapter';
import { firstValueFrom } from 'rxjs';
import { Kernel } from '../kernel/kernel';
import { TypeOf } from '../utility/type-of';
 
/**
 * This prototype property decorator factory **Spawn**s a {@link Worker} and
 * {@link wrap}s and assigns the resulting {@link Remote} to the decorated
 * prototype property.
 *
 * @param worker - The `worker` module name or {@link Endpoint} to **Spawn**.
 * @param source - An optional {@link Kernel.Module} `source`.
 * @returns A prototype property decorator.
 *
 * @example
 * **Spawn** a {@link Worker}:
 * ```ts
 * import { Spawn, type Thread } from '@sgrud/core';
 * import { type ExampleWorker } from 'example-worker';
 *
 * export class ExampleWorkerHandler {
 *
 *   ⁠@Spawn('example-worker')
 *   public readonly worker!: Thread<ExampleWorker>;
 *
 * }
 * ```
 *
 * @see {@link Thread}
 */
export function Spawn(
  worker: string | Endpoint | NodeEndpoint,
  source?: string
) {
 
  /**
   * @param prototype - The `prototype` to be decorated.
   * @param propertyKey - The `prototype` property to be decorated.
   * @throws A {@link ReferenceError} when the environment is incompatible.
   */
  return function(prototype: object, propertyKey: PropertyKey): void {
    let thread;
 
    Object.defineProperty(prototype, propertyKey, {
      enumerable: true,
      get: (): Remote<unknown> => thread ||= (async() => {
        if (TypeOf.process(globalThis.process)) {
          if (TypeOf.string(worker)) {
            const { Worker } = require('worker_threads');
            worker = new Worker(require.resolve(worker));
          }
 
          const nodeEndpoint = require('comlink/dist/umd/node-adapter');
          worker = nodeEndpoint(worker);
        } else if (TypeOf.string(worker)) {
          const kernel = new Kernel();
          source ||= `${kernel.nodeModules}/${worker}`;
          const module = await firstValueFrom(kernel.resolve(worker, source));
 
          if (!globalThis.sgrud && module.exports) {
            worker = new Worker(`${source}/${module.exports}`, {
              type: 'module'
            });
          } else if (globalThis.sgrud && module.unpkg) {
            worker = new Worker(`${source}/${module.unpkg}`, {
              type: 'classic'
            });
          } else {
            throw new ReferenceError(module.name);
          }
        }
 
        return wrap(worker as Endpoint);
      })(),
      set: Function.prototype as (...args: any[]) => any
    });
  };
 
}