Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,26 +160,28 @@ export function getTargetHost(): any {
* @param message The message to send
* @param origin Optional origin for iframe communication
*/
export function postMessageToTarget(target: Target, message: any, origin?: string): void {
export function postMessageToTarget(target: Target, message: any, origin?: string, transferables?: Transferable[]): void {
if (!target) {
throw new Error("Rimless Error: No target specified for postMessage");
}

const transfer: Transferable[] = transferables ?? [];

// Node.js Worker
if (isNodeEnv() && target === parentPort) {
target.postMessage(JSON.parse(JSON.stringify(message)));
target.postMessage(JSON.parse(JSON.stringify(message)), {transfer});
return;
}

// Web Worker
if (isWorker()) {
target.postMessage(JSON.parse(JSON.stringify(message)));
target.postMessage(JSON.parse(JSON.stringify(message)), {transfer});
return;
}

// iframe or window
if (target.postMessage) {
target.postMessage(JSON.parse(JSON.stringify(message)), { targetOrigin: origin || "*" });
target.postMessage(JSON.parse(JSON.stringify(message)), { targetOrigin: origin || "*" , transfer});
return;
}

Expand Down
29 changes: 25 additions & 4 deletions src/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { addEventListener, generateId, getEventData, postMessageToTarget, removeEventListener, set } from "./helpers";
import { isWithTransferables } from "./transferable";
import {
actions,
Environment,
Expand Down Expand Up @@ -31,6 +32,7 @@ export function registerLocalMethods(
async function handleCall(event: any) {
const eventData = getEventData(event);
const { action, callID, connectionID, callName, args = [] } = eventData as RPCRequestPayload;
const transferables: Transferable[] = [];

if (action !== actions.RPC_REQUEST) return;
if (!callID || !callName) return;
Expand All @@ -53,6 +55,11 @@ export function registerLocalMethods(
if (!result) {
// if the result is falsy (null, undefined, "", etc), set it directly
payload.result = result;
} else if (isWithTransferables(result)) {
const {value, transferables: resultTransferables} = result.unwrap();

payload.result = value;
transferables.push(...resultTransferables);
} else {
// otherwise parse a stringified version of it
payload.result = JSON.parse(JSON.stringify(result));
Expand All @@ -62,7 +69,7 @@ export function registerLocalMethods(
payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));
}

postMessageToTarget(sendTo, payload, event?.origin);
postMessageToTarget(sendTo, payload, event?.origin, transferables);
}

// subscribe to the call event
Expand Down Expand Up @@ -93,7 +100,7 @@ export function createRPC(
listenTo: Environment,
sendTo: Target
) {
return (...args: any) => {
return (...args: unknown[]) => {
return new Promise((resolve, reject) => {
const requestID = generateId();

Expand All @@ -112,10 +119,24 @@ export function createRPC(
if (action === actions.RPC_REJECT) return reject(error);
}

const argValues: unknown[] = [];
const transferables: Transferable[] = [];

args.forEach((arg, index) => {
if (isWithTransferables(arg)) {
const {value: argValue, transferables: argTransferables} = arg.unwrap();

argValues[index] = argValue;
transferables.push(...argTransferables);
} else {
argValues[index] = arg;
}
});

// send the RPC request with arguments
const payload = {
action: actions.RPC_REQUEST,
args: JSON.parse(JSON.stringify(args)),
args: JSON.parse(JSON.stringify(argValues)),
callID: requestID,
callName: rpcCallName,
connectionID: rpcConnectionID,
Expand All @@ -124,7 +145,7 @@ export function createRPC(
addEventListener(listenTo, events.MESSAGE, handleResponse);
listeners.push(() => removeEventListener(listenTo, events.MESSAGE, handleResponse));

postMessageToTarget(sendTo, payload, event?.origin);
postMessageToTarget(sendTo, payload, event?.origin, transferables);
});
};
}
Expand Down
32 changes: 32 additions & 0 deletions src/transferable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Simple wrapper to represent return value
* plus transferable objects
*/
export function withTransferables(value: unknown, transferables: Transferable[]) {

if (!transferables || !(Array.isArray(transferables))) {
throw new Error("missing transferables array");
}

return new _WithTransferables(value, transferables);
}

export function isWithTransferables(value: unknown): value is WithTransferables {
return value instanceof _WithTransferables;
}

class _WithTransferables {
private value: unknown;
private transferables: Transferable[];

constructor(value: unknown, transferables: Transferable[]) {
this.value = value;
this.transferables = transferables;
}

unwrap() {
return { value: this.value, transferables: this.transferables };
}
}

export type WithTransferables = _WithTransferables;