@opensip-tools/fitness 1.0.4
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/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/LICENSE +21 -0
- package/dist/__tests__/gate.test.d.ts +13 -0
- package/dist/__tests__/gate.test.d.ts.map +1 -0
- package/dist/__tests__/gate.test.js +422 -0
- package/dist/__tests__/gate.test.js.map +1 -0
- package/dist/__tests__/sarif.test.d.ts +2 -0
- package/dist/__tests__/sarif.test.d.ts.map +1 -0
- package/dist/__tests__/sarif.test.js +169 -0
- package/dist/__tests__/sarif.test.js.map +1 -0
- package/dist/cli/dashboard.d.ts +6 -0
- package/dist/cli/dashboard.d.ts.map +1 -0
- package/dist/cli/dashboard.js +77 -0
- package/dist/cli/dashboard.js.map +1 -0
- package/dist/cli/fit.d.ts +37 -0
- package/dist/cli/fit.d.ts.map +1 -0
- package/dist/cli/fit.js +539 -0
- package/dist/cli/fit.js.map +1 -0
- package/dist/cli/list-checks.d.ts +6 -0
- package/dist/cli/list-checks.d.ts.map +1 -0
- package/dist/cli/list-checks.js +23 -0
- package/dist/cli/list-checks.js.map +1 -0
- package/dist/cli/list-recipes.d.ts +6 -0
- package/dist/cli/list-recipes.d.ts.map +1 -0
- package/dist/cli/list-recipes.js +31 -0
- package/dist/cli/list-recipes.js.map +1 -0
- package/dist/framework/__tests__/ast-utilities.test.d.ts +2 -0
- package/dist/framework/__tests__/ast-utilities.test.d.ts.map +1 -0
- package/dist/framework/__tests__/ast-utilities.test.js +153 -0
- package/dist/framework/__tests__/ast-utilities.test.js.map +1 -0
- package/dist/framework/__tests__/check-config.test.d.ts +2 -0
- package/dist/framework/__tests__/check-config.test.d.ts.map +1 -0
- package/dist/framework/__tests__/check-config.test.js +56 -0
- package/dist/framework/__tests__/check-config.test.js.map +1 -0
- package/dist/framework/__tests__/command-executor.test.d.ts +2 -0
- package/dist/framework/__tests__/command-executor.test.d.ts.map +1 -0
- package/dist/framework/__tests__/command-executor.test.js +71 -0
- package/dist/framework/__tests__/command-executor.test.js.map +1 -0
- package/dist/framework/__tests__/content-filter-dispatch.test.d.ts +2 -0
- package/dist/framework/__tests__/content-filter-dispatch.test.d.ts.map +1 -0
- package/dist/framework/__tests__/content-filter-dispatch.test.js +104 -0
- package/dist/framework/__tests__/content-filter-dispatch.test.js.map +1 -0
- package/dist/framework/__tests__/content-filter.test.d.ts +2 -0
- package/dist/framework/__tests__/content-filter.test.d.ts.map +1 -0
- package/dist/framework/__tests__/content-filter.test.js +126 -0
- package/dist/framework/__tests__/content-filter.test.js.map +1 -0
- package/dist/framework/__tests__/define-check.test.d.ts +2 -0
- package/dist/framework/__tests__/define-check.test.d.ts.map +1 -0
- package/dist/framework/__tests__/define-check.test.js +155 -0
- package/dist/framework/__tests__/define-check.test.js.map +1 -0
- package/dist/framework/__tests__/directive-inventory.test.d.ts +2 -0
- package/dist/framework/__tests__/directive-inventory.test.d.ts.map +1 -0
- package/dist/framework/__tests__/directive-inventory.test.js +44 -0
- package/dist/framework/__tests__/directive-inventory.test.js.map +1 -0
- package/dist/framework/__tests__/execution-context.test.d.ts +2 -0
- package/dist/framework/__tests__/execution-context.test.d.ts.map +1 -0
- package/dist/framework/__tests__/execution-context.test.js +62 -0
- package/dist/framework/__tests__/execution-context.test.js.map +1 -0
- package/dist/framework/__tests__/file-accessor.test.d.ts +2 -0
- package/dist/framework/__tests__/file-accessor.test.d.ts.map +1 -0
- package/dist/framework/__tests__/file-accessor.test.js +106 -0
- package/dist/framework/__tests__/file-accessor.test.js.map +1 -0
- package/dist/framework/__tests__/file-cache.test.d.ts +2 -0
- package/dist/framework/__tests__/file-cache.test.d.ts.map +1 -0
- package/dist/framework/__tests__/file-cache.test.js +122 -0
- package/dist/framework/__tests__/file-cache.test.js.map +1 -0
- package/dist/framework/__tests__/import-graph.test.d.ts +15 -0
- package/dist/framework/__tests__/import-graph.test.d.ts.map +1 -0
- package/dist/framework/__tests__/import-graph.test.js +164 -0
- package/dist/framework/__tests__/import-graph.test.js.map +1 -0
- package/dist/framework/__tests__/path-matcher.test.d.ts +2 -0
- package/dist/framework/__tests__/path-matcher.test.d.ts.map +1 -0
- package/dist/framework/__tests__/path-matcher.test.js +113 -0
- package/dist/framework/__tests__/path-matcher.test.js.map +1 -0
- package/dist/framework/__tests__/register-helpers.test.d.ts +2 -0
- package/dist/framework/__tests__/register-helpers.test.d.ts.map +1 -0
- package/dist/framework/__tests__/register-helpers.test.js +42 -0
- package/dist/framework/__tests__/register-helpers.test.js.map +1 -0
- package/dist/framework/__tests__/registry.test.d.ts +2 -0
- package/dist/framework/__tests__/registry.test.d.ts.map +1 -0
- package/dist/framework/__tests__/registry.test.js +208 -0
- package/dist/framework/__tests__/registry.test.js.map +1 -0
- package/dist/framework/__tests__/result-builder.test.d.ts +2 -0
- package/dist/framework/__tests__/result-builder.test.d.ts.map +1 -0
- package/dist/framework/__tests__/result-builder.test.js +153 -0
- package/dist/framework/__tests__/result-builder.test.js.map +1 -0
- package/dist/framework/__tests__/scope-resolver.test.d.ts +2 -0
- package/dist/framework/__tests__/scope-resolver.test.d.ts.map +1 -0
- package/dist/framework/__tests__/scope-resolver.test.js +140 -0
- package/dist/framework/__tests__/scope-resolver.test.js.map +1 -0
- package/dist/framework/__tests__/severity-mapping.test.d.ts +2 -0
- package/dist/framework/__tests__/severity-mapping.test.d.ts.map +1 -0
- package/dist/framework/__tests__/severity-mapping.test.js +42 -0
- package/dist/framework/__tests__/severity-mapping.test.js.map +1 -0
- package/dist/framework/__tests__/strip-literals.test.d.ts +2 -0
- package/dist/framework/__tests__/strip-literals.test.d.ts.map +1 -0
- package/dist/framework/__tests__/strip-literals.test.js +87 -0
- package/dist/framework/__tests__/strip-literals.test.js.map +1 -0
- package/dist/framework/abortable-exec.d.ts +34 -0
- package/dist/framework/abortable-exec.d.ts.map +1 -0
- package/dist/framework/abortable-exec.js +136 -0
- package/dist/framework/abortable-exec.js.map +1 -0
- package/dist/framework/ast-utilities.d.ts +41 -0
- package/dist/framework/ast-utilities.d.ts.map +1 -0
- package/dist/framework/ast-utilities.js +106 -0
- package/dist/framework/ast-utilities.js.map +1 -0
- package/dist/framework/check-config.d.ts +171 -0
- package/dist/framework/check-config.d.ts.map +1 -0
- package/dist/framework/check-config.js +114 -0
- package/dist/framework/check-config.js.map +1 -0
- package/dist/framework/check-types.d.ts +57 -0
- package/dist/framework/check-types.d.ts.map +1 -0
- package/dist/framework/check-types.js +35 -0
- package/dist/framework/check-types.js.map +1 -0
- package/dist/framework/command-executor.d.ts +25 -0
- package/dist/framework/command-executor.d.ts.map +1 -0
- package/dist/framework/command-executor.js +63 -0
- package/dist/framework/command-executor.js.map +1 -0
- package/dist/framework/constants.d.ts +9 -0
- package/dist/framework/constants.d.ts.map +1 -0
- package/dist/framework/constants.js +16 -0
- package/dist/framework/constants.js.map +1 -0
- package/dist/framework/content-filter.d.ts +33 -0
- package/dist/framework/content-filter.d.ts.map +1 -0
- package/dist/framework/content-filter.js +236 -0
- package/dist/framework/content-filter.js.map +1 -0
- package/dist/framework/define-check.d.ts +38 -0
- package/dist/framework/define-check.d.ts.map +1 -0
- package/dist/framework/define-check.js +252 -0
- package/dist/framework/define-check.js.map +1 -0
- package/dist/framework/directive-inventory.d.ts +34 -0
- package/dist/framework/directive-inventory.d.ts.map +1 -0
- package/dist/framework/directive-inventory.js +77 -0
- package/dist/framework/directive-inventory.js.map +1 -0
- package/dist/framework/directive-parsing.d.ts +20 -0
- package/dist/framework/directive-parsing.d.ts.map +1 -0
- package/dist/framework/directive-parsing.js +121 -0
- package/dist/framework/directive-parsing.js.map +1 -0
- package/dist/framework/execution-context.d.ts +95 -0
- package/dist/framework/execution-context.d.ts.map +1 -0
- package/dist/framework/execution-context.js +122 -0
- package/dist/framework/execution-context.js.map +1 -0
- package/dist/framework/file-accessor.d.ts +20 -0
- package/dist/framework/file-accessor.d.ts.map +1 -0
- package/dist/framework/file-accessor.js +122 -0
- package/dist/framework/file-accessor.js.map +1 -0
- package/dist/framework/file-cache.d.ts +70 -0
- package/dist/framework/file-cache.d.ts.map +1 -0
- package/dist/framework/file-cache.js +178 -0
- package/dist/framework/file-cache.js.map +1 -0
- package/dist/framework/file-type-filter.d.ts +11 -0
- package/dist/framework/file-type-filter.d.ts.map +1 -0
- package/dist/framework/file-type-filter.js +21 -0
- package/dist/framework/file-type-filter.js.map +1 -0
- package/dist/framework/ignore-processing.d.ts +22 -0
- package/dist/framework/ignore-processing.d.ts.map +1 -0
- package/dist/framework/ignore-processing.js +241 -0
- package/dist/framework/ignore-processing.js.map +1 -0
- package/dist/framework/import-graph.d.ts +51 -0
- package/dist/framework/import-graph.d.ts.map +1 -0
- package/dist/framework/import-graph.js +216 -0
- package/dist/framework/import-graph.js.map +1 -0
- package/dist/framework/memory-profiler.d.ts +53 -0
- package/dist/framework/memory-profiler.d.ts.map +1 -0
- package/dist/framework/memory-profiler.js +92 -0
- package/dist/framework/memory-profiler.js.map +1 -0
- package/dist/framework/parse-cache.d.ts +23 -0
- package/dist/framework/parse-cache.d.ts.map +1 -0
- package/dist/framework/parse-cache.js +37 -0
- package/dist/framework/parse-cache.js.map +1 -0
- package/dist/framework/path-matcher.d.ts +86 -0
- package/dist/framework/path-matcher.d.ts.map +1 -0
- package/dist/framework/path-matcher.js +138 -0
- package/dist/framework/path-matcher.js.map +1 -0
- package/dist/framework/register-helpers.d.ts +10 -0
- package/dist/framework/register-helpers.d.ts.map +1 -0
- package/dist/framework/register-helpers.js +17 -0
- package/dist/framework/register-helpers.js.map +1 -0
- package/dist/framework/registry.d.ts +41 -0
- package/dist/framework/registry.d.ts.map +1 -0
- package/dist/framework/registry.js +103 -0
- package/dist/framework/registry.js.map +1 -0
- package/dist/framework/result-builder.d.ts +74 -0
- package/dist/framework/result-builder.d.ts.map +1 -0
- package/dist/framework/result-builder.js +154 -0
- package/dist/framework/result-builder.js.map +1 -0
- package/dist/framework/scope-resolver.d.ts +23 -0
- package/dist/framework/scope-resolver.d.ts.map +1 -0
- package/dist/framework/scope-resolver.js +201 -0
- package/dist/framework/scope-resolver.js.map +1 -0
- package/dist/framework/severity-mapping.d.ts +13 -0
- package/dist/framework/severity-mapping.d.ts.map +1 -0
- package/dist/framework/severity-mapping.js +51 -0
- package/dist/framework/severity-mapping.js.map +1 -0
- package/dist/framework/strip-literals.d.ts +48 -0
- package/dist/framework/strip-literals.d.ts.map +1 -0
- package/dist/framework/strip-literals.js +188 -0
- package/dist/framework/strip-literals.js.map +1 -0
- package/dist/gate.d.ts +74 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +257 -0
- package/dist/gate.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/__tests__/check-package-discovery.test.d.ts +2 -0
- package/dist/plugins/__tests__/check-package-discovery.test.d.ts.map +1 -0
- package/dist/plugins/__tests__/check-package-discovery.test.js +170 -0
- package/dist/plugins/__tests__/check-package-discovery.test.js.map +1 -0
- package/dist/plugins/__tests__/lang-domain.test.d.ts +2 -0
- package/dist/plugins/__tests__/lang-domain.test.d.ts.map +1 -0
- package/dist/plugins/__tests__/lang-domain.test.js +171 -0
- package/dist/plugins/__tests__/lang-domain.test.js.map +1 -0
- package/dist/plugins/__tests__/loader.test.d.ts +2 -0
- package/dist/plugins/__tests__/loader.test.d.ts.map +1 -0
- package/dist/plugins/__tests__/loader.test.js +194 -0
- package/dist/plugins/__tests__/loader.test.js.map +1 -0
- package/dist/plugins/check-package-discovery.d.ts +73 -0
- package/dist/plugins/check-package-discovery.d.ts.map +1 -0
- package/dist/plugins/check-package-discovery.js +212 -0
- package/dist/plugins/check-package-discovery.js.map +1 -0
- package/dist/plugins/loader.d.ts +31 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +290 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/types.d.ts +23 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +9 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/recipes/__tests__/built-in-recipes.test.d.ts +2 -0
- package/dist/recipes/__tests__/built-in-recipes.test.d.ts.map +1 -0
- package/dist/recipes/__tests__/built-in-recipes.test.js +93 -0
- package/dist/recipes/__tests__/built-in-recipes.test.js.map +1 -0
- package/dist/recipes/__tests__/check-config.test.d.ts +5 -0
- package/dist/recipes/__tests__/check-config.test.d.ts.map +1 -0
- package/dist/recipes/__tests__/check-config.test.js +37 -0
- package/dist/recipes/__tests__/check-config.test.js.map +1 -0
- package/dist/recipes/__tests__/check-resolution.test.d.ts +2 -0
- package/dist/recipes/__tests__/check-resolution.test.d.ts.map +1 -0
- package/dist/recipes/__tests__/check-resolution.test.js +135 -0
- package/dist/recipes/__tests__/check-resolution.test.js.map +1 -0
- package/dist/recipes/__tests__/registry.test.d.ts +2 -0
- package/dist/recipes/__tests__/registry.test.d.ts.map +1 -0
- package/dist/recipes/__tests__/registry.test.js +97 -0
- package/dist/recipes/__tests__/registry.test.js.map +1 -0
- package/dist/recipes/__tests__/retry.test.d.ts +2 -0
- package/dist/recipes/__tests__/retry.test.d.ts.map +1 -0
- package/dist/recipes/__tests__/retry.test.js +75 -0
- package/dist/recipes/__tests__/retry.test.js.map +1 -0
- package/dist/recipes/__tests__/service.test.d.ts +11 -0
- package/dist/recipes/__tests__/service.test.d.ts.map +1 -0
- package/dist/recipes/__tests__/service.test.js +482 -0
- package/dist/recipes/__tests__/service.test.js.map +1 -0
- package/dist/recipes/built-in-recipes.d.ts +14 -0
- package/dist/recipes/built-in-recipes.d.ts.map +1 -0
- package/dist/recipes/built-in-recipes.js +247 -0
- package/dist/recipes/built-in-recipes.js.map +1 -0
- package/dist/recipes/check-config.d.ts +40 -0
- package/dist/recipes/check-config.d.ts.map +1 -0
- package/dist/recipes/check-config.js +61 -0
- package/dist/recipes/check-config.js.map +1 -0
- package/dist/recipes/check-resolution.d.ts +21 -0
- package/dist/recipes/check-resolution.d.ts.map +1 -0
- package/dist/recipes/check-resolution.js +121 -0
- package/dist/recipes/check-resolution.js.map +1 -0
- package/dist/recipes/check-result-processor.d.ts +51 -0
- package/dist/recipes/check-result-processor.d.ts.map +1 -0
- package/dist/recipes/check-result-processor.js +158 -0
- package/dist/recipes/check-result-processor.js.map +1 -0
- package/dist/recipes/parallel-execution.d.ts +33 -0
- package/dist/recipes/parallel-execution.d.ts.map +1 -0
- package/dist/recipes/parallel-execution.js +142 -0
- package/dist/recipes/parallel-execution.js.map +1 -0
- package/dist/recipes/registry.d.ts +81 -0
- package/dist/recipes/registry.d.ts.map +1 -0
- package/dist/recipes/registry.js +131 -0
- package/dist/recipes/registry.js.map +1 -0
- package/dist/recipes/retry.d.ts +25 -0
- package/dist/recipes/retry.d.ts.map +1 -0
- package/dist/recipes/retry.js +44 -0
- package/dist/recipes/retry.js.map +1 -0
- package/dist/recipes/sequential-execution.d.ts +10 -0
- package/dist/recipes/sequential-execution.d.ts.map +1 -0
- package/dist/recipes/sequential-execution.js +122 -0
- package/dist/recipes/sequential-execution.js.map +1 -0
- package/dist/recipes/service-types.d.ts +84 -0
- package/dist/recipes/service-types.d.ts.map +1 -0
- package/dist/recipes/service-types.js +8 -0
- package/dist/recipes/service-types.js.map +1 -0
- package/dist/recipes/service.d.ts +71 -0
- package/dist/recipes/service.d.ts.map +1 -0
- package/dist/recipes/service.js +331 -0
- package/dist/recipes/service.js.map +1 -0
- package/dist/recipes/types.d.ts +154 -0
- package/dist/recipes/types.d.ts.map +1 -0
- package/dist/recipes/types.js +54 -0
- package/dist/recipes/types.js.map +1 -0
- package/dist/sarif.d.ts +34 -0
- package/dist/sarif.d.ts.map +1 -0
- package/dist/sarif.js +192 -0
- package/dist/sarif.js.map +1 -0
- package/dist/signalers/__tests__/loader.test.d.ts +2 -0
- package/dist/signalers/__tests__/loader.test.d.ts.map +1 -0
- package/dist/signalers/__tests__/loader.test.js +74 -0
- package/dist/signalers/__tests__/loader.test.js.map +1 -0
- package/dist/signalers/index.d.ts +8 -0
- package/dist/signalers/index.d.ts.map +1 -0
- package/dist/signalers/index.js +9 -0
- package/dist/signalers/index.js.map +1 -0
- package/dist/signalers/loader.d.ts +24 -0
- package/dist/signalers/loader.d.ts.map +1 -0
- package/dist/signalers/loader.js +108 -0
- package/dist/signalers/loader.js.map +1 -0
- package/dist/signalers/schema.d.ts +288 -0
- package/dist/signalers/schema.d.ts.map +1 -0
- package/dist/signalers/schema.js +99 -0
- package/dist/signalers/schema.js.map +1 -0
- package/dist/signalers/types.d.ts +13 -0
- package/dist/signalers/types.d.ts.map +1 -0
- package/dist/signalers/types.js +5 -0
- package/dist/signalers/types.js.map +1 -0
- package/dist/targets/__tests__/loader.test.d.ts +2 -0
- package/dist/targets/__tests__/loader.test.d.ts.map +1 -0
- package/dist/targets/__tests__/loader.test.js +127 -0
- package/dist/targets/__tests__/loader.test.js.map +1 -0
- package/dist/targets/__tests__/resolver.test.d.ts +2 -0
- package/dist/targets/__tests__/resolver.test.d.ts.map +1 -0
- package/dist/targets/__tests__/resolver.test.js +54 -0
- package/dist/targets/__tests__/resolver.test.js.map +1 -0
- package/dist/targets/__tests__/target-registry.test.d.ts +2 -0
- package/dist/targets/__tests__/target-registry.test.d.ts.map +1 -0
- package/dist/targets/__tests__/target-registry.test.js +89 -0
- package/dist/targets/__tests__/target-registry.test.js.map +1 -0
- package/dist/targets/index.d.ts +10 -0
- package/dist/targets/index.d.ts.map +1 -0
- package/dist/targets/index.js +12 -0
- package/dist/targets/index.js.map +1 -0
- package/dist/targets/loader.d.ts +19 -0
- package/dist/targets/loader.d.ts.map +1 -0
- package/dist/targets/loader.js +159 -0
- package/dist/targets/loader.js.map +1 -0
- package/dist/targets/resolver.d.ts +19 -0
- package/dist/targets/resolver.d.ts.map +1 -0
- package/dist/targets/resolver.js +37 -0
- package/dist/targets/resolver.js.map +1 -0
- package/dist/targets/target-registry.d.ts +61 -0
- package/dist/targets/target-registry.d.ts.map +1 -0
- package/dist/targets/target-registry.js +93 -0
- package/dist/targets/target-registry.js.map +1 -0
- package/dist/targets/types.d.ts +85 -0
- package/dist/targets/types.d.ts.map +1 -0
- package/dist/targets/types.js +5 -0
- package/dist/targets/types.js.map +1 -0
- package/dist/tool.d.ts +17 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +282 -0
- package/dist/tool.js.map +1 -0
- package/dist/types/findings.d.ts +117 -0
- package/dist/types/findings.d.ts.map +1 -0
- package/dist/types/findings.js +93 -0
- package/dist/types/findings.js.map +1 -0
- package/dist/types/severity.d.ts +15 -0
- package/dist/types/severity.d.ts.map +1 -0
- package/dist/types/severity.js +36 -0
- package/dist/types/severity.js.map +1 -0
- package/package.json +45 -0
- package/src/__tests__/gate.test.ts +537 -0
- package/src/__tests__/sarif.test.ts +201 -0
- package/src/cli/dashboard.ts +93 -0
- package/src/cli/fit.ts +612 -0
- package/src/cli/list-checks.ts +32 -0
- package/src/cli/list-recipes.ts +38 -0
- package/src/framework/__tests__/ast-utilities.test.ts +157 -0
- package/src/framework/__tests__/check-config.test.ts +65 -0
- package/src/framework/__tests__/command-executor.test.ts +79 -0
- package/src/framework/__tests__/content-filter-dispatch.test.ts +132 -0
- package/src/framework/__tests__/content-filter.test.ts +136 -0
- package/src/framework/__tests__/define-check.test.ts +180 -0
- package/src/framework/__tests__/directive-inventory.test.ts +53 -0
- package/src/framework/__tests__/execution-context.test.ts +80 -0
- package/src/framework/__tests__/file-accessor.test.ts +121 -0
- package/src/framework/__tests__/file-cache.test.ts +142 -0
- package/src/framework/__tests__/import-graph.test.ts +282 -0
- package/src/framework/__tests__/path-matcher.test.ts +130 -0
- package/src/framework/__tests__/register-helpers.test.ts +51 -0
- package/src/framework/__tests__/registry.test.ts +243 -0
- package/src/framework/__tests__/result-builder.test.ts +178 -0
- package/src/framework/__tests__/scope-resolver.test.ts +208 -0
- package/src/framework/__tests__/severity-mapping.test.ts +50 -0
- package/src/framework/__tests__/strip-literals.test.ts +109 -0
- package/src/framework/abortable-exec.ts +177 -0
- package/src/framework/ast-utilities.ts +112 -0
- package/src/framework/check-config.ts +339 -0
- package/src/framework/check-types.ts +77 -0
- package/src/framework/command-executor.ts +100 -0
- package/src/framework/constants.ts +16 -0
- package/src/framework/content-filter.ts +288 -0
- package/src/framework/define-check.ts +336 -0
- package/src/framework/directive-inventory.ts +110 -0
- package/src/framework/directive-parsing.ts +152 -0
- package/src/framework/execution-context.ts +247 -0
- package/src/framework/file-accessor.ts +171 -0
- package/src/framework/file-cache.ts +220 -0
- package/src/framework/file-type-filter.ts +25 -0
- package/src/framework/ignore-processing.ts +350 -0
- package/src/framework/import-graph.ts +280 -0
- package/src/framework/memory-profiler.ts +145 -0
- package/src/framework/parse-cache.ts +38 -0
- package/src/framework/path-matcher.ts +191 -0
- package/src/framework/register-helpers.ts +20 -0
- package/src/framework/registry.ts +125 -0
- package/src/framework/result-builder.ts +225 -0
- package/src/framework/scope-resolver.ts +262 -0
- package/src/framework/severity-mapping.ts +56 -0
- package/src/framework/strip-literals.ts +200 -0
- package/src/gate.ts +337 -0
- package/src/index.ts +110 -0
- package/src/plugins/__tests__/check-package-discovery.test.ts +204 -0
- package/src/plugins/__tests__/lang-domain.test.ts +198 -0
- package/src/plugins/__tests__/loader.test.ts +226 -0
- package/src/plugins/check-package-discovery.ts +242 -0
- package/src/plugins/loader.ts +327 -0
- package/src/plugins/types.ts +25 -0
- package/src/recipes/__tests__/built-in-recipes.test.ts +107 -0
- package/src/recipes/__tests__/check-config.test.ts +51 -0
- package/src/recipes/__tests__/check-resolution.test.ts +185 -0
- package/src/recipes/__tests__/registry.test.ts +115 -0
- package/src/recipes/__tests__/retry.test.ts +83 -0
- package/src/recipes/__tests__/service.test.ts +572 -0
- package/src/recipes/built-in-recipes.ts +273 -0
- package/src/recipes/check-config.ts +64 -0
- package/src/recipes/check-resolution.ts +169 -0
- package/src/recipes/check-result-processor.ts +258 -0
- package/src/recipes/parallel-execution.ts +220 -0
- package/src/recipes/registry.ts +192 -0
- package/src/recipes/retry.ts +69 -0
- package/src/recipes/sequential-execution.ts +139 -0
- package/src/recipes/service-types.ts +105 -0
- package/src/recipes/service.ts +400 -0
- package/src/recipes/types.ts +247 -0
- package/src/sarif.ts +232 -0
- package/src/signalers/__tests__/loader.test.ts +99 -0
- package/src/signalers/index.ts +9 -0
- package/src/signalers/loader.ts +141 -0
- package/src/signalers/schema.ts +117 -0
- package/src/signalers/types.ts +15 -0
- package/src/targets/__tests__/loader.test.ts +170 -0
- package/src/targets/__tests__/resolver.test.ts +74 -0
- package/src/targets/__tests__/target-registry.test.ts +103 -0
- package/src/targets/index.ts +13 -0
- package/src/targets/loader.ts +214 -0
- package/src/targets/resolver.ts +44 -0
- package/src/targets/target-registry.ts +111 -0
- package/src/targets/types.ts +89 -0
- package/src/tool.ts +302 -0
- package/src/types/findings.ts +239 -0
- package/src/types/severity.ts +39 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +33 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// @fitness-ignore-file file-length-limits -- framework/content-filter complexity requires single-file cohesion
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview TypeScript scanner-based content filtering
|
|
4
|
+
*
|
|
5
|
+
* Uses the TypeScript scanner (not full AST parser) to identify string literal
|
|
6
|
+
* and comment regions. String content is replaced with spaces of equal length,
|
|
7
|
+
* preserving line/column positions for accurate violation reporting.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { logger } from '@opensip-tools/core'
|
|
11
|
+
import ts from 'typescript'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// TYPES
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
/** Content processed by the TypeScript scanner with string/comment region tracking */
|
|
19
|
+
export interface FilteredContent {
|
|
20
|
+
/** Content with string literals replaced by whitespace of equal length */
|
|
21
|
+
readonly code: string
|
|
22
|
+
/**
|
|
23
|
+
* Content with both string literals AND comments replaced by whitespace
|
|
24
|
+
* of equal length. Use when a check pattern-matches identifiers via regex
|
|
25
|
+
* and would otherwise false-positive on banned-call references that
|
|
26
|
+
* appear in JSDoc / line / block comments documenting the rule
|
|
27
|
+
* (e.g. ``"Replace getDatabase() with the constructor StoreDeps"`` inside
|
|
28
|
+
* a doc string).
|
|
29
|
+
*/
|
|
30
|
+
readonly codeNoComments: string
|
|
31
|
+
/** Original content (unchanged) */
|
|
32
|
+
readonly raw: string
|
|
33
|
+
/** Set of line numbers (1-based) that are entirely inside comments */
|
|
34
|
+
readonly commentLines: ReadonlySet<number>
|
|
35
|
+
/** Check if a (1-based line, 0-based column) position is inside a string literal */
|
|
36
|
+
readonly isInString: (line: number, column: number) => boolean
|
|
37
|
+
/** Check if a (1-based line, 0-based column) position is inside a comment */
|
|
38
|
+
readonly isInComment: (line: number, column: number) => boolean
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** A region in the source text defined by byte offsets */
|
|
42
|
+
interface Region {
|
|
43
|
+
readonly start: number
|
|
44
|
+
readonly end: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// HELPERS
|
|
49
|
+
// =============================================================================
|
|
50
|
+
|
|
51
|
+
/** Build an array of byte offsets where each line starts. */
|
|
52
|
+
function buildLineStarts(content: string): number[] {
|
|
53
|
+
const lineStarts: number[] = [0]
|
|
54
|
+
// eslint-disable-next-line unicorn/no-for-loop -- offset-bearing scan: stores UTF-16 indexes
|
|
55
|
+
for (let i = 0; i < content.length; i++) {
|
|
56
|
+
if (content[i] === '\n') lineStarts.push(i + 1)
|
|
57
|
+
}
|
|
58
|
+
return lineStarts
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Build a set of 1-based line numbers from a list of regions.
|
|
63
|
+
* A line is included if any part of it falls within a region.
|
|
64
|
+
*/
|
|
65
|
+
function linesToSet(content: string, regions: readonly Region[]): ReadonlySet<number> {
|
|
66
|
+
if (regions.length === 0) return new Set()
|
|
67
|
+
|
|
68
|
+
const lineStarts = buildLineStarts(content)
|
|
69
|
+
|
|
70
|
+
const result = new Set<number>()
|
|
71
|
+
for (const region of regions) {
|
|
72
|
+
for (let lineIdx = 0; lineIdx < lineStarts.length; lineIdx++) {
|
|
73
|
+
const lineStart = lineStarts[lineIdx]
|
|
74
|
+
const lineEnd = lineIdx + 1 < lineStarts.length ? lineStarts[lineIdx + 1] - 1 : content.length
|
|
75
|
+
if (lineStart > region.end) break
|
|
76
|
+
if (lineEnd >= region.start) {
|
|
77
|
+
result.add(lineIdx + 1) // 1-based
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return result
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if a (1-based line, 0-based column) offset falls within any region.
|
|
86
|
+
*/
|
|
87
|
+
function isInRegions(content: string, regions: readonly Region[], line: number, column: number): boolean {
|
|
88
|
+
if (regions.length === 0) return false
|
|
89
|
+
|
|
90
|
+
// Convert line/column to byte offset
|
|
91
|
+
let currentLine = 1
|
|
92
|
+
let lineStart = 0
|
|
93
|
+
// eslint-disable-next-line unicorn/no-for-loop -- offset-bearing scan: captures UTF-16 line start
|
|
94
|
+
for (let i = 0; i < content.length; i++) {
|
|
95
|
+
if (currentLine === line) {
|
|
96
|
+
lineStart = i
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
if (content[i] === '\n') currentLine++
|
|
100
|
+
}
|
|
101
|
+
if (currentLine !== line) return false
|
|
102
|
+
|
|
103
|
+
const offset = lineStart + column
|
|
104
|
+
for (const region of regions) {
|
|
105
|
+
if (offset >= region.start && offset < region.end) return true
|
|
106
|
+
}
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Replace characters in the given range with spaces, preserving newlines.
|
|
112
|
+
* Records the range as a string region.
|
|
113
|
+
*/
|
|
114
|
+
function replaceCharsInRange(chars: string[], start: number, end: number, stringRegions: Region[]): void {
|
|
115
|
+
stringRegions.push({ start, end })
|
|
116
|
+
for (let i = start; i < end; i++) {
|
|
117
|
+
if (chars[i] !== '\n') chars[i] = ' '
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// MAIN
|
|
123
|
+
// =============================================================================
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Scan content using TypeScript's scanner to identify string and comment regions.
|
|
127
|
+
*
|
|
128
|
+
* String literals are replaced with spaces of equal length, preserving
|
|
129
|
+
* line/column positions. Comments are tracked but not removed (directives
|
|
130
|
+
* live in comments and must be preserved).
|
|
131
|
+
*/
|
|
132
|
+
// Module-level cache to avoid re-running the TS scanner on the same content.
|
|
133
|
+
// Bounded by an idle timer (10 min, matching parse-cache.ts) so long-lived
|
|
134
|
+
// embedders don't accumulate cached filter results across runs forever. The
|
|
135
|
+
// timer resets each time filterContent runs, so an active session never
|
|
136
|
+
// loses its cache.
|
|
137
|
+
const FILTER_CACHE_IDLE_TIMEOUT_MS = 10 * 60 * 1000
|
|
138
|
+
const filterCache = new Map<string, FilteredContent>()
|
|
139
|
+
let filterCacheIdleTimer: ReturnType<typeof setTimeout> | null = null
|
|
140
|
+
|
|
141
|
+
function scheduleFilterCacheClear(): void {
|
|
142
|
+
if (filterCacheIdleTimer) clearTimeout(filterCacheIdleTimer)
|
|
143
|
+
filterCacheIdleTimer = setTimeout(() => {
|
|
144
|
+
filterCache.clear()
|
|
145
|
+
filterCacheIdleTimer = null
|
|
146
|
+
}, FILTER_CACHE_IDLE_TIMEOUT_MS)
|
|
147
|
+
filterCacheIdleTimer.unref()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Clear the filter cache (call between runs or on memory pressure) */
|
|
151
|
+
export function clearFilterCache(): void {
|
|
152
|
+
filterCache.clear()
|
|
153
|
+
if (filterCacheIdleTimer) {
|
|
154
|
+
clearTimeout(filterCacheIdleTimer)
|
|
155
|
+
filterCacheIdleTimer = null
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function filterContent(content: string): FilteredContent {
|
|
160
|
+
const cached = filterCache.get(content)
|
|
161
|
+
if (cached) {
|
|
162
|
+
scheduleFilterCacheClear()
|
|
163
|
+
return cached
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const result = filterContentImpl(content)
|
|
168
|
+
filterCache.set(content, result)
|
|
169
|
+
scheduleFilterCacheClear()
|
|
170
|
+
return result
|
|
171
|
+
} catch {
|
|
172
|
+
// Graceful degradation — return raw content if scanner fails
|
|
173
|
+
logger.debug('Content filter fell back to raw content', { evt: 'fitness.content_filter.fallback', module: 'fitness:framework' })
|
|
174
|
+
const fallback: FilteredContent = {
|
|
175
|
+
code: content,
|
|
176
|
+
codeNoComments: content,
|
|
177
|
+
raw: content,
|
|
178
|
+
commentLines: new Set(),
|
|
179
|
+
isInString: () => false,
|
|
180
|
+
isInComment: () => false,
|
|
181
|
+
}
|
|
182
|
+
filterCache.set(content, fallback)
|
|
183
|
+
return fallback
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity -- TS scanner driver: token-by-token loop with per-kind handling; flatter shape would scatter token classification
|
|
188
|
+
function filterContentImpl(content: string): FilteredContent {
|
|
189
|
+
const scanner = ts.createScanner(ts.ScriptTarget.Latest, false, ts.LanguageVariant.Standard, content)
|
|
190
|
+
|
|
191
|
+
const stringRegions: Region[] = []
|
|
192
|
+
const commentRegions: Region[] = []
|
|
193
|
+
const chars = [...content]
|
|
194
|
+
|
|
195
|
+
// Depth counter, not a boolean — a `${ `inner` }` construct nests two templates
|
|
196
|
+
// and each `}` that closes a template-expression must be rescanned. A plain
|
|
197
|
+
// boolean flipped off by the inner TemplateTail would leave the outer unrescanned
|
|
198
|
+
// and desync the scanner for the rest of the file (which silently wipes real
|
|
199
|
+
// code to whitespace). Incremented at TemplateHead, decremented at TemplateTail.
|
|
200
|
+
let templateDepth = 0
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
while (true) {
|
|
204
|
+
let token = scanner.scan()
|
|
205
|
+
// @fitness-ignore-next-line unsafe-secret-comparison -- comparing TypeScript SyntaxKind enum, not a secret
|
|
206
|
+
if (token === ts.SyntaxKind.EndOfFileToken) break
|
|
207
|
+
|
|
208
|
+
// After a CloseBraceToken inside ANY template expression, rescan to get TemplateMiddle/TemplateTail
|
|
209
|
+
// @fitness-ignore-next-line unsafe-secret-comparison -- comparing TypeScript SyntaxKind enum, not a secret
|
|
210
|
+
if (token === ts.SyntaxKind.CloseBraceToken && templateDepth > 0) {
|
|
211
|
+
token = scanner.reScanTemplateToken(false)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const start = scanner.getTokenStart()
|
|
215
|
+
const end = scanner.getTokenEnd()
|
|
216
|
+
|
|
217
|
+
switch (token) {
|
|
218
|
+
case ts.SyntaxKind.StringLiteral:
|
|
219
|
+
case ts.SyntaxKind.NoSubstitutionTemplateLiteral: {
|
|
220
|
+
// Replace content inside quotes/backticks (keep delimiters)
|
|
221
|
+
replaceCharsInRange(chars, start + 1, end - 1, stringRegions)
|
|
222
|
+
break
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
case ts.SyntaxKind.TemplateHead: {
|
|
226
|
+
// `text ${ — replace text between ` and ${
|
|
227
|
+
templateDepth++
|
|
228
|
+
replaceCharsInRange(chars, start + 1, end - 2, stringRegions)
|
|
229
|
+
break
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
case ts.SyntaxKind.TemplateMiddle: {
|
|
233
|
+
// }text ${ — replace text between } and ${
|
|
234
|
+
replaceCharsInRange(chars, start + 1, end - 2, stringRegions)
|
|
235
|
+
break
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
case ts.SyntaxKind.TemplateTail: {
|
|
239
|
+
// }text` — replace text between } and `
|
|
240
|
+
templateDepth--
|
|
241
|
+
replaceCharsInRange(chars, start + 1, end - 1, stringRegions)
|
|
242
|
+
break
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
case ts.SyntaxKind.SingleLineCommentTrivia:
|
|
246
|
+
case ts.SyntaxKind.MultiLineCommentTrivia: {
|
|
247
|
+
// Track comment regions but don't modify content
|
|
248
|
+
commentRegions.push({ start, end })
|
|
249
|
+
break
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// RegularExpressionLiteral — leave unchanged, regex is code
|
|
253
|
+
default: {
|
|
254
|
+
break
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const code = chars.join('')
|
|
260
|
+
const commentLines = linesToSet(content, commentRegions)
|
|
261
|
+
|
|
262
|
+
// Compute `codeNoComments` by additionally replacing comment regions
|
|
263
|
+
// with whitespace. Done as a second pass on a fresh array so `code`
|
|
264
|
+
// (strings-stripped only) and `codeNoComments` (strings + comments
|
|
265
|
+
// stripped) remain available — most checks want one or the other,
|
|
266
|
+
// not both.
|
|
267
|
+
const charsNoComments = [...content]
|
|
268
|
+
for (const region of stringRegions) {
|
|
269
|
+
for (let i = region.start; i < region.end; i++) {
|
|
270
|
+
if (charsNoComments[i] !== '\n') charsNoComments[i] = ' '
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
for (const region of commentRegions) {
|
|
274
|
+
for (let i = region.start; i < region.end; i++) {
|
|
275
|
+
if (charsNoComments[i] !== '\n') charsNoComments[i] = ' '
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const codeNoComments = charsNoComments.join('')
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
code,
|
|
282
|
+
codeNoComments,
|
|
283
|
+
raw: content,
|
|
284
|
+
commentLines,
|
|
285
|
+
isInString: (line, column) => isInRegions(content, stringRegions, line, column),
|
|
286
|
+
isInComment: (line, column) => isInRegions(content, commentRegions, line, column),
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// @fitness-ignore-file module-coupling-metrics -- central orchestration module with necessary coupling
|
|
2
|
+
// @fitness-ignore-file null-safety -- ResultBuilder.create() returns a fluent builder; .totalItems().filesScanned() chain is always safe
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview defineCheck - Unified check definition API
|
|
5
|
+
*
|
|
6
|
+
* The main API for creating fitness checks. Supports three modes:
|
|
7
|
+
* - analyze: Per-file analysis with content and path
|
|
8
|
+
* - analyzeAll: Multi-file analysis with lazy loading FileAccessor
|
|
9
|
+
* - command: External tool execution with output parsing
|
|
10
|
+
*
|
|
11
|
+
* Check authors return CheckViolation[]. The framework converts each
|
|
12
|
+
* CheckViolation into a universal Signal via createSignal().
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { logger , SystemError , createSignal , applyContentFilter } from '@opensip-tools/core'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
getAnalysisMode,
|
|
21
|
+
isAnalyzeConfig,
|
|
22
|
+
isAnalyzeAllConfig,
|
|
23
|
+
isCommandConfig,
|
|
24
|
+
validateCheckConfig,
|
|
25
|
+
} from './check-config.js'
|
|
26
|
+
import { executeCommand } from './command-executor.js'
|
|
27
|
+
import { CheckAbortedError, createExecutionContext } from './execution-context.js'
|
|
28
|
+
import { createFileAccessor } from './file-accessor.js'
|
|
29
|
+
import { filterFilesByType } from './file-type-filter.js'
|
|
30
|
+
import { filterSignalsByDirectives, buildFilteredResult } from './ignore-processing.js'
|
|
31
|
+
import { PathMatcher } from './path-matcher.js'
|
|
32
|
+
import { ResultBuilder } from './result-builder.js'
|
|
33
|
+
import { mapFindingSeverity, mapTagsToSignalCategory } from './severity-mapping.js'
|
|
34
|
+
|
|
35
|
+
import type {
|
|
36
|
+
UnifiedCheckConfig,
|
|
37
|
+
CheckViolation,
|
|
38
|
+
AnalyzeCheckConfig,
|
|
39
|
+
AnalyzeAllCheckConfig,
|
|
40
|
+
CommandCheckConfig,
|
|
41
|
+
} from './check-config.js'
|
|
42
|
+
import type { Check } from './check-types.js'
|
|
43
|
+
import type { ExecutionContext, RunOptions } from './execution-context.js'
|
|
44
|
+
import type { CheckResult } from '../types/findings.js'
|
|
45
|
+
import type { Signal } from '@opensip-tools/core'
|
|
46
|
+
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// VIOLATION → SIGNAL CONVERSION
|
|
49
|
+
// =============================================================================
|
|
50
|
+
|
|
51
|
+
function toSignal(
|
|
52
|
+
violation: CheckViolation,
|
|
53
|
+
checkSlug: string,
|
|
54
|
+
checkTags: readonly string[],
|
|
55
|
+
defaultFilePath?: string,
|
|
56
|
+
provider = 'opensip',
|
|
57
|
+
): Signal {
|
|
58
|
+
const filePath = violation.filePath ?? defaultFilePath ?? ''
|
|
59
|
+
return createSignal({
|
|
60
|
+
source: 'fitness',
|
|
61
|
+
provider,
|
|
62
|
+
severity: mapFindingSeverity(violation.severity),
|
|
63
|
+
category: mapTagsToSignalCategory(checkTags),
|
|
64
|
+
ruleId: `fit:${checkSlug}`,
|
|
65
|
+
message: violation.message,
|
|
66
|
+
suggestion: violation.suggestion,
|
|
67
|
+
code: { file: filePath, line: violation.line, column: violation.column },
|
|
68
|
+
fix: violation.fix
|
|
69
|
+
?? (violation.suggestion ? { action: 'refactor' as const, confidence: 0.5 } : undefined),
|
|
70
|
+
metadata: Object.fromEntries(
|
|
71
|
+
Object.entries({
|
|
72
|
+
match: violation.match,
|
|
73
|
+
type: violation.type,
|
|
74
|
+
checkSlug,
|
|
75
|
+
checkTags: checkTags.length > 0 ? checkTags.join(',') : undefined,
|
|
76
|
+
}).filter(([, v]) => v != null && v !== ''),
|
|
77
|
+
),
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// =============================================================================
|
|
82
|
+
// ANALYSIS MODE EXECUTORS
|
|
83
|
+
// =============================================================================
|
|
84
|
+
|
|
85
|
+
/** @throws {CheckAbortedError} When the check is aborted via AbortSignal */
|
|
86
|
+
async function executeAnalyzeMode(
|
|
87
|
+
config: AnalyzeCheckConfig,
|
|
88
|
+
files: readonly string[],
|
|
89
|
+
ctx: ExecutionContext,
|
|
90
|
+
): Promise<CheckResult> {
|
|
91
|
+
const builder = ResultBuilder.create({
|
|
92
|
+
checkId: config.id,
|
|
93
|
+
itemType: config.itemType ?? 'files',
|
|
94
|
+
})
|
|
95
|
+
.totalItems(files.length)
|
|
96
|
+
.filesScanned(files.length)
|
|
97
|
+
|
|
98
|
+
for (const filePath of files) {
|
|
99
|
+
if (ctx.signal?.aborted) {
|
|
100
|
+
throw new CheckAbortedError(config.slug)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const rawContent = await ctx.readFile(filePath)
|
|
105
|
+
// Dispatch the content filter through the LanguageAdapter for the
|
|
106
|
+
// file's extension. Falls back to raw content when no adapter is
|
|
107
|
+
// registered. See languages/content-filter-dispatch.ts.
|
|
108
|
+
const content = applyContentFilter(filePath, rawContent, config.contentFilter ?? 'none')
|
|
109
|
+
const violations = config.analyze(content, filePath)
|
|
110
|
+
|
|
111
|
+
for (const violation of violations) {
|
|
112
|
+
void builder.addSignal(toSignal(violation, config.slug, config.tags ?? [], filePath, config.provider))
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (error instanceof CheckAbortedError) throw error
|
|
116
|
+
logger.debug('Skipping unreadable file', { evt: 'fitness.check.file.skip', module: 'fitness:framework', filePath, checkSlug: config.slug })
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return builder.build()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** @throws {CheckAbortedError} When the check is aborted via AbortSignal */
|
|
124
|
+
async function executeAnalyzeAllMode(
|
|
125
|
+
config: AnalyzeAllCheckConfig,
|
|
126
|
+
files: readonly string[],
|
|
127
|
+
ctx: ExecutionContext,
|
|
128
|
+
): Promise<CheckResult> {
|
|
129
|
+
if (ctx.signal?.aborted) {
|
|
130
|
+
throw new CheckAbortedError(config.slug)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const fileAccessor = createFileAccessor(files, { signal: ctx.signal, contentFilter: config.contentFilter })
|
|
134
|
+
const violations = await config.analyzeAll(fileAccessor)
|
|
135
|
+
|
|
136
|
+
if (ctx.signal?.aborted) {
|
|
137
|
+
throw new CheckAbortedError(config.slug)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const builder = ResultBuilder.create({
|
|
141
|
+
checkId: config.id,
|
|
142
|
+
itemType: config.itemType ?? 'files',
|
|
143
|
+
})
|
|
144
|
+
.totalItems(files.length)
|
|
145
|
+
.filesScanned(files.length)
|
|
146
|
+
|
|
147
|
+
for (const violation of violations) {
|
|
148
|
+
if (!violation.filePath) {
|
|
149
|
+
ctx.log(`Warning: violation missing filePath in analyzeAll mode`)
|
|
150
|
+
}
|
|
151
|
+
void builder.addSignal(toSignal(violation, config.slug, config.tags ?? [], undefined, config.provider))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return builder.build()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @throws {CheckAbortedError} When the check is aborted via AbortSignal */
|
|
158
|
+
async function executeCommandMode(
|
|
159
|
+
config: CommandCheckConfig,
|
|
160
|
+
files: readonly string[],
|
|
161
|
+
ctx: ExecutionContext,
|
|
162
|
+
): Promise<CheckResult> {
|
|
163
|
+
const result = await executeCommand(config.command, files, {
|
|
164
|
+
cwd: ctx.cwd,
|
|
165
|
+
signal: ctx.signal,
|
|
166
|
+
timeout: config.timeout,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
if (result.aborted) {
|
|
170
|
+
throw new CheckAbortedError(config.slug)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const builder = ResultBuilder.create({
|
|
174
|
+
checkId: config.id,
|
|
175
|
+
itemType: config.itemType ?? 'files',
|
|
176
|
+
})
|
|
177
|
+
.totalItems(files.length)
|
|
178
|
+
.filesScanned(0)
|
|
179
|
+
|
|
180
|
+
if (result.error) {
|
|
181
|
+
return builder.buildError(result.error)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
for (const violation of result.violations) {
|
|
185
|
+
void builder.addSignal(toSignal(violation, config.slug, config.tags ?? [], undefined, config.provider))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return builder.build()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// =============================================================================
|
|
192
|
+
// MAIN EXPORT
|
|
193
|
+
// =============================================================================
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Define a fitness check using the unified API.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* export const noConsoleLog = defineCheck({
|
|
201
|
+
* id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
202
|
+
* slug: 'no-console-log',
|
|
203
|
+
* category: 'quality',
|
|
204
|
+
* description: 'Disallow console.log in production code',
|
|
205
|
+
* analyze: (content, filePath) => {
|
|
206
|
+
* const violations: CheckViolation[] = [];
|
|
207
|
+
* content.split('\n').forEach((line, idx) => {
|
|
208
|
+
* if (line.includes('console.log')) {
|
|
209
|
+
* violations.push({ line: idx + 1, message: 'No console.log', severity: 'error' });
|
|
210
|
+
* }
|
|
211
|
+
* });
|
|
212
|
+
* return violations;
|
|
213
|
+
* },
|
|
214
|
+
* });
|
|
215
|
+
* ```
|
|
216
|
+
* @throws {ValidationError} When the check config is invalid
|
|
217
|
+
*/
|
|
218
|
+
export function defineCheck(config: UnifiedCheckConfig): Check {
|
|
219
|
+
validateCheckConfig(config)
|
|
220
|
+
|
|
221
|
+
const check: Check = {
|
|
222
|
+
config: {
|
|
223
|
+
id: config.id,
|
|
224
|
+
slug: config.slug,
|
|
225
|
+
tags: config.tags ? [...config.tags] : [],
|
|
226
|
+
description: config.description,
|
|
227
|
+
longDescription: config.longDescription,
|
|
228
|
+
analysisMode: getAnalysisMode(config),
|
|
229
|
+
scope: { include: [], exclude: [], description: '' },
|
|
230
|
+
itemType: config.itemType ?? 'files',
|
|
231
|
+
docs: config.docs,
|
|
232
|
+
disabled: config.disabled,
|
|
233
|
+
confidence: config.confidence,
|
|
234
|
+
timeout: config.timeout,
|
|
235
|
+
scansFiles: !isCommandConfig(config),
|
|
236
|
+
fileTypes: config.fileTypes ? [...config.fileTypes] : undefined,
|
|
237
|
+
checkScope: config.scope ? { languages: [...config.scope.languages], concerns: [...config.scope.concerns] } : undefined,
|
|
238
|
+
// @fitness-ignore-next-line concurrency-safety -- async arrow delegates to executeUnifiedCheck which is async; needed for type compatibility
|
|
239
|
+
execute: async (ctx) => executeUnifiedCheck(config, ctx),
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
getScope() {
|
|
243
|
+
return { include: [], exclude: [], description: 'target-based scope' }
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
getMatcher(cwd: string): PathMatcher {
|
|
247
|
+
return PathMatcher.create({
|
|
248
|
+
include: [],
|
|
249
|
+
exclude: [],
|
|
250
|
+
cwd,
|
|
251
|
+
})
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
async run(cwd: string, options?: RunOptions): Promise<CheckResult> {
|
|
255
|
+
const start = Date.now()
|
|
256
|
+
|
|
257
|
+
const matcher = PathMatcher.create({
|
|
258
|
+
include: [],
|
|
259
|
+
exclude: [],
|
|
260
|
+
cwd,
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
const legacyConfig = {
|
|
264
|
+
id: config.id,
|
|
265
|
+
slug: config.slug,
|
|
266
|
+
tags: config.tags ? [...config.tags] : [],
|
|
267
|
+
description: config.description,
|
|
268
|
+
scope: { include: [] as readonly string[], exclude: [] as readonly string[], description: '' },
|
|
269
|
+
itemType: (config.itemType ?? 'files'),
|
|
270
|
+
docs: config.docs,
|
|
271
|
+
disabled: config.disabled,
|
|
272
|
+
timeout: config.timeout,
|
|
273
|
+
scansFiles: !isCommandConfig(config),
|
|
274
|
+
// @fitness-ignore-next-line concurrency-safety -- async arrow delegates to executeUnifiedCheck which is async; needed for type compatibility
|
|
275
|
+
execute: async (ctx: ExecutionContext) => executeUnifiedCheck(config, ctx),
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const ctx = createExecutionContext(legacyConfig, cwd, matcher, options)
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
const result = await executeUnifiedCheck(config, ctx)
|
|
282
|
+
|
|
283
|
+
const { filteredSignals, ignoredCount, appliedDirectives } = await filterSignalsByDirectives(
|
|
284
|
+
result.signals,
|
|
285
|
+
config.slug,
|
|
286
|
+
result.ignoredCount ?? 0,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
const filtered = buildFilteredResult(result, filteredSignals, ignoredCount, start)
|
|
290
|
+
return appliedDirectives.length > 0 ? { ...filtered, appliedDirectives } : filtered
|
|
291
|
+
} catch (error) {
|
|
292
|
+
if (error instanceof CheckAbortedError) throw error
|
|
293
|
+
|
|
294
|
+
const builder = ResultBuilder.create({
|
|
295
|
+
checkId: config.id,
|
|
296
|
+
itemType: config.itemType ?? 'files',
|
|
297
|
+
})
|
|
298
|
+
return builder.buildError(
|
|
299
|
+
`Check ${config.slug} threw an error: ${error instanceof Error ? error.message : String(error)}`,
|
|
300
|
+
error instanceof Error ? error : undefined,
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return check
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Internal: Execute a check based on its analysis mode (analyze /
|
|
311
|
+
* analyzeAll / command).
|
|
312
|
+
* @throws {CheckAbortedError} When the check is aborted via AbortSignal
|
|
313
|
+
* @throws {SystemError} When an unknown analysis mode is encountered
|
|
314
|
+
*/
|
|
315
|
+
async function executeUnifiedCheck(
|
|
316
|
+
config: UnifiedCheckConfig,
|
|
317
|
+
ctx: ExecutionContext,
|
|
318
|
+
): Promise<CheckResult> {
|
|
319
|
+
const matchedFiles = await ctx.matchFiles()
|
|
320
|
+
|
|
321
|
+
// Filter by check's declared file types
|
|
322
|
+
const files = filterFilesByType(matchedFiles, config.fileTypes)
|
|
323
|
+
|
|
324
|
+
ctx.log(`Matched ${files.length} files`)
|
|
325
|
+
|
|
326
|
+
if (isAnalyzeConfig(config)) {
|
|
327
|
+
return executeAnalyzeMode(config, files, ctx)
|
|
328
|
+
} else if (isAnalyzeAllConfig(config)) {
|
|
329
|
+
return executeAnalyzeAllMode(config, files, ctx)
|
|
330
|
+
} else if (isCommandConfig(config)) {
|
|
331
|
+
return executeCommandMode(config, files, ctx)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const _exhaustiveCheck: never = config
|
|
335
|
+
throw new SystemError(`Unknown analysis mode: ${JSON.stringify(_exhaustiveCheck)}`, { code: 'SYSTEM.FITNESS.UNKNOWN_MODE' })
|
|
336
|
+
}
|