@gjsify/fetch 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -1,17 +0,0 @@
1
- class FetchBaseError extends Error {
2
- type;
3
- constructor(message, type) {
4
- super(message);
5
- Error.captureStackTrace(this, this.constructor);
6
- this.type = type;
7
- }
8
- get name() {
9
- return this.constructor.name;
10
- }
11
- get [Symbol.toStringTag]() {
12
- return this.constructor.name;
13
- }
14
- }
15
- export {
16
- FetchBaseError
17
- };
@@ -1,21 +0,0 @@
1
- import { FetchBaseError } from "./base.js";
2
- class FetchError extends FetchBaseError {
3
- code;
4
- errno;
5
- erroredSysCall;
6
- /**
7
- * @param message Error message for human
8
- * @param type Error type for machine
9
- * @param systemError For Node.js system error
10
- */
11
- constructor(message, type, systemError) {
12
- super(message, type);
13
- if (systemError) {
14
- this.code = this.errno = systemError.code;
15
- this.erroredSysCall = systemError.syscall;
16
- }
17
- }
18
- }
19
- export {
20
- FetchError
21
- };
@@ -1,202 +0,0 @@
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
- */
14
- constructor(init) {
15
- let result = [];
16
- 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]));
20
- }
21
- } else if (init == null) {
22
- } else if (typeof init === "object" && !types.isBoxedPrimitive(init)) {
23
- const method = init[Symbol.iterator];
24
- if (method == null) {
25
- result.push(...Object.entries(init));
26
- } else {
27
- if (typeof method !== "function") {
28
- throw new TypeError("Header pairs must be iterable");
29
- }
30
- result = [...init].map((pair) => {
31
- if (typeof pair !== "object" || types.isBoxedPrimitive(pair)) {
32
- throw new TypeError("Each header pair must be an iterable object");
33
- }
34
- return [...pair];
35
- }).map((pair) => {
36
- if (pair.length !== 2) {
37
- throw new TypeError("Each header pair must be a name/value tuple");
38
- }
39
- return [...pair];
40
- });
41
- }
42
- } else {
43
- throw new TypeError("Failed to construct 'Headers': The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)");
44
- }
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
- }
86
- get [Symbol.toStringTag]() {
87
- return this.constructor.name;
88
- }
89
- toString() {
90
- return Object.prototype.toString.call(this);
91
- }
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;
98
- }
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;
116
- }
117
- get(name) {
118
- const values = this.getAll(name);
119
- if (values.length === 0) {
120
- return null;
121
- }
122
- let value = values.join(", ");
123
- if (/^content-encoding$/i.test(name)) {
124
- value = value.toLowerCase();
125
- }
126
- return value;
127
- }
128
- forEach(callback, thisArg = void 0) {
129
- for (const name of this.keys()) {
130
- Reflect.apply(callback, thisArg, [this.get(name), name, this]);
131
- }
132
- }
133
- *values() {
134
- for (const name of this.keys()) {
135
- yield this.get(name);
136
- }
137
- }
138
- /**
139
- *
140
- */
141
- *entries() {
142
- for (const name of this.keys()) {
143
- yield [name, this.get(name)];
144
- }
145
- }
146
- [Symbol.iterator]() {
147
- return this.entries();
148
- }
149
- /**
150
- * Node-fetch non-spec method
151
- * returning all headers and their values as array
152
- */
153
- raw() {
154
- return [...this.keys()].reduce((result, key) => {
155
- result[key] = this.getAll(key);
156
- return result;
157
- }, {});
158
- }
159
- /**
160
- * For better console.log(headers) and also to convert Headers into Node.js Request compatible format
161
- */
162
- [Symbol.for("nodejs.util.inspect.custom")]() {
163
- return [...this.keys()].reduce((result, key) => {
164
- const values = this.getAll(key);
165
- if (key === "host") {
166
- result[key] = values[0];
167
- } else {
168
- result[key] = values.length > 1 ? values : values[0];
169
- }
170
- return result;
171
- }, {});
172
- }
173
- }
174
- Object.defineProperties(
175
- Headers.prototype,
176
- ["get", "entries", "forEach", "values"].reduce((result, property) => {
177
- result[property] = { enumerable: true };
178
- return result;
179
- }, {})
180
- );
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
- export {
200
- Headers as default,
201
- fromRawHeaders
202
- };
package/lib/cjs/index.js DELETED
@@ -1,224 +0,0 @@
1
- import Soup from "@girs/soup-3.0";
2
- import Request from "./request.js";
3
- import zlib from "zlib";
4
- import Stream, { PassThrough, pipeline as pump } from "stream";
5
- import dataUriToBuffer from "data-uri-to-buffer";
6
- import { writeToStream, clone } from "./body.js";
7
- import Response from "./response.js";
8
- import Headers from "./headers.js";
9
- import { getSoupRequestOptions } from "./request.js";
10
- import { FetchError } from "./errors/fetch-error.js";
11
- import { AbortError } from "./errors/abort-error.js";
12
- import { isRedirect } from "./utils/is-redirect.js";
13
- import { FormData } from "formdata-polyfill/esm.min.js";
14
- import { isDomainOrSubdomain, isSameProtocol } from "./utils/is.js";
15
- import { parseReferrerPolicyFromHeader } from "./utils/referrer.js";
16
- import {
17
- Blob,
18
- File,
19
- fileFromSync,
20
- fileFrom,
21
- blobFromSync,
22
- blobFrom
23
- } from "./utils/blob-from.js";
24
- import { URL } from "@gjsify/deno-runtime/ext/url/00_url";
25
- const supportedSchemas = /* @__PURE__ */ new Set(["data:", "http:", "https:"]);
26
- async function fetch(url, init = {}) {
27
- return new Promise(async (resolve, reject) => {
28
- const request = new Request(url, init);
29
- const { parsedURL, options } = getSoupRequestOptions(request);
30
- if (!supportedSchemas.has(parsedURL.protocol)) {
31
- throw new TypeError(`@gjsify/fetch cannot load ${url}. URL scheme "${parsedURL.protocol.replace(/:$/, "")}" is not supported.`);
32
- }
33
- if (parsedURL.protocol === "data:") {
34
- const data = dataUriToBuffer(request.url);
35
- const response2 = new Response(data, { headers: { "Content-Type": data.typeFull } });
36
- resolve(response2);
37
- return;
38
- }
39
- const { signal } = request;
40
- let response = null;
41
- const abort = () => {
42
- const error = new AbortError("The operation was aborted.");
43
- reject(error);
44
- if (request.body && request.body instanceof Stream.Readable) {
45
- request.body.destroy(error);
46
- }
47
- if (!response || !response.body) {
48
- return;
49
- }
50
- response.body.emit("error", error);
51
- };
52
- if (signal && signal.aborted) {
53
- abort();
54
- return;
55
- }
56
- const abortAndFinalize = () => {
57
- abort();
58
- finalize();
59
- };
60
- let readable;
61
- let cancellable;
62
- try {
63
- const sendRes = await request._send(options);
64
- readable = sendRes.readable;
65
- cancellable = sendRes.cancellable;
66
- } catch (error) {
67
- reject(error);
68
- }
69
- if (signal) {
70
- signal.addEventListener("abort", abortAndFinalize);
71
- }
72
- const cancelledSignalId = cancellable.connect("cancelled", () => {
73
- abortAndFinalize();
74
- });
75
- const finalize = () => {
76
- cancellable.cancel();
77
- if (signal) {
78
- signal.removeEventListener("abort", abortAndFinalize);
79
- }
80
- cancellable.disconnect(cancelledSignalId);
81
- };
82
- const message = request._message;
83
- readable.on("error", (error) => {
84
- reject(new FetchError(`request to ${request.url} failed, reason: ${error.message}`, "system", error));
85
- finalize();
86
- });
87
- message.connect("finished", (message2) => {
88
- const headers = Headers._newFromSoupMessage(request._message, Soup.MessageHeadersType.RESPONSE);
89
- const statusCode = message2.status_code;
90
- const statusMessage = message2.get_reason_phrase();
91
- if (isRedirect(statusCode)) {
92
- const location = headers.get("Location");
93
- let locationURL = null;
94
- try {
95
- locationURL = location === null ? null : new URL(location, request.url);
96
- } catch {
97
- if (request.redirect !== "manual") {
98
- reject(new FetchError(`uri requested responds with an invalid redirect URL: ${location}`, "invalid-redirect"));
99
- finalize();
100
- return;
101
- }
102
- }
103
- switch (request.redirect) {
104
- case "error":
105
- reject(new FetchError(`uri requested responds with a redirect, redirect mode is set to error: ${request.url}`, "no-redirect"));
106
- finalize();
107
- return;
108
- case "manual":
109
- break;
110
- case "follow": {
111
- if (locationURL === null) {
112
- break;
113
- }
114
- if (request.counter >= request.follow) {
115
- reject(new FetchError(`maximum redirect reached at: ${request.url}`, "max-redirect"));
116
- finalize();
117
- return;
118
- }
119
- const requestOptions = {
120
- headers: new Headers(request.headers),
121
- follow: request.follow,
122
- counter: request.counter + 1,
123
- agent: request.agent,
124
- compress: request.compress,
125
- method: request.method,
126
- body: clone(request),
127
- signal: request.signal,
128
- size: request.size,
129
- referrer: request.referrer,
130
- referrerPolicy: request.referrerPolicy
131
- };
132
- if (!isDomainOrSubdomain(request.url, locationURL) || !isSameProtocol(request.url, locationURL)) {
133
- for (const name of ["authorization", "www-authenticate", "cookie", "cookie2"]) {
134
- requestOptions.headers.delete(name);
135
- }
136
- }
137
- if (statusCode !== 303 && request.body && init.body instanceof Stream.Readable) {
138
- reject(new FetchError("Cannot follow redirect with body being a readable stream", "unsupported-redirect"));
139
- finalize();
140
- return;
141
- }
142
- if (statusCode === 303 || (statusCode === 301 || statusCode === 302) && request.method === "POST") {
143
- requestOptions.method = "GET";
144
- requestOptions.body = void 0;
145
- requestOptions.headers.delete("content-length");
146
- }
147
- const responseReferrerPolicy = parseReferrerPolicyFromHeader(headers);
148
- if (responseReferrerPolicy) {
149
- requestOptions.referrerPolicy = responseReferrerPolicy;
150
- }
151
- resolve(fetch(new Request(locationURL, requestOptions)));
152
- finalize();
153
- return;
154
- }
155
- default:
156
- return reject(new TypeError(`Redirect option '${request.redirect}' is not a valid value of RequestRedirect`));
157
- }
158
- }
159
- let body = pump(response_, new PassThrough(), (error) => {
160
- if (error) {
161
- reject(error);
162
- }
163
- });
164
- const responseOptions = {
165
- url: request.url,
166
- status: statusCode,
167
- statusText: statusMessage,
168
- headers,
169
- size: request.size,
170
- counter: request.counter,
171
- highWaterMark: request.highWaterMark
172
- };
173
- const codings = headers.get("Content-Encoding");
174
- if (!request.compress || request.method === "HEAD" || codings === null || statusCode === 204 || statusCode === 304) {
175
- response = new Response(body, responseOptions);
176
- resolve(response);
177
- return;
178
- }
179
- const zlibOptions = {
180
- flush: zlib.Z_SYNC_FLUSH,
181
- finishFlush: zlib.Z_SYNC_FLUSH
182
- };
183
- if (codings === "gzip" || codings === "x-gzip") {
184
- body = pump(body, zlib.createGunzip(zlibOptions), (error) => {
185
- if (error) {
186
- reject(error);
187
- }
188
- });
189
- response = new Response(body, responseOptions);
190
- resolve(response);
191
- return;
192
- }
193
- if (codings === "br") {
194
- body = pump(body, zlib.createBrotliDecompress(), (error) => {
195
- if (error) {
196
- reject(error);
197
- }
198
- });
199
- response = new Response(body, responseOptions);
200
- resolve(response);
201
- return;
202
- }
203
- response = new Response(body, responseOptions);
204
- resolve(response);
205
- });
206
- writeToStream(inputStream, request).catch(reject);
207
- });
208
- }
209
- export {
210
- AbortError,
211
- Blob,
212
- FetchError,
213
- File,
214
- FormData,
215
- Headers,
216
- Request,
217
- Response,
218
- blobFrom,
219
- blobFromSync,
220
- fetch as default,
221
- fileFrom,
222
- fileFromSync,
223
- isRedirect
224
- };