@mcptoolshop/registry-stats 0.3.0
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/LICENSE +21 -0
- package/README.es.md +206 -0
- package/README.fr.md +206 -0
- package/README.hi.md +206 -0
- package/README.it.md +206 -0
- package/README.ja.md +206 -0
- package/README.md +239 -0
- package/README.pt-BR.md +206 -0
- package/README.zh.md +206 -0
- package/assets/logo.png +0 -0
- package/dist/cli.js +880 -0
- package/dist/index.cjs +669 -0
- package/dist/index.d.cts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +633 -0
- package/package.json +62 -0
package/README.it.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="README.md">English</a> | <a href="README.ja.md">日本語</a> | <a href="README.zh.md">中文</a> | <a href="README.es.md">Español</a> | <a href="README.fr.md">Français</a> | <a href="README.hi.md">हिन्दी</a> | <strong>Italiano</strong> | <a href="README.pt-BR.md">Português</a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="assets/logo.png" alt="logo registry-stats" width="280" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<h1 align="center">@mcptoolshop/registry-stats</h1>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
Un comando. Cinque registri. Tutte le tue statistiche di download.
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="#installazione">Installazione</a> ·
|
|
17
|
+
<a href="#cli">CLI</a> ·
|
|
18
|
+
<a href="#file-di-configurazione">Configurazione</a> ·
|
|
19
|
+
<a href="#api-programmatica">API</a> ·
|
|
20
|
+
<a href="#server-rest-api">Server REST</a> ·
|
|
21
|
+
<a href="#licenza">Licenza</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
Se pubblichi su npm, PyPI, NuGet, VS Code Marketplace o Docker Hub, attualmente hai bisogno di cinque API diverse per rispondere a "quanti download ho avuto questo mese?" Questa libreria offre un'interfaccia unificata — come CLI o API programmatica.
|
|
27
|
+
|
|
28
|
+
Zero dipendenze. Usa `fetch()` nativo. Node 18+.
|
|
29
|
+
|
|
30
|
+
## Installazione
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @mcptoolshop/registry-stats
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## CLI
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Interrogare un singolo registro
|
|
40
|
+
registry-stats express -r npm
|
|
41
|
+
|
|
42
|
+
# Interrogare tutti i registri
|
|
43
|
+
registry-stats express
|
|
44
|
+
|
|
45
|
+
# Serie temporale con ripartizione mensile + tendenza
|
|
46
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30
|
|
47
|
+
|
|
48
|
+
# Output JSON
|
|
49
|
+
registry-stats express -r npm --json
|
|
50
|
+
|
|
51
|
+
# Altri registri
|
|
52
|
+
registry-stats requests -r pypi
|
|
53
|
+
registry-stats Newtonsoft.Json -r nuget
|
|
54
|
+
registry-stats esbenp.prettier-vscode -r vscode
|
|
55
|
+
registry-stats library/node -r docker
|
|
56
|
+
|
|
57
|
+
# Creare file di configurazione
|
|
58
|
+
registry-stats --init
|
|
59
|
+
|
|
60
|
+
# Eseguire con configurazione — recupera tutti i pacchetti tracciati
|
|
61
|
+
registry-stats
|
|
62
|
+
|
|
63
|
+
# Confrontare tra registri
|
|
64
|
+
registry-stats express --compare
|
|
65
|
+
|
|
66
|
+
# Esportare come CSV o JSON per grafici
|
|
67
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30 --format csv
|
|
68
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30 --format chart
|
|
69
|
+
|
|
70
|
+
# Avviare server REST API
|
|
71
|
+
registry-stats serve --port 3000
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## File di Configurazione
|
|
75
|
+
|
|
76
|
+
Crea un `registry-stats.config.json` nella root del progetto (o esegui `registry-stats --init`):
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"registries": ["npm", "pypi", "nuget", "vscode", "docker"],
|
|
81
|
+
"packages": {
|
|
82
|
+
"mcpt": {
|
|
83
|
+
"npm": "mcpt",
|
|
84
|
+
"pypi": "mcpt"
|
|
85
|
+
},
|
|
86
|
+
"tool-compass": {
|
|
87
|
+
"npm": "@mcptoolshop/tool-compass",
|
|
88
|
+
"vscode": "mcp-tool-shop.tool-compass"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"cache": true,
|
|
92
|
+
"cacheTtlMs": 300000,
|
|
93
|
+
"concurrency": 5
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Esegui `registry-stats` senza argomenti per ottenere le statistiche di tutti i pacchetti configurati. Il CLI cerca il file di configurazione risalendo dalla directory corrente.
|
|
98
|
+
|
|
99
|
+
## API Programmatica
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { stats, calc, createCache } from '@mcptoolshop/registry-stats';
|
|
103
|
+
|
|
104
|
+
// Registro singolo
|
|
105
|
+
const npm = await stats('npm', 'express');
|
|
106
|
+
const pypi = await stats('pypi', 'requests');
|
|
107
|
+
|
|
108
|
+
// Tutti i registri (usa Promise.allSettled — non lancia mai errori)
|
|
109
|
+
const all = await stats.all('express');
|
|
110
|
+
|
|
111
|
+
// In massa — più pacchetti, concorrenza limitata (predefinito: 5)
|
|
112
|
+
const bulk = await stats.bulk('npm', ['express', 'koa', 'fastify']);
|
|
113
|
+
|
|
114
|
+
// Serie temporale (solo npm + pypi)
|
|
115
|
+
const daily = await stats.range('npm', 'express', '2025-01-01', '2025-06-30');
|
|
116
|
+
|
|
117
|
+
// Calcoli
|
|
118
|
+
calc.total(daily); // totale download
|
|
119
|
+
calc.avg(daily); // media giornaliera
|
|
120
|
+
calc.trend(daily); // { direction: 'up', changePercent: 8.3 }
|
|
121
|
+
calc.movingAvg(daily, 7); // media mobile 7 giorni
|
|
122
|
+
calc.popularity(daily); // punteggio 0-100 scala logaritmica
|
|
123
|
+
|
|
124
|
+
// Formati di esportazione
|
|
125
|
+
calc.toCSV(daily); // stringa CSV
|
|
126
|
+
calc.toChartData(daily, 'express'); // { labels: [...], datasets: [...] }
|
|
127
|
+
|
|
128
|
+
// Confronto — stesso pacchetto tra registri
|
|
129
|
+
const comparison = await stats.compare('express');
|
|
130
|
+
await stats.compare('express', ['npm', 'pypi']);
|
|
131
|
+
|
|
132
|
+
// Cache (TTL 5 min, in memoria)
|
|
133
|
+
const cache = createCache();
|
|
134
|
+
await stats('npm', 'express', { cache });
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Supporto Registri
|
|
138
|
+
|
|
139
|
+
| Registro | Formato pacchetto | Serie temporale | Dati disponibili |
|
|
140
|
+
|----------|------------------|-----------------|-----------------|
|
|
141
|
+
| `npm` | `express`, `@scope/pkg` | Sì (549 giorni) | lastDay, lastWeek, lastMonth |
|
|
142
|
+
| `pypi` | `requests` | Sì (180 giorni) | lastDay, lastWeek, lastMonth, total |
|
|
143
|
+
| `nuget` | `Newtonsoft.Json` | No | total |
|
|
144
|
+
| `vscode` | `publisher.extension` | No | total (installazioni), rating, trends |
|
|
145
|
+
| `docker` | `namespace/repo` | No | total (pull), stars |
|
|
146
|
+
|
|
147
|
+
## Affidabilità Integrata
|
|
148
|
+
|
|
149
|
+
- Retry automatico con backoff esponenziale su errori 429/5xx
|
|
150
|
+
- Rispetta le intestazioni `Retry-After`
|
|
151
|
+
- Limitazione della concorrenza per richieste in massa
|
|
152
|
+
- Cache TTL opzionale (estensibile — usa Redis/file tramite interfaccia `StatsCache`)
|
|
153
|
+
|
|
154
|
+
## Server REST API
|
|
155
|
+
|
|
156
|
+
Esegui come microservizio o integra nel tuo server:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
registry-stats serve --port 3000
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
GET /stats/:package # tutti i registri
|
|
164
|
+
GET /stats/:registry/:package # registro singolo
|
|
165
|
+
GET /compare/:package?registries=npm,pypi
|
|
166
|
+
GET /range/:registry/:package?start=YYYY-MM-DD&end=YYYY-MM-DD&format=json|csv|chart
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { createHandler, serve } from '@mcptoolshop/registry-stats';
|
|
171
|
+
|
|
172
|
+
// Avvio rapido
|
|
173
|
+
serve({ port: 3000 });
|
|
174
|
+
|
|
175
|
+
// Server personalizzato
|
|
176
|
+
import { createServer } from 'node:http';
|
|
177
|
+
const handler = createHandler();
|
|
178
|
+
createServer(handler).listen(3000);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Registri Personalizzati
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { registerProvider, type RegistryProvider } from '@mcptoolshop/registry-stats';
|
|
185
|
+
|
|
186
|
+
const cargo: RegistryProvider = {
|
|
187
|
+
name: 'cargo',
|
|
188
|
+
async getStats(pkg) {
|
|
189
|
+
const res = await fetch(`https://crates.io/api/v1/crates/${pkg}`);
|
|
190
|
+
const json = await res.json();
|
|
191
|
+
return {
|
|
192
|
+
registry: 'cargo' as any,
|
|
193
|
+
package: pkg,
|
|
194
|
+
downloads: { total: json.crate.downloads },
|
|
195
|
+
fetchedAt: new Date().toISOString(),
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
registerProvider(cargo);
|
|
201
|
+
await stats('cargo', 'serde');
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Licenza
|
|
205
|
+
|
|
206
|
+
MIT
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="README.md">English</a> | <strong>日本語</strong> | <a href="README.zh.md">中文</a> | <a href="README.es.md">Español</a> | <a href="README.fr.md">Français</a> | <a href="README.hi.md">हिन्दी</a> | <a href="README.it.md">Italiano</a> | <a href="README.pt-BR.md">Português</a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="assets/logo.png" alt="registry-stats ロゴ" width="280" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<h1 align="center">@mcptoolshop/registry-stats</h1>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
1つのコマンド。5つのレジストリ。すべてのダウンロード統計。
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="#インストール">インストール</a> ·
|
|
17
|
+
<a href="#cli">CLI</a> ·
|
|
18
|
+
<a href="#設定ファイル">設定</a> ·
|
|
19
|
+
<a href="#プログラマティックapi">API</a> ·
|
|
20
|
+
<a href="#rest-apiサーバー">RESTサーバー</a> ·
|
|
21
|
+
<a href="#ライセンス">ライセンス</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
npm、PyPI、NuGet、VS Code Marketplace、Docker Hubにパッケージを公開している場合、「今月のダウンロード数は?」という質問に答えるために5つの異なるAPIが必要です。このライブラリは、CLIまたはプログラマティックAPIとして、すべてを1つのインターフェースで提供します。
|
|
27
|
+
|
|
28
|
+
依存関係ゼロ。ネイティブ`fetch()`を使用。Node 18+。
|
|
29
|
+
|
|
30
|
+
## インストール
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @mcptoolshop/registry-stats
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## CLI
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 単一レジストリのクエリ
|
|
40
|
+
registry-stats express -r npm
|
|
41
|
+
|
|
42
|
+
# すべてのレジストリを一度にクエリ
|
|
43
|
+
registry-stats express
|
|
44
|
+
|
|
45
|
+
# 月次内訳 + トレンド付き時系列
|
|
46
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30
|
|
47
|
+
|
|
48
|
+
# JSON出力
|
|
49
|
+
registry-stats express -r npm --json
|
|
50
|
+
|
|
51
|
+
# その他のレジストリ
|
|
52
|
+
registry-stats requests -r pypi
|
|
53
|
+
registry-stats Newtonsoft.Json -r nuget
|
|
54
|
+
registry-stats esbenp.prettier-vscode -r vscode
|
|
55
|
+
registry-stats library/node -r docker
|
|
56
|
+
|
|
57
|
+
# 設定ファイルを作成
|
|
58
|
+
registry-stats --init
|
|
59
|
+
|
|
60
|
+
# 設定から実行 — 追跡パッケージをすべて取得
|
|
61
|
+
registry-stats
|
|
62
|
+
|
|
63
|
+
# レジストリ間の比較
|
|
64
|
+
registry-stats express --compare
|
|
65
|
+
|
|
66
|
+
# CSVまたはチャート用JSONでエクスポート
|
|
67
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30 --format csv
|
|
68
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30 --format chart
|
|
69
|
+
|
|
70
|
+
# REST APIサーバーを起動
|
|
71
|
+
registry-stats serve --port 3000
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 設定ファイル
|
|
75
|
+
|
|
76
|
+
プロジェクトルートに`registry-stats.config.json`を作成します(または`registry-stats --init`を実行):
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"registries": ["npm", "pypi", "nuget", "vscode", "docker"],
|
|
81
|
+
"packages": {
|
|
82
|
+
"mcpt": {
|
|
83
|
+
"npm": "mcpt",
|
|
84
|
+
"pypi": "mcpt"
|
|
85
|
+
},
|
|
86
|
+
"tool-compass": {
|
|
87
|
+
"npm": "@mcptoolshop/tool-compass",
|
|
88
|
+
"vscode": "mcp-tool-shop.tool-compass"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"cache": true,
|
|
92
|
+
"cacheTtlMs": 300000,
|
|
93
|
+
"concurrency": 5
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
引数なしで`registry-stats`を実行すると、設定されたすべてのパッケージの統計を取得します。CLIはcwdから上方に設定ファイルを検索します。
|
|
98
|
+
|
|
99
|
+
## プログラマティックAPI
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { stats, calc, createCache } from '@mcptoolshop/registry-stats';
|
|
103
|
+
|
|
104
|
+
// 単一レジストリ
|
|
105
|
+
const npm = await stats('npm', 'express');
|
|
106
|
+
const pypi = await stats('pypi', 'requests');
|
|
107
|
+
|
|
108
|
+
// すべてのレジストリを一度に(Promise.allSettledを使用 — スローしない)
|
|
109
|
+
const all = await stats.all('express');
|
|
110
|
+
|
|
111
|
+
// 一括 — 複数パッケージ、並行数制限(デフォルト: 5)
|
|
112
|
+
const bulk = await stats.bulk('npm', ['express', 'koa', 'fastify']);
|
|
113
|
+
|
|
114
|
+
// 時系列(npm + pypiのみ)
|
|
115
|
+
const daily = await stats.range('npm', 'express', '2025-01-01', '2025-06-30');
|
|
116
|
+
|
|
117
|
+
// 計算
|
|
118
|
+
calc.total(daily); // ダウンロード合計
|
|
119
|
+
calc.avg(daily); // 日次平均
|
|
120
|
+
calc.trend(daily); // { direction: 'up', changePercent: 8.3 }
|
|
121
|
+
calc.movingAvg(daily, 7); // 7日間移動平均
|
|
122
|
+
calc.popularity(daily); // 0-100 対数スケールスコア
|
|
123
|
+
|
|
124
|
+
// エクスポート形式
|
|
125
|
+
calc.toCSV(daily); // CSV文字列
|
|
126
|
+
calc.toChartData(daily, 'express'); // { labels: [...], datasets: [...] }
|
|
127
|
+
|
|
128
|
+
// 比較 — レジストリ間で同じパッケージ
|
|
129
|
+
const comparison = await stats.compare('express');
|
|
130
|
+
await stats.compare('express', ['npm', 'pypi']);
|
|
131
|
+
|
|
132
|
+
// キャッシュ(5分TTL、インメモリ)
|
|
133
|
+
const cache = createCache();
|
|
134
|
+
await stats('npm', 'express', { cache });
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## レジストリサポート
|
|
138
|
+
|
|
139
|
+
| レジストリ | パッケージ形式 | 時系列 | 利用可能データ |
|
|
140
|
+
|----------|---------------|--------|----------------|
|
|
141
|
+
| `npm` | `express`, `@scope/pkg` | あり(549日) | lastDay, lastWeek, lastMonth |
|
|
142
|
+
| `pypi` | `requests` | あり(180日) | lastDay, lastWeek, lastMonth, total |
|
|
143
|
+
| `nuget` | `Newtonsoft.Json` | なし | total |
|
|
144
|
+
| `vscode` | `publisher.extension` | なし | total(インストール数)、rating、trends |
|
|
145
|
+
| `docker` | `namespace/repo` | なし | total(プル数)、stars |
|
|
146
|
+
|
|
147
|
+
## 組み込みの信頼性
|
|
148
|
+
|
|
149
|
+
- 429/5xxエラーに対する指数バックオフ付き自動リトライ
|
|
150
|
+
- `Retry-After`ヘッダーの尊重
|
|
151
|
+
- 一括リクエストの並行数制限
|
|
152
|
+
- オプションのTTLキャッシュ(プラグイン可能 — `StatsCache`インターフェースでRedis/ファイルバックエンドを利用可能)
|
|
153
|
+
|
|
154
|
+
## REST APIサーバー
|
|
155
|
+
|
|
156
|
+
マイクロサービスとして実行、または独自のサーバーに組み込み:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
registry-stats serve --port 3000
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
GET /stats/:package # すべてのレジストリ
|
|
164
|
+
GET /stats/:registry/:package # 単一レジストリ
|
|
165
|
+
GET /compare/:package?registries=npm,pypi
|
|
166
|
+
GET /range/:registry/:package?start=YYYY-MM-DD&end=YYYY-MM-DD&format=json|csv|chart
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { createHandler, serve } from '@mcptoolshop/registry-stats';
|
|
171
|
+
|
|
172
|
+
// クイックスタート
|
|
173
|
+
serve({ port: 3000 });
|
|
174
|
+
|
|
175
|
+
// カスタムサーバー
|
|
176
|
+
import { createServer } from 'node:http';
|
|
177
|
+
const handler = createHandler();
|
|
178
|
+
createServer(handler).listen(3000);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## カスタムレジストリ
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { registerProvider, type RegistryProvider } from '@mcptoolshop/registry-stats';
|
|
185
|
+
|
|
186
|
+
const cargo: RegistryProvider = {
|
|
187
|
+
name: 'cargo',
|
|
188
|
+
async getStats(pkg) {
|
|
189
|
+
const res = await fetch(`https://crates.io/api/v1/crates/${pkg}`);
|
|
190
|
+
const json = await res.json();
|
|
191
|
+
return {
|
|
192
|
+
registry: 'cargo' as any,
|
|
193
|
+
package: pkg,
|
|
194
|
+
downloads: { total: json.crate.downloads },
|
|
195
|
+
fetchedAt: new Date().toISOString(),
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
registerProvider(cargo);
|
|
201
|
+
await stats('cargo', 'serde');
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## ライセンス
|
|
205
|
+
|
|
206
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<strong>English</strong> | <a href="README.ja.md">日本語</a> | <a href="README.zh.md">中文</a> | <a href="README.es.md">Español</a> | <a href="README.fr.md">Français</a> | <a href="README.hi.md">हिन्दी</a> | <a href="README.it.md">Italiano</a> | <a href="README.pt-BR.md">Português</a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="assets/logo.png" alt="registry-stats logo" width="280" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<h1 align="center">@mcptoolshop/registry-stats</h1>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
One command. Five registries. All your download stats.
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="#install">Install</a> ·
|
|
17
|
+
<a href="#cli">CLI</a> ·
|
|
18
|
+
<a href="#config-file">Config</a> ·
|
|
19
|
+
<a href="#programmatic-api">API</a> ·
|
|
20
|
+
<a href="#rest-api-server">REST Server</a> ·
|
|
21
|
+
<a href="#license">License</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
If you publish to npm, PyPI, NuGet, VS Code Marketplace, or Docker Hub, you currently need five different APIs to answer "how many downloads did I get this month?" This library gives you one interface for all of them — as a CLI or programmatic API.
|
|
27
|
+
|
|
28
|
+
Zero dependencies. Uses native `fetch()`. Node 18+.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @mcptoolshop/registry-stats
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## CLI
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Query a single registry
|
|
40
|
+
registry-stats express -r npm
|
|
41
|
+
# npm | express
|
|
42
|
+
# month: 283,472,710 week: 67,367,773 day: 11,566,113
|
|
43
|
+
|
|
44
|
+
# Query all registries at once
|
|
45
|
+
registry-stats express
|
|
46
|
+
|
|
47
|
+
# Time series with monthly breakdown + trend
|
|
48
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30
|
|
49
|
+
# 2025-01 142,359,021
|
|
50
|
+
# 2025-02 147,522,528
|
|
51
|
+
# ...
|
|
52
|
+
# Total: 448,383,383 Avg/day: 4,982,038 Trend: flat (-0.46%)
|
|
53
|
+
|
|
54
|
+
# Raw JSON output
|
|
55
|
+
registry-stats express -r npm --json
|
|
56
|
+
|
|
57
|
+
# Other registries
|
|
58
|
+
registry-stats requests -r pypi
|
|
59
|
+
registry-stats Newtonsoft.Json -r nuget
|
|
60
|
+
registry-stats esbenp.prettier-vscode -r vscode
|
|
61
|
+
registry-stats library/node -r docker
|
|
62
|
+
|
|
63
|
+
# Create a config file
|
|
64
|
+
registry-stats --init
|
|
65
|
+
|
|
66
|
+
# Run with config — fetches all tracked packages
|
|
67
|
+
registry-stats
|
|
68
|
+
|
|
69
|
+
# Compare across registries
|
|
70
|
+
registry-stats express --compare
|
|
71
|
+
# express — comparison
|
|
72
|
+
#
|
|
73
|
+
# Metric npm pypi
|
|
74
|
+
# ─────────────────────────────────
|
|
75
|
+
# Total - -
|
|
76
|
+
# Month 283,472 47,201
|
|
77
|
+
# Week 67,367 11,800
|
|
78
|
+
# Day 11,566 1,686
|
|
79
|
+
|
|
80
|
+
# Export as CSV or chart-friendly JSON
|
|
81
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30 --format csv
|
|
82
|
+
registry-stats express -r npm --range 2025-01-01:2025-06-30 --format chart
|
|
83
|
+
|
|
84
|
+
# Start a REST API server
|
|
85
|
+
registry-stats serve --port 3000
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Config File
|
|
89
|
+
|
|
90
|
+
Create a `registry-stats.config.json` in your project root (or run `registry-stats --init`):
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"registries": ["npm", "pypi", "nuget", "vscode", "docker"],
|
|
95
|
+
"packages": {
|
|
96
|
+
"mcpt": {
|
|
97
|
+
"npm": "mcpt",
|
|
98
|
+
"pypi": "mcpt"
|
|
99
|
+
},
|
|
100
|
+
"tool-compass": {
|
|
101
|
+
"npm": "@mcptoolshop/tool-compass",
|
|
102
|
+
"vscode": "mcp-tool-shop.tool-compass"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"cache": true,
|
|
106
|
+
"cacheTtlMs": 300000,
|
|
107
|
+
"concurrency": 5
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Run `registry-stats` with no arguments to fetch stats for all configured packages. The CLI walks up from cwd to find the nearest config file.
|
|
112
|
+
|
|
113
|
+
The config is also available programmatically:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { loadConfig, defaultConfig, starterConfig } from '@mcptoolshop/registry-stats';
|
|
117
|
+
|
|
118
|
+
const config = loadConfig(); // finds nearest config file, or null
|
|
119
|
+
const defaults = defaultConfig(); // returns default Config object
|
|
120
|
+
const template = starterConfig(); // returns starter JSON string
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Programmatic API
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { stats, calc, createCache } from '@mcptoolshop/registry-stats';
|
|
127
|
+
|
|
128
|
+
// Single registry
|
|
129
|
+
const npm = await stats('npm', 'express');
|
|
130
|
+
const pypi = await stats('pypi', 'requests');
|
|
131
|
+
const nuget = await stats('nuget', 'Newtonsoft.Json');
|
|
132
|
+
const vscode = await stats('vscode', 'esbenp.prettier-vscode');
|
|
133
|
+
const docker = await stats('docker', 'library/node');
|
|
134
|
+
|
|
135
|
+
// All registries at once (uses Promise.allSettled — never throws)
|
|
136
|
+
const all = await stats.all('express');
|
|
137
|
+
|
|
138
|
+
// Bulk — multiple packages, concurrency-limited (default: 5)
|
|
139
|
+
const bulk = await stats.bulk('npm', ['express', 'koa', 'fastify']);
|
|
140
|
+
|
|
141
|
+
// Time series (npm + pypi only)
|
|
142
|
+
const daily = await stats.range('npm', 'express', '2025-01-01', '2025-06-30');
|
|
143
|
+
|
|
144
|
+
// Calculations
|
|
145
|
+
calc.total(daily); // sum of all downloads
|
|
146
|
+
calc.avg(daily); // daily average
|
|
147
|
+
calc.groupTotals(calc.monthly(daily)); // { '2025-01': 134982, ... }
|
|
148
|
+
calc.trend(daily); // { direction: 'up', changePercent: 8.3 }
|
|
149
|
+
calc.movingAvg(daily, 7); // 7-day moving average
|
|
150
|
+
calc.popularity(daily); // 0-100 log-scale score
|
|
151
|
+
|
|
152
|
+
// Export formats
|
|
153
|
+
calc.toCSV(daily); // "date,downloads\n2025-01-01,1234\n..."
|
|
154
|
+
calc.toChartData(daily, 'express'); // { labels: [...], datasets: [{ label, data }] }
|
|
155
|
+
|
|
156
|
+
// Comparison — same package across registries
|
|
157
|
+
const comparison = await stats.compare('express');
|
|
158
|
+
// → { package: 'express', registries: { npm: {...}, pypi: {...} }, fetchedAt: '...' }
|
|
159
|
+
await stats.compare('express', ['npm', 'pypi']); // specific registries only
|
|
160
|
+
|
|
161
|
+
// Caching (5 min TTL, in-memory)
|
|
162
|
+
const cache = createCache();
|
|
163
|
+
await stats('npm', 'express', { cache }); // fetches
|
|
164
|
+
await stats('npm', 'express', { cache }); // cache hit
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Registry Support
|
|
168
|
+
|
|
169
|
+
| Registry | Package format | Time series | Data available |
|
|
170
|
+
|----------|---------------|-------------|----------------|
|
|
171
|
+
| `npm` | `express`, `@scope/pkg` | Yes (549 days) | lastDay, lastWeek, lastMonth |
|
|
172
|
+
| `pypi` | `requests` | Yes (180 days) | lastDay, lastWeek, lastMonth, total |
|
|
173
|
+
| `nuget` | `Newtonsoft.Json` | No | total |
|
|
174
|
+
| `vscode` | `publisher.extension` | No | total (installs), rating, trends |
|
|
175
|
+
| `docker` | `namespace/repo` | No | total (pulls), stars |
|
|
176
|
+
|
|
177
|
+
## Built-in Reliability
|
|
178
|
+
|
|
179
|
+
- Automatic retry with exponential backoff on 429/5xx errors
|
|
180
|
+
- Respects `Retry-After` headers
|
|
181
|
+
- Concurrency limiting for bulk requests
|
|
182
|
+
- Optional TTL cache (pluggable — bring your own Redis/file backend via `StatsCache` interface)
|
|
183
|
+
|
|
184
|
+
## REST API Server
|
|
185
|
+
|
|
186
|
+
Run as a microservice or embed in your own server:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# CLI
|
|
190
|
+
registry-stats serve --port 3000
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
GET /stats/:package # all registries
|
|
195
|
+
GET /stats/:registry/:package # single registry
|
|
196
|
+
GET /compare/:package?registries=npm,pypi
|
|
197
|
+
GET /range/:registry/:package?start=YYYY-MM-DD&end=YYYY-MM-DD&format=json|csv|chart
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Programmatic usage for custom servers or serverless:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { createHandler, serve } from '@mcptoolshop/registry-stats';
|
|
204
|
+
|
|
205
|
+
// Option 1: Quick start
|
|
206
|
+
serve({ port: 3000 });
|
|
207
|
+
|
|
208
|
+
// Option 2: Bring your own server
|
|
209
|
+
import { createServer } from 'node:http';
|
|
210
|
+
const handler = createHandler();
|
|
211
|
+
createServer(handler).listen(3000);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Custom Registries
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { registerProvider, type RegistryProvider } from '@mcptoolshop/registry-stats';
|
|
218
|
+
|
|
219
|
+
const cargo: RegistryProvider = {
|
|
220
|
+
name: 'cargo',
|
|
221
|
+
async getStats(pkg) {
|
|
222
|
+
const res = await fetch(`https://crates.io/api/v1/crates/${pkg}`);
|
|
223
|
+
const json = await res.json();
|
|
224
|
+
return {
|
|
225
|
+
registry: 'cargo' as any,
|
|
226
|
+
package: pkg,
|
|
227
|
+
downloads: { total: json.crate.downloads },
|
|
228
|
+
fetchedAt: new Date().toISOString(),
|
|
229
|
+
};
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
registerProvider(cargo);
|
|
234
|
+
await stats('cargo', 'serde');
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT
|