@ceon-oy/monitor-sdk 1.0.12 → 1.0.14

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
@@ -17,6 +17,7 @@ Lightweight client SDK for integrating with the Ceon Monitor service. Provides e
17
17
  - [Next.js](#nextjs)
18
18
  - [React (Monolithic)](#react-monolithic)
19
19
  - [React (Separate Server/Client)](#react-separate-serverclient)
20
+ - [Real-World Example: Full-Stack Monitoring](#real-world-example-full-stack-monitoring)
20
21
  - [API Reference](#api-reference)
21
22
  - [Batching Behavior](#batching-behavior)
22
23
  - [Building](#building)
@@ -132,6 +133,111 @@ const monitor = new MonitorClient({
132
133
  });
133
134
  ```
134
135
 
136
+ ## Initialization Patterns by Runtime
137
+
138
+ The SDK initialization pattern depends on your application's runtime environment:
139
+
140
+ ### Long-Running Servers (Express, Fastify, plain Node.js)
141
+
142
+ Traditional Node.js servers run as persistent processes. Use explicit initialization and graceful shutdown:
143
+
144
+ ```typescript
145
+ // lib/monitor.ts
146
+ import { MonitorClient } from '@ceon-oy/monitor-sdk';
147
+
148
+ let monitor: MonitorClient | null = null;
149
+
150
+ export function initializeMonitor() {
151
+ if (!monitor) {
152
+ monitor = new MonitorClient({
153
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
154
+ endpoint: process.env.CEON_MONITOR_ENDPOINT!,
155
+ environment: process.env.NODE_ENV,
156
+ trackDependencies: true,
157
+ autoAudit: true,
158
+ });
159
+ }
160
+ return monitor;
161
+ }
162
+
163
+ export function getMonitor() {
164
+ if (!monitor) {
165
+ throw new Error('Monitor not initialized. Call initializeMonitor() first.');
166
+ }
167
+ return monitor;
168
+ }
169
+
170
+ // index.ts
171
+ import { initializeMonitor, getMonitor } from './lib/monitor';
172
+
173
+ const monitor = initializeMonitor();
174
+
175
+ // Graceful shutdown - ensures queued errors are sent before exit
176
+ process.on('SIGTERM', async () => {
177
+ console.log('Shutting down gracefully...');
178
+ await getMonitor().close();
179
+ process.exit(0);
180
+ });
181
+
182
+ process.on('SIGINT', async () => {
183
+ console.log('Shutting down gracefully...');
184
+ await getMonitor().close();
185
+ process.exit(0);
186
+ });
187
+ ```
188
+
189
+ **Why?** Long-running servers need:
190
+ - Single instance to avoid memory leaks
191
+ - Graceful shutdown to flush queued errors before exit
192
+ - Process signal handlers for clean termination
193
+
194
+ ### Next.js / Serverless / Edge Functions
195
+
196
+ Serverless environments handle lifecycle automatically. Create the client directly:
197
+
198
+ ```typescript
199
+ // lib/monitor.ts
200
+ import { MonitorClient } from '@ceon-oy/monitor-sdk';
201
+
202
+ export const monitor = new MonitorClient({
203
+ apiKey: process.env.CEON_MONITOR_API_KEY!,
204
+ endpoint: process.env.CEON_MONITOR_ENDPOINT!,
205
+ environment: process.env.NODE_ENV,
206
+ batchSize: 1, // Send immediately (no batching for short-lived functions)
207
+ });
208
+ ```
209
+
210
+ **Why no explicit shutdown?**
211
+ - Each request is isolated and short-lived
212
+ - The platform handles process lifecycle
213
+ - No persistent process to clean up
214
+ - Set `batchSize: 1` to send errors immediately (don't wait for batch)
215
+
216
+ For API routes that need guaranteed delivery:
217
+ ```typescript
218
+ // app/api/something/route.ts
219
+ import { monitor } from '@/lib/monitor';
220
+
221
+ export async function POST(request: Request) {
222
+ try {
223
+ // ... your logic
224
+ } catch (error) {
225
+ monitor.captureError(error as Error);
226
+ await monitor.flush(); // Ensure error is sent before response
227
+ }
228
+ }
229
+ ```
230
+
231
+ ### Summary Table
232
+
233
+ | Environment | Initialize Pattern | Graceful Shutdown | batchSize |
234
+ |-------------|-------------------|-------------------|-----------|
235
+ | Express/Fastify/Node.js | `initializeMonitor()` + singleton | Yes (SIGTERM/SIGINT handlers) | Default (10) |
236
+ | Next.js (App Router) | Direct instantiation | No | 1 (recommended) |
237
+ | Next.js (API Routes) | Direct instantiation | No, but call `flush()` | 1 (recommended) |
238
+ | Vercel/AWS Lambda | Direct instantiation | No, but call `flush()` | 1 (required) |
239
+ | Docker/Kubernetes | `initializeMonitor()` + singleton | Yes (for graceful pod termination) | Default (10) |
240
+
135
241
  ## Features
136
242
 
137
243
  ### Error Capture
@@ -193,7 +299,7 @@ await monitor.reportTechnologies([
193
299
 
194
300
  ### Vulnerability Auditing
195
301
 
196
- Run npm audit and send results to the monitoring server:
302
+ Scan your dependencies for security vulnerabilities and report them to Ceon Monitor.
197
303
 
198
304
  ```typescript
199
305
  // Basic audit
@@ -211,6 +317,18 @@ await monitor.auditDependencies({
211
317
  });
212
318
  ```
213
319
 
320
+ #### Supported Package Managers
321
+
322
+ The SDK automatically detects your package manager based on lock files:
323
+
324
+ | Package Manager | Detection | Audit Command |
325
+ |-----------------|-----------|---------------|
326
+ | **npm** | `package-lock.json` (default) | `npm audit --json` |
327
+ | **yarn** | `yarn.lock` | `yarn audit --json` |
328
+ | **pnpm** | `pnpm-lock.yaml` | `npm audit --json` (compatibility) |
329
+
330
+ No configuration needed - the SDK auto-detects and uses the appropriate audit command.
331
+
214
332
  #### Automatic Auditing (Recommended)
215
333
 
216
334
  Enable `autoAudit: true` to let the SDK automatically scan for vulnerabilities based on the interval configured in the Ceon Monitor dashboard:
@@ -738,6 +856,127 @@ root.render(
738
856
  );
739
857
  ```
740
858
 
859
+ ## Real-World Example: Full-Stack Monitoring
860
+
861
+ This example shows how the Ceon Hours project monitors both server and client code from a single SDK installation on the server.
862
+
863
+ **Project Structure:**
864
+ ```
865
+ ceon-projects/
866
+ ├── ceon-hours-api/ # Express server (SDK installed here)
867
+ │ ├── package.json
868
+ │ └── lib/config/monitor-setup.js
869
+ └── ceon-hours-web-app/ # React client (no SDK needed)
870
+ └── package.json
871
+ ```
872
+
873
+ **1. Monitor Setup (`ceon-hours-api/lib/config/monitor-setup.js`):**
874
+
875
+ ```javascript
876
+ const { MonitorClient } = require("@ceon-oy/monitor-sdk");
877
+
878
+ let monitor = null;
879
+
880
+ function initializeMonitor() {
881
+ if (process.env.CEON_MONITOR_API_KEY) {
882
+ monitor = new MonitorClient({
883
+ apiKey: process.env.CEON_MONITOR_API_KEY,
884
+ endpoint: process.env.CEON_MONITOR_ENDPOINT || "https://ceonmonitor.com",
885
+ environment: "server",
886
+ trackDependencies: true,
887
+ autoAudit: true,
888
+ // Track dependencies from both server AND client
889
+ dependencySources: [
890
+ { path: "./package.json", environment: "server" },
891
+ { path: "../ceon-hours-web-app/package.json", environment: "client" },
892
+ ],
893
+ // Audit vulnerabilities in both
894
+ auditPaths: [
895
+ { path: ".", environment: "server" },
896
+ { path: "../ceon-hours-web-app", environment: "client" },
897
+ ],
898
+ });
899
+ console.log("[CeonMonitor] SDK initialized");
900
+ }
901
+ return monitor;
902
+ }
903
+
904
+ function getMonitor() {
905
+ return monitor;
906
+ }
907
+
908
+ module.exports = { initializeMonitor, getMonitor };
909
+ ```
910
+
911
+ **2. Initialize in Server Entry (`ceon-hours-api/index.js`):**
912
+
913
+ ```javascript
914
+ const { initializeMonitor, getMonitor } = require("./lib/config/monitor-setup");
915
+
916
+ // Initialize at startup
917
+ const monitor = initializeMonitor();
918
+
919
+ // Export for middleware
920
+ module.exports.getMonitor = getMonitor;
921
+
922
+ // Graceful shutdown
923
+ process.on("SIGTERM", async () => {
924
+ console.log("[CeonMonitor] Flushing pending errors...");
925
+ if (monitor) await monitor.flush();
926
+ process.exit(0);
927
+ });
928
+ ```
929
+
930
+ **3. Error Handler Middleware (`ceon-hours-api/lib/middleware/errorHandler.js`):**
931
+
932
+ ```javascript
933
+ const { getMonitor } = require("../config/monitor-setup");
934
+
935
+ const errorHandler = (error, req, res, next) => {
936
+ const monitor = getMonitor();
937
+ if (monitor && error.status >= 400) {
938
+ monitor.captureError(error, {
939
+ route: req.path,
940
+ method: req.method,
941
+ statusCode: error.status || 500,
942
+ userAgent: req.get("user-agent"),
943
+ ip: req.ip,
944
+ severity: error.status >= 500 ? "ERROR" : "WARNING",
945
+ }).catch(console.error);
946
+ }
947
+
948
+ res.status(error.status || 500).json({ error: error.message });
949
+ };
950
+
951
+ module.exports = errorHandler;
952
+ ```
953
+
954
+ **4. Environment Variables (`.env`):**
955
+
956
+ ```bash
957
+ # Get API key from https://ceonmonitor.com dashboard
958
+ CEON_MONITOR_API_KEY=your-project-api-key
959
+
960
+ # Endpoint options:
961
+ # - Production: https://ceonmonitor.com
962
+ # - Local dev: http://localhost:4040
963
+ # - Docker dev: http://host.docker.internal:4040
964
+ CEON_MONITOR_ENDPOINT=https://ceonmonitor.com
965
+ ```
966
+
967
+ ### Important: Relative Path Requirements
968
+
969
+ The `dependencySources` and `auditPaths` use **relative paths** from the server's working directory. For multi-project monitoring to work:
970
+
971
+ 1. **Directory structure must match** - The client project must be at the expected relative path (e.g., `../ceon-hours-web-app`)
972
+ 2. **Both projects must be present** - If the client folder doesn't exist, only server dependencies will be tracked
973
+ 3. **npm must be available** - Vulnerability auditing requires npm to be installed
974
+
975
+ **Troubleshooting:** If client dependencies aren't being tracked on other machines:
976
+ - Verify the relative path is correct for that machine's directory structure
977
+ - Check that the client's `package.json` exists at the expected path
978
+ - The SDK will log warnings if paths are invalid
979
+
741
980
  ## API Reference
742
981
 
743
982
  ### `MonitorClient`
package/dist/index.d.mts CHANGED
@@ -41,6 +41,8 @@ interface MonitorClientConfig {
41
41
  autoAudit?: boolean;
42
42
  /** Multiple directories to audit for vulnerabilities (runs npm audit in each) */
43
43
  auditPaths?: AuditPath[];
44
+ /** Include devDependencies when tracking dependencies (default: false) */
45
+ includeDevDependencies?: boolean;
44
46
  }
45
47
  interface TechnologyItem {
46
48
  name: string;
@@ -164,6 +166,7 @@ declare class MonitorClient {
164
166
  private excludePatterns;
165
167
  private compiledExcludePatterns;
166
168
  private auditPaths?;
169
+ private includeDevDependencies;
167
170
  private maxQueueSize;
168
171
  private maxRetries;
169
172
  private retryCount;
package/dist/index.d.ts CHANGED
@@ -41,6 +41,8 @@ interface MonitorClientConfig {
41
41
  autoAudit?: boolean;
42
42
  /** Multiple directories to audit for vulnerabilities (runs npm audit in each) */
43
43
  auditPaths?: AuditPath[];
44
+ /** Include devDependencies when tracking dependencies (default: false) */
45
+ includeDevDependencies?: boolean;
44
46
  }
45
47
  interface TechnologyItem {
46
48
  name: string;
@@ -164,6 +166,7 @@ declare class MonitorClient {
164
166
  private excludePatterns;
165
167
  private compiledExcludePatterns;
166
168
  private auditPaths?;
169
+ private includeDevDependencies;
167
170
  private maxQueueSize;
168
171
  private maxRetries;
169
172
  private retryCount;
package/dist/index.js CHANGED
@@ -116,6 +116,7 @@ var MonitorClient = class {
116
116
  });
117
117
  this.autoAudit = config.autoAudit || false;
118
118
  this.auditPaths = config.auditPaths;
119
+ this.includeDevDependencies = config.includeDevDependencies ?? false;
119
120
  this.startFlushTimer();
120
121
  if (this.trackDependencies) {
121
122
  this.syncDependencies().catch((err) => {
@@ -488,10 +489,7 @@ var MonitorClient = class {
488
489
  }
489
490
  const packageJson = JSON.parse(fs.readFileSync(normalizedPath, "utf-8"));
490
491
  const technologies = [];
491
- const deps = {
492
- ...packageJson.dependencies,
493
- ...packageJson.devDependencies
494
- };
492
+ const deps = this.includeDevDependencies ? { ...packageJson.dependencies, ...packageJson.devDependencies } : { ...packageJson.dependencies };
495
493
  for (const [name, version] of Object.entries(deps)) {
496
494
  if (typeof version === "string") {
497
495
  if (this.shouldExclude(name)) {
package/dist/index.mjs CHANGED
@@ -80,6 +80,7 @@ var MonitorClient = class {
80
80
  });
81
81
  this.autoAudit = config.autoAudit || false;
82
82
  this.auditPaths = config.auditPaths;
83
+ this.includeDevDependencies = config.includeDevDependencies ?? false;
83
84
  this.startFlushTimer();
84
85
  if (this.trackDependencies) {
85
86
  this.syncDependencies().catch((err) => {
@@ -452,10 +453,7 @@ var MonitorClient = class {
452
453
  }
453
454
  const packageJson = JSON.parse(fs.readFileSync(normalizedPath, "utf-8"));
454
455
  const technologies = [];
455
- const deps = {
456
- ...packageJson.dependencies,
457
- ...packageJson.devDependencies
458
- };
456
+ const deps = this.includeDevDependencies ? { ...packageJson.dependencies, ...packageJson.devDependencies } : { ...packageJson.dependencies };
459
457
  for (const [name, version] of Object.entries(deps)) {
460
458
  if (typeof version === "string") {
461
459
  if (this.shouldExclude(name)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ceon-oy/monitor-sdk",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
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",