@arkadia/ai-data-format 0.1.4

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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/dist/config.d.ts +75 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +28 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/core/Decoder.d.ts +86 -0
  8. package/dist/core/Decoder.d.ts.map +1 -0
  9. package/dist/core/Decoder.js +951 -0
  10. package/dist/core/Decoder.js.map +1 -0
  11. package/dist/core/Encoder.d.ts +26 -0
  12. package/dist/core/Encoder.d.ts.map +1 -0
  13. package/dist/core/Encoder.js +368 -0
  14. package/dist/core/Encoder.js.map +1 -0
  15. package/dist/core/Parser.d.ts +6 -0
  16. package/dist/core/Parser.d.ts.map +1 -0
  17. package/dist/core/Parser.js +132 -0
  18. package/dist/core/Parser.js.map +1 -0
  19. package/dist/index.d.ts +22 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +46 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/models/Meta.d.ts +48 -0
  24. package/dist/models/Meta.d.ts.map +1 -0
  25. package/dist/models/Meta.js +113 -0
  26. package/dist/models/Meta.js.map +1 -0
  27. package/dist/models/Node.d.ts +42 -0
  28. package/dist/models/Node.d.ts.map +1 -0
  29. package/dist/models/Node.js +179 -0
  30. package/dist/models/Node.js.map +1 -0
  31. package/dist/models/Schema.d.ts +55 -0
  32. package/dist/models/Schema.d.ts.map +1 -0
  33. package/dist/models/Schema.js +175 -0
  34. package/dist/models/Schema.js.map +1 -0
  35. package/package.json +32 -0
  36. package/scripts/verify-build.js +202 -0
  37. package/src/config.ts +102 -0
  38. package/src/core/Decoder.ts +1057 -0
  39. package/src/core/Encoder.ts +443 -0
  40. package/src/core/Parser.ts +150 -0
  41. package/src/index.ts +46 -0
  42. package/src/models/Meta.ts +135 -0
  43. package/src/models/Node.ts +212 -0
  44. package/src/models/Schema.ts +222 -0
  45. package/tests/00.meta.test.ts +31 -0
  46. package/tests/00.node.test.ts +54 -0
  47. package/tests/00.primitive.test.ts +108 -0
  48. package/tests/00.schema.test.ts +41 -0
  49. package/tests/01.schema.test.ts +70 -0
  50. package/tests/02.data.test.ts +89 -0
  51. package/tests/03.errors.test.ts +71 -0
  52. package/tests/04.list.test.ts +225 -0
  53. package/tests/05.record.test.ts +82 -0
  54. package/tests/06.meta.test.ts +506 -0
  55. package/tests/utils.ts +69 -0
  56. package/tsconfig.json +46 -0
  57. package/vitest.config.ts +9 -0
