toml

js/format/toml.ts

fino:format/toml - TOML 1.0.0 parser and serializer.

TOML is a configuration format optimized for human-edited files with typed values and predictable table structure. This module parses TOML 1.0.0 into JavaScript values and serializes compatible JavaScript objects back to TOML. It is a good fit for application config, project manifests, and small settings files where comments and stable textual shape matter to users.

The parser covers TOML scalar types, basic and literal strings, integers, floats, booleans, offset datetimes, local datetimes, local dates, local times, arrays, inline tables, tables, and arrays of tables. Key uniqueness and structural rules from the spec are enforced. Numeric tokens and date/time ranges are validated rather than partially accepted.

Datetime types: - Offset datetime -> native Date - Local datetime -> TomlLocalDateTime - Local date -> TomlLocalDate - Local time -> TomlLocalTime

Integer overflow throws by default; pass { bigint: true } to receive BigInt values for integers outside JavaScript's safe integer range. Malformed input is reported as ParseError values from fino:parsing/scanner tagged with format: 'toml', carrying line/column location and a render() diagnostic helper.

Stringification emits a normalized TOML document and does not preserve comments, source ordering between scalars and tables, or original quoting style. Heterogeneous arrays are accepted as Fino values even though many TOML tools prefer homogeneous arrays.

import { parse, stringify } from 'fino:format/toml';

const cfg = parse('[server]\nport = 8080\nhosts = ["a", "b"]');
const text = stringify(cfg);
import { parse, TomlLocalDate } from 'fino:format/toml';

const cfg = parse('released = 2026-06-02');
cfg.released instanceof TomlLocalDate; // true

Useful references: - TOML 1.0.0 specification: https://toml.io/en/v1.0.0

Classes

