Files
toon/docs/reference/api.md
2025-11-21 14:02:22 +01:00

8.7 KiB

API Reference

TypeScript/JavaScript API documentation for the @toon-format/toon package. For format rules, see the Format Overview or the Specification. For other languages, see Implementations.

Installation

::: code-group

npm install @toon-format/toon
pnpm add @toon-format/toon
yarn add @toon-format/toon

:::

encode(value, options?)

Converts any JSON-serializable value to TOON format.

import { encode } from '@toon-format/toon'

const toon = encode(data, {
  indent: 2,
  delimiter: ',',
  keyFolding: 'off',
  flattenDepth: Infinity
})

Parameters

Parameter Type Description
value unknown Any JSON-serializable value (object, array, primitive, or nested structure)
options EncodeOptions? Optional encoding options (see below)

Options

Option Type Default Description
indent number 2 Number of spaces per indentation level
delimiter ',' | '\t' | '|' ',' Delimiter for array values and tabular rows
keyFolding 'off' | 'safe' 'off' Enable key folding to collapse single-key wrapper chains into dotted paths
flattenDepth number Infinity Maximum number of segments to fold when keyFolding is enabled (values 0-1 have no practical effect)

Return Value

Returns a TOON-formatted string with no trailing newline or spaces.

Type Normalization

Non-JSON-serializable values are normalized before encoding:

Input Output
Finite number Canonical decimal (no exponent, no leading/trailing zeros: 1e61000000, -00)
NaN, Infinity, -Infinity null
BigInt (within safe range) Number
BigInt (out of range) Quoted decimal string (e.g., "9007199254740993")
Date ISO string in quotes (e.g., "2025-01-01T00:00:00.000Z")
undefined, function, symbol null

Example

import { encode } from '@toon-format/toon'

const items = [
  { sku: 'A1', qty: 2, price: 9.99 },
  { sku: 'B2', qty: 1, price: 14.5 }
]

console.log(encode({ items }))

Output:

items[2]{sku,qty,price}:
  A1,2,9.99
  B2,1,14.5

Delimiter Options

::: code-group

encode(data, { delimiter: ',' })
encode(data, { delimiter: '\t' })
encode(data, { delimiter: '|' })

:::

::: details Why Use Tab Delimiters? Tab delimiters (\t) often tokenize more efficiently than commas:

  • Tabs are single characters
  • Tabs rarely appear in natural text, reducing quote-escaping
  • The delimiter is explicitly encoded in the array header

Example:

items[2	]{sku	name	qty	price}:
  A1	Widget	2	9.99
  B2	Gadget	1	14.5

For maximum token savings on large tabular data, combine tab delimiters with key folding:

encode(data, { delimiter: '\t', keyFolding: 'safe' })

:::

encodeLines(value, options?)

Preferred method for streaming TOON output. Converts any JSON-serializable value to TOON format as a sequence of lines, without building the full string in memory. Suitable for streaming large outputs to files, HTTP responses, or process stdout.

import { encodeLines } from '@toon-format/toon'

// Stream to stdout (Node.js)
for (const line of encodeLines(data)) {
  process.stdout.write(`${line}\n`)
}

// Write to file line-by-line
const lines = encodeLines(data, { indent: 2, delimiter: '\t' })
for (const line of lines) {
  await writeToStream(`${line}\n`)
}

// Collect to array
const lineArray = Array.from(encodeLines(data))

Parameters

Parameter Type Description
value unknown Any JSON-serializable value (object, array, primitive, or nested structure)
options EncodeOptions? Optional encoding options (same as encode())

Return Value

Returns an Iterable<string> that yields TOON lines one at a time. Each yielded string is a single line without a trailing newline character — you must add \n when writing to streams or stdout.

::: info Relationship to encode() encode(value, options) is equivalent to:

Array.from(encodeLines(value, options)).join('\n')

:::

Example

import { createWriteStream } from 'node:fs'
import { encodeLines } from '@toon-format/toon'

const data = {
  items: Array.from({ length: 100000 }, (_, i) => ({
    id: i,
    name: `Item ${i}`,
    value: Math.random()
  }))
}

