@bounded-sh/core 0.0.55 → 0.0.57
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 +20 -0
- package/dist/client/live.d.ts +9 -0
- package/dist/{index-BXvTkFw2.js → index-B30-XpaX.js} +194 -38
- package/dist/index-B30-XpaX.js.map +1 -0
- package/dist/{index-qcTp-5Mt.mjs → index-DYbzK8YT.mjs} +194 -39
- package/dist/index-DYbzK8YT.mjs.map +1 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{realtime-store-BzKEmyxq.mjs → realtime-store-BSuV-Vyr.mjs} +2 -2
- package/dist/{realtime-store-BzKEmyxq.mjs.map → realtime-store-BSuV-Vyr.mjs.map} +1 -1
- package/dist/{realtime-store-DmzboN-H.js → realtime-store-BpQsQYhl.js} +2 -2
- package/dist/{realtime-store-DmzboN-H.js.map → realtime-store-BpQsQYhl.js.map} +1 -1
- package/dist/{sol-utils-oBMCMbe_.js → sol-utils-Cwxyl2h6.js} +2 -13
- package/dist/sol-utils-Cwxyl2h6.js.map +1 -0
- package/dist/{sol-utils-BhjxvQUj.mjs → sol-utils-DVjPeoMy.mjs} +2 -13
- package/dist/sol-utils-DVjPeoMy.mjs.map +1 -0
- package/dist/{solana-message-XxVgbtp2.mjs → solana-message-C2_VvNX7.mjs} +2 -2
- package/dist/{solana-message-XxVgbtp2.mjs.map → solana-message-C2_VvNX7.mjs.map} +1 -1
- package/dist/{solana-message-I9w1TbAj.js → solana-message-DKOPPJwk.js} +2 -2
- package/dist/{solana-message-I9w1TbAj.js.map → solana-message-DKOPPJwk.js.map} +1 -1
- package/dist/{solana-operations-BUJkyVxN.js → solana-operations-BOKHbEuy.js} +18 -6
- package/dist/solana-operations-BOKHbEuy.js.map +1 -0
- package/dist/{solana-operations-ehEsfO5g.mjs → solana-operations-CuYFYlyN.mjs} +18 -6
- package/dist/solana-operations-CuYFYlyN.mjs.map +1 -0
- package/dist/utils/server-session-manager.d.ts +1 -0
- package/dist/utils/session-binding.d.ts +17 -0
- package/dist/utils/sol/{poof4b5pk1L9tmThvBmaABjcyjfhFGbMbQP5BXk2QZpDevnet-program.d.ts → openTv7fbpYSseNHYmCZFZ1CZgj4r8D9fKNgEz1qo6FDevnet-program.d.ts} +97 -113
- package/dist/utils/sol/{poof4b5pk1L9tmThvBmaABjcyjfhFGbMbQP5BXk2QZpMainnet-program.d.ts → openTv7fbpYSseNHYmCZFZ1CZgj4r8D9fKNgEz1qo6FMainnet-program.d.ts} +97 -113
- package/package.json +3 -2
- package/dist/index-BXvTkFw2.js.map +0 -1
- package/dist/index-qcTp-5Mt.mjs.map +0 -1
- package/dist/sol-utils-BhjxvQUj.mjs.map +0 -1
- package/dist/sol-utils-oBMCMbe_.js.map +0 -1
- package/dist/solana-operations-BUJkyVxN.js.map +0 -1
- package/dist/solana-operations-ehEsfO5g.mjs.map +0 -1
- package/dist/utils/sol/taro6CvKqwrYrDc16ufYgzQ2NZcyyVKStffbtudrhRuDevnet-program.d.ts +0 -1161
|
@@ -80,6 +80,16 @@ function getWebhookKeysUrl() {
|
|
|
80
80
|
return undefined;
|
|
81
81
|
return host.replace(/\/$/, '') + '/.well-known/bounded-webhook-keys.json';
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* The app id configured via `init({ appId })`, or `undefined` when none has been set.
|
|
85
|
+
* Synchronous + non-blocking on purpose (mirrors {@link getWebhookKeysUrl}): a pure webhook
|
|
86
|
+
* receiver may never call `init()`, and `getConfig()` would block on initialization. Used by
|
|
87
|
+
* `verifyWebhook` to bind a webhook to THIS app by default (#072) rather than trusting any
|
|
88
|
+
* app's shared-key-signed delivery.
|
|
89
|
+
*/
|
|
90
|
+
function getConfiguredAppId() {
|
|
91
|
+
return clientConfig.appId || undefined;
|
|
92
|
+
}
|
|
83
93
|
/**
|
|
84
94
|
* True when init() configured a Bounded network (the Cloudflare-native stack).
|
|
85
95
|
* Synchronous + non-blocking. Used to route reads/writes that behave differently
|
|
@@ -2793,19 +2803,28 @@ function resolveIssuerBase(issuer, cfg) {
|
|
|
2793
2803
|
return want;
|
|
2794
2804
|
return norm(cfg.authApiUrl);
|
|
2795
2805
|
}
|
|
2796
|
-
|
|
2806
|
+
// #062: key the auth client by its baseURL. The old single memoized client froze the FIRST
|
|
2807
|
+
// environment's authApiUrl, so after re-initializing the SDK to a different issuer (staging->
|
|
2808
|
+
// prod, or a custom issuer) the mint calls (/nonce, /auth/nonce, /session) kept hitting the
|
|
2809
|
+
// original address forever - a "works only after a full reload" login bug, and in a multi-tenant
|
|
2810
|
+
// process one config's address could leak into another's. refresh/revoke already resolve per
|
|
2811
|
+
// call via resolveIssuerBase; this makes minting do the same while still caching per URL.
|
|
2812
|
+
const axiosClientsByUrl = new Map();
|
|
2797
2813
|
async function getAxiosAuthClient() {
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2814
|
+
const config = await getConfig();
|
|
2815
|
+
const baseURL = config.authApiUrl || '';
|
|
2816
|
+
let client = axiosClientsByUrl.get(baseURL);
|
|
2817
|
+
if (!client) {
|
|
2818
|
+
client = axios.create({
|
|
2819
|
+
baseURL,
|
|
2802
2820
|
headers: {
|
|
2803
2821
|
'Content-Type': 'application/json',
|
|
2804
2822
|
},
|
|
2805
2823
|
timeout: 30000, // 30s timeout, matching makeApiRequest
|
|
2806
2824
|
});
|
|
2825
|
+
axiosClientsByUrl.set(baseURL, client);
|
|
2807
2826
|
}
|
|
2808
|
-
return
|
|
2827
|
+
return client;
|
|
2809
2828
|
}
|
|
2810
2829
|
async function genAuthNonce() {
|
|
2811
2830
|
const client = await getAxiosAuthClient();
|
|
@@ -2970,6 +2989,44 @@ async function revokeSession(refreshToken, issuer) {
|
|
|
2970
2989
|
async function signSessionCreateMessage(_signMessageFunction) {
|
|
2971
2990
|
}
|
|
2972
2991
|
|
|
2992
|
+
// Shared session-binding guard for the web + React Native session managers, so
|
|
2993
|
+
// the cross-principal refresh guard (#220 / #221) lives in ONE place and the two
|
|
2994
|
+
// managers can't drift apart. Base64url decoding differs by platform (browser
|
|
2995
|
+
// atob vs a configured decoder), so the caller passes its own decoder in.
|
|
2996
|
+
/**
|
|
2997
|
+
* Extract the `custom:walletAddress` claim from a JWT idToken. Returns undefined
|
|
2998
|
+
* when the token has no wallet claim or cannot be decoded.
|
|
2999
|
+
*/
|
|
3000
|
+
function idTokenWalletAddress(idToken, decodeBase64Url) {
|
|
3001
|
+
try {
|
|
3002
|
+
const payloadB64 = idToken.split(".")[1];
|
|
3003
|
+
if (!payloadB64)
|
|
3004
|
+
return undefined;
|
|
3005
|
+
const wallet = JSON.parse(decodeBase64Url(payloadB64))["custom:walletAddress"];
|
|
3006
|
+
return typeof wallet === "string" && wallet.length > 0 ? wallet : undefined;
|
|
3007
|
+
}
|
|
3008
|
+
catch (_a) {
|
|
3009
|
+
return undefined;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
/**
|
|
3013
|
+
* Whether a refreshed idToken may be grafted onto the CURRENTLY-stored session.
|
|
3014
|
+
*
|
|
3015
|
+
* A silent refresh started for user A can complete after storage was replaced
|
|
3016
|
+
* with user B (a login, guest→wallet upgrade, or a cross-tab login). Overwriting
|
|
3017
|
+
* B's tokens with A's — the app-id matches, so the old guard let it through —
|
|
3018
|
+
* installs a genuine-but-WRONG session: the UI shows B while the app authenticates
|
|
3019
|
+
* as A (#220 / #221). Refuse the write when the refreshed token binds to a
|
|
3020
|
+
* DIFFERENT wallet than the one currently stored. A tokenless/addressless session
|
|
3021
|
+
* (guest / opaque) preserves the prior behavior (returns true).
|
|
3022
|
+
*/
|
|
3023
|
+
function refreshedTokenMayReplaceStored(newIdToken, storedAddress, decodeBase64Url) {
|
|
3024
|
+
const tokenWallet = idTokenWalletAddress(newIdToken, decodeBase64Url);
|
|
3025
|
+
if (!tokenWallet || !storedAddress)
|
|
3026
|
+
return true;
|
|
3027
|
+
return tokenWallet === storedAddress;
|
|
3028
|
+
}
|
|
3029
|
+
|
|
2973
3030
|
class WebSessionManager {
|
|
2974
3031
|
/**
|
|
2975
3032
|
* Decode a base64url-encoded string (used by JWT payloads).
|
|
@@ -3126,16 +3183,27 @@ class WebSessionManager {
|
|
|
3126
3183
|
var _a;
|
|
3127
3184
|
if (typeof window === "undefined")
|
|
3128
3185
|
return;
|
|
3186
|
+
// Resolve config FIRST so the read → check → write below is one synchronous
|
|
3187
|
+
// turn (an atomic CAS): no `await` can let a concurrent login interleave a
|
|
3188
|
+
// different session between the read and the write (#221).
|
|
3189
|
+
const config = await getConfig();
|
|
3129
3190
|
const session = localStorage.getItem(this.BOUNDED_SESSION_STORAGE_KEY);
|
|
3130
3191
|
if (!session)
|
|
3131
3192
|
return;
|
|
3132
3193
|
const sessionObj = JSON.parse(session);
|
|
3133
3194
|
/* ---------- app-id guard ---------- */
|
|
3134
|
-
const config = await getConfig();
|
|
3135
3195
|
if (sessionObj.appId && sessionObj.appId !== config.appId) {
|
|
3136
3196
|
this.clearSession();
|
|
3137
3197
|
return;
|
|
3138
3198
|
}
|
|
3199
|
+
/* ---------- cross-principal guard (#221) ---------- */
|
|
3200
|
+
// Refuse to graft a refreshed token onto a DIFFERENT user's stored session
|
|
3201
|
+
// (a stale refresh completing after a login/upgrade replaced storage, incl.
|
|
3202
|
+
// a cross-tab login). Also re-runs the wallet-binding check storeSession does.
|
|
3203
|
+
if (!refreshedTokenMayReplaceStored(idToken, sessionObj.address, (s) => this.decodeBase64Url(s))) {
|
|
3204
|
+
console.warn("[WebSessionManager] updateIdTokenAndAccessToken: refreshed token wallet does not match the stored session address — discarding stale refresh");
|
|
3205
|
+
return;
|
|
3206
|
+
}
|
|
3139
3207
|
sessionObj.idToken = idToken;
|
|
3140
3208
|
sessionObj.accessToken = accessToken;
|
|
3141
3209
|
// Persist the rotated refresh token when the issuer returns a new one.
|
|
@@ -3344,16 +3412,27 @@ class ReactNativeSessionManager {
|
|
|
3344
3412
|
/* ------------------------------------------------------------------ */
|
|
3345
3413
|
static async updateIdTokenAndAccessToken(idToken, accessToken, refreshToken) {
|
|
3346
3414
|
var _a;
|
|
3415
|
+
// Resolve config FIRST so the read → check → write below is one synchronous
|
|
3416
|
+
// turn (an atomic CAS): no `await` can let a concurrent login interleave a
|
|
3417
|
+
// different session between the read and the write (#220).
|
|
3418
|
+
const config = await getConfig();
|
|
3347
3419
|
const session = this.getStorage().getItem(this.BOUNDED_SESSION_STORAGE_KEY);
|
|
3348
3420
|
if (!session)
|
|
3349
3421
|
return;
|
|
3350
3422
|
const sessionObj = JSON.parse(session);
|
|
3351
3423
|
/* ---------- app-id guard ---------- */
|
|
3352
|
-
const config = await getConfig();
|
|
3353
3424
|
if (sessionObj.appId && sessionObj.appId !== config.appId) {
|
|
3354
3425
|
this.clearSession();
|
|
3355
3426
|
return;
|
|
3356
3427
|
}
|
|
3428
|
+
/* ---------- cross-principal guard (#220) ---------- */
|
|
3429
|
+
// Refuse to graft a refreshed token onto a DIFFERENT user's stored session
|
|
3430
|
+
// (a stale refresh completing after a login/upgrade replaced storage). Also
|
|
3431
|
+
// re-runs the wallet-binding check storeSession does.
|
|
3432
|
+
if (!refreshedTokenMayReplaceStored(idToken, sessionObj.address, (s) => this.decodeBase64Url(s))) {
|
|
3433
|
+
console.warn("[ReactNativeSessionManager] updateIdTokenAndAccessToken: refreshed token wallet does not match the stored session address — discarding stale refresh");
|
|
3434
|
+
return;
|
|
3435
|
+
}
|
|
3357
3436
|
sessionObj.idToken = idToken;
|
|
3358
3437
|
sessionObj.accessToken = accessToken;
|
|
3359
3438
|
if (typeof refreshToken === "string" && refreshToken.length > 0) {
|
|
@@ -3408,6 +3487,12 @@ class ServerSessionManager {
|
|
|
3408
3487
|
this.session = null;
|
|
3409
3488
|
/* Coalesce concurrent getSession() calls into a single in-flight request */
|
|
3410
3489
|
this.pendingSession = null;
|
|
3490
|
+
/* Monotonic generation, bumped on clear/set. An in-flight createSession() only
|
|
3491
|
+
* commits its result to the cache if the generation still matches the value it
|
|
3492
|
+
* captured when it started — so a clearSession()/setSession() that happens WHILE
|
|
3493
|
+
* a create is in flight deterministically wins, and the late result can't
|
|
3494
|
+
* silently re-install a session the caller just cleared/replaced (#259). */
|
|
3495
|
+
this.generation = 0;
|
|
3411
3496
|
this.keypair = keypair !== null && keypair !== void 0 ? keypair : null;
|
|
3412
3497
|
this.sessionFactory = sessionFactory !== null && sessionFactory !== void 0 ? sessionFactory : null;
|
|
3413
3498
|
}
|
|
@@ -3449,7 +3534,7 @@ class ServerSessionManager {
|
|
|
3449
3534
|
const address = kp.publicKey.toBase58();
|
|
3450
3535
|
/* fetch nonce from auth API */
|
|
3451
3536
|
const nonce = await genAuthNonce();
|
|
3452
|
-
const { genSolanaMessage } = await import('./solana-message-
|
|
3537
|
+
const { genSolanaMessage } = await import('./solana-message-C2_VvNX7.mjs');
|
|
3453
3538
|
const message = await genSolanaMessage(address, nonce);
|
|
3454
3539
|
/* sign the message */
|
|
3455
3540
|
const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
|
|
@@ -3470,9 +3555,13 @@ class ServerSessionManager {
|
|
|
3470
3555
|
if (this.pendingSession !== null) {
|
|
3471
3556
|
return this.pendingSession;
|
|
3472
3557
|
}
|
|
3558
|
+
const startedGeneration = this.generation;
|
|
3473
3559
|
this.pendingSession = this.createSession()
|
|
3474
3560
|
.then((session) => {
|
|
3475
|
-
this
|
|
3561
|
+
// Only commit if no clear/set happened while this create was in flight.
|
|
3562
|
+
if (this.generation === startedGeneration) {
|
|
3563
|
+
this.session = session;
|
|
3564
|
+
}
|
|
3476
3565
|
return session;
|
|
3477
3566
|
})
|
|
3478
3567
|
.finally(() => {
|
|
@@ -3484,12 +3573,14 @@ class ServerSessionManager {
|
|
|
3484
3573
|
* STORE (overwrites the cached value)
|
|
3485
3574
|
* ---------------------------------------------- */
|
|
3486
3575
|
setSession(session) {
|
|
3576
|
+
this.generation++;
|
|
3487
3577
|
this.session = session;
|
|
3488
3578
|
}
|
|
3489
3579
|
/* ---------------------------------------------- *
|
|
3490
3580
|
* CLEAR (e.g. after explicit logout)
|
|
3491
3581
|
* ---------------------------------------------- */
|
|
3492
3582
|
clearSession() {
|
|
3583
|
+
this.generation++;
|
|
3493
3584
|
this.session = null;
|
|
3494
3585
|
this.pendingSession = null;
|
|
3495
3586
|
}
|
|
@@ -4023,8 +4114,27 @@ axiosRetry(apiClient, {
|
|
|
4023
4114
|
},
|
|
4024
4115
|
});
|
|
4025
4116
|
const refreshInFlight = new Map();
|
|
4117
|
+
// Short, bounded, non-secret fingerprint of a refresh token — used only to KEY the
|
|
4118
|
+
// in-flight de-dup map (never logged, never the raw secret).
|
|
4119
|
+
function refreshTokenFingerprint(token) {
|
|
4120
|
+
let h = 5381;
|
|
4121
|
+
for (let i = 0; i < token.length; i++)
|
|
4122
|
+
h = ((h << 5) + h + token.charCodeAt(i)) >>> 0;
|
|
4123
|
+
return h.toString(16);
|
|
4124
|
+
}
|
|
4026
4125
|
async function refreshAuthSessionOnce(appId, isServer) {
|
|
4027
|
-
|
|
4126
|
+
// Capture the CURRENT session's refresh token up front and key the in-flight
|
|
4127
|
+
// refresh by it, not just by the app. Keyed by app only, a refresh started for
|
|
4128
|
+
// user A would be reused for a request issued after switching to user B (guest→
|
|
4129
|
+
// wallet upgrade, logout/login), so B's 401 recovery would piggyback on A's
|
|
4130
|
+
// refresh (#112). Per-session keying keeps de-dup for the SAME session while
|
|
4131
|
+
// making a post-switch refresh a distinct entry. (The token WRITE is separately
|
|
4132
|
+
// guarded per-principal in the session managers, #220/#221.)
|
|
4133
|
+
const refreshToken = await getRefreshToken(isServer);
|
|
4134
|
+
if (!refreshToken) {
|
|
4135
|
+
return false;
|
|
4136
|
+
}
|
|
4137
|
+
const key = `${isServer ? "server" : "web"}:${appId}:${refreshTokenFingerprint(refreshToken)}`;
|
|
4028
4138
|
const existing = refreshInFlight.get(key);
|
|
4029
4139
|
if (existing) {
|
|
4030
4140
|
return existing;
|
|
@@ -4032,10 +4142,6 @@ async function refreshAuthSessionOnce(appId, isServer) {
|
|
|
4032
4142
|
const refreshPromise = (async () => {
|
|
4033
4143
|
var _a, _b;
|
|
4034
4144
|
try {
|
|
4035
|
-
const refreshToken = await getRefreshToken(isServer);
|
|
4036
|
-
if (!refreshToken) {
|
|
4037
|
-
return false;
|
|
4038
|
-
}
|
|
4039
4145
|
const refreshData = await refreshSession(refreshToken, getSessionIssuer(isServer));
|
|
4040
4146
|
if (!(refreshData === null || refreshData === void 0 ? void 0 : refreshData.idToken) || !(refreshData === null || refreshData === void 0 ? void 0 : refreshData.accessToken)) {
|
|
4041
4147
|
return false;
|
|
@@ -4076,9 +4182,21 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
|
4076
4182
|
? {}
|
|
4077
4183
|
: await createAuthHeader(config.isServer);
|
|
4078
4184
|
const headers = Object.assign({ "Content-Type": "application/json", "X-App-Id": config.appId }, authHeader);
|
|
4079
|
-
// Apply custom headers from _overrides
|
|
4185
|
+
// Apply custom headers from _overrides. #022/#023: HTTP header names are
|
|
4186
|
+
// case-insensitive, so normalize any override `authorization` (in any casing) onto the
|
|
4187
|
+
// single canonical `Authorization` key - never send two differently-cased Authorization
|
|
4188
|
+
// headers, and keep the credential the request sends identical to the one the read
|
|
4189
|
+
// cache key is derived from.
|
|
4080
4190
|
if (_overrides === null || _overrides === void 0 ? void 0 : _overrides.headers) {
|
|
4081
|
-
Object.
|
|
4191
|
+
for (const [name, value] of Object.entries(_overrides.headers)) {
|
|
4192
|
+
if (name.toLowerCase() === "authorization") {
|
|
4193
|
+
delete headers["authorization"];
|
|
4194
|
+
headers["Authorization"] = value;
|
|
4195
|
+
}
|
|
4196
|
+
else {
|
|
4197
|
+
headers[name] = value;
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4082
4200
|
}
|
|
4083
4201
|
// Writes (PUT/POST/DELETE) can take significantly longer than reads —
|
|
4084
4202
|
// server-side they may trigger ad-hoc LUT creation (create + extend +
|
|
@@ -5890,7 +6008,7 @@ async function doReconnectWithNewAuth() {
|
|
|
5890
6008
|
console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
|
|
5891
6009
|
}
|
|
5892
6010
|
try {
|
|
5893
|
-
const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-
|
|
6011
|
+
const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-BSuV-Vyr.mjs');
|
|
5894
6012
|
await reconnectRealtimeStoreWithNewAuth();
|
|
5895
6013
|
}
|
|
5896
6014
|
catch (error) {
|
|
@@ -6715,14 +6833,25 @@ function hashForKeyCached(value) {
|
|
|
6715
6833
|
principalHashCache.set(value, h);
|
|
6716
6834
|
return h;
|
|
6717
6835
|
}
|
|
6718
|
-
|
|
6836
|
+
// #022: HTTP header names are case-insensitive on the wire, so recognize `authorization`
|
|
6837
|
+
// in ANY casing when deriving the per-user cache key. Otherwise a caller-supplied
|
|
6838
|
+
// `AUTHORIZATION: <token B>` rides into the request while the cache key is computed from
|
|
6839
|
+
// the ambient token A - filing B's data under A's identity. The cache key must always be
|
|
6840
|
+
// derived from the same credential the request authenticates with.
|
|
6841
|
+
function authHeaderEntry(headers) {
|
|
6719
6842
|
if (!headers)
|
|
6720
|
-
return
|
|
6721
|
-
|
|
6722
|
-
|
|
6843
|
+
return undefined;
|
|
6844
|
+
for (const name of Object.keys(headers)) {
|
|
6845
|
+
if (name.toLowerCase() === 'authorization')
|
|
6846
|
+
return headers[name];
|
|
6847
|
+
}
|
|
6848
|
+
return undefined;
|
|
6849
|
+
}
|
|
6850
|
+
function hasAuthHeader(headers) {
|
|
6851
|
+
return authHeaderEntry(headers) !== undefined;
|
|
6723
6852
|
}
|
|
6724
6853
|
function authValueFromHeaders(headers) {
|
|
6725
|
-
return (headers
|
|
6854
|
+
return authHeaderEntry(headers) || '';
|
|
6726
6855
|
}
|
|
6727
6856
|
function principalFromAuthValue(authValue) {
|
|
6728
6857
|
return authValue ? `h${hashForKeyCached(authValue)}` : null;
|
|
@@ -7435,8 +7564,16 @@ async function runExpressionMany(many) {
|
|
|
7435
7564
|
let queries = [];
|
|
7436
7565
|
let overrides = undefined;
|
|
7437
7566
|
for (const { expression, queryArgs, returnType, _overrides } of many) {
|
|
7438
|
-
//
|
|
7439
|
-
|
|
7567
|
+
// All batched queries ride ONE request, so they must run under ONE credential.
|
|
7568
|
+
// Reject mixed per-entry _overrides (fail closed) instead of silently running
|
|
7569
|
+
// every query under the FIRST credential — that would evaluate a query meant
|
|
7570
|
+
// for user B as whoever came first (e.g. an admin), a cross-principal
|
|
7571
|
+
// authorization confusion the caller can't detect (#103). Split the batch by
|
|
7572
|
+
// credential to run under different identities.
|
|
7573
|
+
if (_overrides) {
|
|
7574
|
+
if (overrides && overrides !== _overrides) {
|
|
7575
|
+
throw new Error("[bounded-sdk] runExpressionMany: batched queries carry different _overrides; all queries in one batch share a single request and credential. Split the batch so each request uses one credential.");
|
|
7576
|
+
}
|
|
7440
7577
|
overrides = _overrides;
|
|
7441
7578
|
}
|
|
7442
7579
|
queries.push({ queryArgs, expression, returnType });
|
|
@@ -7578,11 +7715,11 @@ async function setMany(many, options) {
|
|
|
7578
7715
|
transactionResult = await handleEvmTransaction(curTx.evmTransaction, authProvider, options);
|
|
7579
7716
|
}
|
|
7580
7717
|
else if (curTx.serializedTransaction) {
|
|
7581
|
-
const { handlePreBuiltTransaction } = await import('./solana-operations-
|
|
7718
|
+
const { handlePreBuiltTransaction } = await import('./solana-operations-CuYFYlyN.mjs');
|
|
7582
7719
|
transactionResult = await handlePreBuiltTransaction(curTx, authProvider, options, expectedWritePaths);
|
|
7583
7720
|
}
|
|
7584
7721
|
else {
|
|
7585
|
-
const { handleSolanaTransaction } = await import('./solana-operations-
|
|
7722
|
+
const { handleSolanaTransaction } = await import('./solana-operations-CuYFYlyN.mjs');
|
|
7586
7723
|
transactionResult = await handleSolanaTransaction(curTx, authProvider, options, expectedWritePaths);
|
|
7587
7724
|
}
|
|
7588
7725
|
// Sync items after all transactions are confirmed
|
|
@@ -8108,17 +8245,25 @@ function isQueryLevelError(error) {
|
|
|
8108
8245
|
return (error === null || error === void 0 ? void 0 : error.isQueryError) === true || (error === null || error === void 0 ? void 0 : error.isBoundedDecline) === true || (error === null || error === void 0 ? void 0 : error.decline) != null;
|
|
8109
8246
|
}
|
|
8110
8247
|
/**
|
|
8111
|
-
* A RECOGNIZED transient transport failure that a retry might clear (HTTP
|
|
8112
|
-
*
|
|
8113
|
-
*
|
|
8114
|
-
* a retry won't fix) is NOT transient, so it gets surfaced once to onError
|
|
8115
|
-
* silently-dead subscription.
|
|
8248
|
+
* A RECOGNIZED transient transport failure that a retry might clear (HTTP 408/429/5xx, a
|
|
8249
|
+
* network/abort error). These back off SILENTLY - surfacing them per-poll would rebuild the
|
|
8250
|
+
* 429 noise. Anything else (a bad path 404, a permission/config 401/403, a malformed request
|
|
8251
|
+
* 400 - things a retry won't fix) is NOT transient, so it gets surfaced once to onError
|
|
8252
|
+
* instead of a silently-dead subscription.
|
|
8116
8253
|
*/
|
|
8117
8254
|
function isTransientTransportError(error) {
|
|
8118
8255
|
if (error == null)
|
|
8119
8256
|
return true; // unknown - be conservative and keep retrying
|
|
8120
|
-
|
|
8121
|
-
|
|
8257
|
+
// #060: classify by the actual status, not merely by "has a status". Almost every HTTP
|
|
8258
|
+
// failure carries a status, including the TERMINAL ones (400/401/403/404); the old
|
|
8259
|
+
// `typeof status === 'number'` check lumped them all into "transient" and retried them
|
|
8260
|
+
// forever with no onError - the exact silently-dead subscription this is meant to avoid.
|
|
8261
|
+
const status = typeof error.status === 'number' ? error.status
|
|
8262
|
+
: typeof error.statusCode === 'number' ? error.statusCode
|
|
8263
|
+
: undefined;
|
|
8264
|
+
if (status !== undefined) {
|
|
8265
|
+
return status === 408 || status === 429 || status >= 500;
|
|
8266
|
+
}
|
|
8122
8267
|
const code = error.code;
|
|
8123
8268
|
if (code === 'ECONNABORTED' || code === 'ERR_NETWORK' || code === 'ETIMEDOUT' || code === 'ERR_CANCELED')
|
|
8124
8269
|
return true;
|
|
@@ -8668,12 +8813,12 @@ function toMillis(seconds) {
|
|
|
8668
8813
|
|
|
8669
8814
|
/** Load the Anchor/Web3 transaction builder only when an onchain write uses it. */
|
|
8670
8815
|
async function buildSetDocumentsTransaction(connection, idl, programProvider, payerPublicKey, args, remainingAccounts, lutKey, preInstructions, simulate, additionalLutAddresses) {
|
|
8671
|
-
const implementation = await import('./sol-utils-
|
|
8816
|
+
const implementation = await import('./sol-utils-DVjPeoMy.mjs');
|
|
8672
8817
|
return implementation.buildSetDocumentsTransaction(connection, idl, programProvider, payerPublicKey, args, remainingAccounts, lutKey, preInstructions, simulate, additionalLutAddresses);
|
|
8673
8818
|
}
|
|
8674
8819
|
/** SIWS message creation is async already, so its onchain module can be lazy. */
|
|
8675
8820
|
async function genSolanaMessage(address, nonce) {
|
|
8676
|
-
const implementation = await import('./solana-message-
|
|
8821
|
+
const implementation = await import('./solana-message-C2_VvNX7.mjs');
|
|
8677
8822
|
return implementation.genSolanaMessage(address, nonce);
|
|
8678
8823
|
}
|
|
8679
8824
|
const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
@@ -9064,6 +9209,13 @@ async function intent(roomPath, intent, opts = {}) {
|
|
|
9064
9209
|
* This is diagnostic/ops surface. It reports whether the room facet exists,
|
|
9065
9210
|
* whether it is currently ticking, why it last stopped, which module etag is
|
|
9066
9211
|
* loaded, and the current generation used after terminal restarts.
|
|
9212
|
+
*
|
|
9213
|
+
* Auth: status attaches the caller's session token WHEN ONE EXISTS (mirroring
|
|
9214
|
+
* `intent`), but does NOT require it. The server gates the DETAILED body on the
|
|
9215
|
+
* room's read rule — an authenticated caller who can read the room gets full
|
|
9216
|
+
* telemetry; an anonymous / unauthorized caller gets only the slim liveness
|
|
9217
|
+
* shape `{available, started, module}` (#102). The caller's own `opts.headers`
|
|
9218
|
+
* may never smuggle in Authorization: only the derived session token sets it.
|
|
9067
9219
|
*/
|
|
9068
9220
|
async function status(roomPath, opts = {}) {
|
|
9069
9221
|
var _a, _b, _c, _d, _e;
|
|
@@ -9073,7 +9225,10 @@ async function status(roomPath, opts = {}) {
|
|
|
9073
9225
|
const normalizedRoomPath = roomPath.replace(/\/$/, '');
|
|
9074
9226
|
const config = await getConfig();
|
|
9075
9227
|
const base = realtimeHttpBase(config.wsApiUrl);
|
|
9076
|
-
const
|
|
9228
|
+
const authHeader = await liveAuthHeader(config.isServer, opts._overrides);
|
|
9229
|
+
const extraHeaders = withoutAuthorization(opts.headers);
|
|
9230
|
+
const overrideHeaders = withoutAuthorization((_a = opts._overrides) === null || _a === void 0 ? void 0 : _a.headers);
|
|
9231
|
+
const headers = Object.assign(Object.assign(Object.assign({ 'X-App-Id': config.appId }, (overrideHeaders !== null && overrideHeaders !== void 0 ? overrideHeaders : {})), (extraHeaders !== null && extraHeaders !== void 0 ? extraHeaders : {})), (hasAuthorization(authHeader) ? authHeader : {}));
|
|
9077
9232
|
const controller = new AbortController();
|
|
9078
9233
|
const timeoutMs = (_b = opts.timeoutMs) !== null && _b !== void 0 ? _b : 15000;
|
|
9079
9234
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -9205,5 +9360,5 @@ function defineLiveModule(mod) {
|
|
|
9205
9360
|
return mod;
|
|
9206
9361
|
}
|
|
9207
9362
|
|
|
9208
|
-
export {
|
|
9209
|
-
//# sourceMappingURL=index-
|
|
9363
|
+
export { getActiveSessionManager as $, getCachedData as A, reconnectWithNewAuth as B, createClient as C, subscribeQuery as D, closeAllQuerySubscriptions as E, canonicalizeArgs as F, canonicalizeResult as G, DEFAULT_QUERY_POLL_INTERVAL_MS as H, InsufficientBalanceError as I, BoundedDeclineError as J, deriveDecline as K, dispatchDeclineEvent as L, MIN_QUERY_POLL_INTERVAL_MS as M, DECLINE_EVENT_NAME as N, dispatchRateLimitEvent as O, hasActiveConnection as P, increment as Q, RATE_LIMIT_EVENT_NAME as R, ScopedClientReadOnlyError as S, serverTimestamp as T, now as U, toSeconds as V, toMillis as W, getIdToken as X, deriveUserIdentityFromIdToken as Y, WebSessionManager as Z, ReactNativeSessionManager as _, getWebhookKeysUrl as a, ServerSessionManager as a0, createSessionWithSignature as a1, refreshSession as a2, revokeSession as a3, signSessionCreateMessage as a4, genAuthNonce as a5, buildSetDocumentsTransaction as a6, convertRemainingAccounts as a7, genSolanaMessage as a8, genSiweMessage as a9, normalizeEvmAddress as aa, toLowerEvmAddress as ab, assertCanonicalBoundedRuntimeWriteCalldata as ac, assertEvmTransactionMatchesWrite as ad, BOUNDED_RUNTIME_SET_DOCUMENTS_SELECTOR as ae, BOUNDED_RUNTIME_SET_DOCUMENTS_ATTESTED_SELECTOR as af, functions as ag, invoke as ah, FunctionInvokeError as ai, live as aj, intent as ak, status as al, subscribeView as am, LiveIntentError as an, withEffects as ao, isEffectResult as ap, defineLiveModule as aq, EFFECT_INTENT_ADDRESS as ar, bufferExports as b, getConfiguredAppId as c, get as d, getMany as e, docId as f, getConfig as g, setMany as h, init as i, setFile as j, getFiles as k, search as l, runQueryMany as m, runExpression as n, runExpressionMany as o, signMessage as p, queryAggregate as q, runQuery as r, set as s, signTransaction as t, signAndSubmitTransaction as u, count as v, aggregate as w, subscribe as x, closeAllSubscriptions as y, clearCache as z };
|
|
9364
|
+
//# sourceMappingURL=index-DYbzK8YT.mjs.map
|