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; }