Files
ycode/scripts/rea/patches/ElectronVisualProbeSchema.js
T

50 lines
4.9 KiB
JavaScript

import { z } from 'zod';
import { webScreenshotArtifactSchema } from '../webScreenshot.js';
const sampling = { step_id:z.string().min(1).max(80), frames:z.number().int().min(0).max(24).default(1), interval_ms:z.number().int().min(16).max(500).default(50) };
const target = {selector:z.string().min(1).max(500),index:z.number().int().min(0).max(100).optional()};
export const electronVisualProbeInputSchema = z.strictObject({
include_fonts:z.boolean().default(false),
include_message_content:z.boolean().default(false),
media_selectors:z.array(z.string().trim().min(1).max(500)).max(4).default([]),
track_selectors:z.array(z.string().min(1).max(500)).max(12).default([]),
actions:z.array(z.discriminatedUnion('kind',[
z.strictObject({...sampling,...target,kind:z.literal('hover')}),
z.strictObject({...sampling,...target,kind:z.literal('click'),restore:z.boolean().default(false)}),
z.strictObject({...sampling,...target,kind:z.literal('text'),text:z.string().max(2048).refine(value=>!/[\u0000-\u001f\u007f]/u.test(value),'Single-line text only')}),
z.strictObject({...sampling,...target,kind:z.literal('scroll'),delta_y:z.number().min(-3000).max(3000),restore:z.boolean().default(false)}),
z.strictObject({...sampling,kind:z.literal('key'),selector:z.string().min(1).max(500).optional(),index:z.number().int().min(0).max(100).optional(),key:z.enum(['Escape','Enter','Tab','ArrowDown','ArrowUp','ArrowLeft','ArrowRight','Home','End',' ','+','-','0']),modifiers:z.number().int().min(0).max(15).default(0)}),
z.strictObject({...sampling,kind:z.literal('wait'),duration_ms:z.number().int().min(0).max(2000)}),
z.strictObject({...sampling,kind:z.literal('viewport'),width:z.number().int().min(160).max(3840),height:z.number().int().min(160).max(2160)})
,z.strictObject({...sampling,kind:z.literal('media'),reduced_motion:z.enum(['reduce','no-preference'])})
])).max(12).default([])
}).superRefine((value,ctx)=>{
if(value.actions.reduce((n,a)=>n+a.frames,0)>64)ctx.addIssue({code:'custom',message:'At most 64 scenario frames'});
if(value.actions.reduce((n,a)=>n+a.frames*a.interval_ms+(a.duration_ms??0),0)>15000)ctx.addIssue({code:'custom',message:'Declared sampling and waits must fit 15 seconds'});
if(new Set(value.actions.map(a=>a.step_id)).size!==value.actions.length)ctx.addIssue({code:'custom',message:'Step IDs must be unique'});
if(new Set(value.media_selectors).size!==value.media_selectors.length)ctx.addIssue({code:'custom',message:'Media selectors must be unique'});
});
const mediaIdentity = {
selector:z.string(),frame_id:z.string(),node_index:z.number().int().optional(),backend_node_id:z.number().int().optional(),
selected_node:z.object({node_name:z.literal('IMG'),attributes:z.record(z.string(),z.string()),current_source_url:z.string().nullable()}).optional(),
visibility:z.object({status:z.enum(['observed','covered','unknown']),reason:z.string().optional()}).optional(),
visibility_samples:z.array(z.object({x:z.number(),y:z.number(),hit_backend_node_id:z.number().int().optional(),hit_node_index:z.number().int().optional(),hit_node_name:z.string().optional(),hit_class:z.string().optional()})).max(9).optional(),
source:z.object({url:z.string().nullable(),url_sha256:z.string().regex(/^[a-f0-9]{64}$/),selection:z.enum(['current_source_url','src']),mime_type:z.string()}).optional()
};
export const electronMediaAssetsSchema = z.object({
items:z.array(z.discriminatedUnion('status',[
z.object({...mediaIdentity,status:z.literal('exported'),artifact:z.object({mime_type:z.enum(['image/png','image/jpeg','image/gif','image/webp','image/svg+xml']),bytes:z.number().int().positive().max(2097152),sha256:z.string().regex(/^[a-f0-9]{64}$/),data_base64:z.string().max(2796204)})}),
z.object({...mediaIdentity,status:z.literal('unknown'),reason:z.string()})
])).max(4),
total_bytes:z.number().int().min(0).max(8388608),
limits:z.object({max_assets:z.literal(4),max_asset_bytes:z.literal(2097152),max_total_bytes:z.literal(8388608)}),
limitations:z.array(z.string())
});
export const platformFontSampleSchema = z.object({category:z.string(),text:z.string().max(256),fontFamily:z.string(),codeContext:z.enum(['inline','block']).optional(),platformFonts:z.array(z.object({familyName:z.string(),postScriptName:z.string(),isCustomFont:z.boolean(),glyphCount:z.number().int().min(0)}))});
export const visualProbeOutputSchema = z.object({
status:z.enum(['completed','failed']),
steps:z.array(z.object({step_id:z.string(),kind:z.string(),status:z.enum(['completed','failed']),error:z.string().nullable(),action_completed_at_ms:z.number().optional()})),
frames:z.array(z.object({step_id:z.string(),at_ms:z.number(),artifact:webScreenshotArtifactSchema,regions:z.array(z.object({selector:z.string(),bounds:z.tuple([z.number(),z.number(),z.number(),z.number()]).nullable()}))})),
restoration:z.array(z.object({kind:z.string(),status:z.enum(['completed','failed']),error:z.string().nullable()})),
limitations:z.array(z.string())
});