whatwg-url 16.0.0 → 17.0.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/README.md +1 -1
- package/lib/URL-impl.js +2 -1
- package/lib/URLSearchParams.js +4 -1
- package/lib/url-state-machine.js +61 -20
- package/lib/utils.js +100 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ whatwg-url is a full implementation of the WHATWG [URL Standard](https://url.spe
|
|
|
4
4
|
|
|
5
5
|
## Specification conformance
|
|
6
6
|
|
|
7
|
-
whatwg-url is currently up to date with the URL spec up to commit [
|
|
7
|
+
whatwg-url is currently up to date with the URL spec up to commit [3c83874](https://github.com/whatwg/url/commit/3c83874ae1eac84ceb71d8c4344673251c982bbe) and the web platform tests up to commit [2a91c2e](https://github.com/web-platform-tests/wpt/commit/2a91c2e71952bdceef8b2825ce3a0a0dc73aa588).
|
|
8
8
|
|
|
9
9
|
For `file:` URLs, whose [origin is left unspecified](https://url.spec.whatwg.org/#concept-url-origin), whatwg-url chooses to use a new opaque origin (which serializes to `"null"`).
|
|
10
10
|
|
package/lib/URL-impl.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const usm = require("./url-state-machine");
|
|
3
3
|
const urlencoded = require("./urlencoded");
|
|
4
4
|
const URLSearchParams = require("./URLSearchParams");
|
|
5
|
+
const URL = require("./URL");
|
|
5
6
|
|
|
6
7
|
exports.implementation = class URLImpl {
|
|
7
8
|
// Unlike the spec, we duplicate some code between the constructor and canParse, because we want to give useful error
|
|
@@ -32,7 +33,7 @@ exports.implementation = class URLImpl {
|
|
|
32
33
|
|
|
33
34
|
static parse(globalObject, input, base) {
|
|
34
35
|
try {
|
|
35
|
-
return
|
|
36
|
+
return URL.createImpl(globalObject, [input, base]);
|
|
36
37
|
} catch {
|
|
37
38
|
return null;
|
|
38
39
|
}
|
package/lib/URLSearchParams.js
CHANGED
|
@@ -106,7 +106,10 @@ exports.install = (globalObject, globalNames) => {
|
|
|
106
106
|
let curArg = arguments[0];
|
|
107
107
|
if (curArg !== undefined) {
|
|
108
108
|
if (utils.isObject(curArg)) {
|
|
109
|
-
if (
|
|
109
|
+
if (
|
|
110
|
+
utils.getMethod(curArg, Symbol.iterator, "Failed to construct 'URLSearchParams': parameter 1") !==
|
|
111
|
+
undefined
|
|
112
|
+
) {
|
|
110
113
|
if (!utils.isObject(curArg)) {
|
|
111
114
|
throw new globalObject.TypeError(
|
|
112
115
|
"Failed to construct 'URLSearchParams': parameter 1" + " sequence" + " is not an iterable object."
|
package/lib/url-state-machine.js
CHANGED
|
@@ -63,6 +63,10 @@ function containsForbiddenDomainCodePoint(string) {
|
|
|
63
63
|
return containsForbiddenHostCodePoint(string) || string.search(/[\u0000-\u001F]|%|\u007F/u) !== -1;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
function isASCIIString(string) {
|
|
67
|
+
return !/[^\u0000-\u007F]/u.test(string);
|
|
68
|
+
}
|
|
69
|
+
|
|
66
70
|
function isSpecialScheme(scheme) {
|
|
67
71
|
return specialSchemes[scheme] !== undefined;
|
|
68
72
|
}
|
|
@@ -241,7 +245,7 @@ function parseIPv6(input) {
|
|
|
241
245
|
}
|
|
242
246
|
|
|
243
247
|
while (infra.isASCIIDigit(input[pointer])) {
|
|
244
|
-
const number = parseInt(at(input, pointer));
|
|
248
|
+
const number = parseInt(at(input, pointer), 10);
|
|
245
249
|
if (ipv4Piece === null) {
|
|
246
250
|
ipv4Piece = number;
|
|
247
251
|
} else if (ipv4Piece === 0) {
|
|
@@ -342,7 +346,7 @@ function parseHost(input, isOpaque = false) {
|
|
|
342
346
|
}
|
|
343
347
|
|
|
344
348
|
const domain = utf8DecodeWithoutBOM(percentDecodeString(input));
|
|
345
|
-
const asciiDomain =
|
|
349
|
+
const asciiDomain = domainParser(domain);
|
|
346
350
|
if (asciiDomain === failure) {
|
|
347
351
|
return failure;
|
|
348
352
|
}
|
|
@@ -426,28 +430,65 @@ function serializeHost(host) {
|
|
|
426
430
|
return host;
|
|
427
431
|
}
|
|
428
432
|
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (!beStrict) {
|
|
444
|
-
if (result === "") {
|
|
433
|
+
function domainParser(domain, beStrict = false) {
|
|
434
|
+
if (beStrict) {
|
|
435
|
+
const result = tr46.toASCII(domain, {
|
|
436
|
+
checkHyphens: true,
|
|
437
|
+
checkBidi: true,
|
|
438
|
+
checkJoiners: true,
|
|
439
|
+
useSTD3ASCIIRules: true,
|
|
440
|
+
transitionalProcessing: false,
|
|
441
|
+
verifyDNSLength: true,
|
|
442
|
+
ignoreInvalidPunycode: false
|
|
443
|
+
});
|
|
444
|
+
if (result === null) {
|
|
445
445
|
return failure;
|
|
446
446
|
}
|
|
447
|
-
|
|
447
|
+
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
let result;
|
|
452
|
+
if (isASCIIString(domain)) {
|
|
453
|
+
// The `domain parser` algorithm runs Unicode ToASCII here only to generate a `domain-to-ASCII` validation error,
|
|
454
|
+
// and we do not surface validation errors yet. If we ever support them
|
|
455
|
+
// (https://github.com/jsdom/whatwg-url/issues/156), we will need code like the following:
|
|
456
|
+
//
|
|
457
|
+
// if (tr46.toASCII(domain, {
|
|
458
|
+
// checkHyphens: false,
|
|
459
|
+
// checkBidi: true,
|
|
460
|
+
// checkJoiners: true,
|
|
461
|
+
// useSTD3ASCIIRules: false,
|
|
462
|
+
// transitionalProcessing: false,
|
|
463
|
+
// verifyDNSLength: false,
|
|
464
|
+
// ignoreInvalidPunycode: false
|
|
465
|
+
// }) === null) {
|
|
466
|
+
// validationError("domain-to-ASCII");
|
|
467
|
+
// }
|
|
468
|
+
|
|
469
|
+
result = domain.toLowerCase();
|
|
470
|
+
} else {
|
|
471
|
+
result = tr46.toASCII(domain, {
|
|
472
|
+
checkHyphens: false,
|
|
473
|
+
checkBidi: true,
|
|
474
|
+
checkJoiners: true,
|
|
475
|
+
useSTD3ASCIIRules: false,
|
|
476
|
+
transitionalProcessing: false,
|
|
477
|
+
verifyDNSLength: false,
|
|
478
|
+
ignoreInvalidPunycode: false
|
|
479
|
+
});
|
|
480
|
+
if (result === null) {
|
|
448
481
|
return failure;
|
|
449
482
|
}
|
|
450
483
|
}
|
|
484
|
+
|
|
485
|
+
if (result === "") {
|
|
486
|
+
return failure;
|
|
487
|
+
}
|
|
488
|
+
if (containsForbiddenDomainCodePoint(result)) {
|
|
489
|
+
return failure;
|
|
490
|
+
}
|
|
491
|
+
|
|
451
492
|
return result;
|
|
452
493
|
}
|
|
453
494
|
|
|
@@ -857,7 +898,7 @@ URLStateMachine.prototype["parse port"] = function parsePort(c, cStr) {
|
|
|
857
898
|
(isSpecial(this.url) && c === p("\\")) ||
|
|
858
899
|
this.stateOverride) {
|
|
859
900
|
if (this.buffer !== "") {
|
|
860
|
-
const port = parseInt(this.buffer);
|
|
901
|
+
const port = parseInt(this.buffer, 10);
|
|
861
902
|
if (port > 2 ** 16 - 1) {
|
|
862
903
|
this.parseError = true;
|
|
863
904
|
return failure;
|
package/lib/utils.js
CHANGED
|
@@ -111,7 +111,7 @@ function isArrayIndexPropName(P) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
const arrayBufferByteLengthGetter =
|
|
114
|
-
|
|
114
|
+
Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get;
|
|
115
115
|
function isArrayBuffer(value) {
|
|
116
116
|
try {
|
|
117
117
|
arrayBufferByteLengthGetter.call(value);
|
|
@@ -198,6 +198,103 @@ function isAccessorDescriptor(desc) {
|
|
|
198
198
|
return Object.hasOwn(desc, "get") || Object.hasOwn(desc, "set");
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
function getMethod(value, property, errPrefix = "The provided value") {
|
|
202
|
+
const func = value[property];
|
|
203
|
+
if (func === undefined || func === null) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
if (typeof func !== "function") {
|
|
207
|
+
throw new TypeError(`${errPrefix}'s ${property} property is not a function.`);
|
|
208
|
+
}
|
|
209
|
+
return func;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function createAsyncFromSyncIterator(syncIterator) {
|
|
213
|
+
// Instead of re-implementing CreateAsyncFromSyncIterator and %AsyncFromSyncIteratorPrototype%,
|
|
214
|
+
// we use yield* inside an async generator function to achieve the same result.
|
|
215
|
+
|
|
216
|
+
// Wrap the sync iterator inside a sync iterable, so we can use it with yield*.
|
|
217
|
+
const syncIterable = {
|
|
218
|
+
[Symbol.iterator]: () => syncIterator
|
|
219
|
+
};
|
|
220
|
+
// Create an async generator function and immediately invoke it.
|
|
221
|
+
const asyncIterator = (async function* () {
|
|
222
|
+
return yield* syncIterable;
|
|
223
|
+
})();
|
|
224
|
+
// Return as an async iterator record.
|
|
225
|
+
return asyncIterator;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function convertAsyncSequence(object, itemConverter, errPrefix = "The provided value") {
|
|
229
|
+
if (!isObject(object)) {
|
|
230
|
+
throw new TypeError(`${errPrefix} is not an object.`);
|
|
231
|
+
}
|
|
232
|
+
let method = getMethod(object, Symbol.asyncIterator, errPrefix);
|
|
233
|
+
let type = "async";
|
|
234
|
+
if (method === undefined) {
|
|
235
|
+
method = getMethod(object, Symbol.iterator, errPrefix);
|
|
236
|
+
if (method === undefined) {
|
|
237
|
+
throw new TypeError(`${errPrefix} is not an async iterable object.`);
|
|
238
|
+
}
|
|
239
|
+
type = "sync";
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
object,
|
|
244
|
+
method,
|
|
245
|
+
type,
|
|
246
|
+
// The wrapperSymbol ensures that if the async sequence is used as a return value,
|
|
247
|
+
// that it exposes the original JavaScript value.
|
|
248
|
+
// https://webidl.spec.whatwg.org/#js-async-iterable
|
|
249
|
+
[wrapperSymbol]: object,
|
|
250
|
+
// Implement the async iterator protocol, so users can iterate
|
|
251
|
+
// the async sequence directly (e.g. with for await...of)
|
|
252
|
+
// instead of needing to call a separate helper function to open the async sequence.
|
|
253
|
+
// https://webidl.spec.whatwg.org/#async-sequence-open
|
|
254
|
+
[Symbol.asyncIterator]() {
|
|
255
|
+
return openAsyncSequence(object, method, type, itemConverter, `${errPrefix}'s iterator`);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function openAsyncSequence(object, method, type, itemConverter, errPrefix = "The provided value") {
|
|
261
|
+
let iterator = call(method, object);
|
|
262
|
+
if (!isObject(iterator)) {
|
|
263
|
+
throw new TypeError(`${errPrefix}'s method must return an object`);
|
|
264
|
+
}
|
|
265
|
+
if (type === "sync") {
|
|
266
|
+
iterator = createAsyncFromSyncIterator(iterator);
|
|
267
|
+
}
|
|
268
|
+
const nextMethod = iterator.next;
|
|
269
|
+
return {
|
|
270
|
+
async next() {
|
|
271
|
+
const nextResult = await call(nextMethod, iterator);
|
|
272
|
+
if (!isObject(nextResult)) {
|
|
273
|
+
throw new TypeError(`${errPrefix}'s next method must return an object`);
|
|
274
|
+
}
|
|
275
|
+
const { done, value } = nextResult;
|
|
276
|
+
if (done) {
|
|
277
|
+
return { done: true, value: undefined };
|
|
278
|
+
}
|
|
279
|
+
return { done: false, value: itemConverter(value) };
|
|
280
|
+
},
|
|
281
|
+
async return(reason) {
|
|
282
|
+
const returnMethod = getMethod(iterator, "return", errPrefix);
|
|
283
|
+
if (returnMethod === undefined) {
|
|
284
|
+
return { done: true, value: undefined };
|
|
285
|
+
}
|
|
286
|
+
const returnResult = await call(returnMethod, iterator, reason);
|
|
287
|
+
if (!isObject(returnResult)) {
|
|
288
|
+
throw new TypeError(`${errPrefix}'s return method must return an object`);
|
|
289
|
+
}
|
|
290
|
+
return { done: true, value: undefined };
|
|
291
|
+
},
|
|
292
|
+
[Symbol.asyncIterator]() {
|
|
293
|
+
return this;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
201
298
|
const supportsPropertyIndex = Symbol("supports property index");
|
|
202
299
|
const supportedPropertyIndices = Symbol("supported property indices");
|
|
203
300
|
const supportsPropertyName = Symbol("supports property name");
|
|
@@ -232,6 +329,8 @@ module.exports = exports = {
|
|
|
232
329
|
isArrayBuffer,
|
|
233
330
|
isSharedArrayBuffer,
|
|
234
331
|
isArrayIndexPropName,
|
|
332
|
+
getMethod,
|
|
333
|
+
convertAsyncSequence,
|
|
235
334
|
supportsPropertyIndex,
|
|
236
335
|
supportedPropertyIndices,
|
|
237
336
|
supportsPropertyName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whatwg-url",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0",
|
|
4
4
|
"description": "An implementation of the WHATWG URL Standard's URL API and parsing machinery",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -15,21 +15,21 @@
|
|
|
15
15
|
"url": "git+https://github.com/jsdom/whatwg-url.git"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@exodus/bytes": "^1.
|
|
18
|
+
"@exodus/bytes": "^1.15.1",
|
|
19
19
|
"tr46": "^6.0.0",
|
|
20
20
|
"webidl-conversions": "^8.0.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@domenic/eslint-config": "^
|
|
24
|
-
"tinybench": "^6.0.
|
|
25
|
-
"c8": "^
|
|
26
|
-
"esbuild": "^0.
|
|
27
|
-
"eslint": "^
|
|
28
|
-
"globals": "^17.
|
|
29
|
-
"webidl2js": "^
|
|
23
|
+
"@domenic/eslint-config": "^5.1.0",
|
|
24
|
+
"tinybench": "^6.0.2",
|
|
25
|
+
"c8": "^11.0.0",
|
|
26
|
+
"esbuild": "^0.28.1",
|
|
27
|
+
"eslint": "^10.6.0",
|
|
28
|
+
"globals": "^17.7.0",
|
|
29
|
+
"webidl2js": "^20.1.0"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
|
-
"node": "^
|
|
32
|
+
"node": "^22.14.0 || >=24.0.0"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"coverage": "c8 node --test --experimental-test-coverage test/*.js",
|