@shipfox/api-integration-linear 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +10 -0
- package/dist/db/installations.d.ts.map +1 -1
- package/dist/db/installations.js +1 -11
- package/dist/db/installations.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/db/installations.ts +1 -18
- package/tsconfig.build.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled: 23 files with swc (
|
|
2
|
+
Successfully compiled: 23 files with swc (215.26ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @shipfox/api-integration-linear
|
|
2
2
|
|
|
3
|
+
## 4.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- bbba3b7: Adds the Slack integration provider scaffold with installation storage, bot-token custody, and flag-gated registration.
|
|
8
|
+
- Updated dependencies [bbba3b7]
|
|
9
|
+
- @shipfox/node-drizzle@0.3.0
|
|
10
|
+
- @shipfox/api-secrets@4.0.0
|
|
11
|
+
- @shipfox/api-workspaces@4.0.0
|
|
12
|
+
|
|
3
13
|
## 3.0.0
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/db/installations.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/db/installations.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,wBAAwB,CAAC;IACjC,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,8BAA8B;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,MAAM,EAAE,wBAAwB,CAAC;CAClC;AAED,MAAM,WAAW,yCAAyC;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAC/B;AAKD,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,8BAA8B,EACtC,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAyC7B;AAED,wBAAsB,mCAAmC,CACvD,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAUzC;AAED,wBAAsB,qCAAqC,CACzD,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAUzC;AAED,wBAAsB,mCAAmC,CACvD,MAAM,EAAE,yCAAyC,EACjD,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAazC;AAED,wBAAsB,6BAA6B,CACjD,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CASzC;AAED,wBAAsB,sCAAsC,CAC1D,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3B,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED,MAAM,MAAM,uBAAuB,CAAC,CAAC,IAAI;IAAC,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAC,GAAG;IAAC,QAAQ,EAAE,KAAK,CAAA;CAAC,CAAC;AAExF,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,YAAY,EAAE,MAAM,EACpB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAErC"}
|
package/dist/db/installations.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isUniqueViolation } from '@shipfox/node-drizzle';
|
|
1
2
|
import { pgClient } from '@shipfox/node-postgres';
|
|
2
3
|
import { eq } from 'drizzle-orm';
|
|
3
4
|
import { LinearConnectionAlreadyLinkedError, LinearInstallationAlreadyLinkedError } from '#core/errors.js';
|
|
@@ -39,17 +40,6 @@ export async function upsertLinearInstallation(params, options = {}) {
|
|
|
39
40
|
if (!row) throw new LinearInstallationAlreadyLinkedError(params.organizationId);
|
|
40
41
|
return toLinearInstallation(row);
|
|
41
42
|
}
|
|
42
|
-
function isUniqueViolation(error, constraint) {
|
|
43
|
-
let current = error;
|
|
44
|
-
while(typeof current === 'object' && current !== null){
|
|
45
|
-
const postgresError = current;
|
|
46
|
-
if (postgresError.code === '23505' && postgresError.constraint === constraint) {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
current = postgresError.cause;
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
43
|
export async function getLinearInstallationByConnectionId(connectionId, options = {}) {
|
|
54
44
|
const executor = options.tx ?? db();
|
|
55
45
|
const rows = await executor.select().from(linearInstallations).where(eq(linearInstallations.connectionId, connectionId)).limit(1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/installations.ts"],"sourcesContent":["import {pgClient} from '@shipfox/node-postgres';\nimport {eq} from 'drizzle-orm';\nimport {\n LinearConnectionAlreadyLinkedError,\n LinearInstallationAlreadyLinkedError,\n} from '#core/errors.js';\nimport {db} from './db.js';\nimport {linearInstallations, toLinearInstallation} from './schema/installations.js';\n\nexport type LinearInstallationStatus = 'installed' | 'revoked';\n\nexport interface LinearInstallation {\n id: string;\n connectionId: string;\n organizationId: string;\n organizationUrlKey: string;\n appUserId: string;\n scopes: string[];\n tokenExpiresAt: Date | null;\n status: LinearInstallationStatus;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertLinearInstallationParams {\n connectionId: string;\n organizationId: string;\n organizationUrlKey: string;\n appUserId: string;\n scopes: string[];\n tokenExpiresAt?: Date | null | undefined;\n status: LinearInstallationStatus;\n}\n\nexport interface UpdateLinearInstallationTokenExpiryParams {\n connectionId: string;\n tokenExpiresAt: Date | null;\n scopes?: string[] | undefined;\n}\n\ntype LinearDb = ReturnType<typeof db>;\ntype LinearTx = Parameters<Parameters<LinearDb['transaction']>[0]>[0];\n\ninterface PostgresErrorFields {\n code?: unknown;\n constraint?: unknown;\n cause?: unknown;\n}\n\nexport async function upsertLinearInstallation(\n params: UpsertLinearInstallationParams,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const now = new Date();\n let row: typeof linearInstallations.$inferSelect | undefined;\n\n try {\n [row] = await executor\n .insert(linearInstallations)\n .values({\n connectionId: params.connectionId,\n organizationId: params.organizationId,\n organizationUrlKey: params.organizationUrlKey,\n appUserId: params.appUserId,\n scopes: params.scopes,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n status: params.status,\n })\n .onConflictDoUpdate({\n target: linearInstallations.organizationId,\n setWhere: eq(linearInstallations.connectionId, params.connectionId),\n set: {\n connectionId: params.connectionId,\n organizationId: params.organizationId,\n organizationUrlKey: params.organizationUrlKey,\n appUserId: params.appUserId,\n scopes: params.scopes,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n status: params.status,\n updatedAt: now,\n },\n })\n .returning();\n } catch (error) {\n if (isUniqueViolation(error, 'integrations_linear_installations_connection_unique')) {\n throw new LinearConnectionAlreadyLinkedError(params.connectionId);\n }\n throw error;\n }\n\n if (!row) throw new LinearInstallationAlreadyLinkedError(params.organizationId);\n return toLinearInstallation(row);\n}\n\nfunction isUniqueViolation(error: unknown, constraint: string): boolean {\n let current = error;\n while (typeof current === 'object' && current !== null) {\n const postgresError = current as PostgresErrorFields;\n if (postgresError.code === '23505' && postgresError.constraint === constraint) {\n return true;\n }\n current = postgresError.cause;\n }\n return false;\n}\n\nexport async function getLinearInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation | undefined> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const rows = await executor\n .select()\n .from(linearInstallations)\n .where(eq(linearInstallations.connectionId, connectionId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toLinearInstallation(row);\n}\n\nexport async function getLinearInstallationByOrganizationId(\n organizationId: string,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation | undefined> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const rows = await executor\n .select()\n .from(linearInstallations)\n .where(eq(linearInstallations.organizationId, organizationId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toLinearInstallation(row);\n}\n\nexport async function updateLinearInstallationTokenExpiry(\n params: UpdateLinearInstallationTokenExpiryParams,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation | undefined> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const [row] = await executor\n .update(linearInstallations)\n .set({\n tokenExpiresAt: params.tokenExpiresAt,\n ...(params.scopes === undefined ? {} : {scopes: params.scopes}),\n updatedAt: new Date(),\n })\n .where(eq(linearInstallations.connectionId, params.connectionId))\n .returning();\n if (!row) return undefined;\n return toLinearInstallation(row);\n}\n\nexport async function markLinearInstallationRevoked(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation | undefined> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const [row] = await executor\n .update(linearInstallations)\n .set({status: 'revoked', updatedAt: new Date()})\n .where(eq(linearInstallations.connectionId, connectionId))\n .returning();\n if (!row) return undefined;\n return toLinearInstallation(row);\n}\n\nexport async function deleteLinearInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<boolean> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const result = await executor\n .delete(linearInstallations)\n .where(eq(linearInstallations.connectionId, connectionId));\n return (result.rowCount ?? 0) > 0;\n}\n\nexport type LinearRefreshLockResult<T> = {acquired: true; value: T} | {acquired: false};\n\nexport function withLinearRefreshLock<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<LinearRefreshLockResult<T>> {\n return withLinearRefreshLockClient(connectionId, fn);\n}\n\nasync function withLinearRefreshLockClient<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<LinearRefreshLockResult<T>> {\n const client = await pgClient().connect();\n let acquired = false;\n try {\n const lock = await client.query<{acquired: boolean}>(\n 'SELECT pg_try_advisory_lock(hashtext($1)) AS acquired',\n [connectionId],\n );\n acquired = lock.rows[0]?.acquired === true;\n if (!acquired) return {acquired: false};\n\n const value = await fn();\n return {acquired: true, value};\n } finally {\n try {\n if (acquired) {\n await client.query('SELECT pg_advisory_unlock(hashtext($1))', [connectionId]);\n }\n } finally {\n client.release();\n }\n }\n}\n"],"names":["pgClient","eq","LinearConnectionAlreadyLinkedError","LinearInstallationAlreadyLinkedError","db","linearInstallations","toLinearInstallation","upsertLinearInstallation","params","options","executor","tx","now","Date","row","insert","values","connectionId","organizationId","organizationUrlKey","appUserId","scopes","tokenExpiresAt","status","onConflictDoUpdate","target","setWhere","set","updatedAt","returning","error","isUniqueViolation","constraint","current","postgresError","code","cause","getLinearInstallationByConnectionId","rows","select","from","where","limit","undefined","getLinearInstallationByOrganizationId","updateLinearInstallationTokenExpiry","update","markLinearInstallationRevoked","deleteLinearInstallationByConnectionId","result","delete","rowCount","withLinearRefreshLock","fn","withLinearRefreshLockClient","client","connect","acquired","lock","query","value","release"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/db/installations.ts"],"sourcesContent":["import {isUniqueViolation} from '@shipfox/node-drizzle';\nimport {pgClient} from '@shipfox/node-postgres';\nimport {eq} from 'drizzle-orm';\nimport {\n LinearConnectionAlreadyLinkedError,\n LinearInstallationAlreadyLinkedError,\n} from '#core/errors.js';\nimport {db} from './db.js';\nimport {linearInstallations, toLinearInstallation} from './schema/installations.js';\n\nexport type LinearInstallationStatus = 'installed' | 'revoked';\n\nexport interface LinearInstallation {\n id: string;\n connectionId: string;\n organizationId: string;\n organizationUrlKey: string;\n appUserId: string;\n scopes: string[];\n tokenExpiresAt: Date | null;\n status: LinearInstallationStatus;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertLinearInstallationParams {\n connectionId: string;\n organizationId: string;\n organizationUrlKey: string;\n appUserId: string;\n scopes: string[];\n tokenExpiresAt?: Date | null | undefined;\n status: LinearInstallationStatus;\n}\n\nexport interface UpdateLinearInstallationTokenExpiryParams {\n connectionId: string;\n tokenExpiresAt: Date | null;\n scopes?: string[] | undefined;\n}\n\ntype LinearDb = ReturnType<typeof db>;\ntype LinearTx = Parameters<Parameters<LinearDb['transaction']>[0]>[0];\n\nexport async function upsertLinearInstallation(\n params: UpsertLinearInstallationParams,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const now = new Date();\n let row: typeof linearInstallations.$inferSelect | undefined;\n\n try {\n [row] = await executor\n .insert(linearInstallations)\n .values({\n connectionId: params.connectionId,\n organizationId: params.organizationId,\n organizationUrlKey: params.organizationUrlKey,\n appUserId: params.appUserId,\n scopes: params.scopes,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n status: params.status,\n })\n .onConflictDoUpdate({\n target: linearInstallations.organizationId,\n setWhere: eq(linearInstallations.connectionId, params.connectionId),\n set: {\n connectionId: params.connectionId,\n organizationId: params.organizationId,\n organizationUrlKey: params.organizationUrlKey,\n appUserId: params.appUserId,\n scopes: params.scopes,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n status: params.status,\n updatedAt: now,\n },\n })\n .returning();\n } catch (error) {\n if (isUniqueViolation(error, 'integrations_linear_installations_connection_unique')) {\n throw new LinearConnectionAlreadyLinkedError(params.connectionId);\n }\n throw error;\n }\n\n if (!row) throw new LinearInstallationAlreadyLinkedError(params.organizationId);\n return toLinearInstallation(row);\n}\n\nexport async function getLinearInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation | undefined> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const rows = await executor\n .select()\n .from(linearInstallations)\n .where(eq(linearInstallations.connectionId, connectionId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toLinearInstallation(row);\n}\n\nexport async function getLinearInstallationByOrganizationId(\n organizationId: string,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation | undefined> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const rows = await executor\n .select()\n .from(linearInstallations)\n .where(eq(linearInstallations.organizationId, organizationId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toLinearInstallation(row);\n}\n\nexport async function updateLinearInstallationTokenExpiry(\n params: UpdateLinearInstallationTokenExpiryParams,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation | undefined> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const [row] = await executor\n .update(linearInstallations)\n .set({\n tokenExpiresAt: params.tokenExpiresAt,\n ...(params.scopes === undefined ? {} : {scopes: params.scopes}),\n updatedAt: new Date(),\n })\n .where(eq(linearInstallations.connectionId, params.connectionId))\n .returning();\n if (!row) return undefined;\n return toLinearInstallation(row);\n}\n\nexport async function markLinearInstallationRevoked(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<LinearInstallation | undefined> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const [row] = await executor\n .update(linearInstallations)\n .set({status: 'revoked', updatedAt: new Date()})\n .where(eq(linearInstallations.connectionId, connectionId))\n .returning();\n if (!row) return undefined;\n return toLinearInstallation(row);\n}\n\nexport async function deleteLinearInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<boolean> {\n const executor = (options.tx ?? db()) as LinearDb | LinearTx;\n const result = await executor\n .delete(linearInstallations)\n .where(eq(linearInstallations.connectionId, connectionId));\n return (result.rowCount ?? 0) > 0;\n}\n\nexport type LinearRefreshLockResult<T> = {acquired: true; value: T} | {acquired: false};\n\nexport function withLinearRefreshLock<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<LinearRefreshLockResult<T>> {\n return withLinearRefreshLockClient(connectionId, fn);\n}\n\nasync function withLinearRefreshLockClient<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<LinearRefreshLockResult<T>> {\n const client = await pgClient().connect();\n let acquired = false;\n try {\n const lock = await client.query<{acquired: boolean}>(\n 'SELECT pg_try_advisory_lock(hashtext($1)) AS acquired',\n [connectionId],\n );\n acquired = lock.rows[0]?.acquired === true;\n if (!acquired) return {acquired: false};\n\n const value = await fn();\n return {acquired: true, value};\n } finally {\n try {\n if (acquired) {\n await client.query('SELECT pg_advisory_unlock(hashtext($1))', [connectionId]);\n }\n } finally {\n client.release();\n }\n }\n}\n"],"names":["isUniqueViolation","pgClient","eq","LinearConnectionAlreadyLinkedError","LinearInstallationAlreadyLinkedError","db","linearInstallations","toLinearInstallation","upsertLinearInstallation","params","options","executor","tx","now","Date","row","insert","values","connectionId","organizationId","organizationUrlKey","appUserId","scopes","tokenExpiresAt","status","onConflictDoUpdate","target","setWhere","set","updatedAt","returning","error","getLinearInstallationByConnectionId","rows","select","from","where","limit","undefined","getLinearInstallationByOrganizationId","updateLinearInstallationTokenExpiry","update","markLinearInstallationRevoked","deleteLinearInstallationByConnectionId","result","delete","rowCount","withLinearRefreshLock","fn","withLinearRefreshLockClient","client","connect","acquired","lock","query","value","release"],"mappings":"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"}
|