@shipfox/api-integration-core 12.1.0 → 12.2.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.
@@ -1,6 +1,6 @@
1
1
  $ shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js
2
- Successfully compiled: 50 files with swc (284.31ms)
3
- 2026-08-04T14:51:43.676Z [INFO] asset workflow-bundle-646aca428e9943f8cf17.js 3.17 MiB [emitted] [immutable] (name: main)
2
+ Successfully compiled: 50 files with swc (632.34ms)
3
+ 2026-08-06T13:13:12.437Z [INFO] asset workflow-bundle-646aca428e9943f8cf17.js 3.17 MiB [emitted] [immutable] (name: main)
4
4
  orphan modules 33.5 KiB [orphan] 21 modules
5
5
  runtime modules 1.13 KiB 5 modules
6
6
  modules by path ../../../../node_modules/.pnpm/ 970 KiB 196 modules
@@ -12,6 +12,6 @@ optional modules 30 bytes [optional]
12
12
  __temporal_custom_payload_converter (ignored) 15 bytes [optional] [built] [code generated]
13
13
  __temporal_custom_failure_converter (ignored) 15 bytes [optional] [built] [code generated]
14
14
  ../../../shared/node/temporal/dist/workflow-error-interceptor.js 1.21 KiB [built] [code generated]
15
- webpack 5.107.2 compiled successfully in 5477 ms
16
- 2026-08-04T14:51:43.698Z [INFO] Workflow bundle created { size: '3.17MB' }
15
+ webpack 5.107.2 compiled successfully in 10166 ms
16
+ 2026-08-06T13:13:12.466Z [INFO] Workflow bundle created { size: '3.17MB' }
17
17
  /home/runner/work/shipfox/shipfox/libs/api/integration/core/dist/temporal/workflows/index.js: 3323154 bytes
package/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # @shipfox/api-integration-core
2
2
 
3
+ ## 12.2.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 9695359: Cleans up Jira installation records and tokens when a workspace deletes an integration so the site can be reinstalled.
8
+ - 7901a60: Retry workspace workflow definition syncs when an integration connection becomes available.
9
+ - Updated dependencies [9695359]
10
+ - Updated dependencies [7901a60]
11
+ - Updated dependencies [ce0984d]
12
+ - @shipfox/api-integration-jira@12.2.0
13
+ - @shipfox/api-integration-core-dto@12.2.0
14
+ - @shipfox/api-workflows-dto@12.2.0
15
+ - @shipfox/api-integration-spi@1.0.1
16
+ - @shipfox/node-opentelemetry@0.6.4
17
+ - @shipfox/api-agent-dto@12.2.0
18
+ - @shipfox/api-integration-gitea@12.2.0
19
+ - @shipfox/api-integration-github@12.2.0
20
+ - @shipfox/api-integration-linear@12.2.0
21
+ - @shipfox/api-integration-sentry@12.2.0
22
+ - @shipfox/api-integration-slack@12.2.0
23
+ - @shipfox/api-integration-webhook@12.2.0
24
+ - @shipfox/node-fastify@0.4.2
25
+ - @shipfox/node-module@1.0.6
26
+ - @shipfox/node-temporal@0.4.5
27
+ - @shipfox/api-auth-context@12.2.0
28
+
29
+ ## 12.1.1
30
+
31
+ ### Patch Changes
32
+
33
+ - Updated dependencies [4636839]
34
+ - @shipfox/api-integration-jira@12.1.1
35
+ - @shipfox/api-agent-dto@12.0.0
36
+ - @shipfox/api-auth-context@12.0.0
37
+ - @shipfox/api-integration-core-dto@12.0.0
38
+ - @shipfox/api-integration-gitea@12.0.0
39
+ - @shipfox/api-integration-github@12.0.0
40
+ - @shipfox/api-integration-linear@12.0.0
41
+ - @shipfox/api-integration-sentry@12.0.0
42
+ - @shipfox/api-integration-slack@12.0.0
43
+ - @shipfox/api-integration-spi@1.0.0
44
+ - @shipfox/api-integration-webhook@12.0.0
45
+ - @shipfox/api-workflows-dto@12.1.0
46
+ - @shipfox/api-workspaces-dto@12.0.0
47
+ - @shipfox/config@1.2.4
48
+ - @shipfox/inter-module@0.2.3
49
+ - @shipfox/redact@0.2.6
50
+ - @shipfox/regex@0.2.4
51
+ - @shipfox/node-drizzle@0.3.5
52
+ - @shipfox/node-error-monitoring@0.3.0
53
+ - @shipfox/node-fastify@0.4.1
54
+ - @shipfox/node-module@1.0.5
55
+ - @shipfox/node-opentelemetry@0.6.3
56
+ - @shipfox/node-outbox@0.2.6
57
+ - @shipfox/node-postgres@0.5.0
58
+ - @shipfox/node-temporal@0.4.4
59
+
3
60
  ## 12.1.0
4
61
 
