@gjsify/fetch 0.0.4 → 0.1.1

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.
Files changed (88) hide show
  1. package/README.md +27 -2
  2. package/globals.mjs +12 -0
  3. package/lib/body.d.ts +69 -0
  4. package/lib/body.js +375 -0
  5. package/lib/errors/abort-error.d.ts +7 -0
  6. package/lib/errors/abort-error.js +9 -0
  7. package/lib/errors/base.d.ts +6 -0
  8. package/lib/errors/base.js +17 -0
  9. package/lib/errors/fetch-error.d.ts +16 -0
  10. package/lib/errors/fetch-error.js +23 -0
  11. package/lib/esm/body.js +104 -56
  12. package/lib/esm/errors/base.js +3 -1
  13. package/lib/esm/headers.js +116 -131
  14. package/lib/esm/index.js +145 -190
  15. package/lib/esm/request.js +42 -41
  16. package/lib/esm/response.js +19 -4
  17. package/lib/esm/utils/blob-from.js +2 -98
  18. package/lib/esm/utils/data-uri.js +23 -0
  19. package/lib/esm/utils/is.js +7 -3
  20. package/lib/esm/utils/multipart-parser.js +5 -2
  21. package/lib/esm/utils/referrer.js +10 -10
  22. package/lib/esm/utils/soup-helpers.js +22 -0
  23. package/lib/headers.d.ts +33 -0
  24. package/lib/headers.js +195 -0
  25. package/lib/index.d.ts +18 -0
  26. package/lib/index.js +205 -0
  27. package/lib/request.d.ts +101 -0
  28. package/lib/request.js +308 -0
  29. package/lib/response.d.ts +73 -0
  30. package/lib/response.js +158 -0
  31. package/lib/types/index.d.ts +1 -0
  32. package/lib/types/index.js +1 -0
  33. package/lib/types/system-error.d.ts +11 -0
  34. package/lib/types/system-error.js +2 -0
  35. package/lib/utils/blob-from.d.ts +2 -0
  36. package/lib/utils/blob-from.js +4 -0
  37. package/lib/utils/data-uri.d.ts +10 -0
  38. package/lib/utils/data-uri.js +27 -0
  39. package/lib/utils/get-search.d.ts +1 -0
  40. package/lib/utils/get-search.js +8 -0
  41. package/lib/utils/is-redirect.d.ts +7 -0
  42. package/lib/utils/is-redirect.js +10 -0
  43. package/lib/utils/is.d.ts +35 -0
  44. package/lib/utils/is.js +74 -0
  45. package/lib/utils/multipart-parser.d.ts +2 -0
  46. package/lib/utils/multipart-parser.js +396 -0
  47. package/lib/utils/referrer.d.ts +76 -0
  48. package/lib/utils/referrer.js +283 -0
  49. package/lib/utils/soup-helpers.d.ts +12 -0
  50. package/lib/utils/soup-helpers.js +25 -0
  51. package/package.json +23 -27
  52. package/src/body.ts +181 -169
  53. package/src/errors/base.ts +3 -1
  54. package/src/headers.ts +155 -202
  55. package/src/index.spec.ts +268 -3
  56. package/src/index.ts +199 -312
  57. package/src/request.ts +84 -75
  58. package/src/response.ts +48 -18
  59. package/src/test.mts +1 -1
  60. package/src/utils/blob-from.ts +4 -164
  61. package/src/utils/data-uri.ts +29 -0
  62. package/src/utils/is.ts +15 -15
  63. package/src/utils/multipart-parser.ts +3 -3
  64. package/src/utils/referrer.ts +11 -11
  65. package/src/utils/soup-helpers.ts +37 -0
  66. package/tsconfig.json +4 -4
  67. package/tsconfig.tsbuildinfo +1 -0
  68. package/lib/cjs/body.js +0 -255
  69. package/lib/cjs/errors/abort-error.js +0 -9
  70. package/lib/cjs/errors/base.js +0 -17
  71. package/lib/cjs/errors/fetch-error.js +0 -21
  72. package/lib/cjs/headers.js +0 -202
  73. package/lib/cjs/index.js +0 -224
  74. package/lib/cjs/request.js +0 -281
  75. package/lib/cjs/response.js +0 -133
  76. package/lib/cjs/types/index.js +0 -1
  77. package/lib/cjs/types/system-error.js +0 -1
  78. package/lib/cjs/utils/blob-from.js +0 -101
  79. package/lib/cjs/utils/get-search.js +0 -11
  80. package/lib/cjs/utils/is-redirect.js +0 -7
  81. package/lib/cjs/utils/is.js +0 -28
  82. package/lib/cjs/utils/multipart-parser.js +0 -353
  83. package/lib/cjs/utils/referrer.js +0 -153
  84. package/test.gjs.js +0 -34758
  85. package/test.gjs.mjs +0 -53172
  86. package/test.node.js +0 -1226
  87. package/test.node.mjs +0 -6273
  88. package/tsconfig.types.json +0 -8
