@ccheever/exact-ibex-runtime 0.1.0
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 +63 -0
- package/src/abort/AbortController.ts +23 -0
- package/src/abort/AbortSignal.ts +152 -0
- package/src/abort/index.ts +2 -0
- package/src/accessibility.ts +12 -0
- package/src/arraybuffer-detach.ts +109 -0
- package/src/base64/base64.ts +168 -0
- package/src/base64/index.ts +1 -0
- package/src/blob/Blob.ts +259 -0
- package/src/blob/File.ts +59 -0
- package/src/blob/FormData.ts +323 -0
- package/src/blob/index.ts +3 -0
- package/src/bootstrap.ts +1946 -0
- package/src/broadcast/BroadcastChannel.ts +280 -0
- package/src/broadcast/index.ts +5 -0
- package/src/cache/Cache.ts +349 -0
- package/src/cache/CacheStorage.ts +89 -0
- package/src/cache/index.ts +27 -0
- package/src/camera/index.ts +6202 -0
- package/src/camera/processor.worker.ts +194 -0
- package/src/camera/scene.ts +195 -0
- package/src/clipboard/Clipboard.ts +129 -0
- package/src/clipboard/ClipboardItem.ts +97 -0
- package/src/clipboard/index.ts +6 -0
- package/src/clone/index.ts +1 -0
- package/src/clone/structuredClone.ts +389 -0
- package/src/clone/transferableSymbols.ts +2 -0
- package/src/compression/CompressionStream.ts +146 -0
- package/src/compression/DecompressionStream.ts +342 -0
- package/src/compression/index.ts +4 -0
- package/src/console/Console.ts +341 -0
- package/src/console/index.ts +2 -0
- package/src/core/accessibility-state.ts +263 -0
- package/src/core/accessibility.ts +184 -0
- package/src/core/agent-state.ts +37 -0
- package/src/core/diagnostics-logs.ts +144 -0
- package/src/core/host-call-bridge.ts +16 -0
- package/src/core/i18n-helpers.ts +189 -0
- package/src/core/locale-state.ts +253 -0
- package/src/core/locale.ts +95 -0
- package/src/crypto/Crypto.ts +2743 -0
- package/src/crypto/index.ts +1 -0
- package/src/diagnostics/logs.ts +7 -0
- package/src/encoding/TextDecoder.ts +1181 -0
- package/src/encoding/TextDecoderStream.ts +58 -0
- package/src/encoding/TextEncoder.ts +180 -0
- package/src/encoding/TextEncoderStream.ts +39 -0
- package/src/encoding/index.ts +8 -0
- package/src/events/CloseEvent.ts +91 -0
- package/src/events/DOMException.ts +409 -0
- package/src/events/ErrorEvent.ts +39 -0
- package/src/events/Event.ts +151 -0
- package/src/events/EventTarget.ts +280 -0
- package/src/events/FocusEvent.ts +27 -0
- package/src/events/KeyboardEvent.ts +46 -0
- package/src/events/MessageEvent.ts +61 -0
- package/src/events/ProgressEvent.ts +33 -0
- package/src/events/PromiseRejectionEvent.ts +31 -0
- package/src/events/index.ts +52 -0
- package/src/eventsource/EventSource.ts +371 -0
- package/src/eventsource/index.ts +2 -0
- package/src/fetch/Headers.ts +642 -0
- package/src/fetch/Request.ts +760 -0
- package/src/fetch/Response.ts +543 -0
- package/src/fetch/body.ts +1256 -0
- package/src/fetch/cookie-jar.ts +566 -0
- package/src/fetch/demo.ts +207 -0
- package/src/fetch/errors.ts +101 -0
- package/src/fetch/fetch.ts +2610 -0
- package/src/fetch/index.ts +101 -0
- package/src/fetch/native-bridge.ts +65 -0
- package/src/fetch/types.ts +258 -0
- package/src/filereader/FileReader.ts +236 -0
- package/src/filereader/index.ts +1 -0
- package/src/fs/Dirent.ts +39 -0
- package/src/fs/ExactFile.ts +450 -0
- package/src/fs/Stats.ts +80 -0
- package/src/fs/index.ts +944 -0
- package/src/fs/promises.ts +386 -0
- package/src/fs/shared.ts +328 -0
- package/src/http-server/index.js +697 -0
- package/src/http-server/index.ts +27 -0
- package/src/identity.generated.ts +14 -0
- package/src/index.ts +283 -0
- package/src/indexeddb/IDBCursor.ts +188 -0
- package/src/indexeddb/IDBDatabase.ts +343 -0
- package/src/indexeddb/IDBFactory.ts +269 -0
- package/src/indexeddb/IDBIndex.ts +194 -0
- package/src/indexeddb/IDBKeyRange.ts +109 -0
- package/src/indexeddb/IDBObjectStore.ts +468 -0
- package/src/indexeddb/IDBRequest.ts +163 -0
- package/src/indexeddb/IDBTransaction.ts +207 -0
- package/src/indexeddb/index.ts +34 -0
- package/src/indexeddb/utils.ts +52 -0
- package/src/inspect/index.ts +1 -0
- package/src/inspect/inspect.ts +465 -0
- package/src/internal/detect.ts +104 -0
- package/src/locale.ts +10 -0
- package/src/location/index.ts +1059 -0
- package/src/locks/LockManager.ts +460 -0
- package/src/locks/index.ts +12 -0
- package/src/media/VideoFrame.ts +58 -0
- package/src/messaging/MessageChannel.ts +31 -0
- package/src/messaging/MessagePort.ts +180 -0
- package/src/messaging/index.ts +2 -0
- package/src/messaging.ts +247 -0
- package/src/native/NativeModules.ts +354 -0
- package/src/native/index.ts +1 -0
- package/src/navigator/Navigator.ts +351 -0
- package/src/navigator/index.ts +1 -0
- package/src/node/Buffer.ts +1786 -0
- package/src/node/index.ts +4 -0
- package/src/node/path.ts +495 -0
- package/src/node/process.ts +2528 -0
- package/src/performance/Performance.ts +532 -0
- package/src/performance/index.ts +21 -0
- package/src/polyfills/array.ts +236 -0
- package/src/polyfills/arraybuffer.ts +172 -0
- package/src/polyfills/groupby.ts +85 -0
- package/src/polyfills/index.ts +85 -0
- package/src/polyfills/intl.ts +1956 -0
- package/src/polyfills/iterator.ts +479 -0
- package/src/polyfills/promise.ts +37 -0
- package/src/polyfills/set.ts +245 -0
- package/src/polyfills/string.ts +85 -0
- package/src/polyfills/typedarray.ts +110 -0
- package/src/promise-rejection-tracking.ts +464 -0
- package/src/react-native/index.ts +388 -0
- package/src/runtime-entry.ts +55 -0
- package/src/scheduling/AnimationFrame.ts +105 -0
- package/src/scheduling/IdleCallback.ts +167 -0
- package/src/scheduling/index.ts +13 -0
- package/src/security/Capabilities.ts +1146 -0
- package/src/security/Permissions.ts +392 -0
- package/src/security/capability-bits.generated.ts +63 -0
- package/src/security/index.ts +16 -0
- package/src/sqlite/Database.ts +456 -0
- package/src/sqlite/Statement.ts +206 -0
- package/src/sqlite/constants.ts +79 -0
- package/src/sqlite/errors.ts +25 -0
- package/src/sqlite/index.ts +34 -0
- package/src/sqlite/module.js +438 -0
- package/src/storage/Storage.ts +291 -0
- package/src/storage/StorageManager.ts +91 -0
- package/src/storage/index.ts +3 -0
- package/src/stream-compat.ts +47 -0
- package/src/streams/ReadableStream.ts +4131 -0
- package/src/streams/TransformStream.ts +375 -0
- package/src/streams/WritableStream.ts +866 -0
- package/src/streams/index.ts +41 -0
- package/src/timers/Timers.ts +296 -0
- package/src/timers/index.ts +11 -0
- package/src/url/URL.ts +656 -0
- package/src/url/URLPattern.ts +850 -0
- package/src/url/URLSearchParams.ts +244 -0
- package/src/url/index.ts +9 -0
- package/src/websocket/WebSocket.ts +770 -0
- package/src/websocket/WebSocketError.ts +52 -0
- package/src/websocket/WebSocketStream.ts +628 -0
- package/src/websocket/index.ts +7 -0
- package/src/window/index.ts +872 -0
package/src/url/URL.ts
ADDED
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL - WHATWG URL Standard Implementation
|
|
3
|
+
*
|
|
4
|
+
* @see https://url.spec.whatwg.org/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { URLSearchParams } from "./URLSearchParams";
|
|
8
|
+
|
|
9
|
+
// Special schemes that have default ports
|
|
10
|
+
const DEFAULT_PORTS: Record<string, string> = {
|
|
11
|
+
ftp: "21",
|
|
12
|
+
http: "80",
|
|
13
|
+
https: "443",
|
|
14
|
+
ws: "80",
|
|
15
|
+
wss: "443",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Special schemes (cannot have opaque path)
|
|
19
|
+
const SPECIAL_SCHEMES = new Set(["ftp", "file", "http", "https", "ws", "wss"]);
|
|
20
|
+
const OBJECT_URL_PREFIX = "blob:exact:";
|
|
21
|
+
const objectURLRegistry = new Map<string, unknown>();
|
|
22
|
+
let objectURLCounter = 0;
|
|
23
|
+
|
|
24
|
+
function makeMissingArgsError(argumentName: string): TypeError {
|
|
25
|
+
const error = new TypeError(`The "${argumentName}" argument must be specified`);
|
|
26
|
+
(error as any).code = "ERR_MISSING_ARGS";
|
|
27
|
+
return error;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function makeInvalidBlobError(object: unknown): TypeError {
|
|
31
|
+
let received = `type ${typeof object}`;
|
|
32
|
+
if (object === null) {
|
|
33
|
+
received = "null";
|
|
34
|
+
} else if (object !== undefined && typeof object === "object") {
|
|
35
|
+
const ctorName = (object as { constructor?: { name?: string } }).constructor?.name;
|
|
36
|
+
received = ctorName ? `an instance of ${ctorName}` : "an instance of Object";
|
|
37
|
+
}
|
|
38
|
+
const error = new TypeError(
|
|
39
|
+
`The "obj" argument must be an instance of Blob. Received ${received}`,
|
|
40
|
+
);
|
|
41
|
+
(error as any).code = "ERR_INVALID_ARG_TYPE";
|
|
42
|
+
return error;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveHostURLConstructor(): typeof globalThis.URL | undefined {
|
|
46
|
+
const globalURL = globalThis.URL;
|
|
47
|
+
if (typeof globalURL === "function" && globalURL !== (URL as any)) {
|
|
48
|
+
return globalURL;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const globalRef = globalThis as any;
|
|
52
|
+
for (const loaderName of ["__exactRequire", "require"]) {
|
|
53
|
+
const loader = globalRef[loaderName];
|
|
54
|
+
if (typeof loader !== "function") {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const urlModule = loader("url");
|
|
60
|
+
const moduleURL = urlModule?.URL;
|
|
61
|
+
if (typeof moduleURL === "function" && moduleURL !== (URL as any)) {
|
|
62
|
+
return moduleURL;
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
// Fall through to the custom parser if no host URL implementation exists.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function canonicalizeSpecialHost(value: string): string {
|
|
73
|
+
const hostURL = resolveHostURLConstructor();
|
|
74
|
+
if (typeof hostURL !== "function") {
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
const parsed = new hostURL(`https://${value}`);
|
|
78
|
+
return parsed.hostname;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function toHex(code: number): string {
|
|
82
|
+
const hex = code.toString(16).toUpperCase();
|
|
83
|
+
return hex.length === 1 ? `0${hex}` : hex;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function canonicalizeHost(value: string, protocol: string): string {
|
|
87
|
+
const isSpecial = protocol && SPECIAL_SCHEMES.has(protocol.slice(0, -1));
|
|
88
|
+
if (isSpecial) {
|
|
89
|
+
try {
|
|
90
|
+
return canonicalizeSpecialHost(value);
|
|
91
|
+
} catch {
|
|
92
|
+
return value.toLowerCase();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
let out = "";
|
|
96
|
+
for (let i = 0; i < value.length; i++) {
|
|
97
|
+
const char = value.charAt(i);
|
|
98
|
+
if (char.charCodeAt(0) >= 128) {
|
|
99
|
+
out += encodeURIComponent(char);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (
|
|
103
|
+
char === "%" &&
|
|
104
|
+
i + 2 < value.length &&
|
|
105
|
+
/[0-9A-Fa-f]/.test(value.charAt(i + 1)) &&
|
|
106
|
+
/[0-9A-Fa-f]/.test(value.charAt(i + 2))
|
|
107
|
+
) {
|
|
108
|
+
out += `%${value.charAt(i + 1).toUpperCase()}${value.charAt(i + 2).toUpperCase()}`;
|
|
109
|
+
i += 2;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
out += char.toLowerCase();
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function normalizePort(value: string): string {
|
|
118
|
+
if (!value) {
|
|
119
|
+
return "";
|
|
120
|
+
}
|
|
121
|
+
const match = value.match(/^[0-9]+/);
|
|
122
|
+
if (!match) {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
value = match[0];
|
|
126
|
+
return value.replace(/^0+(?=\d)/, "");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function sanitizeUserinfo(value: string): string {
|
|
130
|
+
value = String(value);
|
|
131
|
+
let out = "";
|
|
132
|
+
for (let i = 0; i < value.length; i++) {
|
|
133
|
+
const c = value.charAt(i);
|
|
134
|
+
if (
|
|
135
|
+
c === "%" &&
|
|
136
|
+
__needsHexDigit(value.charAt(i + 1)) &&
|
|
137
|
+
__needsHexDigit(value.charAt(i + 2))
|
|
138
|
+
) {
|
|
139
|
+
out += value.slice(i, i + 3);
|
|
140
|
+
i += 2;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (__isUserinfoUnescaped(c)) {
|
|
145
|
+
out += c;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
out += encodeURIComponent(c);
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function __needsHexDigit(char: string): boolean {
|
|
155
|
+
return !!char && /^[0-9A-Fa-f]$/.test(char);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function __isUserinfoUnescaped(c: string): boolean {
|
|
159
|
+
const code = c.charCodeAt(0);
|
|
160
|
+
if (code >= 0x30 && code <= 0x39) return true;
|
|
161
|
+
if (code >= 0x41 && code <= 0x5A) return true;
|
|
162
|
+
if (code >= 0x61 && code <= 0x7A) return true;
|
|
163
|
+
return (
|
|
164
|
+
code === 0x21 ||
|
|
165
|
+
code === 0x24 ||
|
|
166
|
+
code === 0x26 ||
|
|
167
|
+
code === 0x27 ||
|
|
168
|
+
code === 0x28 ||
|
|
169
|
+
code === 0x29 ||
|
|
170
|
+
code === 0x2A ||
|
|
171
|
+
code === 0x2B ||
|
|
172
|
+
code === 0x2C ||
|
|
173
|
+
code === 0x2D ||
|
|
174
|
+
code === 0x2E ||
|
|
175
|
+
code === 0x25
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function sanitizeHost(value: string, protocol: string): string | null {
|
|
180
|
+
value = String(value);
|
|
181
|
+
let out = "";
|
|
182
|
+
for (let i = 0; i < value.length; i++) {
|
|
183
|
+
const code = value.charCodeAt(i);
|
|
184
|
+
if (code === 0) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
if (protocol === "https:" && code === 0x1f) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
if (code >= 0 && code < 0x20) {
|
|
191
|
+
if (code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
out += `%${toHex(code)}`;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
out += value.charAt(i);
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parseHostInput(value: string, isSpecial: boolean = false): string {
|
|
203
|
+
for (let i = 0; i < value.length; i++) {
|
|
204
|
+
const c = value.charCodeAt(i);
|
|
205
|
+
if (c === 47 || c === 63 || c === 35 || (isSpecial && c === 92)) {
|
|
206
|
+
return value.slice(0, i);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function stripProtocolControlChars(value: string): string {
|
|
213
|
+
value = String(value);
|
|
214
|
+
let out = "";
|
|
215
|
+
for (let i = 0; i < value.length; i++) {
|
|
216
|
+
const code = value.charCodeAt(i);
|
|
217
|
+
if (code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
out += value.charAt(i);
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create a TypeError matching the WHATWG URL spec error format.
|
|
227
|
+
* Redacts base URLs that contain passwords.
|
|
228
|
+
*/
|
|
229
|
+
function _makeURLError(input: string, baseInput?: string): TypeError {
|
|
230
|
+
let msg: string;
|
|
231
|
+
if (baseInput !== undefined) {
|
|
232
|
+
// Redact base if it contains credentials (password portion)
|
|
233
|
+
const hasCredentials = /:[^/].*@/.test(baseInput);
|
|
234
|
+
const displayBase = hasCredentials ? "<redacted>" : JSON.stringify(baseInput);
|
|
235
|
+
msg = `${JSON.stringify(input)} cannot be parsed as a URL against ${displayBase}`;
|
|
236
|
+
} else {
|
|
237
|
+
msg = `${JSON.stringify(input)} cannot be parsed as a URL`;
|
|
238
|
+
}
|
|
239
|
+
const err = new TypeError(msg);
|
|
240
|
+
(err as any).code = "ERR_INVALID_URL";
|
|
241
|
+
return err;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export class URL {
|
|
245
|
+
private _protocol: string = "";
|
|
246
|
+
private _username: string = "";
|
|
247
|
+
private _password: string = "";
|
|
248
|
+
private _hostname: string = "";
|
|
249
|
+
private _port: string = "";
|
|
250
|
+
private _pathname: string = "";
|
|
251
|
+
private _search: string = "";
|
|
252
|
+
private _hash: string = "";
|
|
253
|
+
private _searchParams: URLSearchParams;
|
|
254
|
+
|
|
255
|
+
constructor(url: string, base?: string | URL) {
|
|
256
|
+
if (arguments.length === 0) {
|
|
257
|
+
const error = new TypeError('The "url" argument must be specified');
|
|
258
|
+
(error as any).code = "ERR_MISSING_ARGS";
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Per WHATWG URL spec: convert url and base to USVString
|
|
263
|
+
const urlStr = String(url);
|
|
264
|
+
|
|
265
|
+
// Parse base URL if provided
|
|
266
|
+
let baseURL: URL | null = null;
|
|
267
|
+
const baseStr = base !== undefined ? (base instanceof URL ? base.href : String(base)) : undefined;
|
|
268
|
+
if (base !== undefined) {
|
|
269
|
+
if (base instanceof URL) {
|
|
270
|
+
baseURL = base;
|
|
271
|
+
} else {
|
|
272
|
+
try {
|
|
273
|
+
baseURL = new URL(String(base));
|
|
274
|
+
} catch {
|
|
275
|
+
throw _makeURLError(urlStr, baseStr);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Parse the URL
|
|
281
|
+
this._parse(urlStr, baseURL, baseStr);
|
|
282
|
+
|
|
283
|
+
// Initialize search params
|
|
284
|
+
this._searchParams = new URLSearchParams(this._search);
|
|
285
|
+
this._searchParams._setURL(this);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private _parse(input: string, base: URL | null, baseStr?: string): void {
|
|
289
|
+
const hostURL = resolveHostURLConstructor();
|
|
290
|
+
if (typeof hostURL !== "function") {
|
|
291
|
+
throw _makeURLError(input, baseStr);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
const parsed = base ? new hostURL(input, base.href) : new hostURL(input);
|
|
296
|
+
this._protocol = parsed.protocol;
|
|
297
|
+
this._username = parsed.username;
|
|
298
|
+
this._password = parsed.password;
|
|
299
|
+
this._hostname = parsed.hostname;
|
|
300
|
+
this._port = parsed.port;
|
|
301
|
+
this._pathname = parsed.pathname;
|
|
302
|
+
this._search = parsed.search;
|
|
303
|
+
this._hash = parsed.hash;
|
|
304
|
+
} catch {
|
|
305
|
+
throw _makeURLError(input, baseStr);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private _normalizePath(path: string): string {
|
|
310
|
+
if (!path) return "/";
|
|
311
|
+
|
|
312
|
+
const segments = path.split("/");
|
|
313
|
+
const normalized: string[] = [];
|
|
314
|
+
|
|
315
|
+
for (const segment of segments) {
|
|
316
|
+
if (segment === ".") {
|
|
317
|
+
continue;
|
|
318
|
+
} else if (segment === "..") {
|
|
319
|
+
normalized.pop();
|
|
320
|
+
} else {
|
|
321
|
+
normalized.push(segment);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
let result = normalized.join("/");
|
|
326
|
+
if (!result.startsWith("/")) {
|
|
327
|
+
result = "/" + result;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Getters and setters
|
|
334
|
+
get href(): string {
|
|
335
|
+
let result = this._protocol;
|
|
336
|
+
|
|
337
|
+
if (this._hostname) {
|
|
338
|
+
result += "//";
|
|
339
|
+
|
|
340
|
+
if (this._username) {
|
|
341
|
+
result += this._username;
|
|
342
|
+
if (this._password) {
|
|
343
|
+
result += ":" + this._password;
|
|
344
|
+
}
|
|
345
|
+
result += "@";
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
result += this._hostname;
|
|
349
|
+
|
|
350
|
+
if (this._port) {
|
|
351
|
+
result += ":" + this._port;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
result += this._pathname;
|
|
356
|
+
result += this._search;
|
|
357
|
+
result += this._hash;
|
|
358
|
+
|
|
359
|
+
return result;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
set href(value: string) {
|
|
363
|
+
const newURL = new URL(value);
|
|
364
|
+
this._protocol = newURL._protocol;
|
|
365
|
+
this._username = newURL._username;
|
|
366
|
+
this._password = newURL._password;
|
|
367
|
+
this._hostname = newURL._hostname;
|
|
368
|
+
this._port = newURL._port;
|
|
369
|
+
this._pathname = newURL._pathname;
|
|
370
|
+
this._search = newURL._search;
|
|
371
|
+
this._hash = newURL._hash;
|
|
372
|
+
this._searchParams = new URLSearchParams(this._search);
|
|
373
|
+
this._searchParams._setURL(this);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
get origin(): string {
|
|
377
|
+
const scheme = this._protocol.slice(0, -1);
|
|
378
|
+
|
|
379
|
+
// blob: URLs derive origin from their inner URL
|
|
380
|
+
if (scheme === "blob") {
|
|
381
|
+
try {
|
|
382
|
+
let innerHref = this._pathname + this._search + this._hash;
|
|
383
|
+
if (
|
|
384
|
+
innerHref.startsWith("/") &&
|
|
385
|
+
/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(innerHref.slice(1))
|
|
386
|
+
) {
|
|
387
|
+
innerHref = innerHref.slice(1);
|
|
388
|
+
}
|
|
389
|
+
const innerURL = new URL(innerHref);
|
|
390
|
+
if (innerURL.protocol === "file:") {
|
|
391
|
+
return innerURL.host ? `file://${innerURL.host}` : "file://";
|
|
392
|
+
}
|
|
393
|
+
return innerURL.origin;
|
|
394
|
+
} catch {
|
|
395
|
+
return "null";
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// file: always has null origin per the URL spec
|
|
400
|
+
if (scheme === "file") {
|
|
401
|
+
return "null";
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Only http, https, ftp, ws, wss have a meaningful origin
|
|
405
|
+
if (scheme === "http" || scheme === "https" || scheme === "ftp" || scheme === "ws" || scheme === "wss") {
|
|
406
|
+
return `${this._protocol}//${this.host}`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return "null";
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
get protocol(): string {
|
|
413
|
+
return this._protocol;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
set protocol(value: string) {
|
|
417
|
+
let proto = stripProtocolControlChars(String(value)).toLowerCase();
|
|
418
|
+
if (!proto.endsWith(":")) {
|
|
419
|
+
proto += ":";
|
|
420
|
+
}
|
|
421
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:$/.test(proto)) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (proto === "file:" && (this._username || this._password || this._port)) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
this._protocol = proto;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
get username(): string {
|
|
431
|
+
return this._username;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
set username(value: string) {
|
|
435
|
+
if (this._protocol === "file:" || this._protocol === "unix:") {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
this._username = sanitizeUserinfo(String(value));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
get password(): string {
|
|
442
|
+
return this._password;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
set password(value: string) {
|
|
446
|
+
if (this._protocol === "file:" || this._protocol === "unix:") {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
this._password = sanitizeUserinfo(String(value));
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
get host(): string {
|
|
453
|
+
if (this._port) {
|
|
454
|
+
return `${this._hostname}:${this._port}`;
|
|
455
|
+
}
|
|
456
|
+
return this._hostname;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
set host(value: string) {
|
|
460
|
+
const input = sanitizeHost(value, this._protocol);
|
|
461
|
+
if (input === null) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const isSpecial = SPECIAL_SCHEMES.has(this._protocol.slice(0, -1));
|
|
465
|
+
if (!this._hostname && !isSpecial) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const hostInput = parseHostInput(input, isSpecial);
|
|
469
|
+
if (hostInput === "") {
|
|
470
|
+
if (!isSpecial && this._hostname) {
|
|
471
|
+
this._hostname = "";
|
|
472
|
+
this._port = "";
|
|
473
|
+
}
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (hostInput.indexOf(" ") !== -1) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (hostInput.indexOf("@") !== -1) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (hostInput.indexOf("\\") !== -1 && !isSpecial) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const colonIndex = hostInput.lastIndexOf(":");
|
|
487
|
+
if (colonIndex !== -1 && !input.includes("[")) {
|
|
488
|
+
this._hostname = canonicalizeHost(hostInput.slice(0, colonIndex), this._protocol);
|
|
489
|
+
if (colonIndex < hostInput.length - 1) {
|
|
490
|
+
const port = hostInput.slice(colonIndex + 1);
|
|
491
|
+
const parsedPort = normalizePort(port);
|
|
492
|
+
if (parsedPort) {
|
|
493
|
+
const portNumber = Number(parsedPort);
|
|
494
|
+
if (!Number.isNaN(portNumber) && portNumber <= 65535) {
|
|
495
|
+
this._port = parsedPort;
|
|
496
|
+
}
|
|
497
|
+
} else if (port === "") {
|
|
498
|
+
this._port = "";
|
|
499
|
+
}
|
|
500
|
+
if (this._port && this._protocol.slice(0, -1) && this._port === DEFAULT_PORTS[this._protocol.slice(0, -1)]) {
|
|
501
|
+
this._port = "";
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
} else if (hostInput !== "") {
|
|
505
|
+
this._hostname = canonicalizeHost(hostInput, this._protocol);
|
|
506
|
+
} else {
|
|
507
|
+
this._hostname = "";
|
|
508
|
+
this._port = "";
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
get hostname(): string {
|
|
513
|
+
return this._hostname;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
set hostname(value: string) {
|
|
517
|
+
const input = sanitizeHost(value, this._protocol);
|
|
518
|
+
if (input === null) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const isSpecial = SPECIAL_SCHEMES.has(this._protocol.slice(0, -1));
|
|
522
|
+
if (!this._hostname && !isSpecial) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const hostInput = parseHostInput(input, isSpecial);
|
|
526
|
+
if (hostInput === "" || hostInput.indexOf(" ") !== -1) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (hostInput.indexOf("@") !== -1) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (hostInput.indexOf("\\") !== -1 && !isSpecial) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
this._hostname = canonicalizeHost(hostInput, this._protocol);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
get port(): string {
|
|
539
|
+
return this._port;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
set port(value: string) {
|
|
543
|
+
const port = String(value);
|
|
544
|
+
if (port === "" || port === DEFAULT_PORTS[this._protocol.slice(0, -1)]) {
|
|
545
|
+
this._port = "";
|
|
546
|
+
} else {
|
|
547
|
+
this._port = port;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
get pathname(): string {
|
|
552
|
+
return this._pathname;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
set pathname(value: string) {
|
|
556
|
+
this._pathname = this._normalizePath(String(value));
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
get search(): string {
|
|
560
|
+
return this._search;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
set search(value: string) {
|
|
564
|
+
let search = String(value);
|
|
565
|
+
if (search && !search.startsWith("?")) {
|
|
566
|
+
search = "?" + search;
|
|
567
|
+
}
|
|
568
|
+
this._search = search;
|
|
569
|
+
this._searchParams = new URLSearchParams(search);
|
|
570
|
+
this._searchParams._setURL(this);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
get searchParams(): URLSearchParams {
|
|
574
|
+
return this._searchParams;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
get hash(): string {
|
|
578
|
+
return this._hash;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
set hash(value: string) {
|
|
582
|
+
let hash = String(value);
|
|
583
|
+
if (hash && !hash.startsWith("#")) {
|
|
584
|
+
hash = "#" + hash;
|
|
585
|
+
}
|
|
586
|
+
this._hash = hash;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
toString(): string {
|
|
590
|
+
return this.href;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
toJSON(): string {
|
|
594
|
+
return this.href;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Update search string from URLSearchParams
|
|
599
|
+
* @internal
|
|
600
|
+
*/
|
|
601
|
+
_updateSearch(search: string): void {
|
|
602
|
+
this._search = search ? "?" + search : "";
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Check if a string can be parsed as a URL
|
|
607
|
+
*
|
|
608
|
+
* Per Node.js spec, throws ERR_MISSING_ARGS if called with no arguments.
|
|
609
|
+
*/
|
|
610
|
+
static canParse(url: string, base?: string): boolean {
|
|
611
|
+
// @ts-ignore - check at runtime since TypeScript doesn't track arguments.length
|
|
612
|
+
if (arguments.length === 0) {
|
|
613
|
+
throw makeMissingArgsError("url");
|
|
614
|
+
}
|
|
615
|
+
try {
|
|
616
|
+
new URL(url, base);
|
|
617
|
+
return true;
|
|
618
|
+
} catch {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Parse a URL string, returning null instead of throwing on invalid input.
|
|
625
|
+
*
|
|
626
|
+
* @see https://url.spec.whatwg.org/#dom-url-parse
|
|
627
|
+
*/
|
|
628
|
+
static parse(url: string, base?: string | URL): URL | null {
|
|
629
|
+
try {
|
|
630
|
+
return new URL(url, base);
|
|
631
|
+
} catch {
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
static createObjectURL(object: unknown): string {
|
|
637
|
+
// @ts-ignore - runtime arity check
|
|
638
|
+
if (arguments.length === 0) {
|
|
639
|
+
throw makeMissingArgsError("obj");
|
|
640
|
+
}
|
|
641
|
+
if (typeof Blob !== "function" || !(object instanceof Blob)) {
|
|
642
|
+
throw makeInvalidBlobError(object);
|
|
643
|
+
}
|
|
644
|
+
const url = `${OBJECT_URL_PREFIX}${++objectURLCounter}`;
|
|
645
|
+
objectURLRegistry.set(url, object);
|
|
646
|
+
return url;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
static revokeObjectURL(url: unknown): void {
|
|
650
|
+
// @ts-ignore - runtime arity check
|
|
651
|
+
if (arguments.length === 0) {
|
|
652
|
+
throw makeMissingArgsError("url");
|
|
653
|
+
}
|
|
654
|
+
objectURLRegistry.delete(String(url));
|
|
655
|
+
}
|
|
656
|
+
}
|