midline-agent 0.1.5 → 0.1.7
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 +227 -34
- package/dist/agent.js +1 -1
- package/package.json +1 -1
- package/src/agent.ts +1 -1
package/README.md
CHANGED
|
@@ -32,10 +32,20 @@ After login, navigate to your **Dashboard → API Keys** and generate a key for
|
|
|
32
32
|
### 2. Install Package
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
npm install midline-agent
|
|
35
|
+
npm install midline-agent
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Choose one of the guides below based on your framework.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Framework Setup
|
|
43
|
+
|
|
44
|
+
### Express.js
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install midline-agent express
|
|
48
|
+
```
|
|
39
49
|
|
|
40
50
|
```ts
|
|
41
51
|
import express from "express";
|
|
@@ -43,60 +53,243 @@ import { MidlineAgent, midlineMiddleware, midlineErrorHandler } from "midline-ag
|
|
|
43
53
|
|
|
44
54
|
const app = express();
|
|
45
55
|
|
|
46
|
-
// Initialize Midline Agent
|
|
56
|
+
// Initialize Midline Agent (do this once, early in your app startup)
|
|
47
57
|
MidlineAgent.init({
|
|
48
|
-
apiKey:
|
|
49
|
-
serviceName: "
|
|
50
|
-
|
|
58
|
+
apiKey: process.env.MIDLINE_KEY!,
|
|
59
|
+
serviceName: "checkout-api",
|
|
60
|
+
environment: "production",
|
|
61
|
+
maskFields: ["password", "token", "card_number", "cvv"]
|
|
51
62
|
});
|
|
52
63
|
|
|
53
|
-
//
|
|
64
|
+
// Parse incoming JSON
|
|
54
65
|
app.use(express.json());
|
|
66
|
+
|
|
67
|
+
// Add request capture middleware BEFORE your routes
|
|
55
68
|
app.use(midlineMiddleware());
|
|
56
69
|
|
|
57
|
-
//
|
|
58
|
-
app.get("/
|
|
70
|
+
// Your routes here
|
|
71
|
+
app.get("/health", (req, res) => res.json({ status: "ok" }));
|
|
72
|
+
app.post("/v1/charges", async (req, res) => {
|
|
73
|
+
// Handle payment processing
|
|
74
|
+
res.json({ success: true });
|
|
75
|
+
});
|
|
59
76
|
|
|
60
|
-
//
|
|
77
|
+
// Add error handler middleware AFTER your routes
|
|
78
|
+
// This must be the last middleware registered
|
|
61
79
|
app.use(midlineErrorHandler());
|
|
62
80
|
|
|
63
|
-
// Start server
|
|
64
81
|
app.listen(3000, () => console.log("Server running on port 3000"));
|
|
65
82
|
```
|
|
66
83
|
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
84
|
+
**Key points:**
|
|
85
|
+
- `MidlineAgent.init()` must be called once during app startup
|
|
86
|
+
- `midlineMiddleware()` captures all requests and responses
|
|
87
|
+
- `midlineErrorHandler()` must be registered **after** all other middleware and routes
|
|
88
|
+
- Express only routes errors to handlers declared after the code that threw, so order matters
|
|
71
89
|
|
|
72
|
-
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### NestJS
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm install midline-agent
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Using with NestJS requires wrapping the Express middleware. Here's the setup:**
|
|
73
99
|
|
|
74
100
|
```ts
|
|
75
|
-
|
|
76
|
-
import
|
|
77
|
-
import { MidlineAgent, midlineMiddleware, midlineErrorHandler } from
|
|
101
|
+
import { NestFactory } from '@nestjs/core';
|
|
102
|
+
import { AppModule } from './app.module';
|
|
103
|
+
import { MidlineAgent, midlineMiddleware, midlineErrorHandler } from 'midline-agent';
|
|
78
104
|
|
|
79
|
-
|
|
105
|
+
async function bootstrap() {
|
|
106
|
+
const app = await NestFactory.create(AppModule);
|
|
107
|
+
|
|
108
|
+
// Initialize Midline Agent early in bootstrap
|
|
109
|
+
MidlineAgent.init({
|
|
110
|
+
apiKey: process.env.MIDLINE_KEY!,
|
|
111
|
+
serviceName: 'nest-api',
|
|
112
|
+
environment: process.env.NODE_ENV || 'development',
|
|
113
|
+
maskFields: ['password', 'token', 'apiKey', 'secret']
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Apply request capture middleware
|
|
117
|
+
app.use(midlineMiddleware());
|
|
118
|
+
|
|
119
|
+
// Apply error handler after NestJS is configured
|
|
120
|
+
app.use(midlineErrorHandler());
|
|
121
|
+
|
|
122
|
+
await app.listen(3000);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
bootstrap();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**For module-level integration, create a middleware:**
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// midline.middleware.ts
|
|
132
|
+
import { Injectable, NestMiddleware } from '@nestjs/common';
|
|
133
|
+
import { midlineMiddleware, midlineErrorHandler } from 'midline-agent';
|
|
134
|
+
import { Request, Response, NextFunction } from 'express';
|
|
135
|
+
|
|
136
|
+
@Injectable()
|
|
137
|
+
export class MidlineMiddleware implements NestMiddleware {
|
|
138
|
+
use(req: Request, res: Response, next: NextFunction) {
|
|
139
|
+
midlineMiddleware()(req, res, next);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Then apply it in your module:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { Module, MiddlewareConsumer } from '@nestjs/common';
|
|
148
|
+
import { MidlineMiddleware } from './midline.middleware';
|
|
149
|
+
|
|
150
|
+
@Module({
|
|
151
|
+
imports: [],
|
|
152
|
+
controllers: [],
|
|
153
|
+
providers: [],
|
|
154
|
+
})
|
|
155
|
+
export class AppModule {
|
|
156
|
+
configure(consumer: MiddlewareConsumer) {
|
|
157
|
+
consumer
|
|
158
|
+
.apply(MidlineMiddleware)
|
|
159
|
+
.forRoutes('*');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### Plain Node.js / HTTP Server
|
|
167
|
+
|
|
168
|
+
If you're not using Express or NestJS, you can integrate midline-agent by manually wrapping your request handlers:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import http from 'http';
|
|
172
|
+
import { MidlineAgent, midlineMiddleware, midlineErrorHandler } from 'midline-agent';
|
|
80
173
|
|
|
81
174
|
MidlineAgent.init({
|
|
82
|
-
apiKey:
|
|
83
|
-
serviceName:
|
|
84
|
-
|
|
175
|
+
apiKey: process.env.MIDLINE_KEY!,
|
|
176
|
+
serviceName: 'raw-node-server',
|
|
177
|
+
environment: 'production',
|
|
178
|
+
maskFields: ['password', 'token']
|
|
85
179
|
});
|
|
86
180
|
|
|
87
|
-
|
|
88
|
-
|
|
181
|
+
const server = http.createServer((req, res) => {
|
|
182
|
+
// Create Express-like req/res wrapper for middleware
|
|
183
|
+
const next = (err?: any) => {
|
|
184
|
+
if (err) throw err;
|
|
185
|
+
// Handle your route
|
|
186
|
+
if (req.url === '/health') {
|
|
187
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
188
|
+
res.end(JSON.stringify({ status: 'ok' }));
|
|
189
|
+
} else {
|
|
190
|
+
res.writeHead(404);
|
|
191
|
+
res.end('Not Found');
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
midlineMiddleware()(req, res, next);
|
|
196
|
+
});
|
|
89
197
|
|
|
90
|
-
|
|
198
|
+
server.listen(3000, () => console.log('Server listening on port 3000'));
|
|
199
|
+
```
|
|
91
200
|
|
|
92
|
-
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Configuration Options
|
|
93
204
|
|
|
94
|
-
|
|
205
|
+
```ts
|
|
206
|
+
MidlineAgent.init({
|
|
207
|
+
// Required
|
|
208
|
+
apiKey: string; // Get from https://midline.com/dashboard/api-keys
|
|
209
|
+
serviceName: string; // Name of your service (e.g., "checkout-api", "auth-service")
|
|
210
|
+
|
|
211
|
+
// Optional
|
|
212
|
+
maskFields?: string[]; // Field names to mask before sending (passwords, tokens, etc)
|
|
213
|
+
environment?: string; // e.g., "production", "staging", "development"
|
|
214
|
+
endpoint?: string; // Override default ingest URL (for self-hosted)
|
|
215
|
+
host?: string; // Machine/pod name for identifying instances
|
|
216
|
+
region?: string; // Deployment region (e.g., "us-east-1", "eu-west-1")
|
|
217
|
+
});
|
|
95
218
|
```
|
|
96
219
|
|
|
97
220
|
---
|
|
98
221
|
|
|
99
|
-
##
|
|
222
|
+
## Sensitive Field Masking
|
|
223
|
+
|
|
224
|
+
By default, `maskFields` removes the following fields before serialization:
|
|
225
|
+
- `password`
|
|
226
|
+
- `token`
|
|
227
|
+
- `apiKey`
|
|
228
|
+
- `secret`
|
|
229
|
+
- `authorization`
|
|
230
|
+
|
|
231
|
+
Add custom fields as needed:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
MidlineAgent.init({
|
|
235
|
+
apiKey: process.env.MIDLINE_KEY!,
|
|
236
|
+
serviceName: 'checkout-api',
|
|
237
|
+
maskFields: [
|
|
238
|
+
'password',
|
|
239
|
+
'card_number',
|
|
240
|
+
'cvv',
|
|
241
|
+
'ssn',
|
|
242
|
+
'oauth_token',
|
|
243
|
+
'jwt'
|
|
244
|
+
]
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Important:** Masking happens in your process before the event is built. Masked values never leave your machine and are never stored on Midline servers.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Error Handling
|
|
253
|
+
|
|
254
|
+
The error handler middleware captures:
|
|
255
|
+
- Unhandled errors thrown by your route handlers
|
|
256
|
+
- HTTP error responses
|
|
257
|
+
- Validation errors
|
|
258
|
+
|
|
259
|
+
**Example with error throwing:**
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
app.post('/v1/charges', (req, res, next) => {
|
|
263
|
+
try {
|
|
264
|
+
if (!req.body.amount) {
|
|
265
|
+
throw new Error('Amount is required');
|
|
266
|
+
}
|
|
267
|
+
// Process payment...
|
|
268
|
+
} catch (error) {
|
|
269
|
+
next(error); // Pass to Midline error handler
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// The error handler will capture and report this
|
|
274
|
+
app.use(midlineErrorHandler());
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## How Delivery Works
|
|
280
|
+
|
|
281
|
+
- Events are queued in memory and flushed to Midline Cloud every 1.5 seconds
|
|
282
|
+
- Request capturing is non-blocking — your response is sent before telemetry is reported
|
|
283
|
+
- No network call can slow down your API responses
|
|
284
|
+
|
|
285
|
+
**Important for production:**
|
|
286
|
+
- In-memory events are lost if your process crashes before the flush interval
|
|
287
|
+
- This is acceptable for monitoring telemetry (good for operations, not billing/audit)
|
|
288
|
+
- For high-traffic services, consider using the batch endpoint directly for reliability
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Not using Node.js? Any other backend
|
|
100
293
|
|
|
101
294
|
This agent does two things: it turns a request or an error into an event, and it POSTs that event to
|
|
102
295
|
the Midline ingest API. Both are things your own service can do directly, in whatever language it
|
|
@@ -106,8 +299,8 @@ is written in.
|
|
|
106
299
|
|
|
107
300
|
| Endpoint | Use |
|
|
108
301
|
| --- | --- |
|
|
109
|
-
| `POST https://api.
|
|
110
|
-
| `POST https://api.
|
|
302
|
+
| `POST https://api.usemidline.com/api/api-monitor/events` | One event |
|
|
303
|
+
| `POST https://api.usemidline.com/api/api-monitor/events/batch` | Many events — `{ "events": [ … ] }` |
|
|
111
304
|
|
|
112
305
|
The API key identifies your organisation and travels in the body; there is no separate auth header.
|
|
113
306
|
|
|
@@ -134,7 +327,7 @@ be stored.
|
|
|
134
327
|
**curl**
|
|
135
328
|
|
|
136
329
|
```bash
|
|
137
|
-
curl -X POST https://api.
|
|
330
|
+
curl -X POST https://api.usemidline.com/api/api-monitor/events \
|
|
138
331
|
-H "Content-Type: application/json" \
|
|
139
332
|
-d '{
|
|
140
333
|
"apiKey": "YOUR_API_KEY",
|
|
@@ -155,7 +348,7 @@ curl -X POST https://api.midline.com/api/api-monitor/events \
|
|
|
155
348
|
```python
|
|
156
349
|
import requests, threading
|
|
157
350
|
|
|
158
|
-
INGEST = "https://api.
|
|
351
|
+
INGEST = "https://api.usemidline.com/api/api-monitor/events"
|
|
159
352
|
|
|
160
353
|
def report(event: dict) -> None:
|
|
161
354
|
event["apiKey"] = MIDLINE_KEY
|
|
@@ -194,7 +387,7 @@ func Report(e Event) {
|
|
|
194
387
|
e.APIKey = os.Getenv("MIDLINE_KEY")
|
|
195
388
|
body, _ := json.Marshal(e)
|
|
196
389
|
go http.Post(
|
|
197
|
-
"https://api.
|
|
390
|
+
"https://api.usemidline.com/api/api-monitor/events",
|
|
198
391
|
"application/json",
|
|
199
392
|
bytes.NewReader(body),
|
|
200
393
|
) // fire and forget
|
package/dist/agent.js
CHANGED
|
@@ -8,7 +8,7 @@ const cross_fetch_1 = __importDefault(require("cross-fetch"));
|
|
|
8
8
|
class MidlineAgent {
|
|
9
9
|
static init(config) {
|
|
10
10
|
this.config = {
|
|
11
|
-
endpoint: "https://api.
|
|
11
|
+
endpoint: "https://api.usemidline.com/api/api-monitor/events",
|
|
12
12
|
maskFields: ["password", "token", ...(config.maskFields || [])],
|
|
13
13
|
...config,
|
|
14
14
|
};
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -8,7 +8,7 @@ export class MidlineAgent {
|
|
|
8
8
|
|
|
9
9
|
static init(config: MidlineConfig) {
|
|
10
10
|
this.config = {
|
|
11
|
-
endpoint: "https://api.
|
|
11
|
+
endpoint: "https://api.usemidline.com/api/api-monitor/events",
|
|
12
12
|
maskFields: ["password", "token", ...(config.maskFields || [])],
|
|
13
13
|
...config,
|
|
14
14
|
};
|