@0xarchive/sdk 0.5.4 → 0.6.1

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/dist/index.mjs CHANGED
@@ -682,6 +682,186 @@ var LiquidationsResource = class {
682
682
  }
683
683
  };
684
684
 
685
+ // src/resources/data-quality.ts
686
+ var DataQualityResource = class {
687
+ constructor(http, basePath = "/v1/data-quality") {
688
+ this.http = http;
689
+ this.basePath = basePath;
690
+ }
691
+ // ===========================================================================
692
+ // Status Endpoints
693
+ // ===========================================================================
694
+ /**
695
+ * Get overall system health status
696
+ *
697
+ * @returns StatusResponse with overall status, per-exchange status,
698
+ * per-data-type status, and active incident count
699
+ *
700
+ * @example
701
+ * ```typescript
702
+ * const status = await client.dataQuality.status();
703
+ * console.log(`Overall: ${status.status}`);
704
+ * for (const [exchange, info] of Object.entries(status.exchanges)) {
705
+ * console.log(`${exchange}: ${info.status}`);
706
+ * }
707
+ * ```
708
+ */
709
+ async status() {
710
+ return this.http.get(`${this.basePath}/status`);
711
+ }
712
+ // ===========================================================================
713
+ // Coverage Endpoints
714
+ // ===========================================================================
715
+ /**
716
+ * Get data coverage summary for all exchanges
717
+ *
718
+ * @returns CoverageResponse with coverage info for all exchanges and data types
719
+ *
720
+ * @example
721
+ * ```typescript
722
+ * const coverage = await client.dataQuality.coverage();
723
+ * for (const exchange of coverage.exchanges) {
724
+ * console.log(`${exchange.exchange}:`);
725
+ * for (const [dtype, info] of Object.entries(exchange.dataTypes)) {
726
+ * console.log(` ${dtype}: ${info.totalRecords} records`);
727
+ * }
728
+ * }
729
+ * ```
730
+ */
731
+ async coverage() {
732
+ return this.http.get(`${this.basePath}/coverage`);
733
+ }
734
+ /**
735
+ * Get data coverage for a specific exchange
736
+ *
737
+ * @param exchange - Exchange name ('hyperliquid' or 'lighter')
738
+ * @returns ExchangeCoverage with coverage info for all data types on this exchange
739
+ *
740
+ * @example
741
+ * ```typescript
742
+ * const hl = await client.dataQuality.exchangeCoverage('hyperliquid');
743
+ * console.log(`Orderbook earliest: ${hl.dataTypes.orderbook.earliest}`);
744
+ * ```
745
+ */
746
+ async exchangeCoverage(exchange) {
747
+ return this.http.get(
748
+ `${this.basePath}/coverage/${exchange.toLowerCase()}`
749
+ );
750
+ }
751
+ /**
752
+ * Get data coverage for a specific symbol on an exchange
753
+ *
754
+ * Includes gap detection showing periods where data may be missing.
755
+ *
756
+ * @param exchange - Exchange name ('hyperliquid' or 'lighter')
757
+ * @param symbol - Symbol name (e.g., 'BTC', 'ETH')
758
+ * @returns SymbolCoverageResponse with per-data-type coverage including gaps
759
+ *
760
+ * @example
761
+ * ```typescript
762
+ * const btc = await client.dataQuality.symbolCoverage('hyperliquid', 'BTC');
763
+ * const oi = btc.dataTypes.open_interest;
764
+ * console.log(`OI completeness: ${oi.completeness}%`);
765
+ * console.log(`Gaps found: ${oi.gaps.length}`);
766
+ * for (const gap of oi.gaps.slice(0, 3)) {
767
+ * console.log(` ${gap.durationMinutes} min gap at ${gap.start}`);
768
+ * }
769
+ * ```
770
+ */
771
+ async symbolCoverage(exchange, symbol) {
772
+ return this.http.get(
773
+ `${this.basePath}/coverage/${exchange.toLowerCase()}/${symbol.toUpperCase()}`
774
+ );
775
+ }
776
+ // ===========================================================================
777
+ // Incidents Endpoints
778
+ // ===========================================================================
779
+ /**
780
+ * List incidents with filtering and pagination
781
+ *
782
+ * @param params - Filter and pagination options
783
+ * @returns IncidentsResponse with list of incidents and pagination info
784
+ *
785
+ * @example
786
+ * ```typescript
787
+ * // Get all open incidents
788
+ * const result = await client.dataQuality.listIncidents({ status: 'open' });
789
+ * for (const incident of result.incidents) {
790
+ * console.log(`${incident.severity}: ${incident.title}`);
791
+ * }
792
+ * ```
793
+ */
794
+ async listIncidents(params) {
795
+ return this.http.get(
796
+ `${this.basePath}/incidents`,
797
+ params
798
+ );
799
+ }
800
+ /**
801
+ * Get a specific incident by ID
802
+ *
803
+ * @param incidentId - The incident ID
804
+ * @returns Incident details
805
+ *
806
+ * @example
807
+ * ```typescript
808
+ * const incident = await client.dataQuality.getIncident('inc_123');
809
+ * console.log(`Status: ${incident.status}`);
810
+ * console.log(`Root cause: ${incident.rootCause}`);
811
+ * ```
812
+ */
813
+ async getIncident(incidentId) {
814
+ return this.http.get(`${this.basePath}/incidents/${incidentId}`);
815
+ }
816
+ // ===========================================================================
817
+ // Latency Endpoints
818
+ // ===========================================================================
819
+ /**
820
+ * Get current latency metrics for all exchanges
821
+ *
822
+ * @returns LatencyResponse with WebSocket, REST API, and data freshness metrics
823
+ *
824
+ * @example
825
+ * ```typescript
826
+ * const latency = await client.dataQuality.latency();
827
+ * for (const [exchange, metrics] of Object.entries(latency.exchanges)) {
828
+ * console.log(`${exchange}:`);
829
+ * if (metrics.websocket) {
830
+ * console.log(` WS current: ${metrics.websocket.currentMs}ms`);
831
+ * }
832
+ * console.log(` OB lag: ${metrics.dataFreshness.orderbookLagMs}ms`);
833
+ * }
834
+ * ```
835
+ */
836
+ async latency() {
837
+ return this.http.get(`${this.basePath}/latency`);
838
+ }
839
+ // ===========================================================================
840
+ // SLA Endpoints
841
+ // ===========================================================================
842
+ /**
843
+ * Get SLA compliance metrics for a specific month
844
+ *
845
+ * @param params - Optional year and month (defaults to current month)
846
+ * @returns SlaResponse with SLA targets, actual metrics, and compliance status
847
+ *
848
+ * @example
849
+ * ```typescript
850
+ * const sla = await client.dataQuality.sla({ year: 2026, month: 1 });
851
+ * console.log(`Period: ${sla.period}`);
852
+ * console.log(`Uptime: ${sla.actual.uptime}% (${sla.actual.uptimeStatus})`);
853
+ * console.log(`Completeness: ${sla.actual.dataCompleteness.overall}%`);
854
+ * console.log(`API P99: ${sla.actual.apiLatencyP99Ms}ms`);
855
+ * ```
856
+ */
857
+ async sla(params) {
858
+ return this.http.get(
859
+ `${this.basePath}/sla`,
860
+ params
861
+ );
862
+ }
863
+ };
864
+
685
865
  // src/exchanges.ts
