4.1 KiB
Spec delta — email-processing
ADDED Requirements
Requirement: Parse RFC 822 messages with hook pipeline
The package SHALL expose parseEmail(raw, options?) that:
- accepts
string | ArrayBuffer | Uint8Array, - validates
optionsvia a strict Zod schema, - raises
EmailProcessingError(INVALID_INPUT)for null / undefined / wrong-typeraw, - raises
EmailProcessingError(INVALID_OPTIONS)for any Zod failure, - wraps
postal-mimeparse failures inEmailProcessingError(PARSE_FAILURE)and increments the OTel counteremail_processing.parse.failure_total, - on success, increments
email_processing.parse.total(attributed by attachment count) and pipes the result through theemail:processinghook pipeline.
Scenario: Successful parse threads through the hook pipeline
- WHEN
parseEmailis called with a valid multipart MIME string and anonEmailProcessinghandler is registered that mutatesparsed.subject - THEN the returned
ParsedEmail.subjectreflects the handler's transformation.
Requirement: Attachment processing with size + MIME allowlist
processAttachment SHALL hash content with SHA-256, validate against
maxSize (default 25 MiB) and allowedMimeTypes (wildcard image/*
supported), increment email_processing.attachment.processed_total or
...rejected_total (attributed by reason), and record an
...extraction_duration_ms histogram entry per call.
Scenario: Attachment over maxSize is rejected with reason=size
- WHEN
processAttachmentis called with content larger than the configuredmaxSize - THEN the result has
valid: false, theerrormentions the size limit, and the rejected counter is incremented withreason=size.
Requirement: Hook pipeline with public registry
The package SHALL expose Hooks<TMap>, emailHooks, and
onEmailProcessing. Handler errors SHALL short-circuit the pipeline
and surface as EmailProcessingError(HOOK_FAILURE). The
email_processing.hook.invocation_total counter SHALL be incremented
once per successful pipeline invocation, attributed by event name and
handler count.
Requirement: DKIM / SPF / DMARC verification
verifyDkimSHALL parse theDKIM-Signatureheader, look up the public key through an injectable DNS resolver, recompute the body hash, rebuild the canonicalised signed-header block, callcrypto.subtle.verify, and emitemail_processing.dkim.result_totalattributed by outcome.checkSpfSHALL enforceMAX_INCLUDE_DEPTH = 10andMAX_DNS_LOOKUPS = 10, emitemail_processing.spf.result_totalattributed by outcome.checkDmarcSHALL combine SPF + DKIM into a relaxed / strict alignment evaluation, emitemail_processing.dmarc.result_totalattributed by outcome.
Requirement: Heuristic spam scoring
scoreSpam SHALL apply the twelve default rules, accept
customRules (which replace defaults with the same id), clamp the
score at [0, 10], classify against suspiciousThreshold (default
3) and spamThreshold (default 6), and emit
email_processing.spam.scored_total attributed by classification.
Requirement: Threading
groupThreads SHALL produce one EmailThread per conversation,
using In-Reply-To, References, and a subject-change heuristic,
sorted chronologically by first-message date. The
email_processing.thread.grouped_total counter SHALL be incremented
with the produced thread count per call.
Requirement: Stable error registry
The package SHALL expose EMAIL_PROCESSING_CODES containing exactly
eight codes — INVALID_INPUT, PARSE_FAILURE, INVALID_OPTIONS,
ATTACHMENT_TOO_LARGE, ATTACHMENT_TYPE_REJECTED, HASH_FAILURE,
DKIM_VERIFY_FAILURE, HOOK_FAILURE. Every code SHALL be thrown by
at least one production path. EmailProcessingError's constructor
SHALL be public.
Scenario: Public constructor allows direct construction
- WHEN a downstream package imports
EmailProcessingErrorandEMAIL_PROCESSING_CODES.INVALID_INPUTand callsnew EmailProcessingError(code, key) - THEN TypeScript accepts the call and the resulting instance
has
code === "EMAIL_PROCESSING.INVALID_INPUT".