@agg-build/sdk 4.32.0 → 4.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/{agg-websocket-ClGhhROZ.mjs → agg-websocket-C2neMzGT.mjs} +117 -4
- package/dist/agg-websocket-C2neMzGT.mjs.map +1 -0
- package/dist/{agg-websocket-DsEtBkLk.d.mts → agg-websocket-CvYdVJPc.d.mts} +2452 -2171
- package/dist/agg-websocket-CvYdVJPc.d.mts.map +1 -0
- package/dist/{agg-websocket-DsEtBkLk.d.ts → agg-websocket-CvYdVJPc.d.ts} +2452 -2171
- package/dist/agg-websocket-CvYdVJPc.d.ts.map +1 -0
- package/dist/{agg-websocket-CVZ7laJ4.js → agg-websocket-MYZiMRXn.js} +116 -3
- package/dist/browser.d.mts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/browser.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +568 -100
- package/dist/index.mjs +567 -101
- package/dist/index.mjs.map +1 -1
- package/dist/market-data.d.mts +1 -1
- package/dist/market-data.d.ts +1 -1
- package/dist/market-data.js +1 -1
- package/dist/market-data.mjs +1 -1
- package/package.json +1 -1
- package/dist/agg-websocket-ClGhhROZ.mjs.map +0 -1
- package/dist/agg-websocket-DsEtBkLk.d.mts.map +0 -1
- package/dist/agg-websocket-DsEtBkLk.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as loadImmutableHistory, c as orderbookCrc32, i as snapshotToOrderbook, l as projectedOrderbookChecksum, n as applyOrderbookDelta, r as computeChecksum, s as readChartHistoryText, t as AggWebSocket } from "./agg-websocket-
|
|
1
|
+
import { a as loadImmutableHistory, c as orderbookCrc32, i as snapshotToOrderbook, l as projectedOrderbookChecksum, n as applyOrderbookDelta, r as computeChecksum, s as readChartHistoryText, t as AggWebSocket } from "./agg-websocket-C2neMzGT.mjs";
|
|
2
2
|
import { t as CandleBuilder } from "./candle-builder-BNewIe9K.mjs";
|
|
3
3
|
//#region ../common/src/enums/venue.ts
|
|
4
4
|
/** Use these constants instead of hardcoding "kalshi" or "polymarket". */
|
|
@@ -781,24 +781,6 @@ const isBalanceRefillWithinDailyCap = (minimumRaw, refillAmountRaw, dailyCapRaw,
|
|
|
781
781
|
return BigInt(minimumRaw) <= maximum && BigInt(refillAmountRaw) <= maximum;
|
|
782
782
|
};
|
|
783
783
|
//#endregion
|
|
784
|
-
//#region src/retry-after.ts
|
|
785
|
-
/** Convert explicit retry hints to milliseconds without treating arbitrary strings as dates. */
|
|
786
|
-
function parseRetryAfterSeconds(value) {
|
|
787
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
|
|
788
|
-
const milliseconds = value * 1e3;
|
|
789
|
-
return Number.isSafeInteger(milliseconds) ? milliseconds : void 0;
|
|
790
|
-
}
|
|
791
|
-
function parseRetryAfterHeader(value, now = Date.now()) {
|
|
792
|
-
if (!value) return void 0;
|
|
793
|
-
const text = value.trim();
|
|
794
|
-
if (/^\d+$/.test(text)) return parseRetryAfterSeconds(Number(text));
|
|
795
|
-
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(text)) return void 0;
|
|
796
|
-
const timestamp = Date.parse(text);
|
|
797
|
-
if (!Number.isFinite(timestamp) || new Date(timestamp).toUTCString() !== text) return void 0;
|
|
798
|
-
const milliseconds = timestamp - now;
|
|
799
|
-
return Number.isSafeInteger(milliseconds) && milliseconds >= 0 ? milliseconds : void 0;
|
|
800
|
-
}
|
|
801
|
-
//#endregion
|
|
802
784
|
//#region src/errors.ts
|
|
803
785
|
/**
|
|
804
786
|
* Thrown when the server returns `challenge_required` during auth start.
|
|
@@ -845,6 +827,163 @@ function isAggApiError(value) {
|
|
|
845
827
|
return value instanceof AggApiError;
|
|
846
828
|
}
|
|
847
829
|
//#endregion
|
|
830
|
+
//#region src/venue-event-read-scheduler.ts
|
|
831
|
+
const MAX_TIMER_DELAY_MS = 2147483647;
|
|
832
|
+
const abortError = () => Object.assign(/* @__PURE__ */ new Error("The request was aborted"), { name: "AbortError" });
|
|
833
|
+
/** Instance-local admission for event reads only. Never retries or retains successful data. */
|
|
834
|
+
var VenueEventReadScheduler = class {
|
|
835
|
+
constructor(concurrency = 4, queueLimit = 128) {
|
|
836
|
+
this.concurrency = concurrency;
|
|
837
|
+
this.queueLimit = queueLimit;
|
|
838
|
+
this.jobs = /* @__PURE__ */ new Map();
|
|
839
|
+
this.queue = [];
|
|
840
|
+
this.active = 0;
|
|
841
|
+
this.cooldownUntil = 0;
|
|
842
|
+
}
|
|
843
|
+
read(key, run, signal) {
|
|
844
|
+
if (signal?.aborted) return Promise.reject(abortError());
|
|
845
|
+
let job = this.jobs.get(key);
|
|
846
|
+
if (!job) {
|
|
847
|
+
this.queue = this.queue.filter((entry) => entry.subscribers > 0);
|
|
848
|
+
if (this.queue.length >= this.queueLimit) return Promise.reject(new AggApiError("Too many pending event reads", 429, {
|
|
849
|
+
code: "client_read_queue_full",
|
|
850
|
+
retryable: true,
|
|
851
|
+
retryAfterMs: Math.max(1e3, this.cooldownUntil - Date.now())
|
|
852
|
+
}));
|
|
853
|
+
let resolve;
|
|
854
|
+
let reject;
|
|
855
|
+
job = {
|
|
856
|
+
key,
|
|
857
|
+
run,
|
|
858
|
+
promise: new Promise((res, rej) => {
|
|
859
|
+
resolve = res;
|
|
860
|
+
reject = rej;
|
|
861
|
+
}),
|
|
862
|
+
resolve,
|
|
863
|
+
reject,
|
|
864
|
+
controller: new AbortController(),
|
|
865
|
+
subscribers: 0
|
|
866
|
+
};
|
|
867
|
+
this.jobs.set(key, job);
|
|
868
|
+
this.queue.push(job);
|
|
869
|
+
}
|
|
870
|
+
const entry = job;
|
|
871
|
+
entry.subscribers += 1;
|
|
872
|
+
const result = new Promise((resolve, reject) => {
|
|
873
|
+
let settled = false;
|
|
874
|
+
const finish = (callback) => {
|
|
875
|
+
if (settled) return;
|
|
876
|
+
settled = true;
|
|
877
|
+
signal?.removeEventListener("abort", onAbort);
|
|
878
|
+
entry.subscribers -= 1;
|
|
879
|
+
if (entry.subscribers === 0) {
|
|
880
|
+
if (this.jobs.get(key) === entry) this.jobs.delete(key);
|
|
881
|
+
entry.controller.abort();
|
|
882
|
+
this.pump();
|
|
883
|
+
}
|
|
884
|
+
callback();
|
|
885
|
+
};
|
|
886
|
+
const onAbort = () => finish(() => reject(abortError()));
|
|
887
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
888
|
+
entry.promise.then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error)));
|
|
889
|
+
});
|
|
890
|
+
this.pump();
|
|
891
|
+
return result;
|
|
892
|
+
}
|
|
893
|
+
reset() {
|
|
894
|
+
for (const job of this.jobs.values()) {
|
|
895
|
+
job.controller.abort();
|
|
896
|
+
job.reject(abortError());
|
|
897
|
+
}
|
|
898
|
+
this.jobs.clear();
|
|
899
|
+
this.queue = [];
|
|
900
|
+
this.cooldownUntil = 0;
|
|
901
|
+
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
902
|
+
this.timer = void 0;
|
|
903
|
+
}
|
|
904
|
+
pump() {
|
|
905
|
+
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
906
|
+
this.timer = void 0;
|
|
907
|
+
this.queue = this.queue.filter((job) => job.subscribers > 0);
|
|
908
|
+
if (this.queue.length === 0 || this.active >= this.concurrency) return;
|
|
909
|
+
const delay = this.cooldownUntil - Date.now();
|
|
910
|
+
if (delay > 0) {
|
|
911
|
+
this.timer = setTimeout(() => this.pump(), Math.min(delay, MAX_TIMER_DELAY_MS));
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
while (this.active < this.concurrency && this.queue.length > 0) {
|
|
915
|
+
const job = this.queue.shift();
|
|
916
|
+
this.active += 1;
|
|
917
|
+
this.execute(job);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
async execute(job) {
|
|
921
|
+
try {
|
|
922
|
+
job.resolve(await job.run(job.controller.signal));
|
|
923
|
+
} catch (error) {
|
|
924
|
+
if (!job.controller.signal.aborted && isAggApiError(error) && error.status === 429) {
|
|
925
|
+
const hint = error.retryAfterMs;
|
|
926
|
+
const delay = typeof hint === "number" && Number.isSafeInteger(hint) && hint >= 0 ? hint : 1e3;
|
|
927
|
+
this.cooldownUntil = Math.max(this.cooldownUntil, Date.now() + delay + 1 + Math.floor(Math.random() * 250));
|
|
928
|
+
}
|
|
929
|
+
job.reject(error);
|
|
930
|
+
} finally {
|
|
931
|
+
if (this.jobs.get(job.key) === job) this.jobs.delete(job.key);
|
|
932
|
+
this.active -= 1;
|
|
933
|
+
this.pump();
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
//#endregion
|
|
938
|
+
//#region src/execution-poll-wakeup.ts
|
|
939
|
+
/** A coalesced socket hint can interrupt one poll delay or precede it. */
|
|
940
|
+
var ExecutionPollWakeup = class {
|
|
941
|
+
constructor() {
|
|
942
|
+
this.pending = false;
|
|
943
|
+
}
|
|
944
|
+
notify() {
|
|
945
|
+
if (this.resume) this.resume();
|
|
946
|
+
else this.pending = true;
|
|
947
|
+
}
|
|
948
|
+
wait(ms) {
|
|
949
|
+
if (this.pending) {
|
|
950
|
+
this.pending = false;
|
|
951
|
+
return Promise.resolve();
|
|
952
|
+
}
|
|
953
|
+
return new Promise((resolve) => {
|
|
954
|
+
const finish = () => {
|
|
955
|
+
clearTimeout(timer);
|
|
956
|
+
this.resume = void 0;
|
|
957
|
+
resolve();
|
|
958
|
+
};
|
|
959
|
+
const timer = setTimeout(finish, ms);
|
|
960
|
+
this.resume = finish;
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
dispose() {
|
|
964
|
+
this.resume?.();
|
|
965
|
+
this.pending = false;
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
//#endregion
|
|
969
|
+
//#region src/retry-after.ts
|
|
970
|
+
/** Convert explicit retry hints to milliseconds without treating arbitrary strings as dates. */
|
|
971
|
+
function parseRetryAfterSeconds(value) {
|
|
972
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
|
|
973
|
+
const milliseconds = value * 1e3;
|
|
974
|
+
return Number.isSafeInteger(milliseconds) ? milliseconds : void 0;
|
|
975
|
+
}
|
|
976
|
+
function parseRetryAfterHeader(value, now = Date.now()) {
|
|
977
|
+
if (!value) return void 0;
|
|
978
|
+
const text = value.trim();
|
|
979
|
+
if (/^\d+$/.test(text)) return parseRetryAfterSeconds(Number(text));
|
|
980
|
+
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(text)) return void 0;
|
|
981
|
+
const timestamp = Date.parse(text);
|
|
982
|
+
if (!Number.isFinite(timestamp) || new Date(timestamp).toUTCString() !== text) return void 0;
|
|
983
|
+
const milliseconds = timestamp - now;
|
|
984
|
+
return Number.isSafeInteger(milliseconds) && milliseconds >= 0 ? milliseconds : void 0;
|
|
985
|
+
}
|
|
986
|
+
//#endregion
|
|
848
987
|
//#region src/eip55.ts
|
|
849
988
|
/**
|
|
850
989
|
* EIP-55 address checksumming, self-contained.
|
|
@@ -1131,6 +1270,227 @@ function withTimeout(promise, ms) {
|
|
|
1131
1270
|
});
|
|
1132
1271
|
}
|
|
1133
1272
|
//#endregion
|
|
1273
|
+
//#region src/catalog-feed.ts
|
|
1274
|
+
/** Recommended REST/WS overlap: re-read this much before the resume point. */
|
|
1275
|
+
const CATALOG_DEFAULT_OVERLAP_MS = 6e4;
|
|
1276
|
+
/** Live frames held during a catch-up. Past this the buffer is dropped and the catch-up re-reads. */
|
|
1277
|
+
const LIVE_BUFFER_MAX = 1e4;
|
|
1278
|
+
const RETRY_BASE_MS = 1e3;
|
|
1279
|
+
const RETRY_MAX_MS = 3e4;
|
|
1280
|
+
/** Live applies between dedupe-map prunes. */
|
|
1281
|
+
const PRUNE_EVERY = 1e3;
|
|
1282
|
+
/**
|
|
1283
|
+
* `GET /catalog/changes` answers 410 `resync_required` when the position is
|
|
1284
|
+
* older than the 24h retention window: re-list with the list endpoints, then
|
|
1285
|
+
* resume from the time that re-list started.
|
|
1286
|
+
*/
|
|
1287
|
+
function isCatalogResyncRequired(err) {
|
|
1288
|
+
return isAggApiError(err) && (err.code === "resync_required" || err.status === 410);
|
|
1289
|
+
}
|
|
1290
|
+
/** See {@link AggClient.watchCatalog}. */
|
|
1291
|
+
function watchCatalog(deps, options) {
|
|
1292
|
+
if (Number.isNaN(toMs(options.since))) throw new Error("watchCatalog: `since` must be a valid date or ISO string");
|
|
1293
|
+
const overlapMs = options.overlapMs ?? 6e4;
|
|
1294
|
+
const filter = {
|
|
1295
|
+
...options.venues?.length ? { venues: options.venues } : {},
|
|
1296
|
+
...options.entities?.length ? { entities: options.entities } : {}
|
|
1297
|
+
};
|
|
1298
|
+
let sinceMs = toMs(options.since);
|
|
1299
|
+
let lastChangedAtMs;
|
|
1300
|
+
let lastChangedAt;
|
|
1301
|
+
/**
|
|
1302
|
+
* Dedupe state per `${entity}:${id}`. Pruned below `lastChangedAt - 2 *
|
|
1303
|
+
* overlap`: catch-ups re-read from `lastChangedAt - overlap` and REST
|
|
1304
|
+
* serves each entity's latest state, so nothing older can come back.
|
|
1305
|
+
*/
|
|
1306
|
+
const held = /* @__PURE__ */ new Map();
|
|
1307
|
+
/** matchedOnly: ids the caller holds, so a leave or delete still reaches them. Not pruned. */
|
|
1308
|
+
const delivered = /* @__PURE__ */ new Set();
|
|
1309
|
+
let appliedSincePrune = 0;
|
|
1310
|
+
let buffering = true;
|
|
1311
|
+
let buffer = [];
|
|
1312
|
+
let bufferOverflowed = false;
|
|
1313
|
+
let running = false;
|
|
1314
|
+
let rerun = false;
|
|
1315
|
+
let resyncRequired = false;
|
|
1316
|
+
let stopped = false;
|
|
1317
|
+
let retryTimer = null;
|
|
1318
|
+
let wakeRetry = null;
|
|
1319
|
+
const abort = new AbortController();
|
|
1320
|
+
let resolveReady;
|
|
1321
|
+
const ready = new Promise((resolve) => {
|
|
1322
|
+
resolveReady = resolve;
|
|
1323
|
+
});
|
|
1324
|
+
const report = (err) => {
|
|
1325
|
+
if (stopped) return;
|
|
1326
|
+
try {
|
|
1327
|
+
options.onError?.(err);
|
|
1328
|
+
} catch {}
|
|
1329
|
+
};
|
|
1330
|
+
const prune = () => {
|
|
1331
|
+
appliedSincePrune = 0;
|
|
1332
|
+
if (lastChangedAtMs === void 0) return;
|
|
1333
|
+
const floor = lastChangedAtMs - 2 * overlapMs;
|
|
1334
|
+
for (const [key, h] of held) if (h.changedAtMs < floor) held.delete(key);
|
|
1335
|
+
};
|
|
1336
|
+
const apply = (change) => {
|
|
1337
|
+
const key = `${change.entity}:${change.id}`;
|
|
1338
|
+
const changedAtMs = toMs(change.changedAt);
|
|
1339
|
+
if (Number.isNaN(changedAtMs)) return;
|
|
1340
|
+
const prev = held.get(key);
|
|
1341
|
+
if (prev && (change.version < prev.version || change.version === prev.version && changedAtMs <= prev.changedAtMs)) return;
|
|
1342
|
+
held.set(key, {
|
|
1343
|
+
version: change.version,
|
|
1344
|
+
changedAtMs
|
|
1345
|
+
});
|
|
1346
|
+
if (lastChangedAtMs === void 0 || changedAtMs > lastChangedAtMs) {
|
|
1347
|
+
lastChangedAtMs = changedAtMs;
|
|
1348
|
+
lastChangedAt = change.changedAt;
|
|
1349
|
+
}
|
|
1350
|
+
if (++appliedSincePrune >= PRUNE_EVERY) prune();
|
|
1351
|
+
if (options.matchedOnly) {
|
|
1352
|
+
if ((change.event ?? change.market)?.matched === true && change.op !== "deleted") delivered.add(key);
|
|
1353
|
+
else if (delivered.has(key)) delivered.delete(key);
|
|
1354
|
+
else return;
|
|
1355
|
+
}
|
|
1356
|
+
const { type: _type, ...item } = change;
|
|
1357
|
+
try {
|
|
1358
|
+
options.onChange(item);
|
|
1359
|
+
} catch (err) {
|
|
1360
|
+
report(err);
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
const onLive = (frame) => {
|
|
1364
|
+
if (stopped) return;
|
|
1365
|
+
if (!buffering) {
|
|
1366
|
+
apply(frame);
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
if (buffer.length >= LIVE_BUFFER_MAX) {
|
|
1370
|
+
buffer = [];
|
|
1371
|
+
bufferOverflowed = true;
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
buffer.push(frame);
|
|
1375
|
+
};
|
|
1376
|
+
const readAll = async () => {
|
|
1377
|
+
const from = (lastChangedAtMs ?? sinceMs) - overlapMs;
|
|
1378
|
+
let cursor;
|
|
1379
|
+
for (;;) {
|
|
1380
|
+
const page = await deps.getChanges({
|
|
1381
|
+
...cursor ? { cursor } : { since: new Date(from).toISOString() },
|
|
1382
|
+
...options.pageLimit ? { limit: options.pageLimit } : {},
|
|
1383
|
+
...filter
|
|
1384
|
+
}, abort.signal);
|
|
1385
|
+
if (stopped) return;
|
|
1386
|
+
for (const change of page.data) apply(change);
|
|
1387
|
+
if (!page.hasMore) return;
|
|
1388
|
+
cursor = page.nextCursor;
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
const sleep = (ms) => new Promise((resolve) => {
|
|
1392
|
+
wakeRetry = resolve;
|
|
1393
|
+
retryTimer = setTimeout(() => {
|
|
1394
|
+
retryTimer = null;
|
|
1395
|
+
wakeRetry = null;
|
|
1396
|
+
resolve();
|
|
1397
|
+
}, ms);
|
|
1398
|
+
});
|
|
1399
|
+
const catchUp = async () => {
|
|
1400
|
+
running = true;
|
|
1401
|
+
buffering = true;
|
|
1402
|
+
let attempt = 0;
|
|
1403
|
+
try {
|
|
1404
|
+
do {
|
|
1405
|
+
rerun = false;
|
|
1406
|
+
bufferOverflowed = false;
|
|
1407
|
+
try {
|
|
1408
|
+
await readAll();
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
if (stopped) return;
|
|
1411
|
+
if (isCatalogResyncRequired(err)) {
|
|
1412
|
+
resyncRequired = true;
|
|
1413
|
+
if (options.onResyncRequired) options.onResyncRequired();
|
|
1414
|
+
else report(err);
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
report(err);
|
|
1418
|
+
const step = Math.min(RETRY_BASE_MS * 2 ** attempt++, RETRY_MAX_MS);
|
|
1419
|
+
await sleep(Math.round(step * (.5 + Math.random() * .5)));
|
|
1420
|
+
if (stopped) return;
|
|
1421
|
+
rerun = true;
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
if (stopped) return;
|
|
1425
|
+
attempt = 0;
|
|
1426
|
+
const drained = buffer;
|
|
1427
|
+
buffer = [];
|
|
1428
|
+
for (const frame of drained) apply(frame);
|
|
1429
|
+
if (bufferOverflowed) rerun = true;
|
|
1430
|
+
} while (rerun && !stopped && !resyncRequired);
|
|
1431
|
+
if (stopped || resyncRequired) return;
|
|
1432
|
+
prune();
|
|
1433
|
+
buffering = false;
|
|
1434
|
+
options.onSynced?.({ lastChangedAt });
|
|
1435
|
+
resolveReady();
|
|
1436
|
+
} finally {
|
|
1437
|
+
running = false;
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
const requestCatchUp = () => {
|
|
1441
|
+
if (stopped || resyncRequired) return;
|
|
1442
|
+
if (running) {
|
|
1443
|
+
rerun = true;
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
catchUp().catch(report);
|
|
1447
|
+
};
|
|
1448
|
+
const owned = !options.socket;
|
|
1449
|
+
let socket = null;
|
|
1450
|
+
let unsubscribe = null;
|
|
1451
|
+
try {
|
|
1452
|
+
socket = options.socket ?? deps.createSocket();
|
|
1453
|
+
unsubscribe = socket.subscribeCatalog(filter, onLive, {
|
|
1454
|
+
onSubscribed: requestCatchUp,
|
|
1455
|
+
onInterrupted: requestCatchUp,
|
|
1456
|
+
onError: report
|
|
1457
|
+
});
|
|
1458
|
+
if (owned) socket.connect();
|
|
1459
|
+
} catch (err) {
|
|
1460
|
+
report(err);
|
|
1461
|
+
}
|
|
1462
|
+
return {
|
|
1463
|
+
stop() {
|
|
1464
|
+
if (stopped) return;
|
|
1465
|
+
stopped = true;
|
|
1466
|
+
abort.abort();
|
|
1467
|
+
if (retryTimer) clearTimeout(retryTimer);
|
|
1468
|
+
retryTimer = null;
|
|
1469
|
+
wakeRetry?.();
|
|
1470
|
+
wakeRetry = null;
|
|
1471
|
+
buffer = [];
|
|
1472
|
+
unsubscribe?.();
|
|
1473
|
+
if (owned) socket?.destroy();
|
|
1474
|
+
},
|
|
1475
|
+
get lastChangedAt() {
|
|
1476
|
+
return lastChangedAt;
|
|
1477
|
+
},
|
|
1478
|
+
resync(since) {
|
|
1479
|
+
if (stopped) return;
|
|
1480
|
+
if (Number.isNaN(toMs(since))) throw new Error("resync: `since` must be a valid date or ISO string");
|
|
1481
|
+
sinceMs = toMs(since);
|
|
1482
|
+
lastChangedAtMs = void 0;
|
|
1483
|
+
lastChangedAt = void 0;
|
|
1484
|
+
resyncRequired = false;
|
|
1485
|
+
requestCatchUp();
|
|
1486
|
+
},
|
|
1487
|
+
ready
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
function toMs(value) {
|
|
1491
|
+
return value instanceof Date ? value.getTime() : Date.parse(value);
|
|
1492
|
+
}
|
|
1493
|
+
//#endregion
|
|
1134
1494
|
//#region src/client.ts
|
|
1135
1495
|
const COOKIE_REFRESH_DELIVERY = "cookie-refresh";
|
|
1136
1496
|
const DEFAULT_MIDPOINT_IDS_PER_REQUEST = 75;
|
|
@@ -1221,9 +1581,12 @@ const AUTH_PATHS = /* @__PURE__ */ new Set([
|
|
|
1221
1581
|
]);
|
|
1222
1582
|
var AggClient = class {
|
|
1223
1583
|
constructor(options) {
|
|
1584
|
+
this.venueEventReads = new VenueEventReadScheduler();
|
|
1585
|
+
this.eventReadSession = 0;
|
|
1224
1586
|
this.listeners = /* @__PURE__ */ new Set();
|
|
1225
1587
|
this.webSocketAuthCleanups = /* @__PURE__ */ new Set();
|
|
1226
1588
|
this.tradeSockets = /* @__PURE__ */ new Set();
|
|
1589
|
+
this.signatureWaitListeners = /* @__PURE__ */ new Set();
|
|
1227
1590
|
this._authStatus = "unauthenticated";
|
|
1228
1591
|
this.authChannel = null;
|
|
1229
1592
|
this.pendingRefresh = null;
|
|
@@ -1320,6 +1683,7 @@ var AggClient = class {
|
|
|
1320
1683
|
this.authChannel.onmessage = (event) => {
|
|
1321
1684
|
const { type } = event.data ?? {};
|
|
1322
1685
|
if (type === "logout") {
|
|
1686
|
+
this.invalidateEventReadSession();
|
|
1323
1687
|
this.accessToken = void 0;
|
|
1324
1688
|
this.accessTokenUserId = void 0;
|
|
1325
1689
|
this.refreshTokenValue = void 0;
|
|
@@ -1335,6 +1699,7 @@ var AggClient = class {
|
|
|
1335
1699
|
}
|
|
1336
1700
|
/** Release resources (BroadcastChannel, etc.). Safe to call multiple times. */
|
|
1337
1701
|
destroy() {
|
|
1702
|
+
this.venueEventReads.reset();
|
|
1338
1703
|
this.authChannel?.close();
|
|
1339
1704
|
this.authChannel = null;
|
|
1340
1705
|
for (const cleanup of this.webSocketAuthCleanups) cleanup();
|
|
@@ -1347,15 +1712,17 @@ var AggClient = class {
|
|
|
1347
1712
|
* @param init - Fetch init options, plus optional `query` object for URL search params
|
|
1348
1713
|
*/
|
|
1349
1714
|
async request(path, init, decode) {
|
|
1715
|
+
const requestSession = this.eventReadSession;
|
|
1350
1716
|
const response = await this.rawFetch(path, init);
|
|
1351
|
-
if (response.status === 401 && this.accessToken && !AUTH_PATHS.has(path)) try {
|
|
1717
|
+
if (response.status === 401 && this.accessToken && !AUTH_PATHS.has(path) && this.eventReadSession === requestSession) try {
|
|
1352
1718
|
await this.refreshWithDedup();
|
|
1719
|
+
if (this.eventReadSession !== requestSession) throw Object.assign(/* @__PURE__ */ new Error("Authentication changed during refresh"), { name: "AbortError" });
|
|
1353
1720
|
const retryResponse = await this.rawFetch(path, init);
|
|
1354
1721
|
if (!retryResponse.ok) return this.throwResponseError(retryResponse);
|
|
1355
1722
|
if (retryResponse.status === 204) return void 0;
|
|
1356
1723
|
return decode ? decode(retryResponse) : retryResponse.json();
|
|
1357
1724
|
} catch {
|
|
1358
|
-
this.clearAccessToken();
|
|
1725
|
+
if (!init?.signal?.aborted && this.eventReadSession === requestSession) this.clearAccessToken();
|
|
1359
1726
|
return this.throwResponseError(response);
|
|
1360
1727
|
}
|
|
1361
1728
|
if (!response.ok) return this.throwResponseError(response);
|
|
@@ -1411,9 +1778,12 @@ var AggClient = class {
|
|
|
1411
1778
|
* only one refresh call is made and the rest wait for it.
|
|
1412
1779
|
*/
|
|
1413
1780
|
refreshWithDedup() {
|
|
1414
|
-
if (!this.pendingRefresh)
|
|
1415
|
-
this.
|
|
1416
|
-
|
|
1781
|
+
if (!this.pendingRefresh) {
|
|
1782
|
+
const pending = this.refreshAccessToken().finally(() => {
|
|
1783
|
+
if (this.pendingRefresh === pending) this.pendingRefresh = null;
|
|
1784
|
+
});
|
|
1785
|
+
this.pendingRefresh = pending;
|
|
1786
|
+
}
|
|
1417
1787
|
return this.pendingRefresh;
|
|
1418
1788
|
}
|
|
1419
1789
|
buildAuthRequestInit(init, options) {
|
|
@@ -1444,7 +1814,10 @@ var AggClient = class {
|
|
|
1444
1814
|
}
|
|
1445
1815
|
/** Set the user access token (for future user-tier auth). */
|
|
1446
1816
|
setAccessToken(token) {
|
|
1447
|
-
if (this.accessToken !== token)
|
|
1817
|
+
if (this.accessToken !== token) {
|
|
1818
|
+
this.invalidateEventReadSession();
|
|
1819
|
+
this.accessTokenUserId = void 0;
|
|
1820
|
+
}
|
|
1448
1821
|
this.accessToken = token;
|
|
1449
1822
|
this._authStatus = "authenticated";
|
|
1450
1823
|
this.persistSession();
|
|
@@ -1452,8 +1825,12 @@ var AggClient = class {
|
|
|
1452
1825
|
}
|
|
1453
1826
|
/** Set the current auth session tokens and optionally seed the user profile. */
|
|
1454
1827
|
setSession(session) {
|
|
1828
|
+
this.applySession(session);
|
|
1829
|
+
}
|
|
1830
|
+
applySession(session, tokenRenewal = false) {
|
|
1455
1831
|
const wasAuthenticated = this.isAuthenticated;
|
|
1456
|
-
this.accessTokenUserId
|
|
1832
|
+
if (session.user !== void 0 && this.accessTokenUserId !== void 0 && session.user.id !== this.accessTokenUserId || !tokenRenewal && (this.accessToken !== session.accessToken || session.user && session.user.id !== this.accessTokenUserId)) this.invalidateEventReadSession();
|
|
1833
|
+
this.accessTokenUserId = session.user?.id ?? (tokenRenewal || this.accessToken === session.accessToken ? this.accessTokenUserId : void 0);
|
|
1457
1834
|
this.accessToken = session.accessToken;
|
|
1458
1835
|
if ("refreshToken" in session) this.refreshTokenValue = session.refreshToken;
|
|
1459
1836
|
if (session.user !== void 0) this.currentUser = normalizeSessionUser(session.user, this.currentUser);
|
|
@@ -1464,6 +1841,7 @@ var AggClient = class {
|
|
|
1464
1841
|
}
|
|
1465
1842
|
/** Clear the user access token, refresh token, and user info. Broadcasts cross-tab logout. */
|
|
1466
1843
|
clearAccessToken() {
|
|
1844
|
+
this.invalidateEventReadSession();
|
|
1467
1845
|
this.accessToken = void 0;
|
|
1468
1846
|
this.accessTokenUserId = void 0;
|
|
1469
1847
|
this.refreshTokenValue = void 0;
|
|
@@ -1473,6 +1851,11 @@ var AggClient = class {
|
|
|
1473
1851
|
this.notifyListeners();
|
|
1474
1852
|
this.broadcastAuth("logout");
|
|
1475
1853
|
}
|
|
1854
|
+
invalidateEventReadSession() {
|
|
1855
|
+
this.eventReadSession += 1;
|
|
1856
|
+
this.pendingRefresh = null;
|
|
1857
|
+
this.venueEventReads.reset();
|
|
1858
|
+
}
|
|
1476
1859
|
broadcastAuth(type) {
|
|
1477
1860
|
try {
|
|
1478
1861
|
this.authChannel?.postMessage({ type });
|
|
@@ -1636,16 +2019,18 @@ var AggClient = class {
|
|
|
1636
2019
|
}
|
|
1637
2020
|
/** Refresh the access token using the stored refresh token. */
|
|
1638
2021
|
async refreshAccessToken() {
|
|
2022
|
+
const refreshSession = this.eventReadSession;
|
|
1639
2023
|
if (this.authDelivery !== COOKIE_REFRESH_DELIVERY && !this.refreshTokenValue) throw new Error("No refresh token available");
|
|
1640
2024
|
const res = await this.request("/auth/token/refresh", this.buildAuthRequestInit({
|
|
1641
2025
|
method: "POST",
|
|
1642
2026
|
body: JSON.stringify(this.authDelivery === COOKIE_REFRESH_DELIVERY ? {} : { refreshToken: this.refreshTokenValue })
|
|
1643
2027
|
}));
|
|
1644
|
-
this.
|
|
2028
|
+
if (this.eventReadSession !== refreshSession) throw Object.assign(/* @__PURE__ */ new Error("Authentication changed during refresh"), { name: "AbortError" });
|
|
2029
|
+
this.applySession({
|
|
1645
2030
|
accessToken: res.accessToken,
|
|
1646
2031
|
refreshToken: res.refreshToken,
|
|
1647
2032
|
user: res.user
|
|
1648
|
-
});
|
|
2033
|
+
}, true);
|
|
1649
2034
|
return res.accessToken;
|
|
1650
2035
|
}
|
|
1651
2036
|
/** Get the current user's profile. */
|
|
@@ -2033,14 +2418,24 @@ var AggClient = class {
|
|
|
2033
2418
|
});
|
|
2034
2419
|
}
|
|
2035
2420
|
async getVenueEventById(id, options) {
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2421
|
+
const query = {
|
|
2422
|
+
expand: "",
|
|
2423
|
+
...options?.cluster === false ? { cluster: "false" } : {},
|
|
2424
|
+
...options?.minVolume != null && options.minVolume > 0 ? { minVolume: String(options.minVolume) } : {}
|
|
2425
|
+
};
|
|
2426
|
+
const session = this.eventReadSession;
|
|
2427
|
+
const key = JSON.stringify([
|
|
2428
|
+
id,
|
|
2429
|
+
query,
|
|
2430
|
+
session
|
|
2431
|
+
]);
|
|
2432
|
+
return this.venueEventReads.read(key, (signal) => {
|
|
2433
|
+
if (this.eventReadSession !== session) throw Object.assign(/* @__PURE__ */ new Error("Authentication changed before event read"), { name: "AbortError" });
|
|
2434
|
+
return this.request(`/venue-events/${encodeURIComponent(id)}`, {
|
|
2435
|
+
query,
|
|
2436
|
+
signal
|
|
2437
|
+
});
|
|
2438
|
+
}, options?.signal);
|
|
2044
2439
|
}
|
|
2045
2440
|
/** Get normalized live score state for sports events. Requires appId or admin auth. */
|
|
2046
2441
|
async getSportsLive(eventIds, options) {
|
|
@@ -2410,27 +2805,77 @@ var AggClient = class {
|
|
|
2410
2805
|
});
|
|
2411
2806
|
}
|
|
2412
2807
|
/**
|
|
2808
|
+
* Catalog changes (events and markets created, changed or deleted) after
|
|
2809
|
+
* `since`, oldest first, one item per entity with its current state. Page
|
|
2810
|
+
* with `nextCursor` until `hasMore` is false. Positions older than 24h
|
|
2811
|
+
* throw an {@link AggApiError} with status 410 and code `resync_required`
|
|
2812
|
+
* (see `isCatalogResyncRequired`). Most callers want {@link watchCatalog}.
|
|
2813
|
+
*/
|
|
2814
|
+
async getCatalogChanges(params, options) {
|
|
2815
|
+
const query = {};
|
|
2816
|
+
if (params.since !== void 0) query.since = params.since instanceof Date ? params.since.toISOString() : params.since;
|
|
2817
|
+
if (params.cursor) query.cursor = params.cursor;
|
|
2818
|
+
if (params.limit != null) query.limit = String(params.limit);
|
|
2819
|
+
if (params.venues?.length) query.venues = params.venues;
|
|
2820
|
+
if (params.entities?.length) query.entities = params.entities;
|
|
2821
|
+
return this.request("/catalog/changes", {
|
|
2822
|
+
query,
|
|
2823
|
+
signal: options?.signal
|
|
2824
|
+
});
|
|
2825
|
+
}
|
|
2826
|
+
/**
|
|
2827
|
+
* Keep a copy of the catalog in sync from `since`: subscribes to the live
|
|
2828
|
+
* `catalog` channel, catches up over `GET /catalog/changes` (from the last
|
|
2829
|
+
* applied `changedAt` minus `overlapMs`) on every (re)subscribe and stream
|
|
2830
|
+
* interruption, and delivers each entity's newer state once to `onChange`.
|
|
2831
|
+
*
|
|
2832
|
+
* Persist `handle.lastChangedAt` and pass it back as `since` to resume
|
|
2833
|
+
* after a restart. When that is older than 24h, `onResyncRequired` fires:
|
|
2834
|
+
* re-list with the list endpoints, then call `handle.resync(startOfReList)`.
|
|
2835
|
+
*
|
|
2836
|
+
* Without `socket`, opens a dedicated app-level connection (requires
|
|
2837
|
+
* `wsUrl`) that `stop()` closes.
|
|
2838
|
+
*/
|
|
2839
|
+
watchCatalog(options) {
|
|
2840
|
+
return watchCatalog({
|
|
2841
|
+
getChanges: (params, signal) => this.getCatalogChanges(params, { signal }),
|
|
2842
|
+
createSocket: () => new AggWebSocket(this.webSocketOptions(options.webSocketImpl))
|
|
2843
|
+
}, options);
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* URL and handshake headers for this client's app. Server-side clients with
|
|
2847
|
+
* an API key authenticate the handshake with it (the gateway then skips the
|
|
2848
|
+
* browser Origin check). Never in a browser: browsers cannot set handshake
|
|
2849
|
+
* headers, and keys must not ship there.
|
|
2850
|
+
*/
|
|
2851
|
+
webSocketOptions(webSocketImpl) {
|
|
2852
|
+
if (!this.wsUrl) throw new Error("AggClient WebSockets require wsUrl to be set in client options");
|
|
2853
|
+
const url = new URL(this.wsUrl);
|
|
2854
|
+
if (this._appId) url.searchParams.set("appId", this._appId);
|
|
2855
|
+
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
2856
|
+
const headers = this.apiKey && !isBrowser ? { "x-app-api-key": this.apiKey } : void 0;
|
|
2857
|
+
return {
|
|
2858
|
+
url: url.toString(),
|
|
2859
|
+
...headers ? { headers } : {},
|
|
2860
|
+
...webSocketImpl ? { webSocketImpl } : {}
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2863
|
+
/**
|
|
2413
2864
|
* Create an AggWebSocket instance configured with this client's appId and current auth session.
|
|
2414
2865
|
* Session changes update this socket until it or the client is destroyed.
|
|
2415
2866
|
* Requires `wsUrl` to be set in the client options.
|
|
2416
2867
|
*/
|
|
2417
2868
|
createWebSocket(callbacks, options) {
|
|
2418
2869
|
if (!this.wsUrl) throw new Error("AggClient.createWebSocket() requires wsUrl to be set in client options");
|
|
2419
|
-
const url = new URL(this.wsUrl);
|
|
2420
|
-
if (this._appId) url.searchParams.set("appId", this._appId);
|
|
2421
2870
|
const createSocket = this.webSocketFactory ?? ((socketOptions) => new AggWebSocket(socketOptions));
|
|
2422
2871
|
const watchdogHeaders = {};
|
|
2423
2872
|
if (this._appId) watchdogHeaders["x-app-id"] = this._appId;
|
|
2424
2873
|
if (this.apiKey) watchdogHeaders["x-app-api-key"] = this.apiKey;
|
|
2425
|
-
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
2426
|
-
const headers = this.apiKey && !isBrowser ? { "x-app-api-key": this.apiKey } : void 0;
|
|
2427
2874
|
const socket = createSocket({
|
|
2428
|
-
|
|
2875
|
+
...this.webSocketOptions(options?.webSocketImpl),
|
|
2429
2876
|
callbacks,
|
|
2430
2877
|
initialToken: this.accessToken,
|
|
2431
2878
|
enableLogs: options?.enableLogs ?? false,
|
|
2432
|
-
...headers ? { headers } : {},
|
|
2433
|
-
...options?.webSocketImpl ? { webSocketImpl: options.webSocketImpl } : {},
|
|
2434
2879
|
bookWatchdog: options?.bookWatchdog === false ? void 0 : {
|
|
2435
2880
|
baseUrl: this.baseUrl,
|
|
2436
2881
|
headers: watchdogHeaders,
|
|
@@ -2491,7 +2936,9 @@ var AggClient = class {
|
|
|
2491
2936
|
handlers.onAuthenticated?.(message);
|
|
2492
2937
|
},
|
|
2493
2938
|
onOrderEvent: (message) => {
|
|
2494
|
-
if (canReceiveUserEvent(message.userId))
|
|
2939
|
+
if (!canReceiveUserEvent(message.userId)) return;
|
|
2940
|
+
if (message.event === "step_waiting" && message.waitingFor?.topic === "agg.user.signature") for (const listener of this.signatureWaitListeners) listener(message.orderId);
|
|
2941
|
+
handlers.onOrderEvent?.(message);
|
|
2495
2942
|
},
|
|
2496
2943
|
onOrderEventReceipt: (message) => {
|
|
2497
2944
|
if (canReceiveUserEvent()) handlers.onOrderEventReceipt?.(message);
|
|
@@ -2544,7 +2991,7 @@ var AggClient = class {
|
|
|
2544
2991
|
userId = nextUserId;
|
|
2545
2992
|
if (resetConnection) {
|
|
2546
2993
|
const diagnostics = socket.getDiagnostics();
|
|
2547
|
-
const reconnect = connectRequested || socket.connected || !manuallyDisconnected && (diagnostics.orderbookOutcomeIds.length > 0 || diagnostics.tradeOutcomeIds.length > 0 || diagnostics.arbMarketIds.length > 0 || diagnostics.arbFeedSubscribers > 0);
|
|
2994
|
+
const reconnect = connectRequested || socket.connected || !manuallyDisconnected && (diagnostics.orderbookOutcomeIds.length > 0 || diagnostics.tradeOutcomeIds.length > 0 || diagnostics.arbMarketIds.length > 0 || diagnostics.arbFeedSubscribers > 0 || (diagnostics.catalogSubscribers ?? 0) > 0);
|
|
2548
2995
|
authenticatedUserId = void 0;
|
|
2549
2996
|
awaitingDisconnect = true;
|
|
2550
2997
|
this.tradeSocketAuthenticatedUserId = void 0;
|
|
@@ -2619,69 +3066,80 @@ var AggClient = class {
|
|
|
2619
3066
|
let ordersFilled = false;
|
|
2620
3067
|
const isPostFillFee = (req) => isPostFillFeeRequest(req, ordersFilled);
|
|
2621
3068
|
let orderSubmitted = false;
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
if (
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
};
|
|
3069
|
+
const wakeup = new ExecutionPollWakeup();
|
|
3070
|
+
const orderIds = new Set(first.orderIds ?? []);
|
|
3071
|
+
const onSignatureWait = (orderId) => {
|
|
3072
|
+
if (orderIds.has(orderId)) wakeup.notify();
|
|
3073
|
+
};
|
|
3074
|
+
this.signatureWaitListeners.add(onSignatureWait);
|
|
3075
|
+
try {
|
|
3076
|
+
for (;;) {
|
|
3077
|
+
pending = pending.filter((p) => !declinedOptional.has(p.stepId) && (orderSubmitted || !isOptionalFeeRequest(p)));
|
|
3078
|
+
if (Date.now() > deadline) throw new Error(`Fill for quote ${quoteId} did not finish within the timeout.`);
|
|
3079
|
+
if (pending.length === 0) {
|
|
3080
|
+
await wakeup.wait(nextPollMs);
|
|
3081
|
+
const polled = await this.getExecutionStatus({ quoteId });
|
|
3082
|
+
nextPollMs = Number.isFinite(polled.pollAfterMs) ? Math.min(Math.max(polled.pollAfterMs, MIN_POLL_INTERVAL_MS), MAX_POLL_INTERVAL_MS) : SELF_CUSTODY_POLL_INTERVAL_MS;
|
|
3083
|
+
if (TERMINAL_EXECUTION_STATES.has(polled.overallState)) {
|
|
3084
|
+
if (polled.overallState !== "filled" && polled.overallState !== "partially_filled") throw new Error(`Fill for quote ${quoteId} ended as ${polled.overallState}.`);
|
|
3085
|
+
return {
|
|
3086
|
+
...first,
|
|
3087
|
+
pendingSignatures: []
|
|
3088
|
+
};
|
|
3089
|
+
}
|
|
3090
|
+
ordersFilled = areOrdersFilled(polled.orders);
|
|
3091
|
+
pending = polled.pendingSignatures ?? [];
|
|
3092
|
+
continue;
|
|
2635
3093
|
}
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
3094
|
+
const key = pending.map((p) => p.stepId).sort().join(",");
|
|
3095
|
+
if (key === lastKey) throw new Error(`Fill for quote ${quoteId} did not advance — still awaiting steps [${key}] after signing them.`);
|
|
3096
|
+
lastKey = key;
|
|
3097
|
+
const signatures = [];
|
|
3098
|
+
const ordered = orderSignatureRound(pending, { orderSubmitted });
|
|
3099
|
+
const userSigners = userSignerAddresses(pending, params.signingAddress);
|
|
3100
|
+
for (const req of ordered) {
|
|
3101
|
+
if (Date.now() > new Date(req.expiresAt).getTime()) {
|
|
3102
|
+
if (req.optional) {
|
|
3103
|
+
declinedOptional.add(req.stepId);
|
|
3104
|
+
continue;
|
|
3105
|
+
}
|
|
3106
|
+
if (isPostFillFee(req)) return {
|
|
3107
|
+
...first,
|
|
3108
|
+
pendingSignatures: []
|
|
3109
|
+
};
|
|
3110
|
+
throw new Error(`Signature request ${req.stepId} for quote ${quoteId} expired at ${req.expiresAt}; the fill must be re-quoted.`);
|
|
3111
|
+
}
|
|
3112
|
+
let signed;
|
|
3113
|
+
try {
|
|
3114
|
+
signed = await withTimeout(signer(req), signatureTimeoutMs(req, userSigners));
|
|
3115
|
+
} catch (err) {
|
|
3116
|
+
if (isPostFillFee(req)) return {
|
|
3117
|
+
...first,
|
|
3118
|
+
pendingSignatures: []
|
|
3119
|
+
};
|
|
3120
|
+
if (!req.optional) throw err;
|
|
2649
3121
|
declinedOptional.add(req.stepId);
|
|
2650
3122
|
continue;
|
|
2651
3123
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
}
|
|
2656
|
-
|
|
3124
|
+
signatures.push(typeof signed === "string" ? {
|
|
3125
|
+
stepId: req.stepId,
|
|
3126
|
+
signature: signed
|
|
3127
|
+
} : {
|
|
3128
|
+
stepId: req.stepId,
|
|
3129
|
+
txHash: signed.txHash
|
|
3130
|
+
});
|
|
2657
3131
|
}
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
signed = await withTimeout(signer(req), signatureTimeoutMs(req, userSigners));
|
|
2661
|
-
} catch (err) {
|
|
2662
|
-
if (isPostFillFee(req)) return {
|
|
2663
|
-
...first,
|
|
2664
|
-
pendingSignatures: []
|
|
2665
|
-
};
|
|
2666
|
-
if (!req.optional) throw err;
|
|
2667
|
-
declinedOptional.add(req.stepId);
|
|
3132
|
+
if (signatures.length === 0) {
|
|
3133
|
+
pending = [];
|
|
2668
3134
|
continue;
|
|
2669
3135
|
}
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
} : {
|
|
2674
|
-
stepId: req.stepId,
|
|
2675
|
-
txHash: signed.txHash
|
|
2676
|
-
});
|
|
2677
|
-
}
|
|
2678
|
-
if (signatures.length === 0) {
|
|
2679
|
-
pending = [];
|
|
2680
|
-
continue;
|
|
3136
|
+
const next = await this.submitFillSignatures(quoteId, signatures);
|
|
3137
|
+
if (signatures.some((s) => isOrderSignatureStep(s.stepId))) orderSubmitted = true;
|
|
3138
|
+
pending = next.pendingSignatures ?? [];
|
|
2681
3139
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
3140
|
+
} finally {
|
|
3141
|
+
this.signatureWaitListeners.delete(onSignatureWait);
|
|
3142
|
+
wakeup.dispose();
|
|
2685
3143
|
}
|
|
2686
3144
|
}
|
|
2687
3145
|
/**
|
|
@@ -2765,6 +3223,14 @@ var AggClient = class {
|
|
|
2765
3223
|
body: JSON.stringify(params)
|
|
2766
3224
|
});
|
|
2767
3225
|
}
|
|
3226
|
+
/** Submit signatures for a parked self-custody withdrawal. Returns whatever is still outstanding. */
|
|
3227
|
+
async submitWithdrawalSignatures(withdrawalId, signatures) {
|
|
3228
|
+
if (!withdrawalId) throw new Error("withdrawalId is required");
|
|
3229
|
+
return this.request(`/execution/withdrawals/${encodeURIComponent(withdrawalId)}/signatures`, {
|
|
3230
|
+
method: "POST",
|
|
3231
|
+
body: JSON.stringify({ signatures })
|
|
3232
|
+
});
|
|
3233
|
+
}
|
|
2768
3234
|
/**
|
|
2769
3235
|
* Preview a withdrawal — real fee + receive estimate without creating one.
|
|
2770
3236
|
* Optional: `withdrawManaged` does NOT require a prior preview, so partners
|
|
@@ -3086,6 +3552,6 @@ function createAggClient(options) {
|
|
|
3086
3552
|
return new AggClient(options);
|
|
3087
3553
|
}
|
|
3088
3554
|
//#endregion
|
|
3089
|
-
export { ACTIVE_VENUES, AccountProvider, AccountType, AggApiError, AggClient, AggWebSocket, CONFIRMED_MATCH_STATUSES, CandleBuilder, Chain, DEFAULT_BALANCE_REFILL_DAILY_CAP_RAW, DEFAULT_BALANCE_REFILL_MAX_FEE_RAW, IMAGE_SIZES, INACTIVE_VENUES, ImageSize, LIMIT_PRICE_RAW_SCALE, MarketStatus, MatchStatus, MatchType, OPTIONAL_FEE_SIGNATURE_TIMEOUT_MS, OPTIONAL_SIGNATURE_TIMEOUT_MS, OrderStatus, OrderType, RECURRENCE_CADENCES, RETINA_IMAGE_SIZES, SELF_CUSTODY_FILL_TIMEOUT_MS, SELF_CUSTODY_POLL_INTERVAL_MS, TERMINAL_EXECUTION_STATES, TimeInForce, TradeSide, TurnstileChallengeError, VENUES, VENUE_CHAIN_IDS, Venue, adjustLimitPriceRawToTick, aggregateMidpoint, applyOrderbookDelta, areOrdersFilled, buildVenueUrl, computeBestSplitsByAmount, computeChecksum, createAggClient, enumGuard, formatMarketQuestion, formatOutcomeLabel, formatOutcomeTitle, getBalanceRefillMaximumRaw, getEffectiveDisabledVenues, getWalletAddressFromUserProfile, hasShape, isAggApiError, isBalanceRefillWithinDailyCap, isEmail, isEnum, isFiniteNonNeg, isNonEmptyString, isOptionalFeeRequest, isOrderSignatureStep, isPolymarketOrderSignatureStep, isPostFillFeeRequest, isSameWalletAddress, isVenueActive, isVenueDataVisible, isVenueInactive, loadImmutableHistory, mergeCandles, mergeClosedCandles, normalizeVenueMarketCluster, optimizedImageUrl, orderSignatureRound, orderbookCrc32, parse, parseEmail, parseEmailStrict, prepareHostedVenueFunding, prepareHostedVenueRoute, projectedOrderbookChecksum, retinaImageUrl, safeParse, signatureTimeoutMs, snapshotToOrderbook, sortVenues, toChecksumAddress, userSignerAddresses, withTimeout };
|
|
3555
|
+
export { ACTIVE_VENUES, AccountProvider, AccountType, AggApiError, AggClient, AggWebSocket, CATALOG_DEFAULT_OVERLAP_MS, CONFIRMED_MATCH_STATUSES, CandleBuilder, Chain, DEFAULT_BALANCE_REFILL_DAILY_CAP_RAW, DEFAULT_BALANCE_REFILL_MAX_FEE_RAW, IMAGE_SIZES, INACTIVE_VENUES, ImageSize, LIMIT_PRICE_RAW_SCALE, MarketStatus, MatchStatus, MatchType, OPTIONAL_FEE_SIGNATURE_TIMEOUT_MS, OPTIONAL_SIGNATURE_TIMEOUT_MS, OrderStatus, OrderType, RECURRENCE_CADENCES, RETINA_IMAGE_SIZES, SELF_CUSTODY_FILL_TIMEOUT_MS, SELF_CUSTODY_POLL_INTERVAL_MS, TERMINAL_EXECUTION_STATES, TimeInForce, TradeSide, TurnstileChallengeError, VENUES, VENUE_CHAIN_IDS, Venue, adjustLimitPriceRawToTick, aggregateMidpoint, applyOrderbookDelta, areOrdersFilled, buildVenueUrl, computeBestSplitsByAmount, computeChecksum, createAggClient, enumGuard, formatMarketQuestion, formatOutcomeLabel, formatOutcomeTitle, getBalanceRefillMaximumRaw, getEffectiveDisabledVenues, getWalletAddressFromUserProfile, hasShape, isAggApiError, isBalanceRefillWithinDailyCap, isCatalogResyncRequired, isEmail, isEnum, isFiniteNonNeg, isNonEmptyString, isOptionalFeeRequest, isOrderSignatureStep, isPolymarketOrderSignatureStep, isPostFillFeeRequest, isSameWalletAddress, isVenueActive, isVenueDataVisible, isVenueInactive, loadImmutableHistory, mergeCandles, mergeClosedCandles, normalizeVenueMarketCluster, optimizedImageUrl, orderSignatureRound, orderbookCrc32, parse, parseEmail, parseEmailStrict, prepareHostedVenueFunding, prepareHostedVenueRoute, projectedOrderbookChecksum, retinaImageUrl, safeParse, signatureTimeoutMs, snapshotToOrderbook, sortVenues, toChecksumAddress, userSignerAddresses, withTimeout };
|
|
3090
3556
|
|
|
3091
3557
|
//# sourceMappingURL=index.mjs.map
|