// Stream large dataset to file
const stream = createWriteStream('output.toon')
for (const line of encodeLines(data, { delimiter: '\t' })) {
  stream.write(`${line}\n`)
}
stream.end()

decode(input, options?)

Converts a TOON-formatted string back to JavaScript values.

import { decode } from '@toon-format/toon'

const data = decode(toon, {
  indent: 2,
  strict: true,
  expandPaths: 'off'
})

Parameters

Parameter Type Description
input string A TOON-formatted string to parse
options DecodeOptions? Optional decoding options (see below)

Options

Option Type Default Description
indent number 2 Expected number of spaces per indentation level
strict boolean true Enable strict validation (array counts, indentation, delimiter consistency)
expandPaths 'off' | 'safe' 'off' Enable path expansion to reconstruct dotted keys into nested objects (pairs with keyFolding: 'safe')

Return Value

Returns a JavaScript value (object, array, or primitive) representing the parsed TOON data.

Strict Mode

By default (strict: true), the decoder validates input strictly:

  • Invalid escape sequences: Throws on \x, unterminated strings
  • Syntax errors: Throws on missing colons, malformed headers
  • Array length mismatches: Throws when declared length doesn't match actual count
  • Delimiter mismatches: Throws when row delimiters don't match header
  • Indentation errors: Throws when leading spaces aren't exact multiples of indentSize

Set strict: false to skip validation for lenient parsing.

Example

import { decode } from '@toon-format/toon'

const toon = `
items[2]{sku,qty,price}:
  A1,2,9.99
  B2,1,14.5
`

const data = decode(toon)
console.log(data)

Output:

{
  "items": [
    { "sku": "A1", "qty": 2, "price": 9.99 },
    { "sku": "B2", "qty": 1, "price": 14.5 }
  ]
}

Path Expansion

When expandPaths: 'safe' is enabled, dotted keys are split into nested objects:

import { decode } from '@toon-format/toon'

const toon = 'data.metadata.items[2]: a,b'

const data = decode(toon, { expandPaths: 'safe' })
console.log(data)
// { data: { metadata: { items: ['a', 'b'] } } }

This pairs with keyFolding: 'safe' for lossless round-trips.

::: details Expansion Conflict Resolution When multiple expanded keys construct overlapping paths, the decoder merges them recursively:

  • Object + Object: Deep merge recursively
  • Object + Non-object (array or primitive): Conflict
    • With strict: true (default): Error
    • With strict: false: Last-write-wins (LWW)

Example conflict (strict mode):

const toon = 'a.b: 1\na: 2'
decode(toon, { expandPaths: 'safe', strict: true })
// Error: "Expansion conflict at path 'a' (object vs primitive)"

Example conflict (lenient mode):

const toon = 'a.b: 1\na: 2'
decode(toon, { expandPaths: 'safe', strict: false })
// { a: 2 } (last write wins)

:::

Round-Trip Compatibility

TOON provides lossless round-trips after normalization:

import { decode, encode } from '@toon-format/toon'

const original = {
  users: [
    { id: 1, name: 'Alice', role: 'admin' },
    { id: 2, name: 'Bob', role: 'user' }
  ]
}

const toon = encode(original)
const restored = decode(toon)

console.log(JSON.stringify(original) === JSON.stringify(restored))
// true

With Key Folding

import { decode, encode } from '@toon-format/toon'

const original = { data: { metadata: { items: ['a', 'b'] } } }

// Encode with folding
const toon = encode(original, { keyFolding: 'safe' })
// → "data.metadata.items[2]: a,b"

// Decode with expansion
const restored = decode(toon, { expandPaths: 'safe' })
// → { data: { metadata: { items: ['a', 'b'] } } }

console.log(JSON.stringify(original) === JSON.stringify(restored))
// true

Types

interface EncodeOptions {
  indent?: number
  delimiter?: ',' | '\t' | '|'
  keyFolding?: 'off' | 'safe'
  flattenDepth?: number
}

interface DecodeOptions {
  indent?: number
  strict?: boolean
  expandPaths?: 'off' | 'safe'
}