@shipfox/api-integration-core-dto 9.0.2 → 12.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.
@@ -4,7 +4,7 @@ export declare const WEBHOOK_MAX_RAW_BODY_BYTES: number;
4
4
  export declare const WEBHOOK_MAX_SERIALIZED_REQUEST_BYTES: number;
5
5
  export declare const WEBHOOK_MAX_HEADER_BYTES: number;
6
6
  export declare const WEBHOOK_MAX_RAW_QUERY_STRING_LENGTH: number;
7
- export declare const webhookRouteIds: readonly ["github", "gitea", "linear", "sentry", "slack.event", "slack.command", "webhook.connection"];
7
+ export declare const webhookRouteIds: readonly ["github", "gitea", "linear", "sentry", "slack.event", "slack.command", "webhook.connection", "jira"];
8
8
  export declare const webhookRouteIdSchema: z.ZodEnum<{
9
9
  github: "github";
10
10
  gitea: "gitea";
@@ -13,6 +13,7 @@ export declare const webhookRouteIdSchema: z.ZodEnum<{
13
13
  "slack.event": "slack.event";
14
14
  "slack.command": "slack.command";
15
15
  "webhook.connection": "webhook.connection";
16
+ jira: "jira";
16
17
  }>;
17
18
  export type WebhookRouteId = z.infer<typeof webhookRouteIdSchema>;
18
19
  declare const webhookBodySchema: z.ZodObject<{
@@ -54,6 +55,21 @@ export declare const storedWebhookRequestSchema: z.ZodDiscriminatedUnion<[z.ZodO
54
55
  path_parameters: z.ZodObject<{
55
56
  connection_id: z.ZodString;
56
57
  }, z.core.$strict>;
58
+ }, z.core.$strict>, z.ZodObject<{
59
+ schema_version: z.ZodLiteral<1>;
60
+ request_id: z.ZodString;
61
+ received_at: z.ZodString;
62
+ method: z.ZodLiteral<"POST">;
63
+ raw_query_string: z.ZodString;
64
+ headers: z.ZodRecord<z.ZodString, z.ZodString>;
65
+ body: z.ZodObject<{
66
+ encoding: z.ZodLiteral<"base64">;
67
+ data: z.ZodString;
68
+ }, z.core.$strict>;
69
+ route_id: z.ZodLiteral<"jira">;
70
+ path_parameters: z.ZodObject<{
71
+ connection_id: z.ZodString;
72
+ }, z.core.$strict>;
57
73
  }, z.core.$strict>], "route_id">;
58
74
  export type StoredWebhookRequest = z.infer<typeof storedWebhookRequestSchema>;
59
75
  export declare const webhookProcessingResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -76,15 +92,22 @@ export declare const webhookProcessingResultSchema: z.ZodDiscriminatedUnion<[z.Z
76
92
  deliveryId: z.ZodOptional<z.ZodString>;
77
93
  }, z.core.$strict>], "outcome">;
78
94
  export type WebhookProcessingResult = z.infer<typeof webhookProcessingResultSchema>;
