@bsv/wallet-toolbox
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
111 lines • 4.12 kB
JavaScript
"use strict";
/**
* Client for Arcade transaction status updates.
*
* Uses react-native-sse EventSource to connect to Arcade's
* `GET /events?callbackToken=<token>` endpoint for real-time
* status updates via SSE.
*
* Supports on-demand fetching via fetchEvents() for use on
* app open, balance refresh, transaction list view, etc.
* The EventSource stays connected between fetches for live updates.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ArcSSEClient = void 0;
const TAG = '[ArcSSE]';
class ArcSSEClient {
constructor(options) {
this.options = options;
this.es = null;
this.connected = false;
this.connecting = false;
this._lastEventId = options.lastEventId;
let base = options.baseUrl;
while (base.endsWith('/')) {
base = base.slice(0, -1);
}
this.url = `${base}/events?callbackToken=${encodeURIComponent(options.callbackToken)}`;
}
get lastEventId() {
return this._lastEventId;
}
/**
* Open the SSE connection. Events will be dispatched via onEvent as they arrive.
*/
connect() {
if (this.es) {
console.log(`${TAG} already connected`);
return;
}
this.connecting = true;
const ESClass = this.options.EventSourceClass;
const headers = {
'Last-Event-ID': this._lastEventId || '0'
};
if (this.options.arcApiKey) {
headers['Authorization'] = `Bearer ${this.options.arcApiKey}`;
}
console.log(`${TAG} connecting to ${this.url} (Last-Event-ID: ${headers['Last-Event-ID']})`);
this.es = new ESClass(this.url, {
headers,
debug: true,
pollingInterval: 0 // Don't auto-reconnect on close — we manage lifecycle
});
this.es.addEventListener('open', () => {
this.connected = true;
this.connecting = false;
console.log(`${TAG} connected`);
});
this.es.addEventListener('status', (event) => {
var _a, _b;
try {
const data = JSON.parse(event.data);
console.log(`${TAG} event: txid=${data.txid} status=${data.txStatus}`);
if (event.lastEventId) {
this._lastEventId = event.lastEventId;
(_b = (_a = this.options).onLastEventIdChanged) === null || _b === void 0 ? void 0 : _b.call(_a, event.lastEventId);
}
this.options.onEvent(data);
}
catch (_c) {
console.log(`${TAG} malformed event: ${String(event.data).substring(0, 200)}`);
}
});
this.es.addEventListener('error', (event) => {
var _a, _b;
console.log(`${TAG} error:`, JSON.stringify(event));
this.connected = false;
this.connecting = false;
(_b = (_a = this.options).onError) === null || _b === void 0 ? void 0 : _b.call(_a, new Error(event.message || 'SSE error'));
});
}
/** Close the connection and clean up */
close() {
if (this.es) {
console.log(`${TAG} closing`);
this.es.close();
this.es = null;
this.connected = false;
this.connecting = false;
}
}
/**
* Ensure connection is open. If already connected, this is a no-op.
* If not connected, opens a new connection with catchup from lastEventId.
* Returns immediately — events arrive asynchronously via onEvent callback.
*/
async fetchEvents() {
if (!this.es && !this.connecting) {
this.connect();
}
else if (this.es && !this.connected && !this.connecting) {
// Connection exists but failed — reconnect
this.close();
this.connect();
}
// Events arrive asynchronously — return 0 since we can't know the count synchronously
return 0;
}
}
exports.ArcSSEClient = ArcSSEClient;
//# sourceMappingURL=ArcSSEClient.js.map