@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.
- package/README.md +27 -2
- package/globals.mjs +12 -0
- package/lib/body.d.ts +69 -0
- package/lib/body.js +375 -0
- package/lib/errors/abort-error.d.ts +7 -0
- package/lib/errors/abort-error.js +9 -0
- package/lib/errors/base.d.ts +6 -0
- package/lib/errors/base.js +17 -0
- package/lib/errors/fetch-error.d.ts +16 -0
- package/lib/errors/fetch-error.js +23 -0
- package/lib/esm/body.js +104 -56
- package/lib/esm/errors/base.js +3 -1
- package/lib/esm/headers.js +116 -131
- package/lib/esm/index.js +145 -190
- package/lib/esm/request.js +42 -41
- package/lib/esm/response.js +19 -4
- package/lib/esm/utils/blob-from.js +2 -98
- package/lib/esm/utils/data-uri.js +23 -0
- package/lib/esm/utils/is.js +7 -3
- package/lib/esm/utils/multipart-parser.js +5 -2
- package/lib/esm/utils/referrer.js +10 -10
- package/lib/esm/utils/soup-helpers.js +22 -0
- package/lib/headers.d.ts +33 -0
- package/lib/headers.js +195 -0
- package/lib/index.d.ts +18 -0
- package/lib/index.js +205 -0
- package/lib/request.d.ts +101 -0
- package/lib/request.js +308 -0
- package/lib/response.d.ts +73 -0
- package/lib/response.js +158 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/system-error.d.ts +11 -0
- package/lib/types/system-error.js +2 -0
- package/lib/utils/blob-from.d.ts +2 -0
- package/lib/utils/blob-from.js +4 -0
- package/lib/utils/data-uri.d.ts +10 -0
- package/lib/utils/data-uri.js +27 -0
- package/lib/utils/get-search.d.ts +1 -0
- package/lib/utils/get-search.js +8 -0
- package/lib/utils/is-redirect.d.ts +7 -0
- package/lib/utils/is-redirect.js +10 -0
- package/lib/utils/is.d.ts +35 -0
- package/lib/utils/is.js +74 -0
- package/lib/utils/multipart-parser.d.ts +2 -0
- package/lib/utils/multipart-parser.js +396 -0
- package/lib/utils/referrer.d.ts +76 -0
- package/lib/utils/referrer.js +283 -0
- package/lib/utils/soup-helpers.d.ts +12 -0
- package/lib/utils/soup-helpers.js +25 -0
- package/package.json +23 -27
- package/src/body.ts +181 -169
- package/src/errors/base.ts +3 -1
- package/src/headers.ts +155 -202
- package/src/index.spec.ts +268 -3
- package/src/index.ts +199 -312
- package/src/request.ts +84 -75
- package/src/response.ts +48 -18
- package/src/test.mts +1 -1
- package/src/utils/blob-from.ts +4 -164
- package/src/utils/data-uri.ts +29 -0
- package/src/utils/is.ts +15 -15
- package/src/utils/multipart-parser.ts +3 -3
- package/src/utils/referrer.ts +11 -11
- package/src/utils/soup-helpers.ts +37 -0
- package/tsconfig.json +4 -4
- package/tsconfig.tsbuildinfo +1 -0
- package/lib/cjs/body.js +0 -255
- package/lib/cjs/errors/abort-error.js +0 -9
- package/lib/cjs/errors/base.js +0 -17
- package/lib/cjs/errors/fetch-error.js +0 -21
- package/lib/cjs/headers.js +0 -202
- package/lib/cjs/index.js +0 -224
- package/lib/cjs/request.js +0 -281
- package/lib/cjs/response.js +0 -133
- package/lib/cjs/types/index.js +0 -1
- package/lib/cjs/types/system-error.js +0 -1
- package/lib/cjs/utils/blob-from.js +0 -101
- package/lib/cjs/utils/get-search.js +0 -11
- package/lib/cjs/utils/is-redirect.js +0 -7
- package/lib/cjs/utils/is.js +0 -28
- package/lib/cjs/utils/multipart-parser.js +0 -353
- package/lib/cjs/utils/referrer.js +0 -153
- package/test.gjs.js +0 -34758
- package/test.gjs.mjs +0 -53172
- package/test.node.js +0 -1226
- package/test.node.mjs +0 -6273
- package/tsconfig.types.json +0 -8
package/lib/esm/index.js
CHANGED
|
@@ -1,210 +1,169 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import Stream, { PassThrough, pipeline as pump } from "stream";
|
|
5
|
-
import dataUriToBuffer from "data-uri-to-buffer";
|
|
6
|
-
import { writeToStream, clone } from "./body.js";
|
|
1
|
+
import Stream from "node:stream";
|
|
2
|
+
import { parseDataUri } from "./utils/data-uri.js";
|
|
3
|
+
import { clone } from "./body.js";
|
|
7
4
|
import Response from "./response.js";
|
|
8
5
|
import Headers from "./headers.js";
|
|
9
|
-
import { getSoupRequestOptions } from "./request.js";
|
|
6
|
+
import Request, { getSoupRequestOptions } from "./request.js";
|
|
10
7
|
import { FetchError } from "./errors/fetch-error.js";
|
|
11
8
|
import { AbortError } from "./errors/abort-error.js";
|
|
12
9
|
import { isRedirect } from "./utils/is-redirect.js";
|
|
13
|
-
import { FormData } from "formdata
|
|
10
|
+
import { FormData } from "@gjsify/formdata";
|
|
14
11
|
import { isDomainOrSubdomain, isSameProtocol } from "./utils/is.js";
|
|
15
12
|
import { parseReferrerPolicyFromHeader } from "./utils/referrer.js";
|
|
16
13
|
import {
|
|
17
14
|
Blob,
|
|
18
|
-
File
|
|
19
|
-
fileFromSync,
|
|
20
|
-
fileFrom,
|
|
21
|
-
blobFromSync,
|
|
22
|
-
blobFrom
|
|
15
|
+
File
|
|
23
16
|
} from "./utils/blob-from.js";
|
|
24
|
-
import { URL } from "@gjsify/
|
|
17
|
+
import { URL } from "@gjsify/url";
|
|
25
18
|
const supportedSchemas = /* @__PURE__ */ new Set(["data:", "http:", "https:"]);
|
|
26
19
|
async function fetch(url, init = {}) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
}
|
|
20
|
+
const request = new Request(url, init);
|
|
21
|
+
const { parsedURL, options } = getSoupRequestOptions(request);
|
|
22
|
+
if (!supportedSchemas.has(parsedURL.protocol)) {
|
|
23
|
+
throw new TypeError(`@gjsify/fetch cannot load ${url}. URL scheme "${parsedURL.protocol.replace(/:$/, "")}" is not supported.`);
|
|
24
|
+
}
|
|
25
|
+
if (parsedURL.protocol === "data:") {
|
|
26
|
+
const { buffer, typeFull } = parseDataUri(request.url);
|
|
27
|
+
const response = new Response(Buffer.from(buffer), { headers: { "Content-Type": typeFull } });
|
|
28
|
+
return response;
|
|
29
|
+
}
|
|
30
|
+
const { signal } = request;
|
|
31
|
+
if (signal && signal.aborted) {
|
|
32
|
+
throw new AbortError("The operation was aborted.");
|
|
33
|
+
}
|
|
34
|
+
let readable;
|
|
35
|
+
let cancellable;
|
|
36
|
+
try {
|
|
37
|
+
const sendRes = await request._send(options);
|
|
38
|
+
readable = sendRes.readable;
|
|
39
|
+
cancellable = sendRes.cancellable;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
42
|
+
throw new FetchError(`request to ${request.url} failed, reason: ${err.message}`, "system", err);
|
|
43
|
+
}
|
|
44
|
+
const abortHandler = () => {
|
|
45
|
+
cancellable.cancel();
|
|
46
|
+
};
|
|
47
|
+
if (signal) {
|
|
48
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
49
|
+
}
|
|
50
|
+
const finalize = () => {
|
|
69
51
|
if (signal) {
|
|
70
|
-
signal.
|
|
52
|
+
signal.removeEventListener("abort", abortHandler);
|
|
71
53
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
54
|
+
};
|
|
55
|
+
cancellable.connect("cancelled", () => {
|
|
56
|
+
readable.destroy(new AbortError("The operation was aborted."));
|
|
57
|
+
});
|
|
58
|
+
readable.on("error", (error) => {
|
|
59
|
+
finalize();
|
|
60
|
+
});
|
|
61
|
+
const message = request._message;
|
|
62
|
+
const headers = Headers._newFromSoupMessage(message);
|
|
63
|
+
const statusCode = message.status_code;
|
|
64
|
+
const statusMessage = message.get_reason_phrase();
|
|
65
|
+
if (isRedirect(statusCode)) {
|
|
66
|
+
const location = headers.get("Location");
|
|
67
|
+
let locationURL = null;
|
|
68
|
+
try {
|
|
69
|
+
locationURL = location === null ? null : new URL(location, request.url);
|
|
70
|
+
} catch {
|
|
71
|
+
if (request.redirect !== "manual") {
|
|
72
|
+
finalize();
|
|
73
|
+
throw new FetchError(`uri requested responds with an invalid redirect URL: ${location}`, "invalid-redirect");
|
|
79
74
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
}
|
|
75
|
+
}
|
|
76
|
+
switch (request.redirect) {
|
|
77
|
+
case "error":
|
|
78
|
+
finalize();
|
|
79
|
+
throw new FetchError(`uri requested responds with a redirect, redirect mode is set to error: ${request.url}`, "no-redirect");
|
|
80
|
+
case "manual":
|
|
81
|
+
break;
|
|
82
|
+
case "follow": {
|
|
83
|
+
if (locationURL === null) {
|
|
84
|
+
break;
|
|
102
85
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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;
|
|
86
|
+
if (request.counter >= request.follow) {
|
|
87
|
+
finalize();
|
|
88
|
+
throw new FetchError(`maximum redirect reached at: ${request.url}`, "max-redirect");
|
|
89
|
+
}
|
|
90
|
+
const requestOptions = {
|
|
91
|
+
headers: new Headers(request.headers),
|
|
92
|
+
follow: request.follow,
|
|
93
|
+
counter: request.counter + 1,
|
|
94
|
+
agent: request.agent,
|
|
95
|
+
compress: request.compress,
|
|
96
|
+
method: request.method,
|
|
97
|
+
body: clone(request),
|
|
98
|
+
signal: request.signal,
|
|
99
|
+
size: request.size,
|
|
100
|
+
referrer: request.referrer,
|
|
101
|
+
referrerPolicy: request.referrerPolicy
|
|
102
|
+
};
|
|
103
|
+
if (!isDomainOrSubdomain(request.url, locationURL) || !isSameProtocol(request.url, locationURL)) {
|
|
104
|
+
for (const name of ["authorization", "www-authenticate", "cookie", "cookie2"]) {
|
|
105
|
+
requestOptions.headers.delete(name);
|
|
154
106
|
}
|
|
155
|
-
default:
|
|
156
|
-
return reject(new TypeError(`Redirect option '${request.redirect}' is not a valid value of RequestRedirect`));
|
|
157
107
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
reject(error);
|
|
108
|
+
if (statusCode !== 303 && request.body && init.body instanceof Stream.Readable) {
|
|
109
|
+
finalize();
|
|
110
|
+
throw new FetchError("Cannot follow redirect with body being a readable stream", "unsupported-redirect");
|
|
162
111
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
headers
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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;
|
|
112
|
+
if (statusCode === 303 || (statusCode === 301 || statusCode === 302) && request.method === "POST") {
|
|
113
|
+
requestOptions.method = "GET";
|
|
114
|
+
requestOptions.body = void 0;
|
|
115
|
+
requestOptions.headers.delete("content-length");
|
|
116
|
+
}
|
|
117
|
+
const responseReferrerPolicy = parseReferrerPolicyFromHeader(headers);
|
|
118
|
+
if (responseReferrerPolicy) {
|
|
119
|
+
requestOptions.referrerPolicy = responseReferrerPolicy;
|
|
120
|
+
}
|
|
121
|
+
finalize();
|
|
122
|
+
return fetch(new Request(locationURL, requestOptions));
|
|
192
123
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
124
|
+
default:
|
|
125
|
+
throw new TypeError(`Redirect option '${request.redirect}' is not a valid value of RequestRedirect`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const responseOptions = {
|
|
129
|
+
url: request.url,
|
|
130
|
+
status: statusCode,
|
|
131
|
+
statusText: statusMessage,
|
|
132
|
+
headers,
|
|
133
|
+
size: request.size,
|
|
134
|
+
counter: request.counter,
|
|
135
|
+
highWaterMark: request.highWaterMark
|
|
136
|
+
};
|
|
137
|
+
const codings = headers.get("Content-Encoding");
|
|
138
|
+
if (!request.compress || request.method === "HEAD" || codings === null || statusCode === 204 || statusCode === 304) {
|
|
139
|
+
finalize();
|
|
140
|
+
return new Response(readable, responseOptions);
|
|
141
|
+
}
|
|
142
|
+
if (typeof DecompressionStream !== "undefined") {
|
|
143
|
+
let format = null;
|
|
144
|
+
if (codings === "gzip" || codings === "x-gzip") {
|
|
145
|
+
format = "gzip";
|
|
146
|
+
} else if (codings === "deflate" || codings === "x-deflate") {
|
|
147
|
+
format = "deflate";
|
|
148
|
+
}
|
|
149
|
+
if (format) {
|
|
150
|
+
const webBody = new Response(readable, responseOptions).body;
|
|
151
|
+
if (webBody) {
|
|
152
|
+
const decompressed = webBody.pipeThrough(new DecompressionStream(format));
|
|
153
|
+
finalize();
|
|
154
|
+
return new Response(decompressed, responseOptions);
|
|
202
155
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
finalize();
|
|
159
|
+
return new Response(readable, responseOptions);
|
|
160
|
+
}
|
|
161
|
+
const _isGJS = typeof globalThis.imports !== "undefined";
|
|
162
|
+
if (_isGJS) {
|
|
163
|
+
globalThis.fetch = fetch;
|
|
164
|
+
globalThis.Headers = Headers;
|
|
165
|
+
globalThis.Request = Request;
|
|
166
|
+
globalThis.Response = Response;
|
|
208
167
|
}
|
|
209
168
|
export {
|
|
210
169
|
AbortError,
|
|
@@ -215,10 +174,6 @@ export {
|
|
|
215
174
|
Headers,
|
|
216
175
|
Request,
|
|
217
176
|
Response,
|
|
218
|
-
blobFrom,
|
|
219
|
-
blobFromSync,
|
|
220
177
|
fetch as default,
|
|
221
|
-
fileFrom,
|
|
222
|
-
fileFromSync,
|
|
223
178
|
isRedirect
|
|
224
179
|
};
|
package/lib/esm/request.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import "@girs/gjs";
|
|
2
|
-
import "@girs/gio-2.0";
|
|
3
1
|
import GLib from "@girs/glib-2.0";
|
|
4
2
|
import Soup from "@girs/soup-3.0";
|
|
5
3
|
import Gio from "@girs/gio-2.0";
|
|
6
|
-
import
|
|
7
|
-
import { URL } from "@gjsify/
|
|
4
|
+
import { soupSendAsync, inputStreamToReadable } from "./utils/soup-helpers.js";
|
|
5
|
+
import { URL } from "@gjsify/url";
|
|
8
6
|
import Headers from "./headers.js";
|
|
9
7
|
import Body, { clone, extractContentType, getTotalBytes } from "./body.js";
|
|
10
8
|
import { isAbortSignal } from "./utils/is.js";
|
|
@@ -13,7 +11,7 @@ import {
|
|
|
13
11
|
determineRequestsReferrer,
|
|
14
12
|
DEFAULT_REFERRER_POLICY
|
|
15
13
|
} from "./utils/referrer.js";
|
|
16
|
-
const INTERNALS = Symbol("Request internals");
|
|
14
|
+
const INTERNALS = /* @__PURE__ */ Symbol("Request internals");
|
|
17
15
|
const isRequest = (obj) => {
|
|
18
16
|
return typeof obj === "object" && typeof obj.url === "string";
|
|
19
17
|
};
|
|
@@ -99,43 +97,45 @@ class Request extends Body {
|
|
|
99
97
|
highWaterMark = 16384;
|
|
100
98
|
insecureHTTPParser = false;
|
|
101
99
|
constructor(input, init) {
|
|
100
|
+
const inputRL = input;
|
|
101
|
+
const initRL = init || {};
|
|
102
102
|
let parsedURL;
|
|
103
103
|
let requestObj = {};
|
|
104
104
|
if (isRequest(input)) {
|
|
105
|
-
parsedURL = new URL(
|
|
106
|
-
requestObj =
|
|
105
|
+
parsedURL = new URL(inputRL.url);
|
|
106
|
+
requestObj = inputRL;
|
|
107
107
|
} else {
|
|
108
108
|
parsedURL = new URL(input);
|
|
109
109
|
}
|
|
110
110
|
if (parsedURL.username !== "" || parsedURL.password !== "") {
|
|
111
111
|
throw new TypeError(`${parsedURL} is an url with embedded credentials.`);
|
|
112
112
|
}
|
|
113
|
-
let method =
|
|
113
|
+
let method = initRL.method || requestObj.method || "GET";
|
|
114
114
|
if (/^(delete|get|head|options|post|put)$/i.test(method)) {
|
|
115
115
|
method = method.toUpperCase();
|
|
116
116
|
}
|
|
117
|
-
if ((init
|
|
117
|
+
if ((init?.body != null || isRequest(input) && inputRL.body !== null) && (method === "GET" || method === "HEAD")) {
|
|
118
118
|
throw new TypeError("Request with GET/HEAD method cannot have body");
|
|
119
119
|
}
|
|
120
|
-
const inputBody = init
|
|
120
|
+
const inputBody = init?.body ? init.body : isRequest(input) && inputRL.body !== null ? clone(input) : null;
|
|
121
121
|
super(inputBody, {
|
|
122
|
-
size:
|
|
122
|
+
size: initRL.size || 0
|
|
123
123
|
});
|
|
124
|
-
const headers = new Headers(init
|
|
124
|
+
const headers = new Headers(init?.headers || inputRL.headers || {});
|
|
125
125
|
if (inputBody !== null && !headers.has("Content-Type")) {
|
|
126
126
|
const contentType = extractContentType(inputBody, this);
|
|
127
127
|
if (contentType) {
|
|
128
128
|
headers.set("Content-Type", contentType);
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
-
let signal = isRequest(input) ?
|
|
132
|
-
if ("signal" in init) {
|
|
131
|
+
let signal = isRequest(input) ? inputRL.signal : null;
|
|
132
|
+
if (init && "signal" in init) {
|
|
133
133
|
signal = init.signal;
|
|
134
134
|
}
|
|
135
135
|
if (signal != null && !isAbortSignal(signal)) {
|
|
136
136
|
throw new TypeError("Expected signal to be an instanceof AbortSignal or EventTarget");
|
|
137
137
|
}
|
|
138
|
-
let referrer = init
|
|
138
|
+
let referrer = init?.referrer == null ? inputRL.referrer : init.referrer;
|
|
139
139
|
if (referrer === "") {
|
|
140
140
|
referrer = "no-referrer";
|
|
141
141
|
} else if (referrer) {
|
|
@@ -144,14 +144,19 @@ class Request extends Body {
|
|
|
144
144
|
} else {
|
|
145
145
|
referrer = void 0;
|
|
146
146
|
}
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
const scheme = parsedURL.protocol;
|
|
148
|
+
let session = null;
|
|
149
|
+
let message = null;
|
|
150
|
+
if (scheme === "http:" || scheme === "https:") {
|
|
151
|
+
session = new Soup.Session();
|
|
152
|
+
message = new Soup.Message({
|
|
153
|
+
method,
|
|
154
|
+
uri: GLib.Uri.parse(parsedURL.toString(), GLib.UriFlags.NONE)
|
|
155
|
+
});
|
|
156
|
+
}
|
|
152
157
|
this[INTERNALS] = {
|
|
153
158
|
method,
|
|
154
|
-
redirect: init
|
|
159
|
+
redirect: init?.redirect || inputRL.redirect || "follow",
|
|
155
160
|
headers,
|
|
156
161
|
parsedURL,
|
|
157
162
|
signal,
|
|
@@ -160,24 +165,26 @@ class Request extends Body {
|
|
|
160
165
|
session,
|
|
161
166
|
message
|
|
162
167
|
};
|
|
163
|
-
this.follow =
|
|
164
|
-
this.compress =
|
|
165
|
-
this.counter =
|
|
166
|
-
this.agent =
|
|
167
|
-
this.highWaterMark =
|
|
168
|
-
this.insecureHTTPParser =
|
|
169
|
-
this.referrerPolicy = init
|
|
168
|
+
this.follow = initRL.follow === void 0 ? inputRL.follow === void 0 ? 20 : inputRL.follow : initRL.follow;
|
|
169
|
+
this.compress = initRL.compress === void 0 ? inputRL.compress === void 0 ? true : inputRL.compress : initRL.compress;
|
|
170
|
+
this.counter = initRL.counter || inputRL.counter || 0;
|
|
171
|
+
this.agent = initRL.agent || inputRL.agent;
|
|
172
|
+
this.highWaterMark = initRL.highWaterMark || inputRL.highWaterMark || 16384;
|
|
173
|
+
this.insecureHTTPParser = initRL.insecureHTTPParser || inputRL.insecureHTTPParser || false;
|
|
174
|
+
this.referrerPolicy = init?.referrerPolicy || inputRL.referrerPolicy || "";
|
|
170
175
|
}
|
|
171
176
|
/**
|
|
172
|
-
*
|
|
173
|
-
* @param options
|
|
174
|
-
* @returns
|
|
177
|
+
* Send the request using Soup.
|
|
175
178
|
*/
|
|
176
179
|
async _send(options) {
|
|
177
|
-
|
|
180
|
+
const { session, message } = this[INTERNALS];
|
|
181
|
+
if (!session || !message) {
|
|
182
|
+
throw new Error("Cannot send request: no Soup session (non-HTTP URL?)");
|
|
183
|
+
}
|
|
184
|
+
options.headers._appendToSoupMessage(message);
|
|
178
185
|
const cancellable = new Gio.Cancellable();
|
|
179
|
-
this[INTERNALS].inputStream = await
|
|
180
|
-
this[INTERNALS].readable = this[INTERNALS].inputStream
|
|
186
|
+
this[INTERNALS].inputStream = await soupSendAsync(session, message, GLib.PRIORITY_DEFAULT, cancellable);
|
|
187
|
+
this[INTERNALS].readable = inputStreamToReadable(this[INTERNALS].inputStream);
|
|
181
188
|
return {
|
|
182
189
|
inputStream: this[INTERNALS].inputStream,
|
|
183
190
|
readable: this[INTERNALS].readable,
|
|
@@ -248,7 +255,7 @@ const getSoupRequestOptions = (request) => {
|
|
|
248
255
|
headers.set("Referer", request.referrer);
|
|
249
256
|
}
|
|
250
257
|
if (!headers.has("User-Agent")) {
|
|
251
|
-
headers.set("User-Agent", "
|
|
258
|
+
headers.set("User-Agent", "gjsify-fetch");
|
|
252
259
|
}
|
|
253
260
|
if (request.compress && !headers.has("Accept-Encoding")) {
|
|
254
261
|
headers.set("Accept-Encoding", "gzip, deflate, br");
|
|
@@ -261,13 +268,7 @@ const getSoupRequestOptions = (request) => {
|
|
|
261
268
|
headers.set("Connection", "close");
|
|
262
269
|
}
|
|
263
270
|
const options = {
|
|
264
|
-
// Overwrite search to retain trailing ? (issue #776)
|
|
265
|
-
// path: parsedURL.pathname + search,
|
|
266
|
-
// The following options are not expressed in the URL
|
|
267
|
-
// method: request.method,
|
|
268
271
|
headers
|
|
269
|
-
// insecureHTTPParser: request.insecureHTTPParser,
|
|
270
|
-
// agent
|
|
271
272
|
};
|
|
272
273
|
return {
|
|
273
274
|
parsedURL,
|
package/lib/esm/response.js
CHANGED
|
@@ -3,8 +3,8 @@ import Gio from "@girs/gio-2.0";
|
|
|
3
3
|
import Headers from "./headers.js";
|
|
4
4
|
import Body, { clone, extractContentType } from "./body.js";
|
|
5
5
|
import { isRedirect } from "./utils/is-redirect.js";
|
|
6
|
-
import { URL } from "@gjsify/
|
|
7
|
-
const INTERNALS = Symbol("Response internals");
|
|
6
|
+
import { URL } from "@gjsify/url";
|
|
7
|
+
const INTERNALS = /* @__PURE__ */ Symbol("Response internals");
|
|
8
8
|
class Response extends Body {
|
|
9
9
|
[INTERNALS];
|
|
10
10
|
_inputStream = null;
|
|
@@ -60,7 +60,6 @@ class Response extends Body {
|
|
|
60
60
|
*
|
|
61
61
|
* @return Response
|
|
62
62
|
*/
|
|
63
|
-
// @ts-ignore
|
|
64
63
|
clone() {
|
|
65
64
|
return new Response(clone(this, this.highWaterMark), {
|
|
66
65
|
type: this.type,
|
|
@@ -95,6 +94,22 @@ class Response extends Body {
|
|
|
95
94
|
response[INTERNALS].type = "error";
|
|
96
95
|
return response;
|
|
97
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Create a Response with a JSON body.
|
|
99
|
+
* @param data The data to serialize as JSON.
|
|
100
|
+
* @param init Optional response init options.
|
|
101
|
+
* @returns A Response with the JSON body and appropriate content-type header.
|
|
102
|
+
*/
|
|
103
|
+
static json(data, init) {
|
|
104
|
+
const body = JSON.stringify(data);
|
|
105
|
+
const options = { ...init };
|
|
106
|
+
const headers = new Headers(options.headers);
|
|
107
|
+
if (!headers.has("content-type")) {
|
|
108
|
+
headers.set("content-type", "application/json");
|
|
109
|
+
}
|
|
110
|
+
options.headers = headers;
|
|
111
|
+
return new Response(body, options);
|
|
112
|
+
}
|
|
98
113
|
get [Symbol.toStringTag]() {
|
|
99
114
|
return "Response";
|
|
100
115
|
}
|
|
@@ -104,7 +119,7 @@ class Response extends Body {
|
|
|
104
119
|
}
|
|
105
120
|
const outputStream = Gio.MemoryOutputStream.new_resizable();
|
|
106
121
|
await new Promise((resolve, reject) => {
|
|
107
|
-
outputStream.splice_async(this._inputStream, Gio.OutputStreamSpliceFlags.CLOSE_TARGET | Gio.OutputStreamSpliceFlags.CLOSE_SOURCE, GLib.PRIORITY_DEFAULT, null, (
|
|
122
|
+
outputStream.splice_async(this._inputStream, Gio.OutputStreamSpliceFlags.CLOSE_TARGET | Gio.OutputStreamSpliceFlags.CLOSE_SOURCE, GLib.PRIORITY_DEFAULT, null, (_self, res) => {
|
|
108
123
|
try {
|
|
109
124
|
resolve(outputStream.splice_finish(res));
|
|
110
125
|
} catch (error) {
|