All files run-many-for.ts

100% Statements 71/71
100% Branches 19/19
100% Functions 7/7
100% Lines 71/71

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 721x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 243x 243x 344x 186x 186x 186x 344x 243x 243x 243x 243x 1x 1x 90x 90x 90x 90x 1x 1x 153x 101x 1x 1x 1x 36x 36x 36x 190x 153x 101x 138x 138x 90x 90x 36x 1x 1x 36x 36x 36x 36x 36x 36x 36x 36x 36x 4x 36x  
/**
 * Copyright (c) OpenLens Authors. All rights reserved.
 * Licensed under MIT License. See LICENSE in root directory for more information.
 */
import type { DiContainerForInjection, InjectionToken } from "@ogre-tools/injectable";
import { getOrInsert } from "@k8slens/utilities";
import type TypedEventEmitter from "typed-emitter";
import EventEmitter from "events";
import { convertToWithIdWith, verifyRunnablesAreDAG } from "./helpers";
import type { RunnableWithId, Runnable, Run } from "./types";
import type { Asyncify } from "type-fest";
 
export type RunMany = <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => Asyncify<Run<Param>>;
 
interface BarrierEvent {
  finish: (id: string) => void;
}
 
class DynamicBarrier {
  private readonly finishedIds = new Map<string, Promise<void>>();
  private readonly events: TypedEventEmitter<BarrierEvent> = new EventEmitter();
 
  private initFinishingPromise(id: string): Promise<void> {
    return getOrInsert(this.finishedIds, id, new Promise<void>(resolve => {
      const handler = (finishedId: string) => {
        if (finishedId === id) {
          resolve();
          this.events.removeListener("finish", handler);
        }
      };
 
      this.events.addListener("finish", handler);
    }));
  }
 
  setFinished(id: string): void {
    void this.initFinishingPromise(id);
 
    this.events.emit("finish", id);
  }
 
  async blockOn(id: string): Promise<void> {
    await this.initFinishingPromise(id);
  }
}
 
const executeRunnableWith = <Param>(param: Param) => {
  const barrier = new DynamicBarrier();
 
  return async (runnable: RunnableWithId<Param>): Promise<void> => {
    for (const parentRunnable of runnable.runAfter) {
      await barrier.blockOn(parentRunnable.id);
    }
 
    await runnable.run(param);
    barrier.setFinished(runnable.id);
  };
};
 
export function runManyFor(di: DiContainerForInjection): RunMany {
  const convertToWithId = convertToWithIdWith(di);
 
  return <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => async (param: Param) => {
    const executeRunnable = executeRunnableWith(param);
    const allRunnables = di.injectManyWithMeta(injectionToken).map(x => convertToWithId(x));
 
    verifyRunnablesAreDAG(injectionToken.id, allRunnables);
 
    await Promise.all(allRunnables.map(executeRunnable));
  };
}