@shipfox/api-integration-core 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.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +100 -0
  3. package/dist/core/entities/provider.d.ts +7 -2
  4. package/dist/core/entities/provider.d.ts.map +1 -1
  5. package/dist/core/entities/provider.js.map +1 -1
  6. package/dist/core/errors.d.ts +7 -0
  7. package/dist/core/errors.d.ts.map +1 -1
  8. package/dist/core/errors.js +8 -2
  9. package/dist/core/errors.js.map +1 -1
  10. package/dist/db/webhook-deliveries.d.ts +4 -0
  11. package/dist/db/webhook-deliveries.d.ts.map +1 -1
  12. package/dist/db/webhook-deliveries.js +10 -2
  13. package/dist/db/webhook-deliveries.js.map +1 -1
  14. package/dist/index.d.ts +19 -6
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +51 -6
  17. package/dist/index.js.map +1 -1
  18. package/dist/presentation/inter-module.d.ts +9 -0
  19. package/dist/presentation/inter-module.d.ts.map +1 -0
  20. package/dist/presentation/inter-module.js +121 -0
  21. package/dist/presentation/inter-module.js.map +1 -0
  22. package/dist/presentation/routes/agent-tools-gateway/index.d.ts +1 -0
  23. package/dist/presentation/routes/agent-tools-gateway/index.d.ts.map +1 -1
  24. package/dist/presentation/routes/agent-tools-gateway/index.js +1 -0
  25. package/dist/presentation/routes/agent-tools-gateway/index.js.map +1 -1
  26. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts +5 -2
  27. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts.map +1 -1
  28. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js +53 -17
  29. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js.map +1 -1
  30. package/dist/presentation/routes/index.d.ts +2 -3
  31. package/dist/presentation/routes/index.d.ts.map +1 -1
  32. package/dist/presentation/routes/index.js +3 -3
  33. package/dist/presentation/routes/index.js.map +1 -1
  34. package/dist/presentation/routes/manage-connections.d.ts +1 -1
  35. package/dist/presentation/routes/manage-connections.d.ts.map +1 -1
  36. package/dist/presentation/routes/manage-connections.js +28 -3
  37. package/dist/presentation/routes/manage-connections.js.map +1 -1
  38. package/dist/providers/gitea.d.ts.map +1 -1
  39. package/dist/providers/gitea.js +10 -8
  40. package/dist/providers/gitea.js.map +1 -1
  41. package/dist/providers/github.d.ts.map +1 -1
  42. package/dist/providers/github.js +18 -13
  43. package/dist/providers/github.js.map +1 -1
  44. package/dist/providers/jira.d.ts.map +1 -1
  45. package/dist/providers/jira.js +113 -3
  46. package/dist/providers/jira.js.map +1 -1
  47. package/dist/providers/linear.d.ts.map +1 -1
  48. package/dist/providers/linear.js +35 -16
  49. package/dist/providers/linear.js.map +1 -1
  50. package/dist/providers/sentry.d.ts.map +1 -1
  51. package/dist/providers/sentry.js +13 -11
  52. package/dist/providers/sentry.js.map +1 -1
  53. package/dist/providers/slack.d.ts.map +1 -1
  54. package/dist/providers/slack.js +36 -16
  55. package/dist/providers/slack.js.map +1 -1
  56. package/dist/providers/types.d.ts +12 -0
  57. package/dist/providers/types.d.ts.map +1 -1
  58. package/dist/providers/types.js.map +1 -1
  59. package/dist/providers/webhook.d.ts.map +1 -1
  60. package/dist/providers/webhook.js +11 -9
  61. package/dist/providers/webhook.js.map +1 -1
  62. package/dist/tsconfig.test.tsbuildinfo +1 -1
  63. package/package.json +33 -38
  64. package/src/core/entities/provider.ts +7 -1
  65. package/src/core/errors.ts +12 -2
  66. package/src/db/webhook-deliveries.test.ts +11 -0
  67. package/src/db/webhook-deliveries.ts +12 -3
  68. package/src/index.test.ts +112 -0
  69. package/src/index.ts +104 -10
  70. package/src/presentation/inter-module.ts +155 -0
  71. package/src/presentation/routes/agent-tools-gateway/index.ts +1 -0
  72. package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.test.ts +36 -0
  73. package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.ts +64 -22
  74. package/src/presentation/routes/index.ts +5 -6
  75. package/src/presentation/routes/manage-connections.test.ts +113 -0
  76. package/src/presentation/routes/manage-connections.ts +26 -2
  77. package/src/providers/gitea.ts +11 -8
  78. package/src/providers/github.ts +17 -11
  79. package/src/providers/jira.ts +155 -7
  80. package/src/providers/linear.test.ts +136 -0
  81. package/src/providers/linear.ts +33 -13
  82. package/src/providers/sentry.ts +15 -12
  83. package/src/providers/slack.test.ts +139 -0
  84. package/src/providers/slack.ts +39 -14
  85. package/src/providers/types.ts +15 -0
  86. package/src/providers/webhook.ts +11 -9
  87. package/tsconfig.build.tsbuildinfo +1 -1
@@ -50,15 +50,17 @@ async function loadGiteaModuleParts() {
50
50
  return connection;
51
51
  }));
52
52
  }
