@mcptoolshop/registry-stats 2.3.0 → 3.1.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.es.md +137 -134
- package/README.fr.md +137 -134
- package/README.hi.md +137 -134
- package/README.it.md +137 -134
- package/README.ja.md +137 -134
- package/README.md +47 -5
- package/README.pt-BR.md +137 -134
- package/README.zh.md +137 -134
- package/dist/cli.js +4 -3
- package/dist/index.cjs +311 -3
- package/dist/index.d.cts +108 -1
- package/dist/index.d.ts +108 -1
- package/dist/index.js +304 -3
- package/package.json +1 -1
package/README.zh.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<a href="README.ja.md">日本語</a> | <a href="README.md">English</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 (BR)</a>
|
|
3
|
-
</p>
|
|
4
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="README.ja.md">日本語</a> | <a href="README.md">English</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 (BR)</a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
5
|
<p align="center">
|
|
6
6
|
<img src="https://raw.githubusercontent.com/mcp-tool-shop-org/brand/main/logos/registry-stats/readme.png" alt="registry-stats logo" width="400" />
|
|
7
7
|
</p>
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
<p align="center">
|
|
10
10
|
Five registries. One engine. Dashboard included.
|
|
11
11
|
</p>
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
<p align="center">
|
|
14
14
|
<a href="https://github.com/mcp-tool-shop-org/registry-stats/actions/workflows/pages.yml"><img src="https://github.com/mcp-tool-shop-org/registry-stats/actions/workflows/pages.yml/badge.svg" alt="CI"></a>
|
|
15
15
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="MIT License"></a>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<a href="https://mcp-tool-shop-org.github.io/registry-stats/dashboard/"><img src="https://img.shields.io/badge/Dashboard-live-green" alt="Dashboard"></a>
|
|
18
18
|
<a href="https://mcp-tool-shop-org.github.io/registry-stats/"><img src="https://img.shields.io/badge/Landing_Page-live-blue" alt="Landing Page"></a>
|
|
19
19
|
</p>
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
<p align="center">
|
|
22
22
|
<a href="#dashboard">Dashboard</a> ·
|
|
23
23
|
<a href="#desktop-app">Desktop App</a> ·
|
|
@@ -28,57 +28,60 @@
|
|
|
28
28
|
<a href="#config-file">Config</a> ·
|
|
29
29
|
<a href="#license">License</a>
|
|
30
30
|
</p>
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
---
|
|
33
|
-
|
|
34
|
-
您可以在 npm、PyPI、NuGet、VS Code Marketplace 以及 Docker Hub
|
|
35
|
-
|
|
36
|
-
没有运行时依赖。使用原生的 `fetch()` 方法。Node 18
|
|
37
|
-
|
|
38
|
-
##
|
|
39
|
-
|
|
40
|
-
|
|
|
41
|
-
|-------|-------------|
|
|
42
|
-
| **Engine** | TypeScript 库 + 命令行工具 + REST
|
|
43
|
-
| **Dashboard** |
|
|
44
|
-
| **Desktop** |
|
|
45
|
-
|
|
46
|
-
## 控制面板
|
|
47
|
-
|
|
48
|
-
一个自动更新的统计信息控制面板位于 [`/dashboard/`](https://mcp-tool-shop-org.github.io/registry-stats/dashboard/)。
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
- **AI
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
- **智能增长引擎** —
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
33
|
+
|
|
34
|
+
您可以在 npm、PyPI、NuGet、VS Code Marketplace 以及 Docker Hub 上发布软件包。目前,要了解“我的软件包表现如何?”,您需要查看五个不同的网站。**registry-stats** 是一个完整的平台:一个 TypeScript 引擎(包含命令行工具、API 和 REST 服务器),一个实时 Web 控制面板,以及一个原生 Windows 桌面应用程序——所有这些都来自一个代码仓库。
|
|
35
|
+
|
|
36
|
+
没有运行时依赖。使用原生的 `fetch()` 方法。Node 18+。
|
|
37
|
+
|
|
38
|
+
## 内容
|
|
39
|
+
|
|
40
|
+
| 层 | 功能 |
|
|
41
|
+
|-------|-------------|
|
|
42
|
+
| **Engine** | TypeScript 库 + 命令行工具 + REST 服务器。使用一个界面查询五个注册中心。已发布到 npm,名为 `@mcptoolshop/registry-stats`。 |
|
|
43
|
+
| **Dashboard** | 一个由Astro驱动的Web应用程序,配备Pulse AI智能助手(支持语音流、网页搜索、全屏模式、GitHub数据连接器),包含六个交互式图表,支持实时刷新,并可导出报告(PDF/JSONL/Markdown),以及分Tab的帮助文档。该应用程序每周由CI自动构建,并可按需刷新。 |
|
|
44
|
+
| **Desktop** | 一个原生 Windows 应用程序,使用 WinUI 3 + WebView2。将控制面板打包在本地,按需获取实时统计数据。 |
|
|
45
|
+
|
|
46
|
+
## 控制面板
|
|
47
|
+
|
|
48
|
+
一个自动更新的统计信息控制面板位于 [`/dashboard/`](https://mcp-tool-shop-org.github.io/registry-stats/dashboard/)。
|
|
49
|
+
|
|
50
|
+
- **分Tab界面** — 包含“首页”、“分析”、“排行榜”和“帮助”等Tab。
|
|
51
|
+
- **Pulse AI智能助手** — 基于Ollama的对话式助手,支持语音合成(LLM模型输出时同步语音,提供4种声音,通过[mcp-voice-soundboard](https://github.com/mcp-tool-shop-org/mcp-voice-soundboard)实现),网页搜索(Wikipedia + 可选的SearXNG),自动语音播报,全屏模式,GitHub组织数据连接器,模型选择器,以及对话记忆功能。
|
|
52
|
+
- **关键指标概览** — 健康评分(0–100),多样性指数,每周变化,所有注册表的总下载量。
|
|
53
|
+
- **六个交互式图表** — 30天趋势图(聚合/按注册表/前5名切换),注册表份额(极坐标图),投资组合风险(直方图 + Gini系数和P90),前10名增长情况,速度跟踪图(带sparklines),以及30天热力图(带异常值检测,>2σ)。
|
|
54
|
+
- **智能增长引擎** — 通过基线阈值、百分比上限和阻尼速度公式,处理小基数偏差问题。
|
|
55
|
+
- **可执行的洞察** — 自动生成推荐和关注提醒,用于识别下降的软件包。
|
|
56
|
+
- **Pulse面板** — 分屏显示已建立的流行软件包(≥ 50次/周下载)和新兴/新软件包,并带有内联7天sparklines,绝对值和百分比变化,基线信息,以及简要的执行摘要。
|
|
57
|
+
- **实时刷新** — 客户端按需从npm和PyPI API获取数据,并显示进度指示器;结果缓存在sessionStorage中(5分钟TTL),因此Tab切换速度非常快。
|
|
58
|
+
- **导出报告** — 在“刷新”按钮旁边,提供下拉菜单,可以选择三种格式:**Exec PDF**(通过jsPDF实现),**LLM JSONL**(用于AI模型输入的结构化记录),以及**Dev Markdown**(GFM表格)。
|
|
59
|
+
- **排行榜** — 按照每周下载量对132个软件包进行排名,并带有内联30天sparklines和智能趋势标识。
|
|
60
|
+
- **配置页面** — 包含投资组合编辑器(带验证功能),注册表同步辅助部分,以及流水线概览。
|
|
61
|
+
- **帮助Tab** — 提供用户友好的指南,涵盖每个Tab,关键概念,AI助手使用技巧,数据流水线,以及有用的链接。
|
|
62
|
+
- **深色/浅色主题** — 自动跟随系统偏好设置。
|
|
63
|
+
|
|
64
|
+
数据在构建时获取,并每周由 CI 自动重建(每周一 06:00 UTC)。实时刷新直接从注册中心 API 获取最新数据。在 `site/src/data/packages.json` 中配置要跟踪的软件包(5 个注册中心,共 132 个软件包)。
|
|
65
|
+
|
|
66
|
+
## 桌面应用程序
|
|
67
|
+
|
|
68
|
+
一个原生 Windows 应用程序,它将控制面板封装在一个本地 WebView2 容器中:
|
|
69
|
+
|
|
70
|
+
- **支持离线使用** — 包含打包的 HTML/CSS/JS;无需互联网连接即可使用
|
|
71
|
+
- **实时刷新** — 按需从 GitHub Pages 获取 `stats.json` 文件
|
|
72
|
+
- **CSV 导出** — 一键导出排行榜数据
|
|
73
|
+
- **MSIX 软件包** — 通过 `desktop-ci.yml` 在 CI 中构建和签名
|
|
74
|
+
|
|
75
|
+
桌面应用程序的源代码位于 `desktop/` 目录中。使用 .NET 10 MAUI 构建,目标是 WinUI 3。
|
|
76
|
+
|
|
77
|
+
## 安装
|
|
78
|
+
|
|
76
79
|
```bash
|
|
77
80
|
npm install @mcptoolshop/registry-stats
|
|
78
81
|
```
|
|
79
|
-
|
|
80
|
-
## 命令行界面 (CLI)
|
|
81
|
-
|
|
82
|
+
|
|
83
|
+
## 命令行界面 (CLI)
|
|
84
|
+
|
|
82
85
|
```bash
|
|
83
86
|
# Query a single registry
|
|
84
87
|
registry-stats express -r npm
|
|
@@ -116,11 +119,11 @@ registry-stats express -r npm --range 2025-01-01:2025-06-30 --format chart
|
|
|
116
119
|
# Start a REST API server
|
|
117
120
|
registry-stats serve --port 3000
|
|
118
121
|
```
|
|
119
|
-
|
|
120
|
-
## 配置文件
|
|
121
|
-
|
|
122
|
-
在您的项目根目录下创建一个 `registry-stats.config.json` 文件(或者运行 `registry-stats --init` 命令):
|
|
123
|
-
|
|
122
|
+
|
|
123
|
+
## 配置文件
|
|
124
|
+
|
|
125
|
+
在您的项目根目录下创建一个 `registry-stats.config.json` 文件(或者运行 `registry-stats --init` 命令):
|
|
126
|
+
|
|
124
127
|
```json
|
|
125
128
|
{
|
|
126
129
|
"registries": ["npm", "pypi", "nuget", "vscode", "docker"],
|
|
@@ -139,11 +142,11 @@ registry-stats serve --port 3000
|
|
|
139
142
|
"concurrency": 5
|
|
140
143
|
}
|
|
141
144
|
```
|
|
142
|
-
|
|
143
|
-
运行 `registry-stats` 命令,不带任何参数,即可获取所有配置软件包的统计数据。命令行界面会从当前工作目录向上查找最近的配置文件。
|
|
144
|
-
|
|
145
|
-
配置文件也可以通过编程方式访问:
|
|
146
|
-
|
|
145
|
+
|
|
146
|
+
运行 `registry-stats` 命令,不带任何参数,即可获取所有配置软件包的统计数据。命令行界面会从当前工作目录向上查找最近的配置文件。
|
|
147
|
+
|
|
148
|
+
配置文件也可以通过编程方式访问:
|
|
149
|
+
|
|
147
150
|
```typescript
|
|
148
151
|
import { loadConfig, defaultConfig, starterConfig } from '@mcptoolshop/registry-stats';
|
|
149
152
|
|
|
@@ -151,9 +154,9 @@ const config = loadConfig(); // finds nearest config file, or null
|
|
|
151
154
|
const defaults = defaultConfig(); // returns default Config object
|
|
152
155
|
const template = starterConfig(); // returns starter JSON string
|
|
153
156
|
```
|
|
154
|
-
|
|
155
|
-
## 编程 API
|
|
156
|
-
|
|
157
|
+
|
|
158
|
+
## 编程 API
|
|
159
|
+
|
|
157
160
|
```typescript
|
|
158
161
|
import { stats, calc, createCache } from '@mcptoolshop/registry-stats';
|
|
159
162
|
|
|
@@ -194,41 +197,41 @@ const cache = createCache();
|
|
|
194
197
|
await stats('npm', 'express', { cache }); // fetches
|
|
195
198
|
await stats('npm', 'express', { cache }); // cache hit
|
|
196
199
|
```
|
|
197
|
-
|
|
198
|
-
## 仓库支持
|
|
199
|
-
|
|
200
|
-
| 仓库 | 软件包格式 | 时间序列 | 可用数据 |
|
|
201
|
-
|----------|---------------|-------------|----------------|
|
|
202
|
-
| `npm` | `express`, `@scope/pkg` | 是 (549 天) | 最近一天、最近一周、最近一个月 |
|
|
203
|
-
| `pypi` | `requests` | 是 (180 天) | 最近一天、最近一周、最近一个月、总数 |
|
|
204
|
-
| `nuget` | `Newtonsoft.Json` | No | 总数 |
|
|
205
|
-
| `vscode` | `publisher.extension` | No | 总数(安装量)、评分、趋势 |
|
|
206
|
-
| `docker` | `namespace/repo` | No | 总数(拉取次数)、星级 |
|
|
207
|
-
|
|
208
|
-
## 内置可靠性
|
|
209
|
-
|
|
210
|
-
- 自动重试,并在遇到 429/5xx 错误时采用指数退避策略
|
|
211
|
-
- 尊重 `Retry-After` 头部信息
|
|
212
|
-
- 批量请求的并发限制
|
|
213
|
-
- 可选的 TTL 缓存(可插拔,通过 `StatsCache` 接口,您可以自定义 Redis 或文件后端)
|
|
214
|
-
|
|
215
|
-
## REST API 服务器
|
|
216
|
-
|
|
217
|
-
可以作为微服务运行,也可以嵌入到您自己的服务器中:
|
|
218
|
-
|
|
200
|
+
|
|
201
|
+
## 仓库支持
|
|
202
|
+
|
|
203
|
+
| 仓库 | 软件包格式 | 时间序列 | 可用数据 |
|
|
204
|
+
|----------|---------------|-------------|----------------|
|
|
205
|
+
| `npm` | `express`, `@scope/pkg` | 是 (549 天) | 最近一天、最近一周、最近一个月 |
|
|
206
|
+
| `pypi` | `requests` | 是 (180 天) | 最近一天、最近一周、最近一个月、总数 |
|
|
207
|
+
| `nuget` | `Newtonsoft.Json` | No | 总数 |
|
|
208
|
+
| `vscode` | `publisher.extension` | No | 总数(安装量)、评分、趋势 |
|
|
209
|
+
| `docker` | `namespace/repo` | No | 总数(拉取次数)、星级 |
|
|
210
|
+
|
|
211
|
+
## 内置可靠性
|
|
212
|
+
|
|
213
|
+
- 自动重试,并在遇到 429/5xx 错误时采用指数退避策略
|
|
214
|
+
- 尊重 `Retry-After` 头部信息
|
|
215
|
+
- 批量请求的并发限制
|
|
216
|
+
- 可选的 TTL 缓存(可插拔,通过 `StatsCache` 接口,您可以自定义 Redis 或文件后端)
|
|
217
|
+
|
|
218
|
+
## REST API 服务器
|
|
219
|
+
|
|
220
|
+
可以作为微服务运行,也可以嵌入到您自己的服务器中:
|
|
221
|
+
|
|
219
222
|
```bash
|
|
220
223
|
registry-stats serve --port 3000
|
|
221
224
|
```
|
|
222
|
-
|
|
225
|
+
|
|
223
226
|
```
|
|
224
227
|
GET /stats/:package # all registries
|
|
225
228
|
GET /stats/:registry/:package # single registry
|
|
226
229
|
GET /compare/:package?registries=npm,pypi
|
|
227
230
|
GET /range/:registry/:package?start=YYYY-MM-DD&end=YYYY-MM-DD&format=json|csv|chart
|
|
228
231
|
```
|
|
229
|
-
|
|
230
|
-
用于自定义服务器或无服务器环境的编程用法:
|
|
231
|
-
|
|
232
|
+
|
|
233
|
+
用于自定义服务器或无服务器环境的编程用法:
|
|
234
|
+
|
|
232
235
|
```typescript
|
|
233
236
|
import { createHandler, serve } from '@mcptoolshop/registry-stats';
|
|
234
237
|
|
|
@@ -240,9 +243,9 @@ import { createServer } from 'node:http';
|
|
|
240
243
|
const handler = createHandler();
|
|
241
244
|
createServer(handler).listen(3000);
|
|
242
245
|
```
|
|
243
|
-
|
|
244
|
-
## 自定义仓库
|
|
245
|
-
|
|
246
|
+
|
|
247
|
+
## 自定义仓库
|
|
248
|
+
|
|
246
249
|
```typescript
|
|
247
250
|
import { registerProvider, type RegistryProvider } from '@mcptoolshop/registry-stats';
|
|
248
251
|
|
|
@@ -263,9 +266,9 @@ const cargo: RegistryProvider = {
|
|
|
263
266
|
registerProvider(cargo);
|
|
264
267
|
await stats('cargo', 'serde');
|
|
265
268
|
```
|
|
266
|
-
|
|
267
|
-
## 代码仓库结构
|
|
268
|
-
|
|
269
|
+
|
|
270
|
+
## 代码仓库结构
|
|
271
|
+
|
|
269
272
|
```
|
|
270
273
|
registry-stats/
|
|
271
274
|
├── src/ # TypeScript engine (published to npm)
|
|
@@ -273,9 +276,9 @@ registry-stats/
|
|
|
273
276
|
├── desktop/ # WinUI 3 desktop app (.NET 10 MAUI)
|
|
274
277
|
└── test/ # Library tests (vitest)
|
|
275
278
|
```
|
|
276
|
-
|
|
277
|
-
## 开发
|
|
278
|
-
|
|
279
|
+
|
|
280
|
+
## 开发
|
|
281
|
+
|
|
279
282
|
```bash
|
|
280
283
|
# Engine
|
|
281
284
|
npm install && npm run build && npm test
|
|
@@ -286,36 +289,36 @@ npm run site:dev
|
|
|
286
289
|
# Dashboard (production build)
|
|
287
290
|
npm run site:build
|
|
288
291
|
```
|
|
289
|
-
|
|
290
|
-
##
|
|
291
|
-
|
|
292
|
-
| 方面 | 详细信息 |
|
|
293
|
-
|--------|--------|
|
|
294
|
-
| **Data touched** | 从 npm、PyPI、NuGet、VS Code Marketplace
|
|
295
|
-
| **Data NOT touched** |
|
|
296
|
-
| **Permissions** | 读取:通过 HTTPS
|
|
297
|
-
| **Network** |
|
|
298
|
-
| **Telemetry** | 未收集或发送任何数据。 |
|
|
299
|
-
|
|
300
|
-
请参阅 [SECURITY.md](SECURITY.md)
|
|
301
|
-
|
|
302
|
-
## 评分卡
|
|
303
|
-
|
|
304
|
-
| 类别 | 评分 |
|
|
305
|
-
|----------|-------|
|
|
306
|
-
| A. 安全性 | 10 |
|
|
307
|
-
| B. 错误处理 | 10 |
|
|
308
|
-
| C.
|
|
309
|
-
| D. 发布规范 | 10 |
|
|
310
|
-
| E. 身份验证(软性) | 10 |
|
|
311
|
-
| **Overall** | **50/50** |
|
|
312
|
-
|
|
313
|
-
> 完整审计:[SHIP_GATE.md](SHIP_GATE.md) · [SCORECARD.md](SCORECARD.md)
|
|
314
|
-
|
|
315
|
-
## 许可证
|
|
316
|
-
|
|
317
|
-
MIT
|
|
318
|
-
|
|
292
|
+
|
|
293
|
+
## 安全与数据范围
|
|
294
|
+
|
|
295
|
+
| 方面 | 详细信息 |
|
|
296
|
+
|--------|--------|
|
|
297
|
+
| **Data touched** | 从 npm、PyPI、NuGet、VS Code Marketplace、Docker Hub 获取的公开下载统计数据。可选的内存缓存。 |
|
|
298
|
+
| **Data NOT touched** | 无遥测。无分析。无凭证存储。无用户数据。无文件写入。 |
|
|
299
|
+
| **Permissions** | 读取:通过 HTTPS 访问公共注册表 API。写入:仅限于标准输出/标准错误输出。 |
|
|
300
|
+
| **Network** | 通过 HTTPS 访问公共注册表 API。可选的本地 REST 服务器。 |
|
|
301
|
+
| **Telemetry** | 未收集或发送任何数据。 |
|
|
302
|
+
|
|
303
|
+
请参阅 [SECURITY.md](SECURITY.md) 以报告漏洞。
|
|
304
|
+
|
|
305
|
+
## 评分卡
|
|
306
|
+
|
|
307
|
+
| 类别 | 评分 |
|
|
308
|
+
|----------|-------|
|
|
309
|
+
| A. 安全性 | 10 |
|
|
310
|
+
| B. 错误处理 | 10 |
|
|
311
|
+
| C. 操作文档 | 10 |
|
|
312
|
+
| D. 发布规范 | 10 |
|
|
313
|
+
| E. 身份验证(软性) | 10 |
|
|
314
|
+
| **Overall** | **50/50** |
|
|
315
|
+
|
|
316
|
+
> 完整审计:[SHIP_GATE.md](SHIP_GATE.md) · [SCORECARD.md](SCORECARD.md)
|
|
317
|
+
|
|
318
|
+
## 许可证
|
|
319
|
+
|
|
320
|
+
MIT
|
|
321
|
+
|
|
319
322
|
---
|
|
320
|
-
|
|
321
|
-
由 <a href="https://mcp-tool-shop.github.io/">MCP Tool Shop</a> 构建。
|
|
323
|
+
|
|
324
|
+
由 <a href="https://mcp-tool-shop.github.io/">MCP Tool Shop</a> 构建。
|
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ async function fetchWithRetry(url, registry, init) {
|
|
|
40
40
|
let lastError;
|
|
41
41
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
42
42
|
await acquireSlot(registry);
|
|
43
|
-
const res = await fetch(url, init);
|
|
43
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(3e4), ...init });
|
|
44
44
|
if (res.status === 404) return null;
|
|
45
45
|
if (res.ok) return res.json();
|
|
46
46
|
const retryAfter = res.headers.get("retry-after");
|
|
@@ -62,7 +62,7 @@ async function fetchWithRetry(url, registry, init) {
|
|
|
62
62
|
async function fetchDirect(url, registry, init) {
|
|
63
63
|
let lastError;
|
|
64
64
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
65
|
-
const res = await fetch(url, init);
|
|
65
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(3e4), ...init });
|
|
66
66
|
if (res.status === 404) return null;
|
|
67
67
|
if (res.ok) return res.json();
|
|
68
68
|
const retryAfter = res.headers.get("retry-after");
|
|
@@ -284,7 +284,8 @@ var docker = {
|
|
|
284
284
|
if (options?.dockerToken) {
|
|
285
285
|
headers["Authorization"] = `Bearer ${options.dockerToken}`;
|
|
286
286
|
}
|
|
287
|
-
const
|
|
287
|
+
const safePkg = pkg.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
288
|
+
const json2 = await fetchWithRetry(`${API4}/${safePkg}`, "docker", { headers });
|
|
288
289
|
if (!json2 || !json2.name || !json2.namespace) return null;
|
|
289
290
|
return {
|
|
290
291
|
registry: "docker",
|