Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 1x 1x 1x 1x 24x 7x 17x 1x 1x 1x 2x 2x 1x 1x 1x 2x 2x 2x 4x 4x 3x 1x 2x 1x 2x 2x 1x 1x | /**
* WalkMe Validation Module
*
* Zod schemas for runtime validation of tour configurations.
* Ensures tour definitions are well-formed before they're used.
*/
import { z } from 'zod'
import type { Tour } from '../types/walkme.types'
// ---------------------------------------------------------------------------
// Schemas
// ---------------------------------------------------------------------------
export const TourTriggerSchema = z.object({
type: z.enum(['onFirstVisit', 'onRouteEnter', 'onEvent', 'manual', 'scheduled']),
delay: z.number().min(0).optional(),
route: z.string().optional(),
event: z.string().optional(),
afterVisits: z.number().min(1).optional(),
afterDays: z.number().min(0).optional(),
})
export const TourConditionsSchema = z.object({
userRole: z.array(z.string()).optional(),
featureFlags: z.array(z.string()).optional(),
completedTours: z.array(z.string()).optional(),
notCompletedTours: z.array(z.string()).optional(),
custom: z.function().optional(),
}).optional()
export const TourStepSchema = z.object({
id: z.string().min(1),
type: z.enum(['tooltip', 'modal', 'spotlight', 'beacon', 'floating']),
title: z.string().min(1),
content: z.string(),
target: z.string().optional(),
route: z.string().optional(),
position: z.enum(['top', 'bottom', 'left', 'right', 'auto']).optional(),
actions: z.array(z.enum(['next', 'prev', 'skip', 'complete', 'close'])).min(1),
delay: z.number().min(0).optional(),
autoAdvance: z.number().min(0).optional(),
beforeShow: z.function().optional(),
afterShow: z.function().optional(),
}).refine(
(step) => {
// tooltip, spotlight, and beacon require a target
if (['tooltip', 'spotlight', 'beacon'].includes(step.type)) {
return !!step.target
}
return true
},
{
message: 'Steps of type tooltip, spotlight, and beacon require a target selector',
},
)
export const TourSchema = z.object({
id: z.string().min(1),
name: z.string().min(1),
description: z.string().optional(),
trigger: TourTriggerSchema,
conditions: TourConditionsSchema,
steps: z.array(TourStepSchema).min(1),
onComplete: z.function().optional(),
onSkip: z.function().optional(),
priority: z.number().optional(),
})
export const TourArraySchema = z.array(TourSchema)
// ---------------------------------------------------------------------------
// Validation Functions
// ---------------------------------------------------------------------------
/** Validate a single tour configuration */
export function validateTour(
tour: unknown,
): { valid: boolean; errors?: z.ZodError; tour?: Tour } {
const result = TourSchema.safeParse(tour)
if (result.success) {
return { valid: true, tour: result.data as unknown as Tour }
}
return { valid: false, errors: result.error }
}
/** Validate an array of tours, returning only the valid ones */
export function validateTours(
tours: unknown[],
): { valid: boolean; errors?: z.ZodError[]; validTours: Tour[] } {
const validTours: Tour[] = []
const errors: z.ZodError[] = []
for (const tour of tours) {
const result = TourSchema.safeParse(tour)
if (result.success) {
validTours.push(result.data as unknown as Tour)
} else {
errors.push(result.error)
}
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : undefined,
validTours,
}
}
/** Validate a single step configuration */
export function validateStep(
step: unknown,
): { valid: boolean; errors?: z.ZodError } {
const result = TourStepSchema.safeParse(step)
if (result.success) {
return { valid: true }
}
return { valid: false, errors: result.error }
}
|