@toon-format/spec 1.3.3 → 1.5.0

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +15 -3
  3. package/SPEC.md +435 -102
  4. package/VERSIONING.md +1 -14
  5. package/package.json +1 -1
  6. package/tests/README.md +42 -28
  7. package/tests/fixtures/decode/arrays-nested.json +20 -20
  8. package/tests/fixtures/decode/arrays-primitive.json +14 -14
  9. package/tests/fixtures/decode/arrays-tabular.json +28 -5
  10. package/tests/fixtures/decode/blank-lines.json +14 -14
  11. package/tests/fixtures/decode/delimiters.json +46 -29
  12. package/tests/fixtures/decode/indentation-errors.json +16 -29
  13. package/tests/fixtures/decode/numbers.json +142 -0
  14. package/tests/fixtures/decode/objects.json +29 -29
  15. package/tests/fixtures/decode/path-expansion.json +173 -0
  16. package/tests/fixtures/decode/primitives.json +44 -75
  17. package/tests/fixtures/decode/root-form.json +17 -0
  18. package/tests/fixtures/decode/validation-errors.json +29 -9
  19. package/tests/fixtures/decode/whitespace.json +61 -0
  20. package/tests/fixtures/encode/arrays-nested.json +15 -15
  21. package/tests/fixtures/encode/arrays-objects.json +16 -16
  22. package/tests/fixtures/encode/arrays-primitive.json +14 -14
  23. package/tests/fixtures/encode/arrays-tabular.json +8 -8
  24. package/tests/fixtures/encode/delimiters.json +23 -23
  25. package/tests/fixtures/encode/key-folding.json +218 -0
  26. package/tests/fixtures/encode/objects.json +27 -27
  27. package/tests/fixtures/encode/options.json +1 -1
  28. package/tests/fixtures/encode/primitives.json +61 -36
  29. package/tests/fixtures/encode/whitespace.json +18 -3
  30. package/tests/fixtures.schema.json +20 -4
  31. package/tests/fixtures/encode/normalization.json +0 -107
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3",
2
+ "version": "1.4",
3
3
  "category": "encode",
4
4
  "description": "Primitive value encoding - strings, numbers, booleans, null",
5
5
  "tests": [
@@ -7,220 +7,245 @@
7
7
  "name": "encodes safe strings without quotes",
8
8
  "input": "hello",
9
9
  "expected": "hello",
10
- "specSection": "5"
10
+ "specSection": "7.2"
11
11
  },
12
12
  {
13
13
  "name": "encodes safe string with underscore and numbers",
14
14
  "input": "Ada_99",
15
15
  "expected": "Ada_99",
16
- "specSection": "5"
16
+ "specSection": "7.2"
17
17
  },
18
18
  {
19
19
  "name": "quotes empty string",
20
20
  "input": "",
21
21
  "expected": "\"\"",
22
- "specSection": "5"
22
+ "specSection": "7.2"
23
23
  },
24
24
  {
25
25
  "name": "quotes string that looks like true",
26
26
  "input": "true",
27
27
  "expected": "\"true\"",
28
- "specSection": "5",
28
+ "specSection": "7.2",
29
29
  "note": "String representation of boolean must be quoted"
30
30
  },
31
31
  {
32
32
  "name": "quotes string that looks like false",
33
33
  "input": "false",
34
34
  "expected": "\"false\"",
35
- "specSection": "5"
35
+ "specSection": "7.2"
36
36
  },
37
37
  {
38
38
  "name": "quotes string that looks like null",
39
39
  "input": "null",
40
40
  "expected": "\"null\"",
41
- "specSection": "5"
41
+ "specSection": "7.2"
42
42
  },
43
43
  {
44
44
  "name": "quotes string that looks like integer",
45
45
  "input": "42",
46
46
  "expected": "\"42\"",
47
- "specSection": "5"
47
+ "specSection": "7.2"
48
48
  },
49
49
  {
50
50
  "name": "quotes string that looks like negative decimal",
51
51
  "input": "-3.14",
52
52
  "expected": "\"-3.14\"",
53
- "specSection": "5"
53
+ "specSection": "7.2"
54
54
  },
55
55
  {
56
56
  "name": "quotes string that looks like scientific notation",
57
57
  "input": "1e-6",
58
58
  "expected": "\"1e-6\"",
59
- "specSection": "5"
59
+ "specSection": "7.2"
60
60
  },
61
61
  {
62
62
  "name": "quotes string with leading zero",
63
63
  "input": "05",
64
64
  "expected": "\"05\"",
65
- "specSection": "5",
65
+ "specSection": "7.2",
66
66
  "note": "Leading zeros make it non-numeric"
67
67
  },
