dsh-config-manager 0.1.37 → 0.1.39

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.
Files changed (44) hide show
  1. package/lib/client.d.ts +173 -66
  2. package/lib/client.js +1643 -1115
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +175 -1
  5. package/lib/index.js.map +1 -1
  6. package/lib/market/git-file-writer.d.ts +86 -0
  7. package/lib/market/git-file-writer.js +269 -0
  8. package/lib/market/git-file-writer.js.map +1 -0
  9. package/lib/market/github-repos.d.ts +122 -0
  10. package/lib/market/github-repos.js +407 -0
  11. package/lib/market/github-repos.js.map +1 -0
  12. package/lib/market/my-repo.d.ts +131 -0
  13. package/lib/market/my-repo.js +389 -0
  14. package/lib/market/my-repo.js.map +1 -0
  15. package/lib/ui/i18n.d.ts +9 -10
  16. package/lib/ui/i18n.js +19 -21
  17. package/lib/ui/i18n.js.map +1 -1
  18. package/package.json +1 -1
  19. package/src/client/ConfigManagerSection.tsx +2 -2
  20. package/src/client/client-types.ts +2 -0
  21. package/src/client/index.ts +4 -2
  22. package/src/client/market/MarketPanel.tsx +64 -17
  23. package/src/client/market/MyConfigsView.tsx +745 -0
  24. package/src/client/market/market-api.test.ts +29 -0
  25. package/src/client/market/market-api.ts +5 -2
  26. package/src/client/market/market-locales.ts +118 -117
  27. package/src/client/market/my-configs-api.ts +163 -0
  28. package/src/client/market/my-configs-view.test.ts +289 -0
  29. package/src/client/market/my-configs-view.ts +304 -0
  30. package/src/client/run-store.ts +13 -0
  31. package/src/index.ts +163 -1
  32. package/src/market/git-file-writer.test.ts +249 -0
  33. package/src/market/git-file-writer.ts +336 -0
  34. package/src/market/github-repos.test.ts +400 -0
  35. package/src/market/github-repos.ts +481 -0
  36. package/src/market/my-repo.test.ts +472 -0
  37. package/src/market/my-repo.ts +516 -0
  38. package/src/ui/i18n.ts +19 -21
  39. package/lib/ui/market-publish.d.ts +0 -129
  40. package/lib/ui/market-publish.js +0 -168
  41. package/lib/ui/market-publish.js.map +0 -1
  42. package/src/client/market/PublishView.tsx +0 -388
  43. package/src/ui/market-publish.test.ts +0 -219
  44. package/src/ui/market-publish.ts +0 -256
