@shipfox/api-integration-slack 4.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +59 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +3 -1
- package/dist/api/client.js.map +1 -1
- package/dist/core/signature.d.ts +2 -1
- package/dist/core/signature.d.ts.map +1 -1
- package/dist/core/signature.js +10 -6
- package/dist/core/signature.js.map +1 -1
- package/dist/core/webhook-processor.d.ts +15 -0
- package/dist/core/webhook-processor.d.ts.map +1 -0
- package/dist/core/webhook-processor.js +127 -0
- package/dist/core/webhook-processor.js.map +1 -0
- package/dist/core/webhook.d.ts +5 -3
- package/dist/core/webhook.d.ts.map +1 -1
- package/dist/core/webhook.js +101 -2
- package/dist/core/webhook.js.map +1 -1
- package/dist/db/db.d.ts +34 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/installations.d.ts +2 -0
- package/dist/db/installations.d.ts.map +1 -1
- package/dist/db/installations.js +4 -2
- package/dist/db/installations.js.map +1 -1
- package/dist/db/schema/installations.d.ts +17 -0
- package/dist/db/schema/installations.d.ts.map +1 -1
- package/dist/db/schema/installations.js +3 -1
- package/dist/db/schema/installations.js.map +1 -1
- package/dist/index.d.ts +28 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -7
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/errors.d.ts.map +1 -1
- package/dist/presentation/routes/errors.js +22 -0
- package/dist/presentation/routes/errors.js.map +1 -1
- package/dist/presentation/routes/install.d.ts +6 -1
- package/dist/presentation/routes/install.d.ts.map +1 -1
- package/dist/presentation/routes/install.js +6 -4
- package/dist/presentation/routes/install.js.map +1 -1
- package/dist/presentation/routes/webhooks.d.ts +4 -1
- package/dist/presentation/routes/webhooks.d.ts.map +1 -1
- package/dist/presentation/routes/webhooks.js +94 -92
- package/dist/presentation/routes/webhooks.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0001_dark_tyrannus.sql +1 -0
- package/drizzle/meta/0001_snapshot.json +139 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +20 -30
- package/src/api/client.test.ts +24 -0
- package/src/api/client.ts +3 -1
- package/src/core/disconnect.test.ts +96 -0
- package/src/core/signature.test.ts +3 -3
- package/src/core/signature.ts +19 -9
- package/src/core/tokens.test.ts +16 -2
- package/src/core/webhook-processor.test.ts +106 -0
- package/src/core/webhook-processor.ts +152 -0
- package/src/core/webhook.test.ts +131 -2
- package/src/core/webhook.ts +143 -6
- package/src/db/installations.test.ts +15 -0
- package/src/db/installations.ts +11 -3
- package/src/db/schema/installations.ts +3 -1
- package/src/index.test.ts +11 -0
- package/src/index.ts +30 -3
- package/src/presentation/routes/errors.test.ts +25 -0
- package/src/presentation/routes/errors.ts +20 -0
- package/src/presentation/routes/install.test.ts +2 -4
- package/src/presentation/routes/install.ts +18 -3
- package/src/presentation/routes/webhooks.test.ts +46 -1
- package/src/presentation/routes/webhooks.ts +127 -97
- package/test/globalSetup.ts +0 -9
- package/tsconfig.build.tsbuildinfo +1 -1
- package/turbo.json +9 -0
- package/test/api-secrets.d.ts +0 -26
package/dist/core/webhook.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/webhook.ts"],"sourcesContent":["import type {\n GetIntegrationConnectionByIdFn,\n IntegrationConnection,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n RecordDeliveryOnlyFn,\n} from '@shipfox/api-integration-core-dto';\nimport {\n SLACK_PROVIDER,\n SLACK_SLASH_COMMAND_EVENT,\n type SlackEventBaseEnvelopeDto,\n type SlackSlashCommandDto,\n slackEventEnvelopeSchema,\n} from '@shipfox/api-integration-slack-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {z} from 'zod';\nimport {getSlackInstallationByTeamId, type SlackInstallation} from '#db/installations.js';\n\nconst slackSelfAuthoredEventSchema = z\n .object({\n bot_id: z.string().optional(),\n user: z.string().optional(),\n message: z\n .object({\n bot_id: z.string().optional(),\n user: z.string().optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough();\n\nexport type SlackWebhookOutcome =\n | 'published'\n | 'duplicate'\n | 'unknown-team'\n | 'revoked-installation'\n | 'missing-connection'\n | 'inactive-connection'\n | 'unsupported-event'\n | 'self-message';\n\ninterface SlackWebhookParams {\n tx: IntegrationTx;\n deliveryId: string;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport interface HandleSlackEventParams extends SlackWebhookParams {\n envelope: SlackEventBaseEnvelopeDto;\n}\n\nexport interface HandleSlackCommandParams extends SlackWebhookParams {\n command: SlackSlashCommandDto;\n}\n\ntype SlackConnectionResolution =\n | {kind: 'ok'; connection: IntegrationConnection; installation: SlackInstallation}\n | {\n kind: 'drop';\n outcome: Exclude<\n SlackWebhookOutcome,\n 'published' | 'duplicate' | 'unsupported-event' | 'self-message'\n >;\n };\n\nexport async function handleSlackEvent(\n params: HandleSlackEventParams,\n): Promise<{outcome: SlackWebhookOutcome}> {\n const resolution = await resolveSlackConnection({\n ...params,\n teamId: params.envelope.team_id,\n });\n if (resolution.kind === 'drop') return resolution;\n\n if (isSelfAuthoredSlackEvent(params.envelope.event, resolution.installation.botUserId)) {\n await recordSlackDeliveryOnly(params);\n return {outcome: 'self-message'};\n }\n\n const supported = slackEventEnvelopeSchema.safeParse(params.envelope);\n if (!supported.success) {\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.envelope.team_id,\n type: params.envelope.event.type,\n },\n 'slack webhook: unsupported event, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {outcome: 'unsupported-event'};\n }\n\n const result = await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: SLACK_PROVIDER,\n source: resolution.connection.slug,\n event: supported.data.event.type,\n workspaceId: resolution.connection.workspaceId,\n connectionId: resolution.connection.id,\n connectionName: resolution.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload: {\n ...supported.data.event,\n team_id: supported.data.team_id,\n api_app_id: supported.data.api_app_id,\n event_id: supported.data.event_id,\n event_time: supported.data.event_time,\n },\n },\n });\n\n return {outcome: result.published ? 'published' : 'duplicate'};\n}\n\nexport async function handleSlackCommand(\n params: HandleSlackCommandParams,\n): Promise<{outcome: Exclude<SlackWebhookOutcome, 'unsupported-event' | 'self-message'>}> {\n const resolution = await resolveSlackConnection({\n ...params,\n teamId: params.command.team_id,\n });\n if (resolution.kind === 'drop') return resolution;\n\n const {token: _token, ...payload} = params.command;\n const result = await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: SLACK_PROVIDER,\n source: resolution.connection.slug,\n event: SLACK_SLASH_COMMAND_EVENT,\n workspaceId: resolution.connection.workspaceId,\n connectionId: resolution.connection.id,\n connectionName: resolution.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload,\n },\n });\n\n return {outcome: result.published ? 'published' : 'duplicate'};\n}\n\nexport function isSelfAuthoredSlackEvent(event: unknown, botUserId: string): boolean {\n const parsed = slackSelfAuthoredEventSchema.safeParse(event);\n if (!parsed.success) return false;\n const nestedMessage = parsed.data.message;\n return (\n parsed.data.bot_id !== undefined ||\n parsed.data.user === botUserId ||\n nestedMessage?.bot_id !== undefined ||\n nestedMessage?.user === botUserId\n );\n}\n\nasync function resolveSlackConnection(\n params: SlackWebhookParams & {teamId: string},\n): Promise<SlackConnectionResolution> {\n const installation = await getSlackInstallationByTeamId(params.teamId, {tx: params.tx});\n if (!installation) {\n logger().warn(\n {deliveryId: params.deliveryId, teamId: params.teamId},\n 'slack webhook: unknown team, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {kind: 'drop', outcome: 'unknown-team'};\n }\n\n if (installation.status !== 'installed') {\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.teamId,\n connectionId: installation.connectionId,\n },\n 'slack webhook: installation is not installed, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {kind: 'drop', outcome: 'revoked-installation'};\n }\n\n const connection = await params.getIntegrationConnectionById(installation.connectionId, {\n tx: params.tx,\n });\n if (!connection) {\n logger().warn(\n {\n deliveryId: params.deliveryId,\n teamId: params.teamId,\n connectionId: installation.connectionId,\n },\n 'slack webhook: installation has no connection, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {kind: 'drop', outcome: 'missing-connection'};\n }\n\n if (connection.lifecycleStatus !== 'active') {\n logger().info(\n {deliveryId: params.deliveryId, teamId: params.teamId, connectionId: connection.id},\n 'slack webhook: inactive connection, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {kind: 'drop', outcome: 'inactive-connection'};\n }\n\n return {kind: 'ok', connection, installation};\n}\n\nasync function recordSlackDeliveryOnly(\n params: Pick<SlackWebhookParams, 'tx' | 'deliveryId' | 'recordDeliveryOnly'>,\n): Promise<void> {\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: SLACK_PROVIDER,\n deliveryId: params.deliveryId,\n });\n}\n"],"names":["SLACK_PROVIDER","SLACK_SLASH_COMMAND_EVENT","slackEventEnvelopeSchema","logger","z","getSlackInstallationByTeamId","slackSelfAuthoredEventSchema","object","bot_id","string","optional","user","message","passthrough","handleSlackEvent","params","resolution","resolveSlackConnection","teamId","envelope","team_id","kind","isSelfAuthoredSlackEvent","event","installation","botUserId","recordSlackDeliveryOnly","outcome","supported","safeParse","success","info","deliveryId","type","result","publishIntegrationEventReceived","tx","provider","source","connection","slug","data","workspaceId","connectionId","id","connectionName","displayName","receivedAt","Date","toISOString","payload","api_app_id","event_id","event_time","published","handleSlackCommand","command","token","_token","parsed","nestedMessage","undefined","warn","status","getIntegrationConnectionById","lifecycleStatus","recordDeliveryOnly"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/webhook.ts"],"sourcesContent":["import type {\n ClaimWebhookDeliveryFn,\n GetIntegrationConnectionByIdFn,\n IntegrationConnection,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n RecordDeliveryOnlyFn,\n} from '@shipfox/api-integration-core-dto';\nimport {\n SLACK_APP_UNINSTALLED_EVENT,\n SLACK_PROVIDER,\n SLACK_SLASH_COMMAND_EVENT,\n type SlackEventBaseEnvelopeDto,\n type SlackLifecycleEventType,\n type SlackSlashCommandDto,\n slackEventEnvelopeSchema,\n slackLifecycleEventTypes,\n slackTokensRevokedEventSchema,\n} from '@shipfox/api-integration-slack-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {z} from 'zod';\nimport {\n getSlackInstallationByTeamId,\n markSlackInstallationRevoked,\n type SlackInstallation,\n} from '#db/installations.js';\n\nconst slackSelfAuthoredEventSchema = z\n .object({\n bot_id: z.string().optional(),\n user: z.string().optional(),\n message: z\n .object({\n bot_id: z.string().optional(),\n user: z.string().optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough();\n\nexport type SlackWebhookOutcome =\n | 'published'\n | 'duplicate'\n | 'unknown-team'\n | 'revoked-installation'\n | 'revoked'\n | 'unaffected-revocation'\n | 'stale-lifecycle-event'\n | 'missing-connection'\n | 'inactive-connection'\n | 'unsupported-event'\n | 'self-message';\n\ninterface SlackWebhookParams {\n tx: IntegrationTx;\n deliveryId: string;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport interface HandleSlackEventParams extends SlackWebhookParams {\n envelope: SlackEventBaseEnvelopeDto;\n claimWebhookDelivery: ClaimWebhookDeliveryFn;\n}\n\nexport interface HandleSlackCommandParams extends SlackWebhookParams {\n command: SlackSlashCommandDto;\n}\n\ntype SlackConnectionResolution =\n | {kind: 'ok'; connection: IntegrationConnection; installation: SlackInstallation}\n | {\n kind: 'drop';\n outcome: SlackConnectionDropOutcome;\n };\n\ntype SlackCommandOutcome = Exclude<\n SlackWebhookOutcome,\n | 'unsupported-event'\n | 'self-message'\n | 'revoked'\n | 'unaffected-revocation'\n | 'stale-lifecycle-event'\n>;\ntype SlackConnectionDropOutcome = Exclude<SlackCommandOutcome, 'published' | 'duplicate'>;\n\nexport async function handleSlackEvent(\n params: HandleSlackEventParams,\n): Promise<{outcome: SlackWebhookOutcome}> {\n const lifecycleType = asSlackLifecycleEventType(params.envelope.event.type);\n if (lifecycleType) return handleSlackLifecycleEvent(params, lifecycleType);\n\n const resolution = await resolveSlackConnection({\n ...params,\n teamId: params.envelope.team_id,\n });\n if (resolution.kind === 'drop') return resolution;\n\n if (isSelfAuthoredSlackEvent(params.envelope.event, resolution.installation.botUserId)) {\n await recordSlackDeliveryOnly(params);\n return {outcome: 'self-message'};\n }\n\n const supported = slackEventEnvelopeSchema.safeParse(params.envelope);\n if (!supported.success) {\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.envelope.team_id,\n type: params.envelope.event.type,\n },\n 'slack webhook: unsupported event, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {outcome: 'unsupported-event'};\n }\n\n const result = await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: SLACK_PROVIDER,\n source: resolution.connection.slug,\n event: supported.data.event.type,\n workspaceId: resolution.connection.workspaceId,\n connectionId: resolution.connection.id,\n connectionName: resolution.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload: {\n ...supported.data.event,\n team_id: supported.data.team_id,\n api_app_id: supported.data.api_app_id,\n event_id: supported.data.event_id,\n event_time: supported.data.event_time,\n },\n },\n });\n\n return {outcome: result.published ? 'published' : 'duplicate'};\n}\n\nexport async function handleSlackCommand(\n params: HandleSlackCommandParams,\n): Promise<{outcome: SlackCommandOutcome}> {\n const resolution = await resolveSlackConnection({\n ...params,\n teamId: params.command.team_id,\n });\n if (resolution.kind === 'drop') return resolution;\n\n const {token: _token, ...payload} = params.command;\n const result = await params.publishIntegrationEventReceived({\n tx: params.tx,\n event: {\n provider: SLACK_PROVIDER,\n source: resolution.connection.slug,\n event: SLACK_SLASH_COMMAND_EVENT,\n workspaceId: resolution.connection.workspaceId,\n connectionId: resolution.connection.id,\n connectionName: resolution.connection.displayName,\n deliveryId: params.deliveryId,\n receivedAt: new Date().toISOString(),\n payload,\n },\n });\n\n return {outcome: result.published ? 'published' : 'duplicate'};\n}\n\nexport function isSelfAuthoredSlackEvent(event: unknown, botUserId: string): boolean {\n const parsed = slackSelfAuthoredEventSchema.safeParse(event);\n if (!parsed.success) return false;\n const nestedMessage = parsed.data.message;\n return (\n parsed.data.bot_id !== undefined ||\n parsed.data.user === botUserId ||\n nestedMessage?.bot_id !== undefined ||\n nestedMessage?.user === botUserId\n );\n}\n\nfunction asSlackLifecycleEventType(eventType: string): SlackLifecycleEventType | undefined {\n return slackLifecycleEventTypes.find((type) => type === eventType);\n}\n\nasync function handleSlackLifecycleEvent(\n params: HandleSlackEventParams,\n lifecycleType: SlackLifecycleEventType,\n): Promise<{outcome: SlackWebhookOutcome}> {\n const claim = await params.claimWebhookDelivery({\n tx: params.tx,\n provider: SLACK_PROVIDER,\n deliveryId: params.deliveryId,\n });\n if (!claim.claimed) {\n logger().info(\n {deliveryId: params.deliveryId, teamId: params.envelope.team_id, lifecycleType},\n 'slack lifecycle event: duplicate delivery, dropping',\n );\n return {outcome: 'duplicate'};\n }\n\n const installation = await getSlackInstallationByTeamId(params.envelope.team_id, {tx: params.tx});\n if (!installation) {\n logger().warn(\n {deliveryId: params.deliveryId, teamId: params.envelope.team_id, lifecycleType},\n 'slack lifecycle event: unknown team, dropping',\n );\n return {outcome: 'unknown-team'};\n }\n\n if (installation.status !== 'installed') {\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.envelope.team_id,\n connectionId: installation.connectionId,\n lifecycleType,\n },\n 'slack lifecycle event: installation is not installed, dropping',\n );\n return {outcome: 'revoked-installation'};\n }\n\n if (\n isSlackLifecycleEventOlderThanInstallation(params.envelope.event_time, installation.updatedAt)\n ) {\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.envelope.team_id,\n connectionId: installation.connectionId,\n },\n 'slack lifecycle event: predates the current installation, dropping',\n );\n return {outcome: 'stale-lifecycle-event'};\n }\n\n const revokesInstallation =\n lifecycleType === SLACK_APP_UNINSTALLED_EVENT ||\n slackTokensRevokedAffectsBot(params.envelope.event, installation.botUserId);\n if (!revokesInstallation) {\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.envelope.team_id,\n connectionId: installation.connectionId,\n },\n 'slack lifecycle event: token revocation does not affect installation bot, dropping',\n );\n return {outcome: 'unaffected-revocation'};\n }\n\n const revoked = await markSlackInstallationRevoked(installation.connectionId, {\n tx: params.tx,\n expectedGeneration: installation.generation,\n });\n if (!revoked) {\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.envelope.team_id,\n connectionId: installation.connectionId,\n },\n 'slack lifecycle event: installation changed before revocation, dropping',\n );\n return {outcome: 'stale-lifecycle-event'};\n }\n\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.envelope.team_id,\n connectionId: installation.connectionId,\n lifecycleType,\n },\n 'slack lifecycle event: installation revoked',\n );\n return {outcome: 'revoked'};\n}\n\nfunction slackTokensRevokedAffectsBot(event: unknown, botUserId: string): boolean {\n const parsed = slackTokensRevokedEventSchema.safeParse(event);\n // Only a named bot token proves this installation credential was revoked; OAuth-only and missing lists do not.\n return parsed.success && (parsed.data.tokens?.bot?.includes(botUserId) ?? false);\n}\n\nfunction isSlackLifecycleEventOlderThanInstallation(\n eventTime: number,\n installationUpdatedAt: Date,\n): boolean {\n // Slack event_time has second precision, so an event stamped in the installation second is not provably stale.\n return eventTime < Math.floor(installationUpdatedAt.getTime() / 1000);\n}\n\nasync function resolveSlackConnection(\n params: SlackWebhookParams & {teamId: string},\n): Promise<SlackConnectionResolution> {\n const installation = await getSlackInstallationByTeamId(params.teamId, {tx: params.tx});\n if (!installation) {\n logger().warn(\n {deliveryId: params.deliveryId, teamId: params.teamId},\n 'slack webhook: unknown team, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {kind: 'drop', outcome: 'unknown-team'};\n }\n\n if (installation.status !== 'installed') {\n logger().info(\n {\n deliveryId: params.deliveryId,\n teamId: params.teamId,\n connectionId: installation.connectionId,\n },\n 'slack webhook: installation is not installed, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {kind: 'drop', outcome: 'revoked-installation'};\n }\n\n const connection = await params.getIntegrationConnectionById(installation.connectionId, {\n tx: params.tx,\n });\n if (!connection) {\n logger().warn(\n {\n deliveryId: params.deliveryId,\n teamId: params.teamId,\n connectionId: installation.connectionId,\n },\n 'slack webhook: installation has no connection, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {kind: 'drop', outcome: 'missing-connection'};\n }\n\n if (connection.lifecycleStatus !== 'active') {\n logger().info(\n {deliveryId: params.deliveryId, teamId: params.teamId, connectionId: connection.id},\n 'slack webhook: inactive connection, dropping',\n );\n await recordSlackDeliveryOnly(params);\n return {kind: 'drop', outcome: 'inactive-connection'};\n }\n\n return {kind: 'ok', connection, installation};\n}\n\nasync function recordSlackDeliveryOnly(\n params: Pick<SlackWebhookParams, 'tx' | 'deliveryId' | 'recordDeliveryOnly'>,\n): Promise<void> {\n await params.recordDeliveryOnly({\n tx: params.tx,\n provider: SLACK_PROVIDER,\n deliveryId: params.deliveryId,\n });\n}\n"],"names":["SLACK_APP_UNINSTALLED_EVENT","SLACK_PROVIDER","SLACK_SLASH_COMMAND_EVENT","slackEventEnvelopeSchema","slackLifecycleEventTypes","slackTokensRevokedEventSchema","logger","z","getSlackInstallationByTeamId","markSlackInstallationRevoked","slackSelfAuthoredEventSchema","object","bot_id","string","optional","user","message","passthrough","handleSlackEvent","params","lifecycleType","asSlackLifecycleEventType","envelope","event","type","handleSlackLifecycleEvent","resolution","resolveSlackConnection","teamId","team_id","kind","isSelfAuthoredSlackEvent","installation","botUserId","recordSlackDeliveryOnly","outcome","supported","safeParse","success","info","deliveryId","result","publishIntegrationEventReceived","tx","provider","source","connection","slug","data","workspaceId","connectionId","id","connectionName","displayName","receivedAt","Date","toISOString","payload","api_app_id","event_id","event_time","published","handleSlackCommand","command","token","_token","parsed","nestedMessage","undefined","eventType","find","claim","claimWebhookDelivery","claimed","warn","status","isSlackLifecycleEventOlderThanInstallation","updatedAt","revokesInstallation","slackTokensRevokedAffectsBot","revoked","expectedGeneration","generation","tokens","bot","includes","eventTime","installationUpdatedAt","Math","floor","getTime","getIntegrationConnectionById","lifecycleStatus","recordDeliveryOnly"],"mappings":"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"}
|
package/dist/db/db.d.ts
CHANGED
|
@@ -144,6 +144,23 @@ export declare const schema: {
|
|
|
144
144
|
}, {}, {
|
|
145
145
|
$type: import("./installations.js").SlackInstallationStatus;
|
|
146
146
|
}>;
|
|
147
|
+
generation: import("drizzle-orm/pg-core").PgColumn<{
|
|
148
|
+
name: "generation";
|
|
149
|
+
tableName: "installations";
|
|
150
|
+
dataType: "number";
|
|
151
|
+
columnType: "PgInteger";
|
|
152
|
+
data: number;
|
|
153
|
+
driverParam: string | number;
|
|
154
|
+
notNull: true;
|
|
155
|
+
hasDefault: true;
|
|
156
|
+
isPrimaryKey: false;
|
|
157
|
+
isAutoincrement: false;
|
|
158
|
+
hasRuntimeDefault: false;
|
|
159
|
+
enumValues: undefined;
|
|
160
|
+
baseColumn: never;
|
|
161
|
+
identity: undefined;
|
|
162
|
+
generated: undefined;
|
|
163
|
+
}, {}, {}>;
|
|
147
164
|
tokenExpiresAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
148
165
|
name: "token_expires_at";
|
|
149
166
|
tableName: "installations";
|
|
@@ -344,6 +361,23 @@ export declare function db(): NodePgDatabase<{
|
|
|
344
361
|
}, {}, {
|
|
345
362
|
$type: import("./installations.js").SlackInstallationStatus;
|
|
346
363
|
}>;
|
|
364
|
+
generation: import("drizzle-orm/pg-core").PgColumn<{
|
|
365
|
+
name: "generation";
|
|
366
|
+
tableName: "installations";
|
|
367
|
+
dataType: "number";
|
|
368
|
+
columnType: "PgInteger";
|
|
369
|
+
data: number;
|
|
370
|
+
driverParam: string | number;
|
|
371
|
+
notNull: true;
|
|
372
|
+
hasDefault: true;
|
|
373
|
+
isPrimaryKey: false;
|
|
374
|
+
isAutoincrement: false;
|
|
375
|
+
hasRuntimeDefault: false;
|
|
376
|
+
enumValues: undefined;
|
|
377
|
+
baseColumn: never;
|
|
378
|
+
identity: undefined;
|
|
379
|
+
generated: undefined;
|
|
380
|
+
}, {}, {}>;
|
|
347
381
|
tokenExpiresAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
348
382
|
name: "token_expires_at";
|
|
349
383
|
tableName: "installations";
|
package/dist/db/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAInE,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAInE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAuB,CAAC;AAI3C,wBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGjB;AAED,wBAAgB,OAAO,IAAI,IAAI,CAE9B"}
|
|
@@ -8,6 +8,7 @@ export interface SlackInstallation {
|
|
|
8
8
|
botUserId: string;
|
|
9
9
|
scopes: string[];
|
|
10
10
|
status: SlackInstallationStatus;
|
|
11
|
+
generation: number;
|
|
11
12
|
tokenExpiresAt: Date | null;
|
|
12
13
|
createdAt: Date;
|
|
13
14
|
updatedAt: Date;
|
|
@@ -36,5 +37,6 @@ export declare function deleteSlackInstallationByConnectionId(connectionId: stri
|
|
|
36
37
|
}): Promise<boolean>;
|
|
37
38
|
export declare function markSlackInstallationRevoked(connectionId: string, options?: {
|
|
38
39
|
tx?: unknown;
|
|
40
|
+
expectedGeneration?: number | undefined;
|
|
39
41
|
}): Promise<SlackInstallation | undefined>;
|
|
40
42
|
//# sourceMappingURL=installations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/db/installations.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,uBAAuB,CAAC;IAChC,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,6BAA6B;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,uBAAuB,CAAC;IAChC,cAAc,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1C;AAKD,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,EACrC,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/db/installations.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,uBAAuB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,6BAA6B;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,uBAAuB,CAAC;IAChC,cAAc,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1C;AAKD,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,EACrC,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CA4C5B;AAED,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAUxC;AAED,wBAAsB,kCAAkC,CACtD,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAUxC;AAED,wBAAsB,qCAAqC,CACzD,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED,wBAAsB,4BAA4B,CAChD,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAM,GACpE,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAexC"}
|
package/dist/db/installations.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isUniqueViolation } from '@shipfox/node-drizzle';
|
|
2
|
-
import { eq } from 'drizzle-orm';
|
|
2
|
+
import { and, eq, sql } from 'drizzle-orm';
|
|
3
3
|
import { SlackConnectionAlreadyLinkedError, SlackInstallationAlreadyLinkedError } from '#core/errors.js';
|
|
4
4
|
import { db } from './db.js';
|
|
5
5
|
import { slackInstallations, toSlackInstallation } from './schema/installations.js';
|
|
@@ -28,6 +28,7 @@ export async function upsertSlackInstallation(params, options = {}) {
|
|
|
28
28
|
botUserId: params.botUserId,
|
|
29
29
|
scopes: params.scopes,
|
|
30
30
|
status: params.status,
|
|
31
|
+
generation: sql`${slackInstallations.generation} + 1`,
|
|
31
32
|
tokenExpiresAt: params.tokenExpiresAt ?? null,
|
|
32
33
|
updatedAt: now
|
|
33
34
|
}
|
|
@@ -62,10 +63,11 @@ export async function deleteSlackInstallationByConnectionId(connectionId, option
|
|
|
62
63
|
}
|
|
63
64
|
export async function markSlackInstallationRevoked(connectionId, options = {}) {
|
|
64
65
|
const executor = options.tx ?? db();
|
|
66
|
+
const expectedInstallation = options.expectedGeneration ? and(eq(slackInstallations.connectionId, connectionId), eq(slackInstallations.generation, options.expectedGeneration)) : eq(slackInstallations.connectionId, connectionId);
|
|
65
67
|
const [row] = await executor.update(slackInstallations).set({
|
|
66
68
|
status: 'revoked',
|
|
67
69
|
updatedAt: new Date()
|
|
68
|
-
}).where(
|
|
70
|
+
}).where(expectedInstallation).returning();
|
|
69
71
|
if (!row) return undefined;
|
|
70
72
|
return toSlackInstallation(row);
|
|
71
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/installations.ts"],"sourcesContent":["import {isUniqueViolation} from '@shipfox/node-drizzle';\nimport {eq} from 'drizzle-orm';\nimport {\n SlackConnectionAlreadyLinkedError,\n SlackInstallationAlreadyLinkedError,\n} from '#core/errors.js';\nimport {db} from './db.js';\nimport {slackInstallations, toSlackInstallation} from './schema/installations.js';\n\nexport type SlackInstallationStatus = 'installed' | 'revoked';\n\nexport interface SlackInstallation {\n id: string;\n connectionId: string;\n teamId: string;\n teamName: string;\n appId: string;\n botUserId: string;\n scopes: string[];\n status: SlackInstallationStatus;\n tokenExpiresAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertSlackInstallationParams {\n connectionId: string;\n teamId: string;\n teamName: string;\n appId: string;\n botUserId: string;\n scopes: string[];\n status: SlackInstallationStatus;\n tokenExpiresAt?: Date | null | undefined;\n}\n\ntype SlackDb = ReturnType<typeof db>;\ntype SlackTx = Parameters<Parameters<SlackDb['transaction']>[0]>[0];\n\nexport async function upsertSlackInstallation(\n params: UpsertSlackInstallationParams,\n options: {tx?: unknown} = {},\n): Promise<SlackInstallation> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const now = new Date();\n let row: typeof slackInstallations.$inferSelect | undefined;\n\n try {\n [row] = await executor\n .insert(slackInstallations)\n .values({\n connectionId: params.connectionId,\n teamId: params.teamId,\n teamName: params.teamName,\n appId: params.appId,\n botUserId: params.botUserId,\n scopes: params.scopes,\n status: params.status,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n })\n .onConflictDoUpdate({\n target: slackInstallations.teamId,\n setWhere: eq(slackInstallations.connectionId, params.connectionId),\n set: {\n connectionId: params.connectionId,\n teamId: params.teamId,\n teamName: params.teamName,\n appId: params.appId,\n botUserId: params.botUserId,\n scopes: params.scopes,\n status: params.status,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n updatedAt: now,\n },\n })\n .returning();\n } catch (error) {\n if (isUniqueViolation(error, 'integrations_slack_installations_connection_unique')) {\n throw new SlackConnectionAlreadyLinkedError(params.connectionId);\n }\n throw error;\n }\n\n if (!row) throw new SlackInstallationAlreadyLinkedError(params.teamId);\n return toSlackInstallation(row);\n}\n\nexport async function getSlackInstallationByTeamId(\n teamId: string,\n options: {tx?: unknown} = {},\n): Promise<SlackInstallation | undefined> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const rows = await executor\n .select()\n .from(slackInstallations)\n .where(eq(slackInstallations.teamId, teamId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toSlackInstallation(row);\n}\n\nexport async function getSlackInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<SlackInstallation | undefined> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const rows = await executor\n .select()\n .from(slackInstallations)\n .where(eq(slackInstallations.connectionId, connectionId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toSlackInstallation(row);\n}\n\nexport async function deleteSlackInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<boolean> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const result = await executor\n .delete(slackInstallations)\n .where(eq(slackInstallations.connectionId, connectionId));\n return (result.rowCount ?? 0) > 0;\n}\n\nexport async function markSlackInstallationRevoked(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<SlackInstallation | undefined> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const [row] = await executor\n .update(slackInstallations)\n .set({status: 'revoked', updatedAt: new Date()})\n .where(
|
|
1
|
+
{"version":3,"sources":["../../src/db/installations.ts"],"sourcesContent":["import {isUniqueViolation} from '@shipfox/node-drizzle';\nimport {and, eq, sql} from 'drizzle-orm';\nimport {\n SlackConnectionAlreadyLinkedError,\n SlackInstallationAlreadyLinkedError,\n} from '#core/errors.js';\nimport {db} from './db.js';\nimport {slackInstallations, toSlackInstallation} from './schema/installations.js';\n\nexport type SlackInstallationStatus = 'installed' | 'revoked';\n\nexport interface SlackInstallation {\n id: string;\n connectionId: string;\n teamId: string;\n teamName: string;\n appId: string;\n botUserId: string;\n scopes: string[];\n status: SlackInstallationStatus;\n generation: number;\n tokenExpiresAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertSlackInstallationParams {\n connectionId: string;\n teamId: string;\n teamName: string;\n appId: string;\n botUserId: string;\n scopes: string[];\n status: SlackInstallationStatus;\n tokenExpiresAt?: Date | null | undefined;\n}\n\ntype SlackDb = ReturnType<typeof db>;\ntype SlackTx = Parameters<Parameters<SlackDb['transaction']>[0]>[0];\n\nexport async function upsertSlackInstallation(\n params: UpsertSlackInstallationParams,\n options: {tx?: unknown} = {},\n): Promise<SlackInstallation> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const now = new Date();\n let row: typeof slackInstallations.$inferSelect | undefined;\n\n try {\n [row] = await executor\n .insert(slackInstallations)\n .values({\n connectionId: params.connectionId,\n teamId: params.teamId,\n teamName: params.teamName,\n appId: params.appId,\n botUserId: params.botUserId,\n scopes: params.scopes,\n status: params.status,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n })\n .onConflictDoUpdate({\n target: slackInstallations.teamId,\n setWhere: eq(slackInstallations.connectionId, params.connectionId),\n set: {\n connectionId: params.connectionId,\n teamId: params.teamId,\n teamName: params.teamName,\n appId: params.appId,\n botUserId: params.botUserId,\n scopes: params.scopes,\n status: params.status,\n generation: sql`${slackInstallations.generation} + 1`,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n updatedAt: now,\n },\n })\n .returning();\n } catch (error) {\n if (isUniqueViolation(error, 'integrations_slack_installations_connection_unique')) {\n throw new SlackConnectionAlreadyLinkedError(params.connectionId);\n }\n throw error;\n }\n\n if (!row) throw new SlackInstallationAlreadyLinkedError(params.teamId);\n return toSlackInstallation(row);\n}\n\nexport async function getSlackInstallationByTeamId(\n teamId: string,\n options: {tx?: unknown} = {},\n): Promise<SlackInstallation | undefined> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const rows = await executor\n .select()\n .from(slackInstallations)\n .where(eq(slackInstallations.teamId, teamId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toSlackInstallation(row);\n}\n\nexport async function getSlackInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<SlackInstallation | undefined> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const rows = await executor\n .select()\n .from(slackInstallations)\n .where(eq(slackInstallations.connectionId, connectionId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toSlackInstallation(row);\n}\n\nexport async function deleteSlackInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<boolean> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const result = await executor\n .delete(slackInstallations)\n .where(eq(slackInstallations.connectionId, connectionId));\n return (result.rowCount ?? 0) > 0;\n}\n\nexport async function markSlackInstallationRevoked(\n connectionId: string,\n options: {tx?: unknown; expectedGeneration?: number | undefined} = {},\n): Promise<SlackInstallation | undefined> {\n const executor = (options.tx ?? db()) as SlackDb | SlackTx;\n const expectedInstallation = options.expectedGeneration\n ? and(\n eq(slackInstallations.connectionId, connectionId),\n eq(slackInstallations.generation, options.expectedGeneration),\n )\n : eq(slackInstallations.connectionId, connectionId);\n const [row] = await executor\n .update(slackInstallations)\n .set({status: 'revoked', updatedAt: new Date()})\n .where(expectedInstallation)\n .returning();\n if (!row) return undefined;\n return toSlackInstallation(row);\n}\n"],"names":["isUniqueViolation","and","eq","sql","SlackConnectionAlreadyLinkedError","SlackInstallationAlreadyLinkedError","db","slackInstallations","toSlackInstallation","upsertSlackInstallation","params","options","executor","tx","now","Date","row","insert","values","connectionId","teamId","teamName","appId","botUserId","scopes","status","tokenExpiresAt","onConflictDoUpdate","target","setWhere","set","generation","updatedAt","returning","error","getSlackInstallationByTeamId","rows","select","from","where","limit","undefined","getSlackInstallationByConnectionId","deleteSlackInstallationByConnectionId","result","delete","rowCount","markSlackInstallationRevoked","expectedInstallation","expectedGeneration","update"],"mappings":"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"}
|
|
@@ -143,6 +143,23 @@ export declare const slackInstallations: import("drizzle-orm/pg-core").PgTableWi
|
|
|
143
143
|
}, {}, {
|
|
144
144
|
$type: import("#db/installations.js").SlackInstallationStatus;
|
|
145
145
|
}>;
|
|
146
|
+
generation: import("drizzle-orm/pg-core").PgColumn<{
|
|
147
|
+
name: "generation";
|
|
148
|
+
tableName: "installations";
|
|
149
|
+
dataType: "number";
|
|
150
|
+
columnType: "PgInteger";
|
|
151
|
+
data: number;
|
|
152
|
+
driverParam: string | number;
|
|
153
|
+
notNull: true;
|
|
154
|
+
hasDefault: true;
|
|
155
|
+
isPrimaryKey: false;
|
|
156
|
+
isAutoincrement: false;
|
|
157
|
+
hasRuntimeDefault: false;
|
|
158
|
+
enumValues: undefined;
|
|
159
|
+
baseColumn: never;
|
|
160
|
+
identity: undefined;
|
|
161
|
+
generated: undefined;
|
|
162
|
+
}, {}, {}>;
|
|
146
163
|
tokenExpiresAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
147
164
|
name: "token_expires_at";
|
|
148
165
|
tableName: "installations";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../../src/db/schema/installations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,sBAAsB,CAAC;AAG5D,eAAO,MAAM,kBAAkB
|
|
1
|
+
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../../src/db/schema/installations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,sBAAsB,CAAC;AAG5D,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoB9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAC;AACzE,MAAM,MAAM,yBAAyB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAC;AAE/E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,mBAAmB,GAAG,iBAAiB,CAe/E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { uuidv7PrimaryKey } from '@shipfox/node-drizzle';
|
|
2
|
-
import { jsonb, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { integer, jsonb, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
|
|
3
3
|
import { pgTable } from './common.js';
|
|
4
4
|
export const slackInstallations = pgTable('installations', {
|
|
5
5
|
id: uuidv7PrimaryKey(),
|
|
@@ -10,6 +10,7 @@ export const slackInstallations = pgTable('installations', {
|
|
|
10
10
|
botUserId: text('bot_user_id').notNull(),
|
|
11
11
|
scopes: jsonb('scopes').notNull().$type(),
|
|
12
12
|
status: text('status').notNull().$type(),
|
|
13
|
+
generation: integer('generation').notNull().default(1),
|
|
13
14
|
tokenExpiresAt: timestamp('token_expires_at', {
|
|
14
15
|
withTimezone: true
|
|
15
16
|
}),
|
|
@@ -33,6 +34,7 @@ export function toSlackInstallation(row) {
|
|
|
33
34
|
botUserId: row.botUserId,
|
|
34
35
|
scopes: row.scopes,
|
|
35
36
|
status: row.status,
|
|
37
|
+
generation: row.generation,
|
|
36
38
|
tokenExpiresAt: row.tokenExpiresAt,
|
|
37
39
|
createdAt: row.createdAt,
|
|
38
40
|
updatedAt: row.updatedAt
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/db/schema/installations.ts"],"sourcesContent":["import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';\nimport {jsonb, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';\nimport type {SlackInstallation} from '#db/installations.js';\nimport {pgTable} from './common.js';\n\nexport const slackInstallations = pgTable(\n 'installations',\n {\n id: uuidv7PrimaryKey(),\n connectionId: uuid('connection_id').notNull(),\n teamId: text('team_id').notNull(),\n teamName: text('team_name').notNull(),\n appId: text('app_id').notNull(),\n botUserId: text('bot_user_id').notNull(),\n scopes: jsonb('scopes').notNull().$type<string[]>(),\n status: text('status').notNull().$type<SlackInstallation['status']>(),\n tokenExpiresAt: timestamp('token_expires_at', {withTimezone: true}),\n createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),\n },\n (table) => [\n uniqueIndex('integrations_slack_installations_connection_unique').on(table.connectionId),\n uniqueIndex('integrations_slack_installations_team_unique').on(table.teamId),\n ],\n);\n\nexport type SlackInstallationDb = typeof slackInstallations.$inferSelect;\nexport type SlackInstallationCreateDb = typeof slackInstallations.$inferInsert;\n\nexport function toSlackInstallation(row: SlackInstallationDb): SlackInstallation {\n return {\n id: row.id,\n connectionId: row.connectionId,\n teamId: row.teamId,\n teamName: row.teamName,\n appId: row.appId,\n botUserId: row.botUserId,\n scopes: row.scopes,\n status: row.status,\n tokenExpiresAt: row.tokenExpiresAt,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n"],"names":["uuidv7PrimaryKey","jsonb","text","timestamp","uniqueIndex","uuid","pgTable","slackInstallations","id","connectionId","notNull","teamId","teamName","appId","botUserId","scopes","$type","status","tokenExpiresAt","withTimezone","createdAt","defaultNow","updatedAt","table","on","toSlackInstallation","row"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,wBAAwB;AACvD,SAAQC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,IAAI,QAAO,sBAAsB;
|
|
1
|
+
{"version":3,"sources":["../../../src/db/schema/installations.ts"],"sourcesContent":["import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';\nimport {integer, jsonb, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';\nimport type {SlackInstallation} from '#db/installations.js';\nimport {pgTable} from './common.js';\n\nexport const slackInstallations = pgTable(\n 'installations',\n {\n id: uuidv7PrimaryKey(),\n connectionId: uuid('connection_id').notNull(),\n teamId: text('team_id').notNull(),\n teamName: text('team_name').notNull(),\n appId: text('app_id').notNull(),\n botUserId: text('bot_user_id').notNull(),\n scopes: jsonb('scopes').notNull().$type<string[]>(),\n status: text('status').notNull().$type<SlackInstallation['status']>(),\n generation: integer('generation').notNull().default(1),\n tokenExpiresAt: timestamp('token_expires_at', {withTimezone: true}),\n createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),\n },\n (table) => [\n uniqueIndex('integrations_slack_installations_connection_unique').on(table.connectionId),\n uniqueIndex('integrations_slack_installations_team_unique').on(table.teamId),\n ],\n);\n\nexport type SlackInstallationDb = typeof slackInstallations.$inferSelect;\nexport type SlackInstallationCreateDb = typeof slackInstallations.$inferInsert;\n\nexport function toSlackInstallation(row: SlackInstallationDb): SlackInstallation {\n return {\n id: row.id,\n connectionId: row.connectionId,\n teamId: row.teamId,\n teamName: row.teamName,\n appId: row.appId,\n botUserId: row.botUserId,\n scopes: row.scopes,\n status: row.status,\n generation: row.generation,\n tokenExpiresAt: row.tokenExpiresAt,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n"],"names":["uuidv7PrimaryKey","integer","jsonb","text","timestamp","uniqueIndex","uuid","pgTable","slackInstallations","id","connectionId","notNull","teamId","teamName","appId","botUserId","scopes","$type","status","generation","default","tokenExpiresAt","withTimezone","createdAt","defaultNow","updatedAt","table","on","toSlackInstallation","row"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,wBAAwB;AACvD,SAAQC,OAAO,EAAEC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,IAAI,QAAO,sBAAsB;AAEvF,SAAQC,OAAO,QAAO,cAAc;AAEpC,OAAO,MAAMC,qBAAqBD,QAChC,iBACA;IACEE,IAAIT;IACJU,cAAcJ,KAAK,iBAAiBK,OAAO;IAC3CC,QAAQT,KAAK,WAAWQ,OAAO;IAC/BE,UAAUV,KAAK,aAAaQ,OAAO;IACnCG,OAAOX,KAAK,UAAUQ,OAAO;IAC7BI,WAAWZ,KAAK,eAAeQ,OAAO;IACtCK,QAAQd,MAAM,UAAUS,OAAO,GAAGM,KAAK;IACvCC,QAAQf,KAAK,UAAUQ,OAAO,GAAGM,KAAK;IACtCE,YAAYlB,QAAQ,cAAcU,OAAO,GAAGS,OAAO,CAAC;IACpDC,gBAAgBjB,UAAU,oBAAoB;QAACkB,cAAc;IAAI;IACjEC,WAAWnB,UAAU,cAAc;QAACkB,cAAc;IAAI,GAAGX,OAAO,GAAGa,UAAU;IAC7EC,WAAWrB,UAAU,cAAc;QAACkB,cAAc;IAAI,GAAGX,OAAO,GAAGa,UAAU;AAC/E,GACA,CAACE,QAAU;QACTrB,YAAY,sDAAsDsB,EAAE,CAACD,MAAMhB,YAAY;QACvFL,YAAY,gDAAgDsB,EAAE,CAACD,MAAMd,MAAM;KAC5E,EACD;AAKF,OAAO,SAASgB,oBAAoBC,GAAwB;IAC1D,OAAO;QACLpB,IAAIoB,IAAIpB,EAAE;QACVC,cAAcmB,IAAInB,YAAY;QAC9BE,QAAQiB,IAAIjB,MAAM;QAClBC,UAAUgB,IAAIhB,QAAQ;QACtBC,OAAOe,IAAIf,KAAK;QAChBC,WAAWc,IAAId,SAAS;QACxBC,QAAQa,IAAIb,MAAM;QAClBE,QAAQW,IAAIX,MAAM;QAClBC,YAAYU,IAAIV,UAAU;QAC1BE,gBAAgBQ,IAAIR,cAAc;QAClCE,WAAWM,IAAIN,SAAS;QACxBE,WAAWI,IAAIJ,SAAS;IAC1B;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,13 +23,15 @@ export type { ConnectSlackInstallationInput, HandleSlackCallbackParams } from '#
|
|
|
23
23
|
export { handleSlackCallback, handleSlackOAuthCallbackError } from '#core/install.js';
|
|
24
24
|
export { assertSlackAuthorizationScopes, formatSlackBotScopes, SLACK_BOT_SCOPES, } from '#core/scopes.js';
|
|
25
25
|
export type { VerifySlackSignatureParams } from '#core/signature.js';
|
|
26
|
-
export { verifySlackSignature } from '#core/signature.js';
|
|
26
|
+
export { isSlackTimestampWithinReplayWindow, verifySlackSignature } from '#core/signature.js';
|
|
27
27
|
export type { SlackInstallStateClaims } from '#core/state.js';
|
|
28
28
|
export { signSlackInstallState, verifySlackInstallState } from '#core/state.js';
|
|
29
29
|
export type { CreateSlackTokenStoreParams, GetSlackAccessTokenParams, SlackConnectionResolverResult, SlackSecretsStore, SlackTokenStore, StoreSlackTokensParams, } from '#core/tokens.js';
|
|
30
30
|
export { createSlackTokenStore, slackSecretsNamespace } from '#core/tokens.js';
|
|
31
31
|
export type { HandleSlackCommandParams, HandleSlackEventParams, SlackWebhookOutcome, } from '#core/webhook.js';
|
|
32
32
|
export { handleSlackCommand, handleSlackEvent, isSelfAuthoredSlackEvent } from '#core/webhook.js';
|
|
33
|
+
export type { CreateSlackWebhookProcessorOptions, SlackWebhookProcessingResult, SlackWebhookProcessor, } from '#core/webhook-processor.js';
|
|
34
|
+
export { createSlackWebhookProcessor } from '#core/webhook-processor.js';
|
|
33
35
|
export type { SlackInstallation, SlackInstallationStatus, UpsertSlackInstallationParams, } from '#db/installations.js';
|
|
34
36
|
export { deleteSlackInstallationByConnectionId, getSlackInstallationByConnectionId, getSlackInstallationByTeamId, markSlackInstallationRevoked, upsertSlackInstallation, } from '#db/installations.js';
|
|
35
37
|
export { type CreateSlackE2eRoutesOptions, createSlackE2eRoutes, } from '#presentation/e2eRoutes/index.js';
|
|
@@ -41,9 +43,34 @@ export interface CreateSlackIntegrationProviderOptions {
|
|
|
41
43
|
tokenStore: Pick<SlackTokenStore, 'getAccessToken'>;
|
|
42
44
|
} | undefined;
|
|
43
45
|
getSlackInstallationByConnectionId?: typeof getSlackInstallationByConnectionId | undefined;
|
|
46
|
+
cleanup?: {
|
|
47
|
+
deleteConnectionRecords?: (connection: {
|
|
48
|
+
id: string;
|
|
49
|
+
}, options: {
|
|
50
|
+
tx: unknown;
|
|
51
|
+
}) => Promise<void>;
|
|
52
|
+
deleteConnectionSecrets?: (connection: {
|
|
53
|
+
id: string;
|
|
54
|
+
workspaceId: string;
|
|
55
|
+
}) => Promise<void>;
|
|
56
|
+
} | undefined;
|
|
44
57
|
routes?: SlackRouteOptions | undefined;
|
|
45
58
|
}
|
|
46
59
|
export declare function createSlackIntegrationProvider(options?: CreateSlackIntegrationProviderOptions): {
|
|
60
|
+
routes: RouteGroup[];
|
|
61
|
+
webhookProcessors: {
|
|
62
|
+
routeIds: readonly ["slack.event", "slack.command"];
|
|
63
|
+
processor: import("#core/webhook-processor.js").SlackWebhookProcessor;
|
|
64
|
+
}[] | undefined;
|
|
65
|
+
deleteConnectionRecords?: (connection: {
|
|
66
|
+
id: string;
|
|
67
|
+
}, options: {
|
|
68
|
+
tx: unknown;
|
|
69
|
+
}) => Promise<void>;
|
|
70
|
+
deleteConnectionSecrets?: (connection: {
|
|
71
|
+
id: string;
|
|
72
|
+
workspaceId: string;
|
|
73
|
+
}) => Promise<void>;
|
|
47
74
|
provider: string;
|
|
48
75
|
displayName: string;
|
|
49
76
|
adapters: {
|
|
@@ -54,6 +81,5 @@ export declare function createSlackIntegrationProvider(options?: CreateSlackInte
|
|
|
54
81
|
connectionExternalUrl(connection: {
|
|
55
82
|
id: string;
|
|
56
83
|
}): Promise<string | undefined>;
|
|
57
|
-
routes: RouteGroup[];
|
|
58
84
|
};
|
|
59
85
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAuB,KAAK,cAAc,EAAC,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAC,MAAM,EAAC,MAAM,YAAY,CAAC;AAClC,OAAO,EAAC,uBAAuB,EAAC,MAAM,+BAA+B,CAAC;AACtE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { SLACK_PROVIDER } from '@shipfox/api-integration-slack-dto';
|
|
|
2
2
|
import { createSlackApiClient } from '#api/client.js';
|
|
3
3
|
import { config } from '#config.js';
|
|
4
4
|
import { SlackAgentToolsProvider } from '#core/agent-tools-provider.js';
|
|
5
|
+
import { createSlackWebhookProcessor } from '#core/webhook-processor.js';
|
|
5
6
|
import { closeDb, db } from '#db/db.js';
|
|
6
7
|
import { getSlackInstallationByConnectionId } from '#db/installations.js';
|
|
7
8
|
import { migrationsPath } from '#db/migrations.js';
|
|
@@ -14,10 +15,11 @@ export { disconnectSlackInstallation } from '#core/disconnect.js';
|
|
|
14
15
|
export { SlackAccessTokenUnavailableError, SlackAuthorizationScopeMismatchError, SlackBotTokenMissingError, SlackConnectionAlreadyLinkedError, SlackConnectionNotFoundError, SlackEnterpriseInstallUnsupportedError, SlackInstallationAlreadyLinkedError, SlackInstallStateActorMismatchError, SlackInstallStateError, SlackIntegrationProviderError, SlackOAuthCallbackError } from '#core/errors.js';
|
|
15
16
|
export { handleSlackCallback, handleSlackOAuthCallbackError } from '#core/install.js';
|
|
16
17
|
export { assertSlackAuthorizationScopes, formatSlackBotScopes, SLACK_BOT_SCOPES } from '#core/scopes.js';
|
|
17
|
-
export { verifySlackSignature } from '#core/signature.js';
|
|
18
|
+
export { isSlackTimestampWithinReplayWindow, verifySlackSignature } from '#core/signature.js';
|
|
18
19
|
export { signSlackInstallState, verifySlackInstallState } from '#core/state.js';
|
|
19
20
|
export { createSlackTokenStore, slackSecretsNamespace } from '#core/tokens.js';
|
|
20
21
|
export { handleSlackCommand, handleSlackEvent, isSelfAuthoredSlackEvent } from '#core/webhook.js';
|
|
22
|
+
export { createSlackWebhookProcessor } from '#core/webhook-processor.js';
|
|
21
23
|
export { deleteSlackInstallationByConnectionId, getSlackInstallationByConnectionId, getSlackInstallationByTeamId, markSlackInstallationRevoked, upsertSlackInstallation } from '#db/installations.js';
|
|
22
24
|
export { createSlackE2eRoutes } from '#presentation/e2eRoutes/index.js';
|
|
23
25
|
export { createSlackWebhookRoutes, SLACK_WEBHOOK_BODY_LIMIT, SLASH_COMMAND_ACK } from '#presentation/routes/webhooks.js';
|
|
@@ -35,8 +37,9 @@ export function createSlackIntegrationProvider(options = {}) {
|
|
|
35
37
|
throw new Error('Slack webhook routes require every core persistence dependency');
|
|
36
38
|
}
|
|
37
39
|
const routes = [];
|
|
40
|
+
const webhookProcessor = options.routes && hasSlackWebhookRoutesOptions(options.routes) ? createSlackWebhookProcessor(options.routes) : undefined;
|
|
38
41
|
if (options.routes && hasSlackInstallationRoutesOptions(options.routes)) {
|
|
39
|
-
const { tokenStore, getExistingSlackConnection, connectSlackInstallation, disconnectSlackInstallation } = options.routes;
|
|
42
|
+
const { tokenStore, getExistingSlackConnection, connectSlackInstallation, disconnectSlackInstallation, requireActiveWorkspaceMembership } = options.routes;
|
|
40
43
|
routes.push(createSlackIntegrationRoutes({
|
|
41
44
|
slack,
|
|
42
45
|
connectionCapabilities: adapters.agent_tools ? [
|
|
@@ -45,11 +48,17 @@ export function createSlackIntegrationProvider(options = {}) {
|
|
|
45
48
|
tokenStore,
|
|
46
49
|
getExistingSlackConnection,
|
|
47
50
|
connectSlackInstallation,
|
|
48
|
-
disconnectSlackInstallation
|
|
51
|
+
disconnectSlackInstallation,
|
|
52
|
+
...requireActiveWorkspaceMembership ? {
|
|
53
|
+
requireActiveWorkspaceMembership
|
|
54
|
+
} : {}
|
|
49
55
|
}));
|
|
50
56
|
}
|
|
51
|
-
if (options.routes && hasSlackWebhookRoutesOptions(options.routes)) {
|
|
52
|
-
routes.push(...createSlackWebhookRoutes(
|
|
57
|
+
if (options.routes && hasSlackWebhookRoutesOptions(options.routes) && webhookProcessor) {
|
|
58
|
+
routes.push(...createSlackWebhookRoutes({
|
|
59
|
+
...options.routes,
|
|
60
|
+
processor: webhookProcessor
|
|
61
|
+
}));
|
|
53
62
|
}
|
|
54
63
|
return {
|
|
55
64
|
provider: SLACK_PROVIDER,
|
|
@@ -60,14 +69,24 @@ export function createSlackIntegrationProvider(options = {}) {
|
|
|
60
69
|
if (!installation) return undefined;
|
|
61
70
|
return `https://app.slack.com/client/${encodeURIComponent(installation.teamId)}`;
|
|
62
71
|
},
|
|
63
|
-
|
|
72
|
+
...options.cleanup,
|
|
73
|
+
routes,
|
|
74
|
+
webhookProcessors: webhookProcessor ? [
|
|
75
|
+
{
|
|
76
|
+
routeIds: [
|
|
77
|
+
'slack.event',
|
|
78
|
+
'slack.command'
|
|
79
|
+
],
|
|
80
|
+
processor: webhookProcessor
|
|
81
|
+
}
|
|
82
|
+
] : undefined
|
|
64
83
|
};
|
|
65
84
|
}
|
|
66
85
|
function hasSlackInstallationRoutesOptions(routes) {
|
|
67
86
|
return routes.tokenStore !== undefined && routes.getExistingSlackConnection !== undefined && routes.connectSlackInstallation !== undefined && routes.disconnectSlackInstallation !== undefined;
|
|
68
87
|
}
|
|
69
88
|
function hasSlackWebhookRoutesOptions(routes) {
|
|
70
|
-
return routes.coreDb !== undefined && routes.publishIntegrationEventReceived !== undefined && routes.recordDeliveryOnly !== undefined && routes.getIntegrationConnectionById !== undefined;
|
|
89
|
+
return routes.coreDb !== undefined && routes.claimWebhookDelivery !== undefined && routes.publishIntegrationEventReceived !== undefined && routes.recordDeliveryOnly !== undefined && routes.getIntegrationConnectionById !== undefined;
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {SLACK_PROVIDER} from '@shipfox/api-integration-slack-dto';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {createSlackApiClient, type SlackApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {SlackAgentToolsProvider} from '#core/agent-tools-provider.js';\nimport type {SlackTokenStore} from '#core/tokens.js';\nimport {closeDb, db} from '#db/db.js';\nimport {getSlackInstallationByConnectionId} from '#db/installations.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {\n type CreateSlackIntegrationRoutesOptions,\n createSlackIntegrationRoutes,\n} from '#presentation/routes/install.js';\nimport {\n type CreateSlackWebhookRoutesOptions,\n createSlackWebhookRoutes,\n} from '#presentation/routes/webhooks.js';\n\ntype SlackInstallationRouteOptions = Omit<\n CreateSlackIntegrationRoutesOptions,\n 'slack' | 'connectionCapabilities'\n>;\ntype SlackRouteOptions = Partial<SlackInstallationRouteOptions> &\n Partial<CreateSlackWebhookRoutesOptions>;\n\nexport type {SlackProvider} from '@shipfox/api-integration-slack-dto';\nexport type {SlackApiClient, SlackAuthorization, SlackWebApiResponse} from '#api/client.js';\nexport {createSlackApiClient} from '#api/client.js';\nexport type {\n SlackAgentToolCatalogEntry,\n SlackAgentToolId,\n SlackAgentToolRequiredScope,\n} from '#core/agent-tools.js';\nexport {\n SLACK_TOOL_METHODS,\n slackAgentToolCatalog,\n slackAgentToolSelectionCatalog,\n} from '#core/agent-tools.js';\nexport type {\n SlackAgentToolsProviderOptions,\n SlackToolCallResult,\n} from '#core/agent-tools-provider.js';\nexport {SlackAgentToolsProvider} from '#core/agent-tools-provider.js';\nexport {\n type DisconnectSlackInstallationParams,\n disconnectSlackInstallation,\n} from '#core/disconnect.js';\nexport {\n SlackAccessTokenUnavailableError,\n type SlackAccessTokenUnavailableReason,\n SlackAuthorizationScopeMismatchError,\n SlackBotTokenMissingError,\n SlackConnectionAlreadyLinkedError,\n SlackConnectionNotFoundError,\n SlackEnterpriseInstallUnsupportedError,\n SlackInstallationAlreadyLinkedError,\n SlackInstallStateActorMismatchError,\n SlackInstallStateError,\n SlackIntegrationProviderError,\n SlackOAuthCallbackError,\n} from '#core/errors.js';\nexport type {ConnectSlackInstallationInput, HandleSlackCallbackParams} from '#core/install.js';\nexport {handleSlackCallback, handleSlackOAuthCallbackError} from '#core/install.js';\nexport {\n assertSlackAuthorizationScopes,\n formatSlackBotScopes,\n SLACK_BOT_SCOPES,\n} from '#core/scopes.js';\nexport type {VerifySlackSignatureParams} from '#core/signature.js';\nexport {verifySlackSignature} from '#core/signature.js';\nexport type {SlackInstallStateClaims} from '#core/state.js';\nexport {signSlackInstallState, verifySlackInstallState} from '#core/state.js';\nexport type {\n CreateSlackTokenStoreParams,\n GetSlackAccessTokenParams,\n SlackConnectionResolverResult,\n SlackSecretsStore,\n SlackTokenStore,\n StoreSlackTokensParams,\n} from '#core/tokens.js';\nexport {createSlackTokenStore, slackSecretsNamespace} from '#core/tokens.js';\nexport type {\n HandleSlackCommandParams,\n HandleSlackEventParams,\n SlackWebhookOutcome,\n} from '#core/webhook.js';\nexport {handleSlackCommand, handleSlackEvent, isSelfAuthoredSlackEvent} from '#core/webhook.js';\nexport type {\n SlackInstallation,\n SlackInstallationStatus,\n UpsertSlackInstallationParams,\n} from '#db/installations.js';\nexport {\n deleteSlackInstallationByConnectionId,\n getSlackInstallationByConnectionId,\n getSlackInstallationByTeamId,\n markSlackInstallationRevoked,\n upsertSlackInstallation,\n} from '#db/installations.js';\nexport {\n type CreateSlackE2eRoutesOptions,\n createSlackE2eRoutes,\n} from '#presentation/e2eRoutes/index.js';\nexport {\n type CreateSlackWebhookRoutesOptions,\n createSlackWebhookRoutes,\n SLACK_WEBHOOK_BODY_LIMIT,\n SLASH_COMMAND_ACK,\n} from '#presentation/routes/webhooks.js';\nexport {closeDb, config, db, migrationsPath};\n\nexport interface CreateSlackIntegrationProviderOptions {\n slack?: SlackApiClient | undefined;\n agentTools?: {tokenStore: Pick<SlackTokenStore, 'getAccessToken'>} | undefined;\n getSlackInstallationByConnectionId?: typeof getSlackInstallationByConnectionId | undefined;\n routes?: SlackRouteOptions | undefined;\n}\n\nexport function createSlackIntegrationProvider(\n options: CreateSlackIntegrationProviderOptions = {},\n) {\n const slack = options.slack ?? createSlackApiClient();\n const getInstallationByConnectionId =\n options.getSlackInstallationByConnectionId ?? getSlackInstallationByConnectionId;\n const adapters = options.agentTools\n ? {\n agent_tools: new SlackAgentToolsProvider({\n slack,\n tokenStore: options.agentTools.tokenStore,\n }),\n }\n : {};\n if (\n options.routes &&\n !hasSlackInstallationRoutesOptions(options.routes) &&\n !hasSlackWebhookRoutesOptions(options.routes)\n ) {\n throw new Error('Slack webhook routes require every core persistence dependency');\n }\n const routes: RouteGroup[] = [];\n if (options.routes && hasSlackInstallationRoutesOptions(options.routes)) {\n const {\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n } = options.routes;\n routes.push(\n createSlackIntegrationRoutes({\n slack,\n connectionCapabilities: adapters.agent_tools ? ['agent_tools'] : [],\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n }),\n );\n }\n if (options.routes && hasSlackWebhookRoutesOptions(options.routes)) {\n routes.push(...createSlackWebhookRoutes(options.routes));\n }\n\n return {\n provider: SLACK_PROVIDER,\n displayName: 'Slack',\n adapters,\n async connectionExternalUrl(connection: {id: string}): Promise<string | undefined> {\n const installation = await getInstallationByConnectionId(connection.id);\n if (!installation) return undefined;\n return `https://app.slack.com/client/${encodeURIComponent(installation.teamId)}`;\n },\n routes,\n };\n}\n\nfunction hasSlackInstallationRoutesOptions(\n routes: SlackRouteOptions,\n): routes is SlackInstallationRouteOptions {\n return (\n routes.tokenStore !== undefined &&\n routes.getExistingSlackConnection !== undefined &&\n routes.connectSlackInstallation !== undefined &&\n routes.disconnectSlackInstallation !== undefined\n );\n}\n\nfunction hasSlackWebhookRoutesOptions(\n routes: SlackRouteOptions,\n): routes is CreateSlackWebhookRoutesOptions {\n return (\n routes.coreDb !== undefined &&\n routes.publishIntegrationEventReceived !== undefined &&\n routes.recordDeliveryOnly !== undefined &&\n routes.getIntegrationConnectionById !== undefined\n );\n}\n"],"names":["SLACK_PROVIDER","createSlackApiClient","config","SlackAgentToolsProvider","closeDb","db","getSlackInstallationByConnectionId","migrationsPath","createSlackIntegrationRoutes","createSlackWebhookRoutes","SLACK_TOOL_METHODS","slackAgentToolCatalog","slackAgentToolSelectionCatalog","disconnectSlackInstallation","SlackAccessTokenUnavailableError","SlackAuthorizationScopeMismatchError","SlackBotTokenMissingError","SlackConnectionAlreadyLinkedError","SlackConnectionNotFoundError","SlackEnterpriseInstallUnsupportedError","SlackInstallationAlreadyLinkedError","SlackInstallStateActorMismatchError","SlackInstallStateError","SlackIntegrationProviderError","SlackOAuthCallbackError","handleSlackCallback","handleSlackOAuthCallbackError","assertSlackAuthorizationScopes","formatSlackBotScopes","SLACK_BOT_SCOPES","verifySlackSignature","signSlackInstallState","verifySlackInstallState","createSlackTokenStore","slackSecretsNamespace","handleSlackCommand","handleSlackEvent","isSelfAuthoredSlackEvent","deleteSlackInstallationByConnectionId","getSlackInstallationByTeamId","markSlackInstallationRevoked","upsertSlackInstallation","createSlackE2eRoutes","SLACK_WEBHOOK_BODY_LIMIT","SLASH_COMMAND_ACK","createSlackIntegrationProvider","options","slack","getInstallationByConnectionId","adapters","agentTools","agent_tools","tokenStore","routes","hasSlackInstallationRoutesOptions","hasSlackWebhookRoutesOptions","Error","getExistingSlackConnection","connectSlackInstallation","push","connectionCapabilities","provider","displayName","connectionExternalUrl","connection","installation","id","undefined","encodeURIComponent","teamId","coreDb","publishIntegrationEventReceived","recordDeliveryOnly","getIntegrationConnectionById"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {SLACK_PROVIDER} from '@shipfox/api-integration-slack-dto';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {createSlackApiClient, type SlackApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {SlackAgentToolsProvider} from '#core/agent-tools-provider.js';\nimport type {SlackTokenStore} from '#core/tokens.js';\nimport {createSlackWebhookProcessor} from '#core/webhook-processor.js';\nimport {closeDb, db} from '#db/db.js';\nimport {getSlackInstallationByConnectionId} from '#db/installations.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {\n type CreateSlackIntegrationRoutesOptions,\n createSlackIntegrationRoutes,\n} from '#presentation/routes/install.js';\nimport {\n type CreateSlackWebhookRoutesOptions,\n createSlackWebhookRoutes,\n} from '#presentation/routes/webhooks.js';\n\ntype SlackInstallationRouteOptions = Omit<\n CreateSlackIntegrationRoutesOptions,\n 'slack' | 'connectionCapabilities'\n>;\ntype SlackRouteOptions = Partial<SlackInstallationRouteOptions> &\n Partial<CreateSlackWebhookRoutesOptions>;\n\nexport type {SlackProvider} from '@shipfox/api-integration-slack-dto';\nexport type {SlackApiClient, SlackAuthorization, SlackWebApiResponse} from '#api/client.js';\nexport {createSlackApiClient} from '#api/client.js';\nexport type {\n SlackAgentToolCatalogEntry,\n SlackAgentToolId,\n SlackAgentToolRequiredScope,\n} from '#core/agent-tools.js';\nexport {\n SLACK_TOOL_METHODS,\n slackAgentToolCatalog,\n slackAgentToolSelectionCatalog,\n} from '#core/agent-tools.js';\nexport type {\n SlackAgentToolsProviderOptions,\n SlackToolCallResult,\n} from '#core/agent-tools-provider.js';\nexport {SlackAgentToolsProvider} from '#core/agent-tools-provider.js';\nexport {\n type DisconnectSlackInstallationParams,\n disconnectSlackInstallation,\n} from '#core/disconnect.js';\nexport {\n SlackAccessTokenUnavailableError,\n type SlackAccessTokenUnavailableReason,\n SlackAuthorizationScopeMismatchError,\n SlackBotTokenMissingError,\n SlackConnectionAlreadyLinkedError,\n SlackConnectionNotFoundError,\n SlackEnterpriseInstallUnsupportedError,\n SlackInstallationAlreadyLinkedError,\n SlackInstallStateActorMismatchError,\n SlackInstallStateError,\n SlackIntegrationProviderError,\n SlackOAuthCallbackError,\n} from '#core/errors.js';\nexport type {ConnectSlackInstallationInput, HandleSlackCallbackParams} from '#core/install.js';\nexport {handleSlackCallback, handleSlackOAuthCallbackError} from '#core/install.js';\nexport {\n assertSlackAuthorizationScopes,\n formatSlackBotScopes,\n SLACK_BOT_SCOPES,\n} from '#core/scopes.js';\nexport type {VerifySlackSignatureParams} from '#core/signature.js';\nexport {isSlackTimestampWithinReplayWindow, verifySlackSignature} from '#core/signature.js';\nexport type {SlackInstallStateClaims} from '#core/state.js';\nexport {signSlackInstallState, verifySlackInstallState} from '#core/state.js';\nexport type {\n CreateSlackTokenStoreParams,\n GetSlackAccessTokenParams,\n SlackConnectionResolverResult,\n SlackSecretsStore,\n SlackTokenStore,\n StoreSlackTokensParams,\n} from '#core/tokens.js';\nexport {createSlackTokenStore, slackSecretsNamespace} from '#core/tokens.js';\nexport type {\n HandleSlackCommandParams,\n HandleSlackEventParams,\n SlackWebhookOutcome,\n} from '#core/webhook.js';\nexport {handleSlackCommand, handleSlackEvent, isSelfAuthoredSlackEvent} from '#core/webhook.js';\nexport type {\n CreateSlackWebhookProcessorOptions,\n SlackWebhookProcessingResult,\n SlackWebhookProcessor,\n} from '#core/webhook-processor.js';\nexport {createSlackWebhookProcessor} from '#core/webhook-processor.js';\nexport type {\n SlackInstallation,\n SlackInstallationStatus,\n UpsertSlackInstallationParams,\n} from '#db/installations.js';\nexport {\n deleteSlackInstallationByConnectionId,\n getSlackInstallationByConnectionId,\n getSlackInstallationByTeamId,\n markSlackInstallationRevoked,\n upsertSlackInstallation,\n} from '#db/installations.js';\nexport {\n type CreateSlackE2eRoutesOptions,\n createSlackE2eRoutes,\n} from '#presentation/e2eRoutes/index.js';\nexport {\n type CreateSlackWebhookRoutesOptions,\n createSlackWebhookRoutes,\n SLACK_WEBHOOK_BODY_LIMIT,\n SLASH_COMMAND_ACK,\n} from '#presentation/routes/webhooks.js';\nexport {closeDb, config, db, migrationsPath};\n\nexport interface CreateSlackIntegrationProviderOptions {\n slack?: SlackApiClient | undefined;\n agentTools?: {tokenStore: Pick<SlackTokenStore, 'getAccessToken'>} | undefined;\n getSlackInstallationByConnectionId?: typeof getSlackInstallationByConnectionId | undefined;\n cleanup?:\n | {\n deleteConnectionRecords?: (\n connection: {id: string},\n options: {tx: unknown},\n ) => Promise<void>;\n deleteConnectionSecrets?: (connection: {id: string; workspaceId: string}) => Promise<void>;\n }\n | undefined;\n routes?: SlackRouteOptions | undefined;\n}\n\nexport function createSlackIntegrationProvider(\n options: CreateSlackIntegrationProviderOptions = {},\n) {\n const slack = options.slack ?? createSlackApiClient();\n const getInstallationByConnectionId =\n options.getSlackInstallationByConnectionId ?? getSlackInstallationByConnectionId;\n const adapters = options.agentTools\n ? {\n agent_tools: new SlackAgentToolsProvider({\n slack,\n tokenStore: options.agentTools.tokenStore,\n }),\n }\n : {};\n if (\n options.routes &&\n !hasSlackInstallationRoutesOptions(options.routes) &&\n !hasSlackWebhookRoutesOptions(options.routes)\n ) {\n throw new Error('Slack webhook routes require every core persistence dependency');\n }\n const routes: RouteGroup[] = [];\n const webhookProcessor =\n options.routes && hasSlackWebhookRoutesOptions(options.routes)\n ? createSlackWebhookProcessor(options.routes)\n : undefined;\n if (options.routes && hasSlackInstallationRoutesOptions(options.routes)) {\n const {\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n requireActiveWorkspaceMembership,\n } = options.routes;\n routes.push(\n createSlackIntegrationRoutes({\n slack,\n connectionCapabilities: adapters.agent_tools ? ['agent_tools'] : [],\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n ...(requireActiveWorkspaceMembership ? {requireActiveWorkspaceMembership} : {}),\n }),\n );\n }\n if (options.routes && hasSlackWebhookRoutesOptions(options.routes) && webhookProcessor) {\n routes.push(...createSlackWebhookRoutes({...options.routes, processor: webhookProcessor}));\n }\n\n return {\n provider: SLACK_PROVIDER,\n displayName: 'Slack',\n adapters,\n async connectionExternalUrl(connection: {id: string}): Promise<string | undefined> {\n const installation = await getInstallationByConnectionId(connection.id);\n if (!installation) return undefined;\n return `https://app.slack.com/client/${encodeURIComponent(installation.teamId)}`;\n },\n ...options.cleanup,\n routes,\n webhookProcessors: webhookProcessor\n ? [{routeIds: ['slack.event', 'slack.command'] as const, processor: webhookProcessor}]\n : undefined,\n };\n}\n\nfunction hasSlackInstallationRoutesOptions(\n routes: SlackRouteOptions,\n): routes is SlackInstallationRouteOptions {\n return (\n routes.tokenStore !== undefined &&\n routes.getExistingSlackConnection !== undefined &&\n routes.connectSlackInstallation !== undefined &&\n routes.disconnectSlackInstallation !== undefined\n );\n}\n\nfunction hasSlackWebhookRoutesOptions(\n routes: SlackRouteOptions,\n): routes is CreateSlackWebhookRoutesOptions {\n return (\n routes.coreDb !== undefined &&\n routes.claimWebhookDelivery !== undefined &&\n routes.publishIntegrationEventReceived !== undefined &&\n routes.recordDeliveryOnly !== undefined &&\n routes.getIntegrationConnectionById !== undefined\n );\n}\n"],"names":["SLACK_PROVIDER","createSlackApiClient","config","SlackAgentToolsProvider","createSlackWebhookProcessor","closeDb","db","getSlackInstallationByConnectionId","migrationsPath","createSlackIntegrationRoutes","createSlackWebhookRoutes","SLACK_TOOL_METHODS","slackAgentToolCatalog","slackAgentToolSelectionCatalog","disconnectSlackInstallation","SlackAccessTokenUnavailableError","SlackAuthorizationScopeMismatchError","SlackBotTokenMissingError","SlackConnectionAlreadyLinkedError","SlackConnectionNotFoundError","SlackEnterpriseInstallUnsupportedError","SlackInstallationAlreadyLinkedError","SlackInstallStateActorMismatchError","SlackInstallStateError","SlackIntegrationProviderError","SlackOAuthCallbackError","handleSlackCallback","handleSlackOAuthCallbackError","assertSlackAuthorizationScopes","formatSlackBotScopes","SLACK_BOT_SCOPES","isSlackTimestampWithinReplayWindow","verifySlackSignature","signSlackInstallState","verifySlackInstallState","createSlackTokenStore","slackSecretsNamespace","handleSlackCommand","handleSlackEvent","isSelfAuthoredSlackEvent","deleteSlackInstallationByConnectionId","getSlackInstallationByTeamId","markSlackInstallationRevoked","upsertSlackInstallation","createSlackE2eRoutes","SLACK_WEBHOOK_BODY_LIMIT","SLASH_COMMAND_ACK","createSlackIntegrationProvider","options","slack","getInstallationByConnectionId","adapters","agentTools","agent_tools","tokenStore","routes","hasSlackInstallationRoutesOptions","hasSlackWebhookRoutesOptions","Error","webhookProcessor","undefined","getExistingSlackConnection","connectSlackInstallation","requireActiveWorkspaceMembership","push","connectionCapabilities","processor","provider","displayName","connectionExternalUrl","connection","installation","id","encodeURIComponent","teamId","cleanup","webhookProcessors","routeIds","coreDb","claimWebhookDelivery","publishIntegrationEventReceived","recordDeliveryOnly","getIntegrationConnectionById"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/errors.ts"],"names":[],"mappings":"AAyBA,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CA8D5D"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ConnectionSlugConflictError } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import { workspacesInterModuleContract } from '@shipfox/api-workspaces-dto/inter-module';
|
|
3
|
+
import { isInterModuleKnownError } from '@shipfox/inter-module';
|
|
2
4
|
import { ClientError } from '@shipfox/node-fastify';
|
|
3
5
|
import { SlackAuthorizationScopeMismatchError, SlackConnectionAlreadyLinkedError, SlackEnterpriseInstallUnsupportedError, SlackInstallationAlreadyLinkedError, SlackInstallStateActorMismatchError, SlackInstallStateError, SlackIntegrationProviderError, SlackOAuthCallbackError, SlackTokenRotationUnsupportedError } from '#core/errors.js';
|
|
4
6
|
function providerStatus(reason) {
|
|
@@ -7,6 +9,26 @@ function providerStatus(reason) {
|
|
|
7
9
|
return 422;
|
|
8
10
|
}
|
|
9
11
|
export function slackRouteErrorHandler(error) {
|
|
12
|
+
if (isInterModuleKnownError(workspacesInterModuleContract.methods.requireActiveMembership, error)) {
|
|
13
|
+
if (error.code === 'workspace-not-found') throw new ClientError('Workspace not found', 'not-found', {
|
|
14
|
+
status: 404,
|
|
15
|
+
details: {
|
|
16
|
+
workspace_id: error.details.workspaceId
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
if (error.code === 'membership-required') throw new ClientError('Workspace membership required', 'forbidden', {
|
|
20
|
+
status: 403,
|
|
21
|
+
details: {
|
|
22
|
+
workspace_id: error.details.workspaceId
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
throw new ClientError('Workspace is inactive', 'workspace-inactive', {
|
|
26
|
+
status: 403,
|
|
27
|
+
details: {
|
|
28
|
+
workspace_id: error.details.workspaceId
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
10
32
|
if (error instanceof SlackInstallStateError) {
|
|
11
33
|
throw new ClientError(error.message, 'invalid-slack-install-state', {
|
|
12
34
|
status: 400
|
|
@@ -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 {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 (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","ClientError","SlackAuthorizationScopeMismatchError","SlackConnectionAlreadyLinkedError","SlackEnterpriseInstallUnsupportedError","SlackInstallationAlreadyLinkedError","SlackInstallStateActorMismatchError","SlackInstallStateError","SlackIntegrationProviderError","SlackOAuthCallbackError","SlackTokenRotationUnsupportedError","providerStatus","reason","slackRouteErrorHandler","error","
|
|
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":"AAAA,SACEA,2BAA2B,QAEtB,oCAAoC;AAC3C,SAAQC,6BAA6B,QAAO,2CAA2C;AACvF,SAAQC,uBAAuB,QAAO,wBAAwB;AAC9D,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SACEC,oCAAoC,EACpCC,iCAAiC,EACjCC,sCAAsC,EACtCC,mCAAmC,EACnCC,mCAAmC,EACnCC,sBAAsB,EACtBC,6BAA6B,EAC7BC,uBAAuB,EACvBC,kCAAkC,QAC7B,kBAAkB;AAEzB,SAASC,eAAeC,MAAsC;IAC5D,IAAIA,WAAW,gBAAgB,OAAO;IACtC,IAAIA,WAAW,aAAaA,WAAW,wBAAwB,OAAO;IACtE,OAAO;AACT;AAEA,OAAO,SAASC,uBAAuBC,KAAc;IACnD,IACEd,wBAAwBD,8BAA8BgB,OAAO,CAACC,uBAAuB,EAAEF,QACvF;QACA,IAAIA,MAAMG,IAAI,KAAK,uBACjB,MAAM,IAAIhB,YAAY,uBAAuB,aAAa;YACxDiB,QAAQ;YACRC,SAAS;gBAACC,cAAcN,MAAMK,OAAO,CAACE,WAAW;YAAA;QACnD;QACF,IAAIP,MAAMG,IAAI,KAAK,uBACjB,MAAM,IAAIhB,YAAY,iCAAiC,aAAa;YAClEiB,QAAQ;YACRC,SAAS;gBAACC,cAAcN,MAAMK,OAAO,CAACE,WAAW;YAAA;QACnD;QACF,MAAM,IAAIpB,YAAY,yBAAyB,sBAAsB;YACnEiB,QAAQ;YACRC,SAAS;gBAACC,cAAcN,MAAMK,OAAO,CAACE,WAAW;YAAA;QACnD;IACF;IACA,IAAIP,iBAAiBP,wBAAwB;QAC3C,MAAM,IAAIN,YAAYa,MAAMQ,OAAO,EAAE,+BAA+B;YAACJ,QAAQ;QAAG;IAClF;IACA,IAAIJ,iBAAiBR,qCAAqC;QACxD,MAAM,IAAIL,YAAYa,MAAMQ,OAAO,EAAE,sCAAsC;YAACJ,QAAQ;QAAG;IACzF;IACA,IAAIJ,iBAAiBT,qCAAqC;QACxD,MAAM,IAAIJ,YAAYa,MAAMQ,OAAO,EAAE,qCAAqC;YAACJ,QAAQ;QAAG;IACxF;IACA,IAAIJ,iBAAiBX,mCAAmC;QACtD,MAAM,IAAIF,YAAYa,MAAMQ,OAAO,EAAE,mCAAmC;YAACJ,QAAQ;QAAG;IACtF;IACA,IAAIJ,iBAAiBZ,sCAAsC;QACzD,MAAM,IAAID,YAAYa,MAAMQ,OAAO,EAAE,sCAAsC;YACzEJ,QAAQ;YACRC,SAAS;gBAACI,gBAAgBT,MAAMU,aAAa;YAAA;QAC/C;IACF;IACA,IAAIV,iBAAiBV,wCAAwC;QAC3D,MAAM,IAAIH,YAAYa,MAAMQ,OAAO,EAAE,wCAAwC;YAACJ,QAAQ;QAAG;IAC3F;IACA,IAAIJ,iBAAiBJ,oCAAoC;QACvD,MAAM,IAAIT,YAAYa,MAAMQ,OAAO,EAAE,oCAAoC;YAACJ,QAAQ;QAAG;IACvF;IACA,IAAIJ,iBAAiBL,yBAAyB;QAC5C,MAAM,IAAIR,YAAYa,MAAMQ,OAAO,EAAE,8BAA8B;YACjEJ,QAAQ;YACRC,SAAS;gBACPL,OAAOA,MAAMW,aAAa;gBAC1B,GAAIX,MAAMY,mBAAmB,GAAG;oBAACC,mBAAmBb,MAAMY,mBAAmB;gBAAA,IAAI,CAAC,CAAC;YACrF;QACF;IACF;IACA,IAAIZ,iBAAiBhB,6BAA6B;QAChD,MAAM,IAAIG,YAAYa,MAAMQ,OAAO,EAAE,iBAAiB;YAACJ,QAAQ;QAAG;IACpE;IACA,IAAIJ,iBAAiBN,+BAA+B;QAClD,MAAM,IAAIP,YAAYa,MAAMQ,OAAO,EAAER,MAAMF,MAAM,EAAE;YACjDO,SAAS;gBAACS,qBAAqBd,MAAMe,iBAAiB;YAAA;YACtDX,QAAQP,eAAeG,MAAMF,MAAM;QACrC;IACF;IACA,MAAME;AACR"}
|