5.6 KiB
5.6 KiB
Tasks: enterprise-email-processing
Change ID: enterprise-email-processing
Package: @suaveplan/email-processing
Target path: universal/email/email-processing
Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/validation
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create
universal/email/email-processing/withsrc/,tests/,src/__benchmarks__/and all feature subdirectories per the layout inproposal.md. - Write
package.jsonat version0.1.0with"type": "module","sideEffects": false, correctexportsmap (one subpath per feature module),dependencies, anddevDependencies. - Write
tsconfig.jsonextending@suaveplan/typescript-config/library.json. - Write
vitest.config.tsusingcreateTestConfig()from@suaveplan/testing/config. - Add
lint,typecheck,test,test:coverage,build,docs:checkscripts.
2. Types + errors
src/types/common.ts— defineParsedEmail,EmailAddress,Attachment,ParseOptions,HtmlToTextOptions,ValidateOptions,ValidationResult,EmailParseStream,EmailParseEvent.src/types/common.md(≥200 words, both examples, API reference).src/error-codes.ts— defineEmailProcessingErrorCodeunion (6 codes),EMAIL_PROC_ERROR_METADATA,registerEmailProcessingErrorCodes(), eager call at bottom.src/types/schemas.ts— Zod schemas for all public option types and parsed output shapes;src/types/schemas.md.
3. Core modules
src/core/header-parser/—parseHeaders(raw): fold/unfold RFC 5322 header lines, returnMap<string, string[]>. Tests: fold/unfold round-trip, multi-value headers, encoded-word (RFC 2047) decoding.src/core/address-parser/—parseAddressList(value): RFC 5321/5322 grammar, display names with quotes, group syntax, angle-bracket addresses. Tests: bare address, display-name, group, nested comments, encoded display names.src/core/mime-decoder/— multipart boundary splitting, Base64 and Quoted-Printable decoding, Content-Transfer-Encoding dispatch, charset decoding viaTextDecoder. Tests: multipart/mixed, multipart/alternative, nested multipart, binary attachment, QP line wrapping.src/core/parser/—parseEmail(raw, options): orchestrates header-parser → MIME decoder → body extraction → attachment enumeration. Tests: simple text-only, text+HTML, with attachment, multiple attachments, oversized message (throwsEMAIL_PROC_TOO_LARGE), AbortSignal abort.src/validation/address-validator/—validateAddress(address, options): syntax check (regex), optional disposable-domain lookup (bundled JSON list), result typed. Tests: valid, invalid syntax, disposable domain,checkDisposable:falseskips list.src/extraction/html-to-text/—htmlToText(html, options): strip tags, decode HTML entities, preserve paragraph breaks, handle<a href>link text. Tests: plain paragraph, link, table, nested elements.src/streaming/email-stream/—createEmailStream(options): Web Streams Transform; emitsheaders,textBody,htmlBody,attachmentevents. Tests: pipe a 3-part message, verify events emitted in order, abort mid-stream.
4. OTel instrumentation
- Obtain tracer + meter at module scope (
trace.getTracer('email-processing')). parseEmail: spanemail.parse, attributesemail.attachment_count; counteremail.parse_total; histogramemail.parse_duration_ms.validateAddress: counteremail.validate_totalwith attributeemail.valid.
5. Property-based tests (tests/property.test.ts)
- Header fold/unfold round-trip: folded line reassembles to original for arbitrary ASCII header values ≤998 chars.
- Address list round-trip:
parseAddressList(formatAddressList(list)) ≡ listfor arbitrary arrays ofEmailAddress. - HTML-to-text idempotency:
htmlToText(htmlToText(html)) ≡ htmlToText(html)when input is already plain text (no tags).
6. Benchmarks (src/__benchmarks__/email-processing.bench.ts)
- Parse a 50 KB multipart/mixed message with 2 attachments (cold path).
parseHeadersonly on same message (fast path).parseAddressListon a 50-address list.
7. Docs
- One
.mdper implementation file (≥200 words, both simple + advanced examples, API reference). - Root
README.mdwith purpose, install, quick-start, module table, dependencies.
8. Verify and publish
bunx turbo lint typecheck test --filter=@suaveplan/email-processing— green.bunx turbo test:coverage --filter=@suaveplan/email-processing— 100% statements/branches/functions/lines.bun run docs:check --filter=@suaveplan/email-processing— zero errors.- No
TODO,FIXME,any,.skipinsrc/ortests/. "sideEffects": falseset.bun scripts/publish.ts --package @suaveplan/email-processing.- Archive:
openspec/archive/2026-04-24-enterprise-email-processing/.