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

2.8 KiB

cid-resolver

(no submodules)

Purpose

Resolves Content-ID references inside HTML email bodies. Email clients that send inline images embed them as MIME parts with a Content-ID header and reference them in HTML with <img src="cid:<id>">. After parsing, the rendered HTML needs those references replaced by something a downstream consumer (an inbox UI, a digest builder, an audit trail) can serve — either a data URI, a static URL, or an explicit URL mapping.

Features

  • Three resolution strategies, evaluated in order: explicit urlMap (Object.hasOwn-checked so prototype keys cannot leak), urlPrefix + filename / contentHash, then data URI fallback.
  • Configurable maxDataUriSize (default 5 MiB) — attachments larger than the cap fall through to the unresolved list rather than exploding the HTML string.
  • Deduplicates CID references via extractCidReferences.
  • isReferencedInline short-circuits the lookup so callers can classify attachments as "inline" vs "true attachment" before serialising.
  • Strict input validation — non-string html inputs raise EMAIL_PROCESSING.INVALID_INPUT.

Usage: Basic

import { resolveCidReferences } from "@suaveplan/email-processing/cid-resolver";

const out = resolveCidReferences(parsed.html ?? "", processedAttachments);
console.log(out.resolvedCount, out.unresolvedCids);

Usage: Advanced

import {
    resolveCidReferences,
    isReferencedInline,
} from "@suaveplan/email-processing/cid-resolver";

const result = resolveCidReferences(parsed.html ?? "", processed, {
    urlMap: { "logo@brand": "https://cdn.brand/logo.png" },
    urlPrefix: "/inbox/inline/",
    maxDataUriSize: 2 * 1024 * 1024,
});

const inlineAttachments = processed.filter((a) =>
    isReferencedInline(parsed.html ?? "", a)
);
const trueAttachments = processed.filter(
    (a) => !isReferencedInline(parsed.html ?? "", a)
);

API Reference

  • CidResolverOptions — { maxDataUriSize?, urlPrefix?, urlMap? }.
  • CidResolutionResult — { html, totalReferences, resolvedCount, unresolvedCids }.
  • resolveCidReferences(html, attachments, options?) — primary entry.
  • extractCidReferences(html) — deduped list of CIDs found.
  • isReferencedInline(html, attachment) — boolean.

Errors: INVALID_INPUT for non-string html.

Implementation Notes

Object.hasOwn(urlMap, cleanRef) is used in preference to a direct truthy check so a deliberate urlMap: { id: "" } mapping is still recognised as "set", and so a polluted prototype cannot inject phantom keys. String.prototype.matchAll is used over regex.exec loops to keep the resolver simple and avoid stateful lastIndex bugs. The data URI fallback uses btoa(String.fromCharCode(...)) so it works in the browser and Node without buffer polyfills.