@dimcool/sdk 0.1.29 → 0.1.31
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 +115 -7
- package/dist/index.cjs +639 -134
- package/dist/index.d.cts +673 -459
- package/dist/index.d.ts +673 -459
- package/dist/index.js +637 -134
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -696,6 +696,16 @@ var Auth = class {
|
|
|
696
696
|
this.logger.debug("Token stored in storage", { tokenKey: TOKEN_KEY });
|
|
697
697
|
return response;
|
|
698
698
|
}
|
|
699
|
+
async loginWithExternalSignature(address, signedMessage, options) {
|
|
700
|
+
const response = await this.http.post("/auth/login-wallet", {
|
|
701
|
+
signedMessage,
|
|
702
|
+
address,
|
|
703
|
+
referralCode: options?.referralCode,
|
|
704
|
+
walletMeta: options?.walletMeta
|
|
705
|
+
});
|
|
706
|
+
this.storage.set(TOKEN_KEY, response.access_token);
|
|
707
|
+
return response;
|
|
708
|
+
}
|
|
699
709
|
logout() {
|
|
700
710
|
this.logger.debug("Auth.logout called");
|
|
701
711
|
const hadToken = this.storage.get(TOKEN_KEY) !== null;
|
|
@@ -709,6 +719,10 @@ var Auth = class {
|
|
|
709
719
|
});
|
|
710
720
|
return isAuth;
|
|
711
721
|
}
|
|
722
|
+
async getSessionStats() {
|
|
723
|
+
this.logger.debug("Auth.getSessionStats called");
|
|
724
|
+
return this.http.get("/auth/sessions/stats");
|
|
725
|
+
}
|
|
712
726
|
async getLatestSessions(limit) {
|
|
713
727
|
this.logger.debug("Auth.getLatestSessions called", { limit });
|
|
714
728
|
const params = new URLSearchParams();
|
|
@@ -729,6 +743,9 @@ var Admin = class {
|
|
|
729
743
|
this.http = http;
|
|
730
744
|
this.logger = logger2;
|
|
731
745
|
}
|
|
746
|
+
async getInternalBots() {
|
|
747
|
+
return this.http.get("/admin/internal-bots");
|
|
748
|
+
}
|
|
732
749
|
async getUserById(id) {
|
|
733
750
|
return this.http.get(`/admin/users/${id}`);
|
|
734
751
|
}
|
|
@@ -936,11 +953,25 @@ var Users = class {
|
|
|
936
953
|
async removeFriend(userId) {
|
|
937
954
|
return this.http.delete(`/friends/${userId}`);
|
|
938
955
|
}
|
|
939
|
-
async getIncomingFriendRequests() {
|
|
940
|
-
|
|
956
|
+
async getIncomingFriendRequests(opts) {
|
|
957
|
+
const params = new URLSearchParams();
|
|
958
|
+
if (opts?.limit !== void 0)
|
|
959
|
+
params.append("limit", opts.limit.toString());
|
|
960
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
961
|
+
const qs = params.toString();
|
|
962
|
+
return this.http.get(
|
|
963
|
+
qs ? `/friends/requests/incoming?${qs}` : "/friends/requests/incoming"
|
|
964
|
+
);
|
|
941
965
|
}
|
|
942
|
-
async getOutgoingFriendRequests() {
|
|
943
|
-
|
|
966
|
+
async getOutgoingFriendRequests(opts) {
|
|
967
|
+
const params = new URLSearchParams();
|
|
968
|
+
if (opts?.limit !== void 0)
|
|
969
|
+
params.append("limit", opts.limit.toString());
|
|
970
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
971
|
+
const qs = params.toString();
|
|
972
|
+
return this.http.get(
|
|
973
|
+
qs ? `/friends/requests/outgoing?${qs}` : "/friends/requests/outgoing"
|
|
974
|
+
);
|
|
944
975
|
}
|
|
945
976
|
async acceptFriendRequest(userId) {
|
|
946
977
|
return this.http.post(
|
|
@@ -1029,6 +1060,9 @@ var FeatureFlags = class {
|
|
|
1029
1060
|
this.loaded = true;
|
|
1030
1061
|
return this.flags;
|
|
1031
1062
|
}
|
|
1063
|
+
async getAdminFeatureFlags() {
|
|
1064
|
+
return this.http.get("/feature-flags/admin");
|
|
1065
|
+
}
|
|
1032
1066
|
isEnabledFlag(name) {
|
|
1033
1067
|
const flag = this.flags.find((f) => f.name === name);
|
|
1034
1068
|
return flag?.enabled ?? false;
|
|
@@ -1048,10 +1082,11 @@ var FeatureFlags = class {
|
|
|
1048
1082
|
}
|
|
1049
1083
|
return flag;
|
|
1050
1084
|
}
|
|
1051
|
-
async createFeatureFlag(name, enabled) {
|
|
1085
|
+
async createFeatureFlag(name, enabled, description) {
|
|
1052
1086
|
const flag = await this.http.post("/feature-flags", {
|
|
1053
1087
|
name,
|
|
1054
|
-
enabled
|
|
1088
|
+
enabled,
|
|
1089
|
+
...description !== void 0 && { description }
|
|
1055
1090
|
});
|
|
1056
1091
|
this.flags.push(flag);
|
|
1057
1092
|
return flag;
|
|
@@ -1064,11 +1099,17 @@ var Lobbies = class {
|
|
|
1064
1099
|
this.http = http;
|
|
1065
1100
|
this.logger = logger2;
|
|
1066
1101
|
}
|
|
1102
|
+
/** Called by SDK after the store is created so mutations can update state directly. */
|
|
1103
|
+
setLobbyStore(store) {
|
|
1104
|
+
this.lobbyStore = store;
|
|
1105
|
+
}
|
|
1067
1106
|
async createLobby(gameType, betAmount) {
|
|
1068
1107
|
return this.http.post("/lobbies", { gameType, betAmount });
|
|
1069
1108
|
}
|
|
1070
1109
|
async getLobby(lobbyId) {
|
|
1071
|
-
|
|
1110
|
+
const lobby = await this.http.get(`/lobbies/${lobbyId}`);
|
|
1111
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
1112
|
+
return lobby;
|
|
1072
1113
|
}
|
|
1073
1114
|
async inviteFriend(lobbyId, friendId) {
|
|
1074
1115
|
return this.http.post(`/lobbies/${lobbyId}/invite`, {
|
|
@@ -1079,7 +1120,9 @@ var Lobbies = class {
|
|
|
1079
1120
|
return this.http.post(`/lobbies/${lobbyId}/accept-invite`, {});
|
|
1080
1121
|
}
|
|
1081
1122
|
async joinLobby(lobbyId) {
|
|
1082
|
-
|
|
1123
|
+
const lobby = await this.http.post(`/lobbies/${lobbyId}/join`, {});
|
|
1124
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
1125
|
+
return lobby;
|
|
1083
1126
|
}
|
|
1084
1127
|
async removePlayer(lobbyId, userId) {
|
|
1085
1128
|
return this.http.delete(
|
|
@@ -1096,12 +1139,17 @@ var Lobbies = class {
|
|
|
1096
1139
|
return this.http.post(`/lobbies/${lobbyId}/join-queue`, {});
|
|
1097
1140
|
}
|
|
1098
1141
|
async cancelQueue(lobbyId) {
|
|
1099
|
-
|
|
1142
|
+
const lobby = await this.http.delete(`/lobbies/${lobbyId}/queue`);
|
|
1143
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
1144
|
+
return lobby;
|
|
1100
1145
|
}
|
|
1101
1146
|
async updateBetAmount(lobbyId, betAmount) {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1147
|
+
const lobby = await this.http.patch(
|
|
1148
|
+
`/lobbies/${lobbyId}/bet-amount`,
|
|
1149
|
+
{ betAmount }
|
|
1150
|
+
);
|
|
1151
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
1152
|
+
return lobby;
|
|
1105
1153
|
}
|
|
1106
1154
|
async playSound(lobbyId, sound) {
|
|
1107
1155
|
return this.http.post(`/lobbies/${lobbyId}/sound`, {
|
|
@@ -1136,13 +1184,18 @@ var Lobbies = class {
|
|
|
1136
1184
|
};
|
|
1137
1185
|
|
|
1138
1186
|
// src/games.ts
|
|
1139
|
-
import { Transaction as Transaction2 } from "@solana/web3.js";
|
|
1140
1187
|
var Games = class {
|
|
1141
1188
|
constructor(http, wallet, logger2) {
|
|
1142
1189
|
this.http = http;
|
|
1143
1190
|
this.wallet = wallet;
|
|
1144
1191
|
this.logger = logger2;
|
|
1145
1192
|
}
|
|
1193
|
+
setGameStore(store) {
|
|
1194
|
+
this.gameStore = store;
|
|
1195
|
+
}
|
|
1196
|
+
setGameActionsStore(store) {
|
|
1197
|
+
this.gameActionsStore = store;
|
|
1198
|
+
}
|
|
1146
1199
|
async getAvailableGames() {
|
|
1147
1200
|
return this.http.get("/games/available");
|
|
1148
1201
|
}
|
|
@@ -1155,7 +1208,11 @@ var Games = class {
|
|
|
1155
1208
|
return this.http.get("/games/metrics");
|
|
1156
1209
|
}
|
|
1157
1210
|
async getGame(gameId) {
|
|
1158
|
-
|
|
1211
|
+
const existing = this.gameStore?.store.getState().gamesById[gameId];
|
|
1212
|
+
if (existing?.status === "completed") return existing;
|
|
1213
|
+
const game = await this.http.get(`/games/${gameId}`);
|
|
1214
|
+
this.gameStore?.setBaseState([game]);
|
|
1215
|
+
return game;
|
|
1159
1216
|
}
|
|
1160
1217
|
/**
|
|
1161
1218
|
* Get list of currently active (live) games. Public endpoint for spectating.
|
|
@@ -1182,7 +1239,13 @@ var Games = class {
|
|
|
1182
1239
|
* Get current game state with timer information
|
|
1183
1240
|
*/
|
|
1184
1241
|
async getGameState(gameId) {
|
|
1185
|
-
|
|
1242
|
+
const existing = this.gameActionsStore?.store.getState().statesByGameId[gameId];
|
|
1243
|
+
if (existing?.status === "completed") return existing;
|
|
1244
|
+
const state = await this.http.get(
|
|
1245
|
+
`/games/${gameId}/state`
|
|
1246
|
+
);
|
|
1247
|
+
this.gameActionsStore?.setBaseState(gameId, state);
|
|
1248
|
+
return state;
|
|
1186
1249
|
}
|
|
1187
1250
|
/**
|
|
1188
1251
|
* Request a rematch for a completed game
|
|
@@ -1218,8 +1281,18 @@ var Games = class {
|
|
|
1218
1281
|
{ amountMinor, signedTransaction }
|
|
1219
1282
|
);
|
|
1220
1283
|
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Confirm a donation that was already sent by the client (signAndSendTransaction path).
|
|
1286
|
+
*/
|
|
1287
|
+
async confirmGameDonationSignature(gameId, amountMinor, signature) {
|
|
1288
|
+
return this.http.post(
|
|
1289
|
+
`/games/${gameId}/donate/confirm-signature`,
|
|
1290
|
+
{ amountMinor, signature }
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1221
1293
|
/**
|
|
1222
1294
|
* One-call donation flow: prepare -> sign -> submit.
|
|
1295
|
+
* Automatically uses the right signing path (embedded vs injected wallet).
|
|
1223
1296
|
*/
|
|
1224
1297
|
async sendDonation(gameId, amountMinor) {
|
|
1225
1298
|
if (!this.wallet.hasSigner()) {
|
|
@@ -1228,16 +1301,10 @@ var Games = class {
|
|
|
1228
1301
|
);
|
|
1229
1302
|
}
|
|
1230
1303
|
const prepared = await this.prepareGameDonation(gameId, amountMinor);
|
|
1231
|
-
const
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
);
|
|
1236
|
-
const result = await this.donateToGame(
|
|
1237
|
-
gameId,
|
|
1238
|
-
amountMinor,
|
|
1239
|
-
signedTransaction
|
|
1240
|
-
);
|
|
1304
|
+
const result = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
1305
|
+
onSigned: (signedTxBase64) => this.donateToGame(gameId, amountMinor, signedTxBase64),
|
|
1306
|
+
onSignedAndSent: (sig) => this.confirmGameDonationSignature(gameId, amountMinor, sig)
|
|
1307
|
+
});
|
|
1241
1308
|
return {
|
|
1242
1309
|
...result,
|
|
1243
1310
|
escrowAddress: prepared.escrowAddress,
|
|
@@ -1336,6 +1403,9 @@ var Chat = class {
|
|
|
1336
1403
|
this.logger = logger2;
|
|
1337
1404
|
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions };
|
|
1338
1405
|
}
|
|
1406
|
+
setDmThreadsStore(store) {
|
|
1407
|
+
this.dmThreadsStore = store;
|
|
1408
|
+
}
|
|
1339
1409
|
encodeContextId(id) {
|
|
1340
1410
|
return encodeURIComponent(id);
|
|
1341
1411
|
}
|
|
@@ -1417,7 +1487,9 @@ var Chat = class {
|
|
|
1417
1487
|
return response;
|
|
1418
1488
|
}
|
|
1419
1489
|
async listDmThreads() {
|
|
1420
|
-
|
|
1490
|
+
const threads = await this.http.get("/chat/dm/threads");
|
|
1491
|
+
this.dmThreadsStore?.setBaseState(threads);
|
|
1492
|
+
return threads;
|
|
1421
1493
|
}
|
|
1422
1494
|
async getDmThread(dmKey) {
|
|
1423
1495
|
return this.http.get(
|
|
@@ -1483,7 +1555,6 @@ var Challenges = class {
|
|
|
1483
1555
|
};
|
|
1484
1556
|
|
|
1485
1557
|
// src/tips.ts
|
|
1486
|
-
import { Transaction as Transaction3 } from "@solana/web3.js";
|
|
1487
1558
|
var Tips = class {
|
|
1488
1559
|
constructor(http, wallet, chat, logger2) {
|
|
1489
1560
|
this.http = http;
|
|
@@ -1510,21 +1581,32 @@ var Tips = class {
|
|
|
1510
1581
|
);
|
|
1511
1582
|
}
|
|
1512
1583
|
const { publicKey: senderAddress } = await this.wallet.loadWallet();
|
|
1513
|
-
const
|
|
1514
|
-
const
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
);
|
|
1519
|
-
const transfer = await this.wallet.
|
|
1520
|
-
signedTxBase64
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1584
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
1585
|
+
const prepared = await this.prepare({
|
|
1586
|
+
recipientUsername,
|
|
1587
|
+
amount,
|
|
1588
|
+
supportsPresign
|
|
1589
|
+
});
|
|
1590
|
+
const transfer = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
1591
|
+
onSigned: (signedTxBase64) => this.wallet.submitTransfer(
|
|
1592
|
+
signedTxBase64,
|
|
1593
|
+
senderAddress,
|
|
1594
|
+
prepared.recipientAddress,
|
|
1595
|
+
prepared.amount,
|
|
1596
|
+
"USDC",
|
|
1597
|
+
false,
|
|
1598
|
+
prepared.recipientUsername
|
|
1599
|
+
),
|
|
1600
|
+
onSignedAndSent: (sig) => this.wallet.confirmTransferSignature(
|
|
1601
|
+
sig,
|
|
1602
|
+
senderAddress,
|
|
1603
|
+
prepared.recipientAddress,
|
|
1604
|
+
prepared.amount,
|
|
1605
|
+
"USDC",
|
|
1606
|
+
false,
|
|
1607
|
+
prepared.recipientUsername
|
|
1608
|
+
)
|
|
1609
|
+
});
|
|
1528
1610
|
const message = await this.chat.broadcastGlobalTip(
|
|
1529
1611
|
prepared.recipientUserId,
|
|
1530
1612
|
prepared.amount
|
|
@@ -1636,7 +1718,7 @@ var Achievements = class {
|
|
|
1636
1718
|
};
|
|
1637
1719
|
|
|
1638
1720
|
// src/wallet.ts
|
|
1639
|
-
import { Transaction as
|
|
1721
|
+
import { Transaction as Transaction2 } from "@solana/web3.js";
|
|
1640
1722
|
var TRANSFER_FEE_MINOR = 1e4;
|
|
1641
1723
|
var MIN_TRANSFER_AMOUNT = 5e4;
|
|
1642
1724
|
var MIN_SOL_TRANSFER_AMOUNT = 1e6;
|
|
@@ -1853,13 +1935,15 @@ var Wallet = class {
|
|
|
1853
1935
|
);
|
|
1854
1936
|
}
|
|
1855
1937
|
try {
|
|
1938
|
+
const supportsPresign = !this.isSignAndSendMode();
|
|
1856
1939
|
const response = await this.http.post(
|
|
1857
1940
|
"/wallets/transfer/prepare",
|
|
1858
1941
|
{
|
|
1859
1942
|
senderAddress,
|
|
1860
1943
|
recipient,
|
|
1861
1944
|
amount: amountMinor,
|
|
1862
|
-
token
|
|
1945
|
+
token,
|
|
1946
|
+
supportsPresign
|
|
1863
1947
|
}
|
|
1864
1948
|
);
|
|
1865
1949
|
this.logger.debug("Transfer prepared", {
|
|
@@ -1943,6 +2027,29 @@ var Wallet = class {
|
|
|
1943
2027
|
throw error;
|
|
1944
2028
|
}
|
|
1945
2029
|
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Sign a prepared transaction and invoke the appropriate backend callback
|
|
2032
|
+
* based on the configured signer — without exposing the mode decision to callers.
|
|
2033
|
+
* - Embedded wallet (signAndSend): signs+sends, calls onSignedAndSent(signature)
|
|
2034
|
+
* - Injected wallet (signTransaction): signs locally, calls onSigned(signedTxBase64)
|
|
2035
|
+
*/
|
|
2036
|
+
async signAndDispatch(unsignedTxBase64, handlers) {
|
|
2037
|
+
if (!this.signer) {
|
|
2038
|
+
throw new Error(
|
|
2039
|
+
"No signer configured. Call setSigner() with a WalletSigner implementation first."
|
|
2040
|
+
);
|
|
2041
|
+
}
|
|
2042
|
+
const unsignedTx = Transaction2.from(base64ToBytes(unsignedTxBase64));
|
|
2043
|
+
if (this.isSignAndSendMode()) {
|
|
2044
|
+
const sig = await this.signAndSendTransaction(unsignedTx);
|
|
2045
|
+
return handlers.onSignedAndSent(sig);
|
|
2046
|
+
}
|
|
2047
|
+
const signedTx = await this.signTransaction(unsignedTx);
|
|
2048
|
+
const signedBase64 = bytesToBase64(
|
|
2049
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
2050
|
+
);
|
|
2051
|
+
return handlers.onSigned(signedBase64);
|
|
2052
|
+
}
|
|
1946
2053
|
/**
|
|
1947
2054
|
* Full transfer flow in one call: prepare -> sign -> submit
|
|
1948
2055
|
* Recipient can be username, .sol domain, or Solana address (resolved by backend).
|
|
@@ -1959,7 +2066,7 @@ var Wallet = class {
|
|
|
1959
2066
|
amount,
|
|
1960
2067
|
token
|
|
1961
2068
|
);
|
|
1962
|
-
const unsignedTx =
|
|
2069
|
+
const unsignedTx = Transaction2.from(base64ToBytes(prepared.transaction));
|
|
1963
2070
|
let submitted;
|
|
1964
2071
|
if (this.isSignAndSendMode()) {
|
|
1965
2072
|
const signature = await this.signAndSendTransaction(unsignedTx);
|
|
@@ -1999,7 +2106,7 @@ var Wallet = class {
|
|
|
1999
2106
|
};
|
|
2000
2107
|
|
|
2001
2108
|
// src/escrow.ts
|
|
2002
|
-
import { Transaction as
|
|
2109
|
+
import { Transaction as Transaction3 } from "@solana/web3.js";
|
|
2003
2110
|
var Escrow = class {
|
|
2004
2111
|
constructor(http, wallet, logger2) {
|
|
2005
2112
|
this.http = http;
|
|
@@ -2027,12 +2134,32 @@ var Escrow = class {
|
|
|
2027
2134
|
* initializes depositStatus, and prepares the unsigned transaction in one call.
|
|
2028
2135
|
* Eliminates one HTTP round-trip vs startDeposits() + prepareDepositTransaction().
|
|
2029
2136
|
*/
|
|
2030
|
-
async prepareAndStartDeposit(lobbyId) {
|
|
2137
|
+
async prepareAndStartDeposit(lobbyId, supportsPresign) {
|
|
2138
|
+
const presign = supportsPresign ?? !this.wallet.isSignAndSendMode();
|
|
2031
2139
|
return this.http.post(
|
|
2032
2140
|
`/escrow/lobby/${lobbyId}/deposit/prepare-and-start`,
|
|
2033
|
-
{}
|
|
2141
|
+
{ supportsPresign: presign }
|
|
2034
2142
|
);
|
|
2035
2143
|
}
|
|
2144
|
+
/**
|
|
2145
|
+
* Sign and submit a prepared (unsigned) deposit transaction using the
|
|
2146
|
+
* configured signer. Automatically picks the right path:
|
|
2147
|
+
* - Embedded wallet: signAndSendTransaction → confirmDepositSignature
|
|
2148
|
+
* - Injected wallet: signTransaction → submitDeposit
|
|
2149
|
+
* Returns the on-chain signature.
|
|
2150
|
+
*/
|
|
2151
|
+
async signAndSubmitPreparedDeposit(lobbyId, unsignedTxBase64) {
|
|
2152
|
+
return this.wallet.signAndDispatch(unsignedTxBase64, {
|
|
2153
|
+
onSigned: async (signedTxBase64) => {
|
|
2154
|
+
const result = await this.submitDeposit(lobbyId, signedTxBase64);
|
|
2155
|
+
return result.signature;
|
|
2156
|
+
},
|
|
2157
|
+
onSignedAndSent: async (signature) => {
|
|
2158
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
2159
|
+
return signature;
|
|
2160
|
+
}
|
|
2161
|
+
});
|
|
2162
|
+
}
|
|
2036
2163
|
/**
|
|
2037
2164
|
* Submit a signed deposit transaction
|
|
2038
2165
|
* The transaction will be submitted to the Solana network and confirmed
|
|
@@ -2075,8 +2202,12 @@ var Escrow = class {
|
|
|
2075
2202
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
2076
2203
|
);
|
|
2077
2204
|
}
|
|
2078
|
-
const
|
|
2079
|
-
const
|
|
2205
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
2206
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
2207
|
+
lobbyId,
|
|
2208
|
+
supportsPresign
|
|
2209
|
+
);
|
|
2210
|
+
const unsignedTx = Transaction3.from(base64ToBytes(transaction));
|
|
2080
2211
|
let signature;
|
|
2081
2212
|
if (this.wallet.isSignAndSendMode()) {
|
|
2082
2213
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
@@ -2110,8 +2241,13 @@ var Escrow = class {
|
|
|
2110
2241
|
};
|
|
2111
2242
|
}
|
|
2112
2243
|
/**
|
|
2113
|
-
*
|
|
2114
|
-
*
|
|
2244
|
+
* Deposit for a lobby and wait until the calling user's own deposit is confirmed.
|
|
2245
|
+
* Ideal for agents: returns as soon as your deposit is on-chain without waiting
|
|
2246
|
+
* for the other player. The server auto-joins the queue when all players deposit.
|
|
2247
|
+
*
|
|
2248
|
+
* Confirmation is handled asynchronously by the transaction queue processor.
|
|
2249
|
+
* This method polls the deposit status endpoint until the signature appears as
|
|
2250
|
+
* confirmed (up to 60 seconds).
|
|
2115
2251
|
*
|
|
2116
2252
|
* Automatically uses signAndSendTransaction or signTransaction based on signer capability.
|
|
2117
2253
|
*/
|
|
@@ -2121,26 +2257,45 @@ var Escrow = class {
|
|
|
2121
2257
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
2122
2258
|
);
|
|
2123
2259
|
}
|
|
2124
|
-
const
|
|
2125
|
-
const
|
|
2260
|
+
const supportsPresign2 = !this.wallet.isSignAndSendMode();
|
|
2261
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
2262
|
+
lobbyId,
|
|
2263
|
+
supportsPresign2
|
|
2264
|
+
);
|
|
2265
|
+
const unsignedTx = Transaction3.from(base64ToBytes(transaction));
|
|
2126
2266
|
let signature;
|
|
2127
2267
|
if (this.wallet.isSignAndSendMode()) {
|
|
2128
2268
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
2129
|
-
await this.
|
|
2130
|
-
`/escrow/lobby/${lobbyId}/deposit/confirm-signature?awaitConfirmation=true`,
|
|
2131
|
-
{ signature }
|
|
2132
|
-
);
|
|
2269
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
2133
2270
|
} else {
|
|
2134
2271
|
const signedTx = await this.wallet.signTransaction(unsignedTx);
|
|
2135
2272
|
const signedBase64 = bytesToBase64(
|
|
2136
2273
|
signedTx.serialize({ requireAllSignatures: false })
|
|
2137
2274
|
);
|
|
2138
|
-
const
|
|
2139
|
-
|
|
2140
|
-
});
|
|
2141
|
-
signature = result.signature;
|
|
2275
|
+
const submitResult = await this.submitDeposit(lobbyId, signedBase64);
|
|
2276
|
+
signature = submitResult.signature;
|
|
2142
2277
|
}
|
|
2143
|
-
|
|
2278
|
+
const MAX_WAIT_MS = 6e4;
|
|
2279
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
2280
|
+
const startTime = Date.now();
|
|
2281
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
2282
|
+
const status = await this.getDepositStatus(lobbyId);
|
|
2283
|
+
const myDeposit = status.deposits.find(
|
|
2284
|
+
(d) => d.transactionHash === signature
|
|
2285
|
+
);
|
|
2286
|
+
if (myDeposit?.status === "confirmed") {
|
|
2287
|
+
return {
|
|
2288
|
+
signature,
|
|
2289
|
+
status: "confirmed",
|
|
2290
|
+
canProceedToQueue: status.canProceedToQueue
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
if (status.allConfirmed && status.canProceedToQueue) {
|
|
2294
|
+
return { signature, status: "confirmed", canProceedToQueue: true };
|
|
2295
|
+
}
|
|
2296
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
2297
|
+
}
|
|
2298
|
+
return { signature, status: "pending", canProceedToQueue: false };
|
|
2144
2299
|
}
|
|
2145
2300
|
async claimLobbyDepositRefund(lobbyId, depositSignature) {
|
|
2146
2301
|
return this.http.post(
|
|
@@ -2496,9 +2651,10 @@ var Support = class {
|
|
|
2496
2651
|
|
|
2497
2652
|
// src/markets.ts
|
|
2498
2653
|
var Markets = class {
|
|
2499
|
-
constructor(http, logger2) {
|
|
2654
|
+
constructor(http, logger2, wallet) {
|
|
2500
2655
|
this.http = http;
|
|
2501
2656
|
this.logger = logger2;
|
|
2657
|
+
this.wallet = wallet;
|
|
2502
2658
|
}
|
|
2503
2659
|
/**
|
|
2504
2660
|
* Get the prediction market state for a game.
|
|
@@ -2540,6 +2696,35 @@ var Markets = class {
|
|
|
2540
2696
|
{ signedTransaction, outcomeId, amountMinor }
|
|
2541
2697
|
);
|
|
2542
2698
|
}
|
|
2699
|
+
/**
|
|
2700
|
+
* Confirm a buy order that was already broadcast by the client (signAndSendTransaction path).
|
|
2701
|
+
*/
|
|
2702
|
+
async confirmBuyOrderSignature(gameId, depositSignature, outcomeId, amountMinor) {
|
|
2703
|
+
return this.http.post(
|
|
2704
|
+
`/games/${gameId}/market/orders/buy/confirm-signature`,
|
|
2705
|
+
{ depositSignature, outcomeId, amountMinor }
|
|
2706
|
+
);
|
|
2707
|
+
}
|
|
2708
|
+
/**
|
|
2709
|
+
* One-call buy order: prepare → sign → submit, automatically choosing
|
|
2710
|
+
* the right signing path (embedded vs injected wallet).
|
|
2711
|
+
*/
|
|
2712
|
+
async buy(gameId, outcomeId, amountMinor) {
|
|
2713
|
+
if (!this.wallet?.hasSigner()) {
|
|
2714
|
+
throw new Error(
|
|
2715
|
+
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
2716
|
+
);
|
|
2717
|
+
}
|
|
2718
|
+
const { transaction } = await this.prepareBuyOrder(
|
|
2719
|
+
gameId,
|
|
2720
|
+
outcomeId,
|
|
2721
|
+
amountMinor
|
|
2722
|
+
);
|
|
2723
|
+
return this.wallet.signAndDispatch(transaction, {
|
|
2724
|
+
onSigned: (signedTxBase64) => this.submitBuyOrder(gameId, signedTxBase64, outcomeId, amountMinor),
|
|
2725
|
+
onSignedAndSent: (sig) => this.confirmBuyOrderSignature(gameId, sig, outcomeId, amountMinor)
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2543
2728
|
/**
|
|
2544
2729
|
* Sell shares back to the AMM pool.
|
|
2545
2730
|
* @param gameId - The game ID
|
|
@@ -2589,6 +2774,22 @@ var Markets = class {
|
|
|
2589
2774
|
}
|
|
2590
2775
|
};
|
|
2591
2776
|
|
|
2777
|
+
// src/analytics.ts
|
|
2778
|
+
var NoopAnalyticsClient = class {
|
|
2779
|
+
userLoggedIn(_user, _meta) {
|
|
2780
|
+
}
|
|
2781
|
+
userLoggedOut() {
|
|
2782
|
+
}
|
|
2783
|
+
sessionRestored(_user) {
|
|
2784
|
+
}
|
|
2785
|
+
track(_event, _properties) {
|
|
2786
|
+
}
|
|
2787
|
+
setUserProperties(_properties) {
|
|
2788
|
+
}
|
|
2789
|
+
group(_groupType, _groupKey, _properties) {
|
|
2790
|
+
}
|
|
2791
|
+
};
|
|
2792
|
+
|
|
2592
2793
|
// src/ws/standalone-transport.ts
|
|
2593
2794
|
import { io } from "socket.io-client";
|
|
2594
2795
|
|
|
@@ -2682,22 +2883,35 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2682
2883
|
this.accessToken = null;
|
|
2683
2884
|
this.reconnectAttempts = 0;
|
|
2684
2885
|
this.reconnectTimer = null;
|
|
2685
|
-
this.
|
|
2686
|
-
this.
|
|
2687
|
-
this.reconnectDelay = 1e3;
|
|
2688
|
-
this.reconnectDelayMax = 3e4;
|
|
2689
|
-
// 5 minutes
|
|
2886
|
+
this.needsReconnect = false;
|
|
2887
|
+
this.visibilityHandler = null;
|
|
2690
2888
|
this.wildcardHandlers = /* @__PURE__ */ new Set();
|
|
2691
2889
|
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
2692
2890
|
this.registeredEvents = /* @__PURE__ */ new Set();
|
|
2693
2891
|
this.wildcardRegistered = false;
|
|
2694
2892
|
this.url = url;
|
|
2893
|
+
if (typeof document !== "undefined") {
|
|
2894
|
+
this.visibilityHandler = () => {
|
|
2895
|
+
if (document.visibilityState === "visible" && !this.connectionState.connected) {
|
|
2896
|
+
this.reconnectImmediately();
|
|
2897
|
+
}
|
|
2898
|
+
};
|
|
2899
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
2900
|
+
}
|
|
2695
2901
|
}
|
|
2696
2902
|
connect() {
|
|
2903
|
+
if (this.reconnectTimer !== null) {
|
|
2904
|
+
clearTimeout(this.reconnectTimer);
|
|
2905
|
+
this.reconnectTimer = null;
|
|
2906
|
+
}
|
|
2907
|
+
this.reconnectAttempts = 0;
|
|
2697
2908
|
this.ensureSocket();
|
|
2698
2909
|
}
|
|
2699
2910
|
disconnect() {
|
|
2700
|
-
this.
|
|
2911
|
+
if (this.visibilityHandler && typeof document !== "undefined") {
|
|
2912
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
2913
|
+
this.visibilityHandler = null;
|
|
2914
|
+
}
|
|
2701
2915
|
if (!this.socket) return;
|
|
2702
2916
|
this.socket.disconnect();
|
|
2703
2917
|
this.socket = null;
|
|
@@ -2796,13 +3010,13 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2796
3010
|
reconnection: false
|
|
2797
3011
|
});
|
|
2798
3012
|
socket.on("connect", () => {
|
|
2799
|
-
this.clearPeriodicReconnect();
|
|
2800
3013
|
this.updateConnectionState({
|
|
2801
3014
|
connected: true,
|
|
2802
3015
|
connecting: false,
|
|
2803
3016
|
error: null
|
|
2804
3017
|
});
|
|
2805
|
-
const wasReconnect = this.
|
|
3018
|
+
const wasReconnect = this.needsReconnect;
|
|
3019
|
+
this.needsReconnect = false;
|
|
2806
3020
|
this.reconnectAttempts = 0;
|
|
2807
3021
|
this.onReconnect();
|
|
2808
3022
|
if (wasReconnect) {
|
|
@@ -2813,6 +3027,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2813
3027
|
});
|
|
2814
3028
|
socket.on("disconnect", (reason) => {
|
|
2815
3029
|
this.updateConnectionState({ connected: false, connecting: false });
|
|
3030
|
+
this.needsReconnect = true;
|
|
2816
3031
|
this.clearSocketForReconnect();
|
|
2817
3032
|
if (reason === "io server disconnect") {
|
|
2818
3033
|
this.handleAuthFailure("io server disconnect");
|
|
@@ -2822,6 +3037,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2822
3037
|
});
|
|
2823
3038
|
socket.on("connect_error", (error) => {
|
|
2824
3039
|
const message = error?.message || "connect_error";
|
|
3040
|
+
this.needsReconnect = true;
|
|
2825
3041
|
this.clearSocketForReconnect();
|
|
2826
3042
|
if (message.includes("unauthorized") || message.includes("401")) {
|
|
2827
3043
|
this.handleAuthFailure(message);
|
|
@@ -2849,31 +3065,31 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2849
3065
|
this.socket.removeAllListeners();
|
|
2850
3066
|
this.socket = null;
|
|
2851
3067
|
}
|
|
2852
|
-
|
|
2853
|
-
if (this.
|
|
2854
|
-
|
|
2855
|
-
this.
|
|
3068
|
+
reconnectImmediately() {
|
|
3069
|
+
if (this.reconnectTimer !== null) {
|
|
3070
|
+
clearTimeout(this.reconnectTimer);
|
|
3071
|
+
this.reconnectTimer = null;
|
|
2856
3072
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
if (this.periodicReconnectInterval !== null) return;
|
|
2860
|
-
this.periodicReconnectInterval = setInterval(() => {
|
|
2861
|
-
if (this.connectionState.connected) return;
|
|
2862
|
-
this.reconnectAttempts = 0;
|
|
2863
|
-
this.ensureSocket();
|
|
2864
|
-
}, _StandaloneWsTransport.PERIODIC_RECONNECT_MS);
|
|
3073
|
+
this.reconnectAttempts = 0;
|
|
3074
|
+
this.ensureSocket();
|
|
2865
3075
|
}
|
|
2866
3076
|
scheduleReconnect() {
|
|
2867
3077
|
if (this.reconnectTimer !== null) return;
|
|
2868
|
-
|
|
2869
|
-
this.startPeriodicReconnect();
|
|
2870
|
-
return;
|
|
2871
|
-
}
|
|
2872
|
-
const delay = Math.min(
|
|
2873
|
-
this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
|
|
2874
|
-
this.reconnectDelayMax
|
|
2875
|
-
);
|
|
3078
|
+
const attempt = this.reconnectAttempts;
|
|
2876
3079
|
this.reconnectAttempts += 1;
|
|
3080
|
+
let delay;
|
|
3081
|
+
if (attempt < _StandaloneWsTransport.FAST_RETRY_LIMIT) {
|
|
3082
|
+
delay = _StandaloneWsTransport.FAST_RETRY_DELAY_MS;
|
|
3083
|
+
} else {
|
|
3084
|
+
const backoffAttempt = attempt - _StandaloneWsTransport.FAST_RETRY_LIMIT;
|
|
3085
|
+
delay = Math.min(
|
|
3086
|
+
_StandaloneWsTransport.FAST_RETRY_DELAY_MS * Math.pow(2, backoffAttempt),
|
|
3087
|
+
_StandaloneWsTransport.MAX_BACKOFF_MS
|
|
3088
|
+
);
|
|
3089
|
+
if (attempt === _StandaloneWsTransport.FAST_RETRY_LIMIT) {
|
|
3090
|
+
this.dispatchEvent("connection:slow-retry", { timestamp: Date.now() });
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
2877
3093
|
this.reconnectTimer = setTimeout(() => {
|
|
2878
3094
|
this.reconnectTimer = null;
|
|
2879
3095
|
this.ensureSocket();
|
|
@@ -2934,7 +3150,9 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2934
3150
|
this.wildcardRegistered = true;
|
|
2935
3151
|
}
|
|
2936
3152
|
};
|
|
2937
|
-
_StandaloneWsTransport.
|
|
3153
|
+
_StandaloneWsTransport.FAST_RETRY_LIMIT = 60;
|
|
3154
|
+
_StandaloneWsTransport.FAST_RETRY_DELAY_MS = 1e3;
|
|
3155
|
+
_StandaloneWsTransport.MAX_BACKOFF_MS = 3e4;
|
|
2938
3156
|
var StandaloneWsTransport = _StandaloneWsTransport;
|
|
2939
3157
|
|
|
2940
3158
|
// src/stores/store-utils.ts
|
|
@@ -2961,14 +3179,16 @@ function createLobbyStore(transport) {
|
|
|
2961
3179
|
depositStatusByLobbyId: {}
|
|
2962
3180
|
});
|
|
2963
3181
|
const setBaseState = (lobbies) => {
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
3182
|
+
store.updateState((state) => {
|
|
3183
|
+
const lobbiesById = { ...state.lobbiesById };
|
|
3184
|
+
for (const lobby of lobbies) {
|
|
3185
|
+
const existing = lobbiesById[lobby.id];
|
|
3186
|
+
if (!existing || lobby.updatedAt >= existing.updatedAt) {
|
|
3187
|
+
lobbiesById[lobby.id] = lobby;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
return { ...state, lobbiesById };
|
|
3191
|
+
});
|
|
2972
3192
|
};
|
|
2973
3193
|
const applyWsEvent = (event) => {
|
|
2974
3194
|
switch (event.event) {
|
|
@@ -3100,12 +3320,23 @@ function createGameStore(transport) {
|
|
|
3100
3320
|
gamesById: {},
|
|
3101
3321
|
spectatorCounts: {}
|
|
3102
3322
|
});
|
|
3323
|
+
const STATUS_RANK = {
|
|
3324
|
+
active: 0,
|
|
3325
|
+
completed: 1,
|
|
3326
|
+
abandoned: 1
|
|
3327
|
+
};
|
|
3103
3328
|
const setBaseState = (games) => {
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3329
|
+
store.updateState((state) => {
|
|
3330
|
+
const gamesById = { ...state.gamesById };
|
|
3331
|
+
for (const game of games) {
|
|
3332
|
+
const existing = gamesById[game.gameId];
|
|
3333
|
+
if (existing && STATUS_RANK[existing.status] > STATUS_RANK[game.status]) {
|
|
3334
|
+
continue;
|
|
3335
|
+
}
|
|
3336
|
+
gamesById[game.gameId] = game;
|
|
3337
|
+
}
|
|
3338
|
+
return { ...state, gamesById };
|
|
3339
|
+
});
|
|
3109
3340
|
};
|
|
3110
3341
|
const applyWsEvent = (event) => {
|
|
3111
3342
|
switch (event.event) {
|
|
@@ -3182,6 +3413,19 @@ function createGameStore(transport) {
|
|
|
3182
3413
|
}
|
|
3183
3414
|
|
|
3184
3415
|
// src/stores/game-actions-store.ts
|
|
3416
|
+
function selectGameLifecycleState(gameState) {
|
|
3417
|
+
const s = gameState;
|
|
3418
|
+
if (!s) return null;
|
|
3419
|
+
const raw = s;
|
|
3420
|
+
return {
|
|
3421
|
+
status: raw.status,
|
|
3422
|
+
winnerId: raw.winnerId ?? null,
|
|
3423
|
+
betAmount: raw.betAmount ?? 0,
|
|
3424
|
+
wonAmount: raw.wonAmount ?? null,
|
|
3425
|
+
totalPotMinor: raw.totalPotMinor,
|
|
3426
|
+
currentPlayerId: raw.currentPlayerId ?? null
|
|
3427
|
+
};
|
|
3428
|
+
}
|
|
3185
3429
|
var isRpsCompletionPayload = (payload) => payload.gameType === "rock-paper-scissors";
|
|
3186
3430
|
function createGameActionsStore(transport) {
|
|
3187
3431
|
const store = createSdkStore({
|
|
@@ -3203,8 +3447,21 @@ function createGameActionsStore(transport) {
|
|
|
3203
3447
|
});
|
|
3204
3448
|
};
|
|
3205
3449
|
const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
|
|
3450
|
+
const pendingEvents = /* @__PURE__ */ new Map();
|
|
3451
|
+
function enqueue(gameId, event) {
|
|
3452
|
+
const q = pendingEvents.get(gameId) ?? [];
|
|
3453
|
+
q.push(event);
|
|
3454
|
+
pendingEvents.set(gameId, q);
|
|
3455
|
+
}
|
|
3456
|
+
function drainQueue(gameId) {
|
|
3457
|
+
const q = pendingEvents.get(gameId);
|
|
3458
|
+
if (!q?.length) return;
|
|
3459
|
+
pendingEvents.delete(gameId);
|
|
3460
|
+
for (const ev of q) applyWsEvent(ev);
|
|
3461
|
+
}
|
|
3206
3462
|
const setBaseState = (gameId, state) => {
|
|
3207
3463
|
updateState(gameId, state);
|
|
3464
|
+
drainQueue(gameId);
|
|
3208
3465
|
};
|
|
3209
3466
|
const clearState = (gameId) => {
|
|
3210
3467
|
store.updateState((state) => {
|
|
@@ -3214,6 +3471,7 @@ function createGameActionsStore(transport) {
|
|
|
3214
3471
|
const { [gameId]: _, ...rest } = state.statesByGameId;
|
|
3215
3472
|
return { ...state, statesByGameId: rest };
|
|
3216
3473
|
});
|
|
3474
|
+
pendingEvents.delete(gameId);
|
|
3217
3475
|
};
|
|
3218
3476
|
const applyWsEvent = (event) => {
|
|
3219
3477
|
switch (event.event) {
|
|
@@ -3232,7 +3490,10 @@ function createGameActionsStore(transport) {
|
|
|
3232
3490
|
}
|
|
3233
3491
|
case "game:rps:starting": {
|
|
3234
3492
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3235
|
-
if (!current || !isRpsState(current))
|
|
3493
|
+
if (!current || !isRpsState(current)) {
|
|
3494
|
+
enqueue(event.payload.gameId, event);
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3236
3497
|
const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
|
|
3237
3498
|
const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
|
|
3238
3499
|
const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
|
|
@@ -3253,7 +3514,10 @@ function createGameActionsStore(transport) {
|
|
|
3253
3514
|
}
|
|
3254
3515
|
case "game:rps:round:started": {
|
|
3255
3516
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3256
|
-
if (!current || !isRpsState(current))
|
|
3517
|
+
if (!current || !isRpsState(current)) {
|
|
3518
|
+
enqueue(event.payload.gameId, event);
|
|
3519
|
+
return;
|
|
3520
|
+
}
|
|
3257
3521
|
const actions = {};
|
|
3258
3522
|
const baseUsers = /* @__PURE__ */ new Set();
|
|
3259
3523
|
Object.keys(current.roundState.actions).forEach(
|
|
@@ -3287,7 +3551,10 @@ function createGameActionsStore(transport) {
|
|
|
3287
3551
|
}
|
|
3288
3552
|
case "game:rps:action:received": {
|
|
3289
3553
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3290
|
-
if (!current || !isRpsState(current))
|
|
3554
|
+
if (!current || !isRpsState(current)) {
|
|
3555
|
+
enqueue(event.payload.gameId, event);
|
|
3556
|
+
return;
|
|
3557
|
+
}
|
|
3291
3558
|
const updated = {
|
|
3292
3559
|
...current,
|
|
3293
3560
|
roundState: {
|
|
@@ -3306,7 +3573,10 @@ function createGameActionsStore(transport) {
|
|
|
3306
3573
|
}
|
|
3307
3574
|
case "game:rps:timer:cutoff": {
|
|
3308
3575
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3309
|
-
if (!current || !isRpsState(current))
|
|
3576
|
+
if (!current || !isRpsState(current)) {
|
|
3577
|
+
enqueue(event.payload.gameId, event);
|
|
3578
|
+
return;
|
|
3579
|
+
}
|
|
3310
3580
|
const updated = {
|
|
3311
3581
|
...current,
|
|
3312
3582
|
roundState: {
|
|
@@ -3320,7 +3590,10 @@ function createGameActionsStore(transport) {
|
|
|
3320
3590
|
}
|
|
3321
3591
|
case "game:rps:round:reveal": {
|
|
3322
3592
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3323
|
-
if (!current || !isRpsState(current))
|
|
3593
|
+
if (!current || !isRpsState(current)) {
|
|
3594
|
+
enqueue(event.payload.gameId, event);
|
|
3595
|
+
return;
|
|
3596
|
+
}
|
|
3324
3597
|
const actions = {};
|
|
3325
3598
|
const payloadActions = event.payload.actions;
|
|
3326
3599
|
Object.keys(payloadActions || {}).forEach((userId) => {
|
|
@@ -3344,7 +3617,10 @@ function createGameActionsStore(transport) {
|
|
|
3344
3617
|
}
|
|
3345
3618
|
case "game:rps:round:completed": {
|
|
3346
3619
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3347
|
-
if (!current || !isRpsState(current))
|
|
3620
|
+
if (!current || !isRpsState(current)) {
|
|
3621
|
+
enqueue(event.payload.gameId, event);
|
|
3622
|
+
return;
|
|
3623
|
+
}
|
|
3348
3624
|
const roundHistory = [
|
|
3349
3625
|
...current.roundHistory || [],
|
|
3350
3626
|
{
|
|
@@ -3369,7 +3645,10 @@ function createGameActionsStore(transport) {
|
|
|
3369
3645
|
}
|
|
3370
3646
|
case "game:rps:timeout": {
|
|
3371
3647
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3372
|
-
if (!current || !isRpsState(current))
|
|
3648
|
+
if (!current || !isRpsState(current)) {
|
|
3649
|
+
enqueue(event.payload.gameId, event);
|
|
3650
|
+
return;
|
|
3651
|
+
}
|
|
3373
3652
|
const timedOutUser = event.payload.playerId;
|
|
3374
3653
|
const action = event.payload.action;
|
|
3375
3654
|
const updated = {
|
|
@@ -3394,7 +3673,10 @@ function createGameActionsStore(transport) {
|
|
|
3394
3673
|
const payload = event.payload;
|
|
3395
3674
|
const { gameId } = payload;
|
|
3396
3675
|
const current = store.getState().statesByGameId[gameId];
|
|
3397
|
-
if (!current)
|
|
3676
|
+
if (!current) {
|
|
3677
|
+
enqueue(gameId, event);
|
|
3678
|
+
return;
|
|
3679
|
+
}
|
|
3398
3680
|
const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
|
|
3399
3681
|
...current,
|
|
3400
3682
|
status: "completed",
|
|
@@ -3443,11 +3725,15 @@ function createGameActionsStore(transport) {
|
|
|
3443
3725
|
const current = store.getState().statesByGameId[gameId];
|
|
3444
3726
|
const updated = current ? { ...current, ...incoming } : incoming;
|
|
3445
3727
|
updateState(gameId, updated);
|
|
3728
|
+
drainQueue(gameId);
|
|
3446
3729
|
break;
|
|
3447
3730
|
}
|
|
3448
3731
|
case "game:rematch:requested": {
|
|
3449
3732
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3450
|
-
if (!current)
|
|
3733
|
+
if (!current) {
|
|
3734
|
+
enqueue(event.payload.gameId, event);
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
3451
3737
|
const requestedBy = event.payload.requestedBy;
|
|
3452
3738
|
const userId = event.payload.userId;
|
|
3453
3739
|
const requested = new Set(
|
|
@@ -3463,7 +3749,10 @@ function createGameActionsStore(transport) {
|
|
|
3463
3749
|
}
|
|
3464
3750
|
case "game:rematch:cancelled": {
|
|
3465
3751
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3466
|
-
if (!current)
|
|
3752
|
+
if (!current) {
|
|
3753
|
+
enqueue(event.payload.gameId, event);
|
|
3754
|
+
return;
|
|
3755
|
+
}
|
|
3467
3756
|
const requestedBy = event.payload.requestedBy ?? [];
|
|
3468
3757
|
const updated = {
|
|
3469
3758
|
...current,
|
|
@@ -3474,7 +3763,10 @@ function createGameActionsStore(transport) {
|
|
|
3474
3763
|
}
|
|
3475
3764
|
case "game:rematch:started": {
|
|
3476
3765
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3477
|
-
if (!current)
|
|
3766
|
+
if (!current) {
|
|
3767
|
+
enqueue(event.payload.gameId, event);
|
|
3768
|
+
return;
|
|
3769
|
+
}
|
|
3478
3770
|
const updated = {
|
|
3479
3771
|
...current,
|
|
3480
3772
|
rematchRequestedBy: event.payload.playerIds ?? []
|
|
@@ -3485,7 +3777,10 @@ function createGameActionsStore(transport) {
|
|
|
3485
3777
|
case "game:pot:updated": {
|
|
3486
3778
|
const { gameId, totalPotMinor } = event.payload;
|
|
3487
3779
|
const current = store.getState().statesByGameId[gameId];
|
|
3488
|
-
if (!current)
|
|
3780
|
+
if (!current) {
|
|
3781
|
+
enqueue(gameId, event);
|
|
3782
|
+
return;
|
|
3783
|
+
}
|
|
3489
3784
|
const updated = {
|
|
3490
3785
|
...current,
|
|
3491
3786
|
totalPotMinor
|
|
@@ -3498,12 +3793,123 @@ function createGameActionsStore(transport) {
|
|
|
3498
3793
|
}
|
|
3499
3794
|
};
|
|
3500
3795
|
const joinGame = (gameId) => transport.joinRoom(`game:${gameId}`);
|
|
3796
|
+
const getCountdownDigit = (gameId, nowMs) => {
|
|
3797
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3798
|
+
if (!state) return null;
|
|
3799
|
+
if (isRpsState(state)) {
|
|
3800
|
+
if (state.roundState.phase !== "starting") return null;
|
|
3801
|
+
const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
|
|
3802
|
+
if (remaining <= 0) return null;
|
|
3803
|
+
return Math.ceil(remaining / 1e3);
|
|
3804
|
+
}
|
|
3805
|
+
const bufferEndsAt = state.bufferEndsAt;
|
|
3806
|
+
if (bufferEndsAt) {
|
|
3807
|
+
const remaining = new Date(bufferEndsAt).getTime() - nowMs;
|
|
3808
|
+
if (remaining <= 0) return null;
|
|
3809
|
+
return Math.ceil(remaining / 1e3);
|
|
3810
|
+
}
|
|
3811
|
+
return null;
|
|
3812
|
+
};
|
|
3813
|
+
const getChessClockTimes = (gameId, nowMs) => {
|
|
3814
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3815
|
+
if (!state || state.gameType !== "chess") return null;
|
|
3816
|
+
const s = state;
|
|
3817
|
+
let whiteMs = s.whiteTimeMs ?? 0;
|
|
3818
|
+
let blackMs = s.blackTimeMs ?? 0;
|
|
3819
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3820
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3821
|
+
if (!Number.isNaN(startedAt)) {
|
|
3822
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3823
|
+
if (s.currentPlayerId === s.whitePlayerId) {
|
|
3824
|
+
whiteMs = Math.max(0, whiteMs - elapsed);
|
|
3825
|
+
} else if (s.currentPlayerId === s.blackPlayerId) {
|
|
3826
|
+
blackMs = Math.max(0, blackMs - elapsed);
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
|
|
3831
|
+
};
|
|
3832
|
+
const getChessCapturedPieces = (gameId) => {
|
|
3833
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3834
|
+
if (!state || state.gameType !== "chess") return null;
|
|
3835
|
+
const fen = state.fen;
|
|
3836
|
+
if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
|
|
3837
|
+
const placement = fen.split(" ")[0];
|
|
3838
|
+
const white = {};
|
|
3839
|
+
const black = {};
|
|
3840
|
+
for (const char of placement) {
|
|
3841
|
+
if (char === "/" || char >= "1" && char <= "8") continue;
|
|
3842
|
+
const lower = char.toLowerCase();
|
|
3843
|
+
if (char === lower) {
|
|
3844
|
+
black[lower] = (black[lower] ?? 0) + 1;
|
|
3845
|
+
} else {
|
|
3846
|
+
white[lower] = (white[lower] ?? 0) + 1;
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
const INITIAL = {
|
|
3850
|
+
p: 8,
|
|
3851
|
+
r: 2,
|
|
3852
|
+
n: 2,
|
|
3853
|
+
b: 2,
|
|
3854
|
+
q: 1,
|
|
3855
|
+
k: 1
|
|
3856
|
+
};
|
|
3857
|
+
const capturedByWhite = [];
|
|
3858
|
+
const capturedByBlack = [];
|
|
3859
|
+
for (const [type, initial] of Object.entries(INITIAL)) {
|
|
3860
|
+
const missingBlack = initial - (black[type] ?? 0);
|
|
3861
|
+
for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type}`);
|
|
3862
|
+
const missingWhite = initial - (white[type] ?? 0);
|
|
3863
|
+
for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type}`);
|
|
3864
|
+
}
|
|
3865
|
+
return { capturedByWhite, capturedByBlack };
|
|
3866
|
+
};
|
|
3867
|
+
const getTicTacToeClockTimes = (gameId, nowMs) => {
|
|
3868
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3869
|
+
if (!state || state.gameType !== "tic-tac-toe") return null;
|
|
3870
|
+
const s = state;
|
|
3871
|
+
let xMs = s.xTimeMs ?? 0;
|
|
3872
|
+
let oMs = s.oTimeMs ?? 0;
|
|
3873
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3874
|
+
const currentMark = s.playerMarks[s.currentPlayerId];
|
|
3875
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3876
|
+
if (!Number.isNaN(startedAt)) {
|
|
3877
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3878
|
+
if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
|
|
3879
|
+
else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
return { xTimeMs: xMs, oTimeMs: oMs };
|
|
3883
|
+
};
|
|
3884
|
+
const getConnect4ClockTimes = (gameId, nowMs) => {
|
|
3885
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3886
|
+
if (!state || state.gameType !== "connect-four") return null;
|
|
3887
|
+
const s = state;
|
|
3888
|
+
let redMs = s.redTimeMs ?? 0;
|
|
3889
|
+
let yellowMs = s.yellowTimeMs ?? 0;
|
|
3890
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3891
|
+
const currentColor = s.playerColors[s.currentPlayerId];
|
|
3892
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3893
|
+
if (!Number.isNaN(startedAt)) {
|
|
3894
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3895
|
+
if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
|
|
3896
|
+
else if (currentColor === "YELLOW")
|
|
3897
|
+
yellowMs = Math.max(0, yellowMs - elapsed);
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
return { redTimeMs: redMs, yellowTimeMs: yellowMs };
|
|
3901
|
+
};
|
|
3501
3902
|
return {
|
|
3502
3903
|
store,
|
|
3503
3904
|
setBaseState,
|
|
3504
3905
|
clearState,
|
|
3505
3906
|
applyWsEvent,
|
|
3506
|
-
joinGame
|
|
3907
|
+
joinGame,
|
|
3908
|
+
getCountdownDigit,
|
|
3909
|
+
getChessClockTimes,
|
|
3910
|
+
getChessCapturedPieces,
|
|
3911
|
+
getTicTacToeClockTimes,
|
|
3912
|
+
getConnect4ClockTimes
|
|
3507
3913
|
};
|
|
3508
3914
|
}
|
|
3509
3915
|
function isRpsState(state) {
|
|
@@ -3851,7 +4257,38 @@ function createNotificationsStore() {
|
|
|
3851
4257
|
appNotifications: []
|
|
3852
4258
|
}));
|
|
3853
4259
|
};
|
|
3854
|
-
|
|
4260
|
+
const markAsRead = (id) => {
|
|
4261
|
+
store.updateState((state) => ({
|
|
4262
|
+
...state,
|
|
4263
|
+
appNotifications: state.appNotifications.map(
|
|
4264
|
+
(n) => n.id === id ? { ...n, read: true } : n
|
|
4265
|
+
)
|
|
4266
|
+
}));
|
|
4267
|
+
};
|
|
4268
|
+
const markAllAsRead = () => {
|
|
4269
|
+
store.updateState((state) => ({
|
|
4270
|
+
...state,
|
|
4271
|
+
appNotifications: state.appNotifications.map((n) => ({
|
|
4272
|
+
...n,
|
|
4273
|
+
read: true
|
|
4274
|
+
}))
|
|
4275
|
+
}));
|
|
4276
|
+
};
|
|
4277
|
+
const dismiss = (id) => {
|
|
4278
|
+
store.updateState((state) => ({
|
|
4279
|
+
...state,
|
|
4280
|
+
appNotifications: state.appNotifications.filter((n) => n.id !== id)
|
|
4281
|
+
}));
|
|
4282
|
+
};
|
|
4283
|
+
return {
|
|
4284
|
+
store,
|
|
4285
|
+
applyWsEvent,
|
|
4286
|
+
setListFromApi,
|
|
4287
|
+
clear,
|
|
4288
|
+
markAsRead,
|
|
4289
|
+
markAllAsRead,
|
|
4290
|
+
dismiss
|
|
4291
|
+
};
|
|
3855
4292
|
}
|
|
3856
4293
|
|
|
3857
4294
|
// src/stores/friends-store.ts
|
|
@@ -4142,12 +4579,14 @@ var WsRouter = class {
|
|
|
4142
4579
|
}
|
|
4143
4580
|
const decoded = decodeWsEvent(eventName, payload);
|
|
4144
4581
|
if (!decoded) return;
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
this.deps.
|
|
4148
|
-
this.deps.
|
|
4149
|
-
this.deps.
|
|
4150
|
-
this.deps.
|
|
4582
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
4583
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
4584
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
4585
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
4586
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
4587
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
4588
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
4589
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
4151
4590
|
});
|
|
4152
4591
|
}
|
|
4153
4592
|
stop() {
|
|
@@ -4159,12 +4598,14 @@ var WsRouter = class {
|
|
|
4159
4598
|
this.transport.subscribeEvent(eventName, (payload) => {
|
|
4160
4599
|
const decoded = decodeWsEvent(eventName, payload);
|
|
4161
4600
|
if (!decoded) return;
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
this.deps.
|
|
4165
|
-
this.deps.
|
|
4166
|
-
this.deps.
|
|
4167
|
-
this.deps.
|
|
4601
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
4602
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
4603
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
4604
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
4605
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
4606
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
4607
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
4608
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
4168
4609
|
});
|
|
4169
4610
|
}
|
|
4170
4611
|
}
|
|
@@ -4208,6 +4649,7 @@ var SDK = class {
|
|
|
4208
4649
|
constructor(config) {
|
|
4209
4650
|
const baseUrl = config.baseUrl || "http://localhost:3000";
|
|
4210
4651
|
const logger2 = config.logger || logger;
|
|
4652
|
+
this.analytics = config.analytics ?? new NoopAnalyticsClient();
|
|
4211
4653
|
this.http = config.httpClient || new HttpClient(
|
|
4212
4654
|
baseUrl,
|
|
4213
4655
|
config.storage,
|
|
@@ -4240,14 +4682,18 @@ var SDK = class {
|
|
|
4240
4682
|
this.referrals = new Referrals(this.http, logger2);
|
|
4241
4683
|
this.reports = new Reports(this.http, logger2);
|
|
4242
4684
|
this.support = new Support(this.http, logger2);
|
|
4243
|
-
this.markets = new Markets(this.http, logger2);
|
|
4685
|
+
this.markets = new Markets(this.http, logger2, this.wallet);
|
|
4244
4686
|
this.wsTransport = config.wsTransport || new StandaloneWsTransport(baseUrl);
|
|
4245
4687
|
this.wsTransport.setAppId(config.appId);
|
|
4246
4688
|
this.lobbyStore = createLobbyStore(this.wsTransport);
|
|
4689
|
+
this.lobbies.setLobbyStore(this.lobbyStore);
|
|
4247
4690
|
this.gameStore = createGameStore(this.wsTransport);
|
|
4691
|
+
this.games.setGameStore(this.gameStore);
|
|
4248
4692
|
this.gameActionsStore = createGameActionsStore(this.wsTransport);
|
|
4693
|
+
this.games.setGameActionsStore(this.gameActionsStore);
|
|
4249
4694
|
this.chatStore = createChatStore(this.wsTransport);
|
|
4250
4695
|
this.dmThreadsStore = createDmThreadsStore();
|
|
4696
|
+
this.chat.setDmThreadsStore(this.dmThreadsStore);
|
|
4251
4697
|
this.notificationsStore = createNotificationsStore();
|
|
4252
4698
|
this.friendsStore = createFriendsStore();
|
|
4253
4699
|
this.notifications = new Notifications(
|
|
@@ -4277,6 +4723,29 @@ var SDK = class {
|
|
|
4277
4723
|
this.wsTransport.connect();
|
|
4278
4724
|
await this.wsTransport.waitUntilConnected(timeoutMs);
|
|
4279
4725
|
}
|
|
4726
|
+
/**
|
|
4727
|
+
* Handle the full deposit-to-queue lifecycle for a lobby.
|
|
4728
|
+
*
|
|
4729
|
+
* After the deposit is confirmed, the backend may not have processed the
|
|
4730
|
+
* `lobby.deposit.allConfirmed` event yet, so the lobby can still be in
|
|
4731
|
+
* 'preparing' status. This method re-fetches the lobby and explicitly
|
|
4732
|
+
* joins the queue if needed, then pushes the latest state to the store
|
|
4733
|
+
* so the UI transitions immediately.
|
|
4734
|
+
*/
|
|
4735
|
+
async depositAndJoinQueue(lobbyId) {
|
|
4736
|
+
const result = await this.escrow.depositForLobby(lobbyId);
|
|
4737
|
+
if (!result.canProceedToQueue) {
|
|
4738
|
+
const lobby2 = await this.lobbies.getLobby(lobbyId);
|
|
4739
|
+
this.lobbyStore.setBaseState([lobby2]);
|
|
4740
|
+
return { ...result, lobby: lobby2 };
|
|
4741
|
+
}
|
|
4742
|
+
let lobby = await this.lobbies.getLobby(lobbyId);
|
|
4743
|
+
if (lobby.status === "preparing") {
|
|
4744
|
+
lobby = await this.lobbies.joinQueue(lobbyId);
|
|
4745
|
+
}
|
|
4746
|
+
this.lobbyStore.setBaseState([lobby]);
|
|
4747
|
+
return { ...result, lobby };
|
|
4748
|
+
}
|
|
4280
4749
|
};
|
|
4281
4750
|
|
|
4282
4751
|
// ../ws-shared-worker/dist/protocol.js
|
|
@@ -4384,6 +4853,26 @@ var SharedWorkerClient = class {
|
|
|
4384
4853
|
}
|
|
4385
4854
|
}
|
|
4386
4855
|
}
|
|
4856
|
+
/**
|
|
4857
|
+
* Dispatch a synthetic local event directly to all registered handlers for
|
|
4858
|
+
* that event name (and wildcard handlers), without sending anything over the
|
|
4859
|
+
* wire. Used by SharedWorkerTransport to fire synthetic events like
|
|
4860
|
+
* `connection:reconnected` that originate from transport-layer logic rather
|
|
4861
|
+
* than from the server.
|
|
4862
|
+
*/
|
|
4863
|
+
dispatchLocalEvent(eventName, payload) {
|
|
4864
|
+
const handlers = this.eventHandlers.get(eventName);
|
|
4865
|
+
if (handlers) {
|
|
4866
|
+
for (const handler of handlers) {
|
|
4867
|
+
handler(payload);
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
if (this.wildcardHandlers.size > 0) {
|
|
4871
|
+
for (const handler of this.wildcardHandlers) {
|
|
4872
|
+
handler(eventName, payload);
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4387
4876
|
postMessage(command) {
|
|
4388
4877
|
this.port.postMessage(command);
|
|
4389
4878
|
}
|
|
@@ -4393,6 +4882,10 @@ var SharedWorkerClient = class {
|
|
|
4393
4882
|
var SharedWorkerTransport = class extends BaseWsTransport {
|
|
4394
4883
|
constructor(workerUrl, socketUrl) {
|
|
4395
4884
|
super();
|
|
4885
|
+
/** Tracks whether a successful connection has been established at least once,
|
|
4886
|
+
* so that subsequent ws_open events are treated as reconnects and the
|
|
4887
|
+
* `connection:reconnected` event is dispatched to subscribers. */
|
|
4888
|
+
this.hasConnectedBefore = false;
|
|
4396
4889
|
this.client = new SharedWorkerClient(workerUrl);
|
|
4397
4890
|
this.url = socketUrl;
|
|
4398
4891
|
this.client.onMessage((event) => this.handleWorkerMessage(event));
|
|
@@ -4433,14 +4926,22 @@ var SharedWorkerTransport = class extends BaseWsTransport {
|
|
|
4433
4926
|
}
|
|
4434
4927
|
handleWorkerMessage(event) {
|
|
4435
4928
|
switch (event.type) {
|
|
4436
|
-
case "ws_open":
|
|
4929
|
+
case "ws_open": {
|
|
4930
|
+
const wasReconnect = this.hasConnectedBefore;
|
|
4931
|
+
this.hasConnectedBefore = true;
|
|
4437
4932
|
this.updateConnectionState({
|
|
4438
4933
|
connected: true,
|
|
4439
4934
|
connecting: false,
|
|
4440
4935
|
error: null
|
|
4441
4936
|
});
|
|
4442
4937
|
this.onReconnect();
|
|
4938
|
+
if (wasReconnect) {
|
|
4939
|
+
this.client.dispatchLocalEvent("connection:reconnected", {
|
|
4940
|
+
timestamp: Date.now()
|
|
4941
|
+
});
|
|
4942
|
+
}
|
|
4443
4943
|
break;
|
|
4944
|
+
}
|
|
4444
4945
|
case "ws_close":
|
|
4445
4946
|
this.updateConnectionState({
|
|
4446
4947
|
connected: false,
|
|
@@ -4479,6 +4980,7 @@ export {
|
|
|
4479
4980
|
MIN_TRANSFER_AMOUNT,
|
|
4480
4981
|
Markets,
|
|
4481
4982
|
NodeStorage,
|
|
4983
|
+
NoopAnalyticsClient,
|
|
4482
4984
|
Referrals,
|
|
4483
4985
|
Reports,
|
|
4484
4986
|
SDK,
|
|
@@ -4506,6 +5008,7 @@ export {
|
|
|
4506
5008
|
export_formatMoneyMinor as formatMoneyMinor,
|
|
4507
5009
|
isRetryableError,
|
|
4508
5010
|
logger,
|
|
5011
|
+
selectGameLifecycleState,
|
|
4509
5012
|
export_toMajor as toMajor,
|
|
4510
5013
|
withRetry
|
|
4511
5014
|
};
|