68
68
  {
69
69
  "name": "escapes newline in string",
70
70
  "input": "line1\nline2",
71
71
  "expected": "\"line1\\nline2\"",
72
- "specSection": "5"
72
+ "specSection": "7.1"
73
73
  },
74
74
  {
75
75
  "name": "escapes tab in string",
76
76
  "input": "tab\there",
77
77
  "expected": "\"tab\\there\"",
78
- "specSection": "5"
78
+ "specSection": "7.1"
79
79
  },
80
80
  {
81
81
  "name": "escapes carriage return in string",
82
82
  "input": "return\rcarriage",
83
83
  "expected": "\"return\\rcarriage\"",
84
- "specSection": "5"
84
+ "specSection": "7.1"
85
85
  },
86
86
  {
87
87
  "name": "escapes backslash in string",
88
88
  "input": "C:\\Users\\path",
89
89
  "expected": "\"C:\\\\Users\\\\path\"",
90
- "specSection": "5"
90
+ "specSection": "7.1"
91
91
  },
92
92
  {
93
93
  "name": "quotes string with array-like syntax",
94
94
  "input": "[3]: x,y",
95
95
  "expected": "\"[3]: x,y\"",
96
- "specSection": "5",
96
+ "specSection": "7.2",
97
97
  "note": "Looks like array header"
98
98
  },
99
99
  {
100
100
  "name": "quotes string starting with hyphen-space",
101
101
  "input": "- item",
102
102
  "expected": "\"- item\"",
103
- "specSection": "5",
103
+ "specSection": "7.2",
104
104
  "note": "Looks like list item marker"
105
105
  },
106
+ {
107
+ "name": "quotes single hyphen as object value",
108
+ "input": { "marker": "-" },
109
+ "expected": "marker: \"-\"",
110
+ "specSection": "7.2",
111
+ "note": "Single hyphen must be quoted to avoid list item ambiguity"
112
+ },
113
+ {
114
+ "name": "quotes string starting with hyphen as object value",
115
+ "input": { "note": "- item" },
116
+ "expected": "note: \"- item\"",
117
+ "specSection": "7.2"
118
+ },
119
+ {
120
+ "name": "quotes single hyphen in array",
121
+ "input": { "items": ["-"] },
122
+ "expected": "items[1]: \"-\"",
123
+ "specSection": "7.2"
124
+ },
125
+ {
126
+ "name": "quotes leading-hyphen string in array",
127
+ "input": { "tags": ["a", "- item", "b"] },
128
+ "expected": "tags[3]: a,\"- item\",b",
129
+ "specSection": "7.2"
130
+ },
106
131
  {
107
132
  "name": "quotes string with bracket notation",
108
133
  "input": "[test]",
109
134
  "expected": "\"[test]\"",
110
- "specSection": "5"
135
+ "specSection": "7.2"
111
136
  },
112
137
  {
113
138
  "name": "quotes string with brace notation",
114
139
  "input": "{key}",
115
140
  "expected": "\"{key}\"",
116
- "specSection": "5"
141
+ "specSection": "7.2"
117
142
  },
118
143
  {
119
144
  "name": "encodes Unicode string without quotes",
120
145
  "input": "café",
121
146
  "expected": "café",
122
- "specSection": "5"
147
+ "specSection": "7.2"
123
148
  },
124
149
  {
125
150
  "name": "encodes Chinese characters without quotes",
126
151
  "input": "你好",
127
152
  "expected": "你好",
128
- "specSection": "5"
153
+ "specSection": "7.2"
129
154
  },
130
155
  {
131
156
  "name": "encodes emoji without quotes",
132
157
  "input": "🚀",
133
158
  "expected": "🚀",
134
- "specSection": "5"
159
+ "specSection": "7.2"
135
160
  },
136
161
  {
137
162
  "name": "encodes string with emoji and spaces",
138
163
  "input": "hello 👋 world",
139
164
  "expected": "hello 👋 world",
140
- "specSection": "5"
165
+ "specSection": "7.2"
141
166
  },
142
167
  {
143
168
  "name": "encodes positive integer",
144
169
  "input": 42,
145
170
  "expected": "42",
146
- "specSection": "5"
171
+ "specSection": "2"
147
172
  },
148
173
  {
149
174
  "name": "encodes decimal number",
150
175
  "input": 3.14,
151
176
  "expected": "3.14",
152
- "specSection": "5"
177
+ "specSection": "2"
153
178
  },
