@clickup/rest-client 2.10.294 → 2.11.0
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 +12 -2
- package/.eslintrc.js +4 -4
- package/.github/workflows/ci.yml +26 -0
- package/.github/workflows/semgrep.yml +36 -0
- package/.prettierrc +8 -0
- package/.vscode/extensions.json +8 -0
- package/.vscode/tasks.json +20 -0
- package/README.md +2 -0
- package/dist/.eslintcache +1 -1
- package/dist/RestClient.js +2 -2
- package/dist/RestClient.js.map +1 -1
- package/dist/RestOptions.d.ts +3 -0
- package/dist/RestOptions.d.ts.map +1 -1
- package/dist/RestOptions.js +1 -0
- package/dist/RestOptions.js.map +1 -1
- package/dist/RestRequest.d.ts.map +1 -1
- package/dist/RestRequest.js +1 -0
- package/dist/RestRequest.js.map +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/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/RestFetchReader.d.ts +4 -2
- package/dist/internal/RestFetchReader.d.ts.map +1 -1
- package/dist/internal/RestFetchReader.js +4 -4
- package/dist/internal/RestFetchReader.js.map +1 -1
- package/dist/internal/ellipsis.d.ts +6 -0
- package/dist/internal/ellipsis.d.ts.map +1 -0
- package/dist/internal/ellipsis.js +17 -0
- package/dist/internal/ellipsis.js.map +1 -0
- package/dist/internal/inferResBodyEncoding.js.map +1 -1
- package/dist/internal/inspectPossibleJSON.js +5 -8
- package/dist/internal/inspectPossibleJSON.js.map +1 -1
- package/dist/internal/throwIfErrorResponse.js +1 -1
- package/dist/internal/throwIfErrorResponse.js.map +1 -1
- package/dist/middlewares/paceRequests.d.ts +21 -2
- package/dist/middlewares/paceRequests.d.ts.map +1 -1
- package/dist/middlewares/paceRequests.js +3 -2
- package/dist/middlewares/paceRequests.js.map +1 -1
- package/docs/README.md +2 -0
- 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 +7 -12
- package/docs/interfaces/PacerOutcome.md +25 -0
- package/docs/interfaces/RestLogEvent.md +1 -1
- package/docs/interfaces/RestOptions.md +42 -30
- package/docs/interfaces/TokenGetter.md +3 -3
- package/docs/modules.md +8 -11
- package/internal/clean.sh +4 -0
- package/internal/deploy.sh +7 -0
- package/internal/docs.sh +6 -0
- package/internal/lint.sh +4 -0
- package/jest.config.base.js +13 -0
- package/jest.config.js +1 -10
- package/package.json +10 -6
- package/src/RestClient.ts +21 -21
- package/src/RestOptions.ts +6 -3
- package/src/RestRequest.ts +12 -11
- package/src/RestResponse.ts +1 -1
- package/src/RestStream.ts +1 -1
- package/src/__tests__/RestClient.test.ts +53 -0
- package/src/__tests__/RestFetchReader.test.ts +150 -0
- package/src/__tests__/RestRequest.test.ts +262 -0
- package/src/__tests__/RestStream.test.ts +63 -0
- package/src/__tests__/helpers.ts +173 -0
- 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/index.ts +2 -7
- package/src/internal/RestFetchReader.ts +6 -3
- package/src/internal/RestRangeUploader.ts +2 -2
- package/src/internal/calcRetryDelay.ts +2 -2
- package/src/internal/ellipsis.ts +16 -0
- package/src/internal/inferResBodyEncoding.ts +13 -13
- package/src/internal/inspectPossibleJSON.ts +6 -10
- package/src/internal/substituteParams.ts +1 -1
- package/src/internal/throwIfErrorResponse.ts +8 -8
- package/src/middlewares/paceRequests.ts +28 -3
- package/tsconfig.base.json +31 -0
- package/tsconfig.json +1 -32
- package/typedoc.config.js +20 -0
- package/dist/pacers/Pacer.d.ts +0 -21
- package/dist/pacers/Pacer.d.ts.map +0 -1
- package/dist/pacers/Pacer.js +0 -3
- package/dist/pacers/Pacer.js.map +0 -1
- package/dist/pacers/PacerComposite.d.ts +0 -14
- package/dist/pacers/PacerComposite.d.ts.map +0 -1
- package/dist/pacers/PacerComposite.js +0 -32
- package/dist/pacers/PacerComposite.js.map +0 -1
- package/dist/pacers/PacerQPS.d.ts +0 -53
- package/dist/pacers/PacerQPS.d.ts.map +0 -1
- package/dist/pacers/PacerQPS.js +0 -105
- package/dist/pacers/PacerQPS.js.map +0 -1
- package/docs/classes/PacerComposite.md +0 -62
- package/docs/classes/PacerQPS.md +0 -75
- package/docs/interfaces/PacerDelay.md +0 -25
- package/docs/interfaces/PacerQPSBackend.md +0 -44
- package/docs/interfaces/PacerQPSOptions.md +0 -40
- package/src/pacers/Pacer.ts +0 -22
- package/src/pacers/PacerComposite.ts +0 -29
- package/src/pacers/PacerQPS.ts +0 -147
- package/typedoc.json +0 -22
|
@@ -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
|
|
package/src/index.ts
CHANGED
|
@@ -7,10 +7,7 @@ import RestRetriableError from "./errors/RestRetriableError";
|
|
|
7
7
|
import RestTimeoutError from "./errors/RestTimeoutError";
|
|
8
8
|
import RestTokenInvalidError from "./errors/RestTokenInvalidError";
|
|
9
9
|
import depaginate from "./helpers/depaginate";
|
|
10
|
-
import paceRequests from "./middlewares/paceRequests";
|
|
11
|
-
import Pacer from "./pacers/Pacer";
|
|
12
|
-
import PacerComposite from "./pacers/PacerComposite";
|
|
13
|
-
import PacerQPS, { PacerQPSBackend } from "./pacers/PacerQPS";
|
|
10
|
+
import paceRequests, { Pacer, PacerOutcome } from "./middlewares/paceRequests";
|
|
14
11
|
import RestClient, { TokenGetter } from "./RestClient";
|
|
15
12
|
import RestOptions, {
|
|
16
13
|
RestLogEvent,
|
|
@@ -29,10 +26,8 @@ export {
|
|
|
29
26
|
Headers,
|
|
30
27
|
Middleware,
|
|
31
28
|
Pacer,
|
|
32
|
-
PacerComposite,
|
|
33
29
|
paceRequests,
|
|
34
|
-
|
|
35
|
-
PacerQPSBackend,
|
|
30
|
+
PacerOutcome,
|
|
36
31
|
RestClient,
|
|
37
32
|
RestContentSizeOverLimitError,
|
|
38
33
|
RestError,
|
|
@@ -10,6 +10,7 @@ export interface RestFetchReaderOptions {
|
|
|
10
10
|
heartbeat?: () => Promise<void>;
|
|
11
11
|
onTimeout?: (reader: RestFetchReader, e: any) => void;
|
|
12
12
|
onAfterRead?: (reader: RestFetchReader) => void;
|
|
13
|
+
responseEncoding?: NodeJS.BufferEncoding;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -34,7 +35,7 @@ export default class RestFetchReader {
|
|
|
34
35
|
constructor(
|
|
35
36
|
private _url: string,
|
|
36
37
|
private _reqInit: RequestInit,
|
|
37
|
-
private _options: RestFetchReaderOptions
|
|
38
|
+
private _options: RestFetchReaderOptions,
|
|
38
39
|
) {}
|
|
39
40
|
|
|
40
41
|
/**
|
|
@@ -151,7 +152,7 @@ export default class RestFetchReader {
|
|
|
151
152
|
new Request(this._url, {
|
|
152
153
|
...this._reqInit,
|
|
153
154
|
signal: controller.signal as any,
|
|
154
|
-
})
|
|
155
|
+
}),
|
|
155
156
|
);
|
|
156
157
|
this._status = res.status;
|
|
157
158
|
this._headers = res.headers;
|
|
@@ -160,7 +161,9 @@ export default class RestFetchReader {
|
|
|
160
161
|
// how Node streams and setEncoding() handle decoding when the returned
|
|
161
162
|
// chunks cross the boundaries of multi-byte characters (TL;DR: it works
|
|
162
163
|
// fine, that's why we work with string and not Buffer here).
|
|
163
|
-
res.body.setEncoding(
|
|
164
|
+
res.body.setEncoding(
|
|
165
|
+
this._options.responseEncoding ?? inferResBodyEncoding(res),
|
|
166
|
+
);
|
|
164
167
|
|
|
165
168
|
await this._options.heartbeat?.();
|
|
166
169
|
for await (const chunk of res.body) {
|
|
@@ -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
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const ELLIPSIS = "…";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The fastest possible version of truncation. Lodash'es truncate() messes up
|
|
5
|
+
* with unicode a lot, so for e.g. logging purposes, it's super-slow.
|
|
6
|
+
*/
|
|
7
|
+
export default function ellipsis(string: any, length: number) {
|
|
8
|
+
string = ("" + string).trimEnd();
|
|
9
|
+
length = Math.max(length, ELLIPSIS.length);
|
|
10
|
+
|
|
11
|
+
if (string.length <= length) {
|
|
12
|
+
return string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return string.substring(0, length - ELLIPSIS.length) + ELLIPSIS;
|
|
16
|
+
}
|
|
@@ -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
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { inspect } from "util";
|
|
2
2
|
import sortBy from "lodash/sortBy";
|
|
3
|
-
import
|
|
3
|
+
import ellipsis from "./ellipsis";
|
|
4
4
|
|
|
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,10 +62,6 @@ 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
|
-
|
|
69
|
-
function ellipsis(text: any, length: number) {
|
|
70
|
-
return truncate("" + text, { length }).trimEnd();
|
|
71
|
-
}
|
|
@@ -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,15 +29,15 @@ 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) {
|
|
36
36
|
const retryAfterHeader = res.headers.get("Retry-After") || "0";
|
|
37
37
|
throw new RestRateLimitError(
|
|
38
38
|
`Rate limited by HTTP status ${STATUS_TOO_MANY_REQUESTS}`,
|
|
39
|
-
parseInt(retryAfterHeader) || 0,
|
|
40
|
-
res
|
|
39
|
+
1000 * parseInt(retryAfterHeader) || 0,
|
|
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
|
|
|
@@ -1,15 +1,38 @@
|
|
|
1
|
-
import type Pacer from "../pacers/Pacer";
|
|
2
1
|
import type { Middleware } from "../RestOptions";
|
|
3
2
|
import type RestRequest from "../RestRequest";
|
|
4
3
|
|
|
5
4
|
const MIN_LOG_DELAY_MS = 10;
|
|
6
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Pacer is a class which allows to pace requests on some resource identified by
|
|
8
|
+
* the instance of this class.
|
|
9
|
+
*/
|
|
10
|
+
export interface Pacer {
|
|
11
|
+
/** Human readable name of the pacer, used when composing multiple pacers. */
|
|
12
|
+
readonly key: string;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Signals that we're about to send a request. Returns the delay we need to
|
|
16
|
+
* wait for before actually sending.
|
|
17
|
+
*/
|
|
18
|
+
pace(): Promise<PacerOutcome>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A result of some Pacer work.
|
|
23
|
+
*/
|
|
24
|
+
export interface PacerOutcome {
|
|
25
|
+
delayMs: number;
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
7
29
|
/**
|
|
8
30
|
* Rest Client middleware that adds some delay between requests using one of
|
|
9
31
|
* Pacer implementations.
|
|
10
32
|
*/
|
|
11
33
|
export default function paceRequests(
|
|
12
|
-
pacer: Pacer | ((req: RestRequest) => Promise<Pacer | null>) | null
|
|
34
|
+
pacer: Pacer | ((req: RestRequest) => Promise<Pacer | null>) | null,
|
|
35
|
+
delayMetric?: (delay: number, reason: string) => void,
|
|
13
36
|
): Middleware {
|
|
14
37
|
return async (req, next) => {
|
|
15
38
|
if (typeof pacer === "function") {
|
|
@@ -17,8 +40,10 @@ export default function paceRequests(
|
|
|
17
40
|
}
|
|
18
41
|
|
|
19
42
|
if (pacer) {
|
|
20
|
-
const { delayMs, reason } = await pacer.
|
|
43
|
+
const { delayMs, reason } = await pacer.pace();
|
|
44
|
+
|
|
21
45
|
if (delayMs > 0) {
|
|
46
|
+
delayMetric?.(delayMs, reason);
|
|
22
47
|
await req.options.heartbeater.delay(delayMs);
|
|
23
48
|
}
|
|
24
49
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["src/**/*"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"allowJs": true,
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"disableReferencedProjectLoad": true,
|
|
8
|
+
"disableSourceOfProjectReferenceRedirect": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"experimentalDecorators": true,
|
|
11
|
+
"incremental": true,
|
|
12
|
+
"lib": ["ES2019"],
|
|
13
|
+
"module": "Node16",
|
|
14
|
+
"noEmitOnError": true,
|
|
15
|
+
"noErrorTruncation": true,
|
|
16
|
+
"noImplicitOverride": true,
|
|
17
|
+
"noImplicitReturns": true,
|
|
18
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
19
|
+
"outDir": "dist",
|
|
20
|
+
"pretty": true,
|
|
21
|
+
"removeComments": false,
|
|
22
|
+
"resolveJsonModule": true,
|
|
23
|
+
"rootDir": "src",
|
|
24
|
+
"skipLibCheck": true,
|
|
25
|
+
"sourceMap": true,
|
|
26
|
+
"strict": true,
|
|
27
|
+
"target": "ES2019",
|
|
28
|
+
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
|
|
29
|
+
"types": ["node", "jest"]
|
|
30
|
+
}
|
|
31
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,32 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"include": ["src/**/*"],
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"allowJs": true,
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"declarationMap": true,
|
|
7
|
-
"disableReferencedProjectLoad": true,
|
|
8
|
-
"disableSourceOfProjectReferenceRedirect": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"experimentalDecorators": true,
|
|
11
|
-
"incremental": true,
|
|
12
|
-
"lib": ["es2019"],
|
|
13
|
-
"module": "commonjs",
|
|
14
|
-
"moduleResolution": "node",
|
|
15
|
-
"noEmitOnError": true,
|
|
16
|
-
"noErrorTruncation": true,
|
|
17
|
-
"noImplicitOverride": true,
|
|
18
|
-
"noImplicitReturns": true,
|
|
19
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
20
|
-
"outDir": "dist",
|
|
21
|
-
"pretty": true,
|
|
22
|
-
"removeComments": false,
|
|
23
|
-
"resolveJsonModule": true,
|
|
24
|
-
"rootDir": "src",
|
|
25
|
-
"skipLibCheck": true,
|
|
26
|
-
"sourceMap": true,
|
|
27
|
-
"strict": true,
|
|
28
|
-
"target": "es2019",
|
|
29
|
-
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
|
|
30
|
-
"types": ["node", "jest"]
|
|
31
|
-
}
|
|
32
|
-
}
|
|
1
|
+
{ "extends": "./tsconfig.base.json" }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const { basename } = require("path");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
entryPoints: ["src"],
|
|
6
|
+
exclude: ["**/internal/**", "**/__tests__/**", "**/node_modules/**"],
|
|
7
|
+
entryPointStrategy: "expand",
|
|
8
|
+
mergeModulesMergeMode: "project",
|
|
9
|
+
sort: ["source-order"],
|
|
10
|
+
out: "docs",
|
|
11
|
+
logLevel: "Warn",
|
|
12
|
+
hideGenerator: true,
|
|
13
|
+
excludeInternal: true,
|
|
14
|
+
excludePrivate: true,
|
|
15
|
+
categorizeByGroup: true,
|
|
16
|
+
hideInPageTOC: true,
|
|
17
|
+
gitRevision: "master",
|
|
18
|
+
sourceLinkTemplate: `https://github.com/clickup/${basename(__dirname)}/blob/master/{path}#L{line}`,
|
|
19
|
+
basePath: ".",
|
|
20
|
+
};
|
package/dist/pacers/Pacer.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A result of some Pacer work.
|
|
3
|
-
*/
|
|
4
|
-
export interface PacerDelay {
|
|
5
|
-
delayMs: number;
|
|
6
|
-
reason: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Pacer is a class which allows to pace requests on some resource identified by
|
|
10
|
-
* the instance of this class.
|
|
11
|
-
*/
|
|
12
|
-
export default interface Pacer {
|
|
13
|
-
/** Human readable name of the pacer, used when composing multiple pacers. */
|
|
14
|
-
readonly name: string;
|
|
15
|
-
/**
|
|
16
|
-
* Signals that we're about to send a request. Returns the delay we need to
|
|
17
|
-
* wait for before actually sending.
|
|
18
|
-
*/
|
|
19
|
-
touch(): Promise<PacerDelay>;
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=Pacer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Pacer.d.ts","sourceRoot":"","sources":["../../src/pacers/Pacer.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,WAAW,KAAK;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAC9B"}
|
package/dist/pacers/Pacer.js
DELETED
package/dist/pacers/Pacer.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Pacer.js","sourceRoot":"","sources":["../../src/pacers/Pacer.ts"],"names":[],"mappings":""}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type Pacer from "./Pacer";
|
|
2
|
-
/**
|
|
3
|
-
* A Pacer which runs all sub-pacers and chooses the largest delay.
|
|
4
|
-
*/
|
|
5
|
-
export default class PacerComposite implements Pacer {
|
|
6
|
-
private _pacers;
|
|
7
|
-
readonly name = "";
|
|
8
|
-
constructor(_pacers: Pacer[]);
|
|
9
|
-
touch(): Promise<{
|
|
10
|
-
reason: string;
|
|
11
|
-
delayMs: number;
|
|
12
|
-
}>;
|
|
13
|
-
}
|
|
14
|
-
//# sourceMappingURL=PacerComposite.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PacerComposite.d.ts","sourceRoot":"","sources":["../../src/pacers/PacerComposite.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAEjC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,YAAW,KAAK;IAGtC,OAAO,CAAC,OAAO;IAF3B,QAAQ,CAAC,IAAI,MAAM;gBAEC,OAAO,EAAE,KAAK,EAAE;IAE9B,KAAK;;;;CAiBZ"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const maxBy_1 = __importDefault(require("lodash/maxBy"));
|
|
7
|
-
/**
|
|
8
|
-
* A Pacer which runs all sub-pacers and chooses the largest delay.
|
|
9
|
-
*/
|
|
10
|
-
class PacerComposite {
|
|
11
|
-
constructor(_pacers) {
|
|
12
|
-
this._pacers = _pacers;
|
|
13
|
-
this.name = "";
|
|
14
|
-
}
|
|
15
|
-
async touch() {
|
|
16
|
-
const delays = await Promise["all"](this._pacers.map(async (pacer) => ({
|
|
17
|
-
pacer,
|
|
18
|
-
delay: await pacer.touch(),
|
|
19
|
-
})));
|
|
20
|
-
const pair = (0, maxBy_1.default)(delays, ({ delay }) => delay.delayMs);
|
|
21
|
-
return pair
|
|
22
|
-
? {
|
|
23
|
-
...pair.delay,
|
|
24
|
-
reason: pair.pacer.name
|
|
25
|
-
? `${pair.pacer.constructor.name} ${pair.pacer.name}\n${pair.delay.reason}`
|
|
26
|
-
: pair.delay.reason,
|
|
27
|
-
}
|
|
28
|
-
: { delayMs: 0, reason: "no pacers" };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
exports.default = PacerComposite;
|
|
32
|
-
//# sourceMappingURL=PacerComposite.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PacerComposite.js","sourceRoot":"","sources":["../../src/pacers/PacerComposite.ts"],"names":[],"mappings":";;;;;AAAA,yDAAiC;AAGjC;;GAEG;AACH,MAAqB,cAAc;IAGjC,YAAoB,OAAgB;QAAhB,YAAO,GAAP,OAAO,CAAS;QAF3B,SAAI,GAAG,EAAE,CAAC;IAEoB,CAAC;IAExC,KAAK,CAAC,KAAK;QACT,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACjC,KAAK;YACL,KAAK,EAAE,MAAM,KAAK,CAAC,KAAK,EAAE;SAC3B,CAAC,CAAC,CACJ,CAAC;QACF,MAAM,IAAI,GAAG,IAAA,eAAK,EAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACzD,OAAO,IAAI;YACT,CAAC,CAAC;gBACE,GAAG,IAAI,CAAC,KAAK;gBACb,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;oBACrB,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;oBAC3E,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM;aACtB;YACH,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC1C,CAAC;CACF;AAtBD,iCAsBC"}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { PacerDelay } from "./Pacer";
|
|
2
|
-
import type Pacer from "./Pacer";
|
|
3
|
-
export interface PacerQPSBackend {
|
|
4
|
-
/** Resource key which this backend is operating on. */
|
|
5
|
-
readonly key: string;
|
|
6
|
-
/**
|
|
7
|
-
* Maintains the array of numbers somewhere in memory (time-value pairs),
|
|
8
|
-
* inserts a new time-value pair to the end of this list, and removes all the
|
|
9
|
-
* entries which are earlier than `minTime`. Returns the size of the resulting
|
|
10
|
-
* array and some central tendency statistics about its values.
|
|
11
|
-
*/
|
|
12
|
-
push(props: {
|
|
13
|
-
time: number;
|
|
14
|
-
minTime: number;
|
|
15
|
-
value: number;
|
|
16
|
-
minCountForCentralTendency: number;
|
|
17
|
-
}): Promise<{
|
|
18
|
-
count: number;
|
|
19
|
-
sum: number;
|
|
20
|
-
avg: number;
|
|
21
|
-
median: number;
|
|
22
|
-
}>;
|
|
23
|
-
}
|
|
24
|
-
export interface PacerQPSOptions {
|
|
25
|
-
/** The maximum QPS allowed within the rolling window. */
|
|
26
|
-
qps: number;
|
|
27
|
-
/** The length of the rolling windows in milliseconds. */
|
|
28
|
-
windowSec?: number;
|
|
29
|
-
/** Decrease the delay if the number of requests in the window has dropped
|
|
30
|
-
* below `decreaseThreshold` portion of the limit. */
|
|
31
|
-
decreaseThreshold?: number;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Implements a very simple heuristic:
|
|
35
|
-
* - increase the delay if we're above the QPS within the rolling window;
|
|
36
|
-
* - decrease the delay if we're below the desired QPS.
|
|
37
|
-
*
|
|
38
|
-
* Each worker keeps (and grows/shrinks) its delay individually; this way, we
|
|
39
|
-
* don't need to elect, who's the "source of truth" for the delay.
|
|
40
|
-
*
|
|
41
|
-
* Backend is a concrete (and minimal) implementation of the storage logic for
|
|
42
|
-
* the pacing algorithm.
|
|
43
|
-
*/
|
|
44
|
-
export default class PacerQPS implements Pacer {
|
|
45
|
-
private _options;
|
|
46
|
-
private _backend;
|
|
47
|
-
private _isFirstTouch;
|
|
48
|
-
private _delay;
|
|
49
|
-
constructor(_options: PacerQPSOptions, _backend: PacerQPSBackend);
|
|
50
|
-
get name(): string;
|
|
51
|
-
touch(): Promise<PacerDelay>;
|
|
52
|
-
}
|
|
53
|
-
//# sourceMappingURL=PacerQPS.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PacerQPS.d.ts","sourceRoot":"","sources":["../../src/pacers/PacerQPS.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AA4BjC,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,0BAA0B,EAAE,MAAM,CAAC;KACpC,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;yDACqD;IACrD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,OAAO,QAAS,YAAW,KAAK;IAK1C,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ;IALlB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAAK;gBAGT,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,eAAe;IAGnC,IAAI,IAAI,WAEP;IAEK,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;CAgEnC"}
|