@arkadia/data 0.1.7 → 0.1.8

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 (54) hide show
  1. package/.prettierrc +8 -0
  2. package/README.md +166 -112
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js.map +1 -1
  5. package/dist/core/Decoder.d.ts.map +1 -1
  6. package/dist/core/Decoder.js +123 -97
  7. package/dist/core/Decoder.js.map +1 -1
  8. package/dist/core/Encoder.d.ts +1 -2
  9. package/dist/core/Encoder.d.ts.map +1 -1
  10. package/dist/core/Encoder.js +74 -76
  11. package/dist/core/Encoder.js.map +1 -1
  12. package/dist/core/Parser.d.ts +1 -1
  13. package/dist/core/Parser.d.ts.map +1 -1
  14. package/dist/core/Parser.js +11 -11
  15. package/dist/core/Parser.js.map +1 -1
  16. package/dist/index.d.ts +4 -4
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +7 -8
  19. package/dist/index.js.map +1 -1
  20. package/dist/models/Meta.d.ts +3 -2
  21. package/dist/models/Meta.d.ts.map +1 -1
  22. package/dist/models/Meta.js +3 -3
  23. package/dist/models/Meta.js.map +1 -1
  24. package/dist/models/Node.d.ts +4 -3
  25. package/dist/models/Node.d.ts.map +1 -1
  26. package/dist/models/Node.js +29 -23
  27. package/dist/models/Node.js.map +1 -1
  28. package/dist/models/Schema.d.ts.map +1 -1
  29. package/dist/models/Schema.js +27 -21
  30. package/dist/models/Schema.js.map +1 -1
  31. package/eslint.config.mjs +42 -0
  32. package/package.json +11 -1
  33. package/scripts/verify-build.js +95 -92
  34. package/src/config.ts +75 -75
  35. package/src/core/Decoder.ts +984 -922
  36. package/src/core/Encoder.ts +364 -371
  37. package/src/core/Parser.ts +112 -112
  38. package/src/index.ts +18 -20
  39. package/src/models/Meta.ts +107 -107
  40. package/src/models/Node.ts +190 -185
  41. package/src/models/Schema.ts +198 -193
  42. package/tests/00.meta.test.ts +19 -25
  43. package/tests/00.node.test.ts +40 -48
  44. package/tests/00.primitive.test.ts +121 -95
  45. package/tests/00.schema.test.ts +28 -35
  46. package/tests/01.schema.test.ts +42 -52
  47. package/tests/02.data.test.ts +69 -75
  48. package/tests/03.errors.test.ts +53 -55
  49. package/tests/04.list.test.ts +192 -193
  50. package/tests/05.record.test.ts +54 -56
  51. package/tests/06.meta.test.ts +393 -389
  52. package/tests/utils.ts +47 -44
  53. package/tsconfig.json +27 -29
  54. package/vitest.config.ts +1 -1
@@ -1,11 +1,13 @@
1
1
  import { Meta, MetaInfo, MetaProps } from './Meta';
2
2
  import { Schema } from './Schema';
3
3
 
4
+ export type Primitive = string | number | boolean | undefined | null;
5
+
4
6
  export interface NodeProps extends MetaProps {
5
- name?: string;
6
- value?: any;
7
- fields?: Record<string, Node>;
8
- elements?: Node[];
7
+ name?: string;
8
+ value?: unknown;
9
+ fields?: Record<string, Node>;
10
+ elements?: Node[];
9
11
  }
10
12
 
11
13
  /**
@@ -13,200 +15,203 @@ export interface NodeProps extends MetaProps {
13
15
  * A Node always refers to a Schema which defines how data should be interpreted.
14
16
  */
15
17
  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] : [];
