@bounded-sh/core 0.0.98 → 0.0.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/config.d.ts +1 -0
- package/dist/{index-D-dJjY-Q.js → index-B8PpRj6x.js} +159 -108
- package/dist/index-B8PpRj6x.js.map +1 -0
- package/dist/{index-BDW82Uxu.mjs → index-DW5yMQwV.mjs} +159 -108
- package/dist/index-DW5yMQwV.mjs.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{realtime-store-DTS5OLm2.mjs → realtime-store-CeDT7vJF.mjs} +2 -2
- package/dist/{realtime-store-DTS5OLm2.mjs.map → realtime-store-CeDT7vJF.mjs.map} +1 -1
- package/dist/{realtime-store-Dytq73zO.js → realtime-store-D7mJ7rTj.js} +2 -2
- package/dist/{realtime-store-Dytq73zO.js.map → realtime-store-D7mJ7rTj.js.map} +1 -1
- package/dist/{solana-message-j1s9osZD.mjs → solana-message-BGkYFU3C.mjs} +2 -2
- package/dist/{solana-message-j1s9osZD.mjs.map → solana-message-BGkYFU3C.mjs.map} +1 -1
- package/dist/{solana-message-Cww3s9HW.js → solana-message-w2FxOkFA.js} +2 -2
- package/dist/{solana-message-Cww3s9HW.js.map → solana-message-w2FxOkFA.js.map} +1 -1
- package/dist/{solana-operations-da26Ject.mjs → solana-operations-CHgW7Pue.mjs} +4 -4
- package/dist/{solana-operations-da26Ject.mjs.map → solana-operations-CHgW7Pue.mjs.map} +1 -1
- package/dist/{solana-operations-SR1En23U.js → solana-operations-DJQDYdrk.js} +3 -3
- package/dist/{solana-operations-SR1En23U.js.map → solana-operations-DJQDYdrk.js.map} +1 -1
- package/dist/{solana-v1-operations-DRm8TzzJ.js → solana-v1-operations-BF2rjG23.js} +3 -3
- package/dist/{solana-v1-operations-DRm8TzzJ.js.map → solana-v1-operations-BF2rjG23.js.map} +1 -1
- package/dist/{solana-v1-operations-BQQfm-ni.mjs → solana-v1-operations-BPn-poee.mjs} +3 -3
- package/dist/{solana-v1-operations-BQQfm-ni.mjs.map → solana-v1-operations-BPn-poee.mjs.map} +1 -1
- package/dist/utils/web-session-manager.d.ts +7 -2
- package/package.json +8 -2
- package/dist/index-BDW82Uxu.mjs.map +0 -1
- package/dist/index-D-dJjY-Q.js.map +0 -1
|
@@ -87,6 +87,9 @@ function getWebhookKeysUrl() {
|
|
|
87
87
|
* `verifyWebhook` to bind a webhook to THIS app by default (#072) rather than trusting any
|
|
88
88
|
* app's shared-key-signed delivery.
|
|
89
89
|
*/
|
|
90
|
+
function getConfiguredSessionNamespace() {
|
|
91
|
+
return JSON.stringify([(clientConfig.humanAuthApiUrl || "https://auth.bounded.sh").replace(/\/$/, ""), clientConfig.appId]);
|
|
92
|
+
}
|
|
90
93
|
function getConfiguredAppId() {
|
|
91
94
|
return clientConfig.appId || undefined;
|
|
92
95
|
}
|
|
@@ -925,6 +928,30 @@ function decideCommit(current, expected, next) {
|
|
|
925
928
|
}
|
|
926
929
|
|
|
927
930
|
class WebSessionManager {
|
|
931
|
+
static scopedStorageKey() {
|
|
932
|
+
return 'bounded_session_storage:v2:' + getConfiguredSessionNamespace();
|
|
933
|
+
}
|
|
934
|
+
static get BOUNDED_SESSION_STORAGE_KEY() {
|
|
935
|
+
const scoped = this.scopedStorageKey();
|
|
936
|
+
return localStorage.getItem(scoped + ':scope') === 'app' || localStorage.getItem(scoped) !== null
|
|
937
|
+
? scoped : 'bounded_session_storage';
|
|
938
|
+
}
|
|
939
|
+
static logoutKey() { return this.scopedStorageKey() + ':logout'; }
|
|
940
|
+
/** Capture before login's network work; checked under the session mutation lock. */
|
|
941
|
+
static captureSessionEstablishment() {
|
|
942
|
+
var _a, _b;
|
|
943
|
+
const namespace = getConfiguredSessionNamespace();
|
|
944
|
+
const epoch = this.establishmentEpoch;
|
|
945
|
+
const generation = (_b = (_a = this.readRaw()) === null || _a === void 0 ? void 0 : _a.sessionGeneration) !== null && _b !== void 0 ? _b : null;
|
|
946
|
+
const logout = localStorage.getItem(this.logoutKey());
|
|
947
|
+
return () => {
|
|
948
|
+
var _a, _b;
|
|
949
|
+
if (namespace !== getConfiguredSessionNamespace() || epoch !== this.establishmentEpoch
|
|
950
|
+
|| generation !== ((_b = (_a = this.readRaw()) === null || _a === void 0 ? void 0 : _a.sessionGeneration) !== null && _b !== void 0 ? _b : null)
|
|
951
|
+
|| logout !== localStorage.getItem(this.logoutKey()))
|
|
952
|
+
throw new Error('session_establishment_superseded');
|
|
953
|
+
};
|
|
954
|
+
}
|
|
928
955
|
/** Raw read of the stored blob. Callers inside the lock use this, never a cached copy. */
|
|
929
956
|
static readRaw() {
|
|
930
957
|
if (typeof window === "undefined")
|
|
@@ -984,10 +1011,11 @@ class WebSessionManager {
|
|
|
984
1011
|
/* ------------------------------------------------------------------ */
|
|
985
1012
|
/* STORE */
|
|
986
1013
|
/* ------------------------------------------------------------------ */
|
|
987
|
-
static async storeSession(address, accessToken, idToken, refreshToken, issuer) {
|
|
1014
|
+
static async storeSession(address, accessToken, idToken, refreshToken, issuer, beforeCommit) {
|
|
988
1015
|
var _a;
|
|
989
1016
|
if (typeof window === "undefined")
|
|
990
1017
|
return;
|
|
1018
|
+
let appScoped = false;
|
|
991
1019
|
// JWT-wallet binding: refuse to store a session whose idToken is bound
|
|
992
1020
|
// to a different wallet than `address`. Prevents races that would otherwise
|
|
993
1021
|
// leave localStorage with mismatched address/token state.
|
|
@@ -995,6 +1023,7 @@ class WebSessionManager {
|
|
|
995
1023
|
const payloadB64 = idToken.split(".")[1];
|
|
996
1024
|
if (payloadB64) {
|
|
997
1025
|
const payload = JSON.parse(this.decodeBase64Url(payloadB64));
|
|
1026
|
+
appScoped = payload["custom:walletScope"] === "app";
|
|
998
1027
|
const tokenWallet = payload["custom:walletAddress"];
|
|
999
1028
|
if (tokenWallet && tokenWallet !== address) {
|
|
1000
1029
|
throw new Error(`[WebSessionManager] Refusing to store session: address (${address}) does not match idToken custom:walletAddress (${tokenWallet})`);
|
|
@@ -1012,6 +1041,9 @@ class WebSessionManager {
|
|
|
1012
1041
|
}
|
|
1013
1042
|
const config = await getConfig();
|
|
1014
1043
|
const currentAppId = config.appId;
|
|
1044
|
+
const namespace = getConfiguredSessionNamespace();
|
|
1045
|
+
const storageKey = appScoped || localStorage.getItem(this.scopedStorageKey() + ":scope") === "app"
|
|
1046
|
+
? this.scopedStorageKey() : "bounded_session_storage";
|
|
1015
1047
|
// The auth issuer base that minted this session, so silent refresh POSTs to
|
|
1016
1048
|
// the RIGHT /session/refresh. Email overrides with humanAuthApiUrl; all
|
|
1017
1049
|
// wallet/guest/signature flows default to authApiUrl (the bounded-auth issuer).
|
|
@@ -1019,12 +1051,33 @@ class WebSessionManager {
|
|
|
1019
1051
|
// A login always starts a NEW generation. Refresh commits preserve it, so
|
|
1020
1052
|
// "same generation" means "same login", which is what lets a post-refresh
|
|
1021
1053
|
// retry be told apart from a re-login by the same wallet.
|
|
1054
|
+
const generation = mintSessionGeneration();
|
|
1022
1055
|
await withSessionLock(() => {
|
|
1023
|
-
|
|
1056
|
+
beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit();
|
|
1057
|
+
if (namespace !== getConfiguredSessionNamespace())
|
|
1058
|
+
throw new Error("session_establishment_superseded");
|
|
1059
|
+
++this.establishmentEpoch;
|
|
1060
|
+
if (appScoped)
|
|
1061
|
+
localStorage.setItem(this.scopedStorageKey() + ":scope", "app");
|
|
1062
|
+
localStorage.setItem(storageKey, JSON.stringify(Object.assign({ address,
|
|
1024
1063
|
accessToken,
|
|
1025
1064
|
idToken,
|
|
1026
|
-
refreshToken, appId: currentAppId, sessionGeneration:
|
|
1065
|
+
refreshToken, appId: currentAppId, sessionGeneration: generation }, (sessionIssuer ? { issuer: sessionIssuer } : {}))));
|
|
1027
1066
|
});
|
|
1067
|
+
// Adoption can fail after persistence (for example, logout while the
|
|
1068
|
+
// provider is loading). Remove only this incomplete login, even if the
|
|
1069
|
+
// caller has since switched app configuration or another tab signed in.
|
|
1070
|
+
return async () => {
|
|
1071
|
+
const removed = await withSessionLock(() => {
|
|
1072
|
+
const raw = localStorage.getItem(storageKey);
|
|
1073
|
+
if (!raw || JSON.parse(raw).sessionGeneration !== generation)
|
|
1074
|
+
return false;
|
|
1075
|
+
localStorage.removeItem(storageKey);
|
|
1076
|
+
return true;
|
|
1077
|
+
});
|
|
1078
|
+
if (removed)
|
|
1079
|
+
notifySessionCleared();
|
|
1080
|
+
};
|
|
1028
1081
|
}
|
|
1029
1082
|
/* ------------------------------------------------------------------ */
|
|
1030
1083
|
/* GET */
|
|
@@ -1139,10 +1192,14 @@ class WebSessionManager {
|
|
|
1139
1192
|
static async clearSession() {
|
|
1140
1193
|
if (typeof window === "undefined")
|
|
1141
1194
|
return;
|
|
1195
|
+
++this.establishmentEpoch;
|
|
1196
|
+
const key = this.BOUNDED_SESSION_STORAGE_KEY;
|
|
1197
|
+
const logoutKey = this.logoutKey();
|
|
1142
1198
|
const removed = await withSessionLock(() => {
|
|
1143
|
-
|
|
1199
|
+
localStorage.setItem(logoutKey, mintSessionGeneration());
|
|
1200
|
+
if (localStorage.getItem(key) === null)
|
|
1144
1201
|
return false;
|
|
1145
|
-
localStorage.removeItem(
|
|
1202
|
+
localStorage.removeItem(key);
|
|
1146
1203
|
return true;
|
|
1147
1204
|
});
|
|
1148
1205
|
// Only announce a clear that actually happened, so a no-op cannot sign out
|
|
@@ -1289,7 +1346,7 @@ class WebSessionManager {
|
|
|
1289
1346
|
return { outcome: result.outcome, session: result.session };
|
|
1290
1347
|
}
|
|
1291
1348
|
}
|
|
1292
|
-
WebSessionManager.
|
|
1349
|
+
WebSessionManager.establishmentEpoch = 0;
|
|
1293
1350
|
|
|
1294
1351
|
let _config = null;
|
|
1295
1352
|
class ReactNativeSessionManager {
|
|
@@ -4131,7 +4188,7 @@ class ServerSessionManager {
|
|
|
4131
4188
|
const sessionConfig = (_a = this.sessionConfig) !== null && _a !== void 0 ? _a : undefined;
|
|
4132
4189
|
/* fetch nonce from auth API (bound to the snapshotted app+issuer) */
|
|
4133
4190
|
const nonce = await genAuthNonce(sessionConfig);
|
|
4134
|
-
const { genSolanaMessage } = await import('./solana-message-
|
|
4191
|
+
const { genSolanaMessage } = await import('./solana-message-BGkYFU3C.mjs');
|
|
4135
4192
|
const message = await genSolanaMessage(address, nonce, sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.appId);
|
|
4136
4193
|
/* sign the message */
|
|
4137
4194
|
const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
|
|
@@ -5239,7 +5296,6 @@ const WS_V2_PATH = '/ws/v2';
|
|
|
5239
5296
|
let browserReconnectHooksAttached = false;
|
|
5240
5297
|
let lastBrowserTriggeredReconnectAt = 0;
|
|
5241
5298
|
let reconnectInProgress = null;
|
|
5242
|
-
let ambientAuthFailure = null;
|
|
5243
5299
|
// ============ Helper Functions ============
|
|
5244
5300
|
function generateSubscriptionId() {
|
|
5245
5301
|
return `sub_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
@@ -5367,34 +5423,6 @@ function getTokenExpirationTime(token) {
|
|
|
5367
5423
|
return null;
|
|
5368
5424
|
}
|
|
5369
5425
|
}
|
|
5370
|
-
/**
|
|
5371
|
-
* Latch a failure so a dead session does not re-hammer /session/refresh every tick.
|
|
5372
|
-
*
|
|
5373
|
-
* NOT latched when the failure came with the session being cleared or replaced.
|
|
5374
|
-
* The clear notifies auth-state listeners, which kicks a reconnect that resets this
|
|
5375
|
-
* — and if we re-latched afterwards, `getConnectionAuthToken` would keep throwing
|
|
5376
|
-
* even though storage is now empty, so an ordinary expiry left WebSocket
|
|
5377
|
-
* acquisition permanently broken instead of falling back to an anonymous socket.
|
|
5378
|
-
*/
|
|
5379
|
-
function rememberAmbientAuthFailure(error) {
|
|
5380
|
-
if ((error === null || error === void 0 ? void 0 : error.code) === AUTH_CHANGED_CODE)
|
|
5381
|
-
return error;
|
|
5382
|
-
// The session is gone: the right next move is an anonymous connection, not a
|
|
5383
|
-
// remembered failure.
|
|
5384
|
-
if (!getIdTokenSync())
|
|
5385
|
-
return error;
|
|
5386
|
-
ambientAuthFailure = error;
|
|
5387
|
-
return error;
|
|
5388
|
-
}
|
|
5389
|
-
/** Best-effort synchronous peek, only for deciding whether a session still exists. */
|
|
5390
|
-
function getIdTokenSync() {
|
|
5391
|
-
try {
|
|
5392
|
-
return getActiveSessionManager().getIdToken();
|
|
5393
|
-
}
|
|
5394
|
-
catch (_a) {
|
|
5395
|
-
return null;
|
|
5396
|
-
}
|
|
5397
|
-
}
|
|
5398
5426
|
function failConnectionAuth(connection, error) {
|
|
5399
5427
|
connection.authFailure = error;
|
|
5400
5428
|
connection.pendingAuthToken = null;
|
|
@@ -5449,8 +5477,8 @@ function scheduleTokenRefresh(connection, isServer) {
|
|
|
5449
5477
|
console.info('[WS v2] Token refreshed successfully, stored for next connection');
|
|
5450
5478
|
}
|
|
5451
5479
|
catch (refreshError) {
|
|
5452
|
-
const definitive = (refreshError === null || refreshError === void 0 ? void 0 : refreshError.code) ===
|
|
5453
|
-
|| (
|
|
5480
|
+
const definitive = (refreshError === null || refreshError === void 0 ? void 0 : refreshError.code) === AUTH_CHANGED_CODE
|
|
5481
|
+
|| !await getIdToken(isServer);
|
|
5454
5482
|
if (definitive) {
|
|
5455
5483
|
// The session is dead or belongs to someone else now. The shared
|
|
5456
5484
|
// helper has already cleared storage and notified auth-state
|
|
@@ -5480,28 +5508,19 @@ function scheduleTokenRefresh(connection, isServer) {
|
|
|
5480
5508
|
* WS-local wrapper around the shared freshness helper.
|
|
5481
5509
|
*
|
|
5482
5510
|
* The shared helper owns expiry classification, the deduplicated
|
|
5483
|
-
* snapshot -> refresh -> commit transaction, and conditional clearing.
|
|
5484
|
-
*
|
|
5485
|
-
* refresh on every reconnect attempt and every 5-minute tick.
|
|
5511
|
+
* snapshot -> refresh -> commit transaction, and conditional clearing. A transient
|
|
5512
|
+
* failure must remain retryable; reconnect backoff bounds automatic attempts.
|
|
5486
5513
|
*/
|
|
5487
5514
|
async function getWsAuthToken(isServer) {
|
|
5488
5515
|
var _a, _b;
|
|
5489
|
-
// A remembered failure short-circuits, but only while a session still exists.
|
|
5490
|
-
// See rememberAmbientAuthFailure: once storage is cleared the correct next move
|
|
5491
|
-
// is an anonymous connection, not a replayed error.
|
|
5492
|
-
if (ambientAuthFailure && getIdTokenSync()) {
|
|
5493
|
-
throw ambientAuthFailure;
|
|
5494
|
-
}
|
|
5495
5516
|
try {
|
|
5496
|
-
|
|
5497
|
-
ambientAuthFailure = null;
|
|
5498
|
-
return token;
|
|
5517
|
+
return await getFreshAuthToken(isServer);
|
|
5499
5518
|
}
|
|
5500
5519
|
catch (error) {
|
|
5501
5520
|
// Status only — the raw axios error carries config.data (the refresh token)
|
|
5502
5521
|
// and must never reach a console or log sink.
|
|
5503
5522
|
console.error('[WS v2] Error acquiring auth token. status:', (_b = (_a = error === null || error === void 0 ? void 0 : error.status) !== null && _a !== void 0 ? _a : error === null || error === void 0 ? void 0 : error.code) !== null && _b !== void 0 ? _b : 'unknown');
|
|
5504
|
-
throw
|
|
5523
|
+
throw normalizeAuthExpiredError(error, 'Authentication expired and refresh failed');
|
|
5505
5524
|
}
|
|
5506
5525
|
}
|
|
5507
5526
|
/**
|
|
@@ -5519,18 +5538,21 @@ async function recoverFromServerAuthRejection(connection) {
|
|
|
5519
5538
|
const rejected = connection.pendingAuthToken;
|
|
5520
5539
|
if (!rejected)
|
|
5521
5540
|
return;
|
|
5522
|
-
//
|
|
5523
|
-
//
|
|
5541
|
+
// Share an in-flight attempt and stop retrying a definitively refused token.
|
|
5542
|
+
// Transient failures may retry on the next socket reconnect, with its backoff.
|
|
5524
5543
|
if (connection.serverRejectedAuthToken === rejected)
|
|
5525
5544
|
return;
|
|
5526
5545
|
connection.serverRejectedAuthToken = rejected;
|
|
5527
5546
|
try {
|
|
5528
5547
|
await getFreshAuthToken(connection.isServer, { force: true });
|
|
5529
5548
|
}
|
|
5530
|
-
catch (
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5549
|
+
catch (error) {
|
|
5550
|
+
if ((error === null || error === void 0 ? void 0 : error.code) !== AUTH_CHANGED_CODE && (error === null || error === void 0 ? void 0 : error.status) !== 401 && (error === null || error === void 0 ? void 0 : error.status) !== 403
|
|
5551
|
+
&& connection.serverRejectedAuthToken === rejected) {
|
|
5552
|
+
connection.serverRejectedAuthToken = null;
|
|
5553
|
+
}
|
|
5554
|
+
// The shared helper clears definitively rejected sessions and preserves
|
|
5555
|
+
// transient failures. Never reuse the failed result as a credential.
|
|
5534
5556
|
}
|
|
5535
5557
|
}
|
|
5536
5558
|
async function getConnectionAuthToken(isServer, authTokenProvider, anonymous) {
|
|
@@ -5541,9 +5563,6 @@ async function getConnectionAuthToken(isServer, authTokenProvider, anonymous) {
|
|
|
5541
5563
|
return null;
|
|
5542
5564
|
}
|
|
5543
5565
|
if (!authTokenProvider) {
|
|
5544
|
-
// getWsAuthToken, not the shared helper directly: the wrapper carries the
|
|
5545
|
-
// reconnect-storm guard that keeps a dead session from re-refreshing on
|
|
5546
|
-
// every reconnect attempt.
|
|
5547
5566
|
return getWsAuthToken(isServer);
|
|
5548
5567
|
}
|
|
5549
5568
|
try {
|
|
@@ -5648,11 +5667,13 @@ function cancelIdleConnectionClose(connection) {
|
|
|
5648
5667
|
connection.idleCloseTimer = null;
|
|
5649
5668
|
}
|
|
5650
5669
|
function closeConnectionNow(connection) {
|
|
5670
|
+
var _a;
|
|
5651
5671
|
if (connections.get(connection.key) !== connection)
|
|
5652
5672
|
return;
|
|
5653
5673
|
if (!connection.ws)
|
|
5654
5674
|
return;
|
|
5655
5675
|
cancelIdleConnectionClose(connection);
|
|
5676
|
+
(_a = connection.authRetryWake) === null || _a === void 0 ? void 0 : _a.call(connection);
|
|
5656
5677
|
if (connection.tokenRefreshTimer) {
|
|
5657
5678
|
clearInterval(connection.tokenRefreshTimer);
|
|
5658
5679
|
connection.tokenRefreshTimer = null;
|
|
@@ -5673,7 +5694,7 @@ function closeConnectionNow(connection) {
|
|
|
5673
5694
|
try {
|
|
5674
5695
|
ws.close();
|
|
5675
5696
|
}
|
|
5676
|
-
catch ( /* already closing */
|
|
5697
|
+
catch ( /* already closing */_b) { /* already closing */ }
|
|
5677
5698
|
}
|
|
5678
5699
|
function scheduleConnectionCloseIfIdle(connection) {
|
|
5679
5700
|
var _a;
|
|
@@ -5759,6 +5780,7 @@ async function getOrCreateConnection(appId, isServer, routePath, authTokenProvid
|
|
|
5759
5780
|
}
|
|
5760
5781
|
}
|
|
5761
5782
|
async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, leaseKey, source) {
|
|
5783
|
+
var _a, _b;
|
|
5762
5784
|
attachBrowserReconnectHooksOnce();
|
|
5763
5785
|
const anonymous = (source === null || source === void 0 ? void 0 : source.anonymous) === true;
|
|
5764
5786
|
// A per-room subscription gets its OWN connection routed to the room DO; all
|
|
@@ -5805,9 +5827,6 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
|
|
|
5805
5827
|
// lease also guards this window, but cancelling here avoids a needless
|
|
5806
5828
|
// grace-timer round-trip on the common reuse path.
|
|
5807
5829
|
cancelIdleConnectionClose(connection);
|
|
5808
|
-
if (connection.authFailure) {
|
|
5809
|
-
throw connection.authFailure;
|
|
5810
|
-
}
|
|
5811
5830
|
let currentAuthToken;
|
|
5812
5831
|
try {
|
|
5813
5832
|
currentAuthToken = await getConnectionAuthToken(isServer, authTokenProvider, anonymous);
|
|
@@ -5817,6 +5836,14 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
|
|
|
5817
5836
|
failConnectionAuth(connection, authError);
|
|
5818
5837
|
throw authError;
|
|
5819
5838
|
}
|
|
5839
|
+
if (connection.authFailure) {
|
|
5840
|
+
// The preflight recovered, but failConnectionAuth invalidated this
|
|
5841
|
+
// socket's auth state. Authenticate again before replaying any reads.
|
|
5842
|
+
connection.authFailure = null;
|
|
5843
|
+
(_a = connection.authRetryWake) === null || _a === void 0 ? void 0 : _a.call(connection);
|
|
5844
|
+
connection.ws.reconnect();
|
|
5845
|
+
return connection;
|
|
5846
|
+
}
|
|
5820
5847
|
if (connection.isConnected &&
|
|
5821
5848
|
!connection.isAuthenticating &&
|
|
5822
5849
|
!connectionMatchesAuthToken(connection, currentAuthToken)) {
|
|
@@ -5859,50 +5886,59 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
|
|
|
5859
5886
|
isServer,
|
|
5860
5887
|
serverRejectedAuthToken: null,
|
|
5861
5888
|
};
|
|
5889
|
+
// Resolve configuration before handing control to reconnecting-websocket.
|
|
5890
|
+
// Its 4.4.0 URL-provider promise has no rejection handler and a rejected
|
|
5891
|
+
// provider permanently holds _connectLock. Credential failures therefore
|
|
5892
|
+
// stay inside this bounded, cancellable acquisition loop.
|
|
5893
|
+
const wsApiUrl = (_b = source === null || source === void 0 ? void 0 : source.wsApiUrl) !== null && _b !== void 0 ? _b : (await getConfig()).wsApiUrl;
|
|
5894
|
+
const wsUrl = new URL(wsApiUrl);
|
|
5895
|
+
wsUrl.pathname = WS_V2_PATH;
|
|
5896
|
+
wsUrl.searchParams.append('appId', appId || (await getConfig()).appId);
|
|
5897
|
+
if (connection.routePath)
|
|
5898
|
+
wsUrl.searchParams.append('routePath', connection.routePath);
|
|
5862
5899
|
connections.set(connKey, connection);
|
|
5863
|
-
// URL provider for reconnection with fresh tokens
|
|
5864
5900
|
const urlProvider = async () => {
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
connection.pendingAuthSnapshot = authSnapshotFromToken(authToken);
|
|
5901
|
-
if (authToken) {
|
|
5902
|
-
// Successful token acquisition — reset failure counter
|
|
5901
|
+
let retryDelay = WS_CONFIG.minReconnectionDelay;
|
|
5902
|
+
while (connections.get(connKey) === connection) {
|
|
5903
|
+
let authToken;
|
|
5904
|
+
try {
|
|
5905
|
+
authToken = initialAuthToken !== undefined
|
|
5906
|
+
? initialAuthToken
|
|
5907
|
+
: await getConnectionAuthToken(isServer, connection.authTokenProvider, connection.anonymous);
|
|
5908
|
+
initialAuthToken = undefined;
|
|
5909
|
+
}
|
|
5910
|
+
catch (error) {
|
|
5911
|
+
if (connections.get(connKey) !== connection)
|
|
5912
|
+
break;
|
|
5913
|
+
const authError = normalizeAuthExpiredError(error, 'Authentication expired and refresh failed');
|
|
5914
|
+
failConnectionAuth(connection, authError);
|
|
5915
|
+
// An error callback may synchronously tear down or replace this
|
|
5916
|
+
// connection. Do not install a retry after its cleanup completed.
|
|
5917
|
+
if (connections.get(connKey) !== connection)
|
|
5918
|
+
break;
|
|
5919
|
+
await new Promise((resolve) => {
|
|
5920
|
+
const wake = () => {
|
|
5921
|
+
clearTimeout(timer);
|
|
5922
|
+
if (connection.authRetryWake === wake)
|
|
5923
|
+
connection.authRetryWake = undefined;
|
|
5924
|
+
resolve();
|
|
5925
|
+
};
|
|
5926
|
+
const timer = setTimeout(wake, retryDelay);
|
|
5927
|
+
connection.authRetryWake = wake;
|
|
5928
|
+
});
|
|
5929
|
+
retryDelay = Math.min(MAX_RECONNECT_DELAY_MS, retryDelay * RECONNECT_DELAY_GROW_FACTOR);
|
|
5930
|
+
continue;
|
|
5931
|
+
}
|
|
5932
|
+
if (connections.get(connKey) !== connection)
|
|
5933
|
+
break;
|
|
5934
|
+
connection.pendingAuthToken = authToken || null;
|
|
5935
|
+
connection.pendingAuthSnapshot = authSnapshotFromToken(authToken);
|
|
5903
5936
|
connection.consecutiveAuthFailures = 0;
|
|
5904
5937
|
connection.authFailure = null;
|
|
5938
|
+
return wsUrl.toString();
|
|
5905
5939
|
}
|
|
5940
|
+
// Teardown has called close() on the wrapper, which prevents creation
|
|
5941
|
+
// of a native socket when this abandoned provider resolves.
|
|
5906
5942
|
return wsUrl.toString();
|
|
5907
5943
|
};
|
|
5908
5944
|
// Create WebSocket connection
|
|
@@ -6711,6 +6747,13 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
|
|
|
6711
6747
|
authPrincipal: connection.authPrincipal,
|
|
6712
6748
|
};
|
|
6713
6749
|
connection.pendingSubscriptions.set(subscriptionId, pending);
|
|
6750
|
+
// A fast auth refusal can open and close the socket before the first
|
|
6751
|
+
// caller finishes acquiring it. Keep this subscription for recovery,
|
|
6752
|
+
// but report the already observed disconnect without a timeout delay.
|
|
6753
|
+
if (pending.socketGeneration > 0 && socket.readyState === WS_READY_STATE_CLOSED) {
|
|
6754
|
+
rejectPendingSubscriptionConfirmation(connection, subscriptionId, new Error('WebSocket disconnected'));
|
|
6755
|
+
return;
|
|
6756
|
+
}
|
|
6714
6757
|
// Bound a cold connection/auth wait as well. Once the socket becomes
|
|
6715
6758
|
// ready, sendSubscribeWithConfirmation() replaces this timer with a
|
|
6716
6759
|
// fresh per-socket confirmation window.
|
|
@@ -6788,6 +6831,7 @@ async function removeCallbackFromSubscription(connection, subscriptionId, callba
|
|
|
6788
6831
|
* Close all v2 subscriptions.
|
|
6789
6832
|
*/
|
|
6790
6833
|
async function closeAllSubscriptionsV2() {
|
|
6834
|
+
var _a;
|
|
6791
6835
|
const closePromises = [];
|
|
6792
6836
|
for (const [appId, connection] of connections) {
|
|
6793
6837
|
cancelIdleConnectionClose(connection);
|
|
@@ -6814,8 +6858,14 @@ async function closeAllSubscriptionsV2() {
|
|
|
6814
6858
|
ws.close();
|
|
6815
6859
|
}));
|
|
6816
6860
|
}
|
|
6861
|
+
else {
|
|
6862
|
+
// CLOSED may mean a URL/auth acquisition is still pending.
|
|
6863
|
+
// close() also disables the wrapper's future native socket.
|
|
6864
|
+
ws.close();
|
|
6865
|
+
}
|
|
6817
6866
|
}
|
|
6818
6867
|
connections.delete(appId);
|
|
6868
|
+
(_a = connection.authRetryWake) === null || _a === void 0 ? void 0 : _a.call(connection);
|
|
6819
6869
|
}
|
|
6820
6870
|
connectionAcquisitions.clear();
|
|
6821
6871
|
await Promise.all(closePromises);
|
|
@@ -6894,7 +6944,7 @@ async function reconnectWithNewAuthV2() {
|
|
|
6894
6944
|
}
|
|
6895
6945
|
}
|
|
6896
6946
|
async function doReconnectWithNewAuth() {
|
|
6897
|
-
|
|
6947
|
+
var _a;
|
|
6898
6948
|
// SECURITY (H1): the logged-in identity is changing (login / logout / switch).
|
|
6899
6949
|
// Wipe ALL principal-scoped read caches so the new identity can never observe
|
|
6900
6950
|
// data cached for the previous one. Clear both the WS response cache and the
|
|
@@ -6916,7 +6966,7 @@ async function doReconnectWithNewAuth() {
|
|
|
6916
6966
|
console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
|
|
6917
6967
|
}
|
|
6918
6968
|
try {
|
|
6919
|
-
const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-
|
|
6969
|
+
const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-CeDT7vJF.mjs');
|
|
6920
6970
|
await reconnectRealtimeStoreWithNewAuth();
|
|
6921
6971
|
}
|
|
6922
6972
|
catch (error) {
|
|
@@ -6941,6 +6991,7 @@ async function doReconnectWithNewAuth() {
|
|
|
6941
6991
|
// Reset auth failure counter — this is a proactive reconnect (login, token refresh)
|
|
6942
6992
|
connection.consecutiveAuthFailures = 0;
|
|
6943
6993
|
connection.authFailure = null;
|
|
6994
|
+
(_a = connection.authRetryWake) === null || _a === void 0 ? void 0 : _a.call(connection);
|
|
6944
6995
|
// Close the WebSocket (this triggers reconnection in ReconnectingWebSocket)
|
|
6945
6996
|
// We use reconnect() which will close and re-open with fresh URL (including new token)
|
|
6946
6997
|
try {
|
|
@@ -8366,7 +8417,7 @@ async function setMany(many, options) {
|
|
|
8366
8417
|
'Nothing was signed or submitted.');
|
|
8367
8418
|
}
|
|
8368
8419
|
const solanaChain = workerChain;
|
|
8369
|
-
const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await import('./solana-operations-
|
|
8420
|
+
const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await import('./solana-operations-CHgW7Pue.mjs');
|
|
8370
8421
|
// OA-0001: derive the expected intent from OUR OWN request (the one
|
|
8371
8422
|
// ground truth a tampered builder response cannot change) so the
|
|
8372
8423
|
// validator can reject any transaction whose operation semantics
|
|
@@ -9851,7 +9902,7 @@ function toMillis(seconds) {
|
|
|
9851
9902
|
|
|
9852
9903
|
/** SIWS message creation is async already, so its onchain module can be lazy. */
|
|
9853
9904
|
async function genSolanaMessage(address, nonce) {
|
|
9854
|
-
const implementation = await import('./solana-message-
|
|
9905
|
+
const implementation = await import('./solana-message-BGkYFU3C.mjs');
|
|
9855
9906
|
return implementation.genSolanaMessage(address, nonce);
|
|
9856
9907
|
}
|
|
9857
9908
|
|
|
@@ -10401,4 +10452,4 @@ function defineLiveModule(mod) {
|
|
|
10401
10452
|
}
|
|
10402
10453
|
|
|
10403
10454
|
export { getIdToken as $, subscribe as A, closeAllSubscriptions as B, clearCache as C, getCachedData as D, reconnectWithNewAuth as E, createClient as F, ScopedClientReadOnlyError as G, subscribeQuery as H, InsufficientBalanceError as I, closeAllQuerySubscriptions as J, canonicalizeArgs as K, canonicalizeResult as L, DEFAULT_QUERY_POLL_INTERVAL_MS as M, MIN_QUERY_POLL_INTERVAL_MS as N, BoundedDeclineError as O, deriveDecline as P, dispatchDeclineEvent as Q, DECLINE_EVENT_NAME as R, SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION as S, dispatchRateLimitEvent as T, RATE_LIMIT_EVENT_NAME as U, hasActiveConnection as V, increment as W, serverTimestamp as X, now as Y, toSeconds as Z, toMillis as _, getTransactionJson as a, deriveUserIdentityFromIdToken as a0, getSessionGeneration as a1, WebSessionManager as a2, ReactNativeSessionManager as a3, getActiveSessionManager as a4, onSessionCleared as a5, SessionEnvironmentError as a6, UNSUPPORTED_ENVIRONMENT_CODE as a7, AUTH_CHANGED_CODE as a8, AUTH_EXPIRED_CODE as a9, withEffects as aA, isEffectResult as aB, defineLiveModule as aC, EFFECT_INTENT_ADDRESS as aD, isAuthExpiredError as aa, getFreshAuthToken as ab, ServerSessionManager as ac, createSessionWithSignature as ad, refreshSession as ae, revokeSession as af, signSessionCreateMessage as ag, genAuthNonce as ah, genSolanaMessage as ai, createSolanaSignInRequest as aj, createSolanaSignInMessageText as ak, genSolanaSignInRequest as al, genSolanaSignInMessage as am, browserSignInDomain as an, SOLANA_SIGN_IN_STATEMENT as ao, genSiweMessage as ap, normalizeEvmAddress as aq, toLowerEvmAddress as ar, functions as as, invoke as at, FunctionInvokeError as au, live as av, intent as aw, status as ax, subscribeView as ay, LiveIntentError as az, bufferExports as b, getWebhookKeysUrl as c, getConfiguredAppId as d, get as e, getMany as f, getConfig as g, docId as h, init as i, setMany as j, setFile as k, getFiles as l, search as m, runQueryMany as n, runExpression as o, runExpressionMany as p, queryAggregate as q, runQuery as r, set as s, signMessage as t, signTransaction as u, signAndSubmitTransaction as v, count as w, aggregate as x, reconcileSetResult as y, waitForSetResult as z };
|
|
10404
|
-
//# sourceMappingURL=index-
|
|
10455
|
+
//# sourceMappingURL=index-DW5yMQwV.mjs.map
|