@toon-format/spec 1.5.2 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to the TOON specification will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0] - 2025-11-10
9
+
10
+ ### Breaking Changes
11
+
12
+ - **Removed:** Length marker (`#`) prefix in array headers has been completely removed from the specification
13
+ - The `[#N]` format is no longer valid syntax. All array headers MUST use `[N]` format only
14
+ - Encoders MUST NOT emit `[#N]` format
15
+ - Decoders MUST NOT accept `[#N]` format (breaking change from v1.5)
16
+
17
+ ### Removed
18
+
19
+ - All references to length marker from terminology (§1.4), header syntax (§6), ABNF grammar, conformance requirements (§13.2), and parsing helpers (Appendix B)
20
+ - `lengthMarker` encoder option removed from all implementations
21
+ - Length marker test fixtures removed
22
+
23
+ ### Migration from v1.5
24
+
25
+ - Update decoder implementations to reject `[#N]` syntax
26
+ - Convert any existing `.toon` files using `[#N]` format to `[N]` format
27
+ - Remove `lengthMarker` option from encoder configurations
28
+ - Remove `--length-marker` CLI flags if present
29
+
8
30
  ## [1.5] - 2025-11-08
9
31
 
10
32
  ### Added
package/CONTRIBUTING.md CHANGED
@@ -32,7 +32,7 @@ New data type normalization rules
32
32
  Example: Handling Map or Set differently
33
33
 
34
34
  Changing array header syntax
35
- Example: Optional length markers becoming required
35
+ Example: Making field lists mandatory for all arrays
36
36
  ```
37
37
 
38
38
  ### No – Direct PR or Issue First
package/README.md CHANGED
@@ -1,67 +1,93 @@
1
1
  # TOON Format Specification
2
2
 
3
- [![SPEC v1.5](https://img.shields.io/badge/spec-v1.5-lightgrey)](./SPEC.md)
4
- [![Tests](https://img.shields.io/badge/tests-348-green)](./tests/fixtures/)
3
+ [![SPEC v2.0](https://img.shields.io/badge/spec-v2.0-lightgrey)](./SPEC.md)
4
+ [![Tests](https://img.shields.io/badge/tests-342-green)](./tests/fixtures/)
5
5
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
6
6
 
7
- This repository contains the official specification for **Token-Oriented Object Notation (TOON)**, a compact, human-readable serialization format designed for passing structured data to Large Language Models with significantly reduced token usage.
7
+ This repository contains the official specification for **Token-Oriented Object Notation (TOON)**, a compact, human-readable encoding of the JSON data model for LLM prompts. It provides a lossless serialization of the same objects, arrays, and primitives as JSON, but in a syntax that minimizes tokens and makes structure easy for models to follow.
8
8
 
9
9
  ## 📋 Specification
10
10
 
11
11
  [→ Read the full specification (SPEC.md)](./SPEC.md)
12
12
 
13
- - **Version:** 1.5 (2025-11-10)
13
+ - **Version:** 2.0 (2025-11-10)
14
14
  - **Status:** Working Draft
15
15
  - **License:** MIT
16
16
 
17
17
  The specification includes complete grammar (ABNF), encoding rules, validation requirements, and conformance criteria.
18
18
 
19
- ### New in v1.5
20
-
21
- - **Key Folding** (encode): Collapse nested single-key objects into compact dotted paths
22
- - `{"a": {"b": {"c": 1}}}` → `a.b.c: 1`
23
- - Opt-in via `keyFolding="safe"` with `flattenDepth` control
24
- - **Path Expansion** (decode): Expand dotted keys back to nested objects
25
- - `a.b.c: 1` → `{"a": {"b": {"c": 1}}}`
26
- - Opt-in via `expandPaths="safe"` with deep-merge semantics
27
-
28
- > [!NOTE]
29
- > Both features are opt-in to maintain backward compatibility.
30
-
31
19
  ## What is TOON?
32
20
 
33
- **Token-Oriented Object Notation** is a compact, human-readable serialization format designed for passing structured data to Large Language Models with significantly reduced token usage. It's intended for LLM input, not output.
34
-
35
- TOON's sweet spot is **uniform arrays of objects** – multiple fields per row, same structure across items. It borrows YAML's indentation-based structure for nested objects and CSV's tabular format for uniform data rows, then optimizes both for token efficiency in LLM contexts. For deeply nested or non-uniform data, JSON may be more efficient.
36
-
37
- **Key Features:**
21
+ > [!IMPORTANT]
22
+ > For a high-level overview of TOON, its features and benefits, design goals, and comparisons to other formats, see the [`toon-format/toon` repository](https://github.com/toon-format/toon).
38
23
 
39
- - 💸 **Token-efficient:** typically 30–60% fewer tokens than JSON
40
- - 🤿 **LLM-friendly guardrails:** explicit lengths and fields enable validation
41
- - 🍱 **Minimal syntax:** removes redundant punctuation (braces, brackets, most quotes)
42
- - 📐 **Indentation-based structure:** like YAML, uses whitespace instead of braces
43
- - 🧺 **Tabular arrays:** declare keys once, stream data as rows
24
+ ## Serialization Example
44
25
 
45
- ## Quick Example
46
-
47
- **JSON:**
26
+ <table>
27
+ <tr>
28
+ <th>JSON</th>
29
+ <th>TOON</th>
30
+ </tr>
31
+ <tr>
32
+ <td>
48
33
 
49
34
  ```json
