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. isReferencedInlineshort-circuits the lookup so callers can classify attachments as "inline" vs "true attachment" before serialising.- Strict input validation — non-string
htmlinputs raiseEMAIL_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.