UNPKG

@ayonli/jsext

Version:

A JavaScript extension package for building strong and modern applications.

232 lines (228 loc) 8.41 kB
'use strict'; var index = require('../external/check-iterable/index.js'); var parallel_channel = require('./channel.js'); var parallel_module = require('./module.js'); var object = require('../object.js'); var error = require('../error.js'); var error_Exception = require('../error/Exception.js'); const pendingTasks = new Map(); /** * For some reason, in Node.js and Bun, when import expression throws an * module/package not found error, the error can not be serialized and sent to * the other thread properly. We need to check this situation and sent the error * as plain object instead. */ function isModuleResolveError(value) { var _a; if (typeof value === "object" && typeof (value === null || value === void 0 ? void 0 : value.message) === "string" && /Cannot find (module|package)/.test(value === null || value === void 0 ? void 0 : value.message)) { return (value instanceof Error) // Node.js (possibly bug) || ((_a = value.constructor) === null || _a === void 0 ? void 0 : _a.name) === "Error"; // Bun (doesn't inherit from Error) } return false; } function removeUnserializableProperties(obj) { const _obj = {}; for (const key of Reflect.ownKeys(obj)) { if (typeof obj[key] !== "bigint" && typeof obj[key] !== "function") { _obj[key] = obj[key]; } } return _obj; } function unwrapArgs(args, channelWrite) { return args.map(arg => { if (object.isPlainObject(arg)) { if (arg["@@type"] === "Channel" && typeof arg["@@id"] === "number") { return parallel_channel.unwrapChannel(arg, channelWrite); } else if (arg["@@type"] === "Exception" || arg["@@type"] === "DOMException" || arg["@@type"] === "AggregateError") { return error.fromObject(arg); } } return arg; }); } function wrapReturnValue(value) { const transferable = []; if (value instanceof ArrayBuffer) { transferable.push(value); } else if ((value instanceof error_Exception.default) || error.isDOMException(value) || error.isAggregateError(value) || isModuleResolveError(value)) { value = error.toObject(value); } else if (object.isPlainObject(value)) { for (const key of Object.getOwnPropertyNames(value)) { const _value = value[key]; if (_value instanceof ArrayBuffer) { transferable.push(_value); } else if ((_value instanceof error_Exception.default) || error.isDOMException(_value) || error.isAggregateError(_value) || isModuleResolveError(_value)) { value[key] = error.toObject(_value); } } } else if (Array.isArray(value)) { value = value.map(item => { if (item instanceof ArrayBuffer) { transferable.push(item); return item; } else if ((item instanceof error_Exception.default) || error.isDOMException(item) || error.isAggregateError(item) || isModuleResolveError(item)) { return error.toObject(item); } else { return item; } }); } return { value, transferable }; } /** * @ignore * @internal */ function isCallRequest(msg) { return msg && typeof msg === "object" && ((msg.type === "call" && typeof msg.module === "string" && typeof msg.fn === "string") || (["next", "return", "throw"].includes(msg.type) && typeof msg.taskId === "number")) && Array.isArray(msg.args); } /** * @ignore * @internal */ async function handleCallRequest(msg, reply) { const _reply = reply; reply = (res) => { if (res.type === "error") { if ((res.error instanceof error_Exception.default) || error.isDOMException(res.error) || error.isAggregateError(res.error) || isModuleResolveError(res.error)) { return _reply({ ...res, error: removeUnserializableProperties(error.toObject(res.error)), }); } try { return _reply(res); } catch (_a) { // In case the error cannot be cloned directly, fallback to // transferring it as an object and rebuild in the main thread. return _reply({ ...res, error: removeUnserializableProperties(error.toObject(res.error)), }); } } else { return _reply(res); } }; msg.args = unwrapArgs(msg.args, (type, msg, channelId) => { reply({ type, value: msg, channelId }); }); try { if (msg.taskId && ["next", "return", "throw"].includes(msg.type)) { const req = msg; const task = pendingTasks.get(req.taskId); if (task) { if (req.type === "throw") { try { await task.throw(req.args[0]); } catch (error) { reply({ type: "error", error, taskId: req.taskId }); } } else if (req.type === "return") { try { const res = await task.return(req.args[0]); const { value, transferable } = wrapReturnValue(res.value); reply({ type: "yield", value, done: res.done, taskId: req.taskId, }, transferable); } catch (error) { reply({ type: "error", error, taskId: req.taskId }); } } else { // req.type === "next" try { const res = await task.next(req.args[0]); const { value, transferable } = wrapReturnValue(res.value); reply({ type: "yield", value, done: res.done, taskId: req.taskId, }, transferable); } catch (error) { reply({ type: "error", error, taskId: req.taskId }); } } } else { reply({ type: "error", error: new ReferenceError(`task (${req.taskId}) doesn't exists`), taskId: req.taskId, }); } return; } const req = msg; const module = await parallel_module.resolveModule(req.module); const returns = await module[req.fn](...req.args); if (index.isAsyncGenerator(returns) || index.isGenerator(returns)) { if (req.taskId) { pendingTasks.set(req.taskId, returns); reply({ type: "gen", taskId: req.taskId }); } else { while (true) { try { const res = await returns.next(); const { value, transferable } = wrapReturnValue(res.value); reply({ type: "yield", value, done: res.done }, transferable); if (res.done) { break; } } catch (error) { reply({ type: "error", error }); break; } } } } else { const { value, transferable } = wrapReturnValue(returns); reply({ type: "return", value, taskId: req.taskId }, transferable); } } catch (error) { reply({ type: "error", error, taskId: msg.taskId }); } } exports.handleCallRequest = handleCallRequest; exports.isCallRequest = isCallRequest; //# sourceMappingURL=worker.js.map