@aiready/context-analyzer 0.9.4 → 0.9.5

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 (75) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/.turbo/turbo-test.log +21 -9
  3. package/README.md +8 -0
  4. package/dist/__tests__/analyzer.test.d.ts +2 -0
  5. package/dist/__tests__/analyzer.test.d.ts.map +1 -0
  6. package/dist/__tests__/analyzer.test.js +157 -0
  7. package/dist/__tests__/analyzer.test.js.map +1 -0
  8. package/dist/__tests__/auto-detection.test.d.ts +2 -0
  9. package/dist/__tests__/auto-detection.test.d.ts.map +1 -0
  10. package/dist/__tests__/auto-detection.test.js +132 -0
  11. package/dist/__tests__/auto-detection.test.js.map +1 -0
  12. package/dist/__tests__/enhanced-cohesion.test.d.ts +2 -0
  13. package/dist/__tests__/enhanced-cohesion.test.d.ts.map +1 -0
  14. package/dist/__tests__/enhanced-cohesion.test.js +109 -0
  15. package/dist/__tests__/enhanced-cohesion.test.js.map +1 -0
  16. package/dist/__tests__/fragmentation-advanced.test.d.ts +2 -0
  17. package/dist/__tests__/fragmentation-advanced.test.d.ts.map +1 -0
  18. package/dist/__tests__/fragmentation-advanced.test.js +50 -0
  19. package/dist/__tests__/fragmentation-advanced.test.js.map +1 -0
  20. package/dist/__tests__/fragmentation-coupling.test.d.ts +2 -0
  21. package/dist/__tests__/fragmentation-coupling.test.d.ts.map +1 -0
  22. package/dist/__tests__/fragmentation-coupling.test.js +52 -0
  23. package/dist/__tests__/fragmentation-coupling.test.js.map +1 -0
  24. package/dist/__tests__/fragmentation-log.test.d.ts +2 -0
  25. package/dist/__tests__/fragmentation-log.test.d.ts.map +1 -0
  26. package/dist/__tests__/fragmentation-log.test.js +33 -0
  27. package/dist/__tests__/fragmentation-log.test.js.map +1 -0
  28. package/dist/__tests__/scoring.test.d.ts +2 -0
  29. package/dist/__tests__/scoring.test.d.ts.map +1 -0
  30. package/dist/__tests__/scoring.test.js +118 -0
  31. package/dist/__tests__/scoring.test.js.map +1 -0
  32. package/dist/__tests__/structural-cohesion.test.d.ts +2 -0
  33. package/dist/__tests__/structural-cohesion.test.d.ts.map +1 -0
  34. package/dist/__tests__/structural-cohesion.test.js +29 -0
  35. package/dist/__tests__/structural-cohesion.test.js.map +1 -0
  36. package/dist/analyzer.d.ts +100 -0
  37. package/dist/analyzer.d.ts.map +1 -0
  38. package/dist/analyzer.js +701 -0
  39. package/dist/analyzer.js.map +1 -0
  40. package/dist/analyzers/python-context.d.ts +38 -0
  41. package/dist/analyzers/python-context.d.ts.map +1 -0
  42. package/dist/analyzers/python-context.js +232 -0
  43. package/dist/analyzers/python-context.js.map +1 -0
  44. package/dist/chunk-BD4NWUVG.mjs +1242 -0
  45. package/dist/cli.d.ts.map +1 -0
  46. package/dist/cli.js +139 -13
  47. package/dist/cli.js.map +1 -0
  48. package/dist/cli.mjs +1 -1
  49. package/dist/index.d.mts +3 -0
  50. package/dist/index.d.ts +3 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +139 -13
  53. package/dist/index.js.map +1 -0
  54. package/dist/index.mjs +1 -1
  55. package/dist/scoring.d.ts +13 -0
  56. package/dist/scoring.d.ts.map +1 -0
  57. package/dist/scoring.js +133 -0
  58. package/dist/scoring.js.map +1 -0
  59. package/dist/semantic-analysis.d.ts +44 -0
  60. package/dist/semantic-analysis.d.ts.map +1 -0
  61. package/dist/semantic-analysis.js +241 -0
  62. package/dist/semantic-analysis.js.map +1 -0
  63. package/dist/types.d.ts +117 -0
  64. package/dist/types.d.ts.map +1 -0
  65. package/dist/types.js +2 -0
  66. package/dist/types.js.map +1 -0
  67. package/package.json +2 -2
  68. package/src/__tests__/fragmentation-advanced.test.ts +60 -0
  69. package/src/__tests__/fragmentation-coupling.test.ts +62 -0
  70. package/src/__tests__/fragmentation-log.test.ts +38 -0
  71. package/src/__tests__/structural-cohesion.test.ts +32 -0
  72. package/src/analyzer.ts +193 -18
  73. package/src/index.ts +34 -2
  74. package/src/types.ts +3 -0
  75. package/tsconfig.tsbuildinfo +1 -0
