midline-agent 0.1.5 → 0.1.6

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 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 express
35
+ npm install midline-agent
36
36
  ```
37
37
 
38
- ### Basic Usage
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: "YOUR_API_KEY_FROM_MIDLINE", // Get this from https://midline.com
49
- serviceName: "my-express-service",
50
- maskFields: ["password", "token"] // Optional fields to mask
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
- // Middleware to capture requests
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
- // Example route
58
- app.get("/hello", (req, res) => res.json({ msg: "Hello World" }));
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
- // Middleware to capture errors
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
- ### Advanced Configuration
68
- - maskFields: Array of sensitive fields to exclude from logs (e.g., passwords, tokens)
69
- - endpoint: Optional override default Midline Cloud endpoint
70
- - serviceName: Unique identifier for your application
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
- ### Developer Example
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
- // example.ts
76
- import express from "express";
77
- import { MidlineAgent, midlineMiddleware, midlineErrorHandler } from "midline-agent";
101
+ import { NestFactory } from '@nestjs/core';
102
+ import { AppModule } from './app.module';
103
+ import { MidlineAgent, midlineMiddleware, midlineErrorHandler } from 'midline-agent';
78
104
 
79
- const app = express();
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: "YOUR_API_KEY",
83
- serviceName: "example-service",
84
- maskFields: ["password"]
175
+ apiKey: process.env.MIDLINE_KEY!,
176
+ serviceName: 'raw-node-server',
177
+ environment: 'production',
178
+ maskFields: ['password', 'token']
85
179
  });
86
180
 
87
- app.use(express.json());
88
- app.use(midlineMiddleware());
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
- app.get("/", (req, res) => res.send("Hello Midline!"));
198
+ server.listen(3000, () => console.log('Server listening on port 3000'));
199
+ ```
91
200
 
92
- app.use(midlineErrorHandler());
201
+ ---
202
+
203
+ ## Configuration Options
93
204
 
94
- app.listen(3000, () => console.log("Example server running on port 3000"));
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
- ## Any other backend
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.midline.com/api/api-monitor/events` | One event |
110
- | `POST https://api.midline.com/api/api-monitor/events/batch` | Many events — `{ "events": [ … ] }` |
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.midline.com/api/api-monitor/events \
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.midline.com/api/api-monitor/events"
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.midline.com/api/api-monitor/events",
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.midline.com/api/api-monitor/events",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midline-agent",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Express.js SDK for Midline — request & error monitoring with security detection",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
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.midline.com/api/api-monitor/events",
11
+ endpoint: "https://api.usemidline.com/api/api-monitor/events",
12
12
  maskFields: ["password", "token", ...(config.maskFields || [])],
13
13
  ...config,
14
14
  };