50
35
  {
51
- "users": [
52
- { "id": 1, "name": "Alice", "role": "admin" },
53
- { "id": 2, "name": "Bob", "role": "user" }
36
+ "context": {
37
+ "task": "Our favorite hikes together",
38
+ "location": "Boulder",
39
+ "season": "spring_2025"
40
+ },
41
+ "friends": ["ana", "luis", "sam"],
42
+ "hikes": [
43
+ {
44
+ "id": 1,
45
+ "name": "Blue Lake Trail",
46
+ "distanceKm": 7.5,
47
+ "elevationGain": 320,
48
+ "companion": "ana",
49
+ "wasSunny": true
50
+ },
51
+ {
52
+ "id": 2,
53
+ "name": "Ridge Overlook",
54
+ "distanceKm": 9.2,
55
+ "elevationGain": 540,
56
+ "companion": "luis",
57
+ "wasSunny": false
58
+ },
59
+ {
60
+ "id": 3,
61
+ "name": "Wildflower Loop",
62
+ "distanceKm": 5.1,
63
+ "elevationGain": 180,
64
+ "companion": "sam",
65
+ "wasSunny": true
66
+ }
54
67
  ]
55
68
  }
56
69
  ```
57
70
 
58
- **TOON:**
71
+ </td>
72
+ <td>
59
73
 
74
+ ```toon
75
+ context:
76
+ task: Our favorite hikes together
77
+ location: Boulder
78
+ season: spring_2025
79
+
80
+ friends[3]: ana,luis,sam
81
+
82
+ hikes[3]{id,name,distanceKm,elevationGain,companion,wasSunny}:
83
+ 1,Blue Lake Trail,7.5,320,ana,true
84
+ 2,Ridge Overlook,9.2,540,luis,false
85
+ 3,Wildflower Loop,5.1,180,sam,true
60
86
  ```
61
- users[2]{id,name,role}:
62
- 1,Alice,admin
63
- 2,Bob,user
64
- ```
87
+
88
+ </td>
89
+ </tr>
90
+ </table>
65
91
 
66
92
  ## Reference Implementation
67
93
 
package/SPEC.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Token-Oriented Object Notation
4
4
 
5
- **Version:** 1.5
5
+ **Version:** 2.0
6
6
 
7
7
  **Date:** 2025-11-10
8
8
 
@@ -20,9 +20,9 @@ Token-Oriented Object Notation (TOON) is a line-oriented, indentation-based text
20
20
 
21
21
  ## Status of This Document
22
22
 
23
- This document is a Working Draft v1.4 and may be updated, replaced, or obsoleted. Implementers should monitor the canonical repository at https://github.com/toon-format/spec for changes.
23
+ This document is a Working Draft v2.0 and may be updated, replaced, or obsoleted. Implementers should monitor the canonical repository at https://github.com/toon-format/spec for changes.
24
24
 
25
- This specification is stable for implementation but not yet finalized. Breaking changes are unlikely but possible before v2.0.
25
+ This specification is stable for implementation but not yet finalized. Breaking changes may occur in future major versions.
26
26
 
27
27
  ## Normative References
28
28
 
@@ -165,7 +165,6 @@ Implementations that fail to conform to any MUST or REQUIRED level requirement a
165
165
  - Header: The bracketed declaration for arrays, optionally followed by a field list, and terminating with a colon; e.g., key[3]: or items[2]{a,b}:.
166
166
  - Field list: Brace-enclosed, delimiter-separated list of field names for tabular arrays: {f1<delim>f2}.
167
167
  - List item: A line beginning with "- " at a given depth representing an element in an expanded array.
168
- - Length marker: Optional "#" prefix for array lengths in headers, e.g., [#3]. Decoders MUST accept and ignore it semantically.
169
168
 
170
169
  ### 1.5 Delimiter Terms
171
170
 
@@ -294,13 +293,12 @@ TOON is a deterministic, line-oriented, indentation-based notation.
294
293
  Array headers declare length and active delimiter, and optionally field names.
295
294
 
296
295
  General forms:
297
- - Root header (no key): [<marker?>N<delim?>]:
298
- - With key: key[<marker?>N<delim?>]:
299
- - Tabular fields: key[<marker?>N<delim?>]{field1<delim>field2<delim>…}:
296
+ - Root header (no key): [N<delim?>]:
297
+ - With key: key[N<delim?>]:
298
+ - Tabular fields: key[N<delim?>]{field1<delim>field2<delim>…}:
300
299
 
301
300
  Where:
302
301
  - N is the non-negative integer length.
303
- - <marker?> is optional "#"; decoders MUST accept and ignore it semantically.
304
302
  - <delim?> is:
305
303
  - absent for comma (","),
306
304
  - HTAB (U+0009) for tab,
@@ -329,7 +327,7 @@ LF = %x0A ; line feed
329
327
  SP = %x20 ; space
330
328
 
331
329
  ; Header syntax
332
- bracket-seg = "[" [ "#" ] 1*DIGIT [ delimsym ] "]"
330
+ bracket-seg = "[" 1*DIGIT [ delimsym ] "]"
333
331
  delimsym = HTAB / "|"
334
332
  ; Field names are keys (quoted/unquoted) separated by the active delimiter
335
333
  fields-seg = "{" fieldname *( delim fieldname ) "}"
@@ -592,7 +590,6 @@ Options:
592
590
  - Encoder options:
593
591
  - indent (default: 2 spaces)
594
592
  - delimiter (document delimiter; default: comma; alternatives: tab, pipe)
595
- - lengthMarker (default: disabled)
596
593
  - keyFolding (default: `"off"`; alternatives: `"safe"`)
597
594
  - flattenDepth (default: Infinity when keyFolding is `"safe"`; non-negative integer ≥ 0; values 0 or 1 have no practical folding effect)
598
595
  - Decoder options:
@@ -700,7 +697,6 @@ Conforming decoders MUST:
700
697
  - [ ] Unescape quoted strings with only valid escapes (§7.1)
701
698
  - [ ] Type unquoted primitives: true/false/null → booleans/null, numeric → number, else → string (§4)
702
699
  - [ ] Enforce strict-mode rules when `strict=true` (§14)
703
- - [ ] Accept and ignore optional # length marker (§6)
704
700
  - [ ] Preserve array order and object key order (§2)
705
701
  - [ ] When `expandPaths="safe"`, expansion MUST follow §13.4 (IdentifierSegment-only segments, deep merge, conflict rules)
706
702
  - [ ] When `expandPaths="safe"` with `strict=true`, MUST error on expansion conflicts per §14.5
@@ -827,7 +823,7 @@ count: 2
827
823
  TOON's tabular format generalizes CSV [RFC4180] with several enhancements:
828
824
 
829
825
  Advantages over CSV:
830
- - Explicit array length markers enable validation
826
+ - Explicit array length declarations enable validation
831
827
  - Field names declared in header (no separate header row)
832
828
  - Supports nested structures (CSV is flat-only)
833
829
  - Three delimiter options (comma/tab/pipe) vs CSV's comma-only
@@ -853,7 +849,7 @@ Conversion Guidelines:
853
849
  - CSV headers map to TOON field names
854
850
  - CSV data rows map to TOON tabular rows
855
851
  - CSV string escaping (double-quotes) maps to TOON quoting rules
856
- - CSV row count can be added as array length marker
852
+ - CSV row count can be added as array length declaration
857
853
 
858
854
  ### 17.3 YAML Interoperability
859
855
 
@@ -1007,14 +1003,6 @@ items[2 ]{sku name qty price}:
1007
1003
  tags[3|]: reading|gaming|coding
1008
1004
  ```
1009
1005
 
1010
- Length marker:
1011
- ```
1012
- tags[#3]: reading,gaming,coding
1013
- pairs[#2]:
1014
- - [#2]: a,b
1015
- - [#2]: c,d
1016
- ```
1017
-
1018
1006
  Quoted colons and disambiguation (rows continue; colon is inside quotes):
1019
1007
  ```
1020
1008
  links[2]{id,url}:
@@ -1157,12 +1145,11 @@ These sketches illustrate structure and common decoding helpers. They are inform
1157
1145
  ### B.2 Array Header Parsing
1158
1146
 
1159
1147
  - Locate the first "[ … ]" segment on the line; parse:
1160
- - Optional leading "#" marker (ignored semantically).
1161
1148
  - Length N as decimal integer.
1162
1149
  - Optional delimiter symbol at the end: HTAB or pipe (comma otherwise).
1163
1150
  - If a "{ … }" fields segment occurs between the "]" and the ":", parse field names using the active delimiter; unescape quoted names.
1164
1151
  - Require a colon ":" after the bracket/fields segment.
1165
- - Return the header (key?, length, delimiter, fields?, hasLengthMarker) and any inline values after the colon.
1152
+ - Return the header (key?, length, delimiter, fields?) and any inline values after the colon.
1166
1153
  - Absence of a delimiter symbol in the bracket segment ALWAYS means comma for that header (no inheritance).
1167
1154
 
1168
1155
  ### B.3 parseDelimitedValues
@@ -1231,6 +1218,14 @@ Note: Host-type normalization tests (e.g., BigInt, Date, Set, Map) are language-
1231
1218
 
1232
1219
  ## Appendix D: Document Changelog (Informative)
1233
1220
 
1221
+ ### v2.0 (2025-11-10)
1222
+
1223
+ - Breaking change: Length marker (`#`) prefix in array headers has been completely removed from the specification.
1224
+ - The `[#N]` format is no longer valid syntax. All array headers MUST use `[N]` format only.
1225
+ - Encoders MUST NOT emit `[#N]` format.
1226
+ - Decoders MUST NOT accept `[#N]` format (breaking change from v1.5).
1227
+ - Removed all references to length marker from terminology, grammar, conformance requirements, and parsing helpers.
1228
+
1234
1229
  ### v1.5 (2025-11-08)
1235
1230
 
1236
1231
  - Added optional key folding for encoders: `keyFolding='safe'` mode with `flattenDepth` control (§13.4).
@@ -1291,7 +1286,6 @@ This specification and reference implementation are released under the MIT Licen
1291
1286
  - The reference encoder/decoder test suites implement:
1292
1287
  - Safe-unquoted string rules and delimiter-aware quoting (document vs active delimiter).
1293
1288
  - Header formation and delimiter-aware parsing with active delimiter scoping.
1294
- - Length marker propagation (encoding) and acceptance (decoding).
1295
1289
  - Tabular detection requiring uniform keys and primitive-only values.
1296
1290
  - Objects-as-list-items parsing (+2 nested object rule; +1 siblings).
1297
1291
  - Whitespace invariants for encoding and strict-mode indentation enforcement for decoding.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@toon-format/spec",
3
3
  "type": "module",
4
- "version": "1.5.2",
4
+ "version": "2.0.1",
5
5
  "packageManager": "pnpm@10.19.0",
6
6
  "description": "Official specification for Token-Oriented Object Notation (TOON)",
7
7
  "author": "Johann Schopplich <hello@johannschopplich.com>",
package/tests/README.md CHANGED
@@ -92,7 +92,6 @@ All test fixtures follow a standard JSON structure defined in [`fixtures.schema.
92
92
  {
93
93
  "delimiter": ",",
94
94
  "indent": 2,
95
- "lengthMarker": "#",
96
95
  "keyFolding": "safe",
97
96
  "flattenDepth": 3
98
97
  }
@@ -100,7 +99,6 @@ All test fixtures follow a standard JSON structure defined in [`fixtures.schema.
100
99
 
101
100
  - `delimiter`: `","` (comma, default), `"\t"` (tab), or `"|"` (pipe). Affects encoder output; decoders parse the delimiter declared in array headers
102
101
  - `indent`: Number of spaces per indentation level (default: `2`)
103
- - `lengthMarker`: Optional. Set to `"#"` to prefix array lengths (e.g., `[#3]`). Omit this property to disable length markers
104
102
  - `keyFolding`: `"off"` (default) or `"safe"`. Enables key folding to collapse single-key object chains into dotted-path notation (v1.5+)
105
103
  - `flattenDepth`: Integer. Maximum depth to fold key chains when `keyFolding` is `"safe"` (default: Infinity). Values less than 2 have no practical folding effect (v1.5+)
106
104
 
@@ -161,7 +159,6 @@ The fixture format is language-agnostic JSON, so you can load and iterate it usi
161
159
  | `arrays-objects.json` | Objects as list items, complex nesting | §9, §10 |
162
160
  | `delimiters.json` | Tab and pipe delimiter options | §11 |
163
161
  | `whitespace.json` | Formatting invariants and indentation | §12 |
164
- | `options.json` | Length marker and delimiter option combinations | §3 |
165
162
  | `key-folding.json` | Key folding with safe mode, depth control, collision avoidance | §13.4 |
166
163
 
167
164
  ### Decoding Tests (`fixtures/decode/`)
@@ -69,10 +69,10 @@
69
69
  },
70
70
  {
71
71
  "name": "parses objects containing arrays (including empty arrays) in list format",
72
- "input": "items[1]:\n - name: test\n data[0]:",
72
+ "input": "items[1]:\n - name: Ada\n data[0]:",
73
73
  "expected": {
74
74
  "items": [
75
- { "name": "test", "data": [] }
75
+ { "name": "Ada", "data": [] }
76
76
  ]
77
77
  },
78
78
  "specSection": "9.4"
@@ -120,35 +120,41 @@
120
120
  "specSection": "9.2"
121
121
  },
122
122
  {
123
- "name": "parses root arrays of primitives (inline)",
123
+ "name": "parses root-level primitive array inline",
124
124
  "input": "[5]: x,y,\"true\",true,10",
125
125
  "expected": ["x", "y", "true", true, 10],
126
126
  "specSection": "9.1"
127
127
  },
128
128
  {
129
- "name": "parses root arrays of uniform objects in tabular format",
129
+ "name": "parses root-level array of uniform objects in tabular format",
130
130
  "input": "[2]{id}:\n 1\n 2",
131
131
  "expected": [{ "id": 1 }, { "id": 2 }],
132
132
  "specSection": "9.3"
133
133
  },
134
134
  {
135
- "name": "parses root arrays of non-uniform objects in list format",
135
+ "name": "parses root-level array of non-uniform objects in list format",
136
136
  "input": "[2]:\n - id: 1\n - id: 2\n name: Ada",
137
137
  "expected": [{ "id": 1 }, { "id": 2, "name": "Ada" }],
138
138
  "specSection": "9.4"
139
139
  },
140
140
  {
141
- "name": "parses empty root arrays",
142
- "input": "[0]:",
143
- "expected": [],
144
- "specSection": "9.1"
141
+ "name": "parses root-level array mixing primitive, object, and array of objects in list format",
142
+ "input": "[3]:\n - summary\n - id: 1\n name: Ada\n - [2]:\n - id: 2\n - status: draft",
143
+ "expected": ["summary", { "id": 1, "name": "Ada" }, [{ "id": 2 }, { "status": "draft" }]],
144
+ "specSection": "9.4"
145
145
  },
146
146
  {
147
- "name": "parses root arrays of arrays",
147
+ "name": "parses root-level array of arrays",
148
148
  "input": "[2]:\n - [2]: 1,2\n - [0]:",
149
149
  "expected": [[1, 2], []],
150
150
  "specSection": "9.2"
151
151
  },
152
+ {
153
+ "name": "parses empty root-level array",
154
+ "input": "[0]:",
155
+ "expected": [],
156
+ "specSection": "9.1"
157
+ },
152
158
  {
153
159
  "name": "parses complex mixed object with arrays and nested objects",
154
160
  "input": "user:\n id: 123\n name: Ada\n tags[2]: reading,gaming\n active: true\n prefs[0]:",
@@ -164,7 +170,7 @@
164
170
  "specSection": "8"
165
171
  },
166
172
  {
167
- "name": "parses arrays mixing primitives, objects and strings (list format)",
173
+ "name": "parses arrays mixing primitives, objects, and strings in list format",
168
174
  "input": "items[3]:\n - 1\n - a: 1\n - text",
169
175
  "expected": {
170
176
  "items": [1, { "a": 1 }, "text"]
@@ -59,7 +59,7 @@
59
59
  "specSection": "9.3"
60
60
  },
61
61
  {
62
- "name": "unquoted colon terminates tabular rows and starts key-value pair",
62
+ "name": "treats unquoted colon as terminator for tabular rows and start of key-value pair",
63
63
  "input": "items[2]{id,name}:\n 1,Alice\n 2,Bob\ncount: 2",
64
64
  "expected": {
65
65
  "items": [
@@ -66,7 +66,7 @@
66
66
  "specSection": "11"
67
67
  },
68
68
  {
69
- "name": "nested arrays inside list items default to comma delimiter",
69
+ "name": "parses nested arrays inside list items with default comma delimiter",
70
70
  "input": "items[1\t]:\n - tags[3]: a,b,c",
71
71
  "expected": {
72
72
  "items": [{ "tags": ["a", "b", "c"] }]
@@ -75,7 +75,7 @@
75
75
  "note": "Parent uses tab, nested defaults to comma"
76
76
  },
77
77
  {
78
- "name": "nested arrays inside list items default to comma with pipe parent",
78
+ "name": "parses nested arrays inside list items with default comma delimiter when parent uses pipe",
79
79
  "input": "items[1|]:\n - tags[3]: a,b,c",
80
80
  "expected": {
81
81
  "items": [{ "tags": ["a", "b", "c"] }]
@@ -83,25 +83,25 @@
83
83
  "specSection": "11"
84
84
  },
85
85
  {
86
- "name": "parses root arrays with tab delimiter",
86
+ "name": "parses root-level array with tab delimiter",
87
87
  "input": "[3\t]: x\ty\tz",
88
88
  "expected": ["x", "y", "z"],
89
89
  "specSection": "11"
90
90
  },
91
91
  {
92
- "name": "parses root arrays with pipe delimiter",
92
+ "name": "parses root-level array with pipe delimiter",
93
93
  "input": "[3|]: x|y|z",
94
94
  "expected": ["x", "y", "z"],
95
95
  "specSection": "11"
96
96
  },
97
97
  {
98
- "name": "parses root arrays of objects with tab delimiter",
98
+ "name": "parses root-level array of objects with tab delimiter",
99
99
  "input": "[2\t]{id}:\n 1\n 2",
100
100
  "expected": [{ "id": 1 }, { "id": 2 }],
101
101
  "specSection": "11"
102
102
  },
103
103
  {
104
- "name": "parses root arrays of objects with pipe delimiter",
104
+ "name": "parses root-level array of objects with pipe delimiter",
105
105
  "input": "[2|]{id}:\n 1\n 2",
106
106
  "expected": [{ "id": 1 }, { "id": 2 }],
107
107
  "specSection": "11"
@@ -241,14 +241,6 @@
241
241
  "items": [{ "a|b": 1 }, { "a|b": 2 }]
242
242
  },
243
243
  "specSection": "11"
244
- },
245
- {
246
- "name": "accepts length marker with pipe delimiter",
247
- "input": "tags[#3|]: reading|gaming|coding",
248
- "expected": {
249
- "tags": ["reading", "gaming", "coding"]
250
- },
251
- "specSection": "6"
252
244
  }
253
245
  ]
254
246
  }
@@ -4,7 +4,7 @@
4
4
  "description": "Strict mode indentation validation - non-multiple indentation, tab characters, custom indent sizes",
5
5
  "tests": [
6
6
  {
7
- "name": "throws when object field has non-multiple indentation (3 spaces with indent=2)",
7
+ "name": "throws on object field with non-multiple indentation (3 spaces with indent=2)",
8
8
  "input": "a:\n b: 1",
9
9
  "expected": null,
10
10
  "shouldError": true,
@@ -15,7 +15,7 @@
15
15
  "specSection": "14.3"
16
16
  },
17
17
  {
18
- "name": "throws when list item has non-multiple indentation (3 spaces with indent=2)",
18
+ "name": "throws on list item with non-multiple indentation (3 spaces with indent=2)",
19
19
  "input": "items[2]:\n - id: 1\n - id: 2",
20
20
  "expected": null,
21
21
  "shouldError": true,
@@ -26,7 +26,7 @@
26
26
  "specSection": "14.3"
27
27
  },
28
28
  {
29
- "name": "throws with custom indent size when non-multiple (3 spaces with indent=4)",
29
+ "name": "throws on non-multiple indentation with custom indent=4 (3 spaces)",
30
30
  "input": "a:\n b: 1",
31
31
  "expected": null,
32
32
  "shouldError": true,
@@ -51,7 +51,7 @@
51
51
  "specSection": "12"
52
52
  },
53
53
  {
54
- "name": "throws when tab character used in indentation",
54
+ "name": "throws on tab character used in indentation",
55
55
  "input": "a:\n\tb: 1",
56
56
  "expected": null,
57
57
  "shouldError": true,
@@ -61,7 +61,7 @@
61
61
  "specSection": "14.3"
62
62
  },
63
63
  {
64
- "name": "throws when mixed tabs and spaces in indentation",
64
+ "name": "throws on mixed tabs and spaces in indentation",
65
65
  "input": "a:\n \tb: 1",
66
66
  "expected": null,
67
67
  "shouldError": true,
@@ -71,7 +71,7 @@
71
71
  "specSection": "14.3"
72
72
  },
73
73
  {
74
- "name": "throws when tab at start of line",
74
+ "name": "throws on tab at start of line",
75
75
  "input": "\ta: 1",
76
76
  "expected": null,
77
77
  "shouldError": true,
@@ -144,7 +144,7 @@
144
144
  "specSection": "12"
145
145
  },
146
146
  {
147
- "name": "empty lines do not trigger validation errors",
147
+ "name": "parses empty lines without validation errors",
148
148
  "input": "a: 1\n\nb: 2",
149
149
  "expected": {
150
150
  "a": 1,
@@ -156,7 +156,7 @@
156
156
  "specSection": "12"
157
157
  },
158
158
  {
159
- "name": "root-level content (0 indentation) is always valid",
159
+ "name": "parses root-level content (0 indentation) as always valid",
160
160
  "input": "a: 1\nb: 2\nc: 3",
161
161
  "expected": {
162
162
  "a": 1,
@@ -169,7 +169,7 @@
169
169
  "specSection": "12"
170
170
  },
171
171
  {
172
- "name": "lines with only spaces are not validated if empty",
172
+ "name": "parses lines with only spaces without validation if empty",
173
173
  "input": "a: 1\n \nb: 2",
174
174
  "expected": {
175
175
  "a": 1,
@@ -4,7 +4,7 @@
4
4
  "description": "Root form detection - empty document, single primitive, multiple primitives",
5
5
  "tests": [
6
6
  {
7
- "name": "empty document decodes to empty object",
7
+ "name": "parses empty document as empty object",
8
8
  "input": "",
9
9
  "expected": {},
10
10
  "options": {
@@ -18,14 +18,14 @@
18
18
  "specSection": "14.1"
19
19
  },
20
20
  {
21
- "name": "throws when tabular row value count does not match header field count",
21
+ "name": "throws on tabular row value count mismatch with header field count",
22
22
  "input": "items[2]{id,name}:\n 1,Ada\n 2",
23
23
  "expected": null,
24
24
  "shouldError": true,
25
25
  "specSection": "14.1"
26
26
  },
27
27
  {
28
- "name": "throws when tabular row count does not match header length",
28
+ "name": "throws on tabular row count mismatch with header length",
29
29
  "input": "[1]{id}:\n 1\n 2",
30
30
  "expected": null,
31
31
  "shouldError": true,
@@ -49,7 +49,7 @@
49
49
  "specSection": "12"
50
50
  },
51
51
  {
52
- "name": "empty tokens decode to empty string",
52
+ "name": "parses empty tokens as empty string",
53
53
  "input": "items[3]: a,,c",
54
54
  "expected": {
55
55
  "items": ["a", "", "c"]
@@ -54,10 +54,10 @@
54
54
  "specSection": "9.4"
55
55
  },
56
56
  {
57
- "name": "encodes empty root-level array",
58
- "input": [],
59
- "expected": "[0]:",
60
- "specSection": "9.1"
57
+ "name": "encodes root-level array mixing primitive, object, and array of objects in list format",
58
+ "input": ["summary", { "id": 1, "name": "Ada" }, [{ "id": 2 }, { "status": "draft" }]],
59
+ "expected": "[3]:\n - summary\n - id: 1\n name: Ada\n - [2]:\n - id: 2\n - status: draft",
60
+ "specSection": "9.4"
61
61
  },
62
62
  {
63
63
  "name": "encodes root-level arrays of arrays",
@@ -65,6 +65,12 @@
65
65
  "expected": "[2]:\n - [2]: 1,2\n - [0]:",
66
66
  "specSection": "9.2"
67
67
  },
68
+ {
69
+ "name": "encodes empty root-level array",
70
+ "input": [],
71
+ "expected": "[0]:",
72
+ "specSection": "9.1"
73
+ },
68
74
  {
69
75
  "name": "encodes complex nested structure",
70
76
  "input": {
@@ -27,17 +27,17 @@
27
27
  {
28
28
  "name": "preserves field order in list items - array first",
29
29
  "input": {
30
- "items": [{ "nums": [1, 2, 3], "name": "test" }]
30
+ "items": [{ "nums": [1, 2, 3], "name": "Ada" }]
31
31
  },
32
- "expected": "items[1]:\n - nums[3]: 1,2,3\n name: test",
32
+ "expected": "items[1]:\n - nums[3]: 1,2,3\n name: Ada",
33
33
  "specSection": "10"
34
34
  },
35
35
  {
36
36
  "name": "preserves field order in list items - primitive first",
37
37
  "input": {
38
- "items": [{ "name": "test", "nums": [1, 2, 3] }]
38
+ "items": [{ "name": "Ada", "nums": [1, 2, 3] }]
39
39
  },
40
- "expected": "items[1]:\n - name: test\n nums[3]: 1,2,3",
40
+ "expected": "items[1]:\n - name: Ada\n nums[3]: 1,2,3",
41
41
  "specSection": "10"
42
42
  },
43
43
  {
@@ -90,10 +90,10 @@
90
90
  "name": "encodes objects with empty arrays in list format",
91
91
  "input": {
92
92
  "items": [
93
- { "name": "test", "data": [] }
93
+ { "name": "Ada", "data": [] }
94
94
  ]
95
95
  },
96
- "expected": "items[1]:\n - name: test\n data[0]:",
96
+ "expected": "items[1]:\n - name: Ada\n data[0]:",
97
97
  "specSection": "10"
98
98
  },
99
99
  {
@@ -124,7 +124,7 @@
124
124
  "specSection": "9.3"
125
125
  },
126
126
  {
127
- "name": "uses list format when one object has nested column",
127
+ "name": "uses list format when one object has nested field",
128
128
  "input": {
129
129
  "items": [
130
130
  { "id": 1, "data": "string" },
@@ -4,7 +4,7 @@
4
4
  "description": "Tabular array encoding - arrays of uniform objects with primitive values",
5
5
  "tests": [
6
6
  {
7
- "name": "encodes arrays of similar objects in tabular format",
7
+ "name": "encodes arrays of uniform objects in tabular format",
8
8
  "input": {
9
9
  "items": [
10
10
  { "sku": "A1", "qty": 2, "price": 9.99 },
@@ -87,7 +87,7 @@
87
87
  "specSection": "11"
88
88
  },
89
89
  {
90
- "name": "encodes root arrays with tab delimiter",
90
+ "name": "encodes root-level array with tab delimiter",
91
91
  "input": ["x", "y", "z"],
92
92
  "expected": "[3\t]: x\ty\tz",
93
93
  "options": {
@@ -96,7 +96,7 @@
96
96
  "specSection": "11"
97
97
  },
98
98
  {
99
- "name": "encodes root arrays with pipe delimiter",
99
+ "name": "encodes root-level array with pipe delimiter",
100
100
  "input": ["x", "y", "z"],
101
101
  "expected": "[3|]: x|y|z",
102
102
  "options": {
@@ -105,7 +105,7 @@
105
105
  "specSection": "11"
106
106
  },
107
107
  {
108
- "name": "encodes root arrays of objects with tab delimiter",
108
+ "name": "encodes root-level array of objects with tab delimiter",
109
109
  "input": [{ "id": 1 }, { "id": 2 }],
110
110
  "expected": "[2\t]{id}:\n 1\n 2",
111
111
  "options": {
@@ -114,7 +114,7 @@
114
114
  "specSection": "11"
115
115
  },
116
116
  {
117
- "name": "encodes root arrays of objects with pipe delimiter",
117
+ "name": "encodes root-level array of objects with pipe delimiter",
118
118
  "input": [{ "id": 1 }, { "id": 2 }],
119
119
  "expected": "[2|]{id}:\n 1\n 2",
120
120
  "options": {
@@ -10,7 +10,7 @@
10
10
  "type": "string",
11
11
  "description": "TOON specification version these tests target",
12
12
  "pattern": "^\\d+\\.\\d+$",
13
- "examples": ["1.0", "1.3"]
13
+ "examples": ["1.0", "1.6"]
14
14
  },
15
15
  "category": {
16
16
  "type": "string",
@@ -67,11 +67,6 @@
67
67
  "minimum": 1,
68
68
  "default": 2
69
69
  },
70
- "lengthMarker": {
71
- "type": "string",
72
- "const": "#",
73
- "description": "Optional marker to prefix array lengths (encode only). Omit to disable the marker."
74
- },
75
70
  "strict": {
76
71
  "type": "boolean",
77
72
  "description": "Enable strict validation (decode only)",
@@ -1,88 +0,0 @@
1
- {
2
- "version": "1.4",
3
- "category": "encode",
4
- "description": "Encoding options - lengthMarker option and combinations with delimiters",
5
- "tests": [
6
- {
7
- "name": "adds length marker to primitive arrays",
8
- "input": {
9
- "tags": ["reading", "gaming", "coding"]
10
- },
11
- "expected": "tags[#3]: reading,gaming,coding",
12
- "options": {
13
- "lengthMarker": "#"
14
- },
15
- "specSection": "3"
16
- },
17
- {
18
- "name": "adds length marker to empty arrays",
19
- "input": {
20
- "items": []
21
- },
22
- "expected": "items[#0]:",
23
- "options": {
24
- "lengthMarker": "#"
25
- },
26
- "specSection": "3"
27
- },
28
- {
29
- "name": "adds length marker to tabular arrays",
30
- "input": {
31
- "items": [
32
- { "sku": "A1", "qty": 2, "price": 9.99 },
33
- { "sku": "B2", "qty": 1, "price": 14.5 }
34
- ]
35
- },
36
- "expected": "items[#2]{sku,qty,price}:\n A1,2,9.99\n B2,1,14.5",
37
- "options": {
38
- "lengthMarker": "#"
39
- },
40
- "specSection": "3"
41
- },
42
- {
43
- "name": "adds length marker to nested arrays",
44
- "input": {
45
- "pairs": [["a", "b"], ["c", "d"]]
46
- },
47
- "expected": "pairs[#2]:\n - [#2]: a,b\n - [#2]: c,d",
48
- "options": {
49
- "lengthMarker": "#"
50
- },
51
- "specSection": "3"
52
- },
53
- {
54
- "name": "combines length marker with pipe delimiter",
55
- "input": {
56
- "tags": ["reading", "gaming", "coding"]
57
- },
58
- "expected": "tags[#3|]: reading|gaming|coding",
59
- "options": {
60
- "lengthMarker": "#",
61
- "delimiter": "|"
62
- },
63
- "specSection": "3"
64
- },
65
- {
66
- "name": "combines length marker with tab delimiter",
67
- "input": {
68
- "tags": ["reading", "gaming", "coding"]
69
- },
70
- "expected": "tags[#3\t]: reading\tgaming\tcoding",
71
- "options": {
72
- "lengthMarker": "#",
73
- "delimiter": "\t"
74
- },
75
- "specSection": "3"
76
- },
77
- {
78
- "name": "default lengthMarker is empty (no marker)",
79
- "input": {
80
- "tags": ["reading", "gaming", "coding"]
81
- },
82
- "expected": "tags[3]: reading,gaming,coding",
83
- "options": {},
84
- "specSection": "3",
85
- "note": "Default behavior without lengthMarker option"
86
- }
87
- ]
88
- }