@shipfox/api-integration-core 4.0.0 → 5.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 +4 -4
- package/CHANGELOG.md +35 -0
- package/dist/db/webhook-deliveries.d.ts +4 -0
- package/dist/db/webhook-deliveries.d.ts.map +1 -1
- package/dist/db/webhook-deliveries.js +10 -2
- package/dist/db/webhook-deliveries.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/index.js +1 -1
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/manage-connections.d.ts +1 -1
- package/dist/presentation/routes/manage-connections.d.ts.map +1 -1
- package/dist/presentation/routes/manage-connections.js +28 -3
- package/dist/presentation/routes/manage-connections.js.map +1 -1
- package/dist/providers/jira.d.ts.map +1 -1
- package/dist/providers/jira.js +110 -3
- package/dist/providers/jira.js.map +1 -1
- package/dist/providers/linear.d.ts.map +1 -1
- package/dist/providers/linear.js +15 -1
- package/dist/providers/linear.js.map +1 -1
- package/dist/providers/slack.d.ts.map +1 -1
- package/dist/providers/slack.js +17 -2
- package/dist/providers/slack.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +28 -38
- package/src/db/webhook-deliveries.test.ts +11 -0
- package/src/db/webhook-deliveries.ts +12 -3
- package/src/index.ts +2 -1
- package/src/presentation/routes/index.ts +1 -1
- package/src/presentation/routes/manage-connections.test.ts +113 -0
- package/src/presentation/routes/manage-connections.ts +26 -2
- package/src/providers/jira.ts +152 -7
- package/src/providers/linear.test.ts +136 -0
- package/src/providers/linear.ts +14 -0
- package/src/providers/slack.test.ts +139 -0
- package/src/providers/slack.ts +20 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/linear.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {\n ConnectLinearInstallationInput,\n LinearSecretsStore,\n} from '@shipfox/api-integration-linear';\nimport {config} from '#config.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst LINEAR_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_linear';\nconst LINEAR_SECRETS_NAMESPACE_PREFIX = 'system/integrations/linear/';\n\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nasync function loadLinearModuleParts(\n options: Parameters<IntegrationProviderModule['load']>[0] = {},\n): Promise<IntegrationModuleParts> {\n const {\n createLinearTokenStore,\n createLinearE2eRoutes,\n createLinearIntegrationProvider,\n config: linearConfig,\n disconnectLinearInstallation: disconnectLinearInstallationRecords,\n getLinearInstallationByOrganizationId,\n db: linearDb,\n migrationsPath: linearMigrationsPath,\n upsertLinearInstallation,\n } = await import('@shipfox/api-integration-linear');\n\n async function getExistingLinearConnection(input: {\n organizationId: string;\n }): Promise<CoreIntegrationConnection<'linear'> | undefined> {\n const installation = await getLinearInstallationByOrganizationId(input.organizationId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'linear'>;\n }\n\n async function connectLinearInstallation(\n input: ConnectLinearInstallationInput,\n ): Promise<CoreIntegrationConnection<'linear'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`linear_${input.organizationUrlKey}`, {\n fallback: 'linear',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'linear',\n externalAccountId: input.organizationId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'linear',\n externalAccountId: input.organizationId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertLinearInstallation(\n {\n connectionId: connection.id,\n organizationId: input.organizationId,\n organizationUrlKey: input.organizationUrlKey,\n appUserId: input.appUserId,\n scopes: input.scopes,\n tokenExpiresAt: input.tokenExpiresAt,\n status: 'installed',\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'linear'>;\n }),\n );\n }\n\n async function disconnectLinearInstallation(input: {connectionId: string}): Promise<void> {\n await disconnectLinearInstallationRecords<IntegrationTx>({\n connectionId: input.connectionId,\n getConnection: getIntegrationConnectionById,\n deleteSecrets: (params) =>\n options.secrets?.linear?.deleteSecrets({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, options) =>\n deleteIntegrationConnection({id: params.connectionId}, options),\n });\n }\n\n const fallbackSecrets: LinearSecretsStore = {\n getSecret: () => Promise.resolve(null),\n setSecrets: () => Promise.reject(new Error('Linear token storage is not configured')),\n };\n const secrets: LinearSecretsStore = options.secrets?.linear\n ? {\n getSecret: (params: Parameters<LinearSecretsStore['getSecret']>[0]) =>\n options.secrets?.linear?.getSecret({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(null),\n setSecrets: (params: Parameters<LinearSecretsStore['setSecrets']>[0]) =>\n options.secrets?.linear?.setSecrets({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(),\n }\n : fallbackSecrets;\n const tokenStore = createLinearTokenStore({\n resolveConnection: async (connectionId) => getIntegrationConnectionById(connectionId),\n secrets,\n });\n\n return {\n provider: createLinearIntegrationProvider({\n agentTools: {tokenStore, endpoint: linearConfig.LINEAR_MCP_ENDPOINT},\n routes: {\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n },\n }),\n e2eRoutes: [\n createLinearE2eRoutes({\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n connectionCapabilities: ['agent_tools'],\n }),\n ],\n database: {\n db: linearDb,\n migrationsPath: linearMigrationsPath,\n migrationsTableName: LINEAR_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const linearProviderModule: IntegrationProviderModule = {\n id: 'linear',\n enabled: config.INTEGRATIONS_ENABLE_LINEAR_PROVIDER,\n load: loadLinearModuleParts,\n};\n\nfunction linearNamespaceSuffix(namespace: string): string {\n if (!namespace.startsWith(LINEAR_SECRETS_NAMESPACE_PREFIX)) {\n throw new Error('Linear provider attempted to access an unscoped secret namespace');\n }\n return namespace.slice(LINEAR_SECRETS_NAMESPACE_PREFIX.length);\n}\n"],"names":["slugifyConnectionSlug","config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","LINEAR_MIGRATIONS_TABLE","LINEAR_SECRETS_NAMESPACE_PREFIX","loadLinearModuleParts","options","createLinearTokenStore","createLinearE2eRoutes","createLinearIntegrationProvider","linearConfig","disconnectLinearInstallation","disconnectLinearInstallationRecords","getLinearInstallationByOrganizationId","linearDb","migrationsPath","linearMigrationsPath","upsertLinearInstallation","getExistingLinearConnection","input","installation","organizationId","undefined","connection","connectionId","connectLinearInstallation","transaction","tx","baseSlug","organizationUrlKey","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","appUserId","scopes","tokenExpiresAt","status","getConnection","deleteSecrets","params","secrets","linear","namespace","linearNamespaceSuffix","Promise","resolve","fn","deleteConnection","fallbackSecrets","getSecret","setSecrets","reject","Error","tokenStore","resolveConnection","agentTools","endpoint","LINEAR_MCP_ENDPOINT","routes","coreDb","e2eRoutes","connectionCapabilities","database","migrationsTableName","linearProviderModule","enabled","INTEGRATIONS_ENABLE_LINEAR_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/linear.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {\n ConnectLinearInstallationInput,\n LinearSecretsStore,\n} from '@shipfox/api-integration-linear';\nimport {config} from '#config.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst LINEAR_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_linear';\nconst LINEAR_SECRETS_NAMESPACE_PREFIX = 'system/integrations/linear/';\n\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nasync function loadLinearModuleParts(\n options: Parameters<IntegrationProviderModule['load']>[0] = {},\n): Promise<IntegrationModuleParts> {\n const {\n createLinearTokenStore,\n createLinearE2eRoutes,\n createLinearIntegrationProvider,\n config: linearConfig,\n deleteLinearInstallationByConnectionId,\n disconnectLinearInstallation: disconnectLinearInstallationRecords,\n getLinearInstallationByOrganizationId,\n db: linearDb,\n linearSecretsNamespace,\n migrationsPath: linearMigrationsPath,\n upsertLinearInstallation,\n } = await import('@shipfox/api-integration-linear');\n\n async function getExistingLinearConnection(input: {\n organizationId: string;\n }): Promise<CoreIntegrationConnection<'linear'> | undefined> {\n const installation = await getLinearInstallationByOrganizationId(input.organizationId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'linear'>;\n }\n\n async function connectLinearInstallation(\n input: ConnectLinearInstallationInput,\n ): Promise<CoreIntegrationConnection<'linear'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`linear_${input.organizationUrlKey}`, {\n fallback: 'linear',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'linear',\n externalAccountId: input.organizationId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'linear',\n externalAccountId: input.organizationId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertLinearInstallation(\n {\n connectionId: connection.id,\n organizationId: input.organizationId,\n organizationUrlKey: input.organizationUrlKey,\n appUserId: input.appUserId,\n scopes: input.scopes,\n tokenExpiresAt: input.tokenExpiresAt,\n status: 'installed',\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'linear'>;\n }),\n );\n }\n\n async function disconnectLinearInstallation(input: {connectionId: string}): Promise<void> {\n await disconnectLinearInstallationRecords<IntegrationTx>({\n connectionId: input.connectionId,\n getConnection: getIntegrationConnectionById,\n deleteSecrets: (params) =>\n options.secrets?.linear?.deleteSecrets({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, options) =>\n deleteIntegrationConnection({id: params.connectionId}, options),\n });\n }\n\n const fallbackSecrets: LinearSecretsStore = {\n getSecret: () => Promise.resolve(null),\n setSecrets: () => Promise.reject(new Error('Linear token storage is not configured')),\n };\n const secrets: LinearSecretsStore = options.secrets?.linear\n ? {\n getSecret: (params: Parameters<LinearSecretsStore['getSecret']>[0]) =>\n options.secrets?.linear?.getSecret({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(null),\n setSecrets: (params: Parameters<LinearSecretsStore['setSecrets']>[0]) =>\n options.secrets?.linear?.setSecrets({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(),\n }\n : fallbackSecrets;\n const tokenStore = createLinearTokenStore({\n resolveConnection: async (connectionId) => getIntegrationConnectionById(connectionId),\n secrets,\n });\n\n return {\n provider: createLinearIntegrationProvider({\n agentTools: {tokenStore, endpoint: linearConfig.LINEAR_MCP_ENDPOINT},\n cleanup: {\n deleteConnectionRecords: async (connection, {tx}) => {\n await deleteLinearInstallationByConnectionId(connection.id, {tx});\n },\n deleteConnectionSecrets: async (connection) => {\n // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.\n await (options.secrets?.linear?.deleteSecrets({\n workspaceId: connection.workspaceId,\n namespace: linearNamespaceSuffix(linearSecretsNamespace(connection.id)),\n }) ?? Promise.resolve());\n },\n },\n routes: {\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n },\n }),\n e2eRoutes: [\n createLinearE2eRoutes({\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n connectionCapabilities: ['agent_tools'],\n }),\n ],\n database: {\n db: linearDb,\n migrationsPath: linearMigrationsPath,\n migrationsTableName: LINEAR_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const linearProviderModule: IntegrationProviderModule = {\n id: 'linear',\n enabled: config.INTEGRATIONS_ENABLE_LINEAR_PROVIDER,\n load: loadLinearModuleParts,\n};\n\nfunction linearNamespaceSuffix(namespace: string): string {\n if (!namespace.startsWith(LINEAR_SECRETS_NAMESPACE_PREFIX)) {\n throw new Error('Linear provider attempted to access an unscoped secret namespace');\n }\n return namespace.slice(LINEAR_SECRETS_NAMESPACE_PREFIX.length);\n}\n"],"names":["slugifyConnectionSlug","config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","LINEAR_MIGRATIONS_TABLE","LINEAR_SECRETS_NAMESPACE_PREFIX","loadLinearModuleParts","options","createLinearTokenStore","createLinearE2eRoutes","createLinearIntegrationProvider","linearConfig","deleteLinearInstallationByConnectionId","disconnectLinearInstallation","disconnectLinearInstallationRecords","getLinearInstallationByOrganizationId","linearDb","linearSecretsNamespace","migrationsPath","linearMigrationsPath","upsertLinearInstallation","getExistingLinearConnection","input","installation","organizationId","undefined","connection","connectionId","connectLinearInstallation","transaction","tx","baseSlug","organizationUrlKey","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","appUserId","scopes","tokenExpiresAt","status","getConnection","deleteSecrets","params","secrets","linear","namespace","linearNamespaceSuffix","Promise","resolve","fn","deleteConnection","fallbackSecrets","getSecret","setSecrets","reject","Error","tokenStore","resolveConnection","agentTools","endpoint","LINEAR_MCP_ENDPOINT","cleanup","deleteConnectionRecords","deleteConnectionSecrets","routes","coreDb","e2eRoutes","connectionCapabilities","database","migrationsTableName","linearProviderModule","enabled","INTEGRATIONS_ENABLE_LINEAR_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/providers/slack.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/providers/slack.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAkK3F,eAAO,MAAM,mBAAmB,EAAE,yBAIjC,CAAC"}
|
package/dist/providers/slack.js
CHANGED
|
@@ -2,12 +2,12 @@ import { slugifyConnectionSlug } from '@shipfox/api-integration-core-dto';
|
|
|
2
2
|
import { config } from '#config.js';
|
|
3
3
|
import { deleteIntegrationConnection, getIntegrationConnectionById, resolveUniqueConnectionSlug, upsertIntegrationConnection } from '#db/connections.js';
|
|
4
4
|
import { db } from '#db/db.js';
|
|
5
|
-
import { publishIntegrationEventReceived, recordDeliveryOnly } from '#db/webhook-deliveries.js';
|
|
5
|
+
import { claimWebhookDelivery, publishIntegrationEventReceived, recordDeliveryOnly } from '#db/webhook-deliveries.js';
|
|
6
6
|
import { retryConnectionSlugCollision } from '#providers/connection-slug.js';
|
|
7
7
|
const SLACK_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_slack';
|
|
8
8
|
const SLACK_SECRETS_NAMESPACE_PREFIX = 'system/integrations/slack/';
|
|
9
9
|
async function loadSlackModuleParts(options = {}) {
|
|
10
|
-
const { createSlackE2eRoutes, createSlackIntegrationProvider, createSlackTokenStore, db: slackDb, disconnectSlackInstallation: disconnectSlackInstallationRecords, getSlackInstallationByTeamId, migrationsPath: slackMigrationsPath, upsertSlackInstallation } = await import('@shipfox/api-integration-slack');
|
|
10
|
+
const { createSlackE2eRoutes, createSlackIntegrationProvider, createSlackTokenStore, db: slackDb, deleteSlackInstallationByConnectionId, disconnectSlackInstallation: disconnectSlackInstallationRecords, getSlackInstallationByTeamId, migrationsPath: slackMigrationsPath, slackSecretsNamespace, upsertSlackInstallation } = await import('@shipfox/api-integration-slack');
|
|
11
11
|
async function getExistingSlackConnection(input) {
|
|
12
12
|
const installation = await getSlackInstallationByTeamId(input.teamId);
|
|
13
13
|
if (!installation) return undefined;
|
|
@@ -90,12 +90,27 @@ async function loadSlackModuleParts(options = {}) {
|
|
|
90
90
|
agentTools: {
|
|
91
91
|
tokenStore
|
|
92
92
|
},
|
|
93
|
+
cleanup: {
|
|
94
|
+
deleteConnectionRecords: async (connection, { tx })=>{
|
|
95
|
+
await deleteSlackInstallationByConnectionId(connection.id, {
|
|
96
|
+
tx
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
deleteConnectionSecrets: async (connection)=>{
|
|
100
|
+
// Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
|
|
101
|
+
await (options.secrets?.slack?.deleteSecrets({
|
|
102
|
+
workspaceId: connection.workspaceId,
|
|
103
|
+
namespace: slackNamespaceSuffix(slackSecretsNamespace(connection.id))
|
|
104
|
+
}) ?? Promise.resolve());
|
|
105
|
+
}
|
|
106
|
+
},
|
|
93
107
|
routes: {
|
|
94
108
|
tokenStore,
|
|
95
109
|
getExistingSlackConnection,
|
|
96
110
|
connectSlackInstallation,
|
|
97
111
|
disconnectSlackInstallation,
|
|
98
112
|
coreDb: db,
|
|
113
|
+
claimWebhookDelivery,
|
|
99
114
|
publishIntegrationEventReceived,
|
|
100
115
|
recordDeliveryOnly,
|
|
101
116
|
getIntegrationConnectionById
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/slack.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {\n ConnectSlackInstallationInput,\n SlackSecretsStore,\n} from '@shipfox/api-integration-slack';\nimport {config} from '#config.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst SLACK_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_slack';\nconst SLACK_SECRETS_NAMESPACE_PREFIX = 'system/integrations/slack/';\n\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nasync function loadSlackModuleParts(\n options: Parameters<IntegrationProviderModule['load']>[0] = {},\n): Promise<IntegrationModuleParts> {\n const {\n createSlackE2eRoutes,\n createSlackIntegrationProvider,\n createSlackTokenStore,\n db: slackDb,\n disconnectSlackInstallation: disconnectSlackInstallationRecords,\n getSlackInstallationByTeamId,\n migrationsPath: slackMigrationsPath,\n upsertSlackInstallation,\n } = await import('@shipfox/api-integration-slack');\n\n async function getExistingSlackConnection(input: {\n teamId: string;\n }): Promise<CoreIntegrationConnection<'slack'> | undefined> {\n const installation = await getSlackInstallationByTeamId(input.teamId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'slack'>;\n }\n\n async function connectSlackInstallation(\n input: ConnectSlackInstallationInput,\n ): Promise<CoreIntegrationConnection<'slack'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`slack_${input.teamName || input.teamId}`, {\n fallback: 'slack',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'slack',\n externalAccountId: input.teamId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'slack',\n externalAccountId: input.teamId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n await upsertSlackInstallation(\n {\n connectionId: connection.id,\n teamId: input.teamId,\n teamName: input.teamName,\n appId: input.appId,\n botUserId: input.botUserId,\n scopes: input.scopes,\n tokenExpiresAt: input.tokenExpiresAt,\n status: 'installed',\n },\n {tx},\n );\n return connection as CoreIntegrationConnection<'slack'>;\n }),\n );\n }\n\n async function disconnectSlackInstallation(input: {connectionId: string}): Promise<void> {\n await disconnectSlackInstallationRecords<IntegrationTx>({\n connectionId: input.connectionId,\n getConnection: getIntegrationConnectionById,\n deleteSecrets: (params) =>\n options.secrets?.slack?.deleteSecrets({\n ...params,\n namespace: slackNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, options) =>\n deleteIntegrationConnection({id: params.connectionId}, options),\n });\n }\n\n const fallbackSecrets: SlackSecretsStore = {\n getSecret: () => Promise.resolve(null),\n setSecrets: () => Promise.reject(new Error('Slack token storage is not configured')),\n };\n const secrets: SlackSecretsStore = options.secrets?.slack\n ? {\n getSecret: (params: Parameters<SlackSecretsStore['getSecret']>[0]) =>\n options.secrets?.slack?.getSecret({\n ...params,\n namespace: slackNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(null),\n setSecrets: (params: Parameters<SlackSecretsStore['setSecrets']>[0]) =>\n options.secrets?.slack?.setSecrets({\n ...params,\n namespace: slackNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(),\n }\n : fallbackSecrets;\n const tokenStore = createSlackTokenStore({\n resolveConnection: async (connectionId) => getIntegrationConnectionById(connectionId),\n secrets,\n });\n\n return {\n provider: createSlackIntegrationProvider({\n agentTools: {tokenStore},\n routes: {\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n coreDb: db,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n },\n }),\n e2eRoutes: [\n createSlackE2eRoutes({\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n connectionCapabilities: ['agent_tools'],\n }),\n ],\n database: {\n db: slackDb,\n migrationsPath: slackMigrationsPath,\n migrationsTableName: SLACK_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const slackProviderModule: IntegrationProviderModule = {\n id: 'slack',\n enabled: config.INTEGRATIONS_ENABLE_SLACK_PROVIDER,\n load: loadSlackModuleParts,\n};\n\nfunction slackNamespaceSuffix(namespace: string): string {\n if (!namespace.startsWith(SLACK_SECRETS_NAMESPACE_PREFIX)) {\n throw new Error('Slack provider attempted to access an unscoped secret namespace');\n }\n return namespace.slice(SLACK_SECRETS_NAMESPACE_PREFIX.length);\n}\n"],"names":["slugifyConnectionSlug","config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","SLACK_MIGRATIONS_TABLE","SLACK_SECRETS_NAMESPACE_PREFIX","loadSlackModuleParts","options","createSlackE2eRoutes","createSlackIntegrationProvider","createSlackTokenStore","slackDb","disconnectSlackInstallation","disconnectSlackInstallationRecords","getSlackInstallationByTeamId","migrationsPath","slackMigrationsPath","upsertSlackInstallation","getExistingSlackConnection","input","installation","teamId","undefined","connection","connectionId","connectSlackInstallation","transaction","tx","baseSlug","teamName","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","appId","botUserId","scopes","tokenExpiresAt","status","getConnection","deleteSecrets","params","secrets","slack","namespace","slackNamespaceSuffix","Promise","resolve","fn","deleteConnection","fallbackSecrets","getSecret","setSecrets","reject","Error","tokenStore","resolveConnection","agentTools","routes","coreDb","e2eRoutes","connectionCapabilities","database","migrationsTableName","slackProviderModule","enabled","INTEGRATIONS_ENABLE_SLACK_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/slack.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {\n ConnectSlackInstallationInput,\n SlackSecretsStore,\n} from '@shipfox/api-integration-slack';\nimport {config} from '#config.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {\n claimWebhookDelivery,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst SLACK_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_slack';\nconst SLACK_SECRETS_NAMESPACE_PREFIX = 'system/integrations/slack/';\n\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nasync function loadSlackModuleParts(\n options: Parameters<IntegrationProviderModule['load']>[0] = {},\n): Promise<IntegrationModuleParts> {\n const {\n createSlackE2eRoutes,\n createSlackIntegrationProvider,\n createSlackTokenStore,\n db: slackDb,\n deleteSlackInstallationByConnectionId,\n disconnectSlackInstallation: disconnectSlackInstallationRecords,\n getSlackInstallationByTeamId,\n migrationsPath: slackMigrationsPath,\n slackSecretsNamespace,\n upsertSlackInstallation,\n } = await import('@shipfox/api-integration-slack');\n\n async function getExistingSlackConnection(input: {\n teamId: string;\n }): Promise<CoreIntegrationConnection<'slack'> | undefined> {\n const installation = await getSlackInstallationByTeamId(input.teamId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'slack'>;\n }\n\n async function connectSlackInstallation(\n input: ConnectSlackInstallationInput,\n ): Promise<CoreIntegrationConnection<'slack'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`slack_${input.teamName || input.teamId}`, {\n fallback: 'slack',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'slack',\n externalAccountId: input.teamId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'slack',\n externalAccountId: input.teamId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n await upsertSlackInstallation(\n {\n connectionId: connection.id,\n teamId: input.teamId,\n teamName: input.teamName,\n appId: input.appId,\n botUserId: input.botUserId,\n scopes: input.scopes,\n tokenExpiresAt: input.tokenExpiresAt,\n status: 'installed',\n },\n {tx},\n );\n return connection as CoreIntegrationConnection<'slack'>;\n }),\n );\n }\n\n async function disconnectSlackInstallation(input: {connectionId: string}): Promise<void> {\n await disconnectSlackInstallationRecords<IntegrationTx>({\n connectionId: input.connectionId,\n getConnection: getIntegrationConnectionById,\n deleteSecrets: (params) =>\n options.secrets?.slack?.deleteSecrets({\n ...params,\n namespace: slackNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, options) =>\n deleteIntegrationConnection({id: params.connectionId}, options),\n });\n }\n\n const fallbackSecrets: SlackSecretsStore = {\n getSecret: () => Promise.resolve(null),\n setSecrets: () => Promise.reject(new Error('Slack token storage is not configured')),\n };\n const secrets: SlackSecretsStore = options.secrets?.slack\n ? {\n getSecret: (params: Parameters<SlackSecretsStore['getSecret']>[0]) =>\n options.secrets?.slack?.getSecret({\n ...params,\n namespace: slackNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(null),\n setSecrets: (params: Parameters<SlackSecretsStore['setSecrets']>[0]) =>\n options.secrets?.slack?.setSecrets({\n ...params,\n namespace: slackNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(),\n }\n : fallbackSecrets;\n const tokenStore = createSlackTokenStore({\n resolveConnection: async (connectionId) => getIntegrationConnectionById(connectionId),\n secrets,\n });\n\n return {\n provider: createSlackIntegrationProvider({\n agentTools: {tokenStore},\n cleanup: {\n deleteConnectionRecords: async (connection, {tx}) => {\n await deleteSlackInstallationByConnectionId(connection.id, {tx});\n },\n deleteConnectionSecrets: async (connection) => {\n // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.\n await (options.secrets?.slack?.deleteSecrets({\n workspaceId: connection.workspaceId,\n namespace: slackNamespaceSuffix(slackSecretsNamespace(connection.id)),\n }) ?? Promise.resolve());\n },\n },\n routes: {\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n coreDb: db,\n claimWebhookDelivery,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n },\n }),\n e2eRoutes: [\n createSlackE2eRoutes({\n tokenStore,\n getExistingSlackConnection,\n connectSlackInstallation,\n disconnectSlackInstallation,\n connectionCapabilities: ['agent_tools'],\n }),\n ],\n database: {\n db: slackDb,\n migrationsPath: slackMigrationsPath,\n migrationsTableName: SLACK_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const slackProviderModule: IntegrationProviderModule = {\n id: 'slack',\n enabled: config.INTEGRATIONS_ENABLE_SLACK_PROVIDER,\n load: loadSlackModuleParts,\n};\n\nfunction slackNamespaceSuffix(namespace: string): string {\n if (!namespace.startsWith(SLACK_SECRETS_NAMESPACE_PREFIX)) {\n throw new Error('Slack provider attempted to access an unscoped secret namespace');\n }\n return namespace.slice(SLACK_SECRETS_NAMESPACE_PREFIX.length);\n}\n"],"names":["slugifyConnectionSlug","config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","claimWebhookDelivery","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","SLACK_MIGRATIONS_TABLE","SLACK_SECRETS_NAMESPACE_PREFIX","loadSlackModuleParts","options","createSlackE2eRoutes","createSlackIntegrationProvider","createSlackTokenStore","slackDb","deleteSlackInstallationByConnectionId","disconnectSlackInstallation","disconnectSlackInstallationRecords","getSlackInstallationByTeamId","migrationsPath","slackMigrationsPath","slackSecretsNamespace","upsertSlackInstallation","getExistingSlackConnection","input","installation","teamId","undefined","connection","connectionId","connectSlackInstallation","transaction","tx","baseSlug","teamName","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","appId","botUserId","scopes","tokenExpiresAt","status","getConnection","deleteSecrets","params","secrets","slack","namespace","slackNamespaceSuffix","Promise","resolve","fn","deleteConnection","fallbackSecrets","getSecret","setSecrets","reject","Error","tokenStore","resolveConnection","agentTools","cleanup","deleteConnectionRecords","deleteConnectionSecrets","routes","coreDb","e2eRoutes","connectionCapabilities","database","migrationsTableName","slackProviderModule","enabled","INTEGRATIONS_ENABLE_SLACK_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
|