@warlock.js/core 5.30.0 → 5.31.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/CHANGELOG.md +142 -36
- package/README.md +28 -23
- package/bin/source-load-hook.mjs +63 -0
- package/bin/warlock.js +102 -42
- package/esm/cli/commands/add.command.mjs +4 -2
- package/esm/cli/commands/add.command.mjs.map +1 -1
- package/esm/cli/commands/agents-md/agents-md-command.action.mjs +69 -0
- package/esm/cli/commands/agents-md/agents-md-command.action.mjs.map +1 -0
- package/esm/cli/commands/agents-md.command.mjs +38 -0
- package/esm/cli/commands/agents-md.command.mjs.map +1 -0
- package/esm/cli/commands/board/board-command.action.mjs +279 -0
- package/esm/cli/commands/board/board-command.action.mjs.map +1 -0
- package/esm/cli/commands/board.command.mjs +78 -0
- package/esm/cli/commands/board.command.mjs.map +1 -0
- package/esm/cli/commands/build.command.mjs +6 -1
- package/esm/cli/commands/build.command.mjs.map +1 -1
- package/esm/cli/commands/check/check-command.action.mjs +57 -0
- package/esm/cli/commands/check/check-command.action.mjs.map +1 -0
- package/esm/cli/commands/check/child-typecheck.mjs +66 -0
- package/esm/cli/commands/check/child-typecheck.mjs.map +1 -0
- package/esm/cli/commands/check/format-problems.mjs +43 -0
- package/esm/cli/commands/check/format-problems.mjs.map +1 -0
- package/esm/cli/commands/check/typecheck-command.action.mjs +61 -0
- package/esm/cli/commands/check/typecheck-command.action.mjs.map +1 -0
- package/esm/cli/commands/check.command.mjs +50 -0
- package/esm/cli/commands/check.command.mjs.map +1 -0
- package/esm/cli/commands/dev-server.command.mjs +9 -1
- package/esm/cli/commands/dev-server.command.mjs.map +1 -1
- package/esm/cli/commands/doctor/boot-for-diagnostics.mjs +8 -3
- package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
- package/esm/cli/commands/doctor/check.types.d.mts +76 -0
- package/esm/cli/commands/doctor/check.types.d.mts.map +1 -0
- package/esm/cli/commands/doctor/checks/config.check.mjs +1 -0
- package/esm/cli/commands/doctor/checks/config.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/connectors.check.mjs +2 -0
- package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs +1 -0
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/health.check.mjs +3 -0
- package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/jwt-secret.check.mjs +1 -0
- package/esm/cli/commands/doctor/checks/jwt-secret.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/locales-ts-deprecated.check.mjs +1 -0
- package/esm/cli/commands/doctor/checks/locales-ts-deprecated.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs +1 -0
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs +4 -0
- package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/routes.check.mjs +3 -0
- package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/doctor-command.action.mjs +14 -6
- package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
- package/esm/cli/commands/doctor/format-report.mjs +30 -2
- package/esm/cli/commands/doctor/format-report.mjs.map +1 -1
- package/esm/cli/commands/doctor.command.mjs +4 -0
- package/esm/cli/commands/doctor.command.mjs.map +1 -1
- package/esm/cli/commands/explain/explain-command.action.mjs +64 -0
- package/esm/cli/commands/explain/explain-command.action.mjs.map +1 -0
- package/esm/cli/commands/explain.command.mjs +23 -0
- package/esm/cli/commands/explain.command.mjs.map +1 -0
- package/esm/cli/commands/generate/generate.command.mjs +124 -124
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/endpoint.generator.mjs +311 -0
- package/esm/cli/commands/generate/generators/endpoint.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/migration.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs +1 -1
- package/esm/cli/commands/generate/templates/endpoint-stubs.mjs +341 -0
- package/esm/cli/commands/generate/templates/endpoint-stubs.mjs.map +1 -0
- package/esm/cli/commands/generate/templates/stubs.mjs +19 -6
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/endpoint-schema.mjs +137 -0
- package/esm/cli/commands/generate/utils/endpoint-schema.mjs.map +1 -0
- package/esm/cli/commands/generate/utils/name-parser.mjs +1 -1
- package/esm/cli/commands/generate/utils/name-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/writer.mjs +38 -13
- package/esm/cli/commands/generate/utils/writer.mjs.map +1 -1
- package/esm/cli/commands/inspect/inspect-command.action.mjs +72 -0
- package/esm/cli/commands/inspect/inspect-command.action.mjs.map +1 -0
- package/esm/cli/commands/inspect.command.mjs +39 -0
- package/esm/cli/commands/inspect.command.mjs.map +1 -0
- package/esm/cli/commands/mcp/mcp-command.action.mjs +103 -0
- package/esm/cli/commands/mcp/mcp-command.action.mjs.map +1 -0
- package/esm/cli/commands/mcp.command.mjs +76 -0
- package/esm/cli/commands/mcp.command.mjs.map +1 -0
- package/esm/cli/commands/openapi-generator.action.mjs.map +1 -1
- package/esm/cli/commands/openapi-generator.command.mjs.map +1 -1
- package/esm/cli/commands/postman-generator.action.mjs.map +1 -1
- package/esm/cli/commands/postman-generator.command.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-diff.action.mjs +1 -1
- package/esm/cli/commands/test.command.mjs +131 -0
- package/esm/cli/commands/test.command.mjs.map +1 -0
- package/esm/cli/commands/trace/trace-command.action.mjs +145 -0
- package/esm/cli/commands/trace/trace-command.action.mjs.map +1 -0
- package/esm/cli/commands/trace.command.mjs +60 -0
- package/esm/cli/commands/trace.command.mjs.map +1 -0
- package/esm/cli/commands/typecheck.command.mjs +40 -0
- package/esm/cli/commands/typecheck.command.mjs.map +1 -0
- package/esm/cli/commands/upgrade.command.mjs +46 -0
- package/esm/cli/commands/upgrade.command.mjs.map +1 -0
- package/esm/cli/flush-and-exit.mjs +34 -0
- package/esm/cli/flush-and-exit.mjs.map +1 -0
- package/esm/cli/framework-cli-commands.mjs +23 -1
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/cli/parse-cli-args.mjs +1 -0
- package/esm/cli/parse-cli-args.mjs.map +1 -1
- package/esm/config/config-loader.d.mts.map +1 -1
- package/esm/config/config-loader.mjs +34 -13
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/config/config-schema.d.mts +67 -0
- package/esm/config/config-schema.d.mts.map +1 -0
- package/esm/config/config-schema.mjs +77 -0
- package/esm/config/config-schema.mjs.map +1 -0
- package/esm/config/index.d.mts +6 -0
- package/esm/config/index.mjs +1 -0
- package/esm/connectors/assert-no-reserved-connector-names.d.mts.map +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.mjs +2 -1
- package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
- package/esm/connectors/assert-unique-connector-names.d.mts.map +1 -1
- package/esm/connectors/assert-unique-connector-names.mjs +26 -2
- package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +3 -2
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/single-server-warnings.mjs.map +1 -1
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/container/index.d.mts.map +1 -1
- package/esm/container/index.mjs.map +1 -1
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/models/database-log/database-log.d.mts +0 -28
- package/esm/database/models/database-log/database-log.d.mts.map +1 -1
- package/esm/database/seeds/seeder.errors.d.mts +4 -0
- package/esm/database/seeds/seeder.errors.d.mts.map +1 -1
- package/esm/database/seeds/seeder.errors.mjs +2 -0
- package/esm/database/seeds/seeder.errors.mjs.map +1 -1
- package/esm/dev-server/boot-precondition-error.mjs +1 -0
- package/esm/dev-server/boot-precondition-error.mjs.map +1 -1
- package/esm/dev-server/create-worker.mjs +15 -9
- package/esm/dev-server/create-worker.mjs.map +1 -1
- package/esm/dev-server/dev-agent-tooling.mjs +135 -0
- package/esm/dev-server/dev-agent-tooling.mjs.map +1 -0
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +8 -0
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +4 -5
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +1 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs +2 -3
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs +33 -0
- package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs.map +1 -0
- package/esm/dev-server/health-checker/default-health-checkers.mjs +31 -0
- package/esm/dev-server/health-checker/default-health-checkers.mjs.map +1 -0
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +9 -3
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts.map +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs +15 -4
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
- package/esm/dev-server/health-checker/oxlint-runner.mjs +302 -0
- package/esm/dev-server/health-checker/oxlint-runner.mjs.map +1 -0
- package/esm/dev-server/health-checker/workers/oxlint-health.worker.d.mts +1 -0
- package/esm/dev-server/health-checker/workers/oxlint-health.worker.mjs +42 -0
- package/esm/dev-server/health-checker/workers/oxlint-health.worker.mjs.map +1 -0
- package/esm/dev-server/loader/loader-port-acks.mjs +1 -1
- package/esm/dev-server/loader/loader-port-acks.mjs.map +1 -1
- package/esm/dev-server/loader/register-loader.mjs +1 -1
- package/esm/dev-server/loader/register-loader.mjs.map +1 -1
- package/esm/dev-server/loader/resolve-hook.mjs +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs +1 -1
- package/esm/dev-server/model-resource-typings.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs +1 -0
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +1 -1
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/route-types-publisher.mjs +1 -1
- package/esm/dev-server/start-development-server.mjs.map +1 -1
- package/esm/dev-server/translation-keys-sources.mjs +1 -1
- package/esm/dev-server/translation-keys-sources.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs +3 -3
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/types.d.mts.map +1 -1
- package/esm/errors/container-key-missing-error.mjs +1 -0
- package/esm/errors/container-key-missing-error.mjs.map +1 -1
- package/esm/errors/error-codes.d.mts +653 -0
- package/esm/errors/error-codes.d.mts.map +1 -0
- package/esm/errors/error-codes.mjs +634 -0
- package/esm/errors/error-codes.mjs.map +1 -0
- package/esm/errors/esbuild-binary-missing-error.mjs +1 -0
- package/esm/errors/esbuild-binary-missing-error.mjs.map +1 -1
- package/esm/errors/index.d.mts +3 -1
- package/esm/errors/index.mjs +3 -1
- package/esm/errors/unknown-locale-error.mjs +1 -0
- package/esm/errors/unknown-locale-error.mjs.map +1 -1
- package/esm/errors/warlock-error.d.mts +23 -0
- package/esm/errors/warlock-error.d.mts.map +1 -0
- package/esm/errors/warlock-error.mjs +22 -0
- package/esm/errors/warlock-error.mjs.map +1 -0
- package/esm/generations/add-command.action.mjs +19 -3
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/features/access.feature.mjs +1 -1
- package/esm/generations/features/access.feature.mjs.map +1 -1
- package/esm/generations/features/bull-board.feature.mjs +1 -1
- package/esm/generations/features/bull-board.feature.mjs.map +1 -1
- package/esm/generations/features/mysql.feature.mjs.map +1 -1
- package/esm/generations/features/notifications.feature.mjs +5 -2
- package/esm/generations/features/notifications.feature.mjs.map +1 -1
- package/esm/generations/features/queue.feature.mjs +1 -1
- package/esm/generations/features/queue.feature.mjs.map +1 -1
- package/esm/generations/features/react-email.feature.mjs +1 -1
- package/esm/generations/features/react-email.feature.mjs.map +1 -1
- package/esm/generations/features/shadcn.feature.mjs +1 -1
- package/esm/generations/features/shadcn.feature.mjs.map +1 -1
- package/esm/generations/features/shared/link-ai-package-import.mjs +1 -1
- package/esm/generations/features/shared/link-ai-package-import.mjs.map +1 -1
- package/esm/generations/features/shared/merge-web-sitemap-config.mjs.map +1 -1
- package/esm/generations/features/shared/patch-tsconfig-include.mjs +1 -1
- package/esm/generations/features/shared/patch-tsconfig-include.mjs.map +1 -1
- package/esm/generations/features/shared/relocate-conflicting-home-route.mjs +1 -1
- package/esm/generations/features/shared/relocate-conflicting-home-route.mjs.map +1 -1
- package/esm/generations/features/sitemap.feature.mjs +1 -1
- package/esm/generations/features/sitemap.feature.mjs.map +1 -1
- package/esm/generations/features/tailwind.feature.mjs +1 -1
- package/esm/generations/features/tailwind.feature.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs +11 -6
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +1 -1
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/generations/generation-fs.mjs +73 -0
- package/esm/generations/generation-fs.mjs.map +1 -0
- package/esm/generations/generation-run.mjs +185 -0
- package/esm/generations/generation-run.mjs.map +1 -0
- package/esm/generations/stubs.mjs +155 -13
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/bounded-stream-queue.d.mts +10 -0
- package/esm/http/bounded-stream-queue.d.mts.map +1 -0
- package/esm/http/bounded-stream-queue.mjs +97 -0
- package/esm/http/bounded-stream-queue.mjs.map +1 -0
- package/esm/http/build-rate-limit-options.d.mts +13 -0
- package/esm/http/build-rate-limit-options.d.mts.map +1 -1
- package/esm/http/build-rate-limit-options.mjs.map +1 -1
- package/esm/http/conditional-request.d.mts +86 -0
- package/esm/http/conditional-request.d.mts.map +1 -0
- package/esm/http/conditional-request.mjs +121 -0
- package/esm/http/conditional-request.mjs.map +1 -0
- package/esm/http/csp.d.mts +2 -0
- package/esm/http/csp.d.mts.map +1 -1
- package/esm/http/csp.mjs +1 -0
- package/esm/http/csp.mjs.map +1 -1
- package/esm/http/csrf-origin-policy.mjs.map +1 -1
- package/esm/http/database/RequestLog.d.mts +24 -84
- package/esm/http/database/RequestLog.d.mts.map +1 -1
- package/esm/http/errors/errors.d.mts +2 -0
- package/esm/http/errors/errors.d.mts.map +1 -1
- package/esm/http/errors/errors.mjs +1 -0
- package/esm/http/errors/errors.mjs.map +1 -1
- package/esm/http/escape-header-value.d.mts +18 -0
- package/esm/http/escape-header-value.d.mts.map +1 -0
- package/esm/http/escape-header-value.mjs +27 -0
- package/esm/http/escape-header-value.mjs.map +1 -0
- package/esm/http/flush-pending-cookies.mjs.map +1 -1
- package/esm/http/index.d.mts +5 -2
- package/esm/http/index.mjs +4 -1
- package/esm/http/lazy-static.mjs +194 -0
- package/esm/http/lazy-static.mjs.map +1 -0
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/plugins.d.mts.map +1 -1
- package/esm/http/plugins.mjs +7 -4
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/rate-limit-cache-store.mjs +2 -1
- package/esm/http/rate-limit-cache-store.mjs.map +1 -1
- package/esm/http/request-log-snapshot.d.mts +12 -5
- package/esm/http/request-log-snapshot.d.mts.map +1 -1
- package/esm/http/request-log-snapshot.mjs +83 -4
- package/esm/http/request-log-snapshot.mjs.map +1 -1
- package/esm/http/request.d.mts +2 -2
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +5 -4
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts +11 -1
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs +164 -76
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/safe-redirect.d.mts +51 -0
- package/esm/http/safe-redirect.d.mts.map +1 -0
- package/esm/http/safe-redirect.mjs +80 -0
- package/esm/http/safe-redirect.mjs.map +1 -0
- package/esm/http/serialized.d.mts.map +1 -1
- package/esm/http/server.d.mts +3 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs +14 -4
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/stream-transport.mjs +4 -2
- package/esm/http/stream-transport.mjs.map +1 -1
- package/esm/http/types.d.mts +44 -11
- package/esm/http/types.d.mts.map +1 -1
- package/esm/http/uploads/generate-image-variant.mjs +1 -1
- package/esm/http/uploads/generate-image-variants.d.mts +10 -7
- package/esm/http/uploads/generate-image-variants.d.mts.map +1 -1
- package/esm/http/uploads/generate-image-variants.mjs +61 -24
- package/esm/http/uploads/generate-image-variants.mjs.map +1 -1
- package/esm/http/uploads/image-variant-cache-entries.mjs +78 -0
- package/esm/http/uploads/image-variant-cache-entries.mjs.map +1 -0
- package/esm/http/uploads/image-variant-cache.mjs +72 -30
- package/esm/http/uploads/image-variant-cache.mjs.map +1 -1
- package/esm/http/uploads/image-variant-types.d.mts +2 -0
- package/esm/http/uploads/image-variant-types.d.mts.map +1 -1
- package/esm/http/uploads/image-variants-config-error.d.mts +3 -1
- package/esm/http/uploads/image-variants-config-error.d.mts.map +1 -1
- package/esm/http/uploads/image-variants-config-error.mjs +3 -2
- package/esm/http/uploads/image-variants-config-error.mjs.map +1 -1
- package/esm/http/uploads/load-variant-source.mjs +1 -1
- package/esm/http/uploads/load-variant-source.mjs.map +1 -1
- package/esm/http/uploads/purge-image-variants.d.mts +7 -2
- package/esm/http/uploads/purge-image-variants.d.mts.map +1 -1
- package/esm/http/uploads/purge-image-variants.mjs +16 -16
- package/esm/http/uploads/purge-image-variants.mjs.map +1 -1
- package/esm/http/uploads/remote-variant-source.mjs +115 -0
- package/esm/http/uploads/remote-variant-source.mjs.map +1 -0
- package/esm/http/uploads/resolve-image-variant.d.mts +3 -4
- package/esm/http/uploads/resolve-image-variant.d.mts.map +1 -1
- package/esm/http/uploads/resolve-image-variant.mjs +10 -18
- package/esm/http/uploads/resolve-image-variant.mjs.map +1 -1
- package/esm/http/uploads/resolve-image-variants-config.mjs +5 -3
- package/esm/http/uploads/resolve-image-variants-config.mjs.map +1 -1
- package/esm/http/uploads/resolve-remote-image-variant.mjs +89 -0
- package/esm/http/uploads/resolve-remote-image-variant.mjs.map +1 -0
- package/esm/http/uploads/trim-image-variant-cache.mjs +193 -0
- package/esm/http/uploads/trim-image-variant-cache.mjs.map +1 -0
- package/esm/http/uploads/uploaded-file.controller.d.mts +5 -3
- package/esm/http/uploads/uploaded-file.controller.d.mts.map +1 -1
- package/esm/http/uploads/uploaded-file.controller.mjs +16 -8
- package/esm/http/uploads/uploaded-file.controller.mjs.map +1 -1
- package/esm/http/uploads/variant-cache-key.mjs +1 -1
- package/esm/http/uploads-types.d.mts +6 -2
- package/esm/http/uploads-types.d.mts.map +1 -1
- package/esm/index.d.mts +17 -6
- package/esm/index.mjs +14 -3
- package/esm/localization/parse-locale-dictionary.d.mts +2 -0
- package/esm/localization/parse-locale-dictionary.d.mts.map +1 -1
- package/esm/localization/parse-locale-dictionary.mjs +1 -0
- package/esm/localization/parse-locale-dictionary.mjs.map +1 -1
- package/esm/mail/config.d.mts +12 -1
- package/esm/mail/config.d.mts.map +1 -1
- package/esm/mail/config.mjs +16 -1
- package/esm/mail/config.mjs.map +1 -1
- package/esm/mail/index.d.mts +1 -1
- package/esm/mail/index.mjs +1 -1
- package/esm/net/index.d.mts +2 -0
- package/esm/net/index.mjs +3 -0
- package/esm/openapi/build-openapi-document.d.mts.map +1 -1
- package/esm/openapi/build-openapi-document.mjs +8 -5
- package/esm/openapi/build-openapi-document.mjs.map +1 -1
- package/esm/openapi/error-responses.mjs +28 -2
- package/esm/openapi/error-responses.mjs.map +1 -1
- package/esm/openapi/postman/build-request-body.mjs.map +1 -1
- package/esm/openapi/postman/build-request-url.mjs.map +1 -1
- package/esm/openapi/postman/build-saved-responses.mjs.map +1 -1
- package/esm/openapi/postman/example-from-schema.d.mts.map +1 -1
- package/esm/openapi/postman/example-from-schema.mjs.map +1 -1
- package/esm/openapi/postman/openapi-to-postman-collection.d.mts.map +1 -1
- package/esm/openapi/postman/openapi-to-postman-collection.mjs.map +1 -1
- package/esm/openapi/postman/schema-refs.mjs.map +1 -1
- package/esm/openapi/postman/uuid-v5.mjs.map +1 -1
- package/esm/openapi/request-schemas.mjs.map +1 -1
- package/esm/openapi/response-schemas.mjs.map +1 -1
- package/esm/openapi/schema-registry.d.mts +1 -0
- package/esm/openapi/schema-registry.mjs.map +1 -1
- package/esm/openapi/security.mjs.map +1 -1
- package/esm/openapi/types.d.mts +3 -2
- package/esm/openapi/types.d.mts.map +1 -1
- package/esm/production/assert-generated-imports.mjs +1 -0
- package/esm/production/assert-generated-imports.mjs.map +1 -1
- package/esm/production/production-builder.mjs +12 -4
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/resolve-build-config.mjs +1 -0
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/production/route-registration-child.mjs.map +1 -1
- package/esm/production/route-registration-snapshot.mjs.map +1 -1
- package/esm/repositories/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/repositories/contracts/types.d.mts.map +1 -1
- package/esm/repositories/repository.manager.mjs.map +1 -1
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/resource/types.d.mts.map +1 -1
- package/esm/restful/restful.d.mts +5 -5
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/build-route-rate-limit.mjs.map +1 -1
- package/esm/router/dev-dispatch.mjs +31 -12
- package/esm/router/dev-dispatch.mjs.map +1 -1
- package/esm/router/index.d.mts +4 -2
- package/esm/router/index.mjs +2 -0
- package/esm/router/log-request-lifecycle.mjs.map +1 -1
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
- package/esm/router/resolve-route-rate-limit.mjs +47 -0
- package/esm/router/resolve-route-rate-limit.mjs.map +1 -0
- package/esm/router/resolve-route-validation.mjs +19 -0
- package/esm/router/resolve-route-validation.mjs.map +1 -0
- package/esm/router/route-guard-names.mjs +30 -0
- package/esm/router/route-guard-names.mjs.map +1 -0
- package/esm/router/route-name-taken-error.d.mts +14 -0
- package/esm/router/route-name-taken-error.d.mts.map +1 -0
- package/esm/router/route-name-taken-error.mjs +18 -0
- package/esm/router/route-name-taken-error.mjs.map +1 -0
- package/esm/router/router.d.mts +32 -13
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +71 -38
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +137 -17
- package/esm/router/types.d.mts.map +1 -1
- package/esm/router/unguarded-route-diagnostics.d.mts +40 -0
- package/esm/router/unguarded-route-diagnostics.d.mts.map +1 -0
- package/esm/router/unguarded-route-diagnostics.mjs +95 -0
- package/esm/router/unguarded-route-diagnostics.mjs.map +1 -0
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/drivers/local-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.mjs.map +1 -1
- package/esm/storage/local-disk-roots.mjs +24 -0
- package/esm/storage/local-disk-roots.mjs.map +1 -0
- package/esm/storage/storage.d.mts +14 -0
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/storage.mjs +22 -0
- package/esm/storage/storage.mjs.map +1 -1
- package/esm/storage/types.d.mts.map +1 -1
- package/esm/storage/utils/storage-not-initialized-error.d.mts +2 -0
- package/esm/storage/utils/storage-not-initialized-error.d.mts.map +1 -1
- package/esm/storage/utils/storage-not-initialized-error.mjs +1 -0
- package/esm/storage/utils/storage-not-initialized-error.mjs.map +1 -1
- package/esm/tests/authenticated-test-client.d.mts +32 -0
- package/esm/tests/authenticated-test-client.d.mts.map +1 -0
- package/esm/tests/authenticated-test-client.mjs +40 -0
- package/esm/tests/authenticated-test-client.mjs.map +1 -0
- package/esm/tests/factories.d.mts +59 -0
- package/esm/tests/factories.d.mts.map +1 -0
- package/esm/tests/factories.mjs +105 -0
- package/esm/tests/factories.mjs.map +1 -0
- package/esm/tests/fakes/cache-fake.d.mts +29 -0
- package/esm/tests/fakes/cache-fake.d.mts.map +1 -0
- package/esm/tests/fakes/cache-fake.mjs +79 -0
- package/esm/tests/fakes/cache-fake.mjs.map +1 -0
- package/esm/tests/fakes/clock-fake.d.mts +32 -0
- package/esm/tests/fakes/clock-fake.d.mts.map +1 -0
- package/esm/tests/fakes/clock-fake.mjs +69 -0
- package/esm/tests/fakes/clock-fake.mjs.map +1 -0
- package/esm/tests/fakes/fake-assertion-error.d.mts +13 -0
- package/esm/tests/fakes/fake-assertion-error.d.mts.map +1 -0
- package/esm/tests/fakes/fake-assertion-error.mjs +20 -0
- package/esm/tests/fakes/fake-assertion-error.mjs.map +1 -0
- package/esm/tests/fakes/ids-fake.d.mts +22 -0
- package/esm/tests/fakes/ids-fake.d.mts.map +1 -0
- package/esm/tests/fakes/ids-fake.mjs +57 -0
- package/esm/tests/fakes/ids-fake.mjs.map +1 -0
- package/esm/tests/fakes/index.d.mts +8 -0
- package/esm/tests/fakes/index.mjs +10 -0
- package/esm/tests/fakes/mail-fake.d.mts +41 -0
- package/esm/tests/fakes/mail-fake.d.mts.map +1 -0
- package/esm/tests/fakes/mail-fake.mjs +111 -0
- package/esm/tests/fakes/mail-fake.mjs.map +1 -0
- package/esm/tests/fakes/queue-fake.d.mts +48 -0
- package/esm/tests/fakes/queue-fake.d.mts.map +1 -0
- package/esm/tests/fakes/queue-fake.mjs +134 -0
- package/esm/tests/fakes/queue-fake.mjs.map +1 -0
- package/esm/tests/fakes/storage-fake.d.mts +31 -0
- package/esm/tests/fakes/storage-fake.d.mts.map +1 -0
- package/esm/tests/fakes/storage-fake.mjs +122 -0
- package/esm/tests/fakes/storage-fake.mjs.map +1 -0
- package/esm/tests/fakes/test-fakes.d.mts +39 -0
- package/esm/tests/fakes/test-fakes.d.mts.map +1 -0
- package/esm/tests/fakes/test-fakes.mjs +90 -0
- package/esm/tests/fakes/test-fakes.mjs.map +1 -0
- package/esm/tests/index.d.mts +14 -2
- package/esm/tests/index.mjs +15 -2
- package/esm/tests/snapshot-response.d.mts +34 -0
- package/esm/tests/snapshot-response.d.mts.map +1 -0
- package/esm/tests/snapshot-response.mjs +42 -0
- package/esm/tests/snapshot-response.mjs.map +1 -0
- package/esm/tests/start-http-development-server.d.mts +22 -0
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs +36 -3
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts +20 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-helpers.mjs +40 -27
- package/esm/tests/test-helpers.mjs.map +1 -1
- package/esm/tests/test-http-server.d.mts +62 -0
- package/esm/tests/test-http-server.d.mts.map +1 -0
- package/esm/tests/test-http-server.mjs +119 -0
- package/esm/tests/test-http-server.mjs.map +1 -0
- package/esm/tests/vitest-setup.d.mts +11 -0
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs +12 -4
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/tooling/agents-md/agents-md-markers.d.mts +27 -0
- package/esm/tooling/agents-md/agents-md-markers.d.mts.map +1 -0
- package/esm/tooling/agents-md/agents-md-markers.mjs +59 -0
- package/esm/tooling/agents-md/agents-md-markers.mjs.map +1 -0
- package/esm/tooling/agents-md/agents-md-refresher.d.mts +36 -0
- package/esm/tooling/agents-md/agents-md-refresher.d.mts.map +1 -0
- package/esm/tooling/agents-md/agents-md-refresher.mjs +72 -0
- package/esm/tooling/agents-md/agents-md-refresher.mjs.map +1 -0
- package/esm/tooling/agents-md/index.mjs +7 -0
- package/esm/tooling/agents-md/installed-skills.d.mts +30 -0
- package/esm/tooling/agents-md/installed-skills.d.mts.map +1 -0
- package/esm/tooling/agents-md/installed-skills.mjs +74 -0
- package/esm/tooling/agents-md/installed-skills.mjs.map +1 -0
- package/esm/tooling/agents-md/render-agents-md.d.mts +33 -0
- package/esm/tooling/agents-md/render-agents-md.d.mts.map +1 -0
- package/esm/tooling/agents-md/render-agents-md.mjs +195 -0
- package/esm/tooling/agents-md/render-agents-md.mjs.map +1 -0
- package/esm/tooling/agents-md/write-agents-md.d.mts +33 -0
- package/esm/tooling/agents-md/write-agents-md.d.mts.map +1 -0
- package/esm/tooling/agents-md/write-agents-md.mjs +49 -0
- package/esm/tooling/agents-md/write-agents-md.mjs.map +1 -0
- package/esm/tooling/board/board-message.d.mts +31 -0
- package/esm/tooling/board/board-message.d.mts.map +1 -0
- package/esm/tooling/board/board-message.mjs +86 -0
- package/esm/tooling/board/board-message.mjs.map +1 -0
- package/esm/tooling/board/board.d.mts +109 -0
- package/esm/tooling/board/board.d.mts.map +1 -0
- package/esm/tooling/board/board.mjs +406 -0
- package/esm/tooling/board/board.mjs.map +1 -0
- package/esm/tooling/board/board.types.d.mts +72 -0
- package/esm/tooling/board/board.types.d.mts.map +1 -0
- package/esm/tooling/board/board.types.mjs +54 -0
- package/esm/tooling/board/board.types.mjs.map +1 -0
- package/esm/tooling/check/changed-files.d.mts +33 -0
- package/esm/tooling/check/changed-files.d.mts.map +1 -0
- package/esm/tooling/check/changed-files.mjs +73 -0
- package/esm/tooling/check/changed-files.mjs.map +1 -0
- package/esm/tooling/check/check-report.types.d.mts +62 -0
- package/esm/tooling/check/check-report.types.d.mts.map +1 -0
- package/esm/tooling/check/check-report.types.mjs +37 -0
- package/esm/tooling/check/check-report.types.mjs.map +1 -0
- package/esm/tooling/check/code-style-conventions.mjs +106 -0
- package/esm/tooling/check/code-style-conventions.mjs.map +1 -0
- package/esm/tooling/check/conventions-step.d.mts +34 -0
- package/esm/tooling/check/conventions-step.d.mts.map +1 -0
- package/esm/tooling/check/conventions-step.mjs +184 -0
- package/esm/tooling/check/conventions-step.mjs.map +1 -0
- package/esm/tooling/check/environment-step.d.mts +18 -0
- package/esm/tooling/check/environment-step.d.mts.map +1 -0
- package/esm/tooling/check/environment-step.mjs +78 -0
- package/esm/tooling/check/environment-step.mjs.map +1 -0
- package/esm/tooling/check/index.d.mts +8 -0
- package/esm/tooling/check/index.mjs +10 -0
- package/esm/tooling/check/lint-step.d.mts +19 -0
- package/esm/tooling/check/lint-step.d.mts.map +1 -0
- package/esm/tooling/check/lint-step.mjs +111 -0
- package/esm/tooling/check/lint-step.mjs.map +1 -0
- package/esm/tooling/check/request-input.mjs +132 -0
- package/esm/tooling/check/request-input.mjs.map +1 -0
- package/esm/tooling/check/run-check.d.mts +35 -0
- package/esm/tooling/check/run-check.d.mts.map +1 -0
- package/esm/tooling/check/run-check.mjs +192 -0
- package/esm/tooling/check/run-check.mjs.map +1 -0
- package/esm/tooling/check/run-tests.d.mts +48 -0
- package/esm/tooling/check/run-tests.d.mts.map +1 -0
- package/esm/tooling/check/run-tests.mjs +93 -0
- package/esm/tooling/check/run-tests.mjs.map +1 -0
- package/esm/tooling/check/security-conventions.mjs +648 -0
- package/esm/tooling/check/security-conventions.mjs.map +1 -0
- package/esm/tooling/check/source-ast.mjs +195 -0
- package/esm/tooling/check/source-ast.mjs.map +1 -0
- package/esm/tooling/check/source-files.mjs +27 -0
- package/esm/tooling/check/source-files.mjs.map +1 -0
- package/esm/tooling/check/tests-step.d.mts +34 -0
- package/esm/tooling/check/tests-step.d.mts.map +1 -0
- package/esm/tooling/check/tests-step.mjs +177 -0
- package/esm/tooling/check/tests-step.mjs.map +1 -0
- package/esm/tooling/dev-state/dev-server-state.d.mts +39 -0
- package/esm/tooling/dev-state/dev-server-state.d.mts.map +1 -0
- package/esm/tooling/dev-state/dev-server-state.mjs +67 -0
- package/esm/tooling/dev-state/dev-server-state.mjs.map +1 -0
- package/esm/tooling/devtools/devtools-client.d.mts +91 -0
- package/esm/tooling/devtools/devtools-client.d.mts.map +1 -0
- package/esm/tooling/devtools/devtools-client.mjs +155 -0
- package/esm/tooling/devtools/devtools-client.mjs.map +1 -0
- package/esm/tooling/generate/generation.d.mts +78 -0
- package/esm/tooling/generate/generation.d.mts.map +1 -0
- package/esm/tooling/generate/generation.mjs +123 -0
- package/esm/tooling/generate/generation.mjs.map +1 -0
- package/esm/tooling/generate/generator-spec.d.mts +57 -0
- package/esm/tooling/generate/generator-spec.d.mts.map +1 -0
- package/esm/tooling/generate/generator-spec.mjs +102 -0
- package/esm/tooling/generate/generator-spec.mjs.map +1 -0
- package/esm/tooling/generate/index.mjs +6 -0
- package/esm/tooling/generate/project-files.mjs +121 -0
- package/esm/tooling/generate/project-files.mjs.map +1 -0
- package/esm/tooling/generate/run-cli.d.mts +40 -0
- package/esm/tooling/generate/run-cli.d.mts.map +1 -0
- package/esm/tooling/generate/run-cli.mjs +93 -0
- package/esm/tooling/generate/run-cli.mjs.map +1 -0
- package/esm/tooling/generate/unified-diff.d.mts +37 -0
- package/esm/tooling/generate/unified-diff.d.mts.map +1 -0
- package/esm/tooling/generate/unified-diff.mjs +161 -0
- package/esm/tooling/generate/unified-diff.mjs.map +1 -0
- package/esm/tooling/index.d.mts +41 -0
- package/esm/tooling/index.mjs +45 -0
- package/esm/tooling/inspect/boot-for-inspect.d.mts +23 -0
- package/esm/tooling/inspect/boot-for-inspect.d.mts.map +1 -0
- package/esm/tooling/inspect/boot-for-inspect.mjs +63 -0
- package/esm/tooling/inspect/boot-for-inspect.mjs.map +1 -0
- package/esm/tooling/inspect/collect-config.mjs +77 -0
- package/esm/tooling/inspect/collect-config.mjs.map +1 -0
- package/esm/tooling/inspect/collect-models.d.mts +9 -0
- package/esm/tooling/inspect/collect-models.d.mts.map +1 -0
- package/esm/tooling/inspect/collect-models.mjs +154 -0
- package/esm/tooling/inspect/collect-models.mjs.map +1 -0
- package/esm/tooling/inspect/collect-packages.d.mts +10 -0
- package/esm/tooling/inspect/collect-packages.d.mts.map +1 -0
- package/esm/tooling/inspect/collect-packages.mjs +157 -0
- package/esm/tooling/inspect/collect-packages.mjs.map +1 -0
- package/esm/tooling/inspect/collect-pages.mjs +108 -0
- package/esm/tooling/inspect/collect-pages.mjs.map +1 -0
- package/esm/tooling/inspect/collect-routes.mjs +168 -0
- package/esm/tooling/inspect/collect-routes.mjs.map +1 -0
- package/esm/tooling/inspect/event-recorder.d.mts +37 -0
- package/esm/tooling/inspect/event-recorder.d.mts.map +1 -0
- package/esm/tooling/inspect/event-recorder.mjs +91 -0
- package/esm/tooling/inspect/event-recorder.mjs.map +1 -0
- package/esm/tooling/inspect/index.d.mts +7 -0
- package/esm/tooling/inspect/index.mjs +7 -0
- package/esm/tooling/inspect/inspect-app.d.mts +53 -0
- package/esm/tooling/inspect/inspect-app.d.mts.map +1 -0
- package/esm/tooling/inspect/inspect-app.mjs +92 -0
- package/esm/tooling/inspect/inspect-app.mjs.map +1 -0
- package/esm/tooling/inspect/inspect-report.schema.d.mts +15 -0
- package/esm/tooling/inspect/inspect-report.schema.d.mts.map +1 -0
- package/esm/tooling/inspect/inspect-report.schema.mjs +463 -0
- package/esm/tooling/inspect/inspect-report.schema.mjs.map +1 -0
- package/esm/tooling/inspect/inspect-report.types.d.mts +196 -0
- package/esm/tooling/inspect/inspect-report.types.d.mts.map +1 -0
- package/esm/tooling/inspect/inspect-report.types.mjs +33 -0
- package/esm/tooling/inspect/inspect-report.types.mjs.map +1 -0
- package/esm/tooling/mcp/create-mcp-context.d.mts +26 -0
- package/esm/tooling/mcp/create-mcp-context.d.mts.map +1 -0
- package/esm/tooling/mcp/create-mcp-context.mjs +75 -0
- package/esm/tooling/mcp/create-mcp-context.mjs.map +1 -0
- package/esm/tooling/mcp/db-schema.d.mts +49 -0
- package/esm/tooling/mcp/db-schema.d.mts.map +1 -0
- package/esm/tooling/mcp/db-schema.mjs +56 -0
- package/esm/tooling/mcp/db-schema.mjs.map +1 -0
- package/esm/tooling/mcp/docs-search.d.mts +72 -0
- package/esm/tooling/mcp/docs-search.d.mts.map +1 -0
- package/esm/tooling/mcp/docs-search.mjs +178 -0
- package/esm/tooling/mcp/docs-search.mjs.map +1 -0
- package/esm/tooling/mcp/index.mjs +9 -0
- package/esm/tooling/mcp/load-ai-tools.d.mts +62 -0
- package/esm/tooling/mcp/load-ai-tools.d.mts.map +1 -0
- package/esm/tooling/mcp/load-ai-tools.mjs +44 -0
- package/esm/tooling/mcp/load-ai-tools.mjs.map +1 -0
- package/esm/tooling/mcp/mcp-tools.d.mts +60 -0
- package/esm/tooling/mcp/mcp-tools.d.mts.map +1 -0
- package/esm/tooling/mcp/mcp-tools.mjs +513 -0
- package/esm/tooling/mcp/mcp-tools.mjs.map +1 -0
- package/esm/tooling/mcp/start-mcp-server.d.mts +56 -0
- package/esm/tooling/mcp/start-mcp-server.d.mts.map +1 -0
- package/esm/tooling/mcp/start-mcp-server.mjs +126 -0
- package/esm/tooling/mcp/start-mcp-server.mjs.map +1 -0
- package/esm/tooling/mcp/tool-input.d.mts +75 -0
- package/esm/tooling/mcp/tool-input.d.mts.map +1 -0
- package/esm/tooling/mcp/tool-input.mjs +72 -0
- package/esm/tooling/mcp/tool-input.mjs.map +1 -0
- package/esm/tooling/problem.d.mts +45 -0
- package/esm/tooling/problem.d.mts.map +1 -0
- package/esm/tooling/problem.mjs +28 -0
- package/esm/tooling/problem.mjs.map +1 -0
- package/esm/tooling/trace/trace-report.types.d.mts +71 -0
- package/esm/tooling/trace/trace-report.types.d.mts.map +1 -0
- package/esm/tooling/trace/trace-report.types.mjs +7 -0
- package/esm/tooling/trace/trace-report.types.mjs.map +1 -0
- package/esm/tooling/trace/trace-request.d.mts +83 -0
- package/esm/tooling/trace/trace-request.d.mts.map +1 -0
- package/esm/tooling/trace/trace-request.mjs +241 -0
- package/esm/tooling/trace/trace-request.mjs.map +1 -0
- package/esm/tooling/typecheck/index.d.mts +1 -0
- package/esm/tooling/typecheck/index.mjs +3 -0
- package/esm/tooling/typecheck/run-typecheck.d.mts +59 -0
- package/esm/tooling/typecheck/run-typecheck.d.mts.map +1 -0
- package/esm/tooling/typecheck/run-typecheck.mjs +153 -0
- package/esm/tooling/typecheck/run-typecheck.mjs.map +1 -0
- package/esm/upgrade/codemods/head-viewport.mjs +46 -0
- package/esm/upgrade/codemods/head-viewport.mjs.map +1 -0
- package/esm/upgrade/codemods/helpers.mjs +81 -0
- package/esm/upgrade/codemods/helpers.mjs.map +1 -0
- package/esm/upgrade/codemods/index.mjs +34 -0
- package/esm/upgrade/codemods/index.mjs.map +1 -0
- package/esm/upgrade/codemods/logger-sync-calls.mjs +57 -0
- package/esm/upgrade/codemods/logger-sync-calls.mjs.map +1 -0
- package/esm/upgrade/codemods/package-json.mjs +122 -0
- package/esm/upgrade/codemods/package-json.mjs.map +1 -0
- package/esm/upgrade/codemods/request-log-snapshot.mjs +82 -0
- package/esm/upgrade/codemods/request-log-snapshot.mjs.map +1 -0
- package/esm/upgrade/codemods/review-sites.mjs +262 -0
- package/esm/upgrade/codemods/review-sites.mjs.map +1 -0
- package/esm/upgrade/diff.mjs +119 -0
- package/esm/upgrade/diff.mjs.map +1 -0
- package/esm/upgrade/format-upgrade-report.mjs +32 -0
- package/esm/upgrade/format-upgrade-report.mjs.map +1 -0
- package/esm/upgrade/load-ts-morph.mjs +48 -0
- package/esm/upgrade/load-ts-morph.mjs.map +1 -0
- package/esm/upgrade/notes.mjs +16 -0
- package/esm/upgrade/notes.mjs.map +1 -0
- package/esm/upgrade/run-upgrade.mjs +246 -0
- package/esm/upgrade/run-upgrade.mjs.map +1 -0
- package/esm/upgrade/upgrade-command.action.mjs +37 -0
- package/esm/upgrade/upgrade-command.action.mjs.map +1 -0
- package/esm/upgrade/version.mjs +39 -0
- package/esm/upgrade/version.mjs.map +1 -0
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/clock.d.mts +33 -0
- package/esm/utils/clock.d.mts.map +1 -0
- package/esm/utils/clock.mjs +27 -0
- package/esm/utils/clock.mjs.map +1 -0
- package/esm/utils/framework-vesion.mjs +1 -1
- package/esm/utils/framework-vesion.mjs.map +1 -1
- package/esm/utils/ids.d.mts +32 -0
- package/esm/utils/ids.d.mts.map +1 -0
- package/esm/utils/ids.mjs +33 -0
- package/esm/utils/ids.mjs.map +1 -0
- package/esm/utils/index.d.mts +3 -0
- package/esm/utils/index.mjs +3 -0
- package/esm/utils/runtime.d.mts +59 -0
- package/esm/utils/runtime.d.mts.map +1 -0
- package/esm/utils/runtime.mjs +85 -0
- package/esm/utils/runtime.mjs.map +1 -0
- package/esm/validation/index.d.mts +1 -1
- package/esm/validation/types.d.mts +57 -2
- package/esm/validation/types.d.mts.map +1 -1
- package/esm/validation/validateAll.mjs +19 -2
- package/esm/validation/validateAll.mjs.map +1 -1
- package/esm/validation/validation-failure-status.mjs +15 -0
- package/esm/validation/validation-failure-status.mjs.map +1 -0
- package/esm/validation/validators/file-validator.d.mts +0 -16
- package/esm/validation/validators/file-validator.d.mts.map +1 -1
- package/esm/warlock-config/bundle-config.mjs +1 -1
- package/esm/warlock-config/types.d.mts +27 -3
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/llms-full.txt +3166 -736
- package/llms.txt +16 -6
- package/package.json +43 -13
- package/schemas/inspect-report.schema.json +554 -0
- package/skills/README.md +9 -0
- package/skills/add-connector/SKILL.md +26 -38
- package/skills/benchmark-code/SKILL.md +30 -24
- package/skills/build-restful/SKILL.md +14 -14
- package/skills/build-url/SKILL.md +18 -18
- package/skills/configure-app/SKILL.md +107 -14
- package/skills/create-controller/SKILL.md +25 -12
- package/skills/create-module/SKILL.md +3 -3
- package/skills/define-resource/SKILL.md +32 -29
- package/skills/encrypt-data/SKILL.md +21 -19
- package/skills/generate-openapi/SKILL.md +50 -24
- package/skills/hash-password/SKILL.md +2 -2
- package/skills/health-checks/SKILL.md +24 -7
- package/skills/index.md +9 -4
- package/skills/lower-stage3-decorators/SKILL.md +4 -4
- package/skills/make-endpoint/SKILL.md +110 -0
- package/skills/model-factories/SKILL.md +92 -0
- package/skills/process-image/SKILL.md +32 -30
- package/skills/register-route/SKILL.md +109 -10
- package/skills/request-memo/SKILL.md +1 -1
- package/skills/request-tracing/SKILL.md +4 -1
- package/skills/resolve-path/SKILL.md +45 -44
- package/skills/retry-operation/SKILL.md +20 -11
- package/skills/run-app/SKILL.md +106 -22
- package/skills/send-response/SKILL.md +179 -14
- package/skills/store-file/SKILL.md +93 -53
- package/skills/test-fakes/SKILL.md +123 -0
- package/skills/test-http/SKILL.md +125 -21
- package/skills/test-service/SKILL.md +41 -1
- package/skills/update-packages/SKILL.md +29 -18
- package/skills/upgrade-from-5-29/SKILL.md +413 -0
- package/skills/upload-file/SKILL.md +31 -33
- package/skills/use-app-context/SKILL.md +38 -31
- package/skills/use-middleware/SKILL.md +45 -25
- package/skills/use-model-transformers/SKILL.md +37 -28
- package/skills/use-repository/SKILL.md +2 -2
- package/skills/validate-input/SKILL.md +72 -28
- package/skills/warlock-agents-md/SKILL.md +96 -0
- package/skills/warlock-board/SKILL.md +123 -0
- package/skills/warlock-check/SKILL.md +132 -0
- package/skills/warlock-conventions/SKILL.md +115 -0
- package/skills/warlock-doctor/SKILL.md +55 -22
- package/skills/warlock-inspect/SKILL.md +134 -0
- package/skills/warlock-mcp/SKILL.md +120 -0
- package/skills/warlock-routes/SKILL.md +18 -11
- package/skills/warlock-trace/SKILL.md +81 -0
- package/skills/wire-socket/SKILL.md +8 -8
- package/skills/write-seeder/SKILL.md +46 -32
- package/skills/write-use-case/SKILL.md +40 -27
- package/esm/http/uploads/matches-if-none-match.mjs +0 -13
- package/esm/http/uploads/matches-if-none-match.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-conventions.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/security-conventions.ts"],"sourcesContent":["import { DEFAULT_REDACT_KEY_SET, normalizeRedactKey } from \"@warlock.js/logger\";\nimport ts from \"typescript\";\nimport { isAuthGuardName, isMutatingHttpMethod } from \"../../router/route-guard-names\";\nimport {\n containsRequestInput,\n REQUEST_BODY_MEMBERS,\n REQUEST_NAMES,\n requestMemberAccess,\n} from \"./request-input\";\nimport {\n ancestors,\n calleeName,\n isFunctionLike,\n isMemberAccess,\n keyName,\n literalString,\n memberName,\n referencesOf,\n resolveInitializer,\n rootIdentifier,\n type SourceFinding,\n unwrap,\n walk,\n} from \"./source-ast\";\n\n// ── WL9005: request.all() into a query, a service or inApp() ────────────────\n\n/** Request methods that return every input the client sent, validated or not. */\nconst UNFILTERED_METHODS: ReadonlySet<string> = new Set([\n \"all\",\n \"allExceptParams\",\n \"heavy\",\n \"heavyExceptParams\",\n]);\n\n/**\n * Responses and loggers are not the data sinks `WL9005` guards: echoing input\n * back is not mass assignment, and logging it is `WL9008`'s concern.\n */\nconst IGNORED_RECEIVERS: ReadonlySet<string> = new Set([\n \"response\",\n \"reply\",\n \"res\",\n \"console\",\n \"log\",\n \"logger\",\n]);\n\nfunction displayCallee(callee: ts.Node): string {\n const target = unwrap(callee);\n\n if (ts.isIdentifier(target)) return `${target.text}()`;\n\n if (isMemberAccess(target)) {\n const name = memberName(target);\n const object = unwrap(target.expression);\n const owner = ts.isIdentifier(object) ? `${object.text}.` : \"\";\n\n return name ? `${owner}${name}()` : \"a call\";\n }\n\n return \"a call\";\n}\n\nfunction isIgnoredReceiver(callee: ts.Node): boolean {\n const target = unwrap(callee);\n\n if (!isMemberAccess(target)) return false;\n\n const object = unwrap(target.expression);\n\n return ts.isIdentifier(object) && IGNORED_RECEIVERS.has(object.text);\n}\n\n/** When `node` flows into a call's arguments, the callee's display name. */\nfunction callReceiving(node: ts.Node): string | undefined {\n let current = node;\n\n for (;;) {\n const parent = current.parent;\n\n if (!parent) return undefined;\n\n if (ts.isCallExpression(parent) || ts.isNewExpression(parent)) {\n if (!(parent.arguments ?? []).some((argument) => argument === current)) return undefined;\n if (isIgnoredReceiver(parent.expression)) return undefined;\n\n return displayCallee(parent.expression);\n }\n\n if (\n ts.isSpreadElement(parent) ||\n ts.isSpreadAssignment(parent) ||\n ts.isObjectLiteralExpression(parent) ||\n ts.isArrayLiteralExpression(parent) ||\n (ts.isPropertyAssignment(parent) && parent.initializer === current) ||\n ts.isShorthandPropertyAssignment(parent) ||\n ts.isAsExpression(parent) ||\n ts.isNonNullExpression(parent) ||\n ts.isSatisfiesExpression(parent) ||\n ts.isTypeAssertionExpression(parent) ||\n ts.isParenthesizedExpression(parent) ||\n ts.isAwaitExpression(parent)\n ) {\n current = parent;\n continue;\n }\n\n return undefined;\n }\n}\n\n/**\n * The call that ends up receiving this value: directly as an argument, spread\n * into an object/array argument, or through a local constant later passed to\n * a call.\n */\nfunction receivingCall(node: ts.Node): string | undefined {\n const direct = callReceiving(node);\n\n if (direct) return direct;\n\n // `const data = request.all(); await Model.create(data);`\n const parent = node.parent;\n\n if (\n parent &&\n ts.isVariableDeclaration(parent) &&\n parent.initializer === node &&\n ts.isIdentifier(parent.name)\n ) {\n for (const reference of referencesOf(parent)) {\n const name = callReceiving(reference);\n if (name) return name;\n }\n }\n\n return undefined;\n}\n\nfunction checkRequestAll(node: ts.Node, findings: SourceFinding[]): void {\n if (!ts.isCallExpression(node)) return;\n\n const callee = unwrap(node.expression);\n\n if (!isMemberAccess(callee)) return;\n\n const object = unwrap(callee.expression);\n\n if (!ts.isIdentifier(object) || !REQUEST_NAMES.has(object.text)) return;\n\n const method = memberName(callee);\n\n if (!method || !UNFILTERED_METHODS.has(method)) return;\n\n const target = receivingCall(node);\n\n if (!target) return;\n\n findings.push({\n code: \"WL9005\",\n node,\n message: `\\`${object.text}.${method}()\\` passes every client-sent field to \\`${target}\\`. Use \\`${object.text}.validated()\\` so only the fields the validation schema accepted reach it.`,\n });\n}\n\n// ── WL9006: a mutating route with no auth guard and no public: true ─────────\n\nconst ROUTER_NAMES: ReadonlySet<string> = new Set([\"router\"]);\n\n/** Wrappers whose callback routes are guarded, e.g. `guarded(() => …)`. */\nconst GUARD_FUNCTIONS: ReadonlySet<string> = new Set([\"guarded\"]);\n\n/** `router.post(…)` and friends. */\nconst MUTATING_ROUTER_METHODS: ReadonlySet<string> = new Set([\"post\", \"put\", \"patch\", \"delete\"]);\n\n/** Route builder methods (`router.route(path).create(…)`) that register a mutating route. */\nconst MUTATING_BUILDER_METHODS: ReadonlySet<string> = new Set([\n \"post\",\n \"postOne\",\n \"put\",\n \"updateOne\",\n \"patch\",\n \"patchOne\",\n \"delete\",\n \"deleteOne\",\n \"create\",\n \"update\",\n \"destroy\",\n]);\n\nconst READ_ONLY_RESOURCE_METHODS: ReadonlySet<string> = new Set([\"list\", \"get\", \"show\"]);\n\nfunction isRouter(node: ts.Node): boolean {\n const target = unwrap(node);\n\n return ts.isIdentifier(target) && ROUTER_NAMES.has(target.text);\n}\n\nfunction isGuardName(name: string | undefined): boolean {\n return name !== undefined && isAuthGuardName(name);\n}\n\n/** Whether a constant is initialised with `markAuthGuard(fn)` (not `markAuthGuard(fn, false)`). */\nfunction isMarkedGuard(identifier: ts.Identifier): boolean {\n const initializer = resolveInitializer(identifier);\n const value = initializer ? unwrap(initializer) : undefined;\n\n return (\n value !== undefined &&\n ts.isCallExpression(value) &&\n calleeName(value) === \"markAuthGuard\" &&\n value.arguments[1]?.kind !== ts.SyntaxKind.FalseKeyword\n );\n}\n\nfunction middlewareHasGuard(node: ts.Node): boolean {\n const value = unwrap(node);\n\n // A variable or a call: the check cannot see into it, so it trusts it.\n if (!ts.isArrayLiteralExpression(value)) return true;\n\n return value.elements.some((element) => {\n if (ts.isSpreadElement(element)) return true;\n\n const item = unwrap(element);\n\n if (ts.isIdentifier(item)) return isGuardName(item.text) || isMarkedGuard(item);\n if (ts.isCallExpression(item)) return isGuardName(calleeName(item));\n if (isMemberAccess(item)) return isGuardName(memberName(item));\n\n return false;\n });\n}\n\n/** `public: true`, a guard in `middleware`, or an options value the check cannot see into. */\nfunction optionsGuard(node: ts.Node | undefined): \"guarded\" | \"unguarded\" | \"none\" {\n if (!node) return \"none\";\n\n const value = unwrap(node);\n\n if (!ts.isObjectLiteralExpression(value)) return \"guarded\";\n\n let verdict: \"guarded\" | \"unguarded\" = \"unguarded\";\n\n for (const property of value.properties) {\n if (ts.isSpreadAssignment(property)) {\n verdict = \"guarded\";\n continue;\n }\n\n let key: string | undefined;\n let propertyValue: ts.Node | undefined;\n\n if (ts.isPropertyAssignment(property)) {\n key = keyName(property.name);\n propertyValue = unwrap(property.initializer);\n } else if (ts.isShorthandPropertyAssignment(property)) {\n key = property.name.text;\n propertyValue = property.name;\n }\n\n if (!propertyValue) continue;\n if (key === \"public\" && propertyValue.kind === ts.SyntaxKind.TrueKeyword) return \"guarded\";\n if (key === \"middleware\" && middlewareHasGuard(propertyValue)) return \"guarded\";\n }\n\n return verdict;\n}\n\n/** Whether an enclosing function is a guarded (or public) group callback. */\nfunction insideGuardedGroup(node: ts.Node): boolean {\n for (const ancestor of ancestors(node)) {\n if (!ts.isArrowFunction(ancestor) && !ts.isFunctionExpression(ancestor)) continue;\n\n const parent = ancestor.parent;\n\n if (!parent || !ts.isCallExpression(parent)) continue;\n if (!parent.arguments.some((argument) => argument === ancestor)) continue;\n\n const name = calleeName(parent);\n\n if (name !== undefined && GUARD_FUNCTIONS.has(name)) return true;\n\n const callee = unwrap(parent.expression);\n\n if (\n isMemberAccess(callee) &&\n memberName(callee) === \"group\" &&\n isRouter(callee.expression) &&\n optionsGuard(parent.arguments[0]) === \"guarded\"\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction pathText(node: ts.Node | undefined): string {\n return literalString(node) ?? \"(dynamic path)\";\n}\n\n/**\n * Walk a builder chain back to `router.route(path, options)`, collecting every\n * options object passed along the way (`route()`, `nest()`).\n */\nfunction builderChain(node: ts.Node): { path: string; optionNodes: ts.Node[] } | undefined {\n const optionNodes: ts.Node[] = [];\n let current = unwrap(node);\n\n while (ts.isCallExpression(current)) {\n const callee = unwrap(current.expression);\n\n if (!isMemberAccess(callee)) return undefined;\n\n const name = memberName(callee);\n\n if (name === \"route\" && isRouter(callee.expression)) {\n const options = current.arguments[1];\n if (options) optionNodes.push(options);\n\n return { path: pathText(current.arguments[0]), optionNodes };\n }\n\n if (name === \"nest\") {\n const options = current.arguments[1];\n if (options) optionNodes.push(options);\n }\n\n current = unwrap(callee.expression);\n }\n\n return undefined;\n}\n\nfunction resourceIsReadOnly(node: ts.Node | undefined): boolean {\n if (!node) return false;\n\n const value = unwrap(node);\n\n if (!ts.isObjectLiteralExpression(value)) return false;\n\n for (const property of value.properties) {\n if (!ts.isPropertyAssignment(property) || keyName(property.name) !== \"only\") continue;\n\n const only = unwrap(property.initializer);\n\n if (!ts.isArrayLiteralExpression(only)) return false;\n\n return only.elements.every((element) => {\n const method = literalString(element);\n return method !== undefined && READ_ONLY_RESOURCE_METHODS.has(method);\n });\n }\n\n return false;\n}\n\nfunction unguardedRoute(node: ts.Node, route: string): SourceFinding {\n return {\n code: \"WL9006\",\n node,\n message: `Mutating route \\`${route}\\` has no auth middleware. Add an auth guard (\\`middleware: [authMiddleware()]\\`, or register it inside a guarded group) or mark it intentionally open with \\`public: true\\`.`,\n };\n}\n\nfunction checkRouteGuard(node: ts.Node, findings: SourceFinding[]): void {\n if (!ts.isCallExpression(node)) return;\n\n const callee = unwrap(node.expression);\n\n if (!isMemberAccess(callee)) return;\n\n const method = memberName(callee);\n\n if (!method) return;\n\n const guarded = (options: ts.Node | undefined) =>\n optionsGuard(options) === \"guarded\" || insideGuardedGroup(node);\n\n if (isRouter(callee.expression)) {\n // router.post(path, handler, options)\n if (MUTATING_ROUTER_METHODS.has(method)) {\n if (!guarded(node.arguments[2])) {\n findings.push(\n unguardedRoute(node, `${method.toUpperCase()} ${pathText(node.arguments[0])}`),\n );\n }\n\n return;\n }\n\n // router.add(\"POST\", path, handler, options)\n if (method === \"add\") {\n const httpMethod = literalString(node.arguments[0])?.toUpperCase();\n\n if (!httpMethod || !isMutatingHttpMethod(httpMethod)) return;\n\n if (!guarded(node.arguments[3])) {\n findings.push(unguardedRoute(node, `${httpMethod} ${pathText(node.arguments[1])}`));\n }\n\n return;\n }\n\n // router.restfulResource(path, resource, options)\n if (method === \"restfulResource\") {\n if (resourceIsReadOnly(node.arguments[2])) return;\n\n if (!guarded(node.arguments[2])) {\n findings.push(unguardedRoute(node, `resource ${pathText(node.arguments[0])}`));\n }\n }\n\n return;\n }\n\n // router.route(path, options).create(handler, options)\n if (!MUTATING_BUILDER_METHODS.has(method)) return;\n\n const chain = builderChain(callee.expression);\n\n if (!chain) return;\n if (optionsGuard(node.arguments[1]) === \"guarded\") return;\n if (chain.optionNodes.some((options) => optionsGuard(options) === \"guarded\")) return;\n if (insideGuardedGroup(node)) return;\n\n findings.push(unguardedRoute(node, `${method} ${chain.path}`));\n}\n\n// ── WL9007: a redirect or link target from request input ────────────────────\n\n/** Calls that turn their first argument into a redirect or a navigation. */\nconst REDIRECT_CALLS: ReadonlySet<string> = new Set([\n \"redirect\",\n \"permanentRedirect\",\n \"navigateTo\",\n]);\n\n/** Components whose `to`/`href` prop navigates. */\nconst LINK_COMPONENTS: ReadonlySet<string> = new Set([\"Link\", \"NavLink\"]);\n\n/** Helpers that return an allowed target (or a fallback) for any input. */\nconst REDIRECT_SANITIZERS: ReadonlySet<string> = new Set([\"safeRedirect\", \"safeRedirectTarget\"]);\n\n/** Predicates that, used in an enclosing `if` or `? :`, prove the target was checked. */\nconst REDIRECT_VALIDATORS: ReadonlySet<string> = new Set([\"isSafeRedirect\", \"isSafeRedirectUrl\"]);\n\n/** Client-side input: query-string hooks. */\nconst CLIENT_INPUT_CALLS: ReadonlySet<string> = new Set([\n \"useQueryString\",\n \"useSearchParams\",\n \"useParams\",\n]);\n\n/** Identifiers whose members are client-controlled (a loader's `query`, URLSearchParams). */\nconst CLIENT_INPUT_NAMES: ReadonlySet<string> = new Set([\"query\", \"searchParams\"]);\n\nfunction isClientSource(node: ts.Node): boolean {\n if (ts.isCallExpression(node)) {\n const name = calleeName(node);\n if (name !== undefined && CLIENT_INPUT_CALLS.has(name)) return true;\n }\n\n if (isMemberAccess(node)) {\n const root = rootIdentifier(node);\n\n if (root && CLIENT_INPUT_NAMES.has(root.text)) return true;\n\n // location.search / window.location.hash\n const name = memberName(node);\n\n if (name === \"search\" || name === \"hash\") {\n const object = unwrap(node.expression);\n\n if (ts.isIdentifier(object) && object.text === \"location\") return true;\n if (isMemberAccess(object) && memberName(object) === \"location\") return true;\n }\n }\n\n return false;\n}\n\nfunction isRedirectTainted(node: ts.Node): boolean {\n return containsRequestInput(node, { sanitizers: REDIRECT_SANITIZERS, isSource: isClientSource });\n}\n\n/** An enclosing `if (isSafeRedirect(x))` or `isSafeRedirect(x) ? … : …` validated the target. */\nfunction checkedByValidator(node: ts.Node): boolean {\n for (const ancestor of ancestors(node)) {\n let test: ts.Node | undefined;\n\n if (ts.isIfStatement(ancestor)) test = ancestor.expression;\n else if (ts.isConditionalExpression(ancestor)) test = ancestor.condition;\n else continue;\n\n let validated = false;\n\n walk(test, (child) => {\n if (ts.isCallExpression(child)) {\n const name = calleeName(child);\n if (name !== undefined && REDIRECT_VALIDATORS.has(name)) validated = true;\n }\n\n return undefined;\n });\n\n if (validated) return true;\n }\n\n return false;\n}\n\nfunction unsafeRedirect(node: ts.Node, what: string, helper: string, from: string): SourceFinding {\n return {\n code: \"WL9007\",\n node,\n message: `This ${what} target comes from request input. Pass it through \\`${helper}()\\` from \\`${from}\\` so only same-origin paths and allow-listed origins are followed (open redirects and \\`javascript:\\` URLs otherwise).`,\n };\n}\n\nfunction checkRedirect(node: ts.Node, findings: SourceFinding[]): void {\n if (ts.isCallExpression(node)) {\n const name = calleeName(node);\n\n if (name === undefined || !REDIRECT_CALLS.has(name)) return;\n\n // `router.redirect(from, to)` registers a static redirect, not a request-time one.\n const callee = unwrap(node.expression);\n\n if (isMemberAccess(callee)) {\n const owner = unwrap(callee.expression);\n if (ts.isIdentifier(owner) && owner.text === \"router\") return;\n }\n\n const target = node.arguments[0];\n\n if (!target || ts.isSpreadElement(target)) return;\n if (!isRedirectTainted(target) || checkedByValidator(node)) return;\n\n findings.push(\n name === \"navigateTo\"\n ? unsafeRedirect(target, \"navigation\", \"safeRedirectTarget\", \"@warlock.js/web\")\n : unsafeRedirect(target, \"redirect\", \"safeRedirect\", \"@warlock.js/core\"),\n );\n\n return;\n }\n\n if (!ts.isJsxAttribute(node) || !ts.isIdentifier(node.name)) return;\n if (node.name.text !== \"to\" && node.name.text !== \"href\") return;\n\n const element = node.parent.parent;\n\n if (!ts.isJsxOpeningElement(element) && !ts.isJsxSelfClosingElement(element)) return;\n if (!ts.isIdentifier(element.tagName) || !LINK_COMPONENTS.has(element.tagName.text)) return;\n\n const initializer = node.initializer;\n\n if (!initializer || !ts.isJsxExpression(initializer) || !initializer.expression) return;\n\n const expression = initializer.expression;\n\n if (!isRedirectTainted(expression) || checkedByValidator(expression)) return;\n\n findings.push(unsafeRedirect(expression, \"link\", \"safeRedirectTarget\", \"@warlock.js/web\"));\n}\n\n// ── WL9008: raw emails, credentials and request bodies in logs ──────────────\n\nconst LOGGER_NAMES: ReadonlySet<string> = new Set([\"log\", \"logger\", \"console\"]);\n\nconst LOG_METHODS: ReadonlySet<string> = new Set([\n \"info\",\n \"warn\",\n \"error\",\n \"debug\",\n \"success\",\n \"fatal\",\n \"log\",\n \"assert\",\n \"trace\",\n]);\n\n/** Helpers that make a value safe to log; their arguments are not inspected. */\nconst REDACTORS: ReadonlySet<string> = new Set([\n \"maskEmail\",\n \"maskEmailsInText\",\n \"redact\",\n \"redactHeaders\",\n \"redactError\",\n \"redactUrlCredentials\",\n \"redactQueryCredentials\",\n \"escapeLogValue\",\n \"toLogSnapshot\",\n]);\n\n/** Name endings that mark a value as a credential. */\nconst SECRET_SUFFIXES = [\n \"token\",\n \"password\",\n \"passwd\",\n \"secret\",\n \"apikey\",\n \"authorization\",\n \"cookie\",\n \"cookies\",\n \"otp\",\n \"jwt\",\n \"privatekey\",\n];\n\n/**\n * Whether the logger censors this key by default (`DEFAULT_REDACT_KEYS` from\n * `@warlock.js/logger`). A secret logged under one of them never reaches a\n * channel. Emails are not on that list: they are not censored by default.\n */\nfunction isCensoredKey(key: string): boolean {\n return DEFAULT_REDACT_KEY_SET.has(normalizeRedactKey(key));\n}\n\n/** `email` for addresses, `secret` for credentials, otherwise `undefined`. */\nfunction sensitiveCategory(name: string): \"email\" | \"secret\" | undefined {\n const normalized = normalizeRedactKey(name);\n\n if (normalized.endsWith(\"email\") || normalized.endsWith(\"emailaddress\")) return \"email\";\n if (DEFAULT_REDACT_KEY_SET.has(normalized)) return \"secret\";\n if (SECRET_SUFFIXES.some((suffix) => normalized.endsWith(suffix))) return \"secret\";\n\n return undefined;\n}\n\nfunction describeCategory(category: \"email\" | \"secret\", name: string | undefined): string {\n if (category === \"email\") return \"a raw email address\";\n\n return name ? `the credential \\`${name}\\`` : \"a credential\";\n}\n\nfunction describeMember(member: string): string {\n if (member === \"headers\" || member === \"cookies\") return member;\n\n return `body (\\`${member}\\`)`;\n}\n\n/** `request.all()` / `request.body` itself, not `request.body.title`. */\nfunction isWholeAccess(node: ts.Node, member: string): boolean {\n if (ts.isCallExpression(node)) {\n const callee = unwrap(node.expression);\n return isMemberAccess(callee) && memberName(callee) === member;\n }\n\n return isMemberAccess(node) && memberName(node) === member;\n}\n\ntype LogFinding = { node: ts.Node; what: string };\n\nfunction inspectLogged(node: ts.Node, censoredKey: boolean, found: LogFinding[]): void {\n const value = unwrap(node);\n\n if (isFunctionLike(value)) return;\n\n if (ts.isCallExpression(value)) {\n const name = calleeName(value);\n if (name !== undefined && REDACTORS.has(name)) return;\n }\n\n // request.all() / request.body / request.headers / request.input(\"email\")\n if (ts.isCallExpression(value) || isMemberAccess(value)) {\n const member = requestMemberAccess(value);\n\n if (member !== undefined && isWholeAccess(value, member)) {\n if (REQUEST_BODY_MEMBERS.has(member)) {\n found.push({ node: value, what: `the request ${describeMember(member)}` });\n return;\n }\n\n if (member === \"email\") {\n found.push({ node: value, what: \"a raw email address\" });\n return;\n }\n }\n\n if (ts.isCallExpression(value) && member !== undefined) {\n const key = literalString(value.arguments[0]);\n const category = key ? sensitiveCategory(key) : undefined;\n\n if (category && !(censoredKey && category === \"secret\")) {\n found.push({ node: value, what: describeCategory(category, key) });\n return;\n }\n }\n }\n\n if (ts.isIdentifier(value) || isMemberAccess(value)) {\n const name = ts.isIdentifier(value) ? value.text : memberName(value);\n const category = name ? sensitiveCategory(name) : undefined;\n\n if (category && !(censoredKey && category === \"secret\")) {\n found.push({ node: value, what: describeCategory(category, name) });\n return;\n }\n\n // `request.body.title` reads one field; only the whole body is reported.\n if (isMemberAccess(value) && requestMemberAccess(value) === undefined) {\n inspectLogged(value.expression, false, found);\n\n if (ts.isElementAccessExpression(value))\n inspectLogged(value.argumentExpression, false, found);\n }\n\n return;\n }\n\n if (ts.isObjectLiteralExpression(value)) {\n for (const property of value.properties) {\n if (ts.isSpreadAssignment(property)) {\n inspectLogged(property.expression, false, found);\n } else if (ts.isPropertyAssignment(property)) {\n const key = keyName(property.name);\n inspectLogged(property.initializer, key !== undefined && isCensoredKey(key), found);\n } else if (ts.isShorthandPropertyAssignment(property)) {\n inspectLogged(property.name, isCensoredKey(property.name.text), found);\n }\n }\n\n return;\n }\n\n if (ts.isTemplateExpression(value)) {\n for (const span of value.templateSpans) inspectLogged(span.expression, false, found);\n return;\n }\n\n if (ts.isBinaryExpression(value)) {\n inspectLogged(value.left, false, found);\n inspectLogged(value.right, false, found);\n return;\n }\n\n if (ts.isConditionalExpression(value)) {\n inspectLogged(value.whenTrue, censoredKey, found);\n inspectLogged(value.whenFalse, censoredKey, found);\n return;\n }\n\n if (ts.isArrayLiteralExpression(value)) {\n for (const element of value.elements) {\n if (ts.isOmittedExpression(element)) continue;\n inspectLogged(ts.isSpreadElement(element) ? element.expression : element, false, found);\n }\n\n return;\n }\n\n if (ts.isCallExpression(value)) {\n // JSON.stringify(request.all()), String(user.email), user.email.trim()\n const callee = unwrap(value.expression);\n\n if (isMemberAccess(callee)) inspectLogged(callee.expression, false, found);\n\n for (const argument of value.arguments) {\n inspectLogged(ts.isSpreadElement(argument) ? argument.expression : argument, false, found);\n }\n\n return;\n }\n\n if (ts.isAwaitExpression(value)) inspectLogged(value.expression, censoredKey, found);\n}\n\nfunction checkSensitiveLog(node: ts.Node, findings: SourceFinding[]): void {\n if (!ts.isCallExpression(node)) return;\n\n const callee = unwrap(node.expression);\n\n if (!isMemberAccess(callee)) return;\n\n const method = memberName(callee);\n\n if (!method || !LOG_METHODS.has(method)) return;\n\n const owner = unwrap(callee.expression);\n\n if (!ts.isIdentifier(owner)) return;\n\n // log.info(…), console.log(…), and request.log(…)\n const isLogger =\n LOGGER_NAMES.has(owner.text) || (REQUEST_NAMES.has(owner.text) && method === \"log\");\n\n if (!isLogger) return;\n\n const found: LogFinding[] = [];\n\n for (const argument of node.arguments) {\n inspectLogged(ts.isSpreadElement(argument) ? argument.expression : argument, false, found);\n }\n\n for (const finding of found) {\n findings.push({\n code: \"WL9008\",\n node: finding.node,\n message: `Do not log ${finding.what} as-is. Pass it through a redaction helper (\\`maskEmail()\\`, \\`redact()\\`, \\`request.toLogSnapshot()\\`), or log it under a key the logger censors.`,\n });\n }\n}\n\n// ── WL9009: unbounded regex quantifiers on request input ────────────────────\n\n/** `regex.test(input)` / `regex.exec(input)`. */\nconst REGEX_METHODS: ReadonlySet<string> = new Set([\"test\", \"exec\"]);\n\n/** `input.match(regex)` and friends: the regex is the first argument. */\nconst STRING_METHODS: ReadonlySet<string> = new Set([\n \"match\",\n \"matchAll\",\n \"replace\",\n \"replaceAll\",\n \"search\",\n \"split\",\n]);\n\n/**\n * The first unbounded quantifier in a regex source (`*`, `+`, `{n,}`), skipping\n * escapes and character classes, or `undefined` when every repetition is\n * bounded.\n */\nexport function findUnboundedQuantifier(source: string): string | undefined {\n let inClass = false;\n\n for (let index = 0; index < source.length; index++) {\n const char = source[index];\n\n if (char === \"\\\\\") {\n index++;\n continue;\n }\n\n if (inClass) {\n if (char === \"]\") inClass = false;\n continue;\n }\n\n if (char === \"[\") {\n inClass = true;\n continue;\n }\n\n if (char === \"*\" || char === \"+\") {\n return `${char}${source[index + 1] === \"?\" ? \"?\" : \"\"}`;\n }\n\n if (char === \"{\") {\n const match = /^\\{(\\d+),\\}/.exec(source.slice(index));\n if (match) return match[0];\n }\n }\n\n return undefined;\n}\n\n/** The pattern of a regex literal, `new RegExp(\"…\")`, or a constant holding either. */\nfunction regexSource(node: ts.Node | undefined, depth = 0): string | undefined {\n if (!node || depth > 8) return undefined;\n\n const value = unwrap(node);\n\n if (ts.isRegularExpressionLiteral(value)) {\n return value.text.slice(1, value.text.lastIndexOf(\"/\"));\n }\n\n if (\n (ts.isNewExpression(value) || ts.isCallExpression(value)) &&\n ts.isIdentifier(value.expression) &&\n value.expression.text === \"RegExp\"\n ) {\n return literalString(value.arguments?.[0]);\n }\n\n if (ts.isIdentifier(value)) {\n const initializer = resolveInitializer(value);\n return initializer ? regexSource(initializer, depth + 1) : undefined;\n }\n\n return undefined;\n}\n\nfunction checkUnboundedRegex(node: ts.Node, findings: SourceFinding[]): void {\n if (!ts.isCallExpression(node)) return;\n\n const callee = unwrap(node.expression);\n\n if (!isMemberAccess(callee)) return;\n\n const method = memberName(callee);\n\n if (!method) return;\n\n const first = node.arguments[0];\n const firstArgument = first && !ts.isSpreadElement(first) ? first : undefined;\n let regex: ts.Node | undefined;\n let input: ts.Node | undefined;\n\n if (REGEX_METHODS.has(method)) {\n regex = callee.expression;\n input = firstArgument;\n } else if (STRING_METHODS.has(method)) {\n regex = firstArgument;\n input = callee.expression;\n } else {\n return;\n }\n\n if (!input) return;\n\n const source = regexSource(regex);\n\n if (source === undefined) return;\n\n const quantifier = findUnboundedQuantifier(source);\n\n if (!quantifier || !containsRequestInput(input)) return;\n\n findings.push({\n code: \"WL9009\",\n node,\n message: `This regex has an unbounded quantifier (\\`${quantifier}\\`) and runs on request input, so a crafted input can make it backtrack for a long time (ReDoS). Bound the repetition, e.g. \\`{1,256}\\`, or cap the input length before matching.`,\n });\n}\n\n/**\n * The security conventions in one parsed file:\n *\n * - `WL9005` `request.all()` (or `heavy()`, `allExceptParams()`) passed to a\n * query, a service or `inApp()`;\n * - `WL9006` a POST/PUT/PATCH/DELETE route with no auth middleware and no\n * `public: true`;\n * - `WL9007` a `redirect()` / `navigateTo()` / `<Link to|href>` target taken\n * from request input without `safeRedirect()` / `safeRedirectTarget()` or an\n * `isSafeRedirect()` check;\n * - `WL9008` a raw email, credential or request body passed to a logger;\n * - `WL9009` a regex with an unbounded quantifier run on request input.\n */\nexport function securityFindings(sourceFile: ts.SourceFile): SourceFinding[] {\n const findings: SourceFinding[] = [];\n\n walk(\n sourceFile,\n (node) => {\n checkRequestAll(node, findings);\n checkRouteGuard(node, findings);\n checkRedirect(node, findings);\n checkSensitiveLog(node, findings);\n checkUnboundedRegex(node, findings);\n\n return undefined;\n },\n true,\n );\n\n return findings;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
//#region ../../packages/core/src/tooling/check/source-ast.ts
|
|
4
|
+
/** Whether a node is `a.b` or `a[b]`. */
|
|
5
|
+
function isMemberAccess(node) {
|
|
6
|
+
return ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node);
|
|
7
|
+
}
|
|
8
|
+
/** Strip parentheses and type-only wrappers (`x as T`, `x!`, `<T>x`, `x satisfies T`). */
|
|
9
|
+
function unwrap(node) {
|
|
10
|
+
let current = node;
|
|
11
|
+
while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isNonNullExpression(current) || ts.isTypeAssertionExpression(current) || ts.isSatisfiesExpression(current)) current = current.expression;
|
|
12
|
+
return current;
|
|
13
|
+
}
|
|
14
|
+
/** The text of a string literal or a template without substitutions. */
|
|
15
|
+
function literalString(node) {
|
|
16
|
+
if (!node) return void 0;
|
|
17
|
+
const value = unwrap(node);
|
|
18
|
+
return ts.isStringLiteral(value) || ts.isNoSubstitutionTemplateLiteral(value) ? value.text : void 0;
|
|
19
|
+
}
|
|
20
|
+
/** The static name of a member (`a.b` → `b`, `a["b"]` → `b`). */
|
|
21
|
+
function memberName(node) {
|
|
22
|
+
if (ts.isPropertyAccessExpression(node)) return node.name.text;
|
|
23
|
+
return literalString(node.argumentExpression);
|
|
24
|
+
}
|
|
25
|
+
/** The static name of an object-literal or type-member key (`{ a: 1 }`, `{ "a": 1 }`). */
|
|
26
|
+
function keyName(name) {
|
|
27
|
+
if (!name) return void 0;
|
|
28
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isPrivateIdentifier(name)) return name.text;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The name a call is made through: `foo()` → `foo`, `a.b.foo()` → `foo`.
|
|
32
|
+
* Used to recognise helpers by name wherever they are imported from.
|
|
33
|
+
*/
|
|
34
|
+
function calleeName(call) {
|
|
35
|
+
const callee = unwrap(call.expression);
|
|
36
|
+
if (ts.isIdentifier(callee)) return callee.text;
|
|
37
|
+
if (isMemberAccess(callee)) return memberName(callee);
|
|
38
|
+
}
|
|
39
|
+
/** The root identifier of a member chain: `a.b.c()` → `a`. */
|
|
40
|
+
function rootIdentifier(node) {
|
|
41
|
+
let current = unwrap(node);
|
|
42
|
+
for (;;) {
|
|
43
|
+
if (ts.isIdentifier(current)) return current;
|
|
44
|
+
if (isMemberAccess(current) || ts.isCallExpression(current)) {
|
|
45
|
+
current = unwrap(current.expression);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Whether a node is a function body owner (declaration, expression, arrow, method, accessor). */
|
|
52
|
+
function isFunctionLike(node) {
|
|
53
|
+
return ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node) || ts.isConstructorDeclaration(node);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Walk a node depth-first, skipping type annotations. The visitor returns
|
|
57
|
+
* `false` to skip a node's children. Nested functions are not entered unless
|
|
58
|
+
* `enterFunctions`.
|
|
59
|
+
*/
|
|
60
|
+
function walk(node, visit, enterFunctions = false) {
|
|
61
|
+
if (visit(node) === false) return;
|
|
62
|
+
if (!enterFunctions && isFunctionLike(node)) return;
|
|
63
|
+
ts.forEachChild(node, (child) => {
|
|
64
|
+
if (!ts.isTypeNode(child)) walk(child, visit, enterFunctions);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Every ancestor of a node, nearest first. */
|
|
68
|
+
function ancestors(node) {
|
|
69
|
+
const list = [];
|
|
70
|
+
let current = node.parent;
|
|
71
|
+
while (current) {
|
|
72
|
+
list.push(current);
|
|
73
|
+
current = current.parent;
|
|
74
|
+
}
|
|
75
|
+
return list;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Whether an identifier is a value reference: not the `b` of `a.b`, an object
|
|
79
|
+
* key, or the name a declaration introduces.
|
|
80
|
+
*/
|
|
81
|
+
function isValueReference(identifier) {
|
|
82
|
+
const parent = identifier.parent;
|
|
83
|
+
if (!parent) return true;
|
|
84
|
+
if (ts.isPropertyAccessExpression(parent)) return parent.expression === identifier;
|
|
85
|
+
if (ts.isShorthandPropertyAssignment(parent)) return true;
|
|
86
|
+
if (ts.isPropertyAssignment(parent) || ts.isMethodDeclaration(parent) || ts.isPropertyDeclaration(parent) || ts.isGetAccessorDeclaration(parent) || ts.isSetAccessorDeclaration(parent) || ts.isPropertySignature(parent) || ts.isVariableDeclaration(parent) || ts.isParameter(parent) || ts.isFunctionDeclaration(parent) || ts.isFunctionExpression(parent) || ts.isClassDeclaration(parent) || ts.isClassExpression(parent) || ts.isEnumMember(parent)) return parent.name !== identifier;
|
|
87
|
+
if (ts.isBindingElement(parent)) return parent.name !== identifier && parent.propertyName !== identifier;
|
|
88
|
+
return !(ts.isJsxAttribute(parent) || ts.isImportSpecifier(parent) || ts.isExportSpecifier(parent) || ts.isImportClause(parent) || ts.isNamespaceImport(parent) || ts.isLabeledStatement(parent) || ts.isBreakOrContinueStatement(parent));
|
|
89
|
+
}
|
|
90
|
+
function declarationListDeclares(list, name) {
|
|
91
|
+
for (const declaration of list.declarations) if (ts.isIdentifier(declaration.name)) {
|
|
92
|
+
if (declaration.name.text === name) return declaration;
|
|
93
|
+
} else if (bindingDeclares(declaration.name, name)) return "other";
|
|
94
|
+
}
|
|
95
|
+
function bindingDeclares(binding, name) {
|
|
96
|
+
if (ts.isIdentifier(binding)) return binding.text === name;
|
|
97
|
+
return binding.elements.some((element) => !ts.isOmittedExpression(element) && bindingDeclares(element.name, name));
|
|
98
|
+
}
|
|
99
|
+
function statementsDeclare(statements, name) {
|
|
100
|
+
for (const statement of statements) if (ts.isVariableStatement(statement)) {
|
|
101
|
+
const found = declarationListDeclares(statement.declarationList, name);
|
|
102
|
+
if (found) return found;
|
|
103
|
+
} else if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement) || ts.isEnumDeclaration(statement)) && statement.name?.text === name) return "other";
|
|
104
|
+
else if (ts.isImportDeclaration(statement) && importDeclares(statement, name)) return "other";
|
|
105
|
+
}
|
|
106
|
+
function importDeclares(statement, name) {
|
|
107
|
+
const clause = statement.importClause;
|
|
108
|
+
if (!clause) return false;
|
|
109
|
+
if (clause.name?.text === name) return true;
|
|
110
|
+
const bindings = clause.namedBindings;
|
|
111
|
+
if (!bindings) return false;
|
|
112
|
+
if (ts.isNamespaceImport(bindings)) return bindings.name.text === name;
|
|
113
|
+
return bindings.elements.some((element) => element.name.text === name);
|
|
114
|
+
}
|
|
115
|
+
/** The declaration a scope node introduces for `name`, if it introduces one. */
|
|
116
|
+
function scopeDeclares(scope, name) {
|
|
117
|
+
if (ts.isSourceFile(scope) || ts.isBlock(scope) || ts.isModuleBlock(scope)) return statementsDeclare(scope.statements, name);
|
|
118
|
+
if (ts.isCaseBlock(scope)) {
|
|
119
|
+
for (const clause of scope.clauses) {
|
|
120
|
+
const found = statementsDeclare(clause.statements, name);
|
|
121
|
+
if (found) return found;
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (isFunctionLike(scope)) {
|
|
126
|
+
if (scope.parameters.some((parameter) => bindingDeclares(parameter.name, name))) return "other";
|
|
127
|
+
return ts.isFunctionExpression(scope) && scope.name?.text === name ? "other" : void 0;
|
|
128
|
+
}
|
|
129
|
+
if ((ts.isForStatement(scope) || ts.isForInStatement(scope) || ts.isForOfStatement(scope)) && scope.initializer && ts.isVariableDeclarationList(scope.initializer)) return declarationListDeclares(scope.initializer, name) ? "other" : void 0;
|
|
130
|
+
if (ts.isCatchClause(scope) && scope.variableDeclaration) return bindingDeclares(scope.variableDeclaration.name, name) ? "other" : void 0;
|
|
131
|
+
}
|
|
132
|
+
/** The nearest declaration of an identifier's name, searching outward from it. */
|
|
133
|
+
function findDeclaration(identifier) {
|
|
134
|
+
for (const scope of ancestors(identifier)) {
|
|
135
|
+
const found = scopeDeclares(scope, identifier.text);
|
|
136
|
+
if (found) return found;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const ASSIGNMENT_OPERATORS = new Set([
|
|
140
|
+
ts.SyntaxKind.EqualsToken,
|
|
141
|
+
ts.SyntaxKind.PlusEqualsToken,
|
|
142
|
+
ts.SyntaxKind.MinusEqualsToken,
|
|
143
|
+
ts.SyntaxKind.AsteriskEqualsToken,
|
|
144
|
+
ts.SyntaxKind.SlashEqualsToken,
|
|
145
|
+
ts.SyntaxKind.BarBarEqualsToken,
|
|
146
|
+
ts.SyntaxKind.AmpersandAmpersandEqualsToken,
|
|
147
|
+
ts.SyntaxKind.QuestionQuestionEqualsToken
|
|
148
|
+
]);
|
|
149
|
+
/** Whether a `let`/`var` is assigned again anywhere it is visible. */
|
|
150
|
+
function isReassigned(declaration) {
|
|
151
|
+
const name = declaration.name;
|
|
152
|
+
if (!ts.isIdentifier(name)) return true;
|
|
153
|
+
let reassigned = false;
|
|
154
|
+
walk(declaration.getSourceFile(), (node) => {
|
|
155
|
+
if (reassigned) return false;
|
|
156
|
+
let target;
|
|
157
|
+
if (ts.isBinaryExpression(node) && ASSIGNMENT_OPERATORS.has(node.operatorToken.kind)) target = unwrap(node.left);
|
|
158
|
+
else if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) && (node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken)) target = unwrap(node.operand);
|
|
159
|
+
if (target && ts.isIdentifier(target) && target.text === name.text && findDeclaration(target) === declaration) reassigned = true;
|
|
160
|
+
}, true);
|
|
161
|
+
return reassigned;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The expression an identifier was initialised with, when it names a
|
|
165
|
+
* `const` (or a `let`/`var` never assigned again) declared once.
|
|
166
|
+
*/
|
|
167
|
+
function resolveInitializer(identifier) {
|
|
168
|
+
const declaration = findDeclaration(identifier);
|
|
169
|
+
if (!declaration || declaration === "other" || !declaration.initializer) return void 0;
|
|
170
|
+
const list = declaration.parent;
|
|
171
|
+
if (!(ts.isVariableDeclarationList(list) && (list.flags & ts.NodeFlags.Const) !== 0) && isReassigned(declaration)) return void 0;
|
|
172
|
+
return declaration.initializer;
|
|
173
|
+
}
|
|
174
|
+
/** Every value reference to a variable declaration in its file (not the declaration itself). */
|
|
175
|
+
function referencesOf(declaration) {
|
|
176
|
+
const name = declaration.name;
|
|
177
|
+
if (!ts.isIdentifier(name)) return [];
|
|
178
|
+
const found = [];
|
|
179
|
+
walk(declaration.getSourceFile(), (node) => {
|
|
180
|
+
if (ts.isIdentifier(node) && node !== name && node.text === name.text && isValueReference(node) && findDeclaration(node) === declaration) found.push(node);
|
|
181
|
+
}, true);
|
|
182
|
+
return found;
|
|
183
|
+
}
|
|
184
|
+
/** 1-based line and column of a node's first character. */
|
|
185
|
+
function positionOf(sourceFile, node) {
|
|
186
|
+
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
187
|
+
return {
|
|
188
|
+
line: position.line + 1,
|
|
189
|
+
column: position.character + 1
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
//#endregion
|
|
194
|
+
export { ancestors, calleeName, isFunctionLike, isMemberAccess, isValueReference, keyName, literalString, memberName, positionOf, referencesOf, resolveInitializer, rootIdentifier, unwrap, walk };
|
|
195
|
+
//# sourceMappingURL=source-ast.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-ast.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/source-ast.ts"],"sourcesContent":["import ts from \"typescript\";\nimport type { WarlockErrorCode } from \"../../errors/error-codes\";\n\n/**\n * Syntax helpers for the source convention checks (`WL9005`–`WL9012`).\n *\n * The checks read one file at a time with `ts.createSourceFile` — no program,\n * no type checker — so they stay as fast as a parse and run the same whatever\n * linter the app uses. Identifiers are resolved to their declarations by\n * walking the enclosing scopes of the syntax tree.\n */\n\n/** One problem a source check found, before it is placed and turned into a problem. */\nexport type SourceFinding = {\n code: WarlockErrorCode;\n /** The node the problem is reported at. */\n node: ts.Node;\n message: string;\n /** Default `error`. */\n severity?: \"error\" | \"warning\";\n};\n\n/** `a.b` or `a[\"b\"]`. */\nexport type MemberAccess = ts.PropertyAccessExpression | ts.ElementAccessExpression;\n\n/** Whether a node is `a.b` or `a[b]`. */\nexport function isMemberAccess(node: ts.Node): node is MemberAccess {\n return ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node);\n}\n\n/** Strip parentheses and type-only wrappers (`x as T`, `x!`, `<T>x`, `x satisfies T`). */\nexport function unwrap(node: ts.Node): ts.Node {\n let current = node;\n\n while (\n ts.isParenthesizedExpression(current) ||\n ts.isAsExpression(current) ||\n ts.isNonNullExpression(current) ||\n ts.isTypeAssertionExpression(current) ||\n ts.isSatisfiesExpression(current)\n ) {\n current = current.expression;\n }\n\n return current;\n}\n\n/** The text of a string literal or a template without substitutions. */\nexport function literalString(node: ts.Node | undefined): string | undefined {\n if (!node) return undefined;\n\n const value = unwrap(node);\n\n return ts.isStringLiteral(value) || ts.isNoSubstitutionTemplateLiteral(value)\n ? value.text\n : undefined;\n}\n\n/** The static name of a member (`a.b` → `b`, `a[\"b\"]` → `b`). */\nexport function memberName(node: MemberAccess): string | undefined {\n if (ts.isPropertyAccessExpression(node)) return node.name.text;\n\n return literalString(node.argumentExpression);\n}\n\n/** The static name of an object-literal or type-member key (`{ a: 1 }`, `{ \"a\": 1 }`). */\nexport function keyName(name: ts.PropertyName | undefined): string | undefined {\n if (!name) return undefined;\n if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isPrivateIdentifier(name)) {\n return name.text;\n }\n\n return undefined;\n}\n\n/**\n * The name a call is made through: `foo()` → `foo`, `a.b.foo()` → `foo`.\n * Used to recognise helpers by name wherever they are imported from.\n */\nexport function calleeName(call: ts.CallExpression | ts.NewExpression): string | undefined {\n const callee = unwrap(call.expression);\n\n if (ts.isIdentifier(callee)) return callee.text;\n if (isMemberAccess(callee)) return memberName(callee);\n\n return undefined;\n}\n\n/** The root identifier of a member chain: `a.b.c()` → `a`. */\nexport function rootIdentifier(node: ts.Node): ts.Identifier | undefined {\n let current = unwrap(node);\n\n for (;;) {\n if (ts.isIdentifier(current)) return current;\n\n if (isMemberAccess(current) || ts.isCallExpression(current)) {\n current = unwrap(current.expression);\n continue;\n }\n\n return undefined;\n }\n}\n\n/** Whether a node is a function body owner (declaration, expression, arrow, method, accessor). */\nexport function isFunctionLike(node: ts.Node): node is ts.FunctionLikeDeclaration {\n return (\n ts.isFunctionDeclaration(node) ||\n ts.isFunctionExpression(node) ||\n ts.isArrowFunction(node) ||\n ts.isMethodDeclaration(node) ||\n ts.isGetAccessorDeclaration(node) ||\n ts.isSetAccessorDeclaration(node) ||\n ts.isConstructorDeclaration(node)\n );\n}\n\n/**\n * Walk a node depth-first, skipping type annotations. The visitor returns\n * `false` to skip a node's children. Nested functions are not entered unless\n * `enterFunctions`.\n */\nexport function walk(\n node: ts.Node,\n visit: (node: ts.Node) => boolean | void,\n enterFunctions = false,\n): void {\n if (visit(node) === false) return;\n if (!enterFunctions && isFunctionLike(node)) return;\n\n ts.forEachChild(node, (child) => {\n if (!ts.isTypeNode(child)) walk(child, visit, enterFunctions);\n\n return undefined;\n });\n}\n\n/** Every ancestor of a node, nearest first. */\nexport function ancestors(node: ts.Node): ts.Node[] {\n const list: ts.Node[] = [];\n let current = node.parent;\n\n while (current) {\n list.push(current);\n current = current.parent;\n }\n\n return list;\n}\n\n/**\n * Whether an identifier is a value reference: not the `b` of `a.b`, an object\n * key, or the name a declaration introduces.\n */\nexport function isValueReference(identifier: ts.Identifier): boolean {\n const parent = identifier.parent;\n\n if (!parent) return true;\n if (ts.isPropertyAccessExpression(parent)) return parent.expression === identifier;\n if (ts.isShorthandPropertyAssignment(parent)) return true;\n\n if (\n ts.isPropertyAssignment(parent) ||\n ts.isMethodDeclaration(parent) ||\n ts.isPropertyDeclaration(parent) ||\n ts.isGetAccessorDeclaration(parent) ||\n ts.isSetAccessorDeclaration(parent) ||\n ts.isPropertySignature(parent) ||\n ts.isVariableDeclaration(parent) ||\n ts.isParameter(parent) ||\n ts.isFunctionDeclaration(parent) ||\n ts.isFunctionExpression(parent) ||\n ts.isClassDeclaration(parent) ||\n ts.isClassExpression(parent) ||\n ts.isEnumMember(parent)\n ) {\n return parent.name !== identifier;\n }\n\n if (ts.isBindingElement(parent)) {\n return parent.name !== identifier && parent.propertyName !== identifier;\n }\n\n return !(\n ts.isJsxAttribute(parent) ||\n ts.isImportSpecifier(parent) ||\n ts.isExportSpecifier(parent) ||\n ts.isImportClause(parent) ||\n ts.isNamespaceImport(parent) ||\n ts.isLabeledStatement(parent) ||\n ts.isBreakOrContinueStatement(parent)\n );\n}\n\n/** What declares a name: a single `const`/`let`/`var` declarator, or something else. */\ntype Declaration = ts.VariableDeclaration | \"other\";\n\nfunction declarationListDeclares(\n list: ts.VariableDeclarationList,\n name: string,\n): Declaration | undefined {\n for (const declaration of list.declarations) {\n if (ts.isIdentifier(declaration.name)) {\n if (declaration.name.text === name) return declaration;\n } else if (bindingDeclares(declaration.name, name)) {\n return \"other\";\n }\n }\n\n return undefined;\n}\n\nfunction bindingDeclares(binding: ts.BindingName, name: string): boolean {\n if (ts.isIdentifier(binding)) return binding.text === name;\n\n return binding.elements.some(\n (element) => !ts.isOmittedExpression(element) && bindingDeclares(element.name, name),\n );\n}\n\nfunction statementsDeclare(\n statements: readonly ts.Statement[],\n name: string,\n): Declaration | undefined {\n for (const statement of statements) {\n if (ts.isVariableStatement(statement)) {\n const found = declarationListDeclares(statement.declarationList, name);\n if (found) return found;\n } else if (\n (ts.isFunctionDeclaration(statement) ||\n ts.isClassDeclaration(statement) ||\n ts.isEnumDeclaration(statement)) &&\n statement.name?.text === name\n ) {\n return \"other\";\n } else if (ts.isImportDeclaration(statement) && importDeclares(statement, name)) {\n return \"other\";\n }\n }\n\n return undefined;\n}\n\nfunction importDeclares(statement: ts.ImportDeclaration, name: string): boolean {\n const clause = statement.importClause;\n\n if (!clause) return false;\n if (clause.name?.text === name) return true;\n\n const bindings = clause.namedBindings;\n\n if (!bindings) return false;\n if (ts.isNamespaceImport(bindings)) return bindings.name.text === name;\n\n return bindings.elements.some((element) => element.name.text === name);\n}\n\n/** The declaration a scope node introduces for `name`, if it introduces one. */\nfunction scopeDeclares(scope: ts.Node, name: string): Declaration | undefined {\n if (ts.isSourceFile(scope) || ts.isBlock(scope) || ts.isModuleBlock(scope)) {\n return statementsDeclare(scope.statements, name);\n }\n\n if (ts.isCaseBlock(scope)) {\n for (const clause of scope.clauses) {\n const found = statementsDeclare(clause.statements, name);\n if (found) return found;\n }\n\n return undefined;\n }\n\n if (isFunctionLike(scope)) {\n if (scope.parameters.some((parameter) => bindingDeclares(parameter.name, name))) {\n return \"other\";\n }\n\n return ts.isFunctionExpression(scope) && scope.name?.text === name ? \"other\" : undefined;\n }\n\n if (\n (ts.isForStatement(scope) || ts.isForInStatement(scope) || ts.isForOfStatement(scope)) &&\n scope.initializer &&\n ts.isVariableDeclarationList(scope.initializer)\n ) {\n return declarationListDeclares(scope.initializer, name) ? \"other\" : undefined;\n }\n\n if (ts.isCatchClause(scope) && scope.variableDeclaration) {\n return bindingDeclares(scope.variableDeclaration.name, name) ? \"other\" : undefined;\n }\n\n return undefined;\n}\n\n/** The nearest declaration of an identifier's name, searching outward from it. */\nexport function findDeclaration(identifier: ts.Identifier): Declaration | undefined {\n for (const scope of ancestors(identifier)) {\n const found = scopeDeclares(scope, identifier.text);\n if (found) return found;\n }\n\n return undefined;\n}\n\nconst ASSIGNMENT_OPERATORS: ReadonlySet<ts.SyntaxKind> = new Set([\n ts.SyntaxKind.EqualsToken,\n ts.SyntaxKind.PlusEqualsToken,\n ts.SyntaxKind.MinusEqualsToken,\n ts.SyntaxKind.AsteriskEqualsToken,\n ts.SyntaxKind.SlashEqualsToken,\n ts.SyntaxKind.BarBarEqualsToken,\n ts.SyntaxKind.AmpersandAmpersandEqualsToken,\n ts.SyntaxKind.QuestionQuestionEqualsToken,\n]);\n\n/** Whether a `let`/`var` is assigned again anywhere it is visible. */\nfunction isReassigned(declaration: ts.VariableDeclaration): boolean {\n const name = declaration.name;\n\n if (!ts.isIdentifier(name)) return true;\n\n let reassigned = false;\n\n walk(\n declaration.getSourceFile(),\n (node) => {\n if (reassigned) return false;\n\n let target: ts.Node | undefined;\n\n if (ts.isBinaryExpression(node) && ASSIGNMENT_OPERATORS.has(node.operatorToken.kind)) {\n target = unwrap(node.left);\n } else if (\n (ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&\n (node.operator === ts.SyntaxKind.PlusPlusToken ||\n node.operator === ts.SyntaxKind.MinusMinusToken)\n ) {\n target = unwrap(node.operand);\n }\n\n if (\n target &&\n ts.isIdentifier(target) &&\n target.text === name.text &&\n findDeclaration(target) === declaration\n ) {\n reassigned = true;\n }\n\n return undefined;\n },\n true,\n );\n\n return reassigned;\n}\n\n/**\n * The expression an identifier was initialised with, when it names a\n * `const` (or a `let`/`var` never assigned again) declared once.\n */\nexport function resolveInitializer(identifier: ts.Identifier): ts.Expression | undefined {\n const declaration = findDeclaration(identifier);\n\n if (!declaration || declaration === \"other\" || !declaration.initializer) return undefined;\n\n const list = declaration.parent;\n const isConst = ts.isVariableDeclarationList(list) && (list.flags & ts.NodeFlags.Const) !== 0;\n\n if (!isConst && isReassigned(declaration)) return undefined;\n\n return declaration.initializer;\n}\n\n/** Every value reference to a variable declaration in its file (not the declaration itself). */\nexport function referencesOf(declaration: ts.VariableDeclaration): ts.Identifier[] {\n const name = declaration.name;\n\n if (!ts.isIdentifier(name)) return [];\n\n const found: ts.Identifier[] = [];\n\n walk(\n declaration.getSourceFile(),\n (node) => {\n if (\n ts.isIdentifier(node) &&\n node !== name &&\n node.text === name.text &&\n isValueReference(node) &&\n findDeclaration(node) === declaration\n ) {\n found.push(node);\n }\n\n return undefined;\n },\n true,\n );\n\n return found;\n}\n\n/** 1-based line and column of a node's first character. */\nexport function positionOf(\n sourceFile: ts.SourceFile,\n node: ts.Node,\n): { line: number; column: number } {\n const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));\n\n return { line: position.line + 1, column: position.character + 1 };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import glob from "fast-glob";
|
|
2
|
+
|
|
3
|
+
//#region ../../packages/core/src/tooling/check/source-files.ts
|
|
4
|
+
/** The project source files a full `warlock check` lints and convention-checks. */
|
|
5
|
+
async function listSourceFiles(cwd) {
|
|
6
|
+
return (await glob(["src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"], {
|
|
7
|
+
cwd,
|
|
8
|
+
onlyFiles: true,
|
|
9
|
+
ignore: ["**/node_modules/**"]
|
|
10
|
+
})).sort();
|
|
11
|
+
}
|
|
12
|
+
/** Test files, wherever they live (outside node_modules). */
|
|
13
|
+
async function listTestFiles(cwd) {
|
|
14
|
+
return glob(["**/*.{test,spec}.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"], {
|
|
15
|
+
cwd,
|
|
16
|
+
onlyFiles: true,
|
|
17
|
+
ignore: [
|
|
18
|
+
"**/node_modules/**",
|
|
19
|
+
"**/dist/**",
|
|
20
|
+
"**/.warlock/**"
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { listSourceFiles, listTestFiles };
|
|
27
|
+
//# sourceMappingURL=source-files.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-files.mjs","names":["fg"],"sources":["../../../../../../../../../packages/core/src/tooling/check/source-files.ts"],"sourcesContent":["import fg from \"fast-glob\";\n\n/** The project source files a full `warlock check` lints and convention-checks. */\nexport async function listSourceFiles(cwd: string): Promise<string[]> {\n const files = await fg([\"src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}\"], {\n cwd,\n onlyFiles: true,\n ignore: [\"**/node_modules/**\"],\n });\n\n return files.sort();\n}\n\n/** Test files, wherever they live (outside node_modules). */\nexport async function listTestFiles(cwd: string): Promise<string[]> {\n return fg([\"**/*.{test,spec}.{ts,tsx,js,jsx,mts,cts,mjs,cjs}\"], {\n cwd,\n onlyFiles: true,\n ignore: [\"**/node_modules/**\", \"**/dist/**\", \"**/.warlock/**\"],\n });\n}\n"],"mappings":";;;;AAGA,eAAsB,gBAAgB,KAAgC;CAOpE,QAAO,MANaA,KAAG,CAAC,0CAA0C,GAAG;EACnE;EACA,WAAW;EACX,QAAQ,CAAC,oBAAoB;CAC/B,CAAC,EAEW,CAAC,KAAK;AACpB;;AAGA,eAAsB,cAAc,KAAgC;CAClE,OAAOA,KAAG,CAAC,kDAAkD,GAAG;EAC9D;EACA,WAAW;EACX,QAAQ;GAAC;GAAsB;GAAc;EAAgB;CAC/D,CAAC;AACH"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ToolingProblem } from "../problem.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../../packages/core/src/tooling/check/tests-step.d.ts
|
|
4
|
+
/** How a vitest run ended: its exit code, output, and the JSON report if one was written. */
|
|
5
|
+
type VitestRun = {
|
|
6
|
+
code: number | null;
|
|
7
|
+
signal: string | null;
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string; /** The `--reporter=json` report, parsed; `undefined` when none was written. */
|
|
10
|
+
report: unknown;
|
|
11
|
+
};
|
|
12
|
+
/** Runs the app's vitest with the given arguments. Injectable for tests. */
|
|
13
|
+
type VitestRunner = (binary: string, args: readonly string[], cwd: string) => Promise<VitestRun>;
|
|
14
|
+
/**
|
|
15
|
+
* The app's own vitest CLI entry, resolved from the app root — never a global
|
|
16
|
+
* install — or `undefined` when the app does not have vitest.
|
|
17
|
+
*/
|
|
18
|
+
declare function findVitest(cwd: string): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* The default {@link VitestRunner}: `node <vitest> <args> --reporter=json
|
|
21
|
+
* --outputFile=<tmp>`, reading the report back from the file so test output on
|
|
22
|
+
* stdout cannot corrupt it.
|
|
23
|
+
*/
|
|
24
|
+
declare const runVitest: VitestRunner;
|
|
25
|
+
/**
|
|
26
|
+
* Turn vitest's JSON report (the Jest-compatible shape `--reporter=json`
|
|
27
|
+
* writes) into problems: a failed test is `WL7301`, a file that failed before
|
|
28
|
+
* its tests ran is `WL7302`, and a failure caused by an unreachable service or
|
|
29
|
+
* a busy port is the matching environment code instead.
|
|
30
|
+
*/
|
|
31
|
+
declare function parseVitestReport(report: unknown, cwd: string): ToolingProblem[];
|
|
32
|
+
//#endregion
|
|
33
|
+
export { VitestRun, VitestRunner, findVitest, parseVitestReport, runVitest };
|
|
34
|
+
//# sourceMappingURL=tests-step.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tests-step.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/tests-step.ts"],"mappings":";;;;KASY,SAAA;EACV,IAAA;EACA,MAAA;EACA,MAAA;EACA,MAAA,UAHA;EAKA,MAAA;AAAA;;KAIU,YAAA,IACV,MAAA,UACA,IAAA,qBACA,GAAA,aACG,OAAO,CAAC,SAAA;;AARL;AAIR;;iBAagB,UAAA,CAAW,GAAW;;;;;;cA6BzB,SAAA,EAAW,YAoCvB;AAsDD;;;;;;AAAA,iBAAgB,iBAAA,CAAkB,MAAA,WAAiB,GAAA,WAAc,cAAc"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { errorCodeOf } from "../../errors/error-codes.mjs";
|
|
2
|
+
import { createProblem } from "../problem.mjs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
|
|
9
|
+
//#region ../../packages/core/src/tooling/check/tests-step.ts
|
|
10
|
+
/** Ten minutes: a suite slower than that is reported as a crashed tool, not a failure. */
|
|
11
|
+
const TEST_TIMEOUT_MS = 600 * 1e3;
|
|
12
|
+
/**
|
|
13
|
+
* The app's own vitest CLI entry, resolved from the app root — never a global
|
|
14
|
+
* install — or `undefined` when the app does not have vitest.
|
|
15
|
+
*/
|
|
16
|
+
function findVitest(cwd) {
|
|
17
|
+
try {
|
|
18
|
+
const manifestPath = createRequire(path.join(cwd, "package.json")).resolve("vitest/package.json");
|
|
19
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
20
|
+
const bin = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "bin") : void 0;
|
|
21
|
+
const entry = typeof bin === "string" ? bin : typeof bin === "object" && bin !== null ? Reflect.get(bin, "vitest") : void 0;
|
|
22
|
+
if (typeof entry !== "string") return void 0;
|
|
23
|
+
const binary = path.resolve(path.dirname(manifestPath), entry);
|
|
24
|
+
return existsSync(binary) ? binary : void 0;
|
|
25
|
+
} catch {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The default {@link VitestRunner}: `node <vitest> <args> --reporter=json
|
|
31
|
+
* --outputFile=<tmp>`, reading the report back from the file so test output on
|
|
32
|
+
* stdout cannot corrupt it.
|
|
33
|
+
*/
|
|
34
|
+
const runVitest = (binary, args, cwd) => {
|
|
35
|
+
const directory = mkdtempSync(path.join(os.tmpdir(), "warlock-check-vitest-"));
|
|
36
|
+
const outputFile = path.join(directory, "report.json");
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
execFile(process.execPath, [
|
|
39
|
+
binary,
|
|
40
|
+
...args,
|
|
41
|
+
"--reporter=json",
|
|
42
|
+
`--outputFile=${outputFile}`
|
|
43
|
+
], {
|
|
44
|
+
cwd,
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
windowsHide: true,
|
|
47
|
+
timeout: TEST_TIMEOUT_MS,
|
|
48
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
49
|
+
env: {
|
|
50
|
+
...process.env,
|
|
51
|
+
CI: process.env.CI ?? "1"
|
|
52
|
+
}
|
|
53
|
+
}, (error, stdout, stderr) => {
|
|
54
|
+
let report;
|
|
55
|
+
try {
|
|
56
|
+
report = JSON.parse(readFileSync(outputFile, "utf8"));
|
|
57
|
+
} catch {
|
|
58
|
+
report = void 0;
|
|
59
|
+
}
|
|
60
|
+
rmSync(directory, {
|
|
61
|
+
recursive: true,
|
|
62
|
+
force: true
|
|
63
|
+
});
|
|
64
|
+
resolve({
|
|
65
|
+
code: error === null ? 0 : typeof error.code === "number" ? error.code : null,
|
|
66
|
+
signal: error?.signal ?? null,
|
|
67
|
+
stdout,
|
|
68
|
+
stderr,
|
|
69
|
+
report
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The vitest arguments for a run. Verified against `vitest --help` (4.1):
|
|
76
|
+
* `run` executes once without watch; `related <files> --run` runs only the
|
|
77
|
+
* tests that import the given source files; `--passWithNoTests` exits 0 when
|
|
78
|
+
* nothing matches; `--reporter=json --outputFile=<file>` writes the report.
|
|
79
|
+
*/
|
|
80
|
+
function vitestArguments(files) {
|
|
81
|
+
if (files === null) return ["run", "--passWithNoTests"];
|
|
82
|
+
return [
|
|
83
|
+
"related",
|
|
84
|
+
...files,
|
|
85
|
+
"--run",
|
|
86
|
+
"--passWithNoTests"
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
function isRecord(value) {
|
|
90
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
91
|
+
}
|
|
92
|
+
/** ANSI color sequences, which vitest leaves in failure messages. */
|
|
93
|
+
const ANSI_SEQUENCE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
94
|
+
/** The first meaningful lines of a failure message, bounded for one JSON field. */
|
|
95
|
+
function summarize(message) {
|
|
96
|
+
const text = message.replace(ANSI_SEQUENCE, "").trim();
|
|
97
|
+
return text.length > 1e3 ? `${text.slice(0, 1e3)}…` : text;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Whether a failure message shows the machine rather than the code: a
|
|
101
|
+
* refused/unreachable service (`WL8003`) or a port already taken (`WL8004`).
|
|
102
|
+
*/
|
|
103
|
+
function environmentCodeOf(message) {
|
|
104
|
+
const errno = /\b(ECONNREFUSED|ENOTFOUND|EHOSTUNREACH|EADDRINUSE)\b/.exec(message)?.[1];
|
|
105
|
+
if (!errno) return void 0;
|
|
106
|
+
const code = errorCodeOf({ code: errno });
|
|
107
|
+
return code === "WL8003" || code === "WL8004" ? code : void 0;
|
|
108
|
+
}
|
|
109
|
+
function relative$1(cwd, file) {
|
|
110
|
+
if (typeof file !== "string" || file.length === 0) return null;
|
|
111
|
+
return (path.isAbsolute(file) ? path.relative(cwd, file) : file).split(path.sep).join("/");
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Turn vitest's JSON report (the Jest-compatible shape `--reporter=json`
|
|
115
|
+
* writes) into problems: a failed test is `WL7301`, a file that failed before
|
|
116
|
+
* its tests ran is `WL7302`, and a failure caused by an unreachable service or
|
|
117
|
+
* a busy port is the matching environment code instead.
|
|
118
|
+
*/
|
|
119
|
+
function parseVitestReport(report, cwd) {
|
|
120
|
+
if (!isRecord(report) || !Array.isArray(report.testResults)) return [];
|
|
121
|
+
const problems = [];
|
|
122
|
+
for (const suite of report.testResults) {
|
|
123
|
+
if (!isRecord(suite)) continue;
|
|
124
|
+
const file = relative$1(cwd, suite.name);
|
|
125
|
+
const failed = (Array.isArray(suite.assertionResults) ? suite.assertionResults : []).filter((assertion) => isRecord(assertion) && assertion.status === "failed");
|
|
126
|
+
for (const assertion of failed) {
|
|
127
|
+
const message = summarize((Array.isArray(assertion.failureMessages) ? assertion.failureMessages.filter((message) => typeof message === "string") : []).join("\n") || "Test failed.");
|
|
128
|
+
const location = isRecord(assertion.location) ? assertion.location : void 0;
|
|
129
|
+
const name = typeof assertion.fullName === "string" ? assertion.fullName : typeof assertion.title === "string" ? assertion.title : null;
|
|
130
|
+
const environment = environmentCodeOf(message);
|
|
131
|
+
problems.push(createProblem({
|
|
132
|
+
code: environment ?? "WL7301",
|
|
133
|
+
source: environment ? "environment" : "vitest",
|
|
134
|
+
message,
|
|
135
|
+
file,
|
|
136
|
+
line: typeof location?.line === "number" ? location.line : null,
|
|
137
|
+
column: typeof location?.column === "number" ? location.column : null,
|
|
138
|
+
rule: name
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
const suiteMessage = typeof suite.message === "string" ? suite.message.trim() : "";
|
|
142
|
+
if (suite.status === "failed" && failed.length === 0) {
|
|
143
|
+
const message = summarize(suiteMessage || "The test file failed before its tests ran.");
|
|
144
|
+
const environment = environmentCodeOf(message);
|
|
145
|
+
problems.push(createProblem({
|
|
146
|
+
code: environment ?? "WL7302",
|
|
147
|
+
source: environment ? "environment" : "vitest",
|
|
148
|
+
message,
|
|
149
|
+
file
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return problems;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Problems for a vitest run that produced no report: a crash, a timeout or a
|
|
157
|
+
* signal is the tool (`WL8007`); an exit without a report after a config or
|
|
158
|
+
* import error is the code (`WL7302`).
|
|
159
|
+
*/
|
|
160
|
+
function vitestRunFailure(run) {
|
|
161
|
+
const output = summarize(`${run.stderr}\n${run.stdout}`) || "vitest wrote no report.";
|
|
162
|
+
if (run.signal !== null || run.code === null || run.code > 1 || /heap out of memory/i.test(output)) return [createProblem({
|
|
163
|
+
code: "WL8007",
|
|
164
|
+
source: "environment",
|
|
165
|
+
message: `vitest ${run.signal ? `was killed (${run.signal})` : `exited with ${run.code ?? "no code"}`}: ${output}`
|
|
166
|
+
})];
|
|
167
|
+
const environment = environmentCodeOf(output);
|
|
168
|
+
return [createProblem({
|
|
169
|
+
code: environment ?? "WL7302",
|
|
170
|
+
source: environment ? "environment" : "vitest",
|
|
171
|
+
message: output
|
|
172
|
+
})];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
export { findVitest, parseVitestReport, runVitest, vitestArguments, vitestRunFailure };
|
|
177
|
+
//# sourceMappingURL=tests-step.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tests-step.mjs","names":["relative"],"sources":["../../../../../../../../../packages/core/src/tooling/check/tests-step.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { existsSync, mkdtempSync, readFileSync, rmSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { errorCodeOf } from \"../../errors/error-codes\";\nimport { createProblem, type ToolingProblem } from \"../problem\";\n\n/** How a vitest run ended: its exit code, output, and the JSON report if one was written. */\nexport type VitestRun = {\n code: number | null;\n signal: string | null;\n stdout: string;\n stderr: string;\n /** The `--reporter=json` report, parsed; `undefined` when none was written. */\n report: unknown;\n};\n\n/** Runs the app's vitest with the given arguments. Injectable for tests. */\nexport type VitestRunner = (\n binary: string,\n args: readonly string[],\n cwd: string,\n) => Promise<VitestRun>;\n\n/** Ten minutes: a suite slower than that is reported as a crashed tool, not a failure. */\nconst TEST_TIMEOUT_MS = 10 * 60 * 1000;\n\n/**\n * The app's own vitest CLI entry, resolved from the app root — never a global\n * install — or `undefined` when the app does not have vitest.\n */\nexport function findVitest(cwd: string): string | undefined {\n try {\n const require = createRequire(path.join(cwd, \"package.json\"));\n const manifestPath = require.resolve(\"vitest/package.json\");\n const manifest: unknown = JSON.parse(readFileSync(manifestPath, \"utf8\"));\n const bin =\n typeof manifest === \"object\" && manifest !== null ? Reflect.get(manifest, \"bin\") : undefined;\n const entry =\n typeof bin === \"string\"\n ? bin\n : typeof bin === \"object\" && bin !== null\n ? Reflect.get(bin, \"vitest\")\n : undefined;\n\n if (typeof entry !== \"string\") return undefined;\n\n const binary = path.resolve(path.dirname(manifestPath), entry);\n\n return existsSync(binary) ? binary : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The default {@link VitestRunner}: `node <vitest> <args> --reporter=json\n * --outputFile=<tmp>`, reading the report back from the file so test output on\n * stdout cannot corrupt it.\n */\nexport const runVitest: VitestRunner = (binary, args, cwd) => {\n const directory = mkdtempSync(path.join(os.tmpdir(), \"warlock-check-vitest-\"));\n const outputFile = path.join(directory, \"report.json\");\n\n return new Promise((resolve) => {\n execFile(\n process.execPath,\n [binary, ...args, \"--reporter=json\", `--outputFile=${outputFile}`],\n {\n cwd,\n encoding: \"utf8\",\n windowsHide: true,\n timeout: TEST_TIMEOUT_MS,\n maxBuffer: 64 * 1024 * 1024,\n env: { ...process.env, CI: process.env.CI ?? \"1\" },\n },\n (error, stdout, stderr) => {\n let report: unknown;\n\n try {\n report = JSON.parse(readFileSync(outputFile, \"utf8\"));\n } catch {\n report = undefined;\n }\n\n rmSync(directory, { recursive: true, force: true });\n resolve({\n code: error === null ? 0 : typeof error.code === \"number\" ? error.code : null,\n signal: error?.signal ?? null,\n stdout,\n stderr,\n report,\n });\n },\n );\n });\n};\n\n/**\n * The vitest arguments for a run. Verified against `vitest --help` (4.1):\n * `run` executes once without watch; `related <files> --run` runs only the\n * tests that import the given source files; `--passWithNoTests` exits 0 when\n * nothing matches; `--reporter=json --outputFile=<file>` writes the report.\n */\nexport function vitestArguments(files: readonly string[] | null): string[] {\n if (files === null) return [\"run\", \"--passWithNoTests\"];\n\n return [\"related\", ...files, \"--run\", \"--passWithNoTests\"];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** ANSI color sequences, which vitest leaves in failure messages. */\nconst ANSI_SEQUENCE = new RegExp(`${String.fromCharCode(27)}\\\\[[0-9;]*m`, \"g\");\n\n/** The first meaningful lines of a failure message, bounded for one JSON field. */\nfunction summarize(message: string): string {\n const text = message.replace(ANSI_SEQUENCE, \"\").trim();\n\n return text.length > 1000 ? `${text.slice(0, 1000)}…` : text;\n}\n\n/**\n * Whether a failure message shows the machine rather than the code: a\n * refused/unreachable service (`WL8003`) or a port already taken (`WL8004`).\n */\nexport function environmentCodeOf(message: string): \"WL8003\" | \"WL8004\" | undefined {\n const errno = /\\b(ECONNREFUSED|ENOTFOUND|EHOSTUNREACH|EADDRINUSE)\\b/.exec(message)?.[1];\n\n if (!errno) return undefined;\n\n const code = errorCodeOf({ code: errno });\n\n return code === \"WL8003\" || code === \"WL8004\" ? code : undefined;\n}\n\nfunction relative(cwd: string, file: unknown): string | null {\n if (typeof file !== \"string\" || file.length === 0) return null;\n\n return (path.isAbsolute(file) ? path.relative(cwd, file) : file).split(path.sep).join(\"/\");\n}\n\n/**\n * Turn vitest's JSON report (the Jest-compatible shape `--reporter=json`\n * writes) into problems: a failed test is `WL7301`, a file that failed before\n * its tests ran is `WL7302`, and a failure caused by an unreachable service or\n * a busy port is the matching environment code instead.\n */\nexport function parseVitestReport(report: unknown, cwd: string): ToolingProblem[] {\n if (!isRecord(report) || !Array.isArray(report.testResults)) return [];\n\n const problems: ToolingProblem[] = [];\n\n for (const suite of report.testResults) {\n if (!isRecord(suite)) continue;\n\n const file = relative(cwd, suite.name);\n const assertions = Array.isArray(suite.assertionResults) ? suite.assertionResults : [];\n const failed = assertions.filter(\n (assertion): assertion is Record<string, unknown> =>\n isRecord(assertion) && assertion.status === \"failed\",\n );\n\n for (const assertion of failed) {\n const messages = Array.isArray(assertion.failureMessages)\n ? assertion.failureMessages.filter(\n (message): message is string => typeof message === \"string\",\n )\n : [];\n const message = summarize(messages.join(\"\\n\") || \"Test failed.\");\n const location = isRecord(assertion.location) ? assertion.location : undefined;\n const name =\n typeof assertion.fullName === \"string\"\n ? assertion.fullName\n : typeof assertion.title === \"string\"\n ? assertion.title\n : null;\n const environment = environmentCodeOf(message);\n\n problems.push(\n createProblem({\n code: environment ?? \"WL7301\",\n source: environment ? \"environment\" : \"vitest\",\n message,\n file,\n line: typeof location?.line === \"number\" ? location.line : null,\n column: typeof location?.column === \"number\" ? location.column : null,\n rule: name,\n }),\n );\n }\n\n const suiteMessage = typeof suite.message === \"string\" ? suite.message.trim() : \"\";\n\n if (suite.status === \"failed\" && failed.length === 0) {\n const message = summarize(suiteMessage || \"The test file failed before its tests ran.\");\n const environment = environmentCodeOf(message);\n\n problems.push(\n createProblem({\n code: environment ?? \"WL7302\",\n source: environment ? \"environment\" : \"vitest\",\n message,\n file,\n }),\n );\n }\n }\n\n return problems;\n}\n\n/**\n * Problems for a vitest run that produced no report: a crash, a timeout or a\n * signal is the tool (`WL8007`); an exit without a report after a config or\n * import error is the code (`WL7302`).\n */\nexport function vitestRunFailure(run: VitestRun): ToolingProblem[] {\n const output = summarize(`${run.stderr}\\n${run.stdout}`) || \"vitest wrote no report.\";\n\n if (\n run.signal !== null ||\n run.code === null ||\n run.code > 1 ||\n /heap out of memory/i.test(output)\n ) {\n return [\n createProblem({\n code: \"WL8007\",\n source: \"environment\",\n message: `vitest ${run.signal ? `was killed (${run.signal})` : `exited with ${run.code ?? \"no code\"}`}: ${output}`,\n }),\n ];\n }\n\n const environment = environmentCodeOf(output);\n\n return [\n createProblem({\n code: environment ?? \"WL7302\",\n source: environment ? \"environment\" : \"vitest\",\n message: output,\n }),\n ];\n}\n"],"mappings":"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"}
|