@gjsify/fetch 0.24.1 → 0.25.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.
@@ -15,7 +15,6 @@ export class FetchError extends FetchBaseError {
15
15
  super(message, type);
16
16
  // When err.type is `system`, err.erroredSysCall contains system error and err.code contains system error code
17
17
  if (systemError) {
18
- // eslint-disable-next-line no-multi-assign
19
18
  this.code = this.errno = systemError.code;
20
19
  this.erroredSysCall = systemError.syscall;
21
20
  }
package/lib/esm/index.js CHANGED
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";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 rewriteRootRelativeUrl(e){if(typeof e!=`string`||!e.startsWith(`/`)||e.startsWith(`//`))return e;let t=globalThis,n=t.__GJSIFY_DEBUG_FETCH===!0;try{let r=t.imports?.system?.programPath??t.imports?.system?.programInvocationName??``;if(!r)return e;let i=`file://${y.path_get_dirname(r)}${e}`;return n&&console.log(`[fetch] rewrite ${e} → ${i}`),i}catch(t){return n&&console.warn(`[fetch] rewrite FAILED: ${t instanceof Error?t.message:String(t)}`),e}}async function fetch(t,m={}){t=rewriteRootRelativeUrl(t);let h=new d(t,m),{parsedURL:v,options:x}=f(h);if(!b.has(v.protocol))throw TypeError(`@gjsify/fetch cannot load ${t}. URL scheme "${v.protocol.replace(/:$/,``)}" is not supported.`);if(v.protocol===`data:`){let{buffer:e,typeFull:t}=s(h.url);return new l(Buffer.from(e),{headers:{"Content-Type":t}})}if(v.protocol===`file:`){let e=globalThis.__GJSIFY_DEBUG_FETCH===!0;e&&console.log(`[fetch] file:// ${h.url}`);try{let t=y.filename_from_uri(h.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 ${h.url} failed, reason: ${r.message}`,`system`,r)}}let{signal:S}=h;if(S&&S.aborted)throw new p(`The operation was aborted.`);let C,w;try{let e=await h._send(x);C=e.readable,w=e.cancellable}catch(e){if(S&&S.aborted)throw new p(`The operation was aborted.`);let t=e instanceof Error?e:Error(String(e));throw new n(`request to ${h.url} failed, reason: ${t.message}`,`system`,t)}let abortHandler=()=>{w.cancel()};S&&S.addEventListener(`abort`,abortHandler,{once:!0});let finalize=()=>{S&&S.removeEventListener(`abort`,abortHandler)};w.connect(()=>{C.destroy(new p(`The operation was aborted.`))}),C.on(`error`,e=>{finalize()});let T=h._message,E=o._newFromSoupMessage(T),D=T.status_code,O=T.get_reason_phrase();if(c(D)){let e=E.get(`Location`),t=null;try{t=e===null?null:new g(e,h.url)}catch{if(h.redirect!==`manual`)throw finalize(),new n(`uri requested responds with an invalid redirect URL: ${e}`,`invalid-redirect`)}switch(h.redirect){case`error`:throw finalize(),new n(`uri requested responds with a redirect, redirect mode is set to error: ${h.url}`,`no-redirect`);case`manual`:break;case`follow`:{if(t===null)break;if(h.counter>=h.follow)throw finalize(),new n(`maximum redirect reached at: ${h.url}`,`max-redirect`);let e={headers:new o(h.headers),follow:h.follow,counter:h.counter+1,agent:h.agent,compress:h.compress,method:h.method,body:a(h),signal:h.signal,size:h.size,referrer:h.referrer,referrerPolicy:h.referrerPolicy};if(!r(h.url,t)||!i(h.url,t))for(let t of[`authorization`,`www-authenticate`,`cookie`,`cookie2`])e.headers.delete(t);if(D!==303&&h.body&&m.body instanceof _.Readable)throw finalize(),new n(`Cannot follow redirect with body being a readable stream`,`unsupported-redirect`);(D===303||(D===301||D===302)&&h.method===`POST`)&&(e.method=`GET`,e.body=void 0,e.headers.delete(`content-length`));let s=u(E);return s&&(e.referrerPolicy=s),finalize(),fetch(new d(t,e))}default:throw TypeError(`Redirect option '${h.redirect}' is not a valid value of RequestRedirect`)}}let k={url:h.url,status:D,statusText:O,headers:E,size:h.size,counter:h.counter,highWaterMark:h.highWaterMark},A=E.get(`Content-Encoding`);if(!h.compress||h.method===`HEAD`||A===null||D===204||D===304)return finalize(),new l(C,k);if(typeof DecompressionStream<`u`){let t=null;if(A===`gzip`||A===`x-gzip`?t=`gzip`:(A===`deflate`||A===`x-deflate`)&&(t=`deflate`),t){let n=new e([await new l(C,k).arrayBuffer()]).stream().pipeThrough(new DecompressionStream(t));return finalize(),new l(n,k)}}return finalize(),new l(C,k)}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,fetch as default,c as isRedirect};
1
+ import"./_virtual/_rolldown/runtime.js";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";import b from"system";const x=new Set([`data:`,`http:`,`https:`,`file:`]);function rewriteRootRelativeUrl(e){if(typeof e!=`string`||!e.startsWith(`/`)||e.startsWith(`//`))return e;let t=globalThis.__GJSIFY_DEBUG_FETCH===!0;try{let n=b.programPath??b.programInvocationName??``;if(!n)return e;let r=`file://${y.path_get_dirname(n)}${e}`;return t&&console.log(`[fetch] rewrite ${e} → ${r}`),r}catch(n){return t&&console.warn(`[fetch] rewrite FAILED: ${n instanceof Error?n.message:String(n)}`),e}}async function fetch(t,m={}){t=rewriteRootRelativeUrl(t);let h=new d(t,m),{parsedURL:v,options:b}=f(h);if(!x.has(v.protocol))throw TypeError(`@gjsify/fetch cannot load ${t}. URL scheme "${v.protocol.replace(/:$/,``)}" is not supported.`);if(v.protocol===`data:`){let{buffer:e,typeFull:t}=s(h.url);return new l(Buffer.from(e),{headers:{"Content-Type":t}})}if(v.protocol===`file:`){let e=globalThis.__GJSIFY_DEBUG_FETCH===!0;e&&console.log(`[fetch] file:// ${h.url}`);try{let t=y.filename_from_uri(h.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 ${h.url} failed, reason: ${r.message}`,`system`,r)}}let{signal:S}=h;if(S&&S.aborted)throw new p(`The operation was aborted.`);let C,w;try{let e=await h._send(b);C=e.readable,w=e.cancellable}catch(e){if(S&&S.aborted)throw new p(`The operation was aborted.`);let t=e instanceof Error?e:Error(String(e));throw new n(`request to ${h.url} failed, reason: ${t.message}`,`system`,t)}let abortHandler=()=>{w.cancel()};S&&S.addEventListener(`abort`,abortHandler,{once:!0});let finalize=()=>{S&&S.removeEventListener(`abort`,abortHandler)};w.connect(()=>{C.destroy(new p(`The operation was aborted.`))}),C.on(`error`,e=>{finalize()});let T=h._message,E=o._newFromSoupMessage(T),D=T.status_code,O=T.get_reason_phrase();if(c(D)){let e=E.get(`Location`),t=null;try{t=e===null?null:new g(e,h.url)}catch{if(h.redirect!==`manual`)throw finalize(),new n(`uri requested responds with an invalid redirect URL: ${e}`,`invalid-redirect`)}switch(h.redirect){case`error`:throw finalize(),new n(`uri requested responds with a redirect, redirect mode is set to error: ${h.url}`,`no-redirect`);case`manual`:break;case`follow`:{if(t===null)break;if(h.counter>=h.follow)throw finalize(),new n(`maximum redirect reached at: ${h.url}`,`max-redirect`);let e={headers:new o(h.headers),follow:h.follow,counter:h.counter+1,agent:h.agent,compress:h.compress,method:h.method,body:a(h),signal:h.signal,size:h.size,referrer:h.referrer,referrerPolicy:h.referrerPolicy};if(!r(h.url,t)||!i(h.url,t))for(let t of[`authorization`,`www-authenticate`,`cookie`,`cookie2`])e.headers.delete(t);if(D!==303&&h.body&&m.body instanceof _.Readable)throw finalize(),new n(`Cannot follow redirect with body being a readable stream`,`unsupported-redirect`);(D===303||(D===301||D===302)&&h.method===`POST`)&&(e.method=`GET`,e.body=void 0,e.headers.delete(`content-length`));let s=u(E);return s&&(e.referrerPolicy=s),finalize(),fetch(new d(t,e))}default:throw TypeError(`Redirect option '${h.redirect}' is not a valid value of RequestRedirect`)}}let k={url:h.url,status:D,statusText:O,headers:E,size:h.size,counter:h.counter,highWaterMark:h.highWaterMark},A=E.get(`Content-Encoding`);if(!h.compress||h.method===`HEAD`||A===null||D===204||D===304)return finalize(),new l(C,k);if(typeof DecompressionStream<`u`){let t=null;if(A===`gzip`||A===`x-gzip`?t=`gzip`:(A===`deflate`||A===`x-deflate`)&&(t=`deflate`),t){let n=new e([await new l(C,k).arrayBuffer()]).stream().pipeThrough(new DecompressionStream(t));return finalize(),new l(n,k)}}return finalize(),new l(C,k)}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,fetch as default,c as isRedirect};
package/lib/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // Copyright (c) node-fetch contributors. MIT license.
4
4
  // Modifications: Rewritten for GJS using libsoup 3.0 (Soup.Session)
5
5
  import GLib from '@girs/glib-2.0';
6
+ import System from 'system';
6
7
  import Stream from 'node:stream';
7
8
  import { parseDataUri } from './utils/data-uri.js';
8
9
  import { clone } from './body.js';
@@ -29,8 +30,17 @@ function rewriteRootRelativeUrl(input) {
29
30
  const _g = globalThis;
30
31
  const DEBUG = _g.__GJSIFY_DEBUG_FETCH === true;
31
32
  try {
32
- // GJS-only: derive program dir from System.programInvocationName.
33
- const programPath = _g.imports?.system?.programPath ?? _g.imports?.system?.programInvocationName ?? '';
33
+ // Program dir from the `system` built-in — the SAME source
34
+ // `@gjsify/xmlhttprequest` and `@gjsify/dom-elements` use for this
35
+ // identical rewrite. It used to read `globalThis.imports.system`, which
36
+ // exists only on GJS: on the node-gi reverse bridge the probe was
37
+ // `undefined`, the path was handed to the runtime's native `URL`
38
+ // unchanged, and every root-relative `fetch()` died with
39
+ // `ERR_INVALID_URL` — while XHR-loaded assets in the same app resolved
40
+ // fine, because those two siblings import the module. That asymmetry is
41
+ // what made the excalibur-jelly-jumper showcase render an empty level on
42
+ // node/bun/deno: its Tiled map is the one asset fetched rather than XHR'd.
43
+ const programPath = System.programPath ?? System.programInvocationName ?? '';
34
44
  if (!programPath)
35
45
  return input;
36
46
  const dir = GLib.path_get_dirname(programPath);
package/lib/response.js CHANGED
@@ -22,7 +22,6 @@ export class Response extends Body {
22
22
  _inputStream = null;
23
23
  constructor(body = null, options = {}) {
24
24
  super(body, options);
25
- // eslint-disable-next-line no-eq-null, eqeqeq, no-negated-condition
26
25
  const status = options.status != null ? options.status : 200;
27
26
  const headers = new Headers(options.headers);
28
27
  if (body !== null && !headers.has('Content-Type')) {
@@ -16,7 +16,6 @@ import { isIP } from 'node:net';
16
16
  export function stripURLForUseAsAReferrer(url, originOnly = false) {
17
17
  // 1. If url is null, return no referrer.
18
18
  if (url == null || url === 'no-referrer') {
19
- // eslint-disable-line no-eq-null, eqeqeq
20
19
  return 'no-referrer';
21
20
  }
22
21
  const u = new URL(url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/fetch",
3
- "version": "0.24.1",
3
+ "version": "0.25.1",
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,8 +51,8 @@
51
51
  "fetch"
52
52
  ],
53
53
  "devDependencies": {
54
- "@gjsify/cli": "^0.24.1",
55
- "@gjsify/unit": "^0.24.1",
54
+ "@gjsify/cli": "^0.25.1",
55
+ "@gjsify/unit": "^0.25.1",
56
56
  "@types/node": "^25.9.2",
57
57
  "typescript": "^6.0.3"
58
58
  },
@@ -61,11 +61,11 @@
61
61
  "@girs/gjs": "^4.1.0",
62
62
  "@girs/glib-2.0": "^4.1.0",
63
63
  "@girs/soup-3.0": "^4.1.0",
64
- "@gjsify/dom-events": "^0.24.1",
65
- "@gjsify/formdata": "^0.24.1",
66
- "@gjsify/http": "^0.24.1",
67
- "@gjsify/url": "^0.24.1",
68
- "@gjsify/utils": "^0.24.1"
64
+ "@gjsify/dom-events": "^0.25.1",
65
+ "@gjsify/formdata": "^0.25.1",
66
+ "@gjsify/http": "^0.25.1",
67
+ "@gjsify/url": "^0.25.1",
68
+ "@gjsify/utils": "^0.25.1"
69
69
  },
70
70
  "gjsify": {
71
71
  "runtimes": {