@flaid/devmap 0.2.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/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/ai/completion.d.ts +7 -0
- package/dist/ai/completion.d.ts.map +1 -0
- package/dist/ai/completion.js +27 -0
- package/dist/ai/completion.js.map +1 -0
- package/dist/ai/contextBuilder.d.ts +42 -0
- package/dist/ai/contextBuilder.d.ts.map +1 -0
- package/dist/ai/contextBuilder.js +632 -0
- package/dist/ai/contextBuilder.js.map +1 -0
- package/dist/ai/groq.d.ts +36 -0
- package/dist/ai/groq.d.ts.map +1 -0
- package/dist/ai/groq.js +368 -0
- package/dist/ai/groq.js.map +1 -0
- package/dist/ai/openrouter.d.ts +20 -0
- package/dist/ai/openrouter.d.ts.map +1 -0
- package/dist/ai/openrouter.js +187 -0
- package/dist/ai/openrouter.js.map +1 -0
- package/dist/ai/prompts.d.ts +7 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +88 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/provider.d.ts +15 -0
- package/dist/ai/provider.d.ts.map +1 -0
- package/dist/ai/provider.js +28 -0
- package/dist/ai/provider.js.map +1 -0
- package/dist/ai/snapshotEnrichment.d.ts +4 -0
- package/dist/ai/snapshotEnrichment.d.ts.map +1 -0
- package/dist/ai/snapshotEnrichment.js +188 -0
- package/dist/ai/snapshotEnrichment.js.map +1 -0
- package/dist/ai/types.d.ts +29 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/analyzers/analysis/extractors/fallbackExtractor.d.ts +30 -0
- package/dist/analyzers/analysis/extractors/fallbackExtractor.d.ts.map +1 -0
- package/dist/analyzers/analysis/extractors/fallbackExtractor.js +114 -0
- package/dist/analyzers/analysis/extractors/fallbackExtractor.js.map +1 -0
- package/dist/analyzers/analysis/extractors/index.d.ts +16 -0
- package/dist/analyzers/analysis/extractors/index.d.ts.map +1 -0
- package/dist/analyzers/analysis/extractors/index.js +135 -0
- package/dist/analyzers/analysis/extractors/index.js.map +1 -0
- package/dist/analyzers/analysis/extractors/prismaExtractor.d.ts +15 -0
- package/dist/analyzers/analysis/extractors/prismaExtractor.d.ts.map +1 -0
- package/dist/analyzers/analysis/extractors/prismaExtractor.js +183 -0
- package/dist/analyzers/analysis/extractors/prismaExtractor.js.map +1 -0
- package/dist/analyzers/analysis/extractors/sqlExtractor.d.ts +14 -0
- package/dist/analyzers/analysis/extractors/sqlExtractor.d.ts.map +1 -0
- package/dist/analyzers/analysis/extractors/sqlExtractor.js +163 -0
- package/dist/analyzers/analysis/extractors/sqlExtractor.js.map +1 -0
- package/dist/analyzers/analysis/extractors/types.d.ts +76 -0
- package/dist/analyzers/analysis/extractors/types.d.ts.map +1 -0
- package/dist/analyzers/analysis/extractors/types.js +2 -0
- package/dist/analyzers/analysis/extractors/types.js.map +1 -0
- package/dist/analyzers/analysis/fileAnalysis.d.ts +36 -0
- package/dist/analyzers/analysis/fileAnalysis.d.ts.map +1 -0
- package/dist/analyzers/analysis/fileAnalysis.js +2 -0
- package/dist/analyzers/analysis/fileAnalysis.js.map +1 -0
- package/dist/analyzers/analysis/fileRole.d.ts +30 -0
- package/dist/analyzers/analysis/fileRole.d.ts.map +1 -0
- package/dist/analyzers/analysis/fileRole.js +242 -0
- package/dist/analyzers/analysis/fileRole.js.map +1 -0
- package/dist/analyzers/analysis/fileScanner.d.ts +10 -0
- package/dist/analyzers/analysis/fileScanner.d.ts.map +1 -0
- package/dist/analyzers/analysis/fileScanner.js +44 -0
- package/dist/analyzers/analysis/fileScanner.js.map +1 -0
- package/dist/analyzers/analysis/filterEngine.d.ts +2 -0
- package/dist/analyzers/analysis/filterEngine.d.ts.map +1 -0
- package/dist/analyzers/analysis/filterEngine.js +110 -0
- package/dist/analyzers/analysis/filterEngine.js.map +1 -0
- package/dist/analyzers/analysis/index.d.ts +12 -0
- package/dist/analyzers/analysis/index.d.ts.map +1 -0
- package/dist/analyzers/analysis/index.js +8 -0
- package/dist/analyzers/analysis/index.js.map +1 -0
- package/dist/analyzers/analysis/preprocessors/astroPreprocessor.d.ts +23 -0
- package/dist/analyzers/analysis/preprocessors/astroPreprocessor.d.ts.map +1 -0
- package/dist/analyzers/analysis/preprocessors/astroPreprocessor.js +40 -0
- package/dist/analyzers/analysis/preprocessors/astroPreprocessor.js.map +1 -0
- package/dist/analyzers/analysis/preprocessors/index.d.ts +5 -0
- package/dist/analyzers/analysis/preprocessors/index.d.ts.map +1 -0
- package/dist/analyzers/analysis/preprocessors/index.js +4 -0
- package/dist/analyzers/analysis/preprocessors/index.js.map +1 -0
- package/dist/analyzers/analysis/preprocessors/sveltePreprocessor.d.ts +27 -0
- package/dist/analyzers/analysis/preprocessors/sveltePreprocessor.d.ts.map +1 -0
- package/dist/analyzers/analysis/preprocessors/sveltePreprocessor.js +50 -0
- package/dist/analyzers/analysis/preprocessors/sveltePreprocessor.js.map +1 -0
- package/dist/analyzers/analysis/preprocessors/types.d.ts +45 -0
- package/dist/analyzers/analysis/preprocessors/types.d.ts.map +1 -0
- package/dist/analyzers/analysis/preprocessors/types.js +2 -0
- package/dist/analyzers/analysis/preprocessors/types.js.map +1 -0
- package/dist/analyzers/analysis/preprocessors/vuePreprocessor.d.ts +21 -0
- package/dist/analyzers/analysis/preprocessors/vuePreprocessor.d.ts.map +1 -0
- package/dist/analyzers/analysis/preprocessors/vuePreprocessor.js +44 -0
- package/dist/analyzers/analysis/preprocessors/vuePreprocessor.js.map +1 -0
- package/dist/analyzers/analysis/tsMorphAnalyzer.d.ts +10 -0
- package/dist/analyzers/analysis/tsMorphAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/analysis/tsMorphAnalyzer.js +190 -0
- package/dist/analyzers/analysis/tsMorphAnalyzer.js.map +1 -0
- package/dist/analyzers/detectors/capabilityDetector.d.ts +28 -0
- package/dist/analyzers/detectors/capabilityDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/capabilityDetector.js +288 -0
- package/dist/analyzers/detectors/capabilityDetector.js.map +1 -0
- package/dist/analyzers/detectors/databaseDetector.d.ts +7 -0
- package/dist/analyzers/detectors/databaseDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/databaseDetector.js +71 -0
- package/dist/analyzers/detectors/databaseDetector.js.map +1 -0
- package/dist/analyzers/detectors/frameworkDetector.d.ts +22 -0
- package/dist/analyzers/detectors/frameworkDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/frameworkDetector.js +160 -0
- package/dist/analyzers/detectors/frameworkDetector.js.map +1 -0
- package/dist/analyzers/detectors/frontendFeatureDetector.d.ts +26 -0
- package/dist/analyzers/detectors/frontendFeatureDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/frontendFeatureDetector.js +269 -0
- package/dist/analyzers/detectors/frontendFeatureDetector.js.map +1 -0
- package/dist/analyzers/detectors/index.d.ts +11 -0
- package/dist/analyzers/detectors/index.d.ts.map +1 -0
- package/dist/analyzers/detectors/index.js +7 -0
- package/dist/analyzers/detectors/index.js.map +1 -0
- package/dist/analyzers/detectors/nestRouteDetector.d.ts +13 -0
- package/dist/analyzers/detectors/nestRouteDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/nestRouteDetector.js +69 -0
- package/dist/analyzers/detectors/nestRouteDetector.js.map +1 -0
- package/dist/analyzers/detectors/routeDetector.d.ts +22 -0
- package/dist/analyzers/detectors/routeDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/routeDetector.js +368 -0
- package/dist/analyzers/detectors/routeDetector.js.map +1 -0
- package/dist/analyzers/detectors/serviceDetector.d.ts +3 -0
- package/dist/analyzers/detectors/serviceDetector.d.ts.map +1 -0
- package/dist/analyzers/detectors/serviceDetector.js +81 -0
- package/dist/analyzers/detectors/serviceDetector.js.map +1 -0
- package/dist/analyzers/features/featureDetector.d.ts +22 -0
- package/dist/analyzers/features/featureDetector.d.ts.map +1 -0
- package/dist/analyzers/features/featureDetector.js +821 -0
- package/dist/analyzers/features/featureDetector.js.map +1 -0
- package/dist/analyzers/features/featureMerge.d.ts +65 -0
- package/dist/analyzers/features/featureMerge.d.ts.map +1 -0
- package/dist/analyzers/features/featureMerge.js +168 -0
- package/dist/analyzers/features/featureMerge.js.map +1 -0
- package/dist/analyzers/features/featureSimilarity.d.ts +152 -0
- package/dist/analyzers/features/featureSimilarity.d.ts.map +1 -0
- package/dist/analyzers/features/featureSimilarity.js +264 -0
- package/dist/analyzers/features/featureSimilarity.js.map +1 -0
- package/dist/analyzers/features/index.d.ts +6 -0
- package/dist/analyzers/features/index.d.ts.map +1 -0
- package/dist/analyzers/features/index.js +4 -0
- package/dist/analyzers/features/index.js.map +1 -0
- package/dist/analyzers/graph/dependencyGraph.d.ts +5 -0
- package/dist/analyzers/graph/dependencyGraph.d.ts.map +1 -0
- package/dist/analyzers/graph/dependencyGraph.js +88 -0
- package/dist/analyzers/graph/dependencyGraph.js.map +1 -0
- package/dist/analyzers/graph/dependencyMap.d.ts +43 -0
- package/dist/analyzers/graph/dependencyMap.d.ts.map +1 -0
- package/dist/analyzers/graph/dependencyMap.js +85 -0
- package/dist/analyzers/graph/dependencyMap.js.map +1 -0
- package/dist/analyzers/graph/entryPoints.d.ts +3 -0
- package/dist/analyzers/graph/entryPoints.d.ts.map +1 -0
- package/dist/analyzers/graph/entryPoints.js +21 -0
- package/dist/analyzers/graph/entryPoints.js.map +1 -0
- package/dist/analyzers/graph/index.d.ts +7 -0
- package/dist/analyzers/graph/index.d.ts.map +1 -0
- package/dist/analyzers/graph/index.js +5 -0
- package/dist/analyzers/graph/index.js.map +1 -0
- package/dist/analyzers/graph/sourceScope.d.ts +2 -0
- package/dist/analyzers/graph/sourceScope.d.ts.map +1 -0
- package/dist/analyzers/graph/sourceScope.js +30 -0
- package/dist/analyzers/graph/sourceScope.js.map +1 -0
- package/dist/analyzers/inference/domainInference.d.ts +58 -0
- package/dist/analyzers/inference/domainInference.d.ts.map +1 -0
- package/dist/analyzers/inference/domainInference.js +234 -0
- package/dist/analyzers/inference/domainInference.js.map +1 -0
- package/dist/analyzers/inference/index.d.ts +5 -0
- package/dist/analyzers/inference/index.d.ts.map +1 -0
- package/dist/analyzers/inference/index.js +3 -0
- package/dist/analyzers/inference/index.js.map +1 -0
- package/dist/analyzers/inference/ownershipTopology.d.ts +8 -0
- package/dist/analyzers/inference/ownershipTopology.d.ts.map +1 -0
- package/dist/analyzers/inference/ownershipTopology.js +65 -0
- package/dist/analyzers/inference/ownershipTopology.js.map +1 -0
- package/dist/analyzers/pipeline/analyzerRegistry.d.ts +9 -0
- package/dist/analyzers/pipeline/analyzerRegistry.d.ts.map +1 -0
- package/dist/analyzers/pipeline/analyzerRegistry.js +35 -0
- package/dist/analyzers/pipeline/analyzerRegistry.js.map +1 -0
- package/dist/analyzers/pipeline/heuristicAnalyzer.d.ts +12 -0
- package/dist/analyzers/pipeline/heuristicAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/pipeline/heuristicAnalyzer.js +150 -0
- package/dist/analyzers/pipeline/heuristicAnalyzer.js.map +1 -0
- package/dist/analyzers/pipeline/index.d.ts +7 -0
- package/dist/analyzers/pipeline/index.d.ts.map +1 -0
- package/dist/analyzers/pipeline/index.js +5 -0
- package/dist/analyzers/pipeline/index.js.map +1 -0
- package/dist/analyzers/pipeline/projectMap.d.ts +120 -0
- package/dist/analyzers/pipeline/projectMap.d.ts.map +1 -0
- package/dist/analyzers/pipeline/projectMap.js +726 -0
- package/dist/analyzers/pipeline/projectMap.js.map +1 -0
- package/dist/analyzers/pipeline/projectMetadata.d.ts +19 -0
- package/dist/analyzers/pipeline/projectMetadata.d.ts.map +1 -0
- package/dist/analyzers/pipeline/projectMetadata.js +149 -0
- package/dist/analyzers/pipeline/projectMetadata.js.map +1 -0
- package/dist/analyzers/pipeline/reasonTags.d.ts +6 -0
- package/dist/analyzers/pipeline/reasonTags.d.ts.map +1 -0
- package/dist/analyzers/pipeline/reasonTags.js +6 -0
- package/dist/analyzers/pipeline/reasonTags.js.map +1 -0
- package/dist/analyzers/registry/ai-providers.d.ts +3 -0
- package/dist/analyzers/registry/ai-providers.d.ts.map +1 -0
- package/dist/analyzers/registry/ai-providers.js +94 -0
- package/dist/analyzers/registry/ai-providers.js.map +1 -0
- package/dist/analyzers/registry/analytics.d.ts +3 -0
- package/dist/analyzers/registry/analytics.d.ts.map +1 -0
- package/dist/analyzers/registry/analytics.js +15 -0
- package/dist/analyzers/registry/analytics.js.map +1 -0
- package/dist/analyzers/registry/auth.d.ts +3 -0
- package/dist/analyzers/registry/auth.d.ts.map +1 -0
- package/dist/analyzers/registry/auth.js +18 -0
- package/dist/analyzers/registry/auth.js.map +1 -0
- package/dist/analyzers/registry/background-jobs.d.ts +3 -0
- package/dist/analyzers/registry/background-jobs.d.ts.map +1 -0
- package/dist/analyzers/registry/background-jobs.js +14 -0
- package/dist/analyzers/registry/background-jobs.js.map +1 -0
- package/dist/analyzers/registry/caching.d.ts +3 -0
- package/dist/analyzers/registry/caching.d.ts.map +1 -0
- package/dist/analyzers/registry/caching.js +13 -0
- package/dist/analyzers/registry/caching.js.map +1 -0
- package/dist/analyzers/registry/cms-content.d.ts +3 -0
- package/dist/analyzers/registry/cms-content.d.ts.map +1 -0
- package/dist/analyzers/registry/cms-content.js +15 -0
- package/dist/analyzers/registry/cms-content.js.map +1 -0
- package/dist/analyzers/registry/database.d.ts +3 -0
- package/dist/analyzers/registry/database.d.ts.map +1 -0
- package/dist/analyzers/registry/database.js +13 -0
- package/dist/analyzers/registry/database.js.map +1 -0
- package/dist/analyzers/registry/email.d.ts +3 -0
- package/dist/analyzers/registry/email.d.ts.map +1 -0
- package/dist/analyzers/registry/email.js +19 -0
- package/dist/analyzers/registry/email.js.map +1 -0
- package/dist/analyzers/registry/file-upload.d.ts +3 -0
- package/dist/analyzers/registry/file-upload.d.ts.map +1 -0
- package/dist/analyzers/registry/file-upload.js +19 -0
- package/dist/analyzers/registry/file-upload.js.map +1 -0
- package/dist/analyzers/registry/firebase.d.ts +3 -0
- package/dist/analyzers/registry/firebase.d.ts.map +1 -0
- package/dist/analyzers/registry/firebase.js +8 -0
- package/dist/analyzers/registry/firebase.js.map +1 -0
- package/dist/analyzers/registry/index.d.ts +16 -0
- package/dist/analyzers/registry/index.d.ts.map +1 -0
- package/dist/analyzers/registry/index.js +88 -0
- package/dist/analyzers/registry/index.js.map +1 -0
- package/dist/analyzers/registry/internationalization.d.ts +3 -0
- package/dist/analyzers/registry/internationalization.d.ts.map +1 -0
- package/dist/analyzers/registry/internationalization.js +13 -0
- package/dist/analyzers/registry/internationalization.js.map +1 -0
- package/dist/analyzers/registry/logging-monitoring.d.ts +3 -0
- package/dist/analyzers/registry/logging-monitoring.d.ts.map +1 -0
- package/dist/analyzers/registry/logging-monitoring.js +14 -0
- package/dist/analyzers/registry/logging-monitoring.js.map +1 -0
- package/dist/analyzers/registry/notifications.d.ts +3 -0
- package/dist/analyzers/registry/notifications.d.ts.map +1 -0
- package/dist/analyzers/registry/notifications.js +14 -0
- package/dist/analyzers/registry/notifications.js.map +1 -0
- package/dist/analyzers/registry/payments.d.ts +3 -0
- package/dist/analyzers/registry/payments.d.ts.map +1 -0
- package/dist/analyzers/registry/payments.js +23 -0
- package/dist/analyzers/registry/payments.js.map +1 -0
- package/dist/analyzers/registry/rate-limiting.d.ts +3 -0
- package/dist/analyzers/registry/rate-limiting.d.ts.map +1 -0
- package/dist/analyzers/registry/rate-limiting.js +13 -0
- package/dist/analyzers/registry/rate-limiting.js.map +1 -0
- package/dist/analyzers/registry/search.d.ts +3 -0
- package/dist/analyzers/registry/search.d.ts.map +1 -0
- package/dist/analyzers/registry/search.js +15 -0
- package/dist/analyzers/registry/search.js.map +1 -0
- package/dist/analyzers/registry/testing.d.ts +3 -0
- package/dist/analyzers/registry/testing.d.ts.map +1 -0
- package/dist/analyzers/registry/testing.js +13 -0
- package/dist/analyzers/registry/testing.js.map +1 -0
- package/dist/analyzers/registry/types.d.ts +14 -0
- package/dist/analyzers/registry/types.d.ts.map +1 -0
- package/dist/analyzers/registry/types.js +2 -0
- package/dist/analyzers/registry/types.js.map +1 -0
- package/dist/cache/agentNavigation.d.ts +7 -0
- package/dist/cache/agentNavigation.d.ts.map +1 -0
- package/dist/cache/agentNavigation.js +253 -0
- package/dist/cache/agentNavigation.js.map +1 -0
- package/dist/cache/fileHash.d.ts +2 -0
- package/dist/cache/fileHash.d.ts.map +1 -0
- package/dist/cache/fileHash.js +5 -0
- package/dist/cache/fileHash.js.map +1 -0
- package/dist/cache/snapshot.d.ts +20 -0
- package/dist/cache/snapshot.d.ts.map +1 -0
- package/dist/cache/snapshot.js +184 -0
- package/dist/cache/snapshot.js.map +1 -0
- package/dist/commands/analyze.d.ts +12 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +201 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/config.d.ts +10 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +65 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +141 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/explain.d.ts +37 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +101 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/flow.d.ts +34 -0
- package/dist/commands/flow.d.ts.map +1 -0
- package/dist/commands/flow.js +151 -0
- package/dist/commands/flow.js.map +1 -0
- package/dist/commands/init.d.ts +16 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +197 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/map.d.ts +24 -0
- package/dist/commands/map.d.ts.map +1 -0
- package/dist/commands/map.js +282 -0
- package/dist/commands/map.js.map +1 -0
- package/dist/commands/onboarding.d.ts +36 -0
- package/dist/commands/onboarding.d.ts.map +1 -0
- package/dist/commands/onboarding.js +226 -0
- package/dist/commands/onboarding.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +104 -0
- package/dist/index.js.map +1 -0
- package/dist/onboarding/model.d.ts +27 -0
- package/dist/onboarding/model.d.ts.map +1 -0
- package/dist/onboarding/model.js +2 -0
- package/dist/onboarding/model.js.map +1 -0
- package/dist/onboarding/modelBuilder.d.ts +4 -0
- package/dist/onboarding/modelBuilder.d.ts.map +1 -0
- package/dist/onboarding/modelBuilder.js +330 -0
- package/dist/onboarding/modelBuilder.js.map +1 -0
- package/dist/utils/agentsFile.d.ts +6 -0
- package/dist/utils/agentsFile.d.ts.map +1 -0
- package/dist/utils/agentsFile.js +85 -0
- package/dist/utils/agentsFile.js.map +1 -0
- package/dist/utils/config.d.ts +20 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +88 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/devmapFile.d.ts +3 -0
- package/dist/utils/devmapFile.d.ts.map +1 -0
- package/dist/utils/devmapFile.js +43 -0
- package/dist/utils/devmapFile.js.map +1 -0
- package/dist/utils/errors.d.ts +8 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +92 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/gitignore.d.ts +2 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +22 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/help.d.ts +3 -0
- package/dist/utils/help.d.ts.map +1 -0
- package/dist/utils/help.js +19 -0
- package/dist/utils/help.js.map +1 -0
- package/dist/utils/mapRenderer.d.ts +39 -0
- package/dist/utils/mapRenderer.d.ts.map +1 -0
- package/dist/utils/mapRenderer.js +90 -0
- package/dist/utils/mapRenderer.js.map +1 -0
- package/dist/utils/markdownTerminal.d.ts +6 -0
- package/dist/utils/markdownTerminal.d.ts.map +1 -0
- package/dist/utils/markdownTerminal.js +153 -0
- package/dist/utils/markdownTerminal.js.map +1 -0
- package/dist/utils/output.d.ts +29 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +108 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/packageMetadata.d.ts +2 -0
- package/dist/utils/packageMetadata.d.ts.map +1 -0
- package/dist/utils/packageMetadata.js +5 -0
- package/dist/utils/packageMetadata.js.map +1 -0
- package/dist/utils/prompt.d.ts +11 -0
- package/dist/utils/prompt.d.ts.map +1 -0
- package/dist/utils/prompt.js +81 -0
- package/dist/utils/prompt.js.map +1 -0
- package/dist/utils/slug.d.ts +2 -0
- package/dist/utils/slug.d.ts.map +1 -0
- package/dist/utils/slug.js +8 -0
- package/dist/utils/slug.js.map +1 -0
- package/dist/utils/targetResolver.d.ts +7 -0
- package/dist/utils/targetResolver.d.ts.map +1 -0
- package/dist/utils/targetResolver.js +15 -0
- package/dist/utils/targetResolver.js.map +1 -0
- package/dist/utils/welcome.d.ts +10 -0
- package/dist/utils/welcome.d.ts.map +1 -0
- package/dist/utils/welcome.js +80 -0
- package/dist/utils/welcome.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
import { classifyFileRole, isTechnicalFeatureSource, isDocumentationMeta } from "../analysis/index.js";
|
|
2
|
+
import { detectFrontendPageFeatures, detectClientRouteFeatures } from "../detectors/index.js";
|
|
3
|
+
import { isArchitectureSource } from "../graph/index.js";
|
|
4
|
+
import { mergeIntoFeatureList } from "./featureMerge.js";
|
|
5
|
+
import { FEATURE_SIGNALS, hasAiProviderUrl, isAiProviderImport } from "../registry/index.js";
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// ROLE_FEATURES — only user-visible features, not architectural layers.
|
|
8
|
+
//
|
|
9
|
+
// Excluded intentionally:
|
|
10
|
+
// "api-handler", "service", "middleware", "repository", "ui-component"
|
|
11
|
+
// → architectural implementation concerns, not domain features
|
|
12
|
+
// "ai-integration"
|
|
13
|
+
// → detected via FEATURE_SIGNALS (import-based), role-based too noisy
|
|
14
|
+
// "documentation"
|
|
15
|
+
// → re-added with strict meta-file filtering via isDocumentationEvidence()
|
|
16
|
+
//
|
|
17
|
+
// FEATURE_SIGNALS (vocabulary-driven feature detection) lives in
|
|
18
|
+
// ../registry/index.js — one file per domain under ../registry/.
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
const ROLE_FEATURES = [
|
|
21
|
+
{
|
|
22
|
+
role: "documentation",
|
|
23
|
+
name: "Documentation",
|
|
24
|
+
purpose: "Contains project documentation, API references, and developer guides.",
|
|
25
|
+
terms: ["documentation", "docs", "guide", "reference", "api", "wiki"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
role: "landing-ui",
|
|
29
|
+
name: "Web Landing",
|
|
30
|
+
purpose: "Contains public-facing landing page and marketing UI components.",
|
|
31
|
+
terms: ["web", "landing", "marketing", "hero", "ui", "homepage"]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
role: "cli-command",
|
|
35
|
+
name: "CLI Commands",
|
|
36
|
+
purpose: "Contains CLI entry points and command handlers.",
|
|
37
|
+
terms: ["cli", "command", "bin", "argv", "commander", "yargs"]
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Infrastructure entity names — auth provider internals and ORM bookkeeping
|
|
42
|
+
// entities that should never appear as standalone domain features.
|
|
43
|
+
//
|
|
44
|
+
// These are excluded from entityGraphToFeatures regardless of their relations.
|
|
45
|
+
//
|
|
46
|
+
// Rule: an entity is infrastructure if it exists solely to support an
|
|
47
|
+
// external system (auth provider, ORM, payment processor) and has no
|
|
48
|
+
// business logic of its own visible to the application domain.
|
|
49
|
+
//
|
|
50
|
+
// "Account" is excluded here because in NextAuth / Lucia schemas it
|
|
51
|
+
// represents an OAuth provider link record, not a user-facing account.
|
|
52
|
+
// Projects that genuinely use "Account" as a domain entity (e.g. billing
|
|
53
|
+
// accounts, bank accounts) will still detect it via FEATURE_SIGNALS or
|
|
54
|
+
// capabilitiesToFeatures — so excluding it here is safe.
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
const INFRASTRUCTURE_ENTITY_NAMES = new Set([
|
|
57
|
+
// NextAuth / Lucia / Better-Auth internals
|
|
58
|
+
"Account",
|
|
59
|
+
"Session",
|
|
60
|
+
"VerificationToken",
|
|
61
|
+
"VerificationCode",
|
|
62
|
+
"Authenticator",
|
|
63
|
+
// Generic auth infrastructure
|
|
64
|
+
"PasswordResetToken",
|
|
65
|
+
"RefreshToken",
|
|
66
|
+
"OAuthToken",
|
|
67
|
+
"OAuthAccount",
|
|
68
|
+
// Audit / system tables
|
|
69
|
+
"AuditLog",
|
|
70
|
+
"ActivityLog",
|
|
71
|
+
"EventLog",
|
|
72
|
+
]);
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Documentation evidence filter
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
function isDocumentationEvidence(filePath) {
|
|
77
|
+
const normalized = filePath.toLowerCase();
|
|
78
|
+
const filename = normalized.split("/").at(-1) ?? normalized;
|
|
79
|
+
if (/(^|\/)\.github(\/|$)/.test(normalized))
|
|
80
|
+
return false;
|
|
81
|
+
if (isDocumentationMeta(filename))
|
|
82
|
+
return false;
|
|
83
|
+
return (/(^|\/)docs?(\/|$)/.test(normalized)
|
|
84
|
+
|| /(^|\/)wiki(\/|$)/.test(normalized)
|
|
85
|
+
|| filename === "readme.md"
|
|
86
|
+
|| /\.(guide|tutorial|reference)\.(md|mdx)$/.test(filename)
|
|
87
|
+
|| /(openapi|swagger)\.(json|yaml|yml)$/.test(filename));
|
|
88
|
+
}
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// Feature evidence file filter
|
|
91
|
+
//
|
|
92
|
+
// Excludes files that are generated artifacts or migration history —
|
|
93
|
+
// these contain domain keywords (table/column names) that cause false
|
|
94
|
+
// positive feature detection without representing actual implementation.
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
function isFeatureEvidenceFile(path) {
|
|
97
|
+
const lower = path.toLowerCase();
|
|
98
|
+
return !(/\/prisma\/migrations\//.test(lower)
|
|
99
|
+
|| /\/migrations\//.test(lower)
|
|
100
|
+
|| /\.sql$/.test(lower)
|
|
101
|
+
|| /\/generated\//.test(lower)
|
|
102
|
+
|| /\.generated\./.test(lower)
|
|
103
|
+
|| /\/prisma\/schema\.prisma$/.test(lower)
|
|
104
|
+
|| /^prisma\/schema\.prisma$/.test(lower));
|
|
105
|
+
}
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Registry self-exclusion
|
|
108
|
+
//
|
|
109
|
+
// Files under ../registry/ *define* the detection vocabulary. They are not
|
|
110
|
+
// evidence of a real feature in the scanned project — without this check a
|
|
111
|
+
// file like registry/email.ts would self-match the "email" term it describes.
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
function isRegistryFile(path) {
|
|
114
|
+
return /\/analyzers\/registry\//.test(path.toLowerCase());
|
|
115
|
+
}
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// FEATURE_FILE_PRIORITIES
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
const FEATURE_FILE_PRIORITIES = {
|
|
120
|
+
Documentation: [
|
|
121
|
+
/(^|\/)readme\.md$/,
|
|
122
|
+
/(^|\/)contributing\.md$/,
|
|
123
|
+
/(^|\/)changelog\.md$/,
|
|
124
|
+
/(^|\/)license(\.md)?$/,
|
|
125
|
+
/(^|\/)docs\/index\.md$/,
|
|
126
|
+
/(^|\/)docs\//,
|
|
127
|
+
],
|
|
128
|
+
Authentication: [
|
|
129
|
+
/(^|\/)src\/auth\.[cm]?[jt]sx?$/,
|
|
130
|
+
/(^|\/)auth\.[cm]?[jt]sx?$/,
|
|
131
|
+
/\/auth\/config\.[cm]?[jt]sx?$/,
|
|
132
|
+
/(^|\/)middleware\.[cm]?[jt]sx?$/,
|
|
133
|
+
/\/auth\/middleware\.[cm]?[jt]sx?$/,
|
|
134
|
+
/\/api\/auth\//,
|
|
135
|
+
/\/api\/.*\/(login|register|logout)\.[cm]?[jt]sx?$/,
|
|
136
|
+
/\/providers?\/auth[^/]*\.[cm]?[jt]sx?$/,
|
|
137
|
+
/\/context\/auth[^/]*\.[cm]?[jt]sx?$/,
|
|
138
|
+
],
|
|
139
|
+
Payments: [
|
|
140
|
+
/\/lib\/stripe\.[cm]?[jt]sx?$/,
|
|
141
|
+
/\/lib\/payment[^/]*\.[cm]?[jt]sx?$/,
|
|
142
|
+
/\/api\/.*webhook[^/]*\.[cm]?[jt]sx?$/,
|
|
143
|
+
/\/api\/.*checkout[^/]*\.[cm]?[jt]sx?$/,
|
|
144
|
+
/\/api\/.*payment[^/]*\.[cm]?[jt]sx?$/,
|
|
145
|
+
],
|
|
146
|
+
"AI Integration": [
|
|
147
|
+
/\/lib\/ai\.[cm]?[jt]sx?$/,
|
|
148
|
+
/\/ai\/provider\.[cm]?[jt]sx?$/,
|
|
149
|
+
/\/ai\/client\.[cm]?[jt]sx?$/,
|
|
150
|
+
/\/lib\/openai\.[cm]?[jt]sx?$/,
|
|
151
|
+
/\/lib\/groq\.[cm]?[jt]sx?$/,
|
|
152
|
+
/\/ai\/prompts?\.[cm]?[jt]sx?$/,
|
|
153
|
+
/\/ai\/completion\.[cm]?[jt]sx?$/,
|
|
154
|
+
],
|
|
155
|
+
Email: [
|
|
156
|
+
/\/lib\/email[^/]*\.[cm]?[jt]sx?$/,
|
|
157
|
+
/\/lib\/mailer[^/]*\.[cm]?[jt]sx?$/,
|
|
158
|
+
/\/emails?\//,
|
|
159
|
+
/\/templates?\//,
|
|
160
|
+
],
|
|
161
|
+
"File Upload": [
|
|
162
|
+
/\/lib\/upload[^/]*\.[cm]?[jt]sx?$/,
|
|
163
|
+
/\/lib\/storage[^/]*\.[cm]?[jt]sx?$/,
|
|
164
|
+
/\/lib\/cloudinary[^/]*\.[cm]?[jt]sx?$/,
|
|
165
|
+
/\/api\/.*upload[^/]*\.[cm]?[jt]sx?$/,
|
|
166
|
+
],
|
|
167
|
+
"Background Jobs": [
|
|
168
|
+
/\/lib\/queue[^/]*\.[cm]?[jt]sx?$/,
|
|
169
|
+
/\/lib\/worker[^/]*\.[cm]?[jt]sx?$/,
|
|
170
|
+
/\/workers?\//,
|
|
171
|
+
/\/jobs?\//,
|
|
172
|
+
/\/queues?\//,
|
|
173
|
+
],
|
|
174
|
+
Caching: [
|
|
175
|
+
/\/lib\/redis\.[cm]?[jt]sx?$/,
|
|
176
|
+
/\/lib\/cache[^/]*\.[cm]?[jt]sx?$/,
|
|
177
|
+
/\/cache\//,
|
|
178
|
+
],
|
|
179
|
+
"CLI Commands": [
|
|
180
|
+
/\/src\/index\.[cm]?[jt]sx?$/,
|
|
181
|
+
/\/bin\//,
|
|
182
|
+
/\/commands?\/index\.[cm]?[jt]sx?$/,
|
|
183
|
+
/\/commands?\//,
|
|
184
|
+
],
|
|
185
|
+
"Web Landing": [
|
|
186
|
+
/\/pages\/index\.(astro|tsx?|jsx?)$/,
|
|
187
|
+
/\/app\/page\.(tsx?|jsx?)$/,
|
|
188
|
+
/\/landing\//,
|
|
189
|
+
/(hero|pricing|features?section)[^/]*\.(astro|tsx?|jsx?|vue|svelte)$/,
|
|
190
|
+
],
|
|
191
|
+
Testing: [
|
|
192
|
+
/\/(vitest|jest)\.config\.[cm]?[jt]sx?$/,
|
|
193
|
+
/\/test-utils?\.[cm]?[jt]sx?$/,
|
|
194
|
+
/\/setup\.(test|spec)\.[cm]?[jt]sx?$/,
|
|
195
|
+
],
|
|
196
|
+
Search: [
|
|
197
|
+
/\/lib\/search[^/]*\.[cm]?[jt]sx?$/,
|
|
198
|
+
/\/lib\/meilisearch[^/]*\.[cm]?[jt]sx?$/,
|
|
199
|
+
/\/lib\/algolia[^/]*\.[cm]?[jt]sx?$/,
|
|
200
|
+
],
|
|
201
|
+
"Logging & Monitoring": [
|
|
202
|
+
/\/lib\/logger[^/]*\.[cm]?[jt]sx?$/,
|
|
203
|
+
/\/lib\/sentry[^/]*\.[cm]?[jt]sx?$/,
|
|
204
|
+
/\/instrumentation\.[cm]?[jt]sx?$/,
|
|
205
|
+
/\/sentry\.(client|server|edge)\.[cm]?[jt]sx?$/,
|
|
206
|
+
],
|
|
207
|
+
};
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Entry point scoring
|
|
210
|
+
//
|
|
211
|
+
// Lower score = better entry point candidate.
|
|
212
|
+
// Generic utility/helper files score >= ENTRY_POINT_EXCLUDE_THRESHOLD (excluded).
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
const ENTRY_POINT_EXCLUDE_THRESHOLD = 90;
|
|
215
|
+
export function classifyFileTier(path) {
|
|
216
|
+
const lower = path.toLowerCase();
|
|
217
|
+
if (/\/(prisma\/)?migrations?\//.test(lower))
|
|
218
|
+
return "excluded";
|
|
219
|
+
if (/\.sql$/.test(lower))
|
|
220
|
+
return "excluded";
|
|
221
|
+
if (/\/generated\//.test(lower) || /\.generated\./.test(lower))
|
|
222
|
+
return "excluded";
|
|
223
|
+
if (/\.(lock|log|map)$/.test(lower))
|
|
224
|
+
return "excluded";
|
|
225
|
+
if (/(^|\/)schema\.prisma$/.test(lower))
|
|
226
|
+
return "reference";
|
|
227
|
+
if (/\.(config|conf)\.[cm]?[jt]s$/.test(lower))
|
|
228
|
+
return "reference";
|
|
229
|
+
if (/\/(api|routes?)\//.test(lower))
|
|
230
|
+
return "primary";
|
|
231
|
+
if (/\.(service|usecase|action|route|handler)\.[cm]?[jt]sx?$/.test(lower))
|
|
232
|
+
return "primary";
|
|
233
|
+
if (/\/(hooks?|stores?)\//.test(lower))
|
|
234
|
+
return "primary";
|
|
235
|
+
if (/\.[cm]?[jt]sx?$/.test(lower))
|
|
236
|
+
return "supporting";
|
|
237
|
+
return "reference";
|
|
238
|
+
}
|
|
239
|
+
function scoreEntryPointRelevance(file, _context) {
|
|
240
|
+
const lower = file.toLowerCase();
|
|
241
|
+
if (/\/(utils?|helpers?|constants?|types?|shared)\.[cm]?[jt]sx?$/.test(lower))
|
|
242
|
+
return 100;
|
|
243
|
+
if (/\/(index)\.[cm]?[jt]sx?$/.test(lower) && !/\/(api|routes?|commands?)\//.test(lower))
|
|
244
|
+
return 95;
|
|
245
|
+
if (/\.(d\.ts)$/.test(lower))
|
|
246
|
+
return 100;
|
|
247
|
+
if (/\/(route|handler)\.[cm]?[jt]sx?$/.test(lower))
|
|
248
|
+
return 5;
|
|
249
|
+
if (/\/api\//.test(lower))
|
|
250
|
+
return 10;
|
|
251
|
+
if (/\.(service|usecase|action)\.[cm]?[jt]sx?$/.test(lower))
|
|
252
|
+
return 20;
|
|
253
|
+
if (/\/services?\//.test(lower))
|
|
254
|
+
return 25;
|
|
255
|
+
if (/\/(commands?|bin)\//.test(lower))
|
|
256
|
+
return 15;
|
|
257
|
+
if (/\/(pages?|app)\/.+\/(page|layout)\.[cm]?[jt]sx?$/.test(lower))
|
|
258
|
+
return 30;
|
|
259
|
+
if (/\/components?\//.test(lower))
|
|
260
|
+
return 50;
|
|
261
|
+
if (/\/lib\//.test(lower))
|
|
262
|
+
return 60;
|
|
263
|
+
return 70;
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// Main entry
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
export function detectFeatures(files, analyses, routes, database, entityGraph, capabilities, fileGraph) {
|
|
269
|
+
const features = [];
|
|
270
|
+
const scopedFiles = files.filter((file) => isArchitectureSource(file.path));
|
|
271
|
+
// --- ROLE_FEATURES ---
|
|
272
|
+
for (const definition of ROLE_FEATURES) {
|
|
273
|
+
const evidence = scopedFiles
|
|
274
|
+
.filter((file) => classifyFileRole(file.path) === definition.role
|
|
275
|
+
|| (definition.name === "CLI Commands"
|
|
276
|
+
&& /(^|\/)src\/index\.[cm]?[jt]s$/.test(file.path.toLowerCase())))
|
|
277
|
+
.filter((file) => isFeatureEvidenceFile(file.path))
|
|
278
|
+
.filter((file) => definition.role !== "documentation" || isDocumentationEvidence(file.path))
|
|
279
|
+
.map((file) => file.path)
|
|
280
|
+
.sort((left, right) => featureFilePriority(definition.name, left) - featureFilePriority(definition.name, right)
|
|
281
|
+
|| left.localeCompare(right))
|
|
282
|
+
.slice(0, 12);
|
|
283
|
+
if (evidence.length > 0) {
|
|
284
|
+
features.push(createFeatureInfo(definition.name, evidence, definition.terms, definition.purpose, analyses));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// --- FEATURE_SIGNALS ---
|
|
288
|
+
// Pre-filter once here — avoids redundant isFeatureEvidenceFile calls inside each signal loop
|
|
289
|
+
// Registry files (../registry/**) are excluded: they *define* the vocabulary,
|
|
290
|
+
// so their paths/content would otherwise self-match every signal they describe.
|
|
291
|
+
const technicalFiles = scopedFiles
|
|
292
|
+
.filter((file) => isTechnicalFeatureSource(file.path))
|
|
293
|
+
.filter((file) => isFeatureEvidenceFile(file.path))
|
|
294
|
+
.filter((file) => !isRegistryFile(file.path));
|
|
295
|
+
for (const signal of FEATURE_SIGNALS) {
|
|
296
|
+
const evidence = technicalFiles
|
|
297
|
+
.filter((file) => matchesSignal(file, analyses[file.path], signal.terms, signal.importOnly))
|
|
298
|
+
.map((file) => file.path)
|
|
299
|
+
.sort((left, right) => featureFilePriority(signal.name, left) - featureFilePriority(signal.name, right)
|
|
300
|
+
|| left.localeCompare(right))
|
|
301
|
+
.slice(0, 5);
|
|
302
|
+
const primaryEvidence = evidence.filter((f) => classifyFileTier(f) === "primary" || classifyFileTier(f) === "supporting");
|
|
303
|
+
if (signal.minimumDistinctFiles && primaryEvidence.length < signal.minimumDistinctFiles) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (evidence.length > 0) {
|
|
307
|
+
mergeFeature(features, createFeatureInfo(signal.name, evidence, signal.terms, signal.purpose, analyses));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Database and API Routes are architectural concerns, not domain features.
|
|
311
|
+
// Database info lives in snapshot.database, routes in snapshot.routes.
|
|
312
|
+
if (capabilities && capabilities.length > 0) {
|
|
313
|
+
for (const feature of capabilitiesToFeatures(capabilities)) {
|
|
314
|
+
if (feature !== null)
|
|
315
|
+
mergeFeature(features, feature);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (entityGraph && entityGraph.entityNames.length > 0) {
|
|
319
|
+
for (const feature of entityGraphToFeatures(entityGraph, scopedFiles)) {
|
|
320
|
+
mergeFeature(features, feature);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (fileGraph) {
|
|
324
|
+
for (const feature of detectFrontendPageFeatures(routes, fileGraph)) {
|
|
325
|
+
mergeFeature(features, feature);
|
|
326
|
+
}
|
|
327
|
+
for (const feature of detectClientRouteFeatures(scopedFiles, fileGraph)) {
|
|
328
|
+
mergeFeature(features, feature);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return enrichAuthenticationFeature(features, scopedFiles, analyses)
|
|
332
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
333
|
+
}
|
|
334
|
+
// ---------------------------------------------------------------------------
|
|
335
|
+
// capabilitiesToFeatures
|
|
336
|
+
//
|
|
337
|
+
// Capabilities with no resolvable entry points are dropped — they represent
|
|
338
|
+
// route patterns detected without backing implementation files, which
|
|
339
|
+
// produces low-quality features (empty criticalFiles, misleading names).
|
|
340
|
+
// ---------------------------------------------------------------------------
|
|
341
|
+
function capabilitiesToFeatures(capabilities) {
|
|
342
|
+
return capabilities.map((cap) => {
|
|
343
|
+
const terms = [cap.kind, ...cap.entities.map((e) => e.toLowerCase())];
|
|
344
|
+
const scoredEvidence = cap.evidence
|
|
345
|
+
.map((file) => ({ file, score: scoreEntryPointRelevance(file, cap.kind) }))
|
|
346
|
+
.sort((a, b) => a.score - b.score);
|
|
347
|
+
const entryPoints = scoredEvidence
|
|
348
|
+
.filter((e) => e.score < ENTRY_POINT_EXCLUDE_THRESHOLD)
|
|
349
|
+
.filter((e) => {
|
|
350
|
+
const tier = classifyFileTier(e.file);
|
|
351
|
+
return tier === "primary" || tier === "supporting";
|
|
352
|
+
})
|
|
353
|
+
.slice(0, 2)
|
|
354
|
+
.map((e) => e.file);
|
|
355
|
+
// Drop capabilities with no resolvable entry points and non-high confidence.
|
|
356
|
+
// These are weak detections: route pattern matched but no backing file found.
|
|
357
|
+
if (entryPoints.length === 0 && cap.confidence !== "high")
|
|
358
|
+
return null;
|
|
359
|
+
return {
|
|
360
|
+
name: cap.name,
|
|
361
|
+
purpose: purposeFromCapability(cap),
|
|
362
|
+
files: cap.evidence,
|
|
363
|
+
entryPoints,
|
|
364
|
+
businessFlow: [],
|
|
365
|
+
searchTerms: [...new Set(terms)],
|
|
366
|
+
confidence: cap.confidence,
|
|
367
|
+
evidence: cap.evidence
|
|
368
|
+
};
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function purposeFromCapability(cap) {
|
|
372
|
+
const entityList = cap.entities.length > 0 ? cap.entities.join(", ") : "resources";
|
|
373
|
+
switch (cap.kind) {
|
|
374
|
+
case "crud": return `Handles create, read, update, and delete operations for ${entityList}.`;
|
|
375
|
+
case "sharing": return `Handles content sharing via public links and share tokens.`;
|
|
376
|
+
case "collaboration": return `Handles team collaboration, workspaces, and member management.`;
|
|
377
|
+
case "discovery": return `Handles public content discovery and browsing.`;
|
|
378
|
+
case "publishing": return `Handles content publishing and visibility management.`;
|
|
379
|
+
case "social": return `Handles social interactions like likes, favorites, and reactions.`;
|
|
380
|
+
case "file-management": return `Handles file uploads, storage, and media management.`;
|
|
381
|
+
case "real-time": return `Handles real-time events, websockets, and live updates.`;
|
|
382
|
+
case "search": return `Handles full-text search and content filtering.`;
|
|
383
|
+
case "reporting": return `Handles usage statistics, analytics, and reporting.`;
|
|
384
|
+
default: return `Handles ${cap.kind} operations for ${entityList}.`;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
// entityGraphToFeatures
|
|
389
|
+
//
|
|
390
|
+
// Entity ownership model:
|
|
391
|
+
//
|
|
392
|
+
// TRUE CHILD — entity with a single parent via one-to-many AND no own
|
|
393
|
+
// children (leaf node), OR has a child-like name suffix.
|
|
394
|
+
// Skipped as standalone feature; mentioned in parent's purpose.
|
|
395
|
+
// Example: ChecklistItem (owned by Message, no children, suffix "Item")
|
|
396
|
+
//
|
|
397
|
+
// STANDALONE — entity with multiple parents, or entity that itself owns
|
|
398
|
+
// other entities (intermediate node). Gets its own feature.
|
|
399
|
+
// Example: Room (owned by User, but owns Message[])
|
|
400
|
+
// Example: Message (owned by User+Room, but owns ChecklistItem[])
|
|
401
|
+
//
|
|
402
|
+
// OWNED — non-infra, non-true-child entities this entity owns via
|
|
403
|
+
// one-to-many. Shown in purpose string.
|
|
404
|
+
//
|
|
405
|
+
// PEER — many-to-many associations. Shown as "associates with X".
|
|
406
|
+
//
|
|
407
|
+
// Infrastructure entities (auth provider internals, ORM bookkeeping) are
|
|
408
|
+
// excluded entirely via INFRASTRUCTURE_ENTITY_NAMES.
|
|
409
|
+
// ---------------------------------------------------------------------------
|
|
410
|
+
// Suffixes that semantically indicate a sub-item of a parent entity.
|
|
411
|
+
// Used as a tiebreaker when an entity has a single parent and no children.
|
|
412
|
+
const TRUE_CHILD_SUFFIXES = /(?:Item|Entry|Detail|Line|Row|Part|Step|Variant|Option)$/;
|
|
413
|
+
/**
|
|
414
|
+
* isTrueChildEntity — determines if an entity should be skipped as standalone.
|
|
415
|
+
*
|
|
416
|
+
* An entity is a true child if ALL of:
|
|
417
|
+
* 1. Exactly ONE parent owns it via one-to-many (single exclusive owner)
|
|
418
|
+
* 2. It has NO outgoing one-to-many of its own (leaf node)
|
|
419
|
+
* OR its name has a child-like suffix (Item, Entry, Detail, etc.)
|
|
420
|
+
*/
|
|
421
|
+
function isTrueChildEntity(entityName, relations) {
|
|
422
|
+
const parents = relations.filter((r) => r.to === entityName && r.kind === "one-to-many");
|
|
423
|
+
if (parents.length === 0)
|
|
424
|
+
return false;
|
|
425
|
+
if (parents.length > 1)
|
|
426
|
+
return false;
|
|
427
|
+
const hasOwnChildren = relations.some((r) => r.from === entityName && r.kind === "one-to-many");
|
|
428
|
+
if (!hasOwnChildren)
|
|
429
|
+
return true;
|
|
430
|
+
if (TRUE_CHILD_SUFFIXES.test(entityName))
|
|
431
|
+
return true;
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
function entityFileTierScore(path) {
|
|
435
|
+
if (/\.(prisma)$/.test(path))
|
|
436
|
+
return 50;
|
|
437
|
+
if (/\/(migrations?)\//.test(path))
|
|
438
|
+
return 100;
|
|
439
|
+
return 0;
|
|
440
|
+
}
|
|
441
|
+
function findEntityFiles(entityName, files) {
|
|
442
|
+
const lowerName = entityName.toLowerCase();
|
|
443
|
+
const nameSegments = splitNameToSegments(lowerName);
|
|
444
|
+
const EXCLUDED_EXTS = /\.(sql|lock|log)(\.[^/]+)?$/i;
|
|
445
|
+
const isNonSourceDoc = (p) => /\.md$/i.test(p) && !/\/docs\//.test(p.toLowerCase());
|
|
446
|
+
return files
|
|
447
|
+
.filter((f) => isFeatureEvidenceFile(f.path))
|
|
448
|
+
.filter((f) => {
|
|
449
|
+
const lowerPath = f.path.toLowerCase();
|
|
450
|
+
return !EXCLUDED_EXTS.test(lowerPath) && !isNonSourceDoc(lowerPath);
|
|
451
|
+
})
|
|
452
|
+
.filter((f) => {
|
|
453
|
+
const lowerPath = f.path.toLowerCase();
|
|
454
|
+
const pathSegments = lowerPath.split(/[/\\]/);
|
|
455
|
+
return pathSegments.some((segment) => {
|
|
456
|
+
const fileStem = segment.replace(/\.[^/.]+$/, "");
|
|
457
|
+
if (fileStem === lowerName)
|
|
458
|
+
return true;
|
|
459
|
+
const segParts = splitNameToSegments(fileStem);
|
|
460
|
+
return nameSegments.some((ns) => segParts.includes(ns));
|
|
461
|
+
});
|
|
462
|
+
})
|
|
463
|
+
.map((f) => f.path)
|
|
464
|
+
.sort((a, b) => {
|
|
465
|
+
const tierDiff = entityFileTierScore(a) - entityFileTierScore(b);
|
|
466
|
+
if (tierDiff !== 0)
|
|
467
|
+
return tierDiff;
|
|
468
|
+
return a.localeCompare(b);
|
|
469
|
+
})
|
|
470
|
+
.slice(0, 5);
|
|
471
|
+
}
|
|
472
|
+
function splitNameToSegments(name) {
|
|
473
|
+
return name
|
|
474
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
475
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
|
|
476
|
+
.toLowerCase()
|
|
477
|
+
.split(/[^a-z0-9]+/)
|
|
478
|
+
.filter(Boolean);
|
|
479
|
+
}
|
|
480
|
+
function entityGraphToFeatures(entityGraph, files = []) {
|
|
481
|
+
if (entityGraph.source === "empty")
|
|
482
|
+
return [];
|
|
483
|
+
// Deduplicate relations across the graph
|
|
484
|
+
const relations = [];
|
|
485
|
+
const seenRelKeys = new Set();
|
|
486
|
+
for (const entity of entityGraph.entities) {
|
|
487
|
+
for (const r of entity.relations) {
|
|
488
|
+
const key = `${r.from}→${r.to}:${r.kind}`;
|
|
489
|
+
if (!seenRelKeys.has(key)) {
|
|
490
|
+
seenRelKeys.add(key);
|
|
491
|
+
relations.push(r);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const trueChildNames = new Set(entityGraph.entities
|
|
496
|
+
.map((e) => e.name)
|
|
497
|
+
.filter((name) => isTrueChildEntity(name, relations)));
|
|
498
|
+
const features = [];
|
|
499
|
+
const meaningfulEntities = entityGraph.source === "prisma"
|
|
500
|
+
? entityGraph.entities.filter((e) => relations.some((r) => r.from === e.name || r.to === e.name))
|
|
501
|
+
: entityGraph.entities;
|
|
502
|
+
for (const entity of meaningfulEntities.slice(0, 8)) {
|
|
503
|
+
if (trueChildNames.has(entity.name))
|
|
504
|
+
continue;
|
|
505
|
+
if (INFRASTRUCTURE_ENTITY_NAMES.has(entity.name))
|
|
506
|
+
continue;
|
|
507
|
+
const ownedNames = relations
|
|
508
|
+
.filter((r) => r.from === entity.name && r.kind === "one-to-many")
|
|
509
|
+
.map((r) => r.to)
|
|
510
|
+
.filter((n) => !INFRASTRUCTURE_ENTITY_NAMES.has(n) && !trueChildNames.has(n));
|
|
511
|
+
const oneToOneOwned = relations
|
|
512
|
+
.filter((r) => r.from === entity.name && r.kind === "one-to-one")
|
|
513
|
+
.map((r) => r.to)
|
|
514
|
+
.filter((n) => !INFRASTRUCTURE_ENTITY_NAMES.has(n) && !trueChildNames.has(n));
|
|
515
|
+
const allOwned = [...new Set([...ownedNames, ...oneToOneOwned])];
|
|
516
|
+
const peerNames = relations
|
|
517
|
+
.filter((r) => r.kind === "many-to-many"
|
|
518
|
+
&& (r.from === entity.name || r.to === entity.name))
|
|
519
|
+
.map((r) => r.from === entity.name ? r.to : r.from)
|
|
520
|
+
.filter((n) => !INFRASTRUCTURE_ENTITY_NAMES.has(n) && !trueChildNames.has(n) && n !== entity.name);
|
|
521
|
+
const purpose = buildEntityPurpose(entity.name, allOwned, peerNames);
|
|
522
|
+
const searchTerms = [
|
|
523
|
+
entity.name.toLowerCase(),
|
|
524
|
+
...allOwned.map((n) => n.toLowerCase()),
|
|
525
|
+
...peerNames.map((n) => n.toLowerCase()),
|
|
526
|
+
"management", "crud"
|
|
527
|
+
].filter((v, i, arr) => arr.indexOf(v) === i);
|
|
528
|
+
const entityFiles = entity.sourceFiles ?? findEntityFiles(entity.name, files);
|
|
529
|
+
if (entityFiles.length === 0)
|
|
530
|
+
continue;
|
|
531
|
+
const entryPoints = entityFiles
|
|
532
|
+
.map((file) => ({ file, score: scoreEntryPointRelevance(file, entity.name.toLowerCase()) }))
|
|
533
|
+
.sort((a, b) => a.score - b.score)
|
|
534
|
+
.filter((e) => e.score < ENTRY_POINT_EXCLUDE_THRESHOLD)
|
|
535
|
+
.filter((e) => {
|
|
536
|
+
const tier = classifyFileTier(e.file);
|
|
537
|
+
return tier === "primary" || tier === "supporting";
|
|
538
|
+
})
|
|
539
|
+
.slice(0, 2)
|
|
540
|
+
.map((e) => e.file);
|
|
541
|
+
let confidence = entityGraph.source === "prisma" ? "high" : "medium";
|
|
542
|
+
if (entityFiles.length === 1) {
|
|
543
|
+
const tier = classifyFileTier(entityFiles[0]);
|
|
544
|
+
if (tier === "reference")
|
|
545
|
+
confidence = "low";
|
|
546
|
+
}
|
|
547
|
+
features.push({
|
|
548
|
+
name: `${entity.name} Management`,
|
|
549
|
+
purpose,
|
|
550
|
+
files: entityFiles,
|
|
551
|
+
entryPoints,
|
|
552
|
+
businessFlow: [],
|
|
553
|
+
searchTerms,
|
|
554
|
+
confidence,
|
|
555
|
+
evidence: entityFiles
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
return features;
|
|
559
|
+
}
|
|
560
|
+
function buildEntityPurpose(entityName, owned, peers) {
|
|
561
|
+
if (owned.length > 0 && peers.length > 0) {
|
|
562
|
+
return `Manages ${entityName} (including ${owned.join(", ")}) and its associations with ${peers.join(", ")}.`;
|
|
563
|
+
}
|
|
564
|
+
if (owned.length > 0) {
|
|
565
|
+
return `Manages ${entityName} and its owned items: ${owned.join(", ")}.`;
|
|
566
|
+
}
|
|
567
|
+
if (peers.length > 0) {
|
|
568
|
+
return `Manages ${entityName} and its associations with ${peers.join(", ")}.`;
|
|
569
|
+
}
|
|
570
|
+
return `Manages ${entityName} data and operations.`;
|
|
571
|
+
}
|
|
572
|
+
// ---------------------------------------------------------------------------
|
|
573
|
+
// Confidence calculation
|
|
574
|
+
// ---------------------------------------------------------------------------
|
|
575
|
+
function calculateFeatureConfidence(evidence, analyses) {
|
|
576
|
+
if (evidence.length === 0)
|
|
577
|
+
return "low";
|
|
578
|
+
const highQualityCount = evidence.filter((path) => analyses[path]?.confidence === "high").length;
|
|
579
|
+
if (highQualityCount >= 2)
|
|
580
|
+
return "high";
|
|
581
|
+
if (highQualityCount >= 1 || evidence.length >= 2)
|
|
582
|
+
return "medium";
|
|
583
|
+
return "low";
|
|
584
|
+
}
|
|
585
|
+
function createFeatureInfo(name, evidence, terms, purpose = `Identifies ${name.toLowerCase()} capability in the project.`, analyses = {}) {
|
|
586
|
+
const files = evidence.filter((item) => item.includes("/") || /\.[A-Za-z0-9]+$/.test(item));
|
|
587
|
+
const entryPoints = files
|
|
588
|
+
.map((file) => ({ file, score: scoreEntryPointRelevance(file, name.toLowerCase()) }))
|
|
589
|
+
.sort((a, b) => a.score - b.score)
|
|
590
|
+
.filter((e) => e.score < ENTRY_POINT_EXCLUDE_THRESHOLD)
|
|
591
|
+
.filter((e) => {
|
|
592
|
+
const tier = classifyFileTier(e.file);
|
|
593
|
+
return tier === "primary" || tier === "supporting";
|
|
594
|
+
})
|
|
595
|
+
.slice(0, 2)
|
|
596
|
+
.map((e) => e.file);
|
|
597
|
+
return {
|
|
598
|
+
name,
|
|
599
|
+
purpose,
|
|
600
|
+
files,
|
|
601
|
+
businessFlow: [],
|
|
602
|
+
entryPoints,
|
|
603
|
+
searchTerms: [...new Set(terms.map((term) => term.toLowerCase()))],
|
|
604
|
+
confidence: calculateFeatureConfidence(evidence, analyses),
|
|
605
|
+
evidence
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* mergeFeature — wrapper untuk backward compat internal usage.
|
|
610
|
+
* Delegasi ke similarity-based mergeIntoFeatureList dari featureMerge.ts.
|
|
611
|
+
*
|
|
612
|
+
* Alasan tidak hapus function ini: masih dipanggil di beberapa tempat di file ini.
|
|
613
|
+
* mergeIntoFeatureList menggantikan logika lama yang pakai normalizeFeatureName.
|
|
614
|
+
*/
|
|
615
|
+
function mergeFeature(features, addition) {
|
|
616
|
+
mergeIntoFeatureList(features, addition);
|
|
617
|
+
}
|
|
618
|
+
// ---------------------------------------------------------------------------
|
|
619
|
+
// matchesSignal
|
|
620
|
+
//
|
|
621
|
+
// AI Integration: import-only by default — see ../registry/ai-providers.ts for the
|
|
622
|
+
// provider vocabulary and the fetch()-URL fallback rules.
|
|
623
|
+
//
|
|
624
|
+
// All other signals: path matching first, then import matching.
|
|
625
|
+
// ---------------------------------------------------------------------------
|
|
626
|
+
function matchesSignal(file, analysis, terms, importOnly) {
|
|
627
|
+
if (importOnly) {
|
|
628
|
+
if (analysis?.imports.some(isAiProviderImport))
|
|
629
|
+
return true;
|
|
630
|
+
if (hasAiProviderUrl(file.content))
|
|
631
|
+
return true;
|
|
632
|
+
return classifyFileRole(file.path) === "ai-integration";
|
|
633
|
+
}
|
|
634
|
+
const path = file.path.toLowerCase();
|
|
635
|
+
if (terms.some((term) => matchesPathTerm(path, term)))
|
|
636
|
+
return true;
|
|
637
|
+
if (analysis) {
|
|
638
|
+
return terms.some((term) => analysis.imports.some((specifier) => specifier.toLowerCase().includes(term)));
|
|
639
|
+
}
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
const regexCache = new Map();
|
|
643
|
+
function getOrCompilePattern(term) {
|
|
644
|
+
if (regexCache.has(term))
|
|
645
|
+
return regexCache.get(term);
|
|
646
|
+
const pattern = new RegExp(`(?:^|[/._-])${escapeRegex(term)}(?:[/._-]|$)`);
|
|
647
|
+
regexCache.set(term, pattern);
|
|
648
|
+
return pattern;
|
|
649
|
+
}
|
|
650
|
+
function matchesPathTerm(path, term) {
|
|
651
|
+
// All terms ≤7 chars use word-boundary matching to prevent partial matches.
|
|
652
|
+
//
|
|
653
|
+
// ≤3 chars: "ai" must not match "detail", "tailwind", "email"
|
|
654
|
+
// 4-7 chars: "search" must not match "SearchSurah.tsx" (component name, not a path segment)
|
|
655
|
+
// "upload" must not match "LoginUploadWidget.tsx"
|
|
656
|
+
// ≥8 chars: substring is fine — long terms are specific enough (e.g. "elasticsearch")
|
|
657
|
+
if (term.length <= 7) {
|
|
658
|
+
return getOrCompilePattern(term).test(path);
|
|
659
|
+
}
|
|
660
|
+
return path.includes(term);
|
|
661
|
+
}
|
|
662
|
+
function escapeRegex(str) {
|
|
663
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
664
|
+
}
|
|
665
|
+
// ---------------------------------------------------------------------------
|
|
666
|
+
// Authentication enrichment
|
|
667
|
+
// ---------------------------------------------------------------------------
|
|
668
|
+
function enrichAuthenticationFeature(features, files, analyses) {
|
|
669
|
+
const authFiles = collectAuthenticationFeatureFiles(files, analyses);
|
|
670
|
+
if (authFiles.length === 0)
|
|
671
|
+
return features;
|
|
672
|
+
const existingAuth = features.find((f) => f.name === "Authentication");
|
|
673
|
+
if (existingAuth) {
|
|
674
|
+
return features.map((feature) => feature.name === "Authentication"
|
|
675
|
+
? {
|
|
676
|
+
...feature,
|
|
677
|
+
files: orderAuthenticationFiles([...new Set([...feature.files, ...authFiles])]),
|
|
678
|
+
evidence: orderAuthenticationFiles([...new Set([...feature.evidence, ...authFiles])]),
|
|
679
|
+
confidence: calculateFeatureConfidence([...new Set([...feature.evidence, ...authFiles])], analyses)
|
|
680
|
+
}
|
|
681
|
+
: feature);
|
|
682
|
+
}
|
|
683
|
+
return [
|
|
684
|
+
...features,
|
|
685
|
+
createFeatureInfo("Authentication", authFiles, [
|
|
686
|
+
"auth", "authentication", "login", "session", "jwt", "next-auth"
|
|
687
|
+
], undefined, analyses)
|
|
688
|
+
];
|
|
689
|
+
}
|
|
690
|
+
function collectAuthenticationFeatureFiles(files, analyses) {
|
|
691
|
+
return orderAuthenticationFiles(files
|
|
692
|
+
.filter((file) => isArchitectureSource(file.path))
|
|
693
|
+
.filter((file) => isTechnicalFeatureSource(file.path))
|
|
694
|
+
.filter((file) => isFeatureEvidenceFile(file.path))
|
|
695
|
+
.filter((file) => !isAnalyzerImplementationFile(file.path))
|
|
696
|
+
.filter((file) => classifyFileRole(file.path) !== "ai-integration")
|
|
697
|
+
.filter((file) => {
|
|
698
|
+
const analysis = analyses[file.path];
|
|
699
|
+
const imports = analysis?.imports ?? extractImportsFallback(file.content);
|
|
700
|
+
const symbols = analysis
|
|
701
|
+
? analysis.symbols.map((s) => s.name)
|
|
702
|
+
: extractSymbolsFallback(file.content);
|
|
703
|
+
return detectAuthenticationSemanticRole(file.path, symbols, imports, file.content) !== null;
|
|
704
|
+
})
|
|
705
|
+
.map((file) => file.path));
|
|
706
|
+
}
|
|
707
|
+
function isAnalyzerImplementationFile(path) {
|
|
708
|
+
const normalized = path.toLowerCase();
|
|
709
|
+
return (/(^|\/)(analyzers?|detectors?)\//.test(normalized)
|
|
710
|
+
|| /(^|\/)[^/]+(?:analyzer|detector)\.[cm]?[jt]sx?$/.test(normalized));
|
|
711
|
+
}
|
|
712
|
+
export function detectAuthenticationSemanticRole(path, symbols, imports, content = "") {
|
|
713
|
+
const normalizedPath = path.toLowerCase();
|
|
714
|
+
const text = `${normalizedPath} ${symbols.join(" ")} ${imports.join(" ")} ${content}`.toLowerCase();
|
|
715
|
+
const normalizedImports = imports.map((s) => s.toLowerCase());
|
|
716
|
+
const hasAuthImport = normalizedImports.some((s) => /(^|[/@-])(auth|next-auth|auth0|clerk|lucia|better-auth|passport|kinde)([/.-]|$)/.test(s)
|
|
717
|
+
|| /supabase.*auth/.test(s)
|
|
718
|
+
|| /firebase\/auth/.test(s));
|
|
719
|
+
const hasAuthSymbol = symbols.some((s) => /(auth|session|login|register|signin|signout|jwt|token|credential)/i.test(s));
|
|
720
|
+
const hasAuthPath = /(^|[/._-])(auth|session|login|register|signin|signout)([/._-]|$)/.test(normalizedPath);
|
|
721
|
+
const hasGuardPath = /(^|[/._-])(guard|middleware|proxy|protected)([/._-]|$)/.test(normalizedPath);
|
|
722
|
+
const hasGuardSymbol = symbols.some((s) => /(guard|middleware|proxy|protected)/i.test(s));
|
|
723
|
+
if (/(^|\/)src\/auth\.[cm]?[jt]sx?$/.test(normalizedPath)
|
|
724
|
+
|| /(^|\/)auth\.[cm]?[jt]sx?$/.test(normalizedPath)
|
|
725
|
+
|| (hasSymbol(symbols, "auth") && hasSymbol(symbols, "handlers"))
|
|
726
|
+
|| (hasAuthImport && /\b(nextauth|getserversession|getsession|credentials|authconfig)\b/.test(text)))
|
|
727
|
+
return "auth-config";
|
|
728
|
+
if (/(^|\/)(src\/)?(proxy|middleware)\.[cm]?[jt]sx?$/.test(normalizedPath)
|
|
729
|
+
|| ((hasAuthPath || hasAuthImport || hasAuthSymbol) && (hasGuardPath || hasGuardSymbol)))
|
|
730
|
+
return "guard";
|
|
731
|
+
if (/providers?\.[cm]?[jt]sx?$/.test(normalizedPath) && (hasAuthImport || hasAuthSymbol))
|
|
732
|
+
return "provider";
|
|
733
|
+
if (/(app-shell|layout)\.[cm]?[jt]sx?$/.test(normalizedPath)
|
|
734
|
+
&& /\b(signout|handlesignout|usesession|sessionprovider|useauth)\b/.test(text))
|
|
735
|
+
return "consumer";
|
|
736
|
+
if (hasAuthPath || hasAuthImport || hasAuthSymbol)
|
|
737
|
+
return "consumer";
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
// ---------------------------------------------------------------------------
|
|
741
|
+
// File priority helpers
|
|
742
|
+
// ---------------------------------------------------------------------------
|
|
743
|
+
function featureFilePriority(featureName, path) {
|
|
744
|
+
const normalized = path.toLowerCase();
|
|
745
|
+
if (featureName === "Documentation") {
|
|
746
|
+
if (normalized === "readme.md")
|
|
747
|
+
return 0;
|
|
748
|
+
if (normalized === "contributing.md")
|
|
749
|
+
return 1;
|
|
750
|
+
if (normalized === "changelog.md")
|
|
751
|
+
return 2;
|
|
752
|
+
if (/^packages\/[^/]+\/readme\.md$/.test(normalized))
|
|
753
|
+
return 10;
|
|
754
|
+
if (/^docs\//.test(normalized))
|
|
755
|
+
return 20;
|
|
756
|
+
return 30 + normalized.split("/").length;
|
|
757
|
+
}
|
|
758
|
+
const index = FEATURE_FILE_PRIORITIES[featureName]
|
|
759
|
+
?.findIndex((pattern) => pattern.test(normalized)) ?? -1;
|
|
760
|
+
return index === -1 ? 100 : index;
|
|
761
|
+
}
|
|
762
|
+
export function orderAuthenticationFiles(files) {
|
|
763
|
+
return [...new Set(files)].sort((left, right) => authenticationFilePriority(left) - authenticationFilePriority(right)
|
|
764
|
+
|| left.localeCompare(right));
|
|
765
|
+
}
|
|
766
|
+
export function authenticationFilePriority(path) {
|
|
767
|
+
const normalized = path.toLowerCase();
|
|
768
|
+
if (/(^|\/)(src\/)?(proxy|middleware)\.[cm]?[jt]sx?$/.test(normalized))
|
|
769
|
+
return 10;
|
|
770
|
+
if (/(^|\/)(src\/)?auth\.[cm]?[jt]sx?$/.test(normalized))
|
|
771
|
+
return 20;
|
|
772
|
+
if (/\/auth\/config\.[cm]?[jt]sx?$/.test(normalized))
|
|
773
|
+
return 25;
|
|
774
|
+
if (/\/api\/auth\//.test(normalized))
|
|
775
|
+
return 30;
|
|
776
|
+
if (/\/api\/.*\/(login|register|logout)\.[cm]?[jt]s$/.test(normalized))
|
|
777
|
+
return 35;
|
|
778
|
+
if (/login.*(page|form)\.[cm]?[jt]sx?$/.test(normalized))
|
|
779
|
+
return 40;
|
|
780
|
+
if (/register.*(page|form)\.[cm]?[jt]sx?$/.test(normalized))
|
|
781
|
+
return 45;
|
|
782
|
+
if (/providers?\.[cm]?[jt]sx?$/.test(normalized))
|
|
783
|
+
return 50;
|
|
784
|
+
if (/(dashboard|app-shell|layout)\.[cm]?[jt]sx?$/.test(normalized))
|
|
785
|
+
return 60;
|
|
786
|
+
return 80;
|
|
787
|
+
}
|
|
788
|
+
// ---------------------------------------------------------------------------
|
|
789
|
+
// Fallback helpers (used when ts-morph analysis unavailable)
|
|
790
|
+
// ---------------------------------------------------------------------------
|
|
791
|
+
function extractImportsFallback(content) {
|
|
792
|
+
const imports = [];
|
|
793
|
+
const pattern = /(?:import\s+(?:[^'"]+\s+from\s+)?|require\()\s*['"]([^'"]+)['"]/g;
|
|
794
|
+
let match = pattern.exec(content);
|
|
795
|
+
while (match) {
|
|
796
|
+
imports.push(match[1].toLowerCase());
|
|
797
|
+
match = pattern.exec(content);
|
|
798
|
+
}
|
|
799
|
+
return imports;
|
|
800
|
+
}
|
|
801
|
+
function extractSymbolsFallback(content) {
|
|
802
|
+
const symbols = new Set();
|
|
803
|
+
const patterns = [
|
|
804
|
+
/export\s+(?:async\s+)?function\s+([A-Za-z_$][A-Za-z0-9_$]*)/g,
|
|
805
|
+
/export\s+const\s+([A-Za-z_$][A-Za-z0-9_$]*)/g,
|
|
806
|
+
/export\s+class\s+([A-Za-z_$][A-Za-z0-9_$]*)/g,
|
|
807
|
+
/(?:function|const)\s+([A-Za-z_$][A-Za-z0-9_$]*(?:Auth|Session|Login|Register|SignOut|Provider|Guard)[A-Za-z0-9_$]*)/g
|
|
808
|
+
];
|
|
809
|
+
for (const pattern of patterns) {
|
|
810
|
+
let match = pattern.exec(content);
|
|
811
|
+
while (match) {
|
|
812
|
+
symbols.add(match[1]);
|
|
813
|
+
match = pattern.exec(content);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return [...symbols];
|
|
817
|
+
}
|
|
818
|
+
function hasSymbol(symbols, name) {
|
|
819
|
+
return symbols.some((s) => s.toLowerCase() === name);
|
|
820
|
+
}
|
|
821
|
+
//# sourceMappingURL=featureDetector.js.map
|