@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,726 @@
|
|
|
1
|
+
import { hashContent } from "../../cache/fileHash.js";
|
|
2
|
+
import { REASON_TAGS } from "./reasonTags.js";
|
|
3
|
+
import { analyzeFiles } from "./analyzerRegistry.js";
|
|
4
|
+
import { detectProjectMetadata } from "./projectMetadata.js";
|
|
5
|
+
import { detectDatabase, detectFramework, detectFrameworks, detectRoutes, detectExternalServices, detectCapabilities, } from "../detectors/index.js";
|
|
6
|
+
import { buildDependencyGraph, countReferences, detectEntryPoints, isArchitectureSource, } from "../graph/index.js";
|
|
7
|
+
import { authenticationFilePriority, detectAuthenticationSemanticRole, detectFeatures, orderAuthenticationFiles, mergeDomainFeatures, classifyFileTier, } from "../features/index.js";
|
|
8
|
+
import { scanFiles, extractEntities, } from "../analysis/index.js";
|
|
9
|
+
import { inferDomain, buildDomainInferenceInput, domainFeaturesToFeatureInfo, } from "../inference/index.js";
|
|
10
|
+
export const SNAPSHOT_SCHEMA_VERSION = "1";
|
|
11
|
+
export async function createProjectMap(projectRoot,
|
|
12
|
+
/**
|
|
13
|
+
* Optional AI caller — kalau disediakan, domain inference (Step 5) akan dijalankan.
|
|
14
|
+
* Kalau tidak ada, hanya static analysis yang jalan.
|
|
15
|
+
* Injected biar decoupled dari provider specifics (Groq, OpenRouter, dll).
|
|
16
|
+
*/
|
|
17
|
+
callAI) {
|
|
18
|
+
const files = await scanFiles(projectRoot);
|
|
19
|
+
const analyses = await analyzeFiles(files);
|
|
20
|
+
const graph = buildDependencyGraph(files, analyses);
|
|
21
|
+
const references = countReferences(graph);
|
|
22
|
+
const detectedFramework = detectFramework(files);
|
|
23
|
+
const frameworks = detectFrameworks(files);
|
|
24
|
+
const project = detectProjectMetadata(projectRoot, detectedFramework, files, frameworks);
|
|
25
|
+
const framework = project.framework;
|
|
26
|
+
const entryPoints = detectEntryPoints(graph);
|
|
27
|
+
const routes = detectRoutes(files, project.frameworks, graph);
|
|
28
|
+
const database = detectDatabase(files);
|
|
29
|
+
// Step 1: Extract entities dari schema (Prisma dll) atau route fallback
|
|
30
|
+
const entityGraph = extractEntities(files, routes);
|
|
31
|
+
// Step 2: Detect capabilities dari route patterns + HTTP methods
|
|
32
|
+
const capabilities = detectCapabilities(routes, entityGraph);
|
|
33
|
+
// Step 3: Detect features — consume entityGraph + capabilities
|
|
34
|
+
const features = attachFeatureEntryPoints(detectFeatures(files, analyses, routes, database, entityGraph, capabilities, graph), routes, entryPoints, graph, analyses);
|
|
35
|
+
// Step 4: AI domain inference (optional — hanya jalan kalau callAI disediakan)
|
|
36
|
+
// Kirim structured metadata ke AI, dapat domain summary + domain-specific features.
|
|
37
|
+
// Kalau gagal atau callAI tidak ada, static features tetap lengkap.
|
|
38
|
+
let domain;
|
|
39
|
+
if (callAI) {
|
|
40
|
+
const inferenceInput = buildDomainInferenceInput(entityGraph, capabilities, features, framework, routes.length);
|
|
41
|
+
const result = await inferDomain(inferenceInput, callAI, projectRoot);
|
|
42
|
+
if (result) {
|
|
43
|
+
domain = result;
|
|
44
|
+
// Merge domain-specific features ke features list.
|
|
45
|
+
// Pakai similarity engine — bukan name equality — sehingga
|
|
46
|
+
// "Customizable Plans" tidak duplicate "Plan Management" yang sudah ada.
|
|
47
|
+
// Canonical name (first-seen) dipertahankan oleh mergeDomainFeatures.
|
|
48
|
+
const domainFeatures = domainFeaturesToFeatureInfo(result.domainFeatures);
|
|
49
|
+
mergeDomainFeatures(features, domainFeatures);
|
|
50
|
+
features.sort((a, b) => a.name.localeCompare(b.name));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const criticalFiles = rankCriticalFiles(files, analyses, references, entryPoints);
|
|
54
|
+
const fileIndex = Object.fromEntries(files.map((file) => [
|
|
55
|
+
file.path,
|
|
56
|
+
createFileIndexEntry(file, analyses[file.path], graph[file.path] ?? [], references, entryPoints, criticalFiles, features)
|
|
57
|
+
]));
|
|
58
|
+
const flows = generateMinimalFlows(features, fileIndex, routes, graph);
|
|
59
|
+
return {
|
|
60
|
+
version: SNAPSHOT_SCHEMA_VERSION,
|
|
61
|
+
generatedAt: new Date().toISOString(),
|
|
62
|
+
agentInstructions: createAgentInstructions(),
|
|
63
|
+
fingerprint: createProjectFingerprint(files),
|
|
64
|
+
projectRoot,
|
|
65
|
+
framework,
|
|
66
|
+
project,
|
|
67
|
+
stats: {
|
|
68
|
+
// A pre-filter filesystem count is not collected in schema v1.
|
|
69
|
+
totalFiles: files.length,
|
|
70
|
+
relevantFiles: files.length,
|
|
71
|
+
totalLines: files.reduce((sum, file) => sum + file.lines, 0)
|
|
72
|
+
},
|
|
73
|
+
entryPoints,
|
|
74
|
+
criticalFiles,
|
|
75
|
+
routes,
|
|
76
|
+
apiRoutes: routes.filter((route) => route.kind === "api"),
|
|
77
|
+
externalServices: detectExternalServices(files),
|
|
78
|
+
...(database ? { database } : {}),
|
|
79
|
+
features,
|
|
80
|
+
...(entityGraph.source !== "empty" ? { entityGraph } : {}),
|
|
81
|
+
...(capabilities.length > 0 ? { capabilities } : {}),
|
|
82
|
+
...(domain ? { domain } : {}),
|
|
83
|
+
flows,
|
|
84
|
+
onboarding: {
|
|
85
|
+
recommendedPath: buildOnboardingPath(files, entryPoints, criticalFiles, fileIndex)
|
|
86
|
+
},
|
|
87
|
+
changeImpact: buildChangeImpact(fileIndex, features, flows, graph),
|
|
88
|
+
warnings: detectAnalysisWarnings(files, entryPoints, criticalFiles, features),
|
|
89
|
+
dependencies: readPackageDependencies(files),
|
|
90
|
+
fileGraph: graph,
|
|
91
|
+
fileIndex
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function createAgentInstructions() {
|
|
95
|
+
return {
|
|
96
|
+
navigationPolicy: "index-first",
|
|
97
|
+
defaultMode: "feature-map-first",
|
|
98
|
+
maxInitialFiles: 3,
|
|
99
|
+
missingSnapshotAction: "run-devmap-analyze",
|
|
100
|
+
staleSnapshotAction: "run-devmap-analyze-fresh",
|
|
101
|
+
fallbackRule: "Read snapshot.json only when index.json and feature maps are insufficient; inspect extra source only when exact implementation is required."
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function createProjectFingerprint(files) {
|
|
105
|
+
const content = files
|
|
106
|
+
.map((file) => [file.path, hashContent(file.content)])
|
|
107
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
108
|
+
.map(([path, hash]) => `${path}:${hash}`)
|
|
109
|
+
.join("\n");
|
|
110
|
+
return hashContent(content);
|
|
111
|
+
}
|
|
112
|
+
function rankCriticalFiles(files, analyses, references, entryPoints) {
|
|
113
|
+
const entryPointSet = new Set(entryPoints);
|
|
114
|
+
return files
|
|
115
|
+
.filter((file) => isArchitectureSource(file.path) && /\.[cm]?[jt]sx?$|\.prisma$|\.vue$|\.svelte$|\.astro$/.test(file.path))
|
|
116
|
+
.filter((file) => classifyFileTier(file.path) !== "excluded")
|
|
117
|
+
.map((file) => {
|
|
118
|
+
const referencedBy = references[file.path] ?? 0;
|
|
119
|
+
const reasons = [];
|
|
120
|
+
let score = referencedBy * 3;
|
|
121
|
+
if (referencedBy > 0) {
|
|
122
|
+
reasons.push(`imported by ${referencedBy} file${referencedBy === 1 ? "" : "s"}`);
|
|
123
|
+
}
|
|
124
|
+
if (entryPointSet.has(file.path)) {
|
|
125
|
+
score += 12;
|
|
126
|
+
reasons.push("application entry point");
|
|
127
|
+
}
|
|
128
|
+
const executionBonus = calculateExecutionResponsibilityBonus(file.path);
|
|
129
|
+
if (executionBonus > 0) {
|
|
130
|
+
score += executionBonus;
|
|
131
|
+
reasons.push(REASON_TAGS.CORE_EXECUTION_RESPONSIBILITY);
|
|
132
|
+
}
|
|
133
|
+
if (/(^|\/)(types?|constants?)\.[cm]?[jt]sx?$/.test(file.path)) {
|
|
134
|
+
score = Math.max(0, score - 8);
|
|
135
|
+
}
|
|
136
|
+
if (/(^|\/)(auth|session|db|database|middleware|schema|config)([./-]|$)/i.test(file.path)) {
|
|
137
|
+
score += 3;
|
|
138
|
+
reasons.push(REASON_TAGS.CORE_PROJECT_CONCERN);
|
|
139
|
+
}
|
|
140
|
+
const semanticBonus = calculateCriticalSemanticBonus(file, analyses[file.path]);
|
|
141
|
+
if (semanticBonus > 0) {
|
|
142
|
+
score += semanticBonus;
|
|
143
|
+
reasons.push(REASON_TAGS.SEMANTIC_FEATURE_ANCHOR);
|
|
144
|
+
}
|
|
145
|
+
if (/(^|\/)(page|layout|route|server|app|main|index)\.[cm]?[jt]sx?$/.test(file.path)) {
|
|
146
|
+
score += 2;
|
|
147
|
+
reasons.push("framework convention");
|
|
148
|
+
}
|
|
149
|
+
return { path: file.path, referencedBy, score, reasons };
|
|
150
|
+
})
|
|
151
|
+
.filter((file) => file.score > 0)
|
|
152
|
+
.sort((left, right) => right.score - left.score || left.path.localeCompare(right.path))
|
|
153
|
+
.slice(0, 10);
|
|
154
|
+
}
|
|
155
|
+
function calculateExecutionResponsibilityBonus(path) {
|
|
156
|
+
return /(^|\/)(projectmap|filescanner|analyzerregistry|router|controller|orchestrator|engine)\.[cm]?[jt]sx?$/i.test(path)
|
|
157
|
+
|| /(^|\/)commands?\/[^/]+\.[cm]?[jt]sx?$/i.test(path)
|
|
158
|
+
? 16
|
|
159
|
+
: 0;
|
|
160
|
+
}
|
|
161
|
+
function readPackageDependencies(files) {
|
|
162
|
+
const packageJson = files.find((file) => file.path === "package.json");
|
|
163
|
+
if (!packageJson) {
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const parsed = JSON.parse(packageJson.content);
|
|
168
|
+
return {
|
|
169
|
+
dependencies: Object.keys(parsed.dependencies ?? {}).sort(),
|
|
170
|
+
devDependencies: Object.keys(parsed.devDependencies ?? {}).sort()
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function createFileIndexEntry(file, analysis, imports, references, entryPoints, criticalFiles, features) {
|
|
178
|
+
const topFunctions = analysis.topFunctions;
|
|
179
|
+
const exportedSymbols = analysis.exports;
|
|
180
|
+
const scope = classifyFileScope(file, exportedSymbols, imports);
|
|
181
|
+
const featureRefs = features
|
|
182
|
+
.filter((feature) => feature.files.includes(file.path) || feature.evidence.includes(file.path))
|
|
183
|
+
.map((feature) => feature.name)
|
|
184
|
+
.sort();
|
|
185
|
+
const criticalFile = criticalFiles.find((item) => item.path === file.path);
|
|
186
|
+
const importance = calculateImportance(file.path, references[file.path] ?? 0, entryPoints.includes(file.path), criticalFile?.score ?? 0, featureRefs, scope, exportedSymbols, topFunctions);
|
|
187
|
+
const searchTerms = buildFileSearchTerms(file.path, scope, exportedSymbols, topFunctions, featureRefs);
|
|
188
|
+
const purpose = inferFilePurpose(file.path, scope, featureRefs);
|
|
189
|
+
return {
|
|
190
|
+
analyzer: analysis.analyzer,
|
|
191
|
+
analysisConfidence: analysis.confidence,
|
|
192
|
+
hash: hashContent(file.content),
|
|
193
|
+
imports,
|
|
194
|
+
exportedSymbols,
|
|
195
|
+
symbols: analysis.symbols,
|
|
196
|
+
topFunctions,
|
|
197
|
+
lines: file.lines,
|
|
198
|
+
...(purpose ? { purpose } : {}),
|
|
199
|
+
scope,
|
|
200
|
+
featureRefs,
|
|
201
|
+
searchTerms,
|
|
202
|
+
importance
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function classifyFileScope(file, exportedSymbols, imports) {
|
|
206
|
+
const path = file.path.toLowerCase();
|
|
207
|
+
const name = path.split("/").at(-1) ?? path;
|
|
208
|
+
const text = `${path} ${name} ${exportedSymbols.join(" ")} ${imports.join(" ")}`.toLowerCase();
|
|
209
|
+
if (isTestFile(path))
|
|
210
|
+
return "test";
|
|
211
|
+
if (path.endsWith(".md") || path.startsWith("docs/"))
|
|
212
|
+
return "docs";
|
|
213
|
+
if (/(^|\/)(api|apis|routes?|controllers?|actions?)\//.test(path))
|
|
214
|
+
return "api";
|
|
215
|
+
if (/(^|\/)(server|app|main|index)\.[cm]?[jt]sx?$/.test(path) && imports.some((specifier) => /routes?|controllers?|api/.test(specifier)))
|
|
216
|
+
return "api";
|
|
217
|
+
if (/(^|\/)(views?|pages?|components?|screens?|templates?|layouts?)\//.test(path))
|
|
218
|
+
return "ui";
|
|
219
|
+
if (/(^|\/)(models?|entities|repositories|schemas?|migrations?|database|db|prisma)\//.test(path))
|
|
220
|
+
return "database";
|
|
221
|
+
if (/(^|\/)(commands?|cli|bin|scripts?|console)\//.test(path))
|
|
222
|
+
return "cli";
|
|
223
|
+
if (/(^|\/)(config|configs|settings|env)\//.test(path) || /(^|[.-])(config|settings|env)\./.test(name))
|
|
224
|
+
return "config";
|
|
225
|
+
if (/(^|\/)(lib|libs|shared|utils?|helpers?|services?)\//.test(path))
|
|
226
|
+
return "service";
|
|
227
|
+
if (/\b(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/.test(exportedSymbols.join(" ")))
|
|
228
|
+
return "api";
|
|
229
|
+
if (/\b(Component|View|Page|Layout|Screen)\b/i.test(exportedSymbols.join(" ")))
|
|
230
|
+
return "ui";
|
|
231
|
+
if (/\b(model|entity|repository|schema|migration|database|db)\b/.test(text))
|
|
232
|
+
return "database";
|
|
233
|
+
if (/\b(command|cli|program|argv)\b/.test(text))
|
|
234
|
+
return "cli";
|
|
235
|
+
if (/\b(service|client|provider|adapter|manager|utility|utils|helper)\b/.test(text))
|
|
236
|
+
return "service";
|
|
237
|
+
return "unknown";
|
|
238
|
+
}
|
|
239
|
+
function calculateImportance(path, referencedBy, isEntryPoint, criticalScore, featureRefs, scope, exportedSymbols, topFunctions) {
|
|
240
|
+
let importance = referencedBy * 10 + criticalScore * 5 + featureRefs.length * 8;
|
|
241
|
+
if (isEntryPoint)
|
|
242
|
+
importance += 20;
|
|
243
|
+
if (/(^|\/)(index|main|app|server|layout|page|route)\./.test(path))
|
|
244
|
+
importance += 5;
|
|
245
|
+
if (scope !== "test" && scope !== "docs") {
|
|
246
|
+
importance += calculateSemanticImportanceBonus(path, exportedSymbols, topFunctions, featureRefs);
|
|
247
|
+
}
|
|
248
|
+
return Math.min(100, importance);
|
|
249
|
+
}
|
|
250
|
+
function calculateSemanticImportanceBonus(path, exportedSymbols, topFunctions, featureRefs) {
|
|
251
|
+
const role = detectAuthenticationSemanticRole(path, [...exportedSymbols, ...topFunctions.map((item) => item.name)], []);
|
|
252
|
+
if (role === "auth-config")
|
|
253
|
+
return 70;
|
|
254
|
+
if (role === "guard")
|
|
255
|
+
return 60;
|
|
256
|
+
if (role === "provider")
|
|
257
|
+
return 45;
|
|
258
|
+
if (role === "consumer")
|
|
259
|
+
return 35;
|
|
260
|
+
if (isFeatureConfigFile(path, featureRefs))
|
|
261
|
+
return 30;
|
|
262
|
+
return featureRefs.length > 0 ? 20 : 0;
|
|
263
|
+
}
|
|
264
|
+
function calculateCriticalSemanticBonus(file, analysis) {
|
|
265
|
+
const role = detectAuthenticationSemanticRole(file.path, [...analysis.exports, ...analysis.symbols.map((item) => item.name)], analysis.imports, file.content);
|
|
266
|
+
if (role === "auth-config")
|
|
267
|
+
return 50;
|
|
268
|
+
if (role === "guard")
|
|
269
|
+
return 40;
|
|
270
|
+
if (role === "provider")
|
|
271
|
+
return 35;
|
|
272
|
+
if (role === "consumer")
|
|
273
|
+
return 25;
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
function buildFileSearchTerms(path, scope, exportedSymbols, topFunctions, featureRefs) {
|
|
277
|
+
const terms = new Set();
|
|
278
|
+
for (const part of splitSearchTerms(path)) {
|
|
279
|
+
terms.add(part);
|
|
280
|
+
}
|
|
281
|
+
if (scope !== "unknown") {
|
|
282
|
+
terms.add(scope);
|
|
283
|
+
}
|
|
284
|
+
for (const symbol of exportedSymbols) {
|
|
285
|
+
for (const part of splitSearchTerms(symbol)) {
|
|
286
|
+
terms.add(part);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
for (const item of topFunctions) {
|
|
290
|
+
for (const part of splitSearchTerms(item.name)) {
|
|
291
|
+
terms.add(part);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
for (const feature of featureRefs) {
|
|
295
|
+
for (const part of splitSearchTerms(feature)) {
|
|
296
|
+
terms.add(part);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return [...terms]
|
|
300
|
+
.filter((term) => term.length > 1 && !isVagueSearchTerm(term))
|
|
301
|
+
.slice(0, 12);
|
|
302
|
+
}
|
|
303
|
+
function inferFilePurpose(path, scope, featureRefs) {
|
|
304
|
+
if (scope === "docs" || scope === "test") {
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
const normalized = path.toLowerCase();
|
|
308
|
+
const knownPurpose = inferKnownFilePurpose(normalized);
|
|
309
|
+
if (knownPurpose) {
|
|
310
|
+
return `${path} ${knownPurpose}`;
|
|
311
|
+
}
|
|
312
|
+
if (scope === "unknown" && featureRefs.length === 0) {
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
const fileName = path.split("/").at(-1)?.replace(/\.[^.]+$/, "") ?? "file";
|
|
316
|
+
const responsibility = splitSearchTerms(fileName).join(" ") || fileName.toLowerCase();
|
|
317
|
+
const featureText = featureRefs.length > 0
|
|
318
|
+
? ` for ${featureRefs.slice(0, 2).join(" and ")}`
|
|
319
|
+
: "";
|
|
320
|
+
const scopeText = scope === "unknown" ? "project" : scope;
|
|
321
|
+
return `${path} implements ${responsibility} ${scopeText} responsibilities${featureText}.`;
|
|
322
|
+
}
|
|
323
|
+
function inferKnownFilePurpose(path) {
|
|
324
|
+
if (/\/ai\/provider\.[cm]?[jt]s$/.test(path)) {
|
|
325
|
+
return "selects the configured AI provider and resolves model routing for AI-powered commands.";
|
|
326
|
+
}
|
|
327
|
+
if (/\/ai\/groq\.[cm]?[jt]s$/.test(path)) {
|
|
328
|
+
return "implements Groq requests, retries, streaming, and provider-specific errors.";
|
|
329
|
+
}
|
|
330
|
+
if (/\/ai\/openrouter\.[cm]?[jt]s$/.test(path)) {
|
|
331
|
+
return "implements OpenRouter requests, model selection, streaming, and provider-specific errors.";
|
|
332
|
+
}
|
|
333
|
+
if (/\/ai\/contextbuilder\.[cm]?[jt]s$/.test(path)) {
|
|
334
|
+
return "selects and bounds repository context before an AI request.";
|
|
335
|
+
}
|
|
336
|
+
if (/\/ai\/prompts\.[cm]?[jt]s$/.test(path)) {
|
|
337
|
+
return "constructs grounded prompts from snapshot and retrieval context.";
|
|
338
|
+
}
|
|
339
|
+
if (/\/ai\/completion\.[cm]?[jt]s$/.test(path)) {
|
|
340
|
+
return "coordinates streaming and non-streaming AI completion output.";
|
|
341
|
+
}
|
|
342
|
+
if (/\/analyzers\/projectmap\.[cm]?[jt]s$/.test(path)) {
|
|
343
|
+
return "orchestrates scanning, analysis, feature mapping, flows, and snapshot metadata.";
|
|
344
|
+
}
|
|
345
|
+
if (/\/analyzers\/filescanner\.[cm]?[jt]s$/.test(path)) {
|
|
346
|
+
return "scans eligible project files while applying ignore and safety rules.";
|
|
347
|
+
}
|
|
348
|
+
if (/(^|\/)types?\.[cm]?[jt]s$/.test(path)) {
|
|
349
|
+
return "defines shared type contracts used by neighboring modules.";
|
|
350
|
+
}
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
function isFeatureConfigFile(path, featureRefs) {
|
|
354
|
+
return featureRefs.length > 0
|
|
355
|
+
&& /(^|\/)src\/(lib|utils)\/(config|constants)\.[cm]?[jt]sx?$/.test(path.toLowerCase());
|
|
356
|
+
}
|
|
357
|
+
function attachFeatureEntryPoints(features, routes, entryPoints, graph, analyses) {
|
|
358
|
+
return features.map((feature) => {
|
|
359
|
+
const relatedRouteEntries = routes
|
|
360
|
+
.filter((route) => feature.files.includes(route.file))
|
|
361
|
+
.map((route) => route.file);
|
|
362
|
+
const relatedEntries = [...new Set([
|
|
363
|
+
...feature.files.filter((file) => entryPoints.includes(file)),
|
|
364
|
+
...relatedRouteEntries
|
|
365
|
+
])].sort();
|
|
366
|
+
const orderedFiles = feature.name === "Authentication"
|
|
367
|
+
? orderAuthenticationFiles(feature.files)
|
|
368
|
+
: feature.files;
|
|
369
|
+
const entryPoint = chooseFeatureEntryPoint(feature.name, orderedFiles, relatedEntries, routes, graph);
|
|
370
|
+
const businessFlow = buildFeatureBusinessFlow(feature.name, entryPoint, graph, orderedFiles);
|
|
371
|
+
const hasHighQualityEvidence = analyses
|
|
372
|
+
? feature.evidence.some((path) => analyses[path]?.confidence === "high")
|
|
373
|
+
: false;
|
|
374
|
+
return {
|
|
375
|
+
...feature,
|
|
376
|
+
files: orderedFiles,
|
|
377
|
+
...(entryPoint ? { entryPoint } : {}),
|
|
378
|
+
entryPoints: relatedEntries,
|
|
379
|
+
businessFlow,
|
|
380
|
+
confidence: hasHighQualityEvidence && (feature.files.length >= 2 || relatedEntries.length > 0)
|
|
381
|
+
? "high"
|
|
382
|
+
: feature.confidence
|
|
383
|
+
};
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
function chooseFeatureEntryPoint(featureName, files, relatedEntries, routes, graph) {
|
|
387
|
+
if (featureName === "Authentication") {
|
|
388
|
+
const apiEntry = relatedEntries.find((file) => /(^|\/)api\//.test(file));
|
|
389
|
+
if (apiEntry) {
|
|
390
|
+
return apiEntry;
|
|
391
|
+
}
|
|
392
|
+
const authConfig = files.find((file) => authenticationFilePriority(file) === 20);
|
|
393
|
+
if (authConfig) {
|
|
394
|
+
return authConfig;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (relatedEntries.length > 0) {
|
|
398
|
+
return relatedEntries[0];
|
|
399
|
+
}
|
|
400
|
+
const route = routes.find((candidate) => files.includes(candidate.file)
|
|
401
|
+
|| (graph[candidate.file] ?? []).some((dependency) => files.includes(dependency)));
|
|
402
|
+
if (route) {
|
|
403
|
+
return route.file;
|
|
404
|
+
}
|
|
405
|
+
return files[0];
|
|
406
|
+
}
|
|
407
|
+
function buildFeatureBusinessFlow(featureName, entryPoint, graph, featureFiles) {
|
|
408
|
+
const structuralFlow = buildStructuralFeatureFlow(featureName, Object.keys(graph));
|
|
409
|
+
if (structuralFlow.length >= 3) {
|
|
410
|
+
return structuralFlow;
|
|
411
|
+
}
|
|
412
|
+
if (featureName === "Authentication" && featureFiles.length > 0) {
|
|
413
|
+
const orderedFiles = entryPoint
|
|
414
|
+
? [entryPoint, ...featureFiles.filter((file) => file !== entryPoint)]
|
|
415
|
+
: featureFiles;
|
|
416
|
+
return orderedFiles.slice(0, 8).map((file) => file === entryPoint && /(^|\/)api\//.test(file)
|
|
417
|
+
? `Start at ${file}.`
|
|
418
|
+
: describeAuthenticationFlowStep(file, graph[file] ?? []));
|
|
419
|
+
}
|
|
420
|
+
if (!entryPoint) {
|
|
421
|
+
return [`Identify files related to ${featureName}.`];
|
|
422
|
+
}
|
|
423
|
+
const chain = collectFlowFiles(entryPoint, graph);
|
|
424
|
+
const steps = [`Start at ${entryPoint}.`];
|
|
425
|
+
for (const file of chain.slice(1, 4)) {
|
|
426
|
+
steps.push(`Follow dependency ${file}.`);
|
|
427
|
+
}
|
|
428
|
+
return steps;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* buildStructuralFeatureFlow — build human-readable flow steps untuk sebuah feature.
|
|
432
|
+
*
|
|
433
|
+
* Generic by design — bekerja untuk project apapun berdasarkan:
|
|
434
|
+
* 1. FileRole classification — detect architectural layers dari file paths
|
|
435
|
+
* 2. Naming conventions — common patterns yang berlaku cross-domain
|
|
436
|
+
*
|
|
437
|
+
* Sebelumnya hardcode DevMap-specific paths (Analysis Engine, Snapshot Engine).
|
|
438
|
+
* Sekarang derive flow dari actual files yang ditemukan di project.
|
|
439
|
+
*/
|
|
440
|
+
function buildStructuralFeatureFlow(featureName, files) {
|
|
441
|
+
const find = (...patterns) => files.find((file) => patterns.some((p) => p.test(file.toLowerCase())));
|
|
442
|
+
const findAll = (...patterns) => files.filter((file) => patterns.some((p) => p.test(file.toLowerCase())));
|
|
443
|
+
const steps = [];
|
|
444
|
+
// --- Authentication flow (special case, high value) ---
|
|
445
|
+
if (featureName === "Authentication") {
|
|
446
|
+
steps.push(["Guard protected routes", find(/middleware\.[cm]?[jt]sx?$/, /proxy\.[cm]?[jt]sx?$/)], ["Configure auth provider", find(/\/auth\.[cm]?[jt]sx?$/, /\/auth\/config/)], ["Handle login", find(/login.*route\.[cm]?[jt]s$/, /\/api\/auth\/.*sign/)], ["Handle registration", find(/register.*route\.[cm]?[jt]s$/, /\/api\/auth\/register/)], ["Expose session context", find(/provider.*\.[cm]?[jt]sx?$/, /session.*provider/)]);
|
|
447
|
+
}
|
|
448
|
+
// --- API layer flow ---
|
|
449
|
+
else if (featureName === "API Layer") {
|
|
450
|
+
steps.push(["Receive and validate request", find(/\/(routes?|controllers?|handlers?)\//)], ["Apply business logic", find(/\/(services?|usecases?|domain)\//)], ["Access data layer", find(/\/(repositories?|lib\/prisma|lib\/db)/)], ["Return response", find(/\/(routes?|controllers?|handlers?)\//)]);
|
|
451
|
+
}
|
|
452
|
+
// --- Service layer flow ---
|
|
453
|
+
else if (featureName === "Service Layer") {
|
|
454
|
+
steps.push(["Entry via service interface", find(/\/(services?|usecases?)\//)], ["Validate business rules", find(/\/(validators?|schemas?|dto)\//)], ["Persist via data layer", find(/\/(repositories?|lib\/prisma|lib\/db)/)]);
|
|
455
|
+
}
|
|
456
|
+
// --- CLI Commands flow ---
|
|
457
|
+
else if (featureName === "CLI Commands") {
|
|
458
|
+
steps.push(["Parse and dispatch command", find(/\/(src\/)?index\.[cm]?[jt]s$/, /\/bin\//)], ["Execute command handler", find(/\/(commands?|cli)\//)], ["Render output", find(/\/(output|reporter|formatter|render)\.[cm]?[jt]s$/)]);
|
|
459
|
+
}
|
|
460
|
+
// --- AI Integration flow ---
|
|
461
|
+
else if (featureName === "AI Integration") {
|
|
462
|
+
steps.push(["Build context", find(/\/(context|contextbuilder)\.[cm]?[jt]s$/)], ["Construct prompt", find(/\/(prompt|prompts)\.[cm]?[jt]s$/)], ["Call AI provider", findAll(/\/(openai|groq|openrouter|anthropic|gemini|provider)\.[cm]?[jt]s$/).join(" and ") || undefined], ["Process completion", find(/\/(completion|response|stream)\.[cm]?[jt]s$/)]);
|
|
463
|
+
}
|
|
464
|
+
// --- Generic CRUD flow — berlaku buat Snippet Management, Order Management, dll ---
|
|
465
|
+
else {
|
|
466
|
+
// Extract entity name dari feature name (e.g. "Snippet Management" → "snippet")
|
|
467
|
+
const entitySlug = featureName
|
|
468
|
+
.toLowerCase()
|
|
469
|
+
.replace(/\s+(management|system|module|feature)$/, "")
|
|
470
|
+
.replace(/\s+/g, "[-_]?");
|
|
471
|
+
const entityPattern = new RegExp(`\/${entitySlug}`, "i");
|
|
472
|
+
steps.push(["Handle API request", find(entityPattern, /\/(routes?|controllers?|api)\//i)], ["Apply business logic", find(/\/(services?|usecases?|actions?)\//i)], ["Access data", find(/\/(repositories?|lib\/prisma|db\/)\//i)]);
|
|
473
|
+
}
|
|
474
|
+
return steps
|
|
475
|
+
.filter((step) => Boolean(step[1]))
|
|
476
|
+
.map(([action, file]) => `${action} in ${file}.`);
|
|
477
|
+
}
|
|
478
|
+
function describeAuthenticationFlowStep(file, dependencies) {
|
|
479
|
+
const normalized = file.toLowerCase();
|
|
480
|
+
const dependencyText = dependencies.length > 0
|
|
481
|
+
? ` and connects to ${dependencies.slice(0, 2).join(", ")}`
|
|
482
|
+
: "";
|
|
483
|
+
if (/(^|\/)(src\/)?(proxy|middleware)\.[cm]?[jt]sx?$/.test(normalized)) {
|
|
484
|
+
return `Guard requests in ${file} by checking authentication state before protected routes${dependencyText}.`;
|
|
485
|
+
}
|
|
486
|
+
if (/(^|\/)(src\/)?auth\.[cm]?[jt]sx?$/.test(normalized)) {
|
|
487
|
+
return `Configure authentication in ${file}, including providers, session/JWT callbacks, and shared auth helpers${dependencyText}.`;
|
|
488
|
+
}
|
|
489
|
+
if (/register.*\/route\.[cm]?[jt]s$|\/api\/.*register/.test(normalized)) {
|
|
490
|
+
return `Handle registration in ${file}, validating new users before creating credentials${dependencyText}.`;
|
|
491
|
+
}
|
|
492
|
+
if (/login.*(page|form)\.[cm]?[jt]sx?$/.test(normalized)) {
|
|
493
|
+
return `Render login UI in ${file} and submit credentials to the auth provider${dependencyText}.`;
|
|
494
|
+
}
|
|
495
|
+
if (/register.*(page|form)\.[cm]?[jt]sx?$/.test(normalized)) {
|
|
496
|
+
return `Render registration UI in ${file} and collect account creation details${dependencyText}.`;
|
|
497
|
+
}
|
|
498
|
+
if (/providers?\.[cm]?[jt]sx?$/.test(normalized)) {
|
|
499
|
+
return `Expose session context in ${file} so client components can read authentication state${dependencyText}.`;
|
|
500
|
+
}
|
|
501
|
+
if (/(app-shell|layout)\.[cm]?[jt]sx?$/.test(normalized)) {
|
|
502
|
+
return `Consume session state in ${file} for authenticated layouts, user navigation, or sign-out behavior${dependencyText}.`;
|
|
503
|
+
}
|
|
504
|
+
return `Review authentication-related behavior in ${file}${dependencyText}.`;
|
|
505
|
+
}
|
|
506
|
+
function generateMinimalFlows(features, fileIndex, routes, graph) {
|
|
507
|
+
return [
|
|
508
|
+
...generateFeatureFlows(features, fileIndex),
|
|
509
|
+
...generateRequestFlows(routes, fileIndex, graph)
|
|
510
|
+
];
|
|
511
|
+
}
|
|
512
|
+
export function generateFeatureFlows(features, fileIndex, options = {}) {
|
|
513
|
+
const limit = options.limit ?? 3;
|
|
514
|
+
const minConfidence = options.minConfidence ?? "high";
|
|
515
|
+
return features
|
|
516
|
+
.filter((feature) => feature.confidence === minConfidence
|
|
517
|
+
&& feature.businessFlow.length > 1
|
|
518
|
+
&& !feature.businessFlow.some((step) => /^Identify files related to /i.test(step)))
|
|
519
|
+
.slice(0, limit)
|
|
520
|
+
.map((feature) => {
|
|
521
|
+
const candidateFiles = [...new Set([
|
|
522
|
+
...(feature.entryPoint ? [feature.entryPoint] : []),
|
|
523
|
+
...feature.entryPoints,
|
|
524
|
+
...feature.files
|
|
525
|
+
])];
|
|
526
|
+
const steps = feature.businessFlow.slice(0, 6).map((label) => {
|
|
527
|
+
const file = candidateFiles.find((candidate) => label.includes(candidate));
|
|
528
|
+
return {
|
|
529
|
+
label,
|
|
530
|
+
...(file ? { file, purpose: fileIndex[file]?.purpose } : {})
|
|
531
|
+
};
|
|
532
|
+
});
|
|
533
|
+
return {
|
|
534
|
+
name: `${feature.name} flow`,
|
|
535
|
+
purpose: `Describes the inferred behavior for ${feature.name.toLowerCase()}.`,
|
|
536
|
+
type: "feature",
|
|
537
|
+
...(feature.entryPoint ? { entryPoint: feature.entryPoint } : {}),
|
|
538
|
+
steps,
|
|
539
|
+
...(steps.length > 3 ? { mermaid: renderMermaidFlow(steps) } : {}),
|
|
540
|
+
confidence: feature.confidence === "high" ? "high" : "medium"
|
|
541
|
+
};
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
export function generateRequestFlows(routes, fileIndex, graph, options = {}) {
|
|
545
|
+
const limit = options.limit ?? 5;
|
|
546
|
+
const includeAllRouteKinds = options.includeAllRouteKinds ?? false;
|
|
547
|
+
return routes
|
|
548
|
+
.filter((route) => route.kind === "api" || includeAllRouteKinds)
|
|
549
|
+
.slice(0, limit)
|
|
550
|
+
.map((route) => {
|
|
551
|
+
const files = collectFlowFiles(route.file, graph, fileIndex);
|
|
552
|
+
const steps = files.map((file, index) => ({
|
|
553
|
+
label: renderFlowStepLabel(file, fileIndex[file], index === 0),
|
|
554
|
+
file,
|
|
555
|
+
purpose: fileIndex[file]?.purpose
|
|
556
|
+
}));
|
|
557
|
+
return {
|
|
558
|
+
name: `Request ${route.path}`,
|
|
559
|
+
purpose: `Shows the main files involved in the ${route.path} request path.`,
|
|
560
|
+
type: "request",
|
|
561
|
+
entryPoint: route.file,
|
|
562
|
+
steps,
|
|
563
|
+
...(steps.length > 2 ? { mermaid: renderMermaidFlow(steps) } : {}),
|
|
564
|
+
confidence: steps.length > 1 ? "high" : "medium"
|
|
565
|
+
};
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
function collectFlowFiles(entryFile, graph, fileIndex) {
|
|
569
|
+
const files = [];
|
|
570
|
+
const visited = new Set();
|
|
571
|
+
const queue = [entryFile];
|
|
572
|
+
while (queue.length > 0 && files.length < 5) {
|
|
573
|
+
const file = queue.shift();
|
|
574
|
+
if (!file || visited.has(file) || (fileIndex && !fileIndex[file])) {
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
visited.add(file);
|
|
578
|
+
files.push(file);
|
|
579
|
+
for (const next of graph[file] ?? []) {
|
|
580
|
+
if (!visited.has(next) && (!fileIndex || fileIndex[next])) {
|
|
581
|
+
queue.push(next);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return files;
|
|
586
|
+
}
|
|
587
|
+
function buildOnboardingPath(files, entryPoints, criticalFiles, fileIndex) {
|
|
588
|
+
const availableFiles = new Set(files.map((file) => file.path));
|
|
589
|
+
const path = new Set();
|
|
590
|
+
for (const candidate of ["README.md", "readme.md", "AGENTS.md", "DEVMAP.md", "package.json"]) {
|
|
591
|
+
if (availableFiles.has(candidate)) {
|
|
592
|
+
path.add(candidate);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
for (const entryPoint of entryPoints) {
|
|
596
|
+
path.add(entryPoint);
|
|
597
|
+
}
|
|
598
|
+
for (const file of criticalFiles.map((item) => item.path)) {
|
|
599
|
+
path.add(file);
|
|
600
|
+
}
|
|
601
|
+
for (const [file, metadata] of Object.entries(fileIndex)
|
|
602
|
+
.sort(([, left], [, right]) => right.importance - left.importance)) {
|
|
603
|
+
if (metadata.scope !== "test" && metadata.scope !== "docs") {
|
|
604
|
+
path.add(file);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return [...path].slice(0, 12);
|
|
608
|
+
}
|
|
609
|
+
function buildChangeImpact(fileIndex, features, flows, graph) {
|
|
610
|
+
const reverseDependencies = buildReverseDependencies(graph);
|
|
611
|
+
const impacts = {};
|
|
612
|
+
for (const file of Object.keys(fileIndex)) {
|
|
613
|
+
const impactedFeatures = features
|
|
614
|
+
.filter((feature) => feature.files.includes(file) || feature.entryPoint === file)
|
|
615
|
+
.map((feature) => feature.name);
|
|
616
|
+
const impactedFlows = flows
|
|
617
|
+
.filter((flow) => flow.steps.some((step) => step.file === file))
|
|
618
|
+
.map((flow) => flow.name);
|
|
619
|
+
const dependents = reverseDependencies[file] ?? [];
|
|
620
|
+
const impactNames = [...new Set([...impactedFeatures, ...impactedFlows])].sort();
|
|
621
|
+
if (impactNames.length > 0 || dependents.length > 0) {
|
|
622
|
+
impacts[file] = {
|
|
623
|
+
impacts: impactNames,
|
|
624
|
+
dependents
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return impacts;
|
|
629
|
+
}
|
|
630
|
+
function buildReverseDependencies(graph) {
|
|
631
|
+
const reverse = {};
|
|
632
|
+
for (const [file, dependencies] of Object.entries(graph)) {
|
|
633
|
+
for (const dependency of dependencies) {
|
|
634
|
+
reverse[dependency] ??= [];
|
|
635
|
+
reverse[dependency].push(file);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
for (const dependents of Object.values(reverse)) {
|
|
639
|
+
dependents.sort();
|
|
640
|
+
}
|
|
641
|
+
return reverse;
|
|
642
|
+
}
|
|
643
|
+
function renderFlowStepLabel(file, metadata, isFirstStep) {
|
|
644
|
+
const prefix = isFirstStep ? "Start with" : "Review";
|
|
645
|
+
const symbols = metadata?.topFunctions
|
|
646
|
+
.filter((item) => item.exported)
|
|
647
|
+
.map((item) => item.name)
|
|
648
|
+
.slice(0, 2) ?? [];
|
|
649
|
+
const symbolText = symbols.length > 0 ? ` (${symbols.join(", ")})` : "";
|
|
650
|
+
return `${prefix} ${file}${symbolText}`;
|
|
651
|
+
}
|
|
652
|
+
export function renderMermaidFlow(steps) {
|
|
653
|
+
const lines = ["graph TD"];
|
|
654
|
+
steps.forEach((step, index) => {
|
|
655
|
+
const node = `S${index + 1}`;
|
|
656
|
+
lines.push(` ${node}["${escapeMermaidLabel(step.file ?? step.label)}"]`);
|
|
657
|
+
if (index > 0) {
|
|
658
|
+
lines.push(` S${index} --> ${node}`);
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
return lines.join("\n");
|
|
662
|
+
}
|
|
663
|
+
function escapeMermaidLabel(value) {
|
|
664
|
+
return value.replace(/"/g, "'");
|
|
665
|
+
}
|
|
666
|
+
function isTestFile(path) {
|
|
667
|
+
const normalized = path.toLowerCase();
|
|
668
|
+
return (normalized.includes("/test/")
|
|
669
|
+
|| normalized.includes("/tests/")
|
|
670
|
+
|| normalized.includes("/__tests__/")
|
|
671
|
+
|| normalized.includes("/fixtures/")
|
|
672
|
+
|| normalized.endsWith(".test.ts")
|
|
673
|
+
|| normalized.endsWith(".test.tsx")
|
|
674
|
+
|| normalized.endsWith(".test.js")
|
|
675
|
+
|| normalized.endsWith(".test.jsx")
|
|
676
|
+
|| normalized.endsWith(".spec.ts")
|
|
677
|
+
|| normalized.endsWith(".spec.tsx")
|
|
678
|
+
|| normalized.endsWith(".spec.js")
|
|
679
|
+
|| normalized.endsWith(".spec.jsx"));
|
|
680
|
+
}
|
|
681
|
+
function isVagueSearchTerm(term) {
|
|
682
|
+
return new Set([
|
|
683
|
+
"app",
|
|
684
|
+
"data",
|
|
685
|
+
"feature",
|
|
686
|
+
"file",
|
|
687
|
+
"handler",
|
|
688
|
+
"logic",
|
|
689
|
+
"page",
|
|
690
|
+
"service",
|
|
691
|
+
"system"
|
|
692
|
+
]).has(term);
|
|
693
|
+
}
|
|
694
|
+
function detectAnalysisWarnings(files, entryPoints, criticalFiles, features) {
|
|
695
|
+
const warnings = [];
|
|
696
|
+
if (entryPoints.length === 0) {
|
|
697
|
+
warnings.push("No entry points detected. The project structure may not be fully recognized.");
|
|
698
|
+
}
|
|
699
|
+
if (criticalFiles.length === 0) {
|
|
700
|
+
warnings.push("No critical files detected. Dependency analysis may be incomplete.");
|
|
701
|
+
}
|
|
702
|
+
if (features.length === 0) {
|
|
703
|
+
warnings.push("No features detected. Feature-based navigation may be unavailable.");
|
|
704
|
+
}
|
|
705
|
+
const packageJson = files.find((file) => file.path === "package.json");
|
|
706
|
+
if (!packageJson) {
|
|
707
|
+
return warnings;
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
JSON.parse(packageJson.content);
|
|
711
|
+
}
|
|
712
|
+
catch {
|
|
713
|
+
warnings.push("package.json could not be parsed. Dependency-based detection may be incomplete.");
|
|
714
|
+
}
|
|
715
|
+
return warnings;
|
|
716
|
+
}
|
|
717
|
+
function splitSearchTerms(value) {
|
|
718
|
+
const spaced = value
|
|
719
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
720
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2");
|
|
721
|
+
return spaced
|
|
722
|
+
.toLowerCase()
|
|
723
|
+
.split(/[^a-z0-9]+/)
|
|
724
|
+
.filter(Boolean);
|
|
725
|
+
}
|
|
726
|
+
//# sourceMappingURL=projectMap.js.map
|