@agg-build/hooks 4.17.2 → 4.18.2

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
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  useRampQuotes,
3
3
  useRampSession
4
- } from "./chunk-BBAKQDOH.mjs";
4
+ } from "./chunk-SBGLJDPJ.mjs";
5
5
  import {
6
6
  useWithdrawEstimate,
7
7
  useWithdrawFlow,
8
8
  useWithdrawPreview,
9
9
  useWithdrawalLifecycle
10
- } from "./chunk-IVUHIMRY.mjs";
10
+ } from "./chunk-5B2F3GMC.mjs";
11
11
  import {
12
12
  AggAuthContext,
13
13
  AggBalanceProvider,
@@ -26,20 +26,17 @@ import {
26
26
  Venue,
27
27
  __async,
28
28
  __objRest,
29
+ __pow,
29
30
  __spreadProps,
30
31
  __spreadValues,
31
32
  appConfigQueryKey,
32
- applyOrderbookDepth,
33
33
  computeClosedPositionTotals,
34
- createMarketChartData,
35
34
  createMarketLiveState,
36
- createMarketOrderbookDataFromState,
37
- createMarketOrderbookDataFromVenueOrderbooks,
38
35
  defaultAggUiConfig,
39
36
  executionKeys,
40
37
  getBuilder,
41
38
  getDepositAddress,
42
- getIntervalSeconds,
39
+ getMarketLiveStore,
43
40
  getOrCreateBuilder,
44
41
  getWalletAddressFromUserProfile,
45
42
  invalidateBalanceQueries,
@@ -48,8 +45,6 @@ import {
48
45
  invalidateUserClaimState,
49
46
  invalidateUserMoneyState,
50
47
  marketDataKeys,
51
- marketOrderbookDataToResponse,
52
- mergeMarketChartData,
53
48
  optimizedImageUrl,
54
49
  parseEmail,
55
50
  parseEmailStrict,
@@ -60,6 +55,7 @@ import {
60
55
  resolveMarketWinningOutcome,
61
56
  resolveOrderEligibility,
62
57
  resolveTradingStateKind,
58
+ retinaImageUrl,
63
59
  sortVenues,
64
60
  tradingReducer,
65
61
  useAggAuthContext,
@@ -82,6 +78,7 @@ import {
82
78
  useExecuteManaged,
83
79
  useExecutionProgress,
84
80
  useLabels,
81
+ useLiveStates,
85
82
  useManagedBalances,
86
83
  useOnBalanceUpdate,
87
84
  useOnOrderEvent,
@@ -101,10 +98,10 @@ import {
101
98
  useSyncBalances,
102
99
  useWithdrawManaged,
103
100
  userActivityQueryKeys
104
- } from "./chunk-KQ5TOPNY.mjs";
101
+ } from "./chunk-ESDA4NZI.mjs";
105
102
 
106
103
  // src/index.ts
107
- import { QueryClient as QueryClient4, QueryClientProvider, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
104
+ import { QueryClient as QueryClient4, QueryClientProvider, useQueryClient as useQueryClient5 } from "@tanstack/react-query";
108
105
  import { TurnstileChallengeError } from "@agg-build/sdk";
109
106
 
110
107
  // src/use-geo-block.ts
@@ -170,9 +167,113 @@ var useVenueKeyStatus = (venue, enabled = true) => {
170
167
  });
171
168
  };
172
169
 
170
+ // src/use-hosted-venue-account.ts
171
+ import { useEffect, useRef } from "react";
172
+ import { useMutation, useQuery as useQuery2, useQueryClient } from "@tanstack/react-query";
173
+ var hostedVenueAccountQueryKeys = {
174
+ all: ["agg", "hosted-venue-account"],
175
+ account: (venue, userId, mode) => ["agg", "hosted-venue-account", venue, userId, mode]
176
+ };
177
+ function useHostedVenueAccount(venue, enabled = true) {
178
+ var _a;
179
+ const client = useAggClient();
180
+ const { user, isAuthenticated } = useAggAuthContext();
181
+ const { executionMode, isResolving } = useResolvedExecutionMode();
182
+ const queryClient = useQueryClient();
183
+ const userId = (_a = user == null ? void 0 : user.id) != null ? _a : null;
184
+ const permitted = enabled && isAuthenticated && userId !== null && !isResolving && executionMode === "live";
185
+ const queryKey = hostedVenueAccountQueryKeys.account(venue, userId, executionMode);
186
+ const session = useRef({ client, userId, executionMode, permitted });
187
+ session.current = { client, userId, executionMode, permitted };
188
+ const assertSession = () => {
189
+ const current = session.current;
190
+ if (!permitted || !current.permitted || current.client !== client || current.userId !== userId || current.executionMode !== executionMode)
191
+ throw new Error("Hosted custody is unavailable for this session");
192
+ };
193
+ const account = useQuery2({
194
+ queryKey,
195
+ queryFn: () => {
196
+ assertSession();
197
+ return client.getHostedVenueAccount(venue, { mode: executionMode });
198
+ },
199
+ enabled: permitted,
200
+ staleTime: 3e3,
201
+ refetchInterval: permitted ? 5e3 : false,
202
+ refetchIntervalInBackground: false,
203
+ retry: false
204
+ });
205
+ const provision = useMutation({
206
+ mutationFn: (request2) => request2.run(),
207
+ // Retain the dispatch-time user key even if the observer changes users.
208
+ onSuccess: (result, request2) => {
209
+ queryClient.setQueryData(request2.queryKey, result);
210
+ invalidateBalanceQueries(queryClient);
211
+ },
212
+ retry: false
213
+ });
214
+ const { reset } = provision;
215
+ useEffect(() => {
216
+ reset();
217
+ }, [userId, executionMode, reset]);
218
+ const request = () => ({
219
+ queryKey,
220
+ run: () => {
221
+ assertSession();
222
+ return client.provisionHostedVenueAccount(venue, { mode: "live" });
223
+ }
224
+ });
225
+ return {
226
+ account,
227
+ provision: __spreadProps(__spreadValues({}, provision), {
228
+ mutate: () => provision.mutate(request()),
229
+ mutateAsync: () => provision.mutateAsync(request())
230
+ }),
231
+ isAvailable: permitted
232
+ };
233
+ }
234
+
235
+ // src/use-hosted-venue-activity.ts
236
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
237
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
238
+ function useHostedVenueActivity(venue, page = 0, enabled = true) {
239
+ var _a;
240
+ const client = useAggClient();
241
+ const { user, isAuthenticated } = useAggAuthContext();
242
+ const { executionMode, isResolving } = useResolvedExecutionMode();
243
+ const userId = (_a = user == null ? void 0 : user.id) != null ? _a : null;
244
+ const permitted = enabled && isAuthenticated && userId !== null && !isResolving && executionMode === "live" && Number.isInteger(page) && page >= 0 && page <= 9999;
245
+ const session = useRef2({ client, userId, executionMode, permitted });
246
+ session.current = { client, userId, executionMode, permitted };
247
+ const mounted = useRef2(true);
248
+ useEffect2(() => {
249
+ mounted.current = true;
250
+ return () => {
251
+ mounted.current = false;
252
+ };
253
+ }, []);
254
+ const assertSession = () => {
255
+ const current = session.current;
256
+ if (!mounted.current || !permitted || !current.permitted || current.client !== client || current.userId !== userId || current.executionMode !== executionMode)
257
+ throw new Error("Hosted activity is unavailable for this session");
258
+ };
259
+ const activity = useQuery3({
260
+ queryKey: [...hostedVenueAccountQueryKeys.all, "activity", venue, userId, executionMode, page],
261
+ queryFn: () => __async(null, null, function* () {
262
+ assertSession();
263
+ const result = yield client.getHostedVenueActivity(venue, { mode: executionMode, page });
264
+ assertSession();
265
+ return result;
266
+ }),
267
+ enabled: permitted,
268
+ staleTime: 3e3,
269
+ retry: false
270
+ });
271
+ return { activity, isAvailable: permitted };
272
+ }
273
+
173
274
  // src/use-shared-kyc.ts
174
275
  import { useCallback } from "react";
175
- import { useQuery as useQuery2 } from "@tanstack/react-query";
276
+ import { useQuery as useQuery4 } from "@tanstack/react-query";
176
277
  var sharedKycQueryKeys = {
177
278
  status: (userId) => ["agg", "shared-kyc", userId]
178
279
  };
@@ -181,7 +282,7 @@ var useSharedKyc = () => {
181
282
  const client = useAggClient();
182
283
  const { isAuthenticated, user } = useAggAuthContext();
183
284
  const userId = (_a = user == null ? void 0 : user.id) != null ? _a : null;
184
- const query = useQuery2({
285
+ const query = useQuery4({
185
286
  queryKey: sharedKycQueryKeys.status(userId),
186
287
  queryFn: () => client.getSharedKycStatus(),
187
288
  enabled: isAuthenticated && userId !== null,
@@ -340,10 +441,10 @@ function useCategoryChildren(options) {
340
441
  }
341
442
 
342
443
  // src/use-debounced-value.ts
343
- import { useEffect, useState as useState2 } from "react";
444
+ import { useEffect as useEffect3, useState as useState2 } from "react";
344
445
  function useDebouncedValue(value, delay) {
345
446
  const [debouncedValue, setDebouncedValue] = useState2(value);
346
- useEffect(() => {
447
+ useEffect3(() => {
347
448
  const timeoutId = window.setTimeout(() => {
348
449
  setDebouncedValue(value);
349
450
  }, delay);
@@ -364,11 +465,13 @@ function useExecutionOrders(options = {}) {
364
465
  orderType,
365
466
  orderId,
366
467
  quoteId,
468
+ venueMarketIds,
367
469
  mode,
368
470
  limit = 50,
369
471
  enabled = true,
370
472
  refetchIntervalMs = false
371
473
  } = options;
474
+ const venueMarketIdsKey = (venueMarketIds == null ? void 0 : venueMarketIds.length) ? [...new Set(venueMarketIds)].sort() : null;
372
475
  const query = useInfiniteQuery2({
373
476
  // quoteId + orderId are part of the cache key — different filters
374
477
  // produce different result sets, so they have to vary the key.
@@ -377,16 +480,17 @@ function useExecutionOrders(options = {}) {
377
480
  status != null ? status : "all",
378
481
  quoteId != null ? quoteId : null,
379
482
  orderId != null ? orderId : null,
483
+ ...venueMarketIdsKey ? [{ venueMarketIds: venueMarketIdsKey }] : [],
380
484
  ...orderType ? [orderType] : [],
381
485
  ...mode ? [mode] : [],
382
486
  limit
383
487
  ],
384
- queryFn: ({ pageParam }) => client.getExecutionOrders(__spreadProps(__spreadValues({
488
+ queryFn: ({ pageParam }) => client.getExecutionOrders(__spreadProps(__spreadValues(__spreadValues({
385
489
  status,
386
490
  orderType,
387
491
  orderId,
388
492
  quoteId
389
- }, mode ? { mode } : {}), {
493
+ }, venueMarketIdsKey ? { venueMarketIds: venueMarketIdsKey } : {}), mode ? { mode } : {}), {
390
494
  limit,
391
495
  cursor: pageParam
392
496
  })),
@@ -406,6 +510,85 @@ function useExecutionOrders(options = {}) {
406
510
  });
407
511
  }
408
512
 
513
+ // src/use-execution-orders-for-markets.ts
514
+ import { useQueries as useQueries2 } from "@tanstack/react-query";
515
+ import { useMemo } from "react";
516
+ var MAX_ORDER_VENUE_MARKET_IDS_PER_REQUEST = 75;
517
+ var DEFAULT_PAGE_LIMIT = 100;
518
+ var DEFAULT_MAX_PAGES_PER_BATCH = 5;
519
+ var combineBatches = (results) => {
520
+ var _a, _b;
521
+ return {
522
+ orders: results.flatMap((result) => {
523
+ var _a2, _b2;
524
+ return (_b2 = (_a2 = result.data) == null ? void 0 : _a2.orders) != null ? _b2 : [];
525
+ }),
526
+ isLoading: results.some((result) => result.isLoading),
527
+ error: (_b = (_a = results.find((result) => result.error)) == null ? void 0 : _a.error) != null ? _b : null,
528
+ isTruncated: results.some((result) => {
529
+ var _a2;
530
+ return ((_a2 = result.data) == null ? void 0 : _a2.hasMore) === true;
531
+ })
532
+ };
533
+ };
534
+ var chunk = (items, size) => {
535
+ const chunks = [];
536
+ for (let index = 0; index < items.length; index += size) {
537
+ chunks.push(items.slice(index, index + size));
538
+ }
539
+ return chunks;
540
+ };
541
+ function useExecutionOrdersForMarkets({
542
+ venueMarketIds,
543
+ status,
544
+ orderType,
545
+ mode,
546
+ limit = DEFAULT_PAGE_LIMIT,
547
+ maxPagesPerBatch = DEFAULT_MAX_PAGES_PER_BATCH,
548
+ enabled = true
549
+ }) {
550
+ const client = useAggClient();
551
+ const batchKey = [...new Set(venueMarketIds)].sort().join(",");
552
+ const batches = useMemo(
553
+ () => batchKey ? chunk(batchKey.split(","), MAX_ORDER_VENUE_MARKET_IDS_PER_REQUEST) : [],
554
+ [batchKey]
555
+ );
556
+ return useQueries2({
557
+ combine: combineBatches,
558
+ queries: batches.map((batchVenueMarketIds) => ({
559
+ queryKey: [
560
+ "execution-orders",
561
+ "by-markets",
562
+ batchVenueMarketIds,
563
+ status != null ? status : "all",
564
+ orderType != null ? orderType : null,
565
+ mode != null ? mode : null,
566
+ limit,
567
+ maxPagesPerBatch
568
+ ],
569
+ queryFn: () => __async(null, null, function* () {
570
+ var _a;
571
+ const orders = [];
572
+ let cursor;
573
+ for (let page = 0; page < maxPagesPerBatch; page += 1) {
574
+ const response = yield client.getExecutionOrders(__spreadValues(__spreadProps(__spreadValues({
575
+ venueMarketIds: batchVenueMarketIds,
576
+ status,
577
+ orderType
578
+ }, mode ? { mode } : {}), {
579
+ limit
580
+ }), cursor ? { cursor } : {}));
581
+ orders.push(...response.data);
582
+ cursor = response.hasMore ? (_a = response.nextCursor) != null ? _a : void 0 : void 0;
583
+ if (!cursor) return { orders, hasMore: false };
584
+ }
585
+ return { orders, hasMore: true };
586
+ }),
587
+ enabled
588
+ }))
589
+ });
590
+ }
591
+
409
592
  // src/use-user-activity.ts
410
593
  import { keepPreviousData, useInfiniteQuery as useInfiniteQuery3 } from "@tanstack/react-query";
411
594
  var DEFAULT_PENDING_REFETCH_INTERVAL_MS = 15e3;
@@ -532,17 +715,376 @@ function useExecutionPositions(options = {}) {
532
715
  }
533
716
 
534
717
  // src/use-live-candle-overlay.ts
535
- import { useCallback as useCallback5, useMemo as useMemo4 } from "react";
718
+ import { useCallback as useCallback5, useMemo as useMemo5 } from "react";
536
719
 
537
720
  // src/use-live-candles.ts
538
- import { useMemo as useMemo3 } from "react";
721
+ import { useMemo as useMemo4 } from "react";
539
722
  import { mergeCandles, mergeClosedCandles } from "@agg-build/sdk";
540
723
 
541
724
  // src/use-market-chart.ts
542
- import { keepPreviousData as keepPreviousData2, useQueries as useQueries2 } from "@tanstack/react-query";
725
+ import { keepPreviousData as keepPreviousData2, useQueries as useQueries3, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
726
+
727
+ // src/read-retry.ts
728
+ import { isAggApiError } from "@agg-build/sdk";
729
+ import { isServer } from "@tanstack/react-query";
730
+ var MAX_TIMER_DELAY_MS = 2147483647;
731
+ var hint = (error) => {
732
+ if (!isAggApiError(error)) return void 0;
733
+ const value = error.retryAfterMs;
734
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
735
+ };
736
+ function withReadRetry(options) {
737
+ return {
738
+ retry: (failureCount, error) => {
739
+ var _a, _b;
740
+ if (((_a = hint(error)) != null ? _a : 0) > MAX_TIMER_DELAY_MS) return false;
741
+ const retry = (_b = options.retry) != null ? _b : isServer ? 0 : 3;
742
+ return typeof retry === "function" ? retry(failureCount, error) : retry === true || typeof retry === "number" && failureCount < retry;
743
+ },
744
+ retryDelay: (attempt, error) => {
745
+ var _a, _b;
746
+ return (_b = hint(error)) != null ? _b : typeof options.retryDelay === "function" ? options.retryDelay(attempt, error) : (_a = options.retryDelay) != null ? _a : Math.min(1e3 * __pow(2, attempt), 3e4);
747
+ }
748
+ };
749
+ }
750
+
751
+ // src/market-data/chart-cache.ts
752
+ var MAX_CACHE_RETENTION_SECONDS = 7 * 30 * 24 * 60 * 60 + 14 * 24 * 60 * 60;
753
+ var getIntervalSeconds = (interval) => {
754
+ switch (interval) {
755
+ case "1m":
756
+ return 60;
757
+ case "5m":
758
+ return 5 * 60;
759
+ case "1h":
760
+ return 60 * 60;
761
+ case "1d":
762
+ return 24 * 60 * 60;
763
+ }
764
+ };
765
+ var resolveMaxLastUpdateTs = (venues) => {
766
+ let max = null;
767
+ for (const venueData of Object.values(venues)) {
768
+ if (venueData.lastUpdateTs != null && (max == null || venueData.lastUpdateTs > max)) {
769
+ max = venueData.lastUpdateTs;
770
+ }
771
+ }
772
+ return max;
773
+ };
774
+ var sortCandles = (candles) => {
775
+ return [...candles].sort((left, right) => left.time - right.time);
776
+ };
777
+ var createMarketChartData = (params) => {
778
+ var _a;
779
+ const candles = sortCandles(
780
+ params.response.data.map((candle) => __spreadProps(__spreadValues({
781
+ time: Math.floor(candle.t / 1e3),
782
+ open: candle.o,
783
+ high: candle.h,
784
+ low: candle.l,
785
+ close: candle.c
786
+ }, Number.isFinite(candle.mean) ? { mean: candle.mean } : {}), {
787
+ volume: candle.v,
788
+ source: "rest"
789
+ }))
790
+ );
791
+ const lastCandle = candles[candles.length - 1];
792
+ const lastUpdateTs = (_a = lastCandle == null ? void 0 : lastCandle.time) != null ? _a : null;
793
+ const venues = {
794
+ [params.response.venue]: {
795
+ venue: params.response.venue,
796
+ outcomeIds: [params.response.venueMarketOutcomeId],
797
+ candles,
798
+ liveCandle: null,
799
+ lineValue: lastCandle == null ? void 0 : lastCandle.close,
800
+ lastTrade: null,
801
+ lastUpdateTs
802
+ }
803
+ };
804
+ return {
805
+ marketId: params.marketId,
806
+ delivery: params.response.meta,
807
+ outcomeIds: [params.response.venueMarketOutcomeId],
808
+ primaryVenue: params.response.venue,
809
+ interval: params.interval,
810
+ startTs: params.startTs,
811
+ endTs: params.endTs,
812
+ venues,
813
+ lastUpdateTs
814
+ };
815
+ };
816
+ var mergeMarketChartData = (params) => {
817
+ var _a, _b, _c, _d, _e;
818
+ if (params.datasets.length === 0) {
819
+ return null;
820
+ }
821
+ const primaryVenue = (_b = (_a = params.datasets[0]) == null ? void 0 : _a.primaryVenue) != null ? _b : "aggregate";
822
+ const outcomeIds = Array.from(
823
+ new Set(
824
+ params.datasets.flatMap((dataset) => {
825
+ return dataset.outcomeIds.length > 0 ? dataset.outcomeIds : [dataset.marketId];
826
+ })
827
+ )
828
+ );
829
+ const venues = params.datasets.reduce((acc, dataset) => {
830
+ for (const [venue, venueData] of Object.entries(dataset.venues)) {
831
+ const existingVenueData = acc[venue];
832
+ acc[venue] = existingVenueData ? __spreadProps(__spreadValues({}, venueData), {
833
+ outcomeIds: Array.from(
834
+ /* @__PURE__ */ new Set([...existingVenueData.outcomeIds, ...venueData.outcomeIds])
835
+ )
836
+ }) : venueData;
837
+ }
838
+ return acc;
839
+ }, {});
840
+ return {
841
+ marketId: params.marketId,
842
+ delivery: (_e = (_c = params.datasets.find((dataset) => dataset.marketId === params.marketId)) == null ? void 0 : _c.delivery) != null ? _e : (_d = params.datasets[0]) == null ? void 0 : _d.delivery,
843
+ outcomeIds,
844
+ primaryVenue,
845
+ interval: params.interval,
846
+ startTs: params.startTs,
847
+ endTs: params.endTs,
848
+ venues,
849
+ lastUpdateTs: resolveMaxLastUpdateTs(venues)
850
+ };
851
+ };
852
+
853
+ // src/market-data/chart-retry-gate.ts
854
+ import { isAggApiError as isAggApiError3 } from "@agg-build/sdk";
855
+
856
+ // src/market-data/chart-history.ts
857
+ import {
858
+ loadImmutableHistory,
859
+ isAggApiError as isAggApiError2
860
+ } from "@agg-build/sdk";
861
+ var ChartDeliveryError = class extends Error {
862
+ constructor(state, retryAfterMs) {
863
+ super(`Chart delivery ${state}`);
864
+ this.state = state;
865
+ this.retryAfterMs = retryAfterMs;
866
+ this.name = "ChartDeliveryError";
867
+ }
868
+ };
869
+ var usable = (response) => {
870
+ var _a;
871
+ if (response.data.length === 0 && ((_a = response.meta) == null ? void 0 : _a.state) && response.meta.state !== "empty")
872
+ throw new ChartDeliveryError(response.meta.state, response.meta.retryAfterMs);
873
+ return response;
874
+ };
875
+ var withActualFreshness = (response) => {
876
+ var _a, _b;
877
+ const serverTime = Number.isFinite(response.meta.serverTime) ? response.meta.serverTime : Date.now();
878
+ const lastBarOpenTime = (_b = (_a = response.data.at(-1)) == null ? void 0 : _a.t) != null ? _b : null;
879
+ return __spreadProps(__spreadValues({}, response), {
880
+ meta: __spreadProps(__spreadValues({}, response.meta), {
881
+ serverTime,
882
+ lastBarOpenTime,
883
+ staleSeconds: lastBarOpenTime == null ? null : Math.max(0, (serverTime - lastBarOpenTime) / 1e3)
884
+ })
885
+ });
886
+ };
887
+ function fetchChartHistory(client, socket, request, signal, onInitial) {
888
+ return __async(this, null, function* () {
889
+ var _a, _b, _c;
890
+ const readBars = (params) => __async(null, null, function* () {
891
+ const response = yield client.getChartBars(params, __spreadValues({
892
+ signal
893
+ }, socket ? { decode: (text) => socket.decodeHistory(text) } : {}));
894
+ signal == null ? void 0 : signal.throwIfAborted();
895
+ if (response.venueMarketOutcomeId !== request.venueMarketOutcomeId)
896
+ throw new Error("Chart history identity mismatch");
897
+ return response;
898
+ });
899
+ if (!client.preferImmutableHistory) return usable(yield readBars(request));
900
+ let verified;
901
+ try {
902
+ const manifest2 = yield client.getChartHistoryManifest(request, { signal });
903
+ if (manifest2.venueMarketOutcomeId !== request.venueMarketOutcomeId)
904
+ throw new Error("Chart manifest identity mismatch");
905
+ if (manifest2.meta.complete) {
906
+ const history2 = yield socket ? socket.loadHistory(manifest2, { signal }) : loadImmutableHistory({ manifest: manifest2, signal });
907
+ verified = {
908
+ manifest: manifest2,
909
+ history: history2.filter(
910
+ (bar) => (request.from == null || bar.t >= request.from) && bar.t < request.to
911
+ )
912
+ };
913
+ }
914
+ } catch (error) {
915
+ if ((signal == null ? void 0 : signal.aborted) || isAggApiError2(error) && (error.status === 429 || error.status === 503))
916
+ throw error;
917
+ }
918
+ signal == null ? void 0 : signal.throwIfAborted();
919
+ if (!verified) return usable(yield readBars(request));
920
+ const { manifest, history } = verified;
921
+ const fullyCovered = manifest.finalizedThrough >= request.to;
922
+ const initial = withActualFreshness({
923
+ venueMarketOutcomeId: manifest.venueMarketOutcomeId,
924
+ venue: manifest.venue,
925
+ resolution: manifest.resolution,
926
+ data: history,
927
+ meta: __spreadProps(__spreadValues({}, manifest.meta), {
928
+ state: fullyCovered ? history.length ? "ready" : "empty" : "warming",
929
+ complete: fullyCovered
930
+ })
931
+ });
932
+ if (fullyCovered) return usable(initial);
933
+ if (history.length > 0) onInitial == null ? void 0 : onInitial(initial);
934
+ signal == null ? void 0 : signal.throwIfAborted();
935
+ try {
936
+ const tail = yield readBars(__spreadProps(__spreadValues({}, request), {
937
+ resolution: manifest.resolution,
938
+ from: Math.max((_a = request.from) != null ? _a : 0, manifest.finalizedThrough),
939
+ countBack: void 0
940
+ }));
941
+ if (tail.venue !== manifest.venue || tail.resolution !== manifest.resolution)
942
+ throw new Error("Chart tail series mismatch");
943
+ const data = [
944
+ ...history,
945
+ ...tail.data.filter((bar) => bar.t >= manifest.finalizedThrough && bar.t < request.to)
946
+ ];
947
+ const incomplete = tail.meta.complete === false || ["warming", "failed", "unsupported"].includes((_b = tail.meta.state) != null ? _b : "");
948
+ return usable(
949
+ withActualFreshness(__spreadProps(__spreadValues({}, tail), {
950
+ resolution: manifest.resolution,
951
+ data,
952
+ meta: __spreadProps(__spreadValues(__spreadValues({}, initial.meta), tail.meta), {
953
+ complete: !incomplete,
954
+ state: incomplete ? (_c = tail.meta.state) != null ? _c : "warming" : data.length ? "ready" : "empty"
955
+ })
956
+ }))
957
+ );
958
+ } catch (error) {
959
+ if (signal == null ? void 0 : signal.aborted) throw error;
960
+ return usable(
961
+ withActualFreshness(__spreadProps(__spreadValues({}, initial), {
962
+ meta: __spreadValues(__spreadProps(__spreadValues({}, initial.meta), {
963
+ state: error instanceof ChartDeliveryError ? error.state : "failed",
964
+ complete: false
965
+ }), (error instanceof ChartDeliveryError || isAggApiError2(error)) && error.retryAfterMs != null ? { retryAfterMs: error.retryAfterMs } : {})
966
+ }))
967
+ );
968
+ }
969
+ });
970
+ }
971
+
972
+ // src/market-data/chart-retry-gate.ts
973
+ var MAX_TIMER_DELAY_MS2 = 2147483647;
974
+ var notBefore = /* @__PURE__ */ new WeakMap();
975
+ var validDelay = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
976
+ function record(query, delay) {
977
+ if (validDelay(delay) && delay > 0)
978
+ notBefore.set(query, Math.min(Number.MAX_SAFE_INTEGER, Date.now() + delay));
979
+ else notBefore.delete(query);
980
+ }
981
+ function chartRetryRemaining(query) {
982
+ var _a;
983
+ return Math.max(0, ((_a = notBefore.get(query)) != null ? _a : 0) - Date.now());
984
+ }
985
+ function chartRefetchInterval(query, interval) {
986
+ if (interval === false) return false;
987
+ const remaining = chartRetryRemaining(query);
988
+ return remaining > MAX_TIMER_DELAY_MS2 ? false : Math.max(interval, remaining);
989
+ }
990
+ function waitForDeadline(query, signal) {
991
+ return __async(this, null, function* () {
992
+ while (chartRetryRemaining(query) > 0) {
993
+ signal.throwIfAborted();
994
+ yield new Promise((resolve, reject) => {
995
+ const aborted = () => {
996
+ clearTimeout(timer);
997
+ signal.removeEventListener("abort", aborted);
998
+ reject(signal.reason);
999
+ };
1000
+ const timer = setTimeout(
1001
+ () => {
1002
+ signal.removeEventListener("abort", aborted);
1003
+ resolve();
1004
+ },
1005
+ Math.min(chartRetryRemaining(query), MAX_TIMER_DELAY_MS2)
1006
+ );
1007
+ signal.addEventListener("abort", aborted, { once: true });
1008
+ if (signal.aborted) aborted();
1009
+ });
1010
+ }
1011
+ signal.throwIfAborted();
1012
+ });
1013
+ }
1014
+ function runChartQuery(query, signal, load) {
1015
+ return __async(this, null, function* () {
1016
+ var _a;
1017
+ yield waitForDeadline(query, signal);
1018
+ try {
1019
+ const data = yield load();
1020
+ signal.throwIfAborted();
1021
+ const delivery = data.delivery;
1022
+ record(
1023
+ query,
1024
+ delivery && delivery.complete !== true && !["ready", "empty"].includes((_a = delivery.state) != null ? _a : "") ? delivery.retryAfterMs : void 0
1025
+ );
1026
+ return data;
1027
+ } catch (error) {
1028
+ if (!signal.aborted) {
1029
+ const delay = isAggApiError3(error) ? error.retryAfterMs : error instanceof ChartDeliveryError && validDelay(error.retryAfterMs) ? error.retryAfterMs : void 0;
1030
+ record(query, delay);
1031
+ }
1032
+ throw error;
1033
+ }
1034
+ });
1035
+ }
1036
+
1037
+ // src/market-data/chart-tail.ts
1038
+ var closedCaches = /* @__PURE__ */ new WeakMap();
1039
+ var toChart = (candle) => ({
1040
+ time: candle.openTime,
1041
+ open: candle.o,
1042
+ high: candle.h,
1043
+ low: candle.l,
1044
+ close: candle.c,
1045
+ volume: candle.v,
1046
+ source: "ws"
1047
+ });
1048
+ function overlayCandleTail(history, live, rolling) {
1049
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1050
+ const tail = (_a = live == null ? void 0 : live.candles) == null ? void 0 : _a[history.interval];
1051
+ const venue = history.venues[history.primaryVenue];
1052
+ if (!tail || !venue || !rolling && history.endTs != null && history.endTs < Date.now())
1053
+ return history;
1054
+ const last = (_c = (_b = venue.candles.at(-1)) == null ? void 0 : _b.time) != null ? _c : -Infinity;
1055
+ const closed = tail.closed.filter((candle) => candle.openTime > last);
1056
+ const key = closed.map((candle) => `${candle.openTime}:${candle.c}:${candle.v}`).join("|");
1057
+ let cached = closedCaches.get(history);
1058
+ if (!cached || cached.key !== key) {
1059
+ cached = {
1060
+ key,
1061
+ candles: closed.length ? [...venue.candles, ...closed.map(toChart)] : venue.candles
1062
+ };
1063
+ closedCaches.set(history, cached);
1064
+ }
1065
+ const forming = tail.forming && tail.forming.openTime >= last ? toChart(tail.forming) : null;
1066
+ const lastSourceTime = Math.max(
1067
+ (_e = (_d = live.orderbook) == null ? void 0 : _d.timestamp) != null ? _e : -Infinity,
1068
+ (_g = (_f = live.trades[0]) == null ? void 0 : _f.timestamp) != null ? _g : -Infinity,
1069
+ (_h = venue.lastUpdateTs) != null ? _h : -Infinity
1070
+ );
1071
+ const lastUpdateTs = Number.isFinite(lastSourceTime) ? lastSourceTime : null;
1072
+ return __spreadProps(__spreadValues({}, history), {
1073
+ lastUpdateTs,
1074
+ venues: __spreadProps(__spreadValues({}, history.venues), {
1075
+ [history.primaryVenue]: __spreadProps(__spreadValues({}, venue), {
1076
+ candles: cached.candles,
1077
+ liveCandle: forming,
1078
+ lineValue: (_i = forming == null ? void 0 : forming.close) != null ? _i : venue.lineValue,
1079
+ lastTrade: (_j = live.trades[0]) != null ? _j : venue.lastTrade,
1080
+ lastUpdateTs
1081
+ })
1082
+ })
1083
+ });
1084
+ }
543
1085
 
544
1086
  // src/market-data/subscription.ts
545
- import { useEffect as useEffect2, useMemo } from "react";
1087
+ import { useEffect as useEffect4, useMemo as useMemo2 } from "react";
546
1088
  function useMarketDataSubscription({
547
1089
  marketId,
548
1090
  additionalMarketIds,
@@ -551,7 +1093,7 @@ function useMarketDataSubscription({
551
1093
  trades = false
552
1094
  }) {
553
1095
  const ws = useAggWebSocket();
554
- const allIds = useMemo(() => {
1096
+ const allIds = useMemo2(() => {
555
1097
  const ids = /* @__PURE__ */ new Set();
556
1098
  if (marketId) ids.add(marketId);
557
1099
  if (additionalMarketIds) {
@@ -562,7 +1104,7 @@ function useMarketDataSubscription({
562
1104
  return [...ids];
563
1105
  }, [marketId, additionalMarketIds]);
564
1106
  const stableKey = allIds.join("|");
565
- useEffect2(() => {
1107
+ useEffect4(() => {
566
1108
  if (!ws || !allIds.length || !enabled || !orderbook && !trades) return;
567
1109
  const unsubscribers = [];
568
1110
  for (const id of allIds) {
@@ -584,9 +1126,11 @@ function useMarketDataSubscription({
584
1126
  }
585
1127
 
586
1128
  // src/use-market-chart.ts
587
- function useMarketChart(options) {
588
- var _a, _b, _c;
1129
+ function useMarketChartQueries(options) {
1130
+ var _a, _b;
589
1131
  const client = useAggClient();
1132
+ const queryClient = useQueryClient2();
1133
+ const socket = useAggWebSocket();
590
1134
  const isConnected = useAggWebSocketConnectionState();
591
1135
  const {
592
1136
  features: { enableLiveUpdates }
@@ -594,6 +1138,7 @@ function useMarketChart(options) {
594
1138
  const {
595
1139
  marketId,
596
1140
  venueMarketIds,
1141
+ liveOutcomeIds,
597
1142
  interval = "5m",
598
1143
  startTs = null,
599
1144
  endTs = null,
@@ -601,9 +1146,10 @@ function useMarketChart(options) {
601
1146
  enabled = true,
602
1147
  live,
603
1148
  refetchIntervalMs = null,
1149
+ staleTimeMs,
604
1150
  rangeKey
605
1151
  } = options;
606
- const resolvedRangeKey = rangeKey != null ? rangeKey : interval;
1152
+ const resolvedRangeKey = rangeKey != null ? rangeKey : `${interval}:${startTs != null ? startTs : "count"}:${endTs != null ? endTs : "unset"}`;
607
1153
  const liveEnabled = live != null ? live : enableLiveUpdates;
608
1154
  const outcomeIds = (() => {
609
1155
  const ids = /* @__PURE__ */ new Set();
@@ -617,72 +1163,197 @@ function useMarketChart(options) {
617
1163
  })();
618
1164
  const primaryOutcomeId = (_a = marketId != null ? marketId : venueMarketIds == null ? void 0 : venueMarketIds.find(Boolean)) != null ? _a : null;
619
1165
  const isQueryEnabled = enabled && outcomeIds.length > 0 && endTs != null && (startTs != null || countBack != null);
1166
+ const subscribedIds = liveOutcomeIds ? outcomeIds.filter((id) => liveOutcomeIds.includes(id)) : outcomeIds;
1167
+ const subscribedPrimaryId = primaryOutcomeId && subscribedIds.includes(primaryOutcomeId) ? primaryOutcomeId : (_b = subscribedIds[0]) != null ? _b : null;
620
1168
  useMarketDataSubscription({
621
- marketId: primaryOutcomeId,
622
- additionalMarketIds: outcomeIds.slice(1),
623
- enabled: Boolean(isQueryEnabled && liveEnabled),
1169
+ marketId: subscribedPrimaryId,
1170
+ additionalMarketIds: subscribedIds.filter((id) => id !== subscribedPrimaryId),
1171
+ enabled: Boolean(isQueryEnabled && liveEnabled && subscribedIds.length > 0),
624
1172
  orderbook: true,
625
1173
  trades: true
626
1174
  });
627
- const queries = useQueries2({
628
- queries: outcomeIds.map((outcomeId) => ({
1175
+ const queries = useQueries3({
1176
+ queries: outcomeIds.map((outcomeId) => __spreadProps(__spreadValues({
629
1177
  // Cache key is intentionally time-free. The rolling window's
630
1178
  // startTs/endTs advance on every bucket but the user expects the same
631
1179
  // chart on revisit — see comment on `marketDataKeys.chart`. queryFn
632
1180
  // closes over the current window so refetches still fetch fresh data.
633
1181
  queryKey: marketDataKeys.chart(outcomeId, interval, resolvedRangeKey, countBack),
634
1182
  queryFn: (_0) => __async(null, [_0], function* ({ signal }) {
635
- const response = yield client.getChartBars(
636
- {
637
- venueMarketOutcomeId: outcomeId,
638
- resolution: interval,
639
- from: startTs != null ? startTs : void 0,
640
- to: endTs,
641
- countBack: countBack != null ? countBack : void 0
642
- },
643
- { signal }
644
- );
645
- return createMarketChartData({
646
- marketId: outcomeId,
647
- interval,
648
- startTs,
649
- endTs,
650
- response
1183
+ const owner = queryClient.getQueryCache().find({
1184
+ queryKey: marketDataKeys.chart(outcomeId, interval, resolvedRangeKey, countBack),
1185
+ exact: true
651
1186
  });
1187
+ if (!owner) throw new Error("Chart query was removed before fetching");
1188
+ return runChartQuery(owner, signal, () => __async(null, null, function* () {
1189
+ var _a2;
1190
+ const response = yield fetchChartHistory(
1191
+ client,
1192
+ socket,
1193
+ {
1194
+ venueMarketOutcomeId: outcomeId,
1195
+ resolution: interval,
1196
+ from: startTs != null ? startTs : void 0,
1197
+ to: endTs,
1198
+ countBack: countBack != null ? countBack : void 0
1199
+ },
1200
+ signal,
1201
+ (initial) => {
1202
+ var _a3;
1203
+ if (signal.aborted) return;
1204
+ queryClient.setQueryData(
1205
+ marketDataKeys.chart(outcomeId, interval, resolvedRangeKey, countBack),
1206
+ createMarketChartData({
1207
+ marketId: outcomeId,
1208
+ interval: (_a3 = initial.resolution) != null ? _a3 : interval,
1209
+ startTs,
1210
+ endTs,
1211
+ response: initial
1212
+ })
1213
+ );
1214
+ }
1215
+ );
1216
+ return createMarketChartData({
1217
+ marketId: outcomeId,
1218
+ interval: (_a2 = response.resolution) != null ? _a2 : interval,
1219
+ startTs,
1220
+ endTs,
1221
+ response
1222
+ });
1223
+ }));
652
1224
  }),
653
1225
  enabled: isQueryEnabled,
1226
+ meta: { chartWindow: rangeKey ? "rolling" : "fixed", chartEndTs: endTs },
654
1227
  // Treat data as fresh for one bucket — within that window the
655
1228
  // existing entry serves remounts without a refetch, and the
656
1229
  // `refetchInterval` below schedules the next bucket's refresh.
657
- staleTime: refetchIntervalMs != null && refetchIntervalMs > 0 ? refetchIntervalMs : 3e4,
1230
+ staleTime: (query) => {
1231
+ var _a2;
1232
+ if (rangeKey && endTs != null && ((_a2 = query.state.data) == null ? void 0 : _a2.endTs) != null && query.state.data.endTs < endTs)
1233
+ return 0;
1234
+ return staleTimeMs != null ? staleTimeMs : refetchIntervalMs != null && refetchIntervalMs > 0 ? refetchIntervalMs : 3e4;
1235
+ },
658
1236
  // 60 min — survives typical tab/page revisits without re-fetching.
659
1237
  gcTime: 60 * 6e4,
660
- refetchOnWindowFocus: false,
661
- retry: 1,
1238
+ refetchOnWindowFocus: false
1239
+ }, withReadRetry({
1240
+ retry: (failureCount, error) => {
1241
+ if (error instanceof ChartDeliveryError) {
1242
+ if (error.state === "unsupported") return false;
1243
+ if (error.state === "warming") return failureCount < 8;
1244
+ }
1245
+ return failureCount < 1;
1246
+ },
1247
+ retryDelay: (_attempt, error) => error instanceof ChartDeliveryError && error.retryAfterMs != null ? Math.min(error.state === "warming" ? 1e3 : 5e3, Math.max(0, error.retryAfterMs)) : 1e3
1248
+ })), {
662
1249
  placeholderData: keepPreviousData2,
663
- refetchInterval: refetchIntervalMs != null && refetchIntervalMs > 0 ? refetchIntervalMs : false,
1250
+ refetchInterval: (query) => chartRefetchInterval(
1251
+ query,
1252
+ refetchIntervalMs != null && refetchIntervalMs > 0 ? refetchIntervalMs : false
1253
+ ),
664
1254
  refetchIntervalInBackground: false
665
1255
  }))
666
1256
  });
1257
+ const liveStates = useLiveStates(isQueryEnabled ? outcomeIds : []);
1258
+ const overlaid = queries.map((query, index) => {
1259
+ var _a2, _b2;
1260
+ return __spreadProps(__spreadValues({}, query), {
1261
+ data: ((_a2 = query.data) == null ? void 0 : _a2.marketId) === outcomeIds[index] ? overlayCandleTail(query.data, (_b2 = liveStates[index]) == null ? void 0 : _b2.data, !!rangeKey) : void 0
1262
+ });
1263
+ });
1264
+ const availability = {};
1265
+ if (isQueryEnabled)
1266
+ queries.forEach((query, index) => {
1267
+ var _a2, _b2, _c, _d;
1268
+ const sameOutcome = ((_a2 = query.data) == null ? void 0 : _a2.marketId) === outcomeIds[index];
1269
+ const metadata = sameOutcome ? (_b2 = query.data) == null ? void 0 : _b2.delivery : void 0;
1270
+ const error = query.error;
1271
+ const pending = query.isLoading || query.isPlaceholderData || query.isFetching;
1272
+ const state = error instanceof ChartDeliveryError ? error.state : error ? "failed" : pending || !sameOutcome ? "warming" : (_c = metadata == null ? void 0 : metadata.state) != null ? _c : Object.values(query.data.venues).some(
1273
+ (venue) => venue.candles.length > 0 || venue.liveCandle
1274
+ ) ? "ready" : "empty";
1275
+ availability[outcomeIds[index]] = __spreadValues(__spreadProps(__spreadValues({}, metadata), {
1276
+ state,
1277
+ complete: (state === "ready" || state === "empty") && ((_d = metadata == null ? void 0 : metadata.complete) != null ? _d : true)
1278
+ }), error instanceof ChartDeliveryError && error.retryAfterMs != null ? { retryAfterMs: error.retryAfterMs } : {});
1279
+ });
1280
+ return {
1281
+ queries: overlaid,
1282
+ availability,
1283
+ outcomeIds,
1284
+ primaryOutcomeId,
1285
+ interval,
1286
+ startTs,
1287
+ endTs,
1288
+ isQueryEnabled,
1289
+ liveEnabled: liveEnabled && subscribedIds.length > 0,
1290
+ isConnected
1291
+ };
1292
+ }
1293
+ function useMarketCharts(options) {
1294
+ const { queries, availability, outcomeIds, isQueryEnabled, liveEnabled, isConnected } = useMarketChartQueries(options);
1295
+ const charts = {};
1296
+ const errors = {};
1297
+ queries.forEach((query, index) => {
1298
+ var _a;
1299
+ const outcomeId = outcomeIds[index];
1300
+ if (((_a = query.data) == null ? void 0 : _a.marketId) === outcomeId && !query.isPlaceholderData)
1301
+ charts[outcomeId] = query.data;
1302
+ if (query.error)
1303
+ errors[outcomeId] = query.error instanceof Error ? query.error : new Error(String(query.error));
1304
+ });
1305
+ return {
1306
+ availability,
1307
+ charts,
1308
+ errors,
1309
+ isLoading: Boolean(
1310
+ isQueryEnabled && queries.some((query) => query.isLoading || query.isPlaceholderData)
1311
+ ),
1312
+ isLive: Boolean(isQueryEnabled && liveEnabled && isConnected),
1313
+ refetch: () => __async(null, null, function* () {
1314
+ return Promise.all(queries.map((query) => query.refetch()));
1315
+ })
1316
+ };
1317
+ }
1318
+ function useMarketChart(options) {
1319
+ var _a, _b, _c, _d;
1320
+ const {
1321
+ queries,
1322
+ availability,
1323
+ primaryOutcomeId,
1324
+ interval,
1325
+ startTs,
1326
+ endTs,
1327
+ isQueryEnabled,
1328
+ liveEnabled,
1329
+ isConnected
1330
+ } = useMarketChartQueries(options);
667
1331
  const successfulDatasets = queries.flatMap((query) => {
668
1332
  return query.data ? [query.data] : [];
669
1333
  });
670
1334
  const data = primaryOutcomeId && successfulDatasets.length > 0 ? mergeMarketChartData({
671
1335
  marketId: primaryOutcomeId,
672
- interval,
1336
+ interval: (_b = (_a = successfulDatasets[0]) == null ? void 0 : _a.interval) != null ? _b : interval,
673
1337
  startTs,
674
1338
  endTs,
675
1339
  datasets: successfulDatasets
676
1340
  }) : null;
677
- const hasPendingQuery = queries.some((query) => query.isLoading);
678
- const firstError = (_c = (_b = queries.find((query) => query.error != null)) == null ? void 0 : _b.error) != null ? _c : null;
1341
+ const hasPendingQuery = queries.some((query) => query.isLoading || query.isPlaceholderData);
1342
+ const firstError = (_d = (_c = queries.find((query) => query.error != null)) == null ? void 0 : _c.error) != null ? _d : null;
679
1343
  const error = data == null && !hasPendingQuery ? firstError instanceof Error ? firstError : firstError ? new Error(String(firstError)) : null : null;
680
- const isLoading = Boolean(isQueryEnabled && data == null && hasPendingQuery);
1344
+ const isInRange = (candle) => (startTs == null || candle.time >= startTs / 1e3) && (endTs == null || candle.time <= endTs / 1e3);
1345
+ const hasVisibleHistory = queries.some(
1346
+ (query) => !query.isPlaceholderData && query.data && Object.entries(query.data.venues).some(
1347
+ ([venueName, venue]) => (!options.visibleVenue || venueName === options.visibleVenue) && (venue.candles.some(isInRange) || venue.liveCandle != null && isInRange(venue.liveCandle))
1348
+ )
1349
+ );
1350
+ const isLoading = Boolean(isQueryEnabled && hasPendingQuery && !hasVisibleHistory);
681
1351
  return {
682
1352
  data,
1353
+ availability,
683
1354
  isLoading,
684
1355
  error,
685
- isLive: Boolean(liveEnabled && primaryOutcomeId && isConnected),
1356
+ isLive: Boolean(isQueryEnabled && liveEnabled && primaryOutcomeId && isConnected),
686
1357
  refetch: () => __async(null, null, function* () {
687
1358
  return Promise.all(queries.map((query) => query.refetch()));
688
1359
  })
@@ -690,13 +1361,13 @@ function useMarketChart(options) {
690
1361
  }
691
1362
 
692
1363
  // src/use-live-market-stores.ts
693
- import { useEffect as useEffect3, useMemo as useMemo2, useReducer, useRef } from "react";
1364
+ import { useEffect as useEffect5, useMemo as useMemo3, useReducer, useRef as useRef3 } from "react";
694
1365
  function useLiveMarketStores(venueMarketIds) {
695
1366
  const ws = useAggWebSocket();
696
1367
  const [, forceRender] = useReducer((c) => c + 1, 0);
697
- const subsRef = useRef(/* @__PURE__ */ new Map());
698
- const stableKey = useMemo2(() => venueMarketIds.slice().sort().join("|"), [venueMarketIds]);
699
- useEffect3(() => {
1368
+ const subsRef = useRef3(/* @__PURE__ */ new Map());
1369
+ const stableKey = useMemo3(() => venueMarketIds.slice().sort().join("|"), [venueMarketIds]);
1370
+ useEffect5(() => {
700
1371
  if (!ws || venueMarketIds.length === 0) return;
701
1372
  const current = subsRef.current;
702
1373
  const desired = new Set(venueMarketIds);
@@ -733,7 +1404,7 @@ function candleToLive(c, source) {
733
1404
  function useLiveCandles(options) {
734
1405
  var _a;
735
1406
  const { market, interval = "5m", mode = "venue", startTs, endTs } = options;
736
- const venueMarketIds = useMemo3(() => {
1407
+ const venueMarketIds = useMemo4(() => {
737
1408
  if (!market) return [];
738
1409
  if (mode === "venue") return [market.id];
739
1410
  const ids = [market.id];
@@ -753,7 +1424,7 @@ function useLiveCandles(options) {
753
1424
  endTs: endTs != null ? endTs : null,
754
1425
  enabled: !!restMarketId && startTs != null && endTs != null
755
1426
  });
756
- const liveData = useMemo3(() => {
1427
+ const liveData = useMemo4(() => {
757
1428
  if (builders.length === 0) return { closed: [], forming: null };
758
1429
  if (mode === "venue" || builders.length === 1) {
759
1430
  return {
@@ -766,7 +1437,7 @@ function useLiveCandles(options) {
766
1437
  forming: mergeCandles(builders.map((b) => b.getForming(interval)))
767
1438
  };
768
1439
  }, [builders, interval, mode]);
769
- const candles = useMemo3(() => {
1440
+ const candles = useMemo4(() => {
770
1441
  const byTime = /* @__PURE__ */ new Map();
771
1442
  if (chartData) {
772
1443
  const venueEntries = Object.values(chartData.venues);
@@ -791,7 +1462,7 @@ function useLiveCandles(options) {
791
1462
  merged.sort((a, b) => a.time - b.time);
792
1463
  return merged;
793
1464
  }, [chartData, liveData.closed]);
794
- const liveCandle = useMemo3(() => {
1465
+ const liveCandle = useMemo4(() => {
795
1466
  if (!liveData.forming) return null;
796
1467
  return candleToLive(liveData.forming, "live");
797
1468
  }, [liveData.forming]);
@@ -850,8 +1521,8 @@ function useLiveCandleOverlay(options) {
850
1521
  }),
851
1522
  [scale]
852
1523
  );
853
- const scaledCandles = useMemo4(() => rawCandles.map(scaleCandle), [rawCandles, scaleCandle]);
854
- const liveCandle = useMemo4(() => rawLive ? scaleCandle(rawLive) : null, [rawLive, scaleCandle]);
1524
+ const scaledCandles = useMemo5(() => rawCandles.map(scaleCandle), [rawCandles, scaleCandle]);
1525
+ const liveCandle = useMemo5(() => rawLive ? scaleCandle(rawLive) : null, [rawLive, scaleCandle]);
855
1526
  return {
856
1527
  liveCandle,
857
1528
  scaledCandles,
@@ -862,7 +1533,7 @@ function useLiveCandleOverlay(options) {
862
1533
  }
863
1534
 
864
1535
  // src/use-event-orderbook-data.ts
865
- import { useEffect as useEffect4, useMemo as useMemo5 } from "react";
1536
+ import { useEffect as useEffect6, useMemo as useMemo6 } from "react";
866
1537
  function extractSelectedOutcomeIds(selectedMarket) {
867
1538
  var _a, _b, _c;
868
1539
  if (!selectedMarket) return [];
@@ -882,17 +1553,17 @@ function useEventOrderbookData(venueMarkets, selectedMarketId) {
882
1553
  const {
883
1554
  features: { enableLiveUpdates }
884
1555
  } = useAggUiConfig();
885
- const selectedMarket = useMemo5(() => {
1556
+ const selectedMarket = useMemo6(() => {
886
1557
  var _a;
887
1558
  if (!selectedMarketId || !(venueMarkets == null ? void 0 : venueMarkets.length)) return null;
888
1559
  return (_a = venueMarkets.find((m) => m.id === selectedMarketId)) != null ? _a : null;
889
1560
  }, [venueMarkets, selectedMarketId]);
890
- const selectedOutcomeIds = useMemo5(
1561
+ const selectedOutcomeIds = useMemo6(
891
1562
  () => extractSelectedOutcomeIds(selectedMarket),
892
1563
  [selectedMarket]
893
1564
  );
894
1565
  const selectedKey = selectedOutcomeIds.join("|");
895
- useEffect4(() => {
1566
+ useEffect6(() => {
896
1567
  if (!ws || !enableLiveUpdates || !selectedOutcomeIds.length) return;
897
1568
  const unsubscribers = selectedOutcomeIds.map((id) => ws.subscribe(id, "orderbook"));
898
1569
  return () => {
@@ -901,8 +1572,11 @@ function useEventOrderbookData(venueMarkets, selectedMarketId) {
901
1572
  }, [ws, selectedKey, enableLiveUpdates]);
902
1573
  }
903
1574
 
904
- // src/use-live-market.ts
905
- import { useQuery as useQuery3 } from "@tanstack/react-query";
1575
+ // src/use-market-orderbook.ts
1576
+ import { useQuery as useQuery5, useQueryClient as useQueryClient3 } from "@tanstack/react-query";
1577
+
1578
+ // src/market-data/orderbook-snapshot.ts
1579
+ var orderbookTimestampSeconds = (timestamp) => timestamp >= 1e12 ? timestamp / 1e3 : timestamp;
906
1580
  function outcomeOrderbookToState(response, marketId) {
907
1581
  var _a, _b, _c, _d;
908
1582
  const { venue, orderbook, midpoint, spread, seq, checksum, timestamp, venueMarketOutcomeId } = response;
@@ -936,56 +1610,351 @@ function outcomeOrderbookToState(response, marketId) {
936
1610
  spread,
937
1611
  seq,
938
1612
  checksum,
939
- timestamp
1613
+ timestamp: orderbookTimestampSeconds(timestamp)
1614
+ };
1615
+ }
1616
+
1617
+ // src/market-data/orderbook-cache.ts
1618
+ var truncateLevels = (levels, depth) => {
1619
+ if (depth == null) return levels;
1620
+ return levels.slice(0, Math.max(0, depth));
1621
+ };
1622
+ var truncateVenueOrderbooks = (venueOrderbooks, depth) => {
1623
+ if (depth == null) return venueOrderbooks;
1624
+ return Object.fromEntries(
1625
+ Object.entries(venueOrderbooks).map(([venue, book]) => [
1626
+ venue,
1627
+ {
1628
+ bids: truncateLevels(book.bids, depth),
1629
+ asks: truncateLevels(book.asks, depth)
1630
+ }
1631
+ ])
1632
+ );
1633
+ };
1634
+ var aggregateVenueLevels = (venueOrderbooks, side) => {
1635
+ var _a;
1636
+ const levelsByPrice = /* @__PURE__ */ new Map();
1637
+ for (const [venue, orderbook] of Object.entries(venueOrderbooks)) {
1638
+ for (const level of orderbook[side]) {
1639
+ const currentLevel = levelsByPrice.get(level.price);
1640
+ if (currentLevel) {
1641
+ currentLevel.size += level.size;
1642
+ currentLevel.venues[venue] = ((_a = currentLevel.venues[venue]) != null ? _a : 0) + level.size;
1643
+ continue;
1644
+ }
1645
+ levelsByPrice.set(level.price, {
1646
+ price: level.price,
1647
+ size: level.size,
1648
+ venues: {
1649
+ [venue]: level.size
1650
+ }
1651
+ });
1652
+ }
1653
+ }
1654
+ return [...levelsByPrice.values()].sort(
1655
+ (left, right) => side === "bids" ? right.price - left.price : left.price - right.price
1656
+ );
1657
+ };
1658
+ var createMarketOrderbookDataFromVenueOrderbooks = ({
1659
+ marketId,
1660
+ venueOrderbooks,
1661
+ timestamp = Math.floor(Date.now() / 1e3)
1662
+ }) => {
1663
+ var _a, _b, _c, _d;
1664
+ const bids = aggregateVenueLevels(venueOrderbooks, "bids");
1665
+ const asks = aggregateVenueLevels(venueOrderbooks, "asks");
1666
+ const bestBid = (_b = (_a = bids[0]) == null ? void 0 : _a.price) != null ? _b : null;
1667
+ const bestAsk = (_d = (_c = asks[0]) == null ? void 0 : _c.price) != null ? _d : null;
1668
+ return {
1669
+ marketId,
1670
+ orderbook: {
1671
+ bids,
1672
+ asks
1673
+ },
1674
+ venueOrderbooks,
1675
+ venues: Object.fromEntries(
1676
+ Object.entries(venueOrderbooks).map(([venue, orderbook]) => {
1677
+ var _a2, _b2, _c2, _d2;
1678
+ return [
1679
+ venue,
1680
+ {
1681
+ bestBid: (_b2 = (_a2 = orderbook.bids[0]) == null ? void 0 : _a2.price) != null ? _b2 : null,
1682
+ bestAsk: (_d2 = (_c2 = orderbook.asks[0]) == null ? void 0 : _c2.price) != null ? _d2 : null
1683
+ }
1684
+ ];
1685
+ })
1686
+ ),
1687
+ midpoint: bestBid != null && bestAsk != null ? (bestBid + bestAsk) / 2 : null,
1688
+ spread: bestBid != null && bestAsk != null ? bestAsk - bestBid : null,
1689
+ timestamp,
1690
+ snapshotVersion: 0,
1691
+ integrity: "ok"
1692
+ };
1693
+ };
1694
+ var createMarketOrderbookDataFromState = (state, options) => {
1695
+ var _a;
1696
+ return {
1697
+ marketId: state.marketId,
1698
+ orderbook: {
1699
+ bids: state.bids,
1700
+ asks: state.asks
1701
+ },
1702
+ venueOrderbooks: state.venueOrderbooks,
1703
+ venues: state.venues,
1704
+ midpoint: state.midpoint,
1705
+ spread: state.spread,
1706
+ timestamp: state.timestamp,
1707
+ snapshotVersion: (_a = options == null ? void 0 : options.snapshotVersion) != null ? _a : 0,
1708
+ seq: state.seq,
1709
+ checksum: state.checksum,
1710
+ integrity: "ok"
1711
+ };
1712
+ };
1713
+ var marketOrderbookDataToResponse = (data) => {
1714
+ return {
1715
+ venueMarketId: data.marketId,
1716
+ orderbook: data.orderbook,
1717
+ venueOrderbooks: data.venueOrderbooks,
1718
+ venues: data.venues,
1719
+ midpoint: data.midpoint,
1720
+ spread: data.spread,
1721
+ timestamp: data.timestamp,
1722
+ seq: data.seq,
1723
+ checksum: data.checksum,
1724
+ matchedMarkets: data.matchedMarkets
1725
+ };
1726
+ };
1727
+ var applyOrderbookDepth = (data, depth) => {
1728
+ if (depth == null) return data;
1729
+ return __spreadProps(__spreadValues({}, data), {
1730
+ orderbook: {
1731
+ bids: truncateLevels(data.orderbook.bids, depth),
1732
+ asks: truncateLevels(data.orderbook.asks, depth)
1733
+ },
1734
+ venueOrderbooks: truncateVenueOrderbooks(data.venueOrderbooks, depth)
1735
+ });
1736
+ };
1737
+
1738
+ // src/market-data/snapshot-selection.ts
1739
+ import { computeChecksum } from "@agg-build/sdk";
1740
+ var SOCKET_GRACE_MS = 200;
1741
+ var REST_BUDGET_MS = 1500;
1742
+ var MAX_SEED_AGE_MS = 5e3;
1743
+ function hasFreshSnapshot(store, id, now = Date.now()) {
1744
+ const state = store.get(id);
1745
+ return !!state.orderbook && state.integrity === "ok" && now - state.orderbook.timestamp * 1e3 <= MAX_SEED_AGE_MS && state.orderbook.timestamp * 1e3 <= now + 1e3;
1746
+ }
1747
+ function createOrderbookSnapshotQuery(client, store, ids, socketHealthy) {
1748
+ return fetchSnapshotQuery.bind(null, client, store, ids, socketHealthy);
1749
+ }
1750
+ function fetchSnapshotQuery(client, store, ids, socketHealthy, { signal }) {
1751
+ return selectOrderbookSnapshots(client, store, ids, socketHealthy, signal);
1752
+ }
1753
+ function selectOrderbookSnapshots(client, store, ids, socketHealthy, signal) {
1754
+ return __async(this, null, function* () {
1755
+ if (ids.length > 100) throw new Error("Orderbook snapshot batch exceeds 100 outcomes");
1756
+ const missing = () => ids.filter((id) => !hasFreshSnapshot(store, id));
1757
+ if (!missing().length || signal.aborted) return {};
1758
+ if (socketHealthy) {
1759
+ yield new Promise((resolve) => {
1760
+ let unsubscribe2 = () => {
1761
+ };
1762
+ const finish = () => {
1763
+ clearTimeout(timer2);
1764
+ unsubscribe2();
1765
+ signal.removeEventListener("abort", finish);
1766
+ resolve();
1767
+ };
1768
+ const timer2 = setTimeout(finish, SOCKET_GRACE_MS);
1769
+ unsubscribe2 = store.subscribe(ids, () => {
1770
+ if (!missing().length) finish();
1771
+ });
1772
+ signal.addEventListener("abort", finish, { once: true });
1773
+ });
1774
+ }
1775
+ const requested = missing();
1776
+ if (!requested.length || signal.aborted) return {};
1777
+ const initialStates = new Map(requested.map((id) => [id, store.get(id)]));
1778
+ const controller = new AbortController();
1779
+ const abort = () => controller.abort();
1780
+ signal.addEventListener("abort", abort, { once: true });
1781
+ const timer = setTimeout(abort, REST_BUDGET_MS);
1782
+ const unsubscribe = store.subscribe(requested, () => {
1783
+ if (!missing().length) controller.abort();
1784
+ });
1785
+ try {
1786
+ const response = yield client.getOutcomeOrderbooks(
1787
+ { venueMarketOutcomeIds: requested },
1788
+ { signal: controller.signal }
1789
+ );
1790
+ if (signal.aborted) return {};
1791
+ return Object.fromEntries(
1792
+ response.data.flatMap((book, index) => {
1793
+ const id = requested[index];
1794
+ if (!book || !id || hasFreshSnapshot(store, id)) return [];
1795
+ if (book.venueMarketOutcomeId !== id)
1796
+ throw new Error("Orderbook snapshot identity mismatch");
1797
+ if (!Number.isFinite(book.timestamp) || !Number.isSafeInteger(book.seq) || book.seq < 0 || !Number.isInteger(book.checksum))
1798
+ throw new Error("Invalid orderbook snapshot metadata");
1799
+ const snapshot = outcomeOrderbookToState(book, id);
1800
+ if (![...snapshot.bids, ...snapshot.asks].every(
1801
+ (level) => Number.isFinite(level.price) && Number.isFinite(level.size) && level.size >= 0
1802
+ ))
1803
+ throw new Error("Invalid orderbook snapshot levels");
1804
+ const projectedChecksum = book.snapshotChecksum;
1805
+ if (projectedChecksum != null && computeChecksum(snapshot) !== projectedChecksum)
1806
+ throw new Error("Orderbook snapshot checksum mismatch");
1807
+ const current = store.get(id);
1808
+ const live = current.orderbook;
1809
+ const initial = initialStates.get(id);
1810
+ const initialBook = initial.orderbook;
1811
+ if (current.orderbookSnapshotVersion !== initial.orderbookSnapshotVersion || !!live !== !!initialBook || live && initialBook && (live.seq !== initialBook.seq || live.checksum !== initialBook.checksum || live.timestamp !== initialBook.timestamp))
1812
+ return [];
1813
+ if (live) {
1814
+ const sameGeneration = current.isConnected && current.integrity === "ok" && current.orderbookGeneration === current.connectionGeneration;
1815
+ if (snapshot.timestamp < live.timestamp || sameGeneration && snapshot.seq <= live.seq)
1816
+ return [];
1817
+ if (!sameGeneration && snapshot.timestamp <= live.timestamp) return [];
1818
+ }
1819
+ store.update(id, (previous) => __spreadProps(__spreadValues({}, previous), {
1820
+ orderbook: snapshot,
1821
+ integrity: "ok",
1822
+ orderbookError: null
1823
+ }));
1824
+ return [[id, book]];
1825
+ })
1826
+ );
1827
+ } catch (error) {
1828
+ if (signal.aborted || !missing().length) return {};
1829
+ throw error;
1830
+ } finally {
1831
+ clearTimeout(timer);
1832
+ unsubscribe();
1833
+ signal.removeEventListener("abort", abort);
1834
+ }
1835
+ });
1836
+ }
1837
+
1838
+ // src/use-market-orderbook.ts
1839
+ function useMarketOrderbook(options) {
1840
+ var _a, _b, _c, _d, _e, _f, _g;
1841
+ const queryClient = useQueryClient3();
1842
+ const client = useAggClient();
1843
+ const ws = useAggWebSocket();
1844
+ const isConnected = useAggWebSocketConnectionState();
1845
+ const {
1846
+ features: { enableLiveUpdates }
1847
+ } = useAggUiConfig();
1848
+ const { venueMarketOutcomeId, marketId, depth, enabled = true, venueOutcomes, live } = options;
1849
+ const selectedId = (_a = venueMarketOutcomeId != null ? venueMarketOutcomeId : marketId) != null ? _a : null;
1850
+ const liveEnabled = live != null ? live : enableLiveUpdates;
1851
+ const outcomeIds = venueOutcomes == null ? void 0 : venueOutcomes.map((v) => v.venueMarketOutcomeId).filter(Boolean);
1852
+ const subscriptionIds = (outcomeIds == null ? void 0 : outcomeIds.length) ? [...new Set(outcomeIds)] : selectedId ? [selectedId] : [];
1853
+ const selectedOutcomeId = (_b = subscriptionIds[0]) != null ? _b : null;
1854
+ useMarketDataSubscription({
1855
+ marketId: selectedOutcomeId,
1856
+ additionalMarketIds: subscriptionIds.slice(1),
1857
+ enabled: enabled && !!selectedOutcomeId,
1858
+ orderbook: true
1859
+ });
1860
+ const store = getMarketLiveStore(queryClient);
1861
+ const liveQueries = useLiveStates(enabled ? subscriptionIds : []);
1862
+ const sortedIds = [...subscriptionIds].sort();
1863
+ const preferSocket = enabled && Boolean(ws) && (isConnected || subscriptionIds.every((id) => {
1864
+ var _a2;
1865
+ return ((_a2 = store.get(id).connectionGeneration) != null ? _a2 : 0) === 0;
1866
+ }));
1867
+ const seedQuery = useQuery5({
1868
+ queryKey: ["market-orderbook-seed", sortedIds],
1869
+ queryFn: createOrderbookSnapshotQuery(client, store, sortedIds, preferSocket),
1870
+ enabled: enabled && subscriptionIds.length > 0,
1871
+ staleTime: 5e3,
1872
+ gcTime: 6e4,
1873
+ refetchOnWindowFocus: false,
1874
+ retry: false
1875
+ });
1876
+ const liveStates = subscriptionIds.map((subscriptionId, index) => {
1877
+ var _a2, _b2, _c2;
1878
+ return (_c2 = (_b2 = (_a2 = liveQueries[index]) == null ? void 0 : _a2.data) != null ? _b2 : store.peek(subscriptionId)) != null ? _c2 : createMarketLiveState(subscriptionId);
1879
+ });
1880
+ const seededOrderbooks = subscriptionIds.map((subscriptionId, index) => {
1881
+ var _a2, _b2, _c2, _d2;
1882
+ const liveBook = (_b2 = (_a2 = liveStates[index]) == null ? void 0 : _a2.orderbook) != null ? _b2 : null;
1883
+ if (liveBook) return liveBook;
1884
+ const seed = (_c2 = seedQuery.data) == null ? void 0 : _c2[subscriptionId];
1885
+ return ((_d2 = seed == null ? void 0 : seed.orderbook) == null ? void 0 : _d2.bids) && seed.orderbook.asks ? outcomeOrderbookToState(seed, subscriptionId) : null;
1886
+ });
1887
+ const primaryOrderbook = (_c = seededOrderbooks[0]) != null ? _c : null;
1888
+ const primarySnapshotVersion = (_e = (_d = liveStates[0]) == null ? void 0 : _d.orderbookSnapshotVersion) != null ? _e : 0;
1889
+ const data = primaryOrderbook ? applyOrderbookDepth(
1890
+ createMarketOrderbookDataFromState(primaryOrderbook, {
1891
+ snapshotVersion: primarySnapshotVersion
1892
+ }),
1893
+ depth
1894
+ ) : null;
1895
+ const dataById = Object.fromEntries(
1896
+ seededOrderbooks.flatMap((orderbook, index) => {
1897
+ var _a2;
1898
+ if (!orderbook) return [];
1899
+ return [
1900
+ [
1901
+ subscriptionIds[index],
1902
+ applyOrderbookDepth(
1903
+ createMarketOrderbookDataFromState(orderbook, {
1904
+ snapshotVersion: (_a2 = liveStates[index]) == null ? void 0 : _a2.orderbookSnapshotVersion
1905
+ }),
1906
+ depth
1907
+ )
1908
+ ]
1909
+ ];
1910
+ })
1911
+ );
1912
+ const hasAnyOrderbook = seededOrderbooks.some((orderbook) => orderbook != null);
1913
+ const seedLoading = seedQuery.isLoading;
1914
+ const liveOrderbookErrorMessage = enabled && liveEnabled && !hasAnyOrderbook ? (_g = (_f = liveStates.find(
1915
+ (liveState) => typeof liveState.orderbookError === "string" && liveState.orderbookError.length > 0
1916
+ )) == null ? void 0 : _f.orderbookError) != null ? _g : null : null;
1917
+ return {
1918
+ data,
1919
+ dataById,
1920
+ isLoading: enabled && !hasAnyOrderbook && liveOrderbookErrorMessage == null && seedLoading,
1921
+ error: liveOrderbookErrorMessage ? new Error(liveOrderbookErrorMessage) : !hasAnyOrderbook && seedQuery.error instanceof Error ? seedQuery.error : null,
1922
+ isLive: Boolean(enabled && liveEnabled && selectedOutcomeId && isConnected),
1923
+ refetch: () => __async(null, null, function* () {
1924
+ if (!enabled) return;
1925
+ if (subscriptionIds.length === 0) return;
1926
+ subscriptionIds.forEach(
1927
+ (subscriptionId) => store.update(subscriptionId, (previous) => __spreadProps(__spreadValues({}, previous), { orderbookError: null }))
1928
+ );
1929
+ ws == null ? void 0 : ws.resnapshot(subscriptionIds);
1930
+ return seedQuery.refetch();
1931
+ })
940
1932
  };
941
1933
  }
1934
+
1935
+ // src/use-live-market.ts
942
1936
  function useLiveMarket(canonicalMarketId) {
943
- var _a, _b;
944
- const client = useAggClient();
945
- const isConnected = useAggWebSocketConnectionState();
946
1937
  const {
947
1938
  features: { enableLiveUpdates }
948
1939
  } = useAggUiConfig();
949
- const isLiveSubscriptionEnabled = Boolean(canonicalMarketId && enableLiveUpdates);
950
- useMarketDataSubscription({
951
- marketId: canonicalMarketId,
952
- enabled: isLiveSubscriptionEnabled,
953
- orderbook: true
954
- });
955
- const query = useQuery3({
956
- queryKey: marketDataKeys.live(canonicalMarketId != null ? canonicalMarketId : "__disabled__"),
957
- queryFn: () => __async(null, null, function* () {
958
- return createMarketLiveState(canonicalMarketId != null ? canonicalMarketId : "__disabled__");
959
- }),
960
- enabled: false,
961
- staleTime: Infinity,
962
- gcTime: 5 * 6e4,
963
- initialData: () => createMarketLiveState(canonicalMarketId != null ? canonicalMarketId : "__disabled__", {
964
- isConnected: Boolean(isLiveSubscriptionEnabled && isConnected)
965
- })
966
- });
967
- const seedQuery = useQuery3({
968
- queryKey: ["live-market-seed", canonicalMarketId],
969
- queryFn: ({ signal }) => canonicalMarketId ? client.getOutcomeOrderbook(canonicalMarketId, { signal }) : Promise.resolve(null),
970
- enabled: Boolean(canonicalMarketId),
971
- staleTime: 5e3,
972
- gcTime: 6e4,
973
- refetchOnWindowFocus: false,
974
- retry: 1
1940
+ const connected = useAggWebSocketConnectionState();
1941
+ const [snapshot] = useLiveStates([canonicalMarketId != null ? canonicalMarketId : "__disabled__"]);
1942
+ useMarketOrderbook({
1943
+ venueMarketOutcomeId: canonicalMarketId,
1944
+ enabled: !!canonicalMarketId,
1945
+ live: enableLiveUpdates
975
1946
  });
976
- const data = (_a = query.data) != null ? _a : createMarketLiveState(canonicalMarketId != null ? canonicalMarketId : "__disabled__");
977
- const orderbook = (_b = data.orderbook) != null ? _b : canonicalMarketId && seedQuery.data ? outcomeOrderbookToState(seedQuery.data, canonicalMarketId) : null;
1947
+ const data = snapshot.data;
978
1948
  return {
979
- orderbook,
1949
+ orderbook: data.orderbook,
980
1950
  trades: data.trades,
981
- isConnected: isLiveSubscriptionEnabled ? data.isConnected : false,
1951
+ isConnected: !!canonicalMarketId && enableLiveUpdates && connected,
982
1952
  integrity: data.integrity
983
1953
  };
984
1954
  }
985
1955
 
986
1956
  // src/use-live-outcome-prices.ts
987
- import { useMemo as useMemo6, useRef as useRef2 } from "react";
988
- import { useQueries as useQueries3 } from "@tanstack/react-query";
1957
+ import { useMemo as useMemo7, useRef as useRef4 } from "react";
989
1958
  var EMPTY_PRICES = /* @__PURE__ */ new Map();
990
1959
  var buildMidpointFingerprint = (outcomeIds, queries) => {
991
1960
  var _a, _b, _c;
@@ -1000,7 +1969,7 @@ function useLiveOutcomePrices(venueMarkets) {
1000
1969
  const {
1001
1970
  features: { enableLiveUpdates }
1002
1971
  } = useAggUiConfig();
1003
- const outcomeIds = useMemo6(() => {
1972
+ const outcomeIds = useMemo7(() => {
1004
1973
  var _a, _b;
1005
1974
  if (!(venueMarkets == null ? void 0 : venueMarkets.length)) return [];
1006
1975
  const ids = /* @__PURE__ */ new Set();
@@ -1014,22 +1983,13 @@ function useLiveOutcomePrices(venueMarkets) {
1014
1983
  }
1015
1984
  return [...ids].sort();
1016
1985
  }, [venueMarkets]);
1017
- const queries = useQueries3({
1018
- queries: outcomeIds.map((id) => ({
1019
- queryKey: marketDataKeys.live(id),
1020
- queryFn: () => createMarketLiveState(id),
1021
- enabled: false,
1022
- staleTime: Infinity,
1023
- gcTime: 5 * 6e4,
1024
- initialData: () => createMarketLiveState(id)
1025
- }))
1026
- });
1986
+ const queries = useLiveStates(outcomeIds);
1027
1987
  const fingerprint = buildMidpointFingerprint(outcomeIds, queries);
1028
- const prevRef = useRef2({
1988
+ const prevRef = useRef4({
1029
1989
  fingerprint: "",
1030
1990
  prices: EMPTY_PRICES
1031
1991
  });
1032
- const data = useMemo6(() => {
1992
+ const data = useMemo7(() => {
1033
1993
  var _a, _b, _c;
1034
1994
  if (!(venueMarkets == null ? void 0 : venueMarkets.length)) return EMPTY_PRICES;
1035
1995
  if (fingerprint === prevRef.current.fingerprint) {
@@ -1056,8 +2016,7 @@ function findLivePriceById(livePrices, id) {
1056
2016
  }
1057
2017
 
1058
2018
  // src/use-live-best-prices.ts
1059
- import { useMemo as useMemo7, useRef as useRef3 } from "react";
1060
- import { useQueries as useQueries4 } from "@tanstack/react-query";
2019
+ import { useMemo as useMemo8, useRef as useRef5 } from "react";
1061
2020
  var EMPTY = /* @__PURE__ */ new Map();
1062
2021
  var EMPTY_CANDIDATES = /* @__PURE__ */ new Map();
1063
2022
  var extractOutcomeBestPrices = (state) => {
@@ -1086,7 +2045,7 @@ function useLiveBestPrices(venueMarkets) {
1086
2045
  const {
1087
2046
  features: { enableLiveUpdates }
1088
2047
  } = useAggUiConfig();
1089
- const outcomeIds = useMemo7(() => {
2048
+ const outcomeIds = useMemo8(() => {
1090
2049
  var _a, _b;
1091
2050
  if (!(venueMarkets == null ? void 0 : venueMarkets.length)) return [];
1092
2051
  const ids = /* @__PURE__ */ new Set();
@@ -1100,22 +2059,13 @@ function useLiveBestPrices(venueMarkets) {
1100
2059
  }
1101
2060
  return [...ids].sort();
1102
2061
  }, [venueMarkets]);
1103
- const queries = useQueries4({
1104
- queries: outcomeIds.map((id) => ({
1105
- queryKey: marketDataKeys.live(id),
1106
- queryFn: () => createMarketLiveState(id),
1107
- enabled: false,
1108
- staleTime: Infinity,
1109
- gcTime: 5 * 6e4,
1110
- initialData: () => createMarketLiveState(id)
1111
- }))
1112
- });
2062
+ const queries = useLiveStates(outcomeIds);
1113
2063
  const fingerprint = buildFingerprint(outcomeIds, queries);
1114
- const prevRef = useRef3({
2064
+ const prevRef = useRef5({
1115
2065
  fingerprint: "",
1116
2066
  result: EMPTY
1117
2067
  });
1118
- return useMemo7(() => {
2068
+ return useMemo8(() => {
1119
2069
  var _a, _b, _c, _d;
1120
2070
  if (!(venueMarkets == null ? void 0 : venueMarkets.length) || !enableLiveUpdates) return EMPTY;
1121
2071
  if (fingerprint === prevRef.current.fingerprint) return prevRef.current.result;
@@ -1305,7 +2255,7 @@ function useLiveBestPriceCandidates(venueMarkets) {
1305
2255
  const {
1306
2256
  features: { enableLiveUpdates }
1307
2257
  } = useAggUiConfig();
1308
- const outcomeIds = useMemo7(() => {
2258
+ const outcomeIds = useMemo8(() => {
1309
2259
  var _a, _b;
1310
2260
  if (!(venueMarkets == null ? void 0 : venueMarkets.length)) return [];
1311
2261
  const ids = /* @__PURE__ */ new Set();
@@ -1319,22 +2269,13 @@ function useLiveBestPriceCandidates(venueMarkets) {
1319
2269
  }
1320
2270
  return [...ids].sort();
1321
2271
  }, [venueMarkets]);
1322
- const queries = useQueries4({
1323
- queries: outcomeIds.map((id) => ({
1324
- queryKey: marketDataKeys.live(id),
1325
- queryFn: () => createMarketLiveState(id),
1326
- enabled: false,
1327
- staleTime: Infinity,
1328
- gcTime: 5 * 6e4,
1329
- initialData: () => createMarketLiveState(id)
1330
- }))
1331
- });
2272
+ const queries = useLiveStates(outcomeIds);
1332
2273
  const fingerprint = buildCandidateFingerprint(outcomeIds, queries);
1333
- const prevRef = useRef3({
2274
+ const prevRef = useRef5({
1334
2275
  fingerprint: "",
1335
2276
  result: EMPTY_CANDIDATES
1336
2277
  });
1337
- return useMemo7(() => {
2278
+ return useMemo8(() => {
1338
2279
  if (!(venueMarkets == null ? void 0 : venueMarkets.length) || !enableLiveUpdates) return EMPTY_CANDIDATES;
1339
2280
  if (fingerprint === prevRef.current.fingerprint) return prevRef.current.result;
1340
2281
  const states = outcomeIds.map((_, i) => {
@@ -1363,8 +2304,8 @@ function useLiveTrades(canonicalMarketId) {
1363
2304
  }
1364
2305
 
1365
2306
  // src/use-midpoints.ts
1366
- import { useQuery as useQuery4 } from "@tanstack/react-query";
1367
- import { useMemo as useMemo8 } from "react";
2307
+ import { useQuery as useQuery6 } from "@tanstack/react-query";
2308
+ import { useMemo as useMemo9 } from "react";
1368
2309
  var normalizeVenueMarketIds = (venueMarkets) => {
1369
2310
  var _a;
1370
2311
  const ids = /* @__PURE__ */ new Set();
@@ -1376,6 +2317,21 @@ var normalizeVenueMarketIds = (venueMarkets) => {
1376
2317
  }
1377
2318
  return [...ids].sort((left, right) => left.localeCompare(right));
1378
2319
  };
2320
+ var EMPTY_MIDPOINTS_POLL_MS = 5e3;
2321
+ var EMPTY_MIDPOINTS_MAX_FETCHES = 12;
2322
+ var hasUsableMidpoint = (item) => {
2323
+ var _a;
2324
+ if (!item) return false;
2325
+ if (item.midpoint != null) return true;
2326
+ return ((_a = item.outcomes) != null ? _a : []).some(
2327
+ (outcome) => outcome.midpoint != null || outcome.bestBid != null || outcome.bestAsk != null
2328
+ );
2329
+ };
2330
+ var isMidpointResponseComplete = (data, primaryVenueMarketIds) => {
2331
+ if (!(data == null ? void 0 : data.data)) return false;
2332
+ const itemByMarketId = new Map(data.data.map((item) => [item.venueMarketId, item]));
2333
+ return primaryVenueMarketIds.every((id) => hasUsableMidpoint(itemByMarketId.get(id)));
2334
+ };
1379
2335
  var normalizeOutcomeLabel = (label) => {
1380
2336
  return typeof label === "string" ? label.trim().toLowerCase() : "";
1381
2337
  };
@@ -1537,22 +2493,30 @@ function useMidpoints(venueMarkets, options) {
1537
2493
  market: { maxMidpointIdsPerRequest: configuredMaxMidpointIdsPerRequest }
1538
2494
  } = useAggUiConfig();
1539
2495
  const maxMidpointIdsPerRequest = (_a = options == null ? void 0 : options.maxMidpointIdsPerRequest) != null ? _a : configuredMaxMidpointIdsPerRequest;
1540
- const ids = useMemo8(() => normalizeVenueMarketIds(venueMarkets), [venueMarkets]);
1541
- const bestMidpointCandidateOutcomeIds = useMemo8(
2496
+ const ids = useMemo9(() => normalizeVenueMarketIds(venueMarkets), [venueMarkets]);
2497
+ const bestMidpointCandidateOutcomeIds = useMemo9(
1542
2498
  () => resolveBestMidpointCandidateOutcomeIds(venueMarkets),
1543
2499
  [venueMarkets]
1544
2500
  );
1545
- const { data, isLoading } = useQuery4({
2501
+ const primaryVenueMarketIds = useMemo9(
2502
+ () => (venueMarkets != null ? venueMarkets : []).map((market) => market.id).filter(Boolean),
2503
+ [venueMarkets]
2504
+ );
2505
+ const { data, isLoading } = useQuery6({
1546
2506
  queryKey: ["midpoints", ids, maxMidpointIdsPerRequest],
1547
2507
  queryFn: () => client.getMidpoints(ids, { bestPrice: true, maxMidpointIdsPerRequest }),
1548
2508
  enabled: ids.length > 0,
1549
- staleTime: Infinity,
2509
+ // Complete responses stay cached for the session (unchanged behavior).
2510
+ // Incomplete ones are stale, so a remount refetches them and a short,
2511
+ // bounded poll picks up prices once a cold venue book warms.
2512
+ staleTime: (query) => isMidpointResponseComplete(query.state.data, primaryVenueMarketIds) ? Infinity : 0,
2513
+ refetchInterval: (query) => !isMidpointResponseComplete(query.state.data, primaryVenueMarketIds) && query.state.dataUpdateCount < EMPTY_MIDPOINTS_MAX_FETCHES ? EMPTY_MIDPOINTS_POLL_MS : false,
1550
2514
  gcTime: 30 * 6e4,
1551
- refetchOnMount: false,
2515
+ refetchOnMount: true,
1552
2516
  refetchOnWindowFocus: false,
1553
2517
  refetchOnReconnect: false
1554
2518
  });
1555
- const result = useMemo8(() => {
2519
+ const result = useMemo9(() => {
1556
2520
  var _a2, _b, _c, _d, _e, _f;
1557
2521
  const map = /* @__PURE__ */ new Map();
1558
2522
  const currentMap = /* @__PURE__ */ new Map();
@@ -1605,7 +2569,7 @@ function useMidpoints(venueMarkets, options) {
1605
2569
  }
1606
2570
  return { map, currentMap, venueMap };
1607
2571
  }, [data, venueMarkets]);
1608
- const { bestMidpoint, bestMidpointVenue } = useMemo8(() => {
2572
+ const { bestMidpoint, bestMidpointVenue } = useMemo9(() => {
1609
2573
  let value;
1610
2574
  let venue;
1611
2575
  for (const outcomeId of bestMidpointCandidateOutcomeIds) {
@@ -1626,7 +2590,7 @@ function useMidpoints(venueMarkets, options) {
1626
2590
  }
1627
2591
  return { bestMidpoint: value, bestMidpointVenue: venue };
1628
2592
  }, [bestMidpointCandidateOutcomeIds, result.map, result.venueMap]);
1629
- const { bestPrices, bestPriceVenuesByOutcomeId } = useMemo8(
2593
+ const { bestPrices, bestPriceVenuesByOutcomeId } = useMemo9(
1630
2594
  () => extractBestPrices(data == null ? void 0 : data.data, venueMarkets),
1631
2595
  [data == null ? void 0 : data.data, venueMarkets]
1632
2596
  );
@@ -1644,7 +2608,7 @@ function useMidpoints(venueMarkets, options) {
1644
2608
  }
1645
2609
 
1646
2610
  // src/use-rolling-chart-window.ts
1647
- import { useEffect as useEffect5, useMemo as useMemo9, useState as useState4 } from "react";
2611
+ import { useEffect as useEffect7, useMemo as useMemo10, useState as useState4 } from "react";
1648
2612
  var RANGE_SECONDS_BY_RANGE = {
1649
2613
  "1H": 60 * 60,
1650
2614
  "6H": 6 * 60 * 60,
@@ -1675,135 +2639,27 @@ var useRollingChartWindow = (options) => {
1675
2639
  var _a;
1676
2640
  const tickMs = (_a = options.tickMs) != null ? _a : 1e3;
1677
2641
  const optionsNowMs = options.nowMs;
1678
- const nowMs = useMemo9(() => optionsNowMs != null ? optionsNowMs : (() => Date.now()), [optionsNowMs]);
2642
+ const nowMs = useMemo10(() => optionsNowMs != null ? optionsNowMs : (() => Date.now()), [optionsNowMs]);
1679
2643
  const [nowSec, setNowSec] = useState4(() => Math.floor(nowMs() / 1e3));
1680
- useEffect5(() => {
2644
+ useEffect7(() => {
1681
2645
  setNowSec(Math.floor(nowMs() / 1e3));
1682
2646
  const handle = setInterval(() => {
1683
2647
  setNowSec(Math.floor(nowMs() / 1e3));
1684
2648
  }, tickMs);
1685
2649
  return () => clearInterval(handle);
1686
2650
  }, [tickMs, nowMs]);
1687
- return useMemo9(
2651
+ return useMemo10(
1688
2652
  () => resolveRollingWindow({ range: options.range, nowSec, interval: options.interval }),
1689
2653
  [options.range, options.interval, nowSec]
1690
2654
  );
1691
2655
  };
1692
2656
 
1693
- // src/use-market-orderbook.ts
1694
- import { useQueries as useQueries5, useQueryClient } from "@tanstack/react-query";
1695
- function useMarketOrderbook(options) {
1696
- var _a, _b, _c, _d, _e, _f, _g;
1697
- const queryClient = useQueryClient();
1698
- const client = useAggClient();
1699
- const ws = useAggWebSocket();
1700
- const isConnected = useAggWebSocketConnectionState();
1701
- const {
1702
- features: { enableLiveUpdates }
1703
- } = useAggUiConfig();
1704
- const { venueMarketOutcomeId, marketId, depth, enabled = true, venueOutcomes, live } = options;
1705
- const selectedId = (_a = venueMarketOutcomeId != null ? venueMarketOutcomeId : marketId) != null ? _a : null;
1706
- const liveEnabled = live != null ? live : enableLiveUpdates;
1707
- const outcomeIds = venueOutcomes == null ? void 0 : venueOutcomes.map((v) => v.venueMarketOutcomeId).filter(Boolean);
1708
- const subscriptionIds = (outcomeIds == null ? void 0 : outcomeIds.length) ? [...new Set(outcomeIds)] : selectedId ? [selectedId] : [];
1709
- const selectedOutcomeId = (_b = subscriptionIds[0]) != null ? _b : null;
1710
- useMarketDataSubscription({
1711
- marketId: selectedOutcomeId,
1712
- additionalMarketIds: subscriptionIds.slice(1),
1713
- enabled: enabled && !!selectedOutcomeId,
1714
- orderbook: true
1715
- });
1716
- const liveQueries = useQueries5({
1717
- queries: subscriptionIds.map((subscriptionId) => ({
1718
- queryKey: marketDataKeys.live(subscriptionId),
1719
- queryFn: () => createMarketLiveState(subscriptionId),
1720
- staleTime: Infinity,
1721
- gcTime: 5 * 6e4,
1722
- refetchOnMount: false,
1723
- refetchOnWindowFocus: false,
1724
- refetchOnReconnect: false
1725
- }))
1726
- });
1727
- const seedQueries = useQueries5({
1728
- queries: subscriptionIds.map((subscriptionId) => ({
1729
- queryKey: ["market-orderbook-seed", subscriptionId],
1730
- queryFn: ({ signal }) => client.getOutcomeOrderbook(subscriptionId, { signal }),
1731
- enabled: enabled && !!subscriptionId,
1732
- staleTime: 5e3,
1733
- gcTime: 6e4,
1734
- refetchOnWindowFocus: false,
1735
- retry: 1
1736
- }))
1737
- });
1738
- const liveStates = subscriptionIds.map((subscriptionId, index) => {
1739
- var _a2, _b2;
1740
- return (_b2 = (_a2 = liveQueries[index]) == null ? void 0 : _a2.data) != null ? _b2 : createMarketLiveState(subscriptionId);
1741
- });
1742
- const seededOrderbooks = subscriptionIds.map((subscriptionId, index) => {
1743
- var _a2, _b2, _c2, _d2;
1744
- const liveBook = (_b2 = (_a2 = liveStates[index]) == null ? void 0 : _a2.orderbook) != null ? _b2 : null;
1745
- if (liveBook) return liveBook;
1746
- const seed = (_c2 = seedQueries[index]) == null ? void 0 : _c2.data;
1747
- return ((_d2 = seed == null ? void 0 : seed.orderbook) == null ? void 0 : _d2.bids) && seed.orderbook.asks ? outcomeOrderbookToState(seed, subscriptionId) : null;
1748
- });
1749
- const primaryOrderbook = (_c = seededOrderbooks[0]) != null ? _c : null;
1750
- const primarySnapshotVersion = (_e = (_d = liveStates[0]) == null ? void 0 : _d.orderbookSnapshotVersion) != null ? _e : 0;
1751
- const data = primaryOrderbook ? applyOrderbookDepth(
1752
- createMarketOrderbookDataFromState(primaryOrderbook, {
1753
- snapshotVersion: primarySnapshotVersion
1754
- }),
1755
- depth
1756
- ) : null;
1757
- const dataById = Object.fromEntries(
1758
- seededOrderbooks.flatMap((orderbook, index) => {
1759
- var _a2;
1760
- if (!orderbook) return [];
1761
- return [
1762
- [
1763
- subscriptionIds[index],
1764
- applyOrderbookDepth(
1765
- createMarketOrderbookDataFromState(orderbook, {
1766
- snapshotVersion: (_a2 = liveStates[index]) == null ? void 0 : _a2.orderbookSnapshotVersion
1767
- }),
1768
- depth
1769
- )
1770
- ]
1771
- ];
1772
- })
1773
- );
1774
- const hasAnyOrderbook = seededOrderbooks.some((orderbook) => orderbook != null);
1775
- const seedLoading = seedQueries.some((query) => query.isLoading);
1776
- const liveOrderbookErrorMessage = enabled && liveEnabled && !hasAnyOrderbook ? (_g = (_f = liveStates.find(
1777
- (liveState) => typeof liveState.orderbookError === "string" && liveState.orderbookError.length > 0
1778
- )) == null ? void 0 : _f.orderbookError) != null ? _g : null : null;
1779
- return {
1780
- data,
1781
- dataById,
1782
- isLoading: enabled && !hasAnyOrderbook && liveOrderbookErrorMessage == null && (isConnected || seedLoading),
1783
- error: liveOrderbookErrorMessage ? new Error(liveOrderbookErrorMessage) : null,
1784
- isLive: Boolean(liveEnabled && selectedOutcomeId && isConnected),
1785
- refetch: () => __async(null, null, function* () {
1786
- if (!enabled) return;
1787
- if (subscriptionIds.length === 0) return;
1788
- subscriptionIds.forEach((subscriptionId) => {
1789
- queryClient.setQueryData(
1790
- marketDataKeys.live(subscriptionId),
1791
- (previous) => __spreadProps(__spreadValues({}, previous != null ? previous : createMarketLiveState(subscriptionId)), {
1792
- orderbookError: null
1793
- })
1794
- );
1795
- });
1796
- ws == null ? void 0 : ws.resnapshot(subscriptionIds);
1797
- })
1798
- };
1799
- }
1800
-
1801
2657
  // src/use-arb.ts
1802
- import { useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
2658
+ import { useEffect as useEffect8, useRef as useRef6, useState as useState5 } from "react";
1803
2659
  function useMarketArb(marketId) {
1804
2660
  const ws = useAggWebSocket();
1805
2661
  const [state, setState] = useState5({ arbReturn: null, isLive: false });
1806
- useEffect6(() => {
2662
+ useEffect8(() => {
1807
2663
  if (!ws || !marketId) return;
1808
2664
  const cached = ws.getArb(marketId);
1809
2665
  if (cached != null) {
@@ -1823,9 +2679,9 @@ var EMPTY_ARB_FEED = {
1823
2679
  function useArbFeed() {
1824
2680
  const ws = useAggWebSocket();
1825
2681
  const [state, setState] = useState5(EMPTY_ARB_FEED);
1826
- const stateRef = useRef4(state);
2682
+ const stateRef = useRef6(state);
1827
2683
  stateRef.current = state;
1828
- useEffect6(() => {
2684
+ useEffect8(() => {
1829
2685
  if (!ws) return;
1830
2686
  const off = ws.subscribeArbFeed((batch) => {
1831
2687
  setState((prev) => {
@@ -1857,7 +2713,9 @@ function useArbFeed() {
1857
2713
  }
1858
2714
 
1859
2715
  // src/use-order-book.ts
1860
- import { keepPreviousData as keepPreviousData3, useQuery as useQuery5 } from "@tanstack/react-query";
2716
+ import { keepPreviousData as keepPreviousData3, useQuery as useQuery7 } from "@tanstack/react-query";
2717
+ var fetchBatchBookQuery = (client, venueMarketIds, { signal }) => client.getOrderbooks({ venueMarketIds }, { signal });
2718
+ var createBatchBookQuery = (client, venueMarketIds) => fetchBatchBookQuery.bind(null, client, venueMarketIds);
1861
2719
  function useOrderBook(options) {
1862
2720
  const client = useAggClient();
1863
2721
  const { orderbooks, enabled = true, canonicalMarketId } = options;
@@ -1870,14 +2728,9 @@ function useOrderBook(options) {
1870
2728
  venueMarketOutcomeId: outcome.id
1871
2729
  })) : void 0
1872
2730
  });
1873
- const batchedResult = useQuery5({
2731
+ const batchedResult = useQuery7({
1874
2732
  queryKey: requestedVenueMarketIds.length ? ["orderbooks", requestedVenueMarketIds, null] : ["orderbooks", "__disabled__", null],
1875
- queryFn: ({ signal }) => client.getOrderbooks(
1876
- {
1877
- venueMarketIds: requestedVenueMarketIds
1878
- },
1879
- { signal }
1880
- ),
2733
+ queryFn: createBatchBookQuery(client, requestedVenueMarketIds),
1881
2734
  enabled: enabled && !canonicalMarketId && requestedVenueMarketIds.length > 0,
1882
2735
  staleTime: 1e4,
1883
2736
  gcTime: 5 * 6e4,
@@ -1938,7 +2791,7 @@ function useOrderBook(options) {
1938
2791
  }
1939
2792
 
1940
2793
  // src/use-orderbook-quote.ts
1941
- import { keepPreviousData as keepPreviousData4, useQuery as useQuery6 } from "@tanstack/react-query";
2794
+ import { keepPreviousData as keepPreviousData4, useQuery as useQuery8 } from "@tanstack/react-query";
1942
2795
  var QUOTE_DEBOUNCE_MS = 300;
1943
2796
  var createUnavailableOrderbookError = (message, code, retryable) => {
1944
2797
  const error = new Error(message);
@@ -2000,7 +2853,7 @@ function useOrderbookQuote(options) {
2000
2853
  const { marketId, side, size, enabled = true } = options;
2001
2854
  const debouncedSize = useDebouncedValue(size, QUOTE_DEBOUNCE_MS);
2002
2855
  const shouldFetch = enabled && !!marketId && debouncedSize > 0;
2003
- const query = useQuery6({
2856
+ const query = useQuery8({
2004
2857
  queryKey: marketId ? marketDataKeys.orderbookQuote(marketId, side, debouncedSize) : ["market-data", "orderbook-quote", "__disabled__"],
2005
2858
  queryFn: () => __async(null, null, function* () {
2006
2859
  var _a2, _b, _c, _d;
@@ -2042,12 +2895,12 @@ function useOrderbookQuote(options) {
2042
2895
  }
2043
2896
 
2044
2897
  // src/use-orders.ts
2045
- import { useQuery as useQuery7 } from "@tanstack/react-query";
2898
+ import { useQuery as useQuery9 } from "@tanstack/react-query";
2046
2899
  function useOrders(options = {}) {
2047
2900
  var _a, _b, _c, _d;
2048
2901
  const client = useAggClient();
2049
2902
  const { userId, status, limit = 50, offset = 0, enabled = true } = options;
2050
- const query = useQuery7({
2903
+ const query = useQuery9({
2051
2904
  queryKey: ["orders", userId != null ? userId : "me", status != null ? status : "all", limit, offset],
2052
2905
  queryFn: () => client.getOrders({
2053
2906
  userId,
@@ -2070,15 +2923,24 @@ import {
2070
2923
  keepPreviousData as keepPreviousData5,
2071
2924
  useInfiniteQuery as useInfiniteQuery5
2072
2925
  } from "@tanstack/react-query";
2073
- import { useContext as useContext2, useEffect as useEffect7, useState as useState6 } from "react";
2926
+ import { useContext as useContext2, useEffect as useEffect9, useState as useState6 } from "react";
2074
2927
  function useSearch(options) {
2075
2928
  var _a, _b, _c, _d;
2076
2929
  const client = useContext2(AggClientContext);
2077
2930
  const queryClient = useContext2(QueryClientContext);
2078
2931
  const [fallbackQueryClient] = useState6(() => new QueryClient());
2079
- const { q, type, categoryIds, status, limit = 20, enabled = true, deep = false } = options;
2932
+ const {
2933
+ q,
2934
+ type,
2935
+ categoryIds,
2936
+ status,
2937
+ limit = 20,
2938
+ enabled = true,
2939
+ deep = false,
2940
+ mode = "full"
2941
+ } = options;
2080
2942
  const isEnabled = enabled && q.length > 0;
2081
- useEffect7(() => {
2943
+ useEffect9(() => {
2082
2944
  if (queryClient) return void 0;
2083
2945
  fallbackQueryClient.mount();
2084
2946
  return () => {
@@ -2088,32 +2950,40 @@ function useSearch(options) {
2088
2950
  if (isEnabled && !client) {
2089
2951
  throw new Error("useSearch must be used within an <AggProvider>");
2090
2952
  }
2953
+ const queryKey = [
2954
+ "search",
2955
+ q,
2956
+ type,
2957
+ (_a = categoryIds == null ? void 0 : categoryIds.join(",")) != null ? _a : "",
2958
+ (_b = status == null ? void 0 : status.join(",")) != null ? _b : "",
2959
+ limit,
2960
+ deep,
2961
+ mode
2962
+ ];
2963
+ const effectiveQueryClient = queryClient != null ? queryClient : fallbackQueryClient;
2964
+ const retryOptions = withReadRetry(effectiveQueryClient.defaultQueryOptions({ queryKey }));
2091
2965
  const query = useInfiniteQuery5(
2092
- {
2966
+ __spreadProps(__spreadValues({
2093
2967
  // deep is part of the key — TanStack treats deep vs light as
2094
2968
  // independent queries so users can have both modes cached side-by-side.
2095
- queryKey: [
2096
- "search",
2097
- q,
2098
- type,
2099
- (_a = categoryIds == null ? void 0 : categoryIds.join(",")) != null ? _a : "",
2100
- (_b = status == null ? void 0 : status.join(",")) != null ? _b : "",
2101
- limit,
2102
- deep
2103
- ],
2104
- queryFn: (_0) => __async(null, [_0], function* ({ pageParam }) {
2969
+ queryKey,
2970
+ queryFn: (_0) => __async(null, [_0], function* ({ pageParam, signal }) {
2105
2971
  if (!client) {
2106
2972
  throw new Error("useSearch must be used within an <AggProvider>");
2107
2973
  }
2108
- const res = yield client.search({
2109
- q,
2110
- type,
2111
- categoryIds,
2112
- status,
2113
- limit,
2114
- cursor: pageParam,
2115
- deep
2116
- });
2974
+ const res = yield client.search(
2975
+ {
2976
+ q,
2977
+ type,
2978
+ categoryIds,
2979
+ status,
2980
+ limit,
2981
+ cursor: pageParam,
2982
+ deep,
2983
+ mode
2984
+ },
2985
+ { signal }
2986
+ );
2117
2987
  return res;
2118
2988
  }),
2119
2989
  initialPageParam: void 0,
@@ -2122,10 +2992,11 @@ function useSearch(options) {
2122
2992
  if (!lastPage.hasMore) return void 0;
2123
2993
  return (_a2 = lastPage.nextCursor) != null ? _a2 : void 0;
2124
2994
  },
2125
- placeholderData: keepPreviousData5,
2995
+ placeholderData: keepPreviousData5
2996
+ }, retryOptions), {
2126
2997
  enabled: isEnabled && !!client
2127
- },
2128
- queryClient != null ? queryClient : fallbackQueryClient
2998
+ }),
2999
+ effectiveQueryClient
2129
3000
  );
2130
3001
  const data = (_d = (_c = query.data) == null ? void 0 : _c.pages.flatMap((page) => page.data)) != null ? _d : [];
2131
3002
  return {
@@ -2165,7 +3036,8 @@ function useMarketSearch(options) {
2165
3036
  status,
2166
3037
  limit,
2167
3038
  enabled: suggestionsEnabled,
2168
- deep: false
3039
+ deep: false,
3040
+ mode: "instant"
2169
3041
  });
2170
3042
  const submittedQ = (_a = submittedQuery == null ? void 0 : submittedQuery.trim()) != null ? _a : "";
2171
3043
  const resultsQuery = useSearch({
@@ -2230,7 +3102,7 @@ function useMarketSearch(options) {
2230
3102
  }
2231
3103
 
2232
3104
  // src/use-smart-route.ts
2233
- import { keepPreviousData as keepPreviousData6, useQuery as useQuery8 } from "@tanstack/react-query";
3105
+ import { keepPreviousData as keepPreviousData6, useQuery as useQuery10 } from "@tanstack/react-query";
2234
3106
 
2235
3107
  // src/smart-route-refresh-policy.ts
2236
3108
  var THIN_SMART_ROUTE_REFETCH_INTERVAL_MS = 5e3;
@@ -2370,7 +3242,7 @@ var getIsDocumentVisible = () => {
2370
3242
  return document.visibilityState !== "hidden";
2371
3243
  };
2372
3244
  function useSmartRoute(options) {
2373
- var _a, _b, _c, _d, _e, _f;
3245
+ var _a, _b, _c, _d, _e, _f, _g;
2374
3246
  const client = useAggClient();
2375
3247
  const {
2376
3248
  venueMarketOutcomeId,
@@ -2394,7 +3266,7 @@ function useSmartRoute(options) {
2394
3266
  const resolvedVenueMarketOutcomeId = (_a = venueMarketOutcomeId != null ? venueMarketOutcomeId : venueMarketId) != null ? _a : outcomeId;
2395
3267
  const hasValidInput = (maxSpend != null ? maxSpend : 0) > 0 || (sellShares != null ? sellShares : 0) > 0;
2396
3268
  const queryEnabled = enabled && !!resolvedVenueMarketOutcomeId && hasValidInput;
2397
- const query = useQuery8({
3269
+ const query = useQuery10({
2398
3270
  queryKey: [
2399
3271
  "smart-route",
2400
3272
  resolvedVenueMarketOutcomeId,
@@ -2407,8 +3279,11 @@ function useSmartRoute(options) {
2407
3279
  slipCapBps != null ? slipCapBps : null,
2408
3280
  compareVenues != null ? compareVenues : false,
2409
3281
  deepEstimate != null ? deepEstimate : false,
2410
- ...balanceCacheScope ? [(_b = balanceCacheScope.accountId) != null ? _b : null, (_c = balanceCacheScope.balanceVersion) != null ? _c : null] : [],
2411
- ...tradeSide === "sell" ? [(_d = sellCacheScope == null ? void 0 : sellCacheScope.accountId) != null ? _d : null, (_e = sellCacheScope == null ? void 0 : sellCacheScope.positionVersion) != null ? _e : null] : []
3282
+ ...balanceCacheScope ? [
3283
+ (_b = balanceCacheScope.accountId) != null ? _b : null,
3284
+ (_d = (_c = balanceCacheScope.balanceKey) != null ? _c : balanceCacheScope.balanceVersion) != null ? _d : null
3285
+ ] : [],
3286
+ ...tradeSide === "sell" ? [(_e = sellCacheScope == null ? void 0 : sellCacheScope.accountId) != null ? _e : null, (_f = sellCacheScope == null ? void 0 : sellCacheScope.positionVersion) != null ? _f : null] : []
2412
3287
  ],
2413
3288
  queryFn: (_0) => __async(null, [_0], function* ({ signal }) {
2414
3289
  return client.getSmartRoute(
@@ -2466,7 +3341,7 @@ function useSmartRoute(options) {
2466
3341
  }
2467
3342
  }
2468
3343
  return {
2469
- data: (_f = query.data) != null ? _f : null,
3344
+ data: (_g = query.data) != null ? _g : null,
2470
3345
  isLoading: query.isLoading,
2471
3346
  isFetching: query.isFetching,
2472
3347
  error: error instanceof Error ? error : error ? new Error(String(error)) : null,
@@ -2476,7 +3351,7 @@ function useSmartRoute(options) {
2476
3351
  }
2477
3352
 
2478
3353
  // src/use-limit-order-venue-quotes.ts
2479
- import { keepPreviousData as keepPreviousData7, useQueries as useQueries6, useQuery as useQuery9 } from "@tanstack/react-query";
3354
+ import { keepPreviousData as keepPreviousData7, useQueries as useQueries4, useQuery as useQuery11 } from "@tanstack/react-query";
2480
3355
  var getIsDocumentVisible2 = () => {
2481
3356
  if (typeof document === "undefined") return true;
2482
3357
  return document.visibilityState !== "hidden";
@@ -2531,7 +3406,7 @@ var useLimitOrderVenueQuotes = (options) => {
2531
3406
  const hasRequiredAccount = tradeSide === "buy" || Boolean(accountId);
2532
3407
  const queryEnabled = enabled && hasValidInput && hasRequiredAccount;
2533
3408
  const serializedChainBalances = chainBalances ? JSON.stringify(chainBalances) : null;
2534
- const tickMetadataQuery = useQuery9({
3409
+ const tickMetadataQuery = useQuery11({
2535
3410
  queryKey: ["orderbooks", marketId != null ? marketId : "__disabled__", "limit-order-ticks"],
2536
3411
  queryFn: ({ signal }) => client.getOrderbooks({ venueMarketIds: [marketId] }, { signal }),
2537
3412
  enabled: enabled && Boolean(marketId),
@@ -2540,7 +3415,7 @@ var useLimitOrderVenueQuotes = (options) => {
2540
3415
  refetchOnWindowFocus: false,
2541
3416
  retry: 1
2542
3417
  });
2543
- const queries = useQueries6({
3418
+ const queries = useQueries4({
2544
3419
  queries: uniqueRequests.map((request) => ({
2545
3420
  queryKey: [
2546
3421
  "smart-route",
@@ -2622,7 +3497,7 @@ var useLimitOrderVenueQuotes = (options) => {
2622
3497
  };
2623
3498
 
2624
3499
  // src/use-venue-sell-quotes.ts
2625
- import { useQueries as useQueries7 } from "@tanstack/react-query";
3500
+ import { useQueries as useQueries5 } from "@tanstack/react-query";
2626
3501
  var getIsDocumentVisible3 = () => {
2627
3502
  if (typeof document === "undefined") return true;
2628
3503
  return document.visibilityState !== "hidden";
@@ -2653,7 +3528,7 @@ var useVenueSellQuotes = (options) => {
2653
3528
  refreshContext
2654
3529
  } = options;
2655
3530
  const uniqueRequests = getUniqueVenueRequests2(requests);
2656
- const queries = useQueries7({
3531
+ const queries = useQueries5({
2657
3532
  queries: uniqueRequests.map((request) => {
2658
3533
  const hasValidInput = request.requestedSellShares > 0 && request.sellShares > 0 && request.availableShares > 0;
2659
3534
  const queryEnabled = enabled && !!venueMarketOutcomeId && !!accountId && hasValidInput;
@@ -2768,13 +3643,13 @@ function useUserHoldings(options) {
2768
3643
  }
2769
3644
 
2770
3645
  // src/use-enriched-venue-event.ts
2771
- import { useMemo as useMemo10 } from "react";
3646
+ import { useMemo as useMemo11 } from "react";
2772
3647
 
2773
3648
  // src/use-venue-event.ts
2774
- import { useQuery as useQuery11 } from "@tanstack/react-query";
3649
+ import { useQuery as useQuery13 } from "@tanstack/react-query";
2775
3650
 
2776
3651
  // src/use-recurring-crypto-markets.ts
2777
- import { useQuery as useQuery10 } from "@tanstack/react-query";
3652
+ import { useQuery as useQuery12 } from "@tanstack/react-query";
2778
3653
  var RESOLVING_POLL_INTERVAL_MS = 15e3;
2779
3654
  var FALLBACK_VENUE_EVENT_PAGE_LIMIT = 100;
2780
3655
  var FALLBACK_VENUE_EVENT_MAX_PAGES = 5;
@@ -3274,7 +4149,7 @@ var useRecurringCryptoMarkets = ({
3274
4149
  const client = useAggClient();
3275
4150
  const identity = event ? resolveRecurringCryptoIdentity(event) : null;
3276
4151
  const isEnabled = enabled && event != null && identity != null;
3277
- const query = useQuery10({
4152
+ const query = useQuery12({
3278
4153
  queryKey: [
3279
4154
  "recurring-crypto-markets",
3280
4155
  event == null ? void 0 : event.id,
@@ -3336,7 +4211,7 @@ function useVenueEvent(options) {
3336
4211
  var _a;
3337
4212
  const client = useAggClient();
3338
4213
  const { eventId, enabled = true, cluster } = options;
3339
- const query = useQuery11({
4214
+ const query = useQuery13({
3340
4215
  queryKey: ["venue-event", eventId, cluster === false ? "no-cluster" : ""],
3341
4216
  queryFn: (_0) => __async(null, [_0], function* ({ signal }) {
3342
4217
  return client.getVenueEventById(eventId, { signal, cluster });
@@ -3357,7 +4232,7 @@ function useVenueEvent(options) {
3357
4232
 
3358
4233
  // src/use-venue-markets.ts
3359
4234
  import { QueryClient as QueryClient2, QueryClientContext as QueryClientContext2, useInfiniteQuery as useInfiniteQuery7 } from "@tanstack/react-query";
3360
- import { useContext as useContext3, useEffect as useEffect8, useState as useState8 } from "react";
4235
+ import { useContext as useContext3, useEffect as useEffect10, useState as useState8 } from "react";
3361
4236
  function useVenueMarkets(options) {
3362
4237
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3363
4238
  const client = useContext3(AggClientContext);
@@ -3379,7 +4254,7 @@ function useVenueMarkets(options) {
3379
4254
  const sortBy = options == null ? void 0 : options.sortBy;
3380
4255
  const sortDir = options == null ? void 0 : options.sortDir;
3381
4256
  const context = options == null ? void 0 : options.context;
3382
- useEffect8(() => {
4257
+ useEffect10(() => {
3383
4258
  if (queryClient) return void 0;
3384
4259
  fallbackQueryClient.mount();
3385
4260
  return () => {
@@ -3409,28 +4284,31 @@ function useVenueMarkets(options) {
3409
4284
  sortDir != null ? sortDir : "",
3410
4285
  context != null ? context : ""
3411
4286
  ],
3412
- queryFn: (_0) => __async(null, [_0], function* ({ pageParam }) {
4287
+ queryFn: (_0) => __async(null, [_0], function* ({ pageParam, signal }) {
3413
4288
  if (!client) {
3414
4289
  throw new Error("useVenueMarkets must be used within an <AggProvider>");
3415
4290
  }
3416
- const res = yield client.getVenueMarkets({
3417
- venue,
3418
- venueEventId,
3419
- cluster,
3420
- search,
3421
- matchStatus,
3422
- status,
3423
- aggKey,
3424
- sportsMarketType,
3425
- period,
3426
- marketCategory,
3427
- marketGroup,
3428
- limit,
3429
- cursor: pageParam,
3430
- sortBy,
3431
- sortDir,
3432
- context
3433
- });
4291
+ const res = yield client.getVenueMarkets(
4292
+ {
4293
+ venue,
4294
+ venueEventId,
4295
+ cluster,
4296
+ search,
4297
+ matchStatus,
4298
+ status,
4299
+ aggKey,
4300
+ sportsMarketType,
4301
+ period,
4302
+ marketCategory,
4303
+ marketGroup,
4304
+ limit,
4305
+ cursor: pageParam,
4306
+ sortBy,
4307
+ sortDir,
4308
+ context
4309
+ },
4310
+ { signal }
4311
+ );
3434
4312
  return res;
3435
4313
  }),
3436
4314
  initialPageParam: void 0,
@@ -3447,10 +4325,10 @@ function useVenueMarkets(options) {
3447
4325
  const hasNextPage = !!query.hasNextPage;
3448
4326
  const { fetchNextPage, isFetchingNextPage } = query;
3449
4327
  const fetchAll = (_j = options == null ? void 0 : options.fetchAll) != null ? _j : false;
3450
- useEffect8(() => {
3451
- if (!fetchAll || !hasNextPage || isFetchingNextPage) return;
4328
+ useEffect10(() => {
4329
+ if (!enabled || !client || !fetchAll || !hasNextPage || isFetchingNextPage) return;
3452
4330
  void fetchNextPage();
3453
- }, [fetchAll, hasNextPage, isFetchingNextPage, fetchNextPage]);
4331
+ }, [enabled, client, fetchAll, hasNextPage, isFetchingNextPage, fetchNextPage]);
3454
4332
  return __spreadProps(__spreadValues({}, query), {
3455
4333
  markets,
3456
4334
  hasNextPage
@@ -3473,7 +4351,7 @@ function useEnrichedVenueEvent(options) {
3473
4351
  const _a = useVenueEvent(options), { event, isLoading, error } = _a, rest = __objRest(_a, ["event", "isLoading", "error"]);
3474
4352
  const eventEndedAt = (event == null ? void 0 : event.endDate) && Number.isFinite(Date.parse(event.endDate)) ? Date.parse(event.endDate) : null;
3475
4353
  const shouldFetchOnlyOpenMarkets = eventEndedAt == null || eventEndedAt >= Date.now();
3476
- const { markets: openMarkets, isLoading: isEnrichingOpenMarkets } = useVenueMarkets({
4354
+ const openQuery = useVenueMarkets({
3477
4355
  venueEventId: options.eventId,
3478
4356
  cluster: options.cluster,
3479
4357
  enabled: ((_b = options.enabled) != null ? _b : true) && !!event,
@@ -3483,10 +4361,11 @@ function useEnrichedVenueEvent(options) {
3483
4361
  // MarketDetailsList (including limit, part of the query key) preserves the
3484
4362
  // shared React Query cache key and the event-type ordering.
3485
4363
  limit: 100,
3486
- fetchAll: true
4364
+ fetchAll: !options.progressive
3487
4365
  });
4366
+ const { markets: openMarkets, isLoading: isEnrichingOpenMarkets } = openQuery;
3488
4367
  const shouldFetchAllMarketsFallback = ((_c = options.enabled) != null ? _c : true) && !!event && shouldFetchOnlyOpenMarkets && !isEnrichingOpenMarkets && openMarkets.length === 0;
3489
- const { markets: fallbackMarkets, isLoading: isEnrichingFallbackMarkets } = useVenueMarkets({
4368
+ const fallbackQuery = useVenueMarkets({
3490
4369
  venueEventId: options.eventId,
3491
4370
  cluster: options.cluster,
3492
4371
  enabled: shouldFetchAllMarketsFallback,
@@ -3494,11 +4373,13 @@ function useEnrichedVenueEvent(options) {
3494
4373
  // closed/resolved markets, still render the event page instead of the
3495
4374
  // unavailable state.
3496
4375
  limit: 100,
3497
- fetchAll: true
4376
+ fetchAll: !options.progressive
3498
4377
  });
4378
+ const { markets: fallbackMarkets, isLoading: isEnrichingFallbackMarkets } = fallbackQuery;
4379
+ const activeQuery = shouldFetchAllMarketsFallback ? fallbackQuery : openQuery;
3499
4380
  const markets = fallbackMarkets.length > 0 ? fallbackMarkets : openMarkets;
3500
4381
  const isEnriching = isEnrichingOpenMarkets || isEnrichingFallbackMarkets;
3501
- const enrichedEvent = useMemo10(() => {
4382
+ const enrichedEvent = useMemo11(() => {
3502
4383
  if (!event) return void 0;
3503
4384
  if (markets.length === 0) return event;
3504
4385
  return mergeEventWithFullMarkets(event, markets);
@@ -3507,6 +4388,9 @@ function useEnrichedVenueEvent(options) {
3507
4388
  event: enrichedEvent,
3508
4389
  isLoading,
3509
4390
  isEnriching,
4391
+ hasMoreMarkets: !!activeQuery.hasNextPage,
4392
+ loadMoreMarkets: activeQuery.fetchNextPage,
4393
+ isLoadingMoreMarkets: !!activeQuery.isFetchingNextPage,
3510
4394
  error
3511
4395
  });
3512
4396
  }
@@ -3518,7 +4402,7 @@ import {
3518
4402
  keepPreviousData as keepPreviousData8,
3519
4403
  useInfiniteQuery as useInfiniteQuery8
3520
4404
  } from "@tanstack/react-query";
3521
- import { useContext as useContext4, useEffect as useEffect9, useMemo as useMemo11, useRef as useRef5, useState as useState9 } from "react";
4405
+ import { useContext as useContext4, useEffect as useEffect11, useMemo as useMemo12, useRef as useRef7, useState as useState9 } from "react";
3522
4406
  function useVenueEvents(options) {
3523
4407
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
3524
4408
  const client = useContext4(AggClientContext);
@@ -3540,7 +4424,7 @@ function useVenueEvents(options) {
3540
4424
  const endDateFrom = options == null ? void 0 : options.endDateFrom;
3541
4425
  const grouped = options == null ? void 0 : options.grouped;
3542
4426
  const initialPages = options == null ? void 0 : options.initialPages;
3543
- useEffect9(() => {
4427
+ useEffect11(() => {
3544
4428
  if (queryClient) return void 0;
3545
4429
  fallbackQueryClient.mount();
3546
4430
  return () => {
@@ -3568,7 +4452,7 @@ function useVenueEvents(options) {
3568
4452
  grouped != null ? grouped : false,
3569
4453
  limit
3570
4454
  ],
3571
- queryFn: (_0) => __async(null, [_0], function* ({ pageParam }) {
4455
+ queryFn: (_0) => __async(null, [_0], function* ({ pageParam, signal }) {
3572
4456
  if (!client) {
3573
4457
  throw new Error("useVenueEvents must be used within an <AggProvider>");
3574
4458
  }
@@ -3586,7 +4470,8 @@ function useVenueEvents(options) {
3586
4470
  maxYesPrice,
3587
4471
  endDateFrom,
3588
4472
  grouped,
3589
- cursor: pageParam
4473
+ cursor: pageParam,
4474
+ signal
3590
4475
  });
3591
4476
  return res;
3592
4477
  }),
@@ -3602,12 +4487,13 @@ function useVenueEvents(options) {
3602
4487
  },
3603
4488
  queryClient != null ? queryClient : fallbackQueryClient
3604
4489
  );
3605
- const prefetchedRef = useRef5(false);
3606
- useEffect9(() => {
4490
+ const prefetchedRef = useRef7(false);
4491
+ useEffect11(() => {
3607
4492
  var _a2, _b2;
4493
+ if (!enabled || !client) return;
3608
4494
  if (prefetchedRef.current) return;
3609
4495
  if (!initialPages || initialPages <= 1) return;
3610
- if (query.isLoading) return;
4496
+ if (query.isLoading || query.isFetching || query.isError || query.isPlaceholderData) return;
3611
4497
  const pagesLoaded = (_b2 = (_a2 = query.data) == null ? void 0 : _a2.pages.length) != null ? _b2 : 0;
3612
4498
  if (pagesLoaded >= initialPages || !query.hasNextPage) {
3613
4499
  prefetchedRef.current = true;
@@ -3615,13 +4501,18 @@ function useVenueEvents(options) {
3615
4501
  }
3616
4502
  void query.fetchNextPage();
3617
4503
  }, [
4504
+ enabled,
4505
+ client,
3618
4506
  initialPages,
3619
4507
  query.isLoading,
4508
+ query.isFetching,
4509
+ query.isError,
4510
+ query.isPlaceholderData,
3620
4511
  (_h = query.data) == null ? void 0 : _h.pages.length,
3621
4512
  query.hasNextPage,
3622
4513
  query.fetchNextPage
3623
4514
  ]);
3624
- const events = useMemo11(
4515
+ const events = useMemo12(
3625
4516
  () => {
3626
4517
  var _a2, _b2;
3627
4518
  return (_b2 = (_a2 = query.data) == null ? void 0 : _a2.pages.flatMap((page) => page.data)) != null ? _b2 : [];
@@ -3639,7 +4530,7 @@ function useVenueEvents(options) {
3639
4530
  }
3640
4531
 
3641
4532
  // src/use-sports-live.ts
3642
- import { useQuery as useQuery12 } from "@tanstack/react-query";
4533
+ import { useQuery as useQuery14 } from "@tanstack/react-query";
3643
4534
  var SPORTS_LIVE_POLL_INTERVAL_MS = 5e3;
3644
4535
  var SPORTS_SCHEDULED_POLL_INTERVAL_MS = 3e4;
3645
4536
  var sportsLiveQueryKey = (sortedEventIds) => ["sports-live", ...sortedEventIds];
@@ -3659,7 +4550,7 @@ var useSportsLive = (eventIds, enabled = true) => {
3659
4550
  const sortedEventIds = [
3660
4551
  ...new Set(eventIds.map((eventId) => eventId.trim()).filter(Boolean))
3661
4552
  ].sort();
3662
- const query = useQuery12({
4553
+ const query = useQuery14({
3663
4554
  queryKey: sportsLiveQueryKey(sortedEventIds),
3664
4555
  queryFn: ({ signal }) => client.getSportsLive(sortedEventIds, { signal }),
3665
4556
  enabled: enabled && sortedEventIds.length > 0,
@@ -3679,7 +4570,7 @@ var useSportsLive = (eventIds, enabled = true) => {
3679
4570
  };
3680
4571
 
3681
4572
  // src/use-venue-market-midpoints.ts
3682
- import { keepPreviousData as keepPreviousData9, useQuery as useQuery13 } from "@tanstack/react-query";
4573
+ import { keepPreviousData as keepPreviousData9, useQuery as useQuery15 } from "@tanstack/react-query";
3683
4574
  var normalizeVenueMarketIds2 = (venueMarketIds) => {
3684
4575
  return [
3685
4576
  ...new Set(venueMarketIds.map((venueMarketId) => venueMarketId.trim()).filter(Boolean))
@@ -3718,7 +4609,7 @@ function useVenueMarketMidpoints(options) {
3718
4609
  const enabled = (_a = options.enabled) != null ? _a : true;
3719
4610
  const requestedVenueMarketIds = normalizeVenueMarketIds2(options.venueMarketIds);
3720
4611
  const maxMidpointIdsPerRequest = (_b = options.maxMidpointIdsPerRequest) != null ? _b : configuredMaxMidpointIdsPerRequest;
3721
- const query = useQuery13({
4612
+ const query = useQuery15({
3722
4613
  queryKey: requestedVenueMarketIds.length ? ["venue-market-midpoints", requestedVenueMarketIds, maxMidpointIdsPerRequest] : ["venue-market-midpoints", "__disabled__"],
3723
4614
  queryFn: (_0) => __async(null, [_0], function* ({ signal }) {
3724
4615
  if (requestedVenueMarketIds.length === 0) {
@@ -3794,7 +4685,7 @@ var computePriceGaps = ({
3794
4685
  };
3795
4686
 
3796
4687
  // src/use-viewport-midpoints.ts
3797
- import { useEffect as useEffect10, useMemo as useMemo12, useRef as useRef6, useState as useState10 } from "react";
4688
+ import { useEffect as useEffect12, useMemo as useMemo13, useRef as useRef8, useState as useState10 } from "react";
3798
4689
  var buildOutcomeMidpointMap = (outcomes) => {
3799
4690
  const m = /* @__PURE__ */ new Map();
3800
4691
  if (!outcomes) return m;
@@ -3848,14 +4739,14 @@ function useViewportMidpoints(visibleMarkets, options) {
3848
4739
  } = useAggUiConfig();
3849
4740
  const maxMidpointIdsPerRequest = (_a = options == null ? void 0 : options.maxMidpointIdsPerRequest) != null ? _a : configuredMaxMidpointIdsPerRequest;
3850
4741
  const [cache, setCache] = useState10(() => /* @__PURE__ */ new Map());
3851
- const inFlightRef = useRef6(/* @__PURE__ */ new Set());
3852
- const visibleRef = useRef6(visibleMarkets);
4742
+ const inFlightRef = useRef8(/* @__PURE__ */ new Set());
4743
+ const visibleRef = useRef8(visibleMarkets);
3853
4744
  visibleRef.current = visibleMarkets;
3854
- const visibleFp = useMemo12(
4745
+ const visibleFp = useMemo13(
3855
4746
  () => [...new Set(visibleMarkets.map((m) => m.id))].sort().join("|"),
3856
4747
  [visibleMarkets]
3857
4748
  );
3858
- useEffect10(() => {
4749
+ useEffect12(() => {
3859
4750
  const toFetch = resolveUncachedVisibleMarketIds(visibleRef.current, cache, inFlightRef.current);
3860
4751
  if (!toFetch.length) return;
3861
4752
  let cancelled = false;
@@ -3879,7 +4770,7 @@ function useViewportMidpoints(visibleMarkets, options) {
3879
4770
  cancelled = true;
3880
4771
  };
3881
4772
  }, [visibleFp, cache, maxMidpointIdsPerRequest]);
3882
- const { prices, venueByOutcomeId } = useMemo12(() => {
4773
+ const { prices, venueByOutcomeId } = useMemo13(() => {
3883
4774
  var _a2, _b, _c, _d;
3884
4775
  const map = /* @__PURE__ */ new Map();
3885
4776
  const venueMap = /* @__PURE__ */ new Map();
@@ -3912,15 +4803,15 @@ function useViewportMidpoints(visibleMarkets, options) {
3912
4803
  }
3913
4804
 
3914
4805
  // src/use-visible-ids.ts
3915
- import { useCallback as useCallback7, useEffect as useEffect11, useRef as useRef7, useState as useState11 } from "react";
4806
+ import { useCallback as useCallback7, useEffect as useEffect13, useRef as useRef9, useState as useState11 } from "react";
3916
4807
  function useVisibleIds(options = {}) {
3917
4808
  const { rootMargin = "0px", threshold = 0 } = options;
3918
4809
  const [visibleIds, setVisibleIds] = useState11(() => /* @__PURE__ */ new Set());
3919
- const observerRef = useRef7(null);
3920
- const elementsRef = useRef7(/* @__PURE__ */ new Map());
3921
- const elementIdRef = useRef7(/* @__PURE__ */ new WeakMap());
3922
- const callbackRefsRef = useRef7(/* @__PURE__ */ new Map());
3923
- useEffect11(() => {
4810
+ const observerRef = useRef9(null);
4811
+ const elementsRef = useRef9(/* @__PURE__ */ new Map());
4812
+ const elementIdRef = useRef9(/* @__PURE__ */ new WeakMap());
4813
+ const callbackRefsRef = useRef9(/* @__PURE__ */ new Map());
4814
+ useEffect13(() => {
3924
4815
  if (typeof IntersectionObserver === "undefined") return;
3925
4816
  const observer = new IntersectionObserver(
3926
4817
  (entries) => {
@@ -3985,7 +4876,7 @@ function useVisibleIds(options = {}) {
3985
4876
  }
3986
4877
 
3987
4878
  // src/use-paper-onboarding.ts
3988
- import { useCallback as useCallback8, useEffect as useEffect12, useState as useState12 } from "react";
4879
+ import { useCallback as useCallback8, useEffect as useEffect14, useState as useState12 } from "react";
3989
4880
  var STORAGE_KEY = "agg_paper_onboarding_seen";
3990
4881
  var isBrowser = () => typeof window !== "undefined" && "localStorage" in window;
3991
4882
  var seenKey = (userId) => `${STORAGE_KEY}:${userId}`;
@@ -4010,14 +4901,14 @@ function usePaperOnboarding() {
4010
4901
  const userId = user == null ? void 0 : user.id;
4011
4902
  const [dismissed, setDismissed] = useState12(false);
4012
4903
  const [seen, setSeen] = useState12(null);
4013
- useEffect12(() => {
4904
+ useEffect14(() => {
4014
4905
  if (!userId) {
4015
4906
  setSeen(null);
4016
4907
  return;
4017
4908
  }
4018
4909
  setSeen(hasSeen(userId));
4019
4910
  }, [userId]);
4020
- useEffect12(() => {
4911
+ useEffect14(() => {
4021
4912
  setDismissed(false);
4022
4913
  }, [userId]);
4023
4914
  const dismiss = useCallback8(() => {
@@ -4027,6 +4918,206 @@ function usePaperOnboarding() {
4027
4918
  const isOpen = isPaperMode && !isResolving && isAuthenticated && Boolean(userId) && seen === false && !dismissed;
4028
4919
  return { isOpen, dismiss };
4029
4920
  }
4921
+
4922
+ // src/use-settlement-details-data.ts
4923
+ import { useQuery as useQuery16 } from "@tanstack/react-query";
4924
+ function useSettlementDetailsData(options) {
4925
+ const client = useAggClient();
4926
+ const { eventId, market, enabled, needsEvent, needsMarket } = options;
4927
+ const eventQuery = useQuery16({
4928
+ queryKey: ["settlement-details", "event", client.appId, eventId],
4929
+ queryFn: ({ signal }) => client.getVenueEventById(eventId, { signal }),
4930
+ enabled: enabled && needsEvent,
4931
+ staleTime: 5 * 6e4,
4932
+ gcTime: 10 * 6e4,
4933
+ retry: false
4934
+ });
4935
+ const marketQuery = useQuery16({
4936
+ queryKey: ["settlement-details", "market", client.appId, market.id],
4937
+ queryFn: (_0) => __async(null, [_0], function* ({ signal }) {
4938
+ const response = yield client.getVenueMarkets(
4939
+ { venue: market.venue, externalIdentifier: [market.externalIdentifier], limit: 1 },
4940
+ { signal }
4941
+ );
4942
+ const selected = response.data.find((candidate) => candidate.id === market.id);
4943
+ if (!selected) throw new Error("Selected market details unavailable");
4944
+ return { selected, markets: response.data };
4945
+ }),
4946
+ enabled: enabled && needsMarket,
4947
+ staleTime: 5 * 6e4,
4948
+ gcTime: 10 * 6e4,
4949
+ retry: false
4950
+ });
4951
+ return { eventQuery, marketQuery };
4952
+ }
4953
+
4954
+ // src/use-hosted-venue-transfers.ts
4955
+ import { useEffect as useEffect15, useRef as useRef10, useState as useState13 } from "react";
4956
+ import { useMutation as useMutation2, useQuery as useQuery17, useQueryClient as useQueryClient4 } from "@tanstack/react-query";
4957
+ var validRequest = (value) => {
4958
+ var _a;
4959
+ const row = value;
4960
+ return !!row && ((_a = row.request) == null ? void 0 : _a.mode) === "live" && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
4961
+ row.request.requestId
4962
+ ) && /^[1-9][0-9]{0,31}$/.test(row.request.amountRaw) && /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(row.request.destinationAddress) && (row.withdrawalId === void 0 || /^[a-zA-Z0-9_-]{1,256}$/.test(row.withdrawalId));
4963
+ };
4964
+ function useHostedVenueTransfers(accountId, environment) {
4965
+ var _a, _b, _c, _d, _e;
4966
+ const client = useAggClient();
4967
+ const { user, isAuthenticated } = useAggAuthContext();
4968
+ const { executionMode, isResolving } = useResolvedExecutionMode();
4969
+ const queryClient = useQueryClient4();
4970
+ const userId = (_a = user == null ? void 0 : user.id) != null ? _a : null;
4971
+ const permitted = isAuthenticated && !!userId && !isResolving && executionMode === "live";
4972
+ const scope = JSON.stringify([userId, accountId, environment]);
4973
+ const storageKey = "agg:betdex-withdrawal:" + scope;
4974
+ const [entry, setEntry] = useState13({
4975
+ scope: "",
4976
+ ready: false,
4977
+ journal: null,
4978
+ invalid: false
4979
+ });
4980
+ const session = useRef10({ client, scope, permitted });
4981
+ session.current = { client, scope, permitted };
4982
+ const mounted = useRef10(true);
4983
+ const dispatching = useRef10(false);
4984
+ const intent = useRef10(null);
4985
+ const current = entry.scope === scope ? entry : null;
4986
+ const journal = (_b = current == null ? void 0 : current.journal) != null ? _b : null;
4987
+ const assertSession = () => {
4988
+ if (!mounted.current || !permitted || !session.current.permitted || session.current.client !== client || session.current.scope !== scope)
4989
+ throw new Error("Hosted transfers are unavailable for this session");
4990
+ };
4991
+ const save = (next) => {
4992
+ localStorage.setItem(storageKey, JSON.stringify(next));
4993
+ if (mounted.current && session.current.scope === scope) {
4994
+ intent.current = { scope, journal: next };
4995
+ setEntry({ scope, ready: true, journal: next, invalid: false });
4996
+ }
4997
+ };
4998
+ const assertResult = (result2) => {
4999
+ if (result2.accountId !== accountId || result2.environment !== environment)
5000
+ throw new Error("Hosted transfer identity mismatch");
5001
+ };
5002
+ const invalidate = () => {
5003
+ void queryClient.invalidateQueries({
5004
+ queryKey: hostedVenueAccountQueryKeys.account("betdex", userId, "live")
5005
+ });
5006
+ invalidateBalanceQueries(queryClient);
5007
+ };
5008
+ const deposit = useMutation2({
5009
+ mutationFn: () => __async(null, null, function* () {
5010
+ assertSession();
5011
+ const result2 = yield client.getHostedVenueDepositAddress("betdex", { mode: "live" });
5012
+ assertResult(result2);
5013
+ assertSession();
5014
+ return { scope, result: result2 };
5015
+ }),
5016
+ retry: false
5017
+ });
5018
+ const withdrawal = useMutation2({
5019
+ mutationFn: (request) => __async(null, null, function* () {
5020
+ assertSession();
5021
+ if (dispatching.current) throw new Error("Withdrawal request is already pending");
5022
+ dispatching.current = true;
5023
+ try {
5024
+ save(request);
5025
+ const result2 = request.withdrawalId ? yield client.getHostedVenueWithdrawal("betdex", request.withdrawalId, { mode: "live" }) : yield client.withdrawHostedVenue("betdex", request.request);
5026
+ assertResult(result2);
5027
+ save(__spreadProps(__spreadValues({}, request), { withdrawalId: result2.id }));
5028
+ assertSession();
5029
+ invalidate();
5030
+ return { scope, result: result2 };
5031
+ } finally {
5032
+ dispatching.current = false;
5033
+ }
5034
+ }),
5035
+ retry: false
5036
+ });
5037
+ const status = useQuery17({
5038
+ queryKey: ["agg", "hosted-venue-withdrawal", scope, journal == null ? void 0 : journal.withdrawalId],
5039
+ queryFn: () => __async(null, null, function* () {
5040
+ assertSession();
5041
+ if (!(journal == null ? void 0 : journal.withdrawalId)) throw new Error("Withdrawal identity is unavailable");
5042
+ const result2 = yield client.getHostedVenueWithdrawal("betdex", journal.withdrawalId, {
5043
+ mode: "live"
5044
+ });
5045
+ assertResult(result2);
5046
+ assertSession();
5047
+ if (result2.status === "confirmed" || result2.status === "failed") invalidate();
5048
+ return result2;
5049
+ }),
5050
+ enabled: permitted && !!(journal == null ? void 0 : journal.withdrawalId),
5051
+ retry: false,
5052
+ refetchInterval: (query) => {
5053
+ var _a2;
5054
+ return ((_a2 = query.state.data) == null ? void 0 : _a2.status) === "processing" ? 2e3 : false;
5055
+ },
5056
+ refetchIntervalInBackground: false
5057
+ });
5058
+ useEffect15(() => {
5059
+ mounted.current = true;
5060
+ return () => {
5061
+ mounted.current = false;
5062
+ };
5063
+ }, []);
5064
+ const resetDeposit = deposit.reset;
5065
+ const resetWithdrawal = withdrawal.reset;
5066
+ useEffect15(() => {
5067
+ resetDeposit();
5068
+ resetWithdrawal();
5069
+ if (!permitted) return;
5070
+ try {
5071
+ const text = localStorage.getItem(storageKey);
5072
+ const value = text ? JSON.parse(text) : null;
5073
+ if (value !== null && !validRequest(value)) throw new Error("Invalid saved withdrawal");
5074
+ intent.current = value ? { scope, journal: value } : null;
5075
+ setEntry({ scope, ready: true, journal: value, invalid: false });
5076
+ } catch (e) {
5077
+ setEntry({ scope, ready: false, journal: null, invalid: true });
5078
+ }
5079
+ }, [storageKey, scope, permitted, resetDeposit, resetWithdrawal]);
5080
+ const result = (_d = status.data) != null ? _d : ((_c = withdrawal.data) == null ? void 0 : _c.scope) === scope ? withdrawal.data.result : void 0;
5081
+ const terminal = (result == null ? void 0 : result.status) === "confirmed" || (result == null ? void 0 : result.status) === "failed";
5082
+ return {
5083
+ isAvailable: permitted,
5084
+ ready: !!(current == null ? void 0 : current.ready),
5085
+ storageError: !!(current == null ? void 0 : current.invalid),
5086
+ deposit: __spreadProps(__spreadValues({}, deposit), { data: ((_e = deposit.data) == null ? void 0 : _e.scope) === scope ? deposit.data.result : void 0 }),
5087
+ withdrawal: __spreadProps(__spreadValues({}, withdrawal), { data: result, isError: withdrawal.isError || status.isError }),
5088
+ request: journal == null ? void 0 : journal.request,
5089
+ submit: (amountRaw, destinationAddress) => __async(null, null, function* () {
5090
+ var _a2;
5091
+ assertSession();
5092
+ if (!(current == null ? void 0 : current.ready) || current.invalid || journal || ((_a2 = intent.current) == null ? void 0 : _a2.scope) === scope)
5093
+ throw new Error("Resolve the existing withdrawal first");
5094
+ const request = {
5095
+ mode: "live",
5096
+ requestId: crypto.randomUUID(),
5097
+ amountRaw,
5098
+ destinationAddress
5099
+ };
5100
+ if (!validRequest({ request })) throw new Error("Invalid withdrawal request");
5101
+ save({ request });
5102
+ return withdrawal.mutateAsync({ request });
5103
+ }),
5104
+ check: () => __async(null, null, function* () {
5105
+ assertSession();
5106
+ if (!journal || !(current == null ? void 0 : current.ready)) throw new Error("No saved withdrawal");
5107
+ return withdrawal.mutateAsync(journal);
5108
+ }),
5109
+ clear: () => {
5110
+ var _a2;
5111
+ assertSession();
5112
+ if (!terminal || dispatching.current || ((_a2 = intent.current) == null ? void 0 : _a2.journal.withdrawalId) !== (result == null ? void 0 : result.id))
5113
+ throw new Error("Withdrawal is not terminal");
5114
+ localStorage.removeItem(storageKey);
5115
+ intent.current = null;
5116
+ setEntry({ scope, ready: true, journal: null, invalid: false });
5117
+ withdrawal.reset();
5118
+ }
5119
+ };
5120
+ }
4030
5121
  export {
4031
5122
  AUTH_CHOOSER_OPEN_EVENT,
4032
5123
  AggBalanceProvider,
@@ -4037,6 +5128,7 @@ export {
4037
5128
  CONFIRMED_MATCH_STATUSES,
4038
5129
  DEFAULT_AGG_ROOT_CLASS_NAME,
4039
5130
  EventListStateProvider,
5131
+ MAX_ORDER_VENUE_MARKET_IDS_PER_REQUEST,
4040
5132
  MAX_PRICE_GAP_PCT,
4041
5133
  MIN_PRICE_GAP_PCT,
4042
5134
  MarketStatus,
@@ -4067,6 +5159,7 @@ export {
4067
5159
  getVisibleVenueIdsByConfig,
4068
5160
  getVisibleVenuesByConfig,
4069
5161
  getWalletAddressFromUserProfile,
5162
+ hostedVenueAccountQueryKeys,
4070
5163
  invalidateBalanceQueries,
4071
5164
  invalidatePositionQueries,
4072
5165
  invalidateUserActivityQueries,
@@ -4092,6 +5185,7 @@ export {
4092
5185
  resolveRecurringCryptoTimeline,
4093
5186
  resolveRollingWindow,
4094
5187
  resolveTradingStateKind,
5188
+ retinaImageUrl,
4095
5189
  sharedKycQueryKeys,
4096
5190
  sortVenues,
4097
5191
  sportsLiveQueryKey,
@@ -4123,10 +5217,14 @@ export {
4123
5217
  useEventTradingContext,
4124
5218
  useExecuteManaged,
4125
5219
  useExecutionOrders,
5220
+ useExecutionOrdersForMarkets,
4126
5221
  useExecutionPositions,
4127
5222
  useExecutionProgress,
4128
5223
  useExternalId,
4129
5224
  useGeoBlock,
5225
+ useHostedVenueAccount,
5226
+ useHostedVenueActivity,
5227
+ useHostedVenueTransfers,
4130
5228
  useLabels,
4131
5229
  useLimitOrderVenueQuotes,
4132
5230
  useLinkAccount,
@@ -4141,6 +5239,7 @@ export {
4141
5239
  useManagedBalances,
4142
5240
  useMarketArb,
4143
5241
  useMarketChart,
5242
+ useMarketCharts,
4144
5243
  useMarketOrderbook,
4145
5244
  useMarketSearch,
4146
5245
  useMidpoints,
@@ -4156,7 +5255,7 @@ export {
4156
5255
  usePaperOnboarding,
4157
5256
  usePlaceLimitOrder,
4158
5257
  usePositions,
4159
- useQueryClient2 as useQueryClient,
5258
+ useQueryClient5 as useQueryClient,
4160
5259
  useRampQuotes,
4161
5260
  useRampSession,
4162
5261
  useRecurringCryptoMarkets,
@@ -4169,6 +5268,7 @@ export {
4169
5268
  useSdkLabels,
4170
5269
  useSdkUiConfig,
4171
5270
  useSearch,
5271
+ useSettlementDetailsData,
4172
5272
  useSharedKyc,
4173
5273
  useSmartRoute,
4174
5274
  useSportsLive,