I have written this function to get all transferable elements of an Object — and to get them without duplicates, so the list can actually be used with postMessage().
It includes a check against a pseudo-Abstract Base Class which does lookup against a hard-coded whitelist of known "Transferable" types. This is obviously a nasty, kludgy, fragile hack.
I've written this function because I was unable to find any actual API for doing this check. Does any exist?
function getTransferables(obj, seen=null) {
seen ||= new WeakSet();
if (typeof obj !== 'object')
// Non-transferable leaf
return [];
if (seen.has(obj))
// Avoid infinite loop on cyclic objects
return [];
seen.add(obj);
if (obj instanceof _Transferable)
// Transferable leaf
return [obj];
// Recurse
return Object.entries(obj).flatMap(([,obj]) => getTransferables(obj, seen));
}
// FIXME ugly hack
const _Transferable = Object.assign([
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects#supported_objects
'ArrayBuffer',
'MessagePort',
'ReadableStream',
'WritableStream',
'TransformStream',
'WebTransportReceiveStream',
'WebTransportSendStream',
'AudioData',
'ImageBitmap',
'VideoFrame',
'OffscreenCanvas',
'RTCDataChannel'
].map(clsName => {
"use strict";
if (!this)
throw new RuntimeError('could not get global scope');
return this[clsName];
}).filter(cls => cls), {
[Symbol.hasInstance](obj) {
return this.some(cls => obj instanceof cls);
}
});
<button onclick="{
let a = new ArrayBuffer(69),
b = new ArrayBuffer(42);
let myObj = ({x: {a, b}, y: {a_again: a}});
let myTransfer = getTransferables(myObj);
console.info(`There are ${myTransfer.length} Transferables in ${JSON.stringify(myObj, (k,v) => {if (v instanceof _Transferable) return ({[`!${v.__proto__.constructor.name}`]: {}}); return v;})}.`);
}">Demo</button>