@whatwg-node/node-fetch 0.5.0-alpha-20230710154907-66260af → 0.5.0-alpha-20230710180612-32e574f

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/cjs/Request.js CHANGED
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PonyfillRequest = void 0;
4
4
  const Body_js_1 = require("./Body.js");
5
5
  const Headers_js_1 = require("./Headers.js");
6
- const utils_js_1 = require("./utils.js");
7
6
  function isRequest(input) {
8
7
  return input[Symbol.toStringTag] === 'Request';
9
8
  }
@@ -42,7 +41,7 @@ class PonyfillRequest extends Body_js_1.PonyfillBody {
42
41
  this.referrer = requestInit?.referrer || 'about:client';
43
42
  this.referrerPolicy = requestInit?.referrerPolicy || 'no-referrer';
44
43
  this._signal = requestInit?.signal;
45
- this.headersSerializer = requestInit?.headersSerializer || utils_js_1.defaultHeadersSerializer;
44
+ this.headersSerializer = requestInit?.headersSerializer;
46
45
  this.url = url || '';
47
46
  this.destination = 'document';
48
47
  this.priority = 'auto';
package/cjs/fetch.js CHANGED
@@ -1,46 +1,21 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.fetchPonyfill = void 0;
27
- /* eslint-disable @typescript-eslint/no-this-alias */
28
4
  const fs_1 = require("fs");
29
- const stream_1 = require("stream");
30
5
  const url_1 = require("url");
31
6
  const Blob_js_1 = require("./Blob.js");
32
- const Headers_js_1 = require("./Headers.js");
7
+ const fetchCurl_js_1 = require("./fetchCurl.js");
8
+ const fetchNodeHttp_js_1 = require("./fetchNodeHttp.js");
33
9
  const Request_js_1 = require("./Request.js");
34
10
  const Response_js_1 = require("./Response.js");
35
- const URL_js_1 = require("./URL.js");
36
- const utils_js_1 = require("./utils.js");
11
+ const BASE64_SUFFIX = ';base64';
37
12
  function getResponseForFile(url) {
38
13
  const path = (0, url_1.fileURLToPath)(url);
39
14
  const readable = (0, fs_1.createReadStream)(path);
40
15
  return new Response_js_1.PonyfillResponse(readable);
41
16
  }
42
17
  function getResponseForDataUri(url) {
43
- const [mimeType = 'text/plain', ...datas] = url.pathname.split(',');
18
+ const [mimeType = 'text/plain', ...datas] = url.split(',');
44
19
  const data = decodeURIComponent(datas.join(','));
45
20
  if (mimeType.endsWith(BASE64_SUFFIX)) {
46
21
  const buffer = Buffer.from(data, 'base64url');
@@ -59,89 +34,23 @@ function getResponseForDataUri(url) {
59
34
  },
60
35
  });
61
36
  }
