@shivaedev/github 0.8.6 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +142 -8
- package/dist/GitHub.d.ts +360 -18
- package/dist/GitHub.d.ts.map +1 -1
- package/dist/GitHub.js +19 -6
- package/dist/GitHub.js.map +1 -1
- package/dist/GitHubToken.d.ts +2 -2
- package/dist/GitHubToken.d.ts.map +1 -1
- package/dist/GitHubToken.js +1 -1
- package/dist/GitHubToken.js.map +1 -1
- package/dist/adapters/resolveApiUrl.d.ts +2 -0
- package/dist/adapters/resolveApiUrl.d.ts.map +1 -0
- package/dist/adapters/resolveApiUrl.js +6 -0
- package/dist/adapters/resolveApiUrl.js.map +1 -0
- package/dist/adapters/zipEntries.d.ts +17 -0
- package/dist/adapters/zipEntries.d.ts.map +1 -0
- package/dist/adapters/zipEntries.js +57 -0
- package/dist/adapters/zipEntries.js.map +1 -0
- package/dist/appTokens.d.ts +6 -0
- package/dist/appTokens.d.ts.map +1 -0
- package/dist/appTokens.js +65 -0
- package/dist/appTokens.js.map +1 -0
- package/dist/artifactErrors.d.ts +34 -0
- package/dist/artifactErrors.d.ts.map +1 -0
- package/dist/artifactErrors.js +27 -0
- package/dist/artifactErrors.js.map +1 -0
- package/dist/artifactExtraction.d.ts +12 -0
- package/dist/artifactExtraction.d.ts.map +1 -0
- package/dist/artifactExtraction.js +40 -0
- package/dist/artifactExtraction.js.map +1 -0
- package/dist/artifactPaths.d.ts +4 -0
- package/dist/artifactPaths.d.ts.map +1 -0
- package/dist/artifactPaths.js +35 -0
- package/dist/artifactPaths.js.map +1 -0
- package/dist/artifacts.d.ts +13 -8
- package/dist/artifacts.d.ts.map +1 -1
- package/dist/artifacts.js +15 -8
- package/dist/artifacts.js.map +1 -1
- package/dist/authenticatedRequest.d.ts +1 -1
- package/dist/authenticatedRequest.d.ts.map +1 -1
- package/dist/authenticatedRequest.js +3 -2
- package/dist/authenticatedRequest.js.map +1 -1
- package/dist/cache.d.ts +2 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +3 -0
- package/dist/cache.js.map +1 -1
- package/dist/checkRuns.d.ts +116 -11
- package/dist/checkRuns.d.ts.map +1 -1
- package/dist/checkRuns.js +41 -18
- package/dist/checkRuns.js.map +1 -1
- package/dist/gitSchemas.d.ts +195 -0
- package/dist/gitSchemas.d.ts.map +1 -0
- package/dist/gitSchemas.js +45 -0
- package/dist/gitSchemas.js.map +1 -0
- package/dist/githubApp.d.ts +2 -4
- package/dist/githubApp.d.ts.map +1 -1
- package/dist/githubApp.js +5 -55
- package/dist/githubApp.js.map +1 -1
- package/dist/installationClients.d.ts +461 -0
- package/dist/installationClients.d.ts.map +1 -0
- package/dist/installationClients.js +37 -0
- package/dist/installationClients.js.map +1 -0
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +4 -0
- package/dist/json.js.map +1 -1
- package/dist/pagedLists.d.ts +15 -0
- package/dist/pagedLists.d.ts.map +1 -0
- package/dist/pagedLists.js +16 -0
- package/dist/pagedLists.js.map +1 -0
- package/dist/repositoryReads.d.ts +261 -0
- package/dist/repositoryReads.d.ts.map +1 -0
- package/dist/repositoryReads.js +30 -0
- package/dist/repositoryReads.js.map +1 -0
- package/dist/repositorySchemas.d.ts +73 -0
- package/dist/repositorySchemas.d.ts.map +1 -0
- package/dist/repositorySchemas.js +38 -0
- package/dist/repositorySchemas.js.map +1 -0
- package/dist/representations.d.ts +4 -4
- package/dist/representations.d.ts.map +1 -1
- package/dist/representations.js +3 -2
- package/dist/representations.js.map +1 -1
- package/dist/requests.d.ts +2 -2
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +11 -9
- package/dist/requests.js.map +1 -1
- package/dist/status.d.ts +1 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/stickyComments.d.ts +1 -1
- package/dist/viewerRepositoryAccess.d.ts +46 -0
- package/dist/viewerRepositoryAccess.d.ts.map +1 -0
- package/dist/viewerRepositoryAccess.js +54 -0
- package/dist/viewerRepositoryAccess.js.map +1 -0
- package/package.json +14 -7
- package/src/GitHub.ts +32 -11
- package/src/GitHubToken.ts +4 -2
- package/src/adapters/resolveApiUrl.ts +5 -0
- package/src/adapters/zipEntries.ts +69 -0
- package/src/appTokens.ts +93 -0
- package/src/artifactErrors.ts +26 -0
- package/src/artifactExtraction.ts +62 -0
- package/src/artifactPaths.ts +36 -0
- package/src/artifacts.ts +36 -39
- package/src/authenticatedRequest.ts +3 -1
- package/src/cache.ts +5 -0
- package/src/checkRuns.ts +58 -18
- package/src/gitSchemas.ts +52 -0
- package/src/githubApp.ts +6 -80
- package/src/installationClients.ts +50 -0
- package/src/json.ts +4 -0
- package/src/pagedLists.ts +40 -0
- package/src/repositoryReads.ts +61 -0
- package/src/repositorySchemas.ts +43 -0
- package/src/representations.ts +5 -4
- package/src/requests.ts +11 -9
- package/src/viewerRepositoryAccess.ts +66 -0
package/src/requests.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import {
|
|
2
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
3
3
|
import { readBoundedBody } from "@shivaedev/http/body.ts";
|
|
4
4
|
import { ResponseBodyTooLarge } from "@shivaedev/http/errors.ts";
|
|
5
5
|
import { authenticatedRequest } from "./authenticatedRequest.ts";
|
|
6
|
-
import { type CachedResponse, responseCache } from "./cache.ts";
|
|
6
|
+
import { type CachedResponse, responseCache, revalidatedResponse } from "./cache.ts";
|
|
7
7
|
import { GitHubTransportError } from "./errors.ts";
|
|
8
8
|
import type { GitHubToken } from "./GitHubToken.ts";
|
|
9
9
|
import type { RateLimits } from "./rateLimits.ts";
|
|
@@ -17,28 +17,30 @@ export function requests(
|
|
|
17
17
|
refresh: typeof TokenRefresh.Service = undefined,
|
|
18
18
|
) {
|
|
19
19
|
const cache = responseCache();
|
|
20
|
-
const send = authenticatedRequest(client, token, rates, refresh);
|
|
20
|
+
const send = authenticatedRequest(HttpClient.withScope(client), token, rates, refresh);
|
|
21
21
|
return Effect.fn("GitHub.request")(function* (input: HttpClientRequest.HttpClientRequest, limit: number) {
|
|
22
|
-
const key =
|
|
22
|
+
const key = JSON.stringify([input.url, input.urlParams, input.headers.accept]);
|
|
23
23
|
const cached = input.method === "GET" ? cache.get(key) : undefined;
|
|
24
24
|
let request = HttpClientRequest.removeHeader(input, "if-none-match");
|
|
25
|
-
if (cached?.
|
|
26
|
-
request = HttpClientRequest.setHeader(request, "If-None-Match", cached.
|
|
25
|
+
if (cached?.etag !== undefined) {
|
|
26
|
+
request = HttpClientRequest.setHeader(request, "If-None-Match", cached.etag);
|
|
27
27
|
}
|
|
28
28
|
const response = yield* send(request, cached === undefined ? undefined : "revalidate");
|
|
29
29
|
if (isUnchanged(response) && cached !== undefined) {
|
|
30
30
|
if (cached.bytes.byteLength > limit) {
|
|
31
31
|
return yield* new ResponseBodyTooLarge({ limit });
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
const entry = revalidatedResponse(cached, response);
|
|
34
|
+
cache.set(key, entry);
|
|
35
|
+
return entry;
|
|
34
36
|
}
|
|
35
37
|
const bytes = yield* readBoundedBody(response, limit).pipe(
|
|
36
38
|
Effect.catchTag("HttpClientError", (cause) => Effect.fail(new GitHubTransportError({ path: new URL(input.url).pathname }, { cause }))),
|
|
37
39
|
);
|
|
38
|
-
const entry: CachedResponse = { bytes, decoded: new Map(), response };
|
|
40
|
+
const entry: CachedResponse = { bytes, decoded: new Map(), etag: response.headers.etag, response };
|
|
39
41
|
if (input.method === "GET" && response.headers.etag !== undefined) {
|
|
40
42
|
cache.set(key, entry);
|
|
41
43
|
}
|
|
42
44
|
return entry;
|
|
43
|
-
});
|
|
45
|
+
}, Effect.scoped);
|
|
44
46
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Cache, Effect, Exit, Schema, Stream } from "effect";
|
|
2
|
+
import { decodedPages } from "#pagedLists.ts";
|
|
3
|
+
import type { Representations } from "#representations.ts";
|
|
4
|
+
|
|
5
|
+
export const InstalledRepository = Schema.Struct({
|
|
6
|
+
htmlUrl: Schema.String,
|
|
7
|
+
id: Schema.Number,
|
|
8
|
+
name: Schema.String,
|
|
9
|
+
owner: Schema.Struct({ login: Schema.String }),
|
|
10
|
+
private: Schema.Boolean,
|
|
11
|
+
}).pipe(Schema.encodeKeys({ htmlUrl: "html_url" }));
|
|
12
|
+
|
|
13
|
+
export interface ViewerRepositoryAccess {
|
|
14
|
+
readonly administrable: readonly (typeof InstalledRepository.Type & { readonly installationId: number })[];
|
|
15
|
+
readonly readable: readonly (typeof InstalledRepository.Type & { readonly installationId: number })[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const Account = Schema.Struct({ login: Schema.String });
|
|
19
|
+
const Installation = Schema.Struct({ id: Schema.Number });
|
|
20
|
+
const Permission = Schema.Struct({ permission: Schema.String });
|
|
21
|
+
|
|
22
|
+
const installationAccess = Effect.fnUntraced(function* (client: Representations, installationId: number, login: string) {
|
|
23
|
+
const pages = yield* decodedPages(client, "/installation/repositories?per_page=100", InstalledRepository, {
|
|
24
|
+
arrayKey: "repositories",
|
|
25
|
+
}).pipe(Stream.runCollect);
|
|
26
|
+
const readable: ViewerRepositoryAccess["readable"][number][] = [];
|
|
27
|
+
const administrable: ViewerRepositoryAccess["administrable"][number][] = [];
|
|
28
|
+
for (const repository of pages.flatMap((page) => page.items)) {
|
|
29
|
+
const permissionPath = `/repos/${encodeURIComponent(repository.owner.login)}/${encodeURIComponent(repository.name)}/collaborators/${encodeURIComponent(login)}/permission`;
|
|
30
|
+
const permission = yield* Effect.flatMap(client.read(permissionPath), (response) => response.decode(Permission)).pipe(
|
|
31
|
+
Effect.map((grant) => grant.permission),
|
|
32
|
+
Effect.catchTag("RemoteNotFound", () => Effect.succeed("none")),
|
|
33
|
+
);
|
|
34
|
+
const installed = { ...repository, installationId };
|
|
35
|
+
if (!repository.private || ["read", "triage", "write", "maintain", "admin"].includes(permission)) {
|
|
36
|
+
readable.push(installed);
|
|
37
|
+
}
|
|
38
|
+
if (permission === "admin") {
|
|
39
|
+
administrable.push(installed);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { administrable, readable } satisfies ViewerRepositoryAccess;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export function viewerRepositoryAccess(app: Representations, installation: (id: number) => Effect.Effect<Representations>) {
|
|
46
|
+
const lookup = Effect.fnUntraced(function* (account: string) {
|
|
47
|
+
const path = account.startsWith("id:") ? `/user/${account.slice(3)}` : `/users/${encodeURIComponent(account.slice(6))}`;
|
|
48
|
+
const installations = yield* decodedPages(app, "/app/installations?per_page=100", Installation).pipe(Stream.runCollect);
|
|
49
|
+
const first = installations.flatMap((page) => page.items)[0];
|
|
50
|
+
if (first === undefined) {
|
|
51
|
+
return { administrable: [], readable: [] } satisfies ViewerRepositoryAccess;
|
|
52
|
+
}
|
|
53
|
+
const resolver = yield* installation(first.id);
|
|
54
|
+
const { login } = yield* Effect.flatMap(resolver.read(path), (response) => response.decode(Account));
|
|
55
|
+
const readable: ViewerRepositoryAccess["readable"][number][] = [];
|
|
56
|
+
const administrable: ViewerRepositoryAccess["administrable"][number][] = [];
|
|
57
|
+
for (const { id } of installations.flatMap((page) => page.items)) {
|
|
58
|
+
const client = yield* installation(id);
|
|
59
|
+
const access = yield* installationAccess(client, id, login);
|
|
60
|
+
readable.push(...access.readable);
|
|
61
|
+
administrable.push(...access.administrable);
|
|
62
|
+
}
|
|
63
|
+
return { administrable, readable } satisfies ViewerRepositoryAccess;
|
|
64
|
+
});
|
|
65
|
+
return Cache.makeWith(lookup, { capacity: 100, timeToLive: (exit) => (Exit.isSuccess(exit) ? "60 seconds" : 0) });
|
|
66
|
+
}
|