Files
skills/docs/communication/emails/email-processing/src/attachment-processor/attachment-processor.md
2026-05-29 08:20:56 +01:00

3.1 KiB

attachment-processor

(no submodules)

Purpose

Normalises and validates EmailAttachment payloads emerging from parseEmail. Each attachment is decoded to bytes, validated against a caller-supplied size + MIME-type allowlist, hashed with SHA-256 over the Web Crypto API, and packaged as a ProcessedAttachment carrying the original metadata plus a deterministic content hash for dedup, provenance, and dehydrated-template workflows.

Features

  • Three input shapes: ArrayBuffer, base64 string (per encoding), or plain UTF-8 string — falls back to Uint8Array(0) for unknown shapes (matches upstream behaviour) so callers do not see crashes on rare attachment encodings.
  • Size limit defaults to 25 MiB; overrides validated via Zod.
  • MIME allowlist supports both exact match (application/pdf) and wildcard (image/*).
  • SHA-256 via crypto.subtle.digest — works in browsers, Node, Bun and Deno without polyfills. Hash failures surface as EMAIL_PROCESSING.HASH_FAILURE with the underlying error on cause.
  • OTel counters for processed vs rejected attachments (attributed by reason) and a duration histogram for hash + metadata extraction.

Usage: Basic

import { processAttachment } from "@suaveplan/email-processing/attachments";

const out = await processAttachment(parsed.attachments[0]);
if (out.valid) {
    await store({ hash: out.metadata.contentHash, bytes: out.content });
}

Usage: Advanced

import {
    processAttachments,
    type ProcessedAttachment,
} from "@suaveplan/email-processing/attachments";

const results = await processAttachments(parsed.attachments, {
    maxSize: 5 * 1024 * 1024,
    allowedMimeTypes: ["image/*", "application/pdf"],
});

const valid = results.filter((r): r is ProcessedAttachment => r.valid);
const rejected = results.filter((r) => !r.valid);
log.info({
    valid: valid.length,
    rejected: rejected.length,
    hashes: valid.map((v) => v.metadata.contentHash),
});

API Reference

  • AttachmentMetadata — filename, size, MIME type, disposition, contentHash (hex SHA-256), optional contentId + description.
  • ProcessedAttachment — { metadata, content, valid, error? }.
  • AttachmentProcessorOptions — { allowedMimeTypes?, maxSize? }, Zod-validated.
  • processAttachment(att, options?) — returns ProcessedAttachment.
  • processAttachments(list, options?) — returns ProcessedAttachment[].

Errors: INVALID_OPTIONS for bad options, HASH_FAILURE if the Web Crypto API throws (e.g. in environments where the digest is unavailable).

Implementation Notes

crypto.subtle.digest requires the source buffer to satisfy the BufferSource type; passing a fresh Uint8Array works in every runtime tested. The duration histogram uses performance.now() when available and falls back to Date.now() for environments without it (older Node test isolates). Errors surfaced inside extractMetadata are propagated upward by processAttachment without being swallowed, so callers do not lose root-cause information when hashing fails.