@loaders.gl/worker-utils
Version:
Utilities for running tasks on worker threads
1,130 lines (1,107 loc) • 34.6 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
AsyncQueue: () => AsyncQueue,
ChildProcessProxy: () => ChildProcessProxy,
NullWorker: () => NullWorker,
WorkerBody: () => WorkerBody,
WorkerFarm: () => WorkerFarm,
WorkerJob: () => WorkerJob,
WorkerPool: () => WorkerPool,
WorkerThread: () => WorkerThread,
assert: () => assert,
canProcessOnWorker: () => canProcessOnWorker,
createWorker: () => createWorker,
getLibraryUrl: () => getLibraryUrl,
getTransferList: () => getTransferList,
getTransferListForWriter: () => getTransferListForWriter,
getWorkerURL: () => getWorkerURL,
isBrowser: () => isBrowser,
isWorker: () => isWorker,
loadLibrary: () => loadLibrary,
processOnWorker: () => processOnWorker,
validateWorkerVersion: () => validateWorkerVersion
});
module.exports = __toCommonJS(dist_exports);
// dist/lib/env-utils/version.js
var NPM_TAG = "latest";
function getVersion() {
var _a;
if (!((_a = globalThis._loadersgl_) == null ? void 0 : _a.version)) {
globalThis._loadersgl_ = globalThis._loadersgl_ || {};
if (false) {
console.warn("loaders.gl: The __VERSION__ variable is not injected using babel plugin. Latest unstable workers would be fetched from the CDN.");
globalThis._loadersgl_.version = NPM_TAG;
} else {
globalThis._loadersgl_.version = "4.3.2";
}
}
return globalThis._loadersgl_.version;
}
var VERSION = getVersion();
// dist/lib/env-utils/assert.js
function assert(condition, message) {
if (!condition) {
throw new Error(message || "loaders.gl assertion failed.");
}
}
// dist/lib/env-utils/globals.js
var globals = {
self: typeof self !== "undefined" && self,
window: typeof window !== "undefined" && window,
global: typeof global !== "undefined" && global,
document: typeof document !== "undefined" && document
};
var self_ = globals.self || globals.window || globals.global || {};
var window_ = globals.window || globals.self || globals.global || {};
var global_ = globals.global || globals.self || globals.window || {};
var document_ = globals.document || {};
var isBrowser = (
// @ts-ignore process.browser
typeof process !== "object" || String(process) !== "[object process]" || process.browser
);
var isWorker = typeof importScripts === "function";
var isMobile = typeof window !== "undefined" && typeof window.orientation !== "undefined";
var matches = typeof process !== "undefined" && process.version && /v([0-9]*)/.exec(process.version);
var nodeVersion = matches && parseFloat(matches[1]) || 0;
// dist/lib/worker-farm/worker-job.js
var WorkerJob = class {
name;
workerThread;
isRunning = true;
/** Promise that resolves when Job is done */
result;
_resolve = () => {
};
_reject = () => {
};
constructor(jobName, workerThread) {
this.name = jobName;
this.workerThread = workerThread;
this.result = new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
}
/**
* Send a message to the job's worker thread
* @param data any data structure, ideally consisting mostly of transferrable objects
*/
postMessage(type, payload) {
this.workerThread.postMessage({
source: "loaders.gl",
// Lets worker ignore unrelated messages
type,
payload
});
}
/**
* Call to resolve the `result` Promise with the supplied value
*/
done(value) {
assert(this.isRunning);
this.isRunning = false;
this._resolve(value);
}
/**
* Call to reject the `result` Promise with the supplied error
*/
error(error) {
assert(this.isRunning);
this.isRunning = false;
this._reject(error);
}
};
// dist/lib/node/worker_threads-browser.js
var NodeWorker = class {
terminate() {
}
};
var parentPort = null;
// dist/lib/worker-utils/get-loadable-worker-url.js
var workerURLCache = /* @__PURE__ */ new Map();
function getLoadableWorkerURL(props) {
assert(props.source && !props.url || !props.source && props.url);
let workerURL = workerURLCache.get(props.source || props.url);
if (!workerURL) {
if (props.url) {
workerURL = getLoadableWorkerURLFromURL(props.url);
workerURLCache.set(props.url, workerURL);
}
if (props.source) {
workerURL = getLoadableWorkerURLFromSource(props.source);
workerURLCache.set(props.source, workerURL);
}
}
assert(workerURL);
return workerURL;
}
function getLoadableWorkerURLFromURL(url) {
if (!url.startsWith("http")) {
return url;
}
const workerSource = buildScriptSource(url);
return getLoadableWorkerURLFromSource(workerSource);
}
function getLoadableWorkerURLFromSource(workerSource) {
const blob = new Blob([workerSource], { type: "application/javascript" });
return URL.createObjectURL(blob);
}
function buildScriptSource(workerUrl) {
return `try {
importScripts('${workerUrl}');
} catch (error) {
console.error(error);
throw error;
}`;
}
// dist/lib/worker-utils/get-transfer-list.js
function getTransferList(object, recursive = true, transfers) {
const transfersSet = transfers || /* @__PURE__ */ new Set();
if (!object) {
} else if (isTransferable(object)) {
transfersSet.add(object);
} else if (isTransferable(object.buffer)) {
transfersSet.add(object.buffer);
} else if (ArrayBuffer.isView(object)) {
} else if (recursive && typeof object === "object") {
for (const key in object) {
getTransferList(object[key], recursive, transfersSet);
}
}
return transfers === void 0 ? Array.from(transfersSet) : [];
}
function isTransferable(object) {
if (!object) {
return false;
}
if (object instanceof ArrayBuffer) {
return true;
}
if (typeof MessagePort !== "undefined" && object instanceof MessagePort) {
return true;
}
if (typeof ImageBitmap !== "undefined" && object instanceof ImageBitmap) {
return true;
}
if (typeof OffscreenCanvas !== "undefined" && object instanceof OffscreenCanvas) {
return true;
}
return false;
}
function getTransferListForWriter(object) {
if (object === null) {
return {};
}
const clone = Object.assign({}, object);
Object.keys(clone).forEach((key) => {
if (typeof object[key] === "object" && !ArrayBuffer.isView(object[key]) && !(object[key] instanceof Array)) {
clone[key] = getTransferListForWriter(object[key]);
} else if (typeof clone[key] === "function" || clone[key] instanceof RegExp) {
clone[key] = {};
} else {
clone[key] = object[key];
}
});
return clone;
}
// dist/lib/worker-farm/worker-thread.js
var NOOP = () => {
};
var WorkerThread = class {
name;
source;
url;
terminated = false;
worker;
onMessage;
onError;
_loadableURL = "";
/** Checks if workers are supported on this platform */
static isSupported() {
return typeof Worker !== "undefined" && isBrowser || typeof NodeWorker !== "undefined" && !isBrowser;
}
constructor(props) {
const { name, source, url } = props;
assert(source || url);
this.name = name;
this.source = source;
this.url = url;
this.onMessage = NOOP;
this.onError = (error) => console.log(error);
this.worker = isBrowser ? this._createBrowserWorker() : this._createNodeWorker();
}
/**
* Terminate this worker thread
* @note Can free up significant memory
*/
destroy() {
this.onMessage = NOOP;
this.onError = NOOP;
this.worker.terminate();
this.terminated = true;
}
get isRunning() {
return Boolean(this.onMessage);
}
/**
* Send a message to this worker thread
* @param data any data structure, ideally consisting mostly of transferrable objects
* @param transferList If not supplied, calculated automatically by traversing data
*/
postMessage(data, transferList) {
transferList = transferList || getTransferList(data);
this.worker.postMessage(data, transferList);
}
// PRIVATE
/**
* Generate a standard Error from an ErrorEvent
* @param event
*/
_getErrorFromErrorEvent(event) {
let message = "Failed to load ";
message += `worker ${this.name} from ${this.url}. `;
if (event.message) {
message += `${event.message} in `;
}
if (event.lineno) {
message += `:${event.lineno}:${event.colno}`;
}
return new Error(message);
}
/**
* Creates a worker thread on the browser
*/
_createBrowserWorker() {
this._loadableURL = getLoadableWorkerURL({ source: this.source, url: this.url });
const worker = new Worker(this._loadableURL, { name: this.name });
worker.onmessage = (event) => {
if (!event.data) {
this.onError(new Error("No data received"));
} else {
this.onMessage(event.data);
}
};
worker.onerror = (error) => {
this.onError(this._getErrorFromErrorEvent(error));
this.terminated = true;
};
worker.onmessageerror = (event) => console.error(event);
return worker;
}
/**
* Creates a worker thread in node.js
* @todo https://nodejs.org/api/async_hooks.html#async-resource-worker-pool
*/
_createNodeWorker() {
let worker;
if (this.url) {
const absolute = this.url.includes(":/") || this.url.startsWith("/");
const url = absolute ? this.url : `./${this.url}`;
worker = new NodeWorker(url, { eval: false });
} else if (this.source) {
worker = new NodeWorker(this.source, { eval: true });
} else {
throw new Error("no worker");
}
worker.on("message", (data) => {
this.onMessage(data);
});
worker.on("error", (error) => {
this.onError(error);
});
worker.on("exit", (code) => {
});
return worker;
}
};
// dist/lib/worker-farm/worker-pool.js
var WorkerPool = class {
name = "unnamed";
source;
// | Function;
url;
maxConcurrency = 1;
maxMobileConcurrency = 1;
onDebug = () => {
};
reuseWorkers = true;
props = {};
jobQueue = [];
idleQueue = [];
count = 0;
isDestroyed = false;
/** Checks if workers are supported on this platform */
static isSupported() {
return WorkerThread.isSupported();
}
/**
* @param processor - worker function
* @param maxConcurrency - max count of workers
*/
constructor(props) {
this.source = props.source;
this.url = props.url;
this.setProps(props);
}
/**
* Terminates all workers in the pool
* @note Can free up significant memory
*/
destroy() {
this.idleQueue.forEach((worker) => worker.destroy());
this.isDestroyed = true;
}
setProps(props) {
this.props = { ...this.props, ...props };
if (props.name !== void 0) {
this.name = props.name;
}
if (props.maxConcurrency !== void 0) {
this.maxConcurrency = props.maxConcurrency;
}
if (props.maxMobileConcurrency !== void 0) {
this.maxMobileConcurrency = props.maxMobileConcurrency;
}
if (props.reuseWorkers !== void 0) {
this.reuseWorkers = props.reuseWorkers;
}
if (props.onDebug !== void 0) {
this.onDebug = props.onDebug;
}
}
async startJob(name, onMessage2 = (job, type, data) => job.done(data), onError = (job, error) => job.error(error)) {
const startPromise = new Promise((onStart) => {
this.jobQueue.push({ name, onMessage: onMessage2, onError, onStart });
return this;
});
this._startQueuedJob();
return await startPromise;
}
// PRIVATE
/**
* Starts first queued job if worker is available or can be created
* Called when job is started and whenever a worker returns to the idleQueue
*/
async _startQueuedJob() {
if (!this.jobQueue.length) {
return;
}
const workerThread = this._getAvailableWorker();
if (!workerThread) {
return;
}
const queuedJob = this.jobQueue.shift();
if (queuedJob) {
this.onDebug({
message: "Starting job",
name: queuedJob.name,
workerThread,
backlog: this.jobQueue.length
});
const job = new WorkerJob(queuedJob.name, workerThread);
workerThread.onMessage = (data) => queuedJob.onMessage(job, data.type, data.payload);
workerThread.onError = (error) => queuedJob.onError(job, error);
queuedJob.onStart(job);
try {
await job.result;
} catch (error) {
console.error(`Worker exception: ${error}`);
} finally {
this.returnWorkerToQueue(workerThread);
}
}
}
/**
* Returns a worker to the idle queue
* Destroys the worker if
* - pool is destroyed
* - if this pool doesn't reuse workers
* - if maxConcurrency has been lowered
* @param worker
*/
returnWorkerToQueue(worker) {
const shouldDestroyWorker = (
// Workers on Node.js prevent the process from exiting.
// Until we figure out how to close them before exit, we always destroy them
!isBrowser || // If the pool is destroyed, there is no reason to keep the worker around
this.isDestroyed || // If the app has disabled worker reuse, any completed workers should be destroyed
!this.reuseWorkers || // If concurrency has been lowered, this worker might be surplus to requirements
this.count > this._getMaxConcurrency()
);
if (shouldDestroyWorker) {
worker.destroy();
this.count--;
} else {
this.idleQueue.push(worker);
}
if (!this.isDestroyed) {
this._startQueuedJob();
}
}
/**
* Returns idle worker or creates new worker if maxConcurrency has not been reached
*/
_getAvailableWorker() {
if (this.idleQueue.length > 0) {
return this.idleQueue.shift() || null;
}
if (this.count < this._getMaxConcurrency()) {
this.count++;
const name = `${this.name.toLowerCase()} (#${this.count} of ${this.maxConcurrency})`;
return new WorkerThread({ name, source: this.source, url: this.url });
}
return null;
}
_getMaxConcurrency() {
return isMobile ? this.maxMobileConcurrency : this.maxConcurrency;
}
};
// dist/lib/worker-farm/worker-farm.js
var DEFAULT_PROPS = {
maxConcurrency: 3,
maxMobileConcurrency: 1,
reuseWorkers: true,
onDebug: () => {
}
};
var _WorkerFarm = class {
props;
workerPools = /* @__PURE__ */ new Map();
/** Checks if workers are supported on this platform */
static isSupported() {
return WorkerThread.isSupported();
}
/** Get the singleton instance of the global worker farm */
static getWorkerFarm(props = {}) {
_WorkerFarm._workerFarm = _WorkerFarm._workerFarm || new _WorkerFarm({});
_WorkerFarm._workerFarm.setProps(props);
return _WorkerFarm._workerFarm;
}
/** get global instance with WorkerFarm.getWorkerFarm() */
constructor(props) {
this.props = { ...DEFAULT_PROPS };
this.setProps(props);
this.workerPools = /* @__PURE__ */ new Map();
}
/**
* Terminate all workers in the farm
* @note Can free up significant memory
*/
destroy() {
for (const workerPool of this.workerPools.values()) {
workerPool.destroy();
}
this.workerPools = /* @__PURE__ */ new Map();
}
/**
* Set props used when initializing worker pools
* @param props
*/
setProps(props) {
this.props = { ...this.props, ...props };
for (const workerPool of this.workerPools.values()) {
workerPool.setProps(this._getWorkerPoolProps());
}
}
/**
* Returns a worker pool for the specified worker
* @param options - only used first time for a specific worker name
* @param options.name - the name of the worker - used to identify worker pool
* @param options.url -
* @param options.source -
* @example
* const job = WorkerFarm.getWorkerFarm().getWorkerPool({name, url}).startJob(...);
*/
getWorkerPool(options2) {
const { name, source, url } = options2;
let workerPool = this.workerPools.get(name);
if (!workerPool) {
workerPool = new WorkerPool({
name,
source,
url
});
workerPool.setProps(this._getWorkerPoolProps());
this.workerPools.set(name, workerPool);
}
return workerPool;
}
_getWorkerPoolProps() {
return {
maxConcurrency: this.props.maxConcurrency,
maxMobileConcurrency: this.props.maxMobileConcurrency,
reuseWorkers: this.props.reuseWorkers,
onDebug: this.props.onDebug
};
}
};
var WorkerFarm = _WorkerFarm;
// singleton
__publicField(WorkerFarm, "_workerFarm");
// dist/lib/worker-farm/worker-body.js
async function getParentPort() {
return parentPort;
}
var onMessageWrapperMap = /* @__PURE__ */ new Map();
var WorkerBody = class {
/** Check that we are actually in a worker thread */
static async inWorkerThread() {
return typeof self !== "undefined" || Boolean(await getParentPort());
}
/*
* (type: WorkerMessageType, payload: WorkerMessagePayload) => any
*/
static set onmessage(onMessage2) {
async function handleMessage(message) {
const parentPort2 = await getParentPort();
const { type, payload } = parentPort2 ? message : message.data;
onMessage2(type, payload);
}
getParentPort().then((parentPort2) => {
if (parentPort2) {
parentPort2.on("message", (message) => {
handleMessage(message);
});
parentPort2.on("exit", () => console.debug("Node worker closing"));
} else {
globalThis.onmessage = handleMessage;
}
});
}
static async addEventListener(onMessage2) {
let onMessageWrapper = onMessageWrapperMap.get(onMessage2);
if (!onMessageWrapper) {
onMessageWrapper = async (message) => {
if (!isKnownMessage(message)) {
return;
}
const parentPort3 = await getParentPort();
const { type, payload } = parentPort3 ? message : message.data;
onMessage2(type, payload);
};
}
const parentPort2 = await getParentPort();
if (parentPort2) {
console.error("not implemented");
} else {
globalThis.addEventListener("message", onMessageWrapper);
}
}
static async removeEventListener(onMessage2) {
const onMessageWrapper = onMessageWrapperMap.get(onMessage2);
onMessageWrapperMap.delete(onMessage2);
const parentPort2 = await getParentPort();
if (parentPort2) {
console.error("not implemented");
} else {
globalThis.removeEventListener("message", onMessageWrapper);
}
}
/**
* Send a message from a worker to creating thread (main thread)
* @param type
* @param payload
*/
static async postMessage(type, payload) {
const data = { source: "loaders.gl", type, payload };
const transferList = getTransferList(payload);
const parentPort2 = await getParentPort();
if (parentPort2) {
parentPort2.postMessage(data, transferList);
} else {
globalThis.postMessage(data, transferList);
}
}
};
function isKnownMessage(message) {
const { type, data } = message;
return type === "message" && data && typeof data.source === "string" && data.source.startsWith("loaders.gl");
}
// dist/lib/worker-api/get-worker-url.js
function getWorkerName(worker) {
const warning = worker.version !== VERSION ? ` (worker-utils@${VERSION})` : "";
return `${worker.name}@${worker.version}${warning}`;
}
function getWorkerURL(worker, options2 = {}) {
const workerOptions = options2[worker.id] || {};
const workerFile = isBrowser ? `${worker.id}-worker.js` : `${worker.id}-worker-node.js`;
let url = workerOptions.workerUrl;
if (!url && worker.id === "compression") {
url = options2.workerUrl;
}
if (options2._workerType === "test") {
if (isBrowser) {
url = `modules/${worker.module}/dist/${workerFile}`;
} else {
url = `modules/${worker.module}/src/workers/${worker.id}-worker-node.ts`;
}
}
if (!url) {
let version = worker.version;
if (version === "latest") {
version = NPM_TAG;
}
const versionTag = version ? `@${version}` : "";
url = `https://unpkg.com/@loaders.gl/${worker.module}${versionTag}/dist/${workerFile}`;
}
assert(url);
return url;
}
// dist/lib/worker-api/process-on-worker.js
function canProcessOnWorker(worker, options2) {
if (!WorkerFarm.isSupported()) {
return false;
}
return worker.worker && (options2 == null ? void 0 : options2.worker);
}
async function processOnWorker(worker, data, options2 = {}, context = {}) {
const name = getWorkerName(worker);
const workerFarm = WorkerFarm.getWorkerFarm(options2);
const { source } = options2;
const workerPoolProps = { name, source };
if (!source) {
workerPoolProps.url = getWorkerURL(worker, options2);
}
const workerPool = workerFarm.getWorkerPool(workerPoolProps);
const jobName = options2.jobName || worker.name;
const job = await workerPool.startJob(
jobName,
// eslint-disable-next-line
onMessage.bind(null, context)
);
const transferableOptions = getTransferListForWriter(options2);
job.postMessage("process", { input: data, options: transferableOptions });
const result = await job.result;
return result.result;
}
async function onMessage(context, job, type, payload) {
switch (type) {
case "done":
job.done(payload);
break;
case "error":
job.error(new Error(payload.error));
break;
case "process":
const { id, input, options: options2 } = payload;
try {
if (!context.process) {
job.postMessage("error", { id, error: "Worker not set up to process on main thread" });
return;
}
const result = await context.process(input, options2);
job.postMessage("done", { id, result });
} catch (error) {
const message = error instanceof Error ? error.message : "unknown error";
job.postMessage("error", { id, error: message });
}
break;
default:
console.warn(`process-on-worker: unknown message ${type}`);
}
}
// dist/lib/async-queue/async-queue.js
var AsyncQueue = class {
_values;
_settlers;
_closed;
constructor() {
this._values = [];
this._settlers = [];
this._closed = false;
}
/** Return an async iterator for this queue */
[Symbol.asyncIterator]() {
return this;
}
/** Push a new value - the async iterator will yield a promise resolved to this value */
push(value) {
return this.enqueue(value);
}
/**
* Push a new value - the async iterator will yield a promise resolved to this value
* Add an error - the async iterator will yield a promise rejected with this value
*/
enqueue(value) {
if (this._closed) {
throw new Error("Closed");
}
if (this._settlers.length > 0) {
if (this._values.length > 0) {
throw new Error("Illegal internal state");
}
const settler = this._settlers.shift();
if (value instanceof Error) {
settler.reject(value);
} else {
settler.resolve({ value });
}
} else {
this._values.push(value);
}
}
/** Indicate that we not waiting for more values - The async iterator will be done */
close() {
while (this._settlers.length > 0) {
const settler = this._settlers.shift();
settler.resolve({ done: true });
}
this._closed = true;
}
// ITERATOR IMPLEMENTATION
/** @returns a Promise for an IteratorResult */
next() {
if (this._values.length > 0) {
const value = this._values.shift();
if (value instanceof Error) {
return Promise.reject(value);
}
return Promise.resolve({ done: false, value });
}
if (this._closed) {
if (this._settlers.length > 0) {
throw new Error("Illegal internal state");
}
return Promise.resolve({ done: true, value: void 0 });
}
return new Promise((resolve, reject) => {
this._settlers.push({ resolve, reject });
});
}
};
// dist/lib/worker-api/create-worker.js
var requestId = 0;
var inputBatches;
var options;
async function createWorker(process2, processInBatches) {
if (!await WorkerBody.inWorkerThread()) {
return;
}
const context = {
process: processOnMainThread
};
WorkerBody.onmessage = async (type, payload) => {
try {
switch (type) {
case "process":
if (!process2) {
throw new Error("Worker does not support atomic processing");
}
const result = await process2(payload.input, payload.options || {}, context);
WorkerBody.postMessage("done", { result });
break;
case "process-in-batches":
if (!processInBatches) {
throw new Error("Worker does not support batched processing");
}
inputBatches = new AsyncQueue();
options = payload.options || {};
const resultIterator = processInBatches(inputBatches, options, context);
for await (const batch of resultIterator) {
WorkerBody.postMessage("output-batch", { result: batch });
}
WorkerBody.postMessage("done", {});
break;
case "input-batch":
inputBatches.push(payload.input);
break;
case "input-done":
inputBatches.close();
break;
default:
}
} catch (error) {
const message = error instanceof Error ? error.message : "";
WorkerBody.postMessage("error", { error: message });
}
};
}
function processOnMainThread(arrayBuffer, options2 = {}) {
return new Promise((resolve, reject) => {
const id = requestId++;
const onMessage2 = (type, payload2) => {
if (payload2.id !== id) {
return;
}
switch (type) {
case "done":
WorkerBody.removeEventListener(onMessage2);
resolve(payload2.result);
break;
case "error":
WorkerBody.removeEventListener(onMessage2);
reject(payload2.error);
break;
default:
}
};
WorkerBody.addEventListener(onMessage2);
const payload = { id, input: arrayBuffer, options: options2 };
WorkerBody.postMessage("process", payload);
});
}
// dist/lib/worker-api/validate-worker-version.js
function validateWorkerVersion(worker, coreVersion = VERSION) {
assert(worker, "no worker provided");
const workerVersion = worker.version;
if (!coreVersion || !workerVersion) {
return false;
}
return true;
}
// dist/lib/library-utils/library-utils.js
var loadLibraryPromises = {};
async function loadLibrary(libraryUrl, moduleName = null, options2 = {}, libraryName = null) {
if (moduleName) {
libraryUrl = getLibraryUrl(libraryUrl, moduleName, options2, libraryName);
}
loadLibraryPromises[libraryUrl] = // eslint-disable-next-line @typescript-eslint/no-misused-promises
loadLibraryPromises[libraryUrl] || loadLibraryFromFile(libraryUrl);
return await loadLibraryPromises[libraryUrl];
}
function getLibraryUrl(library, moduleName, options2 = {}, libraryName = null) {
if (!options2.useLocalLibraries && library.startsWith("http")) {
return library;
}
libraryName = libraryName || library;
const modules = options2.modules || {};
if (modules[libraryName]) {
return modules[libraryName];
}
if (!isBrowser) {
return `modules/${moduleName}/dist/libs/${libraryName}`;
}
if (options2.CDN) {
assert(options2.CDN.startsWith("http"));
return `${options2.CDN}/${moduleName}@${VERSION}/dist/libs/${libraryName}`;
}
if (isWorker) {
return `../src/libs/${libraryName}`;
}
return `modules/${moduleName}/src/libs/${libraryName}`;
}
async function loadLibraryFromFile(libraryUrl) {
if (libraryUrl.endsWith("wasm")) {
return await loadAsArrayBuffer(libraryUrl);
}
if (!isBrowser) {
try {
const { requireFromFile } = globalThis.loaders || {};
return await (requireFromFile == null ? void 0 : requireFromFile(libraryUrl));
} catch (error) {
console.error(error);
return null;
}
}
if (isWorker) {
return importScripts(libraryUrl);
}
const scriptSource = await loadAsText(libraryUrl);
return loadLibraryFromString(scriptSource, libraryUrl);
}
function loadLibraryFromString(scriptSource, id) {
if (!isBrowser) {
const { requireFromString } = globalThis.loaders || {};
return requireFromString == null ? void 0 : requireFromString(scriptSource, id);
}
if (isWorker) {
eval.call(globalThis, scriptSource);
return null;
}
const script = document.createElement("script");
script.id = id;
try {
script.appendChild(document.createTextNode(scriptSource));
} catch (e) {
script.text = scriptSource;
}
document.body.appendChild(script);
return null;
}
async function loadAsArrayBuffer(url) {
const { readFileAsArrayBuffer } = globalThis.loaders || {};
if (isBrowser || !readFileAsArrayBuffer || url.startsWith("http")) {
const response = await fetch(url);
return await response.arrayBuffer();
}
return await readFileAsArrayBuffer(url);
}
async function loadAsText(url) {
const { readFileAsText } = globalThis.loaders || {};
if (isBrowser || !readFileAsText || url.startsWith("http")) {
const response = await fetch(url);
return await response.text();
}
return await readFileAsText(url);
}
// dist/lib/process-utils/child-process-proxy.js
var ChildProcess2 = __toESM(require("child_process"), 1);
// dist/lib/process-utils/process-utils.js
var import_child_process = __toESM(require("child_process"), 1);
function getAvailablePort(defaultPort = 3e3) {
return new Promise((resolve) => {
import_child_process.default.exec("lsof -i -P -n | grep LISTEN", (error, stdout) => {
if (error) {
resolve(defaultPort);
return;
}
const portsInUse = [];
const regex = /:(\d+) \(LISTEN\)/;
stdout.split("\n").forEach((line) => {
const match = regex.exec(line);
if (match) {
portsInUse.push(Number(match[1]));
}
});
let port = defaultPort;
while (portsInUse.includes(port)) {
port++;
}
resolve(port);
});
});
}
// dist/lib/process-utils/child-process-proxy.js
var DEFAULT_PROPS2 = {
command: "",
arguments: [],
port: 5e3,
autoPort: true,
wait: 2e3,
onSuccess: (processProxy) => {
console.log(`Started ${processProxy.props.command}`);
}
};
var ChildProcessProxy = class {
id;
props = { ...DEFAULT_PROPS2 };
childProcess = null;
port = 0;
successTimer;
// NodeJS.Timeout;
// constructor(props?: {id?: string});
constructor({ id = "browser-driver" } = {}) {
this.id = id;
}
/** Starts a child process with the provided props */
async start(props) {
props = { ...DEFAULT_PROPS2, ...props };
this.props = props;
const args = [...props.arguments];
this.port = Number(props.port);
if (props.portArg) {
if (props.autoPort) {
this.port = await getAvailablePort(props.port);
}
args.push(props.portArg, String(this.port));
}
return await new Promise((resolve, reject) => {
try {
this._setTimeout(() => {
if (props.onSuccess) {
props.onSuccess(this);
}
resolve({});
});
console.log(`Spawning ${props.command} ${props.arguments.join(" ")}`);
const childProcess = ChildProcess2.spawn(props.command, args, props.spawn);
this.childProcess = childProcess;
childProcess.stdout.on("data", (data) => {
console.log(data.toString());
});
childProcess.stderr.on("data", (data) => {
console.log(`Child process wrote to stderr: "${data}".`);
if (!props.ignoreStderr) {
this._clearTimeout();
reject(new Error(data));
}
});
childProcess.on("error", (error) => {
console.log(`Child process errored with ${error}`);
this._clearTimeout();
reject(error);
});
childProcess.on("close", (code) => {
console.log(`Child process exited with ${code}`);
this.childProcess = null;
this._clearTimeout();
resolve({});
});
} catch (error) {
reject(error);
}
});
}
/** Stops a running child process */
async stop() {
if (this.childProcess) {
this.childProcess.kill();
this.childProcess = null;
}
}
/** Exits this process */
async exit(statusCode = 0) {
try {
await this.stop();
process.exit(statusCode);
} catch (error) {
console.error(error.message || error);
process.exit(1);
}
}
_setTimeout(callback) {
if (Number(this.props.wait) > 0) {
this.successTimer = setTimeout(callback, this.props.wait);
}
}
_clearTimeout() {
if (this.successTimer) {
clearTimeout(this.successTimer);
}
}
};
// dist/index.js
var NullWorker = {
id: "null",
name: "null",
module: "worker-utils",
version: VERSION,
options: {
null: {}
}
};
//# sourceMappingURL=index.cjs.map