summaryrefslogtreecommitdiff
path: root/node_modules/mdurl
diff options
context:
space:
mode:
authorShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
committerShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
commit7a52ddeba2a68388b544f529d2d92104420f77b0 (patch)
tree15ddd47457a2cb4a96060747437d36474e4f6b4e /node_modules/mdurl
parent53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff)
Changed from static to 11ty!
Diffstat (limited to 'node_modules/mdurl')
-rw-r--r--node_modules/mdurl/LICENSE45
-rw-r--r--node_modules/mdurl/README.md102
-rw-r--r--node_modules/mdurl/build/index.cjs.js534
-rw-r--r--node_modules/mdurl/index.mjs11
-rw-r--r--node_modules/mdurl/lib/decode.mjs112
-rw-r--r--node_modules/mdurl/lib/encode.mjs89
-rw-r--r--node_modules/mdurl/lib/format.mjs21
-rw-r--r--node_modules/mdurl/lib/parse.mjs308
-rw-r--r--node_modules/mdurl/package.json37
9 files changed, 1259 insertions, 0 deletions
diff --git a/node_modules/mdurl/LICENSE b/node_modules/mdurl/LICENSE
new file mode 100644
index 0000000..3b2c7bf
--- /dev/null
+++ b/node_modules/mdurl/LICENSE
@@ -0,0 +1,45 @@
+Copyright (c) 2015 Vitaly Puzrin, Alex Kocharin.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+--------------------------------------------------------------------------------
+
+.parse() is based on Joyent's node.js `url` code:
+
+Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/node_modules/mdurl/README.md b/node_modules/mdurl/README.md
new file mode 100644
index 0000000..c7f9e95
--- /dev/null
+++ b/node_modules/mdurl/README.md
@@ -0,0 +1,102 @@
+# mdurl
+
+[![CI](https://github.com/markdown-it/mdurl/actions/workflows/ci.yml/badge.svg)](https://github.com/markdown-it/mdurl/actions/workflows/ci.yml)
+[![NPM version](https://img.shields.io/npm/v/mdurl.svg?style=flat)](https://www.npmjs.org/package/mdurl)
+
+> URL utilities for [markdown-it](https://github.com/markdown-it/markdown-it) parser.
+
+
+## API
+
+### .encode(str [, exclude, keepEncoded]) -> String
+
+Percent-encode a string, avoiding double encoding. Don't touch `/a-zA-Z0-9/` +
+excluded chars + `/%[a-fA-F0-9]{2}/` (if not disabled). Broken surrorates are
+replaced with `U+FFFD`.
+
+Params:
+
+- __str__ - input string.
+- __exclude__ - optional, `;/?:@&=+$,-_.!~*'()#`. Additional chars to keep intact
+ (except `/a-zA-Z0-9/`).
+- __keepEncoded__ - optional, `true`. By default it skips already encoded sequences
+ (`/%[a-fA-F0-9]{2}/`). If set to `false`, `%` will be encoded.
+
+
+### encode.defaultChars, encode.componentChars
+
+You can use these constants as second argument to `encode` function.
+
+ - `encode.defaultChars` is the same exclude set as in the standard `encodeURI()` function
+ - `encode.componentChars` is the same exclude set as in the `encodeURIComponent()` function
+
+For example, `encode('something', encode.componentChars, true)` is roughly the equivalent of
+the `encodeURIComponent()` function (except `encode()` doesn't throw).
+
+
+### .decode(str [, exclude]) -> String
+
+Decode percent-encoded string. Invalid percent-encoded sequences (e.g. `%2G`)
+are left as is. Invalid UTF-8 characters are replaced with `U+FFFD`.
+
+
+Params:
+
+- __str__ - input string.
+- __exclude__ - set of characters to leave encoded, optional, `;/?:@&=+$,#`.
+
+
+### decode.defaultChars, decode.componentChars
+
+You can use these constants as second argument to `decode` function.
+
+ - `decode.defaultChars` is the same exclude set as in the standard `decodeURI()` function
+ - `decode.componentChars` is the same exclude set as in the `decodeURIComponent()` function
+
+For example, `decode('something', decode.defaultChars)` has the same behavior as
+`decodeURI('something')` on a correctly encoded input.
+
+
+### .parse(url, slashesDenoteHost) -> urlObs
+
+Parse url string. Similar to node's [url.parse](http://nodejs.org/api/url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost), but without any
+normalizations and query string parse.
+
+ - __url__ - input url (string)
+ - __slashesDenoteHost__ - if url starts with `//`, expect a hostname after it. Optional, `false`.
+
+Result (hash):
+
+- protocol
+- slashes
+- auth
+- port
+- hostname
+- hash
+- search
+- pathname
+
+Difference with node's `url`:
+
+1. No leading slash in paths, e.g. in `url.parse('http://foo?bar')` pathname is
+ ``, not `/`
+2. Backslashes are not replaced with slashes, so `http:\\example.org\` is
+ treated like a relative path
+3. Trailing colon is treated like a part of the path, i.e. in
+ `http://example.org:foo` pathname is `:foo`
+4. Nothing is URL-encoded in the resulting object, (in joyent/node some chars
+ in auth and paths are encoded)
+5. `url.parse()` does not have `parseQueryString` argument
+6. Removed extraneous result properties: `host`, `path`, `query`, etc.,
+ which can be constructed using other parts of the url.
+
+
+### .format(urlObject)
+
+Format an object previously obtained with `.parse()` function. Similar to node's
+[url.format](http://nodejs.org/api/url.html#url_url_format_urlobj).
+
+
+## License
+
+[MIT](https://github.com/markdown-it/mdurl/blob/master/LICENSE)
diff --git a/node_modules/mdurl/build/index.cjs.js b/node_modules/mdurl/build/index.cjs.js
new file mode 100644
index 0000000..bfa6f01
--- /dev/null
+++ b/node_modules/mdurl/build/index.cjs.js
@@ -0,0 +1,534 @@
+'use strict';
+
+/* eslint-disable no-bitwise */
+
+const decodeCache = {};
+
+function getDecodeCache (exclude) {
+ let cache = decodeCache[exclude];
+ if (cache) { return cache }
+
+ cache = decodeCache[exclude] = [];
+
+ for (let i = 0; i < 128; i++) {
+ const ch = String.fromCharCode(i);
+ cache.push(ch);
+ }
+
+ for (let i = 0; i < exclude.length; i++) {
+ const ch = exclude.charCodeAt(i);
+ cache[ch] = '%' + ('0' + ch.toString(16).toUpperCase()).slice(-2);
+ }
+
+ return cache
+}
+
+// Decode percent-encoded string.
+//
+function decode (string, exclude) {
+ if (typeof exclude !== 'string') {
+ exclude = decode.defaultChars;
+ }
+
+ const cache = getDecodeCache(exclude);
+
+ return string.replace(/(%[a-f0-9]{2})+/gi, function (seq) {
+ let result = '';
+
+ for (let i = 0, l = seq.length; i < l; i += 3) {
+ const b1 = parseInt(seq.slice(i + 1, i + 3), 16);
+
+ if (b1 < 0x80) {
+ result += cache[b1];
+ continue
+ }
+
+ if ((b1 & 0xE0) === 0xC0 && (i + 3 < l)) {
+ // 110xxxxx 10xxxxxx
+ const b2 = parseInt(seq.slice(i + 4, i + 6), 16);
+
+ if ((b2 & 0xC0) === 0x80) {
+ const chr = ((b1 << 6) & 0x7C0) | (b2 & 0x3F);
+
+ if (chr < 0x80) {
+ result += '\ufffd\ufffd';
+ } else {
+ result += String.fromCharCode(chr);
+ }
+
+ i += 3;
+ continue
+ }
+ }
+
+ if ((b1 & 0xF0) === 0xE0 && (i + 6 < l)) {
+ // 1110xxxx 10xxxxxx 10xxxxxx
+ const b2 = parseInt(seq.slice(i + 4, i + 6), 16);
+ const b3 = parseInt(seq.slice(i + 7, i + 9), 16);
+
+ if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
+ const chr = ((b1 << 12) & 0xF000) | ((b2 << 6) & 0xFC0) | (b3 & 0x3F);
+
+ if (chr < 0x800 || (chr >= 0xD800 && chr <= 0xDFFF)) {
+ result += '\ufffd\ufffd\ufffd';
+ } else {
+ result += String.fromCharCode(chr);
+ }
+
+ i += 6;
+ continue
+ }
+ }
+
+ if ((b1 & 0xF8) === 0xF0 && (i + 9 < l)) {
+ // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx
+ const b2 = parseInt(seq.slice(i + 4, i + 6), 16);
+ const b3 = parseInt(seq.slice(i + 7, i + 9), 16);
+ const b4 = parseInt(seq.slice(i + 10, i + 12), 16);
+
+ if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80 && (b4 & 0xC0) === 0x80) {
+ let chr = ((b1 << 18) & 0x1C0000) | ((b2 << 12) & 0x3F000) | ((b3 << 6) & 0xFC0) | (b4 & 0x3F);
+
+ if (chr < 0x10000 || chr > 0x10FFFF) {
+ result += '\ufffd\ufffd\ufffd\ufffd';
+ } else {
+ chr -= 0x10000;
+ result += String.fromCharCode(0xD800 + (chr >> 10), 0xDC00 + (chr & 0x3FF));
+ }
+
+ i += 9;
+ continue
+ }
+ }
+
+ result += '\ufffd';
+ }
+
+ return result
+ })
+}
+
+decode.defaultChars = ';/?:@&=+$,#';
+decode.componentChars = '';
+
+const encodeCache = {};
+
+// Create a lookup array where anything but characters in `chars` string
+// and alphanumeric chars is percent-encoded.
+//
+function getEncodeCache (exclude) {
+ let cache = encodeCache[exclude];
+ if (cache) { return cache }
+
+ cache = encodeCache[exclude] = [];
+
+ for (let i = 0; i < 128; i++) {
+ const ch = String.fromCharCode(i);
+
+ if (/^[0-9a-z]$/i.test(ch)) {
+ // always allow unencoded alphanumeric characters
+ cache.push(ch);
+ } else {
+ cache.push('%' + ('0' + i.toString(16).toUpperCase()).slice(-2));
+ }
+ }
+
+ for (let i = 0; i < exclude.length; i++) {
+ cache[exclude.charCodeAt(i)] = exclude[i];
+ }
+
+ return cache
+}
+
+// Encode unsafe characters with percent-encoding, skipping already
+// encoded sequences.
+//
+// - string - string to encode
+// - exclude - list of characters to ignore (in addition to a-zA-Z0-9)
+// - keepEscaped - don't encode '%' in a correct escape sequence (default: true)
+//
+function encode (string, exclude, keepEscaped) {
+ if (typeof exclude !== 'string') {
+ // encode(string, keepEscaped)
+ keepEscaped = exclude;
+ exclude = encode.defaultChars;
+ }
+
+ if (typeof keepEscaped === 'undefined') {
+ keepEscaped = true;
+ }
+
+ const cache = getEncodeCache(exclude);
+ let result = '';
+
+ for (let i = 0, l = string.length; i < l; i++) {
+ const code = string.charCodeAt(i);
+
+ if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) {
+ if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) {
+ result += string.slice(i, i + 3);
+ i += 2;
+ continue
+ }
+ }
+
+ if (code < 128) {
+ result += cache[code];
+ continue
+ }
+
+ if (code >= 0xD800 && code <= 0xDFFF) {
+ if (code >= 0xD800 && code <= 0xDBFF && i + 1 < l) {
+ const nextCode = string.charCodeAt(i + 1);
+ if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) {
+ result += encodeURIComponent(string[i] + string[i + 1]);
+ i++;
+ continue
+ }
+ }
+ result += '%EF%BF%BD';
+ continue
+ }
+
+ result += encodeURIComponent(string[i]);
+ }
+
+ return result
+}
+
+encode.defaultChars = ";/?:@&=+$,-_.!~*'()#";
+encode.componentChars = "-_.!~*'()";
+
+function format (url) {
+ let result = '';
+
+ result += url.protocol || '';
+ result += url.slashes ? '//' : '';
+ result += url.auth ? url.auth + '@' : '';
+
+ if (url.hostname && url.hostname.indexOf(':') !== -1) {
+ // ipv6 address
+ result += '[' + url.hostname + ']';
+ } else {
+ result += url.hostname || '';
+ }
+
+ result += url.port ? ':' + url.port : '';
+ result += url.pathname || '';
+ result += url.search || '';
+ result += url.hash || '';
+
+ return result
+}
+
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+//
+// Changes from joyent/node:
+//
+// 1. No leading slash in paths,
+// e.g. in `url.parse('http://foo?bar')` pathname is ``, not `/`
+//
+// 2. Backslashes are not replaced with slashes,
+// so `http:\\example.org\` is treated like a relative path
+//
+// 3. Trailing colon is treated like a part of the path,
+// i.e. in `http://example.org:foo` pathname is `:foo`
+//
+// 4. Nothing is URL-encoded in the resulting object,
+// (in joyent/node some chars in auth and paths are encoded)
+//
+// 5. `url.parse()` does not have `parseQueryString` argument
+//
+// 6. Removed extraneous result properties: `host`, `path`, `query`, etc.,
+// which can be constructed using other parts of the url.
+//
+
+function Url () {
+ this.protocol = null;
+ this.slashes = null;
+ this.auth = null;
+ this.port = null;
+ this.hostname = null;
+ this.hash = null;
+ this.search = null;
+ this.pathname = null;
+}
+
+// Reference: RFC 3986, RFC 1808, RFC 2396
+
+// define these here so at least they only have to be
+// compiled once on the first module load.
+const protocolPattern = /^([a-z0-9.+-]+:)/i;
+const portPattern = /:[0-9]*$/;
+
+// Special case for a simple path URL
+/* eslint-disable-next-line no-useless-escape */
+const simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/;
+
+// RFC 2396: characters reserved for delimiting URLs.
+// We actually just auto-escape these.
+const delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'];
+
+// RFC 2396: characters not allowed for various reasons.
+const unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims);
+
+// Allowed by RFCs, but cause of XSS attacks. Always escape these.
+const autoEscape = ['\''].concat(unwise);
+// Characters that are never ever allowed in a hostname.
+// Note that any invalid chars are also handled, but these
+// are the ones that are *expected* to be seen, so we fast-path
+// them.
+const nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape);
+const hostEndingChars = ['/', '?', '#'];
+const hostnameMaxLen = 255;
+const hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/;
+const hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/;
+// protocols that can allow "unsafe" and "unwise" chars.
+// protocols that never have a hostname.
+const hostlessProtocol = {
+ javascript: true,
+ 'javascript:': true
+};
+// protocols that always contain a // bit.
+const slashedProtocol = {
+ http: true,
+ https: true,
+ ftp: true,
+ gopher: true,
+ file: true,
+ 'http:': true,
+ 'https:': true,
+ 'ftp:': true,
+ 'gopher:': true,
+ 'file:': true
+};
+
+function urlParse (url, slashesDenoteHost) {
+ if (url && url instanceof Url) return url
+
+ const u = new Url();
+ u.parse(url, slashesDenoteHost);
+ return u
+}
+
+Url.prototype.parse = function (url, slashesDenoteHost) {
+ let lowerProto, hec, slashes;
+ let rest = url;
+
+ // trim before proceeding.
+ // This is to support parse stuff like " http://foo.com \n"
+ rest = rest.trim();
+
+ if (!slashesDenoteHost && url.split('#').length === 1) {
+ // Try fast path regexp
+ const simplePath = simplePathPattern.exec(rest);
+ if (simplePath) {
+ this.pathname = simplePath[1];
+ if (simplePath[2]) {
+ this.search = simplePath[2];
+ }
+ return this
+ }
+ }
+
+ let proto = protocolPattern.exec(rest);
+ if (proto) {
+ proto = proto[0];
+ lowerProto = proto.toLowerCase();
+ this.protocol = proto;
+ rest = rest.substr(proto.length);
+ }
+
+ // figure out if it's got a host
+ // user@server is *always* interpreted as a hostname, and url
+ // resolution will treat //foo/bar as host=foo,path=bar because that's
+ // how the browser resolves relative URLs.
+ /* eslint-disable-next-line no-useless-escape */
+ if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
+ slashes = rest.substr(0, 2) === '//';
+ if (slashes && !(proto && hostlessProtocol[proto])) {
+ rest = rest.substr(2);
+ this.slashes = true;
+ }
+ }
+
+ if (!hostlessProtocol[proto] &&
+ (slashes || (proto && !slashedProtocol[proto]))) {
+ // there's a hostname.
+ // the first instance of /, ?, ;, or # ends the host.
+ //
+ // If there is an @ in the hostname, then non-host chars *are* allowed
+ // to the left of the last @ sign, unless some host-ending character
+ // comes *before* the @-sign.
+ // URLs are obnoxious.
+ //
+ // ex:
+ // http://a@b@c/ => user:a@b host:c
+ // http://a@b?@c => user:a host:c path:/?@c
+
+ // v0.12 TODO(isaacs): This is not quite how Chrome does things.
+ // Review our test case against browsers more comprehensively.
+
+ // find the first instance of any hostEndingChars
+ let hostEnd = -1;
+ for (let i = 0; i < hostEndingChars.length; i++) {
+ hec = rest.indexOf(hostEndingChars[i]);
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) {
+ hostEnd = hec;
+ }
+ }
+
+ // at this point, either we have an explicit point where the
+ // auth portion cannot go past, or the last @ char is the decider.
+ let auth, atSign;
+ if (hostEnd === -1) {
+ // atSign can be anywhere.
+ atSign = rest.lastIndexOf('@');
+ } else {
+ // atSign must be in auth portion.
+ // http://a@b/c@d => host:b auth:a path:/c@d
+ atSign = rest.lastIndexOf('@', hostEnd);
+ }
+
+ // Now we have a portion which is definitely the auth.
+ // Pull that off.
+ if (atSign !== -1) {
+ auth = rest.slice(0, atSign);
+ rest = rest.slice(atSign + 1);
+ this.auth = auth;
+ }
+
+ // the host is the remaining to the left of the first non-host char
+ hostEnd = -1;
+ for (let i = 0; i < nonHostChars.length; i++) {
+ hec = rest.indexOf(nonHostChars[i]);
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) {
+ hostEnd = hec;
+ }
+ }
+ // if we still have not hit it, then the entire thing is a host.
+ if (hostEnd === -1) {
+ hostEnd = rest.length;
+ }
+
+ if (rest[hostEnd - 1] === ':') { hostEnd--; }
+ const host = rest.slice(0, hostEnd);
+ rest = rest.slice(hostEnd);
+
+ // pull out port.
+ this.parseHost(host);
+
+ // we've indicated that there is a hostname,
+ // so even if it's empty, it has to be present.
+ this.hostname = this.hostname || '';
+
+ // if hostname begins with [ and ends with ]
+ // assume that it's an IPv6 address.
+ const ipv6Hostname = this.hostname[0] === '[' &&
+ this.hostname[this.hostname.length - 1] === ']';
+
+ // validate a little.
+ if (!ipv6Hostname) {
+ const hostparts = this.hostname.split(/\./);
+ for (let i = 0, l = hostparts.length; i < l; i++) {
+ const part = hostparts[i];
+ if (!part) { continue }
+ if (!part.match(hostnamePartPattern)) {
+ let newpart = '';
+ for (let j = 0, k = part.length; j < k; j++) {
+ if (part.charCodeAt(j) > 127) {
+ // we replace non-ASCII char with a temporary placeholder
+ // we need this to make sure size of hostname is not
+ // broken by replacing non-ASCII by nothing
+ newpart += 'x';
+ } else {
+ newpart += part[j];
+ }
+ }
+ // we test again with ASCII char only
+ if (!newpart.match(hostnamePartPattern)) {
+ const validParts = hostparts.slice(0, i);
+ const notHost = hostparts.slice(i + 1);
+ const bit = part.match(hostnamePartStart);
+ if (bit) {
+ validParts.push(bit[1]);
+ notHost.unshift(bit[2]);
+ }
+ if (notHost.length) {
+ rest = notHost.join('.') + rest;
+ }
+ this.hostname = validParts.join('.');
+ break
+ }
+ }
+ }
+ }
+
+ if (this.hostname.length > hostnameMaxLen) {
+ this.hostname = '';
+ }
+
+ // strip [ and ] from the hostname
+ // the host field still retains them, though
+ if (ipv6Hostname) {
+ this.hostname = this.hostname.substr(1, this.hostname.length - 2);
+ }
+ }
+
+ // chop off from the tail first.
+ const hash = rest.indexOf('#');
+ if (hash !== -1) {
+ // got a fragment string.
+ this.hash = rest.substr(hash);
+ rest = rest.slice(0, hash);
+ }
+ const qm = rest.indexOf('?');
+ if (qm !== -1) {
+ this.search = rest.substr(qm);
+ rest = rest.slice(0, qm);
+ }
+ if (rest) { this.pathname = rest; }
+ if (slashedProtocol[lowerProto] &&
+ this.hostname && !this.pathname) {
+ this.pathname = '';
+ }
+
+ return this
+};
+
+Url.prototype.parseHost = function (host) {
+ let port = portPattern.exec(host);
+ if (port) {
+ port = port[0];
+ if (port !== ':') {
+ this.port = port.substr(1);
+ }
+ host = host.substr(0, host.length - port.length);
+ }
+ if (host) { this.hostname = host; }
+};
+
+exports.decode = decode;
+exports.encode = encode;
+exports.format = format;
+exports.parse = urlParse;
diff --git a/node_modules/mdurl/index.mjs b/node_modules/mdurl/index.mjs
new file mode 100644
index 0000000..fd78c37
--- /dev/null
+++ b/node_modules/mdurl/index.mjs
@@ -0,0 +1,11 @@
+import decode from './lib/decode.mjs'
+import encode from './lib/encode.mjs'
+import format from './lib/format.mjs'
+import parse from './lib/parse.mjs'
+
+export {
+ decode,
+ encode,
+ format,
+ parse
+}
diff --git a/node_modules/mdurl/lib/decode.mjs b/node_modules/mdurl/lib/decode.mjs
new file mode 100644
index 0000000..9fdc133
--- /dev/null
+++ b/node_modules/mdurl/lib/decode.mjs
@@ -0,0 +1,112 @@
+/* eslint-disable no-bitwise */
+
+const decodeCache = {}
+
+function getDecodeCache (exclude) {
+ let cache = decodeCache[exclude]
+ if (cache) { return cache }
+
+ cache = decodeCache[exclude] = []
+
+ for (let i = 0; i < 128; i++) {
+ const ch = String.fromCharCode(i)
+ cache.push(ch)
+ }
+
+ for (let i = 0; i < exclude.length; i++) {
+ const ch = exclude.charCodeAt(i)
+ cache[ch] = '%' + ('0' + ch.toString(16).toUpperCase()).slice(-2)
+ }
+
+ return cache
+}
+
+// Decode percent-encoded string.
+//
+function decode (string, exclude) {
+ if (typeof exclude !== 'string') {
+ exclude = decode.defaultChars
+ }
+
+ const cache = getDecodeCache(exclude)
+
+ return string.replace(/(%[a-f0-9]{2})+/gi, function (seq) {
+ let result = ''
+
+ for (let i = 0, l = seq.length; i < l; i += 3) {
+ const b1 = parseInt(seq.slice(i + 1, i + 3), 16)
+
+ if (b1 < 0x80) {
+ result += cache[b1]
+ continue
+ }
+
+ if ((b1 & 0xE0) === 0xC0 && (i + 3 < l)) {
+ // 110xxxxx 10xxxxxx
+ const b2 = parseInt(seq.slice(i + 4, i + 6), 16)
+
+ if ((b2 & 0xC0) === 0x80) {
+ const chr = ((b1 << 6) & 0x7C0) | (b2 & 0x3F)
+
+ if (chr < 0x80) {
+ result += '\ufffd\ufffd'
+ } else {
+ result += String.fromCharCode(chr)
+ }
+
+ i += 3
+ continue
+ }
+ }
+
+ if ((b1 & 0xF0) === 0xE0 && (i + 6 < l)) {
+ // 1110xxxx 10xxxxxx 10xxxxxx
+ const b2 = parseInt(seq.slice(i + 4, i + 6), 16)
+ const b3 = parseInt(seq.slice(i + 7, i + 9), 16)
+
+ if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
+ const chr = ((b1 << 12) & 0xF000) | ((b2 << 6) & 0xFC0) | (b3 & 0x3F)
+
+ if (chr < 0x800 || (chr >= 0xD800 && chr <= 0xDFFF)) {
+ result += '\ufffd\ufffd\ufffd'
+ } else {
+ result += String.fromCharCode(chr)
+ }
+
+ i += 6
+ continue
+ }
+ }
+
+ if ((b1 & 0xF8) === 0xF0 && (i + 9 < l)) {
+ // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx
+ const b2 = parseInt(seq.slice(i + 4, i + 6), 16)
+ const b3 = parseInt(seq.slice(i + 7, i + 9), 16)
+ const b4 = parseInt(seq.slice(i + 10, i + 12), 16)
+
+ if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80 && (b4 & 0xC0) === 0x80) {
+ let chr = ((b1 << 18) & 0x1C0000) | ((b2 << 12) & 0x3F000) | ((b3 << 6) & 0xFC0) | (b4 & 0x3F)
+
+ if (chr < 0x10000 || chr > 0x10FFFF) {
+ result += '\ufffd\ufffd\ufffd\ufffd'
+ } else {
+ chr -= 0x10000
+ result += String.fromCharCode(0xD800 + (chr >> 10), 0xDC00 + (chr & 0x3FF))
+ }
+
+ i += 9
+ continue
+ }
+ }
+
+ result += '\ufffd'
+ }
+
+ return result
+ })
+}
+
+decode.defaultChars = ';/?:@&=+$,#'
+decode.componentChars = ''
+
+export default decode
diff --git a/node_modules/mdurl/lib/encode.mjs b/node_modules/mdurl/lib/encode.mjs
new file mode 100644
index 0000000..7a0c356
--- /dev/null
+++ b/node_modules/mdurl/lib/encode.mjs
@@ -0,0 +1,89 @@
+const encodeCache = {}
+
+// Create a lookup array where anything but characters in `chars` string
+// and alphanumeric chars is percent-encoded.
+//
+function getEncodeCache (exclude) {
+ let cache = encodeCache[exclude]
+ if (cache) { return cache }
+
+ cache = encodeCache[exclude] = []
+
+ for (let i = 0; i < 128; i++) {
+ const ch = String.fromCharCode(i)
+
+ if (/^[0-9a-z]$/i.test(ch)) {
+ // always allow unencoded alphanumeric characters
+ cache.push(ch)
+ } else {
+ cache.push('%' + ('0' + i.toString(16).toUpperCase()).slice(-2))
+ }
+ }
+
+ for (let i = 0; i < exclude.length; i++) {
+ cache[exclude.charCodeAt(i)] = exclude[i]
+ }
+
+ return cache
+}
+
+// Encode unsafe characters with percent-encoding, skipping already
+// encoded sequences.
+//
+// - string - string to encode
+// - exclude - list of characters to ignore (in addition to a-zA-Z0-9)
+// - keepEscaped - don't encode '%' in a correct escape sequence (default: true)
+//
+function encode (string, exclude, keepEscaped) {
+ if (typeof exclude !== 'string') {
+ // encode(string, keepEscaped)
+ keepEscaped = exclude
+ exclude = encode.defaultChars
+ }
+
+ if (typeof keepEscaped === 'undefined') {
+ keepEscaped = true
+ }
+
+ const cache = getEncodeCache(exclude)
+ let result = ''
+
+ for (let i = 0, l = string.length; i < l; i++) {
+ const code = string.charCodeAt(i)
+
+ if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) {
+ if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) {
+ result += string.slice(i, i + 3)
+ i += 2
+ continue
+ }
+ }
+
+ if (code < 128) {
+ result += cache[code]
+ continue
+ }
+
+ if (code >= 0xD800 && code <= 0xDFFF) {
+ if (code >= 0xD800 && code <= 0xDBFF && i + 1 < l) {
+ const nextCode = string.charCodeAt(i + 1)
+ if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) {
+ result += encodeURIComponent(string[i] + string[i + 1])
+ i++
+ continue
+ }
+ }
+ result += '%EF%BF%BD'
+ continue
+ }
+
+ result += encodeURIComponent(string[i])
+ }
+
+ return result
+}
+
+encode.defaultChars = ";/?:@&=+$,-_.!~*'()#"
+encode.componentChars = "-_.!~*'()"
+
+export default encode
diff --git a/node_modules/mdurl/lib/format.mjs b/node_modules/mdurl/lib/format.mjs
new file mode 100644
index 0000000..4b76000
--- /dev/null
+++ b/node_modules/mdurl/lib/format.mjs
@@ -0,0 +1,21 @@
+export default function format (url) {
+ let result = ''
+
+ result += url.protocol || ''
+ result += url.slashes ? '//' : ''
+ result += url.auth ? url.auth + '@' : ''
+
+ if (url.hostname && url.hostname.indexOf(':') !== -1) {
+ // ipv6 address
+ result += '[' + url.hostname + ']'
+ } else {
+ result += url.hostname || ''
+ }
+
+ result += url.port ? ':' + url.port : ''
+ result += url.pathname || ''
+ result += url.search || ''
+ result += url.hash || ''
+
+ return result
+};
diff --git a/node_modules/mdurl/lib/parse.mjs b/node_modules/mdurl/lib/parse.mjs
new file mode 100644
index 0000000..fac5065
--- /dev/null
+++ b/node_modules/mdurl/lib/parse.mjs
@@ -0,0 +1,308 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+//
+// Changes from joyent/node:
+//
+// 1. No leading slash in paths,
+// e.g. in `url.parse('http://foo?bar')` pathname is ``, not `/`
+//
+// 2. Backslashes are not replaced with slashes,
+// so `http:\\example.org\` is treated like a relative path
+//
+// 3. Trailing colon is treated like a part of the path,
+// i.e. in `http://example.org:foo` pathname is `:foo`
+//
+// 4. Nothing is URL-encoded in the resulting object,
+// (in joyent/node some chars in auth and paths are encoded)
+//
+// 5. `url.parse()` does not have `parseQueryString` argument
+//
+// 6. Removed extraneous result properties: `host`, `path`, `query`, etc.,
+// which can be constructed using other parts of the url.
+//
+
+function Url () {
+ this.protocol = null
+ this.slashes = null
+ this.auth = null
+ this.port = null
+ this.hostname = null
+ this.hash = null
+ this.search = null
+ this.pathname = null
+}
+
+// Reference: RFC 3986, RFC 1808, RFC 2396
+
+// define these here so at least they only have to be
+// compiled once on the first module load.
+const protocolPattern = /^([a-z0-9.+-]+:)/i
+const portPattern = /:[0-9]*$/
+
+// Special case for a simple path URL
+/* eslint-disable-next-line no-useless-escape */
+const simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/
+
+// RFC 2396: characters reserved for delimiting URLs.
+// We actually just auto-escape these.
+const delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t']
+
+// RFC 2396: characters not allowed for various reasons.
+const unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims)
+
+// Allowed by RFCs, but cause of XSS attacks. Always escape these.
+const autoEscape = ['\''].concat(unwise)
+// Characters that are never ever allowed in a hostname.
+// Note that any invalid chars are also handled, but these
+// are the ones that are *expected* to be seen, so we fast-path
+// them.
+const nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape)
+const hostEndingChars = ['/', '?', '#']
+const hostnameMaxLen = 255
+const hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/
+const hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/
+// protocols that can allow "unsafe" and "unwise" chars.
+// protocols that never have a hostname.
+const hostlessProtocol = {
+ javascript: true,
+ 'javascript:': true
+}
+// protocols that always contain a // bit.
+const slashedProtocol = {
+ http: true,
+ https: true,
+ ftp: true,
+ gopher: true,
+ file: true,
+ 'http:': true,
+ 'https:': true,
+ 'ftp:': true,
+ 'gopher:': true,
+ 'file:': true
+}
+
+function urlParse (url, slashesDenoteHost) {
+ if (url && url instanceof Url) return url
+
+ const u = new Url()
+ u.parse(url, slashesDenoteHost)
+ return u
+}
+
+Url.prototype.parse = function (url, slashesDenoteHost) {
+ let lowerProto, hec, slashes
+ let rest = url
+
+ // trim before proceeding.
+ // This is to support parse stuff like " http://foo.com \n"
+ rest = rest.trim()
+
+ if (!slashesDenoteHost && url.split('#').length === 1) {
+ // Try fast path regexp
+ const simplePath = simplePathPattern.exec(rest)
+ if (simplePath) {
+ this.pathname = simplePath[1]
+ if (simplePath[2]) {
+ this.search = simplePath[2]
+ }
+ return this
+ }
+ }
+
+ let proto = protocolPattern.exec(rest)
+ if (proto) {
+ proto = proto[0]
+ lowerProto = proto.toLowerCase()
+ this.protocol = proto
+ rest = rest.substr(proto.length)
+ }
+
+ // figure out if it's got a host
+ // user@server is *always* interpreted as a hostname, and url
+ // resolution will treat //foo/bar as host=foo,path=bar because that's
+ // how the browser resolves relative URLs.
+ /* eslint-disable-next-line no-useless-escape */
+ if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
+ slashes = rest.substr(0, 2) === '//'
+ if (slashes && !(proto && hostlessProtocol[proto])) {
+ rest = rest.substr(2)
+ this.slashes = true
+ }
+ }
+
+ if (!hostlessProtocol[proto] &&
+ (slashes || (proto && !slashedProtocol[proto]))) {
+ // there's a hostname.
+ // the first instance of /, ?, ;, or # ends the host.
+ //
+ // If there is an @ in the hostname, then non-host chars *are* allowed
+ // to the left of the last @ sign, unless some host-ending character
+ // comes *before* the @-sign.
+ // URLs are obnoxious.
+ //
+ // ex:
+ // http://a@b@c/ => user:a@b host:c
+ // http://a@b?@c => user:a host:c path:/?@c
+
+ // v0.12 TODO(isaacs): This is not quite how Chrome does things.
+ // Review our test case against browsers more comprehensively.
+
+ // find the first instance of any hostEndingChars
+ let hostEnd = -1
+ for (let i = 0; i < hostEndingChars.length; i++) {
+ hec = rest.indexOf(hostEndingChars[i])
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) {
+ hostEnd = hec
+ }
+ }
+
+ // at this point, either we have an explicit point where the
+ // auth portion cannot go past, or the last @ char is the decider.
+ let auth, atSign
+ if (hostEnd === -1) {
+ // atSign can be anywhere.
+ atSign = rest.lastIndexOf('@')
+ } else {
+ // atSign must be in auth portion.
+ // http://a@b/c@d => host:b auth:a path:/c@d
+ atSign = rest.lastIndexOf('@', hostEnd)
+ }
+
+ // Now we have a portion which is definitely the auth.
+ // Pull that off.
+ if (atSign !== -1) {
+ auth = rest.slice(0, atSign)
+ rest = rest.slice(atSign + 1)
+ this.auth = auth
+ }
+
+ // the host is the remaining to the left of the first non-host char
+ hostEnd = -1
+ for (let i = 0; i < nonHostChars.length; i++) {
+ hec = rest.indexOf(nonHostChars[i])
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) {
+ hostEnd = hec
+ }
+ }
+ // if we still have not hit it, then the entire thing is a host.
+ if (hostEnd === -1) {
+ hostEnd = rest.length
+ }
+
+ if (rest[hostEnd - 1] === ':') { hostEnd-- }
+ const host = rest.slice(0, hostEnd)
+ rest = rest.slice(hostEnd)
+
+ // pull out port.
+ this.parseHost(host)
+
+ // we've indicated that there is a hostname,
+ // so even if it's empty, it has to be present.
+ this.hostname = this.hostname || ''
+
+ // if hostname begins with [ and ends with ]
+ // assume that it's an IPv6 address.
+ const ipv6Hostname = this.hostname[0] === '[' &&
+ this.hostname[this.hostname.length - 1] === ']'
+
+ // validate a little.
+ if (!ipv6Hostname) {
+ const hostparts = this.hostname.split(/\./)
+ for (let i = 0, l = hostparts.length; i < l; i++) {
+ const part = hostparts[i]
+ if (!part) { continue }
+ if (!part.match(hostnamePartPattern)) {
+ let newpart = ''
+ for (let j = 0, k = part.length; j < k; j++) {
+ if (part.charCodeAt(j) > 127) {
+ // we replace non-ASCII char with a temporary placeholder
+ // we need this to make sure size of hostname is not
+ // broken by replacing non-ASCII by nothing
+ newpart += 'x'
+ } else {
+ newpart += part[j]
+ }
+ }
+ // we test again with ASCII char only
+ if (!newpart.match(hostnamePartPattern)) {
+ const validParts = hostparts.slice(0, i)
+ const notHost = hostparts.slice(i + 1)
+ const bit = part.match(hostnamePartStart)
+ if (bit) {
+ validParts.push(bit[1])
+ notHost.unshift(bit[2])
+ }
+ if (notHost.length) {
+ rest = notHost.join('.') + rest
+ }
+ this.hostname = validParts.join('.')
+ break
+ }
+ }
+ }
+ }
+
+ if (this.hostname.length > hostnameMaxLen) {
+ this.hostname = ''
+ }
+
+ // strip [ and ] from the hostname
+ // the host field still retains them, though
+ if (ipv6Hostname) {
+ this.hostname = this.hostname.substr(1, this.hostname.length - 2)
+ }
+ }
+
+ // chop off from the tail first.
+ const hash = rest.indexOf('#')
+ if (hash !== -1) {
+ // got a fragment string.
+ this.hash = rest.substr(hash)
+ rest = rest.slice(0, hash)
+ }
+ const qm = rest.indexOf('?')
+ if (qm !== -1) {
+ this.search = rest.substr(qm)
+ rest = rest.slice(0, qm)
+ }
+ if (rest) { this.pathname = rest }
+ if (slashedProtocol[lowerProto] &&
+ this.hostname && !this.pathname) {
+ this.pathname = ''
+ }
+
+ return this
+}
+
+Url.prototype.parseHost = function (host) {
+ let port = portPattern.exec(host)
+ if (port) {
+ port = port[0]
+ if (port !== ':') {
+ this.port = port.substr(1)
+ }
+ host = host.substr(0, host.length - port.length)
+ }
+ if (host) { this.hostname = host }
+}
+
+export default urlParse
diff --git a/node_modules/mdurl/package.json b/node_modules/mdurl/package.json
new file mode 100644
index 0000000..6e89beb
--- /dev/null
+++ b/node_modules/mdurl/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "mdurl",
+ "version": "2.0.0",
+ "description": "URL utilities for markdown-it",
+ "repository": "markdown-it/mdurl",
+ "license": "MIT",
+ "main": "build/index.cjs.js",
+ "module": "index.mjs",
+ "exports": {
+ ".": {
+ "require": "./build/index.cjs.js",
+ "import": "./index.mjs"
+ },
+ "./*": {
+ "require": "./*",
+ "import": "./*"
+ }
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "build": "rollup -c",
+ "test": "npm run lint && npm run build && c8 --exclude build --exclude test -r text -r html -r lcov mocha",
+ "prepublishOnly": "npm run lint && npm run build"
+ },
+ "files": [
+ "index.mjs",
+ "lib/",
+ "build/"
+ ],
+ "devDependencies": {
+ "c8": "^8.0.1",
+ "eslint": "^8.54.0",
+ "eslint-config-standard": "^17.1.0",
+ "mocha": "^10.2.0",
+ "rollup": "^4.6.1"
+ }
+}