import { z } from "zod/v4"; /** * Surface declarations (plan §7): where an app renders and with which tier. * * - `tree` — tier 1: app logic returns a declarative UI tree; the host * renders it with its own components on every platform. * - `react` — tier 2: a Coline-built React bundle runs in the sandboxed * client runtime. `entry` is the module path within the app * source. * - `web` — tier 3: an iframe served from the app's external origin. * Requires external hosting; excluded from mobile and preview * composition. */ const surfaceEntrySchema = z.string().trim().min(1).max(500); const surfacePathSchema = z .string() .trim() .min(1) .max(2000) .startsWith("/", "Web surface paths must be origin-relative (start with /)."); export const treeSurfaceSchema = z.object({ tier: z.literal("tree"), }); export const reactSurfaceSchema = z.object({ tier: z.literal("react"), entry: surfaceEntrySchema, }); export const webSurfaceSchema = z.object({ tier: z.literal("web"), path: surfacePathSchema, }); export const surfaceVariantSchema = z.discriminatedUnion("tier", [ treeSurfaceSchema, reactSurfaceSchema, webSurfaceSchema, ]); export type SurfaceVariant = z.infer; export type SurfaceTier = SurfaceVariant["tier"]; /** * A surface with an optional mobile override (plan §13): universal by * default; specialize only when the interaction genuinely differs. */ export const appSurfaceSchema = z.discriminatedUnion("tier", [ treeSurfaceSchema.extend({ mobile: surfaceVariantSchema.optional() }), reactSurfaceSchema.extend({ mobile: surfaceVariantSchema.optional() }), webSurfaceSchema.extend({ mobile: surfaceVariantSchema.optional() }), ]); export type AppSurface = z.infer; export const appSurfacesSchema = z .object({ home: appSurfaceSchema.optional(), }) .default({}); export type AppSurfaces = z.infer;