18
+ schema: Schema;
19
+ name: string;
20
+ value: unknown;
21
+ fields: Record<string, Node>;
22
+ elements: Node[];
23
+
24
+ constructor(schema: Schema, props: NodeProps = {}) {
25
+ super(props);
26
+ this.schema = schema;
27
+ this.name = props.name || '';
28
+ this.value = props.value ?? null;
29
+ this.fields = props.fields || {};
30
+ this.elements = props.elements ? [...props.elements] : [];
31
+ }
32
+
33
+ // -----------------------------------------------------------
34
+ // Introspection helpers
35
+ // -----------------------------------------------------------
36
+
37
+ get isPrimitive(): boolean {
38
+ return this.schema && this.schema.isPrimitive;
39
+ }
40
+ get isRecord(): boolean {
41
+ return this.schema && this.schema.isRecord;
42
+ }
43
+ get isList(): boolean {
44
+ return this.schema && this.schema.isList;
45
+ }
46
+
47
+ // -----------------------------------------------------------
48
+ // Meta
49
+ // -----------------------------------------------------------
50
+
51
+ clearMeta(): void {
52
+ this.clearCommonMeta();
53
+ }
54
+
55
+ applyMeta(info: MetaInfo): void {
56
+ // Applies ALL metadata (common stuff: meta dict, comments)
57
+ this.applyCommonMeta(info);
58
+ }
59
+
60
+ // -----------------------------------------------------------
61
+ // Conversion Methods (dict / json)
62
+ // -----------------------------------------------------------
63
+
64
+ /**
65
+ * Recursively converts the Node into a standard JavaScript object/array/primitive.
66
+ * Equivalent to Python's .dict() method.
67
+ */
68
+ dict(): unknown {
69
+ if (this.isPrimitive) {
70
+ return this.value;
29
71
  }
30
72
 
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();
73
+ if (this.isList) {
74
+ return this.elements.map((element) => element.dict());
45
75
  }
46
76
 
47
- applyMeta(info: MetaInfo): void {
48
- // Applies ALL metadata (common stuff: meta dict, comments)
49
- this.applyCommonMeta(info);
77
+ if (this.isRecord) {
78
+ const result: Record<string, unknown> = {};
79
+ for (const [key, fieldNode] of Object.entries(this.fields)) {
80
+ result[key] = fieldNode.dict();
81
+ }
82
+ return result;
50
83
  }
51
84
 
85
+ return this.value;
86
+ }
52
87
 
88
+ /**
89
+ * Converts the Node to a JSON string.
90
+ * * @param indent Number of spaces for indentation.
91
+ * @param colorize If true, applies ANSI colors to keys, strings, numbers, etc.
92
+ */
93
+ JSON(indent: number = 2, colorize: boolean = false): string {
94
+ // 1. Convert to standard JS object
95
+ const data = this.dict();
53
96
 
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
- }
97
+ // 2. Dump to string
98
+ const jsonStr = JSON.stringify(data, null, indent);
66
99
 
67
- if (this.isList) {
68
- return this.elements.map(element => element.dict());
69
- }
100
+ if (!colorize) {
101
+ return jsonStr;
102
+ }
70
103
 
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
- }
104
+ // 3. Apply Colors (Regex Tokenizer)
105
+ // ANSI codes matching the Encoder class
106
+ const C = {
107
+ RESET: '\x1b[0m',
108
+ STRING: '\x1b[92m', // Green
109
+ NUMBER: '\x1b[94m', // Blue
110
+ BOOL: '\x1b[95m', // Magenta
111
+ NULL: '\x1b[90m', // Gray
112
+ KEY: '\x1b[93m', // Yellow
113
+ };
114
+
115
+ // Regex to capture JSON tokens:
116
+ // Group 1: Keys ("key": )
117
+ // Group 2: String values ("value")
118
+ // Group 3: Booleans/Null
119
+ // Group 4: Numbers
120
+ const tokenPattern =
121
+ /(".*?"\s*:)|(".*?")|\b(true|false|null)\b|(-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g;
122
+
123
+ return jsonStr.replace(tokenPattern, (match) => {
124
+ // Check based on content patterns
125
+
126
+ // Key: Ends with ':' (ignoring whitespace) and starts with '"'
127
+ if (/^".*":\s*$/.test(match)) {
128
+ return `${C.KEY}${match}${C.RESET}`;
129
+ }
130
+
131
+ // String value: Starts with '"'
132
+ if (match.startsWith('"')) {
133
+ return `${C.STRING}${match}${C.RESET}`;
134
+ }
135
+
136
+ // Boolean
137
+ if (match === 'true' || match === 'false') {
138
+ return `${C.BOOL}${match}${C.RESET}`;
139
+ }
140
+
141
+ // Null
142
+ if (match === 'null') {
143
+ return `${C.NULL}${match}${C.RESET}`;
144
+ }
145
+
146
+ // Number (Fallthrough)
147
+ return `${C.NUMBER}${match}${C.RESET}`;
148
+ });
149
+ }
150
+
151
+ // -----------------------------------------------------------
152
+ // Debug / Representation
153
+ // -----------------------------------------------------------
154
+
155
+ /**
156
+ * Technical debug representation.
157
+ * Format: <Node(KIND:type) value/len=... details...>
158
+ */
159
+ toString(): string {
160
+ // 1. Type Info
161
+ let typeLabel = 'UNKNOWN';
162
+ if (this.schema) {
163
+ const kind = this.schema.kind;
164
+ const typeName = this.schema.typeName;
165
+
166
+ if (this.isList) {
167
+ const elType = this.schema.element ? this.schema.element.typeName || 'any' : 'any';
168
+ typeLabel = `LIST[${elType}]`;
169
+ } else if (this.isRecord && typeName !== 'record' && typeName !== 'any') {
170
+ typeLabel = `RECORD:${typeName}`;
171
+ } else {
172
+ typeLabel = `${kind}:${typeName}`;
173
+ }
174
+ }
78
175
 
79
- return this.value;
176
+ const header = `<Node(${typeLabel})`;
177
+ const content: string[] = [];
178
+
179
+ // 2. Content Info
180
+ if (this.isPrimitive) {
181
+ let v = String(this.value);
182
+ if (typeof this.value === 'string') v = `"${v}"`;
183
+ if (v.length > 50) v = v.substring(0, 47) + '...';
184
+ content.push(`val=${v}`);
185
+ } else if (this.isList) {
186
+ content.push(`len=${this.elements.length}`);
187
+ } else if (this.isRecord) {
188
+ const keys = Object.keys(this.fields);
189
+ let keysStr = '';
190
+ if (keys.length > 3) {
191
+ keysStr = keys.slice(0, 3).join(', ') + ', ...';
192
+ } else {
193
+ keysStr = keys.join(', ');
194
+ }
195
+ content.push(`fields=[${keysStr}]`);
196
+ } else {
197
+ // Fallback
198
+ let v = String(this.value);
199
+ if (v.length > 50) v = v.substring(0, 47) + '...';
200
+ content.push(`val=${v}`);
80
201
  }
81
202
 
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
- });
203
+ // 3. Meta Indicators
204
+ if (this.comments.length > 0) {
205
+ content.push(`comments=${this.comments.length}`);
142
206
  }
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}>`;
207
+ if (Object.keys(this.attr).length > 0) {
208
+ content.push(`attr=[${Object.keys(this.attr).join(', ')}]`);
209
+ }
210
+ if (this.tags.length > 0) {
211
+ content.push(`tags=[${this.tags.join(', ')}]`);
209
212
  }
210
213
 
211
-
212
- }
214
+ const detailsStr = content.length > 0 ? ' ' + content.join(' ') : '';
215
+ return `${header}${detailsStr}>`;
216
+ }
217
+ }