/** * 入站路由契约(编码规范 §8.1)。 * * 通道入站 → 归属解析 → 统一事件流(OS 06)。MVP 的邮件通道以**模拟通道**接入: * mock 只允许出现在**外界**(邮件通道与外部系统),系统内部机制不得 mock(README MVP 完成判据)。 */ import { z } from 'zod'; import type { RouteContract } from './http.ts'; import { channelIdSchema, eventIdSchema } from '../id.ts'; import { ownershipResolutionSchema } from '../envelope.ts'; /** 一封入站邮件的归一形状 —— 真实邮件通道与模拟通道共用 */ export const inboundMailSchema = z.object({ messageId: z.string().min(1), threadId: z.string().optional(), from: z.string().min(1), to: z.array(z.string().min(1)).min(1), cc: z.array(z.string()).default([]), subject: z.string(), body: z.string(), receivedAt: z.iso.datetime(), attachments: z .array(z.object({ name: z.string(), contentType: z.string(), size: z.number().int().min(0), ref: z.string() })) .default([]), }); export type InboundMail = z.infer; export const ingestMailRoute = { method: 'POST', path: '/api/ingest/channel/:channelId/mail', params: z.object({ channelId: channelIdSchema }), body: inboundMailSchema, output: z.object({ eventId: eventIdSchema, resolution: ownershipResolutionSchema, /** 三级消费的判定结果:命中的任务监听数 / 命中的触发器数;皆零则入消息兜底 */ listenersHit: z.number().int().min(0), triggersHit: z.number().int().min(0), fellBackToMessage: z.boolean(), }), } as const satisfies RouteContract;