@mcptoolshop/registry-stats 1.2.1 → 1.2.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/dist/cli.js CHANGED
@@ -25,8 +25,10 @@ var REGISTRY_DELAYS = {
25
25
  // ~2.5 req/s — safe for 54+ scoped packages
26
26
  pypi: 2200,
27
27
  // 30 req/60s = 1 per 2s, with headroom
28
- docker: 4e3
28
+ docker: 4e3,
29
29
  // 10 req/3600s — very tight
30
+ ghcr: 200
31
+ // 5000 req/hr authenticated, 60/hr unauth
30
32
  };
31
33
  var DEFAULT_DELAY = 100;
32
34
  function acquireSlot(registry) {
@@ -301,6 +303,71 @@ var docker = {
301
303
  }
302
304
  };
303
305
 
306
+ // src/providers/ghcr.ts
307
+ var API5 = "https://api.github.com";
308
+ var ghcr = {
309
+ name: "ghcr",
310
+ rateLimit: { maxRequests: 50, windowSeconds: 3600, authRaisesLimit: true },
311
+ async getStats(pkg, options) {
312
+ const slash = pkg.indexOf("/");
313
+ if (slash === -1) return null;
314
+ const owner = pkg.slice(0, slash);
315
+ const name = pkg.slice(slash + 1);
316
+ const headers = {
317
+ "Accept": "application/vnd.github+json",
318
+ "X-GitHub-Api-Version": "2022-11-28"
319
+ };
320
+ if (options?.ghcrToken) {
321
+ headers["Authorization"] = `Bearer ${options.ghcrToken}`;
322
+ }
323
+ const versions = await fetchWithRetry(
324
+ `${API5}/orgs/${owner}/packages/container/${encodeURIComponent(name)}/versions?per_page=100`,
325
+ "ghcr",
326
+ { headers }
327
+ );
328
+ if (!versions || !Array.isArray(versions)) return null;
329
+ const now = Date.now();
330
+ const DAY = 864e5;
331
+ let activity7d = 0;
332
+ let activity30d = 0;
333
+ let lastPublished = null;
334
+ for (const v of versions) {
335
+ const created = v.created_at ? new Date(v.created_at).getTime() : 0;
336
+ if (created > 0) {
337
+ const age = now - created;
338
+ if (age <= 7 * DAY) activity7d++;
339
+ if (age <= 30 * DAY) activity30d++;
340
+ if (!lastPublished || v.created_at > lastPublished) {
341
+ lastPublished = v.created_at;
342
+ }
343
+ }
344
+ }
345
+ const tags = versions.flatMap((v) => v.metadata?.container?.tags ?? []);
346
+ return {
347
+ registry: "ghcr",
348
+ package: pkg,
349
+ downloads: {
350
+ total: versions.length,
351
+ // versionCount as primary number
352
+ lastWeek: activity7d,
353
+ // new versions in 7d
354
+ lastMonth: activity30d
355
+ // new versions in 30d
356
+ },
357
+ extra: {
358
+ metricType: "versions",
359
+ // signals this is version activity, not downloads
360
+ tags: tags.slice(0, 10),
361
+ versionCount: versions.length,
362
+ activity7d,
363
+ activity30d,
364
+ lastPublished
365
+ },
366
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
367
+ };
368
+ }
369
+ };
370
+
304
371
  // src/calc.ts
