@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.cjs
CHANGED
|
@@ -214,6 +214,7 @@ __export(index_exports, {
|
|
|
214
214
|
MIN_TRANSFER_AMOUNT: () => MIN_TRANSFER_AMOUNT,
|
|
215
215
|
Markets: () => Markets,
|
|
216
216
|
NodeStorage: () => NodeStorage,
|
|
217
|
+
NoopAnalyticsClient: () => NoopAnalyticsClient,
|
|
217
218
|
Referrals: () => Referrals,
|
|
218
219
|
Reports: () => Reports,
|
|
219
220
|
SDK: () => SDK,
|
|
@@ -241,6 +242,7 @@ __export(index_exports, {
|
|
|
241
242
|
formatMoneyMinor: () => import_utils.formatMoneyMinor,
|
|
242
243
|
isRetryableError: () => isRetryableError,
|
|
243
244
|
logger: () => logger,
|
|
245
|
+
selectGameLifecycleState: () => selectGameLifecycleState,
|
|
244
246
|
toMajor: () => import_utils.toMajor,
|
|
245
247
|
withRetry: () => withRetry
|
|
246
248
|
});
|
|
@@ -748,6 +750,16 @@ var Auth = class {
|
|
|
748
750
|
this.logger.debug("Token stored in storage", { tokenKey: TOKEN_KEY });
|
|
749
751
|
return response;
|
|
750
752
|
}
|
|
753
|
+
async loginWithExternalSignature(address, signedMessage, options) {
|
|
754
|
+
const response = await this.http.post("/auth/login-wallet", {
|
|
755
|
+
signedMessage,
|
|
756
|
+
address,
|
|
757
|
+
referralCode: options?.referralCode,
|
|
758
|
+
walletMeta: options?.walletMeta
|
|
759
|
+
});
|
|
760
|
+
this.storage.set(TOKEN_KEY, response.access_token);
|
|
761
|
+
return response;
|
|
762
|
+
}
|
|
751
763
|
logout() {
|
|
752
764
|
this.logger.debug("Auth.logout called");
|
|
753
765
|
const hadToken = this.storage.get(TOKEN_KEY) !== null;
|
|
@@ -761,6 +773,10 @@ var Auth = class {
|
|
|
761
773
|
});
|
|
762
774
|
return isAuth;
|
|
763
775
|
}
|
|
776
|
+
async getSessionStats() {
|
|
777
|
+
this.logger.debug("Auth.getSessionStats called");
|
|
778
|
+
return this.http.get("/auth/sessions/stats");
|
|
779
|
+
}
|
|
764
780
|
async getLatestSessions(limit) {
|
|
765
781
|
this.logger.debug("Auth.getLatestSessions called", { limit });
|
|
766
782
|
const params = new URLSearchParams();
|
|
@@ -781,6 +797,9 @@ var Admin = class {
|
|
|
781
797
|
this.http = http;
|
|
782
798
|
this.logger = logger2;
|
|
783
799
|
}
|
|
800
|
+
async getInternalBots() {
|
|
801
|
+
return this.http.get("/admin/internal-bots");
|
|
802
|
+
}
|
|
784
803
|
async getUserById(id) {
|
|
785
804
|
return this.http.get(`/admin/users/${id}`);
|
|
786
805
|
}
|
|
@@ -988,11 +1007,25 @@ var Users = class {
|
|
|
988
1007
|
async removeFriend(userId) {
|
|
989
1008
|
return this.http.delete(`/friends/${userId}`);
|
|
990
1009
|
}
|
|
991
|
-
async getIncomingFriendRequests() {
|
|
992
|
-
|
|
1010
|
+
async getIncomingFriendRequests(opts) {
|
|
1011
|
+
const params = new URLSearchParams();
|
|
1012
|
+
if (opts?.limit !== void 0)
|
|
1013
|
+
params.append("limit", opts.limit.toString());
|
|
1014
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
1015
|
+
const qs = params.toString();
|
|
1016
|
+
return this.http.get(
|
|
1017
|
+
qs ? `/friends/requests/incoming?${qs}` : "/friends/requests/incoming"
|
|
1018
|
+
);
|
|
993
1019
|
}
|
|
994
|
-
async getOutgoingFriendRequests() {
|
|
995
|
-
|
|
1020
|
+
async getOutgoingFriendRequests(opts) {
|
|
1021
|
+
const params = new URLSearchParams();
|
|
1022
|
+
if (opts?.limit !== void 0)
|
|
1023
|
+
params.append("limit", opts.limit.toString());
|
|
1024
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
1025
|
+
const qs = params.toString();
|
|
1026
|
+
return this.http.get(
|
|
1027
|
+
qs ? `/friends/requests/outgoing?${qs}` : "/friends/requests/outgoing"
|
|
1028
|
+
);
|
|
996
1029
|
}
|
|
997
1030
|
async acceptFriendRequest(userId) {
|
|
998
1031
|
return this.http.post(
|
|
@@ -1081,6 +1114,9 @@ var FeatureFlags = class {
|
|
|
1081
1114
|
this.loaded = true;
|
|
1082
1115
|
return this.flags;
|
|
1083
1116
|
}
|
|
1117
|
+
async getAdminFeatureFlags() {
|
|
1118
|
+
return this.http.get("/feature-flags/admin");
|
|
1119
|
+
}
|
|
1084
1120
|
isEnabledFlag(name) {
|
|
1085
1121
|
const flag = this.flags.find((f) => f.name === name);
|
|
1086
1122
|
return flag?.enabled ?? false;
|
|
@@ -1100,10 +1136,11 @@ var FeatureFlags = class {
|
|
|
1100
1136
|
}
|
|
1101
1137
|
return flag;
|
|
1102
1138
|
}
|
|
1103
|
-
async createFeatureFlag(name, enabled) {
|
|
1139
|
+
async createFeatureFlag(name, enabled, description) {
|
|
1104
1140
|
const flag = await this.http.post("/feature-flags", {
|
|
1105
1141
|
name,
|
|
1106
|
-
enabled
|
|
1142
|
+
enabled,
|
|
1143
|
+
...description !== void 0 && { description }
|
|
1107
1144
|
});
|
|
1108
1145
|
this.flags.push(flag);
|
|
1109
1146
|
return flag;
|
|
@@ -1116,11 +1153,17 @@ var Lobbies = class {
|
|
|
1116
1153
|
this.http = http;
|
|
1117
1154
|
this.logger = logger2;
|
|
1118
1155
|
}
|
|
1156
|
+
/** Called by SDK after the store is created so mutations can update state directly. */
|
|
1157
|
+
setLobbyStore(store) {
|
|
1158
|
+
this.lobbyStore = store;
|
|
1159
|
+
}
|
|
1119
1160
|
async createLobby(gameType, betAmount) {
|
|
1120
1161
|
return this.http.post("/lobbies", { gameType, betAmount });
|
|
1121
1162
|
}
|
|
1122
1163
|
async getLobby(lobbyId) {
|
|
1123
|
-
|
|
1164
|
+
const lobby = await this.http.get(`/lobbies/${lobbyId}`);
|
|
1165
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
1166
|
+
return lobby;
|
|
1124
1167
|
}
|
|
1125
1168
|
async inviteFriend(lobbyId, friendId) {
|
|
1126
1169
|
return this.http.post(`/lobbies/${lobbyId}/invite`, {
|
|
@@ -1131,7 +1174,9 @@ var Lobbies = class {
|
|
|
1131
1174
|
return this.http.post(`/lobbies/${lobbyId}/accept-invite`, {});
|
|
1132
1175
|
}
|
|
1133
1176
|
async joinLobby(lobbyId) {
|
|
1134
|
-
|
|
1177
|
+
const lobby = await this.http.post(`/lobbies/${lobbyId}/join`, {});
|
|
1178
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
1179
|
+
return lobby;
|
|
1135
1180
|
}
|
|
1136
1181
|
async removePlayer(lobbyId, userId) {
|
|
1137
1182
|
return this.http.delete(
|
|
@@ -1148,12 +1193,17 @@ var Lobbies = class {
|
|
|
1148
1193
|
return this.http.post(`/lobbies/${lobbyId}/join-queue`, {});
|
|
1149
1194
|
}
|
|
1150
1195
|
async cancelQueue(lobbyId) {
|
|
1151
|
-
|
|
1196
|
+
const lobby = await this.http.delete(`/lobbies/${lobbyId}/queue`);
|
|
1197
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
1198
|
+
return lobby;
|
|
1152
1199
|
}
|
|
1153
1200
|
async updateBetAmount(lobbyId, betAmount) {
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1201
|
+
const lobby = await this.http.patch(
|
|
1202
|
+
`/lobbies/${lobbyId}/bet-amount`,
|
|
1203
|
+
{ betAmount }
|
|
1204
|
+
);
|
|
1205
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
1206
|
+
return lobby;
|
|
1157
1207
|
}
|
|
1158
1208
|
async playSound(lobbyId, sound) {
|
|
1159
1209
|
return this.http.post(`/lobbies/${lobbyId}/sound`, {
|
|
@@ -1188,13 +1238,18 @@ var Lobbies = class {
|
|
|
1188
1238
|
};
|
|
1189
1239
|
|
|
1190
1240
|
// src/games.ts
|
|
1191
|
-
var import_web32 = require("@solana/web3.js");
|
|
1192
1241
|
var Games = class {
|
|
1193
1242
|
constructor(http, wallet, logger2) {
|
|
1194
1243
|
this.http = http;
|
|
1195
1244
|
this.wallet = wallet;
|
|
1196
1245
|
this.logger = logger2;
|
|
1197
1246
|
}
|
|
1247
|
+
setGameStore(store) {
|
|
1248
|
+
this.gameStore = store;
|
|
1249
|
+
}
|
|
1250
|
+
setGameActionsStore(store) {
|
|
1251
|
+
this.gameActionsStore = store;
|
|
1252
|
+
}
|
|
1198
1253
|
async getAvailableGames() {
|
|
1199
1254
|
return this.http.get("/games/available");
|
|
1200
1255
|
}
|
|
@@ -1207,7 +1262,11 @@ var Games = class {
|
|
|
1207
1262
|
return this.http.get("/games/metrics");
|
|
1208
1263
|
}
|
|
1209
1264
|
async getGame(gameId) {
|
|
1210
|
-
|
|
1265
|
+
const existing = this.gameStore?.store.getState().gamesById[gameId];
|
|
1266
|
+
if (existing?.status === "completed") return existing;
|
|
1267
|
+
const game = await this.http.get(`/games/${gameId}`);
|
|
1268
|
+
this.gameStore?.setBaseState([game]);
|
|
1269
|
+
return game;
|
|
1211
1270
|
}
|
|
1212
1271
|
/**
|
|
1213
1272
|
* Get list of currently active (live) games. Public endpoint for spectating.
|
|
@@ -1234,7 +1293,13 @@ var Games = class {
|
|
|
1234
1293
|
* Get current game state with timer information
|
|
1235
1294
|
*/
|
|
1236
1295
|
async getGameState(gameId) {
|
|
1237
|
-
|
|
1296
|
+
const existing = this.gameActionsStore?.store.getState().statesByGameId[gameId];
|
|
1297
|
+
if (existing?.status === "completed") return existing;
|
|
1298
|
+
const state = await this.http.get(
|
|
1299
|
+
`/games/${gameId}/state`
|
|
1300
|
+
);
|
|
1301
|
+
this.gameActionsStore?.setBaseState(gameId, state);
|
|
1302
|
+
return state;
|
|
1238
1303
|
}
|
|
1239
1304
|
/**
|
|
1240
1305
|
* Request a rematch for a completed game
|
|
@@ -1270,8 +1335,18 @@ var Games = class {
|
|
|
1270
1335
|
{ amountMinor, signedTransaction }
|
|
1271
1336
|
);
|
|
1272
1337
|
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Confirm a donation that was already sent by the client (signAndSendTransaction path).
|
|
1340
|
+
*/
|
|
1341
|
+
async confirmGameDonationSignature(gameId, amountMinor, signature) {
|
|
1342
|
+
return this.http.post(
|
|
1343
|
+
`/games/${gameId}/donate/confirm-signature`,
|
|
1344
|
+
{ amountMinor, signature }
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1273
1347
|
/**
|
|
1274
1348
|
* One-call donation flow: prepare -> sign -> submit.
|
|
1349
|
+
* Automatically uses the right signing path (embedded vs injected wallet).
|
|
1275
1350
|
*/
|
|
1276
1351
|
async sendDonation(gameId, amountMinor) {
|
|
1277
1352
|
if (!this.wallet.hasSigner()) {
|
|
@@ -1280,16 +1355,10 @@ var Games = class {
|
|
|
1280
1355
|
);
|
|
1281
1356
|
}
|
|
1282
1357
|
const prepared = await this.prepareGameDonation(gameId, amountMinor);
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
);
|
|
1288
|
-
const result = await this.donateToGame(
|
|
1289
|
-
gameId,
|
|
1290
|
-
amountMinor,
|
|
1291
|
-
signedTransaction
|
|
1292
|
-
);
|
|
1358
|
+
const result = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
1359
|
+
onSigned: (signedTxBase64) => this.donateToGame(gameId, amountMinor, signedTxBase64),
|
|
1360
|
+
onSignedAndSent: (sig) => this.confirmGameDonationSignature(gameId, amountMinor, sig)
|
|
1361
|
+
});
|
|
1293
1362
|
return {
|
|
1294
1363
|
...result,
|
|
1295
1364
|
escrowAddress: prepared.escrowAddress,
|
|
@@ -1388,6 +1457,9 @@ var Chat = class {
|
|
|
1388
1457
|
this.logger = logger2;
|
|
1389
1458
|
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions };
|
|
1390
1459
|
}
|
|
1460
|
+
setDmThreadsStore(store) {
|
|
1461
|
+
this.dmThreadsStore = store;
|
|
1462
|
+
}
|
|
1391
1463
|
encodeContextId(id) {
|
|
1392
1464
|
return encodeURIComponent(id);
|
|
1393
1465
|
}
|
|
@@ -1469,7 +1541,9 @@ var Chat = class {
|
|
|
1469
1541
|
return response;
|
|
1470
1542
|
}
|
|
1471
1543
|
async listDmThreads() {
|
|
1472
|
-
|
|
1544
|
+
const threads = await this.http.get("/chat/dm/threads");
|
|
1545
|
+
this.dmThreadsStore?.setBaseState(threads);
|
|
1546
|
+
return threads;
|
|
1473
1547
|
}
|
|
1474
1548
|
async getDmThread(dmKey) {
|
|
1475
1549
|
return this.http.get(
|
|
@@ -1535,7 +1609,6 @@ var Challenges = class {
|
|
|
1535
1609
|
};
|
|
1536
1610
|
|
|
1537
1611
|
// src/tips.ts
|
|
1538
|
-
var import_web33 = require("@solana/web3.js");
|
|
1539
1612
|
var Tips = class {
|
|
1540
1613
|
constructor(http, wallet, chat, logger2) {
|
|
1541
1614
|
this.http = http;
|
|
@@ -1562,21 +1635,32 @@ var Tips = class {
|
|
|
1562
1635
|
);
|
|
1563
1636
|
}
|
|
1564
1637
|
const { publicKey: senderAddress } = await this.wallet.loadWallet();
|
|
1565
|
-
const
|
|
1566
|
-
const
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
);
|
|
1571
|
-
const transfer = await this.wallet.
|
|
1572
|
-
signedTxBase64
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1638
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
1639
|
+
const prepared = await this.prepare({
|
|
1640
|
+
recipientUsername,
|
|
1641
|
+
amount,
|
|
1642
|
+
supportsPresign
|
|
1643
|
+
});
|
|
1644
|
+
const transfer = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
1645
|
+
onSigned: (signedTxBase64) => this.wallet.submitTransfer(
|
|
1646
|
+
signedTxBase64,
|
|
1647
|
+
senderAddress,
|
|
1648
|
+
prepared.recipientAddress,
|
|
1649
|
+
prepared.amount,
|
|
1650
|
+
"USDC",
|
|
1651
|
+
false,
|
|
1652
|
+
prepared.recipientUsername
|
|
1653
|
+
),
|
|
1654
|
+
onSignedAndSent: (sig) => this.wallet.confirmTransferSignature(
|
|
1655
|
+
sig,
|
|
1656
|
+
senderAddress,
|
|
1657
|
+
prepared.recipientAddress,
|
|
1658
|
+
prepared.amount,
|
|
1659
|
+
"USDC",
|
|
1660
|
+
false,
|
|
1661
|
+
prepared.recipientUsername
|
|
1662
|
+
)
|
|
1663
|
+
});
|
|
1580
1664
|
const message = await this.chat.broadcastGlobalTip(
|
|
1581
1665
|
prepared.recipientUserId,
|
|
1582
1666
|
prepared.amount
|
|
@@ -1688,7 +1772,7 @@ var Achievements = class {
|
|
|
1688
1772
|
};
|
|
1689
1773
|
|
|
1690
1774
|
// src/wallet.ts
|
|
1691
|
-
var
|
|
1775
|
+
var import_web32 = require("@solana/web3.js");
|
|
1692
1776
|
var TRANSFER_FEE_MINOR = 1e4;
|
|
1693
1777
|
var MIN_TRANSFER_AMOUNT = 5e4;
|
|
1694
1778
|
var MIN_SOL_TRANSFER_AMOUNT = 1e6;
|
|
@@ -1905,13 +1989,15 @@ var Wallet = class {
|
|
|
1905
1989
|
);
|
|
1906
1990
|
}
|
|
1907
1991
|
try {
|
|
1992
|
+
const supportsPresign = !this.isSignAndSendMode();
|
|
1908
1993
|
const response = await this.http.post(
|
|
1909
1994
|
"/wallets/transfer/prepare",
|
|
1910
1995
|
{
|
|
1911
1996
|
senderAddress,
|
|
1912
1997
|
recipient,
|
|
1913
1998
|
amount: amountMinor,
|
|
1914
|
-
token
|
|
1999
|
+
token,
|
|
2000
|
+
supportsPresign
|
|
1915
2001
|
}
|
|
1916
2002
|
);
|
|
1917
2003
|
this.logger.debug("Transfer prepared", {
|
|
@@ -1995,6 +2081,29 @@ var Wallet = class {
|
|
|
1995
2081
|
throw error;
|
|
1996
2082
|
}
|
|
1997
2083
|
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Sign a prepared transaction and invoke the appropriate backend callback
|
|
2086
|
+
* based on the configured signer — without exposing the mode decision to callers.
|
|
2087
|
+
* - Embedded wallet (signAndSend): signs+sends, calls onSignedAndSent(signature)
|
|
2088
|
+
* - Injected wallet (signTransaction): signs locally, calls onSigned(signedTxBase64)
|
|
2089
|
+
*/
|
|
2090
|
+
async signAndDispatch(unsignedTxBase64, handlers) {
|
|
2091
|
+
if (!this.signer) {
|
|
2092
|
+
throw new Error(
|
|
2093
|
+
"No signer configured. Call setSigner() with a WalletSigner implementation first."
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
const unsignedTx = import_web32.Transaction.from(base64ToBytes(unsignedTxBase64));
|
|
2097
|
+
if (this.isSignAndSendMode()) {
|
|
2098
|
+
const sig = await this.signAndSendTransaction(unsignedTx);
|
|
2099
|
+
return handlers.onSignedAndSent(sig);
|
|
2100
|
+
}
|
|
2101
|
+
const signedTx = await this.signTransaction(unsignedTx);
|
|
2102
|
+
const signedBase64 = bytesToBase64(
|
|
2103
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
2104
|
+
);
|
|
2105
|
+
return handlers.onSigned(signedBase64);
|
|
2106
|
+
}
|
|
1998
2107
|
/**
|
|
1999
2108
|
* Full transfer flow in one call: prepare -> sign -> submit
|
|
2000
2109
|
* Recipient can be username, .sol domain, or Solana address (resolved by backend).
|
|
@@ -2011,7 +2120,7 @@ var Wallet = class {
|
|
|
2011
2120
|
amount,
|
|
2012
2121
|
token
|
|
2013
2122
|
);
|
|
2014
|
-
const unsignedTx =
|
|
2123
|
+
const unsignedTx = import_web32.Transaction.from(base64ToBytes(prepared.transaction));
|
|
2015
2124
|
let submitted;
|
|
2016
2125
|
if (this.isSignAndSendMode()) {
|
|
2017
2126
|
const signature = await this.signAndSendTransaction(unsignedTx);
|
|
@@ -2051,7 +2160,7 @@ var Wallet = class {
|
|
|
2051
2160
|
};
|
|
2052
2161
|
|
|
2053
2162
|
// src/escrow.ts
|
|
2054
|
-
var
|
|
2163
|
+
var import_web33 = require("@solana/web3.js");
|
|
2055
2164
|
var Escrow = class {
|
|
2056
2165
|
constructor(http, wallet, logger2) {
|
|
2057
2166
|
this.http = http;
|
|
@@ -2079,12 +2188,32 @@ var Escrow = class {
|
|
|
2079
2188
|
* initializes depositStatus, and prepares the unsigned transaction in one call.
|
|
2080
2189
|
* Eliminates one HTTP round-trip vs startDeposits() + prepareDepositTransaction().
|
|
2081
2190
|
*/
|
|
2082
|
-
async prepareAndStartDeposit(lobbyId) {
|
|
2191
|
+
async prepareAndStartDeposit(lobbyId, supportsPresign) {
|
|
2192
|
+
const presign = supportsPresign ?? !this.wallet.isSignAndSendMode();
|
|
2083
2193
|
return this.http.post(
|
|
2084
2194
|
`/escrow/lobby/${lobbyId}/deposit/prepare-and-start`,
|
|
2085
|
-
{}
|
|
2195
|
+
{ supportsPresign: presign }
|
|
2086
2196
|
);
|
|
2087
2197
|
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Sign and submit a prepared (unsigned) deposit transaction using the
|
|
2200
|
+
* configured signer. Automatically picks the right path:
|
|
2201
|
+
* - Embedded wallet: signAndSendTransaction → confirmDepositSignature
|
|
2202
|
+
* - Injected wallet: signTransaction → submitDeposit
|
|
2203
|
+
* Returns the on-chain signature.
|
|
2204
|
+
*/
|
|
2205
|
+
async signAndSubmitPreparedDeposit(lobbyId, unsignedTxBase64) {
|
|
2206
|
+
return this.wallet.signAndDispatch(unsignedTxBase64, {
|
|
2207
|
+
onSigned: async (signedTxBase64) => {
|
|
2208
|
+
const result = await this.submitDeposit(lobbyId, signedTxBase64);
|
|
2209
|
+
return result.signature;
|
|
2210
|
+
},
|
|
2211
|
+
onSignedAndSent: async (signature) => {
|
|
2212
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
2213
|
+
return signature;
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2088
2217
|
/**
|
|
2089
2218
|
* Submit a signed deposit transaction
|
|
2090
2219
|
* The transaction will be submitted to the Solana network and confirmed
|
|
@@ -2127,8 +2256,12 @@ var Escrow = class {
|
|
|
2127
2256
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
2128
2257
|
);
|
|
2129
2258
|
}
|
|
2130
|
-
const
|
|
2131
|
-
const
|
|
2259
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
2260
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
2261
|
+
lobbyId,
|
|
2262
|
+
supportsPresign
|
|
2263
|
+
);
|
|
2264
|
+
const unsignedTx = import_web33.Transaction.from(base64ToBytes(transaction));
|
|
2132
2265
|
let signature;
|
|
2133
2266
|
if (this.wallet.isSignAndSendMode()) {
|
|
2134
2267
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
@@ -2162,8 +2295,13 @@ var Escrow = class {
|
|
|
2162
2295
|
};
|
|
2163
2296
|
}
|
|
2164
2297
|
/**
|
|
2165
|
-
*
|
|
2166
|
-
*
|
|
2298
|
+
* Deposit for a lobby and wait until the calling user's own deposit is confirmed.
|
|
2299
|
+
* Ideal for agents: returns as soon as your deposit is on-chain without waiting
|
|
2300
|
+
* for the other player. The server auto-joins the queue when all players deposit.
|
|
2301
|
+
*
|
|
2302
|
+
* Confirmation is handled asynchronously by the transaction queue processor.
|
|
2303
|
+
* This method polls the deposit status endpoint until the signature appears as
|
|
2304
|
+
* confirmed (up to 60 seconds).
|
|
2167
2305
|
*
|
|
2168
2306
|
* Automatically uses signAndSendTransaction or signTransaction based on signer capability.
|
|
2169
2307
|
*/
|
|
@@ -2173,26 +2311,45 @@ var Escrow = class {
|
|
|
2173
2311
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
2174
2312
|
);
|
|
2175
2313
|
}
|
|
2176
|
-
const
|
|
2177
|
-
const
|
|
2314
|
+
const supportsPresign2 = !this.wallet.isSignAndSendMode();
|
|
2315
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
2316
|
+
lobbyId,
|
|
2317
|
+
supportsPresign2
|
|
2318
|
+
);
|
|
2319
|
+
const unsignedTx = import_web33.Transaction.from(base64ToBytes(transaction));
|
|
2178
2320
|
let signature;
|
|
2179
2321
|
if (this.wallet.isSignAndSendMode()) {
|
|
2180
2322
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
2181
|
-
await this.
|
|
2182
|
-
`/escrow/lobby/${lobbyId}/deposit/confirm-signature?awaitConfirmation=true`,
|
|
2183
|
-
{ signature }
|
|
2184
|
-
);
|
|
2323
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
2185
2324
|
} else {
|
|
2186
2325
|
const signedTx = await this.wallet.signTransaction(unsignedTx);
|
|
2187
2326
|
const signedBase64 = bytesToBase64(
|
|
2188
2327
|
signedTx.serialize({ requireAllSignatures: false })
|
|
2189
2328
|
);
|
|
2190
|
-
const
|
|
2191
|
-
|
|
2192
|
-
});
|
|
2193
|
-
signature = result.signature;
|
|
2329
|
+
const submitResult = await this.submitDeposit(lobbyId, signedBase64);
|
|
2330
|
+
signature = submitResult.signature;
|
|
2194
2331
|
}
|
|
2195
|
-
|
|
2332
|
+
const MAX_WAIT_MS = 6e4;
|
|
2333
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
2334
|
+
const startTime = Date.now();
|
|
2335
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
2336
|
+
const status = await this.getDepositStatus(lobbyId);
|
|
2337
|
+
const myDeposit = status.deposits.find(
|
|
2338
|
+
(d) => d.transactionHash === signature
|
|
2339
|
+
);
|
|
2340
|
+
if (myDeposit?.status === "confirmed") {
|
|
2341
|
+
return {
|
|
2342
|
+
signature,
|
|
2343
|
+
status: "confirmed",
|
|
2344
|
+
canProceedToQueue: status.canProceedToQueue
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
if (status.allConfirmed && status.canProceedToQueue) {
|
|
2348
|
+
return { signature, status: "confirmed", canProceedToQueue: true };
|
|
2349
|
+
}
|
|
2350
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
2351
|
+
}
|
|
2352
|
+
return { signature, status: "pending", canProceedToQueue: false };
|
|
2196
2353
|
}
|
|
2197
2354
|
async claimLobbyDepositRefund(lobbyId, depositSignature) {
|
|
2198
2355
|
return this.http.post(
|
|
@@ -2548,9 +2705,10 @@ var Support = class {
|
|
|
2548
2705
|
|
|
2549
2706
|
// src/markets.ts
|
|
2550
2707
|
var Markets = class {
|
|
2551
|
-
constructor(http, logger2) {
|
|
2708
|
+
constructor(http, logger2, wallet) {
|
|
2552
2709
|
this.http = http;
|
|
2553
2710
|
this.logger = logger2;
|
|
2711
|
+
this.wallet = wallet;
|
|
2554
2712
|
}
|
|
2555
2713
|
/**
|
|
2556
2714
|
* Get the prediction market state for a game.
|
|
@@ -2592,6 +2750,35 @@ var Markets = class {
|
|
|
2592
2750
|
{ signedTransaction, outcomeId, amountMinor }
|
|
2593
2751
|
);
|
|
2594
2752
|
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Confirm a buy order that was already broadcast by the client (signAndSendTransaction path).
|
|
2755
|
+
*/
|
|
2756
|
+
async confirmBuyOrderSignature(gameId, depositSignature, outcomeId, amountMinor) {
|
|
2757
|
+
return this.http.post(
|
|
2758
|
+
`/games/${gameId}/market/orders/buy/confirm-signature`,
|
|
2759
|
+
{ depositSignature, outcomeId, amountMinor }
|
|
2760
|
+
);
|
|
2761
|
+
}
|
|
2762
|
+
/**
|
|
2763
|
+
* One-call buy order: prepare → sign → submit, automatically choosing
|
|
2764
|
+
* the right signing path (embedded vs injected wallet).
|
|
2765
|
+
*/
|
|
2766
|
+
async buy(gameId, outcomeId, amountMinor) {
|
|
2767
|
+
if (!this.wallet?.hasSigner()) {
|
|
2768
|
+
throw new Error(
|
|
2769
|
+
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
2770
|
+
);
|
|
2771
|
+
}
|
|
2772
|
+
const { transaction } = await this.prepareBuyOrder(
|
|
2773
|
+
gameId,
|
|
2774
|
+
outcomeId,
|
|
2775
|
+
amountMinor
|
|
2776
|
+
);
|
|
2777
|
+
return this.wallet.signAndDispatch(transaction, {
|
|
2778
|
+
onSigned: (signedTxBase64) => this.submitBuyOrder(gameId, signedTxBase64, outcomeId, amountMinor),
|
|
2779
|
+
onSignedAndSent: (sig) => this.confirmBuyOrderSignature(gameId, sig, outcomeId, amountMinor)
|
|
2780
|
+
});
|
|
2781
|
+
}
|
|
2595
2782
|
/**
|
|
2596
2783
|
* Sell shares back to the AMM pool.
|
|
2597
2784
|
* @param gameId - The game ID
|
|
@@ -2641,6 +2828,22 @@ var Markets = class {
|
|
|
2641
2828
|
}
|
|
2642
2829
|
};
|
|
2643
2830
|
|
|
2831
|
+
// src/analytics.ts
|
|
2832
|
+
var NoopAnalyticsClient = class {
|
|
2833
|
+
userLoggedIn(_user, _meta) {
|
|
2834
|
+
}
|
|
2835
|
+
userLoggedOut() {
|
|
2836
|
+
}
|
|
2837
|
+
sessionRestored(_user) {
|
|
2838
|
+
}
|
|
2839
|
+
track(_event, _properties) {
|
|
2840
|
+
}
|
|
2841
|
+
setUserProperties(_properties) {
|
|
2842
|
+
}
|
|
2843
|
+
group(_groupType, _groupKey, _properties) {
|
|
2844
|
+
}
|
|
2845
|
+
};
|
|
2846
|
+
|
|
2644
2847
|
// src/ws/standalone-transport.ts
|
|
2645
2848
|
var import_socket = require("socket.io-client");
|
|
2646
2849
|
|
|
@@ -2734,22 +2937,35 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2734
2937
|
this.accessToken = null;
|
|
2735
2938
|
this.reconnectAttempts = 0;
|
|
2736
2939
|
this.reconnectTimer = null;
|
|
2737
|
-
this.
|
|
2738
|
-
this.
|
|
2739
|
-
this.reconnectDelay = 1e3;
|
|
2740
|
-
this.reconnectDelayMax = 3e4;
|
|
2741
|
-
// 5 minutes
|
|
2940
|
+
this.needsReconnect = false;
|
|
2941
|
+
this.visibilityHandler = null;
|
|
2742
2942
|
this.wildcardHandlers = /* @__PURE__ */ new Set();
|
|
2743
2943
|
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
2744
2944
|
this.registeredEvents = /* @__PURE__ */ new Set();
|
|
2745
2945
|
this.wildcardRegistered = false;
|
|
2746
2946
|
this.url = url;
|
|
2947
|
+
if (typeof document !== "undefined") {
|
|
2948
|
+
this.visibilityHandler = () => {
|
|
2949
|
+
if (document.visibilityState === "visible" && !this.connectionState.connected) {
|
|
2950
|
+
this.reconnectImmediately();
|
|
2951
|
+
}
|
|
2952
|
+
};
|
|
2953
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
2954
|
+
}
|
|
2747
2955
|
}
|
|
2748
2956
|
connect() {
|
|
2957
|
+
if (this.reconnectTimer !== null) {
|
|
2958
|
+
clearTimeout(this.reconnectTimer);
|
|
2959
|
+
this.reconnectTimer = null;
|
|
2960
|
+
}
|
|
2961
|
+
this.reconnectAttempts = 0;
|
|
2749
2962
|
this.ensureSocket();
|
|
2750
2963
|
}
|
|
2751
2964
|
disconnect() {
|
|
2752
|
-
this.
|
|
2965
|
+
if (this.visibilityHandler && typeof document !== "undefined") {
|
|
2966
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
2967
|
+
this.visibilityHandler = null;
|
|
2968
|
+
}
|
|
2753
2969
|
if (!this.socket) return;
|
|
2754
2970
|
this.socket.disconnect();
|
|
2755
2971
|
this.socket = null;
|
|
@@ -2848,13 +3064,13 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2848
3064
|
reconnection: false
|
|
2849
3065
|
});
|
|
2850
3066
|
socket.on("connect", () => {
|
|
2851
|
-
this.clearPeriodicReconnect();
|
|
2852
3067
|
this.updateConnectionState({
|
|
2853
3068
|
connected: true,
|
|
2854
3069
|
connecting: false,
|
|
2855
3070
|
error: null
|
|
2856
3071
|
});
|
|
2857
|
-
const wasReconnect = this.
|
|
3072
|
+
const wasReconnect = this.needsReconnect;
|
|
3073
|
+
this.needsReconnect = false;
|
|
2858
3074
|
this.reconnectAttempts = 0;
|
|
2859
3075
|
this.onReconnect();
|
|
2860
3076
|
if (wasReconnect) {
|
|
@@ -2865,6 +3081,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2865
3081
|
});
|
|
2866
3082
|
socket.on("disconnect", (reason) => {
|
|
2867
3083
|
this.updateConnectionState({ connected: false, connecting: false });
|
|
3084
|
+
this.needsReconnect = true;
|
|
2868
3085
|
this.clearSocketForReconnect();
|
|
2869
3086
|
if (reason === "io server disconnect") {
|
|
2870
3087
|
this.handleAuthFailure("io server disconnect");
|
|
@@ -2874,6 +3091,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2874
3091
|
});
|
|
2875
3092
|
socket.on("connect_error", (error) => {
|
|
2876
3093
|
const message = error?.message || "connect_error";
|
|
3094
|
+
this.needsReconnect = true;
|
|
2877
3095
|
this.clearSocketForReconnect();
|
|
2878
3096
|
if (message.includes("unauthorized") || message.includes("401")) {
|
|
2879
3097
|
this.handleAuthFailure(message);
|
|
@@ -2901,31 +3119,31 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2901
3119
|
this.socket.removeAllListeners();
|
|
2902
3120
|
this.socket = null;
|
|
2903
3121
|
}
|
|
2904
|
-
|
|
2905
|
-
if (this.
|
|
2906
|
-
|
|
2907
|
-
this.
|
|
3122
|
+
reconnectImmediately() {
|
|
3123
|
+
if (this.reconnectTimer !== null) {
|
|
3124
|
+
clearTimeout(this.reconnectTimer);
|
|
3125
|
+
this.reconnectTimer = null;
|
|
2908
3126
|
}
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
if (this.periodicReconnectInterval !== null) return;
|
|
2912
|
-
this.periodicReconnectInterval = setInterval(() => {
|
|
2913
|
-
if (this.connectionState.connected) return;
|
|
2914
|
-
this.reconnectAttempts = 0;
|
|
2915
|
-
this.ensureSocket();
|
|
2916
|
-
}, _StandaloneWsTransport.PERIODIC_RECONNECT_MS);
|
|
3127
|
+
this.reconnectAttempts = 0;
|
|
3128
|
+
this.ensureSocket();
|
|
2917
3129
|
}
|
|
2918
3130
|
scheduleReconnect() {
|
|
2919
3131
|
if (this.reconnectTimer !== null) return;
|
|
2920
|
-
|
|
2921
|
-
this.startPeriodicReconnect();
|
|
2922
|
-
return;
|
|
2923
|
-
}
|
|
2924
|
-
const delay = Math.min(
|
|
2925
|
-
this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
|
|
2926
|
-
this.reconnectDelayMax
|
|
2927
|
-
);
|
|
3132
|
+
const attempt = this.reconnectAttempts;
|
|
2928
3133
|
this.reconnectAttempts += 1;
|
|
3134
|
+
let delay;
|
|
3135
|
+
if (attempt < _StandaloneWsTransport.FAST_RETRY_LIMIT) {
|
|
3136
|
+
delay = _StandaloneWsTransport.FAST_RETRY_DELAY_MS;
|
|
3137
|
+
} else {
|
|
3138
|
+
const backoffAttempt = attempt - _StandaloneWsTransport.FAST_RETRY_LIMIT;
|
|
3139
|
+
delay = Math.min(
|
|
3140
|
+
_StandaloneWsTransport.FAST_RETRY_DELAY_MS * Math.pow(2, backoffAttempt),
|
|
3141
|
+
_StandaloneWsTransport.MAX_BACKOFF_MS
|
|
3142
|
+
);
|
|
3143
|
+
if (attempt === _StandaloneWsTransport.FAST_RETRY_LIMIT) {
|
|
3144
|
+
this.dispatchEvent("connection:slow-retry", { timestamp: Date.now() });
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
2929
3147
|
this.reconnectTimer = setTimeout(() => {
|
|
2930
3148
|
this.reconnectTimer = null;
|
|
2931
3149
|
this.ensureSocket();
|
|
@@ -2986,7 +3204,9 @@ var _StandaloneWsTransport = class _StandaloneWsTransport extends BaseWsTranspor
|
|
|
2986
3204
|
this.wildcardRegistered = true;
|
|
2987
3205
|
}
|
|
2988
3206
|
};
|
|
2989
|
-
_StandaloneWsTransport.
|
|
3207
|
+
_StandaloneWsTransport.FAST_RETRY_LIMIT = 60;
|
|
3208
|
+
_StandaloneWsTransport.FAST_RETRY_DELAY_MS = 1e3;
|
|
3209
|
+
_StandaloneWsTransport.MAX_BACKOFF_MS = 3e4;
|
|
2990
3210
|
var StandaloneWsTransport = _StandaloneWsTransport;
|
|
2991
3211
|
|
|
2992
3212
|
// src/stores/store-utils.ts
|
|
@@ -3013,14 +3233,16 @@ function createLobbyStore(transport) {
|
|
|
3013
3233
|
depositStatusByLobbyId: {}
|
|
3014
3234
|
});
|
|
3015
3235
|
const setBaseState = (lobbies) => {
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3236
|
+
store.updateState((state) => {
|
|
3237
|
+
const lobbiesById = { ...state.lobbiesById };
|
|
3238
|
+
for (const lobby of lobbies) {
|
|
3239
|
+
const existing = lobbiesById[lobby.id];
|
|
3240
|
+
if (!existing || lobby.updatedAt >= existing.updatedAt) {
|
|
3241
|
+
lobbiesById[lobby.id] = lobby;
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
return { ...state, lobbiesById };
|
|
3245
|
+
});
|
|
3024
3246
|
};
|
|
3025
3247
|
const applyWsEvent = (event) => {
|
|
3026
3248
|
switch (event.event) {
|
|
@@ -3152,12 +3374,23 @@ function createGameStore(transport) {
|
|
|
3152
3374
|
gamesById: {},
|
|
3153
3375
|
spectatorCounts: {}
|
|
3154
3376
|
});
|
|
3377
|
+
const STATUS_RANK = {
|
|
3378
|
+
active: 0,
|
|
3379
|
+
completed: 1,
|
|
3380
|
+
abandoned: 1
|
|
3381
|
+
};
|
|
3155
3382
|
const setBaseState = (games) => {
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3383
|
+
store.updateState((state) => {
|
|
3384
|
+
const gamesById = { ...state.gamesById };
|
|
3385
|
+
for (const game of games) {
|
|
3386
|
+
const existing = gamesById[game.gameId];
|
|
3387
|
+
if (existing && STATUS_RANK[existing.status] > STATUS_RANK[game.status]) {
|
|
3388
|
+
continue;
|
|
3389
|
+
}
|
|
3390
|
+
gamesById[game.gameId] = game;
|
|
3391
|
+
}
|
|
3392
|
+
return { ...state, gamesById };
|
|
3393
|
+
});
|
|
3161
3394
|
};
|
|
3162
3395
|
const applyWsEvent = (event) => {
|
|
3163
3396
|
switch (event.event) {
|
|
@@ -3234,6 +3467,19 @@ function createGameStore(transport) {
|
|
|
3234
3467
|
}
|
|
3235
3468
|
|
|
3236
3469
|
// src/stores/game-actions-store.ts
|
|
3470
|
+
function selectGameLifecycleState(gameState) {
|
|
3471
|
+
const s = gameState;
|
|
3472
|
+
if (!s) return null;
|
|
3473
|
+
const raw = s;
|
|
3474
|
+
return {
|
|
3475
|
+
status: raw.status,
|
|
3476
|
+
winnerId: raw.winnerId ?? null,
|
|
3477
|
+
betAmount: raw.betAmount ?? 0,
|
|
3478
|
+
wonAmount: raw.wonAmount ?? null,
|
|
3479
|
+
totalPotMinor: raw.totalPotMinor,
|
|
3480
|
+
currentPlayerId: raw.currentPlayerId ?? null
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3237
3483
|
var isRpsCompletionPayload = (payload) => payload.gameType === "rock-paper-scissors";
|
|
3238
3484
|
function createGameActionsStore(transport) {
|
|
3239
3485
|
const store = createSdkStore({
|
|
@@ -3255,8 +3501,21 @@ function createGameActionsStore(transport) {
|
|
|
3255
3501
|
});
|
|
3256
3502
|
};
|
|
3257
3503
|
const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
|
|
3504
|
+
const pendingEvents = /* @__PURE__ */ new Map();
|
|
3505
|
+
function enqueue(gameId, event) {
|
|
3506
|
+
const q = pendingEvents.get(gameId) ?? [];
|
|
3507
|
+
q.push(event);
|
|
3508
|
+
pendingEvents.set(gameId, q);
|
|
3509
|
+
}
|
|
3510
|
+
function drainQueue(gameId) {
|
|
3511
|
+
const q = pendingEvents.get(gameId);
|
|
3512
|
+
if (!q?.length) return;
|
|
3513
|
+
pendingEvents.delete(gameId);
|
|
3514
|
+
for (const ev of q) applyWsEvent(ev);
|
|
3515
|
+
}
|
|
3258
3516
|
const setBaseState = (gameId, state) => {
|
|
3259
3517
|
updateState(gameId, state);
|
|
3518
|
+
drainQueue(gameId);
|
|
3260
3519
|
};
|
|
3261
3520
|
const clearState = (gameId) => {
|
|
3262
3521
|
store.updateState((state) => {
|
|
@@ -3266,6 +3525,7 @@ function createGameActionsStore(transport) {
|
|
|
3266
3525
|
const { [gameId]: _, ...rest } = state.statesByGameId;
|
|
3267
3526
|
return { ...state, statesByGameId: rest };
|
|
3268
3527
|
});
|
|
3528
|
+
pendingEvents.delete(gameId);
|
|
3269
3529
|
};
|
|
3270
3530
|
const applyWsEvent = (event) => {
|
|
3271
3531
|
switch (event.event) {
|
|
@@ -3284,7 +3544,10 @@ function createGameActionsStore(transport) {
|
|
|
3284
3544
|
}
|
|
3285
3545
|
case "game:rps:starting": {
|
|
3286
3546
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3287
|
-
if (!current || !isRpsState(current))
|
|
3547
|
+
if (!current || !isRpsState(current)) {
|
|
3548
|
+
enqueue(event.payload.gameId, event);
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3288
3551
|
const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
|
|
3289
3552
|
const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
|
|
3290
3553
|
const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
|
|
@@ -3305,7 +3568,10 @@ function createGameActionsStore(transport) {
|
|
|
3305
3568
|
}
|
|
3306
3569
|
case "game:rps:round:started": {
|
|
3307
3570
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3308
|
-
if (!current || !isRpsState(current))
|
|
3571
|
+
if (!current || !isRpsState(current)) {
|
|
3572
|
+
enqueue(event.payload.gameId, event);
|
|
3573
|
+
return;
|
|
3574
|
+
}
|
|
3309
3575
|
const actions = {};
|
|
3310
3576
|
const baseUsers = /* @__PURE__ */ new Set();
|
|
3311
3577
|
Object.keys(current.roundState.actions).forEach(
|
|
@@ -3339,7 +3605,10 @@ function createGameActionsStore(transport) {
|
|
|
3339
3605
|
}
|
|
3340
3606
|
case "game:rps:action:received": {
|
|
3341
3607
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3342
|
-
if (!current || !isRpsState(current))
|
|
3608
|
+
if (!current || !isRpsState(current)) {
|
|
3609
|
+
enqueue(event.payload.gameId, event);
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
3343
3612
|
const updated = {
|
|
3344
3613
|
...current,
|
|
3345
3614
|
roundState: {
|
|
@@ -3358,7 +3627,10 @@ function createGameActionsStore(transport) {
|
|
|
3358
3627
|
}
|
|
3359
3628
|
case "game:rps:timer:cutoff": {
|
|
3360
3629
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3361
|
-
if (!current || !isRpsState(current))
|
|
3630
|
+
if (!current || !isRpsState(current)) {
|
|
3631
|
+
enqueue(event.payload.gameId, event);
|
|
3632
|
+
return;
|
|
3633
|
+
}
|
|
3362
3634
|
const updated = {
|
|
3363
3635
|
...current,
|
|
3364
3636
|
roundState: {
|
|
@@ -3372,7 +3644,10 @@ function createGameActionsStore(transport) {
|
|
|
3372
3644
|
}
|
|
3373
3645
|
case "game:rps:round:reveal": {
|
|
3374
3646
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3375
|
-
if (!current || !isRpsState(current))
|
|
3647
|
+
if (!current || !isRpsState(current)) {
|
|
3648
|
+
enqueue(event.payload.gameId, event);
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3376
3651
|
const actions = {};
|
|
3377
3652
|
const payloadActions = event.payload.actions;
|
|
3378
3653
|
Object.keys(payloadActions || {}).forEach((userId) => {
|
|
@@ -3396,7 +3671,10 @@ function createGameActionsStore(transport) {
|
|
|
3396
3671
|
}
|
|
3397
3672
|
case "game:rps:round:completed": {
|
|
3398
3673
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3399
|
-
if (!current || !isRpsState(current))
|
|
3674
|
+
if (!current || !isRpsState(current)) {
|
|
3675
|
+
enqueue(event.payload.gameId, event);
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3400
3678
|
const roundHistory = [
|
|
3401
3679
|
...current.roundHistory || [],
|
|
3402
3680
|
{
|
|
@@ -3421,7 +3699,10 @@ function createGameActionsStore(transport) {
|
|
|
3421
3699
|
}
|
|
3422
3700
|
case "game:rps:timeout": {
|
|
3423
3701
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3424
|
-
if (!current || !isRpsState(current))
|
|
3702
|
+
if (!current || !isRpsState(current)) {
|
|
3703
|
+
enqueue(event.payload.gameId, event);
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3425
3706
|
const timedOutUser = event.payload.playerId;
|
|
3426
3707
|
const action = event.payload.action;
|
|
3427
3708
|
const updated = {
|
|
@@ -3446,7 +3727,10 @@ function createGameActionsStore(transport) {
|
|
|
3446
3727
|
const payload = event.payload;
|
|
3447
3728
|
const { gameId } = payload;
|
|
3448
3729
|
const current = store.getState().statesByGameId[gameId];
|
|
3449
|
-
if (!current)
|
|
3730
|
+
if (!current) {
|
|
3731
|
+
enqueue(gameId, event);
|
|
3732
|
+
return;
|
|
3733
|
+
}
|
|
3450
3734
|
const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
|
|
3451
3735
|
...current,
|
|
3452
3736
|
status: "completed",
|
|
@@ -3495,11 +3779,15 @@ function createGameActionsStore(transport) {
|
|
|
3495
3779
|
const current = store.getState().statesByGameId[gameId];
|
|
3496
3780
|
const updated = current ? { ...current, ...incoming } : incoming;
|
|
3497
3781
|
updateState(gameId, updated);
|
|
3782
|
+
drainQueue(gameId);
|
|
3498
3783
|
break;
|
|
3499
3784
|
}
|
|
3500
3785
|
case "game:rematch:requested": {
|
|
3501
3786
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3502
|
-
if (!current)
|
|
3787
|
+
if (!current) {
|
|
3788
|
+
enqueue(event.payload.gameId, event);
|
|
3789
|
+
return;
|
|
3790
|
+
}
|
|
3503
3791
|
const requestedBy = event.payload.requestedBy;
|
|
3504
3792
|
const userId = event.payload.userId;
|
|
3505
3793
|
const requested = new Set(
|
|
@@ -3515,7 +3803,10 @@ function createGameActionsStore(transport) {
|
|
|
3515
3803
|
}
|
|
3516
3804
|
case "game:rematch:cancelled": {
|
|
3517
3805
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3518
|
-
if (!current)
|
|
3806
|
+
if (!current) {
|
|
3807
|
+
enqueue(event.payload.gameId, event);
|
|
3808
|
+
return;
|
|
3809
|
+
}
|
|
3519
3810
|
const requestedBy = event.payload.requestedBy ?? [];
|
|
3520
3811
|
const updated = {
|
|
3521
3812
|
...current,
|
|
@@ -3526,7 +3817,10 @@ function createGameActionsStore(transport) {
|
|
|
3526
3817
|
}
|
|
3527
3818
|
case "game:rematch:started": {
|
|
3528
3819
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3529
|
-
if (!current)
|
|
3820
|
+
if (!current) {
|
|
3821
|
+
enqueue(event.payload.gameId, event);
|
|
3822
|
+
return;
|
|
3823
|
+
}
|
|
3530
3824
|
const updated = {
|
|
3531
3825
|
...current,
|
|
3532
3826
|
rematchRequestedBy: event.payload.playerIds ?? []
|
|
@@ -3537,7 +3831,10 @@ function createGameActionsStore(transport) {
|
|
|
3537
3831
|
case "game:pot:updated": {
|
|
3538
3832
|
const { gameId, totalPotMinor } = event.payload;
|
|
3539
3833
|
const current = store.getState().statesByGameId[gameId];
|
|
3540
|
-
if (!current)
|
|
3834
|
+
if (!current) {
|
|
3835
|
+
enqueue(gameId, event);
|
|
3836
|
+
return;
|
|
3837
|
+
}
|
|
3541
3838
|
const updated = {
|
|
3542
3839
|
...current,
|
|
3543
3840
|
totalPotMinor
|
|
@@ -3550,12 +3847,123 @@ function createGameActionsStore(transport) {
|
|
|
3550
3847
|
}
|
|
3551
3848
|
};
|
|
3552
3849
|
const joinGame = (gameId) => transport.joinRoom(`game:${gameId}`);
|
|
3850
|
+
const getCountdownDigit = (gameId, nowMs) => {
|
|
3851
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3852
|
+
if (!state) return null;
|
|
3853
|
+
if (isRpsState(state)) {
|
|
3854
|
+
if (state.roundState.phase !== "starting") return null;
|
|
3855
|
+
const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
|
|
3856
|
+
if (remaining <= 0) return null;
|
|
3857
|
+
return Math.ceil(remaining / 1e3);
|
|
3858
|
+
}
|
|
3859
|
+
const bufferEndsAt = state.bufferEndsAt;
|
|
3860
|
+
if (bufferEndsAt) {
|
|
3861
|
+
const remaining = new Date(bufferEndsAt).getTime() - nowMs;
|
|
3862
|
+
if (remaining <= 0) return null;
|
|
3863
|
+
return Math.ceil(remaining / 1e3);
|
|
3864
|
+
}
|
|
3865
|
+
return null;
|
|
3866
|
+
};
|
|
3867
|
+
const getChessClockTimes = (gameId, nowMs) => {
|
|
3868
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3869
|
+
if (!state || state.gameType !== "chess") return null;
|
|
3870
|
+
const s = state;
|
|
3871
|
+
let whiteMs = s.whiteTimeMs ?? 0;
|
|
3872
|
+
let blackMs = s.blackTimeMs ?? 0;
|
|
3873
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3874
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3875
|
+
if (!Number.isNaN(startedAt)) {
|
|
3876
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3877
|
+
if (s.currentPlayerId === s.whitePlayerId) {
|
|
3878
|
+
whiteMs = Math.max(0, whiteMs - elapsed);
|
|
3879
|
+
} else if (s.currentPlayerId === s.blackPlayerId) {
|
|
3880
|
+
blackMs = Math.max(0, blackMs - elapsed);
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
|
|
3885
|
+
};
|
|
3886
|
+
const getChessCapturedPieces = (gameId) => {
|
|
3887
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3888
|
+
if (!state || state.gameType !== "chess") return null;
|
|
3889
|
+
const fen = state.fen;
|
|
3890
|
+
if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
|
|
3891
|
+
const placement = fen.split(" ")[0];
|
|
3892
|
+
const white = {};
|
|
3893
|
+
const black = {};
|
|
3894
|
+
for (const char of placement) {
|
|
3895
|
+
if (char === "/" || char >= "1" && char <= "8") continue;
|
|
3896
|
+
const lower = char.toLowerCase();
|
|
3897
|
+
if (char === lower) {
|
|
3898
|
+
black[lower] = (black[lower] ?? 0) + 1;
|
|
3899
|
+
} else {
|
|
3900
|
+
white[lower] = (white[lower] ?? 0) + 1;
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
const INITIAL = {
|
|
3904
|
+
p: 8,
|
|
3905
|
+
r: 2,
|
|
3906
|
+
n: 2,
|
|
3907
|
+
b: 2,
|
|
3908
|
+
q: 1,
|
|
3909
|
+
k: 1
|
|
3910
|
+
};
|
|
3911
|
+
const capturedByWhite = [];
|
|
3912
|
+
const capturedByBlack = [];
|
|
3913
|
+
for (const [type, initial] of Object.entries(INITIAL)) {
|
|
3914
|
+
const missingBlack = initial - (black[type] ?? 0);
|
|
3915
|
+
for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type}`);
|
|
3916
|
+
const missingWhite = initial - (white[type] ?? 0);
|
|
3917
|
+
for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type}`);
|
|
3918
|
+
}
|
|
3919
|
+
return { capturedByWhite, capturedByBlack };
|
|
3920
|
+
};
|
|
3921
|
+
const getTicTacToeClockTimes = (gameId, nowMs) => {
|
|
3922
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3923
|
+
if (!state || state.gameType !== "tic-tac-toe") return null;
|
|
3924
|
+
const s = state;
|
|
3925
|
+
let xMs = s.xTimeMs ?? 0;
|
|
3926
|
+
let oMs = s.oTimeMs ?? 0;
|
|
3927
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3928
|
+
const currentMark = s.playerMarks[s.currentPlayerId];
|
|
3929
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3930
|
+
if (!Number.isNaN(startedAt)) {
|
|
3931
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3932
|
+
if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
|
|
3933
|
+
else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
return { xTimeMs: xMs, oTimeMs: oMs };
|
|
3937
|
+
};
|
|
3938
|
+
const getConnect4ClockTimes = (gameId, nowMs) => {
|
|
3939
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3940
|
+
if (!state || state.gameType !== "connect-four") return null;
|
|
3941
|
+
const s = state;
|
|
3942
|
+
let redMs = s.redTimeMs ?? 0;
|
|
3943
|
+
let yellowMs = s.yellowTimeMs ?? 0;
|
|
3944
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3945
|
+
const currentColor = s.playerColors[s.currentPlayerId];
|
|
3946
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3947
|
+
if (!Number.isNaN(startedAt)) {
|
|
3948
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3949
|
+
if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
|
|
3950
|
+
else if (currentColor === "YELLOW")
|
|
3951
|
+
yellowMs = Math.max(0, yellowMs - elapsed);
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
return { redTimeMs: redMs, yellowTimeMs: yellowMs };
|
|
3955
|
+
};
|
|
3553
3956
|
return {
|
|
3554
3957
|
store,
|
|
3555
3958
|
setBaseState,
|
|
3556
3959
|
clearState,
|
|
3557
3960
|
applyWsEvent,
|
|
3558
|
-
joinGame
|
|
3961
|
+
joinGame,
|
|
3962
|
+
getCountdownDigit,
|
|
3963
|
+
getChessClockTimes,
|
|
3964
|
+
getChessCapturedPieces,
|
|
3965
|
+
getTicTacToeClockTimes,
|
|
3966
|
+
getConnect4ClockTimes
|
|
3559
3967
|
};
|
|
3560
3968
|
}
|
|
3561
3969
|
function isRpsState(state) {
|
|
@@ -3903,7 +4311,38 @@ function createNotificationsStore() {
|
|
|
3903
4311
|
appNotifications: []
|
|
3904
4312
|
}));
|
|
3905
4313
|
};
|
|
3906
|
-
|
|
4314
|
+
const markAsRead = (id) => {
|
|
4315
|
+
store.updateState((state) => ({
|
|
4316
|
+
...state,
|
|
4317
|
+
appNotifications: state.appNotifications.map(
|
|
4318
|
+
(n) => n.id === id ? { ...n, read: true } : n
|
|
4319
|
+
)
|
|
4320
|
+
}));
|
|
4321
|
+
};
|
|
4322
|
+
const markAllAsRead = () => {
|
|
4323
|
+
store.updateState((state) => ({
|
|
4324
|
+
...state,
|
|
4325
|
+
appNotifications: state.appNotifications.map((n) => ({
|
|
4326
|
+
...n,
|
|
4327
|
+
read: true
|
|
4328
|
+
}))
|
|
4329
|
+
}));
|
|
4330
|
+
};
|
|
4331
|
+
const dismiss = (id) => {
|
|
4332
|
+
store.updateState((state) => ({
|
|
4333
|
+
...state,
|
|
4334
|
+
appNotifications: state.appNotifications.filter((n) => n.id !== id)
|
|
4335
|
+
}));
|
|
4336
|
+
};
|
|
4337
|
+
return {
|
|
4338
|
+
store,
|
|
4339
|
+
applyWsEvent,
|
|
4340
|
+
setListFromApi,
|
|
4341
|
+
clear,
|
|
4342
|
+
markAsRead,
|
|
4343
|
+
markAllAsRead,
|
|
4344
|
+
dismiss
|
|
4345
|
+
};
|
|
3907
4346
|
}
|
|
3908
4347
|
|
|
3909
4348
|
// src/stores/friends-store.ts
|
|
@@ -4194,12 +4633,14 @@ var WsRouter = class {
|
|
|
4194
4633
|
}
|
|
4195
4634
|
const decoded = decodeWsEvent(eventName, payload);
|
|
4196
4635
|
if (!decoded) return;
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
this.deps.
|
|
4200
|
-
this.deps.
|
|
4201
|
-
this.deps.
|
|
4202
|
-
this.deps.
|
|
4636
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
4637
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
4638
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
4639
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
4640
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
4641
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
4642
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
4643
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
4203
4644
|
});
|
|
4204
4645
|
}
|
|
4205
4646
|
stop() {
|
|
@@ -4211,12 +4652,14 @@ var WsRouter = class {
|
|
|
4211
4652
|
this.transport.subscribeEvent(eventName, (payload) => {
|
|
4212
4653
|
const decoded = decodeWsEvent(eventName, payload);
|
|
4213
4654
|
if (!decoded) return;
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
this.deps.
|
|
4217
|
-
this.deps.
|
|
4218
|
-
this.deps.
|
|
4219
|
-
this.deps.
|
|
4655
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
4656
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
4657
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
4658
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
4659
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
4660
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
4661
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
4662
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
4220
4663
|
});
|
|
4221
4664
|
}
|
|
4222
4665
|
}
|
|
@@ -4260,6 +4703,7 @@ var SDK = class {
|
|
|
4260
4703
|
constructor(config) {
|
|
4261
4704
|
const baseUrl = config.baseUrl || "http://localhost:3000";
|
|
4262
4705
|
const logger2 = config.logger || logger;
|
|
4706
|
+
this.analytics = config.analytics ?? new NoopAnalyticsClient();
|
|
4263
4707
|
this.http = config.httpClient || new HttpClient(
|
|
4264
4708
|
baseUrl,
|
|
4265
4709
|
config.storage,
|
|
@@ -4292,14 +4736,18 @@ var SDK = class {
|
|
|
4292
4736
|
this.referrals = new Referrals(this.http, logger2);
|
|
4293
4737
|
this.reports = new Reports(this.http, logger2);
|
|
4294
4738
|
this.support = new Support(this.http, logger2);
|
|
4295
|
-
this.markets = new Markets(this.http, logger2);
|
|
4739
|
+
this.markets = new Markets(this.http, logger2, this.wallet);
|
|
4296
4740
|
this.wsTransport = config.wsTransport || new StandaloneWsTransport(baseUrl);
|
|
4297
4741
|
this.wsTransport.setAppId(config.appId);
|
|
4298
4742
|
this.lobbyStore = createLobbyStore(this.wsTransport);
|
|
4743
|
+
this.lobbies.setLobbyStore(this.lobbyStore);
|
|
4299
4744
|
this.gameStore = createGameStore(this.wsTransport);
|
|
4745
|
+
this.games.setGameStore(this.gameStore);
|
|
4300
4746
|
this.gameActionsStore = createGameActionsStore(this.wsTransport);
|
|
4747
|
+
this.games.setGameActionsStore(this.gameActionsStore);
|
|
4301
4748
|
this.chatStore = createChatStore(this.wsTransport);
|
|
4302
4749
|
this.dmThreadsStore = createDmThreadsStore();
|
|
4750
|
+
this.chat.setDmThreadsStore(this.dmThreadsStore);
|
|
4303
4751
|
this.notificationsStore = createNotificationsStore();
|
|
4304
4752
|
this.friendsStore = createFriendsStore();
|
|
4305
4753
|
this.notifications = new Notifications(
|
|
@@ -4329,6 +4777,29 @@ var SDK = class {
|
|
|
4329
4777
|
this.wsTransport.connect();
|
|
4330
4778
|
await this.wsTransport.waitUntilConnected(timeoutMs);
|
|
4331
4779
|
}
|
|
4780
|
+
/**
|
|
4781
|
+
* Handle the full deposit-to-queue lifecycle for a lobby.
|
|
4782
|
+
*
|
|
4783
|
+
* After the deposit is confirmed, the backend may not have processed the
|
|
4784
|
+
* `lobby.deposit.allConfirmed` event yet, so the lobby can still be in
|
|
4785
|
+
* 'preparing' status. This method re-fetches the lobby and explicitly
|
|
4786
|
+
* joins the queue if needed, then pushes the latest state to the store
|
|
4787
|
+
* so the UI transitions immediately.
|
|
4788
|
+
*/
|
|
4789
|
+
async depositAndJoinQueue(lobbyId) {
|
|
4790
|
+
const result = await this.escrow.depositForLobby(lobbyId);
|
|
4791
|
+
if (!result.canProceedToQueue) {
|
|
4792
|
+
const lobby2 = await this.lobbies.getLobby(lobbyId);
|
|
4793
|
+
this.lobbyStore.setBaseState([lobby2]);
|
|
4794
|
+
return { ...result, lobby: lobby2 };
|
|
4795
|
+
}
|
|
4796
|
+
let lobby = await this.lobbies.getLobby(lobbyId);
|
|
4797
|
+
if (lobby.status === "preparing") {
|
|
4798
|
+
lobby = await this.lobbies.joinQueue(lobbyId);
|
|
4799
|
+
}
|
|
4800
|
+
this.lobbyStore.setBaseState([lobby]);
|
|
4801
|
+
return { ...result, lobby };
|
|
4802
|
+
}
|
|
4332
4803
|
};
|
|
4333
4804
|
|
|
4334
4805
|
// ../ws-shared-worker/dist/protocol.js
|
|
@@ -4436,6 +4907,26 @@ var SharedWorkerClient = class {
|
|
|
4436
4907
|
}
|
|
4437
4908
|
}
|
|
4438
4909
|
}
|
|
4910
|
+
/**
|
|
4911
|
+
* Dispatch a synthetic local event directly to all registered handlers for
|
|
4912
|
+
* that event name (and wildcard handlers), without sending anything over the
|
|
4913
|
+
* wire. Used by SharedWorkerTransport to fire synthetic events like
|
|
4914
|
+
* `connection:reconnected` that originate from transport-layer logic rather
|
|
4915
|
+
* than from the server.
|
|
4916
|
+
*/
|
|
4917
|
+
dispatchLocalEvent(eventName, payload) {
|
|
4918
|
+
const handlers = this.eventHandlers.get(eventName);
|
|
4919
|
+
if (handlers) {
|
|
4920
|
+
for (const handler of handlers) {
|
|
4921
|
+
handler(payload);
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
if (this.wildcardHandlers.size > 0) {
|
|
4925
|
+
for (const handler of this.wildcardHandlers) {
|
|
4926
|
+
handler(eventName, payload);
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4439
4930
|
postMessage(command) {
|
|
4440
4931
|
this.port.postMessage(command);
|
|
4441
4932
|
}
|
|
@@ -4445,6 +4936,10 @@ var SharedWorkerClient = class {
|
|
|
4445
4936
|
var SharedWorkerTransport = class extends BaseWsTransport {
|
|
4446
4937
|
constructor(workerUrl, socketUrl) {
|
|
4447
4938
|
super();
|
|
4939
|
+
/** Tracks whether a successful connection has been established at least once,
|
|
4940
|
+
* so that subsequent ws_open events are treated as reconnects and the
|
|
4941
|
+
* `connection:reconnected` event is dispatched to subscribers. */
|
|
4942
|
+
this.hasConnectedBefore = false;
|
|
4448
4943
|
this.client = new SharedWorkerClient(workerUrl);
|
|
4449
4944
|
this.url = socketUrl;
|
|
4450
4945
|
this.client.onMessage((event) => this.handleWorkerMessage(event));
|
|
@@ -4485,14 +4980,22 @@ var SharedWorkerTransport = class extends BaseWsTransport {
|
|
|
4485
4980
|
}
|
|
4486
4981
|
handleWorkerMessage(event) {
|
|
4487
4982
|
switch (event.type) {
|
|
4488
|
-
case "ws_open":
|
|
4983
|
+
case "ws_open": {
|
|
4984
|
+
const wasReconnect = this.hasConnectedBefore;
|
|
4985
|
+
this.hasConnectedBefore = true;
|
|
4489
4986
|
this.updateConnectionState({
|
|
4490
4987
|
connected: true,
|
|
4491
4988
|
connecting: false,
|
|
4492
4989
|
error: null
|
|
4493
4990
|
});
|
|
4494
4991
|
this.onReconnect();
|
|
4992
|
+
if (wasReconnect) {
|
|
4993
|
+
this.client.dispatchLocalEvent("connection:reconnected", {
|
|
4994
|
+
timestamp: Date.now()
|
|
4995
|
+
});
|
|
4996
|
+
}
|
|
4495
4997
|
break;
|
|
4998
|
+
}
|
|
4496
4999
|
case "ws_close":
|
|
4497
5000
|
this.updateConnectionState({
|
|
4498
5001
|
connected: false,
|
|
@@ -4529,6 +5032,7 @@ var import_utils = __toESM(require_dist(), 1);
|
|
|
4529
5032
|
MIN_TRANSFER_AMOUNT,
|
|
4530
5033
|
Markets,
|
|
4531
5034
|
NodeStorage,
|
|
5035
|
+
NoopAnalyticsClient,
|
|
4532
5036
|
Referrals,
|
|
4533
5037
|
Reports,
|
|
4534
5038
|
SDK,
|
|
@@ -4556,6 +5060,7 @@ var import_utils = __toESM(require_dist(), 1);
|
|
|
4556
5060
|
formatMoneyMinor,
|
|
4557
5061
|
isRetryableError,
|
|
4558
5062
|
logger,
|
|
5063
|
+
selectGameLifecycleState,
|
|
4559
5064
|
toMajor,
|
|
4560
5065
|
withRetry
|
|
4561
5066
|
});
|