@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.js CHANGED
@@ -90,6 +90,7 @@ __export(index_exports, {
90
90
  OrderStatus: () => OrderStatus,
91
91
  OrderType: () => OrderType,
92
92
  RECURRENCE_CADENCES: () => RECURRENCE_CADENCES,
93
+ RETINA_IMAGE_SIZES: () => RETINA_IMAGE_SIZES,
93
94
  SELF_CUSTODY_FILL_TIMEOUT_MS: () => SELF_CUSTODY_FILL_TIMEOUT_MS,
94
95
  SELF_CUSTODY_POLL_INTERVAL_MS: () => SELF_CUSTODY_POLL_INTERVAL_MS,
95
96
  TERMINAL_EXECUTION_STATES: () => TERMINAL_EXECUTION_STATES,
@@ -123,13 +124,17 @@ __export(index_exports, {
123
124
  isVenueActive: () => isVenueActive,
124
125
  isVenueDataVisible: () => isVenueDataVisible,
125
126
  isVenueInactive: () => isVenueInactive,
127
+ loadImmutableHistory: () => loadImmutableHistory,
126
128
  mergeCandles: () => mergeCandles,
127
129
  mergeClosedCandles: () => mergeClosedCandles,
128
130
  normalizeVenueMarketCluster: () => normalizeVenueMarketCluster,
129
131
  optimizedImageUrl: () => optimizedImageUrl,
132
+ orderbookCrc32: () => orderbookCrc32,
130
133
  parse: () => parse,
131
134
  parseEmail: () => parseEmail,
132
135
  parseEmailStrict: () => parseEmailStrict,
136
+ projectedOrderbookChecksum: () => projectedOrderbookChecksum,
137
+ retinaImageUrl: () => retinaImageUrl,
133
138
  safeParse: () => safeParse,
134
139
  snapshotToOrderbook: () => snapshotToOrderbook,
135
140
  sortVenues: () => sortVenues,
@@ -145,11 +150,13 @@ var Venue = /* @__PURE__ */ ((Venue3) => {
145
150
  Venue3["opinion"] = "opinion";
146
151
  Venue3["predict"] = "predict";
147
152
  Venue3["pred"] = "pred";
153
+ Venue3["tiprun"] = "tiprun";
148
154
  Venue3["probable"] = "probable";
149
155
  Venue3["myriad"] = "myriad";
150
156
  Venue3["hyperliquid"] = "hyperliquid";
151
157
  Venue3["novig"] = "novig";
152
158
  Venue3["prophetx"] = "prophetx";
159
+ Venue3["betdex"] = "betdex";
153
160
  return Venue3;
154
161
  })(Venue || {});
155
162
  var VENUES = [
@@ -159,11 +166,13 @@ var VENUES = [
159
166
  "opinion" /* opinion */,
160
167
  "predict" /* predict */,
161
168
  "pred" /* pred */,
169
+ "tiprun" /* tiprun */,
162
170
  "probable" /* probable */,
163
171
  "myriad" /* myriad */,
164
172
  "hyperliquid" /* hyperliquid */,
165
173
  "novig" /* novig */,
166
- "prophetx" /* prophetx */
174
+ "prophetx" /* prophetx */,
175
+ "betdex" /* betdex */
167
176
  ];
168
177
  var VENUE_CHAIN_IDS = {
169
178
  ["kalshi" /* kalshi */]: 792703809,
@@ -171,6 +180,7 @@ var VENUE_CHAIN_IDS = {
171
180
  ["limitless" /* limitless */]: 8453,
172
181
  ["predict" /* predict */]: 56,
173
182
  ["pred" /* pred */]: 8453,
183
+ ["tiprun" /* tiprun */]: 42161,
174
184
  ["myriad" /* myriad */]: 56,
175
185
  ["hyperliquid" /* hyperliquid */]: 1337,
176
186
  ["novig" /* novig */]: 792703809,
@@ -183,14 +193,22 @@ var ACTIVE_VENUES = [
183
193
  "opinion" /* opinion */,
184
194
  "predict" /* predict */,
185
195
  "pred" /* pred */,
196
+ "tiprun" /* tiprun */,
186
197
  "probable" /* probable */,
187
198
  "myriad" /* myriad */,
188
199
  "hyperliquid" /* hyperliquid */,
189
200
  "novig" /* novig */,
190
- "prophetx" /* prophetx */
201
+ "prophetx" /* prophetx */,
202
+ "betdex" /* betdex */
191
203
  ];
192
204
  var INACTIVE_VENUES = [];
193
- var DEFAULT_DISABLED_VENUES = ["pred" /* pred */, "novig" /* novig */, "prophetx" /* prophetx */];
205
+ var DEFAULT_DISABLED_VENUES = [
206
+ "pred" /* pred */,
207
+ "tiprun" /* tiprun */,
208
+ "novig" /* novig */,
209
+ "prophetx" /* prophetx */,
210
+ "betdex" /* betdex */
211
+ ];
194
212
  var ACTIVE_VENUE_SET = new Set(ACTIVE_VENUES);
195
213
  var INACTIVE_VENUE_SET = new Set(INACTIVE_VENUES);
196
214
  function normalizeVenue(venue) {
@@ -226,10 +244,21 @@ var ImageSize = /* @__PURE__ */ ((ImageSize2) => {
226
244
  ImageSize2[ImageSize2["sm"] = 44] = "sm";
227
245
  ImageSize2[ImageSize2["md"] = 48] = "md";
228
246
  ImageSize2[ImageSize2["lg"] = 60] = "lg";
247
+ ImageSize2[ImageSize2["xl"] = 80] = "xl";
248
+ ImageSize2[ImageSize2["xxl"] = 96] = "xxl";
249
+ ImageSize2[ImageSize2["xxxl"] = 120] = "xxxl";
229
250
  ImageSize2[ImageSize2["original"] = 0] = "original";
230
251
  return ImageSize2;
231
252
  })(ImageSize || {});
232
- var IMAGE_SIZES = [44 /* sm */, 48 /* md */, 60 /* lg */];
253
+ var IMAGE_SIZES = [
254
+ 44 /* sm */,
255
+ 48 /* md */,
256
+ 60 /* lg */,
257
+ 80 /* xl */,
258
+ 96 /* xxl */,
259
+ 120 /* xxxl */
260
+ ];
261
+ var RETINA_IMAGE_SIZES = [80 /* xl */, 96 /* xxl */, 120 /* xxxl */];
233
262
 
234
263
  // ../common/src/enums/account.ts
235
264
  var AccountType = /* @__PURE__ */ ((AccountType2) => {
@@ -442,7 +471,7 @@ function computeBestSplitsByAmount(selections, side, maxDollarAmount) {
442
471
  if (selections.length === 0 || maxDollarAmount <= 0) {
443
472
  return { splits: [], totalSize: 0, totalCost: 0 };
444
473
  }
445
- const levels = [];
474
+ const levels2 = [];
446
475
  for (const o of selections) {
447
476
  const venue = o.market.venue;
448
477
  const venueMarketOutcomeId = o.outcome.id;
@@ -451,7 +480,7 @@ function computeBestSplitsByAmount(selections, side, maxDollarAmount) {
451
480
  [...o.orderbook.asks].sort((a, b) => a.price - b.price).forEach(
452
481
  (l) => {
453
482
  var _a2;
454
- return levels.push({
483
+ return levels2.push({
455
484
  venue,
456
485
  venueMarketOutcomeId,
457
486
  outcomeExternalIdentifier,
@@ -465,7 +494,7 @@ function computeBestSplitsByAmount(selections, side, maxDollarAmount) {
465
494
  [...o.orderbook.bids].sort((a, b) => b.price - a.price).forEach(
466
495
  (l) => {
467
496
  var _a2;
468
- return levels.push({
497
+ return levels2.push({
469
498
  venue,
470
499
  venueMarketOutcomeId,
471
500
  outcomeExternalIdentifier,
@@ -477,10 +506,10 @@ function computeBestSplitsByAmount(selections, side, maxDollarAmount) {
477
506
  );
478
507
  }
479
508
  }
480
- levels.sort((a, b) => a.price - b.price);
509
+ levels2.sort((a, b) => a.price - b.price);
481
510
  let totalCost = 0;
482
511
  const fills = [];
483
- for (const l of levels) {
512
+ for (const l of levels2) {
484
513
  if (totalCost >= maxDollarAmount) break;
485
514
  const remainingBudget = maxDollarAmount - totalCost;
486
515
  const maxSizeAtLevel = Math.min(l.size, remainingBudget / l.price);
@@ -565,6 +594,13 @@ function optimizedImageUrl(url, maxPx) {
565
594
  const filename = variant === 0 /* original */ ? "original.webp" : `${variant}.webp`;
566
595
  return url.replace(VARIANT_RE, `/${filename}`);
567
596
  }
597
+ function retinaImageUrl(url, maxPx) {
598
+ if (!url) return void 0;
599
+ if (!VARIANT_RE.test(url)) return void 0;
600
+ const variant = RETINA_IMAGE_SIZES.find((size) => size >= maxPx * 2);
601
+ if (variant == null) return void 0;
602
+ return url.replace(VARIANT_RE, `/${variant}.webp`);
603
+ }
568
604
 
569
605
  // ../common/src/utils/venue-market-cluster.ts
570
606
  var toNonEmptyString = (value) => {
@@ -689,10 +725,12 @@ var VENUE_SORT_ORDER = [
689
725
  "hyperliquid" /* hyperliquid */,
690
726
  "predict" /* predict */,
691
727
  "prophetx" /* prophetx */,
728
+ "betdex" /* betdex */,
692
729
  "kalshi" /* kalshi */,
693
730
  "limitless" /* limitless */,
694
731
  "myriad" /* myriad */,
695
732
  "pred" /* pred */,
733
+ "tiprun" /* tiprun */,
696
734
  "probable" /* probable */,
697
735
  "novig" /* novig */,
698
736
  "opinion" /* opinion */
@@ -817,6 +855,93 @@ var isBalanceRefillWithinDailyCap = (minimumRaw, refillAmountRaw, dailyCapRaw, m
817
855
  return BigInt(minimumRaw) <= maximum && BigInt(refillAmountRaw) <= maximum;
818
856
  };
819
857
 
858
+ // ../common/src/orderbook-checksum.ts
859
+ function orderbookCrc32(value) {
860
+ let crc = 4294967295;
861
+ for (let i = 0; i < value.length; i++) {
862
+ crc ^= value.charCodeAt(i);
863
+ for (let bit = 0; bit < 8; bit++) crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
864
+ }
865
+ return (crc ^ 4294967295) >>> 0;
866
+ }
867
+ function projectedOrderbookChecksum(book) {
868
+ let result = 0;
869
+ for (const level of book.bids) result ^= orderbookCrc32(`B${level.price}:${level.size}`);
870
+ for (const level of book.asks) result ^= orderbookCrc32(`A${level.price}:${level.size}`);
871
+ return result >>> 0;
872
+ }
873
+
874
+ // src/retry-after.ts
875
+ function parseRetryAfterSeconds(value) {
876
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
877
+ const milliseconds = value * 1e3;
878
+ return Number.isSafeInteger(milliseconds) ? milliseconds : void 0;
879
+ }
880
+ function parseRetryAfterHeader(value, now = Date.now()) {
881
+ if (!value) return void 0;
882
+ const text = value.trim();
883
+ if (/^\d+$/.test(text)) return parseRetryAfterSeconds(Number(text));
884
+ 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(
885
+ text
886
+ ))
887
+ return void 0;
888
+ const timestamp = Date.parse(text);
889
+ if (!Number.isFinite(timestamp) || new Date(timestamp).toUTCString() !== text) return void 0;
890
+ const milliseconds = timestamp - now;
891
+ return Number.isSafeInteger(milliseconds) && milliseconds >= 0 ? milliseconds : void 0;
892
+ }
893
+
894
+ // src/market-data/history-decoder.ts
895
+ function decodeChartHistory(payload) {
896
+ if (payload.length > 1048576) throw new Error("Chart history exceeds decode budget");
897
+ const body = JSON.parse(payload);
898
+ if (!body || !Array.isArray(body.data) || body.data.length > 5e3 || typeof body.venueMarketOutcomeId !== "string" || typeof body.venue !== "string" || !["1m", "5m", "1h", "1d"].includes(body.resolution) || !body.meta)
899
+ throw new Error("Invalid chart history response");
900
+ const price = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
901
+ for (const bar of body.data) {
902
+ if (!Number.isSafeInteger(bar.t) || bar.t < 0 || ![bar.o, bar.h, bar.l, bar.c].every(price) || bar.l > Math.min(bar.o, bar.c) || bar.h < Math.max(bar.o, bar.c) || bar.v !== null && (!Number.isFinite(bar.v) || bar.v < 0) || bar.mean !== void 0 && !price(bar.mean))
903
+ throw new Error("Invalid chart history candle");
904
+ }
905
+ const data = [...body.data].sort((a, b) => a.t - b.t);
906
+ if (data.some((bar, index) => index > 0 && data[index - 1].t === bar.t))
907
+ throw new Error("Duplicate chart history timestamp");
908
+ return __spreadProps(__spreadValues({}, body), { data });
909
+ }
910
+ function readChartHistoryText(response) {
911
+ return __async(this, null, function* () {
912
+ var _a, _b;
913
+ const maxBytes = 1048576;
914
+ if (Number(response.headers.get("content-length")) > maxBytes) {
915
+ yield (_a = response.body) == null ? void 0 : _a.cancel();
916
+ throw new Error("Chart history exceeds decode budget");
917
+ }
918
+ if (!((_b = response.body) == null ? void 0 : _b.getReader)) {
919
+ const text2 = yield response.text();
920
+ if (new TextEncoder().encode(text2).byteLength > maxBytes)
921
+ throw new Error("Chart history exceeds decode budget");
922
+ return text2;
923
+ }
924
+ const reader = response.body.getReader();
925
+ const decoder = new TextDecoder();
926
+ let size = 0;
927
+ let text = "";
928
+ try {
929
+ while (true) {
930
+ const { value, done } = yield reader.read();
931
+ if (done) return text + decoder.decode();
932
+ size += value.byteLength;
933
+ if (size > maxBytes) {
934
+ yield reader.cancel();
935
+ throw new Error("Chart history exceeds decode budget");
936
+ }
937
+ text += decoder.decode(value, { stream: true });
938
+ }
939
+ } finally {
940
+ reader.releaseLock();
941
+ }
942
+ });
943
+ }
944
+
820
945
  // src/errors.ts
821
946
  var TurnstileChallengeError = class extends Error {
822
947
  constructor(siteKey) {
@@ -832,6 +957,7 @@ var AggApiError = class _AggApiError extends Error {
832
957
  this.status = status;
833
958
  if (init.code !== void 0) this.code = init.code;
834
959
  if (init.retryable !== void 0) this.retryable = init.retryable;
960
+ if (init.retryAfterMs !== void 0) this.retryAfterMs = init.retryAfterMs;
835
961
  if (init.errors !== void 0) this.errors = init.errors;
836
962
  Object.setPrototypeOf(this, _AggApiError.prototype);
837
963
  }
@@ -987,23 +1113,153 @@ var RECURRENCE_CADENCES = [
987
1113
  "P1Y"
988
1114
  ];
989
1115
 
990
- // src/orderbook-utils.ts
991
- var PRICE_KEY_SCALE = 1e9;
992
- function crc32(str) {
993
- let crc = 4294967295;
994
- for (let i = 0; i < str.length; i++) {
995
- crc ^= str.charCodeAt(i);
996
- for (let j = 0; j < 8; j++) {
997
- crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
1116
+ // src/market-data/validate.ts
1117
+ var object = (value) => !!value && typeof value === "object" && !Array.isArray(value);
1118
+ var finite = (value) => typeof value === "number" && Number.isFinite(value);
1119
+ var nullable = (value) => value === null || finite(value);
1120
+ var levels = (value, attributed) => Array.isArray(value) && value.length <= 1e4 && value.every(
1121
+ (level) => Array.isArray(level) && finite(level[0]) && level[0] >= 0 && finite(level[1]) && level[1] >= 0 && (!attributed || object(level[2]) && Object.values(level[2]).every((size) => finite(size) && size >= 0))
1122
+ );
1123
+ function validMarketFrame(frame) {
1124
+ if (typeof frame.outcomeId !== "string" || !frame.outcomeId || !finite(frame.timestamp) || frame.timestamp < 0)
1125
+ return false;
1126
+ if (frame.type === "trade")
1127
+ return finite(frame.price) && frame.price >= 0 && finite(frame.size) && frame.size >= 0 && typeof frame.venue === "string" && (frame.side === "buy" || frame.side === "sell");
1128
+ if (!Number.isSafeInteger(frame.seq) || frame.seq < 0 || !Number.isInteger(frame.checksum) || frame.checksum < 0 || frame.checksum > 4294967295 || !nullable(frame.midpoint) || !nullable(frame.spread) || !object(frame.venues))
1129
+ return false;
1130
+ for (const info of Object.values(frame.venues))
1131
+ if (!object(info) || !nullable(info.bestBid) || !nullable(info.bestAsk)) return false;
1132
+ const snapshot = frame.type === "orderbook_snapshot";
1133
+ if (!levels(frame[snapshot ? "bids" : "bidChanges"], true) || !levels(frame[snapshot ? "asks" : "askChanges"], true))
1134
+ return false;
1135
+ const books = frame[snapshot ? "venueOrderbooks" : "venueDeltaBooks"];
1136
+ return object(books) && Object.values(books).every(
1137
+ (book) => object(book) && levels(book[snapshot ? "bids" : "bidChanges"], false) && levels(book[snapshot ? "asks" : "askChanges"], false)
1138
+ );
1139
+ }
1140
+
1141
+ // src/market-history.ts
1142
+ function readBoundedBody(response, signal) {
1143
+ return __async(this, null, function* () {
1144
+ var _a, _b;
1145
+ if (!response.body) throw new Error("Missing history body");
1146
+ const reader = response.body.getReader();
1147
+ const chunks = [];
1148
+ let size = 0;
1149
+ const abort = () => {
1150
+ void reader.cancel(signal.reason).catch(() => void 0);
1151
+ };
1152
+ signal.addEventListener("abort", abort, { once: true });
1153
+ try {
1154
+ while (true) {
1155
+ if (signal.aborted) throw (_a = signal.reason) != null ? _a : new Error("History request aborted");
1156
+ const { done, value } = yield reader.read();
1157
+ if (signal.aborted) throw (_b = signal.reason) != null ? _b : new Error("History request aborted");
1158
+ if (done) break;
1159
+ size += value.byteLength;
1160
+ if (size > 262144) {
1161
+ yield reader.cancel();
1162
+ throw new Error("Oversized history object");
1163
+ }
1164
+ chunks.push(value);
1165
+ }
1166
+ const bytes = new Uint8Array(size);
1167
+ let offset = 0;
1168
+ for (const chunk of chunks) {
1169
+ bytes.set(chunk, offset);
1170
+ offset += chunk.byteLength;
1171
+ }
1172
+ return bytes;
1173
+ } finally {
1174
+ signal.removeEventListener("abort", abort);
1175
+ reader.releaseLock();
998
1176
  }
999
- }
1000
- return (crc ^ 4294967295) >>> 0;
1177
+ });
1001
1178
  }
1179
+ function loadImmutableHistory(_0) {
1180
+ return __async(this, arguments, function* ({
1181
+ manifest,
1182
+ fetch: fetcher = globalThis.fetch,
1183
+ signal,
1184
+ digest
1185
+ }) {
1186
+ if (manifest.version !== 1 || !["venue-ohlcv-v1", "polymarket-price-samples-v1", "kalshi-ohlcv-quote-fallback-v2"].includes(
1187
+ manifest.semantics
1188
+ ) || manifest.chunks.length > 8 || manifest.chunks.reduce((n, c) => n + c.points, 0) > 4096 || !manifest.meta.complete) {
1189
+ throw new Error("History manifest is incomplete or unsupported");
1190
+ }
1191
+ const hash = digest != null ? digest : ((bytes) => __async(null, null, function* () {
1192
+ var _a;
1193
+ if (!((_a = globalThis.crypto) == null ? void 0 : _a.subtle))
1194
+ throw new Error(
1195
+ "History integrity requires WebCrypto or an injected SHA-256 implementation"
1196
+ );
1197
+ const value = yield globalThis.crypto.subtle.digest("SHA-256", bytes);
1198
+ return Array.from(new Uint8Array(value), (b) => b.toString(16).padStart(2, "0")).join("");
1199
+ }));
1200
+ const results = new Array(manifest.chunks.length);
1201
+ let next = 0;
1202
+ const controller = new AbortController();
1203
+ const deadline = setTimeout(
1204
+ () => controller.abort(new Error("History download budget exhausted")),
1205
+ 2e3
1206
+ );
1207
+ const abort = () => controller.abort(signal == null ? void 0 : signal.reason);
1208
+ signal == null ? void 0 : signal.addEventListener("abort", abort, { once: true });
1209
+ if (signal == null ? void 0 : signal.aborted) abort();
1210
+ try {
1211
+ yield Promise.all(
1212
+ Array.from({ length: Math.min(2, manifest.chunks.length) }, () => __async(null, null, function* () {
1213
+ var _a;
1214
+ while (next < manifest.chunks.length) {
1215
+ if (controller.signal.aborted)
1216
+ throw (_a = controller.signal.reason) != null ? _a : new Error("History request aborted");
1217
+ const index = next++;
1218
+ const chunk = manifest.chunks[index];
1219
+ if (chunk.points < 0 || chunk.points > 512 || chunk.from >= chunk.to || new URL(chunk.url).protocol !== "https:")
1220
+ throw new Error("Invalid history reference");
1221
+ const response = yield fetcher(chunk.url, {
1222
+ signal: controller.signal,
1223
+ credentials: "omit"
1224
+ });
1225
+ if (!response.ok) throw new Error(`History download failed (${response.status})`);
1226
+ if (Number(response.headers.get("content-length")) > 262144)
1227
+ throw new Error("Oversized history object");
1228
+ const bytes = yield readBoundedBody(response, controller.signal);
1229
+ if (bytes.byteLength > 262144 || (yield hash(bytes)) !== chunk.sha256)
1230
+ throw new Error("History integrity mismatch");
1231
+ const object2 = JSON.parse(new TextDecoder().decode(bytes));
1232
+ if (object2.version !== 1 || object2.venueMarketOutcomeId !== manifest.venueMarketOutcomeId || object2.venue !== manifest.venue || object2.resolution !== manifest.resolution || object2.semantics !== manifest.semantics || object2.from !== chunk.from || object2.to !== chunk.to || !Array.isArray(object2.data) || object2.data.length !== chunk.points) {
1233
+ throw new Error("History identity mismatch");
1234
+ }
1235
+ let previous = -Infinity;
1236
+ for (const bar of object2.data) {
1237
+ if (!Number.isSafeInteger(bar.t) || bar.t <= previous || bar.t < chunk.from || bar.t >= chunk.to || ![bar.o, bar.h, bar.l, bar.c].every((v) => Number.isFinite(v) && v >= 0 && v <= 1) || bar.l > Math.min(bar.o, bar.c) || bar.h < Math.max(bar.o, bar.c) || bar.v !== null && (!Number.isFinite(bar.v) || bar.v < 0) || bar.mean !== void 0 && (!Number.isFinite(bar.mean) || bar.mean < 0 || bar.mean > 1))
1238
+ throw new Error("Invalid history candle");
1239
+ previous = bar.t;
1240
+ }
1241
+ results[index] = object2.data;
1242
+ }
1243
+ }))
1244
+ );
1245
+ const bars = results.flat().filter((b) => b.t >= manifest.from && b.t < manifest.to).sort((a, b) => a.t - b.t);
1246
+ if (bars.some((b, i) => i > 0 && bars[i - 1].t === b.t))
1247
+ throw new Error("Overlapping history chunks");
1248
+ return bars;
1249
+ } catch (error) {
1250
+ controller.abort(error);
1251
+ throw error;
1252
+ } finally {
1253
+ clearTimeout(deadline);
1254
+ signal == null ? void 0 : signal.removeEventListener("abort", abort);
1255
+ }
1256
+ });
1257
+ }
1258
+
1259
+ // src/orderbook-utils.ts
1260
+ var PRICE_KEY_SCALE = 1e9;
1002
1261
  function computeChecksum(book) {
1003
- let checksum = 0;
1004
- for (const b of book.bids) checksum ^= crc32(`B${b.price}:${b.size}`);
1005
- for (const a of book.asks) checksum ^= crc32(`A${a.price}:${a.size}`);
1006
- return checksum >>> 0;
1262
+ return projectedOrderbookChecksum(book);
1007
1263
  }
1008
1264
  function attributedToLevel(wire) {
1009
1265
  return { price: wire[0], size: wire[1], venues: __spreadValues({}, wire[2]) };
@@ -1088,8 +1344,8 @@ function applyOrderbookDelta(state, delta) {
1088
1344
  function toPriceKey(price) {
1089
1345
  return String(Math.round(price * PRICE_KEY_SCALE));
1090
1346
  }
1091
- function applyChanges(levels, changes, sort) {
1092
- const levelsByPrice = new Map(levels.map((level) => [toPriceKey(level.price), level]));
1347
+ function applyChanges(levels2, changes, sort) {
1348
+ const levelsByPrice = new Map(levels2.map((level) => [toPriceKey(level.price), level]));
1093
1349
  for (const [price, size, venues] of changes) {
1094
1350
  const key = toPriceKey(price);
1095
1351
  if (size === 0) {
@@ -1102,12 +1358,12 @@ function applyChanges(levels, changes, sort) {
1102
1358
  venues: __spreadValues({}, venues != null ? venues : {})
1103
1359
  });
1104
1360
  }
1105
- levels = [...levelsByPrice.values()];
1106
- levels.sort(sort === "desc" ? (a, b) => b.price - a.price : (a, b) => a.price - b.price);
1107
- return levels;
1361
+ levels2 = [...levelsByPrice.values()];
1362
+ levels2.sort(sort === "desc" ? (a, b) => b.price - a.price : (a, b) => a.price - b.price);
1363
+ return levels2;
1108
1364
  }
1109
- function applyVenueChanges(levels, changes, sort) {
1110
- const levelsByPrice = new Map(levels.map((level) => [toPriceKey(level.price), level]));
1365
+ function applyVenueChanges(levels2, changes, sort) {
1366
+ const levelsByPrice = new Map(levels2.map((level) => [toPriceKey(level.price), level]));
1111
1367
  for (const [price, size] of changes) {
1112
1368
  const key = toPriceKey(price);
1113
1369
  if (size === 0) {
@@ -1116,9 +1372,9 @@ function applyVenueChanges(levels, changes, sort) {
1116
1372
  }
1117
1373
  levelsByPrice.set(key, { price, size });
1118
1374
  }
1119
- levels = [...levelsByPrice.values()];
1120
- levels.sort(sort === "desc" ? (a, b) => b.price - a.price : (a, b) => a.price - b.price);
1121
- return levels;
1375
+ levels2 = [...levelsByPrice.values()];
1376
+ levels2.sort(sort === "desc" ? (a, b) => b.price - a.price : (a, b) => a.price - b.price);
1377
+ return levels2;
1122
1378
  }
1123
1379
  function mergeVenueLevelChanges(primary, fallback) {
1124
1380
  if (!(primary == null ? void 0 : primary.length)) return fallback ? [...fallback] : [];
@@ -1164,6 +1420,121 @@ function deriveVenueChangesFromAttributedChanges(previousLevels, changes) {
1164
1420
  }
1165
1421
  return venueChanges;
1166
1422
  }
1423
+ var IndexedOrderbook = class {
1424
+ constructor(state) {
1425
+ this.venueBooks = /* @__PURE__ */ new Map();
1426
+ this.bids = new Map(state.bids.map((level) => [toPriceKey(level.price), level]));
1427
+ this.asks = new Map(state.asks.map((level) => [toPriceKey(level.price), level]));
1428
+ for (const [venue, book] of Object.entries(state.venueOrderbooks)) {
1429
+ this.venueBooks.set(venue, {
1430
+ bids: new Map(book.bids.map((level) => [toPriceKey(level.price), level])),
1431
+ asks: new Map(book.asks.map((level) => [toPriceKey(level.price), level]))
1432
+ });
1433
+ }
1434
+ const _a = state, { bids: _bids, asks: _asks, venueOrderbooks: _venueBooks } = _a, metadata = __objRest(_a, ["bids", "asks", "venueOrderbooks"]);
1435
+ this.metadata = metadata;
1436
+ this.cached = state;
1437
+ this.checksumValue = computeChecksum(state);
1438
+ }
1439
+ get seq() {
1440
+ return this.metadata.seq;
1441
+ }
1442
+ get checksum() {
1443
+ return this.checksumValue;
1444
+ }
1445
+ /** Verify before committing, including duplicate-price changes within a frame. */
1446
+ apply(delta) {
1447
+ var _a;
1448
+ let checksum = this.checksumValue;
1449
+ for (const [levels2, changes, side] of [
1450
+ [this.bids, delta.bidChanges, "B"],
1451
+ [this.asks, delta.askChanges, "A"]
1452
+ ]) {
1453
+ const changed = /* @__PURE__ */ new Map();
1454
+ for (const [price, size, venues] of changes) {
1455
+ const key = toPriceKey(price);
1456
+ const previous = changed.has(key) ? changed.get(key) : levels2.get(key);
1457
+ if (previous) checksum ^= orderbookCrc32(`${side}${previous.price}:${previous.size}`);
1458
+ if (size !== 0) checksum ^= orderbookCrc32(`${side}${price}:${size}`);
1459
+ changed.set(key, size === 0 ? void 0 : { price, size, venues: __spreadValues({}, venues) });
1460
+ }
1461
+ }
1462
+ checksum >>>= 0;
1463
+ if (checksum !== delta.checksum) return checksum;
1464
+ const previousBids = delta.bidChanges.flatMap(
1465
+ ([price]) => {
1466
+ var _a2;
1467
+ return (_a2 = this.bids.get(toPriceKey(price))) != null ? _a2 : [];
1468
+ }
1469
+ );
1470
+ const previousAsks = delta.askChanges.flatMap(
1471
+ ([price]) => {
1472
+ var _a2;
1473
+ return (_a2 = this.asks.get(toPriceKey(price))) != null ? _a2 : [];
1474
+ }
1475
+ );
1476
+ const impliedBids = deriveVenueChangesFromAttributedChanges(previousBids, delta.bidChanges);
1477
+ const impliedAsks = deriveVenueChangesFromAttributedChanges(previousAsks, delta.askChanges);
1478
+ for (const venue of /* @__PURE__ */ new Set([
1479
+ ...Object.keys(delta.venueDeltaBooks),
1480
+ ...Object.keys(impliedBids),
1481
+ ...Object.keys(impliedAsks)
1482
+ ])) {
1483
+ let book = this.venueBooks.get(venue);
1484
+ if (!book) {
1485
+ book = { bids: /* @__PURE__ */ new Map(), asks: /* @__PURE__ */ new Map() };
1486
+ this.venueBooks.set(venue, book);
1487
+ }
1488
+ const explicit = delta.venueDeltaBooks[venue];
1489
+ for (const [levels2, changes] of [
1490
+ [book.bids, mergeVenueLevelChanges(explicit == null ? void 0 : explicit.bidChanges, impliedBids[venue])],
1491
+ [book.asks, mergeVenueLevelChanges(explicit == null ? void 0 : explicit.askChanges, impliedAsks[venue])]
1492
+ ]) {
1493
+ for (const [price, size] of changes) {
1494
+ if (size === 0) levels2.delete(toPriceKey(price));
1495
+ else levels2.set(toPriceKey(price), { price, size });
1496
+ }
1497
+ }
1498
+ }
1499
+ for (const [levels2, changes] of [
1500
+ [this.bids, delta.bidChanges],
1501
+ [this.asks, delta.askChanges]
1502
+ ]) {
1503
+ for (const [price, size, venues] of changes) {
1504
+ if (size === 0) levels2.delete(toPriceKey(price));
1505
+ else levels2.set(toPriceKey(price), { price, size, venues: __spreadValues({}, venues) });
1506
+ }
1507
+ }
1508
+ this.metadata = __spreadProps(__spreadValues({}, this.metadata), {
1509
+ outcomeId: (_a = this.metadata.outcomeId) != null ? _a : delta.outcomeId,
1510
+ venues: __spreadValues(__spreadValues({}, this.metadata.venues), delta.venues),
1511
+ midpoint: delta.midpoint,
1512
+ spread: delta.spread,
1513
+ seq: delta.seq,
1514
+ checksum: delta.checksum,
1515
+ timestamp: delta.timestamp
1516
+ });
1517
+ this.checksumValue = checksum;
1518
+ this.cached = null;
1519
+ return checksum;
1520
+ }
1521
+ view() {
1522
+ if (this.cached) return this.cached;
1523
+ const bids = (levels2) => [...levels2.values()].sort((a, b) => b.price - a.price);
1524
+ const asks = (levels2) => [...levels2.values()].sort((a, b) => a.price - b.price);
1525
+ this.cached = __spreadProps(__spreadValues({}, this.metadata), {
1526
+ bids: bids(this.bids),
1527
+ asks: asks(this.asks),
1528
+ venueOrderbooks: Object.fromEntries(
1529
+ [...this.venueBooks].map(([venue, book]) => [
1530
+ venue,
1531
+ { bids: bids(book.bids), asks: asks(book.asks) }
1532
+ ])
1533
+ )
1534
+ });
1535
+ return this.cached;
1536
+ }
1537
+ };
1167
1538
 
1168
1539
  // src/agg-websocket.ts
1169
1540
  var RECONNECT_BASE_MS = 1e3;
@@ -1203,20 +1574,74 @@ var AggWebSocket = class {
1203
1574
  this.staleTimer = null;
1204
1575
  this.lastMessageTime = 0;
1205
1576
  this.destroyed = false;
1206
- var _a, _b;
1577
+ this.historyControllers = /* @__PURE__ */ new Set();
1578
+ var _a, _b, _c;
1579
+ this.deferBookViews = (_a = options.deferBookViews) != null ? _a : false;
1207
1580
  this.url = options.url;
1208
- this.callbacks = (_a = options.callbacks) != null ? _a : {};
1581
+ this.callbacks = (_b = options.callbacks) != null ? _b : {};
1209
1582
  this.initialToken = options.initialToken;
1210
- this.enableLogs = (_b = options.enableLogs) != null ? _b : false;
1583
+ this.enableLogs = (_c = options.enableLogs) != null ? _c : false;
1211
1584
  }
1212
1585
  // ── Public API ──
1213
1586
  get connected() {
1214
1587
  return this._connected;
1215
1588
  }
1589
+ /** Decode dynamic authenticated REST bytes in the same processing environment as live data. */
1590
+ decodeHistory(payload) {
1591
+ return __async(this, null, function* () {
1592
+ return decodeChartHistory(payload);
1593
+ });
1594
+ }
1595
+ /** Immutable history decoder. Browser adapters execute this inside their Worker. */
1596
+ loadHistory(manifest, options) {
1597
+ return __async(this, null, function* () {
1598
+ var _a, _b, _c;
1599
+ if (this.destroyed) throw new Error("Market-data client disposed");
1600
+ const controller = new AbortController();
1601
+ const abort = () => {
1602
+ var _a2;
1603
+ return controller.abort((_a2 = options == null ? void 0 : options.signal) == null ? void 0 : _a2.reason);
1604
+ };
1605
+ (_a = options == null ? void 0 : options.signal) == null ? void 0 : _a.addEventListener("abort", abort, { once: true });
1606
+ if ((_b = options == null ? void 0 : options.signal) == null ? void 0 : _b.aborted) abort();
1607
+ this.historyControllers.add(controller);
1608
+ try {
1609
+ controller.signal.throwIfAborted();
1610
+ const result = yield loadImmutableHistory({ manifest, signal: controller.signal });
1611
+ controller.signal.throwIfAborted();
1612
+ return result;
1613
+ } finally {
1614
+ this.historyControllers.delete(controller);
1615
+ (_c = options == null ? void 0 : options.signal) == null ? void 0 : _c.removeEventListener("abort", abort);
1616
+ }
1617
+ });
1618
+ }
1619
+ /** Optional framework-independent host diagnostics, cleared on disposal. */
1620
+ setHostDiagnostics(provider) {
1621
+ this.hostDiagnostics = provider;
1622
+ }
1623
+ /** On-demand ownership diagnostics; contains no credentials or user/account data. */
1624
+ getDiagnostics() {
1625
+ var _a;
1626
+ return {
1627
+ connected: this.connected,
1628
+ host: (_a = this.hostDiagnostics) == null ? void 0 : _a.call(this),
1629
+ transport: this.destroyed ? "disposed" : "direct",
1630
+ orderbookOutcomeIds: this.activeOrderbookMarkets(),
1631
+ tradeOutcomeIds: this.activeTradeMarkets(),
1632
+ arbMarketIds: this.activeArbMarkets(),
1633
+ arbFeedSubscribers: this.arbFeedCbs.size,
1634
+ cachedBooks: this.books.size,
1635
+ cachedArb: this.latestArb.size,
1636
+ resyncingBooks: this.resyncing.size,
1637
+ pendingResnapshots: this.pendingResnapshots.size,
1638
+ pendingHistoryRequests: this.historyControllers.size
1639
+ };
1640
+ }
1216
1641
  /** Get the current orderbook state for an outcome (or null if no snapshot received yet). */
1217
1642
  getBook(outcomeId) {
1218
- var _a;
1219
- return (_a = this.books.get(outcomeId)) != null ? _a : null;
1643
+ var _a, _b;
1644
+ return (_b = (_a = this.books.get(outcomeId)) == null ? void 0 : _a.view()) != null ? _b : null;
1220
1645
  }
1221
1646
  /** Whether an outcome is currently resyncing (waiting for fresh snapshot). */
1222
1647
  isResyncing(outcomeId) {
@@ -1299,6 +1724,8 @@ var AggWebSocket = class {
1299
1724
  if (!sub) {
1300
1725
  sub = { refCount: 0, tradeRefs: 0 };
1301
1726
  this.subs.set(outcomeId, sub);
1727
+ }
1728
+ if (sub.refCount === 0) {
1302
1729
  this.pendingSubs.add(outcomeId);
1303
1730
  this.ensureConnected();
1304
1731
  if (this._connected) {
@@ -1309,13 +1736,19 @@ var AggWebSocket = class {
1309
1736
  }
1310
1737
  }
1311
1738
  sub.refCount++;
1739
+ let released = false;
1312
1740
  return () => {
1741
+ if (released) return;
1742
+ released = true;
1313
1743
  const s = this.subs.get(outcomeId);
1314
1744
  if (!s) return;
1315
1745
  s.refCount--;
1316
- if (s.refCount <= 0 && s.tradeRefs <= 0) {
1317
- this.subs.delete(outcomeId);
1318
- this.books.delete(outcomeId);
1746
+ if (s.refCount <= 0) {
1747
+ s.refCount = 0;
1748
+ if (s.tradeRefs <= 0) {
1749
+ this.subs.delete(outcomeId);
1750
+ this.books.delete(outcomeId);
1751
+ }
1319
1752
  this.resyncing.delete(outcomeId);
1320
1753
  if (this._connected) {
1321
1754
  if (this.pendingFlushSubs.has(outcomeId)) {
@@ -1355,7 +1788,10 @@ var AggWebSocket = class {
1355
1788
  }
1356
1789
  }
1357
1790
  sub.tradeRefs++;
1791
+ let released = false;
1358
1792
  return () => {
1793
+ if (released) return;
1794
+ released = true;
1359
1795
  const s = this.subs.get(outcomeId);
1360
1796
  if (!s) return;
1361
1797
  s.tradeRefs--;
@@ -1373,6 +1809,8 @@ var AggWebSocket = class {
1373
1809
  this.pendingResnapshots.delete(outcomeId);
1374
1810
  if (s.refCount <= 0) {
1375
1811
  this.subs.delete(outcomeId);
1812
+ this.books.delete(outcomeId);
1813
+ this.resyncing.delete(outcomeId);
1376
1814
  }
1377
1815
  }
1378
1816
  };
@@ -1411,7 +1849,12 @@ var AggWebSocket = class {
1411
1849
  /** Permanently destroy. Cannot reconnect after this. */
1412
1850
  destroy() {
1413
1851
  this.destroyed = true;
1852
+ for (const controller of this.historyControllers)
1853
+ controller.abort(new Error("Market-data client disposed"));
1854
+ this.historyControllers.clear();
1414
1855
  this.disconnect();
1856
+ this.hostDiagnostics = void 0;
1857
+ this.callbacks = {};
1415
1858
  this.subs.clear();
1416
1859
  this.books.clear();
1417
1860
  this.resyncing.clear();
@@ -1446,6 +1889,7 @@ var AggWebSocket = class {
1446
1889
  const ws = new WebSocket(this.url);
1447
1890
  this.ws = ws;
1448
1891
  ws.onopen = () => {
1892
+ if (this.destroyed || this.ws !== ws) return;
1449
1893
  this.connecting = false;
1450
1894
  this.reconnectAttempts = 0;
1451
1895
  this.resetStaleTimer();
@@ -1454,7 +1898,10 @@ var AggWebSocket = class {
1454
1898
  }
1455
1899
  };
1456
1900
  ws.onmessage = (event) => {
1901
+ var _a, _b;
1902
+ if (this.destroyed || this.ws !== ws) return;
1457
1903
  this.lastMessageTime = Date.now();
1904
+ (_b = (_a = this.callbacks).onFrameReceived) == null ? void 0 : _b.call(_a, this.lastMessageTime);
1458
1905
  this.resetStaleTimer();
1459
1906
  try {
1460
1907
  const raw = JSON.parse(event.data);
@@ -1517,8 +1964,14 @@ var AggWebSocket = class {
1517
1964
  }
1518
1965
  // ── Message handling ──
1519
1966
  handleMessage(raw) {
1520
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
1967
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
1521
1968
  const type = raw.type;
1969
+ if (["orderbook_snapshot", "orderbook_delta", "trade"].includes(type) && !validMarketFrame(raw)) {
1970
+ (_b = (_a = this.callbacks).onError) == null ? void 0 : _b.call(_a, { type: "error", message: "Invalid market-data frame" });
1971
+ if (typeof raw.outcomeId === "string" && this.subs.has(raw.outcomeId))
1972
+ this.requestResnapshot(raw.outcomeId);
1973
+ return;
1974
+ }
1522
1975
  switch (type) {
1523
1976
  case "orderbook_snapshot":
1524
1977
  this.handleSnapshot(raw);
@@ -1533,22 +1986,22 @@ var AggWebSocket = class {
1533
1986
  this.handleHeartbeat(raw);
1534
1987
  break;
1535
1988
  case "subscribed":
1536
- (_b = (_a = this.callbacks).onSubscribed) == null ? void 0 : _b.call(_a, raw);
1989
+ (_d = (_c = this.callbacks).onSubscribed) == null ? void 0 : _d.call(_c, raw);
1537
1990
  break;
1538
1991
  case "unsubscribed":
1539
- (_d = (_c = this.callbacks).onUnsubscribed) == null ? void 0 : _d.call(_c, raw);
1992
+ (_f = (_e = this.callbacks).onUnsubscribed) == null ? void 0 : _f.call(_e, raw);
1540
1993
  break;
1541
1994
  case "connected":
1542
1995
  this.handleConnected(raw);
1543
1996
  break;
1544
1997
  case "authenticated":
1545
- (_f = (_e = this.callbacks).onAuthenticated) == null ? void 0 : _f.call(_e, raw);
1998
+ (_h = (_g = this.callbacks).onAuthenticated) == null ? void 0 : _h.call(_g, raw);
1546
1999
  break;
1547
2000
  case "market_resolved":
1548
2001
  this.handleMarketResolved(raw);
1549
2002
  break;
1550
2003
  case "error":
1551
- (_h = (_g = this.callbacks).onError) == null ? void 0 : _h.call(_g, raw);
2004
+ (_j = (_i = this.callbacks).onError) == null ? void 0 : _j.call(_i, raw);
1552
2005
  break;
1553
2006
  case "order_submitted":
1554
2007
  this.handleOrderSubmitted(raw);
@@ -1557,13 +2010,13 @@ var AggWebSocket = class {
1557
2010
  this.handleBalanceUpdate(raw);
1558
2011
  break;
1559
2012
  case "order_event":
1560
- (_j = (_i = this.callbacks).onOrderEvent) == null ? void 0 : _j.call(_i, raw);
2013
+ (_l = (_k = this.callbacks).onOrderEvent) == null ? void 0 : _l.call(_k, raw);
1561
2014
  break;
1562
2015
  case "redeem_event":
1563
2016
  this.handleRedeemEvent(raw);
1564
2017
  break;
1565
2018
  case "withdrawal_lifecycle":
1566
- (_l = (_k = this.callbacks).onWithdrawalLifecycle) == null ? void 0 : _l.call(_k, raw);
2019
+ (_n = (_m = this.callbacks).onWithdrawalLifecycle) == null ? void 0 : _n.call(_m, raw);
1567
2020
  break;
1568
2021
  case "arb_market_update": {
1569
2022
  const arbMsg = raw;
@@ -1572,13 +2025,13 @@ var AggWebSocket = class {
1572
2025
  if (arbEntry) {
1573
2026
  for (const cb of arbEntry.cbs) cb(arbMsg);
1574
2027
  }
1575
- (_n = (_m = this.callbacks).onArbMarket) == null ? void 0 : _n.call(_m, arbMsg);
2028
+ (_p = (_o = this.callbacks).onArbMarket) == null ? void 0 : _p.call(_o, arbMsg);
1576
2029
  break;
1577
2030
  }
1578
2031
  case "arb_feed_batch": {
1579
2032
  const feedMsg = raw;
1580
2033
  for (const cb of this.arbFeedCbs) cb(feedMsg);
1581
- (_p = (_o = this.callbacks).onArbFeed) == null ? void 0 : _p.call(_o, feedMsg);
2034
+ (_r = (_q = this.callbacks).onArbFeed) == null ? void 0 : _r.call(_q, feedMsg);
1582
2035
  break;
1583
2036
  }
1584
2037
  }
@@ -1630,10 +2083,11 @@ var AggWebSocket = class {
1630
2083
  }
1631
2084
  }
1632
2085
  handleSnapshot(raw) {
1633
- var _a, _b;
2086
+ var _a, _b, _c, _d;
2087
+ if (!this.subs.has(raw.outcomeId)) return;
1634
2088
  const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1635
2089
  const book = snapshotToOrderbook(msg);
1636
- this.books.set(msg.outcomeId, book);
2090
+ this.books.set(msg.outcomeId, new IndexedOrderbook(book));
1637
2091
  this.resyncing.delete(msg.outcomeId);
1638
2092
  this.pendingResnapshots.delete(msg.outcomeId);
1639
2093
  this.debugOrderbook("snapshot_applied", {
@@ -1643,29 +2097,18 @@ var AggWebSocket = class {
1643
2097
  bidsTop: this.summarizeTopLevels(book.bids),
1644
2098
  asksTop: this.summarizeTopLevels(book.asks)
1645
2099
  });
1646
- (_b = (_a = this.callbacks).onSnapshot) == null ? void 0 : _b.call(_a, msg.outcomeId, book);
2100
+ (_b = (_a = this.callbacks).onBookTick) == null ? void 0 : _b.call(_a, msg.outcomeId, {
2101
+ midpoint: msg.midpoint,
2102
+ timestamp: msg.timestamp,
2103
+ source: "snapshot"
2104
+ });
2105
+ if (!this.deferBookViews) (_d = (_c = this.callbacks).onSnapshot) == null ? void 0 : _d.call(_c, msg.outcomeId, book);
1647
2106
  }
1648
2107
  handleDelta(raw) {
1649
- var _a, _b;
2108
+ var _a, _b, _c, _d;
2109
+ if (!this.subs.has(raw.outcomeId)) return;
1650
2110
  const outcomeId = raw.outcomeId;
1651
2111
  const current = this.books.get(outcomeId);
1652
- const deltaTop = {
1653
- bids: raw.bidChanges.slice(0, ORDERBOOK_DEBUG_TOP_LEVELS).map(([price, size]) => ({
1654
- price,
1655
- size
1656
- })),
1657
- asks: raw.askChanges.slice(0, ORDERBOOK_DEBUG_TOP_LEVELS).map(([price, size]) => ({
1658
- price,
1659
- size
1660
- }))
1661
- };
1662
- this.debugOrderbook("delta_received", {
1663
- outcomeId,
1664
- seq: raw.seq,
1665
- checksum: raw.checksum,
1666
- hasCurrentBook: Boolean(current),
1667
- deltaTop
1668
- });
1669
2112
  if (!current) {
1670
2113
  this.debugOrderbook("delta_missing_snapshot", { outcomeId, seq: raw.seq });
1671
2114
  this.requestResnapshot(outcomeId);
@@ -1696,21 +2139,7 @@ var AggWebSocket = class {
1696
2139
  return;
1697
2140
  }
1698
2141
  const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1699
- const newBook = applyOrderbookDelta(current, msg);
1700
- this.debugOrderbook("delta_applied_locally", {
1701
- outcomeId,
1702
- fromSeq: current.seq,
1703
- toSeq: newBook.seq,
1704
- beforeTop: {
1705
- bids: this.summarizeTopLevels(current.bids),
1706
- asks: this.summarizeTopLevels(current.asks)
1707
- },
1708
- afterTop: {
1709
- bids: this.summarizeTopLevels(newBook.bids),
1710
- asks: this.summarizeTopLevels(newBook.asks)
1711
- }
1712
- });
1713
- const computed = computeChecksum(newBook);
2142
+ const computed = current.apply(msg);
1714
2143
  if (computed !== msg.checksum) {
1715
2144
  this.debugOrderbook("delta_checksum_mismatch", {
1716
2145
  outcomeId,
@@ -1728,18 +2157,16 @@ var AggWebSocket = class {
1728
2157
  this.requestResnapshot(outcomeId);
1729
2158
  return;
1730
2159
  }
1731
- this.books.set(outcomeId, newBook);
1732
- this.debugOrderbook("delta_committed", {
1733
- outcomeId,
1734
- seq: newBook.seq,
1735
- checksum: newBook.checksum,
1736
- bidsTop: this.summarizeTopLevels(newBook.bids),
1737
- asksTop: this.summarizeTopLevels(newBook.asks)
2160
+ (_b = (_a = this.callbacks).onBookTick) == null ? void 0 : _b.call(_a, outcomeId, {
2161
+ midpoint: msg.midpoint,
2162
+ timestamp: msg.timestamp,
2163
+ source: "delta"
1738
2164
  });
1739
- (_b = (_a = this.callbacks).onDelta) == null ? void 0 : _b.call(_a, outcomeId, newBook);
2165
+ if (!this.deferBookViews) (_d = (_c = this.callbacks).onDelta) == null ? void 0 : _d.call(_c, outcomeId, current.view());
1740
2166
  }
1741
2167
  handleTrade(raw) {
1742
2168
  var _a, _b;
2169
+ if (!this.subs.has(raw.outcomeId)) return;
1743
2170
  const msg = __spreadProps(__spreadValues({}, raw), { timestamp: raw.timestamp / 1e3 });
1744
2171
  (_b = (_a = this.callbacks).onTrade) == null ? void 0 : _b.call(_a, msg);
1745
2172
  }
@@ -1900,8 +2327,8 @@ var AggWebSocket = class {
1900
2327
  var _a, _b;
1901
2328
  (_b = (_a = this.callbacks).onDiagnostics) == null ? void 0 : _b.call(_a, event);
1902
2329
  }
1903
- summarizeTopLevels(levels) {
1904
- return levels.slice(0, ORDERBOOK_DEBUG_TOP_LEVELS).map((level) => ({ price: level.price, size: level.size }));
2330
+ summarizeTopLevels(levels2) {
2331
+ return levels2.slice(0, ORDERBOOK_DEBUG_TOP_LEVELS).map((level) => ({ price: level.price, size: level.size }));
1905
2332
  }
1906
2333
  debugOrderbook(event, payload) {
1907
2334
  if (!this.isOrderbookDebugEnabled()) return;
@@ -2002,7 +2429,7 @@ var AggClient = class {
2002
2429
  this.authChannel = null;
2003
2430
  /** In-flight refresh promise — serializes concurrent 401 retries so only one refresh runs. */
2004
2431
  this.pendingRefresh = null;
2005
- var _a, _b;
2432
+ var _a, _b, _c;
2006
2433
  if (!options.appId && !options.adminKey) {
2007
2434
  throw new Error("AggClient requires at least one of appId or adminKey");
2008
2435
  }
@@ -2012,9 +2439,11 @@ var AggClient = class {
2012
2439
  this.signer = options.signer;
2013
2440
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
2014
2441
  this.wsUrl = options.wsUrl;
2442
+ this.preferImmutableHistory = (_a = options.preferImmutableHistory) != null ? _a : false;
2443
+ this.webSocketFactory = options.webSocketFactory;
2015
2444
  this.authOptions = options.auth;
2016
- this.storageKey = `agg_auth_${(_a = this._appId) != null ? _a : "default"}`;
2017
- this.authDelivery = (_b = options.authDelivery) != null ? _b : "body";
2445
+ this.storageKey = `agg_auth_${(_b = this._appId) != null ? _b : "default"}`;
2446
+ this.authDelivery = (_c = options.authDelivery) != null ? _c : "body";
2018
2447
  this.shouldPersistSession = options.persistSession !== false;
2019
2448
  this.restoreSession();
2020
2449
  this.initAuthChannel();
@@ -2142,7 +2571,7 @@ var AggClient = class {
2142
2571
  * @param path - API path (e.g. `/venue-events`)
2143
2572
  * @param init - Fetch init options, plus optional `query` object for URL search params
2144
2573
  */
2145
- request(path, init) {
2574
+ request(path, init, decode) {
2146
2575
  return __async(this, null, function* () {
2147
2576
  const response = yield this.rawFetch(path, init);
2148
2577
  if (response.status === 401 && this.accessToken && !AUTH_PATHS.has(path)) {
@@ -2153,7 +2582,7 @@ var AggClient = class {
2153
2582
  return this.throwResponseError(retryResponse);
2154
2583
  }
2155
2584
  if (retryResponse.status === 204) return void 0;
2156
- return retryResponse.json();
2585
+ return decode ? decode(retryResponse) : retryResponse.json();
2157
2586
  } catch (e) {
2158
2587
  this.clearAccessToken();
2159
2588
  return this.throwResponseError(response);
@@ -2163,7 +2592,7 @@ var AggClient = class {
2163
2592
  return this.throwResponseError(response);
2164
2593
  }
2165
2594
  if (response.status === 204) return void 0;
2166
- return response.json();
2595
+ return decode ? decode(response) : response.json();
2167
2596
  });
2168
2597
  }
2169
2598
  rawFetch(path, init) {
@@ -2200,12 +2629,15 @@ var AggClient = class {
2200
2629
  }
2201
2630
  throwResponseError(response) {
2202
2631
  return __async(this, null, function* () {
2632
+ var _a;
2203
2633
  let message = response.status === 404 ? "Market not found" : `AGG API error: ${response.status} ${response.statusText}`;
2204
2634
  let code;
2205
2635
  let retryable;
2636
+ let retryAfterMs = parseRetryAfterHeader((_a = response.headers) == null ? void 0 : _a.get("Retry-After"));
2206
2637
  let errors;
2207
2638
  try {
2208
2639
  const body = yield response.json();
2640
+ retryAfterMs != null ? retryAfterMs : retryAfterMs = parseRetryAfterSeconds(body == null ? void 0 : body.retryAfter);
2209
2641
  if (typeof body.message === "string" && body.message.length > 0) {
2210
2642
  message = body.message;
2211
2643
  }
@@ -2224,7 +2656,7 @@ var AggClient = class {
2224
2656
  }
2225
2657
  } catch (e) {
2226
2658
  }
2227
- throw new AggApiError(message, response.status, { code, retryable, errors });
2659
+ throw new AggApiError(message, response.status, { code, retryable, retryAfterMs, errors });
2228
2660
  });
2229
2661
  }
2230
2662
  /**
@@ -2651,14 +3083,19 @@ Issued At: ${issuedAt}`;
2651
3083
  *
2652
3084
  * Pass `quoteId` to fetch every leg of a multi-venue split route in
2653
3085
  * one call — `useQuoteSummary(quoteId)` reduces the list into a
2654
- * rollup (total cost, share-weighted avg price, to-win).
3086
+ * rollup (total cost, share-weighted avg price, to-win). Pass
3087
+ * `venueMarketIds` to narrow to the orders on those markets. Sent as
3088
+ * repeated params in one request; batch long lists yourself (or use
3089
+ * `useExecutionOrdersForMarkets`) to keep the URL short.
2655
3090
  */
2656
3091
  getExecutionOrders() {
2657
3092
  return __async(this, arguments, function* (params = {}) {
3093
+ var _a;
2658
3094
  const query = {};
2659
3095
  if (params.orderId) query.orderId = params.orderId;
2660
3096
  if (params.quoteId) query.quoteId = params.quoteId;
2661
3097
  if (params.externalId) query.externalId = params.externalId;
3098
+ if ((_a = params.venueMarketIds) == null ? void 0 : _a.length) query.venueMarketIds = params.venueMarketIds;
2662
3099
  if (params.status) query.status = params.status;
2663
3100
  if (params.orderType) query.orderType = params.orderType;
2664
3101
  if (params.mode) query.mode = params.mode;
@@ -2708,16 +3145,92 @@ Issued At: ${issuedAt}`;
2708
3145
  });
2709
3146
  });
2710
3147
  }
2711
- /** Get managed wallet balances, including per-chain cash balances and per-venue position balances. */
3148
+ /**
3149
+ * Get cash balances and per-venue position balances.
3150
+ *
3151
+ * `custody: "self"` returns the wallets linked to this account — plus their
3152
+ * Polymarket deposit and legacy wallets — instead of the managed wallet. Each
3153
+ * chain row then carries `address` and `custodyKind`; `positions` is
3154
+ * account-wide either way.
3155
+ *
3156
+ * Caveat on the headline number: a chain row with `custodyKind:
3157
+ * "POLYMARKET_LEGACY"` and `legacyKind: "proxy"` holds funds that can only
3158
+ * be spent on Polymarket, but it still counts towards its token's
3159
+ * `totalRaw`/`availableRaw` (a token's totals are always the sum of its
3160
+ * chain rows). If you show "spendable anywhere", subtract those rows.
3161
+ */
2712
3162
  getManagedBalances() {
2713
3163
  return __async(this, arguments, function* (params = {}) {
2714
3164
  const query = {};
2715
3165
  if (params.mode) query.mode = params.mode;
3166
+ if (params.custody) query.custody = params.custody;
2716
3167
  return this.request("/execution/balances", {
2717
3168
  query: Object.keys(query).length ? query : void 0
2718
3169
  });
2719
3170
  });
2720
3171
  }
3172
+ /** Read hosted custody status without creating a signing wallet or account. */
3173
+ getHostedVenueAccount(_0) {
3174
+ return __async(this, arguments, function* (venue, params = {}) {
3175
+ return this.request(
3176
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}`,
3177
+ {
3178
+ query: params.mode ? { mode: params.mode } : void 0
3179
+ }
3180
+ );
3181
+ });
3182
+ }
3183
+ /** Owned exact ledger entries; not balances, fill fees or withdrawal finality. */
3184
+ getHostedVenueActivity(_0) {
3185
+ return __async(this, arguments, function* (venue, params = {}) {
3186
+ return this.request(
3187
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}/activity`,
3188
+ {
3189
+ query: __spreadValues(__spreadValues({}, params.mode ? { mode: params.mode } : {}), params.page !== void 0 ? { page: String(params.page) } : {})
3190
+ }
3191
+ );
3192
+ });
3193
+ }
3194
+ /** Explicit hosted-account creation; no signing wallet or credential upload. */
3195
+ provisionHostedVenueAccount(venue, body) {
3196
+ return __async(this, null, function* () {
3197
+ return this.request(
3198
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}`,
3199
+ {
3200
+ method: "POST",
3201
+ body: JSON.stringify(body)
3202
+ }
3203
+ );
3204
+ });
3205
+ }
3206
+ /** Fetch the owned hosted account's verified Solana USDC deposit address. */
3207
+ getHostedVenueDepositAddress(_0) {
3208
+ return __async(this, arguments, function* (venue, params = {}) {
3209
+ return this.request(
3210
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}/deposit-address`,
3211
+ { query: params.mode ? { mode: params.mode } : void 0 }
3212
+ );
3213
+ });
3214
+ }
3215
+ /** Submit exactly one explicit hosted withdrawal under a caller idempotency key. */
3216
+ withdrawHostedVenue(venue, body) {
3217
+ return __async(this, null, function* () {
3218
+ return this.request(
3219
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}/withdrawals`,
3220
+ { method: "POST", body: JSON.stringify(body) }
3221
+ );
3222
+ });
3223
+ }
3224
+ /** Read only Agg's persisted withdrawal state; this never replays a native request. */
3225
+ getHostedVenueWithdrawal(_0, _1) {
3226
+ return __async(this, arguments, function* (venue, withdrawalId, params = {}) {
3227
+ if (!withdrawalId) throw new Error("withdrawalId is required");
3228
+ return this.request(
3229
+ `/users/me/venue-accounts/${encodeURIComponent(venue)}/withdrawals/${encodeURIComponent(withdrawalId)}`,
3230
+ { query: params.mode ? { mode: params.mode } : void 0 }
3231
+ );
3232
+ });
3233
+ }
2721
3234
  /** Store or replace encrypted credentials for a venue. Secrets are never returned. */
2722
3235
  upsertVenueKey(body) {
2723
3236
  return __async(this, null, function* () {
@@ -2887,6 +3400,17 @@ Issued At: ${issuedAt}`;
2887
3400
  });
2888
3401
  }
2889
3402
  /** Get a single venue event by ID. Requires appId or admin auth. */
3403
+ getEventBootstrap(eventId, options) {
3404
+ return __async(this, null, function* () {
3405
+ return this.request(
3406
+ `/venue-events/${encodeURIComponent(eventId)}/bootstrap`,
3407
+ {
3408
+ query: (options == null ? void 0 : options.limit) ? { limit: String(options.limit) } : void 0,
3409
+ signal: options == null ? void 0 : options.signal
3410
+ }
3411
+ );
3412
+ });
3413
+ }
2890
3414
  getVenueEventById(id, options) {
2891
3415
  return __async(this, null, function* () {
2892
3416
  return this.request(`/venue-events/${encodeURIComponent(id)}`, {
@@ -3015,14 +3539,15 @@ Issued At: ${issuedAt}`;
3015
3539
  });
3016
3540
  }
3017
3541
  /** List venue markets with optional filters. Requires appId or admin auth. Supports cursor-based pagination. */
3018
- getVenueMarkets(options) {
3542
+ getVenueMarkets(options, requestOptions) {
3019
3543
  return __async(this, null, function* () {
3020
- var _a;
3544
+ var _a, _b;
3021
3545
  const query = {};
3022
3546
  if (options == null ? void 0 : options.venue) query.venue = options.venue;
3547
+ if ((_a = options == null ? void 0 : options.externalIdentifier) == null ? void 0 : _a.length) query.externalIdentifier = options.externalIdentifier;
3023
3548
  if (options == null ? void 0 : options.venueEventId) query.venueEventId = options.venueEventId;
3024
3549
  if ((options == null ? void 0 : options.cluster) === false) query.cluster = "false";
3025
- if ((_a = options == null ? void 0 : options.search) == null ? void 0 : _a.trim()) query.search = options.search.trim();
3550
+ if ((_b = options == null ? void 0 : options.search) == null ? void 0 : _b.trim()) query.search = options.search.trim();
3026
3551
  if (options == null ? void 0 : options.matchStatus) query.matchStatus = options.matchStatus;
3027
3552
  if (options == null ? void 0 : options.status) query.status = options.status;
3028
3553
  if ((options == null ? void 0 : options.aggKey) && options.aggKey.length > 0) query.aggKey = options.aggKey;
@@ -3042,7 +3567,8 @@ Issued At: ${issuedAt}`;
3042
3567
  if (options == null ? void 0 : options.sortDir) query.sortDir = options.sortDir;
3043
3568
  if (options == null ? void 0 : options.context) query.context = options.context;
3044
3569
  return this.request("/venue-markets", {
3045
- query: Object.keys(query).length > 0 ? query : void 0
3570
+ query: Object.keys(query).length > 0 ? query : void 0,
3571
+ signal: requestOptions == null ? void 0 : requestOptions.signal
3046
3572
  });
3047
3573
  });
3048
3574
  }
@@ -3144,7 +3670,7 @@ Issued At: ${issuedAt}`;
3144
3670
  * format intentionally omits the param so the server hits the cheaper
3145
3671
  * (and longer-TTL) light cache slot.
3146
3672
  */
3147
- search(params) {
3673
+ search(params, options) {
3148
3674
  return __async(this, null, function* () {
3149
3675
  const query = {
3150
3676
  q: params.q,
@@ -3155,8 +3681,10 @@ Issued At: ${issuedAt}`;
3155
3681
  if (params.limit != null) query.limit = String(params.limit);
3156
3682
  if (params.cursor) query.cursor = params.cursor;
3157
3683
  if (params.deep) query.deepSearch = "true";
3684
+ if (params.mode) query.mode = params.mode;
3158
3685
  return this.request("/search", {
3159
- query
3686
+ query,
3687
+ signal: options == null ? void 0 : options.signal
3160
3688
  });
3161
3689
  });
3162
3690
  }
@@ -3218,6 +3746,7 @@ Issued At: ${issuedAt}`;
3218
3746
  /** Get the canonical TradingView-style bar series for a single venue market outcome. */
3219
3747
  getChartBars(params, options) {
3220
3748
  return __async(this, null, function* () {
3749
+ var _a, _b;
3221
3750
  const resolution = mapChartResolution(params.resolution);
3222
3751
  const query = {
3223
3752
  venueMarketOutcomeId: params.venueMarketOutcomeId,
@@ -3226,12 +3755,39 @@ Issued At: ${issuedAt}`;
3226
3755
  if (resolution) query.resolution = resolution;
3227
3756
  if (params.from != null) query.from = String(params.from);
3228
3757
  if (params.countBack != null) query.countBack = String(params.countBack);
3758
+ if (options == null ? void 0 : options.decode) {
3759
+ const text = yield this.request(
3760
+ "/charts/bars",
3761
+ { query, signal: options.signal },
3762
+ (response) => readChartHistoryText(response)
3763
+ );
3764
+ (_a = options.signal) == null ? void 0 : _a.throwIfAborted();
3765
+ const decoded = yield options.decode(text);
3766
+ (_b = options.signal) == null ? void 0 : _b.throwIfAborted();
3767
+ return decoded;
3768
+ }
3229
3769
  return this.request("/charts/bars", {
3230
3770
  query,
3231
3771
  signal: options == null ? void 0 : options.signal
3232
3772
  });
3233
3773
  });
3234
3774
  }
3775
+ getChartHistoryManifest(params, options) {
3776
+ return __async(this, null, function* () {
3777
+ const query = {
3778
+ venueMarketOutcomeId: params.venueMarketOutcomeId,
3779
+ to: String(params.to)
3780
+ };
3781
+ const resolution = mapChartResolution(params.resolution);
3782
+ if (resolution) query.resolution = resolution;
3783
+ if (params.from != null) query.from = String(params.from);
3784
+ if (params.countBack != null) query.countBack = String(params.countBack);
3785
+ return this.request("/charts/history/manifest", {
3786
+ query,
3787
+ signal: options == null ? void 0 : options.signal
3788
+ });
3789
+ });
3790
+ }
3235
3791
  // --- Batch orderbooks ---
3236
3792
  /** Get live per-venue orderbooks for the explicit venueMarketIds requested. */
3237
3793
  getOrderbooks(params, options) {
@@ -3326,22 +3882,34 @@ Issued At: ${issuedAt}`;
3326
3882
  return params.side ? __spreadProps(__spreadValues({}, response), { side: params.side }) : response;
3327
3883
  });
3328
3884
  }
3885
+ /** Seed subscribed outcomes with one bounded batch of compatible snapshots. */
3886
+ getOutcomeOrderbooks(params, options) {
3887
+ return __async(this, null, function* () {
3888
+ return this.request("/orderbooks/outcomes", {
3889
+ query: __spreadValues({
3890
+ venueMarketOutcomeIds: params.venueMarketOutcomeIds.join(",")
3891
+ }, params.depth == null ? {} : { depth: String(params.depth) }),
3892
+ signal: options == null ? void 0 : options.signal
3893
+ });
3894
+ });
3895
+ }
3329
3896
  /**
3330
3897
  * Create an AggWebSocket instance configured with this client's appId and optional auth token.
3331
3898
  * Requires `wsUrl` to be set in the client options.
3332
3899
  */
3333
3900
  createWebSocket(callbacks, options) {
3334
- var _a;
3901
+ var _a, _b;
3335
3902
  if (!this.wsUrl) {
3336
3903
  throw new Error("AggClient.createWebSocket() requires wsUrl to be set in client options");
3337
3904
  }
3338
3905
  const url = new URL(this.wsUrl);
3339
3906
  if (this._appId) url.searchParams.set("appId", this._appId);
3340
- return new AggWebSocket({
3907
+ const createSocket = (_a = this.webSocketFactory) != null ? _a : ((socketOptions) => new AggWebSocket(socketOptions));
3908
+ return createSocket({
3341
3909
  url: url.toString(),
3342
3910
  callbacks,
3343
3911
  initialToken: this.accessToken,
3344
- enableLogs: (_a = options == null ? void 0 : options.enableLogs) != null ? _a : false
3912
+ enableLogs: (_b = options == null ? void 0 : options.enableLogs) != null ? _b : false
3345
3913
  });
3346
3914
  }
3347
3915
  /** Execute a previously quoted managed trade. Returns pending order IDs. */
@@ -3686,6 +4254,24 @@ var CandleBuilder = class {
3686
4254
  var _a;
3687
4255
  return (_a = this.closedMap.get(interval)) != null ? _a : [];
3688
4256
  }
4257
+ /** Apply a bounded Worker view without recomputing candles from presentation samples. */
4258
+ replaceTail(tail) {
4259
+ var _a, _b;
4260
+ for (const interval of ALL_INTERVALS) {
4261
+ const view = tail[interval];
4262
+ if (view.forming) this.forming.set(interval, __spreadValues({}, view.forming));
4263
+ else this.forming.delete(interval);
4264
+ if (!view.closed.length) continue;
4265
+ const closed = new Map(this.getClosed(interval).map((candle) => [candle.openTime, candle]));
4266
+ for (const candle of view.closed) closed.set(candle.openTime, __spreadValues({}, candle));
4267
+ this.closedMap.set(
4268
+ interval,
4269
+ [...closed.values()].sort((a, b) => a.openTime - b.openTime).slice(-MAX_CLOSED)
4270
+ );
4271
+ }
4272
+ this._midpoint = (_b = (_a = tail["1m"].forming) == null ? void 0 : _a.c) != null ? _b : null;
4273
+ this.markDirty();
4274
+ }
3689
4275
  /** Current midpoint (latest value fed). */
3690
4276
  get midpoint() {
3691
4277
  return this._midpoint;
@@ -3848,6 +4434,7 @@ function createAggClient(options) {
3848
4434
  OrderStatus,
3849
4435
  OrderType,
3850
4436
  RECURRENCE_CADENCES,
4437
+ RETINA_IMAGE_SIZES,
3851
4438
  SELF_CUSTODY_FILL_TIMEOUT_MS,
3852
4439
  SELF_CUSTODY_POLL_INTERVAL_MS,
3853
4440
  TERMINAL_EXECUTION_STATES,
@@ -3881,13 +4468,17 @@ function createAggClient(options) {
3881
4468
  isVenueActive,
3882
4469
  isVenueDataVisible,
3883
4470
  isVenueInactive,
4471
+ loadImmutableHistory,
3884
4472
  mergeCandles,
3885
4473
  mergeClosedCandles,
3886
4474
  normalizeVenueMarketCluster,
3887
4475
  optimizedImageUrl,
4476
+ orderbookCrc32,
3888
4477
  parse,
3889
4478
  parseEmail,
3890
4479
  parseEmailStrict,
4480
+ projectedOrderbookChecksum,
4481
+ retinaImageUrl,
3891
4482
  safeParse,
3892
4483
  snapshotToOrderbook,
3893
4484
  sortVenues,