@arkadia/data 0.1.9 → 0.1.11

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.
@@ -8,7 +8,7 @@ describe('AK Data Metadata (Meta)', () => {
8
8
  // ==================================================================================
9
9
 
10
10
  it('should handle comments', () => {
11
- /** Validates that comments /.../ are ignored or handled. */
11
+ /** Validates that comments / * ... * / are handled correctly. */
12
12
  const text = '@User<id:int /*primary key*/, name:string> @User(5, "Bob")';
13
13
  // Note: int normalizes to number in TS
14
14
  const expected = '@User</*primary key*/ id:number,name:string>(5,"Bob")';
@@ -16,8 +16,6 @@ describe('AK Data Metadata (Meta)', () => {
16
16
  const res = decode(text, { debug: false });
17
17
  expect(res.errors).toHaveLength(0);
18
18
 
19
- // If the parser attaches schema comments to fields correctly, good.
20
- // Here we just ensure data parsing works despite comments.
21
19
  expect(res.node.fields['id'].value).toBe(5);
22
20
 
23
21
  assertRoundtrip(text, expected, false);
@@ -28,14 +26,14 @@ describe('AK Data Metadata (Meta)', () => {
28
26
  $a0=5
29
27
  <
30
28
  /* c1 */
31
- / $a1 /* c0 *//
29
+ // $a1 /* c0 *///
32
30
  /* c2 */ $a2=2 /* c3 */ $a3=3 a:number
33
31
  >
34
32
  ($a6 /*a*/ 3)
35
33
  `;
36
34
 
37
35
  const expected =
38
- '<//*c0*/ $a0=5 $a1=true/ /*c1*/ /*c2*/ /*c3*/ $a2=2 $a3=3 a:number>(/*a*/ $a6=true 3)';
36
+ '<///*c0*/ $a0=5 $a1// /*c1*/ /*c2*/ /*c3*/ $a2=2 $a3=3 a:number>(/*a*/ $a6 3)';
39
37
  assertRoundtrip(akdText, expected, false);
40
38
  });
41
39
 
@@ -44,21 +42,20 @@ describe('AK Data Metadata (Meta)', () => {
44
42
  $attr=5
45
43
  <
46
44
  /* comm2 */
47
- / $schema1 /
45
+ // $schema1 //
48
46
  /* comm1 */
49
47
  [a:int]
50
48
  >
51
49
  $attr=3
52
50
  [
53
- / /*meta for list*/ $attr=4 /
51
+ // /*meta for list*/ $attr=4 //
54
52
  /*item1*/ $attr5 (3 $attr6),
55
53
  /*item2*/ {a:5},
56
54
  ]
57
55
  `;
58
56
 
59
- // Note: int -> number
60
57
  const expected =
61
- '<[//*comm2*/ /*comm1*/ $attr=5 $schema1=true/ a:number]>[//*meta for list*/ $attr=4/ (//*item1*/ $attr5=true/ $attr6=true 3),(//*item2*// 5)]';
58
+ '<[///*comm2*/ /*comm1*/ $attr=5 $schema1// a:number]>[///*meta for list*/ $attr=4// (///*item1*/ $attr5// $attr6 3),(///*item2*/// 5)]';
62
59
  assertRoundtrip(akdText, expected, false);
63
60
  });
64
61
 
@@ -67,17 +64,11 @@ describe('AK Data Metadata (Meta)', () => {
67
64
  // ==============================================================================
68
65
 
69
66
  it('should handle list schema with meta', () => {
70
- /**
71
- * Verifies that an empty schema definition < ... > correctly parses:
72
- * 1. Comments (including nested ones).
73
- * 2. Attributes ($key=val).
74
- * 3. Tags (#tag).
75
- */
76
67
  const akdText = `
77
68
  /* 0 */
78
69
  <
79
70
  /* commentm0 */ /* com1 /*com1.2*/ */
80
- / $listAttr="GlobalList" $b=4 #tag /
71
+ // $listAttr="GlobalList" $b=4 #tag //
81
72
  /* comment4 */
82
73
  id:number
83
74
  >
@@ -93,152 +84,78 @@ describe('AK Data Metadata (Meta)', () => {
93
84
 
94
85
  const result = decode(akdText, { debug: false });
95
86
  const node = result.node;
96
- const errors = result.errors;
97
87
 
98
- // 1. Assert no syntax errors
99
- expect(errors).toHaveLength(0);
88
+ expect(result.errors).toHaveLength(0);
100
89
 
101
- // 2. Check Schema Basics
102
90
  const schema = node.schema;
103
91
  expect(schema).not.toBeNull();
104
- expect(schema.kind).toBe(SchemaKind.LIST); // Default kind for <...>
105
-
106
- // 3. Verify Attributes ($key=val)
107
- // Parser casts to number if possible
108
- expect(schema.attr['listAttr']).toBe('GlobalList');
109
- expect(schema.attr['b']).toBe(4);
92
+ expect(schema!.kind).toBe(SchemaKind.LIST);
110
93
 
111
- // 4. Verify Tags (#tag)
112
- expect(schema.tags).toContain('tag');
113
- expect(schema.tags).toHaveLength(1);
114
-
115
- // 5. Verify Comments
116
- // We expect multiple comments to be collected
117
- expect(schema.comments.length).toBeGreaterThan(0);
118
- expect(schema.comments.some((c) => c.includes('0'))).toBe(true);
94
+ expect(schema!.attr['listAttr']).toBe('GlobalList');
95
+ expect(schema!.attr['b']).toBe(4);
96
+ expect(schema!.tags).toContain('tag');
119
97
 
120
98
  const expected =
121
- '<[//*0*/ $listAttr="GlobalList" $b=4 #tag/ number]>[//*a*/ /*b*/ $val=3 #tag1/ 1,2,3]';
99
+ '<[///*0*/ $listAttr="GlobalList" $b=4 #tag// number]>[///*a*/ /*b*/ $val=3 #tag1// 1,2,3]';
122
100
  assertRoundtrip(akdText, expected, false);
123
101
  });
124
102
 
125
103
  it('should encode manual schema with meta', () => {
126
- /**
127
- * Verifies that a Schema with meta/comments is encoded correctly.
128
- * Expected format: < / $attr=val #tag / any >
129
- */
130
-
131
- // 1. Prepare Schema manually
132
104
  const schema = new Schema(SchemaKind.RECORD);
133
105
  schema.comments = ['comment1', 'comment2'];
134
106
  schema.attr = { key: 'value', count: 10 };
135
107
  schema.tags = ['myTag'];
136
108
 
137
- // Create a node using this schema
138
109
  const node = new Node(schema, { value: null });
139
- const expected = '<//*comment1*/ /*comment2*/ $key="value" $count=10 #myTag/ any>(null)';
110
+ const expected = '<///*comment1*/ /*comment2*/ $key="value" $count=10 #myTag// any>(null)';
140
111
 
141
112
  assertRoundtrip(node, expected, false);
142
113
  });
143
114
 
144
115
  it('should round trip schema encode decode', () => {
145
- /**
146
- * Verifies that a Schema with meta/comments can be encoded to text
147
- * and then decoded back, preserving all metadata (Round-Trip).
148
- */
149
-
150
- // 1. Prepare Schema manually
151
116
  const originalSchema = new Schema(SchemaKind.RECORD);
152
117
  originalSchema.comments = ['comment1', 'comment2'];
153
118
  originalSchema.attr = { key: 'value', count: 10, isActive: true };
154
119
  originalSchema.tags = ['myTag', 'urgent'];
155
120
 
156
- // Create a node using this schema
157
121
  const originalNode = new Node(originalSchema, { value: null });
158
122
 
159
- // 2. Encode to String
160
- // Important: We must enable include_comments to verify them after decoding
161
123
  const encodedText = encode(originalNode, {
162
124
  includeComments: true,
163
- compact: true, // Test compact mode (one line)
125
+ compact: true,
164
126
  colorize: false,
165
127
  });
166
128
 
167
- // 3. Decode back to Node
168
129
  const result = decode(encodedText, { debug: false });
169
130
  const decodedNode = result.node;
170
- const errors = result.errors;
171
-
172
- // 4. Verify No Errors
173
- expect(errors).toHaveLength(0);
174
- expect(decodedNode).not.toBeNull();
175
131
 
176
- // 5. Verify Schema Integrity
177
- const decodedSchema = decodedNode.schema;
178
- expect(decodedSchema).not.toBeNull();
179
- expect(decodedSchema.kind).toBe(SchemaKind.RECORD);
180
-
181
- // 6. Verify Meta Data (Attributes)
182
- expect(decodedSchema.attr['key']).toBe('value');
183
- expect(decodedSchema.attr['count']).toBe(10);
184
- expect(decodedSchema.attr['isActive']).toBe(true);
185
-
186
- // 7. Verify Tags
187
- expect(decodedSchema.tags).toContain('myTag');
188
- expect(decodedSchema.tags).toContain('urgent');
189
- expect(decodedSchema.tags).toHaveLength(2);
190
-
191
- // 8. Verify Comments
192
- expect(decodedSchema.comments).toHaveLength(2);
193
- expect(decodedSchema.comments).toContain('comment1');
194
- expect(decodedSchema.comments).toContain('comment2');
132
+ expect(result.errors).toHaveLength(0);
133
+ expect(decodedNode.schema!.attr['key']).toBe('value');
134
+ expect(decodedNode.schema!.tags).toContain('urgent');
195
135
 
196
- // Additional checks for configuration flags
136
+ // Test cleaning meta
197
137
  const decodedTextClean = encode(decodedNode, {
198
138
  compact: true,
199
139
  includeMeta: false,
200
140
  includeComments: false,
201
141
  });
202
142
 
203
- // Assertions for No Meta
204
143
  expect(decodedTextClean).not.toContain('$key');
205
- expect(decodedTextClean).not.toContain('#myTag');
206
- expect(decodedTextClean).not.toContain('/'); // No meta block
207
- // Empty schema without meta encodes to <any> or similar
208
- expect(decodedTextClean).toContain('<any>');
209
-
210
- // B. Encode WITH Meta (includeMeta=true) but NO Type (includeType=false)
211
- const decodedTextWithMeta = encode(decodedNode, {
212
- compact: true,
213
- includeType: false,
214
- includeMeta: true,
215
- includeComments: false,
216
- });
217
-
218
- // Assertions for With Meta
219
- expect(decodedTextWithMeta).toContain('$key=');
220
- expect(decodedTextWithMeta).toContain('#myTag');
221
- expect(decodedTextWithMeta).toContain('$count=10');
222
- expect(decodedTextWithMeta).toContain('/');
144
+ expect(decodedTextClean).not.toContain('//');
223
145
 
224
146
  const expected =
225
- '<//*comment1*/ /*comment2*/ $key="value" $count=10 $isActive=true #myTag #urgent/ any>(null)';
147
+ '<///*comment1*/ /*comment2*/ $key="value" $count=10 $isActive #myTag #urgent// any>(null)';
226
148
  assertRoundtrip(originalNode, expected, false);
227
149
  });
228
150
 
229
151
  it('should handle meta schema list vs element', () => {
230
- /**
231
- * Tests nested metadata within a type definition:
232
- * Outer: / $listAttr="GlobalList" / -> Applies to the entire List
233
- * Inner: / $elemAttr="InnerRecord" / -> Applies to the Element (Record) inside
234
- */
235
152
  const akdText = `
236
153
  <
237
154
  /* comm-header-0 */ /* comm-header-1 /* comm-header-1.1*/ */
238
- / $listAttr="GlobalList" $b=4 /*com-in*/ /
155
+ // $listAttr="GlobalList" $b=4 /*com-in*/ //
239
156
  /* comm-after-header-0 */
240
157
  [
241
- / $elemAttr="InnerRecord" #elem0 /* comm-inside-header-0 */ /
158
+ // $elemAttr="InnerRecord" #elem0 /* comm-inside-header-0 */ //
242
159
  /* comm-inside-field-0 */ #elem1 id: int
243
160
  ]
244
161
  >
@@ -247,30 +164,13 @@ describe('AK Data Metadata (Meta)', () => {
247
164
 
248
165
  const results = decode(akdText, { debug: false });
249
166
  const node = results.node;
250
- const errors = results.errors;
251
-
252
- expect(errors).toHaveLength(0);
253
-
254
- // 1. Check List Meta (Outer)
255
- expect(node.isList).toBe(true);
256
- // Access attributes via .attr (Mixin)
257
- expect(node.schema.attr['listAttr']).toBe('GlobalList');
258
- expect(node.schema.attr['elemAttr']).toBe('InnerRecord');
259
-
260
- expect(node.schema.attr['b']).toBe(4);
261
167
 
262
- // 2. Check Element Meta (Inner Record)
263
- const elemSchema = node.schema.element!;
264
- expect(elemSchema.kind).toBe(SchemaKind.RECORD);
265
- expect(elemSchema.attr).toStrictEqual({});
266
-
267
- // Check if element meta propagated to actual data elements (depends on implementation)
268
- // Usually schema meta is on schema, data meta is on data node.
269
- // Here we check the schema attached to the element node.
270
- expect(node.elements[0].schema.attr).toStrictEqual({});
168
+ expect(results.errors).toHaveLength(0);
169
+ expect(node.schema!.attr['listAttr']).toBe('GlobalList');
170
+ expect(node.schema!.attr['elemAttr']).toBe('InnerRecord');
271
171
 
272
172
  const expected =
273
- '<[//*com-in*/ /*comm-header-0*/ /*comm-header-1 /* comm-header-1.1*/*/ /*comm-after-header-0*/ /*comm-inside-header-0*/ $listAttr="GlobalList" $b=4 $elemAttr="InnerRecord" #elem0/ /*comm-inside-field-0*/ #elem1 id:number]>[(//*comm-data-v1*/ /*comm-data-v2*// 1)]';
173
+ '<[///*com-in*/ /*comm-header-0*/ /*comm-header-1 /* comm-header-1.1*/*/ /*comm-after-header-0*/ /*comm-inside-header-0*/ $listAttr="GlobalList" $b=4 $elemAttr="InnerRecord" #elem0// /*comm-inside-field-0*/ #elem1 id:number]>[(///*comm-data-v1*/ /*comm-data-v2*/// 1)]';
274
174
  assertRoundtrip(akdText, expected, false);
275
175
  });
276
176
 
@@ -278,12 +178,12 @@ describe('AK Data Metadata (Meta)', () => {
278
178
  const akdText = `
279
179
  <
280
180
  /* header-com-0 */
281
- / #tag_header /
181
+ // #tag_header //
282
182
  /* comm-data-v1 */ #tag1 v1: number /* comm-data-v2 */ #tag2,
283
183
  /* comm-data-v3 */ #tag3 v2: number /* comm-data-v3 */ #tag4
284
184
  >
285
185
  [
286
- / #tag_list /
186
+ // #tag_list //
287
187
  /* comm-data-v1 */ #tag1 1 /* comm-data-v2 */ #tag2
288
188
  /* comm-data-v3 */ #tag3 2 /* comm-data-v3 */ #tag4
289
189
  ]
@@ -293,21 +193,17 @@ describe('AK Data Metadata (Meta)', () => {
293
193
  expect(results.errors).toHaveLength(0);
294
194
 
295
195
  const expected =
296
- '<[/#tag_header/ number]>[/#tag_list/ /*comm-data-v1*/ #tag1 1,/*comm-data-v2*/ /*comm-data-v3*/ /*comm-data-v3*/ #tag2 #tag3 #tag4 2]';
196
+ '<[//#tag_header// number]>[//#tag_list// /*comm-data-v1*/ #tag1 1,/*comm-data-v2*/ /*comm-data-v3*/ /*comm-data-v3*/ #tag2 #tag3 #tag4 2]';
297
197
  assertRoundtrip(akdText, expected, false);
298
198
  });
299
199
 
300
200
  it('should warn on meta schema with implicit values', () => {
301
- /**
302
- * Tests handling of malformed meta blocks.
303
- * In the input: / listAttr="GlobalList" / is missing '$' prefix for attribute.
304
- */
305
201
  const akdText = `
306
202
  <
307
- / listAttr="GlobalList" /
203
+ // listAttr="GlobalList" //
308
204
  [
309
205
  /* Missing $ prefix */
310
- / $elemAttr="InnerRecord" /* fixed input */ /
206
+ // $elemAttr="InnerRecord" /* fixed input */ //
311
207
  /* comments2 */ id: int
312
208
  ]
313
209
  >
@@ -315,123 +211,69 @@ describe('AK Data Metadata (Meta)', () => {
315
211
  `;
316
212
 
317
213
  const results = decode(akdText, { debug: false });
318
- const node = results.node;
319
- const warnings = results.warnings;
320
-
321
- // If the input was fixed above, errors should be 0.
322
- // Because we have: listAttr="GlobalList" (implicit), it should be a warning.
323
- expect(warnings.length).toBeGreaterThan(0);
324
- expect(warnings[0].message).toContain("Implicit attribute 'listAttr'");
325
-
326
- // 1. Check List Meta (Outer)
327
- expect(node.isList).toBe(true);
328
- expect(node.schema.attr['listAttr']).toBe('GlobalList');
329
-
330
- // 2. Check Element Meta (Inner Record)
331
- const elemSchema = node.schema.element!;
332
- expect(elemSchema.kind).toBe(SchemaKind.RECORD);
333
- expect(node.schema.attr['elemAttr']).toBe('InnerRecord');
214
+ expect(results.warnings.length).toBeGreaterThan(0);
215
+ expect(results.warnings[0].message).toContain("Implicit attribute 'listAttr'");
334
216
 
335
217
  const expected =
336
- '<[//*fixed input*/ $listAttr="GlobalList" $elemAttr="InnerRecord"/ /*Missing $ prefix*/ /*comments2*/ id:number]>[(1)]';
218
+ '<[///*fixed input*/ $listAttr="GlobalList" $elemAttr="InnerRecord"// /*Missing $ prefix*/ /*comments2*/ id:number]>[(1)]';
337
219
  assertRoundtrip(akdText, expected, false);
338
220
  });
339
221
 
340
222
  it('should handle meta schema field modifiers', () => {
341
- /**
342
- * Tests field modifiers inside a record definition: !required, $key=value.
343
- */
344
223
  const akdText = `
345
224
  <
346
225
  /* comm0 */
347
- / $id=0 /*comm2 /* comm2.5*/ */ /
226
+ // $id=0 /*comm2 /* comm2.5*/ */ //
348
227
 
349
228
  /* comm3 */
350
229
 
351
230
  /* Modifiers block before field name */
352
- !required $key=101 id:int,
231
+ $required $key=101 id:int,
353
232
 
354
233
  $desc="User Name"
355
234
  name: string
356
235
  >
357
- ( /* comment0 */ / $id=3 /*comment2*/ / /*comment3*/ 1, "Alice" $id=65 #alice /*comment4*/ )
236
+ ( /* comment0 */ // $id=3 /*comment2*/ // /*comment3*/ 1, "Alice" $id=65 #alice /*comment4*/ )
358
237
  `;
359
238
 
360
239
  const results = decode(akdText, { debug: false });
361
240
  const node = results.node;
362
241
  expect(results.errors).toHaveLength(0);
363
242
 
364
- expect(node.isRecord).toBe(true);
365
-
366
- // Retrieve field definitions from schema
367
- const fields = node.schema.fields;
368
-
369
- // Field 'id'
370
- const fId = fields.find((f) => f.name === 'id')!;
371
- expect(fId).toBeDefined();
243
+ const fId = node.schema!.fields.find((f) => f.name === 'id')!;
372
244
  expect(fId.required).toBe(true);
373
245
  expect(fId.attr['key']).toBe(101);
374
-
375
- // Field 'name'
376
- const fName = fields.find((f) => f.name === 'name')!;
377
- expect(fName).toBeDefined();
378
- expect(fName.required).toBe(false); // Default
379
- expect(fName.attr['desc']).toBe('User Name');
380
-
381
- // Check Instance Data Meta (the node itself, not the schema)
382
- // The record instance has / $id=3 /
383
246
  expect(node.attr['id']).toBe(3);
384
247
 
385
248
  const expected =
386
- '<//*comm2 /* comm2.5*/*/ $id=0/ /*comm0*/ /*comm3*/ /*Modifiers block before field name*/ !required $key=101 id:number,$desc="User Name" name:string>(//*comment2*/ $id=3/ /*comment0*/ /*comment3*/ 1,/*comment4*/ $id=65 #alice "Alice")';
249
+ '<///*comm2 /* comm2.5*/*/ $id=0// /*comm0*/ /*comm3*/ /*Modifiers block before field name*/ $required $key=101 id:number,$desc="User Name" name:string>(///*comment2*/ $id=3// /*comment0*/ /*comment3*/ 1,/*comment4*/ $id=65 #alice "Alice")';
387
250
  assertRoundtrip(akdText, expected, false);
388
251
  });
389
252
 
390
- // ==============================================================================
391
- // 2. DATA BLOCK META (Metadata inside data blocks [ ... ])
392
- // ==============================================================================
393
-
394
253
  it('should handle meta data block list primitive', () => {
395
- /**
396
- * Tests metadata inside a data block for a simple list.
397
- * Syntax: [ / @size=3 / 1, 2, 3 ]
398
- */
399
- const akdText = '[ / $size=3 $author="me" / 1, 2, 3 ]';
254
+ const akdText = '[ // $size=3 $author="me" // 1, 2, 3 ]';
400
255
 
401
256
  const results = decode(akdText, { debug: false });
402
257
  const node = results.node;
403
258
  expect(results.errors).toHaveLength(0);
404
259
 
405
- expect(node.isList).toBe(true);
406
- // Meta should go to this specific node's attributes
407
260
  expect(node.attr['size']).toBe(3);
408
261
  expect(node.attr['author']).toBe('me');
409
262
 
410
- // Check content
411
- expect(node.elements).toHaveLength(3);
412
- expect(node.elements[0].value).toBe(1);
413
-
414
- const expected = '<[number]>[/$size=3 $author="me"/ 1,2,3]';
263
+ const expected = '<[number]>[//$size=3 $author="me"// 1,2,3]';
415
264
  assertRoundtrip(akdText, expected, false);
416
265
  });
417
266
 
418
- // ==============================================================================
419
- // 3. NESTED META (Lists within lists)
420
- // ==============================================================================
421
-
422
267
  it('should handle meta nested lists', () => {
423
- /**
424
- * Tests metadata assignment in nested lists.
425
- */
426
268
  const akdText = `
427
269
  [
428
- / $level=0 /
270
+ // $level=0 //
429
271
  [
430
- / $level=1 /
272
+ // $level=1 //
431
273
  1, 2
432
274
  ],
433
275
  [
434
- / $level=2 /
276
+ // $level=2 //
435
277
  3, 4
436
278
  ]
437
279
  ]
@@ -441,70 +283,34 @@ describe('AK Data Metadata (Meta)', () => {
441
283
  const node = results.node;
442
284
  expect(results.errors).toHaveLength(0);
443
285
 
444
- // Root Node
445
- expect(node.isList).toBe(true);
446
286
  expect(node.attr['level']).toBe(0);
287
+ expect(node.elements[0].attr['level']).toBe(1);
288
+ expect(node.elements[1].attr['level']).toBe(2);
447
289
 
448
- // Inner Node 1
449
- const inner1 = node.elements[0];
450
- expect(inner1.isList).toBe(true);
451
- expect(inner1.attr['level']).toBe(1);
452
-
453
- // Inner Node 2
454
- const inner2 = node.elements[1];
455
- expect(inner2.isList).toBe(true);
456
- expect(inner2.attr['level']).toBe(2);
457
-
458
- const expected = '<[[number]]>[/$level=0/ [/$level=1/ 1,2],[/$level=2/ 3,4]]';
290
+ const expected = '<[[number]]>[//$level=0// [//$level=1// 1,2],[//$level=2// 3,4]]';
459
291
  assertRoundtrip(akdText, expected, false);
460
292
  });
461
293
 
462
- // ==============================================================================
463
- // 4. EDGE CASES & OVERRIDES
464
- // ==============================================================================
465
-
466
294
  it('should handle meta mixed with type override', () => {
467
- /**
468
- * Tests a scenario where we have metadata for the list AND a type override for an element.
469
- */
470
- const akdText = '[ / $info="mixed" / 1, 2, <string> "3" ]';
471
- const expected = '<[number]>[/$info="mixed"/ 1,2,<string> "3"]';
295
+ const akdText = '[ // $info="mixed" // 1, 2, <string> "3" ]';
296
+ const expected = '<[number]>[//$info="mixed"// 1,2,<string> "3"]';
472
297
 
473
298
  const result = decode(akdText, { debug: false });
474
- const node = result.node;
475
299
  expect(result.errors).toHaveLength(0);
300
+ expect(result.node.attr['info']).toBe('mixed');
301
+ expect(result.node.schema!.element?.typeName).toBe('number');
476
302
 
477
- // List Meta
478
- expect(node.attr['info']).toBe('mixed');
479
-
480
- // List Type Inference (Should be Number based on first element '1')
481
- expect(node.schema.element?.typeName).toBe('number');
482
-
483
- // Element Override
484
- const elLast = node.elements[2];
485
- expect(elLast.schema.typeName).toBe('string');
486
- expect(elLast.value).toBe('3');
487
-
488
- assertRoundtrip(node, expected, false);
303
+ assertRoundtrip(result.node, expected, false);
489
304
  });
490
305
 
491
306
  it('should handle meta and explicit type in data', () => {
492
- /**
493
- * Tests a scenario where an explicit type is provided inside the / ... / block.
494
- * The parser must understand that type is the list type, and @tag is metadata.
495
- */
496
- const akdText = '[ / $tag=1 / 1, 2 ]';
307
+ const akdText = '[ // $tag=1 // 1, 2 ]';
497
308
 
498
309
  const result = decode(akdText, { debug: false });
499
- const node = result.node;
500
310
  expect(result.errors).toHaveLength(0);
311
+ expect(result.node.attr['tag']).toBe(1);
501
312
 
502
- expect(node.isList).toBe(true);
503
- // Inferred type
504
- expect(node.schema.element?.typeName).toBe('number');
505
- expect(Object.keys(node.attr)).toHaveLength(1);
506
-
507
- const expected = '<[number]>[/$tag=1/ 1,2]';
508
- assertRoundtrip(node, expected, false);
313
+ const expected = '<[number]>[//$tag=1// 1,2]';
314
+ assertRoundtrip(result.node, expected, false);
509
315
  });
510
316
  });
@@ -0,0 +1,117 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { decode, encode } from '../src/index';
3
+
4
+ // ==============================================================================
5
+ // 1. PROMPT OUTPUT TESTS (LLM-Friendly Blueprint Mode)
6
+ // ==============================================================================
7
+
8
+ const PROMPT_CONFIG = {
9
+ promptOutput: true,
10
+ compact: false,
11
+ includeSchema: false,
12
+ colorize: false,
13
+ indent: 2,
14
+ };
15
+
16
+ describe('AK Data Prompt Output (LLM Blueprints)', () => {
17
+ it('should transform a record into a key-type-comment blueprint', () => {
18
+ /**
19
+ * Validates that promptOutput=true transforms a record into a
20
+ * key-type-comment blueprint using { } braces instead of ( ) parentheses.
21
+ */
22
+ const akdText = `
23
+ @User <
24
+ id: number /* unique id */,
25
+ name: string /* display name */
26
+ >
27
+ `;
28
+
29
+ const result = decode(akdText);
30
+ expect(result.errors).toHaveLength(0);
31
+
32
+ const output = encode(result.node, PROMPT_CONFIG).trim();
33
+
34
+ const expected = [
35
+ '{',
36
+ ' id: number /* unique id */,',
37
+ ' name: string /* display name */',
38
+ '}',
39
+ ].join('\n');
40
+ expect(output).toBe(expected);
41
+ });
42
+
43
+ it('should show a single example element inside a list with a continuation comment', () => {
44
+ /**
45
+ * Validates that promptOutput=true shows only a single example element
46
+ * inside a list with a continuation comment (...).
47
+ */
48
+ const akdText = `
49
+ <[ /* id */ id: number, name: string, val: <id: string, num: number> ]>
50
+ [ (1, "n", ("id", 3)), (2), (3) ]
51
+ `;
52
+
53
+ const result = decode(akdText);
54
+ expect(result.errors).toHaveLength(0);
55
+
56
+ const output = encode(result.node, PROMPT_CONFIG).trim();
57
+
58
+ const expected = [
59
+ '[',
60
+ ' {',
61
+ ' id: number /* id */,',
62
+ ' name: string,',
63
+ ' val: {',
64
+ ' id: string,',
65
+ ' num: number',
66
+ ' }',
67
+ ' },',
68
+ ' ... /* repeat pattern for additional items */',
69
+ ']',
70
+ ].join('\n');
71
+
72
+ expect(output).toBe(expected);
73
+ });
74
+
75
+ it('should expand nested structures into blueprints in prompt mode', () => {
76
+ /**
77
+ * Verifies that nested structures also expand into blueprints in prompt mode.
78
+ */
79
+ const akdText = `
80
+ <
81
+ name: string,
82
+ meta: < ver: number /* version number */ >
83
+ >
84
+ ("App", (1.0))
85
+ `;
86
+
87
+ const result = decode(akdText);
88
+ expect(result.errors).toHaveLength(0);
89
+
90
+ const output = encode(result.node, PROMPT_CONFIG).trim();
91
+
92
+ const expected = [
93
+ '{',
94
+ ' name: string,',
95
+ ' meta: {',
96
+ ' ver: number /* version number */',
97
+ ' }',
98
+ '}',
99
+ ].join('\n');
100
+ expect(output).toBe(expected);
101
+ });
102
+
103
+ it('should preserve escaped identifiers (backticks) in prompt mode', () => {
104
+ /**
105
+ * Ensures that escaped identifiers (backticks) are preserved in prompt mode.
106
+ */
107
+ const akdText = '< `User ID`: number /* system id */ > (123)';
108
+
109
+ const result = decode(akdText);
110
+ expect(result.errors).toHaveLength(0);
111
+
112
+ const output = encode(result.node, PROMPT_CONFIG).trim();
113
+
114
+ const expected = ['{', ' `User ID`: number /* system id */', '}'].join('\n');
115
+ expect(output).toBe(expected);
116
+ });
117
+ });