@elysiajs/static
Advanced tools
Comparing version 1.0.2 to 1.0.3
@@ -7,21 +7,16 @@ import { stat } from 'fs/promises'; | ||
if (headers['cache-control'] && | ||
headers['cache-control'].indexOf('no-cache') !== -1) { | ||
headers['cache-control'].indexOf('no-cache') !== -1) | ||
return false; | ||
} | ||
// if-none-match | ||
if ('if-none-match' in headers) { | ||
const ifNoneMatch = headers['if-none-match']; | ||
if (ifNoneMatch === '*') { | ||
if (ifNoneMatch === '*') | ||
return true; | ||
} | ||
if (ifNoneMatch === null) { | ||
if (ifNoneMatch === null) | ||
return false; | ||
} | ||
if (typeof etag !== 'string') { | ||
if (typeof etag !== 'string') | ||
return false; | ||
} | ||
const isMatching = ifNoneMatch === etag; | ||
if (isMatching) { | ||
if (isMatching) | ||
return true; | ||
} | ||
/** | ||
@@ -49,5 +44,4 @@ * A recipient MUST ignore If-Modified-Since if the request contains an | ||
if (lastModified !== undefined && | ||
lastModified.getTime() <= Date.parse(ifModifiedSince)) { | ||
lastModified.getTime() <= Date.parse(ifModifiedSince)) | ||
return true; | ||
} | ||
} | ||
@@ -54,0 +48,0 @@ return false; |
@@ -7,20 +7,15 @@ "use strict"; | ||
if (headers['cache-control'] && | ||
headers['cache-control'].indexOf('no-cache') !== -1) { | ||
headers['cache-control'].indexOf('no-cache') !== -1) | ||
return false; | ||
} | ||
if ('if-none-match' in headers) { | ||
const ifNoneMatch = headers['if-none-match']; | ||
if (ifNoneMatch === '*') { | ||
if (ifNoneMatch === '*') | ||
return true; | ||
} | ||
if (ifNoneMatch === null) { | ||
if (ifNoneMatch === null) | ||
return false; | ||
} | ||
if (typeof etag !== 'string') { | ||
if (typeof etag !== 'string') | ||
return false; | ||
} | ||
const isMatching = ifNoneMatch === etag; | ||
if (isMatching) { | ||
if (isMatching) | ||
return true; | ||
} | ||
return false; | ||
@@ -37,5 +32,4 @@ } | ||
if (lastModified !== undefined && | ||
lastModified.getTime() <= Date.parse(ifModifiedSince)) { | ||
lastModified.getTime() <= Date.parse(ifModifiedSince)) | ||
return true; | ||
} | ||
} | ||
@@ -42,0 +36,0 @@ return false; |
import { Elysia } from 'elysia'; | ||
export declare const staticPlugin: <Prefix extends string = "/prefix">({ assets, prefix, staticLimit, alwaysStatic, ignorePatterns, noExtension, enableDecodeURI, resolve, headers, noCache, indexHTML }?: { | ||
export declare const staticPlugin: <Prefix extends string = "/prefix">({ assets, prefix, staticLimit, alwaysStatic, ignorePatterns, noExtension, enableDecodeURI, resolve, headers, noCache, maxAge, directive, indexHTML }?: { | ||
assets?: string | undefined; | ||
@@ -13,2 +13,4 @@ prefix?: Prefix | undefined; | ||
noCache?: boolean | undefined; | ||
directive?: "no-cache" | "public" | "private" | "must-revalidate" | "no-store" | "no-transform" | "proxy-revalidate" | "immutable" | undefined; | ||
maxAge?: number | null | undefined; | ||
indexHTML?: boolean | undefined; | ||
@@ -15,0 +17,0 @@ }) => Promise<Elysia<"", false, { |
@@ -12,2 +12,3 @@ "use strict"; | ||
const cache_1 = require("./cache"); | ||
const URL_PATH_SEP = '/'; | ||
const fileExists = (path) => (0, promises_1.stat)(path).then(() => true, () => false); | ||
@@ -35,3 +36,3 @@ const statCache = new node_cache_1.default({ | ||
const all = await Promise.all(files.map(async (name) => { | ||
const file = dir + '/' + name; | ||
const file = dir + path_1.sep + name; | ||
const stats = await (0, promises_1.stat)(file); | ||
@@ -44,3 +45,3 @@ return stats && stats.isDirectory() | ||
}; | ||
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 } = { | ||
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, maxAge = 86400, directive = 'public', indexHTML = true } = { | ||
assets: 'public', | ||
@@ -59,3 +60,4 @@ prefix: '/public', | ||
const files = await listFiles((0, path_1.resolve)(assets)); | ||
if (prefix === '/') | ||
const isFSSepUnsafe = path_1.sep !== URL_PATH_SEP; | ||
if (prefix === URL_PATH_SEP) | ||
prefix = ''; | ||
@@ -81,5 +83,8 @@ const shouldIgnore = (file) => { | ||
noExtension, | ||
enableDecodeURI, | ||
resolve: resolve.toString(), | ||
headers, | ||
noCache, | ||
maxAge, | ||
directive, | ||
indexHTML | ||
@@ -96,3 +101,3 @@ } | ||
.replace(resolve(), '') | ||
.replace(`${assets}/`, ''); | ||
.replace(`${assets}${path_1.sep}`, ''); | ||
if (noExtension) { | ||
@@ -105,3 +110,6 @@ const temp = fileName.split('.'); | ||
const etag = await (0, cache_1.generateETag)(file); | ||
app.get((0, path_1.join)(prefix, fileName), noCache | ||
const pathName = isFSSepUnsafe | ||
? prefix + fileName.split(path_1.sep).join(URL_PATH_SEP) | ||
: (0, path_1.join)(prefix, fileName); | ||
app.get(pathName, noCache | ||
? new Response(file, { | ||
@@ -118,3 +126,5 @@ headers | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
headers['Cache-Control'] = directive; | ||
if (maxAge !== null) | ||
headers['Cache-Control'] += `, max-age=${maxAge}`; | ||
return new Response(file, { | ||
@@ -124,4 +134,4 @@ headers | ||
}); | ||
if (indexHTML && fileName.endsWith('/index.html')) | ||
app.get((0, path_1.join)(prefix, fileName.replace('/index.html', '')), noCache | ||
if (indexHTML && pathName.endsWith('/index.html')) | ||
app.get((0, path_1.join)(prefix, pathName.replace('/index.html', '')), noCache | ||
? new Response(file, { | ||
@@ -131,3 +141,3 @@ headers | ||
: async ({ headers: reqHeaders }) => { | ||
if (await (0, cache_1.isCached)(reqHeaders, etag, filePath)) { | ||
if (await (0, cache_1.isCached)(reqHeaders, etag, pathName)) { | ||
return new Response(null, { | ||
@@ -139,3 +149,5 @@ status: 304, | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
headers['Cache-Control'] = directive; | ||
if (maxAge !== null) | ||
headers['Cache-Control'] += `, max-age=${maxAge}`; | ||
return new Response(file, { | ||
@@ -149,5 +161,8 @@ headers | ||
app.onError(() => { }).get(`${prefix}/*`, async ({ params, headers: reqHeaders }) => { | ||
const path = enableDecodeURI | ||
let path = enableDecodeURI | ||
? decodeURI(`${assets}/${decodeURI(params['*'])}`) | ||
: `${assets}/${params['*']}`; | ||
if (isFSSepUnsafe) { | ||
path = path.replace(URL_PATH_SEP, path_1.sep); | ||
} | ||
if (shouldIgnore(path)) | ||
@@ -167,11 +182,11 @@ throw new elysia_1.NotFoundError(); | ||
(hasCache = | ||
htmlCache.get(`${path}/index.html`) ?? | ||
(await fileExists(`${path}/index.html`)))) { | ||
htmlCache.get(`${path}${path_1.sep}index.html`) ?? | ||
(await fileExists(`${path}${path_1.sep}index.html`)))) { | ||
if (hasCache === undefined) | ||
htmlCache.set(`${path}/index.html`, true); | ||
file = Bun.file(`${path}/index.html`); | ||
htmlCache.set(`${path}${path_1.sep}index.html`, true); | ||
file = Bun.file(`${path}${path_1.sep}index.html`); | ||
} | ||
else { | ||
if (indexHTML && hasCache === undefined) | ||
htmlCache.set(`${path}/index.html`, false); | ||
htmlCache.set(`${path}${path_1.sep}index.html`, false); | ||
throw new elysia_1.NotFoundError(); | ||
@@ -194,3 +209,5 @@ } | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
headers['Cache-Control'] = directive; | ||
if (maxAge !== null) | ||
headers['Cache-Control'] += `, max-age=${maxAge}`; | ||
return new Response(file, { | ||
@@ -197,0 +214,0 @@ headers |
import { Elysia } from 'elysia'; | ||
export declare const staticPlugin: <Prefix extends string = "/prefix">({ assets, prefix, staticLimit, alwaysStatic, ignorePatterns, noExtension, enableDecodeURI, resolve, headers, noCache, indexHTML }?: { | ||
export declare const staticPlugin: <Prefix extends string = "/prefix">({ assets, prefix, staticLimit, alwaysStatic, ignorePatterns, noExtension, enableDecodeURI, resolve, headers, noCache, maxAge, directive, indexHTML }?: { | ||
/** | ||
@@ -65,2 +65,22 @@ * @default "public" | ||
/** | ||
* @default public | ||
* | ||
* directive for Cache-Control header | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#directives | ||
*/ | ||
directive?: "no-cache" | "public" | "private" | "must-revalidate" | "no-store" | "no-transform" | "proxy-revalidate" | "immutable" | undefined; | ||
/** | ||
* @default 86400 | ||
* | ||
* Specifies the maximum amount of time in seconds, a resource will be considered fresh. | ||
* This freshness lifetime is calculated relative to the time of the request. | ||
* This setting helps control browser caching behavior. | ||
* A `maxAge` of 0 will prevent caching, requiring requests to validate with the server before use. | ||
*/ | ||
maxAge?: number | null | undefined; | ||
/** | ||
* | ||
*/ | ||
/** | ||
* @default true | ||
@@ -67,0 +87,0 @@ * |
import { Elysia, NotFoundError } from 'elysia'; | ||
import { readdir, stat } from 'fs/promises'; | ||
import { resolve, resolve as resolveFn, join } from 'path'; | ||
import { resolve, resolve as resolveFn, join, sep } from 'path'; | ||
import Cache from 'node-cache'; | ||
import { generateETag, isCached } from './cache'; | ||
const URL_PATH_SEP = '/'; | ||
const fileExists = (path) => stat(path).then(() => true, () => false); | ||
@@ -28,3 +29,3 @@ const statCache = new Cache({ | ||
const all = await Promise.all(files.map(async (name) => { | ||
const file = dir + '/' + name; | ||
const file = dir + sep + name; | ||
const stats = await stat(file); | ||
@@ -37,3 +38,3 @@ return stats && stats.isDirectory() | ||
}; | ||
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 } = { | ||
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, maxAge = 86400, directive = 'public', indexHTML = true } = { | ||
assets: 'public', | ||
@@ -52,3 +53,4 @@ prefix: '/public', | ||
const files = await listFiles(resolveFn(assets)); | ||
if (prefix === '/') | ||
const isFSSepUnsafe = sep !== URL_PATH_SEP; | ||
if (prefix === URL_PATH_SEP) | ||
prefix = ''; | ||
@@ -74,5 +76,8 @@ const shouldIgnore = (file) => { | ||
noExtension, | ||
enableDecodeURI, | ||
resolve: resolve.toString(), | ||
headers, | ||
noCache, | ||
maxAge, | ||
directive, | ||
indexHTML | ||
@@ -89,3 +94,3 @@ } | ||
.replace(resolve(), '') | ||
.replace(`${assets}/`, ''); | ||
.replace(`${assets}${sep}`, ''); | ||
if (noExtension) { | ||
@@ -98,3 +103,6 @@ const temp = fileName.split('.'); | ||
const etag = await generateETag(file); | ||
app.get(join(prefix, fileName), noCache | ||
const pathName = isFSSepUnsafe | ||
? prefix + fileName.split(sep).join(URL_PATH_SEP) | ||
: join(prefix, fileName); | ||
app.get(pathName, noCache | ||
? new Response(file, { | ||
@@ -111,3 +119,5 @@ headers | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
headers['Cache-Control'] = directive; | ||
if (maxAge !== null) | ||
headers['Cache-Control'] += `, max-age=${maxAge}`; | ||
return new Response(file, { | ||
@@ -117,4 +127,4 @@ headers | ||
}); | ||
if (indexHTML && fileName.endsWith('/index.html')) | ||
app.get(join(prefix, fileName.replace('/index.html', '')), noCache | ||
if (indexHTML && pathName.endsWith('/index.html')) | ||
app.get(join(prefix, pathName.replace('/index.html', '')), noCache | ||
? new Response(file, { | ||
@@ -124,3 +134,3 @@ headers | ||
: async ({ headers: reqHeaders }) => { | ||
if (await isCached(reqHeaders, etag, filePath)) { | ||
if (await isCached(reqHeaders, etag, pathName)) { | ||
return new Response(null, { | ||
@@ -132,3 +142,5 @@ status: 304, | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
headers['Cache-Control'] = directive; | ||
if (maxAge !== null) | ||
headers['Cache-Control'] += `, max-age=${maxAge}`; | ||
return new Response(file, { | ||
@@ -142,5 +154,10 @@ headers | ||
app.onError(() => { }).get(`${prefix}/*`, async ({ params, headers: reqHeaders }) => { | ||
const path = enableDecodeURI | ||
let path = enableDecodeURI | ||
? decodeURI(`${assets}/${decodeURI(params['*'])}`) | ||
: `${assets}/${params['*']}`; | ||
// Handle varying filepath separators | ||
if (isFSSepUnsafe) { | ||
path = path.replace(URL_PATH_SEP, sep); | ||
} | ||
// Note that path must match the system separator | ||
if (shouldIgnore(path)) | ||
@@ -160,11 +177,11 @@ throw new NotFoundError(); | ||
(hasCache = | ||
htmlCache.get(`${path}/index.html`) ?? | ||
(await fileExists(`${path}/index.html`)))) { | ||
htmlCache.get(`${path}${sep}index.html`) ?? | ||
(await fileExists(`${path}${sep}index.html`)))) { | ||
if (hasCache === undefined) | ||
htmlCache.set(`${path}/index.html`, true); | ||
file = Bun.file(`${path}/index.html`); | ||
htmlCache.set(`${path}${sep}index.html`, true); | ||
file = Bun.file(`${path}${sep}index.html`); | ||
} | ||
else { | ||
if (indexHTML && hasCache === undefined) | ||
htmlCache.set(`${path}/index.html`, false); | ||
htmlCache.set(`${path}${sep}index.html`, false); | ||
throw new NotFoundError(); | ||
@@ -187,3 +204,5 @@ } | ||
headers['Etag'] = etag; | ||
headers['Cache-Control'] = 'public, max-age=0'; | ||
headers['Cache-Control'] = directive; | ||
if (maxAge !== null) | ||
headers['Cache-Control'] += `, max-age=${maxAge}`; | ||
return new Response(file, { | ||
@@ -190,0 +209,0 @@ headers |
{ | ||
"name": "@elysiajs/static", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"author": { | ||
@@ -5,0 +5,0 @@ "name": "saltyAom", |
@@ -15,5 +15,4 @@ import { BunFile } from 'bun' | ||
headers['cache-control'].indexOf('no-cache') !== -1 | ||
) { | ||
) | ||
return false | ||
} | ||
@@ -24,19 +23,11 @@ // if-none-match | ||
if (ifNoneMatch === '*') { | ||
return true | ||
} | ||
if (ifNoneMatch === '*') return true | ||
if (ifNoneMatch === null) { | ||
return false | ||
} | ||
if (ifNoneMatch === null) return false | ||
if (typeof etag !== 'string') { | ||
return false | ||
} | ||
if (typeof etag !== 'string') return false | ||
const isMatching = ifNoneMatch === etag | ||
if (isMatching) { | ||
return true | ||
} | ||
if (isMatching) return true | ||
@@ -68,5 +59,4 @@ /** | ||
lastModified.getTime() <= Date.parse(ifModifiedSince) | ||
) { | ||
) | ||
return true | ||
} | ||
} | ||
@@ -73,0 +63,0 @@ |
import { Elysia, NotFoundError } from 'elysia' | ||
import { readdir, stat } from 'fs/promises' | ||
import { resolve, resolve as resolveFn, join } from 'path' | ||
import { resolve, resolve as resolveFn, join, sep } from 'path' | ||
import Cache from 'node-cache' | ||
@@ -10,3 +10,8 @@ | ||
const fileExists = (path: string) => stat(path).then(() => true, () => false) | ||
const URL_PATH_SEP = '/' | ||
const fileExists = (path: string) => | ||
stat(path).then( | ||
() => true, | ||
() => false | ||
) | ||
@@ -39,3 +44,3 @@ const statCache = new Cache({ | ||
files.map(async (name) => { | ||
const file = dir + '/' + name | ||
const file = dir + sep + name | ||
const stats = await stat(file) | ||
@@ -64,2 +69,4 @@ | ||
noCache = false, | ||
maxAge = 86400, | ||
directive = 'public', | ||
indexHTML = true | ||
@@ -129,2 +136,30 @@ }: { | ||
/** | ||
* @default public | ||
* | ||
* directive for Cache-Control header | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#directives | ||
*/ | ||
directive?: | ||
| 'public' | ||
| 'private' | ||
| 'must-revalidate' | ||
| 'no-cache' | ||
| 'no-store' | ||
| 'no-transform' | ||
| 'proxy-revalidate' | ||
| 'immutable' | ||
/** | ||
* @default 86400 | ||
* | ||
* Specifies the maximum amount of time in seconds, a resource will be considered fresh. | ||
* This freshness lifetime is calculated relative to the time of the request. | ||
* This setting helps control browser caching behavior. | ||
* A `maxAge` of 0 will prevent caching, requiring requests to validate with the server before use. | ||
*/ | ||
maxAge?: number | null | ||
/** | ||
* | ||
*/ | ||
/** | ||
* @default true | ||
@@ -150,4 +185,5 @@ * | ||
const files = await listFiles(resolveFn(assets)) | ||
const isFSSepUnsafe = sep !== URL_PATH_SEP | ||
if (prefix === '/') prefix = '' as Prefix | ||
if (prefix === URL_PATH_SEP) prefix = '' as Prefix | ||
@@ -172,5 +208,8 @@ const shouldIgnore = (file: string) => { | ||
noExtension, | ||
enableDecodeURI, | ||
resolve: resolve.toString(), | ||
headers, | ||
noCache, | ||
maxAge, | ||
directive, | ||
indexHTML | ||
@@ -190,3 +229,3 @@ } | ||
.replace(resolve(), '') | ||
.replace(`${assets}/`, '') | ||
.replace(`${assets}${sep}`, '') | ||
@@ -203,4 +242,8 @@ if (noExtension) { | ||
const pathName = isFSSepUnsafe | ||
? prefix + fileName.split(sep).join(URL_PATH_SEP) | ||
: join(prefix, fileName) | ||
app.get( | ||
join(prefix, fileName), | ||
pathName, | ||
noCache | ||
@@ -219,3 +262,5 @@ ? new Response(file, { | ||
headers['Etag'] = etag | ||
headers['Cache-Control'] = 'public, max-age=0' | ||
headers['Cache-Control'] = directive | ||
if (maxAge !== null) | ||
headers['Cache-Control'] += `, max-age=${maxAge}` | ||
@@ -228,5 +273,5 @@ return new Response(file, { | ||
if (indexHTML && fileName.endsWith('/index.html')) | ||
if (indexHTML && pathName.endsWith('/index.html')) | ||
app.get( | ||
join(prefix, fileName.replace('/index.html', '')), | ||
join(prefix, pathName.replace('/index.html', '')), | ||
noCache | ||
@@ -237,3 +282,3 @@ ? new Response(file, { | ||
: async ({ headers: reqHeaders }) => { | ||
if (await isCached(reqHeaders, etag, filePath)) { | ||
if (await isCached(reqHeaders, etag, pathName)) { | ||
return new Response(null, { | ||
@@ -246,3 +291,7 @@ status: 304, | ||
headers['Etag'] = etag | ||
headers['Cache-Control'] = 'public, max-age=0' | ||
headers['Cache-Control'] = directive | ||
if (maxAge !== null) | ||
headers[ | ||
'Cache-Control' | ||
] += `, max-age=${maxAge}` | ||
@@ -264,6 +313,11 @@ return new Response(file, { | ||
async ({ params, headers: reqHeaders }) => { | ||
const path = enableDecodeURI | ||
let path = enableDecodeURI | ||
? decodeURI(`${assets}/${decodeURI(params['*'])}`) | ||
: `${assets}/${params['*']}` | ||
// Handle varying filepath separators | ||
if (isFSSepUnsafe) { | ||
path = path.replace(URL_PATH_SEP, sep) | ||
} | ||
// Note that path must match the system separator | ||
if (shouldIgnore(path)) throw new NotFoundError() | ||
@@ -291,17 +345,19 @@ | ||
htmlCache.get<boolean>( | ||
`${path}/index.html` | ||
`${path}${sep}index.html` | ||
) ?? | ||
(await fileExists(`${path}/index.html`))) | ||
(await fileExists( | ||
`${path}${sep}index.html` | ||
))) | ||
) { | ||
if (hasCache === undefined) | ||
htmlCache.set( | ||
`${path}/index.html`, | ||
`${path}${sep}index.html`, | ||
true | ||
) | ||
file = Bun.file(`${path}/index.html`) | ||
file = Bun.file(`${path}${sep}index.html`) | ||
} else { | ||
if (indexHTML && hasCache === undefined) | ||
htmlCache.set( | ||
`${path}/index.html`, | ||
`${path}${sep}index.html`, | ||
false | ||
@@ -331,3 +387,5 @@ ) | ||
headers['Etag'] = etag | ||
headers['Cache-Control'] = 'public, max-age=0' | ||
headers['Cache-Control'] = directive | ||
if (maxAge !== null) | ||
headers['Cache-Control'] += `, max-age=${maxAge}` | ||
@@ -334,0 +392,0 @@ return new Response(file, { |
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
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
43685
1074