npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

haraka-email-message

v1.3.2

Published

Haraka email message

Readme

CI Test Status Code Climate Test Coverage

haraka-email-message

RFC 2822 header parser and MIME body parser used throughout Haraka. Can be used standalone.

Installation

npm install haraka-email-message

Quick start

const { Header, Body } = require('haraka-email-message')

const header = new Header()
header.parse([
  'From: Alice <[email protected]>\r\n',
  'To: Bob <[email protected]>\r\n',
  'Subject: Hello\r\n',
])

const body = new Body(header)
body.parse_more('This is the message body.\n')
body.parse_end()

console.log(header.get('from')) // 'Alice <[email protected]>'
console.log(body.bodytext) // 'This is the message body.\n'

Header

An RFC 2822 header block parser. Each MIME part in a Body also carries its own Header instance.

Construction

const header = new Header()

header.parse(lines)

Parses an array of raw header lines. Handles folded (multi-line) headers and decodes RFC 2047 encoded-words and RFC 2231 parameter continuations.

header.parse([
  'Subject: =?UTF-8?Q?Hello_World?=\r\n',
  'Content-Type: multipart/alternative;\r\n',
  '\tboundary="----=_Part_1"\r\n',
])

header.get(key)string

Returns the raw value(s) for key (case-insensitive). Multiple values are joined with \n. Returns '' if absent.

header.get('content-type')
// 'multipart/alternative;\r\n\tboundary="----=_Part_1"'

header.get_all(key)string[] (frozen)

Returns all values for key as a frozen array. Useful for headers that legitimately repeat (e.g. Received).

header.get_all('received') // ['from mx1 ...', 'from mx2 ...']

header.get_decoded(key)string

Like get(), but with RFC 2047 encoded-words decoded and RFC 2231 parameter continuations resolved. Use this for display or further parsing.

header.get_decoded('subject') // 'Hello World'

header.add(key, value)

Prepends a header. Non-ASCII values are automatically Q-encoded.

header.add('X-Spam-Score', '3.2')
header.add('X-Résumé', 'présent') // encoded automatically

Note (Haraka users): use transaction.add_header() instead — that also updates the message stream so the change is reflected in the DATA.

header.add_end(key, value)

Same as add() but appends instead of prepends.

header.remove(key)

Removes all headers with the given name.

Note (Haraka users): use transaction.remove_header() instead.

header.lines()string[] (frozen)

Returns the raw header block as an array of lines (one per logical header, continuations merged).

header.toString()string

Returns the entire header block as a single string.

header.header_list

The internal array of raw header lines. Readable but treat as read-only; mutations bypass the decoded caches.


Body

A streaming MIME body parser. Feed it lines one at a time with parse_more(), then call parse_end() to finalise.

Construction

const body = new Body(header, options)

header is a Header instance (or omit for a headerless body that defaults to text/plain). options is currently reserved for future use.

body.parse_more(line)Buffer | ''

Feeds one line into the parser. line may be a Buffer or string. Returns the (possibly transformed) line, or '' when a filter has consumed the output.

body.parse_end([line])Buffer

Signals end of the part. Decodes the accumulated body, runs any filters, and sets body.bodytext. Call once after all lines have been fed.

for (const line of rawLines) body.parse_more(line)
const lastLine = body.parse_end()

body.bodytextstring

The decoded, UTF-8 body text of this MIME part. HTML parts retain their tags.

body.ctstring | null

The Content-Type of this part, set during parse_start (first non-header line). null before parsing begins.

body.childrenBody[]

Child MIME parts. For a multipart/* message:

body                   ← multipart/alternative
  body.children[0]     ← text/plain
  body.children[1]     ← text/html

body.headerHeader

The Header of this MIME part.

Event: attachment_start

body.on('attachment_start', (contentType, filename, part, stream) => {
  stream.on('data', (chunk) => {
    /* Buffer */
  })
  stream.on('end', () => {
    /* done */
  })
})

Emitted when a non-inline part (attachment or non-text content type) is encountered. stream is an AttachmentStream. The event propagates to child parts automatically.

Event: mime_boundary

body.on('mime_boundary', (line) => {
  /* raw boundary line */
})

Emitted at each MIME boundary line.

body.add_filter(fn)

Registers a filter function invoked at parse_end() with the decoded body buffer. Receives (contentType, encoding, buf, contentDisposition) and should return a Buffer (or undefined to leave unchanged).

body.add_filter((ct, enc, buf) => {
  if (/text\/plain/i.test(ct)) {
    return Buffer.from(buf.toString().toUpperCase())
  }
})

body.set_banner([textBanner, htmlBanner])

Convenience method that adds a filter inserting banners at the end of text/plain and text/html parts. HTML banners are wrapped in <P>...</P> and inserted just before </body> or </html>.

body.set_banner([
  'This message was scanned by AcmeSecurity.',
  '<em>This message was scanned by AcmeSecurity.</em>',
])

body.force_end()

Forces attachment streams to emit end even if the parser is still in attachment state. Used during connection teardown.


AttachmentStream

A Stream subclass that buffers attachment data and supports backpressure via pause() / resume(). Received from the attachment_start event.

body.on('attachment_start', (ct, filename, part, stream) => {
  const chunks = []
  stream.on('data', (chunk) => chunks.push(chunk))
  stream.on('end', () => {
    const data = Buffer.concat(chunks)
    console.log(`${filename}: ${data.length} bytes`)
  })
  stream.pipe(fs.createWriteStream(`/tmp/${filename}`))
})

stream.pause() / stream.resume()

Pauses and resumes data emission. Buffered chunks are held in memory until resumed.

stream.setEncoding('binary')

Makes data events emit strings rather than Buffers. Only 'binary' is supported.

stream.connection

Optional back-reference to the network socket. When set, pause() / resume() also pause / resume the connection to provide true backpressure.


stream export

Re-exports haraka-message-stream, a dual-mode write-then-read buffer for the raw RFC 2822 wire bytes.

const { stream: MessageStream } = require('haraka-email-message')
const ms = new MessageStream(cfg, uuid)
ms.add_line('From: [email protected]\r\n')
ms.add_line('\r\n')
ms.add_line('Body text\r\n')
ms.add_line_end()
ms.pipe(socket, { dot_stuffed: true, ending_dot: true })

createAttachmentStream(header)AttachmentStream

Factory function that returns a new AttachmentStream. In Haraka, plugins can replace this export to substitute a custom implementation.

const msg = require('haraka-email-message')
const orig = msg.createAttachmentStream
msg.createAttachmentStream = (header) => {
  const s = orig(header)
  s.on('end', () => console.log('attachment done'))
  return s
}

Encoding support

iconv-lite handles most encodings. For rare legacy encodings not covered by iconv-lite (e.g. x-mac-cyrillic, koi8-r), install the optional native iconv binding:

npm install iconv

When the native binding is absent a lognotice-level message is emitted at startup. Unrecognised encodings fall back to Buffer.toString() and body.body_encoding is set to broken//<enc>.