runtime:serialization
XML, YAML, TOML, CSV, MessagePack, CBOR and JSONL parse in Rust — the text or bytes cross the op boundary once and a JavaScript value comes back. BSON is encoded and decoded by the engine itself, straight from and into JavaScript objects, and JSONC and JSON5 are parsed the same way. Protobuf is the exception: it is a reflective pure-JavaScript implementation, because a .proto is compiled at run time rather than by a build step.
Exposed as an ES module under the runtime: scheme. Status: Available.
Import
import { JSONL, JSONC, JSON5, XML, YAML, TOML, CSV, MessagePack, CBOR, BSON, Protobuf } from "runtime:serialization";
Every build/encode throws TypeError on a circular value and RangeError past 256 levels of nesting.
JSONL (JSON Lines)
Stream-based parsing and serializing of JSON Lines data. JSONL is heavily optimized for large file handling natively over streams.
| Export | Type | Description | Example |
|---|---|---|---|
new JSONL.DecoderStream(options?) | TransformStream | Parses streaming JSONL byte chunks into JS objects incrementally. Option skipInvalid? (boolean, default false): skip invalid JSON lines instead of destroying the stream; skipped lines are emitted to decoder.onError(err). | stream.pipeThrough(new JSONL.DecoderStream({ skipInvalid: true })) |
new JSONL.EncoderStream() | TransformStream | Serializes a stream of JS objects into JSONL byte chunks. No options are currently supported. | stream.pipeThrough(new JSONL.EncoderStream()) |
JSONC and JSON5
JSON with comments and JSON5 (D191). Both mirror the global JSON.
| Export | Description |
|---|---|
JSONC.parse(text, reviver?) | Parses JSON with // and /* */ comments and one trailing comma in an object or array; a leading byte order mark is ignored. Returns what JSON.parse builds from the same JSON, and runs reviver as it does. |
JSONC.stringify(value, replacer?, space?) | Standard JSON, exactly as JSON.stringify writes it: a comment cannot come from a value. |
JSONC.validate(text, opts?) | Checks the text without building a value; agrees with parse. opts.detailed returns { valid, error? }. |
JSON5.parse(text, reviver?) | Parses JSON5 1.0.0: identifier keys, single-quoted and multi-line strings, hex, Infinity, NaN, leading +, leading and trailing decimal points, comments and trailing commas. For the JSON it shares, the value is what JSON.parse builds. |
JSON5.stringify(value, replacer?, space?) | Writes JSON5: ASCII identifier keys unquoted, Infinity and NaN kept, each string in whichever quote needs fewer escapes (single on a tie), and a trailing comma after each member when indenting. A value's toJSON5() is used before its toJSON(). Also takes (value, { replacer, space, quote }), where quote is "'" or '"'. |
JSON5.validate(text, opts?) | As JSONC.validate. |
JSONC.edit(text, path, value), JSON5.edit(text, path, value) | The text with the value at path (an array of keys and indices) set to value, or removed when value is undefined. Every other byte is kept: comments, blank lines, key order and quotes. An existing value is replaced where it stands (of a repeated key, the last); a missing key is added after its siblings with their indent, colon spacing, trailing comma and, in JSON5, quoting; missing objects on the path are created; an index equal to an array's length appends. A removal takes the member's comma and a comment on the rest of its line, and every occurrence of a repeated key. Text that does not parse is a SyntaxError; a path through a value that cannot hold it, or a value with no form in the format, is a TypeError; an index past an array's end is a RangeError. |
Values follow JSON.parse: a __proto__ key is an own property and never sets the prototype, a repeated key keeps its first place and its last value, and a \u escape may spell a lone surrogate. Text that is not in the dialect is a SyntaxError whose message names it and ends at line L column C; nesting past 256 levels is a RangeError. JSON5.stringify skips and refuses what JSON.stringify does: undefined, functions and symbols are skipped (null in an array), and a BigInt or a circular value is a TypeError.
XML
Synchronous parsing, building, and validation of XML data, plus streaming support for massive documents.
| Export | Type | Description | Example |
|---|---|---|---|
XML.parse(xml) | (string) => object | Parses an XML string into a JavaScript object. | XML.parse("<root>hi</root>") |
XML.build(obj) | (object) => string | Serializes a JavaScript object into an XML string. | XML.build({ root: "hi" }) |
XML.validate(xml, options?) | (string, object) => boolean | object | Validates an XML string. Option detailed? (boolean, default false): return { valid, error? } instead of a boolean, with the exact syntax error on failure. | XML.validate("<root>", { detailed: true }) |
new XML.DecoderStream() | TransformStream | Parses streaming XML byte chunks into JavaScript objects incrementally. | stream.pipeThrough(new XML.DecoderStream()) |
parse returns an object with the root element as its one key: attributes are @name, an element's text is $text, a repeated name is an array, and an empty element (<a/> or <a></a>) is "". References and CDATA are text.
| Input | Result |
|---|---|
| More than one root, or text outside it | SyntaxError |
| An unclosed element, or no element at all | SyntaxError |
A reference other than the five predefined entities and &#…; (a DTD is not processed) | SyntaxError |
A repeated attribute, < in an attribute value, a name that is not an XML name | SyntaxError |
| Nesting past 256 levels | SyntaxError |
build writes @name keys as attributes, $text as text, an array as a repeated element and null/undefined as an empty element. Numbers are written as String(n), a BigInt exactly. An invalid name, NaN/±Infinity, bytes, a character XML 1.0 cannot contain, or an array at the top level is a TypeError. DecoderStream emits one { name: value } per direct child of the root, read by the same reader: a malformed record or a truncated document errors the stream.
YAML
Synchronous parsing, building, and validation of YAML 1.2.
| Export | Type | Description | Example |
|---|---|---|---|
YAML.parse(yaml) | (string) => object | Parses a YAML string into a JavaScript object. | YAML.parse("key: value") |
YAML.build(obj) | (object) => string | Serializes a JavaScript object into a YAML string. | YAML.build({ key: "value" }) |
YAML.validate(yaml, options?) | (string, object) => boolean | object | Validates a YAML string. Option detailed? (boolean, default false): return { valid, error? } instead of a boolean. | YAML.validate("key: value", { detailed: true }) |
parse reads one document; an empty stream is null. Plain scalars resolve by the YAML 1.2 core schema: 017 is 17, yes is "yes", .inf and .nan are Infinity and NaN. Keys keep their source text (1: a gives the key "1"). Merge keys (<<) are applied, and a key written in the mapping wins over a merged one.
| Input | Result |
|---|---|
| More than one document | SyntaxError |
| A duplicate key in one mapping | SyntaxError |
| A sequence or mapping as a key | SyntaxError |
A tag other than !!str, !!int, !!float, !!bool, !!null, !!seq, !!map, !!binary, !!timestamp, !!set, !!omap, !!pairs | SyntaxError |
| Nesting deeper than 256 levels | RangeError |
| Aliases expanding to more than 1,000,000 nodes | RangeError |
build writes block style with two-space indentation. A string is quoted when, written plain, it would read back as something else — under the core schema, or under the wider schema js-yaml applies by default and YAML 1.1 readers (PyYAML, SnakeYAML) use: the 1.1 booleans (yes, off, y), timestamps (2001-12-14), base-60 numbers (1:20), 0b integers and _-separated numbers. js-yaml's dumper quotes the same set. A number is written as String(n), a BigInt exactly, NaN and ±Infinity as .nan and ±.inf, a Date as its ISO-8601 string, and undefined as null. Bytes, a function or a symbol is a TypeError.
TOML
Synchronous parsing, building, and validation of TOML data.
| Export | Type | Description | Example |
|---|---|---|---|
TOML.parse(toml) | (string) => object | Parses a TOML string into a JavaScript object. | TOML.parse("key = 'value'") |
TOML.build(obj) | (object) => string | Serializes a JavaScript object into a TOML string. The root must be an object/table. | TOML.build({ key: "value" }) |
TOML.validate(toml, options?) | (string, object) => boolean | object | Validates a TOML string. Option detailed? (boolean, default false): return { valid, error? } instead of a boolean. | TOML.validate("key = 'value'", { detailed: true }) |
Keys keep their document order. An integer past 253 parses to a BigInt (TOML integers are lossless); datetimes parse to RFC 3339 strings.
build input | Written as |
|---|---|
integral number in the safe range | integer |
any other number, NaN, ±Infinity | float (nan, inf, -inf) |
BigInt within 64 bits | integer |
Date | ISO-8601 string |
null, undefined, bytes, a BigInt past 64 bits | throws TypeError |
CSV
Synchronous parsing, building, and validation of CSV. CSV.parse returns arrays of fields (string[][]) by default and only keys objects when asked; every field is a string unless cast is set.
| Export | Type | Description | Example |
|---|---|---|---|
CSV.parse(csv, options?) | (string, object) => any | Parses CSV text. string[][] by default, or an array of objects when header is set. | CSV.parse("a,b\n1,2\n") |
CSV.build(rows, options?) | (any, object) => string | Writes an array of rows (arrays, or objects keyed by header) as CSV text. | CSV.build([["a", "b"]]) |
CSV.validate(csv, options?) | (string, object) => boolean | object | Validates under the same options as parse. Option detailed? (default false): return { valid, error? }. | CSV.validate("a,b\n1,2\n") |
CSV.DecoderStream(options?) | TransformStream<string | Uint8Array, any> | Streams rows from text/byte chunks, with parse's options and row shapes. A quoted field may span chunks. | source.pipeThrough(new CSV.DecoderStream({ header: true })) |
CSV.EncoderStream(options?) | TransformStream<any, string> | Streams CSV text from rows (one per chunk), with build's options and shape rules. | rows.pipeThrough(new CSV.EncoderStream()) |
| Option | Default | Meaning |
|---|---|---|
delimiter | "," | Field separator (a single byte). |
quote | "\"" | Quote character (a single byte). |
escape | doubling | Escape inside a quoted field; the default is RFC 4180 "". |
header | false | false → arrays; true → keys objects by the first row (consumed); a string[] → names every row and treats the first row as data. |
trim | false | Trim whitespace from unquoted fields, and skip spaces and tabs around a quoted one (a, "b, c"). A tab delimiter is never trimmed. |
skipEmptyLines | false | Drop records that are a single empty field. |
comment | — | A single byte, other than the delimiter, the quote or a line break; lines beginning with it are skipped whole. build quotes a field that starts with it so the row survives a round trip. |
cast | false | Convert plain decimal fields to numbers and true/false to booleans. inf, nan, hex, an overflowing decimal, and an integer past 253 − 1 stay strings. |
relaxColumnCount | false | Allow rows whose field count differs from the header. |
maxFieldBytes | 16 MiB | Per-field byte cap. |
maxColumns | 1,048,576 | Per-record field cap. |
bom | true | Strip a leading UTF-8 BOM. |
newline | "\n" | The record separator build writes. |
A quoted field keeps delimiters and newlines, and "" reads back as one ". With header set, every row must match the header's field count unless relaxColumnCount is on. A duplicate column name (inferred or explicit), a stray character after a closing quote, and a delimiter or quote that is a line break (or a delimiter equal to the quote, or a comment equal to either) are errors. build writes an absent (null/undefined) field empty, a number as String(n) spells it and a BigInt exactly, and quotes a field that starts with the comment byte. Unlike XML/YAML/TOML, CSV.validate forwards the options, because a ragged row is valid without a header and invalid with one.
MessagePack
Synchronous parsing, building, and validation of binary MessagePack data.
| Export | Type | Description | Example |
|---|---|---|---|
MessagePack.decode(msgpack) | (Uint8Array) => object | Parses a MessagePack byte array into a JavaScript object. | MessagePack.decode(bytes) |
MessagePack.encode(obj) | (object) => Uint8Array | Serializes a JavaScript object into a MessagePack byte array. | MessagePack.encode({ key: "value" }) |
MessagePack.validate(msgpack, options?) | (Uint8Array, object) => boolean | object | Validates a MessagePack byte array. Option detailed? (boolean, default false): return { valid, error? } instead of a boolean. | MessagePack.validate(bytes, { detailed: true }) |
Type mapping
| JavaScript | Wire | Decodes back as |
|---|---|---|
Uint8Array, any view, ArrayBuffer | bin | Uint8Array |
Map | map (keys stringified) | object |
Set | array | Array |
Date | ISO-8601 str | string |
function, symbol, BigInt | — | throws TypeError |
| — | ext | Uint8Array (payload) |
A non-string map key becomes a property name by the CBOR rule below. A reserved marker (0xc1), a truncated value, a str that is not UTF-8, nesting past 256 levels, or a byte after the top-level value is a SyntaxError; validate agrees with decode.
CBOR
Synchronous decoding, encoding, and validation of CBOR (RFC 8949, Concise Binary Object Representation). encode writes the deterministic form: shortest integer and length forms, the shortest float that preserves the value, and bytewise-sorted map keys.
| Export | Type | Description | Example |
|---|---|---|---|
CBOR.decode(cbor) | (Uint8Array) => any | Decodes a CBOR byte array into a JavaScript value. Accepts definite and indefinite-length strings, arrays and maps; refuses tags. | CBOR.decode(bytes) |
CBOR.encode(obj) | (any) => Uint8Array | Encodes a JavaScript value into a CBOR byte array in the deterministic form of RFC 8949 §4.2. | CBOR.encode({ key: "value" }) |
CBOR.validate(cbor, options?) | (Uint8Array, object) => boolean | object | Validates a CBOR byte array, agreeing with decode. Option detailed? (boolean, default false): return { valid, error? } instead of a boolean. | CBOR.validate(bytes, { detailed: true }) |
Type mapping
| JavaScript | Wire | Decodes back as |
|---|---|---|
Uint8Array, any view, ArrayBuffer | byte string (major 2) | Uint8Array |
Map | map (keys stringified) | object |
Set | array | Array |
Date | ISO-8601 text string | string |
undefined / null | 0xf7 / 0xf6 | undefined / null |
BigInt in [−264, 264−1] | integer (major 0/1) | number in the safe range, else BigInt |
String/Number/Boolean/BigInt object | its primitive | the primitive |
function, symbol, BigInt outside that range | — | throws TypeError |
| — | tag (major 6) | throws SyntaxError |
An integer-valued number inside the safe range (±(253−1)) encodes as an integer, so a decoded 100000.0 re-encodes as 100000; past it a number is the shortest exact float and decodes back as a number, while a BigInt takes the integer form. A reserved initial byte, a stray break, a trailing byte, a malformed or oversized length, an unassigned simple value (or one below 32 in the two-byte form), a tag, or nesting past 256 levels is a SyntaxError.
BSON
MongoDB's document format. The BSON types are classes on the namespace.
const bytes = BSON.encode({ _id: new BSON.ObjectId(), count: 5n, at: new Date() }); const doc = BSON.decode(bytes); // { _id: ObjectId, count: 5n, at: Date }
| Export | Type | Description |
|---|---|---|
BSON.encode(document) | (object) => Uint8Array | Encodes an object, or a Map with string keys. |
BSON.decode(bytes) | (Uint8Array | ArrayBuffer) => object | Decodes one BSON document. |
BSON.validate(bytes, options?) | (Uint8Array, object) => boolean | object | Validates the bytes, agreeing with decode. Option detailed?: return { valid, error? }. |
new BSON.ObjectId(id?) | ObjectId | A new id, or one from 24 hex digits, 12 bytes or another ObjectId. toHexString(), getTimestamp(), equals(), id; ObjectId.isValid(), createFromHexString(), createFromTime(seconds). |
new BSON.Decimal128(text | bytes) | Decimal128 | A 128-bit decimal, parsed exactly: text that would need rounding is a SyntaxError. toString(), bytes. |
new BSON.UUID(value?) | UUID | A new random UUID, or one from text (dashes optional) or 16 bytes. Binary subtype 4. toString(), toHexString(includeDashes?), equals(), bytes. |
new BSON.Timestamp({ t, i } | bigint) | Timestamp | A server timestamp; t and i are 0 to 232−1. |
new BSON.Binary(buffer?, subType?) | Binary | Binary data of another subtype. Binary.fromFloat32Array(), fromInt8Array(), fromPackedBits(bits, padding?) make vectors (subtype 9); toFloat32Array(), toInt8Array(), toPackedBits() read them. |
new BSON.Code(code, scope?) | Code | JavaScript code, with an optional scope document. |
new BSON.BSONRegExp(pattern, options?) | BSONRegExp | A regular expression as the server stores it (options from ilmsux). toRegExp() is a SyntaxError for l or x. |
new BSON.MinKey(), new BSON.MaxKey() | Sort below and above every other value. | |
new BSON.Double(value) | Double | Encodes as a double even when whole. |
Type mapping
| JavaScript | BSON | Decodes back as |
|---|---|---|
whole number in the int32 range (not -0) | int32 | number |
any other number | double | number |
bigint in the int64 range | int64 | bigint, however small |
undefined in an object / an array | omitted / null | — / null |
Date | datetime | Date |
RegExp | regular expression (flags i, m, s, u) | BSONRegExp |
Uint8Array, ArrayBuffer | binary subtype 0 | Uint8Array |
Map, any other object | document (own enumerable string keys) | plain object |
the BSON.* classes | their types | the same classes |
| — | deprecated undefined, symbol, DBPointer | null, string, { $ref, $id } |
A decoded double that is a whole number re-encodes as int32, as in the official driver; BSON.Double keeps it a double. A repeated key decodes with its last value. A NUL in a key, a cycle, a function, a symbol, a Set or a typed array other than Uint8Array is a TypeError; a bigint outside int64, an Invalid Date or nesting past 256 levels is a RangeError; malformed bytes are a SyntaxError.
Protobuf
Schema-aware Protobuf decoding and encoding. Pure-JS and reflective: the .proto is compiled at runtime (proto3 and editions 2023/2024; proto2-only constructs are rejected). Decoded objects use camelCase keys, BigInt for 64-bit ints, enum value-names, and Uint8Array for bytes.
| Export | Type | Description | Example |
|---|---|---|---|
new Protobuf.Schema(proto, options?) | Schema | Compiles a .proto source string (or a { filename: source } map for multi-file schemas with imports; google/protobuf well-known types resolve automatically). | new Protobuf.Schema('syntax = "proto3"; message Hello { string name = 1; }') |
Protobuf.Schema.fromDescriptorSet(bytes) | (Uint8Array) => Schema | Builds a Schema from a compiled FileDescriptorSet (protoc --descriptor_set_out, ideally with --include_imports) instead of .proto source. | Protobuf.Schema.fromDescriptorSet(await readDescriptorBytes()) |
schema.decode(messageName, bytes) | (string, Uint8Array) => object | Decodes a byte array into a JavaScript object for the fully-qualified message name. | schema.decode("Hello", bytes) |
schema.encode(messageName, value, options?) | (string, object, object) => Uint8Array | Encodes a JavaScript object into a Protobuf byte array. Field names may be snake_case or camelCase; a key matching no field throws unless ignoreUnknownFields is set. | schema.encode("Hello", { name: "world" }) |
schema.encodeDelimited(messageName, value, options?) | (string, object, object) => Uint8Array | Encodes one length-delimited message (varint length prefix + bytes). Concatenate results to write a stream. Same field-name rules as encode. | schema.encodeDelimited("Hello", { name: "world" }) |
schema.decodeDelimited(messageName, source) | (string, ReadableStream | AsyncIterable | Iterable | Uint8Array) => AsyncGenerator<object> | Streams the messages of a length-delimited stream from a chunked byte source. | for await (const m of schema.decodeDelimited("Hello", res.body)) { … } |
schema.toJson(messageName, value) | (string, object) => JsonValue | Converts a decoded value to canonical proto3-JSON (64-bit ints and bytes as strings, enums as value-names, well-known-type special forms). | schema.toJson("Hello", value) |
schema.fromJson(messageName, json, options?) | (string, JsonValue, { ignoreUnknownFields? }) => object | Parses canonical proto3-JSON into the decoded value shape (ready for encode). Strict by default. | schema.fromJson("Hello", { name: "world" }) |
schema.decodeStream(messageName, fieldName, source) | (string, string, ReadableStream | AsyncIterable | Iterable) => AsyncGenerator<object> | Streams the elements of a repeated message field from a chunked byte source, yielding each as it arrives without materializing the whole array. | for await (const item of schema.decodeStream("Catalog", "books", stream)) { … } |
Errors
| Error | When |
|---|---|
SyntaxError | Parsing fails due to malformed XML, YAML, TOML, CSV, JSONC, JSON5, CBOR or BSON input (an unterminated quote, a duplicate column name, a ragged row under header, a CBOR tag or trailing byte). |
TypeError | Building fails because the provided JavaScript object cannot be serialized into the target format (e.g., circular references, invalid keys). |
RangeError | The input exceeds parser depth or memory limits (e.g., XML streaming buffer cap, YAML alias expansion). |