@usine/github 0.1.2 → 0.3.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/LICENSE +661 -21
- package/README.md +35 -56
- package/dist/access.d.ts +30 -8
- package/dist/access.d.ts.map +1 -1
- package/dist/access.js +31 -8
- package/dist/access.js.map +1 -1
- package/dist/credentials.d.ts +6 -6
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +6 -6
- package/dist/credentials.js.map +1 -1
- package/dist/error.d.ts +28 -10
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +34 -7
- package/dist/error.js.map +1 -1
- package/dist/github-app.d.ts +19 -10
- package/dist/github-app.d.ts.map +1 -1
- package/dist/github-app.js +69 -30
- package/dist/github-app.js.map +1 -1
- package/dist/graphql.d.ts +11 -0
- package/dist/graphql.d.ts.map +1 -0
- package/dist/graphql.js +47 -0
- package/dist/graphql.js.map +1 -0
- package/dist/http.d.ts +11 -5
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +27 -9
- package/dist/http.js.map +1 -1
- package/dist/installation.d.ts +17 -9
- package/dist/installation.d.ts.map +1 -1
- package/dist/installation.js +17 -9
- package/dist/installation.js.map +1 -1
- package/dist/item-history.d.ts +57 -0
- package/dist/item-history.d.ts.map +1 -0
- package/dist/item-history.js +190 -0
- package/dist/item-history.js.map +1 -0
- package/dist/item-writer.d.ts +3 -3
- package/dist/item-writer.d.ts.map +1 -1
- package/dist/item-writer.js +2 -2
- package/dist/item-writer.js.map +1 -1
- package/dist/jwt.d.ts +3 -3
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js +4 -4
- package/dist/jwt.js.map +1 -1
- package/dist/label-writer.d.ts +3 -3
- package/dist/label-writer.d.ts.map +1 -1
- package/dist/label-writer.js +3 -3
- package/dist/label-writer.js.map +1 -1
- package/dist/local-secrets.d.ts +2 -2
- package/dist/local-secrets.d.ts.map +1 -1
- package/dist/local-secrets.js +6 -6
- package/dist/local-secrets.js.map +1 -1
- package/dist/polling/content.js +2 -2
- package/dist/polling/detection.d.ts +21 -10
- package/dist/polling/detection.d.ts.map +1 -1
- package/dist/polling/detection.js +20 -10
- package/dist/polling/detection.js.map +1 -1
- package/dist/polling/labels.d.ts +5 -5
- package/dist/polling/labels.d.ts.map +1 -1
- package/dist/polling/labels.js +5 -5
- package/dist/polling/labels.js.map +1 -1
- package/dist/polling/payload.d.ts +12 -8
- package/dist/polling/payload.d.ts.map +1 -1
- package/dist/polling/payload.js +17 -12
- package/dist/polling/payload.js.map +1 -1
- package/dist/polling/polling.d.ts +27 -9
- package/dist/polling/polling.d.ts.map +1 -1
- package/dist/polling/polling.js +94 -56
- package/dist/polling/polling.js.map +1 -1
- package/dist/polling/queries.d.ts +6 -1
- package/dist/polling/queries.d.ts.map +1 -1
- package/dist/polling/queries.js +16 -0
- package/dist/polling/queries.js.map +1 -1
- package/package.json +14 -9
package/README.md
CHANGED
|
@@ -1,41 +1,23 @@
|
|
|
1
1
|
# @usine/github
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Gives a usine factory one GitHub App identity. It signs App JWTs, finds the App's installation, reports registry repos that the installation doesn't cover, and mints two kinds of installation token:
|
|
4
4
|
|
|
5
5
|
- an installation-wide token for orchestrator polling and writes;
|
|
6
|
-
- a token
|
|
6
|
+
- a token for one repo, for a worker attempt.
|
|
7
7
|
|
|
8
|
-
Both
|
|
8
|
+
Both tokens have a one-hour maximum lifetime. Expiry validation tolerates three minutes of clock skew and caps the local lease at one hour; expired tokens are rejected. Token values and the private key stay in Effect `Redacted` values.
|
|
9
9
|
|
|
10
|
-
Import `GitHubApp`, credentials, access results, and typed App errors from `@usine/github/github-app`.
|
|
10
|
+
Import `GitHubApp`, credentials, access results, and typed App errors from `@usine/github/github-app`. `/polling` provides `GitHubPolling` with its cursors, change-detection results, and payload snapshots, plus `RepositoryLabel`, `RepositoryLabelCatalog`, and `RepositoryLabelsNotModified` for label catalogs and conditional responses. Item and label writes use `/item-writer` and `/label-writer`, the App's own changes on an item come from `/item-history`, and local credential loading uses `/local-secrets`.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
`RepositoryLabelsNotModified` for reading label catalogs and handling conditional
|
|
14
|
-
responses. These contracts share the same entry point as `GitHubPolling`.
|
|
12
|
+
The 0.x APIs can change between minor releases.
|
|
15
13
|
|
|
16
|
-
##
|
|
14
|
+
## Set up the App
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
[Run a factory](https://docs.usine.dev/guides/) gives the steps under Connect a GitHub App: an App installable only on your account, without active webhooks, with Contents, Issues, and Pull requests read and write, Checks and Metadata read-only, and **No access** for everything else, including Workflows, Administration, and all organization and account permissions.
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
GitHub grants Metadata read access automatically. Contents lets installation tokens use Git over HTTPS; with Workflows off, worker tokens can't edit `.github/workflows/`. User authorization isn't required. Install the App once, on the account that owns the managed repos, for every repo in `usine.yaml`; a factory expects one installation. GitHub's references are [Registering a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app) and [Choosing permissions for a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app).
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
| --- | --- |
|
|
24
|
-
| Metadata | Read-only |
|
|
25
|
-
| Contents | Read and write |
|
|
26
|
-
| Issues | Read and write |
|
|
27
|
-
| Pull requests | Read and write |
|
|
28
|
-
| Checks | Read-only |
|
|
29
|
-
|
|
30
|
-
Leave Workflows, Administration, every organization permission, and every account permission at **No access**. GitHub grants Metadata read access automatically. The Contents permission lets installation tokens authenticate Git over HTTPS; Workflows remains off, so worker tokens cannot edit files under `.github/workflows/`.
|
|
31
|
-
|
|
32
|
-
Create the App, generate a private key, and note the numeric App ID. On the App's **Install App** page, install it on the account that owns the factory's managed repositories. Select every repository listed in `usine.yaml`. A factory expects one App installation, so don't install the same App on a second account.
|
|
33
|
-
|
|
34
|
-
GitHub's current setup references are [Registering a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app) and [Choosing permissions for a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app).
|
|
35
|
-
|
|
36
|
-
## Store the credentials
|
|
37
|
-
|
|
38
|
-
The local runtime file is `<instance>/.usine/github-app.json`. Keep `.usine/` in the instance repo's `.gitignore`, and set the file mode to `0600`.
|
|
20
|
+
Store the App ID and private key in `<instance>/.usine/github-app.json` with mode `0600`, and keep `.usine/` in the instance repo's `.gitignore`:
|
|
39
21
|
|
|
40
22
|
```json
|
|
41
23
|
{
|
|
@@ -44,20 +26,11 @@ The local runtime file is `<instance>/.usine/github-app.json`. Keep `.usine/` in
|
|
|
44
26
|
}
|
|
45
27
|
```
|
|
46
28
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```sh
|
|
50
|
-
mkdir -p .usine
|
|
51
|
-
install -m 600 /dev/null .usine/github-app.json
|
|
52
|
-
jq -n --argjson app_id "$APP_ID" --rawfile private_key factory.private-key.pem \
|
|
53
|
-
'{app_id: $app_id, private_key: $private_key}' > .usine/github-app.json
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
`USINE_GITHUB_APP_ID` and `USINE_GITHUB_PRIVATE_KEY` override their matching file fields. When both variables exist, the loader does not read the file.
|
|
29
|
+
`USINE_GITHUB_APP_ID` and `USINE_GITHUB_PRIVATE_KEY` override the matching fields. If both are set, the loader doesn't read the file.
|
|
57
30
|
|
|
58
31
|
## Compose the services
|
|
59
32
|
|
|
60
|
-
The package root has no Node dependency. Supply an HTTP client
|
|
33
|
+
The package root has no Node dependency. Supply an HTTP client and platform file and path layers at the edge:
|
|
61
34
|
|
|
62
35
|
```ts
|
|
63
36
|
import { NodeFileSystem, NodePath } from "@effect/platform-node"
|
|
@@ -65,7 +38,7 @@ import { InstanceDirectory } from "@usine/core/instance"
|
|
|
65
38
|
import { GitHubApp } from "@usine/github/github-app"
|
|
66
39
|
import { localSecretsLayer } from "@usine/github/local-secrets"
|
|
67
40
|
import { Layer } from "effect"
|
|
68
|
-
import { FetchHttpClient } from "effect/
|
|
41
|
+
import { FetchHttpClient } from "effect/http"
|
|
69
42
|
|
|
70
43
|
const instance = InstanceDirectory.make("/path/to/instance")
|
|
71
44
|
const platformLayer = Layer.merge(NodeFileSystem.layer, NodePath.layer)
|
|
@@ -79,33 +52,41 @@ export const githubLayer = GitHubApp.layer.pipe(
|
|
|
79
52
|
)
|
|
80
53
|
```
|
|
81
54
|
|
|
82
|
-
`GitHubApp.layer` imports the private key once,
|
|
55
|
+
`GitHubApp.layer` imports the private key once, at build time, so a key that WebCrypto can't import fails composition with `AppAuthenticationError`, not the first request. Another credential source provides `GitHubCredentials` with `AppCredentials` whose key went through the `RedactedPrivateKey` schema.
|
|
56
|
+
|
|
57
|
+
`installation()`, `installationToken()`, and `projectAccess()` share one lease: one installation discovery and installation-wide mint, reused until five minutes before expiry, so polling makes no App requests between renewals. `workerToken()` mints on every call, so each attempt gets its own token.
|
|
83
58
|
|
|
84
|
-
`
|
|
59
|
+
`botAccount()` returns the App's bot account, `<slug>[bot]` and its user ID, which differs from the App ID. It reads `GET /users/<slug>[bot]` with the installation-wide token once per slug. Commits use the account's ID-based noreply address, `<id>+<slug>[bot]@users.noreply.github.com`.
|
|
85
60
|
|
|
86
|
-
Call `GitHubApp.projectAccess(registry)` before scheduling
|
|
61
|
+
Call `GitHubApp.projectAccess(registry)` before scheduling. Each result is a `CoveredProject`, with the repo ID for scoped minting, or a `ProjectNotInstalledError`. A missing repo doesn't block covered ones.
|
|
87
62
|
|
|
88
|
-
Pass a `CoveredProject` to `GitHubApp.workerToken`. GitHub
|
|
63
|
+
Pass a `CoveredProject` and the attempt's loop permission level to `GitHubApp.workerToken`. If GitHub's response doesn't confirm that the token covers exactly that repo ID, it fails with `InvalidGitHubResponseError`. `WORKER_TOKEN_PERMISSIONS` lists each level's permissions: a `read` token has `contents: read`, so it can't push, and keeps `pull_requests: write` and `issues: write` for reviews and comments.
|
|
89
64
|
|
|
90
65
|
## Poll repository changes
|
|
91
66
|
|
|
92
|
-
`GitHubPollingLayer`
|
|
67
|
+
`GitHubPollingLayer` provides:
|
|
93
68
|
|
|
94
|
-
- `changes(repository, cursor)
|
|
95
|
-
- `labels(repository)
|
|
69
|
+
- `changes(repository, cursor)`: one conditional Issues REST request, returning changed issue and pull request numbers, the next ETag and watermark, and any rate-limit delay.
|
|
70
|
+
- `labels(repository)`: every page of the repo's label catalog, for board lane proposals.
|
|
71
|
+
- `baseRefHead(repository, baseRefName, known)`: the head commit of a base branch, from one conditional Git refs REST request with the ETag of `known`. A 304 returns `known` and costs no rate limit; a missing branch fails with a 404 `GitHubApiError`.
|
|
72
|
+
- `snapshot(repository, item)`: the issue or pull request payload through GraphQL, with its SHA-256 content hash. The stored payload keeps timestamps; the hash excludes timestamp fields at every level.
|
|
73
|
+
- `missingIssues(repository, numbers)`: the numbers the repo no longer has, deleted or transferred. It sends one GraphQL request per 50 numbers, with an aliased `issue(number:)` field each; an issue that returns null is missing.
|
|
96
74
|
|
|
97
|
-
Change
|
|
98
|
-
- `snapshot(repository, item)` fetches the issue or pull-request payload through GraphQL and computes its SHA-256 content hash. The stored payload retains timestamps, while the hash excludes timestamp fields at every nesting level.
|
|
75
|
+
Change detection separates `nextPollAt`, the earliest next conditional poll, from `backoffUntil`, a delay from `Retry-After` or rate-limit pressure. Self-write hints can fetch a snapshot within the poll interval while respecting backoff.
|
|
99
76
|
|
|
100
|
-
The REST cursor overlaps its watermark by five seconds. Repeated items are expected; the engine's
|
|
77
|
+
The REST cursor overlaps its watermark by five seconds. Repeated items are expected; the engine's event ledger deduplicates. Provide `GitHubPollingLayer` with `GitHubApp`, an Effect HTTP client, and `Crypto.Crypto`.
|
|
101
78
|
|
|
102
|
-
`GitHubItemWriter.updateLabels(repository, item, mutation)` adds destination lane labels and removes only
|
|
79
|
+
`GitHubItemWriter.updateLabels(repository, item, mutation)` adds the destination lane's labels and removes only labels that lanes own. It never replaces an item's whole label set or deletes label definitions, so unrelated and concurrent labels survive stale or truncated projections.
|
|
103
80
|
|
|
104
|
-
`
|
|
81
|
+
`GitHubItemHistory.appChanges(repository, item, since)` (`/item-history`) reads an issue or pull request timeline through GraphQL from `since`, and returns the factory App's own changes, oldest first: `LabelAdded`, `LabelRemoved`, `Commented`, and `Reviewed` (a submitted review with its state). Each change has GitHub's time; a label has its current name, color, and description, and comments and reviews have their URL. Other users and bots are left out. The App is matched by its slug, `Installation.appSlug` from `GitHubApp.installation()`, since GraphQL reports its writes with a `Bot` actor of that login. It reads up to ten pages of 100 items and fails with `InvalidGitHubResponseError` rather than return part of a longer timeline. An item the repo no longer has fails with `GitHubItemNotFoundError`.
|
|
105
82
|
|
|
106
|
-
|
|
83
|
+
`GitHubLabelWriter.create(repository, label)` creates a label through `POST /repos/{owner}/{repo}/labels` with the installation-wide token. Color and description are optional. The orchestrator owns reconciliation, so this service only creates.
|
|
107
84
|
|
|
108
|
-
|
|
85
|
+
The item writer, item history, and label writer each need `GitHubApp` and an Effect HTTP client.
|
|
86
|
+
|
|
87
|
+
## Real-App integration test
|
|
88
|
+
|
|
89
|
+
Run the opt-in test against a disposable issue in a repo the App covers:
|
|
109
90
|
|
|
110
91
|
```sh
|
|
111
92
|
export USINE_GITHUB_INTEGRATION_INSTANCE_DIRECTORY=/path/to/instance
|
|
@@ -115,6 +96,4 @@ export 'USINE_GITHUB_INTEGRATION_BOT_LOGIN=your-app-slug[bot]'
|
|
|
115
96
|
pnpm --filter @usine/github test:integration
|
|
116
97
|
```
|
|
117
98
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
All APIs remain 0.x and can change between minor releases.
|
|
99
|
+
It mints both token kinds, posts an issue comment with the repo token, checks the author against `<app>[bot]`, and deletes the comment at cleanup. `@usine/orchestrator` has a separate live polling test that creates and applies a repo label.
|
package/dist/access.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RepositoryName } from "@usine/core/repository";
|
|
2
2
|
import { Schema } from "effect";
|
|
3
3
|
import { RepositoryId, Installation } from "./installation.ts";
|
|
4
|
-
/**
|
|
4
|
+
/** The repository permissions required for polling, label updates, and agent-authored changes. */
|
|
5
5
|
export declare const REQUIRED_REPOSITORY_PERMISSIONS: {
|
|
6
6
|
readonly checks: "read";
|
|
7
7
|
readonly contents: "write";
|
|
@@ -10,38 +10,60 @@ export declare const REQUIRED_REPOSITORY_PERMISSIONS: {
|
|
|
10
10
|
readonly pull_requests: "write";
|
|
11
11
|
};
|
|
12
12
|
export type RequiredRepositoryPermission = keyof typeof REQUIRED_REPOSITORY_PERMISSIONS;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The repository permissions of an Attempt's token for each Loop permission level (ADR 0011).
|
|
15
|
+
* A `read` token can read code and post reviews and comments (`pull_requests` and `issues` are
|
|
16
|
+
* `write`), but can't push, merge, or update a branch, which need `contents: write`. Names and
|
|
17
|
+
* levels follow the installation token API:
|
|
18
|
+
* https://docs.github.com/en/rest/authentication/permissions-required-for-github-apps
|
|
19
|
+
*/
|
|
20
|
+
export declare const WORKER_TOKEN_PERMISSIONS: {
|
|
21
|
+
readonly write: {
|
|
22
|
+
readonly checks: "read";
|
|
23
|
+
readonly contents: "write";
|
|
24
|
+
readonly issues: "write";
|
|
25
|
+
readonly metadata: "read";
|
|
26
|
+
readonly pull_requests: "write";
|
|
27
|
+
};
|
|
28
|
+
readonly read: {
|
|
29
|
+
readonly checks: "read";
|
|
30
|
+
readonly contents: "read";
|
|
31
|
+
readonly issues: "write";
|
|
32
|
+
readonly metadata: "read";
|
|
33
|
+
readonly pull_requests: "write";
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
/** Returns the required repository permissions that `granted` doesn't satisfy, as `name:level` strings. */
|
|
14
37
|
export declare const missingPermissions: (granted: Readonly<Record<string, string>>) => ReadonlyArray<string>;
|
|
15
38
|
declare const CoveredProject_base: Schema.Class<CoveredProject, Schema.TaggedStruct<"CoveredProject", {
|
|
16
39
|
readonly installationId: Schema.brand<Schema.Int, "GitHubInstallationId">;
|
|
17
40
|
readonly repository: Schema.brand<Schema.String, "RepositoryName">;
|
|
18
41
|
readonly repositoryId: Schema.brand<Schema.Int, "GitHubRepositoryId">;
|
|
19
42
|
}>, {}>;
|
|
20
|
-
/** A registered repository covered by the
|
|
43
|
+
/** A registered repository covered by the GitHub App installation. */
|
|
21
44
|
export declare class CoveredProject extends CoveredProject_base {
|
|
22
45
|
}
|
|
23
46
|
declare const ProjectNotInstalledError_base: Schema.Class<ProjectNotInstalledError, Schema.TaggedStruct<"ProjectNotInstalledError", {
|
|
24
47
|
readonly repository: Schema.brand<Schema.String, "RepositoryName">;
|
|
25
48
|
readonly message: Schema.NonEmptyString;
|
|
26
49
|
}>, import("effect/Cause").YieldableError>;
|
|
27
|
-
/**
|
|
50
|
+
/** The error for a registry repository the installation doesn't cover. Reported per project, so covered projects still load. */
|
|
28
51
|
export declare class ProjectNotInstalledError extends ProjectNotInstalledError_base {
|
|
29
52
|
}
|
|
30
|
-
/** The installation coverage
|
|
53
|
+
/** The installation coverage of one registered repository. */
|
|
31
54
|
export declare const ProjectAccess: Schema.Union<readonly [typeof CoveredProject, typeof ProjectNotInstalledError]>;
|
|
32
55
|
export type ProjectAccess = typeof ProjectAccess.Type;
|
|
33
|
-
/** A repository reported by GitHub for an installation, identified by its numeric id and full name. */
|
|
34
56
|
export interface InstalledRepository {
|
|
35
57
|
readonly id: RepositoryId;
|
|
36
58
|
readonly name: RepositoryName;
|
|
37
59
|
}
|
|
38
|
-
/**
|
|
60
|
+
/** Returns one `ProjectAccess` per registry repository by checking it against the installation's repositories. */
|
|
39
61
|
export declare const deriveProjectAccess: (installation: Installation, registry: ReadonlyArray<RepositoryName>, installed: ReadonlyArray<InstalledRepository>) => ReadonlyArray<ProjectAccess>;
|
|
40
62
|
declare const AppPermissionError_base: Schema.Class<AppPermissionError, Schema.TaggedStruct<"AppPermissionError", {
|
|
41
63
|
readonly missing: Schema.$Array<Schema.NonEmptyString>;
|
|
42
64
|
readonly message: Schema.NonEmptyString;
|
|
43
65
|
}>, import("effect/Cause").YieldableError>;
|
|
44
|
-
/** The installation lacks required repository
|
|
66
|
+
/** The error when the installation lacks required repository permissions; `missing` uses `name:level` form. */
|
|
45
67
|
export declare class AppPermissionError extends AppPermissionError_base {
|
|
46
68
|
}
|
|
47
69
|
export {};
|
package/dist/access.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAkB,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE/E,kGAAkG;AAClG,eAAO,MAAM,+BAA+B;aAC1C,MAAM,EAAE,MAAM;aACd,QAAQ,EAAE,OAAO;aACjB,MAAM,EAAE,OAAO;aACf,QAAQ,EAAE,MAAM;aAChB,aAAa,EAAE,OAAO;CACd,CAAC;AAEX,MAAM,MAAM,4BAA4B,GAAG,MAAM,OAAO,+BAA+B,CAAC;AAExF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB;;iBAEjC,MAAM,EAAE,MAAM;iBACd,QAAQ,EAAE,OAAO;iBACjB,MAAM,EAAE,OAAO;iBACf,QAAQ,EAAE,MAAM;iBAChB,aAAa,EAAE,OAAO;;;iBAGtB,MAAM,EAAE,MAAM;iBACd,QAAQ,EAAE,MAAM;iBAChB,MAAM,EAAE,OAAO;iBACf,QAAQ,EAAE,MAAM;iBAChB,aAAa,EAAE,OAAO;;CAKzB,CAAC;AAKF,2GAA2G;AAC3G,eAAO,MAAM,kBAAkB,YACpB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KACxC,aAAa,CAAC,MAAM,CAGpB,CAAC;;;;;;AAEJ,sEAAsE;AACtE,qBAAa,cAAe,SAAQ,mBAMlC;CAAG;;;;;AAEL,gIAAgI;AAChI,qBAAa,wBAAyB,SAAQ,6BAK5C;CAAG;AAEL,8DAA8D;AAC9D,eAAO,MAAM,aAAa,iFAA2D,CAAC;AACtF,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC;AAEtD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;CAC/B;AAED,kHAAkH;AAClH,eAAO,MAAM,mBAAmB,iBAChB,YAAY,YAChB,aAAa,CAAC,cAAc,CAAC,aAC5B,aAAa,CAAC,mBAAmB,CAAC,KAC5C,aAAa,CAAC,aAAa,CAe7B,CAAC;;;;;AAEF,+GAA+G;AAC/G,qBAAa,kBAAmB,SAAQ,uBAKtC;CAAG"}
|
package/dist/access.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RepositoryName } from "@usine/core/repository";
|
|
2
2
|
import { Schema } from "effect";
|
|
3
3
|
import { InstallationId, RepositoryId, Installation } from "./installation.js";
|
|
4
|
-
/**
|
|
4
|
+
/** The repository permissions required for polling, label updates, and agent-authored changes. */
|
|
5
5
|
export const REQUIRED_REPOSITORY_PERMISSIONS = {
|
|
6
6
|
checks: "read",
|
|
7
7
|
contents: "write",
|
|
@@ -9,25 +9,48 @@ export const REQUIRED_REPOSITORY_PERMISSIONS = {
|
|
|
9
9
|
metadata: "read",
|
|
10
10
|
pull_requests: "write",
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* The repository permissions of an Attempt's token for each Loop permission level (ADR 0011).
|
|
14
|
+
* A `read` token can read code and post reviews and comments (`pull_requests` and `issues` are
|
|
15
|
+
* `write`), but can't push, merge, or update a branch, which need `contents: write`. Names and
|
|
16
|
+
* levels follow the installation token API:
|
|
17
|
+
* https://docs.github.com/en/rest/authentication/permissions-required-for-github-apps
|
|
18
|
+
*/
|
|
19
|
+
export const WORKER_TOKEN_PERMISSIONS = {
|
|
20
|
+
write: {
|
|
21
|
+
checks: "read",
|
|
22
|
+
contents: "write",
|
|
23
|
+
issues: "write",
|
|
24
|
+
metadata: "read",
|
|
25
|
+
pull_requests: "write",
|
|
26
|
+
},
|
|
27
|
+
read: {
|
|
28
|
+
checks: "read",
|
|
29
|
+
contents: "read",
|
|
30
|
+
issues: "write",
|
|
31
|
+
metadata: "read",
|
|
32
|
+
pull_requests: "write",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
12
35
|
const grants = (actual, required) => actual === required || (required === "read" && actual === "write");
|
|
13
|
-
/**
|
|
36
|
+
/** Returns the required repository permissions that `granted` doesn't satisfy, as `name:level` strings. */
|
|
14
37
|
export const missingPermissions = (granted) => Object.entries(REQUIRED_REPOSITORY_PERMISSIONS).flatMap(([permission, required]) => grants(granted[permission], required) ? [] : [`${permission}:${required}`]);
|
|
15
|
-
/** A registered repository covered by the
|
|
38
|
+
/** A registered repository covered by the GitHub App installation. */
|
|
16
39
|
export class CoveredProject extends Schema.TaggedClass("@usine/github/ProjectAccess/CoveredProject")("CoveredProject", {
|
|
17
40
|
installationId: InstallationId,
|
|
18
41
|
repository: RepositoryName,
|
|
19
42
|
repositoryId: RepositoryId,
|
|
20
43
|
}) {
|
|
21
44
|
}
|
|
22
|
-
/**
|
|
45
|
+
/** The error for a registry repository the installation doesn't cover. Reported per project, so covered projects still load. */
|
|
23
46
|
export class ProjectNotInstalledError extends Schema.TaggedError("@usine/github/ProjectNotInstalledError")("ProjectNotInstalledError", {
|
|
24
47
|
repository: RepositoryName,
|
|
25
48
|
message: Schema.NonEmptyString,
|
|
26
49
|
}) {
|
|
27
50
|
}
|
|
28
|
-
/** The installation coverage
|
|
51
|
+
/** The installation coverage of one registered repository. */
|
|
29
52
|
export const ProjectAccess = Schema.Union([CoveredProject, ProjectNotInstalledError]);
|
|
30
|
-
/**
|
|
53
|
+
/** Returns one `ProjectAccess` per registry repository by checking it against the installation's repositories. */
|
|
31
54
|
export const deriveProjectAccess = (installation, registry, installed) => {
|
|
32
55
|
const byName = new Map(installed.map((repo) => [repo.name.toLowerCase(), repo]));
|
|
33
56
|
return registry.map((repository) => {
|
|
@@ -35,7 +58,7 @@ export const deriveProjectAccess = (installation, registry, installed) => {
|
|
|
35
58
|
return match === undefined
|
|
36
59
|
? new ProjectNotInstalledError({
|
|
37
60
|
repository,
|
|
38
|
-
message: `The GitHub App installation
|
|
61
|
+
message: `The GitHub App installation has no access to "${repository}"`,
|
|
39
62
|
})
|
|
40
63
|
: new CoveredProject({
|
|
41
64
|
installationId: installation.id,
|
|
@@ -44,7 +67,7 @@ export const deriveProjectAccess = (installation, registry, installed) => {
|
|
|
44
67
|
});
|
|
45
68
|
});
|
|
46
69
|
};
|
|
47
|
-
/** The installation lacks required repository
|
|
70
|
+
/** The error when the installation lacks required repository permissions; `missing` uses `name:level` form. */
|
|
48
71
|
export class AppPermissionError extends Schema.TaggedError("@usine/github/AppPermissionError")("AppPermissionError", {
|
|
49
72
|
missing: Schema.Array(Schema.NonEmptyString).check(Schema.isNonEmpty()),
|
|
50
73
|
message: Schema.NonEmptyString,
|
package/dist/access.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE/E,kGAAkG;AAClG,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,OAAO;IACjB,MAAM,EAAE,OAAO;IACf,QAAQ,EAAE,MAAM;IAChB,aAAa,EAAE,OAAO;CACd,CAAC;AAIX;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,KAAK,EAAE;QACL,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,OAAO;QACf,QAAQ,EAAE,MAAM;QAChB,aAAa,EAAE,OAAO;KACvB;IACD,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,OAAO;QACf,QAAQ,EAAE,MAAM;QAChB,aAAa,EAAE,OAAO;KACvB;CAIF,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,MAA0B,EAAE,QAA0B,EAAW,EAAE,CACjF,MAAM,KAAK,QAAQ,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO,CAAC,CAAC;AAErE,2GAA2G;AAC3G,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,OAAyC,EAClB,EAAE,CACzB,MAAM,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,CACjF,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,IAAI,QAAQ,EAAE,CAAC,CAC3E,CAAC;AAEJ,sEAAsE;AACtE,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,CACpD,4CAA4C,CAC7C,CAAC,gBAAgB,EAAE;IAClB,cAAc,EAAE,cAAc;IAC9B,UAAU,EAAE,cAAc;IAC1B,YAAY,EAAE,YAAY;CAC3B,CAAC;CAAG;AAEL,gIAAgI;AAChI,MAAM,OAAO,wBAAyB,SAAQ,MAAM,CAAC,WAAW,CAC9D,wCAAwC,CACzC,CAAC,0BAA0B,EAAE;IAC5B,UAAU,EAAE,cAAc;IAC1B,OAAO,EAAE,MAAM,CAAC,cAAc;CAC/B,CAAC;CAAG;AAEL,8DAA8D;AAC9D,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,EAAE,wBAAwB,CAAC,CAAC,CAAC;AAQtF,kHAAkH;AAClH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,YAA0B,EAC1B,QAAuC,EACvC,SAA6C,EACf,EAAE;IAChC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACjF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QACnD,OAAO,KAAK,KAAK,SAAS;YACxB,CAAC,CAAC,IAAI,wBAAwB,CAAC;gBAC3B,UAAU;gBACV,OAAO,EAAE,iDAAiD,UAAU,GAAG;aACxE,CAAC;YACJ,CAAC,CAAC,IAAI,cAAc,CAAC;gBACjB,cAAc,EAAE,YAAY,CAAC,EAAE;gBAC/B,UAAU;gBACV,YAAY,EAAE,KAAK,CAAC,EAAE;aACvB,CAAC,CAAC;IACT,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,+GAA+G;AAC/G,MAAM,OAAO,kBAAmB,SAAQ,MAAM,CAAC,WAAW,CACxD,kCAAkC,CACnC,CAAC,oBAAoB,EAAE;IACtB,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IACvE,OAAO,EAAE,MAAM,CAAC,cAAc;CAC/B,CAAC;CAAG"}
|
package/dist/credentials.d.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { Context, Schema } from "effect";
|
|
2
|
-
/** The numeric GitHub App
|
|
2
|
+
/** The numeric GitHub App ID included in signed App JWTs. */
|
|
3
3
|
export declare const AppId: Schema.brand<Schema.Int, "GitHubAppId">;
|
|
4
4
|
export type AppId = typeof AppId.Type;
|
|
5
|
-
/** A PEM-encoded RSA private key
|
|
5
|
+
/** A PEM-encoded RSA private key for the GitHub App. */
|
|
6
6
|
export declare const PrivateKey: Schema.brand<Schema.NonEmptyString, "GitHubAppPrivateKey">;
|
|
7
7
|
export type PrivateKey = typeof PrivateKey.Type;
|
|
8
|
-
/**
|
|
8
|
+
/** The App private key, redacted in diagnostics and not encodable. */
|
|
9
9
|
export declare const RedactedPrivateKey: Schema.RedactedFromValue<Schema.brand<Schema.NonEmptyString, "GitHubAppPrivateKey">>;
|
|
10
10
|
declare const AppCredentials_base: Schema.Class<AppCredentials, Schema.Struct<{
|
|
11
11
|
readonly appId: Schema.brand<Schema.Int, "GitHubAppId">;
|
|
12
12
|
readonly privateKey: Schema.RedactedFromValue<Schema.brand<Schema.NonEmptyString, "GitHubAppPrivateKey">>;
|
|
13
13
|
}>, {}>;
|
|
14
|
-
/** The App credentials the orchestrator
|
|
14
|
+
/** The App credentials, kept by the orchestrator and never passed to a Worker. */
|
|
15
15
|
export declare class AppCredentials extends AppCredentials_base {
|
|
16
16
|
}
|
|
17
17
|
declare const GitHubCredentials_base: Context.ServiceClass<GitHubCredentials, "@usine/github/GitHubCredentials", AppCredentials>;
|
|
18
|
-
/**
|
|
18
|
+
/** The service that provides `AppCredentials`. */
|
|
19
19
|
export declare class GitHubCredentials extends GitHubCredentials_base {
|
|
20
20
|
}
|
|
21
21
|
declare const AppAuthenticationError_base: Schema.Class<AppAuthenticationError, Schema.TaggedStruct<"AppAuthenticationError", {
|
|
22
22
|
readonly message: Schema.NonEmptyString;
|
|
23
23
|
}>, import("effect/Cause").YieldableError>;
|
|
24
|
-
/** The App private key
|
|
24
|
+
/** The error when the App private key can't sign, raised once when the App layer is built. */
|
|
25
25
|
export declare class AppAuthenticationError extends AppAuthenticationError_base {
|
|
26
26
|
}
|
|
27
27
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,6DAA6D;AAC7D,eAAO,MAAM,KAAK,yCAEjB,CAAC;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAEtC,wDAAwD;AACxD,eAAO,MAAM,UAAU,4DAKoB,CAAC;AAC5C,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAEhD,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,sFAAiE,CAAC;;;;;AAEjG,kFAAkF;AAClF,qBAAa,cAAe,SAAQ,mBAGlC;CAAG;;AAEL,kDAAkD;AAClD,qBAAa,iBAAkB,SAAQ,sBAEtC;CAAG;;;;AAEJ,8FAA8F;AAC9F,qBAAa,sBAAuB,SAAQ,2BAI1C;CAAG"}
|
package/dist/credentials.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { Context, Schema } from "effect";
|
|
2
|
-
/** The numeric GitHub App
|
|
2
|
+
/** The numeric GitHub App ID included in signed App JWTs. */
|
|
3
3
|
export const AppId = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)).pipe(Schema.brand("GitHubAppId"));
|
|
4
|
-
/** A PEM-encoded RSA private key
|
|
4
|
+
/** A PEM-encoded RSA private key for the GitHub App. */
|
|
5
5
|
export const PrivateKey = Schema.NonEmptyString.check(Schema.isPattern(/^-----BEGIN (?:RSA )?PRIVATE KEY-----[\s\S]+-----END (?:RSA )?PRIVATE KEY-----\s*$/u, { message: "Expected a PEM-encoded RSA private key" })).pipe(Schema.brand("GitHubAppPrivateKey"));
|
|
6
|
-
/**
|
|
6
|
+
/** The App private key, redacted in diagnostics and not encodable. */
|
|
7
7
|
export const RedactedPrivateKey = Schema.RedactedFromValue(PrivateKey, { disallowEncode: true });
|
|
8
|
-
/** The App credentials the orchestrator
|
|
8
|
+
/** The App credentials, kept by the orchestrator and never passed to a Worker. */
|
|
9
9
|
export class AppCredentials extends Schema.Class("@usine/github/AppCredentials")({
|
|
10
10
|
appId: AppId,
|
|
11
11
|
privateKey: RedactedPrivateKey,
|
|
12
12
|
}) {
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
14
|
+
/** The service that provides `AppCredentials`. */
|
|
15
15
|
export class GitHubCredentials extends Context.Service()("@usine/github/GitHubCredentials") {
|
|
16
16
|
}
|
|
17
|
-
/** The App private key
|
|
17
|
+
/** The error when the App private key can't sign, raised once when the App layer is built. */
|
|
18
18
|
export class AppAuthenticationError extends Schema.TaggedError("@usine/github/AppAuthenticationError")("AppAuthenticationError", {
|
|
19
19
|
message: Schema.NonEmptyString,
|
|
20
20
|
}) {
|
package/dist/credentials.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC1E,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAC5B,CAAC;AAGF,wDAAwD;AACxD,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CACnD,MAAM,CAAC,SAAS,CACd,qFAAqF,EACrF,EAAE,OAAO,EAAE,wCAAwC,EAAE,CACtD,CACF,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;AAG5C,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,iBAAiB,CAAC,UAAU,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;AAEjG,kFAAkF;AAClF,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,KAAK,CAAiB,8BAA8B,CAAC,CAAC;IAC/F,KAAK,EAAE,KAAK;IACZ,UAAU,EAAE,kBAAkB;CAC/B,CAAC;CAAG;AAEL,kDAAkD;AAClD,MAAM,OAAO,iBAAkB,SAAQ,OAAO,CAAC,OAAO,EAAqC,CACzF,iCAAiC,CAClC;CAAG;AAEJ,8FAA8F;AAC9F,MAAM,OAAO,sBAAuB,SAAQ,MAAM,CAAC,WAAW,CAC5D,sCAAsC,CACvC,CAAC,wBAAwB,EAAE;IAC1B,OAAO,EAAE,MAAM,CAAC,cAAc;CAC/B,CAAC;CAAG"}
|
package/dist/error.d.ts
CHANGED
|
@@ -1,36 +1,54 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { InstallationDiscoveryError } from "./installation.ts";
|
|
3
3
|
import { AppPermissionError } from "./access.ts";
|
|
4
|
-
/** The GitHub request that failed,
|
|
5
|
-
export declare const GitHubOperation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-item-labels", "fetch-issue-snapshot", "fetch-pull-request-snapshot", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "update-item-labels"]>;
|
|
4
|
+
/** The GitHub request that failed, used by API, rate-limit, and response errors. */
|
|
5
|
+
export declare const GitHubOperation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-base-ref-head", "fetch-item-labels", "fetch-issue-snapshot", "fetch-item-timeline", "fetch-pull-request-snapshot", "find-missing-issues", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "read-bot-account", "update-item-labels"]>;
|
|
6
6
|
export type GitHubOperation = typeof GitHubOperation.Type;
|
|
7
7
|
declare const GitHubApiError_base: Schema.Class<GitHubApiError, Schema.TaggedStruct<"GitHubApiError", {
|
|
8
|
-
readonly operation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-item-labels", "fetch-issue-snapshot", "fetch-pull-request-snapshot", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "update-item-labels"]>;
|
|
8
|
+
readonly operation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-base-ref-head", "fetch-item-labels", "fetch-issue-snapshot", "fetch-item-timeline", "fetch-pull-request-snapshot", "find-missing-issues", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "read-bot-account", "update-item-labels"]>;
|
|
9
9
|
readonly status: Schema.Option<Schema.Int>;
|
|
10
10
|
readonly message: Schema.NonEmptyString;
|
|
11
11
|
}>, import("effect/Cause").YieldableError>;
|
|
12
|
-
/** A failed GitHub request, with
|
|
12
|
+
/** A failed GitHub request, with the HTTP status if a response was received. */
|
|
13
13
|
export declare class GitHubApiError extends GitHubApiError_base {
|
|
14
14
|
}
|
|
15
15
|
declare const GitHubRateLimitError_base: Schema.Class<GitHubRateLimitError, Schema.TaggedStruct<"GitHubRateLimitError", {
|
|
16
|
-
readonly operation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-item-labels", "fetch-issue-snapshot", "fetch-pull-request-snapshot", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "update-item-labels"]>;
|
|
16
|
+
readonly operation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-base-ref-head", "fetch-item-labels", "fetch-issue-snapshot", "fetch-item-timeline", "fetch-pull-request-snapshot", "find-missing-issues", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "read-bot-account", "update-item-labels"]>;
|
|
17
17
|
readonly retryAt: Schema.DateTimeUtc;
|
|
18
18
|
readonly message: Schema.NonEmptyString;
|
|
19
19
|
}>, import("effect/Cause").YieldableError>;
|
|
20
|
-
/** GitHub
|
|
20
|
+
/** The error when GitHub defers the request until `retryAt`. */
|
|
21
21
|
export declare class GitHubRateLimitError extends GitHubRateLimitError_base {
|
|
22
22
|
}
|
|
23
23
|
declare const InvalidGitHubResponseError_base: Schema.Class<InvalidGitHubResponseError, Schema.TaggedStruct<"InvalidGitHubResponseError", {
|
|
24
|
-
readonly operation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-item-labels", "fetch-issue-snapshot", "fetch-pull-request-snapshot", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "update-item-labels"]>;
|
|
24
|
+
readonly operation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-base-ref-head", "fetch-item-labels", "fetch-issue-snapshot", "fetch-item-timeline", "fetch-pull-request-snapshot", "find-missing-issues", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "read-bot-account", "update-item-labels"]>;
|
|
25
25
|
readonly message: Schema.NonEmptyString;
|
|
26
|
-
/**
|
|
26
|
+
/** The decode path, missing field, or GraphQL errors. */
|
|
27
27
|
readonly detail: Schema.NonEmptyString;
|
|
28
28
|
}>, import("effect/Cause").YieldableError>;
|
|
29
|
-
/**
|
|
29
|
+
/** The error when a GitHub response can't be decoded or lacks required content. */
|
|
30
30
|
export declare class InvalidGitHubResponseError extends InvalidGitHubResponseError_base {
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
declare const GitHubItemNotFoundError_base: Schema.Class<GitHubItemNotFoundError, Schema.TaggedStruct<"GitHubItemNotFoundError", {
|
|
33
|
+
readonly operation: Schema.Literals<readonly ["create-repository-label", "discover-installation", "fetch-base-ref-head", "fetch-item-labels", "fetch-issue-snapshot", "fetch-item-timeline", "fetch-pull-request-snapshot", "find-missing-issues", "list-installation-repositories", "list-repository-labels", "mint-installation-token", "mint-worker-token", "poll-repository-changes", "read-bot-account", "update-item-labels"]>;
|
|
34
|
+
readonly item: Schema.Int;
|
|
35
|
+
readonly message: Schema.NonEmptyString;
|
|
36
|
+
}>, import("effect/Cause").YieldableError>;
|
|
37
|
+
/**
|
|
38
|
+
* The error when the repository has no Item with this number because it was deleted,
|
|
39
|
+
* transferred, or is no longer readable by the App. The repository itself responded.
|
|
40
|
+
*/
|
|
41
|
+
export declare class GitHubItemNotFoundError extends GitHubItemNotFoundError_base {
|
|
42
|
+
}
|
|
43
|
+
/** The errors from installation discovery or an authenticated GitHub request. */
|
|
33
44
|
export declare const GitHubAppError: Schema.Union<readonly [typeof InstallationDiscoveryError, typeof AppPermissionError, typeof GitHubApiError, typeof GitHubRateLimitError, typeof InvalidGitHubResponseError]>;
|
|
34
45
|
export type GitHubAppError = typeof GitHubAppError.Type;
|
|
46
|
+
/**
|
|
47
|
+
* Returns whether GitHub gave a definite response that a retry won't change: no usable
|
|
48
|
+
* installation, a missing permission, or a 4xx status other than 408 and 429 (for example, 422
|
|
49
|
+
* for an ungranted permission or 403 for a suspended installation). Send failures, server
|
|
50
|
+
* errors, rate limits, and responses that break GitHub's API contract aren't refusals.
|
|
51
|
+
*/
|
|
52
|
+
export declare const isRefusal: (error: GitHubAppError) => boolean;
|
|
35
53
|
export {};
|
|
36
54
|
//# sourceMappingURL=error.d.ts.map
|
package/dist/error.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,oFAAoF;AACpF,eAAO,MAAM,eAAe,kZAgB1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAC;;;;;;AAE1D,gFAAgF;AAChF,qBAAa,cAAe,SAAQ,mBAMlC;CAAG;;;;;;AAEL,gEAAgE;AAChE,qBAAa,oBAAqB,SAAQ,yBAMxC;CAAG;;;;IAQH,yDAAyD;;;AAN3D,mFAAmF;AACnF,qBAAa,0BAA2B,SAAQ,+BAO9C;CAAG;;;;;;AAEL;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,4BAM3C;CAAG;AAEL,iFAAiF;AACjF,eAAO,MAAM,cAAc,8KAMzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAC;AAExD;;;;;GAKG;AACH,eAAO,MAAM,SAAS,UAAW,cAAc,KAAG,OAQ9C,CAAC"}
|
package/dist/error.js
CHANGED
|
@@ -1,43 +1,57 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
1
|
+
import { Match, Option, Schema } from "effect";
|
|
2
2
|
import { InstallationDiscoveryError } from "./installation.js";
|
|
3
3
|
import { AppPermissionError } from "./access.js";
|
|
4
|
-
/** The GitHub request that failed,
|
|
4
|
+
/** The GitHub request that failed, used by API, rate-limit, and response errors. */
|
|
5
5
|
export const GitHubOperation = Schema.Literals([
|
|
6
6
|
"create-repository-label",
|
|
7
7
|
"discover-installation",
|
|
8
|
+
"fetch-base-ref-head",
|
|
8
9
|
"fetch-item-labels",
|
|
9
10
|
"fetch-issue-snapshot",
|
|
11
|
+
"fetch-item-timeline",
|
|
10
12
|
"fetch-pull-request-snapshot",
|
|
13
|
+
"find-missing-issues",
|
|
11
14
|
"list-installation-repositories",
|
|
12
15
|
"list-repository-labels",
|
|
13
16
|
"mint-installation-token",
|
|
14
17
|
"mint-worker-token",
|
|
15
18
|
"poll-repository-changes",
|
|
19
|
+
"read-bot-account",
|
|
16
20
|
"update-item-labels",
|
|
17
21
|
]);
|
|
18
|
-
/** A failed GitHub request, with
|
|
22
|
+
/** A failed GitHub request, with the HTTP status if a response was received. */
|
|
19
23
|
export class GitHubApiError extends Schema.TaggedError("@usine/github/GitHubApiError")("GitHubApiError", {
|
|
20
24
|
operation: GitHubOperation,
|
|
21
25
|
status: Schema.Option(Schema.Int),
|
|
22
26
|
message: Schema.NonEmptyString,
|
|
23
27
|
}) {
|
|
24
28
|
}
|
|
25
|
-
/** GitHub
|
|
29
|
+
/** The error when GitHub defers the request until `retryAt`. */
|
|
26
30
|
export class GitHubRateLimitError extends Schema.TaggedError("@usine/github/GitHubRateLimitError")("GitHubRateLimitError", {
|
|
27
31
|
operation: GitHubOperation,
|
|
28
32
|
retryAt: Schema.DateTimeUtc,
|
|
29
33
|
message: Schema.NonEmptyString,
|
|
30
34
|
}) {
|
|
31
35
|
}
|
|
32
|
-
/**
|
|
36
|
+
/** The error when a GitHub response can't be decoded or lacks required content. */
|
|
33
37
|
export class InvalidGitHubResponseError extends Schema.TaggedError("@usine/github/InvalidGitHubResponseError")("InvalidGitHubResponseError", {
|
|
34
38
|
operation: GitHubOperation,
|
|
35
39
|
message: Schema.NonEmptyString,
|
|
36
|
-
/**
|
|
40
|
+
/** The decode path, missing field, or GraphQL errors. */
|
|
37
41
|
detail: Schema.NonEmptyString,
|
|
38
42
|
}) {
|
|
39
43
|
}
|
|
40
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* The error when the repository has no Item with this number because it was deleted,
|
|
46
|
+
* transferred, or is no longer readable by the App. The repository itself responded.
|
|
47
|
+
*/
|
|
48
|
+
export class GitHubItemNotFoundError extends Schema.TaggedError("@usine/github/GitHubItemNotFoundError")("GitHubItemNotFoundError", {
|
|
49
|
+
operation: GitHubOperation,
|
|
50
|
+
item: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)),
|
|
51
|
+
message: Schema.NonEmptyString,
|
|
52
|
+
}) {
|
|
53
|
+
}
|
|
54
|
+
/** The errors from installation discovery or an authenticated GitHub request. */
|
|
41
55
|
export const GitHubAppError = Schema.Union([
|
|
42
56
|
InstallationDiscoveryError,
|
|
43
57
|
AppPermissionError,
|
|
@@ -45,4 +59,17 @@ export const GitHubAppError = Schema.Union([
|
|
|
45
59
|
GitHubRateLimitError,
|
|
46
60
|
InvalidGitHubResponseError,
|
|
47
61
|
]);
|
|
62
|
+
/**
|
|
63
|
+
* Returns whether GitHub gave a definite response that a retry won't change: no usable
|
|
64
|
+
* installation, a missing permission, or a 4xx status other than 408 and 429 (for example, 422
|
|
65
|
+
* for an ungranted permission or 403 for a suspended installation). Send failures, server
|
|
66
|
+
* errors, rate limits, and responses that break GitHub's API contract aren't refusals.
|
|
67
|
+
*/
|
|
68
|
+
export const isRefusal = (error) => Match.valueTags(error, {
|
|
69
|
+
InstallationDiscoveryError: () => true,
|
|
70
|
+
AppPermissionError: () => true,
|
|
71
|
+
GitHubApiError: ({ status }) => Option.exists(status, (code) => code >= 400 && code < 500 && code !== 408 && code !== 429),
|
|
72
|
+
GitHubRateLimitError: () => false,
|
|
73
|
+
InvalidGitHubResponseError: () => false,
|
|
74
|
+
});
|
|
48
75
|
//# sourceMappingURL=error.js.map
|
package/dist/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,oFAAoF;AACpF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC7C,yBAAyB;IACzB,uBAAuB;IACvB,qBAAqB;IACrB,mBAAmB;IACnB,sBAAsB;IACtB,qBAAqB;IACrB,6BAA6B;IAC7B,qBAAqB;IACrB,gCAAgC;IAChC,wBAAwB;IACxB,yBAAyB;IACzB,mBAAmB;IACnB,yBAAyB;IACzB,kBAAkB;IAClB,oBAAoB;CACrB,CAAC,CAAC;AAGH,gFAAgF;AAChF,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,CACpD,8BAA8B,CAC/B,CAAC,gBAAgB,EAAE;IAClB,SAAS,EAAE,eAAe;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC,cAAc;CAC/B,CAAC;CAAG;AAEL,gEAAgE;AAChE,MAAM,OAAO,oBAAqB,SAAQ,MAAM,CAAC,WAAW,CAC1D,oCAAoC,CACrC,CAAC,sBAAsB,EAAE;IACxB,SAAS,EAAE,eAAe;IAC1B,OAAO,EAAE,MAAM,CAAC,WAAW;IAC3B,OAAO,EAAE,MAAM,CAAC,cAAc;CAC/B,CAAC;CAAG;AAEL,mFAAmF;AACnF,MAAM,OAAO,0BAA2B,SAAQ,MAAM,CAAC,WAAW,CAChE,0CAA0C,CAC3C,CAAC,4BAA4B,EAAE;IAC9B,SAAS,EAAE,eAAe;IAC1B,OAAO,EAAE,MAAM,CAAC,cAAc;IAC9B,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC,cAAc;CAC9B,CAAC;CAAG;AAEL;;;GAGG;AACH,MAAM,OAAO,uBAAwB,SAAQ,MAAM,CAAC,WAAW,CAC7D,uCAAuC,CACxC,CAAC,yBAAyB,EAAE;IAC3B,SAAS,EAAE,eAAe;IAC1B,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,CAAC,cAAc;CAC/B,CAAC;CAAG;AAEL,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC;IACzC,0BAA0B;IAC1B,kBAAkB;IAClB,cAAc;IACd,oBAAoB;IACpB,0BAA0B;CAC3B,CAAC,CAAC;AAGH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAqB,EAAW,EAAE,CAC1D,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE;IACrB,0BAA0B,EAAE,GAAG,EAAE,CAAC,IAAI;IACtC,kBAAkB,EAAE,GAAG,EAAE,CAAC,IAAI;IAC9B,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAC7B,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC;IAC5F,oBAAoB,EAAE,GAAG,EAAE,CAAC,KAAK;IACjC,0BAA0B,EAAE,GAAG,EAAE,CAAC,KAAK;CACxC,CAAC,CAAC"}
|