@@ -1,6 +1,6 @@
1
1
 
2
2
  
3
- > @aiready/context-analyzer@0.9.4 build /Users/pengcao/projects/aiready/packages/context-analyzer
3
+ > @aiready/context-analyzer@0.9.5 build /Users/pengcao/projects/aiready/packages/context-analyzer
4
4
  > tsup src/index.ts src/cli.ts --format cjs,esm --dts
5
5
 
6
6
  CLI Building entry: src/cli.ts, src/index.ts
@@ -9,18 +9,18 @@
9
9
  CLI Target: es2020
10
10
  CJS Build start
11
11
  ESM Build start
12
+ CJS dist/cli.js 62.78 KB
13
+ CJS dist/index.js 51.03 KB
14
+ CJS ⚡️ Build success in 69ms
12
15
  ESM dist/index.mjs 585.00 B
13
- ESM dist/python-context-UOPTQH44.mjs 5.64 KB
14
- ESM dist/chunk-JZ2SE4DB.mjs 37.53 KB
15
16
  ESM dist/cli.mjs 18.53 KB
16
17
  ESM dist/chunk-Y6FXYEAI.mjs 390.00 B
17
- ESM ⚡️ Build success in 71ms
18
- CJS dist/index.js 45.81 KB
19
- CJS dist/cli.js 57.56 KB
20
- CJS ⚡️ Build success in 72ms
18
+ ESM dist/chunk-BD4NWUVG.mjs 42.75 KB
19
+ ESM dist/python-context-UOPTQH44.mjs 5.64 KB
20
+ ESM ⚡️ Build success in 91ms
21
21
  DTS Build start
22
- DTS ⚡️ Build success in 1424ms
22
+ DTS ⚡️ Build success in 1944ms
23
23
  DTS dist/cli.d.ts 20.00 B
24
- DTS dist/index.d.ts 5.95 KB
24
+ DTS dist/index.d.ts 6.03 KB
25
25
  DTS dist/cli.d.mts 20.00 B
26
- DTS dist/index.d.mts 5.95 KB
26
+ DTS dist/index.d.mts 6.03 KB
@@ -1,19 +1,31 @@
1
1
 
2
2
  
3
- > @aiready/context-analyzer@0.9.4 test /Users/pengcao/projects/aiready/packages/context-analyzer
3
+ > @aiready/context-analyzer@0.9.5 test /Users/pengcao/projects/aiready/packages/context-analyzer
4
4
  > vitest run
5
5
 
