arcvision 0.2.12 โ 0.2.15
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/ARCVISION_DIRECTORY_STRUCTURE.md +104 -0
- package/CLI_STRUCTURE.md +110 -0
- package/CONFIGURATION.md +119 -0
- package/IMPLEMENTATION_SUMMARY.md +99 -0
- package/README.md +149 -89
- package/architecture.authority.ledger.json +46 -0
- package/arcvision-0.2.3.tgz +0 -0
- package/arcvision-0.2.4.tgz +0 -0
- package/arcvision-0.2.5.tgz +0 -0
- package/arcvision.context.diff.json +2181 -0
- package/arcvision.context.json +1021 -0
- package/arcvision.context.v1.json +2163 -0
- package/arcvision.context.v2.json +2173 -0
- package/arcvision_context/README.md +93 -0
- package/arcvision_context/architecture.authority.ledger.json +83 -0
- package/arcvision_context/arcvision.context.json +6884 -0
- package/debug-cycle-detection.js +56 -0
- package/dist/index.js +1626 -25
- package/docs/ENHANCED_ACCURACY_SAFETY_PROTOCOL.md +172 -0
- package/docs/accuracy-enhancement-artifacts/enhanced-validation-config.json +98 -0
- package/docs/acig-robustness-guide.md +164 -0
- package/docs/authoritative-gate-implementation.md +168 -0
- package/docs/cli-strengthening-summary.md +232 -0
- package/docs/invariant-system-summary.md +100 -0
- package/docs/invariant-system.md +112 -0
- package/generate_large_test.js +42 -0
- package/large_test_repo.json +1 -0
- package/output1.json +2163 -0
- package/output2.json +2163 -0
- package/package.json +46 -36
- package/scan_calcom_report.txt +0 -0
- package/scan_leafmint_report.txt +0 -0
- package/scan_output.txt +0 -0
- package/scan_trigger_report.txt +0 -0
- package/schema/arcvision_context_schema_v1.json +136 -1
- package/src/arcvision-guard.js +433 -0
- package/src/core/authority-core-detector.js +382 -0
- package/src/core/authority-ledger.js +300 -0
- package/src/core/blastRadius.js +299 -0
- package/src/core/call-resolver.js +196 -0
- package/src/core/change-evaluator.js +509 -0
- package/src/core/change-evaluator.js.backup +424 -0
- package/src/core/change-evaluator.ts +285 -0
- package/src/core/chunked-uploader.js +180 -0
- package/src/core/circular-dependency-detector.js +404 -0
- package/src/core/cli-error-handler.js +458 -0
- package/src/core/cli-validator.js +458 -0
- package/src/core/compression.js +64 -0
- package/src/core/context_builder.js +741 -0
- package/src/core/dependency-manager.js +134 -0
- package/src/core/di-detector.js +202 -0
- package/src/core/diff-analyzer.js +76 -0
- package/src/core/example-invariants.js +135 -0
- package/src/core/failure-mode-synthesizer.js +341 -0
- package/src/core/invariant-analyzer.js +294 -0
- package/src/core/invariant-detector.js +548 -0
- package/src/core/invariant-enforcer.js +171 -0
- package/src/core/invariant-evaluation-utils.js +172 -0
- package/src/core/invariant-hooks.js +152 -0
- package/src/core/invariant-integration-example.js +186 -0
- package/src/core/invariant-registry.js +298 -0
- package/src/core/invariant-registry.ts +100 -0
- package/src/core/invariant-types.js +66 -0
- package/src/core/invariants-index.js +88 -0
- package/src/core/method-tracker.js +170 -0
- package/src/core/override-handler.js +304 -0
- package/src/core/ownership-resolver.js +227 -0
- package/src/core/parser-enhanced.js +80 -0
- package/src/core/parser.js +610 -0
- package/src/core/path-resolver.js +240 -0
- package/src/core/pattern-matcher.js +246 -0
- package/src/core/progress-tracker.js +71 -0
- package/src/core/react-nextjs-detector.js +245 -0
- package/src/core/readme-generator.js +167 -0
- package/src/core/retry-handler.js +57 -0
- package/src/core/scanner.js +289 -0
- package/src/core/semantic-analyzer.js +204 -0
- package/src/core/structural-context-owner.js +442 -0
- package/src/core/symbol-indexer.js +164 -0
- package/src/core/tsconfig-utils.js +73 -0
- package/src/core/type-analyzer.js +272 -0
- package/src/core/watcher.js +18 -0
- package/src/core/workspace-scanner.js +88 -0
- package/src/engine/context_builder.js +280 -0
- package/src/engine/context_sorter.js +59 -0
- package/src/engine/context_validator.js +200 -0
- package/src/engine/id-generator.js +16 -0
- package/src/engine/pass1_facts.js +260 -0
- package/src/engine/pass2_semantics.js +333 -0
- package/src/engine/pass3_lifter.js +99 -0
- package/src/engine/pass4_signals.js +201 -0
- package/src/index.js +830 -0
- package/src/plugins/express-plugin.js +48 -0
- package/src/plugins/plugin-manager.js +58 -0
- package/src/plugins/react-plugin.js +54 -0
- package/temp_original.js +0 -0
- package/test/determinism-test.js +83 -0
- package/test-authoritative-context.js +53 -0
- package/test-real-authoritative-context.js +118 -0
- package/test-upload-enhancements.js +111 -0
- package/test_repos/allowed-clean-architecture/.arcvision/invariants.json +57 -0
- package/test_repos/allowed-clean-architecture/adapters/controllers/UserController.js +95 -0
- package/test_repos/allowed-clean-architecture/adapters/http/HttpServer.js +78 -0
- package/test_repos/allowed-clean-architecture/application/dtos/CreateUserRequest.js +37 -0
- package/test_repos/allowed-clean-architecture/application/services/UserService.js +61 -0
- package/test_repos/allowed-clean-architecture/arcvision_context/README.md +93 -0
- package/test_repos/allowed-clean-architecture/arcvision_context/arcvision.context.json +2796 -0
- package/test_repos/allowed-clean-architecture/domain/interfaces/UserRepository.js +25 -0
- package/test_repos/allowed-clean-architecture/domain/models/User.js +39 -0
- package/test_repos/allowed-clean-architecture/index.js +45 -0
- package/test_repos/allowed-clean-architecture/infrastructure/database/DatabaseConnection.js +56 -0
- package/test_repos/allowed-clean-architecture/infrastructure/repositories/InMemoryUserRepository.js +61 -0
- package/test_repos/allowed-clean-architecture/package.json +15 -0
- package/test_repos/blocked-legacy-monolith/.arcvision/invariants.json +78 -0
- package/test_repos/blocked-legacy-monolith/arcvision_context/README.md +93 -0
- package/test_repos/blocked-legacy-monolith/arcvision_context/arcvision.context.json +2882 -0
- package/test_repos/blocked-legacy-monolith/database/dbConnection.js +35 -0
- package/test_repos/blocked-legacy-monolith/index.js +38 -0
- package/test_repos/blocked-legacy-monolith/modules/emailService.js +31 -0
- package/test_repos/blocked-legacy-monolith/modules/paymentProcessor.js +37 -0
- package/test_repos/blocked-legacy-monolith/package.json +15 -0
- package/test_repos/blocked-legacy-monolith/shared/utils.js +19 -0
- package/test_repos/blocked-legacy-monolith/utils/helpers.js +23 -0
- package/test_repos/risky-microservices-concerns/.arcvision/invariants.json +69 -0
- package/test_repos/risky-microservices-concerns/arcvision_context/README.md +93 -0
- package/test_repos/risky-microservices-concerns/arcvision_context/arcvision.context.json +3070 -0
- package/test_repos/risky-microservices-concerns/common/utils.js +77 -0
- package/test_repos/risky-microservices-concerns/gateways/apiGateway.js +84 -0
- package/test_repos/risky-microservices-concerns/index.js +20 -0
- package/test_repos/risky-microservices-concerns/libs/deprecatedHelper.js +36 -0
- package/test_repos/risky-microservices-concerns/package.json +15 -0
- package/test_repos/risky-microservices-concerns/services/orderService.js +42 -0
- package/test_repos/risky-microservices-concerns/services/userService.js +48 -0
- package/verify_engine.js +116 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve an import path to its actual file location
|
|
6
|
+
* Handles relative paths, path aliases, workspace packages, and node_modules
|
|
7
|
+
* @param {string} importPath - The import path from the source code
|
|
8
|
+
* @param {string} importerPath - The path of the file doing the import
|
|
9
|
+
* @param {string} projectRoot - The root directory of the project
|
|
10
|
+
* @param {Object|null} tsconfig - Loaded tsconfig with compilerOptions
|
|
11
|
+
* @param {Map<string, string>|null} workspaceMap - Map of package names to their roots
|
|
12
|
+
* @param {Set<string>|null} fileSet - Cached set of all absolute file paths
|
|
13
|
+
* @returns {string|null} The resolved absolute file path or null if not found
|
|
14
|
+
*/
|
|
15
|
+
function resolveImport(importPath, importerPath, projectRoot, tsconfig, workspaceMap, fileSet) {
|
|
16
|
+
// If it's an absolute path or URL, skip resolution
|
|
17
|
+
if (importPath.startsWith('http://') || importPath.startsWith('https://') ||
|
|
18
|
+
importPath.startsWith('data:') || importPath.startsWith('file:')) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const exists = (p) => {
|
|
23
|
+
if (fileSet) return fileSet.has(p);
|
|
24
|
+
return fs.existsSync(p);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Handle relative imports
|
|
28
|
+
if (importPath.startsWith('.')) {
|
|
29
|
+
// Resolve relative to importer
|
|
30
|
+
let resolvedPath = path.resolve(path.dirname(importerPath), importPath);
|
|
31
|
+
|
|
32
|
+
// Try with various extensions in order of preference
|
|
33
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json'];
|
|
34
|
+
for (const ext of extensions) {
|
|
35
|
+
if (exists(resolvedPath + ext)) {
|
|
36
|
+
return resolvedPath + ext;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Try as directory with index files
|
|
41
|
+
const indexFiles = ['index.ts', 'index.tsx', 'index.js', 'index.jsx', 'index.json'];
|
|
42
|
+
for (const idxFile of indexFiles) {
|
|
43
|
+
const indexPath = path.join(resolvedPath, idxFile);
|
|
44
|
+
if (exists(indexPath)) {
|
|
45
|
+
return indexPath;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// If original path exists as-is
|
|
50
|
+
if (exists(resolvedPath)) {
|
|
51
|
+
return resolvedPath;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle path aliases (like @/components/, ~/utils/, etc.)
|
|
58
|
+
if (tsconfig && tsconfig.paths) {
|
|
59
|
+
const paths = tsconfig.paths;
|
|
60
|
+
|
|
61
|
+
// Sort aliases by length (descending) to match longer patterns first
|
|
62
|
+
const sortedAliases = Object.keys(paths).sort((a, b) => b.length - a.length);
|
|
63
|
+
|
|
64
|
+
for (const aliasPattern of sortedAliases) {
|
|
65
|
+
const aliasTargets = paths[aliasPattern];
|
|
66
|
+
|
|
67
|
+
// Handle pattern like "@/*" -> ["./src/*"]
|
|
68
|
+
if (aliasPattern.includes('*')) {
|
|
69
|
+
const patternBase = aliasPattern.replace(/\*.*$/, '');
|
|
70
|
+
|
|
71
|
+
// Match if it's the exact base (e.g. "@/utils" matches "@/utils/*")
|
|
72
|
+
// OR if it starts with the base (e.g. "@/utils/foo" matches "@/utils/*")
|
|
73
|
+
if (importPath === patternBase.slice(0, -1) || importPath.startsWith(patternBase)) {
|
|
74
|
+
const suffix = importPath.length >= patternBase.length ?
|
|
75
|
+
importPath.substring(patternBase.length) : "";
|
|
76
|
+
|
|
77
|
+
for (const target of aliasTargets) {
|
|
78
|
+
const targetBase = target.replace(/\*.*$/, '');
|
|
79
|
+
const targetPath = path.resolve(projectRoot, path.join(targetBase, suffix));
|
|
80
|
+
|
|
81
|
+
// Try with extensions
|
|
82
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json'];
|
|
83
|
+
for (const ext of extensions) {
|
|
84
|
+
if (exists(targetPath + ext)) {
|
|
85
|
+
return targetPath + ext;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Try as directory with index files
|
|
90
|
+
const indexFiles = ['index.ts', 'index.tsx', 'index.js', 'index.jsx', 'index.json'];
|
|
91
|
+
for (const idxFile of indexFiles) {
|
|
92
|
+
const indexPath = path.join(targetPath, idxFile);
|
|
93
|
+
if (exists(indexPath)) {
|
|
94
|
+
return indexPath;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Try exact path
|
|
99
|
+
if (exists(targetPath)) {
|
|
100
|
+
return targetPath;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// Handle exact match patterns
|
|
106
|
+
if (importPath === aliasPattern || importPath.startsWith(aliasPattern + '/')) {
|
|
107
|
+
for (const target of aliasTargets) {
|
|
108
|
+
let targetPath = path.resolve(projectRoot, target);
|
|
109
|
+
|
|
110
|
+
// If importPath is longer than the alias pattern, add the remaining part
|
|
111
|
+
if (importPath.length > aliasPattern.length) {
|
|
112
|
+
const remainingPath = importPath.substring(aliasPattern.length);
|
|
113
|
+
targetPath = path.join(targetPath, remainingPath);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Try with extensions
|
|
117
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json'];
|
|
118
|
+
for (const ext of extensions) {
|
|
119
|
+
if (exists(targetPath + ext)) {
|
|
120
|
+
return targetPath + ext;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Try as directory with index files
|
|
125
|
+
const indexFiles = ['index.ts', 'index.tsx', 'index.js', 'index.jsx', 'index.json'];
|
|
126
|
+
for (const idxFile of indexFiles) {
|
|
127
|
+
const indexPath = path.join(targetPath, idxFile);
|
|
128
|
+
if (exists(indexPath)) {
|
|
129
|
+
return indexPath;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Try exact path
|
|
134
|
+
if (exists(targetPath)) {
|
|
135
|
+
return targetPath;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Handle baseUrl if configured
|
|
144
|
+
if (tsconfig && tsconfig.baseUrl && !importPath.startsWith('.')) {
|
|
145
|
+
let resolvedPath = path.resolve(projectRoot, tsconfig.baseUrl, importPath);
|
|
146
|
+
|
|
147
|
+
// Try with extensions
|
|
148
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json'];
|
|
149
|
+
for (const ext of extensions) {
|
|
150
|
+
if (exists(resolvedPath + ext)) {
|
|
151
|
+
return resolvedPath + ext;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Try as directory with index files
|
|
156
|
+
const indexFiles = ['index.ts', 'index.tsx', 'index.js', 'index.jsx', 'index.json'];
|
|
157
|
+
for (const idxFile of indexFiles) {
|
|
158
|
+
const indexPath = path.join(resolvedPath, idxFile);
|
|
159
|
+
if (exists(indexPath)) {
|
|
160
|
+
return indexPath;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Try exact path
|
|
165
|
+
if (exists(resolvedPath)) {
|
|
166
|
+
return resolvedPath;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Handle Workspace Packages (Monorepo support)
|
|
171
|
+
if (workspaceMap && workspaceMap.has(importPath)) {
|
|
172
|
+
const packageRoot = workspaceMap.get(importPath);
|
|
173
|
+
// Look for entry point in package.json
|
|
174
|
+
try {
|
|
175
|
+
const pkgPath = path.join(packageRoot, 'package.json');
|
|
176
|
+
if (exists(pkgPath)) {
|
|
177
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
178
|
+
const mainFile = pkg.main || pkg.module || 'index.js';
|
|
179
|
+
const searchPath = path.join(packageRoot, mainFile);
|
|
180
|
+
|
|
181
|
+
// Use standard node resolution logic on the main file
|
|
182
|
+
const extensions = ['', '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json'];
|
|
183
|
+
for (const ext of extensions) {
|
|
184
|
+
const p = searchPath + ext;
|
|
185
|
+
if (exists(p)) return p;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// If no package.json or main, try index
|
|
189
|
+
const indexFiles = ['index.ts', 'index.tsx', 'index.js', 'index.jsx'];
|
|
190
|
+
for (const idx of indexFiles) {
|
|
191
|
+
const p = path.join(packageRoot, idx);
|
|
192
|
+
if (exists(p)) return p;
|
|
193
|
+
}
|
|
194
|
+
} catch (e) {
|
|
195
|
+
// ignore
|
|
196
|
+
}
|
|
197
|
+
// Fallback to pure directory path if nothing else
|
|
198
|
+
return packageRoot;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Handle Workspace Sub-paths (e.g. "my-pkg/utils")
|
|
202
|
+
if (workspaceMap) {
|
|
203
|
+
// Find longest matching package name
|
|
204
|
+
// This is O(N) but N is number of packages, usually small.
|
|
205
|
+
for (const [pkgName, pkgRoot] of workspaceMap.entries()) {
|
|
206
|
+
if (importPath.startsWith(pkgName + '/')) {
|
|
207
|
+
const suffix = importPath.substring(pkgName.length + 1);
|
|
208
|
+
const targetPath = path.join(pkgRoot, suffix);
|
|
209
|
+
|
|
210
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', ''];
|
|
211
|
+
for (const ext of extensions) {
|
|
212
|
+
if (exists(targetPath + ext)) {
|
|
213
|
+
return targetPath + ext;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Try as directory
|
|
217
|
+
const indexFiles = ['index.ts', 'index.tsx', 'index.js', 'index.jsx'];
|
|
218
|
+
for (const idx of indexFiles) {
|
|
219
|
+
const p = path.join(targetPath, idx);
|
|
220
|
+
if (exists(p)) return p;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Handle node_modules resolution
|
|
227
|
+
if (!importPath.startsWith('.')) {
|
|
228
|
+
// Check if it's a node module
|
|
229
|
+
try {
|
|
230
|
+
const resolved = require.resolve(importPath, { paths: [path.dirname(importerPath)] });
|
|
231
|
+
return resolved;
|
|
232
|
+
} catch (e) {
|
|
233
|
+
// If require.resolve fails, it's not a node module
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
module.exports = { resolveImport };
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Robust Pattern Matching Utility
|
|
3
|
+
* Provides resilient pattern matching with graceful fallbacks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
class PatternMatcher {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.minimatch = null;
|
|
11
|
+
this.initialized = false;
|
|
12
|
+
this.initMinimatch();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize minimatch with error handling
|
|
17
|
+
*/
|
|
18
|
+
initMinimatch() {
|
|
19
|
+
const { dependencyManager } = require('./dependency-manager');
|
|
20
|
+
|
|
21
|
+
this.minimatch = dependencyManager.safeImport('minimatch', null, { useCache: true });
|
|
22
|
+
|
|
23
|
+
if (this.minimatch) {
|
|
24
|
+
this.initialized = true;
|
|
25
|
+
console.log('โ
Minimatch library initialized successfully');
|
|
26
|
+
} else {
|
|
27
|
+
console.warn('โ ๏ธ Minimatch library not available');
|
|
28
|
+
console.warn('โ ๏ธ Falling back to basic string matching');
|
|
29
|
+
this.minimatch = null;
|
|
30
|
+
this.initialized = false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Robust pattern matching with multiple fallback strategies
|
|
36
|
+
*/
|
|
37
|
+
match(filePath, pattern, options = {}) {
|
|
38
|
+
// Validate inputs
|
|
39
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!pattern || typeof pattern !== 'string') {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Try minimatch first if available
|
|
48
|
+
if (this.initialized && this.minimatch) {
|
|
49
|
+
try {
|
|
50
|
+
const matchOptions = {
|
|
51
|
+
matchBase: true,
|
|
52
|
+
dot: true,
|
|
53
|
+
...options
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return this.minimatch.minimatch(filePath, pattern, matchOptions);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.warn(`โ ๏ธ Minimatch failed for pattern "${pattern}":`, error.message);
|
|
59
|
+
// Fall through to fallback methods
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Fallback 1: Basic glob matching (simple cases)
|
|
64
|
+
if (this.basicGlobMatch(filePath, pattern)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Fallback 2: Path containment matching
|
|
69
|
+
if (this.pathContainmentMatch(filePath, pattern)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Fallback 3: Extension matching
|
|
74
|
+
if (this.extensionMatch(filePath, pattern)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Fallback 4: Exact filename matching
|
|
79
|
+
if (this.filenameMatch(filePath, pattern)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Match array of patterns against file path
|
|
88
|
+
*/
|
|
89
|
+
matchAny(filePath, patterns, options = {}) {
|
|
90
|
+
if (!Array.isArray(patterns)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return patterns.some(pattern =>
|
|
95
|
+
this.match(filePath, pattern, options)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Match all patterns against file path
|
|
101
|
+
*/
|
|
102
|
+
matchAll(filePath, patterns, options = {}) {
|
|
103
|
+
if (!Array.isArray(patterns)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return patterns.every(pattern =>
|
|
108
|
+
this.match(filePath, pattern, options)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Basic glob pattern matching for simple cases
|
|
114
|
+
*/
|
|
115
|
+
basicGlobMatch(filePath, pattern) {
|
|
116
|
+
// Handle simple wildcards
|
|
117
|
+
if (pattern === '**/*') return true;
|
|
118
|
+
if (pattern === '*') return true;
|
|
119
|
+
|
|
120
|
+
// Handle extension patterns
|
|
121
|
+
if (pattern.startsWith('*.')) {
|
|
122
|
+
const ext = pattern.substring(1); // Remove *
|
|
123
|
+
return filePath.endsWith(ext);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Handle directory patterns
|
|
127
|
+
if (pattern.endsWith('/**')) {
|
|
128
|
+
const dir = pattern.slice(0, -3); // Remove /**
|
|
129
|
+
return filePath.includes(dir);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Handle simple contains
|
|
133
|
+
if (pattern.includes('*')) {
|
|
134
|
+
const parts = pattern.split('*');
|
|
135
|
+
let currentIndex = 0;
|
|
136
|
+
|
|
137
|
+
for (const part of parts) {
|
|
138
|
+
if (part === '') continue;
|
|
139
|
+
const foundIndex = filePath.indexOf(part, currentIndex);
|
|
140
|
+
if (foundIndex === -1) return false;
|
|
141
|
+
currentIndex = foundIndex + part.length;
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Path containment matching
|
|
151
|
+
*/
|
|
152
|
+
pathContainmentMatch(filePath, pattern) {
|
|
153
|
+
// Normalize paths for comparison
|
|
154
|
+
const normalizedFile = path.normalize(filePath).toLowerCase();
|
|
155
|
+
const normalizedPattern = path.normalize(pattern).toLowerCase();
|
|
156
|
+
|
|
157
|
+
// Check if pattern is contained in file path
|
|
158
|
+
return normalizedFile.includes(normalizedPattern);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Extension matching
|
|
163
|
+
*/
|
|
164
|
+
extensionMatch(filePath, pattern) {
|
|
165
|
+
if (!pattern.includes('.')) return false;
|
|
166
|
+
|
|
167
|
+
const fileExt = path.extname(filePath).toLowerCase();
|
|
168
|
+
const patternExt = path.extname(pattern).toLowerCase();
|
|
169
|
+
|
|
170
|
+
return fileExt === patternExt;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Filename matching (basename comparison)
|
|
175
|
+
*/
|
|
176
|
+
filenameMatch(filePath, pattern) {
|
|
177
|
+
const fileName = path.basename(filePath);
|
|
178
|
+
const patternName = path.basename(pattern);
|
|
179
|
+
|
|
180
|
+
// Exact match
|
|
181
|
+
if (fileName === patternName) return true;
|
|
182
|
+
|
|
183
|
+
// Case insensitive match
|
|
184
|
+
if (fileName.toLowerCase() === patternName.toLowerCase()) return true;
|
|
185
|
+
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get diagnostic information about matching capabilities
|
|
191
|
+
*/
|
|
192
|
+
getDiagnostics() {
|
|
193
|
+
return {
|
|
194
|
+
minimatchAvailable: this.initialized,
|
|
195
|
+
minimatchVersion: this.minimatch ? require('minimatch/package.json').version : null,
|
|
196
|
+
fallbackMethods: [
|
|
197
|
+
'basicGlobMatch',
|
|
198
|
+
'pathContainmentMatch',
|
|
199
|
+
'extensionMatch',
|
|
200
|
+
'filenameMatch'
|
|
201
|
+
]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Test pattern matching with sample data
|
|
207
|
+
*/
|
|
208
|
+
testPatterns() {
|
|
209
|
+
const testCases = [
|
|
210
|
+
{ file: 'src/components/Button.jsx', pattern: '**/*.jsx', expected: true },
|
|
211
|
+
{ file: 'src/utils/helpers.js', pattern: '**/utils/**', expected: true },
|
|
212
|
+
{ file: 'package.json', pattern: '*.json', expected: true },
|
|
213
|
+
{ file: 'src/App.tsx', pattern: '**/*.{ts,tsx}', expected: true },
|
|
214
|
+
{ file: 'README.md', pattern: 'README.*', expected: true }
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
console.log('๐งช Pattern Matching Diagnostics:');
|
|
218
|
+
console.log('==============================');
|
|
219
|
+
|
|
220
|
+
const diagnostics = this.getDiagnostics();
|
|
221
|
+
console.log('Minimatch Available:', diagnostics.minimatchAvailable);
|
|
222
|
+
console.log('Fallback Methods:', diagnostics.fallbackMethods.length);
|
|
223
|
+
|
|
224
|
+
console.log('\n๐งช Test Results:');
|
|
225
|
+
let passed = 0;
|
|
226
|
+
let failed = 0;
|
|
227
|
+
|
|
228
|
+
testCases.forEach(({ file, pattern, expected }, index) => {
|
|
229
|
+
const result = this.match(file, pattern);
|
|
230
|
+
const status = result === expected ? 'โ
PASS' : 'โ FAIL';
|
|
231
|
+
|
|
232
|
+
if (result === expected) passed++;
|
|
233
|
+
else failed++;
|
|
234
|
+
|
|
235
|
+
console.log(`${status} ${file} =~ ${pattern} (expected: ${expected}, got: ${result})`);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
console.log(`\n๐ Summary: ${passed}/${testCases.length} passed, ${failed} failed`);
|
|
239
|
+
return { passed, failed, total: testCases.length };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Export singleton instance
|
|
244
|
+
const patternMatcher = new PatternMatcher();
|
|
245
|
+
|
|
246
|
+
module.exports = { PatternMatcher, patternMatcher };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
class ProgressTracker {
|
|
4
|
+
constructor(totalSteps) {
|
|
5
|
+
this.totalSteps = totalSteps;
|
|
6
|
+
this.currentStep = 0;
|
|
7
|
+
this.startTime = Date.now();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
update(step, message) {
|
|
11
|
+
this.currentStep = step;
|
|
12
|
+
const progress = (this.currentStep / this.totalSteps) * 100;
|
|
13
|
+
const elapsed = Date.now() - this.startTime;
|
|
14
|
+
|
|
15
|
+
// Format elapsed time
|
|
16
|
+
const elapsedSeconds = Math.floor(elapsed / 1000);
|
|
17
|
+
const minutes = Math.floor(elapsedSeconds / 60);
|
|
18
|
+
const seconds = elapsedSeconds % 60;
|
|
19
|
+
|
|
20
|
+
const timeString = `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
21
|
+
|
|
22
|
+
console.log(
|
|
23
|
+
chalk.blue(`[${timeString}] `) +
|
|
24
|
+
chalk.yellow(`Progress: ${this.currentStep}/${this.totalSteps} (${Math.round(progress)}%) - ${message}`)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
updateSimple(current, total, message) {
|
|
29
|
+
const progress = (current / total) * 100;
|
|
30
|
+
const elapsed = Date.now() - this.startTime;
|
|
31
|
+
|
|
32
|
+
// Format elapsed time
|
|
33
|
+
const elapsedSeconds = Math.floor(elapsed / 1000);
|
|
34
|
+
const minutes = Math.floor(elapsedSeconds / 60);
|
|
35
|
+
const seconds = elapsedSeconds % 60;
|
|
36
|
+
|
|
37
|
+
const timeString = `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
38
|
+
|
|
39
|
+
console.log(
|
|
40
|
+
chalk.blue(`[${timeString}] `) +
|
|
41
|
+
chalk.yellow(`Progress: ${current}/${total} (${Math.round(progress)}%) - ${message}`)
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
complete(message = 'Operation completed') {
|
|
46
|
+
const elapsed = Date.now() - this.startTime;
|
|
47
|
+
const elapsedSeconds = Math.floor(elapsed / 1000);
|
|
48
|
+
const minutes = Math.floor(elapsedSeconds / 60);
|
|
49
|
+
const seconds = elapsedSeconds % 60;
|
|
50
|
+
const timeString = `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
51
|
+
|
|
52
|
+
console.log(
|
|
53
|
+
chalk.green(`[${timeString}] โ ${message}`)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
estimateRemaining(current, total) {
|
|
58
|
+
const elapsed = Date.now() - this.startTime;
|
|
59
|
+
const progress = current / total;
|
|
60
|
+
const estimatedTotalTime = elapsed / progress;
|
|
61
|
+
const remainingTime = estimatedTotalTime - elapsed;
|
|
62
|
+
|
|
63
|
+
const remainingSeconds = Math.floor(remainingTime / 1000);
|
|
64
|
+
const minutes = Math.floor(remainingSeconds / 60);
|
|
65
|
+
const seconds = remainingSeconds % 60;
|
|
66
|
+
|
|
67
|
+
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = ProgressTracker;
|