@theholocron/github-client 1.46.0 → 1.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.mts +32 -1
- package/dist/index.mjs +59 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ const blob = await client.git.createBlob("owner/name", "file contents");
|
|
|
50
50
|
| `labels` | `listLabels`, `createLabel`, `updateLabel`, `deleteLabel` |
|
|
51
51
|
| `topics` | `setTopics` |
|
|
52
52
|
| `properties` | `setProperties` |
|
|
53
|
-
| `git` | `getRef`, `getCommit`, `getTree`, `getContents`, `createBlob`, `createTree`, `createCommit`, `createRef`, `updateRef`, `createPull`
|
|
53
|
+
| `git` | `getRef`, `getCommit`, `getTree`, `getContents`, `putContents`, `createBlob`, `createTree`, `createCommit`, `createRef`, `updateRef`, `createPull` |
|
|
54
54
|
| `checks` | `createCheckRun` |
|
|
55
55
|
|
|
56
56
|
GitHub accepts at most 50 annotations per check-run request. Slice with the
|
package/dist/index.d.mts
CHANGED
|
@@ -131,6 +131,29 @@ interface GitContents {
|
|
|
131
131
|
name: string;
|
|
132
132
|
path: string;
|
|
133
133
|
}
|
|
134
|
+
/** What `git.putContents` returns: the file's new blob sha (the `sha` to pass next time) and the commit it made. */
|
|
135
|
+
interface PutContentsResult {
|
|
136
|
+
content: {
|
|
137
|
+
sha: string;
|
|
138
|
+
path: string;
|
|
139
|
+
};
|
|
140
|
+
commit: {
|
|
141
|
+
sha: string;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
interface PutContentsInput {
|
|
145
|
+
message: string;
|
|
146
|
+
/** The new file text (UTF-8); the client encodes it. */
|
|
147
|
+
content: string;
|
|
148
|
+
/**
|
|
149
|
+
* The blob `sha` of the file being replaced, from `getContents`. Required to update an existing file,
|
|
150
|
+
* and omitted to create one. This is what makes the write safe: if someone changed the file since it was
|
|
151
|
+
* read, GitHub refuses (409) instead of overwriting, so the caller can read again and retry.
|
|
152
|
+
*/
|
|
153
|
+
sha?: string;
|
|
154
|
+
/** Defaults to the repo's default branch. */
|
|
155
|
+
branch?: string;
|
|
156
|
+
}
|
|
134
157
|
interface GitBlob {
|
|
135
158
|
sha: string;
|
|
136
159
|
url: string;
|
|
@@ -361,6 +384,12 @@ interface CodeScanningSetupResult {
|
|
|
361
384
|
//#endregion
|
|
362
385
|
//#region ../../../../../src/teams/teams.d.ts
|
|
363
386
|
type TeamPermission = "pull" | "triage" | "push" | "maintain" | "admin";
|
|
387
|
+
/** A member of a team, as GitHub lists it. `id` is the numeric user id, which survives a rename; `login` does not. */
|
|
388
|
+
interface TeamMember {
|
|
389
|
+
id: number;
|
|
390
|
+
login: string;
|
|
391
|
+
type?: string;
|
|
392
|
+
}
|
|
364
393
|
//#endregion
|
|
365
394
|
//#region ../../../../../src/user/user.d.ts
|
|
366
395
|
interface GitHubUser {
|
|
@@ -502,6 +531,7 @@ declare function createGitHubClient(opts: GitHubClientOptions): {
|
|
|
502
531
|
getCommit: (repo: string, sha: string) => Promise<GitCommit>;
|
|
503
532
|
getTree: (repo: string, sha: string, recursive?: boolean) => Promise<GitTree>;
|
|
504
533
|
getContents: (repo: string, path: string, ref?: string) => Promise<GitContents>;
|
|
534
|
+
putContents: (repo: string, path: string, input: PutContentsInput) => Promise<PutContentsResult>;
|
|
505
535
|
createBlob: (repo: string, content: string, encoding?: string) => Promise<GitBlob>;
|
|
506
536
|
createTree: (repo: string, tree: Array<{
|
|
507
537
|
path: string;
|
|
@@ -581,6 +611,7 @@ declare function createGitHubClient(opts: GitHubClientOptions): {
|
|
|
581
611
|
disableDefaultCodeScanning: (repo: string) => Promise<void>;
|
|
582
612
|
};
|
|
583
613
|
teams: {
|
|
614
|
+
members: (org: string, teamSlug: string) => Promise<TeamMember[]>;
|
|
584
615
|
addRepo: (org: string, teamSlug: string, owner: string, repo: string, permission: TeamPermission) => Promise<void>;
|
|
585
616
|
};
|
|
586
617
|
topics: {
|
|
@@ -597,4 +628,4 @@ declare function createGitHubClient(opts: GitHubClientOptions): {
|
|
|
597
628
|
};
|
|
598
629
|
type GitHubClient = ReturnType<typeof createGitHubClient>;
|
|
599
630
|
//#endregion
|
|
600
|
-
export { type CheckRunAnnotation, type CheckRunAnnotationLevel, type CheckRunConclusion, type CheckRunOutput, type CheckRunStatus, type CodeScanningSetupResult, type CreateCheckRunInput, type CreatePagesPayload, type CreatePullInput, type CreateReviewComment, type CreateReviewInput, type GitBlob, type GitCommit, type GitContents, type GitHubAppCredentials, type GitHubCheckRun, GitHubClient, type GitHubClientOptions, type GitHubContents, type GitHubEnvironment, type GitHubInstallationWebhookPayload, type GitHubIssue, type GitHubLabel, type GitHubMilestone, type GitHubPages, type GitHubPublicKey, type GitHubPullRequest, type GitHubPullRequestCommit, type GitHubPullRequestFile, type GitHubPullRequestReview, type GitHubPullRequestWebhookPayload, type GitHubPushWebhookPayload, type GitHubRepo, type GitHubRuleset, type GitHubUser, type GitHubWebhookHeaders, type GitHubWorkflowRun, type GitPull, type GitRef, type GitTree, type GitTreeItem, type InstallationAccessToken, type IssueSearchParams, MAX_CHECK_RUN_ANNOTATIONS, type PagesBuildStatus, type PagesBuildType, type ReviewThread, type SecretScope, type TeamPermission, type UpdateCheckRunInput, type UpdatePagesPayload, type WorkflowRunFilter, createAppJWT, createGitHubClient, createInstallationClient, getInstallationAccessToken, parseGitHubWebhookHeaders, verifyGitHubWebhookSignature };
|
|
631
|
+
export { type CheckRunAnnotation, type CheckRunAnnotationLevel, type CheckRunConclusion, type CheckRunOutput, type CheckRunStatus, type CodeScanningSetupResult, type CreateCheckRunInput, type CreatePagesPayload, type CreatePullInput, type CreateReviewComment, type CreateReviewInput, type GitBlob, type GitCommit, type GitContents, type GitHubAppCredentials, type GitHubCheckRun, GitHubClient, type GitHubClientOptions, type GitHubContents, type GitHubEnvironment, type GitHubInstallationWebhookPayload, type GitHubIssue, type GitHubLabel, type GitHubMilestone, type GitHubPages, type GitHubPublicKey, type GitHubPullRequest, type GitHubPullRequestCommit, type GitHubPullRequestFile, type GitHubPullRequestReview, type GitHubPullRequestWebhookPayload, type GitHubPushWebhookPayload, type GitHubRepo, type GitHubRuleset, type GitHubUser, type GitHubWebhookHeaders, type GitHubWorkflowRun, type GitPull, type GitRef, type GitTree, type GitTreeItem, type InstallationAccessToken, type IssueSearchParams, MAX_CHECK_RUN_ANNOTATIONS, type PagesBuildStatus, type PagesBuildType, type PutContentsInput, type PutContentsResult, type ReviewThread, type SecretScope, type TeamMember, type TeamPermission, type UpdateCheckRunInput, type UpdatePagesPayload, type WorkflowRunFilter, createAppJWT, createGitHubClient, createInstallationClient, getInstallationAccessToken, parseGitHubWebhookHeaders, verifyGitHubWebhookSignature };
|
package/dist/index.mjs
CHANGED
|
@@ -58,6 +58,24 @@ function splitRepo(repo) {
|
|
|
58
58
|
const [owner, name] = repo.split("/", 2);
|
|
59
59
|
return [owner ?? "", name ?? ""];
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* One URL path segment from a caller-supplied name (an org, a team slug). `.` and `..` are refused because
|
|
63
|
+
* `new URL` would resolve them, sending the authenticated request to a different resource.
|
|
64
|
+
*/
|
|
65
|
+
function segment(value) {
|
|
66
|
+
if (value === "" || value === "." || value === "..") throw new Error(`invalid path segment: ${JSON.stringify(value)}`);
|
|
67
|
+
return encodeURIComponent(value);
|
|
68
|
+
}
|
|
69
|
+
/** A repo file path with each segment encoded, so `?` and `#` stay part of the name and `..` cannot climb out. */
|
|
70
|
+
function filePath(path) {
|
|
71
|
+
return path.split("/").map(segment).join("/");
|
|
72
|
+
}
|
|
73
|
+
/** UTF-8 text as base64 without `Buffer`, so it runs in Workers and browsers as well as Node. */
|
|
74
|
+
function toBase64(text) {
|
|
75
|
+
let binary = "";
|
|
76
|
+
for (const byte of new TextEncoder().encode(text)) binary += String.fromCharCode(byte);
|
|
77
|
+
return btoa(binary);
|
|
78
|
+
}
|
|
61
79
|
//#endregion
|
|
62
80
|
//#region ../../../../../src/branches/branches.ts
|
|
63
81
|
function branches(rest) {
|
|
@@ -119,6 +137,19 @@ function git(rest) {
|
|
|
119
137
|
* (D6-amended, `tech-sentinel-ci-runner.spec.md`), just never a fork's.
|
|
120
138
|
*/
|
|
121
139
|
getContents: (repo, path, ref) => rest.request(`${repoBase(repo)}/contents/${path}`, ref ? { query: { ref } } : void 0),
|
|
140
|
+
/**
|
|
141
|
+
* `PUT /repos/:repo/contents/:path` — creates or updates one file in a single commit. Pass `sha` when
|
|
142
|
+
* updating so a concurrent change is refused (409) and not silently overwritten.
|
|
143
|
+
*/
|
|
144
|
+
putContents: (repo, path, input) => rest.request(`${repoBase(repo)}/contents/${filePath(path)}`, {
|
|
145
|
+
method: "PUT",
|
|
146
|
+
body: {
|
|
147
|
+
message: input.message,
|
|
148
|
+
content: toBase64(input.content),
|
|
149
|
+
...input.sha ? { sha: input.sha } : {},
|
|
150
|
+
...input.branch ? { branch: input.branch } : {}
|
|
151
|
+
}
|
|
152
|
+
}),
|
|
122
153
|
createBlob: (repo, content, encoding = "utf-8") => rest.request(`${repoBase(repo)}/git/blobs`, {
|
|
123
154
|
method: "POST",
|
|
124
155
|
body: {
|
|
@@ -456,11 +487,35 @@ function security(rest) {
|
|
|
456
487
|
}
|
|
457
488
|
//#endregion
|
|
458
489
|
//#region ../../../../../src/teams/teams.ts
|
|
490
|
+
const PAGE = 100;
|
|
491
|
+
/** A guard so a runaway listing cannot loop forever: 50 pages of 100 is far more than any team. */
|
|
492
|
+
const MAX_PAGES = 50;
|
|
459
493
|
function teams(rest) {
|
|
460
|
-
return {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
494
|
+
return {
|
|
495
|
+
/**
|
|
496
|
+
* Every member of a team (maintainers included), read page by page. Members of child teams are
|
|
497
|
+
* included, as GitHub does. A 404 means the team does not exist or the token cannot see it.
|
|
498
|
+
*/
|
|
499
|
+
members: async (org, teamSlug) => {
|
|
500
|
+
const path = `/orgs/${segment(org)}/teams/${segment(teamSlug)}/members`;
|
|
501
|
+
const read = (page) => rest.request(path, { query: {
|
|
502
|
+
per_page: String(PAGE),
|
|
503
|
+
page: String(page)
|
|
504
|
+
} });
|
|
505
|
+
const all = [];
|
|
506
|
+
for (let page = 1; page <= MAX_PAGES; page++) {
|
|
507
|
+
const batch = await read(page);
|
|
508
|
+
all.push(...batch);
|
|
509
|
+
if (batch.length < PAGE) return all;
|
|
510
|
+
}
|
|
511
|
+
if ((await read(51)).length > 0) throw new Error(`team ${org}/${teamSlug} has more than 5000 members; refusing to read them all`);
|
|
512
|
+
return all;
|
|
513
|
+
},
|
|
514
|
+
addRepo: (org, teamSlug, owner, repo, permission) => rest.request(`/orgs/${org}/teams/${teamSlug}/repos/${owner}/${repo}`, {
|
|
515
|
+
method: "PUT",
|
|
516
|
+
body: { permission }
|
|
517
|
+
})
|
|
518
|
+
};
|
|
464
519
|
}
|
|
465
520
|
//#endregion
|
|
466
521
|
//#region ../../../../../src/topics/topics.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/github-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.47.0",
|
|
4
4
|
"description": "A TypeScript client for the GitHub REST API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@theholocron/observability": "^0.3.0",
|
|
36
|
-
"@theholocron/http-client": "^1.
|
|
36
|
+
"@theholocron/http-client": "^1.47.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@theholocron/cli": "5.0.0-alpha.79",
|