@shipfox/api-integration-linear 4.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +56 -0
- package/dist/core/webhook-processor.d.ts +13 -0
- package/dist/core/webhook-processor.d.ts.map +1 -0
- package/dist/core/webhook-processor.js +130 -0
- package/dist/core/webhook-processor.js.map +1 -0
- package/dist/index.d.ts +28 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -4
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/install.d.ts +6 -1
- package/dist/presentation/routes/install.d.ts.map +1 -1
- package/dist/presentation/routes/install.js +6 -4
- package/dist/presentation/routes/install.js.map +1 -1
- package/dist/presentation/routes/webhooks.d.ts +2 -0
- package/dist/presentation/routes/webhooks.d.ts.map +1 -1
- package/dist/presentation/routes/webhooks.js +75 -79
- package/dist/presentation/routes/webhooks.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +20 -31
- package/src/core/disconnect.test.ts +47 -0
- package/src/core/tokens.test.ts +16 -2
- package/src/core/webhook-processor.test.ts +154 -0
- package/src/core/webhook-processor.ts +127 -0
- package/src/index.test.ts +11 -0
- package/src/index.ts +26 -2
- package/src/presentation/routes/install.test.ts +2 -6
- package/src/presentation/routes/install.ts +18 -3
- package/src/presentation/routes/webhooks.test.ts +37 -0
- package/src/presentation/routes/webhooks.ts +88 -86
- package/test/globalSetup.ts +0 -9
- package/tsconfig.build.tsbuildinfo +1 -1
- package/turbo.json +9 -0
- package/test/api-secrets.d.ts +0 -28
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled:
|
|
2
|
+
Successfully compiled: 24 files with swc (423.77ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# @shipfox/api-integration-linear
|
|
2
2
|
|
|
3
|
+
## 6.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f262539: Adds a composed webhook processor and optional provider-neutral delivery source for hosted API runtimes.
|
|
8
|
+
- a42b575: Exposes Secrets through its inter-module contract and migrates Agent, integrations, and Workflows consumers.
|
|
9
|
+
- 8aa7cd3: Adds a shared Linear webhook processor that preserves raw-body signatures and receipt-time replay validation.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- f73da5d: Enforces bounded API context imports and routes inter-module consumers through producer contracts.
|
|
14
|
+
- 326f4c0: Exposes Workspaces inter-module operations and moves Auth and OAuth providers onto injected clients.
|
|
15
|
+
- Updated dependencies [0bb82a4]
|
|
16
|
+
- Updated dependencies [7366f04]
|
|
17
|
+
- Updated dependencies [7ac43a4]
|
|
18
|
+
- Updated dependencies [f262539]
|
|
19
|
+
- Updated dependencies [3bb4e26]
|
|
20
|
+
- Updated dependencies [c2db8c3]
|
|
21
|
+
- Updated dependencies [8bdc149]
|
|
22
|
+
- Updated dependencies [f73da5d]
|
|
23
|
+
- Updated dependencies [6bdf24b]
|
|
24
|
+
- Updated dependencies [b00ed29]
|
|
25
|
+
- Updated dependencies [8aa7cd3]
|
|
26
|
+
- Updated dependencies [326f4c0]
|
|
27
|
+
- Updated dependencies [1820feb]
|
|
28
|
+
- Updated dependencies [4604a06]
|
|
29
|
+
- @shipfox/api-integration-core-dto@6.0.0
|
|
30
|
+
- @shipfox/api-workspaces@6.0.0
|
|
31
|
+
- @shipfox/node-drizzle@0.3.2
|
|
32
|
+
- @shipfox/api-auth-context@6.0.0
|
|
33
|
+
- @shipfox/node-fastify@0.2.4
|
|
34
|
+
- @shipfox/api-integration-linear-dto@6.0.0
|
|
35
|
+
|
|
36
|
+
## 5.0.0
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- fb70438: Cascades provider installation and token deletion when removing a connection.
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- bb037af: Resolves workspace packages from source during development while published consumers continue to use compiled output.
|
|
45
|
+
- Updated dependencies [2875241]
|
|
46
|
+
- Updated dependencies [bb037af]
|
|
47
|
+
- Updated dependencies [fb70438]
|
|
48
|
+
- @shipfox/api-integration-core-dto@5.0.0
|
|
49
|
+
- @shipfox/api-auth-context@5.0.0
|
|
50
|
+
- @shipfox/api-integration-linear-dto@5.0.0
|
|
51
|
+
- @shipfox/api-secrets@5.0.0
|
|
52
|
+
- @shipfox/api-workspaces@5.0.0
|
|
53
|
+
- @shipfox/config@1.2.2
|
|
54
|
+
- @shipfox/node-drizzle@0.3.1
|
|
55
|
+
- @shipfox/node-fastify@0.2.3
|
|
56
|
+
- @shipfox/node-opentelemetry@0.5.2
|
|
57
|
+
- @shipfox/node-postgres@0.4.2
|
|
58
|
+
|
|
3
59
|
## 4.0.0
|
|
4
60
|
|
|
5
61
|
### Patch Changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type GetIntegrationConnectionByIdFn, type PublishIntegrationEventReceivedFn, type RecordDeliveryOnlyFn, type StoredWebhookRequest, type WebhookProcessingResult } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
3
|
+
export interface CreateLinearWebhookProcessorOptions {
|
|
4
|
+
coreDb: () => NodePgDatabase<Record<string, unknown>>;
|
|
5
|
+
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
6
|
+
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
7
|
+
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
8
|
+
}
|
|
9
|
+
export interface LinearWebhookProcessor {
|
|
10
|
+
process(request: StoredWebhookRequest): Promise<WebhookProcessingResult>;
|
|
11
|
+
}
|
|
12
|
+
export declare function createLinearWebhookProcessor(options: CreateLinearWebhookProcessorOptions): LinearWebhookProcessor;
|
|
13
|
+
//# sourceMappingURL=webhook-processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-processor.d.ts","sourceRoot":"","sources":["../../src/core/webhook-processor.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,8BAA8B,EACnC,KAAK,iCAAiC,EACtC,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAC;AAO3C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAS9D,MAAM,WAAW,mCAAmC;IAClD,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;CAC9D;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC1E;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mCAAmC,GAC3C,sBAAsB,CAExB"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { decodeWebhookBody } from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import { LINEAR_PROVIDER, linearWebhookBaseEnvelopeSchema } from '@shipfox/api-integration-linear-dto';
|
|
4
|
+
import { verifyHexHmacSignature } from '@shipfox/node-fastify';
|
|
5
|
+
import { logger } from '@shipfox/node-opentelemetry';
|
|
6
|
+
import { config } from '#config.js';
|
|
7
|
+
import { handleLinearWebhook } from '#core/webhook.js';
|
|
8
|
+
const DELIVERY_HEADER = 'linear-delivery';
|
|
9
|
+
const EVENT_HEADER = 'linear-event';
|
|
10
|
+
const SIGNATURE_HEADER = 'linear-signature';
|
|
11
|
+
const WEBHOOK_REPLAY_WINDOW_MS = 60_000;
|
|
12
|
+
export function createLinearWebhookProcessor(options) {
|
|
13
|
+
return {
|
|
14
|
+
process: (request)=>processLinearWebhookRequest(options, request)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async function processLinearWebhookRequest(options, request) {
|
|
18
|
+
if (request.route_id !== 'linear') {
|
|
19
|
+
throw new Error(`Linear processor cannot process ${request.route_id} requests`);
|
|
20
|
+
}
|
|
21
|
+
const deliveryId = request.headers[DELIVERY_HEADER];
|
|
22
|
+
const event = request.headers[EVENT_HEADER];
|
|
23
|
+
const signature = request.headers[SIGNATURE_HEADER];
|
|
24
|
+
if (!deliveryId || !event || !signature) {
|
|
25
|
+
return {
|
|
26
|
+
outcome: 'discarded',
|
|
27
|
+
reason: 'missing_required_input'
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const rawBody = Buffer.from(decodeWebhookBody(request.body));
|
|
31
|
+
if (!verifyHexHmacSignature({
|
|
32
|
+
rawBody,
|
|
33
|
+
signature,
|
|
34
|
+
secret: config.LINEAR_WEBHOOK_SIGNING_SECRET
|
|
35
|
+
})) {
|
|
36
|
+
return {
|
|
37
|
+
outcome: 'discarded',
|
|
38
|
+
reason: 'invalid_signature',
|
|
39
|
+
deliveryId
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
let rawPayload;
|
|
43
|
+
try {
|
|
44
|
+
rawPayload = JSON.parse(rawBody.toString('utf8'));
|
|
45
|
+
} catch (error) {
|
|
46
|
+
logger().warn({
|
|
47
|
+
deliveryId,
|
|
48
|
+
err: error
|
|
49
|
+
}, 'linear webhook payload JSON parse failed');
|
|
50
|
+
return {
|
|
51
|
+
outcome: 'discarded',
|
|
52
|
+
reason: 'malformed_payload',
|
|
53
|
+
deliveryId
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const payload = linearWebhookBaseEnvelopeSchema.safeParse(rawPayload);
|
|
57
|
+
if (!payload.success) {
|
|
58
|
+
logger().warn({
|
|
59
|
+
deliveryId,
|
|
60
|
+
issues: payload.error.issues
|
|
61
|
+
}, 'linear webhook envelope failed schema validation');
|
|
62
|
+
await recordSignedDeliveryOnly(options, deliveryId);
|
|
63
|
+
return {
|
|
64
|
+
outcome: 'discarded',
|
|
65
|
+
reason: 'unsupported_event',
|
|
66
|
+
deliveryId
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const receivedAt = new Date(request.received_at).getTime();
|
|
70
|
+
if (Math.abs(receivedAt - payload.data.webhookTimestamp) > WEBHOOK_REPLAY_WINDOW_MS) {
|
|
71
|
+
return {
|
|
72
|
+
outcome: 'discarded',
|
|
73
|
+
reason: 'stale_at_receipt',
|
|
74
|
+
deliveryId
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (event !== payload.data.type) {
|
|
78
|
+
logger().warn({
|
|
79
|
+
deliveryId,
|
|
80
|
+
event,
|
|
81
|
+
type: payload.data.type
|
|
82
|
+
}, 'linear webhook event header did not match payload type');
|
|
83
|
+
await recordSignedDeliveryOnly(options, deliveryId);
|
|
84
|
+
return {
|
|
85
|
+
outcome: 'discarded',
|
|
86
|
+
reason: 'unsupported_event',
|
|
87
|
+
deliveryId
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const result = await options.coreDb().transaction(async (tx)=>handleLinearWebhook({
|
|
91
|
+
tx,
|
|
92
|
+
deliveryId,
|
|
93
|
+
payload: payload.data,
|
|
94
|
+
rawPayload,
|
|
95
|
+
publishIntegrationEventReceived: options.publishIntegrationEventReceived,
|
|
96
|
+
recordDeliveryOnly: options.recordDeliveryOnly,
|
|
97
|
+
getIntegrationConnectionById: options.getIntegrationConnectionById
|
|
98
|
+
}));
|
|
99
|
+
if (result.outcome === 'published') return {
|
|
100
|
+
outcome: 'processed',
|
|
101
|
+
deliveryId
|
|
102
|
+
};
|
|
103
|
+
if (result.outcome === 'duplicate') return {
|
|
104
|
+
outcome: 'duplicate',
|
|
105
|
+
deliveryId
|
|
106
|
+
};
|
|
107
|
+
if (result.outcome === 'unsupported-event') {
|
|
108
|
+
return {
|
|
109
|
+
outcome: 'discarded',
|
|
110
|
+
reason: 'unsupported_event',
|
|
111
|
+
deliveryId
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
outcome: 'discarded',
|
|
116
|
+
reason: 'connection_unavailable',
|
|
117
|
+
deliveryId
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async function recordSignedDeliveryOnly(options, deliveryId) {
|
|
121
|
+
await options.coreDb().transaction(async (tx)=>{
|
|
122
|
+
await options.recordDeliveryOnly({
|
|
123
|
+
tx,
|
|
124
|
+
provider: LINEAR_PROVIDER,
|
|
125
|
+
deliveryId
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=webhook-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/webhook-processor.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {\n decodeWebhookBody,\n type GetIntegrationConnectionByIdFn,\n type PublishIntegrationEventReceivedFn,\n type RecordDeliveryOnlyFn,\n type StoredWebhookRequest,\n type WebhookProcessingResult,\n} from '@shipfox/api-integration-core-dto';\nimport {\n LINEAR_PROVIDER,\n linearWebhookBaseEnvelopeSchema,\n} from '@shipfox/api-integration-linear-dto';\nimport {verifyHexHmacSignature} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {NodePgDatabase} from 'drizzle-orm/node-postgres';\nimport {config} from '#config.js';\nimport {handleLinearWebhook} from '#core/webhook.js';\n\nconst DELIVERY_HEADER = 'linear-delivery';\nconst EVENT_HEADER = 'linear-event';\nconst SIGNATURE_HEADER = 'linear-signature';\nconst WEBHOOK_REPLAY_WINDOW_MS = 60_000;\n\nexport interface CreateLinearWebhookProcessorOptions {\n coreDb: () => NodePgDatabase<Record<string, unknown>>;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n recordDeliveryOnly: RecordDeliveryOnlyFn;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport interface LinearWebhookProcessor {\n process(request: StoredWebhookRequest): Promise<WebhookProcessingResult>;\n}\n\nexport function createLinearWebhookProcessor(\n options: CreateLinearWebhookProcessorOptions,\n): LinearWebhookProcessor {\n return {process: (request) => processLinearWebhookRequest(options, request)};\n}\n\nasync function processLinearWebhookRequest(\n options: CreateLinearWebhookProcessorOptions,\n request: StoredWebhookRequest,\n): Promise<WebhookProcessingResult> {\n if (request.route_id !== 'linear') {\n throw new Error(`Linear processor cannot process ${request.route_id} requests`);\n }\n\n const deliveryId = request.headers[DELIVERY_HEADER];\n const event = request.headers[EVENT_HEADER];\n const signature = request.headers[SIGNATURE_HEADER];\n if (!deliveryId || !event || !signature) {\n return {outcome: 'discarded', reason: 'missing_required_input'};\n }\n\n const rawBody = Buffer.from(decodeWebhookBody(request.body));\n if (\n !verifyHexHmacSignature({\n rawBody,\n signature,\n secret: config.LINEAR_WEBHOOK_SIGNING_SECRET,\n })\n ) {\n return {outcome: 'discarded', reason: 'invalid_signature', deliveryId};\n }\n\n let rawPayload: unknown;\n try {\n rawPayload = JSON.parse(rawBody.toString('utf8'));\n } catch (error) {\n logger().warn({deliveryId, err: error}, 'linear webhook payload JSON parse failed');\n return {outcome: 'discarded', reason: 'malformed_payload', deliveryId};\n }\n\n const payload = linearWebhookBaseEnvelopeSchema.safeParse(rawPayload);\n if (!payload.success) {\n logger().warn(\n {deliveryId, issues: payload.error.issues},\n 'linear webhook envelope failed schema validation',\n );\n await recordSignedDeliveryOnly(options, deliveryId);\n return {outcome: 'discarded', reason: 'unsupported_event', deliveryId};\n }\n\n const receivedAt = new Date(request.received_at).getTime();\n if (Math.abs(receivedAt - payload.data.webhookTimestamp) > WEBHOOK_REPLAY_WINDOW_MS) {\n return {outcome: 'discarded', reason: 'stale_at_receipt', deliveryId};\n }\n\n if (event !== payload.data.type) {\n logger().warn(\n {deliveryId, event, type: payload.data.type},\n 'linear webhook event header did not match payload type',\n );\n await recordSignedDeliveryOnly(options, deliveryId);\n return {outcome: 'discarded', reason: 'unsupported_event', deliveryId};\n }\n\n const result = await options.coreDb().transaction(async (tx) =>\n handleLinearWebhook({\n tx,\n deliveryId,\n payload: payload.data,\n rawPayload,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n recordDeliveryOnly: options.recordDeliveryOnly,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n }),\n );\n\n if (result.outcome === 'published') return {outcome: 'processed', deliveryId};\n if (result.outcome === 'duplicate') return {outcome: 'duplicate', deliveryId};\n if (result.outcome === 'unsupported-event') {\n return {outcome: 'discarded', reason: 'unsupported_event', deliveryId};\n }\n return {outcome: 'discarded', reason: 'connection_unavailable', deliveryId};\n}\n\nasync function recordSignedDeliveryOnly(\n options: Pick<CreateLinearWebhookProcessorOptions, 'coreDb' | 'recordDeliveryOnly'>,\n deliveryId: string,\n): Promise<void> {\n await options.coreDb().transaction(async (tx) => {\n await options.recordDeliveryOnly({tx, provider: LINEAR_PROVIDER, deliveryId});\n });\n}\n"],"names":["Buffer","decodeWebhookBody","LINEAR_PROVIDER","linearWebhookBaseEnvelopeSchema","verifyHexHmacSignature","logger","config","handleLinearWebhook","DELIVERY_HEADER","EVENT_HEADER","SIGNATURE_HEADER","WEBHOOK_REPLAY_WINDOW_MS","createLinearWebhookProcessor","options","process","request","processLinearWebhookRequest","route_id","Error","deliveryId","headers","event","signature","outcome","reason","rawBody","from","body","secret","LINEAR_WEBHOOK_SIGNING_SECRET","rawPayload","JSON","parse","toString","error","warn","err","payload","safeParse","success","issues","recordSignedDeliveryOnly","receivedAt","Date","received_at","getTime","Math","abs","data","webhookTimestamp","type","result","coreDb","transaction","tx","publishIntegrationEventReceived","recordDeliveryOnly","getIntegrationConnectionById","provider"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,8 +23,10 @@ export type { CreateLinearTokenStoreParams, GetLinearAccessTokenParams, LinearCo
|
|
|
23
23
|
export { createLinearTokenStore, linearSecretsNamespace, } from '#core/tokens.js';
|
|
24
24
|
export type { HandleLinearWebhookOutcome, HandleLinearWebhookParams } from '#core/webhook.js';
|
|
25
25
|
export { handleLinearWebhook } from '#core/webhook.js';
|
|
26
|
+
export type { CreateLinearWebhookProcessorOptions, LinearWebhookProcessor, } from '#core/webhook-processor.js';
|
|
27
|
+
export { createLinearWebhookProcessor } from '#core/webhook-processor.js';
|
|
26
28
|
export type { LinearInstallation, LinearInstallationStatus, UpdateLinearInstallationTokenExpiryParams, UpsertLinearInstallationParams, } from '#db/installations.js';
|
|
27
|
-
export { getLinearInstallationByConnectionId, getLinearInstallationByOrganizationId, markLinearInstallationRevoked, updateLinearInstallationTokenExpiry, upsertLinearInstallation, withLinearRefreshLock, } from '#db/installations.js';
|
|
29
|
+
export { deleteLinearInstallationByConnectionId, getLinearInstallationByConnectionId, getLinearInstallationByOrganizationId, markLinearInstallationRevoked, updateLinearInstallationTokenExpiry, upsertLinearInstallation, withLinearRefreshLock, } from '#db/installations.js';
|
|
28
30
|
export { type CreateLinearE2eRoutesOptions, createLinearE2eRoutes, } from '#presentation/e2eRoutes/index.js';
|
|
29
31
|
export { closeDb, config, db, migrationsPath };
|
|
30
32
|
export interface CreateLinearIntegrationProviderOptions {
|
|
@@ -35,9 +37,34 @@ export interface CreateLinearIntegrationProviderOptions {
|
|
|
35
37
|
callTimeoutMs?: number | undefined;
|
|
36
38
|
} | undefined;
|
|
37
39
|
getLinearInstallationByConnectionId?: typeof getLinearInstallationByConnectionId | undefined;
|
|
40
|
+
cleanup?: {
|
|
41
|
+
deleteConnectionRecords?: (connection: {
|
|
42
|
+
id: string;
|
|
43
|
+
}, options: {
|
|
44
|
+
tx: unknown;
|
|
45
|
+
}) => Promise<void>;
|
|
46
|
+
deleteConnectionSecrets?: (connection: {
|
|
47
|
+
id: string;
|
|
48
|
+
workspaceId: string;
|
|
49
|
+
}) => Promise<void>;
|
|
50
|
+
} | undefined;
|
|
38
51
|
routes?: (Omit<CreateLinearIntegrationRoutesOptions, 'linear' | 'connectionCapabilities'> & Partial<CreateLinearWebhookRoutesOptions>) | undefined;
|
|
39
52
|
}
|
|
40
53
|
export declare function createLinearIntegrationProvider(options?: CreateLinearIntegrationProviderOptions): {
|
|
54
|
+
routes: import("@shipfox/node-fastify").RouteGroup[];
|
|
55
|
+
webhookProcessors: {
|
|
56
|
+
routeIds: readonly ["linear"];
|
|
57
|
+
processor: import("#core/webhook-processor.js").LinearWebhookProcessor;
|
|
58
|
+
}[] | undefined;
|
|
59
|
+
deleteConnectionRecords?: (connection: {
|
|
60
|
+
id: string;
|
|
61
|
+
}, options: {
|
|
62
|
+
tx: unknown;
|
|
63
|
+
}) => Promise<void>;
|
|
64
|
+
deleteConnectionSecrets?: (connection: {
|
|
65
|
+
id: string;
|
|
66
|
+
workspaceId: string;
|
|
67
|
+
}) => Promise<void>;
|
|
41
68
|
provider: string;
|
|
42
69
|
displayName: string;
|
|
43
70
|
adapters: {
|
|
@@ -48,6 +75,5 @@ export declare function createLinearIntegrationProvider(options?: CreateLinearIn
|
|
|
48
75
|
connectionExternalUrl(connection: {
|
|
49
76
|
id: string;
|
|
50
77
|
}): Promise<string | undefined>;
|
|
51
|
-
routes: import("@shipfox/node-fastify").RouteGroup[];
|
|
52
78
|
};
|
|
53
79
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAC,MAAM,EAAC,MAAM,YAAY,CAAC;AAClC,OAAO,EAAC,wBAAwB,EAAC,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { LINEAR_PROVIDER } from '@shipfox/api-integration-linear-dto';
|
|
|
2
2
|
import { createLinearApiClient } from '#api/client.js';
|
|
3
3
|
import { config } from '#config.js';
|
|
4
4
|
import { LinearAgentToolsProvider } from '#core/agent-tools-provider.js';
|
|
5
|
+
import { createLinearWebhookProcessor } from '#core/webhook-processor.js';
|
|
5
6
|
import { closeDb, db } from '#db/db.js';
|
|
6
7
|
import { getLinearInstallationByConnectionId } from '#db/installations.js';
|
|
7
8
|
import { migrationsPath } from '#db/migrations.js';
|
|
@@ -16,7 +17,8 @@ export { assertLinearAuthorizationScopes, formatLinearOAuthScopes, LINEAR_OAUTH_
|
|
|
16
17
|
export { signLinearInstallState, verifyLinearInstallState } from '#core/state.js';
|
|
17
18
|
export { createLinearTokenStore, linearSecretsNamespace } from '#core/tokens.js';
|
|
18
19
|
export { handleLinearWebhook } from '#core/webhook.js';
|
|
19
|
-
export {
|
|
20
|
+
export { createLinearWebhookProcessor } from '#core/webhook-processor.js';
|
|
21
|
+
export { deleteLinearInstallationByConnectionId, getLinearInstallationByConnectionId, getLinearInstallationByOrganizationId, markLinearInstallationRevoked, updateLinearInstallationTokenExpiry, upsertLinearInstallation, withLinearRefreshLock } from '#db/installations.js';
|
|
20
22
|
export { createLinearE2eRoutes } from '#presentation/e2eRoutes/index.js';
|
|
21
23
|
export { closeDb, config, db, migrationsPath };
|
|
22
24
|
export function createLinearIntegrationProvider(options = {}) {
|
|
@@ -25,6 +27,7 @@ export function createLinearIntegrationProvider(options = {}) {
|
|
|
25
27
|
const adapters = options.agentTools ? {
|
|
26
28
|
agent_tools: new LinearAgentToolsProvider(options.agentTools)
|
|
27
29
|
} : {};
|
|
30
|
+
const webhookProcessor = options.routes && hasLinearWebhookRoutesOptions(options.routes) ? createLinearWebhookProcessor(options.routes) : undefined;
|
|
28
31
|
const routes = options.routes ? [
|
|
29
32
|
createLinearIntegrationRoutes({
|
|
30
33
|
linear,
|
|
@@ -34,8 +37,11 @@ export function createLinearIntegrationProvider(options = {}) {
|
|
|
34
37
|
...options.routes
|
|
35
38
|
})
|
|
36
39
|
] : [];
|
|
37
|
-
if (options.routes && hasLinearWebhookRoutesOptions(options.routes)) {
|
|
38
|
-
routes.push(createLinearWebhookRoutes(
|
|
40
|
+
if (options.routes && hasLinearWebhookRoutesOptions(options.routes) && webhookProcessor) {
|
|
41
|
+
routes.push(createLinearWebhookRoutes({
|
|
42
|
+
...options.routes,
|
|
43
|
+
processor: webhookProcessor
|
|
44
|
+
}));
|
|
39
45
|
}
|
|
40
46
|
return {
|
|
41
47
|
provider: LINEAR_PROVIDER,
|
|
@@ -46,7 +52,16 @@ export function createLinearIntegrationProvider(options = {}) {
|
|
|
46
52
|
if (!installation?.organizationUrlKey) return undefined;
|
|
47
53
|
return `https://linear.app/${encodeURIComponent(installation.organizationUrlKey)}/settings`;
|
|
48
54
|
},
|
|
49
|
-
|
|
55
|
+
...options.cleanup,
|
|
56
|
+
routes,
|
|
57
|
+
webhookProcessors: webhookProcessor ? [
|
|
58
|
+
{
|
|
59
|
+
routeIds: [
|
|
60
|
+
'linear'
|
|
61
|
+
],
|
|
62
|
+
processor: webhookProcessor
|
|
63
|
+
}
|
|
64
|
+
] : undefined
|
|
50
65
|
};
|
|
51
66
|
}
|
|
52
67
|
function hasLinearWebhookRoutesOptions(routes) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {LINEAR_PROVIDER} from '@shipfox/api-integration-linear-dto';\nimport {createLinearApiClient, type LinearApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {LinearAgentToolsProvider} from '#core/agent-tools-provider.js';\nimport type {LinearTokenStore} from '#core/tokens.js';\nimport {closeDb, db} from '#db/db.js';\nimport {getLinearInstallationByConnectionId} from '#db/installations.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {\n type CreateLinearIntegrationRoutesOptions,\n createLinearIntegrationRoutes,\n} from '#presentation/routes/install.js';\nimport {\n type CreateLinearWebhookRoutesOptions,\n createLinearWebhookRoutes,\n} from '#presentation/routes/webhooks.js';\n\nexport type {LinearProvider} from '@shipfox/api-integration-linear-dto';\nexport type {LinearApiClient, LinearAuthorization, LinearIdentity} from '#api/client.js';\nexport {createLinearApiClient} from '#api/client.js';\nexport type {\n LinearAgentToolCatalogEntry,\n LinearAgentToolCategory,\n LinearAgentToolId,\n LinearAgentToolRequiredScope,\n} from '#core/agent-tools.js';\nexport {\n linearAgentToolCatalog,\n linearAgentToolSelectionCatalog,\n} from '#core/agent-tools.js';\nexport {\n type DisconnectLinearInstallationParams,\n disconnectLinearInstallation,\n} from '#core/disconnect.js';\nexport {\n LinearAccessTokenMissingError,\n LinearAuthorizationScopeMismatchError,\n LinearConnectionAlreadyLinkedError,\n LinearConnectionNotFoundError,\n LinearInstallationAlreadyLinkedError,\n LinearInstallStateActorMismatchError,\n LinearInstallStateError,\n LinearIntegrationProviderError,\n LinearOAuthCallbackError,\n LinearTokenUnrefreshableError,\n} from '#core/errors.js';\nexport type {ConnectLinearInstallationInput, HandleLinearCallbackParams} from '#core/install.js';\nexport {handleLinearCallback, handleLinearOAuthCallbackError} from '#core/install.js';\nexport {\n assertLinearAuthorizationScopes,\n formatLinearOAuthScopes,\n LINEAR_OAUTH_SCOPES,\n} from '#core/scopes.js';\nexport type {LinearInstallStateClaims} from '#core/state.js';\nexport {signLinearInstallState, verifyLinearInstallState} from '#core/state.js';\nexport type {\n CreateLinearTokenStoreParams,\n GetLinearAccessTokenParams,\n LinearConnectionResolverResult,\n LinearSecretsStore,\n LinearTokenStore,\n StoreLinearTokensParams,\n} from '#core/tokens.js';\nexport {\n createLinearTokenStore,\n linearSecretsNamespace,\n} from '#core/tokens.js';\nexport type {HandleLinearWebhookOutcome, HandleLinearWebhookParams} from '#core/webhook.js';\nexport {handleLinearWebhook} from '#core/webhook.js';\nexport type {\n LinearInstallation,\n LinearInstallationStatus,\n UpdateLinearInstallationTokenExpiryParams,\n UpsertLinearInstallationParams,\n} from '#db/installations.js';\nexport {\n getLinearInstallationByConnectionId,\n getLinearInstallationByOrganizationId,\n markLinearInstallationRevoked,\n updateLinearInstallationTokenExpiry,\n upsertLinearInstallation,\n withLinearRefreshLock,\n} from '#db/installations.js';\nexport {\n type CreateLinearE2eRoutesOptions,\n createLinearE2eRoutes,\n} from '#presentation/e2eRoutes/index.js';\nexport {closeDb, config, db, migrationsPath};\n\nexport interface CreateLinearIntegrationProviderOptions {\n linear?: LinearApiClient | undefined;\n agentTools?:\n | {\n tokenStore: Pick<LinearTokenStore, 'getAccessToken'>;\n endpoint?: string | URL | undefined;\n callTimeoutMs?: number | undefined;\n }\n | undefined;\n getLinearInstallationByConnectionId?: typeof getLinearInstallationByConnectionId | undefined;\n routes?:\n | (Omit<CreateLinearIntegrationRoutesOptions, 'linear' | 'connectionCapabilities'> &\n Partial<CreateLinearWebhookRoutesOptions>)\n | undefined;\n}\n\nexport function createLinearIntegrationProvider(\n options: CreateLinearIntegrationProviderOptions = {},\n) {\n const linear = options.linear ?? createLinearApiClient();\n const getInstallationByConnectionId =\n options.getLinearInstallationByConnectionId ?? getLinearInstallationByConnectionId;\n const adapters = options.agentTools\n ? {\n agent_tools: new LinearAgentToolsProvider(options.agentTools),\n }\n : {};\n\n const routes = options.routes\n ? [\n createLinearIntegrationRoutes({\n linear,\n connectionCapabilities: adapters.agent_tools ? ['agent_tools'] : [],\n ...options.routes,\n }),\n ]\n : [];\n if (options.routes && hasLinearWebhookRoutesOptions(options.routes)) {\n routes.push(createLinearWebhookRoutes(options.routes));\n }\n\n return {\n provider: LINEAR_PROVIDER,\n displayName: 'Linear',\n adapters,\n async connectionExternalUrl(connection: {id: string}): Promise<string | undefined> {\n const installation = await getInstallationByConnectionId(connection.id);\n if (!installation?.organizationUrlKey) return undefined;\n return `https://linear.app/${encodeURIComponent(installation.organizationUrlKey)}/settings`;\n },\n routes,\n };\n}\n\nfunction hasLinearWebhookRoutesOptions(\n routes: Partial<CreateLinearWebhookRoutesOptions>,\n): routes is CreateLinearWebhookRoutesOptions {\n return (\n routes.coreDb !== undefined &&\n routes.publishIntegrationEventReceived !== undefined &&\n routes.recordDeliveryOnly !== undefined &&\n routes.getIntegrationConnectionById !== undefined\n );\n}\n"],"names":["LINEAR_PROVIDER","createLinearApiClient","config","LinearAgentToolsProvider","closeDb","db","getLinearInstallationByConnectionId","migrationsPath","createLinearIntegrationRoutes","createLinearWebhookRoutes","linearAgentToolCatalog","linearAgentToolSelectionCatalog","disconnectLinearInstallation","LinearAccessTokenMissingError","LinearAuthorizationScopeMismatchError","LinearConnectionAlreadyLinkedError","LinearConnectionNotFoundError","LinearInstallationAlreadyLinkedError","LinearInstallStateActorMismatchError","LinearInstallStateError","LinearIntegrationProviderError","LinearOAuthCallbackError","LinearTokenUnrefreshableError","handleLinearCallback","handleLinearOAuthCallbackError","assertLinearAuthorizationScopes","formatLinearOAuthScopes","LINEAR_OAUTH_SCOPES","signLinearInstallState","verifyLinearInstallState","createLinearTokenStore","linearSecretsNamespace","handleLinearWebhook","getLinearInstallationByOrganizationId","markLinearInstallationRevoked","updateLinearInstallationTokenExpiry","upsertLinearInstallation","withLinearRefreshLock","createLinearE2eRoutes","createLinearIntegrationProvider","options","linear","getInstallationByConnectionId","adapters","agentTools","agent_tools","routes","connectionCapabilities","hasLinearWebhookRoutesOptions","push","provider","displayName","connectionExternalUrl","connection","installation","id","organizationUrlKey","undefined","encodeURIComponent","coreDb","publishIntegrationEventReceived","recordDeliveryOnly","getIntegrationConnectionById"],"mappings":"AAAA,SAAQA,eAAe,QAAO,sCAAsC;AACpE,SAAQC,qBAAqB,QAA6B,iBAAiB;AAC3E,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,wBAAwB,QAAO,gCAAgC;AAEvE,SAAQC,OAAO,EAAEC,EAAE,QAAO,YAAY;AACtC,SAAQC,mCAAmC,QAAO,uBAAuB;AACzE,SAAQC,cAAc,QAAO,oBAAoB;AACjD,SAEEC,6BAA6B,QACxB,kCAAkC;AACzC,SAEEC,yBAAyB,QACpB,mCAAmC;AAI1C,SAAQR,qBAAqB,QAAO,iBAAiB;AAOrD,SACES,sBAAsB,EACtBC,+BAA+B,QAC1B,uBAAuB;AAC9B,SAEEC,4BAA4B,QACvB,sBAAsB;AAC7B,SACEC,6BAA6B,EAC7BC,qCAAqC,EACrCC,kCAAkC,EAClCC,6BAA6B,EAC7BC,oCAAoC,EACpCC,oCAAoC,EACpCC,uBAAuB,EACvBC,8BAA8B,EAC9BC,wBAAwB,EACxBC,6BAA6B,QACxB,kBAAkB;AAEzB,SAAQC,oBAAoB,EAAEC,8BAA8B,QAAO,mBAAmB;AACtF,SACEC,+BAA+B,EAC/BC,uBAAuB,EACvBC,mBAAmB,QACd,kBAAkB;AAEzB,SAAQC,sBAAsB,EAAEC,wBAAwB,QAAO,iBAAiB;AAShF,SACEC,sBAAsB,EACtBC,sBAAsB,QACjB,kBAAkB;AAEzB,SAAQC,mBAAmB,QAAO,mBAAmB;AAOrD,SACE1B,mCAAmC,EACnC2B,qCAAqC,EACrCC,6BAA6B,EAC7BC,mCAAmC,EACnCC,wBAAwB,EACxBC,qBAAqB,QAChB,uBAAuB;AAC9B,SAEEC,qBAAqB,QAChB,mCAAmC;AAC1C,SAAQlC,OAAO,EAAEF,MAAM,EAAEG,EAAE,EAAEE,cAAc,GAAE;AAkB7C,OAAO,SAASgC,gCACdC,UAAkD,CAAC,CAAC;IAEpD,MAAMC,SAASD,QAAQC,MAAM,IAAIxC;IACjC,MAAMyC,gCACJF,QAAQlC,mCAAmC,IAAIA;IACjD,MAAMqC,WAAWH,QAAQI,UAAU,GAC/B;QACEC,aAAa,IAAI1C,yBAAyBqC,QAAQI,UAAU;IAC9D,IACA,CAAC;IAEL,MAAME,SAASN,QAAQM,MAAM,GACzB;QACEtC,8BAA8B;YAC5BiC;YACAM,wBAAwBJ,SAASE,WAAW,GAAG;gBAAC;aAAc,GAAG,EAAE;YACnE,GAAGL,QAAQM,MAAM;QACnB;KACD,GACD,EAAE;IACN,IAAIN,QAAQM,MAAM,IAAIE,8BAA8BR,QAAQM,MAAM,GAAG;QACnEA,OAAOG,IAAI,CAACxC,0BAA0B+B,QAAQM,MAAM;IACtD;IAEA,OAAO;QACLI,UAAUlD;QACVmD,aAAa;QACbR;QACA,MAAMS,uBAAsBC,UAAwB;YAClD,MAAMC,eAAe,MAAMZ,8BAA8BW,WAAWE,EAAE;YACtE,IAAI,CAACD,cAAcE,oBAAoB,OAAOC;YAC9C,OAAO,CAAC,mBAAmB,EAAEC,mBAAmBJ,aAAaE,kBAAkB,EAAE,SAAS,CAAC;QAC7F;QACAV;IACF;AACF;AAEA,SAASE,8BACPF,MAAiD;IAEjD,OACEA,OAAOa,MAAM,KAAKF,aAClBX,OAAOc,+BAA+B,KAAKH,aAC3CX,OAAOe,kBAAkB,KAAKJ,aAC9BX,OAAOgB,4BAA4B,KAAKL;AAE5C"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {LINEAR_PROVIDER} from '@shipfox/api-integration-linear-dto';\nimport {createLinearApiClient, type LinearApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {LinearAgentToolsProvider} from '#core/agent-tools-provider.js';\nimport type {LinearTokenStore} from '#core/tokens.js';\nimport {createLinearWebhookProcessor} from '#core/webhook-processor.js';\nimport {closeDb, db} from '#db/db.js';\nimport {getLinearInstallationByConnectionId} from '#db/installations.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {\n type CreateLinearIntegrationRoutesOptions,\n createLinearIntegrationRoutes,\n} from '#presentation/routes/install.js';\nimport {\n type CreateLinearWebhookRoutesOptions,\n createLinearWebhookRoutes,\n} from '#presentation/routes/webhooks.js';\n\nexport type {LinearProvider} from '@shipfox/api-integration-linear-dto';\nexport type {LinearApiClient, LinearAuthorization, LinearIdentity} from '#api/client.js';\nexport {createLinearApiClient} from '#api/client.js';\nexport type {\n LinearAgentToolCatalogEntry,\n LinearAgentToolCategory,\n LinearAgentToolId,\n LinearAgentToolRequiredScope,\n} from '#core/agent-tools.js';\nexport {\n linearAgentToolCatalog,\n linearAgentToolSelectionCatalog,\n} from '#core/agent-tools.js';\nexport {\n type DisconnectLinearInstallationParams,\n disconnectLinearInstallation,\n} from '#core/disconnect.js';\nexport {\n LinearAccessTokenMissingError,\n LinearAuthorizationScopeMismatchError,\n LinearConnectionAlreadyLinkedError,\n LinearConnectionNotFoundError,\n LinearInstallationAlreadyLinkedError,\n LinearInstallStateActorMismatchError,\n LinearInstallStateError,\n LinearIntegrationProviderError,\n LinearOAuthCallbackError,\n LinearTokenUnrefreshableError,\n} from '#core/errors.js';\nexport type {ConnectLinearInstallationInput, HandleLinearCallbackParams} from '#core/install.js';\nexport {handleLinearCallback, handleLinearOAuthCallbackError} from '#core/install.js';\nexport {\n assertLinearAuthorizationScopes,\n formatLinearOAuthScopes,\n LINEAR_OAUTH_SCOPES,\n} from '#core/scopes.js';\nexport type {LinearInstallStateClaims} from '#core/state.js';\nexport {signLinearInstallState, verifyLinearInstallState} from '#core/state.js';\nexport type {\n CreateLinearTokenStoreParams,\n GetLinearAccessTokenParams,\n LinearConnectionResolverResult,\n LinearSecretsStore,\n LinearTokenStore,\n StoreLinearTokensParams,\n} from '#core/tokens.js';\nexport {\n createLinearTokenStore,\n linearSecretsNamespace,\n} from '#core/tokens.js';\nexport type {HandleLinearWebhookOutcome, HandleLinearWebhookParams} from '#core/webhook.js';\nexport {handleLinearWebhook} from '#core/webhook.js';\nexport type {\n CreateLinearWebhookProcessorOptions,\n LinearWebhookProcessor,\n} from '#core/webhook-processor.js';\nexport {createLinearWebhookProcessor} from '#core/webhook-processor.js';\nexport type {\n LinearInstallation,\n LinearInstallationStatus,\n UpdateLinearInstallationTokenExpiryParams,\n UpsertLinearInstallationParams,\n} from '#db/installations.js';\nexport {\n deleteLinearInstallationByConnectionId,\n getLinearInstallationByConnectionId,\n getLinearInstallationByOrganizationId,\n markLinearInstallationRevoked,\n updateLinearInstallationTokenExpiry,\n upsertLinearInstallation,\n withLinearRefreshLock,\n} from '#db/installations.js';\nexport {\n type CreateLinearE2eRoutesOptions,\n createLinearE2eRoutes,\n} from '#presentation/e2eRoutes/index.js';\nexport {closeDb, config, db, migrationsPath};\n\nexport interface CreateLinearIntegrationProviderOptions {\n linear?: LinearApiClient | undefined;\n agentTools?:\n | {\n tokenStore: Pick<LinearTokenStore, 'getAccessToken'>;\n endpoint?: string | URL | undefined;\n callTimeoutMs?: number | undefined;\n }\n | undefined;\n getLinearInstallationByConnectionId?: typeof getLinearInstallationByConnectionId | undefined;\n cleanup?:\n | {\n deleteConnectionRecords?: (\n connection: {id: string},\n options: {tx: unknown},\n ) => Promise<void>;\n deleteConnectionSecrets?: (connection: {id: string; workspaceId: string}) => Promise<void>;\n }\n | undefined;\n routes?:\n | (Omit<CreateLinearIntegrationRoutesOptions, 'linear' | 'connectionCapabilities'> &\n Partial<CreateLinearWebhookRoutesOptions>)\n | undefined;\n}\n\nexport function createLinearIntegrationProvider(\n options: CreateLinearIntegrationProviderOptions = {},\n) {\n const linear = options.linear ?? createLinearApiClient();\n const getInstallationByConnectionId =\n options.getLinearInstallationByConnectionId ?? getLinearInstallationByConnectionId;\n const adapters = options.agentTools\n ? {\n agent_tools: new LinearAgentToolsProvider(options.agentTools),\n }\n : {};\n const webhookProcessor =\n options.routes && hasLinearWebhookRoutesOptions(options.routes)\n ? createLinearWebhookProcessor(options.routes)\n : undefined;\n\n const routes = options.routes\n ? [\n createLinearIntegrationRoutes({\n linear,\n connectionCapabilities: adapters.agent_tools ? ['agent_tools'] : [],\n ...options.routes,\n }),\n ]\n : [];\n if (options.routes && hasLinearWebhookRoutesOptions(options.routes) && webhookProcessor) {\n routes.push(createLinearWebhookRoutes({...options.routes, processor: webhookProcessor}));\n }\n\n return {\n provider: LINEAR_PROVIDER,\n displayName: 'Linear',\n adapters,\n async connectionExternalUrl(connection: {id: string}): Promise<string | undefined> {\n const installation = await getInstallationByConnectionId(connection.id);\n if (!installation?.organizationUrlKey) return undefined;\n return `https://linear.app/${encodeURIComponent(installation.organizationUrlKey)}/settings`;\n },\n ...options.cleanup,\n routes,\n webhookProcessors: webhookProcessor\n ? [{routeIds: ['linear'] as const, processor: webhookProcessor}]\n : undefined,\n };\n}\n\nfunction hasLinearWebhookRoutesOptions(\n routes: Partial<CreateLinearWebhookRoutesOptions>,\n): routes is CreateLinearWebhookRoutesOptions {\n return (\n routes.coreDb !== undefined &&\n routes.publishIntegrationEventReceived !== undefined &&\n routes.recordDeliveryOnly !== undefined &&\n routes.getIntegrationConnectionById !== undefined\n );\n}\n"],"names":["LINEAR_PROVIDER","createLinearApiClient","config","LinearAgentToolsProvider","createLinearWebhookProcessor","closeDb","db","getLinearInstallationByConnectionId","migrationsPath","createLinearIntegrationRoutes","createLinearWebhookRoutes","linearAgentToolCatalog","linearAgentToolSelectionCatalog","disconnectLinearInstallation","LinearAccessTokenMissingError","LinearAuthorizationScopeMismatchError","LinearConnectionAlreadyLinkedError","LinearConnectionNotFoundError","LinearInstallationAlreadyLinkedError","LinearInstallStateActorMismatchError","LinearInstallStateError","LinearIntegrationProviderError","LinearOAuthCallbackError","LinearTokenUnrefreshableError","handleLinearCallback","handleLinearOAuthCallbackError","assertLinearAuthorizationScopes","formatLinearOAuthScopes","LINEAR_OAUTH_SCOPES","signLinearInstallState","verifyLinearInstallState","createLinearTokenStore","linearSecretsNamespace","handleLinearWebhook","deleteLinearInstallationByConnectionId","getLinearInstallationByOrganizationId","markLinearInstallationRevoked","updateLinearInstallationTokenExpiry","upsertLinearInstallation","withLinearRefreshLock","createLinearE2eRoutes","createLinearIntegrationProvider","options","linear","getInstallationByConnectionId","adapters","agentTools","agent_tools","webhookProcessor","routes","hasLinearWebhookRoutesOptions","undefined","connectionCapabilities","push","processor","provider","displayName","connectionExternalUrl","connection","installation","id","organizationUrlKey","encodeURIComponent","cleanup","webhookProcessors","routeIds","coreDb","publishIntegrationEventReceived","recordDeliveryOnly","getIntegrationConnectionById"],"mappings":"AAAA,SAAQA,eAAe,QAAO,sCAAsC;AACpE,SAAQC,qBAAqB,QAA6B,iBAAiB;AAC3E,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,wBAAwB,QAAO,gCAAgC;AAEvE,SAAQC,4BAA4B,QAAO,6BAA6B;AACxE,SAAQC,OAAO,EAAEC,EAAE,QAAO,YAAY;AACtC,SAAQC,mCAAmC,QAAO,uBAAuB;AACzE,SAAQC,cAAc,QAAO,oBAAoB;AACjD,SAEEC,6BAA6B,QACxB,kCAAkC;AACzC,SAEEC,yBAAyB,QACpB,mCAAmC;AAI1C,SAAQT,qBAAqB,QAAO,iBAAiB;AAOrD,SACEU,sBAAsB,EACtBC,+BAA+B,QAC1B,uBAAuB;AAC9B,SAEEC,4BAA4B,QACvB,sBAAsB;AAC7B,SACEC,6BAA6B,EAC7BC,qCAAqC,EACrCC,kCAAkC,EAClCC,6BAA6B,EAC7BC,oCAAoC,EACpCC,oCAAoC,EACpCC,uBAAuB,EACvBC,8BAA8B,EAC9BC,wBAAwB,EACxBC,6BAA6B,QACxB,kBAAkB;AAEzB,SAAQC,oBAAoB,EAAEC,8BAA8B,QAAO,mBAAmB;AACtF,SACEC,+BAA+B,EAC/BC,uBAAuB,EACvBC,mBAAmB,QACd,kBAAkB;AAEzB,SAAQC,sBAAsB,EAAEC,wBAAwB,QAAO,iBAAiB;AAShF,SACEC,sBAAsB,EACtBC,sBAAsB,QACjB,kBAAkB;AAEzB,SAAQC,mBAAmB,QAAO,mBAAmB;AAKrD,SAAQ7B,4BAA4B,QAAO,6BAA6B;AAOxE,SACE8B,sCAAsC,EACtC3B,mCAAmC,EACnC4B,qCAAqC,EACrCC,6BAA6B,EAC7BC,mCAAmC,EACnCC,wBAAwB,EACxBC,qBAAqB,QAChB,uBAAuB;AAC9B,SAEEC,qBAAqB,QAChB,mCAAmC;AAC1C,SAAQnC,OAAO,EAAEH,MAAM,EAAEI,EAAE,EAAEE,cAAc,GAAE;AA2B7C,OAAO,SAASiC,gCACdC,UAAkD,CAAC,CAAC;IAEpD,MAAMC,SAASD,QAAQC,MAAM,IAAI1C;IACjC,MAAM2C,gCACJF,QAAQnC,mCAAmC,IAAIA;IACjD,MAAMsC,WAAWH,QAAQI,UAAU,GAC/B;QACEC,aAAa,IAAI5C,yBAAyBuC,QAAQI,UAAU;IAC9D,IACA,CAAC;IACL,MAAME,mBACJN,QAAQO,MAAM,IAAIC,8BAA8BR,QAAQO,MAAM,IAC1D7C,6BAA6BsC,QAAQO,MAAM,IAC3CE;IAEN,MAAMF,SAASP,QAAQO,MAAM,GACzB;QACExC,8BAA8B;YAC5BkC;YACAS,wBAAwBP,SAASE,WAAW,GAAG;gBAAC;aAAc,GAAG,EAAE;YACnE,GAAGL,QAAQO,MAAM;QACnB;KACD,GACD,EAAE;IACN,IAAIP,QAAQO,MAAM,IAAIC,8BAA8BR,QAAQO,MAAM,KAAKD,kBAAkB;QACvFC,OAAOI,IAAI,CAAC3C,0BAA0B;YAAC,GAAGgC,QAAQO,MAAM;YAAEK,WAAWN;QAAgB;IACvF;IAEA,OAAO;QACLO,UAAUvD;QACVwD,aAAa;QACbX;QACA,MAAMY,uBAAsBC,UAAwB;YAClD,MAAMC,eAAe,MAAMf,8BAA8Bc,WAAWE,EAAE;YACtE,IAAI,CAACD,cAAcE,oBAAoB,OAAOV;YAC9C,OAAO,CAAC,mBAAmB,EAAEW,mBAAmBH,aAAaE,kBAAkB,EAAE,SAAS,CAAC;QAC7F;QACA,GAAGnB,QAAQqB,OAAO;QAClBd;QACAe,mBAAmBhB,mBACf;YAAC;gBAACiB,UAAU;oBAAC;iBAAS;gBAAWX,WAAWN;YAAgB;SAAE,GAC9DG;IACN;AACF;AAEA,SAASD,8BACPD,MAAiD;IAEjD,OACEA,OAAOiB,MAAM,KAAKf,aAClBF,OAAOkB,+BAA+B,KAAKhB,aAC3CF,OAAOmB,kBAAkB,KAAKjB,aAC9BF,OAAOoB,4BAA4B,KAAKlB;AAE5C"}
|
|
@@ -14,6 +14,11 @@ export interface CreateLinearIntegrationRoutesOptions {
|
|
|
14
14
|
connectionId: string;
|
|
15
15
|
}) => Promise<void>;
|
|
16
16
|
connectionCapabilities: IntegrationCapability[];
|
|
17
|
+
requireActiveWorkspaceMembership?: (input: {
|
|
18
|
+
workspaceId: string;
|
|
19
|
+
userId: string;
|
|
20
|
+
memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;
|
|
21
|
+
}) => Promise<unknown>;
|
|
17
22
|
}
|
|
18
|
-
export declare function createLinearIntegrationRoutes({ linear, tokenStore, getExistingLinearConnection, connectLinearInstallation, disconnectLinearInstallation, connectionCapabilities, }: CreateLinearIntegrationRoutesOptions): RouteGroup;
|
|
23
|
+
export declare function createLinearIntegrationRoutes({ linear, tokenStore, getExistingLinearConnection, connectLinearInstallation, disconnectLinearInstallation, connectionCapabilities, requireActiveWorkspaceMembership, }: CreateLinearIntegrationRoutesOptions): RouteGroup;
|
|
19
24
|
//# sourceMappingURL=install.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;AAQpG,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EACL,KAAK,8BAA8B,EAGpC,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAItD,MAAM,WAAW,oCAAoC;IACnD,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;IAClD,2BAA2B,EAAE,CAAC,KAAK,EAAE;QACnC,cAAc,EAAE,MAAM,CAAC;KACxB,KAAK,OAAO,CAAC,qBAAqB,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC,CAAC;IAC3D,yBAAyB,EAAE,CACzB,KAAK,EAAE,8BAA8B,KAClC,OAAO,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9C,4BAA4B,EAAE,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD,gCAAgC,CAAC,EAAE,CAAC,KAAK,EAAE;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,CAAC,CAAC;KACvF,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,MAAM,EACN,UAAU,EACV,2BAA2B,EAC3B,yBAAyB,EACzB,4BAA4B,EAC5B,sBAAsB,EACtB,gCAAgC,GACjC,EAAE,oCAAoC,GAAG,UAAU,CA+EnD"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AUTH_USER, requireUserContext, requireWorkspaceAccess } from '@shipfox/api-auth-context';
|
|
2
2
|
import { createLinearInstallBodySchema, createLinearInstallResponseSchema, linearCallbackQuerySchema, linearCallbackResponseSchema } from '@shipfox/api-integration-linear-dto';
|
|
3
|
-
import { requireWorkspaceMembership } from '@shipfox/api-workspaces';
|
|
4
3
|
import { defineRoute } from '@shipfox/node-fastify';
|
|
5
4
|
import { config } from '#config.js';
|
|
6
5
|
import { handleLinearCallback, handleLinearOAuthCallbackError } from '#core/install.js';
|
|
@@ -8,7 +7,7 @@ import { formatLinearOAuthScopes } from '#core/scopes.js';
|
|
|
8
7
|
import { signLinearInstallState } from '#core/state.js';
|
|
9
8
|
import { toIntegrationConnectionDto } from '#presentation/dto/integrations.js';
|
|
10
9
|
import { linearRouteErrorHandler } from './errors.js';
|
|
11
|
-
export function createLinearIntegrationRoutes({ linear, tokenStore, getExistingLinearConnection, connectLinearInstallation, disconnectLinearInstallation, connectionCapabilities }) {
|
|
10
|
+
export function createLinearIntegrationRoutes({ linear, tokenStore, getExistingLinearConnection, connectLinearInstallation, disconnectLinearInstallation, connectionCapabilities, requireActiveWorkspaceMembership }) {
|
|
12
11
|
const createInstallRoute = defineRoute({
|
|
13
12
|
method: 'POST',
|
|
14
13
|
path: '/install',
|
|
@@ -65,7 +64,7 @@ export function createLinearIntegrationRoutes({ linear, tokenStore, getExistingL
|
|
|
65
64
|
errorDescription: query.error_description,
|
|
66
65
|
sessionUserId: actor.userId,
|
|
67
66
|
sessionMemberships: actor.memberships,
|
|
68
|
-
requireWorkspaceMembership
|
|
67
|
+
requireWorkspaceMembership: requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck
|
|
69
68
|
});
|
|
70
69
|
}
|
|
71
70
|
const connection = await handleLinearCallback({
|
|
@@ -75,7 +74,7 @@ export function createLinearIntegrationRoutes({ linear, tokenStore, getExistingL
|
|
|
75
74
|
state: query.state,
|
|
76
75
|
sessionUserId: actor.userId,
|
|
77
76
|
sessionMemberships: actor.memberships,
|
|
78
|
-
requireWorkspaceMembership,
|
|
77
|
+
requireWorkspaceMembership: requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,
|
|
79
78
|
getExistingLinearConnection,
|
|
80
79
|
connectLinearInstallation,
|
|
81
80
|
disconnectLinearInstallation
|
|
@@ -93,6 +92,9 @@ export function createLinearIntegrationRoutes({ linear, tokenStore, getExistingL
|
|
|
93
92
|
]
|
|
94
93
|
};
|
|
95
94
|
}
|
|
95
|
+
function unavailableWorkspaceMembershipCheck(_input) {
|
|
96
|
+
return Promise.reject(new Error('Workspaces inter-module client is not configured'));
|
|
97
|
+
}
|
|
96
98
|
function isLinearOAuthErrorCallback(query) {
|
|
97
99
|
return 'error' in query && typeof query.error === 'string';
|
|
98
100
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {\n createLinearInstallBodySchema,\n createLinearInstallResponseSchema,\n type LinearCallbackQueryDto,\n linearCallbackQuerySchema,\n linearCallbackResponseSchema,\n} from '@shipfox/api-integration-linear-dto';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {\n createLinearInstallBodySchema,\n createLinearInstallResponseSchema,\n type LinearCallbackQueryDto,\n linearCallbackQuerySchema,\n linearCallbackResponseSchema,\n} from '@shipfox/api-integration-linear-dto';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {LinearApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {\n type ConnectLinearInstallationInput,\n handleLinearCallback,\n handleLinearOAuthCallbackError,\n} from '#core/install.js';\nimport {formatLinearOAuthScopes} from '#core/scopes.js';\nimport {signLinearInstallState} from '#core/state.js';\nimport type {LinearTokenStore} from '#core/tokens.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\nimport {linearRouteErrorHandler} from './errors.js';\n\nexport interface CreateLinearIntegrationRoutesOptions {\n linear: LinearApiClient;\n tokenStore: Pick<LinearTokenStore, 'storeTokens'>;\n getExistingLinearConnection: (input: {\n organizationId: string;\n }) => Promise<IntegrationConnection<'linear'> | undefined>;\n connectLinearInstallation: (\n input: ConnectLinearInstallationInput,\n ) => Promise<IntegrationConnection<'linear'>>;\n disconnectLinearInstallation: (input: {connectionId: string}) => Promise<void>;\n connectionCapabilities: IntegrationCapability[];\n requireActiveWorkspaceMembership?: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n }) => Promise<unknown>;\n}\n\nexport function createLinearIntegrationRoutes({\n linear,\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n connectionCapabilities,\n requireActiveWorkspaceMembership,\n}: CreateLinearIntegrationRoutesOptions): RouteGroup {\n const createInstallRoute = defineRoute({\n method: 'POST',\n path: '/install',\n auth: AUTH_USER,\n description: 'Create a Linear OAuth authorization URL for a workspace.',\n schema: {\n body: createLinearInstallBodySchema,\n response: {\n 200: createLinearInstallResponseSchema,\n },\n },\n handler: (request) => {\n const {workspace_id: workspaceId} = request.body;\n const actor = requireUserContext(request);\n\n requireWorkspaceAccess({request, workspaceId});\n\n const state = signLinearInstallState({workspaceId, userId: actor.userId});\n const installUrl = new URL('https://linear.app/oauth/authorize');\n installUrl.searchParams.set('client_id', config.LINEAR_OAUTH_CLIENT_ID);\n installUrl.searchParams.set('redirect_uri', config.LINEAR_OAUTH_REDIRECT_URL);\n installUrl.searchParams.set('response_type', 'code');\n installUrl.searchParams.set('state', state);\n installUrl.searchParams.set('actor', 'app');\n installUrl.searchParams.set('scope', formatLinearOAuthScopes());\n\n return {install_url: installUrl.toString()};\n },\n });\n\n const callbackApiRoute = defineRoute({\n method: 'GET',\n path: '/callback/api',\n auth: AUTH_USER,\n description: 'Handle the Linear OAuth callback.',\n schema: {\n querystring: linearCallbackQuerySchema,\n response: {\n 200: linearCallbackResponseSchema,\n },\n },\n errorHandler: linearRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const query = request.query;\n if (isLinearOAuthErrorCallback(query)) {\n return await handleLinearOAuthCallbackError({\n state: query.state,\n error: query.error,\n errorDescription: query.error_description,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership:\n requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,\n });\n }\n const connection = await handleLinearCallback({\n linear,\n tokenStore,\n code: query.code,\n state: query.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership:\n requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n });\n\n return toIntegrationConnectionDto(connection, {capabilities: connectionCapabilities});\n },\n });\n\n return {\n prefix: '/integrations/linear',\n routes: [createInstallRoute, callbackApiRoute],\n };\n}\n\nfunction unavailableWorkspaceMembershipCheck(_input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n}): Promise<never> {\n return Promise.reject(new Error('Workspaces inter-module client is not configured'));\n}\n\nfunction isLinearOAuthErrorCallback(\n query: LinearCallbackQueryDto,\n): query is LinearCallbackQueryDto & {\n error: string;\n error_description?: string | undefined;\n state: string;\n} {\n return 'error' in query && typeof query.error === 'string';\n}\n"],"names":["AUTH_USER","requireUserContext","requireWorkspaceAccess","createLinearInstallBodySchema","createLinearInstallResponseSchema","linearCallbackQuerySchema","linearCallbackResponseSchema","defineRoute","config","handleLinearCallback","handleLinearOAuthCallbackError","formatLinearOAuthScopes","signLinearInstallState","toIntegrationConnectionDto","linearRouteErrorHandler","createLinearIntegrationRoutes","linear","tokenStore","getExistingLinearConnection","connectLinearInstallation","disconnectLinearInstallation","connectionCapabilities","requireActiveWorkspaceMembership","createInstallRoute","method","path","auth","description","schema","body","response","handler","request","workspace_id","workspaceId","actor","state","userId","installUrl","URL","searchParams","set","LINEAR_OAUTH_CLIENT_ID","LINEAR_OAUTH_REDIRECT_URL","install_url","toString","callbackApiRoute","querystring","errorHandler","query","isLinearOAuthErrorCallback","error","errorDescription","error_description","sessionUserId","sessionMemberships","memberships","requireWorkspaceMembership","unavailableWorkspaceMembershipCheck","connection","code","capabilities","prefix","routes","_input","Promise","reject","Error"],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { GetIntegrationConnectionByIdFn, PublishIntegrationEventReceivedFn, RecordDeliveryOnlyFn } from '@shipfox/api-integration-core-dto';
|
|
2
2
|
import { type RouteGroup } from '@shipfox/node-fastify';
|
|
3
3
|
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
4
|
+
import { type LinearWebhookProcessor } from '#core/webhook-processor.js';
|
|
4
5
|
export interface CreateLinearWebhookRoutesOptions {
|
|
5
6
|
coreDb: () => NodePgDatabase<Record<string, unknown>>;
|
|
6
7
|
publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
|
|
7
8
|
recordDeliveryOnly: RecordDeliveryOnlyFn;
|
|
8
9
|
getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
|
|
10
|
+
processor?: LinearWebhookProcessor | undefined;
|
|
9
11
|
}
|
|
10
12
|
export declare function createLinearWebhookRoutes(options: CreateLinearWebhookRoutesOptions): RouteGroup;
|
|
11
13
|
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,8BAA8B,
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,8BAA8B,EAC9B,iCAAiC,EACjC,oBAAoB,EAGrB,MAAM,mCAAmC,CAAC;AAK3C,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,4BAA4B,CAAC;AAMpC,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,SAAS,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;CAChD;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gCAAgC,GAAG,UAAU,CAqD/F"}
|