@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,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Directive Inventory - shared parsing logic for
|
|
3
|
+
* fitness-ignore directives.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// Types
|
|
8
|
+
// =============================================================================
|
|
9
|
+
|
|
10
|
+
/** A single fitness-ignore directive found in a source file. */
|
|
11
|
+
export interface DirectiveEntry {
|
|
12
|
+
filePath: string
|
|
13
|
+
lineNumber: number
|
|
14
|
+
type: 'file' | 'next-line'
|
|
15
|
+
checkId: string
|
|
16
|
+
group: string
|
|
17
|
+
reason: string | null
|
|
18
|
+
weakReason: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Shared Constants
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
/** Patterns that indicate a weak or generic ignore reason. */
|
|
26
|
+
const WEAK_REASON_PATTERNS = Object.freeze<readonly RegExp[]>([
|
|
27
|
+
/^ignore$/i,
|
|
28
|
+
/^skip$/i,
|
|
29
|
+
/^todo$/i,
|
|
30
|
+
/^fixme$/i,
|
|
31
|
+
/^temporary$/i,
|
|
32
|
+
/^temp$/i,
|
|
33
|
+
/^wip$/i,
|
|
34
|
+
/^disable$/i,
|
|
35
|
+
/^suppress$/i,
|
|
36
|
+
/^\s*$/,
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// Shared Parsing
|
|
41
|
+
// =============================================================================
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parse a file-level or next-line directive from a comment line.
|
|
45
|
+
* Accepts both `//` and `/*`-style comments for consistency with the
|
|
46
|
+
* suppression parser in directive-parsing.ts — otherwise block-comment
|
|
47
|
+
* directives suppress findings but vanish from inventory counts.
|
|
48
|
+
*/
|
|
49
|
+
export function parseDirectiveLine(line: string): {
|
|
50
|
+
type: 'file' | 'next-line'
|
|
51
|
+
checkId: string
|
|
52
|
+
reason: string | null
|
|
53
|
+
} | null {
|
|
54
|
+
const trimmed = line.trimStart()
|
|
55
|
+
if (!trimmed.startsWith('// ') && !trimmed.startsWith('/* ')) return null
|
|
56
|
+
|
|
57
|
+
const afterComment = trimmed.slice(3).trimStart()
|
|
58
|
+
|
|
59
|
+
if (afterComment.startsWith('@fitness-ignore-file ')) {
|
|
60
|
+
const rest = afterComment.slice('@fitness-ignore-file '.length)
|
|
61
|
+
return parseDirectiveRest(rest, 'file')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (afterComment.startsWith('@fitness-ignore-next-line ')) {
|
|
65
|
+
const rest = afterComment.slice('@fitness-ignore-next-line '.length)
|
|
66
|
+
return parseDirectiveRest(rest, 'next-line')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseDirectiveRest(
|
|
73
|
+
rest: string,
|
|
74
|
+
type: 'file' | 'next-line',
|
|
75
|
+
): { type: 'file' | 'next-line'; checkId: string; reason: string | null } | null {
|
|
76
|
+
// Strip trailing `*/` from block-comment directives (e.g. `foo */` → `foo`).
|
|
77
|
+
// eslint-disable-next-line sonarjs/slow-regex -- anchored at end-of-string, bounded \s* runs; no ReDoS exposure
|
|
78
|
+
const normalized = rest.replace(/\s*\*\/\s*$/, '').trimEnd()
|
|
79
|
+
|
|
80
|
+
const separatorIndex = normalized.indexOf(' -- ')
|
|
81
|
+
|
|
82
|
+
if (separatorIndex === -1) {
|
|
83
|
+
const checkId = normalized.trim()
|
|
84
|
+
if (!checkId || checkId.includes(' ')) return null
|
|
85
|
+
return { type, checkId, reason: null }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const checkId = normalized.slice(0, separatorIndex).trim()
|
|
89
|
+
const reason = normalized.slice(separatorIndex + 4).trim()
|
|
90
|
+
|
|
91
|
+
if (!checkId || checkId.includes(' ')) return null
|
|
92
|
+
return { type, checkId, reason: reason || null }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if a reason is weak/generic. Missing reason (null) is considered weak.
|
|
97
|
+
*/
|
|
98
|
+
export function isWeakReason(reason: string | null): boolean {
|
|
99
|
+
if (reason === null) return true
|
|
100
|
+
return WEAK_REASON_PATTERNS.some((pattern) => pattern.test(reason.trim()))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Extract the group prefix from a check ID (the directory name).
|
|
105
|
+
*/
|
|
106
|
+
export function extractGroup(checkId: string): string {
|
|
107
|
+
const slashIndex = checkId.indexOf('/')
|
|
108
|
+
return slashIndex > 0 ? checkId.slice(0, slashIndex) : 'other'
|
|
109
|
+
}
|
|
110
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// @fitness-ignore-file semgrep-justifications -- References nosemgrep patterns for directive parsing
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Ignore directive parsing utilities for fitness checks
|
|
4
|
+
*
|
|
5
|
+
* Provides utilities for parsing suppression directives:
|
|
6
|
+
* - @fitness-ignore-file, @fitness-ignore-next-line
|
|
7
|
+
* - eslint-disable-next-line, eslint-disable-line
|
|
8
|
+
* - @ts-expect-error, @ts-ignore
|
|
9
|
+
* - nosemgrep
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// CONSTANTS
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
const KNOWN_DIRECTIVE_KEYWORDS = [
|
|
17
|
+
'eslint-disable-next-line',
|
|
18
|
+
'eslint-disable-line',
|
|
19
|
+
'@ts-expect-error',
|
|
20
|
+
'@ts-ignore',
|
|
21
|
+
'@ts-nocheck',
|
|
22
|
+
'prettier-ignore',
|
|
23
|
+
'biome-ignore',
|
|
24
|
+
'@fitness-ignore-next-line',
|
|
25
|
+
'@fitness-ignore-file',
|
|
26
|
+
] as const
|
|
27
|
+
|
|
28
|
+
const MAX_DIRECTIVE_SKIP = 3
|
|
29
|
+
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// INTERNAL HELPERS
|
|
32
|
+
// =============================================================================
|
|
33
|
+
|
|
34
|
+
function isKnownDirectiveLine(line: string): boolean {
|
|
35
|
+
const trimmed = line.trimStart()
|
|
36
|
+
|
|
37
|
+
if (!trimmed.startsWith('//') && !trimmed.startsWith('/*')) {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const commentContent = trimmed.slice(2).trimStart()
|
|
42
|
+
|
|
43
|
+
return KNOWN_DIRECTIVE_KEYWORDS.some((keyword) => {
|
|
44
|
+
if (!commentContent.startsWith(keyword)) return false
|
|
45
|
+
const nextChar = commentContent[keyword.length]
|
|
46
|
+
return nextChar === undefined || nextChar === ' ' || nextChar === '\t' || nextChar === ':'
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isCheckIdChar(char: string): boolean {
|
|
51
|
+
const code = char.codePointAt(0) ?? 0
|
|
52
|
+
const isLowerCase = code >= 97 && code <= 122
|
|
53
|
+
const isUpperCase = code >= 65 && code <= 90
|
|
54
|
+
const isDigit = code >= 48 && code <= 57
|
|
55
|
+
const isSpecialChar = code === 95 || code === 45 || code === 47
|
|
56
|
+
return isLowerCase || isUpperCase || isDigit || isSpecialChar
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractCheckIdFromDirective(line: string, directiveKeyword: string): string | null {
|
|
60
|
+
// Both `//` and `/*` are 2-char prefixes; sliceLen is fixed.
|
|
61
|
+
const sliceLen = 2
|
|
62
|
+
let commentIndex = line.indexOf('//')
|
|
63
|
+
if (commentIndex === -1) {
|
|
64
|
+
commentIndex = line.indexOf('/*')
|
|
65
|
+
if (commentIndex === -1) return null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const afterComment = line.slice(commentIndex + sliceLen).trimStart()
|
|
69
|
+
if (!afterComment.startsWith(directiveKeyword)) return null
|
|
70
|
+
|
|
71
|
+
const afterDirective = afterComment.slice(directiveKeyword.length)
|
|
72
|
+
if (
|
|
73
|
+
afterDirective.length === 0 ||
|
|
74
|
+
(!afterDirective.startsWith(' ') && !afterDirective.startsWith('\t'))
|
|
75
|
+
) {
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const checkIdStart = afterDirective.trimStart()
|
|
80
|
+
let checkId = ''
|
|
81
|
+
for (const char of checkIdStart) {
|
|
82
|
+
if (isCheckIdChar(char)) {
|
|
83
|
+
checkId += char
|
|
84
|
+
} else {
|
|
85
|
+
break
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return checkId.length > 0 ? checkId : null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// =============================================================================
|
|
93
|
+
// PUBLIC API
|
|
94
|
+
// =============================================================================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Parse file-level ignore directive from file content.
|
|
98
|
+
* Returns true if the file should be entirely ignored for that check.
|
|
99
|
+
*/
|
|
100
|
+
export function parseFileIgnoreDirective(
|
|
101
|
+
content: string,
|
|
102
|
+
checkId: string | readonly string[],
|
|
103
|
+
): boolean {
|
|
104
|
+
const lines = content.split('\n').slice(0, 50)
|
|
105
|
+
const checkIds = Array.isArray(checkId) ? checkId : [checkId]
|
|
106
|
+
|
|
107
|
+
for (const line of lines) {
|
|
108
|
+
const extractedId = extractCheckIdFromDirective(line, '@fitness-ignore-file')
|
|
109
|
+
if (extractedId !== null && checkIds.includes(extractedId)) {
|
|
110
|
+
return true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Parse next-line ignore directives from file content.
|
|
119
|
+
* Returns a set of line numbers that should be ignored.
|
|
120
|
+
*/
|
|
121
|
+
export function parseIgnoreDirectives(
|
|
122
|
+
content: string,
|
|
123
|
+
checkId: string | readonly string[],
|
|
124
|
+
): Set<number> {
|
|
125
|
+
const ignoredLines = new Set<number>()
|
|
126
|
+
const lines = content.split('\n')
|
|
127
|
+
const checkIds = Array.isArray(checkId) ? checkId : [checkId]
|
|
128
|
+
|
|
129
|
+
for (let i = 0; i < lines.length; i++) {
|
|
130
|
+
const extractedId = extractCheckIdFromDirective(lines[i] ?? '', '@fitness-ignore-next-line')
|
|
131
|
+
if (extractedId !== null && checkIds.includes(extractedId)) {
|
|
132
|
+
let targetLine = i + 1
|
|
133
|
+
let skipped = 0
|
|
134
|
+
|
|
135
|
+
while (
|
|
136
|
+
targetLine < lines.length &&
|
|
137
|
+
skipped < MAX_DIRECTIVE_SKIP &&
|
|
138
|
+
isKnownDirectiveLine(lines[targetLine] ?? '')
|
|
139
|
+
) {
|
|
140
|
+
targetLine++
|
|
141
|
+
skipped++
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
ignoredLines.add(targetLine + 1)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return ignoredLines
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// @fitness-ignore-file concurrency-safety -- single-threaded execution context
|
|
2
|
+
// @fitness-ignore-file error-handling-suite -- catch blocks delegate errors through established patterns
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Execution context creation for fitness checks
|
|
5
|
+
*
|
|
6
|
+
* Provides the runtime context available to check execute functions,
|
|
7
|
+
* including file access, pattern matching, and abort support.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from 'node:fs/promises'
|
|
11
|
+
import { relative } from 'node:path'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import { SystemError } from '@opensip-tools/core'
|
|
15
|
+
import { Minimatch } from 'minimatch'
|
|
16
|
+
|
|
17
|
+
import { DEFAULT_EXCLUSION_PATTERNS } from './constants.js'
|
|
18
|
+
import { fileCache } from './file-cache.js'
|
|
19
|
+
import { PathMatcher } from './path-matcher.js'
|
|
20
|
+
import { extractSnippet } from './result-builder.js'
|
|
21
|
+
|
|
22
|
+
import type { ResolvedScope } from './check-config.js'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check identifier (UUID format).
|
|
26
|
+
*/
|
|
27
|
+
// eslint-disable-next-line sonarjs/redundant-type-aliases -- semantic alias for UUID-shaped check identifier
|
|
28
|
+
type CheckId = string
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Error thrown when a check is aborted via AbortSignal.
|
|
32
|
+
*/
|
|
33
|
+
export class CheckAbortedError extends SystemError {
|
|
34
|
+
readonly name = 'CheckAbortedError' as const
|
|
35
|
+
readonly checkId: string
|
|
36
|
+
|
|
37
|
+
constructor(checkId: string, message?: string) {
|
|
38
|
+
super(message ?? `Check ${checkId} was aborted`, { code: 'SYSTEM.FITNESS.CHECK_ABORTED' })
|
|
39
|
+
this.checkId = checkId
|
|
40
|
+
Object.setPrototypeOf(this, CheckAbortedError.prototype)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Result of extracting a code snippet.
|
|
46
|
+
*/
|
|
47
|
+
interface ExtractSnippetResult {
|
|
48
|
+
readonly snippet: string
|
|
49
|
+
readonly contextLines: number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Execution context provided to check execute function.
|
|
54
|
+
*/
|
|
55
|
+
export interface ExecutionContext {
|
|
56
|
+
/** Repository root directory */
|
|
57
|
+
readonly cwd: string
|
|
58
|
+
/** Read a file's contents */
|
|
59
|
+
readonly readFile: (path: string) => Promise<string>
|
|
60
|
+
/** Check if file exists */
|
|
61
|
+
readonly fileExists: (path: string) => Promise<boolean>
|
|
62
|
+
/** The check's stable ID (UUID) */
|
|
63
|
+
readonly checkId: CheckId
|
|
64
|
+
/** The check's human-readable slug (kebab-case) */
|
|
65
|
+
readonly checkSlug: string
|
|
66
|
+
/** Match files using the check's scope or custom patterns */
|
|
67
|
+
readonly matchFiles: (
|
|
68
|
+
patterns?: readonly string[],
|
|
69
|
+
options?: { ignore?: readonly string[] },
|
|
70
|
+
) => Promise<readonly string[]>
|
|
71
|
+
/** Get a PathMatcher for the check's scope */
|
|
72
|
+
readonly getMatcher: () => PathMatcher
|
|
73
|
+
/** Verbose logging enabled */
|
|
74
|
+
readonly verbose: boolean
|
|
75
|
+
/** Log a message (only in verbose mode) */
|
|
76
|
+
readonly log: (message: string) => void
|
|
77
|
+
/** Extract a code snippet with context lines */
|
|
78
|
+
readonly extractSnippet: (
|
|
79
|
+
content: string,
|
|
80
|
+
line: number,
|
|
81
|
+
contextLines?: number,
|
|
82
|
+
) => ExtractSnippetResult
|
|
83
|
+
/** AbortSignal for cancellation support */
|
|
84
|
+
readonly signal?: AbortSignal
|
|
85
|
+
/** Throws if the check has been aborted */
|
|
86
|
+
readonly checkAborted: () => void
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Options for running a check.
|
|
91
|
+
*/
|
|
92
|
+
export interface RunOptions {
|
|
93
|
+
readonly verbose?: boolean
|
|
94
|
+
readonly scopeOverride?: string | ResolvedScope
|
|
95
|
+
readonly additionalExcludes?: readonly string[]
|
|
96
|
+
readonly signal?: AbortSignal
|
|
97
|
+
/** Pre-resolved file paths from per-check target overrides. When set, matchFiles() returns these instead of cache paths. */
|
|
98
|
+
readonly targetFiles?: readonly string[]
|
|
99
|
+
/**
|
|
100
|
+
* Run-wide file exclusion patterns from the project config's
|
|
101
|
+
* `globalExcludes`. Applied to the fileCache fallback path used by
|
|
102
|
+
* scope-empty checks (e.g. `file-length-limit`). Without this filter,
|
|
103
|
+
* a check that declares `scope: { languages: [], concerns: [] }`
|
|
104
|
+
* would scan every prewarmed file regardless of whether the project
|
|
105
|
+
* told us to exclude it — surfacing findings inside `docs/`,
|
|
106
|
+
* `tests/fixtures/`, etc., contrary to user intent.
|
|
107
|
+
*/
|
|
108
|
+
readonly globalExcludes?: readonly string[]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Configuration needed to create execution context.
|
|
113
|
+
*/
|
|
114
|
+
export interface ExecutionContextConfig {
|
|
115
|
+
readonly id: CheckId
|
|
116
|
+
readonly slug: string
|
|
117
|
+
readonly itemType: string
|
|
118
|
+
readonly unit?: string | undefined
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Creates the matchFiles function for the execution context.
|
|
123
|
+
*
|
|
124
|
+
* `globalExcludes` come from the project config's top-level
|
|
125
|
+
* `globalExcludes` array. They are applied ONLY to the fileCache
|
|
126
|
+
* fallback path — the path taken by scope-empty checks. Custom
|
|
127
|
+
* `patterns` arguments are honored as-is (the caller knows what they
|
|
128
|
+
* want), and `targetFiles` from per-check overrides are pre-filtered
|
|
129
|
+
* by `preResolveAllTargets`. Pre-compiled to Minimatch matchers so
|
|
130
|
+
* we don't pay regex compilation per filter call.
|
|
131
|
+
*/
|
|
132
|
+
function createMatchFilesFunction(
|
|
133
|
+
cwd: string,
|
|
134
|
+
matcher: PathMatcher,
|
|
135
|
+
targetFiles?: readonly string[],
|
|
136
|
+
globalExcludes?: readonly string[],
|
|
137
|
+
): (
|
|
138
|
+
patterns?: readonly string[],
|
|
139
|
+
options?: { ignore?: readonly string[] },
|
|
140
|
+
) => Promise<readonly string[]> {
|
|
141
|
+
const compiledGlobalExcludes = globalExcludes && globalExcludes.length > 0
|
|
142
|
+
? globalExcludes.map((pattern) => new Minimatch(pattern, { dot: true }))
|
|
143
|
+
: undefined
|
|
144
|
+
|
|
145
|
+
const applyGlobalExcludes = (files: readonly string[]): readonly string[] => {
|
|
146
|
+
if (!compiledGlobalExcludes) return files
|
|
147
|
+
return files.filter((filePath) => {
|
|
148
|
+
const rel = relative(cwd, filePath)
|
|
149
|
+
return !compiledGlobalExcludes.some((m) => m.match(rel))
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return async (
|
|
154
|
+
patterns?: readonly string[],
|
|
155
|
+
options?: { ignore?: readonly string[] },
|
|
156
|
+
): Promise<readonly string[]> => {
|
|
157
|
+
if (patterns && patterns.length > 0) {
|
|
158
|
+
const customMatcher = PathMatcher.create({
|
|
159
|
+
cwd,
|
|
160
|
+
include: [...patterns],
|
|
161
|
+
exclude: [...(options?.ignore ?? []), ...DEFAULT_EXCLUSION_PATTERNS],
|
|
162
|
+
})
|
|
163
|
+
return customMatcher.files()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Per-check target files take priority over cache.
|
|
167
|
+
// These are already filtered by globalExcludes during target
|
|
168
|
+
// pre-resolution (scope-resolver.ts), so don't re-filter.
|
|
169
|
+
if (targetFiles) {
|
|
170
|
+
return targetFiles
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// When the matcher has no include patterns (checks without targets),
|
|
174
|
+
// fall back to the prewarmed file cache paths. The cache itself
|
|
175
|
+
// honors no exclusion config — that's the layer where globalExcludes
|
|
176
|
+
// must be applied, otherwise scope-empty checks scan every prewarmed
|
|
177
|
+
// file regardless of project intent.
|
|
178
|
+
if (matcher.includePatterns.length === 0) {
|
|
179
|
+
return applyGlobalExcludes(fileCache.paths())
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return matcher.files()
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Creates the execution context for a check.
|
|
188
|
+
*/
|
|
189
|
+
export function createExecutionContext(
|
|
190
|
+
config: ExecutionContextConfig,
|
|
191
|
+
cwd: string,
|
|
192
|
+
matcher: PathMatcher,
|
|
193
|
+
options?: RunOptions,
|
|
194
|
+
): ExecutionContext {
|
|
195
|
+
return {
|
|
196
|
+
cwd,
|
|
197
|
+
checkId: config.id,
|
|
198
|
+
checkSlug: config.slug,
|
|
199
|
+
verbose: options?.verbose ?? false,
|
|
200
|
+
|
|
201
|
+
// @fitness-ignore-next-line unbounded-memory -- size validation via fs.stat() is on the next line; false positive on method name
|
|
202
|
+
/** @throws {SystemError} When the file exceeds 10MB */
|
|
203
|
+
async readFile(filePath: string): Promise<string> {
|
|
204
|
+
const fileStats = await fs.stat(filePath)
|
|
205
|
+
if (fileStats.size > 10_000_000) {
|
|
206
|
+
throw new SystemError(`File too large (${fileStats.size} bytes, max 10MB): ${filePath}`, { code: 'SYSTEM.FITNESS.FILE_TOO_LARGE' })
|
|
207
|
+
}
|
|
208
|
+
return fileCache.get(filePath)
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
fileExists(filePath: string): Promise<boolean> {
|
|
212
|
+
return fileCache.exists(filePath)
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
matchFiles: createMatchFilesFunction(cwd, matcher, options?.targetFiles, options?.globalExcludes),
|
|
216
|
+
|
|
217
|
+
getMatcher(): PathMatcher {
|
|
218
|
+
return matcher
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
log(message: string): void {
|
|
222
|
+
if (options?.verbose) {
|
|
223
|
+
// @fitness-ignore-next-line no-console-log -- Verbose check-level debug output bypasses structured logger for immediate CLI feedback
|
|
224
|
+
// @fitness-ignore-next-line logging-standards -- Verbose check-level debug output bypasses structured logger for immediate CLI feedback
|
|
225
|
+
|
|
226
|
+
console.log(`[${config.slug}] ${message}`)
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
extractSnippet(
|
|
231
|
+
content: string,
|
|
232
|
+
line: number,
|
|
233
|
+
contextLines = 2,
|
|
234
|
+
): ExtractSnippetResult {
|
|
235
|
+
return extractSnippet(content, line, contextLines)
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
...(options?.signal ? { signal: options.signal } : {}),
|
|
239
|
+
|
|
240
|
+
/** @throws {CheckAbortedError} When the check has been aborted */
|
|
241
|
+
checkAborted(): void {
|
|
242
|
+
if (options?.signal?.aborted) {
|
|
243
|
+
throw new CheckAbortedError(config.slug)
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview FileAccessor implementation for lazy file loading
|
|
3
|
+
*
|
|
4
|
+
* Provides lazy-loading file access with LRU caching for
|
|
5
|
+
* analyzeAll mode checks that need to correlate across files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'node:fs/promises'
|
|
9
|
+
|
|
10
|
+
import { ValidationError , applyContentFilter } from '@opensip-tools/core'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
import { fileCache } from './file-cache.js'
|
|
14
|
+
|
|
15
|
+
import type { FileAccessor } from './check-config.js'
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// LRU CACHE
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
class LRUCache<K, V> {
|
|
22
|
+
private readonly cache: Map<K, V>
|
|
23
|
+
private readonly capacity: number
|
|
24
|
+
|
|
25
|
+
constructor(capacity: number) {
|
|
26
|
+
this.cache = new Map()
|
|
27
|
+
this.capacity = capacity
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get(key: K): V | undefined {
|
|
31
|
+
// in-memory: single-threaded Node.js access pattern
|
|
32
|
+
const value = this.cache.get(key)
|
|
33
|
+
if (value !== undefined) {
|
|
34
|
+
this.cache.delete(key)
|
|
35
|
+
this.cache.set(key, value)
|
|
36
|
+
}
|
|
37
|
+
return value
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
set(key: K, value: V): void {
|
|
41
|
+
if (this.cache.has(key)) {
|
|
42
|
+
this.cache.delete(key)
|
|
43
|
+
} else if (this.cache.size >= this.capacity) {
|
|
44
|
+
const firstKey = this.cache.keys().next().value
|
|
45
|
+
if (firstKey !== undefined) {
|
|
46
|
+
this.cache.delete(firstKey)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.cache.set(key, value)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get size(): number {
|
|
53
|
+
return this.cache.size
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
clear(): void {
|
|
57
|
+
this.cache.clear()
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// =============================================================================
|
|
62
|
+
// FILE ACCESSOR IMPLEMENTATION
|
|
63
|
+
// =============================================================================
|
|
64
|
+
|
|
65
|
+
/** Options for creating a FileAccessor instance. */
|
|
66
|
+
export interface FileAccessorOptions {
|
|
67
|
+
readonly cacheCapacity?: number
|
|
68
|
+
readonly signal?: AbortSignal
|
|
69
|
+
/**
|
|
70
|
+
* Content filtering applied before returning file content. See
|
|
71
|
+
* BaseCheckConfig.contentFilter for the canonical doc.
|
|
72
|
+
*/
|
|
73
|
+
readonly contentFilter?: 'raw' | 'strip-strings' | 'strip-strings-and-comments'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const DEFAULT_CACHE_CAPACITY = 100
|
|
77
|
+
|
|
78
|
+
/** FileAccessor implementation with LRU caching and abort signal support. */
|
|
79
|
+
class FileAccessorImpl implements FileAccessor {
|
|
80
|
+
readonly paths: readonly string[]
|
|
81
|
+
private readonly cache: LRUCache<string, string>
|
|
82
|
+
private readonly pathSet: Set<string>
|
|
83
|
+
private readonly signal?: AbortSignal
|
|
84
|
+
private readonly contentFilterMode?: FileAccessorOptions['contentFilter']
|
|
85
|
+
|
|
86
|
+
constructor(filePaths: readonly string[], options: FileAccessorOptions = {}) {
|
|
87
|
+
this.paths = filePaths
|
|
88
|
+
this.pathSet = new Set(filePaths)
|
|
89
|
+
this.cache = new LRUCache(options.cacheCapacity ?? DEFAULT_CACHE_CAPACITY)
|
|
90
|
+
this.signal = options.signal
|
|
91
|
+
this.contentFilterMode = options.contentFilter
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async read(filePath: string): Promise<string> {
|
|
95
|
+
// in-memory: single-threaded Node.js access pattern
|
|
96
|
+
// @fitness-ignore-next-line detached-promises -- throwIfAborted() is synchronous, optional chaining is not a detached promise
|
|
97
|
+
this.signal?.throwIfAborted()
|
|
98
|
+
|
|
99
|
+
if (!this.pathSet.has(filePath)) {
|
|
100
|
+
// @fitness-ignore-next-line result-pattern-consistency -- internal method, exceptions propagate to public Result boundary
|
|
101
|
+
throw new ValidationError(
|
|
102
|
+
`File path not in matched set: ${filePath}. ` +
|
|
103
|
+
`Only paths from the 'paths' property can be read.`,
|
|
104
|
+
{ code: 'VALIDATION.FITNESS.PATH_NOT_IN_SET' },
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const cached = this.cache.get(filePath)
|
|
109
|
+
if (cached !== undefined) {
|
|
110
|
+
return cached
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Try global file cache first (populated by prewarm or previous checks)
|
|
114
|
+
let content = fileCache.getCached(filePath)
|
|
115
|
+
if (content === undefined) {
|
|
116
|
+
const fileStats = await fs.stat(filePath)
|
|
117
|
+
if (fileStats.size > 10_000_000) {
|
|
118
|
+
// @fitness-ignore-next-line result-pattern-consistency -- infrastructure boundary guard, not domain logic
|
|
119
|
+
throw new ValidationError(
|
|
120
|
+
`File too large (${fileStats.size} bytes, max 10MB): ${filePath}`,
|
|
121
|
+
{ code: 'VALIDATION.FITNESS.FILE_TOO_LARGE' },
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
content = await fs.readFile(filePath, 'utf8')
|
|
125
|
+
}
|
|
126
|
+
// Dispatch through the LanguageAdapter for the file's extension.
|
|
127
|
+
// See languages/content-filter-dispatch.ts.
|
|
128
|
+
content = applyContentFilter(filePath, content, this.contentFilterMode ?? 'none')
|
|
129
|
+
this.cache.set(filePath, content)
|
|
130
|
+
return content
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async readMany(filePaths: readonly string[]): Promise<Map<string, string>> {
|
|
134
|
+
// in-memory: single-threaded Node.js access pattern
|
|
135
|
+
const results = new Map<string, string>()
|
|
136
|
+
// @fitness-ignore-next-line no-unbounded-concurrency -- bounded by FileAccessor path set; LRU cache limits memory
|
|
137
|
+
const entries = await Promise.all(
|
|
138
|
+
filePaths.map(async (filePath) => {
|
|
139
|
+
const content = await this.read(filePath)
|
|
140
|
+
return [filePath, content] as const
|
|
141
|
+
}),
|
|
142
|
+
)
|
|
143
|
+
for (const [filePath, content] of entries) {
|
|
144
|
+
results.set(filePath, content)
|
|
145
|
+
}
|
|
146
|
+
return results
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async readAll(): Promise<Map<string, string>> {
|
|
150
|
+
return this.readMany(this.paths)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Number of files currently held in the LRU cache. */
|
|
154
|
+
get cachedCount(): number {
|
|
155
|
+
return this.cache.size
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Evict all entries from the file cache. */
|
|
159
|
+
clearCache(): void {
|
|
160
|
+
this.cache.clear()
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Create a FileAccessor for lazy-loading files with LRU caching. */
|
|
165
|
+
// @fitness-ignore-next-line result-pattern-consistency -- factory function, cannot fail in domain-meaningful ways
|
|
166
|
+
export function createFileAccessor(
|
|
167
|
+
filePaths: readonly string[],
|
|
168
|
+
options: FileAccessorOptions = {},
|
|
169
|
+
): FileAccessor {
|
|
170
|
+
return new FileAccessorImpl(filePaths, options)
|
|
171
|
+
}
|