@gjsify/xmlhttprequest 0.3.13 → 0.3.15

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/index.js CHANGED
@@ -1,237 +1,253 @@
1
1
  import GLib from "gi://GLib?version=2.0";
2
2
  import System from "system";
3
3
  import fetch from "@gjsify/fetch";
4
+
5
+ //#region src/index.ts
4
6
  let _blobCounter = 0;
5
- class FakeBlob {
6
- _tmpPath;
7
- type;
8
- size;
9
- constructor(type, size) {
10
- this.type = type;
11
- this.size = size;
12
- }
13
- async arrayBuffer() {
14
- if (this._tmpPath) {
15
- const [ok, contents] = GLib.file_get_contents(this._tmpPath);
16
- if (ok) return contents.buffer;
17
- }
18
- return new ArrayBuffer(0);
19
- }
20
- async text() {
21
- return new TextDecoder().decode(await this.arrayBuffer());
22
- }
23
- stream() {
24
- const self = this;
25
- return new ReadableStream({
26
- async start(controller) {
27
- const buf = await self.arrayBuffer();
28
- controller.enqueue(new Uint8Array(buf));
29
- controller.close();
30
- }
31
- });
32
- }
33
- }
7
+ var FakeBlob = class {
8
+ _tmpPath;
9
+ type;
10
+ size;
11
+ constructor(type, size) {
12
+ this.type = type;
13
+ this.size = size;
14
+ }
15
+ async arrayBuffer() {
16
+ if (this._tmpPath) {
17
+ const [ok, contents] = GLib.file_get_contents(this._tmpPath);
18
+ if (ok) return contents.buffer;
19
+ }
20
+ return new ArrayBuffer(0);
21
+ }
22
+ async text() {
23
+ return new TextDecoder().decode(await this.arrayBuffer());
24
+ }
25
+ stream() {
26
+ const self = this;
27
+ return new ReadableStream({ async start(controller) {
28
+ const buf = await self.arrayBuffer();
29
+ controller.enqueue(new Uint8Array(buf));
30
+ controller.close();
31
+ } });
32
+ }
33
+ };
34
34
  function guessExt(url) {
35
- const lower = url.toLowerCase();
36
- if (lower.endsWith(".png")) return ".png";
37
- if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return ".jpg";
38
- if (lower.endsWith(".gif")) return ".gif";
39
- if (lower.endsWith(".svg")) return ".svg";
40
- if (lower.endsWith(".ttf")) return ".ttf";
41
- if (lower.endsWith(".otf")) return ".otf";
42
- if (lower.endsWith(".woff")) return ".woff";
43
- if (lower.endsWith(".woff2")) return ".woff2";
44
- if (lower.endsWith(".mp3")) return ".mp3";
45
- if (lower.endsWith(".wav")) return ".wav";
46
- if (lower.endsWith(".ogg")) return ".ogg";
47
- if (lower.endsWith(".tmx") || lower.endsWith(".xml")) return ".xml";
48
- if (lower.endsWith(".json")) return ".json";
49
- return ".bin";
35
+ const lower = url.toLowerCase();
36
+ if (lower.endsWith(".png")) return ".png";
37
+ if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return ".jpg";
38
+ if (lower.endsWith(".gif")) return ".gif";
39
+ if (lower.endsWith(".svg")) return ".svg";
40
+ if (lower.endsWith(".ttf")) return ".ttf";
41
+ if (lower.endsWith(".otf")) return ".otf";
42
+ if (lower.endsWith(".woff")) return ".woff";
43
+ if (lower.endsWith(".woff2")) return ".woff2";
44
+ if (lower.endsWith(".mp3")) return ".mp3";
45
+ if (lower.endsWith(".wav")) return ".wav";
46
+ if (lower.endsWith(".ogg")) return ".ogg";
47
+ if (lower.endsWith(".tmx") || lower.endsWith(".xml")) return ".xml";
48
+ if (lower.endsWith(".json")) return ".json";
49
+ return ".bin";
50
50
  }
