got 11.0.0-beta.1 → 11.0.3
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/dist/source/as-promise/core.d.ts +4 -5
- package/dist/source/as-promise/core.js +28 -27
- package/dist/source/as-promise/index.js +40 -20
- package/dist/source/as-promise/types.d.ts +4 -3
- package/dist/source/as-promise/types.js +3 -2
- package/dist/source/core/index.d.ts +29 -10
- package/dist/source/core/index.js +198 -148
- package/dist/source/core/utils/get-body-size.d.ts +3 -6
- package/dist/source/core/utils/get-body-size.js +1 -2
- package/dist/source/core/utils/options-to-url.js +1 -1
- package/dist/source/core/utils/timed-out.js +4 -6
- package/dist/source/core/utils/weakable-map.d.ts +8 -0
- package/dist/source/core/utils/weakable-map.js +29 -0
- package/dist/source/create.js +8 -6
- package/dist/source/index.js +2 -1
- package/dist/source/types.d.ts +23 -20
- package/package.json +9 -17
- package/readme.md +74 -12
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { URL } from 'url';
|
|
3
|
-
import { Options, NormalizedOptions, Defaults, ResponseType } from './types';
|
|
4
|
-
import Request
|
|
3
|
+
import { Options, NormalizedOptions, Defaults, ResponseType, Response } from './types';
|
|
4
|
+
import Request from '../core';
|
|
5
5
|
export declare const knownBodyTypes: string[];
|
|
6
|
-
export declare const parseBody: (
|
|
6
|
+
export declare const parseBody: (response: Response<unknown>, responseType: ResponseType, encoding?: string | undefined) => unknown;
|
|
7
7
|
export default class PromisableRequest extends Request {
|
|
8
8
|
['constructor']: typeof PromisableRequest;
|
|
9
9
|
options: NormalizedOptions;
|
|
10
|
-
_throwHttpErrors: boolean;
|
|
11
10
|
static normalizeArguments(url?: string | URL, nonNormalizedOptions?: Options, defaults?: Defaults): NormalizedOptions;
|
|
12
11
|
static mergeOptions(...sources: Options[]): NormalizedOptions;
|
|
13
|
-
_beforeError(error:
|
|
12
|
+
_beforeError(error: Error): Promise<void>;
|
|
14
13
|
}
|
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const is_1 = require("@sindresorhus/is");
|
|
4
|
+
const types_1 = require("./types");
|
|
4
5
|
const core_1 = require("../core");
|
|
5
6
|
if (!core_1.knownHookEvents.includes('beforeRetry')) {
|
|
6
7
|
core_1.knownHookEvents.push('beforeRetry', 'afterResponse');
|
|
7
8
|
}
|
|
8
9
|
exports.knownBodyTypes = ['json', 'buffer', 'text'];
|
|
9
10
|
// @ts-ignore The error is: Not all code paths return a value.
|
|
10
|
-
exports.parseBody = (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
exports.parseBody = (response, responseType, encoding) => {
|
|
12
|
+
const { rawBody } = response;
|
|
13
|
+
try {
|
|
14
|
+
if (responseType === 'text') {
|
|
15
|
+
return rawBody.toString(encoding);
|
|
16
|
+
}
|
|
17
|
+
if (responseType === 'json') {
|
|
18
|
+
return rawBody.length === 0 ? '' : JSON.parse(rawBody.toString());
|
|
19
|
+
}
|
|
20
|
+
if (responseType === 'buffer') {
|
|
21
|
+
return Buffer.from(rawBody);
|
|
22
|
+
}
|
|
23
|
+
throw new types_1.ParseError({
|
|
24
|
+
message: `Unknown body type '${responseType}'`,
|
|
25
|
+
name: 'Error'
|
|
26
|
+
}, response);
|
|
19
27
|
}
|
|
20
|
-
|
|
21
|
-
throw new
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new types_1.ParseError(error, response);
|
|
22
30
|
}
|
|
23
31
|
};
|
|
24
32
|
class PromisableRequest extends core_1.default {
|
|
@@ -83,6 +91,10 @@ class PromisableRequest extends core_1.default {
|
|
|
83
91
|
throw new Error('`options.pagination.paginate` must be implemented');
|
|
84
92
|
}
|
|
85
93
|
}
|
|
94
|
+
// JSON mode
|
|
95
|
+
if (options.responseType === 'json' && options.headers.accept === undefined) {
|
|
96
|
+
options.headers.accept = 'application/json';
|
|
97
|
+
}
|
|
86
98
|
return options;
|
|
87
99
|
}
|
|
88
100
|
static mergeOptions(...sources) {
|
|
@@ -93,23 +105,12 @@ class PromisableRequest extends core_1.default {
|
|
|
93
105
|
return mergedOptions;
|
|
94
106
|
}
|
|
95
107
|
async _beforeError(error) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (const hook of this.options.hooks.beforeError) {
|
|
99
|
-
// eslint-disable-next-line no-await-in-loop
|
|
100
|
-
error = await hook(error);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch (error_) {
|
|
104
|
-
this.destroy(error_);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (this._throwHttpErrors && !isHTTPError) {
|
|
108
|
-
this.destroy(error);
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
this.emit('error', error);
|
|
108
|
+
if (!(error instanceof core_1.RequestError)) {
|
|
109
|
+
error = new core_1.RequestError(error.message, error, this);
|
|
112
110
|
}
|
|
111
|
+
// Let the promise decide whether to abort or not
|
|
112
|
+
// It is also responsible for the `beforeError` hook
|
|
113
|
+
this.emit('error', error);
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
exports.default = PromisableRequest;
|
|
@@ -20,22 +20,37 @@ const proxiedRequestEvents = [
|
|
|
20
20
|
];
|
|
21
21
|
function asPromise(options) {
|
|
22
22
|
let retryCount = 0;
|
|
23
|
-
let
|
|
23
|
+
let globalRequest;
|
|
24
|
+
let globalResponse;
|
|
24
25
|
const emitter = new events_1.EventEmitter();
|
|
25
|
-
const promise = new PCancelable((resolve,
|
|
26
|
+
const promise = new PCancelable((resolve, _reject, onCancel) => {
|
|
26
27
|
const makeRequest = () => {
|
|
27
|
-
if (options.responseType === 'json' && options.headers.accept === undefined) {
|
|
28
|
-
options.headers.accept = 'application/json';
|
|
29
|
-
}
|
|
30
28
|
// Support retries
|
|
29
|
+
// `options.throwHttpErrors` needs to be always true,
|
|
30
|
+
// so the HTTP errors are caught and the request is retried.
|
|
31
|
+
// The error is **eventually** thrown if the user value is true.
|
|
31
32
|
const { throwHttpErrors } = options;
|
|
32
33
|
if (!throwHttpErrors) {
|
|
33
34
|
options.throwHttpErrors = true;
|
|
34
35
|
}
|
|
36
|
+
// Note from @szmarczak: I think we should use `request.options` instead of the local options
|
|
35
37
|
const request = new core_1.default(options.url, options);
|
|
36
|
-
request._throwHttpErrors = throwHttpErrors;
|
|
37
38
|
request._noPipe = true;
|
|
38
39
|
onCancel(() => request.destroy());
|
|
40
|
+
const reject = async (error) => {
|
|
41
|
+
try {
|
|
42
|
+
for (const hook of options.hooks.beforeError) {
|
|
43
|
+
// eslint-disable-next-line no-await-in-loop
|
|
44
|
+
error = await hook(error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error_) {
|
|
48
|
+
_reject(new types_1.RequestError(error_.message, error_, request));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
_reject(error);
|
|
52
|
+
};
|
|
53
|
+
globalRequest = request;
|
|
39
54
|
request.once('response', async (response) => {
|
|
40
55
|
response.retryCount = retryCount;
|
|
41
56
|
if (response.request.aborted) {
|
|
@@ -48,23 +63,26 @@ function asPromise(options) {
|
|
|
48
63
|
return (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304;
|
|
49
64
|
};
|
|
50
65
|
// Download body
|
|
66
|
+
let rawBody;
|
|
51
67
|
try {
|
|
52
|
-
|
|
68
|
+
rawBody = await getStream.buffer(request);
|
|
69
|
+
response.rawBody = rawBody;
|
|
53
70
|
}
|
|
54
|
-
catch (
|
|
55
|
-
|
|
71
|
+
catch (_) {
|
|
72
|
+
// The same error is caught below.
|
|
73
|
+
// See request.once('error')
|
|
56
74
|
return;
|
|
57
75
|
}
|
|
58
76
|
// Parse body
|
|
59
77
|
try {
|
|
60
|
-
response.body = core_1.parseBody(
|
|
78
|
+
response.body = core_1.parseBody(response, options.responseType, options.encoding);
|
|
61
79
|
}
|
|
62
80
|
catch (error) {
|
|
63
81
|
// Fallback to `utf8`
|
|
64
|
-
response.body =
|
|
82
|
+
response.body = rawBody.toString();
|
|
65
83
|
if (isOk()) {
|
|
66
|
-
|
|
67
|
-
|
|
84
|
+
// TODO: Call `request._beforeError`, see https://github.com/nodejs/node/issues/32995
|
|
85
|
+
reject(error);
|
|
68
86
|
return;
|
|
69
87
|
}
|
|
70
88
|
}
|
|
@@ -73,7 +91,6 @@ function asPromise(options) {
|
|
|
73
91
|
// @ts-ignore TS doesn't notice that CancelableRequest is a Promise
|
|
74
92
|
// eslint-disable-next-line no-await-in-loop
|
|
75
93
|
response = await hook(response, async (updatedOptions) => {
|
|
76
|
-
request.destroy();
|
|
77
94
|
const typedOptions = core_1.default.normalizeArguments(undefined, {
|
|
78
95
|
...updatedOptions,
|
|
79
96
|
retry: {
|
|
@@ -99,13 +116,15 @@ function asPromise(options) {
|
|
|
99
116
|
}
|
|
100
117
|
}
|
|
101
118
|
catch (error) {
|
|
102
|
-
request._beforeError
|
|
119
|
+
// TODO: Call `request._beforeError`, see https://github.com/nodejs/node/issues/32995
|
|
120
|
+
reject(new types_1.RequestError(error.message, error, request));
|
|
103
121
|
return;
|
|
104
122
|
}
|
|
105
123
|
if (throwHttpErrors && !isOk()) {
|
|
106
|
-
reject(new types_1.HTTPError(response
|
|
124
|
+
reject(new types_1.HTTPError(response));
|
|
107
125
|
return;
|
|
108
126
|
}
|
|
127
|
+
globalResponse = response;
|
|
109
128
|
resolve(options.resolveBodyOnly ? response.body : response);
|
|
110
129
|
});
|
|
111
130
|
request.once('error', (error) => {
|
|
@@ -134,7 +153,7 @@ function asPromise(options) {
|
|
|
134
153
|
catch (error_) {
|
|
135
154
|
// Don't emit the `response` event
|
|
136
155
|
request.destroy();
|
|
137
|
-
reject(new types_1.RequestError(error_.message, error, request
|
|
156
|
+
reject(new types_1.RequestError(error_.message, error, request));
|
|
138
157
|
return;
|
|
139
158
|
}
|
|
140
159
|
if (backoff) {
|
|
@@ -151,7 +170,7 @@ function asPromise(options) {
|
|
|
151
170
|
catch (error_) {
|
|
152
171
|
// Don't emit the `response` event
|
|
153
172
|
request.destroy();
|
|
154
|
-
reject(new types_1.RequestError(error_.message, error, request
|
|
173
|
+
reject(new types_1.RequestError(error_.message, error, request));
|
|
155
174
|
return;
|
|
156
175
|
}
|
|
157
176
|
makeRequest();
|
|
@@ -179,14 +198,15 @@ function asPromise(options) {
|
|
|
179
198
|
};
|
|
180
199
|
const shortcut = (responseType) => {
|
|
181
200
|
const newPromise = (async () => {
|
|
201
|
+
// Wait until downloading has ended
|
|
182
202
|
await promise;
|
|
183
|
-
return core_1.parseBody(
|
|
203
|
+
return core_1.parseBody(globalResponse, responseType, options.encoding);
|
|
184
204
|
})();
|
|
185
205
|
Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise));
|
|
186
206
|
return newPromise;
|
|
187
207
|
};
|
|
188
208
|
promise.json = () => {
|
|
189
|
-
if (
|
|
209
|
+
if (!globalRequest.writableFinished && options.headers.accept === undefined) {
|
|
190
210
|
options.headers.accept = 'application/json';
|
|
191
211
|
}
|
|
192
212
|
return shortcut('json');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import PCancelable = require('p-cancelable');
|
|
3
3
|
import { CancelError } from 'p-cancelable';
|
|
4
|
-
import { Options as RequestOptions, NormalizedOptions as RequestNormalizedOptions, Defaults as RequestDefaults, Hooks as RequestHooks, Response as RequestResponse, RequestError, MaxRedirectsError, CacheError, UploadError, TimeoutError, HTTPError, ReadError, UnsupportedProtocolError, HookEvent as RequestHookEvent, InitHook, BeforeRequestHook, BeforeRedirectHook, BeforeErrorHook, Progress, Headers, RequestFunction, Method, RequestEvents } from '../core';
|
|
4
|
+
import { Options as RequestOptions, NormalizedOptions as RequestNormalizedOptions, Defaults as RequestDefaults, Hooks as RequestHooks, Response as RequestResponse, RequestError, MaxRedirectsError, CacheError, UploadError, TimeoutError, HTTPError, ReadError, UnsupportedProtocolError, HookEvent as RequestHookEvent, InitHook, BeforeRequestHook, BeforeRedirectHook, BeforeErrorHook, Progress, Headers, RequestFunction, Agents, Method, PromiseCookieJar, RequestEvents } from '../core';
|
|
5
5
|
import PromisableRequest from './core';
|
|
6
6
|
export declare type ResponseType = 'json' | 'buffer' | 'text';
|
|
7
7
|
export interface Response<T = unknown> extends RequestResponse<T> {
|
|
@@ -35,6 +35,7 @@ export interface PaginationOptions<T> {
|
|
|
35
35
|
paginate?: (response: Response, allItems: T[], currentItems: T[]) => Options | false;
|
|
36
36
|
shouldContinue?: (item: T, allItems: T[], currentItems: T[]) => boolean;
|
|
37
37
|
countLimit?: number;
|
|
38
|
+
requestLimit?: number;
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
export interface Options extends RequestOptions, PaginationOptions<unknown> {
|
|
@@ -64,7 +65,7 @@ export interface Defaults extends RequestDefaults {
|
|
|
64
65
|
}
|
|
65
66
|
export declare class ParseError extends RequestError {
|
|
66
67
|
readonly response: Response;
|
|
67
|
-
constructor(error: Error, response: Response
|
|
68
|
+
constructor(error: Error, response: Response);
|
|
68
69
|
}
|
|
69
70
|
export interface CancelableRequest<T extends Response | Response['body'] = Response['body']> extends PCancelable<T>, RequestEvents<CancelableRequest<T>> {
|
|
70
71
|
json<ReturnType>(): CancelableRequest<ReturnType>;
|
|
@@ -74,4 +75,4 @@ export interface CancelableRequest<T extends Response | Response['body'] = Respo
|
|
|
74
75
|
export declare type HookEvent = RequestHookEvent | 'beforeRetry' | 'afterResponse';
|
|
75
76
|
export { RequestError, MaxRedirectsError, CacheError, UploadError, TimeoutError, HTTPError, ReadError, UnsupportedProtocolError, CancelError };
|
|
76
77
|
export { InitHook, BeforeRequestHook, BeforeRedirectHook, BeforeErrorHook };
|
|
77
|
-
export { Progress, Headers, RequestFunction };
|
|
78
|
+
export { Progress, Headers, RequestFunction, Agents, Method, PromiseCookieJar };
|
|
@@ -14,8 +14,9 @@ exports.HTTPError = core_1.HTTPError;
|
|
|
14
14
|
exports.ReadError = core_1.ReadError;
|
|
15
15
|
exports.UnsupportedProtocolError = core_1.UnsupportedProtocolError;
|
|
16
16
|
class ParseError extends core_1.RequestError {
|
|
17
|
-
constructor(error, response
|
|
18
|
-
|
|
17
|
+
constructor(error, response) {
|
|
18
|
+
const { options } = response.request;
|
|
19
|
+
super(`${error.message} in "${options.url.toString()}"`, error, response.request);
|
|
19
20
|
this.name = 'ParseError';
|
|
20
21
|
Object.defineProperty(this, 'response', {
|
|
21
22
|
enumerable: false,
|
|
@@ -25,6 +25,10 @@ declare const kUnproxyEvents: unique symbol;
|
|
|
25
25
|
declare const kIsFromCache: unique symbol;
|
|
26
26
|
declare const kCancelTimeouts: unique symbol;
|
|
27
27
|
declare const kStartedReading: unique symbol;
|
|
28
|
+
declare const kStopReading: unique symbol;
|
|
29
|
+
declare const kTriggerRead: unique symbol;
|
|
30
|
+
declare const kBody: unique symbol;
|
|
31
|
+
declare const kJobs: unique symbol;
|
|
28
32
|
export declare const kIsNormalizedAlready: unique symbol;
|
|
29
33
|
export interface Agents {
|
|
30
34
|
http?: http.Agent;
|
|
@@ -120,7 +124,6 @@ export interface NormalizedOptions extends Options {
|
|
|
120
124
|
cache?: string | CacheableRequest.StorageAdapter;
|
|
121
125
|
throwHttpErrors: boolean;
|
|
122
126
|
dnsCache?: CacheableLookup;
|
|
123
|
-
cacheableRequest?: (options: string | URL | http.RequestOptions, callback?: (response: http.ServerResponse | ResponseLike) => void) => CacheableRequest.Emitter;
|
|
124
127
|
http2: boolean;
|
|
125
128
|
allowGetBody: boolean;
|
|
126
129
|
rejectUnauthorized: boolean;
|
|
@@ -174,6 +177,7 @@ export interface PlainResponse extends IncomingMessageWithTimings {
|
|
|
174
177
|
}
|
|
175
178
|
export interface Response<T = unknown> extends PlainResponse {
|
|
176
179
|
body: T;
|
|
180
|
+
rawBody: Buffer;
|
|
177
181
|
retryCount: number;
|
|
178
182
|
}
|
|
179
183
|
export interface RequestEvents<T> {
|
|
@@ -191,29 +195,39 @@ export declare class RequestError extends Error {
|
|
|
191
195
|
readonly timings?: Timings;
|
|
192
196
|
constructor(message: string, error: Partial<Error & {
|
|
193
197
|
code?: string;
|
|
194
|
-
}>,
|
|
198
|
+
}>, self: Request | NormalizedOptions);
|
|
195
199
|
}
|
|
196
200
|
export declare class MaxRedirectsError extends RequestError {
|
|
197
201
|
readonly response: Response;
|
|
198
|
-
|
|
202
|
+
readonly request: Request;
|
|
203
|
+
readonly timings: Timings;
|
|
204
|
+
constructor(request: Request);
|
|
199
205
|
}
|
|
200
206
|
export declare class HTTPError extends RequestError {
|
|
201
207
|
readonly response: Response;
|
|
202
|
-
|
|
208
|
+
readonly request: Request;
|
|
209
|
+
readonly timings: Timings;
|
|
210
|
+
constructor(response: Response);
|
|
203
211
|
}
|
|
204
212
|
export declare class CacheError extends RequestError {
|
|
205
|
-
|
|
213
|
+
readonly request: Request;
|
|
214
|
+
constructor(error: Error, request: Request);
|
|
206
215
|
}
|
|
207
216
|
export declare class UploadError extends RequestError {
|
|
208
|
-
|
|
217
|
+
readonly request: Request;
|
|
218
|
+
constructor(error: Error, request: Request);
|
|
209
219
|
}
|
|
210
220
|
export declare class TimeoutError extends RequestError {
|
|
221
|
+
readonly request: Request;
|
|
211
222
|
readonly timings: Timings;
|
|
212
223
|
readonly event: string;
|
|
213
|
-
constructor(error: TimedOutTimeoutError, timings: Timings,
|
|
224
|
+
constructor(error: TimedOutTimeoutError, timings: Timings, request: Request);
|
|
214
225
|
}
|
|
215
226
|
export declare class ReadError extends RequestError {
|
|
216
|
-
|
|
227
|
+
readonly request: Request;
|
|
228
|
+
readonly response: Response;
|
|
229
|
+
readonly timings: Timings;
|
|
230
|
+
constructor(error: Error, request: Request);
|
|
217
231
|
}
|
|
218
232
|
export declare class UnsupportedProtocolError extends RequestError {
|
|
219
233
|
constructor(options: NormalizedOptions);
|
|
@@ -224,6 +238,10 @@ export default class Request extends Duplex implements RequestEvents<Request> {
|
|
|
224
238
|
_cannotHaveBody: boolean;
|
|
225
239
|
[kDownloadedSize]: number;
|
|
226
240
|
[kUploadedSize]: number;
|
|
241
|
+
[kStopReading]: boolean;
|
|
242
|
+
[kTriggerRead]: boolean;
|
|
243
|
+
[kBody]: Options['body'];
|
|
244
|
+
[kJobs]: Array<() => void>;
|
|
227
245
|
[kBodySize]?: number;
|
|
228
246
|
[kServerResponsesPiped]: Set<ServerResponse>;
|
|
229
247
|
[kIsFromCache]?: boolean;
|
|
@@ -236,9 +254,8 @@ export default class Request extends Duplex implements RequestEvents<Request> {
|
|
|
236
254
|
_progressCallbacks: Array<() => void>;
|
|
237
255
|
options: NormalizedOptions;
|
|
238
256
|
requestUrl: string;
|
|
239
|
-
|
|
257
|
+
requestInitialized: boolean;
|
|
240
258
|
redirects: string[];
|
|
241
|
-
errored: boolean;
|
|
242
259
|
constructor(url: string | URL, options?: Options, defaults?: Defaults);
|
|
243
260
|
static normalizeArguments(url?: string | URL, options?: Options, defaults?: Defaults): NormalizedOptions;
|
|
244
261
|
_lockWrite(): void;
|
|
@@ -246,6 +263,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
|
|
|
246
263
|
_finalizeBody(): Promise<void>;
|
|
247
264
|
_onResponse(response: IncomingMessage): Promise<void>;
|
|
248
265
|
_onRequest(request: ClientRequest): void;
|
|
266
|
+
_createCacheableRequest(url: URL, options: RequestOptions): Promise<ClientRequest | ResponseLike>;
|
|
249
267
|
_makeRequest(): Promise<void>;
|
|
250
268
|
_beforeError(error: Error): Promise<void>;
|
|
251
269
|
_read(): void;
|
|
@@ -255,6 +273,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
|
|
|
255
273
|
_destroy(error: Error | null, callback: (error: Error | null) => void): void;
|
|
256
274
|
get ip(): string | undefined;
|
|
257
275
|
get aborted(): boolean;
|
|
276
|
+
get socket(): Socket | undefined;
|
|
258
277
|
get downloadProgress(): Progress;
|
|
259
278
|
get uploadProgress(): Progress;
|
|
260
279
|
get timings(): Timings | undefined;
|