UNPKG

chaingate

Version:

Multi-chain cryptocurrency SDK for TypeScript — unified API for Bitcoin, Ethereum, Litecoin, Dogecoin, Bitcoin Cash, Polygon, Arbitrum, and any EVM-compatible chain. Create wallets, query balances, send transactions, and manage tokens and NFTs across UTXO

139 lines (138 loc) 6.49 kB
"use strict"; /* eslint-disable @typescript-eslint/no-explicit-any */ // This file is auto-generated by @hey-api/openapi-ts Object.defineProperty(exports, "__esModule", { value: true }); exports.createSseClient = void 0; const createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) => { let lastEventId; const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))); const createStream = async function* () { let retryDelay = sseDefaultRetryDelay ?? 3000; let attempt = 0; const signal = options.signal ?? new AbortController().signal; while (true) { if (signal.aborted) break; attempt++; const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers); if (lastEventId !== undefined) { headers.set('Last-Event-ID', lastEventId); } try { const requestInit = { redirect: 'follow', ...options, body: options.serializedBody, headers, signal, }; let request = new Request(url, requestInit); if (onRequest) { request = await onRequest(url, requestInit); } // fetch must be assigned here, otherwise it would throw the error: // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation const _fetch = options.fetch ?? globalThis.fetch; const response = await _fetch(request); if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`); if (!response.body) throw new Error('No body in SSE response'); const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); let buffer = ''; const abortHandler = () => { try { reader.cancel(); } catch { // noop } }; signal.addEventListener('abort', abortHandler); try { while (true) { const { done, value } = await reader.read(); if (done) break; buffer += value; // Normalize line endings: CRLF -> LF, then CR -> LF buffer = buffer.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); const chunks = buffer.split('\n\n'); buffer = chunks.pop() ?? ''; for (const chunk of chunks) { const lines = chunk.split('\n'); const dataLines = []; let eventName; for (const line of lines) { if (line.startsWith('data:')) { dataLines.push(line.replace(/^data:\s*/, '')); } else if (line.startsWith('event:')) { eventName = line.replace(/^event:\s*/, ''); } else if (line.startsWith('id:')) { lastEventId = line.replace(/^id:\s*/, ''); } else if (line.startsWith('retry:')) { const parsed = Number.parseInt(line.replace(/^retry:\s*/, ''), 10); if (!Number.isNaN(parsed)) { retryDelay = parsed; } } } let data; let parsedJson = false; if (dataLines.length) { const rawData = dataLines.join('\n'); try { data = JSON.parse(rawData); parsedJson = true; } catch { data = rawData; } } if (parsedJson) { if (responseValidator) { await responseValidator(data); } if (responseTransformer) { data = await responseTransformer(data); } } onSseEvent?.({ data, event: eventName, id: lastEventId, retry: retryDelay, }); if (dataLines.length) { yield data; } } } } finally { signal.removeEventListener('abort', abortHandler); reader.releaseLock(); } break; // exit loop on normal completion } catch (error) { // connection failed or aborted; retry after delay onSseError?.(error); if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) { break; // stop after firing error } // exponential backoff: double retry each attempt, cap at 30s const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000); await sleep(backoff); } } }; const stream = createStream(); return { stream }; }; exports.createSseClient = createSseClient;