51
51
  function guessMime(url) {
52
- const lower = url.toLowerCase();
53
- if (lower.endsWith(".png")) return "image/png";
54
- if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
55
- if (lower.endsWith(".gif")) return "image/gif";
56
- if (lower.endsWith(".svg")) return "image/svg+xml";
57
- if (lower.endsWith(".ttf")) return "font/truetype";
58
- if (lower.endsWith(".otf")) return "font/otf";
59
- if (lower.endsWith(".woff")) return "font/woff";
60
- if (lower.endsWith(".woff2")) return "font/woff2";
61
- if (lower.endsWith(".mp3")) return "audio/mpeg";
62
- if (lower.endsWith(".wav")) return "audio/wav";
63
- if (lower.endsWith(".ogg")) return "audio/ogg";
64
- if (lower.endsWith(".json")) return "application/json";
65
- return "application/octet-stream";
52
+ const lower = url.toLowerCase();
53
+ if (lower.endsWith(".png")) return "image/png";
54
+ if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
55
+ if (lower.endsWith(".gif")) return "image/gif";
56
+ if (lower.endsWith(".svg")) return "image/svg+xml";
57
+ if (lower.endsWith(".ttf")) return "font/truetype";
58
+ if (lower.endsWith(".otf")) return "font/otf";
59
+ if (lower.endsWith(".woff")) return "font/woff";
60
+ if (lower.endsWith(".woff2")) return "font/woff2";
61
+ if (lower.endsWith(".mp3")) return "audio/mpeg";
62
+ if (lower.endsWith(".wav")) return "audio/wav";
63
+ if (lower.endsWith(".ogg")) return "audio/ogg";
64
+ if (lower.endsWith(".json")) return "application/json";
65
+ return "application/octet-stream";
66
66
  }
67
+ /** Read a file:// URL synchronously using GLib. */
67
68
  async function readFileUrl(url) {
68
- const path = GLib.filename_from_uri(url)[0];
69
- const [ok, contents] = GLib.file_get_contents(path);
70
- if (!ok) throw new Error(`XMLHttpRequest: cannot read file ${path}`);
71
- const src = contents;
72
- const copy = new Uint8Array(src.byteLength);
73
- copy.set(src);
74
- return copy.buffer;
69
+ const path = GLib.filename_from_uri(url)[0];
70
+ const [ok, contents] = GLib.file_get_contents(path);
71
+ if (!ok) throw new Error(`XMLHttpRequest: cannot read file ${path}`);
72
+ const src = contents;
73
+ const copy = new Uint8Array(src.byteLength);
74
+ copy.set(src);
75
+ return copy.buffer;
75
76
  }
