@usine/github 0.0.0 → 0.1.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 +21 -0
- package/README.md +110 -1
- package/dist/github-app.d.ts +23 -0
- package/dist/github-app.d.ts.map +1 -0
- package/dist/github-app.js +169 -0
- package/dist/github-app.js.map +1 -0
- package/dist/http.d.ts +19 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +68 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/item-writer.d.ts +22 -0
- package/dist/item-writer.d.ts.map +1 -0
- package/dist/item-writer.js +66 -0
- package/dist/item-writer.js.map +1 -0
- package/dist/jwt.d.ts +10 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +72 -0
- package/dist/jwt.js.map +1 -0
- package/dist/local-secrets.d.ts +28 -0
- package/dist/local-secrets.d.ts.map +1 -0
- package/dist/local-secrets.js +56 -0
- package/dist/local-secrets.js.map +1 -0
- package/dist/model.d.ts +128 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +133 -0
- package/dist/model.js.map +1 -0
- package/dist/polling/model.d.ts +624 -0
- package/dist/polling/model.d.ts.map +1 -0
- package/dist/polling/model.js +188 -0
- package/dist/polling/model.js.map +1 -0
- package/dist/polling/polling.d.ts +18 -0
- package/dist/polling/polling.d.ts.map +1 -0
- package/dist/polling/polling.js +320 -0
- package/dist/polling/polling.js.map +1 -0
- package/dist/polling/queries.d.ts +3 -0
- package/dist/polling/queries.d.ts.map +1 -0
- package/dist/polling/queries.js +125 -0
- package/dist/polling/queries.js.map +1 -0
- package/package.json +49 -4
package/dist/jwt.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Clock, Effect, Redacted } from "effect";
|
|
2
|
+
import { AppAuthenticationError } from "./model.js";
|
|
3
|
+
const encoder = new TextEncoder();
|
|
4
|
+
const RSA_ALGORITHM_IDENTIFIER = new Uint8Array([
|
|
5
|
+
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
|
|
6
|
+
]);
|
|
7
|
+
const concat = (...values) => {
|
|
8
|
+
const output = new Uint8Array(values.reduce((size, value) => size + value.length, 0));
|
|
9
|
+
let offset = 0;
|
|
10
|
+
for (const value of values) {
|
|
11
|
+
output.set(value, offset);
|
|
12
|
+
offset += value.length;
|
|
13
|
+
}
|
|
14
|
+
return output;
|
|
15
|
+
};
|
|
16
|
+
const derLength = (length) => {
|
|
17
|
+
if (length < 0x80)
|
|
18
|
+
return new Uint8Array([length]);
|
|
19
|
+
const bytes = [];
|
|
20
|
+
for (let remaining = length; remaining > 0; remaining >>>= 8)
|
|
21
|
+
bytes.unshift(remaining & 0xff);
|
|
22
|
+
return new Uint8Array([0x80 | bytes.length, ...bytes]);
|
|
23
|
+
};
|
|
24
|
+
const der = (tag, value) => concat(new Uint8Array([tag]), derLength(value.length), value);
|
|
25
|
+
/** WebCrypto imports PKCS#8 only; GitHub downloads PKCS#1, so wrap it. */
|
|
26
|
+
const wrapPkcs1AsPkcs8 = (pkcs1) => der(0x30, concat(new Uint8Array([0x02, 0x01, 0x00]), RSA_ALGORITHM_IDENTIFIER, der(0x04, pkcs1)));
|
|
27
|
+
const decodePem = (pem) => {
|
|
28
|
+
const pkcs1 = pem.includes("-----BEGIN RSA PRIVATE KEY-----");
|
|
29
|
+
const base64 = pem
|
|
30
|
+
.replace(/-----BEGIN (?:RSA )?PRIVATE KEY-----/gu, "")
|
|
31
|
+
.replace(/-----END (?:RSA )?PRIVATE KEY-----/gu, "")
|
|
32
|
+
.replace(/\s/gu, "");
|
|
33
|
+
const binary = atob(base64);
|
|
34
|
+
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
35
|
+
return pkcs1 ? wrapPkcs1AsPkcs8(bytes) : bytes;
|
|
36
|
+
};
|
|
37
|
+
const base64Url = (value) => {
|
|
38
|
+
let binary = "";
|
|
39
|
+
for (const byte of value)
|
|
40
|
+
binary += String.fromCharCode(byte);
|
|
41
|
+
return btoa(binary).replace(/=/gu, "").replace(/\+/gu, "-").replace(/\//gu, "_");
|
|
42
|
+
};
|
|
43
|
+
const encodeJson = (value) => base64Url(encoder.encode(JSON.stringify(value)));
|
|
44
|
+
const toArrayBuffer = (value) => {
|
|
45
|
+
const buffer = new ArrayBuffer(value.byteLength);
|
|
46
|
+
new Uint8Array(buffer).set(value);
|
|
47
|
+
return buffer;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Import the App private key once (platform-free WebCrypto, RS256). A key
|
|
51
|
+
* that cannot be imported fails here, at composition, not on the first request.
|
|
52
|
+
*/
|
|
53
|
+
export const appJwtSigner = Effect.fn("AppJwt.import")(function* (credentials) {
|
|
54
|
+
const key = yield* Effect.tryPromise({
|
|
55
|
+
try: async () => crypto.subtle.importKey("pkcs8", toArrayBuffer(decodePem(Redacted.value(credentials.privateKey))), { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["sign"]),
|
|
56
|
+
catch: () => new AppAuthenticationError({
|
|
57
|
+
message: "The GitHub App private key is not an RSA private key WebCrypto can import",
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
const sign = Effect.fn("AppJwt.sign")(function* () {
|
|
61
|
+
const now = Math.floor((yield* Clock.currentTimeMillis) / 1_000);
|
|
62
|
+
const unsigned = `${encodeJson({ alg: "RS256", typ: "JWT" })}.${encodeJson({
|
|
63
|
+
iat: now - 60,
|
|
64
|
+
exp: now + 9 * 60,
|
|
65
|
+
iss: credentials.appId,
|
|
66
|
+
})}`;
|
|
67
|
+
const signature = yield* Effect.promise(() => crypto.subtle.sign("RSASSA-PKCS1-v1_5", key, toArrayBuffer(encoder.encode(unsigned))));
|
|
68
|
+
return Redacted.make(`${unsigned}.${base64Url(new Uint8Array(signature))}`);
|
|
69
|
+
});
|
|
70
|
+
return sign;
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=jwt.js.map
|
package/dist/jwt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Instance } from "@usine/core";
|
|
2
|
+
import { Effect, FileSystem, Layer, Path, Schema } from "effect";
|
|
3
|
+
import { AppCredentials, GitHubCredentials } from "./model.ts";
|
|
4
|
+
export declare const GITHUB_APP_SECRETS_PATH = ".usine/github-app.json";
|
|
5
|
+
declare const EnvironmentCredentialsError_base: Schema.Class<EnvironmentCredentialsError, Schema.TaggedStruct<"EnvironmentCredentialsError", {
|
|
6
|
+
readonly message: Schema.NonEmptyString;
|
|
7
|
+
}>, import("effect/Cause").YieldableError>;
|
|
8
|
+
export declare class EnvironmentCredentialsError extends EnvironmentCredentialsError_base {
|
|
9
|
+
}
|
|
10
|
+
export declare const SecretsFileReason: Schema.Literals<readonly ["read", "permissions", "parse"]>;
|
|
11
|
+
export type SecretsFileReason = typeof SecretsFileReason.Type;
|
|
12
|
+
declare const SecretsFileError_base: Schema.Class<SecretsFileError, Schema.TaggedStruct<"SecretsFileError", {
|
|
13
|
+
readonly path: Schema.NonEmptyString;
|
|
14
|
+
readonly reason: Schema.Literals<readonly ["read", "permissions", "parse"]>;
|
|
15
|
+
readonly message: Schema.NonEmptyString;
|
|
16
|
+
}>, import("effect/Cause").YieldableError>;
|
|
17
|
+
export declare class SecretsFileError extends SecretsFileError_base {
|
|
18
|
+
}
|
|
19
|
+
export declare const LocalSecretsError: Schema.Union<readonly [typeof EnvironmentCredentialsError, typeof SecretsFileError]>;
|
|
20
|
+
export type LocalSecretsError = typeof LocalSecretsError.Type;
|
|
21
|
+
/**
|
|
22
|
+
* Load the orchestrator's App credentials. Each environment variable wins
|
|
23
|
+
* over its file field; when both are present the file is not touched.
|
|
24
|
+
*/
|
|
25
|
+
export declare const loadLocalCredentials: (root: string & import("effect/Brand").Brand<"InstanceDirectory">) => Effect.Effect<AppCredentials, EnvironmentCredentialsError | SecretsFileError, FileSystem.FileSystem | Path.Path>;
|
|
26
|
+
export declare const localSecretsLayer: (root: Instance.Directory) => Layer.Layer<GitHubCredentials, LocalSecretsError, FileSystem.FileSystem | Path.Path>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=local-secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-secrets.d.ts","sourceRoot":"","sources":["../src/local-secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAU,MAAM,EAAE,UAAU,EAAE,KAAK,EAAU,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACjF,OAAO,EAAE,cAAc,EAAS,iBAAiB,EAAsB,MAAM,YAAY,CAAC;AAE1F,eAAO,MAAM,uBAAuB,2BAA2B,CAAC;;;;AAEhE,qBAAa,2BAA4B,SAAQ,gCAI/C;CAAG;AAEL,eAAO,MAAM,iBAAiB,4DAAoD,CAAC;AACnF,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,IAAI,CAAC;;;;;;AAE9D,qBAAa,gBAAiB,SAAQ,qBAMpC;CAAG;AAEL,eAAO,MAAM,iBAAiB,sFAAgE,CAAC;AAC/F,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,IAAI,CAAC;AAwC9D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,wLAoB/B,CAAC;AAEH,eAAO,MAAM,iBAAiB,SACtB,QAAQ,CAAC,SAAS,KACvB,KAAK,CAAC,KAAK,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CACzB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Instance } from "@usine/core";
|
|
2
|
+
import { Config, Effect, FileSystem, Layer, Option, Path, Schema } from "effect";
|
|
3
|
+
import { AppCredentials, AppId, GitHubCredentials, RedactedPrivateKey } from "./model.js";
|
|
4
|
+
export const GITHUB_APP_SECRETS_PATH = ".usine/github-app.json";
|
|
5
|
+
export class EnvironmentCredentialsError extends Schema.TaggedError("@usine/github/EnvironmentCredentialsError")("EnvironmentCredentialsError", {
|
|
6
|
+
message: Schema.NonEmptyString,
|
|
7
|
+
}) {
|
|
8
|
+
}
|
|
9
|
+
export const SecretsFileReason = Schema.Literals(["read", "permissions", "parse"]);
|
|
10
|
+
export class SecretsFileError extends Schema.TaggedError("@usine/github/SecretsFileError")("SecretsFileError", {
|
|
11
|
+
path: Schema.NonEmptyString,
|
|
12
|
+
reason: SecretsFileReason,
|
|
13
|
+
message: Schema.NonEmptyString,
|
|
14
|
+
}) {
|
|
15
|
+
}
|
|
16
|
+
export const LocalSecretsError = Schema.Union([EnvironmentCredentialsError, SecretsFileError]);
|
|
17
|
+
const AppIdFromString = Schema.FiniteFromString.pipe(Schema.decodeTo(AppId));
|
|
18
|
+
/** Each variable is optional on its own; a present but invalid one is an error, never a fallback. */
|
|
19
|
+
const environment = Config.all({
|
|
20
|
+
appId: Config.option(Config.schema(AppIdFromString, "USINE_GITHUB_APP_ID")),
|
|
21
|
+
privateKey: Config.option(Config.schema(RedactedPrivateKey, "USINE_GITHUB_PRIVATE_KEY")),
|
|
22
|
+
});
|
|
23
|
+
const AppCredentialsFile = Schema.fromJsonString(Schema.Struct({ appId: AppId, privateKey: RedactedPrivateKey }).pipe(Schema.encodeKeys({ appId: "app_id", privateKey: "private_key" })));
|
|
24
|
+
const decodeCredentialsFile = Schema.decodeUnknownEffect(AppCredentialsFile);
|
|
25
|
+
const readSecretsFile = Effect.fn("GitHubCredentials.readSecretsFile")(function* (root) {
|
|
26
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
27
|
+
const path = yield* Path.Path;
|
|
28
|
+
const secretsPath = path.join(root, GITHUB_APP_SECRETS_PATH);
|
|
29
|
+
const fail = (reason, message) => new SecretsFileError({ path: secretsPath, reason, message });
|
|
30
|
+
const unreadable = () => fail("read", `Could not read GitHub App secrets at ${secretsPath}`);
|
|
31
|
+
const info = yield* fileSystem.stat(secretsPath).pipe(Effect.mapError(unreadable));
|
|
32
|
+
if (info.type !== "File" || (info.mode & 0o777) !== 0o600) {
|
|
33
|
+
return yield* fail("permissions", `GitHub App secrets must be a regular 0600 file: ${secretsPath}`);
|
|
34
|
+
}
|
|
35
|
+
const source = yield* fileSystem.readFileString(secretsPath).pipe(Effect.mapError(unreadable));
|
|
36
|
+
return yield* decodeCredentialsFile(source, { onExcessProperty: "error" }).pipe(Effect.mapError(() => fail("parse", `GitHub App secrets are invalid at ${secretsPath}`)));
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Load the orchestrator's App credentials. Each environment variable wins
|
|
40
|
+
* over its file field; when both are present the file is not touched.
|
|
41
|
+
*/
|
|
42
|
+
export const loadLocalCredentials = Effect.fn("GitHubCredentials.loadLocal")(function* (root) {
|
|
43
|
+
const env = yield* environment.pipe(Effect.mapError(() => new EnvironmentCredentialsError({
|
|
44
|
+
message: "USINE_GITHUB_APP_ID and USINE_GITHUB_PRIVATE_KEY must contain valid GitHub App credentials",
|
|
45
|
+
})));
|
|
46
|
+
if (Option.isSome(env.appId) && Option.isSome(env.privateKey)) {
|
|
47
|
+
return new AppCredentials({ appId: env.appId.value, privateKey: env.privateKey.value });
|
|
48
|
+
}
|
|
49
|
+
const file = yield* readSecretsFile(root);
|
|
50
|
+
return new AppCredentials({
|
|
51
|
+
appId: Option.getOrElse(env.appId, () => file.appId),
|
|
52
|
+
privateKey: Option.getOrElse(env.privateKey, () => file.privateKey),
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
export const localSecretsLayer = (root) => Layer.effect(GitHubCredentials, loadLocalCredentials(root));
|
|
56
|
+
//# sourceMappingURL=local-secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-secrets.js","sourceRoot":"","sources":["../src/local-secrets.ts"],"names":[],"mappings":"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"}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Repository } from "@usine/core";
|
|
2
|
+
import { Context, Schema } from "effect";
|
|
3
|
+
export declare const AppId: Schema.brand<Schema.Int, "GitHubAppId">;
|
|
4
|
+
export type AppId = typeof AppId.Type;
|
|
5
|
+
export declare const InstallationId: Schema.brand<Schema.Int, "GitHubInstallationId">;
|
|
6
|
+
export type InstallationId = typeof InstallationId.Type;
|
|
7
|
+
export declare const RepositoryId: Schema.brand<Schema.Int, "GitHubRepositoryId">;
|
|
8
|
+
export type RepositoryId = typeof RepositoryId.Type;
|
|
9
|
+
export declare const PrivateKey: Schema.brand<Schema.NonEmptyString, "GitHubAppPrivateKey">;
|
|
10
|
+
export type PrivateKey = typeof PrivateKey.Type;
|
|
11
|
+
/** A PEM string parsed straight into a `Redacted` that can never be encoded back out. */
|
|
12
|
+
export declare const RedactedPrivateKey: Schema.RedactedFromValue<Schema.brand<Schema.NonEmptyString, "GitHubAppPrivateKey">>;
|
|
13
|
+
export declare const GitHubOperation: Schema.Literals<readonly ["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"]>;
|
|
14
|
+
export type GitHubOperation = typeof GitHubOperation.Type;
|
|
15
|
+
declare const AppCredentials_base: Schema.Class<AppCredentials, Schema.Struct<{
|
|
16
|
+
readonly appId: Schema.brand<Schema.Int, "GitHubAppId">;
|
|
17
|
+
readonly privateKey: Schema.RedactedFromValue<Schema.brand<Schema.NonEmptyString, "GitHubAppPrivateKey">>;
|
|
18
|
+
}>, {}>;
|
|
19
|
+
/** Credentials held by the orchestrator and never passed to a worker. */
|
|
20
|
+
export declare class AppCredentials extends AppCredentials_base {
|
|
21
|
+
}
|
|
22
|
+
declare const GitHubCredentials_base: Context.ServiceClass<GitHubCredentials, "@usine/github/GitHubCredentials", AppCredentials>;
|
|
23
|
+
/** Service boundary that keeps credential sourcing separate from App use. */
|
|
24
|
+
export declare class GitHubCredentials extends GitHubCredentials_base {
|
|
25
|
+
}
|
|
26
|
+
export declare const REQUIRED_REPOSITORY_PERMISSIONS: {
|
|
27
|
+
readonly checks: "read";
|
|
28
|
+
readonly contents: "write";
|
|
29
|
+
readonly issues: "write";
|
|
30
|
+
readonly metadata: "read";
|
|
31
|
+
readonly pull_requests: "write";
|
|
32
|
+
};
|
|
33
|
+
export type RequiredRepositoryPermission = keyof typeof REQUIRED_REPOSITORY_PERMISSIONS;
|
|
34
|
+
/** Pure deriver: the required permissions an installation's grant does not satisfy. */
|
|
35
|
+
export declare const missingPermissions: (granted: Readonly<Record<string, string>>) => ReadonlyArray<string>;
|
|
36
|
+
declare const Installation_base: Schema.Class<Installation, Schema.Struct<{
|
|
37
|
+
readonly id: Schema.brand<Schema.Int, "GitHubInstallationId">;
|
|
38
|
+
readonly accountLogin: Schema.NonEmptyString;
|
|
39
|
+
}>, {}>;
|
|
40
|
+
export declare class Installation extends Installation_base {
|
|
41
|
+
}
|
|
42
|
+
declare const InstallationWide_base: Schema.Class<InstallationWide, Schema.TaggedStruct<"InstallationWide", {}>, {}>;
|
|
43
|
+
export declare class InstallationWide extends InstallationWide_base {
|
|
44
|
+
}
|
|
45
|
+
declare const SingleRepository_base: Schema.Class<SingleRepository, Schema.TaggedStruct<"SingleRepository", {
|
|
46
|
+
readonly repository: Schema.brand<Schema.String, "RepoName">;
|
|
47
|
+
readonly repositoryId: Schema.brand<Schema.Int, "GitHubRepositoryId">;
|
|
48
|
+
}>, {}>;
|
|
49
|
+
export declare class SingleRepository extends SingleRepository_base {
|
|
50
|
+
}
|
|
51
|
+
export declare const TokenScope: Schema.Union<readonly [typeof InstallationWide, typeof SingleRepository]>;
|
|
52
|
+
export type TokenScope = typeof TokenScope.Type;
|
|
53
|
+
declare const InstallationToken_base: Schema.Class<InstallationToken, Schema.Struct<{
|
|
54
|
+
readonly value: Schema.Redacted<Schema.NonEmptyString>;
|
|
55
|
+
readonly expiresAt: Schema.DateTimeUtc;
|
|
56
|
+
readonly scope: Schema.Union<readonly [typeof InstallationWide, typeof SingleRepository]>;
|
|
57
|
+
}>, {}>;
|
|
58
|
+
export declare class InstallationToken extends InstallationToken_base {
|
|
59
|
+
}
|
|
60
|
+
declare const CoveredProject_base: Schema.Class<CoveredProject, Schema.TaggedStruct<"CoveredProject", {
|
|
61
|
+
readonly installationId: Schema.brand<Schema.Int, "GitHubInstallationId">;
|
|
62
|
+
readonly repository: Schema.brand<Schema.String, "RepoName">;
|
|
63
|
+
readonly repositoryId: Schema.brand<Schema.Int, "GitHubRepositoryId">;
|
|
64
|
+
}>, {}>;
|
|
65
|
+
export declare class CoveredProject extends CoveredProject_base {
|
|
66
|
+
}
|
|
67
|
+
declare const ProjectNotInstalledError_base: Schema.Class<ProjectNotInstalledError, Schema.TaggedStruct<"ProjectNotInstalledError", {
|
|
68
|
+
readonly repository: Schema.brand<Schema.String, "RepoName">;
|
|
69
|
+
readonly message: Schema.NonEmptyString;
|
|
70
|
+
}>, import("effect/Cause").YieldableError>;
|
|
71
|
+
/** Typed, project-local registry drift. It never fails covered siblings. */
|
|
72
|
+
export declare class ProjectNotInstalledError extends ProjectNotInstalledError_base {
|
|
73
|
+
}
|
|
74
|
+
export declare const ProjectAccess: Schema.Union<readonly [typeof CoveredProject, typeof ProjectNotInstalledError]>;
|
|
75
|
+
export type ProjectAccess = typeof ProjectAccess.Type;
|
|
76
|
+
export interface InstalledRepository {
|
|
77
|
+
readonly id: RepositoryId;
|
|
78
|
+
readonly name: Repository.Name;
|
|
79
|
+
}
|
|
80
|
+
/** Pure registry⊆installation derivation: one result, and one error, per project. */
|
|
81
|
+
export declare const deriveProjectAccess: (installation: Installation, registry: ReadonlyArray<Repository.Name>, installed: ReadonlyArray<InstalledRepository>) => ReadonlyArray<ProjectAccess>;
|
|
82
|
+
declare const AppAuthenticationError_base: Schema.Class<AppAuthenticationError, Schema.TaggedStruct<"AppAuthenticationError", {
|
|
83
|
+
readonly message: Schema.NonEmptyString;
|
|
84
|
+
}>, import("effect/Cause").YieldableError>;
|
|
85
|
+
/** The private key cannot sign: raised once, when the App layer is built. */
|
|
86
|
+
export declare class AppAuthenticationError extends AppAuthenticationError_base {
|
|
87
|
+
}
|
|
88
|
+
export declare const InstallationDiscoveryReason: Schema.Literals<readonly ["missing", "multiple", "suspended"]>;
|
|
89
|
+
export type InstallationDiscoveryReason = typeof InstallationDiscoveryReason.Type;
|
|
90
|
+
declare const InstallationDiscoveryError_base: Schema.Class<InstallationDiscoveryError, Schema.TaggedStruct<"InstallationDiscoveryError", {
|
|
91
|
+
readonly reason: Schema.Literals<readonly ["missing", "multiple", "suspended"]>;
|
|
92
|
+
readonly message: Schema.NonEmptyString;
|
|
93
|
+
}>, import("effect/Cause").YieldableError>;
|
|
94
|
+
export declare class InstallationDiscoveryError extends InstallationDiscoveryError_base {
|
|
95
|
+
}
|
|
96
|
+
declare const AppPermissionError_base: Schema.Class<AppPermissionError, Schema.TaggedStruct<"AppPermissionError", {
|
|
97
|
+
readonly missing: Schema.$Array<Schema.NonEmptyString>;
|
|
98
|
+
readonly message: Schema.NonEmptyString;
|
|
99
|
+
}>, import("effect/Cause").YieldableError>;
|
|
100
|
+
export declare class AppPermissionError extends AppPermissionError_base {
|
|
101
|
+
}
|
|
102
|
+
declare const GitHubApiError_base: Schema.Class<GitHubApiError, Schema.TaggedStruct<"GitHubApiError", {
|
|
103
|
+
readonly operation: Schema.Literals<readonly ["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"]>;
|
|
104
|
+
readonly status: Schema.Option<Schema.Int>;
|
|
105
|
+
readonly message: Schema.NonEmptyString;
|
|
106
|
+
}>, import("effect/Cause").YieldableError>;
|
|
107
|
+
export declare class GitHubApiError extends GitHubApiError_base {
|
|
108
|
+
}
|
|
109
|
+
declare const GitHubRateLimitError_base: Schema.Class<GitHubRateLimitError, Schema.TaggedStruct<"GitHubRateLimitError", {
|
|
110
|
+
readonly operation: Schema.Literals<readonly ["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"]>;
|
|
111
|
+
readonly retryAt: Schema.DateTimeUtc;
|
|
112
|
+
readonly message: Schema.NonEmptyString;
|
|
113
|
+
}>, import("effect/Cause").YieldableError>;
|
|
114
|
+
export declare class GitHubRateLimitError extends GitHubRateLimitError_base {
|
|
115
|
+
}
|
|
116
|
+
declare const InvalidGitHubResponseError_base: Schema.Class<InvalidGitHubResponseError, Schema.TaggedStruct<"InvalidGitHubResponseError", {
|
|
117
|
+
readonly operation: Schema.Literals<readonly ["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"]>;
|
|
118
|
+
readonly message: Schema.NonEmptyString;
|
|
119
|
+
/** What made the response invalid — decode path, missing field, GraphQL errors. */
|
|
120
|
+
readonly detail: Schema.NonEmptyString;
|
|
121
|
+
}>, import("effect/Cause").YieldableError>;
|
|
122
|
+
export declare class InvalidGitHubResponseError extends InvalidGitHubResponseError_base {
|
|
123
|
+
}
|
|
124
|
+
/** Everything one GitHub App call can fail with. */
|
|
125
|
+
export declare const GitHubAppError: Schema.Union<readonly [typeof InstallationDiscoveryError, typeof AppPermissionError, typeof GitHubApiError, typeof GitHubRateLimitError, typeof InvalidGitHubResponseError]>;
|
|
126
|
+
export type GitHubAppError = typeof GitHubAppError.Type;
|
|
127
|
+
export {};
|
|
128
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Repository } from "@usine/core";
|
|
2
|
+
import { Context, Schema } from "effect";
|
|
3
|
+
const PositiveInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1));
|
|
4
|
+
export const AppId = PositiveInt.pipe(Schema.brand("GitHubAppId"));
|
|
5
|
+
export const InstallationId = PositiveInt.pipe(Schema.brand("GitHubInstallationId"));
|
|
6
|
+
export const RepositoryId = PositiveInt.pipe(Schema.brand("GitHubRepositoryId"));
|
|
7
|
+
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"));
|
|
8
|
+
/** A PEM string parsed straight into a `Redacted` that can never be encoded back out. */
|
|
9
|
+
export const RedactedPrivateKey = Schema.RedactedFromValue(PrivateKey, { disallowEncode: true });
|
|
10
|
+
export const GitHubOperation = Schema.Literals([
|
|
11
|
+
"discover-installation",
|
|
12
|
+
"fetch-item-labels",
|
|
13
|
+
"fetch-issue-snapshot",
|
|
14
|
+
"fetch-pull-request-snapshot",
|
|
15
|
+
"list-installation-repositories",
|
|
16
|
+
"list-repository-labels",
|
|
17
|
+
"mint-installation-token",
|
|
18
|
+
"mint-worker-token",
|
|
19
|
+
"poll-repository-changes",
|
|
20
|
+
"update-item-labels",
|
|
21
|
+
]);
|
|
22
|
+
/** Credentials held by the orchestrator and never passed to a worker. */
|
|
23
|
+
export class AppCredentials extends Schema.Class("@usine/github/AppCredentials")({
|
|
24
|
+
appId: AppId,
|
|
25
|
+
privateKey: RedactedPrivateKey,
|
|
26
|
+
}) {
|
|
27
|
+
}
|
|
28
|
+
/** Service boundary that keeps credential sourcing separate from App use. */
|
|
29
|
+
export class GitHubCredentials extends Context.Service()("@usine/github/GitHubCredentials") {
|
|
30
|
+
}
|
|
31
|
+
export const REQUIRED_REPOSITORY_PERMISSIONS = {
|
|
32
|
+
checks: "read",
|
|
33
|
+
contents: "write",
|
|
34
|
+
issues: "write",
|
|
35
|
+
metadata: "read",
|
|
36
|
+
pull_requests: "write",
|
|
37
|
+
};
|
|
38
|
+
const grants = (actual, required) => actual === required || (required === "read" && actual === "write");
|
|
39
|
+
/** Pure deriver: the required permissions an installation's grant does not satisfy. */
|
|
40
|
+
export const missingPermissions = (granted) => Object.entries(REQUIRED_REPOSITORY_PERMISSIONS).flatMap(([permission, required]) => grants(granted[permission], required) ? [] : [`${permission}:${required}`]);
|
|
41
|
+
export class Installation extends Schema.Class("@usine/github/Installation")({
|
|
42
|
+
id: InstallationId,
|
|
43
|
+
accountLogin: Schema.NonEmptyString,
|
|
44
|
+
}) {
|
|
45
|
+
}
|
|
46
|
+
export class InstallationWide extends Schema.TaggedClass("@usine/github/TokenScope/InstallationWide")("InstallationWide", {}) {
|
|
47
|
+
}
|
|
48
|
+
export class SingleRepository extends Schema.TaggedClass("@usine/github/TokenScope/SingleRepository")("SingleRepository", {
|
|
49
|
+
repository: Repository.Name,
|
|
50
|
+
repositoryId: RepositoryId,
|
|
51
|
+
}) {
|
|
52
|
+
}
|
|
53
|
+
export const TokenScope = Schema.Union([InstallationWide, SingleRepository]);
|
|
54
|
+
export class InstallationToken extends Schema.Class("@usine/github/InstallationToken")({
|
|
55
|
+
value: Schema.Redacted(Schema.NonEmptyString, { disallowJsonEncode: true }),
|
|
56
|
+
expiresAt: Schema.DateTimeUtc,
|
|
57
|
+
scope: TokenScope,
|
|
58
|
+
}) {
|
|
59
|
+
}
|
|
60
|
+
export class CoveredProject extends Schema.TaggedClass("@usine/github/ProjectAccess/CoveredProject")("CoveredProject", {
|
|
61
|
+
installationId: InstallationId,
|
|
62
|
+
repository: Repository.Name,
|
|
63
|
+
repositoryId: RepositoryId,
|
|
64
|
+
}) {
|
|
65
|
+
}
|
|
66
|
+
/** Typed, project-local registry drift. It never fails covered siblings. */
|
|
67
|
+
export class ProjectNotInstalledError extends Schema.TaggedError("@usine/github/ProjectNotInstalledError")("ProjectNotInstalledError", {
|
|
68
|
+
repository: Repository.Name,
|
|
69
|
+
message: Schema.NonEmptyString,
|
|
70
|
+
}) {
|
|
71
|
+
}
|
|
72
|
+
export const ProjectAccess = Schema.Union([CoveredProject, ProjectNotInstalledError]);
|
|
73
|
+
/** Pure registry⊆installation derivation: one result, and one error, per project. */
|
|
74
|
+
export const deriveProjectAccess = (installation, registry, installed) => {
|
|
75
|
+
const byName = new Map(installed.map((repo) => [repo.name.toLowerCase(), repo]));
|
|
76
|
+
return registry.map((repository) => {
|
|
77
|
+
const match = byName.get(repository.toLowerCase());
|
|
78
|
+
return match === undefined
|
|
79
|
+
? new ProjectNotInstalledError({
|
|
80
|
+
repository,
|
|
81
|
+
message: `The GitHub App installation does not cover ${repository}`,
|
|
82
|
+
})
|
|
83
|
+
: new CoveredProject({
|
|
84
|
+
installationId: installation.id,
|
|
85
|
+
repository,
|
|
86
|
+
repositoryId: match.id,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
/** The private key cannot sign: raised once, when the App layer is built. */
|
|
91
|
+
export class AppAuthenticationError extends Schema.TaggedError("@usine/github/AppAuthenticationError")("AppAuthenticationError", {
|
|
92
|
+
message: Schema.NonEmptyString,
|
|
93
|
+
}) {
|
|
94
|
+
}
|
|
95
|
+
export const InstallationDiscoveryReason = Schema.Literals(["missing", "multiple", "suspended"]);
|
|
96
|
+
export class InstallationDiscoveryError extends Schema.TaggedError("@usine/github/InstallationDiscoveryError")("InstallationDiscoveryError", {
|
|
97
|
+
reason: InstallationDiscoveryReason,
|
|
98
|
+
message: Schema.NonEmptyString,
|
|
99
|
+
}) {
|
|
100
|
+
}
|
|
101
|
+
export class AppPermissionError extends Schema.TaggedError("@usine/github/AppPermissionError")("AppPermissionError", {
|
|
102
|
+
missing: Schema.Array(Schema.NonEmptyString).check(Schema.isNonEmpty()),
|
|
103
|
+
message: Schema.NonEmptyString,
|
|
104
|
+
}) {
|
|
105
|
+
}
|
|
106
|
+
export class GitHubApiError extends Schema.TaggedError("@usine/github/GitHubApiError")("GitHubApiError", {
|
|
107
|
+
operation: GitHubOperation,
|
|
108
|
+
status: Schema.Option(Schema.Int),
|
|
109
|
+
message: Schema.NonEmptyString,
|
|
110
|
+
}) {
|
|
111
|
+
}
|
|
112
|
+
export class GitHubRateLimitError extends Schema.TaggedError("@usine/github/GitHubRateLimitError")("GitHubRateLimitError", {
|
|
113
|
+
operation: GitHubOperation,
|
|
114
|
+
retryAt: Schema.DateTimeUtc,
|
|
115
|
+
message: Schema.NonEmptyString,
|
|
116
|
+
}) {
|
|
117
|
+
}
|
|
118
|
+
export class InvalidGitHubResponseError extends Schema.TaggedError("@usine/github/InvalidGitHubResponseError")("InvalidGitHubResponseError", {
|
|
119
|
+
operation: GitHubOperation,
|
|
120
|
+
message: Schema.NonEmptyString,
|
|
121
|
+
/** What made the response invalid — decode path, missing field, GraphQL errors. */
|
|
122
|
+
detail: Schema.NonEmptyString,
|
|
123
|
+
}) {
|
|
124
|
+
}
|
|
125
|
+
/** Everything one GitHub App call can fail with. */
|
|
126
|
+
export const GitHubAppError = Schema.Union([
|
|
127
|
+
InstallationDiscoveryError,
|
|
128
|
+
AppPermissionError,
|
|
129
|
+
GitHubApiError,
|
|
130
|
+
GitHubRateLimitError,
|
|
131
|
+
InvalidGitHubResponseError,
|
|
132
|
+
]);
|
|
133
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|