@gjsify/fetch 0.3.21 → 0.4.3
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/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/body.js +1 -1
- package/lib/esm/errors/abort-error.js +1 -1
- package/lib/esm/errors/base.js +1 -1
- package/lib/esm/errors/fetch-error.js +1 -1
- package/lib/esm/headers.js +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/esm/request.js +1 -1
- package/lib/esm/response.js +1 -1
- package/lib/esm/utils/data-uri.js +1 -1
- package/lib/esm/utils/get-search.js +1 -1
- package/lib/esm/utils/is-redirect.js +1 -1
- package/lib/esm/utils/is.js +1 -1
- package/lib/esm/utils/multipart-parser.js +1 -1
- package/lib/esm/utils/referrer.js +1 -1
- package/lib/esm/utils/soup-helpers.js +1 -1
- package/lib/esm/xhr.js +1 -1
- package/package.json +61 -58
- package/src/body.ts +0 -448
- package/src/errors/abort-error.ts +0 -10
- package/src/errors/base.ts +0 -22
- package/src/errors/fetch-error.ts +0 -26
- package/src/headers.ts +0 -232
- package/src/index.spec.ts +0 -339
- package/src/index.ts +0 -316
- package/src/register/fetch.ts +0 -16
- package/src/register/xhr.ts +0 -20
- package/src/register.ts +0 -5
- package/src/request.ts +0 -423
- package/src/response.ts +0 -227
- package/src/test.browser.mts +0 -130
- package/src/test.mts +0 -9
- package/src/types/index.ts +0 -1
- package/src/types/system-error.ts +0 -11
- package/src/utils/blob-from.ts +0 -8
- package/src/utils/data-uri.ts +0 -29
- package/src/utils/get-search.ts +0 -9
- package/src/utils/is-redirect.ts +0 -11
- package/src/utils/is.ts +0 -88
- package/src/utils/multipart-parser.ts +0 -448
- package/src/utils/referrer.ts +0 -350
- package/src/utils/soup-helpers.ts +0 -37
- package/src/xhr.ts +0 -270
- package/tsconfig.json +0 -22
- package/tsconfig.tsbuildinfo +0 -1
package/src/request.ts
DELETED
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
// Adapted from node-fetch (https://github.com/node-fetch/node-fetch/blob/main/src/request.js)
|
|
3
|
-
// Copyright (c) node-fetch contributors. MIT license.
|
|
4
|
-
// Modifications: Rewritten for GJS using Soup.Message and Gio
|
|
5
|
-
|
|
6
|
-
import GLib from '@girs/glib-2.0';
|
|
7
|
-
import Soup from '@girs/soup-3.0';
|
|
8
|
-
import Gio from '@girs/gio-2.0';
|
|
9
|
-
import { soupSendAsync, inputStreamToReadable } from './utils/soup-helpers.js';
|
|
10
|
-
|
|
11
|
-
import { URL } from '@gjsify/url';
|
|
12
|
-
import { Blob } from './utils/blob-from.js';
|
|
13
|
-
|
|
14
|
-
import { Readable } from 'node:stream';
|
|
15
|
-
|
|
16
|
-
import Headers from './headers.js';
|
|
17
|
-
import Body, {clone, extractContentType, getTotalBytes} from './body.js';
|
|
18
|
-
import {isAbortSignal} from './utils/is.js';
|
|
19
|
-
import type { FormData } from '@gjsify/formdata';
|
|
20
|
-
import {
|
|
21
|
-
validateReferrerPolicy, determineRequestsReferrer, DEFAULT_REFERRER_POLICY
|
|
22
|
-
} from './utils/referrer.js';
|
|
23
|
-
|
|
24
|
-
const INTERNALS = Symbol('Request internals');
|
|
25
|
-
|
|
26
|
-
/** Properties that may exist on a Request-like object (used for safe casting). */
|
|
27
|
-
interface RequestLike {
|
|
28
|
-
url?: string;
|
|
29
|
-
method?: string;
|
|
30
|
-
headers?: Headers | HeadersInit;
|
|
31
|
-
redirect?: RequestRedirect;
|
|
32
|
-
signal?: AbortSignal | null;
|
|
33
|
-
referrer?: string;
|
|
34
|
-
referrerPolicy?: ReferrerPolicy;
|
|
35
|
-
body?: BodyInit | null;
|
|
36
|
-
follow?: number;
|
|
37
|
-
compress?: boolean;
|
|
38
|
-
counter?: number;
|
|
39
|
-
agent?: string | ((url: URL) => string);
|
|
40
|
-
highWaterMark?: number;
|
|
41
|
-
insecureHTTPParser?: boolean;
|
|
42
|
-
size?: number;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Check if `obj` is an instance of Request.
|
|
47
|
-
*/
|
|
48
|
-
const isRequest = (obj: RequestInfo | URL | Request): boolean => {
|
|
49
|
-
return (
|
|
50
|
-
typeof obj === 'object' &&
|
|
51
|
-
typeof (obj as RequestLike).url === 'string'
|
|
52
|
-
);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// @ts-expect-error — declaration merging with globalThis.Request for Fetch API compatibility
|
|
56
|
-
export interface Request extends globalThis.Request {}
|
|
57
|
-
|
|
58
|
-
/** This Fetch API interface represents a resource request. */
|
|
59
|
-
export class Request extends Body {
|
|
60
|
-
/** Returns the cache mode associated with request, which is a string indicating how the request will interact with the browser's cache when fetching. */
|
|
61
|
-
readonly cache: RequestCache;
|
|
62
|
-
/** 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. */
|
|
63
|
-
readonly credentials: RequestCredentials;
|
|
64
|
-
/** Returns the kind of resource requested by request, e.g., "document" or "script". */
|
|
65
|
-
readonly destination: RequestDestination;
|
|
66
|
-
/** 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. */
|
|
67
|
-
get headers(): Headers {
|
|
68
|
-
return this[INTERNALS].headers;
|
|
69
|
-
}
|
|
70
|
-
/** 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] */
|
|
71
|
-
readonly integrity: string;
|
|
72
|
-
/** Returns a boolean indicating whether or not request can outlive the global in which it was created. */
|
|
73
|
-
readonly keepalive: boolean;
|
|
74
|
-
/** Returns request's HTTP method, which is "GET" by default. */
|
|
75
|
-
get method(): string {
|
|
76
|
-
return this[INTERNALS].method;
|
|
77
|
-
}
|
|
78
|
-
/** 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. */
|
|
79
|
-
readonly mode: RequestMode;
|
|
80
|
-
/** 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. */
|
|
81
|
-
get redirect(): RequestRedirect {
|
|
82
|
-
return this[INTERNALS].redirect;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Returns the referrer of request.
|
|
86
|
-
* 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.
|
|
87
|
-
* This is used during fetching to determine the value of the `Referer` header of the request being made.
|
|
88
|
-
* @see https://fetch.spec.whatwg.org/#dom-request-referrer
|
|
89
|
-
**/
|
|
90
|
-
get referrer(): string {
|
|
91
|
-
if (this[INTERNALS].referrer === 'no-referrer') {
|
|
92
|
-
return '';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (this[INTERNALS].referrer === 'client') {
|
|
96
|
-
return 'about:client';
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (this[INTERNALS].referrer) {
|
|
100
|
-
return this[INTERNALS].referrer.toString();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
/** Returns the referrer policy associated with request. This is used during fetching to compute the value of the request's referrer. */
|
|
106
|
-
get referrerPolicy(): ReferrerPolicy {
|
|
107
|
-
return this[INTERNALS].referrerPolicy;
|
|
108
|
-
}
|
|
109
|
-
set referrerPolicy(referrerPolicy) {
|
|
110
|
-
this[INTERNALS].referrerPolicy = validateReferrerPolicy(referrerPolicy);
|
|
111
|
-
}
|
|
112
|
-
/** Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. */
|
|
113
|
-
get signal(): AbortSignal {
|
|
114
|
-
return this[INTERNALS].signal;
|
|
115
|
-
}
|
|
116
|
-
/** Returns the URL of request as a string. */
|
|
117
|
-
get url(): string {
|
|
118
|
-
return this[INTERNALS].parsedURL.toString();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
get _uri() {
|
|
122
|
-
return GLib.Uri.parse(this.url, GLib.UriFlags.NONE);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
get _session() {
|
|
126
|
-
return this[INTERNALS].session;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
get _message() {
|
|
130
|
-
return this[INTERNALS].message;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
get _inputStream() {
|
|
134
|
-
return this[INTERNALS].inputStream;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
get [Symbol.toStringTag]() {
|
|
138
|
-
return 'Request';
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
[INTERNALS]: {
|
|
142
|
-
// node-fetch
|
|
143
|
-
method: string;
|
|
144
|
-
redirect: RequestRedirect;
|
|
145
|
-
headers: Headers;
|
|
146
|
-
parsedURL: URL;
|
|
147
|
-
signal: AbortSignal;
|
|
148
|
-
referrer: string | URL;
|
|
149
|
-
referrerPolicy: ReferrerPolicy;
|
|
150
|
-
// Gjsify
|
|
151
|
-
session: Soup.Session | null;
|
|
152
|
-
message: Soup.Message | null;
|
|
153
|
-
inputStream?: Gio.InputStream;
|
|
154
|
-
readable?: Readable
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Node-fetch-only options
|
|
158
|
-
follow: number;
|
|
159
|
-
compress = false;
|
|
160
|
-
counter = 0
|
|
161
|
-
agent: string | ((url: URL) => string) = ''
|
|
162
|
-
highWaterMark = 16384;
|
|
163
|
-
insecureHTTPParser = false;
|
|
164
|
-
|
|
165
|
-
constructor(input: RequestInfo | URL | Request, init?: RequestInit) {
|
|
166
|
-
const inputRL = input as unknown as RequestLike;
|
|
167
|
-
const initRL = (init || {}) as unknown as RequestLike;
|
|
168
|
-
|
|
169
|
-
let parsedURL: URL;
|
|
170
|
-
let requestObj: RequestLike = {};
|
|
171
|
-
|
|
172
|
-
if(isRequest(input)) {
|
|
173
|
-
parsedURL = new URL(inputRL.url);
|
|
174
|
-
requestObj = inputRL;
|
|
175
|
-
} else {
|
|
176
|
-
parsedURL = new URL(input as string | URL);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (parsedURL.username !== '' || parsedURL.password !== '') {
|
|
180
|
-
throw new TypeError(`${parsedURL} is an url with embedded credentials.`);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
let method = initRL.method || requestObj.method || 'GET';
|
|
184
|
-
if (/^(delete|get|head|options|post|put)$/i.test(method)) {
|
|
185
|
-
method = method.toUpperCase();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if ((init?.body != null || (isRequest(input) && inputRL.body !== null)) &&
|
|
189
|
-
(method === 'GET' || method === 'HEAD')) {
|
|
190
|
-
throw new TypeError('Request with GET/HEAD method cannot have body');
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const inputBody = init?.body ? init.body : (isRequest(input) && inputRL.body !== null ? clone(input as unknown as Request & Body) : null);
|
|
194
|
-
|
|
195
|
-
super(inputBody, {
|
|
196
|
-
size: initRL.size || 0
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const headers = new Headers((init?.headers || inputRL.headers || {}) as HeadersInit);
|
|
200
|
-
|
|
201
|
-
if (inputBody !== null && !headers.has('Content-Type')) {
|
|
202
|
-
const contentType = extractContentType(inputBody, this);
|
|
203
|
-
if (contentType) {
|
|
204
|
-
headers.set('Content-Type', contentType);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
let signal = isRequest(input) ?
|
|
209
|
-
inputRL.signal :
|
|
210
|
-
null;
|
|
211
|
-
if (init && 'signal' in init) {
|
|
212
|
-
signal = init.signal;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (signal != null && !isAbortSignal(signal)) {
|
|
216
|
-
throw new TypeError('Expected signal to be an instanceof AbortSignal or EventTarget');
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// §5.4, Request constructor steps, step 15.1
|
|
220
|
-
let referrer: string | URL = init?.referrer == null ? inputRL.referrer : init.referrer;
|
|
221
|
-
if (referrer === '') {
|
|
222
|
-
// §5.4, Request constructor steps, step 15.2
|
|
223
|
-
referrer = 'no-referrer';
|
|
224
|
-
} else if (referrer) {
|
|
225
|
-
// §5.4, Request constructor steps, step 15.3.1, 15.3.2
|
|
226
|
-
const parsedReferrer = new URL(referrer);
|
|
227
|
-
// §5.4, Request constructor steps, step 15.3.3, 15.3.4
|
|
228
|
-
referrer = /^about:(\/\/)?client$/.test(parsedReferrer.toString()) ? 'client' : parsedReferrer;
|
|
229
|
-
} else {
|
|
230
|
-
referrer = undefined;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Only create Soup objects for HTTP/HTTPS — data: URIs etc. don't go through Soup
|
|
234
|
-
const scheme = parsedURL.protocol;
|
|
235
|
-
let session: Soup.Session | null = null;
|
|
236
|
-
let message: Soup.Message | null = null;
|
|
237
|
-
if (scheme === 'http:' || scheme === 'https:') {
|
|
238
|
-
session = new Soup.Session();
|
|
239
|
-
message = new Soup.Message({
|
|
240
|
-
method,
|
|
241
|
-
uri: GLib.Uri.parse(parsedURL.toString(), GLib.UriFlags.NONE),
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
this[INTERNALS] = {
|
|
246
|
-
method,
|
|
247
|
-
redirect: init?.redirect || inputRL.redirect || 'follow',
|
|
248
|
-
headers,
|
|
249
|
-
parsedURL,
|
|
250
|
-
signal,
|
|
251
|
-
referrer,
|
|
252
|
-
referrerPolicy: '',
|
|
253
|
-
session,
|
|
254
|
-
message,
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
// Node-fetch-only options
|
|
258
|
-
this.follow = initRL.follow === undefined ? (inputRL.follow === undefined ? 20 : inputRL.follow) : initRL.follow;
|
|
259
|
-
this.compress = initRL.compress === undefined ? (inputRL.compress === undefined ? true : inputRL.compress) : initRL.compress;
|
|
260
|
-
this.counter = initRL.counter || inputRL.counter || 0;
|
|
261
|
-
this.agent = initRL.agent || inputRL.agent;
|
|
262
|
-
this.highWaterMark = initRL.highWaterMark || inputRL.highWaterMark || 16384;
|
|
263
|
-
this.insecureHTTPParser = initRL.insecureHTTPParser || inputRL.insecureHTTPParser || false;
|
|
264
|
-
|
|
265
|
-
// §5.4, Request constructor steps, step 16.
|
|
266
|
-
// Default is empty string per https://fetch.spec.whatwg.org/#concept-request-referrer-policy
|
|
267
|
-
this.referrerPolicy = init?.referrerPolicy || inputRL.referrerPolicy || '';
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Send the request using Soup.
|
|
272
|
-
*/
|
|
273
|
-
async _send(options: { headers: Headers }) {
|
|
274
|
-
const { session, message } = this[INTERNALS];
|
|
275
|
-
|
|
276
|
-
if (!session || !message) {
|
|
277
|
-
throw new Error('Cannot send request: no Soup session (non-HTTP URL?)');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Soup auto-adds ContentDecoder to new sessions, but it decodes the body
|
|
281
|
-
// without removing the Content-Encoding header, causing double-decompression
|
|
282
|
-
// if we also run DecompressionStream below. Remove it so our JS-level
|
|
283
|
-
// decompression in index.ts handles everything correctly.
|
|
284
|
-
try { session.remove_feature_by_type(Soup.ContentDecoder.$gtype); } catch { /* not present */ }
|
|
285
|
-
|
|
286
|
-
options.headers._appendToSoupMessage(message);
|
|
287
|
-
|
|
288
|
-
// Attach the request body to the Soup message (needed for POST/PUT/PATCH).
|
|
289
|
-
// Use _rawBodyBuffer to read the body without consuming the stream (the
|
|
290
|
-
// `body` getter may have already put the internal Readable into flowing mode,
|
|
291
|
-
// draining its buffer before we get here).
|
|
292
|
-
const rawBuf = this._rawBodyBuffer;
|
|
293
|
-
if (rawBuf !== null && rawBuf.byteLength > 0) {
|
|
294
|
-
const contentType = options.headers.get('content-type') || null;
|
|
295
|
-
message.set_request_body_from_bytes(contentType, new GLib.Bytes(rawBuf));
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const cancellable = new Gio.Cancellable();
|
|
299
|
-
|
|
300
|
-
this[INTERNALS].inputStream = await soupSendAsync(session, message, GLib.PRIORITY_DEFAULT, cancellable);
|
|
301
|
-
this[INTERNALS].readable = inputStreamToReadable(this[INTERNALS].inputStream);
|
|
302
|
-
|
|
303
|
-
return {
|
|
304
|
-
inputStream: this[INTERNALS].inputStream,
|
|
305
|
-
readable: this[INTERNALS].readable,
|
|
306
|
-
cancellable
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Clone this request
|
|
312
|
-
*/
|
|
313
|
-
clone(): Request {
|
|
314
|
-
return new Request(this);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async arrayBuffer(): Promise<ArrayBuffer> {
|
|
318
|
-
return super.arrayBuffer();
|
|
319
|
-
}
|
|
320
|
-
async blob(): Promise<Blob> {
|
|
321
|
-
return super.blob();
|
|
322
|
-
}
|
|
323
|
-
async formData(): Promise<FormData> {
|
|
324
|
-
return super.formData();
|
|
325
|
-
}
|
|
326
|
-
async json(): Promise<unknown> {
|
|
327
|
-
return super.json();
|
|
328
|
-
}
|
|
329
|
-
async text(): Promise<string> {
|
|
330
|
-
return super.text();
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
Object.defineProperties(Request.prototype, {
|
|
335
|
-
method: {enumerable: true},
|
|
336
|
-
url: {enumerable: true},
|
|
337
|
-
headers: {enumerable: true},
|
|
338
|
-
redirect: {enumerable: true},
|
|
339
|
-
clone: {enumerable: true},
|
|
340
|
-
signal: {enumerable: true},
|
|
341
|
-
referrer: {enumerable: true},
|
|
342
|
-
referrerPolicy: {enumerable: true}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
export default Request;
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* @param request
|
|
349
|
-
*/
|
|
350
|
-
export const getSoupRequestOptions = (request: Request) => {
|
|
351
|
-
const { parsedURL } = request[INTERNALS];
|
|
352
|
-
const headers = new Headers(request[INTERNALS].headers);
|
|
353
|
-
|
|
354
|
-
// Fetch step 1.3
|
|
355
|
-
if (!headers.has('Accept')) {
|
|
356
|
-
headers.set('Accept', '*/*');
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// HTTP-network-or-cache fetch steps 2.4-2.7
|
|
360
|
-
let contentLengthValue: string | null = null;
|
|
361
|
-
if (request.body === null && /^(post|put)$/i.test(request.method)) {
|
|
362
|
-
contentLengthValue = '0';
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (request.body !== null) {
|
|
366
|
-
const totalBytes = getTotalBytes(request);
|
|
367
|
-
// Set Content-Length if totalBytes is a Number (that is not NaN)
|
|
368
|
-
if (typeof totalBytes === 'number' && !Number.isNaN(totalBytes)) {
|
|
369
|
-
contentLengthValue = String(totalBytes);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (contentLengthValue) {
|
|
374
|
-
headers.set('Content-Length', contentLengthValue);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// 4.1. Main fetch, step 2.6
|
|
378
|
-
if (request.referrerPolicy === '') {
|
|
379
|
-
request.referrerPolicy = DEFAULT_REFERRER_POLICY;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// 4.1. Main fetch, step 2.7
|
|
383
|
-
if (request.referrer && request.referrer !== 'no-referrer') {
|
|
384
|
-
request[INTERNALS].referrer = determineRequestsReferrer(request);
|
|
385
|
-
} else {
|
|
386
|
-
request[INTERNALS].referrer = 'no-referrer';
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// 4.5. HTTP-network-or-cache fetch, step 6.9
|
|
390
|
-
if (request[INTERNALS].referrer instanceof URL) {
|
|
391
|
-
headers.set('Referer', request.referrer);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// HTTP-network-or-cache fetch step 2.11
|
|
395
|
-
if (!headers.has('User-Agent')) {
|
|
396
|
-
headers.set('User-Agent', 'gjsify-fetch');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// HTTP-network-or-cache fetch step 2.15. Brotli ('br') is omitted: SpiderMonkey
|
|
400
|
-
// 128's DecompressionStream supports only 'gzip' and 'deflate', so advertising
|
|
401
|
-
// 'br' would let servers respond with bodies we cannot decode.
|
|
402
|
-
if (request.compress && !headers.has('Accept-Encoding')) {
|
|
403
|
-
headers.set('Accept-Encoding', 'gzip, deflate');
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
let { agent } = request;
|
|
407
|
-
if (typeof agent === 'function') {
|
|
408
|
-
agent = agent(parsedURL);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (!headers.has('Connection') && !agent) {
|
|
412
|
-
headers.set('Connection', 'close');
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const options = {
|
|
416
|
-
headers,
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
return {
|
|
420
|
-
parsedURL,
|
|
421
|
-
options
|
|
422
|
-
};
|
|
423
|
-
};
|
package/src/response.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
// Adapted from node-fetch (https://github.com/node-fetch/node-fetch/blob/main/src/response.js)
|
|
3
|
-
// Copyright (c) node-fetch contributors. MIT license.
|
|
4
|
-
// Modifications: Rewritten for GJS using Gio streams and GLib
|
|
5
|
-
|
|
6
|
-
import GLib from '@girs/glib-2.0';
|
|
7
|
-
import Gio from '@girs/gio-2.0';
|
|
8
|
-
|
|
9
|
-
import Headers from './headers.js';
|
|
10
|
-
import Body, { clone, extractContentType } from './body.js';
|
|
11
|
-
import { isRedirect } from './utils/is-redirect.js';
|
|
12
|
-
|
|
13
|
-
import { URL } from '@gjsify/url';
|
|
14
|
-
import { Blob } from './utils/blob-from.js';
|
|
15
|
-
|
|
16
|
-
import type { Readable } from 'node:stream';
|
|
17
|
-
|
|
18
|
-
const INTERNALS = Symbol('Response internals');
|
|
19
|
-
|
|
20
|
-
interface ResponseOptions {
|
|
21
|
-
status?: number;
|
|
22
|
-
statusText?: string;
|
|
23
|
-
headers?: HeadersInit | Headers;
|
|
24
|
-
url?: string;
|
|
25
|
-
type?: ResponseType;
|
|
26
|
-
ok?: boolean;
|
|
27
|
-
redirected?: boolean;
|
|
28
|
-
size?: number;
|
|
29
|
-
counter?: number;
|
|
30
|
-
highWaterMark?: number;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface ResponseInit {
|
|
34
|
-
status?: number;
|
|
35
|
-
statusText?: string;
|
|
36
|
-
headers?: HeadersInit | Headers;
|
|
37
|
-
type?: ResponseType;
|
|
38
|
-
url?: string;
|
|
39
|
-
counter?: number;
|
|
40
|
-
highWaterMark?: number;
|
|
41
|
-
ok?: boolean;
|
|
42
|
-
redirected?: boolean;
|
|
43
|
-
size?: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Response class
|
|
48
|
-
*
|
|
49
|
-
* Ref: https://fetch.spec.whatwg.org/#response-class
|
|
50
|
-
*
|
|
51
|
-
* @param body Readable stream
|
|
52
|
-
* @param opts Response options
|
|
53
|
-
*/
|
|
54
|
-
export class Response extends Body {
|
|
55
|
-
|
|
56
|
-
[INTERNALS]: {
|
|
57
|
-
type: ResponseType;
|
|
58
|
-
url: string;
|
|
59
|
-
status: number;
|
|
60
|
-
statusText: string;
|
|
61
|
-
headers: Headers;
|
|
62
|
-
counter: number;
|
|
63
|
-
highWaterMark: number;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
_inputStream: Gio.InputStream | null = null;
|
|
67
|
-
|
|
68
|
-
constructor(body: BodyInit | Readable | Blob | Buffer | null = null, options: ResponseOptions = {}) {
|
|
69
|
-
super(body, options);
|
|
70
|
-
|
|
71
|
-
// eslint-disable-next-line no-eq-null, eqeqeq, no-negated-condition
|
|
72
|
-
const status = options.status != null ? options.status : 200;
|
|
73
|
-
|
|
74
|
-
const headers = new Headers(options.headers);
|
|
75
|
-
|
|
76
|
-
if (body !== null && !headers.has('Content-Type')) {
|
|
77
|
-
const contentType = extractContentType(body, this);
|
|
78
|
-
if (contentType) {
|
|
79
|
-
headers.append('Content-Type', contentType);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
this[INTERNALS] = {
|
|
84
|
-
type: 'default',
|
|
85
|
-
url: options.url,
|
|
86
|
-
status,
|
|
87
|
-
statusText: options.statusText || '',
|
|
88
|
-
headers,
|
|
89
|
-
counter: options.counter,
|
|
90
|
-
highWaterMark: options.highWaterMark
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
get type() {
|
|
95
|
-
return this[INTERNALS].type;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
get url() {
|
|
99
|
-
return this[INTERNALS].url || '';
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
get status() {
|
|
103
|
-
return this[INTERNALS].status;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Convenience property representing if the request ended normally
|
|
108
|
-
*/
|
|
109
|
-
get ok() {
|
|
110
|
-
return this[INTERNALS].status >= 200 && this[INTERNALS].status < 300;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
get redirected() {
|
|
114
|
-
return this[INTERNALS].counter > 0;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
get statusText() {
|
|
118
|
-
return this[INTERNALS].statusText;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
get headers() {
|
|
122
|
-
return this[INTERNALS].headers;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
get highWaterMark() {
|
|
126
|
-
return this[INTERNALS].highWaterMark;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Clone this response
|
|
131
|
-
*
|
|
132
|
-
* @return Response
|
|
133
|
-
*/
|
|
134
|
-
clone() {
|
|
135
|
-
return new Response(clone(this, this.highWaterMark), {
|
|
136
|
-
type: this.type,
|
|
137
|
-
url: this.url,
|
|
138
|
-
status: this.status,
|
|
139
|
-
statusText: this.statusText,
|
|
140
|
-
headers: this.headers,
|
|
141
|
-
ok: this.ok,
|
|
142
|
-
redirected: this.redirected,
|
|
143
|
-
size: this.size,
|
|
144
|
-
highWaterMark: this.highWaterMark
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* @param url The URL that the new response is to originate from.
|
|
150
|
-
* @param status An optional status code for the response (e.g., 302.)
|
|
151
|
-
* @returns A Response object.
|
|
152
|
-
*/
|
|
153
|
-
static redirect(url: string, status = 302) {
|
|
154
|
-
if (!isRedirect(status)) {
|
|
155
|
-
throw new RangeError('Failed to execute "redirect" on "response": Invalid status code');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return new Response(null, {
|
|
159
|
-
headers: {
|
|
160
|
-
location: new URL(url).toString()
|
|
161
|
-
},
|
|
162
|
-
status
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
static error() {
|
|
167
|
-
const response = new Response(null, { status: 0, statusText: '' });
|
|
168
|
-
response[INTERNALS].type = 'error';
|
|
169
|
-
return response;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Create a Response with a JSON body.
|
|
174
|
-
* @param data The data to serialize as JSON.
|
|
175
|
-
* @param init Optional response init options.
|
|
176
|
-
* @returns A Response with the JSON body and appropriate content-type header.
|
|
177
|
-
*/
|
|
178
|
-
static json(data: unknown, init?: ResponseOptions) {
|
|
179
|
-
const body = JSON.stringify(data);
|
|
180
|
-
const options: ResponseOptions = { ...init };
|
|
181
|
-
const headers = new Headers(options.headers);
|
|
182
|
-
if (!headers.has('content-type')) {
|
|
183
|
-
headers.set('content-type', 'application/json');
|
|
184
|
-
}
|
|
185
|
-
options.headers = headers;
|
|
186
|
-
return new Response(body, options);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
get [Symbol.toStringTag]() {
|
|
190
|
-
return 'Response';
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async text(): Promise<string> {
|
|
194
|
-
if (!this._inputStream) {
|
|
195
|
-
return super.text();
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const outputStream = Gio.MemoryOutputStream.new_resizable();
|
|
199
|
-
|
|
200
|
-
await new Promise<number>((resolve, reject) => {
|
|
201
|
-
outputStream.splice_async(this._inputStream, Gio.OutputStreamSpliceFlags.CLOSE_TARGET | Gio.OutputStreamSpliceFlags.CLOSE_SOURCE, GLib.PRIORITY_DEFAULT, null, (_self: Gio.OutputStream, res: Gio.AsyncResult) => {
|
|
202
|
-
try {
|
|
203
|
-
resolve(outputStream.splice_finish(res));
|
|
204
|
-
} catch (error) {
|
|
205
|
-
reject(error);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
const bytes = outputStream.steal_as_bytes();
|
|
211
|
-
|
|
212
|
-
return new TextDecoder().decode(bytes.toArray());
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
Object.defineProperties(Response.prototype, {
|
|
217
|
-
type: { enumerable: true },
|
|
218
|
-
url: { enumerable: true },
|
|
219
|
-
status: { enumerable: true },
|
|
220
|
-
ok: { enumerable: true },
|
|
221
|
-
redirected: { enumerable: true },
|
|
222
|
-
statusText: { enumerable: true },
|
|
223
|
-
headers: { enumerable: true },
|
|
224
|
-
clone: { enumerable: true }
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
export default Response;
|