package/lib/esm/body.js CHANGED
@@ -1,15 +1,24 @@
1
- import { URLSearchParams } from "@gjsify/deno-runtime/ext/url/00_url";
2
- import { Blob } from "@gjsify/deno-runtime/ext/web/09_file";
3
- import { PassThrough, pipeline as pipelineCb, Readable, Stream } from "stream";
4
- import { ReadableStream as StreamWebReadableStream } from "stream/web";
5
- import { types, deprecate, promisify } from "util";
6
- import { Buffer } from "buffer";
7
- import { FormData, formDataToBlob } from "formdata-polyfill/esm.min.js";
1
+ import { URLSearchParams } from "@gjsify/url";
2
+ import { Blob } from "./utils/blob-from.js";
3
+ import { PassThrough, pipeline as pipelineCb, Readable, Stream } from "node:stream";
4
+ import { Buffer } from "node:buffer";
5
+ import { FormData, formDataToBlob } from "@gjsify/formdata";
8
6
  import { FetchError } from "./errors/fetch-error.js";
9
7
  import { FetchBaseError } from "./errors/base.js";
10
8
  import { isBlob, isURLSearchParameters } from "./utils/is.js";
11
- const pipeline = promisify(pipelineCb);
12
- const INTERNALS = Symbol("Body internals");
9
+ const pipeline = (source, dest) => new Promise((resolve, reject) => {
10
+ pipelineCb(source, dest, (err) => {
11
+ if (err) reject(err);
12
+ else resolve();
13
+ });
14
+ });
15
+ const INTERNALS = /* @__PURE__ */ Symbol("Body internals");
16
+ function isAnyArrayBuffer(val) {
17
+ return val instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && val instanceof SharedArrayBuffer;
18
+ }
19
+ function isBoxedPrimitive(val) {
20
+ return val instanceof String || val instanceof Number || val instanceof Boolean || typeof Symbol !== "undefined" && val instanceof Symbol || typeof BigInt !== "undefined" && val instanceof BigInt;
21
+ }
13
22
  class Body {
14
23
  [INTERNALS] = {
15
24
  body: null,
@@ -21,47 +30,71 @@ class Body {
21
30
  size = 0;
22
31
  constructor(body, options = { size: 0 }) {
23
32
  this.size = options.size || 0;
24
- if (body === null) {
33
+ if (body === null || body === void 0) {
25
34
  this[INTERNALS].body = null;
26
35
  } else if (isURLSearchParameters(body)) {
27
36
  this[INTERNALS].body = Buffer.from(body.toString());
28
37
  } else if (isBlob(body)) {
38
+ this[INTERNALS].body = body;
29
39
  } else if (Buffer.isBuffer(body)) {
30
- } else if (types.isAnyArrayBuffer(body)) {
40
+ this[INTERNALS].body = body;
41
+ } else if (isAnyArrayBuffer(body)) {
31
42
  this[INTERNALS].body = Buffer.from(body);
32
43
  } else if (ArrayBuffer.isView(body)) {
33
44
  this[INTERNALS].body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
34
45
  } else if (body instanceof Readable) {
35
46
  this[INTERNALS].body = body;
36
- } else if (body instanceof ReadableStream || body instanceof StreamWebReadableStream) {
37
- this[INTERNALS].body = Readable.fromWeb(body);
47
+ } else if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) {
48
+ this[INTERNALS].body = readableStreamToReadable(body);
38
49
  } else if (body instanceof FormData) {
39
- this[INTERNALS].body = formDataToBlob(body);
40
- this[INTERNALS].boundary = this[INTERNALS].body.type.split("=")[1];
50
+ const blob = formDataToBlob(body);
51
+ this[INTERNALS].body = blob;
52
+ this[INTERNALS].boundary = blob.type?.split("boundary=")?.[1] ?? "";
41
53
  } else if (typeof body === "string") {
42
54
  this[INTERNALS].body = Buffer.from(body);
43
55
  } else if (body instanceof URLSearchParams) {
44
56
  this[INTERNALS].body = Buffer.from(body.toString());
45
57
  } else {
46
58
  console.warn(`Unknown body type "${typeof body}", try to parse the body to string!`);
47
- this[INTERNALS].body = Readable.from(typeof body.toString === "function" ? body.toString() : body);
59
+ this[INTERNALS].body = Buffer.from(String(body));
48
60
  }
49
- if (Buffer.isBuffer(body)) {
50
- this[INTERNALS].stream = Readable.from(body);
51
- } else if (isBlob(body)) {
52
- this[INTERNALS].stream = Readable.from(body.stream());
53
- } else if (body instanceof Readable) {
54
- this[INTERNALS].stream = body;
61
+ const b = this[INTERNALS].body;
62
+ if (Buffer.isBuffer(b)) {
63
+ this[INTERNALS].stream = Readable.from(b);
64
+ } else if (isBlob(b)) {
65
+ this[INTERNALS].stream = Readable.from(blobToAsyncIterable(b));
66
+ } else if (b instanceof Readable) {
67
+ this[INTERNALS].stream = b;
55
68
  }
56
- if (body instanceof Stream) {
57
- body.on("error", (error_) => {
69
+ if (b instanceof Stream) {
70
+ b.on("error", (error_) => {
58
71
  const error = error_ instanceof FetchBaseError ? error_ : new FetchError(`Invalid response body while trying to fetch ${this.url}: ${error_.message}`, "system", error_);
59
72
  this[INTERNALS].error = error;
60
73
  });
61
74
  }
62
75
  }
63
76
  get body() {
64
- return Readable.toWeb(this[INTERNALS].stream);
77
+ const stream = this[INTERNALS].stream;
78
+ if (!stream) return null;
79
+ if (typeof ReadableStream !== "undefined") {
80
+ return new ReadableStream({
81
+ start(controller) {
82
+ stream.on("data", (chunk) => {
83
+ controller.enqueue(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk));
84
+ });
85
+ stream.on("end", () => {
86
+ controller.close();
87
+ });
88
+ stream.on("error", (err) => {
89
+ controller.error(err);
90
+ });
91
+ },
92
+ cancel() {
93
+ stream.destroy();
94
+ }
95
+ });
96
+ }
97
+ return null;
65
98
  }
66
99
  get _stream() {
67
100
  return this[INTERNALS].stream;
@@ -71,8 +104,6 @@ class Body {
71
104
  }
72
105
  /**
73
106
  * Decode response as ArrayBuffer
74
- *
75
- * @return Promise
76
107
  */
77
108
  async arrayBuffer() {
78
109
  const { buffer, byteOffset, byteLength } = await consumeBody(this);
@@ -80,7 +111,7 @@ class Body {
80
111
  }
81
112
  async formData() {
82
113
  const ct = this.headers?.get("content-type");
83
- if (ct.startsWith("application/x-www-form-urlencoded")) {
114
+ if (ct?.startsWith("application/x-www-form-urlencoded")) {
84
115
  const formData = new FormData();
85
116
  const parameters = new URLSearchParams(await this.text());
86
117
  for (const [name, value] of parameters) {
@@ -93,10 +124,7 @@ class Body {
93
124
  }
94
125
  /**
95
126
  * Return raw response as Blob
96
- *
97
- * @return Promise
98
127
  */
99
- // @ts-ignore
100
128
  async blob() {
101
129
  const ct = this.headers?.get("content-type") || this[INTERNALS].body && this[INTERNALS].body.type || "";
102
130
  const buf = await this.arrayBuffer();
@@ -106,8 +134,6 @@ class Body {
106
134
  }
107
135
  /**
108
136
  * Decode response as json
109
- *
110
- * @return Promise
111
137
  */
112
138
  async json() {
113
139
  const text = await this.text();
@@ -115,8 +141,6 @@ class Body {
115
141
  }
116
142
  /**
117
143
  * Decode response as text
118
- *
119
- * @return Promise
120
144
  */
121
145
  async text() {
122
146
  const buffer = await consumeBody(this);
@@ -129,13 +153,7 @@ Object.defineProperties(Body.prototype, {
129
153
  arrayBuffer: { enumerable: true },
130
154
  blob: { enumerable: true },
131
155
  json: { enumerable: true },
132
- text: { enumerable: true },
133
- data: { get: deprecate(
134
- () => {
135
- },
136
- "data doesn't exist, use json(), text(), arrayBuffer(), or body instead",
137
- "https://github.com/node-fetch/node-fetch/issues/1000 (response)"
138
- ) }
156
+ text: { enumerable: true }
139
157
  });
140
158
  async function consumeBody(data) {
141
159
  if (data[INTERNALS].disturbed) {
@@ -165,20 +183,18 @@ async function consumeBody(data) {
165
183
  accum.push(chunk);
166
184
  }
167
185
  } catch (error) {
168
- const error_ = error instanceof FetchBaseError ? error : new FetchError(`Invalid response body while trying to fetch ${data.url}: ${error.message}`, "system", error);
186
+ const err = error instanceof Error ? error : new Error(String(error));
187
+ const error_ = error instanceof FetchBaseError ? error : new FetchError(`Invalid response body while trying to fetch ${data.url}: ${err.message}`, "system", err);
169
188
  throw error_;
170
189
  }
171
- if (body.readableEnded === true || body._readableState.ended === true) {
172
- try {
173
- if (accum.every((c) => typeof c === "string")) {
174
- return Buffer.from(accum.join(""));
175
- }
176
- return Buffer.concat(accum, accumBytes);
177
- } catch (error) {
178
- throw new FetchError(`Could not create Buffer from response body for ${data.url}: ${error.message}`, "system", error);
190
+ try {
191
+ if (accum.every((c) => typeof c === "string")) {
192
+ return Buffer.from(accum.join(""));
179
193
  }
180
- } else {
181
- throw new FetchError(`Premature close of server response while trying to fetch ${data.url}`);
194
+ return Buffer.concat(accum, accumBytes);
195
+ } catch (error) {
196
+ const err = error instanceof Error ? error : new Error(String(error));
197
+ throw new FetchError(`Could not create Buffer from response body for ${data.url}: ${err.message}`, "system", err);
182
198
  }
183
199
  }
184
200
  const clone = (instance, highWaterMark) => {
@@ -211,7 +227,7 @@ const extractContentType = (body, request) => {
211
227
  if (isBlob(body)) {
212
228
  return body.type || null;
213
229
  }
214
- if (Buffer.isBuffer(body) || types.isAnyArrayBuffer(body) || ArrayBuffer.isView(body)) {
230
+ if (Buffer.isBuffer(body) || isAnyArrayBuffer(body) || ArrayBuffer.isView(body)) {
215
231
  return null;
216
232
  }
217
233
  if (body instanceof FormData) {
@@ -234,8 +250,8 @@ const getTotalBytes = (request) => {
234
250
  return body.length;
235
251
  }
236
252
  if (body && typeof body.getLengthSync === "function") {
237
- const anyBody = body;
238
- return anyBody.hasKnownLength && anyBody.hasKnownLength() ? anyBody.getLengthSync() : null;
253
+ const streamBody = body;
254
+ return streamBody.hasKnownLength && streamBody.hasKnownLength() ? streamBody.getLengthSync() : null;
239
255
  }
240
256
  return null;
241
257
  };
@@ -246,6 +262,38 @@ const writeToStream = async (dest, { body }) => {
246
262
  await pipeline(body, dest);
247
263
  }
248
264
  };
265
+ function readableStreamToReadable(webStream) {
266
+ const reader = webStream.getReader();
267
+ return new Readable({
268
+ async read() {
269
+ try {
270
+ const { done, value } = await reader.read();
271
+ if (done) {
272
+ this.push(null);
273
+ } else {
274
+ this.push(Buffer.from(value));
275
+ }
276
+ } catch (err) {
277
+ this.destroy(err);
278
+ }
279
+ },
280
+ destroy(_err, callback) {
281
+ reader.cancel().then(() => callback(null), callback);
282
+ }
283
+ });
284
+ }
285
+ async function* blobToAsyncIterable(blob) {
286
+ if (typeof blob.stream === "function") {
287
+ const reader = blob.stream().getReader();
288
+ while (true) {
289
+ const { done, value } = await reader.read();
290
+ if (done) break;
291
+ yield value;
292
+ }
293
+ } else {
294
+ yield new Uint8Array(await blob.arrayBuffer());
295
+ }
296
+ }
249
297
  export {
250
298
  clone,
251
299
  Body as default,
@@ -2,7 +2,9 @@ class FetchBaseError extends Error {
2
2
  type;
3
3
  constructor(message, type) {
4
4
  super(message);
5
- Error.captureStackTrace(this, this.constructor);
5
+ if (typeof Error.captureStackTrace === "function") {
6
+ Error.captureStackTrace(this, this.constructor);
7
+ }
6
8
  this.type = type;
7
9
  }
8
10
  get name() {
@@ -1,122 +1,80 @@
1
1
  import Soup from "@girs/soup-3.0";
2
- import { URLSearchParams } from "@gjsify/deno-runtime/ext/url/00_url";
3
- import { types } from "util";
4
- import * as http from "http";
5
- const validateHeaderName = http.validateHeaderName;
6
- const validateHeaderValue = http.validateHeaderValue;
7
- class Headers extends URLSearchParams {
8
- /**
9
- * Headers class
10
- *
11
- * @constructor
12
- * @param init Response headers
13
- */
2
+ import { validateHeaderName, validateHeaderValue } from "@gjsify/http";
3
+ const _headers = /* @__PURE__ */ Symbol("Headers.headers");
4
+ function isBoxedPrimitive(val) {
5
+ return val instanceof String || val instanceof Number || val instanceof Boolean || typeof Symbol !== "undefined" && val instanceof Symbol || typeof BigInt !== "undefined" && val instanceof BigInt;
6
+ }
7
+ class Headers {
8
+ [_headers];
14
9
  constructor(init) {
15
- let result = [];
10
+ this[_headers] = /* @__PURE__ */ new Map();
11
+ if (init == null) {
12
+ return;
13
+ }
16
14
  if (init instanceof Headers) {
17
- const raw = init.raw();
18
- for (const [name, values] of Object.entries(raw)) {
19
- result.push(...values.map((value) => [name, value]));
15
+ for (const [name, values] of init[_headers]) {
16
+ this[_headers].set(name, [...values]);
20
17
  }
21
- } else if (init == null) {
22
- } else if (typeof init === "object" && !types.isBoxedPrimitive(init)) {
18
+ return;
19
+ }
20
+ if (typeof init === "object" && !isBoxedPrimitive(init)) {
23
21
  const method = init[Symbol.iterator];
24
22
  if (method == null) {
25
- result.push(...Object.entries(init));
23
+ for (const [name, value] of Object.entries(init)) {
24
+ validateHeaderName(name);
25
+ validateHeaderValue(name, String(value));
26
+ this.append(name, String(value));
27
+ }
26
28
  } else {
27
29
  if (typeof method !== "function") {
28
30
  throw new TypeError("Header pairs must be iterable");
29
31
  }
30
- result = [...init].map((pair) => {
31
- if (typeof pair !== "object" || types.isBoxedPrimitive(pair)) {
32
+ for (const pair of init) {
33
+ if (typeof pair !== "object" || isBoxedPrimitive(pair)) {
32
34
  throw new TypeError("Each header pair must be an iterable object");
33
35
  }
34
- return [...pair];
35
- }).map((pair) => {
36
- if (pair.length !== 2) {
36
+ const arr = [...pair];
37
+ if (arr.length !== 2) {
37
38
  throw new TypeError("Each header pair must be a name/value tuple");
38
39
  }
39
- return [...pair];
40
- });
40
+ validateHeaderName(arr[0]);
41
+ validateHeaderValue(arr[0], String(arr[1]));
42
+ this.append(arr[0], String(arr[1]));
43
+ }
41
44
  }
42
45
  } else {
43
- throw new TypeError("Failed to construct 'Headers': The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)");
46
+ throw new TypeError(
47
+ "Failed to construct 'Headers': The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'"
48
+ );
44
49
  }
45
- result = result.length > 0 ? result.map(([name, value]) => {
46
- validateHeaderName(name);
47
- validateHeaderValue(name, String(value));
48
- return [String(name).toLowerCase(), String(value)];
49
- }) : void 0;
50
- super(result);
51
- return new Proxy(this, {
52
- get(target, p, receiver) {
53
- switch (p) {
54
- case "append":
55
- case "set":
56
- return (name, value) => {
57
- validateHeaderName(name);
58
- validateHeaderValue(name, String(value));
59
- return URLSearchParams.prototype[p].call(
60
- target,
61
- String(name).toLowerCase(),
62
- String(value)
63
- );
64
- };
65
- case "delete":
66
- case "has":
67
- case "getAll":
68
- return (name) => {
69
- validateHeaderName(name);
70
- return URLSearchParams.prototype[p].call(
71
- target,
72
- String(name).toLowerCase()
73
- );
74
- };
75
- case "keys":
76
- return () => {
77
- target.sort();
78
- return new Set(URLSearchParams.prototype.keys.call(target)).keys();
79
- };
80
- default:
81
- return Reflect.get(target, p, receiver);
82
- }
83
- }
84
- });
85
50
  }
86
- get [Symbol.toStringTag]() {
87
- return this.constructor.name;
51
+ append(name, value) {
52
+ validateHeaderName(name);
53
+ validateHeaderValue(name, value);
54
+ const lowerName = String(name).toLowerCase();
55
+ const strValue = String(value);
56
+ const existing = this[_headers].get(lowerName);
57
+ if (existing) {
58
+ existing.push(strValue);
59
+ } else {
60
+ this[_headers].set(lowerName, [strValue]);
61
+ }
88
62
  }
89
- toString() {
90
- return Object.prototype.toString.call(this);
63
+ set(name, value) {
64
+ validateHeaderName(name);
65
+ validateHeaderValue(name, value);
66
+ const lowerName = String(name).toLowerCase();
67
+ this[_headers].set(lowerName, [String(value)]);
91
68
  }
92
- _appendToSoupMessage(message, type = Soup.MessageHeadersType.REQUEST) {
93
- const soupHeaders = message ? message.get_request_headers() : new Soup.MessageHeaders(type);
94
- for (const header in this.entries()) {
95
- soupHeaders.append(header, this.get(header));
96
- }
97
- return soupHeaders;
69
+ delete(name) {
70
+ this[_headers].delete(String(name).toLowerCase());
98
71
  }
99
- static _newFromSoupMessage(message, type = Soup.MessageHeadersType.RESPONSE) {
100
- let soupHeaders;
101
- const headers = new Headers();
102
- if (type === Soup.MessageHeadersType.RESPONSE) {
103
- soupHeaders = message.get_response_headers();
104
- } else if (type === Soup.MessageHeadersType.REQUEST) {
105
- soupHeaders = message.get_request_headers();
106
- } else {
107
- for (const header in message.get_request_headers()) {
108
- headers.append(header, soupHeaders[header]);
109
- }
110
- soupHeaders = message.get_response_headers();
111
- }
112
- for (const header in soupHeaders) {
113
- headers.append(header, soupHeaders[header]);
114
- }
115
- return headers;
72
+ has(name) {
73
+ return this[_headers].has(String(name).toLowerCase());
116
74
  }
117
75
  get(name) {
118
- const values = this.getAll(name);
119
- if (values.length === 0) {
76
+ const values = this[_headers].get(String(name).toLowerCase());
77
+ if (!values || values.length === 0) {
120
78
  return null;
121
79
  }
122
80
  let value = values.join(", ");
@@ -125,19 +83,32 @@ class Headers extends URLSearchParams {
125
83
  }
126
84
  return value;
127
85
  }
128
- forEach(callback, thisArg = void 0) {
86
+ getAll(name) {
87
+ return this[_headers].get(String(name).toLowerCase()) ?? [];
88
+ }
89
+ getSetCookie() {
90
+ return this[_headers].get("set-cookie") ?? [];
91
+ }
92
+ forEach(callback, thisArg) {
129
93
  for (const name of this.keys()) {
130
94
  Reflect.apply(callback, thisArg, [this.get(name), name, this]);
131
95
  }
132
96
  }
97
+ *keys() {
98
+ const sorted = [...this[_headers].keys()].sort();
99
+ const seen = /* @__PURE__ */ new Set();
100
+ for (const key of sorted) {
101
+ if (!seen.has(key)) {
102
+ seen.add(key);
103
+ yield key;
104
+ }
105
+ }
106
+ }
133
107
  *values() {
134
108
  for (const name of this.keys()) {
135
109
  yield this.get(name);
136
110
  }
137
111
  }
138
- /**
139
- *
140
- */
141
112
  *entries() {
142
113
  for (const name of this.keys()) {
143
114
  yield [name, this.get(name)];
@@ -146,29 +117,62 @@ class Headers extends URLSearchParams {
146
117
  [Symbol.iterator]() {
147
118
  return this.entries();
148
119
  }
120
+ get [Symbol.toStringTag]() {
121
+ return "Headers";
122
+ }
123
+ toString() {
124
+ return Object.prototype.toString.call(this);
125
+ }
149
126
  /**
150
- * Node-fetch non-spec method
151
- * returning all headers and their values as array
127
+ * Node-fetch non-spec method: return all headers and their values as arrays.
152
128
  */
153
129
  raw() {
154
- return [...this.keys()].reduce((result, key) => {
155
- result[key] = this.getAll(key);
156
- return result;
157
- }, {});
130
+ const result = {};
131
+ for (const name of this.keys()) {
132
+ result[name] = this.getAll(name);
133
+ }
134
+ return result;
158
135
  }
159
136
  /**
160
- * For better console.log(headers) and also to convert Headers into Node.js Request compatible format
137
+ * Append all headers to a Soup.Message for sending.
161
138
  */
162
- [Symbol.for("nodejs.util.inspect.custom")]() {
163
- return [...this.keys()].reduce((result, key) => {
139
+ _appendToSoupMessage(message, type = Soup.MessageHeadersType.REQUEST) {
140
+ const soupHeaders = message ? message.get_request_headers() : new Soup.MessageHeaders(type);
141
+ for (const [name, value] of this.entries()) {
142
+ soupHeaders.append(name, value);
143
+ }
144
+ return soupHeaders;
145
+ }
146
+ /**
147
+ * Create a Headers instance from a Soup.Message's headers.
148
+ */
149
+ static _newFromSoupMessage(message, type = Soup.MessageHeadersType.RESPONSE) {
150
+ const headers = new Headers();
151
+ let soupHeaders;
152
+ if (type === Soup.MessageHeadersType.RESPONSE) {
153
+ soupHeaders = message.get_response_headers();
154
+ } else {
155
+ soupHeaders = message.get_request_headers();
156
+ }
157
+ soupHeaders.foreach((name, value) => {
158
+ headers.append(name, value);
159
+ });
160
+ return headers;
161
+ }
162
+ /**
163
+ * For better console.log(headers)
164
+ */
165
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
166
+ const result = {};
167
+ for (const key of this.keys()) {
164
168
  const values = this.getAll(key);
165
169
  if (key === "host") {
166
170
  result[key] = values[0];
167
171
  } else {
168
172
  result[key] = values.length > 1 ? values : values[0];
169
173
  }
170
- return result;
171
- }, {});
174
+ }
175
+ return result;
172
176
  }
173
177
  }
174
178
  Object.defineProperties(
@@ -178,25 +182,6 @@ Object.defineProperties(
178
182
  return result;
179
183
  }, {})
180
184
  );
181
- function fromRawHeaders(headers = []) {
182
- return new Headers(
183
- headers.reduce((result, value, index, array) => {
184
- if (index % 2 === 0) {
185
- result.push(array.slice(index, index + 2));
186
- }
187
- return result;
188
- }, []).filter(([name, value]) => {
189
- try {
190
- validateHeaderName(name);
191
- validateHeaderValue(name, String(value));
192
- return true;
193
- } catch {
194
- return false;
195
- }
196
- })
197
- );
198
- }
199
185
  export {
200
- Headers as default,
201
- fromRawHeaders
186
+ Headers as default
202
187
  };