Archived
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
84 lines
3.4 KiB
TypeScript
84 lines
3.4 KiB
TypeScript
/**
|
|
* module: mail —— 对外类型(编码规范 §10.1;设计稿 §5.1)。
|
|
*/
|
|
import type {
|
|
MailAccountInput,
|
|
MailAccountView,
|
|
MailDeliveryView,
|
|
MailInboundDetail,
|
|
MailInboundSummary,
|
|
MailOutboundView,
|
|
MailPollOutcome,
|
|
MailProbeReport,
|
|
MailSendInput,
|
|
MailSendResult,
|
|
} from '@contracts';
|
|
|
|
export type {
|
|
MailAccountInput,
|
|
MailAccountView,
|
|
MailDeliveryView,
|
|
MailInboundDetail,
|
|
MailInboundSummary,
|
|
MailOutboundView,
|
|
MailPollOutcome,
|
|
MailProbeReport,
|
|
MailSendInput,
|
|
MailSendResult,
|
|
};
|
|
|
|
export {
|
|
MailAccountDisabledError,
|
|
MailAccountNameTakenError,
|
|
MailAccountNotFoundError,
|
|
MailAttachmentNotFoundError,
|
|
MailInboundNotFoundError,
|
|
MailNoDefaultAccountError,
|
|
MailPollBusyError,
|
|
MailSecretMissingError,
|
|
MailSendInvalidError,
|
|
MailSubscriberTakenError,
|
|
MailSubscriberUnknownError,
|
|
} from './internal/domain/errors.ts';
|
|
|
|
/**
|
|
* 订阅方回调 —— 「注册回调读到邮件内容」的落点。
|
|
* 返回 `{skipped}` 表示这封信与我无关(留痕 skipped,不算失败);抛错留痕 failed(原话脱敏)。
|
|
* **自动只投一次,不自动重试**(设计稿 §9.3);人按「重新投递」才再来。
|
|
*/
|
|
export type InboundHandler = (
|
|
mail: MailInboundDetail,
|
|
ctx: { readonly accountId: string; readonly channelId: string | undefined },
|
|
) => Promise<void | { readonly skipped: string }>;
|
|
|
|
export type MailService = {
|
|
/* ── 账号 ── */
|
|
listAccounts(): Promise<MailAccountView[]>;
|
|
getAccount(accountId: string): Promise<MailAccountView>;
|
|
createAccount(input: MailAccountInput): Promise<MailAccountView>;
|
|
/** secret 缺省 = 不改密文;kind / settings 变了游标清空重锚 */
|
|
updateAccount(accountId: string, input: MailAccountInput): Promise<MailAccountView>;
|
|
/** 留痕不删(入站 / 出站表对账号不设外键) */
|
|
deleteAccount(accountId: string): Promise<void>;
|
|
/** 已存账号用库里凭证;未存用入参里的;一个字节都不落库(wecom 探到地址的已存账号除外:回填 address) */
|
|
probe(target: { accountId: string } | { input: MailAccountInput; accountId?: string | undefined }): Promise<MailProbeReport>;
|
|
/* ── 收 ── */
|
|
/** 工作者用:到期且未被占的账号各拉一轮(账号间并行) */
|
|
pollDue(now: Date): Promise<MailPollOutcome[]>;
|
|
/** 路由用:租约被占 → MailPollBusyError;停用 → MailAccountDisabledError */
|
|
pollNow(accountId: string): Promise<MailPollOutcome>;
|
|
/** 同名重复注册 → MailSubscriberTakenError;返回注销函数 */
|
|
subscribe(name: string, handler: InboundHandler): () => void;
|
|
listInbound(query: { accountId?: string | undefined; limit?: number | undefined }): Promise<MailInboundSummary[]>;
|
|
getInbound(mailId: string): Promise<MailInboundDetail>;
|
|
attachment(mailId: string, index: number): Promise<{ name: string; contentType: string; bytes: Uint8Array }>;
|
|
/** 原件 .eml;超限没存 → MailAttachmentNotFoundError(index = -1) */
|
|
raw(mailId: string): Promise<{ name: string; bytes: Uint8Array }>;
|
|
/** 人按的钮:指定订阅方或全部;正在 dispatching 的那条跳过 */
|
|
redeliver(mailId: string, subscriber?: string): Promise<MailDeliveryView[]>;
|
|
/* ── 发 ── */
|
|
/** 先落 sending 行再经 provider 发;厂商失败落 failed 并以 state='failed' 返回,不抛 */
|
|
send(input: MailSendInput): Promise<MailSendResult>;
|
|
listOutbound(query: { accountId?: string | undefined; limit?: number | undefined }): Promise<MailOutboundView[]>;
|
|
};
|