@osdlc/typed-gh 0.5.0 → 0.7.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/CHANGELOG.md +61 -1
- package/CONTRIBUTING.md +4 -3
- package/README.md +247 -56
- package/dist/async.d.ts +70 -28
- package/dist/async.js +127 -44
- package/dist/doors.d.ts +82 -0
- package/dist/doors.js +183 -0
- package/dist/failure.d.ts +59 -0
- package/dist/failure.js +93 -0
- package/dist/index.d.ts +12 -8
- package/dist/index.js +9 -4
- package/dist/parse.d.ts +5 -2
- package/dist/parse.js +48 -0
- package/dist/repo.d.ts +42 -11
- package/dist/repo.js +76 -12
- package/dist/sync.d.ts +104 -28
- package/dist/sync.js +155 -46
- package/dist/types.d.ts +37 -1
- package/dist/verbs.d.ts +213 -45
- package/dist/verbs.js +504 -86
- package/package.json +13 -17
- package/async.ts +0 -285
- package/dist/async.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/parse.d.ts.map +0 -1
- package/dist/repo.d.ts.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/verbs.d.ts.map +0 -1
- package/index.ts +0 -97
- package/parse.ts +0 -252
- package/repo.ts +0 -59
- package/sync.ts +0 -326
- package/types.ts +0 -186
- package/verbs.ts +0 -799
package/dist/async.d.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `AsyncGh` — the awaitable GitHub client, with the same methods as {@link Gh}.
|
|
3
3
|
*
|
|
4
|
-
* Same
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Same three doors, promised: the plain method resolves to a value or rejects
|
|
5
|
+
* (a `FailureError` carrying the classified `GhFailure` for a failed gh or output it cannot read;
|
|
6
|
+
* a query for one thing resolves `null` when GitHub has no such thing),
|
|
7
|
+
* `try` resolves to a `Result<T, GhFailure>`, `soft` resolves `null` for a
|
|
8
|
+
* failure, and `raw`/`rawChecked` are the hatches. It is driven by the same pure verb
|
|
7
9
|
* table as `Gh`, so the two cannot drift on argv or parsing. Use it where the
|
|
8
10
|
* sync client would block the event loop: a fan-out of per-PR queries.
|
|
9
11
|
*/
|
|
10
12
|
import { type AsyncCommandRunner, type CommandResult } from '@osdlc/command-seam';
|
|
11
|
-
import
|
|
13
|
+
import { type Soft, type TriedAsync } from './doors.ts';
|
|
14
|
+
import type { IssueRow, Label, OwnerRepo, PrSearchHit, PullRequest, Repository } from './types.ts';
|
|
12
15
|
import * as v from './verbs.ts';
|
|
13
|
-
import type { ApiOptions, ApiResponse, AuthStatus, IssueListOptions, PrListOptions } from './verbs.ts';
|
|
16
|
+
import type { ApiOptions, ApiResponse, AuthStatus, GhTarget, IssueListOptions, ListResult, PrListOptions } from './verbs.ts';
|
|
14
17
|
/**
|
|
15
|
-
* Per-call options for {@link AsyncGh}.
|
|
16
|
-
* `prList`, `prView`, `openPrFor`, `prViewSettled`, `prBody`, `api`, `
|
|
17
|
-
* `issueList` and `
|
|
18
|
+
* Per-call options for {@link AsyncGh}. Sixteen methods accept them: `raw`, `rawChecked`,
|
|
19
|
+
* `prList`, `prView`, `prFind`, `openPrFor`, `prViewSettled`, `prBody`, `api`, `graphql`,
|
|
20
|
+
* `labelList`, `issueList`, `issueView`, `repoList`, `repoView` and `searchPrs`.
|
|
18
21
|
*/
|
|
19
22
|
export interface AsyncCallOptions {
|
|
20
23
|
/**
|
|
@@ -26,25 +29,51 @@ export interface AsyncCallOptions {
|
|
|
26
29
|
/** Aborts the call; the runner stops the child. */
|
|
27
30
|
signal?: AbortSignal | undefined;
|
|
28
31
|
}
|
|
29
|
-
/**
|
|
32
|
+
/** What an {@link AsyncGh} is built with: the runner, and the repository and host it talks to. */
|
|
33
|
+
export interface AsyncGhOptions extends GhTarget {
|
|
34
|
+
runner?: AsyncCommandRunner | undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A typed, awaitable gh client bound to one working directory, and optionally
|
|
38
|
+
* to one repository and host. `repo` and `hostname` work as on `Gh`.
|
|
39
|
+
*/
|
|
30
40
|
export declare class AsyncGh {
|
|
31
41
|
readonly cwd: string;
|
|
42
|
+
readonly repo: string | undefined;
|
|
43
|
+
readonly hostname: string | undefined;
|
|
44
|
+
private readonly target;
|
|
32
45
|
private readonly runner;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/** The same client rooted at a different directory (same runner). */
|
|
46
|
+
private tried;
|
|
47
|
+
private softened;
|
|
48
|
+
constructor(cwd: string, opts?: AsyncGhOptions);
|
|
49
|
+
/** The same client rooted at a different directory (same runner, repo and host). */
|
|
37
50
|
at(cwd: string): AsyncGh;
|
|
51
|
+
/** The same client aimed at another repository (same runner, directory and host). */
|
|
52
|
+
withRepo(repo: string): AsyncGh;
|
|
53
|
+
/**
|
|
54
|
+
* The whole surface again, resolving to a `Result<T, GhFailure>` instead of
|
|
55
|
+
* rejecting. A query for one thing that finds nothing is `ok: true` with
|
|
56
|
+
* `null`. See `Gh.try`.
|
|
57
|
+
*/
|
|
58
|
+
get try(): TriedAsync<AsyncGh>;
|
|
59
|
+
/** Each query again, resolving `null` instead of rejecting when `gh` fails. See `Gh.soft`. */
|
|
60
|
+
get soft(): Soft<AsyncGh>;
|
|
38
61
|
/** Unchecked passthrough for verbs not yet lifted into the API. */
|
|
39
62
|
raw(argv: string[], opts?: AsyncCallOptions): Promise<CommandResult>;
|
|
40
|
-
/** Checked passthrough — rejects with
|
|
63
|
+
/** Checked passthrough — rejects with the classified failure on a non-zero exit. */
|
|
41
64
|
rawChecked(argv: string[], opts?: AsyncCallOptions): Promise<CommandResult>;
|
|
42
65
|
/** Run one verb from the table, honouring its checked/unchecked contract. */
|
|
43
66
|
private exec;
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
|
|
67
|
+
/** {@link exec} for a verb that never answers `null` as a value: a failed or unreadable run rejects. */
|
|
68
|
+
private read;
|
|
69
|
+
/** Pull requests matching `opts`. Rejects when gh fails; `soft.prList` resolves null instead. */
|
|
70
|
+
prList(opts?: PrListOptions & AsyncCallOptions): Promise<ListResult<PullRequest>>;
|
|
71
|
+
/** One PR by number, branch or URL, with the heavy fields, or null. See `Gh.prView`. */
|
|
72
|
+
prView(ref: number | string, opts?: {
|
|
73
|
+
fields?: readonly string[] | undefined;
|
|
74
|
+
} & AsyncCallOptions): Promise<PullRequest | null>;
|
|
75
|
+
/** The one PR that `input` names, or null. See `Gh.prFind`. */
|
|
76
|
+
prFind(input: string, opts?: {
|
|
48
77
|
fields?: readonly string[] | undefined;
|
|
49
78
|
} & AsyncCallOptions): Promise<PullRequest | null>;
|
|
50
79
|
/** The open PR whose head exactly equals `branch`, or null. See `Gh.openPrFor`. */
|
|
@@ -58,18 +87,32 @@ export declare class AsyncGh {
|
|
|
58
87
|
fields?: readonly string[] | undefined;
|
|
59
88
|
sleep?: ((ms: number) => Promise<void>) | undefined;
|
|
60
89
|
} & AsyncCallOptions): Promise<PullRequest | null>;
|
|
61
|
-
/** A PR's body text
|
|
62
|
-
prBody(number: number, opts?: AsyncCallOptions): Promise<string
|
|
90
|
+
/** A PR's body text. Rejects when gh fails. */
|
|
91
|
+
prBody(number: number, opts?: AsyncCallOptions): Promise<string>;
|
|
63
92
|
/** A REST GET through `gh api`; failure is `ok: false`, never a rejection. */
|
|
64
93
|
api(endpoint: string, opts?: ApiOptions & AsyncCallOptions): Promise<ApiResponse>;
|
|
65
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* A GraphQL query; each page's `data`. Rejects on a failed or partial result. See `Gh.graphql`.
|
|
96
|
+
*
|
|
97
|
+
* @experimental
|
|
98
|
+
*/
|
|
99
|
+
graphql(query: string, variables?: Record<string, v.GraphqlVariable>, opts?: v.GraphqlOptions & AsyncCallOptions): Promise<unknown[]>;
|
|
100
|
+
/** Every label on the repository (up to `opts.limit`, default 1000). Rejects when gh fails. */
|
|
66
101
|
labelList(opts?: {
|
|
67
102
|
limit?: number | undefined;
|
|
68
|
-
} & AsyncCallOptions): Promise<Label
|
|
69
|
-
/** Issues matching `opts
|
|
70
|
-
issueList(opts?: IssueListOptions & AsyncCallOptions): Promise<IssueRow
|
|
71
|
-
/** One issue with its labels
|
|
103
|
+
} & AsyncCallOptions): Promise<ListResult<Label>>;
|
|
104
|
+
/** Issues matching `opts`. Rejects when gh fails. */
|
|
105
|
+
issueList(opts?: IssueListOptions & AsyncCallOptions): Promise<ListResult<IssueRow>>;
|
|
106
|
+
/** One issue with its labels; null when there is no such issue. */
|
|
72
107
|
issueView(number: number, opts?: AsyncCallOptions): Promise<IssueRow | null>;
|
|
108
|
+
/** An owner's repositories. Rejects when gh fails. See `Gh.repoList`. */
|
|
109
|
+
repoList(owner: string, opts?: v.RepoListOptions & AsyncCallOptions): Promise<ListResult<Repository>>;
|
|
110
|
+
/** One repository; null when there is no such repository. See `Gh.repoView`. */
|
|
111
|
+
repoView(repo?: string | undefined, opts?: {
|
|
112
|
+
fields?: readonly string[] | undefined;
|
|
113
|
+
} & AsyncCallOptions): Promise<Repository | null>;
|
|
114
|
+
/** Pull requests across repositories matching a search query. Rejects when gh fails. See `Gh.searchPrs`. */
|
|
115
|
+
searchPrs(query: string, opts?: v.SearchPrsOptions & AsyncCallOptions): Promise<ListResult<PrSearchHit>>;
|
|
73
116
|
/** True iff the `gh` binary is on PATH and runs. */
|
|
74
117
|
installed(): Promise<boolean>;
|
|
75
118
|
/** True iff `gh` holds credentials. */
|
|
@@ -92,7 +135,7 @@ export declare class AsyncGh {
|
|
|
92
135
|
}): Promise<void>;
|
|
93
136
|
/** Rewrite just a PR's body. */
|
|
94
137
|
editPrBody(number: number, body: string): Promise<void>;
|
|
95
|
-
/** The PR's unified diff; rejects with
|
|
138
|
+
/** The PR's unified diff; rejects with the classified failure when gh cannot produce it. */
|
|
96
139
|
prDiff(number: number): Promise<string>;
|
|
97
140
|
/** Post one conversation comment on a PR; resolves to its URL ('' if gh printed none). */
|
|
98
141
|
prComment(number: number, body: string): Promise<string>;
|
|
@@ -118,7 +161,7 @@ export declare class AsyncGh {
|
|
|
118
161
|
headSha: string;
|
|
119
162
|
}): Promise<void>;
|
|
120
163
|
/** A PR's review threads, or null when they could not be read completely. */
|
|
121
|
-
prReviewThreads(number: number): Promise<v.ReviewThread[]
|
|
164
|
+
prReviewThreads(number: number): Promise<v.ReviewThread[]>;
|
|
122
165
|
/** Create a label (or force it to `opts`'s color/description if it exists). */
|
|
123
166
|
createLabel(opts: v.CreateLabelOptions): Promise<void>;
|
|
124
167
|
/** Recolor and/or redescribe an existing label. */
|
|
@@ -129,4 +172,3 @@ export declare class AsyncGh {
|
|
|
129
172
|
/** Swap an issue's label in a single `gh issue edit` call. */
|
|
130
173
|
setIssueLabel(number: number, currentLabel: string | null, newLabel: string): Promise<void>;
|
|
131
174
|
}
|
|
132
|
-
//# sourceMappingURL=async.d.ts.map
|
package/dist/async.js
CHANGED
|
@@ -1,60 +1,111 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `AsyncGh` — the awaitable GitHub client, with the same methods as {@link Gh}.
|
|
3
3
|
*
|
|
4
|
-
* Same
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Same three doors, promised: the plain method resolves to a value or rejects
|
|
5
|
+
* (a `FailureError` carrying the classified `GhFailure` for a failed gh or output it cannot read;
|
|
6
|
+
* a query for one thing resolves `null` when GitHub has no such thing),
|
|
7
|
+
* `try` resolves to a `Result<T, GhFailure>`, `soft` resolves `null` for a
|
|
8
|
+
* failure, and `raw`/`rawChecked` are the hatches. It is driven by the same pure verb
|
|
7
9
|
* table as `Gh`, so the two cannot drift on argv or parsing. Use it where the
|
|
8
10
|
* sync client would block the event loop: a fan-out of per-PR queries.
|
|
9
11
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
+
import { resolveRunnerAsync, } from '@osdlc/command-seam';
|
|
13
|
+
import { runCheckedGhAsync, settle, softAsync, triedAsync, } from "./doors.js";
|
|
14
|
+
import { ownerRepoSlug, parseOwnerRepo, parsePrRef } from "./repo.js";
|
|
12
15
|
import * as v from "./verbs.js";
|
|
13
16
|
/** How long to wait between merge-state retries, in milliseconds. */
|
|
14
17
|
const MERGE_STATE_RETRY_DELAY_MS = 1_500;
|
|
15
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* A typed, awaitable gh client bound to one working directory, and optionally
|
|
20
|
+
* to one repository and host. `repo` and `hostname` work as on `Gh`.
|
|
21
|
+
*/
|
|
16
22
|
export class AsyncGh {
|
|
17
23
|
cwd;
|
|
24
|
+
repo;
|
|
25
|
+
hostname;
|
|
26
|
+
target;
|
|
18
27
|
runner;
|
|
28
|
+
tried;
|
|
29
|
+
softened;
|
|
19
30
|
constructor(cwd, opts = {}) {
|
|
31
|
+
v.checkTarget(opts);
|
|
20
32
|
this.cwd = cwd;
|
|
33
|
+
this.repo = opts.repo;
|
|
34
|
+
this.hostname = opts.hostname;
|
|
35
|
+
this.target = { repo: opts.repo, hostname: opts.hostname };
|
|
21
36
|
this.runner = resolveRunnerAsync('gh', opts.runner);
|
|
22
37
|
}
|
|
23
|
-
/** The same client rooted at a different directory (same runner). */
|
|
38
|
+
/** The same client rooted at a different directory (same runner, repo and host). */
|
|
24
39
|
at(cwd) {
|
|
25
|
-
return new AsyncGh(cwd, { runner: this.runner });
|
|
40
|
+
return new AsyncGh(cwd, { runner: this.runner, repo: this.repo, hostname: this.hostname });
|
|
41
|
+
}
|
|
42
|
+
/** The same client aimed at another repository (same runner, directory and host). */
|
|
43
|
+
withRepo(repo) {
|
|
44
|
+
return new AsyncGh(this.cwd, { runner: this.runner, repo, hostname: this.hostname });
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The whole surface again, resolving to a `Result<T, GhFailure>` instead of
|
|
48
|
+
* rejecting. A query for one thing that finds nothing is `ok: true` with
|
|
49
|
+
* `null`. See `Gh.try`.
|
|
50
|
+
*/
|
|
51
|
+
get try() {
|
|
52
|
+
return (this.tried ??= triedAsync(this));
|
|
53
|
+
}
|
|
54
|
+
/** Each query again, resolving `null` instead of rejecting when `gh` fails. See `Gh.soft`. */
|
|
55
|
+
get soft() {
|
|
56
|
+
return (this.softened ??= softAsync(this));
|
|
26
57
|
}
|
|
27
58
|
// ── escape hatches ────────────────────────────────────────────────────────
|
|
28
59
|
/** Unchecked passthrough for verbs not yet lifted into the API. */
|
|
29
60
|
raw(argv, opts = {}) {
|
|
30
61
|
return this.runner.run(argv, { cwd: this.cwd, ...opts });
|
|
31
62
|
}
|
|
32
|
-
/** Checked passthrough — rejects with
|
|
63
|
+
/** Checked passthrough — rejects with the classified failure on a non-zero exit. */
|
|
33
64
|
rawChecked(argv, opts = {}) {
|
|
34
|
-
return
|
|
65
|
+
return runCheckedGhAsync(this.runner, argv, { cwd: this.cwd, ...opts });
|
|
35
66
|
}
|
|
36
67
|
/** Run one verb from the table, honouring its checked/unchecked contract. */
|
|
37
68
|
async exec(verb, call = {}) {
|
|
38
69
|
const opts = { cwd: this.cwd };
|
|
39
70
|
if (verb.input !== undefined)
|
|
40
71
|
opts.input = verb.input;
|
|
72
|
+
if (verb.env !== undefined)
|
|
73
|
+
opts.env = verb.env;
|
|
41
74
|
if (call.timeoutMs !== undefined)
|
|
42
75
|
opts.timeoutMs = call.timeoutMs;
|
|
43
76
|
if (call.signal !== undefined)
|
|
44
77
|
opts.signal = call.signal;
|
|
45
78
|
const result = verb.checked
|
|
46
|
-
? await
|
|
79
|
+
? await runCheckedGhAsync(this.runner, verb.argv, opts)
|
|
47
80
|
: await this.runner.run(verb.argv, opts);
|
|
48
|
-
return verb.
|
|
81
|
+
return settle(verb, result, this.runner.binary);
|
|
82
|
+
}
|
|
83
|
+
/** {@link exec} for a verb that never answers `null` as a value: a failed or unreadable run rejects. */
|
|
84
|
+
async read(verb, call = {}) {
|
|
85
|
+
return (await this.exec(verb, call));
|
|
49
86
|
}
|
|
50
87
|
// ── queries ───────────────────────────────────────────────────────────────
|
|
51
|
-
/** Pull requests matching `opts
|
|
88
|
+
/** Pull requests matching `opts`. Rejects when gh fails; `soft.prList` resolves null instead. */
|
|
52
89
|
prList(opts = {}) {
|
|
53
|
-
return this.
|
|
54
|
-
}
|
|
55
|
-
/** One PR with the heavy fields, or null
|
|
56
|
-
prView(
|
|
57
|
-
return this.exec(v.prView(
|
|
90
|
+
return this.read(v.prList(opts, this.target), opts);
|
|
91
|
+
}
|
|
92
|
+
/** One PR by number, branch or URL, with the heavy fields, or null. See `Gh.prView`. */
|
|
93
|
+
prView(ref, opts = {}) {
|
|
94
|
+
return this.exec(v.prView(ref, opts.fields ?? v.PR_VIEW_FIELDS, this.target), opts);
|
|
95
|
+
}
|
|
96
|
+
/** The one PR that `input` names, or null. See `Gh.prFind`. */
|
|
97
|
+
async prFind(input, opts = {}) {
|
|
98
|
+
const ref = parsePrRef(input);
|
|
99
|
+
if (ref === null)
|
|
100
|
+
return null;
|
|
101
|
+
const call = { ...opts, fields: opts.fields ?? v.PR_LIST_FIELDS };
|
|
102
|
+
if (ref.kind === 'url')
|
|
103
|
+
return this.prView(ref.url, call);
|
|
104
|
+
if (ref.kind === 'branch')
|
|
105
|
+
return prForBranchOn(this, ref.name, call);
|
|
106
|
+
const client = ref.repo === null ? this : this.withRepo(ownerRepoSlug(ref.repo));
|
|
107
|
+
const pr = await client.prView(ref.number, call);
|
|
108
|
+
return pr !== null || ref.orBranch === null ? pr : prForBranchOn(this, ref.orBranch, call);
|
|
58
109
|
}
|
|
59
110
|
/** The open PR whose head exactly equals `branch`, or null. See `Gh.openPrFor`. */
|
|
60
111
|
async openPrFor(branch, opts = {}) {
|
|
@@ -65,7 +116,7 @@ export class AsyncGh {
|
|
|
65
116
|
timeoutMs: opts.timeoutMs,
|
|
66
117
|
signal: opts.signal,
|
|
67
118
|
});
|
|
68
|
-
return v.pickPrForBranch(rows, branch);
|
|
119
|
+
return v.pickPrForBranch(rows.rows, branch);
|
|
69
120
|
}
|
|
70
121
|
/** {@link prView}, re-asked until GitHub settles the merge state. See `Gh.prViewSettled`. */
|
|
71
122
|
async prViewSettled(number, opts = {}) {
|
|
@@ -81,25 +132,45 @@ export class AsyncGh {
|
|
|
81
132
|
}
|
|
82
133
|
return last;
|
|
83
134
|
}
|
|
84
|
-
/** A PR's body text
|
|
135
|
+
/** A PR's body text. Rejects when gh fails. */
|
|
85
136
|
prBody(number, opts = {}) {
|
|
86
|
-
return this.
|
|
137
|
+
return this.read(v.prBody(number, this.target), opts);
|
|
87
138
|
}
|
|
88
139
|
/** A REST GET through `gh api`; failure is `ok: false`, never a rejection. */
|
|
89
140
|
api(endpoint, opts = {}) {
|
|
90
|
-
return this.exec(v.api(endpoint, opts), opts);
|
|
141
|
+
return this.exec(v.api(endpoint, opts, this.target), opts);
|
|
91
142
|
}
|
|
92
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* A GraphQL query; each page's `data`. Rejects on a failed or partial result. See `Gh.graphql`.
|
|
145
|
+
*
|
|
146
|
+
* @experimental
|
|
147
|
+
*/
|
|
148
|
+
graphql(query, variables = {}, opts = {}) {
|
|
149
|
+
return this.read(v.graphql(query, variables, opts, this.target), opts);
|
|
150
|
+
}
|
|
151
|
+
/** Every label on the repository (up to `opts.limit`, default 1000). Rejects when gh fails. */
|
|
93
152
|
labelList(opts = {}) {
|
|
94
|
-
return this.
|
|
153
|
+
return this.read(v.labelList(opts, this.target), opts);
|
|
95
154
|
}
|
|
96
|
-
/** Issues matching `opts
|
|
155
|
+
/** Issues matching `opts`. Rejects when gh fails. */
|
|
97
156
|
issueList(opts = {}) {
|
|
98
|
-
return this.
|
|
157
|
+
return this.read(v.issueList(opts, this.target), opts);
|
|
99
158
|
}
|
|
100
|
-
/** One issue with its labels
|
|
159
|
+
/** One issue with its labels; null when there is no such issue. */
|
|
101
160
|
issueView(number, opts = {}) {
|
|
102
|
-
return this.exec(v.issueView(number), opts);
|
|
161
|
+
return this.exec(v.issueView(number, this.target), opts);
|
|
162
|
+
}
|
|
163
|
+
/** An owner's repositories. Rejects when gh fails. See `Gh.repoList`. */
|
|
164
|
+
repoList(owner, opts = {}) {
|
|
165
|
+
return this.read(v.repoList(owner, opts, this.target), opts);
|
|
166
|
+
}
|
|
167
|
+
/** One repository; null when there is no such repository. See `Gh.repoView`. */
|
|
168
|
+
repoView(repo, opts = {}) {
|
|
169
|
+
return this.exec(v.repoView(repo, opts.fields ?? v.REPO_FIELDS, this.target), opts);
|
|
170
|
+
}
|
|
171
|
+
/** Pull requests across repositories matching a search query. Rejects when gh fails. See `Gh.searchPrs`. */
|
|
172
|
+
searchPrs(query, opts = {}) {
|
|
173
|
+
return this.read(v.searchPrs(query, opts, this.target), opts);
|
|
103
174
|
}
|
|
104
175
|
/** True iff the `gh` binary is on PATH and runs. */
|
|
105
176
|
installed() {
|
|
@@ -111,7 +182,7 @@ export class AsyncGh {
|
|
|
111
182
|
}
|
|
112
183
|
/** Login state and what `gh auth status` printed. */
|
|
113
184
|
authStatus() {
|
|
114
|
-
return this.exec(v.authStatus());
|
|
185
|
+
return this.exec(v.authStatus(this.target));
|
|
115
186
|
}
|
|
116
187
|
/** True iff `gh` is installed AND authenticated; short-circuits. */
|
|
117
188
|
async ready() {
|
|
@@ -127,23 +198,23 @@ export class AsyncGh {
|
|
|
127
198
|
// ── mutations (checked) ───────────────────────────────────────────────────
|
|
128
199
|
/** Open a PR and resolve to its URL ('' if gh printed none). */
|
|
129
200
|
createPr(opts) {
|
|
130
|
-
return this.exec(v.createPr(opts));
|
|
201
|
+
return this.exec(v.createPr(opts, this.target));
|
|
131
202
|
}
|
|
132
203
|
/** Retitle or rewrite a PR body. */
|
|
133
204
|
editPr(number, opts) {
|
|
134
|
-
return this.exec(v.editPr(number, opts));
|
|
205
|
+
return this.exec(v.editPr(number, opts, this.target));
|
|
135
206
|
}
|
|
136
207
|
/** Rewrite just a PR's body. */
|
|
137
208
|
editPrBody(number, body) {
|
|
138
|
-
return this.exec(v.editPrBody(number, body));
|
|
209
|
+
return this.exec(v.editPrBody(number, body, this.target));
|
|
139
210
|
}
|
|
140
|
-
/** The PR's unified diff; rejects with
|
|
211
|
+
/** The PR's unified diff; rejects with the classified failure when gh cannot produce it. */
|
|
141
212
|
prDiff(number) {
|
|
142
|
-
return this.exec(v.prDiff(number));
|
|
213
|
+
return this.exec(v.prDiff(number, this.target));
|
|
143
214
|
}
|
|
144
215
|
/** Post one conversation comment on a PR; resolves to its URL ('' if gh printed none). */
|
|
145
216
|
prComment(number, body) {
|
|
146
|
-
return this.exec(v.prComment(number, body));
|
|
217
|
+
return this.exec(v.prComment(number, body, this.target));
|
|
147
218
|
}
|
|
148
219
|
/**
|
|
149
220
|
* Create a `COMMENT` review with inline comments; resolves to the review's URL ('' if none came back).
|
|
@@ -151,15 +222,15 @@ export class AsyncGh {
|
|
|
151
222
|
* @experimental
|
|
152
223
|
*/
|
|
153
224
|
createReview(number, opts) {
|
|
154
|
-
return this.exec(v.createReview(number, opts));
|
|
225
|
+
return this.exec(v.createReview(number, opts, this.target));
|
|
155
226
|
}
|
|
156
227
|
/** Bring a PR's head branch up to date with its base. */
|
|
157
228
|
prUpdateBranch(number, style = 'merge') {
|
|
158
|
-
return this.exec(v.prUpdateBranch(number, style));
|
|
229
|
+
return this.exec(v.prUpdateBranch(number, style, this.target));
|
|
159
230
|
}
|
|
160
231
|
/** Mark a PR ready for review (`draft: false`) or convert it to a draft (`draft: true`). */
|
|
161
232
|
prSetDraft(number, draft) {
|
|
162
|
-
return this.exec(v.prSetDraft(number, draft));
|
|
233
|
+
return this.exec(v.prSetDraft(number, draft, this.target));
|
|
163
234
|
}
|
|
164
235
|
/**
|
|
165
236
|
* Rewrite one PR conversation comment's body. `commentId` is the numeric REST id; anything else rejects with `RangeError`.
|
|
@@ -168,29 +239,41 @@ export class AsyncGh {
|
|
|
168
239
|
*/
|
|
169
240
|
async issueCommentEdit(commentId, body) {
|
|
170
241
|
// async, so a bad id rejects the Promise instead of throwing at the call.
|
|
171
|
-
return this.exec(v.issueCommentEdit(commentId, body));
|
|
242
|
+
return this.exec(v.issueCommentEdit(commentId, body, this.target));
|
|
172
243
|
}
|
|
173
244
|
/** Merge a PR; GitHub refuses when its head is no longer `opts.headSha`. */
|
|
174
245
|
prMerge(number, opts) {
|
|
175
|
-
return this.exec(v.prMerge(number, opts));
|
|
246
|
+
return this.exec(v.prMerge(number, opts, this.target));
|
|
176
247
|
}
|
|
177
248
|
/** A PR's review threads, or null when they could not be read completely. */
|
|
178
249
|
prReviewThreads(number) {
|
|
179
|
-
return this.
|
|
250
|
+
return this.read(v.prReviewThreads(number, this.target));
|
|
180
251
|
}
|
|
181
252
|
/** Create a label (or force it to `opts`'s color/description if it exists). */
|
|
182
253
|
createLabel(opts) {
|
|
183
|
-
return this.exec(v.createLabel(opts));
|
|
254
|
+
return this.exec(v.createLabel(opts, this.target));
|
|
184
255
|
}
|
|
185
256
|
/** Recolor and/or redescribe an existing label. */
|
|
186
257
|
editLabel(name, opts) {
|
|
187
|
-
return this.exec(v.editLabel(name, opts));
|
|
258
|
+
return this.exec(v.editLabel(name, opts, this.target));
|
|
188
259
|
}
|
|
189
260
|
/** Swap an issue's label in a single `gh issue edit` call. */
|
|
190
261
|
setIssueLabel(number, currentLabel, newLabel) {
|
|
191
|
-
return this.exec(v.setIssueLabel(number, currentLabel, newLabel));
|
|
262
|
+
return this.exec(v.setIssueLabel(number, currentLabel, newLabel, this.target));
|
|
192
263
|
}
|
|
193
264
|
}
|
|
265
|
+
/** The open PR for `branch`, else its newest closed or merged one. */
|
|
266
|
+
async function prForBranchOn(gh, branch, call) {
|
|
267
|
+
const rows = await gh.prList({
|
|
268
|
+
head: branch,
|
|
269
|
+
state: 'all',
|
|
270
|
+
limit: 20,
|
|
271
|
+
fields: v.branchPrFields(call.fields),
|
|
272
|
+
timeoutMs: call.timeoutMs,
|
|
273
|
+
signal: call.signal,
|
|
274
|
+
});
|
|
275
|
+
return v.branchPr(rows);
|
|
276
|
+
}
|
|
194
277
|
function sleepAsync(ms) {
|
|
195
278
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
196
279
|
}
|
package/dist/doors.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three doors, as `@osdlc/typed-git` has them: the plain verb (checked),
|
|
3
|
+
* `gh.try.*` and `gh.soft.*`.
|
|
4
|
+
*
|
|
5
|
+
* - **Plain / checked**: a value, or a throw. A failed `gh` throws a
|
|
6
|
+
* `FailureError` carrying the classified `GhFailure` (its `cause` is the
|
|
7
|
+
* `CommandFailed`), so a caller that reports it, such as `runMain`, reads
|
|
8
|
+
* "signed out" or "rate limited" and not a bare exit; output that exited 0
|
|
9
|
+
* but could not be read throws one of kind `unreadable`. A verb that
|
|
10
|
+
* reads one thing (`prView`, `issueView`, `repoView`) answers `null` when
|
|
11
|
+
* GitHub says there is no such thing: absence is an answer.
|
|
12
|
+
* - **`try`**: the same call, with the failure returned as
|
|
13
|
+
* `Result<T, GhFailure>`. It catches a thrown failure (a `FailureError`, or a
|
|
14
|
+
* `CommandFailed` from `raw`-level code) and nothing else: a `RangeError` for a bad argument still throws.
|
|
15
|
+
* - **`soft`**: the same call, with `null` for a failure. Only the queries
|
|
16
|
+
* have a soft form; no mutation does.
|
|
17
|
+
*
|
|
18
|
+
* Both are facades over the whole client, so a new method costs nothing here.
|
|
19
|
+
*/
|
|
20
|
+
import { type AsyncCommandRunner, type AsyncCommandRunOptions, type CommandResult, type CommandRunner, type CommandRunOptions } from '@osdlc/command-seam';
|
|
21
|
+
import { type Failure, type Result } from '@osdlc/result';
|
|
22
|
+
import { type GhFailure } from './failure.ts';
|
|
23
|
+
import type { GhVerb } from './verbs.ts';
|
|
24
|
+
/**
|
|
25
|
+
* Read the result of running `verb` as the plain door does.
|
|
26
|
+
*
|
|
27
|
+
* A checked verb has already thrown on a non-zero exit (the runner is
|
|
28
|
+
* `runChecked`), and a probe (`installed`, `authStatus`, `api`) carries its
|
|
29
|
+
* failure in its value. A read that exited non-zero throws `CommandFailed`,
|
|
30
|
+
* unless the verb's `absent` says gh's answer was "there is no such thing",
|
|
31
|
+
* which `parse` turns into `null`. A read that exited 0 but parsed to `null`
|
|
32
|
+
* could not be read completely, and throws.
|
|
33
|
+
*
|
|
34
|
+
* @throws `FailureError` carrying the classified {@link GhFailure} for a failed run.
|
|
35
|
+
* @throws `FailureError` carrying an `unreadable` {@link GhFailure}.
|
|
36
|
+
*/
|
|
37
|
+
export declare function settle<T>(verb: GhVerb<T>, result: CommandResult, binary: string | undefined): T;
|
|
38
|
+
/**
|
|
39
|
+
* The error the plain door throws for a failed run: a `FailureError` carrying
|
|
40
|
+
* the {@link GhFailure} that {@link classifyGhFailure} reads from it, with the
|
|
41
|
+
* `CommandFailed` as its `cause`. Anything else is returned as it is.
|
|
42
|
+
*/
|
|
43
|
+
export declare function ghFailureError(error: unknown): unknown;
|
|
44
|
+
/** {@link runChecked}, throwing {@link ghFailureError}'s classified failure. */
|
|
45
|
+
export declare function runCheckedGh(runner: CommandRunner, argv: string[], opts: CommandRunOptions): CommandResult;
|
|
46
|
+
/** {@link runCheckedAsync}, rejecting with {@link ghFailureError}'s classified failure. */
|
|
47
|
+
export declare function runCheckedGhAsync(runner: AsyncCommandRunner, argv: string[], opts: AsyncCommandRunOptions): Promise<CommandResult>;
|
|
48
|
+
/** The methods the doors leave out: the escape hatches, pure helpers and plain properties. */
|
|
49
|
+
type Triable<C> = Omit<C, 'raw' | 'rawChecked' | 'at' | 'withRepo' | 'ownerRepo' | 'try' | 'soft' | 'cwd' | 'repo' | 'hostname'>;
|
|
50
|
+
/** Every synchronous client method, returning a `Result<T, GhFailure>`. */
|
|
51
|
+
export type TriedSync<C> = {
|
|
52
|
+
[K in keyof Triable<C>]: Triable<C>[K] extends (...a: infer A) => infer R ? (...a: A) => Result<R, GhFailure> : never;
|
|
53
|
+
};
|
|
54
|
+
/** Every awaitable client method, promising a `Result<T, GhFailure>`. */
|
|
55
|
+
export type TriedAsync<C> = {
|
|
56
|
+
[K in keyof Triable<C>]: Triable<C>[K] extends (...a: infer A) => Promise<infer R> ? (...a: A) => Promise<Result<R, GhFailure>> : never;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* The queries that have a soft form. A mutation has none: swallowing the failure
|
|
60
|
+
* of a write is how a caller ends up believing it worked.
|
|
61
|
+
*/
|
|
62
|
+
export declare const SOFT_METHODS: readonly ["prList", "prView", "prFind", "openPrFor", "prViewSettled", "prBody", "graphql", "labelList", "issueList", "issueView", "repoList", "repoView", "searchPrs", "prReviewThreads"];
|
|
63
|
+
/** The name of a method with a soft form. */
|
|
64
|
+
export type SoftMethod = (typeof SOFT_METHODS)[number];
|
|
65
|
+
type OrNull<R> = R extends Promise<infer U> ? Promise<U | null> : R | null;
|
|
66
|
+
/** Each queried method again, returning `null` instead of throwing. One type for both clients. */
|
|
67
|
+
export type Soft<C> = {
|
|
68
|
+
[K in SoftMethod & keyof C]: C[K] extends (...a: infer A) => infer R ? (...a: A) => OrNull<R> : never;
|
|
69
|
+
};
|
|
70
|
+
/** The failure a caught `CommandFailed` or `FailureError` stands for. */
|
|
71
|
+
export declare function failureOf(error: {
|
|
72
|
+
failure: Failure;
|
|
73
|
+
}): GhFailure;
|
|
74
|
+
/** Wrap a synchronous client so every method returns a `Result<T, GhFailure>`. */
|
|
75
|
+
export declare function triedSync<C extends object>(client: C): TriedSync<C>;
|
|
76
|
+
/** Wrap an awaitable client so every method promises a `Result<T, GhFailure>`. */
|
|
77
|
+
export declare function triedAsync<C extends object>(client: C): TriedAsync<C>;
|
|
78
|
+
/** Wrap a synchronous client so a failed query returns `null`. */
|
|
79
|
+
export declare function softSync<C extends object>(client: C): Soft<C>;
|
|
80
|
+
/** Wrap an awaitable client so a failed query resolves to `null`. */
|
|
81
|
+
export declare function softAsync<C extends object>(client: C): Soft<C>;
|
|
82
|
+
export {};
|