@shipfox/api-integration-github 2.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.
Files changed (179) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +138 -0
  5. package/LICENSE +21 -0
  6. package/dist/api/client.d.ts +90 -0
  7. package/dist/api/client.d.ts.map +1 -0
  8. package/dist/api/client.js +306 -0
  9. package/dist/api/client.js.map +1 -0
  10. package/dist/api/installation-token-envelope.d.ts +36 -0
  11. package/dist/api/installation-token-envelope.d.ts.map +1 -0
  12. package/dist/api/installation-token-envelope.js +123 -0
  13. package/dist/api/installation-token-envelope.js.map +1 -0
  14. package/dist/api/installation-token-provider.d.ts +23 -0
  15. package/dist/api/installation-token-provider.d.ts.map +1 -0
  16. package/dist/api/installation-token-provider.js +130 -0
  17. package/dist/api/installation-token-provider.js.map +1 -0
  18. package/dist/api/shared-installation-token-cache.d.ts +43 -0
  19. package/dist/api/shared-installation-token-cache.d.ts.map +1 -0
  20. package/dist/api/shared-installation-token-cache.js +224 -0
  21. package/dist/api/shared-installation-token-cache.js.map +1 -0
  22. package/dist/config.d.ts +15 -0
  23. package/dist/config.d.ts.map +1 -0
  24. package/dist/config.js +44 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/core/actions-logs.d.ts +47 -0
  27. package/dist/core/actions-logs.d.ts.map +1 -0
  28. package/dist/core/actions-logs.js +134 -0
  29. package/dist/core/actions-logs.js.map +1 -0
  30. package/dist/core/agent-tool-selection.d.ts +4 -0
  31. package/dist/core/agent-tool-selection.d.ts.map +1 -0
  32. package/dist/core/agent-tool-selection.js +38 -0
  33. package/dist/core/agent-tool-selection.js.map +1 -0
  34. package/dist/core/agent-tools.d.ts +50 -0
  35. package/dist/core/agent-tools.d.ts.map +1 -0
  36. package/dist/core/agent-tools.js +1122 -0
  37. package/dist/core/agent-tools.js.map +1 -0
  38. package/dist/core/errors.d.ts +15 -0
  39. package/dist/core/errors.d.ts.map +1 -0
  40. package/dist/core/errors.js +22 -0
  41. package/dist/core/errors.js.map +1 -0
  42. package/dist/core/install.d.ts +38 -0
  43. package/dist/core/install.d.ts.map +1 -0
  44. package/dist/core/install.js +64 -0
  45. package/dist/core/install.js.map +1 -0
  46. package/dist/core/source-control.d.ts +15 -0
  47. package/dist/core/source-control.d.ts.map +1 -0
  48. package/dist/core/source-control.js +175 -0
  49. package/dist/core/source-control.js.map +1 -0
  50. package/dist/core/state.d.ts +12 -0
  51. package/dist/core/state.d.ts.map +1 -0
  52. package/dist/core/state.js +54 -0
  53. package/dist/core/state.js.map +1 -0
  54. package/dist/core/webhook.d.ts +20 -0
  55. package/dist/core/webhook.d.ts.map +1 -0
  56. package/dist/core/webhook.js +216 -0
  57. package/dist/core/webhook.js.map +1 -0
  58. package/dist/db/db.d.ts +433 -0
  59. package/dist/db/db.d.ts.map +1 -0
  60. package/dist/db/db.js +18 -0
  61. package/dist/db/db.js.map +1 -0
  62. package/dist/db/installation-token-lock.d.ts +8 -0
  63. package/dist/db/installation-token-lock.d.ts.map +1 -0
  64. package/dist/db/installation-token-lock.js +37 -0
  65. package/dist/db/installation-token-lock.js.map +1 -0
  66. package/dist/db/installations.d.ts +33 -0
  67. package/dist/db/installations.d.ts.map +1 -0
  68. package/dist/db/installations.js +56 -0
  69. package/dist/db/installations.js.map +1 -0
  70. package/dist/db/migrations.d.ts +2 -0
  71. package/dist/db/migrations.d.ts.map +1 -0
  72. package/dist/db/migrations.js +5 -0
  73. package/dist/db/migrations.js.map +1 -0
  74. package/dist/db/schema/common.d.ts +2 -0
  75. package/dist/db/schema/common.d.ts.map +1 -0
  76. package/dist/db/schema/common.js +4 -0
  77. package/dist/db/schema/common.js.map +1 -0
  78. package/dist/db/schema/installations.d.ts +218 -0
  79. package/dist/db/schema/installations.d.ts.map +1 -0
  80. package/dist/db/schema/installations.js +46 -0
  81. package/dist/db/schema/installations.js.map +1 -0
  82. package/dist/index.d.ts +53 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +66 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/metrics/index.d.ts +2 -0
  87. package/dist/metrics/index.d.ts.map +1 -0
  88. package/dist/metrics/index.js +3 -0
  89. package/dist/metrics/index.js.map +1 -0
  90. package/dist/metrics/instance.d.ts +14 -0
  91. package/dist/metrics/instance.d.ts.map +1 -0
  92. package/dist/metrics/instance.js +75 -0
  93. package/dist/metrics/instance.js.map +1 -0
  94. package/dist/presentation/dto/integrations.d.ts +15 -0
  95. package/dist/presentation/dto/integrations.d.ts.map +1 -0
  96. package/dist/presentation/dto/integrations.js +10 -0
  97. package/dist/presentation/dto/integrations.js.map +1 -0
  98. package/dist/presentation/e2eRoutes/create-connection.d.ts +11 -0
  99. package/dist/presentation/e2eRoutes/create-connection.d.ts.map +1 -0
  100. package/dist/presentation/e2eRoutes/create-connection.js +57 -0
  101. package/dist/presentation/e2eRoutes/create-connection.js.map +1 -0
  102. package/dist/presentation/e2eRoutes/index.d.ts +5 -0
  103. package/dist/presentation/e2eRoutes/index.d.ts.map +1 -0
  104. package/dist/presentation/e2eRoutes/index.js +11 -0
  105. package/dist/presentation/e2eRoutes/index.js.map +1 -0
  106. package/dist/presentation/routes/errors.d.ts +2 -0
  107. package/dist/presentation/routes/errors.d.ts.map +1 -0
  108. package/dist/presentation/routes/errors.js +46 -0
  109. package/dist/presentation/routes/errors.js.map +1 -0
  110. package/dist/presentation/routes/install.d.ts +13 -0
  111. package/dist/presentation/routes/install.d.ts.map +1 -0
  112. package/dist/presentation/routes/install.js +77 -0
  113. package/dist/presentation/routes/install.js.map +1 -0
  114. package/dist/presentation/routes/webhooks.d.ts +16 -0
  115. package/dist/presentation/routes/webhooks.d.ts.map +1 -0
  116. package/dist/presentation/routes/webhooks.js +110 -0
  117. package/dist/presentation/routes/webhooks.js.map +1 -0
  118. package/dist/tsconfig.test.tsbuildinfo +1 -0
  119. package/drizzle/0000_initial.sql +17 -0
  120. package/drizzle/meta/_journal.json +13 -0
  121. package/drizzle.config.ts +7 -0
  122. package/package.json +71 -0
  123. package/src/api/client.test.ts +94 -0
  124. package/src/api/client.ts +525 -0
  125. package/src/api/installation-token-envelope.ts +171 -0
  126. package/src/api/installation-token-provider.test.ts +241 -0
  127. package/src/api/installation-token-provider.ts +224 -0
  128. package/src/api/shared-installation-token-cache.test.ts +294 -0
  129. package/src/api/shared-installation-token-cache.ts +346 -0
  130. package/src/config.test.ts +14 -0
  131. package/src/config.ts +47 -0
  132. package/src/connection-external-url.test.ts +78 -0
  133. package/src/core/actions-logs.test.ts +156 -0
  134. package/src/core/actions-logs.ts +186 -0
  135. package/src/core/agent-tool-selection.test.ts +41 -0
  136. package/src/core/agent-tool-selection.ts +42 -0
  137. package/src/core/agent-tools.test.ts +539 -0
  138. package/src/core/agent-tools.ts +1269 -0
  139. package/src/core/errors.ts +23 -0
  140. package/src/core/install.test.ts +238 -0
  141. package/src/core/install.ts +119 -0
  142. package/src/core/source-control.test.ts +327 -0
  143. package/src/core/source-control.ts +223 -0
  144. package/src/core/state.test.ts +43 -0
  145. package/src/core/state.ts +85 -0
  146. package/src/core/webhook.test.ts +522 -0
  147. package/src/core/webhook.ts +296 -0
  148. package/src/db/db.ts +18 -0
  149. package/src/db/installation-token-lock.test.ts +68 -0
  150. package/src/db/installation-token-lock.ts +39 -0
  151. package/src/db/installations.test.ts +67 -0
  152. package/src/db/installations.ts +108 -0
  153. package/src/db/migrations.ts +4 -0
  154. package/src/db/schema/common.ts +3 -0
  155. package/src/db/schema/installations.ts +46 -0
  156. package/src/index.ts +124 -0
  157. package/src/metrics/index.ts +1 -0
  158. package/src/metrics/instance.ts +82 -0
  159. package/src/presentation/dto/integrations.ts +8 -0
  160. package/src/presentation/e2eRoutes/create-connection.ts +70 -0
  161. package/src/presentation/e2eRoutes/index.test.ts +126 -0
  162. package/src/presentation/e2eRoutes/index.ts +14 -0
  163. package/src/presentation/routes/errors.ts +43 -0
  164. package/src/presentation/routes/install.test.ts +279 -0
  165. package/src/presentation/routes/install.ts +93 -0
  166. package/src/presentation/routes/webhooks.test.ts +560 -0
  167. package/src/presentation/routes/webhooks.ts +115 -0
  168. package/test/env.ts +16 -0
  169. package/test/factories/github-installation.ts +35 -0
  170. package/test/factories/index.ts +1 -0
  171. package/test/fixtures/github-webhook.ts +20 -0
  172. package/test/globalSetup.ts +14 -0
  173. package/test/index.ts +2 -0
  174. package/test/setup.ts +17 -0
  175. package/tsconfig.build.json +9 -0
  176. package/tsconfig.build.tsbuildinfo +1 -0
  177. package/tsconfig.json +3 -0
  178. package/tsconfig.test.json +9 -0
  179. package/vitest.config.ts +12 -0
