Created
February 16, 2021 18:20
-
-
Save wperron/b332352f3efcce07971f25c44520658a to your computer and use it in GitHub Desktop.
Deno Run Esbuild output
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env -S deno run --allow-net --allow-read | |
(() => { | |
var __defProp = Object.defineProperty; | |
var __export = (target2, all) => { | |
for (var name in all) | |
__defProp(target2, name, {get: all[name], enumerable: true}); | |
}; | |
// _util/os.ts | |
var osType = (() => { | |
if (globalThis.Deno != null) { | |
return Deno.build.os; | |
} | |
const navigator = globalThis.navigator; | |
if (navigator?.appVersion?.includes?.("Win") ?? false) { | |
return "windows"; | |
} | |
return "linux"; | |
})(); | |
var isWindows = osType === "windows"; | |
// path/win32.ts | |
var win32_exports = {}; | |
__export(win32_exports, { | |
basename: () => basename, | |
delimiter: () => delimiter, | |
dirname: () => dirname, | |
extname: () => extname, | |
format: () => format, | |
fromFileUrl: () => fromFileUrl, | |
isAbsolute: () => isAbsolute, | |
join: () => join, | |
normalize: () => normalize, | |
parse: () => parse, | |
relative: () => relative, | |
resolve: () => resolve, | |
sep: () => sep, | |
toFileUrl: () => toFileUrl, | |
toNamespacedPath: () => toNamespacedPath | |
}); | |
// path/_constants.ts | |
var CHAR_UPPERCASE_A = 65; | |
var CHAR_LOWERCASE_A = 97; | |
var CHAR_UPPERCASE_Z = 90; | |
var CHAR_LOWERCASE_Z = 122; | |
var CHAR_DOT = 46; | |
var CHAR_FORWARD_SLASH = 47; | |
var CHAR_BACKWARD_SLASH = 92; | |
var CHAR_COLON = 58; | |
var CHAR_QUESTION_MARK = 63; | |
// path/_util.ts | |
function assertPath(path2) { | |
if (typeof path2 !== "string") { | |
throw new TypeError(`Path must be a string. Received ${JSON.stringify(path2)}`); | |
} | |
} | |
function isPosixPathSeparator(code) { | |
return code === CHAR_FORWARD_SLASH; | |
} | |
function isPathSeparator(code) { | |
return isPosixPathSeparator(code) || code === CHAR_BACKWARD_SLASH; | |
} | |
function isWindowsDeviceRoot(code) { | |
return code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z || code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z; | |
} | |
function normalizeString(path2, allowAboveRoot, separator, isPathSeparator2) { | |
let res = ""; | |
let lastSegmentLength = 0; | |
let lastSlash = -1; | |
let dots = 0; | |
let code; | |
for (let i = 0, len = path2.length; i <= len; ++i) { | |
if (i < len) | |
code = path2.charCodeAt(i); | |
else if (isPathSeparator2(code)) | |
break; | |
else | |
code = CHAR_FORWARD_SLASH; | |
if (isPathSeparator2(code)) { | |
if (lastSlash === i - 1 || dots === 1) { | |
} else if (lastSlash !== i - 1 && dots === 2) { | |
if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== CHAR_DOT || res.charCodeAt(res.length - 2) !== CHAR_DOT) { | |
if (res.length > 2) { | |
const lastSlashIndex = res.lastIndexOf(separator); | |
if (lastSlashIndex === -1) { | |
res = ""; | |
lastSegmentLength = 0; | |
} else { | |
res = res.slice(0, lastSlashIndex); | |
lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); | |
} | |
lastSlash = i; | |
dots = 0; | |
continue; | |
} else if (res.length === 2 || res.length === 1) { | |
res = ""; | |
lastSegmentLength = 0; | |
lastSlash = i; | |
dots = 0; | |
continue; | |
} | |
} | |
if (allowAboveRoot) { | |
if (res.length > 0) | |
res += `${separator}..`; | |
else | |
res = ".."; | |
lastSegmentLength = 2; | |
} | |
} else { | |
if (res.length > 0) | |
res += separator + path2.slice(lastSlash + 1, i); | |
else | |
res = path2.slice(lastSlash + 1, i); | |
lastSegmentLength = i - lastSlash - 1; | |
} | |
lastSlash = i; | |
dots = 0; | |
} else if (code === CHAR_DOT && dots !== -1) { | |
++dots; | |
} else { | |
dots = -1; | |
} | |
} | |
return res; | |
} | |
function _format(sep4, pathObject) { | |
const dir = pathObject.dir || pathObject.root; | |
const base = pathObject.base || (pathObject.name || "") + (pathObject.ext || ""); | |
if (!dir) | |
return base; | |
if (dir === pathObject.root) | |
return dir + base; | |
return dir + sep4 + base; | |
} | |
// _util/assert.ts | |
var DenoStdInternalError = class extends Error { | |
constructor(message) { | |
super(message); | |
this.name = "DenoStdInternalError"; | |
} | |
}; | |
function assert(expr, msg = "") { | |
if (!expr) { | |
throw new DenoStdInternalError(msg); | |
} | |
} | |
// path/win32.ts | |
var sep = "\\"; | |
var delimiter = ";"; | |
function resolve(...pathSegments) { | |
let resolvedDevice = ""; | |
let resolvedTail = ""; | |
let resolvedAbsolute = false; | |
for (let i = pathSegments.length - 1; i >= -1; i--) { | |
let path2; | |
if (i >= 0) { | |
path2 = pathSegments[i]; | |
} else if (!resolvedDevice) { | |
if (globalThis.Deno == null) { | |
throw new TypeError("Resolved a drive-letter-less path without a CWD."); | |
} | |
path2 = Deno.cwd(); | |
} else { | |
if (globalThis.Deno == null) { | |
throw new TypeError("Resolved a relative path without a CWD."); | |
} | |
path2 = Deno.env.get(`=${resolvedDevice}`) || Deno.cwd(); | |
if (path2 === void 0 || path2.slice(0, 3).toLowerCase() !== `${resolvedDevice.toLowerCase()}\\`) { | |
path2 = `${resolvedDevice}\\`; | |
} | |
} | |
assertPath(path2); | |
const len = path2.length; | |
if (len === 0) | |
continue; | |
let rootEnd = 0; | |
let device = ""; | |
let isAbsolute4 = false; | |
const code = path2.charCodeAt(0); | |
if (len > 1) { | |
if (isPathSeparator(code)) { | |
isAbsolute4 = true; | |
if (isPathSeparator(path2.charCodeAt(1))) { | |
let j = 2; | |
let last = j; | |
for (; j < len; ++j) { | |
if (isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j < len && j !== last) { | |
const firstPart = path2.slice(last, j); | |
last = j; | |
for (; j < len; ++j) { | |
if (!isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j < len && j !== last) { | |
last = j; | |
for (; j < len; ++j) { | |
if (isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j === len) { | |
device = `\\\\${firstPart}\\${path2.slice(last)}`; | |
rootEnd = j; | |
} else if (j !== last) { | |
device = `\\\\${firstPart}\\${path2.slice(last, j)}`; | |
rootEnd = j; | |
} | |
} | |
} | |
} else { | |
rootEnd = 1; | |
} | |
} else if (isWindowsDeviceRoot(code)) { | |
if (path2.charCodeAt(1) === CHAR_COLON) { | |
device = path2.slice(0, 2); | |
rootEnd = 2; | |
if (len > 2) { | |
if (isPathSeparator(path2.charCodeAt(2))) { | |
isAbsolute4 = true; | |
rootEnd = 3; | |
} | |
} | |
} | |
} | |
} else if (isPathSeparator(code)) { | |
rootEnd = 1; | |
isAbsolute4 = true; | |
} | |
if (device.length > 0 && resolvedDevice.length > 0 && device.toLowerCase() !== resolvedDevice.toLowerCase()) { | |
continue; | |
} | |
if (resolvedDevice.length === 0 && device.length > 0) { | |
resolvedDevice = device; | |
} | |
if (!resolvedAbsolute) { | |
resolvedTail = `${path2.slice(rootEnd)}\\${resolvedTail}`; | |
resolvedAbsolute = isAbsolute4; | |
} | |
if (resolvedAbsolute && resolvedDevice.length > 0) | |
break; | |
} | |
resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, "\\", isPathSeparator); | |
return resolvedDevice + (resolvedAbsolute ? "\\" : "") + resolvedTail || "."; | |
} | |
function normalize(path2) { | |
assertPath(path2); | |
const len = path2.length; | |
if (len === 0) | |
return "."; | |
let rootEnd = 0; | |
let device; | |
let isAbsolute4 = false; | |
const code = path2.charCodeAt(0); | |
if (len > 1) { | |
if (isPathSeparator(code)) { | |
isAbsolute4 = true; | |
if (isPathSeparator(path2.charCodeAt(1))) { | |
let j = 2; | |
let last = j; | |
for (; j < len; ++j) { | |
if (isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j < len && j !== last) { | |
const firstPart = path2.slice(last, j); | |
last = j; | |
for (; j < len; ++j) { | |
if (!isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j < len && j !== last) { | |
last = j; | |
for (; j < len; ++j) { | |
if (isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j === len) { | |
return `\\\\${firstPart}\\${path2.slice(last)}\\`; | |
} else if (j !== last) { | |
device = `\\\\${firstPart}\\${path2.slice(last, j)}`; | |
rootEnd = j; | |
} | |
} | |
} | |
} else { | |
rootEnd = 1; | |
} | |
} else if (isWindowsDeviceRoot(code)) { | |
if (path2.charCodeAt(1) === CHAR_COLON) { | |
device = path2.slice(0, 2); | |
rootEnd = 2; | |
if (len > 2) { | |
if (isPathSeparator(path2.charCodeAt(2))) { | |
isAbsolute4 = true; | |
rootEnd = 3; | |
} | |
} | |
} | |
} | |
} else if (isPathSeparator(code)) { | |
return "\\"; | |
} | |
let tail; | |
if (rootEnd < len) { | |
tail = normalizeString(path2.slice(rootEnd), !isAbsolute4, "\\", isPathSeparator); | |
} else { | |
tail = ""; | |
} | |
if (tail.length === 0 && !isAbsolute4) | |
tail = "."; | |
if (tail.length > 0 && isPathSeparator(path2.charCodeAt(len - 1))) { | |
tail += "\\"; | |
} | |
if (device === void 0) { | |
if (isAbsolute4) { | |
if (tail.length > 0) | |
return `\\${tail}`; | |
else | |
return "\\"; | |
} else if (tail.length > 0) { | |
return tail; | |
} else { | |
return ""; | |
} | |
} else if (isAbsolute4) { | |
if (tail.length > 0) | |
return `${device}\\${tail}`; | |
else | |
return `${device}\\`; | |
} else if (tail.length > 0) { | |
return device + tail; | |
} else { | |
return device; | |
} | |
} | |
function isAbsolute(path2) { | |
assertPath(path2); | |
const len = path2.length; | |
if (len === 0) | |
return false; | |
const code = path2.charCodeAt(0); | |
if (isPathSeparator(code)) { | |
return true; | |
} else if (isWindowsDeviceRoot(code)) { | |
if (len > 2 && path2.charCodeAt(1) === CHAR_COLON) { | |
if (isPathSeparator(path2.charCodeAt(2))) | |
return true; | |
} | |
} | |
return false; | |
} | |
function join(...paths) { | |
const pathsCount = paths.length; | |
if (pathsCount === 0) | |
return "."; | |
let joined; | |
let firstPart = null; | |
for (let i = 0; i < pathsCount; ++i) { | |
const path2 = paths[i]; | |
assertPath(path2); | |
if (path2.length > 0) { | |
if (joined === void 0) | |
joined = firstPart = path2; | |
else | |
joined += `\\${path2}`; | |
} | |
} | |
if (joined === void 0) | |
return "."; | |
let needsReplace = true; | |
let slashCount = 0; | |
assert(firstPart != null); | |
if (isPathSeparator(firstPart.charCodeAt(0))) { | |
++slashCount; | |
const firstLen = firstPart.length; | |
if (firstLen > 1) { | |
if (isPathSeparator(firstPart.charCodeAt(1))) { | |
++slashCount; | |
if (firstLen > 2) { | |
if (isPathSeparator(firstPart.charCodeAt(2))) | |
++slashCount; | |
else { | |
needsReplace = false; | |
} | |
} | |
} | |
} | |
} | |
if (needsReplace) { | |
for (; slashCount < joined.length; ++slashCount) { | |
if (!isPathSeparator(joined.charCodeAt(slashCount))) | |
break; | |
} | |
if (slashCount >= 2) | |
joined = `\\${joined.slice(slashCount)}`; | |
} | |
return normalize(joined); | |
} | |
function relative(from, to) { | |
assertPath(from); | |
assertPath(to); | |
if (from === to) | |
return ""; | |
const fromOrig = resolve(from); | |
const toOrig = resolve(to); | |
if (fromOrig === toOrig) | |
return ""; | |
from = fromOrig.toLowerCase(); | |
to = toOrig.toLowerCase(); | |
if (from === to) | |
return ""; | |
let fromStart = 0; | |
let fromEnd = from.length; | |
for (; fromStart < fromEnd; ++fromStart) { | |
if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) | |
break; | |
} | |
for (; fromEnd - 1 > fromStart; --fromEnd) { | |
if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) | |
break; | |
} | |
const fromLen = fromEnd - fromStart; | |
let toStart = 0; | |
let toEnd = to.length; | |
for (; toStart < toEnd; ++toStart) { | |
if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) | |
break; | |
} | |
for (; toEnd - 1 > toStart; --toEnd) { | |
if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) | |
break; | |
} | |
const toLen = toEnd - toStart; | |
const length = fromLen < toLen ? fromLen : toLen; | |
let lastCommonSep = -1; | |
let i = 0; | |
for (; i <= length; ++i) { | |
if (i === length) { | |
if (toLen > length) { | |
if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { | |
return toOrig.slice(toStart + i + 1); | |
} else if (i === 2) { | |
return toOrig.slice(toStart + i); | |
} | |
} | |
if (fromLen > length) { | |
if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { | |
lastCommonSep = i; | |
} else if (i === 2) { | |
lastCommonSep = 3; | |
} | |
} | |
break; | |
} | |
const fromCode = from.charCodeAt(fromStart + i); | |
const toCode = to.charCodeAt(toStart + i); | |
if (fromCode !== toCode) | |
break; | |
else if (fromCode === CHAR_BACKWARD_SLASH) | |
lastCommonSep = i; | |
} | |
if (i !== length && lastCommonSep === -1) { | |
return toOrig; | |
} | |
let out = ""; | |
if (lastCommonSep === -1) | |
lastCommonSep = 0; | |
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | |
if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { | |
if (out.length === 0) | |
out += ".."; | |
else | |
out += "\\.."; | |
} | |
} | |
if (out.length > 0) { | |
return out + toOrig.slice(toStart + lastCommonSep, toEnd); | |
} else { | |
toStart += lastCommonSep; | |
if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) | |
++toStart; | |
return toOrig.slice(toStart, toEnd); | |
} | |
} | |
function toNamespacedPath(path2) { | |
if (typeof path2 !== "string") | |
return path2; | |
if (path2.length === 0) | |
return ""; | |
const resolvedPath = resolve(path2); | |
if (resolvedPath.length >= 3) { | |
if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { | |
if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { | |
const code = resolvedPath.charCodeAt(2); | |
if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { | |
return `\\\\?\\UNC\\${resolvedPath.slice(2)}`; | |
} | |
} | |
} else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) { | |
if (resolvedPath.charCodeAt(1) === CHAR_COLON && resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH) { | |
return `\\\\?\\${resolvedPath}`; | |
} | |
} | |
} | |
return path2; | |
} | |
function dirname(path2) { | |
assertPath(path2); | |
const len = path2.length; | |
if (len === 0) | |
return "."; | |
let rootEnd = -1; | |
let end = -1; | |
let matchedSlash = true; | |
let offset = 0; | |
const code = path2.charCodeAt(0); | |
if (len > 1) { | |
if (isPathSeparator(code)) { | |
rootEnd = offset = 1; | |
if (isPathSeparator(path2.charCodeAt(1))) { | |
let j = 2; | |
let last = j; | |
for (; j < len; ++j) { | |
if (isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j < len && j !== last) { | |
last = j; | |
for (; j < len; ++j) { | |
if (!isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j < len && j !== last) { | |
last = j; | |
for (; j < len; ++j) { | |
if (isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j === len) { | |
return path2; | |
} | |
if (j !== last) { | |
rootEnd = offset = j + 1; | |
} | |
} | |
} | |
} | |
} else if (isWindowsDeviceRoot(code)) { | |
if (path2.charCodeAt(1) === CHAR_COLON) { | |
rootEnd = offset = 2; | |
if (len > 2) { | |
if (isPathSeparator(path2.charCodeAt(2))) | |
rootEnd = offset = 3; | |
} | |
} | |
} | |
} else if (isPathSeparator(code)) { | |
return path2; | |
} | |
for (let i = len - 1; i >= offset; --i) { | |
if (isPathSeparator(path2.charCodeAt(i))) { | |
if (!matchedSlash) { | |
end = i; | |
break; | |
} | |
} else { | |
matchedSlash = false; | |
} | |
} | |
if (end === -1) { | |
if (rootEnd === -1) | |
return "."; | |
else | |
end = rootEnd; | |
} | |
return path2.slice(0, end); | |
} | |
function basename(path2, ext = "") { | |
if (ext !== void 0 && typeof ext !== "string") { | |
throw new TypeError('"ext" argument must be a string'); | |
} | |
assertPath(path2); | |
let start = 0; | |
let end = -1; | |
let matchedSlash = true; | |
let i; | |
if (path2.length >= 2) { | |
const drive = path2.charCodeAt(0); | |
if (isWindowsDeviceRoot(drive)) { | |
if (path2.charCodeAt(1) === CHAR_COLON) | |
start = 2; | |
} | |
} | |
if (ext !== void 0 && ext.length > 0 && ext.length <= path2.length) { | |
if (ext.length === path2.length && ext === path2) | |
return ""; | |
let extIdx = ext.length - 1; | |
let firstNonSlashEnd = -1; | |
for (i = path2.length - 1; i >= start; --i) { | |
const code = path2.charCodeAt(i); | |
if (isPathSeparator(code)) { | |
if (!matchedSlash) { | |
start = i + 1; | |
break; | |
} | |
} else { | |
if (firstNonSlashEnd === -1) { | |
matchedSlash = false; | |
firstNonSlashEnd = i + 1; | |
} | |
if (extIdx >= 0) { | |
if (code === ext.charCodeAt(extIdx)) { | |
if (--extIdx === -1) { | |
end = i; | |
} | |
} else { | |
extIdx = -1; | |
end = firstNonSlashEnd; | |
} | |
} | |
} | |
} | |
if (start === end) | |
end = firstNonSlashEnd; | |
else if (end === -1) | |
end = path2.length; | |
return path2.slice(start, end); | |
} else { | |
for (i = path2.length - 1; i >= start; --i) { | |
if (isPathSeparator(path2.charCodeAt(i))) { | |
if (!matchedSlash) { | |
start = i + 1; | |
break; | |
} | |
} else if (end === -1) { | |
matchedSlash = false; | |
end = i + 1; | |
} | |
} | |
if (end === -1) | |
return ""; | |
return path2.slice(start, end); | |
} | |
} | |
function extname(path2) { | |
assertPath(path2); | |
let start = 0; | |
let startDot = -1; | |
let startPart = 0; | |
let end = -1; | |
let matchedSlash = true; | |
let preDotState = 0; | |
if (path2.length >= 2 && path2.charCodeAt(1) === CHAR_COLON && isWindowsDeviceRoot(path2.charCodeAt(0))) { | |
start = startPart = 2; | |
} | |
for (let i = path2.length - 1; i >= start; --i) { | |
const code = path2.charCodeAt(i); | |
if (isPathSeparator(code)) { | |
if (!matchedSlash) { | |
startPart = i + 1; | |
break; | |
} | |
continue; | |
} | |
if (end === -1) { | |
matchedSlash = false; | |
end = i + 1; | |
} | |
if (code === CHAR_DOT) { | |
if (startDot === -1) | |
startDot = i; | |
else if (preDotState !== 1) | |
preDotState = 1; | |
} else if (startDot !== -1) { | |
preDotState = -1; | |
} | |
} | |
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { | |
return ""; | |
} | |
return path2.slice(startDot, end); | |
} | |
function format(pathObject) { | |
if (pathObject === null || typeof pathObject !== "object") { | |
throw new TypeError(`The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`); | |
} | |
return _format("\\", pathObject); | |
} | |
function parse(path2) { | |
assertPath(path2); | |
const ret = {root: "", dir: "", base: "", ext: "", name: ""}; | |
const len = path2.length; | |
if (len === 0) | |
return ret; | |
let rootEnd = 0; | |
let code = path2.charCodeAt(0); | |
if (len > 1) { | |
if (isPathSeparator(code)) { | |
rootEnd = 1; | |
if (isPathSeparator(path2.charCodeAt(1))) { | |
let j = 2; | |
let last = j; | |
for (; j < len; ++j) { | |
if (isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j < len && j !== last) { | |
last = j; | |
for (; j < len; ++j) { | |
if (!isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j < len && j !== last) { | |
last = j; | |
for (; j < len; ++j) { | |
if (isPathSeparator(path2.charCodeAt(j))) | |
break; | |
} | |
if (j === len) { | |
rootEnd = j; | |
} else if (j !== last) { | |
rootEnd = j + 1; | |
} | |
} | |
} | |
} | |
} else if (isWindowsDeviceRoot(code)) { | |
if (path2.charCodeAt(1) === CHAR_COLON) { | |
rootEnd = 2; | |
if (len > 2) { | |
if (isPathSeparator(path2.charCodeAt(2))) { | |
if (len === 3) { | |
ret.root = ret.dir = path2; | |
return ret; | |
} | |
rootEnd = 3; | |
} | |
} else { | |
ret.root = ret.dir = path2; | |
return ret; | |
} | |
} | |
} | |
} else if (isPathSeparator(code)) { | |
ret.root = ret.dir = path2; | |
return ret; | |
} | |
if (rootEnd > 0) | |
ret.root = path2.slice(0, rootEnd); | |
let startDot = -1; | |
let startPart = rootEnd; | |
let end = -1; | |
let matchedSlash = true; | |
let i = path2.length - 1; | |
let preDotState = 0; | |
for (; i >= rootEnd; --i) { | |
code = path2.charCodeAt(i); | |
if (isPathSeparator(code)) { | |
if (!matchedSlash) { | |
startPart = i + 1; | |
break; | |
} | |
continue; | |
} | |
if (end === -1) { | |
matchedSlash = false; | |
end = i + 1; | |
} | |
if (code === CHAR_DOT) { | |
if (startDot === -1) | |
startDot = i; | |
else if (preDotState !== 1) | |
preDotState = 1; | |
} else if (startDot !== -1) { | |
preDotState = -1; | |
} | |
} | |
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { | |
if (end !== -1) { | |
ret.base = ret.name = path2.slice(startPart, end); | |
} | |
} else { | |
ret.name = path2.slice(startPart, startDot); | |
ret.base = path2.slice(startPart, end); | |
ret.ext = path2.slice(startDot, end); | |
} | |
if (startPart > 0 && startPart !== rootEnd) { | |
ret.dir = path2.slice(0, startPart - 1); | |
} else | |
ret.dir = ret.root; | |
return ret; | |
} | |
function fromFileUrl(url) { | |
url = url instanceof URL ? url : new URL(url); | |
if (url.protocol != "file:") { | |
throw new TypeError("Must be a file URL."); | |
} | |
let path2 = decodeURIComponent(url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25")).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); | |
if (url.hostname != "") { | |
path2 = `\\\\${url.hostname}${path2}`; | |
} | |
return path2; | |
} | |
function toFileUrl(path2) { | |
if (!isAbsolute(path2)) { | |
throw new TypeError("Must be an absolute path."); | |
} | |
const [, hostname, pathname] = path2.match(/^(?:[/\\]{2}([^/\\]+)(?=[/\\][^/\\]))?(.*)/); | |
const url = new URL("file:///"); | |
url.pathname = pathname.replace(/%/g, "%25"); | |
if (hostname != null) { | |
url.hostname = hostname; | |
if (!url.hostname) { | |
throw new TypeError("Invalid hostname."); | |
} | |
} | |
return url; | |
} | |
// path/posix.ts | |
var posix_exports = {}; | |
__export(posix_exports, { | |
basename: () => basename2, | |
delimiter: () => delimiter2, | |
dirname: () => dirname2, | |
extname: () => extname2, | |
format: () => format2, | |
fromFileUrl: () => fromFileUrl2, | |
isAbsolute: () => isAbsolute2, | |
join: () => join2, | |
normalize: () => normalize2, | |
parse: () => parse2, | |
relative: () => relative2, | |
resolve: () => resolve2, | |
sep: () => sep2, | |
toFileUrl: () => toFileUrl2, | |
toNamespacedPath: () => toNamespacedPath2 | |
}); | |
var sep2 = "/"; | |
var delimiter2 = ":"; | |
function resolve2(...pathSegments) { | |
let resolvedPath = ""; | |
let resolvedAbsolute = false; | |
for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { | |
let path2; | |
if (i >= 0) | |
path2 = pathSegments[i]; | |
else { | |
if (globalThis.Deno == null) { | |
throw new TypeError("Resolved a relative path without a CWD."); | |
} | |
path2 = Deno.cwd(); | |
} | |
assertPath(path2); | |
if (path2.length === 0) { | |
continue; | |
} | |
resolvedPath = `${path2}/${resolvedPath}`; | |
resolvedAbsolute = path2.charCodeAt(0) === CHAR_FORWARD_SLASH; | |
} | |
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, "/", isPosixPathSeparator); | |
if (resolvedAbsolute) { | |
if (resolvedPath.length > 0) | |
return `/${resolvedPath}`; | |
else | |
return "/"; | |
} else if (resolvedPath.length > 0) | |
return resolvedPath; | |
else | |
return "."; | |
} | |
function normalize2(path2) { | |
assertPath(path2); | |
if (path2.length === 0) | |
return "."; | |
const isAbsolute4 = path2.charCodeAt(0) === CHAR_FORWARD_SLASH; | |
const trailingSeparator = path2.charCodeAt(path2.length - 1) === CHAR_FORWARD_SLASH; | |
path2 = normalizeString(path2, !isAbsolute4, "/", isPosixPathSeparator); | |
if (path2.length === 0 && !isAbsolute4) | |
path2 = "."; | |
if (path2.length > 0 && trailingSeparator) | |
path2 += "/"; | |
if (isAbsolute4) | |
return `/${path2}`; | |
return path2; | |
} | |
function isAbsolute2(path2) { | |
assertPath(path2); | |
return path2.length > 0 && path2.charCodeAt(0) === CHAR_FORWARD_SLASH; | |
} | |
function join2(...paths) { | |
if (paths.length === 0) | |
return "."; | |
let joined; | |
for (let i = 0, len = paths.length; i < len; ++i) { | |
const path2 = paths[i]; | |
assertPath(path2); | |
if (path2.length > 0) { | |
if (!joined) | |
joined = path2; | |
else | |
joined += `/${path2}`; | |
} | |
} | |
if (!joined) | |
return "."; | |
return normalize2(joined); | |
} | |
function relative2(from, to) { | |
assertPath(from); | |
assertPath(to); | |
if (from === to) | |
return ""; | |
from = resolve2(from); | |
to = resolve2(to); | |
if (from === to) | |
return ""; | |
let fromStart = 1; | |
const fromEnd = from.length; | |
for (; fromStart < fromEnd; ++fromStart) { | |
if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) | |
break; | |
} | |
const fromLen = fromEnd - fromStart; | |
let toStart = 1; | |
const toEnd = to.length; | |
for (; toStart < toEnd; ++toStart) { | |
if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) | |
break; | |
} | |
const toLen = toEnd - toStart; | |
const length = fromLen < toLen ? fromLen : toLen; | |
let lastCommonSep = -1; | |
let i = 0; | |
for (; i <= length; ++i) { | |
if (i === length) { | |
if (toLen > length) { | |
if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { | |
return to.slice(toStart + i + 1); | |
} else if (i === 0) { | |
return to.slice(toStart + i); | |
} | |
} else if (fromLen > length) { | |
if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { | |
lastCommonSep = i; | |
} else if (i === 0) { | |
lastCommonSep = 0; | |
} | |
} | |
break; | |
} | |
const fromCode = from.charCodeAt(fromStart + i); | |
const toCode = to.charCodeAt(toStart + i); | |
if (fromCode !== toCode) | |
break; | |
else if (fromCode === CHAR_FORWARD_SLASH) | |
lastCommonSep = i; | |
} | |
let out = ""; | |
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | |
if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { | |
if (out.length === 0) | |
out += ".."; | |
else | |
out += "/.."; | |
} | |
} | |
if (out.length > 0) | |
return out + to.slice(toStart + lastCommonSep); | |
else { | |
toStart += lastCommonSep; | |
if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) | |
++toStart; | |
return to.slice(toStart); | |
} | |
} | |
function toNamespacedPath2(path2) { | |
return path2; | |
} | |
function dirname2(path2) { | |
assertPath(path2); | |
if (path2.length === 0) | |
return "."; | |
const hasRoot = path2.charCodeAt(0) === CHAR_FORWARD_SLASH; | |
let end = -1; | |
let matchedSlash = true; | |
for (let i = path2.length - 1; i >= 1; --i) { | |
if (path2.charCodeAt(i) === CHAR_FORWARD_SLASH) { | |
if (!matchedSlash) { | |
end = i; | |
break; | |
} | |
} else { | |
matchedSlash = false; | |
} | |
} | |
if (end === -1) | |
return hasRoot ? "/" : "."; | |
if (hasRoot && end === 1) | |
return "//"; | |
return path2.slice(0, end); | |
} | |
function basename2(path2, ext = "") { | |
if (ext !== void 0 && typeof ext !== "string") { | |
throw new TypeError('"ext" argument must be a string'); | |
} | |
assertPath(path2); | |
let start = 0; | |
let end = -1; | |
let matchedSlash = true; | |
let i; | |
if (ext !== void 0 && ext.length > 0 && ext.length <= path2.length) { | |
if (ext.length === path2.length && ext === path2) | |
return ""; | |
let extIdx = ext.length - 1; | |
let firstNonSlashEnd = -1; | |
for (i = path2.length - 1; i >= 0; --i) { | |
const code = path2.charCodeAt(i); | |
if (code === CHAR_FORWARD_SLASH) { | |
if (!matchedSlash) { | |
start = i + 1; | |
break; | |
} | |
} else { | |
if (firstNonSlashEnd === -1) { | |
matchedSlash = false; | |
firstNonSlashEnd = i + 1; | |
} | |
if (extIdx >= 0) { | |
if (code === ext.charCodeAt(extIdx)) { | |
if (--extIdx === -1) { | |
end = i; | |
} | |
} else { | |
extIdx = -1; | |
end = firstNonSlashEnd; | |
} | |
} | |
} | |
} | |
if (start === end) | |
end = firstNonSlashEnd; | |
else if (end === -1) | |
end = path2.length; | |
return path2.slice(start, end); | |
} else { | |
for (i = path2.length - 1; i >= 0; --i) { | |
if (path2.charCodeAt(i) === CHAR_FORWARD_SLASH) { | |
if (!matchedSlash) { | |
start = i + 1; | |
break; | |
} | |
} else if (end === -1) { | |
matchedSlash = false; | |
end = i + 1; | |
} | |
} | |
if (end === -1) | |
return ""; | |
return path2.slice(start, end); | |
} | |
} | |
function extname2(path2) { | |
assertPath(path2); | |
let startDot = -1; | |
let startPart = 0; | |
let end = -1; | |
let matchedSlash = true; | |
let preDotState = 0; | |
for (let i = path2.length - 1; i >= 0; --i) { | |
const code = path2.charCodeAt(i); | |
if (code === CHAR_FORWARD_SLASH) { | |
if (!matchedSlash) { | |
startPart = i + 1; | |
break; | |
} | |
continue; | |
} | |
if (end === -1) { | |
matchedSlash = false; | |
end = i + 1; | |
} | |
if (code === CHAR_DOT) { | |
if (startDot === -1) | |
startDot = i; | |
else if (preDotState !== 1) | |
preDotState = 1; | |
} else if (startDot !== -1) { | |
preDotState = -1; | |
} | |
} | |
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { | |
return ""; | |
} | |
return path2.slice(startDot, end); | |
} | |
function format2(pathObject) { | |
if (pathObject === null || typeof pathObject !== "object") { | |
throw new TypeError(`The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`); | |
} | |
return _format("/", pathObject); | |
} | |
function parse2(path2) { | |
assertPath(path2); | |
const ret = {root: "", dir: "", base: "", ext: "", name: ""}; | |
if (path2.length === 0) | |
return ret; | |
const isAbsolute4 = path2.charCodeAt(0) === CHAR_FORWARD_SLASH; | |
let start; | |
if (isAbsolute4) { | |
ret.root = "/"; | |
start = 1; | |
} else { | |
start = 0; | |
} | |
let startDot = -1; | |
let startPart = 0; | |
let end = -1; | |
let matchedSlash = true; | |
let i = path2.length - 1; | |
let preDotState = 0; | |
for (; i >= start; --i) { | |
const code = path2.charCodeAt(i); | |
if (code === CHAR_FORWARD_SLASH) { | |
if (!matchedSlash) { | |
startPart = i + 1; | |
break; | |
} | |
continue; | |
} | |
if (end === -1) { | |
matchedSlash = false; | |
end = i + 1; | |
} | |
if (code === CHAR_DOT) { | |
if (startDot === -1) | |
startDot = i; | |
else if (preDotState !== 1) | |
preDotState = 1; | |
} else if (startDot !== -1) { | |
preDotState = -1; | |
} | |
} | |
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { | |
if (end !== -1) { | |
if (startPart === 0 && isAbsolute4) { | |
ret.base = ret.name = path2.slice(1, end); | |
} else { | |
ret.base = ret.name = path2.slice(startPart, end); | |
} | |
} | |
} else { | |
if (startPart === 0 && isAbsolute4) { | |
ret.name = path2.slice(1, startDot); | |
ret.base = path2.slice(1, end); | |
} else { | |
ret.name = path2.slice(startPart, startDot); | |
ret.base = path2.slice(startPart, end); | |
} | |
ret.ext = path2.slice(startDot, end); | |
} | |
if (startPart > 0) | |
ret.dir = path2.slice(0, startPart - 1); | |
else if (isAbsolute4) | |
ret.dir = "/"; | |
return ret; | |
} | |
function fromFileUrl2(url) { | |
url = url instanceof URL ? url : new URL(url); | |
if (url.protocol != "file:") { | |
throw new TypeError("Must be a file URL."); | |
} | |
return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25")); | |
} | |
function toFileUrl2(path2) { | |
if (!isAbsolute2(path2)) { | |
throw new TypeError("Must be an absolute path."); | |
} | |
const url = new URL("file:///"); | |
url.pathname = path2.replace(/%/g, "%25").replace(/\\/g, "%5C"); | |
return url; | |
} | |
// path/mod.ts | |
var path = isWindows ? win32_exports : posix_exports; | |
var posix = posix_exports; | |
var { | |
basename: basename3, | |
delimiter: delimiter3, | |
dirname: dirname3, | |
extname: extname3, | |
format: format3, | |
fromFileUrl: fromFileUrl3, | |
isAbsolute: isAbsolute3, | |
join: join3, | |
normalize: normalize3, | |
parse: parse3, | |
relative: relative3, | |
resolve: resolve3, | |
sep: sep3, | |
toFileUrl: toFileUrl3, | |
toNamespacedPath: toNamespacedPath3 | |
} = path; | |
// bytes/mod.ts | |
function concat(...buf) { | |
let length = 0; | |
for (const b of buf) { | |
length += b.length; | |
} | |
const output = new Uint8Array(length); | |
let index = 0; | |
for (const b of buf) { | |
output.set(b, index); | |
index += b.length; | |
} | |
return output; | |
} | |
function copy(src, dst, off = 0) { | |
off = Math.max(0, Math.min(off, dst.byteLength)); | |
const dstBytesAvailable = dst.byteLength - off; | |
if (src.byteLength > dstBytesAvailable) { | |
src = src.subarray(0, dstBytesAvailable); | |
} | |
dst.set(src, off); | |
return src.byteLength; | |
} | |
// io/bufio.ts | |
var DEFAULT_BUF_SIZE = 4096; | |
var MIN_BUF_SIZE = 16; | |
var MAX_CONSECUTIVE_EMPTY_READS = 100; | |
var CR = "\r".charCodeAt(0); | |
var LF = "\n".charCodeAt(0); | |
var BufferFullError = class extends Error { | |
constructor(partial) { | |
super("Buffer full"); | |
this.partial = partial; | |
this.name = "BufferFullError"; | |
} | |
}; | |
var PartialReadError = class extends Deno.errors.UnexpectedEof { | |
constructor() { | |
super("Encountered UnexpectedEof, data only partially read"); | |
this.name = "PartialReadError"; | |
} | |
}; | |
var BufReader = class { | |
constructor(rd, size = DEFAULT_BUF_SIZE) { | |
this.r = 0; | |
this.w = 0; | |
this.eof = false; | |
if (size < MIN_BUF_SIZE) { | |
size = MIN_BUF_SIZE; | |
} | |
this._reset(new Uint8Array(size), rd); | |
} | |
static create(r, size = DEFAULT_BUF_SIZE) { | |
return r instanceof BufReader ? r : new BufReader(r, size); | |
} | |
size() { | |
return this.buf.byteLength; | |
} | |
buffered() { | |
return this.w - this.r; | |
} | |
async _fill() { | |
if (this.r > 0) { | |
this.buf.copyWithin(0, this.r, this.w); | |
this.w -= this.r; | |
this.r = 0; | |
} | |
if (this.w >= this.buf.byteLength) { | |
throw Error("bufio: tried to fill full buffer"); | |
} | |
for (let i = MAX_CONSECUTIVE_EMPTY_READS; i > 0; i--) { | |
const rr = await this.rd.read(this.buf.subarray(this.w)); | |
if (rr === null) { | |
this.eof = true; | |
return; | |
} | |
assert(rr >= 0, "negative read"); | |
this.w += rr; | |
if (rr > 0) { | |
return; | |
} | |
} | |
throw new Error(`No progress after ${MAX_CONSECUTIVE_EMPTY_READS} read() calls`); | |
} | |
reset(r) { | |
this._reset(this.buf, r); | |
} | |
_reset(buf, rd) { | |
this.buf = buf; | |
this.rd = rd; | |
this.eof = false; | |
} | |
async read(p) { | |
let rr = p.byteLength; | |
if (p.byteLength === 0) | |
return rr; | |
if (this.r === this.w) { | |
if (p.byteLength >= this.buf.byteLength) { | |
const rr2 = await this.rd.read(p); | |
const nread = rr2 ?? 0; | |
assert(nread >= 0, "negative read"); | |
return rr2; | |
} | |
this.r = 0; | |
this.w = 0; | |
rr = await this.rd.read(this.buf); | |
if (rr === 0 || rr === null) | |
return rr; | |
assert(rr >= 0, "negative read"); | |
this.w += rr; | |
} | |
const copied = copy(this.buf.subarray(this.r, this.w), p, 0); | |
this.r += copied; | |
return copied; | |
} | |
async readFull(p) { | |
let bytesRead = 0; | |
while (bytesRead < p.length) { | |
try { | |
const rr = await this.read(p.subarray(bytesRead)); | |
if (rr === null) { | |
if (bytesRead === 0) { | |
return null; | |
} else { | |
throw new PartialReadError(); | |
} | |
} | |
bytesRead += rr; | |
} catch (err) { | |
err.partial = p.subarray(0, bytesRead); | |
throw err; | |
} | |
} | |
return p; | |
} | |
async readByte() { | |
while (this.r === this.w) { | |
if (this.eof) | |
return null; | |
await this._fill(); | |
} | |
const c = this.buf[this.r]; | |
this.r++; | |
return c; | |
} | |
async readString(delim) { | |
if (delim.length !== 1) { | |
throw new Error("Delimiter should be a single character"); | |
} | |
const buffer = await this.readSlice(delim.charCodeAt(0)); | |
if (buffer === null) | |
return null; | |
return new TextDecoder().decode(buffer); | |
} | |
async readLine() { | |
let line; | |
try { | |
line = await this.readSlice(LF); | |
} catch (err) { | |
let {partial} = err; | |
assert(partial instanceof Uint8Array, "bufio: caught error from `readSlice()` without `partial` property"); | |
if (!(err instanceof BufferFullError)) { | |
throw err; | |
} | |
if (!this.eof && partial.byteLength > 0 && partial[partial.byteLength - 1] === CR) { | |
assert(this.r > 0, "bufio: tried to rewind past start of buffer"); | |
this.r--; | |
partial = partial.subarray(0, partial.byteLength - 1); | |
} | |
return {line: partial, more: !this.eof}; | |
} | |
if (line === null) { | |
return null; | |
} | |
if (line.byteLength === 0) { | |
return {line, more: false}; | |
} | |
if (line[line.byteLength - 1] == LF) { | |
let drop = 1; | |
if (line.byteLength > 1 && line[line.byteLength - 2] === CR) { | |
drop = 2; | |
} | |
line = line.subarray(0, line.byteLength - drop); | |
} | |
return {line, more: false}; | |
} | |
async readSlice(delim) { | |
let s = 0; | |
let slice; | |
while (true) { | |
let i = this.buf.subarray(this.r + s, this.w).indexOf(delim); | |
if (i >= 0) { | |
i += s; | |
slice = this.buf.subarray(this.r, this.r + i + 1); | |
this.r += i + 1; | |
break; | |
} | |
if (this.eof) { | |
if (this.r === this.w) { | |
return null; | |
} | |
slice = this.buf.subarray(this.r, this.w); | |
this.r = this.w; | |
break; | |
} | |
if (this.buffered() >= this.buf.byteLength) { | |
this.r = this.w; | |
const oldbuf = this.buf; | |
const newbuf = this.buf.slice(0); | |
this.buf = newbuf; | |
throw new BufferFullError(oldbuf); | |
} | |
s = this.w - this.r; | |
try { | |
await this._fill(); | |
} catch (err) { | |
err.partial = slice; | |
throw err; | |
} | |
} | |
return slice; | |
} | |
async peek(n) { | |
if (n < 0) { | |
throw Error("negative count"); | |
} | |
let avail = this.w - this.r; | |
while (avail < n && avail < this.buf.byteLength && !this.eof) { | |
try { | |
await this._fill(); | |
} catch (err) { | |
err.partial = this.buf.subarray(this.r, this.w); | |
throw err; | |
} | |
avail = this.w - this.r; | |
} | |
if (avail === 0 && this.eof) { | |
return null; | |
} else if (avail < n && this.eof) { | |
return this.buf.subarray(this.r, this.r + avail); | |
} else if (avail < n) { | |
throw new BufferFullError(this.buf.subarray(this.r, this.w)); | |
} | |
return this.buf.subarray(this.r, this.r + n); | |
} | |
}; | |
var AbstractBufBase = class { | |
constructor() { | |
this.usedBufferBytes = 0; | |
this.err = null; | |
} | |
size() { | |
return this.buf.byteLength; | |
} | |
available() { | |
return this.buf.byteLength - this.usedBufferBytes; | |
} | |
buffered() { | |
return this.usedBufferBytes; | |
} | |
}; | |
var BufWriter = class extends AbstractBufBase { | |
constructor(writer, size = DEFAULT_BUF_SIZE) { | |
super(); | |
this.writer = writer; | |
if (size <= 0) { | |
size = DEFAULT_BUF_SIZE; | |
} | |
this.buf = new Uint8Array(size); | |
} | |
static create(writer, size = DEFAULT_BUF_SIZE) { | |
return writer instanceof BufWriter ? writer : new BufWriter(writer, size); | |
} | |
reset(w) { | |
this.err = null; | |
this.usedBufferBytes = 0; | |
this.writer = w; | |
} | |
async flush() { | |
if (this.err !== null) | |
throw this.err; | |
if (this.usedBufferBytes === 0) | |
return; | |
try { | |
await Deno.writeAll(this.writer, this.buf.subarray(0, this.usedBufferBytes)); | |
} catch (e) { | |
this.err = e; | |
throw e; | |
} | |
this.buf = new Uint8Array(this.buf.length); | |
this.usedBufferBytes = 0; | |
} | |
async write(data) { | |
if (this.err !== null) | |
throw this.err; | |
if (data.length === 0) | |
return 0; | |
let totalBytesWritten = 0; | |
let numBytesWritten = 0; | |
while (data.byteLength > this.available()) { | |
if (this.buffered() === 0) { | |
try { | |
numBytesWritten = await this.writer.write(data); | |
} catch (e) { | |
this.err = e; | |
throw e; | |
} | |
} else { | |
numBytesWritten = copy(data, this.buf, this.usedBufferBytes); | |
this.usedBufferBytes += numBytesWritten; | |
await this.flush(); | |
} | |
totalBytesWritten += numBytesWritten; | |
data = data.subarray(numBytesWritten); | |
} | |
numBytesWritten = copy(data, this.buf, this.usedBufferBytes); | |
this.usedBufferBytes += numBytesWritten; | |
totalBytesWritten += numBytesWritten; | |
return totalBytesWritten; | |
} | |
}; | |
// async/deferred.ts | |
function deferred() { | |
let methods; | |
const promise = new Promise((resolve4, reject) => { | |
methods = {resolve: resolve4, reject}; | |
}); | |
return Object.assign(promise, methods); | |
} | |
// async/mux_async_iterator.ts | |
var MuxAsyncIterator = class { | |
constructor() { | |
this.iteratorCount = 0; | |
this.yields = []; | |
this.throws = []; | |
this.signal = deferred(); | |
} | |
add(iterator) { | |
++this.iteratorCount; | |
this.callIteratorNext(iterator); | |
} | |
async callIteratorNext(iterator) { | |
try { | |
const {value, done} = await iterator.next(); | |
if (done) { | |
--this.iteratorCount; | |
} else { | |
this.yields.push({iterator, value}); | |
} | |
} catch (e) { | |
this.throws.push(e); | |
} | |
this.signal.resolve(); | |
} | |
async *iterate() { | |
while (this.iteratorCount > 0) { | |
await this.signal; | |
for (let i = 0; i < this.yields.length; i++) { | |
const {iterator, value} = this.yields[i]; | |
yield value; | |
this.callIteratorNext(iterator); | |
} | |
if (this.throws.length) { | |
for (const e of this.throws) { | |
throw e; | |
} | |
this.throws.length = 0; | |
} | |
this.yields.length = 0; | |
this.signal = deferred(); | |
} | |
} | |
[Symbol.asyncIterator]() { | |
return this.iterate(); | |
} | |
}; | |
// textproto/mod.ts | |
var invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/g; | |
function str(buf) { | |
if (buf == null) { | |
return ""; | |
} else { | |
return new TextDecoder().decode(buf); | |
} | |
} | |
function charCode(s) { | |
return s.charCodeAt(0); | |
} | |
var TextProtoReader = class { | |
constructor(r) { | |
this.r = r; | |
} | |
async readLine() { | |
const s = await this.readLineSlice(); | |
if (s === null) | |
return null; | |
return str(s); | |
} | |
async readMIMEHeader() { | |
const m = new Headers(); | |
let line; | |
let buf = await this.r.peek(1); | |
if (buf === null) { | |
return null; | |
} else if (buf[0] == charCode(" ") || buf[0] == charCode(" ")) { | |
line = await this.readLineSlice(); | |
} | |
buf = await this.r.peek(1); | |
if (buf === null) { | |
throw new Deno.errors.UnexpectedEof(); | |
} else if (buf[0] == charCode(" ") || buf[0] == charCode(" ")) { | |
throw new Deno.errors.InvalidData(`malformed MIME header initial line: ${str(line)}`); | |
} | |
while (true) { | |
const kv = await this.readLineSlice(); | |
if (kv === null) | |
throw new Deno.errors.UnexpectedEof(); | |
if (kv.byteLength === 0) | |
return m; | |
let i = kv.indexOf(charCode(":")); | |
if (i < 0) { | |
throw new Deno.errors.InvalidData(`malformed MIME header line: ${str(kv)}`); | |
} | |
const key = str(kv.subarray(0, i)); | |
if (key == "") { | |
continue; | |
} | |
i++; | |
while (i < kv.byteLength && (kv[i] == charCode(" ") || kv[i] == charCode(" "))) { | |
i++; | |
} | |
const value = str(kv.subarray(i)).replace(invalidHeaderCharRegex, encodeURI); | |
try { | |
m.append(key, value); | |
} catch { | |
} | |
} | |
} | |
async readLineSlice() { | |
let line; | |
while (true) { | |
const r = await this.r.readLine(); | |
if (r === null) | |
return null; | |
const {line: l, more} = r; | |
if (!line && !more) { | |
if (this.skipSpace(l) === 0) { | |
return new Uint8Array(0); | |
} | |
return l; | |
} | |
line = line ? concat(line, l) : l; | |
if (!more) { | |
break; | |
} | |
} | |
return line; | |
} | |
skipSpace(l) { | |
let n = 0; | |
for (let i = 0; i < l.length; i++) { | |
if (l[i] === charCode(" ") || l[i] === charCode(" ")) { | |
continue; | |
} | |
n++; | |
} | |
return n; | |
} | |
}; | |
// http/http_status.ts | |
var Status; | |
(function(Status2) { | |
Status2[Status2["Continue"] = 100] = "Continue"; | |
Status2[Status2["SwitchingProtocols"] = 101] = "SwitchingProtocols"; | |
Status2[Status2["Processing"] = 102] = "Processing"; | |
Status2[Status2["EarlyHints"] = 103] = "EarlyHints"; | |
Status2[Status2["OK"] = 200] = "OK"; | |
Status2[Status2["Created"] = 201] = "Created"; | |
Status2[Status2["Accepted"] = 202] = "Accepted"; | |
Status2[Status2["NonAuthoritativeInfo"] = 203] = "NonAuthoritativeInfo"; | |
Status2[Status2["NoContent"] = 204] = "NoContent"; | |
Status2[Status2["ResetContent"] = 205] = "ResetContent"; | |
Status2[Status2["PartialContent"] = 206] = "PartialContent"; | |
Status2[Status2["MultiStatus"] = 207] = "MultiStatus"; | |
Status2[Status2["AlreadyReported"] = 208] = "AlreadyReported"; | |
Status2[Status2["IMUsed"] = 226] = "IMUsed"; | |
Status2[Status2["MultipleChoices"] = 300] = "MultipleChoices"; | |
Status2[Status2["MovedPermanently"] = 301] = "MovedPermanently"; | |
Status2[Status2["Found"] = 302] = "Found"; | |
Status2[Status2["SeeOther"] = 303] = "SeeOther"; | |
Status2[Status2["NotModified"] = 304] = "NotModified"; | |
Status2[Status2["UseProxy"] = 305] = "UseProxy"; | |
Status2[Status2["TemporaryRedirect"] = 307] = "TemporaryRedirect"; | |
Status2[Status2["PermanentRedirect"] = 308] = "PermanentRedirect"; | |
Status2[Status2["BadRequest"] = 400] = "BadRequest"; | |
Status2[Status2["Unauthorized"] = 401] = "Unauthorized"; | |
Status2[Status2["PaymentRequired"] = 402] = "PaymentRequired"; | |
Status2[Status2["Forbidden"] = 403] = "Forbidden"; | |
Status2[Status2["NotFound"] = 404] = "NotFound"; | |
Status2[Status2["MethodNotAllowed"] = 405] = "MethodNotAllowed"; | |
Status2[Status2["NotAcceptable"] = 406] = "NotAcceptable"; | |
Status2[Status2["ProxyAuthRequired"] = 407] = "ProxyAuthRequired"; | |
Status2[Status2["RequestTimeout"] = 408] = "RequestTimeout"; | |
Status2[Status2["Conflict"] = 409] = "Conflict"; | |
Status2[Status2["Gone"] = 410] = "Gone"; | |
Status2[Status2["LengthRequired"] = 411] = "LengthRequired"; | |
Status2[Status2["PreconditionFailed"] = 412] = "PreconditionFailed"; | |
Status2[Status2["RequestEntityTooLarge"] = 413] = "RequestEntityTooLarge"; | |
Status2[Status2["RequestURITooLong"] = 414] = "RequestURITooLong"; | |
Status2[Status2["UnsupportedMediaType"] = 415] = "UnsupportedMediaType"; | |
Status2[Status2["RequestedRangeNotSatisfiable"] = 416] = "RequestedRangeNotSatisfiable"; | |
Status2[Status2["ExpectationFailed"] = 417] = "ExpectationFailed"; | |
Status2[Status2["Teapot"] = 418] = "Teapot"; | |
Status2[Status2["MisdirectedRequest"] = 421] = "MisdirectedRequest"; | |
Status2[Status2["UnprocessableEntity"] = 422] = "UnprocessableEntity"; | |
Status2[Status2["Locked"] = 423] = "Locked"; | |
Status2[Status2["FailedDependency"] = 424] = "FailedDependency"; | |
Status2[Status2["TooEarly"] = 425] = "TooEarly"; | |
Status2[Status2["UpgradeRequired"] = 426] = "UpgradeRequired"; | |
Status2[Status2["PreconditionRequired"] = 428] = "PreconditionRequired"; | |
Status2[Status2["TooManyRequests"] = 429] = "TooManyRequests"; | |
Status2[Status2["RequestHeaderFieldsTooLarge"] = 431] = "RequestHeaderFieldsTooLarge"; | |
Status2[Status2["UnavailableForLegalReasons"] = 451] = "UnavailableForLegalReasons"; | |
Status2[Status2["InternalServerError"] = 500] = "InternalServerError"; | |
Status2[Status2["NotImplemented"] = 501] = "NotImplemented"; | |
Status2[Status2["BadGateway"] = 502] = "BadGateway"; | |
Status2[Status2["ServiceUnavailable"] = 503] = "ServiceUnavailable"; | |
Status2[Status2["GatewayTimeout"] = 504] = "GatewayTimeout"; | |
Status2[Status2["HTTPVersionNotSupported"] = 505] = "HTTPVersionNotSupported"; | |
Status2[Status2["VariantAlsoNegotiates"] = 506] = "VariantAlsoNegotiates"; | |
Status2[Status2["InsufficientStorage"] = 507] = "InsufficientStorage"; | |
Status2[Status2["LoopDetected"] = 508] = "LoopDetected"; | |
Status2[Status2["NotExtended"] = 510] = "NotExtended"; | |
Status2[Status2["NetworkAuthenticationRequired"] = 511] = "NetworkAuthenticationRequired"; | |
})(Status || (Status = {})); | |
var STATUS_TEXT = new Map([ | |
[100, "Continue"], | |
[101, "Switching Protocols"], | |
[102, "Processing"], | |
[103, "Early Hints"], | |
[200, "OK"], | |
[201, "Created"], | |
[202, "Accepted"], | |
[203, "Non-Authoritative Information"], | |
[204, "No Content"], | |
[205, "Reset Content"], | |
[206, "Partial Content"], | |
[207, "Multi-Status"], | |
[208, "Already Reported"], | |
[226, "IM Used"], | |
[300, "Multiple Choices"], | |
[301, "Moved Permanently"], | |
[302, "Found"], | |
[303, "See Other"], | |
[304, "Not Modified"], | |
[305, "Use Proxy"], | |
[307, "Temporary Redirect"], | |
[308, "Permanent Redirect"], | |
[400, "Bad Request"], | |
[401, "Unauthorized"], | |
[402, "Payment Required"], | |
[403, "Forbidden"], | |
[404, "Not Found"], | |
[405, "Method Not Allowed"], | |
[406, "Not Acceptable"], | |
[407, "Proxy Authentication Required"], | |
[408, "Request Timeout"], | |
[409, "Conflict"], | |
[410, "Gone"], | |
[411, "Length Required"], | |
[412, "Precondition Failed"], | |
[413, "Request Entity Too Large"], | |
[414, "Request URI Too Long"], | |
[415, "Unsupported Media Type"], | |
[416, "Requested Range Not Satisfiable"], | |
[417, "Expectation Failed"], | |
[418, "I'm a teapot"], | |
[421, "Misdirected Request"], | |
[422, "Unprocessable Entity"], | |
[423, "Locked"], | |
[424, "Failed Dependency"], | |
[425, "Too Early"], | |
[426, "Upgrade Required"], | |
[428, "Precondition Required"], | |
[429, "Too Many Requests"], | |
[431, "Request Header Fields Too Large"], | |
[451, "Unavailable For Legal Reasons"], | |
[500, "Internal Server Error"], | |
[501, "Not Implemented"], | |
[502, "Bad Gateway"], | |
[503, "Service Unavailable"], | |
[504, "Gateway Timeout"], | |
[505, "HTTP Version Not Supported"], | |
[506, "Variant Also Negotiates"], | |
[507, "Insufficient Storage"], | |
[508, "Loop Detected"], | |
[510, "Not Extended"], | |
[511, "Network Authentication Required"] | |
]); | |
// http/_io.ts | |
function emptyReader() { | |
return { | |
read(_) { | |
return Promise.resolve(null); | |
} | |
}; | |
} | |
function bodyReader(contentLength, r) { | |
let totalRead = 0; | |
let finished = false; | |
async function read(buf) { | |
if (finished) | |
return null; | |
let result; | |
const remaining = contentLength - totalRead; | |
if (remaining >= buf.byteLength) { | |
result = await r.read(buf); | |
} else { | |
const readBuf = buf.subarray(0, remaining); | |
result = await r.read(readBuf); | |
} | |
if (result !== null) { | |
totalRead += result; | |
} | |
finished = totalRead === contentLength; | |
return result; | |
} | |
return {read}; | |
} | |
function chunkedBodyReader(h, r) { | |
const tp = new TextProtoReader(r); | |
let finished = false; | |
const chunks = []; | |
async function read(buf) { | |
if (finished) | |
return null; | |
const [chunk] = chunks; | |
if (chunk) { | |
const chunkRemaining = chunk.data.byteLength - chunk.offset; | |
const readLength = Math.min(chunkRemaining, buf.byteLength); | |
for (let i = 0; i < readLength; i++) { | |
buf[i] = chunk.data[chunk.offset + i]; | |
} | |
chunk.offset += readLength; | |
if (chunk.offset === chunk.data.byteLength) { | |
chunks.shift(); | |
if (await tp.readLine() === null) { | |
throw new Deno.errors.UnexpectedEof(); | |
} | |
} | |
return readLength; | |
} | |
const line = await tp.readLine(); | |
if (line === null) | |
throw new Deno.errors.UnexpectedEof(); | |
const [chunkSizeString] = line.split(";"); | |
const chunkSize = parseInt(chunkSizeString, 16); | |
if (Number.isNaN(chunkSize) || chunkSize < 0) { | |
throw new Deno.errors.InvalidData("Invalid chunk size"); | |
} | |
if (chunkSize > 0) { | |
if (chunkSize > buf.byteLength) { | |
let eof = await r.readFull(buf); | |
if (eof === null) { | |
throw new Deno.errors.UnexpectedEof(); | |
} | |
const restChunk = new Uint8Array(chunkSize - buf.byteLength); | |
eof = await r.readFull(restChunk); | |
if (eof === null) { | |
throw new Deno.errors.UnexpectedEof(); | |
} else { | |
chunks.push({ | |
offset: 0, | |
data: restChunk | |
}); | |
} | |
return buf.byteLength; | |
} else { | |
const bufToFill = buf.subarray(0, chunkSize); | |
const eof = await r.readFull(bufToFill); | |
if (eof === null) { | |
throw new Deno.errors.UnexpectedEof(); | |
} | |
if (await tp.readLine() === null) { | |
throw new Deno.errors.UnexpectedEof(); | |
} | |
return chunkSize; | |
} | |
} else { | |
assert(chunkSize === 0); | |
if (await r.readLine() === null) { | |
throw new Deno.errors.UnexpectedEof(); | |
} | |
await readTrailers(h, r); | |
finished = true; | |
return null; | |
} | |
} | |
return {read}; | |
} | |
function isProhibidedForTrailer(key) { | |
const s = new Set(["transfer-encoding", "content-length", "trailer"]); | |
return s.has(key.toLowerCase()); | |
} | |
async function readTrailers(headers, r) { | |
const trailers = parseTrailer(headers.get("trailer")); | |
if (trailers == null) | |
return; | |
const trailerNames = [...trailers.keys()]; | |
const tp = new TextProtoReader(r); | |
const result = await tp.readMIMEHeader(); | |
if (result == null) { | |
throw new Deno.errors.InvalidData("Missing trailer header."); | |
} | |
const undeclared = [...result.keys()].filter((k) => !trailerNames.includes(k)); | |
if (undeclared.length > 0) { | |
throw new Deno.errors.InvalidData(`Undeclared trailers: ${Deno.inspect(undeclared)}.`); | |
} | |
for (const [k, v] of result) { | |
headers.append(k, v); | |
} | |
const missingTrailers = trailerNames.filter((k) => !result.has(k)); | |
if (missingTrailers.length > 0) { | |
throw new Deno.errors.InvalidData(`Missing trailers: ${Deno.inspect(missingTrailers)}.`); | |
} | |
headers.delete("trailer"); | |
} | |
function parseTrailer(field) { | |
if (field == null) { | |
return void 0; | |
} | |
const trailerNames = field.split(",").map((v) => v.trim().toLowerCase()); | |
if (trailerNames.length === 0) { | |
throw new Deno.errors.InvalidData("Empty trailer header."); | |
} | |
const prohibited = trailerNames.filter((k) => isProhibidedForTrailer(k)); | |
if (prohibited.length > 0) { | |
throw new Deno.errors.InvalidData(`Prohibited trailer names: ${Deno.inspect(prohibited)}.`); | |
} | |
return new Headers(trailerNames.map((key) => [key, ""])); | |
} | |
async function writeChunkedBody(w, r) { | |
const encoder2 = new TextEncoder(); | |
for await (const chunk of Deno.iter(r)) { | |
if (chunk.byteLength <= 0) | |
continue; | |
const start = encoder2.encode(`${chunk.byteLength.toString(16)}\r | |
`); | |
const end = encoder2.encode("\r\n"); | |
await w.write(start); | |
await w.write(chunk); | |
await w.write(end); | |
await w.flush(); | |
} | |
const endChunk = encoder2.encode("0\r\n\r\n"); | |
await w.write(endChunk); | |
} | |
async function writeTrailers(w, headers, trailers) { | |
const trailer = headers.get("trailer"); | |
if (trailer === null) { | |
throw new TypeError("Missing trailer header."); | |
} | |
const transferEncoding = headers.get("transfer-encoding"); | |
if (transferEncoding === null || !transferEncoding.match(/^chunked/)) { | |
throw new TypeError(`Trailers are only allowed for "transfer-encoding: chunked", got "transfer-encoding: ${transferEncoding}".`); | |
} | |
const writer = BufWriter.create(w); | |
const trailerNames = trailer.split(",").map((s) => s.trim().toLowerCase()); | |
const prohibitedTrailers = trailerNames.filter((k) => isProhibidedForTrailer(k)); | |
if (prohibitedTrailers.length > 0) { | |
throw new TypeError(`Prohibited trailer names: ${Deno.inspect(prohibitedTrailers)}.`); | |
} | |
const undeclared = [...trailers.keys()].filter((k) => !trailerNames.includes(k)); | |
if (undeclared.length > 0) { | |
throw new TypeError(`Undeclared trailers: ${Deno.inspect(undeclared)}.`); | |
} | |
const encoder2 = new TextEncoder(); | |
for (const [key, value] of trailers) { | |
await writer.write(encoder2.encode(`${key}: ${value}\r | |
`)); | |
} | |
await writer.write(encoder2.encode("\r\n")); | |
await writer.flush(); | |
} | |
async function writeResponse(w, r) { | |
const protoMajor = 1; | |
const protoMinor = 1; | |
const statusCode = r.status || 200; | |
const statusText = STATUS_TEXT.get(statusCode); | |
const writer = BufWriter.create(w); | |
const encoder2 = new TextEncoder(); | |
if (!statusText) { | |
throw new Deno.errors.InvalidData("Bad status code"); | |
} | |
if (!r.body) { | |
r.body = new Uint8Array(); | |
} | |
if (typeof r.body === "string") { | |
r.body = encoder2.encode(r.body); | |
} | |
let out = `HTTP/${protoMajor}.${protoMinor} ${statusCode} ${statusText}\r | |
`; | |
const headers = r.headers ?? new Headers(); | |
if (r.body && !headers.get("content-length")) { | |
if (r.body instanceof Uint8Array) { | |
out += `content-length: ${r.body.byteLength}\r | |
`; | |
} else if (!headers.get("transfer-encoding")) { | |
out += "transfer-encoding: chunked\r\n"; | |
} | |
} | |
for (const [key, value] of headers) { | |
out += `${key}: ${value}\r | |
`; | |
} | |
out += `\r | |
`; | |
const header = encoder2.encode(out); | |
const n = await writer.write(header); | |
assert(n === header.byteLength); | |
if (r.body instanceof Uint8Array) { | |
const n2 = await writer.write(r.body); | |
assert(n2 === r.body.byteLength); | |
} else if (headers.has("content-length")) { | |
const contentLength = headers.get("content-length"); | |
assert(contentLength != null); | |
const bodyLength = parseInt(contentLength); | |
const n2 = await Deno.copy(r.body, writer); | |
assert(n2 === bodyLength); | |
} else { | |
await writeChunkedBody(writer, r.body); | |
} | |
if (r.trailers) { | |
const t = await r.trailers(); | |
await writeTrailers(writer, headers, t); | |
} | |
await writer.flush(); | |
} | |
function parseHTTPVersion(vers) { | |
switch (vers) { | |
case "HTTP/1.1": | |
return [1, 1]; | |
case "HTTP/1.0": | |
return [1, 0]; | |
default: { | |
const Big = 1e6; | |
if (!vers.startsWith("HTTP/")) { | |
break; | |
} | |
const dot = vers.indexOf("."); | |
if (dot < 0) { | |
break; | |
} | |
const majorStr = vers.substring(vers.indexOf("/") + 1, dot); | |
const major = Number(majorStr); | |
if (!Number.isInteger(major) || major < 0 || major > Big) { | |
break; | |
} | |
const minorStr = vers.substring(dot + 1); | |
const minor = Number(minorStr); | |
if (!Number.isInteger(minor) || minor < 0 || minor > Big) { | |
break; | |
} | |
return [major, minor]; | |
} | |
} | |
throw new Error(`malformed HTTP version ${vers}`); | |
} | |
async function readRequest(conn, bufr) { | |
const tp = new TextProtoReader(bufr); | |
const firstLine = await tp.readLine(); | |
if (firstLine === null) | |
return null; | |
const headers = await tp.readMIMEHeader(); | |
if (headers === null) | |
throw new Deno.errors.UnexpectedEof(); | |
const req = new ServerRequest(); | |
req.conn = conn; | |
req.r = bufr; | |
[req.method, req.url, req.proto] = firstLine.split(" ", 3); | |
[req.protoMajor, req.protoMinor] = parseHTTPVersion(req.proto); | |
req.headers = headers; | |
fixLength(req); | |
return req; | |
} | |
function fixLength(req) { | |
const contentLength = req.headers.get("Content-Length"); | |
if (contentLength) { | |
const arrClen = contentLength.split(","); | |
if (arrClen.length > 1) { | |
const distinct = [...new Set(arrClen.map((e) => e.trim()))]; | |
if (distinct.length > 1) { | |
throw Error("cannot contain multiple Content-Length headers"); | |
} else { | |
req.headers.set("Content-Length", distinct[0]); | |
} | |
} | |
const c = req.headers.get("Content-Length"); | |
if (req.method === "HEAD" && c && c !== "0") { | |
throw Error("http: method cannot contain a Content-Length"); | |
} | |
if (c && req.headers.has("transfer-encoding")) { | |
throw new Error("http: Transfer-Encoding and Content-Length cannot be send together"); | |
} | |
} | |
} | |
// http/server.ts | |
var ServerRequest = class { | |
constructor() { | |
this.#done = deferred(); | |
this.#contentLength = void 0; | |
this.#body = void 0; | |
this.#finalized = false; | |
} | |
#done; | |
#contentLength; | |
#body; | |
#finalized; | |
get done() { | |
return this.#done.then((e) => e); | |
} | |
get contentLength() { | |
if (this.#contentLength === void 0) { | |
const cl = this.headers.get("content-length"); | |
if (cl) { | |
this.#contentLength = parseInt(cl); | |
if (Number.isNaN(this.#contentLength)) { | |
this.#contentLength = null; | |
} | |
} else { | |
this.#contentLength = null; | |
} | |
} | |
return this.#contentLength; | |
} | |
get body() { | |
if (!this.#body) { | |
if (this.contentLength != null) { | |
this.#body = bodyReader(this.contentLength, this.r); | |
} else { | |
const transferEncoding = this.headers.get("transfer-encoding"); | |
if (transferEncoding != null) { | |
const parts = transferEncoding.split(",").map((e) => e.trim().toLowerCase()); | |
assert(parts.includes("chunked"), 'transfer-encoding must include "chunked" if content-length is not set'); | |
this.#body = chunkedBodyReader(this.headers, this.r); | |
} else { | |
this.#body = emptyReader(); | |
} | |
} | |
} | |
return this.#body; | |
} | |
async respond(r) { | |
let err; | |
try { | |
await writeResponse(this.w, r); | |
} catch (e) { | |
try { | |
this.conn.close(); | |
} catch { | |
} | |
err = e; | |
} | |
this.#done.resolve(err); | |
if (err) { | |
throw err; | |
} | |
} | |
async finalize() { | |
if (this.#finalized) | |
return; | |
const body = this.body; | |
const buf = new Uint8Array(1024); | |
while (await body.read(buf) !== null) { | |
} | |
this.#finalized = true; | |
} | |
}; | |
var Server = class { | |
constructor(listener) { | |
this.listener = listener; | |
this.#closing = false; | |
this.#connections = []; | |
} | |
#closing; | |
#connections; | |
close() { | |
this.#closing = true; | |
this.listener.close(); | |
for (const conn of this.#connections) { | |
try { | |
conn.close(); | |
} catch (e) { | |
if (!(e instanceof Deno.errors.BadResource)) { | |
throw e; | |
} | |
} | |
} | |
} | |
async *iterateHttpRequests(conn) { | |
const reader = new BufReader(conn); | |
const writer = new BufWriter(conn); | |
while (!this.#closing) { | |
let request; | |
try { | |
request = await readRequest(conn, reader); | |
} catch (error) { | |
if (error instanceof Deno.errors.InvalidData || error instanceof Deno.errors.UnexpectedEof) { | |
try { | |
await writeResponse(writer, { | |
status: 400, | |
body: new TextEncoder().encode(`${error.message}\r | |
\r | |
`) | |
}); | |
} catch (error2) { | |
} | |
} | |
break; | |
} | |
if (request === null) { | |
break; | |
} | |
request.w = writer; | |
yield request; | |
const responseError = await request.done; | |
if (responseError) { | |
this.untrackConnection(request.conn); | |
return; | |
} | |
try { | |
await request.finalize(); | |
} catch (error) { | |
break; | |
} | |
} | |
this.untrackConnection(conn); | |
try { | |
conn.close(); | |
} catch (e) { | |
} | |
} | |
trackConnection(conn) { | |
this.#connections.push(conn); | |
} | |
untrackConnection(conn) { | |
const index = this.#connections.indexOf(conn); | |
if (index !== -1) { | |
this.#connections.splice(index, 1); | |
} | |
} | |
async *acceptConnAndIterateHttpRequests(mux) { | |
if (this.#closing) | |
return; | |
let conn; | |
try { | |
conn = await this.listener.accept(); | |
} catch (error) { | |
if (error instanceof Deno.errors.BadResource || error instanceof Deno.errors.InvalidData || error instanceof Deno.errors.UnexpectedEof || error instanceof Deno.errors.ConnectionReset) { | |
return mux.add(this.acceptConnAndIterateHttpRequests(mux)); | |
} | |
throw error; | |
} | |
this.trackConnection(conn); | |
mux.add(this.acceptConnAndIterateHttpRequests(mux)); | |
yield* this.iterateHttpRequests(conn); | |
} | |
[Symbol.asyncIterator]() { | |
const mux = new MuxAsyncIterator(); | |
mux.add(this.acceptConnAndIterateHttpRequests(mux)); | |
return mux.iterate(); | |
} | |
}; | |
function _parseAddrFromStr(addr) { | |
let url; | |
try { | |
const host = addr.startsWith(":") ? `0.0.0.0${addr}` : addr; | |
url = new URL(`http://${host}`); | |
} catch { | |
throw new TypeError("Invalid address."); | |
} | |
if (url.username || url.password || url.pathname != "/" || url.search || url.hash) { | |
throw new TypeError("Invalid address."); | |
} | |
return { | |
hostname: url.hostname, | |
port: url.port === "" ? 80 : Number(url.port) | |
}; | |
} | |
function serve(addr) { | |
if (typeof addr === "string") { | |
addr = _parseAddrFromStr(addr); | |
} | |
const listener = Deno.listen(addr); | |
return new Server(listener); | |
} | |
async function listenAndServe(addr, handler) { | |
const server = serve(addr); | |
for await (const request of server) { | |
handler(request); | |
} | |
} | |
function serveTLS(options) { | |
const tlsOptions = { | |
...options, | |
transport: "tcp" | |
}; | |
const listener = Deno.listenTls(tlsOptions); | |
return new Server(listener); | |
} | |
async function listenAndServeTLS(options, handler) { | |
const server = serveTLS(options); | |
for await (const request of server) { | |
handler(request); | |
} | |
} | |
// flags/mod.ts | |
function get(obj, key) { | |
if (Object.prototype.hasOwnProperty.call(obj, key)) { | |
return obj[key]; | |
} | |
} | |
function getForce(obj, key) { | |
const v = get(obj, key); | |
assert(v != null); | |
return v; | |
} | |
function isNumber(x) { | |
if (typeof x === "number") | |
return true; | |
if (/^0x[0-9a-f]+$/i.test(String(x))) | |
return true; | |
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(String(x)); | |
} | |
function hasKey(obj, keys) { | |
let o = obj; | |
keys.slice(0, -1).forEach((key2) => { | |
o = get(o, key2) ?? {}; | |
}); | |
const key = keys[keys.length - 1]; | |
return key in o; | |
} | |
function parse4(args, { | |
"--": doubleDash = false, | |
alias = {}, | |
boolean = false, | |
default: defaults = {}, | |
stopEarly = false, | |
string = [], | |
unknown = (i) => i | |
} = {}) { | |
const flags = { | |
bools: {}, | |
strings: {}, | |
unknownFn: unknown, | |
allBools: false | |
}; | |
if (boolean !== void 0) { | |
if (typeof boolean === "boolean") { | |
flags.allBools = !!boolean; | |
} else { | |
const booleanArgs = typeof boolean === "string" ? [boolean] : boolean; | |
for (const key of booleanArgs.filter(Boolean)) { | |
flags.bools[key] = true; | |
} | |
} | |
} | |
const aliases = {}; | |
if (alias !== void 0) { | |
for (const key in alias) { | |
const val = getForce(alias, key); | |
if (typeof val === "string") { | |
aliases[key] = [val]; | |
} else { | |
aliases[key] = val; | |
} | |
for (const alias2 of getForce(aliases, key)) { | |
aliases[alias2] = [key].concat(aliases[key].filter((y) => alias2 !== y)); | |
} | |
} | |
} | |
if (string !== void 0) { | |
const stringArgs = typeof string === "string" ? [string] : string; | |
for (const key of stringArgs.filter(Boolean)) { | |
flags.strings[key] = true; | |
const alias2 = get(aliases, key); | |
if (alias2) { | |
for (const al of alias2) { | |
flags.strings[al] = true; | |
} | |
} | |
} | |
} | |
const argv = {_: []}; | |
function argDefined(key, arg) { | |
return flags.allBools && /^--[^=]+$/.test(arg) || get(flags.bools, key) || !!get(flags.strings, key) || !!get(aliases, key); | |
} | |
function setKey(obj, keys, value) { | |
let o = obj; | |
keys.slice(0, -1).forEach(function(key2) { | |
if (get(o, key2) === void 0) { | |
o[key2] = {}; | |
} | |
o = get(o, key2); | |
}); | |
const key = keys[keys.length - 1]; | |
if (get(o, key) === void 0 || get(flags.bools, key) || typeof get(o, key) === "boolean") { | |
o[key] = value; | |
} else if (Array.isArray(get(o, key))) { | |
o[key].push(value); | |
} else { | |
o[key] = [get(o, key), value]; | |
} | |
} | |
function setArg(key, val, arg = void 0) { | |
if (arg && flags.unknownFn && !argDefined(key, arg)) { | |
if (flags.unknownFn(arg, key, val) === false) | |
return; | |
} | |
const value = !get(flags.strings, key) && isNumber(val) ? Number(val) : val; | |
setKey(argv, key.split("."), value); | |
const alias2 = get(aliases, key); | |
if (alias2) { | |
for (const x of alias2) { | |
setKey(argv, x.split("."), value); | |
} | |
} | |
} | |
function aliasIsBoolean(key) { | |
return getForce(aliases, key).some((x) => typeof get(flags.bools, x) === "boolean"); | |
} | |
for (const key of Object.keys(flags.bools)) { | |
setArg(key, defaults[key] === void 0 ? false : defaults[key]); | |
} | |
let notFlags = []; | |
if (args.includes("--")) { | |
notFlags = args.slice(args.indexOf("--") + 1); | |
args = args.slice(0, args.indexOf("--")); | |
} | |
for (let i = 0; i < args.length; i++) { | |
const arg = args[i]; | |
if (/^--.+=/.test(arg)) { | |
const m = arg.match(/^--([^=]+)=(.*)$/s); | |
assert(m != null); | |
const [, key, value] = m; | |
if (flags.bools[key]) { | |
const booleanValue = value !== "false"; | |
setArg(key, booleanValue, arg); | |
} else { | |
setArg(key, value, arg); | |
} | |
} else if (/^--no-.+/.test(arg)) { | |
const m = arg.match(/^--no-(.+)/); | |
assert(m != null); | |
setArg(m[1], false, arg); | |
} else if (/^--.+/.test(arg)) { | |
const m = arg.match(/^--(.+)/); | |
assert(m != null); | |
const [, key] = m; | |
const next = args[i + 1]; | |
if (next !== void 0 && !/^-/.test(next) && !get(flags.bools, key) && !flags.allBools && (get(aliases, key) ? !aliasIsBoolean(key) : true)) { | |
setArg(key, next, arg); | |
i++; | |
} else if (/^(true|false)$/.test(next)) { | |
setArg(key, next === "true", arg); | |
i++; | |
} else { | |
setArg(key, get(flags.strings, key) ? "" : true, arg); | |
} | |
} else if (/^-[^-]+/.test(arg)) { | |
const letters = arg.slice(1, -1).split(""); | |
let broken = false; | |
for (let j = 0; j < letters.length; j++) { | |
const next = arg.slice(j + 2); | |
if (next === "-") { | |
setArg(letters[j], next, arg); | |
continue; | |
} | |
if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { | |
setArg(letters[j], next.split(/=(.+)/)[1], arg); | |
broken = true; | |
break; | |
} | |
if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { | |
setArg(letters[j], next, arg); | |
broken = true; | |
break; | |
} | |
if (letters[j + 1] && letters[j + 1].match(/\W/)) { | |
setArg(letters[j], arg.slice(j + 2), arg); | |
broken = true; | |
break; | |
} else { | |
setArg(letters[j], get(flags.strings, letters[j]) ? "" : true, arg); | |
} | |
} | |
const [key] = arg.slice(-1); | |
if (!broken && key !== "-") { | |
if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1]) && !get(flags.bools, key) && (get(aliases, key) ? !aliasIsBoolean(key) : true)) { | |
setArg(key, args[i + 1], arg); | |
i++; | |
} else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) { | |
setArg(key, args[i + 1] === "true", arg); | |
i++; | |
} else { | |
setArg(key, get(flags.strings, key) ? "" : true, arg); | |
} | |
} | |
} else { | |
if (!flags.unknownFn || flags.unknownFn(arg) !== false) { | |
argv._.push(flags.strings["_"] ?? !isNumber(arg) ? arg : Number(arg)); | |
} | |
if (stopEarly) { | |
argv._.push(...args.slice(i + 1)); | |
break; | |
} | |
} | |
} | |
for (const key of Object.keys(defaults)) { | |
if (!hasKey(argv, key.split("."))) { | |
setKey(argv, key.split("."), defaults[key]); | |
if (aliases[key]) { | |
for (const x of aliases[key]) { | |
setKey(argv, x.split("."), defaults[key]); | |
} | |
} | |
} | |
} | |
if (doubleDash) { | |
argv["--"] = []; | |
for (const key of notFlags) { | |
argv["--"].push(key); | |
} | |
} else { | |
for (const key of notFlags) { | |
argv._.push(key); | |
} | |
} | |
return argv; | |
} | |
// http/file_server.ts | |
var import_meta = {}; | |
var encoder = new TextEncoder(); | |
var serverArgs = parse4(Deno.args); | |
var target = posix.resolve(serverArgs._[0] ?? ""); | |
var MEDIA_TYPES = { | |
".md": "text/markdown", | |
".html": "text/html", | |
".htm": "text/html", | |
".json": "application/json", | |
".map": "application/json", | |
".txt": "text/plain", | |
".ts": "text/typescript", | |
".tsx": "text/tsx", | |
".js": "application/javascript", | |
".jsx": "text/jsx", | |
".gz": "application/gzip", | |
".css": "text/css", | |
".wasm": "application/wasm", | |
".mjs": "application/javascript", | |
".svg": "image/svg+xml" | |
}; | |
function contentType(path2) { | |
return MEDIA_TYPES[extname3(path2)]; | |
} | |
function modeToString(isDir, maybeMode) { | |
const modeMap = ["---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"]; | |
if (maybeMode === null) { | |
return "(unknown mode)"; | |
} | |
const mode = maybeMode.toString(8); | |
if (mode.length < 3) { | |
return "(unknown mode)"; | |
} | |
let output = ""; | |
mode.split("").reverse().slice(0, 3).forEach((v) => { | |
output = modeMap[+v] + output; | |
}); | |
output = `(${isDir ? "d" : "-"}${output})`; | |
return output; | |
} | |
function fileLenToString(len) { | |
const multiplier = 1024; | |
let base = 1; | |
const suffix = ["B", "K", "M", "G", "T"]; | |
let suffixIndex = 0; | |
while (base * multiplier < len) { | |
if (suffixIndex >= suffix.length - 1) { | |
break; | |
} | |
base *= multiplier; | |
suffixIndex++; | |
} | |
return `${(len / base).toFixed(2)}${suffix[suffixIndex]}`; | |
} | |
async function serveFile(req, filePath) { | |
const [file, fileInfo] = await Promise.all([ | |
Deno.open(filePath), | |
Deno.stat(filePath) | |
]); | |
const headers = new Headers(); | |
headers.set("content-length", fileInfo.size.toString()); | |
const contentTypeValue = contentType(filePath); | |
if (contentTypeValue) { | |
headers.set("content-type", contentTypeValue); | |
} | |
req.done.then(() => { | |
file.close(); | |
}); | |
return { | |
status: 200, | |
body: file, | |
headers | |
}; | |
} | |
async function serveDir(req, dirPath) { | |
const showDotfiles = serverArgs.dotfiles ?? true; | |
const dirUrl = `/${posix.relative(target, dirPath)}`; | |
const listEntry = []; | |
if (dirUrl !== "/") { | |
const prevPath = posix.join(dirPath, ".."); | |
const fileInfo = await Deno.stat(prevPath); | |
listEntry.push({ | |
mode: modeToString(true, fileInfo.mode), | |
size: "", | |
name: "../", | |
url: posix.join(dirUrl, "..") | |
}); | |
} | |
for await (const entry of Deno.readDir(dirPath)) { | |
if (!showDotfiles && entry.name[0] === ".") { | |
continue; | |
} | |
const filePath = posix.join(dirPath, entry.name); | |
const fileUrl = posix.join(dirUrl, entry.name); | |
if (entry.name === "index.html" && entry.isFile) { | |
return serveFile(req, filePath); | |
} | |
const fileInfo = await Deno.stat(filePath); | |
listEntry.push({ | |
mode: modeToString(entry.isDirectory, fileInfo.mode), | |
size: entry.isFile ? fileLenToString(fileInfo.size ?? 0) : "", | |
name: `${entry.name}${entry.isDirectory ? "/" : ""}`, | |
url: fileUrl | |
}); | |
} | |
listEntry.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1); | |
const formattedDirUrl = `${dirUrl.replace(/\/$/, "")}/`; | |
const page = encoder.encode(dirViewerTemplate(formattedDirUrl, listEntry)); | |
const headers = new Headers(); | |
headers.set("content-type", "text/html"); | |
const res = { | |
status: 200, | |
body: page, | |
headers | |
}; | |
return res; | |
} | |
function serveFallback(req, e) { | |
if (e instanceof URIError) { | |
return Promise.resolve({ | |
status: 400, | |
body: encoder.encode("Bad Request") | |
}); | |
} else if (e instanceof Deno.errors.NotFound) { | |
return Promise.resolve({ | |
status: 404, | |
body: encoder.encode("Not Found") | |
}); | |
} else { | |
return Promise.resolve({ | |
status: 500, | |
body: encoder.encode("Internal server error") | |
}); | |
} | |
} | |
function serverLog(req, res) { | |
const d = new Date().toISOString(); | |
const dateFmt = `[${d.slice(0, 10)} ${d.slice(11, 19)}]`; | |
const s = `${dateFmt} "${req.method} ${req.url} ${req.proto}" ${res.status}`; | |
console.log(s); | |
} | |
function setCORS(res) { | |
if (!res.headers) { | |
res.headers = new Headers(); | |
} | |
res.headers.append("access-control-allow-origin", "*"); | |
res.headers.append("access-control-allow-headers", "Origin, X-Requested-With, Content-Type, Accept, Range"); | |
} | |
function dirViewerTemplate(dirname4, entries) { | |
return html` | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | |
<title>Deno File Server</title> | |
<style> | |
:root { | |
--background-color: #fafafa; | |
--color: rgba(0, 0, 0, 0.87); | |
} | |
@media (prefers-color-scheme: dark) { | |
:root { | |
--background-color: #303030; | |
--color: #fff; | |
} | |
} | |
@media (min-width: 960px) { | |
main { | |
max-width: 960px; | |
} | |
body { | |
padding-left: 32px; | |
padding-right: 32px; | |
} | |
} | |
@media (min-width: 600px) { | |
main { | |
padding-left: 24px; | |
padding-right: 24px; | |
} | |
} | |
body { | |
background: var(--background-color); | |
color: var(--color); | |
font-family: "Roboto", "Helvetica", "Arial", sans-serif; | |
font-weight: 400; | |
line-height: 1.43; | |
font-size: 0.875rem; | |
} | |
a { | |
color: #2196f3; | |
text-decoration: none; | |
} | |
a:hover { | |
text-decoration: underline; | |
} | |
table th { | |
text-align: left; | |
} | |
table td { | |
padding: 12px 24px 0 0; | |
} | |
</style> | |
</head> | |
<body> | |
<main> | |
<h1>Index of ${dirname4}</h1> | |
<table> | |
<tr> | |
<th>Mode</th> | |
<th>Size</th> | |
<th>Name</th> | |
</tr> | |
${entries.map((entry) => html` | |
<tr> | |
<td class="mode"> | |
${entry.mode} | |
</td> | |
<td> | |
${entry.size} | |
</td> | |
<td> | |
<a href="${entry.url}">${entry.name}</a> | |
</td> | |
</tr> | |
`)} | |
</table> | |
</main> | |
</body> | |
</html> | |
`; | |
} | |
function html(strings, ...values) { | |
const l = strings.length - 1; | |
let html2 = ""; | |
for (let i = 0; i < l; i++) { | |
let v = values[i]; | |
if (v instanceof Array) { | |
v = v.join(""); | |
} | |
const s = strings[i] + v; | |
html2 += s; | |
} | |
html2 += strings[l]; | |
return html2; | |
} | |
function normalizeURL(url) { | |
let normalizedUrl = url; | |
try { | |
normalizedUrl = decodeURI(normalizedUrl); | |
} catch (e) { | |
if (!(e instanceof URIError)) { | |
throw e; | |
} | |
} | |
try { | |
const absoluteURI = new URL(normalizedUrl); | |
normalizedUrl = absoluteURI.pathname; | |
} catch (e) { | |
if (!(e instanceof TypeError)) { | |
throw e; | |
} | |
} | |
if (normalizedUrl[0] !== "/") { | |
throw new URIError("The request URI is malformed."); | |
} | |
normalizedUrl = posix.normalize(normalizedUrl); | |
const startOfParams = normalizedUrl.indexOf("?"); | |
return startOfParams > -1 ? normalizedUrl.slice(0, startOfParams) : normalizedUrl; | |
} | |
function main() { | |
const CORSEnabled = serverArgs.cors ? true : false; | |
const port = serverArgs.port ?? serverArgs.p ?? 4507; | |
const host = serverArgs.host ?? "0.0.0.0"; | |
const addr = `${host}:${port}`; | |
const tlsOpts = {}; | |
tlsOpts.certFile = serverArgs.cert ?? serverArgs.c ?? ""; | |
tlsOpts.keyFile = serverArgs.key ?? serverArgs.k ?? ""; | |
const dirListingEnabled = serverArgs["dir-listing"] ?? true; | |
if (tlsOpts.keyFile || tlsOpts.certFile) { | |
if (tlsOpts.keyFile === "" || tlsOpts.certFile === "") { | |
console.log("--key and --cert are required for TLS"); | |
serverArgs.h = true; | |
} | |
} | |
if (serverArgs.h ?? serverArgs.help) { | |
console.log(`Deno File Server | |
Serves a local directory in HTTP. | |
INSTALL: | |
deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts | |
USAGE: | |
file_server [path] [options] | |
OPTIONS: | |
-h, --help Prints help information | |
-p, --port <PORT> Set port | |
--cors Enable CORS via the "Access-Control-Allow-Origin" header | |
--host <HOST> Hostname (default is 0.0.0.0) | |
-c, --cert <FILE> TLS certificate file (enables TLS) | |
-k, --key <FILE> TLS key file (enables TLS) | |
--no-dir-listing Disable directory listing | |
--no-dotfiles Do not show dotfiles | |
All TLS options are required when one is provided.`); | |
Deno.exit(); | |
} | |
const handler = async (req) => { | |
let response; | |
try { | |
const normalizedUrl = normalizeURL(req.url); | |
let fsPath = posix.join(target, normalizedUrl); | |
if (fsPath.indexOf(target) !== 0) { | |
fsPath = target; | |
} | |
const fileInfo = await Deno.stat(fsPath); | |
if (fileInfo.isDirectory) { | |
if (dirListingEnabled) { | |
response = await serveDir(req, fsPath); | |
} else { | |
throw new Deno.errors.NotFound(); | |
} | |
} else { | |
response = await serveFile(req, fsPath); | |
} | |
} catch (e) { | |
console.error(e.message); | |
response = await serveFallback(req, e); | |
} finally { | |
if (CORSEnabled) { | |
assert(response); | |
setCORS(response); | |
} | |
serverLog(req, response); | |
try { | |
await req.respond(response); | |
} catch (e) { | |
console.error(e.message); | |
} | |
} | |
}; | |
let proto = "http"; | |
if (tlsOpts.keyFile || tlsOpts.certFile) { | |
proto += "s"; | |
tlsOpts.hostname = host; | |
tlsOpts.port = port; | |
listenAndServeTLS(tlsOpts, handler); | |
} else { | |
listenAndServe(addr, handler); | |
} | |
console.log(`${proto.toUpperCase()} server listening on ${proto}://${addr}/`); | |
} | |
// with the if statement uncommented, `deno run` simply exits | |
// if (import_meta.main) { | |
main(); | |
// } | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment