import type { Transferable } from 'node:worker_threads'; import { MessagePort } from 'node:worker_threads'; const TRANSFER = Symbol.for('moroutine.transfer'); export interface Transferred { readonly [TRANSFER]: true; readonly value: T; } /** * Marks a value for zero-copy transfer via postMessage. The original becomes detached after sending. * @param value - The value to transfer (ArrayBuffer, TypedArray, MessagePort, or stream). * @returns The same value, typed unchanged for transparent use as a moroutine argument. */ export function transfer(value: T): T { return { [TRANSFER]: true as const, value } as unknown as T; } export function extractTransferables(args: unknown[]): { args: unknown[]; transfer: Transferable[] } { const transferList: Transferable[] = []; const processedArgs = args.map((arg) => { if (typeof arg === 'object' && arg !== null && TRANSFER in arg) { const value = (arg as Transferred).value; collectTransferables(value, transferList); return value; } return arg; }); return { args: processedArgs, transfer: transferList }; } export function collectTransferables(value: unknown, list: Transferable[]): void { if (!(typeof value === 'object' && value !== null)) return; // Directly transferable if ( value instanceof ArrayBuffer || value instanceof MessagePort || value instanceof ReadableStream || value instanceof WritableStream ) { list.push(value); return; } // TypedArray or DataView — extract .buffer if (ArrayBuffer.isView(value) && value.buffer instanceof ArrayBuffer) { list.push(value.buffer); return; } // TransformStream — extract both streams if (value instanceof TransformStream) { list.push(value.readable, value.writable); return; } }