305
372
  var calc = {
306
373
  total(records) {
@@ -604,7 +671,8 @@ var providers = {
604
671
  pypi,
605
672
  nuget,
606
673
  vscode,
607
- docker
674
+ docker,
675
+ ghcr
608
676
  };
609
677
  var DEFAULT_TTL = 3e5;
610
678
  async function stats(registry, pkg, options) {
package/dist/index.cjs CHANGED
@@ -54,8 +54,10 @@ var REGISTRY_DELAYS = {
54
54
  // ~2.5 req/s — safe for 54+ scoped packages
55
55
  pypi: 2200,
56
56
  // 30 req/60s = 1 per 2s, with headroom
57
- docker: 4e3
57
+ docker: 4e3,
58
58
  // 10 req/3600s — very tight
59
+ ghcr: 200
60
+ // 5000 req/hr authenticated, 60/hr unauth
59
61
  };
60
62
  var DEFAULT_DELAY = 100;
61
63
  function acquireSlot(registry) {
@@ -330,6 +332,71 @@ var docker = {
330
332
  }
331
333
  };
332
334
 
335
+ // src/providers/ghcr.ts
336
+ var API5 = "https://api.github.com";
337
+ var ghcr = {
338
+ name: "ghcr",
339
+ rateLimit: { maxRequests: 50, windowSeconds: 3600, authRaisesLimit: true },
340
+ async getStats(pkg, options) {
341
+ const slash = pkg.indexOf("/");
342
+ if (slash === -1) return null;
343
+ const owner = pkg.slice(0, slash);
344
+ const name = pkg.slice(slash + 1);
345
+ const headers = {
346
+ "Accept": "application/vnd.github+json",
347
+ "X-GitHub-Api-Version": "2022-11-28"
348
+ };
349
+ if (options?.ghcrToken) {
350
+ headers["Authorization"] = `Bearer ${options.ghcrToken}`;
351
+ }
352
+ const versions = await fetchWithRetry(
353
+ `${API5}/orgs/${owner}/packages/container/${encodeURIComponent(name)}/versions?per_page=100`,
354
+ "ghcr",
355
+ { headers }
356
+ );
357
+ if (!versions || !Array.isArray(versions)) return null;
358
+ const now = Date.now();
359
+ const DAY = 864e5;
360
+ let activity7d = 0;
361
+ let activity30d = 0;
362
+ let lastPublished = null;
363
+ for (const v of versions) {
364
+ const created = v.created_at ? new Date(v.created_at).getTime() : 0;
365
+ if (created > 0) {
366
+ const age = now - created;
367
+ if (age <= 7 * DAY) activity7d++;
368
+ if (age <= 30 * DAY) activity30d++;
369
+ if (!lastPublished || v.created_at > lastPublished) {
370
+ lastPublished = v.created_at;
371
+ }
372
+ }
373
+ }
374
+ const tags = versions.flatMap((v) => v.metadata?.container?.tags ?? []);
375
+ return {
376
+ registry: "ghcr",
377
+ package: pkg,
378
+ downloads: {
379
+ total: versions.length,
380
+ // versionCount as primary number
381
+ lastWeek: activity7d,
382
+ // new versions in 7d
383
+ lastMonth: activity30d
384
+ // new versions in 30d
385
+ },
386
+ extra: {
387
+ metricType: "versions",
388
+ // signals this is version activity, not downloads
389
+ tags: tags.slice(0, 10),
390
+ versionCount: versions.length,
391
+ activity7d,
392
+ activity30d,
393
+ lastPublished
394
+ },
395
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
396
+ };
397
+ }
398
+ };
399
+
333
400
  // src/calc.ts
334
401
  var calc = {
335
402
  total(records) {
@@ -642,7 +709,8 @@ var providers = {
642
709
  pypi,
643
710
  nuget,
644
711
  vscode,
645
- docker
712
+ docker,
713
+ ghcr
646
714
  };
647
715
  function registerProvider(provider) {
648
716
  providers[provider.name] = provider;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as node_http from 'node:http';
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
3
 
4
- type RegistryName = 'npm' | 'pypi' | 'nuget' | 'vscode' | 'docker';
4
+ type RegistryName = 'npm' | 'pypi' | 'nuget' | 'vscode' | 'docker' | 'ghcr';
5
5
  interface PackageStats {
6
6
  registry: RegistryName;
7
7
  package: string;
@@ -38,6 +38,8 @@ interface StatsCache {
38
38
  }
39
39
  interface StatsOptions {
40
40
  dockerToken?: string;
41
+ /** GitHub token for GHCR (GitHub Container Registry) — read:packages scope */
42
+ ghcrToken?: string;
41
43
  /** Max concurrent requests for bulk operations (default: 5) */
42
44
  concurrency?: number;
43
45
  /** Cache instance — use createCache() for built-in TTL cache */
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as node_http from 'node:http';
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
3
 
4
- type RegistryName = 'npm' | 'pypi' | 'nuget' | 'vscode' | 'docker';
4
+ type RegistryName = 'npm' | 'pypi' | 'nuget' | 'vscode' | 'docker' | 'ghcr';
5
5
  interface PackageStats {
6
6
  registry: RegistryName;
7
7
  package: string;
@@ -38,6 +38,8 @@ interface StatsCache {
38
38
  }
39
39
  interface StatsOptions {
40
40
  dockerToken?: string;
41
+ /** GitHub token for GHCR (GitHub Container Registry) — read:packages scope */
42
+ ghcrToken?: string;
41
43
  /** Max concurrent requests for bulk operations (default: 5) */
42
44
  concurrency?: number;
43
45
  /** Cache instance — use createCache() for built-in TTL cache */
package/dist/index.js CHANGED
@@ -19,8 +19,10 @@ var REGISTRY_DELAYS = {
19
19
  // ~2.5 req/s — safe for 54+ scoped packages
20
20
  pypi: 2200,
21
21
  // 30 req/60s = 1 per 2s, with headroom
22
- docker: 4e3
22
+ docker: 4e3,
23
23
  // 10 req/3600s — very tight
24
+ ghcr: 200
25
+ // 5000 req/hr authenticated, 60/hr unauth
24
26
  };
25
27
  var DEFAULT_DELAY = 100;
26
28
  function acquireSlot(registry) {
@@ -295,6 +297,71 @@ var docker = {
295
297
  }
296
298
  };
297
299
 
300
+ // src/providers/ghcr.ts
301
+ var API5 = "https://api.github.com";
302
+ var ghcr = {
303
+ name: "ghcr",
304
+ rateLimit: { maxRequests: 50, windowSeconds: 3600, authRaisesLimit: true },
305
+ async getStats(pkg, options) {
306
+ const slash = pkg.indexOf("/");
307
+ if (slash === -1) return null;
308
+ const owner = pkg.slice(0, slash);
309
+ const name = pkg.slice(slash + 1);
310
+ const headers = {
311
+ "Accept": "application/vnd.github+json",
312
+ "X-GitHub-Api-Version": "2022-11-28"
313
+ };
314
+ if (options?.ghcrToken) {
315
+ headers["Authorization"] = `Bearer ${options.ghcrToken}`;
316
+ }
317
+ const versions = await fetchWithRetry(
318
+ `${API5}/orgs/${owner}/packages/container/${encodeURIComponent(name)}/versions?per_page=100`,
319
+ "ghcr",
320
+ { headers }
321
+ );
322
+ if (!versions || !Array.isArray(versions)) return null;
323
+ const now = Date.now();
324
+ const DAY = 864e5;
325
+ let activity7d = 0;
326
+ let activity30d = 0;
327
+ let lastPublished = null;
328
+ for (const v of versions) {
329
+ const created = v.created_at ? new Date(v.created_at).getTime() : 0;
330
+ if (created > 0) {
331
+ const age = now - created;
332
+ if (age <= 7 * DAY) activity7d++;
333
+ if (age <= 30 * DAY) activity30d++;
334
+ if (!lastPublished || v.created_at > lastPublished) {
335
+ lastPublished = v.created_at;
336
+ }
337
+ }
338
+ }
339
+ const tags = versions.flatMap((v) => v.metadata?.container?.tags ?? []);
340
+ return {
341
+ registry: "ghcr",
342
+ package: pkg,
343
+ downloads: {
344
+ total: versions.length,
345
+ // versionCount as primary number
346
+ lastWeek: activity7d,
347
+ // new versions in 7d
348
+ lastMonth: activity30d
349
+ // new versions in 30d
350
+ },
351
+ extra: {
352
+ metricType: "versions",
353
+ // signals this is version activity, not downloads
354
+ tags: tags.slice(0, 10),
355
+ versionCount: versions.length,
356
+ activity7d,
357
+ activity30d,
358
+ lastPublished
359
+ },
360
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
361
+ };
362
+ }
363
+ };
364
+
298
365
  // src/calc.ts
299
366
  var calc = {
300
367
  total(records) {
@@ -607,7 +674,8 @@ var providers = {
607
674
  pypi,
608
675
  nuget,
609
676
  vscode,
610
- docker
677
+ docker,
678
+ ghcr
611
679
  };
612
680
  function registerProvider(provider) {
613
681
  providers[provider.name] = provider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcptoolshop/registry-stats",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Multi-registry download stats for npm, PyPI, NuGet, VS Code Marketplace, and Docker Hub",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",