@theholocron/github-client 1.58.0-alpha.12 → 1.58.0-alpha.14
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/index.d.mts +86 -1
- package/dist/index.mjs +73 -1
- package/package.json +6 -6
package/dist/index.d.mts
CHANGED
|
@@ -15,6 +15,10 @@ import { ErrorSink, Logger } from "@theholocron/observability/core";
|
|
|
15
15
|
* which transport they're talking to.
|
|
16
16
|
*/
|
|
17
17
|
interface GraphQLClient {
|
|
18
|
+
/**
|
|
19
|
+
* Sends one query or mutation to `POST /graphql` and returns its `data`. Throws a `ProviderApiError` (status `200`, with the
|
|
20
|
+
* response's `errors` array) when GitHub answers with errors, and the transport's own `ProviderApiError` for a network or HTTP failure.
|
|
21
|
+
*/
|
|
18
22
|
query<T>(document: string, variables?: Record<string, unknown>): Promise<T>;
|
|
19
23
|
}
|
|
20
24
|
/** Options for creating a GitHub client. */
|
|
@@ -115,6 +119,8 @@ interface GitHubCheckRun {
|
|
|
115
119
|
conclusion: CheckRunConclusion | null;
|
|
116
120
|
/** The URL of the check run's page on GitHub. */
|
|
117
121
|
html_url: string;
|
|
122
|
+
/** The check's own "Details" link, when it set one. */
|
|
123
|
+
details_url?: string | null;
|
|
118
124
|
}
|
|
119
125
|
/** The body of an `updateCheckRun()` request; every field is optional. */
|
|
120
126
|
interface UpdateCheckRunInput {
|
|
@@ -433,6 +439,40 @@ interface GitHubPullRequest {
|
|
|
433
439
|
/** The head commit. */
|
|
434
440
|
sha: string;
|
|
435
441
|
};
|
|
442
|
+
/** Whether the pull request is a draft. */
|
|
443
|
+
draft?: boolean;
|
|
444
|
+
/** When the pull request was opened, as an ISO 8601 timestamp. */
|
|
445
|
+
created_at?: string;
|
|
446
|
+
/** The author. */
|
|
447
|
+
user?: {
|
|
448
|
+
/** The author's login. */
|
|
449
|
+
login: string;
|
|
450
|
+
/** The account type: `User` or `Bot` (Dependabot is a `Bot`). */
|
|
451
|
+
type: string;
|
|
452
|
+
};
|
|
453
|
+
/** Who merged it; `null` for a pull request that is not merged. */
|
|
454
|
+
merged_by?: {
|
|
455
|
+
login: string;
|
|
456
|
+
} | null;
|
|
457
|
+
/** The base branch. */
|
|
458
|
+
base?: {
|
|
459
|
+
/** The base branch's name, e.g. `alpha`. */
|
|
460
|
+
ref: string;
|
|
461
|
+
};
|
|
462
|
+
/** The people whose review is still asked for. */
|
|
463
|
+
requested_reviewers?: {
|
|
464
|
+
login: string;
|
|
465
|
+
}[];
|
|
466
|
+
/** Lines added, as the single-pull-request endpoint returns it (the list endpoint omits it). */
|
|
467
|
+
additions?: number;
|
|
468
|
+
/** Lines removed; see `additions`. */
|
|
469
|
+
deletions?: number;
|
|
470
|
+
/** Files changed; see `additions`. */
|
|
471
|
+
changed_files?: number;
|
|
472
|
+
/** Whether it merges cleanly; `null` while GitHub is still working it out. */
|
|
473
|
+
mergeable?: boolean | null;
|
|
474
|
+
/** `dirty` means it conflicts with its base; see `mergeable`. */
|
|
475
|
+
mergeable_state?: string;
|
|
436
476
|
}
|
|
437
477
|
/** A commit's name/email as git itself recorded it — `commit.author`/`commit.committer`, distinct from the GitHub-account `author` below (a commit can carry any email, linked to a GitHub account or not). */
|
|
438
478
|
interface GitHubCommitIdentity {
|
|
@@ -511,6 +551,12 @@ interface GitHubPullRequestReview {
|
|
|
511
551
|
id: number;
|
|
512
552
|
/** The URL of the review on GitHub. */
|
|
513
553
|
html_url: string;
|
|
554
|
+
/** `APPROVED`, `CHANGES_REQUESTED`, `COMMENTED`, `DISMISSED` or `PENDING`; set on a review read back. */
|
|
555
|
+
state?: string;
|
|
556
|
+
/** The reviewer; set on a review read back. */
|
|
557
|
+
user?: {
|
|
558
|
+
login: string;
|
|
559
|
+
} | null;
|
|
514
560
|
}
|
|
515
561
|
/**
|
|
516
562
|
* One review thread — GraphQL-only (`resolveReviewThread`, the mutation
|
|
@@ -546,6 +592,8 @@ interface GitHubRepo {
|
|
|
546
592
|
default_branch: string;
|
|
547
593
|
/** The repository's `owner/name`. */
|
|
548
594
|
full_name: string;
|
|
595
|
+
/** Whether the repository is archived (read-only). */
|
|
596
|
+
archived?: boolean;
|
|
549
597
|
/** The repository description. */
|
|
550
598
|
description?: string | null;
|
|
551
599
|
/** The repository homepage URL. */
|
|
@@ -751,6 +799,41 @@ export declare function createAppJWT(creds: GitHubAppCredentials, now?: number):
|
|
|
751
799
|
export declare function getInstallationAccessToken(creds: GitHubAppCredentials, installationId: number, opts?: Pick<GitHubClientOptions, "baseUrl" | "fetch">): Promise<InstallationAccessToken>;
|
|
752
800
|
/** The whole flow in one call — an already-authenticated `GitHubClient` scoped to one installation. */
|
|
753
801
|
export declare function createInstallationClient(creds: GitHubAppCredentials, installationId: number, opts?: Pick<GitHubClientOptions, "baseUrl" | "fetch">): Promise<GitHubClient>;
|
|
802
|
+
/** The account an App or one of its installations belongs to. */
|
|
803
|
+
interface GitHubAppAccount {
|
|
804
|
+
/** The user or organization login. */
|
|
805
|
+
login: string;
|
|
806
|
+
/** `User` or `Organization` (GitHub also reports `Enterprise` for an enterprise-owned App). */
|
|
807
|
+
type: string;
|
|
808
|
+
}
|
|
809
|
+
/** An App as `GET /app` reports it. */
|
|
810
|
+
interface GitHubApp {
|
|
811
|
+
/** The App's URL name, as in `github.com/apps/<slug>`. */
|
|
812
|
+
slug: string;
|
|
813
|
+
/** The App's display name. */
|
|
814
|
+
name: string;
|
|
815
|
+
/** Who owns the App: decides which settings page its permissions live on. */
|
|
816
|
+
owner: GitHubAppAccount | null;
|
|
817
|
+
/** The permissions the App asks for now: name to `read`, `write` or `admin`. */
|
|
818
|
+
permissions: Record<string, string>;
|
|
819
|
+
/** The webhook events the App is subscribed to now. */
|
|
820
|
+
events: string[];
|
|
821
|
+
}
|
|
822
|
+
/** One installation of an App as `GET /app/installations` reports it. */
|
|
823
|
+
interface GitHubAppInstallation {
|
|
824
|
+
/** The installation id. */
|
|
825
|
+
id: number;
|
|
826
|
+
/** The account the App is installed on. */
|
|
827
|
+
account: GitHubAppAccount | null;
|
|
828
|
+
/** What this installation has accepted: it lags the App's own list until an owner accepts a new request. */
|
|
829
|
+
permissions: Record<string, string>;
|
|
830
|
+
/** The events delivered to this installation. */
|
|
831
|
+
events: string[];
|
|
832
|
+
}
|
|
833
|
+
/** The App this JWT identifies: its slug, owner and the permissions and events it asks for now. */
|
|
834
|
+
export declare function getApp(creds: GitHubAppCredentials, opts?: Pick<GitHubClientOptions, "baseUrl" | "fetch">): Promise<GitHubApp>;
|
|
835
|
+
/** Every installation of the App this JWT identifies, with what each has accepted. Reads all pages. */
|
|
836
|
+
export declare function listAppInstallations(creds: GitHubAppCredentials, opts?: Pick<GitHubClientOptions, "baseUrl" | "fetch">): Promise<GitHubAppInstallation[]>;
|
|
754
837
|
/**
|
|
755
838
|
* GitHub's own webhook mechanics — signature verification and header/
|
|
756
839
|
* payload shapes. Pure functions, no REST call, no auth token: these
|
|
@@ -855,6 +938,7 @@ export declare function createGitHubClient(opts: GitHubClientOptions): {
|
|
|
855
938
|
removeBranch: (repo: string, branch: string) => Promise<void>;
|
|
856
939
|
};
|
|
857
940
|
checks: {
|
|
941
|
+
listCheckRunsForRef: (repo: string, ref: string) => Promise<GitHubCheckRun[]>;
|
|
858
942
|
createCheckRun: (repo: string, input: CreateCheckRunInput) => Promise<GitHubCheckRun>;
|
|
859
943
|
updateCheckRun: (repo: string, checkRunId: number, input: UpdateCheckRunInput) => Promise<GitHubCheckRun>;
|
|
860
944
|
};
|
|
@@ -924,6 +1008,7 @@ export declare function createGitHubClient(opts: GitHubClientOptions): {
|
|
|
924
1008
|
listPullRequests: (repo: string, state: "open" | "closed") => Promise<GitHubPullRequest[]>;
|
|
925
1009
|
listCommits: (repo: string, number: number) => Promise<GitHubPullRequestCommit[]>;
|
|
926
1010
|
listFiles: (repo: string, number: number) => Promise<GitHubPullRequestFile[]>;
|
|
1011
|
+
listReviews: (repo: string, number: number) => Promise<GitHubPullRequestReview[]>;
|
|
927
1012
|
createReview: (repo: string, number: number, input: CreateReviewInput) => Promise<GitHubPullRequestReview>;
|
|
928
1013
|
listReviewThreads: (repo: string, number: number) => Promise<ReviewThread[]>;
|
|
929
1014
|
resolveReviewThread: (threadId: string) => Promise<void>;
|
|
@@ -985,4 +1070,4 @@ export declare function createGitHubClient(opts: GitHubClientOptions): {
|
|
|
985
1070
|
};
|
|
986
1071
|
/** The client returned by `createGitHubClient()`: one group of methods per GitHub API area. */
|
|
987
1072
|
export type GitHubClient = ReturnType<typeof createGitHubClient>;
|
|
988
|
-
export type { CheckRunAnnotation, CheckRunAnnotationLevel, CheckRunConclusion, CheckRunOutput, CheckRunStatus, CodeScanningSetupResult, CreateCheckRunInput, CreatePagesPayload, CreatePullInput, CreateReviewComment, CreateReviewInput, GitBlob, GitCommit, GitContents, GitDirectoryEntry, GitHubAppCredentials, GitHubCheckRun, GitHubClientOptions, GitHubContents, GitHubEnvironment, GitHubInstallationWebhookPayload, GitHubIssue, GitHubLabel, GitHubMilestone, GitHubPages, GitHubPublicKey, GitHubPullRequest, GitHubPullRequestCommit, GitHubPullRequestFile, GitHubPullRequestReview, GitHubPullRequestWebhookPayload, GitHubPushWebhookPayload, GitHubRepo, GitHubRuleset, GitHubRulesetDetail, GitHubUser, GitHubUserWithExpiry, GitHubVariable, GitHubWebhookHeaders, GitHubWorkflowRun, GitPull, GitRef, GitTree, GitTreeItem, GraphQLClient, InstallationAccessToken, IssueSearchParams, ListDirectoryOptions, OrgProperty, PagesBuildStatus, PagesBuildType, PutContentsInput, PutContentsResult, RepoPropertyValue, RepoTeam, ReviewThread, SecretScope, TeamMember, TeamPermission, UpdateCheckRunInput, UpdatePagesPayload, VariableScope, VariableVisibility, WorkflowRunFilter };
|
|
1073
|
+
export type { CheckRunAnnotation, CheckRunAnnotationLevel, CheckRunConclusion, CheckRunOutput, CheckRunStatus, CodeScanningSetupResult, CreateCheckRunInput, CreatePagesPayload, CreatePullInput, CreateReviewComment, CreateReviewInput, GitBlob, GitCommit, GitContents, GitDirectoryEntry, GitHubApp, GitHubAppAccount, GitHubAppCredentials, GitHubAppInstallation, GitHubCheckRun, GitHubClientOptions, GitHubContents, GitHubEnvironment, GitHubInstallationWebhookPayload, GitHubIssue, GitHubLabel, GitHubMilestone, GitHubPages, GitHubPublicKey, GitHubPullRequest, GitHubPullRequestCommit, GitHubPullRequestFile, GitHubPullRequestReview, GitHubPullRequestWebhookPayload, GitHubPushWebhookPayload, GitHubRepo, GitHubRuleset, GitHubRulesetDetail, GitHubUser, GitHubUserWithExpiry, GitHubVariable, GitHubWebhookHeaders, GitHubWorkflowRun, GitPull, GitRef, GitTree, GitTreeItem, GraphQLClient, InstallationAccessToken, IssueSearchParams, ListDirectoryOptions, OrgProperty, PagesBuildStatus, PagesBuildType, PutContentsInput, PutContentsResult, RepoPropertyValue, RepoTeam, ReviewThread, SecretScope, TeamMember, TeamPermission, UpdateCheckRunInput, UpdatePagesPayload, VariableScope, VariableVisibility, WorkflowRunFilter };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ProviderApiError, createRestClient } from "@theholocron/http-client";
|
|
2
2
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
3
3
|
// src/graphql/graphql.ts
|
|
4
|
+
/** A {@link GraphQLClient} for GitHub's GraphQL API, built on the shared REST transport. */
|
|
4
5
|
function createGitHubGraphQLClient(config) {
|
|
5
6
|
const rest = createRestClient({
|
|
6
7
|
baseUrl: config.baseUrl ?? "https://api.github.com",
|
|
@@ -107,8 +108,21 @@ function branches(rest) {
|
|
|
107
108
|
* do for you; slice with it rather than hardcoding the number.
|
|
108
109
|
*/
|
|
109
110
|
const MAX_CHECK_RUN_ANNOTATIONS = 50;
|
|
111
|
+
/** Page size and the most pages {@link checks}`.listCheckRunsForRef` reads: 100 a page over 10 pages, so 1,000 check runs. */
|
|
112
|
+
const CHECK_RUN_PAGE_SIZE = 100;
|
|
113
|
+
const CHECK_RUN_MAX_PAGES = 10;
|
|
110
114
|
function checks(rest) {
|
|
111
115
|
return {
|
|
116
|
+
/** Every check run on a commit, read a page at a time until a page comes back short. */
|
|
117
|
+
listCheckRunsForRef: async (repo, ref) => {
|
|
118
|
+
const all = [];
|
|
119
|
+
for (let page = 1; page <= CHECK_RUN_MAX_PAGES; page++) {
|
|
120
|
+
const batch = await rest.request(`${repoBase(repo)}/commits/${ref}/check-runs?per_page=${CHECK_RUN_PAGE_SIZE}&page=${page}`);
|
|
121
|
+
all.push(...batch.check_runs);
|
|
122
|
+
if (batch.check_runs.length < CHECK_RUN_PAGE_SIZE) break;
|
|
123
|
+
}
|
|
124
|
+
return all;
|
|
125
|
+
},
|
|
112
126
|
createCheckRun: (repo, input) => rest.request(`${repoBase(repo)}/check-runs`, {
|
|
113
127
|
method: "POST",
|
|
114
128
|
body: input
|
|
@@ -413,6 +427,8 @@ function pulls(rest, graphql) {
|
|
|
413
427
|
* is an edge case no consumer here needs to paginate for today.
|
|
414
428
|
*/
|
|
415
429
|
listFiles: (repo, number) => rest.request(`${repoBase(repo)}/pulls/${number}/files?per_page=100`),
|
|
430
|
+
/** A PR's reviews, oldest first. `per_page=100` covers every real PR in this org. */
|
|
431
|
+
listReviews: (repo, number) => rest.request(`${repoBase(repo)}/pulls/${number}/reviews?per_page=100`),
|
|
416
432
|
/**
|
|
417
433
|
* Submits a review in one call — GitHub has no separate "create draft,
|
|
418
434
|
* then submit" step when `event` is provided directly (the multi-step
|
|
@@ -875,6 +891,62 @@ async function createInstallationClient(creds, installationId, opts = {}) {
|
|
|
875
891
|
fetch: opts.fetch
|
|
876
892
|
});
|
|
877
893
|
}
|
|
894
|
+
// src/app-info/app-info.ts
|
|
895
|
+
/**
|
|
896
|
+
* What a GitHub App is right now, read with the App's own JWT (`GET /app`, `GET /app/installations`). GitHub has no
|
|
897
|
+
* endpoint that changes an existing App's permissions or event subscriptions, so these reads are how a caller
|
|
898
|
+
* checks the settings page was done by hand.
|
|
899
|
+
*
|
|
900
|
+
* https://docs.github.com/en/rest/apps/apps#get-the-authenticated-app
|
|
901
|
+
* https://docs.github.com/en/rest/apps/apps#list-installations-for-the-authenticated-app
|
|
902
|
+
*/
|
|
903
|
+
const PAGE_SIZE = 100;
|
|
904
|
+
const account = (value) => {
|
|
905
|
+
const { login, type } = value ?? {};
|
|
906
|
+
return typeof login === "string" ? {
|
|
907
|
+
login,
|
|
908
|
+
type: typeof type === "string" ? type : ""
|
|
909
|
+
} : null;
|
|
910
|
+
};
|
|
911
|
+
async function appGet(creds, path, opts) {
|
|
912
|
+
const jwt = await createAppJWT(creds);
|
|
913
|
+
const baseUrl = opts.baseUrl ?? "https://api.github.com";
|
|
914
|
+
const res = await (opts.fetch ?? globalThis.fetch)(`${baseUrl}${path}`, { headers: {
|
|
915
|
+
authorization: `Bearer ${jwt}`,
|
|
916
|
+
accept: "application/vnd.github+json",
|
|
917
|
+
"x-github-api-version": "2022-11-28"
|
|
918
|
+
} });
|
|
919
|
+
if (!res.ok) {
|
|
920
|
+
const body = await res.text().catch(() => "");
|
|
921
|
+
throw new ProviderApiError(`GitHub GET ${path} → ${res.status}`, res.status, body);
|
|
922
|
+
}
|
|
923
|
+
return res.json();
|
|
924
|
+
}
|
|
925
|
+
/** The App this JWT identifies: its slug, owner and the permissions and events it asks for now. */
|
|
926
|
+
async function getApp(creds, opts = {}) {
|
|
927
|
+
const raw = await appGet(creds, "/app", opts);
|
|
928
|
+
return {
|
|
929
|
+
slug: String(raw.slug),
|
|
930
|
+
name: String(raw.name),
|
|
931
|
+
owner: account(raw.owner),
|
|
932
|
+
permissions: raw.permissions ?? {},
|
|
933
|
+
events: raw.events ?? []
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
/** Every installation of the App this JWT identifies, with what each has accepted. Reads all pages. */
|
|
937
|
+
async function listAppInstallations(creds, opts = {}) {
|
|
938
|
+
const installations = [];
|
|
939
|
+
for (let page = 1;; page++) {
|
|
940
|
+
const raws = await appGet(creds, `/app/installations?per_page=${PAGE_SIZE}&page=${page}`, opts);
|
|
941
|
+
for (const raw of raws) installations.push({
|
|
942
|
+
id: Number(raw.id),
|
|
943
|
+
account: account(raw.account),
|
|
944
|
+
permissions: raw.permissions ?? {},
|
|
945
|
+
events: raw.events ?? []
|
|
946
|
+
});
|
|
947
|
+
if (raws.length < PAGE_SIZE) return installations;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
878
950
|
// src/webhooks/webhooks.ts
|
|
879
951
|
/**
|
|
880
952
|
* GitHub's own webhook mechanics — signature verification and header/
|
|
@@ -948,4 +1020,4 @@ function createGitHubClient(opts) {
|
|
|
948
1020
|
workflows: workflows(rest)
|
|
949
1021
|
};
|
|
950
1022
|
}
|
|
951
|
-
export { MAX_CHECK_RUN_ANNOTATIONS, createAppJWT, createGitHubClient, createInstallationClient, getInstallationAccessToken, parseGitHubWebhookHeaders, parseTokenExpiration, verifyGitHubWebhookSignature };
|
|
1023
|
+
export { MAX_CHECK_RUN_ANNOTATIONS, createAppJWT, createGitHubClient, createInstallationClient, getApp, getInstallationAccessToken, listAppInstallations, parseGitHubWebhookHeaders, parseTokenExpiration, verifyGitHubWebhookSignature };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/github-client",
|
|
3
|
-
"version": "1.58.0-alpha.
|
|
3
|
+
"version": "1.58.0-alpha.14",
|
|
4
4
|
"description": "A TypeScript client for the GitHub REST API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@theholocron/http-client": "^1.58.0-alpha.
|
|
35
|
+
"@theholocron/http-client": "^1.58.0-alpha.14",
|
|
36
36
|
"@theholocron/observability": "^0.7.0-alpha.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"tsdown": "^0.23.4",
|
|
46
46
|
"typescript": "^5.9.3",
|
|
47
47
|
"vitest": "^5.0.5",
|
|
48
|
-
"@theholocron/
|
|
49
|
-
"@theholocron/
|
|
50
|
-
"@theholocron/
|
|
51
|
-
"@theholocron/vitest-config": "^8.9.0-alpha.
|
|
48
|
+
"@theholocron/eslint-config": "^8.9.0-alpha.9",
|
|
49
|
+
"@theholocron/tsdown-config": "^8.9.0-alpha.9",
|
|
50
|
+
"@theholocron/tsconfig": "^8.9.0-alpha.9",
|
|
51
|
+
"@theholocron/vitest-config": "^8.9.0-alpha.9"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22.0.0"
|