@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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import {
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { decode, encode } from '../src/index';
|
|
3
3
|
import { assertRoundtrip } from './utils';
|
|
4
4
|
|
|
5
5
|
// ==================================================================================
|
|
@@ -7,102 +7,128 @@ import { assertRoundtrip } from './utils';
|
|
|
7
7
|
// ==================================================================================
|
|
8
8
|
|
|
9
9
|
describe('AK Data Primitives', () => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
it('should encode raw object', () => {
|
|
11
|
+
const input = { foo: 'bar' };
|
|
12
|
+
const expected = '<foo:string>("bar")';
|
|
13
|
+
assertRoundtrip(input, expected);
|
|
14
|
+
});
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
expect(res.errors, `Parsing failed for input: ${text}`).toHaveLength(0);
|
|
31
|
-
expect(res.node.value).toBe(expectedVal);
|
|
32
|
-
expect(res.node.isPrimitive).toBe(true);
|
|
33
|
-
|
|
34
|
-
// 2. ENCODE
|
|
35
|
-
const encodedStr = encode(res.node, { colorize: false, compact: true }).trim();
|
|
36
|
-
|
|
37
|
-
expect(encodedStr).toBe(expectedEnc);
|
|
38
|
-
});
|
|
16
|
+
it.each([
|
|
17
|
+
// [Input Text, Expected Value, Expected Encoded Output]
|
|
18
|
+
['123', 123, '<number>123'],
|
|
19
|
+
['-50', -50, '<number>-50'],
|
|
20
|
+
['"hello"', 'hello', '<string>"hello"'],
|
|
21
|
+
['"hello world"', 'hello world', '<string>"hello world"'],
|
|
22
|
+
['true', true, '<bool>true'],
|
|
23
|
+
['false', false, '<bool>false'],
|
|
24
|
+
['null', null, '<null>null'],
|
|
25
|
+
])('should decode and encode primitive: %s', (text, expectedVal, expectedEnc) => {
|
|
26
|
+
// 1. DECODE
|
|
27
|
+
// We pass an options object (assuming the latest signature: decode(text, { debug: false }))
|
|
28
|
+
const res = decode(text, { debug: false });
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
["-0.005", -0.005],
|
|
44
|
-
["0.0", 0.0]
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
cases.forEach(([text, expected]) => {
|
|
48
|
-
const res = decode(text);
|
|
49
|
-
expect(res.errors).toHaveLength(0);
|
|
50
|
-
expect(res.node.value).toBe(expected);
|
|
51
|
-
expect(typeof res.node.value).toBe('number');
|
|
52
|
-
});
|
|
53
|
-
});
|
|
30
|
+
expect(res.errors, `Parsing failed for input: ${text}`).toHaveLength(0);
|
|
31
|
+
expect(res.node.value).toBe(expectedVal);
|
|
32
|
+
expect(res.node.isPrimitive).toBe(true);
|
|
54
33
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const res = decode(text, { debug: false });
|
|
58
|
-
expect(res.errors).toHaveLength(0);
|
|
59
|
-
|
|
60
|
-
const node = res.node;
|
|
61
|
-
|
|
62
|
-
expect(node.isRecord).toBe(true);
|
|
63
|
-
expect(node.fields["id"].value).toBe(1);
|
|
64
|
-
expect(node.fields["name"].value).toBe("Test");
|
|
65
|
-
|
|
66
|
-
assertRoundtrip(
|
|
67
|
-
node,
|
|
68
|
-
'<id:number,name:string>(1,"Test")',
|
|
69
|
-
false
|
|
70
|
-
);
|
|
71
|
-
});
|
|
34
|
+
// 2. ENCODE
|
|
35
|
+
const encodedStr = encode(res.node, { colorize: false, compact: true }).trim();
|
|
72
36
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// Verify if the parser mapped positional fields to _0, _1
|
|
83
|
-
expect(node.fields["_0"].value).toBe(10);
|
|
84
|
-
expect(node.fields["_1"].value).toBe("Alice");
|
|
85
|
-
|
|
86
|
-
assertRoundtrip(
|
|
87
|
-
node,
|
|
88
|
-
'<_0:number,_1:string>(10,"Alice")',
|
|
89
|
-
false
|
|
90
|
-
);
|
|
91
|
-
});
|
|
37
|
+
expect(encodedStr).toBe(expectedEnc);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should decode floats', () => {
|
|
41
|
+
const cases: [string, number][] = [
|
|
42
|
+
['12.34', 12.34],
|
|
43
|
+
['-0.005', -0.005],
|
|
44
|
+
['0.0', 0.0],
|
|
45
|
+
];
|
|
92
46
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const node = res.node;
|
|
99
|
-
expect(node.fields["color"].value).toBe("red");
|
|
100
|
-
expect(node.fields["status"].value).toBe("active");
|
|
101
|
-
|
|
102
|
-
assertRoundtrip(
|
|
103
|
-
node,
|
|
104
|
-
'<color:string,status:string>("red","active")',
|
|
105
|
-
false
|
|
106
|
-
);
|
|
47
|
+
cases.forEach(([text, expected]) => {
|
|
48
|
+
const res = decode(text);
|
|
49
|
+
expect(res.errors).toHaveLength(0);
|
|
50
|
+
expect(res.node.value).toBe(expected);
|
|
51
|
+
expect(typeof res.node.value).toBe('number');
|
|
107
52
|
});
|
|
108
|
-
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should decode named record', () => {
|
|
56
|
+
const text = '{id: 1, name: "Test"}';
|
|
57
|
+
const res = decode(text, { debug: false });
|
|
58
|
+
expect(res.errors).toHaveLength(0);
|
|
59
|
+
|
|
60
|
+
const node = res.node;
|
|
61
|
+
|
|
62
|
+
expect(node.isRecord).toBe(true);
|
|
63
|
+
expect(node.fields['id'].value).toBe(1);
|
|
64
|
+
expect(node.fields['name'].value).toBe('Test');
|
|
65
|
+
|
|
66
|
+
assertRoundtrip(node, '<id:number,name:string>(1,"Test")', false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should decode positional record', () => {
|
|
70
|
+
const text = '(10, "Alice")';
|
|
71
|
+
const res = decode(text, { debug: false });
|
|
72
|
+
expect(res.errors).toHaveLength(0);
|
|
73
|
+
|
|
74
|
+
const node = res.node;
|
|
75
|
+
|
|
76
|
+
expect(node.isRecord).toBe(true);
|
|
77
|
+
|
|
78
|
+
// Verify if the parser mapped positional fields to _0, _1
|
|
79
|
+
expect(node.fields['_0'].value).toBe(10);
|
|
80
|
+
expect(node.fields['_1'].value).toBe('Alice');
|
|
81
|
+
|
|
82
|
+
assertRoundtrip(node, '<_0:number,_1:string>(10,"Alice")', false);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should decode raw string', () => {
|
|
86
|
+
const text = '{color: red, status: active}';
|
|
87
|
+
const res = decode(text, { debug: false });
|
|
88
|
+
expect(res.errors).toHaveLength(0);
|
|
89
|
+
|
|
90
|
+
const node = res.node;
|
|
91
|
+
expect(node.fields['color'].value).toBe('red');
|
|
92
|
+
expect(node.fields['status'].value).toBe('active');
|
|
93
|
+
|
|
94
|
+
assertRoundtrip(node, '<color:string,status:string>("red","active")', false);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('String Escaping', () => {
|
|
99
|
+
it('should decode string with escaped quotes', () => {
|
|
100
|
+
// Input: { text: "say \"hello\"" }
|
|
101
|
+
// In-memory value: say "hello"
|
|
102
|
+
// Note: In JS string literals, we need extra backslashes to represent a literal backslash.
|
|
103
|
+
const text = '{ text: "say \\"hello\\"" }';
|
|
104
|
+
|
|
105
|
+
const res = decode(text, { debug: false });
|
|
106
|
+
expect(res.errors).toHaveLength(0);
|
|
107
|
+
|
|
108
|
+
const node = res.node;
|
|
109
|
+
// Check in-memory value
|
|
110
|
+
expect(node.fields['text'].value).toBe('say "hello"');
|
|
111
|
+
|
|
112
|
+
// Roundtrip check:
|
|
113
|
+
// Encoder must add backslashes before quotes: "say \"hello\""
|
|
114
|
+
assertRoundtrip(node, '<text:string>("say \\"hello\\"")', false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should decode string with escaped backslashes', () => {
|
|
118
|
+
// Input: { path: "C:\\Program Files" }
|
|
119
|
+
// In-memory value: C:\Program Files
|
|
120
|
+
// Note: "C:\\\\Program Files" in JS literal becomes C:\\Program Files in the string
|
|
121
|
+
const text = '{ path: "C:\\\\Program Files" }';
|
|
122
|
+
|
|
123
|
+
const res = decode(text, { debug: false });
|
|
124
|
+
expect(res.errors).toHaveLength(0);
|
|
125
|
+
|
|
126
|
+
const node = res.node;
|
|
127
|
+
// Check in-memory value (single backslash)
|
|
128
|
+
expect(node.fields['path'].value).toBe('C:\\Program Files');
|
|
129
|
+
|
|
130
|
+
// Roundtrip check:
|
|
131
|
+
// Encoder must escape the backslash: "C:\\Program Files"
|
|
132
|
+
assertRoundtrip(node, '<path:string>("C:\\\\Program Files")', false);
|
|
133
|
+
});
|
|
134
|
+
});
|
package/tests/00.schema.test.ts
CHANGED
|
@@ -2,40 +2,33 @@ import { describe, it, expect } from 'vitest';
|
|
|
2
2
|
import { Schema, SchemaKind } from '../src/index';
|
|
3
3
|
|
|
4
4
|
describe('AI Schema test', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
tags: ["tag1", "tag2"],
|
|
13
|
-
required: true
|
|
14
|
-
|
|
15
|
-
});
|
|
16
|
-
const expected = '<Schema(DICT) name="TestSchema" !required attr=["foo"] tags=[tag1, tag2] comments=1>';
|
|
17
|
-
const expected_val = {
|
|
18
|
-
"comments": [
|
|
19
|
-
"This is a comment"
|
|
20
|
-
],
|
|
21
|
-
"attr": {
|
|
22
|
-
"foo": "bar"
|
|
23
|
-
},
|
|
24
|
-
"tags": [
|
|
25
|
-
"tag1",
|
|
26
|
-
"tag2"
|
|
27
|
-
],
|
|
28
|
-
"_fieldsList": [],
|
|
29
|
-
"_fieldsMap": {},
|
|
30
|
-
"kind": "DICT",
|
|
31
|
-
"typeName": "any",
|
|
32
|
-
"name": "TestSchema",
|
|
33
|
-
"element": null,
|
|
34
|
-
"key": null,
|
|
35
|
-
"value": null,
|
|
36
|
-
"required": true
|
|
37
|
-
}
|
|
38
|
-
expect(schema).toMatchObject(expected_val);
|
|
39
|
-
expect(schema.toString()).toBe(expected);
|
|
5
|
+
it('should encode raw object', () => {
|
|
6
|
+
const schema = new Schema(SchemaKind.DICT, {
|
|
7
|
+
name: 'TestSchema',
|
|
8
|
+
comments: ['This is a comment'],
|
|
9
|
+
attr: { foo: 'bar' },
|
|
10
|
+
tags: ['tag1', 'tag2'],
|
|
11
|
+
required: true,
|
|
40
12
|
});
|
|
13
|
+
const expected =
|
|
14
|
+
'<Schema(DICT) name="TestSchema" !required attr=["foo"] tags=[tag1, tag2] comments=1>';
|
|
15
|
+
const expected_val = {
|
|
16
|
+
comments: ['This is a comment'],
|
|
17
|
+
attr: {
|
|
18
|
+
foo: 'bar',
|
|
19
|
+
},
|
|
20
|
+
tags: ['tag1', 'tag2'],
|
|
21
|
+
_fieldsList: [],
|
|
22
|
+
_fieldsMap: {},
|
|
23
|
+
kind: 'DICT',
|
|
24
|
+
typeName: 'any',
|
|
25
|
+
name: 'TestSchema',
|
|
26
|
+
element: null,
|
|
27
|
+
key: null,
|
|
28
|
+
value: null,
|
|
29
|
+
required: true,
|
|
30
|
+
};
|
|
31
|
+
expect(schema).toMatchObject(expected_val);
|
|
32
|
+
expect(schema.toString()).toBe(expected);
|
|
33
|
+
});
|
|
41
34
|
});
|
package/tests/01.schema.test.ts
CHANGED
|
@@ -7,64 +7,54 @@ import { assertRoundtrip } from './utils';
|
|
|
7
7
|
// ==================================================================================
|
|
8
8
|
|
|
9
9
|
describe('AK Data Schema Definitions', () => {
|
|
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
|
-
* Validates nested structural types.
|
|
42
|
-
*/
|
|
43
|
-
// We define Profile, then User uses it.
|
|
44
|
-
const text = `
|
|
10
|
+
it('should handle schema definition and usage', () => {
|
|
11
|
+
/**
|
|
12
|
+
* Validates that a type defined with @Type<...> is correctly applied
|
|
13
|
+
* to the following value.
|
|
14
|
+
*/
|
|
15
|
+
// Define schema first, then use it explicitly
|
|
16
|
+
const fullText = '@User<id:int, name:string> @User(1, "Admin")';
|
|
17
|
+
const res = decode(fullText, { debug: false });
|
|
18
|
+
|
|
19
|
+
expect(res.errors).toHaveLength(0);
|
|
20
|
+
|
|
21
|
+
const node = res.node;
|
|
22
|
+
// Check if schema is linked correctly
|
|
23
|
+
expect(node.schema).not.toBeNull();
|
|
24
|
+
expect(node.schema?.typeName).toBe('User');
|
|
25
|
+
|
|
26
|
+
// Since we have a schema, positional arguments should be mapped to fields
|
|
27
|
+
// Check by key (Decoder maps positional to fields if schema exists)
|
|
28
|
+
expect(node.fields['id'].value).toBe(1);
|
|
29
|
+
expect(node.fields['name'].value).toBe('Admin');
|
|
30
|
+
|
|
31
|
+
assertRoundtrip(node, '@User<id:number,name:string>(1,"Admin")', false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should handle nested schema structure', () => {
|
|
35
|
+
/**
|
|
36
|
+
* Validates nested structural types.
|
|
37
|
+
*/
|
|
38
|
+
// We define Profile, then User uses it.
|
|
39
|
+
const text = `
|
|
45
40
|
@Profile<level:int>
|
|
46
41
|
@User<id:int, profile: @Profile>
|
|
47
42
|
@User(1, {level: 99})
|
|
48
43
|
`;
|
|
49
|
-
|
|
50
|
-
const res = decode(text, { debug: false });
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
const node = res.node;
|
|
45
|
+
const res = decode(text, { debug: false });
|
|
54
46
|
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
expect(res.errors).toHaveLength(0);
|
|
48
|
+
const node = res.node;
|
|
57
49
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
expect(profileNode).toBeDefined();
|
|
61
|
-
expect(profileNode.fields["level"].value).toBe(99);
|
|
50
|
+
// id should be 1
|
|
51
|
+
expect(node.fields['id'].value).toBe(1);
|
|
62
52
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
);
|
|
68
|
-
});
|
|
53
|
+
// profile should be a node
|
|
54
|
+
const profileNode = node.fields['profile'];
|
|
55
|
+
expect(profileNode).toBeDefined();
|
|
56
|
+
expect(profileNode.fields['level'].value).toBe(99);
|
|
69
57
|
|
|
70
|
-
|
|
58
|
+
assertRoundtrip(node, '@User<id:number,profile:@Profile<level:number>>(1,(99))', false);
|
|
59
|
+
});
|
|
60
|
+
});
|
package/tests/02.data.test.ts
CHANGED
|
@@ -7,83 +7,77 @@ import { assertRoundtrip } from './utils';
|
|
|
7
7
|
// ==================================================================================
|
|
8
8
|
|
|
9
9
|
describe('AK Data Encoding', () => {
|
|
10
|
+
it('should encode simple dict', () => {
|
|
11
|
+
/**
|
|
12
|
+
* Validates encoding a JS Object to AI.DATA format.
|
|
13
|
+
*/
|
|
14
|
+
const data = { x: 10, y: 20 };
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* Validates encoding a JS Object to AI.DATA format.
|
|
14
|
-
*/
|
|
15
|
-
const data = { x: 10, y: 20 };
|
|
16
|
-
|
|
17
|
-
const result = encode(data, { compact: true });
|
|
18
|
-
const expected = '<x:number,y:number>(10,20)';
|
|
19
|
-
|
|
20
|
-
expect(result).toBe(expected);
|
|
21
|
-
|
|
22
|
-
assertRoundtrip(
|
|
23
|
-
result,
|
|
24
|
-
expected,
|
|
25
|
-
false
|
|
26
|
-
);
|
|
27
|
-
});
|
|
16
|
+
const result = encode(data, { compact: true });
|
|
17
|
+
const expected = '<x:number,y:number>(10,20)';
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
19
|
+
expect(result).toBe(expected);
|
|
20
|
+
|
|
21
|
+
assertRoundtrip(result, expected, false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should encode list of objects', () => {
|
|
25
|
+
/**
|
|
26
|
+
* Validates encoding a list of objects.
|
|
27
|
+
*/
|
|
28
|
+
const data = [
|
|
29
|
+
{ name: 'A', val: 1 },
|
|
30
|
+
{ name: 'B', val: 2 },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const result = encode(data, { compact: true, colorize: false });
|
|
34
|
+
const expected = '<[name:string,val:number]>[("A",1),("B",2)]';
|
|
35
|
+
|
|
36
|
+
expect(result).toBe(expected);
|
|
37
|
+
|
|
38
|
+
assertRoundtrip(result, expected, false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle round trip consistency', () => {
|
|
42
|
+
/**
|
|
43
|
+
* Golden Test: Encode -> Decode -> Compare.
|
|
44
|
+
*/
|
|
45
|
+
const originalData = [
|
|
46
|
+
{ id: 1, active: true, tags: ['a', 'b'] },
|
|
47
|
+
{ id: 2, active: false, tags: ['c'] },
|
|
48
|
+
];
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const expected = '<[id:number,active:bool,tags:[string]]>[(1,true,["a","b"]),(2,false,["c"])]';
|
|
57
|
-
|
|
58
|
-
// 1. Encode
|
|
59
|
-
// Note: converted snake_case config keys to camelCase for TS
|
|
60
|
-
const encodedText = encode(
|
|
61
|
-
originalData,
|
|
62
|
-
{ compact: true, includeSchema: true, colorize: false }
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
expect(encodedText).toBe(expected);
|
|
66
|
-
assertRoundtrip(encodedText, expected, false);
|
|
67
|
-
|
|
68
|
-
// 2. Decode
|
|
69
|
-
const res = decode(encodedText, { debug: false });
|
|
70
|
-
|
|
71
|
-
// Optional: inspect schema structure
|
|
72
|
-
|
|
73
|
-
expect(res.errors).toHaveLength(0);
|
|
74
|
-
|
|
75
|
-
// 3. Convert back to JS object (equivalent to .dict() in Python)
|
|
76
|
-
const decodedData = res.node.dict()
|
|
77
|
-
|
|
78
|
-
// 4. Compare
|
|
79
|
-
expect(decodedData).toHaveLength(2);
|
|
80
|
-
expect(decodedData[0].id).toBe(1);
|
|
81
|
-
expect(decodedData[0].active).toBe(true);
|
|
82
|
-
expect(decodedData[0].tags).toEqual(["a", "b"]);
|
|
83
|
-
expect(decodedData[1].active).toBe(false);
|
|
84
|
-
|
|
85
|
-
// Deep equality check
|
|
86
|
-
expect(decodedData).toEqual(originalData);
|
|
50
|
+
const expected = '<[id:number,active:bool,tags:[string]]>[(1,true,["a","b"]),(2,false,["c"])]';
|
|
51
|
+
|
|
52
|
+
// 1. Encode
|
|
53
|
+
// Note: converted snake_case config keys to camelCase for TS
|
|
54
|
+
const encodedText = encode(originalData, {
|
|
55
|
+
compact: true,
|
|
56
|
+
includeSchema: true,
|
|
57
|
+
colorize: false,
|
|
87
58
|
});
|
|
88
59
|
|
|
89
|
-
|
|
60
|
+
expect(encodedText).toBe(expected);
|
|
61
|
+
assertRoundtrip(encodedText, expected, false);
|
|
62
|
+
|
|
63
|
+
// 2. Decode
|
|
64
|
+
const res = decode(encodedText, { debug: false });
|
|
65
|
+
|
|
66
|
+
// Optional: inspect schema structure
|
|
67
|
+
|
|
68
|
+
expect(res.errors).toHaveLength(0);
|
|
69
|
+
|
|
70
|
+
// 3. Convert back to JS object (equivalent to .dict() in Python)
|
|
71
|
+
const decodedData = res.node.dict();
|
|
72
|
+
|
|
73
|
+
// 4. Compare
|
|
74
|
+
expect(decodedData).toHaveLength(2);
|
|
75
|
+
expect(decodedData[0].id).toBe(1);
|
|
76
|
+
expect(decodedData[0].active).toBe(true);
|
|
77
|
+
expect(decodedData[0].tags).toEqual(['a', 'b']);
|
|
78
|
+
expect(decodedData[1].active).toBe(false);
|
|
79
|
+
|
|
80
|
+
// Deep equality check
|
|
81
|
+
expect(decodedData).toEqual(originalData);
|
|
82
|
+
});
|
|
83
|
+
});
|