UNPKG

post-me

Version:

Use web Workers and other Windows through a simple Promise API

953 lines (770 loc) 29.1 kB
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var MARKER = '@post-me'; function createUniqueIdFn() { var __id = 0; return function () { var id = __id; __id += 1; return id; }; } /** * A concrete implementation of the {@link Emitter} interface * * @public */ var ConcreteEmitter = /*#__PURE__*/function () { function ConcreteEmitter() { _classCallCheck(this, ConcreteEmitter); this._listeners = {}; } /** {@inheritDoc Emitter.addEventListener} */ _createClass(ConcreteEmitter, [{ key: "addEventListener", value: function addEventListener(eventName, listener) { var listeners = this._listeners[eventName]; if (!listeners) { listeners = new Set(); this._listeners[eventName] = listeners; } listeners.add(listener); } /** {@inheritDoc Emitter.removeEventListener} */ }, { key: "removeEventListener", value: function removeEventListener(eventName, listener) { var listeners = this._listeners[eventName]; if (!listeners) { return; } listeners["delete"](listener); } /** {@inheritDoc Emitter.once} */ }, { key: "once", value: function once(eventName) { var _this = this; return new Promise(function (resolve) { var listener = function listener(data) { _this.removeEventListener(eventName, listener); resolve(data); }; _this.addEventListener(eventName, listener); }); } /** @internal */ }, { key: "emit", value: function emit(eventName, data) { var listeners = this._listeners[eventName]; if (!listeners) { return; } listeners.forEach(function (listener) { listener(data); }); } /** @internal */ }, { key: "removeAllListeners", value: function removeAllListeners() { Object.values(this._listeners).forEach(function (listeners) { if (listeners) { listeners.clear(); } }); } }]); return ConcreteEmitter; }(); var MessageType; (function (MessageType) { MessageType["HandshakeRequest"] = "handshake-request"; MessageType["HandshakeResponse"] = "handshake-response"; MessageType["Call"] = "call"; MessageType["Response"] = "response"; MessageType["Error"] = "error"; MessageType["Event"] = "event"; MessageType["Callback"] = "callback"; })(MessageType || (MessageType = {})); // Message Creators function createHandshakeRequestMessage(sessionId) { return { type: MARKER, action: MessageType.HandshakeRequest, sessionId: sessionId }; } function createHandshakeResponseMessage(sessionId) { return { type: MARKER, action: MessageType.HandshakeResponse, sessionId: sessionId }; } function createCallMessage(sessionId, requestId, methodName, args) { return { type: MARKER, action: MessageType.Call, sessionId: sessionId, requestId: requestId, methodName: methodName, args: args }; } function createResponsMessage(sessionId, requestId, result, error) { var message = { type: MARKER, action: MessageType.Response, sessionId: sessionId, requestId: requestId }; if (result !== undefined) { message.result = result; } if (error !== undefined) { message.error = error; } return message; } function createCallbackMessage(sessionId, requestId, callbackId, args) { return { type: MARKER, action: MessageType.Callback, sessionId: sessionId, requestId: requestId, callbackId: callbackId, args: args }; } function createEventMessage(sessionId, eventName, payload) { return { type: MARKER, action: MessageType.Event, sessionId: sessionId, eventName: eventName, payload: payload }; } // Type Guards function isMessage(m) { return m && m.type === MARKER; } function isHandshakeRequestMessage(m) { return isMessage(m) && m.action === MessageType.HandshakeRequest; } function isHandshakeResponseMessage(m) { return isMessage(m) && m.action === MessageType.HandshakeResponse; } function isCallMessage(m) { return isMessage(m) && m.action === MessageType.Call; } function isResponseMessage(m) { return isMessage(m) && m.action === MessageType.Response; } function isCallbackMessage(m) { return isMessage(m) && m.action === MessageType.Callback; } function isEventMessage(m) { return isMessage(m) && m.action === MessageType.Event; } function makeCallbackEvent(requestId) { return "callback_".concat(requestId); } function makeResponseEvent(requestId) { return "response_".concat(requestId); } var Dispatcher = /*#__PURE__*/function (_ConcreteEmitter) { _inherits(Dispatcher, _ConcreteEmitter); var _super = _createSuper(Dispatcher); function Dispatcher(messenger, sessionId) { var _this2; _classCallCheck(this, Dispatcher); _this2 = _super.call(this); _this2.uniqueId = createUniqueIdFn(); _this2.messenger = messenger; _this2.sessionId = sessionId; _this2.removeMessengerListener = _this2.messenger.addMessageListener(_this2.messengerListener.bind(_assertThisInitialized(_this2))); return _this2; } _createClass(Dispatcher, [{ key: "messengerListener", value: function messengerListener(event) { var data = event.data; if (!isMessage(data)) { return; } if (this.sessionId !== data.sessionId) { return; } if (isCallMessage(data)) { this.emit(MessageType.Call, data); } else if (isResponseMessage(data)) { this.emit(makeResponseEvent(data.requestId), data); } else if (isEventMessage(data)) { this.emit(MessageType.Event, data); } else if (isCallbackMessage(data)) { this.emit(makeCallbackEvent(data.requestId), data); } } }, { key: "callOnRemote", value: function callOnRemote(methodName, args, transfer) { var requestId = this.uniqueId(); var callbackEvent = makeCallbackEvent(requestId); var responseEvent = makeResponseEvent(requestId); var message = createCallMessage(this.sessionId, requestId, methodName, args); this.messenger.postMessage(message, transfer); return { callbackEvent: callbackEvent, responseEvent: responseEvent }; } }, { key: "respondToRemote", value: function respondToRemote(requestId, value, error, transfer) { if (error instanceof Error) { error = { name: error.name, message: error.message }; } var message = createResponsMessage(this.sessionId, requestId, value, error); this.messenger.postMessage(message, transfer); } }, { key: "callbackToRemote", value: function callbackToRemote(requestId, callbackId, args) { var message = createCallbackMessage(this.sessionId, requestId, callbackId, args); this.messenger.postMessage(message); } }, { key: "emitToRemote", value: function emitToRemote(eventName, payload, transfer) { var message = createEventMessage(this.sessionId, eventName, payload); this.messenger.postMessage(message, transfer); } }, { key: "close", value: function close() { this.removeMessengerListener(); this.removeAllListeners(); } }]); return Dispatcher; }(ConcreteEmitter); var ParentHandshakeDispatcher = /*#__PURE__*/function (_ConcreteEmitter2) { _inherits(ParentHandshakeDispatcher, _ConcreteEmitter2); var _super2 = _createSuper(ParentHandshakeDispatcher); function ParentHandshakeDispatcher(messenger, sessionId) { var _this3; _classCallCheck(this, ParentHandshakeDispatcher); _this3 = _super2.call(this); _this3.messenger = messenger; _this3.sessionId = sessionId; _this3.removeMessengerListener = _this3.messenger.addMessageListener(_this3.messengerListener.bind(_assertThisInitialized(_this3))); return _this3; } _createClass(ParentHandshakeDispatcher, [{ key: "messengerListener", value: function messengerListener(event) { var data = event.data; if (!isMessage(data)) { return; } if (this.sessionId !== data.sessionId) { return; } if (isHandshakeResponseMessage(data)) { this.emit(data.sessionId, data); } } }, { key: "initiateHandshake", value: function initiateHandshake() { var message = createHandshakeRequestMessage(this.sessionId); this.messenger.postMessage(message); return this.sessionId; } }, { key: "close", value: function close() { this.removeMessengerListener(); this.removeAllListeners(); } }]); return ParentHandshakeDispatcher; }(ConcreteEmitter); var ChildHandshakeDispatcher = /*#__PURE__*/function (_ConcreteEmitter3) { _inherits(ChildHandshakeDispatcher, _ConcreteEmitter3); var _super3 = _createSuper(ChildHandshakeDispatcher); function ChildHandshakeDispatcher(messenger) { var _this4; _classCallCheck(this, ChildHandshakeDispatcher); _this4 = _super3.call(this); _this4.messenger = messenger; _this4.removeMessengerListener = _this4.messenger.addMessageListener(_this4.messengerListener.bind(_assertThisInitialized(_this4))); return _this4; } _createClass(ChildHandshakeDispatcher, [{ key: "messengerListener", value: function messengerListener(event) { var data = event.data; if (isHandshakeRequestMessage(data)) { this.emit(MessageType.HandshakeRequest, data); } } }, { key: "acceptHandshake", value: function acceptHandshake(sessionId) { var message = createHandshakeResponseMessage(sessionId); this.messenger.postMessage(message); } }, { key: "close", value: function close() { this.removeMessengerListener(); this.removeAllListeners(); } }]); return ChildHandshakeDispatcher; }(ConcreteEmitter); var ProxyType; (function (ProxyType) { ProxyType["Callback"] = "callback"; })(ProxyType || (ProxyType = {})); function createCallbackProxy(callbackId) { return { type: MARKER, proxy: ProxyType.Callback, callbackId: callbackId }; } function isCallbackProxy(p) { return p && p.type === MARKER && p.proxy === ProxyType.Callback; } var ConcreteRemoteHandle = /*#__PURE__*/function (_ConcreteEmitter4) { _inherits(ConcreteRemoteHandle, _ConcreteEmitter4); var _super4 = _createSuper(ConcreteRemoteHandle); function ConcreteRemoteHandle(dispatcher) { var _this5; _classCallCheck(this, ConcreteRemoteHandle); _this5 = _super4.call(this); _this5._dispatcher = dispatcher; _this5._callTransfer = {}; _this5._dispatcher.addEventListener(MessageType.Event, _this5._handleEvent.bind(_assertThisInitialized(_this5))); return _this5; } _createClass(ConcreteRemoteHandle, [{ key: "close", value: function close() { this.removeAllListeners(); } }, { key: "setCallTransfer", value: function setCallTransfer(methodName, transfer) { this._callTransfer[methodName] = transfer; } }, { key: "call", value: function call(methodName) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } return this.customCall(methodName, args); } }, { key: "customCall", value: function customCall(methodName, args) { var _this6 = this; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return new Promise(function (resolve, reject) { var sanitizedArgs = []; var callbacks = []; var callbackId = 0; args.forEach(function (arg) { if (typeof arg === 'function') { callbacks.push(arg); sanitizedArgs.push(createCallbackProxy(callbackId)); callbackId += 1; } else { sanitizedArgs.push(arg); } }); var hasCallbacks = callbacks.length > 0; var callbackListener = undefined; if (hasCallbacks) { callbackListener = function callbackListener(data) { var callbackId = data.callbackId, args = data.args; callbacks[callbackId].apply(callbacks, _toConsumableArray(args)); }; } var transfer = options.transfer; if (transfer === undefined && _this6._callTransfer[methodName]) { var _this6$_callTransfer; transfer = (_this6$_callTransfer = _this6._callTransfer)[methodName].apply(_this6$_callTransfer, sanitizedArgs); } var _this6$_dispatcher$ca = _this6._dispatcher.callOnRemote(methodName, sanitizedArgs, transfer), callbackEvent = _this6$_dispatcher$ca.callbackEvent, responseEvent = _this6$_dispatcher$ca.responseEvent; if (hasCallbacks) { _this6._dispatcher.addEventListener(callbackEvent, callbackListener); } _this6._dispatcher.once(responseEvent).then(function (response) { if (callbackListener) { _this6._dispatcher.removeEventListener(callbackEvent, callbackListener); } var result = response.result, error = response.error; if (error !== undefined) { reject(error); } else { resolve(result); } }); }); } }, { key: "_handleEvent", value: function _handleEvent(data) { var eventName = data.eventName, payload = data.payload; this.emit(eventName, payload); } }]); return ConcreteRemoteHandle; }(ConcreteEmitter); var ConcreteLocalHandle = /*#__PURE__*/function () { function ConcreteLocalHandle(dispatcher, localMethods) { _classCallCheck(this, ConcreteLocalHandle); this._dispatcher = dispatcher; this._methods = localMethods; this._returnTransfer = {}; this._emitTransfer = {}; this._dispatcher.addEventListener(MessageType.Call, this._handleCall.bind(this)); } _createClass(ConcreteLocalHandle, [{ key: "emit", value: function emit(eventName, payload) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var transfer = options.transfer; if (transfer === undefined && this._emitTransfer[eventName]) { transfer = this._emitTransfer[eventName](payload); } this._dispatcher.emitToRemote(eventName, payload, transfer); } }, { key: "setMethods", value: function setMethods(methods) { this._methods = methods; } }, { key: "setMethod", value: function setMethod(methodName, method) { this._methods[methodName] = method; } }, { key: "setReturnTransfer", value: function setReturnTransfer(methodName, transfer) { this._returnTransfer[methodName] = transfer; } }, { key: "setEmitTransfer", value: function setEmitTransfer(eventName, transfer) { this._emitTransfer[eventName] = transfer; } }, { key: "_handleCall", value: function _handleCall(data) { var _this7 = this; var requestId = data.requestId, methodName = data.methodName, args = data.args; var callMethod = new Promise(function (resolve, reject) { var _this7$_methods; var method = _this7._methods[methodName]; if (typeof method !== 'function') { reject(new Error("The method \"".concat(methodName, "\" has not been implemented."))); return; } var desanitizedArgs = args.map(function (arg) { if (isCallbackProxy(arg)) { var callbackId = arg.callbackId; return function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } _this7._dispatcher.callbackToRemote(requestId, callbackId, args); }; } else { return arg; } }); Promise.resolve((_this7$_methods = _this7._methods)[methodName].apply(_this7$_methods, _toConsumableArray(desanitizedArgs))).then(resolve)["catch"](reject); }); callMethod.then(function (result) { var transfer; if (_this7._returnTransfer[methodName]) { transfer = _this7._returnTransfer[methodName](result); } _this7._dispatcher.respondToRemote(requestId, result, undefined, transfer); })["catch"](function (error) { _this7._dispatcher.respondToRemote(requestId, undefined, error); }); } }]); return ConcreteLocalHandle; }(); var ConcreteConnection = /*#__PURE__*/function () { function ConcreteConnection(dispatcher, localMethods) { _classCallCheck(this, ConcreteConnection); this._dispatcher = dispatcher; this._localHandle = new ConcreteLocalHandle(dispatcher, localMethods); this._remoteHandle = new ConcreteRemoteHandle(dispatcher); } _createClass(ConcreteConnection, [{ key: "close", value: function close() { this._dispatcher.close(); this.remoteHandle().close(); } }, { key: "localHandle", value: function localHandle() { return this._localHandle; } }, { key: "remoteHandle", value: function remoteHandle() { return this._remoteHandle; } }]); return ConcreteConnection; }(); var uniqueSessionId = createUniqueIdFn(); var runUntil = function runUntil(worker, condition, unfulfilled, maxAttempts, attemptInterval) { var attempt = 0; var fn = function fn() { if (!condition() && (attempt < maxAttempts || maxAttempts < 1)) { worker(); attempt += 1; setTimeout(fn, attemptInterval); } else if (!condition() && attempt >= maxAttempts && maxAttempts >= 1) { unfulfilled(); } }; fn(); }; /** * Initiate the handshake from the Parent side * * @param messenger - The Messenger used to send and receive messages from the other end * @param localMethods - The methods that will be exposed to the other end * @param maxAttempts - The maximum number of handshake attempts * @param attemptsInterval - The interval between handshake attempts * @returns A Promise to an active {@link Connection} to the other end * * @public */ function ParentHandshake(messenger) { var localMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var maxAttempts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 5; var attemptsInterval = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 100; var thisSessionId = uniqueSessionId(); var connected = false; return new Promise(function (resolve, reject) { var handshakeDispatcher = new ParentHandshakeDispatcher(messenger, thisSessionId); handshakeDispatcher.once(thisSessionId).then(function (response) { connected = true; handshakeDispatcher.close(); var sessionId = response.sessionId; var dispatcher = new Dispatcher(messenger, sessionId); var connection = new ConcreteConnection(dispatcher, localMethods); resolve(connection); }); runUntil(function () { return handshakeDispatcher.initiateHandshake(); }, function () { return connected; }, function () { return reject(new Error("Handshake failed, reached maximum number of attempts")); }, maxAttempts, attemptsInterval); }); } /** * Initiate the handshake from the Child side * * @param messenger - The Messenger used to send and receive messages from the other end * @param localMethods - The methods that will be exposed to the other end * @returns A Promise to an active {@link Connection} to the other end * * @public */ function ChildHandshake(messenger) { var localMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return new Promise(function (resolve, reject) { var handshakeDispatcher = new ChildHandshakeDispatcher(messenger); handshakeDispatcher.once(MessageType.HandshakeRequest).then(function (response) { var sessionId = response.sessionId; handshakeDispatcher.acceptHandshake(sessionId); handshakeDispatcher.close(); var dispatcher = new Dispatcher(messenger, sessionId); var connection = new ConcreteConnection(dispatcher, localMethods); resolve(connection); }); }); } var acceptableMessageEvent = function acceptableMessageEvent(event, remoteWindow, acceptedOrigin) { var source = event.source, origin = event.origin; if (source !== remoteWindow) { return false; } if (origin !== acceptedOrigin && acceptedOrigin !== '*') { return false; } return true; }; /** * A concrete implementation of {@link Messenger} used to communicate with another Window. * * @public * */ var WindowMessenger = function WindowMessenger(_ref) { var localWindow = _ref.localWindow, remoteWindow = _ref.remoteWindow, remoteOrigin = _ref.remoteOrigin; _classCallCheck(this, WindowMessenger); localWindow = localWindow || window; this.postMessage = function (message, transfer) { remoteWindow.postMessage(message, remoteOrigin, transfer); }; this.addMessageListener = function (listener) { var outerListener = function outerListener(event) { if (acceptableMessageEvent(event, remoteWindow, remoteOrigin)) { listener(event); } }; localWindow.addEventListener('message', outerListener); var removeListener = function removeListener() { localWindow.removeEventListener('message', outerListener); }; return removeListener; }; }; /** @public */ var BareMessenger = function BareMessenger(postable) { _classCallCheck(this, BareMessenger); this.postMessage = function (message) { var transfer = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; postable.postMessage(message, transfer); }; this.addMessageListener = function (listener) { var outerListener = function outerListener(event) { listener(event); }; postable.addEventListener('message', outerListener); var removeListener = function removeListener() { postable.removeEventListener('message', outerListener); }; return removeListener; }; }; /** * A concrete implementation of {@link Messenger} used to communicate with a Worker. * * Takes a {@link Postable} representing the `Worker` (when calling from * the parent context) or the `self` `DedicatedWorkerGlobalScope` object * (when calling from the child context). * * @public * */ var WorkerMessenger = /*#__PURE__*/function (_BareMessenger) { _inherits(WorkerMessenger, _BareMessenger); var _super5 = _createSuper(WorkerMessenger); function WorkerMessenger(_ref2) { var worker = _ref2.worker; _classCallCheck(this, WorkerMessenger); return _super5.call(this, worker); } return WorkerMessenger; }(BareMessenger); /** * A concrete implementation of {@link Messenger} used to communicate with a MessagePort. * * @public * */ var PortMessenger = /*#__PURE__*/function (_BareMessenger2) { _inherits(PortMessenger, _BareMessenger2); var _super6 = _createSuper(PortMessenger); function PortMessenger(_ref3) { var port = _ref3.port; _classCallCheck(this, PortMessenger); port.start(); return _super6.call(this, port); } return PortMessenger; }(BareMessenger); /** * Create a logger function with a specific namespace * * @param namespace - The namespace will be prepended to all the arguments passed to the logger function * @param log - The underlying logger (`console.log` by default) * * @public * */ function debug(namespace, log) { log = log || console.debug || console.log || function () {}; return function () { for (var _len3 = arguments.length, data = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { data[_key3] = arguments[_key3]; } log.apply(void 0, [namespace].concat(data)); }; } /** * Decorate a {@link Messenger} so that it will log any message exchanged * @param messenger - The Messenger that will be decorated * @param log - The logger function that will receive each message * @returns A decorated Messenger * * @public * */ function DebugMessenger(messenger, log) { log = log || debug('post-me'); var debugListener = function debugListener(event) { var data = event.data; log('⬅️ received message', data); }; messenger.addMessageListener(debugListener); return { postMessage: function postMessage(message, transfer) { log('➡️ sending message', message); messenger.postMessage(message, transfer); }, addMessageListener: function addMessageListener(listener) { return messenger.addMessageListener(listener); } }; } export { BareMessenger, ChildHandshake, ConcreteEmitter, DebugMessenger, ParentHandshake, PortMessenger, WindowMessenger, WorkerMessenger, debug };