53
+ const integrationProvider = createGiteaIntegrationProvider({
54
+ getExistingGiteaConnection,
55
+ connectGiteaConnection,
56
+ publishSourcePush,
57
+ recordDeliveryOnly,
58
+ getIntegrationConnectionById,
59
+ coreDb: db
60
+ });
53
61
  return {
54
- provider: createGiteaIntegrationProvider({
55
- getExistingGiteaConnection,
56
- connectGiteaConnection,
57
- publishSourcePush,
58
- recordDeliveryOnly,
59
- getIntegrationConnectionById,
60
- coreDb: db
61
- }),
62
+ provider: integrationProvider,
63
+ webhookProcessors: integrationProvider.webhookProcessors,
62
64
  database: {
63
65
  db: giteaDb,
64
66
  migrationsPath: giteaMigrationsPath,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/gitea.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectGiteaConnectionInput} from '@shipfox/api-integration-gitea';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishSourcePush, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\n// Stable migration-tracking table name for the Gitea provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst GITEA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_gitea';\n\nasync function loadGiteaModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createGiteaIntegrationProvider,\n getGiteaConnectionByOrg,\n upsertGiteaConnection,\n db: giteaDb,\n migrationsPath: giteaMigrationsPath,\n } = await import('@shipfox/api-integration-gitea');\n\n async function getExistingGiteaConnection(input: {\n org: string;\n }): Promise<CoreIntegrationConnection<'gitea'> | undefined> {\n const row = await getGiteaConnectionByOrg(input.org);\n if (!row) return undefined;\n const connection = await getIntegrationConnectionById(row.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'gitea'>;\n }\n\n async function connectGiteaConnection(\n input: ConnectGiteaConnectionInput,\n ): Promise<CoreIntegrationConnection<'gitea'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`gitea_${input.org}`, {fallback: 'gitea'});\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'gitea',\n externalAccountId: input.org,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'gitea',\n externalAccountId: input.org,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertGiteaConnection(\n {\n connectionId: connection.id,\n org: input.org,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'gitea'>;\n }),\n );\n }\n\n return {\n provider: createGiteaIntegrationProvider({\n getExistingGiteaConnection,\n connectGiteaConnection,\n publishSourcePush,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n }),\n database: {\n db: giteaDb,\n migrationsPath: giteaMigrationsPath,\n migrationsTableName: GITEA_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const giteaProviderModule: IntegrationProviderModule = {\n id: 'gitea',\n enabled: config.INTEGRATIONS_ENABLE_GITEA_PROVIDER,\n load: loadGiteaModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishSourcePush","recordDeliveryOnly","retryConnectionSlugCollision","GITEA_MIGRATIONS_TABLE","loadGiteaModuleParts","createGiteaIntegrationProvider","getGiteaConnectionByOrg","upsertGiteaConnection","giteaDb","migrationsPath","giteaMigrationsPath","getExistingGiteaConnection","input","row","org","undefined","connection","connectionId","connectGiteaConnection","transaction","tx","baseSlug","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","coreDb","database","migrationsTableName","giteaProviderModule","enabled","INTEGRATIONS_ENABLE_GITEA_PROVIDER","load"],"mappings":"AAAA,SAEEA,qBAAqB,QAChB,oCAAoC;AAE3C,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,iBAAiB,EAAEC,kBAAkB,QAAO,4BAA4B;AAChF,SAAQC,4BAA4B,QAAO,gCAAgC;AAG3E,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAMC,yBAAyB;AAE/B,eAAeC;IACb,MAAM,EACJC,8BAA8B,EAC9BC,uBAAuB,EACvBC,qBAAqB,EACrBR,IAAIS,OAAO,EACXC,gBAAgBC,mBAAmB,EACpC,GAAG,MAAM,MAAM,CAAC;IAEjB,eAAeC,2BAA2BC,KAEzC;QACC,MAAMC,MAAM,MAAMP,wBAAwBM,MAAME,GAAG;QACnD,IAAI,CAACD,KAAK,OAAOE;QACjB,MAAMC,aAAa,MAAMpB,6BAA6BiB,IAAII,YAAY;QACtE,IAAI,CAACD,YAAY,OAAOD;QACxB,OAAOC;IACT;IAEA,eAAeE,uBACbN,KAAkC;QAElC,OAAO,MAAMV,6BAA6B,IACxCH,KAAKoB,WAAW,CAAC,OAAOC;gBACtB,MAAMC,WAAW3B,sBAAsB,CAAC,MAAM,EAAEkB,MAAME,GAAG,EAAE,EAAE;oBAACQ,UAAU;gBAAO;gBAC/E,MAAMC,OAAO,MAAM1B,4BACjB;oBACE2B,aAAaZ,MAAMY,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBd,MAAME,GAAG;oBAC5BO;gBACF,GACA;oBAACD;gBAAE;gBAEL,MAAMJ,aAAa,MAAMlB,4BACvB;oBACE0B,aAAaZ,MAAMY,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBd,MAAME,GAAG;oBAC5BS;oBACAI,aAAaf,MAAMe,WAAW;oBAC9BC,iBAAiB;gBACnB,GACA;oBAACR;gBAAE;gBAGL,MAAMb,sBACJ;oBACEU,cAAcD,WAAWa,EAAE;oBAC3Bf,KAAKF,MAAME,GAAG;gBAChB,GACA;oBAACM;gBAAE;gBAGL,OAAOJ;YACT;IAEJ;IAEA,OAAO;QACLS,UAAUpB,+BAA+B;YACvCM;YACAO;YACAlB;YACAC;YACAL;YACAkC,QAAQ/B;QACV;QACAgC,UAAU;YACRhC,IAAIS;YACJC,gBAAgBC;YAChBsB,qBAAqB7B;QACvB;IACF;AACF;AAEA,OAAO,MAAM8B,sBAAiD;IAC5DJ,IAAI;IACJK,SAASvC,OAAOwC,kCAAkC;IAClDC,MAAMhC;AACR,EAAE"}
1
+ {"version":3,"sources":["../../src/providers/gitea.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectGiteaConnectionInput} from '@shipfox/api-integration-gitea';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishSourcePush, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\n// Stable migration-tracking table name for the Gitea provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst GITEA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_gitea';\n\nasync function loadGiteaModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createGiteaIntegrationProvider,\n getGiteaConnectionByOrg,\n upsertGiteaConnection,\n db: giteaDb,\n migrationsPath: giteaMigrationsPath,\n } = await import('@shipfox/api-integration-gitea');\n\n async function getExistingGiteaConnection(input: {\n org: string;\n }): Promise<CoreIntegrationConnection<'gitea'> | undefined> {\n const row = await getGiteaConnectionByOrg(input.org);\n if (!row) return undefined;\n const connection = await getIntegrationConnectionById(row.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'gitea'>;\n }\n\n async function connectGiteaConnection(\n input: ConnectGiteaConnectionInput,\n ): Promise<CoreIntegrationConnection<'gitea'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`gitea_${input.org}`, {fallback: 'gitea'});\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'gitea',\n externalAccountId: input.org,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'gitea',\n externalAccountId: input.org,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertGiteaConnection(\n {\n connectionId: connection.id,\n org: input.org,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'gitea'>;\n }),\n );\n }\n\n const integrationProvider = createGiteaIntegrationProvider({\n getExistingGiteaConnection,\n connectGiteaConnection,\n publishSourcePush,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n });\n\n return {\n provider: integrationProvider,\n webhookProcessors: integrationProvider.webhookProcessors,\n database: {\n db: giteaDb,\n migrationsPath: giteaMigrationsPath,\n migrationsTableName: GITEA_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const giteaProviderModule: IntegrationProviderModule = {\n id: 'gitea',\n enabled: config.INTEGRATIONS_ENABLE_GITEA_PROVIDER,\n load: loadGiteaModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishSourcePush","recordDeliveryOnly","retryConnectionSlugCollision","GITEA_MIGRATIONS_TABLE","loadGiteaModuleParts","createGiteaIntegrationProvider","getGiteaConnectionByOrg","upsertGiteaConnection","giteaDb","migrationsPath","giteaMigrationsPath","getExistingGiteaConnection","input","row","org","undefined","connection","connectionId","connectGiteaConnection","transaction","tx","baseSlug","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","integrationProvider","coreDb","webhookProcessors","database","migrationsTableName","giteaProviderModule","enabled","INTEGRATIONS_ENABLE_GITEA_PROVIDER","load"],"mappings":"AAAA,SAEEA,qBAAqB,QAChB,oCAAoC;AAE3C,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,iBAAiB,EAAEC,kBAAkB,QAAO,4BAA4B;AAChF,SAAQC,4BAA4B,QAAO,gCAAgC;AAG3E,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAMC,yBAAyB;AAE/B,eAAeC;IACb,MAAM,EACJC,8BAA8B,EAC9BC,uBAAuB,EACvBC,qBAAqB,EACrBR,IAAIS,OAAO,EACXC,gBAAgBC,mBAAmB,EACpC,GAAG,MAAM,MAAM,CAAC;IAEjB,eAAeC,2BAA2BC,KAEzC;QACC,MAAMC,MAAM,MAAMP,wBAAwBM,MAAME,GAAG;QACnD,IAAI,CAACD,KAAK,OAAOE;QACjB,MAAMC,aAAa,MAAMpB,6BAA6BiB,IAAII,YAAY;QACtE,IAAI,CAACD,YAAY,OAAOD;QACxB,OAAOC;IACT;IAEA,eAAeE,uBACbN,KAAkC;QAElC,OAAO,MAAMV,6BAA6B,IACxCH,KAAKoB,WAAW,CAAC,OAAOC;gBACtB,MAAMC,WAAW3B,sBAAsB,CAAC,MAAM,EAAEkB,MAAME,GAAG,EAAE,EAAE;oBAACQ,UAAU;gBAAO;gBAC/E,MAAMC,OAAO,MAAM1B,4BACjB;oBACE2B,aAAaZ,MAAMY,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBd,MAAME,GAAG;oBAC5BO;gBACF,GACA;oBAACD;gBAAE;gBAEL,MAAMJ,aAAa,MAAMlB,4BACvB;oBACE0B,aAAaZ,MAAMY,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBd,MAAME,GAAG;oBAC5BS;oBACAI,aAAaf,MAAMe,WAAW;oBAC9BC,iBAAiB;gBACnB,GACA;oBAACR;gBAAE;gBAGL,MAAMb,sBACJ;oBACEU,cAAcD,WAAWa,EAAE;oBAC3Bf,KAAKF,MAAME,GAAG;gBAChB,GACA;oBAACM;gBAAE;gBAGL,OAAOJ;YACT;IAEJ;IAEA,MAAMc,sBAAsBzB,+BAA+B;QACzDM;QACAO;QACAlB;QACAC;QACAL;QACAmC,QAAQhC;IACV;IAEA,OAAO;QACL0B,UAAUK;QACVE,mBAAmBF,oBAAoBE,iBAAiB;QACxDC,UAAU;YACRlC,IAAIS;YACJC,gBAAgBC;YAChBwB,qBAAqB/B;QACvB;IACF;AACF;AAEA,OAAO,MAAMgC,sBAAiD;IAC5DN,IAAI;IACJO,SAASzC,OAAO0C,kCAAkC;IAClDC,MAAMlC;AACR,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/providers/github.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,qBAAqB,CAAC;AA2H7B,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/providers/github.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,qBAAqB,CAAC;AAiI7B,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
@@ -69,20 +69,25 @@ async function loadGithubModuleParts(options = {}) {
69
69
  return connection;
70
70
  }));
