@automate.ax/integration-contracts 0.84.0 → 0.86.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/triggers.d.ts +2 -1
- package/dist/webflow/index.d.ts +49 -0
- package/dist/webflow/index.js +54 -0
- package/dist/webflow/schemas.d.ts +119 -0
- package/dist/webflow/schemas.js +140 -0
- package/package.json +10 -3
- package/src/triggers.ts +2 -0
- package/src/webflow/index.ts +98 -0
- package/src/webflow/schemas.ts +306 -0
package/dist/triggers.d.ts
CHANGED
|
@@ -15,9 +15,10 @@ import type { stripeTriggerContracts } from "./stripe/index.js";
|
|
|
15
15
|
import type { teamsTriggerContracts } from "./teams/index.js";
|
|
16
16
|
import type { trelloTriggerContracts } from "./trello/index.js";
|
|
17
17
|
import type { vercelTriggerContracts } from "./vercel/index.js";
|
|
18
|
+
import type { webflowTriggerContracts } from "./webflow/index.js";
|
|
18
19
|
import type { whatsappTriggerContracts } from "./whatsapp/index.js";
|
|
19
20
|
import type { z } from "zod";
|
|
20
|
-
export type TriggerContractMap = typeof airtableTriggerContracts & typeof asanaTriggerContracts & typeof brevoTriggerContracts & typeof convexTriggerContracts & typeof githubTriggerContracts & typeof gmailTriggerContracts & typeof googleCalendarTriggerContracts & typeof googleFormsTriggerContracts & typeof googleMeetTriggerContracts & typeof linearTriggerContracts & typeof outlookTriggerContracts & typeof resendTriggerContracts & typeof slackTriggerContracts & typeof stripeTriggerContracts & typeof teamsTriggerContracts & typeof trelloTriggerContracts & typeof vercelTriggerContracts & typeof whatsappTriggerContracts;
|
|
21
|
+
export type TriggerContractMap = typeof airtableTriggerContracts & typeof asanaTriggerContracts & typeof brevoTriggerContracts & typeof convexTriggerContracts & typeof githubTriggerContracts & typeof gmailTriggerContracts & typeof googleCalendarTriggerContracts & typeof googleFormsTriggerContracts & typeof googleMeetTriggerContracts & typeof linearTriggerContracts & typeof outlookTriggerContracts & typeof resendTriggerContracts & typeof slackTriggerContracts & typeof stripeTriggerContracts & typeof teamsTriggerContracts & typeof trelloTriggerContracts & typeof vercelTriggerContracts & typeof webflowTriggerContracts & typeof whatsappTriggerContracts;
|
|
21
22
|
export type IntegrationTriggerType = keyof TriggerContractMap;
|
|
22
23
|
/** Canonical authoring configuration for one integration trigger type. */
|
|
23
24
|
export type TriggerConfig<TType extends IntegrationTriggerType> = z.input<TriggerContractMap[TType]["configSchema"]> extends Record<string, never> ? object : z.input<TriggerContractMap[TType]["configSchema"]>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import type { WebflowEvent, WebflowSemanticEvent } from "./schemas.js";
|
|
3
|
+
export * from "./schemas.js";
|
|
4
|
+
/** Site selected for one Webflow webhook subscription. */
|
|
5
|
+
export declare const WEBFLOW_TRIGGER_CONFIG_SCHEMA: z.ZodObject<{
|
|
6
|
+
siteId: z.ZodString;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
type WebflowTriggerContract<TEvent> = {
|
|
9
|
+
readonly configSchema: typeof WEBFLOW_TRIGGER_CONFIG_SCHEMA;
|
|
10
|
+
readonly eventSchema: z.ZodType<TEvent>;
|
|
11
|
+
};
|
|
12
|
+
type WebflowSemanticTriggerTypes = {
|
|
13
|
+
readonly "webflow.collectionItem.changed": "collection_item_changed";
|
|
14
|
+
readonly "webflow.collectionItem.created": "collection_item_created";
|
|
15
|
+
readonly "webflow.collectionItem.deleted": "collection_item_deleted";
|
|
16
|
+
readonly "webflow.collectionItem.published": "collection_item_published";
|
|
17
|
+
readonly "webflow.collectionItem.unpublished": "collection_item_unpublished";
|
|
18
|
+
readonly "webflow.comment.created": "comment_created";
|
|
19
|
+
readonly "webflow.form.submitted": "form_submission";
|
|
20
|
+
readonly "webflow.inventory.changed": "ecomm_inventory_changed";
|
|
21
|
+
readonly "webflow.order.changed": "ecomm_order_changed";
|
|
22
|
+
readonly "webflow.order.created": "ecomm_new_order";
|
|
23
|
+
readonly "webflow.page.created": "page_created";
|
|
24
|
+
readonly "webflow.page.deleted": "page_deleted";
|
|
25
|
+
readonly "webflow.page.metadataUpdated": "page_metadata_updated";
|
|
26
|
+
readonly "webflow.site.published": "site_publish";
|
|
27
|
+
};
|
|
28
|
+
type WebflowTriggerContracts = {
|
|
29
|
+
readonly [TTrigger in keyof WebflowSemanticTriggerTypes]: WebflowTriggerContract<WebflowSemanticEvent<WebflowSemanticTriggerTypes[TTrigger]>>;
|
|
30
|
+
} & {
|
|
31
|
+
readonly "webflow.event": WebflowTriggerContract<WebflowEvent>;
|
|
32
|
+
};
|
|
33
|
+
export declare const webflowTriggerContracts: WebflowTriggerContracts;
|
|
34
|
+
export declare const webflowEventTypeMap: {
|
|
35
|
+
readonly collection_item_changed: "webflow.collectionItem.changed";
|
|
36
|
+
readonly collection_item_created: "webflow.collectionItem.created";
|
|
37
|
+
readonly collection_item_deleted: "webflow.collectionItem.deleted";
|
|
38
|
+
readonly collection_item_published: "webflow.collectionItem.published";
|
|
39
|
+
readonly collection_item_unpublished: "webflow.collectionItem.unpublished";
|
|
40
|
+
readonly comment_created: "webflow.comment.created";
|
|
41
|
+
readonly ecomm_inventory_changed: "webflow.inventory.changed";
|
|
42
|
+
readonly ecomm_new_order: "webflow.order.created";
|
|
43
|
+
readonly ecomm_order_changed: "webflow.order.changed";
|
|
44
|
+
readonly form_submission: "webflow.form.submitted";
|
|
45
|
+
readonly page_created: "webflow.page.created";
|
|
46
|
+
readonly page_deleted: "webflow.page.deleted";
|
|
47
|
+
readonly page_metadata_updated: "webflow.page.metadataUpdated";
|
|
48
|
+
readonly site_publish: "webflow.site.published";
|
|
49
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { WEBFLOW_EVENT_SCHEMA, webflowSemanticEventSchema } from "./schemas.js";
|
|
3
|
+
export * from "./schemas.js";
|
|
4
|
+
/** Site selected for one Webflow webhook subscription. */
|
|
5
|
+
export const WEBFLOW_TRIGGER_CONFIG_SCHEMA = z.object({
|
|
6
|
+
siteId: z.string().trim().min(1),
|
|
7
|
+
});
|
|
8
|
+
export const webflowTriggerContracts = {
|
|
9
|
+
"webflow.collectionItem.changed": semanticContract("collection_item_changed"),
|
|
10
|
+
"webflow.collectionItem.created": semanticContract("collection_item_created"),
|
|
11
|
+
"webflow.collectionItem.deleted": semanticContract("collection_item_deleted"),
|
|
12
|
+
"webflow.collectionItem.published": semanticContract("collection_item_published"),
|
|
13
|
+
"webflow.collectionItem.unpublished": semanticContract("collection_item_unpublished"),
|
|
14
|
+
"webflow.comment.created": semanticContract("comment_created"),
|
|
15
|
+
"webflow.event": {
|
|
16
|
+
configSchema: WEBFLOW_TRIGGER_CONFIG_SCHEMA,
|
|
17
|
+
eventSchema: WEBFLOW_EVENT_SCHEMA,
|
|
18
|
+
},
|
|
19
|
+
"webflow.form.submitted": semanticContract("form_submission"),
|
|
20
|
+
"webflow.inventory.changed": semanticContract("ecomm_inventory_changed"),
|
|
21
|
+
"webflow.order.changed": semanticContract("ecomm_order_changed"),
|
|
22
|
+
"webflow.order.created": semanticContract("ecomm_new_order"),
|
|
23
|
+
"webflow.page.created": semanticContract("page_created"),
|
|
24
|
+
"webflow.page.deleted": semanticContract("page_deleted"),
|
|
25
|
+
"webflow.page.metadataUpdated": semanticContract("page_metadata_updated"),
|
|
26
|
+
"webflow.site.published": semanticContract("site_publish"),
|
|
27
|
+
};
|
|
28
|
+
export const webflowEventTypeMap = {
|
|
29
|
+
collection_item_changed: "webflow.collectionItem.changed",
|
|
30
|
+
collection_item_created: "webflow.collectionItem.created",
|
|
31
|
+
collection_item_deleted: "webflow.collectionItem.deleted",
|
|
32
|
+
collection_item_published: "webflow.collectionItem.published",
|
|
33
|
+
collection_item_unpublished: "webflow.collectionItem.unpublished",
|
|
34
|
+
comment_created: "webflow.comment.created",
|
|
35
|
+
ecomm_inventory_changed: "webflow.inventory.changed",
|
|
36
|
+
ecomm_new_order: "webflow.order.created",
|
|
37
|
+
ecomm_order_changed: "webflow.order.changed",
|
|
38
|
+
form_submission: "webflow.form.submitted",
|
|
39
|
+
page_created: "webflow.page.created",
|
|
40
|
+
page_deleted: "webflow.page.deleted",
|
|
41
|
+
page_metadata_updated: "webflow.page.metadataUpdated",
|
|
42
|
+
site_publish: "webflow.site.published",
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Builds a contract for one semantic Webflow webhook event.
|
|
46
|
+
*
|
|
47
|
+
* @param type - Webflow webhook event type exposed by the contract.
|
|
48
|
+
*/
|
|
49
|
+
function semanticContract(type) {
|
|
50
|
+
return {
|
|
51
|
+
configSchema: WEBFLOW_TRIGGER_CONFIG_SCHEMA,
|
|
52
|
+
eventSchema: webflowSemanticEventSchema(type),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { type Encodable } from "@automate.ax/codec";
|
|
2
|
+
import type { Webflow } from "webflow-api";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
export declare const WEBFLOW_EVENT_TYPES: readonly ["form_submission", "site_publish", "page_created", "page_metadata_updated", "page_deleted", "ecomm_new_order", "ecomm_order_changed", "ecomm_inventory_changed", "collection_item_created", "collection_item_changed", "collection_item_deleted", "collection_item_published", "collection_item_unpublished", "comment_created"];
|
|
5
|
+
export type WebflowEventType = (typeof WEBFLOW_EVENT_TYPES)[number];
|
|
6
|
+
/** JSON representation of a Webflow ecommerce order webhook payload. */
|
|
7
|
+
export type WebflowOrderEvent = Omit<Webflow.Order, "acceptedOn" | "disputeUpdatedOn" | "disputedOn" | "fulfilledOn" | "refundedOn"> & {
|
|
8
|
+
acceptedOn?: string;
|
|
9
|
+
disputeUpdatedOn?: string | null;
|
|
10
|
+
disputedOn?: string | null;
|
|
11
|
+
fulfilledOn?: string | null;
|
|
12
|
+
refundedOn?: string | null;
|
|
13
|
+
};
|
|
14
|
+
/** JSON representation of a Webflow collection-item webhook payload. */
|
|
15
|
+
export interface WebflowCollectionItemEvent {
|
|
16
|
+
cmsLocaleId?: string;
|
|
17
|
+
collectionId: string;
|
|
18
|
+
createdOn?: string;
|
|
19
|
+
fieldData: Record<string, Encodable>;
|
|
20
|
+
id: string;
|
|
21
|
+
isArchived?: boolean;
|
|
22
|
+
isDraft?: boolean;
|
|
23
|
+
lastPublished?: string | null;
|
|
24
|
+
lastUpdated?: string;
|
|
25
|
+
siteId: string;
|
|
26
|
+
workspaceId: string;
|
|
27
|
+
}
|
|
28
|
+
/** Webflow form-submission webhook payload. */
|
|
29
|
+
export interface WebflowFormSubmissionEvent {
|
|
30
|
+
data: Record<string, Encodable>;
|
|
31
|
+
formElementId?: string;
|
|
32
|
+
formId: string;
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
schema: Array<{
|
|
36
|
+
fieldElementId: string;
|
|
37
|
+
fieldName: string;
|
|
38
|
+
fieldType: string;
|
|
39
|
+
}>;
|
|
40
|
+
siteId: string;
|
|
41
|
+
submittedAt: string;
|
|
42
|
+
}
|
|
43
|
+
/** Webflow page lifecycle webhook payload. */
|
|
44
|
+
export interface WebflowPageEvent {
|
|
45
|
+
archived: boolean;
|
|
46
|
+
createdOn?: string;
|
|
47
|
+
deletedOn?: string;
|
|
48
|
+
draft: boolean;
|
|
49
|
+
isBranch: boolean;
|
|
50
|
+
lastUpdated?: string;
|
|
51
|
+
pageId: string;
|
|
52
|
+
pageName: string | null;
|
|
53
|
+
pageTitle: string;
|
|
54
|
+
publishedPath: string;
|
|
55
|
+
siteId: string;
|
|
56
|
+
}
|
|
57
|
+
/** Webflow comment or reply webhook payload. */
|
|
58
|
+
export interface WebflowCommentEvent {
|
|
59
|
+
author: WebflowCommentUser;
|
|
60
|
+
breakpoint: string;
|
|
61
|
+
commentId: string;
|
|
62
|
+
content: string;
|
|
63
|
+
createdOn: string;
|
|
64
|
+
isResolved: boolean;
|
|
65
|
+
lastUpdated: string;
|
|
66
|
+
localeId: string | null;
|
|
67
|
+
mentionedUsers: WebflowCommentUser[];
|
|
68
|
+
pageId: string;
|
|
69
|
+
siteId: string;
|
|
70
|
+
threadId: string;
|
|
71
|
+
type: "new_comment" | "reply";
|
|
72
|
+
url: string;
|
|
73
|
+
}
|
|
74
|
+
/** User identity included in a Webflow comment webhook. */
|
|
75
|
+
export interface WebflowCommentUser {
|
|
76
|
+
email: string;
|
|
77
|
+
name: string;
|
|
78
|
+
userId: string;
|
|
79
|
+
}
|
|
80
|
+
/** One authenticated Webflow webhook envelope. */
|
|
81
|
+
export type WebflowEvent = (WebflowTypedEvent<"collection_item_changed", WebflowCollectionItemEvent> | WebflowTypedEvent<"collection_item_created", WebflowCollectionItemEvent> | WebflowTypedEvent<"collection_item_deleted", WebflowDeletedItemEvent> | WebflowTypedEvent<"collection_item_published", WebflowCollectionItemEvent> | WebflowTypedEvent<"collection_item_unpublished", WebflowDeletedItemEvent> | WebflowTypedEvent<"comment_created", WebflowCommentEvent> | WebflowTypedEvent<"ecomm_inventory_changed", WebflowInventoryEvent> | WebflowTypedEvent<"ecomm_new_order", WebflowOrderEvent> | WebflowTypedEvent<"ecomm_order_changed", WebflowOrderEvent> | WebflowTypedEvent<"form_submission", WebflowFormSubmissionEvent> | WebflowTypedEvent<"page_created", WebflowPageEvent> | WebflowTypedEvent<"page_deleted", WebflowPageEvent> | WebflowTypedEvent<"page_metadata_updated", WebflowPageEvent> | WebflowTypedEvent<"site_publish", WebflowSitePublishEvent>) & Encodable;
|
|
82
|
+
export type WebflowSemanticEvent<TType extends WebflowEventType> = Extract<WebflowEvent, {
|
|
83
|
+
triggerType: TType;
|
|
84
|
+
}>;
|
|
85
|
+
interface WebflowTypedEvent<TType extends WebflowEventType, TPayload> {
|
|
86
|
+
payload: TPayload;
|
|
87
|
+
triggerType: TType;
|
|
88
|
+
}
|
|
89
|
+
interface WebflowDeletedItemEvent {
|
|
90
|
+
cmsLocaleId?: string;
|
|
91
|
+
collectionId: string;
|
|
92
|
+
id: string;
|
|
93
|
+
siteId: string;
|
|
94
|
+
workspaceId: string;
|
|
95
|
+
}
|
|
96
|
+
interface WebflowInventoryEvent {
|
|
97
|
+
id: string;
|
|
98
|
+
inventoryType: "finite" | "infinite";
|
|
99
|
+
quantity?: number;
|
|
100
|
+
}
|
|
101
|
+
interface WebflowSitePublishEvent {
|
|
102
|
+
domains: string[];
|
|
103
|
+
publishScope?: "page" | "site";
|
|
104
|
+
publishTime?: string;
|
|
105
|
+
publishedBy: {
|
|
106
|
+
displayName: string;
|
|
107
|
+
};
|
|
108
|
+
publishedOn?: string;
|
|
109
|
+
site?: string;
|
|
110
|
+
siteId?: string;
|
|
111
|
+
}
|
|
112
|
+
export declare const WEBFLOW_EVENT_SCHEMA: z.ZodCustom<WebflowEvent, WebflowEvent>;
|
|
113
|
+
/**
|
|
114
|
+
* Narrows the Webflow event schema to one webhook event type.
|
|
115
|
+
*
|
|
116
|
+
* @param type - Webflow webhook event type to require.
|
|
117
|
+
*/
|
|
118
|
+
export declare function webflowSemanticEventSchema<TType extends WebflowEventType>(type: TType): z.ZodType<WebflowSemanticEvent<TType>>;
|
|
119
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { isEncodable } from "@automate.ax/codec";
|
|
2
|
+
import * as serializers from "webflow-api/serialization";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
export const WEBFLOW_EVENT_TYPES = [
|
|
5
|
+
"form_submission",
|
|
6
|
+
"site_publish",
|
|
7
|
+
"page_created",
|
|
8
|
+
"page_metadata_updated",
|
|
9
|
+
"page_deleted",
|
|
10
|
+
"ecomm_new_order",
|
|
11
|
+
"ecomm_order_changed",
|
|
12
|
+
"ecomm_inventory_changed",
|
|
13
|
+
"collection_item_created",
|
|
14
|
+
"collection_item_changed",
|
|
15
|
+
"collection_item_deleted",
|
|
16
|
+
"collection_item_published",
|
|
17
|
+
"collection_item_unpublished",
|
|
18
|
+
"comment_created",
|
|
19
|
+
];
|
|
20
|
+
const ENCODABLE_RECORD_SCHEMA = z.record(z.string(), z.custom(isEncodable));
|
|
21
|
+
const COLLECTION_ITEM_SCHEMA = z.looseObject({
|
|
22
|
+
cmsLocaleId: z.string().optional(),
|
|
23
|
+
collectionId: z.string(),
|
|
24
|
+
createdOn: z.string().optional(),
|
|
25
|
+
fieldData: ENCODABLE_RECORD_SCHEMA,
|
|
26
|
+
id: z.string(),
|
|
27
|
+
isArchived: z.boolean().optional(),
|
|
28
|
+
isDraft: z.boolean().optional(),
|
|
29
|
+
lastPublished: z.string().nullable().optional(),
|
|
30
|
+
lastUpdated: z.string().optional(),
|
|
31
|
+
siteId: z.string(),
|
|
32
|
+
workspaceId: z.string(),
|
|
33
|
+
});
|
|
34
|
+
const DELETED_ITEM_SCHEMA = z.looseObject({
|
|
35
|
+
cmsLocaleId: z.string().optional(),
|
|
36
|
+
collectionId: z.string(),
|
|
37
|
+
id: z.string(),
|
|
38
|
+
siteId: z.string(),
|
|
39
|
+
workspaceId: z.string(),
|
|
40
|
+
});
|
|
41
|
+
const FORM_SUBMISSION_SCHEMA = z.looseObject({
|
|
42
|
+
data: ENCODABLE_RECORD_SCHEMA,
|
|
43
|
+
formElementId: z.string().optional(),
|
|
44
|
+
formId: z.string(),
|
|
45
|
+
id: z.string(),
|
|
46
|
+
name: z.string(),
|
|
47
|
+
schema: z.array(z.looseObject({
|
|
48
|
+
fieldElementId: z.string(),
|
|
49
|
+
fieldName: z.string(),
|
|
50
|
+
fieldType: z.string(),
|
|
51
|
+
})),
|
|
52
|
+
siteId: z.string(),
|
|
53
|
+
submittedAt: z.string(),
|
|
54
|
+
});
|
|
55
|
+
const PAGE_SCHEMA = z.looseObject({
|
|
56
|
+
archived: z.boolean(),
|
|
57
|
+
createdOn: z.string().optional(),
|
|
58
|
+
deletedOn: z.string().optional(),
|
|
59
|
+
draft: z.boolean(),
|
|
60
|
+
isBranch: z.boolean(),
|
|
61
|
+
lastUpdated: z.string().optional(),
|
|
62
|
+
pageId: z.string(),
|
|
63
|
+
pageName: z.string().nullable(),
|
|
64
|
+
pageTitle: z.string(),
|
|
65
|
+
publishedPath: z.string(),
|
|
66
|
+
siteId: z.string(),
|
|
67
|
+
});
|
|
68
|
+
const COMMENT_USER_SCHEMA = z.looseObject({
|
|
69
|
+
email: z.string(),
|
|
70
|
+
name: z.string(),
|
|
71
|
+
userId: z.string(),
|
|
72
|
+
});
|
|
73
|
+
const COMMENT_SCHEMA = z.looseObject({
|
|
74
|
+
author: COMMENT_USER_SCHEMA,
|
|
75
|
+
breakpoint: z.string(),
|
|
76
|
+
commentId: z.string(),
|
|
77
|
+
content: z.string(),
|
|
78
|
+
createdOn: z.string(),
|
|
79
|
+
isResolved: z.boolean(),
|
|
80
|
+
lastUpdated: z.string(),
|
|
81
|
+
localeId: z.string().nullable(),
|
|
82
|
+
mentionedUsers: z.array(COMMENT_USER_SCHEMA),
|
|
83
|
+
pageId: z.string(),
|
|
84
|
+
siteId: z.string(),
|
|
85
|
+
threadId: z.string(),
|
|
86
|
+
type: z.enum(["new_comment", "reply"]),
|
|
87
|
+
url: z.string(),
|
|
88
|
+
});
|
|
89
|
+
const INVENTORY_SCHEMA = z.looseObject({
|
|
90
|
+
id: z.string(),
|
|
91
|
+
inventoryType: z.enum(["finite", "infinite"]),
|
|
92
|
+
quantity: z.number().optional(),
|
|
93
|
+
});
|
|
94
|
+
const ORDER_SCHEMA = z.custom((value) => serializers.Order.parse(value, { unrecognizedObjectKeys: "fail" }).ok, { message: "Expected a Webflow ecommerce order." });
|
|
95
|
+
const SITE_PUBLISH_SCHEMA = z.looseObject({
|
|
96
|
+
domains: z.array(z.string()),
|
|
97
|
+
publishScope: z.enum(["page", "site"]).optional(),
|
|
98
|
+
publishTime: z.string().optional(),
|
|
99
|
+
publishedBy: z.looseObject({ displayName: z.string() }),
|
|
100
|
+
publishedOn: z.string().optional(),
|
|
101
|
+
site: z.string().optional(),
|
|
102
|
+
siteId: z.string().optional(),
|
|
103
|
+
});
|
|
104
|
+
const WEBFLOW_BASE_EVENT_SCHEMA = z.discriminatedUnion("triggerType", [
|
|
105
|
+
typedEventSchema("collection_item_changed", COLLECTION_ITEM_SCHEMA),
|
|
106
|
+
typedEventSchema("collection_item_created", COLLECTION_ITEM_SCHEMA),
|
|
107
|
+
typedEventSchema("collection_item_deleted", DELETED_ITEM_SCHEMA),
|
|
108
|
+
typedEventSchema("collection_item_published", COLLECTION_ITEM_SCHEMA),
|
|
109
|
+
typedEventSchema("collection_item_unpublished", DELETED_ITEM_SCHEMA),
|
|
110
|
+
typedEventSchema("comment_created", COMMENT_SCHEMA),
|
|
111
|
+
typedEventSchema("ecomm_inventory_changed", INVENTORY_SCHEMA),
|
|
112
|
+
typedEventSchema("ecomm_new_order", ORDER_SCHEMA),
|
|
113
|
+
typedEventSchema("ecomm_order_changed", ORDER_SCHEMA),
|
|
114
|
+
typedEventSchema("form_submission", FORM_SUBMISSION_SCHEMA),
|
|
115
|
+
typedEventSchema("page_created", PAGE_SCHEMA),
|
|
116
|
+
typedEventSchema("page_deleted", PAGE_SCHEMA),
|
|
117
|
+
typedEventSchema("page_metadata_updated", PAGE_SCHEMA),
|
|
118
|
+
typedEventSchema("site_publish", SITE_PUBLISH_SCHEMA),
|
|
119
|
+
]);
|
|
120
|
+
export const WEBFLOW_EVENT_SCHEMA = z.custom((value) => WEBFLOW_BASE_EVENT_SCHEMA.safeParse(value).success, { message: "Expected a Webflow webhook event." });
|
|
121
|
+
/**
|
|
122
|
+
* Narrows the Webflow event schema to one webhook event type.
|
|
123
|
+
*
|
|
124
|
+
* @param type - Webflow webhook event type to require.
|
|
125
|
+
*/
|
|
126
|
+
export function webflowSemanticEventSchema(type) {
|
|
127
|
+
return z.custom((value) => {
|
|
128
|
+
const event = WEBFLOW_EVENT_SCHEMA.safeParse(value);
|
|
129
|
+
return event.success && event.data.triggerType === type;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Builds one concrete Webflow event-envelope schema.
|
|
134
|
+
*
|
|
135
|
+
* @param type - Provider event discriminator.
|
|
136
|
+
* @param payloadSchema - Schema for that event's payload.
|
|
137
|
+
*/
|
|
138
|
+
function typedEventSchema(type, payloadSchema) {
|
|
139
|
+
return z.looseObject({ payload: payloadSchema, triggerType: z.literal(type) });
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automate.ax/integration-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.1",
|
|
4
4
|
"description": "Shared integration payload contracts and provider primitives for Automate.ax.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"./trello": "./src/trello/index.ts",
|
|
37
37
|
"./triggers": "./src/triggers.ts",
|
|
38
38
|
"./vercel": "./src/vercel/index.ts",
|
|
39
|
+
"./webflow": "./src/webflow/index.ts",
|
|
39
40
|
"./whatsapp": "./src/whatsapp/index.ts"
|
|
40
41
|
},
|
|
41
42
|
"cjs": false,
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
}
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
|
-
"@automate.ax/codec": "0.
|
|
48
|
+
"@automate.ax/codec": "0.86.1",
|
|
48
49
|
"@googleapis/calendar": "^15.0.0",
|
|
49
50
|
"@googleapis/forms": "^6.0.1",
|
|
50
51
|
"@googleapis/gmail": "^12.0.0",
|
|
@@ -57,10 +58,11 @@
|
|
|
57
58
|
"postal-mime": "^2.7.5",
|
|
58
59
|
"stripe": "^22.1.0",
|
|
59
60
|
"type-fest": "^5.6.0",
|
|
61
|
+
"webflow-api": "^3.3.4",
|
|
60
62
|
"zod": "^4.3.6"
|
|
61
63
|
},
|
|
62
64
|
"devDependencies": {
|
|
63
|
-
"@internal/config": "0.
|
|
65
|
+
"@internal/config": "0.2.0",
|
|
64
66
|
"@types/bun": "latest",
|
|
65
67
|
"@types/html-to-text": "^9.0.4",
|
|
66
68
|
"@types/nodemailer": "^8.0.1",
|
|
@@ -180,6 +182,11 @@
|
|
|
180
182
|
"types": "./dist/vercel/index.d.ts",
|
|
181
183
|
"default": "./dist/vercel/index.js"
|
|
182
184
|
},
|
|
185
|
+
"./webflow": {
|
|
186
|
+
"bun": "./src/webflow/index.ts",
|
|
187
|
+
"types": "./dist/webflow/index.d.ts",
|
|
188
|
+
"default": "./dist/webflow/index.js"
|
|
189
|
+
},
|
|
183
190
|
"./whatsapp": {
|
|
184
191
|
"bun": "./src/whatsapp/index.ts",
|
|
185
192
|
"types": "./dist/whatsapp/index.d.ts",
|
package/src/triggers.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { stripeTriggerContracts } from "./stripe"
|
|
|
15
15
|
import type { teamsTriggerContracts } from "./teams"
|
|
16
16
|
import type { trelloTriggerContracts } from "./trello"
|
|
17
17
|
import type { vercelTriggerContracts } from "./vercel"
|
|
18
|
+
import type { webflowTriggerContracts } from "./webflow"
|
|
18
19
|
import type { whatsappTriggerContracts } from "./whatsapp"
|
|
19
20
|
import type { z } from "zod"
|
|
20
21
|
|
|
@@ -35,6 +36,7 @@ export type TriggerContractMap = typeof airtableTriggerContracts &
|
|
|
35
36
|
typeof teamsTriggerContracts &
|
|
36
37
|
typeof trelloTriggerContracts &
|
|
37
38
|
typeof vercelTriggerContracts &
|
|
39
|
+
typeof webflowTriggerContracts &
|
|
38
40
|
typeof whatsappTriggerContracts
|
|
39
41
|
export type IntegrationTriggerType = keyof TriggerContractMap
|
|
40
42
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as z from "zod"
|
|
2
|
+
import type {
|
|
3
|
+
WebflowEvent,
|
|
4
|
+
WebflowEventType,
|
|
5
|
+
WebflowSemanticEvent,
|
|
6
|
+
} from "./schemas"
|
|
7
|
+
import { WEBFLOW_EVENT_SCHEMA, webflowSemanticEventSchema } from "./schemas"
|
|
8
|
+
|
|
9
|
+
export * from "./schemas"
|
|
10
|
+
|
|
11
|
+
/** Site selected for one Webflow webhook subscription. */
|
|
12
|
+
export const WEBFLOW_TRIGGER_CONFIG_SCHEMA = z.object({
|
|
13
|
+
siteId: z.string().trim().min(1),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
type WebflowTriggerContract<TEvent> = {
|
|
17
|
+
readonly configSchema: typeof WEBFLOW_TRIGGER_CONFIG_SCHEMA
|
|
18
|
+
readonly eventSchema: z.ZodType<TEvent>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type WebflowSemanticTriggerTypes = {
|
|
22
|
+
readonly "webflow.collectionItem.changed": "collection_item_changed"
|
|
23
|
+
readonly "webflow.collectionItem.created": "collection_item_created"
|
|
24
|
+
readonly "webflow.collectionItem.deleted": "collection_item_deleted"
|
|
25
|
+
readonly "webflow.collectionItem.published": "collection_item_published"
|
|
26
|
+
readonly "webflow.collectionItem.unpublished": "collection_item_unpublished"
|
|
27
|
+
readonly "webflow.comment.created": "comment_created"
|
|
28
|
+
readonly "webflow.form.submitted": "form_submission"
|
|
29
|
+
readonly "webflow.inventory.changed": "ecomm_inventory_changed"
|
|
30
|
+
readonly "webflow.order.changed": "ecomm_order_changed"
|
|
31
|
+
readonly "webflow.order.created": "ecomm_new_order"
|
|
32
|
+
readonly "webflow.page.created": "page_created"
|
|
33
|
+
readonly "webflow.page.deleted": "page_deleted"
|
|
34
|
+
readonly "webflow.page.metadataUpdated": "page_metadata_updated"
|
|
35
|
+
readonly "webflow.site.published": "site_publish"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type WebflowTriggerContracts = {
|
|
39
|
+
readonly [TTrigger in keyof WebflowSemanticTriggerTypes]: WebflowTriggerContract<
|
|
40
|
+
WebflowSemanticEvent<WebflowSemanticTriggerTypes[TTrigger]>
|
|
41
|
+
>
|
|
42
|
+
} & {
|
|
43
|
+
readonly "webflow.event": WebflowTriggerContract<WebflowEvent>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const webflowTriggerContracts: WebflowTriggerContracts = {
|
|
47
|
+
"webflow.collectionItem.changed": semanticContract("collection_item_changed"),
|
|
48
|
+
"webflow.collectionItem.created": semanticContract("collection_item_created"),
|
|
49
|
+
"webflow.collectionItem.deleted": semanticContract("collection_item_deleted"),
|
|
50
|
+
"webflow.collectionItem.published": semanticContract(
|
|
51
|
+
"collection_item_published",
|
|
52
|
+
),
|
|
53
|
+
"webflow.collectionItem.unpublished": semanticContract(
|
|
54
|
+
"collection_item_unpublished",
|
|
55
|
+
),
|
|
56
|
+
"webflow.comment.created": semanticContract("comment_created"),
|
|
57
|
+
"webflow.event": {
|
|
58
|
+
configSchema: WEBFLOW_TRIGGER_CONFIG_SCHEMA,
|
|
59
|
+
eventSchema: WEBFLOW_EVENT_SCHEMA,
|
|
60
|
+
},
|
|
61
|
+
"webflow.form.submitted": semanticContract("form_submission"),
|
|
62
|
+
"webflow.inventory.changed": semanticContract("ecomm_inventory_changed"),
|
|
63
|
+
"webflow.order.changed": semanticContract("ecomm_order_changed"),
|
|
64
|
+
"webflow.order.created": semanticContract("ecomm_new_order"),
|
|
65
|
+
"webflow.page.created": semanticContract("page_created"),
|
|
66
|
+
"webflow.page.deleted": semanticContract("page_deleted"),
|
|
67
|
+
"webflow.page.metadataUpdated": semanticContract("page_metadata_updated"),
|
|
68
|
+
"webflow.site.published": semanticContract("site_publish"),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const webflowEventTypeMap = {
|
|
72
|
+
collection_item_changed: "webflow.collectionItem.changed",
|
|
73
|
+
collection_item_created: "webflow.collectionItem.created",
|
|
74
|
+
collection_item_deleted: "webflow.collectionItem.deleted",
|
|
75
|
+
collection_item_published: "webflow.collectionItem.published",
|
|
76
|
+
collection_item_unpublished: "webflow.collectionItem.unpublished",
|
|
77
|
+
comment_created: "webflow.comment.created",
|
|
78
|
+
ecomm_inventory_changed: "webflow.inventory.changed",
|
|
79
|
+
ecomm_new_order: "webflow.order.created",
|
|
80
|
+
ecomm_order_changed: "webflow.order.changed",
|
|
81
|
+
form_submission: "webflow.form.submitted",
|
|
82
|
+
page_created: "webflow.page.created",
|
|
83
|
+
page_deleted: "webflow.page.deleted",
|
|
84
|
+
page_metadata_updated: "webflow.page.metadataUpdated",
|
|
85
|
+
site_publish: "webflow.site.published",
|
|
86
|
+
} as const satisfies Record<WebflowEventType, keyof WebflowTriggerContracts>
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Builds a contract for one semantic Webflow webhook event.
|
|
90
|
+
*
|
|
91
|
+
* @param type - Webflow webhook event type exposed by the contract.
|
|
92
|
+
*/
|
|
93
|
+
function semanticContract<TType extends WebflowEventType>(type: TType) {
|
|
94
|
+
return {
|
|
95
|
+
configSchema: WEBFLOW_TRIGGER_CONFIG_SCHEMA,
|
|
96
|
+
eventSchema: webflowSemanticEventSchema(type),
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -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
|
+
}
|