first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
# cid-resolver
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
import { resolveCidReferences } from "@suaveplan/email-processing/cid-resolver";
const out = resolveCidReferences(parsed.html ?? "", processedAttachments);
console.log(out.resolvedCount, out.unresolvedCids);
```
## Usage: Advanced
```ts
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.