@shipfox/api-integration-webhook 3.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.
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 6 files with swc (190.46ms)
2
+ Successfully compiled: 7 files with swc (311.89ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @shipfox/api-integration-webhook
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
+ - e3b6338: Adds the shared processor for generic webhook deliveries.
9
+
10
+ ### Patch Changes
11
+
12
+ - f73da5d: Enforces bounded API context imports and routes inter-module consumers through producer contracts.
13
+ - Updated dependencies [0bb82a4]
14
+ - Updated dependencies [f262539]
15
+ - Updated dependencies [3bb4e26]
16
+ - Updated dependencies [8bdc149]
17
+ - Updated dependencies [b00ed29]
18
+ - Updated dependencies [8aa7cd3]
19
+ - Updated dependencies [4604a06]
20
+ - @shipfox/api-integration-core-dto@6.0.0
21
+ - @shipfox/api-auth-context@6.0.0
22
+ - @shipfox/node-fastify@0.2.4
23
+ - @shipfox/api-integration-webhook-dto@6.0.0
24
+
25
+ ## 5.0.0
26
+
27
+ ### Patch Changes
28
+
29
+ - bb037af: Resolves workspace packages from source during development while published consumers continue to use compiled output.
30
+ - Updated dependencies [2875241]
31
+ - Updated dependencies [bb037af]
32
+ - Updated dependencies [fb70438]
33
+ - @shipfox/api-integration-core-dto@5.0.0
34
+ - @shipfox/api-auth-context@5.0.0
35
+ - @shipfox/api-integration-webhook-dto@5.0.0
36
+ - @shipfox/config@1.2.2
37
+ - @shipfox/node-fastify@0.2.3
38
+
3
39
  ## 3.0.0
4
40
 
5
41
  ### Patch Changes
@@ -1,4 +1,5 @@
1
1
  export declare const WEBHOOK_INBOUND_BODY_LIMIT: number;
2
+ export declare const WEBHOOK_ACCEPTED_CONTENT_TYPES: readonly ["application/json", "application/x-www-form-urlencoded", "text/plain"];
2
3
  export declare const WEBHOOK_FORWARDED_HEADERS: readonly ["content-type", "user-agent", "x-delivery-id", "x-request-id"];
3
4
  export declare function redactHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string | string[]>;
4
5
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,QAAkB,CAAC;AAE1D,eAAO,MAAM,yBAAyB,0EAK5B,CAAC;AAIX,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAOnC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,QAAkB,CAAC;AAE1D,eAAO,MAAM,8BAA8B,kFAIjC,CAAC;AAEX,eAAO,MAAM,yBAAyB,0EAK5B,CAAC;AAIX,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAOnC"}
package/dist/constants.js CHANGED
@@ -1,4 +1,9 @@
1
1
  export const WEBHOOK_INBOUND_BODY_LIMIT = 1 * 1024 * 1024;
2
+ export const WEBHOOK_ACCEPTED_CONTENT_TYPES = [
3
+ 'application/json',
4
+ 'application/x-www-form-urlencoded',
5
+ 'text/plain'
6
+ ];
2
7
  export const WEBHOOK_FORWARDED_HEADERS = [
3
8
  'content-type',
4
9
  'user-agent',
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["export const WEBHOOK_INBOUND_BODY_LIMIT = 1 * 1024 * 1024;\n\nexport const WEBHOOK_FORWARDED_HEADERS = [\n 'content-type',\n 'user-agent',\n 'x-delivery-id',\n 'x-request-id',\n] as const;\n\nconst forwardedHeaderSet = new Set<string>(WEBHOOK_FORWARDED_HEADERS);\n\nexport function redactHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string | string[]> {\n const redacted: Record<string, string | string[]> = {};\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n redacted[name] = forwardedHeaderSet.has(name.toLowerCase()) ? value : '[redacted]';\n }\n return redacted;\n}\n"],"names":["WEBHOOK_INBOUND_BODY_LIMIT","WEBHOOK_FORWARDED_HEADERS","forwardedHeaderSet","Set","redactHeaders","headers","redacted","name","value","Object","entries","undefined","has","toLowerCase"],"mappings":"AAAA,OAAO,MAAMA,6BAA6B,IAAI,OAAO,KAAK;AAE1D,OAAO,MAAMC,4BAA4B;IACvC;IACA;IACA;IACA;CACD,CAAU;AAEX,MAAMC,qBAAqB,IAAIC,IAAYF;AAE3C,OAAO,SAASG,cACdC,OAAsD;IAEtD,MAAMC,WAA8C,CAAC;IACrD,KAAK,MAAM,CAACC,MAAMC,MAAM,IAAIC,OAAOC,OAAO,CAACL,SAAU;QACnD,IAAIG,UAAUG,WAAW;QACzBL,QAAQ,CAACC,KAAK,GAAGL,mBAAmBU,GAAG,CAACL,KAAKM,WAAW,MAAML,QAAQ;IACxE;IACA,OAAOF;AACT"}
1
+ {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["export const WEBHOOK_INBOUND_BODY_LIMIT = 1 * 1024 * 1024;\n\nexport const WEBHOOK_ACCEPTED_CONTENT_TYPES = [\n 'application/json',\n 'application/x-www-form-urlencoded',\n 'text/plain',\n] as const;\n\nexport const WEBHOOK_FORWARDED_HEADERS = [\n 'content-type',\n 'user-agent',\n 'x-delivery-id',\n 'x-request-id',\n] as const;\n\nconst forwardedHeaderSet = new Set<string>(WEBHOOK_FORWARDED_HEADERS);\n\nexport function redactHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string | string[]> {\n const redacted: Record<string, string | string[]> = {};\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n redacted[name] = forwardedHeaderSet.has(name.toLowerCase()) ? value : '[redacted]';\n }\n return redacted;\n}\n"],"names":["WEBHOOK_INBOUND_BODY_LIMIT","WEBHOOK_ACCEPTED_CONTENT_TYPES","WEBHOOK_FORWARDED_HEADERS","forwardedHeaderSet","Set","redactHeaders","headers","redacted","name","value","Object","entries","undefined","has","toLowerCase"],"mappings":"AAAA,OAAO,MAAMA,6BAA6B,IAAI,OAAO,KAAK;AAE1D,OAAO,MAAMC,iCAAiC;IAC5C;IACA;IACA;CACD,CAAU;AAEX,OAAO,MAAMC,4BAA4B;IACvC;IACA;IACA;IACA;CACD,CAAU;AAEX,MAAMC,qBAAqB,IAAIC,IAAYF;AAE3C,OAAO,SAASG,cACdC,OAAsD;IAEtD,MAAMC,WAA8C,CAAC;IACrD,KAAK,MAAM,CAACC,MAAMC,MAAM,IAAIC,OAAOC,OAAO,CAACL,SAAU;QACnD,IAAIG,UAAUG,WAAW;QACzBL,QAAQ,CAACC,KAAK,GAAGL,mBAAmBU,GAAG,CAACL,KAAKM,WAAW,MAAML,QAAQ;IACxE;IACA,OAAOF;AACT"}
@@ -0,0 +1,13 @@
1
+ import { type GetIntegrationConnectionByIdFn, type IntegrationConnection, type IntegrationTx, type PublishIntegrationEventReceivedFn, type StoredWebhookRequest, type WebhookProcessingResult } from '@shipfox/api-integration-core-dto';
2
+ export interface CreateGenericWebhookProcessorOptions {
3
+ coreDb: () => {
4
+ transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;
5
+ };
6
+ getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
7
+ publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
8
+ }
9
+ export interface GenericWebhookProcessor {
10
+ process(request: StoredWebhookRequest, connection?: IntegrationConnection | undefined): Promise<WebhookProcessingResult>;
11
+ }
12
+ export declare function createGenericWebhookProcessor(options: CreateGenericWebhookProcessorOptions): GenericWebhookProcessor;
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,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"}
@@ -0,0 +1,110 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { decodeWebhookBody } from '@shipfox/api-integration-core-dto';
3
+ import { WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT } from '@shipfox/api-integration-webhook-dto';
4
+ import { logger } from '@shipfox/node-opentelemetry';
5
+ import { redactHeaders, WEBHOOK_ACCEPTED_CONTENT_TYPES } from '#constants.js';
6
+ const [JSON_CONTENT_TYPE, FORM_CONTENT_TYPE, TEXT_CONTENT_TYPE] = WEBHOOK_ACCEPTED_CONTENT_TYPES;
7
+ export function createGenericWebhookProcessor(options) {
8
+ return {
9
+ process: (request, connection)=>processWebhookRequest(options, request, connection)
10
+ };
11
+ }
12
+ async function processWebhookRequest(options, request, knownConnection) {
13
+ if (request.route_id !== 'webhook.connection') {
14
+ throw new Error(`Generic webhook processor cannot process ${request.route_id} requests`);
15
+ }
16
+ const connection = knownConnection ?? await options.getIntegrationConnectionById(request.path_parameters.connection_id);
17
+ if (!connection || connection.provider !== WEBHOOK_PROVIDER || connection.lifecycleStatus !== 'active') {
18
+ return {
19
+ outcome: 'discarded',
20
+ reason: 'connection_unavailable'
21
+ };
22
+ }
23
+ const deliveryId = request.headers['x-delivery-id'] ?? request.request_id;
24
+ const body = parseWebhookBody(request);
25
+ if (!body.success) {
26
+ logger().warn({
27
+ deliveryId,
28
+ err: body.error
29
+ }, 'generic webhook payload parsing failed');
30
+ return {
31
+ outcome: 'discarded',
32
+ reason: 'malformed_payload',
33
+ deliveryId
34
+ };
35
+ }
36
+ await options.coreDb().transaction(async (tx)=>{
37
+ await options.publishIntegrationEventReceived({
38
+ tx,
39
+ event: {
40
+ provider: WEBHOOK_PROVIDER,
41
+ source: connection.slug,
42
+ event: WEBHOOK_RECEIVED_EVENT,
43
+ workspaceId: connection.workspaceId,
44
+ connectionId: connection.id,
45
+ connectionName: connection.displayName,
46
+ deliveryId,
47
+ receivedAt: request.received_at,
48
+ payload: {
49
+ method: request.method,
50
+ headers: redactHeaders(request.headers),
51
+ query: parseWebhookQuery(request.raw_query_string),
52
+ body: body.value
53
+ }
54
+ }
55
+ });
56
+ });
57
+ return {
58
+ outcome: 'processed',
59
+ deliveryId
60
+ };
61
+ }
62
+ function parseWebhookQuery(rawQueryString) {
63
+ const query = {};
64
+ for (const [name, value] of new URLSearchParams(rawQueryString)){
65
+ const existingValue = query[name];
66
+ if (existingValue === undefined) {
67
+ query[name] = value;
68
+ } else if (Array.isArray(existingValue)) {
69
+ existingValue.push(value);
70
+ } else {
71
+ query[name] = [
72
+ existingValue,
73
+ value
74
+ ];
75
+ }
76
+ }
77
+ return query;
78
+ }
79
+ function parseWebhookBody(request) {
80
+ const body = Buffer.from(decodeWebhookBody(request.body));
81
+ const contentType = request.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase();
82
+ if (contentType === JSON_CONTENT_TYPE) {
83
+ try {
84
+ return {
85
+ success: true,
86
+ value: JSON.parse(body.toString('utf8'))
87
+ };
88
+ } catch (error) {
89
+ return {
90
+ success: false,
91
+ error
92
+ };
93
+ }
94
+ }
95
+ if (contentType === FORM_CONTENT_TYPE) {
96
+ return {
97
+ success: true,
98
+ value: Object.fromEntries(new URLSearchParams(body.toString('utf8')))
99
+ };
100
+ }
101
+ if (contentType === TEXT_CONTENT_TYPE) return {
102
+ success: true,
103
+ value: body.toString('utf8')
104
+ };
105
+ return {
106
+ success: false
107
+ };
108
+ }
109
+
110
+ //# 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 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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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { CreateIntegrationConnectionFn, DeleteIntegrationConnectionFn, GetIntegrationConnectionByIdFn, IntegrationConnection, IntegrationTx, PublishIntegrationEventReceivedFn, UpdateIntegrationConnectionLifecycleStatusFn } from '@shipfox/api-integration-core-dto';
2
2
  export { redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT } from '#constants.js';
3
+ export type { CreateGenericWebhookProcessorOptions, GenericWebhookProcessor, } from '#core/webhook-processor.js';
4
+ export { createGenericWebhookProcessor } from '#core/webhook-processor.js';
3
5
  export interface CreateWebhookIntegrationProviderOptions {
4
6
  coreDb: () => {
5
7
  transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;
@@ -18,5 +20,9 @@ export declare function createWebhookIntegrationProvider(options: CreateWebhookI
18
20
  provider: "webhook";
19
21
  displayName: string;
20
22
  routes: import("@shipfox/node-fastify").RouteGroup[];
23
+ webhookProcessors: {
24
+ routeIds: readonly ["webhook.connection"];
25
+ processor: import("#core/webhook-processor.js").GenericWebhookProcessor;
26
+ }[];
21
27
  };
22
28
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAM3C,OAAO,EAAC,aAAa,EAAE,yBAAyB,EAAE,0BAA0B,EAAC,MAAM,eAAe,CAAC;AAEnG,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;;;;EAsBhG"}
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"}
package/dist/index.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import { WEBHOOK_PROVIDER } from '@shipfox/api-integration-webhook-dto';
2
2
  import { config } from '#config.js';
3
+ import { createGenericWebhookProcessor } from '#core/webhook-processor.js';
3
4
  import { createWebhookConnectionRoutes } from '#presentation/routes/connections.js';
4
5
  import { createWebhookInboundRoutes } from '#presentation/routes/inbound.js';
5
6
  export { redactHeaders, WEBHOOK_FORWARDED_HEADERS, WEBHOOK_INBOUND_BODY_LIMIT } from '#constants.js';
7
+ export { createGenericWebhookProcessor } from '#core/webhook-processor.js';
6
8
  export function createWebhookIntegrationProvider(options) {
7
9
  const baseUrl = options.baseUrl ?? config.WEBHOOK_PUBLIC_URL;
10
+ const webhookProcessor = createGenericWebhookProcessor(options);
8
11
  return {
9
12
  provider: WEBHOOK_PROVIDER,
10
13
  displayName: 'Webhook',
@@ -20,8 +23,17 @@ export function createWebhookIntegrationProvider(options) {
20
23
  createWebhookInboundRoutes({
21
24
  coreDb: options.coreDb,
22
25
  getIntegrationConnectionById: options.getIntegrationConnectionById,
23
- publishIntegrationEventReceived: options.publishIntegrationEventReceived
26
+ publishIntegrationEventReceived: options.publishIntegrationEventReceived,
27
+ processor: webhookProcessor
24
28
  })
29
+ ],
30
+ webhookProcessors: [
31
+ {
32
+ routeIds: [
33
+ 'webhook.connection'
34
+ ],
35
+ processor: webhookProcessor
36
+ }
25
37
  ]
26
38
  };
27
39
  }
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 {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';\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 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 }),\n ],\n };\n}\n"],"names":["WEBHOOK_PROVIDER","config","createWebhookConnectionRoutes","createWebhookInboundRoutes","redactHeaders","WEBHOOK_FORWARDED_HEADERS","WEBHOOK_INBOUND_BODY_LIMIT","createWebhookIntegrationProvider","options","baseUrl","WEBHOOK_PUBLIC_URL","provider","displayName","routes","createIntegrationConnection","listIntegrationConnections","getIntegrationConnectionById","updateIntegrationConnectionLifecycleStatus","deleteIntegrationConnection","coreDb","publishIntegrationEventReceived"],"mappings":"AASA,SAAQA,gBAAgB,QAAO,uCAAuC;AACtE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,0BAA0B,QAAO,kCAAkC;AAE3E,SAAQC,aAAa,EAAEC,yBAAyB,EAAEC,0BAA0B,QAAO,gBAAgB;AAenG,OAAO,SAASC,iCAAiCC,OAAgD;IAC/F,MAAMC,UAAUD,QAAQC,OAAO,IAAIR,OAAOS,kBAAkB;IAC5D,OAAO;QACLC,UAAUX;QACVY,aAAa;QACbC,QAAQ;YACNX,8BAA8B;gBAC5BO;gBACAK,6BAA6BN,QAAQM,2BAA2B;gBAChEC,4BAA4BP,QAAQO,0BAA0B;gBAC9DC,8BAA8BR,QAAQQ,4BAA4B;gBAClEC,4CACET,QAAQS,0CAA0C;gBACpDC,6BAA6BV,QAAQU,2BAA2B;YAClE;YACAf,2BAA2B;gBACzBgB,QAAQX,QAAQW,MAAM;gBACtBH,8BAA8BR,QAAQQ,4BAA4B;gBAClEI,iCAAiCZ,QAAQY,+BAA+B;YAC1E;SACD;IACH;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-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,11 +1,13 @@
1
1
  import type { GetIntegrationConnectionByIdFn, IntegrationTx, PublishIntegrationEventReceivedFn } from '@shipfox/api-integration-core-dto';
2
2
  import { type RouteGroup } from '@shipfox/node-fastify';
3
+ import { type GenericWebhookProcessor } from '#core/webhook-processor.js';
3
4
  export interface CreateWebhookInboundRoutesOptions {
4
5
  coreDb: () => {
5
6
  transaction<T>(callback: (tx: IntegrationTx) => Promise<T>): Promise<T>;
6
7
  };
7
8
  getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
8
9
  publishIntegrationEventReceived: PublishIntegrationEventReceivedFn;
10
+ processor?: GenericWebhookProcessor | undefined;
9
11
  }
10
12
  export declare function createWebhookInboundRoutes(options: CreateWebhookInboundRoutesOptions): RouteGroup;
11
13
  //# sourceMappingURL=inbound.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/inbound.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,8BAA8B,EAC9B,aAAa,EACb,iCAAiC,EAClC,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAA2B,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AA0BhF,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;CACpE;AAyBD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,UAAU,CAiEjG"}
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,27 +1,24 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import formBodyPlugin from '@fastify/formbody';
3
- import { WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT } from '@shipfox/api-integration-webhook-dto';
2
+ import { createStoredWebhookRequest, WEBHOOK_MAX_RAW_BODY_BYTES } from '@shipfox/api-integration-core-dto';
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';
6
6
  import { z } from 'zod';
7
- import { redactHeaders, WEBHOOK_INBOUND_BODY_LIMIT } from '#constants.js';
7
+ import { WEBHOOK_ACCEPTED_CONTENT_TYPES, WEBHOOK_INBOUND_BODY_LIMIT } from '#constants.js';
8
+ import { createGenericWebhookProcessor } from '#core/webhook-processor.js';
8
9
  const MAX_DELIVERY_ID_HEADER_LENGTH = 200;
9
- const acceptedContentTypes = new Set([
10
- 'application/json',
11
- 'application/x-www-form-urlencoded',
12
- 'text/plain'
13
- ]);
14
- const FORM_URLENCODED_CONTENT_TYPE_RE = /^application\/x-www-form-urlencoded(?:;.*)?$/i;
15
- const formBodyRoutePlugin = fp(async (app)=>{
16
- await app.register(formBodyPlugin, {
17
- bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT
18
- });
19
- app.addContentTypeParser(FORM_URLENCODED_CONTENT_TYPE_RE, {
20
- parseAs: 'buffer',
21
- bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT
22
- }, (_request, body, done)=>{
23
- done(null, Object.fromEntries(new URLSearchParams(body.toString('utf8'))));
24
- });
10
+ const acceptedContentTypes = new Set(WEBHOOK_ACCEPTED_CONTENT_TYPES);
11
+ const rawWebhookBodyPlugin = fp((app)=>{
12
+ app.removeAllContentTypeParsers();
13
+ for (const contentType of acceptedContentTypes){
14
+ app.addContentTypeParser(contentType, {
15
+ parseAs: 'buffer',
16
+ bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT
17
+ }, (_request, body, done)=>{
18
+ done(null, body);
19
+ });
20
+ }
21
+ return Promise.resolve();
25
22
  });
26
23
  const inboundParamsSchema = z.object({
27
24
  connectionId: z.string().uuid()
@@ -43,7 +40,11 @@ function deliveryIdFor(header) {
43
40
  }
44
41
  return value;
45
42
  }
43
+ function hasDeliveryId(header) {
44
+ return Boolean(Array.isArray(header) ? header[0] : header);
45
+ }
46
46
  export function createWebhookInboundRoutes(options) {
47
+ const processor = options.processor ?? createGenericWebhookProcessor(options);
47
48
  const inboundRoute = defineRoute({
48
49
  method: 'POST',
49
50
  path: '/:connectionId',
@@ -73,29 +74,20 @@ export function createWebhookInboundRoutes(options) {
73
74
  status: 404
74
75
  });
75
76
  }
76
- const deliveryId = deliveryIdFor(request.headers['x-delivery-id']);
77
- const receivedAt = new Date().toISOString();
78
- await options.coreDb().transaction(async (tx)=>{
79
- await options.publishIntegrationEventReceived({
80
- tx,
81
- event: {
82
- provider: WEBHOOK_PROVIDER,
83
- source: connection.slug,
84
- event: WEBHOOK_RECEIVED_EVENT,
85
- workspaceId: connection.workspaceId,
86
- connectionId: connection.id,
87
- connectionName: connection.displayName,
88
- deliveryId,
89
- receivedAt,
90
- payload: {
91
- method: request.method,
92
- headers: redactHeaders(request.headers),
93
- query: request.query,
94
- body: request.body
95
- }
96
- }
77
+ const deliveryHeader = request.headers['x-delivery-id'];
78
+ const deliveryId = deliveryIdFor(deliveryHeader);
79
+ const result = await processor.process(createGenericStoredWebhookRequest({
80
+ body: request.body,
81
+ connectionId,
82
+ headers: request.headers,
83
+ rawQueryString: request.raw.url?.split('?')[1] ?? '',
84
+ requestId: hasDeliveryId(deliveryHeader) ? randomUUID() : deliveryId
85
+ }, new Date().toISOString()), connection);
86
+ if (result.outcome === 'discarded' && result.reason === 'malformed_payload') {
87
+ throw new ClientError('Malformed webhook payload', 'malformed-payload', {
88
+ status: 400
97
89
  });
98
- });
90
+ }
99
91
  reply.code(202);
100
92
  return {
101
93
  delivery_id: deliveryId
@@ -106,12 +98,50 @@ export function createWebhookInboundRoutes(options) {
106
98
  prefix: '/webhook',
107
99
  auth: [],
108
100
  plugins: [
109
- formBodyRoutePlugin
101
+ rawWebhookBodyPlugin
110
102
  ],
111
103
  routes: [
112
104
  inboundRoute
113
105
  ]
114
106
  };
115
107
  }
108
+ function createGenericStoredWebhookRequest(input, receivedAt) {
109
+ if (!(input.body instanceof Uint8Array)) {
110
+ throw new ClientError('Expected raw webhook body', 'invalid-webhook-request', {
111
+ status: 400
112
+ });
113
+ }
114
+ if (input.body.byteLength > WEBHOOK_MAX_RAW_BODY_BYTES) {
115
+ throw new ClientError('Webhook request body is too large', 'body-too-large', {
116
+ status: 413
117
+ });
118
+ }
119
+ try {
120
+ return createStoredWebhookRequest({
121
+ requestId: input.requestId,
122
+ routeId: 'webhook.connection',
123
+ receivedAt,
124
+ rawQueryString: input.rawQueryString,
125
+ headers: webhookHeaders(input.headers),
126
+ body: input.body,
127
+ connectionId: input.connectionId
128
+ });
129
+ } catch (error) {
130
+ throw new ClientError('Webhook request metadata is invalid', 'invalid-webhook-request', {
131
+ cause: error
132
+ });
133
+ }
134
+ }
135
+ function webhookHeaders(headers) {
136
+ return Object.fromEntries(Object.entries(headers).flatMap(([name, value])=>{
137
+ if (value === undefined) return [];
138
+ return [
139
+ [
140
+ name.toLowerCase(),
141
+ Array.isArray(value) ? value.join(', ') : value
142
+ ]
143
+ ];
144
+ }));
145
+ }
116
146
 
117
147
  //# sourceMappingURL=inbound.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/inbound.ts"],"sourcesContent":["import type {Buffer} from 'node:buffer';\nimport {randomUUID} from 'node:crypto';\nimport formBodyPlugin from '@fastify/formbody';\nimport type {\n GetIntegrationConnectionByIdFn,\n IntegrationTx,\n PublishIntegrationEventReceivedFn,\n} from '@shipfox/api-integration-core-dto';\nimport {WEBHOOK_PROVIDER, WEBHOOK_RECEIVED_EVENT} 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 {redactHeaders, WEBHOOK_INBOUND_BODY_LIMIT} from '#constants.js';\n\nconst MAX_DELIVERY_ID_HEADER_LENGTH = 200;\nconst acceptedContentTypes = new Set([\n 'application/json',\n 'application/x-www-form-urlencoded',\n 'text/plain',\n]);\nconst FORM_URLENCODED_CONTENT_TYPE_RE = /^application\\/x-www-form-urlencoded(?:;.*)?$/i;\n\nconst formBodyRoutePlugin: FastifyPluginAsync = fp(async (app) => {\n await app.register(formBodyPlugin, {bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT});\n\n app.addContentTypeParser(\n FORM_URLENCODED_CONTENT_TYPE_RE,\n {parseAs: 'buffer', bodyLimit: WEBHOOK_INBOUND_BODY_LIMIT},\n (_request, body: Buffer, done) => {\n done(null, Object.fromEntries(new URLSearchParams(body.toString('utf8'))));\n },\n );\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}\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\nexport function createWebhookInboundRoutes(options: CreateWebhookInboundRoutesOptions): RouteGroup {\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 deliveryId = deliveryIdFor(request.headers['x-delivery-id']);\n const receivedAt = new Date().toISOString();\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,\n payload: {\n method: request.method,\n headers: redactHeaders(request.headers),\n query: request.query,\n body: request.body,\n },\n },\n });\n });\n\n reply.code(202);\n return {delivery_id: deliveryId};\n },\n });\n\n return {\n prefix: '/webhook',\n auth: [],\n plugins: [formBodyRoutePlugin],\n routes: [inboundRoute],\n };\n}\n"],"names":["randomUUID","formBodyPlugin","WEBHOOK_PROVIDER","WEBHOOK_RECEIVED_EVENT","ClientError","defineRoute","fp","z","redactHeaders","WEBHOOK_INBOUND_BODY_LIMIT","MAX_DELIVERY_ID_HEADER_LENGTH","acceptedContentTypes","Set","FORM_URLENCODED_CONTENT_TYPE_RE","formBodyRoutePlugin","app","register","bodyLimit","addContentTypeParser","parseAs","_request","body","done","Object","fromEntries","URLSearchParams","toString","inboundParamsSchema","object","connectionId","string","uuid","inboundQuerySchema","record","unknown","inboundAcceptedResponseSchema","delivery_id","contentType","requestContentType","split","trim","toLowerCase","deliveryIdFor","header","value","Array","isArray","length","status","createWebhookInboundRoutes","options","inboundRoute","method","path","auth","description","schema","params","querystring","response","handler","request","reply","mediaType","headers","has","connection","getIntegrationConnectionById","provider","lifecycleStatus","deliveryId","receivedAt","Date","toISOString","coreDb","transaction","tx","publishIntegrationEventReceived","event","source","slug","workspaceId","id","connectionName","displayName","payload","query","code","prefix","plugins","routes"],"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-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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"}