UNPKG

@splitsoftware/splitio-react-native

Version:
311 lines (310 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EventSourceXHR = void 0; function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /** * EventSource polyfill based on https://www.npmjs.com/package/react-native-sse * * The MIT License * * Copyright (c) 2021 Binary Minds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ const XMLReadyStateMap = ['UNSENT', 'OPENED', 'HEADERS_RECEIVED', 'LOADING', 'DONE']; class EventSourceXHR { constructor(url, options = {}) { var _options$timeout, _options$timeoutBefor, _options$pollingInter; _defineProperty(this, "ERROR", -1); _defineProperty(this, "CONNECTING", 0); _defineProperty(this, "OPEN", 1); _defineProperty(this, "CLOSED", 2); _defineProperty(this, "CRLF", '\r\n'); _defineProperty(this, "LF", '\n'); _defineProperty(this, "CR", '\r'); this.lastEventId = null; this.status = this.CONNECTING; this.eventHandlers = { open: [], message: [], error: [], done: [], close: [] }; this.method = options.method || 'GET'; this.timeout = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : 0; this.timeoutBeforeConnection = (_options$timeoutBefor = options.timeoutBeforeConnection) !== null && _options$timeoutBefor !== void 0 ? _options$timeoutBefor : 500; this.withCredentials = options.withCredentials || false; this.body = options.body || undefined; this.debug = options.debug || false; this.interval = (_options$pollingInter = options.pollingInterval) !== null && _options$pollingInter !== void 0 ? _options$pollingInter : 5000; this.lineEndingCharacter = options.lineEndingCharacter || null; const defaultHeaders = { 'Accept': 'text/event-stream', 'Cache-Control': 'no-cache', 'X-Requested-With': 'XMLHttpRequest' }; this.headers = { ...defaultHeaders, ...options.headers }; this._xhr = null; this._pollTimer = null; this._lastIndexProcessed = 0; if (!url || typeof url !== 'string' && typeof url.toString !== 'function') { throw new SyntaxError('[EventSource] Invalid URL argument.'); } if (typeof url.toString === 'function') { this.url = url.toString(); } else { this.url = url; } this._pollAgain(this.timeoutBeforeConnection, true); } _pollAgain(time, allowZero) { if (time > 0 || allowZero) { this._logDebug(`[EventSource] Will open new connection in ${time} ms.`); this._pollTimer = setTimeout(() => { this.open(); }, time); } } open() { try { this.status = this.CONNECTING; this._lastIndexProcessed = 0; this._xhr = new XMLHttpRequest(); this._xhr.open(this.method, this.url, true); if (this.withCredentials) { this._xhr.withCredentials = true; } for (const [key, value] of Object.entries(this.headers)) { if (value !== undefined && value !== null) { this._xhr.setRequestHeader(key, value); } } if (this.lastEventId !== null) { this._xhr.setRequestHeader('Last-Event-ID', this.lastEventId); } this._xhr.timeout = this.timeout; this._xhr.onreadystatechange = () => { if (this.status === this.CLOSED) { return; } const xhr = this._xhr; this._logDebug(`[EventSource][onreadystatechange] ReadyState: ${XMLReadyStateMap[xhr.readyState] || 'Unknown'}(${xhr.readyState}), status: ${xhr.status}`); if (![XMLHttpRequest.DONE, XMLHttpRequest.LOADING].includes(xhr.readyState)) { return; } if (xhr.status >= 200 && xhr.status < 400) { if (this.status === this.CONNECTING) { this.status = this.OPEN; this.dispatch('open', { type: 'open' }); this._logDebug('[EventSource][onreadystatechange][OPEN] Connection opened.'); } this._handleEvent(xhr.responseText || ''); if (xhr.readyState === XMLHttpRequest.DONE) { this._logDebug('[EventSource][onreadystatechange][DONE] Operation done.'); this._pollAgain(this.interval, false); this.dispatch('done', { type: 'done' }); } } else if (xhr.status !== 0) { this.status = this.ERROR; this.dispatch('error', { type: 'error', message: xhr.responseText, xhrStatus: xhr.status, xhrState: xhr.readyState }); if (xhr.readyState === XMLHttpRequest.DONE) { this._logDebug('[EventSource][onreadystatechange][ERROR] Response status error.'); this._pollAgain(this.interval, false); } } }; this._xhr.onerror = () => { if (this.status === this.CLOSED) { return; } this.status = this.ERROR; this.dispatch('error', { type: 'error', message: this._xhr.responseText, xhrStatus: this._xhr.status, xhrState: this._xhr.readyState }); }; if (this.body) { this._xhr.send(this.body); } else { this._xhr.send(); } if (this.timeout > 0) { setTimeout(() => { if (this._xhr.readyState === XMLHttpRequest.LOADING) { this.dispatch('error', { type: 'timeout' }); this.close(); } }, this.timeout); } } catch (e) { this.status = this.ERROR; this.dispatch('error', { type: 'exception', message: e.message, error: e }); } } _logDebug(...msg) { if (this.debug) { console.debug(...msg); } } _handleEvent(response) { if (this.lineEndingCharacter === null) { const detectedNewlineChar = this._detectNewlineChar(response); if (detectedNewlineChar !== null) { this._logDebug(`[EventSource] Automatically detected lineEndingCharacter: ${JSON.stringify(detectedNewlineChar).slice(1, -1)}`); this.lineEndingCharacter = detectedNewlineChar; } else { console.warn("[EventSource] Unable to identify the line ending character. Ensure your server delivers a standard line ending character: \\r\\n, \\n, \\r, or specify your custom character using the 'lineEndingCharacter' option."); return; } } const indexOfDoubleNewline = this._getLastDoubleNewlineIndex(response); if (indexOfDoubleNewline <= this._lastIndexProcessed) { return; } const parts = response.substring(this._lastIndexProcessed, indexOfDoubleNewline).split(this.lineEndingCharacter); this._lastIndexProcessed = indexOfDoubleNewline; let type; let id = null; let data = []; let retry = 0; let line = ''; for (let i = 0; i < parts.length; i++) { line = parts[i].trim(); if (line.startsWith('event')) { type = line.replace(/event:?\s*/, ''); } else if (line.startsWith('retry')) { retry = parseInt(line.replace(/retry:?\s*/, ''), 10); if (!isNaN(retry)) { this.interval = retry; } } else if (line.startsWith('data')) { data.push(line.replace(/data:?\s*/, '')); } else if (line.startsWith('id')) { id = line.replace(/id:?\s*/, ''); if (id !== '') { this.lastEventId = id; } else { this.lastEventId = null; } } else if (line === '') { if (data.length > 0) { const eventType = type || 'message'; const event = { type: eventType, data: data.join('\n'), url: this.url, lastEventId: this.lastEventId }; this.dispatch(eventType, event); data = []; type = undefined; } } } } _detectNewlineChar(response) { const supportedLineEndings = [this.CRLF, this.LF, this.CR]; for (const char of supportedLineEndings) { if (response.includes(char)) { return char; } } return null; } _getLastDoubleNewlineIndex(response) { const doubleLineEndingCharacter = this.lineEndingCharacter + this.lineEndingCharacter; const lastIndex = response.lastIndexOf(doubleLineEndingCharacter); if (lastIndex === -1) { return -1; } return lastIndex + doubleLineEndingCharacter.length; } addEventListener(type, listener) { if (this.eventHandlers[type] === undefined) { this.eventHandlers[type] = []; } this.eventHandlers[type].push(listener); } removeEventListener(type, listener) { if (this.eventHandlers[type] !== undefined) { this.eventHandlers[type] = this.eventHandlers[type].filter(handler => handler !== listener); } } removeAllEventListeners(type) { const availableTypes = Object.keys(this.eventHandlers); if (type === undefined) { for (const eventType of availableTypes) { this.eventHandlers[eventType] = []; } } else { if (!availableTypes.includes(type)) { throw Error(`[EventSource] '${type}' type is not supported event type.`); } this.eventHandlers[type] = []; } } dispatch(type, data) { const availableTypes = Object.keys(this.eventHandlers); if (!availableTypes.includes(type)) { return; } for (const handler of Object.values(this.eventHandlers[type])) { handler(data); } } close() { if (this.status !== this.CLOSED) { this.status = this.CLOSED; this.dispatch('close', { type: 'close' }); } clearTimeout(this._pollTimer); if (this._xhr) { this._xhr.abort(); } } } exports.EventSourceXHR = EventSourceXHR; //# sourceMappingURL=EventSourceXHR.js.map