express-performance-toolkit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +217 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +182 -0
- package/dist/cache.js.map +1 -0
- package/dist/compression.d.ts +7 -0
- package/dist/compression.d.ts.map +1 -0
- package/dist/compression.js +26 -0
- package/dist/compression.js.map +1 -0
- package/dist/dashboard/dashboard.html +756 -0
- package/dist/dashboard/dashboardRouter.d.ts +9 -0
- package/dist/dashboard/dashboardRouter.d.ts.map +1 -0
- package/dist/dashboard/dashboardRouter.js +71 -0
- package/dist/dashboard/dashboardRouter.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +130 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +70 -0
- package/dist/logger.js.map +1 -0
- package/dist/queryHelper.d.ts +8 -0
- package/dist/queryHelper.d.ts.map +1 -0
- package/dist/queryHelper.js +39 -0
- package/dist/queryHelper.js.map +1 -0
- package/dist/store.d.ts +24 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +108 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/example/server.ts +126 -0
- package/example/tsconfig.json +17 -0
- package/jest.config.js +10 -0
- package/package.json +57 -0
- package/src/cache.ts +228 -0
- package/src/compression.ts +25 -0
- package/src/dashboard/dashboard.html +756 -0
- package/src/dashboard/dashboardRouter.ts +45 -0
- package/src/index.ts +141 -0
- package/src/logger.ts +83 -0
- package/src/queryHelper.ts +49 -0
- package/src/store.ts +134 -0
- package/src/types.ts +155 -0
- package/tests/cache.test.ts +76 -0
- package/tests/integration.test.ts +124 -0
- package/tests/store.test.ts +103 -0
- package/tsconfig.json +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# ⚡ Express Performance Toolkit
|
|
2
|
+
|
|
3
|
+
A powerful, all-in-one Express middleware that automatically optimizes your app with **request caching**, **response compression**, **slow API detection**, **query optimization helpers**, and a stunning **real-time performance dashboard**.
|
|
4
|
+
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- 🚀 **Request Caching** — In-memory LRU cache with TTL (+ optional Redis adapter)
|
|
13
|
+
- 🗜️ **Response Compression** — Gzip/deflate with configurable thresholds
|
|
14
|
+
- 🔥 **Slow API Detection** — Flag & log requests exceeding response time thresholds
|
|
15
|
+
- 🔍 **Query Optimization** — N+1 query detection with `req.perfToolkit.trackQuery()`
|
|
16
|
+
- 📊 **Real-time Dashboard** — Beautiful dark-themed dashboard at `/__perf`
|
|
17
|
+
- 📝 **Structured Logging** — Per-request timing, status codes, cache status
|
|
18
|
+
- 🎯 **Fully Typed** — Written in TypeScript with complete type definitions
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install express-performance-toolkit
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 🚀 Quick Start
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import express from 'express';
|
|
34
|
+
import { performanceToolkit } from 'express-performance-toolkit';
|
|
35
|
+
|
|
36
|
+
const app = express();
|
|
37
|
+
|
|
38
|
+
const toolkit = performanceToolkit({
|
|
39
|
+
cache: true,
|
|
40
|
+
logSlowRequests: true,
|
|
41
|
+
dashboard: true,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Apply the composable middleware
|
|
45
|
+
app.use(toolkit.middleware);
|
|
46
|
+
|
|
47
|
+
// Mount the performance dashboard
|
|
48
|
+
app.use('/__perf', toolkit.dashboardRouter);
|
|
49
|
+
|
|
50
|
+
app.get('/api/users', (req, res) => {
|
|
51
|
+
res.json({ users: [{ id: 1, name: 'Alice' }] });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
app.listen(3000, () => {
|
|
55
|
+
console.log('Server running on http://localhost:3000');
|
|
56
|
+
console.log('Dashboard at http://localhost:3000/__perf');
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## ⚙️ Configuration
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
const toolkit = performanceToolkit({
|
|
66
|
+
// Cache — boolean or CacheOptions
|
|
67
|
+
cache: {
|
|
68
|
+
ttl: 60000, // Cache TTL in ms (default: 60000)
|
|
69
|
+
maxSize: 100, // Max LRU entries (default: 100)
|
|
70
|
+
methods: ['GET'], // HTTP methods to cache (default: ['GET'])
|
|
71
|
+
exclude: ['/health', /^\/admin/], // URL patterns to skip
|
|
72
|
+
redis: { // Optional Redis adapter (requires ioredis)
|
|
73
|
+
host: 'localhost',
|
|
74
|
+
port: 6379,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Compression — boolean or CompressionOptions
|
|
79
|
+
compression: {
|
|
80
|
+
threshold: 1024, // Min response size to compress (default: 1024 bytes)
|
|
81
|
+
level: 6, // Compression level 1-9 (default: 6)
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// Logger / Slow Detection — boolean or LoggerOptions
|
|
85
|
+
logSlowRequests: {
|
|
86
|
+
slowThreshold: 1000, // Flag requests slower than this (default: 1000ms)
|
|
87
|
+
console: true, // Log to console (default: true)
|
|
88
|
+
formatter: (entry) => `${entry.method} ${entry.path} ${entry.responseTime}ms`,
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// Query Helper — boolean or QueryHelperOptions
|
|
92
|
+
queryHelper: {
|
|
93
|
+
threshold: 10, // Warn after this many queries/request (default: 10)
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Dashboard — boolean or DashboardOptions
|
|
97
|
+
dashboard: {
|
|
98
|
+
path: '/__perf', // Dashboard mount path (default: '/__perf')
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
maxLogs: 1000, // Max log entries in memory (default: 1000)
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 📊 Dashboard
|
|
108
|
+
|
|
109
|
+
Access the performance dashboard at `http://localhost:3000/__perf`:
|
|
110
|
+
|
|
111
|
+
- **Real-time stats** — Total requests, avg response time, slow request count
|
|
112
|
+
- **Cache performance** — Hit/miss ratio donut chart
|
|
113
|
+
- **Status code breakdown** — Visual bar chart
|
|
114
|
+
- **Slowest routes** — Table of routes sorted by average response time
|
|
115
|
+
- **Request log** — Filterable log with timing, cache status, and 🔥 slow flags
|
|
116
|
+
|
|
117
|
+
### Dashboard API
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
GET /__perf → Dashboard HTML
|
|
121
|
+
GET /__perf/api/metrics → JSON metrics snapshot
|
|
122
|
+
POST /__perf/api/reset → Reset all metrics
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 🔍 Query Tracking
|
|
128
|
+
|
|
129
|
+
Track database queries per request to detect N+1 patterns:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
app.get('/api/posts', async (req, res) => {
|
|
133
|
+
const posts = await db.getPosts();
|
|
134
|
+
|
|
135
|
+
for (const post of posts) {
|
|
136
|
+
req.perfToolkit?.trackQuery(`SELECT comments WHERE post_id=${post.id}`);
|
|
137
|
+
post.comments = await db.getComments(post.id);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
res.json(posts);
|
|
141
|
+
});
|
|
142
|
+
// Console: ⚠️ N+1 Alert: GET /api/posts has made 10+ queries
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 🏗️ Programmatic API
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
const toolkit = performanceToolkit({ cache: true });
|
|
151
|
+
|
|
152
|
+
// Access metrics programmatically
|
|
153
|
+
const metrics = toolkit.getMetrics();
|
|
154
|
+
console.log(metrics.totalRequests, metrics.avgResponseTime);
|
|
155
|
+
|
|
156
|
+
// Reset metrics
|
|
157
|
+
toolkit.resetMetrics();
|
|
158
|
+
|
|
159
|
+
// Manual cache control
|
|
160
|
+
toolkit.cache?.clear();
|
|
161
|
+
toolkit.cache?.delete('GET:/api/users');
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 🧪 Running Tests
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npm test
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 🏃 Running the Example
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx ts-node example/server.ts
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Then visit:
|
|
181
|
+
- `http://localhost:3000/api/users` — fast, cached response
|
|
182
|
+
- `http://localhost:3000/api/slow` — triggers slow request alert
|
|
183
|
+
- `http://localhost:3000/__perf` — performance dashboard
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 📁 Project Structure
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
express-performance-toolkit/
|
|
191
|
+
├── src/
|
|
192
|
+
│ ├── index.ts # Main entrypoint & performanceToolkit()
|
|
193
|
+
│ ├── types.ts # TypeScript interfaces
|
|
194
|
+
│ ├── store.ts # Metrics store (ring buffer + counters)
|
|
195
|
+
│ ├── cache.ts # LRU cache + Redis adapter
|
|
196
|
+
│ ├── compression.ts # Compression middleware wrapper
|
|
197
|
+
│ ├── logger.ts # Request timing & slow detection
|
|
198
|
+
│ ├── queryHelper.ts # N+1 query detection
|
|
199
|
+
│ └── dashboard/
|
|
200
|
+
│ ├── dashboardRouter.ts # Dashboard Express router
|
|
201
|
+
│ └── dashboard.html # Dashboard UI
|
|
202
|
+
├── tests/
|
|
203
|
+
│ ├── cache.test.ts
|
|
204
|
+
│ ├── store.test.ts
|
|
205
|
+
│ └── integration.test.ts
|
|
206
|
+
├── example/
|
|
207
|
+
│ └── server.ts # Example Express app
|
|
208
|
+
├── package.json
|
|
209
|
+
├── tsconfig.json
|
|
210
|
+
└── jest.config.js
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 📄 License
|
|
216
|
+
|
|
217
|
+
MIT
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CacheOptions, CacheEntry, CacheAdapter, CacheMiddleware } from "./types";
|
|
2
|
+
import { MetricsStore } from "./store";
|
|
3
|
+
/**
|
|
4
|
+
* In-memory LRU cache with TTL support.
|
|
5
|
+
*/
|
|
6
|
+
export declare class LRUCache<T = CacheEntry> implements CacheAdapter<T> {
|
|
7
|
+
private maxSize;
|
|
8
|
+
private ttl;
|
|
9
|
+
private cache;
|
|
10
|
+
constructor(options?: {
|
|
11
|
+
maxSize?: number;
|
|
12
|
+
ttl?: number;
|
|
13
|
+
});
|
|
14
|
+
get(key: string): T | null;
|
|
15
|
+
set(key: string, value: T): void;
|
|
16
|
+
has(key: string): boolean;
|
|
17
|
+
delete(key: string): boolean;
|
|
18
|
+
clear(): void;
|
|
19
|
+
get size(): number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create cache middleware.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createCacheMiddleware(options?: CacheOptions, store?: MetricsStore): CacheMiddleware;
|
|
25
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EAEZ,eAAe,EAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;GAEG;AACH,qBAAa,QAAQ,CAAC,CAAC,GAAG,UAAU,CAAE,YAAW,YAAY,CAAC,CAAC,CAAC;IAC9D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,KAAK,CAAgC;gBAEjC,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAO;IAM5D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAgB1B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAehC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AA6CD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,YAAiB,EAC1B,KAAK,CAAC,EAAE,YAAY,GACnB,eAAe,CA0GjB"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LRUCache = void 0;
|
|
4
|
+
exports.createCacheMiddleware = createCacheMiddleware;
|
|
5
|
+
/**
|
|
6
|
+
* In-memory LRU cache with TTL support.
|
|
7
|
+
*/
|
|
8
|
+
class LRUCache {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.maxSize = options.maxSize || 100;
|
|
11
|
+
this.ttl = options.ttl || 60000;
|
|
12
|
+
this.cache = new Map();
|
|
13
|
+
}
|
|
14
|
+
get(key) {
|
|
15
|
+
const entry = this.cache.get(key);
|
|
16
|
+
if (!entry)
|
|
17
|
+
return null;
|
|
18
|
+
// Check TTL
|
|
19
|
+
if (Date.now() - entry.createdAt > this.ttl) {
|
|
20
|
+
this.cache.delete(key);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// Move to end (most recently used)
|
|
24
|
+
this.cache.delete(key);
|
|
25
|
+
this.cache.set(key, entry);
|
|
26
|
+
return entry.value;
|
|
27
|
+
}
|
|
28
|
+
set(key, value) {
|
|
29
|
+
// Remove oldest if at capacity
|
|
30
|
+
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
31
|
+
const firstKey = this.cache.keys().next().value;
|
|
32
|
+
if (firstKey !== undefined) {
|
|
33
|
+
this.cache.delete(firstKey);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
this.cache.set(key, {
|
|
37
|
+
value,
|
|
38
|
+
createdAt: Date.now(),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
has(key) {
|
|
42
|
+
return this.get(key) !== null;
|
|
43
|
+
}
|
|
44
|
+
delete(key) {
|
|
45
|
+
return this.cache.delete(key);
|
|
46
|
+
}
|
|
47
|
+
clear() {
|
|
48
|
+
this.cache.clear();
|
|
49
|
+
}
|
|
50
|
+
get size() {
|
|
51
|
+
return this.cache.size;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.LRUCache = LRUCache;
|
|
55
|
+
/**
|
|
56
|
+
* Create a Redis cache adapter (requires ioredis as peer dependency).
|
|
57
|
+
*/
|
|
58
|
+
function createRedisAdapter(redisConfig) {
|
|
59
|
+
try {
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
61
|
+
const Redis = require("ioredis");
|
|
62
|
+
const client = new Redis(redisConfig);
|
|
63
|
+
const prefix = redisConfig.prefix || "ept:";
|
|
64
|
+
const ttl = redisConfig.ttl || 60;
|
|
65
|
+
return {
|
|
66
|
+
async get(key) {
|
|
67
|
+
const data = await client.get(`${prefix}${key}`);
|
|
68
|
+
return data ? JSON.parse(data) : null;
|
|
69
|
+
},
|
|
70
|
+
async set(key, value) {
|
|
71
|
+
await client.setex(`${prefix}${key}`, ttl, JSON.stringify(value));
|
|
72
|
+
},
|
|
73
|
+
async has(key) {
|
|
74
|
+
return (await client.exists(`${prefix}${key}`)) === 1;
|
|
75
|
+
},
|
|
76
|
+
async delete(key) {
|
|
77
|
+
await client.del(`${prefix}${key}`);
|
|
78
|
+
},
|
|
79
|
+
async clear() {
|
|
80
|
+
const keys = await client.keys(`${prefix}*`);
|
|
81
|
+
if (keys.length > 0)
|
|
82
|
+
await client.del(...keys);
|
|
83
|
+
},
|
|
84
|
+
get size() {
|
|
85
|
+
return -1; // Cannot easily get size from Redis
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
console.warn("[express-performance-toolkit] ioredis not installed. Falling back to in-memory cache.");
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create cache middleware.
|
|
96
|
+
*/
|
|
97
|
+
function createCacheMiddleware(options = {}, store) {
|
|
98
|
+
const { ttl = 60000, maxSize = 100, exclude = [], redis = null, methods = ["GET"], } = options;
|
|
99
|
+
let cacheAdapter;
|
|
100
|
+
if (redis) {
|
|
101
|
+
const redisAdapter = createRedisAdapter({
|
|
102
|
+
...redis,
|
|
103
|
+
ttl: Math.ceil(ttl / 1000),
|
|
104
|
+
});
|
|
105
|
+
cacheAdapter = redisAdapter || new LRUCache({ maxSize, ttl });
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
cacheAdapter = new LRUCache({ maxSize, ttl });
|
|
109
|
+
}
|
|
110
|
+
function shouldExclude(url) {
|
|
111
|
+
return exclude.some((pattern) => {
|
|
112
|
+
if (pattern instanceof RegExp)
|
|
113
|
+
return pattern.test(url);
|
|
114
|
+
return url.includes(pattern);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function getCacheKey(req) {
|
|
118
|
+
return `${req.method}:${req.originalUrl || req.url}`;
|
|
119
|
+
}
|
|
120
|
+
const handler = async (req, res, next) => {
|
|
121
|
+
// Only cache specified methods
|
|
122
|
+
if (!methods.includes(req.method)) {
|
|
123
|
+
return next();
|
|
124
|
+
}
|
|
125
|
+
// Check exclusion patterns
|
|
126
|
+
const url = req.originalUrl || req.url;
|
|
127
|
+
if (shouldExclude(url)) {
|
|
128
|
+
return next();
|
|
129
|
+
}
|
|
130
|
+
const key = getCacheKey(req);
|
|
131
|
+
try {
|
|
132
|
+
const cached = await cacheAdapter.get(key);
|
|
133
|
+
if (cached) {
|
|
134
|
+
if (store)
|
|
135
|
+
store.recordCacheHit();
|
|
136
|
+
const entry = cached;
|
|
137
|
+
res.set("X-Cache", "HIT");
|
|
138
|
+
res.set("Content-Type", entry.contentType || "application/json");
|
|
139
|
+
res.status(entry.statusCode || 200).send(entry.body);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Cache read failed — continue to handler
|
|
145
|
+
}
|
|
146
|
+
if (store)
|
|
147
|
+
store.recordCacheMiss();
|
|
148
|
+
res.set("X-Cache", "MISS");
|
|
149
|
+
// Intercept response to cache it
|
|
150
|
+
const originalSend = res.send.bind(res);
|
|
151
|
+
res.send = function (body) {
|
|
152
|
+
// Only cache successful responses
|
|
153
|
+
if (res.statusCode >= 200 && res.statusCode < 400) {
|
|
154
|
+
const entry = {
|
|
155
|
+
body: body,
|
|
156
|
+
statusCode: res.statusCode,
|
|
157
|
+
contentType: res.get("Content-Type"),
|
|
158
|
+
};
|
|
159
|
+
try {
|
|
160
|
+
cacheAdapter.set(key, entry);
|
|
161
|
+
if (store &&
|
|
162
|
+
typeof cacheAdapter.size === "number" &&
|
|
163
|
+
cacheAdapter.size >= 0) {
|
|
164
|
+
store.setCacheSize(cacheAdapter.size);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Cache write failed — ignore
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return originalSend(body);
|
|
172
|
+
};
|
|
173
|
+
next();
|
|
174
|
+
};
|
|
175
|
+
// Build the CacheMiddleware with attached control methods
|
|
176
|
+
const middleware = handler;
|
|
177
|
+
middleware.clear = () => cacheAdapter.clear();
|
|
178
|
+
middleware.delete = (key) => cacheAdapter.delete(key);
|
|
179
|
+
middleware.adapter = cacheAdapter;
|
|
180
|
+
return middleware;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { CompressionOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Create compression middleware with sensible defaults.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createCompressionMiddleware(options?: CompressionOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
7
|
+
//# sourceMappingURL=compression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compression.d.ts","sourceRoot":"","sources":["../src/compression.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE7C;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,kBAAuB,GAC/B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAe3D"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createCompressionMiddleware = createCompressionMiddleware;
|
|
7
|
+
const compression_1 = __importDefault(require("compression"));
|
|
8
|
+
/**
|
|
9
|
+
* Create compression middleware with sensible defaults.
|
|
10
|
+
*/
|
|
11
|
+
function createCompressionMiddleware(options = {}) {
|
|
12
|
+
const { threshold = 1024, level = 6 } = options;
|
|
13
|
+
return (0, compression_1.default)({
|
|
14
|
+
threshold,
|
|
15
|
+
level,
|
|
16
|
+
filter: (req, res) => {
|
|
17
|
+
// Don't compress if the client didn't request it
|
|
18
|
+
if (req.headers['x-no-compression']) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
// Use compression's default filter
|
|
22
|
+
return compression_1.default.filter(req, res);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=compression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compression.js","sourceRoot":"","sources":["../src/compression.ts"],"names":[],"mappings":";;;;;AAOA,kEAiBC;AAvBD,8DAAgD;AAGhD;;GAEG;AACH,SAAgB,2BAA2B,CACzC,UAA8B,EAAE;IAEhC,MAAM,EAAE,SAAS,GAAG,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IAEhD,OAAO,IAAA,qBAAqB,EAAC;QAC3B,SAAS;QACT,KAAK;QACL,MAAM,EAAE,CAAC,GAAY,EAAE,GAAa,EAAW,EAAE;YAC/C,iDAAiD;YACjD,IAAI,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACpC,OAAO,KAAK,CAAC;YACf,CAAC;YACD,mCAAmC;YACnC,OAAO,qBAAqB,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAChD,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|