@gjsify/fetch 0.4.36 → 0.4.38
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/request.js +1 -1
- package/lib/request.js +37 -1
- package/package.json +10 -9
package/lib/esm/request.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";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`);let g=null;function getSharedSession(){if(g===null){g=new f.Session;try{g.remove_feature_by_type(f.ContentDecoder.$gtype)}catch{}}return g}const isRequest=e=>typeof e==`object`&&typeof e.url==`string`;var _=class Request 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(isRequest(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||isRequest(t)&&o.body!==null)&&(u===`GET`||u===`HEAD`))throw TypeError(`Request with GET/HEAD method cannot have body`);let m=i?.body?i.body:isRequest(t)&&o.body!==null?n(t):null;super(m,{size:s.size||0});let g=new a(i?.headers||o.headers||{});if(m!==null&&!g.has(`Content-Type`)){let e=r(m,this);e&&g.set(`Content-Type`,e)}let _=isRequest(t)?o.signal:null;if(i&&`signal`in i&&(_=i.signal),_!=null&&!e(_))throw TypeError(`Expected signal to be an instanceof AbortSignal or EventTarget`);let v=i?.referrer==null?o.referrer:i.referrer;if(v===``)v=`no-referrer`;else if(v){let e=new d(v);v=/^about:(\/\/)?client$/.test(e.toString())?`client`:e}else v=void 0;let y=c.protocol,b=null,x=null;(y===`http:`||y===`https:`)&&(b=getSharedSession(),x=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:g,parsedURL:c,signal:_,referrer:v,referrerPolicy:``,session:b,message:x},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?)`);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 Request(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 getSoupRequestOptions=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,getSoupRequestOptions};
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";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`);let g=null;function envPositiveInt(e,t){let n=p.getenv(e);if(!n)return t;let r=Number.parseInt(n,10);return Number.isFinite(r)&&r>0?r:t}function getSharedSession(){if(g===null){let e=envPositiveInt(`GJSIFY_FETCH_MAX_CONNS_PER_HOST`,16),t=Math.max(envPositiveInt(`GJSIFY_FETCH_MAX_CONNS`,64),e);g=new f.Session({maxConns:t,maxConnsPerHost:e});try{g.remove_feature_by_type(f.ContentDecoder.$gtype)}catch{}}return g}const isRequest=e=>typeof e==`object`&&typeof e.url==`string`;var _=class Request 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(isRequest(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||isRequest(t)&&o.body!==null)&&(u===`GET`||u===`HEAD`))throw TypeError(`Request with GET/HEAD method cannot have body`);let m=i?.body?i.body:isRequest(t)&&o.body!==null?n(t):null;super(m,{size:s.size||0});let g=new a(i?.headers||o.headers||{});if(m!==null&&!g.has(`Content-Type`)){let e=r(m,this);e&&g.set(`Content-Type`,e)}let _=isRequest(t)?o.signal:null;if(i&&`signal`in i&&(_=i.signal),_!=null&&!e(_))throw TypeError(`Expected signal to be an instanceof AbortSignal or EventTarget`);let v=i?.referrer==null?o.referrer:i.referrer;if(v===``)v=`no-referrer`;else if(v){let e=new d(v);v=/^about:(\/\/)?client$/.test(e.toString())?`client`:e}else v=void 0;let y=c.protocol,b=null,x=null;(y===`http:`||y===`https:`)&&(b=getSharedSession(),x=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:g,parsedURL:c,signal:_,referrer:v,referrerPolicy:``,session:b,message:x},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?)`);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 Request(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 getSoupRequestOptions=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,getSoupRequestOptions};
|
package/lib/request.js
CHANGED
|
@@ -46,9 +46,45 @@ const INTERNALS = Symbol('Request internals');
|
|
|
46
46
|
* instantiate a Soup object.
|
|
47
47
|
*/
|
|
48
48
|
let sharedSession = null;
|
|
49
|
+
/**
|
|
50
|
+
* Connections libsoup may open per host (and overall) on the shared session.
|
|
51
|
+
*
|
|
52
|
+
* libsoup's default `max-conns-per-host` is a conservative **2**, which
|
|
53
|
+
* serialises bulk-fetch workloads to two-at-a-time even over a multiplexed
|
|
54
|
+
* HTTP/2 connection. `@gjsify/npm-registry` (during `gjsify install`) hammers
|
|
55
|
+
* a single host — the npm registry — with hundreds of packument + tarball
|
|
56
|
+
* GETs; at the default cap a 16-wide download pool was measured stalling at
|
|
57
|
+
* ~2 effective in-flight requests (16 packuments in ~3.0s vs ~0.24s once the
|
|
58
|
+
* cap is lifted — a 12× difference, same h2 connection).
|
|
59
|
+
*
|
|
60
|
+
* 16 matches npm's `maxsockets` (15) and pnpm's `network-concurrency` (16)
|
|
61
|
+
* defaults and is browser-competitive (Chrome uses 6 per host for HTTP/1.1,
|
|
62
|
+
* more via h2 stream multiplexing). It is harmless for light fetch users, who
|
|
63
|
+
* never materialise more than a couple of concurrent requests to one host.
|
|
64
|
+
* Override per-host with `GJSIFY_FETCH_MAX_CONNS_PER_HOST` and the global
|
|
65
|
+
* ceiling with `GJSIFY_FETCH_MAX_CONNS`.
|
|
66
|
+
*/
|
|
67
|
+
const DEFAULT_MAX_CONNS_PER_HOST = 16;
|
|
68
|
+
const DEFAULT_MAX_CONNS = 64;
|
|
69
|
+
function envPositiveInt(name, fallback) {
|
|
70
|
+
const raw = GLib.getenv(name);
|
|
71
|
+
if (!raw)
|
|
72
|
+
return fallback;
|
|
73
|
+
const n = Number.parseInt(raw, 10);
|
|
74
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
75
|
+
}
|
|
49
76
|
function getSharedSession() {
|
|
50
77
|
if (sharedSession === null) {
|
|
51
|
-
|
|
78
|
+
const maxPerHost = envPositiveInt('GJSIFY_FETCH_MAX_CONNS_PER_HOST', DEFAULT_MAX_CONNS_PER_HOST);
|
|
79
|
+
// The global ceiling must be ≥ the per-host cap, else libsoup clamps
|
|
80
|
+
// the per-host value down to it.
|
|
81
|
+
const maxConns = Math.max(envPositiveInt('GJSIFY_FETCH_MAX_CONNS', DEFAULT_MAX_CONNS), maxPerHost);
|
|
82
|
+
// `max-conns` / `max-conns-per-host` are construct-only in libsoup 3 —
|
|
83
|
+
// they MUST be passed to the constructor; the setters don't exist.
|
|
84
|
+
sharedSession = new Soup.Session({
|
|
85
|
+
maxConns,
|
|
86
|
+
maxConnsPerHost: maxPerHost,
|
|
87
|
+
});
|
|
52
88
|
// Soup auto-adds a ContentDecoder to new sessions, but it decodes the
|
|
53
89
|
// body without removing the Content-Encoding header, causing
|
|
54
90
|
// double-decompression when index.ts also runs DecompressionStream.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/fetch",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.38",
|
|
4
4
|
"description": "Web and Node.js fetch module for Gjs",
|
|
5
5
|
"module": "lib/esm/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -51,26 +51,27 @@
|
|
|
51
51
|
"fetch"
|
|
52
52
|
],
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@gjsify/cli": "^0.4.
|
|
55
|
-
"@gjsify/unit": "^0.4.
|
|
54
|
+
"@gjsify/cli": "^0.4.38",
|
|
55
|
+
"@gjsify/unit": "^0.4.38",
|
|
56
56
|
"@types/node": "^25.9.1",
|
|
57
|
-
"typescript": "^
|
|
57
|
+
"typescript": "^6.0.3"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@girs/gio-2.0": "2.88.0-4.0.4",
|
|
61
61
|
"@girs/gjs": "4.0.4",
|
|
62
62
|
"@girs/glib-2.0": "2.88.0-4.0.4",
|
|
63
63
|
"@girs/soup-3.0": "3.6.6-4.0.4",
|
|
64
|
-
"@gjsify/formdata": "^0.4.
|
|
65
|
-
"@gjsify/http": "^0.4.
|
|
66
|
-
"@gjsify/url": "^0.4.
|
|
67
|
-
"@gjsify/utils": "^0.4.
|
|
64
|
+
"@gjsify/formdata": "^0.4.38",
|
|
65
|
+
"@gjsify/http": "^0.4.38",
|
|
66
|
+
"@gjsify/url": "^0.4.38",
|
|
67
|
+
"@gjsify/utils": "^0.4.38"
|
|
68
68
|
},
|
|
69
69
|
"gjsify": {
|
|
70
70
|
"runtimes": {
|
|
71
71
|
"gjs": "polyfill",
|
|
72
72
|
"node": "native",
|
|
73
|
-
"browser": "native"
|
|
73
|
+
"browser": "native",
|
|
74
|
+
"nativescript": "none"
|
|
74
75
|
}
|
|
75
76
|
},
|
|
76
77
|
"license": "MIT",
|