@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,150 +1,150 @@
1
- import { Node } from '../models/Node';
1
+ import { Node, Primitive } from '../models/Node';
2
2
  import { Schema, SchemaKind } from '../models/Schema';
3
3
 
4
4
  export class EncodingError extends Error {
5
- constructor(message: string) {
6
- super(message);
7
- this.name = "EncodingError";
8
- }
5
+ constructor(message: string) {
6
+ super(message);
7
+ this.name = 'EncodingError';
8
+ }
9
9
  }
10
10
 
11
11
  // --------------------------------------------------------------
12
12
  // Helper: Type Wrapper
13
13
  // --------------------------------------------------------------
14
- const TYPE_STRING = "string";
15
- const TYPE_NUMBER = "number";
16
- const TYPE_BOOL = "bool";
17
- const TYPE_NULL = "null";
14
+ const TYPE_STRING = 'string';
15
+ const TYPE_NUMBER = 'number';
16
+ const TYPE_BOOL = 'bool';
17
+ const TYPE_NULL = 'null';
18
18
  // const TYPE_ANY = "any";
19
19
 
20
20
  // --------------------------------------------------------------
21
21
  // Primitive -> Node(primitive)
22
22
  // --------------------------------------------------------------
23
23
 
24
- function parsePrimitive(v: any): Node {
25
- let schema: Schema;
26
-
27
- if (typeof v === 'string') {
28
- schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_STRING });
29
- } else if (typeof v === 'boolean') {
30
- schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_BOOL });
31
- } else if (typeof v === 'number') {
32
- schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_NUMBER });
33
- } else if (v === null || v === undefined) {
34
- schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_NULL });
35
- } else {
36
- throw new EncodingError(`Unsupported primitive: ${v}`);
37
- }
38
-
39
- return new Node(schema, { value: v });
24
+ function parsePrimitive(v: Primitive): Node {
25
+ let schema: Schema;
26
+
27
+ if (typeof v === 'string') {
28
+ schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_STRING });
29
+ } else if (typeof v === 'boolean') {
30
+ schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_BOOL });
31
+ } else if (typeof v === 'number') {
32
+ schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_NUMBER });
33
+ } else if (v === null || v === undefined) {
34
+ schema = new Schema(SchemaKind.PRIMITIVE, { typeName: TYPE_NULL });
35
+ } else {
36
+ throw new EncodingError(`Unsupported primitive: ${v}`);
37
+ }
38
+
39
+ return new Node(schema, { value: v });
40
40
  }
41
41
 
42
42
  // --------------------------------------------------------------
43
43
  // List -> Node(list)
44
44
  // --------------------------------------------------------------
45
45
 
