@sv-kit/workerify
Version:
Headless Web Worker RPC: wrap(url), workerify(fn), expose(api). Abort, transferables, and typed proxies.
67 lines (66 loc) • 1.91 kB
JavaScript
// packages/workerify/src/transfer.ts
const TRANSFERABLE_TAG = new Set([
"ArrayBuffer",
"MessagePort",
"ImageBitmap",
"OffscreenCanvas",
"RTCDataChannel",
"AudioData", // 浏览器支持时
"VideoFrame" // 浏览器支持时
]);
export function isTransferable(x) {
if (!x)
return false;
if (typeof ArrayBuffer !== "undefined" && x instanceof ArrayBuffer)
return true;
if (typeof MessagePort !== "undefined" && x instanceof MessagePort)
return true;
if (typeof ImageBitmap !== "undefined" && x instanceof ImageBitmap)
return true;
if (typeof OffscreenCanvas !== "undefined" && x instanceof OffscreenCanvas)
return true;
const tag = Object.prototype.toString.call(x).slice(8, -1);
return TRANSFERABLE_TAG.has(tag);
}
/** 递归收集 Transferable(数组/对象深度遍历,避免循环) */
export function collectTransferables(x, maxDepth = 3) {
const out = [];
const seen = new WeakSet();
function walk(v, d) {
if (!v || d > maxDepth)
return;
if (isTransferable(v)) {
out.push(v);
return;
}
if (typeof v !== "object")
return;
if (seen.has(v))
return;
seen.add(v);
if (Array.isArray(v)) {
for (const it of v)
walk(it, d + 1);
}
else {
for (const k of Object.keys(v))
walk(v[k], d + 1);
}
}
walk(x, 0);
return out;
}
export function toPlainError(err) {
try {
const e = err ?? {};
return {
name: String(e.name ?? "Error"),
message: String(e.message ?? ""),
stack: typeof e.stack === "string" ? e.stack : undefined,
cause: e.cause
};
}
catch {
return { name: "Error", message: String(err) };
}
}