@effected/github 0.14.0 → 0.15.1
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/ArtifactMetadata.js +7 -5
- package/Attestation.js +7 -1
- package/CheckRun.js +10 -5
- package/CodeScanning.js +8 -1
- package/DeploymentEnvironment.js +19 -1
- package/GitBranch.js +22 -3
- package/GitCommit.js +25 -5
- package/GitHubApp.js +33 -14
- package/GitHubClient.js +24 -18
- package/GitHubCommit.js +20 -1
- package/GitHubContent.js +19 -1
- package/GitHubError.js +11 -22
- package/GitHubIssue.js +24 -8
- package/GitHubRelease.js +20 -2
- package/GitHubRepository.js +25 -11
- package/GitTag.js +19 -1
- package/GraphQL.js +21 -18
- package/PullRequest.js +27 -8
- package/PullRequestComment.js +25 -7
- package/README.md +5 -5
- package/Repo.js +8 -15
- package/RepositorySecret.js +19 -1
- package/RepositorySecurity.js +20 -1
- package/RepositoryVariable.js +19 -1
- package/Resilience.js +18 -16
- package/Rest.js +2 -4
- package/Ruleset.js +25 -4
- package/TokenPermissions.js +8 -11
- package/WorkflowDispatch.js +25 -1
- package/index.d.ts +684 -323
- package/internal/headers.js +0 -10
- package/internal/octokit.js +5 -11
- package/internal/paginate.js +1 -4
- package/package.json +5 -5
package/ArtifactMetadata.js
CHANGED
|
@@ -7,10 +7,7 @@ import { Context, Effect, Layer, Schema } from "effect";
|
|
|
7
7
|
* What to record about a published artifact.
|
|
8
8
|
*
|
|
9
9
|
* @remarks
|
|
10
|
-
* These are the fields the endpoint
|
|
11
|
-
* declared a `version` field the endpoint has no notion of — a fabricated key
|
|
12
|
-
* that a `Record<string, unknown>` body accepted silently and the generated
|
|
13
|
-
* types reject outright.
|
|
10
|
+
* These are the fields the storage-record endpoint accepts.
|
|
14
11
|
*
|
|
15
12
|
* @public
|
|
16
13
|
*/
|
|
@@ -29,11 +26,16 @@ var StorageRecordInput = class extends Schema.Class("StorageRecordInput")({
|
|
|
29
26
|
path: Schema.optionalKey(Schema.String)
|
|
30
27
|
}) {};
|
|
31
28
|
/**
|
|
32
|
-
*
|
|
29
|
+
* Records where published artifacts are stored, at the organization level.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Provide it with {@link ArtifactMetadata.layer}, which needs a `GitHubClient`;
|
|
33
|
+
* `createStorageRecord` also needs a `Repo` in `R`.
|
|
33
34
|
*
|
|
34
35
|
* @public
|
|
35
36
|
*/
|
|
36
37
|
var ArtifactMetadata = class ArtifactMetadata extends Context.Service()("@effected/github/ArtifactMetadata") {
|
|
38
|
+
/** The live service, built over a `GitHubClient`. */
|
|
37
39
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
38
40
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
39
41
|
static makeTest = (overrides = {}) => ({ createStorageRecord: overrides.createStorageRecord ?? (() => unstubbed("createStorageRecord")) });
|
package/Attestation.js
CHANGED
|
@@ -42,11 +42,17 @@ const ListResponse = Schema.Struct({ attestations: Schema.optionalKey(Schema.Arr
|
|
|
42
42
|
predicate_type: Schema.optionalKey(Schema.String)
|
|
43
43
|
}))) });
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* Uploads signed attestation bundles to a repository and lists what is attested
|
|
46
|
+
* about a subject digest.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* Provide it with {@link Attestation.layer}, which needs a `GitHubClient`; each
|
|
50
|
+
* method also needs a `Repo` in `R`.
|
|
46
51
|
*
|
|
47
52
|
* @public
|
|
48
53
|
*/
|
|
49
54
|
var Attestation = class Attestation extends Context.Service()("@effected/github/Attestation") {
|
|
55
|
+
/** The live service, built over a `GitHubClient`. */
|
|
50
56
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
51
57
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
52
58
|
static makeTest = (overrides = {}) => ({
|
package/CheckRun.js
CHANGED
|
@@ -43,8 +43,8 @@ var Annotation = class extends Schema.Class("Annotation")({
|
|
|
43
43
|
* GitHub's limits are **byte** limits, and that distinction is the whole reason
|
|
44
44
|
* this class exists rather than a struct: `✅`, `❌`, `🦋` and `│` cost several
|
|
45
45
|
* bytes each, so a character-count check passes while the request comes back
|
|
46
|
-
* 422 saying *"summary exceeds a maximum bytesize of 65535"*.
|
|
47
|
-
*
|
|
46
|
+
* 422 saying *"summary exceeds a maximum bytesize of 65535"*. Use
|
|
47
|
+
* {@link CheckRunOutput.truncated} to cut an output to fit.
|
|
48
48
|
*
|
|
49
49
|
* @public
|
|
50
50
|
*/
|
|
@@ -87,8 +87,7 @@ var CheckRunOutput = class CheckRunOutput extends Schema.Class("CheckRunOutput")
|
|
|
87
87
|
* @remarks
|
|
88
88
|
* Slicing a UTF-8 buffer mid-character decodes to U+FFFD. Splitting a four-byte
|
|
89
89
|
* code point can produce **more than one** replacement character, so the trim
|
|
90
|
-
* loops rather than dropping a single one
|
|
91
|
-
* over the hand-written version it replaces.
|
|
90
|
+
* loops rather than dropping a single one.
|
|
92
91
|
*/
|
|
93
92
|
const capBytes = (value) => {
|
|
94
93
|
if (Buffer.byteLength(value, "utf8") <= CheckRunOutput.LIMIT_BYTES) return value;
|
|
@@ -110,11 +109,17 @@ var CheckRunRef = class extends Schema.Class("CheckRunRef")({
|
|
|
110
109
|
status: Schema.String
|
|
111
110
|
}) {};
|
|
112
111
|
/**
|
|
113
|
-
*
|
|
112
|
+
* Create, update and conclude GitHub check runs, including the
|
|
113
|
+
* {@link CheckRunShape.withCheckRun} bracket that always reaches a terminal state.
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* Provide it with {@link CheckRun.layer}, which needs a `GitHubClient`; each
|
|
117
|
+
* method also needs a `Repo` in `R`.
|
|
114
118
|
*
|
|
115
119
|
* @public
|
|
116
120
|
*/
|
|
117
121
|
var CheckRun = class CheckRun extends Context.Service()("@effected/github/CheckRun") {
|
|
122
|
+
/** The live service, built over a `GitHubClient`. */
|
|
118
123
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
119
124
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
120
125
|
static makeTest = (overrides = {}) => ({
|
package/CodeScanning.js
CHANGED
|
@@ -4,12 +4,19 @@ import { Context, Effect, Layer } from "effect";
|
|
|
4
4
|
|
|
5
5
|
//#region src/CodeScanning.ts
|
|
6
6
|
/**
|
|
7
|
-
* CodeQL default setup
|
|
7
|
+
* Configure CodeQL default setup and read the languages GitHub detects in a
|
|
8
|
+
* repository.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* Provide it with {@link CodeScanning.layer}, which needs a `GitHubClient`; each
|
|
12
|
+
* method also needs a `Repo` in `R`.
|
|
8
13
|
*
|
|
9
14
|
* @public
|
|
10
15
|
*/
|
|
11
16
|
var CodeScanning = class CodeScanning extends Context.Service()("@effected/github/CodeScanning") {
|
|
12
17
|
/**
|
|
18
|
+
* The live service, built over a `GitHubClient`.
|
|
19
|
+
*
|
|
13
20
|
* @remarks
|
|
14
21
|
* The callback is written `(client) => make(client)` rather than passed as
|
|
15
22
|
* `make` directly, and that is load-bearing: a static initializer runs while
|
package/DeploymentEnvironment.js
CHANGED
|
@@ -4,12 +4,30 @@ import { Context, Effect, Layer } from "effect";
|
|
|
4
4
|
|
|
5
5
|
//#region src/DeploymentEnvironment.ts
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Create or update, list and delete a repository's deployment environments.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Provide it with {@link DeploymentEnvironment.layer}, which needs a
|
|
11
|
+
* `GitHubClient`; each method also needs a `Repo` in `R`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { DeploymentEnvironment } from "@effected/github";
|
|
16
|
+
* import { Effect } from "effect";
|
|
17
|
+
*
|
|
18
|
+
* const program = Effect.gen(function* () {
|
|
19
|
+
* const environments = yield* DeploymentEnvironment;
|
|
20
|
+
* yield* environments.upsert("production", { wait_timer: 10 });
|
|
21
|
+
* return yield* environments.list();
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
8
24
|
*
|
|
9
25
|
* @public
|
|
10
26
|
*/
|
|
11
27
|
var DeploymentEnvironment = class DeploymentEnvironment extends Context.Service()("@effected/github/DeploymentEnvironment") {
|
|
12
28
|
/**
|
|
29
|
+
* The live service, built over a `GitHubClient`.
|
|
30
|
+
*
|
|
13
31
|
* @remarks
|
|
14
32
|
* `(client) => make(client)` rather than `make`: a static initializer runs
|
|
15
33
|
* while the module body is still evaluating, so naming a `const` declared
|
package/GitBranch.js
CHANGED
|
@@ -15,12 +15,32 @@ const CreateLinkedBranch = GraphQLDocument.make({
|
|
|
15
15
|
response: Schema.Struct({})
|
|
16
16
|
})();
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Create, move, read and delete branch refs through GitHub's Git Database API,
|
|
19
|
+
* with an `upsert` that needs no existence check.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* Provide it with {@link GitBranch.layer}, which needs a `GitHubClient`; each
|
|
23
|
+
* method also needs a `Repo` in `R`. Branch names are accepted as `main`,
|
|
24
|
+
* `heads/main` or `refs/heads/main`.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { GitBranch } from "@effected/github";
|
|
29
|
+
* import { Effect } from "effect";
|
|
30
|
+
*
|
|
31
|
+
* const ensureBranch = (name: string, sha: string) =>
|
|
32
|
+
* Effect.gen(function* () {
|
|
33
|
+
* const branches = yield* GitBranch;
|
|
34
|
+
* return yield* branches.upsert(name, sha); // "created" | "reset"
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
19
37
|
*
|
|
20
38
|
* @public
|
|
21
39
|
*/
|
|
22
40
|
var GitBranch = class GitBranch extends Context.Service()("@effected/github/GitBranch") {
|
|
23
41
|
/**
|
|
42
|
+
* The live service, built over a `GitHubClient`.
|
|
43
|
+
*
|
|
24
44
|
* @remarks
|
|
25
45
|
* The callback is written `(client) => make(client)` rather than passed as
|
|
26
46
|
* `make` directly, and that is load-bearing: a static initializer runs while
|
|
@@ -71,8 +91,7 @@ const rejectEmpty = (operation, name) => {
|
|
|
71
91
|
* house pattern for a stable dependency — but it would also make a scoped
|
|
72
92
|
* override silently do nothing, because the resource would already hold the
|
|
73
93
|
* repository it was built with. The repository is precisely the dependency that
|
|
74
|
-
* is *not* stable:
|
|
75
|
-
* repositories.
|
|
94
|
+
* is *not* stable: one program may loop over many target repositories.
|
|
76
95
|
*
|
|
77
96
|
* The client stays resolved at construction, so this costs one context read per
|
|
78
97
|
* call and nothing else.
|
package/GitCommit.js
CHANGED
|
@@ -44,10 +44,8 @@ const FileChange = Schema.Union([FileContent, FileDeletion]);
|
|
|
44
44
|
* A commit, projected to the three fields callers actually use.
|
|
45
45
|
*
|
|
46
46
|
* @remarks
|
|
47
|
-
* `treeSha` is here because
|
|
48
|
-
*
|
|
49
|
-
* wants a tree SHA, not a commit SHA"* — the same eight lines written twice, in
|
|
50
|
-
* two files, for one string.
|
|
47
|
+
* `treeSha` is here because the Git Data API's `base_tree` wants a tree SHA,
|
|
48
|
+
* not a commit SHA.
|
|
51
49
|
*
|
|
52
50
|
* @public
|
|
53
51
|
*/
|
|
@@ -60,11 +58,33 @@ var CommitRef = class extends Schema.Class("CommitRef")({
|
|
|
60
58
|
parents: Schema.Array(Schema.String)
|
|
61
59
|
}) {};
|
|
62
60
|
/**
|
|
63
|
-
*
|
|
61
|
+
* Read commits and build trees and commits through GitHub's Git Database API,
|
|
62
|
+
* including a one-call "commit these files onto a branch".
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* Provide it with {@link GitCommit.layer}, which needs a `GitHubClient`; each
|
|
66
|
+
* method also needs a `Repo` in `R`. No local git runs: this is the REST API,
|
|
67
|
+
* not `@effected/git`.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* import { FileContent, GitCommit } from "@effected/github";
|
|
72
|
+
* import { Effect } from "effect";
|
|
73
|
+
*
|
|
74
|
+
* const writeNotes = Effect.gen(function* () {
|
|
75
|
+
* const commits = yield* GitCommit;
|
|
76
|
+
* return yield* commits.commitFiles({
|
|
77
|
+
* branch: "docs/notes",
|
|
78
|
+
* message: "docs: add notes",
|
|
79
|
+
* changes: [FileContent.make({ path: "NOTES.md", content: "# Notes\n" })],
|
|
80
|
+
* }); // the new commit's sha
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
64
83
|
*
|
|
65
84
|
* @public
|
|
66
85
|
*/
|
|
67
86
|
var GitCommit = class GitCommit extends Context.Service()("@effected/github/GitCommit") {
|
|
87
|
+
/** The live service, built over a `GitHubClient`. */
|
|
68
88
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
69
89
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
70
90
|
static makeTest = (overrides = {}) => ({
|
package/GitHubApp.js
CHANGED
|
@@ -76,9 +76,8 @@ var InstallationToken = class extends Schema.Class("InstallationToken")({
|
|
|
76
76
|
* Whether this token is spent, `skew` before its stated expiry.
|
|
77
77
|
*
|
|
78
78
|
* @remarks
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* started answering 401 with no explanation.
|
|
79
|
+
* `skew` defaults to one minute, so a token is treated as spent slightly early
|
|
80
|
+
* rather than answering 401 mid-request.
|
|
82
81
|
*/
|
|
83
82
|
isExpired(nowMillis, skew = DEFAULT_SKEW) {
|
|
84
83
|
return DateTime.toEpochMillis(this.expiresAt) - Duration.toMillis(skew) <= nowMillis;
|
|
@@ -97,10 +96,10 @@ const DEFAULT_SKEW = Duration.seconds(60);
|
|
|
97
96
|
* Who a bot commits as.
|
|
98
97
|
*
|
|
99
98
|
* @remarks
|
|
100
|
-
* A **pure class**, not a service member
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
99
|
+
* A **pure class**, not a `GitHubApp` service member: a synchronous method on
|
|
100
|
+
* the service shape would be required in every `Layer.mock` and would silently
|
|
101
|
+
* degrade every partial double to a full implementation. Get one from
|
|
102
|
+
* `InstallationToken.botIdentity` or `AppIdentity.botIdentity`.
|
|
104
103
|
*
|
|
105
104
|
* @public
|
|
106
105
|
*/
|
|
@@ -189,9 +188,28 @@ var Installation = class extends Schema.Class("Installation")({
|
|
|
189
188
|
* `@effected/commands`' service, for the same reason.
|
|
190
189
|
*
|
|
191
190
|
* The JWT signer is `universal-github-app-jwt` — zero dependencies, and
|
|
192
|
-
* `@octokit/auth-app`'s own JWT dependency.
|
|
193
|
-
*
|
|
194
|
-
*
|
|
191
|
+
* `@octokit/auth-app`'s own JWT dependency.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* import { GitHubApp, GitHubClient } from "@effected/github";
|
|
196
|
+
* import { Effect, Redacted } from "effect";
|
|
197
|
+
*
|
|
198
|
+
* const program = Effect.gen(function* () {
|
|
199
|
+
* const client = yield* GitHubClient;
|
|
200
|
+
* const accessible = yield* client.request("GET /installation/repositories", { per_page: 1 });
|
|
201
|
+
* return accessible.total_count;
|
|
202
|
+
* });
|
|
203
|
+
*
|
|
204
|
+
* // Mints an installation token on build, re-mints before expiry, revokes on release.
|
|
205
|
+
* const layer = GitHubApp.clientLayer({
|
|
206
|
+
* appId: "12345",
|
|
207
|
+
* privateKey: Redacted.make("-----BEGIN PRIVATE KEY-----\n..."),
|
|
208
|
+
* owner: "my-org",
|
|
209
|
+
* });
|
|
210
|
+
*
|
|
211
|
+
* Effect.runPromise(Effect.provide(program, layer));
|
|
212
|
+
* ```
|
|
195
213
|
*
|
|
196
214
|
* @public
|
|
197
215
|
*/
|
|
@@ -214,11 +232,12 @@ var GitHubApp = class GitHubApp extends Context.Service()("@effected/github/GitH
|
|
|
214
232
|
* The token's lifetime is the layer's scope: it is minted on build and
|
|
215
233
|
* **revoked on release**, best-effort, so a workflow does not leave live
|
|
216
234
|
* credentials behind. It is also **re-minted automatically** a minute before
|
|
217
|
-
* it expires,
|
|
218
|
-
*
|
|
219
|
-
* started answering 401 with nothing explaining why.
|
|
235
|
+
* it expires, so a long-running program does not start answering 401 when
|
|
236
|
+
* the hour ends.
|
|
220
237
|
*
|
|
221
|
-
*
|
|
238
|
+
* Building the layer mints the first token, so a misconfigured app fails
|
|
239
|
+
* construction with `GitHubAppError`. After that, a failure to obtain
|
|
240
|
+
* credentials surfaces to the caller as a
|
|
222
241
|
* `GitHubError { kind: "unauthorized" }` carrying the `GitHubAppError` as its
|
|
223
242
|
* cause: from a request's point of view, "could not authenticate" is an
|
|
224
243
|
* authorization failure, and widening every method's error channel to say so
|
package/GitHubClient.js
CHANGED
|
@@ -23,12 +23,27 @@ const unstubbed = (member) => {
|
|
|
23
23
|
* literal and gets both sides typed, with no callback, no type parameter to
|
|
24
24
|
* invent, and no cast.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
26
|
+
* Nothing here asks the caller to invent a response type: the route literal is
|
|
27
|
+
* the only thing written, and a mismatch between a route and its parameters is
|
|
28
|
+
* a compile error.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { GitHubClient } from "@effected/github";
|
|
33
|
+
* import { Effect, Redacted } from "effect";
|
|
34
|
+
*
|
|
35
|
+
* const program = Effect.gen(function* () {
|
|
36
|
+
* const client = yield* GitHubClient;
|
|
37
|
+
* const release = yield* client.request("GET /repos/{owner}/{repo}/releases/latest", {
|
|
38
|
+
* owner: "effect-ts",
|
|
39
|
+
* repo: "effect",
|
|
40
|
+
* });
|
|
41
|
+
* return release.tag_name;
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* const layer = GitHubClient.layerFromToken({ token: Redacted.make("ghp_example") });
|
|
45
|
+
* Effect.runPromise(program.pipe(Effect.provide(layer)));
|
|
46
|
+
* ```
|
|
32
47
|
*
|
|
33
48
|
* @public
|
|
34
49
|
*/
|
|
@@ -51,10 +66,7 @@ var GitHubClient = class GitHubClient extends Context.Service()("@effected/githu
|
|
|
51
66
|
* provides a provider instead of mutating the environment and a non-Actions
|
|
52
67
|
* consumer can source the token however it likes.
|
|
53
68
|
*
|
|
54
|
-
* Construction fails with core's `ConfigError`
|
|
55
|
-
* configured". The layer this replaces failed with a **wire-failure** error
|
|
56
|
-
* type instead, which is why one consumer wrapped it in `Layer.orDie` under a
|
|
57
|
-
* five-line comment explaining that the error did not mean what it said.
|
|
69
|
+
* Construction fails with core's `ConfigError` when no token is configured.
|
|
58
70
|
*/
|
|
59
71
|
static layerFromConfig = (options = {}) => Layer.effect(this, Effect.gen(function* () {
|
|
60
72
|
const token = yield* Config.Redacted(options.name ?? "GITHUB_TOKEN");
|
|
@@ -90,17 +102,11 @@ var GitHubClient = class GitHubClient extends Context.Service()("@effected/githu
|
|
|
90
102
|
* A double over recorded responses that **pages them for real**.
|
|
91
103
|
*
|
|
92
104
|
* @remarks
|
|
93
|
-
* The
|
|
94
|
-
*
|
|
95
|
-
* because it reimplements nothing: it builds a `PageSource` over the recorded
|
|
105
|
+
* The recorded-response double in this package. It reimplements no behavior:
|
|
106
|
+
* it builds a `PageSource` over the recorded
|
|
96
107
|
* array and hands it to the same `paginate` engine the live client uses, so
|
|
97
108
|
* `perPage` and `maxPages` cannot behave differently here than in production.
|
|
98
109
|
*
|
|
99
|
-
* The double it replaces named its pagination parameters `_options` and
|
|
100
|
-
* ignored them, returning every recorded page regardless of what the caller
|
|
101
|
-
* asked for — which made every truncation path in every consumer
|
|
102
|
-
* structurally untestable.
|
|
103
|
-
*
|
|
104
110
|
* `fixtures.requested` is appended to as the test runs, so a suite can assert
|
|
105
111
|
* which routes were walked and at what page size.
|
|
106
112
|
*/
|
package/GitHubCommit.js
CHANGED
|
@@ -89,11 +89,30 @@ var CommitComparison = class extends Schema.Class("CommitComparison")({
|
|
|
89
89
|
files: Schema.Array(CommitFile)
|
|
90
90
|
}) {};
|
|
91
91
|
/**
|
|
92
|
-
*
|
|
92
|
+
* Read commits, compare refs and list changed files through GitHub's commits
|
|
93
|
+
* API.
|
|
94
|
+
*
|
|
95
|
+
* @remarks
|
|
96
|
+
* Provide it with {@link GitHubCommit.layer}, which needs a `GitHubClient`; each
|
|
97
|
+
* method also needs a `Repo` in `R`. For commits as Git Database objects (trees,
|
|
98
|
+
* parents), use `GitCommit` instead.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* import { GitHubCommit } from "@effected/github";
|
|
103
|
+
* import { Effect } from "effect";
|
|
104
|
+
*
|
|
105
|
+
* const changedSinceMain = Effect.gen(function* () {
|
|
106
|
+
* const commits = yield* GitHubCommit;
|
|
107
|
+
* const comparison = yield* commits.compare("main", "feature");
|
|
108
|
+
* return comparison.files.map((file) => file.path);
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
93
111
|
*
|
|
94
112
|
* @public
|
|
95
113
|
*/
|
|
96
114
|
var GitHubCommit = class GitHubCommit extends Context.Service()("@effected/github/GitHubCommit") {
|
|
115
|
+
/** The live service, built over a `GitHubClient`. */
|
|
97
116
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
98
117
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
99
118
|
static makeTest = (overrides = {}) => ({
|
package/GitHubContent.js
CHANGED
|
@@ -5,11 +5,29 @@ import { Context, Effect, Layer, Option } from "effect";
|
|
|
5
5
|
|
|
6
6
|
//#region src/GitHubContent.ts
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Read a text file out of a repository at a ref, with absence as an `Option`
|
|
9
|
+
* when you want it.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* Provide it with {@link GitHubContent.layer}, which needs a `GitHubClient`;
|
|
13
|
+
* each method also needs a `Repo` in `R`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { GitHubContent } from "@effected/github";
|
|
18
|
+
* import { Effect, Option } from "effect";
|
|
19
|
+
*
|
|
20
|
+
* const readme = Effect.gen(function* () {
|
|
21
|
+
* const content = yield* GitHubContent;
|
|
22
|
+
* const file = yield* content.getFileOption("README.md", { ref: "main" });
|
|
23
|
+
* return Option.getOrElse(file, () => "");
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
9
26
|
*
|
|
10
27
|
* @public
|
|
11
28
|
*/
|
|
12
29
|
var GitHubContent = class GitHubContent extends Context.Service()("@effected/github/GitHubContent") {
|
|
30
|
+
/** The live service, built over a `GitHubClient`. */
|
|
13
31
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
14
32
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
15
33
|
static makeTest = (overrides = {}) => ({
|
package/GitHubError.js
CHANGED
|
@@ -6,12 +6,9 @@ import { Schema } from "effect";
|
|
|
6
6
|
* Why a GitHub call failed, as a value you can branch on.
|
|
7
7
|
*
|
|
8
8
|
* @remarks
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* tested `includes("already") || includes("exists")`, and another re-issued an
|
|
13
|
-
* existence check after every failed branch creation because it could not tell
|
|
14
|
-
* "someone else created it" from "that failed".
|
|
9
|
+
* Branch on this discriminant instead of string-matching an error message:
|
|
10
|
+
* `alreadyExists` tells "someone else created it" apart from "that failed", so
|
|
11
|
+
* an upsert needs no follow-up existence check.
|
|
15
12
|
*
|
|
16
13
|
* @public
|
|
17
14
|
*/
|
|
@@ -75,16 +72,9 @@ var GitHubValidationEntry = class extends Schema.Class("GitHubValidationEntry")(
|
|
|
75
72
|
* Every REST failure this package produces, from every resource.
|
|
76
73
|
*
|
|
77
74
|
* @remarks
|
|
78
|
-
* One error class, not one per resource
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* site that disappears entirely now that `GitBranch.upsert` exists. Eighteen
|
|
82
|
-
* near-identical error classes and thirteen near-identical mapper closures
|
|
83
|
-
* bought that one match.
|
|
84
|
-
*
|
|
85
|
-
* What replaces them is {@link GitHubErrorKind} for routing and `operation` for
|
|
86
|
-
* identification. This mirrors `@effected/git`, where the rule is that no
|
|
87
|
-
* consumer ever string-matches stderr because classification happens once.
|
|
75
|
+
* One error class, not one per resource: {@link GitHubErrorKind} routes and
|
|
76
|
+
* `operation` identifies what was attempted. This mirrors `@effected/git`,
|
|
77
|
+
* where classification happens once so no consumer string-matches stderr.
|
|
88
78
|
*
|
|
89
79
|
* @public
|
|
90
80
|
*/
|
|
@@ -103,8 +93,8 @@ var GitHubError = class GitHubError extends Schema.TaggedError()("GitHubError",
|
|
|
103
93
|
* @remarks
|
|
104
94
|
* Written by the client from `retry-after` or the rate-limit reset, and read
|
|
105
95
|
* by exactly one thing: the retry `Schedule`. It is a policy input, not
|
|
106
|
-
* information for a caller
|
|
107
|
-
* `retryable`
|
|
96
|
+
* information for a caller. Whether a failure is worth retrying at all is the
|
|
97
|
+
* derived `retryable` getter.
|
|
108
98
|
*/
|
|
109
99
|
retryAfterMillis: Schema.optionalKey(Schema.Int),
|
|
110
100
|
/**
|
|
@@ -176,7 +166,7 @@ var GitHubError = class GitHubError extends Schema.TaggedError()("GitHubError",
|
|
|
176
166
|
* @remarks
|
|
177
167
|
* The single classification step for the whole package — every resource
|
|
178
168
|
* method's failures come through here, so the taxonomy cannot drift between
|
|
179
|
-
* resources
|
|
169
|
+
* resources.
|
|
180
170
|
*
|
|
181
171
|
* `nowMillis` is passed in rather than read from the wall clock so the
|
|
182
172
|
* function stays pure and total: the rate-limit reset header is an absolute
|
|
@@ -276,9 +266,8 @@ const asRecord = (value) => typeof value === "object" && value !== null ? value
|
|
|
276
266
|
* multi-kilobyte "message".
|
|
277
267
|
*
|
|
278
268
|
* @remarks
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* one through means a log line with a whole web page in it.
|
|
269
|
+
* An HTML body is never a useful reason string, and letting one through means
|
|
270
|
+
* a log line with a whole web page in it.
|
|
282
271
|
*/
|
|
283
272
|
const sanitizeReason = (message) => {
|
|
284
273
|
const trimmed = message.trim();
|
package/GitHubIssue.js
CHANGED
|
@@ -37,10 +37,8 @@ var LinkedIssue = class extends Schema.Class("LinkedIssue")({
|
|
|
37
37
|
* branch or commit messages.
|
|
38
38
|
*
|
|
39
39
|
* @remarks
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* with the field aliased twice to get it — the single largest duplicated
|
|
43
|
-
* document in the survey.
|
|
40
|
+
* Resolved by querying the pull request's closing references twice, once with
|
|
41
|
+
* `userLinkedOnly`, and marking the issues present in the second result.
|
|
44
42
|
*/
|
|
45
43
|
userLinked: Schema.Boolean
|
|
46
44
|
}) {};
|
|
@@ -104,11 +102,30 @@ const CrossReferencedDocument = GraphQLDocument.make({
|
|
|
104
102
|
response: CrossReferencedResponse
|
|
105
103
|
})();
|
|
106
104
|
/**
|
|
107
|
-
*
|
|
105
|
+
* Read, list, close and comment on issues, and resolve the issues a pull
|
|
106
|
+
* request closes.
|
|
107
|
+
*
|
|
108
|
+
* @remarks
|
|
109
|
+
* Provide it with {@link GitHubIssue.layer}, which needs a `GitHubClient`; each
|
|
110
|
+
* method also needs a `Repo` in `R`.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* import { GitHubIssue } from "@effected/github";
|
|
115
|
+
* import { Effect } from "effect";
|
|
116
|
+
*
|
|
117
|
+
* const closeWontFix = (number: number) =>
|
|
118
|
+
* Effect.gen(function* () {
|
|
119
|
+
* const issues = yield* GitHubIssue;
|
|
120
|
+
* yield* issues.comment(number, "Closing as not planned.");
|
|
121
|
+
* yield* issues.close(number, "not_planned");
|
|
122
|
+
* });
|
|
123
|
+
* ```
|
|
108
124
|
*
|
|
109
125
|
* @public
|
|
110
126
|
*/
|
|
111
127
|
var GitHubIssue = class GitHubIssue extends Context.Service()("@effected/github/GitHubIssue") {
|
|
128
|
+
/** The live service, built over a `GitHubClient`. */
|
|
112
129
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
113
130
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
114
131
|
static makeTest = (overrides = {}) => ({
|
|
@@ -139,15 +156,14 @@ const unstubbed = (member) => {
|
|
|
139
156
|
* silence it, because the fetch wrapper resolves `request.log ?? console`.
|
|
140
157
|
* `PATCH /repos/{owner}/{repo}/issues/{issue_number}` is on that list (the
|
|
141
158
|
* singular `assignee` parameter was removed), so every `close` warned once per
|
|
142
|
-
* issue in consumer workflow logs
|
|
159
|
+
* issue in consumer workflow logs.
|
|
143
160
|
*
|
|
144
161
|
* Nothing this module sends or reads changed in 2026-03-10: `state` and
|
|
145
162
|
* `state_reason` are untouched, and the projections read only fields both
|
|
146
163
|
* versions carry. Pinning the current version is therefore the whole fix, and
|
|
147
164
|
* it deliberately does **not** hide future deprecations — when GitHub
|
|
148
165
|
* deprecates 2026-03-10 in turn, the header comes back and octokit warns
|
|
149
|
-
* again.
|
|
150
|
-
* `Deprecation`/`Sunset` headers.
|
|
166
|
+
* again.
|
|
151
167
|
*/
|
|
152
168
|
const API_VERSION_HEADERS = { "x-github-api-version": "2026-03-10" };
|
|
153
169
|
/** GitHub sends a label as either a string or an object; callers want the name. */
|
package/GitHubRelease.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Context, Effect, Layer, Option, Schema } from "effect";
|
|
|
5
5
|
|
|
6
6
|
//#region src/GitHubRelease.ts
|
|
7
7
|
/**
|
|
8
|
-
* A release.
|
|
8
|
+
* A release, projected to the fields callers read.
|
|
9
9
|
*
|
|
10
10
|
* @public
|
|
11
11
|
*/
|
|
@@ -35,11 +35,29 @@ var ReleaseAsset = class extends Schema.Class("ReleaseAsset")({
|
|
|
35
35
|
size: Schema.Int
|
|
36
36
|
}) {};
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Create, read, list and update GitHub releases, and upload and list their
|
|
39
|
+
* assets.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* Provide it with {@link GitHubRelease.layer}, which needs a `GitHubClient`;
|
|
43
|
+
* each method also needs a `Repo` in `R`.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { GitHubRelease } from "@effected/github";
|
|
48
|
+
* import { Effect } from "effect";
|
|
49
|
+
*
|
|
50
|
+
* const publish = Effect.gen(function* () {
|
|
51
|
+
* const releases = yield* GitHubRelease;
|
|
52
|
+
* const release = yield* releases.create({ tag: "v1.2.3", generateReleaseNotes: true });
|
|
53
|
+
* return release.url;
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
39
56
|
*
|
|
40
57
|
* @public
|
|
41
58
|
*/
|
|
42
59
|
var GitHubRelease = class GitHubRelease extends Context.Service()("@effected/github/GitHubRelease") {
|
|
60
|
+
/** The live service, built over a `GitHubClient`. */
|
|
43
61
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
44
62
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
45
63
|
static makeTest = (overrides = {}) => ({
|