@@ -0,0 +1,407 @@
1
+ /**
2
+ * m-market:GitHub REST API 薄客户端 GitHubAuthRest(docs/design/2026-08-20-my-configs-design.md §4.2)。
3
+ *
4
+ * 职责:账户级元操作 ——
5
+ * getUser() 验证 token(GET /user)并取回登录名;
6
+ * repoExists() 判断仓库是否存在(200 / 404);
7
+ * createPublicRepo() 创建公开仓库(POST /user/repos,public + auto_init);
8
+ * ensureFork() 复用用户已 fork 的仓库 / 新建 fork 并轮询就绪(fork 异步创建);
9
+ * readFile() 读仓库内文本文件(contents API,base64 解码);
10
+ * openPullRequest() 开 PR(POST /pulls;缺省目标 = 固定官方收录仓库);
11
+ * listOpenPullRequests() 列 open PR(可带 head 过滤,如 `<login>:<branch>`)。
12
+ *
13
+ * 与 GitTransport 的分工:本模块只做 REST 元操作;内容推送(clone → 写文件 → commit →
14
+ * push)走 GitTransport(token 经 credentials resolve,永不进 URL / 日志 / 浏览器)。
15
+ *
16
+ * 安全不变量(与 src/sync/github-auth.ts 同款纪律):
17
+ * - token 只经 Authorization 头传递,绝不进入 URL / 请求体 / 错误消息 / 返回值;
18
+ * - 所有错误消息(含网络异常原文、GitHub 错误体回显内容)统一过 redact() 幂等脱敏,
19
+ * token 形态(ghp_/gho_/github_pat_/Bearer 等)必被掩码;
20
+ * - token 缺失(空串)→ 明确错误(code='no_token'),由上层映射为「未登录」;
21
+ * - 仓库 URL(cloneUrl / htmlUrl)为 https 形态,绝不拼接凭据。
22
+ *
23
+ * 依赖注入:tokenProvider 必填;fetcher / now / 轮询参数可注入,测试全程 mock 不碰真实网络。
24
+ */
25
+ import { redact } from "../security/redaction.js";
26
+ /** GitHub REST API v3 基址 */
27
+ export const GITHUB_API_BASE = 'https://api.github.com';
28
+ /**
29
+ * 官方收录目标仓库(产品决策 2026-08-20,docs/design/2026-08-20-my-configs-design.md §2.4):
30
+ * 收录 / PR 相关目标**固定**为 xiajiajun516/dsh-config-market,写死常量、界面不提供任何修改入口。
31
+ * 与 src/market/builtin.ts 的内置市场为同一仓库(内置市场 URL 可经 env 覆盖仅用于浏览,
32
+ * 收录目标不受 env 影响,恒为官方仓库)。
33
+ */
34
+ export const MARKET_UPSTREAM_OWNER = 'xiajiajun516';
35
+ /** 官方收录目标仓库名(见 MARKET_UPSTREAM_OWNER) */
36
+ export const MARKET_UPSTREAM_REPO = 'dsh-config-market';
37
+ /** fork 异步创建轮询缺省间隔(毫秒) */
38
+ const DEFAULT_POLL_INTERVAL_MS = 2_000;
39
+ /** fork 异步创建轮询缺省超时(毫秒):GitHub 首次 fork 需复制仓库内容,可能超过 60s;180s 兜底后抛可重试错误 */
40
+ const DEFAULT_POLL_TIMEOUT_MS = 180_000;
41
+ /**
42
+ * GitHub REST 可预期错误:status = HTTP 状态(网络层 / 内部错误无),code 可分类消费。
43
+ * message 已过 redact() 脱敏,绝不内嵌 token 形态内容。
44
+ */
45
+ export class GitHubApiError extends Error {
46
+ code;
47
+ status;
48
+ constructor(message, code, status) {
49
+ super(message);
50
+ this.name = 'GitHubApiError';
51
+ this.code = code;
52
+ this.status = status;
53
+ }
54
+ }
55
+ /* ---------------------------------------------------------------- 响应解析小工具 */
56
+ function asRecord(value) {
57
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
58
+ throw new GitHubApiError('GitHub API 返回了无法识别的数据结构', 'invalid_response');
59
+ }
60
+ return value;
61
+ }
62
+ function getStr(obj, key) {
63
+ const v = obj[key];
64
+ if (typeof v !== 'string' || v === '') {
65
+ throw new GitHubApiError(`GitHub API 响应缺少必要字段 ${key}`, 'invalid_response');
66
+ }
67
+ return v;
68
+ }
69
+ function getOptStr(obj, key) {
70
+ const v = obj[key];
71
+ return typeof v === 'string' && v !== '' ? v : undefined;
72
+ }
73
+ function getNum(obj, key) {
74
+ const v = obj[key];
75
+ if (typeof v !== 'number' || !Number.isFinite(v)) {
76
+ throw new GitHubApiError(`GitHub API 响应缺少必要字段 ${key}`, 'invalid_response');
77
+ }
78
+ return v;
79
+ }
80
+ function getBool(obj, key) {
81
+ const v = obj[key];
82
+ if (typeof v !== 'boolean') {
83
+ throw new GitHubApiError(`GitHub API 响应缺少必要字段 ${key}`, 'invalid_response');
84
+ }
85
+ return v;
86
+ }
87
+ /** 嵌套 owner.login(owner 可能为 null —— fork 异步创建期间未就绪) */
88
+ function ownerLoginOf(obj) {
89
+ const owner = obj['owner'];
90
+ if (owner === null || typeof owner !== 'object' || Array.isArray(owner))
91
+ return undefined;
92
+ const login = owner['login'];
93
+ return typeof login === 'string' && login !== '' ? login : undefined;
94
+ }
95
+ /** 路径片段编码(owner/repo/ref 单段) */
96
+ function seg(value) {
97
+ return encodeURIComponent(value);
98
+ }
99
+ /** 仓库内文件路径编码(逐段编码,保留 '/' 层级;contents API 路径) */
100
+ function segPath(path) {
101
+ return path.split('/').map(encodeURIComponent).join('/');
102
+ }
103
+ function delay(ms) {
104
+ if (ms <= 0)
105
+ return Promise.resolve();
106
+ return new Promise((resolve) => setTimeout(resolve, ms));
107
+ }
108
+ /** 把 GitHub REST 错误响应(或任意非 2xx)归类为可消费的 code */
109
+ function classifyErrorCode(status, rateLimited) {
110
+ if (status === 401)
111
+ return 'unauthorized';
112
+ if (status === 403)
113
+ return rateLimited ? 'rate_limited' : 'forbidden';
114
+ if (status === 404)
115
+ return 'not_found';
116
+ if (status === 409)
117
+ return 'conflict';
118
+ if (status === 422)
119
+ return 'validation_failed';
120
+ if (status >= 500)
121
+ return 'server_error';
122
+ return `http_${status}`;
123
+ }
124
+ async function safeResponseText(response) {
125
+ try {
126
+ return await response.text();
127
+ }
128
+ catch {
129
+ return '';
130
+ }
131
+ }
132
+ /** 仓库 JSON → GitHubRepoInfo(缺 default_branch 时按 'main' 兜底;owner 嵌套对象未就绪时无碍) */
133
+ function toRepoInfo(data) {
134
+ const obj = asRecord(data);
135
+ const fullName = getStr(obj, 'full_name');
136
+ return {
137
+ fullName,
138
+ htmlUrl: getStr(obj, 'html_url'),
139
+ cloneUrl: getOptStr(obj, 'clone_url') ?? `https://github.com/${fullName}.git`,
140
+ defaultBranch: getOptStr(obj, 'default_branch') ?? 'main',
141
+ private: getBool(obj, 'private'),
142
+ fork: getBool(obj, 'fork'),
143
+ };
144
+ }
145
+ /** PR JSON → GitHubPullRequestInfo(head.ref 缺失时置空串) */
146
+ function toPullInfo(data) {
147
+ const obj = asRecord(data);
148
+ const headObj = obj['head'];
149
+ let headRef = '';
150
+ if (headObj !== null && typeof headObj === 'object' && !Array.isArray(headObj)) {
151
+ const ref = headObj['ref'];
152
+ if (typeof ref === 'string')
153
+ headRef = ref;
154
+ }
155
+ return {
156
+ number: getNum(obj, 'number'),
157
+ htmlUrl: getStr(obj, 'html_url'),
158
+ title: getOptStr(obj, 'title') ?? '',
159
+ head: headRef,
160
+ state: getOptStr(obj, 'state') ?? 'open',
161
+ merged: getBool(obj, 'merged'),
162
+ };
163
+ }
164
+ /* ---------------------------------------------------------------- GitHubAuthRest */
165
+ /**
166
+ * GitHub REST 薄客户端。
167
+ * 无状态(token 由 tokenProvider 每次现取),fetch / 时钟 / 轮询参数可注入。
168
+ */
169
+ export class GitHubAuthRest {
170
+ tokenProvider;
171
+ fetcher;
172
+ now;
173
+ pollIntervalMs;
174
+ pollTimeoutMs;
175
+ constructor(options) {
176
+ this.tokenProvider = options.tokenProvider;
177
+ this.fetcher = options.fetcher ?? fetch;
178
+ this.now = options.now ?? (() => Date.now());
179
+ this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
180
+ this.pollTimeoutMs = options.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
181
+ }
182
+ /** 验证 token 并取回当前用户(/me/status 与 ensureFork 的前置)。401 → unauthorized。 */
183
+ async getUser() {
184
+ const data = await this.requestJson('GET', '/user');
185
+ const obj = asRecord(data);
186
+ return { login: getStr(obj, 'login'), id: getNum(obj, 'id'), name: getOptStr(obj, 'name') };
187
+ }
188
+ /** 仓库是否存在:200 → true;404 → false;其余状态抛 GitHubApiError。 */
189
+ async repoExists(owner, repo) {
190
+ const response = await this.request(`/repos/${seg(owner)}/${seg(repo)}`, { method: 'GET' });
191
+ if (response.ok)
192
+ return true;
193
+ if (response.status === 404)
194
+ return false;
195
+ throw await this.apiError(response);
196
+ }
197
+ /** 创建公开仓库(POST /user/repos;private:false + auto_init:true 保证有初始 commit 可 clone)。 */
198
+ async createPublicRepo(name, description) {
199
+ const body = { name, private: false, auto_init: true };
200
+ if (description !== undefined && description !== '')
201
+ body['description'] = description;
202
+ const data = await this.requestJson('POST', '/user/repos', body);
203
+ return toRepoInfo(data);
204
+ }
205
+ /**
206
+ * 确保存在「当前用户 fork 的 <owner>/<repo>」:
207
+ * 1. 优先复用:列出上游仓库 forks,命中 owner.login === 当前登录名的 fork → 直接返回;
208
+ * 2. 否则创建 fork(POST /forks,异步)并轮询 GET /repos/<login>/<repo> 直到就绪
209
+ * (owner.login 匹配 + fork:true);超时抛可重试错误(fork_timeout)。
210
+ */
211
+ async ensureFork(owner, repo) {
212
+ const me = await this.getUser();
213
+ const existing = await this.findUserFork(owner, repo, me.login);
214
+ if (existing !== null)
215
+ return existing;
216
+ await this.createFork(owner, repo);
217
+ return this.waitForkReady(me.login, repo);
218
+ }
219
+ /**
220
+ * 读仓库内文本文件(contents API,base64 解码为 UTF-8)。
221
+ * 404(文件不存在)→ null;路径是目录 / 非文件 → not_a_file;其余错误抛 GitHubApiError。
222
+ */
223
+ async readFile(owner, repo, path, ref) {
224
+ const query = ref !== undefined && ref !== '' ? `?ref=${encodeURIComponent(ref)}` : '';
225
+ const response = await this.request(`/repos/${seg(owner)}/${seg(repo)}/contents/${segPath(path)}${query}`, { method: 'GET' });
226
+ if (response.status === 404)
227
+ return null;
228
+ if (!response.ok)
229
+ throw await this.apiError(response);
230
+ const data = await safeParseJson(response);
231
+ const obj = asRecord(data);
232
+ if (obj['type'] !== 'file') {
233
+ throw new GitHubApiError('GitHub contents 路径不是文件(目录 / 子模块不可作为文本读取)', 'not_a_file');
234
+ }
235
+ const content = obj['content'];
236
+ const encoding = obj['encoding'];
237
+ // 允许空串(0 字节空文件 GitHub 返回 content:"");缺失/非 string 才是异常
238
+ if (typeof content !== 'string') {
239
+ throw new GitHubApiError('GitHub contents 响应缺少文件内容', 'invalid_response');
240
+ }
241
+ if (typeof encoding === 'string' && encoding !== '' && encoding !== 'base64') {
242
+ throw new GitHubApiError(`不支持的 contents 编码:${encoding}`, 'invalid_response');
243
+ }
244
+ return Buffer.from(content, 'base64').toString('utf8');
245
+ }
246
+ /** 开 PR(POST /pulls);owner/repo 缺省 = 固定官方收录仓库。 */
247
+ async openPullRequest(params) {
248
+ const owner = params.owner ?? MARKET_UPSTREAM_OWNER;
249
+ const repo = params.repo ?? MARKET_UPSTREAM_REPO;
250
+ const body = { title: params.title, head: params.head, base: params.base };
251
+ if (params.body !== undefined && params.body !== '')
252
+ body['body'] = params.body;
253
+ const data = await this.requestJson('POST', `/repos/${seg(owner)}/${seg(repo)}/pulls`, body);
254
+ return toPullInfo(data);
255
+ }
256
+ /** 列 open PR(GET /pulls?state=open);head 可选(跨仓库形态 `<login>:<branch>`,URL 编码传入)。 */
257
+ async listOpenPullRequests(owner, repo, head) {
258
+ const query = new URLSearchParams();
259
+ query.set('state', 'open');
260
+ query.set('per_page', '100');
261
+ if (head !== undefined && head !== '')
262
+ query.set('head', head);
263
+ const data = await this.requestJson('GET', `/repos/${seg(owner)}/${seg(repo)}/pulls?${query.toString()}`);
264
+ if (!Array.isArray(data)) {
265
+ throw new GitHubApiError('GitHub pulls 响应不是数组', 'invalid_response');
266
+ }
267
+ return data.map((item) => toPullInfo(item));
268
+ }
269
+ /* ---------------------------------------------------------------- 内部实现 */
270
+ /** 上游仓库 forks 列表命中当前用户 → 返回 fork 信息;否则 null。 */
271
+ async findUserFork(owner, repo, login) {
272
+ const data = await this.requestJson('GET', `/repos/${seg(owner)}/${seg(repo)}/forks?per_page=100&sort=newest`);
273
+ if (!Array.isArray(data)) {
274
+ throw new GitHubApiError('GitHub forks 响应不是数组', 'invalid_response');
275
+ }
276
+ for (const item of data) {
277
+ const obj = asRecord(item);
278
+ if (ownerLoginOf(obj) === login) {
279
+ const fullName = getStr(obj, 'full_name');
280
+ return {
281
+ fullName,
282
+ htmlUrl: getStr(obj, 'html_url'),
283
+ cloneUrl: getOptStr(obj, 'clone_url') ?? `https://github.com/${fullName}.git`,
284
+ defaultBranch: getOptStr(obj, 'default_branch') ?? 'main',
285
+ };
286
+ }
287
+ }
288
+ return null;
289
+ }
290
+ /** 创建 fork(POST /forks)。GitHub 异步执行,返回 202/200 即视为已接受,就绪状态由轮询确认。 */
291
+ async createFork(owner, repo) {
292
+ const response = await this.request(`/repos/${seg(owner)}/${seg(repo)}/forks`, {
293
+ method: 'POST',
294
+ headers: { 'content-type': 'application/json' },
295
+ body: '{}',
296
+ });
297
+ if (!response.ok)
298
+ throw await this.apiError(response);
299
+ }
300
+ /** 轮询 GET /repos/<login>/<repo> 直到 fork 就绪(owner.login 匹配 + fork:true + 有默认分支)。 */
301
+ async waitForkReady(login, repo) {
302
+ const deadline = this.now() + this.pollTimeoutMs;
303
+ for (;;) {
304
+ const response = await this.request(`/repos/${seg(login)}/${seg(repo)}`, { method: 'GET' });
305
+ if (response.ok) {
306
+ const info = await this.tryReadForkInfo(response);
307
+ if (info !== null)
308
+ return info;
309
+ }
310
+ else if (response.status !== 404) {
311
+ throw await this.apiError(response);
312
+ }
313
+ if (this.now() >= deadline) {
314
+ throw new GitHubApiError('fork 创建超时(GitHub 异步创建,请稍后重试)', 'fork_timeout');
315
+ }
316
+ await delay(this.pollIntervalMs);
317
+ }
318
+ }
319
+ /** 解析一次轮询响应;未就绪(结构不完整 / owner 未出现)→ null 继续等。 */
320
+ async tryReadForkInfo(response) {
321
+ try {
322
+ const data = await safeParseJson(response);
323
+ const obj = asRecord(data);
324
+ const login = ownerLoginOf(obj);
325
+ const fullName = getStr(obj, 'full_name');
326
+ if (login === undefined || getBool(obj, 'fork') !== true)
327
+ return null;
328
+ return {
329
+ fullName,
330
+ htmlUrl: getStr(obj, 'html_url'),
331
+ cloneUrl: getOptStr(obj, 'clone_url') ?? `https://github.com/${fullName}.git`,
332
+ defaultBranch: getOptStr(obj, 'default_branch') ?? 'main',
333
+ };
334
+ }
335
+ catch {
336
+ return null; // 未就绪 / 结构异常 → 按未就绪继续轮询
337
+ }
338
+ }
339
+ /** 统一请求管道:注入 token(Bearer 头)、Accept、API 版本;网络异常 → network_error(消息脱敏)。 */
340
+ async request(path, init = {}) {
341
+ const token = await this.tokenProvider();
342
+ if (typeof token !== 'string' || token === '') {
343
+ throw new GitHubApiError('GitHub token 未配置(请先登录)', 'no_token');
344
+ }
345
+ const headers = new Headers(init.headers);
346
+ headers.set('accept', 'application/vnd.github+json');
347
+ headers.set('authorization', `Bearer ${token}`);
348
+ headers.set('x-github-api-version', '2022-11-28');
349
+ let response;
350
+ try {
351
+ response = await this.fetcher(GITHUB_API_BASE + path, { ...init, headers });
352
+ }
353
+ catch (err) {
354
+ throw new GitHubApiError(`GitHub API 请求失败:${redact(err instanceof Error ? err.message : String(err))}`, 'network_error');
355
+ }
356
+ return response;
357
+ }
358
+ /** 请求 + 2xx 校验 + JSON 解析(空体 → undefined)。非 2xx → apiError。 */
359
+ async requestJson(method, path, body) {
360
+ const init = { method };
361
+ const headers = {};
362
+ if (body !== undefined) {
363
+ headers['content-type'] = 'application/json; charset=utf-8';
364
+ init.body = JSON.stringify(body);
365
+ }
366
+ const response = await this.request(path, { ...init, headers });
367
+ if (!response.ok)
368
+ throw await this.apiError(response);
369
+ return safeParseJson(response);
370
+ }
371
+ /** 非 2xx → 分类 GitHubApiError:提取 GitHub message + 脱敏(token 形态必被掩码)。 */
372
+ async apiError(response) {
373
+ const status = response.status;
374
+ const bodyText = await safeResponseText(response);
375
+ let ghMessage = '';
376
+ if (bodyText !== '') {
377
+ try {
378
+ const parsed = JSON.parse(bodyText);
379
+ if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
380
+ const m = parsed['message'];
381
+ if (typeof m === 'string' && m !== '')
382
+ ghMessage = m;
383
+ }
384
+ }
385
+ catch {
386
+ // 非 JSON 错误体:只保留状态码
387
+ }
388
+ }
389
+ const rateLimited = response.headers.get('x-ratelimit-remaining') === '0';
390
+ const code = classifyErrorCode(status, rateLimited);
391
+ const detail = ghMessage !== '' ? `:${ghMessage}` : '';
392
+ return new GitHubApiError(redact(`GitHub API 错误(HTTP ${status})${detail}`), code, status);
393
+ }
394
+ }
395
+ /** 解析 JSON 响应体;空体 → undefined;非法 JSON → invalid_response。 */
396
+ async function safeParseJson(response) {
397
+ const text = await safeResponseText(response);
398
+ if (text.trim() === '')
399
+ return undefined;
400
+ try {
401
+ return JSON.parse(text);
402
+ }
403
+ catch {
404
+ throw new GitHubApiError('GitHub API 返回了无法解析的响应,请稍后重试', 'invalid_response', response.status);
405
+ }
406
+ }
407
+ //# sourceMappingURL=github-repos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-repos.js","sourceRoot":"","sources":["../../src/market/github-repos.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import type { GitHubForkInfo, GitHubPullRequestInfo, GitHubPullRequestParams, GitHubRepoInfo, GitHubUserInfo } from './github-repos.ts';
2
+ import type { GitFileWriter } from './git-file-writer.ts';
3
+ import type { MarketPrepareInput, MarketPrepareResult } from './prepare.ts';
4
+ import type { SectionId } from '../schema/types.ts';
5
+ /** 官方收录目标仓库原样复用 t1 常量(github-repos.ts)
6
+ * —— 与内置市场同一仓库,界面不提供任何修改入口(设计 §2.4 产品决策)。 */
7
+ /** 用户公开仓库固定名:<login>/dsh-configs(系统按规则生成,不可配置) */
8
+ export declare const USER_CONFIGS_REPO = "dsh-configs";
9
+ /** 用户仓库 index.json 的名称/描述(展示用) */
10
+ export declare const USER_INDEX_NAME = "\u6211\u7684\u914D\u7F6E\u4ED3\u5E93";
11
+ /** PR 分支固定名模式:dsh-market-sync/<itemId> */
12
+ export declare const PR_BRANCH_PREFIX = "dsh-market-sync";
13
+ /** 由 itemId 生成 PR 分支名(固定模式,同条目复用同一分支:未合并 force push 更新、已合并基于最新 main 重开) */
14
+ export declare function prBranchFor(itemId: string): string;
15
+ /** 用户公开仓库 URL(https 形态,绝不携带凭据) */
16
+ export declare function userConfigsRepoUrl(login: string): string;
17
+ /** 条目收录状态:未收录(本地独有)| PR 待审核(带 PR 链接)| 已收录(官方市场 index 含该 id) */
18
+ export type MyItemStatus = 'not-listed' | 'pr-pending' | 'listed';
19
+ /** 上传表单:仅用户填写的描述性内容;id/author/version/updatedAt/repoUrl 全自动 */
20
+ export interface MyRepoForm {
21
+ /** 配置名(预填 zip 文件名,可改);id 由其派生稳定 slug */
22
+ name: string;
23
+ description?: string;
24
+ categories?: string[];
25
+ }
26
+ /** upload / update 返回(ok=false 时携带已脱敏 error + errorCode) */
27
+ export interface UploadResult {
28
+ ok: boolean;
29
+ itemId: string;
30
+ version: string;
31
+ sha256: string;
32
+ sections: SectionId[];
33
+ /** 用户公开仓库 URL(https://github.com/<login>/dsh-configs) */
34
+ repoUrl: string;
35
+ prNumber: number | null;
36
+ prUrl: string | null;
37
+ warnings: string[];
38
+ /** ok=false 时:可展示错误(已脱敏,无 token 形态) */
39
+ error?: string;
40
+ /** ok=false 时:错误分类码(unauthorized/prepare_failed/...;无则 'internal') */
41
+ errorCode?: string;
42
+ }
43
+ /** listItems 条目:用户仓库 index.json 条目 + 收录状态 */
44
+ export interface MyItemEntry {
45
+ id: string;
46
+ name: string;
47
+ version?: string;
48
+ description?: string;
49
+ author?: string;
50
+ updatedAt?: string;
51
+ categories?: string[];
52
+ status: MyItemStatus;
53
+ /** status=pr-pending 时:PR 链接 */
54
+ prUrl?: string;
55
+ /** 用户公开仓库 URL */
56
+ repoUrl: string;
57
+ }
58
+ /**
59
+ * MyRepoService 需要的 GitHub REST 子集(GitHubAuthRest 结构兼容;测试用普通对象 mock)。
60
+ * 类型用接口而非具体类,便于 plain-object mock 注入。
61
+ */
62
+ export interface GitHubRestLike {
63
+ getUser(): Promise<GitHubUserInfo>;
64
+ repoExists(owner: string, repo: string): Promise<boolean>;
65
+ createPublicRepo(name: string, description?: string): Promise<GitHubRepoInfo>;
66
+ ensureFork(owner: string, repo: string): Promise<GitHubForkInfo>;
67
+ readFile(owner: string, repo: string, path: string, ref?: string): Promise<string | null>;
68
+ openPullRequest(params: GitHubPullRequestParams): Promise<GitHubPullRequestInfo>;
69
+ listOpenPullRequests(owner: string, repo: string, head?: string): Promise<GitHubPullRequestInfo[]>;
70
+ }
71
+ export interface MyRepoServiceOptions {
72
+ /** 8 道校验 + 秘密扫描(复用 prepareMarketItem;测试 mock;抛 MarketPrepareError 视为校验失败) */
73
+ prepare: (input: MarketPrepareInput) => MarketPrepareResult;
74
+ /** GitHub REST 薄客户端(复用 GitHubAuthRest 或 mock) */
75
+ rest: GitHubRestLike;
76
+ /** git 文件写入器(缺省用 tokenProvider 构造真实 GitFileWriterClient;测试注入 mock) */
77
+ gitWriter?: GitFileWriter;
78
+ /** token 提供者(真实 gitWriter 缺省构造用;与 rest/gitWriter 的 token 同源) */
79
+ tokenProvider: () => Promise<string>;
80
+ /** 时钟(updatedAt 生成用);缺省 new Date() */
81
+ now?: () => Date;
82
+ /** 工作副本根目录(缺省 os.tmpdir()/dsh-config-manager-my-configs);测试注入隔离目录 */
83
+ workDirRoot?: string;
84
+ }
85
+ /** MyRepoService 可预期错误(错误码供上层分类) */
86
+ export declare class MyRepoError extends Error {
87
+ readonly code: string;
88
+ constructor(message: string, code?: string);
89
+ }
90
+ /** name → 稳定安全 itemId:小写、非字母数字折叠为 -、字母数字开头、去头尾分隔符;空 → config-<hash> */
91
+ export declare function slugifyItemId(name: string): string;
92
+ /** 冲突自动加后缀(设计:id=name 稳定 slug,同名已存在 → my-config-2 / -3 ...) */
93
+ export declare function uniqueItemId(base: string, existing: readonly string[]): string;
94
+ /** 版本纯自动 +1:最后一个数字段 +1;非数字尾 → 追加 .1 */
95
+ export declare function bumpVersion(version: string): string;
96
+ export declare class MyRepoService {
97
+ private readonly prepare;
98
+ private readonly rest;
99
+ private readonly gitWriter;
100
+ private readonly tokenProvider;
101
+ private readonly now;
102
+ private readonly workDirRoot;
103
+ constructor(options: MyRepoServiceOptions);
104
+ /** 一键上传:新条目(version 1.0.0;同名冲突自动加后缀)。 */
105
+ upload(params: {
106
+ zipBytes: Uint8Array;
107
+ form: MyRepoForm;
108
+ }): Promise<UploadResult>;
109
+ /** 一键更新:id 保持不变、version 纯自动 +1、updatedAt 刷新;名称变化视为新条目。 */
110
+ update(params: {
111
+ zipBytes: Uint8Array;
112
+ form: MyRepoForm;
113
+ }): Promise<UploadResult>;
114
+ /** 列出已上传条目(读用户仓库 index.json)+ 收录状态(官方 index / open PR)。 */
115
+ listItems(): Promise<MyItemEntry[]>;
116
+ private runPublish;
117
+ /** ⑥ 写用户仓库工作副本(隔离方法,便于单测断言 gitWriter 调用) */
118
+ private gitWriterManifestSync;
119
+ /** 读用户仓库最新 index.json;文件不存在(未上传过)→ 空索引;无法解析 → 中止(防覆盖写坏) */
120
+ private readUserIndex;
121
+ /** 读官方市场 index.json(公开仓库 contents API);缺失/无法解析 → 中止 */
122
+ private readOfficialIndex;
123
+ /** prepare 生成的 manifestText 解析回对象(受控字段);异常 → 内部错误 */
124
+ private parsePreparedManifest;
125
+ /** ⑤ ensure 用户仓库:存在 → 返回已知信息;不存在 → createPublicRepo(公开,系统命名) */
126
+ private ensureUserRepo;
127
+ /** ⑨ ensure PR:open PR(head=<login>:<branch>)存在 → 复用;否则 openPullRequest(base=main) */
128
+ private ensurePullRequest;
129
+ private userWorkDir;
130
+ private forkWorkDir;
131
+ }