import { z } from 'zod'; import { SyncProgressSchema } from './chats.js'; const EventBase = { id: z.string(), occurredAt: z.string().datetime({ offset: true }), }; export const BotRuntimeStatusSchema = z.union([ z.literal('starting'), z.literal('running'), z.literal('stopping'), z.literal('stopped'), ]); export const ConversationStatusSchema = z.union([ z.literal('new'), z.literal('awaiting-reply'), z.literal('replied'), z.literal('closed'), ]); export const RealtimeSyncKindSchema = z.union([ z.literal('media'), z.literal('messages'), z.literal('conversations'), z.literal('full'), ]); export const RealtimeRedditSessionStatusSchema = z.union([ z.literal('unknown'), z.literal('authenticating'), z.literal('authenticated'), z.literal('expired'), z.literal('logged-out'), z.literal('error'), ]); export const DashboardEventSchema = z.discriminatedUnion('type', [ z.strictObject({ ...EventBase, type: z.literal('bot.status.changed'), payload: z.strictObject({ status: BotRuntimeStatusSchema, dryRun: z.boolean(), reason: z.string().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('message.sent'), payload: z.strictObject({ messageId: z.string(), recipient: z.string(), conversationId: z.string().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('message.failed'), payload: z.strictObject({ recipient: z.string(), reason: z.string(), messageId: z.string().optional(), conversationId: z.string().optional(), code: z.string().optional(), retryable: z.boolean().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('conversation.updated'), payload: z.strictObject({ conversationId: z.string(), status: ConversationStatusSchema, unreadCount: z.number().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('reply.received'), payload: z.strictObject({ conversationId: z.string(), replyId: z.string(), sender: z.string(), preview: z.string().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('media.received'), payload: z.strictObject({ conversationId: z.string(), sender: z.string(), count: z.number(), }), }), z.strictObject({ ...EventBase, type: z.literal('sync.started'), payload: z.strictObject({ syncId: z.string(), kind: RealtimeSyncKindSchema, }), }), z.strictObject({ ...EventBase, type: z.literal('sync.progress'), payload: z.strictObject({ syncId: z.string(), kind: RealtimeSyncKindSchema, completed: z.number(), total: z.number().optional(), message: z.string().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('sync.completed'), payload: z.strictObject({ syncId: z.string(), kind: RealtimeSyncKindSchema, processed: z.number(), }), }), z.strictObject({ ...EventBase, type: z.literal('sync.failed'), payload: z.strictObject({ syncId: z.string(), kind: RealtimeSyncKindSchema, reason: z.string(), code: z.string().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('rate-limit.changed'), payload: z.strictObject({ limited: z.boolean(), remaining: z.number().optional(), resetsAt: z.string().optional(), reason: z.string().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('reddit.session.changed'), payload: z.strictObject({ status: RealtimeRedditSessionStatusSchema, username: z.string().optional(), reason: z.string().optional(), }), }), z.strictObject({ ...EventBase, type: z.literal('log.created'), payload: z.strictObject({ level: z.union([z.literal('debug'), z.literal('info'), z.literal('warn'), z.literal('error')]), message: z.string(), data: z.unknown().optional(), }), }), ]); /** Existing `/api/logs/stream` frames, retained for an incremental migration. */ export const LegacyDashboardStreamEventSchema = z.discriminatedUnion('type', [ z.strictObject({ type: z.literal('connected'), timestamp: z.number(), }), z.strictObject({ type: z.literal('log'), timestamp: z.number(), level: z.union([z.literal('info'), z.literal('warn'), z.literal('error'), z.literal('debug')]), message: z.string(), data: z.unknown().optional(), }), z.strictObject({ type: z.literal('sync-progress'), progress: SyncProgressSchema.nullable(), }), ]); export type DashboardEvent = z.infer; export type LegacyDashboardStreamEvent = z.infer;