agentmap 0.2.0 → 0.3.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.
@@ -0,0 +1,1414 @@
1
+ import { beforeAll, describe, expect, test } from 'bun:test';
2
+ import { parseCode, initParser } from '../parser/index.js';
3
+ import { extractDefinitions } from './definitions.js';
4
+ // ============================================================================
5
+ // Setup
6
+ // ============================================================================
7
+ beforeAll(async () => {
8
+ await initParser();
9
+ });
10
+ /**
11
+ * Helper to extract definitions from code string
12
+ */
13
+ async function getDefinitions(code, language) {
14
+ const tree = await parseCode(code, language);
15
+ return extractDefinitions(tree.rootNode, language);
16
+ }
17
+ // ============================================================================
18
+ // TypeScript Tests
19
+ // ============================================================================
20
+ describe('TypeScript', () => {
21
+ test('large function (>7 lines) is included', async () => {
22
+ const code = `function processData(input: string): string {
23
+ const trimmed = input.trim()
24
+ const upper = trimmed.toUpperCase()
25
+ const parts = upper.split(',')
26
+ const filtered = parts.filter(Boolean)
27
+ const joined = filtered.join('-')
28
+ const result = joined + '!'
29
+ return result
30
+ }`;
31
+ const defs = await getDefinitions(code, 'typescript');
32
+ expect(defs).toMatchInlineSnapshot(`
33
+ [
34
+ {
35
+ "endLine": 9,
36
+ "exported": false,
37
+ "line": 1,
38
+ "name": "processData",
39
+ "type": "function",
40
+ },
41
+ ]
42
+ `);
43
+ });
44
+ test('small function (<=7 lines) is excluded', async () => {
45
+ const code = `function add(a: number, b: number): number {
46
+ return a + b
47
+ }`;
48
+ const defs = await getDefinitions(code, 'typescript');
49
+ expect(defs).toMatchInlineSnapshot(`[]`);
50
+ });
51
+ test('large class is included', async () => {
52
+ const code = `class Calculator {
53
+ private value: number = 0
54
+
55
+ add(n: number): this {
56
+ this.value += n
57
+ return this
58
+ }
59
+
60
+ subtract(n: number): this {
61
+ this.value -= n
62
+ return this
63
+ }
64
+ }`;
65
+ const defs = await getDefinitions(code, 'typescript');
66
+ expect(defs).toMatchInlineSnapshot(`
67
+ [
68
+ {
69
+ "endLine": 13,
70
+ "exported": false,
71
+ "line": 1,
72
+ "name": "Calculator",
73
+ "type": "class",
74
+ },
75
+ ]
76
+ `);
77
+ });
78
+ test('small class is excluded', async () => {
79
+ const code = `class Point {
80
+ x: number
81
+ y: number
82
+ }`;
83
+ const defs = await getDefinitions(code, 'typescript');
84
+ expect(defs).toMatchInlineSnapshot(`[]`);
85
+ });
86
+ test('large arrow function is included', async () => {
87
+ const code = `const processItems = (items: string[]) => {
88
+ const result: string[] = []
89
+ for (const item of items) {
90
+ const processed = item.trim()
91
+ const upper = processed.toUpperCase()
92
+ result.push(upper)
93
+ }
94
+ return result
95
+ }`;
96
+ const defs = await getDefinitions(code, 'typescript');
97
+ expect(defs).toMatchInlineSnapshot(`
98
+ [
99
+ {
100
+ "endLine": 9,
101
+ "exported": false,
102
+ "line": 1,
103
+ "name": "processItems",
104
+ "type": "function",
105
+ },
106
+ ]
107
+ `);
108
+ });
109
+ test('small arrow function is excluded', async () => {
110
+ const code = `const double = (n: number) => n * 2`;
111
+ const defs = await getDefinitions(code, 'typescript');
112
+ expect(defs).toMatchInlineSnapshot(`[]`);
113
+ });
114
+ test('exported const is included', async () => {
115
+ const code = `export const CONFIG = {
116
+ apiUrl: 'https://api.example.com',
117
+ timeout: 5000,
118
+ }`;
119
+ const defs = await getDefinitions(code, 'typescript');
120
+ expect(defs).toMatchInlineSnapshot(`
121
+ [
122
+ {
123
+ "endLine": 4,
124
+ "exported": true,
125
+ "line": 1,
126
+ "name": "CONFIG",
127
+ "type": "const",
128
+ },
129
+ ]
130
+ `);
131
+ });
132
+ test('non-exported const is excluded', async () => {
133
+ const code = `const CONFIG = { timeout: 5000 }`;
134
+ const defs = await getDefinitions(code, 'typescript');
135
+ expect(defs).toMatchInlineSnapshot(`[]`);
136
+ });
137
+ test('interface is included (any size)', async () => {
138
+ const code = `interface User {
139
+ name: string
140
+ age: number
141
+ }`;
142
+ const defs = await getDefinitions(code, 'typescript');
143
+ expect(defs).toMatchInlineSnapshot(`
144
+ [
145
+ {
146
+ "endLine": 4,
147
+ "exported": false,
148
+ "line": 1,
149
+ "name": "User",
150
+ "type": "interface",
151
+ },
152
+ ]
153
+ `);
154
+ });
155
+ test('type alias is included (any size)', async () => {
156
+ const code = `type Status = 'pending' | 'active' | 'done'`;
157
+ const defs = await getDefinitions(code, 'typescript');
158
+ expect(defs).toMatchInlineSnapshot(`
159
+ [
160
+ {
161
+ "endLine": 1,
162
+ "exported": false,
163
+ "line": 1,
164
+ "name": "Status",
165
+ "type": "type",
166
+ },
167
+ ]
168
+ `);
169
+ });
170
+ test('enum is included (any size)', async () => {
171
+ const code = `enum Color {
172
+ Red,
173
+ Green,
174
+ Blue,
175
+ }`;
176
+ const defs = await getDefinitions(code, 'typescript');
177
+ expect(defs).toMatchInlineSnapshot(`
178
+ [
179
+ {
180
+ "endLine": 5,
181
+ "exported": false,
182
+ "line": 1,
183
+ "name": "Color",
184
+ "type": "enum",
185
+ },
186
+ ]
187
+ `);
188
+ });
189
+ test('exported function', async () => {
190
+ const code = `export function fetchData(url: string): Promise<Response> {
191
+ const headers = new Headers()
192
+ headers.set('Content-Type', 'application/json')
193
+ const options = { method: 'GET', headers }
194
+ const response = fetch(url, options)
195
+ return response
196
+ }`;
197
+ const defs = await getDefinitions(code, 'typescript');
198
+ expect(defs).toMatchInlineSnapshot(`
199
+ [
200
+ {
201
+ "endLine": 7,
202
+ "exported": true,
203
+ "line": 1,
204
+ "name": "fetchData",
205
+ "type": "function",
206
+ },
207
+ ]
208
+ `);
209
+ });
210
+ test('exported large function', async () => {
211
+ const code = `export function fetchData(url: string): Promise<Response> {
212
+ const headers = new Headers()
213
+ headers.set('Content-Type', 'application/json')
214
+ headers.set('Accept', 'application/json')
215
+ const options = { method: 'GET', headers }
216
+ const response = fetch(url, options)
217
+ return response
218
+ }`;
219
+ const defs = await getDefinitions(code, 'typescript');
220
+ expect(defs).toMatchInlineSnapshot(`
221
+ [
222
+ {
223
+ "endLine": 8,
224
+ "exported": true,
225
+ "line": 1,
226
+ "name": "fetchData",
227
+ "type": "function",
228
+ },
229
+ ]
230
+ `);
231
+ });
232
+ test('async function', async () => {
233
+ const code = `async function loadData(id: string): Promise<Data> {
234
+ const url = buildUrl(id)
235
+ const response = await fetch(url)
236
+ const json = await response.json()
237
+ const validated = validate(json)
238
+ const transformed = transform(validated)
239
+ return transformed
240
+ }`;
241
+ const defs = await getDefinitions(code, 'typescript');
242
+ expect(defs).toMatchInlineSnapshot(`
243
+ [
244
+ {
245
+ "endLine": 8,
246
+ "exported": false,
247
+ "line": 1,
248
+ "name": "loadData",
249
+ "type": "function",
250
+ },
251
+ ]
252
+ `);
253
+ });
254
+ test('generator function', async () => {
255
+ const code = `function* generateSequence(start: number, end: number) {
256
+ let current = start
257
+ while (current <= end) {
258
+ const value = current
259
+ current++
260
+ yield value
261
+ console.log('yielded', value)
262
+ }
263
+ }`;
264
+ const defs = await getDefinitions(code, 'typescript');
265
+ expect(defs).toMatchInlineSnapshot(`[]`);
266
+ });
267
+ test('abstract class', async () => {
268
+ const code = `abstract class BaseProcessor {
269
+ abstract process(input: string): string
270
+
271
+ protected validate(input: string): boolean {
272
+ return input.length > 0
273
+ }
274
+
275
+ protected transform(input: string): string {
276
+ return input.trim()
277
+ }
278
+ }`;
279
+ const defs = await getDefinitions(code, 'typescript');
280
+ expect(defs).toMatchInlineSnapshot(`
281
+ [
282
+ {
283
+ "endLine": 11,
284
+ "exported": false,
285
+ "line": 1,
286
+ "name": "BaseProcessor",
287
+ "type": "class",
288
+ },
289
+ ]
290
+ `);
291
+ });
292
+ test('generic function', async () => {
293
+ const code = `function identity<T>(value: T): T {
294
+ const result = value
295
+ console.log('identity called')
296
+ console.log('value:', result)
297
+ console.log('type:', typeof result)
298
+ console.log('returning...')
299
+ return result
300
+ }`;
301
+ const defs = await getDefinitions(code, 'typescript');
302
+ expect(defs).toMatchInlineSnapshot(`
303
+ [
304
+ {
305
+ "endLine": 8,
306
+ "exported": false,
307
+ "line": 1,
308
+ "name": "identity",
309
+ "type": "function",
310
+ },
311
+ ]
312
+ `);
313
+ });
314
+ test('generic class', async () => {
315
+ const code = `class Container<T> {
316
+ private items: T[] = []
317
+
318
+ add(item: T): void {
319
+ this.items.push(item)
320
+ }
321
+
322
+ get(index: number): T {
323
+ return this.items[index]
324
+ }
325
+
326
+ size(): number {
327
+ return this.items.length
328
+ }
329
+ }`;
330
+ const defs = await getDefinitions(code, 'typescript');
331
+ expect(defs).toMatchInlineSnapshot(`
332
+ [
333
+ {
334
+ "endLine": 15,
335
+ "exported": false,
336
+ "line": 1,
337
+ "name": "Container",
338
+ "type": "class",
339
+ },
340
+ ]
341
+ `);
342
+ });
343
+ test('multiple exports in one line', async () => {
344
+ const code = `export const a = 1, b = 2, c = 3`;
345
+ const defs = await getDefinitions(code, 'typescript');
346
+ expect(defs).toMatchInlineSnapshot(`
347
+ [
348
+ {
349
+ "endLine": 1,
350
+ "exported": true,
351
+ "line": 1,
352
+ "name": "a",
353
+ "type": "const",
354
+ },
355
+ {
356
+ "endLine": 1,
357
+ "exported": true,
358
+ "line": 1,
359
+ "name": "b",
360
+ "type": "const",
361
+ },
362
+ {
363
+ "endLine": 1,
364
+ "exported": true,
365
+ "line": 1,
366
+ "name": "c",
367
+ "type": "const",
368
+ },
369
+ ]
370
+ `);
371
+ });
372
+ test('export default function', async () => {
373
+ const code = `export default function handler(req: Request): Response {
374
+ const body = req.body
375
+ const parsed = JSON.parse(body)
376
+ const validated = validate(parsed)
377
+ const processed = processData(validated)
378
+ const formatted = format(processed)
379
+ const response = JSON.stringify(formatted)
380
+ return new Response(response)
381
+ }`;
382
+ const defs = await getDefinitions(code, 'typescript');
383
+ expect(defs).toMatchInlineSnapshot(`
384
+ [
385
+ {
386
+ "endLine": 9,
387
+ "exported": true,
388
+ "line": 1,
389
+ "name": "handler",
390
+ "type": "function",
391
+ },
392
+ ]
393
+ `);
394
+ });
395
+ test('class with decorators', async () => {
396
+ const code = `@Injectable()
397
+ @Singleton()
398
+ class UserService {
399
+ constructor(private db: Database) {}
400
+
401
+ async findUser(id: string): Promise<User> {
402
+ return this.db.users.find(id)
403
+ }
404
+
405
+ async createUser(data: UserData): Promise<User> {
406
+ return this.db.users.create(data)
407
+ }
408
+ }`;
409
+ const defs = await getDefinitions(code, 'typescript');
410
+ expect(defs).toMatchInlineSnapshot(`
411
+ [
412
+ {
413
+ "endLine": 13,
414
+ "exported": false,
415
+ "line": 1,
416
+ "name": "UserService",
417
+ "type": "class",
418
+ },
419
+ ]
420
+ `);
421
+ });
422
+ test('mixed definitions - filters correctly', async () => {
423
+ const code = `// Small function - excluded
424
+ function util(x: number) {
425
+ return x * 2
426
+ }
427
+
428
+ // Large function - included
429
+ export function processData(input: string): string {
430
+ const step1 = input.trim()
431
+ const step2 = step1.toUpperCase()
432
+ const step3 = step2.split('')
433
+ const step4 = step3.reverse()
434
+ const step5 = step4.join('')
435
+ return step5
436
+ }
437
+
438
+ // Interface - included
439
+ interface Config {
440
+ name: string
441
+ }
442
+
443
+ // Small class - excluded
444
+ class Tiny {
445
+ x = 1
446
+ }
447
+
448
+ // Exported const - included
449
+ export const VERSION = '1.0.0'
450
+
451
+ // Type alias - included
452
+ type ID = string | number`;
453
+ const defs = await getDefinitions(code, 'typescript');
454
+ expect(defs).toMatchInlineSnapshot(`
455
+ [
456
+ {
457
+ "endLine": 14,
458
+ "exported": true,
459
+ "line": 7,
460
+ "name": "processData",
461
+ "type": "function",
462
+ },
463
+ {
464
+ "endLine": 19,
465
+ "exported": false,
466
+ "line": 17,
467
+ "name": "Config",
468
+ "type": "interface",
469
+ },
470
+ {
471
+ "endLine": 27,
472
+ "exported": true,
473
+ "line": 27,
474
+ "name": "VERSION",
475
+ "type": "const",
476
+ },
477
+ {
478
+ "endLine": 30,
479
+ "exported": false,
480
+ "line": 30,
481
+ "name": "ID",
482
+ "type": "type",
483
+ },
484
+ ]
485
+ `);
486
+ });
487
+ });
488
+ // ============================================================================
489
+ // JavaScript Tests
490
+ // ============================================================================
491
+ describe('JavaScript', () => {
492
+ test('large function', async () => {
493
+ const code = `function buildResponse(data) {
494
+ const headers = { 'Content-Type': 'application/json' }
495
+ const body = JSON.stringify(data)
496
+ const status = 200
497
+ const statusText = 'OK'
498
+ const response = { headers, body, status, statusText }
499
+ return response
500
+ }`;
501
+ const defs = await getDefinitions(code, 'javascript');
502
+ expect(defs).toMatchInlineSnapshot(`
503
+ [
504
+ {
505
+ "endLine": 8,
506
+ "exported": false,
507
+ "line": 1,
508
+ "name": "buildResponse",
509
+ "type": "function",
510
+ },
511
+ ]
512
+ `);
513
+ });
514
+ test('small function', async () => {
515
+ const code = `function add(a, b) {
516
+ return a + b
517
+ }`;
518
+ const defs = await getDefinitions(code, 'javascript');
519
+ expect(defs).toMatchInlineSnapshot(`[]`);
520
+ });
521
+ test('large class', async () => {
522
+ const code = `class EventEmitter {
523
+ constructor() {
524
+ this.events = {}
525
+ }
526
+
527
+ on(event, callback) {
528
+ if (!this.events[event]) {
529
+ this.events[event] = []
530
+ }
531
+ this.events[event].push(callback)
532
+ }
533
+
534
+ emit(event, data) {
535
+ const callbacks = this.events[event]
536
+ if (callbacks) {
537
+ callbacks.forEach(cb => cb(data))
538
+ }
539
+ }
540
+ }`;
541
+ const defs = await getDefinitions(code, 'javascript');
542
+ expect(defs).toMatchInlineSnapshot(`
543
+ [
544
+ {
545
+ "endLine": 19,
546
+ "exported": false,
547
+ "line": 1,
548
+ "name": "EventEmitter",
549
+ "type": "class",
550
+ },
551
+ ]
552
+ `);
553
+ });
554
+ test('IIFE is not extracted', async () => {
555
+ const code = `(function() {
556
+ console.log('init')
557
+ const x = 1
558
+ const y = 2
559
+ const z = 3
560
+ console.log(x, y, z)
561
+ return { x, y, z }
562
+ })()`;
563
+ const defs = await getDefinitions(code, 'javascript');
564
+ expect(defs).toMatchInlineSnapshot(`[]`);
565
+ });
566
+ test('function expression assigned to var', async () => {
567
+ const code = `var processItems = function(items) {
568
+ const result = []
569
+ for (const item of items) {
570
+ const processed = item.trim()
571
+ const upper = processed.toUpperCase()
572
+ result.push(upper)
573
+ }
574
+ return result
575
+ }`;
576
+ const defs = await getDefinitions(code, 'javascript');
577
+ expect(defs).toMatchInlineSnapshot(`[]`);
578
+ });
579
+ });
580
+ // ============================================================================
581
+ // Python Tests
582
+ // ============================================================================
583
+ describe('Python', () => {
584
+ test('large function', async () => {
585
+ const code = `def process_data(items):
586
+ result = []
587
+ for item in items:
588
+ cleaned = item.strip()
589
+ upper = cleaned.upper()
590
+ parts = upper.split(',')
591
+ result.extend(parts)
592
+ return result`;
593
+ const defs = await getDefinitions(code, 'python');
594
+ expect(defs).toMatchInlineSnapshot(`
595
+ [
596
+ {
597
+ "endLine": 8,
598
+ "exported": false,
599
+ "line": 1,
600
+ "name": "process_data",
601
+ "type": "function",
602
+ },
603
+ ]
604
+ `);
605
+ });
606
+ test('small function', async () => {
607
+ const code = `def add(a, b):
608
+ return a + b`;
609
+ const defs = await getDefinitions(code, 'python');
610
+ expect(defs).toMatchInlineSnapshot(`[]`);
611
+ });
612
+ test('large class', async () => {
613
+ const code = `class DataProcessor:
614
+ def __init__(self, data):
615
+ self.data = data
616
+ self.processed = False
617
+
618
+ def process(self):
619
+ self.data = [x.strip() for x in self.data]
620
+ self.processed = True
621
+ return self
622
+
623
+ def get_result(self):
624
+ if not self.processed:
625
+ raise ValueError("Not processed")
626
+ return self.data`;
627
+ const defs = await getDefinitions(code, 'python');
628
+ expect(defs).toMatchInlineSnapshot(`
629
+ [
630
+ {
631
+ "endLine": 14,
632
+ "exported": false,
633
+ "line": 1,
634
+ "name": "DataProcessor",
635
+ "type": "class",
636
+ },
637
+ ]
638
+ `);
639
+ });
640
+ test('small class', async () => {
641
+ const code = `class Point:
642
+ x: int
643
+ y: int`;
644
+ const defs = await getDefinitions(code, 'python');
645
+ expect(defs).toMatchInlineSnapshot(`[]`);
646
+ });
647
+ test('async function', async () => {
648
+ const code = `async def fetch_data(url):
649
+ async with aiohttp.ClientSession() as session:
650
+ async with session.get(url) as response:
651
+ data = await response.json()
652
+ validated = validate(data)
653
+ processed = process(validated)
654
+ return processed`;
655
+ const defs = await getDefinitions(code, 'python');
656
+ expect(defs).toMatchInlineSnapshot(`
657
+ [
658
+ {
659
+ "endLine": 7,
660
+ "exported": false,
661
+ "line": 1,
662
+ "name": "fetch_data",
663
+ "type": "function",
664
+ },
665
+ ]
666
+ `);
667
+ });
668
+ test('decorated function', async () => {
669
+ const code = `@app.route('/api/users')
670
+ @require_auth
671
+ def get_users():
672
+ users = db.query(User).all()
673
+ serialized = [u.to_dict() for u in users]
674
+ filtered = filter_active(serialized)
675
+ sorted_users = sort_by_name(filtered)
676
+ return jsonify(sorted_users)`;
677
+ const defs = await getDefinitions(code, 'python');
678
+ expect(defs).toMatchInlineSnapshot(`[]`);
679
+ });
680
+ test('class with inheritance', async () => {
681
+ const code = `class AdminUser(User, PermissionMixin):
682
+ def __init__(self, name, permissions):
683
+ super().__init__(name)
684
+ self.permissions = permissions
685
+
686
+ def has_permission(self, perm):
687
+ return perm in self.permissions
688
+
689
+ def grant(self, perm):
690
+ self.permissions.add(perm)`;
691
+ const defs = await getDefinitions(code, 'python');
692
+ expect(defs).toMatchInlineSnapshot(`
693
+ [
694
+ {
695
+ "endLine": 10,
696
+ "exported": false,
697
+ "line": 1,
698
+ "name": "AdminUser",
699
+ "type": "class",
700
+ },
701
+ ]
702
+ `);
703
+ });
704
+ test('lambda is not extracted', async () => {
705
+ const code = `double = lambda x: x * 2
706
+ triple = lambda x: x * 3`;
707
+ const defs = await getDefinitions(code, 'python');
708
+ expect(defs).toMatchInlineSnapshot(`[]`);
709
+ });
710
+ });
711
+ // ============================================================================
712
+ // Rust Tests
713
+ // ============================================================================
714
+ describe('Rust', () => {
715
+ test('large function', async () => {
716
+ const code = `fn process_items(items: Vec<String>) -> Vec<String> {
717
+ let mut result = Vec::new();
718
+ for item in items {
719
+ let trimmed = item.trim();
720
+ let upper = trimmed.to_uppercase();
721
+ let parts: Vec<&str> = upper.split(',').collect();
722
+ result.extend(parts.iter().map(|s| s.to_string()));
723
+ }
724
+ result
725
+ }`;
726
+ const defs = await getDefinitions(code, 'rust');
727
+ expect(defs).toMatchInlineSnapshot(`
728
+ [
729
+ {
730
+ "endLine": 10,
731
+ "exported": false,
732
+ "line": 1,
733
+ "name": "process_items",
734
+ "type": "function",
735
+ },
736
+ ]
737
+ `);
738
+ });
739
+ test('small function', async () => {
740
+ const code = `fn add(a: i32, b: i32) -> i32 {
741
+ a + b
742
+ }`;
743
+ const defs = await getDefinitions(code, 'rust');
744
+ expect(defs).toMatchInlineSnapshot(`[]`);
745
+ });
746
+ test('large struct', async () => {
747
+ const code = `struct Config {
748
+ api_url: String,
749
+ timeout: u64,
750
+ retries: u32,
751
+ headers: HashMap<String, String>,
752
+ auth_token: Option<String>,
753
+ debug_mode: bool,
754
+ log_level: String,
755
+ max_connections: usize,
756
+ }`;
757
+ const defs = await getDefinitions(code, 'rust');
758
+ expect(defs).toMatchInlineSnapshot(`
759
+ [
760
+ {
761
+ "endLine": 10,
762
+ "exported": false,
763
+ "line": 1,
764
+ "name": "Config",
765
+ "type": "class",
766
+ },
767
+ ]
768
+ `);
769
+ });
770
+ test('small struct', async () => {
771
+ const code = `struct Point {
772
+ x: i32,
773
+ y: i32,
774
+ }`;
775
+ const defs = await getDefinitions(code, 'rust');
776
+ expect(defs).toMatchInlineSnapshot(`[]`);
777
+ });
778
+ test('large impl block', async () => {
779
+ const code = `impl Calculator {
780
+ fn new() -> Self {
781
+ Calculator { value: 0 }
782
+ }
783
+
784
+ fn add(&mut self, n: i32) -> &mut Self {
785
+ self.value += n;
786
+ self
787
+ }
788
+
789
+ fn result(&self) -> i32 {
790
+ self.value
791
+ }
792
+ }`;
793
+ const defs = await getDefinitions(code, 'rust');
794
+ expect(defs).toMatchInlineSnapshot(`
795
+ [
796
+ {
797
+ "endLine": 14,
798
+ "exported": false,
799
+ "line": 1,
800
+ "name": "Calculator",
801
+ "type": "class",
802
+ },
803
+ ]
804
+ `);
805
+ });
806
+ test('large trait', async () => {
807
+ const code = `trait Processor {
808
+ fn process(&self, input: &str) -> String;
809
+ fn validate(&self, input: &str) -> bool;
810
+ fn transform(&self, input: &str) -> Vec<String>;
811
+ fn cleanup(&self);
812
+ fn reset(&mut self);
813
+ fn get_status(&self) -> Status;
814
+ fn set_config(&mut self, config: Config);
815
+ fn run(&self) -> Result<(), Error>;
816
+ }`;
817
+ const defs = await getDefinitions(code, 'rust');
818
+ expect(defs).toMatchInlineSnapshot(`
819
+ [
820
+ {
821
+ "endLine": 10,
822
+ "exported": false,
823
+ "line": 1,
824
+ "name": "Processor",
825
+ "type": "class",
826
+ },
827
+ ]
828
+ `);
829
+ });
830
+ test('enum', async () => {
831
+ const code = `enum Status {
832
+ Pending,
833
+ Active,
834
+ Done,
835
+ }`;
836
+ const defs = await getDefinitions(code, 'rust');
837
+ expect(defs).toMatchInlineSnapshot(`
838
+ [
839
+ {
840
+ "endLine": 5,
841
+ "exported": false,
842
+ "line": 1,
843
+ "name": "Status",
844
+ "type": "enum",
845
+ },
846
+ ]
847
+ `);
848
+ });
849
+ test('type alias', async () => {
850
+ const code = `type Result<T> = std::result::Result<T, Error>;`;
851
+ const defs = await getDefinitions(code, 'rust');
852
+ expect(defs).toMatchInlineSnapshot(`
853
+ [
854
+ {
855
+ "endLine": 1,
856
+ "exported": false,
857
+ "line": 1,
858
+ "name": "Result",
859
+ "type": "type",
860
+ },
861
+ ]
862
+ `);
863
+ });
864
+ test('const item', async () => {
865
+ const code = `const MAX_SIZE: usize = 1024;`;
866
+ const defs = await getDefinitions(code, 'rust');
867
+ expect(defs).toMatchInlineSnapshot(`
868
+ [
869
+ {
870
+ "endLine": 1,
871
+ "exported": false,
872
+ "line": 1,
873
+ "name": "MAX_SIZE",
874
+ "type": "const",
875
+ },
876
+ ]
877
+ `);
878
+ });
879
+ test('static item', async () => {
880
+ const code = `static COUNTER: AtomicUsize = AtomicUsize::new(0);`;
881
+ const defs = await getDefinitions(code, 'rust');
882
+ expect(defs).toMatchInlineSnapshot(`
883
+ [
884
+ {
885
+ "endLine": 1,
886
+ "exported": false,
887
+ "line": 1,
888
+ "name": "COUNTER",
889
+ "type": "const",
890
+ },
891
+ ]
892
+ `);
893
+ });
894
+ test('pub function', async () => {
895
+ const code = `pub fn public_handler(request: Request) -> Response {
896
+ let body = request.body();
897
+ let parsed = parse_body(body);
898
+ let validated = validate(parsed);
899
+ let processed = process(validated);
900
+ let serialized = serialize(processed);
901
+ Response::new(serialized)
902
+ }`;
903
+ const defs = await getDefinitions(code, 'rust');
904
+ expect(defs).toMatchInlineSnapshot(`
905
+ [
906
+ {
907
+ "endLine": 8,
908
+ "exported": false,
909
+ "line": 1,
910
+ "name": "public_handler",
911
+ "type": "function",
912
+ },
913
+ ]
914
+ `);
915
+ });
916
+ test('async function', async () => {
917
+ const code = `async fn fetch_data(url: &str) -> Result<Data, Error> {
918
+ let client = reqwest::Client::new();
919
+ let response = client.get(url).send().await?;
920
+ let body = response.text().await?;
921
+ let parsed = serde_json::from_str(&body)?;
922
+ let validated = validate(parsed)?;
923
+ Ok(validated)
924
+ }`;
925
+ const defs = await getDefinitions(code, 'rust');
926
+ expect(defs).toMatchInlineSnapshot(`
927
+ [
928
+ {
929
+ "endLine": 8,
930
+ "exported": false,
931
+ "line": 1,
932
+ "name": "fetch_data",
933
+ "type": "function",
934
+ },
935
+ ]
936
+ `);
937
+ });
938
+ test('macro definition is not extracted', async () => {
939
+ const code = `macro_rules! create_function {
940
+ ($name:ident) => {
941
+ fn $name() {
942
+ println!("Called {}", stringify!($name));
943
+ }
944
+ };
945
+ }`;
946
+ const defs = await getDefinitions(code, 'rust');
947
+ expect(defs).toMatchInlineSnapshot(`[]`);
948
+ });
949
+ });
950
+ // ============================================================================
951
+ // Go Tests
952
+ // ============================================================================
953
+ describe('Go', () => {
954
+ test('large function', async () => {
955
+ const code = `func processItems(items []string) []string {
956
+ result := make([]string, 0)
957
+ for _, item := range items {
958
+ trimmed := strings.TrimSpace(item)
959
+ upper := strings.ToUpper(trimmed)
960
+ parts := strings.Split(upper, ",")
961
+ result = append(result, parts...)
962
+ }
963
+ return result
964
+ }`;
965
+ const defs = await getDefinitions(code, 'go');
966
+ expect(defs).toMatchInlineSnapshot(`
967
+ [
968
+ {
969
+ "endLine": 10,
970
+ "exported": false,
971
+ "line": 1,
972
+ "name": "processItems",
973
+ "type": "function",
974
+ },
975
+ ]
976
+ `);
977
+ });
978
+ test('small function', async () => {
979
+ const code = `func add(a, b int) int {
980
+ return a + b
981
+ }`;
982
+ const defs = await getDefinitions(code, 'go');
983
+ expect(defs).toMatchInlineSnapshot(`[]`);
984
+ });
985
+ test('large method', async () => {
986
+ const code = `func (c *Calculator) Process(input string) string {
987
+ step1 := strings.TrimSpace(input)
988
+ step2 := strings.ToUpper(step1)
989
+ step3 := strings.Split(step2, ",")
990
+ step4 := strings.Join(step3, "-")
991
+ step5 := step4 + "!"
992
+ return step5
993
+ }`;
994
+ const defs = await getDefinitions(code, 'go');
995
+ expect(defs).toMatchInlineSnapshot(`
996
+ [
997
+ {
998
+ "endLine": 8,
999
+ "exported": false,
1000
+ "line": 1,
1001
+ "name": "Process",
1002
+ "type": "function",
1003
+ },
1004
+ ]
1005
+ `);
1006
+ });
1007
+ test('type struct', async () => {
1008
+ const code = `type Config struct {
1009
+ APIUrl string
1010
+ Timeout int
1011
+ }`;
1012
+ const defs = await getDefinitions(code, 'go');
1013
+ expect(defs).toMatchInlineSnapshot(`
1014
+ [
1015
+ {
1016
+ "endLine": 4,
1017
+ "exported": false,
1018
+ "line": 1,
1019
+ "name": "Config",
1020
+ "type": "type",
1021
+ },
1022
+ ]
1023
+ `);
1024
+ });
1025
+ test('type interface', async () => {
1026
+ const code = `type Reader interface {
1027
+ Read(p []byte) (n int, err error)
1028
+ }`;
1029
+ const defs = await getDefinitions(code, 'go');
1030
+ expect(defs).toMatchInlineSnapshot(`
1031
+ [
1032
+ {
1033
+ "endLine": 3,
1034
+ "exported": false,
1035
+ "line": 1,
1036
+ "name": "Reader",
1037
+ "type": "type",
1038
+ },
1039
+ ]
1040
+ `);
1041
+ });
1042
+ test('const declaration', async () => {
1043
+ const code = `const MaxRetries = 3`;
1044
+ const defs = await getDefinitions(code, 'go');
1045
+ expect(defs).toMatchInlineSnapshot(`
1046
+ [
1047
+ {
1048
+ "endLine": 1,
1049
+ "exported": false,
1050
+ "line": 1,
1051
+ "name": "MaxRetries",
1052
+ "type": "const",
1053
+ },
1054
+ ]
1055
+ `);
1056
+ });
1057
+ test('var declaration', async () => {
1058
+ const code = `var DefaultTimeout = time.Second * 30`;
1059
+ const defs = await getDefinitions(code, 'go');
1060
+ expect(defs).toMatchInlineSnapshot(`
1061
+ [
1062
+ {
1063
+ "endLine": 1,
1064
+ "exported": false,
1065
+ "line": 1,
1066
+ "name": "DefaultTimeout",
1067
+ "type": "const",
1068
+ },
1069
+ ]
1070
+ `);
1071
+ });
1072
+ test('exported function (capitalized)', async () => {
1073
+ const code = `func ProcessData(input string) (string, error) {
1074
+ if input == "" {
1075
+ return "", errors.New("empty input")
1076
+ }
1077
+ trimmed := strings.TrimSpace(input)
1078
+ upper := strings.ToUpper(trimmed)
1079
+ validated := validate(upper)
1080
+ return validated, nil
1081
+ }`;
1082
+ const defs = await getDefinitions(code, 'go');
1083
+ expect(defs).toMatchInlineSnapshot(`
1084
+ [
1085
+ {
1086
+ "endLine": 9,
1087
+ "exported": false,
1088
+ "line": 1,
1089
+ "name": "ProcessData",
1090
+ "type": "function",
1091
+ },
1092
+ ]
1093
+ `);
1094
+ });
1095
+ test('init function is extracted if large', async () => {
1096
+ const code = `func init() {
1097
+ config = loadConfig()
1098
+ db = connectDB()
1099
+ cache = initCache()
1100
+ logger = setupLogger()
1101
+ metrics = initMetrics()
1102
+ validator = newValidator()
1103
+ handlers = registerHandlers()
1104
+ }`;
1105
+ const defs = await getDefinitions(code, 'go');
1106
+ expect(defs).toMatchInlineSnapshot(`
1107
+ [
1108
+ {
1109
+ "endLine": 9,
1110
+ "exported": false,
1111
+ "line": 1,
1112
+ "name": "init",
1113
+ "type": "function",
1114
+ },
1115
+ ]
1116
+ `);
1117
+ });
1118
+ test('type alias', async () => {
1119
+ const code = `type UserID = int64`;
1120
+ const defs = await getDefinitions(code, 'go');
1121
+ expect(defs).toMatchInlineSnapshot(`[]`);
1122
+ });
1123
+ });
1124
+ // ============================================================================
1125
+ // Edge Cases
1126
+ // ============================================================================
1127
+ describe('edge cases', () => {
1128
+ test('empty file', async () => {
1129
+ const defs = await getDefinitions('', 'typescript');
1130
+ expect(defs).toMatchInlineSnapshot(`[]`);
1131
+ });
1132
+ test('only comments', async () => {
1133
+ const code = `// This is a comment
1134
+ // Another comment
1135
+ /* Block comment */`;
1136
+ const defs = await getDefinitions(code, 'typescript');
1137
+ expect(defs).toMatchInlineSnapshot(`[]`);
1138
+ });
1139
+ test('only imports', async () => {
1140
+ const code = `import { foo } from 'bar'
1141
+ import * as baz from 'qux'`;
1142
+ const defs = await getDefinitions(code, 'typescript');
1143
+ expect(defs).toMatchInlineSnapshot(`[]`);
1144
+ });
1145
+ test('nested functions - only top level', async () => {
1146
+ const code = `function outer(x: number): number {
1147
+ function inner(y: number): number {
1148
+ return y * 2
1149
+ }
1150
+ const result = inner(x)
1151
+ const doubled = result * 2
1152
+ const tripled = result * 3
1153
+ return doubled + tripled
1154
+ }`;
1155
+ const defs = await getDefinitions(code, 'typescript');
1156
+ expect(defs).toMatchInlineSnapshot(`
1157
+ [
1158
+ {
1159
+ "endLine": 9,
1160
+ "exported": false,
1161
+ "line": 1,
1162
+ "name": "outer",
1163
+ "type": "function",
1164
+ },
1165
+ ]
1166
+ `);
1167
+ });
1168
+ test('boundary: exactly 8 lines included', async () => {
1169
+ const code = `function eightLines(): void {
1170
+ const a = 1
1171
+ const b = 2
1172
+ const c = 3
1173
+ const d = 4
1174
+ const e = 5
1175
+ console.log(a, b, c, d, e)
1176
+ }`;
1177
+ const defs = await getDefinitions(code, 'typescript');
1178
+ expect(defs).toMatchInlineSnapshot(`
1179
+ [
1180
+ {
1181
+ "endLine": 8,
1182
+ "exported": false,
1183
+ "line": 1,
1184
+ "name": "eightLines",
1185
+ "type": "function",
1186
+ },
1187
+ ]
1188
+ `);
1189
+ });
1190
+ test('boundary: exactly 7 lines excluded', async () => {
1191
+ const code = `function sevenLines(): void {
1192
+ const a = 1
1193
+ const b = 2
1194
+ const c = 3
1195
+ const d = 4
1196
+ console.log(a, b, c, d)
1197
+ }`;
1198
+ const defs = await getDefinitions(code, 'typescript');
1199
+ expect(defs).toMatchInlineSnapshot(`
1200
+ [
1201
+ {
1202
+ "endLine": 7,
1203
+ "exported": false,
1204
+ "line": 1,
1205
+ "name": "sevenLines",
1206
+ "type": "function",
1207
+ },
1208
+ ]
1209
+ `);
1210
+ });
1211
+ test('function with multiline signature', async () => {
1212
+ const code = `function complexSignature(
1213
+ param1: string,
1214
+ param2: number,
1215
+ param3: boolean,
1216
+ param4: object
1217
+ ): Result {
1218
+ const combined = combine(param1, param2)
1219
+ const processed = process(combined, param3)
1220
+ return finalize(processed, param4)
1221
+ }`;
1222
+ const defs = await getDefinitions(code, 'typescript');
1223
+ expect(defs).toMatchInlineSnapshot(`
1224
+ [
1225
+ {
1226
+ "endLine": 10,
1227
+ "exported": false,
1228
+ "line": 1,
1229
+ "name": "complexSignature",
1230
+ "type": "function",
1231
+ },
1232
+ ]
1233
+ `);
1234
+ });
1235
+ test('class with static members', async () => {
1236
+ const code = `class Singleton {
1237
+ private static instance: Singleton
1238
+
1239
+ private constructor() {}
1240
+
1241
+ static getInstance(): Singleton {
1242
+ if (!Singleton.instance) {
1243
+ Singleton.instance = new Singleton()
1244
+ }
1245
+ return Singleton.instance
1246
+ }
1247
+ }`;
1248
+ const defs = await getDefinitions(code, 'typescript');
1249
+ expect(defs).toMatchInlineSnapshot(`
1250
+ [
1251
+ {
1252
+ "endLine": 12,
1253
+ "exported": false,
1254
+ "line": 1,
1255
+ "name": "Singleton",
1256
+ "type": "class",
1257
+ },
1258
+ ]
1259
+ `);
1260
+ });
1261
+ test('re-export statement', async () => {
1262
+ const code = `export { foo, bar } from './module'
1263
+ export * from './other'`;
1264
+ const defs = await getDefinitions(code, 'typescript');
1265
+ expect(defs).toMatchInlineSnapshot(`[]`);
1266
+ });
1267
+ test('interface with generics', async () => {
1268
+ const code = `interface Repository<T, ID> {
1269
+ find(id: ID): Promise<T | null>
1270
+ findAll(): Promise<T[]>
1271
+ save(entity: T): Promise<T>
1272
+ delete(id: ID): Promise<void>
1273
+ }`;
1274
+ const defs = await getDefinitions(code, 'typescript');
1275
+ expect(defs).toMatchInlineSnapshot(`
1276
+ [
1277
+ {
1278
+ "endLine": 6,
1279
+ "exported": false,
1280
+ "line": 1,
1281
+ "name": "Repository",
1282
+ "type": "interface",
1283
+ },
1284
+ ]
1285
+ `);
1286
+ });
1287
+ test('mapped type', async () => {
1288
+ const code = `type Readonly<T> = {
1289
+ readonly [P in keyof T]: T[P]
1290
+ }`;
1291
+ const defs = await getDefinitions(code, 'typescript');
1292
+ expect(defs).toMatchInlineSnapshot(`
1293
+ [
1294
+ {
1295
+ "endLine": 3,
1296
+ "exported": false,
1297
+ "line": 1,
1298
+ "name": "Readonly",
1299
+ "type": "type",
1300
+ },
1301
+ ]
1302
+ `);
1303
+ });
1304
+ test('conditional type', async () => {
1305
+ const code = `type NonNullable<T> = T extends null | undefined ? never : T`;
1306
+ const defs = await getDefinitions(code, 'typescript');
1307
+ expect(defs).toMatchInlineSnapshot(`
1308
+ [
1309
+ {
1310
+ "endLine": 1,
1311
+ "exported": false,
1312
+ "line": 1,
1313
+ "name": "NonNullable",
1314
+ "type": "type",
1315
+ },
1316
+ ]
1317
+ `);
1318
+ });
1319
+ test('overloaded function signatures', async () => {
1320
+ const code = `function process(input: string): string
1321
+ function process(input: number): number
1322
+ function process(input: string | number): string | number {
1323
+ if (typeof input === 'string') {
1324
+ return input.toUpperCase()
1325
+ }
1326
+ return input * 2
1327
+ }`;
1328
+ const defs = await getDefinitions(code, 'typescript');
1329
+ expect(defs).toMatchInlineSnapshot(`
1330
+ [
1331
+ {
1332
+ "endLine": 8,
1333
+ "exported": false,
1334
+ "line": 3,
1335
+ "name": "process",
1336
+ "type": "function",
1337
+ },
1338
+ ]
1339
+ `);
1340
+ });
1341
+ test('namespace', async () => {
1342
+ const code = `namespace Utils {
1343
+ export function helper(x: number): number {
1344
+ return x * 2
1345
+ }
1346
+
1347
+ export const VERSION = '1.0.0'
1348
+ }`;
1349
+ const defs = await getDefinitions(code, 'typescript');
1350
+ expect(defs).toMatchInlineSnapshot(`[]`);
1351
+ });
1352
+ test('declare statements', async () => {
1353
+ const code = `declare function external(x: string): void
1354
+ declare class ExternalClass {
1355
+ method(): void
1356
+ }
1357
+ declare const CONFIG: { url: string }`;
1358
+ const defs = await getDefinitions(code, 'typescript');
1359
+ expect(defs).toMatchInlineSnapshot(`[]`);
1360
+ });
1361
+ test('unicode identifiers', async () => {
1362
+ const code = `function 计算(输入: number): number {
1363
+ const 结果 = 输入 * 2
1364
+ const 验证 = validate(结果)
1365
+ const 处理 = process(验证)
1366
+ const 格式化 = format(处理)
1367
+ const 输出 = finalize(格式化)
1368
+ return 输出
1369
+ }`;
1370
+ const defs = await getDefinitions(code, 'typescript');
1371
+ expect(defs).toMatchInlineSnapshot(`
1372
+ [
1373
+ {
1374
+ "endLine": 8,
1375
+ "exported": false,
1376
+ "line": 1,
1377
+ "name": "计算",
1378
+ "type": "function",
1379
+ },
1380
+ ]
1381
+ `);
1382
+ });
1383
+ test('private class fields', async () => {
1384
+ const code = `class SecureStorage {
1385
+ #data: Map<string, string> = new Map()
1386
+ #key: string
1387
+
1388
+ constructor(key: string) {
1389
+ this.#key = key
1390
+ }
1391
+
1392
+ #encrypt(value: string): string {
1393
+ return btoa(value + this.#key)
1394
+ }
1395
+
1396
+ set(key: string, value: string): void {
1397
+ this.#data.set(key, this.#encrypt(value))
1398
+ }
1399
+ }`;
1400
+ const defs = await getDefinitions(code, 'typescript');
1401
+ expect(defs).toMatchInlineSnapshot(`
1402
+ [
1403
+ {
1404
+ "endLine": 16,
1405
+ "exported": false,
1406
+ "line": 1,
1407
+ "name": "SecureStorage",
1408
+ "type": "class",
1409
+ },
1410
+ ]
1411
+ `);
1412
+ });
1413
+ });
1414
+ //# sourceMappingURL=definitions.test.js.map