links-notation 0.11.2 → 0.13.0

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.
@@ -1,10 +1,10 @@
1
- import { test, expect } from "bun:test";
2
- import { Parser } from "../src/Parser.js";
3
- import { formatLinks } from "../src/Link.js";
1
+ import { test, expect } from 'bun:test';
2
+ import { Parser } from '../src/Parser.js';
3
+ import { formatLinks } from '../src/Link.js';
4
4
 
5
5
  const parser = new Parser();
6
6
 
7
- test("Hero example - mixed modes - issue #105", () => {
7
+ test('Hero example mixed modes', () => {
8
8
  const input = `empInfo
9
9
  employees:
10
10
  (
@@ -24,14 +24,14 @@ test("Hero example - mixed modes - issue #105", () => {
24
24
 
25
25
  expect(result.length).toBeGreaterThan(0);
26
26
  const formatted = formatLinks(result);
27
- expect(formatted).toContain("empInfo");
28
- expect(formatted).toContain("employees:");
29
- expect(formatted).toContain("James Kirk");
30
- expect(formatted).toContain("Jean-Luc Picard");
31
- expect(formatted).toContain("Wesley Crusher");
27
+ expect(formatted).toContain('empInfo');
28
+ expect(formatted).toContain('employees:');
29
+ expect(formatted).toContain('James Kirk');
30
+ expect(formatted).toContain('Jean-Luc Picard');
31
+ expect(formatted).toContain('Wesley Crusher');
32
32
  });
33
33
 
34
- test("Hero example - alternative format - issue #105", () => {
34
+ test('Hero example alternative format', () => {
35
35
  const input = `empInfo
36
36
  (
37
37
  employees:
@@ -53,14 +53,14 @@ test("Hero example - alternative format - issue #105", () => {
53
53
 
54
54
  expect(result.length).toBeGreaterThan(0);
55
55
  const formatted = formatLinks(result);
56
- expect(formatted).toContain("empInfo");
57
- expect(formatted).toContain("employees:");
58
- expect(formatted).toContain("James Kirk");
59
- expect(formatted).toContain("Jean-Luc Picard");
60
- expect(formatted).toContain("Wesley Crusher");
56
+ expect(formatted).toContain('empInfo');
57
+ expect(formatted).toContain('employees:');
58
+ expect(formatted).toContain('James Kirk');
59
+ expect(formatted).toContain('Jean-Luc Picard');
60
+ expect(formatted).toContain('Wesley Crusher');
61
61
  });
62
62
 
63
- test("Hero example - equivalence test - issue #105", () => {
63
+ test('Hero example equivalence', () => {
64
64
  const version1 = `empInfo
65
65
  employees:
66
66
  (
@@ -102,7 +102,7 @@ test("Hero example - equivalence test - issue #105", () => {
102
102
  expect(formatted1).toBe(formatted2);
103
103
  });
104
104
 
105
- test("Set/object context without colon", () => {
105
+ test('Set context without colon', () => {
106
106
  const input = `empInfo
107
107
  employees`;
108
108
 
@@ -110,11 +110,11 @@ test("Set/object context without colon", () => {
110
110
 
111
111
  expect(result.length).toBeGreaterThan(0);
112
112
  const formatted = formatLinks(result);
113
- expect(formatted).toContain("empInfo");
114
- expect(formatted).toContain("employees");
113
+ expect(formatted).toContain('empInfo');
114
+ expect(formatted).toContain('employees');
115
115
  });
116
116
 
117
- test("Sequence/list context with colon", () => {
117
+ test('Sequence context with colon', () => {
118
118
  const input = `employees:
119
119
  James Kirk
120
120
  Jean-Luc Picard
@@ -125,13 +125,13 @@ test("Sequence/list context with colon", () => {
125
125
  expect(result.length).toBeGreaterThan(0);
126
126
  expect(result.length).toBe(1);
127
127
  const formatted = formatLinks(result);
128
- expect(formatted).toContain("employees:");
129
- expect(formatted).toContain("James Kirk");
130
- expect(formatted).toContain("Jean-Luc Picard");
131
- expect(formatted).toContain("Wesley Crusher");
128
+ expect(formatted).toContain('employees:');
129
+ expect(formatted).toContain('James Kirk');
130
+ expect(formatted).toContain('Jean-Luc Picard');
131
+ expect(formatted).toContain('Wesley Crusher');
132
132
  });
133
133
 
134
- test("Sequence context with complex values", () => {
134
+ test('Sequence context with complex values', () => {
135
135
  const input = `employees:
136
136
  (
137
137
  name (James Kirk)
@@ -147,12 +147,12 @@ test("Sequence context with complex values", () => {
147
147
  expect(result.length).toBeGreaterThan(0);
148
148
  expect(result.length).toBe(1);
149
149
  const formatted = formatLinks(result);
150
- expect(formatted).toContain("employees:");
151
- expect(formatted).toContain("James Kirk");
152
- expect(formatted).toContain("Jean-Luc Picard");
150
+ expect(formatted).toContain('employees:');
151
+ expect(formatted).toContain('James Kirk');
152
+ expect(formatted).toContain('Jean-Luc Picard');
153
153
  });
154
154
 
155
- test("Nested set and sequence contexts", () => {
155
+ test('Nested set and sequence contexts', () => {
156
156
  const input = `company
157
157
  departments:
158
158
  engineering
@@ -165,12 +165,12 @@ test("Nested set and sequence contexts", () => {
165
165
 
166
166
  expect(result.length).toBeGreaterThan(0);
167
167
  const formatted = formatLinks(result);
168
- expect(formatted).toContain("company");
169
- expect(formatted).toContain("departments:");
170
- expect(formatted).toContain("employees:");
168
+ expect(formatted).toContain('company');
169
+ expect(formatted).toContain('departments:');
170
+ expect(formatted).toContain('employees:');
171
171
  });
172
172
 
173
- test("Deeply nested mixed modes", () => {
173
+ test('Deeply nested mixed modes', () => {
174
174
  const input = `root
175
175
  level1
176
176
  level2:
@@ -183,6 +183,6 @@ test("Deeply nested mixed modes", () => {
183
183
 
184
184
  expect(result.length).toBeGreaterThan(0);
185
185
  const formatted = formatLinks(result);
186
- expect(formatted).toContain("root");
187
- expect(formatted).toContain("level2:");
186
+ expect(formatted).toContain('root');
187
+ expect(formatted).toContain('level2:');
188
188
  });
@@ -0,0 +1,463 @@
1
+ import { test, expect } from 'bun:test';
2
+ import { Parser } from '../src/Parser.js';
3
+
4
+ const parser = new Parser();
5
+
6
+ // Helper to extract the reference ID from a single-value link
7
+ function getSingleRefId(result) {
8
+ // Single reference parses as: { id: null, values: [{ id: "the-id", values: [] }] }
9
+ if (
10
+ result.length === 1 &&
11
+ result[0].id === null &&
12
+ result[0].values.length === 1
13
+ ) {
14
+ return result[0].values[0].id;
15
+ }
16
+ return result[0]?.id;
17
+ }
18
+
19
+ // ============================================================================
20
+ // Backtick Quote Tests (Single Backtick)
21
+ // ============================================================================
22
+
23
+ test('TestBacktickQuotedReference', () => {
24
+ const input = '`backtick quoted`';
25
+ const result = parser.parse(input);
26
+
27
+ expect(result.length).toBe(1);
28
+ expect(getSingleRefId(result)).toBe('backtick quoted');
29
+ });
30
+
31
+ test('TestBacktickQuotedWithSpaces', () => {
32
+ const input = '`text with spaces`';
33
+ const result = parser.parse(input);
34
+
35
+ expect(result.length).toBe(1);
36
+ expect(getSingleRefId(result)).toBe('text with spaces');
37
+ });
38
+
39
+ test('TestBacktickQuotedMultiline', () => {
40
+ const input = '(`line1\nline2`)';
41
+ const result = parser.parse(input);
42
+
43
+ expect(result.length).toBe(1);
44
+ expect(result[0].values[0].id).toBe('line1\nline2');
45
+ });
46
+
47
+ test('TestBacktickQuotedWithEscapedBacktick', () => {
48
+ const input = '`text with `` escaped backtick`';
49
+ const result = parser.parse(input);
50
+
51
+ expect(result.length).toBe(1);
52
+ expect(getSingleRefId(result)).toBe('text with ` escaped backtick');
53
+ });
54
+
55
+ // ============================================================================
56
+ // Single Quote Tests (with escaping)
57
+ // ============================================================================
58
+
59
+ test('TestSingleQuoteWithEscapedSingleQuote', () => {
60
+ const input = "'text with '' escaped quote'";
61
+ const result = parser.parse(input);
62
+
63
+ expect(result.length).toBe(1);
64
+ expect(getSingleRefId(result)).toBe("text with ' escaped quote");
65
+ });
66
+
67
+ // ============================================================================
68
+ // Double Quote Tests (with escaping)
69
+ // ============================================================================
70
+
71
+ test('TestDoubleQuoteWithEscapedDoubleQuote', () => {
72
+ const input = '"text with "" escaped quote"';
73
+ const result = parser.parse(input);
74
+
75
+ expect(result.length).toBe(1);
76
+ expect(getSingleRefId(result)).toBe('text with " escaped quote');
77
+ });
78
+
79
+ // ============================================================================
80
+ // Double Quotes (2 quote chars) Tests
81
+ // ============================================================================
82
+
83
+ test('TestDoubleDoubleQuotes', () => {
84
+ const input = '""double double quotes""';
85
+ const result = parser.parse(input);
86
+
87
+ expect(result.length).toBe(1);
88
+ expect(getSingleRefId(result)).toBe('double double quotes');
89
+ });
90
+
91
+ test('TestDoubleDoubleQuotesWithSingleQuoteInside', () => {
92
+ const input = '""text with " inside""';
93
+ const result = parser.parse(input);
94
+
95
+ expect(result.length).toBe(1);
96
+ expect(getSingleRefId(result)).toBe('text with " inside');
97
+ });
98
+
99
+ test('TestDoubleDoubleQuotesWithEscape', () => {
100
+ const input = '""text with """" escaped double""';
101
+ const result = parser.parse(input);
102
+
103
+ expect(result.length).toBe(1);
104
+ expect(getSingleRefId(result)).toBe('text with "" escaped double');
105
+ });
106
+
107
+ test('TestDoubleSingleQuotes', () => {
108
+ const input = "''double single quotes''";
109
+ const result = parser.parse(input);
110
+
111
+ expect(result.length).toBe(1);
112
+ expect(getSingleRefId(result)).toBe('double single quotes');
113
+ });
114
+
115
+ test('TestDoubleSingleQuotesWithSingleQuoteInside', () => {
116
+ const input = "''text with ' inside''";
117
+ const result = parser.parse(input);
118
+
119
+ expect(result.length).toBe(1);
120
+ expect(getSingleRefId(result)).toBe("text with ' inside");
121
+ });
122
+
123
+ test('TestDoubleSingleQuotesWithEscape', () => {
124
+ const input = "''text with '''' escaped single''";
125
+ const result = parser.parse(input);
126
+
127
+ expect(result.length).toBe(1);
128
+ expect(getSingleRefId(result)).toBe("text with '' escaped single");
129
+ });
130
+
131
+ test('TestDoubleBacktickQuotes', () => {
132
+ const input = '``double backtick quotes``';
133
+ const result = parser.parse(input);
134
+
135
+ expect(result.length).toBe(1);
136
+ expect(getSingleRefId(result)).toBe('double backtick quotes');
137
+ });
138
+
139
+ test('TestDoubleBacktickQuotesWithBacktickInside', () => {
140
+ const input = '``text with ` inside``';
141
+ const result = parser.parse(input);
142
+
143
+ expect(result.length).toBe(1);
144
+ expect(getSingleRefId(result)).toBe('text with ` inside');
145
+ });
146
+
147
+ test('TestDoubleBacktickQuotesWithEscape', () => {
148
+ const input = '``text with ```` escaped backtick``';
149
+ const result = parser.parse(input);
150
+
151
+ expect(result.length).toBe(1);
152
+ expect(getSingleRefId(result)).toBe('text with `` escaped backtick');
153
+ });
154
+
155
+ // ============================================================================
156
+ // Triple Quotes (3 quote chars) Tests
157
+ // ============================================================================
158
+
159
+ test('TestTripleDoubleQuotes', () => {
160
+ const input = '"""triple double quotes"""';
161
+ const result = parser.parse(input);
162
+
163
+ expect(result.length).toBe(1);
164
+ expect(getSingleRefId(result)).toBe('triple double quotes');
165
+ });
166
+
167
+ test('TestTripleDoubleQuotesWithDoubleQuoteInside', () => {
168
+ const input = '"""text with "" inside"""';
169
+ const result = parser.parse(input);
170
+
171
+ expect(result.length).toBe(1);
172
+ expect(getSingleRefId(result)).toBe('text with "" inside');
173
+ });
174
+
175
+ test('TestTripleDoubleQuotesWithEscape', () => {
176
+ const input = '"""text with """""" escaped triple"""';
177
+ const result = parser.parse(input);
178
+
179
+ expect(result.length).toBe(1);
180
+ expect(getSingleRefId(result)).toBe('text with """ escaped triple');
181
+ });
182
+
183
+ test('TestTripleSingleQuotes', () => {
184
+ const input = "'''triple single quotes'''";
185
+ const result = parser.parse(input);
186
+
187
+ expect(result.length).toBe(1);
188
+ expect(getSingleRefId(result)).toBe('triple single quotes');
189
+ });
190
+
191
+ test('TestTripleSingleQuotesWithDoubleQuoteInside', () => {
192
+ const input = "'''text with '' inside'''";
193
+ const result = parser.parse(input);
194
+
195
+ expect(result.length).toBe(1);
196
+ expect(getSingleRefId(result)).toBe("text with '' inside");
197
+ });
198
+
199
+ test('TestTripleSingleQuotesWithEscape', () => {
200
+ const input = "'''text with '''''' escaped triple'''";
201
+ const result = parser.parse(input);
202
+
203
+ expect(result.length).toBe(1);
204
+ expect(getSingleRefId(result)).toBe("text with ''' escaped triple");
205
+ });
206
+
207
+ test('TestTripleBacktickQuotes', () => {
208
+ const input = '```triple backtick quotes```';
209
+ const result = parser.parse(input);
210
+
211
+ expect(result.length).toBe(1);
212
+ expect(getSingleRefId(result)).toBe('triple backtick quotes');
213
+ });
214
+
215
+ test('TestTripleBacktickQuotesWithDoubleBacktickInside', () => {
216
+ const input = '```text with `` inside```';
217
+ const result = parser.parse(input);
218
+
219
+ expect(result.length).toBe(1);
220
+ expect(getSingleRefId(result)).toBe('text with `` inside');
221
+ });
222
+
223
+ test('TestTripleBacktickQuotesWithEscape', () => {
224
+ const input = '```text with `````` escaped triple```';
225
+ const result = parser.parse(input);
226
+
227
+ expect(result.length).toBe(1);
228
+ expect(getSingleRefId(result)).toBe('text with ``` escaped triple');
229
+ });
230
+
231
+ // ============================================================================
232
+ // Quadruple Quotes (4 quote chars) Tests
233
+ // ============================================================================
234
+
235
+ test('TestQuadrupleDoubleQuotes', () => {
236
+ const input = '""""quadruple double quotes""""';
237
+ const result = parser.parse(input);
238
+
239
+ expect(result.length).toBe(1);
240
+ expect(getSingleRefId(result)).toBe('quadruple double quotes');
241
+ });
242
+
243
+ test('TestQuadrupleDoubleQuotesWithTripleQuoteInside', () => {
244
+ const input = '""""text with """ inside""""';
245
+ const result = parser.parse(input);
246
+
247
+ expect(result.length).toBe(1);
248
+ expect(getSingleRefId(result)).toBe('text with """ inside');
249
+ });
250
+
251
+ test('TestQuadrupleDoubleQuotesWithEscape', () => {
252
+ const input = '""""text with """""""" escaped quad""""';
253
+ const result = parser.parse(input);
254
+
255
+ expect(result.length).toBe(1);
256
+ expect(getSingleRefId(result)).toBe('text with """" escaped quad');
257
+ });
258
+
259
+ test('TestQuadrupleSingleQuotes', () => {
260
+ const input = "''''quadruple single quotes''''";
261
+ const result = parser.parse(input);
262
+
263
+ expect(result.length).toBe(1);
264
+ expect(getSingleRefId(result)).toBe('quadruple single quotes');
265
+ });
266
+
267
+ test('TestQuadrupleBacktickQuotes', () => {
268
+ const input = '````quadruple backtick quotes````';
269
+ const result = parser.parse(input);
270
+
271
+ expect(result.length).toBe(1);
272
+ expect(getSingleRefId(result)).toBe('quadruple backtick quotes');
273
+ });
274
+
275
+ // ============================================================================
276
+ // Quintuple Quotes (5 quote chars) Tests
277
+ // ============================================================================
278
+
279
+ test('TestQuintupleDoubleQuotes', () => {
280
+ const input = '"""""quintuple double quotes"""""';
281
+ const result = parser.parse(input);
282
+
283
+ expect(result.length).toBe(1);
284
+ expect(getSingleRefId(result)).toBe('quintuple double quotes');
285
+ });
286
+
287
+ test('TestQuintupleDoubleQuotesWithQuadQuoteInside', () => {
288
+ const input = '"""""text with """" inside"""""';
289
+ const result = parser.parse(input);
290
+
291
+ expect(result.length).toBe(1);
292
+ expect(getSingleRefId(result)).toBe('text with """" inside');
293
+ });
294
+
295
+ test('TestQuintupleDoubleQuotesWithEscape', () => {
296
+ const input = '"""""text with """""""""" escaped quint"""""';
297
+ const result = parser.parse(input);
298
+
299
+ expect(result.length).toBe(1);
300
+ expect(getSingleRefId(result)).toBe('text with """"" escaped quint');
301
+ });
302
+
303
+ test('TestQuintupleSingleQuotes', () => {
304
+ const input = "'''''quintuple single quotes'''''";
305
+ const result = parser.parse(input);
306
+
307
+ expect(result.length).toBe(1);
308
+ expect(getSingleRefId(result)).toBe('quintuple single quotes');
309
+ });
310
+
311
+ test('TestQuintupleBacktickQuotes', () => {
312
+ const input = '`````quintuple backtick quotes`````';
313
+ const result = parser.parse(input);
314
+
315
+ expect(result.length).toBe(1);
316
+ expect(getSingleRefId(result)).toBe('quintuple backtick quotes');
317
+ });
318
+
319
+ // ============================================================================
320
+ // Complex Scenarios Tests
321
+ // ============================================================================
322
+
323
+ test('TestMixedQuotesInLink', () => {
324
+ const input = '("double" \'single\' `backtick`)';
325
+ const result = parser.parse(input);
326
+
327
+ expect(result.length).toBe(1);
328
+ expect(result[0].values.length).toBe(3);
329
+ expect(result[0].values[0].id).toBe('double');
330
+ expect(result[0].values[1].id).toBe('single');
331
+ expect(result[0].values[2].id).toBe('backtick');
332
+ });
333
+
334
+ test('TestBacktickAsIdInLink', () => {
335
+ const input = '(`myId`: value1 value2)';
336
+ const result = parser.parse(input);
337
+
338
+ expect(result.length).toBe(1);
339
+ expect(result[0].id).toBe('myId');
340
+ expect(result[0].values.length).toBe(2);
341
+ });
342
+
343
+ test('TestCodeBlockLikeContent', () => {
344
+ // This demonstrates using triple backticks for code-like content
345
+ const input = '```const x = 1;```';
346
+ const result = parser.parse(input);
347
+
348
+ expect(result.length).toBe(1);
349
+ expect(getSingleRefId(result)).toBe('const x = 1;');
350
+ });
351
+
352
+ test('TestNestedQuotesInMarkdown', () => {
353
+ // Using double backticks to include single backtick
354
+ const input = '``Use `code` in markdown``';
355
+ const result = parser.parse(input);
356
+
357
+ expect(result.length).toBe(1);
358
+ expect(getSingleRefId(result)).toBe('Use `code` in markdown');
359
+ });
360
+
361
+ test('TestSQLWithQuotes', () => {
362
+ // Using double single quotes to include single quote in SQL-like string
363
+ // Inside '', to get a single quote, we need '''' (4 single quotes = escaped pair)
364
+ const input = "''SELECT * FROM users WHERE name = ''''John''''''";
365
+ const result = parser.parse(input);
366
+
367
+ expect(result.length).toBe(1);
368
+ expect(getSingleRefId(result)).toBe(
369
+ "SELECT * FROM users WHERE name = ''John''"
370
+ );
371
+ });
372
+
373
+ test('TestJSONStringWithQuotes', () => {
374
+ // Using double double quotes to include double quote in JSON-like string
375
+ const input = '""{"key": "value"}""';
376
+ const result = parser.parse(input);
377
+
378
+ expect(result.length).toBe(1);
379
+ expect(getSingleRefId(result)).toBe('{"key": "value"}');
380
+ });
381
+
382
+ // ============================================================================
383
+ // Edge Cases
384
+ // ============================================================================
385
+
386
+ test('TestEmptySingleQuotedReference', () => {
387
+ const input = "''";
388
+ // Empty quoted reference should not match - becomes simple reference or fails
389
+ // Let's verify what happens
390
+ try {
391
+ const result = parser.parse(input);
392
+ // If it parses, check what we get
393
+ expect(result.length).toBeGreaterThanOrEqual(0);
394
+ } catch (e) {
395
+ // Expected for empty quotes
396
+ expect(e).toBeTruthy();
397
+ }
398
+ });
399
+
400
+ test('TestWhitespacePreservedInQuotes', () => {
401
+ const input = '" spaces "';
402
+ const result = parser.parse(input);
403
+
404
+ expect(result.length).toBe(1);
405
+ expect(getSingleRefId(result)).toBe(' spaces ');
406
+ });
407
+
408
+ test('TestMultilineInDoubleDoubleQuotes', () => {
409
+ const input = '(""line1\nline2"")';
410
+ const result = parser.parse(input);
411
+
412
+ expect(result.length).toBe(1);
413
+ expect(result[0].values[0].id).toBe('line1\nline2');
414
+ });
415
+
416
+ // ============================================================================
417
+ // Unlimited Quotes (6+ quote chars) Tests
418
+ // ============================================================================
419
+
420
+ test('TestUnlimitedQuotes6', () => {
421
+ // Test 6-quote strings
422
+ const input = '""""""hello""""""';
423
+ const result = parser.parse(input);
424
+
425
+ expect(result.length).toBe(1);
426
+ expect(getSingleRefId(result)).toBe('hello');
427
+ });
428
+
429
+ test('TestUnlimitedQuotes10', () => {
430
+ // Test 10-quote strings
431
+ const input = '""""""""""very deeply quoted""""""""""';
432
+ const result = parser.parse(input);
433
+
434
+ expect(result.length).toBe(1);
435
+ expect(getSingleRefId(result)).toBe('very deeply quoted');
436
+ });
437
+
438
+ test('TestUnlimitedQuotes6WithInnerQuotes', () => {
439
+ // Test 6-quote strings with inner 5-quote sequences
440
+ const input = '""""""hello with """"" five quotes inside""""""';
441
+ const result = parser.parse(input);
442
+
443
+ expect(result.length).toBe(1);
444
+ expect(getSingleRefId(result)).toBe('hello with """"" five quotes inside');
445
+ });
446
+
447
+ test('TestUnlimitedSingleQuotes7', () => {
448
+ // Test 7-quote single quote strings
449
+ const input = "'''''''seven single quotes'''''''";
450
+ const result = parser.parse(input);
451
+
452
+ expect(result.length).toBe(1);
453
+ expect(getSingleRefId(result)).toBe('seven single quotes');
454
+ });
455
+
456
+ test('TestUnlimitedBackticks8', () => {
457
+ // Test 8-quote backtick strings
458
+ const input = '````````eight backticks````````';
459
+ const result = parser.parse(input);
460
+
461
+ expect(result.length).toBe(1);
462
+ expect(getSingleRefId(result)).toBe('eight backticks');
463
+ });