@whatwg-node/node-fetch 0.5.0-alpha-20230531142534-b373f43 → 0.5.0-alpha-20230622145940-8d0fb21

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/Body.js CHANGED
@@ -232,17 +232,31 @@ function processBodyInit(bodyInit) {
232
232
  };
233
233
  }
234
234
  if (typeof bodyInit === 'string') {
235
- const buffer = Buffer.from(bodyInit, 'utf-8');
235
+ const buffer = Buffer.from(bodyInit);
236
+ const contentLength = buffer.byteLength;
236
237
  return {
237
238
  bodyType: BodyInitType.String,
238
239
  contentType: 'text/plain;charset=UTF-8',
239
- contentLength: buffer.byteLength,
240
+ contentLength,
240
241
  bodyFactory() {
241
242
  const readable = stream_1.Readable.from(buffer);
242
243
  return new ReadableStream_js_1.PonyfillReadableStream(readable);
243
244
  },
244
245
  };
245
246
  }
247
+ if (bodyInit instanceof Buffer) {
248
+ const contentLength = bodyInit.byteLength;
249
+ return {
250
+ bodyType: BodyInitType.Buffer,
251
+ contentLength,
252
+ contentType: null,
253
+ bodyFactory() {
254
+ const readable = stream_1.Readable.from(bodyInit);
255
+ const body = new ReadableStream_js_1.PonyfillReadableStream(readable);
256
+ return body;
257
+ },
258
+ };
259
+ }
246
260
  if (bodyInit instanceof ReadableStream_js_1.PonyfillReadableStream) {
247
261
  return {
248
262
  bodyType: BodyInitType.ReadableStream,
@@ -261,19 +275,6 @@ function processBodyInit(bodyInit) {
261
275
  },
262
276
  };
263
277
  }
