@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 +240 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -4
- package/dist/index.mjs +2 -4
- package/package.json +1 -1
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
|
-
|
|
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