import { ZodRawShape, ZodTypeAny } from 'zod' import { AuthDefinition, BlockDefinition, ActionDefinition } from './types' import { ZodLayoutMetadata, z } from './zod' export const variableStringSchema = z.custom<`{{${string}}}`>((val) => /^{{.+}}$/g.test(val as string) ) export const createAuth = (authDefinition: A) => authDefinition export const createBlock = < Id extends string, A extends AuthDefinition, O extends z.ZodObject >( blockDefinition: BlockDefinition ): BlockDefinition => blockDefinition export const createVersionedBlock = < Blocks extends Record< string, BlockDefinition> > >( blocks: Blocks ): Blocks => blocks export const createAction = < A extends AuthDefinition, BaseOptions extends z.ZodObject = z.ZodObject<{}>, O extends z.ZodObject = z.ZodObject<{}> >( actionDefinition: { auth?: A baseOptions?: BaseOptions } & ActionDefinition ) => actionDefinition export const parseBlockSchema = < I extends string, A extends AuthDefinition, O extends z.ZodObject >( blockDefinition: BlockDefinition ) => { const options = z.discriminatedUnion('action', [ blockDefinition.options ? blockDefinition.options.extend({ credentialsId: z.string().optional(), action: z.undefined(), }) : z.object({ credentialsId: z.string().optional(), action: z.undefined(), }), ...blockDefinition.actions.map((action) => blockDefinition.options ? (blockDefinition.options .extend({ credentialsId: z.string().optional(), }) .extend({ action: z.literal(action.name), }) .merge(action.options ?? z.object({})) as any) : z .object({ credentialsId: z.string().optional(), }) .extend({ action: z.literal(action.name), }) .merge(action.options ?? z.object({})) ), ]) return z.object({ id: z.string(), outgoingEdgeId: z.string().optional(), type: z.literal(blockDefinition.id), options: options.optional(), }) } export const parseBlockCredentials = ( blockId: I, authSchema: z.ZodObject ) => { return z.object({ id: z.string(), type: z.literal(blockId), createdAt: z.date(), workspaceId: z.string(), name: z.string(), iv: z.string(), data: authSchema, }) } export const option = { object: (schema: T) => z.object(schema), literal: (value: T) => z.literal(value), string: z.string().optional(), boolean: z.boolean().optional(), enum: (values: readonly [T, ...T[]]) => z.enum(values).optional(), number: z.number().or(variableStringSchema).optional(), array: (schema: T) => z.array(schema).optional(), discriminatedUnion: < T extends string, J extends [ z.ZodDiscriminatedUnionOption, ...z.ZodDiscriminatedUnionOption[] ] >( field: T, schemas: J ) => // @ts-expect-error z.discriminatedUnion(field, [ z.object({ [field]: z.undefined() }), ...schemas, ]), saveResponseArray: ( items: I, layouts?: { item?: ZodLayoutMetadata variableId?: ZodLayoutMetadata } ) => z .array( z.object({ item: z .enum(items) .optional() .layout({ ...(layouts?.item ?? {}), placeholder: 'Select a response', defaultValue: items[0], }), variableId: z .string() .optional() .layout({ ...(layouts?.variableId ?? {}), inputType: 'variableDropdown', }), }) ) .optional(), filter: ({ operators = defaultFilterOperators, isJoinerHidden, }: { operators?: readonly [string, ...string[]] isJoinerHidden: (currentObj: Record) => boolean }) => z .object({ comparisons: z.array( z.object({ input: z.string().optional().layout({ label: 'Enter a field ' }), operator: z .enum(operators) .optional() .layout({ defaultValue: 'Equal to' }), value: z .string() .optional() .layout({ placeholder: 'Enter a value' }), }) ), joiner: z.enum(['AND', 'OR']).optional().layout({ placeholder: 'Select joiner', isHidden: isJoinerHidden, }), }) .optional(), } const defaultFilterOperators = [ 'Equal to', 'Not equal', 'Contains', 'Does not contain', 'Greater than', 'Less than', 'Is set', 'Is empty', 'Starts with', 'Ends with', 'Matches regex', 'Does not match regex', ] as const export type * from './types'