@b9g/match-pattern 0.1.6 → 0.1.7
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/README.md +124 -41
- package/package.json +14 -14
- package/src/index.d.ts +164 -0
- package/src/index.js +2039 -0
- package/src/match-pattern.d.ts +0 -43
- package/src/match-pattern.js +0 -183
package/src/index.js
ADDED
|
@@ -0,0 +1,2039 @@
|
|
|
1
|
+
/// <reference types="./index.d.ts" />
|
|
2
|
+
// src/index.ts
|
|
3
|
+
function isValidProtocol(protocol) {
|
|
4
|
+
if (!protocol)
|
|
5
|
+
return false;
|
|
6
|
+
if (!/^[a-zA-Z]/.test(protocol))
|
|
7
|
+
return false;
|
|
8
|
+
return /^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(protocol);
|
|
9
|
+
}
|
|
10
|
+
function isSpecialScheme(protocol) {
|
|
11
|
+
const special = ["http", "https", "ws", "wss", "ftp", "file"];
|
|
12
|
+
return special.includes(protocol.toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
function isDefinitelyNonSpecialScheme(protocolPattern) {
|
|
15
|
+
if (!protocolPattern)
|
|
16
|
+
return false;
|
|
17
|
+
if (protocolPattern.includes("*") || protocolPattern.includes("+")) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const groupMatch = protocolPattern.match(/^\(([^)]+)\)$/);
|
|
21
|
+
if (groupMatch) {
|
|
22
|
+
const alternatives = groupMatch[1].split("|");
|
|
23
|
+
return alternatives.every((alt) => !isSpecialScheme(alt.trim()));
|
|
24
|
+
}
|
|
25
|
+
if (protocolPattern.includes(":")) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return !isSpecialScheme(protocolPattern);
|
|
29
|
+
}
|
|
30
|
+
function getDefaultPort(protocol) {
|
|
31
|
+
const defaults = {
|
|
32
|
+
http: "80",
|
|
33
|
+
https: "443",
|
|
34
|
+
ws: "80",
|
|
35
|
+
wss: "443",
|
|
36
|
+
ftp: "21"
|
|
37
|
+
};
|
|
38
|
+
return defaults[protocol.toLowerCase()];
|
|
39
|
+
}
|
|
40
|
+
function toASCII(hostname) {
|
|
41
|
+
try {
|
|
42
|
+
return new URL("http://" + hostname).hostname;
|
|
43
|
+
} catch {
|
|
44
|
+
return hostname;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function requiresVFlag(pattern) {
|
|
48
|
+
let inCharClass = 0;
|
|
49
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
50
|
+
const char = pattern[i];
|
|
51
|
+
if (char === "\\") {
|
|
52
|
+
i++;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (char === "[") {
|
|
56
|
+
inCharClass++;
|
|
57
|
+
} else if (char === "]") {
|
|
58
|
+
inCharClass = Math.max(0, inCharClass - 1);
|
|
59
|
+
} else if (inCharClass > 0) {
|
|
60
|
+
if (char === "-" && pattern[i + 1] === "-") {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (char === "&" && pattern[i + 1] === "&") {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
function validateRegexGroupContent(regexContent) {
|
|
71
|
+
for (const c of regexContent) {
|
|
72
|
+
if (c.charCodeAt(0) > 127) {
|
|
73
|
+
throw new TypeError(
|
|
74
|
+
`Invalid pattern: regex groups cannot contain non-ASCII characters`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const invalidEscape = regexContent.match(
|
|
79
|
+
/\\([^dDwWsSnrtfv0cbBxu.\\[\](){}|^$*+?/=-])/
|
|
80
|
+
);
|
|
81
|
+
if (invalidEscape) {
|
|
82
|
+
throw new TypeError(
|
|
83
|
+
`Invalid pattern: invalid escape sequence '\\${invalidEscape[1]}' in regex group`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function validateIPv6BracketContent(content) {
|
|
88
|
+
let i = 0;
|
|
89
|
+
while (i < content.length) {
|
|
90
|
+
const char = content[i];
|
|
91
|
+
if (char === "\\") {
|
|
92
|
+
i += 2;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (char === ":") {
|
|
96
|
+
const paramMatch = content.slice(i).match(new RegExp("^:(\\p{ID_Start}\\p{ID_Continue}*)([?+*])?", "u"));
|
|
97
|
+
if (paramMatch) {
|
|
98
|
+
i += paramMatch[0].length;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
i++;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (char.charCodeAt(0) > 127) {
|
|
105
|
+
throw new TypeError(
|
|
106
|
+
`Invalid hostname pattern: non-ASCII character '${char}' in IPv6 brackets`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (/[a-zA-Z]/.test(char) && !/[a-fA-F]/.test(char)) {
|
|
110
|
+
throw new TypeError(
|
|
111
|
+
`Invalid hostname pattern: invalid IPv6 character '${char}'`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
i++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function validateHostnamePattern(hostname) {
|
|
118
|
+
const alwaysForbidden = /[\x00 %<>?@\^|]/;
|
|
119
|
+
let i = 0;
|
|
120
|
+
while (i < hostname.length) {
|
|
121
|
+
const char = hostname[i];
|
|
122
|
+
if (char === "\\") {
|
|
123
|
+
if (i + 1 < hostname.length) {
|
|
124
|
+
const escaped = hostname[i + 1];
|
|
125
|
+
if (escaped === ":") {
|
|
126
|
+
throw new TypeError(
|
|
127
|
+
`Invalid hostname pattern: escaped colon is not allowed`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
i += 2;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (char === "{") {
|
|
135
|
+
const closeIdx = hostname.indexOf("}", i);
|
|
136
|
+
if (closeIdx !== -1) {
|
|
137
|
+
const braceContent = hostname.slice(i + 1, closeIdx);
|
|
138
|
+
const bracketMatch = braceContent.match(/\[([^\]]*)\]/);
|
|
139
|
+
if (bracketMatch) {
|
|
140
|
+
validateIPv6BracketContent(bracketMatch[1]);
|
|
141
|
+
}
|
|
142
|
+
i = closeIdx + 1;
|
|
143
|
+
if (hostname[i] === "?" || hostname[i] === "+" || hostname[i] === "*")
|
|
144
|
+
i++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (char === "(") {
|
|
149
|
+
let depth = 1;
|
|
150
|
+
let j = i + 1;
|
|
151
|
+
while (j < hostname.length && depth > 0) {
|
|
152
|
+
if (hostname[j] === "\\") {
|
|
153
|
+
j += 2;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (hostname[j] === "(")
|
|
157
|
+
depth++;
|
|
158
|
+
if (hostname[j] === ")")
|
|
159
|
+
depth--;
|
|
160
|
+
j++;
|
|
161
|
+
}
|
|
162
|
+
i = j;
|
|
163
|
+
if (hostname[i] === "?" || hostname[i] === "+" || hostname[i] === "*")
|
|
164
|
+
i++;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (char === ":") {
|
|
168
|
+
const paramMatch = hostname.slice(i).match(new RegExp("^:(\\p{ID_Continue}+)(\\([^)]*\\))?([?+*])?", "u"));
|
|
169
|
+
if (paramMatch) {
|
|
170
|
+
i += paramMatch[0].length;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
throw new TypeError(
|
|
174
|
+
`Invalid hostname pattern: unescaped colon outside of pattern syntax`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
if (char === "*") {
|
|
178
|
+
i++;
|
|
179
|
+
if (hostname[i] === "?" || hostname[i] === "+" || hostname[i] === "*")
|
|
180
|
+
i++;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (char === "[") {
|
|
184
|
+
const closeIdx = hostname.indexOf("]", i);
|
|
185
|
+
if (closeIdx !== -1) {
|
|
186
|
+
const ipv6Content = hostname.slice(i + 1, closeIdx);
|
|
187
|
+
validateIPv6BracketContent(ipv6Content);
|
|
188
|
+
i = closeIdx + 1;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
throw new TypeError(`Invalid hostname pattern: unmatched '[' character`);
|
|
192
|
+
}
|
|
193
|
+
if (char === "]") {
|
|
194
|
+
throw new TypeError(`Invalid hostname pattern: unmatched ']' character`);
|
|
195
|
+
}
|
|
196
|
+
if (alwaysForbidden.test(char)) {
|
|
197
|
+
throw new TypeError(
|
|
198
|
+
`Invalid hostname pattern: forbidden character '${char}'`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
i++;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function normalizeHostnamePattern(hostname) {
|
|
205
|
+
hostname = hostname.replace(/[\t\n\r]/g, "");
|
|
206
|
+
let truncateIdx = -1;
|
|
207
|
+
for (let i = 0; i < hostname.length; i++) {
|
|
208
|
+
const char = hostname[i];
|
|
209
|
+
if (char === "\\") {
|
|
210
|
+
if (i + 1 < hostname.length && hostname[i + 1] === "?") {
|
|
211
|
+
truncateIdx = i;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
i++;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (char === "#" || char === "/") {
|
|
218
|
+
truncateIdx = i;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (truncateIdx !== -1) {
|
|
223
|
+
hostname = hostname.slice(0, truncateIdx);
|
|
224
|
+
}
|
|
225
|
+
return hostname;
|
|
226
|
+
}
|
|
227
|
+
function canonicalizePort(port, throwOnInvalid = false) {
|
|
228
|
+
if (port === "")
|
|
229
|
+
return "";
|
|
230
|
+
const stripped = port.replace(/[\t\n\r]/g, "");
|
|
231
|
+
const match = stripped.match(/^(\d+)/);
|
|
232
|
+
if (!match)
|
|
233
|
+
return void 0;
|
|
234
|
+
const numericPort = parseInt(match[1], 10);
|
|
235
|
+
if (numericPort > 65535) {
|
|
236
|
+
if (throwOnInvalid) {
|
|
237
|
+
throw new TypeError(`Invalid port: ${port} (must be 0-65535)`);
|
|
238
|
+
}
|
|
239
|
+
return void 0;
|
|
240
|
+
}
|
|
241
|
+
return numericPort.toString();
|
|
242
|
+
}
|
|
243
|
+
function isValidPatternPort(port) {
|
|
244
|
+
if (port === "")
|
|
245
|
+
return true;
|
|
246
|
+
const stripped = port.replace(/[\t\n\r]/g, "");
|
|
247
|
+
return /^\d+$/.test(stripped);
|
|
248
|
+
}
|
|
249
|
+
function encodePathname(pathname) {
|
|
250
|
+
return pathname.split("/").map((segment) => {
|
|
251
|
+
let result = "";
|
|
252
|
+
for (const char of segment) {
|
|
253
|
+
const code = char.charCodeAt(0);
|
|
254
|
+
if (code >= 65 && code <= 90 || // A-Z
|
|
255
|
+
code >= 97 && code <= 122 || // a-z
|
|
256
|
+
code >= 48 && code <= 57 || // 0-9
|
|
257
|
+
"-._~!$&'()*+,;=:@%".includes(char)) {
|
|
258
|
+
result += char;
|
|
259
|
+
} else {
|
|
260
|
+
result += encodeURIComponent(char);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}).join("/");
|
|
265
|
+
}
|
|
266
|
+
function encodeSearch(search) {
|
|
267
|
+
let result = "";
|
|
268
|
+
for (let i = 0; i < search.length; i++) {
|
|
269
|
+
const char = search[i];
|
|
270
|
+
const code = char.charCodeAt(0);
|
|
271
|
+
if (char === "%") {
|
|
272
|
+
if (i + 2 < search.length) {
|
|
273
|
+
const hex = search.slice(i + 1, i + 3);
|
|
274
|
+
if (/^[0-9a-fA-F]{2}$/.test(hex)) {
|
|
275
|
+
result += search.slice(i, i + 3);
|
|
276
|
+
i += 2;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
result += "%25";
|
|
281
|
+
} else if (code >= 128) {
|
|
282
|
+
if (code >= 55296 && code <= 56319 && i + 1 < search.length) {
|
|
283
|
+
const nextCode = search.charCodeAt(i + 1);
|
|
284
|
+
if (nextCode >= 56320 && nextCode <= 57343) {
|
|
285
|
+
result += encodeURIComponent(search.slice(i, i + 2));
|
|
286
|
+
i++;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
result += encodeURIComponent(char);
|
|
291
|
+
} else {
|
|
292
|
+
result += char;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
function encodeHash(hash) {
|
|
298
|
+
try {
|
|
299
|
+
return encodeURIComponent(decodeURIComponent(hash));
|
|
300
|
+
} catch {
|
|
301
|
+
return encodeURIComponent(hash);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function normalizePathname(pathname) {
|
|
305
|
+
if (!pathname || pathname === "/") {
|
|
306
|
+
return "/";
|
|
307
|
+
}
|
|
308
|
+
const hadLeadingSlash = pathname.startsWith("/");
|
|
309
|
+
const hadTrailingSlash = pathname.endsWith("/") && pathname !== "/";
|
|
310
|
+
const segments = pathname.split("/").filter((s, i, arr) => {
|
|
311
|
+
return s !== "" || i === arr.length - 1 && i > 0;
|
|
312
|
+
});
|
|
313
|
+
const normalized = [];
|
|
314
|
+
for (let i = 0; i < segments.length; i++) {
|
|
315
|
+
const segment = segments[i];
|
|
316
|
+
if (segment === ".") {
|
|
317
|
+
continue;
|
|
318
|
+
} else if (segment === "..") {
|
|
319
|
+
if (normalized.length > 0 && normalized[normalized.length - 1] !== "..") {
|
|
320
|
+
normalized.pop();
|
|
321
|
+
}
|
|
322
|
+
} else if (segment === "" && i === segments.length - 1) {
|
|
323
|
+
continue;
|
|
324
|
+
} else {
|
|
325
|
+
normalized.push(segment);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
let result = normalized.join("/");
|
|
329
|
+
if (hadLeadingSlash) {
|
|
330
|
+
result = "/" + result;
|
|
331
|
+
}
|
|
332
|
+
if (hadTrailingSlash && !result.endsWith("/")) {
|
|
333
|
+
result += "/";
|
|
334
|
+
}
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
function isSimplePattern(pathname) {
|
|
338
|
+
if (pathname.includes("\\"))
|
|
339
|
+
return false;
|
|
340
|
+
if (pathname.includes("{"))
|
|
341
|
+
return false;
|
|
342
|
+
if (pathname.includes("("))
|
|
343
|
+
return false;
|
|
344
|
+
if (pathname.includes("+"))
|
|
345
|
+
return false;
|
|
346
|
+
if (pathname.includes("?"))
|
|
347
|
+
return false;
|
|
348
|
+
const wildcardIndex = pathname.indexOf("*");
|
|
349
|
+
if (wildcardIndex !== -1) {
|
|
350
|
+
if (wildcardIndex !== pathname.length - 1)
|
|
351
|
+
return false;
|
|
352
|
+
if (pathname[wildcardIndex - 1] !== "/")
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
const paramMatches = pathname.matchAll(new RegExp(":(\\p{ID_Continue}+)", "gu"));
|
|
356
|
+
for (const match of paramMatches) {
|
|
357
|
+
const afterParam = pathname[match.index + match[0].length];
|
|
358
|
+
if (afterParam === "(" || afterParam === "?" || afterParam === "+" || afterParam === "*") {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
function parseSimplePattern(pathname) {
|
|
365
|
+
if (!isSimplePattern(pathname)) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
const segments = [];
|
|
369
|
+
const paramNames = [];
|
|
370
|
+
let hasWildcard = false;
|
|
371
|
+
let currentStatic = "";
|
|
372
|
+
let i = 0;
|
|
373
|
+
while (i < pathname.length) {
|
|
374
|
+
const char = pathname[i];
|
|
375
|
+
if (char === ":") {
|
|
376
|
+
const match = pathname.slice(i).match(new RegExp("^:(\\p{ID_Continue}+)", "u"));
|
|
377
|
+
if (match) {
|
|
378
|
+
if (currentStatic) {
|
|
379
|
+
segments.push({ type: "static", value: currentStatic });
|
|
380
|
+
currentStatic = "";
|
|
381
|
+
}
|
|
382
|
+
const name = match[1];
|
|
383
|
+
paramNames.push(name);
|
|
384
|
+
segments.push({ type: "param", name });
|
|
385
|
+
i += match[0].length;
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (char === "*") {
|
|
390
|
+
if (currentStatic) {
|
|
391
|
+
segments.push({ type: "static", value: currentStatic });
|
|
392
|
+
currentStatic = "";
|
|
393
|
+
}
|
|
394
|
+
hasWildcard = true;
|
|
395
|
+
segments.push({ type: "wildcard" });
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
currentStatic += char;
|
|
399
|
+
i++;
|
|
400
|
+
}
|
|
401
|
+
if (currentStatic) {
|
|
402
|
+
segments.push({ type: "static", value: currentStatic });
|
|
403
|
+
}
|
|
404
|
+
return { segments, paramNames, hasWildcard };
|
|
405
|
+
}
|
|
406
|
+
function findUnescapedAt(pattern) {
|
|
407
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
408
|
+
if (pattern[i] === "\\" && i + 1 < pattern.length) {
|
|
409
|
+
i++;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
if (pattern[i] === "@") {
|
|
413
|
+
return i;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return -1;
|
|
417
|
+
}
|
|
418
|
+
function findEscapedColon(pattern) {
|
|
419
|
+
for (let i = 0; i < pattern.length - 1; i++) {
|
|
420
|
+
if (pattern[i] === "\\" && pattern[i + 1] === ":") {
|
|
421
|
+
return i;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return -1;
|
|
425
|
+
}
|
|
426
|
+
function findSearchDelimiter(pattern) {
|
|
427
|
+
let parenDepth = 0;
|
|
428
|
+
let bracketDepth = 0;
|
|
429
|
+
let braceDepth = 0;
|
|
430
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
431
|
+
const char = pattern[i];
|
|
432
|
+
if (char === "\\") {
|
|
433
|
+
if (i + 1 < pattern.length && pattern[i + 1] === "?") {
|
|
434
|
+
if (parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
435
|
+
return { index: i, offset: 2 };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
i++;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (char === "(")
|
|
442
|
+
parenDepth++;
|
|
443
|
+
else if (char === ")")
|
|
444
|
+
parenDepth--;
|
|
445
|
+
else if (char === "[")
|
|
446
|
+
bracketDepth++;
|
|
447
|
+
else if (char === "]")
|
|
448
|
+
bracketDepth--;
|
|
449
|
+
else if (char === "{")
|
|
450
|
+
braceDepth++;
|
|
451
|
+
else if (char === "}")
|
|
452
|
+
braceDepth--;
|
|
453
|
+
if (char === "?" && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
454
|
+
const prev = i > 0 ? pattern[i - 1] : "";
|
|
455
|
+
if ("*+)}".includes(prev)) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
let j = i - 1;
|
|
459
|
+
while (j >= 0 && /[\w]/.test(pattern[j])) {
|
|
460
|
+
j--;
|
|
461
|
+
}
|
|
462
|
+
if (j >= 0 && pattern[j] === ":") {
|
|
463
|
+
const paramStart = j + 1;
|
|
464
|
+
if (paramStart < i && /[a-zA-Z]/.test(pattern[paramStart])) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return { index: i, offset: 1 };
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return { index: -1, offset: 0 };
|
|
472
|
+
}
|
|
473
|
+
function findPathnameStart(afterScheme) {
|
|
474
|
+
let parenDepth = 0;
|
|
475
|
+
let bracketDepth = 0;
|
|
476
|
+
let braceDepth = 0;
|
|
477
|
+
let braceStart = -1;
|
|
478
|
+
for (let i = 0; i < afterScheme.length; i++) {
|
|
479
|
+
const char = afterScheme[i];
|
|
480
|
+
if (char === "\\") {
|
|
481
|
+
i++;
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (char === "(")
|
|
485
|
+
parenDepth++;
|
|
486
|
+
else if (char === ")")
|
|
487
|
+
parenDepth--;
|
|
488
|
+
else if (char === "[")
|
|
489
|
+
bracketDepth++;
|
|
490
|
+
else if (char === "]")
|
|
491
|
+
bracketDepth--;
|
|
492
|
+
else if (char === "{") {
|
|
493
|
+
braceDepth++;
|
|
494
|
+
if (braceDepth === 1)
|
|
495
|
+
braceStart = i;
|
|
496
|
+
} else if (char === "}") {
|
|
497
|
+
braceDepth--;
|
|
498
|
+
if (braceDepth === 0)
|
|
499
|
+
braceStart = -1;
|
|
500
|
+
} else if (char === "/" && parenDepth === 0 && bracketDepth === 0) {
|
|
501
|
+
if (braceDepth > 0 && braceStart !== -1) {
|
|
502
|
+
return { index: i, truncateHostname: i, useWildcardPathname: true };
|
|
503
|
+
}
|
|
504
|
+
return { index: i };
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return { index: -1 };
|
|
508
|
+
}
|
|
509
|
+
function validatePatternStructure(pattern) {
|
|
510
|
+
if (pattern.includes("{{") || pattern.includes("}}")) {
|
|
511
|
+
throw new TypeError("Invalid pattern: consecutive braces are not allowed");
|
|
512
|
+
}
|
|
513
|
+
let braceDepth = 0;
|
|
514
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
515
|
+
const char = pattern[i];
|
|
516
|
+
if (char === "\\") {
|
|
517
|
+
i++;
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
if (char === "{") {
|
|
521
|
+
braceDepth++;
|
|
522
|
+
if (braceDepth > 1) {
|
|
523
|
+
throw new TypeError("Invalid pattern: nested braces are not allowed");
|
|
524
|
+
}
|
|
525
|
+
} else if (char === "}") {
|
|
526
|
+
braceDepth--;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const schemeIdx = pattern.indexOf("://");
|
|
530
|
+
if (schemeIdx !== -1) {
|
|
531
|
+
let parenDepth = 0;
|
|
532
|
+
let bracketDepth = 0;
|
|
533
|
+
braceDepth = 0;
|
|
534
|
+
for (let i = 0; i < schemeIdx; i++) {
|
|
535
|
+
const char = pattern[i];
|
|
536
|
+
if (char === "\\") {
|
|
537
|
+
i++;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (char === "(")
|
|
541
|
+
parenDepth++;
|
|
542
|
+
else if (char === ")")
|
|
543
|
+
parenDepth--;
|
|
544
|
+
else if (char === "[")
|
|
545
|
+
bracketDepth++;
|
|
546
|
+
else if (char === "]")
|
|
547
|
+
bracketDepth--;
|
|
548
|
+
else if (char === "{")
|
|
549
|
+
braceDepth++;
|
|
550
|
+
else if (char === "}")
|
|
551
|
+
braceDepth--;
|
|
552
|
+
}
|
|
553
|
+
if (parenDepth > 0 || bracketDepth > 0 || braceDepth > 0) {
|
|
554
|
+
throw new TypeError(
|
|
555
|
+
"Invalid pattern: groups cannot span the scheme separator"
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function parseStringPattern(pattern) {
|
|
561
|
+
validatePatternStructure(pattern);
|
|
562
|
+
if (pattern.startsWith("?")) {
|
|
563
|
+
const hashIndex = pattern.indexOf("#");
|
|
564
|
+
if (hashIndex !== -1) {
|
|
565
|
+
return {
|
|
566
|
+
search: pattern.slice(1, hashIndex),
|
|
567
|
+
hash: pattern.slice(hashIndex + 1)
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
return { search: pattern.slice(1) };
|
|
571
|
+
}
|
|
572
|
+
if (pattern.startsWith("#")) {
|
|
573
|
+
return { hash: pattern.slice(1) };
|
|
574
|
+
}
|
|
575
|
+
const nonHierarchicalMatch = pattern.match(/^([a-zA-Z][a-zA-Z0-9+\-.]*):/);
|
|
576
|
+
if (nonHierarchicalMatch && !pattern.startsWith(nonHierarchicalMatch[0] + "/")) {
|
|
577
|
+
const protocol = nonHierarchicalMatch[1];
|
|
578
|
+
if (pattern.startsWith(protocol + "://")) {
|
|
579
|
+
} else {
|
|
580
|
+
const rest = pattern.slice(nonHierarchicalMatch[0].length);
|
|
581
|
+
if (rest.length > 0) {
|
|
582
|
+
const firstChar = rest[0];
|
|
583
|
+
if (new RegExp("\\p{ID_Start}", "u").test(firstChar)) {
|
|
584
|
+
throw new TypeError(
|
|
585
|
+
"Invalid URL pattern: non-hierarchical URL pathname cannot start with an identifier"
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const hashIndex = rest.indexOf("#");
|
|
590
|
+
const hash = hashIndex === -1 ? void 0 : rest.slice(hashIndex + 1);
|
|
591
|
+
const beforeHash = hashIndex === -1 ? rest : rest.slice(0, hashIndex);
|
|
592
|
+
const searchDelim = findSearchDelimiter(beforeHash);
|
|
593
|
+
const search = searchDelim.index === -1 ? void 0 : beforeHash.slice(searchDelim.index + searchDelim.offset);
|
|
594
|
+
const pathname = searchDelim.index === -1 ? beforeHash : beforeHash.slice(0, searchDelim.index);
|
|
595
|
+
return { protocol, pathname, search, hash };
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const escapedColonMatch = pattern.match(/^([a-zA-Z][a-zA-Z0-9+\-.]*)\\:/);
|
|
599
|
+
if (escapedColonMatch) {
|
|
600
|
+
const protocol = escapedColonMatch[1];
|
|
601
|
+
const rest = pattern.slice(escapedColonMatch[0].length);
|
|
602
|
+
const isSpecial = isSpecialScheme(protocol);
|
|
603
|
+
const atIdx = findUnescapedAt(rest);
|
|
604
|
+
if (isSpecial && atIdx !== -1) {
|
|
605
|
+
const userinfoStr = rest.slice(0, atIdx);
|
|
606
|
+
const afterAt = rest.slice(atIdx + 1);
|
|
607
|
+
let username;
|
|
608
|
+
let password;
|
|
609
|
+
const colonIdx = findEscapedColon(userinfoStr);
|
|
610
|
+
if (colonIdx !== -1) {
|
|
611
|
+
username = userinfoStr.slice(0, colonIdx);
|
|
612
|
+
password = userinfoStr.slice(colonIdx + 2);
|
|
613
|
+
} else {
|
|
614
|
+
username = userinfoStr;
|
|
615
|
+
}
|
|
616
|
+
const slashIdx = afterAt.indexOf("/");
|
|
617
|
+
const hashIdx = afterAt.indexOf("#");
|
|
618
|
+
const searchDelim2 = findSearchDelimiter(afterAt);
|
|
619
|
+
let hostEnd = afterAt.length;
|
|
620
|
+
if (slashIdx !== -1 && slashIdx < hostEnd)
|
|
621
|
+
hostEnd = slashIdx;
|
|
622
|
+
if (searchDelim2.index !== -1 && searchDelim2.index < hostEnd)
|
|
623
|
+
hostEnd = searchDelim2.index;
|
|
624
|
+
if (hashIdx !== -1 && hashIdx < hostEnd)
|
|
625
|
+
hostEnd = hashIdx;
|
|
626
|
+
const hostPart = afterAt.slice(0, hostEnd);
|
|
627
|
+
let hostname;
|
|
628
|
+
let port;
|
|
629
|
+
const portColonIdx = hostPart.lastIndexOf(":");
|
|
630
|
+
if (portColonIdx !== -1 && !hostPart.startsWith("[")) {
|
|
631
|
+
hostname = hostPart.slice(0, portColonIdx);
|
|
632
|
+
port = hostPart.slice(portColonIdx + 1);
|
|
633
|
+
} else {
|
|
634
|
+
hostname = hostPart;
|
|
635
|
+
}
|
|
636
|
+
let pathname2;
|
|
637
|
+
let search2;
|
|
638
|
+
let hash2;
|
|
639
|
+
if (slashIdx !== -1) {
|
|
640
|
+
const pathStart = slashIdx;
|
|
641
|
+
let pathEnd = afterAt.length;
|
|
642
|
+
if (searchDelim2.index !== -1 && searchDelim2.index > slashIdx)
|
|
643
|
+
pathEnd = Math.min(pathEnd, searchDelim2.index);
|
|
644
|
+
if (hashIdx !== -1)
|
|
645
|
+
pathEnd = Math.min(pathEnd, hashIdx);
|
|
646
|
+
pathname2 = afterAt.slice(pathStart, pathEnd);
|
|
647
|
+
}
|
|
648
|
+
if (searchDelim2.index !== -1) {
|
|
649
|
+
let searchEnd = afterAt.length;
|
|
650
|
+
if (hashIdx !== -1 && hashIdx > searchDelim2.index)
|
|
651
|
+
searchEnd = hashIdx;
|
|
652
|
+
search2 = afterAt.slice(
|
|
653
|
+
searchDelim2.index + searchDelim2.offset,
|
|
654
|
+
searchEnd
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
if (hashIdx !== -1) {
|
|
658
|
+
hash2 = afterAt.slice(hashIdx + 1);
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
protocol,
|
|
662
|
+
username,
|
|
663
|
+
password,
|
|
664
|
+
hostname,
|
|
665
|
+
port,
|
|
666
|
+
pathname: pathname2,
|
|
667
|
+
search: search2,
|
|
668
|
+
hash: hash2
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const hashIndex = rest.indexOf("#");
|
|
672
|
+
const hash = hashIndex === -1 ? void 0 : rest.slice(hashIndex + 1);
|
|
673
|
+
const beforeHash = hashIndex === -1 ? rest : rest.slice(0, hashIndex);
|
|
674
|
+
const searchDelim = findSearchDelimiter(beforeHash);
|
|
675
|
+
const search = searchDelim.index === -1 ? void 0 : beforeHash.slice(searchDelim.index + searchDelim.offset);
|
|
676
|
+
const pathname = searchDelim.index === -1 ? beforeHash : beforeHash.slice(0, searchDelim.index);
|
|
677
|
+
return { protocol, pathname, search, hash };
|
|
678
|
+
}
|
|
679
|
+
const protocolMatch = pattern.match(
|
|
680
|
+
/^([a-zA-Z][a-zA-Z0-9+\-.]*(?:\{[^}]*\}\??)?):/
|
|
681
|
+
);
|
|
682
|
+
if (protocolMatch && pattern.includes("://")) {
|
|
683
|
+
const hashIndex = pattern.indexOf("#");
|
|
684
|
+
const hash = hashIndex === -1 ? void 0 : pattern.slice(hashIndex + 1);
|
|
685
|
+
const beforeHash = hashIndex === -1 ? pattern : pattern.slice(0, hashIndex);
|
|
686
|
+
const searchDelim = findSearchDelimiter(beforeHash);
|
|
687
|
+
const ampIndex2 = beforeHash.indexOf("&");
|
|
688
|
+
let queryIndex = -1;
|
|
689
|
+
let searchOffset = 0;
|
|
690
|
+
if (searchDelim.index !== -1 && (ampIndex2 === -1 || searchDelim.index < ampIndex2)) {
|
|
691
|
+
queryIndex = searchDelim.index;
|
|
692
|
+
searchOffset = searchDelim.offset;
|
|
693
|
+
} else if (ampIndex2 !== -1) {
|
|
694
|
+
queryIndex = ampIndex2;
|
|
695
|
+
searchOffset = 1;
|
|
696
|
+
}
|
|
697
|
+
const search = queryIndex === -1 ? void 0 : beforeHash.slice(queryIndex + searchOffset);
|
|
698
|
+
const urlPart = queryIndex === -1 ? beforeHash : beforeHash.slice(0, queryIndex);
|
|
699
|
+
const schemeEndIdx = urlPart.indexOf("://");
|
|
700
|
+
const protocol = urlPart.slice(0, schemeEndIdx);
|
|
701
|
+
let afterScheme = urlPart.slice(schemeEndIdx + 3);
|
|
702
|
+
let username;
|
|
703
|
+
let password;
|
|
704
|
+
let atIndex = -1;
|
|
705
|
+
let bracketDepth = 0;
|
|
706
|
+
let braceDepth = 0;
|
|
707
|
+
let parenDepth = 0;
|
|
708
|
+
for (let i = 0; i < afterScheme.length; i++) {
|
|
709
|
+
const char = afterScheme[i];
|
|
710
|
+
if (char === "\\") {
|
|
711
|
+
i++;
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
if (char === "[")
|
|
715
|
+
bracketDepth++;
|
|
716
|
+
else if (char === "]")
|
|
717
|
+
bracketDepth--;
|
|
718
|
+
else if (char === "{")
|
|
719
|
+
braceDepth++;
|
|
720
|
+
else if (char === "}")
|
|
721
|
+
braceDepth--;
|
|
722
|
+
else if (char === "(")
|
|
723
|
+
parenDepth++;
|
|
724
|
+
else if (char === ")")
|
|
725
|
+
parenDepth--;
|
|
726
|
+
else if (char === "@" && bracketDepth === 0 && braceDepth === 0 && parenDepth === 0) {
|
|
727
|
+
atIndex = i;
|
|
728
|
+
break;
|
|
729
|
+
} else if (char === "/" && bracketDepth === 0 && braceDepth === 0 && parenDepth === 0) {
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (atIndex !== -1) {
|
|
734
|
+
const userinfo = afterScheme.slice(0, atIndex);
|
|
735
|
+
afterScheme = afterScheme.slice(atIndex + 1);
|
|
736
|
+
let colonIndex = -1;
|
|
737
|
+
let braceDepth2 = 0;
|
|
738
|
+
for (let i = 0; i < userinfo.length; i++) {
|
|
739
|
+
const char = userinfo[i];
|
|
740
|
+
if (char === "\\") {
|
|
741
|
+
if (i + 1 < userinfo.length && userinfo[i + 1] === ":") {
|
|
742
|
+
colonIndex = i + 1;
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
i++;
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
if (char === "{")
|
|
749
|
+
braceDepth2++;
|
|
750
|
+
else if (char === "}")
|
|
751
|
+
braceDepth2--;
|
|
752
|
+
else if (char === ":" && braceDepth2 === 0) {
|
|
753
|
+
const afterColon = userinfo.slice(i + 1);
|
|
754
|
+
const paramMatch = afterColon.match(
|
|
755
|
+
new RegExp("^(\\p{ID_Start}\\p{ID_Continue}*)", "u")
|
|
756
|
+
);
|
|
757
|
+
if (paramMatch) {
|
|
758
|
+
i += 1 + paramMatch[1].length;
|
|
759
|
+
if (i < userinfo.length && "?+*".includes(userinfo[i])) {
|
|
760
|
+
i++;
|
|
761
|
+
}
|
|
762
|
+
i--;
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
colonIndex = i;
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (colonIndex !== -1) {
|
|
770
|
+
username = userinfo.slice(0, colonIndex);
|
|
771
|
+
if (username.endsWith("\\")) {
|
|
772
|
+
username = username.slice(0, -1);
|
|
773
|
+
}
|
|
774
|
+
password = userinfo.slice(colonIndex + 1);
|
|
775
|
+
} else {
|
|
776
|
+
username = userinfo;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const pathnameResult = findPathnameStart(afterScheme);
|
|
780
|
+
let pathname;
|
|
781
|
+
let hostPart;
|
|
782
|
+
if (pathnameResult.useWildcardPathname && pathnameResult.truncateHostname !== void 0) {
|
|
783
|
+
hostPart = afterScheme.slice(0, pathnameResult.truncateHostname);
|
|
784
|
+
const lastBrace = hostPart.lastIndexOf("{");
|
|
785
|
+
if (lastBrace !== -1) {
|
|
786
|
+
const beforeBrace = hostPart.slice(0, lastBrace);
|
|
787
|
+
const braceContent = hostPart.slice(lastBrace + 1);
|
|
788
|
+
hostPart = beforeBrace + braceContent;
|
|
789
|
+
}
|
|
790
|
+
pathname = "*";
|
|
791
|
+
} else if (pathnameResult.index === -1) {
|
|
792
|
+
pathname = "/";
|
|
793
|
+
hostPart = afterScheme;
|
|
794
|
+
} else {
|
|
795
|
+
pathname = afterScheme.slice(pathnameResult.index);
|
|
796
|
+
hostPart = afterScheme.slice(0, pathnameResult.index);
|
|
797
|
+
}
|
|
798
|
+
const portMatch = hostPart.match(/:(\d+|\([^)]+\)|\d*\{[^}]+\}\??)$/);
|
|
799
|
+
const port = portMatch ? portMatch[1] : "";
|
|
800
|
+
const hostname = portMatch ? hostPart.slice(0, -portMatch[0].length) : hostPart;
|
|
801
|
+
return {
|
|
802
|
+
protocol,
|
|
803
|
+
username,
|
|
804
|
+
password,
|
|
805
|
+
hostname,
|
|
806
|
+
port,
|
|
807
|
+
pathname,
|
|
808
|
+
search,
|
|
809
|
+
hash
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
if (pattern.startsWith("/")) {
|
|
813
|
+
const hashIndex = pattern.indexOf("#");
|
|
814
|
+
const hash = hashIndex === -1 ? void 0 : pattern.slice(hashIndex + 1);
|
|
815
|
+
const beforeHash = hashIndex === -1 ? pattern : pattern.slice(0, hashIndex);
|
|
816
|
+
const searchDelim = findSearchDelimiter(beforeHash);
|
|
817
|
+
const ampIndex2 = beforeHash.indexOf("&");
|
|
818
|
+
let queryIndex = -1;
|
|
819
|
+
let searchOffset = 0;
|
|
820
|
+
if (searchDelim.index !== -1 && (ampIndex2 === -1 || searchDelim.index < ampIndex2)) {
|
|
821
|
+
queryIndex = searchDelim.index;
|
|
822
|
+
searchOffset = searchDelim.offset;
|
|
823
|
+
} else if (ampIndex2 !== -1) {
|
|
824
|
+
queryIndex = ampIndex2;
|
|
825
|
+
searchOffset = 1;
|
|
826
|
+
}
|
|
827
|
+
const search = queryIndex === -1 ? void 0 : beforeHash.slice(queryIndex + searchOffset);
|
|
828
|
+
const pathname = queryIndex === -1 ? beforeHash : beforeHash.slice(0, queryIndex);
|
|
829
|
+
return { pathname, search, hash };
|
|
830
|
+
}
|
|
831
|
+
const ampIndex = pattern.indexOf("&");
|
|
832
|
+
if (ampIndex === -1) {
|
|
833
|
+
return { pathname: pattern };
|
|
834
|
+
}
|
|
835
|
+
if (ampIndex === 0) {
|
|
836
|
+
return { search: pattern.slice(1) };
|
|
837
|
+
}
|
|
838
|
+
return {
|
|
839
|
+
pathname: pattern.slice(0, ampIndex),
|
|
840
|
+
search: pattern.slice(ampIndex + 1)
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
function compileComponentPattern(component, ignoreCase = false) {
|
|
844
|
+
const paramNames = [];
|
|
845
|
+
let hasWildcard = false;
|
|
846
|
+
let pattern = "";
|
|
847
|
+
let i = 0;
|
|
848
|
+
let inIPv6Brackets = false;
|
|
849
|
+
while (i < component.length) {
|
|
850
|
+
const char = component[i];
|
|
851
|
+
if (char === "[" && !inIPv6Brackets) {
|
|
852
|
+
inIPv6Brackets = true;
|
|
853
|
+
} else if (char === "]" && inIPv6Brackets) {
|
|
854
|
+
inIPv6Brackets = false;
|
|
855
|
+
}
|
|
856
|
+
if (char === "\\") {
|
|
857
|
+
if (i + 1 < component.length) {
|
|
858
|
+
pattern += "\\" + component[i + 1];
|
|
859
|
+
i += 2;
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
pattern += "\\\\";
|
|
863
|
+
i++;
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
if (char === ":") {
|
|
867
|
+
const match = component.slice(i).match(new RegExp("^:(\\p{ID_Continue}+)(\\([^)]*\\))?(\\?|\\+|\\*)?", "u"));
|
|
868
|
+
if (match) {
|
|
869
|
+
const name = match[1];
|
|
870
|
+
const constraint = match[2];
|
|
871
|
+
const modifier = match[3] || "";
|
|
872
|
+
if (paramNames.includes(name)) {
|
|
873
|
+
throw new TypeError(
|
|
874
|
+
`Invalid pattern: duplicate parameter name '${name}'`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
paramNames.push(name);
|
|
878
|
+
let basePattern;
|
|
879
|
+
if (constraint) {
|
|
880
|
+
basePattern = constraint.slice(1, -1);
|
|
881
|
+
} else if (inIPv6Brackets) {
|
|
882
|
+
basePattern = "[^\\[\\]/?#]+?";
|
|
883
|
+
} else {
|
|
884
|
+
basePattern = "[^./:?#]+?";
|
|
885
|
+
}
|
|
886
|
+
if (modifier === "?") {
|
|
887
|
+
pattern += `(${basePattern})?`;
|
|
888
|
+
} else if (modifier === "+") {
|
|
889
|
+
pattern += `(${basePattern})+`;
|
|
890
|
+
} else if (modifier === "*") {
|
|
891
|
+
pattern += `(${basePattern})*`;
|
|
892
|
+
} else {
|
|
893
|
+
pattern += `(${basePattern})`;
|
|
894
|
+
}
|
|
895
|
+
i += match[0].length;
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (char === "(" && component[i + 1] !== "?") {
|
|
900
|
+
let depth = 1;
|
|
901
|
+
let j = i + 1;
|
|
902
|
+
let regexContent = "";
|
|
903
|
+
while (j < component.length && depth > 0) {
|
|
904
|
+
if (component[j] === "\\") {
|
|
905
|
+
regexContent += component[j] + (component[j + 1] || "");
|
|
906
|
+
j += 2;
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
if (component[j] === "(")
|
|
910
|
+
depth++;
|
|
911
|
+
if (component[j] === ")")
|
|
912
|
+
depth--;
|
|
913
|
+
if (depth > 0)
|
|
914
|
+
regexContent += component[j];
|
|
915
|
+
j++;
|
|
916
|
+
}
|
|
917
|
+
validateRegexGroupContent(regexContent);
|
|
918
|
+
const modifier = component[j] || "";
|
|
919
|
+
if (modifier === "?" || modifier === "+" || modifier === "*") {
|
|
920
|
+
pattern += `(${regexContent})${modifier}`;
|
|
921
|
+
j++;
|
|
922
|
+
} else {
|
|
923
|
+
pattern += `(${regexContent})`;
|
|
924
|
+
}
|
|
925
|
+
paramNames.push(`$${paramNames.length}`);
|
|
926
|
+
i = j;
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
if (char === "{") {
|
|
930
|
+
const closeIndex = component.indexOf("}", i);
|
|
931
|
+
if (closeIndex !== -1) {
|
|
932
|
+
const content = component.slice(i + 1, closeIndex);
|
|
933
|
+
const nextChar = component[closeIndex + 1] || "";
|
|
934
|
+
const isModifier = nextChar === "?" || nextChar === "+" || nextChar === "*";
|
|
935
|
+
const compiled = compileComponentPattern(content, ignoreCase);
|
|
936
|
+
if (nextChar === "?") {
|
|
937
|
+
pattern += `(?:${compiled.regex.source.slice(1, -1)})?`;
|
|
938
|
+
} else if (nextChar === "+") {
|
|
939
|
+
pattern += `(?:${compiled.regex.source.slice(1, -1)})+`;
|
|
940
|
+
} else if (nextChar === "*") {
|
|
941
|
+
pattern += `(?:${compiled.regex.source.slice(1, -1)})*`;
|
|
942
|
+
} else {
|
|
943
|
+
pattern += compiled.regex.source.slice(1, -1);
|
|
944
|
+
}
|
|
945
|
+
paramNames.push(...compiled.paramNames);
|
|
946
|
+
i = closeIndex + 1;
|
|
947
|
+
if (isModifier)
|
|
948
|
+
i++;
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (char === "*") {
|
|
953
|
+
hasWildcard = true;
|
|
954
|
+
const modifier = component[i + 1] || "";
|
|
955
|
+
if (modifier === "?" || modifier === "+" || modifier === "*") {
|
|
956
|
+
pattern += `(.*)${modifier}`;
|
|
957
|
+
i += 2;
|
|
958
|
+
} else {
|
|
959
|
+
pattern += "(.*)";
|
|
960
|
+
i++;
|
|
961
|
+
}
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
if (".+?^${}()|[]\\".includes(char)) {
|
|
965
|
+
pattern += "\\" + char;
|
|
966
|
+
i++;
|
|
967
|
+
} else {
|
|
968
|
+
const code = char.charCodeAt(0);
|
|
969
|
+
if (code > 127) {
|
|
970
|
+
let toEncode = char;
|
|
971
|
+
if (code >= 55296 && code <= 56319 && i + 1 < component.length) {
|
|
972
|
+
const nextCode = component.charCodeAt(i + 1);
|
|
973
|
+
if (nextCode >= 56320 && nextCode <= 57343) {
|
|
974
|
+
toEncode = char + component[i + 1];
|
|
975
|
+
i++;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
pattern += encodeURIComponent(toEncode);
|
|
979
|
+
i++;
|
|
980
|
+
} else {
|
|
981
|
+
pattern += char;
|
|
982
|
+
i++;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
const needsVFlag = requiresVFlag(pattern);
|
|
987
|
+
let flags = "";
|
|
988
|
+
if (ignoreCase)
|
|
989
|
+
flags += "i";
|
|
990
|
+
if (needsVFlag)
|
|
991
|
+
flags += "v";
|
|
992
|
+
const regex = new RegExp(`^${pattern}$`, flags || void 0);
|
|
993
|
+
return { regex, paramNames, hasWildcard };
|
|
994
|
+
}
|
|
995
|
+
function compilePathname(pathname, encodeChars = true, ignoreCase = false) {
|
|
996
|
+
const paramNames = [];
|
|
997
|
+
let hasWildcard = false;
|
|
998
|
+
let pattern = "";
|
|
999
|
+
let i = 0;
|
|
1000
|
+
while (i < pathname.length) {
|
|
1001
|
+
const char = pathname[i];
|
|
1002
|
+
if (char === "\\") {
|
|
1003
|
+
if (i + 1 < pathname.length) {
|
|
1004
|
+
const nextChar = pathname[i + 1];
|
|
1005
|
+
const allowedInPath = (c) => {
|
|
1006
|
+
const code = c.charCodeAt(0);
|
|
1007
|
+
return code >= 65 && code <= 90 || // A-Z
|
|
1008
|
+
code >= 97 && code <= 122 || // a-z
|
|
1009
|
+
code >= 48 && code <= 57 || // 0-9
|
|
1010
|
+
"-._~!$&'()*+,;=:@/".includes(c);
|
|
1011
|
+
};
|
|
1012
|
+
if (allowedInPath(nextChar)) {
|
|
1013
|
+
if (".+?^${}()|[]\\*".includes(nextChar)) {
|
|
1014
|
+
pattern += "\\" + nextChar;
|
|
1015
|
+
} else {
|
|
1016
|
+
pattern += nextChar;
|
|
1017
|
+
}
|
|
1018
|
+
} else {
|
|
1019
|
+
pattern += encodeURIComponent(nextChar);
|
|
1020
|
+
}
|
|
1021
|
+
i += 2;
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
pattern += "\\\\";
|
|
1025
|
+
i++;
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
if (char === ":") {
|
|
1029
|
+
const match = pathname.slice(i).match(new RegExp("^:(\\p{ID_Continue}+)(\\([^)]*\\))?(\\?|\\+|\\*)?", "u"));
|
|
1030
|
+
if (match) {
|
|
1031
|
+
const name = match[1];
|
|
1032
|
+
const constraint = match[2];
|
|
1033
|
+
const modifier = match[3] || "";
|
|
1034
|
+
if (paramNames.includes(name)) {
|
|
1035
|
+
throw new TypeError(
|
|
1036
|
+
`Invalid pattern: duplicate parameter name '${name}'`
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
paramNames.push(name);
|
|
1040
|
+
let basePattern;
|
|
1041
|
+
if (constraint) {
|
|
1042
|
+
basePattern = constraint.slice(1, -1);
|
|
1043
|
+
} else {
|
|
1044
|
+
basePattern = "[^/]+?";
|
|
1045
|
+
}
|
|
1046
|
+
const hasPrecedingSlash = pattern.endsWith("/");
|
|
1047
|
+
if (modifier === "?") {
|
|
1048
|
+
const nextChar = pathname[i + match[0].length];
|
|
1049
|
+
const isAtEnd = nextChar === void 0;
|
|
1050
|
+
const isFollowedBySlash = nextChar === "/";
|
|
1051
|
+
if (hasPrecedingSlash && (isAtEnd || isFollowedBySlash)) {
|
|
1052
|
+
pattern = pattern.slice(0, -1);
|
|
1053
|
+
pattern += `(?:/(${basePattern}))?`;
|
|
1054
|
+
} else if (hasPrecedingSlash) {
|
|
1055
|
+
pattern += `([^/]*?)`;
|
|
1056
|
+
} else {
|
|
1057
|
+
pattern += `(${basePattern})?`;
|
|
1058
|
+
}
|
|
1059
|
+
} else if (modifier === "+") {
|
|
1060
|
+
const multiSegmentPattern = `${basePattern}(?:/${basePattern})*`;
|
|
1061
|
+
if (hasPrecedingSlash) {
|
|
1062
|
+
pattern = pattern.slice(0, -1);
|
|
1063
|
+
pattern += `/(${multiSegmentPattern})`;
|
|
1064
|
+
} else {
|
|
1065
|
+
pattern += `(${multiSegmentPattern})`;
|
|
1066
|
+
}
|
|
1067
|
+
} else if (modifier === "*") {
|
|
1068
|
+
const multiSegmentPattern = `${basePattern}(?:/${basePattern})*`;
|
|
1069
|
+
if (hasPrecedingSlash) {
|
|
1070
|
+
pattern = pattern.slice(0, -1);
|
|
1071
|
+
pattern += `(?:/(${multiSegmentPattern}))?`;
|
|
1072
|
+
} else {
|
|
1073
|
+
pattern += `(${multiSegmentPattern})?`;
|
|
1074
|
+
}
|
|
1075
|
+
} else {
|
|
1076
|
+
pattern += `(${basePattern})`;
|
|
1077
|
+
}
|
|
1078
|
+
i += match[0].length;
|
|
1079
|
+
continue;
|
|
1080
|
+
} else {
|
|
1081
|
+
const nextChar = pathname[i + 1];
|
|
1082
|
+
if (nextChar && !/^[\s/?#(){}*+]$/.test(nextChar)) {
|
|
1083
|
+
throw new TypeError(
|
|
1084
|
+
`Invalid pattern: invalid parameter name character after ':'`
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (char === "(" && pathname[i + 1] !== "?") {
|
|
1090
|
+
let depth = 1;
|
|
1091
|
+
let j = i + 1;
|
|
1092
|
+
let regexContent = "";
|
|
1093
|
+
while (j < pathname.length && depth > 0) {
|
|
1094
|
+
if (pathname[j] === "\\") {
|
|
1095
|
+
regexContent += pathname[j] + (pathname[j + 1] || "");
|
|
1096
|
+
j += 2;
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
if (pathname[j] === "(")
|
|
1100
|
+
depth++;
|
|
1101
|
+
if (pathname[j] === ")")
|
|
1102
|
+
depth--;
|
|
1103
|
+
if (depth > 0)
|
|
1104
|
+
regexContent += pathname[j];
|
|
1105
|
+
j++;
|
|
1106
|
+
}
|
|
1107
|
+
validateRegexGroupContent(regexContent);
|
|
1108
|
+
const modifier = pathname[j] || "";
|
|
1109
|
+
const hasPrecedingSlash = pattern.endsWith("/");
|
|
1110
|
+
if (modifier === "?") {
|
|
1111
|
+
if (hasPrecedingSlash) {
|
|
1112
|
+
pattern = pattern.slice(0, -1);
|
|
1113
|
+
pattern += `(?:/(${regexContent}))?`;
|
|
1114
|
+
} else {
|
|
1115
|
+
pattern += `(${regexContent})?`;
|
|
1116
|
+
}
|
|
1117
|
+
j++;
|
|
1118
|
+
} else if (modifier === "+") {
|
|
1119
|
+
const multiSegmentPattern = `${regexContent}(?:/${regexContent})*`;
|
|
1120
|
+
if (hasPrecedingSlash) {
|
|
1121
|
+
pattern = pattern.slice(0, -1);
|
|
1122
|
+
pattern += `/(${multiSegmentPattern})`;
|
|
1123
|
+
} else {
|
|
1124
|
+
pattern += `(${regexContent})+`;
|
|
1125
|
+
}
|
|
1126
|
+
j++;
|
|
1127
|
+
} else if (modifier === "*") {
|
|
1128
|
+
const multiSegmentPattern = `${regexContent}(?:/${regexContent})*`;
|
|
1129
|
+
if (hasPrecedingSlash) {
|
|
1130
|
+
pattern = pattern.slice(0, -1);
|
|
1131
|
+
pattern += `(?:/(${multiSegmentPattern}))?`;
|
|
1132
|
+
} else {
|
|
1133
|
+
pattern += `(${regexContent})*`;
|
|
1134
|
+
}
|
|
1135
|
+
j++;
|
|
1136
|
+
} else {
|
|
1137
|
+
pattern += `(${regexContent})`;
|
|
1138
|
+
}
|
|
1139
|
+
paramNames.push(`$${paramNames.length}`);
|
|
1140
|
+
i = j;
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
if (char === "{") {
|
|
1144
|
+
const closeIndex = pathname.indexOf("}", i);
|
|
1145
|
+
if (closeIndex !== -1) {
|
|
1146
|
+
const content = pathname.slice(i + 1, closeIndex);
|
|
1147
|
+
const nextChar = pathname[closeIndex + 1] || "";
|
|
1148
|
+
const isModifier = nextChar === "?" || nextChar === "+" || nextChar === "*";
|
|
1149
|
+
const compiled = compilePathname(content, encodeChars, ignoreCase);
|
|
1150
|
+
if (nextChar === "?") {
|
|
1151
|
+
pattern += `(?:${compiled.regex.source.slice(1, -1)})?`;
|
|
1152
|
+
} else if (nextChar === "+") {
|
|
1153
|
+
pattern += `(?:${compiled.regex.source.slice(1, -1)})+`;
|
|
1154
|
+
} else if (nextChar === "*") {
|
|
1155
|
+
pattern += `(?:${compiled.regex.source.slice(1, -1)})*`;
|
|
1156
|
+
} else {
|
|
1157
|
+
pattern += compiled.regex.source.slice(1, -1);
|
|
1158
|
+
}
|
|
1159
|
+
paramNames.push(...compiled.paramNames);
|
|
1160
|
+
i = closeIndex + 1;
|
|
1161
|
+
if (isModifier)
|
|
1162
|
+
i++;
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
if (char === "*") {
|
|
1167
|
+
hasWildcard = true;
|
|
1168
|
+
const modifier = pathname[i + 1] || "";
|
|
1169
|
+
const hasPrecedingSlash = pattern.endsWith("/");
|
|
1170
|
+
paramNames.push(String(paramNames.length));
|
|
1171
|
+
if (modifier === "?") {
|
|
1172
|
+
if (hasPrecedingSlash) {
|
|
1173
|
+
pattern = pattern.slice(0, -1);
|
|
1174
|
+
pattern += `(?:/(.*))?`;
|
|
1175
|
+
} else {
|
|
1176
|
+
pattern += `(.*)?`;
|
|
1177
|
+
}
|
|
1178
|
+
i += 2;
|
|
1179
|
+
} else if (modifier === "+") {
|
|
1180
|
+
if (hasPrecedingSlash) {
|
|
1181
|
+
pattern = pattern.slice(0, -1);
|
|
1182
|
+
pattern += `/(.*)(?:/(.*))*`;
|
|
1183
|
+
} else {
|
|
1184
|
+
pattern += `(.*)+`;
|
|
1185
|
+
}
|
|
1186
|
+
i += 2;
|
|
1187
|
+
} else if (modifier === "*") {
|
|
1188
|
+
if (hasPrecedingSlash) {
|
|
1189
|
+
pattern = pattern.slice(0, -1);
|
|
1190
|
+
pattern += `(?:/(.*))*`;
|
|
1191
|
+
} else {
|
|
1192
|
+
pattern += `(.*)*`;
|
|
1193
|
+
}
|
|
1194
|
+
i += 2;
|
|
1195
|
+
} else {
|
|
1196
|
+
pattern += "(.*)";
|
|
1197
|
+
i++;
|
|
1198
|
+
}
|
|
1199
|
+
continue;
|
|
1200
|
+
}
|
|
1201
|
+
if (".+?^${}()|[]\\".includes(char)) {
|
|
1202
|
+
pattern += "\\" + char;
|
|
1203
|
+
} else if (encodeChars) {
|
|
1204
|
+
const code = char.charCodeAt(0);
|
|
1205
|
+
const isAllowedInPath = code >= 65 && code <= 90 || // A-Z
|
|
1206
|
+
code >= 97 && code <= 122 || // a-z
|
|
1207
|
+
code >= 48 && code <= 57 || // 0-9
|
|
1208
|
+
"-._~!$&'()*+,;=:@/%".includes(char);
|
|
1209
|
+
if (isAllowedInPath) {
|
|
1210
|
+
pattern += char;
|
|
1211
|
+
} else {
|
|
1212
|
+
const code2 = char.charCodeAt(0);
|
|
1213
|
+
if (code2 >= 55296 && code2 <= 56319) {
|
|
1214
|
+
const nextCode = i + 1 < pathname.length ? pathname.charCodeAt(i + 1) : 0;
|
|
1215
|
+
if (nextCode >= 56320 && nextCode <= 57343) {
|
|
1216
|
+
pattern += encodeURIComponent(char + pathname[i + 1]);
|
|
1217
|
+
i++;
|
|
1218
|
+
} else {
|
|
1219
|
+
pattern += "%EF%BF%BD";
|
|
1220
|
+
}
|
|
1221
|
+
} else if (code2 >= 56320 && code2 <= 57343) {
|
|
1222
|
+
pattern += "%EF%BF%BD";
|
|
1223
|
+
} else {
|
|
1224
|
+
pattern += encodeURIComponent(char);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
} else {
|
|
1228
|
+
pattern += char;
|
|
1229
|
+
}
|
|
1230
|
+
i++;
|
|
1231
|
+
}
|
|
1232
|
+
const needsVFlag = requiresVFlag(pattern);
|
|
1233
|
+
let flags = "";
|
|
1234
|
+
if (ignoreCase)
|
|
1235
|
+
flags += "i";
|
|
1236
|
+
if (needsVFlag)
|
|
1237
|
+
flags += "v";
|
|
1238
|
+
const regex = new RegExp(`^${pattern}$`, flags || void 0);
|
|
1239
|
+
return { regex, paramNames, hasWildcard };
|
|
1240
|
+
}
|
|
1241
|
+
function testSearchParameters(searchPattern, actualSearch) {
|
|
1242
|
+
const patternParams = parseSearchPattern(searchPattern);
|
|
1243
|
+
const actualParams = parseRawSearchParams(actualSearch);
|
|
1244
|
+
for (const [key, paramDef] of patternParams) {
|
|
1245
|
+
if (!actualParams.has(key)) {
|
|
1246
|
+
return false;
|
|
1247
|
+
}
|
|
1248
|
+
if (paramDef.type === "literal") {
|
|
1249
|
+
if (actualParams.get(key) !== paramDef.value) {
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return true;
|
|
1255
|
+
}
|
|
1256
|
+
function parseRawSearchParams(search) {
|
|
1257
|
+
const params = /* @__PURE__ */ new Map();
|
|
1258
|
+
if (!search)
|
|
1259
|
+
return params;
|
|
1260
|
+
const parts = search.split("&");
|
|
1261
|
+
for (const part of parts) {
|
|
1262
|
+
const eqIdx = part.indexOf("=");
|
|
1263
|
+
if (eqIdx === -1) {
|
|
1264
|
+
params.set(part, "");
|
|
1265
|
+
} else {
|
|
1266
|
+
params.set(part.slice(0, eqIdx), part.slice(eqIdx + 1));
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return params;
|
|
1270
|
+
}
|
|
1271
|
+
function extractSearchParams(searchPattern, actualParams) {
|
|
1272
|
+
const params = {};
|
|
1273
|
+
const patternParams = parseSearchPattern(searchPattern);
|
|
1274
|
+
for (const [key, paramDef] of patternParams) {
|
|
1275
|
+
const value = actualParams.get(key);
|
|
1276
|
+
if (value !== null) {
|
|
1277
|
+
if (paramDef.type === "named" && paramDef.name) {
|
|
1278
|
+
params[paramDef.name] = value;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
for (const [key, value] of actualParams) {
|
|
1283
|
+
if (!params[key]) {
|
|
1284
|
+
params[key] = value;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return params;
|
|
1288
|
+
}
|
|
1289
|
+
function parseSearchPattern(pattern) {
|
|
1290
|
+
const params = /* @__PURE__ */ new Map();
|
|
1291
|
+
const parts = pattern.split("&");
|
|
1292
|
+
for (const part of parts) {
|
|
1293
|
+
const [key, value] = part.split("=");
|
|
1294
|
+
if (!key)
|
|
1295
|
+
continue;
|
|
1296
|
+
if (value === void 0) {
|
|
1297
|
+
params.set(key, { type: "wildcard" });
|
|
1298
|
+
continue;
|
|
1299
|
+
}
|
|
1300
|
+
if (value.startsWith(":")) {
|
|
1301
|
+
const isOptional = value.endsWith("?");
|
|
1302
|
+
params.set(key, {
|
|
1303
|
+
type: "named",
|
|
1304
|
+
name: value.slice(1, isOptional ? -1 : void 0)
|
|
1305
|
+
});
|
|
1306
|
+
} else if (value === "*") {
|
|
1307
|
+
params.set(key, { type: "wildcard" });
|
|
1308
|
+
} else {
|
|
1309
|
+
params.set(key, { type: "literal", value: encodeSearch(value) });
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
return params;
|
|
1313
|
+
}
|
|
1314
|
+
function compileURLPatternInit(init, baseURL, options) {
|
|
1315
|
+
let baseURLParsed;
|
|
1316
|
+
const base = init.baseURL || baseURL;
|
|
1317
|
+
if (base) {
|
|
1318
|
+
try {
|
|
1319
|
+
baseURLParsed = new URL(base);
|
|
1320
|
+
} catch {
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
const result = {};
|
|
1324
|
+
let protocol = init.protocol || (baseURLParsed ? baseURLParsed.protocol.replace(":", "") : void 0);
|
|
1325
|
+
if (protocol) {
|
|
1326
|
+
if (protocol.endsWith(":")) {
|
|
1327
|
+
protocol = protocol.slice(0, -1);
|
|
1328
|
+
}
|
|
1329
|
+
result.protocol = compileComponentPattern(protocol, options.ignoreCase);
|
|
1330
|
+
}
|
|
1331
|
+
let hostname = init.hostname || (baseURLParsed ? baseURLParsed.hostname : void 0);
|
|
1332
|
+
if (hostname) {
|
|
1333
|
+
validateHostnamePattern(hostname);
|
|
1334
|
+
hostname = normalizeHostnamePattern(hostname);
|
|
1335
|
+
if (hostname.startsWith("[")) {
|
|
1336
|
+
hostname = hostname.replace(/[A-F]/g, (c) => c.toLowerCase());
|
|
1337
|
+
} else {
|
|
1338
|
+
const hasPatternSyntax = /[{}()*+?:|\\]/.test(hostname);
|
|
1339
|
+
if (!hasPatternSyntax) {
|
|
1340
|
+
hostname = toASCII(hostname);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
result.hostname = compileComponentPattern(hostname, options.ignoreCase);
|
|
1344
|
+
}
|
|
1345
|
+
if (init.port !== void 0) {
|
|
1346
|
+
let port = init.port;
|
|
1347
|
+
const hasPatternSyntax = /[{}()*+?:|\\]/.test(port);
|
|
1348
|
+
if (protocol && !hasPatternSyntax && isValidPatternPort(port)) {
|
|
1349
|
+
const canonicalPort = canonicalizePort(port, true);
|
|
1350
|
+
if (canonicalPort !== void 0) {
|
|
1351
|
+
const defaultPort = getDefaultPort(protocol);
|
|
1352
|
+
if (defaultPort && canonicalPort === defaultPort) {
|
|
1353
|
+
port = "";
|
|
1354
|
+
} else {
|
|
1355
|
+
port = canonicalPort;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
result.port = compileComponentPattern(port, options.ignoreCase);
|
|
1360
|
+
}
|
|
1361
|
+
if (init.username !== void 0) {
|
|
1362
|
+
result.username = compileComponentPattern(
|
|
1363
|
+
init.username,
|
|
1364
|
+
options.ignoreCase
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
if (init.password !== void 0) {
|
|
1368
|
+
result.password = compileComponentPattern(
|
|
1369
|
+
init.password,
|
|
1370
|
+
options.ignoreCase
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
if (init.search !== void 0) {
|
|
1374
|
+
let search = init.search;
|
|
1375
|
+
if (search.startsWith("?")) {
|
|
1376
|
+
search = search.slice(1);
|
|
1377
|
+
}
|
|
1378
|
+
result.search = compileComponentPattern(search, options.ignoreCase);
|
|
1379
|
+
}
|
|
1380
|
+
if (init.hash !== void 0) {
|
|
1381
|
+
let hash = init.hash;
|
|
1382
|
+
if (hash.startsWith("#")) {
|
|
1383
|
+
hash = hash.slice(1);
|
|
1384
|
+
}
|
|
1385
|
+
result.hash = compileComponentPattern(hash, options.ignoreCase);
|
|
1386
|
+
}
|
|
1387
|
+
let pathname;
|
|
1388
|
+
const isNonSpecialScheme = protocol && isDefinitelyNonSpecialScheme(protocol);
|
|
1389
|
+
if (init.pathname !== void 0 && init.pathname !== "") {
|
|
1390
|
+
pathname = init.pathname;
|
|
1391
|
+
if (baseURLParsed && !pathname.startsWith("/")) {
|
|
1392
|
+
const basePath = baseURLParsed.pathname;
|
|
1393
|
+
if (basePath.endsWith("/")) {
|
|
1394
|
+
pathname = basePath + pathname;
|
|
1395
|
+
} else {
|
|
1396
|
+
pathname = basePath.slice(0, basePath.lastIndexOf("/") + 1) + pathname;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
} else if (baseURLParsed) {
|
|
1400
|
+
pathname = baseURLParsed.pathname;
|
|
1401
|
+
} else {
|
|
1402
|
+
pathname = "*";
|
|
1403
|
+
}
|
|
1404
|
+
if (pathname !== "" && pathname !== "*" && pathname.startsWith("/")) {
|
|
1405
|
+
pathname = normalizePathname(pathname);
|
|
1406
|
+
}
|
|
1407
|
+
const shouldEncodePathname = !isNonSpecialScheme;
|
|
1408
|
+
result.pathname = compilePathname(
|
|
1409
|
+
pathname,
|
|
1410
|
+
shouldEncodePathname,
|
|
1411
|
+
options.ignoreCase
|
|
1412
|
+
);
|
|
1413
|
+
return result;
|
|
1414
|
+
}
|
|
1415
|
+
function testURLComponents(compiled, url) {
|
|
1416
|
+
if (compiled.protocol) {
|
|
1417
|
+
const protocol = url.protocol.replace(":", "");
|
|
1418
|
+
if (!compiled.protocol.regex.test(protocol)) {
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
if (compiled.hostname) {
|
|
1423
|
+
if (!compiled.hostname.regex.test(url.hostname)) {
|
|
1424
|
+
return false;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
if (compiled.port) {
|
|
1428
|
+
if (!compiled.port.regex.test(url.port)) {
|
|
1429
|
+
return false;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
if (compiled.username) {
|
|
1433
|
+
const username = encodeURIComponent(url.username);
|
|
1434
|
+
if (!compiled.username.regex.test(username)) {
|
|
1435
|
+
return false;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
if (compiled.password) {
|
|
1439
|
+
const password = encodeURIComponent(url.password);
|
|
1440
|
+
if (!compiled.password.regex.test(password)) {
|
|
1441
|
+
return false;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
if (compiled.hash) {
|
|
1445
|
+
const hash = url.hash.replace("#", "");
|
|
1446
|
+
if (!compiled.hash.regex.test(hash)) {
|
|
1447
|
+
return false;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
if (!compiled.pathname.regex.test(url.pathname)) {
|
|
1451
|
+
return false;
|
|
1452
|
+
}
|
|
1453
|
+
return true;
|
|
1454
|
+
}
|
|
1455
|
+
function testSearchRegex(compiled, url) {
|
|
1456
|
+
if (compiled.search) {
|
|
1457
|
+
const search = url.search.replace("?", "");
|
|
1458
|
+
return compiled.search.regex.test(search);
|
|
1459
|
+
}
|
|
1460
|
+
return true;
|
|
1461
|
+
}
|
|
1462
|
+
function testSearchParams(searchPattern, rawSearch) {
|
|
1463
|
+
if (!searchPattern) {
|
|
1464
|
+
return true;
|
|
1465
|
+
}
|
|
1466
|
+
return testSearchParameters(searchPattern, rawSearch);
|
|
1467
|
+
}
|
|
1468
|
+
function parseConstructorArgs(input, baseURLOrOptions, options) {
|
|
1469
|
+
let baseURL;
|
|
1470
|
+
let opts = {};
|
|
1471
|
+
if (input === void 0) {
|
|
1472
|
+
input = {};
|
|
1473
|
+
}
|
|
1474
|
+
if (typeof input === "object") {
|
|
1475
|
+
if (typeof baseURLOrOptions === "string") {
|
|
1476
|
+
throw new TypeError(
|
|
1477
|
+
"Invalid arguments: baseURL must be inside the object, not as second argument"
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
opts = baseURLOrOptions || {};
|
|
1481
|
+
if (input.baseURL === "") {
|
|
1482
|
+
throw new TypeError("Invalid pattern: baseURL cannot be empty string");
|
|
1483
|
+
}
|
|
1484
|
+
} else {
|
|
1485
|
+
if (typeof baseURLOrOptions === "string") {
|
|
1486
|
+
if (baseURLOrOptions === "") {
|
|
1487
|
+
throw new TypeError("Invalid pattern: baseURL cannot be empty string");
|
|
1488
|
+
}
|
|
1489
|
+
baseURL = baseURLOrOptions;
|
|
1490
|
+
opts = options || {};
|
|
1491
|
+
} else if (typeof baseURLOrOptions === "object") {
|
|
1492
|
+
opts = baseURLOrOptions;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
const init = typeof input === "string" ? parseStringPattern(input) : input;
|
|
1496
|
+
return { init, baseURL, options: opts };
|
|
1497
|
+
}
|
|
1498
|
+
var URLPattern = class {
|
|
1499
|
+
#compiled;
|
|
1500
|
+
#init;
|
|
1501
|
+
get pathname() {
|
|
1502
|
+
return this.#init.pathname || "*";
|
|
1503
|
+
}
|
|
1504
|
+
get search() {
|
|
1505
|
+
return this.#init.search || "*";
|
|
1506
|
+
}
|
|
1507
|
+
get protocol() {
|
|
1508
|
+
return this.#init.protocol || "*";
|
|
1509
|
+
}
|
|
1510
|
+
get hostname() {
|
|
1511
|
+
return this.#init.hostname || "*";
|
|
1512
|
+
}
|
|
1513
|
+
get port() {
|
|
1514
|
+
return this.#init.port || "*";
|
|
1515
|
+
}
|
|
1516
|
+
get username() {
|
|
1517
|
+
return this.#init.username || "*";
|
|
1518
|
+
}
|
|
1519
|
+
get password() {
|
|
1520
|
+
return this.#init.password || "*";
|
|
1521
|
+
}
|
|
1522
|
+
get hash() {
|
|
1523
|
+
return this.#init.hash || "*";
|
|
1524
|
+
}
|
|
1525
|
+
constructor(input, baseURLOrOptions, options) {
|
|
1526
|
+
const {
|
|
1527
|
+
init,
|
|
1528
|
+
baseURL,
|
|
1529
|
+
options: opts
|
|
1530
|
+
} = parseConstructorArgs(input, baseURLOrOptions, options);
|
|
1531
|
+
if (typeof input === "string" && !init.protocol && !baseURL && !init.baseURL) {
|
|
1532
|
+
throw new TypeError(
|
|
1533
|
+
"Invalid pattern: relative URL pattern requires a baseURL"
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
this.#init = init;
|
|
1537
|
+
this.#compiled = compileURLPatternInit(init, baseURL, {
|
|
1538
|
+
ignoreCase: opts.ignoreCase ?? false
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
test(input, baseURL) {
|
|
1542
|
+
if (input === void 0) {
|
|
1543
|
+
input = {};
|
|
1544
|
+
}
|
|
1545
|
+
if (typeof input === "object" && !(input instanceof URL)) {
|
|
1546
|
+
return this.#testInit(input, baseURL);
|
|
1547
|
+
}
|
|
1548
|
+
let url;
|
|
1549
|
+
if (typeof input === "string") {
|
|
1550
|
+
try {
|
|
1551
|
+
url = baseURL ? new URL(input, baseURL) : new URL(input);
|
|
1552
|
+
} catch {
|
|
1553
|
+
return false;
|
|
1554
|
+
}
|
|
1555
|
+
} else {
|
|
1556
|
+
url = input;
|
|
1557
|
+
}
|
|
1558
|
+
if (!testURLComponents(this.#compiled, url)) {
|
|
1559
|
+
return false;
|
|
1560
|
+
}
|
|
1561
|
+
return testSearchRegex(this.#compiled, url);
|
|
1562
|
+
}
|
|
1563
|
+
#testInit(input, baseURL) {
|
|
1564
|
+
const base = input.baseURL || baseURL;
|
|
1565
|
+
let baseURLObj;
|
|
1566
|
+
if (base) {
|
|
1567
|
+
try {
|
|
1568
|
+
baseURLObj = new URL(base);
|
|
1569
|
+
} catch {
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
const protocol = input.protocol ?? baseURLObj?.protocol.replace(":", "") ?? void 0;
|
|
1573
|
+
if (protocol !== void 0 && !isValidProtocol(protocol))
|
|
1574
|
+
return false;
|
|
1575
|
+
if (this.#compiled.protocol) {
|
|
1576
|
+
if (protocol === void 0 || !this.#compiled.protocol.regex.test(protocol))
|
|
1577
|
+
return false;
|
|
1578
|
+
}
|
|
1579
|
+
if (this.#compiled.hostname) {
|
|
1580
|
+
let hostname = input.hostname ?? baseURLObj?.hostname;
|
|
1581
|
+
if (hostname === void 0)
|
|
1582
|
+
return false;
|
|
1583
|
+
hostname = toASCII(hostname);
|
|
1584
|
+
if (!this.#compiled.hostname.regex.test(hostname))
|
|
1585
|
+
return false;
|
|
1586
|
+
}
|
|
1587
|
+
let port = input.port ?? baseURLObj?.port;
|
|
1588
|
+
if (port !== void 0) {
|
|
1589
|
+
const canonical = canonicalizePort(port);
|
|
1590
|
+
if (canonical === void 0)
|
|
1591
|
+
return false;
|
|
1592
|
+
port = canonical;
|
|
1593
|
+
if (protocol) {
|
|
1594
|
+
const defaultPort = getDefaultPort(protocol);
|
|
1595
|
+
if (defaultPort && port === defaultPort)
|
|
1596
|
+
port = "";
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
if (this.#compiled.port) {
|
|
1600
|
+
if (port === void 0 || !this.#compiled.port.regex.test(port))
|
|
1601
|
+
return false;
|
|
1602
|
+
}
|
|
1603
|
+
if (this.#compiled.username) {
|
|
1604
|
+
const username = input.username ?? baseURLObj?.username;
|
|
1605
|
+
if (username === void 0 || !this.#compiled.username.regex.test(encodeURIComponent(username)))
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
if (this.#compiled.password) {
|
|
1609
|
+
const password = input.password ?? baseURLObj?.password;
|
|
1610
|
+
if (password === void 0 || !this.#compiled.password.regex.test(encodeURIComponent(password)))
|
|
1611
|
+
return false;
|
|
1612
|
+
}
|
|
1613
|
+
let pathname = input.pathname ?? baseURLObj?.pathname ?? "/";
|
|
1614
|
+
if (baseURLObj && !pathname.startsWith("/")) {
|
|
1615
|
+
const basePath = baseURLObj.pathname;
|
|
1616
|
+
if (basePath.endsWith("/")) {
|
|
1617
|
+
pathname = basePath + pathname;
|
|
1618
|
+
} else {
|
|
1619
|
+
pathname = basePath.slice(0, basePath.lastIndexOf("/") + 1) + pathname;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
if (pathname.startsWith("/"))
|
|
1623
|
+
pathname = normalizePathname(pathname);
|
|
1624
|
+
const shouldEncode = !protocol || isSpecialScheme(protocol);
|
|
1625
|
+
if (shouldEncode)
|
|
1626
|
+
pathname = encodePathname(pathname);
|
|
1627
|
+
if (!this.#compiled.pathname.regex.test(pathname))
|
|
1628
|
+
return false;
|
|
1629
|
+
if (this.#compiled.hash) {
|
|
1630
|
+
let hash = input.hash ?? baseURLObj?.hash.replace("#", "") ?? void 0;
|
|
1631
|
+
if (hash === void 0)
|
|
1632
|
+
return false;
|
|
1633
|
+
if (hash.startsWith("#"))
|
|
1634
|
+
hash = hash.slice(1);
|
|
1635
|
+
hash = encodeHash(hash);
|
|
1636
|
+
if (!this.#compiled.hash.regex.test(hash))
|
|
1637
|
+
return false;
|
|
1638
|
+
}
|
|
1639
|
+
if (this.#compiled.search) {
|
|
1640
|
+
let search = input.search ?? baseURLObj?.search?.replace("?", "") ?? "";
|
|
1641
|
+
if (search.startsWith("?"))
|
|
1642
|
+
search = search.slice(1);
|
|
1643
|
+
search = encodeSearch(search);
|
|
1644
|
+
if (!this.#compiled.search.regex.test(search))
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
return true;
|
|
1648
|
+
}
|
|
1649
|
+
exec(input, baseURL) {
|
|
1650
|
+
if (!this.test(input, baseURL))
|
|
1651
|
+
return null;
|
|
1652
|
+
if (typeof input === "object" && !(input instanceof URL)) {
|
|
1653
|
+
return this.#execInit(input, baseURL);
|
|
1654
|
+
}
|
|
1655
|
+
let url;
|
|
1656
|
+
if (typeof input === "string") {
|
|
1657
|
+
url = baseURL ? new URL(input, baseURL) : new URL(input);
|
|
1658
|
+
} else {
|
|
1659
|
+
url = input;
|
|
1660
|
+
}
|
|
1661
|
+
const params = {};
|
|
1662
|
+
const pathnameGroups = {};
|
|
1663
|
+
const searchGroups = {};
|
|
1664
|
+
const match = this.#compiled.pathname.regex.exec(url.pathname);
|
|
1665
|
+
if (match) {
|
|
1666
|
+
for (let i = 0; i < this.#compiled.pathname.paramNames.length; i++) {
|
|
1667
|
+
const name = this.#compiled.pathname.paramNames[i];
|
|
1668
|
+
const value = match[i + 1];
|
|
1669
|
+
if (value !== void 0) {
|
|
1670
|
+
params[name] = value;
|
|
1671
|
+
pathnameGroups[name] = value;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
if (this.#compiled.search) {
|
|
1676
|
+
const search = url.search.replace("?", "");
|
|
1677
|
+
const searchMatch = this.#compiled.search.regex.exec(search);
|
|
1678
|
+
if (searchMatch) {
|
|
1679
|
+
for (let i = 0; i < this.#compiled.search.paramNames.length; i++) {
|
|
1680
|
+
const name = this.#compiled.search.paramNames[i];
|
|
1681
|
+
const value = searchMatch[i + 1];
|
|
1682
|
+
if (value !== void 0) {
|
|
1683
|
+
params[name] = value;
|
|
1684
|
+
searchGroups[name] = value;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
return {
|
|
1690
|
+
params,
|
|
1691
|
+
pathname: { input: url.pathname, groups: pathnameGroups },
|
|
1692
|
+
search: { input: url.search, groups: searchGroups },
|
|
1693
|
+
protocol: { input: url.protocol, groups: {} },
|
|
1694
|
+
hostname: { input: url.hostname, groups: {} },
|
|
1695
|
+
port: { input: url.port, groups: {} },
|
|
1696
|
+
username: { input: url.username, groups: {} },
|
|
1697
|
+
password: { input: url.password, groups: {} },
|
|
1698
|
+
hash: { input: url.hash, groups: {} },
|
|
1699
|
+
inputs: [input]
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
#execInit(input, baseURL) {
|
|
1703
|
+
const base = input.baseURL || baseURL;
|
|
1704
|
+
let baseURLObj;
|
|
1705
|
+
if (base) {
|
|
1706
|
+
try {
|
|
1707
|
+
baseURLObj = new URL(base);
|
|
1708
|
+
} catch {
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
let pathname = input.pathname ?? baseURLObj?.pathname ?? "/";
|
|
1712
|
+
pathname = normalizePathname(pathname);
|
|
1713
|
+
const params = {};
|
|
1714
|
+
const pathnameGroups = {};
|
|
1715
|
+
const searchGroups = {};
|
|
1716
|
+
const match = this.#compiled.pathname.regex.exec(pathname);
|
|
1717
|
+
if (match) {
|
|
1718
|
+
for (let i = 0; i < this.#compiled.pathname.paramNames.length; i++) {
|
|
1719
|
+
const name = this.#compiled.pathname.paramNames[i];
|
|
1720
|
+
const value = match[i + 1];
|
|
1721
|
+
if (value !== void 0) {
|
|
1722
|
+
params[name] = value;
|
|
1723
|
+
pathnameGroups[name] = value;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
if (this.#compiled.search) {
|
|
1728
|
+
let search2 = input.search ?? baseURLObj?.search?.replace("?", "") ?? "";
|
|
1729
|
+
if (search2.startsWith("?"))
|
|
1730
|
+
search2 = search2.slice(1);
|
|
1731
|
+
const searchMatch = this.#compiled.search.regex.exec(search2);
|
|
1732
|
+
if (searchMatch) {
|
|
1733
|
+
for (let i = 0; i < this.#compiled.search.paramNames.length; i++) {
|
|
1734
|
+
const name = this.#compiled.search.paramNames[i];
|
|
1735
|
+
const value = searchMatch[i + 1];
|
|
1736
|
+
if (value !== void 0) {
|
|
1737
|
+
params[name] = value;
|
|
1738
|
+
searchGroups[name] = value;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
const protocol = input.protocol ?? baseURLObj?.protocol.replace(":", "") ?? "";
|
|
1744
|
+
const hostname = input.hostname ?? baseURLObj?.hostname ?? "";
|
|
1745
|
+
let port = input.port ?? baseURLObj?.port ?? "";
|
|
1746
|
+
if (port)
|
|
1747
|
+
port = canonicalizePort(port) || "";
|
|
1748
|
+
const hash = input.hash ?? baseURLObj?.hash?.replace("#", "") ?? "";
|
|
1749
|
+
const search = input.search ?? baseURLObj?.search?.replace("?", "") ?? "";
|
|
1750
|
+
return {
|
|
1751
|
+
params,
|
|
1752
|
+
pathname: { input: pathname, groups: pathnameGroups },
|
|
1753
|
+
search: { input: search ? `?${search}` : "", groups: searchGroups },
|
|
1754
|
+
protocol: { input: protocol ? `${protocol}:` : "", groups: {} },
|
|
1755
|
+
hostname: { input: hostname, groups: {} },
|
|
1756
|
+
port: { input: port, groups: {} },
|
|
1757
|
+
username: { input: "", groups: {} },
|
|
1758
|
+
password: { input: "", groups: {} },
|
|
1759
|
+
hash: { input: hash ? `#${hash}` : "", groups: {} },
|
|
1760
|
+
inputs: [input]
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1764
|
+
var MatchPattern = class {
|
|
1765
|
+
#compiled;
|
|
1766
|
+
#searchPattern;
|
|
1767
|
+
#init;
|
|
1768
|
+
get pathname() {
|
|
1769
|
+
return this.#init.pathname || "*";
|
|
1770
|
+
}
|
|
1771
|
+
get search() {
|
|
1772
|
+
return this.#searchPattern;
|
|
1773
|
+
}
|
|
1774
|
+
get protocol() {
|
|
1775
|
+
return this.#init.protocol;
|
|
1776
|
+
}
|
|
1777
|
+
get hostname() {
|
|
1778
|
+
return this.#init.hostname;
|
|
1779
|
+
}
|
|
1780
|
+
get port() {
|
|
1781
|
+
return this.#init.port;
|
|
1782
|
+
}
|
|
1783
|
+
get username() {
|
|
1784
|
+
return this.#init.username;
|
|
1785
|
+
}
|
|
1786
|
+
get password() {
|
|
1787
|
+
return this.#init.password;
|
|
1788
|
+
}
|
|
1789
|
+
get hash() {
|
|
1790
|
+
return this.#init.hash;
|
|
1791
|
+
}
|
|
1792
|
+
constructor(input, baseURLOrOptions, options) {
|
|
1793
|
+
const {
|
|
1794
|
+
init,
|
|
1795
|
+
baseURL,
|
|
1796
|
+
options: opts
|
|
1797
|
+
} = parseConstructorArgs(input, baseURLOrOptions, options);
|
|
1798
|
+
this.#init = init;
|
|
1799
|
+
this.#searchPattern = init.search;
|
|
1800
|
+
this.#compiled = compileURLPatternInit(init, baseURL, {
|
|
1801
|
+
ignoreCase: opts.ignoreCase ?? false
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
test(input, baseURL) {
|
|
1805
|
+
if (input === void 0) {
|
|
1806
|
+
input = {};
|
|
1807
|
+
}
|
|
1808
|
+
if (typeof input === "object" && !(input instanceof URL)) {
|
|
1809
|
+
return this.#testInit(input, baseURL);
|
|
1810
|
+
}
|
|
1811
|
+
let url;
|
|
1812
|
+
if (typeof input === "string") {
|
|
1813
|
+
try {
|
|
1814
|
+
url = baseURL ? new URL(input, baseURL) : new URL(input);
|
|
1815
|
+
} catch {
|
|
1816
|
+
return false;
|
|
1817
|
+
}
|
|
1818
|
+
} else {
|
|
1819
|
+
url = input;
|
|
1820
|
+
}
|
|
1821
|
+
if (!testURLComponents(this.#compiled, url)) {
|
|
1822
|
+
return false;
|
|
1823
|
+
}
|
|
1824
|
+
if (this.#searchPattern && !this.#searchPattern.includes("=")) {
|
|
1825
|
+
return testSearchRegex(this.#compiled, url);
|
|
1826
|
+
}
|
|
1827
|
+
const rawSearch = url.search.startsWith("?") ? url.search.slice(1) : url.search;
|
|
1828
|
+
return testSearchParams(this.#searchPattern, rawSearch);
|
|
1829
|
+
}
|
|
1830
|
+
#testInit(input, baseURL) {
|
|
1831
|
+
const base = input.baseURL || baseURL;
|
|
1832
|
+
let baseURLObj;
|
|
1833
|
+
if (base) {
|
|
1834
|
+
try {
|
|
1835
|
+
baseURLObj = new URL(base);
|
|
1836
|
+
} catch {
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
const protocol = input.protocol ?? baseURLObj?.protocol.replace(":", "") ?? void 0;
|
|
1840
|
+
if (protocol !== void 0 && !isValidProtocol(protocol))
|
|
1841
|
+
return false;
|
|
1842
|
+
if (this.#compiled.protocol) {
|
|
1843
|
+
if (protocol === void 0 || !this.#compiled.protocol.regex.test(protocol))
|
|
1844
|
+
return false;
|
|
1845
|
+
}
|
|
1846
|
+
if (this.#compiled.hostname) {
|
|
1847
|
+
let hostname = input.hostname ?? baseURLObj?.hostname;
|
|
1848
|
+
if (hostname === void 0)
|
|
1849
|
+
return false;
|
|
1850
|
+
hostname = toASCII(hostname);
|
|
1851
|
+
if (!this.#compiled.hostname.regex.test(hostname))
|
|
1852
|
+
return false;
|
|
1853
|
+
}
|
|
1854
|
+
let port = input.port ?? baseURLObj?.port;
|
|
1855
|
+
if (port !== void 0) {
|
|
1856
|
+
const canonical = canonicalizePort(port);
|
|
1857
|
+
if (canonical === void 0)
|
|
1858
|
+
return false;
|
|
1859
|
+
port = canonical;
|
|
1860
|
+
if (protocol) {
|
|
1861
|
+
const defaultPort = getDefaultPort(protocol);
|
|
1862
|
+
if (defaultPort && port === defaultPort)
|
|
1863
|
+
port = "";
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
if (this.#compiled.port) {
|
|
1867
|
+
if (port === void 0 || !this.#compiled.port.regex.test(port))
|
|
1868
|
+
return false;
|
|
1869
|
+
}
|
|
1870
|
+
if (this.#compiled.username) {
|
|
1871
|
+
const username = input.username ?? baseURLObj?.username;
|
|
1872
|
+
if (username === void 0 || !this.#compiled.username.regex.test(encodeURIComponent(username)))
|
|
1873
|
+
return false;
|
|
1874
|
+
}
|
|
1875
|
+
if (this.#compiled.password) {
|
|
1876
|
+
const password = input.password ?? baseURLObj?.password;
|
|
1877
|
+
if (password === void 0 || !this.#compiled.password.regex.test(encodeURIComponent(password)))
|
|
1878
|
+
return false;
|
|
1879
|
+
}
|
|
1880
|
+
let pathname = input.pathname ?? baseURLObj?.pathname ?? "/";
|
|
1881
|
+
if (baseURLObj && !pathname.startsWith("/")) {
|
|
1882
|
+
try {
|
|
1883
|
+
const resolved = new URL(pathname, baseURLObj.href);
|
|
1884
|
+
pathname = resolved.pathname;
|
|
1885
|
+
} catch {
|
|
1886
|
+
const basePath = baseURLObj.pathname;
|
|
1887
|
+
if (basePath.endsWith("/")) {
|
|
1888
|
+
pathname = basePath + pathname;
|
|
1889
|
+
} else {
|
|
1890
|
+
pathname = basePath.slice(0, basePath.lastIndexOf("/") + 1) + pathname;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
if (pathname.startsWith("/"))
|
|
1895
|
+
pathname = normalizePathname(pathname);
|
|
1896
|
+
const shouldEncode = !protocol || isSpecialScheme(protocol);
|
|
1897
|
+
if (shouldEncode)
|
|
1898
|
+
pathname = encodePathname(pathname);
|
|
1899
|
+
if (!this.#compiled.pathname.regex.test(pathname))
|
|
1900
|
+
return false;
|
|
1901
|
+
if (this.#compiled.hash) {
|
|
1902
|
+
let hash = input.hash ?? baseURLObj?.hash.replace("#", "") ?? void 0;
|
|
1903
|
+
if (hash === void 0)
|
|
1904
|
+
return false;
|
|
1905
|
+
if (hash.startsWith("#"))
|
|
1906
|
+
hash = hash.slice(1);
|
|
1907
|
+
hash = encodeHash(hash);
|
|
1908
|
+
if (!this.#compiled.hash.regex.test(hash))
|
|
1909
|
+
return false;
|
|
1910
|
+
}
|
|
1911
|
+
if (this.#searchPattern) {
|
|
1912
|
+
let search = input.search ?? baseURLObj?.search?.replace("?", "") ?? "";
|
|
1913
|
+
if (search.startsWith("?"))
|
|
1914
|
+
search = search.slice(1);
|
|
1915
|
+
search = encodeSearch(search);
|
|
1916
|
+
if (!this.#searchPattern.includes("=")) {
|
|
1917
|
+
if (this.#compiled.search) {
|
|
1918
|
+
return this.#compiled.search.regex.test(search);
|
|
1919
|
+
}
|
|
1920
|
+
return true;
|
|
1921
|
+
}
|
|
1922
|
+
return testSearchParams(this.#searchPattern, search);
|
|
1923
|
+
}
|
|
1924
|
+
return true;
|
|
1925
|
+
}
|
|
1926
|
+
exec(input, baseURL) {
|
|
1927
|
+
if (!this.test(input, baseURL))
|
|
1928
|
+
return null;
|
|
1929
|
+
if (typeof input === "object" && !(input instanceof URL)) {
|
|
1930
|
+
return this.#execInit(input, baseURL);
|
|
1931
|
+
}
|
|
1932
|
+
let url;
|
|
1933
|
+
if (typeof input === "string") {
|
|
1934
|
+
url = baseURL ? new URL(input, baseURL) : new URL(input);
|
|
1935
|
+
} else {
|
|
1936
|
+
url = input;
|
|
1937
|
+
}
|
|
1938
|
+
const params = {};
|
|
1939
|
+
const pathnameGroups = {};
|
|
1940
|
+
const searchGroups = {};
|
|
1941
|
+
const match = this.#compiled.pathname.regex.exec(url.pathname);
|
|
1942
|
+
if (match) {
|
|
1943
|
+
for (let i = 0; i < this.#compiled.pathname.paramNames.length; i++) {
|
|
1944
|
+
const name = this.#compiled.pathname.paramNames[i];
|
|
1945
|
+
const value = match[i + 1];
|
|
1946
|
+
if (value !== void 0) {
|
|
1947
|
+
params[name] = value;
|
|
1948
|
+
pathnameGroups[name] = value;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
if (this.#searchPattern) {
|
|
1953
|
+
const extracted = extractSearchParams(
|
|
1954
|
+
this.#searchPattern,
|
|
1955
|
+
url.searchParams
|
|
1956
|
+
);
|
|
1957
|
+
Object.assign(params, extracted);
|
|
1958
|
+
Object.assign(searchGroups, extracted);
|
|
1959
|
+
} else {
|
|
1960
|
+
for (const [key, value] of url.searchParams) {
|
|
1961
|
+
params[key] = value;
|
|
1962
|
+
searchGroups[key] = value;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
return {
|
|
1966
|
+
params,
|
|
1967
|
+
pathname: { input: url.pathname, groups: pathnameGroups },
|
|
1968
|
+
search: { input: url.search, groups: searchGroups },
|
|
1969
|
+
protocol: { input: url.protocol, groups: {} },
|
|
1970
|
+
hostname: { input: url.hostname, groups: {} },
|
|
1971
|
+
port: { input: url.port, groups: {} },
|
|
1972
|
+
username: { input: url.username, groups: {} },
|
|
1973
|
+
password: { input: url.password, groups: {} },
|
|
1974
|
+
hash: { input: url.hash, groups: {} },
|
|
1975
|
+
inputs: [input]
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
#execInit(input, baseURL) {
|
|
1979
|
+
const base = input.baseURL || baseURL;
|
|
1980
|
+
let baseURLObj;
|
|
1981
|
+
if (base) {
|
|
1982
|
+
try {
|
|
1983
|
+
baseURLObj = new URL(base);
|
|
1984
|
+
} catch {
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
let pathname = input.pathname ?? baseURLObj?.pathname ?? "/";
|
|
1988
|
+
pathname = normalizePathname(pathname);
|
|
1989
|
+
const params = {};
|
|
1990
|
+
const pathnameGroups = {};
|
|
1991
|
+
const searchGroups = {};
|
|
1992
|
+
const match = this.#compiled.pathname.regex.exec(pathname);
|
|
1993
|
+
if (match) {
|
|
1994
|
+
for (let i = 0; i < this.#compiled.pathname.paramNames.length; i++) {
|
|
1995
|
+
const name = this.#compiled.pathname.paramNames[i];
|
|
1996
|
+
const value = match[i + 1];
|
|
1997
|
+
if (value !== void 0) {
|
|
1998
|
+
params[name] = value;
|
|
1999
|
+
pathnameGroups[name] = value;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
if (this.#searchPattern) {
|
|
2004
|
+
const search2 = input.search ?? baseURLObj?.search?.replace("?", "") ?? "";
|
|
2005
|
+
const extracted = extractSearchParams(
|
|
2006
|
+
this.#searchPattern,
|
|
2007
|
+
new URLSearchParams(search2)
|
|
2008
|
+
);
|
|
2009
|
+
Object.assign(params, extracted);
|
|
2010
|
+
Object.assign(searchGroups, extracted);
|
|
2011
|
+
}
|
|
2012
|
+
const protocol = input.protocol ?? baseURLObj?.protocol.replace(":", "") ?? "";
|
|
2013
|
+
const hostname = input.hostname ?? baseURLObj?.hostname ?? "";
|
|
2014
|
+
let port = input.port ?? baseURLObj?.port ?? "";
|
|
2015
|
+
if (port)
|
|
2016
|
+
port = canonicalizePort(port) || "";
|
|
2017
|
+
const hash = input.hash ?? baseURLObj?.hash?.replace("#", "") ?? "";
|
|
2018
|
+
const search = input.search ?? baseURLObj?.search?.replace("?", "") ?? "";
|
|
2019
|
+
return {
|
|
2020
|
+
params,
|
|
2021
|
+
pathname: { input: pathname, groups: pathnameGroups },
|
|
2022
|
+
search: { input: search ? `?${search}` : "", groups: searchGroups },
|
|
2023
|
+
protocol: { input: protocol ? `${protocol}:` : "", groups: {} },
|
|
2024
|
+
hostname: { input: hostname, groups: {} },
|
|
2025
|
+
port: { input: port, groups: {} },
|
|
2026
|
+
username: { input: "", groups: {} },
|
|
2027
|
+
password: { input: "", groups: {} },
|
|
2028
|
+
hash: { input: hash ? `#${hash}` : "", groups: {} },
|
|
2029
|
+
inputs: [input]
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
};
|
|
2033
|
+
export {
|
|
2034
|
+
MatchPattern,
|
|
2035
|
+
URLPattern,
|
|
2036
|
+
compilePathname,
|
|
2037
|
+
isSimplePattern,
|
|
2038
|
+
parseSimplePattern
|
|
2039
|
+
};
|