@ceon-oy/monitor-sdk 1.0.1 → 1.0.3

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
@@ -1,10 +1,32 @@
1
- # Ceon Monitor - SDK
2
-
3
- Lightweight client SDK for sending errors and messages to the Ceon Monitor service.
1
+ # Ceon Monitor SDK
2
+
3
+ Lightweight client SDK for integrating with the Ceon Monitor service. Provides error reporting, technology tracking, vulnerability auditing, and security event monitoring.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Quick Start](#quick-start)
9
+ - [Configuration](#configuration)
10
+ - [Features](#features)
11
+ - [Error Capture](#error-capture)
12
+ - [Technology Tracking](#technology-tracking)
13
+ - [Vulnerability Auditing](#vulnerability-auditing)
14
+ - [Security Events](#security-events)
15
+ - [Framework Examples](#framework-examples)
16
+ - [Express.js](#expressjs)
17
+ - [Next.js](#nextjs)
18
+ - [React (Monolithic)](#react-monolithic)
19
+ - [React (Separate Server/Client)](#react-separate-serverclient)
20
+ - [API Reference](#api-reference)
21
+ - [Batching Behavior](#batching-behavior)
22
+ - [Building](#building)
4
23
 
5
24
  ## Installation
6
25
 
7
26
  ```bash
27
+ # From npm (recommended)
28
+ npm install @ceon-oy/monitor-sdk
29
+
8
30
  # From GitHub
9
31
  npm install github:ceon-oy/ceon-monitor-sdk
10
32
  ```
@@ -14,7 +36,7 @@ Or add to your `package.json`:
14
36
  ```json
15
37
  {
16
38
  "dependencies": {
17
- "@ceon/monitor-sdk": "github:ceon-oy/ceon-monitor-sdk"
39
+ "@ceon-oy/monitor-sdk": "^1.0.0"
18
40
  }
19
41
  }
20
42
  ```
@@ -26,13 +48,14 @@ Or add to your `package.json`:
26
48
  ## Quick Start
27
49
 
28
50
  ```typescript
29
- import { MonitorClient } from '@ceon/monitor-sdk';
51
+ import { MonitorClient } from '@ceon-oy/monitor-sdk';
30
52
 
31
53
  // Initialize the client
32
54
  const monitor = new MonitorClient({
33
- apiKey: process.env.MONITOR_API_KEY!,
55
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
34
56
  endpoint: 'https://monitor.example.com',
35
57
  environment: process.env.NODE_ENV,
58
+ trackDependencies: true, // Auto-sync package.json
36
59
  });
37
60
 
38
61
  // Capture an error
@@ -51,6 +74,9 @@ await monitor.captureMessage('User signed up', 'INFO', {
51
74
  metadata: { userId: '123', plan: 'premium' },
52
75
  });
53
76
 
77
+ // Run vulnerability audit
78
+ await monitor.auditDependencies();
79
+
54
80
  // Flush and close before shutdown
55
81
  await monitor.close();
56
82
  ```
@@ -59,131 +85,276 @@ await monitor.close();
59
85
 
60
86
  ```typescript
61
87
  interface MonitorClientConfig {
62
- apiKey: string; // Your project API key
63
- endpoint: string; // Monitor service URL
64
- environment?: string; // Environment name (default: 'production')
65
- batchSize?: number; // Errors to batch before sending (default: 10)
66
- flushIntervalMs?: number; // Auto-flush interval in ms (default: 5000)
88
+ apiKey: string; // Your project API key (required)
89
+ endpoint: string; // Monitor service URL (required)
90
+ environment?: string; // Environment name (default: 'production')
91
+ batchSize?: number; // Errors to batch before sending (default: 10)
92
+ flushIntervalMs?: number; // Auto-flush interval in ms (default: 5000)
93
+ trackDependencies?: boolean; // Auto-sync package.json (default: false)
94
+ dependencySources?: { // Multiple package.json sources
95
+ path: string;
96
+ environment: string;
97
+ }[];
98
+ excludePatterns?: string[]; // Glob patterns to exclude (e.g., '@types/*')
99
+ autoAudit?: boolean; // Enable automatic vulnerability scanning (default: false)
67
100
  }
68
101
  ```
69
102
 
70
- ## API
71
-
72
- ### `MonitorClient`
73
-
74
- #### `constructor(config: MonitorClientConfig)`
75
-
76
- Creates a new monitor client instance.
103
+ ### Basic Configuration
77
104
 
78
105
  ```typescript
79
106
  const monitor = new MonitorClient({
80
- apiKey: 'cm_your_api_key_here',
107
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
81
108
  endpoint: 'https://monitor.example.com',
82
109
  environment: 'production',
83
- batchSize: 10,
84
- flushIntervalMs: 5000,
85
110
  });
86
111
  ```
87
112
 
88
- #### `captureError(error: Error, context?: ErrorContext): Promise<void>`
113
+ ### Multi-Environment Dependency Tracking
89
114
 
90
- Captures an error and queues it for sending.
115
+ For projects with separate server and client folders:
91
116
 
92
117
  ```typescript
93
- await monitor.captureError(error, {
94
- severity: 'ERROR', // DEBUG, INFO, WARNING, ERROR, CRITICAL
95
- route: '/api/users', // API route or page path
96
- method: 'POST', // HTTP method
97
- statusCode: 500, // HTTP status code
98
- userAgent: req.headers['user-agent'],
99
- ip: req.ip,
100
- requestId: req.id,
101
- metadata: { // Any additional data
102
- userId: '123',
103
- action: 'create_user',
104
- },
118
+ const monitor = new MonitorClient({
119
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
120
+ endpoint: 'https://monitor.example.com',
121
+ environment: 'server',
122
+ trackDependencies: true,
123
+ dependencySources: [
124
+ { path: './package.json', environment: 'server' },
125
+ { path: '../client/package.json', environment: 'client' },
126
+ ],
127
+ excludePatterns: ['@types/*'], // Filter out TypeScript type definitions
105
128
  });
106
129
  ```
107
130
 
108
- #### `captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>`
131
+ ## Features
132
+
133
+ ### Error Capture
134
+
135
+ #### Capture an Error
109
136
 
110
- Captures a log message.
137
+ ```typescript
138
+ try {
139
+ // ... your code
140
+ } catch (error) {
141
+ await monitor.captureError(error as Error, {
142
+ severity: 'ERROR', // DEBUG, INFO, WARNING, ERROR, CRITICAL
143
+ route: '/api/users', // API route or page path
144
+ method: 'POST', // HTTP method
145
+ statusCode: 500, // HTTP status code
146
+ userAgent: req.headers['user-agent'],
147
+ ip: req.ip,
148
+ requestId: req.id,
149
+ metadata: { // Any additional data
150
+ userId: '123',
151
+ action: 'create_user',
152
+ },
153
+ });
154
+ }
155
+ ```
156
+
157
+ #### Capture a Message
111
158
 
112
159
  ```typescript
113
160
  await monitor.captureMessage('Payment processed', 'INFO', {
161
+ route: '/api/payments',
114
162
  metadata: { orderId: '456', amount: 99.99 },
115
163
  });
116
164
  ```
117
165
 
118
- #### `flush(): Promise<void>`
166
+ ### Technology Tracking
167
+
168
+ #### Automatic Tracking
169
+
170
+ Enable `trackDependencies: true` to automatically sync your `package.json` dependencies:
171
+
172
+ ```typescript
173
+ const monitor = new MonitorClient({
174
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
175
+ endpoint: 'https://monitor.example.com',
176
+ trackDependencies: true,
177
+ });
178
+ ```
119
179
 
120
- Immediately sends all queued errors. Called automatically based on `flushIntervalMs` and `batchSize`.
180
+ #### Manual Reporting
121
181
 
122
182
  ```typescript
123
- await monitor.flush();
183
+ await monitor.reportTechnologies([
184
+ { name: 'node', version: '20.10.0', type: 'runtime' },
185
+ { name: 'express', version: '4.18.2', type: 'framework' },
186
+ { name: 'prisma', version: '5.0.0', type: 'database' },
187
+ ]);
124
188
  ```
125
189
 
126
- #### `close(): Promise<void>`
190
+ ### Vulnerability Auditing
127
191
 
128
- Flushes remaining errors and stops the client. Call this before your application shuts down.
192
+ Run npm audit and send results to the monitoring server:
129
193
 
130
194
  ```typescript
131
- await monitor.close();
195
+ // Basic audit
196
+ const result = await monitor.auditDependencies();
197
+
198
+ if (result) {
199
+ console.log(`Scan complete: ${result.processed} vulnerabilities found`);
200
+ console.log(`Critical: ${result.summary.critical}, High: ${result.summary.high}`);
201
+ }
202
+
203
+ // Audit a specific project directory
204
+ await monitor.auditDependencies({
205
+ projectPath: '/path/to/project',
206
+ environment: 'production',
207
+ });
208
+ ```
209
+
210
+ #### Automatic Auditing (Recommended)
211
+
212
+ Enable `autoAudit: true` to let the SDK automatically scan for vulnerabilities based on the interval configured in the Ceon Monitor dashboard:
213
+
214
+ ```typescript
215
+ const monitor = new MonitorClient({
216
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
217
+ endpoint: 'https://monitor.example.com',
218
+ autoAudit: true, // Enable automatic vulnerability scanning
219
+ });
220
+ ```
221
+
222
+ With `autoAudit` enabled:
223
+ - SDK fetches the scan interval from the server on startup
224
+ - Runs an initial vulnerability scan
225
+ - Schedules recurring scans based on server configuration (e.g., every 24 hours)
226
+ - Responds to on-demand "Run Scan" requests from the dashboard (polled every 5 minutes)
227
+
228
+ Configure the scan interval in the Ceon Monitor dashboard under the project's Vulnerabilities page.
229
+
230
+ #### Manual Scheduled Auditing
231
+
232
+ If you prefer manual control over scheduling:
233
+
234
+ ```typescript
235
+ // Run on app startup
236
+ monitor.auditDependencies();
237
+
238
+ // Run daily via cron
239
+ import cron from 'node-cron';
240
+
241
+ cron.schedule('0 3 * * *', async () => {
242
+ await monitor.auditDependencies();
243
+ });
244
+
245
+ // Or using setInterval
246
+ setInterval(async () => {
247
+ await monitor.auditDependencies();
248
+ }, 24 * 60 * 60 * 1000); // Daily
132
249
  ```
133
250
 
134
- ## Usage Examples
251
+ ### Security Events
135
252
 
136
- ### Express.js Error Handler
253
+ #### Report Security Event
137
254
 
138
255
  ```typescript
139
- import { MonitorClient } from '@ceon/monitor-sdk';
140
- import { ErrorRequestHandler } from 'express';
256
+ await monitor.reportSecurityEvent({
257
+ eventType: 'FAILED_LOGIN',
258
+ category: 'AUTHENTICATION',
259
+ severity: 'MEDIUM',
260
+ ip: '192.168.1.1',
261
+ identifier: 'user@example.com',
262
+ metadata: { attempts: 3 },
263
+ });
264
+ ```
141
265
 
266
+ #### Check for Brute Force
267
+
268
+ ```typescript
269
+ const result = await monitor.checkBruteForce({
270
+ ip: '192.168.1.1',
271
+ identifier: 'user@example.com',
272
+ threshold: 5,
273
+ windowMinutes: 15,
274
+ });
275
+
276
+ if (result.blocked) {
277
+ // Block the request
278
+ throw new Error('Too many attempts. Please try again later.');
279
+ }
280
+ ```
281
+
282
+ ## Framework Examples
283
+
284
+ ### Express.js
285
+
286
+ ```typescript
287
+ import express from 'express';
288
+ import { MonitorClient } from '@ceon-oy/monitor-sdk';
289
+
290
+ const app = express();
142
291
  const monitor = new MonitorClient({
143
- apiKey: process.env.MONITOR_API_KEY!,
144
- endpoint: process.env.MONITOR_ENDPOINT!,
292
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
293
+ endpoint: process.env.CEON_MONITOR_ENDPOINT!,
145
294
  environment: process.env.NODE_ENV,
295
+ trackDependencies: true,
296
+ });
297
+
298
+ // Your routes
299
+ app.get('/api/health', (req, res) => {
300
+ res.json({ status: 'ok' });
146
301
  });
147
302
 
148
- export const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
303
+ // Error handling middleware
304
+ app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
149
305
  monitor.captureError(err, {
150
306
  route: req.path,
151
307
  method: req.method,
152
- statusCode: res.statusCode || 500,
153
- userAgent: req.headers['user-agent'],
308
+ statusCode: 500,
309
+ userAgent: req.get('user-agent'),
154
310
  ip: req.ip,
155
- metadata: {
156
- query: req.query,
157
- body: req.body,
158
- },
159
311
  });
160
-
161
312
  res.status(500).json({ error: 'Internal server error' });
162
- };
313
+ });
163
314
 
164
315
  // Graceful shutdown
165
316
  process.on('SIGTERM', async () => {
166
317
  await monitor.close();
167
318
  process.exit(0);
168
319
  });
320
+
321
+ app.listen(3001, () => console.log('Server running on port 3001'));
322
+ ```
323
+
324
+ ### Next.js
325
+
326
+ **1. Create a monitor utility (`lib/monitor.ts`):**
327
+
328
+ ```typescript
329
+ import { MonitorClient } from '@ceon-oy/monitor-sdk';
330
+
331
+ let monitor: MonitorClient | null = null;
332
+
333
+ export function getMonitor(): MonitorClient {
334
+ if (!monitor) {
335
+ monitor = new MonitorClient({
336
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
337
+ endpoint: process.env.CEON_MONITOR_ENDPOINT || 'https://your-monitor-server.com',
338
+ environment: process.env.NODE_ENV || 'development',
339
+ trackDependencies: true,
340
+ });
341
+ }
342
+ return monitor;
343
+ }
169
344
  ```
170
345
 
171
- ### Next.js API Route
346
+ **2. Use in API routes (`app/api/example/route.ts`):**
172
347
 
173
348
  ```typescript
174
- import { MonitorClient } from '@ceon/monitor-sdk';
175
349
  import { NextRequest, NextResponse } from 'next/server';
350
+ import { getMonitor } from '@/lib/monitor';
176
351
 
177
- const monitor = new MonitorClient({
178
- apiKey: process.env.MONITOR_API_KEY!,
179
- endpoint: process.env.MONITOR_ENDPOINT!,
180
- environment: process.env.NODE_ENV,
181
- });
352
+ export async function POST(request: NextRequest) {
353
+ const monitor = getMonitor();
182
354
 
183
- export async function POST(req: NextRequest) {
184
355
  try {
185
- const body = await req.json();
186
- // ... handle request
356
+ const body = await request.json();
357
+ // Your logic here
187
358
  return NextResponse.json({ success: true });
188
359
  } catch (error) {
189
360
  await monitor.captureError(error as Error, {
@@ -191,35 +362,377 @@ export async function POST(req: NextRequest) {
191
362
  method: 'POST',
192
363
  statusCode: 500,
193
364
  });
194
- return NextResponse.json({ error: 'Internal error' }, { status: 500 });
365
+ return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
195
366
  }
196
367
  }
197
368
  ```
198
369
 
199
- ### Integration with Existing Logger
370
+ **3. Global error handling (`app/error.tsx`):**
371
+
372
+ ```typescript
373
+ 'use client';
374
+
375
+ import { useEffect } from 'react';
376
+
377
+ export default function Error({
378
+ error,
379
+ reset,
380
+ }: {
381
+ error: Error & { digest?: string };
382
+ reset: () => void;
383
+ }) {
384
+ useEffect(() => {
385
+ fetch('/api/log-error', {
386
+ method: 'POST',
387
+ headers: { 'Content-Type': 'application/json' },
388
+ body: JSON.stringify({
389
+ message: error.message,
390
+ stack: error.stack,
391
+ digest: error.digest,
392
+ }),
393
+ });
394
+ }, [error]);
395
+
396
+ return (
397
+ <div>
398
+ <h2>Something went wrong!</h2>
399
+ <button onClick={() => reset()}>Try again</button>
400
+ </div>
401
+ );
402
+ }
403
+ ```
404
+
405
+ **4. Create error logging API route (`app/api/log-error/route.ts`):**
200
406
 
201
407
  ```typescript
202
- import { MonitorClient } from '@ceon/monitor-sdk';
408
+ import { NextRequest, NextResponse } from 'next/server';
409
+ import { getMonitor } from '@/lib/monitor';
410
+
411
+ export async function POST(request: NextRequest) {
412
+ const monitor = getMonitor();
413
+ const { message, stack, digest } = await request.json();
414
+
415
+ await monitor.captureMessage(message, 'ERROR', {
416
+ metadata: { stack, digest, source: 'client' },
417
+ });
418
+
419
+ return NextResponse.json({ logged: true });
420
+ }
421
+ ```
422
+
423
+ ### React (Monolithic)
424
+
425
+ For a React project with Express backend in the same folder:
426
+
427
+ ```
428
+ my-app/
429
+ ├── package.json
430
+ ├── src/
431
+ │ ├── client/ # React frontend
432
+ │ └── server/ # Express backend
433
+ ```
434
+
435
+ **Server (`src/server/index.ts`):**
436
+
437
+ ```typescript
438
+ import express from 'express';
439
+ import path from 'path';
440
+ import { MonitorClient } from '@ceon-oy/monitor-sdk';
441
+
442
+ const app = express();
443
+ app.use(express.json());
203
444
 
204
445
  const monitor = new MonitorClient({
205
- apiKey: process.env.MONITOR_API_KEY!,
206
- endpoint: process.env.MONITOR_ENDPOINT!,
446
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
447
+ endpoint: process.env.CEON_MONITOR_ENDPOINT!,
448
+ environment: process.env.NODE_ENV || 'development',
449
+ trackDependencies: true,
207
450
  });
208
451
 
209
- // Wrap your existing logger
210
- export function logError(message: string, error?: Error, context?: object) {
211
- // Your existing logging
212
- console.error(message, error);
452
+ // Client error logging endpoint
453
+ app.post('/api/log-client-error', async (req, res) => {
454
+ const { message, stack, componentStack } = req.body;
455
+ await monitor.captureMessage(message, 'ERROR', {
456
+ metadata: { stack, componentStack, source: 'client' },
457
+ });
458
+ res.json({ logged: true });
459
+ });
213
460
 
214
- // Also send to monitor
215
- if (error) {
216
- monitor.captureError(error, { metadata: context });
217
- } else {
218
- monitor.captureMessage(message, 'ERROR', { metadata: context });
461
+ // Global error handler
462
+ app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
463
+ monitor.captureError(err, {
464
+ route: req.path,
465
+ method: req.method,
466
+ statusCode: 500,
467
+ });
468
+ res.status(500).json({ error: 'Internal server error' });
469
+ });
470
+
471
+ // Graceful shutdown
472
+ process.on('SIGTERM', async () => {
473
+ await monitor.close();
474
+ process.exit(0);
475
+ });
476
+
477
+ app.listen(3001, () => console.log('Server running on port 3001'));
478
+ ```
479
+
480
+ **React Error Boundary (`src/client/ErrorBoundary.tsx`):**
481
+
482
+ ```typescript
483
+ import React, { Component, ErrorInfo, ReactNode } from 'react';
484
+
485
+ interface Props {
486
+ children: ReactNode;
487
+ }
488
+
489
+ interface State {
490
+ hasError: boolean;
491
+ }
492
+
493
+ class ErrorBoundary extends Component<Props, State> {
494
+ constructor(props: Props) {
495
+ super(props);
496
+ this.state = { hasError: false };
497
+ }
498
+
499
+ static getDerivedStateFromError(): State {
500
+ return { hasError: true };
501
+ }
502
+
503
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
504
+ fetch('/api/log-client-error', {
505
+ method: 'POST',
506
+ headers: { 'Content-Type': 'application/json' },
507
+ body: JSON.stringify({
508
+ message: error.message,
509
+ stack: error.stack,
510
+ componentStack: errorInfo.componentStack,
511
+ }),
512
+ });
513
+ }
514
+
515
+ render() {
516
+ if (this.state.hasError) {
517
+ return <h1>Something went wrong.</h1>;
518
+ }
519
+ return this.props.children;
520
+ }
521
+ }
522
+
523
+ export default ErrorBoundary;
524
+ ```
525
+
526
+ ### React (Separate Server/Client)
527
+
528
+ For projects with separate server and client folders:
529
+
530
+ ```
531
+ my-project/
532
+ ├── server/
533
+ │ ├── package.json
534
+ │ └── src/index.ts
535
+ └── client/
536
+ ├── package.json
537
+ └── src/
538
+ ```
539
+
540
+ **Server (`server/src/index.ts`):**
541
+
542
+ ```typescript
543
+ import express from 'express';
544
+ import cors from 'cors';
545
+ import { MonitorClient } from '@ceon-oy/monitor-sdk';
546
+
547
+ const app = express();
548
+ app.use(cors());
549
+ app.use(express.json());
550
+
551
+ // Multi-environment dependency tracking
552
+ const monitor = new MonitorClient({
553
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
554
+ endpoint: process.env.CEON_MONITOR_ENDPOINT!,
555
+ environment: process.env.NODE_ENV || 'development',
556
+ trackDependencies: true,
557
+ dependencySources: [
558
+ { path: './package.json', environment: 'server' },
559
+ { path: '../client/package.json', environment: 'client' },
560
+ ],
561
+ excludePatterns: ['@types/*'],
562
+ });
563
+
564
+ // Endpoint for client-side error logging
565
+ app.post('/api/log-error', async (req, res) => {
566
+ const { message, stack, componentStack, url, userAgent } = req.body;
567
+
568
+ await monitor.captureMessage(message, 'ERROR', {
569
+ route: url,
570
+ metadata: { stack, componentStack, userAgent, source: 'client' },
571
+ });
572
+
573
+ res.json({ logged: true });
574
+ });
575
+
576
+ // Global error handler
577
+ app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
578
+ monitor.captureError(err, {
579
+ route: req.path,
580
+ method: req.method,
581
+ statusCode: 500,
582
+ userAgent: req.get('user-agent'),
583
+ ip: req.ip,
584
+ });
585
+ res.status(500).json({ error: 'Internal server error' });
586
+ });
587
+
588
+ // Graceful shutdown
589
+ process.on('SIGTERM', async () => {
590
+ await monitor.close();
591
+ process.exit(0);
592
+ });
593
+
594
+ app.listen(3001, () => console.log('Server running on port 3001'));
595
+ ```
596
+
597
+ **React Error Boundary (`client/src/components/ErrorBoundary.tsx`):**
598
+
599
+ ```typescript
600
+ import React, { Component, ErrorInfo, ReactNode } from 'react';
601
+
602
+ interface Props {
603
+ children: ReactNode;
604
+ }
605
+
606
+ interface State {
607
+ hasError: boolean;
608
+ error: Error | null;
609
+ }
610
+
611
+ class ErrorBoundary extends Component<Props, State> {
612
+ constructor(props: Props) {
613
+ super(props);
614
+ this.state = { hasError: false, error: null };
615
+ }
616
+
617
+ static getDerivedStateFromError(error: Error): State {
618
+ return { hasError: true, error };
619
+ }
620
+
621
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
622
+ fetch(`${process.env.REACT_APP_API_URL}/api/log-error`, {
623
+ method: 'POST',
624
+ headers: { 'Content-Type': 'application/json' },
625
+ body: JSON.stringify({
626
+ message: error.message,
627
+ stack: error.stack,
628
+ componentStack: errorInfo.componentStack,
629
+ url: window.location.href,
630
+ userAgent: navigator.userAgent,
631
+ }),
632
+ }).catch(console.error);
633
+ }
634
+
635
+ render() {
636
+ if (this.state.hasError) {
637
+ return (
638
+ <div>
639
+ <h1>Something went wrong</h1>
640
+ <button onClick={() => window.location.reload()}>Reload Page</button>
641
+ </div>
642
+ );
643
+ }
644
+ return this.props.children;
219
645
  }
220
646
  }
647
+
648
+ export default ErrorBoundary;
649
+ ```
650
+
651
+ **Global error handlers (`client/src/index.tsx`):**
652
+
653
+ ```typescript
654
+ import React from 'react';
655
+ import ReactDOM from 'react-dom/client';
656
+ import App from './App';
657
+ import ErrorBoundary from './components/ErrorBoundary';
658
+
659
+ // Global error handlers
660
+ window.onerror = (message, source, lineno, colno, error) => {
661
+ fetch(`${process.env.REACT_APP_API_URL}/api/log-error`, {
662
+ method: 'POST',
663
+ headers: { 'Content-Type': 'application/json' },
664
+ body: JSON.stringify({
665
+ message: String(message),
666
+ stack: error?.stack,
667
+ url: window.location.href,
668
+ userAgent: navigator.userAgent,
669
+ }),
670
+ }).catch(console.error);
671
+ };
672
+
673
+ window.onunhandledrejection = (event) => {
674
+ fetch(`${process.env.REACT_APP_API_URL}/api/log-error`, {
675
+ method: 'POST',
676
+ headers: { 'Content-Type': 'application/json' },
677
+ body: JSON.stringify({
678
+ message: event.reason?.message || 'Unhandled Promise Rejection',
679
+ stack: event.reason?.stack,
680
+ url: window.location.href,
681
+ userAgent: navigator.userAgent,
682
+ }),
683
+ }).catch(console.error);
684
+ };
685
+
686
+ const root = ReactDOM.createRoot(document.getElementById('root')!);
687
+ root.render(
688
+ <React.StrictMode>
689
+ <ErrorBoundary>
690
+ <App />
691
+ </ErrorBoundary>
692
+ </React.StrictMode>
693
+ );
221
694
  ```
222
695
 
696
+ ## API Reference
697
+
698
+ ### `MonitorClient`
699
+
700
+ #### `constructor(config: MonitorClientConfig)`
701
+
702
+ Creates a new monitor client instance.
703
+
704
+ #### `captureError(error: Error, context?: ErrorContext): Promise<void>`
705
+
706
+ Captures an error and queues it for sending.
707
+
708
+ #### `captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>`
709
+
710
+ Captures a log message with optional severity level.
711
+
712
+ #### `reportTechnologies(technologies: Technology[]): Promise<void>`
713
+
714
+ Reports technology stack information.
715
+
716
+ #### `reportSecurityEvent(event: SecurityEvent): Promise<void>`
717
+
718
+ Reports a security event.
719
+
720
+ #### `checkBruteForce(params: BruteForceParams): Promise<BruteForceResult>`
721
+
722
+ Checks for brute force attacks.
723
+
724
+ #### `auditDependencies(options?: AuditOptions): Promise<AuditResult | null>`
725
+
726
+ Runs npm audit and sends results to the server.
727
+
728
+ #### `flush(): Promise<void>`
729
+
730
+ Immediately sends all queued errors.
731
+
732
+ #### `close(): Promise<void>`
733
+
734
+ Flushes remaining errors and stops the client.
735
+
223
736
  ## Batching Behavior
224
737
 
225
738
  The SDK batches errors to reduce network overhead:
@@ -231,9 +744,9 @@ The SDK batches errors to reduce network overhead:
231
744
  - `flush()` is called manually
232
745
  - `close()` is called
233
746
 
234
- For serverless/edge environments where the process may terminate quickly, consider:
235
- - Setting `batchSize: 1` to send immediately
236
- - Calling `await monitor.flush()` at the end of each request
747
+ For serverless/edge environments where the process may terminate quickly:
748
+ - Set `batchSize: 1` to send immediately
749
+ - Call `await monitor.flush()` at the end of each request
237
750
 
238
751
  ## Building
239
752
 
@@ -258,4 +771,24 @@ interface ErrorContext {
258
771
  requestId?: string;
259
772
  metadata?: Record<string, unknown>;
260
773
  }
774
+
775
+ interface Technology {
776
+ name: string;
777
+ version: string;
778
+ type: 'runtime' | 'framework' | 'library' | 'database' | 'tool' | 'other';
779
+ environment?: string;
780
+ }
781
+
782
+ interface SecurityEvent {
783
+ eventType: string;
784
+ category: 'AUTHENTICATION' | 'AUTHORIZATION' | 'RATE_LIMIT' | 'SUSPICIOUS_ACTIVITY' | 'DATA_ACCESS';
785
+ severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
786
+ ip?: string;
787
+ identifier?: string;
788
+ metadata?: Record<string, unknown>;
789
+ }
261
790
  ```
791
+
792
+ ## License
793
+
794
+ MIT
package/dist/index.d.mts CHANGED
@@ -31,6 +31,8 @@ interface MonitorClientConfig {
31
31
  maxRetries?: number;
32
32
  /** Request timeout in ms (default: 10000) */
33
33
  requestTimeoutMs?: number;
34
+ /** Enable automatic vulnerability scanning based on server-configured interval (default: false) */
35
+ autoAudit?: boolean;
34
36
  }
35
37
  interface TechnologyItem {
36
38
  name: string;
@@ -135,12 +137,41 @@ declare class MonitorClient {
135
137
  private retryCount;
136
138
  private isFlushInProgress;
137
139
  private requestTimeoutMs;
140
+ private autoAudit;
141
+ private auditIntervalTimer;
142
+ private settingsPollingTimer;
143
+ private lastScanTime;
144
+ private lastKnownScanRequestedAt;
138
145
  constructor(config: MonitorClientConfig);
139
146
  captureError(error: Error, context?: ErrorContext): Promise<void>;
140
147
  captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>;
141
148
  flush(): Promise<void>;
142
149
  private getErrorKey;
143
150
  close(): Promise<void>;
151
+ private stopAuditIntervalTimer;
152
+ /**
153
+ * Fetch project settings from the monitoring server.
154
+ * Returns configuration including vulnerability scan interval and scan request timestamp.
155
+ */
156
+ fetchProjectSettings(): Promise<{
157
+ name: string;
158
+ vulnerabilityScanIntervalHours: number;
159
+ scanRequestedAt: string | null;
160
+ } | null>;
161
+ /**
162
+ * Setup automatic vulnerability scanning based on server-configured interval.
163
+ * Fetches settings from server and sets up recurring scans.
164
+ * Also sets up polling for on-demand scan requests from the server.
165
+ */
166
+ private setupAutoAudit;
167
+ /**
168
+ * Run a vulnerability scan and track the time it was run.
169
+ */
170
+ private runScanAndTrackTime;
171
+ /**
172
+ * Check if the server has requested an on-demand scan.
173
+ */
174
+ private checkForScanRequest;
144
175
  private enqueue;
145
176
  /**
146
177
  * Fetch with timeout to prevent hanging requests
package/dist/index.d.ts CHANGED
@@ -31,6 +31,8 @@ interface MonitorClientConfig {
31
31
  maxRetries?: number;
32
32
  /** Request timeout in ms (default: 10000) */
33
33
  requestTimeoutMs?: number;
34
+ /** Enable automatic vulnerability scanning based on server-configured interval (default: false) */
35
+ autoAudit?: boolean;
34
36
  }
35
37
  interface TechnologyItem {
36
38
  name: string;
@@ -135,12 +137,41 @@ declare class MonitorClient {
135
137
  private retryCount;
136
138
  private isFlushInProgress;
137
139
  private requestTimeoutMs;
140
+ private autoAudit;
141
+ private auditIntervalTimer;
142
+ private settingsPollingTimer;
143
+ private lastScanTime;
144
+ private lastKnownScanRequestedAt;
138
145
  constructor(config: MonitorClientConfig);
139
146
  captureError(error: Error, context?: ErrorContext): Promise<void>;
140
147
  captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>;
141
148
  flush(): Promise<void>;
142
149
  private getErrorKey;
143
150
  close(): Promise<void>;
151
+ private stopAuditIntervalTimer;
152
+ /**
153
+ * Fetch project settings from the monitoring server.
154
+ * Returns configuration including vulnerability scan interval and scan request timestamp.
155
+ */
156
+ fetchProjectSettings(): Promise<{
157
+ name: string;
158
+ vulnerabilityScanIntervalHours: number;
159
+ scanRequestedAt: string | null;
160
+ } | null>;
161
+ /**
162
+ * Setup automatic vulnerability scanning based on server-configured interval.
163
+ * Fetches settings from server and sets up recurring scans.
164
+ * Also sets up polling for on-demand scan requests from the server.
165
+ */
166
+ private setupAutoAudit;
167
+ /**
168
+ * Run a vulnerability scan and track the time it was run.
169
+ */
170
+ private runScanAndTrackTime;
171
+ /**
172
+ * Check if the server has requested an on-demand scan.
173
+ */
174
+ private checkForScanRequest;
144
175
  private enqueue;
145
176
  /**
146
177
  * Fetch with timeout to prevent hanging requests
package/dist/index.js CHANGED
@@ -42,6 +42,10 @@ var MonitorClient = class {
42
42
  this.isClosed = false;
43
43
  this.retryCount = /* @__PURE__ */ new Map();
44
44
  this.isFlushInProgress = false;
45
+ this.auditIntervalTimer = null;
46
+ this.settingsPollingTimer = null;
47
+ this.lastScanTime = null;
48
+ this.lastKnownScanRequestedAt = null;
45
49
  if (!config.apiKey || config.apiKey.trim().length === 0) {
46
50
  throw new Error("[MonitorClient] API key is required");
47
51
  }
@@ -86,12 +90,18 @@ var MonitorClient = class {
86
90
  "@typescript-eslint/*"
87
91
  ];
88
92
  this.excludePatterns = config.excludePatterns || defaultExcludePatterns;
93
+ this.autoAudit = config.autoAudit || false;
89
94
  this.startFlushTimer();
90
95
  if (this.trackDependencies) {
91
96
  this.syncDependencies().catch((err) => {
92
97
  console.error("[MonitorClient] Failed to sync dependencies:", err instanceof Error ? err.message : String(err));
93
98
  });
94
99
  }
100
+ if (this.autoAudit) {
101
+ this.setupAutoAudit().catch((err) => {
102
+ console.error("[MonitorClient] Failed to setup auto audit:", err instanceof Error ? err.message : String(err));
103
+ });
104
+ }
95
105
  }
96
106
  async captureError(error, context) {
97
107
  if (this.isClosed) return;
@@ -168,8 +178,101 @@ var MonitorClient = class {
168
178
  async close() {
169
179
  this.isClosed = true;
170
180
  this.stopFlushTimer();
181
+ this.stopAuditIntervalTimer();
171
182
  await this.flush();
172
183
  }
184
+ stopAuditIntervalTimer() {
185
+ if (this.auditIntervalTimer) {
186
+ clearInterval(this.auditIntervalTimer);
187
+ this.auditIntervalTimer = null;
188
+ }
189
+ if (this.settingsPollingTimer) {
190
+ clearInterval(this.settingsPollingTimer);
191
+ this.settingsPollingTimer = null;
192
+ }
193
+ }
194
+ /**
195
+ * Fetch project settings from the monitoring server.
196
+ * Returns configuration including vulnerability scan interval and scan request timestamp.
197
+ */
198
+ async fetchProjectSettings() {
199
+ try {
200
+ const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/project/settings`, {
201
+ method: "GET",
202
+ headers: {
203
+ "Authorization": `Bearer ${this.apiKey}`,
204
+ "Content-Type": "application/json"
205
+ }
206
+ });
207
+ if (!response.ok) {
208
+ console.warn("[MonitorClient] Failed to fetch project settings:", response.status);
209
+ return null;
210
+ }
211
+ const result = await response.json();
212
+ return result.data || null;
213
+ } catch (err) {
214
+ console.warn("[MonitorClient] Failed to fetch project settings:", err instanceof Error ? err.message : String(err));
215
+ return null;
216
+ }
217
+ }
218
+ /**
219
+ * Setup automatic vulnerability scanning based on server-configured interval.
220
+ * Fetches settings from server and sets up recurring scans.
221
+ * Also sets up polling for on-demand scan requests from the server.
222
+ */
223
+ async setupAutoAudit() {
224
+ const settings = await this.fetchProjectSettings();
225
+ if (!settings) {
226
+ console.warn("[MonitorClient] Could not fetch project settings, auto audit disabled");
227
+ return;
228
+ }
229
+ if (settings.scanRequestedAt) {
230
+ this.lastKnownScanRequestedAt = new Date(settings.scanRequestedAt);
231
+ }
232
+ const intervalHours = settings.vulnerabilityScanIntervalHours;
233
+ if (intervalHours <= 0) {
234
+ console.log("[MonitorClient] Scheduled vulnerability scanning disabled by server configuration");
235
+ } else {
236
+ console.log(`[MonitorClient] Auto vulnerability scanning enabled (every ${intervalHours} hours)`);
237
+ await this.runScanAndTrackTime();
238
+ const intervalMs = intervalHours * 60 * 60 * 1e3;
239
+ this.auditIntervalTimer = setInterval(() => {
240
+ this.runScanAndTrackTime();
241
+ }, intervalMs);
242
+ }
243
+ console.log("[MonitorClient] Polling for scan requests enabled (every 5 minutes)");
244
+ this.settingsPollingTimer = setInterval(() => {
245
+ this.checkForScanRequest();
246
+ }, 5 * 60 * 1e3);
247
+ }
248
+ /**
249
+ * Run a vulnerability scan and track the time it was run.
250
+ */
251
+ async runScanAndTrackTime() {
252
+ try {
253
+ await this.auditDependencies();
254
+ this.lastScanTime = /* @__PURE__ */ new Date();
255
+ } catch (err) {
256
+ console.error("[MonitorClient] Vulnerability scan failed:", err instanceof Error ? err.message : String(err));
257
+ }
258
+ }
259
+ /**
260
+ * Check if the server has requested an on-demand scan.
261
+ */
262
+ async checkForScanRequest() {
263
+ try {
264
+ const settings = await this.fetchProjectSettings();
265
+ if (!settings || !settings.scanRequestedAt) return;
266
+ const scanRequestedAt = new Date(settings.scanRequestedAt);
267
+ if (!this.lastKnownScanRequestedAt || scanRequestedAt > this.lastKnownScanRequestedAt) {
268
+ console.log("[MonitorClient] On-demand scan requested by server");
269
+ this.lastKnownScanRequestedAt = scanRequestedAt;
270
+ await this.runScanAndTrackTime();
271
+ }
272
+ } catch (err) {
273
+ console.error("[MonitorClient] Failed to check for scan request:", err instanceof Error ? err.message : String(err));
274
+ }
275
+ }
173
276
  enqueue(payload) {
174
277
  if (this.queue.length >= this.maxQueueSize) {
175
278
  console.warn("[MonitorClient] Queue full, dropping oldest error");
@@ -458,16 +561,24 @@ var MonitorClient = class {
458
561
  * @returns Audit summary with vulnerability counts
459
562
  */
460
563
  async auditDependencies(options = {}) {
461
- if (typeof require === "undefined") {
462
- console.warn("[MonitorClient] auditDependencies only works in Node.js environment");
564
+ if (typeof window !== "undefined" || typeof document !== "undefined") {
565
+ console.warn("[MonitorClient] auditDependencies only works in Node.js server environment");
566
+ return null;
567
+ }
568
+ let execSync;
569
+ let path;
570
+ let fs;
571
+ try {
572
+ execSync = require("child_process").execSync;
573
+ path = require("path");
574
+ fs = require("fs");
575
+ } catch {
576
+ console.warn("[MonitorClient] auditDependencies requires Node.js (not available in bundled/browser environments)");
463
577
  return null;
464
578
  }
465
579
  const startTime = Date.now();
466
580
  const environment = options.environment || this.environment;
467
581
  try {
468
- const { execSync } = require("child_process");
469
- const path = require("path");
470
- const fs = require("fs");
471
582
  let projectPath = options.projectPath || process.cwd();
472
583
  if (projectPath.includes("\0") || /[;&|`$(){}[\]<>]/.test(projectPath)) {
473
584
  console.error("[MonitorClient] Invalid projectPath: contains forbidden characters");
package/dist/index.mjs CHANGED
@@ -13,6 +13,10 @@ var MonitorClient = class {
13
13
  this.isClosed = false;
14
14
  this.retryCount = /* @__PURE__ */ new Map();
15
15
  this.isFlushInProgress = false;
16
+ this.auditIntervalTimer = null;
17
+ this.settingsPollingTimer = null;
18
+ this.lastScanTime = null;
19
+ this.lastKnownScanRequestedAt = null;
16
20
  if (!config.apiKey || config.apiKey.trim().length === 0) {
17
21
  throw new Error("[MonitorClient] API key is required");
18
22
  }
@@ -57,12 +61,18 @@ var MonitorClient = class {
57
61
  "@typescript-eslint/*"
58
62
  ];
59
63
  this.excludePatterns = config.excludePatterns || defaultExcludePatterns;
64
+ this.autoAudit = config.autoAudit || false;
60
65
  this.startFlushTimer();
61
66
  if (this.trackDependencies) {
62
67
  this.syncDependencies().catch((err) => {
63
68
  console.error("[MonitorClient] Failed to sync dependencies:", err instanceof Error ? err.message : String(err));
64
69
  });
65
70
  }
71
+ if (this.autoAudit) {
72
+ this.setupAutoAudit().catch((err) => {
73
+ console.error("[MonitorClient] Failed to setup auto audit:", err instanceof Error ? err.message : String(err));
74
+ });
75
+ }
66
76
  }
67
77
  async captureError(error, context) {
68
78
  if (this.isClosed) return;
@@ -139,8 +149,101 @@ var MonitorClient = class {
139
149
  async close() {
140
150
  this.isClosed = true;
141
151
  this.stopFlushTimer();
152
+ this.stopAuditIntervalTimer();
142
153
  await this.flush();
143
154
  }
155
+ stopAuditIntervalTimer() {
156
+ if (this.auditIntervalTimer) {
157
+ clearInterval(this.auditIntervalTimer);
158
+ this.auditIntervalTimer = null;
159
+ }
160
+ if (this.settingsPollingTimer) {
161
+ clearInterval(this.settingsPollingTimer);
162
+ this.settingsPollingTimer = null;
163
+ }
164
+ }
165
+ /**
166
+ * Fetch project settings from the monitoring server.
167
+ * Returns configuration including vulnerability scan interval and scan request timestamp.
168
+ */
169
+ async fetchProjectSettings() {
170
+ try {
171
+ const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/project/settings`, {
172
+ method: "GET",
173
+ headers: {
174
+ "Authorization": `Bearer ${this.apiKey}`,
175
+ "Content-Type": "application/json"
176
+ }
177
+ });
178
+ if (!response.ok) {
179
+ console.warn("[MonitorClient] Failed to fetch project settings:", response.status);
180
+ return null;
181
+ }
182
+ const result = await response.json();
183
+ return result.data || null;
184
+ } catch (err) {
185
+ console.warn("[MonitorClient] Failed to fetch project settings:", err instanceof Error ? err.message : String(err));
186
+ return null;
187
+ }
188
+ }
189
+ /**
190
+ * Setup automatic vulnerability scanning based on server-configured interval.
191
+ * Fetches settings from server and sets up recurring scans.
192
+ * Also sets up polling for on-demand scan requests from the server.
193
+ */
194
+ async setupAutoAudit() {
195
+ const settings = await this.fetchProjectSettings();
196
+ if (!settings) {
197
+ console.warn("[MonitorClient] Could not fetch project settings, auto audit disabled");
198
+ return;
199
+ }
200
+ if (settings.scanRequestedAt) {
201
+ this.lastKnownScanRequestedAt = new Date(settings.scanRequestedAt);
202
+ }
203
+ const intervalHours = settings.vulnerabilityScanIntervalHours;
204
+ if (intervalHours <= 0) {
205
+ console.log("[MonitorClient] Scheduled vulnerability scanning disabled by server configuration");
206
+ } else {
207
+ console.log(`[MonitorClient] Auto vulnerability scanning enabled (every ${intervalHours} hours)`);
208
+ await this.runScanAndTrackTime();
209
+ const intervalMs = intervalHours * 60 * 60 * 1e3;
210
+ this.auditIntervalTimer = setInterval(() => {
211
+ this.runScanAndTrackTime();
212
+ }, intervalMs);
213
+ }
214
+ console.log("[MonitorClient] Polling for scan requests enabled (every 5 minutes)");
215
+ this.settingsPollingTimer = setInterval(() => {
216
+ this.checkForScanRequest();
217
+ }, 5 * 60 * 1e3);
218
+ }
219
+ /**
220
+ * Run a vulnerability scan and track the time it was run.
221
+ */
222
+ async runScanAndTrackTime() {
223
+ try {
224
+ await this.auditDependencies();
225
+ this.lastScanTime = /* @__PURE__ */ new Date();
226
+ } catch (err) {
227
+ console.error("[MonitorClient] Vulnerability scan failed:", err instanceof Error ? err.message : String(err));
228
+ }
229
+ }
230
+ /**
231
+ * Check if the server has requested an on-demand scan.
232
+ */
233
+ async checkForScanRequest() {
234
+ try {
235
+ const settings = await this.fetchProjectSettings();
236
+ if (!settings || !settings.scanRequestedAt) return;
237
+ const scanRequestedAt = new Date(settings.scanRequestedAt);
238
+ if (!this.lastKnownScanRequestedAt || scanRequestedAt > this.lastKnownScanRequestedAt) {
239
+ console.log("[MonitorClient] On-demand scan requested by server");
240
+ this.lastKnownScanRequestedAt = scanRequestedAt;
241
+ await this.runScanAndTrackTime();
242
+ }
243
+ } catch (err) {
244
+ console.error("[MonitorClient] Failed to check for scan request:", err instanceof Error ? err.message : String(err));
245
+ }
246
+ }
144
247
  enqueue(payload) {
145
248
  if (this.queue.length >= this.maxQueueSize) {
146
249
  console.warn("[MonitorClient] Queue full, dropping oldest error");
@@ -429,16 +532,24 @@ var MonitorClient = class {
429
532
  * @returns Audit summary with vulnerability counts
430
533
  */
431
534
  async auditDependencies(options = {}) {
432
- if (typeof __require === "undefined") {
433
- console.warn("[MonitorClient] auditDependencies only works in Node.js environment");
535
+ if (typeof window !== "undefined" || typeof document !== "undefined") {
536
+ console.warn("[MonitorClient] auditDependencies only works in Node.js server environment");
537
+ return null;
538
+ }
539
+ let execSync;
540
+ let path;
541
+ let fs;
542
+ try {
543
+ execSync = __require("child_process").execSync;
544
+ path = __require("path");
545
+ fs = __require("fs");
546
+ } catch {
547
+ console.warn("[MonitorClient] auditDependencies requires Node.js (not available in bundled/browser environments)");
434
548
  return null;
435
549
  }
436
550
  const startTime = Date.now();
437
551
  const environment = options.environment || this.environment;
438
552
  try {
439
- const { execSync } = __require("child_process");
440
- const path = __require("path");
441
- const fs = __require("fs");
442
553
  let projectPath = options.projectPath || process.cwd();
443
554
  if (projectPath.includes("\0") || /[;&|`$(){}[\]<>]/.test(projectPath)) {
444
555
  console.error("[MonitorClient] Invalid projectPath: contains forbidden characters");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ceon-oy/monitor-sdk",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Client SDK for Ceon Monitor - Error tracking, health monitoring, security events, and vulnerability scanning",
5
5
  "author": "Ceon",
6
6
  "license": "MIT",