# @effected/github 中文文档

规范地址：https://npmcn.com/package/%40effected/github/

版本：0\.32\.1
npm 元数据更新时间：2026-11-11T00:58:44.212Z
中文译文更新时间：2026-11-13T08:34:31.199Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40effected/github
原文对照：https://npmcn.com/package/%40effected/github/?lang=original
项目主页：https://github.com/spencerbeggs/effected/tree/main/packages/github#readme
源码仓库：https://github.com/spencerbeggs/effected

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：622 kB，51 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24\.11\.0
- 运行依赖：10 个
- peer 依赖：effect ~4\.1\.0
- 首次发布：2026-07-26T02:48:30.520Z
- 最新版本发布：2026-11-11T00:58:43.823Z
- 近 30 天下载量：16339（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：45（近 12 个月 45 个）

## 中文简介

在octokit核心请求表面上键入了 GitHub REST 和 GraphQL 服务, 并配备了应用认证和资源帮助器 .

## 安装

```sh
npm install @effected/github
```

也可以使用：`pnpm add @effected/github`、`yarn add @effected/github`、`bun add @effected/github`

## README

# @ effect/ github

[![npm](https://img.shields.io/npm/v/@effected%2Fgithub?label=npm&color=cb3837)](https://www.npmjs.com/package/@effected/github) [![License: MIT](https://img.shields.io/badge/License-MIT-4caf50.svg)](https://opensource.org/licenses/MIT) [![Node.js %3E%3D24.11.0](https://img.shields.io/badge/Node.js-%3E%3D24.11.0-5fa04e.svg)](https://nodejs.org/) [![TypeScript 7.0](https://img.shields.io/badge/TypeScript-7.0-3178c6.svg)](https://www.typescriptlang.org/)

输入了 GitHub 的 REST 和 GraphQL [效果](https://effect.website/) v4 (英语). `client.request("GET /repos/{owner}/{repo}", { owner, repo })` 键入参数并返回 `data` 单从路线字面上——没有 `operation: string`没有召回,没有铸币。 一个 `GitHubError` 以一个 `kind` 您可以切换到一条消息 而不是给它添加信息, 一个pagination 引擎可以回溯到每一个开始的路由 `client.request`是因为 `Stream` 格式,以及一组资源服务`GitBranch`, (中文(简体) ), `GitTag`, (中文(简体) ), `CheckRun`, (中文(简体) ), `PullRequest`, (中文(简体) ), `PullRequestComment`, (中文(简体) ), `GitHubRelease`, (中文(简体) ), `Attestation`)将多呼起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起 第二层写出配置的一半:秘密,变量,规则,部署环境,安全切换和代码QL默认设置. `GitHubApp` 薄荷并取消 App auth 的安装令牌.

> **预`1.0.0`。 。 。 .** 此软件包是 `@effected/*` 在稳定基础上建造的工具包 效应v4(`effect` `~4.1.0`) 并仍在 `0.x` 发展。 稳定效果 制作一个工具箱 `1.0.0` 可能,不是自动的。 为了保持你的 `effect` 财务报告和审定财务报表 `@effect/*` 该工具包在线上构建和测试、安装 [`@effected/pnpm-plugin-effected`](https://www.npmjs.com/package/@effected/pnpm-plugin-effected)。 。 。 .
> 
> **稳定:不稳定.** 此软件包的 API 表面尚未考虑 完成并可能改变 `0.x` 释放 定一个确切的版本——甚至 标记软件包 _稳定_ 在此之前 `1.0.0` 可以引入突破性更改 和精确的针 变成一个类型检查错误 而不是一个 跑步时间惊喜。 完全政策: [释放策略](https://github.com/spencerbeggs/effected#release-strategy)。 。 。 .

## 为什么

手卷的octokit包装器往往在同一种形状上相会:a `rest<T>(operation: string, fn: (octokit) => Promise<{ data: T }>)` 帮助在哪里 `T` 不管打电话者写了什么 都与终点无关 此软件包的路由按键 `request` 缩小这一差距—— `@octokit/types` 已经从 GitHub 自己的 OpenAPI 描述生成了地图,并且 `@octokit/core`是因为 `request` 已经消耗了 所以也没有理由再造.

错误模型得到同样的处理. `GitHubError.kind` 字面结合( r) :`notFound`, (中文(简体) ), `alreadyExists`, (中文(简体) ), `rejected`, (中文(简体) ), `unauthorized`, (中文(简体) ), `rateLimited`, (中文(简体) ), `transport`, (中文(简体) ), `decode`)通过一个分类步骤产生, `GitHubError.fromOctokit`,而不是每个资源一个手写地图. `kind: "alreadyExists"` 是什么让 `GitBranch.upsert` 财务报告和审定财务报表 `GitTag.upsert` 而不是在每个需要的呼叫站点重复一次.

这个包还拥有Octokit运行时间,所以下游没有什么需要. `@octokit/rest` 财务报告和审定财务报表 `@octokit/auth-app` 故意缺席——其余的包装器是一个请求-博客插件外加1.4 MB的重复端点类型,以及 `auth-app` 拖入 `createOAuthUserAuth`大约492 KB的OAuth机器 这个包裹从来没有调用。 GitHub App JWT 由工具包自己签名 `@effected/jwt` 相反,在核心 `effect/jose`:RS256没有额外的运行时间依赖,因此App auth运行于Cloudflare Workers和Node上. App 层(`GitHubApp.layer`, (中文(简体) ), `layerWith`, (中文(简体) ), `clientLayer`, (中文(简体) ), `appClientLayer`) 需要核心的 `Signature` 服务; 提供 `Signature.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto))` 从 `effect/crypto` 一次在边缘。 一个只用它已经持有的标志进行认证的消费者,从不将JWT的标志连接起来: App - 认证层生活在自己的模块中(App - 认证层)`GitHubApp`)),这里没有任何物被集合到一个能够击败分裂的命名空间物体.

## 安装

```bash
npm install @effected/github effect
```

```bash
pnpm add @effected/github effect
```

需要节点.js~~24.11.0. `effect` v4 是一个对等依赖.

全体 `@effected/*` 软件包仅为无害环境管理:只公布出口地图 `import` 条件,所以 `require()` 包括解析 CJS 模式的工具 —— 与节点失败 `ERR_PACKAGE_PATH_NOT_EXPORTED` 而不是装入不存在的 CJS 建筑。 从ES模块导入.

## 快开始

```ts
import { GitHubClient, Repo, RepoRef } from "@effected/github";
import { Effect } from "effect";

const program = Effect.gen(function* () {
  const client = yield* GitHubClient;
  const repo = yield* client.request("GET /repos/{owner}/{repo}", { owner: "effect-ts", repo: "effect" });
  return repo.default_branch; // string — typed from the route literal, no cast
});

const ClientLayer = GitHubClient.layerFromConfig(); // reads GITHUB_TOKEN through the ambient ConfigProvider
const RepoLayer = Repo.layer(RepoRef.make({ owner: "effect-ts", repo: "effect" }));

Effect.runPromise(program.pipe(Effect.provide(ClientLayer), Effect.provide(RepoLayer))).then(console.log);
// the repository's default branch name, e.g. "main"
```

`Repo` 携带 `{ owner, repo }` 坐标 `R` 而不是每个调用参数,所以下面的每一种资源方法都读作单一的表达. 在一个以上的寄存器上操作的程序 `Repo.provide(otherRef)` 围绕需要它的部分.

## 资源服务

每个资源都是服务结束 `GitHubClient`,拥有自己的 `layer`, (中文(简体) ), `makeTest` 财务报告和审定财务报表 `layerTest`。 。 。 . `GitBranch.upsert` 促使整个集:

```ts
import { GitBranch, GitHubClient, Repo } from "@effected/github";
import { Effect } from "effect";

const program = Effect.gen(function* () {
  const branches = yield* GitBranch;
  return yield* branches.upsert("release/1.2", "abc123");
});
// Effect<"created" | "reset", GitHubError, GitBranch | Repo>
// one round trip in the common case, two in the raced one — never four
```

`CheckRun.withCheckRun` 在检查运行中运行一个程序,并在每一个退出路径上完成它——成功,输入失败,缺陷或中断——所以运行永远不会被卡住 `in_progress`数字 :

```ts
import { CheckRun } from "@effected/github";
import { Effect } from "effect";

declare const lint: () => Effect.Effect<ReadonlyArray<string>>;
declare const deriveConclusion: (findings: ReadonlyArray<string>) => "success" | "neutral" | "failure";

const program = Effect.gen(function* () {
  const check = yield* CheckRun;
  return yield* check.withCheckRun("lint", "abc123", (_id, conclude) =>
    Effect.gen(function* () {
      const findings = yield* lint();
      yield* conclude(deriveConclusion(findings));
      return findings;
    }),
  );
});
```

`GitTag.latestSemver` 步行标记,并选择最新版本的一 在单一的通过,结束 `@effected/semver`同步比较者——每个候选人不得来回. `PullRequest.upsert` 财务报告和审定财务报表 `PullRequestComment.upsert` (一个标记-标记粘贴的注释)跟随同一个一呼一指的形状.

## 问题和最后参考资料

`GitHubIssue` 包括问题:读取、列表、创建、关闭、评论以及解答拉动请求的GraphQL侧. `commentOnce` 是创建或斯基普对应的 `PullRequestComment.upsert` ——它贴出一个有标记的评论,从来不编辑一个,这是你想要的公告,如果在事实发生后有所改变,它会读作历史的改写:

```ts
import { CommentMarker, GitHubIssue } from "@effected/github";
import { Effect } from "effect";

const marker = CommentMarker.make({ namespace: "release-bot", key: "shipped" });

const program = Effect.gen(function* () {
  const issues = yield* GitHubIssue;
  return yield* issues.commentOnce(1873, marker, "Shipped in the release just published.");
});
// CommentOnceResult — `wrote: true` when this call posted the comment,
// `wrote: false` when the marker was already there and nothing was sent.
// `comment` is the marked comment either way.
```

存在检查将问题的评论页到末尾并匹配标记 `upsert`准确的拼写,所以任何一位成员写出的评论都能够被另一位成员找到. 它是一个外观前写:在同一个问题上的两次跑跑都可以看到没有标记和两个帖子,所以把标记当作记录而不是把支票当作锁.

跑步是否应该评论,通常会转而发布拉出请求的结束,而这是一个语法而不是API调用. 这个语法现在住在 [`@effected/github-references`](https://www.npmjs.com/package/@effected/github-references),一个没有octokit的纯软件包后面;这个软件包再导出它曾经拥有的两种方言,这样现有的代码不断编译:

```ts
import { harvestIssueReferences, parseBareLineReference } from "@effected/github";

console.log(harvestIssueReferences("Fixes #12 and closes #13."));
// [ { issueNumber: 12, keyword: "fixes", start: 0, end: 9 },
//   { issueNumber: 13, keyword: "closes", start: 14, end: 24 } ]

console.log(parseBareLineReference("Closes: #12"));
// Option.some({ issueNumber: 12, keyword: "closes" })

console.log(parseBareLineReference("closes #12 for real"));
// Option.none() — the bare-line dialect takes the whole line or nothing
```

`harvestIssueReferences` 读取 **内在辩护** 方言:9个收尾关键词之一(`CLOSING_KEYWORDS`随后是白空间和 `#<number>`,在文本的任何地方,没有结肠——拼写"GitHub"本身扫描了拉取请求正文. 复制件按书面形式出现,因为 `fixes #1, fixes #1` 意思是一两个意图是打电话者的问题. `parseBareLineReference` 读取 **光线** 方言,其中整条被修剪的行为参考,而结肠是可选的——所生成的参考块写出形状,每行一出. 交叉重排( R)`owner/repo#12`)和全URL引用范围已超出范围.

再出口包括六个名字—— `CLOSING_KEYWORDS`, (中文(简体) ), `ClosingKeyword`, (中文(简体) ), `IssueReference`, (中文(简体) ), `harvestIssueReferences`, (中文(简体) ), `BareLineReference`, (中文(简体) ), `parseBareLineReference` 可在稍后释放时放弃。 从 `@effected/github-references` 直接用于新代码;它还包含第三个方言 这个包不再出口,关闭列表(`Closes #247, #248 and #251`) (中文(简体) ).

## 仓库配置

6项服务涵盖政策而不是内容库的一半: `RepositorySecret`, (中文(简体) ), `RepositoryVariable`, (中文(简体) ), `Ruleset`, (中文(简体) ), `DeploymentEnvironment`, (中文(简体) ), `RepositorySecurity` 财务报告和审定财务报表 `CodeScanning`。它们被设定为一个在整个舰队中应用相同配置的程序,因此每个列表都会读取页尾并截取一页不会成为错误的决定.

GitHub 的 API 要求的 libsodium 封存框中包含的秘密, 这就是为什么这个值是一个 `Redacted<string>` 而不是平庸的:

```ts
import { RepositorySecret } from "@effected/github";
import { Effect, Redacted } from "effect";

declare const token: string;

const program = Effect.gen(function* () {
  const secrets = yield* RepositorySecret;
  yield* secrets.set("NPM_TOKEN", Redacted.make(token)); // the "actions" store by default
  yield* secrets.setForEnvironment("production", "DEPLOY_KEY", Redacted.make(token));
  return yield* secrets.list("dependabot");
});
// ReadonlyArray<SecretInfo> — names only. GitHub returns a secret's value from no endpoint,
// so a diff against desired state detects a deleted secret and never an edited one.
```

`actions`, (中文(简体) ), `dependabot` 财务报告和审定财务报表 `codespaces` 在同一仓库的三个商店,每个商店都有自己的公用钥匙 `scope` 争论在他们之间挑出。 加密只存在于一个模块 `RepositorySecret` 所以一个从未写过秘密的消费者 就不会把密码对连起来. `RepositoryVariable` 对非秘密的值,镜像相同的六种方法 `set` 列表中的第一个原因是 GitHub 将创建并更新两个路径.

`Ruleset.upsert` 按名称匹配已有规则 **财务报告和审定财务报表** `source_type`。第二个字段是投影带的原因: `GET /repos/{owner}/{repo}/rulesets` 返回一个寄存器与自己的组织一起继承的规则,仅根据名称进行匹配就可以让寄存器扫描写入 `PUT` 该组织的规则id——为该组织拥有的每一个存放处改写政策. `Ruleset.teamId` 财务报告和审定财务报表 `Ruleset.roleId` 解决一个绕行角色需要的数字标识,范围为组织 `Repo`。 。 。 .

其余随其端口自取粮. `DeploymentEnvironment.upsert` 是一个平地 `PUT` 因为GitHub的路线是一能的, 以及它的 `delete` 带着环境的秘密和变数, 确定任何清理通道的顺序. `RepositorySecurity` 读取和写取三个切换 GitHub 关闭了存储器的端点: 弱点警报、自动安全修正和私人脆弱性报告. `CodeScanning.configure` 应用一个 CodeQL 默认设置, 并在 GitHub 接受后立即返回 `202` 并同步配置,这里没有民意测验 `CodeScanning.languages` 报告 GitHub 在寄存器中检测到的, 即您在调用前过滤已配置语言列表 `configure`。 。 。 .

`GitHubRepository` 拥有仓库自己的设置. `updateSettings` 是忠实的键入 `PATCH`· ; `applySettings` 是上面的应用器, 取一张公开的地图, 将每个密钥导向 API 可以实际设置它

```ts
import { GitHubRepository } from "@effected/github";
import { Effect } from "effect";

const program = Effect.gen(function* () {
  const repository = yield* GitHubRepository;
  return yield* repository.applySettings({
    has_issues: true,
    has_sponsorships: false, // GraphQL-only: costs one extra read for the node id
    security_and_analysis: { secret_scanning: "enabled" },
  });
});
// AppliedSettings — { rest: [...], graphql: [...], ignored: [...] }, in the caller's own key names.
// The lists agree with the input right up until preparation drops a field
// GitHub would reject, which is what a person reading a dry run is checking for.
// `ignored` names keys neither API accepts (a typo, say): they are never sent,
// because the REST PATCH would ignore them and still answer 200. The REST key
// list is generated from GitHub's OpenAPI description; a key a later
// api-version removed is ignored only for a client pinned at or past it.
```

`updateSettings` 使用octokit自己生成的参数,这些字段拼写为 `has_issues?: boolean` 而不是说 `has_issues?: boolean | undefined` 这样下去 `exactOptionalPropertyTypes` (单位:千美元) `Partial<T>` 从您自己的设置中构建的图案完全没有指定它. `repositoryPatch` 是支持的出路, 它的存在,所以答案从来不是一个铸件:

```ts
import { repositoryPatch } from "@effected/github";

declare const config: { has_issues?: boolean | undefined; description?: string | undefined };

const patch = repositoryPatch({ has_issues: config.has_issues, description: config.description });
// a RepositoryPatch carrying only the fields that were actually set
```

放下钥匙就是电线需要的: `PATCH` 将一个缺失的字段读作"离开它",而一个明确 `null` 是一个值. `RepositoryPatchDraft` 是输入类型——相同的字段,每个字段都允许是明确的 `undefined`。将草稿构建为您可以使用的对象字面:按键循环会击败 TypeScript 两个索引访问器之间的关联,没有帮助者可以修复.

`security_and_analysis` 接受两个赤裸的 `"enabled"` 人类在配置文件中写到 `{ status: "enabled" }` GitHub 自己的参数类型声明 。 触摸两个图形QL 设置的地图从不读取节点 id, 因此常见的大小写会保留一个请求 . `ownerType` 答题 `"User"` 或者说 `"Organization"` 用于存储器 `Repo`,这就是共享设置模板在将自己应用到个人存储器之前,只将GitHub接受的字段降入组织拥有的存储器.

## GitHub 应用程序认证

`GitHubApp.clientLayer` 构建一个 `GitHubClient` 认证为应用程序安装 。 发行时会取消, 所以工作流程不会留下现场记录:

```ts
import { GitHubApp } from "@effected/github";
import { Redacted } from "effect";

// bind once — layers memoize by reference
const AppClient = GitHubApp.clientLayer({
  appId: "123456",
  privateKey: Redacted.make(process.env.GITHUB_APP_PRIVATE_KEY as string),
  owner: "effect-ts",
});
// Layer<GitHubClient, GitHubAppError> — provide it wherever a GitHubClient is needed
```

仅 `GitHubApp` 并且它的静电输入JWT的标志器——一个用平庸的符号认证的消费者从未到达它.

## 充当用户: OAuth 网络流量

`GitHubUserAuth` 运行 OAuth 网络流出一个 GitHub App(或 OAuth App) 作为用户: `exchange` 以重新定向的代码GitHub及其PKCE验证器交换用户的指使; `refresh` 延长到期补助金; `revokeGrant` 使用 HTTP Basic 客户端认证删除用户的全部授权。 它说话 `fetch` 直接,从不通过 `GitHubClient`,因此,一个在链接中只签名用户的工人既不是octokit,也不是JWT签名人:

```ts
import { GitHubUserAuth } from "@effected/github";
import { Effect, Redacted } from "effect";

// bind once — layers memoize by reference
const UserAuth = GitHubUserAuth.layer({
  clientId: "Iv23liExample",
  clientSecret: Redacted.make(process.env.GITHUB_CLIENT_SECRET as string),
});

const renew = (refreshToken: Redacted.Redacted<string>) =>
  Effect.gen(function* () {
    const auth = yield* GitHubUserAuth;
    return yield* auth.refresh({ refreshToken });
  }).pipe(
    // "reconnect": the user's grant is dead; "reconfigure": the app's secret or settings
    // are wrong, so keep the tokens and let it fail on to an operator
    Effect.catchTag("GitHubUserAuthFinal", (error) =>
      error.remedy === "reconnect" ? Effect.succeed("sign-in-again" as const) : Effect.fail(error),
    ),
  );
```

`exchange` 答题 `GitHubUserTokens`数字 : `ExpiringUserTokens` (访问令牌,刷新令牌, 和瞬间都过期,从 `Clock`)或(或) `NonExpiringUserTokens` 当应用程序不过期用户代号时; `refresh` 总是回答即将到期的一对, GitHub 在每个刷新时旋转刷新符号 。 两者都是可编码的,以道具为平板字符串,以时间为ISO即时.

每一个失败都是 `GitHubUserAuthFinal` (吉特胡布拒绝;其 `code`, (中文(简体) ), `description` 财务报告和审定财务报表 `status` 保留)或 `GitHubUserAuthTemporary` (网络,超时,5xx或429,或不是符号回复的机体——稍后重试并保留所持有的符号). 一个暂时的失败已经花费了刷新符号: GitHub 在每次刷新过程中旋转它, 所以刷新在到达 GitHub( a 2xx) 之后失败了 `malformed`,或一个 `network` 或者说 `timeout` 中继)的答案 `bad_refresh_token` 重新测试时,用户再次签名。 GitHub 拒绝使用 HTTP 200 和 an 的坏代码或刷新令牌 `error` 被理解为拒绝. **分会 `remedy`,从不单独在标签上:** `incorrect_client_credentials` 也是最后的,一个处理器 将每个用户的符号都丢在任何 `GitHubUserAuthFinal` 在客户秘密被旋转时 给所有人签名 客户端的秘密,代码,验证人和信使在没有出错消息和没有保留原因的情况下出现. `tokenUrl`, (中文(简体) ), `apiUrl`, (中文(简体) ), `apiVersion`, (中文(简体) ), `userAgent`, (中文(简体) ), `timeout` (10秒钟)和 `fetch` 是选项,用于 GitHub 企业服务器、 工人和测试.

## 错误

`GitHubError` 覆盖REST, `GitHubGraphQLError` 覆盖图QL, `GitHubAppError` 覆盖应用程序认证, `GitHubUserAuthFinal` 编号 : `GitHubUserAuthTemporary` 覆盖 OAuth 用户流,以及 `TokenPermissions` 生产自己的 `TokenPermissionError` ——五种,不是每种资源之一:

```ts
import { GitHubError } from "@effected/github";
import { Effect } from "effect";

declare const upsertBranch: Effect.Effect<"created" | "reset", GitHubError>;

const program = upsertBranch.pipe(
  Effect.catchIf(GitHubError.hasKind("rateLimited"), (error) => Effect.logWarning(`retry after ${error.retryAfterMillis}ms`)),
);
```

GitHub的验证条目为 `validation`每个 `GitHubValidationEntry` 与 `resource`, (中文(简体) ), `field`, (中文(简体) ), `code` 财务报告和审定财务报表 `message` GitHub派来的 仅 `already_exists` 获得一个 `kind` (b) 其他有文件记载的编码(`GitHubValidationCode`),分支与 `hasValidationCode` 而不是阅读 `reason`数字 :

```ts
import { GitHubError } from "@effected/github";
import { Effect } from "effect";

declare const createRelease: Effect.Effect<void, GitHubError>;

const program = createRelease.pipe(
  Effect.catchIf(GitHubError.hasValidationCode("missing_field", "invalid"), (error) =>
    Effect.logError(`bad release parameters: ${error.validation?.map((entry) => entry.field).join(", ")}`),
  ),
);
```

重试处理一次, 在客户端中: `RetryPolicy.default` 重试a `transport` 或者说 `rateLimited` 失败的全速退后, 尊重 GitHub 的 `retry-after` 顶端为可配置的天花板. `RetryPolicy.none` 关闭它。 A 类 `notFound`, (中文(简体) ), `rejected` 或者说 `unauthorized` 失败从不重复——它不能改变在尝试之间的想法.

## 测试

每个资源船 `makeTest(overrides?)` 财务报告和审定财务报表 `layerTest(overrides?)`:让成员进行测试练习,其他每个成员都会自行取名,所以测试证明它碰不到什么,它没有抽筋:

```ts
import { GitBranch } from "@effected/github";
import { Effect } from "effect";

const TestBranches = GitBranch.layerTest({
  upsert: () => Effect.succeed("created"),
});
```

`GitHubClient.layerFixture(fixtures)` 真实的双页:它构建一个 `PageSource` 在记录的阵列上,把它交给 活的客户端使用的同一页码引擎, 所以一个截取路径在测试和生产中 表现相同。 它的合约有三件事值得你先知道 然后再写信给它:

```ts
import { GitHubClient, GitHubError } from "@effected/github";

const fixtures = {
  request: {
    "GET /repos/{owner}/{repo}": { default_branch: "main" },
    // A recorded GitHubError *is* the response: this route fails with it.
    "PATCH /repos/{owner}/{repo}": GitHubError.notFound("updateSettings", "repo"),
  },
  paginate: { "GET /repos/{owner}/{repo}/rulesets": [{ id: 1, name: "main", source_type: "Repository" }] },
  requested: [], // filled in as the test runs
};

const TestClient = GitHubClient.layerFixture(fixtures);
// A route with no entry above DIES naming itself, rather than failing typed.
```

-   **一条没有被怀疑的路由默认死亡.** 缺失的固定是测试接线而不是域结果,打入的失败只在代码中发出响亮而不能抓取——程序处理 `GitHubError` 每个资源都会将缺失的根变成不同的执行路径,然后由于命名为不固定的原因,断言失败. `unstubbed: "fail"` 键入未找到的路由失败,以及 `"empty"` 对于一个以决定而不是终点为主题的套房来说,这是一个空值.
-   **记录 `GitHubError` 值是响应.** 套房就是用404分 422分 或限价的方式 在某条路由外倾只说"无线";一个有记录的错误说出哪条路由失败和原因.
-   **`fixtures.requested` 记录每通电话.** 每个 `RecordedCall` 携带 `kind` 用于表面的 `route` (文档名称为: `graphql`页:1 `params` 电话是用,而且 `perPage` 读一个标注。 params就是让测试证明什么方法 _发送_,这是任何正常写作都开启的问题.

## 特征

-   `GitHubClient` - 型号运输: `request`, (中文(简体) ), `requestDecoded` (用于生成地图以外的路径的强制-中程逃生舱口), `paginate` 编号 : `paginateStream`, (中文(简体) ), `graphql`,以及 `rateLimit` (只观察——这里没有代表你的油门).
-   `Repo` 编号 : `RepoRef` - 那个 `{ owner, repo }` 协调,通过 `R`,与 `Repo.provide` 用于多存储程序.
-   `GitHubRepository` —— 寄存器作为 GitHub 自己生成的类型设置, + `defaultBranch`, (中文(简体) ), `nodeId`, (中文(简体) ), `ownerType` 表示只组织字段, `applySettings` 报告它实际发送的密钥,以及 `topics` 编号 : `setTopics(names)`时 `setTopics` 替换整个集并返回 GitHub 存储的内容(小写).
-   `repositoryPatch` 编号 : `RepositoryPatchDraft` 建立一个 `updateSettings` 从可能存在的字段补丁 `undefined`下页 `exactOptionalPropertyTypes`没有演员.
-   `GitHubError` 编号 : `GitHubGraphQLError` ——每次运输一次出错, `kind`\- 开始与 `hasKind` 页:1 `Effect.catchIf`a REST 422的验证条目 `validation`,与 `hasValidationCode`。 。 。 .
-   `RetryPolicy` ——客户端的一项重试政策:全速回放,服务器推荐的延迟被按到上限.
-   `GitHubApp` - 应用JWT签名、安装代币薄荷/撤销、应用和安装身份,以及 `clientLayer` 用于认证 `GitHubClient`。 。 。 .
-   `GitHubUserAuth` – OAuth 用作用户的网络流量: PKCE 代码交换,刷新, 并批准取消 fright `fetch`,失败可分为最终(`remedy: "reconnect"` 或者说 `"reconfigure"`)和临时的.
-   `GitBranch` 编号 : `GitTag` —— Git Database API refers, 与 `upsert` 将创建或重置的舞蹈倒向一个呼叫 `GitTag.latestSemver` 在一个通道中选择最新版本的标签.
-   `CheckRun` —— 说 `withCheckRun` 在每个出口路径上结束; `CheckRunOutput.truncated()` cut 输出到 GitHub 字节限制 .
-   `PullRequest` 编号 : `PullRequestComment` - 对两者都有好处, `listFiles` 用同样的满分回答 `CommitFile` 记录承诺读取返回, `headSha`编号 :`baseSha` 打开 `PullRequestInfo`附加时 `CommentMarker` 重新找到一个黏糊糊的评论. `PullRequestInfo.mergeCommitSha` 从图QL 读取 `mergeCommit.oid` 仅用于合并拉动请求,因为 REST 对象不再带有: `get`, (中文(简体) ), `update` 财务报告和审定财务报表 `upsert` 总是填满它,而 `list` 财务报告和审定财务报表 `listAssociatedWithCommit` 只填满 `mergeCommits: true` (每100个合并拉力请求一个GraphQL呼叫).
-   `GitHubIssue` 问题及其评论,包括: `commentOnce` 对于一个贴出一次且从未编辑过的标记性评论,以及 `linkedIssues` 为了什么拉拉要求关闭.
-   `harvestIssueReferences` 编号 : `parseBareLineReference` ——移动到两个收尾参考方言的相容性再导出 `@effected/github-references`,与 `CLOSING_KEYWORDS` 及其结果类型.
-   `GitHubRelease` - 发布和资产上传,包括一条路线(`uploadAsset`,带有端点的可选显示标签) GitHub 生成的端点地图外.
-   `RepositorySecret` 编号 : `RepositoryVariable` ——寄存器及环境机密和变量,被封存的盒加密了GitHub的秘密API的要求保存在一个模块中,没有其他导入的内容.
-   `Ruleset` 编号 : `DeploymentEnvironment` 编号 : `RepositorySecurity` 编号 : `CodeScanning` ——其余的配置等级:由名称和范围相匹配的规则,一元环境写作,三相安全切换 GitHub 保持了寄存器端点外,并用门口的语言检测设置了 CodeQL 默认设置.
-   `WorkflowDispatch` - 开火a `workflow_dispatch` 事件, `list` 与状态字符串 GitHub 报告,或发送和等待它创建的运行.
-   `BotIdentity` ——作者并犯"一瓶"罪,如: `signoff` 通过 Git Data API 实现的 DCO 拖车 `git commit -s`。 。 。 .
-   `Attestation` - 上传和列出针对主题摘要的证明;建立和签署捆绑是 `@effected/sbom`这是工作.
-   `TokenPermissions` ——纯粹的参照者,介于被授予的许可和需要的许可之间,只达到 `effect`。 。 。 .

## 许可证

[麻省理工学院](https://npmcn.com/package/%40effected/github/LICENSE)

## 常见问题

### @effected/github 是什么？

在octokit核心请求表面上键入了 GitHub REST 和 GraphQL 服务, 并配备了应用认证和资源帮助器 \.

### 如何安装 @effected/github？

在项目目录运行 npm install @effected/github，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：effect（~4\.1\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @effected/github 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @effected/github 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @effected/github 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=24\.11\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @effected/github 可以商用吗？

@effected/github 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @effected/github 的下载量有多少？

根据 npm 官方下载统计，@effected/github 在 2026/10/13 – 2026/11/11 这 30 天内被下载 16,339 次，日均约 545 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @effected/github 还在维护吗？

最新版本 0\.32\.1 发布于 2026/11/11（2 天前）；至今共发布 45 个版本，其中过去 12 个月发布了 45 个；首次发布于 2026/07/26。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

