import { z } from "zod"; /** * Offline sync envelope (ARCHITEKTUR ยง4.6). The per-op payload schemas live in * src/lib/sync/ops.ts (owned by lane field); this file only fixes the wire format. */ export const SYNC_OP_TYPES = [ "session.start", "session.pause", "session.resume", "session.end", "work_order.transition", "note.create", "checklist.toggle", "material.upsert", "photo.attach", "voice.attach", "report.save_draft", "report.submit", "signature.capture", "emergency.create", ] as const; export type SyncOpType = (typeof SYNC_OP_TYPES)[number]; /** Ops that compare WorkOrder.version and may produce a conflict. All others are additive. */ export const CONFLICTING_OPS: readonly SyncOpType[] = ["work_order.transition", "report.submit"]; export const syncOperationSchema = z.object({ clientOpId: z.string().uuid(), opType: z.enum(SYNC_OP_TYPES), entityType: z.string().max(40).optional(), entityId: z.string().max(64).optional(), baseVersion: z.number().int().positive().optional(), payload: z.record(z.string(), z.unknown()), clientCreatedAt: z.string().datetime(), }); export type SyncOperationInput = z.infer; export const syncRequestSchema = z.object({ deviceId: z.string().max(64), operations: z.array(syncOperationSchema).min(1).max(100), }); export type SyncOpResult = { clientOpId: string; status: "applied" | "duplicate" | "conflict" | "rejected"; /** server ids created by the op, keyed by the client id they replace */ idMap?: Record; entityVersion?: number; errorCode?: "not_found" | "forbidden" | "invalid" | "conflict" | "blocked" | "internal"; message?: string; }; export type SyncResponse = { results: SyncOpResult[]; serverTime: string };