5
62
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,KAAK,8BAA8B,EACpC,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAC,qBAAqB,EAAE,uBAAuB,EAAC,MAAM,4BAA4B,CAAC;AAE/F,qBAAa,kCAAmC,SAAQ,KAAK;gBAC/C,YAAY,EAAE,MAAM;CAGjC;AAED,qBAAa,kCAAmC,SAAQ,KAAK;gBAC/C,YAAY,EAAE,MAAM;CAGjC;AAED,qBAAa,2CAA4C,SAAQ,KAAK;gBACxD,YAAY,EAAE,MAAM;CAGjC;AAED,qBAAa,uCAAwC,SAAQ,KAAK;aAE9C,WAAW,EAAE,MAAM;aACnB,QAAQ,EAAE,uBAAuB;aACjC,iBAAiB,EAAE,MAAM;gBAFzB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,uBAAuB,EACjC,iBAAiB,EAAE,MAAM;CAK5C;AAED,qBAAa,qCAAsC,SAAQ,KAAK;aAE5C,UAAU,EAAE,qBAAqB;aACjC,QAAQ,EAAE,uBAAuB;gBADjC,UAAU,EAAE,qBAAqB,EACjC,QAAQ,EAAE,uBAAuB;CAIpD;AAED,qBAAa,mCAAoC,SAAQ,KAAK;aAChC,QAAQ,EAAE,uBAAuB;gBAAjC,QAAQ,EAAE,uBAAuB;CAG9D;AAED,qBAAa,mCAAoC,SAAQ,KAAK;aAChC,QAAQ,EAAE,uBAAuB;gBAAjC,QAAQ,EAAE,uBAAuB;CAG9D;AAED,qBAAa,kCAAmC,SAAQ,KAAK;aAC/B,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;CAI5C;AAED,OAAO,EAAC,2BAA2B,EAAE,wBAAwB,EAAE,KAAK,8BAA8B,EAAC,CAAC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,KAAK,8BAA8B,EACpC,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAC,qBAAqB,EAAE,uBAAuB,EAAC,MAAM,4BAA4B,CAAC;AAE/F,qBAAa,kCAAmC,SAAQ,KAAK;IAC3D,YAAY,YAAY,EAAE,MAAM,EAE/B;CACF;AAED,qBAAa,kCAAmC,SAAQ,KAAK;IAC3D,YAAY,YAAY,EAAE,MAAM,EAE/B;CACF;AAED,qBAAa,2CAA4C,SAAQ,KAAK;IACpE,YAAY,YAAY,EAAE,MAAM,EAE/B;CACF;AAED,qBAAa,uCAAwC,SAAQ,KAAK;aAE9C,WAAW,EAAE,MAAM;aACnB,QAAQ,EAAE,uBAAuB;aACjC,iBAAiB,EAAE,MAAM;IAH3C,YACkB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,uBAAuB,EACjC,iBAAiB,EAAE,MAAM,EAI1C;CACF;AAED,qBAAa,qCAAsC,SAAQ,KAAK;aAE5C,UAAU,EAAE,qBAAqB;aACjC,QAAQ,EAAE,uBAAuB;IAFnD,YACkB,UAAU,EAAE,qBAAqB,EACjC,QAAQ,EAAE,uBAAuB,EAGlD;CACF;AAED,qBAAa,mCAAoC,SAAQ,KAAK;aAChC,QAAQ,EAAE,uBAAuB;IAA7D,YAA4B,QAAQ,EAAE,uBAAuB,EAE5D;CACF;AAED,qBAAa,mCAAoC,SAAQ,KAAK;aAChC,QAAQ,EAAE,uBAAuB;IAA7D,YAA4B,QAAQ,EAAE,uBAAuB,EAE5D;CACF;AAED,qBAAa,kCAAmC,SAAQ,KAAK;aAC/B,OAAO,EAAE,MAAM;IAA3C,YAA4B,OAAO,EAAE,MAAM,EAG1C;CACF;AAED,OAAO,EAAC,2BAA2B,EAAE,wBAAwB,EAAE,KAAK,8BAA8B,EAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/db/connections.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/db/connections.ts"],"names":[],"mappings":"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"}
@@ -1,33 +1,53 @@
1
- import { CONNECTION_SLUG_MAX_LENGTH } from '@shipfox/api-integration-core-dto';
1
+ import { CONNECTION_SLUG_MAX_LENGTH, INTEGRATION_CONNECTION_AVAILABLE } from '@shipfox/api-integration-core-dto';
2
2
  import { ConnectionSlugConflictError } from '@shipfox/api-integration-spi';
3
+ import { writeOutboxEvent } from '@shipfox/node-outbox';
3
4
  import { and, eq } from 'drizzle-orm';
4
5
  import { IntegrationConnectionAlreadyExistsError } from '#core/errors.js';
5
6
  import { db } from './db.js';
6
7
  import { integrationConnections, toIntegrationConnection } from './schema/connections.js';
8
+ import { integrationsOutbox } from './schema/outbox.js';
7
9
  export async function upsertIntegrationConnection(params, options = {}) {
8
- const executor = options.tx ?? db();
10
+ if (options.tx === undefined) {
11
+ return await db().transaction((tx)=>upsertIntegrationConnection(params, {
12
+ tx
13
+ }));
14
+ }
15
+ const executor = options.tx;
9
16
  const now = new Date();
10
- const [row] = await executor.insert(integrationConnections).values({
17
+ let [row] = await executor.insert(integrationConnections).values({
11
18
  workspaceId: params.workspaceId,
12
19
  provider: params.provider,
13
20
  externalAccountId: params.externalAccountId,
14
21
  slug: params.slug,
15
22
  displayName: params.displayName,
16
23
  lifecycleStatus: params.lifecycleStatus ?? 'active'
17
- }).onConflictDoUpdate({
24
+ }).onConflictDoNothing({
18
25
  target: [
19
26
  integrationConnections.workspaceId,
20
27
  integrationConnections.provider,
21
28
  integrationConnections.externalAccountId
22
- ],
23
- set: {
29
+ ]
30
+ }).returning();
31
+ let becameAvailable = row?.lifecycleStatus === 'active';
32
+ if (!row) {
33
+ const [existing] = await executor.select({
34
+ id: integrationConnections.id,
35
+ lifecycleStatus: integrationConnections.lifecycleStatus
36
+ }).from(integrationConnections).where(and(eq(integrationConnections.workspaceId, params.workspaceId), eq(integrationConnections.provider, params.provider), eq(integrationConnections.externalAccountId, params.externalAccountId))).limit(1).for('update');
37
+ if (!existing) throw new Error('Integration connection upsert conflict row was not found');
38
+ [row] = await executor.update(integrationConnections).set({
24
39
  displayName: params.displayName,
25
40
  lifecycleStatus: params.lifecycleStatus ?? 'active',
26
41
  updatedAt: now
27
- }
28
- }).returning();
42
+ }).where(eq(integrationConnections.id, existing.id)).returning();
43
+ becameAvailable = existing.lifecycleStatus !== 'active' && row?.lifecycleStatus === 'active';
44
+ }
29
45
  if (!row) throw new Error('Integration connection upsert returned no rows');
30
- return toIntegrationConnection(row);
46
+ const connection = toIntegrationConnection(row);
47
+ if (becameAvailable) {
48
+ await writeConnectionAvailableEvent(executor, connection);
49
+ }
50
+ return connection;
31
51
  }
32
52
  const INTEGRATION_CONNECTION_EXTERNAL_UNIQUE_CONSTRAINT = 'integrations_connections_workspace_external_unique';
33
53
  const INTEGRATION_CONNECTION_SLUG_UNIQUE_CONSTRAINT = 'integrations_connections_workspace_slug_unique';
@@ -56,7 +76,12 @@ export function isIntegrationConnectionSlugUniqueViolation(error) {
56
76
  return false;
57
77
  }
