/** * Zod schemas for strong structural validation */ import { z } from 'zod'; import { JWT_ID_PATTERN } from './constants'; // Core schemas export const PlanSummarySchema = z.object({ jwtId: z.string().regex(JWT_ID_PATTERN, 'Invalid JWT ID format'), handleId: z.string().min(1), name: z.string().min(1), description: z.string(), issuerDid: z.string().startsWith('did:key:'), agentDid: z.string().startsWith('did:key:'), startTime: z.string().datetime(), endTime: z.string().datetime(), locLat: z.number().nullable().optional(), locLon: z.number().nullable().optional(), url: z.string().url().nullable().optional(), version: z.string() }); export const PreviousClaimSchema = z.object({ jwtId: z.string().regex(JWT_ID_PATTERN), claimType: z.string(), claimData: z.record(z.unknown()), metadata: z.object({ createdAt: z.string().datetime(), updatedAt: z.string().datetime() }) }); export const PlanSummaryAndPreviousClaimSchema = z.object({ planSummary: PlanSummarySchema, previousClaim: PreviousClaimSchema.optional() }); export const StarredProjectsResponseSchema = z.object({ data: z.array(PlanSummaryAndPreviousClaimSchema), hitLimit: z.boolean(), pagination: z.object({ hasMore: z.boolean(), nextAfterId: z.string().regex(JWT_ID_PATTERN).nullable() }) }); export const StarredProjectsRequestSchema = z.object({ planIds: z.array(z.string()).min(1), afterId: z.string().regex(JWT_ID_PATTERN).optional(), beforeId: z.string().regex(JWT_ID_PATTERN).optional(), limit: z.number().min(1).max(100).default(100) }); // Deep link parameter validation export const DeepLinkParamsSchema = z.object({ jwtIds: z.array(z.string().regex(JWT_ID_PATTERN)).max(10).optional(), projectId: z.string().regex(/^[a-zA-Z0-9_-]+$/).optional(), jwtId: z.string().regex(JWT_ID_PATTERN).optional(), shortlink: z.string().min(1).optional() }).refine( (data) => data.jwtIds || data.jwtId || data.projectId || data.shortlink, 'At least one of jwtIds, jwtId, projectId, or shortlink must be provided' ); // Error response schema export const ErrorResponseSchema = z.object({ error: z.string(), code: z.string().optional(), message: z.string(), details: z.record(z.unknown()).optional(), retryAfter: z.number().optional(), requestId: z.string().optional() }); // Rate limit response schema export const RateLimitResponseSchema = z.object({ error: z.literal('Rate limit exceeded'), code: z.literal('RATE_LIMIT_EXCEEDED'), message: z.string(), details: z.object({ limit: z.number(), window: z.string(), resetAt: z.string().datetime(), remaining: z.number().optional() }), retryAfter: z.number(), requestId: z.string() }); // Acknowledgment request schema export const AcknowledgmentRequestSchema = z.object({ acknowledgedJwtIds: z.array(z.string().regex(JWT_ID_PATTERN)).min(1), acknowledgedAt: z.string().datetime(), clientVersion: z.string() }); // Acknowledgment response schema export const AcknowledgmentResponseSchema = z.object({ acknowledged: z.number(), failed: z.number(), alreadyAcknowledged: z.number(), acknowledgmentId: z.string(), timestamp: z.string().datetime() }); // Clock sync response schema export const ClockSyncResponseSchema = z.object({ serverTime: z.number(), clientTime: z.number(), offset: z.number(), ntpServers: z.array(z.string()).optional() }); // Type exports for use in host apps (only export types not already in types.ts) export type DeepLinkParams = z.infer; export type ErrorResponse = z.infer; export type RateLimitResponse = z.infer; export type AcknowledgmentRequest = z.infer; export type AcknowledgmentResponse = z.infer; export type ClockSyncResponse = z.infer;