Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@elysiajs/static

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@elysiajs/static - npm Package Compare versions

Comparing version 0.8.0 to 0.8.1

.eslintrc

5

dist/cjs/index.d.ts
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: {};

145

dist/cjs/index.js
"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 @@ }

{
"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"
}
}
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 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc