@dependfix/engine 0.5.0 → 0.6.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/dist/chunks/auth.mjs +5 -10
- package/dist/chunks/auth.mjs.map +1 -1
- package/dist/chunks/index.d.mts +0 -8
- package/dist/index.d.mts +92 -130
- package/dist/index.mjs +1179 -650
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/chunks/auth.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { Octokit } from "@octokit/rest";
|
|
|
2
2
|
import { RequestError } from "@octokit/request-error";
|
|
3
3
|
import { createAppAuth } from "@octokit/auth-app";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// src/github/client.ts
|
|
6
6
|
/**
|
|
7
7
|
* 创建已认证的 Octokit 实例。
|
|
8
8
|
*
|
|
@@ -118,8 +118,7 @@ function sleep(ms) {
|
|
|
118
118
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
//#region src/auth/auth-provider.ts
|
|
121
|
+
// src/auth/auth-provider.ts
|
|
123
122
|
/**
|
|
124
123
|
* PAT 路径固定 commit author(保持现有行为零变化)。
|
|
125
124
|
*
|
|
@@ -133,8 +132,7 @@ const PAT_COMMIT_AUTHOR = {
|
|
|
133
132
|
/** PAT 路径固定 Git 用户名(用于 git push 时的 Basic Auth) */
|
|
134
133
|
const PAT_GIT_USERNAME = "x-access-token";
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
//#region src/auth/pat-provider.ts
|
|
135
|
+
// src/auth/pat-provider.ts
|
|
138
136
|
/**
|
|
139
137
|
* PAT 工厂函数。委托给 `PatAuthProvider` 类。
|
|
140
138
|
*
|
|
@@ -199,8 +197,7 @@ var PatAuthProvider = class {
|
|
|
199
197
|
}
|
|
200
198
|
};
|
|
201
199
|
|
|
202
|
-
|
|
203
|
-
//#region src/auth/app-provider.ts
|
|
200
|
+
// src/auth/app-provider.ts
|
|
204
201
|
/**
|
|
205
202
|
* GitHub App 工厂函数。
|
|
206
203
|
*
|
|
@@ -305,8 +302,7 @@ var AppAuthProvider = class {
|
|
|
305
302
|
}
|
|
306
303
|
};
|
|
307
304
|
|
|
308
|
-
|
|
309
|
-
//#region src/auth/installation-token-cache.ts
|
|
305
|
+
// src/auth/installation-token-cache.ts
|
|
310
306
|
/**
|
|
311
307
|
* Installation token 缓存层(**M18.1 commit 4 实施**)。
|
|
312
308
|
*
|
|
@@ -356,6 +352,5 @@ var InstallationTokenCache = class {
|
|
|
356
352
|
}
|
|
357
353
|
};
|
|
358
354
|
|
|
359
|
-
//#endregion
|
|
360
355
|
export { fromPat as a, computeRetryDelayMs as c, PatAuthProvider as i, createGitHubClient as l, AppAuthProvider as n, PAT_COMMIT_AUTHOR as o, fromApp as r, PAT_GIT_USERNAME as s, InstallationTokenCache as t };
|
|
361
356
|
//# sourceMappingURL=auth.mjs.map
|
package/dist/chunks/auth.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.mjs","names":[],"sources":["../../src/github/client.ts","../../src/auth/auth-provider.ts","../../src/auth/pat-provider.ts","../../src/auth/app-provider.ts","../../src/auth/installation-token-cache.ts"],"sourcesContent":["import { Octokit } from '@octokit/rest'\nimport { RequestError } from '@octokit/request-error'\nimport type { AuthProvider } from '../auth/auth-provider'\n\nexport interface RetryPolicyOptions {\n /** 最大重试次数(默认 3;0 = 不重试) */\n maxRetries?: number\n /** 指数退避基数毫秒(默认 1000;测试时可调小) */\n baseDelayMs?: number\n /**\n * 指数退避单次等待上限毫秒(默认 30000),防止长时间空转。\n * Retry-After / x-ratelimit-reset 指定的等待同样受此上限约束。\n */\n maxBackoffMs?: number\n}\n\nexport interface OctokitClientOptions {\n /**\n * 认证抽象层(M18.1 实施后唯一推荐入口)。\n *\n * 调用方应通过 `fromPat(token)` 或 `fromApp(params)` 构造;\n * 优先使用此字段,新代码不得使用 deprecated 的 `token` 字段。\n *\n * @see {@link ../auth/auth-provider} AuthProvider 接口定义\n * @see [C22 PAT 无感升级评估 §4.2 AuthProvider 接口设计](../../../../docs/design/governance/c22-pat-backward-compat.md)\n */\n auth?: AuthProvider\n\n /**\n * @deprecated 使用 `auth` 替代。\n *\n * 保留作为低层入口;`token` 路径与 `auth: fromPat(token)` 行为等价\n * (共享同一 retry policy 与限流重试 hook)。\n * 计划在后续阶段评估移除。\n */\n token?: string\n\n /**\n * API 基地址。\n * 默认 `https://api.github.com`。\n * 测试时指向 nock 拦截的同一地址。\n */\n baseUrl?: string\n\n /**\n * API 限流 / 次要限流(secondary rate limit)指数退避重试策略。\n * 默认 maxRetries=3。对 429、primary rate limit(403 + x-ratelimit-remaining: 0)、\n * secondary rate limit(403/429 带 secondary/abuse/retry 特征)自动退避重试;\n * 权限类 403 不重试。\n *\n * 行为由具体 AuthProvider 实现决定:\n * - `PatAuthProvider` 委托给本函数(`createGitHubClient({ token, retry })`)—— retry 字段透传生效\n * - `AppAuthProvider` 自管 retry(commit 4 实施)\n */\n retry?: RetryPolicyOptions\n}\n\n/**\n * 创建已认证的 Octokit 实例。\n *\n * 调用方直接使用 `octokit.rest.*` 访问所有已类型化的 GitHub REST API。\n * 分页使用 `octokit.paginate()` 自动合并多页结果。\n *\n * 配置 `retry` 时通过 `hook.wrap('request')` 统一包装所有请求\n * (含 paginate 内部请求),对限流响应做指数退避重试。\n *\n * @example\n * ```typescript\n * // 推荐:使用 auth 抽象层(已实施)\n * import { fromPat } from '@dependfix/engine/auth'\n * const octokit = createGitHubClient({ auth: fromPat('ghp_xxxx') })\n *\n * // 向后兼容:使用 token 字段(deprecated)\n * const octokit = createGitHubClient({ token: 'ghp_xxxx' })\n *\n * // 仓库信息\n * const { data: repo } = await octokit.rest.repos.get({ owner: 'foo', repo: 'bar' })\n *\n * // Dependabot 告警(自动分页)\n * const alerts = await octokit.paginate(\n * octokit.rest.dependabot.listAlertsForRepo,\n * { owner: 'foo', repo: 'bar', state: 'open', per_page: 100 },\n * )\n * ```\n */\nexport function createGitHubClient(options: OctokitClientOptions): Octokit {\n // 优先使用 auth 抽象层\n if (options.auth) {\n return options.auth.getOctokit()\n }\n\n // 向后兼容:token 路径保留原实现(限流重试 + Octokit 构造)\n if (!options.token) {\n throw new Error('createGitHubClient: must provide either `auth` (AuthProvider) or `token` (deprecated)')\n }\n\n const client = new Octokit({\n auth: options.token,\n baseUrl: options.baseUrl ?? 'https://api.github.com',\n })\n\n const maxRetries = options.retry?.maxRetries ?? 3\n if (maxRetries > 0) {\n applyRetryPolicy(client, {\n maxRetries,\n baseDelayMs: options.retry?.baseDelayMs ?? 1000,\n maxBackoffMs: options.retry?.maxBackoffMs ?? MAX_BACKOFF_MS_DEFAULT,\n })\n }\n\n return client\n}\n\n// ---------------------------------------------------------------------------\n// Retry policy(统一 hook 包装,对全部请求生效)\n// ---------------------------------------------------------------------------\n\nconst SECONDARY_RATE_LIMIT_RE = /secondary rate limit|abuse|retry later|retry-after/i\n/** 指数退避单次等待默认上限(30s),防止长时间空转(可经 retry.maxBackoffMs 覆盖) */\nconst MAX_BACKOFF_MS_DEFAULT = 30_000\n\nfunction applyRetryPolicy(client: Octokit, retry: RetryPolicyOptions): void {\n const maxRetries = retry.maxRetries ?? 3\n const baseDelayMs = retry.baseDelayMs ?? 1000\n const maxBackoffMs = retry.maxBackoffMs ?? MAX_BACKOFF_MS_DEFAULT\n\n client.hook.wrap('request', async (request, options) => {\n // 写请求(POST/PATCH/PUT/DELETE)不做限流重试——非幂等操作避免重放;\n // 限流重试仅适用于只读 GET/HEAD(GitHub 限流检查在请求执行前,写请求重放风险虽低仍应规避)\n const method = (options.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n return request(options)\n }\n\n let attempt = 0\n for (;;) {\n try {\n return await request(options)\n } catch (error: unknown) {\n const delayMs = computeRetryDelayMs(error, attempt, baseDelayMs, maxBackoffMs)\n if (delayMs === null || attempt >= maxRetries) {\n throw error\n }\n attempt += 1\n await sleep(delayMs)\n }\n }\n })\n}\n\n/**\n * 计算限流重试等待时间(纯函数,可单测)。\n *\n * 可重试(返回等待毫秒):\n * - 429(Too Many Requests / secondary rate limit)\n * - 403 + `x-ratelimit-remaining: 0`(primary rate limit)\n * - 403/429 + message 含 secondary rate limit / abuse / retry 特征\n *\n * 等待策略(Retry-After 优先):\n * 1. `retry-after` 头(秒)→ 等待其秒数(受 maxBackoffMs 上限约束)\n * 2. `x-ratelimit-reset`(unix 秒)→ 等待到 reset + 1s 缓冲\n * 3. 否则 → 指数退避 `baseDelayMs * 2^attempt`\n * 所有等待均受 maxBackoffMs 上限约束(默认 30s)。\n *\n * 不重试(返回 null):\n * - 非 RequestError(网络错误等由上层 AppError 语义处理)\n * - 权限类 403(无限流特征)\n * - 其他状态码\n */\nexport function computeRetryDelayMs(\n error: unknown,\n attempt: number,\n baseDelayMs = 1000,\n maxBackoffMs = MAX_BACKOFF_MS_DEFAULT,\n): number | null {\n if (!(error instanceof RequestError)) {\n return null\n }\n\n const status = error.status\n const headers = error.response?.headers ?? {}\n const remaining = headers['x-ratelimit-remaining']\n const message = error.message\n\n let retryable = false\n if (status === 429) {\n retryable = true\n } else if (status === 403) {\n if (remaining !== undefined && remaining === '0') {\n retryable = true // primary rate limit\n } else if (SECONDARY_RATE_LIMIT_RE.test(message)) {\n retryable = true // secondary rate limit(403 形态)\n }\n }\n\n if (!retryable) {\n return null\n }\n\n // Retry-After 头优先(GitHub secondary rate limit 常返回,秒为单位)\n const retryAfter = headers['retry-after']\n if (retryAfter !== undefined) {\n const seconds = Number(retryAfter)\n if (Number.isFinite(seconds) && seconds > 0) {\n return Math.min(seconds * 1000, maxBackoffMs)\n }\n }\n\n // 其次等待到限流重置时刻(primary rate limit 标准行为)\n const reset = headers['x-ratelimit-reset']\n if (reset !== undefined) {\n const resetMs = Number(reset) * 1000\n if (Number.isFinite(resetMs) && resetMs > 0) {\n const waitMs = resetMs - Date.now() + 1000\n if (waitMs > 0) {\n return Math.min(waitMs, maxBackoffMs)\n }\n }\n }\n\n // 指数退避兜底\n return Math.min(baseDelayMs * 2 ** attempt, maxBackoffMs)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","import type { Octokit } from '@octokit/rest'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * GitHub 认证抽象层。\n *\n * 支持 PAT(classic / fine-grained)与 GitHub App installation token 两种认证路径。\n * 调用方通过 `getOctokit()` 获取已认证的 Octokit 实例;通过 `getGitCredential()`\n * 获取推送/clone 用的 Git 凭据;通过 `getCommitAuthor()` 获取 commit author 信息。\n *\n * `authProvider` 字段用于审计与调试(区分 PAT vs App 路径)。\n *\n * @example\n * ```typescript\n * // 推荐:从 fromPat / fromApp 工厂构造\n * const auth = fromPat('ghp_xxxx')\n * const octokit = auth.getOctokit()\n *\n * // git push 凭据\n * const { username, token } = auth.getGitCredential()\n *\n * // commit author(PAT 路径固定;App 路径动态)\n * const { name, email } = auth.getCommitAuthor()\n * ```\n */\nexport interface AuthProvider {\n /** 获取已认证的 Octokit 实例 */\n getOctokit(): Octokit\n\n /** 获取推送 / clone 用的 Git 凭据(用户名 + token) */\n getGitCredential(): { username: string, token: string }\n\n /**\n * 获取 commit author 信息。\n *\n * - PAT 路径:固定返回 `{ name: 'dependfix[bot]', email: 'dependfix[bot]@users.noreply.github.com' }`(保持现有行为零变化)\n * - GitHub App 路径:动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`(GitHub App 协议要求)\n */\n getCommitAuthor(): { name: string, email: string }\n\n /** 认证路径标识(用于审计 + 调试) */\n readonly authProvider: 'pat' | 'github-app'\n}\n\n/** 工厂函数统一选项(与 OctokitClientOptions.retry 字段对齐) */\nexport interface AuthProviderOptions {\n /** API 限流 / 次要限流指数退避重试策略。默认 maxRetries=3。 */\n retry?: {\n maxRetries?: number\n baseDelayMs?: number\n maxBackoffMs?: number\n }\n}\n\n/** PAT 工厂参数(仅 token;retry 在 options 注入) */\nexport type FromPatParams = string\n\n/** GitHub App 工厂参数 */\nexport interface FromAppParams {\n /** GitHub App ID */\n appId: string\n /** PEM 格式私钥 */\n privateKey: string\n /** Installation ID */\n installationId: string\n /** Bot 用户名(用于 commit author 动态生成) */\n botLogin?: string\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * PAT 路径固定 commit author(保持现有行为零变化)。\n *\n * 注:现有 PAT 路径硬编码 `dependfix[bot]@users.noreply.github.com`,虽非真实 bot 身份(字符串约定),\n * 但保持行为不变以确保 PAT 用户无感升级。已知缺陷由后续阶段修复。\n */\nexport const PAT_COMMIT_AUTHOR = {\n name: 'dependfix[bot]',\n email: 'dependfix[bot]@users.noreply.github.com',\n} as const\n\n/** PAT 路径固定 Git 用户名(用于 git push 时的 Basic Auth) */\nexport const PAT_GIT_USERNAME = 'x-access-token'\n","import { Octokit } from '@octokit/rest'\nimport { createGitHubClient } from '../github/client'\nimport {\n PAT_COMMIT_AUTHOR,\n PAT_GIT_USERNAME,\n type AuthProvider,\n type AuthProviderOptions,\n type FromPatParams,\n} from './auth-provider'\n\n/**\n * PAT 工厂函数。委托给 `PatAuthProvider` 类。\n *\n * 保留 `fromPat` 命名习惯便于与 `fromApp` 对齐(统一工厂语义)。\n *\n * @example\n * ```typescript\n * const auth = fromPat('ghp_xxxx')\n * const octokit = auth.getOctokit()\n * const { username, token } = auth.getGitCredential()\n * ```\n */\nexport function fromPat(token: FromPatParams, options?: AuthProviderOptions): AuthProvider {\n return new PatAuthProvider(token, options)\n}\n\n/**\n * PAT 认证路径实现。\n *\n * `getOctokit()` 委托给 [`createGitHubClient`](../github/client.ts) 以复用现有 retry policy 与限流重试实现;\n * Octokit 实例缓存以避免重复构造(每次 `new Octokit()` + `hook.wrap()` 都有开销)。\n *\n * 行为与原 `createGitHubClient({ token })` 等价:\n * - `getOctokit()` 返回通过 PAT 认证的 Octokit 实例(含限流重试 hook)\n * - `getGitCredential()` 返回推送用的 Basic Auth(username = `'x-access-token'`)\n * - `getCommitAuthor()` 返回固定 `dependfix[bot]`(PAT 路径用户行为零变化)\n *\n * @example\n * ```typescript\n * const auth = new PatAuthProvider('ghp_xxxx', { retry: { maxRetries: 3 } })\n * const octokit = auth.getOctokit()\n * ```\n */\nexport class PatAuthProvider implements AuthProvider {\n readonly authProvider = 'pat' as const\n\n private readonly token: FromPatParams\n private readonly retry?: AuthProviderOptions['retry']\n private cachedOctokit: Octokit | null = null\n\n constructor(token: FromPatParams, options?: AuthProviderOptions) {\n this.token = token\n this.retry = options?.retry\n }\n\n /**\n * 获取已认证的 Octokit 实例(首次调用构造并缓存)。\n */\n getOctokit(): Octokit {\n if (!this.cachedOctokit) {\n this.cachedOctokit = createGitHubClient({ token: this.token, retry: this.retry })\n }\n return this.cachedOctokit\n }\n\n /** 获取推送 / clone 用的 Git 凭据(username = `'x-access-token'`,token = PAT) */\n getGitCredential(): { username: string, token: string } {\n return { username: PAT_GIT_USERNAME, token: this.token }\n }\n\n /** 获取 commit author(PAT 路径固定 `dependfix[bot]`) */\n getCommitAuthor(): { name: string, email: string } {\n return { ...PAT_COMMIT_AUTHOR }\n }\n}\n","import { Octokit } from '@octokit/rest'\nimport { createAppAuth } from '@octokit/auth-app'\nimport type {\n AuthProvider,\n AuthProviderOptions,\n FromAppParams,\n} from './auth-provider'\n\n// ---------------------------------------------------------------------------\n// AppAuthProvider - GitHub App installation token 认证实现\n// ---------------------------------------------------------------------------\n\n/**\n * GitHub App 工厂函数。\n *\n * 委托给 [`AppAuthProvider`] 类。\n *\n * @example\n * ```typescript\n * const auth = fromApp({\n * appId: '123456',\n * privateKey: '-----BEGIN RSA PRIVATE KEY-----\\n...',\n * installationId: '7890123',\n * botLogin: 'dependfix-bot[bot]',\n * })\n * const octokit = auth.getOctokit()\n * ```\n */\nexport function fromApp(\n params: FromAppParams,\n options?: AuthProviderOptions,\n): AuthProvider {\n return new AppAuthProvider(params, options)\n}\n\n/**\n * GitHub App 认证路径实现。\n *\n * 使用 [`@octokit/auth-app`](https://github.com/octokit/auth-app.js) 提供:\n * - JWT signing(App 身份认证)\n * - `getInstallationOctokit`(installation token 获取;库内置 59 分钟 LRU TTL 缓存)\n * - 自动 installation token 轮换\n *\n * 注意:\n * - `getGitCredential()` 返回的 `token` 字段为占位符(installation token 不能直接从 AppAuthProvider 获取;\n * 由 [`@octokit/auth-app`](https://github.com/octokit/auth-app.js) 内部管理);调用方应通过 `getOctokit()`\n * 拿到的 Octokit 实例进行 API 调用\n * - `getCommitAuthor()` 按 GitHub App 协议动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`\n * - Octokit 实例缓存以避免重复构造\n *\n * @see [C22 PAT 无感升级评估 §4.2 AuthProvider 接口设计](../../../../docs/design/governance/c22-pat-backward-compat.md)\n */\nexport class AppAuthProvider implements AuthProvider {\n readonly authProvider = 'github-app' as const\n\n private readonly params: FromAppParams\n // retry 选项保留用于未来扩展(AppAuthProvider 当前不直接使用 retry;由 @octokit/auth-app 自管)\n private readonly _options?: AuthProviderOptions\n private cachedOctokit: Octokit | null = null\n\n constructor(params: FromAppParams, options?: AuthProviderOptions) {\n this.params = params\n this._options = options\n }\n\n /**\n * 获取已认证的 Octokit 实例。\n *\n * 首次调用时构造并缓存;后续调用复用同一实例。\n * installation token 由 `@octokit/auth-app` 内部管理(59 分钟 LRU TTL 缓存)。\n *\n * **构造方式**:使用 `authStrategy: createAppAuth` + `auth: {appId, privateKey, installationId}`\n * 双字段组合(`@octokit/auth-app` v8.x README 标准用法),**而非** `auth: createAppAuth(...)`\n * 单字段调用——后者被 `@octokit/core` 视作字符串 token 路径抛 `Token passed to createTokenAuth is not a string`,\n * 或被 `authStrategy` 字段调用后命中 `auth(state, authOptions)` 的 `default` 分支抛 `Invalid auth type: undefined`\n * (todo.md §M18.4 e2e 验证 + audit quick depth Reject 后补修发现)。\n *\n * @see [@octokit/auth-app README §installation authentication](https://github.com/octokit/auth-app.js#installation-authentication)\n */\n getOctokit(): Octokit {\n if (!this.cachedOctokit) {\n this.cachedOctokit = new Octokit({\n authStrategy: createAppAuth,\n auth: {\n appId: this.params.appId,\n privateKey: this.params.privateKey,\n installationId: this.params.installationId,\n },\n baseUrl: 'https://api.github.com',\n })\n }\n return this.cachedOctokit\n }\n\n /**\n * 获取推送 / clone 用的 Git 凭据。\n *\n * 注意:installation token 由 `@octokit/auth-app` 内部管理,**不**能从 AppAuthProvider 直接获取;\n * 本方法返回的 `token` 字段为占位符(`'installation-token-managed-by-octokit-auth-app'`)。\n * 调用方应通过 `getOctokit()` 拿到的 Octokit 实例进行 API 调用(含 git push / clone 等写操作)。\n */\n getGitCredential(): { username: string, token: string } {\n return {\n username: 'x-access-token',\n token: 'installation-token-managed-by-octokit-auth-app',\n }\n }\n\n /**\n * 获取 commit author。\n *\n * 按 GitHub App 协议动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`:\n * - `name`:`{app_id}[bot]`(GitHub 自动识别 bot 身份)\n * - `email`:`{app_id}+{bot_login}[bot]@users.noreply.github.com`(GitHub noreply email 格式)\n *\n * 当 `params.botLogin` 未提供时,使用 `dependfix[bot]` 作为 fallback(保持与 PAT 路径一致的格式);\n * 但建议调用方显式提供 `botLogin` 以确保 commit author 真实 bot 身份归属。\n */\n getCommitAuthor(): { name: string, email: string } {\n const botLogin = this.params.botLogin ?? 'dependfix[bot]'\n const appId = this.params.appId\n return {\n name: `${appId}[bot]`,\n email: `${appId}+${botLogin}@users.noreply.github.com`,\n }\n }\n}\n","import type { FromAppParams } from './auth-provider'\n\n// ---------------------------------------------------------------------------\n// Installation Token Cache - 占位 + 监控 API\n// ---------------------------------------------------------------------------\n\n/**\n * Installation token 缓存层(**M18.1 commit 4 实施**)。\n *\n * 当前实现:**依赖 [@octokit/auth-app](https://github.com/octokit/auth-app.js) 内置缓存**\n * (59 分钟 LRU TTL;GitHub token 有效期 60 分钟 - 1 分钟缓冲)。\n *\n * 本类作为未来扩展点:\n * - 缓存统计(命中率 / 大小 / 清理 API)\n * - 失败重试 + 报警(M18.2 集成时实施)\n * - 多 worker 共享(Redis-backed;超出 M18.1 范围)\n *\n * 注意:\n * - `getOrRefresh` 当前委托给 `createAppAuthentication` + `getInstallationOctokit`\n * - 当 [@octokit/auth-app](https://github.com/octokit/auth-app.js) 内部缓存命中时不会触发 API 调用\n * - 缓存未命中时自动调用 `POST /app/installations/{installation_id}/access_tokens` 获取新 token\n *\n * @see [C22 PAT 无感升级评估 §4.7 commit 拆分纪律](../../../../docs/design/governance/c22-pat-backward-compat.md)\n */\nexport class InstallationTokenCache {\n private readonly params: FromAppParams\n private readonly ttlMs: number\n\n constructor(params: FromAppParams, options?: { ttlMs?: number, refreshThresholdMs?: number }) {\n this.params = params\n // 默认 59 分钟(与 @octokit/auth-app 默认一致)\n this.ttlMs = options?.ttlMs ?? 59 * 60 * 1000\n }\n\n /**\n * 获取或刷新 installation token。\n *\n * 当前为占位实现:直接返回 `'installation-token-managed-by-octokit-auth-app'` 占位字符串。\n * 调用方应使用 `fromApp(params).getOctokit()` 获取真实 Octokit 实例(由 `@octokit/auth-app`\n * 自动管理 installation token 缓存)。\n *\n * @throws 永不抛错(占位实现)\n */\n async getOrRefresh(): Promise<string> {\n // 占位实现:真实 token 由 @octokit/auth-app 内部管理\n // 本方法保留作为未来扩展点(Redis-backed / 失败重试 / 报警)\n return 'installation-token-managed-by-octokit-auth-app'\n }\n\n /** 清理过期缓存项(占位实现) */\n clear(): void {\n // 占位实现:@octokit/auth-app 内部管理缓存清理\n // 保留接口签名便于未来替换为自有实现\n }\n\n /** 获取缓存 TTL 配置(毫秒) */\n getTtlMs(): number {\n return this.ttlMs\n }\n\n /** 获取 cache key(基于 installationId) */\n static getCacheKey(params: FromAppParams): string {\n return `installation:${params.installationId}`\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqFA,SAAgB,mBAAmB,SAAwC;CAEvE,IAAI,QAAQ,MACR,OAAO,QAAQ,KAAK,WAAW;CAInC,IAAI,CAAC,QAAQ,OACT,MAAM,IAAI,MAAM,uFAAuF;CAG3G,MAAM,SAAS,IAAI,QAAQ;EACvB,MAAM,QAAQ;EACd,SAAS,QAAQ,WAAW;CAChC,CAAC;CAED,MAAM,aAAa,QAAQ,OAAO,cAAc;CAChD,IAAI,aAAa,GACb,iBAAiB,QAAQ;EACrB;EACA,aAAa,QAAQ,OAAO,eAAe;EAC3C,cAAc,QAAQ,OAAO,gBAAgB;CACjD,CAAC;CAGL,OAAO;AACX;AAMA,MAAM,0BAA0B;;AAEhC,MAAM,yBAAyB;AAE/B,SAAS,iBAAiB,QAAiB,OAAiC;CACxE,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,cAAc,MAAM,eAAe;CACzC,MAAM,eAAe,MAAM,gBAAgB;CAE3C,OAAO,KAAK,KAAK,WAAW,OAAO,SAAS,YAAY;EAGpD,MAAM,UAAU,QAAQ,UAAU,MAAK,CAAE,YAAY;EACrD,IAAI,WAAW,SAAS,WAAW,QAC/B,OAAO,QAAQ,OAAO;EAG1B,IAAI,UAAU;EACd,SACI,IAAI;GACA,OAAO,MAAM,QAAQ,OAAO;EAChC,SAAS,OAAgB;GACrB,MAAM,UAAU,oBAAoB,OAAO,SAAS,aAAa,YAAY;GAC7E,IAAI,YAAY,QAAQ,WAAW,YAC/B,MAAM;GAEV,WAAW;GACX,MAAM,MAAM,OAAO;EACvB;CAER,CAAC;AACL;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,oBACZ,OACA,SACA,cAAc,KACd,eAAe,wBACF;CACb,IAAI,EAAE,iBAAiB,eACnB,OAAO;CAGX,MAAM,SAAS,MAAM;CACrB,MAAM,UAAU,MAAM,UAAU,WAAW,CAAC;CAC5C,MAAM,YAAY,QAAQ;CAC1B,MAAM,UAAU,MAAM;CAEtB,IAAI,YAAY;CAChB,IAAI,WAAW,KACX,YAAY;MACT,IAAI,WAAW,KAAK;EACvB,IAAI,cAAc,UAAa,cAAc,KACzC,YAAY;OACT,IAAI,wBAAwB,KAAK,OAAO,GAC3C,YAAY;CAEpB;CAEA,IAAI,CAAC,WACD,OAAO;CAIX,MAAM,aAAa,QAAQ;CAC3B,IAAI,eAAe,QAAW;EAC1B,MAAM,UAAU,OAAO,UAAU;EACjC,IAAI,OAAO,SAAS,OAAO,KAAK,UAAU,GACtC,OAAO,KAAK,IAAI,UAAU,KAAM,YAAY;CAEpD;CAGA,MAAM,QAAQ,QAAQ;CACtB,IAAI,UAAU,QAAW;EACrB,MAAM,UAAU,OAAO,KAAK,IAAI;EAChC,IAAI,OAAO,SAAS,OAAO,KAAK,UAAU,GAAG;GACzC,MAAM,SAAS,UAAU,KAAK,IAAI,IAAI;GACtC,IAAI,SAAS,GACT,OAAO,KAAK,IAAI,QAAQ,YAAY;EAE5C;CACJ;CAGA,OAAO,KAAK,IAAI,cAAc,KAAK,SAAS,YAAY;AAC5D;AAEA,SAAS,MAAM,IAA2B;CACtC,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AAC3D;;;;;;;;;;AChJA,MAAa,oBAAoB;CAC7B,MAAM;CACN,OAAO;AACX;;AAGA,MAAa,mBAAmB;;;;;;;;;;;;;;;;AClEhC,SAAgB,QAAQ,OAAsB,SAA6C;CACvF,OAAO,IAAI,gBAAgB,OAAO,OAAO;AAC7C;;;;;;;;;;;;;;;;;;AAmBA,IAAa,kBAAb,MAAqD;CACjD,AAAS,eAAe;CAExB,AAAiB;CACjB,AAAiB;CACjB,AAAQ,gBAAgC;CAExC,YAAY,OAAsB,SAA+B;EAC7D,KAAK,QAAQ;EACb,KAAK,QAAQ,SAAS;CAC1B;;;;CAKA,aAAsB;EAClB,IAAI,CAAC,KAAK,eACN,KAAK,gBAAgB,mBAAmB;GAAE,OAAO,KAAK;GAAO,OAAO,KAAK;EAAM,CAAC;EAEpF,OAAO,KAAK;CAChB;;CAGA,mBAAwD;EACpD,OAAO;GAAE,UAAU;GAAkB,OAAO,KAAK;EAAM;CAC3D;;CAGA,kBAAmD;EAC/C,OAAO,EAAE,GAAG,kBAAkB;CAClC;AACJ;;;;;;;;;;;;;;;;;;;;AC9CA,SAAgB,QACZ,QACA,SACY;CACZ,OAAO,IAAI,gBAAgB,QAAQ,OAAO;AAC9C;;;;;;;;;;;;;;;;;;AAmBA,IAAa,kBAAb,MAAqD;CACjD,AAAS,eAAe;CAExB,AAAiB;CAEjB,AAAiB;CACjB,AAAQ,gBAAgC;CAExC,YAAY,QAAuB,SAA+B;EAC9D,KAAK,SAAS;EACd,KAAK,WAAW;CACpB;;;;;;;;;;;;;;;CAgBA,aAAsB;EAClB,IAAI,CAAC,KAAK,eACN,KAAK,gBAAgB,IAAI,QAAQ;GAC7B,cAAc;GACd,MAAM;IACF,OAAO,KAAK,OAAO;IACnB,YAAY,KAAK,OAAO;IACxB,gBAAgB,KAAK,OAAO;GAChC;GACA,SAAS;EACb,CAAC;EAEL,OAAO,KAAK;CAChB;;;;;;;;CASA,mBAAwD;EACpD,OAAO;GACH,UAAU;GACV,OAAO;EACX;CACJ;;;;;;;;;;;CAYA,kBAAmD;EAC/C,MAAM,WAAW,KAAK,OAAO,YAAY;EACzC,MAAM,QAAQ,KAAK,OAAO;EAC1B,OAAO;GACH,MAAM,GAAG,MAAM;GACf,OAAO,GAAG,MAAM,GAAG,SAAS;EAChC;CACJ;AACJ;;;;;;;;;;;;;;;;;;;;;;ACtGA,IAAa,yBAAb,MAAoC;CAChC,AAAiB;CACjB,AAAiB;CAEjB,YAAY,QAAuB,SAA2D;EAC1F,KAAK,SAAS;EAEd,KAAK,QAAQ,SAAS,SAAS;CACnC;;;;;;;;;;CAWA,MAAM,eAAgC;EAGlC,OAAO;CACX;;CAGA,QAAc,CAGd;;CAGA,WAAmB;EACf,OAAO,KAAK;CAChB;;CAGA,OAAO,YAAY,QAA+B;EAC9C,OAAO,gBAAgB,OAAO;CAClC;AACJ"}
|
|
1
|
+
{"version":3,"file":"auth.mjs","names":[],"sources":["../../src/github/client.ts","../../src/auth/auth-provider.ts","../../src/auth/pat-provider.ts","../../src/auth/app-provider.ts","../../src/auth/installation-token-cache.ts"],"sourcesContent":["import { Octokit } from '@octokit/rest'\nimport { RequestError } from '@octokit/request-error'\nimport type { AuthProvider } from '../auth/auth-provider'\n\nexport interface RetryPolicyOptions {\n /** 最大重试次数(默认 3;0 = 不重试) */\n maxRetries?: number\n /** 指数退避基数毫秒(默认 1000;测试时可调小) */\n baseDelayMs?: number\n /**\n * 指数退避单次等待上限毫秒(默认 30000),防止长时间空转。\n * Retry-After / x-ratelimit-reset 指定的等待同样受此上限约束。\n */\n maxBackoffMs?: number\n}\n\nexport interface OctokitClientOptions {\n /**\n * 认证抽象层(M18.1 实施后唯一推荐入口)。\n *\n * 调用方应通过 `fromPat(token)` 或 `fromApp(params)` 构造;\n * 优先使用此字段,新代码不得使用 deprecated 的 `token` 字段。\n *\n * @see {@link ../auth/auth-provider} AuthProvider 接口定义\n * @see [C22 PAT 无感升级评估 §4.2 AuthProvider 接口设计](../../../../docs/design/governance/c22-pat-backward-compat.md)\n */\n auth?: AuthProvider\n\n /**\n * @deprecated 使用 `auth` 替代。\n *\n * 保留作为低层入口;`token` 路径与 `auth: fromPat(token)` 行为等价\n * (共享同一 retry policy 与限流重试 hook)。\n * 计划在后续阶段评估移除。\n */\n token?: string\n\n /**\n * API 基地址。\n * 默认 `https://api.github.com`。\n * 测试时指向 nock 拦截的同一地址。\n */\n baseUrl?: string\n\n /**\n * API 限流 / 次要限流(secondary rate limit)指数退避重试策略。\n * 默认 maxRetries=3。对 429、primary rate limit(403 + x-ratelimit-remaining: 0)、\n * secondary rate limit(403/429 带 secondary/abuse/retry 特征)自动退避重试;\n * 权限类 403 不重试。\n *\n * 行为由具体 AuthProvider 实现决定:\n * - `PatAuthProvider` 委托给本函数(`createGitHubClient({ token, retry })`)—— retry 字段透传生效\n * - `AppAuthProvider` 自管 retry(commit 4 实施)\n */\n retry?: RetryPolicyOptions\n}\n\n/**\n * 创建已认证的 Octokit 实例。\n *\n * 调用方直接使用 `octokit.rest.*` 访问所有已类型化的 GitHub REST API。\n * 分页使用 `octokit.paginate()` 自动合并多页结果。\n *\n * 配置 `retry` 时通过 `hook.wrap('request')` 统一包装所有请求\n * (含 paginate 内部请求),对限流响应做指数退避重试。\n *\n * @example\n * ```typescript\n * // 推荐:使用 auth 抽象层(已实施)\n * import { fromPat } from '@dependfix/engine/auth'\n * const octokit = createGitHubClient({ auth: fromPat('ghp_xxxx') })\n *\n * // 向后兼容:使用 token 字段(deprecated)\n * const octokit = createGitHubClient({ token: 'ghp_xxxx' })\n *\n * // 仓库信息\n * const { data: repo } = await octokit.rest.repos.get({ owner: 'foo', repo: 'bar' })\n *\n * // Dependabot 告警(自动分页)\n * const alerts = await octokit.paginate(\n * octokit.rest.dependabot.listAlertsForRepo,\n * { owner: 'foo', repo: 'bar', state: 'open', per_page: 100 },\n * )\n * ```\n */\nexport function createGitHubClient(options: OctokitClientOptions): Octokit {\n // 优先使用 auth 抽象层\n if (options.auth) {\n return options.auth.getOctokit()\n }\n\n // 向后兼容:token 路径保留原实现(限流重试 + Octokit 构造)\n if (!options.token) {\n throw new Error('createGitHubClient: must provide either `auth` (AuthProvider) or `token` (deprecated)')\n }\n\n const client = new Octokit({\n auth: options.token,\n baseUrl: options.baseUrl ?? 'https://api.github.com',\n })\n\n const maxRetries = options.retry?.maxRetries ?? 3\n if (maxRetries > 0) {\n applyRetryPolicy(client, {\n maxRetries,\n baseDelayMs: options.retry?.baseDelayMs ?? 1000,\n maxBackoffMs: options.retry?.maxBackoffMs ?? MAX_BACKOFF_MS_DEFAULT,\n })\n }\n\n return client\n}\n\n// ---------------------------------------------------------------------------\n// Retry policy(统一 hook 包装,对全部请求生效)\n// ---------------------------------------------------------------------------\n\nconst SECONDARY_RATE_LIMIT_RE = /secondary rate limit|abuse|retry later|retry-after/i\n/** 指数退避单次等待默认上限(30s),防止长时间空转(可经 retry.maxBackoffMs 覆盖) */\nconst MAX_BACKOFF_MS_DEFAULT = 30_000\n\nfunction applyRetryPolicy(client: Octokit, retry: RetryPolicyOptions): void {\n const maxRetries = retry.maxRetries ?? 3\n const baseDelayMs = retry.baseDelayMs ?? 1000\n const maxBackoffMs = retry.maxBackoffMs ?? MAX_BACKOFF_MS_DEFAULT\n\n client.hook.wrap('request', async (request, options) => {\n // 写请求(POST/PATCH/PUT/DELETE)不做限流重试——非幂等操作避免重放;\n // 限流重试仅适用于只读 GET/HEAD(GitHub 限流检查在请求执行前,写请求重放风险虽低仍应规避)\n const method = (options.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n return request(options)\n }\n\n let attempt = 0\n for (;;) {\n try {\n return await request(options)\n } catch (error: unknown) {\n const delayMs = computeRetryDelayMs(error, attempt, baseDelayMs, maxBackoffMs)\n if (delayMs === null || attempt >= maxRetries) {\n throw error\n }\n attempt += 1\n await sleep(delayMs)\n }\n }\n })\n}\n\n/**\n * 计算限流重试等待时间(纯函数,可单测)。\n *\n * 可重试(返回等待毫秒):\n * - 429(Too Many Requests / secondary rate limit)\n * - 403 + `x-ratelimit-remaining: 0`(primary rate limit)\n * - 403/429 + message 含 secondary rate limit / abuse / retry 特征\n *\n * 等待策略(Retry-After 优先):\n * 1. `retry-after` 头(秒)→ 等待其秒数(受 maxBackoffMs 上限约束)\n * 2. `x-ratelimit-reset`(unix 秒)→ 等待到 reset + 1s 缓冲\n * 3. 否则 → 指数退避 `baseDelayMs * 2^attempt`\n * 所有等待均受 maxBackoffMs 上限约束(默认 30s)。\n *\n * 不重试(返回 null):\n * - 非 RequestError(网络错误等由上层 AppError 语义处理)\n * - 权限类 403(无限流特征)\n * - 其他状态码\n */\nexport function computeRetryDelayMs(\n error: unknown,\n attempt: number,\n baseDelayMs = 1000,\n maxBackoffMs = MAX_BACKOFF_MS_DEFAULT,\n): number | null {\n if (!(error instanceof RequestError)) {\n return null\n }\n\n const status = error.status\n const headers = error.response?.headers ?? {}\n const remaining = headers['x-ratelimit-remaining']\n const message = error.message\n\n let retryable = false\n if (status === 429) {\n retryable = true\n } else if (status === 403) {\n if (remaining !== undefined && remaining === '0') {\n retryable = true // primary rate limit\n } else if (SECONDARY_RATE_LIMIT_RE.test(message)) {\n retryable = true // secondary rate limit(403 形态)\n }\n }\n\n if (!retryable) {\n return null\n }\n\n // Retry-After 头优先(GitHub secondary rate limit 常返回,秒为单位)\n const retryAfter = headers['retry-after']\n if (retryAfter !== undefined) {\n const seconds = Number(retryAfter)\n if (Number.isFinite(seconds) && seconds > 0) {\n return Math.min(seconds * 1000, maxBackoffMs)\n }\n }\n\n // 其次等待到限流重置时刻(primary rate limit 标准行为)\n const reset = headers['x-ratelimit-reset']\n if (reset !== undefined) {\n const resetMs = Number(reset) * 1000\n if (Number.isFinite(resetMs) && resetMs > 0) {\n const waitMs = resetMs - Date.now() + 1000\n if (waitMs > 0) {\n return Math.min(waitMs, maxBackoffMs)\n }\n }\n }\n\n // 指数退避兜底\n return Math.min(baseDelayMs * 2 ** attempt, maxBackoffMs)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","import type { Octokit } from '@octokit/rest'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * GitHub 认证抽象层。\n *\n * 支持 PAT(classic / fine-grained)与 GitHub App installation token 两种认证路径。\n * 调用方通过 `getOctokit()` 获取已认证的 Octokit 实例;通过 `getGitCredential()`\n * 获取推送/clone 用的 Git 凭据;通过 `getCommitAuthor()` 获取 commit author 信息。\n *\n * `authProvider` 字段用于审计与调试(区分 PAT vs App 路径)。\n *\n * @example\n * ```typescript\n * // 推荐:从 fromPat / fromApp 工厂构造\n * const auth = fromPat('ghp_xxxx')\n * const octokit = auth.getOctokit()\n *\n * // git push 凭据\n * const { username, token } = auth.getGitCredential()\n *\n * // commit author(PAT 路径固定;App 路径动态)\n * const { name, email } = auth.getCommitAuthor()\n * ```\n */\nexport interface AuthProvider {\n /** 获取已认证的 Octokit 实例 */\n getOctokit(): Octokit\n\n /** 获取推送 / clone 用的 Git 凭据(用户名 + token) */\n getGitCredential(): { username: string, token: string }\n\n /**\n * 获取 commit author 信息。\n *\n * - PAT 路径:固定返回 `{ name: 'dependfix[bot]', email: 'dependfix[bot]@users.noreply.github.com' }`(保持现有行为零变化)\n * - GitHub App 路径:动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`(GitHub App 协议要求)\n */\n getCommitAuthor(): { name: string, email: string }\n\n /** 认证路径标识(用于审计 + 调试) */\n readonly authProvider: 'pat' | 'github-app'\n}\n\n/** 工厂函数统一选项(与 OctokitClientOptions.retry 字段对齐) */\nexport interface AuthProviderOptions {\n /** API 限流 / 次要限流指数退避重试策略。默认 maxRetries=3。 */\n retry?: {\n maxRetries?: number\n baseDelayMs?: number\n maxBackoffMs?: number\n }\n}\n\n/** PAT 工厂参数(仅 token;retry 在 options 注入) */\nexport type FromPatParams = string\n\n/** GitHub App 工厂参数 */\nexport interface FromAppParams {\n /** GitHub App ID */\n appId: string\n /** PEM 格式私钥 */\n privateKey: string\n /** Installation ID */\n installationId: string\n /** Bot 用户名(用于 commit author 动态生成) */\n botLogin?: string\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * PAT 路径固定 commit author(保持现有行为零变化)。\n *\n * 注:现有 PAT 路径硬编码 `dependfix[bot]@users.noreply.github.com`,虽非真实 bot 身份(字符串约定),\n * 但保持行为不变以确保 PAT 用户无感升级。已知缺陷由后续阶段修复。\n */\nexport const PAT_COMMIT_AUTHOR = {\n name: 'dependfix[bot]',\n email: 'dependfix[bot]@users.noreply.github.com',\n} as const\n\n/** PAT 路径固定 Git 用户名(用于 git push 时的 Basic Auth) */\nexport const PAT_GIT_USERNAME = 'x-access-token'\n","import { Octokit } from '@octokit/rest'\nimport { createGitHubClient } from '../github/client'\nimport {\n PAT_COMMIT_AUTHOR,\n PAT_GIT_USERNAME,\n type AuthProvider,\n type AuthProviderOptions,\n type FromPatParams,\n} from './auth-provider'\n\n/**\n * PAT 工厂函数。委托给 `PatAuthProvider` 类。\n *\n * 保留 `fromPat` 命名习惯便于与 `fromApp` 对齐(统一工厂语义)。\n *\n * @example\n * ```typescript\n * const auth = fromPat('ghp_xxxx')\n * const octokit = auth.getOctokit()\n * const { username, token } = auth.getGitCredential()\n * ```\n */\nexport function fromPat(token: FromPatParams, options?: AuthProviderOptions): AuthProvider {\n return new PatAuthProvider(token, options)\n}\n\n/**\n * PAT 认证路径实现。\n *\n * `getOctokit()` 委托给 [`createGitHubClient`](../github/client.ts) 以复用现有 retry policy 与限流重试实现;\n * Octokit 实例缓存以避免重复构造(每次 `new Octokit()` + `hook.wrap()` 都有开销)。\n *\n * 行为与原 `createGitHubClient({ token })` 等价:\n * - `getOctokit()` 返回通过 PAT 认证的 Octokit 实例(含限流重试 hook)\n * - `getGitCredential()` 返回推送用的 Basic Auth(username = `'x-access-token'`)\n * - `getCommitAuthor()` 返回固定 `dependfix[bot]`(PAT 路径用户行为零变化)\n *\n * @example\n * ```typescript\n * const auth = new PatAuthProvider('ghp_xxxx', { retry: { maxRetries: 3 } })\n * const octokit = auth.getOctokit()\n * ```\n */\nexport class PatAuthProvider implements AuthProvider {\n readonly authProvider = 'pat' as const\n\n private readonly token: FromPatParams\n private readonly retry?: AuthProviderOptions['retry']\n private cachedOctokit: Octokit | null = null\n\n constructor(token: FromPatParams, options?: AuthProviderOptions) {\n this.token = token\n this.retry = options?.retry\n }\n\n /**\n * 获取已认证的 Octokit 实例(首次调用构造并缓存)。\n */\n getOctokit(): Octokit {\n if (!this.cachedOctokit) {\n this.cachedOctokit = createGitHubClient({ token: this.token, retry: this.retry })\n }\n return this.cachedOctokit\n }\n\n /** 获取推送 / clone 用的 Git 凭据(username = `'x-access-token'`,token = PAT) */\n getGitCredential(): { username: string, token: string } {\n return { username: PAT_GIT_USERNAME, token: this.token }\n }\n\n /** 获取 commit author(PAT 路径固定 `dependfix[bot]`) */\n getCommitAuthor(): { name: string, email: string } {\n return { ...PAT_COMMIT_AUTHOR }\n }\n}\n","import { Octokit } from '@octokit/rest'\nimport { createAppAuth } from '@octokit/auth-app'\nimport type {\n AuthProvider,\n AuthProviderOptions,\n FromAppParams,\n} from './auth-provider'\n\n// ---------------------------------------------------------------------------\n// AppAuthProvider - GitHub App installation token 认证实现\n// ---------------------------------------------------------------------------\n\n/**\n * GitHub App 工厂函数。\n *\n * 委托给 [`AppAuthProvider`] 类。\n *\n * @example\n * ```typescript\n * const auth = fromApp({\n * appId: '123456',\n * privateKey: '-----BEGIN RSA PRIVATE KEY-----\\n...',\n * installationId: '7890123',\n * botLogin: 'dependfix-bot[bot]',\n * })\n * const octokit = auth.getOctokit()\n * ```\n */\nexport function fromApp(\n params: FromAppParams,\n options?: AuthProviderOptions,\n): AuthProvider {\n return new AppAuthProvider(params, options)\n}\n\n/**\n * GitHub App 认证路径实现。\n *\n * 使用 [`@octokit/auth-app`](https://github.com/octokit/auth-app.js) 提供:\n * - JWT signing(App 身份认证)\n * - `getInstallationOctokit`(installation token 获取;库内置 59 分钟 LRU TTL 缓存)\n * - 自动 installation token 轮换\n *\n * 注意:\n * - `getGitCredential()` 返回的 `token` 字段为占位符(installation token 不能直接从 AppAuthProvider 获取;\n * 由 [`@octokit/auth-app`](https://github.com/octokit/auth-app.js) 内部管理);调用方应通过 `getOctokit()`\n * 拿到的 Octokit 实例进行 API 调用\n * - `getCommitAuthor()` 按 GitHub App 协议动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`\n * - Octokit 实例缓存以避免重复构造\n *\n * @see [C22 PAT 无感升级评估 §4.2 AuthProvider 接口设计](../../../../docs/design/governance/c22-pat-backward-compat.md)\n */\nexport class AppAuthProvider implements AuthProvider {\n readonly authProvider = 'github-app' as const\n\n private readonly params: FromAppParams\n // retry 选项保留用于未来扩展(AppAuthProvider 当前不直接使用 retry;由 @octokit/auth-app 自管)\n private readonly _options?: AuthProviderOptions\n private cachedOctokit: Octokit | null = null\n\n constructor(params: FromAppParams, options?: AuthProviderOptions) {\n this.params = params\n this._options = options\n }\n\n /**\n * 获取已认证的 Octokit 实例。\n *\n * 首次调用时构造并缓存;后续调用复用同一实例。\n * installation token 由 `@octokit/auth-app` 内部管理(59 分钟 LRU TTL 缓存)。\n *\n * **构造方式**:使用 `authStrategy: createAppAuth` + `auth: {appId, privateKey, installationId}`\n * 双字段组合(`@octokit/auth-app` v8.x README 标准用法),**而非** `auth: createAppAuth(...)`\n * 单字段调用——后者被 `@octokit/core` 视作字符串 token 路径抛 `Token passed to createTokenAuth is not a string`,\n * 或被 `authStrategy` 字段调用后命中 `auth(state, authOptions)` 的 `default` 分支抛 `Invalid auth type: undefined`\n * (todo.md §M18.4 e2e 验证 + audit quick depth Reject 后补修发现)。\n *\n * @see [@octokit/auth-app README §installation authentication](https://github.com/octokit/auth-app.js#installation-authentication)\n */\n getOctokit(): Octokit {\n if (!this.cachedOctokit) {\n this.cachedOctokit = new Octokit({\n authStrategy: createAppAuth,\n auth: {\n appId: this.params.appId,\n privateKey: this.params.privateKey,\n installationId: this.params.installationId,\n },\n baseUrl: 'https://api.github.com',\n })\n }\n return this.cachedOctokit\n }\n\n /**\n * 获取推送 / clone 用的 Git 凭据。\n *\n * 注意:installation token 由 `@octokit/auth-app` 内部管理,**不**能从 AppAuthProvider 直接获取;\n * 本方法返回的 `token` 字段为占位符(`'installation-token-managed-by-octokit-auth-app'`)。\n * 调用方应通过 `getOctokit()` 拿到的 Octokit 实例进行 API 调用(含 git push / clone 等写操作)。\n */\n getGitCredential(): { username: string, token: string } {\n return {\n username: 'x-access-token',\n token: 'installation-token-managed-by-octokit-auth-app',\n }\n }\n\n /**\n * 获取 commit author。\n *\n * 按 GitHub App 协议动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`:\n * - `name`:`{app_id}[bot]`(GitHub 自动识别 bot 身份)\n * - `email`:`{app_id}+{bot_login}[bot]@users.noreply.github.com`(GitHub noreply email 格式)\n *\n * 当 `params.botLogin` 未提供时,使用 `dependfix[bot]` 作为 fallback(保持与 PAT 路径一致的格式);\n * 但建议调用方显式提供 `botLogin` 以确保 commit author 真实 bot 身份归属。\n */\n getCommitAuthor(): { name: string, email: string } {\n const botLogin = this.params.botLogin ?? 'dependfix[bot]'\n const appId = this.params.appId\n return {\n name: `${appId}[bot]`,\n email: `${appId}+${botLogin}@users.noreply.github.com`,\n }\n }\n}\n","import type { FromAppParams } from './auth-provider'\n\n// ---------------------------------------------------------------------------\n// Installation Token Cache - 占位 + 监控 API\n// ---------------------------------------------------------------------------\n\n/**\n * Installation token 缓存层(**M18.1 commit 4 实施**)。\n *\n * 当前实现:**依赖 [@octokit/auth-app](https://github.com/octokit/auth-app.js) 内置缓存**\n * (59 分钟 LRU TTL;GitHub token 有效期 60 分钟 - 1 分钟缓冲)。\n *\n * 本类作为未来扩展点:\n * - 缓存统计(命中率 / 大小 / 清理 API)\n * - 失败重试 + 报警(M18.2 集成时实施)\n * - 多 worker 共享(Redis-backed;超出 M18.1 范围)\n *\n * 注意:\n * - `getOrRefresh` 当前委托给 `createAppAuthentication` + `getInstallationOctokit`\n * - 当 [@octokit/auth-app](https://github.com/octokit/auth-app.js) 内部缓存命中时不会触发 API 调用\n * - 缓存未命中时自动调用 `POST /app/installations/{installation_id}/access_tokens` 获取新 token\n *\n * @see [C22 PAT 无感升级评估 §4.7 commit 拆分纪律](../../../../docs/design/governance/c22-pat-backward-compat.md)\n */\nexport class InstallationTokenCache {\n private readonly params: FromAppParams\n private readonly ttlMs: number\n\n constructor(params: FromAppParams, options?: { ttlMs?: number, refreshThresholdMs?: number }) {\n this.params = params\n // 默认 59 分钟(与 @octokit/auth-app 默认一致)\n this.ttlMs = options?.ttlMs ?? 59 * 60 * 1000\n }\n\n /**\n * 获取或刷新 installation token。\n *\n * 当前为占位实现:直接返回 `'installation-token-managed-by-octokit-auth-app'` 占位字符串。\n * 调用方应使用 `fromApp(params).getOctokit()` 获取真实 Octokit 实例(由 `@octokit/auth-app`\n * 自动管理 installation token 缓存)。\n *\n * @throws 永不抛错(占位实现)\n */\n async getOrRefresh(): Promise<string> {\n // 占位实现:真实 token 由 @octokit/auth-app 内部管理\n // 本方法保留作为未来扩展点(Redis-backed / 失败重试 / 报警)\n return 'installation-token-managed-by-octokit-auth-app'\n }\n\n /** 清理过期缓存项(占位实现) */\n clear(): void {\n // 占位实现:@octokit/auth-app 内部管理缓存清理\n // 保留接口签名便于未来替换为自有实现\n }\n\n /** 获取缓存 TTL 配置(毫秒) */\n getTtlMs(): number {\n return this.ttlMs\n }\n\n /** 获取 cache key(基于 installationId) */\n static getCacheKey(params: FromAppParams): string {\n return `installation:${params.installationId}`\n }\n}\n"],"mappings":"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"}
|
package/dist/chunks/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Octokit } from "@octokit/rest";
|
|
2
|
-
//#region src/auth/auth-provider.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* GitHub 认证抽象层。
|
|
5
4
|
*
|
|
@@ -77,8 +76,6 @@ declare const PAT_COMMIT_AUTHOR: {
|
|
|
77
76
|
};
|
|
78
77
|
/** PAT 路径固定 Git 用户名(用于 git push 时的 Basic Auth) */
|
|
79
78
|
declare const PAT_GIT_USERNAME = "x-access-token";
|
|
80
|
-
//#endregion
|
|
81
|
-
//#region src/auth/pat-provider.d.ts
|
|
82
79
|
/**
|
|
83
80
|
* PAT 工厂函数。委托给 `PatAuthProvider` 类。
|
|
84
81
|
*
|
|
@@ -130,8 +127,6 @@ declare class PatAuthProvider implements AuthProvider {
|
|
|
130
127
|
email: string;
|
|
131
128
|
};
|
|
132
129
|
}
|
|
133
|
-
//#endregion
|
|
134
|
-
//#region src/auth/app-provider.d.ts
|
|
135
130
|
/**
|
|
136
131
|
* GitHub App 工厂函数。
|
|
137
132
|
*
|
|
@@ -213,8 +208,6 @@ declare class AppAuthProvider implements AuthProvider {
|
|
|
213
208
|
email: string;
|
|
214
209
|
};
|
|
215
210
|
}
|
|
216
|
-
//#endregion
|
|
217
|
-
//#region src/auth/installation-token-cache.d.ts
|
|
218
211
|
/**
|
|
219
212
|
* Installation token 缓存层(**M18.1 commit 4 实施**)。
|
|
220
213
|
*
|
|
@@ -257,6 +250,5 @@ declare class InstallationTokenCache {
|
|
|
257
250
|
/** 获取 cache key(基于 installationId) */
|
|
258
251
|
static getCacheKey(params: FromAppParams): string;
|
|
259
252
|
}
|
|
260
|
-
//#endregion
|
|
261
253
|
export { fromPat as a, FromAppParams as c, PAT_GIT_USERNAME as d, PatAuthProvider as i, FromPatParams as l, AppAuthProvider as n, AuthProvider as o, fromApp as r, AuthProviderOptions as s, InstallationTokenCache as t, PAT_COMMIT_AUTHOR as u };
|
|
262
254
|
//# sourceMappingURL=index.d.mts.map
|