Files
suaveplan/packages/foundation/data/web-workers/src/helpers/define-worker.ts
2026-05-27 23:47:53 +01:00

51 lines
2.0 KiB
TypeScript

import type { WorkerFactory, WorkerLike } from "../types/web-workers.types.js";
/** Options accepted by {@link defineWorker}. */
export interface DefineWorkerOptions {
/** Worker entrypoint URL — typically `new URL("./worker.ts", import.meta.url)`. */
readonly url: string | URL;
/** Worker type. Defaults to "module". */
readonly type?: "classic" | "module";
/**
* Optional override of the global `Worker` constructor. Provided for
* tests; production callers rely on the default (the DOM `Worker`).
*/
readonly workerConstructor?: WorkerConstructorLike;
}
/** Subset of `WorkerConstructor` used by {@link defineWorker}. */
export type WorkerConstructorLike = new (
url: string | URL,
options?: { type?: "classic" | "module" }
) => WorkerLike;
/**
* Bind a worker entrypoint URL + type into a reusable {@link WorkerFactory}.
*
* `WorkerManager` accepts a factory rather than a constructor so consumers
* can substitute fakes in tests. `defineWorker` is the canonical production
* factory — it wraps the standard `new Worker(url, { type })` pattern with
* the Vite/Webpack-friendly `new URL('./worker.ts', import.meta.url)` idiom
* and removes the temptation to inline `new Worker(...)` at call sites.
*/
export function defineWorker(opts: DefineWorkerOptions): WorkerFactory {
const baseType: "classic" | "module" = opts.type ?? "module";
const ctor = opts.workerConstructor ?? getGlobalWorker();
return (urlOverride, options) => {
const url = urlOverride ?? opts.url;
const type = options?.type ?? baseType;
return new ctor(url, { type });
};
}
function getGlobalWorker(): WorkerConstructorLike {
const globalCtor = (globalThis as { Worker?: WorkerConstructorLike })
.Worker;
if (globalCtor === undefined) {
throw new Error(
"defineWorker: globalThis.Worker is undefined; pass workerConstructor explicitly in non-browser environments."
);
}
return globalCtor;
}