@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/README.md
CHANGED
|
@@ -39,6 +39,15 @@ export interface User {
|
|
|
39
39
|
address: string;
|
|
40
40
|
provider: AuthProvider;
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
export interface GetManyResult {
|
|
44
|
+
path: string;
|
|
45
|
+
data: any | null;
|
|
46
|
+
error?: {
|
|
47
|
+
code: 'NOT_FOUND' | 'UNAUTHORIZED' | 'INVALID_PATH';
|
|
48
|
+
message: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
42
51
|
```
|
|
43
52
|
|
|
44
53
|
### Core Operations
|
|
@@ -52,6 +61,7 @@ function getConfig(): Promise<ClientConfig>;
|
|
|
52
61
|
|
|
53
62
|
// Data operations
|
|
54
63
|
function get(path: string): Promise<any>;
|
|
64
|
+
function getMany(paths: string[], options?: { bypassCache?: boolean }): Promise<GetManyResult[]>;
|
|
55
65
|
function set(path: string, data: any, options?: SetOptions): Promise<any>;
|
|
56
66
|
function setMany(paths: { [key: string]: any }, options?: SetOptions): Promise<any>;
|
|
57
67
|
function setFile(path: string, file: File, metadata?: any): Promise<any>;
|
|
@@ -7,6 +7,7 @@ export type RequestOverrides = {
|
|
|
7
7
|
_getAuthHeaders?: () => Promise<Record<string, string>>;
|
|
8
8
|
_clearAuth?: () => Promise<void>;
|
|
9
9
|
_walletAddress?: string;
|
|
10
|
+
timeout?: number;
|
|
10
11
|
};
|
|
11
12
|
export type SetOptions = {
|
|
12
13
|
shouldSubmitTx?: boolean;
|
|
@@ -113,6 +114,20 @@ export declare function count(path: string, opts?: CountOptions): Promise<Aggreg
|
|
|
113
114
|
*/
|
|
114
115
|
export declare function aggregate(path: string, operation: AggregateOperation, opts?: AggregateOptions): Promise<AggregateResult>;
|
|
115
116
|
export declare function get(path: string, opts?: GetOptions): Promise<any>;
|
|
117
|
+
export type GetManyResult = {
|
|
118
|
+
path: string;
|
|
119
|
+
data: any | null;
|
|
120
|
+
error?: {
|
|
121
|
+
code: 'NOT_FOUND' | 'UNAUTHORIZED' | 'INVALID_PATH';
|
|
122
|
+
message: string;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
export declare function getMany(paths: string[], opts?: {
|
|
126
|
+
bypassCache?: boolean;
|
|
127
|
+
_overrides?: {
|
|
128
|
+
headers?: Record<string, string>;
|
|
129
|
+
};
|
|
130
|
+
}): Promise<GetManyResult[]>;
|
|
116
131
|
export type RunExpressionOptions = {
|
|
117
132
|
returnType?: 'Bool' | 'String' | 'Int' | 'UInt';
|
|
118
133
|
_overrides?: RequestOverrides;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { init } from './client/config';
|
|
2
2
|
export { getConfig, ClientConfig } from './client/config';
|
|
3
|
-
export { get, set, setMany, setFile, getFiles, runQuery, runQueryMany, runExpression, runExpressionMany, signMessage, signTransaction, signAndSubmitTransaction, count, aggregate, RequestOverrides, SetOptions, GetOptions, RunQueryOptions, CountOptions, AggregateOptions, AggregateOperation, AggregateResult, RunExpressionOptions, RunExpressionResult, InsufficientBalanceError } from './client/operations';
|
|
3
|
+
export { get, getMany, set, setMany, setFile, getFiles, runQuery, runQueryMany, runExpression, runExpressionMany, signMessage, signTransaction, signAndSubmitTransaction, count, aggregate, RequestOverrides, SetOptions, GetOptions, RunQueryOptions, CountOptions, AggregateOptions, AggregateOperation, AggregateResult, RunExpressionOptions, RunExpressionResult, InsufficientBalanceError, GetManyResult } from './client/operations';
|
|
4
4
|
export { subscribe, closeAllSubscriptions, clearCache, getCachedData, reconnectWithNewAuth } from './client/subscription';
|
|
5
5
|
export * from './types';
|
|
6
6
|
export { getIdToken } from './utils/utils';
|
package/dist/index.js
CHANGED
|
@@ -8,24 +8,279 @@ var BN = require('bn.js');
|
|
|
8
8
|
var ReconnectingWebSocket = require('reconnecting-websocket');
|
|
9
9
|
|
|
10
10
|
function _interopNamespaceDefault(e) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
var anchor__namespace = /*#__PURE__*/_interopNamespaceDefault(anchor);
|
|
28
28
|
|
|
29
|
+
function getDefaultExportFromCjs (x) {
|
|
30
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var isRetryAllowed$1;
|
|
34
|
+
var hasRequiredIsRetryAllowed;
|
|
35
|
+
|
|
36
|
+
function requireIsRetryAllowed () {
|
|
37
|
+
if (hasRequiredIsRetryAllowed) return isRetryAllowed$1;
|
|
38
|
+
hasRequiredIsRetryAllowed = 1;
|
|
39
|
+
|
|
40
|
+
const denyList = new Set([
|
|
41
|
+
'ENOTFOUND',
|
|
42
|
+
'ENETUNREACH',
|
|
43
|
+
|
|
44
|
+
// SSL errors from https://github.com/nodejs/node/blob/fc8e3e2cdc521978351de257030db0076d79e0ab/src/crypto/crypto_common.cc#L301-L328
|
|
45
|
+
'UNABLE_TO_GET_ISSUER_CERT',
|
|
46
|
+
'UNABLE_TO_GET_CRL',
|
|
47
|
+
'UNABLE_TO_DECRYPT_CERT_SIGNATURE',
|
|
48
|
+
'UNABLE_TO_DECRYPT_CRL_SIGNATURE',
|
|
49
|
+
'UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY',
|
|
50
|
+
'CERT_SIGNATURE_FAILURE',
|
|
51
|
+
'CRL_SIGNATURE_FAILURE',
|
|
52
|
+
'CERT_NOT_YET_VALID',
|
|
53
|
+
'CERT_HAS_EXPIRED',
|
|
54
|
+
'CRL_NOT_YET_VALID',
|
|
55
|
+
'CRL_HAS_EXPIRED',
|
|
56
|
+
'ERROR_IN_CERT_NOT_BEFORE_FIELD',
|
|
57
|
+
'ERROR_IN_CERT_NOT_AFTER_FIELD',
|
|
58
|
+
'ERROR_IN_CRL_LAST_UPDATE_FIELD',
|
|
59
|
+
'ERROR_IN_CRL_NEXT_UPDATE_FIELD',
|
|
60
|
+
'OUT_OF_MEM',
|
|
61
|
+
'DEPTH_ZERO_SELF_SIGNED_CERT',
|
|
62
|
+
'SELF_SIGNED_CERT_IN_CHAIN',
|
|
63
|
+
'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
|
|
64
|
+
'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
|
|
65
|
+
'CERT_CHAIN_TOO_LONG',
|
|
66
|
+
'CERT_REVOKED',
|
|
67
|
+
'INVALID_CA',
|
|
68
|
+
'PATH_LENGTH_EXCEEDED',
|
|
69
|
+
'INVALID_PURPOSE',
|
|
70
|
+
'CERT_UNTRUSTED',
|
|
71
|
+
'CERT_REJECTED',
|
|
72
|
+
'HOSTNAME_MISMATCH'
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
// TODO: Use `error?.code` when targeting Node.js 14
|
|
76
|
+
isRetryAllowed$1 = error => !denyList.has(error && error.code);
|
|
77
|
+
return isRetryAllowed$1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var isRetryAllowedExports = requireIsRetryAllowed();
|
|
81
|
+
var isRetryAllowed = /*@__PURE__*/getDefaultExportFromCjs(isRetryAllowedExports);
|
|
82
|
+
|
|
83
|
+
const namespace = 'axios-retry';
|
|
84
|
+
function isNetworkError(error) {
|
|
85
|
+
const CODE_EXCLUDE_LIST = ['ERR_CANCELED', 'ECONNABORTED'];
|
|
86
|
+
if (error.response) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (!error.code) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
// Prevents retrying timed out & cancelled requests
|
|
93
|
+
if (CODE_EXCLUDE_LIST.includes(error.code)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
// Prevents retrying unsafe errors
|
|
97
|
+
return isRetryAllowed(error);
|
|
98
|
+
}
|
|
99
|
+
const SAFE_HTTP_METHODS = ['get', 'head', 'options'];
|
|
100
|
+
const IDEMPOTENT_HTTP_METHODS = SAFE_HTTP_METHODS.concat(['put', 'delete']);
|
|
101
|
+
function isRetryableError(error) {
|
|
102
|
+
return (error.code !== 'ECONNABORTED' &&
|
|
103
|
+
(!error.response ||
|
|
104
|
+
error.response.status === 429 ||
|
|
105
|
+
(error.response.status >= 500 && error.response.status <= 599)));
|
|
106
|
+
}
|
|
107
|
+
function isSafeRequestError(error) {
|
|
108
|
+
if (!error.config?.method) {
|
|
109
|
+
// Cannot determine if the request can be retried
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return isRetryableError(error) && SAFE_HTTP_METHODS.indexOf(error.config.method) !== -1;
|
|
113
|
+
}
|
|
114
|
+
function isIdempotentRequestError(error) {
|
|
115
|
+
if (!error.config?.method) {
|
|
116
|
+
// Cannot determine if the request can be retried
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return isRetryableError(error) && IDEMPOTENT_HTTP_METHODS.indexOf(error.config.method) !== -1;
|
|
120
|
+
}
|
|
121
|
+
function isNetworkOrIdempotentRequestError(error) {
|
|
122
|
+
return isNetworkError(error) || isIdempotentRequestError(error);
|
|
123
|
+
}
|
|
124
|
+
function retryAfter(error = undefined) {
|
|
125
|
+
const retryAfterHeader = error?.response?.headers['retry-after'];
|
|
126
|
+
if (!retryAfterHeader) {
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
// if the retry after header is a number, convert it to milliseconds
|
|
130
|
+
let retryAfterMs = (Number(retryAfterHeader) || 0) * 1000;
|
|
131
|
+
// If the retry after header is a date, get the number of milliseconds until that date
|
|
132
|
+
if (retryAfterMs === 0) {
|
|
133
|
+
retryAfterMs = (new Date(retryAfterHeader).valueOf() || 0) - Date.now();
|
|
134
|
+
}
|
|
135
|
+
return Math.max(0, retryAfterMs);
|
|
136
|
+
}
|
|
137
|
+
function noDelay(_retryNumber = 0, error = undefined) {
|
|
138
|
+
return Math.max(0, retryAfter(error));
|
|
139
|
+
}
|
|
140
|
+
function exponentialDelay(retryNumber = 0, error = undefined, delayFactor = 100) {
|
|
141
|
+
const calculatedDelay = 2 ** retryNumber * delayFactor;
|
|
142
|
+
const delay = Math.max(calculatedDelay, retryAfter(error));
|
|
143
|
+
const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
|
|
144
|
+
return delay + randomSum;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Linear delay
|
|
148
|
+
* @param {number | undefined} delayFactor - delay factor in milliseconds (default: 100)
|
|
149
|
+
* @returns {function} (retryNumber: number, error: AxiosError | undefined) => number
|
|
150
|
+
*/
|
|
151
|
+
function linearDelay(delayFactor = 100) {
|
|
152
|
+
return (retryNumber = 0, error = undefined) => {
|
|
153
|
+
const delay = retryNumber * delayFactor;
|
|
154
|
+
return Math.max(delay, retryAfter(error));
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const DEFAULT_OPTIONS = {
|
|
158
|
+
retries: 3,
|
|
159
|
+
retryCondition: isNetworkOrIdempotentRequestError,
|
|
160
|
+
retryDelay: noDelay,
|
|
161
|
+
shouldResetTimeout: false,
|
|
162
|
+
onRetry: () => { },
|
|
163
|
+
onMaxRetryTimesExceeded: () => { },
|
|
164
|
+
validateResponse: null
|
|
165
|
+
};
|
|
166
|
+
function getRequestOptions(config, defaultOptions) {
|
|
167
|
+
return { ...DEFAULT_OPTIONS, ...defaultOptions, ...config[namespace] };
|
|
168
|
+
}
|
|
169
|
+
function setCurrentState(config, defaultOptions, resetLastRequestTime = false) {
|
|
170
|
+
const currentState = getRequestOptions(config, defaultOptions || {});
|
|
171
|
+
currentState.retryCount = currentState.retryCount || 0;
|
|
172
|
+
if (!currentState.lastRequestTime || resetLastRequestTime) {
|
|
173
|
+
currentState.lastRequestTime = Date.now();
|
|
174
|
+
}
|
|
175
|
+
config[namespace] = currentState;
|
|
176
|
+
return currentState;
|
|
177
|
+
}
|
|
178
|
+
function fixConfig(axiosInstance, config) {
|
|
179
|
+
// @ts-ignore
|
|
180
|
+
if (axiosInstance.defaults.agent === config.agent) {
|
|
181
|
+
// @ts-ignore
|
|
182
|
+
delete config.agent;
|
|
183
|
+
}
|
|
184
|
+
if (axiosInstance.defaults.httpAgent === config.httpAgent) {
|
|
185
|
+
delete config.httpAgent;
|
|
186
|
+
}
|
|
187
|
+
if (axiosInstance.defaults.httpsAgent === config.httpsAgent) {
|
|
188
|
+
delete config.httpsAgent;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async function shouldRetry(currentState, error) {
|
|
192
|
+
const { retries, retryCondition } = currentState;
|
|
193
|
+
const shouldRetryOrPromise = (currentState.retryCount || 0) < retries && retryCondition(error);
|
|
194
|
+
// This could be a promise
|
|
195
|
+
if (typeof shouldRetryOrPromise === 'object') {
|
|
196
|
+
try {
|
|
197
|
+
const shouldRetryPromiseResult = await shouldRetryOrPromise;
|
|
198
|
+
// keep return true unless shouldRetryPromiseResult return false for compatibility
|
|
199
|
+
return shouldRetryPromiseResult !== false;
|
|
200
|
+
}
|
|
201
|
+
catch (_err) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return shouldRetryOrPromise;
|
|
206
|
+
}
|
|
207
|
+
async function handleRetry(axiosInstance, currentState, error, config) {
|
|
208
|
+
currentState.retryCount += 1;
|
|
209
|
+
const { retryDelay, shouldResetTimeout, onRetry } = currentState;
|
|
210
|
+
const delay = retryDelay(currentState.retryCount, error);
|
|
211
|
+
// Axios fails merging this configuration to the default configuration because it has an issue
|
|
212
|
+
// with circular structures: https://github.com/mzabriskie/axios/issues/370
|
|
213
|
+
fixConfig(axiosInstance, config);
|
|
214
|
+
if (!shouldResetTimeout && config.timeout && currentState.lastRequestTime) {
|
|
215
|
+
const lastRequestDuration = Date.now() - currentState.lastRequestTime;
|
|
216
|
+
const timeout = config.timeout - lastRequestDuration - delay;
|
|
217
|
+
if (timeout <= 0) {
|
|
218
|
+
return Promise.reject(error);
|
|
219
|
+
}
|
|
220
|
+
config.timeout = timeout;
|
|
221
|
+
}
|
|
222
|
+
config.transformRequest = [(data) => data];
|
|
223
|
+
await onRetry(currentState.retryCount, error, config);
|
|
224
|
+
if (config.signal?.aborted) {
|
|
225
|
+
return Promise.resolve(axiosInstance(config));
|
|
226
|
+
}
|
|
227
|
+
return new Promise((resolve) => {
|
|
228
|
+
const abortListener = () => {
|
|
229
|
+
clearTimeout(timeout);
|
|
230
|
+
resolve(axiosInstance(config));
|
|
231
|
+
};
|
|
232
|
+
const timeout = setTimeout(() => {
|
|
233
|
+
resolve(axiosInstance(config));
|
|
234
|
+
if (config.signal?.removeEventListener) {
|
|
235
|
+
config.signal.removeEventListener('abort', abortListener);
|
|
236
|
+
}
|
|
237
|
+
}, delay);
|
|
238
|
+
if (config.signal?.addEventListener) {
|
|
239
|
+
config.signal.addEventListener('abort', abortListener, { once: true });
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async function handleMaxRetryTimesExceeded(currentState, error) {
|
|
244
|
+
if (currentState.retryCount >= currentState.retries)
|
|
245
|
+
await currentState.onMaxRetryTimesExceeded(error, currentState.retryCount);
|
|
246
|
+
}
|
|
247
|
+
const axiosRetry = (axiosInstance, defaultOptions) => {
|
|
248
|
+
const requestInterceptorId = axiosInstance.interceptors.request.use((config) => {
|
|
249
|
+
setCurrentState(config, defaultOptions, true);
|
|
250
|
+
if (config[namespace]?.validateResponse) {
|
|
251
|
+
// by setting this, all HTTP responses will be go through the error interceptor first
|
|
252
|
+
config.validateStatus = () => false;
|
|
253
|
+
}
|
|
254
|
+
return config;
|
|
255
|
+
});
|
|
256
|
+
const responseInterceptorId = axiosInstance.interceptors.response.use(null, async (error) => {
|
|
257
|
+
const { config } = error;
|
|
258
|
+
// If we have no information to retry the request
|
|
259
|
+
if (!config) {
|
|
260
|
+
return Promise.reject(error);
|
|
261
|
+
}
|
|
262
|
+
const currentState = setCurrentState(config, defaultOptions);
|
|
263
|
+
if (error.response && currentState.validateResponse?.(error.response)) {
|
|
264
|
+
// no issue with response
|
|
265
|
+
return error.response;
|
|
266
|
+
}
|
|
267
|
+
if (await shouldRetry(currentState, error)) {
|
|
268
|
+
return handleRetry(axiosInstance, currentState, error, config);
|
|
269
|
+
}
|
|
270
|
+
await handleMaxRetryTimesExceeded(currentState, error);
|
|
271
|
+
return Promise.reject(error);
|
|
272
|
+
});
|
|
273
|
+
return { requestInterceptorId, responseInterceptorId };
|
|
274
|
+
};
|
|
275
|
+
// Compatibility with CommonJS
|
|
276
|
+
axiosRetry.isNetworkError = isNetworkError;
|
|
277
|
+
axiosRetry.isSafeRequestError = isSafeRequestError;
|
|
278
|
+
axiosRetry.isIdempotentRequestError = isIdempotentRequestError;
|
|
279
|
+
axiosRetry.isNetworkOrIdempotentRequestError = isNetworkOrIdempotentRequestError;
|
|
280
|
+
axiosRetry.exponentialDelay = exponentialDelay;
|
|
281
|
+
axiosRetry.linearDelay = linearDelay;
|
|
282
|
+
axiosRetry.isRetryableError = isRetryableError;
|
|
283
|
+
|
|
29
284
|
let axiosClient;
|
|
30
285
|
async function getAxiosAuthClient() {
|
|
31
286
|
if (!axiosClient) {
|
|
@@ -35,6 +290,7 @@ async function getAxiosAuthClient() {
|
|
|
35
290
|
headers: {
|
|
36
291
|
'Content-Type': 'application/json',
|
|
37
292
|
},
|
|
293
|
+
timeout: 30000, // 30s timeout, matching makeApiRequest
|
|
38
294
|
});
|
|
39
295
|
}
|
|
40
296
|
return axiosClient;
|
|
@@ -81,15 +337,41 @@ async function createSessionWithPrivy(authToken, address, privyIdToken) {
|
|
|
81
337
|
});
|
|
82
338
|
return response.data;
|
|
83
339
|
}
|
|
340
|
+
// Deduplicate concurrent refreshSession calls to prevent multiple code paths
|
|
341
|
+
// (WebSocket reconnection, periodic refresh, API 401 retry) from all hitting
|
|
342
|
+
// /session/refresh simultaneously.
|
|
343
|
+
// Note: module-level state is shared across requests in SSR/Node — acceptable
|
|
344
|
+
// since there is only one active session per server instance (same pattern as
|
|
345
|
+
// refreshInFlight in api.ts).
|
|
346
|
+
let refreshInFlight$1 = null;
|
|
347
|
+
let refreshInFlightToken = null;
|
|
84
348
|
async function refreshSession(refreshToken) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
349
|
+
if (refreshInFlight$1 && refreshInFlightToken === refreshToken) {
|
|
350
|
+
return refreshInFlight$1;
|
|
351
|
+
}
|
|
352
|
+
refreshInFlightToken = refreshToken;
|
|
353
|
+
let currentFlight;
|
|
354
|
+
currentFlight = refreshInFlight$1 = (async () => {
|
|
355
|
+
try {
|
|
356
|
+
const client = await getAxiosAuthClient();
|
|
357
|
+
const config = await getConfig();
|
|
358
|
+
const appId = config.appId;
|
|
359
|
+
const response = await client.post('/session/refresh', {
|
|
360
|
+
refreshToken,
|
|
361
|
+
appId
|
|
362
|
+
});
|
|
363
|
+
return response.data;
|
|
364
|
+
}
|
|
365
|
+
finally {
|
|
366
|
+
// Only clear if we're still the active in-flight request.
|
|
367
|
+
// A second call with a different token may have replaced us.
|
|
368
|
+
if (refreshInFlight$1 === currentFlight) {
|
|
369
|
+
refreshInFlight$1 = null;
|
|
370
|
+
refreshInFlightToken = null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
})();
|
|
374
|
+
return refreshInFlight$1;
|
|
93
375
|
}
|
|
94
376
|
async function signSessionCreateMessage(_signMessageFunction) {
|
|
95
377
|
}
|
|
@@ -227,14 +509,10 @@ class WebSessionManager {
|
|
|
227
509
|
WebSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
|
|
228
510
|
|
|
229
511
|
var webSessionManager = /*#__PURE__*/Object.freeze({
|
|
230
|
-
|
|
231
|
-
|
|
512
|
+
__proto__: null,
|
|
513
|
+
WebSessionManager: WebSessionManager
|
|
232
514
|
});
|
|
233
515
|
|
|
234
|
-
function getDefaultExportFromCjs (x) {
|
|
235
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
516
|
var buffer = {};
|
|
239
517
|
|
|
240
518
|
var base64Js = {};
|
|
@@ -3031,7 +3309,19 @@ async function buildSetDocumentsTransaction(connection, idl, anchorProvider, pay
|
|
|
3031
3309
|
}
|
|
3032
3310
|
}
|
|
3033
3311
|
else if (lutKey != null) {
|
|
3034
|
-
|
|
3312
|
+
// The LUT may have just been created server-side and the client's RPC node
|
|
3313
|
+
// may not have indexed it yet (load-balancer split). Retry with exponential
|
|
3314
|
+
// back-off; first retry at 100 ms almost always resolves it.
|
|
3315
|
+
let table = null;
|
|
3316
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
3317
|
+
const { value } = await connection.getAddressLookupTable(new web3_js.PublicKey(lutKey));
|
|
3318
|
+
if (value) {
|
|
3319
|
+
table = value;
|
|
3320
|
+
break;
|
|
3321
|
+
}
|
|
3322
|
+
if (attempt < 4)
|
|
3323
|
+
await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt)));
|
|
3324
|
+
}
|
|
3035
3325
|
if (!table)
|
|
3036
3326
|
throw new Error('LUT not found after creation/extend');
|
|
3037
3327
|
lookupTables.push(table);
|
|
@@ -3166,8 +3456,8 @@ class ServerSessionManager {
|
|
|
3166
3456
|
ServerSessionManager.instance = new ServerSessionManager();
|
|
3167
3457
|
|
|
3168
3458
|
var serverSessionManager = /*#__PURE__*/Object.freeze({
|
|
3169
|
-
|
|
3170
|
-
|
|
3459
|
+
__proto__: null,
|
|
3460
|
+
ServerSessionManager: ServerSessionManager
|
|
3171
3461
|
});
|
|
3172
3462
|
|
|
3173
3463
|
/**
|
|
@@ -3266,6 +3556,23 @@ async function updateIdTokenAndAccessToken(idToken, accessToken, isServer = fals
|
|
|
3266
3556
|
await WebSessionManager.updateIdTokenAndAccessToken(idToken, accessToken);
|
|
3267
3557
|
}
|
|
3268
3558
|
|
|
3559
|
+
const apiClient = axios.create();
|
|
3560
|
+
axiosRetry(apiClient, {
|
|
3561
|
+
retries: 2,
|
|
3562
|
+
retryCondition: (error) => {
|
|
3563
|
+
var _a, _b;
|
|
3564
|
+
// Only retry GET requests on network errors (ECONNRESET, ETIMEDOUT, etc.)
|
|
3565
|
+
// Writes (POST/PUT) are not retried — the server may have processed
|
|
3566
|
+
// the request before the connection was reset.
|
|
3567
|
+
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';
|
|
3568
|
+
},
|
|
3569
|
+
retryDelay: axiosRetry.exponentialDelay,
|
|
3570
|
+
shouldResetTimeout: true,
|
|
3571
|
+
onRetry: (retryCount, error, requestConfig) => {
|
|
3572
|
+
var _a;
|
|
3573
|
+
console.warn(`[tarobase-sdk] retry ${retryCount} for ${(_a = requestConfig.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()} ${requestConfig.url} — ${error.code || error.message}`);
|
|
3574
|
+
},
|
|
3575
|
+
});
|
|
3269
3576
|
const refreshInFlight = new Map();
|
|
3270
3577
|
async function refreshAuthSessionOnce(appId, isServer) {
|
|
3271
3578
|
const key = `${isServer ? "server" : "web"}:${appId}`;
|
|
@@ -3312,6 +3619,7 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
|
3312
3619
|
ServerSessionManager.instance.clearSession();
|
|
3313
3620
|
};
|
|
3314
3621
|
async function executeRequest() {
|
|
3622
|
+
var _a;
|
|
3315
3623
|
// When _getAuthHeaders is provided (wallet client), use it as the sole auth source.
|
|
3316
3624
|
// Otherwise use the global createAuthHeader (default path).
|
|
3317
3625
|
const authHeader = (_overrides === null || _overrides === void 0 ? void 0 : _overrides._getAuthHeaders)
|
|
@@ -3333,11 +3641,12 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
|
3333
3641
|
method,
|
|
3334
3642
|
url: `${config.apiUrl}${urlPath.startsWith("/") ? urlPath : `/${urlPath}`}`,
|
|
3335
3643
|
headers,
|
|
3644
|
+
timeout: (_a = _overrides === null || _overrides === void 0 ? void 0 : _overrides.timeout) !== null && _a !== void 0 ? _a : 30000,
|
|
3336
3645
|
};
|
|
3337
3646
|
if (method !== "GET" && method !== "get") {
|
|
3338
3647
|
requestConfig.data = data ? JSON.stringify(data) : {};
|
|
3339
3648
|
}
|
|
3340
|
-
const response = await
|
|
3649
|
+
const response = await apiClient(requestConfig);
|
|
3341
3650
|
return { data: response.data, status: response.status, headers: response.headers };
|
|
3342
3651
|
}
|
|
3343
3652
|
try {
|
|
@@ -3748,6 +4057,90 @@ function cleanupExpiredCache() {
|
|
|
3748
4057
|
});
|
|
3749
4058
|
lastCacheCleanup = now;
|
|
3750
4059
|
}
|
|
4060
|
+
async function getMany(paths, opts = {}) {
|
|
4061
|
+
if (paths.length === 0) {
|
|
4062
|
+
return [];
|
|
4063
|
+
}
|
|
4064
|
+
if (paths.length > 30) {
|
|
4065
|
+
throw new Error('Cannot fetch more than 30 documents at once');
|
|
4066
|
+
}
|
|
4067
|
+
const normalizedPaths = [];
|
|
4068
|
+
for (const path of paths) {
|
|
4069
|
+
let normalizedPath = path.startsWith("/") ? path.slice(1) : path;
|
|
4070
|
+
if (normalizedPath.endsWith("*") && normalizedPath.length > 1) {
|
|
4071
|
+
normalizedPath = normalizedPath.slice(0, -1);
|
|
4072
|
+
}
|
|
4073
|
+
if (!normalizedPath || normalizedPath.length === 0) {
|
|
4074
|
+
throw new Error(`Invalid path provided: ${path}`);
|
|
4075
|
+
}
|
|
4076
|
+
const pathIsDocument = normalizedPath.split("/").length % 2 === 0;
|
|
4077
|
+
if (!pathIsDocument) {
|
|
4078
|
+
throw new Error(`Path must point to a document (even number of segments): ${path}`);
|
|
4079
|
+
}
|
|
4080
|
+
normalizedPaths.push(normalizedPath);
|
|
4081
|
+
}
|
|
4082
|
+
const now = Date.now();
|
|
4083
|
+
const results = new Array(paths.length);
|
|
4084
|
+
const uncachedIndices = [];
|
|
4085
|
+
const uncachedPaths = [];
|
|
4086
|
+
for (let i = 0; i < normalizedPaths.length; i++) {
|
|
4087
|
+
const normalizedPath = normalizedPaths[i];
|
|
4088
|
+
const cacheKey = `${normalizedPath}:`;
|
|
4089
|
+
if (!opts.bypassCache && getCache[cacheKey] && now < getCache[cacheKey].expiresAt) {
|
|
4090
|
+
results[i] = { path: normalizedPath, data: getCache[cacheKey].data };
|
|
4091
|
+
}
|
|
4092
|
+
else {
|
|
4093
|
+
uncachedIndices.push(i);
|
|
4094
|
+
uncachedPaths.push(normalizedPath);
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
if (uncachedPaths.length > 0) {
|
|
4098
|
+
try {
|
|
4099
|
+
const response = await makeApiRequest('POST', 'items/batch', { paths: uncachedPaths }, opts._overrides);
|
|
4100
|
+
const serverResults = response.data.results;
|
|
4101
|
+
const serverResultsMap = new Map();
|
|
4102
|
+
for (const result of serverResults) {
|
|
4103
|
+
serverResultsMap.set(result.path, result);
|
|
4104
|
+
}
|
|
4105
|
+
for (let i = 0; i < uncachedIndices.length; i++) {
|
|
4106
|
+
const originalIndex = uncachedIndices[i];
|
|
4107
|
+
const normalizedPath = uncachedPaths[i];
|
|
4108
|
+
const serverResult = serverResultsMap.get(normalizedPath);
|
|
4109
|
+
if (serverResult) {
|
|
4110
|
+
results[originalIndex] = serverResult;
|
|
4111
|
+
if (!serverResult.error && !opts.bypassCache) {
|
|
4112
|
+
const cacheKey = `${normalizedPath}:`;
|
|
4113
|
+
getCache[cacheKey] = {
|
|
4114
|
+
data: serverResult.data,
|
|
4115
|
+
expiresAt: now + GET_CACHE_TTL
|
|
4116
|
+
};
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
else {
|
|
4120
|
+
results[originalIndex] = {
|
|
4121
|
+
path: normalizedPath,
|
|
4122
|
+
data: null,
|
|
4123
|
+
error: { code: 'NOT_FOUND', message: `No result returned for path ${normalizedPath}` }
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
if (now - lastCacheCleanup > 5000) {
|
|
4128
|
+
cleanupExpiredCache();
|
|
4129
|
+
lastCacheCleanup = now;
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
catch (error) {
|
|
4133
|
+
for (const originalIndex of uncachedIndices) {
|
|
4134
|
+
results[originalIndex] = {
|
|
4135
|
+
path: normalizedPaths[originalIndex],
|
|
4136
|
+
data: null,
|
|
4137
|
+
error: { code: 'NOT_FOUND', message: error instanceof Error ? error.message : 'Unknown error' }
|
|
4138
|
+
};
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
return results;
|
|
4143
|
+
}
|
|
3751
4144
|
async function runQuery(absolutePath, queryName, queryArgs, opts) {
|
|
3752
4145
|
const result = await runQueryMany([{ absolutePath, queryName, queryArgs }], opts);
|
|
3753
4146
|
return result[0];
|
|
@@ -4189,6 +4582,7 @@ function scheduleTokenRefresh(connection, isServer) {
|
|
|
4189
4582
|
// This replaces the old single setTimeout approach which was unreliable for long
|
|
4190
4583
|
// delays (browsers throttle/suspend timers in background tabs).
|
|
4191
4584
|
connection.tokenRefreshTimer = setInterval(async () => {
|
|
4585
|
+
var _a, _b;
|
|
4192
4586
|
try {
|
|
4193
4587
|
const currentToken = await getIdToken(isServer);
|
|
4194
4588
|
if (!currentToken)
|
|
@@ -4219,6 +4613,17 @@ function scheduleTokenRefresh(connection, isServer) {
|
|
|
4219
4613
|
}
|
|
4220
4614
|
}
|
|
4221
4615
|
catch (refreshError) {
|
|
4616
|
+
// If the refresh token itself is invalid (401/403), stop retrying —
|
|
4617
|
+
// the token won't magically become valid next interval, and continuing
|
|
4618
|
+
// to hammer /session/refresh causes 429 rate-limit storms.
|
|
4619
|
+
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) {
|
|
4620
|
+
console.warn('[WS v2] Refresh token rejected (401/403), stopping periodic refresh');
|
|
4621
|
+
if (connection.tokenRefreshTimer) {
|
|
4622
|
+
clearInterval(connection.tokenRefreshTimer);
|
|
4623
|
+
connection.tokenRefreshTimer = null;
|
|
4624
|
+
}
|
|
4625
|
+
return;
|
|
4626
|
+
}
|
|
4222
4627
|
console.warn('[WS v2] Proactive token refresh failed, will retry next interval:', refreshError);
|
|
4223
4628
|
}
|
|
4224
4629
|
}
|
|
@@ -4229,6 +4634,7 @@ function scheduleTokenRefresh(connection, isServer) {
|
|
|
4229
4634
|
}, TOKEN_CHECK_INTERVAL);
|
|
4230
4635
|
}
|
|
4231
4636
|
async function getFreshAuthToken(isServer) {
|
|
4637
|
+
var _a, _b;
|
|
4232
4638
|
const currentToken = await getIdToken(isServer);
|
|
4233
4639
|
if (!currentToken) {
|
|
4234
4640
|
return null;
|
|
@@ -4251,6 +4657,17 @@ async function getFreshAuthToken(isServer) {
|
|
|
4251
4657
|
}
|
|
4252
4658
|
catch (error) {
|
|
4253
4659
|
console.error('[WS v2] Error refreshing token:', error);
|
|
4660
|
+
// If the refresh token is permanently invalid (401/403), clear the stale
|
|
4661
|
+
// session from storage so future attempts don't keep retrying with it.
|
|
4662
|
+
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)) {
|
|
4663
|
+
try {
|
|
4664
|
+
const { WebSessionManager } = await Promise.resolve().then(function () { return webSessionManager; });
|
|
4665
|
+
WebSessionManager.clearSession();
|
|
4666
|
+
}
|
|
4667
|
+
catch (clearError) {
|
|
4668
|
+
console.warn('[WS v2] Failed to clear stale session:', clearError);
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4254
4671
|
}
|
|
4255
4672
|
// Return null instead of the expired token to prevent infinite 401 reconnect storms.
|
|
4256
4673
|
// The server accepts unauthenticated connections; auth-required subscriptions will
|
|
@@ -4371,7 +4788,16 @@ async function getOrCreateConnection(appId, isServer) {
|
|
|
4371
4788
|
ws.addEventListener('open', () => {
|
|
4372
4789
|
connection.isConnecting = false;
|
|
4373
4790
|
connection.isConnected = true;
|
|
4374
|
-
|
|
4791
|
+
// NOTE: Do NOT reset consecutiveAuthFailures here. It is reset when a
|
|
4792
|
+
// fresh auth token is actually obtained (in urlProvider, line ~389) or on
|
|
4793
|
+
// an explicit auth change (reconnectWithNewAuthV2, line ~854). Resetting
|
|
4794
|
+
// on every 'open' event created an infinite loop: auth fails 5x → connect
|
|
4795
|
+
// without auth → open resets counter → disconnect → auth fails 5x again →
|
|
4796
|
+
// repeat forever, hammering /session/refresh and causing 429s.
|
|
4797
|
+
//
|
|
4798
|
+
// An elevated counter is safe for anonymous/guest sessions: when there's no
|
|
4799
|
+
// token at all (getIdToken returns null), the counter is never checked —
|
|
4800
|
+
// urlProvider skips straight to unauthenticated connection.
|
|
4375
4801
|
// Schedule periodic token freshness checks
|
|
4376
4802
|
scheduleTokenRefresh(connection, isServer);
|
|
4377
4803
|
// Re-subscribe to all existing subscriptions after reconnect
|
|
@@ -5007,6 +5433,7 @@ exports.getCachedData = getCachedData;
|
|
|
5007
5433
|
exports.getConfig = getConfig;
|
|
5008
5434
|
exports.getFiles = getFiles;
|
|
5009
5435
|
exports.getIdToken = getIdToken;
|
|
5436
|
+
exports.getMany = getMany;
|
|
5010
5437
|
exports.init = init;
|
|
5011
5438
|
exports.reconnectWithNewAuth = reconnectWithNewAuth;
|
|
5012
5439
|
exports.refreshSession = refreshSession;
|