71
71
  }
72
+ const integrationProvider = createGithubIntegrationProvider({
73
+ getExistingGithubConnection,
74
+ connectGithubInstallation,
75
+ publishIntegrationEventReceived,
76
+ publishSourcePush,
77
+ recordDeliveryOnly,
78
+ getIntegrationConnectionById,
79
+ coreDb: db,
80
+ deleteSecrets: options.secrets?.deleteSecrets,
81
+ agentTools: {
82
+ tokenProvider
83
+ },
84
+ ...options.requireActiveWorkspaceMembership ? {
85
+ requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
86
+ } : {}
87
+ });
72
88
  return {
73
- provider: createGithubIntegrationProvider({
74
- getExistingGithubConnection,
75
- connectGithubInstallation,
76
- publishIntegrationEventReceived,
77
- publishSourcePush,
78
- recordDeliveryOnly,
79
- getIntegrationConnectionById,
80
- coreDb: db,
81
- deleteSecrets: options.secrets?.deleteSecrets,
82
- agentTools: {
83
- tokenProvider
84
- }
85
- }),
89
+ provider: integrationProvider,
90
+ webhookProcessors: integrationProvider.webhookProcessors,
86
91
  e2eRoutes: [
87
92
  createGithubE2eRoutes({
88
93
  getExistingGithubConnection,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/github.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectGithubInstallationInput} from '@shipfox/api-integration-github';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {\n publishIntegrationEventReceived,\n publishSourcePush,\n recordDeliveryOnly,\n} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {\n IntegrationModuleParts,\n IntegrationProviderModule,\n IntegrationProviderModuleLoadOptions,\n} from '#providers/types.js';\n\n// Stable migration-tracking table name for the GitHub provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst GITHUB_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_github';\n\nasync function loadGithubModuleParts(\n options: IntegrationProviderModuleLoadOptions = {},\n): Promise<IntegrationModuleParts> {\n const {\n createGithubInstallationTokenProvider,\n createGithubE2eRoutes,\n encodeInstallationTokenEnvelope,\n createGithubIntegrationProvider,\n getGithubInstallationByInstallationId,\n githubInstallationTokenNamespace,\n db: githubDb,\n migrationsPath: githubMigrationsPath,\n upsertGithubInstallation,\n } = await import('@shipfox/api-integration-github');\n\n const tokenProvider = createGithubInstallationTokenProvider({\n getIntegrationConnectionById,\n secretStore: options.secrets?.github\n ? {\n read: async (workspaceId, installationId) =>\n (await options.secrets?.github?.getSecret({\n workspaceId,\n namespace: githubInstallationTokenNamespace(installationId),\n key: 'envelope',\n })) ?? null,\n write: async (workspaceId, installationId, envelope) => {\n await options.secrets?.github?.setSecrets({\n workspaceId,\n namespace: githubInstallationTokenNamespace(installationId),\n values: {envelope: encodeInstallationTokenEnvelope(envelope)},\n });\n },\n }\n : undefined,\n });\n\n async function getExistingGithubConnection(input: {\n installationId: string;\n }): Promise<CoreIntegrationConnection<'github'> | undefined> {\n const installation = await getGithubInstallationByInstallationId(input.installationId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'github'>;\n }\n\n async function connectGithubInstallation(\n input: ConnectGithubInstallationInput,\n ): Promise<CoreIntegrationConnection<'github'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`github_${input.installation.accountLogin}`, {\n fallback: 'github',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'github',\n externalAccountId: input.installationId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'github',\n externalAccountId: input.installationId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertGithubInstallation(\n {\n connectionId: connection.id,\n ...input.installation,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'github'>;\n }),\n );\n }\n\n return {\n provider: createGithubIntegrationProvider({\n getExistingGithubConnection,\n connectGithubInstallation,\n publishIntegrationEventReceived,\n publishSourcePush,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n deleteSecrets: options.secrets?.deleteSecrets,\n agentTools: {tokenProvider},\n }),\n e2eRoutes: [\n createGithubE2eRoutes({\n getExistingGithubConnection,\n connectGithubInstallation,\n connectionCapabilities: ['source_control', 'agent_tools'],\n }),\n ],\n database: {\n db: githubDb,\n migrationsPath: githubMigrationsPath,\n migrationsTableName: GITHUB_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const githubProviderModule: IntegrationProviderModule = {\n id: 'github',\n enabled: config.INTEGRATIONS_ENABLE_GITHUB_PROVIDER,\n load: loadGithubModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishIntegrationEventReceived","publishSourcePush","recordDeliveryOnly","retryConnectionSlugCollision","GITHUB_MIGRATIONS_TABLE","loadGithubModuleParts","options","createGithubInstallationTokenProvider","createGithubE2eRoutes","encodeInstallationTokenEnvelope","createGithubIntegrationProvider","getGithubInstallationByInstallationId","githubInstallationTokenNamespace","githubDb","migrationsPath","githubMigrationsPath","upsertGithubInstallation","tokenProvider","secretStore","secrets","github","read","workspaceId","installationId","getSecret","namespace","key","write","envelope","setSecrets","values","undefined","getExistingGithubConnection","input","installation","connection","connectionId","connectGithubInstallation","transaction","tx","baseSlug","accountLogin","fallback","slug","provider","externalAccountId","displayName","lifecycleStatus","id","coreDb","deleteSecrets","agentTools","e2eRoutes","connectionCapabilities","database","migrationsTableName","githubProviderModule","enabled","INTEGRATIONS_ENABLE_GITHUB_PROVIDER","load"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/providers/github.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectGithubInstallationInput} from '@shipfox/api-integration-github';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {\n publishIntegrationEventReceived,\n publishSourcePush,\n recordDeliveryOnly,\n} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {\n IntegrationModuleParts,\n IntegrationProviderModule,\n IntegrationProviderModuleLoadOptions,\n} from '#providers/types.js';\n\n// Stable migration-tracking table name for the GitHub provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst GITHUB_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_github';\n\nasync function loadGithubModuleParts(\n options: IntegrationProviderModuleLoadOptions = {},\n): Promise<IntegrationModuleParts> {\n const {\n createGithubInstallationTokenProvider,\n createGithubE2eRoutes,\n encodeInstallationTokenEnvelope,\n createGithubIntegrationProvider,\n getGithubInstallationByInstallationId,\n githubInstallationTokenNamespace,\n db: githubDb,\n migrationsPath: githubMigrationsPath,\n upsertGithubInstallation,\n } = await import('@shipfox/api-integration-github');\n\n const tokenProvider = createGithubInstallationTokenProvider({\n getIntegrationConnectionById,\n secretStore: options.secrets?.github\n ? {\n read: async (workspaceId, installationId) =>\n (await options.secrets?.github?.getSecret({\n workspaceId,\n namespace: githubInstallationTokenNamespace(installationId),\n key: 'envelope',\n })) ?? null,\n write: async (workspaceId, installationId, envelope) => {\n await options.secrets?.github?.setSecrets({\n workspaceId,\n namespace: githubInstallationTokenNamespace(installationId),\n values: {envelope: encodeInstallationTokenEnvelope(envelope)},\n });\n },\n }\n : undefined,\n });\n\n async function getExistingGithubConnection(input: {\n installationId: string;\n }): Promise<CoreIntegrationConnection<'github'> | undefined> {\n const installation = await getGithubInstallationByInstallationId(input.installationId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'github'>;\n }\n\n async function connectGithubInstallation(\n input: ConnectGithubInstallationInput,\n ): Promise<CoreIntegrationConnection<'github'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`github_${input.installation.accountLogin}`, {\n fallback: 'github',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'github',\n externalAccountId: input.installationId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'github',\n externalAccountId: input.installationId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertGithubInstallation(\n {\n connectionId: connection.id,\n ...input.installation,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'github'>;\n }),\n );\n }\n\n const integrationProvider = createGithubIntegrationProvider({\n getExistingGithubConnection,\n connectGithubInstallation,\n publishIntegrationEventReceived,\n publishSourcePush,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n deleteSecrets: options.secrets?.deleteSecrets,\n agentTools: {tokenProvider},\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n });\n\n return {\n provider: integrationProvider,\n webhookProcessors: integrationProvider.webhookProcessors,\n e2eRoutes: [\n createGithubE2eRoutes({\n getExistingGithubConnection,\n connectGithubInstallation,\n connectionCapabilities: ['source_control', 'agent_tools'],\n }),\n ],\n database: {\n db: githubDb,\n migrationsPath: githubMigrationsPath,\n migrationsTableName: GITHUB_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const githubProviderModule: IntegrationProviderModule = {\n id: 'github',\n enabled: config.INTEGRATIONS_ENABLE_GITHUB_PROVIDER,\n load: loadGithubModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishIntegrationEventReceived","publishSourcePush","recordDeliveryOnly","retryConnectionSlugCollision","GITHUB_MIGRATIONS_TABLE","loadGithubModuleParts","options","createGithubInstallationTokenProvider","createGithubE2eRoutes","encodeInstallationTokenEnvelope","createGithubIntegrationProvider","getGithubInstallationByInstallationId","githubInstallationTokenNamespace","githubDb","migrationsPath","githubMigrationsPath","upsertGithubInstallation","tokenProvider","secretStore","secrets","github","read","workspaceId","installationId","getSecret","namespace","key","write","envelope","setSecrets","values","undefined","getExistingGithubConnection","input","installation","connection","connectionId","connectGithubInstallation","transaction","tx","baseSlug","accountLogin","fallback","slug","provider","externalAccountId","displayName","lifecycleStatus","id","integrationProvider","coreDb","deleteSecrets","agentTools","requireActiveWorkspaceMembership","webhookProcessors","e2eRoutes","connectionCapabilities","database","migrationsTableName","githubProviderModule","enabled","INTEGRATIONS_ENABLE_GITHUB_PROVIDER","load"],"mappings":"AAAA,SAEEA,qBAAqB,QAChB,oCAAoC;AAE3C,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,EAAE,QAAO,YAAY;AAC7B,SACEC,+BAA+B,EAC/BC,iBAAiB,EACjBC,kBAAkB,QACb,4BAA4B;AACnC,SAAQC,4BAA4B,QAAO,gCAAgC;AAO3E,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAMC,0BAA0B;AAEhC,eAAeC,sBACbC,UAAgD,CAAC,CAAC;IAElD,MAAM,EACJC,qCAAqC,EACrCC,qBAAqB,EACrBC,+BAA+B,EAC/BC,+BAA+B,EAC/BC,qCAAqC,EACrCC,gCAAgC,EAChCb,IAAIc,QAAQ,EACZC,gBAAgBC,oBAAoB,EACpCC,wBAAwB,EACzB,GAAG,MAAM,MAAM,CAAC;IAEjB,MAAMC,gBAAgBV,sCAAsC;QAC1DX;QACAsB,aAAaZ,QAAQa,OAAO,EAAEC,SAC1B;YACEC,MAAM,OAAOC,aAAaC,iBACxB,AAAC,MAAMjB,QAAQa,OAAO,EAAEC,QAAQI,UAAU;oBACxCF;oBACAG,WAAWb,iCAAiCW;oBAC5CG,KAAK;gBACP,MAAO;YACTC,OAAO,OAAOL,aAAaC,gBAAgBK;gBACzC,MAAMtB,QAAQa,OAAO,EAAEC,QAAQS,WAAW;oBACxCP;oBACAG,WAAWb,iCAAiCW;oBAC5CO,QAAQ;wBAACF,UAAUnB,gCAAgCmB;oBAAS;gBAC9D;YACF;QACF,IACAG;IACN;IAEA,eAAeC,4BAA4BC,KAE1C;QACC,MAAMC,eAAe,MAAMvB,sCAAsCsB,MAAMV,cAAc;QACrF,IAAI,CAACW,cAAc,OAAOH;QAC1B,MAAMI,aAAa,MAAMvC,6BAA6BsC,aAAaE,YAAY;QAC/E,IAAI,CAACD,YAAY,OAAOJ;QACxB,OAAOI;IACT;IAEA,eAAeE,0BACbJ,KAAqC;QAErC,OAAO,MAAM9B,6BAA6B,IACxCJ,KAAKuC,WAAW,CAAC,OAAOC;gBACtB,MAAMC,WAAW9C,sBAAsB,CAAC,OAAO,EAAEuC,MAAMC,YAAY,CAACO,YAAY,EAAE,EAAE;oBAClFC,UAAU;gBACZ;gBACA,MAAMC,OAAO,MAAM9C,4BACjB;oBACEyB,aAAaW,MAAMX,WAAW;oBAC9BsB,UAAU;oBACVC,mBAAmBZ,MAAMV,cAAc;oBACvCiB;gBACF,GACA;oBAACD;gBAAE;gBAEL,MAAMJ,aAAa,MAAMrC,4BACvB;oBACEwB,aAAaW,MAAMX,WAAW;oBAC9BsB,UAAU;oBACVC,mBAAmBZ,MAAMV,cAAc;oBACvCoB;oBACAG,aAAab,MAAMa,WAAW;oBAC9BC,iBAAiB;gBACnB,GACA;oBAACR;gBAAE;gBAGL,MAAMvB,yBACJ;oBACEoB,cAAcD,WAAWa,EAAE;oBAC3B,GAAGf,MAAMC,YAAY;gBACvB,GACA;oBAACK;gBAAE;gBAGL,OAAOJ;YACT;IAEJ;IAEA,MAAMc,sBAAsBvC,gCAAgC;QAC1DsB;QACAK;QACArC;QACAC;QACAC;QACAN;QACAsD,QAAQnD;QACRoD,eAAe7C,QAAQa,OAAO,EAAEgC;QAChCC,YAAY;YAACnC;QAAa;QAC1B,GAAIX,QAAQ+C,gCAAgC,GACxC;YAACA,kCAAkC/C,QAAQ+C,gCAAgC;QAAA,IAC3E,CAAC,CAAC;IACR;IAEA,OAAO;QACLT,UAAUK;QACVK,mBAAmBL,oBAAoBK,iBAAiB;QACxDC,WAAW;YACT/C,sBAAsB;gBACpBwB;gBACAK;gBACAmB,wBAAwB;oBAAC;oBAAkB;iBAAc;YAC3D;SACD;QACDC,UAAU;YACR1D,IAAIc;YACJC,gBAAgBC;YAChB2C,qBAAqBtD;QACvB;IACF;AACF;AAEA,OAAO,MAAMuD,uBAAkD;IAC7DX,IAAI;IACJY,SAASjE,OAAOkE,mCAAmC;IACnDC,MAAMzD;AACR,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/providers/jira.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAqB3F,eAAO,MAAM,kBAAkB,EAAE,yBAIhC,CAAC"}
1
+ {"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/providers/jira.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAgJ3F,eAAO,MAAM,kBAAkB,EAAE,yBAIhC,CAAC"}
@@ -1,9 +1,113 @@
1
+ import { slugifyConnectionSlug } from '@shipfox/api-integration-core-dto';
1
2
  import { config } from '#config.js';
3
+ import { deleteIntegrationConnection, getIntegrationConnectionById, resolveUniqueConnectionSlug, updateIntegrationConnectionLifecycleStatus, upsertIntegrationConnection } from '#db/connections.js';
4
+ import { db } from '#db/db.js';
5
+ import { retryConnectionSlugCollision } from '#providers/connection-slug.js';
2
6
  const JIRA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_jira';
3
- async function loadJiraModuleParts() {
4
- const { createJiraIntegrationProvider, db: jiraDb, migrationsPath } = await import('@shipfox/api-integration-jira');
7
+ const JIRA_SECRETS_NAMESPACE_PREFIX = 'system/integrations/jira/';
8
+ async function loadJiraModuleParts(options = {}) {
9
+ const { createJiraIntegrationProvider, createJiraPendingSelectionStore, createJiraTokenStore, db: jiraDb, disconnectJiraInstallation: disconnectJiraInstallationRecords, getJiraInstallationByCloudId, migrationsPath, upsertJiraInstallation } = await import('@shipfox/api-integration-jira');
10
+ async function getExistingJiraConnection(input) {
11
+ const installation = await getJiraInstallationByCloudId(input.cloudId);
12
+ if (!installation) return undefined;
13
+ return await getIntegrationConnectionById(installation.connectionId);
14
+ }
15
+ function connectJiraInstallation(input) {
16
+ return retryConnectionSlugCollision(()=>db().transaction(async (tx)=>{
17
+ const slug = await resolveUniqueConnectionSlug({
18
+ workspaceId: input.workspaceId,
19
+ provider: 'jira',
20
+ externalAccountId: input.cloudId,
21
+ baseSlug: slugifyConnectionSlug(`jira_${input.siteName || input.cloudId}`, {
22
+ fallback: 'jira'
23
+ })
24
+ }, {
25
+ tx
26
+ });
27
+ const connection = await upsertIntegrationConnection({
28
+ workspaceId: input.workspaceId,
29
+ provider: 'jira',
30
+ externalAccountId: input.cloudId,
31
+ slug,
32
+ displayName: input.displayName,
33
+ lifecycleStatus: 'active'
34
+ }, {
35
+ tx
36
+ });
37
+ await upsertJiraInstallation({
38
+ connectionId: connection.id,
39
+ cloudId: input.cloudId,
40
+ siteUrl: input.siteUrl,
41
+ siteName: input.siteName,
42
+ authorizingAccountId: input.authorizingAccountId,
43
+ scopes: input.scopes,
44
+ status: 'installed',
45
+ tokenExpiresAt: input.tokenExpiresAt
46
+ }, {
47
+ tx
48
+ });
49
+ return connection;
50
+ }));
51
+ }
52
+ async function disconnectJiraInstallation(input) {
53
+ await disconnectJiraInstallationRecords({
54
+ connectionId: input.connectionId,
55
+ getConnection: getIntegrationConnectionById,
56
+ deleteSecrets: (params)=>options.secrets?.jira?.deleteSecrets({
57
+ ...params,
58
+ namespace: jiraNamespaceSuffix(params.namespace)
59
+ }) ?? Promise.resolve(0),
60
+ transaction: (fn)=>db().transaction((tx)=>fn(tx)),
61
+ deleteConnection: (params, transactionOptions)=>deleteIntegrationConnection({
62
+ id: params.connectionId
63
+ }, transactionOptions)
64
+ });
65
+ }
66
+ const fallbackSecrets = {
67
+ getSecret: ()=>Promise.resolve(null),
68
+ setSecrets: ()=>Promise.reject(new Error('Jira token storage is not configured')),
69
+ deleteSecrets: ()=>Promise.resolve(0)
70
+ };
71
+ const secrets = options.secrets?.jira ? {
72
+ getSecret: (params)=>options.secrets?.jira?.getSecret({
73
+ ...params,
74
+ namespace: jiraNamespaceSuffix(params.namespace)
75
+ }) ?? Promise.resolve(null),
76
+ setSecrets: (params)=>options.secrets?.jira?.setSecrets({
77
+ ...params,
78
+ namespace: jiraNamespaceSuffix(params.namespace)
79
+ }) ?? Promise.resolve(),
80
+ deleteSecrets: (params)=>options.secrets?.jira?.deleteSecrets({
81
+ ...params,
82
+ namespace: jiraNamespaceSuffix(params.namespace)
83
+ }) ?? Promise.resolve(0)
84
+ } : fallbackSecrets;
85
+ const tokenStore = createJiraTokenStore({
86
+ resolveConnection: getIntegrationConnectionById,
87
+ secrets,
88
+ markConnectionError: async ({ connectionId })=>{
89
+ await updateIntegrationConnectionLifecycleStatus({
90
+ id: connectionId,
91
+ lifecycleStatus: 'error'
92
+ });
93
+ }
94
+ });
95
+ const pendingStore = createJiraPendingSelectionStore({
96
+ secrets
97
+ });
5
98
  return {
6
- provider: createJiraIntegrationProvider(),
99
+ provider: createJiraIntegrationProvider({
100
+ routes: {
101
+ tokenStore,
102
+ pendingStore,
103
+ getExistingJiraConnection,
104
+ connectJiraInstallation,
105
+ disconnectJiraInstallation,
106
+ ...options.requireActiveWorkspaceMembership ? {
107
+ requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
108
+ } : {}
109
+ }
110
+ }),
7
111
  database: {
8
112
  db: jiraDb,
9
113
  migrationsPath,
@@ -16,5 +120,11 @@ export const jiraProviderModule = {
16
120
  enabled: config.INTEGRATIONS_ENABLE_JIRA_PROVIDER,
17
121
  load: loadJiraModuleParts
18
122
  };
123
+ function jiraNamespaceSuffix(namespace) {
124
+ if (!namespace.startsWith(JIRA_SECRETS_NAMESPACE_PREFIX)) {
125
+ throw new Error('Jira provider attempted to access an unscoped secret namespace');
126
+ }
127
+ return namespace.slice(JIRA_SECRETS_NAMESPACE_PREFIX.length);
128
+ }
19
129
 
20
130
  //# sourceMappingURL=jira.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/jira.ts"],"sourcesContent":["import {config} from '#config.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst JIRA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_jira';\n\nasync function loadJiraModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createJiraIntegrationProvider,\n db: jiraDb,\n migrationsPath,\n } = await import('@shipfox/api-integration-jira');\n\n return {\n provider: createJiraIntegrationProvider(),\n database: {\n db: jiraDb,\n migrationsPath,\n migrationsTableName: JIRA_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const jiraProviderModule: IntegrationProviderModule = {\n id: 'jira',\n enabled: config.INTEGRATIONS_ENABLE_JIRA_PROVIDER,\n load: loadJiraModuleParts,\n};\n"],"names":["config","JIRA_MIGRATIONS_TABLE","loadJiraModuleParts","createJiraIntegrationProvider","db","jiraDb","migrationsPath","provider","database","migrationsTableName","jiraProviderModule","id","enabled","INTEGRATIONS_ENABLE_JIRA_PROVIDER","load"],"mappings":"AAAA,SAAQA,MAAM,QAAO,aAAa;AAGlC,MAAMC,wBAAwB;AAE9B,eAAeC;IACb,MAAM,EACJC,6BAA6B,EAC7BC,IAAIC,MAAM,EACVC,cAAc,EACf,GAAG,MAAM,MAAM,CAAC;IAEjB,OAAO;QACLC,UAAUJ;QACVK,UAAU;YACRJ,IAAIC;YACJC;YACAG,qBAAqBR;QACvB;IACF;AACF;AAEA,OAAO,MAAMS,qBAAgD;IAC3DC,IAAI;IACJC,SAASZ,OAAOa,iCAAiC;IACjDC,MAAMZ;AACR,EAAE"}
1
+ {"version":3,"sources":["../../src/providers/jira.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {\n ConnectJiraInstallationInput,\n JiraPendingSelectionSecretsStore,\n JiraSecretsStore,\n} from '@shipfox/api-integration-jira';\nimport {config} from '#config.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n updateIntegrationConnectionLifecycleStatus,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst JIRA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_jira';\nconst JIRA_SECRETS_NAMESPACE_PREFIX = 'system/integrations/jira/';\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nasync function loadJiraModuleParts(\n options: Parameters<IntegrationProviderModule['load']>[0] = {},\n): Promise<IntegrationModuleParts> {\n const {\n createJiraIntegrationProvider,\n createJiraPendingSelectionStore,\n createJiraTokenStore,\n db: jiraDb,\n disconnectJiraInstallation: disconnectJiraInstallationRecords,\n getJiraInstallationByCloudId,\n migrationsPath,\n upsertJiraInstallation,\n } = await import('@shipfox/api-integration-jira');\n\n async function getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<CoreIntegrationConnection<'jira'> | undefined> {\n const installation = await getJiraInstallationByCloudId(input.cloudId);\n if (!installation) return undefined;\n return (await getIntegrationConnectionById(installation.connectionId)) as\n | CoreIntegrationConnection<'jira'>\n | undefined;\n }\n\n function connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<CoreIntegrationConnection<'jira'>> {\n return retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'jira',\n externalAccountId: input.cloudId,\n baseSlug: slugifyConnectionSlug(`jira_${input.siteName || input.cloudId}`, {\n fallback: 'jira',\n }),\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'jira',\n externalAccountId: input.cloudId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n await upsertJiraInstallation(\n {\n connectionId: connection.id,\n cloudId: input.cloudId,\n siteUrl: input.siteUrl,\n siteName: input.siteName,\n authorizingAccountId: input.authorizingAccountId,\n scopes: input.scopes,\n status: 'installed',\n tokenExpiresAt: input.tokenExpiresAt,\n },\n {tx},\n );\n return connection as CoreIntegrationConnection<'jira'>;\n }),\n );\n }\n\n async function disconnectJiraInstallation(input: {connectionId: string}): Promise<void> {\n await disconnectJiraInstallationRecords<IntegrationTx>({\n connectionId: input.connectionId,\n getConnection: getIntegrationConnectionById,\n deleteSecrets: (params) =>\n options.secrets?.jira?.deleteSecrets({\n ...params,\n namespace: jiraNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, transactionOptions) =>\n deleteIntegrationConnection({id: params.connectionId}, transactionOptions),\n });\n }\n\n const fallbackSecrets: JiraSecretsStore & JiraPendingSelectionSecretsStore = {\n getSecret: () => Promise.resolve(null),\n setSecrets: () => Promise.reject(new Error('Jira token storage is not configured')),\n deleteSecrets: () => Promise.resolve(0),\n };\n const secrets: JiraSecretsStore & JiraPendingSelectionSecretsStore = options.secrets?.jira\n ? {\n getSecret: (params) =>\n options.secrets?.jira?.getSecret({\n ...params,\n namespace: jiraNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(null),\n setSecrets: (params) =>\n options.secrets?.jira?.setSecrets({\n ...params,\n namespace: jiraNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(),\n deleteSecrets: (params) =>\n options.secrets?.jira?.deleteSecrets({\n ...params,\n namespace: jiraNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n }\n : fallbackSecrets;\n const tokenStore = createJiraTokenStore({\n resolveConnection: getIntegrationConnectionById,\n secrets,\n markConnectionError: async ({connectionId}) => {\n await updateIntegrationConnectionLifecycleStatus({\n id: connectionId,\n lifecycleStatus: 'error',\n });\n },\n });\n const pendingStore = createJiraPendingSelectionStore({secrets});\n\n return {\n provider: createJiraIntegrationProvider({\n routes: {\n tokenStore,\n pendingStore,\n getExistingJiraConnection,\n connectJiraInstallation,\n disconnectJiraInstallation,\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n },\n }),\n database: {db: jiraDb, migrationsPath, migrationsTableName: JIRA_MIGRATIONS_TABLE},\n };\n}\n\nexport const jiraProviderModule: IntegrationProviderModule = {\n id: 'jira',\n enabled: config.INTEGRATIONS_ENABLE_JIRA_PROVIDER,\n load: loadJiraModuleParts,\n};\n\nfunction jiraNamespaceSuffix(namespace: string): string {\n if (!namespace.startsWith(JIRA_SECRETS_NAMESPACE_PREFIX)) {\n throw new Error('Jira provider attempted to access an unscoped secret namespace');\n }\n return namespace.slice(JIRA_SECRETS_NAMESPACE_PREFIX.length);\n}\n"],"names":["slugifyConnectionSlug","config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","retryConnectionSlugCollision","JIRA_MIGRATIONS_TABLE","JIRA_SECRETS_NAMESPACE_PREFIX","loadJiraModuleParts","options","createJiraIntegrationProvider","createJiraPendingSelectionStore","createJiraTokenStore","jiraDb","disconnectJiraInstallation","disconnectJiraInstallationRecords","getJiraInstallationByCloudId","migrationsPath","upsertJiraInstallation","getExistingJiraConnection","input","installation","cloudId","undefined","connectionId","connectJiraInstallation","transaction","tx","slug","workspaceId","provider","externalAccountId","baseSlug","siteName","fallback","connection","displayName","lifecycleStatus","id","siteUrl","authorizingAccountId","scopes","status","tokenExpiresAt","getConnection","deleteSecrets","params","secrets","jira","namespace","jiraNamespaceSuffix","Promise","resolve","fn","deleteConnection","transactionOptions","fallbackSecrets","getSecret","setSecrets","reject","Error","tokenStore","resolveConnection","markConnectionError","pendingStore","routes","requireActiveWorkspaceMembership","database","migrationsTableName","jiraProviderModule","enabled","INTEGRATIONS_ENABLE_JIRA_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/providers/linear.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAqJ3F,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
1
+ {"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/providers/linear.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAyK3F,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
@@ -7,7 +7,7 @@ import { retryConnectionSlugCollision } from '#providers/connection-slug.js';
7
7
  const LINEAR_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_linear';
8
8
  const LINEAR_SECRETS_NAMESPACE_PREFIX = 'system/integrations/linear/';
9
9
  async function loadLinearModuleParts(options = {}) {
10
- const { createLinearTokenStore, createLinearE2eRoutes, createLinearIntegrationProvider, config: linearConfig, disconnectLinearInstallation: disconnectLinearInstallationRecords, getLinearInstallationByOrganizationId, db: linearDb, migrationsPath: linearMigrationsPath, upsertLinearInstallation } = await import('@shipfox/api-integration-linear');
10
+ const { createLinearTokenStore, createLinearE2eRoutes, createLinearIntegrationProvider, config: linearConfig, deleteLinearInstallationByConnectionId, disconnectLinearInstallation: disconnectLinearInstallationRecords, getLinearInstallationByOrganizationId, db: linearDb, linearSecretsNamespace, migrationsPath: linearMigrationsPath, upsertLinearInstallation } = await import('@shipfox/api-integration-linear');
11
11
  async function getExistingLinearConnection(input) {
12
12
  const installation = await getLinearInstallationByOrganizationId(input.organizationId);
13
13
  if (!installation) return undefined;
@@ -84,23 +84,42 @@ async function loadLinearModuleParts(options = {}) {
84
84
  resolveConnection: async (connectionId)=>getIntegrationConnectionById(connectionId),
85
85
  secrets
86
86
  });
87
- return {
88
- provider: createLinearIntegrationProvider({
89
- agentTools: {
90
- tokenStore,
91
- endpoint: linearConfig.LINEAR_MCP_ENDPOINT
87
+ const integrationProvider = createLinearIntegrationProvider({
88
+ agentTools: {
89
+ tokenStore,
90
+ endpoint: linearConfig.LINEAR_MCP_ENDPOINT
91
+ },
92
+ cleanup: {
93
+ deleteConnectionRecords: async (connection, { tx })=>{
94
+ await deleteLinearInstallationByConnectionId(connection.id, {
95
+ tx
96
+ });
92
97
  },
93
- routes: {
94
- tokenStore,
95
- getExistingLinearConnection,
96
- connectLinearInstallation,
97
- disconnectLinearInstallation,
98
- publishIntegrationEventReceived,
99
- recordDeliveryOnly,
100
- getIntegrationConnectionById,
101
- coreDb: db
98
+ deleteConnectionSecrets: async (connection)=>{
99
+ // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
100
+ await (options.secrets?.linear?.deleteSecrets({
101
+ workspaceId: connection.workspaceId,
102
+ namespace: linearNamespaceSuffix(linearSecretsNamespace(connection.id))
103
+ }) ?? Promise.resolve());
102
104
  }
103
- }),
105
+ },
106
+ routes: {
107
+ tokenStore,
108
+ getExistingLinearConnection,
109
+ connectLinearInstallation,
110
+ disconnectLinearInstallation,
111
+ publishIntegrationEventReceived,
112
+ recordDeliveryOnly,
113
+ getIntegrationConnectionById,
114
+ coreDb: db,
115
+ ...options.requireActiveWorkspaceMembership ? {
116
+ requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
117
+ } : {}
118
+ }
119
+ });
120
+ return {
121
+ provider: integrationProvider,
122
+ webhookProcessors: integrationProvider.webhookProcessors,
104
123
  e2eRoutes: [
105
124
  createLinearE2eRoutes({
106
125
  tokenStore,
@@ -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 const integrationProvider = 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 ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n },\n });\n\n return {\n provider: integrationProvider,\n webhookProcessors: integrationProvider.webhookProcessors,\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","integrationProvider","agentTools","endpoint","LINEAR_MCP_ENDPOINT","cleanup","deleteConnectionRecords","deleteConnectionSecrets","routes","coreDb","requireActiveWorkspaceMembership","webhookProcessors","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":"sentry.d.ts","sourceRoot":"","sources":["../../src/providers/sentry.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AA6F3F,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
1
+ {"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/providers/sentry.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAgG3F,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
@@ -52,18 +52,20 @@ async function loadSentryModuleParts() {
52
52
  return connection;
53
53
  }));
54
54
  }
55
+ const integrationProvider = createSentryIntegrationProvider({
56
+ getSentryInstallation: ({ installationUuid })=>getSentryInstallationByInstallationUuid(installationUuid),
57
+ getConnectionById,
58
+ connectSentryInstallation,
59
+ persistVerifiedUnclaimedInstallation,
60
+ coreDb: db,
61
+ publishIntegrationEventReceived,
62
+ recordDeliveryOnly,
63
+ getIntegrationConnectionById,
64
+ updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus
65
+ });
55
66
  return {
56
- provider: createSentryIntegrationProvider({
57
- getSentryInstallation: ({ installationUuid })=>getSentryInstallationByInstallationUuid(installationUuid),
58
- getConnectionById,
59
- connectSentryInstallation,
60
- persistVerifiedUnclaimedInstallation,
61
- coreDb: db,
62
- publishIntegrationEventReceived,
63
- recordDeliveryOnly,
64
- getIntegrationConnectionById,
65
- updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus
66
- }),
67
+ provider: integrationProvider,
68
+ webhookProcessors: integrationProvider.webhookProcessors,
67
69
  database: {
68
70
  db: sentryDb,
69
71
  migrationsPath: sentryMigrationsPath,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/sentry.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectSentryInstallationInput} from '@shipfox/api-integration-sentry';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n updateIntegrationConnectionLifecycleStatus,\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\n// Stable migration-tracking table name for the Sentry provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst SENTRY_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_sentry';\n\nasync function loadSentryModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createSentryIntegrationProvider,\n createSentryMaintenanceWorker,\n getSentryInstallationByInstallationUuid,\n persistVerifiedUnclaimedInstallation,\n upsertSentryInstallation,\n db: sentryDb,\n migrationsPath: sentryMigrationsPath,\n } = await import('@shipfox/api-integration-sentry');\n\n async function getConnectionById(\n id: string,\n ): Promise<CoreIntegrationConnection<'sentry'> | undefined> {\n const connection = await getIntegrationConnectionById(id);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'sentry'>;\n }\n\n async function connectSentryInstallation(\n input: ConnectSentryInstallationInput,\n ): Promise<CoreIntegrationConnection<'sentry'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`sentry_${input.orgSlug}`, {fallback: 'sentry'});\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'sentry',\n externalAccountId: input.installationUuid,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'sentry',\n externalAccountId: input.installationUuid,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertSentryInstallation(\n {\n connectionId: connection.id,\n installationUuid: input.installationUuid,\n orgSlug: input.orgSlug,\n status: 'installed',\n codeHash: input.codeHash,\n installerUserId: input.installerUserId,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'sentry'>;\n }),\n );\n }\n\n return {\n provider: createSentryIntegrationProvider({\n getSentryInstallation: ({installationUuid}) =>\n getSentryInstallationByInstallationUuid(installationUuid),\n getConnectionById,\n connectSentryInstallation,\n persistVerifiedUnclaimedInstallation,\n coreDb: db,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus,\n }),\n database: {\n db: sentryDb,\n migrationsPath: sentryMigrationsPath,\n migrationsTableName: SENTRY_MIGRATIONS_TABLE,\n },\n workers: [createSentryMaintenanceWorker()],\n };\n}\n\nexport const sentryProviderModule: IntegrationProviderModule = {\n id: 'sentry',\n enabled: config.INTEGRATIONS_ENABLE_SENTRY_PROVIDER,\n load: loadSentryModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","SENTRY_MIGRATIONS_TABLE","loadSentryModuleParts","createSentryIntegrationProvider","createSentryMaintenanceWorker","getSentryInstallationByInstallationUuid","persistVerifiedUnclaimedInstallation","upsertSentryInstallation","sentryDb","migrationsPath","sentryMigrationsPath","getConnectionById","id","connection","undefined","connectSentryInstallation","input","transaction","tx","baseSlug","orgSlug","fallback","slug","workspaceId","provider","externalAccountId","installationUuid","displayName","lifecycleStatus","connectionId","status","codeHash","installerUserId","getSentryInstallation","coreDb","updateConnectionLifecycleStatus","database","migrationsTableName","workers","sentryProviderModule","enabled","INTEGRATIONS_ENABLE_SENTRY_PROVIDER","load"],"mappings":"AAAA,SAEEA,qBAAqB,QAChB,oCAAoC;AAE3C,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,0CAA0C,EAC1CC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,+BAA+B,EAAEC,kBAAkB,QAAO,4BAA4B;AAC9F,SAAQC,4BAA4B,QAAO,gCAAgC;AAG3E,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAMC,0BAA0B;AAEhC,eAAeC;IACb,MAAM,EACJC,+BAA+B,EAC/BC,6BAA6B,EAC7BC,uCAAuC,EACvCC,oCAAoC,EACpCC,wBAAwB,EACxBV,IAAIW,QAAQ,EACZC,gBAAgBC,oBAAoB,EACrC,GAAG,MAAM,MAAM,CAAC;IAEjB,eAAeC,kBACbC,EAAU;QAEV,MAAMC,aAAa,MAAMpB,6BAA6BmB;QACtD,IAAI,CAACC,YAAY,OAAOC;QACxB,OAAOD;IACT;IAEA,eAAeE,0BACbC,KAAqC;QAErC,OAAO,MAAMhB,6BAA6B,IACxCH,KAAKoB,WAAW,CAAC,OAAOC;gBACtB,MAAMC,WAAW5B,sBAAsB,CAAC,OAAO,EAAEyB,MAAMI,OAAO,EAAE,EAAE;oBAACC,UAAU;gBAAQ;gBACrF,MAAMC,OAAO,MAAM5B,4BACjB;oBACE6B,aAAaP,MAAMO,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBT,MAAMU,gBAAgB;oBACzCP;gBACF,GACA;oBAACD;gBAAE;gBAEL,MAAML,aAAa,MAAMjB,4BACvB;oBACE2B,aAAaP,MAAMO,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBT,MAAMU,gBAAgB;oBACzCJ;oBACAK,aAAaX,MAAMW,WAAW;oBAC9BC,iBAAiB;gBACnB,GACA;oBAACV;gBAAE;gBAGL,MAAMX,yBACJ;oBACEsB,cAAchB,WAAWD,EAAE;oBAC3Bc,kBAAkBV,MAAMU,gBAAgB;oBACxCN,SAASJ,MAAMI,OAAO;oBACtBU,QAAQ;oBACRC,UAAUf,MAAMe,QAAQ;oBACxBC,iBAAiBhB,MAAMgB,eAAe;gBACxC,GACA;oBAACd;gBAAE;gBAGL,OAAOL;YACT;IAEJ;IAEA,OAAO;QACLW,UAAUrB,gCAAgC;YACxC8B,uBAAuB,CAAC,EAACP,gBAAgB,EAAC,GACxCrB,wCAAwCqB;YAC1Cf;YACAI;YACAT;YACA4B,QAAQrC;YACRC;YACAC;YACAN;YACA0C,iCAAiCxC;QACnC;QACAyC,UAAU;YACRvC,IAAIW;YACJC,gBAAgBC;YAChB2B,qBAAqBpC;QACvB;QACAqC,SAAS;YAAClC;SAAgC;IAC5C;AACF;AAEA,OAAO,MAAMmC,uBAAkD;IAC7D3B,IAAI;IACJ4B,SAAShD,OAAOiD,mCAAmC;IACnDC,MAAMxC;AACR,EAAE"}
1
+ {"version":3,"sources":["../../src/providers/sentry.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectSentryInstallationInput} from '@shipfox/api-integration-sentry';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n updateIntegrationConnectionLifecycleStatus,\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\n// Stable migration-tracking table name for the Sentry provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst SENTRY_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_sentry';\n\nasync function loadSentryModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createSentryIntegrationProvider,\n createSentryMaintenanceWorker,\n getSentryInstallationByInstallationUuid,\n persistVerifiedUnclaimedInstallation,\n upsertSentryInstallation,\n db: sentryDb,\n migrationsPath: sentryMigrationsPath,\n } = await import('@shipfox/api-integration-sentry');\n\n async function getConnectionById(\n id: string,\n ): Promise<CoreIntegrationConnection<'sentry'> | undefined> {\n const connection = await getIntegrationConnectionById(id);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'sentry'>;\n }\n\n async function connectSentryInstallation(\n input: ConnectSentryInstallationInput,\n ): Promise<CoreIntegrationConnection<'sentry'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`sentry_${input.orgSlug}`, {fallback: 'sentry'});\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'sentry',\n externalAccountId: input.installationUuid,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'sentry',\n externalAccountId: input.installationUuid,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertSentryInstallation(\n {\n connectionId: connection.id,\n installationUuid: input.installationUuid,\n orgSlug: input.orgSlug,\n status: 'installed',\n codeHash: input.codeHash,\n installerUserId: input.installerUserId,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'sentry'>;\n }),\n );\n }\n\n const integrationProvider = createSentryIntegrationProvider({\n getSentryInstallation: ({installationUuid}) =>\n getSentryInstallationByInstallationUuid(installationUuid),\n getConnectionById,\n connectSentryInstallation,\n persistVerifiedUnclaimedInstallation,\n coreDb: db,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus,\n });\n\n return {\n provider: integrationProvider,\n webhookProcessors: integrationProvider.webhookProcessors,\n database: {\n db: sentryDb,\n migrationsPath: sentryMigrationsPath,\n migrationsTableName: SENTRY_MIGRATIONS_TABLE,\n },\n workers: [createSentryMaintenanceWorker()],\n };\n}\n\nexport const sentryProviderModule: IntegrationProviderModule = {\n id: 'sentry',\n enabled: config.INTEGRATIONS_ENABLE_SENTRY_PROVIDER,\n load: loadSentryModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","SENTRY_MIGRATIONS_TABLE","loadSentryModuleParts","createSentryIntegrationProvider","createSentryMaintenanceWorker","getSentryInstallationByInstallationUuid","persistVerifiedUnclaimedInstallation","upsertSentryInstallation","sentryDb","migrationsPath","sentryMigrationsPath","getConnectionById","id","connection","undefined","connectSentryInstallation","input","transaction","tx","baseSlug","orgSlug","fallback","slug","workspaceId","provider","externalAccountId","installationUuid","displayName","lifecycleStatus","connectionId","status","codeHash","installerUserId","integrationProvider","getSentryInstallation","coreDb","updateConnectionLifecycleStatus","webhookProcessors","database","migrationsTableName","workers","sentryProviderModule","enabled","INTEGRATIONS_ENABLE_SENTRY_PROVIDER","load"],"mappings":"AAAA,SAEEA,qBAAqB,QAChB,oCAAoC;AAE3C,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,0CAA0C,EAC1CC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,+BAA+B,EAAEC,kBAAkB,QAAO,4BAA4B;AAC9F,SAAQC,4BAA4B,QAAO,gCAAgC;AAG3E,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAMC,0BAA0B;AAEhC,eAAeC;IACb,MAAM,EACJC,+BAA+B,EAC/BC,6BAA6B,EAC7BC,uCAAuC,EACvCC,oCAAoC,EACpCC,wBAAwB,EACxBV,IAAIW,QAAQ,EACZC,gBAAgBC,oBAAoB,EACrC,GAAG,MAAM,MAAM,CAAC;IAEjB,eAAeC,kBACbC,EAAU;QAEV,MAAMC,aAAa,MAAMpB,6BAA6BmB;QACtD,IAAI,CAACC,YAAY,OAAOC;QACxB,OAAOD;IACT;IAEA,eAAeE,0BACbC,KAAqC;QAErC,OAAO,MAAMhB,6BAA6B,IACxCH,KAAKoB,WAAW,CAAC,OAAOC;gBACtB,MAAMC,WAAW5B,sBAAsB,CAAC,OAAO,EAAEyB,MAAMI,OAAO,EAAE,EAAE;oBAACC,UAAU;gBAAQ;gBACrF,MAAMC,OAAO,MAAM5B,4BACjB;oBACE6B,aAAaP,MAAMO,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBT,MAAMU,gBAAgB;oBACzCP;gBACF,GACA;oBAACD;gBAAE;gBAEL,MAAML,aAAa,MAAMjB,4BACvB;oBACE2B,aAAaP,MAAMO,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBT,MAAMU,gBAAgB;oBACzCJ;oBACAK,aAAaX,MAAMW,WAAW;oBAC9BC,iBAAiB;gBACnB,GACA;oBAACV;gBAAE;gBAGL,MAAMX,yBACJ;oBACEsB,cAAchB,WAAWD,EAAE;oBAC3Bc,kBAAkBV,MAAMU,gBAAgB;oBACxCN,SAASJ,MAAMI,OAAO;oBACtBU,QAAQ;oBACRC,UAAUf,MAAMe,QAAQ;oBACxBC,iBAAiBhB,MAAMgB,eAAe;gBACxC,GACA;oBAACd;gBAAE;gBAGL,OAAOL;YACT;IAEJ;IAEA,MAAMoB,sBAAsB9B,gCAAgC;QAC1D+B,uBAAuB,CAAC,EAACR,gBAAgB,EAAC,GACxCrB,wCAAwCqB;QAC1Cf;QACAI;QACAT;QACA6B,QAAQtC;QACRC;QACAC;QACAN;QACA2C,iCAAiCzC;IACnC;IAEA,OAAO;QACL6B,UAAUS;QACVI,mBAAmBJ,oBAAoBI,iBAAiB;QACxDC,UAAU;YACRzC,IAAIW;YACJC,gBAAgBC;YAChB6B,qBAAqBtC;QACvB;QACAuC,SAAS;YAACpC;SAAgC;IAC5C;AACF;AAEA,OAAO,MAAMqC,uBAAkD;IAC7D7B,IAAI;IACJ8B,SAASlD,OAAOmD,mCAAmC;IACnDC,MAAM1C;AACR,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/providers/slack.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAmJ3F,eAAO,MAAM,mBAAmB,EAAE,yBAIjC,CAAC"}
1
+ {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/providers/slack.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAwK3F,eAAO,MAAM,mBAAmB,EAAE,yBAIjC,CAAC"}
@@ -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;
@@ -85,22 +85,42 @@ async function loadSlackModuleParts(options = {}) {
85
85
  resolveConnection: async (connectionId)=>getIntegrationConnectionById(connectionId),
86
86
  secrets
87
87
  });
88
- return {
89
- provider: createSlackIntegrationProvider({
90
- agentTools: {
91
- tokenStore
88
+ const integrationProvider = createSlackIntegrationProvider({
89
+ agentTools: {
90
+ tokenStore
91
+ },
92
+ cleanup: {
93
+ deleteConnectionRecords: async (connection, { tx })=>{
94
+ await deleteSlackInstallationByConnectionId(connection.id, {
95
+ tx
96
+ });
92
97
  },
93
- routes: {
94
- tokenStore,
95
- getExistingSlackConnection,
96
- connectSlackInstallation,
97
- disconnectSlackInstallation,
98
- coreDb: db,
99
- publishIntegrationEventReceived,
100
- recordDeliveryOnly,
101
- getIntegrationConnectionById
98
+ deleteConnectionSecrets: async (connection)=>{
99
+ // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
100
+ await (options.secrets?.slack?.deleteSecrets({
101
+ workspaceId: connection.workspaceId,
102
+ namespace: slackNamespaceSuffix(slackSecretsNamespace(connection.id))
103
+ }) ?? Promise.resolve());
102
104
  }
103
- }),
105
+ },
106
+ routes: {
107
+ tokenStore,
108
+ getExistingSlackConnection,
109
+ connectSlackInstallation,
110
+ disconnectSlackInstallation,
111
+ coreDb: db,
112
+ claimWebhookDelivery,
113
+ publishIntegrationEventReceived,
114
+ recordDeliveryOnly,
115
+ getIntegrationConnectionById,
116
+ ...options.requireActiveWorkspaceMembership ? {
117
+ requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
118
+ } : {}
119
+ }
120
+ });
121
+ return {
122
+ provider: integrationProvider,
123
+ webhookProcessors: integrationProvider.webhookProcessors,
104
124
  e2eRoutes: [
105
125
  createSlackE2eRoutes({
106
126
  tokenStore,