@lensjs/express 1.0.12 → 1.2.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 +93 -0
- package/dist/adapter.cjs +29 -9
- package/dist/adapter.d.cts +1 -0
- package/dist/adapter.d.ts +1 -0
- package/dist/adapter.js +33 -11
- package/dist/index.cjs +85 -18
- package/dist/index.d.cts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +90 -20
- package/dist/types/index.d.cts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/package.json +10 -7
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# @lensjs/express
|
|
2
|
+
|
|
3
|
+
Express.js adapter for Lens. This package provides middleware and integration points to connect your Express application with the Lens monitoring and debugging tool. It enables automatic logging of requests, queries (via `@lensjs/watchers`), and cache events.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
* **`lens(config: ExpressAdapterConfig)` function**: The main entry point to initialize and integrate Lens with an Express application.
|
|
8
|
+
* **`ExpressAdapter` class**: Extends `LensAdapter` from `@lensjs/core` to provide Express-specific implementations for setting up watchers, registering routes, and serving the Lens UI.
|
|
9
|
+
* **Request Watching**: Automatically captures incoming request details (method, path, headers, body, status, duration, IP) and logs them.
|
|
10
|
+
* **Query Watching**: Integrates with `@lensjs/watchers` to capture database queries from various ORMs (Kysely, Prisma, Sequelize) if configured.
|
|
11
|
+
* **Cache Watching**: Integrates with `@lensjs/watchers` to capture cache events if configured.
|
|
12
|
+
* **Exception Watching**: Captures and logs unhandled exceptions and errors within your Express application.
|
|
13
|
+
* **UI Serving**: Serves the Lens UI within your Express application at a configurable path.
|
|
14
|
+
* **Configurable Paths**: Allows specifying base paths, ignored paths, and only paths for request watching.
|
|
15
|
+
* **Body Purging**: Prevents sensitive information from being logged in responses by purging certain body types (e.g., file paths, binary data).
|
|
16
|
+
* **Authentication/User Context**: Supports optional `isAuthenticated` and `getUser` functions to associate request logs with authenticated users.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @lensjs/express
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage Example
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import express from 'express';
|
|
28
|
+
import { lens } from '@lensjs/express';
|
|
29
|
+
import { createKyselyHandler } from '@lensjs/watchers/query/kysely'; // Example for Kysely
|
|
30
|
+
|
|
31
|
+
const app = express();
|
|
32
|
+
app.use(express.json()); // Enable JSON body parsing
|
|
33
|
+
|
|
34
|
+
// Example Kysely setup (replace with your actual Kysely instance)
|
|
35
|
+
const db = {
|
|
36
|
+
// ... your Kysely instance methods
|
|
37
|
+
// Mocking a Kysely-like interface for demonstration
|
|
38
|
+
on: (event: string, callback: (payload: any) => void) => {
|
|
39
|
+
if (event === 'query') {
|
|
40
|
+
// Simulate a query event
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
callback({
|
|
43
|
+
level: 'query',
|
|
44
|
+
query: { sql: 'SELECT * FROM users', parameters: [] },
|
|
45
|
+
queryDurationMillis: 15.2,
|
|
46
|
+
error: undefined,
|
|
47
|
+
});
|
|
48
|
+
}, 100);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Initialize Lens with Express
|
|
54
|
+
lens({
|
|
55
|
+
app,
|
|
56
|
+
appName: 'My Express App',
|
|
57
|
+
enabled: true, // Set to false in production
|
|
58
|
+
path: '/lens-dashboard', // Access Lens UI at /lens-dashboard
|
|
59
|
+
requestWatcherEnabled: true,
|
|
60
|
+
cacheWatcherEnabled: true,
|
|
61
|
+
exceptionWatcherEnabled: true,
|
|
62
|
+
queryWatcher: {
|
|
63
|
+
enabled: true,
|
|
64
|
+
handler: createKyselyHandler({ provider: 'sqlite' }), // Or createPrismaHandler, createSequelizeHandler
|
|
65
|
+
},
|
|
66
|
+
// Optional: Integrate with your authentication system
|
|
67
|
+
isAuthenticated: async (req) => {
|
|
68
|
+
return !!req.headers.authorization;
|
|
69
|
+
},
|
|
70
|
+
getUser: async (req) => {
|
|
71
|
+
// Return user details based on your auth system
|
|
72
|
+
return { id: '1', name: 'Authenticated User' };
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Your Express routes
|
|
77
|
+
app.get('/', (req, res) => {
|
|
78
|
+
res.send('Hello World!');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
app.get('/users', async (req, res) => {
|
|
82
|
+
// Simulate a database query
|
|
83
|
+
db.on('query', ({ query, queryDurationMillis }) => {
|
|
84
|
+
console.log(`Simulated query: ${query.sql}, Duration: ${queryDurationMillis}ms`);
|
|
85
|
+
});
|
|
86
|
+
res.json([{ id: 1, name: 'Alice' }]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
app.listen(3000, () => {
|
|
90
|
+
console.log('Express app listening on port 3000');
|
|
91
|
+
console.log('Lens UI available at http://localhost:3000/lens-dashboard');
|
|
92
|
+
});
|
|
93
|
+
```
|
package/dist/adapter.cjs
CHANGED
|
@@ -53,10 +53,19 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
53
53
|
for (const watcher of this.getWatchers()) {
|
|
54
54
|
switch (watcher.name) {
|
|
55
55
|
case import_core.WatcherTypeEnum.REQUEST:
|
|
56
|
-
this.
|
|
56
|
+
if (this.config.requestWatcherEnabled) {
|
|
57
|
+
this.watchRequests(watcher);
|
|
58
|
+
}
|
|
57
59
|
break;
|
|
58
60
|
case import_core.WatcherTypeEnum.QUERY:
|
|
59
|
-
|
|
61
|
+
if (this.config.queryWatcher.enabled) {
|
|
62
|
+
void this.watchQueries(watcher);
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
case import_core.WatcherTypeEnum.CACHE:
|
|
66
|
+
if (this.config.cacheWatcherEnabled) {
|
|
67
|
+
void this.watchCache(watcher);
|
|
68
|
+
}
|
|
60
69
|
break;
|
|
61
70
|
}
|
|
62
71
|
}
|
|
@@ -90,6 +99,12 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
90
99
|
return res.sendFile(path.join(uiPath, "index.html"));
|
|
91
100
|
});
|
|
92
101
|
}
|
|
102
|
+
async watchCache(watcher) {
|
|
103
|
+
if (!this.config.cacheWatcherEnabled) return;
|
|
104
|
+
import_core.lensEmitter.on("cache", async (data) => {
|
|
105
|
+
await watcher?.log(data);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
93
108
|
async watchQueries(watcher) {
|
|
94
109
|
if (!this.config.queryWatcher.enabled) return;
|
|
95
110
|
const handler = this.config.queryWatcher.handler;
|
|
@@ -98,7 +113,7 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
98
113
|
const queryPayload = {
|
|
99
114
|
query: query.query,
|
|
100
115
|
duration: query.duration || "0 ms",
|
|
101
|
-
createdAt: query.createdAt || (0, import_date.
|
|
116
|
+
createdAt: query.createdAt || (0, import_date.nowISO)(),
|
|
102
117
|
type: query.type
|
|
103
118
|
};
|
|
104
119
|
await watcher?.log({
|
|
@@ -111,12 +126,17 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
111
126
|
if (!this.config.requestWatcherEnabled) return;
|
|
112
127
|
this.app.use((req, res, next) => {
|
|
113
128
|
if (this.shouldIgnorePath(req.path)) return next();
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
129
|
+
const context = {
|
|
130
|
+
requestId: import_core.lensUtils.generateRandomUuid()
|
|
131
|
+
};
|
|
132
|
+
import_core.lensContext.run(context, () => {
|
|
133
|
+
const start = process.hrtime();
|
|
134
|
+
this.patchResponseMethods(res);
|
|
135
|
+
res.on("finish", async () => {
|
|
136
|
+
await this.finalizeRequestLog(req, res, requestWatcher, start);
|
|
137
|
+
});
|
|
138
|
+
next();
|
|
118
139
|
});
|
|
119
|
-
next();
|
|
120
140
|
});
|
|
121
141
|
}
|
|
122
142
|
patchResponseMethods(res) {
|
|
@@ -158,7 +178,7 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
158
178
|
const duration = import_core.lensUtils.prettyHrTime(process.hrtime(start));
|
|
159
179
|
const logPayload = {
|
|
160
180
|
request: {
|
|
161
|
-
id: import_core.lensUtils.generateRandomUuid(),
|
|
181
|
+
id: import_core.lensContext.getStore()?.requestId || import_core.lensUtils.generateRandomUuid(),
|
|
162
182
|
method: req.method,
|
|
163
183
|
duration,
|
|
164
184
|
path: req.originalUrl,
|
package/dist/adapter.d.cts
CHANGED
|
@@ -13,6 +13,7 @@ declare class ExpressAdapter extends LensAdapter {
|
|
|
13
13
|
setup(): void;
|
|
14
14
|
registerRoutes(routes: RouteDefinition[]): void;
|
|
15
15
|
serveUI(uiPath: string, spaRoute: string, _dataToInject: Record<string, any>): void;
|
|
16
|
+
private watchCache;
|
|
16
17
|
private watchQueries;
|
|
17
18
|
private watchRequests;
|
|
18
19
|
private patchResponseMethods;
|
package/dist/adapter.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ declare class ExpressAdapter extends LensAdapter {
|
|
|
13
13
|
setup(): void;
|
|
14
14
|
registerRoutes(routes: RouteDefinition[]): void;
|
|
15
15
|
serveUI(uiPath: string, spaRoute: string, _dataToInject: Record<string, any>): void;
|
|
16
|
+
private watchCache;
|
|
16
17
|
private watchQueries;
|
|
17
18
|
private watchRequests;
|
|
18
19
|
private patchResponseMethods;
|
package/dist/adapter.js
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
LensAdapter,
|
|
4
4
|
lensUtils,
|
|
5
|
-
WatcherTypeEnum
|
|
5
|
+
WatcherTypeEnum,
|
|
6
|
+
lensContext,
|
|
7
|
+
lensEmitter
|
|
6
8
|
} from "@lensjs/core";
|
|
7
9
|
import * as path from "path";
|
|
8
10
|
import fs from "fs";
|
|
9
11
|
import express from "express";
|
|
10
|
-
import { nowISO
|
|
12
|
+
import { nowISO } from "@lensjs/date";
|
|
11
13
|
var ExpressAdapter = class extends LensAdapter {
|
|
12
14
|
app;
|
|
13
15
|
config;
|
|
@@ -23,10 +25,19 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
23
25
|
for (const watcher of this.getWatchers()) {
|
|
24
26
|
switch (watcher.name) {
|
|
25
27
|
case WatcherTypeEnum.REQUEST:
|
|
26
|
-
this.
|
|
28
|
+
if (this.config.requestWatcherEnabled) {
|
|
29
|
+
this.watchRequests(watcher);
|
|
30
|
+
}
|
|
27
31
|
break;
|
|
28
32
|
case WatcherTypeEnum.QUERY:
|
|
29
|
-
|
|
33
|
+
if (this.config.queryWatcher.enabled) {
|
|
34
|
+
void this.watchQueries(watcher);
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
case WatcherTypeEnum.CACHE:
|
|
38
|
+
if (this.config.cacheWatcherEnabled) {
|
|
39
|
+
void this.watchCache(watcher);
|
|
40
|
+
}
|
|
30
41
|
break;
|
|
31
42
|
}
|
|
32
43
|
}
|
|
@@ -60,6 +71,12 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
60
71
|
return res.sendFile(path.join(uiPath, "index.html"));
|
|
61
72
|
});
|
|
62
73
|
}
|
|
74
|
+
async watchCache(watcher) {
|
|
75
|
+
if (!this.config.cacheWatcherEnabled) return;
|
|
76
|
+
lensEmitter.on("cache", async (data) => {
|
|
77
|
+
await watcher?.log(data);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
63
80
|
async watchQueries(watcher) {
|
|
64
81
|
if (!this.config.queryWatcher.enabled) return;
|
|
65
82
|
const handler = this.config.queryWatcher.handler;
|
|
@@ -68,7 +85,7 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
68
85
|
const queryPayload = {
|
|
69
86
|
query: query.query,
|
|
70
87
|
duration: query.duration || "0 ms",
|
|
71
|
-
createdAt: query.createdAt ||
|
|
88
|
+
createdAt: query.createdAt || nowISO(),
|
|
72
89
|
type: query.type
|
|
73
90
|
};
|
|
74
91
|
await watcher?.log({
|
|
@@ -81,12 +98,17 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
81
98
|
if (!this.config.requestWatcherEnabled) return;
|
|
82
99
|
this.app.use((req, res, next) => {
|
|
83
100
|
if (this.shouldIgnorePath(req.path)) return next();
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
const context = {
|
|
102
|
+
requestId: lensUtils.generateRandomUuid()
|
|
103
|
+
};
|
|
104
|
+
lensContext.run(context, () => {
|
|
105
|
+
const start = process.hrtime();
|
|
106
|
+
this.patchResponseMethods(res);
|
|
107
|
+
res.on("finish", async () => {
|
|
108
|
+
await this.finalizeRequestLog(req, res, requestWatcher, start);
|
|
109
|
+
});
|
|
110
|
+
next();
|
|
88
111
|
});
|
|
89
|
-
next();
|
|
90
112
|
});
|
|
91
113
|
}
|
|
92
114
|
patchResponseMethods(res) {
|
|
@@ -128,7 +150,7 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
128
150
|
const duration = lensUtils.prettyHrTime(process.hrtime(start));
|
|
129
151
|
const logPayload = {
|
|
130
152
|
request: {
|
|
131
|
-
id: lensUtils.generateRandomUuid(),
|
|
153
|
+
id: lensContext.getStore()?.requestId || lensUtils.generateRandomUuid(),
|
|
132
154
|
method: req.method,
|
|
133
155
|
duration,
|
|
134
156
|
path: req.originalUrl,
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
handleExceptions: () => handleExceptions,
|
|
33
34
|
lens: () => lens
|
|
34
35
|
});
|
|
35
36
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -56,10 +57,19 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
56
57
|
for (const watcher of this.getWatchers()) {
|
|
57
58
|
switch (watcher.name) {
|
|
58
59
|
case import_core.WatcherTypeEnum.REQUEST:
|
|
59
|
-
this.
|
|
60
|
+
if (this.config.requestWatcherEnabled) {
|
|
61
|
+
this.watchRequests(watcher);
|
|
62
|
+
}
|
|
60
63
|
break;
|
|
61
64
|
case import_core.WatcherTypeEnum.QUERY:
|
|
62
|
-
|
|
65
|
+
if (this.config.queryWatcher.enabled) {
|
|
66
|
+
void this.watchQueries(watcher);
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
case import_core.WatcherTypeEnum.CACHE:
|
|
70
|
+
if (this.config.cacheWatcherEnabled) {
|
|
71
|
+
void this.watchCache(watcher);
|
|
72
|
+
}
|
|
63
73
|
break;
|
|
64
74
|
}
|
|
65
75
|
}
|
|
@@ -93,6 +103,12 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
93
103
|
return res.sendFile(path.join(uiPath, "index.html"));
|
|
94
104
|
});
|
|
95
105
|
}
|
|
106
|
+
async watchCache(watcher) {
|
|
107
|
+
if (!this.config.cacheWatcherEnabled) return;
|
|
108
|
+
import_core.lensEmitter.on("cache", async (data) => {
|
|
109
|
+
await watcher?.log(data);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
96
112
|
async watchQueries(watcher) {
|
|
97
113
|
if (!this.config.queryWatcher.enabled) return;
|
|
98
114
|
const handler = this.config.queryWatcher.handler;
|
|
@@ -101,7 +117,7 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
101
117
|
const queryPayload = {
|
|
102
118
|
query: query.query,
|
|
103
119
|
duration: query.duration || "0 ms",
|
|
104
|
-
createdAt: query.createdAt || (0, import_date.
|
|
120
|
+
createdAt: query.createdAt || (0, import_date.nowISO)(),
|
|
105
121
|
type: query.type
|
|
106
122
|
};
|
|
107
123
|
await watcher?.log({
|
|
@@ -114,12 +130,17 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
114
130
|
if (!this.config.requestWatcherEnabled) return;
|
|
115
131
|
this.app.use((req, res, next) => {
|
|
116
132
|
if (this.shouldIgnorePath(req.path)) return next();
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
133
|
+
const context = {
|
|
134
|
+
requestId: import_core.lensUtils.generateRandomUuid()
|
|
135
|
+
};
|
|
136
|
+
import_core.lensContext.run(context, () => {
|
|
137
|
+
const start = process.hrtime();
|
|
138
|
+
this.patchResponseMethods(res);
|
|
139
|
+
res.on("finish", async () => {
|
|
140
|
+
await this.finalizeRequestLog(req, res, requestWatcher, start);
|
|
141
|
+
});
|
|
142
|
+
next();
|
|
121
143
|
});
|
|
122
|
-
next();
|
|
123
144
|
});
|
|
124
145
|
}
|
|
125
146
|
patchResponseMethods(res) {
|
|
@@ -161,7 +182,7 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
161
182
|
const duration = import_core.lensUtils.prettyHrTime(process.hrtime(start));
|
|
162
183
|
const logPayload = {
|
|
163
184
|
request: {
|
|
164
|
-
id: import_core.lensUtils.generateRandomUuid(),
|
|
185
|
+
id: import_core.lensContext.getStore()?.requestId || import_core.lensUtils.generateRandomUuid(),
|
|
165
186
|
method: req.method,
|
|
166
187
|
duration,
|
|
167
188
|
path: req.originalUrl,
|
|
@@ -198,27 +219,50 @@ var ExpressAdapter = class extends import_core.LensAdapter {
|
|
|
198
219
|
};
|
|
199
220
|
|
|
200
221
|
// src/index.ts
|
|
222
|
+
var import_core3 = require("@lensjs/core");
|
|
223
|
+
var import_core4 = require("@lensjs/core");
|
|
224
|
+
var import_core5 = require("@lensjs/core");
|
|
225
|
+
var import_core6 = require("@lensjs/core");
|
|
201
226
|
var defaultConfig = {
|
|
202
227
|
appName: "Lens",
|
|
203
228
|
enabled: true,
|
|
204
229
|
path: "/lens",
|
|
205
230
|
ignoredPaths: [],
|
|
206
231
|
onlyPaths: [],
|
|
207
|
-
requestWatcherEnabled: true
|
|
232
|
+
requestWatcherEnabled: true,
|
|
233
|
+
cacheWatcherEnabled: false,
|
|
234
|
+
exceptionWatcherEnabled: true
|
|
208
235
|
};
|
|
209
236
|
var lens = async (config) => {
|
|
210
237
|
const adapter = new ExpressAdapter({ app: config.app });
|
|
211
238
|
const watchers = [];
|
|
212
239
|
const mergedConfig = {
|
|
213
|
-
...
|
|
214
|
-
...
|
|
240
|
+
...defaultConfig,
|
|
241
|
+
...config
|
|
215
242
|
};
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
243
|
+
const defaultWatchers = [
|
|
244
|
+
{
|
|
245
|
+
enabled: mergedConfig.requestWatcherEnabled,
|
|
246
|
+
watcher: new import_core2.RequestWatcher()
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
enabled: mergedConfig.cacheWatcherEnabled,
|
|
250
|
+
watcher: new import_core2.CacheWatcher()
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
enabled: mergedConfig.queryWatcher?.enabled,
|
|
254
|
+
watcher: new import_core2.QueryWatcher()
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
enabled: mergedConfig.exceptionWatcherEnabled,
|
|
258
|
+
watcher: new import_core2.ExceptionWatcher()
|
|
259
|
+
}
|
|
260
|
+
];
|
|
261
|
+
defaultWatchers.forEach((watcher) => {
|
|
262
|
+
if (watcher.enabled) {
|
|
263
|
+
watchers.push(watcher.watcher);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
222
266
|
const { ignoredPaths, normalizedPath } = import_core2.lensUtils.prepareIgnoredPaths(
|
|
223
267
|
mergedConfig.path,
|
|
224
268
|
mergedConfig.ignoredPaths
|
|
@@ -229,8 +273,31 @@ var lens = async (config) => {
|
|
|
229
273
|
enabled: mergedConfig.enabled,
|
|
230
274
|
basePath: normalizedPath
|
|
231
275
|
});
|
|
276
|
+
return {
|
|
277
|
+
handleExceptions: (app) => handleExceptions({
|
|
278
|
+
app,
|
|
279
|
+
enabled: mergedConfig.exceptionWatcherEnabled,
|
|
280
|
+
watcher: watchers.find((w) => w.name === import_core3.WatcherTypeEnum.EXCEPTION)
|
|
281
|
+
})
|
|
282
|
+
};
|
|
232
283
|
};
|
|
284
|
+
function handleExceptions({
|
|
285
|
+
app,
|
|
286
|
+
enabled,
|
|
287
|
+
watcher
|
|
288
|
+
}) {
|
|
289
|
+
if (!enabled || !watcher) return;
|
|
290
|
+
app.use(async (err, _, __, next) => {
|
|
291
|
+
await watcher.log({
|
|
292
|
+
...import_core6.lensExceptionUtils.constructErrorObject(err),
|
|
293
|
+
requestId: import_core4.lensContext.getStore()?.requestId
|
|
294
|
+
});
|
|
295
|
+
next(err);
|
|
296
|
+
});
|
|
297
|
+
(0, import_core5.handleUncaughExceptions)(watcher);
|
|
298
|
+
}
|
|
233
299
|
// Annotate the CommonJS export names for ESM import in node:
|
|
234
300
|
0 && (module.exports = {
|
|
301
|
+
handleExceptions,
|
|
235
302
|
lens
|
|
236
303
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { ExceptionWatcher } from '@lensjs/core';
|
|
1
2
|
import { ExpressAdapterConfig } from './types/index.cjs';
|
|
2
|
-
import 'express';
|
|
3
|
+
import { Application } from 'express';
|
|
3
4
|
import '@lensjs/watchers';
|
|
4
|
-
import '@lensjs/core';
|
|
5
5
|
|
|
6
|
-
declare const lens: (config: ExpressAdapterConfig) => Promise<
|
|
6
|
+
declare const lens: (config: ExpressAdapterConfig) => Promise<{
|
|
7
|
+
handleExceptions: (app: Application) => void;
|
|
8
|
+
}>;
|
|
9
|
+
declare function handleExceptions({ app, enabled, watcher, }: {
|
|
10
|
+
app: Application;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
watcher?: ExceptionWatcher;
|
|
13
|
+
}): void;
|
|
7
14
|
|
|
8
|
-
export { lens };
|
|
15
|
+
export { handleExceptions, lens };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { ExceptionWatcher } from '@lensjs/core';
|
|
1
2
|
import { ExpressAdapterConfig } from './types/index.js';
|
|
2
|
-
import 'express';
|
|
3
|
+
import { Application } from 'express';
|
|
3
4
|
import '@lensjs/watchers';
|
|
4
|
-
import '@lensjs/core';
|
|
5
5
|
|
|
6
|
-
declare const lens: (config: ExpressAdapterConfig) => Promise<
|
|
6
|
+
declare const lens: (config: ExpressAdapterConfig) => Promise<{
|
|
7
|
+
handleExceptions: (app: Application) => void;
|
|
8
|
+
}>;
|
|
9
|
+
declare function handleExceptions({ app, enabled, watcher, }: {
|
|
10
|
+
app: Application;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
watcher?: ExceptionWatcher;
|
|
13
|
+
}): void;
|
|
7
14
|
|
|
8
|
-
export { lens };
|
|
15
|
+
export { handleExceptions, lens };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
|
+
CacheWatcher as CacheWatcher2,
|
|
4
|
+
ExceptionWatcher,
|
|
3
5
|
Lens,
|
|
4
6
|
lensUtils as lensUtils2,
|
|
5
7
|
QueryWatcher as QueryWatcher2,
|
|
@@ -10,12 +12,14 @@ import {
|
|
|
10
12
|
import {
|
|
11
13
|
LensAdapter,
|
|
12
14
|
lensUtils,
|
|
13
|
-
WatcherTypeEnum
|
|
15
|
+
WatcherTypeEnum,
|
|
16
|
+
lensContext,
|
|
17
|
+
lensEmitter
|
|
14
18
|
} from "@lensjs/core";
|
|
15
19
|
import * as path from "path";
|
|
16
20
|
import fs from "fs";
|
|
17
21
|
import express from "express";
|
|
18
|
-
import { nowISO
|
|
22
|
+
import { nowISO } from "@lensjs/date";
|
|
19
23
|
var ExpressAdapter = class extends LensAdapter {
|
|
20
24
|
app;
|
|
21
25
|
config;
|
|
@@ -31,10 +35,19 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
31
35
|
for (const watcher of this.getWatchers()) {
|
|
32
36
|
switch (watcher.name) {
|
|
33
37
|
case WatcherTypeEnum.REQUEST:
|
|
34
|
-
this.
|
|
38
|
+
if (this.config.requestWatcherEnabled) {
|
|
39
|
+
this.watchRequests(watcher);
|
|
40
|
+
}
|
|
35
41
|
break;
|
|
36
42
|
case WatcherTypeEnum.QUERY:
|
|
37
|
-
|
|
43
|
+
if (this.config.queryWatcher.enabled) {
|
|
44
|
+
void this.watchQueries(watcher);
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
case WatcherTypeEnum.CACHE:
|
|
48
|
+
if (this.config.cacheWatcherEnabled) {
|
|
49
|
+
void this.watchCache(watcher);
|
|
50
|
+
}
|
|
38
51
|
break;
|
|
39
52
|
}
|
|
40
53
|
}
|
|
@@ -68,6 +81,12 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
68
81
|
return res.sendFile(path.join(uiPath, "index.html"));
|
|
69
82
|
});
|
|
70
83
|
}
|
|
84
|
+
async watchCache(watcher) {
|
|
85
|
+
if (!this.config.cacheWatcherEnabled) return;
|
|
86
|
+
lensEmitter.on("cache", async (data) => {
|
|
87
|
+
await watcher?.log(data);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
71
90
|
async watchQueries(watcher) {
|
|
72
91
|
if (!this.config.queryWatcher.enabled) return;
|
|
73
92
|
const handler = this.config.queryWatcher.handler;
|
|
@@ -76,7 +95,7 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
76
95
|
const queryPayload = {
|
|
77
96
|
query: query.query,
|
|
78
97
|
duration: query.duration || "0 ms",
|
|
79
|
-
createdAt: query.createdAt ||
|
|
98
|
+
createdAt: query.createdAt || nowISO(),
|
|
80
99
|
type: query.type
|
|
81
100
|
};
|
|
82
101
|
await watcher?.log({
|
|
@@ -89,12 +108,17 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
89
108
|
if (!this.config.requestWatcherEnabled) return;
|
|
90
109
|
this.app.use((req, res, next) => {
|
|
91
110
|
if (this.shouldIgnorePath(req.path)) return next();
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
111
|
+
const context = {
|
|
112
|
+
requestId: lensUtils.generateRandomUuid()
|
|
113
|
+
};
|
|
114
|
+
lensContext.run(context, () => {
|
|
115
|
+
const start = process.hrtime();
|
|
116
|
+
this.patchResponseMethods(res);
|
|
117
|
+
res.on("finish", async () => {
|
|
118
|
+
await this.finalizeRequestLog(req, res, requestWatcher, start);
|
|
119
|
+
});
|
|
120
|
+
next();
|
|
96
121
|
});
|
|
97
|
-
next();
|
|
98
122
|
});
|
|
99
123
|
}
|
|
100
124
|
patchResponseMethods(res) {
|
|
@@ -136,7 +160,7 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
136
160
|
const duration = lensUtils.prettyHrTime(process.hrtime(start));
|
|
137
161
|
const logPayload = {
|
|
138
162
|
request: {
|
|
139
|
-
id: lensUtils.generateRandomUuid(),
|
|
163
|
+
id: lensContext.getStore()?.requestId || lensUtils.generateRandomUuid(),
|
|
140
164
|
method: req.method,
|
|
141
165
|
duration,
|
|
142
166
|
path: req.originalUrl,
|
|
@@ -173,27 +197,50 @@ var ExpressAdapter = class extends LensAdapter {
|
|
|
173
197
|
};
|
|
174
198
|
|
|
175
199
|
// src/index.ts
|
|
200
|
+
import { WatcherTypeEnum as WatcherTypeEnum2 } from "@lensjs/core";
|
|
201
|
+
import { lensContext as lensContext2 } from "@lensjs/core";
|
|
202
|
+
import { handleUncaughExceptions } from "@lensjs/core";
|
|
203
|
+
import { lensExceptionUtils } from "@lensjs/core";
|
|
176
204
|
var defaultConfig = {
|
|
177
205
|
appName: "Lens",
|
|
178
206
|
enabled: true,
|
|
179
207
|
path: "/lens",
|
|
180
208
|
ignoredPaths: [],
|
|
181
209
|
onlyPaths: [],
|
|
182
|
-
requestWatcherEnabled: true
|
|
210
|
+
requestWatcherEnabled: true,
|
|
211
|
+
cacheWatcherEnabled: false,
|
|
212
|
+
exceptionWatcherEnabled: true
|
|
183
213
|
};
|
|
184
214
|
var lens = async (config) => {
|
|
185
215
|
const adapter = new ExpressAdapter({ app: config.app });
|
|
186
216
|
const watchers = [];
|
|
187
217
|
const mergedConfig = {
|
|
188
|
-
...
|
|
189
|
-
...
|
|
218
|
+
...defaultConfig,
|
|
219
|
+
...config
|
|
190
220
|
};
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
221
|
+
const defaultWatchers = [
|
|
222
|
+
{
|
|
223
|
+
enabled: mergedConfig.requestWatcherEnabled,
|
|
224
|
+
watcher: new RequestWatcher2()
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
enabled: mergedConfig.cacheWatcherEnabled,
|
|
228
|
+
watcher: new CacheWatcher2()
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
enabled: mergedConfig.queryWatcher?.enabled,
|
|
232
|
+
watcher: new QueryWatcher2()
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
enabled: mergedConfig.exceptionWatcherEnabled,
|
|
236
|
+
watcher: new ExceptionWatcher()
|
|
237
|
+
}
|
|
238
|
+
];
|
|
239
|
+
defaultWatchers.forEach((watcher) => {
|
|
240
|
+
if (watcher.enabled) {
|
|
241
|
+
watchers.push(watcher.watcher);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
197
244
|
const { ignoredPaths, normalizedPath } = lensUtils2.prepareIgnoredPaths(
|
|
198
245
|
mergedConfig.path,
|
|
199
246
|
mergedConfig.ignoredPaths
|
|
@@ -204,7 +251,30 @@ var lens = async (config) => {
|
|
|
204
251
|
enabled: mergedConfig.enabled,
|
|
205
252
|
basePath: normalizedPath
|
|
206
253
|
});
|
|
254
|
+
return {
|
|
255
|
+
handleExceptions: (app) => handleExceptions({
|
|
256
|
+
app,
|
|
257
|
+
enabled: mergedConfig.exceptionWatcherEnabled,
|
|
258
|
+
watcher: watchers.find((w) => w.name === WatcherTypeEnum2.EXCEPTION)
|
|
259
|
+
})
|
|
260
|
+
};
|
|
207
261
|
};
|
|
262
|
+
function handleExceptions({
|
|
263
|
+
app,
|
|
264
|
+
enabled,
|
|
265
|
+
watcher
|
|
266
|
+
}) {
|
|
267
|
+
if (!enabled || !watcher) return;
|
|
268
|
+
app.use(async (err, _, __, next) => {
|
|
269
|
+
await watcher.log({
|
|
270
|
+
...lensExceptionUtils.constructErrorObject(err),
|
|
271
|
+
requestId: lensContext2.getStore()?.requestId
|
|
272
|
+
});
|
|
273
|
+
next(err);
|
|
274
|
+
});
|
|
275
|
+
handleUncaughExceptions(watcher);
|
|
276
|
+
}
|
|
208
277
|
export {
|
|
278
|
+
handleExceptions,
|
|
209
279
|
lens
|
|
210
280
|
};
|
package/dist/types/index.d.cts
CHANGED
|
@@ -10,6 +10,8 @@ type ExpressAdapterConfig = {
|
|
|
10
10
|
ignoredPaths?: RegExp[];
|
|
11
11
|
onlyPaths?: RegExp[];
|
|
12
12
|
requestWatcherEnabled?: boolean;
|
|
13
|
+
cacheWatcherEnabled?: boolean;
|
|
14
|
+
exceptionWatcherEnabled?: boolean;
|
|
13
15
|
queryWatcher?: {
|
|
14
16
|
enabled: boolean;
|
|
15
17
|
handler: QueryWatcherHandler;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ type ExpressAdapterConfig = {
|
|
|
10
10
|
ignoredPaths?: RegExp[];
|
|
11
11
|
onlyPaths?: RegExp[];
|
|
12
12
|
requestWatcherEnabled?: boolean;
|
|
13
|
+
cacheWatcherEnabled?: boolean;
|
|
14
|
+
exceptionWatcherEnabled?: boolean;
|
|
13
15
|
queryWatcher?: {
|
|
14
16
|
enabled: boolean;
|
|
15
17
|
handler: QueryWatcherHandler;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensjs/express",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Express adapter for LensJs",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -19,15 +19,18 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"express": "^5.1.0",
|
|
22
|
-
"@lensjs/
|
|
23
|
-
"@lensjs/
|
|
24
|
-
"@lensjs/watchers": "1.0.
|
|
25
|
-
"@lensjs/
|
|
22
|
+
"@lensjs/core": "2.1.0",
|
|
23
|
+
"@lensjs/typescript-config": "1.0.12",
|
|
24
|
+
"@lensjs/watchers": "1.0.14",
|
|
25
|
+
"@lensjs/date": "1.0.12"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@types/express": "^5.0.3"
|
|
28
|
+
"@types/express": "^5.0.3",
|
|
29
|
+
"supertest": "^7.1.4",
|
|
30
|
+
"vitest": "^3.2.4"
|
|
29
31
|
},
|
|
30
32
|
"scripts": {
|
|
31
|
-
"build": "tsup --clean"
|
|
33
|
+
"build": "tsup --clean",
|
|
34
|
+
"test": "vitest run"
|
|
32
35
|
}
|
|
33
36
|
}
|