77
+ /** Write bytes to a temp file and return the path. */
76
78
  function writeToTmp(bytes, ext) {
77
- const tmpPath = GLib.build_filenamev([
78
- GLib.get_tmp_dir(),
79
- `gjsify-blob-${_blobCounter++}${ext}`
80
- ]);
81
- GLib.file_set_contents(tmpPath, bytes);
82
- return tmpPath;
83
- }
84
- class XMLHttpRequest {
85
- // State
86
- UNSENT = 0;
87
- OPENED = 1;
88
- HEADERS_RECEIVED = 2;
89
- LOADING = 3;
90
- DONE = 4;
91
- readyState = 0;
92
- status = 0;
93
- statusText = "";
94
- response = null;
95
- responseText = "";
96
- responseType = "";
97
- responseURL = "";
98
- timeout = 0;
99
- withCredentials = false;
100
- // Event handler properties
101
- onloadstart = null;
102
- onprogress = null;
103
- onload = null;
104
- onloadend = null;
105
- onerror = null;
106
- onabort = null;
107
- ontimeout = null;
108
- onreadystatechange = null;
109
- _url = "";
110
- _method = "GET";
111
- _aborted = false;
112
- _listeners = {};
113
- open(method, url, _async = true) {
114
- this._method = method.toUpperCase();
115
- this._url = url;
116
- this._aborted = false;
117
- this.readyState = this.OPENED;
118
- }
119
- setRequestHeader(_name, _value) {
120
- }
121
- overrideMimeType(_mime) {
122
- }
123
- send(_body) {
124
- let url = this._url;
125
- const responseType = this.responseType;
126
- const DEBUG = globalThis.__GJSIFY_DEBUG_XHR === true;
127
- if (url.startsWith("/") && !url.startsWith("//")) {
128
- const prog = System.programPath ?? System.programInvocationName ?? "";
129
- if (prog) {
130
- const programDir = GLib.path_get_dirname(prog);
131
- url = `file://${programDir}${this._url}`;
132
- if (DEBUG) console.log(`[xmlhttprequest] rewrite ${this._url} \u2192 ${url}`);
133
- }
134
- }
135
- if (DEBUG) console.log(`[xmlhttprequest] ${this._method} ${url} responseType=${responseType}`);
136
- const doFetch = () => {
137
- if (url.startsWith("file://")) {
138
- return readFileUrl(url);
139
- }
140
- return fetch(url, { method: this._method }).then((r) => {
141
- if (DEBUG) console.log(`[xmlhttprequest] fetch ok ${url} status=${r.status}`);
142
- this.status = r.status === 0 ? 200 : r.status;
143
- this.statusText = r.statusText || "OK";
144
- this.responseURL = r.url || url;
145
- return r.arrayBuffer();
146
- });
147
- };
148
- this.readyState = this.LOADING;
149
- this._emit("loadstart", { loaded: 0, total: 0, lengthComputable: false });
150
- Promise.resolve().then(doFetch).then((arrBuf) => {
151
- if (this._aborted) return;
152
- if (this.status === 0) this.status = 200;
153
- if (!this.statusText) this.statusText = "OK";
154
- this.readyState = this.DONE;
155
- const bytes = new Uint8Array(arrBuf);
156
- const len = bytes.byteLength;
157
- this._emit("progress", { loaded: len, total: len, lengthComputable: true });
158
- if (responseType === "blob" || responseType === "") {
159
- const ext = guessExt(url);
160
- const tmpPath = writeToTmp(bytes, ext);
161
- const blob = new FakeBlob(guessMime(url), len);
162
- blob._tmpPath = tmpPath;
163
- this.response = blob;
164
- } else if (responseType === "arraybuffer") {
165
- this.response = arrBuf;
166
- } else if (responseType === "json") {
167
- this.response = JSON.parse(new TextDecoder().decode(bytes));
168
- } else {
169
- this.responseText = new TextDecoder().decode(bytes);
170
- this.response = this.responseText;
171
- }
172
- this._emit("load", { target: this, loaded: len, total: len, lengthComputable: true });
173
- this._emit("loadend", { target: this, loaded: len, total: len, lengthComputable: true });
174
- if (this.onreadystatechange) this.onreadystatechange({});
175
- }).catch((err) => {
176
- if (this._aborted) return;
177
- console.warn(`[xmlhttprequest] ${this._method} ${url} \u2014 ${err?.message ?? err}`);
178
- this.readyState = this.DONE;
179
- this._emit("error", { error: err });
180
- this._emit("loadend", {});
181
- });
182
- }
183
- abort() {
184
- this._aborted = true;
185
- this.readyState = this.DONE;
186
- this._emit("abort", {});
187
- this._emit("loadend", {});
188
- }
189
- addEventListener(type, fn) {
190
- (this._listeners[type] ??= []).push(fn);
191
- }
192
- removeEventListener(type, fn) {
193
- this._listeners[type] = (this._listeners[type] ?? []).filter((f) => f !== fn);
194
- }
195
- getResponseHeader(_name) {
196
- return null;
197
- }
198
- getAllResponseHeaders() {
199
- return "";
200
- }
201
- _emit(type, event) {
202
- const handler = this["on" + type];
203
- if (typeof handler === "function") handler.call(this, { type, ...event });
204
- for (const fn of this._listeners[type] ?? []) fn.call(this, { type, ...event });
205
- }
79
+ const tmpPath = GLib.build_filenamev([GLib.get_tmp_dir(), `gjsify-blob-${_blobCounter++}${ext}`]);
80
+ GLib.file_set_contents(tmpPath, bytes);
81
+ return tmpPath;
206
82
  }