class TomlParseError extends ParseError {

TOML-branded parse error subclass of ParseError from fino:parsing/scanner.

It inherits source location metadata (offset, line, column) and the render() diagnostic helper from ParseError. Note that parse() itself reports malformed input — invalid values, duplicate keys, duplicate table declarations, and integer overflow without bigint: true — as base ParseError instances whose format is 'toml', not as instances of this subclass. Catch with instanceof ParseError (which also matches TomlParseError) and check error.format when the format matters; this class exists for code layered on top of this module that wants to raise a TOML-specific error distinguishable by name.

import { ParseError } from 'fino:parsing/scanner';
import { parse } from 'fino:format/toml';

try {
  parse('answer =');
} catch (error) {
  if (error instanceof ParseError && error.format === 'toml') {
    console.error(error.render());
  }
}

Properties

name

Error name, always 'TomlParseError'.

Useful for distinguishing TOML failures in logs or serialized error reports where instanceof checks are unavailable.

class TomlLocalDate {

TOML local date value without a time or UTC offset.

parse() returns this wrapper for TOML local-date values such as 2026-06-02. It preserves the date as written instead of converting through a timezone. toString() and toJSON() return TOML-compatible YYYY-MM-DD text.

import { TomlLocalDate } from 'fino:format/toml';

const date = new TomlLocalDate(2026, 6, 2);
date.toString(); // '2026-06-02'

Readonly Properties

readonly year: number

Four-digit calendar year.

The constructor does not normalize invalid calendar values; parsed TOML is expected to supply spec-valid values.

import { TomlLocalDate } from 'fino:format/toml';

new TomlLocalDate(2026, 6, 2).year;
readonly month: number

One-based calendar month.

January is 1 and December is 12.

import { TomlLocalDate } from 'fino:format/toml';

new TomlLocalDate(2026, 6, 2).month;
readonly day: number

One-based day of month.

The value is emitted with two digits by toString().

import { TomlLocalDate } from 'fino:format/toml';

new TomlLocalDate(2026, 6, 2).day;

Constructors

constructor(y: number, mo: number, d: number)

Create a TOML local date wrapper.

The values are stored directly and are not converted to a JavaScript Date.

import { TomlLocalDate } from 'fino:format/toml';

const date = new TomlLocalDate(2026, 6, 2);

Methods

toString()

Format the date as TOML local-date text.

The return value is zero-padded and contains no timezone information.

import { TomlLocalDate } from 'fino:format/toml';

new TomlLocalDate(2026, 6, 2).toString();
toJSON()

Return the JSON representation used by JSON.stringify().

The value matches toString() so local dates remain timezone-free when serialized to JSON.

import { TomlLocalDate } from 'fino:format/toml';

JSON.stringify({ released: new TomlLocalDate(2026, 6, 2) });

class TomlLocalTime {

TOML local time value without a date or UTC offset.

parse() returns this wrapper for TOML local-time values such as 12:30:00. It preserves wall-clock time and does not attach a timezone or date.

import { TomlLocalTime } from 'fino:format/toml';

const time = new TomlLocalTime(9, 30, 0);
time.toString(); // '09:30:00'

Readonly Properties

readonly hour: number

Hour in 24-hour time.

The value is emitted with two digits by toString().

import { TomlLocalTime } from 'fino:format/toml';

new TomlLocalTime(9, 30, 0).hour;
readonly minute: number

Minute within the hour.

The value is emitted with two digits by toString().

import { TomlLocalTime } from 'fino:format/toml';

new TomlLocalTime(9, 30, 0).minute;
readonly second: number

Second within the minute.

Fractional precision, when present, is stored separately in ms.

import { TomlLocalTime } from 'fino:format/toml';

new TomlLocalTime(9, 30, 15).second;
readonly ms: number

Millisecond fraction. Defaults to 0.

Parsed TOML fractional seconds are rounded to the nearest millisecond.

import { TomlLocalTime } from 'fino:format/toml';

new TomlLocalTime(9, 30, 15, 250).ms;

Constructors

constructor(h: number, m: number, s: number, ms = 0)

Create a TOML local time wrapper.

The constructor stores values directly and does not normalize overflow.

import { TomlLocalTime } from 'fino:format/toml';

const time = new TomlLocalTime(9, 30, 15, 250);

Methods

toString()

Format the value as TOML local-time text.

Milliseconds are emitted only when non-zero.

import { TomlLocalTime } from 'fino:format/toml';

new TomlLocalTime(9, 30, 15, 250).toString();
toJSON()

Return the JSON representation used by JSON.stringify().

The value matches toString() and remains date- and timezone-free.

import { TomlLocalTime } from 'fino:format/toml';

JSON.stringify({ startsAt: new TomlLocalTime(9, 30, 0) });

class TomlLocalDateTime {

TOML local date-time value without a UTC offset.

parse() returns this wrapper for TOML local datetimes such as 2026-06-02T09:30:00. Offset datetimes are returned as native Date instances instead.

import { TomlLocalDate, TomlLocalDateTime, TomlLocalTime } from 'fino:format/toml';

const value = new TomlLocalDateTime(
  new TomlLocalDate(2026, 6, 2),
  new TomlLocalTime(9, 30, 0),
);

Readonly Properties

readonly date: TomlLocalDate

Local date component.

This component carries no timezone and is preserved independently from JavaScript Date.

import { TomlLocalDate, TomlLocalDateTime, TomlLocalTime } from 'fino:format/toml';

new TomlLocalDateTime(new TomlLocalDate(2026, 6, 2), new TomlLocalTime(9, 30, 0)).date;
readonly time: TomlLocalTime

Local time component.

The component includes optional millisecond precision but no timezone.

import { TomlLocalDate, TomlLocalDateTime, TomlLocalTime } from 'fino:format/toml';

new TomlLocalDateTime(new TomlLocalDate(2026, 6, 2), new TomlLocalTime(9, 30, 0)).time;

Constructors

constructor(d: TomlLocalDate, t: TomlLocalTime)

Create a TOML local date-time wrapper from local date and time parts.

No timezone conversion or validation is performed by the constructor.

import { TomlLocalDate, TomlLocalDateTime, TomlLocalTime } from 'fino:format/toml';

const value = new TomlLocalDateTime(new TomlLocalDate(2026, 6, 2), new TomlLocalTime(9, 30, 0));

Methods

toString()

Format the value as TOML local-date-time text.

The returned string uses T between date and time and includes no offset.

import { TomlLocalDate, TomlLocalDateTime, TomlLocalTime } from 'fino:format/toml';

new TomlLocalDateTime(new TomlLocalDate(2026, 6, 2), new TomlLocalTime(9, 30, 0)).toString();
toJSON()

Return the JSON representation used by JSON.stringify().

The value matches toString() and stays offset-free.

import { TomlLocalDate, TomlLocalDateTime, TomlLocalTime } from 'fino:format/toml';

JSON.stringify({
  start: new TomlLocalDateTime(new TomlLocalDate(2026, 6, 2), new TomlLocalTime(9, 30, 0)),
});

Types

type TomlValue = string | number | bigint | boolean | Date | TomlLocalDate | TomlLocalTime | TomlLocalDateTime | TomlValue[] | { [k: string]: TomlValue; }

Value types produced by the TOML parser and accepted by the stringifier.

Offset datetimes are native Date values; local temporal values use the wrapper classes exported by this module. Objects represent TOML tables and arrays represent TOML arrays or arrays of tables depending on context.

import { stringify, type TomlValue } from 'fino:format/toml';

const value: TomlValue = { server: { port: 8080, enabled: true } };
stringify(value as Record<string, TomlValue>);

Interfaces

interface TomlParseOptions {

Options controlling TOML parsing.

Parsing is strict by default and throws on integers outside JavaScript's safe integer range. Enable bigint when preserving oversized TOML integers is more important than returning only number values.

import { parse, type TomlParseOptions } from 'fino:format/toml';

const options: TomlParseOptions = { bigint: true };
parse('huge = 9223372036854775807', options);

Properties

bigint?: boolean

Return oversized TOML integers as BigInt instead of throwing.

Defaults to false. Safe integers are still returned as number.

import { parse } from 'fino:format/toml';

const cfg = parse('huge = 9223372036854775807', { bigint: true });

interface TomlStringifyOptions {

Options controlling TOML output formatting.

The current stringifier emits one key per line, table headers for nested objects, and arrays of tables for arrays of object values.

import { stringify, type TomlStringifyOptions } from 'fino:format/toml';

const options: TomlStringifyOptions = { indent: '' };
stringify({ server: { port: 8080 } }, options);

Properties

indent?: string

Reserved indentation string for TOML output. Defaults to "".

The current formatter stores this value for future formatting support, but TOML tables and arrays are emitted in a compact one-entry-per-line style.

import { stringify } from 'fino:format/toml';

stringify({ server: { port: 8080 } }, { indent: '' });

Functions

function parse( input: string | Uint8Array, options: TomlParseOptions = { } ): Record<string, TomlValue>

Parse a TOML document into a plain object.

Input may be a string or UTF-8 bytes. The parser enforces TOML 1.0.0 key uniqueness, table structure, scalar syntax, and integer range rules. Tables become plain objects, arrays of tables become arrays of objects, and temporal values follow the mapping described in the module header (offset datetimes become native Date, local values use the wrapper classes).

Throws a ParseError (from fino:parsing/scanner, with format set to 'toml') on malformed syntax, duplicate keys, duplicate table declarations, and integers outside JavaScript's safe range unless bigint: true is passed.

import { parse } from 'fino:format/toml';

const cfg = parse(`
title = "Fino"

[server]
port = 8080
hosts = ["a.example", "b.example"]

[[jobs]]
name = "backup"
at = 02:30:00
`);
cfg.title;                     // 'Fino'
(cfg.server as any).port;      // 8080

function stringify(value: Record<string, TomlValue>, options: TomlStringifyOptions = {}): string

Serialize a TOML-compatible object to TOML text.

The stringifier emits scalars before nested tables, nested objects as [table] headers, and arrays of objects as [[array-of-tables]] entries. Native Date values become UTC offset datetimes (+00:00), local temporal wrappers serialize through their toString() methods, and non-finite numbers become inf, -inf, or nan. Keys that are not bare-key safe are quoted, and strings prefer literal (single-quoted) form when they contain no single quotes or newlines. Output is normalized: comments and original source formatting from a parsed document are not preserved.

import { parse, stringify } from 'fino:format/toml';

const text = stringify({
  title: 'Fino',
  server: { port: 8080, hosts: ['a.example', 'b.example'] },
  listeners: [{ port: 8080 }, { port: 8443 }],
});
parse(text); // round-trips to an equivalent object