62
- const BASE64_SUFFIX = ';base64';
63
37
  async function fetchPonyfill(info, init) {
64
38
  if (typeof info === 'string' || 'href' in info) {
65
39
  const ponyfillRequest = new Request_js_1.PonyfillRequest(info, init);
66
40
  return fetchPonyfill(ponyfillRequest);
67
41
  }
68
42
  const fetchRequest = info;
69
- const url = new URL_js_1.PonyfillURL(fetchRequest.url, 'http://localhost');
70
- if (url.protocol === 'data:') {
71
- const response = getResponseForDataUri(url);
43
+ if (fetchRequest.url.startsWith('data:')) {
44
+ const response = getResponseForDataUri(fetchRequest.url);
72
45
  return Promise.resolve(response);
73
46
  }
74
- if (url.protocol === 'file:') {
47
+ if (fetchRequest.url.startsWith('file:')) {
75
48
  const response = getResponseForFile(fetchRequest.url);
76
49
  return Promise.resolve(response);
77
50
  }
78
- const nodeReadable = (fetchRequest.body != null
79
- ? 'pipe' in fetchRequest.body
80
- ? fetchRequest.body
81
- : stream_1.Readable.from(fetchRequest.body)
82
- : null);
83
- const headersSerializer = fetchRequest.headersSerializer || utils_js_1.defaultHeadersSerializer;
84
- let size;
85
- const curlyHeaders = headersSerializer(fetchRequest.headers, value => {
86
- size = Number(value);
87
- });
88
- let easyNativeBinding;
89
- const curlyOptions = {
90
- // we want the unparsed binary response to be returned as a stream to us
91
- curlyStreamResponse: true,
92
- curlyResponseBodyParser: false,
93
- curlyProgressCallback() {
94
- if (easyNativeBinding == null) {
95
- easyNativeBinding = this;
96
- }
97
- return fetchRequest.signal.aborted ? 1 : 0;
98
- },
99
- upload: nodeReadable != null,
100
- transferEncoding: false,
101
- httpTransferDecoding: true,
102
- followLocation: fetchRequest.redirect === 'follow',
103
- maxRedirs: 20,
104
- acceptEncoding: '',
105
- curlyStreamUpload: nodeReadable,
106
- // this will just make libcurl use their own progress function (which is pretty neat)
107
- // curlyProgressCallback() { return CurlProgressFunc.Continue },
108
- // verbose: true,
109
- httpHeader: curlyHeaders,
110
- customRequest: fetchRequest.method,
111
- };
112
- if (size != null) {
113
- curlyOptions.inFileSize = size;
51
+ if (globalThis.libcurl) {
52
+ return (0, fetchCurl_js_1.fetchCurl)(fetchRequest);
114
53
  }
115
- const { curly, CurlCode, CurlPause } = globalThis['libcurl'] || (await Promise.resolve().then(() => __importStar(require('node-libcurl'))));
116
- fetchRequest.signal.onabort = () => {
117
- if (easyNativeBinding != null) {
118
- easyNativeBinding.pause(CurlPause.Recv);
119
- }
120
- };
121
- const curlyResult = await curly(fetchRequest.url, curlyOptions);
122
- const responseHeaders = new Headers_js_1.PonyfillHeaders();
123
- curlyResult.headers.forEach(headerInfo => {
124
- for (const key in headerInfo) {
125
- if (key === 'location' || (key === 'Location' && fetchRequest.redirect === 'error')) {
126
- throw new Error('redirects are not allowed');
127
- }
128
- if (key !== 'result') {
129
- responseHeaders.append(key, headerInfo[key]);
130
- }
131
- }
132
- });
133
- curlyResult.data.on('error', (err) => {
134
- if (err.isCurlError && err.code === CurlCode.CURLE_ABORTED_BY_CALLBACK) {
135
- // this is expected
136
- }
137
- else {
138
- throw err;
139
- }
140
- });
141
- return new Response_js_1.PonyfillResponse(curlyResult.data, {
142
- status: curlyResult.statusCode,
143
- headers: responseHeaders,
144
- url: info.url,
145
- });
54
+ return (0, fetchNodeHttp_js_1.fetchNodeHttp)(fetchRequest);
146
55
  }
147
56
  exports.fetchPonyfill = fetchPonyfill;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchCurl = void 0;
4
+ /* eslint-disable @typescript-eslint/no-this-alias */
5
+ const stream_1 = require("stream");
6
+ const Headers_js_1 = require("./Headers.js");
7
+ const Response_js_1 = require("./Response.js");
8
+ const utils_js_1 = require("./utils.js");
9
+ async function fetchCurl(fetchRequest) {
10
+ const nodeReadable = (fetchRequest.body != null
11
+ ? 'pipe' in fetchRequest.body
12
+ ? fetchRequest.body
13
+ : stream_1.Readable.from(fetchRequest.body)
14
+ : null);
15
+ const headersSerializer = fetchRequest.headersSerializer || utils_js_1.defaultHeadersSerializer;
16
+ let size;
17
+ const curlyHeaders = headersSerializer(fetchRequest.headers, value => {
18
+ size = Number(value);
19
+ });
20
+ let easyNativeBinding;
21
+ const curlyOptions = {
22
+ // we want the unparsed binary response to be returned as a stream to us
23
+ curlyStreamResponse: true,
24
+ curlyResponseBodyParser: false,
25
+ curlyProgressCallback() {
26
+ if (easyNativeBinding == null) {
27
+ easyNativeBinding = this;
28
+ }
29
+ return fetchRequest.signal.aborted ? 1 : 0;
30
+ },
31
+ upload: nodeReadable != null,
32
+ transferEncoding: false,
33
+ httpTransferDecoding: true,
34
+ followLocation: fetchRequest.redirect === 'follow',
35
+ maxRedirs: 20,
36
+ acceptEncoding: '',
37
+ curlyStreamUpload: nodeReadable,
38
+ // this will just make libcurl use their own progress function (which is pretty neat)
39
+ // curlyProgressCallback() { return CurlProgressFunc.Continue },
40
+ // verbose: true,
41
+ httpHeader: curlyHeaders,
42
+ customRequest: fetchRequest.method,
43
+ };
44
+ if (size != null) {
45
+ curlyOptions.inFileSize = size;
46
+ }
47
+ const { curly, CurlCode, CurlPause } = globalThis['libcurl'];
48
+ fetchRequest.signal.onabort = () => {
49
+ if (easyNativeBinding != null) {
50
+ easyNativeBinding.pause(CurlPause.Recv);
51
+ }
52
+ };
53
+ const curlyResult = await curly(fetchRequest.url, curlyOptions);
54
+ const responseHeaders = new Headers_js_1.PonyfillHeaders();
55
+ curlyResult.headers.forEach(headerInfo => {
56
+ for (const key in headerInfo) {
57
+ if (key === 'location' || (key === 'Location' && fetchRequest.redirect === 'error')) {
58
+ throw new Error('redirects are not allowed');
59
+ }
60
+ if (key !== 'result') {
61
+ responseHeaders.append(key, headerInfo[key]);
62
+ }
63
+ }
64
+ });
65
+ curlyResult.data.on('error', (err) => {
66
+ if (err.isCurlError && err.code === CurlCode.CURLE_ABORTED_BY_CALLBACK) {
67
+ // this is expected
68
+ }
69
+ else {
70
+ throw err;
71
+ }
72
+ });
73
+ return new Response_js_1.PonyfillResponse(curlyResult.data, {
74
+ status: curlyResult.statusCode,
75
+ headers: responseHeaders,
76
+ url: fetchRequest.url,
77
+ });
78
+ }
79
+ exports.fetchCurl = fetchCurl;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchNodeHttp = void 0;
4
+ const http_1 = require("http");
5
+ const https_1 = require("https");
6
+ const stream_1 = require("stream");
7
+ const zlib_1 = require("zlib");
8
+ const AbortError_js_1 = require("./AbortError.js");
9
+ const Request_js_1 = require("./Request.js");
10
+ const Response_js_1 = require("./Response.js");
11
+ const URL_js_1 = require("./URL.js");
12
+ const utils_js_1 = require("./utils.js");
13
+ function getRequestFnForProtocol(url) {
14
+ if (url.startsWith('http:')) {
15
+ return http_1.request;
16
+ }
17
+ else if (url.startsWith('https:')) {
18
+ return https_1.request;
19
+ }
20
+ throw new Error(`Unsupported protocol: ${url.split(':')[0] || url}`);
21
+ }
22
+ function fetchNodeHttp(fetchRequest) {
23
+ return new Promise((resolve, reject) => {
24
+ try {
25
+ const requestFn = getRequestFnForProtocol(fetchRequest.url);
26
+ const nodeReadable = (fetchRequest.body != null
27
+ ? 'pipe' in fetchRequest.body
28
+ ? fetchRequest.body
29
+ : stream_1.Readable.from(fetchRequest.body)
30
+ : null);
31
+ const headersSerializer = fetchRequest.headersSerializer || utils_js_1.getHeadersObj;
32
+ const nodeHeaders = headersSerializer(fetchRequest.headers);
33
+ const nodeRequest = requestFn(fetchRequest.url, {
34
+ method: fetchRequest.method,
35
+ headers: nodeHeaders,
36
+ signal: fetchRequest.signal,
37
+ agent: fetchRequest.agent,
38
+ });
39
+ // TODO: will be removed after v16 reaches EOL
40
+ fetchRequest.signal?.addEventListener('abort', () => {
41
+ if (!nodeRequest.aborted) {
42
+ nodeRequest.abort();
43
+ }
44
+ });
45
+ // TODO: will be removed after v16 reaches EOL
46
+ nodeRequest.once('abort', (reason) => {
47
+ reject(new AbortError_js_1.PonyfillAbortError(reason));
48
+ });
49
+ nodeRequest.once('response', nodeResponse => {
50
+ let responseBody = nodeResponse;
51
+ const contentEncoding = nodeResponse.headers['content-encoding'];
52
+ switch (contentEncoding) {
53
+ case 'x-gzip':
54
+ case 'gzip':
55
+ responseBody = nodeResponse.pipe((0, zlib_1.createGunzip)());
56
+ break;
57
+ case 'x-deflate':
58
+ case 'deflate':
59
+ responseBody = nodeResponse.pipe((0, zlib_1.createInflate)());
60
+ break;
61
+ case 'br':
62
+ responseBody = nodeResponse.pipe((0, zlib_1.createBrotliDecompress)());
63
+ break;
64
+ }
65
+ if (nodeResponse.headers.location) {
66
+ if (fetchRequest.redirect === 'error') {
67
+ const redirectError = new Error('Redirects are not allowed');
68
+ reject(redirectError);
69
+ nodeResponse.resume();
70
+ return;
71
+ }
72
+ if (fetchRequest.redirect === 'follow') {
73
+ const redirectedUrl = new URL_js_1.PonyfillURL(nodeResponse.headers.location, fetchRequest.url);
74
+ const redirectResponse$ = fetchNodeHttp(new Request_js_1.PonyfillRequest(redirectedUrl, fetchRequest));
75
+ resolve(redirectResponse$.then(redirectResponse => {
76
+ redirectResponse.redirected = true;
77
+ return redirectResponse;
78
+ }));
79
+ nodeResponse.resume();
80
+ return;
81
+ }
82
+ }
83
+ const ponyfillResponse = new Response_js_1.PonyfillResponse(responseBody, {
84
+ status: nodeResponse.statusCode,
85
+ statusText: nodeResponse.statusMessage,
86
+ headers: nodeResponse.headers,
87
+ url: fetchRequest.url,
88
+ });
89
+ resolve(ponyfillResponse);
90
+ });
91
+ nodeRequest.once('error', reject);
92
+ if (nodeReadable) {
93
+ nodeReadable.pipe(nodeRequest);
94
+ }
95
+ else {
96
+ nodeRequest.end();
97
+ }
98
+ }
99
+ catch (e) {
100
+ reject(e);
101
+ }
102
+ });
103
+ }
104
+ exports.fetchNodeHttp = fetchNodeHttp;
package/esm/Request.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { PonyfillBody } from './Body.js';
2
2
  import { isHeadersLike, PonyfillHeaders } from './Headers.js';
3
- import { defaultHeadersSerializer } from './utils.js';
4
3
  function isRequest(input) {
5
4
  return input[Symbol.toStringTag] === 'Request';
6
5
  }
@@ -39,7 +38,7 @@ export class PonyfillRequest extends PonyfillBody {
39
38
  this.referrer = requestInit?.referrer || 'about:client';
40
39
  this.referrerPolicy = requestInit?.referrerPolicy || 'no-referrer';
41
40
  this._signal = requestInit?.signal;
42
- this.headersSerializer = requestInit?.headersSerializer || defaultHeadersSerializer;
41
+ this.headersSerializer = requestInit?.headersSerializer;
43
42
  this.url = url || '';
44
43
  this.destination = 'document';
45
44
  this.priority = 'auto';
package/esm/fetch.js CHANGED
@@ -1,20 +1,18 @@
1
- /* eslint-disable @typescript-eslint/no-this-alias */
2
1
  import { createReadStream } from 'fs';
3
- import { Readable } from 'stream';
4
2
  import { fileURLToPath } from 'url';
5
3
  import { PonyfillBlob } from './Blob.js';
6
- import { PonyfillHeaders } from './Headers.js';
4
+ import { fetchCurl } from './fetchCurl.js';
5
+ import { fetchNodeHttp } from './fetchNodeHttp.js';
7
6
  import { PonyfillRequest } from './Request.js';
8
7
  import { PonyfillResponse } from './Response.js';
9
- import { PonyfillURL } from './URL.js';
10
- import { defaultHeadersSerializer } from './utils.js';
8
+ const BASE64_SUFFIX = ';base64';
11
9
  function getResponseForFile(url) {
12
10
  const path = fileURLToPath(url);
13
11
  const readable = createReadStream(path);
14
12
  return new PonyfillResponse(readable);
15
13
  }
16
14
  function getResponseForDataUri(url) {
17
- const [mimeType = 'text/plain', ...datas] = url.pathname.split(',');
15
+ const [mimeType = 'text/plain', ...datas] = url.split(',');
18
16
  const data = decodeURIComponent(datas.join(','));
19
17
  if (mimeType.endsWith(BASE64_SUFFIX)) {
20
18
  const buffer = Buffer.from(data, 'base64url');
@@ -33,88 +31,22 @@ function getResponseForDataUri(url) {
33
31
  },
34
32
  });
35
33
  }
36
- const BASE64_SUFFIX = ';base64';
37
34
  export async function fetchPonyfill(info, init) {
38
35
  if (typeof info === 'string' || 'href' in info) {
39
36
  const ponyfillRequest = new PonyfillRequest(info, init);
40
37
  return fetchPonyfill(ponyfillRequest);
41
38
  }
42
39
  const fetchRequest = info;
43
- const url = new PonyfillURL(fetchRequest.url, 'http://localhost');
44
- if (url.protocol === 'data:') {
45
- const response = getResponseForDataUri(url);
40
+ if (fetchRequest.url.startsWith('data:')) {
41
+ const response = getResponseForDataUri(fetchRequest.url);
46
42
  return Promise.resolve(response);
47
43
  }
48
- if (url.protocol === 'file:') {
44
+ if (fetchRequest.url.startsWith('file:')) {
49
45
  const response = getResponseForFile(fetchRequest.url);
50
46
  return Promise.resolve(response);
51
47
  }
52
- const nodeReadable = (fetchRequest.body != null
53
- ? 'pipe' in fetchRequest.body
54
- ? fetchRequest.body
55
- : Readable.from(fetchRequest.body)
56
- : null);
57
- const headersSerializer = fetchRequest.headersSerializer || defaultHeadersSerializer;
58
- let size;
59
- const curlyHeaders = headersSerializer(fetchRequest.headers, value => {
60
- size = Number(value);
61
- });
62
- let easyNativeBinding;
63
- const curlyOptions = {
64
- // we want the unparsed binary response to be returned as a stream to us
65
- curlyStreamResponse: true,
66
- curlyResponseBodyParser: false,
67
- curlyProgressCallback() {
68
- if (easyNativeBinding == null) {
69
- easyNativeBinding = this;
70
- }
71
- return fetchRequest.signal.aborted ? 1 : 0;
72
- },
73
- upload: nodeReadable != null,
74
- transferEncoding: false,
75
- httpTransferDecoding: true,
76
- followLocation: fetchRequest.redirect === 'follow',
77
- maxRedirs: 20,
78
- acceptEncoding: '',
79
- curlyStreamUpload: nodeReadable,
80
- // this will just make libcurl use their own progress function (which is pretty neat)
81
- // curlyProgressCallback() { return CurlProgressFunc.Continue },
82
- // verbose: true,
83
- httpHeader: curlyHeaders,
84
- customRequest: fetchRequest.method,
85
- };
86
- if (size != null) {
87
- curlyOptions.inFileSize = size;
48
+ if (globalThis.libcurl) {
49
+ return fetchCurl(fetchRequest);
88
50
  }
89
- const { curly, CurlCode, CurlPause } = globalThis['libcurl'] || (await import('node-libcurl'));
90
- fetchRequest.signal.onabort = () => {
91
- if (easyNativeBinding != null) {
92
- easyNativeBinding.pause(CurlPause.Recv);
93
- }
94
- };
95
- const curlyResult = await curly(fetchRequest.url, curlyOptions);
96
- const responseHeaders = new PonyfillHeaders();
97
- curlyResult.headers.forEach(headerInfo => {
98
- for (const key in headerInfo) {
99
- if (key === 'location' || (key === 'Location' && fetchRequest.redirect === 'error')) {
100
- throw new Error('redirects are not allowed');
101
- }
102
- if (key !== 'result') {
103
- responseHeaders.append(key, headerInfo[key]);
104
- }
105
- }
106
- });
107
- curlyResult.data.on('error', (err) => {
108
- if (err.isCurlError && err.code === CurlCode.CURLE_ABORTED_BY_CALLBACK) {
109
- // this is expected
110
- }
111
- else {
112
- throw err;
113
- }
114
- });
115
- return new PonyfillResponse(curlyResult.data, {
116
- status: curlyResult.statusCode,
117
- headers: responseHeaders,
118
- url: info.url,
119
- });
51
+ return fetchNodeHttp(fetchRequest);
120
52
  }
@@ -0,0 +1,75 @@
1
+ /* eslint-disable @typescript-eslint/no-this-alias */
2
+ import { Readable } from 'stream';
3
+ import { PonyfillHeaders } from './Headers.js';
4
+ import { PonyfillResponse } from './Response.js';
5
+ import { defaultHeadersSerializer } from './utils.js';
6
+ export async function fetchCurl(fetchRequest) {
7
+ const nodeReadable = (fetchRequest.body != null
8
+ ? 'pipe' in fetchRequest.body
9
+ ? fetchRequest.body
10
+ : Readable.from(fetchRequest.body)
11
+ : null);
12
+ const headersSerializer = fetchRequest.headersSerializer || defaultHeadersSerializer;
13
+ let size;
14
+ const curlyHeaders = headersSerializer(fetchRequest.headers, value => {
15
+ size = Number(value);
16
+ });
17
+ let easyNativeBinding;
18
+ const curlyOptions = {
19
+ // we want the unparsed binary response to be returned as a stream to us
20
+ curlyStreamResponse: true,
21
+ curlyResponseBodyParser: false,
22
+ curlyProgressCallback() {
23
+ if (easyNativeBinding == null) {
24
+ easyNativeBinding = this;
25
+ }
26
+ return fetchRequest.signal.aborted ? 1 : 0;
27
+ },
28
+ upload: nodeReadable != null,
29
+ transferEncoding: false,
30
+ httpTransferDecoding: true,
31
+ followLocation: fetchRequest.redirect === 'follow',
32
+ maxRedirs: 20,
33
+ acceptEncoding: '',
34
+ curlyStreamUpload: nodeReadable,
35
+ // this will just make libcurl use their own progress function (which is pretty neat)
36
+ // curlyProgressCallback() { return CurlProgressFunc.Continue },
37
+ // verbose: true,
38
+ httpHeader: curlyHeaders,
39
+ customRequest: fetchRequest.method,
40
+ };
41
+ if (size != null) {
42
+ curlyOptions.inFileSize = size;
43
+ }
44
+ const { curly, CurlCode, CurlPause } = globalThis['libcurl'];
45
+ fetchRequest.signal.onabort = () => {
46
+ if (easyNativeBinding != null) {
47
+ easyNativeBinding.pause(CurlPause.Recv);
48
+ }
49
+ };
50
+ const curlyResult = await curly(fetchRequest.url, curlyOptions);
51
+ const responseHeaders = new PonyfillHeaders();
52
+ curlyResult.headers.forEach(headerInfo => {
53
+ for (const key in headerInfo) {
54
+ if (key === 'location' || (key === 'Location' && fetchRequest.redirect === 'error')) {
55
+ throw new Error('redirects are not allowed');
56
+ }
57
+ if (key !== 'result') {
58
+ responseHeaders.append(key, headerInfo[key]);
59
+ }
60
+ }
61
+ });
62
+ curlyResult.data.on('error', (err) => {
63
+ if (err.isCurlError && err.code === CurlCode.CURLE_ABORTED_BY_CALLBACK) {
64
+ // this is expected
65
+ }
66
+ else {
67
+ throw err;
68
+ }
69
+ });
70
+ return new PonyfillResponse(curlyResult.data, {
71
+ status: curlyResult.statusCode,
72
+ headers: responseHeaders,
73
+ url: fetchRequest.url,
74
+ });
75
+ }
@@ -0,0 +1,100 @@
1
+ import { request as httpRequest } from 'http';
2
+ import { request as httpsRequest } from 'https';
3
+ import { Readable } from 'stream';
4
+ import { createBrotliDecompress, createGunzip, createInflate } from 'zlib';
5
+ import { PonyfillAbortError } from './AbortError.js';
6
+ import { PonyfillRequest } from './Request.js';
7
+ import { PonyfillResponse } from './Response.js';
8
+ import { PonyfillURL } from './URL.js';
9
+ import { getHeadersObj } from './utils.js';
10
+ function getRequestFnForProtocol(url) {
11
+ if (url.startsWith('http:')) {
12
+ return httpRequest;
13
+ }
14
+ else if (url.startsWith('https:')) {
15
+ return httpsRequest;
16
+ }
17
+ throw new Error(`Unsupported protocol: ${url.split(':')[0] || url}`);
18
+ }
19
+ export function fetchNodeHttp(fetchRequest) {
20
+ return new Promise((resolve, reject) => {
21
+ try {
22
+ const requestFn = getRequestFnForProtocol(fetchRequest.url);
23
+ const nodeReadable = (fetchRequest.body != null
24
+ ? 'pipe' in fetchRequest.body
25
+ ? fetchRequest.body
26
+ : Readable.from(fetchRequest.body)
27
+ : null);
28
+ const headersSerializer = fetchRequest.headersSerializer || getHeadersObj;
29
+ const nodeHeaders = headersSerializer(fetchRequest.headers);
30
+ const nodeRequest = requestFn(fetchRequest.url, {
31
+ method: fetchRequest.method,
32
+ headers: nodeHeaders,
33
+ signal: fetchRequest.signal,
34
+ agent: fetchRequest.agent,
35
+ });
36
+ // TODO: will be removed after v16 reaches EOL
37
+ fetchRequest.signal?.addEventListener('abort', () => {
38
+ if (!nodeRequest.aborted) {
39
+ nodeRequest.abort();
40
+ }
41
+ });
42
+ // TODO: will be removed after v16 reaches EOL
43
+ nodeRequest.once('abort', (reason) => {
44
+ reject(new PonyfillAbortError(reason));
45
+ });
46
+ nodeRequest.once('response', nodeResponse => {
47
+ let responseBody = nodeResponse;
48
+ const contentEncoding = nodeResponse.headers['content-encoding'];
49
+ switch (contentEncoding) {
50
+ case 'x-gzip':
51
+ case 'gzip':
52
+ responseBody = nodeResponse.pipe(createGunzip());
53
+ break;
54
+ case 'x-deflate':
55
+ case 'deflate':
56
+ responseBody = nodeResponse.pipe(createInflate());
57
+ break;
58
+ case 'br':
59
+ responseBody = nodeResponse.pipe(createBrotliDecompress());
60
+ break;
61
+ }
62
+ if (nodeResponse.headers.location) {
63
+ if (fetchRequest.redirect === 'error') {
64
+ const redirectError = new Error('Redirects are not allowed');
65
+ reject(redirectError);
66
+ nodeResponse.resume();
67
+ return;
68
+ }
69
+ if (fetchRequest.redirect === 'follow') {
70
+ const redirectedUrl = new PonyfillURL(nodeResponse.headers.location, fetchRequest.url);
71
+ const redirectResponse$ = fetchNodeHttp(new PonyfillRequest(redirectedUrl, fetchRequest));
72
+ resolve(redirectResponse$.then(redirectResponse => {
73
+ redirectResponse.redirected = true;
74
+ return redirectResponse;
75
+ }));
76
+ nodeResponse.resume();
77
+ return;
78
+ }
79
+ }
80
+ const ponyfillResponse = new PonyfillResponse(responseBody, {
81
+ status: nodeResponse.statusCode,
82
+ statusText: nodeResponse.statusMessage,
83
+ headers: nodeResponse.headers,
84
+ url: fetchRequest.url,
85
+ });
86
+ resolve(ponyfillResponse);
87
+ });
88
+ nodeRequest.once('error', reject);
89
+ if (nodeReadable) {
90
+ nodeReadable.pipe(nodeRequest);
91
+ }
92
+ else {
93
+ nodeRequest.end();
94
+ }
95
+ }
96
+ catch (e) {
97
+ reject(e);
98
+ }
99
+ });
100
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whatwg-node/node-fetch",
3
- "version": "0.5.0-alpha-20230710154907-66260af",
3
+ "version": "0.5.0-alpha-20230710180612-32e574f",
4
4
  "description": "Fetch API implementation for Node",
5
5
  "sideEffects": false,
6
6
  "dependencies": {
@@ -8,7 +8,6 @@
8
8
  "busboy": "^1.6.0",
9
9
  "fast-querystring": "^1.1.1",
10
10
  "fast-url-parser": "^1.1.3",
11
- "node-libcurl": "^3.0.0",
12
11
  "tslib": "^2.3.1"
13
12
  },
14
13
  "repository": {
@@ -1,14 +1,17 @@
1
+ /// <reference types="node" />
2
+ import { Agent } from 'http';
1
3
  import { BodyPonyfillInit, PonyfillBody, PonyfillBodyOptions } from './Body.cjs';
2
4
  import { PonyfillHeadersInit } from './Headers.cjs';
3
5
  export type RequestPonyfillInit = PonyfillBodyOptions & Omit<RequestInit, 'body' | 'headers'> & {
4
6
  body?: BodyPonyfillInit | null;
5
7
  headers?: PonyfillHeadersInit;
6
8
  headersSerializer?: HeadersSerializer;
9
+ agent?: Agent;
7
10
  };
8
11
  type HeadersSerializer = (headers: Headers, onContentLength?: (contentLength: string) => void) => string[];
9
12
  export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> implements Request {
10
13
  constructor(input: RequestInfo | URL, options?: RequestPonyfillInit);
11
- headersSerializer: HeadersSerializer;
14
+ headersSerializer?: HeadersSerializer;
12
15
  cache: RequestCache;
13
16
  credentials: RequestCredentials;
14
17
  destination: RequestDestination;
@@ -22,8 +25,9 @@ export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> im
22
25
  referrer: string;
23
26
  referrerPolicy: ReferrerPolicy;
24
27
  url: string;
28
+ agent?: Agent;
25
29
  private _signal;
26
30
  get signal(): AbortSignal;
27
- clone(): PonyfillRequest;
31
+ clone(): PonyfillRequest<TJSON>;
28
32
  }
29
33
  export {};
@@ -1,14 +1,17 @@
1
+ /// <reference types="node" />
2
+ import { Agent } from 'http';
1
3
  import { BodyPonyfillInit, PonyfillBody, PonyfillBodyOptions } from './Body.js';
2
4
  import { PonyfillHeadersInit } from './Headers.js';
3
5
  export type RequestPonyfillInit = PonyfillBodyOptions & Omit<RequestInit, 'body' | 'headers'> & {
4
6
  body?: BodyPonyfillInit | null;
5
7
  headers?: PonyfillHeadersInit;
6
8
  headersSerializer?: HeadersSerializer;
9
+ agent?: Agent;
7
10
  };
8
11
  type HeadersSerializer = (headers: Headers, onContentLength?: (contentLength: string) => void) => string[];
9
12
  export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> implements Request {
10
13
  constructor(input: RequestInfo | URL, options?: RequestPonyfillInit);
11
- headersSerializer: HeadersSerializer;
14
+ headersSerializer?: HeadersSerializer;
12
15
  cache: RequestCache;
13
16
  credentials: RequestCredentials;
14
17
  destination: RequestDestination;
@@ -22,8 +25,9 @@ export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> im
22
25
  referrer: string;
23
26
  referrerPolicy: ReferrerPolicy;
24
27
  url: string;
28
+ agent?: Agent;
25
29
  private _signal;
26
30
  get signal(): AbortSignal;
27
- clone(): PonyfillRequest;
31
+ clone(): PonyfillRequest<TJSON>;
28
32
  }
29
33
  export {};
@@ -0,0 +1,3 @@
1
+ import { PonyfillRequest } from './Request.cjs';
2
+ import { PonyfillResponse } from './Response.cjs';
3
+ export declare function fetchCurl<TResponseJSON = any, TRequestJSON = any>(fetchRequest: PonyfillRequest<TRequestJSON>): Promise<PonyfillResponse<TResponseJSON>>;
@@ -0,0 +1,3 @@
1
+ import { PonyfillRequest } from './Request.js';
2
+ import { PonyfillResponse } from './Response.js';
3
+ export declare function fetchCurl<TResponseJSON = any, TRequestJSON = any>(fetchRequest: PonyfillRequest<TRequestJSON>): Promise<PonyfillResponse<TResponseJSON>>;
@@ -0,0 +1,3 @@
1
+ import { PonyfillRequest } from './Request.cjs';
2
+ import { PonyfillResponse } from './Response.cjs';
3
+ export declare function fetchNodeHttp<TResponseJSON = any, TRequestJSON = any>(fetchRequest: PonyfillRequest<TRequestJSON>): Promise<PonyfillResponse<TResponseJSON>>;
@@ -0,0 +1,3 @@
1
+ import { PonyfillRequest } from './Request.js';
2
+ import { PonyfillResponse } from './Response.js';
3
+ export declare function fetchNodeHttp<TResponseJSON = any, TRequestJSON = any>(fetchRequest: PonyfillRequest<TRequestJSON>): Promise<PonyfillResponse<TResponseJSON>>;