@@ -0,0 +1,57 @@
1
+ import { toIntegrationConnectionDto } from '@shipfox/api-integration-core-dto';
2
+ import { createE2eGithubConnectionBodySchema, createE2eGithubConnectionResponseSchema } from '@shipfox/api-integration-github-dto';
3
+ import { ClientError, defineRoute } from '@shipfox/node-fastify';
4
+ export function createE2eGithubConnectionRoute(options) {
5
+ return defineRoute({
6
+ method: 'POST',
7
+ path: '/github-connections',
8
+ description: 'Create a synthetic GitHub connection for E2E tests.',
9
+ schema: {
10
+ body: createE2eGithubConnectionBodySchema,
11
+ response: {
12
+ 201: createE2eGithubConnectionResponseSchema
13
+ }
14
+ },
15
+ handler: async (request, reply)=>{
16
+ const body = request.body;
17
+ const installationId = String(body.installation_id);
18
+ const existing = await options.getExistingGithubConnection({
19
+ installationId
20
+ });
21
+ if (existing && existing.workspaceId !== body.workspace_id) {
22
+ throw new ClientError('GitHub installation is already connected to another workspace', 'github-connection-workspace-mismatch', {
23
+ status: 409
24
+ });
25
+ }
26
+ const connection = existing ?? await options.connectGithubInstallation({
27
+ workspaceId: body.workspace_id,
28
+ installationId,
29
+ displayName: body.display_name,
30
+ installerUserId: body.installer_user_id,
31
+ installation: {
32
+ installationId,
33
+ accountLogin: body.account_login,
34
+ accountType: 'Organization',
35
+ repositorySelection: 'all',
36
+ suspendedAt: null,
37
+ deletedAt: null,
38
+ latestEvent: {
39
+ id: body.installation_id,
40
+ account: {
41
+ login: body.account_login,
42
+ type: 'Organization'
43
+ },
44
+ repository_selection: 'all'
45
+ },
46
+ installerUserId: body.installer_user_id
47
+ }
48
+ });
49
+ reply.code(201);
50
+ return toIntegrationConnectionDto(connection, {
51
+ capabilities: options.connectionCapabilities
52
+ });
53
+ }
54
+ });
55
+ }
56
+
57
+ //# sourceMappingURL=create-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/e2eRoutes/create-connection.ts"],"sourcesContent":["import type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {toIntegrationConnectionDto} from '@shipfox/api-integration-core-dto';\nimport {\n createE2eGithubConnectionBodySchema,\n createE2eGithubConnectionResponseSchema,\n} from '@shipfox/api-integration-github-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport type {ConnectGithubInstallationInput} from '#core/install.js';\n\nexport interface CreateE2eGithubConnectionRouteOptions {\n getExistingGithubConnection: (input: {\n installationId: string;\n }) => Promise<IntegrationConnection<'github'> | undefined>;\n connectGithubInstallation: (\n input: ConnectGithubInstallationInput,\n ) => Promise<IntegrationConnection<'github'>>;\n connectionCapabilities: IntegrationCapability[];\n}\n\nexport function createE2eGithubConnectionRoute(options: CreateE2eGithubConnectionRouteOptions) {\n return defineRoute({\n method: 'POST',\n path: '/github-connections',\n description: 'Create a synthetic GitHub connection for E2E tests.',\n schema: {\n body: createE2eGithubConnectionBodySchema,\n response: {201: createE2eGithubConnectionResponseSchema},\n },\n handler: async (request, reply) => {\n const body = request.body;\n const installationId = String(body.installation_id);\n const existing = await options.getExistingGithubConnection({installationId});\n if (existing && existing.workspaceId !== body.workspace_id) {\n throw new ClientError(\n 'GitHub installation is already connected to another workspace',\n 'github-connection-workspace-mismatch',\n {status: 409},\n );\n }\n\n const connection =\n existing ??\n (await options.connectGithubInstallation({\n workspaceId: body.workspace_id,\n installationId,\n displayName: body.display_name,\n installerUserId: body.installer_user_id,\n installation: {\n installationId,\n accountLogin: body.account_login,\n accountType: 'Organization',\n repositorySelection: 'all',\n suspendedAt: null,\n deletedAt: null,\n latestEvent: {\n id: body.installation_id,\n account: {login: body.account_login, type: 'Organization'},\n repository_selection: 'all',\n },\n installerUserId: body.installer_user_id,\n },\n }));\n\n reply.code(201);\n return toIntegrationConnectionDto(connection, {\n capabilities: options.connectionCapabilities,\n });\n },\n });\n}\n"],"names":["toIntegrationConnectionDto","createE2eGithubConnectionBodySchema","createE2eGithubConnectionResponseSchema","ClientError","defineRoute","createE2eGithubConnectionRoute","options","method","path","description","schema","body","response","handler","request","reply","installationId","String","installation_id","existing","getExistingGithubConnection","workspaceId","workspace_id","status","connection","connectGithubInstallation","displayName","display_name","installerUserId","installer_user_id","installation","accountLogin","account_login","accountType","repositorySelection","suspendedAt","deletedAt","latestEvent","id","account","login","type","repository_selection","code","capabilities","connectionCapabilities"],"mappings":"AACA,SAAQA,0BAA0B,QAAO,oCAAoC;AAC7E,SACEC,mCAAmC,EACnCC,uCAAuC,QAClC,sCAAsC;AAC7C,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAa/D,OAAO,SAASC,+BAA+BC,OAA8C;IAC3F,OAAOF,YAAY;QACjBG,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,MAAMV;YACNW,UAAU;gBAAC,KAAKV;YAAuC;QACzD;QACAW,SAAS,OAAOC,SAASC;YACvB,MAAMJ,OAAOG,QAAQH,IAAI;YACzB,MAAMK,iBAAiBC,OAAON,KAAKO,eAAe;YAClD,MAAMC,WAAW,MAAMb,QAAQc,2BAA2B,CAAC;gBAACJ;YAAc;YAC1E,IAAIG,YAAYA,SAASE,WAAW,KAAKV,KAAKW,YAAY,EAAE;gBAC1D,MAAM,IAAInB,YACR,iEACA,wCACA;oBAACoB,QAAQ;gBAAG;YAEhB;YAEA,MAAMC,aACJL,YACC,MAAMb,QAAQmB,yBAAyB,CAAC;gBACvCJ,aAAaV,KAAKW,YAAY;gBAC9BN;gBACAU,aAAaf,KAAKgB,YAAY;gBAC9BC,iBAAiBjB,KAAKkB,iBAAiB;gBACvCC,cAAc;oBACZd;oBACAe,cAAcpB,KAAKqB,aAAa;oBAChCC,aAAa;oBACbC,qBAAqB;oBACrBC,aAAa;oBACbC,WAAW;oBACXC,aAAa;wBACXC,IAAI3B,KAAKO,eAAe;wBACxBqB,SAAS;4BAACC,OAAO7B,KAAKqB,aAAa;4BAAES,MAAM;wBAAc;wBACzDC,sBAAsB;oBACxB;oBACAd,iBAAiBjB,KAAKkB,iBAAiB;gBACzC;YACF;YAEFd,MAAM4B,IAAI,CAAC;YACX,OAAO3C,2BAA2BwB,YAAY;gBAC5CoB,cAActC,QAAQuC,sBAAsB;YAC9C;QACF;IACF;AACF"}
@@ -0,0 +1,5 @@
1
+ import type { RouteGroup } from '@shipfox/node-fastify';
2
+ import { type CreateE2eGithubConnectionRouteOptions } from './create-connection.js';
3
+ export type CreateGithubE2eRoutesOptions = CreateE2eGithubConnectionRouteOptions;
4
+ export declare function createGithubE2eRoutes(options: CreateGithubE2eRoutesOptions): RouteGroup;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/e2eRoutes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACtD,OAAO,EACL,KAAK,qCAAqC,EAE3C,MAAM,wBAAwB,CAAC;AAEhC,MAAM,MAAM,4BAA4B,GAAG,qCAAqC,CAAC;AAEjF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,UAAU,CAKvF"}
@@ -0,0 +1,11 @@
1
+ import { createE2eGithubConnectionRoute } from './create-connection.js';
2
+ export function createGithubE2eRoutes(options) {
3
+ return {
4
+ prefix: '/integrations',
5
+ routes: [
6
+ createE2eGithubConnectionRoute(options)
7
+ ]
8
+ };
9
+ }
10
+
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/e2eRoutes/index.ts"],"sourcesContent":["import type {RouteGroup} from '@shipfox/node-fastify';\nimport {\n type CreateE2eGithubConnectionRouteOptions,\n createE2eGithubConnectionRoute,\n} from './create-connection.js';\n\nexport type CreateGithubE2eRoutesOptions = CreateE2eGithubConnectionRouteOptions;\n\nexport function createGithubE2eRoutes(options: CreateGithubE2eRoutesOptions): RouteGroup {\n return {\n prefix: '/integrations',\n routes: [createE2eGithubConnectionRoute(options)],\n };\n}\n"],"names":["createE2eGithubConnectionRoute","createGithubE2eRoutes","options","prefix","routes"],"mappings":"AACA,SAEEA,8BAA8B,QACzB,yBAAyB;AAIhC,OAAO,SAASC,sBAAsBC,OAAqC;IACzE,OAAO;QACLC,QAAQ;QACRC,QAAQ;YAACJ,+BAA+BE;SAAS;IACnD;AACF"}
@@ -0,0 +1,2 @@
1
+ export declare function githubRouteErrorHandler(error: unknown): never;
2
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/errors.ts"],"names":[],"mappings":"AAmBA,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAuB7D"}
@@ -0,0 +1,46 @@
1
+ import { ConnectionSlugConflictError } from '@shipfox/api-integration-core-dto';
2
+ import { ClientError } from '@shipfox/node-fastify';
3
+ import { GithubInstallationAlreadyLinkedError, GithubInstallationNotAuthorizedError, GithubInstallStateActorMismatchError, GithubInstallStateError, GithubIntegrationProviderError } from '#core/errors.js';
4
+ function providerStatus(reason) {
5
+ if (reason === 'rate-limited') return 429;
6
+ if (reason === 'timeout' || reason === 'provider-unavailable') return 503;
7
+ return 422;
8
+ }
9
+ export function githubRouteErrorHandler(error) {
10
+ if (error instanceof GithubInstallStateError) {
11
+ throw new ClientError(error.message, 'invalid-github-install-state', {
12
+ status: 400
13
+ });
14
+ }
15
+ if (error instanceof GithubInstallStateActorMismatchError) {
16
+ throw new ClientError(error.message, 'github-install-state-actor-mismatch', {
17
+ status: 403
18
+ });
19
+ }
20
+ if (error instanceof GithubInstallationNotAuthorizedError) {
21
+ throw new ClientError(error.message, 'github-installation-not-authorized', {
22
+ status: 403
23
+ });
24
+ }
25
+ if (error instanceof GithubInstallationAlreadyLinkedError) {
26
+ throw new ClientError(error.message, 'github-installation-already-linked', {
27
+ status: 409
28
+ });
29
+ }
30
+ if (error instanceof ConnectionSlugConflictError) {
31
+ throw new ClientError(error.message, 'slug-conflict', {
32
+ status: 409
33
+ });
34
+ }
35
+ if (error instanceof GithubIntegrationProviderError) {
36
+ throw new ClientError(error.message, error.reason, {
37
+ details: {
38
+ retry_after_seconds: error.retryAfterSeconds
39
+ },
40
+ status: providerStatus(error.reason)
41
+ });
42
+ }
43
+ throw error;
44
+ }
45
+
46
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/routes/errors.ts"],"sourcesContent":["import {\n ConnectionSlugConflictError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-core-dto';\nimport {ClientError} from '@shipfox/node-fastify';\nimport {\n GithubInstallationAlreadyLinkedError,\n GithubInstallationNotAuthorizedError,\n GithubInstallStateActorMismatchError,\n GithubInstallStateError,\n GithubIntegrationProviderError,\n} from '#core/errors.js';\n\nfunction providerStatus(reason: IntegrationProviderErrorReason): number {\n if (reason === 'rate-limited') return 429;\n if (reason === 'timeout' || reason === 'provider-unavailable') return 503;\n return 422;\n}\n\nexport function githubRouteErrorHandler(error: unknown): never {\n if (error instanceof GithubInstallStateError) {\n throw new ClientError(error.message, 'invalid-github-install-state', {status: 400});\n }\n if (error instanceof GithubInstallStateActorMismatchError) {\n throw new ClientError(error.message, 'github-install-state-actor-mismatch', {status: 403});\n }\n if (error instanceof GithubInstallationNotAuthorizedError) {\n throw new ClientError(error.message, 'github-installation-not-authorized', {status: 403});\n }\n if (error instanceof GithubInstallationAlreadyLinkedError) {\n throw new ClientError(error.message, 'github-installation-already-linked', {status: 409});\n }\n if (error instanceof ConnectionSlugConflictError) {\n throw new ClientError(error.message, 'slug-conflict', {status: 409});\n }\n if (error instanceof GithubIntegrationProviderError) {\n throw new ClientError(error.message, error.reason, {\n details: {retry_after_seconds: error.retryAfterSeconds},\n status: providerStatus(error.reason),\n });\n }\n throw error;\n}\n"],"names":["ConnectionSlugConflictError","ClientError","GithubInstallationAlreadyLinkedError","GithubInstallationNotAuthorizedError","GithubInstallStateActorMismatchError","GithubInstallStateError","GithubIntegrationProviderError","providerStatus","reason","githubRouteErrorHandler","error","message","status","details","retry_after_seconds","retryAfterSeconds"],"mappings":"AAAA,SACEA,2BAA2B,QAEtB,oCAAoC;AAC3C,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SACEC,oCAAoC,EACpCC,oCAAoC,EACpCC,oCAAoC,EACpCC,uBAAuB,EACvBC,8BAA8B,QACzB,kBAAkB;AAEzB,SAASC,eAAeC,MAAsC;IAC5D,IAAIA,WAAW,gBAAgB,OAAO;IACtC,IAAIA,WAAW,aAAaA,WAAW,wBAAwB,OAAO;IACtE,OAAO;AACT;AAEA,OAAO,SAASC,wBAAwBC,KAAc;IACpD,IAAIA,iBAAiBL,yBAAyB;QAC5C,MAAM,IAAIJ,YAAYS,MAAMC,OAAO,EAAE,gCAAgC;YAACC,QAAQ;QAAG;IACnF;IACA,IAAIF,iBAAiBN,sCAAsC;QACzD,MAAM,IAAIH,YAAYS,MAAMC,OAAO,EAAE,uCAAuC;YAACC,QAAQ;QAAG;IAC1F;IACA,IAAIF,iBAAiBP,sCAAsC;QACzD,MAAM,IAAIF,YAAYS,MAAMC,OAAO,EAAE,sCAAsC;YAACC,QAAQ;QAAG;IACzF;IACA,IAAIF,iBAAiBR,sCAAsC;QACzD,MAAM,IAAID,YAAYS,MAAMC,OAAO,EAAE,sCAAsC;YAACC,QAAQ;QAAG;IACzF;IACA,IAAIF,iBAAiBV,6BAA6B;QAChD,MAAM,IAAIC,YAAYS,MAAMC,OAAO,EAAE,iBAAiB;YAACC,QAAQ;QAAG;IACpE;IACA,IAAIF,iBAAiBJ,gCAAgC;QACnD,MAAM,IAAIL,YAAYS,MAAMC,OAAO,EAAED,MAAMF,MAAM,EAAE;YACjDK,SAAS;gBAACC,qBAAqBJ,MAAMK,iBAAiB;YAAA;YACtDH,QAAQL,eAAeG,MAAMF,MAAM;QACrC;IACF;IACA,MAAME;AACR"}
@@ -0,0 +1,13 @@
1
+ import type { IntegrationConnection } from '@shipfox/api-integration-core-dto';
2
+ import { type RouteGroup } from '@shipfox/node-fastify';
3
+ import type { GithubApiClient } from '#api/client.js';
4
+ import { type ConnectGithubInstallationInput } from '#core/install.js';
5
+ export interface CreateGithubIntegrationRoutesOptions {
6
+ github: GithubApiClient;
7
+ getExistingGithubConnection: (input: {
8
+ installationId: string;
9
+ }) => Promise<IntegrationConnection<'github'> | undefined>;
10
+ connectGithubInstallation: (input: ConnectGithubInstallationInput) => Promise<IntegrationConnection<'github'>>;
11
+ }
12
+ export declare function createGithubIntegrationRoutes({ github, getExistingGithubConnection, connectGithubInstallation, }: CreateGithubIntegrationRoutesOptions): RouteGroup;
13
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;AAQ7E,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAC,KAAK,8BAA8B,EAAuB,MAAM,kBAAkB,CAAC;AAK3F,MAAM,WAAW,oCAAoC;IACnD,MAAM,EAAE,eAAe,CAAC;IACxB,2BAA2B,EAAE,CAAC,KAAK,EAAE;QACnC,cAAc,EAAE,MAAM,CAAC;KACxB,KAAK,OAAO,CAAC,qBAAqB,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC,CAAC;IAC3D,yBAAyB,EAAE,CACzB,KAAK,EAAE,8BAA8B,KAClC,OAAO,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC;CAC/C;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,MAAM,EACN,2BAA2B,EAC3B,yBAAyB,GAC1B,EAAE,oCAAoC,GAAG,UAAU,CA6DnD"}
@@ -0,0 +1,77 @@
1
+ import { AUTH_USER, requireUserContext, requireWorkspaceAccess } from '@shipfox/api-auth-context';
2
+ import { createGithubInstallBodySchema, createGithubInstallResponseSchema, githubCallbackQuerySchema, githubCallbackResponseSchema } from '@shipfox/api-integration-github-dto';
3
+ import { requireWorkspaceMembership } from '@shipfox/api-workspaces';
4
+ import { defineRoute } from '@shipfox/node-fastify';
5
+ import { config } from '#config.js';
6
+ import { handleGithubCallback } from '#core/install.js';
7
+ import { signGithubInstallState } from '#core/state.js';
8
+ import { toIntegrationConnectionDto } from '#presentation/dto/integrations.js';
9
+ import { githubRouteErrorHandler } from './errors.js';
10
+ export function createGithubIntegrationRoutes({ github, getExistingGithubConnection, connectGithubInstallation }) {
11
+ const createInstallRoute = defineRoute({
12
+ method: 'POST',
13
+ path: '/install',
14
+ auth: AUTH_USER,
15
+ description: 'Create a GitHub App installation URL for a workspace.',
16
+ schema: {
17
+ body: createGithubInstallBodySchema,
18
+ response: {
19
+ 200: createGithubInstallResponseSchema
20
+ }
21
+ },
22
+ handler: (request)=>{
23
+ const { workspace_id: workspaceId } = request.body;
24
+ const actor = requireUserContext(request);
25
+ requireWorkspaceAccess({
26
+ request,
27
+ workspaceId
28
+ });
29
+ const state = signGithubInstallState({
30
+ workspaceId,
31
+ userId: actor.userId
32
+ });
33
+ const installUrl = new URL(`https://github.com/apps/${config.GITHUB_APP_SLUG}/installations/new`);
34
+ installUrl.searchParams.set('state', state);
35
+ return {
36
+ install_url: installUrl.toString()
37
+ };
38
+ }
39
+ });
40
+ const callbackApiRoute = defineRoute({
41
+ method: 'GET',
42
+ path: '/callback/api',
43
+ auth: AUTH_USER,
44
+ description: 'Handle the GitHub App installation callback.',
45
+ schema: {
46
+ querystring: githubCallbackQuerySchema,
47
+ response: {
48
+ 200: githubCallbackResponseSchema
49
+ }
50
+ },
51
+ errorHandler: githubRouteErrorHandler,
52
+ handler: async (request)=>{
53
+ const actor = requireUserContext(request);
54
+ const connection = await handleGithubCallback({
55
+ github,
56
+ code: request.query.code,
57
+ installationId: request.query.installation_id,
58
+ state: request.query.state,
59
+ sessionUserId: actor.userId,
60
+ sessionMemberships: actor.memberships,
61
+ requireWorkspaceMembership,
62
+ getExistingGithubConnection,
63
+ connectGithubInstallation
64
+ });
65
+ return toIntegrationConnectionDto(connection);
66
+ }
67
+ });
68
+ return {
69
+ prefix: '/integrations/github',
70
+ routes: [
71
+ createInstallRoute,
72
+ callbackApiRoute
73
+ ]
74
+ };
75
+ }
76
+
77
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport type {IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {\n createGithubInstallBodySchema,\n createGithubInstallResponseSchema,\n githubCallbackQuerySchema,\n githubCallbackResponseSchema,\n} from '@shipfox/api-integration-github-dto';\nimport {requireWorkspaceMembership} from '@shipfox/api-workspaces';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {GithubApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {type ConnectGithubInstallationInput, handleGithubCallback} from '#core/install.js';\nimport {signGithubInstallState} from '#core/state.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\nimport {githubRouteErrorHandler} from './errors.js';\n\nexport interface CreateGithubIntegrationRoutesOptions {\n github: GithubApiClient;\n getExistingGithubConnection: (input: {\n installationId: string;\n }) => Promise<IntegrationConnection<'github'> | undefined>;\n connectGithubInstallation: (\n input: ConnectGithubInstallationInput,\n ) => Promise<IntegrationConnection<'github'>>;\n}\n\nexport function createGithubIntegrationRoutes({\n github,\n getExistingGithubConnection,\n connectGithubInstallation,\n}: CreateGithubIntegrationRoutesOptions): RouteGroup {\n const createInstallRoute = defineRoute({\n method: 'POST',\n path: '/install',\n auth: AUTH_USER,\n description: 'Create a GitHub App installation URL for a workspace.',\n schema: {\n body: createGithubInstallBodySchema,\n response: {\n 200: createGithubInstallResponseSchema,\n },\n },\n handler: (request) => {\n const {workspace_id: workspaceId} = request.body;\n const actor = requireUserContext(request);\n\n requireWorkspaceAccess({request, workspaceId});\n const state = signGithubInstallState({workspaceId, userId: actor.userId});\n const installUrl = new URL(\n `https://github.com/apps/${config.GITHUB_APP_SLUG}/installations/new`,\n );\n installUrl.searchParams.set('state', state);\n\n return {install_url: installUrl.toString()};\n },\n });\n\n const callbackApiRoute = defineRoute({\n method: 'GET',\n path: '/callback/api',\n auth: AUTH_USER,\n description: 'Handle the GitHub App installation callback.',\n schema: {\n querystring: githubCallbackQuerySchema,\n response: {\n 200: githubCallbackResponseSchema,\n },\n },\n errorHandler: githubRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const connection = await handleGithubCallback({\n github,\n code: request.query.code,\n installationId: request.query.installation_id,\n state: request.query.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership,\n getExistingGithubConnection,\n connectGithubInstallation,\n });\n\n return toIntegrationConnectionDto(connection);\n },\n });\n\n return {\n prefix: '/integrations/github',\n routes: [createInstallRoute, callbackApiRoute],\n };\n}\n"],"names":["AUTH_USER","requireUserContext","requireWorkspaceAccess","createGithubInstallBodySchema","createGithubInstallResponseSchema","githubCallbackQuerySchema","githubCallbackResponseSchema","requireWorkspaceMembership","defineRoute","config","handleGithubCallback","signGithubInstallState","toIntegrationConnectionDto","githubRouteErrorHandler","createGithubIntegrationRoutes","github","getExistingGithubConnection","connectGithubInstallation","createInstallRoute","method","path","auth","description","schema","body","response","handler","request","workspace_id","workspaceId","actor","state","userId","installUrl","URL","GITHUB_APP_SLUG","searchParams","set","install_url","toString","callbackApiRoute","querystring","errorHandler","connection","code","query","installationId","installation_id","sessionUserId","sessionMemberships","memberships","prefix","routes"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,kBAAkB,EAAEC,sBAAsB,QAAO,4BAA4B;AAEhG,SACEC,6BAA6B,EAC7BC,iCAAiC,EACjCC,yBAAyB,EACzBC,4BAA4B,QACvB,sCAAsC;AAC7C,SAAQC,0BAA0B,QAAO,0BAA0B;AACnE,SAAQC,WAAW,QAAwB,wBAAwB;AAEnE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAA6CC,oBAAoB,QAAO,mBAAmB;AAC3F,SAAQC,sBAAsB,QAAO,iBAAiB;AACtD,SAAQC,0BAA0B,QAAO,oCAAoC;AAC7E,SAAQC,uBAAuB,QAAO,cAAc;AAYpD,OAAO,SAASC,8BAA8B,EAC5CC,MAAM,EACNC,2BAA2B,EAC3BC,yBAAyB,EACY;IACrC,MAAMC,qBAAqBV,YAAY;QACrCW,QAAQ;QACRC,MAAM;QACNC,MAAMrB;QACNsB,aAAa;QACbC,QAAQ;YACNC,MAAMrB;YACNsB,UAAU;gBACR,KAAKrB;YACP;QACF;QACAsB,SAAS,CAACC;YACR,MAAM,EAACC,cAAcC,WAAW,EAAC,GAAGF,QAAQH,IAAI;YAChD,MAAMM,QAAQ7B,mBAAmB0B;YAEjCzB,uBAAuB;gBAACyB;gBAASE;YAAW;YAC5C,MAAME,QAAQpB,uBAAuB;gBAACkB;gBAAaG,QAAQF,MAAME,MAAM;YAAA;YACvE,MAAMC,aAAa,IAAIC,IACrB,CAAC,wBAAwB,EAAEzB,OAAO0B,eAAe,CAAC,kBAAkB,CAAC;YAEvEF,WAAWG,YAAY,CAACC,GAAG,CAAC,SAASN;YAErC,OAAO;gBAACO,aAAaL,WAAWM,QAAQ;YAAE;QAC5C;IACF;IAEA,MAAMC,mBAAmBhC,YAAY;QACnCW,QAAQ;QACRC,MAAM;QACNC,MAAMrB;QACNsB,aAAa;QACbC,QAAQ;YACNkB,aAAapC;YACboB,UAAU;gBACR,KAAKnB;YACP;QACF;QACAoC,cAAc7B;QACda,SAAS,OAAOC;YACd,MAAMG,QAAQ7B,mBAAmB0B;YACjC,MAAMgB,aAAa,MAAMjC,qBAAqB;gBAC5CK;gBACA6B,MAAMjB,QAAQkB,KAAK,CAACD,IAAI;gBACxBE,gBAAgBnB,QAAQkB,KAAK,CAACE,eAAe;gBAC7ChB,OAAOJ,QAAQkB,KAAK,CAACd,KAAK;gBAC1BiB,eAAelB,MAAME,MAAM;gBAC3BiB,oBAAoBnB,MAAMoB,WAAW;gBACrC3C;gBACAS;gBACAC;YACF;YAEA,OAAOL,2BAA2B+B;QACpC;IACF;IAEA,OAAO;QACLQ,QAAQ;QACRC,QAAQ;YAAClC;YAAoBsB;SAAiB;IAChD;AACF"}
@@ -0,0 +1,16 @@
1
+ import type { GetIntegrationConnectionByIdFn, PublishIntegrationEventReceivedFn, PublishSourcePushFn, RecordDeliveryOnlyFn } from '@shipfox/api-integration-core-dto';
2
+ import { type RouteGroup } from '@shipfox/node-fastify';
3
+ import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
4
+ export interface CreateGithubWebhookRoutesOptions {
5
+ coreDb: () => NodePgDatabase<Record<string, unknown>>;
6
+ publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
7
+ publishSourcePush: PublishSourcePushFn;
8
+ recordDeliveryOnly: RecordDeliveryOnlyFn;
9
+ getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
10
+ deleteInstallationTokenSecret?: ((params: {
11
+ workspaceId: string;
12
+ installationId: number;
13
+ }) => Promise<unknown>) | undefined;
14
+ }
15
+ export declare function createGithubWebhookRoutes(options: CreateGithubWebhookRoutesOptions): RouteGroup;
16
+ //# sourceMappingURL=webhooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/webhooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,8BAA8B,EAC9B,iCAAiC,EACjC,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAQ9D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,iBAAiB,EAAE,mBAAmB,CAAC;IACvC,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,6BAA6B,CAAC,EAC1B,CAAC,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAC7E,SAAS,CAAC;CACf;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gCAAgC,GAAG,UAAU,CAgF/F"}
@@ -0,0 +1,110 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { Webhooks } from '@octokit/webhooks';
3
+ import { defineRoute, rawBodyPlugin, WEBHOOK_BODY_LIMIT } from '@shipfox/node-fastify';
4
+ import { logger } from '@shipfox/node-opentelemetry';
5
+ import { config } from '#config.js';
6
+ import { handleGithubEvent } from '#core/webhook.js';
7
+ const SIGNATURE_HEADER = 'x-hub-signature-256';
8
+ const EVENT_HEADER = 'x-github-event';
9
+ const DELIVERY_HEADER = 'x-github-delivery';
10
+ export function createGithubWebhookRoutes(options) {
11
+ const webhooks = new Webhooks({
12
+ secret: config.GITHUB_APP_WEBHOOK_SECRET
13
+ });
14
+ const pushRoute = defineRoute({
15
+ method: 'POST',
16
+ path: '/',
17
+ auth: [],
18
+ description: 'GitHub App webhook receiver.',
19
+ options: {
20
+ bodyLimit: WEBHOOK_BODY_LIMIT
21
+ },
22
+ handler: async (request, reply)=>{
23
+ const deliveryId = request.headers[DELIVERY_HEADER];
24
+ const signature = request.headers[SIGNATURE_HEADER];
25
+ const event = request.headers[EVENT_HEADER];
26
+ if (typeof deliveryId !== 'string' || !deliveryId) {
27
+ reply.code(400);
28
+ return {
29
+ error: 'missing X-GitHub-Delivery header'
30
+ };
31
+ }
32
+ if (typeof signature !== 'string' || !signature) {
33
+ reply.code(401);
34
+ return {
35
+ error: 'missing X-Hub-Signature-256 header'
36
+ };
37
+ }
38
+ if (typeof event !== 'string' || !event) {
39
+ reply.code(400);
40
+ return {
41
+ error: 'missing X-GitHub-Event header'
42
+ };
43
+ }
44
+ const body = request.body;
45
+ if (!Buffer.isBuffer(body)) {
46
+ reply.code(400);
47
+ return {
48
+ error: 'expected raw JSON body'
49
+ };
50
+ }
51
+ const rawBody = body.toString('utf8');
52
+ let verified;
53
+ try {
54
+ verified = await webhooks.verify(rawBody, signature);
55
+ } catch (error) {
56
+ logger().warn({
57
+ deliveryId,
58
+ err: error
59
+ }, 'github webhook signature verification threw');
60
+ verified = false;
61
+ }
62
+ if (!verified) {
63
+ reply.code(401);
64
+ return {
65
+ error: 'invalid signature'
66
+ };
67
+ }
68
+ let parsedJson;
69
+ try {
70
+ parsedJson = JSON.parse(rawBody);
71
+ } catch (error) {
72
+ logger().warn({
73
+ deliveryId,
74
+ err: error
75
+ }, 'github webhook payload JSON parse failed');
76
+ reply.code(400);
77
+ return {
78
+ error: 'malformed JSON'
79
+ };
80
+ }
81
+ await options.coreDb().transaction(async (tx)=>{
82
+ await handleGithubEvent({
83
+ tx,
84
+ deliveryId,
85
+ event,
86
+ payload: parsedJson,
87
+ publishIntegrationEventReceived: options.publishIntegrationEventReceived,
88
+ publishSourcePush: options.publishSourcePush,
89
+ recordDeliveryOnly: options.recordDeliveryOnly,
90
+ getIntegrationConnectionById: options.getIntegrationConnectionById,
91
+ deleteInstallationTokenSecret: options.deleteInstallationTokenSecret
92
+ });
93
+ });
94
+ reply.code(204);
95
+ return null;
96
+ }
97
+ });
98
+ return {
99
+ prefix: '/webhooks/integrations/github',
100
+ auth: [],
101
+ plugins: [
102
+ rawBodyPlugin
103
+ ],
104
+ routes: [
105
+ pushRoute
106
+ ]
107
+ };
108
+ }
109
+
110
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/routes/webhooks.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {Webhooks} from '@octokit/webhooks';\nimport type {\n GetIntegrationConnectionByIdFn,\n PublishIntegrationEventReceivedFn,\n PublishSourcePushFn,\n RecordDeliveryOnlyFn,\n} from '@shipfox/api-integration-core-dto';\nimport {\n defineRoute,\n type RouteGroup,\n rawBodyPlugin,\n WEBHOOK_BODY_LIMIT,\n} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {config} from '#config.js';\nimport {handleGithubEvent} from '#core/webhook.js';\n\nconst SIGNATURE_HEADER = 'x-hub-signature-256';\nconst EVENT_HEADER = 'x-github-event';\nconst DELIVERY_HEADER = 'x-github-delivery';\n\nexport interface CreateGithubWebhookRoutesOptions {\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n publishSourcePush: PublishSourcePushFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n deleteInstallationTokenSecret?:\n | ((params: {workspaceId: string; installationId: number}) => Promise<unknown>)\n | undefined;\n}\n\nexport function createGithubWebhookRoutes(options: CreateGithubWebhookRoutesOptions): RouteGroup {\n const webhooks = new Webhooks({secret: config.GITHUB_APP_WEBHOOK_SECRET});\n\n const pushRoute = defineRoute({\n method: 'POST',\n path: '/',\n auth: [],\n description: 'GitHub App webhook receiver.',\n options: {bodyLimit: WEBHOOK_BODY_LIMIT},\n handler: async (request, reply) => {\n const deliveryId = request.headers[DELIVERY_HEADER];\n const signature = request.headers[SIGNATURE_HEADER];\n const event = request.headers[EVENT_HEADER];\n\n if (typeof deliveryId !== 'string' || !deliveryId) {\n reply.code(400);\n return {error: 'missing X-GitHub-Delivery header'};\n }\n if (typeof signature !== 'string' || !signature) {\n reply.code(401);\n return {error: 'missing X-Hub-Signature-256 header'};\n }\n if (typeof event !== 'string' || !event) {\n reply.code(400);\n return {error: 'missing X-GitHub-Event header'};\n }\n\n const body = request.body;\n if (!Buffer.isBuffer(body)) {\n reply.code(400);\n return {error: 'expected raw JSON body'};\n }\n const rawBody = body.toString('utf8');\n\n let verified: boolean;\n try {\n verified = await webhooks.verify(rawBody, signature);\n } catch (error) {\n logger().warn({deliveryId, err: error}, 'github webhook signature verification threw');\n verified = false;\n }\n if (!verified) {\n reply.code(401);\n return {error: 'invalid signature'};\n }\n\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(rawBody);\n } catch (error) {\n logger().warn({deliveryId, err: error}, 'github webhook payload JSON parse failed');\n reply.code(400);\n return {error: 'malformed JSON'};\n }\n\n await options.coreDb().transaction(async (tx) => {\n await handleGithubEvent({\n tx,\n deliveryId,\n event,\n payload: parsedJson,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n publishSourcePush: options.publishSourcePush,\n recordDeliveryOnly: options.recordDeliveryOnly,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n deleteInstallationTokenSecret: options.deleteInstallationTokenSecret,\n });\n });\n\n reply.code(204);\n return null;\n },\n });\n\n return {\n prefix: '/webhooks/integrations/github',\n auth: [],\n plugins: [rawBodyPlugin],\n routes: [pushRoute],\n };\n}\n"],"names":["Buffer","Webhooks","defineRoute","rawBodyPlugin","WEBHOOK_BODY_LIMIT","logger","config","handleGithubEvent","SIGNATURE_HEADER","EVENT_HEADER","DELIVERY_HEADER","createGithubWebhookRoutes","options","webhooks","secret","GITHUB_APP_WEBHOOK_SECRET","pushRoute","method","path","auth","description","bodyLimit","handler","request","reply","deliveryId","headers","signature","event","code","error","body","isBuffer","rawBody","toString","verified","verify","warn","err","parsedJson","JSON","parse","coreDb","transaction","tx","payload","publishIntegrationEventReceived","publishSourcePush","recordDeliveryOnly","getIntegrationConnectionById","deleteInstallationTokenSecret","prefix","plugins","routes"],"mappings":"AAAA,SAAQA,MAAM,QAAO,cAAc;AACnC,SAAQC,QAAQ,QAAO,oBAAoB;AAO3C,SACEC,WAAW,EAEXC,aAAa,EACbC,kBAAkB,QACb,wBAAwB;AAC/B,SAAQC,MAAM,QAAO,8BAA8B;AAEnD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,QAAO,mBAAmB;AAEnD,MAAMC,mBAAmB;AACzB,MAAMC,eAAe;AACrB,MAAMC,kBAAkB;AAaxB,OAAO,SAASC,0BAA0BC,OAAyC;IACjF,MAAMC,WAAW,IAAIZ,SAAS;QAACa,QAAQR,OAAOS,yBAAyB;IAAA;IAEvE,MAAMC,YAAYd,YAAY;QAC5Be,QAAQ;QACRC,MAAM;QACNC,MAAM,EAAE;QACRC,aAAa;QACbR,SAAS;YAACS,WAAWjB;QAAkB;QACvCkB,SAAS,OAAOC,SAASC;YACvB,MAAMC,aAAaF,QAAQG,OAAO,CAAChB,gBAAgB;YACnD,MAAMiB,YAAYJ,QAAQG,OAAO,CAAClB,iBAAiB;YACnD,MAAMoB,QAAQL,QAAQG,OAAO,CAACjB,aAAa;YAE3C,IAAI,OAAOgB,eAAe,YAAY,CAACA,YAAY;gBACjDD,MAAMK,IAAI,CAAC;gBACX,OAAO;oBAACC,OAAO;gBAAkC;YACnD;YACA,IAAI,OAAOH,cAAc,YAAY,CAACA,WAAW;gBAC/CH,MAAMK,IAAI,CAAC;gBACX,OAAO;oBAACC,OAAO;gBAAoC;YACrD;YACA,IAAI,OAAOF,UAAU,YAAY,CAACA,OAAO;gBACvCJ,MAAMK,IAAI,CAAC;gBACX,OAAO;oBAACC,OAAO;gBAA+B;YAChD;YAEA,MAAMC,OAAOR,QAAQQ,IAAI;YACzB,IAAI,CAAC/B,OAAOgC,QAAQ,CAACD,OAAO;gBAC1BP,MAAMK,IAAI,CAAC;gBACX,OAAO;oBAACC,OAAO;gBAAwB;YACzC;YACA,MAAMG,UAAUF,KAAKG,QAAQ,CAAC;YAE9B,IAAIC;YACJ,IAAI;gBACFA,WAAW,MAAMtB,SAASuB,MAAM,CAACH,SAASN;YAC5C,EAAE,OAAOG,OAAO;gBACdzB,SAASgC,IAAI,CAAC;oBAACZ;oBAAYa,KAAKR;gBAAK,GAAG;gBACxCK,WAAW;YACb;YACA,IAAI,CAACA,UAAU;gBACbX,MAAMK,IAAI,CAAC;gBACX,OAAO;oBAACC,OAAO;gBAAmB;YACpC;YAEA,IAAIS;YACJ,IAAI;gBACFA,aAAaC,KAAKC,KAAK,CAACR;YAC1B,EAAE,OAAOH,OAAO;gBACdzB,SAASgC,IAAI,CAAC;oBAACZ;oBAAYa,KAAKR;gBAAK,GAAG;gBACxCN,MAAMK,IAAI,CAAC;gBACX,OAAO;oBAACC,OAAO;gBAAgB;YACjC;YAEA,MAAMlB,QAAQ8B,MAAM,GAAGC,WAAW,CAAC,OAAOC;gBACxC,MAAMrC,kBAAkB;oBACtBqC;oBACAnB;oBACAG;oBACAiB,SAASN;oBACTO,iCAAiClC,QAAQkC,+BAA+B;oBACxEC,mBAAmBnC,QAAQmC,iBAAiB;oBAC5CC,oBAAoBpC,QAAQoC,kBAAkB;oBAC9CC,8BAA8BrC,QAAQqC,4BAA4B;oBAClEC,+BAA+BtC,QAAQsC,6BAA6B;gBACtE;YACF;YAEA1B,MAAMK,IAAI,CAAC;YACX,OAAO;QACT;IACF;IAEA,OAAO;QACLsB,QAAQ;QACRhC,MAAM,EAAE;QACRiC,SAAS;YAACjD;SAAc;QACxBkD,QAAQ;YAACrC;SAAU;IACrB;AACF"}