UNPKG

@instantdb/core

Version:
189 lines 5.42 kB
let _connId = 0; export class WSConnection { type = 'ws'; conn; id; onopen; onmessage; onclose; onerror; constructor(url) { this.id = `${this.type}_${_connId++}`; this.conn = new WebSocket(url); this.conn.onopen = (_e) => { if (this.onopen) { this.onopen({ target: this }); } }; this.conn.onmessage = (e) => { if (this.onmessage) { this.onmessage({ target: this, message: JSON.parse(e.data.toString()), }); } }; this.conn.onclose = (_e) => { if (this.onclose) { this.onclose({ target: this }); } }; this.conn.onerror = (_e) => { if (this.onerror) { this.onerror({ target: this }); } }; } close() { this.conn.close(); } isOpen() { return this.conn.readyState === (WebSocket.OPEN ?? 1); } isConnecting() { return this.conn.readyState === (WebSocket.CONNECTING ?? 0); } send(msg) { return this.conn.send(JSON.stringify(msg)); } } export class SSEConnection { type = 'sse'; initParams = null; sendQueue = []; sendPromise; closeFired = false; sseInitTimeout = undefined; ES; messageUrl; conn; url; id; onopen; onmessage; onclose; onerror; constructor(ES, url, messageUrl) { this.id = `${this.type}_${_connId++}`; this.url = url; this.messageUrl = messageUrl || this.url; this.ES = ES; this.conn = new ES(url); // Close the connection if we didn't get an init within 10 seconds this.sseInitTimeout = setTimeout(() => { if (!this.initParams) { this.handleError(); } }, 10000); this.conn.onmessage = (e) => { const message = JSON.parse(e.data); if (Array.isArray(message)) { for (const msg of message) { this.handleMessage(msg); } } else { this.handleMessage(message); } }; this.conn.onerror = (e) => { this.handleError(); }; } handleMessage(msg) { if (msg.op === 'sse-init') { this.initParams = { machineId: msg['machine-id'], sessionId: msg['session-id'], sseToken: msg['sse-token'], }; if (this.onopen) { this.onopen({ target: this }); } clearTimeout(this.sseInitTimeout); return; } if (this.onmessage) { this.onmessage({ target: this, message: msg, }); } } // Runs the onerror and closes the connection handleError() { try { if (this.onerror) { this.onerror({ target: this }); } } finally { this.handleClose(); } } handleClose() { this.conn.close(); if (this.onclose && !this.closeFired) { this.closeFired = true; this.onclose({ target: this }); } } async postMessages(messages) { // TODO(dww): Create a connection with chunked encoding so we can // send multiple messages over one request try { const resp = await fetch(this.messageUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ machine_id: this.initParams?.machineId, session_id: this.initParams?.sessionId, sse_token: this.initParams?.sseToken, messages, }), }); if (!resp.ok) { this.handleError(); } } catch (e) { this.handleError(); } } async flushQueue() { if (this.sendPromise || !this.sendQueue.length) return; const messages = this.sendQueue; this.sendQueue = []; const sendPromise = this.postMessages(messages); this.sendPromise = sendPromise; sendPromise.then(() => { this.sendPromise = null; this.flushQueue(); }); } send(msg) { if (!this.isOpen() || !this.initParams) { if (this.isConnecting()) { throw new Error(`Failed to execute 'send' on 'EventSource': Still in CONNECTING state.`); } if (this.conn.readyState === this.ES.CLOSED) { throw new Error(`EventSource is already in CLOSING or CLOSED state.`); } throw new Error(`EventSource is in invalid state.`); } this.sendQueue.push(msg); this.flushQueue(); } isOpen() { return this.conn.readyState === this.ES.OPEN && this.initParams !== null; } isConnecting() { return (this.conn.readyState === this.ES.CONNECTING || (this.conn.readyState === this.ES.OPEN && this.initParams === null)); } close() { this.handleClose(); } } //# sourceMappingURL=Connection.js.map