Initial Commit
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# parser
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Wraps `postal-mime` with a strict input/option boundary, OTel
|
||||
instrumentation, structured error codes, and the
|
||||
`@suaveplan/email-processing/hooks` pipeline. `parseEmail` is the
|
||||
package's primary entry point: it takes a raw RFC 822 message in any
|
||||
of the three supported input shapes (string, `ArrayBuffer`,
|
||||
`Uint8Array`) and returns a fully-parsed `ParsedEmail` whose
|
||||
attachments are real MIME parts (no stubs).
|
||||
|
||||
## Features
|
||||
|
||||
- Three input modes — string, `ArrayBuffer`, `Uint8Array` — covering
|
||||
both browser and Node consumers.
|
||||
- Zod-validated options at the boundary, raising
|
||||
`EMAIL_PROCESSING.INVALID_OPTIONS` with the failing issues on
|
||||
`context` so callers can surface diagnostics.
|
||||
- Distinct error codes for `INVALID_INPUT` (null/undefined/wrong type)
|
||||
and `PARSE_FAILURE` (`postal-mime` threw); `parse.failure_total`
|
||||
is incremented for the latter.
|
||||
- Hook pipeline integration — every parse returns the result of
|
||||
`emailHooks.applyHooksAsync("email:processing", …)`.
|
||||
- `parseEmailAddress` and `decodeEmailWords` re-export the
|
||||
`postal-mime` low-level helpers under the package's typed surface,
|
||||
with the same `INVALID_INPUT` guard.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { parseEmail } from "@suaveplan/email-processing/parser";
|
||||
|
||||
const parsed = await parseEmail(rawMimeString);
|
||||
console.log(parsed.subject, parsed.attachments.length);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { parseEmail } from "@suaveplan/email-processing/parser";
|
||||
import { onEmailProcessing } from "@suaveplan/email-processing/hooks";
|
||||
|
||||
const off = onEmailProcessing((data) => ({
|
||||
...data,
|
||||
parsed: {
|
||||
...data.parsed,
|
||||
html: data.parsed.html?.replace(/<script[\s\S]*?<\/script>/gi, ""),
|
||||
},
|
||||
}));
|
||||
|
||||
try {
|
||||
const parsed = await parseEmail(buffer, {
|
||||
attachmentEncoding: "base64",
|
||||
maxNestingDepth: 64,
|
||||
});
|
||||
await persist(parsed);
|
||||
} finally {
|
||||
off();
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `parseEmail(raw, options?)` — async; returns `ParsedEmail`. Throws
|
||||
`EmailProcessingError` with codes `INVALID_INPUT`,
|
||||
`INVALID_OPTIONS`, or `PARSE_FAILURE`.
|
||||
- `parseEmailAddress(addressString)` — sync; returns `EmailAddress[]`.
|
||||
Throws `EmailProcessingError(INVALID_INPUT)` for non-string input.
|
||||
- `decodeEmailWords(text)` — sync; returns decoded RFC 2047 string.
|
||||
Throws `EmailProcessingError(INVALID_INPUT)` for non-string input.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`parseEmail` records `email_processing.parse.total` with the attachment
|
||||
count as an attribute so dashboards can surface average
|
||||
attachments-per-message. Parse failures increment a separate
|
||||
`parse.failure_total` counter (no attachment count is recorded because
|
||||
the parser never reached the attachment array). The Zod options schema
|
||||
is `.strict()` so unknown option keys are rejected — this catches
|
||||
typos at the boundary instead of letting them silently no-op.
|
||||
Reference in New Issue
Block a user