154
179
  {
155
180
  "name": "encodes negative integer",
156
181
  "input": -7,
157
182
  "expected": "-7",
158
- "specSection": "5"
183
+ "specSection": "2"
159
184
  },
160
185
  {
161
186
  "name": "encodes zero",
162
187
  "input": 0,
163
188
  "expected": "0",
164
- "specSection": "5"
189
+ "specSection": "2"
165
190
  },
166
191
  {
167
192
  "name": "encodes negative zero as zero",
168
193
  "input": -0,
169
194
  "expected": "0",
170
- "specSection": "5",
195
+ "specSection": "2",
171
196
  "note": "Negative zero normalizes to zero"
172
197
  },
173
198
  {
174
199
  "name": "encodes scientific notation as decimal",
175
200
  "input": 1000000,
176
201
  "expected": "1000000",
177
- "specSection": "5",
202
+ "specSection": "2",
178
203
  "note": "1e6 input, but represented as decimal"
179
204
  },
180
205
  {
181
206
  "name": "encodes small decimal from scientific notation",
182
207
  "input": 0.000001,
183
208
  "expected": "0.000001",
184
- "specSection": "5",
209
+ "specSection": "2",
185
210
  "note": "1e-6 input"
186
211
  },
187
212
  {
188
213
  "name": "encodes large number",
189
214
  "input": 100000000000000000000,
190
215
  "expected": "100000000000000000000",
191
- "specSection": "5",
216
+ "specSection": "2",
192
217
  "note": "1e20"
193
218
  },
194
219
  {
195
220
  "name": "encodes MAX_SAFE_INTEGER",
196
221
  "input": 9007199254740991,
197
222
  "expected": "9007199254740991",
198
- "specSection": "5"
223
+ "specSection": "2"
199
224
  },
200
225
  {
201
226
  "name": "encodes repeating decimal with full precision",
202
227
  "input": 0.3333333333333333,
203
228
  "expected": "0.3333333333333333",
204
- "specSection": "5",
229
+ "specSection": "2",
205
230
  "note": "Result of 1/3 in JavaScript"
206
231
  },
207
232
  {
208
233
  "name": "encodes true",
209
234
  "input": true,
210
235
  "expected": "true",
211
- "specSection": "5"
236
+ "specSection": "2"
212
237
  },
213
238
  {
214
239
  "name": "encodes false",
215
240
  "input": false,
216
241
  "expected": "false",
217
- "specSection": "5"
242
+ "specSection": "2"
218
243
  },
219
244
  {
220
245
  "name": "encodes null",
221
246
  "input": null,
222
247
  "expected": "null",
223
- "specSection": "5"
248
+ "specSection": "2"
224
249
  }
225
250
  ]
226
251
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3",
2
+ "version": "1.4",
3
3
  "category": "encode",
4
4
  "description": "Whitespace and formatting invariants - no trailing spaces, no trailing newlines",
5
5
  "tests": [
@@ -9,7 +9,7 @@
9
9
  "id": 123
10
10
  },
11
11
  "expected": "id: 123",
12
- "specSection": "4",
12
+ "specSection": "12",
13
13
  "note": "Output should not end with newline character"
14
14
  },
15
15
  {
@@ -22,8 +22,23 @@
22
22
  "items": ["a", "b"]
23
23
  },
24
24
  "expected": "user:\n id: 123\n name: Ada\nitems[2]: a,b",
25
- "specSection": "4",
25
+ "specSection": "12",
26
26
  "note": "2-space indentation, no trailing spaces on any line"
27
+ },
28
+ {
29
+ "name": "respects custom indent size option",
30
+ "input": {
31
+ "user": {
32
+ "name": "Ada",
33
+ "role": "admin"
34
+ }
35
+ },
36
+ "expected": "user:\n name: Ada\n role: admin",
37
+ "specSection": "12",
38
+ "options": {
39
+ "indent": 4
40
+ },
41
+ "note": "4-space indentation for nested objects when indent option is set to 4"
27
42
  }
28
43
  ]
29
44
  }
@@ -69,14 +69,30 @@
69
69
  },
70
70
  "lengthMarker": {
71
71
  "type": "string",
72
- "enum": ["#", ""],
73
- "description": "Optional marker to prefix array lengths (encode only)",
74
- "default": ""
72
+ "const": "#",
73
+ "description": "Optional marker to prefix array lengths (encode only). Omit to disable the marker."
75
74
  },
