UNPKG

ifrau

Version:

Free-range app utility for IFRAME-based FRAs

134 lines (107 loc) 3.4 kB
import { fromError, toError } from './transform-error.js'; import { Port } from './port.js'; import { RequestTypeError } from './request-type-error.js'; export class PortWithRequests extends Port { constructor(endPoint, targetOrigin, options) { super(endPoint, targetOrigin, options); this._pendingRequests = {}; this._requestHandlers = {}; this._requestCounter = 0; this._waitingRequests = []; this._onMessage('req', (eventType, payload) => { this._receiveRequest(eventType, payload); }); this._onMessage('res', (eventType, payload) => { this._receiveRequestResponse(eventType, payload); }); } onRequest(requestType, handler) { if (this._isConnected) throw new Error('Add request handlers before connecting'); if (this._requestHandlers[requestType] !== undefined) { throw new Error(`Duplicate onRequest handler for type "${requestType}"`); } this.debug(`onRequest handler added for ${requestType}`); this._requestHandlers[requestType] = handler; // process requests we've received before adding handler, somehow this._sendRequestResponse(requestType); return this; } request(requestType) { const args = new Array(arguments.length - 1); for (let i = 1; i < arguments.length; ++i) { args[i - 1] = arguments[i]; } return new Promise((resolve, reject) => { const counter = ++this._requestCounter; const id = `${this._id}_${counter}`; this._initHashArrAndPush(this._pendingRequests, requestType, { id: id, resolve: resolve, reject: reject }); const finish = () => { this._sendMessage('req', requestType, { id: id, args: args }); }; if (this._isConnected) finish(); else this._connectQueue.push(finish); }); } _receiveRequest(requestType, payload) { this._initHashArrAndPush(this._waitingRequests, requestType, payload); this._sendRequestResponse(requestType); } _receiveRequestResponse(requestType, payload) { const requests = this._pendingRequests[requestType]; if (requests === undefined) { return; } // search for the request this response is for for (let i = 0; i < requests.length; ++i) { const req = requests[i]; if (req.id !== payload.id) { continue; } // eslint-disable-next-line no-prototype-builtins if (payload.hasOwnProperty('err')) { const error = toError(payload.err); req.reject(error); } else { req.resolve(payload.val); } requests.splice(i, 1); return; } } _sendRequestResponse(requestType) { const handler = this._requestHandlers[requestType]; const waiting = this._waitingRequests[requestType]; delete this._waitingRequests[requestType]; if (waiting === undefined || waiting.length === 0) { return; } if (handler === undefined) { const noHandler = fromError(new RequestTypeError(requestType)); waiting.forEach(w => { this._sendMessage('res', requestType, { id: w.id, err: noHandler }); }); return; } waiting.forEach(w => { Promise.resolve().then(() => { if (typeof handler === 'function') { return handler.apply(handler, w.args); } // otherwise "handler" is a value / Promise return handler; }).then(val => { this._sendMessage('res', requestType, { id: w.id, val: val }); }).catch(e => { const err = fromError(e); this._sendMessage('res', requestType, { id: w.id, err: err }); }); }); } }