@@ -0,0 +1,135 @@
1
+
2
+ export interface MetaProps {
3
+ comments?: string[];
4
+ attr?: Record<string, any>;
5
+ tags?: string[];
6
+ }
7
+
8
+ /**
9
+ * Mixin/Base class that adds metadata storage capabilities to Node and Schema.
10
+ */
11
+ export class Meta {
12
+ comments: string[];
13
+ attr: Record<string, any>;
14
+ tags: string[];
15
+
16
+ constructor({ comments, attr, tags }: MetaProps = {}) {
17
+ this.comments = comments ? [...comments] : [];
18
+ this.attr = attr ? { ...attr } : {};
19
+ this.tags = tags ? [...tags] : [];
20
+ }
21
+
22
+ /**
23
+ * Clears ALL metadata (comments, attributes, tags).
24
+ */
25
+ clearCommonMeta(): void {
26
+ this.comments = [];
27
+ this.attr = {};
28
+ this.tags = [];
29
+ }
30
+
31
+ /**
32
+ * Merges only the common fields (attributes, comments, tags)
33
+ * from a MetaInfo object. Safe for both Node and Schema.
34
+ */
35
+ applyCommonMeta(info: Meta): void {
36
+ // Append comments
37
+ if (info.comments.length > 0) {
38
+ this.comments.push(...info.comments);
39
+ }
40
+
41
+ // Merge attributes ($key=value)
42
+ if (Object.keys(info.attr).length > 0) {
43
+ Object.assign(this.attr, info.attr);
44
+ }
45
+
46
+ // Append tags
47
+ if (info.tags.length > 0) {
48
+ this.tags.push(...info.tags);
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * A temporary container (DTO) holding parsed metadata from a / ... / block.
55
+ * It contains BOTH Schema constraints (!required) and Node attributes ($key=val).
56
+ */
57
+ export class MetaInfo extends Meta {
58
+ required: boolean;
59
+
60
+ constructor(props: MetaProps & { required?: boolean } = {}) {
61
+ super(props);
62
+ this.required = props.required || false;
63
+ }
64
+
65
+ /**
66
+ * Merges everything from another MetaInfo object, including 'required' flag.
67
+ */
68
+ applyMeta(info: MetaInfo): void {
69
+ // Apply common fields (comments, attr, tags)
70
+ this.applyCommonMeta(info);
71
+
72
+ // Override required meta (Schema Only)
73
+ if (info.required) {
74
+ this.required = true;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Allows usage checks like 'if meta.isEmpty()' to see if any metadata was collected.
80
+ * Equivalent to Python's __bool__.
81
+ */
82
+ isEmpty(): boolean {
83
+ return (
84
+ this.comments.length === 0 &&
85
+ Object.keys(this.attr).length === 0 &&
86
+ this.tags.length === 0 &&
87
+ !this.required
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Debug representation mimicking the actual ADF format style.
93
+ * Example: <MetaInfo !required #tag $key=val >
94
+ */
95
+ toString(): string {
96
+ const parts: string[] = [];
97
+
98
+ // 1. Flags
99
+ if (this.required) {
100
+ parts.push("!required");
101
+ }
102
+
103
+ // 2. Tags
104
+ for (const t of this.tags) {
105
+ parts.push(`#${t}`);
106
+ }
107
+
108
+ // 3. Attributes
109
+ for (const [k, v] of Object.entries(this.attr)) {
110
+ // Simplistic value repr for debug
111
+ let valStr: string;
112
+ if (typeof v === 'string') {
113
+ valStr = `"${v}"`;
114
+ } else {
115
+ valStr = String(v);
116
+ }
117
+ parts.push(`$${k}=${valStr}`);
118
+ }
119
+
120
+ // 4. Comments (Summary)
121
+ if (this.comments.length > 0) {
122
+ if (this.comments.length === 1) {
123
+ const c = this.comments[0];
124
+ const preview = c.length > 15 ? c.substring(0, 15) + '..' : c;
125
+ parts.push(`/* ${preview} */`);
126
+ } else {
127
+ parts.push(`/* ${this.comments.length} comments */`);
128
+ }
129
+ }
130
+
131
+ const content = parts.join(" ");
132
+ return content ? `<MetaInfo ${content}>` : "<MetaInfo (empty)>";
133
+ }
134
+
135
+ }
@@ -0,0 +1,212 @@
1
+ import { Meta, MetaInfo, MetaProps } from './Meta';
2
+ import { Schema } from './Schema';
3
+
4
+ export interface NodeProps extends MetaProps {
5
+ name?: string;
6
+ value?: any;
7
+ fields?: Record<string, Node>;
8
+ elements?: Node[];
9
+ }
10
+
11
+ /**
12
+ * Canonical runtime data object for AI.DATA.
13
+ * A Node always refers to a Schema which defines how data should be interpreted.
14
+ */
15
+ export class Node extends Meta {
16
+ schema: Schema;
17
+ name: string;
18
+ value: any;
19
+ fields: Record<string, Node>;
20
+ elements: Node[];
21
+
22
+ constructor(schema: Schema, props: NodeProps = {}) {
23
+ super(props);
24
+ this.schema = schema;
25
+ this.name = props.name || "";
26
+ this.value = props.value ?? null;
27
+ this.fields = props.fields || {};
28
+ this.elements = props.elements ? [...props.elements] : [];
29
+ }
30
+
31
+ // -----------------------------------------------------------
32
+ // Introspection helpers
33
+ // -----------------------------------------------------------
34
+
35
+ get isPrimitive(): boolean { return this.schema && this.schema.isPrimitive; }
36
+ get isRecord(): boolean { return this.schema && this.schema.isRecord; }
37
+ get isList(): boolean { return this.schema && this.schema.isList; }
38
+
39
+ // -----------------------------------------------------------
40
+ // Meta
41
+ // -----------------------------------------------------------
42
+
43
+ clearMeta(): void {
44
+ this.clearCommonMeta();
45
+ }
46
+
47
+ applyMeta(info: MetaInfo): void {
48
+ // Applies ALL metadata (common stuff: meta dict, comments)
49
+ this.applyCommonMeta(info);
50
+ }
51
+
52
+
53
+
54
+ // -----------------------------------------------------------
55
+ // Conversion Methods (dict / json)
56
+ // -----------------------------------------------------------
57
+
58
+ /**
59
+ * Recursively converts the Node into a standard JavaScript object/array/primitive.
60
+ * Equivalent to Python's .dict() method.
61
+ */
62
+ dict(): any {
63
+ if (this.isPrimitive) {
64
+ return this.value;
65
+ }
66
+
67
+ if (this.isList) {
68
+ return this.elements.map(element => element.dict());
69
+ }
70
+
71
+ if (this.isRecord) {
72
+ const result: Record<string, any> = {};
73
+ for (const [key, fieldNode] of Object.entries(this.fields)) {
74
+ result[key] = fieldNode.dict();
75
+ }
76
+ return result;
77
+ }
78
+
79
+ return this.value;
80
+ }
81
+
82
+ /**
83
+ * Converts the Node to a JSON string.
84
+ * * @param indent Number of spaces for indentation.
85
+ * @param colorize If true, applies ANSI colors to keys, strings, numbers, etc.
86
+ */
87
+ JSON(indent: number = 2, colorize: boolean = false): string {
88
+ // 1. Convert to standard JS object
89
+ const data = this.dict();
90
+
91
+ // 2. Dump to string
92
+ const jsonStr = JSON.stringify(data, null, indent);
93
+
94
+ if (!colorize) {
95
+ return jsonStr;
96
+ }
97
+
98
+ // 3. Apply Colors (Regex Tokenizer)
99
+ // ANSI codes matching the Encoder class
100
+ const C = {
101
+ RESET: "\x1b[0m",
102
+ STRING: "\x1b[92m", // Green
103
+ NUMBER: "\x1b[94m", // Blue
104
+ BOOL: "\x1b[95m", // Magenta
105
+ NULL: "\x1b[90m", // Gray
106
+ KEY: "\x1b[93m" // Yellow
107
+ };
108
+
109
+ // Regex to capture JSON tokens:
110
+ // Group 1: Keys ("key": )
111
+ // Group 2: String values ("value")
112
+ // Group 3: Booleans/Null
113
+ // Group 4: Numbers
114
+ const tokenPattern = /(".*?"\s*:)|(".*?")|\b(true|false|null)\b|(-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g;
115
+
116
+ return jsonStr.replace(tokenPattern, (match) => {
117
+ // Check based on content patterns
118
+
119
+ // Key: Ends with ':' (ignoring whitespace) and starts with '"'
120
+ if (/^".*":\s*$/.test(match)) {
121
+ return `${C.KEY}${match}${C.RESET}`;
122
+ }
123
+
124
+ // String value: Starts with '"'
125
+ if (match.startsWith('"')) {
126
+ return `${C.STRING}${match}${C.RESET}`;
127
+ }
128
+
129
+ // Boolean
130
+ if (match === "true" || match === "false") {
131
+ return `${C.BOOL}${match}${C.RESET}`;
132
+ }
133
+
134
+ // Null
135
+ if (match === "null") {
136
+ return `${C.NULL}${match}${C.RESET}`;
137
+ }
138
+
139
+ // Number (Fallthrough)
140
+ return `${C.NUMBER}${match}${C.RESET}`;
141
+ });
142
+ }
143
+
144
+ // -----------------------------------------------------------
145
+ // Debug / Representation
146
+ // -----------------------------------------------------------
147
+
148
+ /**
149
+ * Technical debug representation.
150
+ * Format: <Node(KIND:type) value/len=... details...>
151
+ */
152
+ toString(): string {
153
+ // 1. Type Info
154
+ let typeLabel = "UNKNOWN";
155
+ if (this.schema) {
156
+ const kind = this.schema.kind;
157
+ const typeName = this.schema.typeName;
158
+
159
+ if (this.isList) {
160
+ const elType = this.schema.element ? (this.schema.element.typeName || "any") : "any";
161
+ typeLabel = `LIST[${elType}]`;
162
+ } else if (this.isRecord && typeName !== "record" && typeName !== "any") {
163
+ typeLabel = `RECORD:${typeName}`;
164
+ } else {
165
+ typeLabel = `${kind}:${typeName}`;
166
+ }
167
+ }
168
+
169
+ const header = `<Node(${typeLabel})`;
170
+ const content: string[] = [];
171
+
172
+ // 2. Content Info
173
+ if (this.isPrimitive) {
174
+ let v = String(this.value);
175
+ if (typeof this.value === 'string') v = `"${v}"`;
176
+ if (v.length > 50) v = v.substring(0, 47) + "...";
177
+ content.push(`val=${v}`);
178
+ } else if (this.isList) {
179
+ content.push(`len=${this.elements.length}`);
180
+ } else if (this.isRecord) {
181
+ const keys = Object.keys(this.fields);
182
+ let keysStr = "";
183
+ if (keys.length > 3) {
184
+ keysStr = keys.slice(0, 3).join(", ") + ", ...";
185
+ } else {
186
+ keysStr = keys.join(", ");
187
+ }
188
+ content.push(`fields=[${keysStr}]`);
189
+ } else {
190
+ // Fallback
191
+ let v = String(this.value);
192
+ if (v.length > 50) v = v.substring(0, 47) + "...";
193
+ content.push(`val=${v}`);
194
+ }
195
+
196
+ // 3. Meta Indicators
197
+ if (this.comments.length > 0) {
198
+ content.push(`comments=${this.comments.length}`);
199
+ }
200
+ if (Object.keys(this.attr).length > 0) {
201
+ content.push(`attr=[${Object.keys(this.attr).join(', ')}]`);
202
+ }
203
+ if (this.tags.length > 0) {
204
+ content.push(`tags=[${this.tags.join(', ')}]`);
205
+ }
206
+
207
+ const detailsStr = content.length > 0 ? " " + content.join(" ") : "";
208
+ return `${header}${detailsStr}>`;
209
+ }
210
+
211
+
212
+ }
@@ -0,0 +1,222 @@
1
+ import { Meta, MetaInfo, MetaProps } from './Meta';
2
+
3
+ export enum SchemaKind {
4
+ PRIMITIVE = "PRIMITIVE", // int, string, bool, null
5
+ RECORD = "RECORD", // User, Point, or anonymous <...>
6
+ LIST = "LIST", // Array/Sequence
7
+ DICT = "DICT", // Future-proofing: Key-Value pairs
8
+ ANY = "ANY" // Fallback
9
+ }
10
+
11
+ export interface SchemaProps extends MetaProps {
12
+ typeName?: string;
13
+ name?: string;
14
+ fields?: Schema[];
15
+ element?: Schema;
16
+ key?: Schema;
17
+ value?: Schema;
18
+ required?: boolean;
19
+ }
20
+
21
+ export class Schema extends Meta {
22
+ kind: SchemaKind;
23
+ typeName: string;
24
+ name: string;
25
+
26
+ // Structure references
27
+ element: Schema | null;
28
+ key: Schema | null;
29
+ value: Schema | null;
30
+
31
+ // Flags
32
+ required: boolean;
33
+
34
+ // Internal fields storage
35
+ private _fieldsList: Schema[] = [];
36
+ private _fieldsMap: Map<string, Schema> = new Map();
37
+
38
+ constructor(kind: SchemaKind, props: SchemaProps = {}) {
39
+ super(props);
40
+
41
+ this.kind = kind;
42
+ this.typeName = props.typeName || "any";
43
+ this.name = props.name || "";
44
+
45
+ this.element = props.element || null;
46
+ this.key = props.key || null;
47
+ this.value = props.value || null;
48
+ this.required = props.required || false;
49
+
50
+ if (props.fields) {
51
+ props.fields.forEach(f => this.addField(f));
52
+ }
53
+ }
54
+
55
+ // -----------------------------------------------------------
56
+ // Properties (Is...)
57
+ // -----------------------------------------------------------
58
+
59
+ get isPrimitive(): boolean { return this.kind === SchemaKind.PRIMITIVE; }
60
+ get isRecord(): boolean { return this.kind === SchemaKind.RECORD; }
61
+ get isList(): boolean { return this.kind === SchemaKind.LIST; }
62
+
63
+ get isAny(): boolean {
64
+ return (
65
+ this.kind === SchemaKind.ANY ||
66
+ (this.typeName === "any" && this.kind === SchemaKind.PRIMITIVE) ||
67
+ (this.typeName === "any" && this.kind === SchemaKind.RECORD)
68
+ );
69
+ }
70
+
71
+ get fields(): Schema[] {
72
+ return this._fieldsList;
73
+ }
74
+
75
+ // -----------------------------------------------------------
76
+ // Field Management
77
+ // -----------------------------------------------------------
78
+
79
+ clearFields(): void {
80
+ this._fieldsList = [];
81
+ this._fieldsMap.clear();
82
+ }
83
+
84
+ addField(field: Schema): void {
85
+ // Python logic: Auto-switch to RECORD if adding fields
86
+ if (this.kind !== SchemaKind.RECORD) {
87
+ this.kind = SchemaKind.RECORD;
88
+ }
89
+
90
+ // Auto-naming if missing
91
+ const fName = field.name || String(this._fieldsList.length);
92
+ field.name = fName;
93
+
94
+ this._fieldsList.push(field);
95
+ this._fieldsMap.set(fName, field);
96
+ }
97
+
98
+ /**
99
+ * Equivalent to Python's __getitem__.
100
+ * Allows access by numeric index or field name string.
101
+ */
102
+ getField(key: number | string): Schema | undefined {
103
+ if (!this.isRecord) {
104
+ throw new Error(`Schema kind ${this.kind} is not subscriptable (not a RECORD).`);
105
+ }
106
+ if (typeof key === 'number') {
107
+ return this._fieldsList[key];
108
+ }
109
+ return this._fieldsMap.get(key);
110
+ }
111
+
112
+ /**
113
+ * Replaces an existing field with a new definition based on field.name.
114
+ * Preserves the original order in the fields list.
115
+ * If the field does not exist, it appends it (like addField).
116
+ */
117
+ replaceField(field: Schema): void {
118
+ const fName = field.name;
119
+ if (!fName) {
120
+ throw new Error("Cannot replace a field without a name.");
121
+ }
122
+
123
+ if (this._fieldsMap.has(fName)) {
124
+ // 1. Retrieve the old object to find its index
125
+ const oldField = this._fieldsMap.get(fName)!;
126
+ const idx = this._fieldsList.indexOf(oldField);
127
+
128
+ if (idx !== -1) {
129
+ // 2. Replace in list (preserve order)
130
+ this._fieldsList[idx] = field;
131
+ } else {
132
+ // Fallback safety (should not happen if map/list synced)
133
+ this._fieldsList.push(field);
134
+ }
135
+
136
+ // 3. Update map
137
+ this._fieldsMap.set(fName, field);
138
+ } else {
139
+ // Field doesn't exist, treat as add
140
+ this.addField(field);
141
+ }
142
+ }
143
+
144
+ // -----------------------------------------------------------
145
+ // Meta Management
146
+ // -----------------------------------------------------------
147
+
148
+ clearMeta(): void {
149
+ this.clearCommonMeta();
150
+ this.required = false;
151
+ }
152
+
153
+ applyMeta(info: MetaInfo | Schema | undefined): void {
154
+ if(!info) return;
155
+ // 1. Apply common stuff (meta dict, comments, tags)
156
+ this.applyCommonMeta(info);
157
+
158
+ // 2. Apply Schema-specific constraints
159
+ if (info.required) {
160
+ this.required = true;
161
+ }
162
+ }
163
+
164
+ // -----------------------------------------------------------
165
+ // Debug / Representation
166
+ // -----------------------------------------------------------
167
+
168
+ /**
169
+ * Technical debug representation.
170
+ * Format: <Schema(KIND:type_name) name='...' details...>
171
+ */
172
+ toString(): string {
173
+ // 1. Basic Info: Kind and TypeName
174
+ const kindStr = this.kind;
175
+
176
+ let typeLabel = "";
177
+ if (this.typeName && this.typeName !== "any" && this.typeName !== this.kind) {
178
+ typeLabel = `:${this.typeName}`;
179
+ }
180
+
181
+ const header = `<Schema(${kindStr}${typeLabel})`;
182
+
183
+ // 2. Field Name
184
+ const nameStr = this.name ? ` name="${this.name}"` : "";
185
+
186
+ // 3. Details
187
+ const details: string[] = [];
188
+
189
+ if (this.required) details.push("!required");
190
+
191
+ const attrKeys = Object.keys(this.attr);
192
+ if (attrKeys.length > 0) details.push(`attr=[${attrKeys.map(a => '"' + a + '"').join(', ')}]`);
193
+
194
+ if (this.tags.length > 0) details.push(`tags=[${this.tags.join(', ')}]`);
195
+ if (this.comments.length > 0) details.push(`comments=${this.comments.length}`);
196
+
197
+ // Structure: Record
198
+ if (this.isRecord) {
199
+ const count = this._fieldsList.length;
200
+ if (count > 0) {
201
+ const limit = 3;
202
+ const fieldNames = this._fieldsList.slice(0, limit).map(f => f.name);
203
+ if (count > limit) fieldNames.push("...");
204
+ details.push(`fields(${count})=[${fieldNames.join(', ')}]`);
205
+ } else {
206
+ details.push("fields=[]");
207
+ }
208
+ }
209
+ // Structure: List
210
+ else if (this.isList) {
211
+ const elType = this.element ? (this.element.typeName || "None") : "None";
212
+ const elKind = this.element ? this.element.kind : "ANY";
213
+ details.push(`element=${elKind}:${elType}`);
214
+ }
215
+
216
+ const detailsStr = details.length > 0 ? " " + details.join(" ") : "";
217
+
218
+ return `${header}${nameStr}${detailsStr}>`;
219
+ }
220
+
221
+
222
+ }
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { MetaInfo } from '../src/index';
3
+
4
+ describe('AI Data Meta', () => {
5
+ it('should Meta be properly formatted', () => {
6
+ const meta = new MetaInfo({
7
+
8
+ comments: ["This is a comment"],
9
+ attr: { foo: "bar" },
10
+ tags: ["tag1", "tag2"],
11
+ required: true
12
+ });
13
+ const expected = '<MetaInfo !required #tag1 #tag2 $foo="bar" /* This is a comme.. */>';
14
+ // const expectedJSON
15
+ const expected_val = {
16
+ "comments": [
17
+ "This is a comment"
18
+ ],
19
+ "attr": {
20
+ "foo": "bar"
21
+ },
22
+ "tags": [
23
+ "tag1",
24
+ "tag2"
25
+ ],
26
+ "required": true
27
+ }
28
+ expect(meta).toMatchObject(expected_val);
29
+ expect(meta.toString()).toBe(expected);
30
+ });
31
+ });
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Schema, SchemaKind, Node } from '../src/index';
3
+
4
+ describe('AI Node test', () => {
5
+ it('should encode raw object', () => {
6
+ const schema = new Schema(
7
+ SchemaKind.DICT,
8
+ {
9
+ name: "TestSchema",
10
+ comments: ["This is a comment"],
11
+ attr: { foo: "bar" },
12
+ tags: ["tag1", "tag2"],
13
+ required: true
14
+
15
+ });
16
+ const node = new Node(schema, {
17
+ value: 3,
18
+ })
19
+
20
+ const expected = '<Node(DICT:any) val=3>';
21
+ const expected_val = {
22
+ "comments": [],
23
+ "attr": {},
24
+ "tags": [],
25
+ "schema": {
26
+ "comments": [
27
+ "This is a comment"
28
+ ],
29
+ "attr": {
30
+ "foo": "bar"
31
+ },
32
+ "tags": [
33
+ "tag1",
34
+ "tag2"
35
+ ],
36
+ "_fieldsList": [],
37
+ "_fieldsMap": {},
38
+ "kind": "DICT",
39
+ "typeName": "any",
40
+ "name": "TestSchema",
41
+ "element": null,
42
+ "key": null,
43
+ "value": null,
44
+ "required": true
45
+ },
46
+ "name": "",
47
+ "value": 3,
48
+ "fields": {},
49
+ "elements": []
50
+ }
51
+ expect(node).toMatchObject(expected_val);
52
+ expect(node.toString()).toBe(expected);
53
+ });
54
+ });