@ceon-oy/monitor-sdk 1.0.1

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 ADDED
@@ -0,0 +1,261 @@
1
+ # Ceon Monitor - SDK
2
+
3
+ Lightweight client SDK for sending errors and messages to the Ceon Monitor service.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # From GitHub
9
+ npm install github:ceon-oy/ceon-monitor-sdk
10
+ ```
11
+
12
+ Or add to your `package.json`:
13
+
14
+ ```json
15
+ {
16
+ "dependencies": {
17
+ "@ceon/monitor-sdk": "github:ceon-oy/ceon-monitor-sdk"
18
+ }
19
+ }
20
+ ```
21
+
22
+ ## Related
23
+
24
+ - [Ceon Monitor](https://github.com/ceon-oy/ceon-monitor) - Main monitoring server and dashboard
25
+
26
+ ## Quick Start
27
+
28
+ ```typescript
29
+ import { MonitorClient } from '@ceon/monitor-sdk';
30
+
31
+ // Initialize the client
32
+ const monitor = new MonitorClient({
33
+ apiKey: process.env.MONITOR_API_KEY!,
34
+ endpoint: 'https://monitor.example.com',
35
+ environment: process.env.NODE_ENV,
36
+ });
37
+
38
+ // Capture an error
39
+ try {
40
+ await riskyOperation();
41
+ } catch (error) {
42
+ await monitor.captureError(error as Error, {
43
+ route: '/api/users',
44
+ method: 'POST',
45
+ statusCode: 500,
46
+ });
47
+ }
48
+
49
+ // Capture a message
50
+ await monitor.captureMessage('User signed up', 'INFO', {
51
+ metadata: { userId: '123', plan: 'premium' },
52
+ });
53
+
54
+ // Flush and close before shutdown
55
+ await monitor.close();
56
+ ```
57
+
58
+ ## Configuration
59
+
60
+ ```typescript
61
+ 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)
67
+ }
68
+ ```
69
+
70
+ ## API
71
+
72
+ ### `MonitorClient`
73
+
74
+ #### `constructor(config: MonitorClientConfig)`
75
+
76
+ Creates a new monitor client instance.
77
+
78
+ ```typescript
79
+ const monitor = new MonitorClient({
80
+ apiKey: 'cm_your_api_key_here',
81
+ endpoint: 'https://monitor.example.com',
82
+ environment: 'production',
83
+ batchSize: 10,
84
+ flushIntervalMs: 5000,
85
+ });
86
+ ```
87
+
88
+ #### `captureError(error: Error, context?: ErrorContext): Promise<void>`
89
+
90
+ Captures an error and queues it for sending.
91
+
92
+ ```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
+ },
105
+ });
106
+ ```
107
+
108
+ #### `captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>`
109
+
110
+ Captures a log message.
111
+
112
+ ```typescript
113
+ await monitor.captureMessage('Payment processed', 'INFO', {
114
+ metadata: { orderId: '456', amount: 99.99 },
115
+ });
116
+ ```
117
+
118
+ #### `flush(): Promise<void>`
119
+
120
+ Immediately sends all queued errors. Called automatically based on `flushIntervalMs` and `batchSize`.
121
+
122
+ ```typescript
123
+ await monitor.flush();
124
+ ```
125
+
126
+ #### `close(): Promise<void>`
127
+
128
+ Flushes remaining errors and stops the client. Call this before your application shuts down.
129
+
130
+ ```typescript
131
+ await monitor.close();
132
+ ```
133
+
134
+ ## Usage Examples
135
+
136
+ ### Express.js Error Handler
137
+
138
+ ```typescript
139
+ import { MonitorClient } from '@ceon/monitor-sdk';
140
+ import { ErrorRequestHandler } from 'express';
141
+
142
+ const monitor = new MonitorClient({
143
+ apiKey: process.env.MONITOR_API_KEY!,
144
+ endpoint: process.env.MONITOR_ENDPOINT!,
145
+ environment: process.env.NODE_ENV,
146
+ });
147
+
148
+ export const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
149
+ monitor.captureError(err, {
150
+ route: req.path,
151
+ method: req.method,
152
+ statusCode: res.statusCode || 500,
153
+ userAgent: req.headers['user-agent'],
154
+ ip: req.ip,
155
+ metadata: {
156
+ query: req.query,
157
+ body: req.body,
158
+ },
159
+ });
160
+
161
+ res.status(500).json({ error: 'Internal server error' });
162
+ };
163
+
164
+ // Graceful shutdown
165
+ process.on('SIGTERM', async () => {
166
+ await monitor.close();
167
+ process.exit(0);
168
+ });
169
+ ```
170
+
171
+ ### Next.js API Route
172
+
173
+ ```typescript
174
+ import { MonitorClient } from '@ceon/monitor-sdk';
175
+ import { NextRequest, NextResponse } from 'next/server';
176
+
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
+ });
182
+
183
+ export async function POST(req: NextRequest) {
184
+ try {
185
+ const body = await req.json();
186
+ // ... handle request
187
+ return NextResponse.json({ success: true });
188
+ } catch (error) {
189
+ await monitor.captureError(error as Error, {
190
+ route: '/api/example',
191
+ method: 'POST',
192
+ statusCode: 500,
193
+ });
194
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 });
195
+ }
196
+ }
197
+ ```
198
+
199
+ ### Integration with Existing Logger
200
+
201
+ ```typescript
202
+ import { MonitorClient } from '@ceon/monitor-sdk';
203
+
204
+ const monitor = new MonitorClient({
205
+ apiKey: process.env.MONITOR_API_KEY!,
206
+ endpoint: process.env.MONITOR_ENDPOINT!,
207
+ });
208
+
209
+ // Wrap your existing logger
210
+ export function logError(message: string, error?: Error, context?: object) {
211
+ // Your existing logging
212
+ console.error(message, error);
213
+
214
+ // Also send to monitor
215
+ if (error) {
216
+ monitor.captureError(error, { metadata: context });
217
+ } else {
218
+ monitor.captureMessage(message, 'ERROR', { metadata: context });
219
+ }
220
+ }
221
+ ```
222
+
223
+ ## Batching Behavior
224
+
225
+ The SDK batches errors to reduce network overhead:
226
+
227
+ 1. Errors are queued locally
228
+ 2. Queue is flushed when:
229
+ - `batchSize` errors are queued (default: 10)
230
+ - `flushIntervalMs` milliseconds pass (default: 5000ms)
231
+ - `flush()` is called manually
232
+ - `close()` is called
233
+
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
237
+
238
+ ## Building
239
+
240
+ ```bash
241
+ npm install
242
+ npm run build # Build for production
243
+ npm run dev # Watch mode for development
244
+ ```
245
+
246
+ ## Types
247
+
248
+ ```typescript
249
+ type Severity = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR' | 'CRITICAL';
250
+
251
+ interface ErrorContext {
252
+ severity?: Severity;
253
+ route?: string;
254
+ method?: string;
255
+ statusCode?: number;
256
+ userAgent?: string;
257
+ ip?: string;
258
+ requestId?: string;
259
+ metadata?: Record<string, unknown>;
260
+ }
261
+ ```
@@ -0,0 +1,241 @@
1
+ type Severity = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR' | 'CRITICAL';
2
+ type TechnologyType = 'FRAMEWORK' | 'LIBRARY' | 'DATABASE' | 'RUNTIME' | 'TOOL' | 'OTHER';
3
+ type SecurityCategory = 'AUTHENTICATION' | 'AUTHORIZATION' | 'RATE_LIMIT' | 'INPUT_VALIDATION' | 'SUSPICIOUS_ACTIVITY';
4
+ type SecuritySeverity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
5
+ interface DependencySource {
6
+ path: string;
7
+ environment: string;
8
+ }
9
+ interface MonitorClientConfig {
10
+ /** API key for authentication (required, format: cm_xxx) */
11
+ apiKey: string;
12
+ /** Endpoint URL for the monitoring server (required, must be HTTP/HTTPS) */
13
+ endpoint: string;
14
+ /** Environment label (default: 'production') */
15
+ environment?: string;
16
+ /** Number of errors to batch before sending (default: 10, min: 1) */
17
+ batchSize?: number;
18
+ /** Interval in ms between automatic flushes (default: 5000, min: 1000) */
19
+ flushIntervalMs?: number;
20
+ /** Whether to track package.json dependencies (default: false) */
21
+ trackDependencies?: boolean;
22
+ /** Path to package.json for dependency tracking */
23
+ packageJsonPath?: string;
24
+ /** Multiple dependency sources with environment labels */
25
+ dependencySources?: DependencySource[];
26
+ /** Glob patterns for packages to exclude from tracking */
27
+ excludePatterns?: string[];
28
+ /** Maximum queue size to prevent memory exhaustion (default: 1000) */
29
+ maxQueueSize?: number;
30
+ /** Maximum retry attempts for failed sends (default: 3) */
31
+ maxRetries?: number;
32
+ /** Request timeout in ms (default: 10000) */
33
+ requestTimeoutMs?: number;
34
+ }
35
+ interface TechnologyItem {
36
+ name: string;
37
+ version: string;
38
+ type?: TechnologyType;
39
+ }
40
+ interface ErrorContext {
41
+ severity?: Severity;
42
+ route?: string;
43
+ method?: string;
44
+ statusCode?: number;
45
+ userAgent?: string;
46
+ ip?: string;
47
+ requestId?: string;
48
+ metadata?: Record<string, unknown>;
49
+ }
50
+ interface ErrorPayload {
51
+ severity: Severity;
52
+ message: string;
53
+ stack?: string;
54
+ environment: string;
55
+ route?: string;
56
+ method?: string;
57
+ statusCode?: number;
58
+ userAgent?: string;
59
+ ip?: string;
60
+ requestId?: string;
61
+ metadata?: Record<string, unknown>;
62
+ }
63
+ interface SecurityEventInput {
64
+ eventType: string;
65
+ category: SecurityCategory;
66
+ severity: SecuritySeverity;
67
+ ip?: string;
68
+ identifier?: string;
69
+ endpoint?: string;
70
+ userAgent?: string;
71
+ metadata?: Record<string, unknown>;
72
+ }
73
+ interface SecurityEventPayload extends SecurityEventInput {
74
+ environment: string;
75
+ }
76
+ interface BruteForceDetectionResult {
77
+ detected: boolean;
78
+ pattern: string;
79
+ count: number;
80
+ threshold: number;
81
+ timeWindowMinutes: number;
82
+ ip?: string;
83
+ identifier?: string;
84
+ }
85
+ type VulnerabilitySeverity = 'info' | 'low' | 'moderate' | 'high' | 'critical';
86
+ interface VulnerabilityItem {
87
+ packageName: string;
88
+ severity: VulnerabilitySeverity;
89
+ title: string;
90
+ url?: string;
91
+ vulnerableRange?: string;
92
+ installedVersion?: string;
93
+ patchedVersions?: string;
94
+ path?: string;
95
+ recommendation?: string;
96
+ cwe?: string[];
97
+ cvss?: number;
98
+ isFixable?: boolean;
99
+ isDirect?: boolean;
100
+ }
101
+ interface AuditResult {
102
+ environment: string;
103
+ totalDeps: number;
104
+ vulnerabilities: VulnerabilityItem[];
105
+ scanDurationMs: number;
106
+ }
107
+ interface AuditSummary {
108
+ scanId: string;
109
+ processed: number;
110
+ resolved: number;
111
+ summary: {
112
+ critical: number;
113
+ high: number;
114
+ moderate: number;
115
+ low: number;
116
+ info: number;
117
+ };
118
+ }
119
+
120
+ declare class MonitorClient {
121
+ private apiKey;
122
+ private endpoint;
123
+ private environment;
124
+ private batchSize;
125
+ private flushIntervalMs;
126
+ private queue;
127
+ private flushTimer;
128
+ private isClosed;
129
+ private trackDependencies;
130
+ private packageJsonPath?;
131
+ private dependencySources?;
132
+ private excludePatterns;
133
+ private maxQueueSize;
134
+ private maxRetries;
135
+ private retryCount;
136
+ private isFlushInProgress;
137
+ private requestTimeoutMs;
138
+ constructor(config: MonitorClientConfig);
139
+ captureError(error: Error, context?: ErrorContext): Promise<void>;
140
+ captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>;
141
+ flush(): Promise<void>;
142
+ private getErrorKey;
143
+ close(): Promise<void>;
144
+ private enqueue;
145
+ /**
146
+ * Fetch with timeout to prevent hanging requests
147
+ */
148
+ private fetchWithTimeout;
149
+ private sendSingle;
150
+ private sendBatch;
151
+ private startFlushTimer;
152
+ private stopFlushTimer;
153
+ syncDependencies(): Promise<void>;
154
+ syncTechnologies(technologies: TechnologyItem[]): Promise<void>;
155
+ private readPackageJson;
156
+ private readPackageJsonFromPath;
157
+ private shouldExclude;
158
+ private sendTechnologies;
159
+ private sendTechnologiesWithEnvironment;
160
+ /**
161
+ * Capture a security event (auth failures, rate limits, suspicious activity, etc.)
162
+ * Returns brute force warning if pattern is detected
163
+ */
164
+ captureSecurityEvent(input: SecurityEventInput): Promise<{
165
+ warning?: BruteForceDetectionResult;
166
+ }>;
167
+ /**
168
+ * Capture a login failure event (convenience method)
169
+ */
170
+ captureLoginFailure(options: {
171
+ ip?: string;
172
+ identifier?: string;
173
+ endpoint?: string;
174
+ userAgent?: string;
175
+ reason?: string;
176
+ authMethod?: 'password' | 'magic_link' | 'oauth' | 'azure_ad' | 'google' | 'other';
177
+ }): Promise<{
178
+ warning?: BruteForceDetectionResult;
179
+ }>;
180
+ /**
181
+ * Capture a successful login event
182
+ */
183
+ captureLoginSuccess(options: {
184
+ ip?: string;
185
+ identifier?: string;
186
+ endpoint?: string;
187
+ userAgent?: string;
188
+ authMethod?: 'password' | 'magic_link' | 'oauth' | 'azure_ad' | 'google' | 'other';
189
+ }): Promise<void>;
190
+ /**
191
+ * Capture a rate limit event
192
+ */
193
+ captureRateLimit(options: {
194
+ ip?: string;
195
+ identifier?: string;
196
+ endpoint?: string;
197
+ userAgent?: string;
198
+ limit?: number;
199
+ window?: string;
200
+ }): Promise<void>;
201
+ /**
202
+ * Capture an authorization failure (user tried to access unauthorized resource)
203
+ */
204
+ captureAuthorizationFailure(options: {
205
+ ip?: string;
206
+ identifier?: string;
207
+ endpoint?: string;
208
+ userAgent?: string;
209
+ resource?: string;
210
+ action?: string;
211
+ }): Promise<void>;
212
+ /**
213
+ * Check if an IP or identifier has triggered brute force detection
214
+ */
215
+ checkBruteForce(options: {
216
+ ip?: string;
217
+ identifier?: string;
218
+ timeWindowMinutes?: number;
219
+ threshold?: number;
220
+ }): Promise<BruteForceDetectionResult>;
221
+ /**
222
+ * Run npm audit and send results to the monitoring server.
223
+ * This scans the project for known vulnerabilities in dependencies.
224
+ *
225
+ * @param options.projectPath - Path to the project directory (defaults to cwd)
226
+ * @param options.environment - Environment label (defaults to client environment)
227
+ * @returns Audit summary with vulnerability counts
228
+ */
229
+ auditDependencies(options?: {
230
+ projectPath?: string;
231
+ environment?: string;
232
+ }): Promise<AuditSummary | null>;
233
+ /**
234
+ * Parse npm audit JSON output into vulnerability items
235
+ */
236
+ private parseNpmAuditOutput;
237
+ private getFixVersion;
238
+ private getRecommendation;
239
+ }
240
+
241
+ export { type AuditResult, type AuditSummary, type BruteForceDetectionResult, type DependencySource, type ErrorContext, type ErrorPayload, MonitorClient, type MonitorClientConfig, type SecurityCategory, type SecurityEventInput, type SecurityEventPayload, type SecuritySeverity, type Severity, type TechnologyItem, type TechnologyType, type VulnerabilityItem, type VulnerabilitySeverity };