58
78
  export async function createIntegrationConnection(params, options = {}) {
59
- const executor = options.tx ?? db();
79
+ if (options.tx === undefined) {
80
+ return await db().transaction((tx)=>createIntegrationConnection(params, {
81
+ tx
82
+ }));
83
+ }
84
+ const executor = options.tx;
60
85
  let rows;
61
86
  try {
62
87
  rows = await executor.insert(integrationConnections).values({
@@ -78,7 +103,11 @@ export async function createIntegrationConnection(params, options = {}) {
78
103
  }
79
104
  const row = rows[0];
80
105
  if (!row) throw new Error('Integration connection insert returned no rows');
81
- return toIntegrationConnection(row);
106
+ const connection = toIntegrationConnection(row);
107
+ if (connection.lifecycleStatus === 'active') {
108
+ await writeConnectionAvailableEvent(executor, connection);
109
+ }
110
+ return connection;
82
111
  }
83
112
  export async function resolveUniqueConnectionSlug(params, options = {}) {
84
113
  const executor = options.tx ?? db();
@@ -111,13 +140,37 @@ export async function getIntegrationConnectionBySlug(params) {
111
140
  return toIntegrationConnection(row);
112
141
  }
113
142
  export async function updateIntegrationConnectionLifecycleStatus(params, options = {}) {
114
- const executor = options.tx ?? db();
143
+ if (options.tx === undefined) {
144
+ return await db().transaction((tx)=>updateIntegrationConnectionLifecycleStatus(params, {
145
+ tx
146
+ }));
147
+ }
148
+ const executor = options.tx;
149
+ const [existing] = await executor.select({
150
+ lifecycleStatus: integrationConnections.lifecycleStatus
151
+ }).from(integrationConnections).where(eq(integrationConnections.id, params.id)).limit(1).for('update');
152
+ if (!existing) return undefined;
115
153
  const [row] = await executor.update(integrationConnections).set({
116
154
  lifecycleStatus: params.lifecycleStatus,
117
155
  updatedAt: new Date()
118
156
  }).where(eq(integrationConnections.id, params.id)).returning();
119
157
  if (!row) return undefined;
120
- return toIntegrationConnection(row);
158
+ const connection = toIntegrationConnection(row);
159
+ if (existing.lifecycleStatus !== 'active' && connection.lifecycleStatus === 'active') {
160
+ await writeConnectionAvailableEvent(executor, connection);
161
+ }
162
+ return connection;
163
+ }
164
+ async function writeConnectionAvailableEvent(executor, connection) {
165
+ await writeOutboxEvent(executor, integrationsOutbox, {
166
+ type: INTEGRATION_CONNECTION_AVAILABLE,
167
+ payload: {
168
+ provider: connection.provider,
169
+ workspaceId: connection.workspaceId,
170
+ connectionId: connection.id,
171
+ slug: connection.slug
172
+ }
173
+ });
121
174
  }
122
175
  export async function deleteIntegrationConnection(params, options = {}) {
123
176
  const executor = options.tx ?? db();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/connections.ts"],"sourcesContent":["import {CONNECTION_SLUG_MAX_LENGTH} from '@shipfox/api-integration-core-dto';\nimport {ConnectionSlugConflictError} from '@shipfox/api-integration-spi';\nimport {and, eq} from 'drizzle-orm';\nimport type {\n IntegrationConnection,\n IntegrationConnectionLifecycleStatus,\n} from '#core/entities/connection.js';\nimport type {IntegrationProviderKind} from '#core/entities/provider.js';\nimport {IntegrationConnectionAlreadyExistsError} from '#core/errors.js';\nimport {db} from './db.js';\nimport {integrationConnections, toIntegrationConnection} from './schema/connections.js';\n\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nexport interface UpsertIntegrationConnectionParams {\n workspaceId: string;\n provider: IntegrationProviderKind;\n externalAccountId: string;\n slug: string;\n displayName: string;\n lifecycleStatus?: IntegrationConnectionLifecycleStatus | undefined;\n}\n\nexport async function upsertIntegrationConnection(\n params: UpsertIntegrationConnectionParams,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<IntegrationConnection> {\n const executor = options.tx ?? db();\n const now = new Date();\n const [row] = await executor\n .insert(integrationConnections)\n .values({\n workspaceId: params.workspaceId,\n provider: params.provider,\n externalAccountId: params.externalAccountId,\n slug: params.slug,\n displayName: params.displayName,\n lifecycleStatus: params.lifecycleStatus ?? 'active',\n })\n .onConflictDoUpdate({\n target: [\n integrationConnections.workspaceId,\n integrationConnections.provider,\n integrationConnections.externalAccountId,\n ],\n set: {\n displayName: params.displayName,\n lifecycleStatus: params.lifecycleStatus ?? 'active',\n updatedAt: now,\n },\n })\n .returning();\n\n if (!row) throw new Error('Integration connection upsert returned no rows');\n return toIntegrationConnection(row);\n}\n\nexport interface CreateIntegrationConnectionParams {\n workspaceId: string;\n provider: IntegrationProviderKind;\n externalAccountId: string;\n slug: string;\n displayName: string;\n lifecycleStatus?: IntegrationConnectionLifecycleStatus | undefined;\n}\n\nconst INTEGRATION_CONNECTION_EXTERNAL_UNIQUE_CONSTRAINT =\n 'integrations_connections_workspace_external_unique';\nconst INTEGRATION_CONNECTION_SLUG_UNIQUE_CONSTRAINT =\n 'integrations_connections_workspace_slug_unique';\n\nexport function isIntegrationConnectionUniqueViolation(error: unknown): boolean {\n let current: unknown = error;\n for (let depth = 0; depth < 5 && current != null; depth += 1) {\n if (typeof current !== 'object') return false;\n const {code, constraint} = current as {code?: unknown; constraint?: unknown};\n if (code === '23505' && constraint === INTEGRATION_CONNECTION_EXTERNAL_UNIQUE_CONSTRAINT) {\n return true;\n }\n current = (current as {cause?: unknown}).cause;\n }\n return false;\n}\n\nexport function isIntegrationConnectionSlugUniqueViolation(error: unknown): boolean {\n let current: unknown = error;\n for (let depth = 0; depth < 5 && current != null; depth += 1) {\n if (typeof current !== 'object') return false;\n const {code, constraint} = current as {code?: unknown; constraint?: unknown};\n if (code === '23505' && constraint === INTEGRATION_CONNECTION_SLUG_UNIQUE_CONSTRAINT) {\n return true;\n }\n current = (current as {cause?: unknown}).cause;\n }\n return false;\n}\n\nexport async function createIntegrationConnection(\n params: CreateIntegrationConnectionParams,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<IntegrationConnection> {\n const executor = options.tx ?? db();\n let rows: (typeof integrationConnections.$inferSelect)[];\n try {\n rows = await executor\n .insert(integrationConnections)\n .values({\n workspaceId: params.workspaceId,\n provider: params.provider,\n externalAccountId: params.externalAccountId,\n slug: params.slug,\n displayName: params.displayName,\n lifecycleStatus: params.lifecycleStatus ?? 'active',\n })\n .returning();\n } catch (error) {\n if (isIntegrationConnectionUniqueViolation(error)) {\n throw new IntegrationConnectionAlreadyExistsError(\n params.workspaceId,\n params.provider,\n params.externalAccountId,\n );\n }\n if (isIntegrationConnectionSlugUniqueViolation(error)) {\n throw new ConnectionSlugConflictError(error);\n }\n throw error;\n }\n\n const row = rows[0];\n if (!row) throw new Error('Integration connection insert returned no rows');\n return toIntegrationConnection(row);\n}\n\nexport interface ResolveUniqueConnectionSlugParams {\n workspaceId: string;\n provider: IntegrationProviderKind;\n externalAccountId: string;\n baseSlug: string;\n}\n\nexport async function resolveUniqueConnectionSlug(\n params: ResolveUniqueConnectionSlugParams,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<string> {\n const executor = options.tx ?? db();\n const [existing] = await executor\n .select({slug: integrationConnections.slug})\n .from(integrationConnections)\n .where(\n and(\n eq(integrationConnections.workspaceId, params.workspaceId),\n eq(integrationConnections.provider, params.provider),\n eq(integrationConnections.externalAccountId, params.externalAccountId),\n ),\n )\n .limit(1);\n if (existing) return existing.slug;\n\n const workspaceSlugs = await executor\n .select({slug: integrationConnections.slug})\n .from(integrationConnections)\n .where(eq(integrationConnections.workspaceId, params.workspaceId));\n const used = new Set(workspaceSlugs.map((row) => row.slug));\n\n for (let suffixNumber = 1; ; suffixNumber += 1) {\n const suffix = suffixNumber === 1 ? '' : `_${suffixNumber}`;\n const baseBudget = CONNECTION_SLUG_MAX_LENGTH - suffix.length;\n const candidate = `${params.baseSlug.slice(0, baseBudget).replaceAll(/[_-]+$/g, '')}${suffix}`;\n if (!used.has(candidate)) return candidate;\n }\n}\n\nexport type CreateIntegrationConnectionFn = typeof createIntegrationConnection;\n\nexport async function getIntegrationConnectionById(\n id: string,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<IntegrationConnection | undefined> {\n const executor = options.tx ?? db();\n const rows = await executor\n .select()\n .from(integrationConnections)\n .where(eq(integrationConnections.id, id))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toIntegrationConnection(row);\n}\n\nexport type GetIntegrationConnectionByIdFn = typeof getIntegrationConnectionById;\n\nexport interface GetIntegrationConnectionBySlugParams {\n workspaceId: string;\n slug: string;\n}\n\nexport async function getIntegrationConnectionBySlug(\n params: GetIntegrationConnectionBySlugParams,\n): Promise<IntegrationConnection | undefined> {\n const rows = await db()\n .select()\n .from(integrationConnections)\n .where(\n and(\n eq(integrationConnections.workspaceId, params.workspaceId),\n eq(integrationConnections.slug, params.slug),\n ),\n )\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toIntegrationConnection(row);\n}\n\nexport type GetIntegrationConnectionBySlugFn = typeof getIntegrationConnectionBySlug;\n\nexport interface UpdateIntegrationConnectionLifecycleStatusParams {\n id: string;\n lifecycleStatus: IntegrationConnectionLifecycleStatus;\n}\n\nexport async function updateIntegrationConnectionLifecycleStatus(\n params: UpdateIntegrationConnectionLifecycleStatusParams,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<IntegrationConnection | undefined> {\n const executor = options.tx ?? db();\n const [row] = await executor\n .update(integrationConnections)\n .set({lifecycleStatus: params.lifecycleStatus, updatedAt: new Date()})\n .where(eq(integrationConnections.id, params.id))\n .returning();\n if (!row) return undefined;\n return toIntegrationConnection(row);\n}\n\nexport type UpdateIntegrationConnectionLifecycleStatusFn =\n typeof updateIntegrationConnectionLifecycleStatus;\n\nexport async function deleteIntegrationConnection(\n params: {id: string},\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<boolean> {\n const executor = options.tx ?? db();\n const result = await executor\n .delete(integrationConnections)\n .where(eq(integrationConnections.id, params.id));\n return (result.rowCount ?? 0) > 0;\n}\n\nexport type DeleteIntegrationConnectionFn = typeof deleteIntegrationConnection;\n\nexport interface ListIntegrationConnectionsParams {\n workspaceId: string;\n}\n\nexport async function listIntegrationConnections(\n params: ListIntegrationConnectionsParams,\n): Promise<IntegrationConnection[]> {\n const rows = await db()\n .select()\n .from(integrationConnections)\n .where(eq(integrationConnections.workspaceId, params.workspaceId))\n .orderBy(integrationConnections.createdAt, integrationConnections.id);\n\n const connections = rows.map(toIntegrationConnection);\n return connections;\n}\n\nexport interface ListIntegrationConnectionsByProviderParams {\n provider: IntegrationProviderKind;\n}\n\nexport async function listIntegrationConnectionsByProvider(\n params: ListIntegrationConnectionsByProviderParams,\n): Promise<IntegrationConnection[]> {\n const rows = await db()\n .select()\n .from(integrationConnections)\n .where(eq(integrationConnections.provider, params.provider))\n .orderBy(integrationConnections.createdAt, integrationConnections.id);\n\n return rows.map(toIntegrationConnection);\n}\n"],"names":["CONNECTION_SLUG_MAX_LENGTH","ConnectionSlugConflictError","and","eq","IntegrationConnectionAlreadyExistsError","db","integrationConnections","toIntegrationConnection","upsertIntegrationConnection","params","options","executor","tx","now","Date","row","insert","values","workspaceId","provider","externalAccountId","slug","displayName","lifecycleStatus","onConflictDoUpdate","target","set","updatedAt","returning","Error","INTEGRATION_CONNECTION_EXTERNAL_UNIQUE_CONSTRAINT","INTEGRATION_CONNECTION_SLUG_UNIQUE_CONSTRAINT","isIntegrationConnectionUniqueViolation","error","current","depth","code","constraint","cause","isIntegrationConnectionSlugUniqueViolation","createIntegrationConnection","rows","resolveUniqueConnectionSlug","existing","select","from","where","limit","workspaceSlugs","used","Set","map","suffixNumber","suffix","baseBudget","length","candidate","baseSlug","slice","replaceAll","has","getIntegrationConnectionById","id","undefined","getIntegrationConnectionBySlug","updateIntegrationConnectionLifecycleStatus","update","deleteIntegrationConnection","result","delete","rowCount","listIntegrationConnections","orderBy","createdAt","connections","listIntegrationConnectionsByProvider"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/db/connections.ts"],"sourcesContent":["import {\n CONNECTION_SLUG_MAX_LENGTH,\n INTEGRATION_CONNECTION_AVAILABLE,\n type IntegrationsEventMap,\n} from '@shipfox/api-integration-core-dto';\nimport {ConnectionSlugConflictError} from '@shipfox/api-integration-spi';\nimport {writeOutboxEvent} from '@shipfox/node-outbox';\nimport {and, eq} from 'drizzle-orm';\nimport type {\n IntegrationConnection,\n IntegrationConnectionLifecycleStatus,\n} from '#core/entities/connection.js';\nimport type {IntegrationProviderKind} from '#core/entities/provider.js';\nimport {IntegrationConnectionAlreadyExistsError} from '#core/errors.js';\nimport {db} from './db.js';\nimport {integrationConnections, toIntegrationConnection} from './schema/connections.js';\nimport {integrationsOutbox} from './schema/outbox.js';\n\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nexport interface UpsertIntegrationConnectionParams {\n workspaceId: string;\n provider: IntegrationProviderKind;\n externalAccountId: string;\n slug: string;\n displayName: string;\n lifecycleStatus?: IntegrationConnectionLifecycleStatus | undefined;\n}\n\nexport async function upsertIntegrationConnection(\n params: UpsertIntegrationConnectionParams,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<IntegrationConnection> {\n if (options.tx === undefined) {\n return await db().transaction((tx) => upsertIntegrationConnection(params, {tx}));\n }\n\n const executor = options.tx;\n const now = new Date();\n let [row] = await executor\n .insert(integrationConnections)\n .values({\n workspaceId: params.workspaceId,\n provider: params.provider,\n externalAccountId: params.externalAccountId,\n slug: params.slug,\n displayName: params.displayName,\n lifecycleStatus: params.lifecycleStatus ?? 'active',\n })\n .onConflictDoNothing({\n target: [\n integrationConnections.workspaceId,\n integrationConnections.provider,\n integrationConnections.externalAccountId,\n ],\n })\n .returning();\n\n let becameAvailable = row?.lifecycleStatus === 'active';\n if (!row) {\n const [existing] = await executor\n .select({\n id: integrationConnections.id,\n lifecycleStatus: integrationConnections.lifecycleStatus,\n })\n .from(integrationConnections)\n .where(\n and(\n eq(integrationConnections.workspaceId, params.workspaceId),\n eq(integrationConnections.provider, params.provider),\n eq(integrationConnections.externalAccountId, params.externalAccountId),\n ),\n )\n .limit(1)\n .for('update');\n if (!existing) throw new Error('Integration connection upsert conflict row was not found');\n\n [row] = await executor\n .update(integrationConnections)\n .set({\n displayName: params.displayName,\n lifecycleStatus: params.lifecycleStatus ?? 'active',\n updatedAt: now,\n })\n .where(eq(integrationConnections.id, existing.id))\n .returning();\n becameAvailable = existing.lifecycleStatus !== 'active' && row?.lifecycleStatus === 'active';\n }\n\n if (!row) throw new Error('Integration connection upsert returned no rows');\n const connection = toIntegrationConnection(row);\n if (becameAvailable) {\n await writeConnectionAvailableEvent(executor, connection);\n }\n return connection;\n}\n\nexport interface CreateIntegrationConnectionParams {\n workspaceId: string;\n provider: IntegrationProviderKind;\n externalAccountId: string;\n slug: string;\n displayName: string;\n lifecycleStatus?: IntegrationConnectionLifecycleStatus | undefined;\n}\n\nconst INTEGRATION_CONNECTION_EXTERNAL_UNIQUE_CONSTRAINT =\n 'integrations_connections_workspace_external_unique';\nconst INTEGRATION_CONNECTION_SLUG_UNIQUE_CONSTRAINT =\n 'integrations_connections_workspace_slug_unique';\n\nexport function isIntegrationConnectionUniqueViolation(error: unknown): boolean {\n let current: unknown = error;\n for (let depth = 0; depth < 5 && current != null; depth += 1) {\n if (typeof current !== 'object') return false;\n const {code, constraint} = current as {code?: unknown; constraint?: unknown};\n if (code === '23505' && constraint === INTEGRATION_CONNECTION_EXTERNAL_UNIQUE_CONSTRAINT) {\n return true;\n }\n current = (current as {cause?: unknown}).cause;\n }\n return false;\n}\n\nexport function isIntegrationConnectionSlugUniqueViolation(error: unknown): boolean {\n let current: unknown = error;\n for (let depth = 0; depth < 5 && current != null; depth += 1) {\n if (typeof current !== 'object') return false;\n const {code, constraint} = current as {code?: unknown; constraint?: unknown};\n if (code === '23505' && constraint === INTEGRATION_CONNECTION_SLUG_UNIQUE_CONSTRAINT) {\n return true;\n }\n current = (current as {cause?: unknown}).cause;\n }\n return false;\n}\n\nexport async function createIntegrationConnection(\n params: CreateIntegrationConnectionParams,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<IntegrationConnection> {\n if (options.tx === undefined) {\n return await db().transaction((tx) => createIntegrationConnection(params, {tx}));\n }\n\n const executor = options.tx;\n let rows: (typeof integrationConnections.$inferSelect)[];\n try {\n rows = await executor\n .insert(integrationConnections)\n .values({\n workspaceId: params.workspaceId,\n provider: params.provider,\n externalAccountId: params.externalAccountId,\n slug: params.slug,\n displayName: params.displayName,\n lifecycleStatus: params.lifecycleStatus ?? 'active',\n })\n .returning();\n } catch (error) {\n if (isIntegrationConnectionUniqueViolation(error)) {\n throw new IntegrationConnectionAlreadyExistsError(\n params.workspaceId,\n params.provider,\n params.externalAccountId,\n );\n }\n if (isIntegrationConnectionSlugUniqueViolation(error)) {\n throw new ConnectionSlugConflictError(error);\n }\n throw error;\n }\n\n const row = rows[0];\n if (!row) throw new Error('Integration connection insert returned no rows');\n const connection = toIntegrationConnection(row);\n if (connection.lifecycleStatus === 'active') {\n await writeConnectionAvailableEvent(executor, connection);\n }\n return connection;\n}\n\nexport interface ResolveUniqueConnectionSlugParams {\n workspaceId: string;\n provider: IntegrationProviderKind;\n externalAccountId: string;\n baseSlug: string;\n}\n\nexport async function resolveUniqueConnectionSlug(\n params: ResolveUniqueConnectionSlugParams,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<string> {\n const executor = options.tx ?? db();\n const [existing] = await executor\n .select({slug: integrationConnections.slug})\n .from(integrationConnections)\n .where(\n and(\n eq(integrationConnections.workspaceId, params.workspaceId),\n eq(integrationConnections.provider, params.provider),\n eq(integrationConnections.externalAccountId, params.externalAccountId),\n ),\n )\n .limit(1);\n if (existing) return existing.slug;\n\n const workspaceSlugs = await executor\n .select({slug: integrationConnections.slug})\n .from(integrationConnections)\n .where(eq(integrationConnections.workspaceId, params.workspaceId));\n const used = new Set(workspaceSlugs.map((row) => row.slug));\n\n for (let suffixNumber = 1; ; suffixNumber += 1) {\n const suffix = suffixNumber === 1 ? '' : `_${suffixNumber}`;\n const baseBudget = CONNECTION_SLUG_MAX_LENGTH - suffix.length;\n const candidate = `${params.baseSlug.slice(0, baseBudget).replaceAll(/[_-]+$/g, '')}${suffix}`;\n if (!used.has(candidate)) return candidate;\n }\n}\n\nexport type CreateIntegrationConnectionFn = typeof createIntegrationConnection;\n\nexport async function getIntegrationConnectionById(\n id: string,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<IntegrationConnection | undefined> {\n const executor = options.tx ?? db();\n const rows = await executor\n .select()\n .from(integrationConnections)\n .where(eq(integrationConnections.id, id))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toIntegrationConnection(row);\n}\n\nexport type GetIntegrationConnectionByIdFn = typeof getIntegrationConnectionById;\n\nexport interface GetIntegrationConnectionBySlugParams {\n workspaceId: string;\n slug: string;\n}\n\nexport async function getIntegrationConnectionBySlug(\n params: GetIntegrationConnectionBySlugParams,\n): Promise<IntegrationConnection | undefined> {\n const rows = await db()\n .select()\n .from(integrationConnections)\n .where(\n and(\n eq(integrationConnections.workspaceId, params.workspaceId),\n eq(integrationConnections.slug, params.slug),\n ),\n )\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toIntegrationConnection(row);\n}\n\nexport type GetIntegrationConnectionBySlugFn = typeof getIntegrationConnectionBySlug;\n\nexport interface UpdateIntegrationConnectionLifecycleStatusParams {\n id: string;\n lifecycleStatus: IntegrationConnectionLifecycleStatus;\n}\n\nexport async function updateIntegrationConnectionLifecycleStatus(\n params: UpdateIntegrationConnectionLifecycleStatusParams,\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<IntegrationConnection | undefined> {\n if (options.tx === undefined) {\n return await db().transaction((tx) => updateIntegrationConnectionLifecycleStatus(params, {tx}));\n }\n\n const executor = options.tx;\n const [existing] = await executor\n .select({lifecycleStatus: integrationConnections.lifecycleStatus})\n .from(integrationConnections)\n .where(eq(integrationConnections.id, params.id))\n .limit(1)\n .for('update');\n if (!existing) return undefined;\n\n const [row] = await executor\n .update(integrationConnections)\n .set({lifecycleStatus: params.lifecycleStatus, updatedAt: new Date()})\n .where(eq(integrationConnections.id, params.id))\n .returning();\n if (!row) return undefined;\n const connection = toIntegrationConnection(row);\n if (existing.lifecycleStatus !== 'active' && connection.lifecycleStatus === 'active') {\n await writeConnectionAvailableEvent(executor, connection);\n }\n return connection;\n}\n\nexport type UpdateIntegrationConnectionLifecycleStatusFn =\n typeof updateIntegrationConnectionLifecycleStatus;\n\nasync function writeConnectionAvailableEvent(\n executor: IntegrationDb | IntegrationTx,\n connection: IntegrationConnection,\n): Promise<void> {\n await writeOutboxEvent<IntegrationsEventMap>(executor, integrationsOutbox, {\n type: INTEGRATION_CONNECTION_AVAILABLE,\n payload: {\n provider: connection.provider,\n workspaceId: connection.workspaceId,\n connectionId: connection.id,\n slug: connection.slug,\n },\n });\n}\n\nexport async function deleteIntegrationConnection(\n params: {id: string},\n options: {tx?: IntegrationDb | IntegrationTx | undefined} = {},\n): Promise<boolean> {\n const executor = options.tx ?? db();\n const result = await executor\n .delete(integrationConnections)\n .where(eq(integrationConnections.id, params.id));\n return (result.rowCount ?? 0) > 0;\n}\n\nexport type DeleteIntegrationConnectionFn = typeof deleteIntegrationConnection;\n\nexport interface ListIntegrationConnectionsParams {\n workspaceId: string;\n}\n\nexport async function listIntegrationConnections(\n params: ListIntegrationConnectionsParams,\n): Promise<IntegrationConnection[]> {\n const rows = await db()\n .select()\n .from(integrationConnections)\n .where(eq(integrationConnections.workspaceId, params.workspaceId))\n .orderBy(integrationConnections.createdAt, integrationConnections.id);\n\n const connections = rows.map(toIntegrationConnection);\n return connections;\n}\n\nexport interface ListIntegrationConnectionsByProviderParams {\n provider: IntegrationProviderKind;\n}\n\nexport async function listIntegrationConnectionsByProvider(\n params: ListIntegrationConnectionsByProviderParams,\n): Promise<IntegrationConnection[]> {\n const rows = await db()\n .select()\n .from(integrationConnections)\n .where(eq(integrationConnections.provider, params.provider))\n .orderBy(integrationConnections.createdAt, integrationConnections.id);\n\n return rows.map(toIntegrationConnection);\n}\n"],"names":["CONNECTION_SLUG_MAX_LENGTH","INTEGRATION_CONNECTION_AVAILABLE","ConnectionSlugConflictError","writeOutboxEvent","and","eq","IntegrationConnectionAlreadyExistsError","db","integrationConnections","toIntegrationConnection","integrationsOutbox","upsertIntegrationConnection","params","options","tx","undefined","transaction","executor","now","Date","row","insert","values","workspaceId","provider","externalAccountId","slug","displayName","lifecycleStatus","onConflictDoNothing","target","returning","becameAvailable","existing","select","id","from","where","limit","for","Error","update","set","updatedAt","connection","writeConnectionAvailableEvent","INTEGRATION_CONNECTION_EXTERNAL_UNIQUE_CONSTRAINT","INTEGRATION_CONNECTION_SLUG_UNIQUE_CONSTRAINT","isIntegrationConnectionUniqueViolation","error","current","depth","code","constraint","cause","isIntegrationConnectionSlugUniqueViolation","createIntegrationConnection","rows","resolveUniqueConnectionSlug","workspaceSlugs","used","Set","map","suffixNumber","suffix","baseBudget","length","candidate","baseSlug","slice","replaceAll","has","getIntegrationConnectionById","getIntegrationConnectionBySlug","updateIntegrationConnectionLifecycleStatus","type","payload","connectionId","deleteIntegrationConnection","result","delete","rowCount","listIntegrationConnections","orderBy","createdAt","connections","listIntegrationConnectionsByProvider"],"mappings":"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"}
package/dist/db/db.d.ts CHANGED
@@ -158,7 +158,7 @@ export declare const schema: {
158
158
  generated: undefined;
159
159
  }, {}, {}>;
160
160
  };
161
- dialect: "pg";
161
+ dialect: 'pg';
162
162
  }>;
163
163
  integrationsOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
164
164
  name: "outbox";
@@ -352,7 +352,7 @@ export declare const schema: {
352
352
  generated: undefined;
353
353
  }, {}, {}>;
354
354
  };
355
- dialect: "pg";
355
+ dialect: 'pg';
356
356
  }>;
357
357
  integrationsWebhookDeliveries: import("drizzle-orm/pg-core").PgTableWithColumns<{
358
358
  name: "webhook_deliveries";
@@ -427,7 +427,7 @@ export declare const schema: {
427
427
  generated: undefined;
428
428
  }, {}, {}>;
429
429
  };
430
- dialect: "pg";
430
+ dialect: 'pg';
431
431
  }>;
432
432
  };
433
433
  export declare function db(): NodePgDatabase<{
@@ -589,7 +589,7 @@ export declare function db(): NodePgDatabase<{
589
589
  generated: undefined;
590
590
  }, {}, {}>;
591
591
  };
592
- dialect: "pg";
592
+ dialect: 'pg';
593
593
  }>;
594
594
  integrationsOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
595
595
  name: "outbox";
@@ -783,7 +783,7 @@ export declare function db(): NodePgDatabase<{
783
783
  generated: undefined;
784
784
  }, {}, {}>;
785
785
  };
786
- dialect: "pg";
786
+ dialect: 'pg';
787
787
  }>;
