Skip to content

Commit b39eab5

Browse files
committed
Switch to a complete UTS #46 implementation
1 parent ba3e36f commit b39eab5

File tree

7 files changed

+10
-59
lines changed

7 files changed

+10
-59
lines changed

.npmignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
eslint.config.js
33
**/*.test.js
44
tsconfig.json
5-
documents/
65
scratch/
76
TODO*

documents/idna2008-limitations.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
"devDependencies": {
2828
"@stylistic/eslint-plugin": "*",
2929
"@types/node": "*",
30-
"@types/tr46": "*",
3130
"eslint-import-resolver-typescript": "*",
3231
"eslint-plugin-import": "*",
3332
"json-schema-test-suite": "github:json-schema-org/json-schema-test-suite",
@@ -37,6 +36,6 @@
3736
},
3837
"dependencies": {
3938
"@hyperjump/uri": "^1.3.2",
40-
"tr46": "^6.0.0"
39+
"idn-hostname": "^15.1.2"
4140
}
4241
}

src/index.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
/**
22
* @module
3-
*
4-
* @document ../documents/idna2008-limitations.md
53
*/
64

75
// JSON Schema Validation - Dates, Times, and Duration
@@ -13,7 +11,7 @@ export { isIdnEmail } from "./rfc6531.js";
1311

1412
// JSON Schema Validation - Hostnames
1513
export { isHostname } from "./rfc1123.js";
16-
export { isAsciiIdn, isIdn } from "./idna2008.js";
14+
export { isAsciiIdn, isIdn } from "./uts46.js";
1715

1816
// JSON Schema Validation - IP Addresses
1917
export { isIPv4 } from "./rfc2673.js";

src/json-schema-test-suite.test.js

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,7 @@ await testSuite("draft2020-12/optional/format/email", (email) => typeof email !=
6363
await testSuite("draft6/optional/format/hostname", (hostname) => typeof hostname !== "string" || isHostname(hostname));
6464
await testSuite("draft2020-12/optional/format/hostname", (hostname) => typeof hostname !== "string" || isAsciiIdn(hostname));
6565
await testSuite("draft2020-12/optional/format/idn-email", (email) => typeof email !== "string" || isIdnEmail(email));
66-
await testSuite("draft2020-12/optional/format/idn-hostname", (hostname) => typeof hostname !== "string" || isIdn(hostname), {
67-
"validation of internationalized host names": new Set([
68-
"contains illegal char U+302E Hangul single dot tone mark",
69-
"Exceptions that are DISALLOWED, right-to-left chars",
70-
"Exceptions that are DISALLOWED, left-to-right chars",
71-
"MIDDLE DOT with no preceding 'l'",
72-
"MIDDLE DOT with nothing preceding",
73-
"MIDDLE DOT with no following 'l'",
74-
"MIDDLE DOT with nothing following",
75-
"Greek KERAIA not followed by Greek",
76-
"Greek KERAIA not followed by anything",
77-
"Hebrew GERESH not preceded by anything",
78-
"Hebrew GERSHAYIM not preceded by anything",
79-
"KATAKANA MIDDLE DOT with no Hiragana, Katakana, or Han",
80-
"KATAKANA MIDDLE DOT with no other characters"
81-
])
82-
});
66+
await testSuite("draft2020-12/optional/format/idn-hostname", (hostname) => typeof hostname !== "string" || isIdn(hostname));
8367
await testSuite("draft2020-12/optional/format/ipv4", (ip) => typeof ip !== "string" || isIPv4(ip));
8468
await testSuite("draft2020-12/optional/format/ipv6", (ip) => typeof ip !== "string" || isIPv6(ip));
8569
await testSuite("draft2020-12/optional/format/iri-reference", (iri) => typeof iri !== "string" || isIriReference(iri));

src/rfc6531.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isIdn } from "./idna2008.js";
1+
import { isIdn } from "./uts46.js";
22

