mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
node:url implement domainToASCII and domainToUnicode (#9257)
* node:url implement domainToASCII and domainToUnicode * fix arg checks * remove unneeded use of WTF::Vector * tidy * throw a js error if icu fails * add a ton more tests, fix ascii guard, upconvert latin1 * even more tests * add a comment for this guard * use ASSERT_NOT_REACHED() instead of raise(SIGABRT)
This commit is contained in:
@@ -30,17 +30,20 @@ Click the link in the right column to jump to the associated documentation.
|
||||
---
|
||||
|
||||
- File I/O
|
||||
- [`Bun.file`](/docs/api/file-io#reading-files-bun-file) [`Bun.write`](/docs/api/file-io#writing-files-bun-write)
|
||||
- [`Bun.file`](/docs/api/file-io#reading-files-bun-file)
|
||||
[`Bun.write`](/docs/api/file-io#writing-files-bun-write)
|
||||
|
||||
---
|
||||
|
||||
- Child processes
|
||||
- [`Bun.spawn`](/docs/api/spawn#spawn-a-process-bun-spawn) [`Bun.spawnSync`](/docs/api/spawn#blocking-api-bun-spawnsync)
|
||||
- [`Bun.spawn`](/docs/api/spawn#spawn-a-process-bun-spawn)
|
||||
[`Bun.spawnSync`](/docs/api/spawn#blocking-api-bun-spawnsync)
|
||||
|
||||
---
|
||||
|
||||
- TCP
|
||||
- [`Bun.listen`](/docs/api/tcp#start-a-server-bun-listen) [`Bun.connect`](/docs/api/tcp#start-a-server-bun-listen)
|
||||
- [`Bun.listen`](/docs/api/tcp#start-a-server-bun-listen)
|
||||
[`Bun.connect`](/docs/api/tcp#start-a-server-bun-listen)
|
||||
|
||||
---
|
||||
|
||||
@@ -60,7 +63,8 @@ Click the link in the right column to jump to the associated documentation.
|
||||
---
|
||||
|
||||
- Hashing
|
||||
- [`Bun.hash`](/docs/api/hashing#bun-hash) [`Bun.CryptoHasher`](/docs/api/hashing#bun-cryptohasher)
|
||||
- [`Bun.hash`](/docs/api/hashing#bun-hash)
|
||||
[`Bun.CryptoHasher`](/docs/api/hashing#bun-cryptohasher)
|
||||
|
||||
---
|
||||
|
||||
@@ -100,6 +104,26 @@ Click the link in the right column to jump to the associated documentation.
|
||||
---
|
||||
|
||||
- Utilities
|
||||
- [`Bun.version`](/docs/api/utils#bun-version) [`Bun.revision`](/docs/api/utils#bun-revision) [`Bun.env`](/docs/api/utils#bun-env) [`Bun.main`](/docs/api/utils#bun-main) [`Bun.sleep()`](/docs/api/utils#bun-sleep) [`Bun.sleepSync()`](/docs/api/utils#bun-sleepsync) [`Bun.which()`](/docs/api/utils#bun-which) [`Bun.peek()`](/docs/api/utils#bun-peek) [`Bun.openInEditor()`](/docs/api/utils#bun-openineditor) [`Bun.deepEquals()`](/docs/api/utils#bun-deepequals) [`Bun.escapeHTML()`](/docs/api/utils#bun-escapehtml) [`Bun.fileURLToPath()`](/docs/api/utils#bun-fileurltopath) [`Bun.pathToFileURL()`](/docs/api/utils#bun-pathtofileurl) [`Bun.gzipSync()`](/docs/api/utils#bun-gzipsync) [`Bun.gunzipSync()`](/docs/api/utils#bun-gunzipsync) [`Bun.deflateSync()`](/docs/api/utils#bun-deflatesync) [`Bun.inflateSync()`](/docs/api/utils#bun-inflatesync) [`Bun.inspect()`](/docs/api/utils#bun-inspect) [`Bun.nanoseconds()`](/docs/api/utils#bun-nanoseconds) [`Bun.readableStreamTo*()`](/docs/api/utils#bun-readablestreamto) [`Bun.resolveSync()`](/docs/api/utils#bun-resolvesync)
|
||||
- [`Bun.version`](/docs/api/utils#bun-version)
|
||||
[`Bun.revision`](/docs/api/utils#bun-revision)
|
||||
[`Bun.env`](/docs/api/utils#bun-env)
|
||||
[`Bun.main`](/docs/api/utils#bun-main)
|
||||
[`Bun.sleep()`](/docs/api/utils#bun-sleep)
|
||||
[`Bun.sleepSync()`](/docs/api/utils#bun-sleepsync)
|
||||
[`Bun.which()`](/docs/api/utils#bun-which)
|
||||
[`Bun.peek()`](/docs/api/utils#bun-peek)
|
||||
[`Bun.openInEditor()`](/docs/api/utils#bun-openineditor)
|
||||
[`Bun.deepEquals()`](/docs/api/utils#bun-deepequals)
|
||||
[`Bun.escapeHTML()`](/docs/api/utils#bun-escapehtml)
|
||||
[`Bun.fileURLToPath()`](/docs/api/utils#bun-fileurltopath)
|
||||
[`Bun.pathToFileURL()`](/docs/api/utils#bun-pathtofileurl)
|
||||
[`Bun.gzipSync()`](/docs/api/utils#bun-gzipsync)
|
||||
[`Bun.gunzipSync()`](/docs/api/utils#bun-gunzipsync)
|
||||
[`Bun.deflateSync()`](/docs/api/utils#bun-deflatesync)
|
||||
[`Bun.inflateSync()`](/docs/api/utils#bun-inflatesync)
|
||||
[`Bun.inspect()`](/docs/api/utils#bun-inspect)
|
||||
[`Bun.nanoseconds()`](/docs/api/utils#bun-nanoseconds)
|
||||
[`Bun.readableStreamTo*()`](/docs/api/utils#bun-readablestreamto)
|
||||
[`Bun.resolveSync()`](/docs/api/utils#bun-resolvesync)
|
||||
|
||||
{% /table %}
|
||||
|
||||
@@ -148,7 +148,7 @@ Some methods are not optimized yet.
|
||||
|
||||
### [`node:url`](https://nodejs.org/api/url.html)
|
||||
|
||||
🟡 Missing `domainToASCII` `domainToUnicode`. It's recommended to use `URL` and `URLSearchParams` globals instead.
|
||||
🟢 Fully implemented.
|
||||
|
||||
### [`node:util`](https://nodejs.org/api/util.html)
|
||||
|
||||
|
||||
@@ -66,4 +66,4 @@ dep lshpack liblshpack.a
|
||||
|
||||
if [ "$BUILT_ANY" -eq 0 ]; then
|
||||
printf "(run with -f to rebuild)\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#include "JavaScriptCore/WasmFaultSignalHandler.h"
|
||||
#include "wtf/Gigacage.h"
|
||||
#include "wtf/URL.h"
|
||||
#include "wtf/URLParser.h"
|
||||
#include "wtf/text/ExternalStringImpl.h"
|
||||
#include "wtf/text/StringCommon.h"
|
||||
#include "wtf/text/StringImpl.h"
|
||||
@@ -138,6 +139,8 @@
|
||||
|
||||
#include "ProcessBindingConstants.h"
|
||||
|
||||
#include <unicode/uidna.h>
|
||||
|
||||
#if ENABLE(REMOTE_INSPECTOR)
|
||||
#include "JavaScriptCore/RemoteInspectorServer.h"
|
||||
#endif
|
||||
@@ -1724,6 +1727,8 @@ JSC_DEFINE_CUSTOM_SETTER(noop_setter,
|
||||
|
||||
static NeverDestroyed<const String> pathToFileURLString(MAKE_STATIC_STRING_IMPL("pathToFileURL"));
|
||||
static NeverDestroyed<const String> fileURLToPathString(MAKE_STATIC_STRING_IMPL("fileURLToPath"));
|
||||
static NeverDestroyed<const String> domainToASCIIString(MAKE_STATIC_STRING_IMPL("domainToASCII"));
|
||||
static NeverDestroyed<const String> domainToUnicodeString(MAKE_STATIC_STRING_IMPL("domainToUnicode"));
|
||||
|
||||
enum ReadableStreamTag : int32_t {
|
||||
Invalid = -1,
|
||||
@@ -1779,6 +1784,142 @@ JSC_DEFINE_HOST_FUNCTION(jsReceiveMessageOnPort, (JSGlobalObject * lexicalGlobal
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(functionDomainToASCII, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwTypeError(globalObject, scope, "domainToASCII needs 1 argument"_s);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
|
||||
auto arg0 = callFrame->argument(0);
|
||||
if (arg0.isUndefined())
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
if (arg0.isNull())
|
||||
return JSC::JSValue::encode(jsNull());
|
||||
if (!arg0.isString()) {
|
||||
throwTypeError(globalObject, scope, "the \"domain\" argument must be a string"_s);
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
auto domain = arg0.toWTFString(globalObject);
|
||||
if (domain.isNull())
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
|
||||
// https://url.spec.whatwg.org/#forbidden-host-code-point
|
||||
if (
|
||||
domain.contains(0x0000) || // U+0000 NULL
|
||||
domain.contains(0x0009) || // U+0009 TAB
|
||||
domain.contains(0x000A) || // U+000A LF
|
||||
domain.contains(0x000D) || // U+000D CR
|
||||
domain.contains(0x0020) || // U+0020 SPACE
|
||||
domain.contains(0x0023) || // U+0023 (#)
|
||||
domain.contains(0x002F) || // U+002F (/)
|
||||
domain.contains(0x003A) || // U+003A (:)
|
||||
domain.contains(0x003C) || // U+003C (<)
|
||||
domain.contains(0x003E) || // U+003E (>)
|
||||
domain.contains(0x003F) || // U+003F (?)
|
||||
domain.contains(0x0040) || // U+0040 (@)
|
||||
domain.contains(0x005B) || // U+005B ([)
|
||||
domain.contains(0x005C) || // U+005C (\)
|
||||
domain.contains(0x005D) || // U+005D (])
|
||||
domain.contains(0x005E) || // U+005E (^)
|
||||
domain.contains(0x007C) // // U+007C (|).
|
||||
)
|
||||
return JSC::JSValue::encode(jsEmptyString(vm));
|
||||
|
||||
if (domain.containsOnlyASCII())
|
||||
return JSC::JSValue::encode(arg0);
|
||||
if (domain.is8Bit())
|
||||
domain.convertTo16Bit();
|
||||
|
||||
constexpr static int allowedNameToASCIIErrors = UIDNA_ERROR_EMPTY_LABEL | UIDNA_ERROR_LABEL_TOO_LONG | UIDNA_ERROR_DOMAIN_NAME_TOO_LONG | UIDNA_ERROR_LEADING_HYPHEN | UIDNA_ERROR_TRAILING_HYPHEN | UIDNA_ERROR_HYPHEN_3_4;
|
||||
constexpr static size_t hostnameBufferLength = 2048;
|
||||
|
||||
auto encoder = &WTF::URLParser::internationalDomainNameTranscoder();
|
||||
UChar hostnameBuffer[hostnameBufferLength];
|
||||
UErrorCode error = U_ZERO_ERROR;
|
||||
UIDNAInfo processingDetails = UIDNA_INFO_INITIALIZER;
|
||||
int32_t numCharactersConverted = uidna_nameToASCII(encoder, StringView(domain).characters16(), domain.length(), hostnameBuffer, hostnameBufferLength, &processingDetails, &error);
|
||||
|
||||
if (U_SUCCESS(error) && !(processingDetails.errors & ~allowedNameToASCIIErrors) && numCharactersConverted) {
|
||||
return JSC::JSValue::encode(JSC::jsString(vm, WTF::String(hostnameBuffer, numCharactersConverted)));
|
||||
}
|
||||
throwTypeError(globalObject, scope, "domainToASCII failed"_s);
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(functionDomainToUnicode, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwTypeError(globalObject, scope, "domainToUnicode needs 1 argument"_s);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
|
||||
auto arg0 = callFrame->argument(0);
|
||||
if (arg0.isUndefined())
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
if (arg0.isNull())
|
||||
return JSC::JSValue::encode(jsNull());
|
||||
if (!arg0.isString()) {
|
||||
throwTypeError(globalObject, scope, "the \"domain\" argument must be a string"_s);
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
auto domain = arg0.toWTFString(globalObject);
|
||||
if (domain.isNull())
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
|
||||
// https://url.spec.whatwg.org/#forbidden-host-code-point
|
||||
if (
|
||||
domain.contains(0x0000) || // U+0000 NULL
|
||||
domain.contains(0x0009) || // U+0009 TAB
|
||||
domain.contains(0x000A) || // U+000A LF
|
||||
domain.contains(0x000D) || // U+000D CR
|
||||
domain.contains(0x0020) || // U+0020 SPACE
|
||||
domain.contains(0x0023) || // U+0023 (#)
|
||||
domain.contains(0x002F) || // U+002F (/)
|
||||
domain.contains(0x003A) || // U+003A (:)
|
||||
domain.contains(0x003C) || // U+003C (<)
|
||||
domain.contains(0x003E) || // U+003E (>)
|
||||
domain.contains(0x003F) || // U+003F (?)
|
||||
domain.contains(0x0040) || // U+0040 (@)
|
||||
domain.contains(0x005B) || // U+005B ([)
|
||||
domain.contains(0x005C) || // U+005C (\)
|
||||
domain.contains(0x005D) || // U+005D (])
|
||||
domain.contains(0x005E) || // U+005E (^)
|
||||
domain.contains(0x007C) // // U+007C (|).
|
||||
)
|
||||
return JSC::JSValue::encode(jsEmptyString(vm));
|
||||
|
||||
if (!domain.is8Bit())
|
||||
// this function is only for undoing punycode so its okay if utf-16 text makes it out unchanged.
|
||||
return JSC::JSValue::encode(arg0);
|
||||
|
||||
domain.convertTo16Bit();
|
||||
|
||||
constexpr static int allowedNameToUnicodeErrors = UIDNA_ERROR_EMPTY_LABEL | UIDNA_ERROR_LABEL_TOO_LONG | UIDNA_ERROR_DOMAIN_NAME_TOO_LONG | UIDNA_ERROR_LEADING_HYPHEN | UIDNA_ERROR_TRAILING_HYPHEN | UIDNA_ERROR_HYPHEN_3_4;
|
||||
constexpr static int hostnameBufferLength = 2048;
|
||||
|
||||
auto encoder = &WTF::URLParser::internationalDomainNameTranscoder();
|
||||
UChar hostnameBuffer[hostnameBufferLength];
|
||||
UErrorCode error = U_ZERO_ERROR;
|
||||
UIDNAInfo processingDetails = UIDNA_INFO_INITIALIZER;
|
||||
|
||||
int32_t numCharactersConverted = uidna_nameToUnicode(encoder, StringView(domain).characters16(), domain.length(), hostnameBuffer, hostnameBufferLength, &processingDetails, &error);
|
||||
|
||||
if (U_SUCCESS(error) && !(processingDetails.errors & ~allowedNameToUnicodeErrors) && numCharactersConverted) {
|
||||
return JSC::JSValue::encode(JSC::jsString(vm, WTF::String(hostnameBuffer, numCharactersConverted)));
|
||||
}
|
||||
throwTypeError(globalObject, scope, "domainToUnicode failed"_s);
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
extern "C" EncodedJSValue BunInternalFunction__syntaxHighlighter(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame);
|
||||
|
||||
// we're trying out a new way to do this lazy loading
|
||||
@@ -1891,6 +2032,16 @@ JSC_DEFINE_HOST_FUNCTION(functionLazyLoad,
|
||||
return JSValue::encode(JSC::JSFunction::create(vm, globalObject, 1, "getStringWidth"_s, BunString__getStringWidth, ImplementationVisibility::Public));
|
||||
}
|
||||
|
||||
if (string == "domainToASCII"_s) {
|
||||
return JSValue::encode(
|
||||
JSFunction::create(vm, globalObject, 1, domainToASCIIString, functionDomainToASCII, ImplementationVisibility::Public, NoIntrinsic));
|
||||
}
|
||||
|
||||
if (string == "domainToUnicode"_s) {
|
||||
return JSValue::encode(
|
||||
JSFunction::create(vm, globalObject, 1, domainToUnicodeString, functionDomainToUnicode, ImplementationVisibility::Public, NoIntrinsic));
|
||||
}
|
||||
|
||||
if (string == "pathToFileURL"_s) {
|
||||
return JSValue::encode(
|
||||
JSFunction::create(vm, globalObject, 1, pathToFileURLString, functionPathToFileURL, ImplementationVisibility::Public, NoIntrinsic));
|
||||
@@ -1916,6 +2067,7 @@ JSC_DEFINE_HOST_FUNCTION(functionLazyLoad,
|
||||
JSC::JSFunction::create(vm, globalObject, 0, "emitReadable"_s, jsReadable_emitReadable_, ImplementationVisibility::Public), 0);
|
||||
return JSValue::encode(obj);
|
||||
}
|
||||
|
||||
if (string == "events"_s) {
|
||||
return JSValue::encode(WebCore::JSEventEmitter::getConstructor(vm, globalObject));
|
||||
}
|
||||
@@ -1970,6 +2122,7 @@ JSC_DEFINE_HOST_FUNCTION(functionLazyLoad,
|
||||
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getUnpackedSettings"_s)), JSC::JSFunction::create(vm, globalObject, 1, "getUnpackedSettings"_s, BUN__HTTP2__getUnpackedSettings, ImplementationVisibility::Public, NoIntrinsic), 0);
|
||||
return JSValue::encode(obj);
|
||||
}
|
||||
|
||||
if (string == "internal/tls"_s) {
|
||||
auto* obj = constructEmptyObject(globalObject);
|
||||
|
||||
@@ -2053,9 +2206,13 @@ JSC_DEFINE_HOST_FUNCTION(functionLazyLoad,
|
||||
return JSC::JSValue::encode(obj);
|
||||
}
|
||||
|
||||
#if BUN_DEBUG
|
||||
// this should always be unreachable unless in development and adding a new lazy value
|
||||
// and it has not been added above yet.
|
||||
ASSERT_NOT_REACHED();
|
||||
#else
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3713,6 +3870,7 @@ JSC_DEFINE_HOST_FUNCTION(functionGetDirectStreamDetails, (JSC::JSGlobalObject *
|
||||
|
||||
return JSC::JSValue::encode(resultObject);
|
||||
}
|
||||
|
||||
JSC::GCClient::IsoSubspace* GlobalObject::subspaceForImpl(JSC::VM& vm)
|
||||
{
|
||||
return WebCore::subspaceForImpl<GlobalObject, WebCore::UseCustomHeapCellType::Yes>(
|
||||
|
||||
@@ -824,6 +824,8 @@ function urlToHttpOptions(url) {
|
||||
|
||||
const pathToFileURL = $lazy("pathToFileURL");
|
||||
const fileURLToPath = $lazy("fileURLToPath");
|
||||
const domainToASCII = $lazy("domainToASCII");
|
||||
const domainToUnicode = $lazy("domainToUnicode");
|
||||
|
||||
export default {
|
||||
parse: urlParse,
|
||||
@@ -836,4 +838,6 @@ export default {
|
||||
pathToFileURL,
|
||||
fileURLToPath,
|
||||
urlToHttpOptions,
|
||||
domainToASCII,
|
||||
domainToUnicode,
|
||||
};
|
||||
|
||||
@@ -741,10 +741,14 @@ export { Url as Url };
|
||||
|
||||
export var pathToFileURL;
|
||||
export var fileURLToPath;
|
||||
export var domainToASCII;
|
||||
export var domainToUnicode;
|
||||
|
||||
if (lazy) {
|
||||
pathToFileURL = lazy("pathToFileURL");
|
||||
fileURLToPath = lazy("fileURLToPath");
|
||||
domainToASCII = lazy("domainToASCII");
|
||||
domainToUnicode = lazy("domainToUnicode");
|
||||
}
|
||||
|
||||
export default {
|
||||
@@ -755,6 +759,8 @@ export default {
|
||||
Url: Url,
|
||||
pathToFileURL: pathToFileURL,
|
||||
fileURLToPath: fileURLToPath,
|
||||
domainToASCII,
|
||||
domainToUnicode,
|
||||
URL,
|
||||
URLSearchParams,
|
||||
};
|
||||
|
||||
@@ -4452,6 +4452,8 @@
|
||||
},
|
||||
"URLSearchParams": "function",
|
||||
"Url": "function",
|
||||
"domainToASCII": "function",
|
||||
"domainToUnicode": "function",
|
||||
"fileURLToPath": "function",
|
||||
"format": "function",
|
||||
"parse": "function",
|
||||
@@ -4460,6 +4462,8 @@
|
||||
"resolveObject": "function",
|
||||
"urlToHttpOptions": "function"
|
||||
},
|
||||
"domainToASCII": "function",
|
||||
"domainToUnicode": "function",
|
||||
"fileURLToPath": "function",
|
||||
"format": "function",
|
||||
"parse": "function",
|
||||
|
||||
@@ -4396,6 +4396,8 @@
|
||||
},
|
||||
"URLSearchParams": "function",
|
||||
"Url": "function",
|
||||
"domainToASCII": "function",
|
||||
"domainToUnicode": "function",
|
||||
"fileURLToPath": "function",
|
||||
"format": "function",
|
||||
"parse": "function",
|
||||
@@ -4404,6 +4406,8 @@
|
||||
"resolveObject": "function",
|
||||
"urlToHttpOptions": "function"
|
||||
},
|
||||
"domainToASCII": "function",
|
||||
"domainToUnicode": "function",
|
||||
"fileURLToPath": "function",
|
||||
"format": "function",
|
||||
"parse": "function",
|
||||
@@ -7434,6 +7438,8 @@
|
||||
"fileURLToPath": "function",
|
||||
"nanoseconds": "function",
|
||||
"password": "object",
|
||||
"domainToASCII": "function",
|
||||
"domainToUnicode": "function",
|
||||
"pathToFileURL": "function",
|
||||
"peek": "function",
|
||||
"plugin": "function",
|
||||
@@ -8016,6 +8022,8 @@
|
||||
},
|
||||
"URLSearchParams": "function",
|
||||
"Url": "function",
|
||||
"domainToASCII": "function",
|
||||
"domainToUnicode": "function",
|
||||
"fileURLToPath": "function",
|
||||
"format": "function",
|
||||
"parse": "function",
|
||||
@@ -8027,6 +8035,8 @@
|
||||
"fileURLToPath": "function",
|
||||
"format": "function",
|
||||
"parse": "function",
|
||||
"domainToASCII": "function",
|
||||
"domainToUnicode": "function",
|
||||
"pathToFileURL": "function",
|
||||
"resolve": "function",
|
||||
"resolveObject": "function",
|
||||
|
||||
@@ -1,31 +1,101 @@
|
||||
import { describe, test } from "bun:test";
|
||||
import assert from "node:assert";
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import url from "node:url";
|
||||
|
||||
const domainToASCII = url.domainToASCII;
|
||||
const domainToUnicode = url.domainToUnicode;
|
||||
const pairs = [
|
||||
["ıíd", "xn--d-iga7r"],
|
||||
["يٴ", "xn--mhb8f"],
|
||||
["www.ϧƽəʐ.com", "www.xn--cja62apfr6c.com"],
|
||||
["новини.com", "xn--b1amarcd.com"],
|
||||
["名がドメイン.com", "xn--v8jxj3d1dzdz08w.com"],
|
||||
["افغانستا.icom.museum", "xn--mgbaal8b0b9b2b.icom.museum"],
|
||||
["الجزائر.icom.fake", "xn--lgbbat1ad8j.icom.fake"],
|
||||
["भारत.org", "xn--h2brj9c.org"],
|
||||
["افغانستا.icom.museum", "xn--mgbaal8b0b9b2b.icom.museum"],
|
||||
["الجزائر.icom.museum", "xn--lgbbat1ad8j.icom.museum"],
|
||||
["österreich.icom.museum", "xn--sterreich-z7a.icom.museum"],
|
||||
["বাংলাদেশ.icom.museum", "xn--54b6eqazv8bc7e.icom.museum"],
|
||||
["беларусь.icom.museum", "xn--80abmy0agn7e.icom.museum"],
|
||||
["belgië.icom.museum", "xn--belgi-rsa.icom.museum"],
|
||||
["българия.icom.museum", "xn--80abgvm6a7d2b.icom.museum"],
|
||||
["تشادر.icom.museum", "xn--mgbfqim.icom.museum"],
|
||||
["中国.icom.museum", "xn--fiqs8s.icom.museum"],
|
||||
["القمر.icom.museum", "xn--mgbu4chg.icom.museum"],
|
||||
["κυπρος.icom.museum", "xn--vxakcego.icom.museum"],
|
||||
["českárepublika.icom.museum", "xn--eskrepublika-ebb62d.icom.museum"],
|
||||
["مصر.icom.museum", "xn--wgbh1c.icom.museum"],
|
||||
["ελλάδα.icom.museum", "xn--hxakic4aa.icom.museum"],
|
||||
["magyarország.icom.museum", "xn--magyarorszg-t7a.icom.museum"],
|
||||
["ísland.icom.museum", "xn--sland-ysa.icom.museum"],
|
||||
["भारत.icom.museum", "xn--h2brj9c.icom.museum"],
|
||||
["ايران.icom.museum", "xn--mgba3a4fra.icom.museum"],
|
||||
["éire.icom.museum", "xn--ire-9la.icom.museum"],
|
||||
["איקו״ם.ישראל.museum", "xn--4dbklr2c8d.xn--4dbrk0ce.museum"],
|
||||
["日本.icom.museum", "xn--wgv71a.icom.museum"],
|
||||
["الأردن.icom.museum", "xn--igbhzh7gpa.icom.museum"],
|
||||
["қазақстан.icom.museum", "xn--80aaa0a6awh12ed.icom.museum"],
|
||||
["한국.icom.museum", "xn--3e0b707e.icom.museum"],
|
||||
["кыргызстан.icom.museum", "xn--80afmksoji0fc.icom.museum"],
|
||||
["ລາວ.icom.museum", "xn--q7ce6a.icom.museum"],
|
||||
["لبنان.icom.museum", "xn--mgbb7fjb.icom.museum"],
|
||||
["македонија.icom.museum", "xn--80aaldqjmmi6x.icom.museum"],
|
||||
["موريتانيا.icom.museum", "xn--mgbah1a3hjkrd.icom.museum"],
|
||||
["méxico.icom.museum", "xn--mxico-bsa.icom.museum"],
|
||||
["монголулс.icom.museum", "xn--c1aqabffc0aq.icom.museum"],
|
||||
["المغرب.icom.museum", "xn--mgbc0a9azcg.icom.museum"],
|
||||
["नेपाल.icom.museum", "xn--l2bey1c2b.icom.museum"],
|
||||
["عمان.icom.museum", "xn--mgb9awbf.icom.museum"],
|
||||
["قطر.icom.museum", "xn--wgbl6a.icom.museum"],
|
||||
["românia.icom.museum", "xn--romnia-yta.icom.museum"],
|
||||
["россия.иком.museum", "xn--h1alffa9f.xn--h1aegh.museum"],
|
||||
["србијаицрнагора.иком.museum", "xn--80aaabm1ab4blmeec9e7n.xn--h1aegh.museum"],
|
||||
["இலங்கை.icom.museum", "xn--xkc2al3hye2a.icom.museum"],
|
||||
["españa.icom.museum", "xn--espaa-rta.icom.museum"],
|
||||
["ไทย.icom.museum", "xn--o3cw4h.icom.museum"],
|
||||
["تونس.icom.museum", "xn--pgbs0dh.icom.museum"],
|
||||
["türkiye.icom.museum", "xn--trkiye-3ya.icom.museum"],
|
||||
["украина.icom.museum", "xn--80aaxgrpt.icom.museum"],
|
||||
["việtnam.icom.museum", "xn--vitnam-jk8b.icom.museum"],
|
||||
[`${"a".repeat(64)}.com`, `${"a".repeat(64)}.com`],
|
||||
[`${`${"a".repeat(64)}.`.repeat(4)}com`, `${`${"a".repeat(64)}.`.repeat(4)}com`],
|
||||
["r4---sn-a5mlrn7s.gevideo.com", "r4---sn-a5mlrn7s.gevideo.com"],
|
||||
["-sn-a5mlrn7s.gevideo.com", "-sn-a5mlrn7s.gevideo.com"],
|
||||
["sn-a5mlrn7s-.gevideo.com", "sn-a5mlrn7s-.gevideo.com"],
|
||||
["-sn-a5mlrn7s-.gevideo.com", "-sn-a5mlrn7s-.gevideo.com"],
|
||||
["-sn--a5mlrn7s-.gevideo.com", "-sn--a5mlrn7s-.gevideo.com"],
|
||||
];
|
||||
|
||||
// TODO: Support url.domainToASCII and url.domainToUnicode.
|
||||
describe.todo("url.domainToASCII and url.domainToUnicode", () => {
|
||||
test("convert from unicode to ascii and back", () => {
|
||||
const domainWithASCII = [
|
||||
["ıíd", "xn--d-iga7r"],
|
||||
["يٴ", "xn--mhb8f"],
|
||||
["www.ϧƽəʐ.com", "www.xn--cja62apfr6c.com"],
|
||||
["новини.com", "xn--b1amarcd.com"],
|
||||
["名がドメイン.com", "xn--v8jxj3d1dzdz08w.com"],
|
||||
["افغانستا.icom.museum", "xn--mgbaal8b0b9b2b.icom.museum"],
|
||||
["الجزائر.icom.fake", "xn--lgbbat1ad8j.icom.fake"],
|
||||
["भारत.org", "xn--h2brj9c.org"],
|
||||
];
|
||||
const invalids = [
|
||||
["@", ""],
|
||||
["a@b", ""],
|
||||
[null, null],
|
||||
[undefined, undefined],
|
||||
["2001:0db8:85a3:0000:0000:8a2e:0370:7334", ""],
|
||||
];
|
||||
|
||||
domainWithASCII.forEach(pair => {
|
||||
const domain = pair[0];
|
||||
const ascii = pair[1];
|
||||
const domainConvertedToASCII = domainToASCII(domain);
|
||||
assert.strictEqual(domainConvertedToASCII, ascii);
|
||||
const asciiConvertedToUnicode = domainToUnicode(ascii);
|
||||
assert.strictEqual(asciiConvertedToUnicode, domain);
|
||||
describe("url.domainToASCII", () => {
|
||||
for (const [domain, ascii] of pairs) {
|
||||
test(`convert from '${domain}' to '${ascii}'`, () => {
|
||||
const domainConvertedToASCII = url.domainToASCII(domain);
|
||||
expect(domainConvertedToASCII).toEqual(ascii);
|
||||
});
|
||||
});
|
||||
}
|
||||
for (const [input, expected] of invalids) {
|
||||
test(`-> '${input}' is '${expected}'`, () => {
|
||||
expect(url.domainToASCII(input)).toEqual(expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("url.domainToUnicode", () => {
|
||||
for (const [domain, ascii] of pairs) {
|
||||
test(`convert from '${ascii}' to '${domain}'`, () => {
|
||||
const asciiConvertedToUnicode = url.domainToUnicode(ascii);
|
||||
expect(asciiConvertedToUnicode).toEqual(domain);
|
||||
});
|
||||
}
|
||||
for (const [input, expected] of invalids) {
|
||||
test(`-> '${input}' is '${expected}'`, () => {
|
||||
expect(url.domainToASCII(input)).toEqual(expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user