788
788
  integrationsWebhookDeliveries: import("drizzle-orm/pg-core").PgTableWithColumns<{
789
789
  name: "webhook_deliveries";
@@ -858,7 +858,7 @@ export declare function db(): NodePgDatabase<{
858
858
  generated: undefined;
859
859
  }, {}, {}>;
860
860
  };
861
- dialect: "pg";
861
+ dialect: 'pg';
862
862
  }>;
863
863
  }>;
864
864
  export declare function closeDb(): void;
@@ -157,7 +157,7 @@ export declare const integrationConnections: import("drizzle-orm/pg-core").PgTab
157
157
  generated: undefined;
158
158
  }, {}, {}>;
159
159
  };
160
- dialect: "pg";
160
+ dialect: 'pg';
161
161
  }>;
162
162
  export type IntegrationConnectionDb = typeof integrationConnections.$inferSelect;
163
163
  export type IntegrationConnectionCreateDb = typeof integrationConnections.$inferInsert;
@@ -190,6 +190,6 @@ export declare const integrationsOutbox: import("drizzle-orm/pg-core").PgTableWi
190
190
  generated: undefined;
191
191
  }, {}, {}>;
192
192
  };
193
- dialect: "pg";
193
+ dialect: 'pg';
194
194
  }>;
195
195
  //# sourceMappingURL=outbox.d.ts.map
