Files
2026-05-27 23:29:53 +01:00

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 status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in 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.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create universal/email/email-processing/ with src/, tests/, src/__benchmarks__/ and all feature subdirectories per the layout in proposal.md.
  • Write package.json at version 0.1.0 with "type": "module", "sideEffects": false, correct exports map (one subpath per feature module), dependencies, and devDependencies.
  • Write tsconfig.json extending @suaveplan/typescript-config/library.json.
  • Write vitest.config.ts using createTestConfig() from @suaveplan/testing/config.
  • Add lint, typecheck, test, test:coverage, build, docs:check scripts.

2. Types + errors

  • src/types/common.ts — define ParsedEmail, EmailAddress, Attachment, ParseOptions, HtmlToTextOptions, ValidateOptions, ValidationResult, EmailParseStream, EmailParseEvent.
  • src/types/common.md (≥200 words, both examples, API reference).
  • src/error-codes.ts — define EmailProcessingErrorCode union (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, return Map<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 via TextDecoder. 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 (throws EMAIL_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:false skips 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; emits headers, textBody, htmlBody, attachment events. 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: span email.parse, attributes email.attachment_count; counter email.parse_total; histogram email.parse_duration_ms.
  • validateAddress: counter email.validate_total with attribute email.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)) ≡ list for arbitrary arrays of EmailAddress.
  • 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).
  • parseHeaders only on same message (fast path).
  • parseAddressList on a 50-address list.

7. Docs

  • One .md per implementation file (≥200 words, both simple + advanced examples, API reference).
  • Root README.md with 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, .skip in src/ or tests/.
  • "sideEffects": false set.
  • bun scripts/publish.ts --package @suaveplan/email-processing.
  • Archive: openspec/archive/2026-04-24-enterprise-email-processing/.