76
75
  "strict": {
77
76
  "type": "boolean",
78
77
  "description": "Enable strict validation (decode only)",
79
78
  "default": true
79
+ },
80
+ "keyFolding": {
81
+ "type": "string",
82
+ "enum": ["off", "safe"],
83
+ "description": "Key folding mode for encoders (v1.5+): 'off' (default) or 'safe' (encode only)",
84
+ "default": "off"
85
+ },
86
+ "flattenDepth": {
87
+ "type": "integer",
88
+ "description": "Maximum depth to fold key chains when keyFolding is 'safe' (v1.5+). Values less than 2 have no practical folding effect (encode only)",
89
+ "minimum": 0
90
+ },
91
+ "expandPaths": {
92
+ "type": "string",
93
+ "enum": ["off", "safe"],
94
+ "description": "Path expansion mode for decoders (v1.5+): 'off' (default) or 'safe' (decode only)",
95
+ "default": "off"
80
96
  }
81
97
  },
82
98
  "additionalProperties": false
@@ -95,7 +111,7 @@
95
111
  "type": "string",
96
112
  "description": "Minimum specification version required for this test",
97
113
  "pattern": "^\\d+\\.\\d+$",
98
- "examples": ["1.0", "1.3"]
114
+ "examples": ["1.0", "1.4"]
99
115
  }
100
116
  },
101
117
  "additionalProperties": false
@@ -1,107 +0,0 @@
1
- {
2
- "version": "1.3",
3
- "category": "encode",
4
- "description": "Non-JSON type normalization - BigInt, Date, undefined, NaN, Infinity, functions, symbols",
5
- "tests": [
6
- {
7
- "name": "converts BigInt to number",
8
- "input": 123,
9
- "expected": "123",
10
- "specSection": "5",
11
- "note": "BigInt(123) in JavaScript becomes 123"
12
- },
13
- {
14
- "name": "converts BigInt in object to number",
15
- "input": {
16
- "id": 456
17
- },
18
- "expected": "id: 456",
19
- "specSection": "5",
20
- "note": "BigInt(456) in JavaScript becomes 456"
21
- },
22
- {
23
- "name": "converts Date to ISO string",
24
- "input": "2025-01-01T00:00:00.000Z",
25
- "expected": "\"2025-01-01T00:00:00.000Z\"",
26
- "specSection": "5",
27
- "note": "new Date('2025-01-01T00:00:00.000Z') becomes quoted ISO string"
28
- },
29
- {
30
- "name": "converts Date in object to ISO string",
31
- "input": {
32
- "created": "2025-01-01T00:00:00.000Z"
33
- },
34
- "expected": "created: \"2025-01-01T00:00:00.000Z\"",
35
- "specSection": "5"
36
- },
37
- {
38
- "name": "converts undefined to null",
39
- "input": null,
40
- "expected": "null",
41
- "specSection": "5",
42
- "note": "undefined in JavaScript becomes null"
43
- },
44
- {
45
- "name": "converts undefined in object to null",
46
- "input": {
47
- "value": null
48
- },
49
- "expected": "value: null",
50
- "specSection": "5",
51
- "note": "undefined in JavaScript becomes null"
52
- },
53
- {
54
- "name": "converts Infinity to null",
55
- "input": null,
56
- "expected": "null",
57
- "specSection": "5",
58
- "note": "Infinity becomes null"
59
- },
60
- {
61
- "name": "converts negative Infinity to null",
62
- "input": null,
63
- "expected": "null",
64
- "specSection": "5",
65
- "note": "-Infinity becomes null"
66
- },
67
- {
68
- "name": "converts NaN to null",
69
- "input": null,
70
- "expected": "null",
71
- "specSection": "5",
72
- "note": "Number.NaN becomes null"
73
- },
74
- {
75
- "name": "converts function to null",
76
- "input": null,
77
- "expected": "null",
78
- "specSection": "5",
79
- "note": "Functions become null"
80
- },
81
- {
82
- "name": "converts function in object to null",
83
- "input": {
84
- "fn": null
85
- },
86
- "expected": "fn: null",
87
- "specSection": "5",
88
- "note": "Functions become null"
89
- },
90
- {
91
- "name": "converts symbol to null",
92
- "input": null,
93
- "expected": "null",
94
- "specSection": "5",
95
- "note": "Symbols become null"
96
- },
97
- {
98
- "name": "converts symbol in object to null",
99
- "input": {
100
- "sym": null
101
- },
102
- "expected": "sym: null",
103
- "specSection": "5",
104
- "note": "Symbols become null"
105
- }
106
- ]
107
- }