@@ -71,6 +71,6 @@ export declare const integrationsWebhookDeliveries: import("drizzle-orm/pg-core"
71
71
  generated: undefined;
72
72
  }, {}, {}>;
73
73
  };
74
- dialect: "pg";
74
+ dialect: 'pg';
75
75
  }>;
76
76
  //# sourceMappingURL=webhook-deliveries.d.ts.map
@@ -10,9 +10,6 @@ export declare function toIntegrationConnectionDto(connection: IntegrationConnec
10
10
  capabilities: IntegrationCapability[];
11
11
  externalUrl?: string | undefined;
12
12
  }): {
13
- created_at: string;
14
- updated_at: string;
15
- external_url?: string;
16
13
  id: string;
17
14
  workspace_id: string;
18
15
  provider: string;
@@ -21,6 +18,9 @@ export declare function toIntegrationConnectionDto(connection: IntegrationConnec
21
18
  display_name: string;
22
19
  lifecycle_status: import("#core/entities/connection.js").IntegrationConnectionLifecycleStatus;
23
20
  capabilities: IntegrationCapability[];
21
+ external_url?: string;
22
+ created_at: string;
23
+ updated_at: string;
24
24
  };
25
25
  export declare function toRepositoryDto(connectionId: string, repository: RepositorySnapshot): {
26
26
  connection_id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EACV,qBAAqB,EACrB,6BAA6B,EAC9B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,mCAAmC,CAAC;AAE1E,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,6BAA6B;;;;EAM/E;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE;IACP,YAAY,EAAE,qBAAqB,EAAE,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;;;;;;;;;;;;EAGF;AAsBD,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB;;;;;;;;;;EAYnF"}
1
+ {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EACV,qBAAqB,EACrB,6BAA6B,EAC9B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,mCAAmC,CAAC;AAE1E,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,6BAA6B;IAE5E,QAAQ;IACR,YAAY;IACZ,YAAY;EAEf;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE;IACP,YAAY,EAAE,qBAAqB,EAAE,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;;;;;;;;;;;;EAGF;AAsBD,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB;IAEhF,aAAa;IACb,sBAAsB;IACtB,KAAK;IACL,IAAI;IACJ,SAAS;IACT,cAAc;IACd,UAAU;IACV,SAAS;IACT,QAAQ;EAEX"}
@@ -1 +1 @@
1
- {"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/providers/jira.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAgL3F,eAAO,MAAM,kBAAkB,EAAE,yBAIhC,CAAC"}
1
+ {"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/providers/jira.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAiM3F,eAAO,MAAM,kBAAkB,EAAE,yBAIhC,CAAC"}
@@ -5,7 +5,7 @@ import { publishIntegrationEventReceived, recordDeliveryOnly } from '#db/webhook
5
5
  import { retryConnectionSlugCollision, slugifyConnectionSlug } from '#providers/connection-slug.js';
6
6
  const JIRA_SECRETS_NAMESPACE_PREFIX = 'system/integrations/jira/';
7
7
  async function loadJiraModuleParts(options = {}) {
8
- const { createJiraIntegrationProvider, createJiraMaintenanceWorker, createJiraPendingSelectionStore, createJiraTokenStore, db: jiraDb, disconnectJiraInstallation: disconnectJiraInstallationRecords, getJiraInstallationByCloudId, migrationsPath, upsertJiraInstallation } = await import('@shipfox/api-integration-jira');
8
+ const { createJiraIntegrationProvider, createJiraMaintenanceWorker, createJiraPendingSelectionStore, createJiraTokenStore, db: jiraDb, deleteJiraInstallationByConnectionId, disconnectJiraInstallation: disconnectJiraInstallationRecords, getJiraInstallationByCloudId, jiraSecretsNamespace, migrationsPath, upsertJiraInstallation, withJiraRefreshLockAndWait } = await import('@shipfox/api-integration-jira');
9
9
  async function getExistingJiraConnection(input) {
10
10
  const installation = await getJiraInstallationByCloudId(input.cloudId);
11
11
  if (!installation) return undefined;
@@ -98,6 +98,22 @@ async function loadJiraModuleParts(options = {}) {
98
98
  agentTools: {
99
99
  tokenStore
100
100
  },
101
+ cleanup: {
102
+ deleteConnectionRecords: async (connection, { tx })=>{
103
+ await deleteJiraInstallationByConnectionId(connection.id, {
104
+ tx
105
+ });
106
+ },
107
+ deleteConnectionSecrets: async (connection)=>{
108
+ await withJiraRefreshLockAndWait(connection.id, async ()=>{
109
+ // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
110
+ await (options.secrets?.jira?.deleteSecrets({
111
+ workspaceId: connection.workspaceId,
112
+ namespace: jiraNamespaceSuffix(jiraSecretsNamespace(connection.id))
113
+ }) ?? Promise.resolve());
114
+ });
115
+ }
116
+ },
101
117
  routes: {
102
118
  tokenStore,
103
119
  pendingStore,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/jira.ts"],"sourcesContent":["import type {\n ConnectJiraInstallationInput,\n JiraPendingSelectionSecretsStore,\n JiraSecretsStore,\n} from '@shipfox/api-integration-jira';\nimport type {IntegrationConnection as CoreIntegrationConnection} from '@shipfox/api-integration-spi';\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 {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision, slugifyConnectionSlug} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\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 createJiraMaintenanceWorker,\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 const integrationProvider = createJiraIntegrationProvider({\n agentTools: {tokenStore},\n routes: {\n tokenStore,\n pendingStore,\n getExistingJiraConnection,\n connectJiraInstallation,\n disconnectJiraInstallation,\n coreDb: db,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n markConnectionActive: async ({connectionId, tx}) => {\n await updateIntegrationConnectionLifecycleStatus(\n {\n id: connectionId,\n lifecycleStatus: 'active',\n },\n tx === undefined ? {} : {tx: tx as IntegrationTx},\n );\n },\n markConnectionError: async ({connectionId, tx}) => {\n await updateIntegrationConnectionLifecycleStatus(\n {\n id: connectionId,\n lifecycleStatus: 'error',\n },\n tx === undefined ? {} : {tx: tx as IntegrationTx},\n );\n },\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n },\n });\n\n return {\n provider: integrationProvider,\n workers: [\n createJiraMaintenanceWorker({\n tokenStore,\n resolveConnection: getIntegrationConnectionById,\n }),\n ],\n webhookProcessors: integrationProvider.webhookProcessors,\n database: {db: jiraDb, migrationsPath, databaseNamespace: 'integrations_jira'},\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":["config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","slugifyConnectionSlug","JIRA_SECRETS_NAMESPACE_PREFIX","loadJiraModuleParts","options","createJiraIntegrationProvider","createJiraMaintenanceWorker","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","integrationProvider","agentTools","routes","coreDb","markConnectionActive","requireActiveWorkspaceMembership","workers","webhookProcessors","database","databaseNamespace","jiraProviderModule","enabled","INTEGRATIONS_ENABLE_JIRA_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/providers/jira.ts"],"sourcesContent":["import type {\n ConnectJiraInstallationInput,\n JiraPendingSelectionSecretsStore,\n JiraSecretsStore,\n} from '@shipfox/api-integration-jira';\nimport type {IntegrationConnection as CoreIntegrationConnection} from '@shipfox/api-integration-spi';\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 {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision, slugifyConnectionSlug} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\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 createJiraMaintenanceWorker,\n createJiraPendingSelectionStore,\n createJiraTokenStore,\n db: jiraDb,\n deleteJiraInstallationByConnectionId,\n disconnectJiraInstallation: disconnectJiraInstallationRecords,\n getJiraInstallationByCloudId,\n jiraSecretsNamespace,\n migrationsPath,\n upsertJiraInstallation,\n withJiraRefreshLockAndWait,\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 const integrationProvider = createJiraIntegrationProvider({\n agentTools: {tokenStore},\n cleanup: {\n deleteConnectionRecords: async (connection, {tx}) => {\n await deleteJiraInstallationByConnectionId(connection.id, {tx});\n },\n deleteConnectionSecrets: async (connection) => {\n await withJiraRefreshLockAndWait(connection.id, async () => {\n // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.\n await (options.secrets?.jira?.deleteSecrets({\n workspaceId: connection.workspaceId,\n namespace: jiraNamespaceSuffix(jiraSecretsNamespace(connection.id)),\n }) ?? Promise.resolve());\n });\n },\n },\n routes: {\n tokenStore,\n pendingStore,\n getExistingJiraConnection,\n connectJiraInstallation,\n disconnectJiraInstallation,\n coreDb: db,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n markConnectionActive: async ({connectionId, tx}) => {\n await updateIntegrationConnectionLifecycleStatus(\n {\n id: connectionId,\n lifecycleStatus: 'active',\n },\n tx === undefined ? {} : {tx: tx as IntegrationTx},\n );\n },\n markConnectionError: async ({connectionId, tx}) => {\n await updateIntegrationConnectionLifecycleStatus(\n {\n id: connectionId,\n lifecycleStatus: 'error',\n },\n tx === undefined ? {} : {tx: tx as IntegrationTx},\n );\n },\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n },\n });\n\n return {\n provider: integrationProvider,\n workers: [\n createJiraMaintenanceWorker({\n tokenStore,\n resolveConnection: getIntegrationConnectionById,\n }),\n ],\n webhookProcessors: integrationProvider.webhookProcessors,\n database: {db: jiraDb, migrationsPath, databaseNamespace: 'integrations_jira'},\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":["config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","slugifyConnectionSlug","JIRA_SECRETS_NAMESPACE_PREFIX","loadJiraModuleParts","options","createJiraIntegrationProvider","createJiraMaintenanceWorker","createJiraPendingSelectionStore","createJiraTokenStore","jiraDb","deleteJiraInstallationByConnectionId","disconnectJiraInstallation","disconnectJiraInstallationRecords","getJiraInstallationByCloudId","jiraSecretsNamespace","migrationsPath","upsertJiraInstallation","withJiraRefreshLockAndWait","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","integrationProvider","agentTools","cleanup","deleteConnectionRecords","deleteConnectionSecrets","routes","coreDb","markConnectionActive","requireActiveWorkspaceMembership","workers","webhookProcessors","database","databaseNamespace","jiraProviderModule","enabled","INTEGRATIONS_ENABLE_JIRA_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}