@gjsify/fetch 0.3.15 → 0.3.17
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/lib/esm/body.js +1 -349
- package/lib/esm/errors/abort-error.js +1 -14
- package/lib/esm/errors/base.js +1 -20
- package/lib/esm/errors/fetch-error.js +1 -26
- package/lib/esm/headers.js +1 -193
- package/lib/esm/index.js +1 -232
- package/lib/esm/register/fetch.js +1 -20
- package/lib/esm/register/xhr.js +1 -11
- package/lib/esm/register.js +1 -2
- package/lib/esm/request.js +1 -287
- package/lib/esm/response.js +1 -157
- package/lib/esm/types/index.js +1 -1
- package/lib/esm/types/system-error.js +0 -4
- package/lib/esm/utils/blob-from.js +1 -3
- package/lib/esm/utils/data-uri.js +1 -33
- package/lib/esm/utils/get-search.js +1 -12
- package/lib/esm/utils/is-redirect.js +1 -20
- package/lib/esm/utils/is.js +1 -64
- package/lib/esm/utils/multipart-parser.js +2 -352
- package/lib/esm/utils/referrer.js +1 -205
- package/lib/esm/utils/soup-helpers.js +1 -29
- package/lib/esm/xhr.js +2 -246
- package/package.json +12 -12
- package/tsconfig.tsbuildinfo +1 -1
package/lib/esm/index.js
CHANGED
|
@@ -1,232 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { FetchError } from "./errors/fetch-error.js";
|
|
3
|
-
import { isDomainOrSubdomain, isSameProtocol } from "./utils/is.js";
|
|
4
|
-
import { clone } from "./body.js";
|
|
5
|
-
import Headers from "./headers.js";
|
|
6
|
-
import { parseDataUri } from "./utils/data-uri.js";
|
|
7
|
-
import { isRedirect } from "./utils/is-redirect.js";
|
|
8
|
-
import { Response } from "./response.js";
|
|
9
|
-
import { parseReferrerPolicyFromHeader } from "./utils/referrer.js";
|
|
10
|
-
import { Request, getSoupRequestOptions } from "./request.js";
|
|
11
|
-
import { AbortError } from "./errors/abort-error.js";
|
|
12
|
-
import { XMLHttpRequest, XMLHttpRequestUpload } from "./xhr.js";
|
|
13
|
-
import { URL } from "@gjsify/url";
|
|
14
|
-
import Stream from "node:stream";
|
|
15
|
-
import { FormData } from "@gjsify/formdata";
|
|
16
|
-
import GLib from "@girs/glib-2.0";
|
|
17
|
-
|
|
18
|
-
//#region src/index.ts
|
|
19
|
-
const supportedSchemas = new Set([
|
|
20
|
-
"data:",
|
|
21
|
-
"http:",
|
|
22
|
-
"https:",
|
|
23
|
-
"file:"
|
|
24
|
-
]);
|
|
25
|
-
/**
|
|
26
|
-
* Rewrite root-relative URLs (e.g. `/res/images/foo.png`) to `file://` relative
|
|
27
|
-
* to the program directory. In a browser these would resolve against the page
|
|
28
|
-
* origin; in GJS there is no origin, so we map them to the running bundle's
|
|
29
|
-
* directory. This lets apps use the same asset paths across browser and GJS.
|
|
30
|
-
*/
|
|
31
|
-
/**
|
|
32
|
-
* Rewrite root-relative URLs (e.g. `/res/images/foo.png`) to `file://` relative
|
|
33
|
-
* to the program directory. This lets GJS apps load bundled assets using the
|
|
34
|
-
* same paths as in the browser. The security implications (arbitrary file
|
|
35
|
-
* reads via fetch) are acceptable for the current use cases — revisit if
|
|
36
|
-
* @gjsify/fetch is ever used to handle untrusted input.
|
|
37
|
-
*/
|
|
38
|
-
function rewriteRootRelativeUrl(input) {
|
|
39
|
-
if (typeof input !== "string") return input;
|
|
40
|
-
if (!input.startsWith("/") || input.startsWith("//")) return input;
|
|
41
|
-
const DEBUG = globalThis.__GJSIFY_DEBUG_FETCH === true;
|
|
42
|
-
try {
|
|
43
|
-
const imports = globalThis.imports;
|
|
44
|
-
const programPath = imports?.system?.programPath ?? imports?.system?.programInvocationName ?? "";
|
|
45
|
-
if (!programPath) return input;
|
|
46
|
-
const dir = GLib.path_get_dirname(programPath);
|
|
47
|
-
const rewritten = `file://${dir}${input}`;
|
|
48
|
-
if (DEBUG) console.log(`[fetch] rewrite ${input} → ${rewritten}`);
|
|
49
|
-
return rewritten;
|
|
50
|
-
} catch (err) {
|
|
51
|
-
if (DEBUG) console.warn(`[fetch] rewrite FAILED: ${err?.message ?? err}`);
|
|
52
|
-
return input;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Fetch function
|
|
57
|
-
*
|
|
58
|
-
* @param url Absolute url or Request instance
|
|
59
|
-
* @param init Fetch options
|
|
60
|
-
*/
|
|
61
|
-
async function fetch(url, init = {}) {
|
|
62
|
-
url = rewriteRootRelativeUrl(url);
|
|
63
|
-
const request = new Request(url, init);
|
|
64
|
-
const { parsedURL, options } = getSoupRequestOptions(request);
|
|
65
|
-
if (!supportedSchemas.has(parsedURL.protocol)) {
|
|
66
|
-
throw new TypeError(`@gjsify/fetch cannot load ${url}. URL scheme "${parsedURL.protocol.replace(/:$/, "")}" is not supported.`);
|
|
67
|
-
}
|
|
68
|
-
if (parsedURL.protocol === "data:") {
|
|
69
|
-
const { buffer, typeFull } = parseDataUri(request.url);
|
|
70
|
-
const response = new Response(Buffer.from(buffer), { headers: { "Content-Type": typeFull } });
|
|
71
|
-
return response;
|
|
72
|
-
}
|
|
73
|
-
if (parsedURL.protocol === "file:") {
|
|
74
|
-
const DEBUG = globalThis.__GJSIFY_DEBUG_FETCH === true;
|
|
75
|
-
if (DEBUG) console.log(`[fetch] file:// ${request.url}`);
|
|
76
|
-
try {
|
|
77
|
-
const path = GLib.filename_from_uri(request.url)[0];
|
|
78
|
-
if (DEBUG) console.log(`[fetch] file:// path=${path}`);
|
|
79
|
-
const [ok, contents] = GLib.file_get_contents(path);
|
|
80
|
-
if (DEBUG) console.log(`[fetch] file:// ok=${ok} bytes=${contents?.byteLength ?? "?"}`);
|
|
81
|
-
if (!ok) {
|
|
82
|
-
throw new FetchError(`Failed to read file: ${path}`, "system");
|
|
83
|
-
}
|
|
84
|
-
const bytes = contents;
|
|
85
|
-
const body = new Uint8Array(bytes.byteLength);
|
|
86
|
-
body.set(bytes);
|
|
87
|
-
const resp = new Response(body);
|
|
88
|
-
if (DEBUG) console.log(`[fetch] file:// response created`);
|
|
89
|
-
return resp;
|
|
90
|
-
} catch (error) {
|
|
91
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
92
|
-
if (DEBUG) console.warn(`[fetch] file:// FAIL: ${err.message}`);
|
|
93
|
-
throw new FetchError(`request to ${request.url} failed, reason: ${err.message}`, "system", err);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
const { signal } = request;
|
|
97
|
-
if (signal && signal.aborted) {
|
|
98
|
-
throw new AbortError("The operation was aborted.");
|
|
99
|
-
}
|
|
100
|
-
let readable;
|
|
101
|
-
let cancellable;
|
|
102
|
-
try {
|
|
103
|
-
const sendRes = await request._send(options);
|
|
104
|
-
readable = sendRes.readable;
|
|
105
|
-
cancellable = sendRes.cancellable;
|
|
106
|
-
} catch (error) {
|
|
107
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
108
|
-
throw new FetchError(`request to ${request.url} failed, reason: ${err.message}`, "system", err);
|
|
109
|
-
}
|
|
110
|
-
const abortHandler = () => {
|
|
111
|
-
cancellable.cancel();
|
|
112
|
-
};
|
|
113
|
-
if (signal) {
|
|
114
|
-
signal.addEventListener("abort", abortHandler, { once: true });
|
|
115
|
-
}
|
|
116
|
-
const finalize = () => {
|
|
117
|
-
if (signal) {
|
|
118
|
-
signal.removeEventListener("abort", abortHandler);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
cancellable.connect(() => {
|
|
122
|
-
readable.destroy(new AbortError("The operation was aborted."));
|
|
123
|
-
});
|
|
124
|
-
readable.on("error", (error) => {
|
|
125
|
-
finalize();
|
|
126
|
-
});
|
|
127
|
-
const message = request._message;
|
|
128
|
-
const headers = Headers._newFromSoupMessage(message);
|
|
129
|
-
const statusCode = message.status_code;
|
|
130
|
-
const statusMessage = message.get_reason_phrase();
|
|
131
|
-
if (isRedirect(statusCode)) {
|
|
132
|
-
const location = headers.get("Location");
|
|
133
|
-
let locationURL = null;
|
|
134
|
-
try {
|
|
135
|
-
locationURL = location === null ? null : new URL(location, request.url);
|
|
136
|
-
} catch {
|
|
137
|
-
if (request.redirect !== "manual") {
|
|
138
|
-
finalize();
|
|
139
|
-
throw new FetchError(`uri requested responds with an invalid redirect URL: ${location}`, "invalid-redirect");
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
switch (request.redirect) {
|
|
143
|
-
case "error":
|
|
144
|
-
finalize();
|
|
145
|
-
throw new FetchError(`uri requested responds with a redirect, redirect mode is set to error: ${request.url}`, "no-redirect");
|
|
146
|
-
case "manual": break;
|
|
147
|
-
case "follow": {
|
|
148
|
-
if (locationURL === null) {
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
if (request.counter >= request.follow) {
|
|
152
|
-
finalize();
|
|
153
|
-
throw new FetchError(`maximum redirect reached at: ${request.url}`, "max-redirect");
|
|
154
|
-
}
|
|
155
|
-
const requestOptions = {
|
|
156
|
-
headers: new Headers(request.headers),
|
|
157
|
-
follow: request.follow,
|
|
158
|
-
counter: request.counter + 1,
|
|
159
|
-
agent: request.agent,
|
|
160
|
-
compress: request.compress,
|
|
161
|
-
method: request.method,
|
|
162
|
-
body: clone(request),
|
|
163
|
-
signal: request.signal,
|
|
164
|
-
size: request.size,
|
|
165
|
-
referrer: request.referrer,
|
|
166
|
-
referrerPolicy: request.referrerPolicy
|
|
167
|
-
};
|
|
168
|
-
if (!isDomainOrSubdomain(request.url, locationURL) || !isSameProtocol(request.url, locationURL)) {
|
|
169
|
-
for (const name of [
|
|
170
|
-
"authorization",
|
|
171
|
-
"www-authenticate",
|
|
172
|
-
"cookie",
|
|
173
|
-
"cookie2"
|
|
174
|
-
]) {
|
|
175
|
-
requestOptions.headers.delete(name);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (statusCode !== 303 && request.body && init.body instanceof Stream.Readable) {
|
|
179
|
-
finalize();
|
|
180
|
-
throw new FetchError("Cannot follow redirect with body being a readable stream", "unsupported-redirect");
|
|
181
|
-
}
|
|
182
|
-
if (statusCode === 303 || (statusCode === 301 || statusCode === 302) && request.method === "POST") {
|
|
183
|
-
requestOptions.method = "GET";
|
|
184
|
-
requestOptions.body = undefined;
|
|
185
|
-
requestOptions.headers.delete("content-length");
|
|
186
|
-
}
|
|
187
|
-
const responseReferrerPolicy = parseReferrerPolicyFromHeader(headers);
|
|
188
|
-
if (responseReferrerPolicy) {
|
|
189
|
-
requestOptions.referrerPolicy = responseReferrerPolicy;
|
|
190
|
-
}
|
|
191
|
-
finalize();
|
|
192
|
-
return fetch(new Request(locationURL, requestOptions));
|
|
193
|
-
}
|
|
194
|
-
default: throw new TypeError(`Redirect option '${request.redirect}' is not a valid value of RequestRedirect`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
const responseOptions = {
|
|
198
|
-
url: request.url,
|
|
199
|
-
status: statusCode,
|
|
200
|
-
statusText: statusMessage,
|
|
201
|
-
headers,
|
|
202
|
-
size: request.size,
|
|
203
|
-
counter: request.counter,
|
|
204
|
-
highWaterMark: request.highWaterMark
|
|
205
|
-
};
|
|
206
|
-
const codings = headers.get("Content-Encoding");
|
|
207
|
-
if (!request.compress || request.method === "HEAD" || codings === null || statusCode === 204 || statusCode === 304) {
|
|
208
|
-
finalize();
|
|
209
|
-
return new Response(readable, responseOptions);
|
|
210
|
-
}
|
|
211
|
-
if (typeof DecompressionStream !== "undefined") {
|
|
212
|
-
let format = null;
|
|
213
|
-
if (codings === "gzip" || codings === "x-gzip") {
|
|
214
|
-
format = "gzip";
|
|
215
|
-
} else if (codings === "deflate" || codings === "x-deflate") {
|
|
216
|
-
format = "deflate";
|
|
217
|
-
}
|
|
218
|
-
if (format) {
|
|
219
|
-
const webBody = new Response(readable, responseOptions).body;
|
|
220
|
-
if (webBody) {
|
|
221
|
-
const decompressed = webBody.pipeThrough(new DecompressionStream(format));
|
|
222
|
-
finalize();
|
|
223
|
-
return new Response(decompressed, responseOptions);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
finalize();
|
|
228
|
-
return new Response(readable, responseOptions);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
//#endregion
|
|
232
|
-
export { AbortError, Blob, FetchError, File, FormData, Headers, Request, Response, XMLHttpRequest, XMLHttpRequestUpload, fetch as default, isRedirect };
|
|
1
|
+
import{Blob as e,File as t}from"./utils/blob-from.js";import{FetchError as n}from"./errors/fetch-error.js";import{isDomainOrSubdomain as r,isSameProtocol as i}from"./utils/is.js";import{clone as a}from"./body.js";import o from"./headers.js";import{parseDataUri as s}from"./utils/data-uri.js";import{isRedirect as c}from"./utils/is-redirect.js";import{Response as l}from"./response.js";import{parseReferrerPolicyFromHeader as u}from"./utils/referrer.js";import{Request as d,getSoupRequestOptions as f}from"./request.js";import{AbortError as p}from"./errors/abort-error.js";import{XMLHttpRequest as m,XMLHttpRequestUpload as h}from"./xhr.js";import{URL as g}from"@gjsify/url";import _ from"node:stream";import{FormData as v}from"@gjsify/formdata";import y from"@girs/glib-2.0";const b=new Set([`data:`,`http:`,`https:`,`file:`]);function x(e){if(typeof e!=`string`||!e.startsWith(`/`)||e.startsWith(`//`))return e;let t=globalThis.__GJSIFY_DEBUG_FETCH===!0;try{let n=globalThis.imports,r=n?.system?.programPath??n?.system?.programInvocationName??``;if(!r)return e;let i=`file://${y.path_get_dirname(r)}${e}`;return t&&console.log(`[fetch] rewrite ${e} → ${i}`),i}catch(n){return t&&console.warn(`[fetch] rewrite FAILED: ${n?.message??n}`),e}}async function S(e,t={}){e=x(e);let m=new d(e,t),{parsedURL:h,options:v}=f(m);if(!b.has(h.protocol))throw TypeError(`@gjsify/fetch cannot load ${e}. URL scheme "${h.protocol.replace(/:$/,``)}" is not supported.`);if(h.protocol===`data:`){let{buffer:e,typeFull:t}=s(m.url);return new l(Buffer.from(e),{headers:{"Content-Type":t}})}if(h.protocol===`file:`){let e=globalThis.__GJSIFY_DEBUG_FETCH===!0;e&&console.log(`[fetch] file:// ${m.url}`);try{let t=y.filename_from_uri(m.url)[0];e&&console.log(`[fetch] file:// path=${t}`);let[r,i]=y.file_get_contents(t);if(e&&console.log(`[fetch] file:// ok=${r} bytes=${i?.byteLength??`?`}`),!r)throw new n(`Failed to read file: ${t}`,`system`);let a=i,o=new Uint8Array(a.byteLength);o.set(a);let s=new l(o);return e&&console.log(`[fetch] file:// response created`),s}catch(t){let r=t instanceof Error?t:Error(String(t));throw e&&console.warn(`[fetch] file:// FAIL: ${r.message}`),new n(`request to ${m.url} failed, reason: ${r.message}`,`system`,r)}}let{signal:C}=m;if(C&&C.aborted)throw new p(`The operation was aborted.`);let w,T;try{let e=await m._send(v);w=e.readable,T=e.cancellable}catch(e){let t=e instanceof Error?e:Error(String(e));throw new n(`request to ${m.url} failed, reason: ${t.message}`,`system`,t)}let E=()=>{T.cancel()};C&&C.addEventListener(`abort`,E,{once:!0});let D=()=>{C&&C.removeEventListener(`abort`,E)};T.connect(()=>{w.destroy(new p(`The operation was aborted.`))}),w.on(`error`,e=>{D()});let O=m._message,k=o._newFromSoupMessage(O),A=O.status_code,j=O.get_reason_phrase();if(c(A)){let e=k.get(`Location`),s=null;try{s=e===null?null:new g(e,m.url)}catch{if(m.redirect!==`manual`)throw D(),new n(`uri requested responds with an invalid redirect URL: ${e}`,`invalid-redirect`)}switch(m.redirect){case`error`:throw D(),new n(`uri requested responds with a redirect, redirect mode is set to error: ${m.url}`,`no-redirect`);case`manual`:break;case`follow`:{if(s===null)break;if(m.counter>=m.follow)throw D(),new n(`maximum redirect reached at: ${m.url}`,`max-redirect`);let e={headers:new o(m.headers),follow:m.follow,counter:m.counter+1,agent:m.agent,compress:m.compress,method:m.method,body:a(m),signal:m.signal,size:m.size,referrer:m.referrer,referrerPolicy:m.referrerPolicy};if(!r(m.url,s)||!i(m.url,s))for(let t of[`authorization`,`www-authenticate`,`cookie`,`cookie2`])e.headers.delete(t);if(A!==303&&m.body&&t.body instanceof _.Readable)throw D(),new n(`Cannot follow redirect with body being a readable stream`,`unsupported-redirect`);(A===303||(A===301||A===302)&&m.method===`POST`)&&(e.method=`GET`,e.body=void 0,e.headers.delete(`content-length`));let c=u(k);return c&&(e.referrerPolicy=c),D(),S(new d(s,e))}default:throw TypeError(`Redirect option '${m.redirect}' is not a valid value of RequestRedirect`)}}let M={url:m.url,status:A,statusText:j,headers:k,size:m.size,counter:m.counter,highWaterMark:m.highWaterMark},N=k.get(`Content-Encoding`);if(!m.compress||m.method===`HEAD`||N===null||A===204||A===304)return D(),new l(w,M);if(typeof DecompressionStream<`u`){let e=null;if(N===`gzip`||N===`x-gzip`?e=`gzip`:(N===`deflate`||N===`x-deflate`)&&(e=`deflate`),e){let t=new l(w,M).body;if(t){let n=t.pipeThrough(new DecompressionStream(e));return D(),new l(n,M)}}}return D(),new l(w,M)}export{p as AbortError,e as Blob,n as FetchError,t as File,v as FormData,o as Headers,d as Request,l as Response,m as XMLHttpRequest,h as XMLHttpRequestUpload,S as default,c as isRedirect};
|
|
@@ -1,20 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Response } from "../response.js";
|
|
3
|
-
import { Request } from "../request.js";
|
|
4
|
-
import fetch from "../index.js";
|
|
5
|
-
|
|
6
|
-
//#region src/register/fetch.ts
|
|
7
|
-
if (typeof globalThis.fetch === "undefined") {
|
|
8
|
-
globalThis.fetch = fetch;
|
|
9
|
-
}
|
|
10
|
-
if (typeof globalThis.Headers === "undefined") {
|
|
11
|
-
globalThis.Headers = Headers;
|
|
12
|
-
}
|
|
13
|
-
if (typeof globalThis.Request === "undefined") {
|
|
14
|
-
globalThis.Request = Request;
|
|
15
|
-
}
|
|
16
|
-
if (typeof globalThis.Response === "undefined") {
|
|
17
|
-
globalThis.Response = Response;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
//#endregion
|
|
1
|
+
import e from"../headers.js";import{Response as t}from"../response.js";import{Request as n}from"../request.js";import r from"../index.js";globalThis.fetch===void 0&&(globalThis.fetch=r),globalThis.Headers===void 0&&(globalThis.Headers=e),globalThis.Request===void 0&&(globalThis.Request=n),globalThis.Response===void 0&&(globalThis.Response=t);
|
package/lib/esm/register/xhr.js
CHANGED
|
@@ -1,11 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
//#region src/register/xhr.ts
|
|
4
|
-
if (typeof globalThis.XMLHttpRequest === "undefined") {
|
|
5
|
-
globalThis.XMLHttpRequest = XMLHttpRequest;
|
|
6
|
-
}
|
|
7
|
-
if (typeof globalThis.XMLHttpRequestUpload === "undefined") {
|
|
8
|
-
globalThis.XMLHttpRequestUpload = XMLHttpRequestUpload;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
//#endregion
|
|
1
|
+
import{XMLHttpRequest as e,XMLHttpRequestUpload as t}from"../xhr.js";globalThis.XMLHttpRequest===void 0&&(globalThis.XMLHttpRequest=e),globalThis.XMLHttpRequestUpload===void 0&&(globalThis.XMLHttpRequestUpload=t);
|
package/lib/esm/register.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "./register/xhr.js";
|
|
1
|
+
import"./register/fetch.js";import"./register/xhr.js";
|
package/lib/esm/request.js
CHANGED
|
@@ -1,287 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import Body, { clone, extractContentType, getTotalBytes } from "./body.js";
|
|
3
|
-
import Headers from "./headers.js";
|
|
4
|
-
import { inputStreamToReadable, soupSendAsync } from "./utils/soup-helpers.js";
|
|
5
|
-
import { DEFAULT_REFERRER_POLICY, determineRequestsReferrer, validateReferrerPolicy } from "./utils/referrer.js";
|
|
6
|
-
import { URL } from "@gjsify/url";
|
|
7
|
-
import Soup from "@girs/soup-3.0";
|
|
8
|
-
import GLib from "@girs/glib-2.0";
|
|
9
|
-
import Gio from "@girs/gio-2.0";
|
|
10
|
-
|
|
11
|
-
//#region src/request.ts
|
|
12
|
-
const INTERNALS = Symbol("Request internals");
|
|
13
|
-
/**
|
|
14
|
-
* Check if `obj` is an instance of Request.
|
|
15
|
-
*/
|
|
16
|
-
const isRequest = (obj) => {
|
|
17
|
-
return typeof obj === "object" && typeof obj.url === "string";
|
|
18
|
-
};
|
|
19
|
-
/** This Fetch API interface represents a resource request. */
|
|
20
|
-
var Request = class Request extends Body {
|
|
21
|
-
/** Returns the cache mode associated with request, which is a string indicating how the request will interact with the browser's cache when fetching. */
|
|
22
|
-
cache;
|
|
23
|
-
/** Returns the credentials mode associated with request, which is a string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. */
|
|
24
|
-
credentials;
|
|
25
|
-
/** Returns the kind of resource requested by request, e.g., "document" or "script". */
|
|
26
|
-
destination;
|
|
27
|
-
/** Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header. */
|
|
28
|
-
get headers() {
|
|
29
|
-
return this[INTERNALS].headers;
|
|
30
|
-
}
|
|
31
|
-
/** Returns request's subresource integrity metadata, which is a cryptographic hash of the resource being fetched. Its value consists of multiple hashes separated by whitespace. [SRI] */
|
|
32
|
-
integrity;
|
|
33
|
-
/** Returns a boolean indicating whether or not request can outlive the global in which it was created. */
|
|
34
|
-
keepalive;
|
|
35
|
-
/** Returns request's HTTP method, which is "GET" by default. */
|
|
36
|
-
get method() {
|
|
37
|
-
return this[INTERNALS].method;
|
|
38
|
-
}
|
|
39
|
-
/** Returns the mode associated with request, which is a string indicating whether the request will use CORS, or will be restricted to same-origin URLs. */
|
|
40
|
-
mode;
|
|
41
|
-
/** Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default. */
|
|
42
|
-
get redirect() {
|
|
43
|
-
return this[INTERNALS].redirect;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Returns the referrer of request.
|
|
47
|
-
* Its value can be a same-origin URL if explicitly set in init, the empty string to indicate no referrer, and "about:client" when defaulting to the global's default.
|
|
48
|
-
* This is used during fetching to determine the value of the `Referer` header of the request being made.
|
|
49
|
-
* @see https://fetch.spec.whatwg.org/#dom-request-referrer
|
|
50
|
-
**/
|
|
51
|
-
get referrer() {
|
|
52
|
-
if (this[INTERNALS].referrer === "no-referrer") {
|
|
53
|
-
return "";
|
|
54
|
-
}
|
|
55
|
-
if (this[INTERNALS].referrer === "client") {
|
|
56
|
-
return "about:client";
|
|
57
|
-
}
|
|
58
|
-
if (this[INTERNALS].referrer) {
|
|
59
|
-
return this[INTERNALS].referrer.toString();
|
|
60
|
-
}
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
/** Returns the referrer policy associated with request. This is used during fetching to compute the value of the request's referrer. */
|
|
64
|
-
get referrerPolicy() {
|
|
65
|
-
return this[INTERNALS].referrerPolicy;
|
|
66
|
-
}
|
|
67
|
-
set referrerPolicy(referrerPolicy) {
|
|
68
|
-
this[INTERNALS].referrerPolicy = validateReferrerPolicy(referrerPolicy);
|
|
69
|
-
}
|
|
70
|
-
/** Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. */
|
|
71
|
-
get signal() {
|
|
72
|
-
return this[INTERNALS].signal;
|
|
73
|
-
}
|
|
74
|
-
/** Returns the URL of request as a string. */
|
|
75
|
-
get url() {
|
|
76
|
-
return this[INTERNALS].parsedURL.toString();
|
|
77
|
-
}
|
|
78
|
-
get _uri() {
|
|
79
|
-
return GLib.Uri.parse(this.url, GLib.UriFlags.NONE);
|
|
80
|
-
}
|
|
81
|
-
get _session() {
|
|
82
|
-
return this[INTERNALS].session;
|
|
83
|
-
}
|
|
84
|
-
get _message() {
|
|
85
|
-
return this[INTERNALS].message;
|
|
86
|
-
}
|
|
87
|
-
get _inputStream() {
|
|
88
|
-
return this[INTERNALS].inputStream;
|
|
89
|
-
}
|
|
90
|
-
get [Symbol.toStringTag]() {
|
|
91
|
-
return "Request";
|
|
92
|
-
}
|
|
93
|
-
[INTERNALS];
|
|
94
|
-
follow;
|
|
95
|
-
compress = false;
|
|
96
|
-
counter = 0;
|
|
97
|
-
agent = "";
|
|
98
|
-
highWaterMark = 16384;
|
|
99
|
-
insecureHTTPParser = false;
|
|
100
|
-
constructor(input, init) {
|
|
101
|
-
const inputRL = input;
|
|
102
|
-
const initRL = init || {};
|
|
103
|
-
let parsedURL;
|
|
104
|
-
let requestObj = {};
|
|
105
|
-
if (isRequest(input)) {
|
|
106
|
-
parsedURL = new URL(inputRL.url);
|
|
107
|
-
requestObj = inputRL;
|
|
108
|
-
} else {
|
|
109
|
-
parsedURL = new URL(input);
|
|
110
|
-
}
|
|
111
|
-
if (parsedURL.username !== "" || parsedURL.password !== "") {
|
|
112
|
-
throw new TypeError(`${parsedURL} is an url with embedded credentials.`);
|
|
113
|
-
}
|
|
114
|
-
let method = initRL.method || requestObj.method || "GET";
|
|
115
|
-
if (/^(delete|get|head|options|post|put)$/i.test(method)) {
|
|
116
|
-
method = method.toUpperCase();
|
|
117
|
-
}
|
|
118
|
-
if ((init?.body != null || isRequest(input) && inputRL.body !== null) && (method === "GET" || method === "HEAD")) {
|
|
119
|
-
throw new TypeError("Request with GET/HEAD method cannot have body");
|
|
120
|
-
}
|
|
121
|
-
const inputBody = init?.body ? init.body : isRequest(input) && inputRL.body !== null ? clone(input) : null;
|
|
122
|
-
super(inputBody, { size: initRL.size || 0 });
|
|
123
|
-
const headers = new Headers(init?.headers || inputRL.headers || {});
|
|
124
|
-
if (inputBody !== null && !headers.has("Content-Type")) {
|
|
125
|
-
const contentType = extractContentType(inputBody, this);
|
|
126
|
-
if (contentType) {
|
|
127
|
-
headers.set("Content-Type", contentType);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
let signal = isRequest(input) ? inputRL.signal : null;
|
|
131
|
-
if (init && "signal" in init) {
|
|
132
|
-
signal = init.signal;
|
|
133
|
-
}
|
|
134
|
-
if (signal != null && !isAbortSignal(signal)) {
|
|
135
|
-
throw new TypeError("Expected signal to be an instanceof AbortSignal or EventTarget");
|
|
136
|
-
}
|
|
137
|
-
let referrer = init?.referrer == null ? inputRL.referrer : init.referrer;
|
|
138
|
-
if (referrer === "") {
|
|
139
|
-
referrer = "no-referrer";
|
|
140
|
-
} else if (referrer) {
|
|
141
|
-
const parsedReferrer = new URL(referrer);
|
|
142
|
-
referrer = /^about:(\/\/)?client$/.test(parsedReferrer.toString()) ? "client" : parsedReferrer;
|
|
143
|
-
} else {
|
|
144
|
-
referrer = undefined;
|
|
145
|
-
}
|
|
146
|
-
const scheme = parsedURL.protocol;
|
|
147
|
-
let session = null;
|
|
148
|
-
let message = null;
|
|
149
|
-
if (scheme === "http:" || scheme === "https:") {
|
|
150
|
-
session = new Soup.Session();
|
|
151
|
-
message = new Soup.Message({
|
|
152
|
-
method,
|
|
153
|
-
uri: GLib.Uri.parse(parsedURL.toString(), GLib.UriFlags.NONE)
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
this[INTERNALS] = {
|
|
157
|
-
method,
|
|
158
|
-
redirect: init?.redirect || inputRL.redirect || "follow",
|
|
159
|
-
headers,
|
|
160
|
-
parsedURL,
|
|
161
|
-
signal,
|
|
162
|
-
referrer,
|
|
163
|
-
referrerPolicy: "",
|
|
164
|
-
session,
|
|
165
|
-
message
|
|
166
|
-
};
|
|
167
|
-
this.follow = initRL.follow === undefined ? inputRL.follow === undefined ? 20 : inputRL.follow : initRL.follow;
|
|
168
|
-
this.compress = initRL.compress === undefined ? inputRL.compress === undefined ? true : inputRL.compress : initRL.compress;
|
|
169
|
-
this.counter = initRL.counter || inputRL.counter || 0;
|
|
170
|
-
this.agent = initRL.agent || inputRL.agent;
|
|
171
|
-
this.highWaterMark = initRL.highWaterMark || inputRL.highWaterMark || 16384;
|
|
172
|
-
this.insecureHTTPParser = initRL.insecureHTTPParser || inputRL.insecureHTTPParser || false;
|
|
173
|
-
this.referrerPolicy = init?.referrerPolicy || inputRL.referrerPolicy || "";
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Send the request using Soup.
|
|
177
|
-
*/
|
|
178
|
-
async _send(options) {
|
|
179
|
-
const { session, message } = this[INTERNALS];
|
|
180
|
-
if (!session || !message) {
|
|
181
|
-
throw new Error("Cannot send request: no Soup session (non-HTTP URL?)");
|
|
182
|
-
}
|
|
183
|
-
try {
|
|
184
|
-
session.remove_feature_by_type(Soup.ContentDecoder.$gtype);
|
|
185
|
-
} catch {}
|
|
186
|
-
options.headers._appendToSoupMessage(message);
|
|
187
|
-
const rawBuf = this._rawBodyBuffer;
|
|
188
|
-
if (rawBuf !== null && rawBuf.byteLength > 0) {
|
|
189
|
-
const contentType = options.headers.get("content-type") || null;
|
|
190
|
-
message.set_request_body_from_bytes(contentType, new GLib.Bytes(rawBuf));
|
|
191
|
-
}
|
|
192
|
-
const cancellable = new Gio.Cancellable();
|
|
193
|
-
this[INTERNALS].inputStream = await soupSendAsync(session, message, GLib.PRIORITY_DEFAULT, cancellable);
|
|
194
|
-
this[INTERNALS].readable = inputStreamToReadable(this[INTERNALS].inputStream);
|
|
195
|
-
return {
|
|
196
|
-
inputStream: this[INTERNALS].inputStream,
|
|
197
|
-
readable: this[INTERNALS].readable,
|
|
198
|
-
cancellable
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Clone this request
|
|
203
|
-
*/
|
|
204
|
-
clone() {
|
|
205
|
-
return new Request(this);
|
|
206
|
-
}
|
|
207
|
-
async arrayBuffer() {
|
|
208
|
-
return super.arrayBuffer();
|
|
209
|
-
}
|
|
210
|
-
async blob() {
|
|
211
|
-
return super.blob();
|
|
212
|
-
}
|
|
213
|
-
async formData() {
|
|
214
|
-
return super.formData();
|
|
215
|
-
}
|
|
216
|
-
async json() {
|
|
217
|
-
return super.json();
|
|
218
|
-
}
|
|
219
|
-
async text() {
|
|
220
|
-
return super.text();
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
Object.defineProperties(Request.prototype, {
|
|
224
|
-
method: { enumerable: true },
|
|
225
|
-
url: { enumerable: true },
|
|
226
|
-
headers: { enumerable: true },
|
|
227
|
-
redirect: { enumerable: true },
|
|
228
|
-
clone: { enumerable: true },
|
|
229
|
-
signal: { enumerable: true },
|
|
230
|
-
referrer: { enumerable: true },
|
|
231
|
-
referrerPolicy: { enumerable: true }
|
|
232
|
-
});
|
|
233
|
-
/**
|
|
234
|
-
* @param request
|
|
235
|
-
*/
|
|
236
|
-
const getSoupRequestOptions = (request) => {
|
|
237
|
-
const { parsedURL } = request[INTERNALS];
|
|
238
|
-
const headers = new Headers(request[INTERNALS].headers);
|
|
239
|
-
if (!headers.has("Accept")) {
|
|
240
|
-
headers.set("Accept", "*/*");
|
|
241
|
-
}
|
|
242
|
-
let contentLengthValue = null;
|
|
243
|
-
if (request.body === null && /^(post|put)$/i.test(request.method)) {
|
|
244
|
-
contentLengthValue = "0";
|
|
245
|
-
}
|
|
246
|
-
if (request.body !== null) {
|
|
247
|
-
const totalBytes = getTotalBytes(request);
|
|
248
|
-
if (typeof totalBytes === "number" && !Number.isNaN(totalBytes)) {
|
|
249
|
-
contentLengthValue = String(totalBytes);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (contentLengthValue) {
|
|
253
|
-
headers.set("Content-Length", contentLengthValue);
|
|
254
|
-
}
|
|
255
|
-
if (request.referrerPolicy === "") {
|
|
256
|
-
request.referrerPolicy = DEFAULT_REFERRER_POLICY;
|
|
257
|
-
}
|
|
258
|
-
if (request.referrer && request.referrer !== "no-referrer") {
|
|
259
|
-
request[INTERNALS].referrer = determineRequestsReferrer(request);
|
|
260
|
-
} else {
|
|
261
|
-
request[INTERNALS].referrer = "no-referrer";
|
|
262
|
-
}
|
|
263
|
-
if (request[INTERNALS].referrer instanceof URL) {
|
|
264
|
-
headers.set("Referer", request.referrer);
|
|
265
|
-
}
|
|
266
|
-
if (!headers.has("User-Agent")) {
|
|
267
|
-
headers.set("User-Agent", "gjsify-fetch");
|
|
268
|
-
}
|
|
269
|
-
if (request.compress && !headers.has("Accept-Encoding")) {
|
|
270
|
-
headers.set("Accept-Encoding", "gzip, deflate");
|
|
271
|
-
}
|
|
272
|
-
let { agent } = request;
|
|
273
|
-
if (typeof agent === "function") {
|
|
274
|
-
agent = agent(parsedURL);
|
|
275
|
-
}
|
|
276
|
-
if (!headers.has("Connection") && !agent) {
|
|
277
|
-
headers.set("Connection", "close");
|
|
278
|
-
}
|
|
279
|
-
const options = { headers };
|
|
280
|
-
return {
|
|
281
|
-
parsedURL,
|
|
282
|
-
options
|
|
283
|
-
};
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
//#endregion
|
|
287
|
-
export { Request, Request as default, getSoupRequestOptions };
|
|
1
|
+
import{isAbortSignal as e}from"./utils/is.js";import t,{clone as n,extractContentType as r,getTotalBytes as i}from"./body.js";import a from"./headers.js";import{inputStreamToReadable as o,soupSendAsync as s}from"./utils/soup-helpers.js";import{DEFAULT_REFERRER_POLICY as c,determineRequestsReferrer as l,validateReferrerPolicy as u}from"./utils/referrer.js";import{URL as d}from"@gjsify/url";import f from"@girs/soup-3.0";import p from"@girs/glib-2.0";import m from"@girs/gio-2.0";const h=Symbol(`Request internals`),g=e=>typeof e==`object`&&typeof e.url==`string`;var _=class i extends t{cache;credentials;destination;get headers(){return this[h].headers}integrity;keepalive;get method(){return this[h].method}mode;get redirect(){return this[h].redirect}get referrer(){if(this[h].referrer===`no-referrer`)return``;if(this[h].referrer===`client`)return`about:client`;if(this[h].referrer)return this[h].referrer.toString()}get referrerPolicy(){return this[h].referrerPolicy}set referrerPolicy(e){this[h].referrerPolicy=u(e)}get signal(){return this[h].signal}get url(){return this[h].parsedURL.toString()}get _uri(){return p.Uri.parse(this.url,p.UriFlags.NONE)}get _session(){return this[h].session}get _message(){return this[h].message}get _inputStream(){return this[h].inputStream}get[Symbol.toStringTag](){return`Request`}[h];follow;compress=!1;counter=0;agent=``;highWaterMark=16384;insecureHTTPParser=!1;constructor(t,i){let o=t,s=i||{},c,l={};if(g(t)?(c=new d(o.url),l=o):c=new d(t),c.username!==``||c.password!==``)throw TypeError(`${c} is an url with embedded credentials.`);let u=s.method||l.method||`GET`;if(/^(delete|get|head|options|post|put)$/i.test(u)&&(u=u.toUpperCase()),(i?.body!=null||g(t)&&o.body!==null)&&(u===`GET`||u===`HEAD`))throw TypeError(`Request with GET/HEAD method cannot have body`);let m=i?.body?i.body:g(t)&&o.body!==null?n(t):null;super(m,{size:s.size||0});let _=new a(i?.headers||o.headers||{});if(m!==null&&!_.has(`Content-Type`)){let e=r(m,this);e&&_.set(`Content-Type`,e)}let v=g(t)?o.signal:null;if(i&&`signal`in i&&(v=i.signal),v!=null&&!e(v))throw TypeError(`Expected signal to be an instanceof AbortSignal or EventTarget`);let y=i?.referrer==null?o.referrer:i.referrer;if(y===``)y=`no-referrer`;else if(y){let e=new d(y);y=/^about:(\/\/)?client$/.test(e.toString())?`client`:e}else y=void 0;let b=c.protocol,x=null,S=null;(b===`http:`||b===`https:`)&&(x=new f.Session,S=new f.Message({method:u,uri:p.Uri.parse(c.toString(),p.UriFlags.NONE)})),this[h]={method:u,redirect:i?.redirect||o.redirect||`follow`,headers:_,parsedURL:c,signal:v,referrer:y,referrerPolicy:``,session:x,message:S},this.follow=s.follow===void 0?o.follow===void 0?20:o.follow:s.follow,this.compress=s.compress===void 0?o.compress===void 0?!0:o.compress:s.compress,this.counter=s.counter||o.counter||0,this.agent=s.agent||o.agent,this.highWaterMark=s.highWaterMark||o.highWaterMark||16384,this.insecureHTTPParser=s.insecureHTTPParser||o.insecureHTTPParser||!1,this.referrerPolicy=i?.referrerPolicy||o.referrerPolicy||``}async _send(e){let{session:t,message:n}=this[h];if(!t||!n)throw Error(`Cannot send request: no Soup session (non-HTTP URL?)`);try{t.remove_feature_by_type(f.ContentDecoder.$gtype)}catch{}e.headers._appendToSoupMessage(n);let r=this._rawBodyBuffer;if(r!==null&&r.byteLength>0){let t=e.headers.get(`content-type`)||null;n.set_request_body_from_bytes(t,new p.Bytes(r))}let i=new m.Cancellable;return this[h].inputStream=await s(t,n,p.PRIORITY_DEFAULT,i),this[h].readable=o(this[h].inputStream),{inputStream:this[h].inputStream,readable:this[h].readable,cancellable:i}}clone(){return new i(this)}async arrayBuffer(){return super.arrayBuffer()}async blob(){return super.blob()}async formData(){return super.formData()}async json(){return super.json()}async text(){return super.text()}};Object.defineProperties(_.prototype,{method:{enumerable:!0},url:{enumerable:!0},headers:{enumerable:!0},redirect:{enumerable:!0},clone:{enumerable:!0},signal:{enumerable:!0},referrer:{enumerable:!0},referrerPolicy:{enumerable:!0}});const v=e=>{let{parsedURL:t}=e[h],n=new a(e[h].headers);n.has(`Accept`)||n.set(`Accept`,`*/*`);let r=null;if(e.body===null&&/^(post|put)$/i.test(e.method)&&(r=`0`),e.body!==null){let t=i(e);typeof t==`number`&&!Number.isNaN(t)&&(r=String(t))}r&&n.set(`Content-Length`,r),e.referrerPolicy===``&&(e.referrerPolicy=c),e.referrer&&e.referrer!==`no-referrer`?e[h].referrer=l(e):e[h].referrer=`no-referrer`,e[h].referrer instanceof d&&n.set(`Referer`,e.referrer),n.has(`User-Agent`)||n.set(`User-Agent`,`gjsify-fetch`),e.compress&&!n.has(`Accept-Encoding`)&&n.set(`Accept-Encoding`,`gzip, deflate`);let{agent:o}=e;return typeof o==`function`&&(o=o(t)),!n.has(`Connection`)&&!o&&n.set(`Connection`,`close`),{parsedURL:t,options:{headers:n}}};export{_ as Request,_ as default,v as getSoupRequestOptions};
|