* docs: add Python (C++ backend) implementation to ecosystem list
Added a new entry for the Python implementation with a C++ backend (ctoon) to the ecosystem documentation, ensuring it appears alongside other language implementations.
* docs: add Matlab implementation to ecosystem implementations list
Add Matlab entry (ctoon) to the ecosystem implementations table, acknowledging the new community implementation and providing its repository link and author.
Drop the (§2 carve-out) suffix from the describe block — no other test in this file references spec sections. Switch it() titles from 'emits X' to 'encodes X as Y' to match the existing converts/normalizes verb pattern.
- bump @toon-format/spec to ^3.3.0; update SPEC.md pointer and badges
- docs: align number-form prose with §2 carve-out (canonical in [1e-6, 1e21) or zero; exponent form permitted outside)
- docs/reference/spec.md: add lowercase booleans/null to encoder checklist; add Java to Appendix F language list
- docs/reference/efficiency-formalization.md: drop overstated "matching TOON's requirement" claim
- packages/toon/README.md: drop "What's New in v3.2" section (now stale)
All 593 tests pass (packages/toon: 497, packages/cli: 96). Encoder remains conformant by default under the v3.3 §2 carve-out via String(value).
Two regrade fixes surfaced by a post-v3.2 verification swarm:
* llm-prompts.md and api.md had ISO-8601 timestamps as unquoted TOON
values, violating §7.2 (strings containing a colon MUST be quoted).
The spec repo's examples and packages/toon/README.md already quote
timestamps; the docs repo had drifted.
* docs/cli/index.md is the docs-site twin of packages/cli/README.md.
The prior v3.2 swarm (dd856fe) updated the README's --no-strict
options row and Lenient Decoding prose to cite "header delimiter"
but did not propagate to the parallel paragraphs in docs/cli/index.md
(lines 159 and 234).
Matches the lockfile bumped in d6c64d2; CI's frozen-lockfile install
fails until the manifest catches up. The dep was skip-worktree'd during
the v3.2 development window.
- packages/toon/README.md: drop two v3.1 bullets that crept into the
"What's new in v3.2" list (canonical empty-array form and \uXXXX
escape both landed in v3.1).
- docs/reference/api.md: rename "Delimiter mismatches" entry to
"Header delimiter mismatch" and cite §14.2 – the previous wording
invited the false reading that non-active delimiter chars in row
data are an error. Add §14.4 cross-ref to the duplicate-key entry.
- docs/reference/spec.md: include \uXXXX in the §7 escape list; soften
the charset MUST claim to match §17; en-dash the §-ranges in the
glance table.
- docs/reference/syntax-cheatsheet.md: rename "Strings with Active
Delimiter" to "Strings Containing Delimiters" and distinguish
active-delim (array scope) from document-delim (object field
values) per §11.1.
- docs/guide/format-overview.md: same active-vs-document distinction
in the delimiter paragraph; drop incorrect "strict mode" qualifier
from the unknown-escape error claim (§7.1 rejects unconditionally);
trim redundant "with leading zeros" parenthetical.
- docs/guide/llm-prompts.md: fix two broken anchors
(/reference/api#decode → #decode-input-options, similar for
encodelines); use -o instead of --output for CLI example
consistency.
- packages/cli/README.md: enumerate what --no-strict actually skips
in the options table; replace "delimiter consistency" with the
more specific "header delimiter mismatches" in the Lenient Decoding
prose to avoid the same false reading the api.md fix addresses.
The bracket-to-colon and bracket-to-fields checks previously trimmed
before comparing, so `items[2] :` and `items[2] {a,b}:` slipped through
strict mode. Tighten both checks to reject any non-empty gap and
distinguish whitespace-only gaps in the error message.
The README now flips the shields.io SPEC badge to v3.2 (the conformance
work just landed), adds a six-bullet "What's new in v3.2" subsection
between Key Features and Media Type, and fixes the stale §18.2 anchor
in the Media Type paragraph to point at §17 IANA Considerations.
In v3.2, --no-strict no longer just skips count/indent/delimiter
validation - it also applies last-write-wins on duplicate sibling
keys and tolerates malformed array headers as plain key-value lines.
The previous "faster processing" framing didn't reflect any of this.
- Lenient Decoding sections in cli docs and packages/cli/README spell
out which checks the decoder stops enforcing
- Options table description shifts from "Disable strict validation"
to "Skip decode validation; last-write-wins on duplicate keys"
- --strict help text in the CLI reads "Strict decode validation
(disable with --no-strict)"
- §17 IANA anchor fix in the cli docs media-type note
- Canonical empty-array form (`key: []` / root `[]`) replaces `key[0]:`
in format-overview, syntax-cheatsheet, and the efficiency-formalization
worked example. Legacy form still decodes.
- Escape tables grow a `\uXXXX` row covering U+0000-U+001F outside
\n/\r/\t, with a note rejecting lone-surrogate \uXXXX values.
- "Special characters" lists widened to "any control character (U+0000-U+001F)"
instead of the LF/HTAB/CR triplet.
- Tabular eligibility note adds the "at least one key" rule so arrays
containing an empty {} fall back to the expanded list.
- New short example for nested arrays-of-objects as list items (`- [N]:`).
- API reference: Date row gets an info callout noting it's an
implementation choice the spec leaves open; strict-mode bullet list
picks up header structure, duplicate sibling keys, path-expansion
conflicts, and lone-surrogate escapes; Expansion Conflict Resolution
notes the matching duplicate-key policy.
- §17 IANA anchor fix in getting-started media-type paragraph.
- Drop the removed §17 Interoperability and §19 Core Profile bullets
- Renumber §18 IANA -> §17, add §18 Versioning and §19 IP entries
- Appendix G -> F (Host Type Normalization Examples)
- Update the Sections-at-a-Glance table to the v3.2 layout
- §14.5 -> §14.3 path-expansion reference in the Decoder Checklist
- §14 enumeration covers count/width, syntax/structural, expansion, dup keys
- Encoder Checklist mentions \uXXXX escapes alongside the five base escapes
- Major-version example swapped to v2 -> v3 / v3.1 -> v3.2
- Working Draft date updated to 2026-05-20
Updates the JSDoc `@example` blocks for `encode` and `decode` to reflect
v3.2 output: tabular header is `users[2]{id}:` not `users[]:`, and
empty arrays render as `tags: []`.
Widens decoder behavior to match v3.2 strict-mode normative rules:
- Accepts the canonical `[]` empty-array form on all decode paths
(root, key-value, list-item-after-hyphen — sync and async)
- §6: parseBracketSegment now uses an anchored regex for the length
(rejects 0x10, 01, 1e5, -0, etc.) and throws in strict mode on
malformed bracket segments
- §14.2: parser scans the fields segment for any unquoted candidate
delimiter that mismatches the bracket-declared one and throws in
strict mode
- §14.4: assertNoDuplicateKey rejects duplicate sibling keys in strict
mode at every nested object site (root, child objects, list-item
objects, tabular-first list items)
- Strict flag now threads through parseArrayHeaderLine so header
diagnostics surface as decode errors rather than silent fallthrough
- Multi-line root that starts with a bare token now errors in strict
mode with a clear diagnostic
Per §9.1 v3.2, empty arrays MUST emit as `key: []` (and `- []` after a
list-item marker), not as `key[0]:`. The header-only emission is now
reserved for non-empty arrays.
Per the v3.2 spec, control characters U+0000–U+001F outside of \n, \r, \t
encode as \uXXXX escapes and decode them back. Lone surrogates are rejected.
- escapeString emits \uXXXX for any remaining U+0000–U+001F byte
- unescapeString recognises \uXXXX (4 hex digits) and rejects D800–DFFF
- isSafeUnquoted widens the control-char check to cover the full range
Adds the [Tooner](https://github.com/chaindead/tooner) MCP proxy entry to the Tools & Integration list in the package README and to docs/ecosystem/tools-and-playgrounds.md.
* feat(docs): add YAML input to Playground with tests
- Add input format selector and YAML parsing (YAML 1.2, merge keys off)
- Extract parse/stringify helpers for testability and SPEC-aligned encoding
- Add Vitest for docs package and playground parse tests
- Update Playground copy in package README and ecosystem docs
* refactor(docs): simplify Playground YAML support
- Inline parse/stringify helpers into the component, drop the separate
parse-input module and its tests
- Rename input state from `jsonInput`/`formattedJson`/`jsonTokens` to
`inputText`/`formattedInput`/`inputTokens` so names no longer lie when
YAML is selected
- Hide the JSON Baseline selector in YAML mode and make the token-count
tooltip conditional on input format
- Store input under `state.input` with a fallback read of legacy
`state.json` so existing share URLs keep working
- Drop redundant YAML parse options (merge/version match library
defaults); use lineWidth: 0 for editor display so long scalars do not
fold mid-sentence
- Remove the duplicated vitest devDep, docs vitest.config.ts, and `test`
script – the playground is verified through the dev server
* chore(docs): tighten Playground wording and drop empty-string fallback
- "in real-time" → "in real time" (real-time only hyphenates as a
compound modifier before a noun)
- Header copy now matches the ecosystem page: "Convert JSON or YAML to
TOON in real time"
- Token-count tooltip unified across input formats; the JSON baseline
selector is right next to it and already communicates the format
- Drop `?? ''` fallback on state.input — decodeState already gates on
successful JSON parse; the chain falls through to the legacy
state.json field
---------
Co-authored-by: Sweta <sweta@Swetas-MacBook-Air.local>
Co-authored-by: Johann Schopplich <johann@schopplich.com>