@principal-ai/codebase-composition 0.1.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.
- package/README.md +67 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/DependencyLayerModule.d.ts +44 -0
- package/dist/modules/DependencyLayerModule.d.ts.map +1 -0
- package/dist/modules/DependencyLayerModule.js +286 -0
- package/dist/modules/DependencyLayerModule.js.map +1 -0
- package/dist/modules/FileSystemModule.d.ts +30 -0
- package/dist/modules/FileSystemModule.d.ts.map +1 -0
- package/dist/modules/FileSystemModule.js +46 -0
- package/dist/modules/FileSystemModule.js.map +1 -0
- package/dist/modules/FileTypeLayerModule.d.ts +34 -0
- package/dist/modules/FileTypeLayerModule.d.ts.map +1 -0
- package/dist/modules/FileTypeLayerModule.js +169 -0
- package/dist/modules/FileTypeLayerModule.js.map +1 -0
- package/dist/modules/FrameworkLayerModule.d.ts +22 -0
- package/dist/modules/FrameworkLayerModule.d.ts.map +1 -0
- package/dist/modules/FrameworkLayerModule.js +388 -0
- package/dist/modules/FrameworkLayerModule.js.map +1 -0
- package/dist/modules/PackageLayerModule.d.ts +23 -0
- package/dist/modules/PackageLayerModule.d.ts.map +1 -0
- package/dist/modules/PackageLayerModule.js +810 -0
- package/dist/modules/PackageLayerModule.js.map +1 -0
- package/dist/modules/TypeExtractionModule.d.ts +37 -0
- package/dist/modules/TypeExtractionModule.d.ts.map +1 -0
- package/dist/modules/TypeExtractionModule.js +180 -0
- package/dist/modules/TypeExtractionModule.js.map +1 -0
- package/dist/modules/VersionControlLayerModule.d.ts +10 -0
- package/dist/modules/VersionControlLayerModule.d.ts.map +1 -0
- package/dist/modules/VersionControlLayerModule.js +32 -0
- package/dist/modules/VersionControlLayerModule.js.map +1 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts +4 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts.map +1 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js +7 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js.map +1 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts +15 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts.map +1 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js +3 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js.map +1 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts +34 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts.map +1 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js +23 -0
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js.map +1 -0
- package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts +39 -0
- package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts.map +1 -0
- package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js +39 -0
- package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js.map +1 -0
- package/dist/modules/extractors/TypeScriptExtractor.d.ts +18 -0
- package/dist/modules/extractors/TypeScriptExtractor.d.ts.map +1 -0
- package/dist/modules/extractors/TypeScriptExtractor.js +361 -0
- package/dist/modules/extractors/TypeScriptExtractor.js.map +1 -0
- package/dist/modules/index.d.ts +13 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +21 -0
- package/dist/modules/index.js.map +1 -0
- package/dist/providers/GitVersionControlProvider.d.ts +108 -0
- package/dist/providers/GitVersionControlProvider.d.ts.map +1 -0
- package/dist/providers/GitVersionControlProvider.js +380 -0
- package/dist/providers/GitVersionControlProvider.js.map +1 -0
- package/dist/providers/PackageManagerApiProvider.d.ts +78 -0
- package/dist/providers/PackageManagerApiProvider.d.ts.map +1 -0
- package/dist/providers/PackageManagerApiProvider.js +14 -0
- package/dist/providers/PackageManagerApiProvider.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +10 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/services/FilesystemService.d.ts +59 -0
- package/dist/services/FilesystemService.d.ts.map +1 -0
- package/dist/services/FilesystemService.js +391 -0
- package/dist/services/FilesystemService.js.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +7 -0
- package/dist/services/index.js.map +1 -0
- package/dist/types/file-system.d.ts +7 -0
- package/dist/types/file-system.d.ts.map +1 -0
- package/dist/types/file-system.js +7 -0
- package/dist/types/file-system.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/layer-types.d.ts +187 -0
- package/dist/types/layer-types.d.ts.map +1 -0
- package/dist/types/layer-types.js +7 -0
- package/dist/types/layer-types.js.map +1 -0
- package/dist/types/version-control-layer.d.ts +53 -0
- package/dist/types/version-control-layer.d.ts.map +1 -0
- package/dist/types/version-control-layer.js +3 -0
- package/dist/types/version-control-layer.js.map +1 -0
- package/dist/types/workspace-boundaries.d.ts +17 -0
- package/dist/types/workspace-boundaries.d.ts.map +1 -0
- package/dist/types/workspace-boundaries.js +7 -0
- package/dist/types/workspace-boundaries.js.map +1 -0
- package/package.json +42 -0
- package/src/index.ts +62 -0
- package/src/modules/DependencyLayerModule.ts +329 -0
- package/src/modules/FileSystemModule.ts +65 -0
- package/src/modules/FileTypeLayerModule.ts +199 -0
- package/src/modules/FrameworkLayerModule.ts +437 -0
- package/src/modules/PackageLayerModule.ts +979 -0
- package/src/modules/TypeExtractionModule.test.ts +340 -0
- package/src/modules/TypeExtractionModule.ts +180 -0
- package/src/modules/VersionControlLayerModule.ts +31 -0
- package/src/modules/__fixtures__/typescript-packages/complex-types/package.json +6 -0
- package/src/modules/__fixtures__/typescript-packages/complex-types/src/index.ts +6 -0
- package/src/modules/__fixtures__/typescript-packages/complex-types/src/models/category.ts +15 -0
- package/src/modules/__fixtures__/typescript-packages/complex-types/src/models/product.ts +48 -0
- package/src/modules/__fixtures__/typescript-packages/javascript-only/index.js +18 -0
- package/src/modules/__fixtures__/typescript-packages/javascript-only/package.json +5 -0
- package/src/modules/__fixtures__/typescript-packages/simple-types/index.ts +53 -0
- package/src/modules/__fixtures__/typescript-packages/simple-types/package.json +6 -0
- package/src/modules/extractors/README.md +55 -0
- package/src/modules/extractors/TypeScriptExtractor.ts +409 -0
- package/src/modules/index.ts +13 -0
- package/src/providers/GitVersionControlProvider.ts +500 -0
- package/src/providers/PackageManagerApiProvider.ts +108 -0
- package/src/providers/README.md +88 -0
- package/src/providers/index.ts +17 -0
- package/src/services/FilesystemService.ts +530 -0
- package/src/services/index.ts +2 -0
- package/src/types/file-system.ts +11 -0
- package/src/types/index.ts +24 -0
- package/src/types/layer-types.ts +264 -0
- package/src/types/version-control-layer.ts +87 -0
- package/src/types/workspace-boundaries.ts +17 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Version control providers
|
|
2
|
+
export { GitVersionControlProvider } from './GitVersionControlProvider';
|
|
3
|
+
export type { FileSystemAdapter, GitAdapter, ShellAdapter } from './GitVersionControlProvider';
|
|
4
|
+
|
|
5
|
+
// Package management providers
|
|
6
|
+
export {
|
|
7
|
+
PackageManagerApiProvider,
|
|
8
|
+
type PackageManager,
|
|
9
|
+
type PackageVersionInfo,
|
|
10
|
+
type VersionCheckResult,
|
|
11
|
+
type VulnerabilityInfo,
|
|
12
|
+
type VulnerabilityCheckResult,
|
|
13
|
+
type LicenseInfo,
|
|
14
|
+
type LicenseCheckResult,
|
|
15
|
+
type DependencyCheckProgress,
|
|
16
|
+
type BatchCheckOptions,
|
|
17
|
+
} from './PackageManagerApiProvider';
|
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
import ignore from 'ignore';
|
|
2
|
+
|
|
3
|
+
import { FileTree, FileInfo, DirectoryInfo } from '@principal-ai/repository-abstraction';
|
|
4
|
+
import { FileSystemAdapter } from '../providers';
|
|
5
|
+
import { FileSystemFilterLayer } from '../types';
|
|
6
|
+
|
|
7
|
+
// Use path operations that work in browser
|
|
8
|
+
const path = {
|
|
9
|
+
basename: (p: string) => p.split('/').pop() || '',
|
|
10
|
+
join: (...parts: string[]) => {
|
|
11
|
+
const joined = parts.filter(p => p).join('/');
|
|
12
|
+
return joined.replace(/\/+/g, '/');
|
|
13
|
+
},
|
|
14
|
+
relative: (from: string, to: string) => {
|
|
15
|
+
if (!from || from === '.') {
|
|
16
|
+
return to;
|
|
17
|
+
}
|
|
18
|
+
if (to.startsWith(from + '/')) {
|
|
19
|
+
return to.slice(from.length + 1);
|
|
20
|
+
}
|
|
21
|
+
if (to === from) {
|
|
22
|
+
return '.';
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
},
|
|
26
|
+
dirname: (p: string) => {
|
|
27
|
+
const lastSlash = p.lastIndexOf('/');
|
|
28
|
+
return lastSlash > 0 ? p.slice(0, lastSlash) : '';
|
|
29
|
+
},
|
|
30
|
+
extname: (p: string) => {
|
|
31
|
+
const lastDot = p.lastIndexOf('.');
|
|
32
|
+
return lastDot > 0 ? p.slice(lastDot) : '';
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
interface FilterStats {
|
|
37
|
+
totalFiltered: number;
|
|
38
|
+
filteredByLayer: Record<string, number>;
|
|
39
|
+
examples: Array<{ path: string; filteredBy: string }>;
|
|
40
|
+
filteredByDirectory: Record<string, { count: number; reasons: Set<string> }>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class FilesystemService {
|
|
44
|
+
private filterStats: FilterStats = {
|
|
45
|
+
totalFiltered: 0,
|
|
46
|
+
filteredByLayer: {},
|
|
47
|
+
examples: [],
|
|
48
|
+
filteredByDirectory: {},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
constructor(private adapter?: FileSystemAdapter) {}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Build file tree using the provided adapter with efficient filtering
|
|
55
|
+
*/
|
|
56
|
+
async buildFileSystemTreeFromPath(
|
|
57
|
+
directoryPath: string,
|
|
58
|
+
filters?: FileSystemFilterLayer[],
|
|
59
|
+
options?: { enableInverseFiltering?: boolean } | object,
|
|
60
|
+
): Promise<
|
|
61
|
+
FileTree & {
|
|
62
|
+
filterStats?: FilterStats;
|
|
63
|
+
appliedFilters?: FileSystemFilterLayer[];
|
|
64
|
+
inverseMatches?: Array<{ path: string; matchedFilters: string[] }>;
|
|
65
|
+
}
|
|
66
|
+
> {
|
|
67
|
+
console.time('[FilesystemService] Total build time');
|
|
68
|
+
|
|
69
|
+
// Reset filter stats
|
|
70
|
+
this.filterStats = {
|
|
71
|
+
totalFiltered: 0,
|
|
72
|
+
filteredByLayer: {},
|
|
73
|
+
examples: [],
|
|
74
|
+
filteredByDirectory: {},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const allFilters = filters || [];
|
|
78
|
+
// Handle both old and new options format for backward compatibility
|
|
79
|
+
const enableInverseFiltering =
|
|
80
|
+
(options as { enableInverseFiltering?: boolean })?.enableInverseFiltering || false;
|
|
81
|
+
|
|
82
|
+
// Handle case when no adapter is provided
|
|
83
|
+
if (!this.adapter) {
|
|
84
|
+
// Return empty tree structure for backward compatibility
|
|
85
|
+
const rootDir = {
|
|
86
|
+
path: directoryPath,
|
|
87
|
+
name: directoryPath.split('/').pop() || directoryPath,
|
|
88
|
+
children: [],
|
|
89
|
+
fileCount: 0,
|
|
90
|
+
totalSize: 0,
|
|
91
|
+
depth: 0,
|
|
92
|
+
relativePath: '.',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
sha: '',
|
|
97
|
+
root: rootDir,
|
|
98
|
+
allFiles: [],
|
|
99
|
+
allDirectories: [rootDir],
|
|
100
|
+
stats: {
|
|
101
|
+
totalFiles: 0,
|
|
102
|
+
totalDirectories: 1,
|
|
103
|
+
totalSize: 0,
|
|
104
|
+
maxDepth: 0,
|
|
105
|
+
buildingTypeDistribution: {},
|
|
106
|
+
directoryTypeDistribution: {},
|
|
107
|
+
combinedTypeDistribution: {},
|
|
108
|
+
},
|
|
109
|
+
filterStats: this.filterStats,
|
|
110
|
+
appliedFilters: allFilters,
|
|
111
|
+
inverseMatches: enableInverseFiltering ? [] : undefined,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.time('[FilesystemService] Build file tree');
|
|
116
|
+
|
|
117
|
+
// Use the adapter's optimized implementation
|
|
118
|
+
const patterns: string[] = [];
|
|
119
|
+
let sourceDir: string | undefined;
|
|
120
|
+
|
|
121
|
+
// Convert filters to patterns for the adapter
|
|
122
|
+
for (const filter of allFilters) {
|
|
123
|
+
if (filter.enabled && filter.filterData?.excludedPatterns) {
|
|
124
|
+
patterns.push(...filter.filterData.excludedPatterns.map(p => p.pattern));
|
|
125
|
+
if (filter.filterData.sourceDirectory) {
|
|
126
|
+
sourceDir = filter.filterData.sourceDirectory;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const adapterResult = await this.adapter.buildFilteredFileTree(
|
|
132
|
+
directoryPath,
|
|
133
|
+
patterns,
|
|
134
|
+
sourceDir,
|
|
135
|
+
);
|
|
136
|
+
const crawledPaths = adapterResult.paths;
|
|
137
|
+
const fileStats = adapterResult.stats;
|
|
138
|
+
|
|
139
|
+
// Update filter stats
|
|
140
|
+
const ignoreInstances = this.createIgnoreInstances(allFilters);
|
|
141
|
+
for (const path of crawledPaths) {
|
|
142
|
+
if (!this.shouldIncludeFile(path, ignoreInstances, allFilters)) {
|
|
143
|
+
this.filterStats.totalFiltered++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.timeEnd('[FilesystemService] Build file tree');
|
|
148
|
+
console.log(`[FilesystemService] Found ${crawledPaths.length} paths after filtering`);
|
|
149
|
+
|
|
150
|
+
// Convert paths to FileInfo and DirectoryInfo objects
|
|
151
|
+
console.time('[FilesystemService] Convert to tree structure');
|
|
152
|
+
const { allFiles, allDirectories, rootDir } = await this.convertPathsToTree(
|
|
153
|
+
crawledPaths,
|
|
154
|
+
directoryPath,
|
|
155
|
+
fileStats,
|
|
156
|
+
);
|
|
157
|
+
console.timeEnd('[FilesystemService] Convert to tree structure');
|
|
158
|
+
|
|
159
|
+
// Handle inverse filtering for power users
|
|
160
|
+
let inverseMatches: Array<{ path: string; matchedFilters: string[] }> = [];
|
|
161
|
+
if (enableInverseFiltering) {
|
|
162
|
+
inverseMatches = await this.findInverseMatches(directoryPath, ignoreInstances, allFilters);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Calculate stats
|
|
166
|
+
const totalSize = allFiles.reduce((sum, file) => sum + (file.size || 0), 0);
|
|
167
|
+
const maxDepth = Math.max(...allDirectories.map(d => d.depth || 0));
|
|
168
|
+
|
|
169
|
+
// Update filter layer stats
|
|
170
|
+
allFilters.forEach(layer => {
|
|
171
|
+
if (layer.enabled && this.filterStats.filteredByLayer[layer.id]) {
|
|
172
|
+
if (!layer.filterData.stats) {
|
|
173
|
+
layer.filterData.stats = {
|
|
174
|
+
totalFiltered: 0,
|
|
175
|
+
bytesExcluded: 0,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
layer.filterData.stats.totalFiltered = this.filterStats.filteredByLayer[layer.id];
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const treeResult: FileTree & {
|
|
183
|
+
filterStats?: FilterStats;
|
|
184
|
+
appliedFilters?: FileSystemFilterLayer[];
|
|
185
|
+
inverseMatches?: Array<{ path: string; matchedFilters: string[] }>;
|
|
186
|
+
} = {
|
|
187
|
+
sha: '',
|
|
188
|
+
root: rootDir || {
|
|
189
|
+
path: '.',
|
|
190
|
+
name: path.basename(directoryPath) || 'root',
|
|
191
|
+
children: [],
|
|
192
|
+
fileCount: 0,
|
|
193
|
+
totalSize: 0,
|
|
194
|
+
depth: 0,
|
|
195
|
+
relativePath: '.',
|
|
196
|
+
},
|
|
197
|
+
allFiles,
|
|
198
|
+
allDirectories,
|
|
199
|
+
stats: {
|
|
200
|
+
totalFiles: allFiles.length,
|
|
201
|
+
totalDirectories: allDirectories.length,
|
|
202
|
+
totalSize,
|
|
203
|
+
maxDepth,
|
|
204
|
+
buildingTypeDistribution: {},
|
|
205
|
+
directoryTypeDistribution: {},
|
|
206
|
+
combinedTypeDistribution: {},
|
|
207
|
+
},
|
|
208
|
+
filterStats: this.filterStats,
|
|
209
|
+
appliedFilters: allFilters,
|
|
210
|
+
inverseMatches: inverseMatches,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
console.timeEnd('[FilesystemService] Total build time');
|
|
214
|
+
return treeResult;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Create optimized ignore instances from filter layers
|
|
219
|
+
*/
|
|
220
|
+
private createIgnoreInstances(filters: FileSystemFilterLayer[]): Map<
|
|
221
|
+
string,
|
|
222
|
+
{
|
|
223
|
+
ignore: ReturnType<typeof ignore>;
|
|
224
|
+
sourceDirectory: string;
|
|
225
|
+
filterId: string;
|
|
226
|
+
filterName: string;
|
|
227
|
+
}
|
|
228
|
+
> {
|
|
229
|
+
const ignoreInstances = new Map<
|
|
230
|
+
string,
|
|
231
|
+
{
|
|
232
|
+
ignore: ReturnType<typeof ignore>;
|
|
233
|
+
sourceDirectory: string;
|
|
234
|
+
filterId: string;
|
|
235
|
+
filterName: string;
|
|
236
|
+
}
|
|
237
|
+
>();
|
|
238
|
+
|
|
239
|
+
for (const filter of filters) {
|
|
240
|
+
if (!filter.enabled || !filter.filterData?.excludedPatterns) continue;
|
|
241
|
+
|
|
242
|
+
const sourceDirectory = filter.filterData.sourceDirectory || '';
|
|
243
|
+
const key = `${filter.id}:${sourceDirectory}`;
|
|
244
|
+
|
|
245
|
+
const ig = ignore();
|
|
246
|
+
const patterns = filter.filterData.excludedPatterns.map(p => p.pattern);
|
|
247
|
+
ig.add(patterns);
|
|
248
|
+
|
|
249
|
+
ignoreInstances.set(key, {
|
|
250
|
+
ignore: ig,
|
|
251
|
+
sourceDirectory,
|
|
252
|
+
filterId: filter.id,
|
|
253
|
+
filterName: filter.name,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return ignoreInstances;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Fast filtering using node-ignore during fdir crawl
|
|
262
|
+
*/
|
|
263
|
+
private shouldIncludeFile(
|
|
264
|
+
filePath: string,
|
|
265
|
+
ignoreInstances: Map<
|
|
266
|
+
string,
|
|
267
|
+
{
|
|
268
|
+
ignore: ReturnType<typeof ignore>;
|
|
269
|
+
sourceDirectory: string;
|
|
270
|
+
filterId: string;
|
|
271
|
+
filterName: string;
|
|
272
|
+
}
|
|
273
|
+
>,
|
|
274
|
+
_filters: FileSystemFilterLayer[],
|
|
275
|
+
): boolean {
|
|
276
|
+
for (const [, { ignore: ig, sourceDirectory, filterId, filterName }] of ignoreInstances) {
|
|
277
|
+
// Check if this path is within the filter's scope
|
|
278
|
+
if (sourceDirectory !== '') {
|
|
279
|
+
if (!filePath.startsWith(sourceDirectory + '/') && filePath !== sourceDirectory) {
|
|
280
|
+
continue; // Skip this filter - path is outside its scope
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Get the path relative to the filter's source directory for testing
|
|
285
|
+
let pathToTest = filePath;
|
|
286
|
+
if (sourceDirectory !== '' && filePath.startsWith(sourceDirectory + '/')) {
|
|
287
|
+
pathToTest = filePath.slice(sourceDirectory.length + 1);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Skip empty paths and "." which node-ignore doesn't handle
|
|
291
|
+
if (!pathToTest || pathToTest === '.') {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Use node-ignore for fast pattern matching
|
|
296
|
+
if (ig.ignores(pathToTest)) {
|
|
297
|
+
// Track statistics
|
|
298
|
+
this.filterStats.totalFiltered++;
|
|
299
|
+
this.filterStats.filteredByLayer[filterId] =
|
|
300
|
+
(this.filterStats.filteredByLayer[filterId] || 0) + 1;
|
|
301
|
+
|
|
302
|
+
// Track by directory
|
|
303
|
+
const dirPath = path.dirname(filePath) || '.';
|
|
304
|
+
if (!this.filterStats.filteredByDirectory[dirPath]) {
|
|
305
|
+
this.filterStats.filteredByDirectory[dirPath] = { count: 0, reasons: new Set() };
|
|
306
|
+
}
|
|
307
|
+
this.filterStats.filteredByDirectory[dirPath].count++;
|
|
308
|
+
this.filterStats.filteredByDirectory[dirPath].reasons.add(filterName);
|
|
309
|
+
|
|
310
|
+
return false; // Exclude this file
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return true; // Include this file
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Convert array of paths to FileTree structure
|
|
319
|
+
*/
|
|
320
|
+
private async convertPathsToTree(
|
|
321
|
+
paths: string[],
|
|
322
|
+
rootPath: string,
|
|
323
|
+
providedStats?: Map<string, { size: number; isDirectory: boolean; lastModified: Date }>,
|
|
324
|
+
): Promise<{
|
|
325
|
+
allFiles: FileInfo[];
|
|
326
|
+
allDirectories: DirectoryInfo[];
|
|
327
|
+
rootDir: DirectoryInfo | null;
|
|
328
|
+
}> {
|
|
329
|
+
const allFiles: FileInfo[] = [];
|
|
330
|
+
const allDirectories: DirectoryInfo[] = [];
|
|
331
|
+
const directoryMap = new Map<string, DirectoryInfo>();
|
|
332
|
+
|
|
333
|
+
// Use provided stats or fetch them if adapter supports it
|
|
334
|
+
const fileStats =
|
|
335
|
+
providedStats ||
|
|
336
|
+
new Map<string, { size: number; lastModified: Date; isDirectory: boolean }>();
|
|
337
|
+
|
|
338
|
+
if (!providedStats && this.adapter && 'getFileStats' in this.adapter) {
|
|
339
|
+
// Batch file stats operations for better performance
|
|
340
|
+
const statPromises = paths.map(async filePath => {
|
|
341
|
+
try {
|
|
342
|
+
const fullPath = path.join(rootPath, filePath);
|
|
343
|
+
const stats = await (
|
|
344
|
+
this.adapter as FileSystemAdapter & {
|
|
345
|
+
getFileStats: (
|
|
346
|
+
path: string,
|
|
347
|
+
) => Promise<{ size?: number; lastModified?: Date; isDirectory?: boolean }>;
|
|
348
|
+
}
|
|
349
|
+
).getFileStats(fullPath);
|
|
350
|
+
if (stats) {
|
|
351
|
+
fileStats.set(filePath, {
|
|
352
|
+
size: stats.size || 0,
|
|
353
|
+
lastModified: stats.lastModified || new Date(),
|
|
354
|
+
isDirectory: stats.isDirectory || false,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
} catch {
|
|
358
|
+
// Ignore stat errors, will use defaults
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
await Promise.all(statPromises);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Create file and directory objects
|
|
366
|
+
for (const filePath of paths) {
|
|
367
|
+
const stats = fileStats.get(filePath);
|
|
368
|
+
const isDirectory = stats?.isDirectory || filePath.endsWith('/');
|
|
369
|
+
|
|
370
|
+
if (isDirectory) {
|
|
371
|
+
const dirInfo: DirectoryInfo = {
|
|
372
|
+
path: filePath || '.',
|
|
373
|
+
name: path.basename(filePath) || 'root',
|
|
374
|
+
children: [],
|
|
375
|
+
fileCount: 0,
|
|
376
|
+
totalSize: 0,
|
|
377
|
+
depth: filePath.split('/').length - 1,
|
|
378
|
+
relativePath: filePath || '.',
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
allDirectories.push(dirInfo);
|
|
382
|
+
directoryMap.set(filePath, dirInfo);
|
|
383
|
+
} else {
|
|
384
|
+
const fileInfo: FileInfo = {
|
|
385
|
+
path: filePath,
|
|
386
|
+
name: path.basename(filePath),
|
|
387
|
+
extension: path.extname(filePath).toLowerCase(),
|
|
388
|
+
size: stats?.size || 0,
|
|
389
|
+
lastModified: stats?.lastModified || new Date(),
|
|
390
|
+
isDirectory: false,
|
|
391
|
+
relativePath: filePath,
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
allFiles.push(fileInfo);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Build hierarchical structure (simplified - just return root)
|
|
399
|
+
const rootDir = directoryMap.get('.') ||
|
|
400
|
+
directoryMap.get('') || {
|
|
401
|
+
path: '.',
|
|
402
|
+
name: path.basename(rootPath) || 'root',
|
|
403
|
+
children: [],
|
|
404
|
+
fileCount: allFiles.length,
|
|
405
|
+
totalSize: allFiles.reduce((sum, f) => sum + (f.size || 0), 0),
|
|
406
|
+
depth: 0,
|
|
407
|
+
relativePath: '.',
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
return { allFiles, allDirectories, rootDir };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Find files that would match specific filters - for power user investigation
|
|
415
|
+
*/
|
|
416
|
+
private async findInverseMatches(
|
|
417
|
+
projectPath: string,
|
|
418
|
+
ignoreInstances: Map<
|
|
419
|
+
string,
|
|
420
|
+
{
|
|
421
|
+
ignore: ReturnType<typeof ignore>;
|
|
422
|
+
sourceDirectory: string;
|
|
423
|
+
filterId: string;
|
|
424
|
+
filterName: string;
|
|
425
|
+
}
|
|
426
|
+
>,
|
|
427
|
+
_filters: FileSystemFilterLayer[],
|
|
428
|
+
): Promise<Array<{ path: string; matchedFilters: string[] }>> {
|
|
429
|
+
console.log('[FilesystemService] Running inverse filter analysis...');
|
|
430
|
+
|
|
431
|
+
const inverseMatches: Array<{ path: string; matchedFilters: string[] }> = [];
|
|
432
|
+
|
|
433
|
+
// Get ALL files without any filtering to get the complete picture
|
|
434
|
+
if (!this.adapter) {
|
|
435
|
+
return [];
|
|
436
|
+
}
|
|
437
|
+
const unfilteredResult = await this.adapter.buildFilteredFileTree(projectPath);
|
|
438
|
+
const allPaths = unfilteredResult.paths;
|
|
439
|
+
|
|
440
|
+
console.log(`[FilesystemService] Found ${allPaths.length} total paths for inverse analysis`);
|
|
441
|
+
|
|
442
|
+
// Test each path against each filter to see what matches
|
|
443
|
+
for (const filePath of allPaths) {
|
|
444
|
+
const matchedFilters: string[] = [];
|
|
445
|
+
|
|
446
|
+
for (const [, { ignore: ig, sourceDirectory, filterName }] of ignoreInstances) {
|
|
447
|
+
// Check if this path is within the filter's scope
|
|
448
|
+
if (sourceDirectory !== '') {
|
|
449
|
+
if (!filePath.startsWith(sourceDirectory + '/') && filePath !== sourceDirectory) {
|
|
450
|
+
continue; // Skip this filter - path is outside its scope
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Get the path relative to the filter's source directory for testing
|
|
455
|
+
let pathToTest = filePath;
|
|
456
|
+
if (sourceDirectory !== '' && filePath.startsWith(sourceDirectory + '/')) {
|
|
457
|
+
pathToTest = filePath.slice(sourceDirectory.length + 1);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Skip empty paths and "." which node-ignore doesn't handle
|
|
461
|
+
if (!pathToTest || pathToTest === '.') {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Check if this filter would match this path
|
|
466
|
+
if (ig.ignores(pathToTest)) {
|
|
467
|
+
matchedFilters.push(filterName);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// If any filters matched, record it
|
|
472
|
+
if (matchedFilters.length > 0) {
|
|
473
|
+
inverseMatches.push({
|
|
474
|
+
path: filePath,
|
|
475
|
+
matchedFilters,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
console.log(
|
|
481
|
+
`[FilesystemService] Inverse analysis complete: ${inverseMatches.length} files matched filters`,
|
|
482
|
+
);
|
|
483
|
+
return inverseMatches;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Get the filter statistics from the last build
|
|
488
|
+
*/
|
|
489
|
+
getFilterStats(): FilterStats {
|
|
490
|
+
return { ...this.filterStats };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Create an empty tree structure - matches original FilesystemService API
|
|
495
|
+
*/
|
|
496
|
+
private getEmptyTree(directoryPath: string): FileTree & {
|
|
497
|
+
filterStats?: FilterStats;
|
|
498
|
+
appliedFilters?: FileSystemFilterLayer[];
|
|
499
|
+
inverseMatches?: Array<{ path: string; matchedFilters: string[] }>;
|
|
500
|
+
} {
|
|
501
|
+
const rootDir: DirectoryInfo = {
|
|
502
|
+
path: '.',
|
|
503
|
+
name: path.basename(directoryPath) || 'root',
|
|
504
|
+
children: [],
|
|
505
|
+
fileCount: 0,
|
|
506
|
+
totalSize: 0,
|
|
507
|
+
depth: 0,
|
|
508
|
+
relativePath: '.',
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
sha: '',
|
|
513
|
+
root: rootDir,
|
|
514
|
+
allFiles: [],
|
|
515
|
+
allDirectories: [rootDir],
|
|
516
|
+
stats: {
|
|
517
|
+
totalFiles: 0,
|
|
518
|
+
totalDirectories: 1,
|
|
519
|
+
totalSize: 0,
|
|
520
|
+
maxDepth: 0,
|
|
521
|
+
buildingTypeDistribution: {},
|
|
522
|
+
directoryTypeDistribution: {},
|
|
523
|
+
combinedTypeDistribution: {},
|
|
524
|
+
},
|
|
525
|
+
filterStats: this.filterStats,
|
|
526
|
+
appliedFilters: [],
|
|
527
|
+
inverseMatches: [],
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File system types for layer modules
|
|
3
|
+
* Re-exported from core/file-tree to maintain consistency
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Re-export from the central file-tree module
|
|
7
|
+
export type { FileInfo, DirectoryInfo, FileTree } from '@principal-ai/repository-abstraction';
|
|
8
|
+
|
|
9
|
+
// Alias FileTree as FileSystemTree for backward compatibility
|
|
10
|
+
// This can be removed in a future version
|
|
11
|
+
export type { FileTree as FileSystemTree } from '@principal-ai/repository-abstraction';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Export layer types
|
|
2
|
+
export type {
|
|
3
|
+
FilePattern,
|
|
4
|
+
FileSet,
|
|
5
|
+
BaseLayer,
|
|
6
|
+
PackageCommand,
|
|
7
|
+
ConfigFile,
|
|
8
|
+
PackageLayer,
|
|
9
|
+
FileSystemFilterLayer,
|
|
10
|
+
ExtractedType,
|
|
11
|
+
PackageTypes,
|
|
12
|
+
TypeDefinitionLayer,
|
|
13
|
+
TypeExtractor,
|
|
14
|
+
} from './layer-types';
|
|
15
|
+
|
|
16
|
+
// Export version control types
|
|
17
|
+
export type {
|
|
18
|
+
VersionControlLayer,
|
|
19
|
+
VersionControlIgnorePattern,
|
|
20
|
+
VersionControlProvider,
|
|
21
|
+
} from './version-control-layer';
|
|
22
|
+
|
|
23
|
+
// Export file system types
|
|
24
|
+
export type { FileInfo, DirectoryInfo, FileTree, FileSystemTree } from './file-system';
|