83
+ var XMLHttpRequest = class {
84
+ UNSENT = 0;
85
+ OPENED = 1;
86
+ HEADERS_RECEIVED = 2;
87
+ LOADING = 3;
88
+ DONE = 4;
89
+ readyState = 0;
90
+ status = 0;
91
+ statusText = "";
92
+ response = null;
93
+ responseText = "";
94
+ responseType = "";
95
+ responseURL = "";
96
+ timeout = 0;
97
+ withCredentials = false;
98
+ onloadstart = null;
99
+ onprogress = null;
100
+ onload = null;
101
+ onloadend = null;
102
+ onerror = null;
103
+ onabort = null;
104
+ ontimeout = null;
105
+ onreadystatechange = null;
106
+ _url = "";
107
+ _method = "GET";
108
+ _aborted = false;
109
+ _listeners = {};
110
+ open(method, url, _async = true) {
111
+ this._method = method.toUpperCase();
112
+ this._url = url;
113
+ this._aborted = false;
114
+ this.readyState = this.OPENED;
115
+ }
116
+ setRequestHeader(_name, _value) {}
117
+ overrideMimeType(_mime) {}
118
+ send(_body) {
119
+ let url = this._url;
120
+ const responseType = this.responseType;
121
+ const DEBUG = globalThis.__GJSIFY_DEBUG_XHR === true;
122
+ if (url.startsWith("/") && !url.startsWith("//")) {
123
+ const prog = System.programPath ?? System.programInvocationName ?? "";
124
+ if (prog) {
125
+ const programDir = GLib.path_get_dirname(prog);
126
+ url = `file://${programDir}${this._url}`;
127
+ if (DEBUG) console.log(`[xmlhttprequest] rewrite ${this._url} → ${url}`);
128
+ }
129
+ }
130
+ if (DEBUG) console.log(`[xmlhttprequest] ${this._method} ${url} responseType=${responseType}`);
131
+ const doFetch = () => {
132
+ if (url.startsWith("file://")) {
133
+ return readFileUrl(url);
134
+ }
135
+ return fetch(url, { method: this._method }).then((r) => {
136
+ if (DEBUG) console.log(`[xmlhttprequest] fetch ok ${url} status=${r.status}`);
137
+ this.status = r.status === 0 ? 200 : r.status;
138
+ this.statusText = r.statusText || "OK";
139
+ this.responseURL = r.url || url;
140
+ return r.arrayBuffer();
141
+ });
142
+ };
143
+ this.readyState = this.LOADING;
144
+ this._emit("loadstart", {
145
+ loaded: 0,
146
+ total: 0,
147
+ lengthComputable: false
148
+ });
149
+ Promise.resolve().then(doFetch).then((arrBuf) => {
150
+ if (this._aborted) return;
151
+ if (this.status === 0) this.status = 200;
152
+ if (!this.statusText) this.statusText = "OK";
153
+ this.readyState = this.DONE;
154
+ const bytes = new Uint8Array(arrBuf);
155
+ const len = bytes.byteLength;
156
+ this._emit("progress", {
157
+ loaded: len,
158
+ total: len,
159
+ lengthComputable: true
160
+ });
161
+ if (responseType === "blob" || responseType === "") {
162
+ const ext = guessExt(url);
163
+ const tmpPath = writeToTmp(bytes, ext);
164
+ const blob = new FakeBlob(guessMime(url), len);
165
+ blob._tmpPath = tmpPath;
166
+ this.response = blob;
167
+ } else if (responseType === "arraybuffer") {
168
+ this.response = arrBuf;
169
+ } else if (responseType === "json") {
170
+ this.response = JSON.parse(new TextDecoder().decode(bytes));
171
+ } else {
172
+ this.responseText = new TextDecoder().decode(bytes);
173
+ this.response = this.responseText;
174
+ }
175
+ this._emit("load", {
176
+ target: this,
177
+ loaded: len,
178
+ total: len,
179
+ lengthComputable: true
180
+ });
181
+ this._emit("loadend", {
182
+ target: this,
183
+ loaded: len,
184
+ total: len,
185
+ lengthComputable: true
186
+ });
187
+ if (this.onreadystatechange) this.onreadystatechange({});
188
+ }).catch((err) => {
189
+ if (this._aborted) return;
190
+ console.warn(`[xmlhttprequest] ${this._method} ${url} — ${err?.message ?? err}`);
191
+ this.readyState = this.DONE;
192
+ this._emit("error", { error: err });
193
+ this._emit("loadend", {});
194
+ });
195
+ }
196
+ abort() {
197
+ this._aborted = true;
198
+ this.readyState = this.DONE;
199
+ this._emit("abort", {});
200
+ this._emit("loadend", {});
201
+ }
202
+ addEventListener(type, fn) {
203
+ (this._listeners[type] ??= []).push(fn);
204
+ }
205
+ removeEventListener(type, fn) {
206
+ this._listeners[type] = (this._listeners[type] ?? []).filter((f) => f !== fn);
207
+ }
208
+ getResponseHeader(_name) {
209
+ return null;
210
+ }
211
+ getAllResponseHeaders() {
212
+ return "";
213
+ }
214
+ _emit(type, event) {
215
+ const handler = this["on" + type];
216
+ if (typeof handler === "function") handler.call(this, {
217
+ type,
218
+ ...event
219
+ });
220
+ for (const fn of this._listeners[type] ?? []) fn.call(this, {
221
+ type,
222
+ ...event
223
+ });
224
+ }
225
+ };
207
226
  function installObjectURLSupport() {
208
- if (typeof URL.createObjectURL !== "function" || URL.__gjsify_objecturl !== true) {
209
- const _objectURLPaths = /* @__PURE__ */ new Map();
210
- URL.createObjectURL = function(blob) {
211
- if (blob._tmpPath) {
212
- const url = `file://${blob._tmpPath}`;
213
- _objectURLPaths.set(url, blob._tmpPath);
214
- return url;
215
- }
216
- console.warn("[createObjectURL] received non-FakeBlob \u2014 cannot create file:// URL");
217
- return "file:///dev/null";
218
- };
219
- URL.revokeObjectURL = function(url) {
220
- const path = _objectURLPaths.get(url);
221
- if (path) {
222
- try {
223
- const file = GLib.build_filenamev([path]);
224
- GLib.unlink(file);
225
- } catch {
226
- }
227
- _objectURLPaths.delete(url);
228
- }
229
- };
230
- URL.__gjsify_objecturl = true;
231
- }
227
+ if (typeof URL.createObjectURL !== "function" || URL.__gjsify_objecturl !== true) {
228
+ const _objectURLPaths = new Map();
229
+ URL.createObjectURL = function(blob) {
230
+ if (blob._tmpPath) {
231
+ const url = `file://${blob._tmpPath}`;
232
+ _objectURLPaths.set(url, blob._tmpPath);
233
+ return url;
234
+ }
235
+ console.warn("[createObjectURL] received non-FakeBlob cannot create file:// URL");
236
+ return "file:///dev/null";
237
+ };
238
+ URL.revokeObjectURL = function(url) {
239
+ const path = _objectURLPaths.get(url);
240
+ if (path) {
241
+ try {
242
+ const file = GLib.build_filenamev([path]);
243
+ GLib.unlink(file);
244
+ } catch {}
245
+ _objectURLPaths.delete(url);
246
+ }
247
+ };
248
+ URL.__gjsify_objecturl = true;
249
+ }
232
250
  }
