CSV

RFC 4180 parser + serializer with operator-friendly defaults.

b.csv.parse accepts a string, Buffer, or Uint8Array and returns either an array of row objects (header mode, default) or an array of arrays (when header: false). Strips a leading UTF-8 BOM, handles CRLF / LF / CR line endings, and supports doubled-quote escapes inside quoted fields.

b.csv.stringify accepts an array of objects or arrays and emits RFC 4180 output. Cells are quoted only when they contain the delimiter, the quote char, CR, or LF — unless alwaysQuote: true forces full quoting.

Anti-DoS bounds are on by default: maxBytes (16 MiB), maxRows (1,000,000), and maxFieldBytes (1 MiB). Each cap is validated as a positive finite integer at call time — passing Infinity throws, never a silent bypass.

SCOPE: this module is for trusted-source-only emission. It performs RFC 4180 quote/delimiter escaping but does NOT defend against the broader CSV-injection threat catalog (Excel/Sheets formula triggers, Unicode bidi overrides, dangerous-function denylist, homoglyphs, control-byte injection, BOM mid-stream, dialect ambiguity, CSV-bombs). Any path that emits or accepts user-supplied cells MUST route through b.guardCsv — its serialize / validate / sanitize / gate surface handles every documented threat with a single profile choice (strict / balanced / permissive / email-attachment) or compliance posture (hipaa / pci-dss / gdpr / soc2).

Throws CsvError (FrameworkError, permanent) on shape violations.

b.csv.CsvError #

0.4.0

FrameworkError subclass thrown by b.csv.parse and b.csv.stringify on shape violations: bad delimiter / quote, unterminated quoted field, oversized input, oversized field, row-length mismatch, or unsupported eol / onBadRow value. alwaysPermanent — never retried by b.retry. Operators catch it to distinguish CSV-shape problems from upstream IO errors.

try {
  b.csv.parse('name,age\n"unterminated', { header: true });
} catch (e) {
  e instanceof b.csv.CsvError;   // → true
  e.code;                        // → "csv/unterminated-quote"
}

b.csv.DEFAULTS_PARSE #

0.4.0

Frozen-by-convention defaults applied to b.csv.parse(input, opts) before the call's own opts overlay. Exposed so operators can introspect the active limits (maxBytes, maxRows, maxFieldBytes) without re-deriving them from documentation.

b.csv.DEFAULTS_PARSE.maxBytes;       // → 16777216  (16 MiB)
b.csv.DEFAULTS_PARSE.maxRows;        // → 1000000
b.csv.DEFAULTS_PARSE.maxFieldBytes;  // → 1048576   (1 MiB)
b.csv.DEFAULTS_PARSE.delimiter;      // → ","

b.csv.DEFAULTS_STRINGIFY #

0.4.0

Frozen-by-convention defaults applied to b.csv.stringify(rows, opts) before the call's own opts overlay. Exposed so operators can introspect the active emission policy (eol, delimiter, alwaysQuote) without re-deriving it from documentation.

b.csv.DEFAULTS_STRINGIFY.header;       // → true
b.csv.DEFAULTS_STRINGIFY.delimiter;    // → ","
b.csv.DEFAULTS_STRINGIFY.eol;          // → "\r\n"
b.csv.DEFAULTS_STRINGIFY.alwaysQuote;  // → false

b.csv.parse(input, opts?) #

0.4.0
{
  header:        boolean,  // first row is column names (default true)
  delimiter:     string,   // single byte, default ","
  quote:         string,   // single byte, default '"'
  trim:          boolean,  // strip leading/trailing whitespace per cell
  maxBytes:      number,   // input cap, default 16 MiB
  maxRows:       number,   // row-count cap, default 1,000,000
  maxFieldBytes: number,   // per-cell cap, default 1 MiB
  onBadRow:      string,   // "throw" (default) or "skip"
}

Parse RFC 4180 CSV text into rows. By default the first row is treated as a header and each subsequent row is returned as an object keyed by header name; pass header: false to receive an array of arrays instead. Accepts a string, Buffer, or Uint8Array; a leading UTF-8 BOM is stripped. CR, LF, and CRLF are all accepted as row terminators. Doubled-quote sequences inside a quoted field decode to a literal quote character.

Anti-DoS caps (maxBytes, maxRows, maxFieldBytes) are enforced as positive finite integers. Passing Infinity or NaN throws CsvError rather than silently disabling the cap.

var rows = b.csv.parse("name,age\nalice,30\nbob,25");
// → [ { name: "alice", age: "30" }, { name: "bob", age: "25" } ]

var arrays = b.csv.parse("a,b\n1,2", { header: false });
// → [ [ "a", "b" ], [ "1", "2" ] ]

// Doubled-quote escape inside a quoted field decodes to one quote.
var quoted = b.csv.parse('msg\n"she said ""hi"""', { header: true });
// → [ { msg: 'she said "hi"' } ]

// Tab-separated values via the delimiter opt.
var tsv = b.csv.parse("a\tb\n1\t2", { delimiter: "\t" });
// → [ { a: "1", b: "2" } ]

b.csv.stringify(rows, opts?) #

0.4.0
{
  header:      boolean,         // emit a header row (default true)
  delimiter:   string,          // single byte, default ","
  quote:       string,          // single byte, default '"'
  eol:         string,          // "\r\n" (default) or "\n"
  alwaysQuote: boolean,         // quote every cell unconditionally
  columns:     Array,   // explicit column order / subset
}

Serialize an array of rows to RFC 4180 CSV text. Rows may be arrays (positional) or plain objects (keyed by header). When the first row is an object, header columns default to that row's Object.keys; pass opts.columns to force an explicit column order or to surface keys missing from the first row. Cells are quoted only when they contain the delimiter, the quote char, CR, or LF — unless alwaysQuote: true forces full quoting. null and undefined cells emit as empty strings; everything else is coerced via String().

The default end-of-line is CRLF per RFC 4180; pass eol: "\n" for plain LF output.

var out = b.csv.stringify([
  { name: "alice", age: 30 },
  { name: "bob",   age: 25 },
]);
// → "name,age\r\nalice,30\r\nbob,25"

// Cells containing the delimiter are quoted; embedded quotes double.
var quoted = b.csv.stringify([
  { msg: 'she said "hi", then left' },
], { eol: "\n" });
// → 'msg\n"she said ""hi"", then left"'

// Array-of-arrays input with an explicit column header.
var cols = b.csv.stringify(
  [ [ "1", "2" ], [ "3", "4" ] ],
  { columns: [ "a", "b" ], eol: "\n" }
);
// → "a,b\n1,2\n3,4"

Last updated 2026-08-08T16:39:49.652Z by seeder.