@arkadia/data 0.1.7 → 0.1.9
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/.prettierrc +8 -0
- package/README.md +159 -112
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/Decoder.d.ts.map +1 -1
- package/dist/core/Decoder.js +123 -97
- package/dist/core/Decoder.js.map +1 -1
- package/dist/core/Encoder.d.ts +1 -2
- package/dist/core/Encoder.d.ts.map +1 -1
- package/dist/core/Encoder.js +74 -76
- package/dist/core/Encoder.js.map +1 -1
- package/dist/core/Parser.d.ts +1 -1
- package/dist/core/Parser.d.ts.map +1 -1
- package/dist/core/Parser.js +11 -11
- package/dist/core/Parser.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/dist/models/Meta.d.ts +3 -2
- package/dist/models/Meta.d.ts.map +1 -1
- package/dist/models/Meta.js +3 -3
- package/dist/models/Meta.js.map +1 -1
- package/dist/models/Node.d.ts +4 -3
- package/dist/models/Node.d.ts.map +1 -1
- package/dist/models/Node.js +29 -23
- package/dist/models/Node.js.map +1 -1
- package/dist/models/Schema.d.ts.map +1 -1
- package/dist/models/Schema.js +27 -21
- package/dist/models/Schema.js.map +1 -1
- package/eslint.config.mjs +42 -0
- package/package.json +11 -1
- package/scripts/verify-build.js +95 -92
- package/src/config.ts +75 -75
- package/src/core/Decoder.ts +984 -922
- package/src/core/Encoder.ts +364 -371
- package/src/core/Parser.ts +112 -112
- package/src/index.ts +18 -20
- package/src/models/Meta.ts +107 -107
- package/src/models/Node.ts +190 -185
- package/src/models/Schema.ts +198 -193
- package/tests/00.meta.test.ts +19 -25
- package/tests/00.node.test.ts +40 -48
- package/tests/00.primitive.test.ts +121 -95
- package/tests/00.schema.test.ts +28 -35
- package/tests/01.schema.test.ts +42 -52
- package/tests/02.data.test.ts +69 -75
- package/tests/03.errors.test.ts +53 -55
- package/tests/04.list.test.ts +192 -193
- package/tests/05.record.test.ts +54 -56
- package/tests/06.meta.test.ts +393 -389
- package/tests/utils.ts +47 -44
- package/tsconfig.json +27 -29
- package/vitest.config.ts +1 -1
package/tests/03.errors.test.ts
CHANGED
|
@@ -6,66 +6,64 @@ import { encode, decode } from '../src/index';
|
|
|
6
6
|
// ==================================================================================
|
|
7
7
|
|
|
8
8
|
describe('A Data Error Handling', () => {
|
|
9
|
+
it('should report errors on unstripped ANSI codes', () => {
|
|
10
|
+
/**
|
|
11
|
+
* Validates that the Decoder handles ANSI codes gracefully (by reporting errors)
|
|
12
|
+
* if they are not stripped out before decoding.
|
|
13
|
+
*/
|
|
14
|
+
const originalData = [
|
|
15
|
+
{ id: 1, active: true },
|
|
16
|
+
{ id: 2, active: false },
|
|
17
|
+
];
|
|
9
18
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
// 1. Encode WITH colors
|
|
20
|
+
// Note: We use camelCase keys for config in TypeScript
|
|
21
|
+
const encodedText = encode(originalData, {
|
|
22
|
+
compact: true,
|
|
23
|
+
includeSchema: true,
|
|
24
|
+
colorize: true,
|
|
25
|
+
});
|
|
16
26
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
originalData,
|
|
21
|
-
{
|
|
22
|
-
compact: true,
|
|
23
|
-
includeSchema: true,
|
|
24
|
-
colorize: true,
|
|
25
|
-
}
|
|
26
|
-
);
|
|
27
|
+
// 2. Decode WITHOUT stripping colors (simulate user error)
|
|
28
|
+
// Pass removeAnsiColors: false explicitely
|
|
29
|
+
const res = decode(encodedText, { removeAnsiColors: false, debug: false });
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const res = decode(encodedText, { removeAnsiColors: false, debug: false });
|
|
31
|
+
// 3. Assertions
|
|
32
|
+
expect(res.errors.length).toBeGreaterThan(0); // Decoder should report errors on raw ANSI codes
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const hasUnexpectedChar = messages.some(m => m.includes("Unexpected character"));
|
|
38
|
-
expect(hasUnexpectedChar).toBe(true);
|
|
39
|
-
});
|
|
34
|
+
// Optional: Verify error content
|
|
35
|
+
const messages = res.errors.map((e) => e.message);
|
|
36
|
+
const hasUnexpectedChar = messages.some((m) => m.includes('Unexpected character'));
|
|
37
|
+
expect(hasUnexpectedChar).toBe(true);
|
|
38
|
+
});
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
// ==================================================================================
|
|
41
|
+
// 5. PARSING ERROR SCENARIOS
|
|
42
|
+
// ==================================================================================
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
expect(res.errors.length).toBeGreaterThan(0);
|
|
53
|
-
|
|
54
|
-
const msg = res.errors[0].message;
|
|
55
|
-
// Check for typical error messages related to missing tokens or EOF
|
|
56
|
-
const isExpectedError = msg.includes("Expected") || msg.includes("got") || msg.includes("EOF");
|
|
57
|
-
expect(isExpectedError).toBe(true);
|
|
58
|
-
});
|
|
44
|
+
it('should fail on unclosed list', () => {
|
|
45
|
+
/**
|
|
46
|
+
* Ensures parsing fails for malformed lists.
|
|
47
|
+
*/
|
|
48
|
+
const text = '[1, 2, 3'; // Missing closing bracket
|
|
49
|
+
const res = decode(text, { debug: false });
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
51
|
+
expect(res.errors.length).toBeGreaterThan(0);
|
|
52
|
+
|
|
53
|
+
const msg = res.errors[0].message;
|
|
54
|
+
// Check for typical error messages related to missing tokens or EOF
|
|
55
|
+
const isExpectedError = msg.includes('Expected') || msg.includes('got') || msg.includes('EOF');
|
|
56
|
+
expect(isExpectedError).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should fail on unexpected character', () => {
|
|
60
|
+
/**
|
|
61
|
+
* Ensures parsing fails for illegal characters.
|
|
62
|
+
*/
|
|
63
|
+
const text = '(1, ?)';
|
|
64
|
+
const res = decode(text, { debug: false });
|
|
70
65
|
|
|
71
|
-
|
|
66
|
+
expect(res.errors.length).toBeGreaterThan(0);
|
|
67
|
+
expect(res.errors[0].message).toContain('Unexpected character');
|
|
68
|
+
});
|
|
69
|
+
});
|
package/tests/04.list.test.ts
CHANGED
|
@@ -3,223 +3,222 @@ import { encode, decode, parse, SchemaKind } from '../src/index';
|
|
|
3
3
|
import { assertRoundtrip } from './utils';
|
|
4
4
|
|
|
5
5
|
describe('AK List Handling', () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const akdText = `
|
|
6
|
+
it('should decode list of primitives', () => {
|
|
7
|
+
/**
|
|
8
|
+
* Validates simple lists.
|
|
9
|
+
*/
|
|
10
|
+
const text = '[1, 2, 3]';
|
|
11
|
+
const expected = '<[number]>[1,2,3]';
|
|
12
|
+
|
|
13
|
+
const res = decode(text, { debug: false });
|
|
14
|
+
expect(res.errors).toHaveLength(0);
|
|
15
|
+
|
|
16
|
+
const node = res.node;
|
|
17
|
+
// Check elements (Node logic stores simple lists in elements)
|
|
18
|
+
const values = node.elements.map((el) => el.value);
|
|
19
|
+
expect(values).toEqual([1, 2, 3]);
|
|
20
|
+
|
|
21
|
+
assertRoundtrip(node, expected, false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should infer mixed list type from first element', () => {
|
|
25
|
+
/**
|
|
26
|
+
* Tests if the list type is inferred based on the first element ("a" -> string).
|
|
27
|
+
* As a result, the number 3 (int) should be treated as a mismatch and marked with the <number> tag.
|
|
28
|
+
*/
|
|
29
|
+
// 1. Input data: List starts with strings but has an int at the end
|
|
30
|
+
const data = { tests: ['a', 'b', 'c', 3] };
|
|
31
|
+
|
|
32
|
+
// 2. Encoding (inference happens here in Parser.ts)
|
|
33
|
+
const node = parse(data);
|
|
34
|
+
|
|
35
|
+
expect(node.schema).not.toBeNull();
|
|
36
|
+
expect(node.schema.isRecord).toBe(true);
|
|
37
|
+
expect(node.fields).toHaveProperty('tests');
|
|
38
|
+
|
|
39
|
+
const testsNode = node.fields['tests'];
|
|
40
|
+
expect(testsNode.isList).toBe(true);
|
|
41
|
+
expect(testsNode.schema).not.toBeNull();
|
|
42
|
+
expect(testsNode.schema.element).not.toBeNull();
|
|
43
|
+
|
|
44
|
+
expect(testsNode.schema.element?.kind).toBe(SchemaKind.PRIMITIVE);
|
|
45
|
+
expect(testsNode.schema.element?.typeName).toBe('string'); // Inferred from first element "a"
|
|
46
|
+
|
|
47
|
+
expect(testsNode.elements).toHaveLength(4);
|
|
48
|
+
|
|
49
|
+
const output = encode(data, { compact: true, colorize: false });
|
|
50
|
+
|
|
51
|
+
// We expect the list NOT to be [any], but [string] (implied or explicit),
|
|
52
|
+
// so strings will be "clean", and the number will get a tag.
|
|
53
|
+
|
|
54
|
+
// Check if 'a' is treated normally (as a string in a string list)
|
|
55
|
+
expect(output).toContain('"a"');
|
|
56
|
+
|
|
57
|
+
// KEY: Check if 3 got a tag because it doesn't match the inferred String type
|
|
58
|
+
// TS Encoder outputs <number>
|
|
59
|
+
expect(output).toContain('<number> 3');
|
|
60
|
+
|
|
61
|
+
// Ensure there is NO tag next to strings (because they match the list type)
|
|
62
|
+
expect(output).not.toContain('<string> "a"');
|
|
63
|
+
|
|
64
|
+
const expected = '<tests:[string]>(["a","b","c",<number> 3])';
|
|
65
|
+
assertRoundtrip(node, expected, false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should generate tags for explicit any list due to primitive mismatch', () => {
|
|
69
|
+
/**
|
|
70
|
+
* Tests the scenario where a list is defined as [any].
|
|
71
|
+
* * Behavior:
|
|
72
|
+
* 1. 'any' is parsed as a PRIMITIVE type named "any".
|
|
73
|
+
* 2. The Decoder updates the schema based on the first element found ("a" -> string).
|
|
74
|
+
* 3. Because "string" != "number" (for value 3), the Encoder sees a mismatch and adds explicit tags.
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
// 1. Input in AKD format
|
|
78
|
+
const akdText = `
|
|
80
79
|
<tests: [any]>
|
|
81
80
|
(
|
|
82
81
|
["a", "b", "c", 3]
|
|
83
82
|
)
|
|
84
83
|
`;
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
85
|
+
// 2. Decode
|
|
86
|
+
const result = decode(akdText, { debug: false });
|
|
87
|
+
const node = result.node;
|
|
88
|
+
expect(result.errors).toHaveLength(0);
|
|
89
|
+
|
|
90
|
+
// 3. Verify Internal State
|
|
91
|
+
const testsNode = node.fields['tests'];
|
|
92
|
+
|
|
93
|
+
// Verify the list definition
|
|
94
|
+
const elementSchema = testsNode.schema.element;
|
|
95
|
+
expect(elementSchema?.kind).toBe(SchemaKind.PRIMITIVE);
|
|
96
|
+
|
|
97
|
+
// Decoder logic updates "any" element schema based on the first item found
|
|
98
|
+
expect(elementSchema?.typeName).toBe('string');
|
|
99
|
+
|
|
100
|
+
// Verify the actual elements
|
|
101
|
+
expect(testsNode.elements[0].schema.typeName).toBe('string');
|
|
102
|
+
expect(testsNode.elements[3].schema.typeName).toBe('number');
|
|
103
|
+
|
|
104
|
+
// 4. Encode
|
|
105
|
+
const expected = '<tests:[string]>(["a","b","c",<number> 3])';
|
|
106
|
+
assertRoundtrip(node, expected, false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should handle inference happy path', () => {
|
|
110
|
+
/**
|
|
111
|
+
* Theory: If a list has no type (or [any]), the first element ("a")
|
|
112
|
+
* should refine the list schema to [string].
|
|
113
|
+
*/
|
|
114
|
+
const dataStr = '["a", "b"]'; // No header = SchemaKind.ANY
|
|
115
|
+
|
|
116
|
+
const result = decode(dataStr, { debug: false });
|
|
117
|
+
const node = result.node;
|
|
118
|
+
expect(result.errors).toHaveLength(0);
|
|
119
|
+
|
|
120
|
+
expect(node.isList).toBe(true);
|
|
121
|
+
expect(node.schema.element?.typeName).toBe('string'); // Inferred!
|
|
122
|
+
|
|
123
|
+
const expected = '<[string]>["a","b"]';
|
|
124
|
+
assertRoundtrip(node, expected, false);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should handle inference mismatch (string vs number)', () => {
|
|
128
|
+
/**
|
|
129
|
+
* Theory: First element "a" sets list to [string].
|
|
130
|
+
* The number 3 is mismatch and gets tagged.
|
|
131
|
+
*/
|
|
132
|
+
const dataStr = '["a", 3]';
|
|
133
|
+
|
|
134
|
+
const result = decode(dataStr, { debug: false });
|
|
135
|
+
const node = result.node;
|
|
136
|
+
expect(result.errors).toHaveLength(0);
|
|
137
|
+
|
|
138
|
+
// The list should have become [string] due to "a"
|
|
139
|
+
expect(node.schema.element?.typeName).toBe('string');
|
|
140
|
+
|
|
141
|
+
const expected = '<[string]>["a",<number> 3]';
|
|
142
|
+
assertRoundtrip(node, expected, false);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should handle inference mismatch (number vs string)', () => {
|
|
146
|
+
/**
|
|
147
|
+
* Theory: First element 3 sets list to [number].
|
|
148
|
+
* The string "a" is mismatch and gets tagged.
|
|
149
|
+
*/
|
|
150
|
+
const dataStr = '[3, "a"]';
|
|
151
|
+
|
|
152
|
+
const result = decode(dataStr, { debug: false });
|
|
153
|
+
const node = result.node;
|
|
154
|
+
expect(result.errors).toHaveLength(0);
|
|
155
|
+
|
|
156
|
+
// The list should have become [number] due to 3
|
|
157
|
+
expect(node.schema.element?.typeName).toBe('number');
|
|
158
|
+
|
|
159
|
+
const expected = '<[number]>[3,<string> "a"]';
|
|
160
|
+
assertRoundtrip(node, expected, false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should fix schema crash on override logic', () => {
|
|
164
|
+
/**
|
|
165
|
+
* Theory: We expect a String, but we get a List.
|
|
166
|
+
* This triggers 'needs_override' logic in the Encoder.
|
|
167
|
+
*/
|
|
168
|
+
// Header says 'test' is a string, but body has a list
|
|
169
|
+
const akdText = `
|
|
171
170
|
<test: string>
|
|
172
171
|
(
|
|
173
172
|
["a", "b"]
|
|
174
173
|
)
|
|
175
174
|
`;
|
|
176
175
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
const result = decode(akdText, { debug: false });
|
|
177
|
+
const node = result.node;
|
|
178
|
+
expect(result.errors).toHaveLength(0);
|
|
180
179
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
// The Encoder sees a List Node vs String Schema -> Mismatch -> Adds Tag
|
|
181
|
+
const expected = '<test:string>(<[string]> ["a","b"])';
|
|
182
|
+
assertRoundtrip(node, expected, false);
|
|
183
|
+
});
|
|
185
184
|
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
it('should handle primitive list with implicit output', () => {
|
|
186
|
+
const akdText = `
|
|
188
187
|
<ab>
|
|
189
188
|
{
|
|
190
189
|
ab: ["a", "b", "c", 3]
|
|
191
190
|
}
|
|
192
191
|
`;
|
|
193
192
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
const result = decode(akdText, { debug: false });
|
|
194
|
+
const node = result.node;
|
|
195
|
+
expect(result.errors).toHaveLength(0);
|
|
197
196
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
const expected = '<ab:[string]>(["a","b","c",<number> 3])';
|
|
198
|
+
assertRoundtrip(node, expected, false);
|
|
199
|
+
});
|
|
201
200
|
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
it('should handle simple mixed types', () => {
|
|
202
|
+
const akdText = '["a", "b", "c", 3]';
|
|
204
203
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
204
|
+
const result = decode(akdText, { debug: false });
|
|
205
|
+
const node = result.node;
|
|
206
|
+
expect(result.errors).toHaveLength(0);
|
|
208
207
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
const expected = '<[string]>["a","b","c",<number> 3]';
|
|
209
|
+
assertRoundtrip(node, expected, false);
|
|
210
|
+
});
|
|
212
211
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
212
|
+
it('should handle inner list types', () => {
|
|
213
|
+
// <[[int]]>[[2,3,4],[5,6,7]]
|
|
214
|
+
const akdText = '<[[int]]>[[2,3,4],[5,6,7]]';
|
|
216
215
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
const result = decode(akdText, { debug: false });
|
|
217
|
+
const node = result.node;
|
|
218
|
+
expect(result.errors).toHaveLength(0);
|
|
220
219
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
});
|
|
220
|
+
// 'int' normalizes to 'number' in the TS implementation
|
|
221
|
+
const expected = '<[[number]]>[[2,3,4],[5,6,7]]';
|
|
222
|
+
assertRoundtrip(node, expected, false);
|
|
223
|
+
});
|
|
224
|
+
});
|