@automate.ax/integration-contracts 0.83.3 → 0.85.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.
@@ -0,0 +1,306 @@
1
+ import { isEncodable, type Encodable } from "@automate.ax/codec"
2
+ import type { Webflow } from "webflow-api"
3
+ import * as serializers from "webflow-api/serialization"
4
+ import * as z from "zod"
5
+
6
+ export const WEBFLOW_EVENT_TYPES = [
7
+ "form_submission",
8
+ "site_publish",
9
+ "page_created",
10
+ "page_metadata_updated",
11
+ "page_deleted",
12
+ "ecomm_new_order",
13
+ "ecomm_order_changed",
14
+ "ecomm_inventory_changed",
15
+ "collection_item_created",
16
+ "collection_item_changed",
17
+ "collection_item_deleted",
18
+ "collection_item_published",
19
+ "collection_item_unpublished",
20
+ "comment_created",
21
+ ] as const satisfies readonly Webflow.TriggerType[]
22
+
23
+ export type WebflowEventType = (typeof WEBFLOW_EVENT_TYPES)[number]
24
+
25
+ /** JSON representation of a Webflow ecommerce order webhook payload. */
26
+ export type WebflowOrderEvent = Omit<
27
+ Webflow.Order,
28
+ | "acceptedOn"
29
+ | "disputeUpdatedOn"
30
+ | "disputedOn"
31
+ | "fulfilledOn"
32
+ | "refundedOn"
33
+ > & {
34
+ acceptedOn?: string
35
+ disputeUpdatedOn?: string | null
36
+ disputedOn?: string | null
37
+ fulfilledOn?: string | null
38
+ refundedOn?: string | null
39
+ }
40
+
41
+ /** JSON representation of a Webflow collection-item webhook payload. */
42
+ export interface WebflowCollectionItemEvent {
43
+ cmsLocaleId?: string
44
+ collectionId: string
45
+ createdOn?: string
46
+ fieldData: Record<string, Encodable>
47
+ id: string
48
+ isArchived?: boolean
49
+ isDraft?: boolean
50
+ lastPublished?: string | null
51
+ lastUpdated?: string
52
+ siteId: string
53
+ workspaceId: string
54
+ }
55
+
56
+ /** Webflow form-submission webhook payload. */
57
+ export interface WebflowFormSubmissionEvent {
58
+ data: Record<string, Encodable>
59
+ formElementId?: string
60
+ formId: string
61
+ id: string
62
+ name: string
63
+ schema: Array<{
64
+ fieldElementId: string
65
+ fieldName: string
66
+ fieldType: string
67
+ }>
68
+ siteId: string
69
+ submittedAt: string
70
+ }
71
+
72
+ /** Webflow page lifecycle webhook payload. */
73
+ export interface WebflowPageEvent {
74
+ archived: boolean
75
+ createdOn?: string
76
+ deletedOn?: string
77
+ draft: boolean
78
+ isBranch: boolean
79
+ lastUpdated?: string
80
+ pageId: string
81
+ pageName: string | null
82
+ pageTitle: string
83
+ publishedPath: string
84
+ siteId: string
85
+ }
86
+
87
+ /** Webflow comment or reply webhook payload. */
88
+ export interface WebflowCommentEvent {
89
+ author: WebflowCommentUser
90
+ breakpoint: string
91
+ commentId: string
92
+ content: string
93
+ createdOn: string
94
+ isResolved: boolean
95
+ lastUpdated: string
96
+ localeId: string | null
97
+ mentionedUsers: WebflowCommentUser[]
98
+ pageId: string
99
+ siteId: string
100
+ threadId: string
101
+ type: "new_comment" | "reply"
102
+ url: string
103
+ }
104
+
105
+ /** User identity included in a Webflow comment webhook. */
106
+ export interface WebflowCommentUser {
107
+ email: string
108
+ name: string
109
+ userId: string
110
+ }
111
+
112
+ /** One authenticated Webflow webhook envelope. */
113
+ export type WebflowEvent = (
114
+ | WebflowTypedEvent<"collection_item_changed", WebflowCollectionItemEvent>
115
+ | WebflowTypedEvent<"collection_item_created", WebflowCollectionItemEvent>
116
+ | WebflowTypedEvent<"collection_item_deleted", WebflowDeletedItemEvent>
117
+ | WebflowTypedEvent<"collection_item_published", WebflowCollectionItemEvent>
118
+ | WebflowTypedEvent<"collection_item_unpublished", WebflowDeletedItemEvent>
119
+ | WebflowTypedEvent<"comment_created", WebflowCommentEvent>
120
+ | WebflowTypedEvent<"ecomm_inventory_changed", WebflowInventoryEvent>
121
+ | WebflowTypedEvent<"ecomm_new_order", WebflowOrderEvent>
122
+ | WebflowTypedEvent<"ecomm_order_changed", WebflowOrderEvent>
123
+ | WebflowTypedEvent<"form_submission", WebflowFormSubmissionEvent>
124
+ | WebflowTypedEvent<"page_created", WebflowPageEvent>
125
+ | WebflowTypedEvent<"page_deleted", WebflowPageEvent>
126
+ | WebflowTypedEvent<"page_metadata_updated", WebflowPageEvent>
127
+ | WebflowTypedEvent<"site_publish", WebflowSitePublishEvent>
128
+ ) &
129
+ Encodable
130
+
131
+ export type WebflowSemanticEvent<TType extends WebflowEventType> = Extract<
132
+ WebflowEvent,
133
+ { triggerType: TType }
134
+ >
135
+
136
+ interface WebflowTypedEvent<TType extends WebflowEventType, TPayload> {
137
+ payload: TPayload
138
+ triggerType: TType
139
+ }
140
+
141
+ interface WebflowDeletedItemEvent {
142
+ cmsLocaleId?: string
143
+ collectionId: string
144
+ id: string
145
+ siteId: string
146
+ workspaceId: string
147
+ }
148
+
149
+ interface WebflowInventoryEvent {
150
+ id: string
151
+ inventoryType: "finite" | "infinite"
152
+ quantity?: number
153
+ }
154
+
155
+ interface WebflowSitePublishEvent {
156
+ domains: string[]
157
+ publishScope?: "page" | "site"
158
+ publishTime?: string
159
+ publishedBy: { displayName: string }
160
+ publishedOn?: string
161
+ site?: string
162
+ siteId?: string
163
+ }
164
+
165
+ const ENCODABLE_RECORD_SCHEMA = z.record(
166
+ z.string(),
167
+ z.custom<Encodable>(isEncodable),
168
+ )
169
+ const COLLECTION_ITEM_SCHEMA = z.looseObject({
170
+ cmsLocaleId: z.string().optional(),
171
+ collectionId: z.string(),
172
+ createdOn: z.string().optional(),
173
+ fieldData: ENCODABLE_RECORD_SCHEMA,
174
+ id: z.string(),
175
+ isArchived: z.boolean().optional(),
176
+ isDraft: z.boolean().optional(),
177
+ lastPublished: z.string().nullable().optional(),
178
+ lastUpdated: z.string().optional(),
179
+ siteId: z.string(),
180
+ workspaceId: z.string(),
181
+ })
182
+ const DELETED_ITEM_SCHEMA = z.looseObject({
183
+ cmsLocaleId: z.string().optional(),
184
+ collectionId: z.string(),
185
+ id: z.string(),
186
+ siteId: z.string(),
187
+ workspaceId: z.string(),
188
+ })
189
+ const FORM_SUBMISSION_SCHEMA = z.looseObject({
190
+ data: ENCODABLE_RECORD_SCHEMA,
191
+ formElementId: z.string().optional(),
192
+ formId: z.string(),
193
+ id: z.string(),
194
+ name: z.string(),
195
+ schema: z.array(
196
+ z.looseObject({
197
+ fieldElementId: z.string(),
198
+ fieldName: z.string(),
199
+ fieldType: z.string(),
200
+ }),
201
+ ),
202
+ siteId: z.string(),
203
+ submittedAt: z.string(),
204
+ })
205
+ const PAGE_SCHEMA = z.looseObject({
206
+ archived: z.boolean(),
207
+ createdOn: z.string().optional(),
208
+ deletedOn: z.string().optional(),
209
+ draft: z.boolean(),
210
+ isBranch: z.boolean(),
211
+ lastUpdated: z.string().optional(),
212
+ pageId: z.string(),
213
+ pageName: z.string().nullable(),
214
+ pageTitle: z.string(),
215
+ publishedPath: z.string(),
216
+ siteId: z.string(),
217
+ })
218
+ const COMMENT_USER_SCHEMA = z.looseObject({
219
+ email: z.string(),
220
+ name: z.string(),
221
+ userId: z.string(),
222
+ })
223
+ const COMMENT_SCHEMA = z.looseObject({
224
+ author: COMMENT_USER_SCHEMA,
225
+ breakpoint: z.string(),
226
+ commentId: z.string(),
227
+ content: z.string(),
228
+ createdOn: z.string(),
229
+ isResolved: z.boolean(),
230
+ lastUpdated: z.string(),
231
+ localeId: z.string().nullable(),
232
+ mentionedUsers: z.array(COMMENT_USER_SCHEMA),
233
+ pageId: z.string(),
234
+ siteId: z.string(),
235
+ threadId: z.string(),
236
+ type: z.enum(["new_comment", "reply"]),
237
+ url: z.string(),
238
+ })
239
+ const INVENTORY_SCHEMA = z.looseObject({
240
+ id: z.string(),
241
+ inventoryType: z.enum(["finite", "infinite"]),
242
+ quantity: z.number().optional(),
243
+ })
244
+ const ORDER_SCHEMA = z.custom<WebflowOrderEvent>(
245
+ (value) =>
246
+ serializers.Order.parse(value, { unrecognizedObjectKeys: "fail" }).ok,
247
+ { message: "Expected a Webflow ecommerce order." },
248
+ )
249
+ const SITE_PUBLISH_SCHEMA = z.looseObject({
250
+ domains: z.array(z.string()),
251
+ publishScope: z.enum(["page", "site"]).optional(),
252
+ publishTime: z.string().optional(),
253
+ publishedBy: z.looseObject({ displayName: z.string() }),
254
+ publishedOn: z.string().optional(),
255
+ site: z.string().optional(),
256
+ siteId: z.string().optional(),
257
+ })
258
+
259
+ const WEBFLOW_BASE_EVENT_SCHEMA = z.discriminatedUnion("triggerType", [
260
+ typedEventSchema("collection_item_changed", COLLECTION_ITEM_SCHEMA),
261
+ typedEventSchema("collection_item_created", COLLECTION_ITEM_SCHEMA),
262
+ typedEventSchema("collection_item_deleted", DELETED_ITEM_SCHEMA),
263
+ typedEventSchema("collection_item_published", COLLECTION_ITEM_SCHEMA),
264
+ typedEventSchema("collection_item_unpublished", DELETED_ITEM_SCHEMA),
265
+ typedEventSchema("comment_created", COMMENT_SCHEMA),
266
+ typedEventSchema("ecomm_inventory_changed", INVENTORY_SCHEMA),
267
+ typedEventSchema("ecomm_new_order", ORDER_SCHEMA),
268
+ typedEventSchema("ecomm_order_changed", ORDER_SCHEMA),
269
+ typedEventSchema("form_submission", FORM_SUBMISSION_SCHEMA),
270
+ typedEventSchema("page_created", PAGE_SCHEMA),
271
+ typedEventSchema("page_deleted", PAGE_SCHEMA),
272
+ typedEventSchema("page_metadata_updated", PAGE_SCHEMA),
273
+ typedEventSchema("site_publish", SITE_PUBLISH_SCHEMA),
274
+ ])
275
+
276
+ export const WEBFLOW_EVENT_SCHEMA = z.custom<WebflowEvent>(
277
+ (value) => WEBFLOW_BASE_EVENT_SCHEMA.safeParse(value).success,
278
+ { message: "Expected a Webflow webhook event." },
279
+ )
280
+
281
+ /**
282
+ * Narrows the Webflow event schema to one webhook event type.
283
+ *
284
+ * @param type - Webflow webhook event type to require.
285
+ */
286
+ export function webflowSemanticEventSchema<TType extends WebflowEventType>(
287
+ type: TType,
288
+ ): z.ZodType<WebflowSemanticEvent<TType>> {
289
+ return z.custom<WebflowSemanticEvent<TType>>((value) => {
290
+ const event = WEBFLOW_EVENT_SCHEMA.safeParse(value)
291
+ return event.success && event.data.triggerType === type
292
+ })
293
+ }
294
+
295
+ /**
296
+ * Builds one concrete Webflow event-envelope schema.
297
+ *
298
+ * @param type - Provider event discriminator.
299
+ * @param payloadSchema - Schema for that event's payload.
300
+ */
301
+ function typedEventSchema<TType extends WebflowEventType>(
302
+ type: TType,
303
+ payloadSchema: z.ZodType,
304
+ ) {
305
+ return z.looseObject({ payload: payloadSchema, triggerType: z.literal(type) })
306
+ }