Files
skills/docs/communication/emails/dmarc/src/parsers/aggregate.md
2026-05-29 08:20:56 +01:00

4.0 KiB

aggregate

Purpose

Implements parseDmarcAggregate, the primary entry point for converting raw DMARC aggregate (RUA) report data into validated, strongly-typed DmarcAggregateReport objects. It handles the full pipeline: gzip auto-detection and decompression, XML parsing, snake_case-to-camelCase field mapping, single-vs-multiple element normalisation, and Zod schema validation with structured error reporting.

Features

  • Auto-detects gzip input via magic-byte probe — no caller configuration needed for compressed reports
  • Accepts both raw XML strings and Uint8Array binary blobs in a single overload
  • Full snake_case → camelCase mapping for all RFC 7489 field names
  • Handles variable-arity elements: <record>, <dkim>, <spf> can be single or multiple
  • Validates the mapped structure with DmarcAggregateReportSchema (Zod)
  • Emits OTel counter and histogram via recordParse() on both success and error paths
  • Wraps all failures in typed DmarcError with stable codes

Usage: Basic Example

import { parseDmarcAggregate } from "@suaveplan/dmarc/parsers/aggregate";

const report = parseDmarcAggregate(`
  <feedback>
    <version>1.0</version>
    <report_metadata>
      <org_name>Example Corp</org_name>
      <email>dmarc@example.com</email>
      <report_id>12345</report_id>
      <date_range><begin>1609459200</begin><end>1609545600</end></date_range>
    </report_metadata>
    <policy_published>
      <domain>example.com</domain>
      <adkim>r</adkim><aspf>r</aspf><p>none</p><pct>100</pct>
    </policy_published>
    <record>
      <row>
        <source_ip>192.0.2.1</source_ip><count>5</count>
        <policy_evaluated><disposition>none</disposition><dkim>pass</dkim><spf>pass</spf></policy_evaluated>
      </row>
      <identifiers><header_from>example.com</header_from></identifiers>
      <auth_results>
        <dkim><domain>example.com</domain><result>pass</result></dkim>
        <spf><domain>example.com</domain><result>pass</result></spf>
      </auth_results>
    </record>
  </feedback>
`);

console.log(report.reportMetadata.orgName); // "Example Corp"
console.log(report.record[0].row.count);    // 5

Usage: Advanced Example

import { parseDmarcAggregate } from "@suaveplan/dmarc/parsers/aggregate";
import { DmarcError, DMARC_CODES } from "@suaveplan/dmarc/errors";
import { gzipSync, strToU8 } from "fflate";

// Parse from gzip-compressed bytes
const xmlBytes = strToU8(xmlString);
const compressed = gzipSync(xmlBytes);
const report = parseDmarcAggregate(compressed);

// Error discrimination
try {
  parseDmarcAggregate("not xml");
} catch (err) {
  if (err instanceof DmarcError) {
    switch (err.code) {
      case DMARC_CODES.PARSE_ERROR:
        // XML could not be parsed
        break;
      case DMARC_CODES.INVALID_REPORT:
        // Parsed but failed schema validation
        break;
      case DMARC_CODES.UNSUPPORTED_ENCODING:
        // Gzip decompression failed
        break;
    }
  }
}

API Reference

parseDmarcAggregate(input, options?): DmarcAggregateReport

Parameters:

  • input: string | Uint8Array — raw XML string or binary data (plain or gzip-compressed)
  • options.decompress?: boolean — force gzip decompression even without magic bytes

Returns: DmarcAggregateReport — fully validated aggregate report

Throws:

  • DmarcError(DMARC.PARSE_ERROR) — XML parse failure
  • DmarcError(DMARC.INVALID_REPORT) — missing required elements or schema validation failure
  • DmarcError(DMARC.UNSUPPORTED_ENCODING) — gzip decompression failure

ParseDmarcAggregateOptions

{ decompress?: boolean }

Implementation Notes

The parser uses a two-phase approach: structural mapping first (XML → plain object), validation second (Zod). This separation means that when validation fails the error message includes Zod's structured issue list rather than a raw type error, making debugging significantly easier. The performance.now() timing wraps the entire pipeline so OTel histograms reflect the true end-to-end cost including decompression and schema validation.