@clickup/rest-client 2.10.294 → 2.10.296
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/.eslintrc.base.js +2 -1
- package/README.md +2 -0
- package/dist/.eslintcache +1 -1
- package/dist/errors/RestRateLimitError.d.ts.map +1 -1
- package/dist/errors/RestRateLimitError.js.map +1 -1
- package/dist/errors/RestRetriableError.d.ts.map +1 -1
- package/dist/errors/RestRetriableError.js.map +1 -1
- package/dist/errors/RestTokenInvalidError.d.ts.map +1 -1
- package/dist/errors/RestTokenInvalidError.js.map +1 -1
- package/dist/internal/inferResBodyEncoding.js.map +1 -1
- package/docs/README.md +2 -0
- package/docs/classes/PacerComposite.md +7 -3
- package/docs/classes/PacerQPS.md +7 -3
- package/docs/classes/RestClient.md +32 -28
- package/docs/classes/RestContentSizeOverLimitError.md +5 -1
- package/docs/classes/RestError.md +5 -1
- package/docs/classes/RestRateLimitError.md +6 -2
- package/docs/classes/RestRequest.md +22 -18
- package/docs/classes/RestResponse.md +7 -3
- package/docs/classes/RestResponseError.md +5 -1
- package/docs/classes/RestRetriableError.md +6 -2
- package/docs/classes/RestStream.md +12 -8
- package/docs/classes/RestTimeoutError.md +5 -1
- package/docs/classes/RestTokenInvalidError.md +6 -2
- package/docs/interfaces/Middleware.md +4 -4
- package/docs/interfaces/Pacer.md +2 -2
- package/docs/interfaces/PacerQPSBackend.md +2 -2
- package/docs/interfaces/RestLogEvent.md +1 -1
- package/docs/interfaces/RestOptions.md +39 -2
- package/docs/interfaces/TokenGetter.md +3 -3
- package/docs/modules.md +4 -4
- package/package.json +5 -2
- package/src/RestClient.ts +19 -19
- package/src/RestOptions.ts +3 -3
- package/src/RestRequest.ts +11 -11
- package/src/RestResponse.ts +1 -1
- package/src/RestStream.ts +1 -1
- package/src/errors/RestRateLimitError.ts +5 -1
- package/src/errors/RestResponseError.ts +3 -3
- package/src/errors/RestRetriableError.ts +5 -1
- package/src/errors/RestTokenInvalidError.ts +4 -1
- package/src/helpers/depaginate.ts +3 -3
- package/src/internal/RestFetchReader.ts +2 -2
- package/src/internal/RestRangeUploader.ts +2 -2
- package/src/internal/calcRetryDelay.ts +2 -2
- package/src/internal/inferResBodyEncoding.ts +13 -13
- package/src/internal/inspectPossibleJSON.ts +5 -5
- package/src/internal/substituteParams.ts +1 -1
- package/src/internal/throwIfErrorResponse.ts +7 -7
- package/src/middlewares/paceRequests.ts +1 -1
- package/src/pacers/PacerComposite.ts +1 -1
- package/src/pacers/PacerQPS.ts +3 -3
- package/tsconfig.json +3 -4
package/src/RestClient.ts
CHANGED
|
@@ -54,7 +54,7 @@ export default class RestClient {
|
|
|
54
54
|
*/
|
|
55
55
|
withMiddleware(
|
|
56
56
|
middleware: RestOptions["middlewares"][0],
|
|
57
|
-
method: "unshift" | "push" = "push"
|
|
57
|
+
method: "unshift" | "push" = "push",
|
|
58
58
|
) {
|
|
59
59
|
const clone = new RestClient(this._options);
|
|
60
60
|
clone._options.middlewares[method](middleware);
|
|
@@ -156,7 +156,7 @@ export default class RestClient {
|
|
|
156
156
|
*/
|
|
157
157
|
withOAuth1(
|
|
158
158
|
consumer: { consumerKey: string; consumerSecret: string },
|
|
159
|
-
token: TokenGetter<{ token: string; tokenSecret: string }
|
|
159
|
+
token: TokenGetter<{ token: string; tokenSecret: string }>,
|
|
160
160
|
) {
|
|
161
161
|
const oauth = new OAuth1({
|
|
162
162
|
consumer: { key: consumer.consumerKey, secret: consumer.consumerSecret },
|
|
@@ -176,7 +176,7 @@ export default class RestClient {
|
|
|
176
176
|
oauth.authorize(requestData, {
|
|
177
177
|
key: tokenData.token,
|
|
178
178
|
secret: tokenData.tokenSecret,
|
|
179
|
-
})
|
|
179
|
+
}),
|
|
180
180
|
);
|
|
181
181
|
for (const [name, value] of Object.entries(addHeaders)) {
|
|
182
182
|
req.headers.set(name, value);
|
|
@@ -196,7 +196,7 @@ export default class RestClient {
|
|
|
196
196
|
const unencodedHeader = name + ":" + password;
|
|
197
197
|
req.headers.set(
|
|
198
198
|
"Authorization",
|
|
199
|
-
"Basic " + Buffer.from(unencodedHeader).toString("base64")
|
|
199
|
+
"Basic " + Buffer.from(unencodedHeader).toString("base64"),
|
|
200
200
|
);
|
|
201
201
|
return next(req);
|
|
202
202
|
});
|
|
@@ -211,7 +211,7 @@ export default class RestClient {
|
|
|
211
211
|
get(
|
|
212
212
|
path: string,
|
|
213
213
|
args: Partial<Record<string, string | number | string[]>> = {},
|
|
214
|
-
accept: string = "application/json"
|
|
214
|
+
accept: string = "application/json",
|
|
215
215
|
) {
|
|
216
216
|
return this._noBodyRequest(path, args, "GET", accept);
|
|
217
217
|
}
|
|
@@ -224,7 +224,7 @@ export default class RestClient {
|
|
|
224
224
|
body: string | Buffer | NodeJS.ReadableStream,
|
|
225
225
|
contentType: string,
|
|
226
226
|
method: "POST" | "PUT" | "PATCH" = "POST",
|
|
227
|
-
accept?: string
|
|
227
|
+
accept?: string,
|
|
228
228
|
) {
|
|
229
229
|
const origShape = simpleShape(path);
|
|
230
230
|
return new RestRequest(
|
|
@@ -236,7 +236,7 @@ export default class RestClient {
|
|
|
236
236
|
"Content-Type": contentType,
|
|
237
237
|
}),
|
|
238
238
|
body,
|
|
239
|
-
origShape
|
|
239
|
+
origShape,
|
|
240
240
|
);
|
|
241
241
|
}
|
|
242
242
|
|
|
@@ -247,7 +247,7 @@ export default class RestClient {
|
|
|
247
247
|
path: string,
|
|
248
248
|
body: any,
|
|
249
249
|
method: "POST" | "PUT" | "PATCH" | "DELETE" = "POST",
|
|
250
|
-
accept: string = "application/json"
|
|
250
|
+
accept: string = "application/json",
|
|
251
251
|
) {
|
|
252
252
|
const origShape = simpleShape(path, body);
|
|
253
253
|
[path, body] = substituteParams(path, body);
|
|
@@ -260,7 +260,7 @@ export default class RestClient {
|
|
|
260
260
|
"Content-Type": "application/json",
|
|
261
261
|
}),
|
|
262
262
|
JSON.stringify(body),
|
|
263
|
-
origShape
|
|
263
|
+
origShape,
|
|
264
264
|
);
|
|
265
265
|
}
|
|
266
266
|
|
|
@@ -271,7 +271,7 @@ export default class RestClient {
|
|
|
271
271
|
path: string,
|
|
272
272
|
body: Partial<Record<string, string>> | string,
|
|
273
273
|
method: "POST" | "PUT" | "PATCH" = "POST",
|
|
274
|
-
accept: string = "application/json"
|
|
274
|
+
accept: string = "application/json",
|
|
275
275
|
) {
|
|
276
276
|
const origShape = simpleShape(path, body);
|
|
277
277
|
[path, body] = substituteParams(path, body);
|
|
@@ -286,9 +286,9 @@ export default class RestClient {
|
|
|
286
286
|
typeof body === "string"
|
|
287
287
|
? body
|
|
288
288
|
: new URLSearchParams(
|
|
289
|
-
omitBy(body, isUndefined) as Record<string, string
|
|
289
|
+
omitBy(body, isUndefined) as Record<string, string>,
|
|
290
290
|
).toString(),
|
|
291
|
-
origShape
|
|
291
|
+
origShape,
|
|
292
292
|
);
|
|
293
293
|
}
|
|
294
294
|
|
|
@@ -298,7 +298,7 @@ export default class RestClient {
|
|
|
298
298
|
writeDelete(
|
|
299
299
|
path: string,
|
|
300
300
|
args: Partial<Record<string, string>> = {},
|
|
301
|
-
accept: string = "application/json"
|
|
301
|
+
accept: string = "application/json",
|
|
302
302
|
) {
|
|
303
303
|
return this._noBodyRequest(path, args, "DELETE", accept);
|
|
304
304
|
}
|
|
@@ -355,14 +355,14 @@ export default class RestClient {
|
|
|
355
355
|
mimeType: string,
|
|
356
356
|
stream: AsyncIterable<Buffer>,
|
|
357
357
|
method: "POST" | "PUT" = "POST",
|
|
358
|
-
chunkSize: number
|
|
358
|
+
chunkSize: number,
|
|
359
359
|
) {
|
|
360
360
|
return new RestRangeUploader(
|
|
361
361
|
this,
|
|
362
362
|
chunkSize,
|
|
363
363
|
method,
|
|
364
364
|
path,
|
|
365
|
-
mimeType
|
|
365
|
+
mimeType,
|
|
366
366
|
).upload(stream);
|
|
367
367
|
}
|
|
368
368
|
|
|
@@ -381,7 +381,7 @@ export default class RestClient {
|
|
|
381
381
|
"",
|
|
382
382
|
new Headers({ "Content-Type": "application/json" }),
|
|
383
383
|
JSON.stringify({ variables, query }), // variables first - for truncation in logs,
|
|
384
|
-
origShape
|
|
384
|
+
origShape,
|
|
385
385
|
);
|
|
386
386
|
}
|
|
387
387
|
|
|
@@ -392,7 +392,7 @@ export default class RestClient {
|
|
|
392
392
|
path: string,
|
|
393
393
|
args: Partial<Record<string, string | number | string[]>> = {},
|
|
394
394
|
method: "GET" | "DELETE",
|
|
395
|
-
accept: string = "application/json"
|
|
395
|
+
accept: string = "application/json",
|
|
396
396
|
) {
|
|
397
397
|
const origShape = simpleShape(path, args);
|
|
398
398
|
[path, args] = substituteParams(path, args);
|
|
@@ -418,7 +418,7 @@ export default class RestClient {
|
|
|
418
418
|
path,
|
|
419
419
|
new Headers({ Accept: accept }),
|
|
420
420
|
"",
|
|
421
|
-
origShape
|
|
421
|
+
origShape,
|
|
422
422
|
);
|
|
423
423
|
}
|
|
424
424
|
}
|
|
@@ -431,7 +431,7 @@ export default class RestClient {
|
|
|
431
431
|
*/
|
|
432
432
|
export async function tokenRetryStrategy<TData>(
|
|
433
433
|
token: TokenGetter<TData>,
|
|
434
|
-
body: (tokenData: TData) => Promise<RestResponse
|
|
434
|
+
body: (tokenData: TData) => Promise<RestResponse>,
|
|
435
435
|
) {
|
|
436
436
|
let tokenData = await token(null);
|
|
437
437
|
try {
|
package/src/RestOptions.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface RestLogEvent {
|
|
|
27
27
|
export interface Middleware {
|
|
28
28
|
(
|
|
29
29
|
req: RestRequest,
|
|
30
|
-
next: (req: RestRequest) => Promise<RestResponse
|
|
30
|
+
next: (req: RestRequest) => Promise<RestResponse>,
|
|
31
31
|
): Promise<RestResponse>;
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -140,7 +140,7 @@ export default interface RestOptions {
|
|
|
140
140
|
* isRateLimitError and isRetriableError handlers set up, and they return
|
|
141
141
|
* contradictory information; then isRateLimitError wins. */
|
|
142
142
|
isRateLimitError: (
|
|
143
|
-
res: RestResponse
|
|
143
|
+
res: RestResponse,
|
|
144
144
|
) => "SOMETHING_ELSE" | "RATE_LIMIT" | "BEST_EFFORT" | number;
|
|
145
145
|
/** Decides whether the response is a token-invalid error or not. In case it's
|
|
146
146
|
* not, the response ought to be either success or some other error. */
|
|
@@ -154,7 +154,7 @@ export default interface RestOptions {
|
|
|
154
154
|
* retry will happen in not less than this number of milliseconds. */
|
|
155
155
|
isRetriableError: (
|
|
156
156
|
res: RestResponse,
|
|
157
|
-
_error: any
|
|
157
|
+
_error: any,
|
|
158
158
|
) => "NEVER_RETRY" | "RETRY" | "BEST_EFFORT" | number;
|
|
159
159
|
}
|
|
160
160
|
|
package/src/RestRequest.ts
CHANGED
|
@@ -33,7 +33,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
33
33
|
public url: string,
|
|
34
34
|
public readonly headers: Headers,
|
|
35
35
|
public readonly body: string | Buffer | NodeJS.ReadableStream,
|
|
36
|
-
public readonly shape?: string
|
|
36
|
+
public readonly shape?: string,
|
|
37
37
|
) {
|
|
38
38
|
this.options = { ...options };
|
|
39
39
|
}
|
|
@@ -120,7 +120,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
120
120
|
// By passing Number.MAX_SAFE_INTEGER to stream(), we ensure that the
|
|
121
121
|
// entire data will be preloaded, or the loading will fail due to
|
|
122
122
|
// throwIfResIsBigger limitation, whichever will happen faster.
|
|
123
|
-
Number.MAX_SAFE_INTEGER
|
|
123
|
+
Number.MAX_SAFE_INTEGER,
|
|
124
124
|
);
|
|
125
125
|
await stream.close();
|
|
126
126
|
return stream.res;
|
|
@@ -182,7 +182,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
182
182
|
await reader?.close();
|
|
183
183
|
throw e;
|
|
184
184
|
}
|
|
185
|
-
}
|
|
185
|
+
},
|
|
186
186
|
);
|
|
187
187
|
|
|
188
188
|
// The only place where we return the response. Otherwise we retry or
|
|
@@ -215,7 +215,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
215
215
|
error,
|
|
216
216
|
this.options,
|
|
217
217
|
res,
|
|
218
|
-
retryDelayMs
|
|
218
|
+
retryDelayMs,
|
|
219
219
|
);
|
|
220
220
|
if (newRetryDelay === "no_retry") {
|
|
221
221
|
throw error;
|
|
@@ -228,7 +228,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
228
228
|
retryDelayMs *= random(
|
|
229
229
|
1 - this.options.retryDelayJitter,
|
|
230
230
|
1 + this.options.retryDelayJitter,
|
|
231
|
-
true
|
|
231
|
+
true,
|
|
232
232
|
);
|
|
233
233
|
await this.options.heartbeater.delay(retryDelayMs);
|
|
234
234
|
|
|
@@ -279,7 +279,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
279
279
|
const isInternal = range !== "unicast";
|
|
280
280
|
if (isInternal) {
|
|
281
281
|
throw new RestError(
|
|
282
|
-
`Domain ${hostname} resolves to a non-public (${range}) IP address ${addr.address}
|
|
282
|
+
`Domain ${hostname} resolves to a non-public (${range}) IP address ${addr.address}`,
|
|
283
283
|
);
|
|
284
284
|
}
|
|
285
285
|
|
|
@@ -333,7 +333,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
333
333
|
onTimeout: (reader) => {
|
|
334
334
|
throw new RestTimeoutError(
|
|
335
335
|
`Timed out while reading response body (${this.options.timeoutMs} ms)`,
|
|
336
|
-
this._createRestResponse(reader)
|
|
336
|
+
this._createRestResponse(reader)!,
|
|
337
337
|
);
|
|
338
338
|
},
|
|
339
339
|
onAfterRead: (reader) => {
|
|
@@ -343,7 +343,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
343
343
|
) {
|
|
344
344
|
throw new RestContentSizeOverLimitError(
|
|
345
345
|
`Content size is over limit of ${this.options.throwIfResIsBigger} characters`,
|
|
346
|
-
this._createRestResponse(reader)
|
|
346
|
+
this._createRestResponse(reader)!,
|
|
347
347
|
);
|
|
348
348
|
}
|
|
349
349
|
},
|
|
@@ -361,7 +361,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
361
361
|
reader.status,
|
|
362
362
|
reader.headers,
|
|
363
363
|
reader.textFetched,
|
|
364
|
-
reader.textIsPartial
|
|
364
|
+
reader.textIsPartial,
|
|
365
365
|
);
|
|
366
366
|
}
|
|
367
367
|
|
|
@@ -405,7 +405,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
405
405
|
inspectPossibleJSON(
|
|
406
406
|
event.res.headers,
|
|
407
407
|
event.res.text,
|
|
408
|
-
MAX_DEBUG_LEN
|
|
408
|
+
MAX_DEBUG_LEN,
|
|
409
409
|
);
|
|
410
410
|
}
|
|
411
411
|
} else if (event.exception) {
|
|
@@ -430,7 +430,7 @@ export default class RestRequest<TAssertShape = any> {
|
|
|
430
430
|
async function runMiddlewares(
|
|
431
431
|
req: RestRequest,
|
|
432
432
|
middlewares: RestOptions["middlewares"],
|
|
433
|
-
last: (req: RestRequest) => Promise<RestResponse
|
|
433
|
+
last: (req: RestRequest) => Promise<RestResponse>,
|
|
434
434
|
): Promise<RestResponse> {
|
|
435
435
|
if (middlewares.length > 0) {
|
|
436
436
|
const [head, ...tail] = middlewares;
|
package/src/RestResponse.ts
CHANGED
package/src/RestStream.ts
CHANGED
|
@@ -2,7 +2,11 @@ import type RestResponse from "../RestResponse";
|
|
|
2
2
|
import RestResponseError from "./RestResponseError";
|
|
3
3
|
|
|
4
4
|
export default class RestRateLimitError extends RestResponseError {
|
|
5
|
-
constructor(
|
|
5
|
+
constructor(
|
|
6
|
+
message: string,
|
|
7
|
+
public delayMs: number,
|
|
8
|
+
res: RestResponse,
|
|
9
|
+
) {
|
|
6
10
|
super(message, res);
|
|
7
11
|
}
|
|
8
12
|
}
|
|
@@ -21,8 +21,8 @@ export default class RestResponseError extends RestError {
|
|
|
21
21
|
`HTTP ${res.status}: ` +
|
|
22
22
|
(message ? `${message}: ` : "") +
|
|
23
23
|
prependNewlineIfMultiline(
|
|
24
|
-
inspectPossibleJSON(res.headers, res.text, RESPONSE_MAX_LEN_IN_ERROR)
|
|
25
|
-
)
|
|
24
|
+
inspectPossibleJSON(res.headers, res.text, RESPONSE_MAX_LEN_IN_ERROR),
|
|
25
|
+
),
|
|
26
26
|
);
|
|
27
27
|
Object.defineProperty(this, "res", { value: res, enumerable: false }); // hidden from inspect()
|
|
28
28
|
const url = new URL(res.req.url);
|
|
@@ -34,7 +34,7 @@ export default class RestResponseError extends RestError {
|
|
|
34
34
|
this.requestBody = inspectPossibleJSON(
|
|
35
35
|
res.headers,
|
|
36
36
|
res.req.body,
|
|
37
|
-
RESPONSE_MAX_LEN_IN_ERROR
|
|
37
|
+
RESPONSE_MAX_LEN_IN_ERROR,
|
|
38
38
|
);
|
|
39
39
|
this.responseHeaders = [...res.headers]
|
|
40
40
|
.map(([name, value]) => `${name}: ${value}`)
|
|
@@ -2,7 +2,11 @@ import type RestResponse from "../RestResponse";
|
|
|
2
2
|
import RestResponseError from "./RestResponseError";
|
|
3
3
|
|
|
4
4
|
export default class RestRetriableError extends RestResponseError {
|
|
5
|
-
constructor(
|
|
5
|
+
constructor(
|
|
6
|
+
message: string,
|
|
7
|
+
public delayMs: number,
|
|
8
|
+
res: RestResponse,
|
|
9
|
+
) {
|
|
6
10
|
super(message, res);
|
|
7
11
|
}
|
|
8
12
|
}
|
|
@@ -2,7 +2,10 @@ import type RestResponse from "../RestResponse";
|
|
|
2
2
|
import RestResponseError from "./RestResponseError";
|
|
3
3
|
|
|
4
4
|
export default class RestTokenInvalidError extends RestResponseError {
|
|
5
|
-
constructor(
|
|
5
|
+
constructor(
|
|
6
|
+
public readonly humanReason: string,
|
|
7
|
+
res: RestResponse,
|
|
8
|
+
) {
|
|
6
9
|
super(humanReason, res);
|
|
7
10
|
}
|
|
8
11
|
}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export default async function* depaginate<TItem, TCursor = string>(
|
|
10
10
|
readFunc: (
|
|
11
|
-
cursor: TCursor | undefined
|
|
12
|
-
) => Promise<readonly [TItem[], TCursor | null | undefined]
|
|
11
|
+
cursor: TCursor | undefined,
|
|
12
|
+
) => Promise<readonly [TItem[], TCursor | null | undefined]>,
|
|
13
13
|
): AsyncGenerator<TItem, void, undefined> {
|
|
14
14
|
let prevCursor: TCursor | null | undefined = undefined;
|
|
15
15
|
let cursor: TCursor | null | undefined = undefined;
|
|
@@ -28,7 +28,7 @@ export default async function* depaginate<TItem, TCursor = string>(
|
|
|
28
28
|
JSON.stringify(prevCursor) +
|
|
29
29
|
", cursor=" +
|
|
30
30
|
JSON.stringify(cursor) +
|
|
31
|
-
" (they must differ)"
|
|
31
|
+
" (they must differ)",
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -34,7 +34,7 @@ export default class RestFetchReader {
|
|
|
34
34
|
constructor(
|
|
35
35
|
private _url: string,
|
|
36
36
|
private _reqInit: RequestInit,
|
|
37
|
-
private _options: RestFetchReaderOptions
|
|
37
|
+
private _options: RestFetchReaderOptions,
|
|
38
38
|
) {}
|
|
39
39
|
|
|
40
40
|
/**
|
|
@@ -151,7 +151,7 @@ export default class RestFetchReader {
|
|
|
151
151
|
new Request(this._url, {
|
|
152
152
|
...this._reqInit,
|
|
153
153
|
signal: controller.signal as any,
|
|
154
|
-
})
|
|
154
|
+
}),
|
|
155
155
|
);
|
|
156
156
|
this._status = res.status;
|
|
157
157
|
this._headers = res.headers;
|
|
@@ -18,7 +18,7 @@ export default class RestRangeUploader {
|
|
|
18
18
|
private _chunkSize: number,
|
|
19
19
|
private _method: "POST" | "PUT",
|
|
20
20
|
private _path: string,
|
|
21
|
-
private _mimeType: string
|
|
21
|
+
private _mimeType: string,
|
|
22
22
|
) {}
|
|
23
23
|
|
|
24
24
|
async upload(stream: AsyncIterable<Buffer>) {
|
|
@@ -52,7 +52,7 @@ export default class RestRangeUploader {
|
|
|
52
52
|
.writeRaw(this._path, buf, this._mimeType, this._method, "*/*")
|
|
53
53
|
.setHeader(
|
|
54
54
|
"Content-Range",
|
|
55
|
-
`bytes ${this._pos}-${this._pos + buf.length - 1}/${totalSize}
|
|
55
|
+
`bytes ${this._pos}-${this._pos + buf.length - 1}/${totalSize}`,
|
|
56
56
|
)
|
|
57
57
|
.text();
|
|
58
58
|
this._pos += buf.length;
|
|
@@ -13,7 +13,7 @@ export default function calcRetryDelay(
|
|
|
13
13
|
error: any,
|
|
14
14
|
options: RestOptions,
|
|
15
15
|
res: RestResponse,
|
|
16
|
-
retryDelayMs: number
|
|
16
|
+
retryDelayMs: number,
|
|
17
17
|
): number | "no_retry" {
|
|
18
18
|
if (
|
|
19
19
|
error instanceof RestRateLimitError ||
|
|
@@ -22,7 +22,7 @@ export default function calcRetryDelay(
|
|
|
22
22
|
// We've already made a decision to retry this error.
|
|
23
23
|
return Math.min(
|
|
24
24
|
options.retryDelayMaxMs,
|
|
25
|
-
Math.max(retryDelayMs, error.delayMs)
|
|
25
|
+
Math.max(retryDelayMs, error.delayMs),
|
|
26
26
|
);
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -17,17 +17,17 @@ export default function inferResBodyEncoding(res: Response): BufferEncoding {
|
|
|
17
17
|
? // It's a binary Content-Type.
|
|
18
18
|
"binary"
|
|
19
19
|
: charset && !BUFFER_ENCODINGS.includes(charset)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
? // The charset is provided in Content-Type, but unknown by Buffer.
|
|
21
|
+
"binary"
|
|
22
|
+
: charset && BUFFER_ENCODINGS.includes(charset)
|
|
23
|
+
? // Charset is provided in Content-Type header, and Buffer knows
|
|
24
|
+
// how to decode it.
|
|
25
|
+
(charset as BufferEncoding)
|
|
26
|
+
: // An opinionated choice is made here to always default-decode the
|
|
27
|
+
// response stream as UTF-8. This is because JSON is by definition a UTF-8
|
|
28
|
+
// stream, and people often time respond with JSONs forgetting to provide
|
|
29
|
+
// "; charset=utf-8" part of the Content-Type header (or they forget
|
|
30
|
+
// Content-Type header at all, or put some wrong value as "text/plain"
|
|
31
|
+
// there; there is an endless list of mistake variations here).
|
|
32
|
+
"utf-8";
|
|
33
33
|
}
|
|
@@ -5,7 +5,7 @@ import truncate from "lodash/truncate";
|
|
|
5
5
|
export default function inspectPossibleJSON(
|
|
6
6
|
headers: { get(name: string): string | null },
|
|
7
7
|
text: string | Buffer | NodeJS.ReadableStream,
|
|
8
|
-
maxOutputLen: number
|
|
8
|
+
maxOutputLen: number,
|
|
9
9
|
): string {
|
|
10
10
|
const MAX_LEN_TO_TRY_PARSE = 1024 * 1024;
|
|
11
11
|
|
|
@@ -31,13 +31,13 @@ export default function inspectPossibleJSON(
|
|
|
31
31
|
// man's approach: of course not all APIs return error/errors fields at
|
|
32
32
|
// all, but it's hard to reorder at any other layer of abstraction.
|
|
33
33
|
reorderObjectProps(json, (k) =>
|
|
34
|
-
k === "error" || k === "errors" ? "" : k
|
|
34
|
+
k === "error" || k === "errors" ? "" : k,
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
return ellipsis(
|
|
39
39
|
inspect(json, { depth: 20, compact: true }),
|
|
40
|
-
maxOutputLen
|
|
40
|
+
maxOutputLen,
|
|
41
41
|
);
|
|
42
42
|
} catch (e: any) {
|
|
43
43
|
return ellipsis(text, maxOutputLen);
|
|
@@ -53,7 +53,7 @@ export default function inspectPossibleJSON(
|
|
|
53
53
|
*/
|
|
54
54
|
function reorderObjectProps(
|
|
55
55
|
obj: Record<string, any>,
|
|
56
|
-
ranker: (k: string, v: any) => string | number
|
|
56
|
+
ranker: (k: string, v: any) => string | number,
|
|
57
57
|
) {
|
|
58
58
|
const entries = Object.entries(obj);
|
|
59
59
|
for (const k in obj) {
|
|
@@ -62,7 +62,7 @@ function reorderObjectProps(
|
|
|
62
62
|
|
|
63
63
|
Object.assign(
|
|
64
64
|
obj,
|
|
65
|
-
Object.fromEntries(sortBy(entries, ([k, v]) => ranker(k, v)))
|
|
65
|
+
Object.fromEntries(sortBy(entries, ([k, v]) => ranker(k, v))),
|
|
66
66
|
);
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -16,7 +16,7 @@ const STATUS_TOO_MANY_REQUESTS = 429;
|
|
|
16
16
|
*/
|
|
17
17
|
export default function throwIfErrorResponse(
|
|
18
18
|
options: RestOptions,
|
|
19
|
-
res: RestResponse
|
|
19
|
+
res: RestResponse,
|
|
20
20
|
) {
|
|
21
21
|
const isSuccessResponse = options.isSuccessResponse(res);
|
|
22
22
|
if (isSuccessResponse === "SUCCESS") {
|
|
@@ -29,7 +29,7 @@ export default function throwIfErrorResponse(
|
|
|
29
29
|
throw new RestRateLimitError(
|
|
30
30
|
`isRateLimitError() returned ${rateLimitDelayMs}`,
|
|
31
31
|
0,
|
|
32
|
-
res
|
|
32
|
+
res,
|
|
33
33
|
);
|
|
34
34
|
case "BEST_EFFORT":
|
|
35
35
|
if (res.status === STATUS_TOO_MANY_REQUESTS) {
|
|
@@ -37,7 +37,7 @@ export default function throwIfErrorResponse(
|
|
|
37
37
|
throw new RestRateLimitError(
|
|
38
38
|
`Rate limited by HTTP status ${STATUS_TOO_MANY_REQUESTS}`,
|
|
39
39
|
parseInt(retryAfterHeader) || 0,
|
|
40
|
-
res
|
|
40
|
+
res,
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -48,7 +48,7 @@ export default function throwIfErrorResponse(
|
|
|
48
48
|
throw new RestRateLimitError(
|
|
49
49
|
`isRateLimitError() returned retry delay ${rateLimitDelayMs} ms`,
|
|
50
50
|
rateLimitDelayMs,
|
|
51
|
-
res
|
|
51
|
+
res,
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -63,7 +63,7 @@ export default function throwIfErrorResponse(
|
|
|
63
63
|
throw new RestRetriableError(
|
|
64
64
|
`isRetriableError() returned ${retryDelayMs}`,
|
|
65
65
|
0,
|
|
66
|
-
res
|
|
66
|
+
res,
|
|
67
67
|
);
|
|
68
68
|
case "BEST_EFFORT":
|
|
69
69
|
case "NEVER_RETRY":
|
|
@@ -72,14 +72,14 @@ export default function throwIfErrorResponse(
|
|
|
72
72
|
throw new RestRetriableError(
|
|
73
73
|
`"isRetriableError() returned retry delay ${retryDelayMs} ms`,
|
|
74
74
|
retryDelayMs,
|
|
75
|
-
res
|
|
75
|
+
res,
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
if (isSuccessResponse === "THROW") {
|
|
80
80
|
throw new RestResponseError(
|
|
81
81
|
`isSuccessResponse() returned ${isSuccessResponse}`,
|
|
82
|
-
res
|
|
82
|
+
res,
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -9,7 +9,7 @@ const MIN_LOG_DELAY_MS = 10;
|
|
|
9
9
|
* Pacer implementations.
|
|
10
10
|
*/
|
|
11
11
|
export default function paceRequests(
|
|
12
|
-
pacer: Pacer | ((req: RestRequest) => Promise<Pacer | null>) | null
|
|
12
|
+
pacer: Pacer | ((req: RestRequest) => Promise<Pacer | null>) | null,
|
|
13
13
|
): Middleware {
|
|
14
14
|
return async (req, next) => {
|
|
15
15
|
if (typeof pacer === "function") {
|
package/src/pacers/PacerQPS.ts
CHANGED
|
@@ -73,7 +73,7 @@ export default class PacerQPS implements Pacer {
|
|
|
73
73
|
|
|
74
74
|
constructor(
|
|
75
75
|
private _options: PacerQPSOptions,
|
|
76
|
-
private _backend: PacerQPSBackend
|
|
76
|
+
private _backend: PacerQPSBackend,
|
|
77
77
|
) {}
|
|
78
78
|
|
|
79
79
|
get name() {
|
|
@@ -107,7 +107,7 @@ export default class PacerQPS implements Pacer {
|
|
|
107
107
|
// situation, when we don't know much about the entire fleet average delay
|
|
108
108
|
// yet, or when this delay is too small to count on.
|
|
109
109
|
const singleWorkerDelayStepMs = Math.round(
|
|
110
|
-
((windowSec * 1000) / limit) * DELAY_AVG_TO_STEP_FACTOR
|
|
110
|
+
((windowSec * 1000) / limit) * DELAY_AVG_TO_STEP_FACTOR,
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
// Considering that there are multiple workers running, and that the current
|
|
@@ -116,7 +116,7 @@ export default class PacerQPS implements Pacer {
|
|
|
116
116
|
const multiWorkerDelayStepMs = Math.round(
|
|
117
117
|
avg *
|
|
118
118
|
DELAY_AVG_TO_STEP_FACTOR *
|
|
119
|
-
random(1 - DELAY_STEP_JITTER, 1 + DELAY_STEP_JITTER, true)
|
|
119
|
+
random(1 - DELAY_STEP_JITTER, 1 + DELAY_STEP_JITTER, true),
|
|
120
120
|
);
|
|
121
121
|
|
|
122
122
|
// If average fleet delay is not representative yet, we fallback to a
|
package/tsconfig.json
CHANGED
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
"esModuleInterop": true,
|
|
10
10
|
"experimentalDecorators": true,
|
|
11
11
|
"incremental": true,
|
|
12
|
-
"lib": ["
|
|
13
|
-
"module": "
|
|
14
|
-
"moduleResolution": "node",
|
|
12
|
+
"lib": ["ES2019"],
|
|
13
|
+
"module": "Node16",
|
|
15
14
|
"noEmitOnError": true,
|
|
16
15
|
"noErrorTruncation": true,
|
|
17
16
|
"noImplicitOverride": true,
|
|
@@ -25,7 +24,7 @@
|
|
|
25
24
|
"skipLibCheck": true,
|
|
26
25
|
"sourceMap": true,
|
|
27
26
|
"strict": true,
|
|
28
|
-
"target": "
|
|
27
|
+
"target": "ES2019",
|
|
29
28
|
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
|
|
30
29
|
"types": ["node", "jest"]
|
|
31
30
|
}
|