@shipfox/api-integration-slack 8.0.0 → 9.0.2

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 (59) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +52 -0
  3. package/dist/core/agent-tools-provider.d.ts +9 -9
  4. package/dist/core/agent-tools-provider.d.ts.map +1 -1
  5. package/dist/core/agent-tools-provider.js.map +1 -1
  6. package/dist/core/agent-tools.d.ts +1 -1
  7. package/dist/core/agent-tools.d.ts.map +1 -1
  8. package/dist/core/agent-tools.js.map +1 -1
  9. package/dist/core/errors.d.ts +1 -1
  10. package/dist/core/errors.d.ts.map +1 -1
  11. package/dist/core/errors.js +1 -1
  12. package/dist/core/errors.js.map +1 -1
  13. package/dist/core/install.d.ts +1 -1
  14. package/dist/core/install.d.ts.map +1 -1
  15. package/dist/core/install.js.map +1 -1
  16. package/dist/core/webhook-processor.d.ts +1 -1
  17. package/dist/core/webhook-processor.d.ts.map +1 -1
  18. package/dist/core/webhook-processor.js +1 -1
  19. package/dist/core/webhook-processor.js.map +1 -1
  20. package/dist/core/webhook.d.ts +1 -1
  21. package/dist/core/webhook.d.ts.map +1 -1
  22. package/dist/core/webhook.js.map +1 -1
  23. package/dist/presentation/dto/integrations.d.ts +15 -1
  24. package/dist/presentation/dto/integrations.d.ts.map +1 -1
  25. package/dist/presentation/dto/integrations.js +14 -1
  26. package/dist/presentation/dto/integrations.js.map +1 -1
  27. package/dist/presentation/e2eRoutes/create-connection.d.ts +1 -1
  28. package/dist/presentation/e2eRoutes/create-connection.d.ts.map +1 -1
  29. package/dist/presentation/e2eRoutes/create-connection.js.map +1 -1
  30. package/dist/presentation/routes/errors.js +1 -1
  31. package/dist/presentation/routes/errors.js.map +1 -1
  32. package/dist/presentation/routes/install.d.ts +1 -1
  33. package/dist/presentation/routes/install.d.ts.map +1 -1
  34. package/dist/presentation/routes/install.js.map +1 -1
  35. package/dist/presentation/routes/webhooks.d.ts +1 -1
  36. package/dist/presentation/routes/webhooks.d.ts.map +1 -1
  37. package/dist/presentation/routes/webhooks.js +1 -1
  38. package/dist/presentation/routes/webhooks.js.map +1 -1
  39. package/dist/tsconfig.test.tsbuildinfo +1 -1
  40. package/package.json +22 -15
  41. package/src/core/agent-tools-provider.test.ts +1 -1
  42. package/src/core/agent-tools-provider.ts +1 -1
  43. package/src/core/agent-tools.ts +1 -1
  44. package/src/core/errors.ts +1 -1
  45. package/src/core/install.test.ts +1 -1
  46. package/src/core/install.ts +1 -1
  47. package/src/core/webhook-processor.test.ts +2 -2
  48. package/src/core/webhook-processor.ts +6 -6
  49. package/src/core/webhook.test.ts +1 -1
  50. package/src/core/webhook.ts +8 -8
  51. package/src/presentation/dto/integrations.ts +19 -1
  52. package/src/presentation/e2eRoutes/create-connection.ts +1 -1
  53. package/src/presentation/e2eRoutes/index.test.ts +1 -1
  54. package/src/presentation/routes/errors.ts +1 -1
  55. package/src/presentation/routes/install.test.ts +1 -1
  56. package/src/presentation/routes/install.ts +1 -1
  57. package/src/presentation/routes/webhooks.test.ts +2 -2
  58. package/src/presentation/routes/webhooks.ts +2 -5
  59. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,3 +1,16 @@
1
- export { toIntegrationConnectionDto } from '@shipfox/api-integration-core-dto';
1
+ export function toIntegrationConnectionDto(connection, params) {
2
+ return {
3
+ id: connection.id,
4
+ workspace_id: connection.workspaceId,
5
+ provider: connection.provider,
6
+ external_account_id: connection.externalAccountId,
7
+ slug: connection.slug,
8
+ display_name: connection.displayName,
9
+ lifecycle_status: connection.lifecycleStatus,
10
+ capabilities: params.capabilities,
11
+ created_at: connection.createdAt.toISOString(),
12
+ updated_at: connection.updatedAt.toISOString()
13
+ };
14
+ }
2
15
 
3
16
  //# sourceMappingURL=integrations.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/dto/integrations.ts"],"sourcesContent":["export {toIntegrationConnectionDto} from '@shipfox/api-integration-core-dto';\n"],"names":["toIntegrationConnectionDto"],"mappings":"AAAA,SAAQA,0BAA0B,QAAO,oCAAoC"}
1
+ {"version":3,"sources":["../../../src/presentation/dto/integrations.ts"],"sourcesContent":["import type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-spi';\n\nexport function toIntegrationConnectionDto(\n connection: IntegrationConnection,\n params: {capabilities: IntegrationCapability[]},\n) {\n return {\n id: connection.id,\n workspace_id: connection.workspaceId,\n provider: connection.provider,\n external_account_id: connection.externalAccountId,\n slug: connection.slug,\n display_name: connection.displayName,\n lifecycle_status: connection.lifecycleStatus,\n capabilities: params.capabilities,\n created_at: connection.createdAt.toISOString(),\n updated_at: connection.updatedAt.toISOString(),\n };\n}\n"],"names":["toIntegrationConnectionDto","connection","params","id","workspace_id","workspaceId","provider","external_account_id","externalAccountId","slug","display_name","displayName","lifecycle_status","lifecycleStatus","capabilities","created_at","createdAt","toISOString","updated_at","updatedAt"],"mappings":"AAEA,OAAO,SAASA,2BACdC,UAAiC,EACjCC,MAA+C;IAE/C,OAAO;QACLC,IAAIF,WAAWE,EAAE;QACjBC,cAAcH,WAAWI,WAAW;QACpCC,UAAUL,WAAWK,QAAQ;QAC7BC,qBAAqBN,WAAWO,iBAAiB;QACjDC,MAAMR,WAAWQ,IAAI;QACrBC,cAAcT,WAAWU,WAAW;QACpCC,kBAAkBX,WAAWY,eAAe;QAC5CC,cAAcZ,OAAOY,YAAY;QACjCC,YAAYd,WAAWe,SAAS,CAACC,WAAW;QAC5CC,YAAYjB,WAAWkB,SAAS,CAACF,WAAW;IAC9C;AACF"}
@@ -1,4 +1,4 @@
1
- import type { IntegrationCapability, IntegrationConnection } from '@shipfox/api-integration-core-dto';
1
+ import type { IntegrationCapability, IntegrationConnection } from '@shipfox/api-integration-spi';
2
2
  import type { ConnectSlackInstallationInput } from '#core/install.js';
3
3
  import type { SlackTokenStore } from '#core/tokens.js';
4
4
  export interface CreateE2eSlackConnectionRouteOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"create-connection.d.ts","sourceRoot":"","sources":["../../../src/presentation/e2eRoutes/create-connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;AAOpG,OAAO,KAAK,EAAC,6BAA6B,EAAC,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAGrD,MAAM,WAAW,oCAAoC;IACnD,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;IACjD,0BAA0B,EAAE,CAAC,KAAK,EAAE;QAClC,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1D,wBAAwB,EAAE,CACxB,KAAK,EAAE,6BAA6B,KACjC,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,2BAA2B,EAAE,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;CACjD;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,oCAAoC,mDA4C1F"}