686
866
  var HyperliquidClient = class {
687
867
  /**
@@ -772,6 +952,10 @@ var OxArchive = class {
772
952
  * Lighter.xyz exchange data (August 2025+)
773
953
  */
774
954
  lighter;
955
+ /**
956
+ * Data quality metrics: status, coverage, incidents, latency, SLA
957
+ */
958
+ dataQuality;
775
959
  /**
776
960
  * @deprecated Use client.hyperliquid.orderbook instead
777
961
  */
@@ -809,6 +993,7 @@ var OxArchive = class {
809
993
  });
810
994
  this.hyperliquid = new HyperliquidClient(this.http);
811
995
  this.lighter = new LighterClient(this.http);
996
+ this.dataQuality = new DataQualityResource(this.http);
812
997
  const legacyBase = "/v1/hyperliquid";
813
998
  this.orderbook = new OrderBookResource(this.http, legacyBase);
814
999
  this.trades = new TradesResource(this.http, legacyBase);
@@ -913,6 +1098,7 @@ var OxArchiveWs = class {
913
1098
  streamCompleteHandlers = [];
914
1099
  orderbookHandlers = [];
915
1100
  tradesHandlers = [];
1101
+ gapHandlers = [];
916
1102
  constructor(options) {
917
1103
  this.options = {
918
1104
  apiKey: options.apiKey,
@@ -1202,6 +1388,25 @@ var OxArchiveWs = class {
1202
1388
  onStreamComplete(handler) {
1203
1389
  this.streamCompleteHandlers.push(handler);
1204
1390
  }
1391
+ /**
1392
+ * Handle gap detected events during replay or streaming.
1393
+ * Called when there's a gap in the historical data exceeding the threshold.
1394
+ * Thresholds: 2 minutes for orderbook/candles/liquidations, 60 minutes for trades.
1395
+ *
1396
+ * @param handler - Callback receiving channel, coin, gap start/end timestamps (ms), and duration (minutes)
1397
+ *
1398
+ * @example
1399
+ * ```typescript
1400
+ * ws.onGap((channel, coin, gapStart, gapEnd, durationMinutes) => {
1401
+ * console.warn(`Gap detected in ${channel} ${coin}: ${durationMinutes} minutes`);
1402
+ * console.warn(` From: ${new Date(gapStart).toISOString()}`);
1403
+ * console.warn(` To: ${new Date(gapEnd).toISOString()}`);
1404
+ * });
1405
+ * ```
1406
+ */
1407
+ onGap(handler) {
1408
+ this.gapHandlers.push(handler);
1409
+ }
1205
1410
  /**
1206
1411
  * Get current connection state
1207
1412
  */
@@ -1344,6 +1549,13 @@ var OxArchiveWs = class {
1344
1549
  }
1345
1550
  break;
1346
1551
  }
1552
+ case "gap_detected": {
1553
+ const msg = message;
1554
+ for (const handler of this.gapHandlers) {
1555
+ handler(msg.channel, msg.coin, msg.gap_start, msg.gap_end, msg.duration_minutes);
1556
+ }
1557
+ break;
1558
+ }
1347
1559
  case "data": {
1348
1560
  if (message.channel === "orderbook") {
1349
1561
  const orderbook = transformOrderbook(message.coin, message.data);