got 12.0.0-beta.1 → 12.0.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.
@@ -1,12 +1,14 @@
1
- import { promisify, inspect } from 'util';
2
- import { URL, URLSearchParams } from 'url';
3
- import { checkServerIdentity } from 'tls';
4
- import { request as httpRequest } from 'http';
5
- import { request as httpsRequest } from 'https';
1
+ import process from 'node:process';
2
+ import { promisify, inspect } from 'node:util';
3
+ import { URL, URLSearchParams } from 'node:url';
4
+ import { checkServerIdentity } from 'node:tls';
5
+ import { request as httpRequest } from 'node:http';
6
+ import { request as httpsRequest } from 'node:https';
6
7
  import is, { assert } from '@sindresorhus/is';
7
8
  import lowercaseKeys from 'lowercase-keys';
8
9
  import CacheableLookup from 'cacheable-lookup';
9
10
  import http2wrapper from 'http2-wrapper';
11
+ import { isFormDataLike } from 'form-data-encoder';
10
12
  import parseLinkHeader from './parse-link-header.js';
11
13
  const [major, minor] = process.versions.node.split('.').map(v => Number(v));
12
14
  function validateSearchParameters(searchParameters) {
@@ -162,7 +164,9 @@ const defaultInternals = {
162
164
  const parsed = parseLinkHeader(rawLinkHeader);
163
165
  const next = parsed.find(entry => entry.parameters.rel === 'next' || entry.parameters.rel === '"next"');
164
166
  if (next) {
165
- return { url: next.reference };
167
+ return {
168
+ url: new URL(next.reference, response.url),
169
+ };
166
170
  }
167
171
  return false;
168
172
  },
@@ -200,7 +204,7 @@ const cloneInternals = (internals) => {
200
204
  beforeRetry: [...hooks.beforeRetry],
201
205
  afterResponse: [...hooks.afterResponse],
202
206
  },
203
- searchParameters: internals.searchParams ? new URLSearchParams(internals.searchParams) : undefined,
207
+ searchParams: internals.searchParams ? new URLSearchParams(internals.searchParams) : undefined,
204
208
  pagination: { ...internals.pagination },
205
209
  };
206
210
  if (result.url !== undefined) {
@@ -208,6 +212,71 @@ const cloneInternals = (internals) => {
208
212
  }
209
213
  return result;
210
214
  };