79
- export interface CreateStoredWebhookRequestInput {
95
+ interface CreateStoredWebhookRequestInputBase {
80
96
  requestId: string;
81
97
  routeId: WebhookRouteId;
82
98
  receivedAt: string;
83
99
  rawQueryString: string;
84
100
  headers: Record<string, string>;
85
101
  body: Uint8Array;
86
- connectionId?: string | undefined;
87
102
  }
103
+ type ConnectionScopedWebhookRouteId = 'webhook.connection' | 'jira';
104
+ export type CreateStoredWebhookRequestInput = (CreateStoredWebhookRequestInputBase & {
105
+ routeId: ConnectionScopedWebhookRouteId;
106
+ connectionId: string;
107
+ }) | (CreateStoredWebhookRequestInputBase & {
108
+ routeId: Exclude<WebhookRouteId, ConnectionScopedWebhookRouteId>;
109
+ connectionId?: never;
110
+ });
88
111
  export declare function encodeWebhookBody(body: Uint8Array): string;
89
112
  export declare function decodeWebhookBody(body: z.infer<typeof webhookBodySchema>): Uint8Array;
90
113
  export declare function createStoredWebhookRequest(input: CreateStoredWebhookRequestInput): StoredWebhookRequest;
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,eAAO,MAAM,8BAA8B,EAAG,CAAU,CAAC;AACzD,eAAO,MAAM,0BAA0B,QAAa,CAAC;AACrD,eAAO,MAAM,oCAAoC,QAAc,CAAC;AAChE,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAClD,eAAO,MAAM,mCAAmC,QAAW,CAAC;AAW5D,eAAO,MAAM,eAAe,wGAA8D,CAAC;AAE3F,eAAO,MAAM,oBAAoB;;;;;;;;EAA0B,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AA4BlE,QAAA,MAAM,iBAAiB;;;kBAYpB,CAAC;AA6BJ,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAGrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;+BAuBxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA0B1D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,GAAG,UAAU,CAyBrF;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,oBAAoB,CAetB"}
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,eAAO,MAAM,8BAA8B,EAAG,CAAU,CAAC;AACzD,eAAO,MAAM,0BAA0B,QAAa,CAAC;AACrD,eAAO,MAAM,oCAAoC,QAAc,CAAC;AAChE,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAClD,eAAO,MAAM,mCAAmC,QAAW,CAAC;AAW5D,eAAO,MAAM,eAAe,gHAAsE,CAAC;AAEnG,eAAO,MAAM,oBAAoB;;;;;;;;;EAA0B,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AA4BlE,QAAA,MAAM,iBAAiB;;;kBAYpB,CAAC;AAoCJ,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAIrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;+BAuBxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,UAAU,mCAAmC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,KAAK,8BAA8B,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEpE,MAAM,MAAM,+BAA+B,GACvC,CAAC,mCAAmC,GAAG;IACrC,OAAO,EAAE,8BAA8B,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,GACF,CAAC,mCAAmC,GAAG;IACrC,OAAO,EAAE,OAAO,CAAC,cAAc,EAAE,8BAA8B,CAAC,CAAC;IACjE,YAAY,CAAC,EAAE,KAAK,CAAC;CACtB,CAAC,CAAC;AAEP,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA0B1D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,GAAG,UAAU,CAyBrF;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,oBAAoB,CAiBtB"}
package/dist/webhooks.js CHANGED
@@ -14,7 +14,8 @@ const standardWebhookRouteIds = [
14
14
  ];
15
15
  export const webhookRouteIds = [
16
16
  ...standardWebhookRouteIds,
17
- 'webhook.connection'
17
+ 'webhook.connection',
18
+ 'jira'
18
19
  ];
19
20
  export const webhookRouteIdSchema = z.enum(webhookRouteIds);
20
21
  const base64Pattern = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
@@ -53,9 +54,14 @@ const genericWebhookRouteRequestSchema = storedWebhookRequestBaseSchema.extend({
53
54
  route_id: z.literal('webhook.connection'),
54
55
  path_parameters: connectionPathParametersSchema
55
56
  }).strict();
57
+ const jiraWebhookRouteRequestSchema = storedWebhookRequestBaseSchema.extend({
58
+ route_id: z.literal('jira'),
59
+ path_parameters: connectionPathParametersSchema
60
+ }).strict();
56
61
  export const storedWebhookRequestSchema = z.discriminatedUnion('route_id', [
57
62
  standardWebhookRouteRequestSchema,
58
- genericWebhookRouteRequestSchema
63
+ genericWebhookRouteRequestSchema,
64
+ jiraWebhookRouteRequestSchema
59
65
  ]);
60
66
  export const webhookProcessingResultSchema = z.discriminatedUnion('outcome', [
61
67
  z.object({
@@ -112,7 +118,7 @@ export function decodeWebhookBody(body) {
112
118
  return output;
113
119
  }
114
120
  export function createStoredWebhookRequest(input) {
115
- const pathParameters = input.routeId === 'webhook.connection' ? {
121
+ const pathParameters = input.routeId === 'webhook.connection' || input.routeId === 'jira' ? {
116
122
  connection_id: input.connectionId
117
123
  } : {};
118
124
  return storedWebhookRequestSchema.parse({
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/webhooks.ts"],"sourcesContent":["import {z} from 'zod';\n\nexport const WEBHOOK_REQUEST_SCHEMA_VERSION = 1 as const;\nexport const WEBHOOK_MAX_RAW_BODY_BYTES = 512 * 1024;\nexport const WEBHOOK_MAX_SERIALIZED_REQUEST_BYTES = 1024 * 1024;\nexport const WEBHOOK_MAX_HEADER_BYTES = 64 * 1024;\nexport const WEBHOOK_MAX_RAW_QUERY_STRING_LENGTH = 8 * 1024;\n\nconst standardWebhookRouteIds = [\n 'github',\n 'gitea',\n 'linear',\n 'sentry',\n 'slack.event',\n 'slack.command',\n] as const;\n\nexport const webhookRouteIds = [...standardWebhookRouteIds, 'webhook.connection'] as const;\n\nexport const webhookRouteIdSchema = z.enum(webhookRouteIds);\nexport type WebhookRouteId = z.infer<typeof webhookRouteIdSchema>;\n\nconst base64Pattern = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;\n\nfunction decodedBase64Length(value: string): number {\n const paddingLength = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0;\n\n return (value.length / 4) * 3 - paddingLength;\n}\n\nfunction headersByteLength(headers: Record<string, string>): number {\n return new TextEncoder().encode(JSON.stringify(headers)).byteLength;\n}\n\nconst webhookHeadersSchema = z\n .record(z.string(), z.string().max(8 * 1024))\n .refine(\n (headers) =>\n Object.keys(headers).every(\n (name) => name === name.toLowerCase() && name.length > 0 && name.length <= 128,\n ),\n 'Webhook header names must be lowercase and at most 128 characters',\n )\n .refine(\n (headers) => headersByteLength(headers) <= WEBHOOK_MAX_HEADER_BYTES,\n `Webhook headers must serialize to at most ${WEBHOOK_MAX_HEADER_BYTES} bytes`,\n );\n\nconst webhookBodySchema = z\n .object({\n encoding: z.literal('base64'),\n data: z\n .string()\n .max(Math.ceil(WEBHOOK_MAX_RAW_BODY_BYTES / 3) * 4)\n .regex(base64Pattern),\n })\n .strict()\n .refine(\n (body) => decodedBase64Length(body.data) <= WEBHOOK_MAX_RAW_BODY_BYTES,\n `Webhook bodies must decode to at most ${WEBHOOK_MAX_RAW_BODY_BYTES} bytes`,\n );\n\nconst storedWebhookRequestBaseSchema = z.object({\n schema_version: z.literal(WEBHOOK_REQUEST_SCHEMA_VERSION),\n request_id: z.string().uuid(),\n received_at: z.string().datetime({offset: true}),\n method: z.literal('POST'),\n raw_query_string: z.string().max(WEBHOOK_MAX_RAW_QUERY_STRING_LENGTH),\n headers: webhookHeadersSchema,\n body: webhookBodySchema,\n});\n\nconst emptyPathParametersSchema = z.object({}).strict();\nconst connectionPathParametersSchema = z.object({connection_id: z.string().uuid()}).strict();\n\nconst standardWebhookRouteRequestSchema = storedWebhookRequestBaseSchema\n .extend({\n route_id: z.enum(standardWebhookRouteIds),\n path_parameters: emptyPathParametersSchema,\n })\n .strict();\n\nconst genericWebhookRouteRequestSchema = storedWebhookRequestBaseSchema\n .extend({\n route_id: z.literal('webhook.connection'),\n path_parameters: connectionPathParametersSchema,\n })\n .strict();\n\nexport const storedWebhookRequestSchema = z.discriminatedUnion('route_id', [\n standardWebhookRouteRequestSchema,\n genericWebhookRouteRequestSchema,\n]);\nexport type StoredWebhookRequest = z.infer<typeof storedWebhookRequestSchema>;\n\nexport const webhookProcessingResultSchema = z.discriminatedUnion('outcome', [\n z\n .object({\n outcome: z.literal('processed'),\n deliveryId: z.string().min(1).optional(),\n challenge: z.string().min(1).optional(),\n })\n .strict(),\n z.object({outcome: z.literal('duplicate'), deliveryId: z.string().min(1)}).strict(),\n z\n .object({\n outcome: z.literal('discarded'),\n reason: z.enum([\n 'missing_required_input',\n 'invalid_signature',\n 'stale_at_receipt',\n 'malformed_payload',\n 'unsupported_event',\n 'connection_unavailable',\n ]),\n deliveryId: z.string().min(1).optional(),\n })\n .strict(),\n]);\nexport type WebhookProcessingResult = z.infer<typeof webhookProcessingResultSchema>;\n\nexport interface CreateStoredWebhookRequestInput {\n requestId: string;\n routeId: WebhookRouteId;\n receivedAt: string;\n rawQueryString: string;\n headers: Record<string, string>;\n body: Uint8Array;\n connectionId?: string | undefined;\n}\n\nexport function encodeWebhookBody(body: Uint8Array): string {\n let encoded = '';\n\n for (let index = 0; index < body.length; index += 3) {\n const first = body[index] ?? 0;\n const second = body[index + 1];\n const third = body[index + 2];\n const combined = (first << 16) | ((second ?? 0) << 8) | (third ?? 0);\n\n encoded += 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[\n (combined >> 18) & 63\n ];\n encoded += 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[\n (combined >> 12) & 63\n ];\n encoded +=\n second === undefined\n ? '='\n : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[(combined >> 6) & 63];\n encoded +=\n third === undefined\n ? '='\n : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[combined & 63];\n }\n\n return encoded;\n}\n\nexport function decodeWebhookBody(body: z.infer<typeof webhookBodySchema>): Uint8Array {\n const output = new Uint8Array(decodedBase64Length(body.data));\n let outputIndex = 0;\n\n for (let index = 0; index < body.data.length; index += 4) {\n const chunk = body.data.slice(index, index + 4);\n const values = chunk\n .split('')\n .map((character) =>\n character === '='\n ? 0\n : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.indexOf(character),\n );\n const first = values[0] ?? 0;\n const second = values[1] ?? 0;\n const third = values[2] ?? 0;\n const fourth = values[3] ?? 0;\n const combined = (first << 18) | (second << 12) | (third << 6) | fourth;\n\n output[outputIndex++] = (combined >> 16) & 255;\n if (chunk[2] !== '=') output[outputIndex++] = (combined >> 8) & 255;\n if (chunk[3] !== '=') output[outputIndex++] = combined & 255;\n }\n\n return output;\n}\n\nexport function createStoredWebhookRequest(\n input: CreateStoredWebhookRequestInput,\n): StoredWebhookRequest {\n const pathParameters =\n input.routeId === 'webhook.connection' ? {connection_id: input.connectionId} : {};\n\n return storedWebhookRequestSchema.parse({\n schema_version: WEBHOOK_REQUEST_SCHEMA_VERSION,\n request_id: input.requestId,\n route_id: input.routeId,\n received_at: input.receivedAt,\n method: 'POST',\n path_parameters: pathParameters,\n raw_query_string: input.rawQueryString,\n headers: input.headers,\n body: {encoding: 'base64', data: encodeWebhookBody(input.body)},\n });\n}\n"],"names":["z","WEBHOOK_REQUEST_SCHEMA_VERSION","WEBHOOK_MAX_RAW_BODY_BYTES","WEBHOOK_MAX_SERIALIZED_REQUEST_BYTES","WEBHOOK_MAX_HEADER_BYTES","WEBHOOK_MAX_RAW_QUERY_STRING_LENGTH","standardWebhookRouteIds","webhookRouteIds","webhookRouteIdSchema","enum","base64Pattern","decodedBase64Length","value","paddingLength","endsWith","length","headersByteLength","headers","TextEncoder","encode","JSON","stringify","byteLength","webhookHeadersSchema","record","string","max","refine","Object","keys","every","name","toLowerCase","webhookBodySchema","object","encoding","literal","data","Math","ceil","regex","strict","body","storedWebhookRequestBaseSchema","schema_version","request_id","uuid","received_at","datetime","offset","method","raw_query_string","emptyPathParametersSchema","connectionPathParametersSchema","connection_id","standardWebhookRouteRequestSchema","extend","route_id","path_parameters","genericWebhookRouteRequestSchema","storedWebhookRequestSchema","discriminatedUnion","webhookProcessingResultSchema","outcome","deliveryId","min","optional","challenge","reason","encodeWebhookBody","encoded","index","first","second","third","combined","undefined","decodeWebhookBody","output","Uint8Array","outputIndex","chunk","slice","values","split","map","character","indexOf","fourth","createStoredWebhookRequest","input","pathParameters","routeId","connectionId","parse","requestId","receivedAt","rawQueryString"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/webhooks.ts"],"sourcesContent":["import {z} from 'zod';\n\nexport const WEBHOOK_REQUEST_SCHEMA_VERSION = 1 as const;\nexport const WEBHOOK_MAX_RAW_BODY_BYTES = 512 * 1024;\nexport const WEBHOOK_MAX_SERIALIZED_REQUEST_BYTES = 1024 * 1024;\nexport const WEBHOOK_MAX_HEADER_BYTES = 64 * 1024;\nexport const WEBHOOK_MAX_RAW_QUERY_STRING_LENGTH = 8 * 1024;\n\nconst standardWebhookRouteIds = [\n 'github',\n 'gitea',\n 'linear',\n 'sentry',\n 'slack.event',\n 'slack.command',\n] as const;\n\nexport const webhookRouteIds = [...standardWebhookRouteIds, 'webhook.connection', 'jira'] as const;\n\nexport const webhookRouteIdSchema = z.enum(webhookRouteIds);\nexport type WebhookRouteId = z.infer<typeof webhookRouteIdSchema>;\n\nconst base64Pattern = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;\n\nfunction decodedBase64Length(value: string): number {\n const paddingLength = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0;\n\n return (value.length / 4) * 3 - paddingLength;\n}\n\nfunction headersByteLength(headers: Record<string, string>): number {\n return new TextEncoder().encode(JSON.stringify(headers)).byteLength;\n}\n\nconst webhookHeadersSchema = z\n .record(z.string(), z.string().max(8 * 1024))\n .refine(\n (headers) =>\n Object.keys(headers).every(\n (name) => name === name.toLowerCase() && name.length > 0 && name.length <= 128,\n ),\n 'Webhook header names must be lowercase and at most 128 characters',\n )\n .refine(\n (headers) => headersByteLength(headers) <= WEBHOOK_MAX_HEADER_BYTES,\n `Webhook headers must serialize to at most ${WEBHOOK_MAX_HEADER_BYTES} bytes`,\n );\n\nconst webhookBodySchema = z\n .object({\n encoding: z.literal('base64'),\n data: z\n .string()\n .max(Math.ceil(WEBHOOK_MAX_RAW_BODY_BYTES / 3) * 4)\n .regex(base64Pattern),\n })\n .strict()\n .refine(\n (body) => decodedBase64Length(body.data) <= WEBHOOK_MAX_RAW_BODY_BYTES,\n `Webhook bodies must decode to at most ${WEBHOOK_MAX_RAW_BODY_BYTES} bytes`,\n );\n\nconst storedWebhookRequestBaseSchema = z.object({\n schema_version: z.literal(WEBHOOK_REQUEST_SCHEMA_VERSION),\n request_id: z.string().uuid(),\n received_at: z.string().datetime({offset: true}),\n method: z.literal('POST'),\n raw_query_string: z.string().max(WEBHOOK_MAX_RAW_QUERY_STRING_LENGTH),\n headers: webhookHeadersSchema,\n body: webhookBodySchema,\n});\n\nconst emptyPathParametersSchema = z.object({}).strict();\nconst connectionPathParametersSchema = z.object({connection_id: z.string().uuid()}).strict();\n\nconst standardWebhookRouteRequestSchema = storedWebhookRequestBaseSchema\n .extend({\n route_id: z.enum(standardWebhookRouteIds),\n path_parameters: emptyPathParametersSchema,\n })\n .strict();\n\nconst genericWebhookRouteRequestSchema = storedWebhookRequestBaseSchema\n .extend({\n route_id: z.literal('webhook.connection'),\n path_parameters: connectionPathParametersSchema,\n })\n .strict();\n\nconst jiraWebhookRouteRequestSchema = storedWebhookRequestBaseSchema\n .extend({\n route_id: z.literal('jira'),\n path_parameters: connectionPathParametersSchema,\n })\n .strict();\n\nexport const storedWebhookRequestSchema = z.discriminatedUnion('route_id', [\n standardWebhookRouteRequestSchema,\n genericWebhookRouteRequestSchema,\n jiraWebhookRouteRequestSchema,\n]);\nexport type StoredWebhookRequest = z.infer<typeof storedWebhookRequestSchema>;\n\nexport const webhookProcessingResultSchema = z.discriminatedUnion('outcome', [\n z\n .object({\n outcome: z.literal('processed'),\n deliveryId: z.string().min(1).optional(),\n challenge: z.string().min(1).optional(),\n })\n .strict(),\n z.object({outcome: z.literal('duplicate'), deliveryId: z.string().min(1)}).strict(),\n z\n .object({\n outcome: z.literal('discarded'),\n reason: z.enum([\n 'missing_required_input',\n 'invalid_signature',\n 'stale_at_receipt',\n 'malformed_payload',\n 'unsupported_event',\n 'connection_unavailable',\n ]),\n deliveryId: z.string().min(1).optional(),\n })\n .strict(),\n]);\nexport type WebhookProcessingResult = z.infer<typeof webhookProcessingResultSchema>;\n\ninterface CreateStoredWebhookRequestInputBase {\n requestId: string;\n routeId: WebhookRouteId;\n receivedAt: string;\n rawQueryString: string;\n headers: Record<string, string>;\n body: Uint8Array;\n}\n\ntype ConnectionScopedWebhookRouteId = 'webhook.connection' | 'jira';\n\nexport type CreateStoredWebhookRequestInput =\n | (CreateStoredWebhookRequestInputBase & {\n routeId: ConnectionScopedWebhookRouteId;\n connectionId: string;\n })\n | (CreateStoredWebhookRequestInputBase & {\n routeId: Exclude<WebhookRouteId, ConnectionScopedWebhookRouteId>;\n connectionId?: never;\n });\n\nexport function encodeWebhookBody(body: Uint8Array): string {\n let encoded = '';\n\n for (let index = 0; index < body.length; index += 3) {\n const first = body[index] ?? 0;\n const second = body[index + 1];\n const third = body[index + 2];\n const combined = (first << 16) | ((second ?? 0) << 8) | (third ?? 0);\n\n encoded += 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[\n (combined >> 18) & 63\n ];\n encoded += 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[\n (combined >> 12) & 63\n ];\n encoded +=\n second === undefined\n ? '='\n : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[(combined >> 6) & 63];\n encoded +=\n third === undefined\n ? '='\n : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[combined & 63];\n }\n\n return encoded;\n}\n\nexport function decodeWebhookBody(body: z.infer<typeof webhookBodySchema>): Uint8Array {\n const output = new Uint8Array(decodedBase64Length(body.data));\n let outputIndex = 0;\n\n for (let index = 0; index < body.data.length; index += 4) {\n const chunk = body.data.slice(index, index + 4);\n const values = chunk\n .split('')\n .map((character) =>\n character === '='\n ? 0\n : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.indexOf(character),\n );\n const first = values[0] ?? 0;\n const second = values[1] ?? 0;\n const third = values[2] ?? 0;\n const fourth = values[3] ?? 0;\n const combined = (first << 18) | (second << 12) | (third << 6) | fourth;\n\n output[outputIndex++] = (combined >> 16) & 255;\n if (chunk[2] !== '=') output[outputIndex++] = (combined >> 8) & 255;\n if (chunk[3] !== '=') output[outputIndex++] = combined & 255;\n }\n\n return output;\n}\n\nexport function createStoredWebhookRequest(\n input: CreateStoredWebhookRequestInput,\n): StoredWebhookRequest {\n const pathParameters =\n input.routeId === 'webhook.connection' || input.routeId === 'jira'\n ? {connection_id: input.connectionId}\n : {};\n\n return storedWebhookRequestSchema.parse({\n schema_version: WEBHOOK_REQUEST_SCHEMA_VERSION,\n request_id: input.requestId,\n route_id: input.routeId,\n received_at: input.receivedAt,\n method: 'POST',\n path_parameters: pathParameters,\n raw_query_string: input.rawQueryString,\n headers: input.headers,\n body: {encoding: 'base64', data: encodeWebhookBody(input.body)},\n });\n}\n"],"names":["z","WEBHOOK_REQUEST_SCHEMA_VERSION","WEBHOOK_MAX_RAW_BODY_BYTES","WEBHOOK_MAX_SERIALIZED_REQUEST_BYTES","WEBHOOK_MAX_HEADER_BYTES","WEBHOOK_MAX_RAW_QUERY_STRING_LENGTH","standardWebhookRouteIds","webhookRouteIds","webhookRouteIdSchema","enum","base64Pattern","decodedBase64Length","value","paddingLength","endsWith","length","headersByteLength","headers","TextEncoder","encode","JSON","stringify","byteLength","webhookHeadersSchema","record","string","max","refine","Object","keys","every","name","toLowerCase","webhookBodySchema","object","encoding","literal","data","Math","ceil","regex","strict","body","storedWebhookRequestBaseSchema","schema_version","request_id","uuid","received_at","datetime","offset","method","raw_query_string","emptyPathParametersSchema","connectionPathParametersSchema","connection_id","standardWebhookRouteRequestSchema","extend","route_id","path_parameters","genericWebhookRouteRequestSchema","jiraWebhookRouteRequestSchema","storedWebhookRequestSchema","discriminatedUnion","webhookProcessingResultSchema","outcome","deliveryId","min","optional","challenge","reason","encodeWebhookBody","encoded","index","first","second","third","combined","undefined","decodeWebhookBody","output","Uint8Array","outputIndex","chunk","slice","values","split","map","character","indexOf","fourth","createStoredWebhookRequest","input","pathParameters","routeId","connectionId","parse","requestId","receivedAt","rawQueryString"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/api-integration-core-dto",
3
3
  "license": "MIT",
4
- "version": "9.0.2",
4
+ "version": "12.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "zod": "^4.4.3",
26
- "@shipfox/inter-module": "0.2.2"
26
+ "@shipfox/inter-module": "0.2.3"
27
27
  },
28
28
  "imports": {
29
29
  "#*": "./dist/*"
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  INTEGRATION_EVENT_RECEIVED,
3
3
  INTEGRATION_SOURCE_COMMIT_PUSHED,
4
+ INTEGRATION_SOURCE_REPOSITORY_UPDATED,
4
5
  integrationEventReceivedSchema,
5
6
  integrationSourceCommitPushedSchema,
7
+ integrationSourceRepositoryUpdatedSchema,
6
8
  integrationsEventSchemas,
7
9
  } from './events.js';
8
10
 
@@ -33,6 +35,20 @@ const validCommitPushed = {
33
35
  },
34
36
  };
35
37
 
38
+ const validRepositoryUpdated = {
39
+ provider: 'github',
40
+ workspaceId: 'ws-1',
41
+ connectionId: 'conn-1',
42
+ deliveryId: 'delivery-1',
43
+ receivedAt: '2026-06-21T00:00:00.000Z',
44
+ repository: {
45
+ externalRepositoryId: 'github:42',
46
+ owner: 'acme',
47
+ name: 'platform-renamed',
48
+ defaultBranch: 'main',
49
+ },
50
+ };
51
+
36
52
  describe('integrationSourceCommitPushedSchema', () => {
37
53
  it('parses a valid commit-pushed payload unchanged', () => {
38
54
  const result = integrationSourceCommitPushedSchema.parse(validCommitPushed);
@@ -98,12 +114,32 @@ describe('integrationEventReceivedSchema', () => {
98
114
  });
99
115
  });
100
116
 
117
+ describe('integrationSourceRepositoryUpdatedSchema', () => {
118
+ it('parses a valid repository identity update unchanged', () => {
119
+ const result = integrationSourceRepositoryUpdatedSchema.parse(validRepositoryUpdated);
120
+
121
+ expect(result).toEqual(validRepositoryUpdated);
122
+ });
123
+
124
+ it('rejects a payload missing repository identity', () => {
125
+ const {repository: _repository, ...withoutRepository} = validRepositoryUpdated;
126
+
127
+ const parse = () => integrationSourceRepositoryUpdatedSchema.parse(withoutRepository);
128
+
129
+ expect(parse).toThrow();
130
+ });
131
+ });
132
+
101
133
  describe('integrationsEventSchemas', () => {
102
134
  it('registers every integration publisher event type', () => {
103
135
  const registeredTypes = Object.keys(integrationsEventSchemas).sort();
104
136
 
105
137
  expect(registeredTypes).toEqual(
106
- [INTEGRATION_EVENT_RECEIVED, INTEGRATION_SOURCE_COMMIT_PUSHED].sort(),
138
+ [
139
+ INTEGRATION_EVENT_RECEIVED,
140
+ INTEGRATION_SOURCE_COMMIT_PUSHED,
141
+ INTEGRATION_SOURCE_REPOSITORY_UPDATED,
142
+ ].sort(),
107
143
  );
108
144
  });
109
145
  });
package/src/events.ts CHANGED
@@ -51,6 +51,32 @@ export type IntegrationSourceCommitPushedEvent = z.infer<
51
51
  typeof integrationSourceCommitPushedSchema
52
52
  >;
53
53
 
54
+ export const sourceRepositoryIdentitySchema = z.object({
55
+ externalRepositoryId: nonEmptyStringSchema,
56
+ owner: nonEmptyStringSchema,
57
+ name: nonEmptyStringSchema,
58
+ defaultBranch: nonEmptyStringSchema,
59
+ });
60
+ export type SourceRepositoryIdentity = z.infer<typeof sourceRepositoryIdentitySchema>;
61
+
62
+ export const INTEGRATION_SOURCE_REPOSITORY_UPDATED =
63
+ 'integrations.source_control.repository_updated' as const;
64
+
65
+ // Typed, provider-agnostic source-control event. The producing provider owns the
66
+ // translation from its raw webhook into this shape, so domain consumers never decode
67
+ // provider payloads.
68
+ export const integrationSourceRepositoryUpdatedSchema = z.object({
69
+ provider: nonEmptyStringSchema,
70
+ workspaceId: nonEmptyStringSchema,
71
+ connectionId: nonEmptyStringSchema,
72
+ deliveryId: nonEmptyStringSchema,
73
+ receivedAt: isoDateTimeSchema,
74
+ repository: sourceRepositoryIdentitySchema,
75
+ });
76
+ export type IntegrationSourceRepositoryUpdatedEvent = z.infer<
77
+ typeof integrationSourceRepositoryUpdatedSchema
78
+ >;
79
+
54
80
  export interface SentryIssuePayload {
55
81
  action: SentryIssueAction;
56
82
  issueId: string;
@@ -83,9 +109,11 @@ export type SentryIssueAction = (typeof SENTRY_ISSUE_ACTIONS)[number];
83
109
  export interface IntegrationsEventMap {
84
110
  [INTEGRATION_EVENT_RECEIVED]: IntegrationEventReceivedEvent;
85
111
  [INTEGRATION_SOURCE_COMMIT_PUSHED]: IntegrationSourceCommitPushedEvent;
112
+ [INTEGRATION_SOURCE_REPOSITORY_UPDATED]: IntegrationSourceRepositoryUpdatedEvent;
86
113
  }
87
114
 
88
115
  export const integrationsEventSchemas = {
89
116
  [INTEGRATION_EVENT_RECEIVED]: integrationEventReceivedSchema,
90
117
  [INTEGRATION_SOURCE_COMMIT_PUSHED]: integrationSourceCommitPushedSchema,
118
+ [INTEGRATION_SOURCE_REPOSITORY_UPDATED]: integrationSourceRepositoryUpdatedSchema,
91
119
  } satisfies Record<keyof IntegrationsEventMap, z.ZodType>;
@@ -1,6 +1,32 @@
1
1
  import {integrationsInterModuleContract} from './inter-module.js';
2
2
 
3
3
  describe('integrationsInterModuleContract', () => {
4
+ test('accepts a nullable normalized trigger reference', () => {
5
+ const result = integrationsInterModuleContract.methods.resolveTriggerReference.output.parse({
6
+ externalRepositoryId: 'github:42',
7
+ ref: 'refs/heads/main',
8
+ commit: 'a'.repeat(40),
9
+ actor: 'octocat',
10
+ });
11
+
12
+ expect(result?.externalRepositoryId).toBe('github:42');
13
+ expect(result?.actor).toBe('octocat');
14
+ expect(
15
+ integrationsInterModuleContract.methods.resolveTriggerReference.output.parse(null),
16
+ ).toBeNull();
17
+ });
18
+
19
+ test('accepts a reference from a payload that named no actor', () => {
20
+ const result = integrationsInterModuleContract.methods.resolveTriggerReference.output.parse({
21
+ externalRepositoryId: 'github:42',
22
+ ref: 'refs/heads/main',
23
+ commit: 'a'.repeat(40),
24
+ actor: null,
25
+ });
26
+
27
+ expect(result?.actor).toBeNull();
28
+ });
29
+
4
30
  test('accepts a source repository lookup through the producer contract', () => {
5
31
  const result = integrationsInterModuleContract.methods.resolveSourceRepository.output.parse({
6
32
  connection: {
@@ -23,6 +49,22 @@ describe('integrationsInterModuleContract', () => {
23
49
  expect(result.repository.fullName).toBe('shipfox/project');
24
50
  });
25
51
 
52
+ test('accepts a workspace-scoped connection lookup through the producer contract', () => {
53
+ const input = integrationsInterModuleContract.methods.resolveConnection.input.parse({
54
+ workspaceId: '00000000-0000-4000-8000-000000000001',
55
+ slug: 'github-main',
56
+ });
57
+ const output = integrationsInterModuleContract.methods.resolveConnection.output.parse({
58
+ id: '00000000-0000-4000-8000-000000000002',
59
+ provider: 'github',
60
+ slug: 'github-main',
61
+ });
62
+
63
+ expect(input.slug).toBe('github-main');
64
+ expect(output?.id).toBe('00000000-0000-4000-8000-000000000002');
65
+ expect(integrationsInterModuleContract.methods.resolveConnection.output.parse(null)).toBeNull();
66
+ });
67
+
26
68
  test.each([
27
69
  ['connection-not-found', {connectionId: '00000000-0000-4000-8000-000000000001'}],
28
70
  ['provider-unavailable', {provider: 'github'}],
@@ -4,6 +4,7 @@ import {z} from 'zod';
4
4
  const id = z.string().uuid();
5
5
  const provider = z.string().min(1);
6
6
  const capability = z.enum(['source_control', 'agent_tools']);
7
+ const connection = z.object({id, provider, slug: z.string().min(1)});
7
8
  const repository = z.object({
8
9
  externalRepositoryId: z.string(),
9
10
  owner: z.string(),
@@ -14,6 +15,12 @@ const repository = z.object({
14
15
  cloneUrl: z.string(),
15
16
  htmlUrl: z.string(),
16
17
  });
18
+ const triggerReference = z.object({
19
+ externalRepositoryId: z.string(),
20
+ ref: z.string(),
21
+ commit: z.string(),
22
+ actor: z.string().nullable(),
23
+ });
17
24
  const sourceInput = z.object({workspaceId: id, connectionId: id, externalRepositoryId: z.string()});
18
25
  const providerError = z.object({
19
26
  reason: z.string(),
@@ -35,7 +42,16 @@ export const integrationsInterModuleContract = defineInterModuleContract({
35
42
  methods: {
36
43
  resolveSourceRepository: {
37
44
  input: sourceInput,
38
- output: z.object({connection: z.object({id, provider, slug: z.string()}), repository}),
45
+ output: z.object({connection, repository}),
46
+ errors: sourceErrors,
47
+ },
48
+ resolveConnection: {
49
+ input: z.object({workspaceId: id, slug: z.string().min(1)}),
50
+ output: connection.nullable(),
51
+ },
52
+ resolveTriggerReference: {
53
+ input: z.object({workspaceId: id, connectionId: id, payload: z.unknown()}),
54
+ output: triggerReference.nullable(),
39
55
  errors: sourceErrors,
40
56
  },
41
57
  listSourceFiles: {
@@ -90,6 +90,24 @@ describe('storedWebhookRequestSchema', () => {
90
90
  expect(storedWebhookRequestSchema.safeParse(withoutConnectionId).success).toBe(false);
91
91
  });
92
92
 
93
+ it('requires and round-trips a connection identifier for Jira requests', () => {
94
+ const request = createStoredWebhookRequest({
95
+ requestId,
96
+ routeId: 'jira',
97
+ receivedAt,
98
+ rawQueryString: '',
99
+ headers: {authorization: 'Bearer token'},
100
+ body: new Uint8Array([1, 2, 3]),
101
+ connectionId: 'c0a8012e-0b6d-4d8f-8d5c-6d74102602b0',
102
+ });
103
+
104
+ expect(request.path_parameters.connection_id).toBe('c0a8012e-0b6d-4d8f-8d5c-6d74102602b0');
105
+ expect(storedWebhookRequestSchema.parse(JSON.parse(JSON.stringify(request)))).toEqual(request);
106
+ expect(storedWebhookRequestSchema.safeParse({...request, path_parameters: {}}).success).toBe(
107
+ false,
108
+ );
109
+ });
110
+
93
111
  it('keeps the maximum-size fixture below the SQS message limit', () => {
94
112
  const fixture = createMaximumSizeStoredWebhookRequestFixture();
95
113
  const serializedSize = new TextEncoder().encode(JSON.stringify(fixture)).byteLength;
package/src/webhooks.ts CHANGED
@@ -15,7 +15,7 @@ const standardWebhookRouteIds = [
15
15
  'slack.command',
16
16
  ] as const;
17
17
 
18
- export const webhookRouteIds = [...standardWebhookRouteIds, 'webhook.connection'] as const;
18
+ export const webhookRouteIds = [...standardWebhookRouteIds, 'webhook.connection', 'jira'] as const;
19
19
 
20
20
  export const webhookRouteIdSchema = z.enum(webhookRouteIds);
21
21
  export type WebhookRouteId = z.infer<typeof webhookRouteIdSchema>;
@@ -87,9 +87,17 @@ const genericWebhookRouteRequestSchema = storedWebhookRequestBaseSchema
87
87
  })
88
88
  .strict();
89
89
 
90
+ const jiraWebhookRouteRequestSchema = storedWebhookRequestBaseSchema
91
+ .extend({
92
+ route_id: z.literal('jira'),
93
+ path_parameters: connectionPathParametersSchema,
94
+ })
95
+ .strict();
96
+
90
97
  export const storedWebhookRequestSchema = z.discriminatedUnion('route_id', [
91
98
  standardWebhookRouteRequestSchema,
92
99
  genericWebhookRouteRequestSchema,
100
+ jiraWebhookRouteRequestSchema,
93
101
  ]);
94
102
  export type StoredWebhookRequest = z.infer<typeof storedWebhookRequestSchema>;
95
103
 
@@ -119,16 +127,27 @@ export const webhookProcessingResultSchema = z.discriminatedUnion('outcome', [
119
127
  ]);
120
128
  export type WebhookProcessingResult = z.infer<typeof webhookProcessingResultSchema>;
121
129
 
122
- export interface CreateStoredWebhookRequestInput {
130
+ interface CreateStoredWebhookRequestInputBase {
123
131
  requestId: string;
124
132
  routeId: WebhookRouteId;
125
133
  receivedAt: string;
126
134
  rawQueryString: string;
127
135
  headers: Record<string, string>;
128
136
  body: Uint8Array;
129
- connectionId?: string | undefined;
130
137
  }
131
138
 
139
+ type ConnectionScopedWebhookRouteId = 'webhook.connection' | 'jira';
140
+
141
+ export type CreateStoredWebhookRequestInput =
142
+ | (CreateStoredWebhookRequestInputBase & {
143
+ routeId: ConnectionScopedWebhookRouteId;
144
+ connectionId: string;
145
+ })
146
+ | (CreateStoredWebhookRequestInputBase & {
147
+ routeId: Exclude<WebhookRouteId, ConnectionScopedWebhookRouteId>;
148
+ connectionId?: never;
149
+ });
150
+
132
151
  export function encodeWebhookBody(body: Uint8Array): string {
133
152
  let encoded = '';
134
153
 
@@ -188,7 +207,9 @@ export function createStoredWebhookRequest(
188
207
  input: CreateStoredWebhookRequestInput,
189
208
  ): StoredWebhookRequest {
190
209
  const pathParameters =
191
- input.routeId === 'webhook.connection' ? {connection_id: input.connectionId} : {};
210
+ input.routeId === 'webhook.connection' || input.routeId === 'jira'
211
+ ? {connection_id: input.connectionId}
212
+ : {};
192
213
 
193
214
  return storedWebhookRequestSchema.parse({
194
215
  schema_version: WEBHOOK_REQUEST_SCHEMA_VERSION,