@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/GitHubRepository.js
CHANGED
|
@@ -10,10 +10,7 @@ import { Context, Effect, Layer, Schema } from "effect";
|
|
|
10
10
|
*
|
|
11
11
|
* @remarks
|
|
12
12
|
* The supported spelling for "apply only what was configured" — the natural
|
|
13
|
-
* shape for a sync action,
|
|
14
|
-
* Without it a consumer under the recommended tsconfig is quietly pushed toward
|
|
15
|
-
* `as`, which is a real cost in a package whose stated design property is that
|
|
16
|
-
* the route is the key and there are no casts.
|
|
13
|
+
* shape for a sync action, without a cast at the call site.
|
|
17
14
|
*
|
|
18
15
|
* Dropping the key rather than sending `undefined` is what the wire needs:
|
|
19
16
|
* `PATCH` treats an absent field as "leave it alone", while an explicit `null`
|
|
@@ -74,12 +71,9 @@ const SECURITY_ANALYSIS_STATUS_FIELDS = /* @__PURE__ */ new Set([
|
|
|
74
71
|
* `PATCH /repos/{owner}/{repo}` route accepts none of them, and
|
|
75
72
|
* `has_discussions` is the treacherous one: the REST **read** returns it, so
|
|
76
73
|
* routing its write to the PATCH looks symmetric, but the PATCH silently
|
|
77
|
-
* ignores unknown body fields and answers 200
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* Field names verified against `UpdateRepositoryInput` by live introspection,
|
|
82
|
-
* 2026-08-15.
|
|
74
|
+
* ignores unknown body fields and answers 200, so the write would look applied
|
|
75
|
+
* while the repository never changed. Setting any of these forces a second
|
|
76
|
+
* round trip to learn the repository's node id.
|
|
83
77
|
*
|
|
84
78
|
* @public
|
|
85
79
|
*/
|
|
@@ -175,11 +169,31 @@ const preparePatch = (patch) => {
|
|
|
175
169
|
return out;
|
|
176
170
|
};
|
|
177
171
|
/**
|
|
178
|
-
*
|
|
172
|
+
* Read and update a repository's settings, including settings only GraphQL can
|
|
173
|
+
* write, and look up its default branch, node id and owner type.
|
|
174
|
+
*
|
|
175
|
+
* @remarks
|
|
176
|
+
* Provide it with {@link GitHubRepository.layer}, which needs a `GitHubClient`;
|
|
177
|
+
* every member also needs a `Repo` in `R`. `settings`, `defaultBranch`, `nodeId`
|
|
178
|
+
* and `ownerType` are `Effect` values, not functions.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* import { GitHubRepository } from "@effected/github";
|
|
183
|
+
* import { Effect } from "effect";
|
|
184
|
+
*
|
|
185
|
+
* const program = Effect.gen(function* () {
|
|
186
|
+
* const repository = yield* GitHubRepository;
|
|
187
|
+
* const branch = yield* repository.defaultBranch;
|
|
188
|
+
* const applied = yield* repository.applySettings({ has_wiki: false, has_discussions: true });
|
|
189
|
+
* return { branch, applied }; // applied.rest, applied.graphql
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
179
192
|
*
|
|
180
193
|
* @public
|
|
181
194
|
*/
|
|
182
195
|
var GitHubRepository = class GitHubRepository extends Context.Service()("@effected/github/GitHubRepository") {
|
|
196
|
+
/** The live service, built over a `GitHubClient`. */
|
|
183
197
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
184
198
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
185
199
|
static makeTest = (overrides = {}) => ({
|
package/GitTag.js
CHANGED
|
@@ -39,11 +39,29 @@ const versionFromTag = (tag) => {
|
|
|
39
39
|
return stripped === "" ? Option.none() : Option.some(stripped);
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* Create, move, resolve, list and delete tag refs through GitHub's Git Database
|
|
43
|
+
* API, and find the newest version-shaped tag.
|
|
44
|
+
*
|
|
45
|
+
* @remarks
|
|
46
|
+
* Provide it with {@link GitTag.layer}, which needs a `GitHubClient`; each
|
|
47
|
+
* method also needs a `Repo` in `R`.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { GitTag } from "@effected/github";
|
|
52
|
+
* import { Effect, Option } from "effect";
|
|
53
|
+
*
|
|
54
|
+
* const latest = Effect.gen(function* () {
|
|
55
|
+
* const tags = yield* GitTag;
|
|
56
|
+
* const newest = yield* tags.latestSemver({ prefix: "v" });
|
|
57
|
+
* return Option.map(newest, (tag) => tag.version.toString());
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
43
60
|
*
|
|
44
61
|
* @public
|
|
45
62
|
*/
|
|
46
63
|
var GitTag = class GitTag extends Context.Service()("@effected/github/GitTag") {
|
|
64
|
+
/** The live service, built over a `GitHubClient`. */
|
|
47
65
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
48
66
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
49
67
|
static makeTest = (overrides = {}) => ({
|
package/GraphQL.js
CHANGED
|
@@ -19,7 +19,7 @@ var GraphQLErrorEntry = class extends Schema.Class("GraphQLErrorEntry")({
|
|
|
19
19
|
* @remarks
|
|
20
20
|
* Separate from `GitHubError` because GraphQL genuinely answers differently:
|
|
21
21
|
* a 200 response can still carry failures, and it carries a **list** of them.
|
|
22
|
-
* `errors`
|
|
22
|
+
* `errors` carries every entry GitHub reported, in order.
|
|
23
23
|
*
|
|
24
24
|
* @public
|
|
25
25
|
*/
|
|
@@ -29,9 +29,8 @@ var GitHubGraphQLError = class GitHubGraphQLError extends Schema.TaggedError()("
|
|
|
29
29
|
*
|
|
30
30
|
* @remarks
|
|
31
31
|
* `"alreadyExists"` exists here for the same reason it exists on the REST
|
|
32
|
-
* error:
|
|
33
|
-
*
|
|
34
|
-
* make project creation idempotent.
|
|
32
|
+
* error: it lets a caller make a create idempotent without lowercasing the
|
|
33
|
+
* message and grepping it.
|
|
35
34
|
*/
|
|
36
35
|
kind: Schema.Literals([
|
|
37
36
|
"alreadyExists",
|
|
@@ -131,25 +130,29 @@ const classify = (status, entries, reason, retryAfterMillis) => {
|
|
|
131
130
|
*
|
|
132
131
|
* @remarks
|
|
133
132
|
* This is the mechanism that makes `client.graphql` return a **domain value**
|
|
134
|
-
* rather than an `unknown` the caller casts
|
|
135
|
-
* query
|
|
136
|
-
* so every consumer wrote its own response interface and hoped.
|
|
133
|
+
* rather than an `unknown` the caller casts: the response schema ties the
|
|
134
|
+
* query to its decoded type.
|
|
137
135
|
*
|
|
138
|
-
*
|
|
139
|
-
* its own
|
|
140
|
-
*
|
|
141
|
-
* without this package having to know about its schema.
|
|
136
|
+
* This package owns the documents its resource services need; a consumer with a
|
|
137
|
+
* domain of its own (GitHub Projects, say) builds its own `GraphQLDocument` and
|
|
138
|
+
* gets the same typing and the same error taxonomy.
|
|
142
139
|
*
|
|
143
140
|
* @example
|
|
144
141
|
* ```ts
|
|
145
|
-
* import { GraphQLDocument } from "@effected/github";
|
|
146
|
-
* import { Schema } from "effect";
|
|
142
|
+
* import { GitHubClient, GraphQLDocument } from "@effected/github";
|
|
143
|
+
* import { Effect, Schema } from "effect";
|
|
147
144
|
*
|
|
148
|
-
* const
|
|
149
|
-
* name: "
|
|
150
|
-
* document: `query {
|
|
151
|
-
* response: Schema.Struct({
|
|
152
|
-
* })<{ readonly
|
|
145
|
+
* const OwnerLogin = GraphQLDocument.make({
|
|
146
|
+
* name: "ownerLogin",
|
|
147
|
+
* document: `query ($owner: String!) { repositoryOwner(login: $owner) { login } }`,
|
|
148
|
+
* response: Schema.Struct({ repositoryOwner: Schema.NullOr(Schema.Struct({ login: Schema.String })) }),
|
|
149
|
+
* })<{ readonly owner: string }>();
|
|
150
|
+
*
|
|
151
|
+
* const login = Effect.gen(function* () {
|
|
152
|
+
* const client = yield* GitHubClient;
|
|
153
|
+
* const data = yield* client.graphql(OwnerLogin, { owner: "effect-ts" });
|
|
154
|
+
* return data.repositoryOwner?.login;
|
|
155
|
+
* });
|
|
153
156
|
* ```
|
|
154
157
|
*
|
|
155
158
|
* @public
|
package/PullRequest.js
CHANGED
|
@@ -18,13 +18,9 @@ const MergeMethod = Schema.Literals([
|
|
|
18
18
|
*
|
|
19
19
|
* @remarks
|
|
20
20
|
* This is the **domain** shape, and the only pull-request type this package
|
|
21
|
-
* exports.
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
* link `html_url`, where this class flattens them to `head`/`headSha` and
|
|
25
|
-
* spells it `url`. Reading the wrong one produces code that typechecks against
|
|
26
|
-
* a member set the boundary will never hand it. If you are writing a test
|
|
27
|
-
* double or a fixture, this is the type you build.
|
|
21
|
+
* exports. It flattens GitHub's nested `head`/`base` objects to
|
|
22
|
+
* `head`/`headSha` and `base`/`baseSha`, and spells the link `url`. If you are
|
|
23
|
+
* writing a test double or a fixture, this is the type you build.
|
|
28
24
|
*
|
|
29
25
|
* @public
|
|
30
26
|
*/
|
|
@@ -85,11 +81,34 @@ const GRAPHQL_MERGE_METHOD = {
|
|
|
85
81
|
rebase: "REBASE"
|
|
86
82
|
};
|
|
87
83
|
/**
|
|
88
|
-
*
|
|
84
|
+
* Read, list, create, update, merge and label pull requests, and control
|
|
85
|
+
* auto-merge.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* Provide it with {@link PullRequest.layer}, which needs a `GitHubClient`; each
|
|
89
|
+
* method also needs a `Repo` in `R`.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* import { PullRequest } from "@effected/github";
|
|
94
|
+
* import { Effect } from "effect";
|
|
95
|
+
*
|
|
96
|
+
* const openOrUpdate = Effect.gen(function* () {
|
|
97
|
+
* const pulls = yield* PullRequest;
|
|
98
|
+
* const { pullRequest, created } = yield* pulls.upsert({
|
|
99
|
+
* title: "chore: release",
|
|
100
|
+
* head: "changeset-release/main",
|
|
101
|
+
* base: "main",
|
|
102
|
+
* });
|
|
103
|
+
* yield* pulls.setAutoMerge(pullRequest, "squash");
|
|
104
|
+
* return created;
|
|
105
|
+
* });
|
|
106
|
+
* ```
|
|
89
107
|
*
|
|
90
108
|
* @public
|
|
91
109
|
*/
|
|
92
110
|
var PullRequest = class PullRequest extends Context.Service()("@effected/github/PullRequest") {
|
|
111
|
+
/** The live service, built over a `GitHubClient`. */
|
|
93
112
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
94
113
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
95
114
|
static makeTest = (overrides = {}) => ({
|
package/PullRequestComment.js
CHANGED
|
@@ -8,11 +8,9 @@ import { Context, Effect, Layer, Option, Schema } from "effect";
|
|
|
8
8
|
* The hidden marker that makes a comment findable again.
|
|
9
9
|
*
|
|
10
10
|
* @remarks
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* testable without a client, and the library has no opinion about whose comments
|
|
15
|
-
* these are.
|
|
11
|
+
* The marker is `<!-- namespace:key -->`, an HTML comment appended to the body.
|
|
12
|
+
* The namespace is the caller's, so the library has no opinion about whose
|
|
13
|
+
* comments these are, and a marker is testable without a client.
|
|
16
14
|
*
|
|
17
15
|
* @public
|
|
18
16
|
*/
|
|
@@ -32,7 +30,7 @@ var CommentMarker = class extends Schema.Class("CommentMarker")({
|
|
|
32
30
|
}
|
|
33
31
|
};
|
|
34
32
|
/**
|
|
35
|
-
* A comment this package wrote or found.
|
|
33
|
+
* A comment this package wrote or found: its id, body and web URL.
|
|
36
34
|
*
|
|
37
35
|
* @public
|
|
38
36
|
*/
|
|
@@ -42,11 +40,31 @@ var CommentRecord = class extends Schema.Class("CommentRecord")({
|
|
|
42
40
|
url: Schema.String
|
|
43
41
|
}) {};
|
|
44
42
|
/**
|
|
45
|
-
*
|
|
43
|
+
* Post, update, find and delete comments on a pull request or issue, including
|
|
44
|
+
* a "sticky" comment kept current through a `CommentMarker`.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* Provide it with {@link PullRequestComment.layer}, which needs a `GitHubClient`;
|
|
48
|
+
* each method also needs a `Repo` in `R`.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { CommentMarker, PullRequestComment } from "@effected/github";
|
|
53
|
+
* import { Effect } from "effect";
|
|
54
|
+
*
|
|
55
|
+
* const marker = CommentMarker.make({ namespace: "my-action", key: "report" });
|
|
56
|
+
*
|
|
57
|
+
* const report = (pullNumber: number, body: string) =>
|
|
58
|
+
* Effect.gen(function* () {
|
|
59
|
+
* const comments = yield* PullRequestComment;
|
|
60
|
+
* return yield* comments.upsert(pullNumber, marker, body); // edits the marked comment in place
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
46
63
|
*
|
|
47
64
|
* @public
|
|
48
65
|
*/
|
|
49
66
|
var PullRequestComment = class PullRequestComment extends Context.Service()("@effected/github/PullRequestComment") {
|
|
67
|
+
/** The live service, built over a `GitHubClient`. */
|
|
50
68
|
static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
|
|
51
69
|
/** An in-memory double; unstubbed members die naming themselves. */
|
|
52
70
|
static makeTest = (overrides = {}) => ({
|
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
Typed GitHub REST and GraphQL for [Effect](https://effect.website) v4. `client.request("GET /repos/{owner}/{repo}", { owner, repo })` types both the parameters and the returned `data` from the route literal alone — no `operation: string`, no callback, no cast. One `GitHubError` covers every REST failure with a `kind` you branch on instead of grepping a message, one pagination engine backs every paginating route and `client.request`'s `Stream` form, and a set of resource services (`GitBranch`, `GitTag`, `CheckRun`, `PullRequest`, `PullRequestComment`, `GitHubRelease`, `Attestation`) turn multi-call dances — "does this branch already exist?", "conclude this check run no matter how the program exits" — into one call. A second tier writes the configuration half: secrets, variables, rulesets, deployment environments, the security toggles and CodeQL default setup. `GitHubApp` mints and revokes installation tokens for App auth.
|
|
9
9
|
|
|
10
|
-
> **Pre
|
|
11
|
-
>
|
|
12
|
-
> `1.0.0`
|
|
13
|
-
>
|
|
10
|
+
> **Pre-`1.0.0`.** This package is part of the `@effected/*` kit, built on stable
|
|
11
|
+
> Effect v4 (`effect` `^4.0.0`) and still in `0.x` development. Stable Effect
|
|
12
|
+
> makes a kit `1.0.0` possible, not automatic. To keep your `effect` and
|
|
13
|
+
> `@effect/*` versions on the line the kit is built and tested against, install
|
|
14
14
|
> [`@effected/pnpm-plugin-effect`](https://www.npmjs.com/package/@effected/pnpm-plugin-effect).
|
|
15
15
|
>
|
|
16
16
|
> **Stability: unstable.** This package's API surface is not yet considered
|
|
@@ -287,7 +287,7 @@ const TestClient = GitHubClient.layerFixture(fixtures);
|
|
|
287
287
|
// A route with no entry above DIES naming itself, rather than failing typed.
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
-
- **An unstubbed route dies by default.** A missing fixture is test wiring rather than a domain outcome, and a typed failure is only loud in code that does not catch — a program handling `GitHubError` per resource turns a missing stub into a different execution path, and the assertions then fail for reasons that name no fixture. `unstubbed: "fail"`
|
|
290
|
+
- **An unstubbed route dies by default.** A missing fixture is test wiring rather than a domain outcome, and a typed failure is only loud in code that does not catch — a program handling `GitHubError` per resource turns a missing stub into a different execution path, and the assertions then fail for reasons that name no fixture. `unstubbed: "fail"` fails the route with a typed not-found instead, and `"empty"` serves an empty value for a suite whose subject is decisions rather than endpoints.
|
|
291
291
|
- **A recorded `GitHubError` value is the response.** That is how a suite stubs a 404, a 422 or a rate limit deliberately. Leaning on a route's absence says only "unwired"; a recorded error says which route fails and why.
|
|
292
292
|
- **`fixtures.requested` records every call.** Each `RecordedCall` carries the `kind` of surface used, the `route` (the document name for `graphql`), the `params` the call was made with, and `perPage` for a paginated read. Params are what let a test assert what a method *sent*, which is the question any normalising write turns on.
|
|
293
293
|
|
package/Repo.js
CHANGED
|
@@ -56,33 +56,26 @@ var RepoRef = class RepoRef extends Schema.Class("RepoRef")({
|
|
|
56
56
|
* `{ owner, repo }` argument**, which is what makes a read like
|
|
57
57
|
* `GitHubRepository.defaultBranch` a single expression instead of a preamble.
|
|
58
58
|
*
|
|
59
|
-
* The
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
59
|
+
* The coordinate is a plain value. The only env-driven way to get one is
|
|
60
|
+
* {@link Repo.layerFromConfig}, which is named for being env-driven, and a
|
|
61
|
+
* program that acts on several repositories uses {@link Repo.provide}.
|
|
62
|
+
*
|
|
63
|
+
* The service shape is entirely one immutable value (a `RepoRef`), with no
|
|
64
|
+
* methods, so `Layer.succeed` is the correct test double.
|
|
65
65
|
*
|
|
66
66
|
* @example
|
|
67
67
|
* ```ts
|
|
68
|
-
* import { Repo } from "@effected/github";
|
|
68
|
+
* import { Repo, RepoRef } from "@effected/github";
|
|
69
69
|
* import { Effect } from "effect";
|
|
70
70
|
*
|
|
71
71
|
* declare const syncOne: Effect.Effect<void, never, Repo>;
|
|
72
|
-
* declare const targets: ReadonlyArray<
|
|
72
|
+
* declare const targets: ReadonlyArray<RepoRef>;
|
|
73
73
|
*
|
|
74
74
|
* const syncAll = Effect.forEach(targets, (target) => syncOne.pipe(Repo.provide(target)), {
|
|
75
75
|
* concurrency: 4,
|
|
76
76
|
* });
|
|
77
77
|
* ```
|
|
78
78
|
*
|
|
79
|
-
* **A deliberate exception to "no non-effectful members on a service shape."**
|
|
80
|
-
* This shape is entirely one immutable value: there is nothing to leave
|
|
81
|
-
* unimplemented, so `Layer.mock` has nothing to degrade and `Layer.succeed` is
|
|
82
|
-
* the correct double. The rule exists to stop a sync member from quietly
|
|
83
|
-
* degrading a *mixed* shape's partial mock. The boundary to hold: the moment a
|
|
84
|
-
* method appears here, this is a service again and the rule applies.
|
|
85
|
-
*
|
|
86
79
|
* @public
|
|
87
80
|
*/
|
|
88
81
|
var Repo = class Repo extends Context.Service()("@effected/github/Repo") {
|
package/RepositorySecret.js
CHANGED
|
@@ -26,7 +26,8 @@ const ROUTES = {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Write, list and delete repository and environment secrets, encrypted
|
|
30
|
+
* client-side before they leave the process.
|
|
30
31
|
*
|
|
31
32
|
* @remarks
|
|
32
33
|
* Every write is a **two-step**: fetch the store's public key, then `PUT` a
|
|
@@ -42,10 +43,27 @@ const ROUTES = {
|
|
|
42
43
|
* the type; this module performs the single `Redacted.value` unwrap, at the
|
|
43
44
|
* moment of encryption, and the sealed box is what continues.
|
|
44
45
|
*
|
|
46
|
+
* Provide it with {@link RepositorySecret.layer}, which needs a `GitHubClient`;
|
|
47
|
+
* each method also needs a `Repo` in `R`.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { RepositorySecret } from "@effected/github";
|
|
52
|
+
* import { Effect, Redacted } from "effect";
|
|
53
|
+
*
|
|
54
|
+
* const program = Effect.gen(function* () {
|
|
55
|
+
* const secrets = yield* RepositorySecret;
|
|
56
|
+
* yield* secrets.set("NPM_TOKEN", Redacted.make("npm_example"), "actions");
|
|
57
|
+
* return yield* secrets.list("actions"); // names only; values are never returned
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
45
61
|
* @public
|
|
46
62
|
*/
|
|
47
63
|
var RepositorySecret = class RepositorySecret extends Context.Service()("@effected/github/RepositorySecret") {
|
|
48
64
|
/**
|
|
65
|
+
* The live service, built over a `GitHubClient`.
|
|
66
|
+
*
|
|
49
67
|
* @remarks
|
|
50
68
|
* `(client) => make(client)` rather than `make`: a static initializer runs
|
|
51
69
|
* while the module body is still evaluating, so naming a `const` declared
|
package/RepositorySecurity.js
CHANGED
|
@@ -4,7 +4,8 @@ import { Context, Effect, Layer } from "effect";
|
|
|
4
4
|
|
|
5
5
|
//#region src/RepositorySecurity.ts
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Read and toggle Dependabot alerts, Dependabot security fixes and private
|
|
8
|
+
* vulnerability reporting.
|
|
8
9
|
*
|
|
9
10
|
* @remarks
|
|
10
11
|
* These are **not** `security_and_analysis` fields and cannot ride along on the
|
|
@@ -27,10 +28,28 @@ import { Context, Effect, Layer } from "effect";
|
|
|
27
28
|
* every other failure still fails. A 404 from the other two is a real failure
|
|
28
29
|
* and stays one, which is why the mapping is not applied uniformly.
|
|
29
30
|
*
|
|
31
|
+
* Provide it with {@link RepositorySecurity.layer}, which needs a
|
|
32
|
+
* `GitHubClient`; each method also needs a `Repo` in `R`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { RepositorySecurity } from "@effected/github";
|
|
37
|
+
* import { Effect } from "effect";
|
|
38
|
+
*
|
|
39
|
+
* const harden = Effect.gen(function* () {
|
|
40
|
+
* const security = yield* RepositorySecurity;
|
|
41
|
+
* yield* security.setVulnerabilityAlerts(true);
|
|
42
|
+
* yield* security.setPrivateVulnerabilityReporting(true);
|
|
43
|
+
* return yield* security.vulnerabilityAlerts(); // true
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
30
47
|
* @public
|
|
31
48
|
*/
|
|
32
49
|
var RepositorySecurity = class RepositorySecurity extends Context.Service()("@effected/github/RepositorySecurity") {
|
|
33
50
|
/**
|
|
51
|
+
* The live service, built over a `GitHubClient`.
|
|
52
|
+
*
|
|
34
53
|
* @remarks
|
|
35
54
|
* `(client) => make(client)` rather than `make`: a static initializer runs
|
|
36
55
|
* while the module body is still evaluating, so naming a `const` declared
|
package/RepositoryVariable.js
CHANGED
|
@@ -4,7 +4,8 @@ import { Context, Effect, Layer } from "effect";
|
|
|
4
4
|
|
|
5
5
|
//#region src/RepositoryVariable.ts
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Create or update, list and delete Actions variables on a repository and on
|
|
8
|
+
* its environments.
|
|
8
9
|
*
|
|
9
10
|
* @remarks
|
|
10
11
|
* No encryption and no public key, unlike secrets — but also **no upsert**,
|
|
@@ -12,10 +13,27 @@ import { Context, Effect, Layer } from "effect";
|
|
|
12
13
|
* the create and update routes are different endpoints with different verbs and
|
|
13
14
|
* neither tolerates the other's case.
|
|
14
15
|
*
|
|
16
|
+
* Provide it with {@link RepositoryVariable.layer}, which needs a
|
|
17
|
+
* `GitHubClient`; each method also needs a `Repo` in `R`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { RepositoryVariable } from "@effected/github";
|
|
22
|
+
* import { Effect } from "effect";
|
|
23
|
+
*
|
|
24
|
+
* const program = Effect.gen(function* () {
|
|
25
|
+
* const variables = yield* RepositoryVariable;
|
|
26
|
+
* yield* variables.set("DEPLOY_REGION", "eu-west-1"); // creates or updates
|
|
27
|
+
* return yield* variables.list();
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
15
31
|
* @public
|
|
16
32
|
*/
|
|
17
33
|
var RepositoryVariable = class RepositoryVariable extends Context.Service()("@effected/github/RepositoryVariable") {
|
|
18
34
|
/**
|
|
35
|
+
* The live service, built over a `GitHubClient`.
|
|
36
|
+
*
|
|
19
37
|
* @remarks
|
|
20
38
|
* `(client) => make(client)` rather than `make`: a static initializer runs
|
|
21
39
|
* while the module body is still evaluating, so naming a `const` declared
|
package/Resilience.js
CHANGED
|
@@ -10,11 +10,8 @@ import { Duration, Effect, Random, Schedule, Schema } from "effect";
|
|
|
10
10
|
* one. Read it through the client's `rateLimit` member when you want to pace
|
|
11
11
|
* yourself — **nothing in this package throttles on your behalf.**
|
|
12
12
|
*
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
* so an application that forgot to provide it got two private cells and a
|
|
16
|
-
* silently dead feature. Here the cell lives inside the client layer that writes
|
|
17
|
-
* it, and this is the only way to read it.
|
|
13
|
+
* The cell lives inside the client layer that writes it, so there is nothing
|
|
14
|
+
* extra to provide: reading it through the client is the only way.
|
|
18
15
|
*
|
|
19
16
|
* @public
|
|
20
17
|
*/
|
|
@@ -40,15 +37,22 @@ var RateLimitSnapshot = class extends Schema.Class("RateLimitSnapshot")({
|
|
|
40
37
|
*
|
|
41
38
|
* @remarks
|
|
42
39
|
* There is exactly **one** retry policy in this package, and it is wired into
|
|
43
|
-
* the client so every resource inherits it and no resource carries its own.
|
|
44
|
-
* package this replaces shipped four mutually inconsistent policies — a
|
|
45
|
-
* hand-rolled recursive loop, a second exported `Schedule` that ignored
|
|
46
|
-
* server-advised delays, a per-operation branch retry layered on top of the
|
|
47
|
-
* client's, and a rate-limiter retry with no predicate at all, which cheerfully
|
|
48
|
-
* retried permission denials — and consumers added two more on top.
|
|
40
|
+
* the client so every resource inherits it and no resource carries its own.
|
|
49
41
|
*
|
|
50
|
-
* Only failures that report `retryable` are retried, which for `GitHubError`
|
|
51
|
-
*
|
|
42
|
+
* Only failures that report `retryable` are retried, which for `GitHubError`
|
|
43
|
+
* means a transport failure or a rate limit. A 404, a validation rejection and
|
|
44
|
+
* an authorization failure fail on the first attempt.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { GitHubClient, RetryPolicy } from "@effected/github";
|
|
49
|
+
* import { Duration, Redacted } from "effect";
|
|
50
|
+
*
|
|
51
|
+
* const layer = GitHubClient.layerFromToken({
|
|
52
|
+
* token: Redacted.make("ghp_example"),
|
|
53
|
+
* retry: RetryPolicy.make({ ...RetryPolicy.default, maxRetries: 2, maxDelay: Duration.seconds(10) }),
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
52
56
|
*
|
|
53
57
|
* @public
|
|
54
58
|
*/
|
|
@@ -133,9 +137,7 @@ var RetryPolicy = class RetryPolicy extends Schema.Class("RetryPolicy")({
|
|
|
133
137
|
* Built on `Schedule.modifyDelay`, whose callback receives the schedule's
|
|
134
138
|
* `Metadata` — including the **input that failed**. That is what makes a
|
|
135
139
|
* header-driven policy expressible as a `Schedule` at all: the delay is a
|
|
136
|
-
* function of the error, not only of the attempt number.
|
|
137
|
-
* available is why the package this replaces hand-rolled a recursive retry
|
|
138
|
-
* loop instead of using `Effect.retry`.
|
|
140
|
+
* function of the error, not only of the attempt number.
|
|
139
141
|
*/
|
|
140
142
|
schedule() {
|
|
141
143
|
return Schedule.forever.pipe(Schedule.modifyDelay(({ input, attempt }) => Effect.map(Random.next, (random) => this.delayFor(input, attempt, random))), Schedule.while(({ input, attempt }) => attempt <= this.maxRetries && this.retries(input)));
|
package/Rest.js
CHANGED
|
@@ -5,10 +5,8 @@ import { Schema } from "effect";
|
|
|
5
5
|
* How far a paginated read should go.
|
|
6
6
|
*
|
|
7
7
|
* @remarks
|
|
8
|
-
* Both fields are honored by every paginating method in this package.
|
|
9
|
-
*
|
|
10
|
-
* of its eight call sites, so callers silently got 100-item pages and an
|
|
11
|
-
* unbounded walk with no way to say otherwise.
|
|
8
|
+
* Both fields are honored by every paginating method in this package. Unset,
|
|
9
|
+
* a read requests 100-item pages and walks until GitHub stops.
|
|
12
10
|
*
|
|
13
11
|
* `perPage` is **validated, not clamped**: GitHub caps a page at 100 and
|
|
14
12
|
* silently ignores anything larger, so a caller asking for 250 has a bug whose
|
package/Ruleset.js
CHANGED
|
@@ -5,7 +5,8 @@ import { Context, Effect, Layer } from "effect";
|
|
|
5
5
|
|
|
6
6
|
//#region src/Ruleset.ts
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Create or update, list and delete repository rulesets, and look up the team
|
|
9
|
+
* and role ids their bypass actors need.
|
|
9
10
|
*
|
|
10
11
|
* @remarks
|
|
11
12
|
* ## An inherited ruleset is never written to
|
|
@@ -17,14 +18,34 @@ import { Context, Effect, Layer } from "effect";
|
|
|
17
18
|
* that never mentioned the organization.
|
|
18
19
|
*
|
|
19
20
|
* {@link RulesetShape.upsert} filters on `source_type` before matching, so an
|
|
20
|
-
* inherited ruleset can never be the target of a write.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* inherited ruleset can never be the target of a write.
|
|
22
|
+
*
|
|
23
|
+
* Provide it with {@link Ruleset.layer}, which needs a `GitHubClient`; each
|
|
24
|
+
* method also needs a `Repo` in `R`.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { Ruleset } from "@effected/github";
|
|
29
|
+
* import { Effect } from "effect";
|
|
30
|
+
*
|
|
31
|
+
* const protectMain = Effect.gen(function* () {
|
|
32
|
+
* const rulesets = yield* Ruleset;
|
|
33
|
+
* yield* rulesets.upsert({
|
|
34
|
+
* name: "protect-main",
|
|
35
|
+
* target: "branch",
|
|
36
|
+
* enforcement: "active",
|
|
37
|
+
* conditions: { ref_name: { include: ["~DEFAULT_BRANCH"], exclude: [] } },
|
|
38
|
+
* rules: [{ type: "deletion" }],
|
|
39
|
+
* });
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
23
42
|
*
|
|
24
43
|
* @public
|
|
25
44
|
*/
|
|
26
45
|
var Ruleset = class Ruleset extends Context.Service()("@effected/github/Ruleset") {
|
|
27
46
|
/**
|
|
47
|
+
* The live service, built over a `GitHubClient`.
|
|
48
|
+
*
|
|
28
49
|
* @remarks
|
|
29
50
|
* `(client) => make(client)` rather than `make`: a static initializer runs
|
|
30
51
|
* while the module body is still evaluating, so naming a `const` declared
|
package/TokenPermissions.js
CHANGED
|
@@ -36,7 +36,9 @@ var PermissionGap = class extends Schema.Class("PermissionGap")({
|
|
|
36
36
|
* @public
|
|
37
37
|
*/
|
|
38
38
|
var ExtraPermission = class extends Schema.Class("ExtraPermission")({
|
|
39
|
+
/** The permission's name, e.g. `"contents"`. */
|
|
39
40
|
permission: Schema.String,
|
|
41
|
+
/** What the token has. */
|
|
40
42
|
granted: PermissionLevel,
|
|
41
43
|
/** What was asked for, when anything was. */
|
|
42
44
|
required: Schema.optionalKey(PermissionLevel)
|
|
@@ -80,17 +82,12 @@ var TokenPermissionError = class extends Schema.TaggedError()("TokenPermissionEr
|
|
|
80
82
|
* The permissions a token was granted, and what they satisfy.
|
|
81
83
|
*
|
|
82
84
|
* @remarks
|
|
83
|
-
* **A pure class, not a service.**
|
|
84
|
-
* `
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* Here there is no service, no layer and no double: a caller holds the
|
|
91
|
-
* permissions GitHub already gave it (`InstallationToken.permissions`) and
|
|
92
|
-
* compares them. The only `Effect`s are the two assertions, and they exist only
|
|
93
|
-
* because failing typed is more useful than returning a boolean.
|
|
85
|
+
* **A pure class, not a service.** It compares permission levels
|
|
86
|
+
* (`read < write < admin`), so there is no layer and no test double: a caller
|
|
87
|
+
* holds the permissions GitHub already gave it (`InstallationToken.permissions`)
|
|
88
|
+
* and compares them. The only `Effect`s are the two assertions, which fail with
|
|
89
|
+
* `TokenPermissionError` because failing typed is more useful than returning a
|
|
90
|
+
* boolean.
|
|
94
91
|
*
|
|
95
92
|
* @example
|
|
96
93
|
* ```ts
|