190 lines
5.6 KiB
JavaScript
190 lines
5.6 KiB
JavaScript
// source/worker.js
|
|
import { parentPort, workerData } from "node:worker_threads";
|
|
|
|
// source/constants.js
|
|
var WORKER_FILE_NAME = "worker.mjs";
|
|
var WORKER_ACTION_APPLY = "[[WORKER_ACTION_APPLY]]";
|
|
var WORKER_ACTION_GET = "[[WORKER_ACTION_GET]]";
|
|
var WORKER_ACTION_OWN_KEYS = "[[WORKER_ACTION_OWN_KEYS]]";
|
|
var WORKER_ACTION_GET_INFORMATION = "[[WORKER_ACTION_GET_INFORMATION]]";
|
|
var VALUE_TYPE_FUNCTION = "[[VALUE_TYPE_FUNCTION]]";
|
|
var VALUE_TYPE_PRIMITIVE = "[[VALUE_TYPE_PRIMITIVE]]";
|
|
var VALUE_TYPE_PLAIN_OBJECT = "[[VALUE_TYPE_PLAIN_OBJECT]]";
|
|
var VALUE_TYPE_UNKNOWN = "[[VALUE_TYPE_UNKNOWN]]";
|
|
var WORKER_FILE = new URL(WORKER_FILE_NAME, import.meta.url);
|
|
var STDIO_STREAMS = ["stdout", "stderr"];
|
|
|
|
// source/get-value-information.js
|
|
var PRIMITIVE_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
"undefined",
|
|
"boolean",
|
|
"number",
|
|
"bigint",
|
|
"string"
|
|
]);
|
|
var isPrimitive = (value) => value === null || PRIMITIVE_VALUE_TYPES.has(typeof value);
|
|
function getPlainObjectPropertyInformation(object, key) {
|
|
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
if (!Object.hasOwn(descriptor, "value")) {
|
|
return;
|
|
}
|
|
const { value } = descriptor;
|
|
if (isPrimitive(value)) {
|
|
return { type: VALUE_TYPE_PRIMITIVE, value };
|
|
}
|
|
}
|
|
function getValueInformation(value) {
|
|
if (typeof value === "function") {
|
|
return { type: VALUE_TYPE_FUNCTION };
|
|
}
|
|
if (isPrimitive(value)) {
|
|
return { type: VALUE_TYPE_PRIMITIVE, value };
|
|
}
|
|
const information = { type: VALUE_TYPE_UNKNOWN };
|
|
if (Object.getPrototypeOf(value) === null) {
|
|
information.type = VALUE_TYPE_PLAIN_OBJECT;
|
|
information.isNullPrototypeObject = true;
|
|
}
|
|
if (value.constructor === Object) {
|
|
information.type = VALUE_TYPE_PLAIN_OBJECT;
|
|
}
|
|
if (information.type === VALUE_TYPE_PLAIN_OBJECT) {
|
|
information.properties = new Map(
|
|
Object.keys(value).map((property) => [
|
|
property,
|
|
getPlainObjectPropertyInformation(value, property)
|
|
])
|
|
);
|
|
}
|
|
return information;
|
|
}
|
|
var get_value_information_default = getValueInformation;
|
|
|
|
// source/lock.js
|
|
var STATE_UNLOCKED = 2;
|
|
var SIGNAL_INDEX = 0;
|
|
var unlock = (semaphore) => {
|
|
Atomics.store(semaphore, SIGNAL_INDEX, STATE_UNLOCKED);
|
|
Atomics.notify(semaphore, SIGNAL_INDEX, 1);
|
|
};
|
|
|
|
// source/property-path.js
|
|
var normalizePath = (propertyOrPath = []) => Array.isArray(propertyOrPath) ? propertyOrPath : [propertyOrPath];
|
|
|
|
// source/response.js
|
|
import process from "node:process";
|
|
import util from "node:util";
|
|
var processExit = process.exit;
|
|
var Response = class {
|
|
#responseSemaphore;
|
|
#responsePort;
|
|
#actionHandlers;
|
|
#stdio = [];
|
|
constructor(actionHandlers2) {
|
|
this.#actionHandlers = actionHandlers2;
|
|
process.exit = () => {
|
|
this.#terminate();
|
|
processExit();
|
|
};
|
|
for (const stream of STDIO_STREAMS) {
|
|
process[stream]._writev = (chunks, callback) => {
|
|
for (const { chunk } of chunks) {
|
|
this.#stdio.push({ stream, chunk });
|
|
}
|
|
callback();
|
|
};
|
|
}
|
|
}
|
|
#send(response) {
|
|
const responsePort = this.#responsePort;
|
|
try {
|
|
responsePort.postMessage({ ...response, stdio: this.#stdio });
|
|
} catch {
|
|
const error = new Error(
|
|
`Cannot serialize worker response:
|
|
${util.inspect(response.result)}`
|
|
);
|
|
responsePort.postMessage({ error, stdio: this.#stdio });
|
|
} finally {
|
|
this.#finish();
|
|
}
|
|
}
|
|
#sendResult(result) {
|
|
this.#send({ result });
|
|
}
|
|
#throws(error) {
|
|
this.#send({ error, errorData: { ...error } });
|
|
}
|
|
#finish() {
|
|
unlock(this.#responseSemaphore);
|
|
process.exitCode = void 0;
|
|
this.#responsePort.close();
|
|
this.#responseSemaphore = void 0;
|
|
this.#responsePort = void 0;
|
|
this.#stdio.length = 0;
|
|
}
|
|
#terminate() {
|
|
this.#send({ terminated: true });
|
|
}
|
|
#processAction(action, payload) {
|
|
const actionHandlers2 = this.#actionHandlers;
|
|
if (!actionHandlers2.has(action)) {
|
|
throw new Error(`Unknown action '${action}'.`);
|
|
}
|
|
return actionHandlers2.get(action)(payload);
|
|
}
|
|
listen(receivePort) {
|
|
receivePort.addListener(
|
|
"message",
|
|
async ({ responseSemaphore, responsePort, action, payload }) => {
|
|
this.#responseSemaphore = responseSemaphore;
|
|
this.#responsePort = responsePort;
|
|
try {
|
|
this.#sendResult(await this.#processAction(action, payload));
|
|
} catch (error) {
|
|
this.#throws(error);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
};
|
|
var response_default = Response;
|
|
|
|
// source/worker.js
|
|
var moduleImportPromise;
|
|
var module;
|
|
async function getValue(payload) {
|
|
moduleImportPromise ??= import(workerData.moduleId);
|
|
module ??= await moduleImportPromise;
|
|
let value = module;
|
|
let receiver;
|
|
for (const property of normalizePath(payload.path)) {
|
|
receiver = value;
|
|
value = Reflect.get(value, property, value);
|
|
}
|
|
return { value, receiver };
|
|
}
|
|
var createHandler = (handler) => async (payload) => handler(await getValue(payload), payload);
|
|
var actionHandlers = new Map(
|
|
[
|
|
[WORKER_ACTION_GET, ({ value }) => value],
|
|
[
|
|
WORKER_ACTION_APPLY,
|
|
({ value: method, receiver }, { argumentsList }) => Reflect.apply(method, receiver, argumentsList)
|
|
],
|
|
[
|
|
WORKER_ACTION_OWN_KEYS,
|
|
({ value }) => Reflect.ownKeys(value).filter((key) => typeof key !== "symbol")
|
|
],
|
|
[WORKER_ACTION_GET_INFORMATION, ({ value }) => get_value_information_default(value)]
|
|
].map(([action, handler]) => [action, createHandler(handler)])
|
|
);
|
|
if (parentPort) {
|
|
const response = new response_default(actionHandlers);
|
|
response.listen(parentPort);
|
|
}
|
|
var workerRunningSemaphore = workerData?.workerRunningSemaphore;
|
|
if (workerRunningSemaphore) {
|
|
unlock(workerRunningSemaphore);
|
|
}
|