quickbase-js 0.1.0-beta.8 → 0.1.0-beta.9
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/CHANGELOG.md +29 -0
- package/README.md +46 -130
- package/dist/esm/quickbase.d.ts +822 -119
- package/dist/esm/quickbase.js +373 -236
- package/dist/esm/quickbase.js.map +1 -1
- package/dist/esm/quickbase.min.js +1 -1
- package/dist/esm/quickbase.min.js.map +1 -1
- package/dist/umd/quickbase.umd.js +373 -236
- package/dist/umd/quickbase.umd.js.map +1 -1
- package/dist/umd/quickbase.umd.min.js +1 -1
- package/dist/umd/quickbase.umd.min.js.map +1 -1
- package/open-api/generate-unified-interface.ts +97 -17
- package/open-api/utils/generateJsDoc.ts +96 -0
- package/package.json +3 -1
- package/src/ThrottleBucket.ts +17 -12
- package/src/authorizationStrategy.ts +246 -0
- package/src/generated-unified/QuickbaseClient.ts +809 -119
- package/src/invokeMethod.ts +150 -265
- package/src/quickbaseClient.ts +85 -58
- package/src/rateLimiter.ts +25 -0
- package/src/tokenCache.ts +10 -26
- package/tests/vitest/qb/auth/mockQuickbaseServer.ts +60 -0
- package/tests/vitest/qb/auth/retryUserToken401.test.ts +59 -42
- package/tests/vitest/qb/auth/ssoTokenIntegrationWithServer.test.ts +65 -0
- package/tests/vitest/qb/fields/getFieldUsage.test.ts +58 -150
- package/tests/vitest/qb/formulas/runFormula.test.ts +96 -0
- package/tests/vitest/qb/tables/createRelationship.test.ts +102 -0
- package/tests/vitest/qb/tables/deleteRelationship.test.ts +143 -0
- package/tests/vitest/qb/tables/updateRelationship.test.ts +118 -145
- package/tests/vitest/unit/ThrottleBucket.test.ts +95 -0
- package/tests/vitest/unit/apps/copyApp.test.ts +133 -40
- package/tests/vitest/unit/apps/updateApp.test.ts +42 -11
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +3 -2
- package/tests/vitest/unit/auth/getTempToken.test.ts +42 -18
- package/tests/vitest/unit/auth/retryTempToken401.test.ts +46 -64
- package/tests/vitest/unit/auth/retryTempTokenMaxRetries.test.ts +235 -0
- package/tests/vitest/unit/auth/retryUserToken401.test.ts +56 -43
- package/tests/vitest/unit/auth/ssoTokenRefresh.test.ts +325 -0
- package/tests/vitest/unit/fields/createField.test.ts +180 -116
- package/tests/vitest/unit/fields/getFieldUsage.test.ts +17 -3
- package/tests/vitest/unit/fields/updateField.test.ts +12 -7
- package/tests/vitest/unit/formulas/runFormula.test.ts +205 -0
- package/tests/vitest/unit/index.ts +2 -1
- package/tests/vitest/unit/inferHttpMethod.test.ts +131 -0
- package/tests/vitest/unit/rateLimit.test.ts +4 -2
- package/tests/vitest/unit/tables/createRelationship.test.ts +212 -0
- package/tests/vitest/unit/tables/deleteRelationship.test.ts +97 -0
- package/tests/vitest/unit/tables/updateRelationship.test.ts +12 -10
- package/tests/vitest/unit/tokenLifespan.test.ts +149 -83
- package/tools/log-gen.ts +14 -5
- package/tools/log-gen.yaml +710 -268
- package/tests/vitest/unit/auth/retryTempTokenTwo401.test.ts +0 -81
package/src/invokeMethod.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// src/invokeMethod.ts
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
|
|
3
|
+
import { AuthorizationStrategy, extractDbid } from "./authorizationStrategy";
|
|
4
|
+
import { RateLimiter } from "./rateLimiter";
|
|
5
5
|
import { RateLimitError } from "./RateLimitError";
|
|
6
|
+
import { ResponseError } from "./generated/runtime";
|
|
7
|
+
import { QuickbaseClient } from "./quickbaseClient";
|
|
6
8
|
import { GetTempTokenDBID200Response } from "./generated/models";
|
|
7
9
|
|
|
8
10
|
export type ApiMethod<K extends keyof QuickbaseClient> = (
|
|
@@ -17,92 +19,73 @@ export interface MethodInfo<K extends keyof QuickbaseClient> {
|
|
|
17
19
|
httpMethod: string;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export interface TempTokenParams {
|
|
21
|
-
appId?: string;
|
|
22
|
-
tableId?: string;
|
|
23
|
-
dbid?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface TokenCache {
|
|
27
|
-
get: (dbid: string) => string | undefined;
|
|
28
|
-
set: (dbid: string, token: string) => void;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
22
|
export async function invokeMethod<K extends keyof QuickbaseClient>(
|
|
32
23
|
methodName: K,
|
|
33
|
-
params: Parameters<QuickbaseClient[K]>[0] &
|
|
24
|
+
params: Parameters<QuickbaseClient[K]>[0] & {
|
|
25
|
+
dbid?: string;
|
|
26
|
+
tableId?: string;
|
|
27
|
+
appId?: string;
|
|
28
|
+
},
|
|
34
29
|
methodMap: { [P in keyof QuickbaseClient]: MethodInfo<P> },
|
|
35
30
|
baseHeaders: Record<string, string>,
|
|
36
|
-
|
|
31
|
+
authStrategy: AuthorizationStrategy,
|
|
32
|
+
rateLimiter: RateLimiter,
|
|
37
33
|
fetchTempToken: (dbid: string) => Promise<string>,
|
|
38
34
|
transformDates: (obj: any, convertStringsToDates: boolean) => any,
|
|
39
|
-
initialTempToken: string | undefined,
|
|
40
|
-
userToken: string | undefined,
|
|
41
|
-
useTempTokens: boolean | undefined,
|
|
42
35
|
debug: boolean | undefined,
|
|
43
|
-
convertDates: boolean
|
|
44
|
-
retryCount: number = 0,
|
|
45
|
-
throttleBucket: ThrottleBucket | null = null,
|
|
46
|
-
maxRetries: number = 3,
|
|
47
|
-
retryDelay: number = 1000
|
|
36
|
+
convertDates: boolean
|
|
48
37
|
): Promise<ReturnType<QuickbaseClient[K]>> {
|
|
38
|
+
console.log("[invokeMethod] Starting for method:", methodName);
|
|
39
|
+
|
|
49
40
|
const methodInfo = methodMap[methodName];
|
|
50
41
|
if (!methodInfo) throw new Error(`Method ${methodName} not found`);
|
|
51
42
|
|
|
52
43
|
const hasBody = "body" in params && params.body !== undefined;
|
|
53
|
-
const body = hasBody ?
|
|
44
|
+
const body = hasBody ? params.body : undefined;
|
|
54
45
|
const restParams: any = hasBody
|
|
55
46
|
? Object.fromEntries(
|
|
56
47
|
Object.entries(params).filter(([key]) => key !== "body")
|
|
57
48
|
)
|
|
58
49
|
: { ...params };
|
|
59
|
-
|
|
60
50
|
const requestParameters: any = {
|
|
61
51
|
...restParams,
|
|
62
52
|
...(hasBody ? { generated: body } : {}),
|
|
63
53
|
};
|
|
64
54
|
|
|
65
|
-
|
|
66
|
-
credentials: "omit",
|
|
55
|
+
const requestOptions: RequestInit = {
|
|
56
|
+
credentials: methodName === "getTempTokenDBID" ? "include" : "omit",
|
|
67
57
|
method: methodInfo.httpMethod,
|
|
68
|
-
...(hasBody ? { body } : {}),
|
|
69
58
|
};
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let authorizationToken = selectedToken;
|
|
60
|
+
let dbid: string | undefined = extractDbid(params);
|
|
61
|
+
console.log("[invokeMethod] Extracted dbid:", dbid);
|
|
74
62
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
let token = dbid
|
|
64
|
+
? await authStrategy.getToken(dbid)
|
|
65
|
+
: await authStrategy.getToken("");
|
|
66
|
+
console.log("[invokeMethod] Initial token:", token);
|
|
67
|
+
|
|
68
|
+
if (!token && dbid) {
|
|
69
|
+
token = await fetchTempToken(dbid);
|
|
70
|
+
authStrategy.applyHeaders(baseHeaders, token);
|
|
71
|
+
requestOptions.headers = { ...baseHeaders };
|
|
72
|
+
console.log("[invokeMethod] Fetched new token:", token);
|
|
83
73
|
}
|
|
84
74
|
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
75
|
+
if (token) {
|
|
76
|
+
authStrategy.applyHeaders(baseHeaders, token);
|
|
77
|
+
requestOptions.headers = { ...baseHeaders };
|
|
78
|
+
console.log(
|
|
79
|
+
"[invokeMethod] Token found, headers set:",
|
|
80
|
+
requestOptions.headers
|
|
89
81
|
);
|
|
90
|
-
const cachedToken = tokenCache.get(dbid);
|
|
91
|
-
authorizationToken = cachedToken || (await fetchTempToken(dbid));
|
|
92
82
|
if (methodName === "getTempTokenDBID") {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
console.log("[invokeMethod] Using cached token:", token);
|
|
84
|
+
return { temporaryAuthorization: token } as ReturnType<
|
|
85
|
+
QuickbaseClient[K]
|
|
86
|
+
>;
|
|
96
87
|
}
|
|
97
88
|
}
|
|
98
|
-
requestOptions.headers = authorizationToken
|
|
99
|
-
? {
|
|
100
|
-
...baseHeaders,
|
|
101
|
-
Authorization: useTempTokens
|
|
102
|
-
? `QB-TEMP-TOKEN ${authorizationToken}`
|
|
103
|
-
: `QB-USER-TOKEN ${authorizationToken}`,
|
|
104
|
-
}
|
|
105
|
-
: baseHeaders;
|
|
106
89
|
|
|
107
90
|
if (debug) {
|
|
108
91
|
console.log(`[${methodName}] requestParameters:`, requestParameters);
|
|
@@ -113,24 +96,11 @@ export async function invokeMethod<K extends keyof QuickbaseClient>(
|
|
|
113
96
|
rawResponse: any
|
|
114
97
|
): Promise<ReturnType<QuickbaseClient[K]>> {
|
|
115
98
|
if (debug) console.log(`[${methodName}] rawResponse:`, rawResponse);
|
|
116
|
-
|
|
117
99
|
if (rawResponse instanceof Response) {
|
|
118
100
|
const contentType = rawResponse.headers
|
|
119
101
|
.get("Content-Type")
|
|
120
102
|
?.toLowerCase();
|
|
121
103
|
if (debug) console.log(`[${methodName}] contentType:`, contentType);
|
|
122
|
-
|
|
123
|
-
if (contentType?.includes("application/octet-stream")) {
|
|
124
|
-
return (await rawResponse.arrayBuffer()) as ReturnType<
|
|
125
|
-
QuickbaseClient[K]
|
|
126
|
-
>;
|
|
127
|
-
}
|
|
128
|
-
if (
|
|
129
|
-
contentType?.includes("application/x-yaml") ||
|
|
130
|
-
contentType?.includes("text/yaml")
|
|
131
|
-
) {
|
|
132
|
-
return (await rawResponse.text()) as ReturnType<QuickbaseClient[K]>;
|
|
133
|
-
}
|
|
134
104
|
if (contentType?.includes("application/json")) {
|
|
135
105
|
const jsonResponse = await rawResponse.json();
|
|
136
106
|
return transformDates(jsonResponse, convertDates) as ReturnType<
|
|
@@ -139,7 +109,6 @@ export async function invokeMethod<K extends keyof QuickbaseClient>(
|
|
|
139
109
|
}
|
|
140
110
|
return rawResponse as ReturnType<QuickbaseClient[K]>;
|
|
141
111
|
}
|
|
142
|
-
|
|
143
112
|
if (rawResponse && typeof rawResponse.value === "function") {
|
|
144
113
|
const response = await rawResponse.value();
|
|
145
114
|
if (debug)
|
|
@@ -148,210 +117,126 @@ export async function invokeMethod<K extends keyof QuickbaseClient>(
|
|
|
148
117
|
QuickbaseClient[K]
|
|
149
118
|
>;
|
|
150
119
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
console.log(`[${methodName}] Transformed non-Response:`, transformed);
|
|
155
|
-
return transformed as ReturnType<QuickbaseClient[K]>;
|
|
120
|
+
return transformDates(rawResponse, convertDates) as ReturnType<
|
|
121
|
+
QuickbaseClient[K]
|
|
122
|
+
>;
|
|
156
123
|
}
|
|
157
124
|
|
|
158
|
-
async function
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
await throttleBucket.acquire();
|
|
173
|
-
if (debug) console.log(`[${methodName}] Throttle bucket acquired`);
|
|
174
|
-
}
|
|
175
|
-
return await fn();
|
|
176
|
-
} catch (error) {
|
|
177
|
-
console.log(`[${methodName}] Caught error:`, error);
|
|
178
|
-
console.log(
|
|
179
|
-
`[${methodName}] instanceof ResponseError:`,
|
|
180
|
-
error instanceof ResponseError
|
|
181
|
-
);
|
|
182
|
-
console.log(
|
|
183
|
-
`[${methodName}] Response status:`,
|
|
184
|
-
(error as any).response?.status
|
|
185
|
-
);
|
|
186
|
-
const effectiveError =
|
|
187
|
-
error instanceof Object &&
|
|
188
|
-
"cause" in error &&
|
|
189
|
-
error.cause instanceof ResponseError
|
|
190
|
-
? error.cause
|
|
191
|
-
: error instanceof ResponseError
|
|
192
|
-
? error
|
|
193
|
-
: error;
|
|
194
|
-
console.log(`[${methodName}] Effective error:`, effectiveError);
|
|
195
|
-
console.log(
|
|
196
|
-
`[${methodName}] Effective instanceof ResponseError:`,
|
|
197
|
-
effectiveError instanceof ResponseError
|
|
198
|
-
);
|
|
199
|
-
console.log(
|
|
200
|
-
`[${methodName}] Effective response status:`,
|
|
201
|
-
(effectiveError as any).response?.status
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
attempt++;
|
|
125
|
+
async function parseErrorResponse(
|
|
126
|
+
response: Response
|
|
127
|
+
): Promise<{ message: string; status: number }> {
|
|
128
|
+
let message = "Unknown error";
|
|
129
|
+
let status = response.status || 500;
|
|
130
|
+
try {
|
|
131
|
+
const contentType =
|
|
132
|
+
response.headers?.get("Content-Type")?.toLowerCase() ||
|
|
133
|
+
"application/json";
|
|
134
|
+
if (
|
|
135
|
+
contentType.includes("application/json") &&
|
|
136
|
+
typeof response.json === "function"
|
|
137
|
+
) {
|
|
138
|
+
const errorBody = await response.json();
|
|
205
139
|
if (
|
|
206
|
-
|
|
207
|
-
|
|
140
|
+
errorBody &&
|
|
141
|
+
typeof errorBody === "object" &&
|
|
142
|
+
"message" in errorBody
|
|
208
143
|
) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
: String(error);
|
|
213
|
-
console.log(`[${methodName}] No retry, throwing:`, errorMessage);
|
|
214
|
-
if (
|
|
215
|
-
effectiveError instanceof ResponseError &&
|
|
216
|
-
effectiveError.response
|
|
217
|
-
) {
|
|
218
|
-
try {
|
|
219
|
-
const errorBody = await effectiveError.response.json();
|
|
220
|
-
if (debug)
|
|
221
|
-
console.log(`[${methodName}] Error response body:`, errorBody);
|
|
222
|
-
errorMessage = errorBody?.message || errorMessage;
|
|
223
|
-
} catch (e) {
|
|
224
|
-
if (debug)
|
|
225
|
-
console.log(`[${methodName}] Failed to parse error body:`, e);
|
|
226
|
-
}
|
|
227
|
-
throw new Error(
|
|
228
|
-
`API Error: ${errorMessage} (Status: ${effectiveError.response.status})`
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
throw new Error(`API Error: ${errorMessage} (Status: unknown)`);
|
|
232
|
-
}
|
|
233
|
-
if (options.onRetry) {
|
|
234
|
-
console.log(`[${methodName}] Retrying, attempt:`, attempt);
|
|
235
|
-
await options.onRetry(effectiveError, attempt);
|
|
144
|
+
message = errorBody.message;
|
|
145
|
+
} else {
|
|
146
|
+
message = "Invalid error response format";
|
|
236
147
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
: retryDelay * Math.pow(2, attempt - 1);
|
|
240
|
-
if (debug)
|
|
241
|
-
console.log(
|
|
242
|
-
`[${methodName}] Retrying after ${delayMs}ms (attempt ${attempt}/${
|
|
243
|
-
options.maxAttempts - 1
|
|
244
|
-
})`
|
|
245
|
-
);
|
|
246
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
148
|
+
} else if (typeof response.text === "function") {
|
|
149
|
+
message = (await response.text()) || message;
|
|
247
150
|
}
|
|
151
|
+
} catch (e) {
|
|
152
|
+
if (debug) console.log(`[${methodName}] Error parsing response body:`, e);
|
|
153
|
+
message = "Failed to parse error response";
|
|
248
154
|
}
|
|
155
|
+
console.log(
|
|
156
|
+
"[invokeMethod] Parsed error - status:",
|
|
157
|
+
status,
|
|
158
|
+
"message:",
|
|
159
|
+
message
|
|
160
|
+
);
|
|
161
|
+
return { message, status };
|
|
249
162
|
}
|
|
250
163
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
{
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
: String(fetchError);
|
|
283
|
-
throw new Error(
|
|
284
|
-
`API Error: ${fetchErrorMessage} (Status: ${
|
|
285
|
-
fetchError instanceof ResponseError &&
|
|
286
|
-
fetchError.response?.status
|
|
287
|
-
? fetchError.response.status
|
|
288
|
-
: "unknown"
|
|
289
|
-
})`
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
requestOptions.headers = {
|
|
293
|
-
...baseHeaders,
|
|
294
|
-
Authorization: `QB-TEMP-TOKEN ${authorizationToken}`,
|
|
295
|
-
};
|
|
296
|
-
} else if (userToken) {
|
|
297
|
-
if (debug)
|
|
298
|
-
console.log(
|
|
299
|
-
`Authorization error for ${methodName} (user token), retrying with same token:`,
|
|
300
|
-
error.message
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
if (debug)
|
|
304
|
-
console.log(
|
|
305
|
-
`Retrying ${methodName} with ${
|
|
306
|
-
useTempTokens ? "temp" : "user"
|
|
307
|
-
} token`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
}
|
|
312
|
-
);
|
|
164
|
+
let attempt = 0;
|
|
165
|
+
const maxAttempts = rateLimiter.maxRetries + 1;
|
|
166
|
+
|
|
167
|
+
while (attempt < maxAttempts) {
|
|
168
|
+
console.log("[invokeMethod] Attempt:", attempt + 1, "of", maxAttempts);
|
|
169
|
+
try {
|
|
170
|
+
await rateLimiter.throttle();
|
|
171
|
+
console.log("[invokeMethod] About to call API for method:", methodName);
|
|
172
|
+
const response = await methodInfo.method(
|
|
173
|
+
requestParameters,
|
|
174
|
+
requestOptions
|
|
175
|
+
);
|
|
176
|
+
console.log("[invokeMethod] API call completed for method:", methodName);
|
|
177
|
+
return await processResponse(response);
|
|
178
|
+
} catch (error: unknown) {
|
|
179
|
+
let status: number;
|
|
180
|
+
let message: string;
|
|
181
|
+
let response: Response;
|
|
182
|
+
|
|
183
|
+
console.log("[invokeMethod] Caught error:", error);
|
|
184
|
+
|
|
185
|
+
if (error instanceof ResponseError && error.response) {
|
|
186
|
+
response = error.response;
|
|
187
|
+
({ message, status } = await parseErrorResponse(response));
|
|
188
|
+
} else if (error instanceof Response) {
|
|
189
|
+
response = error;
|
|
190
|
+
({ message, status } = await parseErrorResponse(response));
|
|
191
|
+
} else {
|
|
192
|
+
if (debug) console.log(`[${methodName}] Unexpected error:`, error);
|
|
193
|
+
throw error; // Rethrow non-response errors
|
|
194
|
+
}
|
|
313
195
|
|
|
314
|
-
|
|
315
|
-
let effectiveError: unknown = error;
|
|
316
|
-
if (
|
|
317
|
-
error instanceof Object &&
|
|
318
|
-
"cause" in error &&
|
|
319
|
-
error.cause instanceof ResponseError
|
|
320
|
-
) {
|
|
321
|
-
effectiveError = error.cause;
|
|
322
|
-
} else if (error instanceof ResponseError) {
|
|
323
|
-
effectiveError = error;
|
|
324
|
-
}
|
|
196
|
+
console.log("[invokeMethod] Handling error with status:", status);
|
|
325
197
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
?
|
|
338
|
-
:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
throw effectiveError;
|
|
347
|
-
});
|
|
348
|
-
}
|
|
198
|
+
if (status === 429) {
|
|
199
|
+
if (!(error instanceof ResponseError)) {
|
|
200
|
+
throw new Error("Expected ResponseError for 429 handling"); // Shouldn't happen
|
|
201
|
+
}
|
|
202
|
+
const delay = await rateLimiter.handle429(error, attempt + 1);
|
|
203
|
+
if (debug) console.log(`[${methodName}] 429 delay: ${delay}ms`);
|
|
204
|
+
if (attempt + 1 === maxAttempts) {
|
|
205
|
+
throw new RateLimitError(
|
|
206
|
+
`API Error: ${message} (Status: ${status})`,
|
|
207
|
+
status,
|
|
208
|
+
response.headers.get("Retry-After")
|
|
209
|
+
? parseInt(response.headers.get("Retry-After")!, 10)
|
|
210
|
+
: undefined
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
214
|
+
attempt++;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
349
217
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
218
|
+
const newToken = await authStrategy.handleError(
|
|
219
|
+
status,
|
|
220
|
+
params,
|
|
221
|
+
fetchTempToken,
|
|
222
|
+
attempt,
|
|
223
|
+
maxAttempts,
|
|
224
|
+
debug,
|
|
225
|
+
methodName
|
|
226
|
+
);
|
|
227
|
+
if (newToken) {
|
|
228
|
+
token = newToken;
|
|
229
|
+
authStrategy.applyHeaders(baseHeaders, token);
|
|
230
|
+
requestOptions.headers = { ...baseHeaders };
|
|
231
|
+
attempt++;
|
|
232
|
+
if (debug)
|
|
233
|
+
console.log(
|
|
234
|
+
`[${methodName}] Retrying with token: ${token.substring(0, 10)}...`
|
|
235
|
+
);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
throw new Error(`API Error: ${message} (Status: ${status})`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
throw new Error(`API Error: Exhausted retries after ${maxAttempts} attempts`);
|
|
357
242
|
}
|