233
- export {
234
- FakeBlob,
235
- XMLHttpRequest,
236
- installObjectURLSupport
237
- };
251
+
252
+ //#endregion
253
+ export { FakeBlob, XMLHttpRequest, installObjectURLSupport };
@@ -1,5 +1,9 @@
1
1
  import { XMLHttpRequest, installObjectURLSupport } from "./index.js";
2
+
3
+ //#region src/register.ts
2
4
  if (typeof globalThis.XMLHttpRequest === "undefined") {
3
- globalThis.XMLHttpRequest = XMLHttpRequest;
5
+ globalThis.XMLHttpRequest = XMLHttpRequest;
4
6
  }
5
7
  installObjectURLSupport();
8
+
9
+ //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/xmlhttprequest",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "XMLHttpRequest and URL.createObjectURL/revokeObjectURL for GJS — backed by @gjsify/fetch and GLib",
5
5
  "module": "lib/esm/index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -32,13 +32,13 @@
32
32
  "web-api"
33
33
  ],
34
34
  "dependencies": {
35
- "@girs/gio-2.0": "^2.88.0-4.0.0-rc.9",
36
- "@girs/gjs": "^4.0.0-rc.9",
37
- "@girs/glib-2.0": "^2.88.0-4.0.0-rc.9",
38
- "@gjsify/fetch": "^0.3.13"
35
+ "@girs/gio-2.0": "2.88.0-4.0.0-rc.9",
36
+ "@girs/gjs": "4.0.0-rc.9",
37
+ "@girs/glib-2.0": "2.88.0-4.0.0-rc.9",
38
+ "@gjsify/fetch": "^0.3.15"
39
39
  },
40
40
  "devDependencies": {
41
- "@gjsify/cli": "^0.3.13",
41
+ "@gjsify/cli": "^0.3.15",
42
42
  "@types/node": "^25.6.0",
43
43
  "typescript": "^6.0.3"
44
44
  }