@shipfox/api-integration-webhook 7.1.0 → 9.0.1

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,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 7 files with swc (417.6ms)
2
+ Successfully compiled: 7 files with swc (262.85ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # @shipfox/api-integration-webhook
2
2
 
3
+ ## 9.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 475ce59: Republishes all public packages after restoring release authorization.
8
+ - Updated dependencies [475ce59]
9
+ - @shipfox/api-auth-context@9.0.1
10
+ - @shipfox/api-integration-spi@0.2.1
11
+ - @shipfox/api-integration-webhook-dto@9.0.1
12
+ - @shipfox/config@1.2.3
13
+ - @shipfox/node-fastify@0.3.1
14
+ - @shipfox/node-opentelemetry@0.6.1
15
+
16
+ ## 9.0.0
17
+
18
+ ### Patch Changes
19
+
20
+ - 4a6d124: Separates Integrations provider SPI contracts from the public DTO surface.
21
+ - Updated dependencies [4a6d124]
22
+ - @shipfox/api-integration-spi@0.2.0
23
+ - @shipfox/api-auth-context@9.0.0
24
+ - @shipfox/api-integration-webhook-dto@9.0.0
25
+ - @shipfox/config@1.2.2
26
+ - @shipfox/node-fastify@0.3.0
27
+ - @shipfox/node-opentelemetry@0.6.0
28
+
29
+ ## 8.0.0
30
+
31
+ ### Patch Changes
32
+
33
+ - Updated dependencies [7f227c6]
34
+ - @shipfox/api-integration-core-dto@8.0.0
35
+ - @shipfox/api-integration-webhook-dto@8.0.0
36
+
3
37
  ## 7.1.0
4
38
 
5
39
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- import { type GetIntegrationConnectionByIdFn, type IntegrationConnection, type IntegrationTx, type PublishIntegrationEventReceivedFn, type StoredWebhookRequest, type WebhookProcessingResult } from '@shipfox/api-integration-core-dto';
1
+ import { type GetIntegrationConnectionByIdFn, type IntegrationConnection, type IntegrationTx, type PublishIntegrationEventReceivedFn, type StoredWebhookRequest, type WebhookProcessingResult } from '@shipfox/api-integration-spi';
2
2
  export interface CreateGenericWebhookProcessorOptions {
3
3
  coreDb: () => {
4
4
  transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;
@@ -1 +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,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,iCAAiC,EACtC,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAC;AAO3C,MAAM,WAAW,oCAAoC;IACnD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,+BAA+B,EAAE,iCAAiC,CAAC;CACpE;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,CACL,OAAO,EAAE,oBAAoB,EAC7B,UAAU,CAAC,EAAE,qBAAqB,GAAG,SAAS,GAC7C,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACrC;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,uBAAuB,CAIzB"}
1
+ {"version":3,"file":"webhook-processor.d.ts","sourceRoot":"","sources":["../../src/core/webhook-processor.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,8BAA8B,EACnC,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,iCAAiC,EACtC,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,8BAA8B,CAAC;AAOtC,MAAM,WAAW,oCAAoC;IACnD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,+BAA+B,EAAE,iCAAiC,CAAC;CACpE;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,CACL,OAAO,EAAE,oBAAoB,EAC7B,UAAU,CAAC,EAAE,qBAAqB,GAAG,SAAS,GAC7C,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACrC;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,uBAAuB,CAIzB"}
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import { decodeWebhookBody } from '@shipfox/api-integration-core-dto';
2
+ import { decodeWebhookBody } from '@shipfox/api-integration-spi';
3
3
  import { WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT } from '@shipfox/api-integration-webhook-dto';
4
4
  import { logger } from '@shipfox/node-opentelemetry';
5
5
  import { redactHeaders, WEBHOOK_ACCEPTED_CONTENT_TYPES } from '#constants.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/webhook-processor.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {\n decodeWebhookBody,\n type GetIntegrationConnectionByIdFn,\n type IntegrationConnection,\n type IntegrationTx,\n type PublishIntegrationEventReceivedFn,\n type StoredWebhookRequest,\n type WebhookProcessingResult,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT} from '@shipfox/api-integration-webhook-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {redactHeaders, WEBHOOK_ACCEPTED_CONTENT_TYPES} from '#constants.js';\n\nconst [JSON_CONTENT_TYPE, FORM_CONTENT_TYPE, TEXT_CONTENT_TYPE] = WEBHOOK_ACCEPTED_CONTENT_TYPES;\n\nexport interface CreateGenericWebhookProcessorOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n}\n\nexport interface GenericWebhookProcessor {\n process(\n request: StoredWebhookRequest,\n connection?: IntegrationConnection | undefined,\n ): Promise<WebhookProcessingResult>;\n}\n\nexport function createGenericWebhookProcessor(\n options: CreateGenericWebhookProcessorOptions,\n): GenericWebhookProcessor {\n return {\n process: (request, connection) => processWebhookRequest(options, request, connection),\n };\n}\n\nasync function processWebhookRequest(\n options: CreateGenericWebhookProcessorOptions,\n request: StoredWebhookRequest,\n knownConnection?: IntegrationConnection | undefined,\n): Promise<WebhookProcessingResult> {\n if (request.route_id !== 'webhook.connection') {\n throw new Error(`Generic webhook processor cannot process ${request.route_id} requests`);\n }\n\n const connection =\n knownConnection ??\n (await options.getIntegrationConnectionById(request.path_parameters.connection_id));\n if (\n !connection ||\n connection.provider !== WEBHOOK_PROVIDER ||\n connection.lifecycleStatus !== 'active'\n ) {\n return {outcome: 'discarded', reason: 'connection_unavailable'};\n }\n\n const deliveryId = request.headers['x-delivery-id'] ?? request.request_id;\n const body = parseWebhookBody(request);\n if (!body.success) {\n logger().warn({deliveryId, err: body.error}, 'generic webhook payload parsing failed');\n return {outcome: 'discarded', reason: 'malformed_payload', deliveryId};\n }\n\n await options.coreDb().transaction(async (tx) => {\n await options.publishIntegrationEventReceived({\n tx,\n event: {\n provider: WEBHOOK_PROVIDER,\n source: connection.slug,\n event: WEBHOOK_RECEIVED_EVENT,\n workspaceId: connection.workspaceId,\n connectionId: connection.id,\n connectionName: connection.displayName,\n deliveryId,\n receivedAt: request.received_at,\n payload: {\n method: request.method,\n headers: redactHeaders(request.headers),\n query: parseWebhookQuery(request.raw_query_string),\n body: body.value,\n },\n },\n });\n });\n\n return {outcome: 'processed', deliveryId};\n}\n\nfunction parseWebhookQuery(rawQueryString: string): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n\n for (const [name, value] of new URLSearchParams(rawQueryString)) {\n const existingValue = query[name];\n if (existingValue === undefined) {\n query[name] = value;\n } else if (Array.isArray(existingValue)) {\n existingValue.push(value);\n } else {\n query[name] = [existingValue, value];\n }\n }\n\n return query;\n}\n\nfunction parseWebhookBody(\n request: StoredWebhookRequest,\n): {success: true; value: unknown} | {success: false; error?: unknown} {\n const body = Buffer.from(decodeWebhookBody(request.body));\n const contentType = request.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase();\n\n if (contentType === JSON_CONTENT_TYPE) {\n try {\n return {success: true, value: JSON.parse(body.toString('utf8'))};\n } catch (error) {\n return {success: false, error};\n }\n }\n if (contentType === FORM_CONTENT_TYPE) {\n return {success: true, value: Object.fromEntries(new URLSearchParams(body.toString('utf8')))};\n }\n if (contentType === TEXT_CONTENT_TYPE) return {success: true, value: body.toString('utf8')};\n\n return {success: false};\n}\n"],"names":["Buffer","decodeWebhookBody","WEBHOOK_PROVIDER","WEBHOOK_RECEIVED_EVENT","logger","redactHeaders","WEBHOOK_ACCEPTED_CONTENT_TYPES","JSON_CONTENT_TYPE","FORM_CONTENT_TYPE","TEXT_CONTENT_TYPE","createGenericWebhookProcessor","options","process","request","connection","processWebhookRequest","knownConnection","route_id","Error","getIntegrationConnectionById","path_parameters","connection_id","provider","lifecycleStatus","outcome","reason","deliveryId","headers","request_id","body","parseWebhookBody","success","warn","err","error","coreDb","transaction","tx","publishIntegrationEventReceived","event","source","slug","workspaceId","connectionId","id","connectionName","displayName","receivedAt","received_at","payload","method","query","parseWebhookQuery","raw_query_string","value","rawQueryString","name","URLSearchParams","existingValue","undefined","Array","isArray","push","from","contentType","split","trim","toLowerCase","JSON","parse","toString","Object","fromEntries"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/webhook-processor.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {\n decodeWebhookBody,\n type GetIntegrationConnectionByIdFn,\n type IntegrationConnection,\n type IntegrationTx,\n type PublishIntegrationEventReceivedFn,\n type StoredWebhookRequest,\n type WebhookProcessingResult,\n} from '@shipfox/api-integration-spi';\nimport {WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT} from '@shipfox/api-integration-webhook-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {redactHeaders, WEBHOOK_ACCEPTED_CONTENT_TYPES} from '#constants.js';\n\nconst [JSON_CONTENT_TYPE, FORM_CONTENT_TYPE, TEXT_CONTENT_TYPE] = WEBHOOK_ACCEPTED_CONTENT_TYPES;\n\nexport interface CreateGenericWebhookProcessorOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n}\n\nexport interface GenericWebhookProcessor {\n process(\n request: StoredWebhookRequest,\n connection?: IntegrationConnection | undefined,\n ): Promise<WebhookProcessingResult>;\n}\n\nexport function createGenericWebhookProcessor(\n options: CreateGenericWebhookProcessorOptions,\n): GenericWebhookProcessor {\n return {\n process: (request, connection) => processWebhookRequest(options, request, connection),\n };\n}\n\nasync function processWebhookRequest(\n options: CreateGenericWebhookProcessorOptions,\n request: StoredWebhookRequest,\n knownConnection?: IntegrationConnection | undefined,\n): Promise<WebhookProcessingResult> {\n if (request.route_id !== 'webhook.connection') {\n throw new Error(`Generic webhook processor cannot process ${request.route_id} requests`);\n }\n\n const connection =\n knownConnection ??\n (await options.getIntegrationConnectionById(request.path_parameters.connection_id));\n if (\n !connection ||\n connection.provider !== WEBHOOK_PROVIDER ||\n connection.lifecycleStatus !== 'active'\n ) {\n return {outcome: 'discarded', reason: 'connection_unavailable'};\n }\n\n const deliveryId = request.headers['x-delivery-id'] ?? request.request_id;\n const body = parseWebhookBody(request);\n if (!body.success) {\n logger().warn({deliveryId, err: body.error}, 'generic webhook payload parsing failed');\n return {outcome: 'discarded', reason: 'malformed_payload', deliveryId};\n }\n\n await options.coreDb().transaction(async (tx) => {\n await options.publishIntegrationEventReceived({\n tx,\n event: {\n provider: WEBHOOK_PROVIDER,\n source: connection.slug,\n event: WEBHOOK_RECEIVED_EVENT,\n workspaceId: connection.workspaceId,\n connectionId: connection.id,\n connectionName: connection.displayName,\n deliveryId,\n receivedAt: request.received_at,\n payload: {\n method: request.method,\n headers: redactHeaders(request.headers),\n query: parseWebhookQuery(request.raw_query_string),\n body: body.value,\n },\n },\n });\n });\n\n return {outcome: 'processed', deliveryId};\n}\n\nfunction parseWebhookQuery(rawQueryString: string): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n\n for (const [name, value] of new URLSearchParams(rawQueryString)) {\n const existingValue = query[name];\n if (existingValue === undefined) {\n query[name] = value;\n } else if (Array.isArray(existingValue)) {\n existingValue.push(value);\n } else {\n query[name] = [existingValue, value];\n }\n }\n\n return query;\n}\n\nfunction parseWebhookBody(\n request: StoredWebhookRequest,\n): {success: true; value: unknown} | {success: false; error?: unknown} {\n const body = Buffer.from(decodeWebhookBody(request.body));\n const contentType = request.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase();\n\n if (contentType === JSON_CONTENT_TYPE) {\n try {\n return {success: true, value: JSON.parse(body.toString('utf8'))};\n } catch (error) {\n return {success: false, error};\n }\n }\n if (contentType === FORM_CONTENT_TYPE) {\n return {success: true, value: Object.fromEntries(new URLSearchParams(body.toString('utf8')))};\n }\n if (contentType === TEXT_CONTENT_TYPE) return {success: true, value: body.toString('utf8')};\n\n return {success: false};\n}\n"],"names":["Buffer","decodeWebhookBody","WEBHOOK_PROVIDER","WEBHOOK_RECEIVED_EVENT","logger","redactHeaders","WEBHOOK_ACCEPTED_CONTENT_TYPES","JSON_CONTENT_TYPE","FORM_CONTENT_TYPE","TEXT_CONTENT_TYPE","createGenericWebhookProcessor","options","process","request","connection","processWebhookRequest","knownConnection","route_id","Error","getIntegrationConnectionById","path_parameters","connection_id","provider","lifecycleStatus","outcome","reason","deliveryId","headers","request_id","body","parseWebhookBody","success","warn","err","error","coreDb","transaction","tx","publishIntegrationEventReceived","event","source","slug","workspaceId","connectionId","id","connectionName","displayName","receivedAt","received_at","payload","method","query","parseWebhookQuery","raw_query_string","value","rawQueryString","name","URLSearchParams","existingValue","undefined","Array","isArray","push","from","contentType","split","trim","toLowerCase","JSON","parse","toString","Object","fromEntries"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CreateIntegrationConnectionFn, DeleteIntegrationConnectionFn, GetIntegrationConnectionByIdFn, IntegrationConnection, IntegrationTx, PublishIntegrationEventReceivedFn, UpdateIntegrationConnectionLifecycleStatusFn } from '@shipfox/api-integration-core-dto';
1
+ import type { CreateIntegrationConnectionFn, DeleteIntegrationConnectionFn, GetIntegrationConnectionByIdFn, IntegrationConnection, IntegrationTx, PublishIntegrationEventReceivedFn, UpdateIntegrationConnectionLifecycleStatusFn } from '@shipfox/api-integration-spi';
2
2
  export { redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT } from '#constants.js';
3
3
  export type { CreateGenericWebhookProcessorOptions, GenericWebhookProcessor, } from '#core/webhook-processor.js';
4
4
  export { createGenericWebhookProcessor } from '#core/webhook-processor.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,6BAA6B,EAC7B,8BAA8B,EAC9B,qBAAqB,EACrB,aAAa,EACb,iCAAiC,EACjC,4CAA4C,EAC7C,MAAM,mCAAmC,CAAC;AAO3C,OAAO,EAAC,aAAa,EAAE,yBAAyB,EAAE,0BAA0B,EAAC,MAAM,eAAe,CAAC;AACnG,YAAY,EACV,oCAAoC,EACpC,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAC,6BAA6B,EAAC,MAAM,4BAA4B,CAAC;AAEzE,MAAM,WAAW,uCAAuC;IACtD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,2BAA2B,EAAE,6BAA6B,CAAC;IAC3D,0BAA0B,EAAE,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAChG,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,0CAA0C,EAAE,4CAA4C,CAAC;IACzF,2BAA2B,EAAE,6BAA6B,CAAC;IAC3D,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,uCAAuC;;;;;;;;EAyBhG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,6BAA6B,EAC7B,8BAA8B,EAC9B,qBAAqB,EACrB,aAAa,EACb,iCAAiC,EACjC,4CAA4C,EAC7C,MAAM,8BAA8B,CAAC;AAOtC,OAAO,EAAC,aAAa,EAAE,yBAAyB,EAAE,0BAA0B,EAAC,MAAM,eAAe,CAAC;AACnG,YAAY,EACV,oCAAoC,EACpC,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAC,6BAA6B,EAAC,MAAM,4BAA4B,CAAC;AAEzE,MAAM,WAAW,uCAAuC;IACtD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,2BAA2B,EAAE,6BAA6B,CAAC;IAC3D,0BAA0B,EAAE,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAChG,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,0CAA0C,EAAE,4CAA4C,CAAC;IACzF,2BAA2B,EAAE,6BAA6B,CAAC;IAC3D,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,uCAAuC;;;;;;;;EAyBhG"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n CreateIntegrationConnectionFn,\n DeleteIntegrationConnectionFn,\n GetIntegrationConnectionByIdFn,\n IntegrationConnection,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n UpdateIntegrationConnectionLifecycleStatusFn,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER} from '@shipfox/api-integration-webhook-dto';\nimport {config} from '#config.js';\nimport {createGenericWebhookProcessor} from '#core/webhook-processor.js';\nimport {createWebhookConnectionRoutes} from '#presentation/routes/connections.js';\nimport {createWebhookInboundRoutes} from '#presentation/routes/inbound.js';\n\nexport {redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\nexport type {\n CreateGenericWebhookProcessorOptions,\n GenericWebhookProcessor,\n} from '#core/webhook-processor.js';\nexport {createGenericWebhookProcessor} from '#core/webhook-processor.js';\n\nexport interface CreateWebhookIntegrationProviderOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n createIntegrationConnection: CreateIntegrationConnectionFn;\n listIntegrationConnections: (params: {workspaceId: string}) => Promise<IntegrationConnection[]>;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n updateIntegrationConnectionLifecycleStatus: UpdateIntegrationConnectionLifecycleStatusFn;\n deleteIntegrationConnection: DeleteIntegrationConnectionFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n baseUrl?: string | undefined;\n}\n\nexport function createWebhookIntegrationProvider(options: CreateWebhookIntegrationProviderOptions) {\n const baseUrl = options.baseUrl ?? config.WEBHOOK_PUBLIC_URL;\n const webhookProcessor = createGenericWebhookProcessor(options);\n return {\n provider: WEBHOOK_PROVIDER,\n displayName: 'Webhook',\n routes: [\n createWebhookConnectionRoutes({\n baseUrl,\n createIntegrationConnection: options.createIntegrationConnection,\n listIntegrationConnections: options.listIntegrationConnections,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n updateIntegrationConnectionLifecycleStatus:\n options.updateIntegrationConnectionLifecycleStatus,\n deleteIntegrationConnection: options.deleteIntegrationConnection,\n }),\n createWebhookInboundRoutes({\n coreDb: options.coreDb,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n processor: webhookProcessor,\n }),\n ],\n webhookProcessors: [{routeIds: ['webhook.connection'] as const, processor: webhookProcessor}],\n };\n}\n"],"names":["WEBHOOK_PROVIDER","config","createGenericWebhookProcessor","createWebhookConnectionRoutes","createWebhookInboundRoutes","redactHeaders","WEBHOOK_FORWARDED_HEADERS","WEBHOOK_INBOUND_BODY_LIMIT","createWebhookIntegrationProvider","options","baseUrl","WEBHOOK_PUBLIC_URL","webhookProcessor","provider","displayName","routes","createIntegrationConnection","listIntegrationConnections","getIntegrationConnectionById","updateIntegrationConnectionLifecycleStatus","deleteIntegrationConnection","coreDb","publishIntegrationEventReceived","processor","webhookProcessors","routeIds"],"mappings":"AASA,SAAQA,gBAAgB,QAAO,uCAAuC;AACtE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,6BAA6B,QAAO,6BAA6B;AACzE,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,0BAA0B,QAAO,kCAAkC;AAE3E,SAAQC,aAAa,EAAEC,yBAAyB,EAAEC,0BAA0B,QAAO,gBAAgB;AAKnG,SAAQL,6BAA6B,QAAO,6BAA6B;AAezE,OAAO,SAASM,iCAAiCC,OAAgD;IAC/F,MAAMC,UAAUD,QAAQC,OAAO,IAAIT,OAAOU,kBAAkB;IAC5D,MAAMC,mBAAmBV,8BAA8BO;IACvD,OAAO;QACLI,UAAUb;QACVc,aAAa;QACbC,QAAQ;YACNZ,8BAA8B;gBAC5BO;gBACAM,6BAA6BP,QAAQO,2BAA2B;gBAChEC,4BAA4BR,QAAQQ,0BAA0B;gBAC9DC,8BAA8BT,QAAQS,4BAA4B;gBAClEC,4CACEV,QAAQU,0CAA0C;gBACpDC,6BAA6BX,QAAQW,2BAA2B;YAClE;YACAhB,2BAA2B;gBACzBiB,QAAQZ,QAAQY,MAAM;gBACtBH,8BAA8BT,QAAQS,4BAA4B;gBAClEI,iCAAiCb,QAAQa,+BAA+B;gBACxEC,WAAWX;YACb;SACD;QACDY,mBAAmB;YAAC;gBAACC,UAAU;oBAAC;iBAAqB;gBAAWF,WAAWX;YAAgB;SAAE;IAC/F;AACF"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n CreateIntegrationConnectionFn,\n DeleteIntegrationConnectionFn,\n GetIntegrationConnectionByIdFn,\n IntegrationConnection,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n UpdateIntegrationConnectionLifecycleStatusFn,\n} from '@shipfox/api-integration-spi';\nimport {WEBHOOK_PROVIDER} from '@shipfox/api-integration-webhook-dto';\nimport {config} from '#config.js';\nimport {createGenericWebhookProcessor} from '#core/webhook-processor.js';\nimport {createWebhookConnectionRoutes} from '#presentation/routes/connections.js';\nimport {createWebhookInboundRoutes} from '#presentation/routes/inbound.js';\n\nexport {redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\nexport type {\n CreateGenericWebhookProcessorOptions,\n GenericWebhookProcessor,\n} from '#core/webhook-processor.js';\nexport {createGenericWebhookProcessor} from '#core/webhook-processor.js';\n\nexport interface CreateWebhookIntegrationProviderOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n createIntegrationConnection: CreateIntegrationConnectionFn;\n listIntegrationConnections: (params: {workspaceId: string}) => Promise<IntegrationConnection[]>;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n updateIntegrationConnectionLifecycleStatus: UpdateIntegrationConnectionLifecycleStatusFn;\n deleteIntegrationConnection: DeleteIntegrationConnectionFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n baseUrl?: string | undefined;\n}\n\nexport function createWebhookIntegrationProvider(options: CreateWebhookIntegrationProviderOptions) {\n const baseUrl = options.baseUrl ?? config.WEBHOOK_PUBLIC_URL;\n const webhookProcessor = createGenericWebhookProcessor(options);\n return {\n provider: WEBHOOK_PROVIDER,\n displayName: 'Webhook',\n routes: [\n createWebhookConnectionRoutes({\n baseUrl,\n createIntegrationConnection: options.createIntegrationConnection,\n listIntegrationConnections: options.listIntegrationConnections,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n updateIntegrationConnectionLifecycleStatus:\n options.updateIntegrationConnectionLifecycleStatus,\n deleteIntegrationConnection: options.deleteIntegrationConnection,\n }),\n createWebhookInboundRoutes({\n coreDb: options.coreDb,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n publishIntegrationEventReceived: options.publishIntegrationEventReceived,\n processor: webhookProcessor,\n }),\n ],\n webhookProcessors: [{routeIds: ['webhook.connection'] as const, processor: webhookProcessor}],\n };\n}\n"],"names":["WEBHOOK_PROVIDER","config","createGenericWebhookProcessor","createWebhookConnectionRoutes","createWebhookInboundRoutes","redactHeaders","WEBHOOK_FORWARDED_HEADERS","WEBHOOK_INBOUND_BODY_LIMIT","createWebhookIntegrationProvider","options","baseUrl","WEBHOOK_PUBLIC_URL","webhookProcessor","provider","displayName","routes","createIntegrationConnection","listIntegrationConnections","getIntegrationConnectionById","updateIntegrationConnectionLifecycleStatus","deleteIntegrationConnection","coreDb","publishIntegrationEventReceived","processor","webhookProcessors","routeIds"],"mappings":"AASA,SAAQA,gBAAgB,QAAO,uCAAuC;AACtE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,6BAA6B,QAAO,6BAA6B;AACzE,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,0BAA0B,QAAO,kCAAkC;AAE3E,SAAQC,aAAa,EAAEC,yBAAyB,EAAEC,0BAA0B,QAAO,gBAAgB;AAKnG,SAAQL,6BAA6B,QAAO,6BAA6B;AAezE,OAAO,SAASM,iCAAiCC,OAAgD;IAC/F,MAAMC,UAAUD,QAAQC,OAAO,IAAIT,OAAOU,kBAAkB;IAC5D,MAAMC,mBAAmBV,8BAA8BO;IACvD,OAAO;QACLI,UAAUb;QACVc,aAAa;QACbC,QAAQ;YACNZ,8BAA8B;gBAC5BO;gBACAM,6BAA6BP,QAAQO,2BAA2B;gBAChEC,4BAA4BR,QAAQQ,0BAA0B;gBAC9DC,8BAA8BT,QAAQS,4BAA4B;gBAClEC,4CACEV,QAAQU,0CAA0C;gBACpDC,6BAA6BX,QAAQW,2BAA2B;YAClE;YACAhB,2BAA2B;gBACzBiB,QAAQZ,QAAQY,MAAM;gBACtBH,8BAA8BT,QAAQS,4BAA4B;gBAClEI,iCAAiCb,QAAQa,+BAA+B;gBACxEC,WAAWX;YACb;SACD;QACDY,mBAAmB;YAAC;gBAACC,UAAU;oBAAC;iBAAqB;gBAAWF,WAAWX;YAAgB;SAAE;IAC/F;AACF"}
@@ -1,4 +1,4 @@
1
- import type { IntegrationConnection } from '@shipfox/api-integration-core-dto';
1
+ import type { IntegrationConnection } from '@shipfox/api-integration-spi';
2
2
  import type { WebhookConnectionDto } from '@shipfox/api-integration-webhook-dto';
3
3
  export declare function toWebhookConnectionDto(connection: IntegrationConnection, baseUrl: string): WebhookConnectionDto;
4
4
  //# sourceMappingURL=connections.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/connections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;AAC7E,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,sCAAsC,CAAC;AAI/E,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,MAAM,GACd,oBAAoB,CAWtB"}
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/connections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,sCAAsC,CAAC;AAI/E,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,MAAM,GACd,oBAAoB,CAWtB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/dto/connections.ts"],"sourcesContent":["import type {IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport type {WebhookConnectionDto} from '@shipfox/api-integration-webhook-dto';\n\nconst TRAILING_SLASHES_RE = /\\/+$/;\n\nexport function toWebhookConnectionDto(\n connection: IntegrationConnection,\n baseUrl: string,\n): WebhookConnectionDto {\n return {\n id: connection.id,\n workspace_id: connection.workspaceId,\n name: connection.displayName,\n slug: connection.slug,\n lifecycle_status: connection.lifecycleStatus,\n inbound_url: `${baseUrl.replace(TRAILING_SLASHES_RE, '')}/webhook/${connection.id}`,\n created_at: connection.createdAt.toISOString(),\n updated_at: connection.updatedAt.toISOString(),\n };\n}\n"],"names":["TRAILING_SLASHES_RE","toWebhookConnectionDto","connection","baseUrl","id","workspace_id","workspaceId","name","displayName","slug","lifecycle_status","lifecycleStatus","inbound_url","replace","created_at","createdAt","toISOString","updated_at","updatedAt"],"mappings":"AAGA,MAAMA,sBAAsB;AAE5B,OAAO,SAASC,uBACdC,UAAiC,EACjCC,OAAe;IAEf,OAAO;QACLC,IAAIF,WAAWE,EAAE;QACjBC,cAAcH,WAAWI,WAAW;QACpCC,MAAML,WAAWM,WAAW;QAC5BC,MAAMP,WAAWO,IAAI;QACrBC,kBAAkBR,WAAWS,eAAe;QAC5CC,aAAa,GAAGT,QAAQU,OAAO,CAACb,qBAAqB,IAAI,SAAS,EAAEE,WAAWE,EAAE,EAAE;QACnFU,YAAYZ,WAAWa,SAAS,CAACC,WAAW;QAC5CC,YAAYf,WAAWgB,SAAS,CAACF,WAAW;IAC9C;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/dto/connections.ts"],"sourcesContent":["import type {IntegrationConnection} from '@shipfox/api-integration-spi';\nimport type {WebhookConnectionDto} from '@shipfox/api-integration-webhook-dto';\n\nconst TRAILING_SLASHES_RE = /\\/+$/;\n\nexport function toWebhookConnectionDto(\n connection: IntegrationConnection,\n baseUrl: string,\n): WebhookConnectionDto {\n return {\n id: connection.id,\n workspace_id: connection.workspaceId,\n name: connection.displayName,\n slug: connection.slug,\n lifecycle_status: connection.lifecycleStatus,\n inbound_url: `${baseUrl.replace(TRAILING_SLASHES_RE, '')}/webhook/${connection.id}`,\n created_at: connection.createdAt.toISOString(),\n updated_at: connection.updatedAt.toISOString(),\n };\n}\n"],"names":["TRAILING_SLASHES_RE","toWebhookConnectionDto","connection","baseUrl","id","workspace_id","workspaceId","name","displayName","slug","lifecycle_status","lifecycleStatus","inbound_url","replace","created_at","createdAt","toISOString","updated_at","updatedAt"],"mappings":"AAGA,MAAMA,sBAAsB;AAE5B,OAAO,SAASC,uBACdC,UAAiC,EACjCC,OAAe;IAEf,OAAO;QACLC,IAAIF,WAAWE,EAAE;QACjBC,cAAcH,WAAWI,WAAW;QACpCC,MAAML,WAAWM,WAAW;QAC5BC,MAAMP,WAAWO,IAAI;QACrBC,kBAAkBR,WAAWS,eAAe;QAC5CC,aAAa,GAAGT,QAAQU,OAAO,CAACb,qBAAqB,IAAI,SAAS,EAAEE,WAAWE,EAAE,EAAE;QACnFU,YAAYZ,WAAWa,SAAS,CAACC,WAAW;QAC5CC,YAAYf,WAAWgB,SAAS,CAACF,WAAW;IAC9C;AACF"}
@@ -1,4 +1,4 @@
1
- import { type CreateIntegrationConnectionFn, type DeleteIntegrationConnectionFn, type GetIntegrationConnectionByIdFn, type IntegrationConnection, type UpdateIntegrationConnectionLifecycleStatusFn } from '@shipfox/api-integration-core-dto';
1
+ import { type CreateIntegrationConnectionFn, type DeleteIntegrationConnectionFn, type GetIntegrationConnectionByIdFn, type IntegrationConnection, type UpdateIntegrationConnectionLifecycleStatusFn } from '@shipfox/api-integration-spi';
2
2
  import { type RouteGroup } from '@shipfox/node-fastify';
3
3
  export interface CreateWebhookConnectionRoutesOptions {
4
4
  baseUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/connections.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EACnC,KAAK,qBAAqB,EAC1B,KAAK,4CAA4C,EAClD,MAAM,mCAAmC,CAAC;AAS3C,OAAO,EAA2B,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAKhF,MAAM,WAAW,oCAAoC;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B,EAAE,6BAA6B,CAAC;IAC3D,0BAA0B,EAAE,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAChG,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,0CAA0C,EAAE,4CAA4C,CAAC;IACzF,2BAA2B,EAAE,6BAA6B,CAAC;CAC5D;AA6BD,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,UAAU,CA2HZ"}
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/connections.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EACnC,KAAK,qBAAqB,EAC1B,KAAK,4CAA4C,EAClD,MAAM,8BAA8B,CAAC;AAStC,OAAO,EAA2B,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAKhF,MAAM,WAAW,oCAAoC;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B,EAAE,6BAA6B,CAAC;IAC3D,0BAA0B,EAAE,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAChG,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,0CAA0C,EAAE,4CAA4C,CAAC;IACzF,2BAA2B,EAAE,6BAA6B,CAAC;CAC5D;AA6BD,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,UAAU,CA2HZ"}
@@ -1,5 +1,5 @@
1
1
  import { AUTH_USER, requireWorkspaceAccess } from '@shipfox/api-auth-context';
2
- import { ConnectionSlugConflictError } from '@shipfox/api-integration-core-dto';
2
+ import { ConnectionSlugConflictError } from '@shipfox/api-integration-spi';
3
3
  import { createWebhookConnectionBodySchema, listWebhookConnectionsQuerySchema, listWebhookConnectionsResponseSchema, updateWebhookConnectionBodySchema, WEBHOOK_PROVIDER, webhookConnectionDtoSchema } from '@shipfox/api-integration-webhook-dto';
4
4
  import { ClientError, defineRoute } from '@shipfox/node-fastify';
5
5
  import { z } from 'zod';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/connections.ts"],"sourcesContent":["import {AUTH_USER, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {\n ConnectionSlugConflictError,\n type CreateIntegrationConnectionFn,\n type DeleteIntegrationConnectionFn,\n type GetIntegrationConnectionByIdFn,\n type IntegrationConnection,\n type UpdateIntegrationConnectionLifecycleStatusFn,\n} from '@shipfox/api-integration-core-dto';\nimport {\n createWebhookConnectionBodySchema,\n listWebhookConnectionsQuerySchema,\n listWebhookConnectionsResponseSchema,\n updateWebhookConnectionBodySchema,\n WEBHOOK_PROVIDER,\n webhookConnectionDtoSchema,\n} from '@shipfox/api-integration-webhook-dto';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {FastifyRequest} from 'fastify';\nimport {z} from 'zod';\nimport {toWebhookConnectionDto} from '#presentation/dto/connections.js';\n\nexport interface CreateWebhookConnectionRoutesOptions {\n baseUrl: string;\n createIntegrationConnection: CreateIntegrationConnectionFn;\n listIntegrationConnections: (params: {workspaceId: string}) => Promise<IntegrationConnection[]>;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n updateIntegrationConnectionLifecycleStatus: UpdateIntegrationConnectionLifecycleStatusFn;\n deleteIntegrationConnection: DeleteIntegrationConnectionFn;\n}\n\nconst connectionParamsSchema = z.object({\n connectionId: z.string().uuid(),\n});\n\nfunction isConnectionAlreadyExistsError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n ((error as {name?: unknown}).name === 'IntegrationConnectionAlreadyExistsError' ||\n error instanceof ConnectionSlugConflictError)\n );\n}\n\nasync function requireWebhookConnection(params: {\n request: FastifyRequest;\n connectionId: string;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}): Promise<IntegrationConnection> {\n const connection = await params.getIntegrationConnectionById(params.connectionId);\n if (!connection || connection.provider !== WEBHOOK_PROVIDER) {\n throw new ClientError('Webhook connection not found', 'not-found', {status: 404});\n }\n\n requireWorkspaceAccess({request: params.request, workspaceId: connection.workspaceId});\n return connection;\n}\n\nexport function createWebhookConnectionRoutes(\n options: CreateWebhookConnectionRoutesOptions,\n): RouteGroup {\n const createConnectionRoute = defineRoute({\n method: 'POST',\n path: '/connections',\n auth: AUTH_USER,\n description: 'Create a generic webhook connection.',\n schema: {\n body: createWebhookConnectionBodySchema,\n response: {\n 201: webhookConnectionDtoSchema,\n },\n },\n errorHandler: (error) => {\n if (isConnectionAlreadyExistsError(error)) {\n throw new ClientError('Webhook slug already exists', 'slug-already-exists', {status: 409});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const {workspace_id: workspaceId, name, slug} = request.body;\n\n requireWorkspaceAccess({request, workspaceId});\n const connection = await options.createIntegrationConnection({\n workspaceId,\n provider: WEBHOOK_PROVIDER,\n // The generic webhook provider uses the human slug as its provider-side account id.\n externalAccountId: slug,\n slug,\n displayName: name,\n });\n\n reply.status(201);\n return toWebhookConnectionDto(connection, options.baseUrl);\n },\n });\n\n const listConnectionsRoute = defineRoute({\n method: 'GET',\n path: '/connections',\n auth: AUTH_USER,\n description: 'List generic webhook connections for a workspace.',\n schema: {\n querystring: listWebhookConnectionsQuerySchema,\n response: {\n 200: listWebhookConnectionsResponseSchema,\n },\n },\n handler: async (request) => {\n const {workspace_id: workspaceId} = request.query;\n\n requireWorkspaceAccess({request, workspaceId});\n const connections = (await options.listIntegrationConnections({workspaceId})).filter(\n (connection) => connection.provider === WEBHOOK_PROVIDER,\n );\n return {\n connections: connections.map((connection) =>\n toWebhookConnectionDto(connection, options.baseUrl),\n ),\n };\n },\n });\n\n const updateConnectionRoute = defineRoute({\n method: 'PATCH',\n path: '/connections/:connectionId',\n auth: AUTH_USER,\n description: 'Update a generic webhook connection.',\n schema: {\n params: connectionParamsSchema,\n body: updateWebhookConnectionBodySchema,\n response: {\n 200: webhookConnectionDtoSchema,\n },\n },\n handler: async (request) => {\n const connection = await requireWebhookConnection({\n request,\n connectionId: request.params.connectionId,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n });\n const updated = await options.updateIntegrationConnectionLifecycleStatus({\n id: connection.id,\n lifecycleStatus: request.body.lifecycle_status,\n });\n if (!updated) {\n throw new ClientError('Webhook connection not found', 'not-found', {status: 404});\n }\n return toWebhookConnectionDto(updated, options.baseUrl);\n },\n });\n\n const deleteConnectionRoute = defineRoute({\n method: 'DELETE',\n path: '/connections/:connectionId',\n auth: AUTH_USER,\n description: 'Delete a generic webhook connection.',\n schema: {\n params: connectionParamsSchema,\n response: {\n 204: z.void(),\n },\n },\n handler: async (request, reply) => {\n const connection = await requireWebhookConnection({\n request,\n connectionId: request.params.connectionId,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n });\n\n await options.deleteIntegrationConnection({id: connection.id});\n reply.code(204);\n },\n });\n\n return {\n prefix: '/integrations/webhook',\n routes: [\n createConnectionRoute,\n listConnectionsRoute,\n updateConnectionRoute,\n deleteConnectionRoute,\n ],\n };\n}\n"],"names":["AUTH_USER","requireWorkspaceAccess","ConnectionSlugConflictError","createWebhookConnectionBodySchema","listWebhookConnectionsQuerySchema","listWebhookConnectionsResponseSchema","updateWebhookConnectionBodySchema","WEBHOOK_PROVIDER","webhookConnectionDtoSchema","ClientError","defineRoute","z","toWebhookConnectionDto","connectionParamsSchema","object","connectionId","string","uuid","isConnectionAlreadyExistsError","error","name","requireWebhookConnection","params","connection","getIntegrationConnectionById","provider","status","request","workspaceId","createWebhookConnectionRoutes","options","createConnectionRoute","method","path","auth","description","schema","body","response","errorHandler","handler","reply","workspace_id","slug","createIntegrationConnection","externalAccountId","displayName","baseUrl","listConnectionsRoute","querystring","query","connections","listIntegrationConnections","filter","map","updateConnectionRoute","updated","updateIntegrationConnectionLifecycleStatus","id","lifecycleStatus","lifecycle_status","deleteConnectionRoute","void","deleteIntegrationConnection","code","prefix","routes"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/connections.ts"],"sourcesContent":["import {AUTH_USER, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {\n ConnectionSlugConflictError,\n type CreateIntegrationConnectionFn,\n type DeleteIntegrationConnectionFn,\n type GetIntegrationConnectionByIdFn,\n type IntegrationConnection,\n type UpdateIntegrationConnectionLifecycleStatusFn,\n} from '@shipfox/api-integration-spi';\nimport {\n createWebhookConnectionBodySchema,\n listWebhookConnectionsQuerySchema,\n listWebhookConnectionsResponseSchema,\n updateWebhookConnectionBodySchema,\n WEBHOOK_PROVIDER,\n webhookConnectionDtoSchema,\n} from '@shipfox/api-integration-webhook-dto';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {FastifyRequest} from 'fastify';\nimport {z} from 'zod';\nimport {toWebhookConnectionDto} from '#presentation/dto/connections.js';\n\nexport interface CreateWebhookConnectionRoutesOptions {\n baseUrl: string;\n createIntegrationConnection: CreateIntegrationConnectionFn;\n listIntegrationConnections: (params: {workspaceId: string}) => Promise<IntegrationConnection[]>;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n updateIntegrationConnectionLifecycleStatus: UpdateIntegrationConnectionLifecycleStatusFn;\n deleteIntegrationConnection: DeleteIntegrationConnectionFn;\n}\n\nconst connectionParamsSchema = z.object({\n connectionId: z.string().uuid(),\n});\n\nfunction isConnectionAlreadyExistsError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n ((error as {name?: unknown}).name === 'IntegrationConnectionAlreadyExistsError' ||\n error instanceof ConnectionSlugConflictError)\n );\n}\n\nasync function requireWebhookConnection(params: {\n request: FastifyRequest;\n connectionId: string;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}): Promise<IntegrationConnection> {\n const connection = await params.getIntegrationConnectionById(params.connectionId);\n if (!connection || connection.provider !== WEBHOOK_PROVIDER) {\n throw new ClientError('Webhook connection not found', 'not-found', {status: 404});\n }\n\n requireWorkspaceAccess({request: params.request, workspaceId: connection.workspaceId});\n return connection;\n}\n\nexport function createWebhookConnectionRoutes(\n options: CreateWebhookConnectionRoutesOptions,\n): RouteGroup {\n const createConnectionRoute = defineRoute({\n method: 'POST',\n path: '/connections',\n auth: AUTH_USER,\n description: 'Create a generic webhook connection.',\n schema: {\n body: createWebhookConnectionBodySchema,\n response: {\n 201: webhookConnectionDtoSchema,\n },\n },\n errorHandler: (error) => {\n if (isConnectionAlreadyExistsError(error)) {\n throw new ClientError('Webhook slug already exists', 'slug-already-exists', {status: 409});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const {workspace_id: workspaceId, name, slug} = request.body;\n\n requireWorkspaceAccess({request, workspaceId});\n const connection = await options.createIntegrationConnection({\n workspaceId,\n provider: WEBHOOK_PROVIDER,\n // The generic webhook provider uses the human slug as its provider-side account id.\n externalAccountId: slug,\n slug,\n displayName: name,\n });\n\n reply.status(201);\n return toWebhookConnectionDto(connection, options.baseUrl);\n },\n });\n\n const listConnectionsRoute = defineRoute({\n method: 'GET',\n path: '/connections',\n auth: AUTH_USER,\n description: 'List generic webhook connections for a workspace.',\n schema: {\n querystring: listWebhookConnectionsQuerySchema,\n response: {\n 200: listWebhookConnectionsResponseSchema,\n },\n },\n handler: async (request) => {\n const {workspace_id: workspaceId} = request.query;\n\n requireWorkspaceAccess({request, workspaceId});\n const connections = (await options.listIntegrationConnections({workspaceId})).filter(\n (connection) => connection.provider === WEBHOOK_PROVIDER,\n );\n return {\n connections: connections.map((connection) =>\n toWebhookConnectionDto(connection, options.baseUrl),\n ),\n };\n },\n });\n\n const updateConnectionRoute = defineRoute({\n method: 'PATCH',\n path: '/connections/:connectionId',\n auth: AUTH_USER,\n description: 'Update a generic webhook connection.',\n schema: {\n params: connectionParamsSchema,\n body: updateWebhookConnectionBodySchema,\n response: {\n 200: webhookConnectionDtoSchema,\n },\n },\n handler: async (request) => {\n const connection = await requireWebhookConnection({\n request,\n connectionId: request.params.connectionId,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n });\n const updated = await options.updateIntegrationConnectionLifecycleStatus({\n id: connection.id,\n lifecycleStatus: request.body.lifecycle_status,\n });\n if (!updated) {\n throw new ClientError('Webhook connection not found', 'not-found', {status: 404});\n }\n return toWebhookConnectionDto(updated, options.baseUrl);\n },\n });\n\n const deleteConnectionRoute = defineRoute({\n method: 'DELETE',\n path: '/connections/:connectionId',\n auth: AUTH_USER,\n description: 'Delete a generic webhook connection.',\n schema: {\n params: connectionParamsSchema,\n response: {\n 204: z.void(),\n },\n },\n handler: async (request, reply) => {\n const connection = await requireWebhookConnection({\n request,\n connectionId: request.params.connectionId,\n getIntegrationConnectionById: options.getIntegrationConnectionById,\n });\n\n await options.deleteIntegrationConnection({id: connection.id});\n reply.code(204);\n },\n });\n\n return {\n prefix: '/integrations/webhook',\n routes: [\n createConnectionRoute,\n listConnectionsRoute,\n updateConnectionRoute,\n deleteConnectionRoute,\n ],\n };\n}\n"],"names":["AUTH_USER","requireWorkspaceAccess","ConnectionSlugConflictError","createWebhookConnectionBodySchema","listWebhookConnectionsQuerySchema","listWebhookConnectionsResponseSchema","updateWebhookConnectionBodySchema","WEBHOOK_PROVIDER","webhookConnectionDtoSchema","ClientError","defineRoute","z","toWebhookConnectionDto","connectionParamsSchema","object","connectionId","string","uuid","isConnectionAlreadyExistsError","error","name","requireWebhookConnection","params","connection","getIntegrationConnectionById","provider","status","request","workspaceId","createWebhookConnectionRoutes","options","createConnectionRoute","method","path","auth","description","schema","body","response","errorHandler","handler","reply","workspace_id","slug","createIntegrationConnection","externalAccountId","displayName","baseUrl","listConnectionsRoute","querystring","query","connections","listIntegrationConnections","filter","map","updateConnectionRoute","updated","updateIntegrationConnectionLifecycleStatus","id","lifecycleStatus","lifecycle_status","deleteConnectionRoute","void","deleteIntegrationConnection","code","prefix","routes"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,sBAAsB,QAAO,4BAA4B;AAC5E,SACEC,2BAA2B,QAMtB,+BAA+B;AACtC,SACEC,iCAAiC,EACjCC,iCAAiC,EACjCC,oCAAoC,EACpCC,iCAAiC,EACjCC,gBAAgB,EAChBC,0BAA0B,QACrB,uCAAuC;AAC9C,SAAQC,WAAW,EAAEC,WAAW,QAAwB,wBAAwB;AAEhF,SAAQC,CAAC,QAAO,MAAM;AACtB,SAAQC,sBAAsB,QAAO,mCAAmC;AAWxE,MAAMC,yBAAyBF,EAAEG,MAAM,CAAC;IACtCC,cAAcJ,EAAEK,MAAM,GAAGC,IAAI;AAC/B;AAEA,SAASC,+BAA+BC,KAAc;IACpD,OACE,OAAOA,UAAU,YACjBA,UAAU,QACT,CAAA,AAACA,MAA2BC,IAAI,KAAK,6CACpCD,iBAAiBjB,2BAA0B;AAEjD;AAEA,eAAemB,yBAAyBC,MAIvC;IACC,MAAMC,aAAa,MAAMD,OAAOE,4BAA4B,CAACF,OAAOP,YAAY;IAChF,IAAI,CAACQ,cAAcA,WAAWE,QAAQ,KAAKlB,kBAAkB;QAC3D,MAAM,IAAIE,YAAY,gCAAgC,aAAa;YAACiB,QAAQ;QAAG;IACjF;IAEAzB,uBAAuB;QAAC0B,SAASL,OAAOK,OAAO;QAAEC,aAAaL,WAAWK,WAAW;IAAA;IACpF,OAAOL;AACT;AAEA,OAAO,SAASM,8BACdC,OAA6C;IAE7C,MAAMC,wBAAwBrB,YAAY;QACxCsB,QAAQ;QACRC,MAAM;QACNC,MAAMlC;QACNmC,aAAa;QACbC,QAAQ;YACNC,MAAMlC;YACNmC,UAAU;gBACR,KAAK9B;YACP;QACF;QACA+B,cAAc,CAACpB;YACb,IAAID,+BAA+BC,QAAQ;gBACzC,MAAM,IAAIV,YAAY,+BAA+B,uBAAuB;oBAACiB,QAAQ;gBAAG;YAC1F;YACA,MAAMP;QACR;QACAqB,SAAS,OAAOb,SAASc;YACvB,MAAM,EAACC,cAAcd,WAAW,EAAER,IAAI,EAAEuB,IAAI,EAAC,GAAGhB,QAAQU,IAAI;YAE5DpC,uBAAuB;gBAAC0B;gBAASC;YAAW;YAC5C,MAAML,aAAa,MAAMO,QAAQc,2BAA2B,CAAC;gBAC3DhB;gBACAH,UAAUlB;gBACV,oFAAoF;gBACpFsC,mBAAmBF;gBACnBA;gBACAG,aAAa1B;YACf;YAEAqB,MAAMf,MAAM,CAAC;YACb,OAAOd,uBAAuBW,YAAYO,QAAQiB,OAAO;QAC3D;IACF;IAEA,MAAMC,uBAAuBtC,YAAY;QACvCsB,QAAQ;QACRC,MAAM;QACNC,MAAMlC;QACNmC,aAAa;QACbC,QAAQ;YACNa,aAAa7C;YACbkC,UAAU;gBACR,KAAKjC;YACP;QACF;QACAmC,SAAS,OAAOb;YACd,MAAM,EAACe,cAAcd,WAAW,EAAC,GAAGD,QAAQuB,KAAK;YAEjDjD,uBAAuB;gBAAC0B;gBAASC;YAAW;YAC5C,MAAMuB,cAAc,AAAC,CAAA,MAAMrB,QAAQsB,0BAA0B,CAAC;gBAACxB;YAAW,EAAC,EAAGyB,MAAM,CAClF,CAAC9B,aAAeA,WAAWE,QAAQ,KAAKlB;YAE1C,OAAO;gBACL4C,aAAaA,YAAYG,GAAG,CAAC,CAAC/B,aAC5BX,uBAAuBW,YAAYO,QAAQiB,OAAO;YAEtD;QACF;IACF;IAEA,MAAMQ,wBAAwB7C,YAAY;QACxCsB,QAAQ;QACRC,MAAM;QACNC,MAAMlC;QACNmC,aAAa;QACbC,QAAQ;YACNd,QAAQT;YACRwB,MAAM/B;YACNgC,UAAU;gBACR,KAAK9B;YACP;QACF;QACAgC,SAAS,OAAOb;YACd,MAAMJ,aAAa,MAAMF,yBAAyB;gBAChDM;gBACAZ,cAAcY,QAAQL,MAAM,CAACP,YAAY;gBACzCS,8BAA8BM,QAAQN,4BAA4B;YACpE;YACA,MAAMgC,UAAU,MAAM1B,QAAQ2B,0CAA0C,CAAC;gBACvEC,IAAInC,WAAWmC,EAAE;gBACjBC,iBAAiBhC,QAAQU,IAAI,CAACuB,gBAAgB;YAChD;YACA,IAAI,CAACJ,SAAS;gBACZ,MAAM,IAAI/C,YAAY,gCAAgC,aAAa;oBAACiB,QAAQ;gBAAG;YACjF;YACA,OAAOd,uBAAuB4C,SAAS1B,QAAQiB,OAAO;QACxD;IACF;IAEA,MAAMc,wBAAwBnD,YAAY;QACxCsB,QAAQ;QACRC,MAAM;QACNC,MAAMlC;QACNmC,aAAa;QACbC,QAAQ;YACNd,QAAQT;YACRyB,UAAU;gBACR,KAAK3B,EAAEmD,IAAI;YACb;QACF;QACAtB,SAAS,OAAOb,SAASc;YACvB,MAAMlB,aAAa,MAAMF,yBAAyB;gBAChDM;gBACAZ,cAAcY,QAAQL,MAAM,CAACP,YAAY;gBACzCS,8BAA8BM,QAAQN,4BAA4B;YACpE;YAEA,MAAMM,QAAQiC,2BAA2B,CAAC;gBAACL,IAAInC,WAAWmC,EAAE;YAAA;YAC5DjB,MAAMuB,IAAI,CAAC;QACb;IACF;IAEA,OAAO;QACLC,QAAQ;QACRC,QAAQ;YACNnC;YACAiB;YACAO;YACAM;SACD;IACH;AACF"}
@@ -1,4 +1,4 @@
1
- import type { GetIntegrationConnectionByIdFn, IntegrationTx, PublishIntegrationEventReceivedFn } from '@shipfox/api-integration-core-dto';
1
+ import type { GetIntegrationConnectionByIdFn, IntegrationTx, PublishIntegrationEventReceivedFn } from '@shipfox/api-integration-spi';
2
2
  import { type RouteGroup } from '@shipfox/node-fastify';
3
3
  import { type GenericWebhookProcessor } from '#core/webhook-processor.js';
4
4
  export interface CreateWebhookInboundRoutesOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/inbound.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,8BAA8B,EAC9B,aAAa,EACb,iCAAiC,EAClC,MAAM,mCAAmC,CAAC;AAO3C,OAAO,EAA2B,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAKhF,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,4BAA4B,CAAC;AAkBpC,MAAM,WAAW,iCAAiC;IAChD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,SAAS,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CACjD;AA6BD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,UAAU,CA8DjG"}
1
+ {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/inbound.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,8BAA8B,EAC9B,aAAa,EACb,iCAAiC,EAClC,MAAM,8BAA8B,CAAC;AAOtC,OAAO,EAA2B,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAKhF,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,4BAA4B,CAAC;AAkBpC,MAAM,WAAW,iCAAiC;IAChD,MAAM,EAAE,MAAM;QACZ,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC;IACF,4BAA4B,EAAE,8BAA8B,CAAC;IAC7D,+BAA+B,EAAE,iCAAiC,CAAC;IACnE,SAAS,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CACjD;AA6BD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,UAAU,CA8DjG"}
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { createStoredWebhookRequest, WEBHOOK_MAX_RAW_BODY_BYTES } from '@shipfox/api-integration-core-dto';
2
+ import { createStoredWebhookRequest, WEBHOOK_MAX_RAW_BODY_BYTES } from '@shipfox/api-integration-spi';
3
3
  import { WEBHOOK_PROVIDER } from '@shipfox/api-integration-webhook-dto';
4
4
  import { ClientError, defineRoute } from '@shipfox/node-fastify';
5
5
  import fp from 'fastify-plugin';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/inbound.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport type {\n GetIntegrationConnectionByIdFn,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n} from '@shipfox/api-integration-core-dto';\nimport {\n createStoredWebhookRequest,\n type StoredWebhookRequest,\n WEBHOOK_MAX_RAW_BODY_BYTES,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER} from '@shipfox/api-integration-webhook-dto';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {FastifyPluginAsync} from 'fastify';\nimport fp from 'fastify-plugin';\nimport {z} from 'zod';\nimport {WEBHOOK_ACCEPTED_CONTENT_TYPES, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\nimport {\n createGenericWebhookProcessor,\n type GenericWebhookProcessor,\n} from '#core/webhook-processor.js';\n\nconst MAX_DELIVERY_ID_HEADER_LENGTH = 200;\nconst acceptedContentTypes = new Set<string>(WEBHOOK_ACCEPTED_CONTENT_TYPES);\nconst rawWebhookBodyPlugin: FastifyPluginAsync = fp((app) => {\n app.removeAllContentTypeParsers();\n for (const contentType of acceptedContentTypes) {\n app.addContentTypeParser(\n contentType,\n {parseAs: 'buffer', bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n (_request, body, done) => {\n done(null, body);\n },\n );\n }\n return Promise.resolve();\n});\n\nexport interface CreateWebhookInboundRoutesOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n processor?: GenericWebhookProcessor | undefined;\n}\n\nconst inboundParamsSchema = z.object({\n connectionId: z.string().uuid(),\n});\n\nconst inboundQuerySchema = z.record(z.string(), z.unknown());\n\nconst inboundAcceptedResponseSchema = z.object({\n delivery_id: z.string(),\n});\n\nfunction contentType(requestContentType: string | undefined): string | undefined {\n return requestContentType?.split(';', 1)[0]?.trim().toLowerCase();\n}\n\nfunction deliveryIdFor(header: string | string[] | undefined): string {\n const value = Array.isArray(header) ? header[0] : header;\n if (!value) return randomUUID();\n if (value.length > MAX_DELIVERY_ID_HEADER_LENGTH) {\n throw new ClientError('Delivery ID header is too long', 'delivery-id-too-long', {status: 400});\n }\n return value;\n}\n\nfunction hasDeliveryId(header: string | string[] | undefined): boolean {\n return Boolean(Array.isArray(header) ? header[0] : header);\n}\n\nexport function createWebhookInboundRoutes(options: CreateWebhookInboundRoutesOptions): RouteGroup {\n const processor = options.processor ?? createGenericWebhookProcessor(options);\n const inboundRoute = defineRoute({\n method: 'POST',\n path: '/:connectionId',\n auth: [],\n description: 'Generic webhook receiver.',\n options: {bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n schema: {\n params: inboundParamsSchema,\n querystring: inboundQuerySchema,\n response: {\n 202: inboundAcceptedResponseSchema,\n },\n },\n handler: async (request, reply) => {\n const mediaType = contentType(request.headers['content-type']);\n if (!mediaType || !acceptedContentTypes.has(mediaType)) {\n throw new ClientError('Unsupported content type', 'unsupported-media-type', {status: 415});\n }\n\n const {connectionId} = request.params;\n const connection = await options.getIntegrationConnectionById(connectionId);\n if (\n !connection ||\n connection.provider !== WEBHOOK_PROVIDER ||\n connection.lifecycleStatus !== 'active'\n ) {\n throw new ClientError('Webhook connection not found', 'not-found', {status: 404});\n }\n\n const deliveryHeader = request.headers['x-delivery-id'];\n const deliveryId = deliveryIdFor(deliveryHeader);\n const result = await processor.process(\n createGenericStoredWebhookRequest(\n {\n body: request.body,\n connectionId,\n headers: request.headers,\n rawQueryString: request.raw.url?.split('?')[1] ?? '',\n requestId: hasDeliveryId(deliveryHeader) ? randomUUID() : deliveryId,\n },\n new Date().toISOString(),\n ),\n connection,\n );\n\n if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {\n throw new ClientError('Malformed webhook payload', 'malformed-payload', {status: 400});\n }\n\n reply.code(202);\n return {delivery_id: deliveryId};\n },\n });\n\n return {\n prefix: '/webhook',\n auth: [],\n plugins: [rawWebhookBodyPlugin],\n routes: [inboundRoute],\n };\n}\n\nfunction createGenericStoredWebhookRequest(\n input: {\n body: unknown;\n connectionId: string;\n headers: Record<string, string | string[] | undefined>;\n rawQueryString: string;\n requestId: string;\n },\n receivedAt: string,\n): StoredWebhookRequest {\n if (!(input.body instanceof Uint8Array)) {\n throw new ClientError('Expected raw webhook body', 'invalid-webhook-request', {status: 400});\n }\n if (input.body.byteLength > WEBHOOK_MAX_RAW_BODY_BYTES) {\n throw new ClientError('Webhook request body is too large', 'body-too-large', {status: 413});\n }\n\n try {\n return createStoredWebhookRequest({\n requestId: input.requestId,\n routeId: 'webhook.connection',\n receivedAt,\n rawQueryString: input.rawQueryString,\n headers: webhookHeaders(input.headers),\n body: input.body,\n connectionId: input.connectionId,\n });\n } catch (error) {\n throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {\n cause: error,\n });\n }\n}\n\nfunction webhookHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.entries(headers).flatMap(([name, value]) => {\n if (value === undefined) return [];\n return [[name.toLowerCase(), Array.isArray(value) ? value.join(', ') : value]];\n }),\n );\n}\n"],"names":["randomUUID","createStoredWebhookRequest","WEBHOOK_MAX_RAW_BODY_BYTES","WEBHOOK_PROVIDER","ClientError","defineRoute","fp","z","WEBHOOK_ACCEPTED_CONTENT_TYPES","WEBHOOK_INBOUND_BODY_LIMIT","createGenericWebhookProcessor","MAX_DELIVERY_ID_HEADER_LENGTH","acceptedContentTypes","Set","rawWebhookBodyPlugin","app","removeAllContentTypeParsers","contentType","addContentTypeParser","parseAs","bodyLimit","_request","body","done","Promise","resolve","inboundParamsSchema","object","connectionId","string","uuid","inboundQuerySchema","record","unknown","inboundAcceptedResponseSchema","delivery_id","requestContentType","split","trim","toLowerCase","deliveryIdFor","header","value","Array","isArray","length","status","hasDeliveryId","Boolean","createWebhookInboundRoutes","options","processor","inboundRoute","method","path","auth","description","schema","params","querystring","response","handler","request","reply","mediaType","headers","has","connection","getIntegrationConnectionById","provider","lifecycleStatus","deliveryHeader","deliveryId","result","process","createGenericStoredWebhookRequest","rawQueryString","raw","url","requestId","Date","toISOString","outcome","reason","code","prefix","plugins","routes","input","receivedAt","Uint8Array","byteLength","routeId","webhookHeaders","error","cause","Object","fromEntries","entries","flatMap","name","undefined","join"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/inbound.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport type {\n GetIntegrationConnectionByIdFn,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n} from '@shipfox/api-integration-spi';\nimport {\n createStoredWebhookRequest,\n type StoredWebhookRequest,\n WEBHOOK_MAX_RAW_BODY_BYTES,\n} from '@shipfox/api-integration-spi';\nimport {WEBHOOK_PROVIDER} from '@shipfox/api-integration-webhook-dto';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {FastifyPluginAsync} from 'fastify';\nimport fp from 'fastify-plugin';\nimport {z} from 'zod';\nimport {WEBHOOK_ACCEPTED_CONTENT_TYPES, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\nimport {\n createGenericWebhookProcessor,\n type GenericWebhookProcessor,\n} from '#core/webhook-processor.js';\n\nconst MAX_DELIVERY_ID_HEADER_LENGTH = 200;\nconst acceptedContentTypes = new Set<string>(WEBHOOK_ACCEPTED_CONTENT_TYPES);\nconst rawWebhookBodyPlugin: FastifyPluginAsync = fp((app) => {\n app.removeAllContentTypeParsers();\n for (const contentType of acceptedContentTypes) {\n app.addContentTypeParser(\n contentType,\n {parseAs: 'buffer', bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n (_request, body, done) => {\n done(null, body);\n },\n );\n }\n return Promise.resolve();\n});\n\nexport interface CreateWebhookInboundRoutesOptions {\n coreDb: () => {\n transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;\n };\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;\n processor?: GenericWebhookProcessor | undefined;\n}\n\nconst inboundParamsSchema = z.object({\n connectionId: z.string().uuid(),\n});\n\nconst inboundQuerySchema = z.record(z.string(), z.unknown());\n\nconst inboundAcceptedResponseSchema = z.object({\n delivery_id: z.string(),\n});\n\nfunction contentType(requestContentType: string | undefined): string | undefined {\n return requestContentType?.split(';', 1)[0]?.trim().toLowerCase();\n}\n\nfunction deliveryIdFor(header: string | string[] | undefined): string {\n const value = Array.isArray(header) ? header[0] : header;\n if (!value) return randomUUID();\n if (value.length > MAX_DELIVERY_ID_HEADER_LENGTH) {\n throw new ClientError('Delivery ID header is too long', 'delivery-id-too-long', {status: 400});\n }\n return value;\n}\n\nfunction hasDeliveryId(header: string | string[] | undefined): boolean {\n return Boolean(Array.isArray(header) ? header[0] : header);\n}\n\nexport function createWebhookInboundRoutes(options: CreateWebhookInboundRoutesOptions): RouteGroup {\n const processor = options.processor ?? createGenericWebhookProcessor(options);\n const inboundRoute = defineRoute({\n method: 'POST',\n path: '/:connectionId',\n auth: [],\n description: 'Generic webhook receiver.',\n options: {bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n schema: {\n params: inboundParamsSchema,\n querystring: inboundQuerySchema,\n response: {\n 202: inboundAcceptedResponseSchema,\n },\n },\n handler: async (request, reply) => {\n const mediaType = contentType(request.headers['content-type']);\n if (!mediaType || !acceptedContentTypes.has(mediaType)) {\n throw new ClientError('Unsupported content type', 'unsupported-media-type', {status: 415});\n }\n\n const {connectionId} = request.params;\n const connection = await options.getIntegrationConnectionById(connectionId);\n if (\n !connection ||\n connection.provider !== WEBHOOK_PROVIDER ||\n connection.lifecycleStatus !== 'active'\n ) {\n throw new ClientError('Webhook connection not found', 'not-found', {status: 404});\n }\n\n const deliveryHeader = request.headers['x-delivery-id'];\n const deliveryId = deliveryIdFor(deliveryHeader);\n const result = await processor.process(\n createGenericStoredWebhookRequest(\n {\n body: request.body,\n connectionId,\n headers: request.headers,\n rawQueryString: request.raw.url?.split('?')[1] ?? '',\n requestId: hasDeliveryId(deliveryHeader) ? randomUUID() : deliveryId,\n },\n new Date().toISOString(),\n ),\n connection,\n );\n\n if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {\n throw new ClientError('Malformed webhook payload', 'malformed-payload', {status: 400});\n }\n\n reply.code(202);\n return {delivery_id: deliveryId};\n },\n });\n\n return {\n prefix: '/webhook',\n auth: [],\n plugins: [rawWebhookBodyPlugin],\n routes: [inboundRoute],\n };\n}\n\nfunction createGenericStoredWebhookRequest(\n input: {\n body: unknown;\n connectionId: string;\n headers: Record<string, string | string[] | undefined>;\n rawQueryString: string;\n requestId: string;\n },\n receivedAt: string,\n): StoredWebhookRequest {\n if (!(input.body instanceof Uint8Array)) {\n throw new ClientError('Expected raw webhook body', 'invalid-webhook-request', {status: 400});\n }\n if (input.body.byteLength > WEBHOOK_MAX_RAW_BODY_BYTES) {\n throw new ClientError('Webhook request body is too large', 'body-too-large', {status: 413});\n }\n\n try {\n return createStoredWebhookRequest({\n requestId: input.requestId,\n routeId: 'webhook.connection',\n receivedAt,\n rawQueryString: input.rawQueryString,\n headers: webhookHeaders(input.headers),\n body: input.body,\n connectionId: input.connectionId,\n });\n } catch (error) {\n throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {\n cause: error,\n });\n }\n}\n\nfunction webhookHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.entries(headers).flatMap(([name, value]) => {\n if (value === undefined) return [];\n return [[name.toLowerCase(), Array.isArray(value) ? value.join(', ') : value]];\n }),\n );\n}\n"],"names":["randomUUID","createStoredWebhookRequest","WEBHOOK_MAX_RAW_BODY_BYTES","WEBHOOK_PROVIDER","ClientError","defineRoute","fp","z","WEBHOOK_ACCEPTED_CONTENT_TYPES","WEBHOOK_INBOUND_BODY_LIMIT","createGenericWebhookProcessor","MAX_DELIVERY_ID_HEADER_LENGTH","acceptedContentTypes","Set","rawWebhookBodyPlugin","app","removeAllContentTypeParsers","contentType","addContentTypeParser","parseAs","bodyLimit","_request","body","done","Promise","resolve","inboundParamsSchema","object","connectionId","string","uuid","inboundQuerySchema","record","unknown","inboundAcceptedResponseSchema","delivery_id","requestContentType","split","trim","toLowerCase","deliveryIdFor","header","value","Array","isArray","length","status","hasDeliveryId","Boolean","createWebhookInboundRoutes","options","processor","inboundRoute","method","path","auth","description","schema","params","querystring","response","handler","request","reply","mediaType","headers","has","connection","getIntegrationConnectionById","provider","lifecycleStatus","deliveryHeader","deliveryId","result","process","createGenericStoredWebhookRequest","rawQueryString","raw","url","requestId","Date","toISOString","outcome","reason","code","prefix","plugins","routes","input","receivedAt","Uint8Array","byteLength","routeId","webhookHeaders","error","cause","Object","fromEntries","entries","flatMap","name","undefined","join"],"mappings":"AAAA,SAAQA,UAAU,QAAO,cAAc;AAMvC,SACEC,0BAA0B,EAE1BC,0BAA0B,QACrB,+BAA+B;AACtC,SAAQC,gBAAgB,QAAO,uCAAuC;AACtE,SAAQC,WAAW,EAAEC,WAAW,QAAwB,wBAAwB;AAEhF,OAAOC,QAAQ,iBAAiB;AAChC,SAAQC,CAAC,QAAO,MAAM;AACtB,SAAQC,8BAA8B,EAAEC,0BAA0B,QAAO,gBAAgB;AACzF,SACEC,6BAA6B,QAExB,6BAA6B;AAEpC,MAAMC,gCAAgC;AACtC,MAAMC,uBAAuB,IAAIC,IAAYL;AAC7C,MAAMM,uBAA2CR,GAAG,CAACS;IACnDA,IAAIC,2BAA2B;IAC/B,KAAK,MAAMC,eAAeL,qBAAsB;QAC9CG,IAAIG,oBAAoB,CACtBD,aACA;YAACE,SAAS;YAAUC,WAAWX;QAA0B,GACzD,CAACY,UAAUC,MAAMC;YACfA,KAAK,MAAMD;QACb;IAEJ;IACA,OAAOE,QAAQC,OAAO;AACxB;AAWA,MAAMC,sBAAsBnB,EAAEoB,MAAM,CAAC;IACnCC,cAAcrB,EAAEsB,MAAM,GAAGC,IAAI;AAC/B;AAEA,MAAMC,qBAAqBxB,EAAEyB,MAAM,CAACzB,EAAEsB,MAAM,IAAItB,EAAE0B,OAAO;AAEzD,MAAMC,gCAAgC3B,EAAEoB,MAAM,CAAC;IAC7CQ,aAAa5B,EAAEsB,MAAM;AACvB;AAEA,SAASZ,YAAYmB,kBAAsC;IACzD,OAAOA,oBAAoBC,MAAM,KAAK,EAAE,CAAC,EAAE,EAAEC,OAAOC;AACtD;AAEA,SAASC,cAAcC,MAAqC;IAC1D,MAAMC,QAAQC,MAAMC,OAAO,CAACH,UAAUA,MAAM,CAAC,EAAE,GAAGA;IAClD,IAAI,CAACC,OAAO,OAAO1C;IACnB,IAAI0C,MAAMG,MAAM,GAAGlC,+BAA+B;QAChD,MAAM,IAAIP,YAAY,kCAAkC,wBAAwB;YAAC0C,QAAQ;QAAG;IAC9F;IACA,OAAOJ;AACT;AAEA,SAASK,cAAcN,MAAqC;IAC1D,OAAOO,QAAQL,MAAMC,OAAO,CAACH,UAAUA,MAAM,CAAC,EAAE,GAAGA;AACrD;AAEA,OAAO,SAASQ,2BAA2BC,OAA0C;IACnF,MAAMC,YAAYD,QAAQC,SAAS,IAAIzC,8BAA8BwC;IACrE,MAAME,eAAe/C,YAAY;QAC/BgD,QAAQ;QACRC,MAAM;QACNC,MAAM,EAAE;QACRC,aAAa;QACbN,SAAS;YAAC9B,WAAWX;QAA0B;QAC/CgD,QAAQ;YACNC,QAAQhC;YACRiC,aAAa5B;YACb6B,UAAU;gBACR,KAAK1B;YACP;QACF;QACA2B,SAAS,OAAOC,SAASC;YACvB,MAAMC,YAAY/C,YAAY6C,QAAQG,OAAO,CAAC,eAAe;YAC7D,IAAI,CAACD,aAAa,CAACpD,qBAAqBsD,GAAG,CAACF,YAAY;gBACtD,MAAM,IAAI5D,YAAY,4BAA4B,0BAA0B;oBAAC0C,QAAQ;gBAAG;YAC1F;YAEA,MAAM,EAAClB,YAAY,EAAC,GAAGkC,QAAQJ,MAAM;YACrC,MAAMS,aAAa,MAAMjB,QAAQkB,4BAA4B,CAACxC;YAC9D,IACE,CAACuC,cACDA,WAAWE,QAAQ,KAAKlE,oBACxBgE,WAAWG,eAAe,KAAK,UAC/B;gBACA,MAAM,IAAIlE,YAAY,gCAAgC,aAAa;oBAAC0C,QAAQ;gBAAG;YACjF;YAEA,MAAMyB,iBAAiBT,QAAQG,OAAO,CAAC,gBAAgB;YACvD,MAAMO,aAAahC,cAAc+B;YACjC,MAAME,SAAS,MAAMtB,UAAUuB,OAAO,CACpCC,kCACE;gBACErD,MAAMwC,QAAQxC,IAAI;gBAClBM;gBACAqC,SAASH,QAAQG,OAAO;gBACxBW,gBAAgBd,QAAQe,GAAG,CAACC,GAAG,EAAEzC,MAAM,IAAI,CAAC,EAAE,IAAI;gBAClD0C,WAAWhC,cAAcwB,kBAAkBvE,eAAewE;YAC5D,GACA,IAAIQ,OAAOC,WAAW,KAExBd;YAGF,IAAIM,OAAOS,OAAO,KAAK,eAAeT,OAAOU,MAAM,KAAK,qBAAqB;gBAC3E,MAAM,IAAI/E,YAAY,6BAA6B,qBAAqB;oBAAC0C,QAAQ;gBAAG;YACtF;YAEAiB,MAAMqB,IAAI,CAAC;YACX,OAAO;gBAACjD,aAAaqC;YAAU;QACjC;IACF;IAEA,OAAO;QACLa,QAAQ;QACR9B,MAAM,EAAE;QACR+B,SAAS;YAACxE;SAAqB;QAC/ByE,QAAQ;YAACnC;SAAa;IACxB;AACF;AAEA,SAASuB,kCACPa,KAMC,EACDC,UAAkB;IAElB,IAAI,CAAED,CAAAA,MAAMlE,IAAI,YAAYoE,UAAS,GAAI;QACvC,MAAM,IAAItF,YAAY,6BAA6B,2BAA2B;YAAC0C,QAAQ;QAAG;IAC5F;IACA,IAAI0C,MAAMlE,IAAI,CAACqE,UAAU,GAAGzF,4BAA4B;QACtD,MAAM,IAAIE,YAAY,qCAAqC,kBAAkB;YAAC0C,QAAQ;QAAG;IAC3F;IAEA,IAAI;QACF,OAAO7C,2BAA2B;YAChC8E,WAAWS,MAAMT,SAAS;YAC1Ba,SAAS;YACTH;YACAb,gBAAgBY,MAAMZ,cAAc;YACpCX,SAAS4B,eAAeL,MAAMvB,OAAO;YACrC3C,MAAMkE,MAAMlE,IAAI;YAChBM,cAAc4D,MAAM5D,YAAY;QAClC;IACF,EAAE,OAAOkE,OAAO;QACd,MAAM,IAAI1F,YAAY,uCAAuC,2BAA2B;YACtF2F,OAAOD;QACT;IACF;AACF;AAEA,SAASD,eACP5B,OAAsD;IAEtD,OAAO+B,OAAOC,WAAW,CACvBD,OAAOE,OAAO,CAACjC,SAASkC,OAAO,CAAC,CAAC,CAACC,MAAM1D,MAAM;QAC5C,IAAIA,UAAU2D,WAAW,OAAO,EAAE;QAClC,OAAO;YAAC;gBAACD,KAAK7D,WAAW;gBAAII,MAAMC,OAAO,CAACF,SAASA,MAAM4D,IAAI,CAAC,QAAQ5D;aAAM;SAAC;IAChF;AAEJ"}