/** @format */ import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { CdnEvent, InstallInputs, InstallLoadingGraphInputs } from '..'; import { WorkersPoolView } from './views'; import { InWorkerAction, IWWorkerProxy, WWorkerTrait } from './web-worker.proxy'; import { BackendConfiguration } from '../backend-configuration'; import { FrontendConfiguration } from '../frontend-configuration'; type WorkerId = string; export interface ContextTrait { withChild: any; info: any; } export declare class NoContext implements ContextTrait { withChild(name: string, cb: (ctx: ContextTrait) => T): T; info(_text: string): void; } /** * Any {@link MainModule.CdnEvent} emitted from a Worker ({@link WWorkerTrait}). * @category Events */ export type CdnEventWorker = CdnEvent & { workerId: string; }; /** * @category Events */ export declare function implementEventWithWorkerTrait(event: unknown): event is CdnEventWorker; /** * A special type of {@link MessageData} for {@link MainModule.CdnEvent}. * @category Worker's Message */ export interface MessageCdnEvent { type: 'CdnEvent'; workerId: string; event: CdnEvent; } /** * @category Worker Environment */ export interface WorkerFunction { id: string; target: T; } /** * @category Worker Environment */ export interface WorkerVariable { id: string; value: T; } /** * Task specification. * * @typeParam TArgs Type of the entry point's arguments * @typeParam TReturn Type of the entry point's return * (emitted afterward using {@link MessageExit.result | MessageExit.result}). */ export interface Task { /** * Title of the task. */ title: string; /** * Entry point implementation, the value returned must follow * [structured clone algo](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) * @param args arguments of the entrypoint, must follow * [structured clone algo](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) */ entryPoint: (args: TArgs) => TReturn | Promise; /** * Arguments to forward to the entry point upon execution. */ args: TArgs; } /** * @category Worker Environment */ export interface WorkerEnvironment { /** * Global variables accessible in worker environment. */ variables: WorkerVariable[]; /** * Global functions accessible in worker environment. */ functions: WorkerFunction[]; /** * Installation instruction to be executed in worker environment. */ cdnInstallation: InstallInputs | InstallLoadingGraphInputs; /** * Tasks to realized after installation is done and before marking a worker as ready. */ postInstallTasks?: Task[]; } /** * Context available in {@link WWorkerTrait} to log info or send data. * All data must follow * [structured clone algo](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). * @category Worker Environment */ export interface WorkerContext { /** * The info logged are send from the workers to the main thread as * {@link MessageLog}. * * @param text title of the log * @param data data associated. */ info: (text: string, data?: unknown) => void; /** * The data logged are send from the workers to the main thread as * {@link MessageData}. * * @param data data to send. */ sendData: (data: Record) => void; /** * If defined by the developer in its worker's implementation, * every message send using {@link WorkersPool.sendData} will * be forwarded to this callback. */ onData?: (message: any) => void; } /** * Message send from the workers to the main thread when a task is started. * * @category Worker's Message */ export interface MessageExecute { /** * Id of the task */ taskId: string; /** * Id of the worker */ workerId: string; /** * Serialized entry point */ entryPoint: string; /** * Arguments provided */ args: unknown; } /** * Message emitted from workers when a task is started. * * @category Worker's Message */ export interface MessageStart { taskId: string; workerId: string; } /** * Message emitted from workers when a task is terminated. * * @category Worker's Message */ export interface MessageExit { taskId: string; workerId: string; error: boolean; result: unknown; } /** * Message emitted from workers when a log is sent (see {@link WorkerContext}). * * @category Worker's Message */ export interface MessageLog { workerId: string; taskId: string; text: string; json: unknown; } /** * Message emitted from workers when a data is sent (see {@link WorkerContext}). * * @category Worker's Message */ export interface MessageData { taskId: string; workerId: string; [k: string]: unknown; } /** * Message emitted from workers when an error occurred. * * @category Worker's Message */ export interface MessagePostError { taskId: string; workerId: string; error: Error; } /** * Message send from the main thread to a worker for a particular task. * See {@link WorkersPool.sendData}. * * @category Worker's Message */ export interface MainToWorkerMessage { /** * Id of the task */ taskId: string; /** * Id of the worker */ workerId: string; /** * Data forwarded */ data: unknown; } /** * Messages exchanged between the main thread and the workers' thread. * * @category Worker's Message */ export interface Message { type: 'Execute' | 'Exit' | 'Start' | 'Log' | 'Data' | 'MainToWorkerMessage' | 'PostError'; data: MessageExecute | MessageData | MessageExit | MessageLog | MessageStart | MainToWorkerMessage | MessagePostError; } /** * Encapsulates arguments to be sent to a task's entry point (implementation function). * * @category Worker Environment */ export interface EntryPointArguments { args: TArgs; taskId: string; workerId: string; context: WorkerContext; workerScope: any; } /** * This function is exposed mostly because it is useful in terms of testing to bypass serialization in string. * @category Worker Environment */ export declare function entryPointWorker(messageEvent: MessageEvent): void; /** * Message sent from the main thread to the workers to request installation of the {@link WorkerEnvironment}. * * @category Worker's Message */ export interface MessageInstall { backendConfiguration: BackendConfiguration; frontendConfiguration: FrontendConfiguration; cdnUrl: string; variables: WorkerVariable[]; functions: { id: string; target: string; }[]; cdnInstallation: InstallInputs | InstallLoadingGraphInputs; postInstallTasks: { title: string; entryPoint: string; args: unknown; }[]; onBeforeInstall: InWorkerAction; onAfterInstall: InWorkerAction; } /** * A process is an abstraction managing lifecycle of a particular task. * Not doing much for now besides gathering callbacks to call at different stage of the task (logging into console). */ export declare class Process { /** * Task's id. */ readonly taskId: string; /** * Task's title. */ readonly title: string; /** * Associated context. */ readonly context: ContextTrait; constructor(params: { taskId: string; title: string; context: ContextTrait; }); schedule(): void; start(): void; fail(error: unknown): void; succeed(): void; log(text: string): void; } /** * Pool size specification. */ export type PoolSize = { /** * Initial number of workers to get ready before {@link WorkersPool.ready} is fulfilled. * Set to `1` by default. */ startAt?: number; /** * Maximum number of workers. * Set to `max(1, navigator.hardwareConcurrency - 1)` by default. */ stretchTo?: number; }; /** * Input for {@link WorkersPool.constructor}. * */ export type WorkersPoolInput = { /** * If provided, all events regarding installation are forwarded here. * Otherwise {@link WorkersPool.cdnEvent$ | WorkersPool.cdnEvent$} is initialized and used. */ cdnEvent$?: Subject; /** * Globals to be copied in workers' environment. */ globals?: { [_k: string]: unknown; }; /** * Installation to proceed in the workers. */ install?: InstallInputs | InstallLoadingGraphInputs; /** * A list of tasks to execute in workers after installation is completed. */ postInstallTasks?: Task[]; /** * A factory that create a `Context` objects used for logging purposes. * It serves as dependency injection; the `Context` class from the library * [@youwol/logging](https://github.com/youwol/logging) is appropriate for that purpose. * * @param name name of the root node of the context * @return a `Context` object implementing {@link ContextTrait} */ ctxFactory?: (name: string) => ContextTrait; /** * Constraints on the workers pool size. */ pool?: PoolSize; }; /** * Input for {@link WorkersPool.schedule}. * * @typeParam TArgs type of the entry point's argument. */ export type ScheduleInput = { /** * Title of the task */ title: string; /** * Entry point of the task */ entryPoint: (input: EntryPointArguments) => void; /** * Arguments to forward to the entry point when executed */ args: TArgs; /** * If provided, schedule the task on this particular worker. */ targetWorkerId?: string; }; /** * Entry point to create workers pool. * * @category Getting Started */ export declare class WorkersPool { static BackendConfiguration: BackendConfiguration; static FrontendConfiguration: FrontendConfiguration; static webWorkersProxy: IWWorkerProxy; /** * Constraints on workers' pool size. * @group Immutable Constants */ readonly pool: PoolSize; private requestedWorkersCount; /** * All the {@link Message | messages } from all workers. * * @group Observables */ readonly mergedChannel$: Subject; /** * Observable that emit the list of started workers as soon as one or more is starting creation. * * @group Observables */ readonly startedWorkers$: BehaviorSubject; /** * Observable that emit a dictionary `workerId -> {worker, channel$}` each time new workers * are ready to be used (installation & post-install tasks achieved). * * The `channel$` object is streaming all associated worker's {@link Message}. * * @group Observables */ readonly workers$: BehaviorSubject<{ [p: string]: { worker: WWorkerTrait; channel$: Observable; }; }>; /** * Observable that emit the list of running tasks each time one or more are created or stopped. * * @group Observables */ readonly runningTasks$: BehaviorSubject<{ workerId: string; taskId: string; title: string; }[]>; /** * Observable that emits the id of workers that are currently running a tasks each time a task is started * or stopped. * * @group Observables */ readonly busyWorkers$: BehaviorSubject; /** * Observable that emits `{taskId, workerId}` each time a worker finished processing a task. * * @group Observables */ readonly workerReleased$: Subject<{ workerId: WorkerId; taskId: string; }>; /** * If `CtxFactory` is provided in constructor's argument ({@link WorkersPoolInput}), * main thread logging information is available here. */ readonly backgroundContext: ContextTrait; /** * Observable that gathers all the {@link CdnEventWorker} emitted by the workers. * * @group Observables */ readonly cdnEvent$: Subject; /** * Workers' environment. * * @group Immutable Constants */ readonly environment: WorkerEnvironment; private tasksQueue; constructor(params: WorkersPoolInput); /** * Reserve a particular amount of worker. * No workers are deleted, and the number of worker can not exceed `pool.stretchTo` property. * @param workersCount */ reserve({ workersCount }: { workersCount: number; }): Observable[]>; /** * When this method is awaited, it ensures that `pool.startAt` workers are ready to be used * (installation & post-install tasks achieved). */ ready(): Promise; /** * Schedule a task. * * @param input task description * @param context context to log run-time info * @typeParam TArgs type of the entry point's argument */ schedule(input: ScheduleInput, context?: NoContext): Observable; /** * Send a message from main thread to the worker processing a target task. * The function running in the worker has to instrument the received `context` * argument in order to process the messages. * E.g. * ``` * async function functionInWorker({ * args, * workerScope, * workerId, * taskId, * context, * }){ * context.onData = (args) => { * console.log('Received data from main thread', args) * } * } * ``` * @param taskId target taskId * @param args arguments to forward, * should be valid regarding the [structured clone algo](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). */ sendData({ taskId, data }: { taskId: string; data: T; }): void; /** * Return the Web Workers proxy. */ getWebWorkersProxy(): IWWorkerProxy; private getTaskChannel$; private getIdleWorkerOrCreate$; private createWorker$; /** * Start a worker with first task in its queue */ private pickTask; /** * Terminate all the workers. */ terminate(): void; /** * Return a reactive view presenting the workers' state & information. */ view(): WorkersPoolView; } export {};