From d154c4e2f2ed0548130344ede257e19fb43f6be7 Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Tue, 14 Mar 2023 16:42:12 +0100 Subject: [PATCH] :recycle: Replace schemas with merge and discriminated unions Closes #374 --- .../GoogleSheetsSettingsBody.tsx | 11 +- .../api/procedures/listTypebotsProcedure.ts | 2 +- apps/docs/openapi/builder/_spec_.json | 270 +- apps/docs/openapi/chat/_spec_.json | 6851 +++++++---------- .../api/utils/executeGoogleSheetBlock.ts | 6 +- packages/models/features/answer.ts | 2 +- .../models/features/blocks/bubbles/audio.ts | 2 +- .../models/features/blocks/bubbles/embed.ts | 2 +- .../models/features/blocks/bubbles/image.ts | 2 +- .../models/features/blocks/bubbles/index.ts | 3 +- .../models/features/blocks/bubbles/schemas.ts | 21 - .../models/features/blocks/bubbles/text.ts | 2 +- .../features/blocks/bubbles/video/schemas.ts | 2 +- packages/models/features/blocks/index.ts | 2 +- .../models/features/blocks/inputs/choice.ts | 6 +- .../models/features/blocks/inputs/date.ts | 4 +- .../models/features/blocks/inputs/email.ts | 6 +- .../models/features/blocks/inputs/file.ts | 4 +- .../models/features/blocks/inputs/index.ts | 1 - .../models/features/blocks/inputs/number.ts | 6 +- .../features/blocks/inputs/payment/schemas.ts | 4 +- .../models/features/blocks/inputs/phone.ts | 6 +- .../models/features/blocks/inputs/rating.ts | 4 +- .../models/features/blocks/inputs/schemas.ts | 42 - .../models/features/blocks/inputs/text.ts | 6 +- packages/models/features/blocks/inputs/url.ts | 6 +- .../features/blocks/integrations/chatwoot.ts | 2 +- .../blocks/integrations/googleAnalytics.ts | 2 +- .../integrations/googleSheets/schemas.ts | 24 +- .../features/blocks/integrations/index.ts | 1 - .../features/blocks/integrations/makeCom.ts | 2 +- .../blocks/integrations/pabblyConnect.ts | 2 +- .../features/blocks/integrations/schemas.ts | 23 - .../features/blocks/integrations/sendEmail.ts | 2 +- .../features/blocks/integrations/webhook.ts | 2 +- .../features/blocks/integrations/zapier.ts | 2 +- .../models/features/blocks/logic/condition.ts | 4 +- .../models/features/blocks/logic/index.ts | 1 - packages/models/features/blocks/logic/jump.ts | 2 +- .../features/blocks/logic/logicBlock.ts | 26 - .../models/features/blocks/logic/redirect.ts | 2 +- .../models/features/blocks/logic/script.ts | 2 +- .../features/blocks/logic/setVariable.ts | 2 +- .../features/blocks/logic/typebotLink.ts | 2 +- packages/models/features/blocks/logic/wait.ts | 2 +- packages/models/features/blocks/schemas.ts | 131 +- .../models/features/blocks/start/schemas.ts | 2 +- packages/models/features/chat.ts | 24 +- packages/models/features/publicTypebot.ts | 2 +- packages/models/features/result.ts | 4 +- .../models/features/typebot/theme/schemas.ts | 2 +- packages/models/features/typebot/typebot.ts | 2 +- 52 files changed, 3217 insertions(+), 4328 deletions(-) delete mode 100644 packages/models/features/blocks/bubbles/schemas.ts delete mode 100644 packages/models/features/blocks/inputs/schemas.ts delete mode 100644 packages/models/features/blocks/integrations/schemas.ts delete mode 100644 packages/models/features/blocks/logic/logicBlock.ts diff --git a/apps/builder/src/features/blocks/integrations/googleSheets/components/GoogleSheetsSettingsBody/GoogleSheetsSettingsBody.tsx b/apps/builder/src/features/blocks/integrations/googleSheets/components/GoogleSheetsSettingsBody/GoogleSheetsSettingsBody.tsx index f800510f7..eadfc2599 100644 --- a/apps/builder/src/features/blocks/integrations/googleSheets/components/GoogleSheetsSettingsBody/GoogleSheetsSettingsBody.tsx +++ b/apps/builder/src/features/blocks/integrations/googleSheets/components/GoogleSheetsSettingsBody/GoogleSheetsSettingsBody.tsx @@ -14,7 +14,7 @@ import { GoogleSheetsUpdateRowOptions, } from 'models' import React, { useMemo } from 'react' -import { isDefined, omit } from 'utils' +import { isDefined } from 'utils' import { SheetsDropdown } from './SheetsDropdown' import { SpreadsheetsDropdown } from './SpreadsheetDropdown' import { CellWithValueStack } from './CellWithValueStack' @@ -50,8 +50,11 @@ export const GoogleSheetsSettingsBody = ({ () => sheets?.find((s) => s.id === options?.sheetId), [sheets, options?.sheetId] ) - const handleCredentialsIdChange = (credentialsId?: string) => - onOptionsChange({ ...omit(options, 'credentialsId'), credentialsId }) + const handleCredentialsIdChange = (credentialsId: string | undefined) => + onOptionsChange({ + ...options, + credentialsId, + }) const handleSpreadsheetIdChange = (spreadsheetId: string | undefined) => onOptionsChange({ ...options, spreadsheetId }) const handleSheetIdChange = (sheetId: string | undefined) => @@ -137,7 +140,7 @@ export const GoogleSheetsSettingsBody = ({ /> )} - {'action' in options && ( + {options.action && ( => { - if (!('action' in block.options)) - return { outgoingEdgeId: block.outgoingEdgeId } - switch (block.options.action) { + const action = block.options.action + if (!action) return { outgoingEdgeId: block.outgoingEdgeId } + switch (action) { case GoogleSheetsAction.INSERT_ROW: return insertRow(state, { options: block.options, diff --git a/packages/models/features/answer.ts b/packages/models/features/answer.ts index f65b92dea..2a078363d 100644 --- a/packages/models/features/answer.ts +++ b/packages/models/features/answer.ts @@ -18,7 +18,7 @@ export const answerInputSchema = answerSchema variableId: true, storageUsed: true, }) - .and( + .merge( z.object({ variableId: z.string().nullish(), storageUsed: z.number().nullish(), diff --git a/packages/models/features/blocks/bubbles/audio.ts b/packages/models/features/blocks/bubbles/audio.ts index a91a2e04c..1e52d6b6a 100644 --- a/packages/models/features/blocks/bubbles/audio.ts +++ b/packages/models/features/blocks/bubbles/audio.ts @@ -6,7 +6,7 @@ export const audioBubbleContentSchema = z.object({ url: z.string().optional(), }) -export const audioBubbleBlockSchema = blockBaseSchema.and( +export const audioBubbleBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([BubbleBlockType.AUDIO]), content: audioBubbleContentSchema, diff --git a/packages/models/features/blocks/bubbles/embed.ts b/packages/models/features/blocks/bubbles/embed.ts index 29b2048ce..83d57cd8f 100644 --- a/packages/models/features/blocks/bubbles/embed.ts +++ b/packages/models/features/blocks/bubbles/embed.ts @@ -8,7 +8,7 @@ export const embedBubbleContentSchema = z.object({ height: z.number().or(variableStringSchema), }) -export const embedBubbleBlockSchema = blockBaseSchema.and( +export const embedBubbleBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([BubbleBlockType.EMBED]), content: embedBubbleContentSchema, diff --git a/packages/models/features/blocks/bubbles/image.ts b/packages/models/features/blocks/bubbles/image.ts index 6b698de4e..721373c02 100644 --- a/packages/models/features/blocks/bubbles/image.ts +++ b/packages/models/features/blocks/bubbles/image.ts @@ -6,7 +6,7 @@ export const imageBubbleContentSchema = z.object({ url: z.string().optional(), }) -export const imageBubbleBlockSchema = blockBaseSchema.and( +export const imageBubbleBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([BubbleBlockType.IMAGE]), content: imageBubbleContentSchema, diff --git a/packages/models/features/blocks/bubbles/index.ts b/packages/models/features/blocks/bubbles/index.ts index ebdb5e43f..fa4f127d1 100644 --- a/packages/models/features/blocks/bubbles/index.ts +++ b/packages/models/features/blocks/bubbles/index.ts @@ -1,7 +1,6 @@ export * from './audio' export * from './embed' -export * from './enums' export * from './image' -export * from './schemas' export * from './text' export * from './video' +export * from './enums' diff --git a/packages/models/features/blocks/bubbles/schemas.ts b/packages/models/features/blocks/bubbles/schemas.ts deleted file mode 100644 index 9ea95b2be..000000000 --- a/packages/models/features/blocks/bubbles/schemas.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { z } from 'zod' -import { audioBubbleBlockSchema, audioBubbleContentSchema } from './audio' -import { embedBubbleContentSchema, embedBubbleBlockSchema } from './embed' -import { imageBubbleContentSchema, imageBubbleBlockSchema } from './image' -import { textBubbleContentSchema, textBubbleBlockSchema } from './text' -import { videoBubbleContentSchema, videoBubbleBlockSchema } from './video' - -export const bubbleBlockContentSchema = textBubbleContentSchema - .or(imageBubbleContentSchema) - .or(videoBubbleContentSchema) - .or(embedBubbleContentSchema) - .or(audioBubbleContentSchema) - -export const bubbleBlockSchema = textBubbleBlockSchema - .or(imageBubbleBlockSchema) - .or(videoBubbleBlockSchema) - .or(embedBubbleBlockSchema) - .or(audioBubbleBlockSchema) - -export type BubbleBlock = z.infer -export type BubbleBlockContent = z.infer diff --git a/packages/models/features/blocks/bubbles/text.ts b/packages/models/features/blocks/bubbles/text.ts index 2ad1658fa..ad90048c7 100644 --- a/packages/models/features/blocks/bubbles/text.ts +++ b/packages/models/features/blocks/bubbles/text.ts @@ -16,7 +16,7 @@ export const textBubbleContentSchema = z.object({ export type TextBubbleContent = z.infer -export const textBubbleBlockSchema = blockBaseSchema.and( +export const textBubbleBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([BubbleBlockType.TEXT]), content: textBubbleContentSchema, diff --git a/packages/models/features/blocks/bubbles/video/schemas.ts b/packages/models/features/blocks/bubbles/video/schemas.ts index 69b7c3c47..59e7c4fdd 100644 --- a/packages/models/features/blocks/bubbles/video/schemas.ts +++ b/packages/models/features/blocks/bubbles/video/schemas.ts @@ -9,7 +9,7 @@ export const videoBubbleContentSchema = z.object({ type: z.nativeEnum(VideoBubbleContentType).optional(), }) -export const videoBubbleBlockSchema = blockBaseSchema.and( +export const videoBubbleBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([BubbleBlockType.VIDEO]), content: videoBubbleContentSchema, diff --git a/packages/models/features/blocks/index.ts b/packages/models/features/blocks/index.ts index cb7c94151..c16455286 100644 --- a/packages/models/features/blocks/index.ts +++ b/packages/models/features/blocks/index.ts @@ -3,5 +3,5 @@ export * from './bubbles' export * from './inputs' export * from './integrations' export * from './logic' -export * from './schemas' export * from './start' +export * from './schemas' diff --git a/packages/models/features/blocks/inputs/choice.ts b/packages/models/features/blocks/inputs/choice.ts index 59fe5e031..6402fa7a4 100644 --- a/packages/models/features/blocks/inputs/choice.ts +++ b/packages/models/features/blocks/inputs/choice.ts @@ -5,7 +5,7 @@ import { optionBaseSchema, blockBaseSchema } from '../baseSchemas' import { defaultButtonLabel } from './constants' import { InputBlockType } from './enums' -export const choiceInputOptionsSchema = optionBaseSchema.and( +export const choiceInputOptionsSchema = optionBaseSchema.merge( z.object({ isMultipleChoice: z.boolean(), buttonLabel: z.string(), @@ -18,14 +18,14 @@ export const defaultChoiceInputOptions: ChoiceInputOptions = { isMultipleChoice: false, } -export const buttonItemSchema = itemBaseSchema.and( +export const buttonItemSchema = itemBaseSchema.merge( z.object({ type: z.literal(ItemType.BUTTON), content: z.string().optional(), }) ) -export const choiceInputSchema = blockBaseSchema.and( +export const choiceInputSchema = blockBaseSchema.merge( z.object({ type: z.enum([InputBlockType.CHOICE]), items: z.array(buttonItemSchema), diff --git a/packages/models/features/blocks/inputs/date.ts b/packages/models/features/blocks/inputs/date.ts index 87c31d476..b56377b89 100644 --- a/packages/models/features/blocks/inputs/date.ts +++ b/packages/models/features/blocks/inputs/date.ts @@ -3,7 +3,7 @@ import { optionBaseSchema, blockBaseSchema } from '../baseSchemas' import { defaultButtonLabel } from './constants' import { InputBlockType } from './enums' -export const dateInputOptionsSchema = optionBaseSchema.and( +export const dateInputOptionsSchema = optionBaseSchema.merge( z.object({ labels: z.object({ button: z.string(), @@ -15,7 +15,7 @@ export const dateInputOptionsSchema = optionBaseSchema.and( }) ) -export const dateInputSchema = blockBaseSchema.and( +export const dateInputSchema = blockBaseSchema.merge( z.object({ type: z.enum([InputBlockType.DATE]), options: dateInputOptionsSchema, diff --git a/packages/models/features/blocks/inputs/email.ts b/packages/models/features/blocks/inputs/email.ts index b9043884b..2b227fa71 100644 --- a/packages/models/features/blocks/inputs/email.ts +++ b/packages/models/features/blocks/inputs/email.ts @@ -5,14 +5,14 @@ import { InputBlockType } from './enums' import { textInputOptionsBaseSchema } from './text' export const emailInputOptionsSchema = optionBaseSchema - .and(textInputOptionsBaseSchema) - .and( + .merge(textInputOptionsBaseSchema) + .merge( z.object({ retryMessageContent: z.string(), }) ) -export const emailInputSchema = blockBaseSchema.and( +export const emailInputSchema = blockBaseSchema.merge( z.object({ type: z.enum([InputBlockType.EMAIL]), options: emailInputOptionsSchema, diff --git a/packages/models/features/blocks/inputs/file.ts b/packages/models/features/blocks/inputs/file.ts index 55811a3e0..46a455834 100644 --- a/packages/models/features/blocks/inputs/file.ts +++ b/packages/models/features/blocks/inputs/file.ts @@ -2,7 +2,7 @@ import { z } from 'zod' import { optionBaseSchema, blockBaseSchema } from '../baseSchemas' import { InputBlockType } from './enums' -export const fileInputOptionsSchema = optionBaseSchema.and( +export const fileInputOptionsSchema = optionBaseSchema.merge( z.object({ isRequired: z.boolean().optional(), isMultipleAllowed: z.boolean(), @@ -16,7 +16,7 @@ export const fileInputOptionsSchema = optionBaseSchema.and( }) ) -export const fileInputStepSchema = blockBaseSchema.and( +export const fileInputStepSchema = blockBaseSchema.merge( z.object({ type: z.literal(InputBlockType.FILE), options: fileInputOptionsSchema, diff --git a/packages/models/features/blocks/inputs/index.ts b/packages/models/features/blocks/inputs/index.ts index 25cff208d..87aac3b7e 100644 --- a/packages/models/features/blocks/inputs/index.ts +++ b/packages/models/features/blocks/inputs/index.ts @@ -8,6 +8,5 @@ export * from './number' export * from './payment' export * from './phone' export * from './rating' -export * from './schemas' export * from './text' export * from './url' diff --git a/packages/models/features/blocks/inputs/number.ts b/packages/models/features/blocks/inputs/number.ts index b70718f47..d4abb1690 100644 --- a/packages/models/features/blocks/inputs/number.ts +++ b/packages/models/features/blocks/inputs/number.ts @@ -5,8 +5,8 @@ import { InputBlockType } from './enums' import { textInputOptionsBaseSchema } from './text' export const numberInputOptionsSchema = optionBaseSchema - .and(textInputOptionsBaseSchema) - .and( + .merge(textInputOptionsBaseSchema) + .merge( z.object({ min: z.number().optional(), max: z.number().optional(), @@ -14,7 +14,7 @@ export const numberInputOptionsSchema = optionBaseSchema }) ) -export const numberInputSchema = blockBaseSchema.and( +export const numberInputSchema = blockBaseSchema.merge( z.object({ type: z.enum([InputBlockType.NUMBER]), options: numberInputOptionsSchema, diff --git a/packages/models/features/blocks/inputs/payment/schemas.ts b/packages/models/features/blocks/inputs/payment/schemas.ts index 7dcc88f55..3846ecf16 100644 --- a/packages/models/features/blocks/inputs/payment/schemas.ts +++ b/packages/models/features/blocks/inputs/payment/schemas.ts @@ -14,7 +14,7 @@ export type CreditCardDetails = { cvc: string } -export const paymentInputOptionsSchema = optionBaseSchema.and( +export const paymentInputOptionsSchema = optionBaseSchema.merge( z.object({ provider: z.nativeEnum(PaymentProvider), labels: z.object({ @@ -40,7 +40,7 @@ export const paymentInputRuntimeOptionsSchema = z.object({ publicKey: z.string(), }) -export const paymentInputSchema = blockBaseSchema.and( +export const paymentInputSchema = blockBaseSchema.merge( z.object({ type: z.enum([InputBlockType.PAYMENT]), options: paymentInputOptionsSchema, diff --git a/packages/models/features/blocks/inputs/phone.ts b/packages/models/features/blocks/inputs/phone.ts index a668b9eb2..ed1d2cb15 100644 --- a/packages/models/features/blocks/inputs/phone.ts +++ b/packages/models/features/blocks/inputs/phone.ts @@ -5,15 +5,15 @@ import { InputBlockType } from './enums' import { textInputOptionsBaseSchema } from './text' export const phoneNumberInputOptionsSchema = optionBaseSchema - .and(textInputOptionsBaseSchema) - .and( + .merge(textInputOptionsBaseSchema) + .merge( z.object({ retryMessageContent: z.string(), defaultCountryCode: z.string().optional(), }) ) -export const phoneNumberInputBlockSchema = blockBaseSchema.and( +export const phoneNumberInputBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([InputBlockType.PHONE]), options: phoneNumberInputOptionsSchema, diff --git a/packages/models/features/blocks/inputs/rating.ts b/packages/models/features/blocks/inputs/rating.ts index 85a164015..6cc9d8032 100644 --- a/packages/models/features/blocks/inputs/rating.ts +++ b/packages/models/features/blocks/inputs/rating.ts @@ -10,7 +10,7 @@ export const defaultRatingInputOptions: RatingInputOptions = { customIcon: { isEnabled: false }, } -export const ratingInputOptionsSchema = optionBaseSchema.and( +export const ratingInputOptionsSchema = optionBaseSchema.merge( z.object({ buttonType: z.literal('Icons').or(z.literal('Numbers')), length: z.number(), @@ -27,7 +27,7 @@ export const ratingInputOptionsSchema = optionBaseSchema.and( }) ) -export const ratingInputBlockSchema = blockBaseSchema.and( +export const ratingInputBlockSchema = blockBaseSchema.merge( z.object({ type: z.literal(InputBlockType.RATING), options: ratingInputOptionsSchema, diff --git a/packages/models/features/blocks/inputs/schemas.ts b/packages/models/features/blocks/inputs/schemas.ts deleted file mode 100644 index d45fa0227..000000000 --- a/packages/models/features/blocks/inputs/schemas.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { z } from 'zod' -import { choiceInputOptionsSchema, choiceInputSchema } from './choice' -import { dateInputOptionsSchema, dateInputSchema } from './date' -import { emailInputOptionsSchema, emailInputSchema } from './email' -import { numberInputOptionsSchema, numberInputSchema } from './number' -import { paymentInputOptionsSchema, paymentInputSchema } from './payment' -import { - phoneNumberInputOptionsSchema, - phoneNumberInputBlockSchema, -} from './phone' -import { ratingInputOptionsSchema, ratingInputBlockSchema } from './rating' -import { textInputOptionsSchema, textInputSchema } from './text' -import { fileInputOptionsSchema, fileInputStepSchema } from './file' -import { urlInputOptionsSchema, urlInputSchema } from './url' -import { optionBaseSchema } from '../baseSchemas' - -export type OptionBase = z.infer - -export const inputBlockOptionsSchema = textInputOptionsSchema - .or(choiceInputOptionsSchema) - .or(emailInputOptionsSchema) - .or(numberInputOptionsSchema) - .or(urlInputOptionsSchema) - .or(phoneNumberInputOptionsSchema) - .or(dateInputOptionsSchema) - .or(paymentInputOptionsSchema) - .or(ratingInputOptionsSchema) - .or(fileInputOptionsSchema) - -export const inputBlockSchema = textInputSchema - .or(numberInputSchema) - .or(emailInputSchema) - .or(urlInputSchema) - .or(dateInputSchema) - .or(phoneNumberInputBlockSchema) - .or(choiceInputSchema) - .or(paymentInputSchema) - .or(ratingInputBlockSchema) - .or(fileInputStepSchema) - -export type InputBlock = z.infer -export type InputBlockOptions = z.infer diff --git a/packages/models/features/blocks/inputs/text.ts b/packages/models/features/blocks/inputs/text.ts index 2b0bf91bb..3f62f01aa 100644 --- a/packages/models/features/blocks/inputs/text.ts +++ b/packages/models/features/blocks/inputs/text.ts @@ -11,8 +11,8 @@ export const textInputOptionsBaseSchema = z.object({ }) export const textInputOptionsSchema = textInputOptionsBaseSchema - .and(optionBaseSchema) - .and( + .merge(optionBaseSchema) + .merge( z.object({ isLong: z.boolean(), }) @@ -23,7 +23,7 @@ export const defaultTextInputOptions: TextInputOptions = { labels: { button: defaultButtonLabel, placeholder: 'Type your answer...' }, } -export const textInputSchema = blockBaseSchema.and( +export const textInputSchema = blockBaseSchema.merge( z.object({ type: z.enum([InputBlockType.TEXT]), options: textInputOptionsSchema, diff --git a/packages/models/features/blocks/inputs/url.ts b/packages/models/features/blocks/inputs/url.ts index 7639a3df2..b6824ae33 100644 --- a/packages/models/features/blocks/inputs/url.ts +++ b/packages/models/features/blocks/inputs/url.ts @@ -5,14 +5,14 @@ import { InputBlockType } from './enums' import { textInputOptionsBaseSchema } from './text' export const urlInputOptionsSchema = optionBaseSchema - .and(textInputOptionsBaseSchema) - .and( + .merge(textInputOptionsBaseSchema) + .merge( z.object({ retryMessageContent: z.string(), }) ) -export const urlInputSchema = blockBaseSchema.and( +export const urlInputSchema = blockBaseSchema.merge( z.object({ type: z.enum([InputBlockType.URL]), options: urlInputOptionsSchema, diff --git a/packages/models/features/blocks/integrations/chatwoot.ts b/packages/models/features/blocks/integrations/chatwoot.ts index 72fec765d..5073db847 100644 --- a/packages/models/features/blocks/integrations/chatwoot.ts +++ b/packages/models/features/blocks/integrations/chatwoot.ts @@ -19,7 +19,7 @@ export const chatwootOptionsSchema = z.object({ .optional(), }) -export const chatwootBlockSchema = blockBaseSchema.and( +export const chatwootBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([IntegrationBlockType.CHATWOOT]), options: chatwootOptionsSchema, diff --git a/packages/models/features/blocks/integrations/googleAnalytics.ts b/packages/models/features/blocks/integrations/googleAnalytics.ts index 32b20f1c9..f0e86e15e 100644 --- a/packages/models/features/blocks/integrations/googleAnalytics.ts +++ b/packages/models/features/blocks/integrations/googleAnalytics.ts @@ -10,7 +10,7 @@ export const googleAnalyticsOptionsSchema = z.object({ value: z.number().optional(), }) -export const googleAnalyticsBlockSchema = blockBaseSchema.and( +export const googleAnalyticsBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([IntegrationBlockType.GOOGLE_ANALYTICS]), options: googleAnalyticsOptionsSchema, diff --git a/packages/models/features/blocks/integrations/googleSheets/schemas.ts b/packages/models/features/blocks/integrations/googleSheets/schemas.ts index b6a3ef67b..4ca983f2e 100644 --- a/packages/models/features/blocks/integrations/googleSheets/schemas.ts +++ b/packages/models/features/blocks/integrations/googleSheets/schemas.ts @@ -29,7 +29,13 @@ const rowsFilterComparisonSchema = z.object({ value: z.string().optional(), }) -const googleSheetsGetOptionsSchema = googleSheetsOptionsBaseSchema.and( +const initialGoogleSheetsOptionsSchema = googleSheetsOptionsBaseSchema.merge( + z.object({ + action: z.undefined(), + }) +) + +const googleSheetsGetOptionsSchema = googleSheetsOptionsBaseSchema.merge( z.object({ action: z.enum([GoogleSheetsAction.GET]), // TODO: remove referenceCell once migrated to filtering @@ -42,14 +48,14 @@ const googleSheetsGetOptionsSchema = googleSheetsOptionsBaseSchema.and( }) ) -const googleSheetsInsertRowOptionsSchema = googleSheetsOptionsBaseSchema.and( +const googleSheetsInsertRowOptionsSchema = googleSheetsOptionsBaseSchema.merge( z.object({ action: z.enum([GoogleSheetsAction.INSERT_ROW]), cellsToInsert: z.array(cellSchema), }) ) -const googleSheetsUpdateRowOptionsSchema = googleSheetsOptionsBaseSchema.and( +const googleSheetsUpdateRowOptionsSchema = googleSheetsOptionsBaseSchema.merge( z.object({ action: z.enum([GoogleSheetsAction.UPDATE_ROW]), cellsToUpsert: z.array(cellSchema), @@ -57,12 +63,14 @@ const googleSheetsUpdateRowOptionsSchema = googleSheetsOptionsBaseSchema.and( }) ) -export const googleSheetsOptionsSchema = googleSheetsGetOptionsSchema - .or(googleSheetsInsertRowOptionsSchema) - .or(googleSheetsUpdateRowOptionsSchema) - .or(googleSheetsOptionsBaseSchema) +export const googleSheetsOptionsSchema = z.discriminatedUnion('action', [ + googleSheetsGetOptionsSchema, + googleSheetsInsertRowOptionsSchema, + googleSheetsUpdateRowOptionsSchema, + initialGoogleSheetsOptionsSchema, +]) -export const googleSheetsBlockSchema = blockBaseSchema.and( +export const googleSheetsBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([IntegrationBlockType.GOOGLE_SHEETS]), options: googleSheetsOptionsSchema, diff --git a/packages/models/features/blocks/integrations/index.ts b/packages/models/features/blocks/integrations/index.ts index df6c88689..365ab7d1c 100644 --- a/packages/models/features/blocks/integrations/index.ts +++ b/packages/models/features/blocks/integrations/index.ts @@ -4,7 +4,6 @@ export * from './googleAnalytics' export * from './googleSheets' export * from './makeCom' export * from './pabblyConnect' -export * from './schemas' export * from './sendEmail' export * from './webhook' export * from './zapier' diff --git a/packages/models/features/blocks/integrations/makeCom.ts b/packages/models/features/blocks/integrations/makeCom.ts index 6a2c15373..4650f21cc 100644 --- a/packages/models/features/blocks/integrations/makeCom.ts +++ b/packages/models/features/blocks/integrations/makeCom.ts @@ -3,7 +3,7 @@ import { blockBaseSchema } from '../baseSchemas' import { IntegrationBlockType } from './enums' import { webhookOptionsSchema } from './webhook' -export const makeComBlockSchema = blockBaseSchema.and( +export const makeComBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([IntegrationBlockType.MAKE_COM]), options: webhookOptionsSchema, diff --git a/packages/models/features/blocks/integrations/pabblyConnect.ts b/packages/models/features/blocks/integrations/pabblyConnect.ts index 3abccf946..45fc3105c 100644 --- a/packages/models/features/blocks/integrations/pabblyConnect.ts +++ b/packages/models/features/blocks/integrations/pabblyConnect.ts @@ -3,7 +3,7 @@ import { blockBaseSchema } from '../baseSchemas' import { IntegrationBlockType } from './enums' import { webhookOptionsSchema } from './webhook' -export const pabblyConnectBlockSchema = blockBaseSchema.and( +export const pabblyConnectBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([IntegrationBlockType.PABBLY_CONNECT]), options: webhookOptionsSchema, diff --git a/packages/models/features/blocks/integrations/schemas.ts b/packages/models/features/blocks/integrations/schemas.ts deleted file mode 100644 index b9b68cccb..000000000 --- a/packages/models/features/blocks/integrations/schemas.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { z } from 'zod' -import { chatwootBlockSchema } from './chatwoot' -import { googleAnalyticsBlockSchema } from './googleAnalytics' -import { googleSheetsBlockSchema } from './googleSheets/schemas' -import { makeComBlockSchema } from './makeCom' -import { openAIBlockSchema } from './openai' -import { pabblyConnectBlockSchema } from './pabblyConnect' -import { sendEmailBlockSchema } from './sendEmail' -import { webhookBlockSchema } from './webhook' -import { zapierBlockSchema } from './zapier' - -export const integrationBlockSchema = googleSheetsBlockSchema - .or(googleAnalyticsBlockSchema) - .or(webhookBlockSchema) - .or(sendEmailBlockSchema) - .or(zapierBlockSchema) - .or(makeComBlockSchema) - .or(pabblyConnectBlockSchema) - .or(chatwootBlockSchema) - .or(openAIBlockSchema) - -export type IntegrationBlock = z.infer -export type IntegrationBlockOptions = IntegrationBlock['options'] diff --git a/packages/models/features/blocks/integrations/sendEmail.ts b/packages/models/features/blocks/integrations/sendEmail.ts index 050472d84..7dd56e359 100644 --- a/packages/models/features/blocks/integrations/sendEmail.ts +++ b/packages/models/features/blocks/integrations/sendEmail.ts @@ -15,7 +15,7 @@ export const sendEmailOptionsSchema = z.object({ attachmentsVariableId: z.string().optional(), }) -export const sendEmailBlockSchema = blockBaseSchema.and( +export const sendEmailBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([IntegrationBlockType.EMAIL]), options: sendEmailOptionsSchema, diff --git a/packages/models/features/blocks/integrations/webhook.ts b/packages/models/features/blocks/integrations/webhook.ts index 0f6eeba73..f369c4276 100644 --- a/packages/models/features/blocks/integrations/webhook.ts +++ b/packages/models/features/blocks/integrations/webhook.ts @@ -21,7 +21,7 @@ export const webhookOptionsSchema = z.object({ isCustomBody: z.boolean().optional(), }) -export const webhookBlockSchema = blockBaseSchema.and( +export const webhookBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([IntegrationBlockType.WEBHOOK]), options: webhookOptionsSchema, diff --git a/packages/models/features/blocks/integrations/zapier.ts b/packages/models/features/blocks/integrations/zapier.ts index 09019d926..470ab9e50 100644 --- a/packages/models/features/blocks/integrations/zapier.ts +++ b/packages/models/features/blocks/integrations/zapier.ts @@ -3,7 +3,7 @@ import { blockBaseSchema } from '../baseSchemas' import { IntegrationBlockType } from './enums' import { webhookOptionsSchema } from './webhook' -export const zapierBlockSchema = blockBaseSchema.and( +export const zapierBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([IntegrationBlockType.ZAPIER]), options: webhookOptionsSchema, diff --git a/packages/models/features/blocks/logic/condition.ts b/packages/models/features/blocks/logic/condition.ts index 0b216c17e..9346fbf72 100644 --- a/packages/models/features/blocks/logic/condition.ts +++ b/packages/models/features/blocks/logic/condition.ts @@ -30,14 +30,14 @@ const conditionContentSchema = z.object({ comparisons: z.array(comparisonSchema), }) -export const conditionItemSchema = itemBaseSchema.and( +export const conditionItemSchema = itemBaseSchema.merge( z.object({ type: z.literal(ItemType.CONDITION), content: conditionContentSchema, }) ) -export const conditionBlockSchema = blockBaseSchema.and( +export const conditionBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([LogicBlockType.CONDITION]), items: z.array(conditionItemSchema), diff --git a/packages/models/features/blocks/logic/index.ts b/packages/models/features/blocks/logic/index.ts index 966d0d293..7dc3029be 100644 --- a/packages/models/features/blocks/logic/index.ts +++ b/packages/models/features/blocks/logic/index.ts @@ -1,7 +1,6 @@ export * from './script' export * from './condition' export * from './enums' -export * from './logicBlock' export * from './redirect' export * from './setVariable' export * from './typebotLink' diff --git a/packages/models/features/blocks/logic/jump.ts b/packages/models/features/blocks/logic/jump.ts index 958ed6e58..5da30473f 100644 --- a/packages/models/features/blocks/logic/jump.ts +++ b/packages/models/features/blocks/logic/jump.ts @@ -7,7 +7,7 @@ export const jumpOptionsSchema = z.object({ blockId: z.string().optional(), }) -export const jumpBlockSchema = blockBaseSchema.and( +export const jumpBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([LogicBlockType.JUMP]), options: jumpOptionsSchema, diff --git a/packages/models/features/blocks/logic/logicBlock.ts b/packages/models/features/blocks/logic/logicBlock.ts deleted file mode 100644 index a9a631073..000000000 --- a/packages/models/features/blocks/logic/logicBlock.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { z } from 'zod' -import { scriptOptionsSchema, scriptBlockSchema } from './script' -import { conditionBlockSchema } from './condition' -import { redirectOptionsSchema, redirectBlockSchema } from './redirect' -import { setVariableOptionsSchema, setVariableBlockSchema } from './setVariable' -import { typebotLinkOptionsSchema, typebotLinkBlockSchema } from './typebotLink' -import { waitBlockSchema, waitOptionsSchema } from './wait' -import { jumpBlockSchema, jumpOptionsSchema } from './jump' - -const logicBlockOptionsSchema = scriptOptionsSchema - .or(redirectOptionsSchema) - .or(setVariableOptionsSchema) - .or(typebotLinkOptionsSchema) - .or(waitOptionsSchema) - .or(jumpOptionsSchema) - -export const logicBlockSchema = scriptBlockSchema - .or(conditionBlockSchema) - .or(redirectBlockSchema) - .or(typebotLinkBlockSchema) - .or(setVariableBlockSchema) - .or(waitBlockSchema) - .or(jumpBlockSchema) - -export type LogicBlock = z.infer -export type LogicBlockOptions = z.infer diff --git a/packages/models/features/blocks/logic/redirect.ts b/packages/models/features/blocks/logic/redirect.ts index 698daabcf..4a5ad8cdc 100644 --- a/packages/models/features/blocks/logic/redirect.ts +++ b/packages/models/features/blocks/logic/redirect.ts @@ -7,7 +7,7 @@ export const redirectOptionsSchema = z.object({ isNewTab: z.boolean(), }) -export const redirectBlockSchema = blockBaseSchema.and( +export const redirectBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([LogicBlockType.REDIRECT]), options: redirectOptionsSchema, diff --git a/packages/models/features/blocks/logic/script.ts b/packages/models/features/blocks/logic/script.ts index b60ae5001..a585e9b75 100644 --- a/packages/models/features/blocks/logic/script.ts +++ b/packages/models/features/blocks/logic/script.ts @@ -8,7 +8,7 @@ export const scriptOptionsSchema = z.object({ shouldExecuteInParentContext: z.boolean().optional(), }) -export const scriptBlockSchema = blockBaseSchema.and( +export const scriptBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([LogicBlockType.SCRIPT]), options: scriptOptionsSchema, diff --git a/packages/models/features/blocks/logic/setVariable.ts b/packages/models/features/blocks/logic/setVariable.ts index d9db9b6f2..8468ea57a 100644 --- a/packages/models/features/blocks/logic/setVariable.ts +++ b/packages/models/features/blocks/logic/setVariable.ts @@ -8,7 +8,7 @@ export const setVariableOptionsSchema = z.object({ isCode: z.boolean().optional(), }) -export const setVariableBlockSchema = blockBaseSchema.and( +export const setVariableBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([LogicBlockType.SET_VARIABLE]), options: setVariableOptionsSchema, diff --git a/packages/models/features/blocks/logic/typebotLink.ts b/packages/models/features/blocks/logic/typebotLink.ts index a574a41d6..cb600d038 100644 --- a/packages/models/features/blocks/logic/typebotLink.ts +++ b/packages/models/features/blocks/logic/typebotLink.ts @@ -7,7 +7,7 @@ export const typebotLinkOptionsSchema = z.object({ groupId: z.string().optional(), }) -export const typebotLinkBlockSchema = blockBaseSchema.and( +export const typebotLinkBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([LogicBlockType.TYPEBOT_LINK]), options: typebotLinkOptionsSchema, diff --git a/packages/models/features/blocks/logic/wait.ts b/packages/models/features/blocks/logic/wait.ts index dd3d7b27c..9e8cadc92 100644 --- a/packages/models/features/blocks/logic/wait.ts +++ b/packages/models/features/blocks/logic/wait.ts @@ -6,7 +6,7 @@ export const waitOptionsSchema = z.object({ secondsToWaitFor: z.string().optional(), }) -export const waitBlockSchema = blockBaseSchema.and( +export const waitBlockSchema = blockBaseSchema.merge( z.object({ type: z.enum([LogicBlockType.WAIT]), options: waitOptionsSchema, diff --git a/packages/models/features/blocks/schemas.ts b/packages/models/features/blocks/schemas.ts index 79891563c..a20e1d6c5 100644 --- a/packages/models/features/blocks/schemas.ts +++ b/packages/models/features/blocks/schemas.ts @@ -1,28 +1,49 @@ import { z } from 'zod' import { BubbleBlockType } from './bubbles/enums' -import { BubbleBlock, bubbleBlockSchema } from './bubbles/schemas' -import { ChoiceInputBlock } from './inputs/choice' +import { ChoiceInputBlock, choiceInputSchema } from './inputs/choice' import { InputBlockType } from './inputs/enums' -import { - InputBlock, - InputBlockOptions, - inputBlockSchema, -} from './inputs/schemas' import { IntegrationBlockType } from './integrations/enums' -import { - IntegrationBlock, - IntegrationBlockOptions, - integrationBlockSchema, -} from './integrations/schemas' -import { ConditionBlock } from './logic/condition' +import { ConditionBlock, conditionBlockSchema } from './logic/condition' import { LogicBlockType } from './logic/enums' -import { - LogicBlock, - LogicBlockOptions, - logicBlockSchema, -} from './logic/logicBlock' import { blockBaseSchema } from './baseSchemas' import { startBlockSchema } from './start/schemas' +import { + textBubbleBlockSchema, + imageBubbleBlockSchema, + videoBubbleBlockSchema, + embedBubbleBlockSchema, + audioBubbleBlockSchema, +} from './bubbles' +import { + textInputSchema, + emailInputSchema, + numberInputSchema, + urlInputSchema, + phoneNumberInputBlockSchema, + dateInputSchema, + paymentInputSchema, + ratingInputBlockSchema, + fileInputStepSchema, +} from './inputs' +import { + chatwootBlockSchema, + googleAnalyticsBlockSchema, + googleSheetsBlockSchema, + makeComBlockSchema, + pabblyConnectBlockSchema, + sendEmailBlockSchema, + webhookBlockSchema, + zapierBlockSchema, +} from './integrations' +import { openAIBlockSchema } from './integrations/openai' +import { + scriptBlockSchema, + redirectBlockSchema, + setVariableBlockSchema, + typebotLinkBlockSchema, + waitBlockSchema, +} from './logic' +import { jumpBlockSchema } from './logic/jump' export type DraggableBlock = | BubbleBlock @@ -67,10 +88,74 @@ export type BlockIndices = { blockIndex: number } -export const blockSchema = startBlockSchema - .or(bubbleBlockSchema) - .or(inputBlockSchema) - .or(logicBlockSchema) - .or(integrationBlockSchema) +const bubbleBlockSchema = z.discriminatedUnion('type', [ + textBubbleBlockSchema, + imageBubbleBlockSchema, + videoBubbleBlockSchema, + embedBubbleBlockSchema, + audioBubbleBlockSchema, +]) + +export type BubbleBlock = z.infer +export type BubbleBlockContent = BubbleBlock['content'] + +export const inputBlockSchema = z.discriminatedUnion('type', [ + textInputSchema, + choiceInputSchema, + emailInputSchema, + numberInputSchema, + urlInputSchema, + phoneNumberInputBlockSchema, + dateInputSchema, + paymentInputSchema, + ratingInputBlockSchema, + fileInputStepSchema, +]) + +export type InputBlock = z.infer +export type InputBlockOptions = InputBlock['options'] + +export const logicBlockSchema = z.discriminatedUnion('type', [ + scriptBlockSchema, + conditionBlockSchema, + redirectBlockSchema, + setVariableBlockSchema, + typebotLinkBlockSchema, + waitBlockSchema, + jumpBlockSchema, +]) + +export type LogicBlock = z.infer + +export type LogicBlockOptions = LogicBlock extends + | { + options?: infer Options + } + | {} + ? Options + : never + +export const integrationBlockSchema = z.discriminatedUnion('type', [ + chatwootBlockSchema, + googleAnalyticsBlockSchema, + googleSheetsBlockSchema, + makeComBlockSchema, + openAIBlockSchema, + pabblyConnectBlockSchema, + sendEmailBlockSchema, + webhookBlockSchema, + zapierBlockSchema, +]) + +export type IntegrationBlock = z.infer +export type IntegrationBlockOptions = IntegrationBlock['options'] + +export const blockSchema = z.union([ + startBlockSchema, + bubbleBlockSchema, + inputBlockSchema, + logicBlockSchema, + integrationBlockSchema, +]) export type Block = z.infer diff --git a/packages/models/features/blocks/start/schemas.ts b/packages/models/features/blocks/start/schemas.ts index a7f03e53f..3c2f85198 100644 --- a/packages/models/features/blocks/start/schemas.ts +++ b/packages/models/features/blocks/start/schemas.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { blockBaseSchema } from '../baseSchemas' -export const startBlockSchema = blockBaseSchema.and( +export const startBlockSchema = blockBaseSchema.merge( z.object({ type: z.literal('start'), label: z.string(), diff --git a/packages/models/features/chat.ts b/packages/models/features/chat.ts index 81ebe5085..0d34d3148 100644 --- a/packages/models/features/chat.ts +++ b/packages/models/features/chat.ts @@ -1,7 +1,6 @@ import { z } from 'zod' import { googleAnalyticsOptionsSchema, - inputBlockSchema, paymentInputRuntimeOptionsSchema, redirectOptionsSchema, } from './blocks' @@ -9,7 +8,6 @@ import { publicTypebotSchema } from './publicTypebot' import { logSchema, resultSchema } from './result' import { typebotSchema } from './typebot' import { - BubbleBlockType, textBubbleContentSchema, imageBubbleContentSchema, videoBubbleContentSchema, @@ -17,6 +15,8 @@ import { embedBubbleContentSchema, } from './blocks/bubbles' import { answerSchema } from './answer' +import { BubbleBlockType } from './blocks/bubbles/enums' +import { inputBlockSchema } from './blocks/schemas' const typebotInSessionStateSchema = publicTypebotSchema.pick({ id: true, @@ -40,7 +40,7 @@ const resultInSessionStateSchema = resultSchema .pick({ variables: true, }) - .and( + .merge( z.object({ answers: z.array(answerInSessionStateSchema), id: z.string().optional(), @@ -72,7 +72,7 @@ const chatSessionSchema = z.object({ }) const textMessageSchema = z.object({ - type: z.enum([BubbleBlockType.TEXT]), + type: z.literal(BubbleBlockType.TEXT), content: textBubbleContentSchema.omit({ richText: true, }), @@ -99,17 +99,19 @@ const embedMessageSchema = z.object({ .omit({ height: true, }) - .and(z.object({ height: z.number().optional() })), + .merge(z.object({ height: z.number().optional() })), }) const chatMessageSchema = z .object({ id: z.string() }) .and( - textMessageSchema - .or(imageMessageSchema) - .or(videoMessageSchema) - .or(audioMessageSchema) - .or(embedMessageSchema) + z.discriminatedUnion('type', [ + textMessageSchema, + imageMessageSchema, + videoMessageSchema, + audioMessageSchema, + embedMessageSchema, + ]) ) const scriptToExecuteSchema = z.object({ @@ -187,7 +189,7 @@ const replyLogSchema = logSchema status: true, description: true, }) - .and(z.object({ details: z.unknown().optional() })) + .merge(z.object({ details: z.unknown().optional() })) const clientSideActionSchema = z .object({ diff --git a/packages/models/features/publicTypebot.ts b/packages/models/features/publicTypebot.ts index 66fd4f581..03684385f 100644 --- a/packages/models/features/publicTypebot.ts +++ b/packages/models/features/publicTypebot.ts @@ -22,7 +22,7 @@ export const publicTypebotSchema = z.object({ settings: settingsSchema, }) satisfies z.ZodType -const publicTypebotWithName = publicTypebotSchema.and( +const publicTypebotWithName = publicTypebotSchema.merge( typebotSchema.pick({ name: true, isArchived: true, isClosed: true }) ) diff --git a/packages/models/features/result.ts b/packages/models/features/result.ts index e55742c98..ea952c85d 100644 --- a/packages/models/features/result.ts +++ b/packages/models/features/result.ts @@ -14,13 +14,13 @@ export const resultSchema = z.object({ isArchived: z.boolean().nullable(), }) satisfies z.ZodType -export const resultWithAnswersSchema = resultSchema.and( +export const resultWithAnswersSchema = resultSchema.merge( z.object({ answers: z.array(answerSchema), }) ) -export const resultWithAnswersInputSchema = resultSchema.and( +export const resultWithAnswersInputSchema = resultSchema.merge( z.object({ answers: z.array(answerInputSchema), }) diff --git a/packages/models/features/typebot/theme/schemas.ts b/packages/models/features/typebot/theme/schemas.ts index dadfd9b24..b10f60a87 100644 --- a/packages/models/features/typebot/theme/schemas.ts +++ b/packages/models/features/typebot/theme/schemas.ts @@ -11,7 +11,7 @@ const containerColorsSchema = z.object({ color: z.string(), }) -const inputColorsSchema = containerColorsSchema.and( +const inputColorsSchema = containerColorsSchema.merge( z.object({ placeholderColor: z.string(), }) diff --git a/packages/models/features/typebot/typebot.ts b/packages/models/features/typebot/typebot.ts index e0b8f5d85..708c4a3ca 100644 --- a/packages/models/features/typebot/typebot.ts +++ b/packages/models/features/typebot/typebot.ts @@ -1,9 +1,9 @@ import { z } from 'zod' import { settingsSchema } from './settings' -import { blockSchema } from '../blocks' import { themeSchema } from './theme' import { variableSchema } from './variable' import { Typebot as TypebotPrisma } from 'db' +import { blockSchema } from '../blocks/schemas' export const groupSchema = z.object({ id: z.string(),