@covalenthq/client-sdk 2.2.6 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -8
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1274 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/src/GoldRushClient.d.ts +4 -1
- package/dist/cjs/src/services/StreamingService.d.ts +151 -0
- package/dist/cjs/src/utils/types/StreamingService.types.d.ts +201 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1275 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/GoldRushClient.d.ts +4 -1
- package/dist/esm/src/services/StreamingService.d.ts +151 -0
- package/dist/esm/src/utils/types/StreamingService.types.d.ts +201 -0
- package/package.json +16 -14
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var version = "2.
|
|
1
|
+
var version = "2.3.0";
|
|
2
2
|
|
|
3
3
|
const bigIntParser = (val) => {
|
|
4
4
|
if (val === null || val === undefined) {
|
|
@@ -2408,6 +2408,1238 @@ class SecurityService {
|
|
|
2408
2408
|
}
|
|
2409
2409
|
}
|
|
2410
2410
|
|
|
2411
|
+
function extendedTypeof(val) {
|
|
2412
|
+
if (val === null) {
|
|
2413
|
+
return "null";
|
|
2414
|
+
}
|
|
2415
|
+
if (Array.isArray(val)) {
|
|
2416
|
+
return "array";
|
|
2417
|
+
}
|
|
2418
|
+
return typeof val;
|
|
2419
|
+
}
|
|
2420
|
+
function isObject(val) {
|
|
2421
|
+
return extendedTypeof(val) === "object";
|
|
2422
|
+
}
|
|
2423
|
+
function areGraphQLFormattedErrors(obj) {
|
|
2424
|
+
return Array.isArray(obj) && // must be at least one error
|
|
2425
|
+
obj.length > 0 && // error has at least a message
|
|
2426
|
+
obj.every((ob) => "message" in ob);
|
|
2427
|
+
}
|
|
2428
|
+
function limitCloseReason(reason, whenTooLong) {
|
|
2429
|
+
return reason.length < 124 ? reason : whenTooLong;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
const GRAPHQL_TRANSPORT_WS_PROTOCOL = "graphql-transport-ws";
|
|
2433
|
+
var CloseCode = /* @__PURE__ */ ((CloseCode2) => {
|
|
2434
|
+
CloseCode2[CloseCode2["InternalServerError"] = 4500] = "InternalServerError";
|
|
2435
|
+
CloseCode2[CloseCode2["InternalClientError"] = 4005] = "InternalClientError";
|
|
2436
|
+
CloseCode2[CloseCode2["BadRequest"] = 4400] = "BadRequest";
|
|
2437
|
+
CloseCode2[CloseCode2["BadResponse"] = 4004] = "BadResponse";
|
|
2438
|
+
CloseCode2[CloseCode2["Unauthorized"] = 4401] = "Unauthorized";
|
|
2439
|
+
CloseCode2[CloseCode2["Forbidden"] = 4403] = "Forbidden";
|
|
2440
|
+
CloseCode2[CloseCode2["SubprotocolNotAcceptable"] = 4406] = "SubprotocolNotAcceptable";
|
|
2441
|
+
CloseCode2[CloseCode2["ConnectionInitialisationTimeout"] = 4408] = "ConnectionInitialisationTimeout";
|
|
2442
|
+
CloseCode2[CloseCode2["ConnectionAcknowledgementTimeout"] = 4504] = "ConnectionAcknowledgementTimeout";
|
|
2443
|
+
CloseCode2[CloseCode2["SubscriberAlreadyExists"] = 4409] = "SubscriberAlreadyExists";
|
|
2444
|
+
CloseCode2[CloseCode2["TooManyInitialisationRequests"] = 4429] = "TooManyInitialisationRequests";
|
|
2445
|
+
return CloseCode2;
|
|
2446
|
+
})(CloseCode || {});
|
|
2447
|
+
var MessageType = /* @__PURE__ */ ((MessageType2) => {
|
|
2448
|
+
MessageType2["ConnectionInit"] = "connection_init";
|
|
2449
|
+
MessageType2["ConnectionAck"] = "connection_ack";
|
|
2450
|
+
MessageType2["Ping"] = "ping";
|
|
2451
|
+
MessageType2["Pong"] = "pong";
|
|
2452
|
+
MessageType2["Subscribe"] = "subscribe";
|
|
2453
|
+
MessageType2["Next"] = "next";
|
|
2454
|
+
MessageType2["Error"] = "error";
|
|
2455
|
+
MessageType2["Complete"] = "complete";
|
|
2456
|
+
return MessageType2;
|
|
2457
|
+
})(MessageType || {});
|
|
2458
|
+
function validateMessage(val) {
|
|
2459
|
+
if (!isObject(val)) {
|
|
2460
|
+
throw new Error(
|
|
2461
|
+
`Message is expected to be an object, but got ${extendedTypeof(val)}`
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
if (!val.type) {
|
|
2465
|
+
throw new Error(`Message is missing the 'type' property`);
|
|
2466
|
+
}
|
|
2467
|
+
if (typeof val.type !== "string") {
|
|
2468
|
+
throw new Error(
|
|
2469
|
+
`Message is expects the 'type' property to be a string, but got ${extendedTypeof(
|
|
2470
|
+
val.type
|
|
2471
|
+
)}`
|
|
2472
|
+
);
|
|
2473
|
+
}
|
|
2474
|
+
switch (val.type) {
|
|
2475
|
+
case "connection_init" /* ConnectionInit */:
|
|
2476
|
+
case "connection_ack" /* ConnectionAck */:
|
|
2477
|
+
case "ping" /* Ping */:
|
|
2478
|
+
case "pong" /* Pong */: {
|
|
2479
|
+
if (val.payload != null && !isObject(val.payload)) {
|
|
2480
|
+
throw new Error(
|
|
2481
|
+
`"${val.type}" message expects the 'payload' property to be an object or nullish or missing, but got "${val.payload}"`
|
|
2482
|
+
);
|
|
2483
|
+
}
|
|
2484
|
+
break;
|
|
2485
|
+
}
|
|
2486
|
+
case "subscribe" /* Subscribe */: {
|
|
2487
|
+
if (typeof val.id !== "string") {
|
|
2488
|
+
throw new Error(
|
|
2489
|
+
`"${val.type}" message expects the 'id' property to be a string, but got ${extendedTypeof(
|
|
2490
|
+
val.id
|
|
2491
|
+
)}`
|
|
2492
|
+
);
|
|
2493
|
+
}
|
|
2494
|
+
if (!val.id) {
|
|
2495
|
+
throw new Error(
|
|
2496
|
+
`"${val.type}" message requires a non-empty 'id' property`
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
if (!isObject(val.payload)) {
|
|
2500
|
+
throw new Error(
|
|
2501
|
+
`"${val.type}" message expects the 'payload' property to be an object, but got ${extendedTypeof(
|
|
2502
|
+
val.payload
|
|
2503
|
+
)}`
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
if (typeof val.payload.query !== "string") {
|
|
2507
|
+
throw new Error(
|
|
2508
|
+
`"${val.type}" message payload expects the 'query' property to be a string, but got ${extendedTypeof(
|
|
2509
|
+
val.payload.query
|
|
2510
|
+
)}`
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
if (val.payload.variables != null && !isObject(val.payload.variables)) {
|
|
2514
|
+
throw new Error(
|
|
2515
|
+
`"${val.type}" message payload expects the 'variables' property to be a an object or nullish or missing, but got ${extendedTypeof(
|
|
2516
|
+
val.payload.variables
|
|
2517
|
+
)}`
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
if (val.payload.operationName != null && extendedTypeof(val.payload.operationName) !== "string") {
|
|
2521
|
+
throw new Error(
|
|
2522
|
+
`"${val.type}" message payload expects the 'operationName' property to be a string or nullish or missing, but got ${extendedTypeof(
|
|
2523
|
+
val.payload.operationName
|
|
2524
|
+
)}`
|
|
2525
|
+
);
|
|
2526
|
+
}
|
|
2527
|
+
if (val.payload.extensions != null && !isObject(val.payload.extensions)) {
|
|
2528
|
+
throw new Error(
|
|
2529
|
+
`"${val.type}" message payload expects the 'extensions' property to be a an object or nullish or missing, but got ${extendedTypeof(
|
|
2530
|
+
val.payload.extensions
|
|
2531
|
+
)}`
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
break;
|
|
2535
|
+
}
|
|
2536
|
+
case "next" /* Next */: {
|
|
2537
|
+
if (typeof val.id !== "string") {
|
|
2538
|
+
throw new Error(
|
|
2539
|
+
`"${val.type}" message expects the 'id' property to be a string, but got ${extendedTypeof(
|
|
2540
|
+
val.id
|
|
2541
|
+
)}`
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2544
|
+
if (!val.id) {
|
|
2545
|
+
throw new Error(
|
|
2546
|
+
`"${val.type}" message requires a non-empty 'id' property`
|
|
2547
|
+
);
|
|
2548
|
+
}
|
|
2549
|
+
if (!isObject(val.payload)) {
|
|
2550
|
+
throw new Error(
|
|
2551
|
+
`"${val.type}" message expects the 'payload' property to be an object, but got ${extendedTypeof(
|
|
2552
|
+
val.payload
|
|
2553
|
+
)}`
|
|
2554
|
+
);
|
|
2555
|
+
}
|
|
2556
|
+
break;
|
|
2557
|
+
}
|
|
2558
|
+
case "error" /* Error */: {
|
|
2559
|
+
if (typeof val.id !== "string") {
|
|
2560
|
+
throw new Error(
|
|
2561
|
+
`"${val.type}" message expects the 'id' property to be a string, but got ${extendedTypeof(
|
|
2562
|
+
val.id
|
|
2563
|
+
)}`
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
if (!val.id) {
|
|
2567
|
+
throw new Error(
|
|
2568
|
+
`"${val.type}" message requires a non-empty 'id' property`
|
|
2569
|
+
);
|
|
2570
|
+
}
|
|
2571
|
+
if (!areGraphQLFormattedErrors(val.payload)) {
|
|
2572
|
+
throw new Error(
|
|
2573
|
+
`"${val.type}" message expects the 'payload' property to be an array of GraphQL errors, but got ${JSON.stringify(
|
|
2574
|
+
val.payload
|
|
2575
|
+
)}`
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
break;
|
|
2579
|
+
}
|
|
2580
|
+
case "complete" /* Complete */: {
|
|
2581
|
+
if (typeof val.id !== "string") {
|
|
2582
|
+
throw new Error(
|
|
2583
|
+
`"${val.type}" message expects the 'id' property to be a string, but got ${extendedTypeof(
|
|
2584
|
+
val.id
|
|
2585
|
+
)}`
|
|
2586
|
+
);
|
|
2587
|
+
}
|
|
2588
|
+
if (!val.id) {
|
|
2589
|
+
throw new Error(
|
|
2590
|
+
`"${val.type}" message requires a non-empty 'id' property`
|
|
2591
|
+
);
|
|
2592
|
+
}
|
|
2593
|
+
break;
|
|
2594
|
+
}
|
|
2595
|
+
default:
|
|
2596
|
+
throw new Error(`Invalid message 'type' property "${val.type}"`);
|
|
2597
|
+
}
|
|
2598
|
+
return val;
|
|
2599
|
+
}
|
|
2600
|
+
function parseMessage(data, reviver) {
|
|
2601
|
+
return validateMessage(
|
|
2602
|
+
typeof data === "string" ? JSON.parse(data, reviver) : data
|
|
2603
|
+
);
|
|
2604
|
+
}
|
|
2605
|
+
function stringifyMessage(msg, replacer) {
|
|
2606
|
+
validateMessage(msg);
|
|
2607
|
+
return JSON.stringify(msg, replacer);
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
function createClient(options) {
|
|
2611
|
+
const {
|
|
2612
|
+
url,
|
|
2613
|
+
connectionParams,
|
|
2614
|
+
lazy = true,
|
|
2615
|
+
onNonLazyError = console.error,
|
|
2616
|
+
lazyCloseTimeout: lazyCloseTimeoutMs = 0,
|
|
2617
|
+
keepAlive = 0,
|
|
2618
|
+
disablePong,
|
|
2619
|
+
connectionAckWaitTimeout = 0,
|
|
2620
|
+
retryAttempts = 5,
|
|
2621
|
+
retryWait = async function randomisedExponentialBackoff(retries2) {
|
|
2622
|
+
const retryDelaySeconds = Math.pow(2, retries2);
|
|
2623
|
+
await new Promise(
|
|
2624
|
+
(resolve) => setTimeout(
|
|
2625
|
+
resolve,
|
|
2626
|
+
retryDelaySeconds * 1e3 + // add random timeout from 300ms to 3s
|
|
2627
|
+
Math.floor(Math.random() * (3e3 - 300) + 300)
|
|
2628
|
+
)
|
|
2629
|
+
);
|
|
2630
|
+
},
|
|
2631
|
+
shouldRetry = isLikeCloseEvent,
|
|
2632
|
+
on,
|
|
2633
|
+
webSocketImpl,
|
|
2634
|
+
/**
|
|
2635
|
+
* Generates a v4 UUID to be used as the ID using `Math`
|
|
2636
|
+
* as the random number generator. Supply your own generator
|
|
2637
|
+
* in case you need more uniqueness.
|
|
2638
|
+
*
|
|
2639
|
+
* Reference: https://gist.github.com/jed/982883
|
|
2640
|
+
*/
|
|
2641
|
+
generateID = function generateUUID() {
|
|
2642
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
2643
|
+
const r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8;
|
|
2644
|
+
return v.toString(16);
|
|
2645
|
+
});
|
|
2646
|
+
},
|
|
2647
|
+
jsonMessageReplacer: replacer,
|
|
2648
|
+
jsonMessageReviver: reviver
|
|
2649
|
+
} = options;
|
|
2650
|
+
let ws;
|
|
2651
|
+
if (webSocketImpl) {
|
|
2652
|
+
if (!isWebSocket(webSocketImpl)) {
|
|
2653
|
+
throw new Error("Invalid WebSocket implementation provided");
|
|
2654
|
+
}
|
|
2655
|
+
ws = webSocketImpl;
|
|
2656
|
+
} else if (typeof WebSocket !== "undefined") {
|
|
2657
|
+
ws = WebSocket;
|
|
2658
|
+
} else if (typeof global !== "undefined") {
|
|
2659
|
+
ws = global.WebSocket || // @ts-expect-error: Support more browsers
|
|
2660
|
+
global.MozWebSocket;
|
|
2661
|
+
} else if (typeof window !== "undefined") {
|
|
2662
|
+
ws = window.WebSocket || // @ts-expect-error: Support more browsers
|
|
2663
|
+
window.MozWebSocket;
|
|
2664
|
+
}
|
|
2665
|
+
if (!ws)
|
|
2666
|
+
throw new Error(
|
|
2667
|
+
"WebSocket implementation missing; on Node you can `import WebSocket from 'ws';` and pass `webSocketImpl: WebSocket` to `createClient`"
|
|
2668
|
+
);
|
|
2669
|
+
const WebSocketImpl = ws;
|
|
2670
|
+
const emitter = (() => {
|
|
2671
|
+
const message = /* @__PURE__ */ (() => {
|
|
2672
|
+
const listeners2 = {};
|
|
2673
|
+
return {
|
|
2674
|
+
on(id, listener) {
|
|
2675
|
+
listeners2[id] = listener;
|
|
2676
|
+
return () => {
|
|
2677
|
+
delete listeners2[id];
|
|
2678
|
+
};
|
|
2679
|
+
},
|
|
2680
|
+
emit(message2) {
|
|
2681
|
+
if ("id" in message2) listeners2[message2.id]?.(message2);
|
|
2682
|
+
}
|
|
2683
|
+
};
|
|
2684
|
+
})();
|
|
2685
|
+
const listeners = {
|
|
2686
|
+
connecting: on?.connecting ? [on.connecting] : [],
|
|
2687
|
+
opened: on?.opened ? [on.opened] : [],
|
|
2688
|
+
connected: on?.connected ? [on.connected] : [],
|
|
2689
|
+
ping: on?.ping ? [on.ping] : [],
|
|
2690
|
+
pong: on?.pong ? [on.pong] : [],
|
|
2691
|
+
message: on?.message ? [message.emit, on.message] : [message.emit],
|
|
2692
|
+
closed: on?.closed ? [on.closed] : [],
|
|
2693
|
+
error: on?.error ? [on.error] : []
|
|
2694
|
+
};
|
|
2695
|
+
return {
|
|
2696
|
+
onMessage: message.on,
|
|
2697
|
+
on(event, listener) {
|
|
2698
|
+
const l = listeners[event];
|
|
2699
|
+
l.push(listener);
|
|
2700
|
+
return () => {
|
|
2701
|
+
l.splice(l.indexOf(listener), 1);
|
|
2702
|
+
};
|
|
2703
|
+
},
|
|
2704
|
+
emit(event, ...args) {
|
|
2705
|
+
for (const listener of [...listeners[event]]) {
|
|
2706
|
+
listener(...args);
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
};
|
|
2710
|
+
})();
|
|
2711
|
+
function errorOrClosed(cb) {
|
|
2712
|
+
const listening = [
|
|
2713
|
+
// errors are fatal and more critical than close events, throw them first
|
|
2714
|
+
emitter.on("error", (err) => {
|
|
2715
|
+
listening.forEach((unlisten) => unlisten());
|
|
2716
|
+
cb(err);
|
|
2717
|
+
}),
|
|
2718
|
+
// closes can be graceful and not fatal, throw them second (if error didnt throw)
|
|
2719
|
+
emitter.on("closed", (event) => {
|
|
2720
|
+
listening.forEach((unlisten) => unlisten());
|
|
2721
|
+
cb(event);
|
|
2722
|
+
})
|
|
2723
|
+
];
|
|
2724
|
+
}
|
|
2725
|
+
let connecting, locks = 0, lazyCloseTimeout, retrying = false, retries = 0, disposed = false;
|
|
2726
|
+
async function connect() {
|
|
2727
|
+
clearTimeout(lazyCloseTimeout);
|
|
2728
|
+
const [socket, throwOnClose] = await (connecting ?? (connecting = new Promise(
|
|
2729
|
+
(connected, denied) => (async () => {
|
|
2730
|
+
if (retrying) {
|
|
2731
|
+
await retryWait(retries);
|
|
2732
|
+
if (!locks) {
|
|
2733
|
+
connecting = undefined;
|
|
2734
|
+
return denied({ code: 1e3, reason: "All Subscriptions Gone" });
|
|
2735
|
+
}
|
|
2736
|
+
retries++;
|
|
2737
|
+
}
|
|
2738
|
+
emitter.emit("connecting", retrying);
|
|
2739
|
+
const socket2 = new WebSocketImpl(
|
|
2740
|
+
typeof url === "function" ? await url() : url,
|
|
2741
|
+
GRAPHQL_TRANSPORT_WS_PROTOCOL
|
|
2742
|
+
);
|
|
2743
|
+
let connectionAckTimeout, queuedPing;
|
|
2744
|
+
function enqueuePing() {
|
|
2745
|
+
if (isFinite(keepAlive) && keepAlive > 0) {
|
|
2746
|
+
clearTimeout(queuedPing);
|
|
2747
|
+
queuedPing = setTimeout(() => {
|
|
2748
|
+
if (socket2.readyState === WebSocketImpl.OPEN) {
|
|
2749
|
+
socket2.send(stringifyMessage({ type: MessageType.Ping }));
|
|
2750
|
+
emitter.emit("ping", false, undefined);
|
|
2751
|
+
}
|
|
2752
|
+
}, keepAlive);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
errorOrClosed((errOrEvent) => {
|
|
2756
|
+
connecting = undefined;
|
|
2757
|
+
clearTimeout(connectionAckTimeout);
|
|
2758
|
+
clearTimeout(queuedPing);
|
|
2759
|
+
denied(errOrEvent);
|
|
2760
|
+
if (errOrEvent instanceof TerminatedCloseEvent) {
|
|
2761
|
+
socket2.close(4499, "Terminated");
|
|
2762
|
+
socket2.onerror = null;
|
|
2763
|
+
socket2.onclose = null;
|
|
2764
|
+
}
|
|
2765
|
+
});
|
|
2766
|
+
socket2.onerror = (err) => emitter.emit("error", err);
|
|
2767
|
+
socket2.onclose = (event) => emitter.emit("closed", event);
|
|
2768
|
+
socket2.onopen = async () => {
|
|
2769
|
+
try {
|
|
2770
|
+
emitter.emit("opened", socket2);
|
|
2771
|
+
const payload = typeof connectionParams === "function" ? await connectionParams() : connectionParams;
|
|
2772
|
+
if (socket2.readyState !== WebSocketImpl.OPEN) return;
|
|
2773
|
+
socket2.send(
|
|
2774
|
+
stringifyMessage(
|
|
2775
|
+
payload ? {
|
|
2776
|
+
type: MessageType.ConnectionInit,
|
|
2777
|
+
payload
|
|
2778
|
+
} : {
|
|
2779
|
+
type: MessageType.ConnectionInit
|
|
2780
|
+
// payload is completely absent if not provided
|
|
2781
|
+
},
|
|
2782
|
+
replacer
|
|
2783
|
+
)
|
|
2784
|
+
);
|
|
2785
|
+
if (isFinite(connectionAckWaitTimeout) && connectionAckWaitTimeout > 0) {
|
|
2786
|
+
connectionAckTimeout = setTimeout(() => {
|
|
2787
|
+
socket2.close(
|
|
2788
|
+
CloseCode.ConnectionAcknowledgementTimeout,
|
|
2789
|
+
"Connection acknowledgement timeout"
|
|
2790
|
+
);
|
|
2791
|
+
}, connectionAckWaitTimeout);
|
|
2792
|
+
}
|
|
2793
|
+
enqueuePing();
|
|
2794
|
+
} catch (err) {
|
|
2795
|
+
emitter.emit("error", err);
|
|
2796
|
+
socket2.close(
|
|
2797
|
+
CloseCode.InternalClientError,
|
|
2798
|
+
limitCloseReason(
|
|
2799
|
+
err instanceof Error ? err.message : String(err),
|
|
2800
|
+
"Internal client error"
|
|
2801
|
+
)
|
|
2802
|
+
);
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
let acknowledged = false;
|
|
2806
|
+
socket2.onmessage = ({ data }) => {
|
|
2807
|
+
try {
|
|
2808
|
+
const message = parseMessage(data, reviver);
|
|
2809
|
+
emitter.emit("message", message);
|
|
2810
|
+
if (message.type === "ping" || message.type === "pong") {
|
|
2811
|
+
emitter.emit(message.type, true, message.payload);
|
|
2812
|
+
if (message.type === "pong") {
|
|
2813
|
+
enqueuePing();
|
|
2814
|
+
} else if (!disablePong) {
|
|
2815
|
+
socket2.send(
|
|
2816
|
+
stringifyMessage(
|
|
2817
|
+
message.payload ? {
|
|
2818
|
+
type: MessageType.Pong,
|
|
2819
|
+
payload: message.payload
|
|
2820
|
+
} : {
|
|
2821
|
+
type: MessageType.Pong
|
|
2822
|
+
// payload is completely absent if not provided
|
|
2823
|
+
}
|
|
2824
|
+
)
|
|
2825
|
+
);
|
|
2826
|
+
emitter.emit("pong", false, message.payload);
|
|
2827
|
+
}
|
|
2828
|
+
return;
|
|
2829
|
+
}
|
|
2830
|
+
if (acknowledged) return;
|
|
2831
|
+
if (message.type !== MessageType.ConnectionAck)
|
|
2832
|
+
throw new Error(
|
|
2833
|
+
`First message cannot be of type ${message.type}`
|
|
2834
|
+
);
|
|
2835
|
+
clearTimeout(connectionAckTimeout);
|
|
2836
|
+
acknowledged = true;
|
|
2837
|
+
emitter.emit("connected", socket2, message.payload, retrying);
|
|
2838
|
+
retrying = false;
|
|
2839
|
+
retries = 0;
|
|
2840
|
+
connected([
|
|
2841
|
+
socket2,
|
|
2842
|
+
new Promise((_, reject) => errorOrClosed(reject))
|
|
2843
|
+
]);
|
|
2844
|
+
} catch (err) {
|
|
2845
|
+
socket2.onmessage = null;
|
|
2846
|
+
emitter.emit("error", err);
|
|
2847
|
+
socket2.close(
|
|
2848
|
+
CloseCode.BadResponse,
|
|
2849
|
+
limitCloseReason(
|
|
2850
|
+
err instanceof Error ? err.message : String(err),
|
|
2851
|
+
"Bad response"
|
|
2852
|
+
)
|
|
2853
|
+
);
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
})()
|
|
2857
|
+
)));
|
|
2858
|
+
if (socket.readyState === WebSocketImpl.CLOSING) await throwOnClose;
|
|
2859
|
+
let release = () => {
|
|
2860
|
+
};
|
|
2861
|
+
const released = new Promise((resolve) => release = resolve);
|
|
2862
|
+
return [
|
|
2863
|
+
socket,
|
|
2864
|
+
release,
|
|
2865
|
+
Promise.race([
|
|
2866
|
+
// wait for
|
|
2867
|
+
released.then(() => {
|
|
2868
|
+
if (!locks) {
|
|
2869
|
+
const complete = () => socket.close(1e3, "Normal Closure");
|
|
2870
|
+
if (isFinite(lazyCloseTimeoutMs) && lazyCloseTimeoutMs > 0) {
|
|
2871
|
+
lazyCloseTimeout = setTimeout(() => {
|
|
2872
|
+
if (socket.readyState === WebSocketImpl.OPEN) complete();
|
|
2873
|
+
}, lazyCloseTimeoutMs);
|
|
2874
|
+
} else {
|
|
2875
|
+
complete();
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
}),
|
|
2879
|
+
// or
|
|
2880
|
+
throwOnClose
|
|
2881
|
+
])
|
|
2882
|
+
];
|
|
2883
|
+
}
|
|
2884
|
+
function shouldRetryConnectOrThrow(errOrCloseEvent) {
|
|
2885
|
+
if (isLikeCloseEvent(errOrCloseEvent) && (isFatalInternalCloseCode(errOrCloseEvent.code) || [
|
|
2886
|
+
CloseCode.InternalServerError,
|
|
2887
|
+
CloseCode.InternalClientError,
|
|
2888
|
+
CloseCode.BadRequest,
|
|
2889
|
+
CloseCode.BadResponse,
|
|
2890
|
+
CloseCode.Unauthorized,
|
|
2891
|
+
// CloseCode.Forbidden, might grant access out after retry
|
|
2892
|
+
CloseCode.SubprotocolNotAcceptable,
|
|
2893
|
+
// CloseCode.ConnectionInitialisationTimeout, might not time out after retry
|
|
2894
|
+
// CloseCode.ConnectionAcknowledgementTimeout, might not time out after retry
|
|
2895
|
+
CloseCode.SubscriberAlreadyExists,
|
|
2896
|
+
CloseCode.TooManyInitialisationRequests
|
|
2897
|
+
// 4499, // Terminated, probably because the socket froze, we want to retry
|
|
2898
|
+
].includes(errOrCloseEvent.code)))
|
|
2899
|
+
throw errOrCloseEvent;
|
|
2900
|
+
if (disposed) return false;
|
|
2901
|
+
if (isLikeCloseEvent(errOrCloseEvent) && errOrCloseEvent.code === 1e3)
|
|
2902
|
+
return locks > 0;
|
|
2903
|
+
if (!retryAttempts || retries >= retryAttempts) throw errOrCloseEvent;
|
|
2904
|
+
if (!shouldRetry(errOrCloseEvent)) throw errOrCloseEvent;
|
|
2905
|
+
return retrying = true;
|
|
2906
|
+
}
|
|
2907
|
+
if (!lazy) {
|
|
2908
|
+
(async () => {
|
|
2909
|
+
locks++;
|
|
2910
|
+
for (; ; ) {
|
|
2911
|
+
try {
|
|
2912
|
+
const [, , throwOnClose] = await connect();
|
|
2913
|
+
await throwOnClose;
|
|
2914
|
+
} catch (errOrCloseEvent) {
|
|
2915
|
+
try {
|
|
2916
|
+
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) return;
|
|
2917
|
+
} catch (errOrCloseEvent2) {
|
|
2918
|
+
return onNonLazyError?.(errOrCloseEvent2);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
})();
|
|
2923
|
+
}
|
|
2924
|
+
function subscribe(payload, sink) {
|
|
2925
|
+
const id = generateID(payload);
|
|
2926
|
+
let done = false, errored = false, releaser = () => {
|
|
2927
|
+
locks--;
|
|
2928
|
+
done = true;
|
|
2929
|
+
};
|
|
2930
|
+
(async () => {
|
|
2931
|
+
locks++;
|
|
2932
|
+
for (; ; ) {
|
|
2933
|
+
try {
|
|
2934
|
+
const [socket, release, waitForReleaseOrThrowOnClose] = await connect();
|
|
2935
|
+
if (done) return release();
|
|
2936
|
+
const unlisten = emitter.onMessage(id, (message) => {
|
|
2937
|
+
switch (message.type) {
|
|
2938
|
+
case MessageType.Next: {
|
|
2939
|
+
sink.next(message.payload);
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
case MessageType.Error: {
|
|
2943
|
+
errored = true, done = true;
|
|
2944
|
+
sink.error(message.payload);
|
|
2945
|
+
releaser();
|
|
2946
|
+
return;
|
|
2947
|
+
}
|
|
2948
|
+
case MessageType.Complete: {
|
|
2949
|
+
done = true;
|
|
2950
|
+
releaser();
|
|
2951
|
+
return;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
socket.send(
|
|
2956
|
+
stringifyMessage(
|
|
2957
|
+
{
|
|
2958
|
+
id,
|
|
2959
|
+
type: MessageType.Subscribe,
|
|
2960
|
+
payload
|
|
2961
|
+
},
|
|
2962
|
+
replacer
|
|
2963
|
+
)
|
|
2964
|
+
);
|
|
2965
|
+
releaser = () => {
|
|
2966
|
+
if (!done && socket.readyState === WebSocketImpl.OPEN)
|
|
2967
|
+
socket.send(
|
|
2968
|
+
stringifyMessage(
|
|
2969
|
+
{
|
|
2970
|
+
id,
|
|
2971
|
+
type: MessageType.Complete
|
|
2972
|
+
},
|
|
2973
|
+
replacer
|
|
2974
|
+
)
|
|
2975
|
+
);
|
|
2976
|
+
locks--;
|
|
2977
|
+
done = true;
|
|
2978
|
+
release();
|
|
2979
|
+
};
|
|
2980
|
+
await waitForReleaseOrThrowOnClose.finally(unlisten);
|
|
2981
|
+
return;
|
|
2982
|
+
} catch (errOrCloseEvent) {
|
|
2983
|
+
if (!shouldRetryConnectOrThrow(errOrCloseEvent)) return;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
})().then(() => {
|
|
2987
|
+
if (!errored) sink.complete();
|
|
2988
|
+
}).catch((err) => {
|
|
2989
|
+
sink.error(err);
|
|
2990
|
+
});
|
|
2991
|
+
return () => {
|
|
2992
|
+
if (!done) releaser();
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
return {
|
|
2996
|
+
on: emitter.on,
|
|
2997
|
+
subscribe,
|
|
2998
|
+
iterate(request) {
|
|
2999
|
+
const pending = [];
|
|
3000
|
+
const deferred = {
|
|
3001
|
+
done: false,
|
|
3002
|
+
error: null,
|
|
3003
|
+
resolve: () => {
|
|
3004
|
+
}
|
|
3005
|
+
};
|
|
3006
|
+
const dispose = subscribe(request, {
|
|
3007
|
+
next(val) {
|
|
3008
|
+
pending.push(val);
|
|
3009
|
+
deferred.resolve();
|
|
3010
|
+
},
|
|
3011
|
+
error(err) {
|
|
3012
|
+
deferred.done = true;
|
|
3013
|
+
deferred.error = err;
|
|
3014
|
+
deferred.resolve();
|
|
3015
|
+
},
|
|
3016
|
+
complete() {
|
|
3017
|
+
deferred.done = true;
|
|
3018
|
+
deferred.resolve();
|
|
3019
|
+
}
|
|
3020
|
+
});
|
|
3021
|
+
const iterator = async function* iterator2() {
|
|
3022
|
+
for (; ; ) {
|
|
3023
|
+
if (!pending.length) {
|
|
3024
|
+
await new Promise((resolve) => deferred.resolve = resolve);
|
|
3025
|
+
}
|
|
3026
|
+
while (pending.length) {
|
|
3027
|
+
yield pending.shift();
|
|
3028
|
+
}
|
|
3029
|
+
if (deferred.error) {
|
|
3030
|
+
throw deferred.error;
|
|
3031
|
+
}
|
|
3032
|
+
if (deferred.done) {
|
|
3033
|
+
return;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
}();
|
|
3037
|
+
iterator.throw = async (err) => {
|
|
3038
|
+
if (!deferred.done) {
|
|
3039
|
+
deferred.done = true;
|
|
3040
|
+
deferred.error = err;
|
|
3041
|
+
deferred.resolve();
|
|
3042
|
+
}
|
|
3043
|
+
return { done: true, value: undefined };
|
|
3044
|
+
};
|
|
3045
|
+
iterator.return = async () => {
|
|
3046
|
+
dispose();
|
|
3047
|
+
return { done: true, value: undefined };
|
|
3048
|
+
};
|
|
3049
|
+
return iterator;
|
|
3050
|
+
},
|
|
3051
|
+
async dispose() {
|
|
3052
|
+
disposed = true;
|
|
3053
|
+
if (connecting) {
|
|
3054
|
+
const [socket] = await connecting;
|
|
3055
|
+
socket.close(1e3, "Normal Closure");
|
|
3056
|
+
}
|
|
3057
|
+
},
|
|
3058
|
+
terminate() {
|
|
3059
|
+
if (connecting) {
|
|
3060
|
+
emitter.emit("closed", new TerminatedCloseEvent());
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
class TerminatedCloseEvent extends Error {
|
|
3066
|
+
name = "TerminatedCloseEvent";
|
|
3067
|
+
message = "4499: Terminated";
|
|
3068
|
+
code = 4499;
|
|
3069
|
+
reason = "Terminated";
|
|
3070
|
+
wasClean = false;
|
|
3071
|
+
}
|
|
3072
|
+
function isLikeCloseEvent(val) {
|
|
3073
|
+
return isObject(val) && "code" in val && "reason" in val;
|
|
3074
|
+
}
|
|
3075
|
+
function isFatalInternalCloseCode(code) {
|
|
3076
|
+
if ([
|
|
3077
|
+
1e3,
|
|
3078
|
+
// Normal Closure is not an erroneous close code
|
|
3079
|
+
1001,
|
|
3080
|
+
// Going Away
|
|
3081
|
+
1006,
|
|
3082
|
+
// Abnormal Closure
|
|
3083
|
+
1005,
|
|
3084
|
+
// No Status Received
|
|
3085
|
+
1012,
|
|
3086
|
+
// Service Restart
|
|
3087
|
+
1013,
|
|
3088
|
+
// Try Again Later
|
|
3089
|
+
1014
|
|
3090
|
+
// Bad Gateway
|
|
3091
|
+
].includes(code))
|
|
3092
|
+
return false;
|
|
3093
|
+
return code >= 1e3 && code <= 1999;
|
|
3094
|
+
}
|
|
3095
|
+
function isWebSocket(val) {
|
|
3096
|
+
return typeof val === "function" && "constructor" in val && "CLOSED" in val && "CLOSING" in val && "CONNECTING" in val && "OPEN" in val;
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
/**
|
|
3100
|
+
* Streaming API Service
|
|
3101
|
+
*
|
|
3102
|
+
*/
|
|
3103
|
+
class StreamingService {
|
|
3104
|
+
constructor(apiKey, config) {
|
|
3105
|
+
this.defaultConfig = {
|
|
3106
|
+
shouldRetry: (retries) => retries < 5,
|
|
3107
|
+
maxReconnectAttempts: 5,
|
|
3108
|
+
onConnecting: () => {
|
|
3109
|
+
console.info("StreamingService Connection Connecting...");
|
|
3110
|
+
},
|
|
3111
|
+
onOpened: () => {
|
|
3112
|
+
console.info("StreamingService Connection Established Successfully!");
|
|
3113
|
+
},
|
|
3114
|
+
onClosed: () => {
|
|
3115
|
+
console.info("StreamingService Connection Closed");
|
|
3116
|
+
},
|
|
3117
|
+
onError: (err) => {
|
|
3118
|
+
console.error("StreamingService Connection Error:", err);
|
|
3119
|
+
},
|
|
3120
|
+
};
|
|
3121
|
+
StreamingWebSocketClient.configure(apiKey, {
|
|
3122
|
+
...this.defaultConfig,
|
|
3123
|
+
...config,
|
|
3124
|
+
});
|
|
3125
|
+
StreamingWebSocketClient.getInstance();
|
|
3126
|
+
}
|
|
3127
|
+
/**
|
|
3128
|
+
* Initialize the streaming connection
|
|
3129
|
+
*/
|
|
3130
|
+
getClient() {
|
|
3131
|
+
return StreamingWebSocketClient.getClient();
|
|
3132
|
+
}
|
|
3133
|
+
/**
|
|
3134
|
+
* Disconnect from the streaming service
|
|
3135
|
+
*/
|
|
3136
|
+
async disconnect() {
|
|
3137
|
+
await StreamingWebSocketClient.disconnect();
|
|
3138
|
+
}
|
|
3139
|
+
/**
|
|
3140
|
+
* Check if the client is connected
|
|
3141
|
+
*/
|
|
3142
|
+
get isConnected() {
|
|
3143
|
+
return StreamingWebSocketClient.isConnected;
|
|
3144
|
+
}
|
|
3145
|
+
/**
|
|
3146
|
+
* Subscribe to a custom GraphQL subscription
|
|
3147
|
+
* This allows for advanced usage and future extensibility
|
|
3148
|
+
*
|
|
3149
|
+
* @param query - GraphQL subscription query
|
|
3150
|
+
* @param variables - Query variables
|
|
3151
|
+
* @param callbacks - Subscription callbacks
|
|
3152
|
+
* @returns Unsubscribe function
|
|
3153
|
+
*/
|
|
3154
|
+
rawQuery(query, variables, callbacks) {
|
|
3155
|
+
const client = StreamingWebSocketClient.getClient();
|
|
3156
|
+
return client.subscribe({
|
|
3157
|
+
query,
|
|
3158
|
+
variables,
|
|
3159
|
+
}, {
|
|
3160
|
+
next: (data) => callbacks.next(data),
|
|
3161
|
+
error: callbacks.error || (() => { }),
|
|
3162
|
+
complete: callbacks.complete || (() => { }),
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
/**
|
|
3166
|
+
* Subscribe to OHLCV data for specific pairs
|
|
3167
|
+
*
|
|
3168
|
+
* @param params - Parameters for the OHLCV pairs stream
|
|
3169
|
+
* @param callbacks - Subscription callbacks
|
|
3170
|
+
* @returns Unsubscribe function
|
|
3171
|
+
*
|
|
3172
|
+
* @example
|
|
3173
|
+
* ```typescript
|
|
3174
|
+
* const unsubscribe = streamingService.subscribeToOHLCVPairs(
|
|
3175
|
+
* {
|
|
3176
|
+
* chain_name: StreamingChain.BASE_MAINNET,
|
|
3177
|
+
* pair_addresses: ["0x9c087Eb773291e50CF6c6a90ef0F4500e349B903"],
|
|
3178
|
+
* interval: StreamingInterval.ONE_MINUTE,
|
|
3179
|
+
* timeframe: StreamingTimeframe.ONE_HOUR
|
|
3180
|
+
* },
|
|
3181
|
+
* {
|
|
3182
|
+
* next: (data) => console.log("OHLCV Data:", data),
|
|
3183
|
+
* error: (err) => console.error("Error:", err),
|
|
3184
|
+
* complete: () => console.log("Stream completed")
|
|
3185
|
+
* }
|
|
3186
|
+
* );
|
|
3187
|
+
* ```
|
|
3188
|
+
*/
|
|
3189
|
+
subscribeToOHLCVPairs(params, callbacks) {
|
|
3190
|
+
// Format the pair addresses array for the query
|
|
3191
|
+
const pairAddressesString = params.pair_addresses
|
|
3192
|
+
.map((addr) => `"${addr}"`)
|
|
3193
|
+
.join(", ");
|
|
3194
|
+
const query = `
|
|
3195
|
+
subscription {
|
|
3196
|
+
ohlcvCandlesForPair(
|
|
3197
|
+
chain_name: ${params.chain_name}
|
|
3198
|
+
pair_addresses: [${pairAddressesString}]
|
|
3199
|
+
interval: ${params.interval}
|
|
3200
|
+
timeframe: ${params.timeframe}
|
|
3201
|
+
${params.limit ? `limit: ${params.limit}` : ""}
|
|
3202
|
+
) {
|
|
3203
|
+
open
|
|
3204
|
+
high
|
|
3205
|
+
low
|
|
3206
|
+
close
|
|
3207
|
+
volume
|
|
3208
|
+
price_usd
|
|
3209
|
+
volume_usd
|
|
3210
|
+
chain_name
|
|
3211
|
+
pair_address
|
|
3212
|
+
interval
|
|
3213
|
+
timeframe
|
|
3214
|
+
timestamp
|
|
3215
|
+
base_token {
|
|
3216
|
+
contract_name
|
|
3217
|
+
contract_address
|
|
3218
|
+
contract_decimals
|
|
3219
|
+
contract_ticker_symbol
|
|
3220
|
+
}
|
|
3221
|
+
quote_token {
|
|
3222
|
+
contract_name
|
|
3223
|
+
contract_address
|
|
3224
|
+
contract_decimals
|
|
3225
|
+
contract_ticker_symbol
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
`;
|
|
3230
|
+
const client = StreamingWebSocketClient.getClient();
|
|
3231
|
+
return client.subscribe({
|
|
3232
|
+
query,
|
|
3233
|
+
}, {
|
|
3234
|
+
next: (data) => {
|
|
3235
|
+
if (data.data) {
|
|
3236
|
+
const ohlcvData = data.data.ohlcvCandlesForPair;
|
|
3237
|
+
if (ohlcvData) {
|
|
3238
|
+
callbacks.next(ohlcvData);
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
else if (data.errors) {
|
|
3242
|
+
callbacks.error?.(data.errors);
|
|
3243
|
+
}
|
|
3244
|
+
},
|
|
3245
|
+
error: callbacks.error || (() => { }),
|
|
3246
|
+
complete: callbacks.complete || (() => { }),
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
/**
|
|
3250
|
+
* Subscribe to OHLCV data for specific tokens
|
|
3251
|
+
*
|
|
3252
|
+
* @param params - Parameters for the OHLCV tokens stream
|
|
3253
|
+
* @param callbacks - Subscription callbacks
|
|
3254
|
+
* @returns Unsubscribe function
|
|
3255
|
+
*
|
|
3256
|
+
* @example
|
|
3257
|
+
* ```typescript
|
|
3258
|
+
* const unsubscribe = streamingService.subscribeToOHLCVTokens(
|
|
3259
|
+
* {
|
|
3260
|
+
* chain_name: StreamingChain.BASE_MAINNET,
|
|
3261
|
+
* token_addresses: ["0x4B6104755AfB5Da4581B81C552DA3A25608c73B8"],
|
|
3262
|
+
* interval: StreamingInterval.ONE_MINUTE,
|
|
3263
|
+
* timeframe: StreamingTimeframe.ONE_HOUR
|
|
3264
|
+
* },
|
|
3265
|
+
* {
|
|
3266
|
+
* next: (data) => console.log("OHLCV Token Data:", data),
|
|
3267
|
+
* error: (err) => console.error("Error:", err),
|
|
3268
|
+
* complete: () => console.log("Stream completed")
|
|
3269
|
+
* }
|
|
3270
|
+
* );
|
|
3271
|
+
* ```
|
|
3272
|
+
*/
|
|
3273
|
+
subscribeToOHLCVTokens(params, callbacks) {
|
|
3274
|
+
// Format the token addresses array for the query
|
|
3275
|
+
const tokenAddressesString = params.token_addresses
|
|
3276
|
+
.map((addr) => `"${addr}"`)
|
|
3277
|
+
.join(", ");
|
|
3278
|
+
const query = `
|
|
3279
|
+
subscription {
|
|
3280
|
+
ohlcvCandlesForToken(
|
|
3281
|
+
chain_name: ${params.chain_name}
|
|
3282
|
+
token_addresses: [${tokenAddressesString}]
|
|
3283
|
+
interval: ${params.interval}
|
|
3284
|
+
timeframe: ${params.timeframe}
|
|
3285
|
+
${params.limit ? `limit: ${params.limit}` : ""}
|
|
3286
|
+
) {
|
|
3287
|
+
chain_name
|
|
3288
|
+
pair_address
|
|
3289
|
+
interval
|
|
3290
|
+
timeframe
|
|
3291
|
+
timestamp
|
|
3292
|
+
open
|
|
3293
|
+
high
|
|
3294
|
+
low
|
|
3295
|
+
close
|
|
3296
|
+
volume
|
|
3297
|
+
volume_usd
|
|
3298
|
+
quote_rate
|
|
3299
|
+
quote_rate_usd
|
|
3300
|
+
base_token {
|
|
3301
|
+
contract_name
|
|
3302
|
+
contract_address
|
|
3303
|
+
contract_decimals
|
|
3304
|
+
contract_ticker_symbol
|
|
3305
|
+
}
|
|
3306
|
+
quote_token {
|
|
3307
|
+
contract_name
|
|
3308
|
+
contract_address
|
|
3309
|
+
contract_decimals
|
|
3310
|
+
contract_ticker_symbol
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
`;
|
|
3315
|
+
const client = StreamingWebSocketClient.getClient();
|
|
3316
|
+
return client.subscribe({
|
|
3317
|
+
query,
|
|
3318
|
+
}, {
|
|
3319
|
+
next: (data) => {
|
|
3320
|
+
if (data.data) {
|
|
3321
|
+
const ohlcvData = data.data.ohlcvCandlesForToken;
|
|
3322
|
+
if (ohlcvData) {
|
|
3323
|
+
callbacks.next(ohlcvData);
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
else if (data.errors) {
|
|
3327
|
+
callbacks.error?.(data.errors);
|
|
3328
|
+
}
|
|
3329
|
+
},
|
|
3330
|
+
error: callbacks.error || (() => { }),
|
|
3331
|
+
complete: callbacks.complete || (() => { }),
|
|
3332
|
+
});
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Subscribe to new DEX pairs created on supported decentralized exchanges
|
|
3336
|
+
*
|
|
3337
|
+
* @param params - Parameters for the new pairs stream
|
|
3338
|
+
* @param callbacks - Subscription callbacks
|
|
3339
|
+
* @returns Unsubscribe function
|
|
3340
|
+
*
|
|
3341
|
+
* @example
|
|
3342
|
+
* ```typescript
|
|
3343
|
+
* const unsubscribe = streamingService.subscribeToNewPairs(
|
|
3344
|
+
* {
|
|
3345
|
+
* chain_name: StreamingChain.BASE_MAINNET,
|
|
3346
|
+
* protocols: [StreamingProtocol.UNISWAP_V2, StreamingProtocol.UNISWAP_V3]
|
|
3347
|
+
* },
|
|
3348
|
+
* {
|
|
3349
|
+
* next: (data) => console.log("New Pairs:", data),
|
|
3350
|
+
* error: (err) => console.error("Error:", err),
|
|
3351
|
+
* complete: () => console.log("Stream completed")
|
|
3352
|
+
* }
|
|
3353
|
+
* );
|
|
3354
|
+
* ```
|
|
3355
|
+
*/
|
|
3356
|
+
subscribeToNewPairs(params, callbacks) {
|
|
3357
|
+
const query = `
|
|
3358
|
+
subscription {
|
|
3359
|
+
newPairs(
|
|
3360
|
+
chain_name: ${params.chain_name},
|
|
3361
|
+
protocols: [${params.protocols.join(", ")}]
|
|
3362
|
+
) {
|
|
3363
|
+
chain_name
|
|
3364
|
+
protocol
|
|
3365
|
+
protocol_version
|
|
3366
|
+
pair_address
|
|
3367
|
+
deployer_address
|
|
3368
|
+
tx_hash
|
|
3369
|
+
block_signed_at
|
|
3370
|
+
liquidity
|
|
3371
|
+
supply
|
|
3372
|
+
market_cap
|
|
3373
|
+
event_name
|
|
3374
|
+
quote_rate
|
|
3375
|
+
quote_rate_usd
|
|
3376
|
+
base_token_metadata {
|
|
3377
|
+
contract_address
|
|
3378
|
+
contract_decimals
|
|
3379
|
+
contract_name
|
|
3380
|
+
contract_ticker_symbol
|
|
3381
|
+
}
|
|
3382
|
+
pair_metadata {
|
|
3383
|
+
contract_address
|
|
3384
|
+
contract_decimals
|
|
3385
|
+
contract_name
|
|
3386
|
+
contract_ticker_symbol
|
|
3387
|
+
}
|
|
3388
|
+
quote_token_metadata {
|
|
3389
|
+
contract_address
|
|
3390
|
+
contract_decimals
|
|
3391
|
+
contract_name
|
|
3392
|
+
contract_ticker_symbol
|
|
3393
|
+
}
|
|
3394
|
+
prices {
|
|
3395
|
+
last_5m
|
|
3396
|
+
last_1hr
|
|
3397
|
+
last_6hr
|
|
3398
|
+
last_24hr
|
|
3399
|
+
}
|
|
3400
|
+
swaps {
|
|
3401
|
+
last_5m
|
|
3402
|
+
last_1hr
|
|
3403
|
+
last_6hr
|
|
3404
|
+
last_24hr
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
`;
|
|
3409
|
+
const client = StreamingWebSocketClient.getClient();
|
|
3410
|
+
return client.subscribe({
|
|
3411
|
+
query,
|
|
3412
|
+
}, {
|
|
3413
|
+
next: (data) => {
|
|
3414
|
+
if (data.data) {
|
|
3415
|
+
const newPairsData = data.data.newPairs;
|
|
3416
|
+
if (newPairsData) {
|
|
3417
|
+
callbacks.next(newPairsData);
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
else if (data.errors) {
|
|
3421
|
+
callbacks.error?.(data.errors);
|
|
3422
|
+
}
|
|
3423
|
+
},
|
|
3424
|
+
error: callbacks.error || (() => { }),
|
|
3425
|
+
complete: callbacks.complete || (() => { }),
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3428
|
+
/**
|
|
3429
|
+
* Subscribe to real-time token balance updates for a specific wallet address
|
|
3430
|
+
*
|
|
3431
|
+
* @param params - Parameters for the token balances stream
|
|
3432
|
+
* @param callbacks - Subscription callbacks
|
|
3433
|
+
* @returns Unsubscribe function
|
|
3434
|
+
*
|
|
3435
|
+
* @example
|
|
3436
|
+
* ```typescript
|
|
3437
|
+
* const unsubscribe = streamingService.subscribeToTokenBalances(
|
|
3438
|
+
* {
|
|
3439
|
+
* chain_name: StreamingChain.BASE_MAINNET,
|
|
3440
|
+
* wallet_address: "0x198ef79f1f515f02dfe9e3115ed9fc07183f02fc"
|
|
3441
|
+
* },
|
|
3442
|
+
* {
|
|
3443
|
+
* next: (data) => console.log("Token Balances:", data),
|
|
3444
|
+
* error: (err) => console.error("Error:", err),
|
|
3445
|
+
* complete: () => console.log("Stream completed")
|
|
3446
|
+
* }
|
|
3447
|
+
* );
|
|
3448
|
+
* ```
|
|
3449
|
+
*/
|
|
3450
|
+
subscribeToTokenBalances(params, callbacks) {
|
|
3451
|
+
const query = `
|
|
3452
|
+
subscription {
|
|
3453
|
+
tokenBalancesForWalletAddress(
|
|
3454
|
+
chain_name: ${params.chain_name},
|
|
3455
|
+
wallet_address: "${params.wallet_address}"
|
|
3456
|
+
) {
|
|
3457
|
+
wallet_address
|
|
3458
|
+
chain_name
|
|
3459
|
+
last_block
|
|
3460
|
+
items {
|
|
3461
|
+
balance
|
|
3462
|
+
balance_pretty
|
|
3463
|
+
quote_rate_usd
|
|
3464
|
+
quote_usd
|
|
3465
|
+
metadata {
|
|
3466
|
+
contract_name
|
|
3467
|
+
contract_address
|
|
3468
|
+
contract_decimals
|
|
3469
|
+
contract_ticker_symbol
|
|
3470
|
+
}
|
|
3471
|
+
is_native
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
`;
|
|
3476
|
+
const client = StreamingWebSocketClient.getClient();
|
|
3477
|
+
return client.subscribe({
|
|
3478
|
+
query,
|
|
3479
|
+
}, {
|
|
3480
|
+
next: (data) => {
|
|
3481
|
+
if (data.data) {
|
|
3482
|
+
const tokenBalancesData = data.data.tokenBalancesForWalletAddress;
|
|
3483
|
+
if (tokenBalancesData) {
|
|
3484
|
+
callbacks.next(tokenBalancesData);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
else if (data.errors) {
|
|
3488
|
+
callbacks.error?.(data.errors);
|
|
3489
|
+
}
|
|
3490
|
+
},
|
|
3491
|
+
error: callbacks.error || (() => { }),
|
|
3492
|
+
complete: callbacks.complete || (() => { }),
|
|
3493
|
+
});
|
|
3494
|
+
}
|
|
3495
|
+
/**
|
|
3496
|
+
* Subscribe to real-time wallet activity including transactions, token transfers, and smart contract interactions
|
|
3497
|
+
*
|
|
3498
|
+
* @param params - Parameters for the wallet activity stream
|
|
3499
|
+
* @param callbacks - Subscription callbacks
|
|
3500
|
+
* @returns Unsubscribe function
|
|
3501
|
+
*
|
|
3502
|
+
* @example
|
|
3503
|
+
* ```typescript
|
|
3504
|
+
* const unsubscribe = streamingService.subscribeToWalletActivity(
|
|
3505
|
+
* {
|
|
3506
|
+
* chain_name: StreamingChain.BASE_MAINNET,
|
|
3507
|
+
* wallet_addresses: ["0x198ef79f1f515f02dfe9e3115ed9fc07183f02fc"]
|
|
3508
|
+
* },
|
|
3509
|
+
* {
|
|
3510
|
+
* next: (data) => console.log("Wallet Activity:", data),
|
|
3511
|
+
* error: (err) => console.error("Error:", err),
|
|
3512
|
+
* complete: () => console.log("Stream completed")
|
|
3513
|
+
* }
|
|
3514
|
+
* );
|
|
3515
|
+
* ```
|
|
3516
|
+
*/
|
|
3517
|
+
subscribeToWalletActivity(params, callbacks) {
|
|
3518
|
+
// Format the wallet addresses array for the query
|
|
3519
|
+
const walletAddressesString = params.wallet_addresses
|
|
3520
|
+
.map((addr) => `"${addr}"`)
|
|
3521
|
+
.join(", ");
|
|
3522
|
+
const query = `
|
|
3523
|
+
subscription {
|
|
3524
|
+
walletTxs(
|
|
3525
|
+
chain_name: ${params.chain_name},
|
|
3526
|
+
wallet_addresses: [${walletAddressesString}]
|
|
3527
|
+
) {
|
|
3528
|
+
tx_hash
|
|
3529
|
+
from_address
|
|
3530
|
+
to_address
|
|
3531
|
+
value
|
|
3532
|
+
chain_name
|
|
3533
|
+
block_signed_at
|
|
3534
|
+
block_height
|
|
3535
|
+
block_hash
|
|
3536
|
+
miner_address
|
|
3537
|
+
gas_used
|
|
3538
|
+
tx_offset
|
|
3539
|
+
successful
|
|
3540
|
+
decoded_type
|
|
3541
|
+
logs {
|
|
3542
|
+
emitter_address
|
|
3543
|
+
log_offset
|
|
3544
|
+
data
|
|
3545
|
+
topics
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
`;
|
|
3550
|
+
const client = StreamingWebSocketClient.getClient();
|
|
3551
|
+
return client.subscribe({
|
|
3552
|
+
query,
|
|
3553
|
+
}, {
|
|
3554
|
+
next: (data) => {
|
|
3555
|
+
if (data.data) {
|
|
3556
|
+
const walletActivityData = data.data.walletTxs;
|
|
3557
|
+
if (walletActivityData) {
|
|
3558
|
+
callbacks.next(walletActivityData);
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
else if (data.errors) {
|
|
3562
|
+
callbacks.error?.(data.errors);
|
|
3563
|
+
}
|
|
3564
|
+
},
|
|
3565
|
+
error: callbacks.error || (() => { }),
|
|
3566
|
+
complete: callbacks.complete || (() => { }),
|
|
3567
|
+
});
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
/**
|
|
3571
|
+
* Singleton WebSocket Client for GoldRush Streaming
|
|
3572
|
+
*
|
|
3573
|
+
*/
|
|
3574
|
+
class StreamingWebSocketClient {
|
|
3575
|
+
constructor() { }
|
|
3576
|
+
static getInstance() {
|
|
3577
|
+
if (!StreamingWebSocketClient.instance) {
|
|
3578
|
+
StreamingWebSocketClient.instance = new StreamingWebSocketClient();
|
|
3579
|
+
}
|
|
3580
|
+
return StreamingWebSocketClient.instance;
|
|
3581
|
+
}
|
|
3582
|
+
static configure(apiKey, config) {
|
|
3583
|
+
StreamingWebSocketClient.apiKey = apiKey;
|
|
3584
|
+
StreamingWebSocketClient.config = config;
|
|
3585
|
+
}
|
|
3586
|
+
static getClient() {
|
|
3587
|
+
if (!StreamingWebSocketClient.client ||
|
|
3588
|
+
!StreamingWebSocketClient.connected) {
|
|
3589
|
+
StreamingWebSocketClient.connect();
|
|
3590
|
+
}
|
|
3591
|
+
return StreamingWebSocketClient.client;
|
|
3592
|
+
}
|
|
3593
|
+
static connect() {
|
|
3594
|
+
if (StreamingWebSocketClient.client)
|
|
3595
|
+
return;
|
|
3596
|
+
StreamingWebSocketClient.client = createClient({
|
|
3597
|
+
url: "wss://gr-staging.streaming.covalenthq.com/graphql",
|
|
3598
|
+
connectionParams: {
|
|
3599
|
+
GOLDRUSH_API_KEY: StreamingWebSocketClient.apiKey,
|
|
3600
|
+
},
|
|
3601
|
+
shouldRetry: () => StreamingWebSocketClient.config.shouldRetry(StreamingWebSocketClient.reconnectAttempts),
|
|
3602
|
+
on: {
|
|
3603
|
+
connecting: () => {
|
|
3604
|
+
StreamingWebSocketClient.config.onConnecting();
|
|
3605
|
+
},
|
|
3606
|
+
opened: () => {
|
|
3607
|
+
StreamingWebSocketClient.config.onOpened();
|
|
3608
|
+
StreamingWebSocketClient.reconnectAttempts = 0;
|
|
3609
|
+
StreamingWebSocketClient.connected = true;
|
|
3610
|
+
},
|
|
3611
|
+
closed: () => {
|
|
3612
|
+
StreamingWebSocketClient.config.onClosed();
|
|
3613
|
+
StreamingWebSocketClient.connected = false;
|
|
3614
|
+
},
|
|
3615
|
+
error: (err) => {
|
|
3616
|
+
StreamingWebSocketClient.config.onError(err);
|
|
3617
|
+
StreamingWebSocketClient.connected = false;
|
|
3618
|
+
},
|
|
3619
|
+
},
|
|
3620
|
+
});
|
|
3621
|
+
}
|
|
3622
|
+
static get isConnected() {
|
|
3623
|
+
return StreamingWebSocketClient.connected;
|
|
3624
|
+
}
|
|
3625
|
+
static async disconnect() {
|
|
3626
|
+
if (!StreamingWebSocketClient.client)
|
|
3627
|
+
return;
|
|
3628
|
+
if (!StreamingWebSocketClient.connected)
|
|
3629
|
+
return;
|
|
3630
|
+
// * INFO: Is a fire & forget
|
|
3631
|
+
await StreamingWebSocketClient.client.dispose();
|
|
3632
|
+
StreamingWebSocketClient.client = null;
|
|
3633
|
+
StreamingWebSocketClient.connected = false;
|
|
3634
|
+
// * INFO: Wait for the client to disconnect
|
|
3635
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
StreamingWebSocketClient.instance = null;
|
|
3639
|
+
StreamingWebSocketClient.client = null;
|
|
3640
|
+
StreamingWebSocketClient.connected = false;
|
|
3641
|
+
StreamingWebSocketClient.reconnectAttempts = 0;
|
|
3642
|
+
|
|
2411
3643
|
/**
|
|
2412
3644
|
* Transactions API
|
|
2413
3645
|
*
|
|
@@ -3217,7 +4449,7 @@ const isValidApiKey = (apiKey) => {
|
|
|
3217
4449
|
* GoldRushClient Class
|
|
3218
4450
|
*/
|
|
3219
4451
|
class GoldRushClient {
|
|
3220
|
-
constructor(apiKey, settings = {}) {
|
|
4452
|
+
constructor(apiKey, settings = {}, streamingConfig = {}) {
|
|
3221
4453
|
this.userAgent = `com.covalenthq.sdk.typescript/${version}`;
|
|
3222
4454
|
const validKey = isValidApiKey(apiKey);
|
|
3223
4455
|
if (!validKey) {
|
|
@@ -3242,6 +4474,7 @@ class GoldRushClient {
|
|
|
3242
4474
|
this.PricingService = new PricingService(execution);
|
|
3243
4475
|
this.SecurityService = new SecurityService(execution);
|
|
3244
4476
|
this.TransactionService = new TransactionService(execution);
|
|
4477
|
+
this.StreamingService = new StreamingService(apiKey, streamingConfig);
|
|
3245
4478
|
}
|
|
3246
4479
|
}
|
|
3247
4480
|
|
|
@@ -4420,5 +5653,44 @@ const timestampParser = (timestamp, type) => {
|
|
|
4420
5653
|
}
|
|
4421
5654
|
};
|
|
4422
5655
|
|
|
4423
|
-
|
|
5656
|
+
/**
|
|
5657
|
+
* Common enums and types for Streaming API
|
|
5658
|
+
*/
|
|
5659
|
+
var StreamingChain;
|
|
5660
|
+
(function (StreamingChain) {
|
|
5661
|
+
StreamingChain["BASE_MAINNET"] = "BASE_MAINNET";
|
|
5662
|
+
StreamingChain["ETH_MAINNET"] = "ETH_MAINNET";
|
|
5663
|
+
StreamingChain["BSC_MAINNET"] = "BSC_MAINNET";
|
|
5664
|
+
})(StreamingChain || (StreamingChain = {}));
|
|
5665
|
+
var StreamingInterval;
|
|
5666
|
+
(function (StreamingInterval) {
|
|
5667
|
+
StreamingInterval["FIFTEEN_SECONDS"] = "FIFTEEN_SECONDS";
|
|
5668
|
+
StreamingInterval["THIRTY_SECONDS"] = "THIRTY_SECONDS";
|
|
5669
|
+
StreamingInterval["ONE_MINUTE"] = "ONE_MINUTE";
|
|
5670
|
+
StreamingInterval["FIVE_MINUTES"] = "FIVE_MINUTES";
|
|
5671
|
+
StreamingInterval["FIFTEEN_MINUTES"] = "FIFTEEN_MINUTES";
|
|
5672
|
+
StreamingInterval["THIRTY_MINUTES"] = "THIRTY_MINUTES";
|
|
5673
|
+
StreamingInterval["ONE_HOUR"] = "ONE_HOUR";
|
|
5674
|
+
StreamingInterval["FOUR_HOURS"] = "FOUR_HOURS";
|
|
5675
|
+
StreamingInterval["ONE_DAY"] = "ONE_DAY";
|
|
5676
|
+
})(StreamingInterval || (StreamingInterval = {}));
|
|
5677
|
+
var StreamingTimeframe;
|
|
5678
|
+
(function (StreamingTimeframe) {
|
|
5679
|
+
StreamingTimeframe["ONE_MINUTE"] = "ONE_MINUTE";
|
|
5680
|
+
StreamingTimeframe["FIVE_MINUTES"] = "FIVE_MINUTES";
|
|
5681
|
+
StreamingTimeframe["FIFTEEN_MINUTES"] = "FIFTEEN_MINUTES";
|
|
5682
|
+
StreamingTimeframe["THIRTY_MINUTES"] = "THIRTY_MINUTES";
|
|
5683
|
+
StreamingTimeframe["ONE_HOUR"] = "ONE_HOUR";
|
|
5684
|
+
StreamingTimeframe["FOUR_HOURS"] = "FOUR_HOURS";
|
|
5685
|
+
StreamingTimeframe["ONE_DAY"] = "ONE_DAY";
|
|
5686
|
+
StreamingTimeframe["ONE_WEEK"] = "ONE_WEEK";
|
|
5687
|
+
StreamingTimeframe["ONE_MONTH"] = "ONE_MONTH";
|
|
5688
|
+
})(StreamingTimeframe || (StreamingTimeframe = {}));
|
|
5689
|
+
var StreamingProtocol;
|
|
5690
|
+
(function (StreamingProtocol) {
|
|
5691
|
+
StreamingProtocol["UNISWAP_V2"] = "UNISWAP_V2";
|
|
5692
|
+
StreamingProtocol["UNISWAP_V3"] = "UNISWAP_V3";
|
|
5693
|
+
})(StreamingProtocol || (StreamingProtocol = {}));
|
|
5694
|
+
|
|
5695
|
+
export { ChainID, ChainName, GoldRushClient, StreamingChain, StreamingInterval, StreamingProtocol, StreamingTimeframe, bigIntParser, calculatePrettyBalance, isValidApiKey, prettifyCurrency, timestampParser };
|
|
4424
5696
|
//# sourceMappingURL=index.js.map
|