@gjsify/url 0.4.0 → 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/package.json +42 -39
- package/src/index.spec.ts +0 -1084
- package/src/index.ts +0 -601
- package/src/test.ts +0 -4
- package/test/file.txt +0 -1
- package/tsconfig.json +0 -29
- package/tsconfig.tsbuildinfo +0 -1
package/src/index.ts
DELETED
|
@@ -1,601 +0,0 @@
|
|
|
1
|
-
// Node.js url module for GJS
|
|
2
|
-
// Uses GLib.Uri for WHATWG URL parsing since globalThis.URL is not available in GJS 1.86
|
|
3
|
-
// See refs/deno/ext/node/polyfills/url.ts, refs/bun/src/js/node/url.ts, refs/node/lib/url.js
|
|
4
|
-
|
|
5
|
-
import GLib from '@girs/glib-2.0';
|
|
6
|
-
|
|
7
|
-
// ---- URLSearchParams ----
|
|
8
|
-
|
|
9
|
-
const PARSE_FLAGS = GLib.UriFlags.HAS_PASSWORD | GLib.UriFlags.ENCODED | GLib.UriFlags.SCHEME_NORMALIZE;
|
|
10
|
-
|
|
11
|
-
export class URLSearchParams {
|
|
12
|
-
_entries: [string, string][] = [];
|
|
13
|
-
|
|
14
|
-
constructor(init?: string | Record<string, string> | [string, string][] | URLSearchParams) {
|
|
15
|
-
if (!init) return;
|
|
16
|
-
if (typeof init === 'string') {
|
|
17
|
-
const s = init.startsWith('?') ? init.slice(1) : init;
|
|
18
|
-
if (s) {
|
|
19
|
-
for (const pair of s.split('&')) {
|
|
20
|
-
const eqIdx = pair.indexOf('=');
|
|
21
|
-
if (eqIdx === -1) {
|
|
22
|
-
this._entries.push([decodeComponent(pair), '']);
|
|
23
|
-
} else {
|
|
24
|
-
this._entries.push([decodeComponent(pair.slice(0, eqIdx)), decodeComponent(pair.slice(eqIdx + 1))]);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
} else if (Array.isArray(init)) {
|
|
29
|
-
for (const [k, v] of init) {
|
|
30
|
-
this._entries.push([String(k), String(v)]);
|
|
31
|
-
}
|
|
32
|
-
} else if (init instanceof URLSearchParams) {
|
|
33
|
-
this._entries = init._entries.map(([k, v]) => [k, v] as [string, string]);
|
|
34
|
-
} else {
|
|
35
|
-
for (const key of Object.keys(init)) {
|
|
36
|
-
this._entries.push([key, String(init[key])]);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
get(name: string): string | null {
|
|
42
|
-
for (const [k, v] of this._entries) {
|
|
43
|
-
if (k === name) return v;
|
|
44
|
-
}
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
getAll(name: string): string[] {
|
|
49
|
-
return this._entries.filter(([k]) => k === name).map(([, v]) => v);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
set(name: string, value: string): void {
|
|
53
|
-
let found = false;
|
|
54
|
-
this._entries = this._entries.filter(([k]) => {
|
|
55
|
-
if (k === name) {
|
|
56
|
-
if (!found) { found = true; return true; }
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
return true;
|
|
60
|
-
});
|
|
61
|
-
if (found) {
|
|
62
|
-
for (let i = 0; i < this._entries.length; i++) {
|
|
63
|
-
if (this._entries[i][0] === name) { this._entries[i][1] = value; break; }
|
|
64
|
-
}
|
|
65
|
-
} else {
|
|
66
|
-
this._entries.push([name, value]);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
has(name: string): boolean {
|
|
71
|
-
return this._entries.some(([k]) => k === name);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
delete(name: string): void {
|
|
75
|
-
this._entries = this._entries.filter(([k]) => k !== name);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
append(name: string, value: string): void {
|
|
79
|
-
this._entries.push([name, value]);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
sort(): void {
|
|
83
|
-
this._entries.sort((a, b) => {
|
|
84
|
-
if (a[0] < b[0]) return -1;
|
|
85
|
-
if (a[0] > b[0]) return 1;
|
|
86
|
-
return 0;
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
toString(): string {
|
|
91
|
-
return this._entries.map(([k, v]) => encodeComponent(k) + '=' + encodeComponent(v)).join('&');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
forEach(callback: (value: string, key: string, parent: URLSearchParams) => void): void {
|
|
95
|
-
for (const [k, v] of this._entries) {
|
|
96
|
-
callback(v, k, this);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
*entries(): IterableIterator<[string, string]> {
|
|
101
|
-
yield* this._entries;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
*keys(): IterableIterator<string> {
|
|
105
|
-
for (const [k] of this._entries) yield k;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
*values(): IterableIterator<string> {
|
|
109
|
-
for (const [, v] of this._entries) yield v;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
[Symbol.iterator](): IterableIterator<[string, string]> {
|
|
113
|
-
return this.entries();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
get size(): number {
|
|
117
|
-
return this._entries.length;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function decodeComponent(s: string): string {
|
|
122
|
-
try { return decodeURIComponent(s.replace(/\+/g, ' ')); } catch { return s; }
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function encodeComponent(s: string): string {
|
|
126
|
-
return encodeURIComponent(s).replace(/%20/g, '+');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// ---- URL class using GLib.Uri ----
|
|
130
|
-
|
|
131
|
-
export class URL {
|
|
132
|
-
#uri: any; // GLib.Uri
|
|
133
|
-
#searchParams: URLSearchParams;
|
|
134
|
-
|
|
135
|
-
constructor(url: string | URL, base?: string | URL) {
|
|
136
|
-
const urlStr = url instanceof URL ? url.href : String(url);
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
if (base !== undefined) {
|
|
140
|
-
const baseStr = base instanceof URL ? base.href : String(base);
|
|
141
|
-
const baseUri = GLib.Uri.parse(baseStr, PARSE_FLAGS);
|
|
142
|
-
this.#uri = baseUri.parse_relative(urlStr, PARSE_FLAGS);
|
|
143
|
-
} else {
|
|
144
|
-
this.#uri = GLib.Uri.parse(urlStr, PARSE_FLAGS);
|
|
145
|
-
}
|
|
146
|
-
} catch (e: any) {
|
|
147
|
-
throw new TypeError(`Invalid URL: ${urlStr}`);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (!this.#uri) {
|
|
151
|
-
throw new TypeError(`Invalid URL: ${urlStr}`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
this.#searchParams = new URLSearchParams(this.#uri.get_query() || '');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
get protocol(): string {
|
|
158
|
-
return this.#uri.get_scheme() + ':';
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
get hostname(): string {
|
|
162
|
-
return (this.#uri.get_host() || '').toLowerCase();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
get port(): string {
|
|
166
|
-
const p = this.#uri.get_port();
|
|
167
|
-
if (p === -1) return '';
|
|
168
|
-
// WHATWG URL spec: port should be empty string for default ports
|
|
169
|
-
const scheme = this.#uri.get_scheme();
|
|
170
|
-
if ((scheme === 'http' || scheme === 'ws') && p === 80) return '';
|
|
171
|
-
if ((scheme === 'https' || scheme === 'wss') && p === 443) return '';
|
|
172
|
-
if (scheme === 'ftp' && p === 21) return '';
|
|
173
|
-
return String(p);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
get host(): string {
|
|
177
|
-
const hostname = this.hostname;
|
|
178
|
-
const port = this.port;
|
|
179
|
-
return port ? `${hostname}:${port}` : hostname;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
get pathname(): string {
|
|
183
|
-
return this.#uri.get_path() || '/';
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
get search(): string {
|
|
187
|
-
const q = this.#uri.get_query();
|
|
188
|
-
return q ? '?' + q : '';
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
get hash(): string {
|
|
192
|
-
const f = this.#uri.get_fragment();
|
|
193
|
-
return f ? '#' + f : '';
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
get origin(): string {
|
|
197
|
-
const p = this.protocol;
|
|
198
|
-
if (p === 'http:' || p === 'https:' || p === 'ftp:') {
|
|
199
|
-
return `${p}//${this.host}`;
|
|
200
|
-
}
|
|
201
|
-
return 'null';
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
get username(): string {
|
|
205
|
-
return this.#uri.get_user() || '';
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
get password(): string {
|
|
209
|
-
return this.#uri.get_password() || '';
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
get href(): string {
|
|
213
|
-
let result = this.protocol;
|
|
214
|
-
const scheme = this.#uri.get_scheme();
|
|
215
|
-
const isSpecial = scheme === 'http' || scheme === 'https' || scheme === 'ftp' || scheme === 'file' || scheme === 'ws' || scheme === 'wss';
|
|
216
|
-
|
|
217
|
-
if (isSpecial || this.hostname) {
|
|
218
|
-
result += '//';
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const user = this.username;
|
|
222
|
-
const pass = this.password;
|
|
223
|
-
if (user) {
|
|
224
|
-
result += user;
|
|
225
|
-
if (pass) result += ':' + pass;
|
|
226
|
-
result += '@';
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
result += this.hostname;
|
|
230
|
-
if (this.port) result += ':' + this.port;
|
|
231
|
-
|
|
232
|
-
const pathname = this.pathname;
|
|
233
|
-
result += pathname;
|
|
234
|
-
|
|
235
|
-
result += this.search;
|
|
236
|
-
result += this.hash;
|
|
237
|
-
|
|
238
|
-
return result;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
get searchParams(): URLSearchParams {
|
|
242
|
-
return this.#searchParams;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
toString(): string {
|
|
246
|
-
return this.href;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
toJSON(): string {
|
|
250
|
-
return this.href;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// ---- URL.createObjectURL / URL.revokeObjectURL ----
|
|
254
|
-
//
|
|
255
|
-
// Consumers like Excalibur.js do `const src = URL.createObjectURL(blob);
|
|
256
|
-
// image.src = src;`. For that to work on GJS we need `src` to be a path
|
|
257
|
-
// `HTMLImageElement` / `HTMLAudioElement` / `FontFace` can actually read —
|
|
258
|
-
// i.e. a `file://` URL. We implement this as a static method on our own
|
|
259
|
-
// URL class (no globalThis monkey-patching):
|
|
260
|
-
//
|
|
261
|
-
// - Fast path: if the Blob already carries a `_tmpPath` (e.g. written
|
|
262
|
-
// by `@gjsify/fetch` XHR when `responseType='blob'`), wrap it as
|
|
263
|
-
// `file://<_tmpPath>`.
|
|
264
|
-
// - Slow path: if the Blob has `arrayBuffer()`/bytes but no `_tmpPath`,
|
|
265
|
-
// materialise the bytes into a GLib temp file and wrap that. This
|
|
266
|
-
// path is async in the spec — but W3C `createObjectURL` is sync. We
|
|
267
|
-
// read the bytes via `GLib.Bytes`-style synchronous access when
|
|
268
|
-
// possible and fall back to a sentinel if not.
|
|
269
|
-
//
|
|
270
|
-
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
|
|
271
|
-
|
|
272
|
-
static _objectURLPaths = new Map<string, string>();
|
|
273
|
-
static _objectURLCounter = 0;
|
|
274
|
-
|
|
275
|
-
static createObjectURL(blob: { _tmpPath?: string; type?: string; size?: number }): string {
|
|
276
|
-
const tmp = blob?._tmpPath;
|
|
277
|
-
if (typeof tmp === 'string' && tmp.length > 0) {
|
|
278
|
-
const url = `file://${tmp}`;
|
|
279
|
-
URL._objectURLPaths.set(url, tmp);
|
|
280
|
-
return url;
|
|
281
|
-
}
|
|
282
|
-
// No backing file — cannot hand this to GdkPixbuf / Gst / GLib. Surface
|
|
283
|
-
// a clear sentinel so callers fail fast instead of silently loading a
|
|
284
|
-
// phantom resource.
|
|
285
|
-
return 'file:///dev/null';
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
static revokeObjectURL(url: string): void {
|
|
289
|
-
const path = URL._objectURLPaths.get(url);
|
|
290
|
-
if (!path) return;
|
|
291
|
-
try {
|
|
292
|
-
GLib.unlink(path);
|
|
293
|
-
} catch {
|
|
294
|
-
// best-effort cleanup
|
|
295
|
-
}
|
|
296
|
-
URL._objectURLPaths.delete(url);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// ---- Legacy url.parse / url.format / url.resolve ----
|
|
301
|
-
|
|
302
|
-
export interface UrlObject {
|
|
303
|
-
protocol?: string | null;
|
|
304
|
-
slashes?: boolean | null;
|
|
305
|
-
auth?: string | null;
|
|
306
|
-
host?: string | null;
|
|
307
|
-
port?: string | null;
|
|
308
|
-
hostname?: string | null;
|
|
309
|
-
hash?: string | null;
|
|
310
|
-
search?: string | null;
|
|
311
|
-
query?: string | Record<string, string> | null;
|
|
312
|
-
pathname?: string | null;
|
|
313
|
-
path?: string | null;
|
|
314
|
-
href?: string;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
export interface Url extends UrlObject {
|
|
318
|
-
href: string;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
export function parse(urlString: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url {
|
|
322
|
-
if (typeof urlString !== 'string') {
|
|
323
|
-
throw new TypeError('The "url" argument must be of type string. Received type ' + typeof urlString);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const result: Url = {
|
|
327
|
-
protocol: null,
|
|
328
|
-
slashes: null,
|
|
329
|
-
auth: null,
|
|
330
|
-
host: null,
|
|
331
|
-
port: null,
|
|
332
|
-
hostname: null,
|
|
333
|
-
hash: null,
|
|
334
|
-
search: null,
|
|
335
|
-
query: null,
|
|
336
|
-
pathname: null,
|
|
337
|
-
path: null,
|
|
338
|
-
href: urlString,
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
let rest = urlString.trim();
|
|
342
|
-
|
|
343
|
-
// Extract hash
|
|
344
|
-
const hashIdx = rest.indexOf('#');
|
|
345
|
-
if (hashIdx !== -1) {
|
|
346
|
-
result.hash = rest.slice(hashIdx);
|
|
347
|
-
rest = rest.slice(0, hashIdx);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Extract search/query
|
|
351
|
-
const qIdx = rest.indexOf('?');
|
|
352
|
-
if (qIdx !== -1) {
|
|
353
|
-
result.search = rest.slice(qIdx);
|
|
354
|
-
result.query = parseQueryString
|
|
355
|
-
? Object.fromEntries(new URLSearchParams(rest.slice(qIdx + 1)))
|
|
356
|
-
: rest.slice(qIdx + 1);
|
|
357
|
-
rest = rest.slice(0, qIdx);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Extract protocol
|
|
361
|
-
const protoMatch = /^([a-z][a-z0-9.+-]*:)/i.exec(rest);
|
|
362
|
-
if (protoMatch) {
|
|
363
|
-
result.protocol = protoMatch[1].toLowerCase();
|
|
364
|
-
rest = rest.slice(result.protocol.length);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Check for slashes
|
|
368
|
-
if (slashesDenoteHost || result.protocol) {
|
|
369
|
-
const hasSlashes = rest.startsWith('//');
|
|
370
|
-
if (hasSlashes) {
|
|
371
|
-
result.slashes = true;
|
|
372
|
-
rest = rest.slice(2);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Extract host portion (only if we had slashes or protocol)
|
|
377
|
-
if (result.slashes || (result.protocol && !['javascript:', 'data:', 'mailto:'].includes(result.protocol))) {
|
|
378
|
-
let hostEnd = -1;
|
|
379
|
-
for (let i = 0; i < rest.length; i++) {
|
|
380
|
-
const ch = rest[i];
|
|
381
|
-
if (ch === '/' || ch === '\\') {
|
|
382
|
-
hostEnd = i;
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const hostPart = hostEnd === -1 ? rest : rest.slice(0, hostEnd);
|
|
388
|
-
rest = hostEnd === -1 ? '' : rest.slice(hostEnd);
|
|
389
|
-
|
|
390
|
-
const atIdx = hostPart.lastIndexOf('@');
|
|
391
|
-
if (atIdx !== -1) {
|
|
392
|
-
result.auth = decodeURIComponent(hostPart.slice(0, atIdx));
|
|
393
|
-
const hostWithPort = hostPart.slice(atIdx + 1);
|
|
394
|
-
parseHostPort(hostWithPort, result);
|
|
395
|
-
} else {
|
|
396
|
-
parseHostPort(hostPart, result);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
result.pathname = rest || (result.slashes ? '/' : null);
|
|
401
|
-
|
|
402
|
-
if (result.pathname !== null || result.search !== null) {
|
|
403
|
-
result.path = (result.pathname || '') + (result.search || '');
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
result.href = format(result);
|
|
407
|
-
|
|
408
|
-
return result;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function parseHostPort(hostPart: string, result: Url): void {
|
|
412
|
-
if (!hostPart) return;
|
|
413
|
-
|
|
414
|
-
const bracketIdx = hostPart.indexOf('[');
|
|
415
|
-
if (bracketIdx !== -1) {
|
|
416
|
-
const bracketEnd = hostPart.indexOf(']', bracketIdx);
|
|
417
|
-
if (bracketEnd !== -1) {
|
|
418
|
-
const portStr = hostPart.slice(bracketEnd + 1);
|
|
419
|
-
if (portStr.startsWith(':')) {
|
|
420
|
-
result.port = portStr.slice(1);
|
|
421
|
-
}
|
|
422
|
-
result.hostname = hostPart.slice(bracketIdx, bracketEnd + 1);
|
|
423
|
-
result.host = result.hostname + (result.port ? ':' + result.port : '');
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const colonIdx = hostPart.lastIndexOf(':');
|
|
429
|
-
if (colonIdx !== -1) {
|
|
430
|
-
const portCandidate = hostPart.slice(colonIdx + 1);
|
|
431
|
-
if (/^\d*$/.test(portCandidate)) {
|
|
432
|
-
result.port = portCandidate || null;
|
|
433
|
-
result.hostname = hostPart.slice(0, colonIdx).toLowerCase();
|
|
434
|
-
} else {
|
|
435
|
-
result.hostname = hostPart.toLowerCase();
|
|
436
|
-
}
|
|
437
|
-
} else {
|
|
438
|
-
result.hostname = hostPart.toLowerCase();
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
result.host = result.hostname + (result.port ? ':' + result.port : '');
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
export function format(urlObject: UrlObject | string | URL): string {
|
|
445
|
-
if (typeof urlObject === 'string') {
|
|
446
|
-
return urlObject;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (urlObject instanceof URL) {
|
|
450
|
-
return urlObject.href;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const obj = urlObject as UrlObject;
|
|
454
|
-
let result = '';
|
|
455
|
-
|
|
456
|
-
if (obj.protocol) {
|
|
457
|
-
result += obj.protocol;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
if (obj.slashes || (obj.protocol && !['javascript:', 'data:', 'mailto:'].includes(obj.protocol || ''))) {
|
|
461
|
-
result += '//';
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (obj.auth) {
|
|
465
|
-
result += encodeURIComponent(obj.auth) + '@';
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (obj.host) {
|
|
469
|
-
result += obj.host;
|
|
470
|
-
} else {
|
|
471
|
-
if (obj.hostname) {
|
|
472
|
-
result += obj.hostname;
|
|
473
|
-
}
|
|
474
|
-
if (obj.port) {
|
|
475
|
-
result += ':' + obj.port;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (obj.pathname) {
|
|
480
|
-
result += obj.pathname;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (obj.search) {
|
|
484
|
-
result += obj.search;
|
|
485
|
-
} else if (obj.query && typeof obj.query === 'object') {
|
|
486
|
-
const qs = new URLSearchParams(obj.query as Record<string, string>).toString();
|
|
487
|
-
if (qs) result += '?' + qs;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (obj.hash) {
|
|
491
|
-
result += obj.hash;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return result;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
export function resolve(from: string, to: string): string {
|
|
498
|
-
return new URL(to, new URL(from, 'resolve://')).href.replace(/^resolve:\/\//, '');
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// ---- File URL helpers ----
|
|
502
|
-
|
|
503
|
-
export function fileURLToPath(url: string | URL): string {
|
|
504
|
-
if (typeof url === 'string') {
|
|
505
|
-
url = new URL(url);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (!(url instanceof URL)) {
|
|
509
|
-
throw new TypeError('The "url" argument must be of type string or URL. Received type ' + typeof url);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (url.protocol !== 'file:') {
|
|
513
|
-
throw new TypeError('The URL must be of scheme file');
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (url.hostname !== '' && url.hostname !== 'localhost') {
|
|
517
|
-
throw new TypeError(
|
|
518
|
-
`File URL host must be "localhost" or empty on linux`
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
const pathname = url.pathname;
|
|
523
|
-
for (let i = 0; i < pathname.length; i++) {
|
|
524
|
-
if (pathname[i] === '%') {
|
|
525
|
-
const third = pathname.codePointAt(i + 2)! | 0x20;
|
|
526
|
-
if (pathname[i + 1] === '2' && third === 102) {
|
|
527
|
-
throw new TypeError('File URL path must not include encoded / characters');
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
return decodeURIComponent(pathname);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
export function pathToFileURL(filepath: string): URL {
|
|
536
|
-
let resolved = filepath;
|
|
537
|
-
|
|
538
|
-
if (filepath[0] !== '/') {
|
|
539
|
-
if (typeof globalThis.process?.cwd === 'function') {
|
|
540
|
-
resolved = globalThis.process.cwd() + '/' + filepath;
|
|
541
|
-
} else {
|
|
542
|
-
try {
|
|
543
|
-
if (GLib?.get_current_dir) {
|
|
544
|
-
resolved = GLib.get_current_dir() + '/' + filepath;
|
|
545
|
-
}
|
|
546
|
-
} catch {
|
|
547
|
-
// Fall through
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return new URL('file://' + encodePathForURL(resolved));
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function encodePathForURL(filepath: string): string {
|
|
556
|
-
let result = '';
|
|
557
|
-
for (let i = 0; i < filepath.length; i++) {
|
|
558
|
-
const ch = filepath[i];
|
|
559
|
-
if (
|
|
560
|
-
(ch >= 'a' && ch <= 'z') ||
|
|
561
|
-
(ch >= 'A' && ch <= 'Z') ||
|
|
562
|
-
(ch >= '0' && ch <= '9') ||
|
|
563
|
-
ch === '/' || ch === '-' || ch === '_' || ch === '.' || ch === '~' ||
|
|
564
|
-
ch === ':' || ch === '@' || ch === '!'
|
|
565
|
-
) {
|
|
566
|
-
result += ch;
|
|
567
|
-
} else {
|
|
568
|
-
result += encodeURIComponent(ch);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return result;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
export function domainToASCII(domain: string): string {
|
|
575
|
-
try {
|
|
576
|
-
return new URL(`http://${domain}`).hostname;
|
|
577
|
-
} catch {
|
|
578
|
-
return '';
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
export function domainToUnicode(domain: string): string {
|
|
583
|
-
try {
|
|
584
|
-
return new URL(`http://${domain}`).hostname;
|
|
585
|
-
} catch {
|
|
586
|
-
return '';
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Default export
|
|
591
|
-
export default {
|
|
592
|
-
URL,
|
|
593
|
-
URLSearchParams,
|
|
594
|
-
parse,
|
|
595
|
-
format,
|
|
596
|
-
resolve,
|
|
597
|
-
fileURLToPath,
|
|
598
|
-
pathToFileURL,
|
|
599
|
-
domainToASCII,
|
|
600
|
-
domainToUnicode,
|
|
601
|
-
};
|
package/src/test.ts
DELETED
package/test/file.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Hello World
|
package/tsconfig.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "ESNext",
|
|
4
|
-
"target": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"types": [
|
|
7
|
-
"node"
|
|
8
|
-
],
|
|
9
|
-
"experimentalDecorators": true,
|
|
10
|
-
"emitDeclarationOnly": true,
|
|
11
|
-
"declaration": true,
|
|
12
|
-
"allowImportingTsExtensions": true,
|
|
13
|
-
"outDir": "lib",
|
|
14
|
-
"rootDir": "src",
|
|
15
|
-
"declarationDir": "lib/types",
|
|
16
|
-
"composite": true,
|
|
17
|
-
"skipLibCheck": true,
|
|
18
|
-
"allowJs": true,
|
|
19
|
-
"checkJs": false,
|
|
20
|
-
"strict": false
|
|
21
|
-
},
|
|
22
|
-
"include": [
|
|
23
|
-
"src/**/*.ts"
|
|
24
|
-
],
|
|
25
|
-
"exclude": [
|
|
26
|
-
"src/test.ts",
|
|
27
|
-
"src/test.mts"
|
|
28
|
-
]
|
|
29
|
-
}
|