@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 +22 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +63 -37
- package/SPEC.md +18 -24
- package/package.json +1 -1
- package/tests/README.md +0 -3
- package/tests/fixtures/decode/arrays-nested.json +17 -11
- package/tests/fixtures/decode/arrays-tabular.json +1 -1
- package/tests/fixtures/decode/delimiters.json +6 -14
- package/tests/fixtures/decode/indentation-errors.json +9 -9
- package/tests/fixtures/decode/root-form.json +1 -1
- package/tests/fixtures/decode/validation-errors.json +2 -2
- package/tests/fixtures/decode/whitespace.json +1 -1
- package/tests/fixtures/encode/arrays-nested.json +10 -4
- package/tests/fixtures/encode/arrays-objects.json +7 -7
- package/tests/fixtures/encode/arrays-tabular.json +1 -1
- package/tests/fixtures/encode/delimiters.json +4 -4
- package/tests/fixtures.schema.json +1 -6
- package/tests/fixtures/encode/options.json +0 -88
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:
|
|
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.md)
|
|
4
|
+
[](./tests/fixtures/)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
|
|
7
|
-
This repository contains the official specification for **Token-Oriented Object Notation (TOON)**, a compact, human-readable
|
|
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:**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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:**
|
|
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
|
|
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
|
|
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): [
|
|
298
|
-
- With key: key[
|
|
299
|
-
- Tabular fields: key[
|
|
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 = "["
|
|
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
|
|
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
|
|
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
|
|
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": "
|
|
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:
|
|
72
|
+
"input": "items[1]:\n - name: Ada\n data[0]:",
|
|
73
73
|
"expected": {
|
|
74
74
|
"items": [
|
|
75
|
-
{ "name": "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
142
|
-
"input": "[
|
|
143
|
-
"expected": [],
|
|
144
|
-
"specSection": "9.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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,
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
"specSection": "14.1"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
|
-
"name": "throws
|
|
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
|
|
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,
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
"specSection": "9.4"
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
|
-
"name": "encodes
|
|
58
|
-
"input": [],
|
|
59
|
-
"expected": "[
|
|
60
|
-
"specSection": "9.
|
|
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": "
|
|
30
|
+
"items": [{ "nums": [1, 2, 3], "name": "Ada" }]
|
|
31
31
|
},
|
|
32
|
-
"expected": "items[1]:\n - nums[3]: 1,2,3\n name:
|
|
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": "
|
|
38
|
+
"items": [{ "name": "Ada", "nums": [1, 2, 3] }]
|
|
39
39
|
},
|
|
40
|
-
"expected": "items[1]:\n - name:
|
|
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": "
|
|
93
|
+
{ "name": "Ada", "data": [] }
|
|
94
94
|
]
|
|
95
95
|
},
|
|
96
|
-
"expected": "items[1]:\n - name:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
}
|