@elysiajs/static
Advanced tools
Comparing version 0.8.0 to 0.8.1
import { Elysia } from 'elysia'; | ||
export declare const staticPlugin: <Prefix extends string = "/prefix">({ assets, prefix, staticLimit, alwaysStatic, ignorePatterns, noExtension, resolve, headers }?: { | ||
export declare const staticPlugin: <Prefix extends string = "/prefix">({ assets, prefix, staticLimit, alwaysStatic, ignorePatterns, noExtension, enableDecodeURI, resolve, headers, noCache, indexHTML }?: { | ||
assets?: string | undefined; | ||
@@ -9,4 +9,7 @@ prefix?: Prefix | undefined; | ||
noExtension?: boolean | undefined; | ||
enableDecodeURI?: boolean | undefined; | ||
resolve?: ((...pathSegments: string[]) => string) | undefined; | ||
headers?: Record<string, string> | undefined; | ||
noCache?: boolean | undefined; | ||
indexHTML?: boolean | undefined; | ||
}) => Promise<Elysia<"", { | ||
@@ -13,0 +16,0 @@ request: {}; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -7,2 +10,22 @@ exports.staticPlugin = void 0; | ||
const path_1 = require("path"); | ||
const node_cache_1 = __importDefault(require("node-cache")); | ||
const cache_1 = require("./cache"); | ||
const statCache = new node_cache_1.default({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}); | ||
const fileCache = new node_cache_1.default({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}); | ||
const htmlCache = new node_cache_1.default({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}); | ||
const listFiles = async (dir) => { | ||
@@ -19,3 +42,3 @@ const files = await (0, promises_1.readdir)(dir); | ||
}; | ||
const staticPlugin = async ({ assets = 'public', prefix = '/public', staticLimit = 1024, alwaysStatic = false, ignorePatterns = ['.DS_Store', '.git', '.env'], noExtension = false, resolve = path_1.resolve, headers = {} } = { | ||
const staticPlugin = async ({ assets = 'public', prefix = '/public', staticLimit = 1024, alwaysStatic = false, ignorePatterns = ['.DS_Store', '.git', '.env'], noExtension = false, enableDecodeURI = false, resolve = path_1.resolve, headers = {}, noCache = false, indexHTML = true } = { | ||
assets: 'public', | ||
@@ -27,4 +50,7 @@ prefix: '/public', | ||
noExtension: false, | ||
enableDecodeURI: false, | ||
resolve: path_1.resolve, | ||
headers: {} | ||
headers: {}, | ||
noCache: false, | ||
indexHTML: true | ||
}) => { | ||
@@ -53,3 +79,6 @@ const files = await listFiles((0, path_1.resolve)(assets)); | ||
noExtension, | ||
resolve: resolve.toString() | ||
resolve: resolve.toString(), | ||
headers, | ||
noCache, | ||
indexHTML | ||
} | ||
@@ -60,11 +89,8 @@ }); | ||
for (let i = 0; i < files.length; i++) { | ||
const file = files[i]; | ||
if (!file || shouldIgnore(file)) | ||
const filePath = files[i]; | ||
if (!filePath || shouldIgnore(filePath)) | ||
continue; | ||
const response = Object.keys(headers).length | ||
? () => new Response(Bun.file(file), { | ||
headers | ||
}) | ||
: () => new Response(Bun.file(file)); | ||
let fileName = file.replace(resolve(), '').replace(`${assets}/`, ''); | ||
let fileName = filePath | ||
.replace(resolve(), '') | ||
.replace(`${assets}/`, ''); | ||
if (noExtension) { | ||
@@ -75,17 +101,94 @@ const temp = fileName.split('.'); | ||
} | ||
app.get((0, path_1.join)(prefix, fileName), response); | ||
const file = Bun.file(filePath); | ||
const etag = await (0, cache_1.generateETag)(file); | ||
app.get((0, path_1.join)(prefix, fileName), noCache | ||
? new Response(file, { | ||
headers | ||
}) | ||
: async ({ headers: reqHeaders }) => { | ||
if (await (0, cache_1.isCached)(reqHeaders, etag, filePath)) { | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}); | ||
} | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
return new Response(file, { | ||
headers | ||
}); | ||
}); | ||
if (indexHTML && fileName.endsWith('/index.html')) | ||
app.get((0, path_1.join)(prefix, fileName.replace('/index.html', '')), noCache | ||
? new Response(file, { | ||
headers | ||
}) | ||
: async ({ headers: reqHeaders }) => { | ||
if (await (0, cache_1.isCached)(reqHeaders, etag, filePath)) { | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}); | ||
} | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
return new Response(file, { | ||
headers | ||
}); | ||
}); | ||
} | ||
else { | ||
if (!app.routes.find(({ method, path }) => path === `${prefix}/*` && method === 'GET')) | ||
app.onError(() => { }).get(`${prefix}/*`, async ({ params }) => { | ||
const file = `${assets}/${params['*']}`; | ||
if (shouldIgnore(file)) | ||
app.onError(() => { }).get(`${prefix}/*`, async ({ params, headers: reqHeaders }) => { | ||
const path = enableDecodeURI | ||
? decodeURI(`${assets}/${decodeURI(params['*'])}`) | ||
: `${assets}/${params['*']}`; | ||
if (shouldIgnore(path)) | ||
throw new elysia_1.NotFoundError(); | ||
return (0, promises_1.stat)(file) | ||
.then((status) => new Response(Bun.file(file), { | ||
headers | ||
})) | ||
.catch((error) => { | ||
try { | ||
let status = statCache.get(path); | ||
if (!status) { | ||
status = await (0, promises_1.stat)(path); | ||
statCache.set(path, status); | ||
} | ||
let file = fileCache.get(path); | ||
if (!file) { | ||
if (status.isDirectory()) { | ||
let hasCache = false; | ||
if (indexHTML && | ||
(hasCache = | ||
htmlCache.get(`${path}/index.html`) ?? | ||
(await (0, promises_1.exists)(`${path}/index.html`)))) { | ||
if (hasCache === undefined) | ||
htmlCache.set(`${path}/index.html`, true); | ||
file = Bun.file(`${path}/index.html`); | ||
} | ||
else { | ||
if (indexHTML && hasCache === undefined) | ||
htmlCache.set(`${path}/index.html`, false); | ||
throw new elysia_1.NotFoundError(); | ||
} | ||
} | ||
file ??= Bun.file(path); | ||
fileCache.set(path, file); | ||
} | ||
if (noCache) | ||
return new Response(file, { | ||
headers | ||
}); | ||
const etag = await (0, cache_1.generateETag)(file); | ||
if (await (0, cache_1.isCached)(reqHeaders, etag, path)) | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}); | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
return new Response(file, { | ||
headers | ||
}); | ||
} | ||
catch (error) { | ||
throw new elysia_1.NotFoundError(); | ||
}); | ||
} | ||
}); | ||
@@ -92,0 +195,0 @@ } |
import { Elysia } from 'elysia'; | ||
export declare const staticPlugin: <Prefix extends string = "/prefix">({ assets, prefix, staticLimit, alwaysStatic, ignorePatterns, noExtension, resolve, headers }?: { | ||
export declare const staticPlugin: <Prefix extends string = "/prefix">({ assets, prefix, staticLimit, alwaysStatic, ignorePatterns, noExtension, enableDecodeURI, resolve, headers, noCache, indexHTML }?: { | ||
/** | ||
@@ -26,3 +26,3 @@ * @default "public" | ||
* | ||
* If set to true, file will always use static path instead | ||
* Should file always be served statically | ||
*/ | ||
@@ -45,2 +45,9 @@ alwaysStatic?: boolean | undefined; | ||
/** | ||
* | ||
* When url needs to be decoded | ||
* | ||
* Only works if `alwaysStatic` is set to false | ||
*/ | ||
enableDecodeURI?: boolean | undefined; | ||
/** | ||
* Nodejs resolve function | ||
@@ -53,2 +60,14 @@ */ | ||
headers?: Record<string, string> | undefined; | ||
/** | ||
* @default false | ||
* | ||
* If set to true, browser caching will be disabled | ||
*/ | ||
noCache?: boolean | undefined; | ||
/** | ||
* @default true | ||
* | ||
* Enable serving of index.html as default / route | ||
*/ | ||
indexHTML?: boolean | undefined; | ||
}) => Promise<Elysia<"", { | ||
@@ -58,9 +77,3 @@ request: {}; | ||
derive: {}; | ||
resolve: {}; /** | ||
* @default 1024 | ||
* | ||
* If total files exceed this number, | ||
* file will be handled via wildcard instead of static route | ||
* to reduce memory usage | ||
*/ | ||
resolve: {}; | ||
}, { | ||
@@ -67,0 +80,0 @@ type: {}; |
import { NotFoundError, Elysia } from 'elysia'; | ||
import { readdir, stat } from 'fs/promises'; | ||
import { exists, readdir, stat } from 'fs/promises'; | ||
import { resolve, resolve as resolveFn, join } from 'path'; | ||
import Cache from 'node-cache'; | ||
import { generateETag, isCached } from './cache'; | ||
const statCache = new Cache({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}); | ||
const fileCache = new Cache({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}); | ||
const htmlCache = new Cache({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}); | ||
const listFiles = async (dir) => { | ||
@@ -15,3 +35,3 @@ const files = await readdir(dir); | ||
}; | ||
export const staticPlugin = async ({ assets = 'public', prefix = '/public', staticLimit = 1024, alwaysStatic = false, ignorePatterns = ['.DS_Store', '.git', '.env'], noExtension = false, resolve = resolveFn, headers = {} } = { | ||
export const staticPlugin = async ({ assets = 'public', prefix = '/public', staticLimit = 1024, alwaysStatic = false, ignorePatterns = ['.DS_Store', '.git', '.env'], noExtension = false, enableDecodeURI = false, resolve = resolveFn, headers = {}, noCache = false, indexHTML = true } = { | ||
assets: 'public', | ||
@@ -23,4 +43,7 @@ prefix: '/public', | ||
noExtension: false, | ||
enableDecodeURI: false, | ||
resolve: resolveFn, | ||
headers: {} | ||
headers: {}, | ||
noCache: false, | ||
indexHTML: true | ||
}) => { | ||
@@ -49,3 +72,6 @@ const files = await listFiles(resolveFn(assets)); | ||
noExtension, | ||
resolve: resolve.toString() | ||
resolve: resolve.toString(), | ||
headers, | ||
noCache, | ||
indexHTML | ||
} | ||
@@ -56,11 +82,8 @@ }); | ||
for (let i = 0; i < files.length; i++) { | ||
const file = files[i]; | ||
if (!file || shouldIgnore(file)) | ||
const filePath = files[i]; | ||
if (!filePath || shouldIgnore(filePath)) | ||
continue; | ||
const response = Object.keys(headers).length | ||
? () => new Response(Bun.file(file), { | ||
headers | ||
}) | ||
: () => new Response(Bun.file(file)); | ||
let fileName = file.replace(resolve(), '').replace(`${assets}/`, ''); | ||
let fileName = filePath | ||
.replace(resolve(), '') | ||
.replace(`${assets}/`, ''); | ||
if (noExtension) { | ||
@@ -71,19 +94,94 @@ const temp = fileName.split('.'); | ||
} | ||
app.get(join(prefix, fileName), response); | ||
const file = Bun.file(filePath); | ||
const etag = await generateETag(file); | ||
app.get(join(prefix, fileName), noCache | ||
? new Response(file, { | ||
headers | ||
}) | ||
: async ({ headers: reqHeaders }) => { | ||
if (await isCached(reqHeaders, etag, filePath)) { | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}); | ||
} | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
return new Response(file, { | ||
headers | ||
}); | ||
}); | ||
if (indexHTML && fileName.endsWith('/index.html')) | ||
app.get(join(prefix, fileName.replace('/index.html', '')), noCache | ||
? new Response(file, { | ||
headers | ||
}) | ||
: async ({ headers: reqHeaders }) => { | ||
if (await isCached(reqHeaders, etag, filePath)) { | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}); | ||
} | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
return new Response(file, { | ||
headers | ||
}); | ||
}); | ||
} | ||
else { | ||
if ( | ||
// @ts-ignore | ||
!app.routes.find(({ method, path }) => path === `${prefix}/*` && method === 'GET')) | ||
app.onError(() => { }).get(`${prefix}/*`, async ({ params }) => { | ||
const file = `${assets}/${params['*']}`; | ||
if (shouldIgnore(file)) | ||
if (!app.routes.find(({ method, path }) => path === `${prefix}/*` && method === 'GET')) | ||
app.onError(() => { }).get(`${prefix}/*`, async ({ params, headers: reqHeaders }) => { | ||
const path = enableDecodeURI | ||
? decodeURI(`${assets}/${decodeURI(params['*'])}`) | ||
: `${assets}/${params['*']}`; | ||
if (shouldIgnore(path)) | ||
throw new NotFoundError(); | ||
return stat(file) | ||
.then((status) => new Response(Bun.file(file), { | ||
headers | ||
})) | ||
.catch((error) => { | ||
try { | ||
let status = statCache.get(path); | ||
if (!status) { | ||
status = await stat(path); | ||
statCache.set(path, status); | ||
} | ||
let file = fileCache.get(path); | ||
if (!file) { | ||
if (status.isDirectory()) { | ||
let hasCache = false; | ||
if (indexHTML && | ||
(hasCache = | ||
htmlCache.get(`${path}/index.html`) ?? | ||
(await exists(`${path}/index.html`)))) { | ||
if (hasCache === undefined) | ||
htmlCache.set(`${path}/index.html`, true); | ||
file = Bun.file(`${path}/index.html`); | ||
} | ||
else { | ||
if (indexHTML && hasCache === undefined) | ||
htmlCache.set(`${path}/index.html`, false); | ||
throw new NotFoundError(); | ||
} | ||
} | ||
file ??= Bun.file(path); | ||
fileCache.set(path, file); | ||
} | ||
if (noCache) | ||
return new Response(file, { | ||
headers | ||
}); | ||
const etag = await generateETag(file); | ||
if (await isCached(reqHeaders, etag, path)) | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}); | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
return new Response(file, { | ||
headers | ||
}); | ||
} | ||
catch (error) { | ||
throw new NotFoundError(); | ||
}); | ||
} | ||
}); | ||
@@ -90,0 +188,0 @@ } |
100
package.json
{ | ||
"name": "@elysiajs/static", | ||
"version": "0.8.0", | ||
"author": { | ||
"name": "saltyAom", | ||
"url": "https://github.com/SaltyAom", | ||
"email": "saltyaom@gmail.com" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/elysiajs/elysia-static" | ||
}, | ||
"main": "./dist/index.js", | ||
"devDependencies": { | ||
"@types/node": "^20.1.4", | ||
"bun-types": "^1.0.2", | ||
"elysia": "0.8.0", | ||
"eslint": "^8.40.0", | ||
"rimraf": "4.4.1", | ||
"typescript": "^5.2.2" | ||
}, | ||
"peerDependencies": { | ||
"elysia": ">= 0.8.0" | ||
}, | ||
"exports": { | ||
"bun": "./dist/index.js", | ||
"node": "./dist/cjs/index.js", | ||
"require": "./dist/cjs/index.js", | ||
"import": "./dist/index.js", | ||
"default": "./dist/index.js" | ||
}, | ||
"bugs": "https://github.com/elysiajs/elysia-static/issues", | ||
"description": "Plugin for Elysia for serving static folder", | ||
"homepage": "https://github.com/elysiajs/elysia-static", | ||
"keywords": [ | ||
"elysia", | ||
"static", | ||
"public" | ||
], | ||
"license": "MIT", | ||
"scripts": { | ||
"dev": "bun run --hot example/index.ts", | ||
"test": "bun test && npm run test:node", | ||
"test:node": "npm install --prefix ./test/node/cjs/ && npm install --prefix ./test/node/esm/ && node ./test/node/cjs/index.js && node ./test/node/esm/index.js", | ||
"build": "rimraf dist && tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json", | ||
"release": "npm run build && npm run test && npm publish --access public" | ||
}, | ||
"types": "./src/index.ts" | ||
} | ||
"name": "@elysiajs/static", | ||
"version": "0.8.1", | ||
"author": { | ||
"name": "saltyAom", | ||
"url": "https://github.com/SaltyAom", | ||
"email": "saltyaom@gmail.com" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/elysiajs/elysia-static" | ||
}, | ||
"main": "./dist/index.js", | ||
"devDependencies": { | ||
"@types/node": "^20.1.4", | ||
"@typescript-eslint/eslint-plugin": "^6.7.4", | ||
"bun-types": "^1.0.2", | ||
"elysia": "0.8.0", | ||
"eslint": "^8.40.0", | ||
"rimraf": "4.4.1", | ||
"typescript": "^5.2.2" | ||
}, | ||
"peerDependencies": { | ||
"elysia": ">= 0.8.0" | ||
}, | ||
"exports": { | ||
"bun": "./dist/index.js", | ||
"node": "./dist/cjs/index.js", | ||
"require": "./dist/cjs/index.js", | ||
"import": "./dist/index.js", | ||
"default": "./dist/index.js" | ||
}, | ||
"bugs": "https://github.com/elysiajs/elysia-static/issues", | ||
"description": "Plugin for Elysia for serving static folder", | ||
"homepage": "https://github.com/elysiajs/elysia-static", | ||
"keywords": [ | ||
"elysia", | ||
"static", | ||
"public" | ||
], | ||
"license": "MIT", | ||
"scripts": { | ||
"dev": "bun run --hot example/index.ts", | ||
"test": "bun test && npm run test:node", | ||
"test:node": "npm install --prefix ./test/node/cjs/ && npm install --prefix ./test/node/esm/ && node ./test/node/cjs/index.js && node ./test/node/esm/index.js", | ||
"build": "rimraf dist && tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json", | ||
"release": "npm run build && npm run test && npm publish --access public" | ||
}, | ||
"types": "./src/index.ts", | ||
"dependencies": { | ||
"node-cache": "^5.1.2" | ||
} | ||
} |
213
src/index.ts
import { NotFoundError, Elysia } from 'elysia' | ||
import { readdir, stat } from 'fs/promises' | ||
import { exists, readdir, stat } from 'fs/promises' | ||
import { resolve, resolve as resolveFn, join } from 'path' | ||
import Cache from 'node-cache' | ||
import { generateETag, isCached } from './cache' | ||
import { Stats } from 'fs' | ||
const statCache = new Cache({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}) | ||
const fileCache = new Cache({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}) | ||
const htmlCache = new Cache({ | ||
useClones: false, | ||
checkperiod: 5 * 60, | ||
stdTTL: 3 * 60 * 60, | ||
maxKeys: 250 | ||
}) | ||
const listFiles = async (dir: string): Promise<string[]> => { | ||
@@ -31,4 +56,7 @@ const files = await readdir(dir) | ||
noExtension = false, | ||
enableDecodeURI = false, | ||
resolve = resolveFn, | ||
headers = {} | ||
headers = {}, | ||
noCache = false, | ||
indexHTML = true | ||
}: { | ||
@@ -58,3 +86,3 @@ /** | ||
* | ||
* If set to true, file will always use static path instead | ||
* Should file always be served statically | ||
*/ | ||
@@ -77,2 +105,9 @@ alwaysStatic?: boolean | ||
/** | ||
* | ||
* When url needs to be decoded | ||
* | ||
* Only works if `alwaysStatic` is set to false | ||
*/ | ||
enableDecodeURI?: boolean | ||
/** | ||
* Nodejs resolve function | ||
@@ -85,2 +120,14 @@ */ | ||
headers?: Record<string, string> | undefined | ||
/** | ||
* @default false | ||
* | ||
* If set to true, browser caching will be disabled | ||
*/ | ||
noCache?: boolean | ||
/** | ||
* @default true | ||
* | ||
* Enable serving of index.html as default / route | ||
*/ | ||
indexHTML?: boolean | ||
} = { | ||
@@ -93,4 +140,7 @@ assets: 'public', | ||
noExtension: false, | ||
enableDecodeURI: false, | ||
resolve: resolveFn, | ||
headers: {} | ||
headers: {}, | ||
noCache: false, | ||
indexHTML: true | ||
} | ||
@@ -120,3 +170,6 @@ ) => { | ||
noExtension, | ||
resolve: resolve.toString() | ||
resolve: resolve.toString(), | ||
headers, | ||
noCache, | ||
indexHTML | ||
} | ||
@@ -130,14 +183,9 @@ }) | ||
for (let i = 0; i < files.length; i++) { | ||
const file = files[i] | ||
if (!file || shouldIgnore(file)) continue | ||
const filePath = files[i] | ||
if (!filePath || shouldIgnore(filePath)) continue | ||
const response = Object.keys(headers).length | ||
? () => | ||
new Response(Bun.file(file), { | ||
headers | ||
}) | ||
: () => new Response(Bun.file(file)) | ||
let fileName = filePath | ||
.replace(resolve(), '') | ||
.replace(`${assets}/`, '') | ||
let fileName = file.replace(resolve(), '').replace(`${assets}/`, '') | ||
if (noExtension) { | ||
@@ -150,7 +198,54 @@ const temp = fileName.split('.') | ||
app.get(join(prefix, fileName), response) | ||
const file = Bun.file(filePath) | ||
const etag = await generateETag(file) | ||
app.get( | ||
join(prefix, fileName), | ||
noCache | ||
? new Response(file, { | ||
headers | ||
}) | ||
: async ({ headers: reqHeaders }) => { | ||
if (await isCached(reqHeaders, etag, filePath)) { | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}) | ||
} | ||
headers['Etag'] = etag | ||
headers['Cache-Control'] = 'public, max-age=0' | ||
return new Response(file, { | ||
headers | ||
}) | ||
} | ||
) | ||
if (indexHTML && fileName.endsWith('/index.html')) | ||
app.get( | ||
join(prefix, fileName.replace('/index.html', '')), | ||
noCache | ||
? new Response(file, { | ||
headers | ||
}) | ||
: async ({ headers: reqHeaders }) => { | ||
if (await isCached(reqHeaders, etag, filePath)) { | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}) | ||
} | ||
headers['Etag'] = etag | ||
headers['Cache-Control'] = 'public, max-age=0' | ||
return new Response(file, { | ||
headers | ||
}) | ||
} | ||
) | ||
} | ||
else { | ||
if ( | ||
// @ts-ignore | ||
!app.routes.find( | ||
@@ -160,18 +255,80 @@ ({ method, path }) => path === `${prefix}/*` && method === 'GET' | ||
) | ||
app.onError(() => {}).get(`${prefix}/*`, async ({ params }) => { | ||
const file = `${assets}/${(params as any)['*']}` | ||
app.onError(() => {}).get( | ||
`${prefix}/*`, | ||
async ({ params, headers: reqHeaders }) => { | ||
const path = enableDecodeURI | ||
? decodeURI(`${assets}/${decodeURI(params['*'])}`) | ||
: `${assets}/${params['*']}` | ||
if (shouldIgnore(file)) throw new NotFoundError() | ||
if (shouldIgnore(path)) throw new NotFoundError() | ||
return stat(file) | ||
.then( | ||
(status) => | ||
new Response(Bun.file(file), { | ||
try { | ||
let status = statCache.get<Stats>(path) | ||
if (!status) { | ||
status = await stat(path) | ||
statCache.set(path, status) | ||
} | ||
let file = | ||
fileCache.get<ReturnType<(typeof Bun)['file']>>( | ||
path | ||
) | ||
if (!file) { | ||
if (status.isDirectory()) { | ||
let hasCache = false | ||
if ( | ||
indexHTML && | ||
(hasCache = | ||
htmlCache.get<boolean>( | ||
`${path}/index.html` | ||
) ?? | ||
(await exists(`${path}/index.html`))) | ||
) { | ||
if (hasCache === undefined) | ||
htmlCache.set( | ||
`${path}/index.html`, | ||
true | ||
) | ||
file = Bun.file(`${path}/index.html`) | ||
} else { | ||
if (indexHTML && hasCache === undefined) | ||
htmlCache.set( | ||
`${path}/index.html`, | ||
false | ||
) | ||
throw new NotFoundError() | ||
} | ||
} | ||
file ??= Bun.file(path) | ||
fileCache.set(path, file) | ||
} | ||
if (noCache) | ||
return new Response(file, { | ||
headers | ||
}) | ||
) | ||
.catch((error) => { | ||
const etag = await generateETag(file) | ||
if (await isCached(reqHeaders, etag, path)) | ||
return new Response(null, { | ||
status: 304, | ||
headers | ||
}) | ||
headers['Etag'] = etag | ||
headers['Cache-Control'] = 'public, max-age=0' | ||
return new Response(file, { | ||
headers | ||
}) | ||
} catch (error) { | ||
throw new NotFoundError() | ||
}) | ||
}) | ||
} | ||
} | ||
) | ||
} | ||
@@ -178,0 +335,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38435
16
952
2
7
8
1
+ Addednode-cache@^5.1.2
+ Addedclone@2.1.2(transitive)
+ Addednode-cache@5.1.2(transitive)