@opensip-cli/checks-typescript 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/__tests__/all-checks-execute.test.d.ts +12 -0
- package/dist/__tests__/all-checks-execute.test.d.ts.map +1 -0
- package/dist/__tests__/all-checks-execute.test.js +846 -0
- package/dist/__tests__/all-checks-execute.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-2.test.d.ts +9 -0
- package/dist/__tests__/behavior-fixtures-2.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-2.test.js +625 -0
- package/dist/__tests__/behavior-fixtures-2.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-3.test.d.ts +7 -0
- package/dist/__tests__/behavior-fixtures-3.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-3.test.js +658 -0
- package/dist/__tests__/behavior-fixtures-3.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-4.test.d.ts +8 -0
- package/dist/__tests__/behavior-fixtures-4.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-4.test.js +590 -0
- package/dist/__tests__/behavior-fixtures-4.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-5.test.d.ts +7 -0
- package/dist/__tests__/behavior-fixtures-5.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-5.test.js +548 -0
- package/dist/__tests__/behavior-fixtures-5.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-6.test.d.ts +18 -0
- package/dist/__tests__/behavior-fixtures-6.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-6.test.js +1700 -0
- package/dist/__tests__/behavior-fixtures-6.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures.test.d.ts +10 -0
- package/dist/__tests__/behavior-fixtures.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures.test.js +812 -0
- package/dist/__tests__/behavior-fixtures.test.js.map +1 -0
- package/dist/__tests__/branch-fixtures-2.test.d.ts +6 -0
- package/dist/__tests__/branch-fixtures-2.test.d.ts.map +1 -0
- package/dist/__tests__/branch-fixtures-2.test.js +1369 -0
- package/dist/__tests__/branch-fixtures-2.test.js.map +1 -0
- package/dist/__tests__/branch-fixtures-3.test.d.ts +7 -0
- package/dist/__tests__/branch-fixtures-3.test.d.ts.map +1 -0
- package/dist/__tests__/branch-fixtures-3.test.js +877 -0
- package/dist/__tests__/branch-fixtures-3.test.js.map +1 -0
- package/dist/__tests__/branch-fixtures.test.d.ts +6 -0
- package/dist/__tests__/branch-fixtures.test.d.ts.map +1 -0
- package/dist/__tests__/branch-fixtures.test.js +1072 -0
- package/dist/__tests__/branch-fixtures.test.js.map +1 -0
- package/dist/__tests__/checks.test.d.ts +2 -0
- package/dist/__tests__/checks.test.d.ts.map +1 -0
- package/dist/__tests__/checks.test.js +39 -0
- package/dist/__tests__/checks.test.js.map +1 -0
- package/dist/__tests__/fixture-coverage.allowlist.d.ts +19 -0
- package/dist/__tests__/fixture-coverage.allowlist.d.ts.map +1 -0
- package/dist/__tests__/fixture-coverage.allowlist.js +27 -0
- package/dist/__tests__/fixture-coverage.allowlist.js.map +1 -0
- package/dist/__tests__/fixture-coverage.test.d.ts +13 -0
- package/dist/__tests__/fixture-coverage.test.d.ts.map +1 -0
- package/dist/__tests__/fixture-coverage.test.js +57 -0
- package/dist/__tests__/fixture-coverage.test.js.map +1 -0
- package/dist/__tests__/no-bootstrap-tool-import.test.d.ts +2 -0
- package/dist/__tests__/no-bootstrap-tool-import.test.d.ts.map +1 -0
- package/dist/__tests__/no-bootstrap-tool-import.test.js +75 -0
- package/dist/__tests__/no-bootstrap-tool-import.test.js.map +1 -0
- package/dist/__tests__/phantom-dependency-detection.test.d.ts +12 -0
- package/dist/__tests__/phantom-dependency-detection.test.d.ts.map +1 -0
- package/dist/__tests__/phantom-dependency-detection.test.js +112 -0
- package/dist/__tests__/phantom-dependency-detection.test.js.map +1 -0
- package/dist/__tests__/typescript-frontend.test.d.ts +8 -0
- package/dist/__tests__/typescript-frontend.test.d.ts.map +1 -0
- package/dist/__tests__/typescript-frontend.test.js +57 -0
- package/dist/__tests__/typescript-frontend.test.js.map +1 -0
- package/dist/checks/architecture/circular-import-detection.d.ts +14 -0
- package/dist/checks/architecture/circular-import-detection.d.ts.map +1 -0
- package/dist/checks/architecture/circular-import-detection.js +55 -0
- package/dist/checks/architecture/circular-import-detection.js.map +1 -0
- package/dist/checks/architecture/contracts-schema-consistency.d.ts +11 -0
- package/dist/checks/architecture/contracts-schema-consistency.d.ts.map +1 -0
- package/dist/checks/architecture/contracts-schema-consistency.js +75 -0
- package/dist/checks/architecture/contracts-schema-consistency.js.map +1 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.d.ts +12 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.d.ts.map +1 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.js +92 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.js.map +1 -0
- package/dist/checks/architecture/index.d.ts +10 -0
- package/dist/checks/architecture/index.d.ts.map +1 -0
- package/dist/checks/architecture/index.js +10 -0
- package/dist/checks/architecture/index.js.map +1 -0
- package/dist/checks/architecture/missing-type-exports.d.ts +13 -0
- package/dist/checks/architecture/missing-type-exports.d.ts.map +1 -0
- package/dist/checks/architecture/missing-type-exports.js +245 -0
- package/dist/checks/architecture/missing-type-exports.js.map +1 -0
- package/dist/checks/architecture/module-coupling-fan-out.d.ts +20 -0
- package/dist/checks/architecture/module-coupling-fan-out.d.ts.map +1 -0
- package/dist/checks/architecture/module-coupling-fan-out.js +120 -0
- package/dist/checks/architecture/module-coupling-fan-out.js.map +1 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.d.ts +38 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.d.ts.map +1 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.js +95 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.js.map +1 -0
- package/dist/checks/architecture/package-json-exports-field.d.ts +10 -0
- package/dist/checks/architecture/package-json-exports-field.d.ts.map +1 -0
- package/dist/checks/architecture/package-json-exports-field.js +56 -0
- package/dist/checks/architecture/package-json-exports-field.js.map +1 -0
- package/dist/checks/architecture/phantom-dependency-detection.d.ts +22 -0
- package/dist/checks/architecture/phantom-dependency-detection.d.ts.map +1 -0
- package/dist/checks/architecture/phantom-dependency-detection.js +330 -0
- package/dist/checks/architecture/phantom-dependency-detection.js.map +1 -0
- package/dist/checks/architecture/tsconfig-extends-validation.d.ts +10 -0
- package/dist/checks/architecture/tsconfig-extends-validation.d.ts.map +1 -0
- package/dist/checks/architecture/tsconfig-extends-validation.js +78 -0
- package/dist/checks/architecture/tsconfig-extends-validation.js.map +1 -0
- package/dist/checks/index.d.ts +6 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +6 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/checks/quality/api/api-contract-validation.d.ts +15 -0
- package/dist/checks/quality/api/api-contract-validation.d.ts.map +1 -0
- package/dist/checks/quality/api/api-contract-validation.js +316 -0
- package/dist/checks/quality/api/api-contract-validation.js.map +1 -0
- package/dist/checks/quality/api/api-response-validation.d.ts +14 -0
- package/dist/checks/quality/api/api-response-validation.d.ts.map +1 -0
- package/dist/checks/quality/api/api-response-validation.js +209 -0
- package/dist/checks/quality/api/api-response-validation.js.map +1 -0
- package/dist/checks/quality/api/fastify-route-validation.d.ts +14 -0
- package/dist/checks/quality/api/fastify-route-validation.d.ts.map +1 -0
- package/dist/checks/quality/api/fastify-route-validation.js +298 -0
- package/dist/checks/quality/api/fastify-route-validation.js.map +1 -0
- package/dist/checks/quality/api/fastify-schema-coverage.d.ts +11 -0
- package/dist/checks/quality/api/fastify-schema-coverage.d.ts.map +1 -0
- package/dist/checks/quality/api/fastify-schema-coverage.js +261 -0
- package/dist/checks/quality/api/fastify-schema-coverage.js.map +1 -0
- package/dist/checks/quality/api/index.d.ts +5 -0
- package/dist/checks/quality/api/index.d.ts.map +1 -0
- package/dist/checks/quality/api/index.js +5 -0
- package/dist/checks/quality/api/index.js.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts +32 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.js +451 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.js.map +1 -0
- package/dist/checks/quality/code-structure/index.d.ts +3 -0
- package/dist/checks/quality/code-structure/index.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/index.js +3 -0
- package/dist/checks/quality/code-structure/index.js.map +1 -0
- package/dist/checks/quality/code-structure/no-any-types.d.ts +13 -0
- package/dist/checks/quality/code-structure/no-any-types.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/no-any-types.js +116 -0
- package/dist/checks/quality/code-structure/no-any-types.js.map +1 -0
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.d.ts +15 -0
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js +51 -0
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js.map +1 -0
- package/dist/checks/quality/data-integrity/array-validation.d.ts +16 -0
- package/dist/checks/quality/data-integrity/array-validation.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/array-validation.js +508 -0
- package/dist/checks/quality/data-integrity/array-validation.js.map +1 -0
- package/dist/checks/quality/data-integrity/database-index-coverage.d.ts +14 -0
- package/dist/checks/quality/data-integrity/database-index-coverage.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/database-index-coverage.js +235 -0
- package/dist/checks/quality/data-integrity/database-index-coverage.js.map +1 -0
- package/dist/checks/quality/data-integrity/database-schema-validation.d.ts +16 -0
- package/dist/checks/quality/data-integrity/database-schema-validation.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/database-schema-validation.js +328 -0
- package/dist/checks/quality/data-integrity/database-schema-validation.js.map +1 -0
- package/dist/checks/quality/data-integrity/in-memory-repository-detection.d.ts +14 -0
- package/dist/checks/quality/data-integrity/in-memory-repository-detection.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/in-memory-repository-detection.js +157 -0
- package/dist/checks/quality/data-integrity/in-memory-repository-detection.js.map +1 -0
- package/dist/checks/quality/data-integrity/index.d.ts +8 -0
- package/dist/checks/quality/data-integrity/index.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/index.js +8 -0
- package/dist/checks/quality/data-integrity/index.js.map +1 -0
- package/dist/checks/quality/data-integrity/missing-input-validation.d.ts +12 -0
- package/dist/checks/quality/data-integrity/missing-input-validation.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/missing-input-validation.js +180 -0
- package/dist/checks/quality/data-integrity/missing-input-validation.js.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety.d.ts +33 -0
- package/dist/checks/quality/data-integrity/null-safety.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety.js +766 -0
- package/dist/checks/quality/data-integrity/null-safety.js.map +1 -0
- package/dist/checks/quality/data-integrity/numeric-validation.d.ts +12 -0
- package/dist/checks/quality/data-integrity/numeric-validation.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/numeric-validation.js +409 -0
- package/dist/checks/quality/data-integrity/numeric-validation.js.map +1 -0
- package/dist/checks/quality/frontend/a11y-form-labels.d.ts +14 -0
- package/dist/checks/quality/frontend/a11y-form-labels.d.ts.map +1 -0
- package/dist/checks/quality/frontend/a11y-form-labels.js +93 -0
- package/dist/checks/quality/frontend/a11y-form-labels.js.map +1 -0
- package/dist/checks/quality/frontend/a11y-semantic-html.d.ts +14 -0
- package/dist/checks/quality/frontend/a11y-semantic-html.d.ts.map +1 -0
- package/dist/checks/quality/frontend/a11y-semantic-html.js +88 -0
- package/dist/checks/quality/frontend/a11y-semantic-html.js.map +1 -0
- package/dist/checks/quality/frontend/index.d.ts +4 -0
- package/dist/checks/quality/frontend/index.d.ts.map +1 -0
- package/dist/checks/quality/frontend/index.js +4 -0
- package/dist/checks/quality/frontend/index.js.map +1 -0
- package/dist/checks/quality/frontend/test-only-frontend-modules.d.ts +13 -0
- package/dist/checks/quality/frontend/test-only-frontend-modules.d.ts.map +1 -0
- package/dist/checks/quality/frontend/test-only-frontend-modules.js +159 -0
- package/dist/checks/quality/frontend/test-only-frontend-modules.js.map +1 -0
- package/dist/checks/quality/incomplete-regex-escaping.d.ts +13 -0
- package/dist/checks/quality/incomplete-regex-escaping.d.ts.map +1 -0
- package/dist/checks/quality/incomplete-regex-escaping.js +207 -0
- package/dist/checks/quality/incomplete-regex-escaping.js.map +1 -0
- package/dist/checks/quality/index.d.ts +11 -0
- package/dist/checks/quality/index.d.ts.map +1 -0
- package/dist/checks/quality/index.js +11 -0
- package/dist/checks/quality/index.js.map +1 -0
- package/dist/checks/quality/linting/index.d.ts +2 -0
- package/dist/checks/quality/linting/index.d.ts.map +1 -0
- package/dist/checks/quality/linting/index.js +2 -0
- package/dist/checks/quality/linting/index.js.map +1 -0
- package/dist/checks/quality/linting/typescript-frontend.d.ts +25 -0
- package/dist/checks/quality/linting/typescript-frontend.d.ts.map +1 -0
- package/dist/checks/quality/linting/typescript-frontend.js +159 -0
- package/dist/checks/quality/linting/typescript-frontend.js.map +1 -0
- package/dist/checks/quality/observability/index.d.ts +5 -0
- package/dist/checks/quality/observability/index.d.ts.map +1 -0
- package/dist/checks/quality/observability/index.js +5 -0
- package/dist/checks/quality/observability/index.js.map +1 -0
- package/dist/checks/quality/observability/logger-event-name-format.d.ts +12 -0
- package/dist/checks/quality/observability/logger-event-name-format.d.ts.map +1 -0
- package/dist/checks/quality/observability/logger-event-name-format.js +124 -0
- package/dist/checks/quality/observability/logger-event-name-format.js.map +1 -0
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.d.ts +5 -0
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.d.ts.map +1 -0
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.js +77 -0
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.d.ts +11 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.js +107 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.d.ts +12 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.js +94 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/analyzer.d.ts +13 -0
- package/dist/checks/quality/observability/observability-coverage/analyzer.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/analyzer.js +117 -0
- package/dist/checks/quality/observability/observability-coverage/analyzer.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/index.d.ts +4 -0
- package/dist/checks/quality/observability/observability-coverage/index.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/index.js +4 -0
- package/dist/checks/quality/observability/observability-coverage/index.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/logger-detector.d.ts +29 -0
- package/dist/checks/quality/observability/observability-coverage/logger-detector.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/logger-detector.js +111 -0
- package/dist/checks/quality/observability/observability-coverage/logger-detector.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/types.d.ts +64 -0
- package/dist/checks/quality/observability/observability-coverage/types.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/types.js +6 -0
- package/dist/checks/quality/observability/observability-coverage/types.js.map +1 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.d.ts +22 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.d.ts.map +1 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.js +212 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.js.map +1 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.test.d.ts +11 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.test.d.ts.map +1 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.test.js +46 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.test.js.map +1 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.d.ts +14 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.d.ts.map +1 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js +61 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js.map +1 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.d.ts +26 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.d.ts.map +1 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.js +410 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.js.map +1 -0
- package/dist/checks/quality/patterns/dispose-pattern-completeness.d.ts +13 -0
- package/dist/checks/quality/patterns/dispose-pattern-completeness.d.ts.map +1 -0
- package/dist/checks/quality/patterns/dispose-pattern-completeness.js +220 -0
- package/dist/checks/quality/patterns/dispose-pattern-completeness.js.map +1 -0
- package/dist/checks/quality/patterns/error-handling-quality.d.ts +17 -0
- package/dist/checks/quality/patterns/error-handling-quality.d.ts.map +1 -0
- package/dist/checks/quality/patterns/error-handling-quality.js +335 -0
- package/dist/checks/quality/patterns/error-handling-quality.js.map +1 -0
- package/dist/checks/quality/patterns/index.d.ts +10 -0
- package/dist/checks/quality/patterns/index.d.ts.map +1 -0
- package/dist/checks/quality/patterns/index.js +10 -0
- package/dist/checks/quality/patterns/index.js.map +1 -0
- package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.d.ts +16 -0
- package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.d.ts.map +1 -0
- package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.js +205 -0
- package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.js.map +1 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.d.ts +16 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.d.ts.map +1 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.js +328 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.js.map +1 -0
- package/dist/checks/quality/patterns/silent-early-returns.d.ts +23 -0
- package/dist/checks/quality/patterns/silent-early-returns.d.ts.map +1 -0
- package/dist/checks/quality/patterns/silent-early-returns.js +266 -0
- package/dist/checks/quality/patterns/silent-early-returns.js.map +1 -0
- package/dist/checks/quality/patterns/stream-buffer-size-limits.d.ts +13 -0
- package/dist/checks/quality/patterns/stream-buffer-size-limits.d.ts.map +1 -0
- package/dist/checks/quality/patterns/stream-buffer-size-limits.js +163 -0
- package/dist/checks/quality/patterns/stream-buffer-size-limits.js.map +1 -0
- package/dist/checks/quality/patterns/throws-documentation.d.ts +23 -0
- package/dist/checks/quality/patterns/throws-documentation.d.ts.map +1 -0
- package/dist/checks/quality/patterns/throws-documentation.js +519 -0
- package/dist/checks/quality/patterns/throws-documentation.js.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition.d.ts +48 -0
- package/dist/checks/quality/patterns/toctou-race-condition.d.ts.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition.js +639 -0
- package/dist/checks/quality/patterns/toctou-race-condition.js.map +1 -0
- package/dist/checks/quality/stubbed-implementation-detection.d.ts +24 -0
- package/dist/checks/quality/stubbed-implementation-detection.d.ts.map +1 -0
- package/dist/checks/quality/stubbed-implementation-detection.js +355 -0
- package/dist/checks/quality/stubbed-implementation-detection.js.map +1 -0
- package/dist/checks/quality/unused-config-options.d.ts +12 -0
- package/dist/checks/quality/unused-config-options.d.ts.map +1 -0
- package/dist/checks/quality/unused-config-options.js +245 -0
- package/dist/checks/quality/unused-config-options.js.map +1 -0
- package/dist/checks/resilience/__tests__/callback-invocation-safe.test.d.ts +2 -0
- package/dist/checks/resilience/__tests__/callback-invocation-safe.test.d.ts.map +1 -0
- package/dist/checks/resilience/__tests__/callback-invocation-safe.test.js +79 -0
- package/dist/checks/resilience/__tests__/callback-invocation-safe.test.js.map +1 -0
- package/dist/checks/resilience/__tests__/context-leakage-fp.test.d.ts +12 -0
- package/dist/checks/resilience/__tests__/context-leakage-fp.test.d.ts.map +1 -0
- package/dist/checks/resilience/__tests__/context-leakage-fp.test.js +34 -0
- package/dist/checks/resilience/__tests__/context-leakage-fp.test.js.map +1 -0
- package/dist/checks/resilience/__tests__/context-mutation.test.d.ts +11 -0
- package/dist/checks/resilience/__tests__/context-mutation.test.d.ts.map +1 -0
- package/dist/checks/resilience/__tests__/context-mutation.test.js +54 -0
- package/dist/checks/resilience/__tests__/context-mutation.test.js.map +1 -0
- package/dist/checks/resilience/callback-invocation-safe.d.ts +34 -0
- package/dist/checks/resilience/callback-invocation-safe.d.ts.map +1 -0
- package/dist/checks/resilience/callback-invocation-safe.js +247 -0
- package/dist/checks/resilience/callback-invocation-safe.js.map +1 -0
- package/dist/checks/resilience/context-leakage.d.ts +25 -0
- package/dist/checks/resilience/context-leakage.d.ts.map +1 -0
- package/dist/checks/resilience/context-leakage.js +435 -0
- package/dist/checks/resilience/context-leakage.js.map +1 -0
- package/dist/checks/resilience/context-mutation.d.ts +21 -0
- package/dist/checks/resilience/context-mutation.d.ts.map +1 -0
- package/dist/checks/resilience/context-mutation.js +368 -0
- package/dist/checks/resilience/context-mutation.js.map +1 -0
- package/dist/checks/resilience/detached-promises.d.ts +40 -0
- package/dist/checks/resilience/detached-promises.d.ts.map +1 -0
- package/dist/checks/resilience/detached-promises.js +646 -0
- package/dist/checks/resilience/detached-promises.js.map +1 -0
- package/dist/checks/resilience/index.d.ts +7 -0
- package/dist/checks/resilience/index.d.ts.map +1 -0
- package/dist/checks/resilience/index.js +7 -0
- package/dist/checks/resilience/index.js.map +1 -0
- package/dist/checks/resilience/no-raw-fetch.d.ts +11 -0
- package/dist/checks/resilience/no-raw-fetch.d.ts.map +1 -0
- package/dist/checks/resilience/no-raw-fetch.js +110 -0
- package/dist/checks/resilience/no-raw-fetch.js.map +1 -0
- package/dist/checks/resilience/no-unbounded-concurrency.d.ts +11 -0
- package/dist/checks/resilience/no-unbounded-concurrency.d.ts.map +1 -0
- package/dist/checks/resilience/no-unbounded-concurrency.js +117 -0
- package/dist/checks/resilience/no-unbounded-concurrency.js.map +1 -0
- package/dist/checks/security/__tests__/sql-injection.test.d.ts +17 -0
- package/dist/checks/security/__tests__/sql-injection.test.d.ts.map +1 -0
- package/dist/checks/security/__tests__/sql-injection.test.js +97 -0
- package/dist/checks/security/__tests__/sql-injection.test.js.map +1 -0
- package/dist/checks/security/index.d.ts +4 -0
- package/dist/checks/security/index.d.ts.map +1 -0
- package/dist/checks/security/index.js +4 -0
- package/dist/checks/security/index.js.map +1 -0
- package/dist/checks/security/input-sanitization.d.ts +20 -0
- package/dist/checks/security/input-sanitization.d.ts.map +1 -0
- package/dist/checks/security/input-sanitization.js +255 -0
- package/dist/checks/security/input-sanitization.js.map +1 -0
- package/dist/checks/security/sql-injection.d.ts +24 -0
- package/dist/checks/security/sql-injection.d.ts.map +1 -0
- package/dist/checks/security/sql-injection.js +330 -0
- package/dist/checks/security/sql-injection.js.map +1 -0
- package/dist/checks/security/unsafe-secret-comparison.d.ts +17 -0
- package/dist/checks/security/unsafe-secret-comparison.d.ts.map +1 -0
- package/dist/checks/security/unsafe-secret-comparison.js +227 -0
- package/dist/checks/security/unsafe-secret-comparison.js.map +1 -0
- package/dist/checks/testing/index.d.ts +2 -0
- package/dist/checks/testing/index.d.ts.map +1 -0
- package/dist/checks/testing/index.js +2 -0
- package/dist/checks/testing/index.js.map +1 -0
- package/dist/checks/testing/mock-implementations-in-production.d.ts +12 -0
- package/dist/checks/testing/mock-implementations-in-production.d.ts.map +1 -0
- package/dist/checks/testing/mock-implementations-in-production.js +211 -0
- package/dist/checks/testing/mock-implementations-in-production.js.map +1 -0
- package/dist/display/architecture.d.ts +9 -0
- package/dist/display/architecture.d.ts.map +1 -0
- package/dist/display/architecture.js +18 -0
- package/dist/display/architecture.js.map +1 -0
- package/dist/display/index.d.ts +20 -0
- package/dist/display/index.d.ts.map +1 -0
- package/dist/display/index.js +30 -0
- package/dist/display/index.js.map +1 -0
- package/dist/display/quality.d.ts +7 -0
- package/dist/display/quality.d.ts.map +1 -0
- package/dist/display/quality.js +39 -0
- package/dist/display/quality.js.map +1 -0
- package/dist/display/resilience.d.ts +7 -0
- package/dist/display/resilience.d.ts.map +1 -0
- package/dist/display/resilience.js +13 -0
- package/dist/display/resilience.js.map +1 -0
- package/dist/display/security-testing.d.ts +9 -0
- package/dist/display/security-testing.d.ts.map +1 -0
- package/dist/display/security-testing.js +14 -0
- package/dist/display/security-testing.js.map +1 -0
- package/dist/display/types.d.ts +6 -0
- package/dist/display/types.d.ts.map +1 -0
- package/dist/display/types.js +6 -0
- package/dist/display/types.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// @fitness-ignore-file correlation-id-coverage -- Fitness check implementation, not an API handler
|
|
2
|
+
// @fitness-ignore-file duplicate-utility-functions -- Check-specific helpers (getFunctionName, isHandlerFunction) use check-local types; extracting would couple independent checks
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview API Contract Validation Check
|
|
5
|
+
*
|
|
6
|
+
* Ensures API handlers have proper validation, typed responses, and error handling.
|
|
7
|
+
* Validates that request/response contracts are enforced through types and schemas.
|
|
8
|
+
*/
|
|
9
|
+
import { defineCheck, isAPIFile } from '@opensip-cli/fitness';
|
|
10
|
+
import { getSharedSourceFile } from '@opensip-cli/lang-typescript';
|
|
11
|
+
import * as ts from 'typescript';
|
|
12
|
+
/**
|
|
13
|
+
* Validation detection patterns
|
|
14
|
+
*/
|
|
15
|
+
const VALIDATION_METHOD_NAMES = new Set([
|
|
16
|
+
'parse',
|
|
17
|
+
'safeParse',
|
|
18
|
+
'validate',
|
|
19
|
+
'validateRequest',
|
|
20
|
+
'validateBody',
|
|
21
|
+
'validateParams',
|
|
22
|
+
'validateQuery',
|
|
23
|
+
]);
|
|
24
|
+
/**
|
|
25
|
+
* Handler name patterns
|
|
26
|
+
*/
|
|
27
|
+
const HANDLER_NAME_PATTERNS = [/handler$/i, /^handle[A-Z]/, /^process[A-Z]/];
|
|
28
|
+
/**
|
|
29
|
+
* Names that match HANDLER_NAME_PATTERNS but are themselves error
|
|
30
|
+
* translators / classifiers — they're called from inside a catch block,
|
|
31
|
+
* so requiring try-catch around their body is error-handling-inception.
|
|
32
|
+
* Match: handleXxxError, handleStoreError, processError.
|
|
33
|
+
*/
|
|
34
|
+
const ERROR_HANDLER_NAME_PATTERNS = [/^handle[A-Z].*Error$/, /^process[A-Z].*Error$/];
|
|
35
|
+
/**
|
|
36
|
+
* Get function name from node
|
|
37
|
+
* @returns {string} The function name or '<anonymous>' if not found
|
|
38
|
+
*/
|
|
39
|
+
function getFunctionName(node) {
|
|
40
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
41
|
+
return node.name.text;
|
|
42
|
+
}
|
|
43
|
+
if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
44
|
+
return node.name.text;
|
|
45
|
+
}
|
|
46
|
+
// Combined condition for arrow function with variable declaration
|
|
47
|
+
if (ts.isArrowFunction(node) &&
|
|
48
|
+
ts.isVariableDeclaration(node.parent) &&
|
|
49
|
+
ts.isIdentifier(node.parent.name)) {
|
|
50
|
+
return node.parent.name.text;
|
|
51
|
+
}
|
|
52
|
+
return '<anonymous>';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if function has Express handler signature
|
|
56
|
+
* @returns {boolean} True if the function has Express handler signature (req, res)
|
|
57
|
+
*/
|
|
58
|
+
function getParamIdentifierName(p) {
|
|
59
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
60
|
+
return p && ts.isIdentifier(p.name) ? p.name.text : '';
|
|
61
|
+
}
|
|
62
|
+
function hasExpressHandlerSignature(node) {
|
|
63
|
+
const params = node.parameters;
|
|
64
|
+
if (params.length < 2)
|
|
65
|
+
return false;
|
|
66
|
+
const param1 = getParamIdentifierName(params[0]);
|
|
67
|
+
const param2 = getParamIdentifierName(params[1]);
|
|
68
|
+
return (param1 === 'req' || param1 === 'request') && (param2 === 'res' || param2 === 'response');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if function name suggests it's a handler
|
|
72
|
+
* @returns {boolean} True if the function appears to be a handler
|
|
73
|
+
*/
|
|
74
|
+
function isHandlerFunction(name, filePath, node) {
|
|
75
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
76
|
+
if (!isAPIFile(filePath))
|
|
77
|
+
return false;
|
|
78
|
+
// Check if exported
|
|
79
|
+
let isExported = false;
|
|
80
|
+
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
|
|
81
|
+
isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
82
|
+
}
|
|
83
|
+
else if (ts.isArrowFunction(node) && ts.isVariableDeclaration(node.parent)) {
|
|
84
|
+
const varStatement = node.parent.parent.parent;
|
|
85
|
+
if (ts.isVariableStatement(varStatement)) {
|
|
86
|
+
isExported =
|
|
87
|
+
varStatement.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Other node types remain isExported = false (initial value)
|
|
92
|
+
}
|
|
93
|
+
if (!isExported)
|
|
94
|
+
return false;
|
|
95
|
+
return HANDLER_NAME_PATTERNS.some((pattern) => pattern.test(name));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if function should be skipped
|
|
99
|
+
* @returns {boolean} True if the function should be skipped in validation
|
|
100
|
+
*/
|
|
101
|
+
function shouldSkipFunction(node, functionName, filePath) {
|
|
102
|
+
if (ts.isMethodDeclaration(node)) {
|
|
103
|
+
const isPrivate = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword);
|
|
104
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
105
|
+
if (isPrivate)
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
const isExpressHandler = hasExpressHandlerSignature(node);
|
|
109
|
+
if (functionName === '<anonymous>' && isExpressHandler)
|
|
110
|
+
return true;
|
|
111
|
+
if (!isExpressHandler && !isHandlerFunction(functionName, filePath, node))
|
|
112
|
+
return true;
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check if function has request parameter
|
|
117
|
+
* @returns {boolean} True if the function has a request parameter
|
|
118
|
+
*/
|
|
119
|
+
function hasRequestParameter(node) {
|
|
120
|
+
return node.parameters.some((param) => {
|
|
121
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
122
|
+
if (!ts.isIdentifier(param.name))
|
|
123
|
+
return false;
|
|
124
|
+
const name = param.name.text;
|
|
125
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
126
|
+
return name === 'req' || name === 'request' || name.includes('Request');
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check if a call expression is a validation method call
|
|
131
|
+
*/
|
|
132
|
+
function isValidationMethodCall(n) {
|
|
133
|
+
if (ts.isPropertyAccessExpression(n.expression)) {
|
|
134
|
+
return VALIDATION_METHOD_NAMES.has(n.expression.name.text);
|
|
135
|
+
}
|
|
136
|
+
if (ts.isIdentifier(n.expression)) {
|
|
137
|
+
return n.expression.text.toLowerCase().includes('validate');
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Check if function has validation logic
|
|
143
|
+
* @returns {boolean} True if the function has validation logic
|
|
144
|
+
*/
|
|
145
|
+
function hasRequestValidation(node) {
|
|
146
|
+
let hasValidation = false;
|
|
147
|
+
const visit = (n) => {
|
|
148
|
+
if (ts.isCallExpression(n) && isValidationMethodCall(n)) {
|
|
149
|
+
hasValidation = true;
|
|
150
|
+
}
|
|
151
|
+
if (!hasValidation)
|
|
152
|
+
ts.forEachChild(n, visit);
|
|
153
|
+
};
|
|
154
|
+
if (node.body)
|
|
155
|
+
visit(node.body);
|
|
156
|
+
return hasValidation;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check if function has try-catch block
|
|
160
|
+
* @returns {boolean} True if the function has a try-catch block
|
|
161
|
+
*/
|
|
162
|
+
function hasTryCatchBlock(node) {
|
|
163
|
+
let hasTryCatch = false;
|
|
164
|
+
const visit = (n) => {
|
|
165
|
+
if (ts.isTryStatement(n))
|
|
166
|
+
hasTryCatch = true;
|
|
167
|
+
if (!hasTryCatch)
|
|
168
|
+
ts.forEachChild(n, visit);
|
|
169
|
+
};
|
|
170
|
+
visit(node);
|
|
171
|
+
return hasTryCatch;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Check if function has untyped parameters
|
|
175
|
+
* @returns {boolean} True if the function has any untyped parameters
|
|
176
|
+
*/
|
|
177
|
+
function hasUntypedParameters(node) {
|
|
178
|
+
return node.parameters.some((param) => !param.type);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Check if function has proper API contract
|
|
182
|
+
* @param {CheckFunctionContractOptions} options - The contract check options
|
|
183
|
+
* @returns {CheckViolation[]} Array of contract violations found
|
|
184
|
+
*/
|
|
185
|
+
function checkFunctionContract(options) {
|
|
186
|
+
const { absolutePath, sourceFile, node } = options;
|
|
187
|
+
const violations = [];
|
|
188
|
+
const functionName = getFunctionName(node);
|
|
189
|
+
if (shouldSkipFunction(node, functionName, absolutePath)) {
|
|
190
|
+
return violations;
|
|
191
|
+
}
|
|
192
|
+
const { line: lineIdx, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
193
|
+
const line = lineIdx + 1;
|
|
194
|
+
const functionSignature = node.getText(sourceFile).split('{')[0]?.trim().slice(0, 100) ?? functionName;
|
|
195
|
+
// Check 1: Has explicit return type
|
|
196
|
+
if (!node.type) {
|
|
197
|
+
violations.push({
|
|
198
|
+
line,
|
|
199
|
+
column: character + 1,
|
|
200
|
+
message: `Handler '${functionName}' missing explicit return type`,
|
|
201
|
+
severity: 'error',
|
|
202
|
+
suggestion: `Add explicit return type to '${functionName}', e.g., ': Promise<ServiceResult<T>>' or ': Promise<Response<T>>'`,
|
|
203
|
+
type: 'missing-response-type',
|
|
204
|
+
match: functionSignature,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// Check 2: Has typed parameters
|
|
208
|
+
if (hasUntypedParameters(node)) {
|
|
209
|
+
violations.push({
|
|
210
|
+
line,
|
|
211
|
+
column: character + 1,
|
|
212
|
+
message: `Handler '${functionName}' has untyped parameters`,
|
|
213
|
+
severity: 'error',
|
|
214
|
+
suggestion: `Add explicit TypeScript types to all parameters in '${functionName}' for type safety`,
|
|
215
|
+
type: 'untyped-parameters',
|
|
216
|
+
match: functionSignature,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// Check 3: Has validation for request parameters
|
|
220
|
+
if (!hasRequestValidation(node) && hasRequestParameter(node)) {
|
|
221
|
+
violations.push({
|
|
222
|
+
line,
|
|
223
|
+
column: character + 1,
|
|
224
|
+
message: `Handler '${functionName}' accepts request but has no validation`,
|
|
225
|
+
severity: 'error',
|
|
226
|
+
suggestion: `Add Zod schema validation using .parse() or .safeParse() for request body/params in '${functionName}'`,
|
|
227
|
+
type: 'missing-validation',
|
|
228
|
+
match: functionSignature,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
// Check 4: Has try-catch for error handling
|
|
232
|
+
// Skip functions that ARE error handlers (handleXxxError) — wrapping
|
|
233
|
+
// them in another try-catch is error-handling-inception.
|
|
234
|
+
const isErrorHandler = ERROR_HANDLER_NAME_PATTERNS.some((p) => p.test(functionName));
|
|
235
|
+
if (!isErrorHandler && !hasTryCatchBlock(node)) {
|
|
236
|
+
violations.push({
|
|
237
|
+
line,
|
|
238
|
+
column: character + 1,
|
|
239
|
+
message: `Handler '${functionName}' missing try-catch error handling`,
|
|
240
|
+
severity: 'warning',
|
|
241
|
+
suggestion: `Wrap the body of '${functionName}' in try-catch to handle errors gracefully and return appropriate error responses`,
|
|
242
|
+
type: 'missing-error-handling',
|
|
243
|
+
match: functionSignature,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
return violations;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Analyze a file for API contract violations
|
|
250
|
+
* @returns {CheckViolation[]} Array of violations found in the file
|
|
251
|
+
*/
|
|
252
|
+
function analyzeFile(absolutePath, content) {
|
|
253
|
+
const violations = [];
|
|
254
|
+
const sourceFile = getSharedSourceFile(absolutePath, content);
|
|
255
|
+
/* v8 ignore next -- defensive guard */
|
|
256
|
+
if (!sourceFile)
|
|
257
|
+
return [];
|
|
258
|
+
const visit = (node) => {
|
|
259
|
+
if (ts.isFunctionDeclaration(node) ||
|
|
260
|
+
ts.isArrowFunction(node) ||
|
|
261
|
+
ts.isMethodDeclaration(node)) {
|
|
262
|
+
const nodeViolations = checkFunctionContract({
|
|
263
|
+
absolutePath,
|
|
264
|
+
sourceFile,
|
|
265
|
+
node,
|
|
266
|
+
});
|
|
267
|
+
violations.push(...nodeViolations);
|
|
268
|
+
}
|
|
269
|
+
ts.forEachChild(node, visit);
|
|
270
|
+
};
|
|
271
|
+
visit(sourceFile);
|
|
272
|
+
return violations;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Check: quality/api-contract-validation
|
|
276
|
+
*
|
|
277
|
+
* Validates that API handlers have proper validation, typed responses,
|
|
278
|
+
* and error handling for type safety and reliability.
|
|
279
|
+
*
|
|
280
|
+
*/
|
|
281
|
+
export const apiContractValidation = defineCheck({
|
|
282
|
+
id: 'ef307717-de39-41d3-8344-0e6f7562367a',
|
|
283
|
+
slug: 'api-contract-validation',
|
|
284
|
+
scope: { languages: ['typescript'], concerns: ['backend', 'server'] },
|
|
285
|
+
contentFilter: 'strip-strings',
|
|
286
|
+
confidence: 'high',
|
|
287
|
+
description: 'Validate API handlers have proper validation, typed responses, and error handling',
|
|
288
|
+
longDescription: `**Purpose:** Enforces that API handler functions have proper type contracts, input validation, and error handling.
|
|
289
|
+
|
|
290
|
+
**Detects:**
|
|
291
|
+
- Handlers (matching \`/handler$/i\`, \`/^handle[A-Z]/\`, \`/^process[A-Z]/\`) missing explicit return types
|
|
292
|
+
- Handler parameters without TypeScript type annotations
|
|
293
|
+
- Handlers accepting \`req\`/\`request\` params without calling \`parse\`, \`safeParse\`, \`validate\`, or similar validation methods
|
|
294
|
+
- Handler bodies missing try-catch error handling
|
|
295
|
+
|
|
296
|
+
**Why it matters:** Untyped or unvalidated API handlers allow malformed requests through and produce unpredictable error responses.
|
|
297
|
+
|
|
298
|
+
**Scope:** General best practice. Analyzes each file individually using TypeScript AST parsing. Only processes files identified as API files by \`isAPIFile()\`.`,
|
|
299
|
+
tags: ['quality', 'api', 'type-safety', 'validation'],
|
|
300
|
+
fileTypes: ['ts'],
|
|
301
|
+
analyze(content, filePath) {
|
|
302
|
+
// Only analyze API files
|
|
303
|
+
if (!isAPIFile(filePath)) {
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
return analyzeFile(filePath, content);
|
|
308
|
+
/* v8 ignore next 1 -- defensive catch: parse failures already handled */
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
// @swallow-ok Skip files that fail to parse
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
//# sourceMappingURL=api-contract-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-contract-validation.js","sourceRoot":"","sources":["../../../../src/checks/quality/api/api-contract-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview API Response Validation Check
|
|
3
|
+
*
|
|
4
|
+
* Ensures API responses are validated with Zod schemas before being sent to clients.
|
|
5
|
+
* Validates that response types match their Zod schema definitions.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check: quality/api-response-validation
|
|
9
|
+
*
|
|
10
|
+
* Ensures API responses are validated with Zod schemas.
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
export declare const apiResponseValidation: import("@opensip-cli/fitness").Check;
|
|
14
|
+
//# sourceMappingURL=api-response-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-response-validation.d.ts","sourceRoot":"","sources":["../../../../src/checks/quality/api/api-response-validation.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAoMH;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,sCAsChC,CAAC"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// @fitness-ignore-file correlation-id-coverage -- Fitness check implementation, not an API handler
|
|
2
|
+
// @fitness-ignore-file duplicate-implementation-detection -- similar patterns across diagnostic modules
|
|
3
|
+
// @fitness-ignore-file no-raw-regex-on-code -- fitness check: regex patterns analyze trusted codebase content, not user input
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview API Response Validation Check
|
|
6
|
+
*
|
|
7
|
+
* Ensures API responses are validated with Zod schemas before being sent to clients.
|
|
8
|
+
* Validates that response types match their Zod schema definitions.
|
|
9
|
+
*/
|
|
10
|
+
import { basename } from 'node:path';
|
|
11
|
+
import { defineCheck, isAPIFile } from '@opensip-cli/fitness';
|
|
12
|
+
import { getSharedSourceFile } from '@opensip-cli/lang-typescript';
|
|
13
|
+
import * as ts from 'typescript';
|
|
14
|
+
/**
|
|
15
|
+
* Check if a file contains Fastify route-level response schema definitions.
|
|
16
|
+
* Routes using `schema: { response: ... }` already validate responses at the framework level.
|
|
17
|
+
*
|
|
18
|
+
* The previous regex `\{[^}]*response\s*:` failed on real route schemas
|
|
19
|
+
* because real schemas contain nested object literals — once the regex
|
|
20
|
+
* hits the first `}` (e.g. closing a `querystring` block) it gives up
|
|
21
|
+
* before reaching `response:`. The lazy `.*?` form scans across nested
|
|
22
|
+
* braces correctly. We also accept the related forms a Zod-typed Fastify
|
|
23
|
+
* route uses: bare `response: {` blocks, response-status-keyed objects,
|
|
24
|
+
* and ResponseSchema imports paired with .send.
|
|
25
|
+
*/
|
|
26
|
+
function hasRouteResponseSchema(content) {
|
|
27
|
+
// schema: { ... response: ... } — handles nested braces via lazy match.
|
|
28
|
+
if (/schema\s*:\s*\{.*?\bresponse\s*:/s.test(content))
|
|
29
|
+
return true;
|
|
30
|
+
// Direct `response: { 200: ... }` block — Fastify also accepts this
|
|
31
|
+
// shape outside an enclosing `schema:` wrapper inside `RouteOptions`.
|
|
32
|
+
if (/\bresponse\s*:\s*\{\s*[12345]\d{2}\s*:/s.test(content))
|
|
33
|
+
return true;
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if a file only uses `reply.send()` with dynamic/passthrough payloads.
|
|
38
|
+
* These routes forward external API responses or computed results that have no fixed shape.
|
|
39
|
+
*/
|
|
40
|
+
function isDynamicReplyOnly(content) {
|
|
41
|
+
// If file has reply.send() but no reply.status().send({ ... }) with object literals,
|
|
42
|
+
// it's likely a passthrough/dynamic endpoint
|
|
43
|
+
const hasSendCalls = content.includes('reply.send(') || content.includes('reply.send (');
|
|
44
|
+
if (!hasSendCalls)
|
|
45
|
+
return false;
|
|
46
|
+
// If there's a reply.code/status + structured object, it likely has a fixed shape
|
|
47
|
+
const hasStructuredResponse = /reply\s*\.\s*(?:code|status)\s*\([^)]+\)\s*\.\s*send\s*\(\s*\{/.test(content);
|
|
48
|
+
// If there's no structured response pattern, treat as dynamic
|
|
49
|
+
return !hasStructuredResponse;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if an import declaration imports schema/contracts
|
|
53
|
+
*/
|
|
54
|
+
function checkSchemaImport(node, state) {
|
|
55
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
56
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
57
|
+
const importPath = moduleSpecifier.text;
|
|
58
|
+
if (importPath.includes('schema') || importPath.includes('contracts')) {
|
|
59
|
+
state.hasResponseSchemaImport = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if a call expression validates a response
|
|
65
|
+
*/
|
|
66
|
+
function checkValidationCall(node, sourceFile, state) {
|
|
67
|
+
const callText = node.expression.getText(sourceFile);
|
|
68
|
+
if (!callText.includes('parse') && !callText.includes('safeParse')) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
/* v8 ignore next -- defensive nullish fallback */
|
|
72
|
+
const argText = node.arguments[0]?.getText(sourceFile) ?? '';
|
|
73
|
+
if (argText.includes('response') || argText.includes('result') || argText.includes('data')) {
|
|
74
|
+
state.hasResponseValidation = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check if a return statement returns an API response
|
|
79
|
+
*/
|
|
80
|
+
function checkApiResponseReturn(node, sourceFile, state) {
|
|
81
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
82
|
+
if (!node.expression)
|
|
83
|
+
return;
|
|
84
|
+
const returnText = node.expression.getText(sourceFile);
|
|
85
|
+
if (returnText.includes('res.') ||
|
|
86
|
+
returnText.includes('reply.') ||
|
|
87
|
+
returnText.includes('response.')) {
|
|
88
|
+
state.hasApiResponse = true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if a file is an SSE/streaming endpoint.
|
|
93
|
+
* These routes use raw writes or event-stream content types, not structured JSON responses.
|
|
94
|
+
*/
|
|
95
|
+
function isStreamingFile(content) {
|
|
96
|
+
return content.includes('text/event-stream') || content.includes('reply.raw.write');
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if a file serves diagnostic, debug, or health endpoints.
|
|
100
|
+
* These routes intentionally return dynamic/unstructured payloads.
|
|
101
|
+
*/
|
|
102
|
+
function isDiagnosticFile(filePath) {
|
|
103
|
+
const lowerPath = filePath.toLowerCase();
|
|
104
|
+
return (lowerPath.includes('diagnostic') || lowerPath.includes('debug') || lowerPath.includes('health'));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* @returns {*}
|
|
108
|
+
* Analyze a file for response validation issues
|
|
109
|
+
*/
|
|
110
|
+
function analyzeFile(absolutePath, content) {
|
|
111
|
+
const violations = [];
|
|
112
|
+
// Skip SSE/streaming routes — they don't return structured JSON
|
|
113
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
114
|
+
if (isStreamingFile(content))
|
|
115
|
+
return [];
|
|
116
|
+
// Skip diagnostic/debug/health endpoints — intentionally dynamic responses
|
|
117
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
118
|
+
if (isDiagnosticFile(absolutePath))
|
|
119
|
+
return [];
|
|
120
|
+
// Skip routes that define response schemas at the Fastify route level
|
|
121
|
+
/* v8 ignore next -- defensive AST/type guard */
|
|
122
|
+
if (hasRouteResponseSchema(content))
|
|
123
|
+
return [];
|
|
124
|
+
// Skip routes that only use reply.send() with dynamic/passthrough payloads
|
|
125
|
+
if (isDynamicReplyOnly(content))
|
|
126
|
+
return [];
|
|
127
|
+
const sourceFile = getSharedSourceFile(absolutePath, content);
|
|
128
|
+
/* v8 ignore next -- defensive guard */
|
|
129
|
+
if (!sourceFile)
|
|
130
|
+
return [];
|
|
131
|
+
// Track if file has response schema imports
|
|
132
|
+
// Use state object to track findings across callback invocations
|
|
133
|
+
// (TypeScript can't track primitive mutations in callbacks)
|
|
134
|
+
const state = {
|
|
135
|
+
hasResponseSchemaImport: false,
|
|
136
|
+
hasResponseValidation: false,
|
|
137
|
+
hasApiResponse: false,
|
|
138
|
+
};
|
|
139
|
+
const visit = (node) => {
|
|
140
|
+
if (ts.isImportDeclaration(node)) {
|
|
141
|
+
checkSchemaImport(node, state);
|
|
142
|
+
}
|
|
143
|
+
if (ts.isCallExpression(node)) {
|
|
144
|
+
checkValidationCall(node, sourceFile, state);
|
|
145
|
+
}
|
|
146
|
+
if (ts.isReturnStatement(node)) {
|
|
147
|
+
checkApiResponseReturn(node, sourceFile, state);
|
|
148
|
+
}
|
|
149
|
+
ts.forEachChild(node, visit);
|
|
150
|
+
};
|
|
151
|
+
visit(sourceFile);
|
|
152
|
+
// If file has API responses but no validation, flag it
|
|
153
|
+
if (state.hasApiResponse && !state.hasResponseValidation && !state.hasResponseSchemaImport) {
|
|
154
|
+
violations.push({
|
|
155
|
+
line: 1,
|
|
156
|
+
column: 0,
|
|
157
|
+
message: 'API file sends responses without schema validation',
|
|
158
|
+
severity: 'warning',
|
|
159
|
+
suggestion: 'Import a Zod response schema from shared contract schemas and use .parse() or .safeParse() to validate API responses before sending to clients',
|
|
160
|
+
type: 'missing-response-validation',
|
|
161
|
+
match: basename(absolutePath),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return violations;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check: quality/api-response-validation
|
|
168
|
+
*
|
|
169
|
+
* Ensures API responses are validated with Zod schemas.
|
|
170
|
+
*
|
|
171
|
+
*/
|
|
172
|
+
export const apiResponseValidation = defineCheck({
|
|
173
|
+
id: '8822fd30-bcd5-48d5-80d6-2f3abdec7f70',
|
|
174
|
+
slug: 'api-response-validation',
|
|
175
|
+
scope: { languages: ['typescript'], concerns: ['backend', 'server'] },
|
|
176
|
+
confidence: 'high',
|
|
177
|
+
description: 'Ensure API responses are validated with Zod schemas',
|
|
178
|
+
longDescription: `**Purpose:** Ensures API files validate outbound responses with Zod schemas before sending them to clients.
|
|
179
|
+
|
|
180
|
+
**Detects:**
|
|
181
|
+
- Files that return API responses (via \`res.\`, \`reply.\`, or \`response.\`) but lack both a schema/contracts import and any \`parse\`/\`safeParse\` call on response/result/data arguments
|
|
182
|
+
|
|
183
|
+
**Skips:**
|
|
184
|
+
- SSE/streaming endpoints (files using \`text/event-stream\` or \`reply.raw.write\`)
|
|
185
|
+
- Diagnostic, debug, and health endpoints (path-based detection)
|
|
186
|
+
- Routes with Fastify-level response schemas (\`schema: { response: ... }\`)
|
|
187
|
+
- Routes that only use \`reply.send()\` with dynamic/passthrough payloads
|
|
188
|
+
|
|
189
|
+
**Why it matters:** Without response validation, API endpoints can silently send malformed or extra data to clients, breaking contracts and leaking internal fields.
|
|
190
|
+
|
|
191
|
+
**Scope:** General best practice. Analyzes each file individually using TypeScript AST parsing. Only processes files identified as API files by \`isAPIFile()\`.`,
|
|
192
|
+
tags: ['quality', 'api', 'validation', 'zod'],
|
|
193
|
+
fileTypes: ['ts'],
|
|
194
|
+
analyze(content, filePath) {
|
|
195
|
+
// Only analyze API files
|
|
196
|
+
if (!isAPIFile(filePath)) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
return analyzeFile(filePath, content);
|
|
201
|
+
/* v8 ignore next 1 -- defensive catch: parse failures already handled */
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// @swallow-ok Skip files that fail to parse
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
//# sourceMappingURL=api-response-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-response-validation.js","sourceRoot":"","sources":["../../../../src/checks/quality/api/api-response-validation.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,wGAAwG;AACxG,8HAA8H;AAC9H;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,WAAW,EAAuB,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC;;;;;;;;;;;GAWG;AACH,SAAS,sBAAsB,CAAC,OAAe;IAC7C,wEAAwE;IACxE,IAAI,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACnE,oEAAoE;IACpE,sEAAsE;IACtE,IAAI,yCAAyC,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CAAC,OAAe;IACzC,qFAAqF;IACrF,6CAA6C;IAC7C,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACzF,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhC,kFAAkF;IAClF,MAAM,qBAAqB,GACzB,gEAAgE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAEjF,8DAA8D;IAC9D,OAAO,CAAC,qBAAqB,CAAC;AAChC,CAAC;AAWD;;GAEG;AACH,SAAS,iBAAiB,CAAC,IAA0B,EAAE,KAAuB;IAC5E,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC;IAC7C,IAAI,EAAE,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC;QACxC,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACtE,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACvC,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAC1B,IAAuB,EACvB,UAAyB,EACzB,KAAuB;IAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACnE,OAAO;IACT,CAAC;IAED,kDAAkD;IAClD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC7D,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3F,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC;IACrC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAC7B,IAAwB,EACxB,UAAyB,EACzB,KAAuB;IAEvB,gDAAgD;IAChD,IAAI,CAAC,IAAI,CAAC,UAAU;QAAE,OAAO;IAE7B,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACvD,IACE,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC3B,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC7B,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,EAChC,CAAC;QACD,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;IAC9B,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,OAAe;IACtC,OAAO,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;AACtF,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,QAAgB;IACxC,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACzC,OAAO,CACL,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAChG,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,YAAoB,EAAE,OAAe;IACxD,MAAM,UAAU,GAAqB,EAAE,CAAC;IAExC,gEAAgE;IAChE,gDAAgD;IAChD,IAAI,eAAe,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,2EAA2E;IAC3E,gDAAgD;IAChD,IAAI,gBAAgB,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9C,sEAAsE;IACtE,gDAAgD;IAChD,IAAI,sBAAsB,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAE/C,2EAA2E;IAC3E,IAAI,kBAAkB,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAE3C,MAAM,UAAU,GAAG,mBAAmB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC9D,uCAAuC;IACvC,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAC;IAE3B,4CAA4C;IAC5C,iEAAiE;IACjE,4DAA4D;IAC5D,MAAM,KAAK,GAAqB;QAC9B,uBAAuB,EAAE,KAAK;QAC9B,qBAAqB,EAAE,KAAK;QAC5B,cAAc,EAAE,KAAK;KACtB,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,IAAa,EAAE,EAAE;QAC9B,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,sBAAsB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;QACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,KAAK,CAAC,UAAU,CAAC,CAAC;IAElB,uDAAuD;IACvD,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,qBAAqB,IAAI,CAAC,KAAK,CAAC,uBAAuB,EAAE,CAAC;QAC3F,UAAU,CAAC,IAAI,CAAC;YACd,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,oDAAoD;YAC7D,QAAQ,EAAE,SAAS;YACnB,UAAU,EACR,gJAAgJ;YAClJ,IAAI,EAAE,6BAA6B;YACnC,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC;SAC9B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,WAAW,CAAC;IAC/C,EAAE,EAAE,sCAAsC;IAC1C,IAAI,EAAE,yBAAyB;IAC/B,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE;IAErE,UAAU,EAAE,MAAM;IAClB,WAAW,EAAE,qDAAqD;IAClE,eAAe,EAAE;;;;;;;;;;;;;iKAa8I;IAC/J,IAAI,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,SAAS,EAAE,CAAC,IAAI,CAAC;IAEjB,OAAO,CAAC,OAAO,EAAE,QAAQ;QACvB,yBAAyB;QACzB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC;YACH,OAAO,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACtC,yEAAyE;QAC3E,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;YAC5C,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Fastify Route Validation Check
|
|
3
|
+
*
|
|
4
|
+
* Ensures all Fastify POST/PATCH/PUT route handlers validate request bodies
|
|
5
|
+
* using Zod schemas.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check: quality/fastify-route-validation
|
|
9
|
+
*
|
|
10
|
+
* Ensures all Fastify POST/PATCH/PUT route handlers validate request bodies
|
|
11
|
+
* with Zod schemas.
|
|
12
|
+
*/
|
|
13
|
+
export declare const fastifyRouteValidation: import("@opensip-cli/fitness").Check;
|
|
14
|
+
//# sourceMappingURL=fastify-route-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify-route-validation.d.ts","sourceRoot":"","sources":["../../../../src/checks/quality/api/fastify-route-validation.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAkTH;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,sCAwCjC,CAAC"}
|