264
- if (bodyInit instanceof Buffer) {
265
- const contentLength = bodyInit.length;
266
- return {
267
- bodyType: BodyInitType.Buffer,
268
- contentLength,
269
- contentType: null,
270
- bodyFactory() {
271
- const readable = stream_1.Readable.from(bodyInit);
272
- const body = new ReadableStream_js_1.PonyfillReadableStream(readable);
273
- return body;
274
- },
275
- };
276
- }
277
278
  if (bodyInit instanceof Uint8Array) {
278
279
  const contentLength = bodyInit.byteLength;
279
280
  return {
package/cjs/Headers.js CHANGED
@@ -1,22 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PonyfillHeaders = void 0;
3
+ exports.splitSetCookieHeader = exports.PonyfillHeaders = exports.isHeadersLike = void 0;
4
+ const node_util_1 = require("node:util");
4
5
  function isHeadersLike(headers) {
5
- return headers && typeof headers.get === 'function';
6
+ return headers?.get && headers?.forEach;
6
7
  }
8
+ exports.isHeadersLike = isHeadersLike;
7
9
  class PonyfillHeaders {
8
10
  constructor(headersInit) {
9
11
  this.headersInit = headersInit;
10
- this.map = new Map();
11
- this.mapIsBuilt = false;
12
12
  this.objectNormalizedKeysOfHeadersInit = [];
13
13
  this.objectOriginalKeysOfHeadersInit = [];
14
14
  }
15
15
  // perf: we don't need to build `this.map` for Requests, as we can access the headers directly
16
16
  _get(key) {
17
17
  // If the map is built, reuse it
18
- if (this.mapIsBuilt) {
19
- return this.map.get(key.toLowerCase()) || null;
18
+ if (this._map) {
19
+ return this._map.get(key.toLowerCase()) || null;
20
20
  }
21
21
  // If the map is not built, try to get the value from the this.headersInit
22
22
  if (this.headersInit == null) {
@@ -24,7 +24,7 @@ class PonyfillHeaders {
24
24
  }
25
25
  const normalized = key.toLowerCase();
26
26
  if (Array.isArray(this.headersInit)) {
27
- return this.headersInit.find(header => header[0] === normalized);
27
+ return this.headersInit.find(header => header[0] === normalized)?.[1] || null;
28
28
  }
29
29
  else if (isHeadersLike(this.headersInit)) {
30
30
  return this.headersInit.get(normalized);
@@ -51,31 +51,33 @@ class PonyfillHeaders {
51
51
  // perf: Build the map of headers lazily, only when we need to access all headers or write to it.
52
52
  // I could do a getter here, but I'm too lazy to type `getter`.
53
53
  getMap() {
54
- if (this.mapIsBuilt) {
55
- return this.map;
56
- }
57
- if (this.headersInit != null) {
58
- if (Array.isArray(this.headersInit)) {
59
- this.map = new Map(this.headersInit);
60
- }
61
- else if (isHeadersLike(this.headersInit)) {
62
- this.headersInit.forEach((value, key) => {
63
- this.map.set(key, value);
64
- });
65
- }
66
- else {
67
- for (const initKey in this.headersInit) {
68
- const initValue = this.headersInit[initKey];
69
- if (initValue != null) {
70
- const normalizedValue = Array.isArray(initValue) ? initValue.join(', ') : initValue;
71
- const normalizedKey = initKey.toLowerCase();
72
- this.map.set(normalizedKey, normalizedValue);
54
+ if (!this._map) {
55
+ if (this.headersInit != null) {
56
+ if (Array.isArray(this.headersInit)) {
57
+ this._map = new Map(this.headersInit);
58
+ }
59
+ else if (isHeadersLike(this.headersInit)) {
60
+ this._map = new Map();
61
+ this.headersInit.forEach((value, key) => {
62
+ this._map.set(key, value);
63
+ });
64
+ }
65
+ else {
66
+ this._map = new Map();
67
+ for (const initKey in this.headersInit) {
68
+ const initValue = this.headersInit[initKey];
69
+ if (initValue != null) {
70
+ const normalizedKey = initKey.toLowerCase();
71
+ this._map.set(normalizedKey, initValue);
72
+ }
73
73
  }
74
74
  }
75
75
  }
76
+ else {
77
+ this._map = new Map();
78
+ }
76
79
  }
77
- this.mapIsBuilt = true;
78
- return this.map;
80
+ return this._map;
79
81
  }
80
82
  append(name, value) {
81
83
  const key = name.toLowerCase();
@@ -84,19 +86,14 @@ class PonyfillHeaders {
84
86
  this.getMap().set(key, finalValue);
85
87
  }
86
88
  get(name) {
87
- const key = name.toLowerCase();
88
- const value = this._get(key);
89
+ const value = this._get(name);
89
90
  if (value == null) {
90
91
  return null;
91
92
  }
92
- if (Array.isArray(value)) {
93
- return value.join(', ');
94
- }
95
93
  return value;
96
94
  }
97
95
  has(name) {
98
- const key = name.toLowerCase();
99
- return !!this._get(key); // we might need to check if header exists and not just check if it's not nullable
96
+ return !!this._get(name); // we might need to check if header exists and not just check if it's not nullable
100
97
  }
101
98
  set(name, value) {
102
99
  const key = name.toLowerCase();
@@ -107,21 +104,123 @@ class PonyfillHeaders {
107
104
  this.getMap().delete(key);
108
105
  }
109
106
  forEach(callback) {
107
+ if (!this._map) {
108
+ if (this.headersInit) {
109
+ if (Array.isArray(this.headersInit)) {
110
+ this.headersInit.forEach(([key, value]) => {
111
+ callback(value, key, this);
112
+ });
113
+ return;
114
+ }
115
+ if (isHeadersLike(this.headersInit)) {
116
+ this.headersInit.forEach(callback);
117
+ return;
118
+ }
119
+ Object.entries(this.headersInit).forEach(([key, value]) => {
120
+ if (value != null) {
121
+ callback(value, key, this);
122
+ }
123
+ });
124
+ }
125
+ return;
126
+ }
110
127
  this.getMap().forEach((value, key) => {
111
128
  callback(value, key, this);
112
129
  });
113
130
  }
114
131
  keys() {
132
+ if (!this._map) {
133
+ if (this.headersInit) {
134
+ if (Array.isArray(this.headersInit)) {
135
+ return this.headersInit.map(([key]) => key)[Symbol.iterator]();
136
+ }
137
+ if (isHeadersLike(this.headersInit)) {
138
+ return this.headersInit.keys();
139
+ }
140
+ return Object.keys(this.headersInit)[Symbol.iterator]();
141
+ }
142
+ }
115
143
  return this.getMap().keys();
116
144
  }
117
145
  values() {
146
+ if (!this._map) {
147
+ if (this.headersInit) {
148
+ if (Array.isArray(this.headersInit)) {
149
+ return this.headersInit.map(([, value]) => value)[Symbol.iterator]();
150
+ }
151
+ if (isHeadersLike(this.headersInit)) {
152
+ return this.headersInit.values();
153
+ }
154
+ return Object.values(this.headersInit)[Symbol.iterator]();
155
+ }
156
+ }
118
157
  return this.getMap().values();
119
158
  }
120
159
  entries() {
160
+ if (!this._map) {
161
+ if (this.headersInit) {
162
+ if (Array.isArray(this.headersInit)) {
163
+ return this.headersInit[Symbol.iterator]();
164
+ }
165
+ if (isHeadersLike(this.headersInit)) {
166
+ return this.headersInit.entries();
167
+ }
168
+ return Object.entries(this.headersInit)[Symbol.iterator]();
169
+ }
170
+ }
121
171
  return this.getMap().entries();
122
172
  }
173
+ getSetCookie() {
174
+ const setCookieHeader = this.get('set-cookie');
175
+ if (!setCookieHeader) {
176
+ return [];
177
+ }
178
+ return splitSetCookieHeader(setCookieHeader);
179
+ }
123
180
  [Symbol.iterator]() {
124
- return this.getMap().entries();
181
+ return this.entries();
182
+ }
183
+ [Symbol.for('nodejs.util.inspect.custom')]() {
184
+ const record = {};
185
+ this.forEach((value, key) => {
186
+ if (key === 'set-cookie') {
187
+ record['set-cookie'] = this.getSetCookie();
188
+ }
189
+ else {
190
+ record[key] = value.includes(',') ? value.split(',').map(el => el.trim()) : value;
191
+ }
192
+ });
193
+ return `Headers ${(0, node_util_1.inspect)(record)}`;
125
194
  }
126
195
  }
127
196
  exports.PonyfillHeaders = PonyfillHeaders;
197
+ function splitSetCookieHeader(setCookieHeader) {
198
+ const setCookieHeaders = [];
199
+ let currentStr = '';
200
+ let ignoreComma = false;
201
+ for (const ch of setCookieHeader) {
202
+ if (currentStr.endsWith('Expires=')) {
203
+ ignoreComma = true;
204
+ }
205
+ if (ignoreComma) {
206
+ if (ch === ';') {
207
+ ignoreComma = false;
208
+ }
209
+ if (ch === ',' && currentStr.split('Expires=')[1].length > 3) {
210
+ ignoreComma = false;
211
+ }
212
+ }
213
+ if (ch === ',' && !ignoreComma) {
214
+ setCookieHeaders.push(currentStr.trim());
215
+ currentStr = '';
216
+ }
217
+ else {
218
+ currentStr += ch;
219
+ }
220
+ }
221
+ if (currentStr) {
222
+ setCookieHeaders.push(currentStr.trim());
223
+ }
224
+ return setCookieHeaders;
225
+ }
226
+ exports.splitSetCookieHeader = splitSetCookieHeader;
package/cjs/Request.js CHANGED
@@ -28,11 +28,12 @@ class PonyfillRequest extends Body_js_1.PonyfillBody {
28
28
  requestInit = options;
29
29
  }
30
30
  super(bodyInit, options);
31
- this.destination = '';
32
- this.priority = 'auto';
33
31
  this.cache = requestInit?.cache || 'default';
34
32
  this.credentials = requestInit?.credentials || 'same-origin';
35
- this.headers = new Headers_js_1.PonyfillHeaders(requestInit?.headers);
33
+ this.headers =
34
+ requestInit?.headers && (0, Headers_js_1.isHeadersLike)(requestInit.headers)
35
+ ? requestInit.headers
36
+ : new Headers_js_1.PonyfillHeaders(requestInit?.headers);
36
37
  this.integrity = requestInit?.integrity || '';
37
38
  this.keepalive = requestInit?.keepalive != null ? requestInit?.keepalive : false;
38
39
  this.method = requestInit?.method?.toUpperCase() || 'GET';
@@ -40,30 +41,42 @@ class PonyfillRequest extends Body_js_1.PonyfillBody {
40
41
  this.redirect = requestInit?.redirect || 'follow';
41
42
  this.referrer = requestInit?.referrer || 'about:client';
42
43
  this.referrerPolicy = requestInit?.referrerPolicy || 'no-referrer';
43
- this.signal = requestInit?.signal || new AbortController().signal;
44
- this.headersSerializer = requestInit?.headersSerializer || utils_js_1.getHeadersObj;
44
+ this._signal = requestInit?.signal;
45
+ this.headersSerializer = requestInit?.headersSerializer || utils_js_1.defaultHeadersSerializer;
45
46
  this.url = url || '';
46
- const contentTypeInHeaders = this.headers.get('content-type');
47
- if (!contentTypeInHeaders) {
48
- if (this.contentType) {
49
- this.headers.set('content-type', this.contentType);
47
+ this.destination = 'document';
48
+ this.priority = 'auto';
49
+ if (this.method !== 'GET' && this.method !== 'HEAD') {
50
+ const contentTypeInHeaders = this.headers.get('content-type');
51
+ if (!contentTypeInHeaders) {
52
+ if (this.contentType) {
53
+ this.headers.set('content-type', this.contentType);
54
+ }
50
55
  }
51
- }
52
- else {
53
- this.contentType = contentTypeInHeaders;
54
- }
55
- const contentLengthInHeaders = this.headers.get('content-length');
56
- if (!contentLengthInHeaders) {
57
- if (this.contentLength) {
58
- this.headers.set('content-length', this.contentLength.toString());
56
+ else {
57
+ this.contentType = contentTypeInHeaders;
58
+ }
59
+ const contentLengthInHeaders = this.headers.get('content-length');
60
+ if (!contentLengthInHeaders) {
61
+ if (this.contentLength) {
62
+ this.headers.set('content-length', this.contentLength.toString());
63
+ }
64
+ }
65
+ else {
66
+ this.contentLength = parseInt(contentLengthInHeaders, 10);
59
67
  }
60
68
  }
61
- else {
62
- this.contentLength = parseInt(contentLengthInHeaders, 10);
69
+ }
70
+ get signal() {
71
+ // Create a new signal only if needed
72
+ // Because the creation of signal is expensive
73
+ if (!this._signal) {
74
+ this._signal = new AbortController().signal;
63
75
  }
76
+ return this._signal;
64
77
  }
65
78
  clone() {
66
- return new Request(this);
79
+ return new PonyfillRequest(this);
67
80
  }
68
81
  }
69
82
  exports.PonyfillRequest = PonyfillRequest;
package/cjs/Response.js CHANGED
@@ -4,23 +4,19 @@ exports.PonyfillResponse = void 0;
4
4
  const http_1 = require("http");
5
5
  const Body_js_1 = require("./Body.js");
6
6
  const Headers_js_1 = require("./Headers.js");
7
+ const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
7
8
  class PonyfillResponse extends Body_js_1.PonyfillBody {
8
9
  constructor(body, init) {
9
10
  super(body || null, init);
10
- this.headers = new Headers_js_1.PonyfillHeaders();
11
- this.status = 200;
12
- this.statusText = 'OK';
13
- this.url = '';
14
- this.redirected = false;
15
- this.type = 'default';
16
- if (init) {
17
- this.headers = new Headers_js_1.PonyfillHeaders(init.headers);
18
- this.status = init.status || 200;
19
- this.statusText = init.statusText || http_1.STATUS_CODES[this.status] || 'OK';
20
- this.url = init.url || '';
21
- this.redirected = init.redirected || false;
22
- this.type = init.type || 'default';
23
- }
11
+ this.headers =
12
+ init?.headers && (0, Headers_js_1.isHeadersLike)(init.headers)
13
+ ? init.headers
14
+ : new Headers_js_1.PonyfillHeaders(init?.headers);
15
+ this.status = init?.status || 200;
16
+ this.statusText = init?.statusText || http_1.STATUS_CODES[this.status] || 'OK';
17
+ this.url = init?.url || '';
18
+ this.redirected = init?.redirected || false;
19
+ this.type = init?.type || 'default';
24
20
  const contentTypeInHeaders = this.headers.get('content-type');
25
21
  if (!contentTypeInHeaders) {
26
22
  if (this.contentType) {
@@ -64,13 +60,14 @@ class PonyfillResponse extends Body_js_1.PonyfillBody {
64
60
  });
65
61
  }
66
62
  static json(data, init = {}) {
67
- return new PonyfillResponse(JSON.stringify(data), {
68
- ...init,
69
- headers: {
70
- 'Content-Type': 'application/json',
71
- ...init?.headers,
72
- },
73
- });
63
+ init.headers =
64
+ init?.headers && (0, Headers_js_1.isHeadersLike)(init.headers)
65
+ ? init.headers
66
+ : new Headers_js_1.PonyfillHeaders(init?.headers);
67
+ if (!init.headers.has('content-type')) {
68
+ init.headers.set('content-type', JSON_CONTENT_TYPE);
69
+ }
70
+ return new PonyfillResponse(JSON.stringify(data), init);
74
71
  }
75
72
  }
76
73
  exports.PonyfillResponse = PonyfillResponse;
package/cjs/fetch.js CHANGED
@@ -33,6 +33,7 @@ const Headers_js_1 = require("./Headers.js");
33
33
  const Request_js_1 = require("./Request.js");
34
34
  const Response_js_1 = require("./Response.js");
35
35
  const URL_js_1 = require("./URL.js");
36
+ const utils_js_1 = require("./utils.js");
36
37
  function getResponseForFile(url) {
37
38
  const path = (0, url_1.fileURLToPath)(url);
38
39
  const readable = (0, fs_1.createReadStream)(path);
@@ -79,13 +80,10 @@ async function fetchPonyfill(info, init) {
79
80
  ? fetchRequest.body
80
81
  : stream_1.Readable.from(fetchRequest.body)
81
82
  : null);
82
- const curlyHeaders = [];
83
+ const headersSerializer = fetchRequest.headersSerializer || utils_js_1.defaultHeadersSerializer;
83
84
  let size;
84
- fetchRequest.headers.forEach((value, key) => {
85
- curlyHeaders.push(`${key}: ${value}`);
86
- if (key === 'content-length') {
87
- size = Number(value);
88
- }
85
+ const curlyHeaders = headersSerializer(fetchRequest.headers, value => {
86
+ size = Number(value);
89
87
  });
90
88
  let easyNativeBinding;
91
89
  const curlyOptions = {
@@ -114,7 +112,7 @@ async function fetchPonyfill(info, init) {
114
112
  if (size != null) {
115
113
  curlyOptions.inFileSize = size;
116
114
  }
117
- const { curly, CurlCode, CurlPause } = await Promise.resolve().then(() => __importStar(require('node-libcurl')));
115
+ const { curly, CurlCode, CurlPause } = globalThis['libcurl'] || (await Promise.resolve().then(() => __importStar(require('node-libcurl'))));
118
116
  fetchRequest.signal.onabort = () => {
119
117
  if (easyNativeBinding != null) {
120
118
  easyNativeBinding.pause(CurlPause.Recv);
package/cjs/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.uint8ArrayToArrayBuffer = exports.getHeadersObj = void 0;
3
+ exports.defaultHeadersSerializer = exports.uint8ArrayToArrayBuffer = exports.getHeadersObj = void 0;
4
4
  function getHeadersObj(headers) {
5
5
  if (headers == null || !('forEach' in headers)) {
6
6
  return headers;
@@ -16,3 +16,14 @@ function uint8ArrayToArrayBuffer(uint8array) {
16
16
  return uint8array.buffer.slice(uint8array.byteOffset, uint8array.byteOffset + uint8array.byteLength);
17
17
  }
18
18
  exports.uint8ArrayToArrayBuffer = uint8ArrayToArrayBuffer;
19
+ function defaultHeadersSerializer(headers, onContentLength) {
20
+ const headerArray = [];
21
+ headers.forEach((value, key) => {
22
+ if (onContentLength && key === 'content-length') {
23
+ onContentLength(value);
24
+ }
25
+ headerArray.push(`${key}: ${value}`);
26
+ });
27
+ return headerArray;
28
+ }
29
+ exports.defaultHeadersSerializer = defaultHeadersSerializer;
package/esm/Body.js CHANGED
@@ -227,17 +227,31 @@ function processBodyInit(bodyInit) {
227
227
  };
228
228
  }
229
229
  if (typeof bodyInit === 'string') {
230
- const buffer = Buffer.from(bodyInit, 'utf-8');
230
+ const buffer = Buffer.from(bodyInit);
231
+ const contentLength = buffer.byteLength;
231
232
  return {
232
233
  bodyType: BodyInitType.String,
233
234
  contentType: 'text/plain;charset=UTF-8',
234
- contentLength: buffer.byteLength,
235
+ contentLength,
235
236
  bodyFactory() {
236
237
  const readable = Readable.from(buffer);
237
238
  return new PonyfillReadableStream(readable);
238
239
  },
239
240
  };
240
241
  }
242
+ if (bodyInit instanceof Buffer) {
243
+ const contentLength = bodyInit.byteLength;
244
+ return {
245
+ bodyType: BodyInitType.Buffer,
246
+ contentLength,
247
+ contentType: null,
248
+ bodyFactory() {
249
+ const readable = Readable.from(bodyInit);
250
+ const body = new PonyfillReadableStream(readable);
251
+ return body;
252
+ },
253
+ };
254
+ }
241
255
  if (bodyInit instanceof PonyfillReadableStream) {
242
256
  return {
243
257
  bodyType: BodyInitType.ReadableStream,
@@ -256,19 +270,6 @@ function processBodyInit(bodyInit) {
256
270
  },
257
271
  };
258
272
  }
259
- if (bodyInit instanceof Buffer) {
260
- const contentLength = bodyInit.length;
261
- return {
262
- bodyType: BodyInitType.Buffer,
263
- contentLength,
264
- contentType: null,
265
- bodyFactory() {
266
- const readable = Readable.from(bodyInit);
267
- const body = new PonyfillReadableStream(readable);
268
- return body;
269
- },
270
- };
271
- }
272
273
  if (bodyInit instanceof Uint8Array) {
273
274
  const contentLength = bodyInit.byteLength;
274
275
  return {
package/esm/Headers.js CHANGED
@@ -1,19 +1,18 @@
1
- function isHeadersLike(headers) {
2
- return headers && typeof headers.get === 'function';
1
+ import { inspect } from 'node:util';
2
+ export function isHeadersLike(headers) {
3
+ return headers?.get && headers?.forEach;
3
4
  }
4
5
  export class PonyfillHeaders {
5
6
  constructor(headersInit) {
6
7
  this.headersInit = headersInit;
7
- this.map = new Map();
8
- this.mapIsBuilt = false;
9
8
  this.objectNormalizedKeysOfHeadersInit = [];
10
9
  this.objectOriginalKeysOfHeadersInit = [];
11
10
  }
12
11
  // perf: we don't need to build `this.map` for Requests, as we can access the headers directly
13
12
  _get(key) {
14
13
  // If the map is built, reuse it
15
- if (this.mapIsBuilt) {
16
- return this.map.get(key.toLowerCase()) || null;
14
+ if (this._map) {
15
+ return this._map.get(key.toLowerCase()) || null;
17
16
  }
18
17
  // If the map is not built, try to get the value from the this.headersInit
19
18
  if (this.headersInit == null) {
@@ -21,7 +20,7 @@ export class PonyfillHeaders {
21
20
  }
22
21
  const normalized = key.toLowerCase();
23
22
  if (Array.isArray(this.headersInit)) {
24
- return this.headersInit.find(header => header[0] === normalized);
23
+ return this.headersInit.find(header => header[0] === normalized)?.[1] || null;
25
24
  }
26
25
  else if (isHeadersLike(this.headersInit)) {
27
26
  return this.headersInit.get(normalized);
@@ -48,31 +47,33 @@ export class PonyfillHeaders {
48
47
  // perf: Build the map of headers lazily, only when we need to access all headers or write to it.
49
48
  // I could do a getter here, but I'm too lazy to type `getter`.
50
49
  getMap() {
51
- if (this.mapIsBuilt) {
52
- return this.map;
53
- }
54
- if (this.headersInit != null) {
55
- if (Array.isArray(this.headersInit)) {
56
- this.map = new Map(this.headersInit);
57
- }
58
- else if (isHeadersLike(this.headersInit)) {
59
- this.headersInit.forEach((value, key) => {
60
- this.map.set(key, value);
61
- });
62
- }
63
- else {
64
- for (const initKey in this.headersInit) {
65
- const initValue = this.headersInit[initKey];
66
- if (initValue != null) {
67
- const normalizedValue = Array.isArray(initValue) ? initValue.join(', ') : initValue;
68
- const normalizedKey = initKey.toLowerCase();
69
- this.map.set(normalizedKey, normalizedValue);
50
+ if (!this._map) {
51
+ if (this.headersInit != null) {
52
+ if (Array.isArray(this.headersInit)) {
53
+ this._map = new Map(this.headersInit);
54
+ }
55
+ else if (isHeadersLike(this.headersInit)) {
56
+ this._map = new Map();
57
+ this.headersInit.forEach((value, key) => {
58
+ this._map.set(key, value);
59
+ });
60
+ }
61
+ else {
62
+ this._map = new Map();
63
+ for (const initKey in this.headersInit) {
64
+ const initValue = this.headersInit[initKey];
65
+ if (initValue != null) {
66
+ const normalizedKey = initKey.toLowerCase();
67
+ this._map.set(normalizedKey, initValue);
68
+ }
70
69
  }
71
70
  }
72
71
  }
72
+ else {
73
+ this._map = new Map();
74
+ }
73
75
  }
74
- this.mapIsBuilt = true;
75
- return this.map;
76
+ return this._map;
76
77
  }
77
78
  append(name, value) {
78
79
  const key = name.toLowerCase();
@@ -81,19 +82,14 @@ export class PonyfillHeaders {
81
82
  this.getMap().set(key, finalValue);
82
83
  }
83
84
  get(name) {
84
- const key = name.toLowerCase();
85
- const value = this._get(key);
85
+ const value = this._get(name);
86
86
  if (value == null) {
87
87
  return null;
88
88
  }
89
- if (Array.isArray(value)) {
90
- return value.join(', ');
91
- }
92
89
  return value;
93
90
  }
94
91
  has(name) {
95
- const key = name.toLowerCase();
96
- return !!this._get(key); // we might need to check if header exists and not just check if it's not nullable
92
+ return !!this._get(name); // we might need to check if header exists and not just check if it's not nullable
97
93
  }
98
94
  set(name, value) {
99
95
  const key = name.toLowerCase();
@@ -104,20 +100,121 @@ export class PonyfillHeaders {
104
100
  this.getMap().delete(key);
105
101
  }
106
102
  forEach(callback) {
103
+ if (!this._map) {
104
+ if (this.headersInit) {
105
+ if (Array.isArray(this.headersInit)) {
106
+ this.headersInit.forEach(([key, value]) => {
107
+ callback(value, key, this);
108
+ });
109
+ return;
110
+ }
111
+ if (isHeadersLike(this.headersInit)) {
112
+ this.headersInit.forEach(callback);
113
+ return;
114
+ }
115
+ Object.entries(this.headersInit).forEach(([key, value]) => {
116
+ if (value != null) {
117
+ callback(value, key, this);
118
+ }
119
+ });
120
+ }
121
+ return;
122
+ }
107
123
  this.getMap().forEach((value, key) => {
108
124
  callback(value, key, this);
109
125
  });
110
126
  }
111
127
  keys() {
128
+ if (!this._map) {
129
+ if (this.headersInit) {
130
+ if (Array.isArray(this.headersInit)) {
131
+ return this.headersInit.map(([key]) => key)[Symbol.iterator]();
132
+ }
133
+ if (isHeadersLike(this.headersInit)) {
134
+ return this.headersInit.keys();
135
+ }
136
+ return Object.keys(this.headersInit)[Symbol.iterator]();
137
+ }
138
+ }
112
139
  return this.getMap().keys();
113
140
  }
114
141
  values() {
142
+ if (!this._map) {
143
+ if (this.headersInit) {
144
+ if (Array.isArray(this.headersInit)) {
145
+ return this.headersInit.map(([, value]) => value)[Symbol.iterator]();
146
+ }
147
+ if (isHeadersLike(this.headersInit)) {
148
+ return this.headersInit.values();
149
+ }
150
+ return Object.values(this.headersInit)[Symbol.iterator]();
151
+ }
152
+ }
115
153
  return this.getMap().values();
116
154
  }
117
155
  entries() {
156
+ if (!this._map) {
157
+ if (this.headersInit) {
158
+ if (Array.isArray(this.headersInit)) {
159
+ return this.headersInit[Symbol.iterator]();
160
+ }
161
+ if (isHeadersLike(this.headersInit)) {
162
+ return this.headersInit.entries();
163
+ }
164
+ return Object.entries(this.headersInit)[Symbol.iterator]();
165
+ }
166
+ }
118
167
  return this.getMap().entries();
119
168
  }
169
+ getSetCookie() {
170
+ const setCookieHeader = this.get('set-cookie');
171
+ if (!setCookieHeader) {
172
+ return [];
173
+ }
174
+ return splitSetCookieHeader(setCookieHeader);
175
+ }
120
176
  [Symbol.iterator]() {
121
- return this.getMap().entries();
177
+ return this.entries();
178
+ }
179
+ [Symbol.for('nodejs.util.inspect.custom')]() {
180
+ const record = {};
181
+ this.forEach((value, key) => {
182
+ if (key === 'set-cookie') {
183
+ record['set-cookie'] = this.getSetCookie();
184
+ }
185
+ else {
186
+ record[key] = value.includes(',') ? value.split(',').map(el => el.trim()) : value;
187
+ }
188
+ });
189
+ return `Headers ${inspect(record)}`;
190
+ }
191
+ }
192
+ export function splitSetCookieHeader(setCookieHeader) {
193
+ const setCookieHeaders = [];
194
+ let currentStr = '';
195
+ let ignoreComma = false;
196
+ for (const ch of setCookieHeader) {
197
+ if (currentStr.endsWith('Expires=')) {
198
+ ignoreComma = true;
199
+ }
200
+ if (ignoreComma) {
201
+ if (ch === ';') {
202
+ ignoreComma = false;
203
+ }
204
+ if (ch === ',' && currentStr.split('Expires=')[1].length > 3) {
205
+ ignoreComma = false;
206
+ }
207
+ }
208
+ if (ch === ',' && !ignoreComma) {
209
+ setCookieHeaders.push(currentStr.trim());
210
+ currentStr = '';
211
+ }
212
+ else {
213
+ currentStr += ch;
214
+ }
215
+ }
216
+ if (currentStr) {
217
+ setCookieHeaders.push(currentStr.trim());
122
218
  }
219
+ return setCookieHeaders;
123
220
  }
package/esm/Request.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { PonyfillBody } from './Body.js';
2
- import { PonyfillHeaders } from './Headers.js';
3
- import { getHeadersObj } from './utils.js';
2
+ import { isHeadersLike, PonyfillHeaders } from './Headers.js';
3
+ import { defaultHeadersSerializer } from './utils.js';
4
4
  function isRequest(input) {
5
5
  return input[Symbol.toStringTag] === 'Request';
6
6
  }
@@ -25,11 +25,12 @@ export class PonyfillRequest extends PonyfillBody {
25
25
  requestInit = options;
26
26
  }
27
27
  super(bodyInit, options);
28
- this.destination = '';
29
- this.priority = 'auto';
30
28
  this.cache = requestInit?.cache || 'default';
31
29
  this.credentials = requestInit?.credentials || 'same-origin';
32
- this.headers = new PonyfillHeaders(requestInit?.headers);
30
+ this.headers =
31
+ requestInit?.headers && isHeadersLike(requestInit.headers)
32
+ ? requestInit.headers
33
+ : new PonyfillHeaders(requestInit?.headers);
33
34
  this.integrity = requestInit?.integrity || '';
34
35
  this.keepalive = requestInit?.keepalive != null ? requestInit?.keepalive : false;
35
36
  this.method = requestInit?.method?.toUpperCase() || 'GET';
@@ -37,29 +38,41 @@ export class PonyfillRequest extends PonyfillBody {
37
38
  this.redirect = requestInit?.redirect || 'follow';
38
39
  this.referrer = requestInit?.referrer || 'about:client';
39
40
  this.referrerPolicy = requestInit?.referrerPolicy || 'no-referrer';
40
- this.signal = requestInit?.signal || new AbortController().signal;
41
- this.headersSerializer = requestInit?.headersSerializer || getHeadersObj;
41
+ this._signal = requestInit?.signal;
42
+ this.headersSerializer = requestInit?.headersSerializer || defaultHeadersSerializer;
42
43
  this.url = url || '';
43
- const contentTypeInHeaders = this.headers.get('content-type');
44
- if (!contentTypeInHeaders) {
45
- if (this.contentType) {
46
- this.headers.set('content-type', this.contentType);
44
+ this.destination = 'document';
45
+ this.priority = 'auto';
46
+ if (this.method !== 'GET' && this.method !== 'HEAD') {
47
+ const contentTypeInHeaders = this.headers.get('content-type');
48
+ if (!contentTypeInHeaders) {
49
+ if (this.contentType) {
50
+ this.headers.set('content-type', this.contentType);
51
+ }
47
52
  }
48
- }
49
- else {
50
- this.contentType = contentTypeInHeaders;
51
- }
52
- const contentLengthInHeaders = this.headers.get('content-length');
53
- if (!contentLengthInHeaders) {
54
- if (this.contentLength) {
55
- this.headers.set('content-length', this.contentLength.toString());
53
+ else {
54
+ this.contentType = contentTypeInHeaders;
55
+ }
56
+ const contentLengthInHeaders = this.headers.get('content-length');
57
+ if (!contentLengthInHeaders) {
58
+ if (this.contentLength) {
59
+ this.headers.set('content-length', this.contentLength.toString());
60
+ }
61
+ }
62
+ else {
63
+ this.contentLength = parseInt(contentLengthInHeaders, 10);
56
64
  }
57
65
  }
58
- else {
59
- this.contentLength = parseInt(contentLengthInHeaders, 10);
66
+ }
67
+ get signal() {
68
+ // Create a new signal only if needed
69
+ // Because the creation of signal is expensive
70
+ if (!this._signal) {
71
+ this._signal = new AbortController().signal;
60
72
  }
73
+ return this._signal;
61
74
  }
62
75
  clone() {
63
- return new Request(this);
76
+ return new PonyfillRequest(this);
64
77
  }
65
78
  }
package/esm/Response.js CHANGED
@@ -1,23 +1,19 @@
1
1
  import { STATUS_CODES } from 'http';
2
2
  import { PonyfillBody } from './Body.js';
3
- import { PonyfillHeaders } from './Headers.js';
3
+ import { isHeadersLike, PonyfillHeaders } from './Headers.js';
4
+ const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
4
5
  export class PonyfillResponse extends PonyfillBody {
5
6
  constructor(body, init) {
6
7
  super(body || null, init);
7
- this.headers = new PonyfillHeaders();
8
- this.status = 200;
9
- this.statusText = 'OK';
10
- this.url = '';
11
- this.redirected = false;
12
- this.type = 'default';
13
- if (init) {
14
- this.headers = new PonyfillHeaders(init.headers);
15
- this.status = init.status || 200;
16
- this.statusText = init.statusText || STATUS_CODES[this.status] || 'OK';
17
- this.url = init.url || '';
18
- this.redirected = init.redirected || false;
19
- this.type = init.type || 'default';
20
- }
8
+ this.headers =
9
+ init?.headers && isHeadersLike(init.headers)
10
+ ? init.headers
11
+ : new PonyfillHeaders(init?.headers);
12
+ this.status = init?.status || 200;
13
+ this.statusText = init?.statusText || STATUS_CODES[this.status] || 'OK';
14
+ this.url = init?.url || '';
15
+ this.redirected = init?.redirected || false;
16
+ this.type = init?.type || 'default';
21
17
  const contentTypeInHeaders = this.headers.get('content-type');
22
18
  if (!contentTypeInHeaders) {
23
19
  if (this.contentType) {
@@ -61,12 +57,13 @@ export class PonyfillResponse extends PonyfillBody {
61
57
  });
62
58
  }
63
59
  static json(data, init = {}) {
64
- return new PonyfillResponse(JSON.stringify(data), {
65
- ...init,
66
- headers: {
67
- 'Content-Type': 'application/json',
68
- ...init?.headers,
69
- },
70
- });
60
+ init.headers =
61
+ init?.headers && isHeadersLike(init.headers)
62
+ ? init.headers
63
+ : new PonyfillHeaders(init?.headers);
64
+ if (!init.headers.has('content-type')) {
65
+ init.headers.set('content-type', JSON_CONTENT_TYPE);
66
+ }
67
+ return new PonyfillResponse(JSON.stringify(data), init);
71
68
  }
72
69
  }
package/esm/fetch.js CHANGED
@@ -7,6 +7,7 @@ import { PonyfillHeaders } from './Headers.js';
7
7
  import { PonyfillRequest } from './Request.js';
8
8
  import { PonyfillResponse } from './Response.js';
9
9
  import { PonyfillURL } from './URL.js';
10
+ import { defaultHeadersSerializer } from './utils.js';
10
11
  function getResponseForFile(url) {
11
12
  const path = fileURLToPath(url);
12
13
  const readable = createReadStream(path);
@@ -53,13 +54,10 @@ export async function fetchPonyfill(info, init) {
53
54
  ? fetchRequest.body
54
55
  : Readable.from(fetchRequest.body)
55
56
  : null);
56
- const curlyHeaders = [];
57
+ const headersSerializer = fetchRequest.headersSerializer || defaultHeadersSerializer;
57
58
  let size;
58
- fetchRequest.headers.forEach((value, key) => {
59
- curlyHeaders.push(`${key}: ${value}`);
60
- if (key === 'content-length') {
61
- size = Number(value);
62
- }
59
+ const curlyHeaders = headersSerializer(fetchRequest.headers, value => {
60
+ size = Number(value);
63
61
  });
64
62
  let easyNativeBinding;
65
63
  const curlyOptions = {
@@ -88,7 +86,7 @@ export async function fetchPonyfill(info, init) {
88
86
  if (size != null) {
89
87
  curlyOptions.inFileSize = size;
90
88
  }
91
- const { curly, CurlCode, CurlPause } = await import('node-libcurl');
89
+ const { curly, CurlCode, CurlPause } = globalThis['libcurl'] || (await import('node-libcurl'));
92
90
  fetchRequest.signal.onabort = () => {
93
91
  if (easyNativeBinding != null) {
94
92
  easyNativeBinding.pause(CurlPause.Recv);
package/esm/utils.js CHANGED
@@ -11,3 +11,13 @@ export function getHeadersObj(headers) {
11
11
  export function uint8ArrayToArrayBuffer(uint8array) {
12
12
  return uint8array.buffer.slice(uint8array.byteOffset, uint8array.byteOffset + uint8array.byteLength);
13
13
  }
14
+ export function defaultHeadersSerializer(headers, onContentLength) {
15
+ const headerArray = [];
16
+ headers.forEach((value, key) => {
17
+ if (onContentLength && key === 'content-length') {
18
+ onContentLength(value);
19
+ }
20
+ headerArray.push(`${key}: ${value}`);
21
+ });
22
+ return headerArray;
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whatwg-node/node-fetch",
3
- "version": "0.5.0-alpha-20230531142534-b373f43",
3
+ "version": "0.5.0-alpha-20230622145940-8d0fb21",
4
4
  "description": "Fetch API implementation for Node",
5
5
  "sideEffects": false,
6
6
  "dependencies": {
@@ -1,8 +1,8 @@
1
- export type PonyfillHeadersInit = [string, string][] | Record<string, string | string[] | undefined> | Headers;
1
+ export type PonyfillHeadersInit = [string, string][] | Record<string, string> | Headers;
2
+ export declare function isHeadersLike(headers: any): headers is Headers;
2
3
  export declare class PonyfillHeaders implements Headers {
3
4
  private headersInit?;
4
- private map;
5
- private mapIsBuilt;
5
+ private _map;
6
6
  private objectNormalizedKeysOfHeadersInit;
7
7
  private objectOriginalKeysOfHeadersInit;
8
8
  constructor(headersInit?: PonyfillHeadersInit | undefined);
@@ -17,5 +17,7 @@ export declare class PonyfillHeaders implements Headers {
17
17
  keys(): IterableIterator<string>;
18
18
  values(): IterableIterator<string>;
19
19
  entries(): IterableIterator<[string, string]>;
20
+ getSetCookie(): string[];
20
21
  [Symbol.iterator](): IterableIterator<[string, string]>;
21
22
  }
23
+ export declare function splitSetCookieHeader(setCookieHeader: string): string[];
@@ -1,8 +1,8 @@
1
- export type PonyfillHeadersInit = [string, string][] | Record<string, string | string[] | undefined> | Headers;
1
+ export type PonyfillHeadersInit = [string, string][] | Record<string, string> | Headers;
2
+ export declare function isHeadersLike(headers: any): headers is Headers;
2
3
  export declare class PonyfillHeaders implements Headers {
3
4
  private headersInit?;
4
- private map;
5
- private mapIsBuilt;
5
+ private _map;
6
6
  private objectNormalizedKeysOfHeadersInit;
7
7
  private objectOriginalKeysOfHeadersInit;
8
8
  constructor(headersInit?: PonyfillHeadersInit | undefined);
@@ -17,5 +17,7 @@ export declare class PonyfillHeaders implements Headers {
17
17
  keys(): IterableIterator<string>;
18
18
  values(): IterableIterator<string>;
19
19
  entries(): IterableIterator<[string, string]>;
20
+ getSetCookie(): string[];
20
21
  [Symbol.iterator](): IterableIterator<[string, string]>;
21
22
  }
23
+ export declare function splitSetCookieHeader(setCookieHeader: string): string[];
@@ -5,7 +5,7 @@ export type RequestPonyfillInit = PonyfillBodyOptions & Omit<RequestInit, 'body'
5
5
  headers?: PonyfillHeadersInit;
6
6
  headersSerializer?: HeadersSerializer;
7
7
  };
8
- type HeadersSerializer = (headers: Headers) => Record<string, string>;
8
+ type HeadersSerializer = (headers: Headers, onContentLength?: (contentLength: string) => void) => string[];
9
9
  export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> implements Request {
10
10
  constructor(input: RequestInfo | URL, options?: RequestPonyfillInit);
11
11
  headersSerializer: HeadersSerializer;
@@ -22,7 +22,8 @@ export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> im
22
22
  referrer: string;
23
23
  referrerPolicy: ReferrerPolicy;
24
24
  url: string;
25
- signal: AbortSignal;
26
- clone(): Request;
25
+ private _signal;
26
+ get signal(): AbortSignal;
27
+ clone(): PonyfillRequest;
27
28
  }
28
29
  export {};
@@ -5,7 +5,7 @@ export type RequestPonyfillInit = PonyfillBodyOptions & Omit<RequestInit, 'body'
5
5
  headers?: PonyfillHeadersInit;
6
6
  headersSerializer?: HeadersSerializer;
7
7
  };
8
- type HeadersSerializer = (headers: Headers) => Record<string, string>;
8
+ type HeadersSerializer = (headers: Headers, onContentLength?: (contentLength: string) => void) => string[];
9
9
  export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> implements Request {
10
10
  constructor(input: RequestInfo | URL, options?: RequestPonyfillInit);
11
11
  headersSerializer: HeadersSerializer;
@@ -22,7 +22,8 @@ export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> im
22
22
  referrer: string;
23
23
  referrerPolicy: ReferrerPolicy;
24
24
  url: string;
25
- signal: AbortSignal;
26
- clone(): Request;
25
+ private _signal;
26
+ get signal(): AbortSignal;
27
+ clone(): PonyfillRequest;
27
28
  }
28
29
  export {};
@@ -7,8 +7,8 @@ export type ResponsePonyfilInit = PonyfillBodyOptions & Omit<ResponseInit, 'head
7
7
  type?: ResponseType;
8
8
  };
9
9
  export declare class PonyfillResponse<TJSON = any> extends PonyfillBody<TJSON> implements Response {
10
- constructor(body?: BodyPonyfillInit | null, init?: ResponsePonyfilInit);
11
10
  headers: Headers;
11
+ constructor(body?: BodyPonyfillInit | null, init?: ResponsePonyfilInit);
12
12
  get ok(): boolean;
13
13
  status: number;
14
14
  statusText: string;
@@ -18,5 +18,5 @@ export declare class PonyfillResponse<TJSON = any> extends PonyfillBody<TJSON> i
18
18
  clone(): PonyfillResponse<any>;
19
19
  static error(): PonyfillResponse<any>;
20
20
  static redirect(url: string, status?: number): PonyfillResponse<any>;
21
- static json<T = any>(data: T, init?: RequestInit): PonyfillResponse<T>;
21
+ static json<T = any>(data: T, init?: ResponsePonyfilInit): PonyfillResponse<T>;
22
22
  }
@@ -7,8 +7,8 @@ export type ResponsePonyfilInit = PonyfillBodyOptions & Omit<ResponseInit, 'head
7
7
  type?: ResponseType;
8
8
  };
9
9
  export declare class PonyfillResponse<TJSON = any> extends PonyfillBody<TJSON> implements Response {
10
- constructor(body?: BodyPonyfillInit | null, init?: ResponsePonyfilInit);
11
10
  headers: Headers;
11
+ constructor(body?: BodyPonyfillInit | null, init?: ResponsePonyfilInit);
12
12
  get ok(): boolean;
13
13
  status: number;
14
14
  statusText: string;
@@ -18,5 +18,5 @@ export declare class PonyfillResponse<TJSON = any> extends PonyfillBody<TJSON> i
18
18
  clone(): PonyfillResponse<any>;
19
19
  static error(): PonyfillResponse<any>;
20
20
  static redirect(url: string, status?: number): PonyfillResponse<any>;
21
- static json<T = any>(data: T, init?: RequestInit): PonyfillResponse<T>;
21
+ static json<T = any>(data: T, init?: ResponsePonyfilInit): PonyfillResponse<T>;
22
22
  }
@@ -1,2 +1,3 @@
1
1
  export declare function getHeadersObj(headers: Headers): Record<string, string>;
2
2
  export declare function uint8ArrayToArrayBuffer(uint8array: Uint8Array): ArrayBuffer;
3
+ export declare function defaultHeadersSerializer(headers: Headers, onContentLength?: (value: string) => void): string[];
@@ -1,2 +1,3 @@
1
1
  export declare function getHeadersObj(headers: Headers): Record<string, string>;
2
2
  export declare function uint8ArrayToArrayBuffer(uint8array: Uint8Array): ArrayBuffer;
3
+ export declare function defaultHeadersSerializer(headers: Headers, onContentLength?: (value: string) => void): string[];