import { defineAction } from "../../../automation/actions" import type { Webflow } from "webflow-api" import * as serializers from "webflow-api/serialization" import * as z from "zod" import { getWebflowApi, webflowAction, webflowActionSchemas, webflowInputSchema, webflowObjectSchema, webflowRequest, webflowVoidSchema, type WebflowInputValue, } from "../lib" type SiteInput = WebflowInputValue & { siteId: string } type PageInput = WebflowInputValue & { pageId: string } type CommentInput = SiteInput & { commentThreadId: string } /** Mutable request fields for applying registered scripts to a site or page. */ export type WebflowScriptApplyRequest = Pick const SCRIPT_APPLY_REQUEST_SERIALIZER: NonNullable< Parameters[1] > = { json: (value, options) => { const request = z .strictObject({ scripts: z.unknown().optional() }) .safeParse(value) return request.success ? serializers.ScriptApplyList.json(request.data, options) : { ok: false } }, } const SCRIPT_DISPLAY_NAME_SCHEMA = z .string() .min(1) .max(50) .regex(/^(?=.*[A-Za-z0-9])[A-Za-z0-9 ]+$/) const SCRIPT_INTEGRITY_HASH_SCHEMA = z .string() .regex(/^sha(?:256|384|512)-[A-Za-z0-9+/]+={0,2}$/) const SCRIPT_VERSION_SCHEMA = z .string() .regex( /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/, ) const HOSTED_SCRIPT_REQUEST_SERIALIZER: NonNullable< Parameters[1] > = { json: (value, options) => { const request = z .strictObject({ canCopy: z.boolean().optional(), displayName: SCRIPT_DISPLAY_NAME_SCHEMA, hostedLocation: z.url(), integrityHash: SCRIPT_INTEGRITY_HASH_SCHEMA, version: SCRIPT_VERSION_SCHEMA, }) .safeParse(value) return request.success ? serializers.CustomCodeHostedRequest.json(request.data, options) : { ok: false } }, } const INLINE_SCRIPT_REQUEST_SERIALIZER: NonNullable< Parameters[1] > = { json: (value, options) => { const request = z .strictObject({ canCopy: z.boolean().optional(), displayName: SCRIPT_DISPLAY_NAME_SCHEMA, integrityHash: SCRIPT_INTEGRITY_HASH_SCHEMA.optional(), sourceCode: z.string().max(2000), version: SCRIPT_VERSION_SCHEMA, }) .safeParse(value) return request.success ? serializers.CustomCodeInlineRequest.json(request.data, options) : { ok: false } }, } /** Lists comment threads on one Webflow site. */ export const listWebflowCommentThreads = webflowAction< SiteInput, Webflow.CommentThreadList >( defineAction("List Webflow comment threads") .describe( "Lists design-review comment threads with filters and pagination.", ) .account("webflow", "comments:read") .input( webflowInputSchema< SiteInput >(webflowActionSchemas.comments("siteId")), ) .output(webflowObjectSchema()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getWebflowApi(account).sites.comments.listCommentThreads( input.siteId, webflowRequest(input, "siteId"), ), ), ) /** Retrieves one Webflow design-review comment thread. */ export const getWebflowCommentThread = webflowAction< CommentInput, Webflow.CommentThread >( defineAction("Get Webflow comment thread") .describe("Retrieves one design-review comment thread and its context.") .account("webflow", "comments:read") .input( webflowInputSchema< CommentInput >(webflowActionSchemas.comments("siteId", "commentThreadId")), ) .output(webflowObjectSchema()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getWebflowApi(account).sites.comments.getCommentThread( input.siteId, input.commentThreadId, webflowRequest(input, "siteId", "commentThreadId"), ), ), ) /** Lists replies in one Webflow design-review comment thread. */ export const listWebflowCommentReplies = webflowAction< CommentInput, Webflow.CommentReplyList >( defineAction("List Webflow comment replies") .describe("Lists replies in one design-review comment thread.") .account("webflow", "comments:read") .input( webflowInputSchema< CommentInput >(webflowActionSchemas.comments("siteId", "commentThreadId")), ) .output(webflowObjectSchema()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getWebflowApi(account).sites.comments.listCommentReplies( input.siteId, input.commentThreadId, webflowRequest(input, "siteId", "commentThreadId"), ), ), ) /** Lists JavaScript resources registered for a Webflow site. */ export const listWebflowRegisteredScripts = webflowAction< SiteInput, Webflow.RegisteredScriptList >( defineAction("List Webflow registered scripts") .describe("Lists hosted and inline scripts registered for one site.") .account("webflow", "custom_code:read") .input(webflowInputSchema(webflowActionSchemas.id("siteId"))) .output(webflowObjectSchema()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getWebflowApi(account).scripts.list(input.siteId), ), ) /** Registers a versioned hosted JavaScript resource with a Webflow site. */ export const registerHostedWebflowScript = webflowAction< SiteInput, Webflow.CustomCodeHostedResponse >( defineAction("Register hosted Webflow script") .describe( "Registers a hosted script URL, version, attributes, and integrity.", ) .account("webflow", "custom_code:write") .input( webflowInputSchema>( webflowActionSchemas.id("siteId"), HOSTED_SCRIPT_REQUEST_SERIALIZER, ), ) .output(webflowObjectSchema()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getWebflowApi(account).scripts.registerHosted( input.siteId, webflowRequest(input, "siteId"), ), ), ) /** Registers an inline JavaScript resource with a Webflow site. */ export const registerInlineWebflowScript = webflowAction< SiteInput, Webflow.CustomCodeInlineResponse >( defineAction("Register inline Webflow script") .describe("Registers inline JavaScript with a version and placement.") .account("webflow", "custom_code:write") .input( webflowInputSchema>( webflowActionSchemas.id("siteId"), INLINE_SCRIPT_REQUEST_SERIALIZER, ), ) .output(webflowObjectSchema()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getWebflowApi(account).scripts.registerInline( input.siteId, webflowRequest(input, "siteId"), ), ), ) /** Retrieves scripts currently applied to one Webflow site. */ export const getWebflowSiteCustomCode = webflowAction< SiteInput, Webflow.ScriptApplyList >( defineAction("Get Webflow site custom code") .describe("Retrieves registered scripts currently applied site-wide.") .account("webflow", "custom_code:read") .input(webflowInputSchema(webflowActionSchemas.id("siteId"))) .output(webflowObjectSchema()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getWebflowApi(account).sites.scripts.getCustomCode(input.siteId), ), ) /** Replaces scripts applied site-wide on one Webflow site. */ export const setWebflowSiteCustomCode = webflowAction< SiteInput, Webflow.ScriptApplyList >( defineAction("Set Webflow site custom code") .describe("Replaces the registered scripts applied site-wide.") .account("webflow", "custom_code:write") .input( webflowInputSchema>( webflowActionSchemas.id("siteId"), SCRIPT_APPLY_REQUEST_SERIALIZER, ), ) .output(webflowObjectSchema()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getWebflowApi(account).sites.scripts.upsertCustomCode( input.siteId, webflowRequest(input, "siteId"), ), ), ) /** Removes every site-wide custom-code application from a Webflow site. */ export const deleteWebflowSiteCustomCode = webflowAction( defineAction("Delete Webflow site custom code") .describe("Removes all registered scripts applied site-wide.") .account("webflow", "custom_code:write") .input(webflowInputSchema(webflowActionSchemas.id("siteId"))) .output(webflowVoidSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await getWebflowApi(account).sites.scripts.deleteCustomCode(input.siteId) }), ) /** Lists reusable custom-code blocks defined for a Webflow site. */ export const listWebflowCustomCodeBlocks = webflowAction< SiteInput, Webflow.ListCustomCodeBlocks >( defineAction("List Webflow custom code blocks") .describe("Lists reusable custom-code blocks defined for one site.") .account("webflow", "custom_code:read") .input( webflowInputSchema< SiteInput >(webflowActionSchemas.paginated("siteId")), ) .output(webflowObjectSchema()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getWebflowApi(account).sites.scripts.listCustomCodeBlocks( input.siteId, webflowRequest(input, "siteId"), ), ), ) /** Retrieves scripts currently applied to one Webflow page. */ export const getWebflowPageCustomCode = webflowAction< PageInput, Webflow.ScriptApplyList >( defineAction("Get Webflow page custom code") .describe("Retrieves registered scripts currently applied to one page.") .account("webflow", "custom_code:read") .input(webflowInputSchema(webflowActionSchemas.id("pageId"))) .output(webflowObjectSchema()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getWebflowApi(account).pages.scripts.getCustomCode(input.pageId), ), ) /** Replaces scripts applied to one Webflow page. */ export const setWebflowPageCustomCode = webflowAction< PageInput, Webflow.ScriptApplyList >( defineAction("Set Webflow page custom code") .describe("Replaces the registered scripts applied to one page.") .account("webflow", "custom_code:write") .input( webflowInputSchema>( webflowActionSchemas.id("pageId"), SCRIPT_APPLY_REQUEST_SERIALIZER, ), ) .output(webflowObjectSchema()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getWebflowApi(account).pages.scripts.upsertCustomCode( input.pageId, webflowRequest(input, "pageId"), ), ), ) /** Removes every custom-code application from one Webflow page. */ export const deleteWebflowPageCustomCode = webflowAction( defineAction("Delete Webflow page custom code") .describe("Removes all registered scripts applied to one page.") .account("webflow", "custom_code:write") .input(webflowInputSchema(webflowActionSchemas.id("pageId"))) .output(webflowVoidSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await getWebflowApi(account).pages.scripts.deleteCustomCode(input.pageId) }), )