@pooflabs/core 0.0.41 → 0.0.43
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 +10 -0
- package/dist/client/operations.d.ts +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +460 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +446 -20
- package/dist/index.mjs.map +1 -1
- package/dist/utils/api.d.ts +1 -0
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,261 @@ import { Program } from '@coral-xyz/anchor';
|
|
|
6
6
|
import BN from 'bn.js';
|
|
7
7
|
import ReconnectingWebSocket from 'reconnecting-websocket';
|
|
8
8
|
|
|
9
|
+
function getDefaultExportFromCjs (x) {
|
|
10
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
var isRetryAllowed$1;
|
|
14
|
+
var hasRequiredIsRetryAllowed;
|
|
15
|
+
|
|
16
|
+
function requireIsRetryAllowed () {
|
|
17
|
+
if (hasRequiredIsRetryAllowed) return isRetryAllowed$1;
|
|
18
|
+
hasRequiredIsRetryAllowed = 1;
|
|
19
|
+
|
|
20
|
+
const denyList = new Set([
|
|
21
|
+
'ENOTFOUND',
|
|
22
|
+
'ENETUNREACH',
|
|
23
|
+
|
|
24
|
+
// SSL errors from https://github.com/nodejs/node/blob/fc8e3e2cdc521978351de257030db0076d79e0ab/src/crypto/crypto_common.cc#L301-L328
|
|
25
|
+
'UNABLE_TO_GET_ISSUER_CERT',
|
|
26
|
+
'UNABLE_TO_GET_CRL',
|
|
27
|
+
'UNABLE_TO_DECRYPT_CERT_SIGNATURE',
|
|
28
|
+
'UNABLE_TO_DECRYPT_CRL_SIGNATURE',
|
|
29
|
+
'UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY',
|
|
30
|
+
'CERT_SIGNATURE_FAILURE',
|
|
31
|
+
'CRL_SIGNATURE_FAILURE',
|
|
32
|
+
'CERT_NOT_YET_VALID',
|
|
33
|
+
'CERT_HAS_EXPIRED',
|
|
34
|
+
'CRL_NOT_YET_VALID',
|
|
35
|
+
'CRL_HAS_EXPIRED',
|
|
36
|
+
'ERROR_IN_CERT_NOT_BEFORE_FIELD',
|
|
37
|
+
'ERROR_IN_CERT_NOT_AFTER_FIELD',
|
|
38
|
+
'ERROR_IN_CRL_LAST_UPDATE_FIELD',
|
|
39
|
+
'ERROR_IN_CRL_NEXT_UPDATE_FIELD',
|
|
40
|
+
'OUT_OF_MEM',
|
|
41
|
+
'DEPTH_ZERO_SELF_SIGNED_CERT',
|
|
42
|
+
'SELF_SIGNED_CERT_IN_CHAIN',
|
|
43
|
+
'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
|
|
44
|
+
'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
|
|
45
|
+
'CERT_CHAIN_TOO_LONG',
|
|
46
|
+
'CERT_REVOKED',
|
|
47
|
+
'INVALID_CA',
|
|
48
|
+
'PATH_LENGTH_EXCEEDED',
|
|
49
|
+
'INVALID_PURPOSE',
|
|
50
|
+
'CERT_UNTRUSTED',
|
|
51
|
+
'CERT_REJECTED',
|
|
52
|
+
'HOSTNAME_MISMATCH'
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
// TODO: Use `error?.code` when targeting Node.js 14
|
|
56
|
+
isRetryAllowed$1 = error => !denyList.has(error && error.code);
|
|
57
|
+
return isRetryAllowed$1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
var isRetryAllowedExports = requireIsRetryAllowed();
|
|
61
|
+
var isRetryAllowed = /*@__PURE__*/getDefaultExportFromCjs(isRetryAllowedExports);
|
|
62
|
+
|
|
63
|
+
const namespace = 'axios-retry';
|
|
64
|
+
function isNetworkError(error) {
|
|
65
|
+
const CODE_EXCLUDE_LIST = ['ERR_CANCELED', 'ECONNABORTED'];
|
|
66
|
+
if (error.response) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (!error.code) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// Prevents retrying timed out & cancelled requests
|
|
73
|
+
if (CODE_EXCLUDE_LIST.includes(error.code)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
// Prevents retrying unsafe errors
|
|
77
|
+
return isRetryAllowed(error);
|
|
78
|
+
}
|
|
79
|
+
const SAFE_HTTP_METHODS = ['get', 'head', 'options'];
|
|
80
|
+
const IDEMPOTENT_HTTP_METHODS = SAFE_HTTP_METHODS.concat(['put', 'delete']);
|
|
81
|
+
function isRetryableError(error) {
|
|
82
|
+
return (error.code !== 'ECONNABORTED' &&
|
|
83
|
+
(!error.response ||
|
|
84
|
+
error.response.status === 429 ||
|
|
85
|
+
(error.response.status >= 500 && error.response.status <= 599)));
|
|
86
|
+
}
|
|
87
|
+
function isSafeRequestError(error) {
|
|
88
|
+
if (!error.config?.method) {
|
|
89
|
+
// Cannot determine if the request can be retried
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return isRetryableError(error) && SAFE_HTTP_METHODS.indexOf(error.config.method) !== -1;
|
|
93
|
+
}
|
|
94
|
+
function isIdempotentRequestError(error) {
|
|
95
|
+
if (!error.config?.method) {
|
|
96
|
+
// Cannot determine if the request can be retried
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return isRetryableError(error) && IDEMPOTENT_HTTP_METHODS.indexOf(error.config.method) !== -1;
|
|
100
|
+
}
|
|
101
|
+
function isNetworkOrIdempotentRequestError(error) {
|
|
102
|
+
return isNetworkError(error) || isIdempotentRequestError(error);
|
|
103
|
+
}
|
|
104
|
+
function retryAfter(error = undefined) {
|
|
105
|
+
const retryAfterHeader = error?.response?.headers['retry-after'];
|
|
106
|
+
if (!retryAfterHeader) {
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
// if the retry after header is a number, convert it to milliseconds
|
|
110
|
+
let retryAfterMs = (Number(retryAfterHeader) || 0) * 1000;
|
|
111
|
+
// If the retry after header is a date, get the number of milliseconds until that date
|
|
112
|
+
if (retryAfterMs === 0) {
|
|
113
|
+
retryAfterMs = (new Date(retryAfterHeader).valueOf() || 0) - Date.now();
|
|
114
|
+
}
|
|
115
|
+
return Math.max(0, retryAfterMs);
|
|
116
|
+
}
|
|
117
|
+
function noDelay(_retryNumber = 0, error = undefined) {
|
|
118
|
+
return Math.max(0, retryAfter(error));
|
|
119
|
+
}
|
|
120
|
+
function exponentialDelay(retryNumber = 0, error = undefined, delayFactor = 100) {
|
|
121
|
+
const calculatedDelay = 2 ** retryNumber * delayFactor;
|
|
122
|
+
const delay = Math.max(calculatedDelay, retryAfter(error));
|
|
123
|
+
const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
|
|
124
|
+
return delay + randomSum;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Linear delay
|
|
128
|
+
* @param {number | undefined} delayFactor - delay factor in milliseconds (default: 100)
|
|
129
|
+
* @returns {function} (retryNumber: number, error: AxiosError | undefined) => number
|
|
130
|
+
*/
|
|
131
|
+
function linearDelay(delayFactor = 100) {
|
|
132
|
+
return (retryNumber = 0, error = undefined) => {
|
|
133
|
+
const delay = retryNumber * delayFactor;
|
|
134
|
+
return Math.max(delay, retryAfter(error));
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const DEFAULT_OPTIONS = {
|
|
138
|
+
retries: 3,
|
|
139
|
+
retryCondition: isNetworkOrIdempotentRequestError,
|
|
140
|
+
retryDelay: noDelay,
|
|
141
|
+
shouldResetTimeout: false,
|
|
142
|
+
onRetry: () => { },
|
|
143
|
+
onMaxRetryTimesExceeded: () => { },
|
|
144
|
+
validateResponse: null
|
|
145
|
+
};
|
|
146
|
+
function getRequestOptions(config, defaultOptions) {
|
|
147
|
+
return { ...DEFAULT_OPTIONS, ...defaultOptions, ...config[namespace] };
|
|
148
|
+
}
|
|
149
|
+
function setCurrentState(config, defaultOptions, resetLastRequestTime = false) {
|
|
150
|
+
const currentState = getRequestOptions(config, defaultOptions || {});
|
|
151
|
+
currentState.retryCount = currentState.retryCount || 0;
|
|
152
|
+
if (!currentState.lastRequestTime || resetLastRequestTime) {
|
|
153
|
+
currentState.lastRequestTime = Date.now();
|
|
154
|
+
}
|
|
155
|
+
config[namespace] = currentState;
|
|
156
|
+
return currentState;
|
|
157
|
+
}
|
|
158
|
+
function fixConfig(axiosInstance, config) {
|
|
159
|
+
// @ts-ignore
|
|
160
|
+
if (axiosInstance.defaults.agent === config.agent) {
|
|
161
|
+
// @ts-ignore
|
|
162
|
+
delete config.agent;
|
|
163
|
+
}
|
|
164
|
+
if (axiosInstance.defaults.httpAgent === config.httpAgent) {
|
|
165
|
+
delete config.httpAgent;
|
|
166
|
+
}
|
|
167
|
+
if (axiosInstance.defaults.httpsAgent === config.httpsAgent) {
|
|
168
|
+
delete config.httpsAgent;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function shouldRetry(currentState, error) {
|
|
172
|
+
const { retries, retryCondition } = currentState;
|
|
173
|
+
const shouldRetryOrPromise = (currentState.retryCount || 0) < retries && retryCondition(error);
|
|
174
|
+
// This could be a promise
|
|
175
|
+
if (typeof shouldRetryOrPromise === 'object') {
|
|
176
|
+
try {
|
|
177
|
+
const shouldRetryPromiseResult = await shouldRetryOrPromise;
|
|
178
|
+
// keep return true unless shouldRetryPromiseResult return false for compatibility
|
|
179
|
+
return shouldRetryPromiseResult !== false;
|
|
180
|
+
}
|
|
181
|
+
catch (_err) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return shouldRetryOrPromise;
|
|
186
|
+
}
|
|
187
|
+
async function handleRetry(axiosInstance, currentState, error, config) {
|
|
188
|
+
currentState.retryCount += 1;
|
|
189
|
+
const { retryDelay, shouldResetTimeout, onRetry } = currentState;
|
|
190
|
+
const delay = retryDelay(currentState.retryCount, error);
|
|
191
|
+
// Axios fails merging this configuration to the default configuration because it has an issue
|
|
192
|
+
// with circular structures: https://github.com/mzabriskie/axios/issues/370
|
|
193
|
+
fixConfig(axiosInstance, config);
|
|
194
|
+
if (!shouldResetTimeout && config.timeout && currentState.lastRequestTime) {
|
|
195
|
+
const lastRequestDuration = Date.now() - currentState.lastRequestTime;
|
|
196
|
+
const timeout = config.timeout - lastRequestDuration - delay;
|
|
197
|
+
if (timeout <= 0) {
|
|
198
|
+
return Promise.reject(error);
|
|
199
|
+
}
|
|
200
|
+
config.timeout = timeout;
|
|
201
|
+
}
|
|
202
|
+
config.transformRequest = [(data) => data];
|
|
203
|
+
await onRetry(currentState.retryCount, error, config);
|
|
204
|
+
if (config.signal?.aborted) {
|
|
205
|
+
return Promise.resolve(axiosInstance(config));
|
|
206
|
+
}
|
|
207
|
+
return new Promise((resolve) => {
|
|
208
|
+
const abortListener = () => {
|
|
209
|
+
clearTimeout(timeout);
|
|
210
|
+
resolve(axiosInstance(config));
|
|
211
|
+
};
|
|
212
|
+
const timeout = setTimeout(() => {
|
|
213
|
+
resolve(axiosInstance(config));
|
|
214
|
+
if (config.signal?.removeEventListener) {
|
|
215
|
+
config.signal.removeEventListener('abort', abortListener);
|
|
216
|
+
}
|
|
217
|
+
}, delay);
|
|
218
|
+
if (config.signal?.addEventListener) {
|
|
219
|
+
config.signal.addEventListener('abort', abortListener, { once: true });
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async function handleMaxRetryTimesExceeded(currentState, error) {
|
|
224
|
+
if (currentState.retryCount >= currentState.retries)
|
|
225
|
+
await currentState.onMaxRetryTimesExceeded(error, currentState.retryCount);
|
|
226
|
+
}
|
|
227
|
+
const axiosRetry = (axiosInstance, defaultOptions) => {
|
|
228
|
+
const requestInterceptorId = axiosInstance.interceptors.request.use((config) => {
|
|
229
|
+
setCurrentState(config, defaultOptions, true);
|
|
230
|
+
if (config[namespace]?.validateResponse) {
|
|
231
|
+
// by setting this, all HTTP responses will be go through the error interceptor first
|
|
232
|
+
config.validateStatus = () => false;
|
|
233
|
+
}
|
|
234
|
+
return config;
|
|
235
|
+
});
|
|
236
|
+
const responseInterceptorId = axiosInstance.interceptors.response.use(null, async (error) => {
|
|
237
|
+
const { config } = error;
|
|
238
|
+
// If we have no information to retry the request
|
|
239
|
+
if (!config) {
|
|
240
|
+
return Promise.reject(error);
|
|
241
|
+
}
|
|
242
|
+
const currentState = setCurrentState(config, defaultOptions);
|
|
243
|
+
if (error.response && currentState.validateResponse?.(error.response)) {
|
|
244
|
+
// no issue with response
|
|
245
|
+
return error.response;
|
|
246
|
+
}
|
|
247
|
+
if (await shouldRetry(currentState, error)) {
|
|
248
|
+
return handleRetry(axiosInstance, currentState, error, config);
|
|
249
|
+
}
|
|
250
|
+
await handleMaxRetryTimesExceeded(currentState, error);
|
|
251
|
+
return Promise.reject(error);
|
|
252
|
+
});
|
|
253
|
+
return { requestInterceptorId, responseInterceptorId };
|
|
254
|
+
};
|
|
255
|
+
// Compatibility with CommonJS
|
|
256
|
+
axiosRetry.isNetworkError = isNetworkError;
|
|
257
|
+
axiosRetry.isSafeRequestError = isSafeRequestError;
|
|
258
|
+
axiosRetry.isIdempotentRequestError = isIdempotentRequestError;
|
|
259
|
+
axiosRetry.isNetworkOrIdempotentRequestError = isNetworkOrIdempotentRequestError;
|
|
260
|
+
axiosRetry.exponentialDelay = exponentialDelay;
|
|
261
|
+
axiosRetry.linearDelay = linearDelay;
|
|
262
|
+
axiosRetry.isRetryableError = isRetryableError;
|
|
263
|
+
|
|
9
264
|
let axiosClient;
|
|
10
265
|
async function getAxiosAuthClient() {
|
|
11
266
|
if (!axiosClient) {
|
|
@@ -15,6 +270,7 @@ async function getAxiosAuthClient() {
|
|
|
15
270
|
headers: {
|
|
16
271
|
'Content-Type': 'application/json',
|
|
17
272
|
},
|
|
273
|
+
timeout: 30000, // 30s timeout, matching makeApiRequest
|
|
18
274
|
});
|
|
19
275
|
}
|
|
20
276
|
return axiosClient;
|
|
@@ -61,15 +317,41 @@ async function createSessionWithPrivy(authToken, address, privyIdToken) {
|
|
|
61
317
|
});
|
|
62
318
|
return response.data;
|
|
63
319
|
}
|
|
320
|
+
// Deduplicate concurrent refreshSession calls to prevent multiple code paths
|
|
321
|
+
// (WebSocket reconnection, periodic refresh, API 401 retry) from all hitting
|
|
322
|
+
// /session/refresh simultaneously.
|
|
323
|
+
// Note: module-level state is shared across requests in SSR/Node — acceptable
|
|
324
|
+
// since there is only one active session per server instance (same pattern as
|
|
325
|
+
// refreshInFlight in api.ts).
|
|
326
|
+
let refreshInFlight$1 = null;
|
|
327
|
+
let refreshInFlightToken = null;
|
|
64
328
|
async function refreshSession(refreshToken) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
329
|
+
if (refreshInFlight$1 && refreshInFlightToken === refreshToken) {
|
|
330
|
+
return refreshInFlight$1;
|
|
331
|
+
}
|
|
332
|
+
refreshInFlightToken = refreshToken;
|
|
333
|
+
let currentFlight;
|
|
334
|
+
currentFlight = refreshInFlight$1 = (async () => {
|
|
335
|
+
try {
|
|
336
|
+
const client = await getAxiosAuthClient();
|
|
337
|
+
const config = await getConfig();
|
|
338
|
+
const appId = config.appId;
|
|
339
|
+
const response = await client.post('/session/refresh', {
|
|
340
|
+
refreshToken,
|
|
341
|
+
appId
|
|
342
|
+
});
|
|
343
|
+
return response.data;
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
// Only clear if we're still the active in-flight request.
|
|
347
|
+
// A second call with a different token may have replaced us.
|
|
348
|
+
if (refreshInFlight$1 === currentFlight) {
|
|
349
|
+
refreshInFlight$1 = null;
|
|
350
|
+
refreshInFlightToken = null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
})();
|
|
354
|
+
return refreshInFlight$1;
|
|
73
355
|
}
|
|
74
356
|
async function signSessionCreateMessage(_signMessageFunction) {
|
|
75
357
|
}
|
|
@@ -207,14 +489,10 @@ class WebSessionManager {
|
|
|
207
489
|
WebSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
|
|
208
490
|
|
|
209
491
|
var webSessionManager = /*#__PURE__*/Object.freeze({
|
|
210
|
-
|
|
211
|
-
|
|
492
|
+
__proto__: null,
|
|
493
|
+
WebSessionManager: WebSessionManager
|
|
212
494
|
});
|
|
213
495
|
|
|
214
|
-
function getDefaultExportFromCjs (x) {
|
|
215
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
496
|
var buffer = {};
|
|
219
497
|
|
|
220
498
|
var base64Js = {};
|
|
@@ -3011,7 +3289,19 @@ async function buildSetDocumentsTransaction(connection, idl, anchorProvider, pay
|
|
|
3011
3289
|
}
|
|
3012
3290
|
}
|
|
3013
3291
|
else if (lutKey != null) {
|
|
3014
|
-
|
|
3292
|
+
// The LUT may have just been created server-side and the client's RPC node
|
|
3293
|
+
// may not have indexed it yet (load-balancer split). Retry with exponential
|
|
3294
|
+
// back-off; first retry at 100 ms almost always resolves it.
|
|
3295
|
+
let table = null;
|
|
3296
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
3297
|
+
const { value } = await connection.getAddressLookupTable(new PublicKey(lutKey));
|
|
3298
|
+
if (value) {
|
|
3299
|
+
table = value;
|
|
3300
|
+
break;
|
|
3301
|
+
}
|
|
3302
|
+
if (attempt < 4)
|
|
3303
|
+
await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt)));
|
|
3304
|
+
}
|
|
3015
3305
|
if (!table)
|
|
3016
3306
|
throw new Error('LUT not found after creation/extend');
|
|
3017
3307
|
lookupTables.push(table);
|
|
@@ -3146,8 +3436,8 @@ class ServerSessionManager {
|
|
|
3146
3436
|
ServerSessionManager.instance = new ServerSessionManager();
|
|
3147
3437
|
|
|
3148
3438
|
var serverSessionManager = /*#__PURE__*/Object.freeze({
|
|
3149
|
-
|
|
3150
|
-
|
|
3439
|
+
__proto__: null,
|
|
3440
|
+
ServerSessionManager: ServerSessionManager
|
|
3151
3441
|
});
|
|
3152
3442
|
|
|
3153
3443
|
/**
|
|
@@ -3246,6 +3536,23 @@ async function updateIdTokenAndAccessToken(idToken, accessToken, isServer = fals
|
|
|
3246
3536
|
await WebSessionManager.updateIdTokenAndAccessToken(idToken, accessToken);
|
|
3247
3537
|
}
|
|
3248
3538
|
|
|
3539
|
+
const apiClient = axios.create();
|
|
3540
|
+
axiosRetry(apiClient, {
|
|
3541
|
+
retries: 2,
|
|
3542
|
+
retryCondition: (error) => {
|
|
3543
|
+
var _a, _b;
|
|
3544
|
+
// Only retry GET requests on network errors (ECONNRESET, ETIMEDOUT, etc.)
|
|
3545
|
+
// Writes (POST/PUT) are not retried — the server may have processed
|
|
3546
|
+
// the request before the connection was reset.
|
|
3547
|
+
return axiosRetry.isNetworkError(error) && ((_b = (_a = error.config) === null || _a === void 0 ? void 0 : _a.method) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'get';
|
|
3548
|
+
},
|
|
3549
|
+
retryDelay: axiosRetry.exponentialDelay,
|
|
3550
|
+
shouldResetTimeout: true,
|
|
3551
|
+
onRetry: (retryCount, error, requestConfig) => {
|
|
3552
|
+
var _a;
|
|
3553
|
+
console.warn(`[tarobase-sdk] retry ${retryCount} for ${(_a = requestConfig.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()} ${requestConfig.url} — ${error.code || error.message}`);
|
|
3554
|
+
},
|
|
3555
|
+
});
|
|
3249
3556
|
const refreshInFlight = new Map();
|
|
3250
3557
|
async function refreshAuthSessionOnce(appId, isServer) {
|
|
3251
3558
|
const key = `${isServer ? "server" : "web"}:${appId}`;
|
|
@@ -3292,6 +3599,7 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
|
3292
3599
|
ServerSessionManager.instance.clearSession();
|
|
3293
3600
|
};
|
|
3294
3601
|
async function executeRequest() {
|
|
3602
|
+
var _a;
|
|
3295
3603
|
// When _getAuthHeaders is provided (wallet client), use it as the sole auth source.
|
|
3296
3604
|
// Otherwise use the global createAuthHeader (default path).
|
|
3297
3605
|
const authHeader = (_overrides === null || _overrides === void 0 ? void 0 : _overrides._getAuthHeaders)
|
|
@@ -3313,11 +3621,12 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
|
3313
3621
|
method,
|
|
3314
3622
|
url: `${config.apiUrl}${urlPath.startsWith("/") ? urlPath : `/${urlPath}`}`,
|
|
3315
3623
|
headers,
|
|
3624
|
+
timeout: (_a = _overrides === null || _overrides === void 0 ? void 0 : _overrides.timeout) !== null && _a !== void 0 ? _a : 30000,
|
|
3316
3625
|
};
|
|
3317
3626
|
if (method !== "GET" && method !== "get") {
|
|
3318
3627
|
requestConfig.data = data ? JSON.stringify(data) : {};
|
|
3319
3628
|
}
|
|
3320
|
-
const response = await
|
|
3629
|
+
const response = await apiClient(requestConfig);
|
|
3321
3630
|
return { data: response.data, status: response.status, headers: response.headers };
|
|
3322
3631
|
}
|
|
3323
3632
|
try {
|
|
@@ -3728,6 +4037,90 @@ function cleanupExpiredCache() {
|
|
|
3728
4037
|
});
|
|
3729
4038
|
lastCacheCleanup = now;
|
|
3730
4039
|
}
|
|
4040
|
+
async function getMany(paths, opts = {}) {
|
|
4041
|
+
if (paths.length === 0) {
|
|
4042
|
+
return [];
|
|
4043
|
+
}
|
|
4044
|
+
if (paths.length > 30) {
|
|
4045
|
+
throw new Error('Cannot fetch more than 30 documents at once');
|
|
4046
|
+
}
|
|
4047
|
+
const normalizedPaths = [];
|
|
4048
|
+
for (const path of paths) {
|
|
4049
|
+
let normalizedPath = path.startsWith("/") ? path.slice(1) : path;
|
|
4050
|
+
if (normalizedPath.endsWith("*") && normalizedPath.length > 1) {
|
|
4051
|
+
normalizedPath = normalizedPath.slice(0, -1);
|
|
4052
|
+
}
|
|
4053
|
+
if (!normalizedPath || normalizedPath.length === 0) {
|
|
4054
|
+
throw new Error(`Invalid path provided: ${path}`);
|
|
4055
|
+
}
|
|
4056
|
+
const pathIsDocument = normalizedPath.split("/").length % 2 === 0;
|
|
4057
|
+
if (!pathIsDocument) {
|
|
4058
|
+
throw new Error(`Path must point to a document (even number of segments): ${path}`);
|
|
4059
|
+
}
|
|
4060
|
+
normalizedPaths.push(normalizedPath);
|
|
4061
|
+
}
|
|
4062
|
+
const now = Date.now();
|
|
4063
|
+
const results = new Array(paths.length);
|
|
4064
|
+
const uncachedIndices = [];
|
|
4065
|
+
const uncachedPaths = [];
|
|
4066
|
+
for (let i = 0; i < normalizedPaths.length; i++) {
|
|
4067
|
+
const normalizedPath = normalizedPaths[i];
|
|
4068
|
+
const cacheKey = `${normalizedPath}:`;
|
|
4069
|
+
if (!opts.bypassCache && getCache[cacheKey] && now < getCache[cacheKey].expiresAt) {
|
|
4070
|
+
results[i] = { path: normalizedPath, data: getCache[cacheKey].data };
|
|
4071
|
+
}
|
|
4072
|
+
else {
|
|
4073
|
+
uncachedIndices.push(i);
|
|
4074
|
+
uncachedPaths.push(normalizedPath);
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
if (uncachedPaths.length > 0) {
|
|
4078
|
+
try {
|
|
4079
|
+
const response = await makeApiRequest('POST', 'items/batch', { paths: uncachedPaths }, opts._overrides);
|
|
4080
|
+
const serverResults = response.data.results;
|
|
4081
|
+
const serverResultsMap = new Map();
|
|
4082
|
+
for (const result of serverResults) {
|
|
4083
|
+
serverResultsMap.set(result.path, result);
|
|
4084
|
+
}
|
|
4085
|
+
for (let i = 0; i < uncachedIndices.length; i++) {
|
|
4086
|
+
const originalIndex = uncachedIndices[i];
|
|
4087
|
+
const normalizedPath = uncachedPaths[i];
|
|
4088
|
+
const serverResult = serverResultsMap.get(normalizedPath);
|
|
4089
|
+
if (serverResult) {
|
|
4090
|
+
results[originalIndex] = serverResult;
|
|
4091
|
+
if (!serverResult.error && !opts.bypassCache) {
|
|
4092
|
+
const cacheKey = `${normalizedPath}:`;
|
|
4093
|
+
getCache[cacheKey] = {
|
|
4094
|
+
data: serverResult.data,
|
|
4095
|
+
expiresAt: now + GET_CACHE_TTL
|
|
4096
|
+
};
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
else {
|
|
4100
|
+
results[originalIndex] = {
|
|
4101
|
+
path: normalizedPath,
|
|
4102
|
+
data: null,
|
|
4103
|
+
error: { code: 'NOT_FOUND', message: `No result returned for path ${normalizedPath}` }
|
|
4104
|
+
};
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
if (now - lastCacheCleanup > 5000) {
|
|
4108
|
+
cleanupExpiredCache();
|
|
4109
|
+
lastCacheCleanup = now;
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
catch (error) {
|
|
4113
|
+
for (const originalIndex of uncachedIndices) {
|
|
4114
|
+
results[originalIndex] = {
|
|
4115
|
+
path: normalizedPaths[originalIndex],
|
|
4116
|
+
data: null,
|
|
4117
|
+
error: { code: 'NOT_FOUND', message: error instanceof Error ? error.message : 'Unknown error' }
|
|
4118
|
+
};
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
return results;
|
|
4123
|
+
}
|
|
3731
4124
|
async function runQuery(absolutePath, queryName, queryArgs, opts) {
|
|
3732
4125
|
const result = await runQueryMany([{ absolutePath, queryName, queryArgs }], opts);
|
|
3733
4126
|
return result[0];
|
|
@@ -4169,6 +4562,7 @@ function scheduleTokenRefresh(connection, isServer) {
|
|
|
4169
4562
|
// This replaces the old single setTimeout approach which was unreliable for long
|
|
4170
4563
|
// delays (browsers throttle/suspend timers in background tabs).
|
|
4171
4564
|
connection.tokenRefreshTimer = setInterval(async () => {
|
|
4565
|
+
var _a, _b;
|
|
4172
4566
|
try {
|
|
4173
4567
|
const currentToken = await getIdToken(isServer);
|
|
4174
4568
|
if (!currentToken)
|
|
@@ -4199,6 +4593,17 @@ function scheduleTokenRefresh(connection, isServer) {
|
|
|
4199
4593
|
}
|
|
4200
4594
|
}
|
|
4201
4595
|
catch (refreshError) {
|
|
4596
|
+
// If the refresh token itself is invalid (401/403), stop retrying —
|
|
4597
|
+
// the token won't magically become valid next interval, and continuing
|
|
4598
|
+
// to hammer /session/refresh causes 429 rate-limit storms.
|
|
4599
|
+
if (((_a = refreshError === null || refreshError === void 0 ? void 0 : refreshError.response) === null || _a === void 0 ? void 0 : _a.status) === 401 || ((_b = refreshError === null || refreshError === void 0 ? void 0 : refreshError.response) === null || _b === void 0 ? void 0 : _b.status) === 403) {
|
|
4600
|
+
console.warn('[WS v2] Refresh token rejected (401/403), stopping periodic refresh');
|
|
4601
|
+
if (connection.tokenRefreshTimer) {
|
|
4602
|
+
clearInterval(connection.tokenRefreshTimer);
|
|
4603
|
+
connection.tokenRefreshTimer = null;
|
|
4604
|
+
}
|
|
4605
|
+
return;
|
|
4606
|
+
}
|
|
4202
4607
|
console.warn('[WS v2] Proactive token refresh failed, will retry next interval:', refreshError);
|
|
4203
4608
|
}
|
|
4204
4609
|
}
|
|
@@ -4209,6 +4614,7 @@ function scheduleTokenRefresh(connection, isServer) {
|
|
|
4209
4614
|
}, TOKEN_CHECK_INTERVAL);
|
|
4210
4615
|
}
|
|
4211
4616
|
async function getFreshAuthToken(isServer) {
|
|
4617
|
+
var _a, _b;
|
|
4212
4618
|
const currentToken = await getIdToken(isServer);
|
|
4213
4619
|
if (!currentToken) {
|
|
4214
4620
|
return null;
|
|
@@ -4231,6 +4637,17 @@ async function getFreshAuthToken(isServer) {
|
|
|
4231
4637
|
}
|
|
4232
4638
|
catch (error) {
|
|
4233
4639
|
console.error('[WS v2] Error refreshing token:', error);
|
|
4640
|
+
// If the refresh token is permanently invalid (401/403), clear the stale
|
|
4641
|
+
// session from storage so future attempts don't keep retrying with it.
|
|
4642
|
+
if (!isServer && (((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) === 401 || ((_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.status) === 403)) {
|
|
4643
|
+
try {
|
|
4644
|
+
const { WebSessionManager } = await Promise.resolve().then(function () { return webSessionManager; });
|
|
4645
|
+
WebSessionManager.clearSession();
|
|
4646
|
+
}
|
|
4647
|
+
catch (clearError) {
|
|
4648
|
+
console.warn('[WS v2] Failed to clear stale session:', clearError);
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4234
4651
|
}
|
|
4235
4652
|
// Return null instead of the expired token to prevent infinite 401 reconnect storms.
|
|
4236
4653
|
// The server accepts unauthenticated connections; auth-required subscriptions will
|
|
@@ -4351,7 +4768,16 @@ async function getOrCreateConnection(appId, isServer) {
|
|
|
4351
4768
|
ws.addEventListener('open', () => {
|
|
4352
4769
|
connection.isConnecting = false;
|
|
4353
4770
|
connection.isConnected = true;
|
|
4354
|
-
|
|
4771
|
+
// NOTE: Do NOT reset consecutiveAuthFailures here. It is reset when a
|
|
4772
|
+
// fresh auth token is actually obtained (in urlProvider, line ~389) or on
|
|
4773
|
+
// an explicit auth change (reconnectWithNewAuthV2, line ~854). Resetting
|
|
4774
|
+
// on every 'open' event created an infinite loop: auth fails 5x → connect
|
|
4775
|
+
// without auth → open resets counter → disconnect → auth fails 5x again →
|
|
4776
|
+
// repeat forever, hammering /session/refresh and causing 429s.
|
|
4777
|
+
//
|
|
4778
|
+
// An elevated counter is safe for anonymous/guest sessions: when there's no
|
|
4779
|
+
// token at all (getIdToken returns null), the counter is never checked —
|
|
4780
|
+
// urlProvider skips straight to unauthenticated connection.
|
|
4355
4781
|
// Schedule periodic token freshness checks
|
|
4356
4782
|
scheduleTokenRefresh(connection, isServer);
|
|
4357
4783
|
// Re-subscribe to all existing subscriptions after reconnect
|
|
@@ -4968,5 +5394,5 @@ class ReactNativeSessionManager {
|
|
|
4968
5394
|
}
|
|
4969
5395
|
ReactNativeSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
|
|
4970
5396
|
|
|
4971
|
-
export { InsufficientBalanceError, ReactNativeSessionManager, ServerSessionManager, WebSessionManager, aggregate, buildSetDocumentsTransaction, clearCache, closeAllSubscriptions, convertRemainingAccounts, count, createSessionWithPrivy, createSessionWithSignature, genAuthNonce, genSolanaMessage, get, getCachedData, getConfig, getFiles, getIdToken, init, reconnectWithNewAuth, refreshSession, runExpression, runExpressionMany, runQuery, runQueryMany, set, setFile, setMany, signAndSubmitTransaction, signMessage, signSessionCreateMessage, signTransaction, subscribe };
|
|
5397
|
+
export { InsufficientBalanceError, ReactNativeSessionManager, ServerSessionManager, WebSessionManager, aggregate, buildSetDocumentsTransaction, clearCache, closeAllSubscriptions, convertRemainingAccounts, count, createSessionWithPrivy, createSessionWithSignature, genAuthNonce, genSolanaMessage, get, getCachedData, getConfig, getFiles, getIdToken, getMany, init, reconnectWithNewAuth, refreshSession, runExpression, runExpressionMany, runQuery, runQueryMany, set, setFile, setMany, signAndSubmitTransaction, signMessage, signSessionCreateMessage, signTransaction, subscribe };
|
|
4972
5398
|
//# sourceMappingURL=index.mjs.map
|