@clickup/rest-client 2.10.292
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 +412 -0
- package/.eslintrc.js +5 -0
- package/LICENSE +22 -0
- package/README.md +121 -0
- package/dist/RestClient.d.ts +154 -0
- package/dist/RestClient.d.ts.map +1 -0
- package/dist/RestClient.js +361 -0
- package/dist/RestClient.js.map +1 -0
- package/dist/RestOptions.d.ts +143 -0
- package/dist/RestOptions.d.ts.map +1 -0
- package/dist/RestOptions.js +63 -0
- package/dist/RestOptions.js.map +1 -0
- package/dist/RestRequest.d.ts +81 -0
- package/dist/RestRequest.d.ts.map +1 -0
- package/dist/RestRequest.js +367 -0
- package/dist/RestRequest.js.map +1 -0
- package/dist/RestResponse.d.ts +37 -0
- package/dist/RestResponse.d.ts.map +1 -0
- package/dist/RestResponse.js +56 -0
- package/dist/RestResponse.js.map +1 -0
- package/dist/RestStream.d.ts +29 -0
- package/dist/RestStream.d.ts.map +1 -0
- package/dist/RestStream.js +85 -0
- package/dist/RestStream.js.map +1 -0
- package/dist/errors/RestContentSizeOverLimitError.d.ts +4 -0
- package/dist/errors/RestContentSizeOverLimitError.d.ts.map +1 -0
- package/dist/errors/RestContentSizeOverLimitError.js +10 -0
- package/dist/errors/RestContentSizeOverLimitError.js.map +1 -0
- package/dist/errors/RestError.d.ts +4 -0
- package/dist/errors/RestError.d.ts.map +1 -0
- package/dist/errors/RestError.js +10 -0
- package/dist/errors/RestError.js.map +1 -0
- package/dist/errors/RestRateLimitError.d.ts +7 -0
- package/dist/errors/RestRateLimitError.d.ts.map +1 -0
- package/dist/errors/RestRateLimitError.js +14 -0
- package/dist/errors/RestRateLimitError.js.map +1 -0
- package/dist/errors/RestResponseError.d.ts +13 -0
- package/dist/errors/RestResponseError.d.ts.map +1 -0
- package/dist/errors/RestResponseError.js +32 -0
- package/dist/errors/RestResponseError.js.map +1 -0
- package/dist/errors/RestRetriableError.d.ts +7 -0
- package/dist/errors/RestRetriableError.d.ts.map +1 -0
- package/dist/errors/RestRetriableError.js +14 -0
- package/dist/errors/RestRetriableError.js.map +1 -0
- package/dist/errors/RestTimeoutError.d.ts +4 -0
- package/dist/errors/RestTimeoutError.d.ts.map +1 -0
- package/dist/errors/RestTimeoutError.js +10 -0
- package/dist/errors/RestTimeoutError.js.map +1 -0
- package/dist/errors/RestTokenInvalidError.d.ts +7 -0
- package/dist/errors/RestTokenInvalidError.d.ts.map +1 -0
- package/dist/errors/RestTokenInvalidError.js +14 -0
- package/dist/errors/RestTokenInvalidError.js.map +1 -0
- package/dist/helpers/depaginate.d.ts +10 -0
- package/dist/helpers/depaginate.d.ts.map +1 -0
- package/dist/helpers/depaginate.js +32 -0
- package/dist/helpers/depaginate.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/RestFetchReader.d.ts +72 -0
- package/dist/internal/RestFetchReader.d.ts.map +1 -0
- package/dist/internal/RestFetchReader.js +192 -0
- package/dist/internal/RestFetchReader.js.map +1 -0
- package/dist/internal/RestRangeUploader.d.ts +24 -0
- package/dist/internal/RestRangeUploader.d.ts.map +1 -0
- package/dist/internal/RestRangeUploader.js +54 -0
- package/dist/internal/RestRangeUploader.js.map +1 -0
- package/dist/internal/calcRetryDelay.d.ts +8 -0
- package/dist/internal/calcRetryDelay.d.ts.map +1 -0
- package/dist/internal/calcRetryDelay.js +44 -0
- package/dist/internal/calcRetryDelay.js.map +1 -0
- package/dist/internal/inspectPossibleJSON.d.ts +6 -0
- package/dist/internal/inspectPossibleJSON.d.ts.map +1 -0
- package/dist/internal/inspectPossibleJSON.js +53 -0
- package/dist/internal/inspectPossibleJSON.js.map +1 -0
- package/dist/internal/prependNewlineIfMultiline.d.ts +2 -0
- package/dist/internal/prependNewlineIfMultiline.d.ts.map +1 -0
- package/dist/internal/prependNewlineIfMultiline.js +7 -0
- package/dist/internal/prependNewlineIfMultiline.js.map +1 -0
- package/dist/internal/substituteParams.d.ts +7 -0
- package/dist/internal/substituteParams.d.ts.map +1 -0
- package/dist/internal/substituteParams.js +24 -0
- package/dist/internal/substituteParams.js.map +1 -0
- package/dist/internal/throwIfErrorResponse.d.ts +11 -0
- package/dist/internal/throwIfErrorResponse.d.ts.map +1 -0
- package/dist/internal/throwIfErrorResponse.js +60 -0
- package/dist/internal/throwIfErrorResponse.js.map +1 -0
- package/dist/internal/toFloatMs.d.ts +2 -0
- package/dist/internal/toFloatMs.d.ts.map +1 -0
- package/dist/internal/toFloatMs.js +7 -0
- package/dist/internal/toFloatMs.js.map +1 -0
- package/dist/middlewares/paceRequests.d.ts +9 -0
- package/dist/middlewares/paceRequests.d.ts.map +1 -0
- package/dist/middlewares/paceRequests.js +36 -0
- package/dist/middlewares/paceRequests.js.map +1 -0
- package/dist/pacers/Pacer.d.ts +21 -0
- package/dist/pacers/Pacer.d.ts.map +1 -0
- package/dist/pacers/Pacer.js +3 -0
- package/dist/pacers/Pacer.js.map +1 -0
- package/dist/pacers/PacerComposite.d.ts +14 -0
- package/dist/pacers/PacerComposite.d.ts.map +1 -0
- package/dist/pacers/PacerComposite.js +32 -0
- package/dist/pacers/PacerComposite.js.map +1 -0
- package/dist/pacers/PacerQPS.d.ts +53 -0
- package/dist/pacers/PacerQPS.d.ts.map +1 -0
- package/dist/pacers/PacerQPS.js +105 -0
- package/dist/pacers/PacerQPS.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/README.md +123 -0
- package/docs/classes/PacerComposite.md +62 -0
- package/docs/classes/PacerQPS.md +75 -0
- package/docs/classes/RestClient.md +424 -0
- package/docs/classes/RestContentSizeOverLimitError.md +128 -0
- package/docs/classes/RestError.md +31 -0
- package/docs/classes/RestRateLimitError.md +139 -0
- package/docs/classes/RestRequest.md +257 -0
- package/docs/classes/RestResponse.md +110 -0
- package/docs/classes/RestResponseError.md +110 -0
- package/docs/classes/RestRetriableError.md +139 -0
- package/docs/classes/RestStream.md +92 -0
- package/docs/classes/RestTimeoutError.md +128 -0
- package/docs/classes/RestTokenInvalidError.md +138 -0
- package/docs/interfaces/Middleware.md +27 -0
- package/docs/interfaces/Pacer.md +40 -0
- package/docs/interfaces/PacerDelay.md +25 -0
- package/docs/interfaces/PacerQPSBackend.md +44 -0
- package/docs/interfaces/PacerQPSOptions.md +40 -0
- package/docs/interfaces/RestLogEvent.md +95 -0
- package/docs/interfaces/RestOptions.md +351 -0
- package/docs/interfaces/TokenGetter.md +34 -0
- package/docs/modules.md +87 -0
- package/jest.config.js +8 -0
- package/package.json +42 -0
- package/tsconfig.json +39 -0
- package/typedoc.json +17 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { Agent as HttpAgent } from "http";
|
|
4
|
+
import { Agent as HttpsAgent } from "https";
|
|
5
|
+
import type RestRequest from "./RestRequest";
|
|
6
|
+
import type RestResponse from "./RestResponse";
|
|
7
|
+
/**
|
|
8
|
+
* An event which is passed to an external logger (see RestOptions).
|
|
9
|
+
*/
|
|
10
|
+
export interface RestLogEvent {
|
|
11
|
+
attempt: number;
|
|
12
|
+
req: RestRequest;
|
|
13
|
+
res: RestResponse | "backoff_delay" | null;
|
|
14
|
+
exception: any | null;
|
|
15
|
+
timestamp: number;
|
|
16
|
+
elapsed: number;
|
|
17
|
+
isFinalAttempt: boolean;
|
|
18
|
+
privateDataInResponse: boolean;
|
|
19
|
+
comment: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Middlewares allow to modify RestRequest and RestResponse objects during the
|
|
23
|
+
* request processing.
|
|
24
|
+
*/
|
|
25
|
+
export interface Middleware {
|
|
26
|
+
(req: RestRequest, next: (req: RestRequest) => Promise<RestResponse>): Promise<RestResponse>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @ignore
|
|
30
|
+
* Parameters for Agents.
|
|
31
|
+
*/
|
|
32
|
+
interface AgentOptions {
|
|
33
|
+
keepAlive: boolean;
|
|
34
|
+
maxSockets?: number;
|
|
35
|
+
rejectUnauthorized?: boolean;
|
|
36
|
+
family?: 4 | 6 | 0;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @ignore
|
|
40
|
+
* An internal class which keeps the list of HttpAgent instances used in a
|
|
41
|
+
* particular RestClient instance.
|
|
42
|
+
*/
|
|
43
|
+
export declare class Agents {
|
|
44
|
+
http(options: AgentOptions): HttpAgent;
|
|
45
|
+
https(options: AgentOptions): HttpsAgent;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Options passed to RestClient. More options can be added by cloning an
|
|
49
|
+
* instance of RestClient, withOption() method.
|
|
50
|
+
*/
|
|
51
|
+
export default interface RestOptions {
|
|
52
|
+
/** Max number of retries. Default is 0, because some requests are from the
|
|
53
|
+
* web app, and we don't want to retry them. */
|
|
54
|
+
retries: number;
|
|
55
|
+
/** How much time to wait by default on the 1st retry attempt. */
|
|
56
|
+
retryDelayFirstMs: number;
|
|
57
|
+
/** How much to increase the retry delay on each retry. */
|
|
58
|
+
retryDelayFactor: number;
|
|
59
|
+
/** Use this fraction (random) of the current retry delay to jitter both ways
|
|
60
|
+
* (e.g. 0.1 means 90%...110% of the delay to be actually applied). */
|
|
61
|
+
retryDelayJitter: number;
|
|
62
|
+
/** Maximum delay between each retry. */
|
|
63
|
+
retryDelayMaxMs: number;
|
|
64
|
+
/** A logic which runs on different IO stages (delay and heartbeats). */
|
|
65
|
+
heartbeater: {
|
|
66
|
+
heartbeat(): Promise<void>;
|
|
67
|
+
/** A function which, when runs, resolves in the provided number of ms. Can
|
|
68
|
+
* be used for several purposes, like overriding in unit tests or to pass a
|
|
69
|
+
* custom delay implementation which can throw on some external event (like
|
|
70
|
+
* when the process wants to gracefully stop). */
|
|
71
|
+
delay(ms: number): Promise<void>;
|
|
72
|
+
};
|
|
73
|
+
/** Allows to limit huge requests and throw instead. */
|
|
74
|
+
throwIfResIsBigger: number | undefined;
|
|
75
|
+
/** Passed to the logger which may decide, should it log details of the
|
|
76
|
+
* response or not. */
|
|
77
|
+
privateDataInResponse: boolean;
|
|
78
|
+
/** If true, non-public IP addresses are allowed too; otherwise, only unicast
|
|
79
|
+
* addresses are allowed. */
|
|
80
|
+
allowInternalIPs: boolean;
|
|
81
|
+
/** If true, logs request-response pairs to console. */
|
|
82
|
+
isDebug: boolean;
|
|
83
|
+
/** @ignore Holds HttpsAgent/HttpAgent instances; used internally only. */
|
|
84
|
+
agents: Agents;
|
|
85
|
+
/** Sets Keep-Alive parameters (persistent connections). */
|
|
86
|
+
keepAlive: {
|
|
87
|
+
/** A hint to the server, how much time to keep the connection alive. Not
|
|
88
|
+
* all the servers respect it though (e.g. nginx and express do not). */
|
|
89
|
+
timeout: number;
|
|
90
|
+
/** How many requests are allowed to be processed in one connection. */
|
|
91
|
+
max: number;
|
|
92
|
+
/** How many sockets at maximum will be kept open. */
|
|
93
|
+
maxSockets?: number;
|
|
94
|
+
};
|
|
95
|
+
/** When resolving DNS, use IPv4, IPv6 or both (see dns.lookup() docs). */
|
|
96
|
+
family: 4 | 6 | 0;
|
|
97
|
+
/** Max timeout to wait for a response. */
|
|
98
|
+
timeoutMs: number;
|
|
99
|
+
/** Logger to be used for each responses (including retried) plus for backoff
|
|
100
|
+
* delay events logging. */
|
|
101
|
+
logger: (event: RestLogEvent) => void;
|
|
102
|
+
/** Middlewares to wrap requests. May alter both request and response. */
|
|
103
|
+
middlewares: Middleware[];
|
|
104
|
+
/** If set, makes decision whether the response is successful or not. The
|
|
105
|
+
* response will either be returned to the client, or an error will be thrown.
|
|
106
|
+
* This allows to treat some non-successful HTTP statuses as success if the
|
|
107
|
+
* remote API is that weird. Return values:
|
|
108
|
+
* * "SUCCESS" - the request will be considered successful, no further checks
|
|
109
|
+
* will be performed;
|
|
110
|
+
* * "BEST_EFFORT" - inconclusive, the request may be either successful or
|
|
111
|
+
* unsuccessful, additional tests (e.g. will check HTTP status code) will be
|
|
112
|
+
* performed;
|
|
113
|
+
* * "THROW" - the request resulted in error. Additional tests will be
|
|
114
|
+
* performed to determine is the error is retriable, is OAuth token good,
|
|
115
|
+
* and etc.
|
|
116
|
+
*/
|
|
117
|
+
isSuccessResponse: (res: RestResponse) => "SUCCESS" | "THROW" | "BEST_EFFORT";
|
|
118
|
+
/** Decides whether the response is a rate-limit error or not. Returning
|
|
119
|
+
* non-zero value is treated as retry delay (if retries are set up). In case
|
|
120
|
+
* the returned value is "SOMETHING_ELSE", the response ought to be either
|
|
121
|
+
* success or some other error. Returning "BEST_EFFORT" turns on built-in
|
|
122
|
+
* heuristic (e.g. relying on HTTP status code and Retry-After header). In
|
|
123
|
+
* case we've made a decision that it's a rate limited error, the request is
|
|
124
|
+
* always retried; this covers a very common case when we have both
|
|
125
|
+
* isRateLimitError and isRetriableError handlers set up, and they return
|
|
126
|
+
* contradictory information; then isRateLimitError wins. */
|
|
127
|
+
isRateLimitError: (res: RestResponse) => "SOMETHING_ELSE" | "RATE_LIMIT" | "BEST_EFFORT" | number;
|
|
128
|
+
/** Decides whether the response is a token-invalid error or not. In case it's
|
|
129
|
+
* not, the response ought to be either success or some other error. */
|
|
130
|
+
isTokenInvalidError: (res: RestResponse) => boolean;
|
|
131
|
+
/** Called only if we haven't decided earlier that it's a rate limit error.
|
|
132
|
+
* Decides whether the response is a retriable error or not. In case the
|
|
133
|
+
* returned value is "NEVER_RETRY", the response ought to be either success or
|
|
134
|
+
* some other error, but it's guaranteed that the request won't be retried.
|
|
135
|
+
* Returning "BEST_EFFORT" turns on built-in heuristics (e.g. never retry "not
|
|
136
|
+
* found" errors). Returning a number is treated as "RETRY", and the next
|
|
137
|
+
* retry will happen in not less than this number of milliseconds. */
|
|
138
|
+
isRetriableError: (res: RestResponse, _error: any) => "NEVER_RETRY" | "RETRY" | "BEST_EFFORT" | number;
|
|
139
|
+
}
|
|
140
|
+
/** @ignore */
|
|
141
|
+
export declare const DEFAULT_OPTIONS: RestOptions;
|
|
142
|
+
export {};
|
|
143
|
+
//# sourceMappingURL=RestOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RestOptions.d.ts","sourceRoot":"","sources":["../src/RestOptions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.DEFAULT_OPTIONS = exports.Agents = void 0;
|
|
13
|
+
const http_1 = require("http");
|
|
14
|
+
const https_1 = require("https");
|
|
15
|
+
const delay_1 = __importDefault(require("delay"));
|
|
16
|
+
const fast_typescript_memoize_1 = require("fast-typescript-memoize");
|
|
17
|
+
/**
|
|
18
|
+
* @ignore
|
|
19
|
+
* An internal class which keeps the list of HttpAgent instances used in a
|
|
20
|
+
* particular RestClient instance.
|
|
21
|
+
*/
|
|
22
|
+
class Agents {
|
|
23
|
+
http(options) {
|
|
24
|
+
return new http_1.Agent(options);
|
|
25
|
+
}
|
|
26
|
+
https(options) {
|
|
27
|
+
return new https_1.Agent(options);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.Agents = Agents;
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, fast_typescript_memoize_1.Memoize)((...args) => JSON.stringify(args))
|
|
33
|
+
], Agents.prototype, "http", null);
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, fast_typescript_memoize_1.Memoize)((...args) => JSON.stringify(args))
|
|
36
|
+
], Agents.prototype, "https", null);
|
|
37
|
+
/** @ignore */
|
|
38
|
+
exports.DEFAULT_OPTIONS = {
|
|
39
|
+
retries: 0,
|
|
40
|
+
retryDelayFirstMs: 1000,
|
|
41
|
+
retryDelayFactor: 2,
|
|
42
|
+
retryDelayJitter: 0.1,
|
|
43
|
+
retryDelayMaxMs: Number.MAX_SAFE_INTEGER,
|
|
44
|
+
heartbeater: {
|
|
45
|
+
heartbeat: async () => { },
|
|
46
|
+
delay: delay_1.default,
|
|
47
|
+
},
|
|
48
|
+
throwIfResIsBigger: undefined,
|
|
49
|
+
privateDataInResponse: false,
|
|
50
|
+
allowInternalIPs: false,
|
|
51
|
+
isDebug: false,
|
|
52
|
+
agents: new Agents(),
|
|
53
|
+
keepAlive: { timeout: 10, max: 100 },
|
|
54
|
+
family: 4,
|
|
55
|
+
timeoutMs: 4 * 60 * 1000,
|
|
56
|
+
logger: () => { },
|
|
57
|
+
middlewares: [],
|
|
58
|
+
isSuccessResponse: () => "BEST_EFFORT",
|
|
59
|
+
isRateLimitError: () => "BEST_EFFORT",
|
|
60
|
+
isTokenInvalidError: () => false,
|
|
61
|
+
isRetriableError: () => "BEST_EFFORT",
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=RestOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RestOptions.js","sourceRoot":"","sources":["../src/RestOptions.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,+BAA0C;AAC1C,iCAA4C;AAC5C,kDAA0B;AAC1B,qEAAkD;AAyClD;;;;GAIG;AACH,MAAa,MAAM;IAEjB,IAAI,CAAC,OAAqB;QACxB,OAAO,IAAI,YAAS,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAGD,KAAK,CAAC,OAAqB;QACzB,OAAO,IAAI,aAAU,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;CACF;AAVD,wBAUC;AARC;IADC,IAAA,iCAAO,EAAC,CAAC,GAAG,IAAW,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;kCAGjD;AAGD;IADC,IAAA,iCAAO,EAAC,CAAC,GAAG,IAAW,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;mCAGjD;AAwGH,cAAc;AACD,QAAA,eAAe,GAAgB;IAC1C,OAAO,EAAE,CAAC;IACV,iBAAiB,EAAE,IAAI;IACvB,gBAAgB,EAAE,CAAC;IACnB,gBAAgB,EAAE,GAAG;IACrB,eAAe,EAAE,MAAM,CAAC,gBAAgB;IACxC,WAAW,EAAE;QACX,SAAS,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;QACzB,KAAK,EAAL,eAAK;KACN;IACD,kBAAkB,EAAE,SAAS;IAC7B,qBAAqB,EAAE,KAAK;IAC5B,gBAAgB,EAAE,KAAK;IACvB,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,IAAI,MAAM,EAAE;IACpB,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE;IACpC,MAAM,EAAE,CAAC;IACT,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;IACxB,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC;IAChB,WAAW,EAAE,EAAE;IACf,iBAAiB,EAAE,GAAG,EAAE,CAAC,aAAa;IACtC,gBAAgB,EAAE,GAAG,EAAE,CAAC,aAAa;IACrC,mBAAmB,EAAE,GAAG,EAAE,CAAC,KAAK;IAChC,gBAAgB,EAAE,GAAG,EAAE,CAAC,aAAa;CACtC,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { Headers } from "node-fetch";
|
|
4
|
+
import type RestOptions from "./RestOptions";
|
|
5
|
+
import RestResponse from "./RestResponse";
|
|
6
|
+
import RestStream from "./RestStream";
|
|
7
|
+
/**
|
|
8
|
+
* Type TAssertShape allows to limit json()'s assert callbacks to only those
|
|
9
|
+
* which return an object compatible with TAssertShape.
|
|
10
|
+
*/
|
|
11
|
+
export default class RestRequest<TAssertShape = any> {
|
|
12
|
+
readonly method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
13
|
+
url: string;
|
|
14
|
+
readonly headers: Headers;
|
|
15
|
+
readonly body: string | Buffer | NodeJS.ReadableStream;
|
|
16
|
+
readonly shape?: string | undefined;
|
|
17
|
+
readonly options: RestOptions;
|
|
18
|
+
constructor(options: RestOptions, method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, headers: Headers, body: string | Buffer | NodeJS.ReadableStream, shape?: string | undefined);
|
|
19
|
+
/**
|
|
20
|
+
* Modifies the request by adding a custom HTTP header.
|
|
21
|
+
*/
|
|
22
|
+
setHeader(name: string, value: string): this;
|
|
23
|
+
/**
|
|
24
|
+
* Modifies the request by adding a custom request option.
|
|
25
|
+
*/
|
|
26
|
+
setOptions(options: Partial<RestOptions>): this;
|
|
27
|
+
/**
|
|
28
|
+
* Forces RestClient to debug-output the request and response to console.
|
|
29
|
+
* Never use in production.
|
|
30
|
+
*/
|
|
31
|
+
setDebug(flag?: boolean): this;
|
|
32
|
+
/**
|
|
33
|
+
* Sends the request and reads the response a JSON. In absolute most of the
|
|
34
|
+
* cases, this method is used to reach API responses. The assert callback
|
|
35
|
+
* (typically generated by typescript-is) is intentionally made mandatory to
|
|
36
|
+
* not let people to do anti-patterns.
|
|
37
|
+
*/
|
|
38
|
+
json<TJson extends TAssertShape>(assert: ((obj: any) => TJson) | {
|
|
39
|
+
mask(obj: any): TJson;
|
|
40
|
+
} | {
|
|
41
|
+
$assert(obj: any): TJson;
|
|
42
|
+
}, ...checkers: Array<(json: TJson, res: RestResponse) => false | Error>): Promise<TJson>;
|
|
43
|
+
/**
|
|
44
|
+
* Sends the request and returns plaintext response.
|
|
45
|
+
*/
|
|
46
|
+
text(): Promise<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Returns the entire RestResponse object with response status and headers
|
|
49
|
+
* information in it. Try to minimize usage of this method, because it doesn't
|
|
50
|
+
* make any assumptions on the response structure.
|
|
51
|
+
*/
|
|
52
|
+
response(): Promise<RestResponse>;
|
|
53
|
+
/**
|
|
54
|
+
* Sends the requests and returns RestStream object. You MUST iterate over
|
|
55
|
+
* this object entirely (or call its return() method), otherwise the
|
|
56
|
+
* connection will remain dangling.
|
|
57
|
+
*/
|
|
58
|
+
stream(preloadChars?: number): Promise<RestStream>;
|
|
59
|
+
/**
|
|
60
|
+
* We can actually only create RequestInit. We can't create an instance of
|
|
61
|
+
* node-fetch.Request object since it doesn't allow injection of
|
|
62
|
+
* AbortController later, and we don't want to deal with AbortController here.
|
|
63
|
+
*/
|
|
64
|
+
private _createFetchRequest;
|
|
65
|
+
/**
|
|
66
|
+
* Creates an instance of RestFetchReader.
|
|
67
|
+
*/
|
|
68
|
+
private _createFetchReader;
|
|
69
|
+
/**
|
|
70
|
+
* Creates a RestResponse from a RestFetchReader. Assumes that
|
|
71
|
+
* RestFetchReader.preload() has already been called.
|
|
72
|
+
*/
|
|
73
|
+
private _createRestResponse;
|
|
74
|
+
/**
|
|
75
|
+
* Logs a response event, an error event or a backoff event. If RestResponse
|
|
76
|
+
* is not yet known (e.g. an exception happened in a DNS resolution), res must
|
|
77
|
+
* be passed as null.
|
|
78
|
+
*/
|
|
79
|
+
private _logResponse;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=RestRequest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RestRequest.d.ts","sourceRoot":"","sources":["../src/RestRequest.ts"],"names":[],"mappings":";;AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AASrC,OAAO,KAAK,WAAW,MAAM,eAAe,CAAC;AAE7C,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,UAAU,MAAM,cAAc,CAAC;AAItC;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW,CAAC,YAAY,GAAG,GAAG;aAK/B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ;IAC5D,GAAG,EAAE,MAAM;aACF,OAAO,EAAE,OAAO;aAChB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,cAAc;aAC7C,KAAK,CAAC;IARxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;gBAG5B,OAAO,EAAE,WAAW,EACJ,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,EAC5D,GAAG,EAAE,MAAM,EACF,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,cAAc,EAC7C,KAAK,CAAC,oBAAQ;IAKhC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAKrC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC;IAQxC;;;OAGG;IACH,QAAQ,CAAC,IAAI,UAAO;IAKpB;;;;;OAKG;IACG,IAAI,CAAC,KAAK,SAAS,YAAY,EACnC,MAAM,EACF,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,KAAK,CAAC,GACrB;QAAE,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,KAAK,CAAA;KAAE,GACzB;QAAE,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,KAAK,CAAA;KAAE,EAChC,GAAG,QAAQ,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,KAAK,KAAK,GAAG,KAAK,CAAC,GACpE,OAAO,CAAC,KAAK,CAAC;IAuBjB;;OAEG;IACG,IAAI;IAKV;;;;OAIG;IAEG,QAAQ,IAAI,OAAO,CAAC,YAAY,CAAC;IAWvC;;;;OAIG;IACG,MAAM,CAAC,YAAY,SAAa,GAAG,OAAO,CAAC,UAAU,CAAC;IAwH5D;;;;OAIG;YACW,mBAAmB;IAyEjC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAwB1B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;;;OAIG;IACH,OAAO,CAAC,YAAY;CAgDrB"}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const dns_1 = __importDefault(require("dns"));
|
|
13
|
+
const util_1 = require("util");
|
|
14
|
+
const fast_typescript_memoize_1 = require("fast-typescript-memoize");
|
|
15
|
+
const ipaddr_js_1 = require("ipaddr.js");
|
|
16
|
+
const random_1 = __importDefault(require("lodash/random"));
|
|
17
|
+
const node_fetch_1 = require("node-fetch");
|
|
18
|
+
const RestContentSizeOverLimitError_1 = __importDefault(require("./errors/RestContentSizeOverLimitError"));
|
|
19
|
+
const RestError_1 = __importDefault(require("./errors/RestError"));
|
|
20
|
+
const RestTimeoutError_1 = __importDefault(require("./errors/RestTimeoutError"));
|
|
21
|
+
const calcRetryDelay_1 = __importDefault(require("./internal/calcRetryDelay"));
|
|
22
|
+
const inspectPossibleJSON_1 = __importDefault(require("./internal/inspectPossibleJSON"));
|
|
23
|
+
const RestFetchReader_1 = __importDefault(require("./internal/RestFetchReader"));
|
|
24
|
+
const throwIfErrorResponse_1 = __importDefault(require("./internal/throwIfErrorResponse"));
|
|
25
|
+
const toFloatMs_1 = __importDefault(require("./internal/toFloatMs"));
|
|
26
|
+
const RestResponse_1 = __importDefault(require("./RestResponse"));
|
|
27
|
+
const RestStream_1 = __importDefault(require("./RestStream"));
|
|
28
|
+
const MAX_DEBUG_LEN = 1024 * 100;
|
|
29
|
+
/**
|
|
30
|
+
* Type TAssertShape allows to limit json()'s assert callbacks to only those
|
|
31
|
+
* which return an object compatible with TAssertShape.
|
|
32
|
+
*/
|
|
33
|
+
class RestRequest {
|
|
34
|
+
constructor(options, method, url, headers, body, shape) {
|
|
35
|
+
this.method = method;
|
|
36
|
+
this.url = url;
|
|
37
|
+
this.headers = headers;
|
|
38
|
+
this.body = body;
|
|
39
|
+
this.shape = shape;
|
|
40
|
+
this.options = { ...options };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Modifies the request by adding a custom HTTP header.
|
|
44
|
+
*/
|
|
45
|
+
setHeader(name, value) {
|
|
46
|
+
this.headers.append(name, value);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Modifies the request by adding a custom request option.
|
|
51
|
+
*/
|
|
52
|
+
setOptions(options) {
|
|
53
|
+
for (const [k, v] of Object.entries(options)) {
|
|
54
|
+
this.options[k] = v;
|
|
55
|
+
}
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Forces RestClient to debug-output the request and response to console.
|
|
60
|
+
* Never use in production.
|
|
61
|
+
*/
|
|
62
|
+
setDebug(flag = true) {
|
|
63
|
+
this.options.isDebug = flag;
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Sends the request and reads the response a JSON. In absolute most of the
|
|
68
|
+
* cases, this method is used to reach API responses. The assert callback
|
|
69
|
+
* (typically generated by typescript-is) is intentionally made mandatory to
|
|
70
|
+
* not let people to do anti-patterns.
|
|
71
|
+
*/
|
|
72
|
+
async json(assert, ...checkers) {
|
|
73
|
+
const res = await this.response();
|
|
74
|
+
// Support Superstruct in a duck-typing way.
|
|
75
|
+
if (typeof assert !== "function") {
|
|
76
|
+
if ("mask" in assert) {
|
|
77
|
+
assert = assert.mask.bind(assert);
|
|
78
|
+
}
|
|
79
|
+
else if ("$assert" in assert) {
|
|
80
|
+
assert = assert.$assert.bind(assert);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const json = assert(res.json);
|
|
84
|
+
for (const checker of checkers) {
|
|
85
|
+
const error = checker(json, res);
|
|
86
|
+
if (error !== false) {
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return json;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Sends the request and returns plaintext response.
|
|
94
|
+
*/
|
|
95
|
+
async text() {
|
|
96
|
+
const res = await this.response();
|
|
97
|
+
return res.text;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Returns the entire RestResponse object with response status and headers
|
|
101
|
+
* information in it. Try to minimize usage of this method, because it doesn't
|
|
102
|
+
* make any assumptions on the response structure.
|
|
103
|
+
*/
|
|
104
|
+
async response() {
|
|
105
|
+
const stream = await this.stream(
|
|
106
|
+
// By passing Number.MAX_SAFE_INTEGER to stream(), we ensure that the
|
|
107
|
+
// entire data will be preloaded, or the loading will fail due to
|
|
108
|
+
// throwIfResIsBigger limitation, whichever will happen faster.
|
|
109
|
+
Number.MAX_SAFE_INTEGER);
|
|
110
|
+
await stream.close();
|
|
111
|
+
return stream.res;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Sends the requests and returns RestStream object. You MUST iterate over
|
|
115
|
+
* this object entirely (or call its return() method), otherwise the
|
|
116
|
+
* connection will remain dangling.
|
|
117
|
+
*/
|
|
118
|
+
async stream(preloadChars = 128 * 1024) {
|
|
119
|
+
const finalAttempt = Math.max(this.options.retries + 1, 1);
|
|
120
|
+
let retryDelayMs = Math.max(this.options.retryDelayFirstMs, 10);
|
|
121
|
+
for (let attempt = 1; attempt <= finalAttempt; attempt++) {
|
|
122
|
+
let timeStart = process.hrtime();
|
|
123
|
+
let res = null;
|
|
124
|
+
try {
|
|
125
|
+
// Middlewares work with RestResponse (can alter it) and not with
|
|
126
|
+
// RestStream intentionally. So the goal here is to create a
|
|
127
|
+
// RestResponse object, and then later derive a RestStream from it.
|
|
128
|
+
let reader = null;
|
|
129
|
+
res = await runMiddlewares(this, this.options.middlewares, async (req) => {
|
|
130
|
+
var _a;
|
|
131
|
+
reader = null;
|
|
132
|
+
try {
|
|
133
|
+
timeStart = process.hrtime();
|
|
134
|
+
res = null;
|
|
135
|
+
try {
|
|
136
|
+
const fetchReq = await req._createFetchRequest();
|
|
137
|
+
reader = req._createFetchReader(fetchReq);
|
|
138
|
+
await reader.preload(preloadChars);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
res =
|
|
142
|
+
(_a = req._createRestResponse(reader)) !== null && _a !== void 0 ? _a : new RestResponse_1.default(req, 0, new node_fetch_1.Headers(), "", false);
|
|
143
|
+
}
|
|
144
|
+
(0, throwIfErrorResponse_1.default)(req.options, res);
|
|
145
|
+
req._logResponse({
|
|
146
|
+
attempt,
|
|
147
|
+
req,
|
|
148
|
+
res,
|
|
149
|
+
exception: null,
|
|
150
|
+
timestamp: Date.now(),
|
|
151
|
+
elapsed: (0, toFloatMs_1.default)(process.hrtime(timeStart)),
|
|
152
|
+
isFinalAttempt: attempt === finalAttempt,
|
|
153
|
+
privateDataInResponse: req.options.privateDataInResponse,
|
|
154
|
+
comment: "",
|
|
155
|
+
});
|
|
156
|
+
return res;
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
await (reader === null || reader === void 0 ? void 0 : reader.close());
|
|
160
|
+
throw e;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
// The only place where we return the response. Otherwise we retry or
|
|
164
|
+
// throw an exception.
|
|
165
|
+
return new RestStream_1.default(res, reader);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
this._logResponse({
|
|
169
|
+
attempt,
|
|
170
|
+
req: this,
|
|
171
|
+
res,
|
|
172
|
+
exception: error,
|
|
173
|
+
timestamp: Date.now(),
|
|
174
|
+
elapsed: (0, toFloatMs_1.default)(process.hrtime(timeStart)),
|
|
175
|
+
isFinalAttempt: attempt === finalAttempt,
|
|
176
|
+
privateDataInResponse: this.options.privateDataInResponse,
|
|
177
|
+
comment: "",
|
|
178
|
+
});
|
|
179
|
+
if (res === null) {
|
|
180
|
+
// An error in internal function or middleware; this must not happen.
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
if (attempt === finalAttempt) {
|
|
184
|
+
// Last retry attempt; always throw.
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
const newRetryDelay = (0, calcRetryDelay_1.default)(error, this.options, res, retryDelayMs);
|
|
188
|
+
if (newRetryDelay === "no_retry") {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
retryDelayMs = newRetryDelay;
|
|
192
|
+
}
|
|
193
|
+
const delayStart = process.hrtime();
|
|
194
|
+
retryDelayMs *= (0, random_1.default)(1 - this.options.retryDelayJitter, 1 + this.options.retryDelayJitter, true);
|
|
195
|
+
await this.options.heartbeater.delay(retryDelayMs);
|
|
196
|
+
this._logResponse({
|
|
197
|
+
attempt,
|
|
198
|
+
req: this,
|
|
199
|
+
res: "backoff_delay",
|
|
200
|
+
exception: null,
|
|
201
|
+
timestamp: Date.now(),
|
|
202
|
+
elapsed: (0, toFloatMs_1.default)(process.hrtime(delayStart)),
|
|
203
|
+
isFinalAttempt: false,
|
|
204
|
+
privateDataInResponse: this.options.privateDataInResponse,
|
|
205
|
+
comment: "",
|
|
206
|
+
});
|
|
207
|
+
retryDelayMs *= this.options.retryDelayFactor;
|
|
208
|
+
retryDelayMs = Math.min(this.options.retryDelayMaxMs, retryDelayMs);
|
|
209
|
+
}
|
|
210
|
+
throw Error("BUG: should never happen");
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* We can actually only create RequestInit. We can't create an instance of
|
|
214
|
+
* node-fetch.Request object since it doesn't allow injection of
|
|
215
|
+
* AbortController later, and we don't want to deal with AbortController here.
|
|
216
|
+
*/
|
|
217
|
+
async _createFetchRequest() {
|
|
218
|
+
var _a;
|
|
219
|
+
const url = new URL(this.url);
|
|
220
|
+
if (!["http:", "https:"].includes(url.protocol)) {
|
|
221
|
+
throw new RestError_1.default(`Unsupported protocol: ${url.protocol}`);
|
|
222
|
+
}
|
|
223
|
+
// TODO: rework DNS resolution to use a custom Agent. We'd be able to
|
|
224
|
+
// support safe redirects then.
|
|
225
|
+
// Resolve IP address, check it for being a public IP address and substitute
|
|
226
|
+
// it in the URL; the hostname will be passed separately via Host header.
|
|
227
|
+
const hostname = url.hostname;
|
|
228
|
+
const headers = new node_fetch_1.Headers(this.headers);
|
|
229
|
+
const addr = await (0, util_1.promisify)(dns_1.default.lookup)(hostname, {
|
|
230
|
+
family: this.options.family,
|
|
231
|
+
});
|
|
232
|
+
let redirectMode = "follow";
|
|
233
|
+
if (!this.options.allowInternalIPs) {
|
|
234
|
+
const range = (0, ipaddr_js_1.parse)(addr.address).range();
|
|
235
|
+
// External requests are returned as "unicast" by ipaddr.js.
|
|
236
|
+
const isInternal = range !== "unicast";
|
|
237
|
+
if (isInternal) {
|
|
238
|
+
throw new RestError_1.default(`Domain ${hostname} resolves to a non-public (${range}) IP address ${addr.address}`);
|
|
239
|
+
}
|
|
240
|
+
url.hostname = addr.address;
|
|
241
|
+
if (!headers.get("host")) {
|
|
242
|
+
headers.set("host", hostname);
|
|
243
|
+
}
|
|
244
|
+
// ATTENTION: don't turn on redirects, it's a security breach when using
|
|
245
|
+
// with allowInternalIPs=false which is default!
|
|
246
|
+
redirectMode = "error";
|
|
247
|
+
}
|
|
248
|
+
const hasKeepAlive = this.options.keepAlive.timeout > 0;
|
|
249
|
+
if (hasKeepAlive) {
|
|
250
|
+
headers.append("connection", "Keep-Alive");
|
|
251
|
+
headers.append("keep-alive", "timeout=" +
|
|
252
|
+
this.options.keepAlive.timeout +
|
|
253
|
+
", max=" +
|
|
254
|
+
this.options.keepAlive.max);
|
|
255
|
+
}
|
|
256
|
+
// Use lazily created/cached per-RestClient Agent instance to utilize HTTP
|
|
257
|
+
// persistent connections and save on HTTPS connection re-establishment.
|
|
258
|
+
const agent = (url.protocol === "https:"
|
|
259
|
+
? this.options.agents.https.bind(this.options.agents)
|
|
260
|
+
: this.options.agents.http.bind(this.options.agents))({
|
|
261
|
+
keepAlive: hasKeepAlive,
|
|
262
|
+
maxSockets: (_a = this.options.keepAlive) === null || _a === void 0 ? void 0 : _a.maxSockets,
|
|
263
|
+
rejectUnauthorized: this.options.allowInternalIPs ? false : undefined,
|
|
264
|
+
family: this.options.family,
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
url: url.toString(),
|
|
268
|
+
method: this.method,
|
|
269
|
+
headers,
|
|
270
|
+
body: this.body || undefined,
|
|
271
|
+
timeout: this.options.timeoutMs,
|
|
272
|
+
redirect: redirectMode,
|
|
273
|
+
agent,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Creates an instance of RestFetchReader.
|
|
278
|
+
*/
|
|
279
|
+
_createFetchReader(req) {
|
|
280
|
+
return new RestFetchReader_1.default(req.url, req, {
|
|
281
|
+
timeoutMs: this.options.timeoutMs,
|
|
282
|
+
heartbeat: async () => this.options.heartbeater.heartbeat(),
|
|
283
|
+
onTimeout: (reader) => {
|
|
284
|
+
throw new RestTimeoutError_1.default(`Timed out while reading response body (${this.options.timeoutMs} ms)`, this._createRestResponse(reader));
|
|
285
|
+
},
|
|
286
|
+
onAfterRead: (reader) => {
|
|
287
|
+
if (this.options.throwIfResIsBigger &&
|
|
288
|
+
reader.charsRead > this.options.throwIfResIsBigger) {
|
|
289
|
+
throw new RestContentSizeOverLimitError_1.default(`Content size is over limit of ${this.options.throwIfResIsBigger} characters`, this._createRestResponse(reader));
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Creates a RestResponse from a RestFetchReader. Assumes that
|
|
296
|
+
* RestFetchReader.preload() has already been called.
|
|
297
|
+
*/
|
|
298
|
+
_createRestResponse(reader) {
|
|
299
|
+
return reader
|
|
300
|
+
? new RestResponse_1.default(this, reader.status, reader.headers, reader.textFetched, reader.textIsPartial)
|
|
301
|
+
: null;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Logs a response event, an error event or a backoff event. If RestResponse
|
|
305
|
+
* is not yet known (e.g. an exception happened in a DNS resolution), res must
|
|
306
|
+
* be passed as null.
|
|
307
|
+
*/
|
|
308
|
+
_logResponse(event) {
|
|
309
|
+
this.options.logger(event);
|
|
310
|
+
// Debug-logging to console?
|
|
311
|
+
if (this.options.isDebug ||
|
|
312
|
+
(process.env["NODE_ENV"] === "development" &&
|
|
313
|
+
event.res === "backoff_delay")) {
|
|
314
|
+
let reqMessage = `${this.method} ${this.url}\n` +
|
|
315
|
+
Object.entries(this.headers.raw())
|
|
316
|
+
.map(([k, vs]) => vs.map((v) => `${k}: ${v}`))
|
|
317
|
+
.join("\n");
|
|
318
|
+
if (this.body) {
|
|
319
|
+
reqMessage +=
|
|
320
|
+
"\n" + (0, inspectPossibleJSON_1.default)(this.headers, this.body, MAX_DEBUG_LEN);
|
|
321
|
+
}
|
|
322
|
+
let resMessage = "";
|
|
323
|
+
if (event.res === "backoff_delay") {
|
|
324
|
+
resMessage =
|
|
325
|
+
"Previous request failed, backoff delay elapsed, retrying attempt " +
|
|
326
|
+
(event.attempt + 1) +
|
|
327
|
+
"...";
|
|
328
|
+
}
|
|
329
|
+
else if (event.res) {
|
|
330
|
+
resMessage =
|
|
331
|
+
`HTTP ${event.res.status} ` +
|
|
332
|
+
`(took ${Math.round(event.elapsed)} ms)`;
|
|
333
|
+
if (event.res.text) {
|
|
334
|
+
resMessage +=
|
|
335
|
+
"\n" +
|
|
336
|
+
(0, inspectPossibleJSON_1.default)(event.res.headers, event.res.text, MAX_DEBUG_LEN);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else if (event.exception) {
|
|
340
|
+
resMessage = "" + event.exception;
|
|
341
|
+
}
|
|
342
|
+
// eslint-disable-next-line no-console
|
|
343
|
+
console.log(reqMessage.replace(/^/gm, "+++ "));
|
|
344
|
+
// eslint-disable-next-line no-console
|
|
345
|
+
console.log(resMessage.replace(/^/gm, "=== ") + "\n");
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
exports.default = RestRequest;
|
|
350
|
+
__decorate([
|
|
351
|
+
(0, fast_typescript_memoize_1.Memoize)()
|
|
352
|
+
], RestRequest.prototype, "response", null);
|
|
353
|
+
/**
|
|
354
|
+
* Runs the middlewares chain of responsibility. Each middleware receives a
|
|
355
|
+
* mutable RestRequest object and next() callback which, when called, triggers
|
|
356
|
+
* execution of the remaining middlewares in the chain. This allows a middleware
|
|
357
|
+
* to not only modify the request object, but also alter the response received
|
|
358
|
+
* from the subsequent middlewares.
|
|
359
|
+
*/
|
|
360
|
+
async function runMiddlewares(req, middlewares, last) {
|
|
361
|
+
if (middlewares.length > 0) {
|
|
362
|
+
const [head, ...tail] = middlewares;
|
|
363
|
+
return head(req, async (req) => runMiddlewares(req, tail, last));
|
|
364
|
+
}
|
|
365
|
+
return last(req);
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=RestRequest.js.map
|