elysia-remix
Version:
Use React Router v7 or Remix with Elysia with HMR support!
810 lines (802 loc) • 17.3 kB
JavaScript
import { Elysia, NotFoundError } from 'elysia';
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
var fastDecodeUriComponent;
var hasRequiredFastDecodeUriComponent;
function requireFastDecodeUriComponent () {
if (hasRequiredFastDecodeUriComponent) return fastDecodeUriComponent;
hasRequiredFastDecodeUriComponent = 1;
var UTF8_ACCEPT = 12;
var UTF8_REJECT = 0;
var UTF8_DATA = [
// The first part of the table maps bytes to character to a transition.
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
4,
4,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
6,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
7,
8,
7,
7,
10,
9,
9,
9,
11,
4,
4,
4,
4,
4,
4,
4,
4,
4,
4,
4,
// The second part of the table maps a state to a new state when adding a
// transition.
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
12,
0,
0,
0,
0,
24,
36,
48,
60,
72,
84,
96,
0,
12,
12,
12,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
24,
0,
0,
0,
0,
0,
0,
0,
0,
0,
24,
24,
24,
0,
0,
0,
0,
0,
0,
0,
0,
0,
24,
24,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
48,
48,
48,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
48,
48,
0,
0,
0,
0,
0,
0,
0,
0,
0,
48,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
// The third part maps the current transition to a mask that needs to apply
// to the byte.
127,
63,
63,
63,
0,
31,
15,
15,
15,
7,
7,
7
];
function decodeURIComponent(uri) {
var percentPosition = uri.indexOf("%");
if (percentPosition === -1) return uri;
var length = uri.length;
var decoded = "";
var last = 0;
var codepoint = 0;
var startOfOctets = percentPosition;
var state = UTF8_ACCEPT;
while (percentPosition > -1 && percentPosition < length) {
var high = hexCodeToInt(uri[percentPosition + 1], 4);
var low = hexCodeToInt(uri[percentPosition + 2], 0);
var byte = high | low;
var type = UTF8_DATA[byte];
state = UTF8_DATA[256 + state + type];
codepoint = codepoint << 6 | byte & UTF8_DATA[364 + type];
if (state === UTF8_ACCEPT) {
decoded += uri.slice(last, startOfOctets);
decoded += codepoint <= 65535 ? String.fromCharCode(codepoint) : String.fromCharCode(
55232 + (codepoint >> 10),
56320 + (codepoint & 1023)
);
codepoint = 0;
last = percentPosition + 3;
percentPosition = startOfOctets = uri.indexOf("%", last);
} else if (state === UTF8_REJECT) {
return null;
} else {
percentPosition += 3;
if (percentPosition < length && uri.charCodeAt(percentPosition) === 37) continue;
return null;
}
}
return decoded + uri.slice(last);
}
var HEX = {
"0": 0,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"a": 10,
"A": 10,
"b": 11,
"B": 11,
"c": 12,
"C": 12,
"d": 13,
"D": 13,
"e": 14,
"E": 14,
"f": 15,
"F": 15
};
function hexCodeToInt(c, shift) {
var i = HEX[c];
return i === void 0 ? 255 : i << shift;
}
fastDecodeUriComponent = decodeURIComponent;
return fastDecodeUriComponent;
}
var fastDecodeUriComponentExports = requireFastDecodeUriComponent();
var fastDecodeURI = /*@__PURE__*/getDefaultExportFromCjs(fastDecodeUriComponentExports);
let fs, path;
const isBun = typeof Bun < "u" && !!Bun.file;
function getBuiltinModule() {
if (fs || (fs = process.getBuiltinModule("fs/promises")), path || (path = process.getBuiltinModule("path")), !path) {
console.warn("@elysiajs/static require path to be available.");
return;
}
return [fs, path];
}
async function listHTMLFiles(dir) {
if (fs || getBuiltinModule(), isBun) {
const glob = new Bun.Glob("**/*.html"), files = [];
for await (const file of glob.scan(dir))
files.push(path.join(dir, file));
return files;
}
return [];
}
async function listFiles(dir) {
if (fs || getBuiltinModule(), isBun) {
const glob = new Bun.Glob("**/*"), files2 = [];
for await (const file of glob.scan(dir))
files2.push(path.join(dir, file));
return files2;
}
const files = await fs.readdir(dir).catch(() => []);
return (await Promise.all(
files.map(async (name) => {
const file = dir + path.sep + name, stats = await fs.stat(file).catch(() => null);
return stats ? stats.isDirectory() ? await listFiles(file) : [path.resolve(dir, file)] : [];
})
)).flat();
}
function fileExists(path2) {
return fs || getBuiltinModule(), fs.stat(path2).then(
() => true,
() => false
);
}
class LRUCache {
constructor(max = 250, ttl = 10800) {
this.max = max;
this.ttl = ttl;
this.map = /* @__PURE__ */ new Map();
}
get(key) {
const entry = this.map.get(key);
if (entry)
return entry[1] <= Date.now() ? void this.delete(key) : (this.map.delete(key), this.map.set(key, entry), entry[0]);
}
set(key, value) {
if (this.interval || (this.interval = setInterval(() => {
const now = Date.now();
for (const [key2, entry] of this.map)
entry[1] <= now && this.map.delete(key2);
}, this.ttl)), this.map.has(key)) this.map.delete(key);
else if (this.map.size >= this.max) {
const oldestKey = this.map.keys().next().value;
oldestKey !== void 0 && this.delete(oldestKey);
}
this.map.set(key, [value, Date.now() + this.ttl * 1e3]);
}
delete(key) {
this.map.get(key) && this.map.delete(key);
}
clear() {
this.map.clear();
}
size() {
return this.map.size;
}
[Symbol.dispose]() {
this.interval && clearInterval(this.interval);
}
}
function isCached(headers, etag, filePath) {
if (headers["cache-control"] && /no-cache|no-store/.test(headers["cache-control"]))
return false;
if ("if-none-match" in headers) {
const ifNoneMatch = headers["if-none-match"];
return ifNoneMatch === "*" ? true : ifNoneMatch === null || typeof etag != "string" ? false : ifNoneMatch === etag;
}
if (headers["if-modified-since"]) {
const ifModifiedSince = headers["if-modified-since"];
try {
return fs.stat(filePath).then((stat) => {
if (stat.mtime !== void 0 && stat.mtime.getTime() <= Date.parse(ifModifiedSince))
return true;
});
} catch {
}
}
return false;
}
let Crypto;
function getFile(path2) {
return isBun ? Bun.file(path2) : (fs || getBuiltinModule(), fs.readFile(path2));
}
async function generateETag(file) {
return isBun ? new Bun.CryptoHasher("md5").update(await file.arrayBuffer()).digest("base64") : (Crypto || (Crypto = process.getBuiltinModule("crypto")), Crypto ? Crypto.createHash("md5").update(file).digest("base64") : void console.warn(
"[@elysiajs/static] crypto is required to generate etag."
));
}
const isNotEmpty = (obj) => {
if (!obj) return false;
for (const _ in obj) return true;
return false;
};
async function staticPlugin({
assets = "public",
prefix = "/public",
staticLimit = 1024,
alwaysStatic = process.env.NODE_ENV === "production",
ignorePatterns = [".DS_Store", ".git", ".env"],
headers: initialHeaders,
maxAge = 86400,
directive = "public",
etag: useETag = true,
extension = true,
indexHTML = true,
detail,
bunFullstack = false,
decodeURI,
silent
} = {}) {
if (typeof process > "u" || typeof process.getBuiltinModule > "u")
return silent || console.warn(
"[@elysiajs/static] require process.getBuiltinModule. Static plugin is disabled"
), new Elysia();
const builtinModule = getBuiltinModule();
if (!builtinModule) return new Elysia();
const [fs, path] = builtinModule, normalizePath = path.sep !== "/" ? (p) => p.replace(/\\/g, "/") : (p) => p, fileCache = new LRUCache();
prefix === path.sep && (prefix = "");
const assetsDir = path.resolve(assets), shouldIgnore = ignorePatterns.length ? (file) => ignorePatterns.find(
(pattern) => typeof pattern == "string" ? pattern.includes(file) : pattern.test(file)
) : () => false, app = new Elysia({
name: "static",
seed: prefix
});
if (alwaysStatic) {
const files = await listFiles(path.resolve(assets));
if (files.length <= staticLimit)
for (const absolutePath of files) {
let handleCache2 = function({
headers: requestHeaders
}) {
if (etag) {
let cached = isCached(
requestHeaders,
etag,
absolutePath
);
if (cached === true)
return new Response(null, {
status: 304,
headers: isNotEmpty(initialHeaders) ? initialHeaders : void 0
});
if (cached !== false) {
const cache2 = fileCache.get(pathName);
return cache2 ? cache2.clone() : cached.then((cached2) => {
if (cached2)
return new Response(null, {
status: 304,
headers: initialHeaders || void 0
});
const response2 = new Response(file, {
headers: Object.assign(
{
"Cache-Control": maxAge ? `${directive}, max-age=${maxAge}` : directive
},
initialHeaders,
etag ? { Etag: etag } : {}
)
});
return fileCache.set(prefix, response2), response2.clone();
});
}
}
const cache = fileCache.get(pathName);
if (cache) return cache.clone();
const response = new Response(file, {
headers: Object.assign(
{
"Cache-Control": maxAge ? `${directive}, max-age=${maxAge}` : directive
},
initialHeaders,
etag ? { Etag: etag } : {}
)
});
return fileCache.set(pathName, response), response.clone();
};
if (!absolutePath || shouldIgnore(absolutePath)) continue;
let relativePath = absolutePath.replace(assetsDir, "");
decodeURI && (relativePath = fastDecodeURI(relativePath) ?? relativePath);
let pathName = normalizePath(path.join(prefix, relativePath));
if (isBun && absolutePath.endsWith(".html")) {
let htmlFile;
try {
htmlFile = bunFullstack ? (await import(absolutePath)).default : getFile(absolutePath);
} catch (error) {
silent || console.error(
`[@elysiajs/static] Failed to load HTML file: ${absolutePath}`,
error
);
continue;
}
app.get(pathName, htmlFile, {
detail: typeof detail == "function" ? detail(pathName) : detail
}), indexHTML && pathName.endsWith("/index.html") && app.get(pathName.replace("/index.html", ""), htmlFile, {
detail: typeof detail == "function" ? detail(
pathName.replace("/index.html", "")
) : detail
});
continue;
}
extension || (pathName = normalizePath(
pathName.slice(0, pathName.lastIndexOf("."))
));
const file = isBun ? getFile(absolutePath) : await getFile(absolutePath);
if (!file)
return silent || console.warn(
`[@elysiajs/static] Failed to load file: ${absolutePath}`
), new Elysia();
const etag = await generateETag(file);
app.get(
pathName,
useETag ? handleCache2 : new Response(
file,
isNotEmpty(initialHeaders) ? {
headers: initialHeaders
} : void 0
),
{
detail: typeof detail == "function" ? detail(pathName) : detail
}
), indexHTML && pathName.endsWith("/index.html") && app.get(
pathName.replace("/index.html", ""),
useETag ? handleCache2 : new Response(
file,
isNotEmpty(initialHeaders) ? {
headers: initialHeaders
} : void 0
),
{
detail: typeof detail == "function" ? detail(
pathName.replace("/index.html", "")
) : detail
}
);
}
return app;
}
if (
// @ts-ignore private property
!(`GET_${prefix}/*` in app.routeTree)
) {
if (isBun) {
const htmls = await listHTMLFiles(path.resolve(assets));
for (const absolutePath of htmls) {
if (!absolutePath || shouldIgnore(absolutePath)) continue;
let relativePath = absolutePath.replace(assetsDir, "");
const pathName = normalizePath(path.join(prefix, relativePath));
let htmlFile;
try {
htmlFile = bunFullstack ? (await import(absolutePath)).default : getFile(absolutePath);
} catch (error) {
silent || console.error(
`[@elysiajs/static] Failed to load HTML file: ${absolutePath}`,
error
);
continue;
}
app.get(pathName, htmlFile, {
detail: typeof detail == "function" ? detail(pathName) : detail
}), indexHTML && pathName.endsWith("/index.html") && app.get(pathName.replace("/index.html", ""), htmlFile, {
detail: typeof detail == "function" ? detail(pathName.replace("/index.html", "")) : detail
});
}
}
app.onError(() => {
}).get(
`${prefix.endsWith("/") ? prefix.slice(0, -1) : prefix}/*`,
async ({ params, headers: requestHeaders }) => {
const pathName = normalizePath(
path.join(
assets,
decodeURI ? fastDecodeURI(params["*"]) ?? params["*"] : params["*"]
)
);
if (shouldIgnore(pathName)) throw new NotFoundError();
const cache = fileCache.get(pathName);
if (cache) return cache.clone();
try {
const fileStat = await fs.stat(pathName).catch(() => null);
if (!fileStat) throw new NotFoundError();
if (!indexHTML && fileStat.isDirectory())
throw new NotFoundError();
let file;
if (!isBun && indexHTML) {
const htmlPath = path.join(pathName, "index.html"), cache2 = fileCache.get(htmlPath);
if (cache2) return cache2.clone();
await fileExists(htmlPath) && (file = await getFile(htmlPath));
}
if (!file && !fileStat.isDirectory() && await fileExists(pathName))
file = await getFile(pathName);
else throw new NotFoundError();
if (!useETag)
return new Response(
file,
isNotEmpty(initialHeaders) ? { headers: initialHeaders } : void 0
);
const etag = await generateETag(file);
if (etag && await isCached(requestHeaders, etag, pathName))
return new Response(null, {
status: 304
});
const response = new Response(file, {
headers: Object.assign(
{
"Cache-Control": maxAge ? `${directive}, max-age=${maxAge}` : directive
},
initialHeaders,
etag ? { Etag: etag } : {}
)
});
return fileCache.set(pathName, response), response.clone();
} catch (error) {
throw error instanceof NotFoundError ? error : (silent || console.error("[@elysiajs/static]", error), new NotFoundError());
}
},
{
detail: typeof detail == "function" ? detail(
`${prefix.endsWith("/") ? prefix.slice(0, -1) : prefix}/*`
) : detail
}
);
}
return app;
}
export { getDefaultExportFromCjs as g, staticPlugin as s };