@agg-build/sdk 4.17.4 → 4.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,3 +1,65 @@
1
+ import {
2
+ CandleBuilder
3
+ } from "./chunk-VD5UTAVD.mjs";
4
+ import {
5
+ ACTIVE_VENUES,
6
+ AccountProvider,
7
+ AccountType,
8
+ AggWebSocket,
9
+ CONFIRMED_MATCH_STATUSES,
10
+ Chain,
11
+ DEFAULT_BALANCE_REFILL_DAILY_CAP_RAW,
12
+ DEFAULT_BALANCE_REFILL_MAX_FEE_RAW,
13
+ IMAGE_SIZES,
14
+ INACTIVE_VENUES,
15
+ ImageSize,
16
+ LIMIT_PRICE_RAW_SCALE,
17
+ MarketStatus,
18
+ MatchStatus,
19
+ MatchType,
20
+ OrderStatus,
21
+ OrderType,
22
+ RETINA_IMAGE_SIZES,
23
+ TimeInForce,
24
+ TradeSide,
25
+ VENUES,
26
+ VENUE_CHAIN_IDS,
27
+ Venue,
28
+ adjustLimitPriceRawToTick,
29
+ applyOrderbookDelta,
30
+ buildVenueUrl,
31
+ computeBestSplitsByAmount,
32
+ computeChecksum,
33
+ enumGuard,
34
+ formatMarketQuestion,
35
+ formatOutcomeLabel,
36
+ formatOutcomeTitle,
37
+ getBalanceRefillMaximumRaw,
38
+ getEffectiveDisabledVenues,
39
+ getWalletAddressFromUserProfile,
40
+ hasShape,
41
+ isBalanceRefillWithinDailyCap,
42
+ isEmail,
43
+ isEnum,
44
+ isFiniteNonNeg,
45
+ isNonEmptyString,
46
+ isVenueActive,
47
+ isVenueDataVisible,
48
+ isVenueInactive,
49
+ loadImmutableHistory,
50
+ normalizeVenueMarketCluster,
51
+ optimizedImageUrl,
52
+ orderbookCrc32,
53
+ parse,
54
+ parseEmail,
55
+ parseEmailStrict,
56
+ projectedOrderbookChecksum,
57
+ readChartHistoryText,
58
+ retinaImageUrl,
59
+ safeParse,
60
+ snapshotToOrderbook,
61
+ sortVenues
62
+ } from "./chunk-QXBVA5TW.mjs";
1
63
  import {
2
64
  __async,
3
65
  __objRest,
@@ -5,686 +67,26 @@ import {
5
67
  __spreadValues
6
68
  } from "./chunk-WNQUEZJF.mjs";
7
69
 
8
- // ../common/src/enums/venue.ts
9
- var Venue = /* @__PURE__ */ ((Venue3) => {
10
- Venue3["kalshi"] = "kalshi";
11
- Venue3["polymarket"] = "polymarket";
12
- Venue3["limitless"] = "limitless";
13
- Venue3["opinion"] = "opinion";
14
- Venue3["predict"] = "predict";
15
- Venue3["pred"] = "pred";
16
- Venue3["probable"] = "probable";
17
- Venue3["myriad"] = "myriad";
18
- Venue3["hyperliquid"] = "hyperliquid";
19
- Venue3["novig"] = "novig";
20
- Venue3["prophetx"] = "prophetx";
21
- return Venue3;
22
- })(Venue || {});
23
- var VENUES = [
24
- "kalshi" /* kalshi */,
25
- "polymarket" /* polymarket */,
26
- "limitless" /* limitless */,
27
- "opinion" /* opinion */,
28
- "predict" /* predict */,
29
- "pred" /* pred */,
30
- "probable" /* probable */,
31
- "myriad" /* myriad */,
32
- "hyperliquid" /* hyperliquid */,
33
- "novig" /* novig */,
34
- "prophetx" /* prophetx */
35
- ];
36
- var VENUE_CHAIN_IDS = {
37
- ["kalshi" /* kalshi */]: 792703809,
38
- ["polymarket" /* polymarket */]: 137,
39
- ["limitless" /* limitless */]: 8453,
40
- ["predict" /* predict */]: 56,
41
- ["pred" /* pred */]: 8453,
42
- ["myriad" /* myriad */]: 56,
43
- ["hyperliquid" /* hyperliquid */]: 1337,
44
- ["novig" /* novig */]: 792703809,
45
- ["prophetx" /* prophetx */]: 792703809
46
- };
47
- var ACTIVE_VENUES = [
48
- "kalshi" /* kalshi */,
49
- "polymarket" /* polymarket */,
50
- "limitless" /* limitless */,
51
- "opinion" /* opinion */,
52
- "predict" /* predict */,
53
- "pred" /* pred */,
54
- "probable" /* probable */,
55
- "myriad" /* myriad */,
56
- "hyperliquid" /* hyperliquid */,
57
- "novig" /* novig */,
58
- "prophetx" /* prophetx */
59
- ];
60
- var INACTIVE_VENUES = [];
61
- var DEFAULT_DISABLED_VENUES = ["pred" /* pred */, "novig" /* novig */, "prophetx" /* prophetx */];
62
- var ACTIVE_VENUE_SET = new Set(ACTIVE_VENUES);
63
- var INACTIVE_VENUE_SET = new Set(INACTIVE_VENUES);
64
- function normalizeVenue(venue) {
65
- return venue.trim().toLowerCase();
66
- }
67
- function isVenueActive(venue) {
68
- if (ACTIVE_VENUE_SET.has(venue)) return true;
69
- if (INACTIVE_VENUE_SET.has(venue)) return false;
70
- return ACTIVE_VENUE_SET.has(normalizeVenue(venue));
71
- }
72
- function isVenueInactive(venue) {
73
- if (INACTIVE_VENUE_SET.has(venue)) return true;
74
- if (ACTIVE_VENUE_SET.has(venue)) return false;
75
- return INACTIVE_VENUE_SET.has(normalizeVenue(venue));
76
- }
77
- function isVenueDataVisible(venue, dataVisibleOverride = []) {
78
- if (isVenueActive(venue)) return true;
79
- const n = normalizeVenue(venue);
80
- return dataVisibleOverride.some((v) => normalizeVenue(v) === n);
70
+ // src/retry-after.ts
71
+ function parseRetryAfterSeconds(value) {
72
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
73
+ const milliseconds = value * 1e3;
74
+ return Number.isSafeInteger(milliseconds) ? milliseconds : void 0;
81
75
  }
82
- function getEffectiveDisabledVenues(disabledVenues, dataVisibleVenues = []) {
83
- if (disabledVenues === void 0) {
84
- const disabled2 = new Set(DEFAULT_DISABLED_VENUES);
85
- for (const venue of dataVisibleVenues) disabled2.delete(normalizeVenue(venue));
86
- return VENUES.filter((venue) => disabled2.has(venue));
87
- }
88
- const disabled = new Set(disabledVenues.map(normalizeVenue));
89
- return VENUES.filter((venue) => disabled.has(venue));
76
+ function parseRetryAfterHeader(value, now = Date.now()) {
77
+ if (!value) return void 0;
78
+ const text = value.trim();
79
+ if (/^\d+$/.test(text)) return parseRetryAfterSeconds(Number(text));
80
+ if (!/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/.test(
81
+ text
82
+ ))
83
+ return void 0;
84
+ const timestamp = Date.parse(text);
85
+ if (!Number.isFinite(timestamp) || new Date(timestamp).toUTCString() !== text) return void 0;
86
+ const milliseconds = timestamp - now;
87
+ return Number.isSafeInteger(milliseconds) && milliseconds >= 0 ? milliseconds : void 0;
90
88
  }
91
89
 
92
- // ../common/src/enums/image-size.ts
93
- var ImageSize = /* @__PURE__ */ ((ImageSize2) => {
94
- ImageSize2[ImageSize2["sm"] = 44] = "sm";
95
- ImageSize2[ImageSize2["md"] = 48] = "md";
96
- ImageSize2[ImageSize2["lg"] = 60] = "lg";
97
- ImageSize2[ImageSize2["original"] = 0] = "original";
98
- return ImageSize2;
99
- })(ImageSize || {});
100
- var IMAGE_SIZES = [44 /* sm */, 48 /* md */, 60 /* lg */];
101
-
102
- // ../common/src/enums/account.ts
103
- var AccountType = /* @__PURE__ */ ((AccountType2) => {
104
- AccountType2["oauth"] = "oauth";
105
- AccountType2["siwe"] = "siwe";
106
- AccountType2["siws"] = "siws";
107
- AccountType2["email"] = "email";
108
- return AccountType2;
109
- })(AccountType || {});
110
- var AccountProvider = /* @__PURE__ */ ((AccountProvider2) => {
111
- AccountProvider2["wallet"] = "wallet";
112
- AccountProvider2["solana_wallet"] = "solana_wallet";
113
- AccountProvider2["google"] = "google";
114
- AccountProvider2["twitter"] = "twitter";
115
- AccountProvider2["apple"] = "apple";
116
- AccountProvider2["email"] = "email";
117
- return AccountProvider2;
118
- })(AccountProvider || {});
119
-
120
- // ../common/src/enums/chain.ts
121
- var Chain = /* @__PURE__ */ ((Chain2) => {
122
- Chain2["evm"] = "evm";
123
- Chain2["svm"] = "svm";
124
- return Chain2;
125
- })(Chain || {});
126
-
127
- // ../common/src/enums/market-status.ts
128
- var MatchStatus = /* @__PURE__ */ ((MatchStatus3) => {
129
- MatchStatus3["pending"] = "pending";
130
- MatchStatus3["unmatched"] = "unmatched";
131
- MatchStatus3["review"] = "review";
132
- MatchStatus3["matched"] = "matched";
133
- MatchStatus3["verified"] = "verified";
134
- MatchStatus3["rejected"] = "rejected";
135
- return MatchStatus3;
136
- })(MatchStatus || {});
137
- var MatchType = /* @__PURE__ */ ((MatchType2) => {
138
- MatchType2["manual"] = "manual";
139
- MatchType2["llm"] = "llm";
140
- return MatchType2;
141
- })(MatchType || {});
142
- var CONFIRMED_MATCH_STATUSES = ["matched" /* matched */, "verified" /* verified */];
143
- var MarketStatus = /* @__PURE__ */ ((MarketStatus3) => {
144
- MarketStatus3["open"] = "open";
145
- MarketStatus3["closed"] = "closed";
146
- MarketStatus3["resolved"] = "resolved";
147
- MarketStatus3["unopened"] = "unopened";
148
- MarketStatus3["paused"] = "paused";
149
- return MarketStatus3;
150
- })(MarketStatus || {});
151
-
152
- // ../common/src/enums/trading.ts
153
- var OrderStatus = /* @__PURE__ */ ((OrderStatus2) => {
154
- OrderStatus2["pending"] = "pending";
155
- OrderStatus2["signing"] = "signing";
156
- OrderStatus2["pending_bridge"] = "pending_bridge";
157
- OrderStatus2["submitting"] = "submitting";
158
- OrderStatus2["submitted"] = "submitted";
159
- OrderStatus2["open"] = "open";
160
- OrderStatus2["partially_filled_open"] = "partially_filled_open";
161
- OrderStatus2["cancel_pending"] = "cancel_pending";
162
- OrderStatus2["filled"] = "filled";
163
- OrderStatus2["partial_fill"] = "partial_fill";
164
- OrderStatus2["failed"] = "failed";
165
- OrderStatus2["expired"] = "expired";
166
- OrderStatus2["cancelled"] = "cancelled";
167
- return OrderStatus2;
168
- })(OrderStatus || {});
169
- var TradeSide = /* @__PURE__ */ ((TradeSide2) => {
170
- TradeSide2["Buy"] = "buy";
171
- TradeSide2["Sell"] = "sell";
172
- return TradeSide2;
173
- })(TradeSide || {});
174
- var OrderType = /* @__PURE__ */ ((OrderType2) => {
175
- OrderType2["Market"] = "market";
176
- OrderType2["Limit"] = "limit";
177
- return OrderType2;
178
- })(OrderType || {});
179
- var TimeInForce = /* @__PURE__ */ ((TimeInForce2) => {
180
- TimeInForce2["GTC"] = "GTC";
181
- TimeInForce2["GTD"] = "GTD";
182
- TimeInForce2["FOK"] = "FOK";
183
- TimeInForce2["FAK"] = "FAK";
184
- TimeInForce2["IOC"] = "IOC";
185
- TimeInForce2["ALO"] = "ALO";
186
- return TimeInForce2;
187
- })(TimeInForce || {});
188
-
189
- // ../common/src/utils/format-market-display.ts
190
- function formatOutcomeLabel(venue, outcome, _market) {
191
- return outcome.label;
192
- }
193
- function formatMarketQuestion(venue, market) {
194
- var _a, _b, _c, _d;
195
- if ((_a = market.shortTitle) == null ? void 0 : _a.trim()) return market.shortTitle.trim();
196
- if (venue === "kalshi" /* kalshi */) {
197
- const firstTitle = (_d = (_c = (_b = market.venueMarketOutcomes) == null ? void 0 : _b[0]) == null ? void 0 : _c.title) == null ? void 0 : _d.trim();
198
- if (firstTitle) return firstTitle;
199
- }
200
- return market.question.trim();
201
- }
202
- function formatOutcomeTitle(_venue, outcome, market) {
203
- var _a, _b;
204
- const outcomes = (_a = market.venueMarketOutcomes) != null ? _a : [];
205
- if (outcomes.length >= 2) {
206
- const normalized = outcomes.map((o) => {
207
- var _a2, _b2;
208
- return (_b2 = (_a2 = o.title) == null ? void 0 : _a2.trim()) != null ? _b2 : "";
209
- });
210
- const hasAnyTitle = normalized.some((t) => t.length > 0);
211
- if (hasAnyTitle && normalized.every((t) => t === normalized[0])) return null;
212
- }
213
- const title = (_b = outcome.title) == null ? void 0 : _b.trim();
214
- if (title) return title;
215
- return deriveOverUnderTitle(outcome.label, market.question);
216
- }
217
- var OVER_UNDER_QUESTION = /\bO\/U\s+\d/i;
218
- function deriveOverUnderTitle(label, question) {
219
- if (!question || !OVER_UNDER_QUESTION.test(question)) return null;
220
- const normalized = label.trim().toLowerCase();
221
- if (normalized === "yes") return "Over";
222
- if (normalized === "no") return "Under";
223
- return null;
224
- }
225
-
226
- // ../common/src/utils/auth.ts
227
- function getWalletAddressFromUserProfile(user) {
228
- var _a, _b, _c, _d, _e;
229
- const oauthWallet = (_b = (_a = user == null ? void 0 : user.accounts) == null ? void 0 : _a.find(
230
- (a) => a.type === "oauth" /* oauth */ && String(a.provider).toLowerCase() === "wallet"
231
- )) != null ? _b : null;
232
- if (oauthWallet == null ? void 0 : oauthWallet.providerAccountId) return oauthWallet.providerAccountId;
233
- const siweAccount = (_d = (_c = user == null ? void 0 : user.accounts) == null ? void 0 : _c.find((a) => a.type === "siwe" /* siwe */)) != null ? _d : null;
234
- return (_e = siweAccount == null ? void 0 : siweAccount.providerAccountId) != null ? _e : void 0;
235
- }
236
-
237
- // ../common/src/utils/validate.ts
238
- function safeParse(value, guard) {
239
- if (guard(value)) return { success: true, data: value };
240
- return { success: false };
241
- }
242
- function parse(value, guard, message = "Validation failed") {
243
- if (guard(value)) return value;
244
- const err = new Error(message);
245
- err.issues = [{ message }];
246
- throw err;
247
- }
248
- function isEnum(values) {
249
- return (v) => typeof v === "string" && values.has(v);
250
- }
251
- function enumGuard(source) {
252
- const values = new Set(Array.isArray(source) ? source : Object.values(source));
253
- return isEnum(values);
254
- }
255
- function isFiniteNonNeg(v) {
256
- return typeof v === "number" && Number.isFinite(v) && v >= 0;
257
- }
258
- function isNonEmptyString(v) {
259
- return typeof v === "string" && v.length > 0;
260
- }
261
- function hasShape(v, checks) {
262
- if (typeof v !== "object" || v === null) return false;
263
- const obj = v;
264
- for (const [key, check] of Object.entries(checks)) {
265
- if (!(key in obj) || !check(obj[key])) return false;
266
- }
267
- return true;
268
- }
269
- var EMAIL_MSG = "Enter a valid email address.";
270
- var EMAIL_MAX_LEN = 254;
271
- function isEmail(v) {
272
- const n = v.length;
273
- if (n === 0 || n > EMAIL_MAX_LEN) return false;
274
- let atIdx = -1;
275
- for (let i = 0; i < n; i++) {
276
- const c = v.charCodeAt(i);
277
- if (c === 32 || c >= 9 && c <= 13) return false;
278
- if (c === 64) {
279
- if (atIdx !== -1) return false;
280
- atIdx = i;
281
- }
282
- }
283
- if (atIdx <= 0 || atIdx === n - 1) return false;
284
- for (let i = atIdx + 2; i < n - 1; i++) {
285
- if (v.charCodeAt(i) === 46) return true;
286
- }
287
- return false;
288
- }
289
- function parseEmail(raw) {
290
- const email = raw.trim();
291
- if (!isEmail(email)) {
292
- return { success: false, error: { issues: [{ message: EMAIL_MSG }] } };
293
- }
294
- return { success: true, data: { email } };
295
- }
296
- function parseEmailStrict(raw) {
297
- var _a;
298
- const result = parseEmail(raw);
299
- if (!result.success) {
300
- const err = new Error(EMAIL_MSG);
301
- err.issues = (_a = result.error) == null ? void 0 : _a.issues;
302
- throw err;
303
- }
304
- return result.data;
305
- }
306
-
307
- // ../common/src/utils/trading-splits.ts
308
- function computeBestSplitsByAmount(selections, side, maxDollarAmount) {
309
- var _a;
310
- if (selections.length === 0 || maxDollarAmount <= 0) {
311
- return { splits: [], totalSize: 0, totalCost: 0 };
312
- }
313
- const levels = [];
314
- for (const o of selections) {
315
- const venue = o.market.venue;
316
- const venueMarketOutcomeId = o.outcome.id;
317
- const outcomeExternalIdentifier = (_a = o.outcome.externalIdentifier) != null ? _a : void 0;
318
- if (side === "buy" /* Buy */) {
319
- [...o.orderbook.asks].sort((a, b) => a.price - b.price).forEach(
320
- (l) => {
321
- var _a2;
322
- return levels.push({
323
- venue,
324
- venueMarketOutcomeId,
325
- outcomeExternalIdentifier,
326
- price: l.price,
327
- size: l.size,
328
- negRisk: (_a2 = o.orderbook.negRisk) != null ? _a2 : false
329
- });
330
- }
331
- );
332
- } else {
333
- [...o.orderbook.bids].sort((a, b) => b.price - a.price).forEach(
334
- (l) => {
335
- var _a2;
336
- return levels.push({
337
- venue,
338
- venueMarketOutcomeId,
339
- outcomeExternalIdentifier,
340
- price: 1 - l.price,
341
- size: l.size,
342
- negRisk: (_a2 = o.orderbook.negRisk) != null ? _a2 : false
343
- });
344
- }
345
- );
346
- }
347
- }
348
- levels.sort((a, b) => a.price - b.price);
349
- let totalCost = 0;
350
- const fills = [];
351
- for (const l of levels) {
352
- if (totalCost >= maxDollarAmount) break;
353
- const remainingBudget = maxDollarAmount - totalCost;
354
- const maxSizeAtLevel = Math.min(l.size, remainingBudget / l.price);
355
- if (maxSizeAtLevel <= 0) continue;
356
- const take = maxSizeAtLevel;
357
- fills.push({
358
- venue: l.venue,
359
- venueMarketOutcomeId: l.venueMarketOutcomeId,
360
- outcomeExternalIdentifier: l.outcomeExternalIdentifier,
361
- price: l.price,
362
- size: take,
363
- negRisk: l.negRisk
364
- });
365
- totalCost += l.price * take;
366
- }
367
- if (fills.length === 0) {
368
- return { splits: [], totalSize: 0, totalCost: 0 };
369
- }
370
- const byOutcome = {};
371
- for (const f of fills) {
372
- const key = `${f.venue}:${f.venueMarketOutcomeId}`;
373
- const existing = byOutcome[key];
374
- if (!existing) {
375
- byOutcome[key] = {
376
- size: f.size,
377
- venue: f.venue,
378
- venueMarketOutcomeId: f.venueMarketOutcomeId,
379
- outcomeExternalIdentifier: f.outcomeExternalIdentifier,
380
- negRisk: f.negRisk
381
- };
382
- } else {
383
- existing.size += f.size;
384
- }
385
- }
386
- const splits = [];
387
- let totalSize = 0;
388
- for (const key in byOutcome) {
389
- const { size, venue, venueMarketOutcomeId, outcomeExternalIdentifier, negRisk } = byOutcome[key];
390
- totalSize += size;
391
- let price = 0;
392
- if (size > 0) {
393
- const outcomeFills = fills.filter(
394
- (f) => f.venue === venue && f.venueMarketOutcomeId === venueMarketOutcomeId
395
- );
396
- if (side === "buy" /* Buy */) {
397
- const rawPrice = Math.max(...outcomeFills.map((f) => f.price));
398
- price = Math.ceil(rawPrice * 100) / 100;
399
- } else {
400
- const rawPrice = Math.min(...outcomeFills.map((f) => 1 - f.price));
401
- price = Math.floor(rawPrice * 100) / 100;
402
- }
403
- }
404
- splits.push({
405
- venue,
406
- venueMarketOutcomeId,
407
- side,
408
- price,
409
- size,
410
- negRisk,
411
- outcomeExternalIdentifier
412
- });
413
- }
414
- return {
415
- splits,
416
- totalSize,
417
- totalCost
418
- };
419
- }
420
-
421
- // ../common/src/utils/image-url.ts
422
- function pickVariant(px) {
423
- if (px <= 44 /* sm */) return 44 /* sm */;
424
- if (px <= 48 /* md */) return 48 /* md */;
425
- if (px <= 60 /* lg */) return 60 /* lg */;
426
- return 0 /* original */;
427
- }
428
- var VARIANT_RE = /\/(original|raw|\d+)\.webp$/;
429
- function optimizedImageUrl(url, maxPx) {
430
- if (!url) return void 0;
431
- if (!VARIANT_RE.test(url)) return url;
432
- const variant = pickVariant(maxPx);
433
- const filename = variant === 0 /* original */ ? "original.webp" : `${variant}.webp`;
434
- return url.replace(VARIANT_RE, `/${filename}`);
435
- }
436
-
437
- // ../common/src/utils/venue-market-cluster.ts
438
- var toNonEmptyString = (value) => {
439
- if (typeof value !== "string") return void 0;
440
- const trimmedValue = value.trim();
441
- return trimmedValue ? trimmedValue : void 0;
442
- };
443
- var addGraphEdge = (adjacencyById, fromMarketId, toMarketId) => {
444
- var _a, _b;
445
- if (fromMarketId === toMarketId) return;
446
- const fromNeighbors = (_a = adjacencyById.get(fromMarketId)) != null ? _a : /* @__PURE__ */ new Set();
447
- fromNeighbors.add(toMarketId);
448
- adjacencyById.set(fromMarketId, fromNeighbors);
449
- const toNeighbors = (_b = adjacencyById.get(toMarketId)) != null ? _b : /* @__PURE__ */ new Set();
450
- toNeighbors.add(fromMarketId);
451
- adjacencyById.set(toMarketId, toNeighbors);
452
- };
453
- var collectDirectRelationMarkets = (market) => {
454
- var _a, _b, _c;
455
- const relationMarkets = [];
456
- const seenRelationIds = /* @__PURE__ */ new Set();
457
- const addRelationMarket = (relationMarket) => {
458
- if (!relationMarket) return;
459
- if (seenRelationIds.has(relationMarket.id)) return;
460
- seenRelationIds.add(relationMarket.id);
461
- relationMarkets.push(relationMarket);
462
- };
463
- for (const matchedVenueMarket of (_a = market.matchedVenueMarkets) != null ? _a : []) {
464
- addRelationMarket(matchedVenueMarket);
465
- }
466
- addRelationMarket((_c = (_b = market.matchEntry) == null ? void 0 : _b.targetVenueMarket) != null ? _c : void 0);
467
- return relationMarkets;
468
- };
469
- var dedupeSourceMarketsById = (markets) => {
470
- const seenMarketIds = /* @__PURE__ */ new Set();
471
- return markets.filter((market) => {
472
- if (seenMarketIds.has(market.id)) {
473
- return false;
474
- }
475
- seenMarketIds.add(market.id);
476
- return true;
477
- });
478
- };
479
- var collectUniqueRelatedMarkets = (markets, sourceMarketsById) => {
480
- const relatedMarketsById = /* @__PURE__ */ new Map();
481
- for (const market of markets) {
482
- for (const relatedMarket of collectDirectRelationMarkets(market)) {
483
- if (sourceMarketsById.has(relatedMarket.id) || relatedMarketsById.has(relatedMarket.id)) {
484
- continue;
485
- }
486
- relatedMarketsById.set(relatedMarket.id, relatedMarket);
487
- }
488
- }
489
- return Array.from(relatedMarketsById.values());
490
- };
491
- var normalizeVenueMarketCluster = (markets, selectedMarketId) => {
492
- var _a, _b;
493
- if (markets.length === 0) return [];
494
- const orderedSourceMarkets = dedupeSourceMarketsById(markets);
495
- const sourceMarketsById = new Map(orderedSourceMarkets.map((market) => [market.id, market]));
496
- const orderedRelatedMarkets = collectUniqueRelatedMarkets(
497
- orderedSourceMarkets,
498
- sourceMarketsById
499
- );
500
- const relatedMarketsById = new Map(orderedRelatedMarkets.map((market) => [market.id, market]));
501
- const resolvedSelectedMarketId = toNonEmptyString(selectedMarketId);
502
- if (!resolvedSelectedMarketId) {
503
- return [...orderedSourceMarkets, ...orderedRelatedMarkets];
504
- }
505
- const adjacencyById = /* @__PURE__ */ new Map();
506
- for (const market of orderedSourceMarkets) {
507
- for (const relatedMarket of collectDirectRelationMarkets(market)) {
508
- addGraphEdge(adjacencyById, market.id, relatedMarket.id);
509
- }
510
- const targetVenueMarketId = toNonEmptyString((_a = market.matchEntry) == null ? void 0 : _a.targetVenueMarketId);
511
- if (targetVenueMarketId) {
512
- addGraphEdge(adjacencyById, market.id, targetVenueMarketId);
513
- }
514
- }
515
- if (!sourceMarketsById.has(resolvedSelectedMarketId) && !relatedMarketsById.has(resolvedSelectedMarketId)) {
516
- return [];
517
- }
518
- const visitedMarketIds = /* @__PURE__ */ new Set();
519
- const queue = [resolvedSelectedMarketId];
520
- while (queue.length > 0) {
521
- const currentMarketId = queue.shift();
522
- if (!currentMarketId || visitedMarketIds.has(currentMarketId)) {
523
- continue;
524
- }
525
- visitedMarketIds.add(currentMarketId);
526
- const neighborMarketIds = adjacencyById.get(currentMarketId);
527
- for (const neighborMarketId of Array.from(neighborMarketIds != null ? neighborMarketIds : /* @__PURE__ */ new Set())) {
528
- if (!visitedMarketIds.has(neighborMarketId)) {
529
- queue.push(neighborMarketId);
530
- }
531
- }
532
- }
533
- const orderedClusterMarkets = [];
534
- const appendedMarketIds = /* @__PURE__ */ new Set();
535
- const appendMarket = (market) => {
536
- if (!market) return;
537
- if (!visitedMarketIds.has(market.id)) return;
538
- if (appendedMarketIds.has(market.id)) return;
539
- appendedMarketIds.add(market.id);
540
- orderedClusterMarkets.push(market);
541
- };
542
- appendMarket(
543
- (_b = sourceMarketsById.get(resolvedSelectedMarketId)) != null ? _b : relatedMarketsById.get(resolvedSelectedMarketId)
544
- );
545
- for (const market of orderedSourceMarkets) {
546
- appendMarket(market);
547
- }
548
- for (const market of orderedRelatedMarkets) {
549
- appendMarket(market);
550
- }
551
- return orderedClusterMarkets;
552
- };
553
-
554
- // ../common/src/utils/sort-venues.ts
555
- var VENUE_SORT_ORDER = [
556
- "polymarket" /* polymarket */,
557
- "hyperliquid" /* hyperliquid */,
558
- "predict" /* predict */,
559
- "prophetx" /* prophetx */,
560
- "kalshi" /* kalshi */,
561
- "limitless" /* limitless */,
562
- "myriad" /* myriad */,
563
- "pred" /* pred */,
564
- "probable" /* probable */,
565
- "novig" /* novig */,
566
- "opinion" /* opinion */
567
- ];
568
- var venueOrderIndex = new Map(VENUE_SORT_ORDER.map((v, i) => [v, i]));
569
- var getVenueOrder = (venue) => {
570
- var _a;
571
- return (_a = venueOrderIndex.get(venue)) != null ? _a : VENUE_SORT_ORDER.length;
572
- };
573
- var sortVenues = (venues) => [...venues].sort((a, b) => getVenueOrder(a) - getVenueOrder(b));
574
-
575
- // ../common/src/utils/venue-url.ts
576
- function buildVenueUrl(venue, opts) {
577
- switch (venue) {
578
- case "kalshi": {
579
- const series = opts.seriesExternalId;
580
- const event = opts.eventExternalId;
581
- if (series && event) {
582
- return `https://kalshi.com/markets/${series.toLowerCase()}/${event.toLowerCase()}`;
583
- }
584
- return null;
585
- }
586
- case "polymarket": {
587
- if (opts.eventSlug) return `https://polymarket.com/event/${opts.eventSlug}`;
588
- if (opts.conditionId) return `https://polymarket.com/market/${opts.conditionId}`;
589
- return null;
590
- }
591
- case "predict": {
592
- if (opts.eventSlug) return `https://predict.fun/market/${opts.eventSlug}`;
593
- return null;
594
- }
595
- case "pred": {
596
- if (opts.eventSlug) return `https://www.pred.app/trade/${opts.eventSlug}`;
597
- return null;
598
- }
599
- case "limitless": {
600
- if (opts.eventSlug) return `https://limitless.exchange/markets/${opts.eventSlug}`;
601
- return null;
602
- }
603
- case "opinion": {
604
- if (opts.eventSlug) return `https://www.opinion.trade/market/${opts.eventSlug}`;
605
- return null;
606
- }
607
- case "myriad": {
608
- if (opts.eventSlug) return `https://myriad.markets/markets/${opts.eventSlug}`;
609
- return null;
610
- }
611
- case "hyperliquid": {
612
- if (opts.eventSlug) return `https://app.hyperliquid.xyz/trade/${opts.eventSlug}`;
613
- return null;
614
- }
615
- case "probable": {
616
- if (opts.eventSlug) return `https://probable.markets/event/${opts.eventSlug}`;
617
- return null;
618
- }
619
- case "novig": {
620
- if (opts.outcomeExternalId && opts.partnerId) {
621
- return opts.platform === "native" ? `novigapp://events/${opts.outcomeExternalId}/${opts.partnerId}` : `https://novig.com/events/${opts.outcomeExternalId}/${opts.partnerId}`;
622
- }
623
- return null;
624
- }
625
- default:
626
- return null;
627
- }
628
- }
629
-
630
- // ../common/src/utils/limit-price-ticks.ts
631
- var LIMIT_PRICE_RAW_SCALE = BigInt(1e6);
632
- var resolveTickSizeRaw = (tickSize) => {
633
- const scaledTick = tickSize * Number(LIMIT_PRICE_RAW_SCALE);
634
- const roundedTick = Math.round(scaledTick);
635
- if (!Number.isFinite(scaledTick) || roundedTick <= 0 || Math.abs(scaledTick - roundedTick) > 1e-6) {
636
- throw new RangeError(`Unsupported limit-order tick size: ${tickSize}`);
637
- }
638
- return BigInt(roundedTick);
639
- };
640
- var adjustLimitPriceRawToTick = ({
641
- limitPriceRaw,
642
- side,
643
- tickSize
644
- }) => {
645
- const originalPrice = BigInt(limitPriceRaw);
646
- const originalPriceRaw = originalPrice.toString();
647
- if (tickSize == null) {
648
- return {
649
- adjusted: false,
650
- adjustedPriceRaw: originalPriceRaw,
651
- originalPriceRaw,
652
- tickSizeRaw: null
653
- };
654
- }
655
- const tickSizeRaw = resolveTickSizeRaw(tickSize);
656
- const remainder = originalPrice % tickSizeRaw;
657
- if (remainder === BigInt(0)) {
658
- return {
659
- adjusted: false,
660
- adjustedPriceRaw: originalPriceRaw,
661
- originalPriceRaw,
662
- tickSizeRaw: tickSizeRaw.toString()
663
- };
664
- }
665
- const adjustedPrice = side === "buy" ? originalPrice - remainder : originalPrice + (tickSizeRaw - remainder);
666
- return {
667
- adjusted: true,
668
- adjustedPriceRaw: adjustedPrice.toString(),
669
- originalPriceRaw,
670
- tickSizeRaw: tickSizeRaw.toString()
671
- };
672
- };
673
-
674
- // ../common/src/utils/balance-refill.ts
675
- var DEFAULT_BALANCE_REFILL_DAILY_CAP_RAW = "1002000000";
676
- var DEFAULT_BALANCE_REFILL_MAX_FEE_RAW = "2000000";
677
- var getBalanceRefillMaximumRaw = (dailyCapRaw, maxFeeRaw) => {
678
- const dailyCap = BigInt(dailyCapRaw);
679
- const maximumFee = BigInt(maxFeeRaw);
680
- if (dailyCap <= maximumFee) return "0";
681
- return (dailyCap - maximumFee).toString();
682
- };
683
- var isBalanceRefillWithinDailyCap = (minimumRaw, refillAmountRaw, dailyCapRaw, maxFeeRaw) => {
684
- const maximum = BigInt(getBalanceRefillMaximumRaw(dailyCapRaw, maxFeeRaw));
685
- return BigInt(minimumRaw) <= maximum && BigInt(refillAmountRaw) <= maximum;
686
- };
687
-
688
90
  // src/errors.ts
689
91
  var TurnstileChallengeError = class extends Error {
690
92
  constructor(siteKey) {
@@ -700,6 +102,7 @@ var AggApiError = class _AggApiError extends Error {
700
102
  this.status = status;
701
103
  if (init.code !== void 0) this.code = init.code;
702
104
  if (init.retryable !== void 0) this.retryable = init.retryable;
105
+ if (init.retryAfterMs !== void 0) this.retryAfterMs = init.retryAfterMs;
703
106
  if (init.errors !== void 0) this.errors = init.errors;
704
107
  Object.setPrototypeOf(this, _AggApiError.prototype);
705
108
  }
@@ -855,931 +258,6 @@ var RECURRENCE_CADENCES = [
855
258
  "P1Y"
856
259
  ];
857
260
 
858
- // src/orderbook-utils.ts
859
- var PRICE_KEY_SCALE = 1e9;
860
- function crc32(str) {
861
- let crc = 4294967295;
862
- for (let i = 0; i < str.length; i++) {
863
- crc ^= str.charCodeAt(i);
864
- for (let j = 0; j < 8; j++) {
865
- crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
866
- }
867
- }
868
- return (crc ^ 4294967295) >>> 0;
869
- }
870
- function computeChecksum(book) {
871
- let checksum = 0;
872
- for (const b of book.bids) checksum ^= crc32(`B${b.price}:${b.size}`);
873
- for (const a of book.asks) checksum ^= crc32(`A${a.price}:${a.size}`);
874
- return checksum >>> 0;
875
- }
876
- function attributedToLevel(wire) {
877
- return { price: wire[0], size: wire[1], venues: __spreadValues({}, wire[2]) };
878
- }
879
- function venueLevelToLevel(wire) {
880
- return { price: wire[0], size: wire[1] };
881
- }
882
- function convertVenueBooks(raw) {
883
- const result = {};
884
- for (const [venue, book] of Object.entries(raw)) {
885
- result[venue] = {
886
- bids: book.bids.map(venueLevelToLevel),
887
- asks: book.asks.map(venueLevelToLevel)
888
- };
889
- }
890
- return result;
891
- }
892
- function snapshotToOrderbook(snapshot) {
893
- return {
894
- marketId: snapshot.outcomeId,
895
- outcomeId: snapshot.outcomeId,
896
- bids: snapshot.bids.map(attributedToLevel),
897
- asks: snapshot.asks.map(attributedToLevel),
898
- venueOrderbooks: convertVenueBooks(snapshot.venueOrderbooks),
899
- venues: __spreadValues({}, snapshot.venues),
900
- midpoint: snapshot.midpoint,
901
- spread: snapshot.spread,
902
- seq: snapshot.seq,
903
- checksum: snapshot.checksum,
904
- timestamp: snapshot.timestamp
905
- };
906
- }
907
- function applyOrderbookDelta(state, delta) {
908
- var _a, _b;
909
- const bids = applyChanges([...state.bids], delta.bidChanges, "desc");
910
- const asks = applyChanges([...state.asks], delta.askChanges, "asc");
911
- const impliedBidChangesByVenue = deriveVenueChangesFromAttributedChanges(
912
- state.bids,
913
- delta.bidChanges
914
- );
915
- const impliedAskChangesByVenue = deriveVenueChangesFromAttributedChanges(
916
- state.asks,
917
- delta.askChanges
918
- );
919
- const venueOrderbooks = __spreadValues({}, state.venueOrderbooks);
920
- const venuesWithDelta = /* @__PURE__ */ new Set([
921
- ...Object.keys(delta.venueDeltaBooks),
922
- ...Object.keys(impliedBidChangesByVenue),
923
- ...Object.keys(impliedAskChangesByVenue)
924
- ]);
925
- for (const venue of venuesWithDelta) {
926
- const explicitChanges = delta.venueDeltaBooks[venue];
927
- const bidChanges = mergeVenueLevelChanges(
928
- explicitChanges == null ? void 0 : explicitChanges.bidChanges,
929
- impliedBidChangesByVenue[venue]
930
- );
931
- const askChanges = mergeVenueLevelChanges(
932
- explicitChanges == null ? void 0 : explicitChanges.askChanges,
933
- impliedAskChangesByVenue[venue]
934
- );
935
- if (bidChanges.length === 0 && askChanges.length === 0) continue;
936
- const existing = (_a = venueOrderbooks[venue]) != null ? _a : { bids: [], asks: [] };
937
- venueOrderbooks[venue] = {
938
- bids: applyVenueChanges([...existing.bids], bidChanges, "desc"),
939
- asks: applyVenueChanges([...existing.asks], askChanges, "asc")
940
- };
941
- }
942
- return {
943
- marketId: state.marketId,
944
- outcomeId: (_b = state.outcomeId) != null ? _b : delta.outcomeId,
945
- bids,
946
- asks,
947
- venueOrderbooks,
948
- venues: __spreadValues(__spreadValues({}, state.venues), delta.venues),
949
- midpoint: delta.midpoint,
950
- spread: delta.spread,
951
- seq: delta.seq,
952
- checksum: delta.checksum,
953
- timestamp: delta.timestamp
954
- };
955
- }
956
- function toPriceKey(price) {
957
- return String(Math.round(price * PRICE_KEY_SCALE));
958
- }
959
- function applyChanges(levels, changes, sort) {
960
- const levelsByPrice = new Map(levels.map((level) => [toPriceKey(level.price), level]));
961
- for (const [price, size, venues] of changes) {
962
- const key = toPriceKey(price);
963
- if (size === 0) {
964
- levelsByPrice.delete(key);
965
- continue;
966
- }
967
- levelsByPrice.set(key, {
968
- price,
969
- size,
970
- venues: __spreadValues({}, venues != null ? venues : {})
971
- });
972
- }
973
- levels = [...levelsByPrice.values()];
974
- levels.sort(sort === "desc" ? (a, b) => b.price - a.price : (a, b) => a.price - b.price);
975
- return levels;
976
- }
977
- function applyVenueChanges(levels, changes, sort) {
978
- const levelsByPrice = new Map(levels.map((level) => [toPriceKey(level.price), level]));
979
- for (const [price, size] of changes) {
980
- const key = toPriceKey(price);
981
- if (size === 0) {
982
- levelsByPrice.delete(key);
983
- continue;
984
- }
985
- levelsByPrice.set(key, { price, size });
986
- }
987
- levels = [...levelsByPrice.values()];
988
- levels.sort(sort === "desc" ? (a, b) => b.price - a.price : (a, b) => a.price - b.price);
989
- return levels;
990
- }
991
- function mergeVenueLevelChanges(primary, fallback) {
992
- if (!(primary == null ? void 0 : primary.length)) return fallback ? [...fallback] : [];
993
- if (!(fallback == null ? void 0 : fallback.length)) return [...primary];
994
- const changesByPrice = /* @__PURE__ */ new Map();
995
- for (const change of fallback) {
996
- changesByPrice.set(toPriceKey(change[0]), change);
997
- }
998
- for (const change of primary) {
999
- changesByPrice.set(toPriceKey(change[0]), change);
1000
- }
1001
- return [...changesByPrice.values()];
1002
- }
1003
- function deriveVenueChangesFromAttributedChanges(previousLevels, changes) {
1004
- var _a, _b;
1005
- const previousLevelsByPrice = new Map(
1006
- previousLevels.map((level) => [toPriceKey(level.price), level])
1007
- );
1008
- const venueChanges = {};
1009
- for (const [price, size, nextVenuesRaw] of changes) {
1010
- const key = toPriceKey(price);
1011
- const previousLevel = previousLevelsByPrice.get(key);
1012
- const previousVenues = (_a = previousLevel == null ? void 0 : previousLevel.venues) != null ? _a : {};
1013
- const nextVenues = nextVenuesRaw != null ? nextVenuesRaw : {};
1014
- const affectedVenues = /* @__PURE__ */ new Set([...Object.keys(previousVenues), ...Object.keys(nextVenues)]);
1015
- for (const venue of affectedVenues) {
1016
- const nextSize = Number((_b = nextVenues[venue]) != null ? _b : 0);
1017
- if (!Number.isFinite(nextSize)) continue;
1018
- if (!venueChanges[venue]) {
1019
- venueChanges[venue] = [];
1020
- }
1021
- venueChanges[venue].push([price, nextSize]);
1022
- }
1023
- if (size === 0) {
1024
- previousLevelsByPrice.delete(key);
1025
- continue;
1026
- }
1027
- previousLevelsByPrice.set(key, {
1028
- price,
1029
- size,
1030
- venues: __spreadValues({}, nextVenues)
1031
- });
1032
- }
1033
- return venueChanges;
1034
- }
1035
-
1036
- // src/agg-websocket.ts
1037
- var RECONNECT_BASE_MS = 1e3;
1038
- var RECONNECT_MAX_MS = 3e4;
1039
- var STALE_TIMEOUT_MS = 45e3;
1040
- var ORDERBOOK_DEBUG_TOP_LEVELS = 5;
1041
- var AggWebSocket = class {
1042
- constructor(options) {
1043
- this.ws = null;
1044
- // ── Subscription tracking ──
1045
- this.subs = /* @__PURE__ */ new Map();
1046
- this.pendingSubs = /* @__PURE__ */ new Set();
1047
- this.pendingTradeSubs = /* @__PURE__ */ new Set();
1048
- this.pendingResnapshots = /* @__PURE__ */ new Set();
1049
- // ── Post-connect batching (coalesces same-tick sub/unsub calls into one frame) ──
1050
- this.pendingFlushSubs = /* @__PURE__ */ new Set();
1051
- this.pendingFlushUnsubs = /* @__PURE__ */ new Set();
1052
- this.pendingFlushTradeSubs = /* @__PURE__ */ new Set();
1053
- this.pendingFlushTradeUnsubs = /* @__PURE__ */ new Set();
1054
- this.pendingFlushArbSubs = /* @__PURE__ */ new Set();
1055
- this.pendingFlushArbUnsubs = /* @__PURE__ */ new Set();
1056
- this.flushScheduled = false;
1057
- // ── Arb subscriptions ──
1058
- this.arbSubs = /* @__PURE__ */ new Map();
1059
- this.arbFeedCbs = /* @__PURE__ */ new Set();
1060
- /** Latest arb market update per marketId (last-value-wins, loss-tolerant). */
1061
- this.latestArb = /* @__PURE__ */ new Map();
1062
- // ── Orderbook state per market ──
1063
- this.books = /* @__PURE__ */ new Map();
1064
- /** Markets currently resyncing (awaiting snapshot after seq gap / checksum mismatch). */
1065
- this.resyncing = /* @__PURE__ */ new Set();
1066
- // ── Connection state ──
1067
- this.connecting = false;
1068
- this._connected = false;
1069
- this.reconnectAttempts = 0;
1070
- this.reconnectTimer = null;
1071
- this.staleTimer = null;
1072
- this.lastMessageTime = 0;
1073
- this.destroyed = false;
1074
- var _a, _b;
1075
- this.url = options.url;
1076
- this.callbacks = (_a = options.callbacks) != null ? _a : {};
1077
- this.initialToken = options.initialToken;
1078
- this.enableLogs = (_b = options.enableLogs) != null ? _b : false;
1079
- }
1080
- // ── Public API ──
1081
- get connected() {
1082
- return this._connected;
1083
- }
1084
- /** Get the current orderbook state for an outcome (or null if no snapshot received yet). */
1085
- getBook(outcomeId) {
1086
- var _a;
1087
- return (_a = this.books.get(outcomeId)) != null ? _a : null;
1088
- }
1089
- /** Whether an outcome is currently resyncing (waiting for fresh snapshot). */
1090
- isResyncing(outcomeId) {
1091
- return this.resyncing.has(outcomeId);
1092
- }
1093
- /** Get the latest arb update for a market (or null if none received yet). */
1094
- getArb(marketId) {
1095
- var _a;
1096
- return (_a = this.latestArb.get(marketId)) != null ? _a : null;
1097
- }
1098
- /**
1099
- * Subscribe to per-market arb updates for a given marketId.
1100
- * Ref-counted: multiple callers for the same marketId share one server subscription.
1101
- * Returns an unsubscribe function.
1102
- */
1103
- subscribeArb(marketId, cb) {
1104
- let entry = this.arbSubs.get(marketId);
1105
- if (!entry) {
1106
- entry = { refCount: 0, cbs: /* @__PURE__ */ new Set() };
1107
- this.arbSubs.set(marketId, entry);
1108
- this.ensureConnected();
1109
- if (this._connected) {
1110
- this.pendingFlushArbUnsubs.delete(marketId);
1111
- this.pendingFlushArbSubs.add(marketId);
1112
- this.scheduleFlush();
1113
- }
1114
- }
1115
- entry.cbs.add(cb);
1116
- entry.refCount++;
1117
- return () => {
1118
- const e = this.arbSubs.get(marketId);
1119
- if (!e) return;
1120
- e.cbs.delete(cb);
1121
- e.refCount--;
1122
- if (e.refCount <= 0) {
1123
- this.arbSubs.delete(marketId);
1124
- if (this._connected) {
1125
- if (this.pendingFlushArbSubs.has(marketId)) {
1126
- this.pendingFlushArbSubs.delete(marketId);
1127
- } else {
1128
- this.pendingFlushArbUnsubs.add(marketId);
1129
- this.scheduleFlush();
1130
- }
1131
- }
1132
- }
1133
- };
1134
- }
1135
- /**
1136
- * Subscribe to the global arb-feed batches.
1137
- * Only one server subscription is maintained regardless of how many callers.
1138
- * Returns an unsubscribe function.
1139
- */
1140
- subscribeArbFeed(cb) {
1141
- const wasEmpty = this.arbFeedCbs.size === 0;
1142
- this.arbFeedCbs.add(cb);
1143
- if (wasEmpty) {
1144
- this.ensureConnected();
1145
- if (this._connected) {
1146
- this.send({ action: "subscribe", channel: "arb-feed" });
1147
- }
1148
- }
1149
- return () => {
1150
- this.arbFeedCbs.delete(cb);
1151
- if (this.arbFeedCbs.size === 0 && this._connected) {
1152
- this.send({ action: "unsubscribe", channel: "arb-feed" });
1153
- }
1154
- };
1155
- }
1156
- /**
1157
- * Subscribe to a feed by outcome ID.
1158
- * @param outcomeId The venueMarketOutcomeId to subscribe to.
1159
- * @param channel "orderbook" (default) or "trades".
1160
- * Returns an unsubscribe function (ref-counted).
1161
- */
1162
- subscribe(outcomeId, channel = "orderbook") {
1163
- if (channel === "trades") {
1164
- return this._subscribeTrades(outcomeId);
1165
- }
1166
- let sub = this.subs.get(outcomeId);
1167
- if (!sub) {
1168
- sub = { refCount: 0, tradeRefs: 0 };
1169
- this.subs.set(outcomeId, sub);
1170
- this.pendingSubs.add(outcomeId);
1171
- this.ensureConnected();
1172
- if (this._connected) {
1173
- this.pendingFlushUnsubs.delete(outcomeId);
1174
- this.pendingFlushSubs.add(outcomeId);
1175
- this.pendingSubs.delete(outcomeId);
1176
- this.scheduleFlush();
1177
- }
1178
- }
1179
- sub.refCount++;
1180
- return () => {
1181
- const s = this.subs.get(outcomeId);
1182
- if (!s) return;
1183
- s.refCount--;
1184
- if (s.refCount <= 0 && s.tradeRefs <= 0) {
1185
- this.subs.delete(outcomeId);
1186
- this.books.delete(outcomeId);
1187
- this.resyncing.delete(outcomeId);
1188
- if (this._connected) {
1189
- if (this.pendingFlushSubs.has(outcomeId)) {
1190
- this.pendingFlushSubs.delete(outcomeId);
1191
- } else {
1192
- this.pendingFlushUnsubs.add(outcomeId);
1193
- this.scheduleFlush();
1194
- }
1195
- }
1196
- this.pendingSubs.delete(outcomeId);
1197
- this.pendingResnapshots.delete(outcomeId);
1198
- }
1199
- };
1200
- }
1201
- /**
1202
- * Subscribe to an outcome's trade feed.
1203
- * @deprecated Use `subscribe(outcomeId, "trades")` instead.
1204
- * Returns an unsubscribe function.
1205
- */
1206
- subscribeTrades(outcomeId) {
1207
- return this._subscribeTrades(outcomeId);
1208
- }
1209
- _subscribeTrades(outcomeId) {
1210
- let sub = this.subs.get(outcomeId);
1211
- if (!sub) {
1212
- sub = { refCount: 0, tradeRefs: 0 };
1213
- this.subs.set(outcomeId, sub);
1214
- }
1215
- if (sub.tradeRefs === 0) {
1216
- this.pendingTradeSubs.add(outcomeId);
1217
- this.ensureConnected();
1218
- if (this._connected) {
1219
- this.pendingFlushTradeUnsubs.delete(outcomeId);
1220
- this.pendingFlushTradeSubs.add(outcomeId);
1221
- this.pendingTradeSubs.delete(outcomeId);
1222
- this.scheduleFlush();
1223
- }
1224
- }
1225
- sub.tradeRefs++;
1226
- return () => {
1227
- const s = this.subs.get(outcomeId);
1228
- if (!s) return;
1229
- s.tradeRefs--;
1230
- if (s.tradeRefs <= 0) {
1231
- s.tradeRefs = 0;
1232
- if (this._connected) {
1233
- if (this.pendingFlushTradeSubs.has(outcomeId)) {
1234
- this.pendingFlushTradeSubs.delete(outcomeId);
1235
- } else {
1236
- this.pendingFlushTradeUnsubs.add(outcomeId);
1237
- this.scheduleFlush();
1238
- }
1239
- }
1240
- this.pendingTradeSubs.delete(outcomeId);
1241
- this.pendingResnapshots.delete(outcomeId);
1242
- if (s.refCount <= 0) {
1243
- this.subs.delete(outcomeId);
1244
- }
1245
- }
1246
- };
1247
- }
1248
- /** Send a JWT to authenticate (or re-authenticate) the connection. */
1249
- authenticate(token) {
1250
- if (this._connected) {
1251
- this.send({ action: "authenticate", token });
1252
- }
1253
- }
1254
- /** Request a fresh snapshot for one or more subscribed outcomes. */
1255
- resnapshot(outcomeIdOrIds) {
1256
- const outcomeIds = Array.isArray(outcomeIdOrIds) ? outcomeIdOrIds : [outcomeIdOrIds];
1257
- outcomeIds.forEach((outcomeId) => {
1258
- this.requestResnapshot(outcomeId, true);
1259
- });
1260
- }
1261
- /** Connect (idempotent). Called automatically on first subscribe. */
1262
- connect() {
1263
- this.ensureConnected();
1264
- }
1265
- /** Disconnect and clean up. Reusable — call connect() to reconnect. */
1266
- disconnect() {
1267
- var _a, _b;
1268
- this.cancelReconnect();
1269
- this.cancelStaleTimer();
1270
- this._connected = false;
1271
- this.connecting = false;
1272
- if (this.ws) {
1273
- this.ws.onclose = null;
1274
- this.ws.close();
1275
- this.ws = null;
1276
- }
1277
- (_b = (_a = this.callbacks).onConnectionStateChange) == null ? void 0 : _b.call(_a, false);
1278
- }
1279
- /** Permanently destroy. Cannot reconnect after this. */
1280
- destroy() {
1281
- this.destroyed = true;
1282
- this.disconnect();
1283
- this.subs.clear();
1284
- this.books.clear();
1285
- this.resyncing.clear();
1286
- this.pendingSubs.clear();
1287
- this.pendingTradeSubs.clear();
1288
- this.pendingResnapshots.clear();
1289
- this.pendingFlushSubs.clear();
1290
- this.pendingFlushUnsubs.clear();
1291
- this.pendingFlushTradeSubs.clear();
1292
- this.pendingFlushTradeUnsubs.clear();
1293
- this.arbSubs.clear();
1294
- this.arbFeedCbs.clear();
1295
- this.latestArb.clear();
1296
- this.pendingFlushArbSubs.clear();
1297
- this.pendingFlushArbUnsubs.clear();
1298
- this.flushScheduled = false;
1299
- }
1300
- /** Update callbacks (e.g., after React re-render). */
1301
- setCallbacks(callbacks) {
1302
- this.callbacks = callbacks;
1303
- }
1304
- // ── Connection management ──
1305
- ensureConnected() {
1306
- var _a;
1307
- if (this.destroyed) return;
1308
- if (((_a = this.ws) == null ? void 0 : _a.readyState) === WebSocket.OPEN || this.connecting) return;
1309
- this.doConnect();
1310
- }
1311
- doConnect() {
1312
- if (this.destroyed) return;
1313
- this.connecting = true;
1314
- const ws = new WebSocket(this.url);
1315
- this.ws = ws;
1316
- ws.onopen = () => {
1317
- this.connecting = false;
1318
- this.reconnectAttempts = 0;
1319
- this.resetStaleTimer();
1320
- if (this.initialToken) {
1321
- this.send({ action: "authenticate", token: this.initialToken });
1322
- }
1323
- };
1324
- ws.onmessage = (event) => {
1325
- this.lastMessageTime = Date.now();
1326
- this.resetStaleTimer();
1327
- try {
1328
- const raw = JSON.parse(event.data);
1329
- this.handleMessage(raw);
1330
- } catch (e) {
1331
- }
1332
- };
1333
- ws.onclose = () => {
1334
- var _a, _b;
1335
- this.ws = null;
1336
- this.connecting = false;
1337
- const wasConnected = this._connected;
1338
- this._connected = false;
1339
- this.cancelStaleTimer();
1340
- if (wasConnected) {
1341
- (_b = (_a = this.callbacks).onConnectionStateChange) == null ? void 0 : _b.call(_a, false);
1342
- }
1343
- if (!this.destroyed && (this.subs.size > 0 || this.arbSubs.size > 0 || this.arbFeedCbs.size > 0)) {
1344
- this.scheduleReconnect();
1345
- }
1346
- };
1347
- ws.onerror = () => {
1348
- };
1349
- }
1350
- scheduleReconnect() {
1351
- this.cancelReconnect();
1352
- const delay = Math.min(
1353
- RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempts),
1354
- RECONNECT_MAX_MS
1355
- );
1356
- this.reconnectAttempts++;
1357
- this.emitDiagnostic({ type: "reconnect", attempt: this.reconnectAttempts, delayMs: delay });
1358
- this.reconnectTimer = setTimeout(() => {
1359
- this.reconnectTimer = null;
1360
- this.doConnect();
1361
- }, delay);
1362
- }
1363
- cancelReconnect() {
1364
- if (this.reconnectTimer) {
1365
- clearTimeout(this.reconnectTimer);
1366
- this.reconnectTimer = null;
1367
- }
1368
- }
1369
- // ── Staleness detection ──
1370
- resetStaleTimer() {
1371
- this.cancelStaleTimer();
1372
- this.staleTimer = setTimeout(() => {
1373
- const age = Date.now() - this.lastMessageTime;
1374
- this.emitDiagnostic({ type: "stale_connection", lastMessageAge: age });
1375
- if (this.ws) {
1376
- this.ws.close();
1377
- }
1378
- }, STALE_TIMEOUT_MS);
1379
- }
1380
- cancelStaleTimer() {
1381
- if (this.staleTimer) {
1382
- clearTimeout(this.staleTimer);
1383
- this.staleTimer = null;
1384
- }
1385
- }
1386
- // ── Message handling ──
1387
- handleMessage(raw) {
1388
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
1389
- const type = raw.type;
1390
- switch (type) {
1391
- case "orderbook_snapshot":
1392
- this.handleSnapshot(raw);
1393
- break;
1394
- case "orderbook_delta":
1395
- this.handleDelta(raw);
1396
- break;
1397
- case "trade":
1398
- this.handleTrade(raw);
1399
- break;
1400
- case "heartbeat":
1401
- this.handleHeartbeat(raw);
1402
- break;
1403
- case "subscribed":
1404
- (_b = (_a = this.callbacks).onSubscribed) == null ? void 0 : _b.call(_a, raw);
1405
- break;
1406
- case "unsubscribed":
1407
- (_d = (_c = this.callbacks).onUnsubscribed) == null ? void 0 : _d.call(_c, raw);
1408
- break;
1409
- case "connected":
1410
- this.handleConnected(raw);
1411
- break;
1412
- case "authenticated":
1413
- (_f = (_e = this.callbacks).onAuthenticated) == null ? void 0 : _f.call(_e, raw);
1414
- break;
1415
- case "market_resolved":
1416
- this.handleMarketResolved(raw);
1417
- break;
1418
- case "error":
1419
- (_h = (_g = this.callbacks).onError) == null ? void 0 : _h.call(_g, raw);
1420
- break;
1421
- case "order_submitted":
1422
- this.handleOrderSubmitted(raw);
1423
- break;
1424
- case "balance_update":
1425
- this.handleBalanceUpdate(raw);
1426
- break;
1427
- case "order_event":
1428
- (_j = (_i = this.callbacks).onOrderEvent) == null ? void 0 : _j.call(_i, raw);
1429
- break;
1430
- case "redeem_event":
1431
- this.handleRedeemEvent(raw);
1432
- break;
1433
- case "withdrawal_lifecycle":
1434
- (_l = (_k = this.callbacks).onWithdrawalLifecycle) == null ? void 0 : _l.call(_k, raw);
1435
- break;
1436
- case "arb_market_update": {
1437
- const arbMsg = raw;
1438
- this.latestArb.set(arbMsg.marketId, arbMsg);
1439
- const arbEntry = this.arbSubs.get(arbMsg.marketId);
1440
- if (arbEntry) {
1441
- for (const cb of arbEntry.cbs) cb(arbMsg);
1442
- }
1443
- (_n = (_m = this.callbacks).onArbMarket) == null ? void 0 : _n.call(_m, arbMsg);
1444
- break;
1445
- }
1446
- case "arb_feed_batch": {
1447
- const feedMsg = raw;
1448
- for (const cb of this.arbFeedCbs) cb(feedMsg);
1449
- (_p = (_o = this.callbacks).onArbFeed) == null ? void 0 : _p.call(_o, feedMsg);
1450
- break;
1451
- }
1452
- }
1453
- }
1454
- /**
1455
- * Server confirmed the connection — gateway has validated our appId and is
1456
- * ready to accept subscriptions. Now flush all pending subs.
1457
- */
1458
- handleConnected(msg) {
1459
- var _a, _b, _c, _d;
1460
- this._connected = true;
1461
- (_b = (_a = this.callbacks).onConnectionStateChange) == null ? void 0 : _b.call(_a, true);
1462
- (_d = (_c = this.callbacks).onConnected) == null ? void 0 : _d.call(_c, msg);
1463
- const orderbookSet = new Set(this.activeOrderbookMarkets());
1464
- this.pendingSubs.forEach((id) => orderbookSet.add(id));
1465
- this.pendingFlushSubs.forEach((id) => orderbookSet.add(id));
1466
- const orderbookIds = Array.from(orderbookSet);
1467
- if (orderbookIds.length > 0) {
1468
- this.sendSubscribe(orderbookIds);
1469
- }
1470
- this.pendingSubs.clear();
1471
- this.pendingFlushSubs.clear();
1472
- this.pendingFlushUnsubs.clear();
1473
- const tradeSet = new Set(this.activeTradeMarkets());
1474
- this.pendingTradeSubs.forEach((id) => tradeSet.add(id));
1475
- this.pendingFlushTradeSubs.forEach((id) => tradeSet.add(id));
1476
- const tradeIds = Array.from(tradeSet);
1477
- if (tradeIds.length > 0) {
1478
- this.send({ action: "subscribe", channel: "trades", outcomeIds: tradeIds });
1479
- }
1480
- this.pendingTradeSubs.clear();
1481
- this.pendingFlushTradeSubs.clear();
1482
- this.pendingFlushTradeUnsubs.clear();
1483
- const resnapshotIds = Array.from(this.pendingResnapshots);
1484
- if (resnapshotIds.length > 0) {
1485
- this.send({ action: "resnapshot", channel: "orderbook", outcomeIds: resnapshotIds });
1486
- }
1487
- this.pendingResnapshots.clear();
1488
- const arbSet = new Set(this.activeArbMarkets());
1489
- this.pendingFlushArbSubs.forEach((id) => arbSet.add(id));
1490
- const arbIds = Array.from(arbSet);
1491
- if (arbIds.length > 0) {
1492
- this.send({ action: "subscribe", channel: "arb", marketIds: arbIds });
1493
- }
1494
- this.pendingFlushArbSubs.clear();
1495
- this.pendingFlushArbUnsubs.clear();
1496
- if (this.arbFeedCbs.size > 0) {
1497
- this.send({ action: "subscribe", channel: "arb-feed" });
1498
- }
1499
- }
1500
- handleSnapshot(raw) {
1501
- var _a, _b;
1502
- const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1503
- const book = snapshotToOrderbook(msg);
1504
- this.books.set(msg.outcomeId, book);
1505
- this.resyncing.delete(msg.outcomeId);
1506
- this.pendingResnapshots.delete(msg.outcomeId);
1507
- this.debugOrderbook("snapshot_applied", {
1508
- outcomeId: msg.outcomeId,
1509
- seq: msg.seq,
1510
- checksum: msg.checksum,
1511
- bidsTop: this.summarizeTopLevels(book.bids),
1512
- asksTop: this.summarizeTopLevels(book.asks)
1513
- });
1514
- (_b = (_a = this.callbacks).onSnapshot) == null ? void 0 : _b.call(_a, msg.outcomeId, book);
1515
- }
1516
- handleDelta(raw) {
1517
- var _a, _b;
1518
- const outcomeId = raw.outcomeId;
1519
- const current = this.books.get(outcomeId);
1520
- const deltaTop = {
1521
- bids: raw.bidChanges.slice(0, ORDERBOOK_DEBUG_TOP_LEVELS).map(([price, size]) => ({
1522
- price,
1523
- size
1524
- })),
1525
- asks: raw.askChanges.slice(0, ORDERBOOK_DEBUG_TOP_LEVELS).map(([price, size]) => ({
1526
- price,
1527
- size
1528
- }))
1529
- };
1530
- this.debugOrderbook("delta_received", {
1531
- outcomeId,
1532
- seq: raw.seq,
1533
- checksum: raw.checksum,
1534
- hasCurrentBook: Boolean(current),
1535
- deltaTop
1536
- });
1537
- if (!current) {
1538
- this.debugOrderbook("delta_missing_snapshot", { outcomeId, seq: raw.seq });
1539
- this.requestResnapshot(outcomeId);
1540
- return;
1541
- }
1542
- if (raw.seq <= current.seq) {
1543
- this.debugOrderbook("delta_stale_ignored", {
1544
- outcomeId,
1545
- currentSeq: current.seq,
1546
- receivedSeq: raw.seq
1547
- });
1548
- return;
1549
- }
1550
- if (raw.seq > current.seq + 1) {
1551
- this.debugOrderbook("delta_seq_gap", {
1552
- outcomeId,
1553
- expectedSeq: current.seq + 1,
1554
- receivedSeq: raw.seq
1555
- });
1556
- this.emitDiagnostic({
1557
- type: "seq_gap",
1558
- marketId: outcomeId,
1559
- outcomeId,
1560
- expected: current.seq + 1,
1561
- received: raw.seq
1562
- });
1563
- this.requestResnapshot(outcomeId);
1564
- return;
1565
- }
1566
- const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1567
- const newBook = applyOrderbookDelta(current, msg);
1568
- this.debugOrderbook("delta_applied_locally", {
1569
- outcomeId,
1570
- fromSeq: current.seq,
1571
- toSeq: newBook.seq,
1572
- beforeTop: {
1573
- bids: this.summarizeTopLevels(current.bids),
1574
- asks: this.summarizeTopLevels(current.asks)
1575
- },
1576
- afterTop: {
1577
- bids: this.summarizeTopLevels(newBook.bids),
1578
- asks: this.summarizeTopLevels(newBook.asks)
1579
- }
1580
- });
1581
- const computed = computeChecksum(newBook);
1582
- if (computed !== msg.checksum) {
1583
- this.debugOrderbook("delta_checksum_mismatch", {
1584
- outcomeId,
1585
- seq: msg.seq,
1586
- expected: msg.checksum,
1587
- computed
1588
- });
1589
- this.emitDiagnostic({
1590
- type: "checksum_mismatch",
1591
- marketId: outcomeId,
1592
- outcomeId,
1593
- expected: msg.checksum,
1594
- computed
1595
- });
1596
- this.requestResnapshot(outcomeId);
1597
- return;
1598
- }
1599
- this.books.set(outcomeId, newBook);
1600
- this.debugOrderbook("delta_committed", {
1601
- outcomeId,
1602
- seq: newBook.seq,
1603
- checksum: newBook.checksum,
1604
- bidsTop: this.summarizeTopLevels(newBook.bids),
1605
- asksTop: this.summarizeTopLevels(newBook.asks)
1606
- });
1607
- (_b = (_a = this.callbacks).onDelta) == null ? void 0 : _b.call(_a, outcomeId, newBook);
1608
- }
1609
- handleTrade(raw) {
1610
- var _a, _b;
1611
- const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1612
- (_b = (_a = this.callbacks).onTrade) == null ? void 0 : _b.call(_a, msg);
1613
- }
1614
- handleHeartbeat(raw) {
1615
- var _a, _b;
1616
- const msg = __spreadProps(__spreadValues({}, raw), { ts: raw.ts / 1e3 });
1617
- (_b = (_a = this.callbacks).onHeartbeat) == null ? void 0 : _b.call(_a, msg);
1618
- }
1619
- handleMarketResolved(raw) {
1620
- var _a, _b;
1621
- const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1622
- this.books.delete(msg.outcomeId);
1623
- this.pendingResnapshots.delete(msg.outcomeId);
1624
- this.resyncing.delete(msg.outcomeId);
1625
- (_b = (_a = this.callbacks).onMarketResolved) == null ? void 0 : _b.call(_a, msg);
1626
- }
1627
- handleOrderSubmitted(raw) {
1628
- var _a, _b;
1629
- const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1630
- (_b = (_a = this.callbacks).onOrderSubmitted) == null ? void 0 : _b.call(_a, msg);
1631
- }
1632
- handleBalanceUpdate(raw) {
1633
- var _a, _b;
1634
- const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1635
- (_b = (_a = this.callbacks).onBalanceUpdate) == null ? void 0 : _b.call(_a, msg);
1636
- }
1637
- handleRedeemEvent(raw) {
1638
- var _a, _b;
1639
- const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1640
- (_b = (_a = this.callbacks).onRedeemEvent) == null ? void 0 : _b.call(_a, msg);
1641
- }
1642
- // ── Resnapshot ──
1643
- requestResnapshot(outcomeId, force = false) {
1644
- if (this.resyncing.has(outcomeId) && !force) return;
1645
- this.resyncing.add(outcomeId);
1646
- if (this._connected) {
1647
- this.send({ action: "resnapshot", channel: "orderbook", outcomeIds: [outcomeId] });
1648
- this.pendingResnapshots.delete(outcomeId);
1649
- return;
1650
- }
1651
- this.pendingResnapshots.add(outcomeId);
1652
- this.ensureConnected();
1653
- }
1654
- // ── Helpers ──
1655
- /**
1656
- * Schedule a microtask that flushes queued subscribe/unsubscribe frames.
1657
- * Coalesces multiple same-tick sub/unsub calls into at most one frame per
1658
- * channel+action pair.
1659
- */
1660
- scheduleFlush() {
1661
- if (this.flushScheduled) return;
1662
- this.flushScheduled = true;
1663
- queueMicrotask(() => {
1664
- this.flushScheduled = false;
1665
- if (!this._connected) {
1666
- this.pendingFlushSubs.clear();
1667
- this.pendingFlushUnsubs.clear();
1668
- this.pendingFlushTradeSubs.clear();
1669
- this.pendingFlushTradeUnsubs.clear();
1670
- this.pendingFlushArbSubs.clear();
1671
- this.pendingFlushArbUnsubs.clear();
1672
- return;
1673
- }
1674
- this.flushPending();
1675
- });
1676
- }
1677
- flushPending() {
1678
- for (const id of [...this.pendingFlushSubs]) {
1679
- if (this.pendingFlushUnsubs.has(id)) {
1680
- this.pendingFlushSubs.delete(id);
1681
- this.pendingFlushUnsubs.delete(id);
1682
- }
1683
- }
1684
- if (this.pendingFlushSubs.size > 0) {
1685
- this.sendSubscribe(Array.from(this.pendingFlushSubs));
1686
- this.pendingFlushSubs.clear();
1687
- }
1688
- if (this.pendingFlushUnsubs.size > 0) {
1689
- this.send({
1690
- action: "unsubscribe",
1691
- channel: "orderbook",
1692
- outcomeIds: Array.from(this.pendingFlushUnsubs)
1693
- });
1694
- this.pendingFlushUnsubs.clear();
1695
- }
1696
- for (const id of [...this.pendingFlushTradeSubs]) {
1697
- if (this.pendingFlushTradeUnsubs.has(id)) {
1698
- this.pendingFlushTradeSubs.delete(id);
1699
- this.pendingFlushTradeUnsubs.delete(id);
1700
- }
1701
- }
1702
- if (this.pendingFlushTradeSubs.size > 0) {
1703
- this.send({
1704
- action: "subscribe",
1705
- channel: "trades",
1706
- outcomeIds: Array.from(this.pendingFlushTradeSubs)
1707
- });
1708
- this.pendingFlushTradeSubs.clear();
1709
- }
1710
- if (this.pendingFlushTradeUnsubs.size > 0) {
1711
- this.send({
1712
- action: "unsubscribe",
1713
- channel: "trades",
1714
- outcomeIds: Array.from(this.pendingFlushTradeUnsubs)
1715
- });
1716
- this.pendingFlushTradeUnsubs.clear();
1717
- }
1718
- for (const id of [...this.pendingFlushArbSubs]) {
1719
- if (this.pendingFlushArbUnsubs.has(id)) {
1720
- this.pendingFlushArbSubs.delete(id);
1721
- this.pendingFlushArbUnsubs.delete(id);
1722
- }
1723
- }
1724
- if (this.pendingFlushArbSubs.size > 0) {
1725
- this.send({
1726
- action: "subscribe",
1727
- channel: "arb",
1728
- marketIds: Array.from(this.pendingFlushArbSubs)
1729
- });
1730
- this.pendingFlushArbSubs.clear();
1731
- }
1732
- if (this.pendingFlushArbUnsubs.size > 0) {
1733
- this.send({
1734
- action: "unsubscribe",
1735
- channel: "arb",
1736
- marketIds: Array.from(this.pendingFlushArbUnsubs)
1737
- });
1738
- this.pendingFlushArbUnsubs.clear();
1739
- }
1740
- }
1741
- sendSubscribe(outcomeIds) {
1742
- this.send({ action: "subscribe", channel: "orderbook", outcomeIds });
1743
- }
1744
- send(msg) {
1745
- var _a;
1746
- if (((_a = this.ws) == null ? void 0 : _a.readyState) === WebSocket.OPEN) {
1747
- this.ws.send(JSON.stringify(msg));
1748
- }
1749
- }
1750
- activeOrderbookMarkets() {
1751
- const markets = [];
1752
- this.subs.forEach((sub, id) => {
1753
- if (sub.refCount > 0) markets.push(id);
1754
- });
1755
- return markets;
1756
- }
1757
- activeTradeMarkets() {
1758
- const markets = [];
1759
- this.subs.forEach((sub, id) => {
1760
- if (sub.tradeRefs > 0) markets.push(id);
1761
- });
1762
- return markets;
1763
- }
1764
- activeArbMarkets() {
1765
- return Array.from(this.arbSubs.keys());
1766
- }
1767
- emitDiagnostic(event) {
1768
- var _a, _b;
1769
- (_b = (_a = this.callbacks).onDiagnostics) == null ? void 0 : _b.call(_a, event);
1770
- }
1771
- summarizeTopLevels(levels) {
1772
- return levels.slice(0, ORDERBOOK_DEBUG_TOP_LEVELS).map((level) => ({ price: level.price, size: level.size }));
1773
- }
1774
- debugOrderbook(event, payload) {
1775
- if (!this.isOrderbookDebugEnabled()) return;
1776
- console.debug(`[AggWebSocket][orderbook] ${event}`, payload);
1777
- }
1778
- isOrderbookDebugEnabled() {
1779
- return this.enableLogs;
1780
- }
1781
- };
1782
-
1783
261
  // src/client.ts
1784
262
  var COOKIE_REFRESH_DELIVERY = "cookie-refresh";
1785
263
  var DEFAULT_MIDPOINT_IDS_PER_REQUEST = 75;
@@ -1870,7 +348,7 @@ var AggClient = class {
1870
348
  this.authChannel = null;
1871
349
  /** In-flight refresh promise — serializes concurrent 401 retries so only one refresh runs. */
1872
350
  this.pendingRefresh = null;
1873
- var _a, _b;
351
+ var _a, _b, _c;
1874
352
  if (!options.appId && !options.adminKey) {
1875
353
  throw new Error("AggClient requires at least one of appId or adminKey");
1876
354
  }
@@ -1880,9 +358,11 @@ var AggClient = class {
1880
358
  this.signer = options.signer;
1881
359
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
1882
360
  this.wsUrl = options.wsUrl;
361
+ this.preferImmutableHistory = (_a = options.preferImmutableHistory) != null ? _a : false;
362
+ this.webSocketFactory = options.webSocketFactory;
1883
363
  this.authOptions = options.auth;
1884
- this.storageKey = `agg_auth_${(_a = this._appId) != null ? _a : "default"}`;
1885
- this.authDelivery = (_b = options.authDelivery) != null ? _b : "body";
364
+ this.storageKey = `agg_auth_${(_b = this._appId) != null ? _b : "default"}`;
365
+ this.authDelivery = (_c = options.authDelivery) != null ? _c : "body";
1886
366
  this.shouldPersistSession = options.persistSession !== false;
1887
367
  this.restoreSession();
1888
368
  this.initAuthChannel();
@@ -2010,7 +490,7 @@ var AggClient = class {
2010
490
  * @param path - API path (e.g. `/venue-events`)
2011
491
  * @param init - Fetch init options, plus optional `query` object for URL search params
2012
492
  */
2013
- request(path, init) {
493
+ request(path, init, decode) {
2014
494
  return __async(this, null, function* () {
2015
495
  const response = yield this.rawFetch(path, init);
2016
496
  if (response.status === 401 && this.accessToken && !AUTH_PATHS.has(path)) {
@@ -2021,7 +501,7 @@ var AggClient = class {
2021
501
  return this.throwResponseError(retryResponse);
2022
502
  }
2023
503
  if (retryResponse.status === 204) return void 0;
2024
- return retryResponse.json();
504
+ return decode ? decode(retryResponse) : retryResponse.json();
2025
505
  } catch (e) {
2026
506
  this.clearAccessToken();
2027
507
  return this.throwResponseError(response);
@@ -2031,7 +511,7 @@ var AggClient = class {
2031
511
  return this.throwResponseError(response);
2032
512
  }
2033
513
  if (response.status === 204) return void 0;
2034
- return response.json();
514
+ return decode ? decode(response) : response.json();
2035
515
  });
2036
516
  }
2037
517
  rawFetch(path, init) {
@@ -2068,12 +548,15 @@ var AggClient = class {
2068
548
  }
2069
549
  throwResponseError(response) {
2070
550
  return __async(this, null, function* () {
551
+ var _a;
2071
552
  let message = response.status === 404 ? "Market not found" : `AGG API error: ${response.status} ${response.statusText}`;
2072
553
  let code;
2073
554
  let retryable;
555
+ let retryAfterMs = parseRetryAfterHeader((_a = response.headers) == null ? void 0 : _a.get("Retry-After"));
2074
556
  let errors;
2075
557
  try {
2076
558
  const body = yield response.json();
559
+ retryAfterMs != null ? retryAfterMs : retryAfterMs = parseRetryAfterSeconds(body == null ? void 0 : body.retryAfter);
2077
560
  if (typeof body.message === "string" && body.message.length > 0) {
2078
561
  message = body.message;
2079
562
  }
@@ -2092,7 +575,7 @@ var AggClient = class {
2092
575
  }
2093
576
  } catch (e) {
2094
577
  }
2095
- throw new AggApiError(message, response.status, { code, retryable, errors });
578
+ throw new AggApiError(message, response.status, { code, retryable, retryAfterMs, errors });
2096
579
  });
2097
580
  }
2098
581
  /**
@@ -2519,14 +1002,19 @@ Issued At: ${issuedAt}`;
2519
1002
  *
2520
1003
  * Pass `quoteId` to fetch every leg of a multi-venue split route in
2521
1004
  * one call — `useQuoteSummary(quoteId)` reduces the list into a
2522
- * rollup (total cost, share-weighted avg price, to-win).
1005
+ * rollup (total cost, share-weighted avg price, to-win). Pass
1006
+ * `venueMarketIds` to narrow to the orders on those markets. Sent as
1007
+ * repeated params in one request; batch long lists yourself (or use
1008
+ * `useExecutionOrdersForMarkets`) to keep the URL short.
2523
1009
  */
2524
1010
  getExecutionOrders() {
2525
1011
  return __async(this, arguments, function* (params = {}) {
1012
+ var _a;
2526
1013
  const query = {};
2527
1014
  if (params.orderId) query.orderId = params.orderId;
2528
1015
  if (params.quoteId) query.quoteId = params.quoteId;
2529
1016
  if (params.externalId) query.externalId = params.externalId;
1017
+ if ((_a = params.venueMarketIds) == null ? void 0 : _a.length) query.venueMarketIds = params.venueMarketIds;
2530
1018
  if (params.status) query.status = params.status;
2531
1019
  if (params.orderType) query.orderType = params.orderType;
2532
1020
  if (params.mode) query.mode = params.mode;
@@ -2576,16 +1064,92 @@ Issued At: ${issuedAt}`;
2576
1064
  });
2577
1065
  });
2578
1066
  }
2579
- /** Get managed wallet balances, including per-chain cash balances and per-venue position balances. */
1067
+ /**
1068
+ * Get cash balances and per-venue position balances.
1069
+ *
1070
+ * `custody: "self"` returns the wallets linked to this account — plus their
1071
+ * Polymarket deposit and legacy wallets — instead of the managed wallet. Each
1072
+ * chain row then carries `address` and `custodyKind`; `positions` is
1073
+ * account-wide either way.
1074
+ *
1075
+ * Caveat on the headline number: a chain row with `custodyKind:
1076
+ * "POLYMARKET_LEGACY"` and `legacyKind: "proxy"` holds funds that can only
1077
+ * be spent on Polymarket, but it still counts towards its token's
1078
+ * `totalRaw`/`availableRaw` (a token's totals are always the sum of its
1079
+ * chain rows). If you show "spendable anywhere", subtract those rows.
1080
+ */
2580
1081
  getManagedBalances() {
2581
1082
  return __async(this, arguments, function* (params = {}) {
2582
1083
  const query = {};
2583
1084
  if (params.mode) query.mode = params.mode;
1085
+ if (params.custody) query.custody = params.custody;
2584
1086
  return this.request("/execution/balances", {
2585
1087
  query: Object.keys(query).length ? query : void 0
2586
1088
  });
2587
1089
  });
2588
1090
  }
1091
+ /** Read hosted custody status without creating a signing wallet or account. */
1092
+ getHostedVenueAccount(_0) {
1093
+ return __async(this, arguments, function* (venue, params = {}) {
1094
+ return this.request(
1095
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}`,
1096
+ {
1097
+ query: params.mode ? { mode: params.mode } : void 0
1098
+ }
1099
+ );
1100
+ });
1101
+ }
1102
+ /** Owned exact ledger entries; not balances, fill fees or withdrawal finality. */
1103
+ getHostedVenueActivity(_0) {
1104
+ return __async(this, arguments, function* (venue, params = {}) {
1105
+ return this.request(
1106
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}/activity`,
1107
+ {
1108
+ query: __spreadValues(__spreadValues({}, params.mode ? { mode: params.mode } : {}), params.page !== void 0 ? { page: String(params.page) } : {})
1109
+ }
1110
+ );
1111
+ });
1112
+ }
1113
+ /** Explicit hosted-account creation; no signing wallet or credential upload. */
1114
+ provisionHostedVenueAccount(venue, body) {
1115
+ return __async(this, null, function* () {
1116
+ return this.request(
1117
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}`,
1118
+ {
1119
+ method: "POST",
1120
+ body: JSON.stringify(body)
1121
+ }
1122
+ );
1123
+ });
1124
+ }
1125
+ /** Fetch the owned hosted account's verified Solana USDC deposit address. */
1126
+ getHostedVenueDepositAddress(_0) {
1127
+ return __async(this, arguments, function* (venue, params = {}) {
1128
+ return this.request(
1129
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}/deposit-address`,
1130
+ { query: params.mode ? { mode: params.mode } : void 0 }
1131
+ );
1132
+ });
1133
+ }
1134
+ /** Submit exactly one explicit hosted withdrawal under a caller idempotency key. */
1135
+ withdrawHostedVenue(venue, body) {
1136
+ return __async(this, null, function* () {
1137
+ return this.request(
1138
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}/withdrawals`,
1139
+ { method: "POST", body: JSON.stringify(body) }
1140
+ );
1141
+ });
1142
+ }
1143
+ /** Read only Agg's persisted withdrawal state; this never replays a native request. */
1144
+ getHostedVenueWithdrawal(_0, _1) {
1145
+ return __async(this, arguments, function* (venue, withdrawalId, params = {}) {
1146
+ if (!withdrawalId) throw new Error("withdrawalId is required");
1147
+ return this.request(
1148
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}/withdrawals/${encodeURIComponent(withdrawalId)}`,
1149
+ { query: params.mode ? { mode: params.mode } : void 0 }
1150
+ );
1151
+ });
1152
+ }
2589
1153
  /** Store or replace encrypted credentials for a venue. Secrets are never returned. */
2590
1154
  upsertVenueKey(body) {
2591
1155
  return __async(this, null, function* () {
@@ -2755,6 +1319,17 @@ Issued At: ${issuedAt}`;
2755
1319
  });
2756
1320
  }
2757
1321
  /** Get a single venue event by ID. Requires appId or admin auth. */
1322
+ getEventBootstrap(eventId, options) {
1323
+ return __async(this, null, function* () {
1324
+ return this.request(
1325
+ `/venue-events/${encodeURIComponent(eventId)}/bootstrap`,
1326
+ {
1327
+ query: (options == null ? void 0 : options.limit) ? { limit: String(options.limit) } : void 0,
1328
+ signal: options == null ? void 0 : options.signal
1329
+ }
1330
+ );
1331
+ });
1332
+ }
2758
1333
  getVenueEventById(id, options) {
2759
1334
  return __async(this, null, function* () {
2760
1335
  return this.request(`/venue-events/${encodeURIComponent(id)}`, {
@@ -2883,14 +1458,15 @@ Issued At: ${issuedAt}`;
2883
1458
  });
2884
1459
  }
2885
1460
  /** List venue markets with optional filters. Requires appId or admin auth. Supports cursor-based pagination. */
2886
- getVenueMarkets(options) {
1461
+ getVenueMarkets(options, requestOptions) {
2887
1462
  return __async(this, null, function* () {
2888
- var _a;
1463
+ var _a, _b;
2889
1464
  const query = {};
2890
1465
  if (options == null ? void 0 : options.venue) query.venue = options.venue;
1466
+ if ((_a = options == null ? void 0 : options.externalIdentifier) == null ? void 0 : _a.length) query.externalIdentifier = options.externalIdentifier;
2891
1467
  if (options == null ? void 0 : options.venueEventId) query.venueEventId = options.venueEventId;
2892
1468
  if ((options == null ? void 0 : options.cluster) === false) query.cluster = "false";
2893
- if ((_a = options == null ? void 0 : options.search) == null ? void 0 : _a.trim()) query.search = options.search.trim();
1469
+ if ((_b = options == null ? void 0 : options.search) == null ? void 0 : _b.trim()) query.search = options.search.trim();
2894
1470
  if (options == null ? void 0 : options.matchStatus) query.matchStatus = options.matchStatus;
2895
1471
  if (options == null ? void 0 : options.status) query.status = options.status;
2896
1472
  if ((options == null ? void 0 : options.aggKey) && options.aggKey.length > 0) query.aggKey = options.aggKey;
@@ -2910,7 +1486,8 @@ Issued At: ${issuedAt}`;
2910
1486
  if (options == null ? void 0 : options.sortDir) query.sortDir = options.sortDir;
2911
1487
  if (options == null ? void 0 : options.context) query.context = options.context;
2912
1488
  return this.request("/venue-markets", {
2913
- query: Object.keys(query).length > 0 ? query : void 0
1489
+ query: Object.keys(query).length > 0 ? query : void 0,
1490
+ signal: requestOptions == null ? void 0 : requestOptions.signal
2914
1491
  });
2915
1492
  });
2916
1493
  }
@@ -3012,7 +1589,7 @@ Issued At: ${issuedAt}`;
3012
1589
  * format intentionally omits the param so the server hits the cheaper
3013
1590
  * (and longer-TTL) light cache slot.
3014
1591
  */
3015
- search(params) {
1592
+ search(params, options) {
3016
1593
  return __async(this, null, function* () {
3017
1594
  const query = {
3018
1595
  q: params.q,
@@ -3023,8 +1600,10 @@ Issued At: ${issuedAt}`;
3023
1600
  if (params.limit != null) query.limit = String(params.limit);
3024
1601
  if (params.cursor) query.cursor = params.cursor;
3025
1602
  if (params.deep) query.deepSearch = "true";
1603
+ if (params.mode) query.mode = params.mode;
3026
1604
  return this.request("/search", {
3027
- query
1605
+ query,
1606
+ signal: options == null ? void 0 : options.signal
3028
1607
  });
3029
1608
  });
3030
1609
  }
@@ -3086,6 +1665,7 @@ Issued At: ${issuedAt}`;
3086
1665
  /** Get the canonical TradingView-style bar series for a single venue market outcome. */
3087
1666
  getChartBars(params, options) {
3088
1667
  return __async(this, null, function* () {
1668
+ var _a, _b;
3089
1669
  const resolution = mapChartResolution(params.resolution);
3090
1670
  const query = {
3091
1671
  venueMarketOutcomeId: params.venueMarketOutcomeId,
@@ -3094,12 +1674,39 @@ Issued At: ${issuedAt}`;
3094
1674
  if (resolution) query.resolution = resolution;
3095
1675
  if (params.from != null) query.from = String(params.from);
3096
1676
  if (params.countBack != null) query.countBack = String(params.countBack);
1677
+ if (options == null ? void 0 : options.decode) {
1678
+ const text = yield this.request(
1679
+ "/charts/bars",
1680
+ { query, signal: options.signal },
1681
+ (response) => readChartHistoryText(response)
1682
+ );
1683
+ (_a = options.signal) == null ? void 0 : _a.throwIfAborted();
1684
+ const decoded = yield options.decode(text);
1685
+ (_b = options.signal) == null ? void 0 : _b.throwIfAborted();
1686
+ return decoded;
1687
+ }
3097
1688
  return this.request("/charts/bars", {
3098
1689
  query,
3099
1690
  signal: options == null ? void 0 : options.signal
3100
1691
  });
3101
1692
  });
3102
1693
  }
1694
+ getChartHistoryManifest(params, options) {
1695
+ return __async(this, null, function* () {
1696
+ const query = {
1697
+ venueMarketOutcomeId: params.venueMarketOutcomeId,
1698
+ to: String(params.to)
1699
+ };
1700
+ const resolution = mapChartResolution(params.resolution);
1701
+ if (resolution) query.resolution = resolution;
1702
+ if (params.from != null) query.from = String(params.from);
1703
+ if (params.countBack != null) query.countBack = String(params.countBack);
1704
+ return this.request("/charts/history/manifest", {
1705
+ query,
1706
+ signal: options == null ? void 0 : options.signal
1707
+ });
1708
+ });
1709
+ }
3103
1710
  // --- Batch orderbooks ---
3104
1711
  /** Get live per-venue orderbooks for the explicit venueMarketIds requested. */
3105
1712
  getOrderbooks(params, options) {
@@ -3194,22 +1801,34 @@ Issued At: ${issuedAt}`;
3194
1801
  return params.side ? __spreadProps(__spreadValues({}, response), { side: params.side }) : response;
3195
1802
  });
3196
1803
  }
1804
+ /** Seed subscribed outcomes with one bounded batch of compatible snapshots. */
1805
+ getOutcomeOrderbooks(params, options) {
1806
+ return __async(this, null, function* () {
1807
+ return this.request("/orderbooks/outcomes", {
1808
+ query: __spreadValues({
1809
+ venueMarketOutcomeIds: params.venueMarketOutcomeIds.join(",")
1810
+ }, params.depth == null ? {} : { depth: String(params.depth) }),
1811
+ signal: options == null ? void 0 : options.signal
1812
+ });
1813
+ });
1814
+ }
3197
1815
  /**
3198
1816
  * Create an AggWebSocket instance configured with this client's appId and optional auth token.
3199
1817
  * Requires `wsUrl` to be set in the client options.
3200
1818
  */
3201
1819
  createWebSocket(callbacks, options) {
3202
- var _a;
1820
+ var _a, _b;
3203
1821
  if (!this.wsUrl) {
3204
1822
  throw new Error("AggClient.createWebSocket() requires wsUrl to be set in client options");
3205
1823
  }
3206
1824
  const url = new URL(this.wsUrl);
3207
1825
  if (this._appId) url.searchParams.set("appId", this._appId);
3208
- return new AggWebSocket({
1826
+ const createSocket = (_a = this.webSocketFactory) != null ? _a : ((socketOptions) => new AggWebSocket(socketOptions));
1827
+ return createSocket({
3209
1828
  url: url.toString(),
3210
1829
  callbacks,
3211
1830
  initialToken: this.accessToken,
3212
- enableLogs: (_a = options == null ? void 0 : options.enableLogs) != null ? _a : false
1831
+ enableLogs: (_b = options == null ? void 0 : options.enableLogs) != null ? _b : false
3213
1832
  });
3214
1833
  }
3215
1834
  /** Execute a previously quoted managed trade. Returns pending order IDs. */
@@ -3505,138 +2124,6 @@ Issued At: ${issuedAt}`;
3505
2124
  // src/best-split.ts
3506
2125
  var computeBestSplitsByAmount2 = computeBestSplitsByAmount;
3507
2126
 
3508
- // src/candle-builder.ts
3509
- var INTERVAL_SECS = {
3510
- "1m": 60,
3511
- "5m": 300,
3512
- "1h": 3600,
3513
- "1d": 86400
3514
- };
3515
- var ALL_INTERVALS = ["1m", "5m", "1h", "1d"];
3516
- var MAX_CLOSED = 500;
3517
- var CandleBuilder = class {
3518
- constructor() {
3519
- this.forming = /* @__PURE__ */ new Map();
3520
- this.closedMap = /* @__PURE__ */ new Map();
3521
- this.listeners = /* @__PURE__ */ new Set();
3522
- this._midpoint = null;
3523
- this.dirty = false;
3524
- this.scheduled = false;
3525
- for (const interval of ALL_INTERVALS) {
3526
- this.closedMap.set(interval, []);
3527
- }
3528
- }
3529
- /** Feed a midpoint (from orderbook delta). Updates O/H/L/C for all intervals. */
3530
- addMidpoint(midpoint, timestampSecs) {
3531
- if (!Number.isFinite(midpoint) || !Number.isFinite(timestampSecs)) return;
3532
- this._midpoint = midpoint;
3533
- for (const interval of ALL_INTERVALS) {
3534
- this.updateCandle(interval, midpoint, timestampSecs);
3535
- }
3536
- this.markDirty();
3537
- }
3538
- /** Feed a trade. Updates volume + refines H/L/C from trade price. */
3539
- addTrade(price, size, timestampSecs) {
3540
- if (!Number.isFinite(price) || !Number.isFinite(size) || !Number.isFinite(timestampSecs))
3541
- return;
3542
- for (const interval of ALL_INTERVALS) {
3543
- this.updateCandle(interval, price, timestampSecs, size);
3544
- }
3545
- this.markDirty();
3546
- }
3547
- /** Current forming candle for an interval. */
3548
- getForming(interval) {
3549
- var _a;
3550
- return (_a = this.forming.get(interval)) != null ? _a : null;
3551
- }
3552
- /** Closed candles for an interval (ring buffer, max 500, newest last). */
3553
- getClosed(interval) {
3554
- var _a;
3555
- return (_a = this.closedMap.get(interval)) != null ? _a : [];
3556
- }
3557
- /** Current midpoint (latest value fed). */
3558
- get midpoint() {
3559
- return this._midpoint;
3560
- }
3561
- /**
3562
- * Subscribe to state changes. Returns unsubscribe function.
3563
- * BATCHED: callbacks fire at most once per rAF frame (~16ms).
3564
- * In non-browser environments, falls back to 16ms setTimeout.
3565
- */
3566
- onChange(listener) {
3567
- this.listeners.add(listener);
3568
- return () => {
3569
- this.listeners.delete(listener);
3570
- };
3571
- }
3572
- /** Clear all state (on reconnect). */
3573
- reset() {
3574
- this.forming.clear();
3575
- for (const interval of ALL_INTERVALS) {
3576
- this.closedMap.set(interval, []);
3577
- }
3578
- this._midpoint = null;
3579
- this.dirty = false;
3580
- }
3581
- /** Clear all state AND unsubscribe all listeners. Called on store eviction. */
3582
- dispose() {
3583
- this.reset();
3584
- this.listeners.clear();
3585
- }
3586
- updateCandle(interval, price, timestampSecs, tradeSize) {
3587
- const secs = INTERVAL_SECS[interval];
3588
- const openTime = Math.floor(timestampSecs / secs) * secs;
3589
- const current = this.forming.get(interval);
3590
- if (current && current.openTime !== openTime) {
3591
- const closed = this.closedMap.get(interval);
3592
- closed.push(__spreadProps(__spreadValues({}, current), { isFinal: true }));
3593
- if (closed.length > MAX_CLOSED) closed.shift();
3594
- this.forming.set(interval, {
3595
- openTime,
3596
- o: price,
3597
- h: price,
3598
- l: price,
3599
- c: price,
3600
- v: tradeSize != null ? tradeSize : 0,
3601
- tradeCount: tradeSize != null ? 1 : 0,
3602
- isFinal: false
3603
- });
3604
- } else if (!current) {
3605
- this.forming.set(interval, {
3606
- openTime,
3607
- o: price,
3608
- h: price,
3609
- l: price,
3610
- c: price,
3611
- v: tradeSize != null ? tradeSize : 0,
3612
- tradeCount: tradeSize != null ? 1 : 0,
3613
- isFinal: false
3614
- });
3615
- } else {
3616
- current.h = Math.max(current.h, price);
3617
- current.l = Math.min(current.l, price);
3618
- current.c = price;
3619
- if (tradeSize != null) {
3620
- current.v += tradeSize;
3621
- current.tradeCount += 1;
3622
- }
3623
- }
3624
- }
3625
- markDirty() {
3626
- this.dirty = true;
3627
- if (this.scheduled) return;
3628
- this.scheduled = true;
3629
- const tick = typeof requestAnimationFrame === "function" ? requestAnimationFrame : (fn) => setTimeout(fn, 16);
3630
- tick(() => {
3631
- this.scheduled = false;
3632
- if (this.dirty) {
3633
- this.dirty = false;
3634
- this.listeners.forEach((l) => l());
3635
- }
3636
- });
3637
- }
3638
- };
3639
-
3640
2127
  // src/market-aggregator.ts
3641
2128
  function mergeCandles(candles) {
3642
2129
  const valid = candles.filter((c2) => c2 != null);
@@ -3715,6 +2202,7 @@ export {
3715
2202
  OrderStatus,
3716
2203
  OrderType,
3717
2204
  RECURRENCE_CADENCES,
2205
+ RETINA_IMAGE_SIZES,
3718
2206
  SELF_CUSTODY_FILL_TIMEOUT_MS,
3719
2207
  SELF_CUSTODY_POLL_INTERVAL_MS,
3720
2208
  TERMINAL_EXECUTION_STATES,
@@ -3748,13 +2236,17 @@ export {
3748
2236
  isVenueActive,
3749
2237
  isVenueDataVisible,
3750
2238
  isVenueInactive,
2239
+ loadImmutableHistory,
3751
2240
  mergeCandles,
3752
2241
  mergeClosedCandles,
3753
2242
  normalizeVenueMarketCluster,
3754
2243
  optimizedImageUrl,
2244
+ orderbookCrc32,
3755
2245
  parse,
3756
2246
  parseEmail,
3757
2247
  parseEmailStrict,
2248
+ projectedOrderbookChecksum,
2249
+ retinaImageUrl,
3758
2250
  safeParse,
3759
2251
  snapshotToOrderbook,
3760
2252
  sortVenues,