46
- function parseList(arr: any[]): Node {
47
- // 1. EMPTY LIST
48
- if (arr.length === 0) {
49
- const elementSchema = new Schema(SchemaKind.PRIMITIVE, { typeName: "any" });
50
- const listSchema = new Schema(SchemaKind.LIST, { element: elementSchema });
51
- return new Node(listSchema, { elements: [] });
52
- }
53
-
54
- // 2. PARSE ALL ITEMS
55
- const parsedItems: Node[] = arr.map(v => parseDataToNode(v));
56
-
57
- // Use the first element as the baseline
58
- const firstSchema = parsedItems[0].schema;
59
- const isListOfRecords = (firstSchema.kind === SchemaKind.RECORD);
60
-
61
- let unifiedElementSchema: Schema;
62
-
63
- // 3. DETERMINE ELEMENT SCHEMA
64
- if (isListOfRecords) {
65
- // RECORDS: Create a unified schema containing ALL fields from ALL items.
66
- unifiedElementSchema = new Schema(SchemaKind.RECORD, { typeName: "record" });
67
- const seenFields = new Set<string>();
68
-
69
- for (const item of parsedItems) {
70
- // Skip non-record items if mixed list (or handle as error depending on strictness)
71
- if (item.schema.kind === SchemaKind.RECORD) {
72
- for (const field of item.schema.fields) {
73
- if (!seenFields.has(field.name)) {
74
- unifiedElementSchema.addField(field);
75
- seenFields.add(field.name);
76
- }
77
- }
78
- }
46
+ function parseList(arr: unknown[]): Node {
47
+ // 1. EMPTY LIST
48
+ if (arr.length === 0) {
49
+ const elementSchema = new Schema(SchemaKind.PRIMITIVE, { typeName: 'any' });
50
+ const listSchema = new Schema(SchemaKind.LIST, { element: elementSchema });
51
+ return new Node(listSchema, { elements: [] });
52
+ }
53
+
54
+ // 2. PARSE ALL ITEMS
55
+ const parsedItems: Node[] = arr.map((v) => parseDataToNode(v));
56
+
57
+ // Use the first element as the baseline
58
+ const firstSchema = parsedItems[0].schema;
59
+ const isListOfRecords = firstSchema.kind === SchemaKind.RECORD;
60
+
61
+ let unifiedElementSchema: Schema;
62
+
63
+ // 3. DETERMINE ELEMENT SCHEMA
64
+ if (isListOfRecords) {
65
+ // RECORDS: Create a unified schema containing ALL fields from ALL items.
66
+ unifiedElementSchema = new Schema(SchemaKind.RECORD, { typeName: 'record' });
67
+ const seenFields = new Set<string>();
68
+
69
+ for (const item of parsedItems) {
70
+ // Skip non-record items if mixed list (or handle as error depending on strictness)
71
+ if (item.schema.kind === SchemaKind.RECORD) {
72
+ for (const field of item.schema.fields) {
73
+ if (!seenFields.has(field.name)) {
74
+ unifiedElementSchema.addField(field);
75
+ seenFields.add(field.name);
76
+ }
79
77
  }
80
- } else {
81
- // PRIMITIVES: Simply take the schema of the first element.
82
- // We assume the list is homogeneous based on the first item.
83
- unifiedElementSchema = firstSchema;
78
+ }
84
79
  }
85
-
86
- // 4. FINALIZE
87
- const listSchema = new Schema(SchemaKind.LIST, {
88
- typeName: "list",
89
- element: unifiedElementSchema
90
- });
91
-
92
- return new Node(listSchema, { elements: parsedItems });
80
+ } else {
81
+ // PRIMITIVES: Simply take the schema of the first element.
82
+ // We assume the list is homogeneous based on the first item.
83
+ unifiedElementSchema = firstSchema;
84
+ }
85
+
86
+ // 4. FINALIZE
87
+ const listSchema = new Schema(SchemaKind.LIST, {
88
+ typeName: 'list',
89
+ element: unifiedElementSchema,
90
+ });
91
+
92
+ return new Node(listSchema, { elements: parsedItems });
93
93
  }
94
94
 
95
95
  // --------------------------------------------------------------
96
96
  // Dict -> Node(record)
97
97
  // --------------------------------------------------------------
98
98
 
99
- function parseDict(obj: Record<string, any>): Node {
100
- /**
101
- * JSON objects -> named records.
102
- */
103
- const fieldsData: Record<string, Node> = {};
104
- const schema = new Schema(SchemaKind.RECORD);
99
+ function parseDict(obj: Record<string, unknown>): Node {
100
+ /**
101
+ * JSON objects -> named records.
102
+ */
103
+ const fieldsData: Record<string, Node> = {};
104
+ const schema = new Schema(SchemaKind.RECORD);
105
105
 
106
- for (const [key, rawValue] of Object.entries(obj)) {
107
- const childNode = parseDataToNode(rawValue);
106
+ for (const [key, rawValue] of Object.entries(obj)) {
107
+ const childNode = parseDataToNode(rawValue);
108
108
 
109
- // Assign field name to the child's schema so it knows it is a field
110
- childNode.schema.name = key;
109
+ // Assign field name to the child's schema so it knows it is a field
110
+ childNode.schema.name = key;
111
111
 
112
- fieldsData[key] = childNode;
113
- schema.addField(childNode.schema);
114
- }
112
+ fieldsData[key] = childNode;
113
+ schema.addField(childNode.schema);
114
+ }
115
115
 
116
- return new Node(schema, { fields: fieldsData });
116
+ return new Node(schema, { fields: fieldsData });
117
117
  }
118
118
 
119
119
  // --------------------------------------------------------------
120
120
  // Main entrypoint
121
121
  // --------------------------------------------------------------
122
122
 
123
- export function parseDataToNode(value: any): Node {
124
- if (value instanceof Node) {
125
- return value;
126
- }
127
-
128
- // 1. Primitive Check
129
- if (
130
- value === null ||
131
- value === undefined ||
132
- typeof value === 'string' ||
133
- typeof value === 'number' ||
134
- typeof value === 'boolean'
135
- ) {
136
- return parsePrimitive(value);
137
- }
138
-
139
- // 2. List Check
140
- if (Array.isArray(value)) {
141
- return parseList(value);
142
- }
143
-
144
- // 3. Object Check (Dict)
145
- if (typeof value === 'object') {
146
- return parseDict(value);
147
- }
148
-
149
- throw new EncodingError(`Unsupported structure type: ${typeof value}`);
150
- }
123
+ export function parseDataToNode(value: Node | unknown): Node {
124
+ if (value instanceof Node) {
125
+ return value;
126
+ }
127
+
128
+ // 1. Primitive Check
129
+ if (
130
+ value === null ||
131
+ value === undefined ||
132
+ typeof value === 'string' ||
133
+ typeof value === 'number' ||
134
+ typeof value === 'boolean'
135
+ ) {
136
+ return parsePrimitive(value);
137
+ }
138
+
139
+ // 2. List Check
140
+ if (Array.isArray(value)) {
141
+ return parseList(value);
142
+ }
143
+
144
+ // 3. Object Check (Dict)
145
+ if (typeof value === 'object') {
146
+ return parseDict(value as Record<string, unknown>);
147
+ }
148
+
149
+ throw new EncodingError(`Unsupported structure type: ${typeof value}`);
150
+ }
package/src/index.ts CHANGED
@@ -1,14 +1,13 @@
1
- import { EncoderConfig, DEFAULT_CONFIG } from './config';
2
- import { Encoder } from './core/Encoder';
1
+ import { DEFAULT_CONFIG, EncoderConfig } from './config';
3
2
  import { Decoder, DecodeResult } from './core/Decoder';
3
+ import { Encoder } from './core/Encoder';
4
4
  import { parseDataToNode } from './core/Parser';
5
+ import { Meta, MetaInfo, MetaProps } from './models/Meta';
5
6
  import { Node } from './models/Node';
6
7
  import { Schema, SchemaKind } from './models/Schema';
7
- import { MetaInfo, Meta, MetaProps } from './models/Meta';
8
8
 
9
9
  // Re-export types and classes
10
- export { Node, Schema, SchemaKind, DecodeResult, EncoderConfig,
11
- Meta, MetaInfo, MetaProps };
10
+ export { DecodeResult, EncoderConfig, Meta, MetaInfo, MetaProps, Node, Schema, SchemaKind };
12
11
 
13
12
  // =============================================================
14
13
  // PUBLIC API
@@ -17,30 +16,29 @@ export { Node, Schema, SchemaKind, DecodeResult, EncoderConfig,
17
16
  /**
18
17
  * Encode input data into valid **AK Data** format.
19
18
  */
20
- export function encode(data: any | Node, config: Partial<EncoderConfig> = {}): string {
21
- let node: Node;
22
- node = parseDataToNode(data);
23
- const finalConfig: EncoderConfig = { ...DEFAULT_CONFIG, ...config };
24
- const encoder = new Encoder(finalConfig);
25
- return encoder.encode(node);
19
+ export function encode(data: Node | unknown, config: Partial<EncoderConfig> = {}): string {
20
+ const node = parseDataToNode(data);
21
+ const finalConfig: EncoderConfig = { ...DEFAULT_CONFIG, ...config };
22
+ const encoder = new Encoder(finalConfig);
23
+ return encoder.encode(node);
26
24
  }
27
25
 
28
26
  /**
29
27
  * Decode AK Data format text into a Node structure.
30
28
  */
31
29
  export function decode(
32
- text: string,
33
- options: { removeAnsiColors?: boolean; debug?: boolean } = {},
34
- schema: string = ""
30
+ text: string,
31
+ options: { removeAnsiColors?: boolean; debug?: boolean } = {},
32
+ schema: string = '',
35
33
  ): DecodeResult {
36
- const { removeAnsiColors = false, debug = false } = options;
37
- const decoder = new Decoder(text, schema, removeAnsiColors, debug);
38
- return decoder.decode();
34
+ const { removeAnsiColors = false, debug = false } = options;
35
+ const decoder = new Decoder(text, schema, removeAnsiColors, debug);
36
+ return decoder.decode();
39
37
  }
40
38
 
41
39
  /**
42
40
  * Convert raw JavaScript object/value into a Node.
43
41
  */
44
- export function parse(data: any): Node {
45
- return parseDataToNode(data);
46
- }
42
+ export function parse(data: unknown): Node {
43
+ return parseDataToNode(data);
44
+ }
@@ -1,53 +1,54 @@
1
+ import { Primitive } from './Node';
1
2
 
2
3
  export interface MetaProps {
3
- comments?: string[];
4
- attr?: Record<string, any>;
5
- tags?: string[];
4
+ comments?: string[];
5
+ attr?: Record<string, Primitive>;
6
+ tags?: string[];
6
7
  }
7
8
 
8
9
  /**
9
10
  * Mixin/Base class that adds metadata storage capabilities to Node and Schema.
10
11
  */
11
12
  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] : [];
13
+ comments: string[];
14
+ attr: Record<string, Primitive>;
15
+ tags: string[];
16
+
17
+ constructor({ comments, attr, tags }: MetaProps = {}) {
18
+ this.comments = comments ? [...comments] : [];
19
+ this.attr = attr ? { ...attr } : {};
20
+ this.tags = tags ? [...tags] : [];
21
+ }
22
+
23
+ /**
24
+ * Clears ALL metadata (comments, attributes, tags).
25
+ */
26
+ clearCommonMeta(): void {
27
+ this.comments = [];
28
+ this.attr = {};
29
+ this.tags = [];
30
+ }
31
+
32
+ /**
33
+ * Merges only the common fields (attributes, comments, tags)
34
+ * from a MetaInfo object. Safe for both Node and Schema.
35
+ */
36
+ applyCommonMeta(info: Meta): void {
37
+ // Append comments
38
+ if (info.comments.length > 0) {
39
+ this.comments.push(...info.comments);
20
40
  }
21
41
 
22
- /**
23
- * Clears ALL metadata (comments, attributes, tags).
24
- */
25
- clearCommonMeta(): void {
26
- this.comments = [];
27
- this.attr = {};
28
- this.tags = [];
42
+ // Merge attributes ($key=value)
43
+ if (Object.keys(info.attr).length > 0) {
44
+ Object.assign(this.attr, info.attr);
29
45
  }
30
46
 
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
- }
47
+ // Append tags
48
+ if (info.tags.length > 0) {
49
+ this.tags.push(...info.tags);
50
50
  }
51
+ }
51
52
  }
52
53
 
53
54
  /**
@@ -55,81 +56,80 @@ export class Meta {
55
56
  * It contains BOTH Schema constraints (!required) and Node attributes ($key=val).
56
57
  */
57
58
  export class MetaInfo extends Meta {
58
- required: boolean;
59
-
60
- constructor(props: MetaProps & { required?: boolean } = {}) {
61
- super(props);
62
- this.required = props.required || false;
59
+ required: boolean;
60
+
61
+ constructor(props: MetaProps & { required?: boolean } = {}) {
62
+ super(props);
63
+ this.required = props.required || false;
64
+ }
65
+
66
+ /**
67
+ * Merges everything from another MetaInfo object, including 'required' flag.
68
+ */
69
+ applyMeta(info: MetaInfo): void {
70
+ // Apply common fields (comments, attr, tags)
71
+ this.applyCommonMeta(info);
72
+
73
+ // Override required meta (Schema Only)
74
+ if (info.required) {
75
+ this.required = true;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Allows usage checks like 'if meta.isEmpty()' to see if any metadata was collected.
81
+ * Equivalent to Python's __bool__.
82
+ */
83
+ isEmpty(): boolean {
84
+ return (
85
+ this.comments.length === 0 &&
86
+ Object.keys(this.attr).length === 0 &&
87
+ this.tags.length === 0 &&
88
+ !this.required
89
+ );
90
+ }
91
+
92
+ /**
93
+ * Debug representation mimicking the actual ADF format style.
94
+ * Example: <MetaInfo !required #tag $key=val >
95
+ */
96
+ toString(): string {
97
+ const parts: string[] = [];
98
+
99
+ // 1. Flags
100
+ if (this.required) {
101
+ parts.push('!required');
63
102
  }
64
103
 
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
- }
104
+ // 2. Tags
105
+ for (const t of this.tags) {
106
+ parts.push(`#${t}`);
76
107
  }
77
108
 
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
- );
109
+ // 3. Attributes
110
+ for (const [k, v] of Object.entries(this.attr)) {
111
+ // Simplistic value repr for debug
112
+ let valStr: string;
113
+ if (typeof v === 'string') {
114
+ valStr = `"${v}"`;
115
+ } else {
116
+ valStr = String(v);
117
+ }
118
+ parts.push(`$${k}=${valStr}`);
89
119
  }
90
120
 
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)>";
121
+ // 4. Comments (Summary)
122
+ if (this.comments.length > 0) {
123
+ if (this.comments.length === 1) {
124
+ const c = this.comments[0];
125
+ const preview = c.length > 15 ? c.substring(0, 15) + '..' : c;
126
+ parts.push(`/* ${preview} */`);
127
+ } else {
128
+ parts.push(`/* ${this.comments.length} comments */`);
129
+ }
133
130
  }
134
131
 
135
- }
132
+ const content = parts.join(' ');
133
+ return content ? `<MetaInfo ${content}>` : '<MetaInfo (empty)>';
134
+ }
135
+ }