1
+ {"version":3,"file":"create-connection.d.ts","sourceRoot":"","sources":["../../../src/presentation/e2eRoutes/create-connection.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AAG/F,OAAO,KAAK,EAAC,6BAA6B,EAAC,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAGrD,MAAM,WAAW,oCAAoC;IACnD,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;IACjD,0BAA0B,EAAE,CAAC,KAAK,EAAE;QAClC,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1D,wBAAwB,EAAE,CACxB,KAAK,EAAE,6BAA6B,KACjC,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,2BAA2B,EAAE,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;CACjD;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,oCAAoC,mDA4C1F"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/e2eRoutes/create-connection.ts"],"sourcesContent":["import type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {\n createE2eSlackConnectionBodySchema,\n createE2eSlackConnectionResponseSchema,\n} from '@shipfox/api-integration-slack-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {ConnectSlackInstallationInput} from '#core/install.js';\nimport type {SlackTokenStore} from '#core/tokens.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\n\nexport interface CreateE2eSlackConnectionRouteOptions {\n tokenStore: Pick<SlackTokenStore, 'storeTokens'>;\n getExistingSlackConnection: (input: {\n teamId: string;\n }) => Promise<IntegrationConnection<'slack'> | undefined>;\n connectSlackInstallation: (\n input: ConnectSlackInstallationInput,\n ) => Promise<IntegrationConnection<'slack'>>;\n disconnectSlackInstallation: (input: {connectionId: string}) => Promise<void>;\n connectionCapabilities: IntegrationCapability[];\n}\n\nexport function createE2eSlackConnectionRoute(options: CreateE2eSlackConnectionRouteOptions) {\n return defineRoute({\n method: 'POST',\n path: '/slack-connections',\n description: 'Create a synthetic Slack connection for E2E tests.',\n schema: {\n body: createE2eSlackConnectionBodySchema,\n response: {201: createE2eSlackConnectionResponseSchema},\n },\n handler: async (request, reply) => {\n const body = request.body;\n const existing = await options.getExistingSlackConnection({teamId: body.team_id});\n if (existing && existing.workspaceId !== body.workspace_id) {\n throw new ClientError(\n 'Slack team is already connected to another workspace',\n 'slack-connection-workspace-mismatch',\n {status: 409},\n );\n }\n const connection =\n existing ??\n (await options.connectSlackInstallation({\n workspaceId: body.workspace_id,\n teamId: body.team_id,\n teamName: body.team_name,\n appId: body.app_id,\n botUserId: body.bot_user_id,\n scopes: body.scopes,\n tokenExpiresAt: null,\n displayName: `Slack ${body.team_name}`,\n }));\n try {\n await options.tokenStore.storeTokens({\n connectionId: connection.id,\n botToken: body.bot_token,\n });\n } catch (error) {\n if (!existing) await bestEffortDisconnectSlackInstallation(options, connection.id);\n throw error;\n }\n reply.code(201);\n return toIntegrationConnectionDto(connection, {capabilities: options.connectionCapabilities});\n },\n });\n}\n\nasync function bestEffortDisconnectSlackInstallation(\n options: CreateE2eSlackConnectionRouteOptions,\n connectionId: string,\n): Promise<void> {\n try {\n await options.disconnectSlackInstallation({connectionId});\n } catch (error) {\n logger().warn(\n {err: error, connectionId},\n 'Slack E2E connection compensation failed after token storage rejection',\n );\n }\n}\n"],"names":["createE2eSlackConnectionBodySchema","createE2eSlackConnectionResponseSchema","ClientError","defineRoute","logger","toIntegrationConnectionDto","createE2eSlackConnectionRoute","options","method","path","description","schema","body","response","handler","request","reply","existing","getExistingSlackConnection","teamId","team_id","workspaceId","workspace_id","status","connection","connectSlackInstallation","teamName","team_name","appId","app_id","botUserId","bot_user_id","scopes","tokenExpiresAt","displayName","tokenStore","storeTokens","connectionId","id","botToken","bot_token","error","bestEffortDisconnectSlackInstallation","code","capabilities","connectionCapabilities","disconnectSlackInstallation","warn","err"],"mappings":"AACA,SACEA,kCAAkC,EAClCC,sCAAsC,QACjC,qCAAqC;AAC5C,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,MAAM,QAAO,8BAA8B;AAGnD,SAAQC,0BAA0B,QAAO,oCAAoC;AAc7E,OAAO,SAASC,8BAA8BC,OAA6C;IACzF,OAAOJ,YAAY;QACjBK,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,MAAMZ;YACNa,UAAU;gBAAC,KAAKZ;YAAsC;QACxD;QACAa,SAAS,OAAOC,SAASC;YACvB,MAAMJ,OAAOG,QAAQH,IAAI;YACzB,MAAMK,WAAW,MAAMV,QAAQW,0BAA0B,CAAC;gBAACC,QAAQP,KAAKQ,OAAO;YAAA;YAC/E,IAAIH,YAAYA,SAASI,WAAW,KAAKT,KAAKU,YAAY,EAAE;gBAC1D,MAAM,IAAIpB,YACR,wDACA,uCACA;oBAACqB,QAAQ;gBAAG;YAEhB;YACA,MAAMC,aACJP,YACC,MAAMV,QAAQkB,wBAAwB,CAAC;gBACtCJ,aAAaT,KAAKU,YAAY;gBAC9BH,QAAQP,KAAKQ,OAAO;gBACpBM,UAAUd,KAAKe,SAAS;gBACxBC,OAAOhB,KAAKiB,MAAM;gBAClBC,WAAWlB,KAAKmB,WAAW;gBAC3BC,QAAQpB,KAAKoB,MAAM;gBACnBC,gBAAgB;gBAChBC,aAAa,CAAC,MAAM,EAAEtB,KAAKe,SAAS,EAAE;YACxC;YACF,IAAI;gBACF,MAAMpB,QAAQ4B,UAAU,CAACC,WAAW,CAAC;oBACnCC,cAAcb,WAAWc,EAAE;oBAC3BC,UAAU3B,KAAK4B,SAAS;gBAC1B;YACF,EAAE,OAAOC,OAAO;gBACd,IAAI,CAACxB,UAAU,MAAMyB,sCAAsCnC,SAASiB,WAAWc,EAAE;gBACjF,MAAMG;YACR;YACAzB,MAAM2B,IAAI,CAAC;YACX,OAAOtC,2BAA2BmB,YAAY;gBAACoB,cAAcrC,QAAQsC,sBAAsB;YAAA;QAC7F;IACF;AACF;AAEA,eAAeH,sCACbnC,OAA6C,EAC7C8B,YAAoB;IAEpB,IAAI;QACF,MAAM9B,QAAQuC,2BAA2B,CAAC;YAACT;QAAY;IACzD,EAAE,OAAOI,OAAO;QACdrC,SAAS2C,IAAI,CACX;YAACC,KAAKP;YAAOJ;QAAY,GACzB;IAEJ;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/e2eRoutes/create-connection.ts"],"sourcesContent":["import {\n createE2eSlackConnectionBodySchema,\n createE2eSlackConnectionResponseSchema,\n} from '@shipfox/api-integration-slack-dto';\nimport type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-spi';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {ConnectSlackInstallationInput} from '#core/install.js';\nimport type {SlackTokenStore} from '#core/tokens.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\n\nexport interface CreateE2eSlackConnectionRouteOptions {\n tokenStore: Pick<SlackTokenStore, 'storeTokens'>;\n getExistingSlackConnection: (input: {\n teamId: string;\n }) => Promise<IntegrationConnection<'slack'> | undefined>;\n connectSlackInstallation: (\n input: ConnectSlackInstallationInput,\n ) => Promise<IntegrationConnection<'slack'>>;\n disconnectSlackInstallation: (input: {connectionId: string}) => Promise<void>;\n connectionCapabilities: IntegrationCapability[];\n}\n\nexport function createE2eSlackConnectionRoute(options: CreateE2eSlackConnectionRouteOptions) {\n return defineRoute({\n method: 'POST',\n path: '/slack-connections',\n description: 'Create a synthetic Slack connection for E2E tests.',\n schema: {\n body: createE2eSlackConnectionBodySchema,\n response: {201: createE2eSlackConnectionResponseSchema},\n },\n handler: async (request, reply) => {\n const body = request.body;\n const existing = await options.getExistingSlackConnection({teamId: body.team_id});\n if (existing && existing.workspaceId !== body.workspace_id) {\n throw new ClientError(\n 'Slack team is already connected to another workspace',\n 'slack-connection-workspace-mismatch',\n {status: 409},\n );\n }\n const connection =\n existing ??\n (await options.connectSlackInstallation({\n workspaceId: body.workspace_id,\n teamId: body.team_id,\n teamName: body.team_name,\n appId: body.app_id,\n botUserId: body.bot_user_id,\n scopes: body.scopes,\n tokenExpiresAt: null,\n displayName: `Slack ${body.team_name}`,\n }));\n try {\n await options.tokenStore.storeTokens({\n connectionId: connection.id,\n botToken: body.bot_token,\n });\n } catch (error) {\n if (!existing) await bestEffortDisconnectSlackInstallation(options, connection.id);\n throw error;\n }\n reply.code(201);\n return toIntegrationConnectionDto(connection, {capabilities: options.connectionCapabilities});\n },\n });\n}\n\nasync function bestEffortDisconnectSlackInstallation(\n options: CreateE2eSlackConnectionRouteOptions,\n connectionId: string,\n): Promise<void> {\n try {\n await options.disconnectSlackInstallation({connectionId});\n } catch (error) {\n logger().warn(\n {err: error, connectionId},\n 'Slack E2E connection compensation failed after token storage rejection',\n );\n }\n}\n"],"names":["createE2eSlackConnectionBodySchema","createE2eSlackConnectionResponseSchema","ClientError","defineRoute","logger","toIntegrationConnectionDto","createE2eSlackConnectionRoute","options","method","path","description","schema","body","response","handler","request","reply","existing","getExistingSlackConnection","teamId","team_id","workspaceId","workspace_id","status","connection","connectSlackInstallation","teamName","team_name","appId","app_id","botUserId","bot_user_id","scopes","tokenExpiresAt","displayName","tokenStore","storeTokens","connectionId","id","botToken","bot_token","error","bestEffortDisconnectSlackInstallation","code","capabilities","connectionCapabilities","disconnectSlackInstallation","warn","err"],"mappings":"AAAA,SACEA,kCAAkC,EAClCC,sCAAsC,QACjC,qCAAqC;AAE5C,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,MAAM,QAAO,8BAA8B;AAGnD,SAAQC,0BAA0B,QAAO,oCAAoC;AAc7E,OAAO,SAASC,8BAA8BC,OAA6C;IACzF,OAAOJ,YAAY;QACjBK,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,MAAMZ;YACNa,UAAU;gBAAC,KAAKZ;YAAsC;QACxD;QACAa,SAAS,OAAOC,SAASC;YACvB,MAAMJ,OAAOG,QAAQH,IAAI;YACzB,MAAMK,WAAW,MAAMV,QAAQW,0BAA0B,CAAC;gBAACC,QAAQP,KAAKQ,OAAO;YAAA;YAC/E,IAAIH,YAAYA,SAASI,WAAW,KAAKT,KAAKU,YAAY,EAAE;gBAC1D,MAAM,IAAIpB,YACR,wDACA,uCACA;oBAACqB,QAAQ;gBAAG;YAEhB;YACA,MAAMC,aACJP,YACC,MAAMV,QAAQkB,wBAAwB,CAAC;gBACtCJ,aAAaT,KAAKU,YAAY;gBAC9BH,QAAQP,KAAKQ,OAAO;gBACpBM,UAAUd,KAAKe,SAAS;gBACxBC,OAAOhB,KAAKiB,MAAM;gBAClBC,WAAWlB,KAAKmB,WAAW;gBAC3BC,QAAQpB,KAAKoB,MAAM;gBACnBC,gBAAgB;gBAChBC,aAAa,CAAC,MAAM,EAAEtB,KAAKe,SAAS,EAAE;YACxC;YACF,IAAI;gBACF,MAAMpB,QAAQ4B,UAAU,CAACC,WAAW,CAAC;oBACnCC,cAAcb,WAAWc,EAAE;oBAC3BC,UAAU3B,KAAK4B,SAAS;gBAC1B;YACF,EAAE,OAAOC,OAAO;gBACd,IAAI,CAACxB,UAAU,MAAMyB,sCAAsCnC,SAASiB,WAAWc,EAAE;gBACjF,MAAMG;YACR;YACAzB,MAAM2B,IAAI,CAAC;YACX,OAAOtC,2BAA2BmB,YAAY;gBAACoB,cAAcrC,QAAQsC,sBAAsB;YAAA;QAC7F;IACF;AACF;AAEA,eAAeH,sCACbnC,OAA6C,EAC7C8B,YAAoB;IAEpB,IAAI;QACF,MAAM9B,QAAQuC,2BAA2B,CAAC;YAACT;QAAY;IACzD,EAAE,OAAOI,OAAO;QACdrC,SAAS2C,IAAI,CACX;YAACC,KAAKP;YAAOJ;QAAY,GACzB;IAEJ;AACF"}
@@ -1,4 +1,4 @@
1
- import { ConnectionSlugConflictError } from '@shipfox/api-integration-core-dto';
1
+ import { ConnectionSlugConflictError } from '@shipfox/api-integration-spi';
2
2
  import { workspacesInterModuleContract } from '@shipfox/api-workspaces-dto/inter-module';
3
3
  import { isInterModuleKnownError } from '@shipfox/inter-module';
4
4
  import { ClientError } from '@shipfox/node-fastify';
@@ -1 +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 {workspacesInterModuleContract} from '@shipfox/api-workspaces-dto/inter-module';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {ClientError} from '@shipfox/node-fastify';\nimport {\n SlackAuthorizationScopeMismatchError,\n SlackConnectionAlreadyLinkedError,\n SlackEnterpriseInstallUnsupportedError,\n SlackInstallationAlreadyLinkedError,\n SlackInstallStateActorMismatchError,\n SlackInstallStateError,\n SlackIntegrationProviderError,\n SlackOAuthCallbackError,\n SlackTokenRotationUnsupportedError,\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 slackRouteErrorHandler(error: unknown): never {\n if (\n isInterModuleKnownError(workspacesInterModuleContract.methods.requireActiveMembership, error)\n ) {\n if (error.code === 'workspace-not-found')\n throw new ClientError('Workspace not found', 'not-found', {\n status: 404,\n details: {workspace_id: error.details.workspaceId},\n });\n if (error.code === 'membership-required')\n throw new ClientError('Workspace membership required', 'forbidden', {\n status: 403,\n details: {workspace_id: error.details.workspaceId},\n });\n throw new ClientError('Workspace is inactive', 'workspace-inactive', {\n status: 403,\n details: {workspace_id: error.details.workspaceId},\n });\n }\n if (error instanceof SlackInstallStateError) {\n throw new ClientError(error.message, 'invalid-slack-install-state', {status: 400});\n }\n if (error instanceof SlackInstallStateActorMismatchError) {\n throw new ClientError(error.message, 'slack-install-state-actor-mismatch', {status: 403});\n }\n if (error instanceof SlackInstallationAlreadyLinkedError) {\n throw new ClientError(error.message, 'slack-installation-already-linked', {status: 409});\n }\n if (error instanceof SlackConnectionAlreadyLinkedError) {\n throw new ClientError(error.message, 'slack-connection-already-linked', {status: 409});\n }\n if (error instanceof SlackAuthorizationScopeMismatchError) {\n throw new ClientError(error.message, 'slack-authorization-scope-mismatch', {\n status: 422,\n details: {missing_scopes: error.missingScopes},\n });\n }\n if (error instanceof SlackEnterpriseInstallUnsupportedError) {\n throw new ClientError(error.message, 'slack-enterprise-install-unsupported', {status: 422});\n }\n if (error instanceof SlackTokenRotationUnsupportedError) {\n throw new ClientError(error.message, 'slack-token-rotation-unsupported', {status: 422});\n }\n if (error instanceof SlackOAuthCallbackError) {\n throw new ClientError(error.message, 'slack-oauth-callback-error', {\n status: 422,\n details: {\n error: error.providerError,\n ...(error.providerDescription ? {error_description: error.providerDescription} : {}),\n },\n });\n }\n if (error instanceof ConnectionSlugConflictError) {\n throw new ClientError(error.message, 'slug-conflict', {status: 409});\n }\n if (error instanceof SlackIntegrationProviderError) {\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","workspacesInterModuleContract","isInterModuleKnownError","ClientError","SlackAuthorizationScopeMismatchError","SlackConnectionAlreadyLinkedError","SlackEnterpriseInstallUnsupportedError","SlackInstallationAlreadyLinkedError","SlackInstallStateActorMismatchError","SlackInstallStateError","SlackIntegrationProviderError","SlackOAuthCallbackError","SlackTokenRotationUnsupportedError","providerStatus","reason","slackRouteErrorHandler","error","methods","requireActiveMembership","code","status","details","workspace_id","workspaceId","message","missing_scopes","missingScopes","providerError","providerDescription","error_description","retry_after_seconds","retryAfterSeconds"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/errors.ts"],"sourcesContent":["import {\n ConnectionSlugConflictError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-spi';\nimport {workspacesInterModuleContract} from '@shipfox/api-workspaces-dto/inter-module';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {ClientError} from '@shipfox/node-fastify';\nimport {\n SlackAuthorizationScopeMismatchError,\n SlackConnectionAlreadyLinkedError,\n SlackEnterpriseInstallUnsupportedError,\n SlackInstallationAlreadyLinkedError,\n SlackInstallStateActorMismatchError,\n SlackInstallStateError,\n SlackIntegrationProviderError,\n SlackOAuthCallbackError,\n SlackTokenRotationUnsupportedError,\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 slackRouteErrorHandler(error: unknown): never {\n if (\n isInterModuleKnownError(workspacesInterModuleContract.methods.requireActiveMembership, error)\n ) {\n if (error.code === 'workspace-not-found')\n throw new ClientError('Workspace not found', 'not-found', {\n status: 404,\n details: {workspace_id: error.details.workspaceId},\n });\n if (error.code === 'membership-required')\n throw new ClientError('Workspace membership required', 'forbidden', {\n status: 403,\n details: {workspace_id: error.details.workspaceId},\n });\n throw new ClientError('Workspace is inactive', 'workspace-inactive', {\n status: 403,\n details: {workspace_id: error.details.workspaceId},\n });\n }\n if (error instanceof SlackInstallStateError) {\n throw new ClientError(error.message, 'invalid-slack-install-state', {status: 400});\n }\n if (error instanceof SlackInstallStateActorMismatchError) {\n throw new ClientError(error.message, 'slack-install-state-actor-mismatch', {status: 403});\n }\n if (error instanceof SlackInstallationAlreadyLinkedError) {\n throw new ClientError(error.message, 'slack-installation-already-linked', {status: 409});\n }\n if (error instanceof SlackConnectionAlreadyLinkedError) {\n throw new ClientError(error.message, 'slack-connection-already-linked', {status: 409});\n }\n if (error instanceof SlackAuthorizationScopeMismatchError) {\n throw new ClientError(error.message, 'slack-authorization-scope-mismatch', {\n status: 422,\n details: {missing_scopes: error.missingScopes},\n });\n }\n if (error instanceof SlackEnterpriseInstallUnsupportedError) {\n throw new ClientError(error.message, 'slack-enterprise-install-unsupported', {status: 422});\n }\n if (error instanceof SlackTokenRotationUnsupportedError) {\n throw new ClientError(error.message, 'slack-token-rotation-unsupported', {status: 422});\n }\n if (error instanceof SlackOAuthCallbackError) {\n throw new ClientError(error.message, 'slack-oauth-callback-error', {\n status: 422,\n details: {\n error: error.providerError,\n ...(error.providerDescription ? {error_description: error.providerDescription} : {}),\n },\n });\n }\n if (error instanceof ConnectionSlugConflictError) {\n throw new ClientError(error.message, 'slug-conflict', {status: 409});\n }\n if (error instanceof SlackIntegrationProviderError) {\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","workspacesInterModuleContract","isInterModuleKnownError","ClientError","SlackAuthorizationScopeMismatchError","SlackConnectionAlreadyLinkedError","SlackEnterpriseInstallUnsupportedError","SlackInstallationAlreadyLinkedError","SlackInstallStateActorMismatchError","SlackInstallStateError","SlackIntegrationProviderError","SlackOAuthCallbackError","SlackTokenRotationUnsupportedError","providerStatus","reason","slackRouteErrorHandler","error","methods","requireActiveMembership","code","status","details","workspace_id","workspaceId","message","missing_scopes","missingScopes","providerError","providerDescription","error_description","retry_after_seconds","retryAfterSeconds"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { IntegrationCapability, IntegrationConnection } from '@shipfox/api-integration-core-dto';
1
+ import type { IntegrationCapability, IntegrationConnection } from '@shipfox/api-integration-spi';
2
2
  import { type RouteGroup } from '@shipfox/node-fastify';
3
3
  import type { SlackApiClient } from '#api/client.js';
4
4
  import { type ConnectSlackInstallationInput } from '#core/install.js';
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;AAQpG,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EACL,KAAK,6BAA6B,EAGnC,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAIrD,MAAM,WAAW,mCAAmC;IAClD,KAAK,EAAE,cAAc,CAAC;IACtB,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;IACjD,0BAA0B,EAAE,CAAC,KAAK,EAAE;QAClC,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1D,wBAAwB,EAAE,CACxB,KAAK,EAAE,6BAA6B,KACjC,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,2BAA2B,EAAE,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD,gCAAgC,CAAC,EAAE,CAAC,KAAK,EAAE;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,CAAC,CAAC;KACvF,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,KAAK,EACL,UAAU,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,EACtB,gCAAgC,GACjC,EAAE,mCAAmC,GAAG,UAAU,CAkElD"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EACL,KAAK,6BAA6B,EAGnC,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAIrD,MAAM,WAAW,mCAAmC;IAClD,KAAK,EAAE,cAAc,CAAC;IACtB,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;IACjD,0BAA0B,EAAE,CAAC,KAAK,EAAE;QAClC,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1D,wBAAwB,EAAE,CACxB,KAAK,EAAE,6BAA6B,KACjC,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,2BAA2B,EAAE,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD,gCAAgC,CAAC,EAAE,CAAC,KAAK,EAAE;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,CAAC,CAAC;KACvF,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,KAAK,EACL,UAAU,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,EACtB,gCAAgC,GACjC,EAAE,mCAAmC,GAAG,UAAU,CAkElD"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {\n createSlackInstallBodySchema,\n createSlackInstallResponseSchema,\n type SlackCallbackQueryDto,\n slackCallbackQuerySchema,\n slackCallbackResponseSchema,\n} from '@shipfox/api-integration-slack-dto';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {SlackApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {\n type ConnectSlackInstallationInput,\n handleSlackCallback,\n handleSlackOAuthCallbackError,\n} from '#core/install.js';\nimport {formatSlackBotScopes} from '#core/scopes.js';\nimport {signSlackInstallState} from '#core/state.js';\nimport type {SlackTokenStore} from '#core/tokens.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\nimport {slackRouteErrorHandler} from './errors.js';\n\nexport interface CreateSlackIntegrationRoutesOptions {\n slack: SlackApiClient;\n tokenStore: Pick<SlackTokenStore, 'storeTokens'>;\n getExistingSlackConnection: (input: {\n teamId: string;\n }) => Promise<IntegrationConnection<'slack'> | undefined>;\n connectSlackInstallation: (\n input: ConnectSlackInstallationInput,\n ) => Promise<IntegrationConnection<'slack'>>;\n disconnectSlackInstallation: (input: {connectionId: string}) => Promise<void>;\n connectionCapabilities: IntegrationCapability[];\n requireActiveWorkspaceMembership?: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n }) => Promise<unknown>;\n}\n\nexport function createSlackIntegrationRoutes({\n slack,\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n connectionCapabilities,\n requireActiveWorkspaceMembership,\n}: CreateSlackIntegrationRoutesOptions): RouteGroup {\n const installRoute = defineRoute({\n method: 'POST',\n path: '/install',\n auth: AUTH_USER,\n description: 'Create a Slack OAuth authorization URL for a workspace.',\n schema: {\n body: createSlackInstallBodySchema,\n response: {200: createSlackInstallResponseSchema},\n },\n handler: (request) => {\n const {workspace_id: workspaceId} = request.body;\n const actor = requireUserContext(request);\n requireWorkspaceAccess({request, workspaceId});\n const state = signSlackInstallState({workspaceId, userId: actor.userId});\n const installUrl = new URL('https://slack.com/oauth/v2/authorize');\n installUrl.searchParams.set('client_id', config.SLACK_OAUTH_CLIENT_ID);\n installUrl.searchParams.set('scope', formatSlackBotScopes());\n installUrl.searchParams.set('redirect_uri', config.SLACK_OAUTH_REDIRECT_URL);\n installUrl.searchParams.set('state', state);\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 Slack OAuth callback.',\n schema: {\n querystring: slackCallbackQuerySchema,\n response: {200: slackCallbackResponseSchema},\n },\n errorHandler: slackRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const query = request.query;\n if (isSlackOAuthErrorCallback(query)) {\n return await handleSlackOAuthCallbackError({\n state: query.state,\n error: query.error,\n errorDescription: query.error_description,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership:\n requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,\n });\n }\n const connection = await handleSlackCallback({\n slack,\n tokenStore,\n code: query.code,\n state: query.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership:\n requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n });\n return toIntegrationConnectionDto(connection, {capabilities: connectionCapabilities});\n },\n });\n\n return {prefix: '/integrations/slack', routes: [installRoute, callbackApiRoute]};\n}\n\nfunction unavailableWorkspaceMembershipCheck(_input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n}): Promise<never> {\n return Promise.reject(new Error('Workspaces inter-module client is not configured'));\n}\n\nfunction isSlackOAuthErrorCallback(query: SlackCallbackQueryDto): query is SlackCallbackQueryDto & {\n error: string;\n error_description?: string | undefined;\n state: string;\n} {\n return 'error' in query && typeof query.error === 'string';\n}\n"],"names":["AUTH_USER","requireUserContext","requireWorkspaceAccess","createSlackInstallBodySchema","createSlackInstallResponseSchema","slackCallbackQuerySchema","slackCallbackResponseSchema","defineRoute","config","handleSlackCallback","handleSlackOAuthCallbackError","formatSlackBotScopes","signSlackInstallState","toIntegrationConnectionDto","slackRouteErrorHandler","createSlackIntegrationRoutes","slack","tokenStore","getExistingSlackConnection","connectSlackInstallation","disconnectSlackInstallation","connectionCapabilities","requireActiveWorkspaceMembership","installRoute","method","path","auth","description","schema","body","response","handler","request","workspace_id","workspaceId","actor","state","userId","installUrl","URL","searchParams","set","SLACK_OAUTH_CLIENT_ID","SLACK_OAUTH_REDIRECT_URL","install_url","toString","callbackApiRoute","querystring","errorHandler","query","isSlackOAuthErrorCallback","error","errorDescription","error_description","sessionUserId","sessionMemberships","memberships","requireWorkspaceMembership","unavailableWorkspaceMembershipCheck","connection","code","capabilities","prefix","routes","_input","Promise","reject","Error"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,kBAAkB,EAAEC,sBAAsB,QAAO,4BAA4B;AAEhG,SACEC,4BAA4B,EAC5BC,gCAAgC,EAEhCC,wBAAwB,EACxBC,2BAA2B,QACtB,qCAAqC;AAC5C,SAAQC,WAAW,QAAwB,wBAAwB;AAEnE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAEEC,mBAAmB,EACnBC,6BAA6B,QACxB,mBAAmB;AAC1B,SAAQC,oBAAoB,QAAO,kBAAkB;AACrD,SAAQC,qBAAqB,QAAO,iBAAiB;AAErD,SAAQC,0BAA0B,QAAO,oCAAoC;AAC7E,SAAQC,sBAAsB,QAAO,cAAc;AAoBnD,OAAO,SAASC,6BAA6B,EAC3CC,KAAK,EACLC,UAAU,EACVC,0BAA0B,EAC1BC,wBAAwB,EACxBC,2BAA2B,EAC3BC,sBAAsB,EACtBC,gCAAgC,EACI;IACpC,MAAMC,eAAehB,YAAY;QAC/BiB,QAAQ;QACRC,MAAM;QACNC,MAAM1B;QACN2B,aAAa;QACbC,QAAQ;YACNC,MAAM1B;YACN2B,UAAU;gBAAC,KAAK1B;YAAgC;QAClD;QACA2B,SAAS,CAACC;YACR,MAAM,EAACC,cAAcC,WAAW,EAAC,GAAGF,QAAQH,IAAI;YAChD,MAAMM,QAAQlC,mBAAmB+B;YACjC9B,uBAAuB;gBAAC8B;gBAASE;YAAW;YAC5C,MAAME,QAAQxB,sBAAsB;gBAACsB;gBAAaG,QAAQF,MAAME,MAAM;YAAA;YACtE,MAAMC,aAAa,IAAIC,IAAI;YAC3BD,WAAWE,YAAY,CAACC,GAAG,CAAC,aAAajC,OAAOkC,qBAAqB;YACrEJ,WAAWE,YAAY,CAACC,GAAG,CAAC,SAAS9B;YACrC2B,WAAWE,YAAY,CAACC,GAAG,CAAC,gBAAgBjC,OAAOmC,wBAAwB;YAC3EL,WAAWE,YAAY,CAACC,GAAG,CAAC,SAASL;YACrC,OAAO;gBAACQ,aAAaN,WAAWO,QAAQ;YAAE;QAC5C;IACF;IAEA,MAAMC,mBAAmBvC,YAAY;QACnCiB,QAAQ;QACRC,MAAM;QACNC,MAAM1B;QACN2B,aAAa;QACbC,QAAQ;YACNmB,aAAa1C;YACbyB,UAAU;gBAAC,KAAKxB;YAA2B;QAC7C;QACA0C,cAAclC;QACdiB,SAAS,OAAOC;YACd,MAAMG,QAAQlC,mBAAmB+B;YACjC,MAAMiB,QAAQjB,QAAQiB,KAAK;YAC3B,IAAIC,0BAA0BD,QAAQ;gBACpC,OAAO,MAAMvC,8BAA8B;oBACzC0B,OAAOa,MAAMb,KAAK;oBAClBe,OAAOF,MAAME,KAAK;oBAClBC,kBAAkBH,MAAMI,iBAAiB;oBACzCC,eAAenB,MAAME,MAAM;oBAC3BkB,oBAAoBpB,MAAMqB,WAAW;oBACrCC,4BACEnC,oCAAoCoC;gBACxC;YACF;YACA,MAAMC,aAAa,MAAMlD,oBAAoB;gBAC3CO;gBACAC;gBACA2C,MAAMX,MAAMW,IAAI;gBAChBxB,OAAOa,MAAMb,KAAK;gBAClBkB,eAAenB,MAAME,MAAM;gBAC3BkB,oBAAoBpB,MAAMqB,WAAW;gBACrCC,4BACEnC,oCAAoCoC;gBACtCxC;gBACAC;gBACAC;YACF;YACA,OAAOP,2BAA2B8C,YAAY;gBAACE,cAAcxC;YAAsB;QACrF;IACF;IAEA,OAAO;QAACyC,QAAQ;QAAuBC,QAAQ;YAACxC;YAAcuB;SAAiB;IAAA;AACjF;AAEA,SAASY,oCAAoCM,MAI5C;IACC,OAAOC,QAAQC,MAAM,CAAC,IAAIC,MAAM;AAClC;AAEA,SAASjB,0BAA0BD,KAA4B;IAK7D,OAAO,WAAWA,SAAS,OAAOA,MAAME,KAAK,KAAK;AACpD"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {\n createSlackInstallBodySchema,\n createSlackInstallResponseSchema,\n type SlackCallbackQueryDto,\n slackCallbackQuerySchema,\n slackCallbackResponseSchema,\n} from '@shipfox/api-integration-slack-dto';\nimport type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-spi';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {SlackApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {\n type ConnectSlackInstallationInput,\n handleSlackCallback,\n handleSlackOAuthCallbackError,\n} from '#core/install.js';\nimport {formatSlackBotScopes} from '#core/scopes.js';\nimport {signSlackInstallState} from '#core/state.js';\nimport type {SlackTokenStore} from '#core/tokens.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\nimport {slackRouteErrorHandler} from './errors.js';\n\nexport interface CreateSlackIntegrationRoutesOptions {\n slack: SlackApiClient;\n tokenStore: Pick<SlackTokenStore, 'storeTokens'>;\n getExistingSlackConnection: (input: {\n teamId: string;\n }) => Promise<IntegrationConnection<'slack'> | undefined>;\n connectSlackInstallation: (\n input: ConnectSlackInstallationInput,\n ) => Promise<IntegrationConnection<'slack'>>;\n disconnectSlackInstallation: (input: {connectionId: string}) => Promise<void>;\n connectionCapabilities: IntegrationCapability[];\n requireActiveWorkspaceMembership?: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n }) => Promise<unknown>;\n}\n\nexport function createSlackIntegrationRoutes({\n slack,\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n connectionCapabilities,\n requireActiveWorkspaceMembership,\n}: CreateSlackIntegrationRoutesOptions): RouteGroup {\n const installRoute = defineRoute({\n method: 'POST',\n path: '/install',\n auth: AUTH_USER,\n description: 'Create a Slack OAuth authorization URL for a workspace.',\n schema: {\n body: createSlackInstallBodySchema,\n response: {200: createSlackInstallResponseSchema},\n },\n handler: (request) => {\n const {workspace_id: workspaceId} = request.body;\n const actor = requireUserContext(request);\n requireWorkspaceAccess({request, workspaceId});\n const state = signSlackInstallState({workspaceId, userId: actor.userId});\n const installUrl = new URL('https://slack.com/oauth/v2/authorize');\n installUrl.searchParams.set('client_id', config.SLACK_OAUTH_CLIENT_ID);\n installUrl.searchParams.set('scope', formatSlackBotScopes());\n installUrl.searchParams.set('redirect_uri', config.SLACK_OAUTH_REDIRECT_URL);\n installUrl.searchParams.set('state', state);\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 Slack OAuth callback.',\n schema: {\n querystring: slackCallbackQuerySchema,\n response: {200: slackCallbackResponseSchema},\n },\n errorHandler: slackRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const query = request.query;\n if (isSlackOAuthErrorCallback(query)) {\n return await handleSlackOAuthCallbackError({\n state: query.state,\n error: query.error,\n errorDescription: query.error_description,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership:\n requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,\n });\n }\n const connection = await handleSlackCallback({\n slack,\n tokenStore,\n code: query.code,\n state: query.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership:\n requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n });\n return toIntegrationConnectionDto(connection, {capabilities: connectionCapabilities});\n },\n });\n\n return {prefix: '/integrations/slack', routes: [installRoute, callbackApiRoute]};\n}\n\nfunction unavailableWorkspaceMembershipCheck(_input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n}): Promise<never> {\n return Promise.reject(new Error('Workspaces inter-module client is not configured'));\n}\n\nfunction isSlackOAuthErrorCallback(query: SlackCallbackQueryDto): query is SlackCallbackQueryDto & {\n error: string;\n error_description?: string | undefined;\n state: string;\n} {\n return 'error' in query && typeof query.error === 'string';\n}\n"],"names":["AUTH_USER","requireUserContext","requireWorkspaceAccess","createSlackInstallBodySchema","createSlackInstallResponseSchema","slackCallbackQuerySchema","slackCallbackResponseSchema","defineRoute","config","handleSlackCallback","handleSlackOAuthCallbackError","formatSlackBotScopes","signSlackInstallState","toIntegrationConnectionDto","slackRouteErrorHandler","createSlackIntegrationRoutes","slack","tokenStore","getExistingSlackConnection","connectSlackInstallation","disconnectSlackInstallation","connectionCapabilities","requireActiveWorkspaceMembership","installRoute","method","path","auth","description","schema","body","response","handler","request","workspace_id","workspaceId","actor","state","userId","installUrl","URL","searchParams","set","SLACK_OAUTH_CLIENT_ID","SLACK_OAUTH_REDIRECT_URL","install_url","toString","callbackApiRoute","querystring","errorHandler","query","isSlackOAuthErrorCallback","error","errorDescription","error_description","sessionUserId","sessionMemberships","memberships","requireWorkspaceMembership","unavailableWorkspaceMembershipCheck","connection","code","capabilities","prefix","routes","_input","Promise","reject","Error"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,kBAAkB,EAAEC,sBAAsB,QAAO,4BAA4B;AAChG,SACEC,4BAA4B,EAC5BC,gCAAgC,EAEhCC,wBAAwB,EACxBC,2BAA2B,QACtB,qCAAqC;AAE5C,SAAQC,WAAW,QAAwB,wBAAwB;AAEnE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAEEC,mBAAmB,EACnBC,6BAA6B,QACxB,mBAAmB;AAC1B,SAAQC,oBAAoB,QAAO,kBAAkB;AACrD,SAAQC,qBAAqB,QAAO,iBAAiB;AAErD,SAAQC,0BAA0B,QAAO,oCAAoC;AAC7E,SAAQC,sBAAsB,QAAO,cAAc;AAoBnD,OAAO,SAASC,6BAA6B,EAC3CC,KAAK,EACLC,UAAU,EACVC,0BAA0B,EAC1BC,wBAAwB,EACxBC,2BAA2B,EAC3BC,sBAAsB,EACtBC,gCAAgC,EACI;IACpC,MAAMC,eAAehB,YAAY;QAC/BiB,QAAQ;QACRC,MAAM;QACNC,MAAM1B;QACN2B,aAAa;QACbC,QAAQ;YACNC,MAAM1B;YACN2B,UAAU;gBAAC,KAAK1B;YAAgC;QAClD;QACA2B,SAAS,CAACC;YACR,MAAM,EAACC,cAAcC,WAAW,EAAC,GAAGF,QAAQH,IAAI;YAChD,MAAMM,QAAQlC,mBAAmB+B;YACjC9B,uBAAuB;gBAAC8B;gBAASE;YAAW;YAC5C,MAAME,QAAQxB,sBAAsB;gBAACsB;gBAAaG,QAAQF,MAAME,MAAM;YAAA;YACtE,MAAMC,aAAa,IAAIC,IAAI;YAC3BD,WAAWE,YAAY,CAACC,GAAG,CAAC,aAAajC,OAAOkC,qBAAqB;YACrEJ,WAAWE,YAAY,CAACC,GAAG,CAAC,SAAS9B;YACrC2B,WAAWE,YAAY,CAACC,GAAG,CAAC,gBAAgBjC,OAAOmC,wBAAwB;YAC3EL,WAAWE,YAAY,CAACC,GAAG,CAAC,SAASL;YACrC,OAAO;gBAACQ,aAAaN,WAAWO,QAAQ;YAAE;QAC5C;IACF;IAEA,MAAMC,mBAAmBvC,YAAY;QACnCiB,QAAQ;QACRC,MAAM;QACNC,MAAM1B;QACN2B,aAAa;QACbC,QAAQ;YACNmB,aAAa1C;YACbyB,UAAU;gBAAC,KAAKxB;YAA2B;QAC7C;QACA0C,cAAclC;QACdiB,SAAS,OAAOC;YACd,MAAMG,QAAQlC,mBAAmB+B;YACjC,MAAMiB,QAAQjB,QAAQiB,KAAK;YAC3B,IAAIC,0BAA0BD,QAAQ;gBACpC,OAAO,MAAMvC,8BAA8B;oBACzC0B,OAAOa,MAAMb,KAAK;oBAClBe,OAAOF,MAAME,KAAK;oBAClBC,kBAAkBH,MAAMI,iBAAiB;oBACzCC,eAAenB,MAAME,MAAM;oBAC3BkB,oBAAoBpB,MAAMqB,WAAW;oBACrCC,4BACEnC,oCAAoCoC;gBACxC;YACF;YACA,MAAMC,aAAa,MAAMlD,oBAAoB;gBAC3CO;gBACAC;gBACA2C,MAAMX,MAAMW,IAAI;gBAChBxB,OAAOa,MAAMb,KAAK;gBAClBkB,eAAenB,MAAME,MAAM;gBAC3BkB,oBAAoBpB,MAAMqB,WAAW;gBACrCC,4BACEnC,oCAAoCoC;gBACtCxC;gBACAC;gBACAC;YACF;YACA,OAAOP,2BAA2B8C,YAAY;gBAACE,cAAcxC;YAAsB;QACrF;IACF;IAEA,OAAO;QAACyC,QAAQ;QAAuBC,QAAQ;YAACxC;YAAcuB;SAAiB;IAAA;AACjF;AAEA,SAASY,oCAAoCM,MAI5C;IACC,OAAOC,QAAQC,MAAM,CAAC,IAAIC,MAAM;AAClC;AAEA,SAASjB,0BAA0BD,KAA4B;IAK7D,OAAO,WAAWA,SAAS,OAAOA,MAAME,KAAK,KAAK;AACpD"}
@@ -1,4 +1,4 @@
1
- import type { ClaimWebhookDeliveryFn, GetIntegrationConnectionByIdFn, PublishIntegrationEventReceivedFn, RecordDeliveryOnlyFn } from '@shipfox/api-integration-core-dto';
1
+ import type { ClaimWebhookDeliveryFn, GetIntegrationConnectionByIdFn, PublishIntegrationEventReceivedFn, RecordDeliveryOnlyFn } from '@shipfox/api-integration-spi';
2
2
  import { type RouteGroup } from '@shipfox/node-fastify';
3
3
  import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
4
4
  import { type SlackWebhookProcessor } from '#core/webhook-processor.js';
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EACtB,8BAA8B,EAC9B,iCAAiC,EACjC,oBAAoB,EAGrB,MAAM,mCAAmC,CAAC;AAK3C,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAA8B,KAAK,qBAAqB,EAAC,MAAM,4BAA4B,CAAC;AAEnG,eAAO,MAAM,wBAAwB,QAAc,CAAC;AACpD,eAAO,MAAM,iBAAiB;;;CAAgE,CAAC;AAW/F,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,oBAAoB,EAAE,sBAAsB,CAAC;IAC7C,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,SAAS,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG,UAAU,EAAE,CAqE/F"}
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EACtB,8BAA8B,EAC9B,iCAAiC,EACjC,oBAAoB,EAGrB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAA8B,KAAK,qBAAqB,EAAC,MAAM,4BAA4B,CAAC;AAEnG,eAAO,MAAM,wBAAwB,QAAc,CAAC;AACpD,eAAO,MAAM,iBAAiB;;;CAAgE,CAAC;AAW/F,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,oBAAoB,EAAE,sBAAsB,CAAC;IAC7C,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,SAAS,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG,UAAU,EAAE,CAqE/F"}
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { createStoredWebhookRequest, WEBHOOK_MAX_RAW_BODY_BYTES } from '@shipfox/api-integration-core-dto';
2
+ import { createStoredWebhookRequest, WEBHOOK_MAX_RAW_BODY_BYTES } from '@shipfox/api-integration-spi';
3
3
  import { ClientError, createRawBodyPlugin, defineRoute } from '@shipfox/node-fastify';
4
4
  import { createSlackWebhookProcessor } from '#core/webhook-processor.js';
5
5
  export const SLACK_WEBHOOK_BODY_LIMIT = 1024 * 1024;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/webhooks.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport type {\n ClaimWebhookDeliveryFn,\n GetIntegrationConnectionByIdFn,\n PublishIntegrationEventReceivedFn,\n RecordDeliveryOnlyFn,\n StoredWebhookRequest,\n WebhookProcessingResult,\n} from '@shipfox/api-integration-core-dto';\nimport {\n createStoredWebhookRequest,\n WEBHOOK_MAX_RAW_BODY_BYTES,\n} from '@shipfox/api-integration-core-dto';\nimport {\n ClientError,\n createRawBodyPlugin,\n defineRoute,\n type RouteGroup,\n} from '@shipfox/node-fastify';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {createSlackWebhookProcessor, type SlackWebhookProcessor} from '#core/webhook-processor.js';\n\nexport const SLACK_WEBHOOK_BODY_LIMIT = 1024 * 1024;\nexport const SLASH_COMMAND_ACK = {response_type: 'ephemeral', text: 'Working on it.'} as const;\n\nconst slackJsonRawBodyPlugin = createRawBodyPlugin({\n contentType: 'application/json',\n bodyLimit: SLACK_WEBHOOK_BODY_LIMIT,\n});\nconst slackFormRawBodyPlugin = createRawBodyPlugin({\n contentType: 'application/x-www-form-urlencoded',\n bodyLimit: SLACK_WEBHOOK_BODY_LIMIT,\n});\n\nexport interface CreateSlackWebhookRoutesOptions {\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n claimWebhookDelivery: ClaimWebhookDeliveryFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n processor?: SlackWebhookProcessor | undefined;\n}\n\nexport function createSlackWebhookRoutes(options: CreateSlackWebhookRoutesOptions): RouteGroup[] {\n const processor = options.processor ?? createSlackWebhookProcessor(options);\n const eventsRoute = defineRoute({\n method: 'POST',\n path: '/',\n auth: [],\n description: 'Slack Events API receiver.',\n options: {bodyLimit: SLACK_WEBHOOK_BODY_LIMIT},\n handler: async (request, reply) => {\n const body = rawRequestBody(request.body);\n if (!body) {\n reply.code(400);\n return {error: 'expected raw JSON body'};\n }\n const result = await processor.process(\n createSlackStoredWebhookRequest(\n {\n routeId: 'slack.event',\n body,\n headers: request.headers,\n rawQueryString: rawQueryString(request),\n },\n new Date().toISOString(),\n ),\n );\n return sendSlackEventResponse(reply, result);\n },\n });\n const commandsRoute = defineRoute({\n method: 'POST',\n path: '/',\n auth: [],\n description: 'Slack slash-command receiver.',\n options: {bodyLimit: SLACK_WEBHOOK_BODY_LIMIT},\n handler: async (request, reply) => {\n const body = rawRequestBody(request.body);\n if (!body) {\n reply.code(400);\n return {error: 'expected raw form body'};\n }\n const result = await processor.process(\n createSlackStoredWebhookRequest(\n {\n routeId: 'slack.command',\n body,\n headers: request.headers,\n rawQueryString: rawQueryString(request),\n },\n new Date().toISOString(),\n ),\n );\n return sendSlackCommandResponse(reply, result);\n },\n });\n\n return [\n {\n prefix: '/webhooks/integrations/slack/events',\n auth: [],\n plugins: [slackJsonRawBodyPlugin],\n routes: [eventsRoute],\n },\n {\n prefix: '/webhooks/integrations/slack/commands',\n auth: [],\n plugins: [slackFormRawBodyPlugin],\n routes: [commandsRoute],\n },\n ];\n}\n\nfunction rawRequestBody(body: unknown): Uint8Array | undefined {\n return body instanceof Uint8Array ? body : undefined;\n}\n\nfunction rawQueryString(request: {raw: {url?: string | undefined}}): string {\n return request.raw.url?.split('?')[1] ?? '';\n}\n\nfunction createSlackStoredWebhookRequest(\n input: {\n routeId: 'slack.event' | 'slack.command';\n body: Uint8Array;\n headers: Record<string, string | string[] | undefined>;\n rawQueryString: string;\n },\n receivedAt: string,\n): StoredWebhookRequest {\n if (input.body.byteLength > WEBHOOK_MAX_RAW_BODY_BYTES) {\n throw new ClientError('Webhook request body is too large', 'body-too-large', {status: 413});\n }\n try {\n return createStoredWebhookRequest({\n requestId: randomUUID(),\n routeId: input.routeId,\n receivedAt,\n rawQueryString: input.rawQueryString,\n headers: slackWebhookHeaders(input.headers),\n body: input.body,\n });\n } catch (error) {\n throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {\n cause: error,\n });\n }\n}\n\nfunction slackWebhookHeaders(headers: Record<string, string | string[] | undefined>) {\n return Object.fromEntries(\n ['content-type', 'x-slack-signature', 'x-slack-request-timestamp'].flatMap((name) => {\n const value = headers[name];\n return typeof value === 'string' ? [[name, value]] : [];\n }),\n );\n}\n\nfunction sendSlackEventResponse(\n reply: {code(statusCode: number): void},\n result: WebhookProcessingResult,\n) {\n if (result.outcome === 'processed' && 'challenge' in result) {\n reply.code(200);\n return {challenge: result.challenge};\n }\n if (\n result.outcome === 'discarded' &&\n (result.reason === 'invalid_signature' ||\n result.reason === 'missing_required_input' ||\n result.reason === 'stale_at_receipt')\n ) {\n reply.code(401);\n return {error: 'invalid signature'};\n }\n if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {\n reply.code(400);\n return {error: 'malformed JSON'};\n }\n reply.code(200);\n return null;\n}\n\nfunction sendSlackCommandResponse(\n reply: {code(statusCode: number): void},\n result: WebhookProcessingResult,\n) {\n if (\n result.outcome === 'discarded' &&\n (result.reason === 'invalid_signature' ||\n result.reason === 'missing_required_input' ||\n result.reason === 'stale_at_receipt')\n ) {\n reply.code(401);\n return {error: 'invalid signature'};\n }\n reply.code(200);\n return SLASH_COMMAND_ACK;\n}\n"],"names":["randomUUID","createStoredWebhookRequest","WEBHOOK_MAX_RAW_BODY_BYTES","ClientError","createRawBodyPlugin","defineRoute","createSlackWebhookProcessor","SLACK_WEBHOOK_BODY_LIMIT","SLASH_COMMAND_ACK","response_type","text","slackJsonRawBodyPlugin","contentType","bodyLimit","slackFormRawBodyPlugin","createSlackWebhookRoutes","options","processor","eventsRoute","method","path","auth","description","handler","request","reply","body","rawRequestBody","code","error","result","process","createSlackStoredWebhookRequest","routeId","headers","rawQueryString","Date","toISOString","sendSlackEventResponse","commandsRoute","sendSlackCommandResponse","prefix","plugins","routes","Uint8Array","undefined","raw","url","split","input","receivedAt","byteLength","status","requestId","slackWebhookHeaders","cause","Object","fromEntries","flatMap","name","value","outcome","challenge","reason"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/webhooks.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport type {\n ClaimWebhookDeliveryFn,\n GetIntegrationConnectionByIdFn,\n PublishIntegrationEventReceivedFn,\n RecordDeliveryOnlyFn,\n StoredWebhookRequest,\n WebhookProcessingResult,\n} from '@shipfox/api-integration-spi';\nimport {createStoredWebhookRequest, WEBHOOK_MAX_RAW_BODY_BYTES} from '@shipfox/api-integration-spi';\nimport {\n ClientError,\n createRawBodyPlugin,\n defineRoute,\n type RouteGroup,\n} from '@shipfox/node-fastify';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {createSlackWebhookProcessor, type SlackWebhookProcessor} from '#core/webhook-processor.js';\n\nexport const SLACK_WEBHOOK_BODY_LIMIT = 1024 * 1024;\nexport const SLASH_COMMAND_ACK = {response_type: 'ephemeral', text: 'Working on it.'} as const;\n\nconst slackJsonRawBodyPlugin = createRawBodyPlugin({\n contentType: 'application/json',\n bodyLimit: SLACK_WEBHOOK_BODY_LIMIT,\n});\nconst slackFormRawBodyPlugin = createRawBodyPlugin({\n contentType: 'application/x-www-form-urlencoded',\n bodyLimit: SLACK_WEBHOOK_BODY_LIMIT,\n});\n\nexport interface CreateSlackWebhookRoutesOptions {\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n claimWebhookDelivery: ClaimWebhookDeliveryFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n processor?: SlackWebhookProcessor | undefined;\n}\n\nexport function createSlackWebhookRoutes(options: CreateSlackWebhookRoutesOptions): RouteGroup[] {\n const processor = options.processor ?? createSlackWebhookProcessor(options);\n const eventsRoute = defineRoute({\n method: 'POST',\n path: '/',\n auth: [],\n description: 'Slack Events API receiver.',\n options: {bodyLimit: SLACK_WEBHOOK_BODY_LIMIT},\n handler: async (request, reply) => {\n const body = rawRequestBody(request.body);\n if (!body) {\n reply.code(400);\n return {error: 'expected raw JSON body'};\n }\n const result = await processor.process(\n createSlackStoredWebhookRequest(\n {\n routeId: 'slack.event',\n body,\n headers: request.headers,\n rawQueryString: rawQueryString(request),\n },\n new Date().toISOString(),\n ),\n );\n return sendSlackEventResponse(reply, result);\n },\n });\n const commandsRoute = defineRoute({\n method: 'POST',\n path: '/',\n auth: [],\n description: 'Slack slash-command receiver.',\n options: {bodyLimit: SLACK_WEBHOOK_BODY_LIMIT},\n handler: async (request, reply) => {\n const body = rawRequestBody(request.body);\n if (!body) {\n reply.code(400);\n return {error: 'expected raw form body'};\n }\n const result = await processor.process(\n createSlackStoredWebhookRequest(\n {\n routeId: 'slack.command',\n body,\n headers: request.headers,\n rawQueryString: rawQueryString(request),\n },\n new Date().toISOString(),\n ),\n );\n return sendSlackCommandResponse(reply, result);\n },\n });\n\n return [\n {\n prefix: '/webhooks/integrations/slack/events',\n auth: [],\n plugins: [slackJsonRawBodyPlugin],\n routes: [eventsRoute],\n },\n {\n prefix: '/webhooks/integrations/slack/commands',\n auth: [],\n plugins: [slackFormRawBodyPlugin],\n routes: [commandsRoute],\n },\n ];\n}\n\nfunction rawRequestBody(body: unknown): Uint8Array | undefined {\n return body instanceof Uint8Array ? body : undefined;\n}\n\nfunction rawQueryString(request: {raw: {url?: string | undefined}}): string {\n return request.raw.url?.split('?')[1] ?? '';\n}\n\nfunction createSlackStoredWebhookRequest(\n input: {\n routeId: 'slack.event' | 'slack.command';\n body: Uint8Array;\n headers: Record<string, string | string[] | undefined>;\n rawQueryString: string;\n },\n receivedAt: string,\n): StoredWebhookRequest {\n if (input.body.byteLength > WEBHOOK_MAX_RAW_BODY_BYTES) {\n throw new ClientError('Webhook request body is too large', 'body-too-large', {status: 413});\n }\n try {\n return createStoredWebhookRequest({\n requestId: randomUUID(),\n routeId: input.routeId,\n receivedAt,\n rawQueryString: input.rawQueryString,\n headers: slackWebhookHeaders(input.headers),\n body: input.body,\n });\n } catch (error) {\n throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {\n cause: error,\n });\n }\n}\n\nfunction slackWebhookHeaders(headers: Record<string, string | string[] | undefined>) {\n return Object.fromEntries(\n ['content-type', 'x-slack-signature', 'x-slack-request-timestamp'].flatMap((name) => {\n const value = headers[name];\n return typeof value === 'string' ? [[name, value]] : [];\n }),\n );\n}\n\nfunction sendSlackEventResponse(\n reply: {code(statusCode: number): void},\n result: WebhookProcessingResult,\n) {\n if (result.outcome === 'processed' && 'challenge' in result) {\n reply.code(200);\n return {challenge: result.challenge};\n }\n if (\n result.outcome === 'discarded' &&\n (result.reason === 'invalid_signature' ||\n result.reason === 'missing_required_input' ||\n result.reason === 'stale_at_receipt')\n ) {\n reply.code(401);\n return {error: 'invalid signature'};\n }\n if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {\n reply.code(400);\n return {error: 'malformed JSON'};\n }\n reply.code(200);\n return null;\n}\n\nfunction sendSlackCommandResponse(\n reply: {code(statusCode: number): void},\n result: WebhookProcessingResult,\n) {\n if (\n result.outcome === 'discarded' &&\n (result.reason === 'invalid_signature' ||\n result.reason === 'missing_required_input' ||\n result.reason === 'stale_at_receipt')\n ) {\n reply.code(401);\n return {error: 'invalid signature'};\n }\n reply.code(200);\n return SLASH_COMMAND_ACK;\n}\n"],"names":["randomUUID","createStoredWebhookRequest","WEBHOOK_MAX_RAW_BODY_BYTES","ClientError","createRawBodyPlugin","defineRoute","createSlackWebhookProcessor","SLACK_WEBHOOK_BODY_LIMIT","SLASH_COMMAND_ACK","response_type","text","slackJsonRawBodyPlugin","contentType","bodyLimit","slackFormRawBodyPlugin","createSlackWebhookRoutes","options","processor","eventsRoute","method","path","auth","description","handler","request","reply","body","rawRequestBody","code","error","result","process","createSlackStoredWebhookRequest","routeId","headers","rawQueryString","Date","toISOString","sendSlackEventResponse","commandsRoute","sendSlackCommandResponse","prefix","plugins","routes","Uint8Array","undefined","raw","url","split","input","receivedAt","byteLength","status","requestId","slackWebhookHeaders","cause","Object","fromEntries","flatMap","name","value","outcome","challenge","reason"],"mappings":"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"}