penpal
Version:
A promise-based library for communicating with iframes via postMessage.
101 lines (85 loc) • 3.45 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _errorSerialization = require("./errorSerialization");
var _enums = require("./enums");
/**
* Listens for "call" messages coming from the remote, executes the corresponding method, and
* responds with the return value.
*/
var _default = (info, serializedMethods, log) => {
const {
localName,
local,
remote,
originForSending,
originForReceiving
} = info;
let destroyed = false;
const handleMessageEvent = event => {
if (event.source !== remote || event.data.penpal !== _enums.MessageType.Call) {
return;
}
if (originForReceiving !== '*' && event.origin !== originForReceiving) {
log("".concat(localName, " received message from origin ").concat(event.origin, " which did not match expected origin ").concat(originForReceiving));
return;
}
const callMessage = event.data;
const {
methodName,
args,
id
} = callMessage;
log("".concat(localName, ": Received ").concat(methodName, "() call"));
const createPromiseHandler = resolution => {
return returnValue => {
log("".concat(localName, ": Sending ").concat(methodName, "() reply"));
if (destroyed) {
// It's possible to throw an error here, but it would need to be thrown asynchronously
// and would only be catchable using window.onerror. This is because the consumer
// is merely returning a value from their method and not calling any function
// that they could wrap in a try-catch. Even if the consumer were to catch the error,
// the value of doing so is questionable. Instead, we'll just log a message.
log("".concat(localName, ": Unable to send ").concat(methodName, "() reply due to destroyed connection"));
return;
}
const message = {
penpal: _enums.MessageType.Reply,
id,
resolution,
returnValue
};
if (resolution === _enums.Resolution.Rejected && returnValue instanceof Error) {
message.returnValue = (0, _errorSerialization.serializeError)(returnValue);
message.returnValueIsError = true;
}
try {
remote.postMessage(message, originForSending);
} catch (err) {
// If a consumer attempts to send an object that's not cloneable (e.g., window),
// we want to ensure the receiver's promise gets rejected.
if (err.name === _enums.NativeErrorName.DataCloneError) {
const errorReplyMessage = {
penpal: _enums.MessageType.Reply,
id,
resolution: _enums.Resolution.Rejected,
returnValue: (0, _errorSerialization.serializeError)(err),
returnValueIsError: true
};
remote.postMessage(errorReplyMessage, originForSending);
}
throw err;
}
};
};
new Promise(resolve => resolve(serializedMethods[methodName].apply(serializedMethods, args))).then(createPromiseHandler(_enums.Resolution.Fulfilled), createPromiseHandler(_enums.Resolution.Rejected));
};
local.addEventListener(_enums.NativeEventType.Message, handleMessageEvent);
return () => {
destroyed = true;
local.removeEventListener(_enums.NativeEventType.Message, handleMessageEvent);
};
};
exports.default = _default;
;