@shipfox/api-integration-github 13.1.0 → 14.0.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +17 -0
- package/dist/api/client.d.ts +9 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +32 -2
- package/dist/api/client.js.map +1 -1
- package/dist/api/installation-token-envelope.d.ts.map +1 -1
- package/dist/api/installation-token-envelope.js.map +1 -1
- package/dist/core/source-control.d.ts +2 -1
- package/dist/core/source-control.d.ts.map +1 -1
- package/dist/core/source-control.js +27 -1
- package/dist/core/source-control.js.map +1 -1
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/api/client.test.ts +89 -19
- package/src/api/client.ts +54 -1
- package/src/api/installation-token-envelope.ts +3 -1
- package/src/core/agent-tools.test.ts +1 -0
- package/src/core/install.test.ts +3 -0
- package/src/core/source-control.test.ts +93 -0
- package/src/core/source-control.ts +38 -0
- package/src/index.test.ts +3 -1
- package/src/index.ts +2 -0
- package/src/presentation/routes/install.test.ts +3 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled: 32 files with swc (
|
|
2
|
+
Successfully compiled: 32 files with swc (433.51ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @shipfox/api-integration-github
|
|
2
2
|
|
|
3
|
+
## 14.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 18e9bad: Adds source-control ref resolution that pins branch and tag names to commits.
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- 1b71a66: Exposes each provider's event catalog and the fixed-event providers on the integration validation context. Every provider now refuses the reserved `manual` and `cron` connection slugs.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [18e9bad]
|
|
16
|
+
- Updated dependencies [1b71a66]
|
|
17
|
+
- @shipfox/api-integration-spi@2.0.0
|
|
18
|
+
- @shipfox/api-integration-github-dto@14.0.0
|
|
19
|
+
|
|
3
20
|
## 13.1.0
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
package/dist/api/client.d.ts
CHANGED
|
@@ -38,6 +38,9 @@ export interface GithubFileContent {
|
|
|
38
38
|
content: string;
|
|
39
39
|
size: number;
|
|
40
40
|
}
|
|
41
|
+
export interface GithubCommit {
|
|
42
|
+
sha: string;
|
|
43
|
+
}
|
|
41
44
|
export interface GithubUserInstallationPage {
|
|
42
45
|
installationIds: number[];
|
|
43
46
|
nextCursor: string | null;
|
|
@@ -82,6 +85,11 @@ export interface GithubApiClient extends Partial<GithubBotUserClient> {
|
|
|
82
85
|
ref: string;
|
|
83
86
|
path: string;
|
|
84
87
|
}): Promise<GithubFileContent>;
|
|
88
|
+
listRepositoryCommits(input: {
|
|
89
|
+
installationId: number;
|
|
90
|
+
repositoryId: number;
|
|
91
|
+
ref: string;
|
|
92
|
+
}): Promise<GithubCommit[]>;
|
|
85
93
|
createInstallationAccessToken(input: {
|
|
86
94
|
installationId: number;
|
|
87
95
|
repositoryId: number;
|
|
@@ -96,5 +104,5 @@ export interface GithubInstallationAccessToken {
|
|
|
96
104
|
permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;
|
|
97
105
|
}
|
|
98
106
|
export declare function createGithubApiClient(): GithubApiClient & GithubBotUserClient;
|
|
99
|
-
export declare function mapGithubError<T>(operation: () => Promise<T>, notFoundReason?: 'repository-not-found' | 'installation-not-found' | 'file-not-found' | 'provider-rejected'): Promise<T>;
|
|
107
|
+
export declare function mapGithubError<T>(operation: () => Promise<T>, notFoundReason?: 'repository-not-found' | 'installation-not-found' | 'file-not-found' | 'ref-not-found' | 'provider-rejected'): Promise<T>;
|
|
100
108
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/api/client.js
CHANGED
|
@@ -113,7 +113,10 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
|
|
|
113
113
|
async getRepository(input) {
|
|
114
114
|
const octokit = await mapGithubError(()=>getGithubInstallationOctokit(this.getApp(), input.installationId));
|
|
115
115
|
const response = await mapGithubError(()=>octokit.request('GET /repositories/{repository_id}', {
|
|
116
|
-
repository_id: input.repositoryId
|
|
116
|
+
repository_id: input.repositoryId,
|
|
117
|
+
request: {
|
|
118
|
+
signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)
|
|
119
|
+
}
|
|
117
120
|
}));
|
|
118
121
|
return toGithubRepository(response.data);
|
|
119
122
|
}
|
|
@@ -211,6 +214,30 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
|
|
|
211
214
|
content: Buffer.from(data.content, 'base64').toString('utf8')
|
|
212
215
|
};
|
|
213
216
|
}
|
|
217
|
+
async listRepositoryCommits(input) {
|
|
218
|
+
const octokit = await mapGithubError(()=>getGithubInstallationOctokit(this.getApp(), input.installationId));
|
|
219
|
+
const repository = await this.getRepository({
|
|
220
|
+
installationId: input.installationId,
|
|
221
|
+
repositoryId: input.repositoryId
|
|
222
|
+
});
|
|
223
|
+
const response = await mapGithubError(()=>octokit.rest.repos.listCommits({
|
|
224
|
+
owner: repository.ownerLogin,
|
|
225
|
+
repo: repository.name,
|
|
226
|
+
sha: input.ref,
|
|
227
|
+
per_page: 1,
|
|
228
|
+
request: {
|
|
229
|
+
signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)
|
|
230
|
+
}
|
|
231
|
+
}), 'ref-not-found');
|
|
232
|
+
return response.data.map((commit)=>{
|
|
233
|
+
if (typeof commit.sha !== 'string') {
|
|
234
|
+
throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub commit response is missing the commit sha');
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
sha: commit.sha
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
}
|
|
214
241
|
async createInstallationAccessToken(input) {
|
|
215
242
|
const response = await mapGithubError(()=>this.getApp().octokit.rest.apps.createInstallationAccessToken({
|
|
216
243
|
installation_id: input.installationId,
|
|
@@ -322,7 +349,10 @@ export async function mapGithubError(operation, notFoundReason = 'repository-not
|
|
|
322
349
|
}
|
|
323
350
|
if (error instanceof RequestError) {
|
|
324
351
|
if (error.status === 404) {
|
|
325
|
-
throw new GithubIntegrationProviderError(notFoundReason, error.message, undefined, error.status);
|
|
352
|
+
throw new GithubIntegrationProviderError(notFoundReason === 'ref-not-found' ? 'repository-not-found' : notFoundReason, error.message, undefined, error.status);
|
|
353
|
+
}
|
|
354
|
+
if (notFoundReason === 'ref-not-found' && (error.status === 409 || error.status === 422)) {
|
|
355
|
+
throw new GithubIntegrationProviderError('ref-not-found', error.message, undefined, error.status);
|
|
326
356
|
}
|
|
327
357
|
if (error.status === 429 || isGithubRateLimitError(error)) {
|
|
328
358
|
throw new GithubIntegrationProviderError('rate-limited', error.message, retryAfterSeconds(error), error.status);
|
package/dist/api/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {isRecord, MAX_REPOSITORY_FILE_BYTES} from '@shipfox/api-integration-spi';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {App, Octokit, RequestError} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\nimport {recordInstallationTokenFormat} from '#metrics/index.js';\nimport {\n getGithubInstallationOctokit,\n githubInstallationTokenFormatPlugin,\n} from './github-octokit.js';\n\nconst NEXT_PAGE_RE = /[?&]page=(\\d+)/;\nconst TRAILING_SLASHES_RE = /\\/+$/;\nconst MAX_TREE_WALK_DEPTH = 10;\nconst GITHUB_API_TIMEOUT_MS = 10_000;\n\nexport interface GithubAccount {\n login: string;\n type: string;\n}\n\nexport interface GithubInstallationDetails {\n id: number;\n account: GithubAccount;\n repositorySelection: string;\n suspendedAt: Date | null;\n htmlUrl: string;\n raw: Record<string, unknown>;\n}\n\nexport interface GithubRepository {\n id: number;\n ownerLogin: string;\n name: string;\n fullName: string;\n defaultBranch: string;\n private: boolean;\n visibility?: string | undefined;\n cloneUrl: string;\n htmlUrl: string;\n}\n\nexport interface GithubRepositoryPage {\n repositories: GithubRepository[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileEntry {\n path: string;\n size: number | null;\n}\n\nexport interface GithubFilePage {\n files: GithubFileEntry[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileContent {\n path: string;\n content: string;\n size: number;\n}\n\nexport interface GithubUserInstallationPage {\n installationIds: number[];\n nextCursor: string | null;\n}\n\nexport interface GithubBotUser {\n id: number;\n login: string;\n}\n\nexport interface GithubBotUserClient {\n getBotUser(input: {username: string; installationAccessToken: string}): Promise<GithubBotUser>;\n}\n\nexport interface GithubApiClient extends Partial<GithubBotUserClient> {\n exchangeOAuthCode(code: string): Promise<string>;\n listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage>;\n getInstallation(installationId: number): Promise<GithubInstallationDetails>;\n listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage>;\n getRepository(input: {installationId: number; repositoryId: number}): Promise<GithubRepository>;\n listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage>;\n fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent>;\n createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken>;\n}\n\nexport interface GithubInstallationAccessToken {\n token: string;\n expiresAt: Date;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n}\n\nexport function createGithubApiClient(): GithubApiClient & GithubBotUserClient {\n return new OctokitGithubApiClient();\n}\n\nclass OctokitGithubApiClient implements GithubApiClient, GithubBotUserClient {\n private app: App | undefined;\n private readonly botUsers = new Map<string, GithubBotUser>();\n private readonly botUserLookups = new Map<\n string,\n {installationAccessToken: string; promise: Promise<GithubBotUser>}\n >();\n\n async exchangeOAuthCode(code: string): Promise<string> {\n const body = await mapGithubOAuthError(() =>\n ky\n .post('https://github.com/login/oauth/access_token', {\n headers: {accept: 'application/json'},\n json: {\n client_id: config.GITHUB_APP_CLIENT_ID,\n client_secret: config.GITHUB_APP_CLIENT_SECRET,\n code,\n },\n })\n .json<{access_token?: unknown}>(),\n );\n\n if (typeof body.access_token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub OAuth response did not include an access token',\n );\n }\n return body.access_token;\n }\n\n getBotUser(input: {username: string; installationAccessToken: string}): Promise<GithubBotUser> {\n const cacheKey = input.username.trim().toLowerCase();\n const cached = this.botUsers.get(cacheKey);\n if (cached) return Promise.resolve(cached);\n\n const pending = this.botUserLookups.get(cacheKey);\n if (pending?.installationAccessToken === input.installationAccessToken) {\n return pending.promise;\n }\n\n const lookup = this.fetchBotUser(input).then((botUser) => {\n const resolved = this.botUsers.get(cacheKey);\n if (resolved) return resolved;\n\n this.botUsers.set(cacheKey, botUser);\n logger().info(\n {githubAppBotLogin: botUser.login, githubAppBotUserId: botUser.id},\n 'Resolved GitHub App bot identity',\n );\n return botUser;\n });\n const trackedLookup = lookup.finally(() => {\n if (this.botUserLookups.get(cacheKey)?.promise === trackedLookup) {\n this.botUserLookups.delete(cacheKey);\n }\n });\n this.botUserLookups.set(cacheKey, {\n installationAccessToken: input.installationAccessToken,\n promise: trackedLookup,\n });\n return trackedLookup;\n }\n\n async listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage> {\n const octokit = new Octokit({\n auth: input.userAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.request('GET /user/installations', {\n per_page: 100,\n page,\n }),\n );\n\n const installations = response.data.installations ?? [];\n return {\n installationIds: installations.map((installation) => installation.id),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getInstallation(installationId: number): Promise<GithubInstallationDetails> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.getInstallation({installation_id: installationId}),\n );\n const data = response.data;\n const account = data.account;\n if (!account) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation response did not include an account',\n );\n }\n\n const login = 'login' in account ? account.login : account.slug;\n return {\n id: data.id,\n account: {\n login,\n type: 'type' in account ? account.type : 'Enterprise',\n },\n repositorySelection: data.repository_selection,\n suspendedAt: data.suspended_at ? new Date(data.suspended_at) : null,\n htmlUrl: data.html_url,\n raw: data as unknown as Record<string, unknown>,\n };\n }\n\n async listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.rest.apps.listReposAccessibleToInstallation({\n per_page: input.limit,\n page,\n }),\n );\n\n return {\n repositories: response.data.repositories.map(toGithubRepository),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getRepository(input: {\n installationId: number;\n repositoryId: number;\n }): Promise<GithubRepository> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const response = await mapGithubError(() =>\n octokit.request('GET /repositories/{repository_id}', {\n repository_id: input.repositoryId,\n }),\n );\n\n return toGithubRepository(response.data);\n }\n\n async listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const owner = repository.ownerLogin;\n const repo = repository.name;\n const startPath = input.prefix.replace(TRAILING_SLASHES_RE, '');\n const offset = input.cursor ? Number.parseInt(input.cursor, 10) : 0;\n const start = Number.isNaN(offset) || offset < 0 ? 0 : offset;\n const collected: GithubFileEntry[] = [];\n const overflowLimit = start + input.limit + 1;\n\n type GetContentData = Awaited<ReturnType<typeof octokit.rest.repos.getContent>>['data'];\n\n const walk = async (path: string, depth: number): Promise<void> => {\n if (collected.length >= overflowLimit) return;\n if (depth > MAX_TREE_WALK_DEPTH) return;\n\n let data: GetContentData;\n try {\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner,\n repo,\n path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n data = response.data;\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.reason === 'file-not-found') {\n return;\n }\n throw error;\n }\n\n if (!Array.isArray(data)) {\n if (data.type === 'file' && data.path) {\n collected.push({path: data.path, size: typeof data.size === 'number' ? data.size : null});\n }\n return;\n }\n\n const entries = [...data].sort((a, b) => (a.path ?? '').localeCompare(b.path ?? ''));\n for (const entry of entries) {\n if (collected.length >= overflowLimit) return;\n if (!entry.path) continue;\n if (entry.type === 'file') {\n collected.push({\n path: entry.path,\n size: typeof entry.size === 'number' ? entry.size : null,\n });\n } else if (entry.type === 'dir') {\n await walk(entry.path, depth + 1);\n }\n }\n };\n\n await walk(startPath, 0);\n\n const sorted = collected.sort((a, b) => a.path.localeCompare(b.path));\n const page = sorted.slice(start, start + input.limit);\n const consumed = start + page.length;\n const hasMore = consumed < sorted.length;\n\n return {\n files: page,\n nextCursor: hasMore ? String(consumed) : null,\n };\n }\n\n async fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner: repository.ownerLogin,\n repo: repository.name,\n path: input.path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n const data = response.data;\n\n if (Array.isArray(data) || data.type !== 'file') {\n throw new GithubIntegrationProviderError('file-not-found', 'GitHub path is not a file');\n }\n if (data.size > MAX_REPOSITORY_FILE_BYTES) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n if (typeof data.content !== 'string' || data.encoding !== 'base64') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub file response did not include base64 content',\n );\n }\n\n return {\n path: data.path,\n size: data.size,\n content: Buffer.from(data.content, 'base64').toString('utf8'),\n };\n }\n\n async createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.createInstallationAccessToken({\n installation_id: input.installationId,\n repository_ids: [input.repositoryId],\n permissions: input.permissions ?? {contents: 'read'},\n }),\n );\n\n if (typeof response.data.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a token',\n );\n }\n\n recordInstallationTokenFormat(response.data.token);\n\n const expiresAt = new Date(response.data.expires_at);\n if (Number.isNaN(expiresAt.getTime())) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a valid expiry',\n );\n }\n\n return {\n token: response.data.token,\n expiresAt,\n ...(response.data.permissions === undefined ? {} : {permissions: response.data.permissions}),\n };\n }\n\n private getApp(): App {\n if (!this.app) {\n this.app = new App({\n appId: config.GITHUB_APP_ID,\n privateKey: normalizedGithubPrivateKey(),\n Octokit: Octokit.plugin(githubInstallationTokenFormatPlugin).defaults({\n baseUrl: normalizedGithubApiBaseUrl(),\n }),\n });\n }\n return this.app;\n }\n\n private async fetchBotUser(input: {\n username: string;\n installationAccessToken: string;\n }): Promise<GithubBotUser> {\n const octokit = new Octokit({\n auth: input.installationAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n let response: Awaited<ReturnType<typeof octokit.rest.users.getByUsername>>;\n try {\n response = await mapGithubError(\n () =>\n octokit.rest.users.getByUsername({\n username: input.username,\n request: {signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)},\n }),\n 'provider-rejected',\n );\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.status === 404) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n `Configured GitHub bot user ${input.username} was not found`,\n undefined,\n error.status,\n );\n }\n throw error;\n }\n\n const data: unknown = response.data;\n if (!isRecord(data)) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response is missing required fields',\n );\n }\n const {id, login, type} = data;\n if (\n typeof id !== 'number' ||\n !Number.isSafeInteger(id) ||\n id <= 0 ||\n typeof login !== 'string' ||\n login.trim().length === 0\n ) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response is missing required fields',\n );\n }\n if (type !== 'Bot') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'Configured GitHub username is not a bot account',\n );\n }\n const canonicalLogin = login.trim();\n if (canonicalLogin.toLowerCase() !== input.username.trim().toLowerCase()) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response did not match the configured username',\n );\n }\n return {id, login: canonicalLogin};\n }\n}\n\nasync function mapGithubOAuthError<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof HTTPError) {\n if (error.response.status === 429) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n 'GitHub OAuth code exchange failed',\n retryAfterSecondsFromHeaders(error.response.headers),\n );\n }\n if (error.response.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n 'GitHub OAuth code exchange failed',\n );\n }\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub OAuth code exchange failed',\n );\n }\n if (error instanceof TimeoutError) {\n throw new GithubIntegrationProviderError('timeout', 'GitHub OAuth request timed out');\n }\n throw error;\n }\n}\n\nexport async function mapGithubError<T>(\n operation: () => Promise<T>,\n notFoundReason:\n | 'repository-not-found'\n | 'installation-not-found'\n | 'file-not-found'\n | 'provider-rejected' = 'repository-not-found',\n): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError) throw error;\n if (isGithubTimeoutError(error)) {\n throw new GithubIntegrationProviderError('timeout', 'GitHub request timed out');\n }\n if (error instanceof RequestError) {\n if (error.status === 404) {\n throw new GithubIntegrationProviderError(\n notFoundReason,\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status === 429 || isGithubRateLimitError(error)) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status === 401 || error.status === 403) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status >= 400) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n error.message,\n undefined,\n error.status,\n );\n }\n }\n throw error;\n }\n}\n\nfunction isGithubTimeoutError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n if (error.name === 'AbortError' || error.name === 'TimeoutError') return true;\n return error.cause instanceof Error && isGithubTimeoutError(error.cause);\n}\n\nfunction isGithubRateLimitError(error: RequestError): boolean {\n return error.status === 403 && error.response?.headers['x-ratelimit-remaining'] === '0';\n}\n\nfunction retryAfterSeconds(error: RequestError): number | undefined {\n return parseRetryAfterSeconds(error.response?.headers['retry-after']);\n}\n\nfunction retryAfterSecondsFromHeaders(headers: Headers): number | undefined {\n return parseRetryAfterSeconds(headers.get('retry-after'));\n}\n\nfunction parseRetryAfterSeconds(\n retryAfter: string | number | null | undefined,\n): number | undefined {\n if (!retryAfter) return undefined;\n const parsed = Number.parseInt(String(retryAfter), 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction cursorToPage(cursor: string | undefined): number {\n if (!cursor) return 1;\n const page = Number.parseInt(cursor, 10);\n return Number.isNaN(page) || page < 1 ? 1 : page;\n}\n\nfunction nextCursorFromLink(link: string | undefined): string | null {\n if (!link) return null;\n const next = link.split(',').find((part) => part.includes('rel=\"next\"'));\n if (!next) return null;\n const match = next.match(NEXT_PAGE_RE);\n return match?.[1] ?? null;\n}\n\nfunction toGithubRepository(repository: {\n id: number;\n owner: {login: string};\n name: string;\n full_name: string;\n default_branch?: string | null | undefined;\n private: boolean;\n visibility?: string | undefined;\n clone_url?: string | null | undefined;\n html_url?: string | null | undefined;\n}): GithubRepository {\n if (!repository.default_branch || !repository.clone_url || !repository.html_url) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub repository response is missing required fields',\n );\n }\n return {\n id: repository.id,\n ownerLogin: repository.owner.login,\n name: repository.name,\n fullName: repository.full_name,\n defaultBranch: repository.default_branch,\n private: repository.private,\n visibility: repository.visibility,\n cloneUrl: repository.clone_url,\n htmlUrl: repository.html_url,\n };\n}\n"],"names":["Buffer","isRecord","MAX_REPOSITORY_FILE_BYTES","logger","ky","HTTPError","TimeoutError","App","Octokit","RequestError","config","normalizedGithubApiBaseUrl","normalizedGithubPrivateKey","GithubIntegrationProviderError","recordInstallationTokenFormat","getGithubInstallationOctokit","githubInstallationTokenFormatPlugin","NEXT_PAGE_RE","TRAILING_SLASHES_RE","MAX_TREE_WALK_DEPTH","GITHUB_API_TIMEOUT_MS","createGithubApiClient","OctokitGithubApiClient","exchangeOAuthCode","code","body","mapGithubOAuthError","post","headers","accept","json","client_id","GITHUB_APP_CLIENT_ID","client_secret","GITHUB_APP_CLIENT_SECRET","access_token","getBotUser","input","cacheKey","username","trim","toLowerCase","cached","botUsers","get","Promise","resolve","pending","botUserLookups","installationAccessToken","promise","lookup","fetchBotUser","then","botUser","resolved","set","info","githubAppBotLogin","login","githubAppBotUserId","id","trackedLookup","finally","delete","listUserInstallations","octokit","auth","userAccessToken","baseUrl","page","cursorToPage","cursor","response","mapGithubError","request","per_page","installations","data","installationIds","map","installation","nextCursor","nextCursorFromLink","link","getInstallation","installationId","getApp","rest","apps","installation_id","account","slug","type","repositorySelection","repository_selection","suspendedAt","suspended_at","Date","htmlUrl","html_url","raw","listInstallationRepositories","listReposAccessibleToInstallation","limit","repositories","toGithubRepository","getRepository","repository_id","repositoryId","listRepositoryFiles","repository","owner","ownerLogin","repo","name","startPath","prefix","replace","offset","Number","parseInt","start","isNaN","collected","overflowLimit","walk","path","depth","length","repos","getContent","ref","error","reason","Array","isArray","push","size","entries","sort","a","b","localeCompare","entry","sorted","slice","consumed","hasMore","files","String","fetchRepositoryFile","content","encoding","from","toString","createInstallationAccessToken","repository_ids","permissions","contents","token","expiresAt","expires_at","getTime","undefined","app","appId","GITHUB_APP_ID","privateKey","plugin","defaults","users","getByUsername","signal","AbortSignal","timeout","status","isSafeInteger","canonicalLogin","Map","operation","retryAfterSecondsFromHeaders","notFoundReason","isGithubTimeoutError","message","isGithubRateLimitError","retryAfterSeconds","Error","cause","parseRetryAfterSeconds","retryAfter","parsed","next","split","find","part","includes","match","default_branch","clone_url","fullName","full_name","defaultBranch","private","visibility","cloneUrl"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {isRecord, MAX_REPOSITORY_FILE_BYTES} from '@shipfox/api-integration-spi';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {App, Octokit, RequestError} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\nimport {recordInstallationTokenFormat} from '#metrics/index.js';\nimport {\n getGithubInstallationOctokit,\n githubInstallationTokenFormatPlugin,\n} from './github-octokit.js';\n\nconst NEXT_PAGE_RE = /[?&]page=(\\d+)/;\nconst TRAILING_SLASHES_RE = /\\/+$/;\nconst MAX_TREE_WALK_DEPTH = 10;\nconst GITHUB_API_TIMEOUT_MS = 10_000;\n\nexport interface GithubAccount {\n login: string;\n type: string;\n}\n\nexport interface GithubInstallationDetails {\n id: number;\n account: GithubAccount;\n repositorySelection: string;\n suspendedAt: Date | null;\n htmlUrl: string;\n raw: Record<string, unknown>;\n}\n\nexport interface GithubRepository {\n id: number;\n ownerLogin: string;\n name: string;\n fullName: string;\n defaultBranch: string;\n private: boolean;\n visibility?: string | undefined;\n cloneUrl: string;\n htmlUrl: string;\n}\n\nexport interface GithubRepositoryPage {\n repositories: GithubRepository[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileEntry {\n path: string;\n size: number | null;\n}\n\nexport interface GithubFilePage {\n files: GithubFileEntry[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileContent {\n path: string;\n content: string;\n size: number;\n}\n\nexport interface GithubCommit {\n sha: string;\n}\n\nexport interface GithubUserInstallationPage {\n installationIds: number[];\n nextCursor: string | null;\n}\n\nexport interface GithubBotUser {\n id: number;\n login: string;\n}\n\nexport interface GithubBotUserClient {\n getBotUser(input: {username: string; installationAccessToken: string}): Promise<GithubBotUser>;\n}\n\nexport interface GithubApiClient extends Partial<GithubBotUserClient> {\n exchangeOAuthCode(code: string): Promise<string>;\n listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage>;\n getInstallation(installationId: number): Promise<GithubInstallationDetails>;\n listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage>;\n getRepository(input: {installationId: number; repositoryId: number}): Promise<GithubRepository>;\n listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage>;\n fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent>;\n listRepositoryCommits(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n }): Promise<GithubCommit[]>;\n createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken>;\n}\n\nexport interface GithubInstallationAccessToken {\n token: string;\n expiresAt: Date;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n}\n\nexport function createGithubApiClient(): GithubApiClient & GithubBotUserClient {\n return new OctokitGithubApiClient();\n}\n\nclass OctokitGithubApiClient implements GithubApiClient, GithubBotUserClient {\n private app: App | undefined;\n private readonly botUsers = new Map<string, GithubBotUser>();\n private readonly botUserLookups = new Map<\n string,\n {installationAccessToken: string; promise: Promise<GithubBotUser>}\n >();\n\n async exchangeOAuthCode(code: string): Promise<string> {\n const body = await mapGithubOAuthError(() =>\n ky\n .post('https://github.com/login/oauth/access_token', {\n headers: {accept: 'application/json'},\n json: {\n client_id: config.GITHUB_APP_CLIENT_ID,\n client_secret: config.GITHUB_APP_CLIENT_SECRET,\n code,\n },\n })\n .json<{access_token?: unknown}>(),\n );\n\n if (typeof body.access_token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub OAuth response did not include an access token',\n );\n }\n return body.access_token;\n }\n\n getBotUser(input: {username: string; installationAccessToken: string}): Promise<GithubBotUser> {\n const cacheKey = input.username.trim().toLowerCase();\n const cached = this.botUsers.get(cacheKey);\n if (cached) return Promise.resolve(cached);\n\n const pending = this.botUserLookups.get(cacheKey);\n if (pending?.installationAccessToken === input.installationAccessToken) {\n return pending.promise;\n }\n\n const lookup = this.fetchBotUser(input).then((botUser) => {\n const resolved = this.botUsers.get(cacheKey);\n if (resolved) return resolved;\n\n this.botUsers.set(cacheKey, botUser);\n logger().info(\n {githubAppBotLogin: botUser.login, githubAppBotUserId: botUser.id},\n 'Resolved GitHub App bot identity',\n );\n return botUser;\n });\n const trackedLookup = lookup.finally(() => {\n if (this.botUserLookups.get(cacheKey)?.promise === trackedLookup) {\n this.botUserLookups.delete(cacheKey);\n }\n });\n this.botUserLookups.set(cacheKey, {\n installationAccessToken: input.installationAccessToken,\n promise: trackedLookup,\n });\n return trackedLookup;\n }\n\n async listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage> {\n const octokit = new Octokit({\n auth: input.userAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.request('GET /user/installations', {\n per_page: 100,\n page,\n }),\n );\n\n const installations = response.data.installations ?? [];\n return {\n installationIds: installations.map((installation) => installation.id),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getInstallation(installationId: number): Promise<GithubInstallationDetails> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.getInstallation({installation_id: installationId}),\n );\n const data = response.data;\n const account = data.account;\n if (!account) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation response did not include an account',\n );\n }\n\n const login = 'login' in account ? account.login : account.slug;\n return {\n id: data.id,\n account: {\n login,\n type: 'type' in account ? account.type : 'Enterprise',\n },\n repositorySelection: data.repository_selection,\n suspendedAt: data.suspended_at ? new Date(data.suspended_at) : null,\n htmlUrl: data.html_url,\n raw: data as unknown as Record<string, unknown>,\n };\n }\n\n async listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.rest.apps.listReposAccessibleToInstallation({\n per_page: input.limit,\n page,\n }),\n );\n\n return {\n repositories: response.data.repositories.map(toGithubRepository),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getRepository(input: {\n installationId: number;\n repositoryId: number;\n }): Promise<GithubRepository> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const response = await mapGithubError(() =>\n octokit.request('GET /repositories/{repository_id}', {\n repository_id: input.repositoryId,\n request: {signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)},\n }),\n );\n\n return toGithubRepository(response.data);\n }\n\n async listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const owner = repository.ownerLogin;\n const repo = repository.name;\n const startPath = input.prefix.replace(TRAILING_SLASHES_RE, '');\n const offset = input.cursor ? Number.parseInt(input.cursor, 10) : 0;\n const start = Number.isNaN(offset) || offset < 0 ? 0 : offset;\n const collected: GithubFileEntry[] = [];\n const overflowLimit = start + input.limit + 1;\n\n type GetContentData = Awaited<ReturnType<typeof octokit.rest.repos.getContent>>['data'];\n\n const walk = async (path: string, depth: number): Promise<void> => {\n if (collected.length >= overflowLimit) return;\n if (depth > MAX_TREE_WALK_DEPTH) return;\n\n let data: GetContentData;\n try {\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner,\n repo,\n path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n data = response.data;\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.reason === 'file-not-found') {\n return;\n }\n throw error;\n }\n\n if (!Array.isArray(data)) {\n if (data.type === 'file' && data.path) {\n collected.push({path: data.path, size: typeof data.size === 'number' ? data.size : null});\n }\n return;\n }\n\n const entries = [...data].sort((a, b) => (a.path ?? '').localeCompare(b.path ?? ''));\n for (const entry of entries) {\n if (collected.length >= overflowLimit) return;\n if (!entry.path) continue;\n if (entry.type === 'file') {\n collected.push({\n path: entry.path,\n size: typeof entry.size === 'number' ? entry.size : null,\n });\n } else if (entry.type === 'dir') {\n await walk(entry.path, depth + 1);\n }\n }\n };\n\n await walk(startPath, 0);\n\n const sorted = collected.sort((a, b) => a.path.localeCompare(b.path));\n const page = sorted.slice(start, start + input.limit);\n const consumed = start + page.length;\n const hasMore = consumed < sorted.length;\n\n return {\n files: page,\n nextCursor: hasMore ? String(consumed) : null,\n };\n }\n\n async fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner: repository.ownerLogin,\n repo: repository.name,\n path: input.path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n const data = response.data;\n\n if (Array.isArray(data) || data.type !== 'file') {\n throw new GithubIntegrationProviderError('file-not-found', 'GitHub path is not a file');\n }\n if (data.size > MAX_REPOSITORY_FILE_BYTES) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n if (typeof data.content !== 'string' || data.encoding !== 'base64') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub file response did not include base64 content',\n );\n }\n\n return {\n path: data.path,\n size: data.size,\n content: Buffer.from(data.content, 'base64').toString('utf8'),\n };\n }\n\n async listRepositoryCommits(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n }): Promise<GithubCommit[]> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.listCommits({\n owner: repository.ownerLogin,\n repo: repository.name,\n sha: input.ref,\n per_page: 1,\n request: {signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)},\n }),\n 'ref-not-found',\n );\n return response.data.map((commit) => {\n if (typeof commit.sha !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub commit response is missing the commit sha',\n );\n }\n return {sha: commit.sha};\n });\n }\n\n async createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.createInstallationAccessToken({\n installation_id: input.installationId,\n repository_ids: [input.repositoryId],\n permissions: input.permissions ?? {contents: 'read'},\n }),\n );\n\n if (typeof response.data.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a token',\n );\n }\n\n recordInstallationTokenFormat(response.data.token);\n\n const expiresAt = new Date(response.data.expires_at);\n if (Number.isNaN(expiresAt.getTime())) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a valid expiry',\n );\n }\n\n return {\n token: response.data.token,\n expiresAt,\n ...(response.data.permissions === undefined ? {} : {permissions: response.data.permissions}),\n };\n }\n\n private getApp(): App {\n if (!this.app) {\n this.app = new App({\n appId: config.GITHUB_APP_ID,\n privateKey: normalizedGithubPrivateKey(),\n Octokit: Octokit.plugin(githubInstallationTokenFormatPlugin).defaults({\n baseUrl: normalizedGithubApiBaseUrl(),\n }),\n });\n }\n return this.app;\n }\n\n private async fetchBotUser(input: {\n username: string;\n installationAccessToken: string;\n }): Promise<GithubBotUser> {\n const octokit = new Octokit({\n auth: input.installationAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n let response: Awaited<ReturnType<typeof octokit.rest.users.getByUsername>>;\n try {\n response = await mapGithubError(\n () =>\n octokit.rest.users.getByUsername({\n username: input.username,\n request: {signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)},\n }),\n 'provider-rejected',\n );\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.status === 404) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n `Configured GitHub bot user ${input.username} was not found`,\n undefined,\n error.status,\n );\n }\n throw error;\n }\n\n const data: unknown = response.data;\n if (!isRecord(data)) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response is missing required fields',\n );\n }\n const {id, login, type} = data;\n if (\n typeof id !== 'number' ||\n !Number.isSafeInteger(id) ||\n id <= 0 ||\n typeof login !== 'string' ||\n login.trim().length === 0\n ) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response is missing required fields',\n );\n }\n if (type !== 'Bot') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'Configured GitHub username is not a bot account',\n );\n }\n const canonicalLogin = login.trim();\n if (canonicalLogin.toLowerCase() !== input.username.trim().toLowerCase()) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response did not match the configured username',\n );\n }\n return {id, login: canonicalLogin};\n }\n}\n\nasync function mapGithubOAuthError<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof HTTPError) {\n if (error.response.status === 429) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n 'GitHub OAuth code exchange failed',\n retryAfterSecondsFromHeaders(error.response.headers),\n );\n }\n if (error.response.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n 'GitHub OAuth code exchange failed',\n );\n }\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub OAuth code exchange failed',\n );\n }\n if (error instanceof TimeoutError) {\n throw new GithubIntegrationProviderError('timeout', 'GitHub OAuth request timed out');\n }\n throw error;\n }\n}\n\nexport async function mapGithubError<T>(\n operation: () => Promise<T>,\n notFoundReason:\n | 'repository-not-found'\n | 'installation-not-found'\n | 'file-not-found'\n | 'ref-not-found'\n | 'provider-rejected' = 'repository-not-found',\n): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError) throw error;\n if (isGithubTimeoutError(error)) {\n throw new GithubIntegrationProviderError('timeout', 'GitHub request timed out');\n }\n if (error instanceof RequestError) {\n if (error.status === 404) {\n throw new GithubIntegrationProviderError(\n notFoundReason === 'ref-not-found' ? 'repository-not-found' : notFoundReason,\n error.message,\n undefined,\n error.status,\n );\n }\n if (notFoundReason === 'ref-not-found' && (error.status === 409 || error.status === 422)) {\n throw new GithubIntegrationProviderError(\n 'ref-not-found',\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status === 429 || isGithubRateLimitError(error)) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status === 401 || error.status === 403) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status >= 400) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n error.message,\n undefined,\n error.status,\n );\n }\n }\n throw error;\n }\n}\n\nfunction isGithubTimeoutError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n if (error.name === 'AbortError' || error.name === 'TimeoutError') return true;\n return error.cause instanceof Error && isGithubTimeoutError(error.cause);\n}\n\nfunction isGithubRateLimitError(error: RequestError): boolean {\n return error.status === 403 && error.response?.headers['x-ratelimit-remaining'] === '0';\n}\n\nfunction retryAfterSeconds(error: RequestError): number | undefined {\n return parseRetryAfterSeconds(error.response?.headers['retry-after']);\n}\n\nfunction retryAfterSecondsFromHeaders(headers: Headers): number | undefined {\n return parseRetryAfterSeconds(headers.get('retry-after'));\n}\n\nfunction parseRetryAfterSeconds(\n retryAfter: string | number | null | undefined,\n): number | undefined {\n if (!retryAfter) return undefined;\n const parsed = Number.parseInt(String(retryAfter), 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction cursorToPage(cursor: string | undefined): number {\n if (!cursor) return 1;\n const page = Number.parseInt(cursor, 10);\n return Number.isNaN(page) || page < 1 ? 1 : page;\n}\n\nfunction nextCursorFromLink(link: string | undefined): string | null {\n if (!link) return null;\n const next = link.split(',').find((part) => part.includes('rel=\"next\"'));\n if (!next) return null;\n const match = next.match(NEXT_PAGE_RE);\n return match?.[1] ?? null;\n}\n\nfunction toGithubRepository(repository: {\n id: number;\n owner: {login: string};\n name: string;\n full_name: string;\n default_branch?: string | null | undefined;\n private: boolean;\n visibility?: string | undefined;\n clone_url?: string | null | undefined;\n html_url?: string | null | undefined;\n}): GithubRepository {\n if (!repository.default_branch || !repository.clone_url || !repository.html_url) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub repository response is missing required fields',\n );\n }\n return {\n id: repository.id,\n ownerLogin: repository.owner.login,\n name: repository.name,\n fullName: repository.full_name,\n defaultBranch: repository.default_branch,\n private: repository.private,\n visibility: repository.visibility,\n cloneUrl: repository.clone_url,\n htmlUrl: repository.html_url,\n };\n}\n"],"names":["Buffer","isRecord","MAX_REPOSITORY_FILE_BYTES","logger","ky","HTTPError","TimeoutError","App","Octokit","RequestError","config","normalizedGithubApiBaseUrl","normalizedGithubPrivateKey","GithubIntegrationProviderError","recordInstallationTokenFormat","getGithubInstallationOctokit","githubInstallationTokenFormatPlugin","NEXT_PAGE_RE","TRAILING_SLASHES_RE","MAX_TREE_WALK_DEPTH","GITHUB_API_TIMEOUT_MS","createGithubApiClient","OctokitGithubApiClient","exchangeOAuthCode","code","body","mapGithubOAuthError","post","headers","accept","json","client_id","GITHUB_APP_CLIENT_ID","client_secret","GITHUB_APP_CLIENT_SECRET","access_token","getBotUser","input","cacheKey","username","trim","toLowerCase","cached","botUsers","get","Promise","resolve","pending","botUserLookups","installationAccessToken","promise","lookup","fetchBotUser","then","botUser","resolved","set","info","githubAppBotLogin","login","githubAppBotUserId","id","trackedLookup","finally","delete","listUserInstallations","octokit","auth","userAccessToken","baseUrl","page","cursorToPage","cursor","response","mapGithubError","request","per_page","installations","data","installationIds","map","installation","nextCursor","nextCursorFromLink","link","getInstallation","installationId","getApp","rest","apps","installation_id","account","slug","type","repositorySelection","repository_selection","suspendedAt","suspended_at","Date","htmlUrl","html_url","raw","listInstallationRepositories","listReposAccessibleToInstallation","limit","repositories","toGithubRepository","getRepository","repository_id","repositoryId","signal","AbortSignal","timeout","listRepositoryFiles","repository","owner","ownerLogin","repo","name","startPath","prefix","replace","offset","Number","parseInt","start","isNaN","collected","overflowLimit","walk","path","depth","length","repos","getContent","ref","error","reason","Array","isArray","push","size","entries","sort","a","b","localeCompare","entry","sorted","slice","consumed","hasMore","files","String","fetchRepositoryFile","content","encoding","from","toString","listRepositoryCommits","listCommits","sha","commit","createInstallationAccessToken","repository_ids","permissions","contents","token","expiresAt","expires_at","getTime","undefined","app","appId","GITHUB_APP_ID","privateKey","plugin","defaults","users","getByUsername","status","isSafeInteger","canonicalLogin","Map","operation","retryAfterSecondsFromHeaders","notFoundReason","isGithubTimeoutError","message","isGithubRateLimitError","retryAfterSeconds","Error","cause","parseRetryAfterSeconds","retryAfter","parsed","next","split","find","part","includes","match","default_branch","clone_url","fullName","full_name","defaultBranch","private","visibility","cloneUrl"],"mappings":"AAAA,SAAQA,MAAM,QAAO,cAAc;AACnC,SAAQC,QAAQ,EAAEC,yBAAyB,QAAO,+BAA+B;AACjF,SAAQC,MAAM,QAAO,8BAA8B;AACnD,OAAOC,MAAKC,SAAS,EAAEC,YAAY,QAAO,KAAK;AAC/C,SAAQC,GAAG,EAAEC,OAAO,EAAEC,YAAY,QAAO,UAAU;AACnD,SAAQC,MAAM,EAAEC,0BAA0B,EAAEC,0BAA0B,QAAO,aAAa;AAC1F,SAAQC,8BAA8B,QAAO,kBAAkB;AAC/D,SAAQC,6BAA6B,QAAO,oBAAoB;AAChE,SACEC,4BAA4B,EAC5BC,mCAAmC,QAC9B,sBAAsB;AAE7B,MAAMC,eAAe;AACrB,MAAMC,sBAAsB;AAC5B,MAAMC,sBAAsB;AAC5B,MAAMC,wBAAwB;AAgH9B,OAAO,SAASC;IACd,OAAO,IAAIC;AACb;AAEA,IAAA,AAAMA,yBAAN,MAAMA;IAQJ,MAAMC,kBAAkBC,IAAY,EAAmB;QACrD,MAAMC,OAAO,MAAMC,oBAAoB,IACrCtB,GACGuB,IAAI,CAAC,+CAA+C;gBACnDC,SAAS;oBAACC,QAAQ;gBAAkB;gBACpCC,MAAM;oBACJC,WAAWrB,OAAOsB,oBAAoB;oBACtCC,eAAevB,OAAOwB,wBAAwB;oBAC9CV;gBACF;YACF,GACCM,IAAI;QAGT,IAAI,OAAOL,KAAKU,YAAY,KAAK,UAAU;YACzC,MAAM,IAAItB,+BACR,+BACA;QAEJ;QACA,OAAOY,KAAKU,YAAY;IAC1B;IAEAC,WAAWC,KAA0D,EAA0B;QAC7F,MAAMC,WAAWD,MAAME,QAAQ,CAACC,IAAI,GAAGC,WAAW;QAClD,MAAMC,SAAS,IAAI,CAACC,QAAQ,CAACC,GAAG,CAACN;QACjC,IAAII,QAAQ,OAAOG,QAAQC,OAAO,CAACJ;QAEnC,MAAMK,UAAU,IAAI,CAACC,cAAc,CAACJ,GAAG,CAACN;QACxC,IAAIS,SAASE,4BAA4BZ,MAAMY,uBAAuB,EAAE;YACtE,OAAOF,QAAQG,OAAO;QACxB;QAEA,MAAMC,SAAS,IAAI,CAACC,YAAY,CAACf,OAAOgB,IAAI,CAAC,CAACC;YAC5C,MAAMC,WAAW,IAAI,CAACZ,QAAQ,CAACC,GAAG,CAACN;YACnC,IAAIiB,UAAU,OAAOA;YAErB,IAAI,CAACZ,QAAQ,CAACa,GAAG,CAAClB,UAAUgB;YAC5BnD,SAASsD,IAAI,CACX;gBAACC,mBAAmBJ,QAAQK,KAAK;gBAAEC,oBAAoBN,QAAQO,EAAE;YAAA,GACjE;YAEF,OAAOP;QACT;QACA,MAAMQ,gBAAgBX,OAAOY,OAAO,CAAC;YACnC,IAAI,IAAI,CAACf,cAAc,CAACJ,GAAG,CAACN,WAAWY,YAAYY,eAAe;gBAChE,IAAI,CAACd,cAAc,CAACgB,MAAM,CAAC1B;YAC7B;QACF;QACA,IAAI,CAACU,cAAc,CAACQ,GAAG,CAAClB,UAAU;YAChCW,yBAAyBZ,MAAMY,uBAAuB;YACtDC,SAASY;QACX;QACA,OAAOA;IACT;IAEA,MAAMG,sBAAsB5B,KAG3B,EAAuC;QACtC,MAAM6B,UAAU,IAAI1D,QAAQ;YAC1B2D,MAAM9B,MAAM+B,eAAe;YAC3BC,SAAS1D;QACX;QACA,MAAM2D,OAAOC,aAAalC,MAAMmC,MAAM;QACtC,MAAMC,WAAW,MAAMC,eAAe,IACpCR,QAAQS,OAAO,CAAC,2BAA2B;gBACzCC,UAAU;gBACVN;YACF;QAGF,MAAMO,gBAAgBJ,SAASK,IAAI,CAACD,aAAa,IAAI,EAAE;QACvD,OAAO;YACLE,iBAAiBF,cAAcG,GAAG,CAAC,CAACC,eAAiBA,aAAapB,EAAE;YACpEqB,YAAYC,mBAAmBV,SAAS7C,OAAO,CAACwD,IAAI;QACtD;IACF;IAEA,MAAMC,gBAAgBC,cAAsB,EAAsC;QAChF,MAAMb,WAAW,MAAMC,eAAe,IACpC,IAAI,CAACa,MAAM,GAAGrB,OAAO,CAACsB,IAAI,CAACC,IAAI,CAACJ,eAAe,CAAC;gBAACK,iBAAiBJ;YAAc;QAElF,MAAMR,OAAOL,SAASK,IAAI;QAC1B,MAAMa,UAAUb,KAAKa,OAAO;QAC5B,IAAI,CAACA,SAAS;YACZ,MAAM,IAAI9E,+BACR,+BACA;QAEJ;QAEA,MAAM8C,QAAQ,WAAWgC,UAAUA,QAAQhC,KAAK,GAAGgC,QAAQC,IAAI;QAC/D,OAAO;YACL/B,IAAIiB,KAAKjB,EAAE;YACX8B,SAAS;gBACPhC;gBACAkC,MAAM,UAAUF,UAAUA,QAAQE,IAAI,GAAG;YAC3C;YACAC,qBAAqBhB,KAAKiB,oBAAoB;YAC9CC,aAAalB,KAAKmB,YAAY,GAAG,IAAIC,KAAKpB,KAAKmB,YAAY,IAAI;YAC/DE,SAASrB,KAAKsB,QAAQ;YACtBC,KAAKvB;QACP;IACF;IAEA,MAAMwB,6BAA6BjE,KAIlC,EAAiC;QAChC,MAAM6B,UAAU,MAAMQ,eAAe,IACnC3D,6BAA6B,IAAI,CAACwE,MAAM,IAAIlD,MAAMiD,cAAc;QAElE,MAAMhB,OAAOC,aAAalC,MAAMmC,MAAM;QACtC,MAAMC,WAAW,MAAMC,eAAe,IACpCR,QAAQsB,IAAI,CAACC,IAAI,CAACc,iCAAiC,CAAC;gBAClD3B,UAAUvC,MAAMmE,KAAK;gBACrBlC;YACF;QAGF,OAAO;YACLmC,cAAchC,SAASK,IAAI,CAAC2B,YAAY,CAACzB,GAAG,CAAC0B;YAC7CxB,YAAYC,mBAAmBV,SAAS7C,OAAO,CAACwD,IAAI;QACtD;IACF;IAEA,MAAMuB,cAActE,KAGnB,EAA6B;QAC5B,MAAM6B,UAAU,MAAMQ,eAAe,IACnC3D,6BAA6B,IAAI,CAACwE,MAAM,IAAIlD,MAAMiD,cAAc;QAElE,MAAMb,WAAW,MAAMC,eAAe,IACpCR,QAAQS,OAAO,CAAC,qCAAqC;gBACnDiC,eAAevE,MAAMwE,YAAY;gBACjClC,SAAS;oBAACmC,QAAQC,YAAYC,OAAO,CAAC5F;gBAAsB;YAC9D;QAGF,OAAOsF,mBAAmBjC,SAASK,IAAI;IACzC;IAEA,MAAMmC,oBAAoB5E,KAOzB,EAA2B;QAC1B,MAAM6B,UAAU,MAAMQ,eAAe,IACnC3D,6BAA6B,IAAI,CAACwE,MAAM,IAAIlD,MAAMiD,cAAc;QAElE,MAAM4B,aAAa,MAAM,IAAI,CAACP,aAAa,CAAC;YAC1CrB,gBAAgBjD,MAAMiD,cAAc;YACpCuB,cAAcxE,MAAMwE,YAAY;QAClC;QACA,MAAMM,QAAQD,WAAWE,UAAU;QACnC,MAAMC,OAAOH,WAAWI,IAAI;QAC5B,MAAMC,YAAYlF,MAAMmF,MAAM,CAACC,OAAO,CAACvG,qBAAqB;QAC5D,MAAMwG,SAASrF,MAAMmC,MAAM,GAAGmD,OAAOC,QAAQ,CAACvF,MAAMmC,MAAM,EAAE,MAAM;QAClE,MAAMqD,QAAQF,OAAOG,KAAK,CAACJ,WAAWA,SAAS,IAAI,IAAIA;QACvD,MAAMK,YAA+B,EAAE;QACvC,MAAMC,gBAAgBH,QAAQxF,MAAMmE,KAAK,GAAG;QAI5C,MAAMyB,OAAO,OAAOC,MAAcC;YAChC,IAAIJ,UAAUK,MAAM,IAAIJ,eAAe;YACvC,IAAIG,QAAQhH,qBAAqB;YAEjC,IAAI2D;YACJ,IAAI;gBACF,MAAML,WAAW,MAAMC,eACrB,IACER,QAAQsB,IAAI,CAAC6C,KAAK,CAACC,UAAU,CAAC;wBAC5BnB;wBACAE;wBACAa;wBACAK,KAAKlG,MAAMkG,GAAG;oBAChB,IACF;gBAEFzD,OAAOL,SAASK,IAAI;YACtB,EAAE,OAAO0D,OAAO;gBACd,IAAIA,iBAAiB3H,kCAAkC2H,MAAMC,MAAM,KAAK,kBAAkB;oBACxF;gBACF;gBACA,MAAMD;YACR;YAEA,IAAI,CAACE,MAAMC,OAAO,CAAC7D,OAAO;gBACxB,IAAIA,KAAKe,IAAI,KAAK,UAAUf,KAAKoD,IAAI,EAAE;oBACrCH,UAAUa,IAAI,CAAC;wBAACV,MAAMpD,KAAKoD,IAAI;wBAAEW,MAAM,OAAO/D,KAAK+D,IAAI,KAAK,WAAW/D,KAAK+D,IAAI,GAAG;oBAAI;gBACzF;gBACA;YACF;YAEA,MAAMC,UAAU;mBAAIhE;aAAK,CAACiE,IAAI,CAAC,CAACC,GAAGC,IAAM,AAACD,CAAAA,EAAEd,IAAI,IAAI,EAAC,EAAGgB,aAAa,CAACD,EAAEf,IAAI,IAAI;YAChF,KAAK,MAAMiB,SAASL,QAAS;gBAC3B,IAAIf,UAAUK,MAAM,IAAIJ,eAAe;gBACvC,IAAI,CAACmB,MAAMjB,IAAI,EAAE;gBACjB,IAAIiB,MAAMtD,IAAI,KAAK,QAAQ;oBACzBkC,UAAUa,IAAI,CAAC;wBACbV,MAAMiB,MAAMjB,IAAI;wBAChBW,MAAM,OAAOM,MAAMN,IAAI,KAAK,WAAWM,MAAMN,IAAI,GAAG;oBACtD;gBACF,OAAO,IAAIM,MAAMtD,IAAI,KAAK,OAAO;oBAC/B,MAAMoC,KAAKkB,MAAMjB,IAAI,EAAEC,QAAQ;gBACjC;YACF;QACF;QAEA,MAAMF,KAAKV,WAAW;QAEtB,MAAM6B,SAASrB,UAAUgB,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEd,IAAI,CAACgB,aAAa,CAACD,EAAEf,IAAI;QACnE,MAAM5D,OAAO8E,OAAOC,KAAK,CAACxB,OAAOA,QAAQxF,MAAMmE,KAAK;QACpD,MAAM8C,WAAWzB,QAAQvD,KAAK8D,MAAM;QACpC,MAAMmB,UAAUD,WAAWF,OAAOhB,MAAM;QAExC,OAAO;YACLoB,OAAOlF;YACPY,YAAYqE,UAAUE,OAAOH,YAAY;QAC3C;IACF;IAEA,MAAMI,oBAAoBrH,KAKzB,EAA8B;QAC7B,MAAM6B,UAAU,MAAMQ,eAAe,IACnC3D,6BAA6B,IAAI,CAACwE,MAAM,IAAIlD,MAAMiD,cAAc;QAElE,MAAM4B,aAAa,MAAM,IAAI,CAACP,aAAa,CAAC;YAC1CrB,gBAAgBjD,MAAMiD,cAAc;YACpCuB,cAAcxE,MAAMwE,YAAY;QAClC;QACA,MAAMpC,WAAW,MAAMC,eACrB,IACER,QAAQsB,IAAI,CAAC6C,KAAK,CAACC,UAAU,CAAC;gBAC5BnB,OAAOD,WAAWE,UAAU;gBAC5BC,MAAMH,WAAWI,IAAI;gBACrBY,MAAM7F,MAAM6F,IAAI;gBAChBK,KAAKlG,MAAMkG,GAAG;YAChB,IACF;QAEF,MAAMzD,OAAOL,SAASK,IAAI;QAE1B,IAAI4D,MAAMC,OAAO,CAAC7D,SAASA,KAAKe,IAAI,KAAK,QAAQ;YAC/C,MAAM,IAAIhF,+BAA+B,kBAAkB;QAC7D;QACA,IAAIiE,KAAK+D,IAAI,GAAG3I,2BAA2B;YACzC,MAAM,IAAIW,+BACR,qBACA;QAEJ;QACA,IAAI,OAAOiE,KAAK6E,OAAO,KAAK,YAAY7E,KAAK8E,QAAQ,KAAK,UAAU;YAClE,MAAM,IAAI/I,+BACR,+BACA;QAEJ;QAEA,OAAO;YACLqH,MAAMpD,KAAKoD,IAAI;YACfW,MAAM/D,KAAK+D,IAAI;YACfc,SAAS3J,OAAO6J,IAAI,CAAC/E,KAAK6E,OAAO,EAAE,UAAUG,QAAQ,CAAC;QACxD;IACF;IAEA,MAAMC,sBAAsB1H,KAI3B,EAA2B;QAC1B,MAAM6B,UAAU,MAAMQ,eAAe,IACnC3D,6BAA6B,IAAI,CAACwE,MAAM,IAAIlD,MAAMiD,cAAc;QAElE,MAAM4B,aAAa,MAAM,IAAI,CAACP,aAAa,CAAC;YAC1CrB,gBAAgBjD,MAAMiD,cAAc;YACpCuB,cAAcxE,MAAMwE,YAAY;QAClC;QACA,MAAMpC,WAAW,MAAMC,eACrB,IACER,QAAQsB,IAAI,CAAC6C,KAAK,CAAC2B,WAAW,CAAC;gBAC7B7C,OAAOD,WAAWE,UAAU;gBAC5BC,MAAMH,WAAWI,IAAI;gBACrB2C,KAAK5H,MAAMkG,GAAG;gBACd3D,UAAU;gBACVD,SAAS;oBAACmC,QAAQC,YAAYC,OAAO,CAAC5F;gBAAsB;YAC9D,IACF;QAEF,OAAOqD,SAASK,IAAI,CAACE,GAAG,CAAC,CAACkF;YACxB,IAAI,OAAOA,OAAOD,GAAG,KAAK,UAAU;gBAClC,MAAM,IAAIpJ,+BACR,+BACA;YAEJ;YACA,OAAO;gBAACoJ,KAAKC,OAAOD,GAAG;YAAA;QACzB;IACF;IAEA,MAAME,8BAA8B9H,KAInC,EAA0C;QACzC,MAAMoC,WAAW,MAAMC,eAAe,IACpC,IAAI,CAACa,MAAM,GAAGrB,OAAO,CAACsB,IAAI,CAACC,IAAI,CAAC0E,6BAA6B,CAAC;gBAC5DzE,iBAAiBrD,MAAMiD,cAAc;gBACrC8E,gBAAgB;oBAAC/H,MAAMwE,YAAY;iBAAC;gBACpCwD,aAAahI,MAAMgI,WAAW,IAAI;oBAACC,UAAU;gBAAM;YACrD;QAGF,IAAI,OAAO7F,SAASK,IAAI,CAACyF,KAAK,KAAK,UAAU;YAC3C,MAAM,IAAI1J,+BACR,+BACA;QAEJ;QAEAC,8BAA8B2D,SAASK,IAAI,CAACyF,KAAK;QAEjD,MAAMC,YAAY,IAAItE,KAAKzB,SAASK,IAAI,CAAC2F,UAAU;QACnD,IAAI9C,OAAOG,KAAK,CAAC0C,UAAUE,OAAO,KAAK;YACrC,MAAM,IAAI7J,+BACR,+BACA;QAEJ;QAEA,OAAO;YACL0J,OAAO9F,SAASK,IAAI,CAACyF,KAAK;YAC1BC;YACA,GAAI/F,SAASK,IAAI,CAACuF,WAAW,KAAKM,YAAY,CAAC,IAAI;gBAACN,aAAa5F,SAASK,IAAI,CAACuF,WAAW;YAAA,CAAC;QAC7F;IACF;IAEQ9E,SAAc;QACpB,IAAI,CAAC,IAAI,CAACqF,GAAG,EAAE;YACb,IAAI,CAACA,GAAG,GAAG,IAAIrK,IAAI;gBACjBsK,OAAOnK,OAAOoK,aAAa;gBAC3BC,YAAYnK;gBACZJ,SAASA,QAAQwK,MAAM,CAAChK,qCAAqCiK,QAAQ,CAAC;oBACpE5G,SAAS1D;gBACX;YACF;QACF;QACA,OAAO,IAAI,CAACiK,GAAG;IACjB;IAEA,MAAcxH,aAAaf,KAG1B,EAA0B;QACzB,MAAM6B,UAAU,IAAI1D,QAAQ;YAC1B2D,MAAM9B,MAAMY,uBAAuB;YACnCoB,SAAS1D;QACX;QACA,IAAI8D;QACJ,IAAI;YACFA,WAAW,MAAMC,eACf,IACER,QAAQsB,IAAI,CAAC0F,KAAK,CAACC,aAAa,CAAC;oBAC/B5I,UAAUF,MAAME,QAAQ;oBACxBoC,SAAS;wBAACmC,QAAQC,YAAYC,OAAO,CAAC5F;oBAAsB;gBAC9D,IACF;QAEJ,EAAE,OAAOoH,OAAO;YACd,IAAIA,iBAAiB3H,kCAAkC2H,MAAM4C,MAAM,KAAK,KAAK;gBAC3E,MAAM,IAAIvK,+BACR,qBACA,CAAC,2BAA2B,EAAEwB,MAAME,QAAQ,CAAC,cAAc,CAAC,EAC5DoI,WACAnC,MAAM4C,MAAM;YAEhB;YACA,MAAM5C;QACR;QAEA,MAAM1D,OAAgBL,SAASK,IAAI;QACnC,IAAI,CAAC7E,SAAS6E,OAAO;YACnB,MAAM,IAAIjE,+BACR,+BACA;QAEJ;QACA,MAAM,EAACgD,EAAE,EAAEF,KAAK,EAAEkC,IAAI,EAAC,GAAGf;QAC1B,IACE,OAAOjB,OAAO,YACd,CAAC8D,OAAO0D,aAAa,CAACxH,OACtBA,MAAM,KACN,OAAOF,UAAU,YACjBA,MAAMnB,IAAI,GAAG4F,MAAM,KAAK,GACxB;YACA,MAAM,IAAIvH,+BACR,+BACA;QAEJ;QACA,IAAIgF,SAAS,OAAO;YAClB,MAAM,IAAIhF,+BACR,+BACA;QAEJ;QACA,MAAMyK,iBAAiB3H,MAAMnB,IAAI;QACjC,IAAI8I,eAAe7I,WAAW,OAAOJ,MAAME,QAAQ,CAACC,IAAI,GAAGC,WAAW,IAAI;YACxE,MAAM,IAAI5B,+BACR,+BACA;QAEJ;QACA,OAAO;YAACgD;YAAIF,OAAO2H;QAAc;IACnC;;aA/aiB3I,WAAW,IAAI4I;aACfvI,iBAAiB,IAAIuI;;AA+axC;AAEA,eAAe7J,oBAAuB8J,SAA2B;IAC/D,IAAI;QACF,OAAO,MAAMA;IACf,EAAE,OAAOhD,OAAO;QACd,IAAIA,iBAAiBnI,WAAW;YAC9B,IAAImI,MAAM/D,QAAQ,CAAC2G,MAAM,KAAK,KAAK;gBACjC,MAAM,IAAIvK,+BACR,gBACA,qCACA4K,6BAA6BjD,MAAM/D,QAAQ,CAAC7C,OAAO;YAEvD;YACA,IAAI4G,MAAM/D,QAAQ,CAAC2G,MAAM,IAAI,KAAK;gBAChC,MAAM,IAAIvK,+BACR,wBACA;YAEJ;YACA,MAAM,IAAIA,+BACR,iBACA;QAEJ;QACA,IAAI2H,iBAAiBlI,cAAc;YACjC,MAAM,IAAIO,+BAA+B,WAAW;QACtD;QACA,MAAM2H;IACR;AACF;AAEA,OAAO,eAAe9D,eACpB8G,SAA2B,EAC3BE,iBAK0B,sBAAsB;IAEhD,IAAI;QACF,OAAO,MAAMF;IACf,EAAE,OAAOhD,OAAO;QACd,IAAIA,iBAAiB3H,gCAAgC,MAAM2H;QAC3D,IAAImD,qBAAqBnD,QAAQ;YAC/B,MAAM,IAAI3H,+BAA+B,WAAW;QACtD;QACA,IAAI2H,iBAAiB/H,cAAc;YACjC,IAAI+H,MAAM4C,MAAM,KAAK,KAAK;gBACxB,MAAM,IAAIvK,+BACR6K,mBAAmB,kBAAkB,yBAAyBA,gBAC9DlD,MAAMoD,OAAO,EACbjB,WACAnC,MAAM4C,MAAM;YAEhB;YACA,IAAIM,mBAAmB,mBAAoBlD,CAAAA,MAAM4C,MAAM,KAAK,OAAO5C,MAAM4C,MAAM,KAAK,GAAE,GAAI;gBACxF,MAAM,IAAIvK,+BACR,iBACA2H,MAAMoD,OAAO,EACbjB,WACAnC,MAAM4C,MAAM;YAEhB;YACA,IAAI5C,MAAM4C,MAAM,KAAK,OAAOS,uBAAuBrD,QAAQ;gBACzD,MAAM,IAAI3H,+BACR,gBACA2H,MAAMoD,OAAO,EACbE,kBAAkBtD,QAClBA,MAAM4C,MAAM;YAEhB;YACA,IAAI5C,MAAM4C,MAAM,KAAK,OAAO5C,MAAM4C,MAAM,KAAK,KAAK;gBAChD,MAAM,IAAIvK,+BACR,iBACA2H,MAAMoD,OAAO,EACbE,kBAAkBtD,QAClBA,MAAM4C,MAAM;YAEhB;YACA,IAAI5C,MAAM4C,MAAM,IAAI,KAAK;gBACvB,MAAM,IAAIvK,+BACR,wBACA2H,MAAMoD,OAAO,EACbjB,WACAnC,MAAM4C,MAAM;YAEhB;YACA,IAAI5C,MAAM4C,MAAM,IAAI,KAAK;gBACvB,MAAM,IAAIvK,+BACR,qBACA2H,MAAMoD,OAAO,EACbjB,WACAnC,MAAM4C,MAAM;YAEhB;QACF;QACA,MAAM5C;IACR;AACF;AAEA,SAASmD,qBAAqBnD,KAAc;IAC1C,IAAI,CAAEA,CAAAA,iBAAiBuD,KAAI,GAAI,OAAO;IACtC,IAAIvD,MAAMlB,IAAI,KAAK,gBAAgBkB,MAAMlB,IAAI,KAAK,gBAAgB,OAAO;IACzE,OAAOkB,MAAMwD,KAAK,YAAYD,SAASJ,qBAAqBnD,MAAMwD,KAAK;AACzE;AAEA,SAASH,uBAAuBrD,KAAmB;IACjD,OAAOA,MAAM4C,MAAM,KAAK,OAAO5C,MAAM/D,QAAQ,EAAE7C,OAAO,CAAC,wBAAwB,KAAK;AACtF;AAEA,SAASkK,kBAAkBtD,KAAmB;IAC5C,OAAOyD,uBAAuBzD,MAAM/D,QAAQ,EAAE7C,OAAO,CAAC,cAAc;AACtE;AAEA,SAAS6J,6BAA6B7J,OAAgB;IACpD,OAAOqK,uBAAuBrK,QAAQgB,GAAG,CAAC;AAC5C;AAEA,SAASqJ,uBACPC,UAA8C;IAE9C,IAAI,CAACA,YAAY,OAAOvB;IACxB,MAAMwB,SAASxE,OAAOC,QAAQ,CAAC6B,OAAOyC,aAAa;IACnD,OAAOvE,OAAOG,KAAK,CAACqE,UAAUxB,YAAYwB;AAC5C;AAEA,SAAS5H,aAAaC,MAA0B;IAC9C,IAAI,CAACA,QAAQ,OAAO;IACpB,MAAMF,OAAOqD,OAAOC,QAAQ,CAACpD,QAAQ;IACrC,OAAOmD,OAAOG,KAAK,CAACxD,SAASA,OAAO,IAAI,IAAIA;AAC9C;AAEA,SAASa,mBAAmBC,IAAwB;IAClD,IAAI,CAACA,MAAM,OAAO;IAClB,MAAMgH,OAAOhH,KAAKiH,KAAK,CAAC,KAAKC,IAAI,CAAC,CAACC,OAASA,KAAKC,QAAQ,CAAC;IAC1D,IAAI,CAACJ,MAAM,OAAO;IAClB,MAAMK,QAAQL,KAAKK,KAAK,CAACxL;IACzB,OAAOwL,OAAO,CAAC,EAAE,IAAI;AACvB;AAEA,SAAS/F,mBAAmBQ,UAU3B;IACC,IAAI,CAACA,WAAWwF,cAAc,IAAI,CAACxF,WAAWyF,SAAS,IAAI,CAACzF,WAAWd,QAAQ,EAAE;QAC/E,MAAM,IAAIvF,+BACR,+BACA;IAEJ;IACA,OAAO;QACLgD,IAAIqD,WAAWrD,EAAE;QACjBuD,YAAYF,WAAWC,KAAK,CAACxD,KAAK;QAClC2D,MAAMJ,WAAWI,IAAI;QACrBsF,UAAU1F,WAAW2F,SAAS;QAC9BC,eAAe5F,WAAWwF,cAAc;QACxCK,SAAS7F,WAAW6F,OAAO;QAC3BC,YAAY9F,WAAW8F,UAAU;QACjCC,UAAU/F,WAAWyF,SAAS;QAC9BxG,SAASe,WAAWd,QAAQ;IAC9B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installation-token-envelope.d.ts","sourceRoot":"","sources":["../../src/api/installation-token-envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAC,8BAA8B,EAAC,MAAM,iBAAiB,CAAC;AAE/D,eAAO,MAAM,uBAAuB,QAAgB,CAAC;AACrD,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAClD,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAClD,eAAO,MAAM,wBAAwB,QAAgB,CAAC;AACtD,eAAO,MAAM,mBAAmB,QAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"installation-token-envelope.d.ts","sourceRoot":"","sources":["../../src/api/installation-token-envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAC,8BAA8B,EAAC,MAAM,iBAAiB,CAAC;AAE/D,eAAO,MAAM,uBAAuB,QAAgB,CAAC;AACrD,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAClD,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAClD,eAAO,MAAM,wBAAwB,QAAgB,CAAC;AACtD,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AA8ClD,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,GAAG,SAAS,CAAC;IACrE,YAAY,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAChC,aAAa,CAAC,EAAE,8BAA8B,GAAG,SAAS,CAAC;IAC3D,YAAY,CAAC,EACT;QACE,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,GACD,SAAS,CAAC;CACf;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,8BAA8B,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,wBAAgB,gCAAgC,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,CAW3F;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,GAAG,SAAS,CAmBjG;AAED,wBAAgB,MAAM,CACpB,QAAQ,EAAE,yBAAyB,GAAG,SAAS,EAC/C,GAAG,EAAE,IAAI,GACR,QAAQ,IAAI,yBAAyB,GAAG;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAC,CAM1E;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAEhE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,IAAI,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAE1E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,yBAAyB,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAMjG;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAUrE;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,8BAA8B,GAAG,cAAc,CAE9F;AAED,wBAAgB,SAAS,CAAC,UAAU,EAAE,mBAAmB,GAAG,MAAM,CAKjE;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,8BAA8B,EACtC,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,yBAAyB,CAAC,cAAc,CAAC,GACvD,8BAA8B,CAOhC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAc9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/installation-token-envelope.ts"],"sourcesContent":["import {\n IntegrationProviderError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-spi';\nimport {z} from 'zod';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\n\nexport const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;\nexport const TOKEN_VALIDITY_BUFFER_MS = 60 * 1000;\nexport const TRANSIENT_BACKOFF_MIN_MS = 30 * 1000;\nexport const TRANSIENT_BACKOFF_MAX_MS = 5 * 60 * 1000;\nexport const TERMINAL_BACKOFF_MS = 15 * 60 * 1000;\n\nconst providerErrorReasons = [\n 'repository-not-found',\n 'installation-not-found',\n 'file-not-found',\n 'access-denied',\n 'rate-limited',\n 'timeout',\n 'provider-unavailable',\n 'provider-rejected',\n 'malformed-provider-response',\n 'content-too-large',\n 'too-many-files',\n] as const satisfies readonly IntegrationProviderErrorReason[];\n\nconst providerErrorReasonSchema = z.enum(providerErrorReasons);\nconst backoffErrorSchema = z.object({\n message: z.string(),\n status: z.number().int().optional(),\n});\nconst terminalMintErrorReasons = new Set<IntegrationProviderErrorReason>([\n 'access-denied',\n 'installation-not-found',\n 'provider-rejected',\n 'malformed-provider-response',\n]);\n\ntype MissingProviderErrorReason = Exclude<\n IntegrationProviderErrorReason,\n (typeof providerErrorReasons)[number]\n>;\nconst providerErrorReasonSchemaCoversUnion: Record<MissingProviderErrorReason, never> = {};\nvoid providerErrorReasonSchemaCoversUnion;\n\nconst installationTokenEnvelopeSchema = z.object({\n token: z.string().min(1).optional(),\n expiresAt: z.string().datetime().optional(),\n permissions: z.record(z.string(), z.enum(['read', 'write', 'admin'])).optional(),\n backoffUntil: z.string().datetime().optional(),\n backoffReason: providerErrorReasonSchema.optional(),\n backoffError: backoffErrorSchema.optional(),\n});\n\nexport interface InstallationTokenEnvelope {\n token?: string | undefined;\n expiresAt?: Date | undefined;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n backoffUntil?: Date | undefined;\n backoffReason?: IntegrationProviderErrorReason | undefined;\n backoffError?:\n | {\n message: string;\n status?: number | undefined;\n }\n | undefined;\n}\n\nexport type MintErrorClass = 'transient' | 'terminal';\n\nexport interface ClassifiedMintError {\n class: MintErrorClass;\n reason: IntegrationProviderErrorReason;\n retryAfterSeconds?: number | undefined;\n}\n\nexport function githubInstallationTokenNamespace(installationId: number): string {\n return `system/github/installation-token/${installationId}`;\n}\n\nexport function encodeInstallationTokenEnvelope(envelope: InstallationTokenEnvelope): string {\n return JSON.stringify({\n ...(envelope.token !== undefined && {token: envelope.token}),\n ...(envelope.expiresAt !== undefined && {expiresAt: envelope.expiresAt.toISOString()}),\n ...(envelope.permissions !== undefined && {permissions: envelope.permissions}),\n ...(envelope.backoffUntil !== undefined && {\n backoffUntil: envelope.backoffUntil.toISOString(),\n }),\n ...(envelope.backoffReason !== undefined && {backoffReason: envelope.backoffReason}),\n ...(envelope.backoffError !== undefined && {backoffError: envelope.backoffError}),\n });\n}\n\nexport function parseInstallationTokenEnvelope(raw: string): InstallationTokenEnvelope | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n\n const result = installationTokenEnvelopeSchema.safeParse(parsed);\n if (!result.success) return undefined;\n\n return {\n token: result.data.token,\n expiresAt: result.data.expiresAt ? new Date(result.data.expiresAt) : undefined,\n permissions: result.data.permissions,\n backoffUntil: result.data.backoffUntil ? new Date(result.data.backoffUntil) : undefined,\n backoffReason: result.data.backoffReason,\n backoffError: result.data.backoffError,\n };\n}\n\nexport function usable(\n envelope: InstallationTokenEnvelope | undefined,\n now: Date,\n): envelope is InstallationTokenEnvelope & {token: string; expiresAt: Date} {\n return (\n envelope?.token !== undefined &&\n envelope.expiresAt !== undefined &&\n !needsRefresh(envelope.expiresAt, now)\n );\n}\n\nexport function needsRefresh(expiresAt: Date, now: Date): boolean {\n return expiresAt.getTime() <= now.getTime() + TOKEN_REFRESH_MARGIN_MS;\n}\n\nexport function stillValid(expiresAt: Date | undefined, now: Date): boolean {\n return expiresAt !== undefined && expiresAt.getTime() > now.getTime() + TOKEN_VALIDITY_BUFFER_MS;\n}\n\nexport function backoffActive(envelope: InstallationTokenEnvelope | undefined, now: Date): boolean {\n return (\n envelope?.backoffUntil !== undefined &&\n envelope.backoffReason !== undefined &&\n envelope.backoffUntil.getTime() > now.getTime()\n );\n}\n\nexport function classifyMintError(error: unknown): ClassifiedMintError {\n if (error instanceof IntegrationProviderError) {\n return {\n reason: error.reason,\n retryAfterSeconds: error.retryAfterSeconds,\n class: mintErrorClassForReason(error.reason),\n };\n }\n\n return {reason: 'provider-unavailable', class: 'transient'};\n}\n\nexport function mintErrorClassForReason(reason: IntegrationProviderErrorReason): MintErrorClass {\n return terminalMintErrorReasons.has(reason) ? 'terminal' : 'transient';\n}\n\nexport function backoffMs(classified: ClassifiedMintError): number {\n if (classified.class === 'terminal') return TERMINAL_BACKOFF_MS;\n\n const retryAfterMs = (classified.retryAfterSeconds ?? 0) * 1000;\n return Math.min(TRANSIENT_BACKOFF_MAX_MS, Math.max(TRANSIENT_BACKOFF_MIN_MS, retryAfterMs));\n}\n\nexport function providerErrorFromBackoff(\n reason: IntegrationProviderErrorReason,\n retryAfterMs: number,\n backoffError?: InstallationTokenEnvelope['backoffError'],\n): GithubIntegrationProviderError {\n return new GithubIntegrationProviderError(\n reason,\n backoffError?.message ?? `GitHub installation token mint is backed off after ${reason}`,\n Math.max(1, Math.ceil(retryAfterMs / 1000)),\n backoffError?.status,\n );\n}\n\nexport function toProviderError(error: unknown): GithubIntegrationProviderError {\n if (error instanceof GithubIntegrationProviderError) return error;\n if (error instanceof IntegrationProviderError) {\n return new GithubIntegrationProviderError(\n error.reason,\n error.message,\n error.retryAfterSeconds,\n error.status,\n );\n }\n return new GithubIntegrationProviderError(\n 'provider-unavailable',\n error instanceof Error ? error.message : 'GitHub installation token mint failed',\n );\n}\n"],"names":["IntegrationProviderError","z","GithubIntegrationProviderError","TOKEN_REFRESH_MARGIN_MS","TOKEN_VALIDITY_BUFFER_MS","TRANSIENT_BACKOFF_MIN_MS","TRANSIENT_BACKOFF_MAX_MS","TERMINAL_BACKOFF_MS","providerErrorReasons","providerErrorReasonSchema","enum","backoffErrorSchema","object","message","string","status","number","int","optional","terminalMintErrorReasons","Set","providerErrorReasonSchemaCoversUnion","installationTokenEnvelopeSchema","token","min","expiresAt","datetime","permissions","record","backoffUntil","backoffReason","backoffError","githubInstallationTokenNamespace","installationId","encodeInstallationTokenEnvelope","envelope","JSON","stringify","undefined","toISOString","parseInstallationTokenEnvelope","raw","parsed","parse","result","safeParse","success","data","Date","usable","now","needsRefresh","getTime","stillValid","backoffActive","classifyMintError","error","reason","retryAfterSeconds","class","mintErrorClassForReason","has","backoffMs","classified","retryAfterMs","Math","max","providerErrorFromBackoff","ceil","toProviderError","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/api/installation-token-envelope.ts"],"sourcesContent":["import {\n IntegrationProviderError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-spi';\nimport {z} from 'zod';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\n\nexport const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;\nexport const TOKEN_VALIDITY_BUFFER_MS = 60 * 1000;\nexport const TRANSIENT_BACKOFF_MIN_MS = 30 * 1000;\nexport const TRANSIENT_BACKOFF_MAX_MS = 5 * 60 * 1000;\nexport const TERMINAL_BACKOFF_MS = 15 * 60 * 1000;\n\nconst providerErrorReasons = [\n 'repository-not-found',\n 'installation-not-found',\n 'file-not-found',\n 'access-denied',\n 'rate-limited',\n 'timeout',\n 'provider-unavailable',\n 'provider-rejected',\n 'malformed-provider-response',\n 'content-too-large',\n 'too-many-files',\n] as const satisfies readonly IntegrationProviderErrorReason[];\n\nconst providerErrorReasonSchema = z.enum(providerErrorReasons);\nconst backoffErrorSchema = z.object({\n message: z.string(),\n status: z.number().int().optional(),\n});\nconst terminalMintErrorReasons = new Set<IntegrationProviderErrorReason>([\n 'access-denied',\n 'installation-not-found',\n 'provider-rejected',\n 'malformed-provider-response',\n]);\n\n// Ref reasons describe ref-resolution failures, which a token mint never\n// observes; keep them out of the envelope schema and acknowledge them here.\ntype MissingProviderErrorReason = Exclude<\n IntegrationProviderErrorReason,\n (typeof providerErrorReasons)[number] | 'ref-not-found' | 'ref-invalid'\n>;\nconst providerErrorReasonSchemaCoversUnion: Record<MissingProviderErrorReason, never> = {};\nvoid providerErrorReasonSchemaCoversUnion;\n\nconst installationTokenEnvelopeSchema = z.object({\n token: z.string().min(1).optional(),\n expiresAt: z.string().datetime().optional(),\n permissions: z.record(z.string(), z.enum(['read', 'write', 'admin'])).optional(),\n backoffUntil: z.string().datetime().optional(),\n backoffReason: providerErrorReasonSchema.optional(),\n backoffError: backoffErrorSchema.optional(),\n});\n\nexport interface InstallationTokenEnvelope {\n token?: string | undefined;\n expiresAt?: Date | undefined;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n backoffUntil?: Date | undefined;\n backoffReason?: IntegrationProviderErrorReason | undefined;\n backoffError?:\n | {\n message: string;\n status?: number | undefined;\n }\n | undefined;\n}\n\nexport type MintErrorClass = 'transient' | 'terminal';\n\nexport interface ClassifiedMintError {\n class: MintErrorClass;\n reason: IntegrationProviderErrorReason;\n retryAfterSeconds?: number | undefined;\n}\n\nexport function githubInstallationTokenNamespace(installationId: number): string {\n return `system/github/installation-token/${installationId}`;\n}\n\nexport function encodeInstallationTokenEnvelope(envelope: InstallationTokenEnvelope): string {\n return JSON.stringify({\n ...(envelope.token !== undefined && {token: envelope.token}),\n ...(envelope.expiresAt !== undefined && {expiresAt: envelope.expiresAt.toISOString()}),\n ...(envelope.permissions !== undefined && {permissions: envelope.permissions}),\n ...(envelope.backoffUntil !== undefined && {\n backoffUntil: envelope.backoffUntil.toISOString(),\n }),\n ...(envelope.backoffReason !== undefined && {backoffReason: envelope.backoffReason}),\n ...(envelope.backoffError !== undefined && {backoffError: envelope.backoffError}),\n });\n}\n\nexport function parseInstallationTokenEnvelope(raw: string): InstallationTokenEnvelope | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n\n const result = installationTokenEnvelopeSchema.safeParse(parsed);\n if (!result.success) return undefined;\n\n return {\n token: result.data.token,\n expiresAt: result.data.expiresAt ? new Date(result.data.expiresAt) : undefined,\n permissions: result.data.permissions,\n backoffUntil: result.data.backoffUntil ? new Date(result.data.backoffUntil) : undefined,\n backoffReason: result.data.backoffReason,\n backoffError: result.data.backoffError,\n };\n}\n\nexport function usable(\n envelope: InstallationTokenEnvelope | undefined,\n now: Date,\n): envelope is InstallationTokenEnvelope & {token: string; expiresAt: Date} {\n return (\n envelope?.token !== undefined &&\n envelope.expiresAt !== undefined &&\n !needsRefresh(envelope.expiresAt, now)\n );\n}\n\nexport function needsRefresh(expiresAt: Date, now: Date): boolean {\n return expiresAt.getTime() <= now.getTime() + TOKEN_REFRESH_MARGIN_MS;\n}\n\nexport function stillValid(expiresAt: Date | undefined, now: Date): boolean {\n return expiresAt !== undefined && expiresAt.getTime() > now.getTime() + TOKEN_VALIDITY_BUFFER_MS;\n}\n\nexport function backoffActive(envelope: InstallationTokenEnvelope | undefined, now: Date): boolean {\n return (\n envelope?.backoffUntil !== undefined &&\n envelope.backoffReason !== undefined &&\n envelope.backoffUntil.getTime() > now.getTime()\n );\n}\n\nexport function classifyMintError(error: unknown): ClassifiedMintError {\n if (error instanceof IntegrationProviderError) {\n return {\n reason: error.reason,\n retryAfterSeconds: error.retryAfterSeconds,\n class: mintErrorClassForReason(error.reason),\n };\n }\n\n return {reason: 'provider-unavailable', class: 'transient'};\n}\n\nexport function mintErrorClassForReason(reason: IntegrationProviderErrorReason): MintErrorClass {\n return terminalMintErrorReasons.has(reason) ? 'terminal' : 'transient';\n}\n\nexport function backoffMs(classified: ClassifiedMintError): number {\n if (classified.class === 'terminal') return TERMINAL_BACKOFF_MS;\n\n const retryAfterMs = (classified.retryAfterSeconds ?? 0) * 1000;\n return Math.min(TRANSIENT_BACKOFF_MAX_MS, Math.max(TRANSIENT_BACKOFF_MIN_MS, retryAfterMs));\n}\n\nexport function providerErrorFromBackoff(\n reason: IntegrationProviderErrorReason,\n retryAfterMs: number,\n backoffError?: InstallationTokenEnvelope['backoffError'],\n): GithubIntegrationProviderError {\n return new GithubIntegrationProviderError(\n reason,\n backoffError?.message ?? `GitHub installation token mint is backed off after ${reason}`,\n Math.max(1, Math.ceil(retryAfterMs / 1000)),\n backoffError?.status,\n );\n}\n\nexport function toProviderError(error: unknown): GithubIntegrationProviderError {\n if (error instanceof GithubIntegrationProviderError) return error;\n if (error instanceof IntegrationProviderError) {\n return new GithubIntegrationProviderError(\n error.reason,\n error.message,\n error.retryAfterSeconds,\n error.status,\n );\n }\n return new GithubIntegrationProviderError(\n 'provider-unavailable',\n error instanceof Error ? error.message : 'GitHub installation token mint failed',\n );\n}\n"],"names":["IntegrationProviderError","z","GithubIntegrationProviderError","TOKEN_REFRESH_MARGIN_MS","TOKEN_VALIDITY_BUFFER_MS","TRANSIENT_BACKOFF_MIN_MS","TRANSIENT_BACKOFF_MAX_MS","TERMINAL_BACKOFF_MS","providerErrorReasons","providerErrorReasonSchema","enum","backoffErrorSchema","object","message","string","status","number","int","optional","terminalMintErrorReasons","Set","providerErrorReasonSchemaCoversUnion","installationTokenEnvelopeSchema","token","min","expiresAt","datetime","permissions","record","backoffUntil","backoffReason","backoffError","githubInstallationTokenNamespace","installationId","encodeInstallationTokenEnvelope","envelope","JSON","stringify","undefined","toISOString","parseInstallationTokenEnvelope","raw","parsed","parse","result","safeParse","success","data","Date","usable","now","needsRefresh","getTime","stillValid","backoffActive","classifyMintError","error","reason","retryAfterSeconds","class","mintErrorClassForReason","has","backoffMs","classified","retryAfterMs","Math","max","providerErrorFromBackoff","ceil","toProviderError","Error"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CheckoutSpec, type CreateCheckoutSpecInput, type FetchFileInput, type FilePage, type FileSnapshot, type IntegrationConnection, type ListFilesInput, type ListRepositoriesInput, type RepositoryPage, type RepositorySnapshot, type ResolveRepositoryInput, type SourceControlProvider, type TriggerReference } from '@shipfox/api-integration-spi';
|
|
1
|
+
import { type CheckoutSpec, type CreateCheckoutSpecInput, type FetchFileInput, type FilePage, type FileSnapshot, type IntegrationConnection, type ListFilesInput, type ListRepositoriesInput, type RepositoryPage, type RepositorySnapshot, type ResolvedRef, type ResolveRefInput, type ResolveRepositoryInput, type SourceControlProvider, type TriggerReference } from '@shipfox/api-integration-spi';
|
|
2
2
|
import type { GithubApiClient } from '#api/client.js';
|
|
3
3
|
type GithubIntegrationConnection = IntegrationConnection<'github'>;
|
|
4
4
|
export declare class GithubSourceControlProvider implements SourceControlProvider<GithubIntegrationConnection> {
|
|
@@ -10,6 +10,7 @@ export declare class GithubSourceControlProvider implements SourceControlProvide
|
|
|
10
10
|
listFiles(input: ListFilesInput<GithubIntegrationConnection>): Promise<FilePage>;
|
|
11
11
|
fetchFile(input: FetchFileInput<GithubIntegrationConnection>): Promise<FileSnapshot>;
|
|
12
12
|
resolveTriggerReference(payload: unknown): TriggerReference | null;
|
|
13
|
+
resolveRef(input: ResolveRefInput<GithubIntegrationConnection>): Promise<ResolvedRef>;
|
|
13
14
|
createCheckoutSpec(input: CreateCheckoutSpecInput<GithubIntegrationConnection>): Promise<CheckoutSpec>;
|
|
14
15
|
private installationId;
|
|
15
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-control.d.ts","sourceRoot":"","sources":["../../src/core/source-control.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,qBAAqB,
|
|
1
|
+
{"version":3,"file":"source-control.d.ts","sourceRoot":"","sources":["../../src/core/source-control.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAK1B,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAK1B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAC,eAAe,EAAmB,MAAM,gBAAgB,CAAC;AAKtE,KAAK,2BAA2B,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;AAMnE,qBAAa,2BACX,YAAW,qBAAqB,CAAC,2BAA2B,CAAC;IAG3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAF9B,YACmB,MAAM,EAAE,eAAe,EACvB,WAAW,GAAE,MAAM,MAAM,GAAG,SAAuC,EAClF;IAEE,gBAAgB,CACpB,KAAK,EAAE,qBAAqB,CAAC,2BAA2B,CAAC,GACxD,OAAO,CAAC,cAAc,CAAC,CA+CzB;IAEK,iBAAiB,CACrB,KAAK,EAAE,sBAAsB,CAAC,2BAA2B,CAAC,GACzD,OAAO,CAAC,kBAAkB,CAAC,CAS7B;IAEK,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,2BAA2B,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgBrF;IAEK,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,2BAA2B,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAyBzF;IAED,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAwCjE;IAEK,UAAU,CAAC,KAAK,EAAE,eAAe,CAAC,2BAA2B,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CA6B1F;IAEK,kBAAkB,CACtB,KAAK,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,GAC1D,OAAO,CAAC,YAAY,CAAC,CAsBvB;YAEa,cAAc;CAW7B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
-
import { asRecord, buildProviderRepositoryId, isRecord, isValidGitObjectId, isValidTriggerRef, MAX_REPOSITORY_FILE_BYTES, nonEmptyString, parseProviderRepositoryId, positiveInteger } from '@shipfox/api-integration-spi';
|
|
2
|
+
import { asRecord, buildProviderRepositoryId, isRecord, isValidGitObjectId, isValidResolvableRef, isValidTriggerRef, MAX_REPOSITORY_FILE_BYTES, nonEmptyString, parseProviderRepositoryId, positiveInteger } from '@shipfox/api-integration-spi';
|
|
3
3
|
import { getGithubInstallationByConnectionId } from '#db/installations.js';
|
|
4
4
|
import { configuredGithubAppBotLogin } from './bot-identity.js';
|
|
5
5
|
import { GithubIntegrationProviderError } from './errors.js';
|
|
@@ -138,6 +138,29 @@ export class GithubSourceControlProvider {
|
|
|
138
138
|
actor
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
|
+
async resolveRef(input) {
|
|
142
|
+
if (!isValidResolvableRef(input.ref)) {
|
|
143
|
+
throw new GithubIntegrationProviderError('ref-invalid', `GitHub ref ${formatRefForMessage(input.ref)} is not a resolvable branch or tag name`);
|
|
144
|
+
}
|
|
145
|
+
const installationId = await this.installationId(input.connection.id);
|
|
146
|
+
const { repositoryId } = parseGithubRepositoryLocator(input.externalRepositoryId);
|
|
147
|
+
const commits = await this.github.listRepositoryCommits({
|
|
148
|
+
installationId,
|
|
149
|
+
repositoryId,
|
|
150
|
+
ref: input.ref
|
|
151
|
+
});
|
|
152
|
+
const commit = commits[0]?.sha;
|
|
153
|
+
if (!commit) {
|
|
154
|
+
throw new GithubIntegrationProviderError('ref-not-found', `GitHub ref ${formatRefForMessage(input.ref)} does not resolve to a commit`);
|
|
155
|
+
}
|
|
156
|
+
if (!isValidGitObjectId(commit)) {
|
|
157
|
+
throw new GithubIntegrationProviderError('malformed-provider-response', `GitHub ref ${formatRefForMessage(input.ref)} resolved to an invalid commit`);
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
ref: input.ref,
|
|
161
|
+
commit
|
|
162
|
+
};
|
|
163
|
+
}
|
|
141
164
|
async createCheckoutSpec(input) {
|
|
142
165
|
const installationId = await this.installationId(input.connection.id);
|
|
143
166
|
const { repositoryId } = parseGithubRepositoryLocator(input.externalRepositoryId);
|
|
@@ -190,6 +213,9 @@ function sameGithubRepository(first, second) {
|
|
|
190
213
|
const secondName = nonEmptyString(second.full_name);
|
|
191
214
|
return Boolean(firstName && secondName && firstName.toLowerCase() === secondName.toLowerCase());
|
|
192
215
|
}
|
|
216
|
+
function formatRefForMessage(ref) {
|
|
217
|
+
return JSON.stringify(ref);
|
|
218
|
+
}
|
|
193
219
|
async function githubAppGitAuthor(github, installationAccessToken, name) {
|
|
194
220
|
if (!github.getBotUser) {
|
|
195
221
|
throw new GithubIntegrationProviderError('provider-unavailable', 'GitHub bot identity resolution is unavailable');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/source-control.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {\n asRecord,\n buildProviderRepositoryId,\n type CheckoutSpec,\n type CreateCheckoutSpecInput,\n type FetchFileInput,\n type FilePage,\n type FileSnapshot,\n type IntegrationConnection,\n isRecord,\n isValidGitObjectId,\n isValidTriggerRef,\n type ListFilesInput,\n type ListRepositoriesInput,\n MAX_REPOSITORY_FILE_BYTES,\n nonEmptyString,\n parseProviderRepositoryId,\n positiveInteger,\n type RepositoryPage,\n type RepositorySnapshot,\n type RepositoryVisibility,\n type ResolveRepositoryInput,\n type SourceControlProvider,\n type TriggerReference,\n} from '@shipfox/api-integration-spi';\nimport type {GithubApiClient, GithubRepository} from '#api/client.js';\nimport {getGithubInstallationByConnectionId} from '#db/installations.js';\nimport {configuredGithubAppBotLogin} from './bot-identity.js';\nimport {GithubIntegrationProviderError} from './errors.js';\n\ntype GithubIntegrationConnection = IntegrationConnection<'github'>;\n\nconst GITHUB_PROVIDER = 'github';\nconst SEARCH_PAGE_SIZE = 100;\nconst SEARCH_MAX_PAGES_PER_REQUEST = 5;\n\nexport class GithubSourceControlProvider\n implements SourceControlProvider<GithubIntegrationConnection>\n{\n constructor(\n private readonly github: GithubApiClient,\n private readonly appBotLogin: () => string | undefined = configuredGithubAppBotLogin,\n ) {}\n\n async listRepositories(\n input: ListRepositoriesInput<GithubIntegrationConnection>,\n ): Promise<RepositoryPage> {\n const installation = await getGithubInstallationByConnectionId(input.connection.id);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub installation details were not found for the connection',\n );\n }\n\n const installationId = Number.parseInt(installation.installationId, 10);\n const needle = input.search?.trim().toLowerCase();\n\n if (!needle) {\n const page = await this.github.listInstallationRepositories({\n installationId,\n limit: input.limit,\n cursor: input.cursor,\n });\n return {\n repositories: page.repositories.map(toRepositorySnapshot),\n nextCursor: page.nextCursor,\n };\n }\n\n const matches: RepositorySnapshot[] = [];\n let cursor = input.cursor;\n let pagesScanned = 0;\n while (matches.length < input.limit && pagesScanned < SEARCH_MAX_PAGES_PER_REQUEST) {\n const page = await this.github.listInstallationRepositories({\n installationId,\n limit: SEARCH_PAGE_SIZE,\n cursor,\n });\n pagesScanned += 1;\n for (const repo of page.repositories) {\n if (repo.fullName.toLowerCase().includes(needle)) {\n matches.push(toRepositorySnapshot(repo));\n }\n }\n cursor = page.nextCursor ?? undefined;\n if (!cursor) break;\n }\n\n return {\n repositories: matches.slice(0, input.limit),\n nextCursor: cursor ?? null,\n };\n }\n\n async resolveRepository(\n input: ResolveRepositoryInput<GithubIntegrationConnection>,\n ): Promise<RepositorySnapshot> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const repository = await this.github.getRepository({\n installationId,\n repositoryId,\n });\n\n return toRepositorySnapshot(repository);\n }\n\n async listFiles(input: ListFilesInput<GithubIntegrationConnection>): Promise<FilePage> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const page = await this.github.listRepositoryFiles({\n installationId,\n repositoryId,\n ref: input.ref,\n prefix: input.prefix,\n limit: input.limit,\n cursor: input.cursor,\n });\n\n return {\n files: page.files.map((file) => ({path: file.path, type: 'file', size: file.size})),\n nextCursor: page.nextCursor,\n };\n }\n\n async fetchFile(input: FetchFileInput<GithubIntegrationConnection>): Promise<FileSnapshot> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const file = await this.github.fetchRepositoryFile({\n installationId,\n repositoryId,\n ref: input.ref,\n path: input.path,\n });\n\n if (\n file.size > MAX_REPOSITORY_FILE_BYTES ||\n Buffer.byteLength(file.content, 'utf8') > MAX_REPOSITORY_FILE_BYTES\n ) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n\n return {\n path: file.path,\n ref: input.ref,\n content: file.content,\n };\n }\n\n resolveTriggerReference(payload: unknown): TriggerReference | null {\n if (!isRecord(payload)) return null;\n\n const actor = githubEventActor(payload);\n const pullRequest = asRecord(payload.pull_request);\n if (pullRequest) {\n const head = asRecord(pullRequest.head);\n const repository = asRecord(head?.repo);\n const base = asRecord(pullRequest.base);\n const baseRepository = asRecord(base?.repo) ?? asRecord(payload.repository);\n if (!repository || !baseRepository || !sameGithubRepository(repository, baseRepository)) {\n return null;\n }\n const repositoryId = githubRepositoryId(repository);\n const number = positiveInteger(pullRequest.number);\n const commit = nonEmptyString(head?.sha);\n const ref = number === null ? null : `refs/pull/${number}/head`;\n if (!repositoryId || !ref || !commit) return null;\n const hasValidReference = isValidGitObjectId(commit) && isValidTriggerRef(ref);\n if (!hasValidReference) return null;\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, repositoryId),\n ref,\n commit,\n actor,\n };\n }\n\n const repositoryId = githubRepositoryId(asRecord(payload.repository));\n const ref = nonEmptyString(payload.ref);\n const commit = nonEmptyString(payload.after);\n if (!repositoryId || !ref || !commit) return null;\n const hasValidReference = isValidGitObjectId(commit) && isValidTriggerRef(ref);\n if (!hasValidReference) return null;\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, repositoryId),\n ref,\n commit,\n actor,\n };\n }\n\n async createCheckoutSpec(\n input: CreateCheckoutSpecInput<GithubIntegrationConnection>,\n ): Promise<CheckoutSpec> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const repository = await this.github.getRepository({installationId, repositoryId});\n const ref = input.ref?.trim() || repository.defaultBranch;\n const {token, expiresAt} = await this.github.createInstallationAccessToken({\n installationId,\n repositoryId,\n permissions: input.permissions,\n });\n const botLogin = this.appBotLogin();\n const gitAuthor =\n botLogin && input.permissions?.contents === 'write'\n ? await githubAppGitAuthor(this.github, token, botLogin)\n : undefined;\n\n return {\n repositoryUrl: repository.cloneUrl,\n ref,\n credentials: {username: 'x-access-token', token, expiresAt},\n ...(gitAuthor ? {gitAuthor} : {}),\n };\n }\n\n private async installationId(connectionId: string): Promise<number> {\n const installation = await getGithubInstallationByConnectionId(connectionId);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub installation details were not found for the connection',\n );\n }\n\n return Number.parseInt(installation.installationId, 10);\n }\n}\n\nfunction githubRepositoryId(repository: Record<string, unknown> | null): string | null {\n return positiveInteger(repository?.id) === null ? null : String(repository?.id);\n}\n\n// `sender` is the one actor field every GitHub webhook event carries, so it stays correct\n// for pushes, pull requests, and anything else that reaches a trigger reference.\nfunction githubEventActor(payload: Record<string, unknown>): string | null {\n return nonEmptyString(asRecord(payload.sender)?.login);\n}\n\nfunction sameGithubRepository(\n first: Record<string, unknown>,\n second: Record<string, unknown>,\n): boolean {\n const firstId = githubRepositoryId(first);\n const secondId = githubRepositoryId(second);\n if (firstId && secondId) return firstId === secondId;\n\n const firstName = nonEmptyString(first.full_name);\n const secondName = nonEmptyString(second.full_name);\n return Boolean(firstName && secondName && firstName.toLowerCase() === secondName.toLowerCase());\n}\n\nasync function githubAppGitAuthor(\n github: GithubApiClient,\n installationAccessToken: string,\n name: string,\n): Promise<CheckoutSpec['gitAuthor']> {\n if (!github.getBotUser) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n 'GitHub bot identity resolution is unavailable',\n );\n }\n\n const botUser = await github.getBotUser({username: name, installationAccessToken});\n return {\n name: botUser.login,\n email: `${botUser.id}+${botUser.login}@users.noreply.github.com`,\n };\n}\n\nfunction toRepositorySnapshot(repository: GithubRepository): RepositorySnapshot {\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, String(repository.id)),\n owner: repository.ownerLogin,\n name: repository.name,\n fullName: repository.fullName,\n defaultBranch: repository.defaultBranch,\n visibility: toRepositoryVisibility(repository),\n cloneUrl: repository.cloneUrl,\n htmlUrl: repository.htmlUrl,\n };\n}\n\nfunction parseGithubRepositoryLocator(externalRepositoryId: string): {\n repositoryId: number;\n} {\n const value = parseProviderRepositoryId(externalRepositoryId, GITHUB_PROVIDER);\n const repositoryId = Number.parseInt(value, 10);\n if (!Number.isInteger(repositoryId) || repositoryId <= 0 || String(repositoryId) !== value) {\n throw new GithubIntegrationProviderError(\n 'repository-not-found',\n `GitHub repository id ${externalRepositoryId} must follow the form ${GITHUB_PROVIDER}:<numeric-id>`,\n );\n }\n\n return {repositoryId};\n}\n\nfunction toRepositoryVisibility(repository: GithubRepository): RepositoryVisibility {\n if (repository.visibility === 'public') return 'public';\n if (repository.visibility === 'private') return 'private';\n if (repository.visibility === 'internal') return 'internal';\n return repository.private ? 'private' : 'public';\n}\n"],"names":["Buffer","asRecord","buildProviderRepositoryId","isRecord","isValidGitObjectId","isValidTriggerRef","MAX_REPOSITORY_FILE_BYTES","nonEmptyString","parseProviderRepositoryId","positiveInteger","getGithubInstallationByConnectionId","configuredGithubAppBotLogin","GithubIntegrationProviderError","GITHUB_PROVIDER","SEARCH_PAGE_SIZE","SEARCH_MAX_PAGES_PER_REQUEST","GithubSourceControlProvider","github","appBotLogin","listRepositories","input","installation","connection","id","installationId","Number","parseInt","needle","search","trim","toLowerCase","page","listInstallationRepositories","limit","cursor","repositories","map","toRepositorySnapshot","nextCursor","matches","pagesScanned","length","repo","fullName","includes","push","undefined","slice","resolveRepository","repositoryId","parseGithubRepositoryLocator","externalRepositoryId","repository","getRepository","listFiles","listRepositoryFiles","ref","prefix","files","file","path","type","size","fetchFile","fetchRepositoryFile","byteLength","content","resolveTriggerReference","payload","actor","githubEventActor","pullRequest","pull_request","head","base","baseRepository","sameGithubRepository","githubRepositoryId","number","commit","sha","hasValidReference","after","createCheckoutSpec","defaultBranch","token","expiresAt","createInstallationAccessToken","permissions","botLogin","gitAuthor","contents","githubAppGitAuthor","repositoryUrl","cloneUrl","credentials","username","connectionId","String","sender","login","first","second","firstId","secondId","firstName","full_name","secondName","Boolean","installationAccessToken","name","getBotUser","botUser","email","owner","ownerLogin","visibility","toRepositoryVisibility","htmlUrl","value","isInteger","private"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/source-control.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {\n asRecord,\n buildProviderRepositoryId,\n type CheckoutSpec,\n type CreateCheckoutSpecInput,\n type FetchFileInput,\n type FilePage,\n type FileSnapshot,\n type IntegrationConnection,\n isRecord,\n isValidGitObjectId,\n isValidResolvableRef,\n isValidTriggerRef,\n type ListFilesInput,\n type ListRepositoriesInput,\n MAX_REPOSITORY_FILE_BYTES,\n nonEmptyString,\n parseProviderRepositoryId,\n positiveInteger,\n type RepositoryPage,\n type RepositorySnapshot,\n type RepositoryVisibility,\n type ResolvedRef,\n type ResolveRefInput,\n type ResolveRepositoryInput,\n type SourceControlProvider,\n type TriggerReference,\n} from '@shipfox/api-integration-spi';\nimport type {GithubApiClient, GithubRepository} from '#api/client.js';\nimport {getGithubInstallationByConnectionId} from '#db/installations.js';\nimport {configuredGithubAppBotLogin} from './bot-identity.js';\nimport {GithubIntegrationProviderError} from './errors.js';\n\ntype GithubIntegrationConnection = IntegrationConnection<'github'>;\n\nconst GITHUB_PROVIDER = 'github';\nconst SEARCH_PAGE_SIZE = 100;\nconst SEARCH_MAX_PAGES_PER_REQUEST = 5;\n\nexport class GithubSourceControlProvider\n implements SourceControlProvider<GithubIntegrationConnection>\n{\n constructor(\n private readonly github: GithubApiClient,\n private readonly appBotLogin: () => string | undefined = configuredGithubAppBotLogin,\n ) {}\n\n async listRepositories(\n input: ListRepositoriesInput<GithubIntegrationConnection>,\n ): Promise<RepositoryPage> {\n const installation = await getGithubInstallationByConnectionId(input.connection.id);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub installation details were not found for the connection',\n );\n }\n\n const installationId = Number.parseInt(installation.installationId, 10);\n const needle = input.search?.trim().toLowerCase();\n\n if (!needle) {\n const page = await this.github.listInstallationRepositories({\n installationId,\n limit: input.limit,\n cursor: input.cursor,\n });\n return {\n repositories: page.repositories.map(toRepositorySnapshot),\n nextCursor: page.nextCursor,\n };\n }\n\n const matches: RepositorySnapshot[] = [];\n let cursor = input.cursor;\n let pagesScanned = 0;\n while (matches.length < input.limit && pagesScanned < SEARCH_MAX_PAGES_PER_REQUEST) {\n const page = await this.github.listInstallationRepositories({\n installationId,\n limit: SEARCH_PAGE_SIZE,\n cursor,\n });\n pagesScanned += 1;\n for (const repo of page.repositories) {\n if (repo.fullName.toLowerCase().includes(needle)) {\n matches.push(toRepositorySnapshot(repo));\n }\n }\n cursor = page.nextCursor ?? undefined;\n if (!cursor) break;\n }\n\n return {\n repositories: matches.slice(0, input.limit),\n nextCursor: cursor ?? null,\n };\n }\n\n async resolveRepository(\n input: ResolveRepositoryInput<GithubIntegrationConnection>,\n ): Promise<RepositorySnapshot> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const repository = await this.github.getRepository({\n installationId,\n repositoryId,\n });\n\n return toRepositorySnapshot(repository);\n }\n\n async listFiles(input: ListFilesInput<GithubIntegrationConnection>): Promise<FilePage> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const page = await this.github.listRepositoryFiles({\n installationId,\n repositoryId,\n ref: input.ref,\n prefix: input.prefix,\n limit: input.limit,\n cursor: input.cursor,\n });\n\n return {\n files: page.files.map((file) => ({path: file.path, type: 'file', size: file.size})),\n nextCursor: page.nextCursor,\n };\n }\n\n async fetchFile(input: FetchFileInput<GithubIntegrationConnection>): Promise<FileSnapshot> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const file = await this.github.fetchRepositoryFile({\n installationId,\n repositoryId,\n ref: input.ref,\n path: input.path,\n });\n\n if (\n file.size > MAX_REPOSITORY_FILE_BYTES ||\n Buffer.byteLength(file.content, 'utf8') > MAX_REPOSITORY_FILE_BYTES\n ) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n\n return {\n path: file.path,\n ref: input.ref,\n content: file.content,\n };\n }\n\n resolveTriggerReference(payload: unknown): TriggerReference | null {\n if (!isRecord(payload)) return null;\n\n const actor = githubEventActor(payload);\n const pullRequest = asRecord(payload.pull_request);\n if (pullRequest) {\n const head = asRecord(pullRequest.head);\n const repository = asRecord(head?.repo);\n const base = asRecord(pullRequest.base);\n const baseRepository = asRecord(base?.repo) ?? asRecord(payload.repository);\n if (!repository || !baseRepository || !sameGithubRepository(repository, baseRepository)) {\n return null;\n }\n const repositoryId = githubRepositoryId(repository);\n const number = positiveInteger(pullRequest.number);\n const commit = nonEmptyString(head?.sha);\n const ref = number === null ? null : `refs/pull/${number}/head`;\n if (!repositoryId || !ref || !commit) return null;\n const hasValidReference = isValidGitObjectId(commit) && isValidTriggerRef(ref);\n if (!hasValidReference) return null;\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, repositoryId),\n ref,\n commit,\n actor,\n };\n }\n\n const repositoryId = githubRepositoryId(asRecord(payload.repository));\n const ref = nonEmptyString(payload.ref);\n const commit = nonEmptyString(payload.after);\n if (!repositoryId || !ref || !commit) return null;\n const hasValidReference = isValidGitObjectId(commit) && isValidTriggerRef(ref);\n if (!hasValidReference) return null;\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, repositoryId),\n ref,\n commit,\n actor,\n };\n }\n\n async resolveRef(input: ResolveRefInput<GithubIntegrationConnection>): Promise<ResolvedRef> {\n if (!isValidResolvableRef(input.ref)) {\n throw new GithubIntegrationProviderError(\n 'ref-invalid',\n `GitHub ref ${formatRefForMessage(input.ref)} is not a resolvable branch or tag name`,\n );\n }\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const commits = await this.github.listRepositoryCommits({\n installationId,\n repositoryId,\n ref: input.ref,\n });\n const commit = commits[0]?.sha;\n if (!commit) {\n throw new GithubIntegrationProviderError(\n 'ref-not-found',\n `GitHub ref ${formatRefForMessage(input.ref)} does not resolve to a commit`,\n );\n }\n if (!isValidGitObjectId(commit)) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n `GitHub ref ${formatRefForMessage(input.ref)} resolved to an invalid commit`,\n );\n }\n\n return {ref: input.ref, commit};\n }\n\n async createCheckoutSpec(\n input: CreateCheckoutSpecInput<GithubIntegrationConnection>,\n ): Promise<CheckoutSpec> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const repository = await this.github.getRepository({installationId, repositoryId});\n const ref = input.ref?.trim() || repository.defaultBranch;\n const {token, expiresAt} = await this.github.createInstallationAccessToken({\n installationId,\n repositoryId,\n permissions: input.permissions,\n });\n const botLogin = this.appBotLogin();\n const gitAuthor =\n botLogin && input.permissions?.contents === 'write'\n ? await githubAppGitAuthor(this.github, token, botLogin)\n : undefined;\n\n return {\n repositoryUrl: repository.cloneUrl,\n ref,\n credentials: {username: 'x-access-token', token, expiresAt},\n ...(gitAuthor ? {gitAuthor} : {}),\n };\n }\n\n private async installationId(connectionId: string): Promise<number> {\n const installation = await getGithubInstallationByConnectionId(connectionId);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub installation details were not found for the connection',\n );\n }\n\n return Number.parseInt(installation.installationId, 10);\n }\n}\n\nfunction githubRepositoryId(repository: Record<string, unknown> | null): string | null {\n return positiveInteger(repository?.id) === null ? null : String(repository?.id);\n}\n\n// `sender` is the one actor field every GitHub webhook event carries, so it stays correct\n// for pushes, pull requests, and anything else that reaches a trigger reference.\nfunction githubEventActor(payload: Record<string, unknown>): string | null {\n return nonEmptyString(asRecord(payload.sender)?.login);\n}\n\nfunction sameGithubRepository(\n first: Record<string, unknown>,\n second: Record<string, unknown>,\n): boolean {\n const firstId = githubRepositoryId(first);\n const secondId = githubRepositoryId(second);\n if (firstId && secondId) return firstId === secondId;\n\n const firstName = nonEmptyString(first.full_name);\n const secondName = nonEmptyString(second.full_name);\n return Boolean(firstName && secondName && firstName.toLowerCase() === secondName.toLowerCase());\n}\n\nfunction formatRefForMessage(ref: string): string {\n return JSON.stringify(ref);\n}\n\nasync function githubAppGitAuthor(\n github: GithubApiClient,\n installationAccessToken: string,\n name: string,\n): Promise<CheckoutSpec['gitAuthor']> {\n if (!github.getBotUser) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n 'GitHub bot identity resolution is unavailable',\n );\n }\n\n const botUser = await github.getBotUser({username: name, installationAccessToken});\n return {\n name: botUser.login,\n email: `${botUser.id}+${botUser.login}@users.noreply.github.com`,\n };\n}\n\nfunction toRepositorySnapshot(repository: GithubRepository): RepositorySnapshot {\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, String(repository.id)),\n owner: repository.ownerLogin,\n name: repository.name,\n fullName: repository.fullName,\n defaultBranch: repository.defaultBranch,\n visibility: toRepositoryVisibility(repository),\n cloneUrl: repository.cloneUrl,\n htmlUrl: repository.htmlUrl,\n };\n}\n\nfunction parseGithubRepositoryLocator(externalRepositoryId: string): {\n repositoryId: number;\n} {\n const value = parseProviderRepositoryId(externalRepositoryId, GITHUB_PROVIDER);\n const repositoryId = Number.parseInt(value, 10);\n if (!Number.isInteger(repositoryId) || repositoryId <= 0 || String(repositoryId) !== value) {\n throw new GithubIntegrationProviderError(\n 'repository-not-found',\n `GitHub repository id ${externalRepositoryId} must follow the form ${GITHUB_PROVIDER}:<numeric-id>`,\n );\n }\n\n return {repositoryId};\n}\n\nfunction toRepositoryVisibility(repository: GithubRepository): RepositoryVisibility {\n if (repository.visibility === 'public') return 'public';\n if (repository.visibility === 'private') return 'private';\n if (repository.visibility === 'internal') return 'internal';\n return repository.private ? 'private' : 'public';\n}\n"],"names":["Buffer","asRecord","buildProviderRepositoryId","isRecord","isValidGitObjectId","isValidResolvableRef","isValidTriggerRef","MAX_REPOSITORY_FILE_BYTES","nonEmptyString","parseProviderRepositoryId","positiveInteger","getGithubInstallationByConnectionId","configuredGithubAppBotLogin","GithubIntegrationProviderError","GITHUB_PROVIDER","SEARCH_PAGE_SIZE","SEARCH_MAX_PAGES_PER_REQUEST","GithubSourceControlProvider","github","appBotLogin","listRepositories","input","installation","connection","id","installationId","Number","parseInt","needle","search","trim","toLowerCase","page","listInstallationRepositories","limit","cursor","repositories","map","toRepositorySnapshot","nextCursor","matches","pagesScanned","length","repo","fullName","includes","push","undefined","slice","resolveRepository","repositoryId","parseGithubRepositoryLocator","externalRepositoryId","repository","getRepository","listFiles","listRepositoryFiles","ref","prefix","files","file","path","type","size","fetchFile","fetchRepositoryFile","byteLength","content","resolveTriggerReference","payload","actor","githubEventActor","pullRequest","pull_request","head","base","baseRepository","sameGithubRepository","githubRepositoryId","number","commit","sha","hasValidReference","after","resolveRef","formatRefForMessage","commits","listRepositoryCommits","createCheckoutSpec","defaultBranch","token","expiresAt","createInstallationAccessToken","permissions","botLogin","gitAuthor","contents","githubAppGitAuthor","repositoryUrl","cloneUrl","credentials","username","connectionId","String","sender","login","first","second","firstId","secondId","firstName","full_name","secondName","Boolean","JSON","stringify","installationAccessToken","name","getBotUser","botUser","email","owner","ownerLogin","visibility","toRepositoryVisibility","htmlUrl","value","isInteger","private"],"mappings":"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"}
|