@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
|
@@ -3,7 +3,7 @@ import { VariantOutputFormat } from "./image-variant-types.mjs";
|
|
|
3
3
|
|
|
4
4
|
//#region ../../packages/core/src/http/uploads/resolve-image-variant.d.ts
|
|
5
5
|
/**
|
|
6
|
-
* What to render: a file on a
|
|
6
|
+
* What to render: a file on a storage disk and a variant named in
|
|
7
7
|
* `uploads.images.variants`
|
|
8
8
|
*/
|
|
9
9
|
type ResolveImageVariantInput = {
|
|
@@ -21,7 +21,7 @@ type ResolveImageVariantInput = {
|
|
|
21
21
|
*/
|
|
22
22
|
format?: VariantOutputFormat;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Disk the source lives on; the default disk when omitted
|
|
25
25
|
*/
|
|
26
26
|
disk?: StorageDriverName;
|
|
27
27
|
};
|
|
@@ -41,7 +41,7 @@ type ResolvedImageVariant = {
|
|
|
41
41
|
contentType: string;
|
|
42
42
|
};
|
|
43
43
|
/**
|
|
44
|
-
* Resolve, and render on a cache miss, one variant of an image on a
|
|
44
|
+
* Resolve, and render on a cache miss, one variant of an image on a storage
|
|
45
45
|
* disk. This is what `uploadedFileController` serves; call it from your own
|
|
46
46
|
* route to serve variants of a private disk behind authentication:
|
|
47
47
|
*
|
|
@@ -61,7 +61,6 @@ type ResolvedImageVariant = {
|
|
|
61
61
|
* are pruned once a new derivative is written.
|
|
62
62
|
*
|
|
63
63
|
* @returns `undefined` when the path does not resolve to a file on the disk
|
|
64
|
-
* @throws ImageVariantsConfigError when the disk is not a local disk
|
|
65
64
|
* @throws HttpError(400) when variants are not configured, or the variant or
|
|
66
65
|
* format is not allowed
|
|
67
66
|
* @throws HttpError(413) when the source is over the configured limits
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-image-variant.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/resolve-image-variant.ts"],"mappings":";;;;;;AAoCA;;KAAY,wBAAA;EAoBc;;;EAhBxB,IAAA;EAWS;;;EANT,OAAA;EAWwB;AAM1B;;;EAXE,MAAA,GAAS,mBAAA;EAeT;;;EAVA,IAAA,GAAO,iBAAiB;AAAA;;AAmBb;
|
|
1
|
+
{"version":3,"file":"resolve-image-variant.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/resolve-image-variant.ts"],"mappings":";;;;;;AAoCA;;KAAY,wBAAA;EAoBc;;;EAhBxB,IAAA;EAWS;;;EANT,OAAA;EAWwB;AAM1B;;;EAXE,MAAA,GAAS,mBAAA;EAeT;;;EAVA,IAAA,GAAO,iBAAiB;AAAA;;AAmBb;AA6Bb;KA1CY,oBAAA;;;;EAIV,YAAA;EAwCQ;;;EAnCR,IAAA;EAEA,MAAA,EAAQ,mBAAmB;EAE3B,WAAA;AAAA;AA+B6B;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iBAFT,mBAAA,CACpB,KAAA,EAAO,wBAAA,GACN,OAAA,CAAQ,oBAAA"}
|
|
@@ -2,14 +2,14 @@ import { HttpError } from "../errors/errors.mjs";
|
|
|
2
2
|
import { uploadsConfig } from "../uploads-config.mjs";
|
|
3
3
|
import { variantCacheKey, variantCachePath, variantGenerationTag, variantSourceDirectory, variantSourceKey } from "./variant-cache-key.mjs";
|
|
4
4
|
import { pruneStaleVariantGenerationsIfCurrent, retryOnVanishedDirectory, warnIfVariantCacheIsPublic } from "./image-variant-cache.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { ImageVariantsConfigError } from "./image-variants-config-error.mjs";
|
|
5
|
+
import { localDiskRoot } from "./local-disk-root.mjs";
|
|
7
6
|
import { loadVariantSource } from "./load-variant-source.mjs";
|
|
7
|
+
import { generateImageVariant } from "./generate-image-variant.mjs";
|
|
8
8
|
import { resolveImageVariantsConfig } from "./resolve-image-variants-config.mjs";
|
|
9
|
-
import {
|
|
9
|
+
import { freshImageVariant, scheduleImageVariantCacheTrim } from "./trim-image-variant-cache.mjs";
|
|
10
10
|
import { variantGenerations } from "./variant-generations.mjs";
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
11
|
+
import { resolveRemoteImageVariant } from "./resolve-remote-image-variant.mjs";
|
|
12
|
+
import { resolveVariantCandidate } from "./resolve-variant-candidate.mjs";
|
|
13
13
|
|
|
14
14
|
//#region ../../packages/core/src/http/uploads/resolve-image-variant.ts
|
|
15
15
|
const CONTENT_TYPES = {
|
|
@@ -18,16 +18,8 @@ const CONTENT_TYPES = {
|
|
|
18
18
|
webp: "image/webp",
|
|
19
19
|
avif: "image/avif"
|
|
20
20
|
};
|
|
21
|
-
async function exists(target) {
|
|
22
|
-
try {
|
|
23
|
-
await fs.access(target);
|
|
24
|
-
return true;
|
|
25
|
-
} catch {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
21
|
/**
|
|
30
|
-
* Resolve, and render on a cache miss, one variant of an image on a
|
|
22
|
+
* Resolve, and render on a cache miss, one variant of an image on a storage
|
|
31
23
|
* disk. This is what `uploadedFileController` serves; call it from your own
|
|
32
24
|
* route to serve variants of a private disk behind authentication:
|
|
33
25
|
*
|
|
@@ -47,7 +39,6 @@ async function exists(target) {
|
|
|
47
39
|
* are pruned once a new derivative is written.
|
|
48
40
|
*
|
|
49
41
|
* @returns `undefined` when the path does not resolve to a file on the disk
|
|
50
|
-
* @throws ImageVariantsConfigError when the disk is not a local disk
|
|
51
42
|
* @throws HttpError(400) when variants are not configured, or the variant or
|
|
52
43
|
* format is not allowed
|
|
53
44
|
* @throws HttpError(413) when the source is over the configured limits
|
|
@@ -55,7 +46,7 @@ async function exists(target) {
|
|
|
55
46
|
*/
|
|
56
47
|
async function resolveImageVariant(input) {
|
|
57
48
|
const diskRoot = localDiskRoot(input.disk);
|
|
58
|
-
if (!diskRoot)
|
|
49
|
+
if (!diskRoot) return resolveRemoteImageVariant(input);
|
|
59
50
|
const images = resolveImageVariantsConfig(uploadsConfig("images"), diskRoot);
|
|
60
51
|
if (!images) throw new HttpError(400, "Image variants are not configured.");
|
|
61
52
|
const variant = Object.prototype.hasOwnProperty.call(images.variants, input.variant) ? images.variants[input.variant] : void 0;
|
|
@@ -79,8 +70,8 @@ async function resolveImageVariant(input) {
|
|
|
79
70
|
sourceKey,
|
|
80
71
|
generation
|
|
81
72
|
}, hash, format);
|
|
82
|
-
if (!await
|
|
83
|
-
if (await
|
|
73
|
+
if (!await freshImageVariant(target, images.maxCacheAge)) await variantGenerations.run(target, async () => {
|
|
74
|
+
if (await freshImageVariant(target, images.maxCacheAge)) return;
|
|
84
75
|
const image = await loadVariantSource(source.absolutePath, {
|
|
85
76
|
maxSourceBytes: images.maxSourceBytes,
|
|
86
77
|
maxSourcePixels: images.maxSourcePixels,
|
|
@@ -92,6 +83,7 @@ async function resolveImageVariant(input) {
|
|
|
92
83
|
format,
|
|
93
84
|
targetPath: target
|
|
94
85
|
}));
|
|
86
|
+
scheduleImageVariantCacheTrim(images, target);
|
|
95
87
|
await pruneStaleVariantGenerationsIfCurrent(source.absolutePath, variantSourceDirectory(images.cacheDirectory, sourceKey), generation);
|
|
96
88
|
});
|
|
97
89
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-image-variant.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/resolve-image-variant.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"resolve-image-variant.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/resolve-image-variant.ts"],"sourcesContent":["import type { StorageDriverName } from \"../../storage/types\";\nimport { HttpError } from \"../errors\";\nimport { uploadsConfig } from \"../uploads-config\";\nimport { generateImageVariant } from \"./generate-image-variant\";\nimport {\n pruneStaleVariantGenerationsIfCurrent,\n retryOnVanishedDirectory,\n warnIfVariantCacheIsPublic,\n} from \"./image-variant-cache\";\nimport { resolveRemoteImageVariant } from \"./resolve-remote-image-variant\";\nimport type { VariantOutputFormat } from \"./image-variant-types\";\nimport { loadVariantSource } from \"./load-variant-source\";\nimport { localDiskRoot } from \"./local-disk-root\";\nimport { resolveImageVariantsConfig } from \"./resolve-image-variants-config\";\nimport { resolveVariantCandidate } from \"./resolve-variant-candidate\";\nimport {\n variantCacheKey,\n variantCachePath,\n variantGenerationTag,\n variantSourceDirectory,\n variantSourceKey,\n} from \"./variant-cache-key\";\nimport { freshImageVariant, scheduleImageVariantCacheTrim } from \"./trim-image-variant-cache\";\nimport { variantGenerations } from \"./variant-generations\";\n\nconst CONTENT_TYPES: Record<VariantOutputFormat, string> = {\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n avif: \"image/avif\",\n};\n\n/**\n * What to render: a file on a storage disk and a variant named in\n * `uploads.images.variants`\n */\nexport type ResolveImageVariantInput = {\n /**\n * Path of the source, relative to the disk root\n */\n path: string;\n\n /**\n * Variant name from `uploads.images.variants`\n */\n variant: string;\n\n /**\n * Output format; defaults to the source's own format. Must be the source\n * format or one listed in `uploads.images.formats`.\n */\n format?: VariantOutputFormat;\n\n /**\n * Disk the source lives on; the default disk when omitted\n */\n disk?: StorageDriverName;\n};\n\n/**\n * A derivative on disk, ready to send\n */\nexport type ResolvedImageVariant = {\n /**\n * Absolute path of the cached derivative\n */\n absolutePath: string;\n\n /**\n * sha256 of everything the derivative depends on; a strong ETag value\n */\n hash: string;\n\n format: VariantOutputFormat;\n\n contentType: string;\n};\n\n/**\n * Resolve, and render on a cache miss, one variant of an image on a storage\n * disk. This is what `uploadedFileController` serves; call it from your own\n * route to serve variants of a private disk behind authentication:\n *\n * ```ts\n * const variant = await resolveImageVariant({ disk: \"private\", path, variant: \"thumb\" });\n * if (!variant) return response.notFound();\n * return response.sendBuffer(await fs.readFile(variant.absolutePath), {\n * contentType: variant.contentType,\n * });\n * ```\n *\n * The same guards apply as on the public route: the path must stay inside\n * the disk root and outside the derivative cache, the source must be a\n * jpeg/png/webp/avif within `maxSourceBytes`/`maxSourcePixels`, and only\n * declared variants and formats render. Derivatives are generated once per\n * key, even under concurrent calls, and older versions of a rewritten source\n * are pruned once a new derivative is written.\n *\n * @returns `undefined` when the path does not resolve to a file on the disk\n * @throws HttpError(400) when variants are not configured, or the variant or\n * format is not allowed\n * @throws HttpError(413) when the source is over the configured limits\n * @throws HttpError(415) when the source is not jpeg, png, webp or avif\n */\nexport async function resolveImageVariant(\n input: ResolveImageVariantInput,\n): Promise<ResolvedImageVariant | undefined> {\n const diskRoot = localDiskRoot(input.disk);\n\n if (!diskRoot) return resolveRemoteImageVariant(input);\n\n const images = resolveImageVariantsConfig(uploadsConfig(\"images\"), diskRoot);\n\n if (!images) {\n throw new HttpError(400, \"Image variants are not configured.\");\n }\n\n const variant = Object.prototype.hasOwnProperty.call(images.variants, input.variant)\n ? images.variants[input.variant]\n : undefined;\n\n if (variant === undefined) {\n throw new HttpError(400, `Unknown variant \"${input.variant}\".`);\n }\n\n if (input.format !== undefined && !images.formats.includes(input.format as never)) {\n throw new HttpError(400, `Format \"${input.format}\" is not allowed.`);\n }\n\n warnIfVariantCacheIsPublic(images.cacheDirectory);\n\n const candidate = await resolveVariantCandidate(input.path, diskRoot, images);\n\n if (!candidate) return undefined;\n\n const { source, stats, sourceFormat } = candidate;\n const format: VariantOutputFormat = input.format ?? sourceFormat;\n const hash = variantCacheKey({\n relativePath: source.relativePath,\n sourceSize: stats.size,\n sourceMtimeMs: stats.mtimeMs,\n variant,\n format,\n });\n const sourceKey = variantSourceKey(source.root, source.relativePath);\n const generation = variantGenerationTag(stats.size, stats.mtimeMs);\n const target = variantCachePath(images.cacheDirectory, { sourceKey, generation }, hash, format);\n\n if (!(await freshImageVariant(target, images.maxCacheAge))) {\n // Keyed on the target path, which includes the disk's real root: the\n // same file on two disks (a mirror copied with its mtime) has the same\n // `hash`, and joining the other disk's flight would leave this target\n // unwritten.\n await variantGenerations.run(target, async () => {\n if (await freshImageVariant(target, images.maxCacheAge)) return;\n\n const image = await loadVariantSource(source.absolutePath, {\n maxSourceBytes: images.maxSourceBytes,\n maxSourcePixels: images.maxSourcePixels,\n expectedFormat: sourceFormat,\n });\n\n await retryOnVanishedDirectory(() =>\n generateImageVariant({ image: image.clone(), variant, format, targetPath: target }),\n );\n scheduleImageVariantCacheTrim(images, target);\n await pruneStaleVariantGenerationsIfCurrent(\n source.absolutePath,\n variantSourceDirectory(images.cacheDirectory, sourceKey),\n generation,\n );\n });\n }\n\n return { absolutePath: target, hash, format, contentType: CONTENT_TYPES[format] };\n}\n"],"mappings":";;;;;;;;;;;;;;AAyBA,MAAM,gBAAqD;CACzD,MAAM;CACN,KAAK;CACL,MAAM;CACN,MAAM;AACR;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,eAAsB,oBACpB,OAC2C;CAC3C,MAAM,WAAW,cAAc,MAAM,IAAI;CAEzC,IAAI,CAAC,UAAU,OAAO,0BAA0B,KAAK;CAErD,MAAM,SAAS,2BAA2B,cAAc,QAAQ,GAAG,QAAQ;CAE3E,IAAI,CAAC,QACH,MAAM,IAAI,UAAU,KAAK,oCAAoC;CAG/D,MAAM,UAAU,OAAO,UAAU,eAAe,KAAK,OAAO,UAAU,MAAM,OAAO,IAC/E,OAAO,SAAS,MAAM,WACtB;CAEJ,IAAI,YAAY,QACd,MAAM,IAAI,UAAU,KAAK,oBAAoB,MAAM,QAAQ,GAAG;CAGhE,IAAI,MAAM,WAAW,UAAa,CAAC,OAAO,QAAQ,SAAS,MAAM,MAAe,GAC9E,MAAM,IAAI,UAAU,KAAK,WAAW,MAAM,OAAO,kBAAkB;CAGrE,2BAA2B,OAAO,cAAc;CAEhD,MAAM,YAAY,MAAM,wBAAwB,MAAM,MAAM,UAAU,MAAM;CAE5E,IAAI,CAAC,WAAW,OAAO;CAEvB,MAAM,EAAE,QAAQ,OAAO,iBAAiB;CACxC,MAAM,SAA8B,MAAM,UAAU;CACpD,MAAM,OAAO,gBAAgB;EAC3B,cAAc,OAAO;EACrB,YAAY,MAAM;EAClB,eAAe,MAAM;EACrB;EACA;CACF,CAAC;CACD,MAAM,YAAY,iBAAiB,OAAO,MAAM,OAAO,YAAY;CACnE,MAAM,aAAa,qBAAqB,MAAM,MAAM,MAAM,OAAO;CACjE,MAAM,SAAS,iBAAiB,OAAO,gBAAgB;EAAE;EAAW;CAAW,GAAG,MAAM,MAAM;CAE9F,IAAI,CAAE,MAAM,kBAAkB,QAAQ,OAAO,WAAW,GAKtD,MAAM,mBAAmB,IAAI,QAAQ,YAAY;EAC/C,IAAI,MAAM,kBAAkB,QAAQ,OAAO,WAAW,GAAG;EAEzD,MAAM,QAAQ,MAAM,kBAAkB,OAAO,cAAc;GACzD,gBAAgB,OAAO;GACvB,iBAAiB,OAAO;GACxB,gBAAgB;EAClB,CAAC;EAED,MAAM,+BACJ,qBAAqB;GAAE,OAAO,MAAM,MAAM;GAAG;GAAS;GAAQ,YAAY;EAAO,CAAC,CACpF;EACA,8BAA8B,QAAQ,MAAM;EAC5C,MAAM,sCACJ,OAAO,cACP,uBAAuB,OAAO,gBAAgB,SAAS,GACvD,UACF;CACF,CAAC;CAGH,OAAO;EAAE,cAAc;EAAQ;EAAM;EAAQ,aAAa,cAAc;CAAQ;AAClF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ImageVariantsConfigError } from "./image-variants-config-error.mjs";
|
|
2
|
-
import
|
|
2
|
+
import { variantCacheDirectoryFor } from "./image-variant-cache.mjs";
|
|
3
3
|
|
|
4
4
|
//#region ../../packages/core/src/http/uploads/resolve-image-variants-config.ts
|
|
5
5
|
/**
|
|
@@ -76,13 +76,15 @@ function resolveImageVariantsConfig(images, storageRoot) {
|
|
|
76
76
|
if (!rawVariants || typeof rawVariants !== "object" || Object.keys(rawVariants).length === 0) throw new ImageVariantsConfigError("uploads.images.variants must declare at least one variant.");
|
|
77
77
|
const variants = {};
|
|
78
78
|
for (const [name, raw] of Object.entries(rawVariants)) variants[name] = resolveVariant(name, raw);
|
|
79
|
-
const cacheDirectory =
|
|
79
|
+
const cacheDirectory = variantCacheDirectoryFor(storageRoot, images);
|
|
80
80
|
return {
|
|
81
81
|
variants,
|
|
82
82
|
formats: resolveFormats(images.formats),
|
|
83
83
|
maxSourceBytes: resolveLimit("maxSourceBytes", images.maxSourceBytes, DEFAULT_MAX_SOURCE_BYTES),
|
|
84
84
|
maxSourcePixels: resolveLimit("maxSourcePixels", images.maxSourcePixels, DEFAULT_MAX_SOURCE_PIXELS),
|
|
85
|
-
cacheDirectory
|
|
85
|
+
cacheDirectory,
|
|
86
|
+
maxCacheBytes: resolveLimit("maxCacheBytes", images.maxCacheBytes, 512 * 1024 * 1024),
|
|
87
|
+
maxCacheAge: resolveLimit("maxCacheAge", images.maxCacheAge, 10080 * 60)
|
|
86
88
|
};
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-image-variants-config.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/resolve-image-variants-config.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"resolve-image-variants-config.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/resolve-image-variants-config.ts"],"sourcesContent":["import type {\n ImageVariantDefinition,\n ImageVariantFit,\n ImageVariantOutputFormat,\n UploadsImagesConfigurations,\n} from \"../uploads-types\";\nimport { variantCacheDirectoryFor } from \"./image-variant-cache\";\nimport { ImageVariantsConfigError } from \"./image-variants-config-error\";\nimport type { ResolvedImageVariantsConfig } from \"./image-variant-types\";\n\n/**\n * Largest width or height a variant may declare\n */\nexport const MAX_VARIANT_DIMENSION = 8192;\n\n/**\n * Default `maxSourceBytes`: 25 MB\n */\nexport const DEFAULT_MAX_SOURCE_BYTES = 25 * 1024 * 1024;\n\n/**\n * Default `maxSourcePixels`\n */\nexport const DEFAULT_MAX_SOURCE_PIXELS = 40_000_000;\n\nconst FITS: readonly ImageVariantFit[] = [\"cover\", \"contain\", \"inside\"];\n\nconst OUTPUT_FORMATS: readonly ImageVariantOutputFormat[] = [\"avif\", \"webp\"];\n\nconst VARIANT_NAME = /^[A-Za-z0-9_-]+$/;\n\nfunction isPositiveInteger(value: unknown, max = Number.MAX_SAFE_INTEGER): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 1 && value <= max;\n}\n\nfunction resolveVariant(name: string, raw: unknown): ImageVariantDefinition {\n const key = `uploads.images.variants.${name}`;\n\n if (!VARIANT_NAME.test(name)) {\n throw new ImageVariantsConfigError(\n `${key}: a variant name may only use letters, digits, \"-\" and \"_\".`,\n );\n }\n\n if (!raw || typeof raw !== \"object\") {\n throw new ImageVariantsConfigError(`${key} must be an object with a width.`);\n }\n\n const { width, height, fit, quality, enlarge } = raw as Record<string, unknown>;\n\n if (!isPositiveInteger(width, MAX_VARIANT_DIMENSION)) {\n throw new ImageVariantsConfigError(\n `${key}.width must be an integer from 1 to ${MAX_VARIANT_DIMENSION}, got ${JSON.stringify(width)}.`,\n );\n }\n\n const variant: ImageVariantDefinition = { width };\n\n if (height !== undefined) {\n if (!isPositiveInteger(height, MAX_VARIANT_DIMENSION)) {\n throw new ImageVariantsConfigError(\n `${key}.height must be an integer from 1 to ${MAX_VARIANT_DIMENSION}, got ${JSON.stringify(height)}.`,\n );\n }\n\n variant.height = height;\n }\n\n if (fit !== undefined) {\n if (!FITS.includes(fit as ImageVariantFit)) {\n throw new ImageVariantsConfigError(\n `${key}.fit must be one of ${FITS.join(\", \")}, got ${JSON.stringify(fit)}.`,\n );\n }\n\n variant.fit = fit as ImageVariantFit;\n }\n\n if (quality !== undefined) {\n if (!isPositiveInteger(quality, 100)) {\n throw new ImageVariantsConfigError(\n `${key}.quality must be an integer from 1 to 100, got ${JSON.stringify(quality)}.`,\n );\n }\n\n variant.quality = quality;\n }\n\n if (enlarge !== undefined && typeof enlarge !== \"boolean\") {\n throw new ImageVariantsConfigError(\n `${key}.enlarge must be a boolean, got ${JSON.stringify(enlarge)}.`,\n );\n }\n\n // Always set, never left undefined: the effective flag (default false) is\n // part of the normalized variant, so toggling it changes the cache key.\n variant.enlarge = enlarge === true;\n\n return variant;\n}\n\nfunction resolveFormats(raw: unknown): readonly ImageVariantOutputFormat[] {\n if (raw === undefined) return [];\n\n if (!Array.isArray(raw)) {\n throw new ImageVariantsConfigError(`uploads.images.formats must be an array.`);\n }\n\n for (const format of raw) {\n if (!OUTPUT_FORMATS.includes(format)) {\n throw new ImageVariantsConfigError(\n `uploads.images.formats may only contain ${OUTPUT_FORMATS.join(\", \")}, got ${JSON.stringify(format)}.`,\n );\n }\n }\n\n return [...new Set(raw as ImageVariantOutputFormat[])];\n}\n\nfunction resolveLimit(name: string, raw: unknown, fallback: number): number {\n if (raw === undefined) return fallback;\n\n if (!isPositiveInteger(raw)) {\n throw new ImageVariantsConfigError(\n `uploads.images.${name} must be a positive integer, got ${JSON.stringify(raw)}.`,\n );\n }\n\n return raw;\n}\n\n/**\n * Validate the `uploads.images` config and apply its defaults.\n *\n * Every variant is copied field by field, so an unknown key in the config can\n * never reach sharp, and the result is what the cache key is computed from.\n *\n * @param images - the raw `uploads.images` value\n * @param storageRoot - the local storage root, base of the default cache directory\n * @returns the resolved config, or `undefined` when variants are not configured\n * @throws ImageVariantsConfigError when any value is out of bounds\n */\nexport function resolveImageVariantsConfig(\n images: UploadsImagesConfigurations | undefined,\n storageRoot: string,\n): ResolvedImageVariantsConfig | undefined {\n if (images === undefined || images === null) return undefined;\n\n if (typeof images !== \"object\") {\n throw new ImageVariantsConfigError(\"uploads.images must be an object.\");\n }\n\n const rawVariants = (images as { variants?: unknown }).variants;\n\n if (!rawVariants || typeof rawVariants !== \"object\" || Object.keys(rawVariants).length === 0) {\n throw new ImageVariantsConfigError(\n \"uploads.images.variants must declare at least one variant.\",\n );\n }\n\n const variants: Record<string, ImageVariantDefinition> = {};\n\n for (const [name, raw] of Object.entries(rawVariants)) {\n variants[name] = resolveVariant(name, raw);\n }\n\n const cacheDirectory = variantCacheDirectoryFor(storageRoot, images);\n\n return {\n variants,\n formats: resolveFormats(images.formats),\n maxSourceBytes: resolveLimit(\"maxSourceBytes\", images.maxSourceBytes, DEFAULT_MAX_SOURCE_BYTES),\n maxSourcePixels: resolveLimit(\n \"maxSourcePixels\",\n images.maxSourcePixels,\n DEFAULT_MAX_SOURCE_PIXELS,\n ),\n cacheDirectory,\n maxCacheBytes: resolveLimit(\"maxCacheBytes\", images.maxCacheBytes, 512 * 1024 * 1024),\n maxCacheAge: resolveLimit(\"maxCacheAge\", images.maxCacheAge, 7 * 24 * 60 * 60),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { HttpError } from "../errors/errors.mjs";
|
|
2
|
+
import { uploadsConfig } from "../uploads-config.mjs";
|
|
3
|
+
import { variantCacheKey, variantCachePath, variantSourceDirectory, variantSourceKey } from "./variant-cache-key.mjs";
|
|
4
|
+
import { retryOnVanishedDirectory, warnIfVariantCacheIsPublic } from "./image-variant-cache.mjs";
|
|
5
|
+
import { isVariantSourceFormat } from "./detect-image-format.mjs";
|
|
6
|
+
import { remoteVariantDisk, remoteVariantSource } from "./remote-variant-source.mjs";
|
|
7
|
+
import { generateImageVariant } from "./generate-image-variant.mjs";
|
|
8
|
+
import { resolveImageVariantsConfig } from "./resolve-image-variants-config.mjs";
|
|
9
|
+
import { freshImageVariant, scheduleImageVariantCacheTrim } from "./trim-image-variant-cache.mjs";
|
|
10
|
+
import { variantGenerations } from "./variant-generations.mjs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { atomicWriteAsync } from "@warlock.js/fs";
|
|
13
|
+
import fs from "node:fs/promises";
|
|
14
|
+
|
|
15
|
+
//#region ../../packages/core/src/http/uploads/resolve-remote-image-variant.ts
|
|
16
|
+
/** Cloud sources are read through their driver; derivatives remain in a local cache. */
|
|
17
|
+
async function resolveRemoteImageVariant(input) {
|
|
18
|
+
const disk = remoteVariantDisk(input.disk);
|
|
19
|
+
const images = resolveImageVariantsConfig(uploadsConfig("images"), disk.cacheRoot);
|
|
20
|
+
if (!images) throw new HttpError(400, "Image variants are not configured.");
|
|
21
|
+
if (!Object.prototype.hasOwnProperty.call(images.variants, input.variant)) throw new HttpError(400, `Unknown variant "${input.variant}".`);
|
|
22
|
+
if (input.format !== void 0 && !images.formats.includes(input.format)) throw new HttpError(400, `Format "${input.format}" is not allowed.`);
|
|
23
|
+
const source = await remoteVariantSource(input.path, disk, images);
|
|
24
|
+
if (!source) return void 0;
|
|
25
|
+
try {
|
|
26
|
+
return await renderRemoteImageVariant(input, disk, images, source);
|
|
27
|
+
} finally {
|
|
28
|
+
source.release();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Ingest rendering reuses one source handle and its memoized bounded download. */
|
|
32
|
+
async function renderRemoteImageVariant(input, disk, images, source) {
|
|
33
|
+
const variant = Object.prototype.hasOwnProperty.call(images.variants, input.variant) ? images.variants[input.variant] : void 0;
|
|
34
|
+
if (!variant) throw new HttpError(400, `Unknown variant "${input.variant}".`);
|
|
35
|
+
if (input.format !== void 0 && !images.formats.includes(input.format)) throw new HttpError(400, `Format "${input.format}" is not allowed.`);
|
|
36
|
+
const generation = source.metadataGeneration ?? (await source.load()).generation;
|
|
37
|
+
const sourceKey = variantSourceKey(disk.root, source.sourcePath);
|
|
38
|
+
const manifestPath = path.join(variantSourceDirectory(images.cacheDirectory, sourceKey), generation, "source.json");
|
|
39
|
+
let format = input.format;
|
|
40
|
+
if (!format) {
|
|
41
|
+
try {
|
|
42
|
+
const savedFormat = JSON.parse(await fs.readFile(manifestPath, "utf8"))?.format;
|
|
43
|
+
if (isVariantSourceFormat(savedFormat)) format = savedFormat;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (error.code !== "ENOENT" && !(error instanceof SyntaxError)) throw error;
|
|
46
|
+
}
|
|
47
|
+
format ??= (await source.load()).sourceFormat;
|
|
48
|
+
}
|
|
49
|
+
const hash = variantCacheKey({
|
|
50
|
+
relativePath: JSON.stringify([
|
|
51
|
+
disk.root,
|
|
52
|
+
source.sourcePath,
|
|
53
|
+
generation
|
|
54
|
+
]),
|
|
55
|
+
sourceSize: 0,
|
|
56
|
+
sourceMtimeMs: 0,
|
|
57
|
+
variant,
|
|
58
|
+
format
|
|
59
|
+
});
|
|
60
|
+
const target = variantCachePath(images.cacheDirectory, {
|
|
61
|
+
sourceKey,
|
|
62
|
+
generation
|
|
63
|
+
}, hash, format);
|
|
64
|
+
warnIfVariantCacheIsPublic(images.cacheDirectory);
|
|
65
|
+
if (!await freshImageVariant(target, images.maxCacheAge)) await variantGenerations.run(target, async () => {
|
|
66
|
+
if (await freshImageVariant(target, images.maxCacheAge)) return;
|
|
67
|
+
const loaded = await source.load();
|
|
68
|
+
await retryOnVanishedDirectory(async () => {
|
|
69
|
+
await generateImageVariant({
|
|
70
|
+
image: loaded.image.clone(),
|
|
71
|
+
variant,
|
|
72
|
+
format,
|
|
73
|
+
targetPath: target
|
|
74
|
+
});
|
|
75
|
+
await atomicWriteAsync(manifestPath, JSON.stringify({ format: loaded.sourceFormat }));
|
|
76
|
+
});
|
|
77
|
+
scheduleImageVariantCacheTrim(images, target);
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
absolutePath: target,
|
|
81
|
+
hash,
|
|
82
|
+
format,
|
|
83
|
+
contentType: `image/${format}`
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
export { renderRemoteImageVariant, resolveRemoteImageVariant };
|
|
89
|
+
//# sourceMappingURL=resolve-remote-image-variant.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-remote-image-variant.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/resolve-remote-image-variant.ts"],"sourcesContent":["import { atomicWriteAsync } from \"@warlock.js/fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { HttpError } from \"../errors\";\nimport { uploadsConfig } from \"../uploads-config\";\nimport { isVariantSourceFormat } from \"./detect-image-format\";\nimport { generateImageVariant } from \"./generate-image-variant\";\nimport { retryOnVanishedDirectory, warnIfVariantCacheIsPublic } from \"./image-variant-cache\";\nimport type { ResolvedImageVariantsConfig, VariantOutputFormat } from \"./image-variant-types\";\nimport {\n remoteVariantDisk,\n remoteVariantSource,\n type RemoteVariantSource,\n} from \"./remote-variant-source\";\nimport { resolveImageVariantsConfig } from \"./resolve-image-variants-config\";\nimport type { ResolveImageVariantInput, ResolvedImageVariant } from \"./resolve-image-variant\";\nimport { freshImageVariant, scheduleImageVariantCacheTrim } from \"./trim-image-variant-cache\";\nimport {\n variantCacheKey,\n variantCachePath,\n variantSourceDirectory,\n variantSourceKey,\n} from \"./variant-cache-key\";\nimport { variantGenerations } from \"./variant-generations\";\n\n/** Cloud sources are read through their driver; derivatives remain in a local cache. */\nexport async function resolveRemoteImageVariant(\n input: ResolveImageVariantInput,\n): Promise<ResolvedImageVariant | undefined> {\n const disk = remoteVariantDisk(input.disk);\n const images = resolveImageVariantsConfig(uploadsConfig(\"images\"), disk.cacheRoot);\n if (!images) throw new HttpError(400, \"Image variants are not configured.\");\n if (!Object.prototype.hasOwnProperty.call(images.variants, input.variant))\n throw new HttpError(400, `Unknown variant \"${input.variant}\".`);\n if (input.format !== undefined && !images.formats.includes(input.format as never))\n throw new HttpError(400, `Format \"${input.format}\" is not allowed.`);\n const source = await remoteVariantSource(input.path, disk, images);\n if (!source) return undefined;\n try {\n return await renderRemoteImageVariant(input, disk, images, source);\n } finally {\n source.release();\n }\n}\n\n/** Ingest rendering reuses one source handle and its memoized bounded download. */\nexport async function renderRemoteImageVariant(\n input: ResolveImageVariantInput,\n disk: ReturnType<typeof remoteVariantDisk>,\n images: ResolvedImageVariantsConfig,\n source: RemoteVariantSource,\n): Promise<ResolvedImageVariant> {\n const variant = Object.prototype.hasOwnProperty.call(images.variants, input.variant)\n ? images.variants[input.variant]\n : undefined;\n if (!variant) throw new HttpError(400, `Unknown variant \"${input.variant}\".`);\n if (input.format !== undefined && !images.formats.includes(input.format as never))\n throw new HttpError(400, `Format \"${input.format}\" is not allowed.`);\n const generation = source.metadataGeneration ?? (await source.load()).generation;\n const sourceKey = variantSourceKey(disk.root, source.sourcePath);\n const manifestPath = path.join(\n variantSourceDirectory(images.cacheDirectory, sourceKey),\n generation,\n \"source.json\",\n );\n let format: VariantOutputFormat | undefined = input.format;\n if (!format) {\n try {\n const saved: unknown = JSON.parse(await fs.readFile(manifestPath, \"utf8\"));\n const savedFormat = (saved as { format?: unknown } | null)?.format;\n if (isVariantSourceFormat(savedFormat as never)) format = savedFormat as VariantOutputFormat;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\" && !(error instanceof SyntaxError))\n throw error;\n }\n format ??= (await source.load()).sourceFormat;\n }\n const hash = variantCacheKey({\n relativePath: JSON.stringify([disk.root, source.sourcePath, generation]),\n sourceSize: 0,\n sourceMtimeMs: 0,\n variant,\n format,\n });\n const target = variantCachePath(images.cacheDirectory, { sourceKey, generation }, hash, format);\n warnIfVariantCacheIsPublic(images.cacheDirectory);\n if (!(await freshImageVariant(target, images.maxCacheAge))) {\n await variantGenerations.run(target, async () => {\n if (await freshImageVariant(target, images.maxCacheAge)) return;\n const loaded = await source.load();\n await retryOnVanishedDirectory(async () => {\n await generateImageVariant({\n image: loaded.image.clone(),\n variant,\n format,\n targetPath: target,\n });\n await atomicWriteAsync(manifestPath, JSON.stringify({ format: loaded.sourceFormat }));\n });\n scheduleImageVariantCacheTrim(images, target);\n // Retain other generations: an older concurrent download must not prune\n // a newer object's derivatives. Background eviction reclaims them.\n });\n }\n return { absolutePath: target, hash, format, contentType: `image/${format}` };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA0BA,eAAsB,0BACpB,OAC2C;CAC3C,MAAM,OAAO,kBAAkB,MAAM,IAAI;CACzC,MAAM,SAAS,2BAA2B,cAAc,QAAQ,GAAG,KAAK,SAAS;CACjF,IAAI,CAAC,QAAQ,MAAM,IAAI,UAAU,KAAK,oCAAoC;CAC1E,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,OAAO,UAAU,MAAM,OAAO,GACtE,MAAM,IAAI,UAAU,KAAK,oBAAoB,MAAM,QAAQ,GAAG;CAChE,IAAI,MAAM,WAAW,UAAa,CAAC,OAAO,QAAQ,SAAS,MAAM,MAAe,GAC9E,MAAM,IAAI,UAAU,KAAK,WAAW,MAAM,OAAO,kBAAkB;CACrE,MAAM,SAAS,MAAM,oBAAoB,MAAM,MAAM,MAAM,MAAM;CACjE,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI;EACF,OAAO,MAAM,yBAAyB,OAAO,MAAM,QAAQ,MAAM;CACnE,UAAU;EACR,OAAO,QAAQ;CACjB;AACF;;AAGA,eAAsB,yBACpB,OACA,MACA,QACA,QAC+B;CAC/B,MAAM,UAAU,OAAO,UAAU,eAAe,KAAK,OAAO,UAAU,MAAM,OAAO,IAC/E,OAAO,SAAS,MAAM,WACtB;CACJ,IAAI,CAAC,SAAS,MAAM,IAAI,UAAU,KAAK,oBAAoB,MAAM,QAAQ,GAAG;CAC5E,IAAI,MAAM,WAAW,UAAa,CAAC,OAAO,QAAQ,SAAS,MAAM,MAAe,GAC9E,MAAM,IAAI,UAAU,KAAK,WAAW,MAAM,OAAO,kBAAkB;CACrE,MAAM,aAAa,OAAO,uBAAuB,MAAM,OAAO,KAAK,EAAC,CAAE;CACtE,MAAM,YAAY,iBAAiB,KAAK,MAAM,OAAO,UAAU;CAC/D,MAAM,eAAe,KAAK,KACxB,uBAAuB,OAAO,gBAAgB,SAAS,GACvD,YACA,aACF;CACA,IAAI,SAA0C,MAAM;CACpD,IAAI,CAAC,QAAQ;EACX,IAAI;GAEF,MAAM,cADiB,KAAK,MAAM,MAAM,GAAG,SAAS,cAAc,MAAM,CAC/C,CAAC,EAAkC;GAC5D,IAAI,sBAAsB,WAAoB,GAAG,SAAS;EAC5D,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,YAAY,EAAE,iBAAiB,cAC3E,MAAM;EACV;EACA,YAAY,MAAM,OAAO,KAAK,EAAC,CAAE;CACnC;CACA,MAAM,OAAO,gBAAgB;EAC3B,cAAc,KAAK,UAAU;GAAC,KAAK;GAAM,OAAO;GAAY;EAAU,CAAC;EACvE,YAAY;EACZ,eAAe;EACf;EACA;CACF,CAAC;CACD,MAAM,SAAS,iBAAiB,OAAO,gBAAgB;EAAE;EAAW;CAAW,GAAG,MAAM,MAAM;CAC9F,2BAA2B,OAAO,cAAc;CAChD,IAAI,CAAE,MAAM,kBAAkB,QAAQ,OAAO,WAAW,GACtD,MAAM,mBAAmB,IAAI,QAAQ,YAAY;EAC/C,IAAI,MAAM,kBAAkB,QAAQ,OAAO,WAAW,GAAG;EACzD,MAAM,SAAS,MAAM,OAAO,KAAK;EACjC,MAAM,yBAAyB,YAAY;GACzC,MAAM,qBAAqB;IACzB,OAAO,OAAO,MAAM,MAAM;IAC1B;IACA;IACA,YAAY;GACd,CAAC;GACD,MAAM,iBAAiB,cAAc,KAAK,UAAU,EAAE,QAAQ,OAAO,aAAa,CAAC,CAAC;EACtF,CAAC;EACD,8BAA8B,QAAQ,MAAM;CAG9C,CAAC;CAEH,OAAO;EAAE,cAAc;EAAQ;EAAM;EAAQ,aAAa,SAAS;CAAS;AAC9E"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { variantCacheGenerations } from "./image-variant-cache-entries.mjs";
|
|
2
|
+
import { log } from "@warlock.js/logger";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
|
|
5
|
+
//#region ../../packages/core/src/http/uploads/trim-image-variant-cache.ts
|
|
6
|
+
const SWEEP_INTERVAL_MS = 6e4;
|
|
7
|
+
/**
|
|
8
|
+
* A writer's temporary file older than this is an orphan left by a crashed
|
|
9
|
+
* write and is removed by the sweep. Younger ones are never touched: a writer
|
|
10
|
+
* may still be renaming them into place.
|
|
11
|
+
*/
|
|
12
|
+
const ORPHAN_TEMP_AGE_MS = 60 * 6e4;
|
|
13
|
+
/**
|
|
14
|
+
* A generation directory left without derivatives is removed only when it
|
|
15
|
+
* was last changed longer ago than this, so a directory a writer has just
|
|
16
|
+
* created for its first derivative survives. The writers also retry once when
|
|
17
|
+
* their directory vanishes between `mkdir` and the rename.
|
|
18
|
+
*/
|
|
19
|
+
const EMPTY_GENERATION_GRACE_MS = 5 * 6e4;
|
|
20
|
+
const caches = /* @__PURE__ */ new Map();
|
|
21
|
+
function cacheState(directory) {
|
|
22
|
+
let state = caches.get(directory);
|
|
23
|
+
if (!state) {
|
|
24
|
+
state = {
|
|
25
|
+
lastSweep: 0,
|
|
26
|
+
lastWalk: 0,
|
|
27
|
+
bytes: 0,
|
|
28
|
+
sizes: /* @__PURE__ */ new Map(),
|
|
29
|
+
keep: /* @__PURE__ */ new Set(),
|
|
30
|
+
reconciled: false
|
|
31
|
+
};
|
|
32
|
+
caches.set(directory, state);
|
|
33
|
+
}
|
|
34
|
+
return state;
|
|
35
|
+
}
|
|
36
|
+
function errorCode(error) {
|
|
37
|
+
return error?.code;
|
|
38
|
+
}
|
|
39
|
+
/** Age is measured from generation; hits do not extend a derivative's life. */
|
|
40
|
+
async function freshImageVariant(target, maxAgeSeconds) {
|
|
41
|
+
try {
|
|
42
|
+
const stats = await fs.stat(target);
|
|
43
|
+
return stats.isFile() && Date.now() - stats.mtimeMs < maxAgeSeconds * 1e3;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (errorCode(error) === "ENOENT") return false;
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether a scheduled check must walk the cache. It may skip only when a walk
|
|
51
|
+
* has already reconciled the byte estimate, the cache is within its byte
|
|
52
|
+
* budget, nothing retained has expired (new writes are always younger than
|
|
53
|
+
* the oldest retained derivative), and a walk ran within the last
|
|
54
|
+
* `maxCacheAge`, which also bounds how long an orphaned temporary can linger.
|
|
55
|
+
*/
|
|
56
|
+
function sweepIsDue(state, images) {
|
|
57
|
+
const maxAgeMs = images.maxCacheAge * 1e3;
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
return !state.reconciled || state.bytes > images.maxCacheBytes || state.oldestMtimeMs !== void 0 && state.oldestMtimeMs <= now - maxAgeMs || now - state.lastWalk >= maxAgeMs;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Schedule housekeeping only after a write. Hits do no cache-directory I/O.
|
|
63
|
+
* Writes update a running byte estimate; at most one background check per
|
|
64
|
+
* minute walks the cache, and only when it may have something to reclaim
|
|
65
|
+
* (see {@link sweepIsDue}).
|
|
66
|
+
*/
|
|
67
|
+
function scheduleImageVariantCacheTrim(images, target) {
|
|
68
|
+
const state = cacheState(images.cacheDirectory);
|
|
69
|
+
fs.stat(target).then((stats) => {
|
|
70
|
+
state.bytes += stats.size - (state.sizes.get(target) ?? 0);
|
|
71
|
+
state.sizes.set(target, stats.size);
|
|
72
|
+
state.oldestMtimeMs = Math.min(state.oldestMtimeMs ?? stats.mtimeMs, stats.mtimeMs);
|
|
73
|
+
if (state.timer) return;
|
|
74
|
+
const delay = Math.max(0, state.lastSweep + SWEEP_INTERVAL_MS - Date.now());
|
|
75
|
+
state.timer = setTimeout(() => {
|
|
76
|
+
state.timer = void 0;
|
|
77
|
+
if (!sweepIsDue(state, images)) {
|
|
78
|
+
state.lastSweep = Date.now();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
trimImageVariantCache(images, target).catch((error) => {
|
|
82
|
+
log.warn("uploads", "image-variants", error);
|
|
83
|
+
});
|
|
84
|
+
}, delay);
|
|
85
|
+
state.timer.unref();
|
|
86
|
+
}).catch((error) => {
|
|
87
|
+
if (errorCode(error) !== "ENOENT") log.warn("uploads", "image-variants", error);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** Remove a temporary only when it is a regular file older than {@link ORPHAN_TEMP_AGE_MS}. */
|
|
91
|
+
async function removeOrphanTemporary(target) {
|
|
92
|
+
try {
|
|
93
|
+
const stats = await fs.lstat(target);
|
|
94
|
+
if (stats.isFile() && Date.now() - stats.mtimeMs > 36e5) await fs.rm(target, { force: true });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (errorCode(error) !== "ENOENT") throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Remove a generation left without derivatives: its manifest, then the
|
|
101
|
+
* directory itself with a non-recursive `rmdir`, so anything a writer has put
|
|
102
|
+
* there since the walk keeps the directory in place.
|
|
103
|
+
*/
|
|
104
|
+
async function removeEmptyGeneration(generation) {
|
|
105
|
+
if (generation.derivatives > 0) return;
|
|
106
|
+
if (Date.now() - generation.mtimeMs <= 3e5) return;
|
|
107
|
+
if (generation.manifest) await fs.rm(generation.manifest, { force: true });
|
|
108
|
+
try {
|
|
109
|
+
if (!(await fs.lstat(generation.directory)).isDirectory()) return;
|
|
110
|
+
await fs.rmdir(generation.directory);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
const code = errorCode(error);
|
|
113
|
+
if (code !== "ENOENT" && code !== "ENOTEMPTY" && code !== "EEXIST") throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Join an in-flight sweep rather than queueing another walk. Retain the file
|
|
118
|
+
* about to be served, even if it alone exceeds the byte budget. Only paths in
|
|
119
|
+
* the generated cache layout are touched and symlinks are never followed:
|
|
120
|
+
* expired and oldest derivatives are evicted, writer temporaries older than
|
|
121
|
+
* {@link ORPHAN_TEMP_AGE_MS} are removed, and a generation directory left
|
|
122
|
+
* without derivatives loses its manifest and the directory itself once it is
|
|
123
|
+
* older than {@link EMPTY_GENERATION_GRACE_MS}. The cache root, shard and
|
|
124
|
+
* source directories, user folders and unrelated files stay in place.
|
|
125
|
+
*/
|
|
126
|
+
async function trimImageVariantCache(images, keep) {
|
|
127
|
+
const state = cacheState(images.cacheDirectory);
|
|
128
|
+
state.keep.add(keep);
|
|
129
|
+
if (state.sweep) return state.sweep;
|
|
130
|
+
state.lastSweep = Date.now();
|
|
131
|
+
state.lastWalk = state.lastSweep;
|
|
132
|
+
const sweep = (async () => {
|
|
133
|
+
const files = [];
|
|
134
|
+
const generations = [];
|
|
135
|
+
for await (const found of variantCacheGenerations(images.cacheDirectory)) {
|
|
136
|
+
let mtimeMs;
|
|
137
|
+
try {
|
|
138
|
+
mtimeMs = (await fs.lstat(found.directory)).mtimeMs;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (errorCode(error) === "ENOENT") continue;
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
const generation = {
|
|
144
|
+
directory: found.directory,
|
|
145
|
+
mtimeMs,
|
|
146
|
+
derivatives: 0
|
|
147
|
+
};
|
|
148
|
+
generations.push(generation);
|
|
149
|
+
for (const entry of found.entries) if (entry.type === "manifest") generation.manifest = entry.target;
|
|
150
|
+
else if (entry.type === "temporary") await removeOrphanTemporary(entry.target);
|
|
151
|
+
else try {
|
|
152
|
+
const stats = await fs.stat(entry.target);
|
|
153
|
+
files.push({
|
|
154
|
+
target: entry.target,
|
|
155
|
+
size: stats.size,
|
|
156
|
+
mtimeMs: stats.mtimeMs,
|
|
157
|
+
generation
|
|
158
|
+
});
|
|
159
|
+
generation.derivatives += 1;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (errorCode(error) !== "ENOENT") throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
state.sizes = new Map(files.map((file) => [file.target, file.size]));
|
|
165
|
+
state.bytes = files.reduce((sum, file) => sum + file.size, 0);
|
|
166
|
+
const cutoff = Date.now() - images.maxCacheAge * 1e3;
|
|
167
|
+
let oldestMtimeMs;
|
|
168
|
+
for (const file of files.sort((a, b) => a.mtimeMs - b.mtimeMs)) {
|
|
169
|
+
if (!state.keep.has(file.target) && (file.mtimeMs <= cutoff || state.bytes > images.maxCacheBytes)) {
|
|
170
|
+
await fs.rm(file.target, { force: true });
|
|
171
|
+
state.bytes -= file.size;
|
|
172
|
+
state.sizes.delete(file.target);
|
|
173
|
+
file.generation.derivatives -= 1;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
oldestMtimeMs = Math.min(oldestMtimeMs ?? file.mtimeMs, file.mtimeMs);
|
|
177
|
+
}
|
|
178
|
+
for (const generation of generations) await removeEmptyGeneration(generation);
|
|
179
|
+
state.oldestMtimeMs = oldestMtimeMs;
|
|
180
|
+
state.reconciled = true;
|
|
181
|
+
})();
|
|
182
|
+
state.sweep = sweep;
|
|
183
|
+
try {
|
|
184
|
+
await sweep;
|
|
185
|
+
} finally {
|
|
186
|
+
state.sweep = void 0;
|
|
187
|
+
state.keep.clear();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//#endregion
|
|
192
|
+
export { freshImageVariant, scheduleImageVariantCacheTrim };
|
|
193
|
+
//# sourceMappingURL=trim-image-variant-cache.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trim-image-variant-cache.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/trim-image-variant-cache.ts"],"sourcesContent":["import { log } from \"@warlock.js/logger\";\nimport fs from \"node:fs/promises\";\nimport type { ResolvedImageVariantsConfig } from \"./image-variant-types\";\nimport { variantCacheGenerations } from \"./image-variant-cache-entries\";\n\nconst SWEEP_INTERVAL_MS = 60_000;\n\n/**\n * A writer's temporary file older than this is an orphan left by a crashed\n * write and is removed by the sweep. Younger ones are never touched: a writer\n * may still be renaming them into place.\n */\nexport const ORPHAN_TEMP_AGE_MS = 60 * 60_000;\n\n/**\n * A generation directory left without derivatives is removed only when it\n * was last changed longer ago than this, so a directory a writer has just\n * created for its first derivative survives. The writers also retry once when\n * their directory vanishes between `mkdir` and the rename.\n */\nexport const EMPTY_GENERATION_GRACE_MS = 5 * 60_000;\n\ntype CacheState = {\n sweep?: Promise<void>;\n timer?: ReturnType<typeof setTimeout>;\n /** When the last scheduled check ran, whether or not it walked the cache. */\n lastSweep: number;\n /** When the cache was last walked. */\n lastWalk: number;\n bytes: number;\n sizes: Map<string, number>;\n keep: Set<string>;\n /** True once a walk has reconciled `bytes` with the disk. */\n reconciled: boolean;\n /** The oldest retained derivative's mtime; undefined when none is known. */\n oldestMtimeMs?: number;\n};\nconst caches = new Map<string, CacheState>();\nfunction cacheState(directory: string): CacheState {\n let state = caches.get(directory);\n if (!state) {\n state = {\n lastSweep: 0,\n lastWalk: 0,\n bytes: 0,\n sizes: new Map(),\n keep: new Set(),\n reconciled: false,\n };\n caches.set(directory, state);\n }\n return state;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n return (error as NodeJS.ErrnoException | undefined)?.code;\n}\n\n/** Age is measured from generation; hits do not extend a derivative's life. */\nexport async function freshImageVariant(target: string, maxAgeSeconds: number): Promise<boolean> {\n try {\n const stats = await fs.stat(target);\n return stats.isFile() && Date.now() - stats.mtimeMs < maxAgeSeconds * 1000;\n } catch (error) {\n if (errorCode(error) === \"ENOENT\") return false;\n throw error;\n }\n}\n\n/**\n * Whether a scheduled check must walk the cache. It may skip only when a walk\n * has already reconciled the byte estimate, the cache is within its byte\n * budget, nothing retained has expired (new writes are always younger than\n * the oldest retained derivative), and a walk ran within the last\n * `maxCacheAge`, which also bounds how long an orphaned temporary can linger.\n */\nfunction sweepIsDue(state: CacheState, images: ResolvedImageVariantsConfig): boolean {\n const maxAgeMs = images.maxCacheAge * 1000;\n const now = Date.now();\n return (\n !state.reconciled ||\n state.bytes > images.maxCacheBytes ||\n (state.oldestMtimeMs !== undefined && state.oldestMtimeMs <= now - maxAgeMs) ||\n now - state.lastWalk >= maxAgeMs\n );\n}\n\n/**\n * Schedule housekeeping only after a write. Hits do no cache-directory I/O.\n * Writes update a running byte estimate; at most one background check per\n * minute walks the cache, and only when it may have something to reclaim\n * (see {@link sweepIsDue}).\n */\nexport function scheduleImageVariantCacheTrim(\n images: ResolvedImageVariantsConfig,\n target: string,\n): void {\n const state = cacheState(images.cacheDirectory);\n void fs\n .stat(target)\n .then((stats) => {\n state.bytes += stats.size - (state.sizes.get(target) ?? 0);\n state.sizes.set(target, stats.size);\n state.oldestMtimeMs = Math.min(state.oldestMtimeMs ?? stats.mtimeMs, stats.mtimeMs);\n if (state.timer) return;\n const delay = Math.max(0, state.lastSweep + SWEEP_INTERVAL_MS - Date.now());\n state.timer = setTimeout(() => {\n state.timer = undefined;\n if (!sweepIsDue(state, images)) {\n state.lastSweep = Date.now();\n return;\n }\n void trimImageVariantCache(images, target).catch((error: unknown) => {\n log.warn(\"uploads\", \"image-variants\", error);\n });\n }, delay);\n state.timer.unref();\n })\n .catch((error: unknown) => {\n if (errorCode(error) !== \"ENOENT\") log.warn(\"uploads\", \"image-variants\", error);\n });\n}\n\ntype Derivative = { target: string; size: number; mtimeMs: number; generation: Generation };\ntype Generation = {\n directory: string;\n /** The directory's mtime before this sweep removed anything from it. */\n mtimeMs: number;\n derivatives: number;\n manifest?: string;\n};\n\n/** Remove a temporary only when it is a regular file older than {@link ORPHAN_TEMP_AGE_MS}. */\nasync function removeOrphanTemporary(target: string): Promise<void> {\n try {\n const stats = await fs.lstat(target);\n if (stats.isFile() && Date.now() - stats.mtimeMs > ORPHAN_TEMP_AGE_MS) {\n await fs.rm(target, { force: true });\n }\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\") throw error;\n }\n}\n\n/**\n * Remove a generation left without derivatives: its manifest, then the\n * directory itself with a non-recursive `rmdir`, so anything a writer has put\n * there since the walk keeps the directory in place.\n */\nasync function removeEmptyGeneration(generation: Generation): Promise<void> {\n if (generation.derivatives > 0) return;\n if (Date.now() - generation.mtimeMs <= EMPTY_GENERATION_GRACE_MS) return;\n if (generation.manifest) await fs.rm(generation.manifest, { force: true });\n try {\n if (!(await fs.lstat(generation.directory)).isDirectory()) return;\n await fs.rmdir(generation.directory);\n } catch (error) {\n const code = errorCode(error);\n if (code !== \"ENOENT\" && code !== \"ENOTEMPTY\" && code !== \"EEXIST\") throw error;\n }\n}\n\n/**\n * Join an in-flight sweep rather than queueing another walk. Retain the file\n * about to be served, even if it alone exceeds the byte budget. Only paths in\n * the generated cache layout are touched and symlinks are never followed:\n * expired and oldest derivatives are evicted, writer temporaries older than\n * {@link ORPHAN_TEMP_AGE_MS} are removed, and a generation directory left\n * without derivatives loses its manifest and the directory itself once it is\n * older than {@link EMPTY_GENERATION_GRACE_MS}. The cache root, shard and\n * source directories, user folders and unrelated files stay in place.\n */\nexport async function trimImageVariantCache(\n images: ResolvedImageVariantsConfig,\n keep: string,\n): Promise<void> {\n const state = cacheState(images.cacheDirectory);\n state.keep.add(keep);\n if (state.sweep) return state.sweep;\n state.lastSweep = Date.now();\n state.lastWalk = state.lastSweep;\n const sweep = (async () => {\n const files: Derivative[] = [];\n const generations: Generation[] = [];\n for await (const found of variantCacheGenerations(images.cacheDirectory)) {\n let mtimeMs: number;\n try {\n mtimeMs = (await fs.lstat(found.directory)).mtimeMs;\n } catch (error) {\n if (errorCode(error) === \"ENOENT\") continue;\n throw error;\n }\n const generation: Generation = { directory: found.directory, mtimeMs, derivatives: 0 };\n generations.push(generation);\n for (const entry of found.entries) {\n if (entry.type === \"manifest\") {\n generation.manifest = entry.target;\n } else if (entry.type === \"temporary\") {\n await removeOrphanTemporary(entry.target);\n } else {\n try {\n const stats = await fs.stat(entry.target);\n files.push({\n target: entry.target,\n size: stats.size,\n mtimeMs: stats.mtimeMs,\n generation,\n });\n generation.derivatives += 1;\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\") throw error;\n }\n }\n }\n }\n state.sizes = new Map(files.map((file) => [file.target, file.size]));\n state.bytes = files.reduce((sum, file) => sum + file.size, 0);\n const cutoff = Date.now() - images.maxCacheAge * 1000;\n let oldestMtimeMs: number | undefined;\n for (const file of files.sort((a, b) => a.mtimeMs - b.mtimeMs)) {\n if (\n !state.keep.has(file.target) &&\n (file.mtimeMs <= cutoff || state.bytes > images.maxCacheBytes)\n ) {\n await fs.rm(file.target, { force: true });\n state.bytes -= file.size;\n state.sizes.delete(file.target);\n file.generation.derivatives -= 1;\n continue;\n }\n oldestMtimeMs = Math.min(oldestMtimeMs ?? file.mtimeMs, file.mtimeMs);\n }\n for (const generation of generations) await removeEmptyGeneration(generation);\n state.oldestMtimeMs = oldestMtimeMs;\n state.reconciled = true;\n })();\n state.sweep = sweep;\n try {\n await sweep;\n } finally {\n state.sweep = undefined;\n state.keep.clear();\n }\n}\n"],"mappings":"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"}
|
|
@@ -6,13 +6,15 @@ import { RequestHandler } from "../../router/types.mjs";
|
|
|
6
6
|
*/
|
|
7
7
|
type UploadedFileControllerOptions = {
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Disk to serve from; remote disks support variants only. Uses the default
|
|
10
|
+
* disk when omitted. A private disk
|
|
10
11
|
* should be served from a route that checks access first.
|
|
11
12
|
*/
|
|
12
|
-
disk?: StorageDriverName;
|
|
13
|
+
disk?: StorageDriverName; /** Allow derivatives from the default cloud disk. Explicit disk selection also opts in. */
|
|
14
|
+
remote?: boolean;
|
|
13
15
|
};
|
|
14
16
|
/**
|
|
15
|
-
* Serves local uploads
|
|
17
|
+
* Serves local uploads and bounded on-demand image variants from any disk.
|
|
16
18
|
*
|
|
17
19
|
* ```ts
|
|
18
20
|
* router.get("/uploads/*", uploadedFileController);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploaded-file.controller.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/uploaded-file.controller.ts"],"mappings":";;;;;;KA6FY,6BAAA;EAA6B
|
|
1
|
+
{"version":3,"file":"uploaded-file.controller.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/uploaded-file.controller.ts"],"mappings":";;;;;;KA6FY,6BAAA;EAA6B;;;;;EAMvC,IAAA,GAAO,iBAAiB,EAGlB;EAAN,MAAA;AAAA;;;;;;;;AA0Be;AA+HjB;;;;AAAoF;;;;;;;;;iBAjIpE,4BAAA,CACd,OAAA,GAAS,6BAAA,GACR,cAAc;;;;cA+HJ,sBAAA,EAAwB,cAA+C"}
|