6
6
  [?25l
7
7
   RUN  v4.0.18 /Users/pengcao/projects/aiready/packages/context-analyzer
8
8
 
9
- ✓ src/__tests__/scoring.test.ts (7 tests) 16ms
10
- ✓ src/__tests__/enhanced-cohesion.test.ts (6 tests) 9ms
11
- ✓ src/__tests__/auto-detection.test.ts (8 tests) 19ms
12
- ✓ src/__tests__/analyzer.test.ts (14 tests) 41ms
9
+ ✓ src/__tests__/scoring.test.ts (7 tests) 5ms
10
+ ✓ dist/__tests__/scoring.test.js (7 tests) 2ms
11
+ ✓ src/__tests__/enhanced-cohesion.test.ts (6 tests) 3ms
12
+ ✓ src/__tests__/fragmentation-coupling.test.ts (2 tests) 19ms
13
+ ✓ src/__tests__/analyzer.test.ts (14 tests) 35ms
14
+ ✓ src/__tests__/auto-detection.test.ts (8 tests) 28ms
15
+ ✓ src/__tests__/fragmentation-advanced.test.ts (3 tests) 6ms
16
+ ✓ dist/__tests__/fragmentation-advanced.test.js (3 tests) 2ms
17
+ ✓ dist/__tests__/enhanced-cohesion.test.js (6 tests) 31ms
18
+ ✓ dist/__tests__/fragmentation-coupling.test.js (2 tests) 29ms
19
+ ✓ dist/__tests__/analyzer.test.js (14 tests) 101ms
20
+ ✓ dist/__tests__/auto-detection.test.js (8 tests) 111ms
21
+ ✓ dist/__tests__/fragmentation-log.test.js (3 tests) 2ms
22
+ ✓ src/__tests__/fragmentation-log.test.ts (3 tests) 1ms
23
+ ✓ dist/__tests__/structural-cohesion.test.js (4 tests) 1ms
24
+ ✓ src/__tests__/structural-cohesion.test.ts (4 tests) 1ms
13
25
 
14
-  Test Files  4 passed (4)
15
-  Tests  35 passed (35)
16
-  Start at  22:45:25
17
-  Duration  1.60s (transform 898ms, setup 0ms, import 2.84s, tests 85ms, environment 0ms)
26
+  Test Files  16 passed (16)
27
+  Tests  94 passed (94)
28
+  Start at  22:40:01
29
+  Duration  1.72s (transform 2.67s, setup 0ms, import 12.51s, tests 378ms, environment 1ms)
18
30
 
19
31
  [?25h
package/README.md CHANGED
@@ -250,6 +250,14 @@ src/user/types.ts // 600 tokens
250
250
 
251
251
  **🎯 Recommendation:** Domains with high fragmentation should be **consolidated** into cohesive modules.
252
252
 
253
+ ### 🔎 Additional Fragmentation Signals (new)
254
+
255
+ - **Path Entropy:** A normalized Shannon-entropy over directory distribution. Distinguishes "9 files in one folder + 1 outlier" from "5 and 5" — higher entropy means more evenly spread (worse).
256
+ - **Directory Distance:** Measures how deep the common ancestor is across file pairs. Files sharing a deep common ancestor are considered "closer" (lower distance) than files only sharing the repo root.
257
+ - **Import Cohesion (coupling discount):** If fragmented files actively import each other, the analyzer reduces the fragmentation penalty (up to 20%). This rewards intentional modularization where cross-folder imports are a natural design instead of accidental scattering.
258
+
259
+ These signals are included in the JSON summary (`fragmentedModules` entries) so you can prioritize consolidation efforts with more context: `fragmentationScore`, `pathEntropy`, `directoryDistance`, and `importCohesion`.
260
+
253
261
  ---
254
262
 
255
263
  ### ⚖️ The Tradeoff: Splitting vs. Consolidating
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=analyzer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/analyzer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,157 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { buildDependencyGraph, calculateImportDepth, getTransitiveDependencies, calculateContextBudget, detectCircularDependencies, calculateCohesion, calculateFragmentation, } from '../analyzer';
3
+ describe('buildDependencyGraph', () => {
4
+ it('should build a basic dependency graph', () => {
5
+ const files = [
6
+ {
7
+ file: 'a.ts',
8
+ content: `
9
+ export const a = 1;
10
+ `,
11
+ },
12
+ {
13
+ file: 'b.ts',
14
+ content: `
15
+ import { a } from './a';
16
+ export const b = a + 1;
17
+ `,
18
+ },
19
+ ];
20
+ const graph = buildDependencyGraph(files);
21
+ expect(graph.nodes.size).toBe(2);
22
+ expect(graph.edges.get('b.ts')?.has('./a')).toBe(true);
23
+ });
24
+ });
25
+ describe('calculateImportDepth', () => {
26
+ it('should calculate import depth correctly', () => {
27
+ const files = [
28
+ { file: 'a.ts', content: 'export const a = 1;' },
29
+ { file: 'b.ts', content: 'import { a } from "a.ts";\nexport const b = a;' },
30
+ { file: 'c.ts', content: 'import { b } from "b.ts";\nexport const c = b;' },
31
+ ];
32
+ const graph = buildDependencyGraph(files);
33
+ expect(calculateImportDepth('a.ts', graph)).toBe(0);
34
+ expect(calculateImportDepth('b.ts', graph)).toBe(1);
35
+ expect(calculateImportDepth('c.ts', graph)).toBe(2);
36
+ });
37
+ it('should handle circular dependencies gracefully', () => {
38
+ const files = [
39
+ { file: 'a.ts', content: 'import { b } from "./b";\nexport const a = 1;' },
40
+ { file: 'b.ts', content: 'import { a } from "./a";\nexport const b = 2;' },
41
+ ];
42
+ const graph = buildDependencyGraph(files);
43
+ // Should not infinite loop
44
+ const depth = calculateImportDepth('a.ts', graph);
45
+ expect(depth).toBeGreaterThanOrEqual(0);
46
+ });
47
+ });
48
+ describe('getTransitiveDependencies', () => {
49
+ it('should get all transitive dependencies', () => {
50
+ const files = [
51
+ { file: 'a.ts', content: 'export const a = 1;' },
52
+ { file: 'b.ts', content: 'import { a } from "a.ts";\nexport const b = a;' },
53
+ { file: 'c.ts', content: 'import { b } from "b.ts";\nexport const c = b;' },
54
+ ];
55
+ const graph = buildDependencyGraph(files);
56
+ const deps = getTransitiveDependencies('c.ts', graph);
57
+ expect(deps).toContain('b.ts');
58
+ expect(deps).toContain('a.ts');
59
+ expect(deps.length).toBe(2);
60
+ });
61
+ });
62
+ describe('calculateContextBudget', () => {
63
+ it('should calculate total token cost including dependencies', () => {
64
+ const files = [
65
+ { file: 'a.ts', content: 'export const a = 1;'.repeat(10) }, // ~40 tokens
66
+ { file: 'b.ts', content: 'import { a } from "./a";\nexport const b = a;'.repeat(10) }, // ~60 tokens
67
+ ];
68
+ const graph = buildDependencyGraph(files);
69
+ const budget = calculateContextBudget('b.ts', graph);
70
+ // Should include both files' tokens
71
+ expect(budget).toBeGreaterThan(80);
72
+ });
73
+ });
74
+ describe('detectCircularDependencies', () => {
75
+ it('should detect circular dependencies', () => {
76
+ const files = [
77
+ { file: 'a.ts', content: 'import { b } from "b.ts";\nexport const a = 1;' },
78
+ { file: 'b.ts', content: 'import { a } from "a.ts";\nexport const b = 2;' },
79
+ ];
80
+ const graph = buildDependencyGraph(files);
81
+ const cycles = detectCircularDependencies(graph);
82
+ expect(cycles.length).toBeGreaterThan(0);
83
+ });
84
+ it('should return empty for no circular dependencies', () => {
85
+ const files = [
86
+ { file: 'a.ts', content: 'export const a = 1;' },
87
+ { file: 'b.ts', content: 'import { a } from "a.ts";\nexport const b = a;' },
88
+ ];
89
+ const graph = buildDependencyGraph(files);
90
+ const cycles = detectCircularDependencies(graph);
91
+ expect(cycles.length).toBe(0);
92
+ });
93
+ });
94
+ describe('calculateCohesion', () => {
95
+ it('should return 1 for single export', () => {
96
+ const exports = [{ name: 'foo', type: 'function', inferredDomain: 'user' }];
97
+ expect(calculateCohesion(exports)).toBe(1);
98
+ });
99
+ it('should return high cohesion for related exports', () => {
100
+ const exports = [
101
+ { name: 'getUser', type: 'function', inferredDomain: 'user' },
102
+ { name: 'updateUser', type: 'function', inferredDomain: 'user' },
103
+ { name: 'deleteUser', type: 'function', inferredDomain: 'user' },
104
+ ];
105
+ const cohesion = calculateCohesion(exports);
106
+ expect(cohesion).toBeGreaterThan(0.9);
107
+ });
108
+ it('should return low cohesion for mixed exports', () => {
109
+ const exports = [
110
+ { name: 'getUser', type: 'function', inferredDomain: 'user' },
111
+ { name: 'getOrder', type: 'function', inferredDomain: 'order' },
112
+ { name: 'parseConfig', type: 'function', inferredDomain: 'config' },
113
+ ];
114
+ const cohesion = calculateCohesion(exports);
115
+ expect(cohesion).toBeLessThan(0.5);
116
+ });
117
+ it('should return 1 for test files even with mixed domains', () => {
118
+ const exports = [
119
+ { name: 'mockUser', type: 'function', inferredDomain: 'user' },
120
+ { name: 'mockOrder', type: 'function', inferredDomain: 'order' },
121
+ { name: 'setupTestDb', type: 'function', inferredDomain: 'helper' },
122
+ ];
123
+ // Test file - should return 1 despite mixed domains
124
+ const cohesionTestFile = calculateCohesion(exports, 'src/__tests__/helpers.test.ts');
125
+ expect(cohesionTestFile).toBe(1);
126
+ // Mock file - should return 1 despite mixed domains
127
+ const cohesionMockFile = calculateCohesion(exports, 'src/test-utils/mocks.ts');
128
+ expect(cohesionMockFile).toBe(1);
129
+ // Fixture file - should return 1 despite mixed domains
130
+ const cohesionFixtureFile = calculateCohesion(exports, 'src/fixtures/data.ts');
131
+ expect(cohesionFixtureFile).toBe(1);
132
+ // Regular file - should have low cohesion
133
+ const cohesionRegularFile = calculateCohesion(exports, 'src/utils/helpers.ts');
134
+ expect(cohesionRegularFile).toBeLessThan(0.5);
135
+ });
136
+ });
137
+ describe('calculateFragmentation', () => {
138
+ it('should return 0 for single file', () => {
139
+ const files = ['src/user/user.ts'];
140
+ expect(calculateFragmentation(files, 'user')).toBe(0);
141
+ });
142
+ it('should return 0 for files in same directory', () => {
143
+ const files = ['src/user/get.ts', 'src/user/update.ts'];
144
+ expect(calculateFragmentation(files, 'user')).toBe(0);
145
+ });
146
+ it('should return high fragmentation for scattered files', () => {
147
+ const files = [
148
+ 'src/user/get.ts',
149
+ 'src/api/user.ts',
150
+ 'src/services/user.ts',
151
+ 'src/helpers/user.ts',
152
+ ];
153
+ const fragmentation = calculateFragmentation(files, 'user');
154
+ expect(fragmentation).toBeGreaterThan(0.8);
155
+ });
156
+ });
157
+ //# sourceMappingURL=analyzer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.test.js","sourceRoot":"","sources":["../../src/__tests__/analyzer.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,sBAAsB,EACtB,0BAA0B,EAC1B,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,KAAK,GAAG;YACZ;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;;SAER;aACF;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;;;SAGR;aACF;SACF,CAAC;QAEF,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAE1C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE;YAChD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gDAAgD,EAAE;YAC3E,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gDAAgD,EAAE;SAC5E,CAAC;QAEF,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAE1C,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,+CAA+C,EAAE;YAC1E,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,+CAA+C,EAAE;SAC3E,CAAC;QAEF,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAE1C,2BAA2B;QAC3B,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE;YAChD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gDAAgD,EAAE;YAC3E,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gDAAgD,EAAE;SAC5E,CAAC;QAEF,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,yBAAyB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAEtD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,aAAa;YAC1E,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,+CAA+C,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,aAAa;SACrG,CAAC;QAEF,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,sBAAsB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAErD,oCAAoC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gDAAgD,EAAE;YAC3E,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gDAAgD,EAAE;SAC5E,CAAC;QAEF,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,0BAA0B,CAAC,KAAK,CAAC,CAAC;QAEjD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE;YAChD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gDAAgD,EAAE;SAC5E,CAAC;QAEF,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,0BAA0B,CAAC,KAAK,CAAC,CAAC;QAEjD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC;QACrF,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,OAAO,GAAG;YACd,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,MAAM,EAAE;YACtE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,MAAM,EAAE;YACzE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,MAAM,EAAE;SAC1E,CAAC;QAEF,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,CAAC,QAAQ,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,OAAO,GAAG;YACd,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,MAAM,EAAE;YACtE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,OAAO,EAAE;YACxE,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,QAAQ,EAAE;SAC7E,CAAC;QAEF,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,OAAO,GAAG;YACd,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,MAAM,EAAE;YACvE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,OAAO,EAAE;YACzE,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,UAAmB,EAAE,cAAc,EAAE,QAAQ,EAAE;SAC7E,CAAC;QAEF,oDAAoD;QACpD,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,OAAO,EAAE,+BAA+B,CAAC,CAAC;QACrF,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEjC,oDAAoD;QACpD,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,OAAO,EAAE,yBAAyB,CAAC,CAAC;QAC/E,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEjC,uDAAuD;QACvD,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;QAC/E,MAAM,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEpC,0CAA0C;QAC1C,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;QAC/E,MAAM,CAAC,mBAAmB,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,KAAK,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACnC,MAAM,CAAC,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;QACxD,MAAM,CAAC,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,KAAK,GAAG;YACZ,iBAAiB;YACjB,iBAAiB;YACjB,sBAAsB;YACtB,qBAAqB;SACtB,CAAC;QAEF,MAAM,aAAa,GAAG,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5D,MAAM,CAAC,aAAa,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auto-detection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-detection.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/auto-detection.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,132 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { buildDependencyGraph } from '../analyzer';
3
+ describe('Auto-detection from folder structure', () => {
4
+ it('should auto-detect domain keywords from folder paths', () => {
5
+ const files = [
6
+ {
7
+ file: 'src/payments/process.ts',
8
+ content: 'export function processPayment() { return 1; }',
9
+ },
10
+ {
11
+ file: 'src/orders/create.ts',
12
+ content: 'export function createOrder() { return 2; }',
13
+ },
14
+ ];
15
+ const graph = buildDependencyGraph(files);
16
+ const paymentsNode = graph.nodes.get('src/payments/process.ts');
17
+ const ordersNode = graph.nodes.get('src/orders/create.ts');
18
+ // Should detect 'payment' from processPayment (now part of auto-detected keywords)
19
+ expect(paymentsNode?.exports[0].inferredDomain).toBe('payment');
20
+ // Should detect 'order' from createOrder
21
+ expect(ordersNode?.exports[0].inferredDomain).toBe('order');
22
+ });
23
+ it('should detect domains from nested folders', () => {
24
+ const files = [
25
+ {
26
+ file: 'src/api/invoices/handler.ts',
27
+ content: 'export function handleRequest() { return 1; }',
28
+ },
29
+ ];
30
+ const graph = buildDependencyGraph(files);
31
+ const node = graph.nodes.get('src/api/invoices/handler.ts');
32
+ // Should detect 'invoice' from path (invoices folder)
33
+ expect(node?.exports[0].inferredDomain).toBe('invoice');
34
+ });
35
+ it('should skip common infrastructure folders', () => {
36
+ const files = [
37
+ {
38
+ file: 'src/utils/helpers/format.ts',
39
+ content: 'export function formatData() { return 1; }',
40
+ },
41
+ ];
42
+ const graph = buildDependencyGraph(files);
43
+ const node = graph.nodes.get('src/utils/helpers/format.ts');
44
+ // 'utils' and 'helpers' should be skipped, no domain detected
45
+ expect(node?.exports[0].inferredDomain).toBe('unknown');
46
+ });
47
+ it('should merge auto-detected with custom keywords', () => {
48
+ const files = [
49
+ {
50
+ file: 'src/receipts/scan.ts',
51
+ content: 'export function scanReceipt() { return 1; }',
52
+ },
53
+ ];
54
+ const graph = buildDependencyGraph(files, {
55
+ domainKeywords: ['receipt'], // Custom keyword
56
+ });
57
+ const node = graph.nodes.get('src/receipts/scan.ts');
58
+ // Should detect 'receipt' from both auto-detection AND custom keywords
59
+ expect(node?.exports[0].inferredDomain).toBe('receipt');
60
+ });
61
+ });
62
+ describe('Import-path domain inference', () => {
63
+ it('should infer domain from import paths', () => {
64
+ const files = [
65
+ {
66
+ file: 'src/lib/session.ts',
67
+ content: `
68
+ import { processPayment } from '../payments/processor';
69
+ export function createSession() { return 1; }
70
+ `,
71
+ },
72
+ {
73
+ file: 'src/payments/processor.ts',
74
+ content: 'export function processPayment() { return 2; }',
75
+ },
76
+ ];
77
+ const graph = buildDependencyGraph(files);
78
+ const sessionNode = graph.nodes.get('src/lib/session.ts');
79
+ // session.ts imports from '../payments/...' so should infer 'payment' domain
80
+ expect(sessionNode?.exports[0].inferredDomain).toBe('payment');
81
+ });
82
+ it('should infer domain from absolute import paths', () => {
83
+ const files = [
84
+ {
85
+ file: 'src/components/nav-links.ts',
86
+ content: `
87
+ import { getOrders } from '@/orders/service';
88
+ export function NavLinks() { return 'nav'; }
89
+ `,
90
+ },
91
+ {
92
+ file: 'src/orders/service.ts',
93
+ content: 'export function getOrders() { return []; }',
94
+ },
95
+ ];
96
+ const graph = buildDependencyGraph(files);
97
+ const navNode = graph.nodes.get('src/components/nav-links.ts');
98
+ // nav-links.ts imports from '@/orders/...' so should infer 'order' domain
99
+ expect(navNode?.exports[0].inferredDomain).toBe('order');
100
+ });
101
+ it('should use identifier name first before import-path fallback', () => {
102
+ const files = [
103
+ {
104
+ file: 'src/lib/handler.ts',
105
+ content: `
106
+ import { processPayment } from '../payments/processor';
107
+ export function processInvoice() { return 1; }
108
+ `,
109
+ },
110
+ ];
111
+ const graph = buildDependencyGraph(files);
112
+ const node = graph.nodes.get('src/lib/handler.ts');
113
+ // processInvoice should match 'invoice' from identifier, not 'payment' from imports
114
+ expect(node?.exports[0].inferredDomain).toBe('invoice');
115
+ });
116
+ it('should fall back to import-path when identifier is generic', () => {
117
+ const files = [
118
+ {
119
+ file: 'src/lib/dynamodb.ts',
120
+ content: `
121
+ import { Customer } from '../customers/model';
122
+ export function connect() { return 1; }
123
+ `,
124
+ },
125
+ ];
126
+ const graph = buildDependencyGraph(files);
127
+ const node = graph.nodes.get('src/lib/dynamodb.ts');
128
+ // 'connect' is generic, should infer 'customer' from import path
129
+ expect(node?.exports[0].inferredDomain).toBe('customer');
130
+ });
131
+ });
132
+ //# sourceMappingURL=auto-detection.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-detection.test.js","sourceRoot":"","sources":["../../src/__tests__/auto-detection.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=enhanced-cohesion.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enhanced-cohesion.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/enhanced-cohesion.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,109 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { calculateCohesion } from '../analyzer';
3
+ describe('Enhanced Cohesion Calculation', () => {
4
+ it('should use domain-based cohesion when no import data available', () => {
5
+ const exports = [
6
+ { name: 'getUserData', type: 'function', inferredDomain: 'user' },
7
+ { name: 'getProductData', type: 'function', inferredDomain: 'product' },
8
+ ];
9
+ const cohesion = calculateCohesion(exports);
10
+ // With mixed domains (user, product) and no import data, should use domain-based calculation
11
+ // Domain entropy for 2 different domains = low cohesion
12
+ expect(cohesion).toBeLessThan(0.5);
13
+ });
14
+ it('should use import-based cohesion when import data available', () => {
15
+ const exports = [
16
+ {
17
+ name: 'getUserData',
18
+ type: 'function',
19
+ inferredDomain: 'user',
20
+ imports: ['react', 'axios', 'lodash'],
21
+ },
22
+ {
23
+ name: 'getProductData',
24
+ type: 'function',
25
+ inferredDomain: 'product',
26
+ imports: ['react', 'axios', 'lodash'], // Same imports!
27
+ },
28
+ ];
29
+ const cohesion = calculateCohesion(exports);
30
+ // Even though domains differ, imports are identical (Jaccard = 1.0)
31
+ // Enhanced cohesion = 0.6 * 1.0 + 0.4 * 0.0 (different domains) = 0.6
32
+ // Should be >= 0.6 (import-based weight)
33
+ expect(cohesion).toBeGreaterThanOrEqual(0.6);
34
+ });
35
+ it('should weight import-based similarity higher than domain-based', () => {
36
+ const exportsWithSharedImports = [
37
+ {
38
+ name: 'getUserData',
39
+ type: 'function',
40
+ inferredDomain: 'user',
41
+ imports: ['react', 'axios'],
42
+ },
43
+ {
44
+ name: 'getProductData',
45
+ type: 'function',
46
+ inferredDomain: 'product',
47
+ imports: ['react', 'axios'],
48
+ },
49
+ ];
50
+ const exportsWithoutSharedImports = [
51
+ {
52
+ name: 'getUserData',
53
+ type: 'function',
54
+ inferredDomain: 'user',
55
+ imports: ['react', 'axios'],
56
+ },
57
+ {
58
+ name: 'getProductData',
59
+ type: 'function',
60
+ inferredDomain: 'product',
61
+ imports: ['lodash', 'moment'],
62
+ },
63
+ ];
64
+ const cohesionWithShared = calculateCohesion(exportsWithSharedImports);
65
+ const cohesionWithoutShared = calculateCohesion(exportsWithoutSharedImports);
66
+ // Shared imports should result in higher cohesion
67
+ expect(cohesionWithShared).toBeGreaterThan(cohesionWithoutShared);
68
+ });
69
+ it('should handle mixed case: some exports with imports, some without', () => {
70
+ const exports = [
71
+ {
72
+ name: 'getUserData',
73
+ type: 'function',
74
+ inferredDomain: 'user',
75
+ imports: ['react', 'axios'],
76
+ },
77
+ {
78
+ name: 'getProductData',
79
+ type: 'function',
80
+ inferredDomain: 'product',
81
+ // No imports field
82
+ },
83
+ ];
84
+ const cohesion = calculateCohesion(exports);
85
+ // Should fall back to domain-based when not all exports have import data
86
+ expect(cohesion).toBeGreaterThan(0);
87
+ expect(cohesion).toBeLessThan(1);
88
+ });
89
+ it('should return 1 for single export', () => {
90
+ const exports = [
91
+ {
92
+ name: 'getUserData',
93
+ type: 'function',
94
+ inferredDomain: 'user',
95
+ imports: ['react'],
96
+ },
97
+ ];
98
+ expect(calculateCohesion(exports)).toBe(1);
99
+ });
100
+ it('should return 1 for test files regardless of domains or imports', () => {
101
+ const exports = [
102
+ { name: 'testUserLogin', type: 'function', inferredDomain: 'user', imports: ['react'] },
103
+ { name: 'testProductView', type: 'function', inferredDomain: 'product', imports: [] },
104
+ ];
105
+ const cohesion = calculateCohesion(exports, 'src/utils/test-helpers.ts');
106
+ expect(cohesion).toBe(1);
107
+ });
108
+ });
109
+ //# sourceMappingURL=enhanced-cohesion.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enhanced-cohesion.test.js","sourceRoot":"","sources":["../../src/__tests__/enhanced-cohesion.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fragmentation-advanced.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragmentation-advanced.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fragmentation-advanced.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { calculatePathEntropy, calculateDirectoryDistance, } from '../analyzer';
3
+ describe('advanced fragmentation heuristics', () => {
4
+ it('path entropy is low when most files are in one directory', () => {
5
+ const files = [
6
+ 'src/billing/a.ts',
7
+ 'src/billing/b.ts',
8
+ 'src/billing/c.ts',
9
+ 'src/billing/d.ts',
10
+ 'src/billing/e.ts',
11
+ 'src/billing/f.ts',
12
+ 'src/billing/g.ts',
13
+ 'src/billing/h.ts',
14
+ 'src/billing/i.ts',
15
+ 'src/other/x.ts',
16
+ ];
17
+ const entropy = calculatePathEntropy(files);
18
+ // 9 in one folder and 1 in another should yield low entropy (< 0.5)
19
+ expect(entropy).toBeLessThan(0.5);
20
+ });
21
+ it('path entropy is high when files are evenly distributed', () => {
22
+ const files = [
23
+ 'a/f1.ts',
24
+ 'b/f2.ts',
25
+ 'c/f3.ts',
26
+ 'd/f4.ts',
27
+ 'e/f5.ts',
28
+ ];
29
+ const entropy = calculatePathEntropy(files);
30
+ expect(entropy).toBeGreaterThan(0.8);
31
+ });
32
+ it('directory distance is low for sibling files and high for scattered files', () => {
33
+ const siblings = [
34
+ 'src/auth/ui/button.ts',
35
+ 'src/auth/ui/input.ts',
36
+ 'src/auth/ui/modal.ts',
37
+ ];
38
+ const scattered = [
39
+ 'src/auth/ui/button.ts',
40
+ 'src/payments/api/charge.ts',
41
+ 'lib/analytics/track.ts',
42
+ ];
43
+ const distSiblings = calculateDirectoryDistance(siblings);
44
+ const distScattered = calculateDirectoryDistance(scattered);
45
+ expect(distSiblings).toBeLessThan(distScattered);
46
+ expect(distSiblings).toBeLessThan(0.5);
47
+ expect(distScattered).toBeGreaterThan(0.6);
48
+ });
49
+ });
50
+ //# sourceMappingURL=fragmentation-advanced.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragmentation-advanced.test.js","sourceRoot":"","sources":["../../src/__tests__/fragmentation-advanced.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AAErB,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,KAAK,GAAG;YACZ,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,gBAAgB;SACjB,CAAC;QAEF,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5C,oEAAoE;QACpE,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,KAAK,GAAG;YACZ,SAAS;YACT,SAAS;YACT,SAAS;YACT,SAAS;YACT,SAAS;SACV,CAAC;QAEF,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,QAAQ,GAAG;YACf,uBAAuB;YACvB,sBAAsB;YACtB,sBAAsB;SACvB,CAAC;QAEF,MAAM,SAAS,GAAG;YAChB,uBAAuB;YACvB,4BAA4B;YAC5B,wBAAwB;SACzB,CAAC;QAEF,MAAM,YAAY,GAAG,0BAA0B,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,aAAa,GAAG,0BAA0B,CAAC,SAAS,CAAC,CAAC;QAE5D,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QACjD,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,CAAC,aAAa,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fragmentation-coupling.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragmentation-coupling.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fragmentation-coupling.test.ts"],"names":[],"mappings":""}