First cut
This commit is contained in:
152
shared/src/schemas/index.ts
Normal file
152
shared/src/schemas/index.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// ── Site Context ──
|
||||
export const siteContextSchema = z.object({
|
||||
businessName: z.string(),
|
||||
tagline: z.string().optional(),
|
||||
tone: z.string().default('professional and friendly'),
|
||||
style: z.string().optional(),
|
||||
promptContext: z.string().optional(),
|
||||
primaryColor: z.string().optional(),
|
||||
contactEmail: z.string().email().optional(),
|
||||
contactPhone: z.string().optional(),
|
||||
address: z.string().optional(),
|
||||
});
|
||||
export type SiteContext = z.infer<typeof siteContextSchema>;
|
||||
|
||||
// ── Section File (union of section types) ──
|
||||
const baseSectionFields = {
|
||||
id: z.string(),
|
||||
order: z.number().int().default(0),
|
||||
visible: z.boolean().default(true),
|
||||
image: z.string().optional(),
|
||||
};
|
||||
|
||||
export const heroSectionSchema = z.object({
|
||||
...baseSectionFields,
|
||||
type: z.literal('hero'),
|
||||
headline: z.string(),
|
||||
subheading: z.string().optional(),
|
||||
ctaText: z.string().optional(),
|
||||
ctaLink: z.string().optional(),
|
||||
});
|
||||
|
||||
export const aboutSectionSchema = z.object({
|
||||
...baseSectionFields,
|
||||
type: z.literal('about'),
|
||||
title: z.string().default('About Us'),
|
||||
content: z.string(),
|
||||
});
|
||||
|
||||
export const featureItemSchema = z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
icon: z.string().optional(),
|
||||
});
|
||||
|
||||
export const featuresSectionSchema = z.object({
|
||||
...baseSectionFields,
|
||||
type: z.literal('features'),
|
||||
title: z.string().default('What We Offer'),
|
||||
items: z.array(featureItemSchema).min(1),
|
||||
});
|
||||
|
||||
export const testimonialItemSchema = z.object({
|
||||
quote: z.string(),
|
||||
author: z.string(),
|
||||
role: z.string().optional(),
|
||||
});
|
||||
|
||||
export const testimonialsSectionSchema = z.object({
|
||||
...baseSectionFields,
|
||||
type: z.literal('testimonials'),
|
||||
title: z.string().default('What Our Clients Say'),
|
||||
items: z.array(testimonialItemSchema).min(1),
|
||||
});
|
||||
|
||||
export const textSectionSchema = z.object({
|
||||
...baseSectionFields,
|
||||
type: z.literal('text'),
|
||||
heading: z.string().optional(),
|
||||
content: z.string(),
|
||||
});
|
||||
|
||||
export const sectionFileSchema = z.discriminatedUnion('type', [
|
||||
heroSectionSchema,
|
||||
aboutSectionSchema,
|
||||
featuresSectionSchema,
|
||||
testimonialsSectionSchema,
|
||||
textSectionSchema,
|
||||
]);
|
||||
export type SectionFile = z.infer<typeof sectionFileSchema>;
|
||||
|
||||
// ── Events ──
|
||||
export const eventItemSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
description: z.string().optional(),
|
||||
date: z.string(),
|
||||
time: z.string().optional(),
|
||||
location: z.string().optional(),
|
||||
});
|
||||
|
||||
export const eventsFileSchema = z.object({
|
||||
events: z.array(eventItemSchema),
|
||||
});
|
||||
export type EventsFile = z.infer<typeof eventsFileSchema>;
|
||||
|
||||
// ── SMS Sites Config ──
|
||||
export const smsSiteEntrySchema = z.object({
|
||||
siteId: z.string(),
|
||||
phoneNumber: z.string(),
|
||||
allowedSenders: z.array(z.string()),
|
||||
repoRoot: z.string().optional(),
|
||||
});
|
||||
|
||||
export const smsSitesConfigSchema = z.object({
|
||||
sites: z.array(smsSiteEntrySchema),
|
||||
});
|
||||
export type SmsSitesConfig = z.infer<typeof smsSitesConfigSchema>;
|
||||
|
||||
// ── Edit Request ──
|
||||
export const editRequestSchema = z.object({
|
||||
message: z.string().min(1),
|
||||
repo_relative_path: z.string().optional(),
|
||||
proposal_id: z.string().optional(),
|
||||
confirm: z.enum(['yes', 'no']).optional(),
|
||||
});
|
||||
export type EditRequest = z.infer<typeof editRequestSchema>;
|
||||
|
||||
export const editJobPayloadSchema = z.discriminatedUnion('kind', [
|
||||
z.object({
|
||||
kind: z.literal('propose'),
|
||||
id: z.string(),
|
||||
message: z.string(),
|
||||
repo_relative_path: z.string().optional(),
|
||||
source: z.enum(['sms', 'http', 'editor']).default('http'),
|
||||
smsReplyMeta: z.object({
|
||||
from: z.string(),
|
||||
to: z.string(),
|
||||
}).optional(),
|
||||
}),
|
||||
z.object({
|
||||
kind: z.literal('apply'),
|
||||
id: z.string(),
|
||||
proposal_id: z.string(),
|
||||
source: z.enum(['sms', 'http', 'editor']).default('http'),
|
||||
smsReplyMeta: z.object({
|
||||
from: z.string(),
|
||||
to: z.string(),
|
||||
}).optional(),
|
||||
}),
|
||||
]);
|
||||
export type EditJobPayload = z.infer<typeof editJobPayloadSchema>;
|
||||
|
||||
// ── Routing output (LLM structured output) ──
|
||||
export const routingOutputSchema = z.object({
|
||||
repo_relative_path: z.string(),
|
||||
needs_clarification: z.boolean().default(false),
|
||||
reason: z.string(),
|
||||
clarification_message: z.string().optional(),
|
||||
});
|
||||
export type RoutingOutput = z.infer<typeof routingOutputSchema>;
|
||||
Reference in New Issue
Block a user