@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/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> &middot;
17
+ <a href="#cli">CLI</a> &middot;
18
+ <a href="#file-di-configurazione">Configurazione</a> &middot;
19
+ <a href="#api-programmatica">API</a> &middot;
20
+ <a href="#server-rest-api">Server REST</a> &middot;
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> &middot;
17
+ <a href="#cli">CLI</a> &middot;
18
+ <a href="#設定ファイル">設定</a> &middot;
19
+ <a href="#プログラマティックapi">API</a> &middot;
20
+ <a href="#rest-apiサーバー">RESTサーバー</a> &middot;
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> &middot;
17
+ <a href="#cli">CLI</a> &middot;
18
+ <a href="#config-file">Config</a> &middot;
19
+ <a href="#programmatic-api">API</a> &middot;
20
+ <a href="#rest-api-server">REST Server</a> &middot;
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