driftdetect-detectors 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/dist/accessibility/alt-text.d.ts +63 -0
- package/dist/accessibility/alt-text.d.ts.map +1 -0
- package/dist/accessibility/alt-text.js +100 -0
- package/dist/accessibility/alt-text.js.map +1 -0
- package/dist/accessibility/aria-roles.d.ts +65 -0
- package/dist/accessibility/aria-roles.d.ts.map +1 -0
- package/dist/accessibility/aria-roles.js +87 -0
- package/dist/accessibility/aria-roles.js.map +1 -0
- package/dist/accessibility/focus-management.d.ts +62 -0
- package/dist/accessibility/focus-management.d.ts.map +1 -0
- package/dist/accessibility/focus-management.js +88 -0
- package/dist/accessibility/focus-management.js.map +1 -0
- package/dist/accessibility/heading-hierarchy.d.ts +66 -0
- package/dist/accessibility/heading-hierarchy.d.ts.map +1 -0
- package/dist/accessibility/heading-hierarchy.js +94 -0
- package/dist/accessibility/heading-hierarchy.js.map +1 -0
- package/dist/accessibility/index.d.ts +25 -0
- package/dist/accessibility/index.d.ts.map +1 -0
- package/dist/accessibility/index.js +21 -0
- package/dist/accessibility/index.js.map +1 -0
- package/dist/accessibility/keyboard-nav.d.ts +63 -0
- package/dist/accessibility/keyboard-nav.d.ts.map +1 -0
- package/dist/accessibility/keyboard-nav.js +86 -0
- package/dist/accessibility/keyboard-nav.js.map +1 -0
- package/dist/accessibility/semantic-html.d.ts +76 -0
- package/dist/accessibility/semantic-html.d.ts.map +1 -0
- package/dist/accessibility/semantic-html.js +204 -0
- package/dist/accessibility/semantic-html.js.map +1 -0
- package/dist/api/client-patterns.d.ts +121 -0
- package/dist/api/client-patterns.d.ts.map +1 -0
- package/dist/api/client-patterns.js +478 -0
- package/dist/api/client-patterns.js.map +1 -0
- package/dist/api/error-format.d.ts +140 -0
- package/dist/api/error-format.d.ts.map +1 -0
- package/dist/api/error-format.js +614 -0
- package/dist/api/error-format.js.map +1 -0
- package/dist/api/http-methods.d.ts +255 -0
- package/dist/api/http-methods.d.ts.map +1 -0
- package/dist/api/http-methods.js +890 -0
- package/dist/api/http-methods.js.map +1 -0
- package/dist/api/index.d.ts +16 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +37 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/pagination.d.ts +133 -0
- package/dist/api/pagination.d.ts.map +1 -0
- package/dist/api/pagination.js +521 -0
- package/dist/api/pagination.js.map +1 -0
- package/dist/api/response-envelope.d.ts +261 -0
- package/dist/api/response-envelope.d.ts.map +1 -0
- package/dist/api/response-envelope.js +1050 -0
- package/dist/api/response-envelope.js.map +1 -0
- package/dist/api/retry-patterns.d.ts +117 -0
- package/dist/api/retry-patterns.d.ts.map +1 -0
- package/dist/api/retry-patterns.js +480 -0
- package/dist/api/retry-patterns.js.map +1 -0
- package/dist/api/route-structure.d.ts +128 -0
- package/dist/api/route-structure.d.ts.map +1 -0
- package/dist/api/route-structure.js +738 -0
- package/dist/api/route-structure.js.map +1 -0
- package/dist/auth/audit-logging.d.ts +80 -0
- package/dist/auth/audit-logging.d.ts.map +1 -0
- package/dist/auth/audit-logging.js +370 -0
- package/dist/auth/audit-logging.js.map +1 -0
- package/dist/auth/index.d.ts +33 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +49 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/middleware-usage.d.ts +65 -0
- package/dist/auth/middleware-usage.d.ts.map +1 -0
- package/dist/auth/middleware-usage.js +192 -0
- package/dist/auth/middleware-usage.js.map +1 -0
- package/dist/auth/permission-checks.d.ts +60 -0
- package/dist/auth/permission-checks.d.ts.map +1 -0
- package/dist/auth/permission-checks.js +159 -0
- package/dist/auth/permission-checks.js.map +1 -0
- package/dist/auth/rbac-patterns.d.ts +68 -0
- package/dist/auth/rbac-patterns.d.ts.map +1 -0
- package/dist/auth/rbac-patterns.js +143 -0
- package/dist/auth/rbac-patterns.js.map +1 -0
- package/dist/auth/resource-ownership.d.ts +77 -0
- package/dist/auth/resource-ownership.d.ts.map +1 -0
- package/dist/auth/resource-ownership.js +324 -0
- package/dist/auth/resource-ownership.js.map +1 -0
- package/dist/auth/token-handling.d.ts +64 -0
- package/dist/auth/token-handling.d.ts.map +1 -0
- package/dist/auth/token-handling.js +151 -0
- package/dist/auth/token-handling.js.map +1 -0
- package/dist/base/ast-detector.d.ts +421 -0
- package/dist/base/ast-detector.d.ts.map +1 -0
- package/dist/base/ast-detector.js +699 -0
- package/dist/base/ast-detector.js.map +1 -0
- package/dist/base/base-detector.d.ts +366 -0
- package/dist/base/base-detector.d.ts.map +1 -0
- package/dist/base/base-detector.js +170 -0
- package/dist/base/base-detector.js.map +1 -0
- package/dist/base/index.d.ts +12 -0
- package/dist/base/index.d.ts.map +1 -0
- package/dist/base/index.js +17 -0
- package/dist/base/index.js.map +1 -0
- package/dist/base/regex-detector.d.ts +421 -0
- package/dist/base/regex-detector.d.ts.map +1 -0
- package/dist/base/regex-detector.js +537 -0
- package/dist/base/regex-detector.js.map +1 -0
- package/dist/base/structural-detector.d.ts +424 -0
- package/dist/base/structural-detector.d.ts.map +1 -0
- package/dist/base/structural-detector.js +731 -0
- package/dist/base/structural-detector.js.map +1 -0
- package/dist/base/types.d.ts +53 -0
- package/dist/base/types.d.ts.map +1 -0
- package/dist/base/types.js +5 -0
- package/dist/base/types.js.map +1 -0
- package/dist/components/component-structure.d.ts +163 -0
- package/dist/components/component-structure.d.ts.map +1 -0
- package/dist/components/component-structure.js +500 -0
- package/dist/components/component-structure.js.map +1 -0
- package/dist/components/composition.d.ts +287 -0
- package/dist/components/composition.d.ts.map +1 -0
- package/dist/components/composition.js +1123 -0
- package/dist/components/composition.js.map +1 -0
- package/dist/components/duplicate-detection.d.ts +251 -0
- package/dist/components/duplicate-detection.d.ts.map +1 -0
- package/dist/components/duplicate-detection.js +804 -0
- package/dist/components/duplicate-detection.js.map +1 -0
- package/dist/components/index.d.ts +16 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +51 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/near-duplicate.d.ts +402 -0
- package/dist/components/near-duplicate.d.ts.map +1 -0
- package/dist/components/near-duplicate.js +1090 -0
- package/dist/components/near-duplicate.js.map +1 -0
- package/dist/components/props-patterns.d.ts +194 -0
- package/dist/components/props-patterns.d.ts.map +1 -0
- package/dist/components/props-patterns.js +795 -0
- package/dist/components/props-patterns.js.map +1 -0
- package/dist/components/ref-forwarding.d.ts +250 -0
- package/dist/components/ref-forwarding.d.ts.map +1 -0
- package/dist/components/ref-forwarding.js +832 -0
- package/dist/components/ref-forwarding.js.map +1 -0
- package/dist/components/state-patterns.d.ts +291 -0
- package/dist/components/state-patterns.d.ts.map +1 -0
- package/dist/components/state-patterns.js +970 -0
- package/dist/components/state-patterns.js.map +1 -0
- package/dist/config/config-validation.d.ts +74 -0
- package/dist/config/config-validation.d.ts.map +1 -0
- package/dist/config/config-validation.js +446 -0
- package/dist/config/config-validation.js.map +1 -0
- package/dist/config/default-values.d.ts +72 -0
- package/dist/config/default-values.d.ts.map +1 -0
- package/dist/config/default-values.js +386 -0
- package/dist/config/default-values.js.map +1 -0
- package/dist/config/env-naming.d.ts +73 -0
- package/dist/config/env-naming.d.ts.map +1 -0
- package/dist/config/env-naming.js +429 -0
- package/dist/config/env-naming.js.map +1 -0
- package/dist/config/environment-detection.d.ts +72 -0
- package/dist/config/environment-detection.d.ts.map +1 -0
- package/dist/config/environment-detection.js +400 -0
- package/dist/config/environment-detection.js.map +1 -0
- package/dist/config/feature-flags.d.ts +72 -0
- package/dist/config/feature-flags.d.ts.map +1 -0
- package/dist/config/feature-flags.js +384 -0
- package/dist/config/feature-flags.js.map +1 -0
- package/dist/config/index.d.ts +27 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +43 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/required-optional.d.ts +71 -0
- package/dist/config/required-optional.d.ts.map +1 -0
- package/dist/config/required-optional.js +344 -0
- package/dist/config/required-optional.js.map +1 -0
- package/dist/data-access/connection-pooling.d.ts +63 -0
- package/dist/data-access/connection-pooling.d.ts.map +1 -0
- package/dist/data-access/connection-pooling.js +297 -0
- package/dist/data-access/connection-pooling.js.map +1 -0
- package/dist/data-access/dto-patterns.d.ts +64 -0
- package/dist/data-access/dto-patterns.d.ts.map +1 -0
- package/dist/data-access/dto-patterns.js +291 -0
- package/dist/data-access/dto-patterns.js.map +1 -0
- package/dist/data-access/index.d.ts +31 -0
- package/dist/data-access/index.d.ts.map +1 -0
- package/dist/data-access/index.js +49 -0
- package/dist/data-access/index.js.map +1 -0
- package/dist/data-access/n-plus-one.d.ts +60 -0
- package/dist/data-access/n-plus-one.d.ts.map +1 -0
- package/dist/data-access/n-plus-one.js +264 -0
- package/dist/data-access/n-plus-one.js.map +1 -0
- package/dist/data-access/query-patterns.d.ts +64 -0
- package/dist/data-access/query-patterns.d.ts.map +1 -0
- package/dist/data-access/query-patterns.js +314 -0
- package/dist/data-access/query-patterns.js.map +1 -0
- package/dist/data-access/repository-pattern.d.ts +62 -0
- package/dist/data-access/repository-pattern.d.ts.map +1 -0
- package/dist/data-access/repository-pattern.js +257 -0
- package/dist/data-access/repository-pattern.js.map +1 -0
- package/dist/data-access/transaction-patterns.d.ts +61 -0
- package/dist/data-access/transaction-patterns.d.ts.map +1 -0
- package/dist/data-access/transaction-patterns.js +277 -0
- package/dist/data-access/transaction-patterns.js.map +1 -0
- package/dist/data-access/validation-patterns.d.ts +62 -0
- package/dist/data-access/validation-patterns.d.ts.map +1 -0
- package/dist/data-access/validation-patterns.js +301 -0
- package/dist/data-access/validation-patterns.js.map +1 -0
- package/dist/documentation/deprecation.d.ts +62 -0
- package/dist/documentation/deprecation.d.ts.map +1 -0
- package/dist/documentation/deprecation.js +83 -0
- package/dist/documentation/deprecation.js.map +1 -0
- package/dist/documentation/example-code.d.ts +64 -0
- package/dist/documentation/example-code.d.ts.map +1 -0
- package/dist/documentation/example-code.js +79 -0
- package/dist/documentation/example-code.js.map +1 -0
- package/dist/documentation/index.d.ts +22 -0
- package/dist/documentation/index.d.ts.map +1 -0
- package/dist/documentation/index.js +19 -0
- package/dist/documentation/index.js.map +1 -0
- package/dist/documentation/jsdoc-patterns.d.ts +72 -0
- package/dist/documentation/jsdoc-patterns.d.ts.map +1 -0
- package/dist/documentation/jsdoc-patterns.js +92 -0
- package/dist/documentation/jsdoc-patterns.js.map +1 -0
- package/dist/documentation/readme-structure.d.ts +67 -0
- package/dist/documentation/readme-structure.d.ts.map +1 -0
- package/dist/documentation/readme-structure.js +76 -0
- package/dist/documentation/readme-structure.js.map +1 -0
- package/dist/documentation/todo-patterns.d.ts +67 -0
- package/dist/documentation/todo-patterns.d.ts.map +1 -0
- package/dist/documentation/todo-patterns.js +73 -0
- package/dist/documentation/todo-patterns.js.map +1 -0
- package/dist/errors/async-errors.d.ts +72 -0
- package/dist/errors/async-errors.d.ts.map +1 -0
- package/dist/errors/async-errors.js +214 -0
- package/dist/errors/async-errors.js.map +1 -0
- package/dist/errors/circuit-breaker.d.ts +53 -0
- package/dist/errors/circuit-breaker.d.ts.map +1 -0
- package/dist/errors/circuit-breaker.js +241 -0
- package/dist/errors/circuit-breaker.js.map +1 -0
- package/dist/errors/error-codes.d.ts +73 -0
- package/dist/errors/error-codes.d.ts.map +1 -0
- package/dist/errors/error-codes.js +211 -0
- package/dist/errors/error-codes.js.map +1 -0
- package/dist/errors/error-logging.d.ts +73 -0
- package/dist/errors/error-logging.d.ts.map +1 -0
- package/dist/errors/error-logging.js +256 -0
- package/dist/errors/error-logging.js.map +1 -0
- package/dist/errors/error-propagation.d.ts +73 -0
- package/dist/errors/error-propagation.d.ts.map +1 -0
- package/dist/errors/error-propagation.js +244 -0
- package/dist/errors/error-propagation.js.map +1 -0
- package/dist/errors/exception-hierarchy.d.ts +75 -0
- package/dist/errors/exception-hierarchy.d.ts.map +1 -0
- package/dist/errors/exception-hierarchy.js +259 -0
- package/dist/errors/exception-hierarchy.js.map +1 -0
- package/dist/errors/index.d.ts +31 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +49 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/try-catch-placement.d.ts +73 -0
- package/dist/errors/try-catch-placement.d.ts.map +1 -0
- package/dist/errors/try-catch-placement.js +214 -0
- package/dist/errors/try-catch-placement.js.map +1 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +245 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/context-fields.d.ts +48 -0
- package/dist/logging/context-fields.d.ts.map +1 -0
- package/dist/logging/context-fields.js +160 -0
- package/dist/logging/context-fields.js.map +1 -0
- package/dist/logging/correlation-ids.d.ts +44 -0
- package/dist/logging/correlation-ids.d.ts.map +1 -0
- package/dist/logging/correlation-ids.js +144 -0
- package/dist/logging/correlation-ids.js.map +1 -0
- package/dist/logging/health-checks.d.ts +45 -0
- package/dist/logging/health-checks.d.ts.map +1 -0
- package/dist/logging/health-checks.js +165 -0
- package/dist/logging/health-checks.js.map +1 -0
- package/dist/logging/index.d.ts +31 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +49 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/log-levels.d.ts +46 -0
- package/dist/logging/log-levels.d.ts.map +1 -0
- package/dist/logging/log-levels.js +178 -0
- package/dist/logging/log-levels.js.map +1 -0
- package/dist/logging/metric-naming.d.ts +46 -0
- package/dist/logging/metric-naming.d.ts.map +1 -0
- package/dist/logging/metric-naming.js +157 -0
- package/dist/logging/metric-naming.js.map +1 -0
- package/dist/logging/pii-redaction.d.ts +44 -0
- package/dist/logging/pii-redaction.d.ts.map +1 -0
- package/dist/logging/pii-redaction.js +166 -0
- package/dist/logging/pii-redaction.js.map +1 -0
- package/dist/logging/structured-format.d.ts +53 -0
- package/dist/logging/structured-format.d.ts.map +1 -0
- package/dist/logging/structured-format.js +235 -0
- package/dist/logging/structured-format.js.map +1 -0
- package/dist/performance/bundle-size.d.ts +79 -0
- package/dist/performance/bundle-size.d.ts.map +1 -0
- package/dist/performance/bundle-size.js +276 -0
- package/dist/performance/bundle-size.js.map +1 -0
- package/dist/performance/caching-patterns.d.ts +78 -0
- package/dist/performance/caching-patterns.d.ts.map +1 -0
- package/dist/performance/caching-patterns.js +257 -0
- package/dist/performance/caching-patterns.js.map +1 -0
- package/dist/performance/code-splitting.d.ts +86 -0
- package/dist/performance/code-splitting.d.ts.map +1 -0
- package/dist/performance/code-splitting.js +447 -0
- package/dist/performance/code-splitting.js.map +1 -0
- package/dist/performance/debounce-throttle.d.ts +75 -0
- package/dist/performance/debounce-throttle.d.ts.map +1 -0
- package/dist/performance/debounce-throttle.js +232 -0
- package/dist/performance/debounce-throttle.js.map +1 -0
- package/dist/performance/index.d.ts +28 -0
- package/dist/performance/index.d.ts.map +1 -0
- package/dist/performance/index.js +39 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/performance/lazy-loading.d.ts +75 -0
- package/dist/performance/lazy-loading.d.ts.map +1 -0
- package/dist/performance/lazy-loading.js +233 -0
- package/dist/performance/lazy-loading.js.map +1 -0
- package/dist/performance/memoization.d.ts +75 -0
- package/dist/performance/memoization.d.ts.map +1 -0
- package/dist/performance/memoization.js +251 -0
- package/dist/performance/memoization.js.map +1 -0
- package/dist/registry/detector-registry.d.ts +266 -0
- package/dist/registry/detector-registry.d.ts.map +1 -0
- package/dist/registry/detector-registry.js +526 -0
- package/dist/registry/detector-registry.js.map +1 -0
- package/dist/registry/index.d.ts +10 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +10 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/loader.d.ts +232 -0
- package/dist/registry/loader.d.ts.map +1 -0
- package/dist/registry/loader.js +419 -0
- package/dist/registry/loader.js.map +1 -0
- package/dist/registry/types.d.ts +111 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +19 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/security/csp-headers.d.ts +78 -0
- package/dist/security/csp-headers.d.ts.map +1 -0
- package/dist/security/csp-headers.js +401 -0
- package/dist/security/csp-headers.js.map +1 -0
- package/dist/security/csrf-protection.d.ts +72 -0
- package/dist/security/csrf-protection.d.ts.map +1 -0
- package/dist/security/csrf-protection.js +344 -0
- package/dist/security/csrf-protection.js.map +1 -0
- package/dist/security/index.d.ts +30 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +48 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/input-sanitization.d.ts +74 -0
- package/dist/security/input-sanitization.d.ts.map +1 -0
- package/dist/security/input-sanitization.js +373 -0
- package/dist/security/input-sanitization.js.map +1 -0
- package/dist/security/rate-limiting.d.ts +81 -0
- package/dist/security/rate-limiting.d.ts.map +1 -0
- package/dist/security/rate-limiting.js +535 -0
- package/dist/security/rate-limiting.js.map +1 -0
- package/dist/security/secret-management.d.ts +83 -0
- package/dist/security/secret-management.d.ts.map +1 -0
- package/dist/security/secret-management.js +547 -0
- package/dist/security/secret-management.js.map +1 -0
- package/dist/security/sql-injection.d.ts +76 -0
- package/dist/security/sql-injection.d.ts.map +1 -0
- package/dist/security/sql-injection.js +383 -0
- package/dist/security/sql-injection.js.map +1 -0
- package/dist/security/xss-prevention.d.ts +80 -0
- package/dist/security/xss-prevention.d.ts.map +1 -0
- package/dist/security/xss-prevention.js +416 -0
- package/dist/security/xss-prevention.js.map +1 -0
- package/dist/structural/barrel-exports.d.ts +178 -0
- package/dist/structural/barrel-exports.d.ts.map +1 -0
- package/dist/structural/barrel-exports.js +553 -0
- package/dist/structural/barrel-exports.js.map +1 -0
- package/dist/structural/circular-deps.d.ts +140 -0
- package/dist/structural/circular-deps.d.ts.map +1 -0
- package/dist/structural/circular-deps.js +422 -0
- package/dist/structural/circular-deps.js.map +1 -0
- package/dist/structural/co-location.d.ts +202 -0
- package/dist/structural/co-location.d.ts.map +1 -0
- package/dist/structural/co-location.js +640 -0
- package/dist/structural/co-location.js.map +1 -0
- package/dist/structural/directory-structure.d.ts +151 -0
- package/dist/structural/directory-structure.d.ts.map +1 -0
- package/dist/structural/directory-structure.js +457 -0
- package/dist/structural/directory-structure.js.map +1 -0
- package/dist/structural/file-naming.d.ts +61 -0
- package/dist/structural/file-naming.d.ts.map +1 -0
- package/dist/structural/file-naming.js +231 -0
- package/dist/structural/file-naming.js.map +1 -0
- package/dist/structural/import-ordering.d.ts +212 -0
- package/dist/structural/import-ordering.d.ts.map +1 -0
- package/dist/structural/import-ordering.js +821 -0
- package/dist/structural/import-ordering.js.map +1 -0
- package/dist/structural/index.d.ts +23 -0
- package/dist/structural/index.d.ts.map +1 -0
- package/dist/structural/index.js +26 -0
- package/dist/structural/index.js.map +1 -0
- package/dist/structural/module-boundaries.d.ts +164 -0
- package/dist/structural/module-boundaries.d.ts.map +1 -0
- package/dist/structural/module-boundaries.js +616 -0
- package/dist/structural/module-boundaries.js.map +1 -0
- package/dist/structural/package-boundaries.d.ts +182 -0
- package/dist/structural/package-boundaries.d.ts.map +1 -0
- package/dist/structural/package-boundaries.js +602 -0
- package/dist/structural/package-boundaries.js.map +1 -0
- package/dist/styling/class-naming.d.ts +263 -0
- package/dist/styling/class-naming.d.ts.map +1 -0
- package/dist/styling/class-naming.js +892 -0
- package/dist/styling/class-naming.js.map +1 -0
- package/dist/styling/color-usage.d.ts +213 -0
- package/dist/styling/color-usage.d.ts.map +1 -0
- package/dist/styling/color-usage.js +732 -0
- package/dist/styling/color-usage.js.map +1 -0
- package/dist/styling/design-tokens.d.ts +212 -0
- package/dist/styling/design-tokens.d.ts.map +1 -0
- package/dist/styling/design-tokens.js +748 -0
- package/dist/styling/design-tokens.js.map +1 -0
- package/dist/styling/index.d.ts +16 -0
- package/dist/styling/index.d.ts.map +1 -0
- package/dist/styling/index.js +56 -0
- package/dist/styling/index.js.map +1 -0
- package/dist/styling/responsive.d.ts +304 -0
- package/dist/styling/responsive.d.ts.map +1 -0
- package/dist/styling/responsive.js +888 -0
- package/dist/styling/responsive.js.map +1 -0
- package/dist/styling/spacing-scale.d.ts +248 -0
- package/dist/styling/spacing-scale.d.ts.map +1 -0
- package/dist/styling/spacing-scale.js +865 -0
- package/dist/styling/spacing-scale.js.map +1 -0
- package/dist/styling/tailwind-patterns.d.ts +305 -0
- package/dist/styling/tailwind-patterns.d.ts.map +1 -0
- package/dist/styling/tailwind-patterns.js +1181 -0
- package/dist/styling/tailwind-patterns.js.map +1 -0
- package/dist/styling/typography.d.ts +281 -0
- package/dist/styling/typography.d.ts.map +1 -0
- package/dist/styling/typography.js +1004 -0
- package/dist/styling/typography.js.map +1 -0
- package/dist/styling/z-index-scale.d.ts +270 -0
- package/dist/styling/z-index-scale.d.ts.map +1 -0
- package/dist/styling/z-index-scale.js +714 -0
- package/dist/styling/z-index-scale.js.map +1 -0
- package/dist/testing/co-location.d.ts +42 -0
- package/dist/testing/co-location.d.ts.map +1 -0
- package/dist/testing/co-location.js +134 -0
- package/dist/testing/co-location.js.map +1 -0
- package/dist/testing/describe-naming.d.ts +47 -0
- package/dist/testing/describe-naming.d.ts.map +1 -0
- package/dist/testing/describe-naming.js +150 -0
- package/dist/testing/describe-naming.js.map +1 -0
- package/dist/testing/file-naming.d.ts +44 -0
- package/dist/testing/file-naming.d.ts.map +1 -0
- package/dist/testing/file-naming.js +131 -0
- package/dist/testing/file-naming.js.map +1 -0
- package/dist/testing/fixture-patterns.d.ts +52 -0
- package/dist/testing/fixture-patterns.d.ts.map +1 -0
- package/dist/testing/fixture-patterns.js +228 -0
- package/dist/testing/fixture-patterns.js.map +1 -0
- package/dist/testing/index.d.ts +31 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +49 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-patterns.d.ts +53 -0
- package/dist/testing/mock-patterns.d.ts.map +1 -0
- package/dist/testing/mock-patterns.js +264 -0
- package/dist/testing/mock-patterns.js.map +1 -0
- package/dist/testing/setup-teardown.d.ts +55 -0
- package/dist/testing/setup-teardown.d.ts.map +1 -0
- package/dist/testing/setup-teardown.js +262 -0
- package/dist/testing/setup-teardown.js.map +1 -0
- package/dist/testing/test-structure.d.ts +51 -0
- package/dist/testing/test-structure.d.ts.map +1 -0
- package/dist/testing/test-structure.js +225 -0
- package/dist/testing/test-structure.js.map +1 -0
- package/dist/types/any-usage.d.ts +99 -0
- package/dist/types/any-usage.d.ts.map +1 -0
- package/dist/types/any-usage.js +641 -0
- package/dist/types/any-usage.js.map +1 -0
- package/dist/types/file-location.d.ts +76 -0
- package/dist/types/file-location.d.ts.map +1 -0
- package/dist/types/file-location.js +395 -0
- package/dist/types/file-location.js.map +1 -0
- package/dist/types/generic-patterns.d.ts +97 -0
- package/dist/types/generic-patterns.d.ts.map +1 -0
- package/dist/types/generic-patterns.js +615 -0
- package/dist/types/generic-patterns.js.map +1 -0
- package/dist/types/index.d.ts +31 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +43 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/interface-vs-type.d.ts +81 -0
- package/dist/types/interface-vs-type.d.ts.map +1 -0
- package/dist/types/interface-vs-type.js +440 -0
- package/dist/types/interface-vs-type.js.map +1 -0
- package/dist/types/naming-conventions.d.ts +84 -0
- package/dist/types/naming-conventions.d.ts.map +1 -0
- package/dist/types/naming-conventions.js +455 -0
- package/dist/types/naming-conventions.js.map +1 -0
- package/dist/types/type-assertions.d.ts +98 -0
- package/dist/types/type-assertions.d.ts.map +1 -0
- package/dist/types/type-assertions.js +639 -0
- package/dist/types/type-assertions.js.map +1 -0
- package/dist/types/utility-types.d.ts +110 -0
- package/dist/types/utility-types.d.ts.map +1 -0
- package/dist/types/utility-types.js +547 -0
- package/dist/types/utility-types.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route Structure Detector - URL pattern detection
|
|
3
|
+
*
|
|
4
|
+
* Detects route URL structure patterns including:
|
|
5
|
+
* - RESTful URL patterns (e.g., /api/v1/users, /api/v1/users/:id)
|
|
6
|
+
* - Next.js App Router patterns (e.g., /app/api/.../route.ts)
|
|
7
|
+
* - Next.js Pages Router patterns (e.g., /pages/api/....ts)
|
|
8
|
+
* - Express-style route patterns (e.g., router.get('/users/:id'))
|
|
9
|
+
* - URL versioning patterns (e.g., /v1/, /v2/)
|
|
10
|
+
* - Resource naming conventions (plural vs singular)
|
|
11
|
+
* - Nested resource patterns (e.g., /users/:userId/posts/:postId)
|
|
12
|
+
* - Query parameter patterns
|
|
13
|
+
*
|
|
14
|
+
* Flags violations:
|
|
15
|
+
* - Inconsistent URL casing (mixing kebab-case and camelCase)
|
|
16
|
+
* - Inconsistent resource naming (mixing plural and singular)
|
|
17
|
+
* - Missing API versioning
|
|
18
|
+
* - Deeply nested routes (more than 3 levels)
|
|
19
|
+
* - Non-RESTful URL patterns
|
|
20
|
+
*
|
|
21
|
+
* @requirements 10.1 - THE API_Detector SHALL detect route URL structure patterns
|
|
22
|
+
*/
|
|
23
|
+
import { RegexDetector } from '../base/index.js';
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Constants
|
|
26
|
+
// ============================================================================
|
|
27
|
+
export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
|
|
28
|
+
export const EXPRESS_ROUTE_PATTERNS = [
|
|
29
|
+
// TypeScript/JavaScript patterns
|
|
30
|
+
/(?:router|app)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
|
|
31
|
+
/\.route\s*\(\s*['"`]([^'"`]+)['"`]\)/gi,
|
|
32
|
+
// Python patterns - FastAPI, Flask, Django
|
|
33
|
+
/@(?:app|router)\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
|
|
34
|
+
/@app\.route\s*\(\s*['"`]([^'"`]+)['"`]/gi,
|
|
35
|
+
/path\s*\(\s*['"`]([^'"`]+)['"`]/gi,
|
|
36
|
+
];
|
|
37
|
+
export const NEXTJS_APP_ROUTER_PATTERN = /\/app\/(?:api\/)?(?:[^/]+\/)*route\.(ts|js|tsx|jsx)$/;
|
|
38
|
+
export const NEXTJS_PAGES_ROUTER_PATTERN = /\/pages\/api\/(?:[^/]+\/)*[^/]+\.(ts|js|tsx|jsx)$/;
|
|
39
|
+
export const RESTFUL_URL_PATTERNS = [
|
|
40
|
+
/\/api\/v\d+\/[a-z][a-z0-9-]*/gi,
|
|
41
|
+
/\/v\d+\/[a-z][a-z0-9-]*/gi,
|
|
42
|
+
/\/[a-z][a-z0-9-]*(?:\/:[a-z][a-z0-9]*)?/gi,
|
|
43
|
+
];
|
|
44
|
+
export const API_VERSIONING_PATTERNS = [
|
|
45
|
+
/\/api\/v(\d+)\//gi,
|
|
46
|
+
/\/v(\d+)\//gi,
|
|
47
|
+
/['"`](?:Accept-Version|X-API-Version)['"`]/gi,
|
|
48
|
+
];
|
|
49
|
+
export const ROUTE_PARAMETER_PATTERNS = {
|
|
50
|
+
express: /:([a-zA-Z][a-zA-Z0-9]*)/g,
|
|
51
|
+
nextjsDynamic: /\[([a-zA-Z][a-zA-Z0-9]*)\]/g,
|
|
52
|
+
nextjsCatchAll: /\[\.\.\.([a-zA-Z][a-zA-Z0-9]*)\]/g,
|
|
53
|
+
nextjsOptionalCatchAll: /\[\[\.\.\.([a-zA-Z][a-zA-Z0-9]*)\]\]/g,
|
|
54
|
+
};
|
|
55
|
+
export const PLURAL_RESOURCES = new Set([
|
|
56
|
+
'users', 'posts', 'comments', 'articles', 'products', 'orders',
|
|
57
|
+
'items', 'categories', 'tags', 'files', 'images', 'documents',
|
|
58
|
+
'messages', 'notifications', 'events', 'tasks', 'projects',
|
|
59
|
+
'teams', 'members', 'roles', 'permissions', 'settings',
|
|
60
|
+
'accounts', 'profiles', 'sessions', 'tokens', 'keys',
|
|
61
|
+
'logs', 'metrics', 'reports', 'analytics', 'dashboards',
|
|
62
|
+
]);
|
|
63
|
+
export const SINGULAR_RESOURCES = new Set([
|
|
64
|
+
'user', 'post', 'comment', 'article', 'product', 'order',
|
|
65
|
+
'item', 'category', 'tag', 'file', 'image', 'document',
|
|
66
|
+
'message', 'notification', 'event', 'task', 'project',
|
|
67
|
+
'team', 'member', 'role', 'permission', 'setting',
|
|
68
|
+
'account', 'profile', 'session', 'token', 'key',
|
|
69
|
+
'log', 'metric', 'report', 'analytic', 'dashboard',
|
|
70
|
+
]);
|
|
71
|
+
export const MAX_NESTING_DEPTH = 3;
|
|
72
|
+
export const EXCLUDED_FILE_PATTERNS = [
|
|
73
|
+
/\.test\.[jt]sx?$/,
|
|
74
|
+
/\.spec\.[jt]sx?$/,
|
|
75
|
+
/\.stories\.[jt]sx?$/,
|
|
76
|
+
/\.d\.ts$/,
|
|
77
|
+
/node_modules\//,
|
|
78
|
+
];
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// Helper Functions
|
|
81
|
+
// ============================================================================
|
|
82
|
+
export function shouldExcludeFile(filePath) {
|
|
83
|
+
return EXCLUDED_FILE_PATTERNS.some(pattern => pattern.test(filePath));
|
|
84
|
+
}
|
|
85
|
+
export function detectCasing(segment) {
|
|
86
|
+
if (segment.startsWith(':') || segment.startsWith('[')) {
|
|
87
|
+
return 'lowercase';
|
|
88
|
+
}
|
|
89
|
+
if (segment.includes('-')) {
|
|
90
|
+
return 'kebab-case';
|
|
91
|
+
}
|
|
92
|
+
if (segment.includes('_')) {
|
|
93
|
+
return 'snake_case';
|
|
94
|
+
}
|
|
95
|
+
if (/[A-Z]/.test(segment) && /[a-z]/.test(segment)) {
|
|
96
|
+
return 'camelCase';
|
|
97
|
+
}
|
|
98
|
+
return 'lowercase';
|
|
99
|
+
}
|
|
100
|
+
export function isPlural(resource) {
|
|
101
|
+
const normalized = resource.toLowerCase().replace(/[-_]/g, '');
|
|
102
|
+
return PLURAL_RESOURCES.has(normalized) || normalized.endsWith('s');
|
|
103
|
+
}
|
|
104
|
+
export function isSingular(resource) {
|
|
105
|
+
const normalized = resource.toLowerCase().replace(/[-_]/g, '');
|
|
106
|
+
return SINGULAR_RESOURCES.has(normalized);
|
|
107
|
+
}
|
|
108
|
+
export function toPlural(singular) {
|
|
109
|
+
if (singular.endsWith('y') && !/[aeiou]y$/.test(singular)) {
|
|
110
|
+
return singular.slice(0, -1) + 'ies';
|
|
111
|
+
}
|
|
112
|
+
if (singular.endsWith('s') || singular.endsWith('x') || singular.endsWith('ch') || singular.endsWith('sh')) {
|
|
113
|
+
return singular + 'es';
|
|
114
|
+
}
|
|
115
|
+
return singular + 's';
|
|
116
|
+
}
|
|
117
|
+
export function toKebabCase(str) {
|
|
118
|
+
return str
|
|
119
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
120
|
+
.replace(/_/g, '-')
|
|
121
|
+
.toLowerCase();
|
|
122
|
+
}
|
|
123
|
+
export function calculateNestingDepth(routePath) {
|
|
124
|
+
const segments = routePath.replace(/^\//, '').split('/').filter(Boolean);
|
|
125
|
+
let depth = 0;
|
|
126
|
+
for (const segment of segments) {
|
|
127
|
+
if (/^v\d+$/.test(segment) || segment === 'api') {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (segment.startsWith(':') || segment.startsWith('[')) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
depth++;
|
|
134
|
+
}
|
|
135
|
+
return depth;
|
|
136
|
+
}
|
|
137
|
+
export function extractRouteParameters(routePath) {
|
|
138
|
+
const params = [];
|
|
139
|
+
let match;
|
|
140
|
+
const expressRegex = new RegExp(ROUTE_PARAMETER_PATTERNS.express.source, 'g');
|
|
141
|
+
while ((match = expressRegex.exec(routePath)) !== null) {
|
|
142
|
+
if (match[1])
|
|
143
|
+
params.push(match[1]);
|
|
144
|
+
}
|
|
145
|
+
const nextjsRegex = new RegExp(ROUTE_PARAMETER_PATTERNS.nextjsDynamic.source, 'g');
|
|
146
|
+
while ((match = nextjsRegex.exec(routePath)) !== null) {
|
|
147
|
+
if (match[1])
|
|
148
|
+
params.push(match[1]);
|
|
149
|
+
}
|
|
150
|
+
return params;
|
|
151
|
+
}
|
|
152
|
+
function isInsideComment(content, index) {
|
|
153
|
+
const beforeIndex = content.slice(0, index);
|
|
154
|
+
const lastNewline = beforeIndex.lastIndexOf('\n');
|
|
155
|
+
const currentLine = beforeIndex.slice(lastNewline + 1);
|
|
156
|
+
if (currentLine.includes('//')) {
|
|
157
|
+
const commentStart = currentLine.indexOf('//');
|
|
158
|
+
const positionInLine = index - lastNewline - 1;
|
|
159
|
+
if (positionInLine > commentStart) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const lastBlockCommentStart = beforeIndex.lastIndexOf('/*');
|
|
164
|
+
const lastBlockCommentEnd = beforeIndex.lastIndexOf('*/');
|
|
165
|
+
if (lastBlockCommentStart > lastBlockCommentEnd) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
export function detectExpressRoutes(content, file) {
|
|
171
|
+
const results = [];
|
|
172
|
+
const lines = content.split('\n');
|
|
173
|
+
for (const pattern of EXPRESS_ROUTE_PATTERNS) {
|
|
174
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
175
|
+
let match;
|
|
176
|
+
while ((match = regex.exec(content)) !== null) {
|
|
177
|
+
if (isInsideComment(content, match.index)) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const beforeMatch = content.slice(0, match.index);
|
|
181
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
182
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
183
|
+
const column = match.index - lastNewline;
|
|
184
|
+
const httpMethod = match[1]?.toUpperCase();
|
|
185
|
+
const routePath = match[2] || match[1];
|
|
186
|
+
results.push({
|
|
187
|
+
type: 'express-route',
|
|
188
|
+
file,
|
|
189
|
+
line: lineNumber,
|
|
190
|
+
column,
|
|
191
|
+
matchedText: match[0],
|
|
192
|
+
routePath,
|
|
193
|
+
httpMethod,
|
|
194
|
+
parameters: extractRouteParameters(routePath || ''),
|
|
195
|
+
context: lines[lineNumber - 1] || '',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return results;
|
|
200
|
+
}
|
|
201
|
+
export function detectNextjsAppRouterPatterns(content, file) {
|
|
202
|
+
const results = [];
|
|
203
|
+
if (!NEXTJS_APP_ROUTER_PATTERN.test(file)) {
|
|
204
|
+
return results;
|
|
205
|
+
}
|
|
206
|
+
const routeMatch = file.match(/\/app(\/(?:api\/)?[^/]+(?:\/[^/]+)*?)\/route\.[jt]sx?$/);
|
|
207
|
+
if (routeMatch && routeMatch[1]) {
|
|
208
|
+
const routePath = routeMatch[1];
|
|
209
|
+
results.push({
|
|
210
|
+
type: 'nextjs-app-router',
|
|
211
|
+
file,
|
|
212
|
+
line: 1,
|
|
213
|
+
column: 1,
|
|
214
|
+
matchedText: file,
|
|
215
|
+
routePath,
|
|
216
|
+
parameters: extractRouteParameters(routePath),
|
|
217
|
+
context: `Next.js App Router: ${routePath}`,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const httpMethodPattern = /export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s*\(/gi;
|
|
221
|
+
const lines = content.split('\n');
|
|
222
|
+
let match;
|
|
223
|
+
while ((match = httpMethodPattern.exec(content)) !== null) {
|
|
224
|
+
const beforeMatch = content.slice(0, match.index);
|
|
225
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
226
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
227
|
+
const column = match.index - lastNewline;
|
|
228
|
+
results.push({
|
|
229
|
+
type: 'nextjs-app-router',
|
|
230
|
+
file,
|
|
231
|
+
line: lineNumber,
|
|
232
|
+
column,
|
|
233
|
+
matchedText: match[0],
|
|
234
|
+
httpMethod: match[1]?.toUpperCase(),
|
|
235
|
+
context: lines[lineNumber - 1] || '',
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return results;
|
|
239
|
+
}
|
|
240
|
+
export function detectNextjsPagesRouterPatterns(_content, file) {
|
|
241
|
+
const results = [];
|
|
242
|
+
if (!NEXTJS_PAGES_ROUTER_PATTERN.test(file)) {
|
|
243
|
+
return results;
|
|
244
|
+
}
|
|
245
|
+
const routeMatch = file.match(/\/pages\/api(\/[^.]+)\.[jt]sx?$/);
|
|
246
|
+
if (routeMatch && routeMatch[1]) {
|
|
247
|
+
const routePath = '/api' + routeMatch[1];
|
|
248
|
+
results.push({
|
|
249
|
+
type: 'nextjs-pages-router',
|
|
250
|
+
file,
|
|
251
|
+
line: 1,
|
|
252
|
+
column: 1,
|
|
253
|
+
matchedText: file,
|
|
254
|
+
routePath,
|
|
255
|
+
parameters: extractRouteParameters(routePath),
|
|
256
|
+
context: `Next.js Pages Router: ${routePath}`,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
261
|
+
export function detectVersioningPatterns(content, file) {
|
|
262
|
+
const results = [];
|
|
263
|
+
const lines = content.split('\n');
|
|
264
|
+
for (const pattern of API_VERSIONING_PATTERNS) {
|
|
265
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
266
|
+
let match;
|
|
267
|
+
while ((match = regex.exec(content)) !== null) {
|
|
268
|
+
if (isInsideComment(content, match.index)) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const beforeMatch = content.slice(0, match.index);
|
|
272
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
273
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
274
|
+
const column = match.index - lastNewline;
|
|
275
|
+
results.push({
|
|
276
|
+
type: 'versioned-api',
|
|
277
|
+
file,
|
|
278
|
+
line: lineNumber,
|
|
279
|
+
column,
|
|
280
|
+
matchedText: match[0],
|
|
281
|
+
context: lines[lineNumber - 1] || '',
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return results;
|
|
286
|
+
}
|
|
287
|
+
export function detectUrlLiterals(content, file) {
|
|
288
|
+
const results = [];
|
|
289
|
+
const lines = content.split('\n');
|
|
290
|
+
const urlPattern = /['"`](\/(?:api\/)?[a-zA-Z][a-zA-Z0-9/_:-]*(?:\?[^'"`]*)?)['"`]/g;
|
|
291
|
+
let match;
|
|
292
|
+
while ((match = urlPattern.exec(content)) !== null) {
|
|
293
|
+
if (isInsideComment(content, match.index)) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const routePath = match[1];
|
|
297
|
+
if (!routePath)
|
|
298
|
+
continue;
|
|
299
|
+
if (!routePath.includes('/api/') && !routePath.startsWith('/v') && !routePath.includes(':')) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const beforeMatch = content.slice(0, match.index);
|
|
303
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
304
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
305
|
+
const column = match.index - lastNewline;
|
|
306
|
+
results.push({
|
|
307
|
+
type: 'restful-route',
|
|
308
|
+
file,
|
|
309
|
+
line: lineNumber,
|
|
310
|
+
column,
|
|
311
|
+
matchedText: match[0],
|
|
312
|
+
routePath,
|
|
313
|
+
parameters: extractRouteParameters(routePath),
|
|
314
|
+
context: lines[lineNumber - 1] || '',
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return results;
|
|
318
|
+
}
|
|
319
|
+
export function detectCasingViolations(routePatterns, file) {
|
|
320
|
+
const violations = [];
|
|
321
|
+
const casingCounts = {
|
|
322
|
+
'kebab-case': 0,
|
|
323
|
+
'camelCase': 0,
|
|
324
|
+
'snake_case': 0,
|
|
325
|
+
'lowercase': 0,
|
|
326
|
+
};
|
|
327
|
+
for (const pattern of routePatterns) {
|
|
328
|
+
if (!pattern.routePath)
|
|
329
|
+
continue;
|
|
330
|
+
const segments = pattern.routePath.split('/').filter(Boolean);
|
|
331
|
+
for (const segment of segments) {
|
|
332
|
+
if (segment.startsWith(':') || segment.startsWith('[') ||
|
|
333
|
+
/^v\d+$/.test(segment) || segment === 'api') {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
const casing = detectCasing(segment);
|
|
337
|
+
casingCounts[casing]++;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
let dominantCasing = 'kebab-case';
|
|
341
|
+
let maxCount = 0;
|
|
342
|
+
for (const [casing, count] of Object.entries(casingCounts)) {
|
|
343
|
+
if (count > maxCount) {
|
|
344
|
+
maxCount = count;
|
|
345
|
+
dominantCasing = casing;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
for (const pattern of routePatterns) {
|
|
349
|
+
if (!pattern.routePath)
|
|
350
|
+
continue;
|
|
351
|
+
const segments = pattern.routePath.split('/').filter(Boolean);
|
|
352
|
+
for (const segment of segments) {
|
|
353
|
+
if (segment.startsWith(':') || segment.startsWith('[') ||
|
|
354
|
+
/^v\d+$/.test(segment) || segment === 'api') {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const casing = detectCasing(segment);
|
|
358
|
+
if (casing !== dominantCasing && casing !== 'lowercase') {
|
|
359
|
+
violations.push({
|
|
360
|
+
type: 'inconsistent-casing',
|
|
361
|
+
file,
|
|
362
|
+
line: pattern.line,
|
|
363
|
+
column: pattern.column,
|
|
364
|
+
endLine: pattern.line,
|
|
365
|
+
endColumn: pattern.column + pattern.matchedText.length,
|
|
366
|
+
value: segment,
|
|
367
|
+
issue: `URL segment '${segment}' uses ${casing} but project uses ${dominantCasing}`,
|
|
368
|
+
suggestedFix: dominantCasing === 'kebab-case' ? toKebabCase(segment) : segment,
|
|
369
|
+
lineContent: pattern.context || '',
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return violations;
|
|
375
|
+
}
|
|
376
|
+
export function detectNamingViolations(routePatterns, file) {
|
|
377
|
+
const violations = [];
|
|
378
|
+
let pluralCount = 0;
|
|
379
|
+
let singularCount = 0;
|
|
380
|
+
for (const pattern of routePatterns) {
|
|
381
|
+
if (!pattern.routePath)
|
|
382
|
+
continue;
|
|
383
|
+
const segments = pattern.routePath.split('/').filter(Boolean);
|
|
384
|
+
for (const segment of segments) {
|
|
385
|
+
if (segment.startsWith(':') || segment.startsWith('[') ||
|
|
386
|
+
/^v\d+$/.test(segment) || segment === 'api') {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (isPlural(segment)) {
|
|
390
|
+
pluralCount++;
|
|
391
|
+
}
|
|
392
|
+
else if (isSingular(segment)) {
|
|
393
|
+
singularCount++;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const usePlural = pluralCount >= singularCount;
|
|
398
|
+
for (const pattern of routePatterns) {
|
|
399
|
+
if (!pattern.routePath)
|
|
400
|
+
continue;
|
|
401
|
+
const segments = pattern.routePath.split('/').filter(Boolean);
|
|
402
|
+
for (const segment of segments) {
|
|
403
|
+
if (segment.startsWith(':') || segment.startsWith('[') ||
|
|
404
|
+
/^v\d+$/.test(segment) || segment === 'api') {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (usePlural && isSingular(segment)) {
|
|
408
|
+
violations.push({
|
|
409
|
+
type: 'inconsistent-naming',
|
|
410
|
+
file,
|
|
411
|
+
line: pattern.line,
|
|
412
|
+
column: pattern.column,
|
|
413
|
+
endLine: pattern.line,
|
|
414
|
+
endColumn: pattern.column + pattern.matchedText.length,
|
|
415
|
+
value: segment,
|
|
416
|
+
issue: `Resource '${segment}' should use plural form for RESTful consistency`,
|
|
417
|
+
suggestedFix: toPlural(segment),
|
|
418
|
+
lineContent: pattern.context || '',
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return violations;
|
|
424
|
+
}
|
|
425
|
+
export function detectMissingVersioning(routePatterns, file) {
|
|
426
|
+
const violations = [];
|
|
427
|
+
const hasVersioning = routePatterns.some(p => p.type === 'versioned-api' ||
|
|
428
|
+
(p.routePath && /\/v\d+\//.test(p.routePath)));
|
|
429
|
+
if (hasVersioning) {
|
|
430
|
+
for (const pattern of routePatterns) {
|
|
431
|
+
if (!pattern.routePath)
|
|
432
|
+
continue;
|
|
433
|
+
if (/\/v\d+\//.test(pattern.routePath)) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (!pattern.routePath.includes('/api/')) {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
violations.push({
|
|
440
|
+
type: 'missing-versioning',
|
|
441
|
+
file,
|
|
442
|
+
line: pattern.line,
|
|
443
|
+
column: pattern.column,
|
|
444
|
+
endLine: pattern.line,
|
|
445
|
+
endColumn: pattern.column + pattern.matchedText.length,
|
|
446
|
+
value: pattern.routePath,
|
|
447
|
+
issue: `API route '${pattern.routePath}' is missing version prefix`,
|
|
448
|
+
suggestedFix: pattern.routePath.replace('/api/', '/api/v1/'),
|
|
449
|
+
lineContent: pattern.context || '',
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return violations;
|
|
454
|
+
}
|
|
455
|
+
export function detectDeepNestingViolations(routePatterns, file) {
|
|
456
|
+
const violations = [];
|
|
457
|
+
for (const pattern of routePatterns) {
|
|
458
|
+
if (!pattern.routePath)
|
|
459
|
+
continue;
|
|
460
|
+
const depth = calculateNestingDepth(pattern.routePath);
|
|
461
|
+
if (depth > MAX_NESTING_DEPTH) {
|
|
462
|
+
violations.push({
|
|
463
|
+
type: 'deeply-nested',
|
|
464
|
+
file,
|
|
465
|
+
line: pattern.line,
|
|
466
|
+
column: pattern.column,
|
|
467
|
+
endLine: pattern.line,
|
|
468
|
+
endColumn: pattern.column + pattern.matchedText.length,
|
|
469
|
+
value: pattern.routePath,
|
|
470
|
+
issue: `Route '${pattern.routePath}' has ${depth} levels of nesting (max ${MAX_NESTING_DEPTH})`,
|
|
471
|
+
suggestedFix: 'Consider flattening the route structure or using query parameters',
|
|
472
|
+
lineContent: pattern.context || '',
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return violations;
|
|
477
|
+
}
|
|
478
|
+
export function analyzeRouteStructure(content, file) {
|
|
479
|
+
if (shouldExcludeFile(file)) {
|
|
480
|
+
return {
|
|
481
|
+
routePatterns: [],
|
|
482
|
+
violations: [],
|
|
483
|
+
usesRestfulPatterns: false,
|
|
484
|
+
usesVersioning: false,
|
|
485
|
+
detectedCasing: null,
|
|
486
|
+
usesPluralResources: false,
|
|
487
|
+
patternAdherenceConfidence: 1.0,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
const expressRoutes = detectExpressRoutes(content, file);
|
|
491
|
+
const nextjsAppRoutes = detectNextjsAppRouterPatterns(content, file);
|
|
492
|
+
const nextjsPagesRoutes = detectNextjsPagesRouterPatterns(content, file);
|
|
493
|
+
const versioningPatterns = detectVersioningPatterns(content, file);
|
|
494
|
+
const urlLiterals = detectUrlLiterals(content, file);
|
|
495
|
+
const routePatterns = [
|
|
496
|
+
...expressRoutes,
|
|
497
|
+
...nextjsAppRoutes,
|
|
498
|
+
...nextjsPagesRoutes,
|
|
499
|
+
...versioningPatterns,
|
|
500
|
+
...urlLiterals,
|
|
501
|
+
];
|
|
502
|
+
const casingViolations = detectCasingViolations(routePatterns, file);
|
|
503
|
+
const namingViolations = detectNamingViolations(routePatterns, file);
|
|
504
|
+
const versioningViolations = detectMissingVersioning(routePatterns, file);
|
|
505
|
+
const nestingViolations = detectDeepNestingViolations(routePatterns, file);
|
|
506
|
+
const violations = [
|
|
507
|
+
...casingViolations,
|
|
508
|
+
...namingViolations,
|
|
509
|
+
...versioningViolations,
|
|
510
|
+
...nestingViolations,
|
|
511
|
+
];
|
|
512
|
+
const usesRestfulPatterns = routePatterns.some(p => p.type === 'restful-route' || p.type === 'express-route');
|
|
513
|
+
const usesVersioning = versioningPatterns.length > 0 ||
|
|
514
|
+
routePatterns.some(p => p.routePath && /\/v\d+\//.test(p.routePath));
|
|
515
|
+
const casingCounts = {
|
|
516
|
+
'kebab-case': 0,
|
|
517
|
+
'camelCase': 0,
|
|
518
|
+
'snake_case': 0,
|
|
519
|
+
'lowercase': 0,
|
|
520
|
+
};
|
|
521
|
+
for (const pattern of routePatterns) {
|
|
522
|
+
if (!pattern.routePath)
|
|
523
|
+
continue;
|
|
524
|
+
const segments = pattern.routePath.split('/').filter(Boolean);
|
|
525
|
+
for (const segment of segments) {
|
|
526
|
+
if (!segment.startsWith(':') && !segment.startsWith('[') &&
|
|
527
|
+
!/^v\d+$/.test(segment) && segment !== 'api') {
|
|
528
|
+
casingCounts[detectCasing(segment)]++;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
let detectedCasing = null;
|
|
533
|
+
let maxCasingCount = 0;
|
|
534
|
+
for (const [casing, count] of Object.entries(casingCounts)) {
|
|
535
|
+
if (count > maxCasingCount) {
|
|
536
|
+
maxCasingCount = count;
|
|
537
|
+
detectedCasing = casing;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
let pluralCount = 0;
|
|
541
|
+
let singularCount = 0;
|
|
542
|
+
for (const pattern of routePatterns) {
|
|
543
|
+
if (!pattern.routePath)
|
|
544
|
+
continue;
|
|
545
|
+
const segments = pattern.routePath.split('/').filter(Boolean);
|
|
546
|
+
for (const segment of segments) {
|
|
547
|
+
if (isPlural(segment))
|
|
548
|
+
pluralCount++;
|
|
549
|
+
else if (isSingular(segment))
|
|
550
|
+
singularCount++;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const usesPluralResources = pluralCount >= singularCount;
|
|
554
|
+
const hasPatterns = routePatterns.length > 0;
|
|
555
|
+
const hasViolations = violations.length > 0;
|
|
556
|
+
let patternAdherenceConfidence = 1.0;
|
|
557
|
+
if (hasPatterns && hasViolations) {
|
|
558
|
+
patternAdherenceConfidence = Math.max(0, 1 - (violations.length / routePatterns.length));
|
|
559
|
+
}
|
|
560
|
+
else if (!hasPatterns) {
|
|
561
|
+
patternAdherenceConfidence = 0.5;
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
routePatterns,
|
|
565
|
+
violations,
|
|
566
|
+
usesRestfulPatterns,
|
|
567
|
+
usesVersioning,
|
|
568
|
+
detectedCasing,
|
|
569
|
+
usesPluralResources,
|
|
570
|
+
patternAdherenceConfidence,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
// ============================================================================
|
|
574
|
+
// Route Structure Detector Class
|
|
575
|
+
// ============================================================================
|
|
576
|
+
export class RouteStructureDetector extends RegexDetector {
|
|
577
|
+
id = 'api/route-structure';
|
|
578
|
+
category = 'api';
|
|
579
|
+
subcategory = 'route-structure';
|
|
580
|
+
name = 'Route Structure Detector';
|
|
581
|
+
description = 'Detects route URL structure patterns and flags inconsistencies';
|
|
582
|
+
supportedLanguages = ['typescript', 'javascript', 'python'];
|
|
583
|
+
async detect(context) {
|
|
584
|
+
const patterns = [];
|
|
585
|
+
const violations = [];
|
|
586
|
+
const analysis = analyzeRouteStructure(context.content, context.file);
|
|
587
|
+
if (analysis.usesRestfulPatterns) {
|
|
588
|
+
patterns.push(this.createRestfulPattern(context.file, analysis));
|
|
589
|
+
}
|
|
590
|
+
if (analysis.usesVersioning) {
|
|
591
|
+
patterns.push(this.createVersioningPattern(context.file, analysis));
|
|
592
|
+
}
|
|
593
|
+
const routeTypes = new Set(analysis.routePatterns.map(p => p.type));
|
|
594
|
+
for (const routeType of routeTypes) {
|
|
595
|
+
const firstOfType = analysis.routePatterns.find(p => p.type === routeType);
|
|
596
|
+
if (firstOfType) {
|
|
597
|
+
patterns.push({
|
|
598
|
+
patternId: `${this.id}/${routeType}`,
|
|
599
|
+
location: {
|
|
600
|
+
file: context.file,
|
|
601
|
+
line: firstOfType.line,
|
|
602
|
+
column: firstOfType.column,
|
|
603
|
+
},
|
|
604
|
+
confidence: 1.0,
|
|
605
|
+
isOutlier: false,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
for (const violation of analysis.violations) {
|
|
610
|
+
violations.push(this.createViolation(violation));
|
|
611
|
+
}
|
|
612
|
+
return this.createResult(patterns, violations, analysis.patternAdherenceConfidence);
|
|
613
|
+
}
|
|
614
|
+
createRestfulPattern(file, analysis) {
|
|
615
|
+
const restfulPatterns = analysis.routePatterns.filter(p => p.type === 'restful-route' || p.type === 'express-route');
|
|
616
|
+
const firstPattern = restfulPatterns[0];
|
|
617
|
+
return {
|
|
618
|
+
patternId: `${this.id}/restful`,
|
|
619
|
+
location: {
|
|
620
|
+
file,
|
|
621
|
+
line: firstPattern?.line || 1,
|
|
622
|
+
column: firstPattern?.column || 1,
|
|
623
|
+
},
|
|
624
|
+
confidence: 1.0,
|
|
625
|
+
isOutlier: false,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
createVersioningPattern(file, analysis) {
|
|
629
|
+
const versionedPatterns = analysis.routePatterns.filter(p => p.type === 'versioned-api');
|
|
630
|
+
const firstPattern = versionedPatterns[0];
|
|
631
|
+
return {
|
|
632
|
+
patternId: `${this.id}/versioning`,
|
|
633
|
+
location: {
|
|
634
|
+
file,
|
|
635
|
+
line: firstPattern?.line || 1,
|
|
636
|
+
column: firstPattern?.column || 1,
|
|
637
|
+
},
|
|
638
|
+
confidence: 1.0,
|
|
639
|
+
isOutlier: false,
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
createViolation(info) {
|
|
643
|
+
const violation = {
|
|
644
|
+
id: `${this.id}-${info.file}-${info.line}-${info.column}`,
|
|
645
|
+
patternId: this.id,
|
|
646
|
+
severity: info.type === 'deeply-nested' ? 'warning' : 'info',
|
|
647
|
+
file: info.file,
|
|
648
|
+
range: {
|
|
649
|
+
start: { line: info.line - 1, character: info.column - 1 },
|
|
650
|
+
end: { line: info.endLine - 1, character: info.endColumn - 1 },
|
|
651
|
+
},
|
|
652
|
+
message: info.issue,
|
|
653
|
+
explanation: this.getExplanation(info.type),
|
|
654
|
+
expected: info.suggestedFix || 'Follow established route patterns',
|
|
655
|
+
actual: info.value,
|
|
656
|
+
aiExplainAvailable: true,
|
|
657
|
+
aiFixAvailable: !!info.suggestedFix,
|
|
658
|
+
firstSeen: new Date(),
|
|
659
|
+
occurrences: 1,
|
|
660
|
+
};
|
|
661
|
+
if (info.suggestedFix) {
|
|
662
|
+
const quickFix = this.createQuickFixForViolation(info);
|
|
663
|
+
if (quickFix) {
|
|
664
|
+
violation.quickFix = quickFix;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return violation;
|
|
668
|
+
}
|
|
669
|
+
getExplanation(type) {
|
|
670
|
+
const explanations = {
|
|
671
|
+
'inconsistent-casing': 'Consistent URL casing improves API discoverability and reduces errors. ' +
|
|
672
|
+
'Choose one convention (kebab-case is recommended) and use it throughout.',
|
|
673
|
+
'inconsistent-naming': 'RESTful APIs should use plural nouns for resource collections (e.g., /users, /posts). ' +
|
|
674
|
+
'This makes the API more intuitive and follows REST conventions.',
|
|
675
|
+
'missing-versioning': 'API versioning allows you to make breaking changes without affecting existing clients. ' +
|
|
676
|
+
'Use URL path versioning (e.g., /api/v1/) for consistency.',
|
|
677
|
+
'deeply-nested': 'Deeply nested routes are harder to understand and maintain. ' +
|
|
678
|
+
'Consider flattening the structure or using query parameters for filtering.',
|
|
679
|
+
'non-restful': 'RESTful URL patterns use nouns for resources and HTTP methods for actions. ' +
|
|
680
|
+
'Avoid verbs in URLs (e.g., use DELETE /users/:id instead of POST /users/:id/delete).',
|
|
681
|
+
};
|
|
682
|
+
return explanations[type] || 'Follow established route structure patterns for consistency.';
|
|
683
|
+
}
|
|
684
|
+
createQuickFixForViolation(info) {
|
|
685
|
+
if (!info.suggestedFix) {
|
|
686
|
+
return undefined;
|
|
687
|
+
}
|
|
688
|
+
return {
|
|
689
|
+
title: `Fix: ${info.suggestedFix}`,
|
|
690
|
+
kind: 'quickfix',
|
|
691
|
+
edit: {
|
|
692
|
+
changes: {
|
|
693
|
+
[info.file]: [
|
|
694
|
+
{
|
|
695
|
+
range: {
|
|
696
|
+
start: { line: info.line - 1, character: info.column - 1 },
|
|
697
|
+
end: { line: info.endLine - 1, character: info.endColumn - 1 },
|
|
698
|
+
},
|
|
699
|
+
newText: info.lineContent.replace(info.value, info.suggestedFix),
|
|
700
|
+
},
|
|
701
|
+
],
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
isPreferred: true,
|
|
705
|
+
confidence: 0.7,
|
|
706
|
+
preview: `Replace '${info.value}' with '${info.suggestedFix}'`,
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
generateQuickFix(violation) {
|
|
710
|
+
if (!violation.patternId.startsWith('api/route-structure')) {
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
if (violation.expected && violation.expected !== 'Follow established route patterns') {
|
|
714
|
+
return {
|
|
715
|
+
title: `Fix: ${violation.expected}`,
|
|
716
|
+
kind: 'quickfix',
|
|
717
|
+
edit: {
|
|
718
|
+
changes: {
|
|
719
|
+
[violation.file]: [
|
|
720
|
+
{
|
|
721
|
+
range: violation.range,
|
|
722
|
+
newText: violation.expected,
|
|
723
|
+
},
|
|
724
|
+
],
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
isPreferred: true,
|
|
728
|
+
confidence: 0.7,
|
|
729
|
+
preview: `Replace '${violation.actual}' with '${violation.expected}'`,
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
export function createRouteStructureDetector() {
|
|
736
|
+
return new RouteStructureDetector();
|
|
737
|
+
}
|
|
738
|
+
//# sourceMappingURL=route-structure.js.map
|