All files / tao-utils/src bridge.js

100% Statements 31/31
100% Branches 16/16
100% Functions 13/13
100% Lines 28/28

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    1x     8x   7x 1x   6x           8x 8x 7x   1x 2x 1x                     9x 1x     8x 8x 8x 8x 8x 2x 2x   6x 2x   6x 6x 5x                                         2x                                       1x                                       6x    
import { Kernel, AppCtx, INTERCEPT, ASYNC, INLINE } from '@tao.js/core';
 
const NOOP = () => {};
 
function forwardHandler(destination) {
  return (tao, data) => {
    // console.log('bridging::tao', tao);
    if (tao instanceof AppCtx) {
      destination.setAppCtx(tao);
    } else {
      destination.setCtx(tao, data);
    }
  };
}
 
function filteredForwardHandler(destination, filter) {
  const forward = forwardHandler(destination);
  if (!filter) {
    return forward;
  }
  return (tao, data) => {
    if (filter(tao, data)) {
      forward(tao, data);
    }
  };
}
 
function bridge(type, source, destination, filters) {
  /* istanbul ignore next -- public bridge factories always provide a valid phase. */
  // Stryker disable next-line all: unreachable via the exported bridge factories, which always pass a valid phase constant
  if (type !== INTERCEPT && type !== ASYNC && type !== INLINE) {
    return NOOP;
  }
  if (!(source instanceof Kernel) || !(destination instanceof Kernel)) {
    return NOOP;
  }
  const filterFunction =
    typeof filters[0] === 'function' ? filters.shift() : undefined;
  const handler = filteredForwardHandler(destination, filterFunction);
  const attachment = `add${type}Handler`;
  const detachment = `remove${type}Handler`;
  if (!filters.length) {
    source[attachment]({}, handler);
    return () => source[detachment]({}, handler);
  }
  if (Array.isArray(filters[0])) {
    filters = filters[0];
  }
  filters.forEach((trigram) => source[attachment](trigram, handler));
  return () =>
    filters.forEach((trigrams) => source[detachment](trigrams, handler));
}
 
/**
 * Bridges signals handled on the `source` Kernel into the `destination`
 * Kernel via an `InterceptHandler` — the forwarded signal re-enters the
 * destination through `setCtx` (or `setAppCtx` for AppCtx values). The
 * bridging handler returns nothing, so an intercept bridge observes without
 * halting the source cascade.
 *
 * @export
 * @param {Kernel} source - the Kernel to bridge signals from
 * @param {Kernel} destination - the Kernel to bridge signals into
 * @param {...*} filters - optional leading filter function
 *        `(tao, data) => boolean` gating which signals forward, then
 *        trigrams (or a single array of trigrams) to bridge; with no
 *        trigrams the bridge attaches to the wildcard `{}`
 * @returns {function(): void} detaches the bridge from `source` (a no-op
 *          when `source`/`destination` are not Kernel instances)
 */
export function interceptBridge(source, destination, ...filters) {
  return bridge(INTERCEPT, source, destination, filters);
}
 
/**
 * Bridges signals handled on the `source` Kernel into the `destination`
 * Kernel via an `AsyncHandler` — the forwarded signal re-enters the
 * destination through `setCtx` (or `setAppCtx` for AppCtx values) on the
 * async fork of the source cascade.
 *
 * @export
 * @param {Kernel} source - the Kernel to bridge signals from
 * @param {Kernel} destination - the Kernel to bridge signals into
 * @param {...*} filters - optional leading filter function
 *        `(tao, data) => boolean` gating which signals forward, then
 *        trigrams (or a single array of trigrams) to bridge; with no
 *        trigrams the bridge attaches to the wildcard `{}`
 * @returns {function(): void} detaches the bridge from `source` (a no-op
 *          when `source`/`destination` are not Kernel instances)
 */
export function asyncBridge(source, destination, ...filters) {
  return bridge(ASYNC, source, destination, filters);
}
 
/**
 * Bridges signals handled on the `source` Kernel into the `destination`
 * Kernel via an `InlineHandler` — the forwarded signal re-enters the
 * destination through `setCtx` (or `setAppCtx` for AppCtx values) in the
 * source cascade's inline spool.
 *
 * @export
 * @param {Kernel} source - the Kernel to bridge signals from
 * @param {Kernel} destination - the Kernel to bridge signals into
 * @param {...*} filters - optional leading filter function
 *        `(tao, data) => boolean` gating which signals forward, then
 *        trigrams (or a single array of trigrams) to bridge; with no
 *        trigrams the bridge attaches to the wildcard `{}`
 * @returns {function(): void} detaches the bridge from `source` (a no-op
 *          when `source`/`destination` are not Kernel instances)
 */
export function inlineBridge(source, destination, ...filters) {
  return bridge(INLINE, source, destination, filters);
}