@ceon-oy/monitor-sdk 1.0.13 → 1.0.15
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 +122 -0
- package/dist/index.d.mts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +66 -6
- package/dist/index.mjs +66 -6
- 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)
|
|
@@ -855,6 +856,127 @@ root.render(
|
|
|
855
856
|
);
|
|
856
857
|
```
|
|
857
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
|
+
|
|
858
980
|
## API Reference
|
|
859
981
|
|
|
860
982
|
### `MonitorClient`
|
package/dist/index.d.mts
CHANGED
|
@@ -41,11 +41,16 @@ 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;
|
|
46
|
+
/** Enable fetching latest versions from npm registry (default: true) */
|
|
47
|
+
versionCheckEnabled?: boolean;
|
|
44
48
|
}
|
|
45
49
|
interface TechnologyItem {
|
|
46
50
|
name: string;
|
|
47
51
|
version: string;
|
|
48
52
|
type?: TechnologyType;
|
|
53
|
+
latestVersion?: string;
|
|
49
54
|
}
|
|
50
55
|
interface ErrorContext {
|
|
51
56
|
severity?: Severity;
|
|
@@ -164,6 +169,7 @@ declare class MonitorClient {
|
|
|
164
169
|
private excludePatterns;
|
|
165
170
|
private compiledExcludePatterns;
|
|
166
171
|
private auditPaths?;
|
|
172
|
+
private includeDevDependencies;
|
|
167
173
|
private maxQueueSize;
|
|
168
174
|
private maxRetries;
|
|
169
175
|
private retryCount;
|
|
@@ -175,6 +181,7 @@ declare class MonitorClient {
|
|
|
175
181
|
private lastScanTime;
|
|
176
182
|
private lastKnownScanRequestedAt;
|
|
177
183
|
private lastKnownTechScanRequestedAt;
|
|
184
|
+
private versionCheckEnabled;
|
|
178
185
|
constructor(config: MonitorClientConfig);
|
|
179
186
|
/**
|
|
180
187
|
* Security: Validate and sanitize metadata to prevent oversized payloads
|
|
@@ -226,10 +233,24 @@ declare class MonitorClient {
|
|
|
226
233
|
private startFlushTimer;
|
|
227
234
|
private stopFlushTimer;
|
|
228
235
|
syncDependencies(): Promise<void>;
|
|
236
|
+
/**
|
|
237
|
+
* Enrich technologies with latest version information from npm registry.
|
|
238
|
+
* Only runs if versionCheckEnabled is true.
|
|
239
|
+
*/
|
|
240
|
+
private enrichWithLatestVersions;
|
|
229
241
|
syncTechnologies(technologies: TechnologyItem[]): Promise<void>;
|
|
230
242
|
private readPackageJson;
|
|
231
243
|
private readPackageJsonFromPath;
|
|
232
244
|
private shouldExclude;
|
|
245
|
+
/**
|
|
246
|
+
* Fetch the latest version of a package from npm registry.
|
|
247
|
+
* Returns null if the package cannot be found or the request fails.
|
|
248
|
+
*/
|
|
249
|
+
private fetchLatestVersion;
|
|
250
|
+
/**
|
|
251
|
+
* Fetch latest versions for multiple packages in parallel with concurrency limit.
|
|
252
|
+
*/
|
|
253
|
+
private fetchLatestVersions;
|
|
233
254
|
private sendTechnologies;
|
|
234
255
|
private sendTechnologiesWithEnvironment;
|
|
235
256
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -41,11 +41,16 @@ 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;
|
|
46
|
+
/** Enable fetching latest versions from npm registry (default: true) */
|
|
47
|
+
versionCheckEnabled?: boolean;
|
|
44
48
|
}
|
|
45
49
|
interface TechnologyItem {
|
|
46
50
|
name: string;
|
|
47
51
|
version: string;
|
|
48
52
|
type?: TechnologyType;
|
|
53
|
+
latestVersion?: string;
|
|
49
54
|
}
|
|
50
55
|
interface ErrorContext {
|
|
51
56
|
severity?: Severity;
|
|
@@ -164,6 +169,7 @@ declare class MonitorClient {
|
|
|
164
169
|
private excludePatterns;
|
|
165
170
|
private compiledExcludePatterns;
|
|
166
171
|
private auditPaths?;
|
|
172
|
+
private includeDevDependencies;
|
|
167
173
|
private maxQueueSize;
|
|
168
174
|
private maxRetries;
|
|
169
175
|
private retryCount;
|
|
@@ -175,6 +181,7 @@ declare class MonitorClient {
|
|
|
175
181
|
private lastScanTime;
|
|
176
182
|
private lastKnownScanRequestedAt;
|
|
177
183
|
private lastKnownTechScanRequestedAt;
|
|
184
|
+
private versionCheckEnabled;
|
|
178
185
|
constructor(config: MonitorClientConfig);
|
|
179
186
|
/**
|
|
180
187
|
* Security: Validate and sanitize metadata to prevent oversized payloads
|
|
@@ -226,10 +233,24 @@ declare class MonitorClient {
|
|
|
226
233
|
private startFlushTimer;
|
|
227
234
|
private stopFlushTimer;
|
|
228
235
|
syncDependencies(): Promise<void>;
|
|
236
|
+
/**
|
|
237
|
+
* Enrich technologies with latest version information from npm registry.
|
|
238
|
+
* Only runs if versionCheckEnabled is true.
|
|
239
|
+
*/
|
|
240
|
+
private enrichWithLatestVersions;
|
|
229
241
|
syncTechnologies(technologies: TechnologyItem[]): Promise<void>;
|
|
230
242
|
private readPackageJson;
|
|
231
243
|
private readPackageJsonFromPath;
|
|
232
244
|
private shouldExclude;
|
|
245
|
+
/**
|
|
246
|
+
* Fetch the latest version of a package from npm registry.
|
|
247
|
+
* Returns null if the package cannot be found or the request fails.
|
|
248
|
+
*/
|
|
249
|
+
private fetchLatestVersion;
|
|
250
|
+
/**
|
|
251
|
+
* Fetch latest versions for multiple packages in parallel with concurrency limit.
|
|
252
|
+
*/
|
|
253
|
+
private fetchLatestVersions;
|
|
233
254
|
private sendTechnologies;
|
|
234
255
|
private sendTechnologiesWithEnvironment;
|
|
235
256
|
/**
|
package/dist/index.js
CHANGED
|
@@ -116,6 +116,8 @@ var MonitorClient = class {
|
|
|
116
116
|
});
|
|
117
117
|
this.autoAudit = config.autoAudit || false;
|
|
118
118
|
this.auditPaths = config.auditPaths;
|
|
119
|
+
this.includeDevDependencies = config.includeDevDependencies ?? false;
|
|
120
|
+
this.versionCheckEnabled = config.versionCheckEnabled ?? true;
|
|
119
121
|
this.startFlushTimer();
|
|
120
122
|
if (this.trackDependencies) {
|
|
121
123
|
this.syncDependencies().catch((err) => {
|
|
@@ -436,17 +438,40 @@ var MonitorClient = class {
|
|
|
436
438
|
for (const source of this.dependencySources) {
|
|
437
439
|
const technologies = await this.readPackageJsonFromPath(source.path);
|
|
438
440
|
if (technologies.length === 0) continue;
|
|
439
|
-
await this.
|
|
441
|
+
const enrichedTechnologies = await this.enrichWithLatestVersions(technologies);
|
|
442
|
+
await this.sendTechnologiesWithEnvironment(enrichedTechnologies, source.environment);
|
|
440
443
|
}
|
|
441
444
|
} else {
|
|
442
445
|
const technologies = await this.readPackageJson();
|
|
443
446
|
if (technologies.length === 0) return;
|
|
444
|
-
await this.
|
|
447
|
+
const enrichedTechnologies = await this.enrichWithLatestVersions(technologies);
|
|
448
|
+
await this.sendTechnologies(enrichedTechnologies);
|
|
445
449
|
}
|
|
446
450
|
} catch (err) {
|
|
447
451
|
console.error("[MonitorClient] Failed to sync dependencies:", err);
|
|
448
452
|
}
|
|
449
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* Enrich technologies with latest version information from npm registry.
|
|
456
|
+
* Only runs if versionCheckEnabled is true.
|
|
457
|
+
*/
|
|
458
|
+
async enrichWithLatestVersions(technologies) {
|
|
459
|
+
if (!this.versionCheckEnabled) {
|
|
460
|
+
return technologies;
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
console.log(`[MonitorClient] Fetching latest versions for ${technologies.length} packages...`);
|
|
464
|
+
const packageNames = technologies.map((t) => t.name);
|
|
465
|
+
const latestVersions = await this.fetchLatestVersions(packageNames);
|
|
466
|
+
return technologies.map((tech) => ({
|
|
467
|
+
...tech,
|
|
468
|
+
latestVersion: latestVersions.get(tech.name) || void 0
|
|
469
|
+
}));
|
|
470
|
+
} catch (err) {
|
|
471
|
+
console.warn("[MonitorClient] Failed to fetch latest versions, continuing without:", err instanceof Error ? err.message : String(err));
|
|
472
|
+
return technologies;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
450
475
|
async syncTechnologies(technologies) {
|
|
451
476
|
await this.sendTechnologies(technologies);
|
|
452
477
|
}
|
|
@@ -488,10 +513,7 @@ var MonitorClient = class {
|
|
|
488
513
|
}
|
|
489
514
|
const packageJson = JSON.parse(fs.readFileSync(normalizedPath, "utf-8"));
|
|
490
515
|
const technologies = [];
|
|
491
|
-
const deps = {
|
|
492
|
-
...packageJson.dependencies,
|
|
493
|
-
...packageJson.devDependencies
|
|
494
|
-
};
|
|
516
|
+
const deps = this.includeDevDependencies ? { ...packageJson.dependencies, ...packageJson.devDependencies } : { ...packageJson.dependencies };
|
|
495
517
|
for (const [name, version] of Object.entries(deps)) {
|
|
496
518
|
if (typeof version === "string") {
|
|
497
519
|
if (this.shouldExclude(name)) {
|
|
@@ -516,6 +538,44 @@ var MonitorClient = class {
|
|
|
516
538
|
}
|
|
517
539
|
return false;
|
|
518
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Fetch the latest version of a package from npm registry.
|
|
543
|
+
* Returns null if the package cannot be found or the request fails.
|
|
544
|
+
*/
|
|
545
|
+
async fetchLatestVersion(packageName) {
|
|
546
|
+
try {
|
|
547
|
+
const encodedName = encodeURIComponent(packageName).replace("%40", "@");
|
|
548
|
+
const response = await fetch(`https://registry.npmjs.org/${encodedName}`, {
|
|
549
|
+
headers: { "Accept": "application/json" },
|
|
550
|
+
signal: AbortSignal.timeout(5e3)
|
|
551
|
+
});
|
|
552
|
+
if (!response.ok) return null;
|
|
553
|
+
const data = await response.json();
|
|
554
|
+
return data["dist-tags"]?.latest || null;
|
|
555
|
+
} catch {
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Fetch latest versions for multiple packages in parallel with concurrency limit.
|
|
561
|
+
*/
|
|
562
|
+
async fetchLatestVersions(packageNames) {
|
|
563
|
+
const results = /* @__PURE__ */ new Map();
|
|
564
|
+
const concurrencyLimit = 5;
|
|
565
|
+
for (let i = 0; i < packageNames.length; i += concurrencyLimit) {
|
|
566
|
+
const batch = packageNames.slice(i, i + concurrencyLimit);
|
|
567
|
+
const batchResults = await Promise.all(
|
|
568
|
+
batch.map(async (name) => ({
|
|
569
|
+
name,
|
|
570
|
+
version: await this.fetchLatestVersion(name)
|
|
571
|
+
}))
|
|
572
|
+
);
|
|
573
|
+
for (const { name, version } of batchResults) {
|
|
574
|
+
results.set(name, version);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return results;
|
|
578
|
+
}
|
|
519
579
|
async sendTechnologies(technologies) {
|
|
520
580
|
await this.sendTechnologiesWithEnvironment(technologies, this.environment);
|
|
521
581
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -80,6 +80,8 @@ var MonitorClient = class {
|
|
|
80
80
|
});
|
|
81
81
|
this.autoAudit = config.autoAudit || false;
|
|
82
82
|
this.auditPaths = config.auditPaths;
|
|
83
|
+
this.includeDevDependencies = config.includeDevDependencies ?? false;
|
|
84
|
+
this.versionCheckEnabled = config.versionCheckEnabled ?? true;
|
|
83
85
|
this.startFlushTimer();
|
|
84
86
|
if (this.trackDependencies) {
|
|
85
87
|
this.syncDependencies().catch((err) => {
|
|
@@ -400,17 +402,40 @@ var MonitorClient = class {
|
|
|
400
402
|
for (const source of this.dependencySources) {
|
|
401
403
|
const technologies = await this.readPackageJsonFromPath(source.path);
|
|
402
404
|
if (technologies.length === 0) continue;
|
|
403
|
-
await this.
|
|
405
|
+
const enrichedTechnologies = await this.enrichWithLatestVersions(technologies);
|
|
406
|
+
await this.sendTechnologiesWithEnvironment(enrichedTechnologies, source.environment);
|
|
404
407
|
}
|
|
405
408
|
} else {
|
|
406
409
|
const technologies = await this.readPackageJson();
|
|
407
410
|
if (technologies.length === 0) return;
|
|
408
|
-
await this.
|
|
411
|
+
const enrichedTechnologies = await this.enrichWithLatestVersions(technologies);
|
|
412
|
+
await this.sendTechnologies(enrichedTechnologies);
|
|
409
413
|
}
|
|
410
414
|
} catch (err) {
|
|
411
415
|
console.error("[MonitorClient] Failed to sync dependencies:", err);
|
|
412
416
|
}
|
|
413
417
|
}
|
|
418
|
+
/**
|
|
419
|
+
* Enrich technologies with latest version information from npm registry.
|
|
420
|
+
* Only runs if versionCheckEnabled is true.
|
|
421
|
+
*/
|
|
422
|
+
async enrichWithLatestVersions(technologies) {
|
|
423
|
+
if (!this.versionCheckEnabled) {
|
|
424
|
+
return technologies;
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
console.log(`[MonitorClient] Fetching latest versions for ${technologies.length} packages...`);
|
|
428
|
+
const packageNames = technologies.map((t) => t.name);
|
|
429
|
+
const latestVersions = await this.fetchLatestVersions(packageNames);
|
|
430
|
+
return technologies.map((tech) => ({
|
|
431
|
+
...tech,
|
|
432
|
+
latestVersion: latestVersions.get(tech.name) || void 0
|
|
433
|
+
}));
|
|
434
|
+
} catch (err) {
|
|
435
|
+
console.warn("[MonitorClient] Failed to fetch latest versions, continuing without:", err instanceof Error ? err.message : String(err));
|
|
436
|
+
return technologies;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
414
439
|
async syncTechnologies(technologies) {
|
|
415
440
|
await this.sendTechnologies(technologies);
|
|
416
441
|
}
|
|
@@ -452,10 +477,7 @@ var MonitorClient = class {
|
|
|
452
477
|
}
|
|
453
478
|
const packageJson = JSON.parse(fs.readFileSync(normalizedPath, "utf-8"));
|
|
454
479
|
const technologies = [];
|
|
455
|
-
const deps = {
|
|
456
|
-
...packageJson.dependencies,
|
|
457
|
-
...packageJson.devDependencies
|
|
458
|
-
};
|
|
480
|
+
const deps = this.includeDevDependencies ? { ...packageJson.dependencies, ...packageJson.devDependencies } : { ...packageJson.dependencies };
|
|
459
481
|
for (const [name, version] of Object.entries(deps)) {
|
|
460
482
|
if (typeof version === "string") {
|
|
461
483
|
if (this.shouldExclude(name)) {
|
|
@@ -480,6 +502,44 @@ var MonitorClient = class {
|
|
|
480
502
|
}
|
|
481
503
|
return false;
|
|
482
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* Fetch the latest version of a package from npm registry.
|
|
507
|
+
* Returns null if the package cannot be found or the request fails.
|
|
508
|
+
*/
|
|
509
|
+
async fetchLatestVersion(packageName) {
|
|
510
|
+
try {
|
|
511
|
+
const encodedName = encodeURIComponent(packageName).replace("%40", "@");
|
|
512
|
+
const response = await fetch(`https://registry.npmjs.org/${encodedName}`, {
|
|
513
|
+
headers: { "Accept": "application/json" },
|
|
514
|
+
signal: AbortSignal.timeout(5e3)
|
|
515
|
+
});
|
|
516
|
+
if (!response.ok) return null;
|
|
517
|
+
const data = await response.json();
|
|
518
|
+
return data["dist-tags"]?.latest || null;
|
|
519
|
+
} catch {
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Fetch latest versions for multiple packages in parallel with concurrency limit.
|
|
525
|
+
*/
|
|
526
|
+
async fetchLatestVersions(packageNames) {
|
|
527
|
+
const results = /* @__PURE__ */ new Map();
|
|
528
|
+
const concurrencyLimit = 5;
|
|
529
|
+
for (let i = 0; i < packageNames.length; i += concurrencyLimit) {
|
|
530
|
+
const batch = packageNames.slice(i, i + concurrencyLimit);
|
|
531
|
+
const batchResults = await Promise.all(
|
|
532
|
+
batch.map(async (name) => ({
|
|
533
|
+
name,
|
|
534
|
+
version: await this.fetchLatestVersion(name)
|
|
535
|
+
}))
|
|
536
|
+
);
|
|
537
|
+
for (const { name, version } of batchResults) {
|
|
538
|
+
results.set(name, version);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return results;
|
|
542
|
+
}
|
|
483
543
|
async sendTechnologies(technologies) {
|
|
484
544
|
await this.sendTechnologiesWithEnvironment(technologies, this.environment);
|
|
485
545
|
}
|
package/package.json
CHANGED