215
+ const cloneRaw = (raw) => {
216
+ const { hooks, retry } = raw;
217
+ const result = { ...raw };
218
+ if (is.object(raw.context)) {
219
+ result.context = { ...raw.context };
220
+ }
221
+ if (is.object(raw.cacheOptions)) {
222
+ result.cacheOptions = { ...raw.cacheOptions };
223
+ }
224
+ if (is.object(raw.https)) {
225
+ result.https = { ...raw.https };
226
+ }
227
+ if (is.object(raw.cacheOptions)) {
228
+ result.cacheOptions = { ...result.cacheOptions };
229
+ }
230
+ if (is.object(raw.agent)) {
231
+ result.agent = { ...raw.agent };
232
+ }
233
+ if (is.object(raw.headers)) {
234
+ result.headers = { ...raw.headers };
235
+ }
236
+ if (is.object(retry)) {
237
+ result.retry = { ...retry };
238
+ if (is.array(retry.errorCodes)) {
239
+ result.retry.errorCodes = [...retry.errorCodes];
240
+ }
241
+ if (is.array(retry.methods)) {
242
+ result.retry.methods = [...retry.methods];
243
+ }
244
+ if (is.array(retry.statusCodes)) {
245
+ result.retry.statusCodes = [...retry.statusCodes];
246
+ }
247
+ }
248
+ if (is.object(raw.timeout)) {
249
+ result.timeout = { ...raw.timeout };
250
+ }
251
+ if (is.object(hooks)) {
252
+ result.hooks = {
253
+ ...hooks,
254
+ };
255
+ if (is.array(hooks.init)) {
256
+ result.hooks.init = [...hooks.init];
257
+ }
258
+ if (is.array(hooks.beforeRequest)) {
259
+ result.hooks.beforeRequest = [...hooks.beforeRequest];
260
+ }
261
+ if (is.array(hooks.beforeError)) {
262
+ result.hooks.beforeError = [...hooks.beforeError];
263
+ }
264
+ if (is.array(hooks.beforeRedirect)) {
265
+ result.hooks.beforeRedirect = [...hooks.beforeRedirect];
266
+ }
267
+ if (is.array(hooks.beforeRetry)) {
268
+ result.hooks.beforeRetry = [...hooks.beforeRetry];
269
+ }
270
+ if (is.array(hooks.afterResponse)) {
271
+ result.hooks.afterResponse = [...hooks.afterResponse];
272
+ }
273
+ }
274
+ // TODO: raw.searchParams
275
+ if (is.object(raw.pagination)) {
276
+ result.pagination = { ...raw.pagination };
277
+ }
278
+ return result;
279
+ };
211
280
  const getHttp2TimeoutOption = (internals) => {
212
281
  const delays = [internals.timeout.socket, internals.timeout.connect, internals.timeout.lookup, internals.timeout.request, internals.timeout.secureConnect].filter(delay => typeof delay === 'number');
213
282
  if (delays.length > 0) {
@@ -313,19 +382,9 @@ export default class Options {
313
382
  }
314
383
  return;
315
384
  }
385
+ options = cloneRaw(options);
316
386
  init(this, options, this);
317
387
  init(options, options, this);
318
- // This is way much faster than cloning ^_^
319
- Object.freeze(options);
320
- Object.freeze(options.hooks);
321
- Object.freeze(options.https);
322
- Object.freeze(options.cacheOptions);
323
- Object.freeze(options.agent);
324
- Object.freeze(options.headers);
325
- Object.freeze(options.timeout);
326
- Object.freeze(options.retry);
327
- Object.freeze(options.hooks);
328
- Object.freeze(options.context);
329
388
  this._merging = true;
330
389
  // Always merge `isStream` first
331
390
  if ('isStream' in options) {
@@ -539,7 +598,7 @@ export default class Options {
539
598
 
540
599
  __Note #4__: This option is not enumerable and will not be merged with the instance defaults.
541
600
 
542
- The `content-length` header will be automatically set if `body` is a `string` / `Buffer` / [`form-data` instance](https://github.com/form-data/form-data), and `content-length` and `transfer-encoding` are not manually set in `options.headers`.
601
+ The `content-length` header will be automatically set if `body` is a `string` / `Buffer` / [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) / [`form-data` instance](https://github.com/form-data/form-data), and `content-length` and `transfer-encoding` are not manually set in `options.headers`.
543
602
 
544
603
  Since Got 12, the `content-length` is not automatically set when `body` is a `fs.createReadStream`.
545
604
  */
@@ -547,7 +606,7 @@ export default class Options {
547
606
  return this._internals.body;
548
607
  }
549
608
  set body(value) {
550
- assert.any([is.string, is.buffer, is.nodeStream, is.generator, is.asyncGenerator, is.undefined], value);
609
+ assert.any([is.string, is.buffer, is.nodeStream, is.generator, is.asyncGenerator, isFormDataLike, is.undefined], value);
551
610
  if (is.nodeStream(value)) {
552
611
  assert.truthy(value.readable);
553
612
  }
@@ -607,10 +666,10 @@ export default class Options {
607
666
  @example
608
667
  ```
609
668
  await got('https://example.com/?query=a b'); //=> https://example.com/?query=a%20b
610
- await got('https://example.com/', {searchParameters: {query: 'a b'}}); //=> https://example.com/?query=a+b
669
+ await got('https://example.com/', {searchParams: {query: 'a b'}}); //=> https://example.com/?query=a+b
611
670
 
612
- // The query string is overridden by `searchParameters`
613
- await got('https://example.com/?query=a b', {searchParameters: {query: 'a b'}}); //=> https://example.com/?query=a+b
671
+ // The query string is overridden by `searchParams`
672
+ await got('https://example.com/?query=a b', {searchParams: {query: 'a b'}}); //=> https://example.com/?query=a+b
614
673
  ```
615
674
  */
616
675
  get url() {
@@ -719,11 +778,11 @@ export default class Options {
719
778
  ```
720
779
  import got from 'got';
721
780
 
722
- const searchParameters = new URLSearchParams([['key', 'a'], ['key', 'b']]);
781
+ const searchParams = new URLSearchParams([['key', 'a'], ['key', 'b']]);
723
782
 
724
- await got('https://example.com', {searchParameters});
783
+ await got('https://example.com', {searchParams});
725
784
 
726
- console.log(searchParameters.toString());
785
+ console.log(searchParams.toString());
727
786
  //=> 'key=a&key=b'
728
787
  ```
729
788
  */
@@ -731,6 +790,9 @@ export default class Options {
731
790
  if (this._internals.url) {
732
791
  return this._internals.url.searchParams;
733
792
  }
793
+ if (this._internals.searchParams === undefined) {
794
+ this._internals.searchParams = new URLSearchParams();
795
+ }
734
796
  return this._internals.searchParams;
735
797
  }
736
798
  set searchParams(value) {
@@ -743,11 +805,14 @@ export default class Options {
743
805
  }
744
806
  return;
745
807
  }
746
- let searchParameters = (this.searchParams ?? new URLSearchParams());
808
+ const searchParameters = this.searchParams;
747
809
  let updated;
748
- if (is.string(value) || (value instanceof URLSearchParams)) {
810
+ if (is.string(value)) {
749
811
  updated = new URLSearchParams(value);
750
812
  }
813
+ else if (value instanceof URLSearchParams) {
814
+ updated = value;
815
+ }
751
816
  else {
752
817
  validateSearchParameters(value);
753
818
  updated = new URLSearchParams();
@@ -757,21 +822,27 @@ export default class Options {
757
822
  if (entry === null) {
758
823
  updated.append(key, '');
759
824
  }
760
- else if (entry !== undefined) {
825
+ else if (entry === undefined) {
826
+ searchParameters.delete(key);
827
+ }
828
+ else {
761
829
  updated.append(key, entry);
762
830
  }
763
831
  }
764
832
  }
765
833
  if (this._merging) {
766
- // eslint-disable-next-line unicorn/no-array-for-each
767
- updated.forEach((value, key) => {
768
- searchParameters.set(key, value);
769
- });
834
+ // These keys will be replaced
835
+ for (const key of updated.keys()) {
836
+ searchParameters.delete(key);
837
+ }
838
+ for (const [key, value] of updated) {
839
+ searchParameters.append(key, value);
840
+ }
770
841
  }
771
- else {
772
- searchParameters = updated;
842
+ else if (url) {
843
+ url.search = searchParameters.toString();
773
844
  }
774
- if (!url) {
845
+ else {
775
846
  this._internals.searchParams = searchParameters;
776
847
  }
777
848
  }
@@ -870,24 +941,27 @@ export default class Options {
870
941
  if (!(knownHookEvent in this._internals.hooks)) {
871
942
  throw new Error(`Unexpected hook event: ${knownHookEvent}`);
872
943
  }
873
- const hooks = value[knownHookEvent];
944
+ const typedKnownHookEvent = knownHookEvent;
945
+ const typedValue = value;
946
+ const hooks = typedValue[typedKnownHookEvent];
874
947
  assert.any([is.array, is.undefined], hooks);
875
- for (const hook of hooks) {
876
- assert.function_(hook);
948
+ if (hooks) {
949
+ for (const hook of hooks) {
950
+ assert.function_(hook);
951
+ }
877
952
  }
878
953
  if (this._merging) {
879
954
  if (hooks) {
880
955
  // @ts-expect-error FIXME
881
- this._internals.hooks[knownHookEvent].push(...hooks);
956
+ this._internals.hooks[typedKnownHookEvent].push(...hooks);
882
957
  }
883
958
  }
884
- else if (hooks) {
885
- // @ts-expect-error FIXME
886
- this._internals.hooks[knownHookEvent] = [...hooks];
887
- }
888
959
  else {
960
+ if (!hooks) {
961
+ throw new Error(`Missing hook event: ${knownHookEvent}`);
962
+ }
889
963
  // @ts-expect-error FIXME
890
- this._internals.hooks[knownHookEvent] = [];
964
+ this._internals.hooks[knownHookEvent] = [...hooks];
891
965
  }
892
966
  }
893
967
  }
@@ -961,36 +1035,34 @@ export default class Options {
961
1035
  }
962
1036
  get username() {
963
1037
  const url = this._internals.url;
964
- if (url) {
965
- return url.username;
966
- }
967
- return this._internals.username;
1038
+ const value = url ? url.username : this._internals.username;
1039
+ return decodeURIComponent(value);
968
1040
  }
969
1041
  set username(value) {
970
1042
  assert.string(value);
971
1043
  const url = this._internals.url;
1044
+ const fixedValue = encodeURIComponent(value);
972
1045
  if (url) {
973
- url.username = value;
1046
+ url.username = fixedValue;
974
1047
  }
975
1048
  else {
976
- this._internals.username = value;
1049
+ this._internals.username = fixedValue;
977
1050
  }
978
1051
  }
979
1052
  get password() {
980
1053
  const url = this._internals.url;
981
- if (url) {
982
- return url.password;
983
- }
984
- return this._internals.password;
1054
+ const value = url ? url.password : this._internals.password;
1055
+ return decodeURIComponent(value);
985
1056
  }
986
1057
  set password(value) {
987
1058
  assert.string(value);
988
1059
  const url = this._internals.url;
1060
+ const fixedValue = encodeURIComponent(value);
989
1061
  if (url) {
990
- url.password = value;
1062
+ url.password = fixedValue;
991
1063
  }
992
1064
  else {
993
- this._internals.password = value;
1065
+ this._internals.password = fixedValue;
994
1066
  }
995
1067
  }
996
1068
  /**
@@ -1426,6 +1498,7 @@ export default class Options {
1426
1498
  assert.any([is.number, is.undefined], value);
1427
1499
  this._internals.maxHeaderSize = value;
1428
1500
  }
1501
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1429
1502
  toJSON() {
1430
1503
  return { ...this._internals };
1431
1504
  }
@@ -1454,6 +1527,8 @@ export default class Options {
1454
1527
  ...internals.cacheOptions,
1455
1528
  ...this._unixOptions,
1456
1529
  // HTTPS options
1530
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1531
+ ALPNProtocols: https.alpnProtocols,
1457
1532
  ca: https.certificateAuthority,
1458
1533
  cert: https.certificate,
1459
1534
  key: https.key,
@@ -1495,6 +1570,9 @@ export default class Options {
1495
1570
  }
1496
1571
  getFallbackRequestFunction() {
1497
1572
  const url = this._internals.url;
1573
+ if (!url) {
1574
+ return;
1575
+ }
1498
1576
  if (url.protocol === 'https:') {
1499
1577
  if (this._internals.http2) {
1500
1578
  if (major < 15 || (major === 15 && minor < 10)) {
@@ -1512,53 +1590,21 @@ export default class Options {
1512
1590
  const options = this._internals;
1513
1591
  Object.freeze(options);
1514
1592
  Object.freeze(options.hooks);
1593
+ Object.freeze(options.hooks.afterResponse);
1594
+ Object.freeze(options.hooks.beforeError);
1595
+ Object.freeze(options.hooks.beforeRedirect);
1596
+ Object.freeze(options.hooks.beforeRequest);
1597
+ Object.freeze(options.hooks.beforeRetry);
1598
+ Object.freeze(options.hooks.init);
1515
1599
  Object.freeze(options.https);
1516
1600
  Object.freeze(options.cacheOptions);
1517
1601
  Object.freeze(options.agent);
1518
1602
  Object.freeze(options.headers);
1519
1603
  Object.freeze(options.timeout);
1520
1604
  Object.freeze(options.retry);
1521
- Object.freeze(options.hooks);
1605
+ Object.freeze(options.retry.errorCodes);
1606
+ Object.freeze(options.retry.methods);
1607
+ Object.freeze(options.retry.statusCodes);
1522
1608
  Object.freeze(options.context);
1523
1609
  }
1524
1610
  }
1525
- // It's user responsibility to make sensitive data in `context` non-enumerable
1526
- const nonEnumerableProperties = new Set([
1527
- // Functions
1528
- 'constructor',
1529
- 'merge',
1530
- 'tryMerge',
1531
- 'createNativeRequestOptions',
1532
- 'getRequestFunction',
1533
- 'getFallbackRequestFunction',
1534
- 'freeze',
1535
- // Payload
1536
- 'body',
1537
- 'form',
1538
- 'json',
1539
- // Getters that always throw
1540
- 'auth',
1541
- 'followRedirects',
1542
- 'searchParameters',
1543
- // May contain sensitive data
1544
- 'username',
1545
- 'password',
1546
- 'headers',
1547
- 'searchParams',
1548
- 'url',
1549
- // Privates
1550
- '_unixOptions',
1551
- '_internals',
1552
- '_merging',
1553
- '_init',
1554
- ]);
1555
- // We want all the properties to be enumerable, so people instead doing
1556
- // `util.inspect(options, {getters: true, showHidden: true})`
1557
- // can do just `util.inspect(options, {getters: true})`.
1558
- const propertyDescriptors = {};
1559
- const keys = Object.getOwnPropertyNames(Options.prototype).filter(property => !nonEnumerableProperties.has(property));
1560
- const makeEnumerable = { enumerable: true };
1561
- for (const key of keys) {
1562
- propertyDescriptors[key] = makeEnumerable;
1563
- }
1564
- Object.defineProperties(Options.prototype, propertyDescriptors);
@@ -1,4 +1,6 @@
1
1
  /// <reference types="node" />
2
+ import type { Buffer } from 'node:buffer';
3
+ import type { URL } from 'node:url';
2
4
  import type { IncomingMessageWithTimings, Timings } from '@szmarczak/http-timer';
3
5
  import { RequestError } from './errors.js';
4
6
  import type { ParseJsonFunction, ResponseType } from './options.js';
@@ -28,12 +28,12 @@ export const parseBody = (response, responseType, parseJson, encoding) => {
28
28
  if (responseType === 'buffer') {
29
29
  return rawBody;
30
30
  }
31
- throw new ParseError({
32
- message: `Unknown body type '${responseType}'`,
33
- name: 'Error',
34
- }, response);
35
31
  }
36
32
  catch (error) {
37
33
  throw new ParseError(error, response);
38
34
  }
35
+ throw new ParseError({
36
+ message: `Unknown body type '${responseType}'`,
37
+ name: 'Error',
38
+ }, response);
39
39
  };
@@ -1,4 +1,4 @@
1
- import { ClientRequest } from 'http';
1
+ import { ClientRequest } from 'node:http';
2
2
  declare const reentry: unique symbol;
3
3
  interface TimedOutOptions {
4
4
  host?: string;
@@ -1,4 +1,4 @@
1
- import net from 'net';
1
+ import net from 'node:net';
2
2
  import unhandler from './utils/unhandle.js';
3
3
  const reentry = Symbol('reentry');
4
4
  const noop = () => { };
@@ -1,3 +1,3 @@
1
1
  /// <reference types="node" />
2
- import { ClientRequestArgs } from 'http';
2
+ import { ClientRequestArgs } from 'node:http';
3
3
  export default function getBodySize(body: unknown, headers: ClientRequestArgs['headers']): Promise<number | undefined>;
@@ -1,4 +1,5 @@
1
- import { promisify } from 'util';
1
+ import { Buffer } from 'node:buffer';
2
+ import { promisify } from 'node:util';
2
3
  import is from '@sindresorhus/is';
3
4
  import isFormData from './is-form-data.js';
4
5
  export default async function getBodySize(body, headers) {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import type { Writable, Readable } from 'stream';
3
- import type { ClientRequest } from 'http';
2
+ import type { Writable, Readable } from 'node:stream';
3
+ import type { ClientRequest } from 'node:http';
4
4
  declare function isClientRequest(clientRequest: Writable | Readable): clientRequest is ClientRequest;
5
5
  export default isClientRequest;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { Readable } from 'stream';
2
+ import { Readable } from 'node:stream';
3
3
  interface FormData extends Readable {
4
4
  getBoundary: () => string;
5
5
  getLength: (callback: (error: Error | null, length: number) => void) => void;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { URL } from 'url';
2
+ import { URL } from 'node:url';
3
3
  export interface URLOptions {
4
4
  href?: string;
5
5
  protocol?: string;
@@ -1,5 +1,5 @@
1
1
  /* istanbul ignore file: deprecated */
2
- import { URL } from 'url';
2
+ import { URL } from 'node:url';
3
3
  const keys = [
4
4
  'protocol',
5
5
  'host',
@@ -1,3 +1,3 @@
1
1
  /// <reference types="node" />
2
- import { EventEmitter } from 'events';
3
- export default function proxyEvents(from: EventEmitter, to: EventEmitter, events: string[]): () => void;
2
+ import { EventEmitter } from 'node:events';
3
+ export default function proxyEvents(from: EventEmitter, to: EventEmitter, events: Readonly<string[]>): () => void;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { EventEmitter } from 'events';
2
+ import { EventEmitter } from 'node:events';
3
3
  declare type Origin = EventEmitter;
4
4
  declare type Event = string | symbol;
5
5
  declare type Fn = (...args: any[]) => void;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { URL, UrlWithStringQuery } from 'url';
2
+ import { URL, UrlWithStringQuery } from 'node:url';
3
3
  export interface LegacyUrlOptions {
4
4
  protocol: string;
5
5
  hostname: string;
@@ -6,7 +6,7 @@ import Options from './core/options.js';
6
6
  const delay = async (ms) => new Promise(resolve => {
7
7
  setTimeout(resolve, ms);
8
8
  });
9
- const isGotInstance = (value) => ('defaults' in value && 'options' in value.defaults);
9
+ const isGotInstance = (value) => is.function_(value);
10
10
  const aliases = [
11
11
  'get',
12
12
  'post',
@@ -146,6 +146,7 @@ const create = (defaults) => {
146
146
  }
147
147
  else {
148
148
  normalizedOptions.merge(optionsToMerge);
149
+ assert.any([is.urlInstance, is.undefined], optionsToMerge.url);
149
150
  if (optionsToMerge.url !== undefined) {
150
151
  normalizedOptions.prefixUrl = '';
151
152
  normalizedOptions.url = optionsToMerge.url;
@@ -1,9 +1,10 @@
1
1
  /// <reference types="node" />
2
- import type { URL } from 'url';
2
+ import type { Buffer } from 'node:buffer';
3
+ import type { URL } from 'node:url';
3
4
  import type { CancelableRequest } from './as-promise/types.js';
4
5
  import type { Response } from './core/response.js';
5
6
  import type Options from './core/options.js';
6
- import type { PaginationOptions, OptionsInit, InternalsType } from './core/options.js';
7
+ import type { PaginationOptions, OptionsInit } from './core/options.js';
7
8
  import type Request from './core/index.js';
8
9
  declare type Except<ObjectType, KeysType extends keyof ObjectType> = Pick<ObjectType, Exclude<keyof ObjectType, KeysType>>;
9
10
  declare type Merge<FirstType, SecondType> = Except<FirstType, Extract<keyof FirstType, keyof SecondType>> & SecondType;
@@ -14,7 +15,7 @@ export interface InstanceDefaults {
14
15
  /**
15
16
  An object containing the default options of Got.
16
17
  */
17
- options: InternalsType | Options;
18
+ options: Options;
18
19
  /**
19
20
  An array of functions. You execute them directly by calling `got()`.
20
21
  They are some sort of "global hooks" - these functions are called first.