33
const ucschar = `[\\u{A0}-\\u{D7FF}\\u{F900}-\\u{FDCF}\\u{FDF0}-\\u{FFEF}\\u{10000}-\\u{1FFFD}\\u{20000}-\\u{2FFFD}\\u{30000}-\\u{3FFFD}\\u{40000}-\\u{4FFFD}\\u{50000}-\\u{5FFFD}\\u{60000}-\\u{6FFFD}\\u{70000}-\\u{7FFFD}\\u{80000}-\\u{8FFFD}\\u{90000}-\\u{9FFFD}\\u{A0000}-\\u{AFFFD}\\u{B0000}-\\u{BFFFD}\\u{C0000}-\\u{CFFFD}\\u{D0000}-\\u{DFFFD}\\u{E1000}-\\u{EFFFD}]`;
44

@@ -47,10 +47,6 @@ const mailboxPattern = new RegExp(`^${mailbox}$`, "u");
4747
* The 'idn-email' format. Validates that a string represents an email as
4848
* defined by the "Mailbox" ABNF rule in [RFC 6531, section 3.3](https://www.rfc-editor.org/rfc/rfc6531.html#section-3.3).
4949
*
50-
* **NOTE**: Validation of the domain has some minor limitations. The spec
51-
* requires the domain be a valid IDNA2008 IDN. See {@link !"IDNA2008
52-
* Limitations" | IDNA2008 Limitations} for details.
53-
*
5450
* @see [JSON Schema Core, section 7.3.2](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.2)
5551
*
5652
* @param {string} email
Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
1-
import tr46 from "tr46";
2-
3-
const label = `[A-Za-z0-9-]{1,63}`;
4-
const domain = `${label}(?:\\.${label})*`;
5-
6-
const domainPattern = new RegExp(`^${domain}$`);
7-
8-
const parserOptions = {
9-
checkBidi: true,
10-
checkJoiners: true,
11-
checkHyphens: true
12-
};
1+
import { isIdnHostname } from "idn-hostname";
2+
import { isHostname } from "./rfc1123.js";
133

144
/**
155
* The 'hostname' format since draft-07. Validates that a string represents an
@@ -19,38 +9,28 @@ const parserOptions = {
199
* **NOTE**: The 'hostname' format changed in draft-07. Use {@link isHostname}
2010
* for draft-06 and earlier.
2111
*
22-
* **WARNING**: This function can't completely validate IDNA2008 hostnames. See
23-
* {@link !"IDNA2008 Limitations" | IDNA2008 Limitations} for details.
24-
*
2512
* @see [JSON Schema Core, section 7.3.3](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.3)
2613
*
2714
* @param {string} hostname
2815
* @returns {boolean}
2916
*/
3017
export const isAsciiIdn = (hostname) => {
31-
return domainPattern.test(hostname)
32-
&& hostname.length < 256
33-
&& !tr46.toUnicode(hostname, parserOptions).error;
18+
return isHostname(hostname) && isIdn(hostname);
3419
};
3520

3621
/**
3722
* The 'idn-hostname' format. Validates that a string represents an IDNA2008
3823
* internationalized domain name as defined by [RFC 5890, section 2.3.2.1](https://www.rfc-editor.org/rfc/rfc5890.html#section-2.3.2.1).
3924
*
40-
* **WARNING**: This function can't completely validate IDNA2008 hostnames. See
41-
* {@link !"IDNA2008 Limitations" | IDNA2008 Limitations} for details.
42-
*
4325
* @see [JSON Schema Core, section 7.3.3](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.3)
4426
*
4527
* @param {string} hostname
4628
* @returns {boolean}
4729
*/
4830
export const isIdn = (hostname) => {
49-
const asciiHostname = tr46.toASCII(hostname);
50-
51-
if (!asciiHostname) {
31+
try {
32+
return isIdnHostname(hostname);
33+
} catch (_error) {
5234
return false;
5335
}
54-
55-
return isAsciiIdn(asciiHostname);
5636
};

0 commit comments

Comments
 (0)