@warlock.js/core 5.28.0 → 5.30.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 +71 -0
- package/esm/application/app.d.mts +1 -1
- package/esm/application/app.d.mts.map +1 -1
- package/esm/application/app.mjs +1 -1
- package/esm/application/app.mjs.map +1 -1
- package/esm/application/application-config-types.d.mts +1 -1
- package/esm/application/application-config-types.d.mts.map +1 -1
- package/esm/application/application.d.mts +1 -1
- package/esm/application/application.d.mts.map +1 -1
- package/esm/application/application.mjs +1 -1
- package/esm/application/application.mjs.map +1 -1
- package/esm/application/boot-signal.d.mts +1 -1
- package/esm/application/boot-signal.d.mts.map +1 -1
- package/esm/application/boot-signal.mjs +1 -1
- package/esm/application/boot-signal.mjs.map +1 -1
- package/esm/application/public-url.d.mts +1 -1
- package/esm/application/public-url.d.mts.map +1 -1
- package/esm/application/public-url.mjs +1 -1
- package/esm/application/public-url.mjs.map +1 -1
- package/esm/application/roles.d.mts +1 -1
- package/esm/application/roles.d.mts.map +1 -1
- package/esm/application/roles.mjs +1 -1
- package/esm/application/roles.mjs.map +1 -1
- package/esm/benchmark/benchmark-snapshots.d.mts +1 -1
- package/esm/benchmark/benchmark-snapshots.d.mts.map +1 -1
- package/esm/benchmark/benchmark-snapshots.mjs +1 -1
- package/esm/benchmark/benchmark-snapshots.mjs.map +1 -1
- package/esm/benchmark/benchmark.d.mts +1 -1
- package/esm/benchmark/benchmark.d.mts.map +1 -1
- package/esm/benchmark/benchmark.mjs +1 -1
- package/esm/benchmark/benchmark.mjs.map +1 -1
- package/esm/benchmark/channels/console.channel.d.mts +1 -1
- package/esm/benchmark/channels/console.channel.d.mts.map +1 -1
- package/esm/benchmark/channels/console.channel.mjs +1 -1
- package/esm/benchmark/channels/console.channel.mjs.map +1 -1
- package/esm/benchmark/channels/noop.channel.d.mts +1 -1
- package/esm/benchmark/channels/noop.channel.d.mts.map +1 -1
- package/esm/benchmark/channels/noop.channel.mjs +1 -1
- package/esm/benchmark/channels/noop.channel.mjs.map +1 -1
- package/esm/benchmark/profiler.d.mts +1 -1
- package/esm/benchmark/profiler.d.mts.map +1 -1
- package/esm/benchmark/profiler.mjs +1 -1
- package/esm/benchmark/profiler.mjs.map +1 -1
- package/esm/benchmark/types.d.mts +1 -1
- package/esm/benchmark/types.d.mts.map +1 -1
- package/esm/bootstrap/setup.d.mts +1 -1
- package/esm/bootstrap/setup.d.mts.map +1 -1
- package/esm/bootstrap/setup.mjs +1 -1
- package/esm/bootstrap/setup.mjs.map +1 -1
- package/esm/bootstrap.d.mts +1 -1
- package/esm/bootstrap.d.mts.map +1 -1
- package/esm/bootstrap.mjs +1 -1
- package/esm/bootstrap.mjs.map +1 -1
- package/esm/cache/database-cache-driver.d.mts +1 -1
- package/esm/cache/database-cache-driver.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.mjs +1 -1
- package/esm/cache/database-cache-driver.mjs.map +1 -1
- package/esm/cache/index.mjs +1 -1
- package/esm/cache/index.mjs.map +1 -1
- package/esm/cli/cli-commands.manager.mjs +1 -1
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/cli-commands.utils.mjs +1 -1
- package/esm/cli/cli-commands.utils.mjs.map +1 -1
- package/esm/cli/commands/add.command.mjs +1 -1
- package/esm/cli/commands/add.command.mjs.map +1 -1
- package/esm/cli/commands/build.command.mjs +1 -1
- package/esm/cli/commands/build.command.mjs.map +1 -1
- package/esm/cli/commands/create-database.command.mjs +1 -1
- package/esm/cli/commands/create-database.command.mjs.map +1 -1
- package/esm/cli/commands/dev-server.command.mjs +1 -1
- package/esm/cli/commands/dev-server.command.mjs.map +1 -1
- package/esm/cli/commands/doctor/boot-for-diagnostics.mjs +1 -1
- package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/config.check.mjs +1 -1
- package/esm/cli/commands/doctor/checks/config.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/connectors.check.mjs +1 -1
- package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/health.check.mjs +1 -1
- package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/index.mjs +1 -1
- package/esm/cli/commands/doctor/checks/index.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/jwt-secret.check.mjs +1 -1
- 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 -1
- 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 -1
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs +1 -1
- package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/routes.check.mjs +1 -1
- package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/doctor-command.action.mjs +1 -1
- package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
- package/esm/cli/commands/doctor/format-report.mjs +1 -1
- package/esm/cli/commands/doctor/format-report.mjs.map +1 -1
- package/esm/cli/commands/doctor/run-checks.mjs +1 -1
- package/esm/cli/commands/doctor/run-checks.mjs.map +1 -1
- package/esm/cli/commands/doctor.command.mjs +1 -1
- package/esm/cli/commands/doctor.command.mjs.map +1 -1
- package/esm/cli/commands/drop-tables.command.mjs +1 -1
- package/esm/cli/commands/drop-tables.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs +1 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/controller.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
- 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/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/repository.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/resource.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/service.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/use-case.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/use-case.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/templates/stubs.mjs +1 -1
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
- 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/path-resolver.mjs +1 -1
- package/esm/cli/commands/generate/utils/path-resolver.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/prompt.mjs +1 -1
- package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/writer.mjs +1 -1
- package/esm/cli/commands/generate/utils/writer.mjs.map +1 -1
- package/esm/cli/commands/migrate.command.mjs +1 -1
- package/esm/cli/commands/migrate.command.mjs.map +1 -1
- package/esm/cli/commands/openapi-generator.action.mjs +1 -1
- package/esm/cli/commands/openapi-generator.action.mjs.map +1 -1
- package/esm/cli/commands/openapi-generator.command.mjs +1 -1
- package/esm/cli/commands/openapi-generator.command.mjs.map +1 -1
- package/esm/cli/commands/postman-generator.action.mjs +1 -1
- package/esm/cli/commands/postman-generator.action.mjs.map +1 -1
- package/esm/cli/commands/postman-generator.command.mjs +1 -1
- package/esm/cli/commands/postman-generator.command.mjs.map +1 -1
- package/esm/cli/commands/routes/diff-page-routes.mjs +1 -1
- package/esm/cli/commands/routes/diff-page-routes.mjs.map +1 -1
- package/esm/cli/commands/routes/format-routes-table.mjs +1 -1
- package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
- package/esm/cli/commands/routes/route-row.mjs +1 -1
- package/esm/cli/commands/routes/route-row.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-diff.action.mjs +1 -1
- package/esm/cli/commands/routes/routes-diff.action.mjs.map +1 -1
- package/esm/cli/commands/routes/with-console-on-stderr.mjs +1 -1
- package/esm/cli/commands/routes/with-console-on-stderr.mjs.map +1 -1
- package/esm/cli/commands/routes.command.mjs +1 -1
- package/esm/cli/commands/routes.command.mjs.map +1 -1
- package/esm/cli/commands/seed.command.mjs +1 -1
- package/esm/cli/commands/seed.command.mjs.map +1 -1
- package/esm/cli/commands/start-production.command.mjs +1 -1
- package/esm/cli/commands/start-production.command.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs +1 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/storage-put.command.mjs +1 -1
- package/esm/cli/commands/storage-put.command.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs +1 -1
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/cli/commands/update.command.mjs +1 -1
- package/esm/cli/commands/update.command.mjs.map +1 -1
- package/esm/cli/commands-loader.mjs +1 -1
- package/esm/cli/commands-loader.mjs.map +1 -1
- package/esm/cli/framework-cli-commands.mjs +1 -1
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/cli/parse-cli-args.mjs +1 -1
- package/esm/cli/parse-cli-args.mjs.map +1 -1
- package/esm/cli/start.mjs +1 -1
- package/esm/cli/start.mjs.map +1 -1
- package/esm/cli/string-similarity.mjs +1 -1
- package/esm/cli/string-similarity.mjs.map +1 -1
- package/esm/commands/cli-command.d.mts +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -1
- package/esm/commands/cli-command.mjs +1 -1
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/commands/types.d.mts +1 -1
- package/esm/commands/types.d.mts.map +1 -1
- package/esm/config/config-getter.d.mts +1 -1
- package/esm/config/config-getter.d.mts.map +1 -1
- package/esm/config/config-getter.mjs +1 -1
- package/esm/config/config-getter.mjs.map +1 -1
- package/esm/config/config-handlers.d.mts +1 -1
- package/esm/config/config-handlers.d.mts.map +1 -1
- package/esm/config/config-handlers.mjs +1 -1
- package/esm/config/config-handlers.mjs.map +1 -1
- package/esm/config/config-key-from-path.mjs +1 -1
- package/esm/config/config-key-from-path.mjs.map +1 -1
- package/esm/config/config-loader.d.mts +1 -1
- package/esm/config/config-loader.d.mts.map +1 -1
- package/esm/config/config-loader.mjs +1 -1
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/config/config-manager.mjs +1 -1
- package/esm/config/config-manager.mjs.map +1 -1
- package/esm/config/config-setter.d.mts +1 -1
- package/esm/config/config-setter.d.mts.map +1 -1
- package/esm/config/config-setter.mjs +1 -1
- package/esm/config/config-setter.mjs.map +1 -1
- package/esm/config/config-special-handlers.d.mts +1 -1
- package/esm/config/config-special-handlers.d.mts.map +1 -1
- package/esm/config/config-special-handlers.mjs +1 -1
- package/esm/config/config-special-handlers.mjs.map +1 -1
- package/esm/config/load-config-files.mjs +1 -1
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/config/locale-configuration.mjs +1 -1
- package/esm/config/locale-configuration.mjs.map +1 -1
- package/esm/config/types.d.mts +1 -1
- package/esm/config/types.d.mts.map +1 -1
- package/esm/connectors/access-connector.d.mts +1 -1
- package/esm/connectors/access-connector.d.mts.map +1 -1
- package/esm/connectors/access-connector.mjs +1 -1
- package/esm/connectors/access-connector.mjs.map +1 -1
- package/esm/connectors/ai-connector.d.mts +1 -1
- package/esm/connectors/ai-connector.d.mts.map +1 -1
- package/esm/connectors/ai-connector.mjs +1 -1
- package/esm/connectors/ai-connector.mjs.map +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.d.mts +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.d.mts.map +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.mjs +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
- package/esm/connectors/assert-unique-connector-names.d.mts +1 -1
- package/esm/connectors/assert-unique-connector-names.d.mts.map +1 -1
- package/esm/connectors/assert-unique-connector-names.mjs +1 -1
- package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
- package/esm/connectors/base-connector.d.mts +1 -1
- package/esm/connectors/base-connector.d.mts.map +1 -1
- package/esm/connectors/base-connector.mjs +1 -1
- package/esm/connectors/base-connector.mjs.map +1 -1
- package/esm/connectors/cache-connector.d.mts +1 -1
- package/esm/connectors/cache-connector.d.mts.map +1 -1
- package/esm/connectors/cache-connector.mjs +1 -1
- package/esm/connectors/cache-connector.mjs.map +1 -1
- package/esm/connectors/connectors-manager.d.mts +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +2 -2
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/database-connector.d.mts +1 -1
- package/esm/connectors/database-connector.d.mts.map +1 -1
- package/esm/connectors/database-connector.mjs +1 -1
- package/esm/connectors/database-connector.mjs.map +1 -1
- package/esm/connectors/describe-server-address.mjs +1 -1
- package/esm/connectors/describe-server-address.mjs.map +1 -1
- package/esm/connectors/herald-connector.d.mts +1 -1
- package/esm/connectors/herald-connector.d.mts.map +1 -1
- package/esm/connectors/herald-connector.mjs +1 -1
- package/esm/connectors/herald-connector.mjs.map +1 -1
- package/esm/connectors/http-connector.d.mts +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +1 -1
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/logger-connector.d.mts +1 -1
- package/esm/connectors/logger-connector.d.mts.map +1 -1
- package/esm/connectors/logger-connector.mjs +1 -1
- package/esm/connectors/logger-connector.mjs.map +1 -1
- package/esm/connectors/mail-connector.d.mts +1 -1
- package/esm/connectors/mail-connector.d.mts.map +1 -1
- package/esm/connectors/mail-connector.mjs +1 -1
- package/esm/connectors/mail-connector.mjs.map +1 -1
- package/esm/connectors/notifications-connector.d.mts +1 -1
- package/esm/connectors/notifications-connector.d.mts.map +1 -1
- package/esm/connectors/notifications-connector.mjs +1 -1
- package/esm/connectors/notifications-connector.mjs.map +1 -1
- package/esm/connectors/read-bound-port.mjs +1 -1
- package/esm/connectors/read-bound-port.mjs.map +1 -1
- package/esm/connectors/register-configured-connectors.d.mts +1 -1
- package/esm/connectors/register-configured-connectors.d.mts.map +1 -1
- package/esm/connectors/register-configured-connectors.mjs +1 -1
- package/esm/connectors/register-configured-connectors.mjs.map +1 -1
- package/esm/connectors/single-server-warnings.mjs +1 -1
- package/esm/connectors/single-server-warnings.mjs.map +1 -1
- package/esm/connectors/socket-connector.d.mts +1 -1
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs +1 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/storage.connector.d.mts +1 -1
- package/esm/connectors/storage.connector.d.mts.map +1 -1
- package/esm/connectors/storage.connector.mjs +1 -1
- package/esm/connectors/storage.connector.mjs.map +1 -1
- package/esm/connectors/types.d.mts +1 -1
- package/esm/connectors/types.d.mts.map +1 -1
- package/esm/connectors/types.mjs +1 -1
- package/esm/connectors/types.mjs.map +1 -1
- package/esm/container/container-instance-registry.mjs +1 -1
- package/esm/container/container-instance-registry.mjs.map +1 -1
- package/esm/container/development-app-modules.d.mts +31 -0
- package/esm/container/development-app-modules.d.mts.map +1 -0
- package/esm/container/development-model-modules.d.mts +1 -1
- package/esm/container/development-model-modules.d.mts.map +1 -1
- package/esm/container/index.d.mts +3 -1
- package/esm/container/index.d.mts.map +1 -1
- package/esm/container/index.mjs +1 -1
- package/esm/container/index.mjs.map +1 -1
- package/esm/database/create-database-action.mjs +1 -1
- package/esm/database/create-database-action.mjs.map +1 -1
- package/esm/database/drop-tables-action.mjs +1 -1
- package/esm/database/drop-tables-action.mjs.map +1 -1
- package/esm/database/migrate-action.mjs +1 -1
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/models/database-log/database-log.d.mts +31 -39
- package/esm/database/models/database-log/database-log.d.mts.map +1 -1
- package/esm/database/models/database-log/database-log.mjs +1 -1
- package/esm/database/models/database-log/database-log.mjs.map +1 -1
- package/esm/database/pending-exit-code.mjs +1 -1
- package/esm/database/pending-exit-code.mjs.map +1 -1
- package/esm/database/resolve-pending-migrations.mjs +1 -1
- package/esm/database/resolve-pending-migrations.mjs.map +1 -1
- package/esm/database/seed-command-action.mjs +1 -1
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/database/seeds/seed-records-table-migration.mjs +1 -1
- package/esm/database/seeds/seed-records-table-migration.mjs.map +1 -1
- package/esm/database/seeds/seeder.d.mts +1 -1
- package/esm/database/seeds/seeder.d.mts.map +1 -1
- package/esm/database/seeds/seeder.errors.d.mts +1 -1
- package/esm/database/seeds/seeder.errors.d.mts.map +1 -1
- package/esm/database/seeds/seeder.errors.mjs +1 -1
- package/esm/database/seeds/seeder.errors.mjs.map +1 -1
- package/esm/database/seeds/seeder.mjs +1 -1
- package/esm/database/seeds/seeder.mjs.map +1 -1
- package/esm/database/seeds/seeders.manager.mjs +1 -1
- package/esm/database/seeds/seeders.manager.mjs.map +1 -1
- package/esm/database/seeds/seeds-table-migration.mjs +1 -1
- package/esm/database/seeds/seeds-table-migration.mjs.map +1 -1
- package/esm/database/seeds/types.d.mts +1 -1
- package/esm/database/seeds/types.d.mts.map +1 -1
- package/esm/database/seeds/utils.mjs +1 -1
- package/esm/database/seeds/utils.mjs.map +1 -1
- package/esm/database/utils.d.mts +1 -1
- package/esm/database/utils.d.mts.map +1 -1
- package/esm/database/utils.mjs +1 -1
- package/esm/database/utils.mjs.map +1 -1
- package/esm/dev-server/app-module-registry.mjs +52 -0
- package/esm/dev-server/app-module-registry.mjs.map +1 -0
- package/esm/dev-server/boot-precondition-error.mjs +1 -1
- package/esm/dev-server/boot-precondition-error.mjs.map +1 -1
- package/esm/dev-server/build-development-openapi-document.mjs +1 -1
- package/esm/dev-server/build-development-openapi-document.mjs.map +1 -1
- package/esm/dev-server/check-for-updates.mjs +1 -1
- package/esm/dev-server/check-for-updates.mjs.map +1 -1
- package/esm/dev-server/collect-development-api-routes.mjs +1 -1
- package/esm/dev-server/collect-development-api-routes.mjs.map +1 -1
- package/esm/dev-server/create-worker.mjs +1 -1
- package/esm/dev-server/create-worker.mjs.map +1 -1
- package/esm/dev-server/dependency-graph.d.mts +1 -1
- package/esm/dev-server/dependency-graph.d.mts.map +1 -1
- package/esm/dev-server/dependency-graph.mjs +1 -1
- package/esm/dev-server/dependency-graph.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs +1 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +1 -1
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/events.mjs +1 -1
- package/esm/dev-server/events.mjs.map +1 -1
- package/esm/dev-server/extract-translation-keys.mjs +1 -1
- package/esm/dev-server/extract-translation-keys.mjs.map +1 -1
- package/esm/dev-server/file-event-handler.mjs +1 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.d.mts +1 -1
- package/esm/dev-server/file-manager.d.mts.map +1 -1
- package/esm/dev-server/file-manager.mjs +1 -1
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/file-operations.d.mts +1 -1
- package/esm/dev-server/file-operations.d.mts.map +1 -1
- package/esm/dev-server/file-operations.mjs +1 -1
- package/esm/dev-server/file-operations.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +28 -14
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +1 -1
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/flags.mjs +1 -1
- package/esm/dev-server/flags.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/base-health-checker.mjs +1 -1
- package/esm/dev-server/health-checker/checkers/base-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs +1 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs +1 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +1 -1
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.d.mts +1 -1
- package/esm/dev-server/health-checker/file-health-result.d.mts.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.mjs +1 -1
- package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +1 -1
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +1 -1
- package/esm/dev-server/health-checker/workers/ts-health.worker.mjs +1 -1
- package/esm/dev-server/health-checker/workers/ts-health.worker.mjs.map +1 -1
- package/esm/dev-server/layer-executor.mjs +1 -1
- package/esm/dev-server/layer-executor.mjs.map +1 -1
- package/esm/dev-server/legacy-locales-warning.mjs +1 -1
- package/esm/dev-server/legacy-locales-warning.mjs.map +1 -1
- package/esm/dev-server/loader/build-transpile-init.mjs +1 -1
- package/esm/dev-server/loader/build-transpile-init.mjs.map +1 -1
- package/esm/dev-server/loader/hook-thread.d.mts +1 -1
- package/esm/dev-server/loader/hook-thread.d.mts.map +1 -1
- package/esm/dev-server/loader/hook-thread.mjs +1 -1
- package/esm/dev-server/loader/hook-thread.mjs.map +1 -1
- package/esm/dev-server/loader/load-hook.d.mts +1 -1
- package/esm/dev-server/loader/load-hook.d.mts.map +1 -1
- package/esm/dev-server/loader/load-hook.mjs +1 -1
- package/esm/dev-server/loader/load-hook.mjs.map +1 -1
- package/esm/dev-server/loader/loader-port-acks.mjs +34 -0
- package/esm/dev-server/loader/loader-port-acks.mjs.map +1 -0
- package/esm/dev-server/loader/own-resolver.mjs +1 -1
- package/esm/dev-server/loader/own-resolver.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-capture.mjs +1 -1
- package/esm/dev-server/loader/resolve-capture.mjs.map +1 -1
- package/esm/dev-server/loader/resolve-hook.d.mts +1 -1
- package/esm/dev-server/loader/resolve-hook.d.mts.map +1 -1
- package/esm/dev-server/loader/resolve-hook.mjs +2 -2
- package/esm/dev-server/loader/resolve-hook.mjs.map +1 -1
- package/esm/dev-server/loader/source-slug.mjs +1 -1
- package/esm/dev-server/loader/source-slug.mjs.map +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
- package/esm/dev-server/loader/version-registry.mjs +1 -1
- package/esm/dev-server/loader/version-registry.mjs.map +1 -1
- package/esm/dev-server/manifest-manager.d.mts +1 -1
- package/esm/dev-server/manifest-manager.d.mts.map +1 -1
- package/esm/dev-server/manifest-manager.mjs +1 -1
- package/esm/dev-server/manifest-manager.mjs.map +1 -1
- package/esm/dev-server/model-module-registry.mjs +1 -1
- package/esm/dev-server/model-module-registry.mjs.map +1 -1
- package/esm/dev-server/model-resource-typings.mjs +1 -1
- package/esm/dev-server/model-resource-typings.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs +1 -1
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/package-json-manager.mjs +1 -1
- package/esm/dev-server/package-json-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +73 -6
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/read-config-ast.mjs +1 -1
- package/esm/dev-server/read-config-ast.mjs.map +1 -1
- package/esm/dev-server/ready-block.mjs +1 -1
- package/esm/dev-server/ready-block.mjs.map +1 -1
- package/esm/dev-server/register-dev-shortcuts.mjs +1 -1
- package/esm/dev-server/register-dev-shortcuts.mjs.map +1 -1
- package/esm/dev-server/register-installed-devtools.mjs +1 -1
- package/esm/dev-server/register-installed-devtools.mjs.map +1 -1
- package/esm/dev-server/restart-dev-server.mjs +1 -1
- package/esm/dev-server/restart-dev-server.mjs.map +1 -1
- package/esm/dev-server/route-types-publisher.mjs +1 -1
- package/esm/dev-server/route-types-publisher.mjs.map +1 -1
- package/esm/dev-server/run-connector-typings.mjs +1 -1
- package/esm/dev-server/run-connector-typings.mjs.map +1 -1
- package/esm/dev-server/run-typings-generation.mjs +1 -1
- package/esm/dev-server/run-typings-generation.mjs.map +1 -1
- package/esm/dev-server/shortcuts.mjs +1 -1
- package/esm/dev-server/shortcuts.mjs.map +1 -1
- package/esm/dev-server/special-file-patterns.mjs +1 -1
- package/esm/dev-server/special-file-patterns.mjs.map +1 -1
- package/esm/dev-server/special-files-collector.d.mts +1 -1
- package/esm/dev-server/special-files-collector.d.mts.map +1 -1
- package/esm/dev-server/special-files-collector.mjs +1 -1
- package/esm/dev-server/special-files-collector.mjs.map +1 -1
- package/esm/dev-server/start-development-server.mjs +1 -1
- package/esm/dev-server/start-development-server.mjs.map +1 -1
- package/esm/dev-server/supervisor.mjs +1 -1
- package/esm/dev-server/supervisor.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 +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs +32 -11
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/types.d.mts +1 -1
- package/esm/dev-server/types.d.mts.map +1 -1
- package/esm/dev-server/update-check-cache.mjs +1 -1
- package/esm/dev-server/update-check-cache.mjs.map +1 -1
- package/esm/dev-server/utils.mjs +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/encryption/encrypt.d.mts +1 -1
- package/esm/encryption/encrypt.d.mts.map +1 -1
- package/esm/encryption/encrypt.mjs +1 -1
- package/esm/encryption/encrypt.mjs.map +1 -1
- package/esm/encryption/hash.d.mts +1 -1
- package/esm/encryption/hash.d.mts.map +1 -1
- package/esm/encryption/hash.mjs +1 -1
- package/esm/encryption/hash.mjs.map +1 -1
- package/esm/encryption/password.d.mts +1 -1
- package/esm/encryption/password.d.mts.map +1 -1
- package/esm/encryption/password.mjs +1 -1
- package/esm/encryption/password.mjs.map +1 -1
- package/esm/encryption/types.d.mts +1 -1
- package/esm/encryption/types.d.mts.map +1 -1
- package/esm/errors/container-key-missing-error.mjs +1 -1
- package/esm/errors/container-key-missing-error.mjs.map +1 -1
- package/esm/errors/esbuild-binary-missing-error.mjs +1 -1
- package/esm/errors/esbuild-binary-missing-error.mjs.map +1 -1
- package/esm/errors/index.d.mts +2 -0
- package/esm/errors/index.mjs +3 -0
- package/esm/errors/unknown-locale-error.mjs +1 -1
- package/esm/errors/unknown-locale-error.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +29 -6
- 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/ai-anthropic.feature.mjs +1 -1
- package/esm/generations/features/ai-anthropic.feature.mjs.map +1 -1
- package/esm/generations/features/ai-bedrock.feature.mjs +1 -1
- package/esm/generations/features/ai-bedrock.feature.mjs.map +1 -1
- package/esm/generations/features/ai-google.feature.mjs +1 -1
- package/esm/generations/features/ai-google.feature.mjs.map +1 -1
- package/esm/generations/features/ai-ollama.feature.mjs +1 -1
- package/esm/generations/features/ai-ollama.feature.mjs.map +1 -1
- package/esm/generations/features/ai-openai.feature.mjs +1 -1
- package/esm/generations/features/ai-openai.feature.mjs.map +1 -1
- package/esm/generations/features/ai-panoptic.feature.mjs +1 -1
- package/esm/generations/features/ai-panoptic.feature.mjs.map +1 -1
- package/esm/generations/features/ai-tools.feature.mjs +1 -1
- package/esm/generations/features/ai-tools.feature.mjs.map +1 -1
- package/esm/generations/features/ai-workspace.feature.mjs +1 -1
- package/esm/generations/features/ai-workspace.feature.mjs.map +1 -1
- package/esm/generations/features/ai.feature.mjs +1 -1
- package/esm/generations/features/ai.feature.mjs.map +1 -1
- package/esm/generations/features/auth-google.feature.mjs +1 -1
- package/esm/generations/features/auth-google.feature.mjs.map +1 -1
- package/esm/generations/features/auth-passkeys.feature.mjs +1 -1
- package/esm/generations/features/auth-passkeys.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/devtools.feature.mjs +1 -1
- package/esm/generations/features/devtools.feature.mjs.map +1 -1
- package/esm/generations/features/herald.feature.mjs +1 -1
- package/esm/generations/features/herald.feature.mjs.map +1 -1
- package/esm/generations/features/image.feature.mjs +1 -1
- package/esm/generations/features/image.feature.mjs.map +1 -1
- package/esm/generations/features/index.mjs +1 -1
- package/esm/generations/features/index.mjs.map +1 -1
- package/esm/generations/features/mail.feature.mjs +1 -1
- package/esm/generations/features/mail.feature.mjs.map +1 -1
- package/esm/generations/features/mongodb.feature.mjs +1 -1
- package/esm/generations/features/mongodb.feature.mjs.map +1 -1
- package/esm/generations/features/mysql.feature.mjs +1 -1
- package/esm/generations/features/mysql.feature.mjs.map +1 -1
- package/esm/generations/features/notifications.feature.mjs +121 -17
- package/esm/generations/features/notifications.feature.mjs.map +1 -1
- package/esm/generations/features/postgres.feature.mjs +1 -1
- package/esm/generations/features/postgres.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/react.feature.mjs +1 -1
- package/esm/generations/features/react.feature.mjs.map +1 -1
- package/esm/generations/features/redis.feature.mjs +1 -1
- package/esm/generations/features/redis.feature.mjs.map +1 -1
- package/esm/generations/features/s3.feature.mjs +1 -1
- package/esm/generations/features/s3.feature.mjs.map +1 -1
- package/esm/generations/features/scheduler.feature.mjs +1 -1
- package/esm/generations/features/scheduler.feature.mjs.map +1 -1
- package/esm/generations/features/ses.feature.mjs +1 -1
- package/esm/generations/features/ses.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/insert-connector-entry.mjs +1 -1
- package/esm/generations/features/shared/insert-connector-entry.mjs.map +1 -1
- package/esm/generations/features/shared/insert-queue-dashboard-block.mjs +1 -1
- package/esm/generations/features/shared/insert-queue-dashboard-block.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 +1 -1
- package/esm/generations/features/shared/merge-web-sitemap-config.mjs.map +1 -1
- package/esm/generations/features/shared/migration-timestamp.mjs +1 -1
- package/esm/generations/features/shared/migration-timestamp.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/shared/resolve-contact-scaffold.mjs +1 -1
- package/esm/generations/features/shared/resolve-contact-scaffold.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/socket.feature.mjs +1 -1
- package/esm/generations/features/socket.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 +1 -1
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/types.mjs +1 -1
- package/esm/generations/features/types.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/pnpm-build-approvals.mjs +89 -3
- package/esm/generations/pnpm-build-approvals.mjs.map +1 -1
- package/esm/generations/stubs.mjs +184 -78
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/boot-port-preflight.d.mts +1 -1
- package/esm/http/boot-port-preflight.d.mts.map +1 -1
- package/esm/http/boot-port-preflight.mjs +1 -1
- package/esm/http/boot-port-preflight.mjs.map +1 -1
- package/esm/http/build-cors-options.mjs +1 -1
- package/esm/http/build-cors-options.mjs.map +1 -1
- package/esm/http/build-rate-limit-options.d.mts +22 -1
- package/esm/http/build-rate-limit-options.d.mts.map +1 -1
- package/esm/http/build-rate-limit-options.mjs +7 -1
- package/esm/http/build-rate-limit-options.mjs.map +1 -1
- package/esm/http/config.d.mts +1 -1
- package/esm/http/config.d.mts.map +1 -1
- package/esm/http/config.mjs +1 -1
- package/esm/http/config.mjs.map +1 -1
- package/esm/http/context/request-context.d.mts +1 -1
- package/esm/http/context/request-context.d.mts.map +1 -1
- package/esm/http/context/request-context.mjs +1 -1
- package/esm/http/context/request-context.mjs.map +1 -1
- package/esm/http/context/request-memo.d.mts +1 -1
- package/esm/http/context/request-memo.d.mts.map +1 -1
- package/esm/http/context/request-memo.mjs +1 -1
- package/esm/http/context/request-memo.mjs.map +1 -1
- package/esm/http/createHttpApplication.d.mts +1 -1
- package/esm/http/createHttpApplication.d.mts.map +1 -1
- package/esm/http/createHttpApplication.mjs +1 -1
- package/esm/http/createHttpApplication.mjs.map +1 -1
- package/esm/http/csp.d.mts +1 -1
- package/esm/http/csp.d.mts.map +1 -1
- package/esm/http/csp.mjs +1 -1
- package/esm/http/csp.mjs.map +1 -1
- package/esm/http/csrf-default-guard.d.mts +1 -1
- package/esm/http/csrf-default-guard.d.mts.map +1 -1
- package/esm/http/csrf-default-guard.mjs +1 -1
- package/esm/http/csrf-default-guard.mjs.map +1 -1
- package/esm/http/csrf-origin-policy.d.mts +1 -1
- package/esm/http/csrf-origin-policy.d.mts.map +1 -1
- package/esm/http/csrf-origin-policy.mjs +1 -1
- package/esm/http/csrf-origin-policy.mjs.map +1 -1
- package/esm/http/database/RequestLog.d.mts +63 -111
- package/esm/http/database/RequestLog.d.mts.map +1 -1
- package/esm/http/database/RequestLog.mjs +1 -1
- package/esm/http/database/RequestLog.mjs.map +1 -1
- package/esm/http/error-codes.d.mts +1 -1
- package/esm/http/error-codes.d.mts.map +1 -1
- package/esm/http/error-codes.mjs +1 -1
- package/esm/http/error-codes.mjs.map +1 -1
- package/esm/http/error-mapper.d.mts +48 -0
- package/esm/http/error-mapper.d.mts.map +1 -0
- package/esm/http/error-mapper.mjs +48 -0
- package/esm/http/error-mapper.mjs.map +1 -0
- package/esm/http/errors/errors.d.mts +1 -1
- package/esm/http/errors/errors.d.mts.map +1 -1
- package/esm/http/errors/errors.mjs +1 -1
- package/esm/http/errors/errors.mjs.map +1 -1
- package/esm/http/events.d.mts +1 -1
- package/esm/http/events.d.mts.map +1 -1
- package/esm/http/events.mjs +1 -1
- package/esm/http/events.mjs.map +1 -1
- package/esm/http/flush-pending-cookies.mjs +1 -1
- package/esm/http/flush-pending-cookies.mjs.map +1 -1
- package/esm/http/health.d.mts +1 -1
- package/esm/http/health.d.mts.map +1 -1
- package/esm/http/health.mjs +1 -1
- package/esm/http/health.mjs.map +1 -1
- package/esm/http/index.d.mts +10 -6
- package/esm/http/index.mjs +8 -5
- package/esm/http/middleware/cache-response-middleware.d.mts +1 -1
- package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.d.mts +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.d.mts.map +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.mjs +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.d.mts +1 -1
- package/esm/http/middleware/idempotency.middleware.d.mts.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/inject-request-context.d.mts +1 -1
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +4 -2
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/ip-filter.middleware.d.mts +1 -1
- package/esm/http/middleware/ip-filter.middleware.d.mts.map +1 -1
- package/esm/http/middleware/ip-filter.middleware.mjs +1 -1
- package/esm/http/middleware/ip-filter.middleware.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.d.mts +1 -1
- package/esm/http/middleware/maintenance.middleware.d.mts.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/middleware/max-body-size.middleware.d.mts +1 -1
- package/esm/http/middleware/max-body-size.middleware.d.mts.map +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
- package/esm/http/middleware/middleware-list.d.mts +1 -1
- package/esm/http/middleware/middleware-list.d.mts.map +1 -1
- package/esm/http/middleware/middleware-list.mjs +1 -1
- package/esm/http/middleware/middleware-list.mjs.map +1 -1
- package/esm/http/middleware/rate-limit.middleware.d.mts +20 -5
- package/esm/http/middleware/rate-limit.middleware.d.mts.map +1 -1
- package/esm/http/middleware/rate-limit.middleware.mjs +34 -12
- package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
- package/esm/http/middleware/utils/cidr-match.d.mts +1 -1
- package/esm/http/middleware/utils/cidr-match.d.mts.map +1 -1
- package/esm/http/middleware/utils/cidr-match.mjs +1 -1
- package/esm/http/middleware/utils/cidr-match.mjs.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.d.mts +1 -1
- package/esm/http/middleware/utils/idempotency-key.d.mts.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
- package/esm/http/middleware/utils/parse-size.d.mts +1 -1
- package/esm/http/middleware/utils/parse-size.d.mts.map +1 -1
- package/esm/http/middleware/utils/parse-size.mjs +1 -1
- package/esm/http/middleware/utils/parse-size.mjs.map +1 -1
- package/esm/http/parse-urlencoded-body.mjs +1 -1
- package/esm/http/parse-urlencoded-body.mjs.map +1 -1
- package/esm/http/plugins.d.mts +9 -2
- package/esm/http/plugins.d.mts.map +1 -1
- package/esm/http/plugins.mjs +18 -4
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/port-preflight.d.mts +1 -1
- package/esm/http/port-preflight.d.mts.map +1 -1
- package/esm/http/port-preflight.mjs +1 -1
- package/esm/http/port-preflight.mjs.map +1 -1
- package/esm/http/rate-limit-cache-store.mjs +155 -0
- package/esm/http/rate-limit-cache-store.mjs.map +1 -0
- package/esm/http/ready-report.d.mts +1 -1
- package/esm/http/ready-report.d.mts.map +1 -1
- package/esm/http/ready-report.mjs +1 -1
- package/esm/http/ready-report.mjs.map +1 -1
- package/esm/http/request-controller.d.mts +1 -1
- package/esm/http/request-controller.d.mts.map +1 -1
- package/esm/http/request-controller.mjs +1 -1
- package/esm/http/request-controller.mjs.map +1 -1
- package/esm/http/request-log-snapshot.d.mts +123 -0
- package/esm/http/request-log-snapshot.d.mts.map +1 -0
- package/esm/http/request-log-snapshot.mjs +141 -0
- package/esm/http/request-log-snapshot.mjs.map +1 -0
- package/esm/http/request.d.mts +41 -2
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +65 -2
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/resolve-bind-port.mjs +1 -1
- package/esm/http/resolve-bind-port.mjs.map +1 -1
- package/esm/http/response.d.mts +40 -8
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs +173 -61
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/serialized.d.mts +1 -1
- package/esm/http/serialized.d.mts.map +1 -1
- package/esm/http/server-rebuilt.d.mts +1 -1
- package/esm/http/server-rebuilt.d.mts.map +1 -1
- package/esm/http/server-rebuilt.mjs +1 -1
- package/esm/http/server-rebuilt.mjs.map +1 -1
- package/esm/http/server.d.mts +34 -6
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs +70 -5
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/stream-react-response.d.mts +1 -1
- package/esm/http/stream-react-response.d.mts.map +1 -1
- package/esm/http/stream-react-response.mjs +1 -1
- package/esm/http/stream-react-response.mjs.map +1 -1
- package/esm/http/stream-transport.mjs +75 -0
- package/esm/http/stream-transport.mjs.map +1 -0
- package/esm/http/tracing/trace-id.d.mts +1 -1
- package/esm/http/tracing/trace-id.d.mts.map +1 -1
- package/esm/http/tracing/trace-id.mjs +1 -1
- package/esm/http/tracing/trace-id.mjs.map +1 -1
- package/esm/http/tracing/tracing-dispatcher.d.mts +1 -1
- package/esm/http/tracing/tracing-dispatcher.d.mts.map +1 -1
- package/esm/http/tracing/tracing-dispatcher.mjs +1 -1
- package/esm/http/tracing/tracing-dispatcher.mjs.map +1 -1
- package/esm/http/tracing/tracing.type.d.mts +1 -1
- package/esm/http/tracing/tracing.type.d.mts.map +1 -1
- package/esm/http/types.d.mts +131 -6
- package/esm/http/types.d.mts.map +1 -1
- package/esm/http/uploaded-file.d.mts +2 -2
- package/esm/http/uploaded-file.d.mts.map +1 -1
- package/esm/http/uploaded-file.mjs +2 -2
- package/esm/http/uploaded-file.mjs.map +1 -1
- package/esm/http/uploads/detect-image-format.mjs +1 -1
- package/esm/http/uploads/detect-image-format.mjs.map +1 -1
- package/esm/http/uploads/generate-image-variant.mjs +1 -1
- package/esm/http/uploads/generate-image-variant.mjs.map +1 -1
- package/esm/http/uploads/generate-image-variants.d.mts +1 -1
- package/esm/http/uploads/generate-image-variants.d.mts.map +1 -1
- package/esm/http/uploads/generate-image-variants.mjs +15 -7
- package/esm/http/uploads/generate-image-variants.mjs.map +1 -1
- package/esm/http/uploads/image-variant-cache.mjs +143 -0
- package/esm/http/uploads/image-variant-cache.mjs.map +1 -0
- package/esm/http/uploads/image-variant-types.d.mts +1 -1
- package/esm/http/uploads/image-variant-types.d.mts.map +1 -1
- package/esm/http/uploads/image-variants-config-error.d.mts +1 -1
- package/esm/http/uploads/image-variants-config-error.d.mts.map +1 -1
- package/esm/http/uploads/image-variants-config-error.mjs +1 -1
- package/esm/http/uploads/image-variants-config-error.mjs.map +1 -1
- package/esm/http/uploads/index.d.mts +3 -1
- package/esm/http/uploads/index.mjs +3 -1
- package/esm/http/uploads/is-path-inside.mjs +1 -1
- package/esm/http/uploads/is-path-inside.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/local-disk-root.mjs +24 -0
- package/esm/http/uploads/local-disk-root.mjs.map +1 -0
- package/esm/http/uploads/matches-if-none-match.mjs +1 -1
- package/esm/http/uploads/matches-if-none-match.mjs.map +1 -1
- package/esm/http/uploads/parse-uploaded-file-query.mjs +1 -1
- package/esm/http/uploads/parse-uploaded-file-query.mjs.map +1 -1
- package/esm/http/uploads/purge-image-variants.d.mts +32 -0
- package/esm/http/uploads/purge-image-variants.d.mts.map +1 -0
- package/esm/http/uploads/purge-image-variants.mjs +46 -0
- package/esm/http/uploads/purge-image-variants.mjs.map +1 -0
- package/esm/http/uploads/read-file-head.mjs +1 -1
- package/esm/http/uploads/read-file-head.mjs.map +1 -1
- package/esm/http/uploads/resolve-image-variant.d.mts +73 -0
- package/esm/http/uploads/resolve-image-variant.d.mts.map +1 -0
- package/esm/http/uploads/resolve-image-variant.mjs +107 -0
- package/esm/http/uploads/resolve-image-variant.mjs.map +1 -0
- package/esm/http/uploads/resolve-image-variants-config.mjs +1 -1
- package/esm/http/uploads/resolve-image-variants-config.mjs.map +1 -1
- package/esm/http/uploads/resolve-inline-image-content-type.mjs +1 -1
- package/esm/http/uploads/resolve-inline-image-content-type.mjs.map +1 -1
- package/esm/http/uploads/resolve-original-content-type.mjs +1 -1
- package/esm/http/uploads/resolve-original-content-type.mjs.map +1 -1
- package/esm/http/uploads/resolve-upload-path.mjs +3 -2
- package/esm/http/uploads/resolve-upload-path.mjs.map +1 -1
- package/esm/http/uploads/resolve-variant-candidate.mjs +1 -1
- package/esm/http/uploads/resolve-variant-candidate.mjs.map +1 -1
- package/esm/http/uploads/single-flight.mjs +1 -1
- package/esm/http/uploads/single-flight.mjs.map +1 -1
- package/esm/http/uploads/uploaded-file.controller.d.mts +24 -6
- package/esm/http/uploads/uploaded-file.controller.d.mts.map +1 -1
- package/esm/http/uploads/uploaded-file.controller.mjs +63 -91
- package/esm/http/uploads/uploaded-file.controller.mjs.map +1 -1
- package/esm/http/uploads/variant-cache-key.mjs +30 -5
- package/esm/http/uploads/variant-cache-key.mjs.map +1 -1
- package/esm/http/uploads/variant-generations.mjs +1 -1
- package/esm/http/uploads/variant-generations.mjs.map +1 -1
- package/esm/http/uploads-config.d.mts +1 -1
- package/esm/http/uploads-config.d.mts.map +1 -1
- package/esm/http/uploads-config.mjs +1 -1
- package/esm/http/uploads-config.mjs.map +1 -1
- package/esm/http/uploads-types.d.mts +1 -1
- package/esm/http/uploads-types.d.mts.map +1 -1
- package/esm/http/xmlable.d.mts +1 -1
- package/esm/http/xmlable.d.mts.map +1 -1
- package/esm/image/image.d.mts +1 -1
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs +1 -1
- package/esm/image/image.mjs.map +1 -1
- package/esm/index.d.mts +15 -9
- package/esm/index.mjs +11 -7
- package/esm/loader/app-module-importer.d.mts +1 -1
- package/esm/loader/app-module-importer.d.mts.map +1 -1
- package/esm/loader/app-module-importer.mjs +1 -1
- package/esm/loader/app-module-importer.mjs.map +1 -1
- package/esm/localization/parse-locale-dictionary.d.mts +7 -2
- package/esm/localization/parse-locale-dictionary.d.mts.map +1 -1
- package/esm/localization/parse-locale-dictionary.mjs +6 -2
- package/esm/localization/parse-locale-dictionary.mjs.map +1 -1
- package/esm/localization/register-locale-dictionary.d.mts +1 -1
- package/esm/localization/register-locale-dictionary.d.mts.map +1 -1
- package/esm/localization/register-locale-dictionary.mjs +1 -1
- package/esm/localization/register-locale-dictionary.mjs.map +1 -1
- package/esm/logger/fatal-visibility.d.mts +1 -1
- package/esm/logger/fatal-visibility.d.mts.map +1 -1
- package/esm/logger/fatal-visibility.mjs +1 -1
- package/esm/logger/fatal-visibility.mjs.map +1 -1
- package/esm/logger/logger.d.mts +1 -1
- package/esm/logger/logger.d.mts.map +1 -1
- package/esm/logger/logger.mjs +1 -1
- package/esm/logger/logger.mjs.map +1 -1
- package/esm/logger/types.d.mts +1 -1
- package/esm/logger/types.d.mts.map +1 -1
- package/esm/mail/config.d.mts +1 -1
- package/esm/mail/config.d.mts.map +1 -1
- package/esm/mail/config.mjs +1 -1
- package/esm/mail/config.mjs.map +1 -1
- package/esm/mail/events.d.mts +1 -1
- package/esm/mail/events.d.mts.map +1 -1
- package/esm/mail/events.mjs +1 -1
- package/esm/mail/events.mjs.map +1 -1
- package/esm/mail/mail.d.mts +1 -1
- package/esm/mail/mail.d.mts.map +1 -1
- package/esm/mail/mail.mjs +1 -1
- package/esm/mail/mail.mjs.map +1 -1
- package/esm/mail/mailer-pool.d.mts +1 -1
- package/esm/mail/mailer-pool.d.mts.map +1 -1
- package/esm/mail/mailer-pool.mjs +1 -1
- package/esm/mail/mailer-pool.mjs.map +1 -1
- package/esm/mail/react-mail.d.mts +1 -1
- package/esm/mail/react-mail.d.mts.map +1 -1
- package/esm/mail/react-mail.mjs +1 -1
- package/esm/mail/react-mail.mjs.map +1 -1
- package/esm/mail/send-mail.d.mts +1 -1
- package/esm/mail/send-mail.d.mts.map +1 -1
- package/esm/mail/send-mail.mjs +7 -7
- package/esm/mail/send-mail.mjs.map +1 -1
- package/esm/mail/test-mailbox.d.mts +1 -1
- package/esm/mail/test-mailbox.d.mts.map +1 -1
- package/esm/mail/test-mailbox.mjs +1 -1
- package/esm/mail/test-mailbox.mjs.map +1 -1
- package/esm/mail/types.d.mts +1 -1
- package/esm/mail/types.d.mts.map +1 -1
- package/esm/mail/types.mjs +1 -1
- package/esm/mail/types.mjs.map +1 -1
- package/esm/manifest/manifest-manager.mjs +1 -1
- package/esm/manifest/manifest-manager.mjs.map +1 -1
- package/esm/openapi/build-openapi-document.d.mts +1 -1
- package/esm/openapi/build-openapi-document.d.mts.map +1 -1
- package/esm/openapi/build-openapi-document.mjs +1 -1
- package/esm/openapi/build-openapi-document.mjs.map +1 -1
- package/esm/openapi/cast-schema.mjs +1 -1
- package/esm/openapi/cast-schema.mjs.map +1 -1
- package/esm/openapi/collect-openapi-document.mjs +1 -1
- package/esm/openapi/collect-openapi-document.mjs.map +1 -1
- package/esm/openapi/development-openapi-provider.d.mts +1 -1
- package/esm/openapi/development-openapi-provider.d.mts.map +1 -1
- package/esm/openapi/development-openapi-provider.mjs +1 -1
- package/esm/openapi/development-openapi-provider.mjs.map +1 -1
- package/esm/openapi/error-responses.mjs +1 -1
- package/esm/openapi/error-responses.mjs.map +1 -1
- package/esm/openapi/openapi-child-output.mjs +1 -1
- package/esm/openapi/openapi-child-output.mjs.map +1 -1
- package/esm/openapi/openapi-protocol.mjs +1 -1
- package/esm/openapi/openapi-protocol.mjs.map +1 -1
- package/esm/openapi/paths.mjs +1 -1
- package/esm/openapi/paths.mjs.map +1 -1
- package/esm/openapi/postman/build-request-body.mjs +1 -1
- package/esm/openapi/postman/build-request-body.mjs.map +1 -1
- package/esm/openapi/postman/build-request-url.mjs +1 -1
- package/esm/openapi/postman/build-request-url.mjs.map +1 -1
- package/esm/openapi/postman/build-saved-responses.mjs +1 -1
- package/esm/openapi/postman/build-saved-responses.mjs.map +1 -1
- package/esm/openapi/postman/example-from-schema.d.mts +1 -1
- package/esm/openapi/postman/example-from-schema.d.mts.map +1 -1
- package/esm/openapi/postman/example-from-schema.mjs +1 -1
- package/esm/openapi/postman/example-from-schema.mjs.map +1 -1
- package/esm/openapi/postman/http-status-text.mjs +1 -1
- package/esm/openapi/postman/http-status-text.mjs.map +1 -1
- package/esm/openapi/postman/openapi-to-postman-collection.d.mts +1 -1
- package/esm/openapi/postman/openapi-to-postman-collection.d.mts.map +1 -1
- package/esm/openapi/postman/openapi-to-postman-collection.mjs +1 -1
- package/esm/openapi/postman/openapi-to-postman-collection.mjs.map +1 -1
- package/esm/openapi/postman/postman-types.d.mts +1 -1
- package/esm/openapi/postman/postman-types.d.mts.map +1 -1
- package/esm/openapi/postman/resolve-operation-auth.mjs +1 -1
- package/esm/openapi/postman/resolve-operation-auth.mjs.map +1 -1
- package/esm/openapi/postman/schema-refs.d.mts +1 -1
- package/esm/openapi/postman/schema-refs.d.mts.map +1 -1
- package/esm/openapi/postman/schema-refs.mjs +1 -1
- package/esm/openapi/postman/schema-refs.mjs.map +1 -1
- package/esm/openapi/postman/uuid-v5.mjs +1 -1
- package/esm/openapi/postman/uuid-v5.mjs.map +1 -1
- package/esm/openapi/request-schemas.mjs +1 -1
- package/esm/openapi/request-schemas.mjs.map +1 -1
- package/esm/openapi/response-schemas.mjs +5 -2
- package/esm/openapi/response-schemas.mjs.map +1 -1
- package/esm/openapi/schema-registry.mjs +7 -1
- package/esm/openapi/schema-registry.mjs.map +1 -1
- package/esm/openapi/security.mjs +1 -1
- package/esm/openapi/security.mjs.map +1 -1
- package/esm/openapi/types.d.mts +1 -1
- package/esm/openapi/types.d.mts.map +1 -1
- package/esm/production/assert-dist-ready-to-start.mjs +1 -1
- package/esm/production/assert-dist-ready-to-start.mjs.map +1 -1
- package/esm/production/assert-generated-imports.mjs +1 -1
- package/esm/production/assert-generated-imports.mjs.map +1 -1
- package/esm/production/build-app-production.mjs +1 -1
- package/esm/production/build-app-production.mjs.map +1 -1
- package/esm/production/build-contributions.mjs +1 -1
- package/esm/production/build-contributions.mjs.map +1 -1
- package/esm/production/bundle-framework-dependencies.mjs +1 -1
- package/esm/production/bundle-framework-dependencies.mjs.map +1 -1
- package/esm/production/dist-build-manifest.mjs +1 -1
- package/esm/production/dist-build-manifest.mjs.map +1 -1
- package/esm/production/esbuild-plugins.mjs +1 -1
- package/esm/production/esbuild-plugins.mjs.map +1 -1
- package/esm/production/esbuild-preflight.mjs +1 -1
- package/esm/production/esbuild-preflight.mjs.map +1 -1
- package/esm/production/production-builder.mjs +1 -1
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/production-supervisor.mjs +1 -1
- package/esm/production/production-supervisor.mjs.map +1 -1
- package/esm/production/promote-dist.mjs +1 -1
- package/esm/production/promote-dist.mjs.map +1 -1
- package/esm/production/registration-child-runner.mjs +1 -1
- package/esm/production/registration-child-runner.mjs.map +1 -1
- package/esm/production/resolve-build-config.d.mts +1 -1
- package/esm/production/resolve-build-config.d.mts.map +1 -1
- package/esm/production/resolve-build-config.mjs +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/production/route-registration-child.mjs +1 -1
- package/esm/production/route-registration-child.mjs.map +1 -1
- package/esm/production/route-registration-snapshot.mjs +1 -1
- package/esm/production/route-registration-snapshot.mjs.map +1 -1
- package/esm/production/tsconfig-path-aliases.mjs +1 -1
- package/esm/production/tsconfig-path-aliases.mjs.map +1 -1
- package/esm/react/index.d.mts +1 -1
- package/esm/react/index.d.mts.map +1 -1
- package/esm/react/index.mjs +1 -1
- package/esm/react/index.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.mjs +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/repositories/contracts/query-builder.contract.d.mts +1 -1
- package/esm/repositories/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/repositories/contracts/repository-adapter.contract.d.mts +1 -1
- package/esm/repositories/contracts/repository-adapter.contract.d.mts.map +1 -1
- package/esm/repositories/contracts/types.d.mts +1 -1
- package/esm/repositories/contracts/types.d.mts.map +1 -1
- package/esm/repositories/repository.manager.d.mts +1 -1
- package/esm/repositories/repository.manager.d.mts.map +1 -1
- package/esm/repositories/repository.manager.mjs +1 -1
- package/esm/repositories/repository.manager.mjs.map +1 -1
- package/esm/resource/define-resource.d.mts +1 -1
- package/esm/resource/define-resource.d.mts.map +1 -1
- package/esm/resource/define-resource.mjs +1 -1
- package/esm/resource/define-resource.mjs.map +1 -1
- package/esm/resource/index.d.mts +1 -0
- package/esm/resource/index.mjs +1 -0
- package/esm/resource/nullable-resource.d.mts +44 -0
- package/esm/resource/nullable-resource.d.mts.map +1 -0
- package/esm/resource/nullable-resource.mjs +38 -0
- package/esm/resource/nullable-resource.mjs.map +1 -0
- package/esm/resource/register-resource.d.mts +1 -1
- package/esm/resource/register-resource.d.mts.map +1 -1
- package/esm/resource/register-resource.mjs +1 -1
- package/esm/resource/register-resource.mjs.map +1 -1
- package/esm/resource/resource-field-builder.d.mts +1 -1
- package/esm/resource/resource-field-builder.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.mjs +1 -1
- package/esm/resource/resource-field-builder.mjs.map +1 -1
- package/esm/resource/resource.d.mts +1 -1
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs +23 -4
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/resource/types.d.mts +11 -6
- package/esm/resource/types.d.mts.map +1 -1
- package/esm/restful/restful.d.mts +1 -1
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs +1 -1
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/build-route-rate-limit.mjs +1 -1
- package/esm/router/build-route-rate-limit.mjs.map +1 -1
- package/esm/router/describe-route-for-log.mjs +1 -1
- package/esm/router/describe-route-for-log.mjs.map +1 -1
- package/esm/router/dev-dispatch.mjs +1 -1
- package/esm/router/dev-dispatch.mjs.map +1 -1
- package/esm/router/log-request-lifecycle.mjs +1 -1
- package/esm/router/log-request-lifecycle.mjs.map +1 -1
- package/esm/router/named-api-routes-with-responses.mjs +1 -1
- package/esm/router/named-api-routes-with-responses.mjs.map +1 -1
- package/esm/router/normalize-request-path.d.mts +1 -1
- package/esm/router/normalize-request-path.d.mts.map +1 -1
- package/esm/router/normalize-request-path.mjs +1 -1
- package/esm/router/normalize-request-path.mjs.map +1 -1
- package/esm/router/normalize-route-path.d.mts +1 -1
- package/esm/router/normalize-route-path.d.mts.map +1 -1
- package/esm/router/normalize-route-path.mjs +1 -1
- package/esm/router/normalize-route-path.mjs.map +1 -1
- package/esm/router/positional-handler-diagnostics.d.mts +1 -1
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.mjs +1 -1
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
- package/esm/router/response-schema-types.mjs +3 -1
- package/esm/router/response-schema-types.mjs.map +1 -1
- package/esm/router/route-builder.d.mts +1 -1
- package/esm/router/route-builder.d.mts.map +1 -1
- package/esm/router/route-builder.mjs +1 -1
- package/esm/router/route-builder.mjs.map +1 -1
- package/esm/router/route-name-method-suffix.d.mts +1 -1
- package/esm/router/route-name-method-suffix.d.mts.map +1 -1
- package/esm/router/route-name-method-suffix.mjs +1 -1
- package/esm/router/route-name-method-suffix.mjs.map +1 -1
- package/esm/router/route-registry.d.mts +1 -1
- package/esm/router/route-registry.d.mts.map +1 -1
- package/esm/router/route-registry.mjs +1 -1
- package/esm/router/route-registry.mjs.map +1 -1
- package/esm/router/router.d.mts +8 -1
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +20 -15
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +13 -1
- package/esm/router/types.d.mts.map +1 -1
- package/esm/socket/types.d.mts +1 -1
- package/esm/socket/types.d.mts.map +1 -1
- package/esm/socket/utils.d.mts +1 -1
- package/esm/socket/utils.d.mts.map +1 -1
- package/esm/socket/utils.mjs +1 -1
- package/esm/socket/utils.mjs.map +1 -1
- package/esm/storage/config.d.mts +1 -1
- package/esm/storage/config.d.mts.map +1 -1
- package/esm/storage/config.mjs +1 -1
- package/esm/storage/config.mjs.map +1 -1
- package/esm/storage/context/storage-driver-context.d.mts +1 -1
- package/esm/storage/context/storage-driver-context.d.mts.map +1 -1
- package/esm/storage/context/storage-driver-context.mjs +1 -1
- package/esm/storage/context/storage-driver-context.mjs.map +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs +1 -1
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/drivers/do-spaces-driver.d.mts +1 -1
- package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
- package/esm/storage/drivers/do-spaces-driver.mjs +1 -1
- package/esm/storage/drivers/do-spaces-driver.mjs.map +1 -1
- package/esm/storage/drivers/local-driver.d.mts +12 -1
- package/esm/storage/drivers/local-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.mjs +33 -1
- package/esm/storage/drivers/local-driver.mjs.map +1 -1
- package/esm/storage/drivers/r2-driver.d.mts +1 -1
- package/esm/storage/drivers/r2-driver.d.mts.map +1 -1
- package/esm/storage/drivers/r2-driver.mjs +1 -1
- package/esm/storage/drivers/r2-driver.mjs.map +1 -1
- package/esm/storage/drivers/s3-driver.d.mts +1 -1
- package/esm/storage/drivers/s3-driver.d.mts.map +1 -1
- package/esm/storage/drivers/s3-driver.mjs +1 -1
- package/esm/storage/drivers/s3-driver.mjs.map +1 -1
- package/esm/storage/index.d.mts +1 -1
- package/esm/storage/scoped-storage.d.mts +1 -1
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs +1 -1
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage-file.d.mts +1 -1
- package/esm/storage/storage-file.d.mts.map +1 -1
- package/esm/storage/storage-file.mjs +1 -1
- package/esm/storage/storage-file.mjs.map +1 -1
- package/esm/storage/storage.d.mts +3 -3
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/storage.mjs +1 -1
- package/esm/storage/storage.mjs.map +1 -1
- package/esm/storage/types.d.mts +3 -2
- package/esm/storage/types.d.mts.map +1 -1
- package/esm/storage/utils/classify-ip-address.d.mts +1 -1
- package/esm/storage/utils/classify-ip-address.d.mts.map +1 -1
- package/esm/storage/utils/classify-ip-address.mjs +1 -1
- package/esm/storage/utils/classify-ip-address.mjs.map +1 -1
- package/esm/storage/utils/contain-path.d.mts +1 -1
- package/esm/storage/utils/contain-path.d.mts.map +1 -1
- package/esm/storage/utils/contain-path.mjs +1 -1
- package/esm/storage/utils/contain-path.mjs.map +1 -1
- package/esm/storage/utils/mime.d.mts +1 -1
- package/esm/storage/utils/mime.d.mts.map +1 -1
- package/esm/storage/utils/mime.mjs +1 -1
- package/esm/storage/utils/mime.mjs.map +1 -1
- package/esm/storage/utils/safe-fetch.d.mts +1 -1
- package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs +1 -1
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/storage/utils/storage-capability-error.d.mts +1 -1
- package/esm/storage/utils/storage-capability-error.d.mts.map +1 -1
- package/esm/storage/utils/storage-capability-error.mjs +1 -1
- package/esm/storage/utils/storage-capability-error.mjs.map +1 -1
- package/esm/storage/utils/storage-error.d.mts +1 -1
- package/esm/storage/utils/storage-error.d.mts.map +1 -1
- package/esm/storage/utils/storage-error.mjs +1 -1
- package/esm/storage/utils/storage-error.mjs.map +1 -1
- package/esm/storage/utils/storage-not-initialized-error.d.mts +1 -1
- package/esm/storage/utils/storage-not-initialized-error.d.mts.map +1 -1
- package/esm/storage/utils/storage-not-initialized-error.mjs +1 -1
- package/esm/storage/utils/storage-not-initialized-error.mjs.map +1 -1
- package/esm/tests/start-http-development-server.d.mts +1 -1
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs +1 -1
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-connectors-selection.d.mts +1 -1
- package/esm/tests/test-connectors-selection.d.mts.map +1 -1
- package/esm/tests/test-connectors-selection.mjs +1 -1
- package/esm/tests/test-connectors-selection.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts +1 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-helpers.mjs +1 -1
- package/esm/tests/test-helpers.mjs.map +1 -1
- package/esm/tests/test-lifecycle-state.mjs +1 -1
- package/esm/tests/test-lifecycle-state.mjs.map +1 -1
- package/esm/tests/test-server-port-channel.mjs +1 -1
- package/esm/tests/test-server-port-channel.mjs.map +1 -1
- package/esm/tests/test-setup-timeout.mjs +1 -1
- package/esm/tests/test-setup-timeout.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs +1 -1
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/updater/package-manager.mjs +1 -1
- package/esm/updater/package-manager.mjs.map +1 -1
- package/esm/updater/update-warlock-packages.mjs +1 -1
- package/esm/updater/update-warlock-packages.mjs.map +1 -1
- package/esm/use-cases/types.d.mts +1 -1
- package/esm/use-cases/types.d.mts.map +1 -1
- package/esm/use-cases/use-case-broadcast.d.mts +1 -1
- package/esm/use-cases/use-case-broadcast.d.mts.map +1 -1
- package/esm/use-cases/use-case-broadcast.mjs +1 -1
- package/esm/use-cases/use-case-broadcast.mjs.map +1 -1
- package/esm/use-cases/use-case-events.d.mts +1 -1
- package/esm/use-cases/use-case-events.d.mts.map +1 -1
- package/esm/use-cases/use-case-events.mjs +1 -1
- package/esm/use-cases/use-case-events.mjs.map +1 -1
- package/esm/use-cases/use-case-pipeline.d.mts +1 -1
- package/esm/use-cases/use-case-pipeline.d.mts.map +1 -1
- package/esm/use-cases/use-case-pipeline.mjs +1 -1
- package/esm/use-cases/use-case-pipeline.mjs.map +1 -1
- package/esm/use-cases/use-case.d.mts +1 -1
- package/esm/use-cases/use-case.d.mts.map +1 -1
- package/esm/use-cases/use-case.errors.d.mts +1 -1
- package/esm/use-cases/use-case.errors.d.mts.map +1 -1
- package/esm/use-cases/use-case.errors.mjs +1 -1
- package/esm/use-cases/use-case.errors.mjs.map +1 -1
- package/esm/use-cases/use-case.mjs +1 -1
- package/esm/use-cases/use-case.mjs.map +1 -1
- package/esm/use-cases/use-cases-registry.d.mts +1 -1
- package/esm/use-cases/use-cases-registry.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.mjs +1 -1
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/app-log.d.mts +6 -6
- package/esm/utils/app-log.d.mts.map +1 -1
- package/esm/utils/app-log.mjs +1 -1
- package/esm/utils/app-log.mjs.map +1 -1
- package/esm/utils/database-log.d.mts +1 -1
- package/esm/utils/database-log.d.mts.map +1 -1
- package/esm/utils/database-log.mjs +1 -1
- package/esm/utils/database-log.mjs.map +1 -1
- package/esm/utils/detect-environment-overrides.mjs +1 -1
- package/esm/utils/detect-environment-overrides.mjs.map +1 -1
- package/esm/utils/environment.d.mts +1 -1
- package/esm/utils/environment.d.mts.map +1 -1
- package/esm/utils/environment.mjs +1 -1
- package/esm/utils/environment.mjs.map +1 -1
- package/esm/utils/framework-vesion.mjs +1 -1
- package/esm/utils/framework-vesion.mjs.map +1 -1
- package/esm/utils/get-localized.d.mts +1 -1
- package/esm/utils/get-localized.d.mts.map +1 -1
- package/esm/utils/get-localized.mjs +1 -1
- package/esm/utils/get-localized.mjs.map +1 -1
- package/esm/utils/is-test-file.mjs +1 -1
- package/esm/utils/is-test-file.mjs.map +1 -1
- package/esm/utils/load-environment.mjs +1 -1
- package/esm/utils/load-environment.mjs.map +1 -1
- package/esm/utils/normalized-path.d.mts +1 -1
- package/esm/utils/normalized-path.d.mts.map +1 -1
- package/esm/utils/normalized-path.mjs +1 -1
- package/esm/utils/normalized-path.mjs.map +1 -1
- package/esm/utils/npm-registry.d.mts +1 -1
- package/esm/utils/npm-registry.d.mts.map +1 -1
- package/esm/utils/npm-registry.mjs +1 -1
- package/esm/utils/npm-registry.mjs.map +1 -1
- package/esm/utils/paths.d.mts +1 -1
- package/esm/utils/paths.d.mts.map +1 -1
- package/esm/utils/paths.mjs +1 -1
- package/esm/utils/paths.mjs.map +1 -1
- package/esm/utils/promise-all-object.d.mts +1 -1
- package/esm/utils/promise-all-object.d.mts.map +1 -1
- package/esm/utils/promise-all-object.mjs +1 -1
- package/esm/utils/promise-all-object.mjs.map +1 -1
- package/esm/utils/queue.d.mts +1 -1
- package/esm/utils/queue.d.mts.map +1 -1
- package/esm/utils/queue.mjs +1 -1
- package/esm/utils/queue.mjs.map +1 -1
- package/esm/utils/recorded-environment-overrides.mjs +1 -1
- package/esm/utils/recorded-environment-overrides.mjs.map +1 -1
- package/esm/utils/report-environment-overrides.mjs +1 -1
- package/esm/utils/report-environment-overrides.mjs.map +1 -1
- package/esm/utils/sleep.d.mts +1 -1
- package/esm/utils/sleep.d.mts.map +1 -1
- package/esm/utils/sleep.mjs +1 -1
- package/esm/utils/sleep.mjs.map +1 -1
- package/esm/utils/sluggable.d.mts +1 -1
- package/esm/utils/sluggable.d.mts.map +1 -1
- package/esm/utils/sluggable.mjs +1 -1
- package/esm/utils/sluggable.mjs.map +1 -1
- package/esm/utils/to-json.d.mts +1 -1
- package/esm/utils/to-json.d.mts.map +1 -1
- package/esm/utils/to-json.mjs +1 -1
- package/esm/utils/to-json.mjs.map +1 -1
- package/esm/utils/types.d.mts +1 -1
- package/esm/utils/types.d.mts.map +1 -1
- package/esm/utils/urls.d.mts +1 -1
- package/esm/utils/urls.d.mts.map +1 -1
- package/esm/utils/urls.mjs +1 -1
- package/esm/utils/urls.mjs.map +1 -1
- package/esm/utils/version-compare.d.mts +1 -1
- package/esm/utils/version-compare.d.mts.map +1 -1
- package/esm/utils/version-compare.mjs +1 -1
- package/esm/utils/version-compare.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-id.d.mts +1 -1
- package/esm/validation/database/exists-except-current-id.d.mts.map +1 -1
- package/esm/validation/database/exists-except-current-id.mjs +1 -1
- package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-user.d.mts +1 -1
- package/esm/validation/database/exists-except-current-user.d.mts.map +1 -1
- package/esm/validation/database/exists-except-current-user.mjs +1 -1
- package/esm/validation/database/exists-except-current-user.mjs.map +1 -1
- package/esm/validation/database/types.d.mts +1 -1
- package/esm/validation/database/types.d.mts.map +1 -1
- package/esm/validation/database/unique-except-current-id.d.mts +1 -1
- package/esm/validation/database/unique-except-current-id.d.mts.map +1 -1
- package/esm/validation/database/unique-except-current-id.mjs +1 -1
- package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
- package/esm/validation/database/unique-except-current-user.d.mts +1 -1
- package/esm/validation/database/unique-except-current-user.d.mts.map +1 -1
- package/esm/validation/database/unique-except-current-user.mjs +1 -1
- package/esm/validation/database/unique-except-current-user.mjs.map +1 -1
- package/esm/validation/file/file.d.mts +1 -1
- package/esm/validation/file/file.d.mts.map +1 -1
- package/esm/validation/file/file.mjs +1 -1
- package/esm/validation/file/file.mjs.map +1 -1
- package/esm/validation/init.mjs +1 -1
- package/esm/validation/init.mjs.map +1 -1
- package/esm/validation/plugins/database-plugin.mjs +1 -1
- package/esm/validation/plugins/database-plugin.mjs.map +1 -1
- package/esm/validation/plugins/file-plugin.mjs +1 -1
- package/esm/validation/plugins/file-plugin.mjs.map +1 -1
- package/esm/validation/plugins/localized-plugin.mjs +1 -1
- package/esm/validation/plugins/localized-plugin.mjs.map +1 -1
- package/esm/validation/types.d.mts +1 -1
- package/esm/validation/types.d.mts.map +1 -1
- package/esm/validation/validateAll.mjs +1 -1
- package/esm/validation/validateAll.mjs.map +1 -1
- package/esm/validation/validators/file-validator.d.mts +44 -41
- package/esm/validation/validators/file-validator.d.mts.map +1 -1
- package/esm/validation/validators/file-validator.mjs +4 -1
- package/esm/validation/validators/file-validator.mjs.map +1 -1
- package/esm/vite/lower-stage3-decorators.d.mts +1 -1
- package/esm/vite/lower-stage3-decorators.d.mts.map +1 -1
- package/esm/vite/lower-stage3-decorators.mjs +1 -1
- package/esm/vite/lower-stage3-decorators.mjs.map +1 -1
- package/esm/warlock-config/bundle-config.mjs +148 -0
- package/esm/warlock-config/bundle-config.mjs.map +1 -0
- package/esm/warlock-config/default-configurations.mjs +1 -1
- package/esm/warlock-config/default-configurations.mjs.map +1 -1
- package/esm/warlock-config/define-config.d.mts +1 -1
- package/esm/warlock-config/define-config.d.mts.map +1 -1
- package/esm/warlock-config/define-config.mjs +1 -1
- package/esm/warlock-config/define-config.mjs.map +1 -1
- package/esm/warlock-config/index.d.mts +1 -1
- package/esm/warlock-config/index.mjs +1 -1
- package/esm/warlock-config/normalize-build-config.mjs +1 -1
- package/esm/warlock-config/normalize-build-config.mjs.map +1 -1
- package/esm/warlock-config/types.d.mts +1 -1
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts +32 -7
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs +94 -18
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/esm/warlock-config/warn-deprecated-config.mjs +1 -1
- package/esm/warlock-config/warn-deprecated-config.mjs.map +1 -1
- package/llms-full.txt +306 -249
- package/llms.txt +42 -42
- package/package.json +20 -13
- package/skills/README.md +39 -39
- package/skills/add-connector/SKILL.md +4 -4
- package/skills/benchmark-code/SKILL.md +3 -3
- package/skills/build-restful/SKILL.md +5 -5
- package/skills/build-url/SKILL.md +7 -7
- package/skills/configure-app/SKILL.md +30 -8
- package/skills/create-controller/SKILL.md +12 -8
- package/skills/create-module/SKILL.md +9 -9
- package/skills/define-resource/SKILL.md +4 -4
- package/skills/encrypt-data/SKILL.md +7 -7
- package/skills/generate-openapi/SKILL.md +5 -5
- package/skills/hash-password/SKILL.md +8 -8
- package/skills/health-checks/SKILL.md +8 -6
- package/skills/lower-stage3-decorators/SKILL.md +4 -4
- package/skills/process-image/SKILL.md +5 -5
- package/skills/register-route/SKILL.md +16 -6
- package/skills/request-memo/SKILL.md +4 -4
- package/skills/request-tracing/SKILL.md +6 -6
- package/skills/resolve-path/SKILL.md +6 -6
- package/skills/retry-operation/SKILL.md +4 -4
- package/skills/run-app/SKILL.md +20 -13
- package/skills/send-mail/SKILL.md +3 -3
- package/skills/send-response/SKILL.md +16 -8
- package/skills/store-file/SKILL.md +9 -5
- package/skills/test-http/SKILL.md +5 -5
- package/skills/test-service/SKILL.md +5 -5
- package/skills/update-packages/SKILL.md +6 -6
- package/skills/upload-file/SKILL.md +7 -7
- package/skills/use-app-context/SKILL.md +7 -7
- package/skills/use-localization/SKILL.md +6 -6
- package/skills/use-middleware/SKILL.md +8 -8
- package/skills/use-model-transformers/SKILL.md +6 -6
- package/skills/use-repository/SKILL.md +7 -7
- package/skills/use-request-locals/SKILL.md +4 -4
- package/skills/validate-input/SKILL.md +7 -7
- package/skills/warlock-conventions/SKILL.md +6 -6
- package/skills/warlock-doctor/SKILL.md +5 -5
- package/skills/warlock-routes/SKILL.md +6 -6
- package/skills/wire-socket/SKILL.md +4 -4
- package/skills/write-cli-command/SKILL.md +6 -6
- package/skills/write-middleware/SKILL.md +8 -8
- package/skills/write-seeder/SKILL.md +5 -5
- package/skills/write-use-case/SKILL.md +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploaded-file.mjs","names":[],"sources":["../../../../../../../core/src/http/uploaded-file.ts"],"sourcesContent":["import type { MultipartFile } from \"@fastify/multipart\";\nimport { Random } from \"@mongez/reinforcements\";\nimport dayjs from \"dayjs\";\nimport path from \"path\";\nimport { Image, type ImageFormat, type ImageTransformOptions } from \"../image\";\nimport { storage, type ScopedStorage, type StorageDriverName, type StorageFile } from \"../storage\";\nimport { sanitizePath } from \"../utils/paths\";\nimport { uploadsConfig } from \"./uploads-config\";\nimport type {\n FileNamingStrategy,\n ImageTransformCallback,\n PrefixConfig,\n SaveAsOptions,\n SaveOptions,\n UploadedFileImageOptions,\n} from \"./uploads-types\";\n\ntype UploadedFileMetadata = {\n name: string;\n mimeType: string;\n extension: string;\n size: number;\n width?: number;\n height?: number;\n};\n\n/**\n * What an `UploadedFile` looks like on the wire.\n *\n * Every member is knowable **without reading the file**, which is what lets\n * {@link UploadedFile.toJSON} be synchronous — see the note there. `size` is\n * the one exception and it is optional for exactly that reason: present when\n * the file has already been buffered, absent otherwise, never guessed.\n */\nexport type UploadedFileJson = {\n name: string;\n mimeType: string;\n extension: string;\n isImage: boolean;\n isVideo: boolean;\n isAudio: boolean;\n size?: number;\n};\n\n/**\n * Options for validating file before saving\n */\nexport type FileValidationOptions = {\n /**\n * List of allowed MIME types\n *\n * @example [\"image/jpeg\", \"image/png\", \"image/webp\"]\n */\n allowedMimeTypes?: string[];\n\n /**\n * List of allowed file extensions (without dot)\n *\n * @example [\"jpg\", \"jpeg\", \"png\", \"webp\"]\n */\n allowedExtensions?: string[];\n\n /**\n * Maximum file size in bytes\n *\n * @example 5 * 1024 * 1024 // 5MB\n */\n maxSize?: number;\n};\n\n/**\n * UploadedFile - Handles multipart file uploads with storage and image integration\n *\n * Provides a fluent API for validating, transforming, and saving uploaded files\n * to various storage drivers (local, S3, R2, etc.).\n *\n * @example\n * ```typescript\n * // Simple save with random name\n * const file = await uploadedFile.save(\"avatars\");\n *\n * // Original name with date prefix\n * const file = await uploadedFile.save(\"avatars\", {\n * name: \"original\",\n * prefix: { format: \"yyyy/mm/dd\", as: \"directory\" }\n * });\n *\n * // With image transformations\n * const file = await uploadedFile\n * .resize(800, 600)\n * .quality(85)\n * .format(\"webp\")\n * .save(\"avatars\");\n *\n * // Different storage driver\n * const file = await uploadedFile.use(\"s3\").save(\"avatars\");\n * ```\n */\nexport class UploadedFile {\n /**\n * File buffered content\n * @internal\n */\n protected bufferedFileContent?: Buffer;\n\n /**\n * Upload file hash (SHA-256)\n *\n * Populated after file is saved.\n */\n public hash = \"\";\n\n /**\n * Selected storage driver\n * @internal\n */\n protected _storage: ScopedStorage = storage;\n\n /**\n * Saved StorageFile reference\n * @internal\n */\n protected _storageFile?: StorageFile;\n\n /**\n * Queued image options (high-level API)\n * @internal\n */\n protected _imageOptions: UploadedFileImageOptions = {};\n\n /**\n * Full transform options or callback\n * @internal\n */\n protected _transformConfig?: ImageTransformOptions | ImageTransformCallback;\n\n /**\n * Create a new UploadedFile instance\n *\n * @param fileData - Multipart file data from Fastify\n * @throws Error if file data is invalid\n */\n public constructor(protected readonly fileData: MultipartFile) {\n if (!fileData?.filename) {\n throw new Error(\"Invalid file data: filename is required\");\n }\n }\n\n // ============================================================\n // File Properties\n // ============================================================\n\n /**\n * Get file name (sanitized)\n *\n * Returns the original filename with special characters removed/replaced.\n */\n public get name(): string {\n return sanitizePath(this.fileData.filename);\n }\n\n /**\n * Get file MIME type\n *\n * @example \"image/jpeg\", \"application/pdf\"\n */\n public get mimeType(): string {\n return this.fileData.mimetype;\n }\n\n /**\n * Get file extension (lowercase, without dot)\n *\n * @example \"jpg\", \"png\", \"pdf\"\n */\n public get extension(): string {\n return path.extname(this.fileData.filename).replace(\".\", \"\").toLowerCase();\n }\n\n /**\n * Get file metadata\n */\n public async metadata(): Promise<UploadedFileMetadata> {\n const data: UploadedFileMetadata = {\n name: this.name,\n mimeType: this.mimeType,\n extension: this.extension,\n size: await this.size(),\n };\n\n if (this.isImage) {\n const dimensions = await this.dimensions();\n data.width = dimensions.width;\n data.height = dimensions.height;\n }\n\n return data;\n }\n\n /**\n * Get file size in bytes\n *\n * Buffers the file content if not already buffered.\n */\n public async size(): Promise<number> {\n const file = await this.buffer();\n return file.length;\n }\n\n /**\n * Get file buffer\n *\n * Caches the buffer after first call for efficiency.\n */\n public async buffer(): Promise<Buffer> {\n if (this.bufferedFileContent) {\n return this.bufferedFileContent;\n }\n\n this.bufferedFileContent = await this.fileData.toBuffer();\n return this.bufferedFileContent;\n }\n\n // ============================================================\n // File Type Checks\n // ============================================================\n\n /**\n * Check if file is an image based on MIME type\n */\n public get isImage(): boolean {\n return this.mimeType.startsWith(\"image\");\n }\n\n /**\n * Check if file is a video based on MIME type\n */\n public get isVideo(): boolean {\n return this.mimeType.startsWith(\"video\");\n }\n\n /**\n * Check if file is an audio based on MIME type\n */\n public get isAudio(): boolean {\n return this.mimeType.startsWith(\"audio\");\n }\n\n // ============================================================\n // Storage Driver Selection\n // ============================================================\n\n /**\n * Select a specific storage driver for this upload\n *\n * Returns this instance for chaining. The driver is used\n * when `save()` or `saveAs()` is called.\n *\n * @param driver - Driver name from storage configuration\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.use(\"s3\").save(\"avatars\");\n * await file.use(\"r2\").save(\"cdn/images\");\n * ```\n */\n public use(driver: StorageDriverName): this {\n this._storage = storage.use(driver) as ScopedStorage;\n return this;\n }\n\n // ============================================================\n // High-Level Image Transform API\n // ============================================================\n\n /**\n * Resize the image before saving\n *\n * Only applies to image files. Non-image files ignore this.\n *\n * @param width - Target width in pixels\n * @param height - Optional target height (maintains aspect ratio if omitted)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.resize(800).save(\"thumbnails\");\n * await file.resize(400, 400).save(\"avatars\");\n * ```\n */\n public resize(width: number, height?: number): this {\n this._imageOptions.resize = { width, height };\n return this;\n }\n\n /**\n * Set image output quality\n *\n * Quality affects file size and visual fidelity.\n * Only applies to formats that support quality (JPEG, WebP, AVIF).\n *\n * @param value - Quality value (1-100)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.quality(85).save(\"images\");\n * ```\n */\n public quality(value: number): this {\n if (value < 1 || value > 100) {\n throw new Error(\"Quality must be between 1 and 100\");\n }\n this._imageOptions.quality = value;\n return this;\n }\n\n /**\n * Convert image to a specific format\n *\n * Changes the output format and updates the file extension accordingly.\n *\n * @param format - Target image format (jpeg, png, webp, avif, etc.)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.format(\"webp\").save(\"images\");\n * await file.resize(800).format(\"avif\").quality(80).save(\"optimized\");\n * ```\n */\n public format(format: ImageFormat): this {\n this._imageOptions.format = format;\n return this;\n }\n\n /**\n * Rotate the image\n *\n * @param degrees - Rotation angle in degrees (positive = clockwise)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.rotate(90).save(\"rotated\");\n * ```\n */\n public rotate(degrees: number): this {\n this._imageOptions.rotate = degrees;\n return this;\n }\n\n /**\n * Apply blur effect to the image\n *\n * @param sigma - Blur intensity (default: 3, minimum: 0.3)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.blur(5).save(\"blurred\");\n * ```\n */\n public blur(sigma = 3): this {\n if (sigma < 0.3) {\n throw new Error(\"Blur sigma must be at least 0.3\");\n }\n this._imageOptions.blur = sigma;\n return this;\n }\n\n /**\n * Convert image to grayscale (black and white)\n *\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.grayscale().save(\"bw-images\");\n * ```\n */\n public grayscale(): this {\n this._imageOptions.grayscale = true;\n return this;\n }\n\n // ============================================================\n // Full Transform Control\n // ============================================================\n\n /**\n * Apply full image transformations\n *\n * Provides complete control over image processing. Can be used with:\n * - An options object for predefined transforms\n * - A callback function for chained operations\n *\n * @param config - Transform options or callback function\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * // Using options object\n * await file.transform({\n * resize: { width: 800, fit: \"inside\" },\n * quality: 85\n * }).save(\"images\");\n *\n * // Using callback for full control\n * await file.transform(img =>\n * img.resize({ width: 800 })\n * .watermark(\"logo.png\", { gravity: \"southeast\" })\n * .sharpen()\n * ).save(\"products\");\n * ```\n */\n public transform(config: ImageTransformOptions | ImageTransformCallback): this {\n this._transformConfig = config;\n return this;\n }\n\n /**\n * Get an Image instance for advanced manipulation\n *\n * Returns an Image instance from the file buffer for manual processing.\n * Use this when you need operations not covered by the fluent API.\n *\n * @returns Promise resolving to Image instance\n *\n * @example\n * ```typescript\n * const img = await file.toImage();\n * await img\n * .resize({ width: 800 })\n * .watermark(\"logo.png\", { gravity: \"southeast\" })\n * .save(\"path/to/output.jpg\");\n * ```\n */\n public async toImage(): Promise<Image> {\n return new Image(await this.buffer());\n }\n\n // ============================================================\n // Image Metadata\n // ============================================================\n\n /**\n * Get file width and height (only for images)\n *\n * @returns Dimensions object, or empty object if not an image\n */\n public async dimensions(): Promise<{ width?: number; height?: number }> {\n if (!this.isImage) {\n return {};\n }\n\n return new Image(await this.buffer()).dimensions();\n }\n\n // ============================================================\n // Validation\n // ============================================================\n\n /**\n * Validate file against the given options\n *\n * @param options - Validation rules\n * @throws Error if validation fails\n *\n * @example\n * ```typescript\n * await file.validate({\n * allowedMimeTypes: [\"image/jpeg\", \"image/png\"],\n * maxSize: 5 * 1024 * 1024 // 5MB\n * });\n * ```\n */\n public async validate(options: FileValidationOptions): Promise<void> {\n const { allowedMimeTypes, allowedExtensions, maxSize } = options;\n\n if (allowedMimeTypes && !allowedMimeTypes.includes(this.mimeType)) {\n throw new Error(\n `Invalid file type: ${this.mimeType}. Allowed types: ${allowedMimeTypes.join(\", \")}`,\n );\n }\n\n if (allowedExtensions && !allowedExtensions.includes(this.extension)) {\n throw new Error(\n `Invalid file extension: ${this.extension}. Allowed extensions: ${allowedExtensions.join(\", \")}`,\n );\n }\n\n if (maxSize) {\n const fileSize = await this.size();\n if (fileSize > maxSize) {\n throw new Error(`File too large: ${fileSize} bytes. Maximum allowed: ${maxSize} bytes`);\n }\n }\n }\n\n // ============================================================\n // Save Operations\n // ============================================================\n\n /**\n * Save the file to a directory with automatic naming\n * Keep in mind to use only relative path to the root of storage\n * If you are using local driver\n * Uses the configured naming strategy and prefix options to generate\n * the final path. Returns a StorageFile for accessing file metadata.\n *\n * @param directory - Target directory path\n * @param options - Save options (name, prefix, driver, validate)\n * @returns StorageFile instance with file metadata and operations\n *\n * @example\n * ```typescript\n * // Random name (default)\n * await file.save(\"avatars\");\n * // → avatars/x7k9m2p4.jpg\n *\n * // Original name\n * await file.save(\"avatars\", { name: \"original\" });\n * // → avatars/photo.jpg\n *\n * // With date directory\n * await file.save(\"avatars\", {\n * prefix: { format: \"yyyy/mm/dd\", as: \"directory\" }\n * });\n * // → avatars/2025/12/21/x7k9m2p4.jpg\n *\n * // Original name with datetime prefix\n * await file.save(\"avatars\", { name: \"original\", prefix: true });\n * // → avatars/21-12-2025-16-45-30-photo.jpg\n * ```\n */\n public async save(directory: string, options?: SaveOptions): Promise<StorageFile> {\n // Validate if requested\n if (options?.validate) {\n await this.validate(options.validate);\n }\n\n // Resolve filename and prefix\n const filename = this.resolveFilename(options);\n const prefix = this.resolvePrefix(options?.prefix);\n const location = this.buildLocation(directory, prefix, filename);\n\n return this.saveToLocation(location, options?.driver);\n }\n\n /**\n * Save the file to an explicit path\n *\n * Unlike `save()`, this method uses the exact path you provide.\n * No automatic naming or prefix is applied.\n *\n * @param location - Full file path (directory + filename)\n * @param options - Save options (driver, validate)\n * @returns StorageFile instance with file metadata\n *\n * @example\n * ```typescript\n * await file.saveAs(\"avatars/profile-123.png\");\n * await file.saveAs(\"products/2025/featured-image.webp\");\n * ```\n */\n public async saveAs(location: string, options?: SaveAsOptions): Promise<StorageFile> {\n // Validate if requested\n if (options?.validate) {\n await this.validate(options.validate);\n }\n\n return this.saveToLocation(location, options?.driver);\n }\n\n /**\n * Get the StorageFile reference if file has been saved\n *\n * Returns undefined if file hasn't been saved yet.\n */\n public get storageFile(): StorageFile | undefined {\n return this._storageFile;\n }\n\n // ============================================================\n // Internal Helpers\n // ============================================================\n\n /**\n * Execute save to the specified location\n * @internal\n */\n protected async saveToLocation(\n location: string,\n driver?: StorageDriverName,\n ): Promise<StorageFile> {\n // Get file content (apply transforms if image)\n const content = await this.getProcessedContent();\n\n // Select storage\n const storageInstance = this.resolveStorage(driver);\n\n // Determine final location (adjust extension if format changed)\n const finalLocation = this.adjustLocationForFormat(location);\n\n // Save to storage\n this._storageFile = await storageInstance.put(content, finalLocation, {\n mimeType: this.getFinalMimeType(),\n });\n\n // Store hash from StorageFile\n const info = await this._storageFile.data();\n this.hash = info.hash || \"\";\n\n return this._storageFile;\n }\n\n /**\n * Get processed content (with transforms applied if applicable)\n * @internal\n */\n protected async getProcessedContent(): Promise<Buffer> {\n const content = await this.buffer();\n\n // If not an image or no transforms, return as-is\n if (!this.isImage || !this.hasTransforms()) {\n return content;\n }\n\n // Create Image and apply transforms\n let img = new Image(content);\n\n // Apply transform callback or options\n if (typeof this._transformConfig === \"function\") {\n img = this._transformConfig(img);\n } else if (this._transformConfig) {\n img = img.apply(this._transformConfig);\n }\n\n // Apply high-level options on top\n img = this.applyImageOptions(img);\n\n return img.toBuffer();\n }\n\n /**\n * Apply high-level image options\n * @internal\n */\n protected applyImageOptions(img: Image): Image {\n const opts = this._imageOptions;\n\n if (opts.resize) {\n img = img.resize({ width: opts.resize.width, height: opts.resize.height });\n }\n\n if (opts.rotate !== undefined) {\n img = img.rotate(opts.rotate);\n }\n\n if (opts.blur !== undefined) {\n img = img.blur(opts.blur);\n }\n\n if (opts.grayscale) {\n img = img.grayscale();\n }\n\n if (opts.quality !== undefined) {\n img = img.quality(opts.quality);\n }\n\n if (opts.format) {\n img = img.format(opts.format);\n }\n\n return img;\n }\n\n /**\n * Check if any transforms are queued\n * @internal\n */\n protected hasTransforms(): boolean {\n return this._transformConfig !== undefined || Object.keys(this._imageOptions).length > 0;\n }\n\n /**\n * Resolve storage instance to use\n * @internal\n */\n protected resolveStorage(driver?: StorageDriverName): ScopedStorage | typeof storage {\n if (driver) {\n return storage.use(driver) as ScopedStorage;\n }\n\n return this._storage || storage;\n }\n\n /**\n * Resolve the filename based on options and config\n * @internal\n */\n protected resolveFilename(options?: SaveOptions): string {\n const namingStrategy: FileNamingStrategy = options?.name ?? uploadsConfig(\"name\") ?? \"random\";\n let baseName: string;\n\n if (namingStrategy === \"original\") {\n baseName = path.basename(this.name, path.extname(this.name));\n } else if (namingStrategy === \"random\") {\n const length = uploadsConfig(\"randomLength\");\n baseName = Random.string(length);\n } else {\n // Custom name - strip extension if provided, we'll add it\n baseName = path.basename(namingStrategy, path.extname(namingStrategy));\n }\n\n // Get extension (may be changed by format transform)\n const ext = this.getFinalExtension();\n\n return `${baseName}.${ext}`;\n }\n\n /**\n * Resolve prefix based on options and config\n * @internal\n */\n protected resolvePrefix(prefix?: PrefixConfig): string {\n // No prefix\n if (prefix === false || prefix === undefined) {\n const configPrefix = uploadsConfig(\"prefix\");\n if (!configPrefix) {\n return \"\";\n }\n // Use config prefix if no explicit prefix provided\n prefix = configPrefix;\n }\n\n // Boolean true = use default format\n if (prefix === true) {\n const format = uploadsConfig(\"defaultPrefixFormat\")!;\n return this.formatDatePrefix(format, \"file\");\n }\n\n // String = static prefix\n if (typeof prefix === \"string\") {\n return prefix;\n }\n\n // PrefixOptions object\n const parts: string[] = [];\n\n // Add date format if specified\n if (prefix.format) {\n parts.push(this.formatDate(prefix.format));\n }\n\n // Add random string if specified\n if (prefix.randomLength) {\n parts.push(Random.string(prefix.randomLength));\n }\n\n const combined = parts.join(\"-\");\n const as = prefix.as ?? \"file\";\n\n if (as === \"directory\") {\n return combined ? `${combined}/` : \"\";\n }\n\n return combined ? `${combined}-` : \"\";\n }\n\n /**\n * Format a date prefix with the given format\n * @internal\n */\n protected formatDatePrefix(format: string, as: \"file\" | \"directory\"): string {\n const formatted = this.formatDate(format);\n return as === \"directory\" ? `${formatted}/` : `${formatted}-`;\n }\n\n /**\n * Format current date using token-based format string\n * @internal\n */\n protected formatDate(format: string): string {\n return dayjs().format(format);\n }\n\n /**\n * Build final location from directory, prefix, and filename\n * @internal\n */\n protected buildLocation(directory: string, prefix: string, filename: string): string {\n // Clean up directory (remove trailing slash)\n const dir = directory.replace(/\\/$/, \"\");\n\n // If prefix ends with /, it's a directory\n if (prefix.endsWith(\"/\")) {\n return `${dir}/${prefix}${filename}`;\n }\n\n // Otherwise prefix is part of filename\n return `${dir}/${prefix}${filename}`;\n }\n\n /**\n * Get final extension (accounting for format changes)\n * @internal\n */\n protected getFinalExtension(): string {\n if (this._imageOptions.format) {\n // Map formats to extensions\n const format = this._imageOptions.format;\n\n if (format === \"jpeg\") return \"jpg\";\n\n return format;\n }\n\n return this.extension;\n }\n\n /**\n * Get final MIME type (accounting for format changes)\n * @internal\n */\n protected getFinalMimeType(): string {\n if (this._imageOptions.format) {\n const format = this._imageOptions.format;\n\n if (format === \"jpeg\" || format === \"jpg\") return \"image/jpeg\";\n\n return `image/${format}`;\n }\n\n return this.mimeType;\n }\n\n /**\n * Adjust location to use correct extension if format changed\n * @internal\n */\n protected adjustLocationForFormat(location: string): string {\n if (!this._imageOptions.format) {\n return location;\n }\n\n const ext = this.getFinalExtension();\n const currentExt = path.extname(location);\n\n if (currentExt) {\n return location.replace(currentExt, `.${ext}`);\n }\n\n return `${location}.${ext}`;\n }\n\n // ============================================================\n // Serialization\n // ============================================================\n\n /**\n * Convert to JSON representation.\n *\n * **Synchronous, and that is the whole point.** `toJSON` is not an ordinary\n * method name — it is the hook `JSON.stringify` calls, and `JSON.stringify`\n * does not await. While this was `async`, every implicit serialization of an\n * upload produced `{}`: no base64, no size, not even the filename.\n *\n * Two call sites in core awaited it and worked (`http/response.ts:304`,\n * `utils/to-json.ts:9`). The ones that could not were the ones that mattered:\n * `cascade`'s model serializer calls `toJSON()` without awaiting, and\n * `Resource.toJSON()` is itself synchronous. Those paths were silently\n * broken. See canon `cd60b894`.\n *\n * **What is NOT here, and why.** `base64` forced the async — `buffer()` is\n * async — and it also put the entire file, inflated by a third, into any\n * response that happened to carry an upload. It is opt-in now via\n * {@link UploadedFile.toBase64}. `dimensions` costs an image decode; ask for\n * it with {@link UploadedFile.dimensions} when you want it.\n *\n * `size` is reported **only when the file has already been read**. Reading it\n * here would make the method async again, and guessing would be worse than\n * omitting: a wrong number is believed, an absent one is asked about. In\n * practice validation or `save()` has usually buffered the file long before\n * anything serializes it.\n */\n public toJSON(): UploadedFileJson {\n return {\n name: this.name,\n mimeType: this.mimeType,\n extension: this.extension,\n isImage: this.isImage,\n isVideo: this.isVideo,\n isAudio: this.isAudio,\n size: this.bufferedFileContent?.length,\n };\n }\n\n /**\n * The file's contents as base64.\n *\n * Explicit because it is expensive: it reads the whole file into memory and\n * grows it by a third. This used to happen on every serialization.\n */\n public async toBase64(): Promise<string> {\n return (await this.buffer()).toString(\"base64\");\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkGA,IAAa,eAAb,MAA0B;;;;;;;CA4CxB,AAAO,YAAY,AAAmB,UAAyB;EAAzB;cAhCxB;kBAMsB;uBAYgB,CAAC;EAenD,IAAI,CAAC,UAAU,UACb,MAAM,IAAI,MAAM,yCAAyC;CAE7D;;;;;;CAWA,IAAW,OAAe;EACxB,OAAO,aAAa,KAAK,SAAS,QAAQ;CAC5C;;;;;;CAOA,IAAW,WAAmB;EAC5B,OAAO,KAAK,SAAS;CACvB;;;;;;CAOA,IAAW,YAAoB;EAC7B,OAAO,KAAK,QAAQ,KAAK,SAAS,QAAQ,CAAC,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,YAAY;CAC3E;;;;CAKA,MAAa,WAA0C;EACrD,MAAM,OAA6B;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,WAAW,KAAK;GAChB,MAAM,MAAM,KAAK,KAAK;EACxB;EAEA,IAAI,KAAK,SAAS;GAChB,MAAM,aAAa,MAAM,KAAK,WAAW;GACzC,KAAK,QAAQ,WAAW;GACxB,KAAK,SAAS,WAAW;EAC3B;EAEA,OAAO;CACT;;;;;;CAOA,MAAa,OAAwB;EAEnC,QAAO,MADY,KAAK,OAAO,EACpB,CAAC;CACd;;;;;;CAOA,MAAa,SAA0B;EACrC,IAAI,KAAK,qBACP,OAAO,KAAK;EAGd,KAAK,sBAAsB,MAAM,KAAK,SAAS,SAAS;EACxD,OAAO,KAAK;CACd;;;;CASA,IAAW,UAAmB;EAC5B,OAAO,KAAK,SAAS,WAAW,OAAO;CACzC;;;;CAKA,IAAW,UAAmB;EAC5B,OAAO,KAAK,SAAS,WAAW,OAAO;CACzC;;;;CAKA,IAAW,UAAmB;EAC5B,OAAO,KAAK,SAAS,WAAW,OAAO;CACzC;;;;;;;;;;;;;;;;CAqBA,AAAO,IAAI,QAAiC;EAC1C,KAAK,WAAW,QAAQ,IAAI,MAAM;EAClC,OAAO;CACT;;;;;;;;;;;;;;;;CAqBA,AAAO,OAAO,OAAe,QAAuB;EAClD,KAAK,cAAc,SAAS;GAAE;GAAO;EAAO;EAC5C,OAAO;CACT;;;;;;;;;;;;;;;CAgBA,AAAO,QAAQ,OAAqB;EAClC,IAAI,QAAQ,KAAK,QAAQ,KACvB,MAAM,IAAI,MAAM,mCAAmC;EAErD,KAAK,cAAc,UAAU;EAC7B,OAAO;CACT;;;;;;;;;;;;;;;CAgBA,AAAO,OAAO,QAA2B;EACvC,KAAK,cAAc,SAAS;EAC5B,OAAO;CACT;;;;;;;;;;;;CAaA,AAAO,OAAO,SAAuB;EACnC,KAAK,cAAc,SAAS;EAC5B,OAAO;CACT;;;;;;;;;;;;CAaA,AAAO,KAAK,QAAQ,GAAS;EAC3B,IAAI,QAAQ,IACV,MAAM,IAAI,MAAM,iCAAiC;EAEnD,KAAK,cAAc,OAAO;EAC1B,OAAO;CACT;;;;;;;;;;;CAYA,AAAO,YAAkB;EACvB,KAAK,cAAc,YAAY;EAC/B,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCA,AAAO,UAAU,QAA8D;EAC7E,KAAK,mBAAmB;EACxB,OAAO;CACT;;;;;;;;;;;;;;;;;;CAmBA,MAAa,UAA0B;EACrC,OAAO,IAAI,MAAM,MAAM,KAAK,OAAO,CAAC;CACtC;;;;;;CAWA,MAAa,aAA2D;EACtE,IAAI,CAAC,KAAK,SACR,OAAO,CAAC;EAGV,OAAO,IAAI,MAAM,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW;CACnD;;;;;;;;;;;;;;;CAoBA,MAAa,SAAS,SAA+C;EACnE,MAAM,EAAE,kBAAkB,mBAAmB,YAAY;EAEzD,IAAI,oBAAoB,CAAC,iBAAiB,SAAS,KAAK,QAAQ,GAC9D,MAAM,IAAI,MACR,sBAAsB,KAAK,SAAS,mBAAmB,iBAAiB,KAAK,IAAI,GACnF;EAGF,IAAI,qBAAqB,CAAC,kBAAkB,SAAS,KAAK,SAAS,GACjE,MAAM,IAAI,MACR,2BAA2B,KAAK,UAAU,wBAAwB,kBAAkB,KAAK,IAAI,GAC/F;EAGF,IAAI,SAAS;GACX,MAAM,WAAW,MAAM,KAAK,KAAK;GACjC,IAAI,WAAW,SACb,MAAM,IAAI,MAAM,mBAAmB,SAAS,2BAA2B,QAAQ,OAAO;EAE1F;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCA,MAAa,KAAK,WAAmB,SAA6C;EAEhF,IAAI,SAAS,UACX,MAAM,KAAK,SAAS,QAAQ,QAAQ;EAItC,MAAM,WAAW,KAAK,gBAAgB,OAAO;EAC7C,MAAM,SAAS,KAAK,cAAc,SAAS,MAAM;EACjD,MAAM,WAAW,KAAK,cAAc,WAAW,QAAQ,QAAQ;EAE/D,OAAO,KAAK,eAAe,UAAU,SAAS,MAAM;CACtD;;;;;;;;;;;;;;;;;CAkBA,MAAa,OAAO,UAAkB,SAA+C;EAEnF,IAAI,SAAS,UACX,MAAM,KAAK,SAAS,QAAQ,QAAQ;EAGtC,OAAO,KAAK,eAAe,UAAU,SAAS,MAAM;CACtD;;;;;;CAOA,IAAW,cAAuC;EAChD,OAAO,KAAK;CACd;;;;;CAUA,MAAgB,eACd,UACA,QACsB;EAEtB,MAAM,UAAU,MAAM,KAAK,oBAAoB;EAG/C,MAAM,kBAAkB,KAAK,eAAe,MAAM;EAGlD,MAAM,gBAAgB,KAAK,wBAAwB,QAAQ;EAG3D,KAAK,eAAe,MAAM,gBAAgB,IAAI,SAAS,eAAe,EACpE,UAAU,KAAK,iBAAiB,EAClC,CAAC;EAGD,MAAM,OAAO,MAAM,KAAK,aAAa,KAAK;EAC1C,KAAK,OAAO,KAAK,QAAQ;EAEzB,OAAO,KAAK;CACd;;;;;CAMA,MAAgB,sBAAuC;EACrD,MAAM,UAAU,MAAM,KAAK,OAAO;EAGlC,IAAI,CAAC,KAAK,WAAW,CAAC,KAAK,cAAc,GACvC,OAAO;EAIT,IAAI,MAAM,IAAI,MAAM,OAAO;EAG3B,IAAI,OAAO,KAAK,qBAAqB,YACnC,MAAM,KAAK,iBAAiB,GAAG;OAC1B,IAAI,KAAK,kBACd,MAAM,IAAI,MAAM,KAAK,gBAAgB;EAIvC,MAAM,KAAK,kBAAkB,GAAG;EAEhC,OAAO,IAAI,SAAS;CACtB;;;;;CAMA,AAAU,kBAAkB,KAAmB;EAC7C,MAAM,OAAO,KAAK;EAElB,IAAI,KAAK,QACP,MAAM,IAAI,OAAO;GAAE,OAAO,KAAK,OAAO;GAAO,QAAQ,KAAK,OAAO;EAAO,CAAC;EAG3E,IAAI,KAAK,WAAW,QAClB,MAAM,IAAI,OAAO,KAAK,MAAM;EAG9B,IAAI,KAAK,SAAS,QAChB,MAAM,IAAI,KAAK,KAAK,IAAI;EAG1B,IAAI,KAAK,WACP,MAAM,IAAI,UAAU;EAGtB,IAAI,KAAK,YAAY,QACnB,MAAM,IAAI,QAAQ,KAAK,OAAO;EAGhC,IAAI,KAAK,QACP,MAAM,IAAI,OAAO,KAAK,MAAM;EAG9B,OAAO;CACT;;;;;CAMA,AAAU,gBAAyB;EACjC,OAAO,KAAK,qBAAqB,UAAa,OAAO,KAAK,KAAK,aAAa,CAAC,CAAC,SAAS;CACzF;;;;;CAMA,AAAU,eAAe,QAA4D;EACnF,IAAI,QACF,OAAO,QAAQ,IAAI,MAAM;EAG3B,OAAO,KAAK,YAAY;CAC1B;;;;;CAMA,AAAU,gBAAgB,SAA+B;EACvD,MAAM,iBAAqC,SAAS,QAAQ,cAAc,MAAM,KAAK;EACrF,IAAI;EAEJ,IAAI,mBAAmB,YACrB,WAAW,KAAK,SAAS,KAAK,MAAM,KAAK,QAAQ,KAAK,IAAI,CAAC;OACtD,IAAI,mBAAmB,UAAU;GACtC,MAAM,SAAS,cAAc,cAAc;GAC3C,WAAW,OAAO,OAAO,MAAM;EACjC,OAEE,WAAW,KAAK,SAAS,gBAAgB,KAAK,QAAQ,cAAc,CAAC;EAIvE,MAAM,MAAM,KAAK,kBAAkB;EAEnC,OAAO,GAAG,SAAS,GAAG;CACxB;;;;;CAMA,AAAU,cAAc,QAA+B;EAErD,IAAI,WAAW,SAAS,WAAW,QAAW;GAC5C,MAAM,eAAe,cAAc,QAAQ;GAC3C,IAAI,CAAC,cACH,OAAO;GAGT,SAAS;EACX;EAGA,IAAI,WAAW,MAAM;GACnB,MAAM,SAAS,cAAc,qBAAqB;GAClD,OAAO,KAAK,iBAAiB,QAAQ,MAAM;EAC7C;EAGA,IAAI,OAAO,WAAW,UACpB,OAAO;EAIT,MAAM,QAAkB,CAAC;EAGzB,IAAI,OAAO,QACT,MAAM,KAAK,KAAK,WAAW,OAAO,MAAM,CAAC;EAI3C,IAAI,OAAO,cACT,MAAM,KAAK,OAAO,OAAO,OAAO,YAAY,CAAC;EAG/C,MAAM,WAAW,MAAM,KAAK,GAAG;EAG/B,KAFW,OAAO,MAAM,YAEb,aACT,OAAO,WAAW,GAAG,SAAS,KAAK;EAGrC,OAAO,WAAW,GAAG,SAAS,KAAK;CACrC;;;;;CAMA,AAAU,iBAAiB,QAAgB,IAAkC;EAC3E,MAAM,YAAY,KAAK,WAAW,MAAM;EACxC,OAAO,OAAO,cAAc,GAAG,UAAU,KAAK,GAAG,UAAU;CAC7D;;;;;CAMA,AAAU,WAAW,QAAwB;EAC3C,OAAO,MAAM,CAAC,CAAC,OAAO,MAAM;CAC9B;;;;;CAMA,AAAU,cAAc,WAAmB,QAAgB,UAA0B;EAEnF,MAAM,MAAM,UAAU,QAAQ,OAAO,EAAE;EAGvC,IAAI,OAAO,SAAS,GAAG,GACrB,OAAO,GAAG,IAAI,GAAG,SAAS;EAI5B,OAAO,GAAG,IAAI,GAAG,SAAS;CAC5B;;;;;CAMA,AAAU,oBAA4B;EACpC,IAAI,KAAK,cAAc,QAAQ;GAE7B,MAAM,SAAS,KAAK,cAAc;GAElC,IAAI,WAAW,QAAQ,OAAO;GAE9B,OAAO;EACT;EAEA,OAAO,KAAK;CACd;;;;;CAMA,AAAU,mBAA2B;EACnC,IAAI,KAAK,cAAc,QAAQ;GAC7B,MAAM,SAAS,KAAK,cAAc;GAElC,IAAI,WAAW,UAAU,WAAW,OAAO,OAAO;GAElD,OAAO,SAAS;EAClB;EAEA,OAAO,KAAK;CACd;;;;;CAMA,AAAU,wBAAwB,UAA0B;EAC1D,IAAI,CAAC,KAAK,cAAc,QACtB,OAAO;EAGT,MAAM,MAAM,KAAK,kBAAkB;EACnC,MAAM,aAAa,KAAK,QAAQ,QAAQ;EAExC,IAAI,YACF,OAAO,SAAS,QAAQ,YAAY,IAAI,KAAK;EAG/C,OAAO,GAAG,SAAS,GAAG;CACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCA,AAAO,SAA2B;EAChC,OAAO;GACL,MAAM,KAAK;GACX,UAAU,KAAK;GACf,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,SAAS,KAAK;GACd,SAAS,KAAK;GACd,MAAM,KAAK,qBAAqB;EAClC;CACF;;;;;;;CAQA,MAAa,WAA4B;EACvC,QAAQ,MAAM,KAAK,OAAO,EAAC,CAAE,SAAS,QAAQ;CAChD;AACF"}
|
|
1
|
+
{"version":3,"file":"uploaded-file.mjs","names":[],"sources":["../../../../../../../../packages/core/src/http/uploaded-file.ts"],"sourcesContent":["import type { MultipartFile } from \"@fastify/multipart\";\nimport { Random } from \"@mongez/reinforcements\";\nimport dayjs from \"dayjs\";\nimport path from \"path\";\nimport { Image, type ImageFormat, type ImageTransformOptions } from \"../image\";\nimport { storage, type ScopedStorage, type StorageDriverName, type StorageFile } from \"../storage\";\nimport { sanitizePath } from \"../utils/paths\";\nimport { uploadsConfig } from \"./uploads-config\";\nimport type {\n FileNamingStrategy,\n ImageTransformCallback,\n PrefixConfig,\n SaveAsOptions,\n SaveOptions,\n UploadedFileImageOptions,\n} from \"./uploads-types\";\n\ntype UploadedFileMetadata = {\n name: string;\n mimeType: string;\n extension: string;\n size: number;\n width?: number;\n height?: number;\n};\n\n/**\n * What an `UploadedFile` looks like on the wire.\n *\n * Every member is knowable **without reading the file**, which is what lets\n * {@link UploadedFile.toJSON} be synchronous — see the note there. `size` is\n * the one exception and it is optional for exactly that reason: present when\n * the file has already been buffered, absent otherwise, never guessed.\n */\nexport type UploadedFileJson = {\n name: string;\n mimeType: string;\n extension: string;\n isImage: boolean;\n isVideo: boolean;\n isAudio: boolean;\n size?: number;\n};\n\n/**\n * Options for validating file before saving\n */\nexport type FileValidationOptions = {\n /**\n * List of allowed MIME types\n *\n * @example [\"image/jpeg\", \"image/png\", \"image/webp\"]\n */\n allowedMimeTypes?: string[];\n\n /**\n * List of allowed file extensions (without dot)\n *\n * @example [\"jpg\", \"jpeg\", \"png\", \"webp\"]\n */\n allowedExtensions?: string[];\n\n /**\n * Maximum file size in bytes\n *\n * @example 5 * 1024 * 1024 // 5MB\n */\n maxSize?: number;\n};\n\n/**\n * UploadedFile - Handles multipart file uploads with storage and image integration\n *\n * Provides a fluent API for validating, transforming, and saving uploaded files\n * to various storage drivers (local, S3, R2, etc.).\n *\n * @example\n * ```typescript\n * // Simple save with random name\n * const file = await uploadedFile.save(\"avatars\");\n *\n * // Original name with date prefix\n * const file = await uploadedFile.save(\"avatars\", {\n * name: \"original\",\n * prefix: { format: \"yyyy/mm/dd\", as: \"directory\" }\n * });\n *\n * // With image transformations\n * const file = await uploadedFile\n * .resize(800, 600)\n * .quality(85)\n * .format(\"webp\")\n * .save(\"avatars\");\n *\n * // Different storage driver\n * const file = await uploadedFile.use(\"s3\").save(\"avatars\");\n * ```\n */\nexport class UploadedFile {\n /**\n * File buffered content\n * @internal\n */\n protected bufferedFileContent?: Buffer;\n\n /**\n * Upload file hash (SHA-256)\n *\n * Populated after file is saved.\n */\n public hash = \"\";\n\n /**\n * Selected storage driver\n * @internal\n */\n protected _storage: ScopedStorage = storage;\n\n /**\n * Saved StorageFile reference\n * @internal\n */\n protected _storageFile?: StorageFile;\n\n /**\n * Queued image options (high-level API)\n * @internal\n */\n protected _imageOptions: UploadedFileImageOptions = {};\n\n /**\n * Full transform options or callback\n * @internal\n */\n protected _transformConfig?: ImageTransformOptions | ImageTransformCallback;\n\n /**\n * Create a new UploadedFile instance\n *\n * @param fileData - Multipart file data from Fastify\n * @throws Error if file data is invalid\n */\n public constructor(protected readonly fileData: MultipartFile) {\n if (!fileData?.filename) {\n throw new Error(\"Invalid file data: filename is required\");\n }\n }\n\n // ============================================================\n // File Properties\n // ============================================================\n\n /**\n * Get file name (sanitized)\n *\n * Returns the original filename with special characters removed/replaced.\n */\n public get name(): string {\n return sanitizePath(this.fileData.filename);\n }\n\n /**\n * Get file MIME type\n *\n * @example \"image/jpeg\", \"application/pdf\"\n */\n public get mimeType(): string {\n return this.fileData.mimetype;\n }\n\n /**\n * Get file extension (lowercase, without dot)\n *\n * @example \"jpg\", \"png\", \"pdf\"\n */\n public get extension(): string {\n return path.extname(this.fileData.filename).replace(\".\", \"\").toLowerCase();\n }\n\n /**\n * Get file metadata\n */\n public async metadata(): Promise<UploadedFileMetadata> {\n const data: UploadedFileMetadata = {\n name: this.name,\n mimeType: this.mimeType,\n extension: this.extension,\n size: await this.size(),\n };\n\n if (this.isImage) {\n const dimensions = await this.dimensions();\n data.width = dimensions.width;\n data.height = dimensions.height;\n }\n\n return data;\n }\n\n /**\n * Get file size in bytes\n *\n * Buffers the file content if not already buffered.\n */\n public async size(): Promise<number> {\n const file = await this.buffer();\n return file.length;\n }\n\n /**\n * Get file buffer\n *\n * Caches the buffer after first call for efficiency.\n */\n public async buffer(): Promise<Buffer> {\n if (this.bufferedFileContent) {\n return this.bufferedFileContent;\n }\n\n this.bufferedFileContent = await this.fileData.toBuffer();\n return this.bufferedFileContent;\n }\n\n // ============================================================\n // File Type Checks\n // ============================================================\n\n /**\n * Check if file is an image based on MIME type\n */\n public get isImage(): boolean {\n return this.mimeType.startsWith(\"image\");\n }\n\n /**\n * Check if file is a video based on MIME type\n */\n public get isVideo(): boolean {\n return this.mimeType.startsWith(\"video\");\n }\n\n /**\n * Check if file is an audio based on MIME type\n */\n public get isAudio(): boolean {\n return this.mimeType.startsWith(\"audio\");\n }\n\n // ============================================================\n // Storage Driver Selection\n // ============================================================\n\n /**\n * Select a specific storage driver for this upload\n *\n * Returns this instance for chaining. The driver is used\n * when `save()` or `saveAs()` is called.\n *\n * @param driver - Driver name from storage configuration\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.use(\"s3\").save(\"avatars\");\n * await file.use(\"r2\").save(\"cdn/images\");\n * ```\n */\n public use(driver: StorageDriverName): this {\n this._storage = storage.use(driver) as ScopedStorage;\n return this;\n }\n\n // ============================================================\n // High-Level Image Transform API\n // ============================================================\n\n /**\n * Resize the image before saving\n *\n * Only applies to image files. Non-image files ignore this.\n *\n * @param width - Target width in pixels\n * @param height - Optional target height (maintains aspect ratio if omitted)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.resize(800).save(\"thumbnails\");\n * await file.resize(400, 400).save(\"avatars\");\n * ```\n */\n public resize(width: number, height?: number): this {\n this._imageOptions.resize = { width, height };\n return this;\n }\n\n /**\n * Set image output quality\n *\n * Quality affects file size and visual fidelity.\n * Only applies to formats that support quality (JPEG, WebP, AVIF).\n *\n * @param value - Quality value (1-100)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.quality(85).save(\"images\");\n * ```\n */\n public quality(value: number): this {\n if (value < 1 || value > 100) {\n throw new Error(\"Quality must be between 1 and 100\");\n }\n this._imageOptions.quality = value;\n return this;\n }\n\n /**\n * Convert image to a specific format\n *\n * Changes the output format and updates the file extension accordingly.\n *\n * @param format - Target image format (jpeg, png, webp, avif, etc.)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.format(\"webp\").save(\"images\");\n * await file.resize(800).format(\"avif\").quality(80).save(\"optimized\");\n * ```\n */\n public format(format: ImageFormat): this {\n this._imageOptions.format = format;\n return this;\n }\n\n /**\n * Rotate the image\n *\n * @param degrees - Rotation angle in degrees (positive = clockwise)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.rotate(90).save(\"rotated\");\n * ```\n */\n public rotate(degrees: number): this {\n this._imageOptions.rotate = degrees;\n return this;\n }\n\n /**\n * Apply blur effect to the image\n *\n * @param sigma - Blur intensity (default: 3, minimum: 0.3)\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.blur(5).save(\"blurred\");\n * ```\n */\n public blur(sigma = 3): this {\n if (sigma < 0.3) {\n throw new Error(\"Blur sigma must be at least 0.3\");\n }\n this._imageOptions.blur = sigma;\n return this;\n }\n\n /**\n * Convert image to grayscale (black and white)\n *\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * await file.grayscale().save(\"bw-images\");\n * ```\n */\n public grayscale(): this {\n this._imageOptions.grayscale = true;\n return this;\n }\n\n // ============================================================\n // Full Transform Control\n // ============================================================\n\n /**\n * Apply full image transformations\n *\n * Provides complete control over image processing. Can be used with:\n * - An options object for predefined transforms\n * - A callback function for chained operations\n *\n * @param config - Transform options or callback function\n * @returns This instance for chaining\n *\n * @example\n * ```typescript\n * // Using options object\n * await file.transform({\n * resize: { width: 800, fit: \"inside\" },\n * quality: 85\n * }).save(\"images\");\n *\n * // Using callback for full control\n * await file.transform(img =>\n * img.resize({ width: 800 })\n * .watermark(\"logo.png\", { gravity: \"southeast\" })\n * .sharpen()\n * ).save(\"products\");\n * ```\n */\n public transform(config: ImageTransformOptions | ImageTransformCallback): this {\n this._transformConfig = config;\n return this;\n }\n\n /**\n * Get an Image instance for advanced manipulation\n *\n * Returns an Image instance from the file buffer for manual processing.\n * Use this when you need operations not covered by the fluent API.\n *\n * @returns Promise resolving to Image instance\n *\n * @example\n * ```typescript\n * const img = await file.toImage();\n * await img\n * .resize({ width: 800 })\n * .watermark(\"logo.png\", { gravity: \"southeast\" })\n * .save(\"path/to/output.jpg\");\n * ```\n */\n public async toImage(): Promise<Image> {\n return new Image(await this.buffer());\n }\n\n // ============================================================\n // Image Metadata\n // ============================================================\n\n /**\n * Get file width and height (only for images)\n *\n * @returns Dimensions object, or empty object if not an image\n */\n public async dimensions(): Promise<{ width?: number; height?: number }> {\n if (!this.isImage) {\n return {};\n }\n\n return new Image(await this.buffer()).dimensions();\n }\n\n // ============================================================\n // Validation\n // ============================================================\n\n /**\n * Validate file against the given options\n *\n * @param options - Validation rules\n * @throws Error if validation fails\n *\n * @example\n * ```typescript\n * await file.validate({\n * allowedMimeTypes: [\"image/jpeg\", \"image/png\"],\n * maxSize: 5 * 1024 * 1024 // 5MB\n * });\n * ```\n */\n public async validate(options: FileValidationOptions): Promise<void> {\n const { allowedMimeTypes, allowedExtensions, maxSize } = options;\n\n if (allowedMimeTypes && !allowedMimeTypes.includes(this.mimeType)) {\n throw new Error(\n `Invalid file type: ${this.mimeType}. Allowed types: ${allowedMimeTypes.join(\", \")}`,\n );\n }\n\n if (allowedExtensions && !allowedExtensions.includes(this.extension)) {\n throw new Error(\n `Invalid file extension: ${this.extension}. Allowed extensions: ${allowedExtensions.join(\", \")}`,\n );\n }\n\n if (maxSize) {\n const fileSize = await this.size();\n if (fileSize > maxSize) {\n throw new Error(`File too large: ${fileSize} bytes. Maximum allowed: ${maxSize} bytes`);\n }\n }\n }\n\n // ============================================================\n // Save Operations\n // ============================================================\n\n /**\n * Save the file to a directory with automatic naming\n * Keep in mind to use only relative path to the root of storage\n * If you are using local driver\n * Uses the configured naming strategy and prefix options to generate\n * the final path. Returns a StorageFile for accessing file metadata.\n *\n * @param directory - Target directory path\n * @param options - Save options (name, prefix, driver, validate)\n * @returns StorageFile instance with file metadata and operations\n *\n * @example\n * ```typescript\n * // Random name (default)\n * await file.save(\"avatars\");\n * // → avatars/x7k9m2p4.jpg\n *\n * // Original name\n * await file.save(\"avatars\", { name: \"original\" });\n * // → avatars/photo.jpg\n *\n * // With date directory\n * await file.save(\"avatars\", {\n * prefix: { format: \"yyyy/mm/dd\", as: \"directory\" }\n * });\n * // → avatars/2025/12/21/x7k9m2p4.jpg\n *\n * // Original name with datetime prefix\n * await file.save(\"avatars\", { name: \"original\", prefix: true });\n * // → avatars/21-12-2025-16-45-30-photo.jpg\n * ```\n */\n public async save(directory: string, options?: SaveOptions): Promise<StorageFile> {\n // Validate if requested\n if (options?.validate) {\n await this.validate(options.validate);\n }\n\n // Resolve filename and prefix\n const filename = this.resolveFilename(options);\n const prefix = this.resolvePrefix(options?.prefix);\n const location = this.buildLocation(directory, prefix, filename);\n\n return this.saveToLocation(location, options?.driver);\n }\n\n /**\n * Save the file to an explicit path\n *\n * Unlike `save()`, this method uses the exact path you provide.\n * No automatic naming or prefix is applied.\n *\n * @param location - Full file path (directory + filename)\n * @param options - Save options (driver, validate)\n * @returns StorageFile instance with file metadata\n *\n * @example\n * ```typescript\n * await file.saveAs(\"avatars/profile-123.png\");\n * await file.saveAs(\"products/2025/featured-image.webp\");\n * ```\n */\n public async saveAs(location: string, options?: SaveAsOptions): Promise<StorageFile> {\n // Validate if requested\n if (options?.validate) {\n await this.validate(options.validate);\n }\n\n return this.saveToLocation(location, options?.driver);\n }\n\n /**\n * Get the StorageFile reference if file has been saved\n *\n * Returns undefined if file hasn't been saved yet.\n */\n public get storageFile(): StorageFile | undefined {\n return this._storageFile;\n }\n\n // ============================================================\n // Internal Helpers\n // ============================================================\n\n /**\n * Execute save to the specified location\n * @internal\n */\n protected async saveToLocation(\n location: string,\n driver?: StorageDriverName,\n ): Promise<StorageFile> {\n // Get file content (apply transforms if image)\n const content = await this.getProcessedContent();\n\n // Select storage\n const storageInstance = this.resolveStorage(driver);\n\n // Determine final location (adjust extension if format changed)\n const finalLocation = this.adjustLocationForFormat(location);\n\n // Save to storage\n this._storageFile = await storageInstance.put(content, finalLocation, {\n mimeType: this.getFinalMimeType(),\n });\n\n // Store hash from StorageFile\n const info = await this._storageFile.data();\n this.hash = info.hash || \"\";\n\n return this._storageFile;\n }\n\n /**\n * Get processed content (with transforms applied if applicable)\n * @internal\n */\n protected async getProcessedContent(): Promise<Buffer> {\n const content = await this.buffer();\n\n // If not an image or no transforms, return as-is\n if (!this.isImage || !this.hasTransforms()) {\n return content;\n }\n\n // Create Image and apply transforms\n let img = new Image(content);\n\n // Apply transform callback or options\n if (typeof this._transformConfig === \"function\") {\n img = this._transformConfig(img);\n } else if (this._transformConfig) {\n img = img.apply(this._transformConfig);\n }\n\n // Apply high-level options on top\n img = this.applyImageOptions(img);\n\n return img.toBuffer();\n }\n\n /**\n * Apply high-level image options\n * @internal\n */\n protected applyImageOptions(img: Image): Image {\n const opts = this._imageOptions;\n\n if (opts.resize) {\n img = img.resize({ width: opts.resize.width, height: opts.resize.height });\n }\n\n if (opts.rotate !== undefined) {\n img = img.rotate(opts.rotate);\n }\n\n if (opts.blur !== undefined) {\n img = img.blur(opts.blur);\n }\n\n if (opts.grayscale) {\n img = img.grayscale();\n }\n\n if (opts.quality !== undefined) {\n img = img.quality(opts.quality);\n }\n\n if (opts.format) {\n img = img.format(opts.format);\n }\n\n return img;\n }\n\n /**\n * Check if any transforms are queued\n * @internal\n */\n protected hasTransforms(): boolean {\n return this._transformConfig !== undefined || Object.keys(this._imageOptions).length > 0;\n }\n\n /**\n * Resolve storage instance to use\n * @internal\n */\n protected resolveStorage(driver?: StorageDriverName): ScopedStorage | typeof storage {\n if (driver) {\n return storage.use(driver) as ScopedStorage;\n }\n\n return this._storage || storage;\n }\n\n /**\n * Resolve the filename based on options and config\n * @internal\n */\n protected resolveFilename(options?: SaveOptions): string {\n const namingStrategy: FileNamingStrategy = options?.name ?? uploadsConfig(\"name\") ?? \"random\";\n let baseName: string;\n\n if (namingStrategy === \"original\") {\n baseName = path.basename(this.name, path.extname(this.name));\n } else if (namingStrategy === \"random\") {\n const length = uploadsConfig(\"randomLength\");\n baseName = Random.string(length);\n } else {\n // Custom name - strip extension if provided, we'll add it\n baseName = path.basename(namingStrategy, path.extname(namingStrategy));\n }\n\n // Get extension (may be changed by format transform)\n const ext = this.getFinalExtension();\n\n return `${baseName}.${ext}`;\n }\n\n /**\n * Resolve prefix based on options and config\n * @internal\n */\n protected resolvePrefix(prefix?: PrefixConfig): string {\n // No prefix\n if (prefix === false || prefix === undefined) {\n const configPrefix = uploadsConfig(\"prefix\");\n if (!configPrefix) {\n return \"\";\n }\n // Use config prefix if no explicit prefix provided\n prefix = configPrefix;\n }\n\n // Boolean true = use default format\n if (prefix === true) {\n const format = uploadsConfig(\"defaultPrefixFormat\")!;\n return this.formatDatePrefix(format, \"file\");\n }\n\n // String = static prefix\n if (typeof prefix === \"string\") {\n return prefix;\n }\n\n // PrefixOptions object\n const parts: string[] = [];\n\n // Add date format if specified\n if (prefix.format) {\n parts.push(this.formatDate(prefix.format));\n }\n\n // Add random string if specified\n if (prefix.randomLength) {\n parts.push(Random.string(prefix.randomLength));\n }\n\n const combined = parts.join(\"-\");\n const as = prefix.as ?? \"file\";\n\n if (as === \"directory\") {\n return combined ? `${combined}/` : \"\";\n }\n\n return combined ? `${combined}-` : \"\";\n }\n\n /**\n * Format a date prefix with the given format\n * @internal\n */\n protected formatDatePrefix(format: string, as: \"file\" | \"directory\"): string {\n const formatted = this.formatDate(format);\n return as === \"directory\" ? `${formatted}/` : `${formatted}-`;\n }\n\n /**\n * Format current date using token-based format string\n * @internal\n */\n protected formatDate(format: string): string {\n return dayjs().format(format);\n }\n\n /**\n * Build final location from directory, prefix, and filename\n * @internal\n */\n protected buildLocation(directory: string, prefix: string, filename: string): string {\n // Clean up directory (remove trailing slash)\n const dir = directory.replace(/\\/$/, \"\");\n\n // If prefix ends with /, it's a directory\n if (prefix.endsWith(\"/\")) {\n return `${dir}/${prefix}${filename}`;\n }\n\n // Otherwise prefix is part of filename\n return `${dir}/${prefix}${filename}`;\n }\n\n /**\n * Get final extension (accounting for format changes)\n * @internal\n */\n protected getFinalExtension(): string {\n if (this._imageOptions.format) {\n // Map formats to extensions\n const format = this._imageOptions.format;\n\n if (format === \"jpeg\") return \"jpg\";\n\n return format;\n }\n\n return this.extension;\n }\n\n /**\n * Get final MIME type (accounting for format changes)\n * @internal\n */\n protected getFinalMimeType(): string {\n if (this._imageOptions.format) {\n const format = this._imageOptions.format;\n\n if (format === \"jpeg\" || format === \"jpg\") return \"image/jpeg\";\n\n return `image/${format}`;\n }\n\n return this.mimeType;\n }\n\n /**\n * Adjust location to use correct extension if format changed\n * @internal\n */\n protected adjustLocationForFormat(location: string): string {\n if (!this._imageOptions.format) {\n return location;\n }\n\n const ext = this.getFinalExtension();\n const currentExt = path.extname(location);\n\n if (currentExt) {\n return location.replace(currentExt, `.${ext}`);\n }\n\n return `${location}.${ext}`;\n }\n\n // ============================================================\n // Serialization\n // ============================================================\n\n /**\n * Convert to JSON representation.\n *\n * **Synchronous, and that is the whole point.** `toJSON` is not an ordinary\n * method name — it is the hook `JSON.stringify` calls, and `JSON.stringify`\n * does not await. While this was `async`, every implicit serialization of an\n * upload produced `{}`: no base64, no size, not even the filename.\n *\n * Two call sites in core awaited it and worked (`http/response.ts:304`,\n * `utils/to-json.ts:9`). The ones that could not were the ones that mattered:\n * `cascade`'s model serializer calls `toJSON()` without awaiting, and\n * `Resource.toJSON()` is itself synchronous. Those paths were silently\n * broken. See canon `cd60b894`.\n *\n * **What is NOT here, and why.** `base64` forced the async — `buffer()` is\n * async — and it also put the entire file, inflated by a third, into any\n * response that happened to carry an upload. It is opt-in now via\n * {@link UploadedFile.toBase64}. `dimensions` costs an image decode; ask for\n * it with {@link UploadedFile.dimensions} when you want it.\n *\n * `size` is reported **only when the file has already been read**. Reading it\n * here would make the method async again, and guessing would be worse than\n * omitting: a wrong number is believed, an absent one is asked about. In\n * practice validation or `save()` has usually buffered the file long before\n * anything serializes it.\n */\n public toJSON(): UploadedFileJson {\n return {\n name: this.name,\n mimeType: this.mimeType,\n extension: this.extension,\n isImage: this.isImage,\n isVideo: this.isVideo,\n isAudio: this.isAudio,\n size: this.bufferedFileContent?.length,\n };\n }\n\n /**\n * The file's contents as base64.\n *\n * Explicit because it is expensive: it reads the whole file into memory and\n * grows it by a third. This used to happen on every serialization.\n */\n public async toBase64(): Promise<string> {\n return (await this.buffer()).toString(\"base64\");\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-image-format.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"detect-image-format.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/detect-image-format.ts"],"sourcesContent":["import type { DetectedImageFormat, VariantSourceFormat } from \"./image-variant-types\";\n\n/**\n * Bytes `detectImageFormat` needs to see\n */\nexport const IMAGE_SIGNATURE_BYTES = 512;\n\nfunction startsWith(buffer: Buffer, signature: number[], offset = 0): boolean {\n return signature.every((byte, index) => buffer[offset + index] === byte);\n}\n\nfunction ascii(buffer: Buffer, start: number, end: number): string {\n return buffer.subarray(start, end).toString(\"latin1\");\n}\n\n/**\n * Detect an image format from its magic bytes, never from the file name.\n *\n * Recognises the variant sources (jpeg, png, webp, still avif) and the formats\n * that are refused but worth naming (gif, svg). An animated avif sequence\n * (`avis` brand) is not reported as avif.\n *\n * @returns the format, or `undefined` for anything else\n */\nexport function detectImageFormat(buffer: Buffer): DetectedImageFormat | undefined {\n if (startsWith(buffer, [0xff, 0xd8, 0xff])) return \"jpeg\";\n\n if (startsWith(buffer, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) return \"png\";\n\n if (ascii(buffer, 0, 4) === \"RIFF\" && ascii(buffer, 8, 12) === \"WEBP\") return \"webp\";\n\n if (ascii(buffer, 0, 6) === \"GIF87a\" || ascii(buffer, 0, 6) === \"GIF89a\") return \"gif\";\n\n if (ascii(buffer, 4, 8) === \"ftyp\" && ascii(buffer, 8, 12) === \"avif\") return \"avif\";\n\n const head = buffer\n .subarray(0, IMAGE_SIGNATURE_BYTES)\n .toString(\"utf8\")\n .replace(/^/, \"\")\n .trimStart()\n .toLowerCase();\n\n if (head.startsWith(\"<svg\") || (head.startsWith(\"<?xml\") && head.includes(\"<svg\"))) return \"svg\";\n\n return undefined;\n}\n\nconst VARIANT_SOURCE_FORMATS: readonly DetectedImageFormat[] = [\"jpeg\", \"png\", \"webp\", \"avif\"];\n\n/**\n * Whether a detected format can be the source of a variant. gif is refused\n * (animation is ambiguous) and svg is refused (not a raster).\n */\nexport function isVariantSourceFormat(\n format: DetectedImageFormat | undefined,\n): format is VariantSourceFormat {\n return format !== undefined && VARIANT_SOURCE_FORMATS.includes(format);\n}\n"],"mappings":";;;;AAKA,MAAa,wBAAwB;AAErC,SAAS,WAAW,QAAgB,WAAqB,SAAS,GAAY;CAC5E,OAAO,UAAU,OAAO,MAAM,UAAU,OAAO,SAAS,WAAW,IAAI;AACzE;AAEA,SAAS,MAAM,QAAgB,OAAe,KAAqB;CACjE,OAAO,OAAO,SAAS,OAAO,GAAG,CAAC,CAAC,SAAS,QAAQ;AACtD;;;;;;;;;;AAWA,SAAgB,kBAAkB,QAAiD;CACjF,IAAI,WAAW,QAAQ;EAAC;EAAM;EAAM;CAAI,CAAC,GAAG,OAAO;CAEnD,IAAI,WAAW,QAAQ;EAAC;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;CAAI,CAAC,GAAG,OAAO;CAEjF,IAAI,MAAM,QAAQ,GAAG,CAAC,MAAM,UAAU,MAAM,QAAQ,GAAG,EAAE,MAAM,QAAQ,OAAO;CAE9E,IAAI,MAAM,QAAQ,GAAG,CAAC,MAAM,YAAY,MAAM,QAAQ,GAAG,CAAC,MAAM,UAAU,OAAO;CAEjF,IAAI,MAAM,QAAQ,GAAG,CAAC,MAAM,UAAU,MAAM,QAAQ,GAAG,EAAE,MAAM,QAAQ,OAAO;CAE9E,MAAM,OAAO,OACV,SAAS,MAAwB,CAAC,CAClC,SAAS,MAAM,CAAC,CAChB,QAAQ,MAAM,EAAE,CAAC,CACjB,UAAU,CAAC,CACX,YAAY;CAEf,IAAI,KAAK,WAAW,MAAM,KAAM,KAAK,WAAW,OAAO,KAAK,KAAK,SAAS,MAAM,GAAI,OAAO;AAG7F;AAEA,MAAM,yBAAyD;CAAC;CAAQ;CAAO;CAAQ;AAAM;;;;;AAM7F,SAAgB,sBACd,QAC+B;CAC/B,OAAO,WAAW,UAAa,uBAAuB,SAAS,MAAM;AACvE"}
|
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
|
|
5
|
-
//#region
|
|
5
|
+
//#region ../../packages/core/src/http/uploads/generate-image-variant.ts
|
|
6
6
|
async function fileExists(target) {
|
|
7
7
|
try {
|
|
8
8
|
await fs.access(target);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-image-variant.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"generate-image-variant.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/generate-image-variant.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Image } from \"../../image\";\nimport type { ImageVariantDefinition } from \"../uploads-types\";\nimport type { VariantOutputFormat } from \"./image-variant-types\";\n\n/**\n * Inputs of one derivative generation\n */\nexport type GenerateImageVariantOptions = {\n image: Image;\n variant: ImageVariantDefinition;\n format: VariantOutputFormat;\n targetPath: string;\n};\n\nasync function fileExists(target: string): Promise<boolean> {\n try {\n await fs.access(target);\n\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Render a derivative and write it atomically.\n *\n * The image is written to `<target>.tmp-<random>` and then renamed onto the\n * target, so a reader sees no file or a complete one, never a partial one. On\n * any failure the temp file is removed before the error is rethrown.\n */\nexport async function generateImageVariant({\n image,\n variant,\n format,\n targetPath,\n}: GenerateImageVariantOptions): Promise<void> {\n image.resize({\n width: variant.width,\n height: variant.height,\n fit: variant.fit,\n withoutEnlargement: variant.enlarge !== true,\n });\n image.format(format);\n\n if (variant.quality !== undefined) {\n image.quality(variant.quality);\n }\n\n await fs.mkdir(path.dirname(targetPath), { recursive: true });\n\n const temporaryPath = `${targetPath}.tmp-${randomBytes(8).toString(\"hex\")}`;\n\n try {\n await image.save(temporaryPath);\n\n try {\n await fs.rename(temporaryPath, targetPath);\n } catch (error) {\n // Another process won the race and its file is being read (win32 refuses\n // to replace an open file). Same key, same bytes: theirs will do.\n if (!(await fileExists(targetPath))) throw error;\n }\n } finally {\n await fs.rm(temporaryPath, { force: true });\n }\n}\n"],"mappings":";;;;;AAiBA,eAAe,WAAW,QAAkC;CAC1D,IAAI;EACF,MAAM,GAAG,OAAO,MAAM;EAEtB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;AASA,eAAsB,qBAAqB,EACzC,OACA,SACA,QACA,cAC6C;CAC7C,MAAM,OAAO;EACX,OAAO,QAAQ;EACf,QAAQ,QAAQ;EAChB,KAAK,QAAQ;EACb,oBAAoB,QAAQ,YAAY;CAC1C,CAAC;CACD,MAAM,OAAO,MAAM;CAEnB,IAAI,QAAQ,YAAY,QACtB,MAAM,QAAQ,QAAQ,OAAO;CAG/B,MAAM,GAAG,MAAM,KAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;CAE5D,MAAM,gBAAgB,GAAG,WAAW,OAAO,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK;CAExE,IAAI;EACF,MAAM,MAAM,KAAK,aAAa;EAE9B,IAAI;GACF,MAAM,GAAG,OAAO,eAAe,UAAU;EAC3C,SAAS,OAAO;GAGd,IAAI,CAAE,MAAM,WAAW,UAAU,GAAI,MAAM;EAC7C;CACF,UAAU;EACR,MAAM,GAAG,GAAG,eAAe,EAAE,OAAO,KAAK,CAAC;CAC5C;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-image-variants.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"generate-image-variants.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/generate-image-variants.ts"],"mappings":";;;;;AAiCA;KAAY,4BAAA;;;AAKF;AA8EV;EA9EE,QAAQ;AAAA;;;;;;;;;;;;AAiFyB;;;;;;;;;;;;;;;;;;;;;iBAHb,qBAAA,CACpB,YAAA,UACA,OAAA,GAAS,4BAAA,GACR,OAAA,CAAQ,wBAAA"}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { HttpError } from "../errors/errors.mjs";
|
|
2
|
+
import { uploadsConfig } from "../uploads-config.mjs";
|
|
3
|
+
import { variantCacheKey, variantCachePath, variantGenerationTag, variantSourceDirectory, variantSourceKey } from "./variant-cache-key.mjs";
|
|
4
|
+
import { pruneStaleVariantGenerationsIfCurrent, retryOnVanishedDirectory, warnIfVariantCacheIsPublic } from "./image-variant-cache.mjs";
|
|
2
5
|
import { storage } from "../../storage/storage.mjs";
|
|
3
6
|
import "../../storage/index.mjs";
|
|
4
7
|
import { Image } from "../../image/image.mjs";
|
|
5
8
|
import "../../image/index.mjs";
|
|
6
|
-
import { uploadsConfig } from "../uploads-config.mjs";
|
|
7
9
|
import { generateImageVariant } from "./generate-image-variant.mjs";
|
|
8
10
|
import { ImageVariantsConfigError } from "./image-variants-config-error.mjs";
|
|
9
11
|
import { loadVariantSource } from "./load-variant-source.mjs";
|
|
10
12
|
import { resolveImageVariantsConfig } from "./resolve-image-variants-config.mjs";
|
|
11
13
|
import { resolveVariantCandidate } from "./resolve-variant-candidate.mjs";
|
|
12
|
-
import { variantCacheKey, variantCachePath } from "./variant-cache-key.mjs";
|
|
13
14
|
import { variantGenerations } from "./variant-generations.mjs";
|
|
14
15
|
import fs from "node:fs/promises";
|
|
15
16
|
|
|
16
|
-
//#region
|
|
17
|
+
//#region ../../packages/core/src/http/uploads/generate-image-variants.ts
|
|
17
18
|
async function exists(target) {
|
|
18
19
|
try {
|
|
19
20
|
await fs.access(target);
|
|
@@ -91,6 +92,7 @@ async function generateImageVariants(relativePath, options = {}) {
|
|
|
91
92
|
expectedFormat: sourceFormat
|
|
92
93
|
});
|
|
93
94
|
const { width: sourceWidth, height: sourceHeight } = await image.metadata();
|
|
95
|
+
warnIfVariantCacheIsPublic(images.cacheDirectory);
|
|
94
96
|
const src = `/uploads/${encodeUploadsPath(source.relativePath)}`;
|
|
95
97
|
const variants = {};
|
|
96
98
|
for (const name of names) {
|
|
@@ -106,15 +108,21 @@ async function generateImageVariants(relativePath, options = {}) {
|
|
|
106
108
|
variant,
|
|
107
109
|
format
|
|
108
110
|
});
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
+
const sourceKey = variantSourceKey(source.root, source.relativePath);
|
|
112
|
+
const generation = variantGenerationTag(stats.size, stats.mtimeMs);
|
|
113
|
+
const target = variantCachePath(images.cacheDirectory, {
|
|
114
|
+
sourceKey,
|
|
115
|
+
generation
|
|
116
|
+
}, hash, format);
|
|
117
|
+
if (!await exists(target)) await variantGenerations.run(target, async () => {
|
|
111
118
|
if (await exists(target)) return;
|
|
112
|
-
await generateImageVariant({
|
|
119
|
+
await retryOnVanishedDirectory(() => generateImageVariant({
|
|
113
120
|
image: image.clone(),
|
|
114
121
|
variant,
|
|
115
122
|
format,
|
|
116
123
|
targetPath: target
|
|
117
|
-
});
|
|
124
|
+
}));
|
|
125
|
+
await pruneStaleVariantGenerationsIfCurrent(source.absolutePath, variantSourceDirectory(images.cacheDirectory, sourceKey), generation);
|
|
118
126
|
});
|
|
119
127
|
if (dimensions === void 0) dimensions = await readDerivativeDimensions(target);
|
|
120
128
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-image-variants.mjs","names":[],"sources":["../../../../../../../../core/src/http/uploads/generate-image-variants.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport { Image } from \"../../image\";\nimport { storage } from \"../../storage\";\nimport { HttpError } from \"../errors\";\nimport { uploadsConfig } from \"../uploads-config\";\nimport type { ImageVariantOutputFormat } from \"../uploads-types\";\nimport { generateImageVariant } from \"./generate-image-variant\";\nimport { ImageVariantsConfigError } from \"./image-variants-config-error\";\nimport type {\n GeneratedImageDescriptor,\n GeneratedImageVariant,\n VariantOutputFormat,\n} from \"./image-variant-types\";\nimport { loadVariantSource } from \"./load-variant-source\";\nimport { resolveImageVariantsConfig } from \"./resolve-image-variants-config\";\nimport { resolveVariantCandidate } from \"./resolve-variant-candidate\";\nimport { variantCacheKey, variantCachePath } from \"./variant-cache-key\";\nimport { variantGenerations } from \"./variant-generations\";\n\n/**\n * Options for `generateImageVariants`\n */\nexport type GenerateImageVariantsOptions = {\n /**\n * Limit generation to these variant names, instead of every variant in\n * `uploads.images.variants`.\n */\n variants?: readonly string[];\n};\n\nasync function exists(target: string): Promise<boolean> {\n try {\n await fs.access(target);\n\n return true;\n } catch {\n return false;\n }\n}\n\nfunction encodeUploadsPath(relativePath: string): string {\n return relativePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\nfunction variantUrl(src: string, name: string, format?: VariantOutputFormat): string {\n const query = format\n ? `variant=${encodeURIComponent(name)}&format=${format}`\n : `variant=${encodeURIComponent(name)}`;\n\n return `${src}?${query}`;\n}\n\nasync function readDerivativeDimensions(\n target: string,\n): Promise<{ width: number; height: number }> {\n const derivative = new Image(await fs.readFile(target));\n const { width, height } = await derivative.metadata();\n\n return { width: width ?? 0, height: height ?? 0 };\n}\n\n/**\n * Every distinct format a variant is rendered in: the source format plus\n * whatever `uploads.images.formats` adds, with duplicates dropped so a format\n * that is both the source format and an allowed format is generated once.\n */\nfunction outputFormatsFor(\n sourceFormat: VariantOutputFormat,\n configuredFormats: readonly ImageVariantOutputFormat[],\n): VariantOutputFormat[] {\n return [...new Set<VariantOutputFormat>([sourceFormat, ...configuredFormats])];\n}\n\n/**\n * Render every configured variant of one uploaded image, right after it is\n * saved.\n *\n * Uses the exact same normalized config (`uploads.images`), cache key and\n * derivative path as `uploadedFileController`, so a variant generated here is\n * already on disk the first time `GET /uploads/<path>?variant=<name>` is\n * requested — no second render. `variantGenerations` (the single-flight used\n * by the route) is reused, so a concurrent request for a variant this call is\n * still generating joins the same run instead of starting a second one.\n *\n * The returned descriptor is plain JSON, safe to store next to the record\n * that owns the image, or to carry through loader data. On local storage the\n * `url`/`urls` already point at `uploadedFileController`; an app serving\n * uploads from object storage or a CDN should not proxy the remote files\n * through this route at all — store the descriptor once generation runs\n * against the same bucket the CDN serves from, or resolve its URLs through a\n * custom `ImageLoader` on the client instead.\n *\n * @param relativePath - path of the saved upload, relative to the storage root\n * @param options.variants - limit generation to these variant names\n * @throws ImageVariantsConfigError when there is no local storage root, or\n * `uploads.images` is not configured — both are server misconfiguration, not\n * a bad request\n * @throws HttpError(400) when a name in `options.variants` is not declared in\n * `uploads.images.variants`\n * @throws HttpError(404) when `relativePath` does not resolve to a file\n * inside the storage root\n * @throws HttpError(413) when the source is over `maxSourceBytes` or\n * `maxSourcePixels`\n * @throws HttpError(415) when the source is not jpeg, png, webp or avif\n */\nexport async function generateImageVariants(\n relativePath: string,\n options: GenerateImageVariantsOptions = {},\n): Promise<GeneratedImageDescriptor> {\n const storageRoot = storage.root();\n\n if (!storageRoot || storageRoot === \".\") {\n throw new ImageVariantsConfigError(\n \"generateImageVariants needs a local storage driver with a root.\",\n );\n }\n\n const images = resolveImageVariantsConfig(uploadsConfig(\"images\"), storageRoot);\n\n if (!images) {\n throw new ImageVariantsConfigError(\"uploads.images is not configured.\");\n }\n\n const names = options.variants ?? Object.keys(images.variants);\n\n for (const name of names) {\n if (!Object.prototype.hasOwnProperty.call(images.variants, name)) {\n throw new HttpError(400, `Unknown variant \"${name}\".`);\n }\n }\n\n const candidate = await resolveVariantCandidate(relativePath, storageRoot, images);\n\n if (!candidate) {\n throw new HttpError(404, \"File Not Found\");\n }\n\n const { source, stats, sourceFormat } = candidate;\n\n const image = await loadVariantSource(source.absolutePath, {\n maxSourceBytes: images.maxSourceBytes,\n maxSourcePixels: images.maxSourcePixels,\n expectedFormat: sourceFormat,\n });\n\n const { width: sourceWidth, height: sourceHeight } = await image.metadata();\n const src = `/uploads/${encodeUploadsPath(source.relativePath)}`;\n const variants: Record<string, GeneratedImageVariant> = {};\n\n for (const name of names) {\n const variant = images.variants[name];\n if (variant === undefined) {\n throw new HttpError(400, `Unknown variant \"${name}\".`);\n }\n const formats = outputFormatsFor(sourceFormat, images.formats);\n let dimensions: { width: number; height: number } | undefined;\n\n for (const format of formats) {\n const hash = variantCacheKey({\n relativePath: source.relativePath,\n sourceSize: stats.size,\n sourceMtimeMs: stats.mtimeMs,\n variant,\n format,\n });\n const target = variantCachePath(images.cacheDirectory, hash, format);\n\n if (!(await exists(target))) {\n await variantGenerations.run(hash, async () => {\n if (await exists(target)) return;\n\n await generateImageVariant({ image: image.clone(), variant, format, targetPath: target });\n });\n }\n\n if (dimensions === undefined) {\n dimensions = await readDerivativeDimensions(target);\n }\n }\n\n const urls: Partial<Record<ImageVariantOutputFormat, string>> = {};\n\n for (const format of images.formats) {\n urls[format] = variantUrl(src, name, format);\n }\n\n variants[name] = {\n width: dimensions?.width ?? variant.width,\n height:\n dimensions?.height ??\n variant.height ??\n Math.round(variant.width * ((sourceHeight ?? 1) / (sourceWidth ?? 1))),\n url: variantUrl(src, name),\n ...(Object.keys(urls).length > 0 ? { urls } : {}),\n };\n }\n\n return {\n src,\n width: sourceWidth ?? 0,\n height: sourceHeight ?? 0,\n variants,\n ...(images.formats.length > 0 ? { formats: images.formats } : {}),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generate-image-variants.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/generate-image-variants.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport { Image } from \"../../image\";\nimport { storage } from \"../../storage\";\nimport { HttpError } from \"../errors\";\nimport { uploadsConfig } from \"../uploads-config\";\nimport type { ImageVariantOutputFormat } from \"../uploads-types\";\nimport { generateImageVariant } from \"./generate-image-variant\";\nimport {\n pruneStaleVariantGenerationsIfCurrent,\n retryOnVanishedDirectory,\n warnIfVariantCacheIsPublic,\n} from \"./image-variant-cache\";\nimport { ImageVariantsConfigError } from \"./image-variants-config-error\";\nimport type {\n GeneratedImageDescriptor,\n GeneratedImageVariant,\n VariantOutputFormat,\n} from \"./image-variant-types\";\nimport { loadVariantSource } from \"./load-variant-source\";\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 { variantGenerations } from \"./variant-generations\";\n\n/**\n * Options for `generateImageVariants`\n */\nexport type GenerateImageVariantsOptions = {\n /**\n * Limit generation to these variant names, instead of every variant in\n * `uploads.images.variants`.\n */\n variants?: readonly string[];\n};\n\nasync function exists(target: string): Promise<boolean> {\n try {\n await fs.access(target);\n\n return true;\n } catch {\n return false;\n }\n}\n\nfunction encodeUploadsPath(relativePath: string): string {\n return relativePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\nfunction variantUrl(src: string, name: string, format?: VariantOutputFormat): string {\n const query = format\n ? `variant=${encodeURIComponent(name)}&format=${format}`\n : `variant=${encodeURIComponent(name)}`;\n\n return `${src}?${query}`;\n}\n\nasync function readDerivativeDimensions(\n target: string,\n): Promise<{ width: number; height: number }> {\n const derivative = new Image(await fs.readFile(target));\n const { width, height } = await derivative.metadata();\n\n return { width: width ?? 0, height: height ?? 0 };\n}\n\n/**\n * Every distinct format a variant is rendered in: the source format plus\n * whatever `uploads.images.formats` adds, with duplicates dropped so a format\n * that is both the source format and an allowed format is generated once.\n */\nfunction outputFormatsFor(\n sourceFormat: VariantOutputFormat,\n configuredFormats: readonly ImageVariantOutputFormat[],\n): VariantOutputFormat[] {\n return [...new Set<VariantOutputFormat>([sourceFormat, ...configuredFormats])];\n}\n\n/**\n * Render every configured variant of one uploaded image, right after it is\n * saved.\n *\n * Uses the exact same normalized config (`uploads.images`), cache key and\n * derivative path as `uploadedFileController`, so a variant generated here is\n * already on disk the first time `GET /uploads/<path>?variant=<name>` is\n * requested — no second render. `variantGenerations` (the single-flight used\n * by the route) is reused, so a concurrent request for a variant this call is\n * still generating joins the same run instead of starting a second one.\n *\n * The returned descriptor is plain JSON, safe to store next to the record\n * that owns the image, or to carry through loader data. On local storage the\n * `url`/`urls` already point at `uploadedFileController`; an app serving\n * uploads from object storage or a CDN should not proxy the remote files\n * through this route at all — store the descriptor once generation runs\n * against the same bucket the CDN serves from, or resolve its URLs through a\n * custom `ImageLoader` on the client instead.\n *\n * @param relativePath - path of the saved upload, relative to the storage root\n * @param options.variants - limit generation to these variant names\n * @throws ImageVariantsConfigError when there is no local storage root, or\n * `uploads.images` is not configured — both are server misconfiguration, not\n * a bad request\n * @throws HttpError(400) when a name in `options.variants` is not declared in\n * `uploads.images.variants`\n * @throws HttpError(404) when `relativePath` does not resolve to a file\n * inside the storage root\n * @throws HttpError(413) when the source is over `maxSourceBytes` or\n * `maxSourcePixels`\n * @throws HttpError(415) when the source is not jpeg, png, webp or avif\n */\nexport async function generateImageVariants(\n relativePath: string,\n options: GenerateImageVariantsOptions = {},\n): Promise<GeneratedImageDescriptor> {\n const storageRoot = storage.root();\n\n if (!storageRoot || storageRoot === \".\") {\n throw new ImageVariantsConfigError(\n \"generateImageVariants needs a local storage driver with a root.\",\n );\n }\n\n const images = resolveImageVariantsConfig(uploadsConfig(\"images\"), storageRoot);\n\n if (!images) {\n throw new ImageVariantsConfigError(\"uploads.images is not configured.\");\n }\n\n const names = options.variants ?? Object.keys(images.variants);\n\n for (const name of names) {\n if (!Object.prototype.hasOwnProperty.call(images.variants, name)) {\n throw new HttpError(400, `Unknown variant \"${name}\".`);\n }\n }\n\n const candidate = await resolveVariantCandidate(relativePath, storageRoot, images);\n\n if (!candidate) {\n throw new HttpError(404, \"File Not Found\");\n }\n\n const { source, stats, sourceFormat } = candidate;\n\n const image = await loadVariantSource(source.absolutePath, {\n maxSourceBytes: images.maxSourceBytes,\n maxSourcePixels: images.maxSourcePixels,\n expectedFormat: sourceFormat,\n });\n\n const { width: sourceWidth, height: sourceHeight } = await image.metadata();\n warnIfVariantCacheIsPublic(images.cacheDirectory);\n\n const src = `/uploads/${encodeUploadsPath(source.relativePath)}`;\n const variants: Record<string, GeneratedImageVariant> = {};\n\n for (const name of names) {\n const variant = images.variants[name];\n if (variant === undefined) {\n throw new HttpError(400, `Unknown variant \"${name}\".`);\n }\n const formats = outputFormatsFor(sourceFormat, images.formats);\n let dimensions: { width: number; height: number } | undefined;\n\n for (const format of formats) {\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(\n images.cacheDirectory,\n { sourceKey, generation },\n hash,\n format,\n );\n\n if (!(await exists(target))) {\n // keyed on the target, which includes the disk root (see resolveImageVariant)\n await variantGenerations.run(target, async () => {\n if (await exists(target)) return;\n\n await retryOnVanishedDirectory(() =>\n generateImageVariant({ image: image.clone(), variant, format, targetPath: target }),\n );\n await pruneStaleVariantGenerationsIfCurrent(\n source.absolutePath,\n variantSourceDirectory(images.cacheDirectory, sourceKey),\n generation,\n );\n });\n }\n\n if (dimensions === undefined) {\n dimensions = await readDerivativeDimensions(target);\n }\n }\n\n const urls: Partial<Record<ImageVariantOutputFormat, string>> = {};\n\n for (const format of images.formats) {\n urls[format] = variantUrl(src, name, format);\n }\n\n variants[name] = {\n width: dimensions?.width ?? variant.width,\n height:\n dimensions?.height ??\n variant.height ??\n Math.round(variant.width * ((sourceHeight ?? 1) / (sourceWidth ?? 1))),\n url: variantUrl(src, name),\n ...(Object.keys(urls).length > 0 ? { urls } : {}),\n };\n }\n\n return {\n src,\n width: sourceWidth ?? 0,\n height: sourceHeight ?? 0,\n variants,\n ...(images.formats.length > 0 ? { formats: images.formats } : {}),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAyCA,eAAe,OAAO,QAAkC;CACtD,IAAI;EACF,MAAM,GAAG,OAAO,MAAM;EAEtB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,kBAAkB,cAA8B;CACvD,OAAO,aAAa,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;AACjE;AAEA,SAAS,WAAW,KAAa,MAAc,QAAsC;CAKnF,OAAO,GAAG,IAAI,GAJA,SACV,WAAW,mBAAmB,IAAI,EAAE,UAAU,WAC9C,WAAW,mBAAmB,IAAI;AAGxC;AAEA,eAAe,yBACb,QAC4C;CAE5C,MAAM,EAAE,OAAO,WAAW,MAAM,IADT,MAAM,MAAM,GAAG,SAAS,MAAM,CACZ,CAAC,CAAC,SAAS;CAEpD,OAAO;EAAE,OAAO,SAAS;EAAG,QAAQ,UAAU;CAAE;AAClD;;;;;;AAOA,SAAS,iBACP,cACA,mBACuB;CACvB,OAAO,CAAC,GAAG,IAAI,IAAyB,CAAC,cAAc,GAAG,iBAAiB,CAAC,CAAC;AAC/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,eAAsB,sBACpB,cACA,UAAwC,CAAC,GACN;CACnC,MAAM,cAAc,QAAQ,KAAK;CAEjC,IAAI,CAAC,eAAe,gBAAgB,KAClC,MAAM,IAAI,yBACR,iEACF;CAGF,MAAM,SAAS,2BAA2B,cAAc,QAAQ,GAAG,WAAW;CAE9E,IAAI,CAAC,QACH,MAAM,IAAI,yBAAyB,mCAAmC;CAGxE,MAAM,QAAQ,QAAQ,YAAY,OAAO,KAAK,OAAO,QAAQ;CAE7D,KAAK,MAAM,QAAQ,OACjB,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,OAAO,UAAU,IAAI,GAC7D,MAAM,IAAI,UAAU,KAAK,oBAAoB,KAAK,GAAG;CAIzD,MAAM,YAAY,MAAM,wBAAwB,cAAc,aAAa,MAAM;CAEjF,IAAI,CAAC,WACH,MAAM,IAAI,UAAU,KAAK,gBAAgB;CAG3C,MAAM,EAAE,QAAQ,OAAO,iBAAiB;CAExC,MAAM,QAAQ,MAAM,kBAAkB,OAAO,cAAc;EACzD,gBAAgB,OAAO;EACvB,iBAAiB,OAAO;EACxB,gBAAgB;CAClB,CAAC;CAED,MAAM,EAAE,OAAO,aAAa,QAAQ,iBAAiB,MAAM,MAAM,SAAS;CAC1E,2BAA2B,OAAO,cAAc;CAEhD,MAAM,MAAM,YAAY,kBAAkB,OAAO,YAAY;CAC7D,MAAM,WAAkD,CAAC;CAEzD,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,UAAU,OAAO,SAAS;EAChC,IAAI,YAAY,QACd,MAAM,IAAI,UAAU,KAAK,oBAAoB,KAAK,GAAG;EAEvD,MAAM,UAAU,iBAAiB,cAAc,OAAO,OAAO;EAC7D,IAAI;EAEJ,KAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,OAAO,gBAAgB;IAC3B,cAAc,OAAO;IACrB,YAAY,MAAM;IAClB,eAAe,MAAM;IACrB;IACA;GACF,CAAC;GACD,MAAM,YAAY,iBAAiB,OAAO,MAAM,OAAO,YAAY;GACnE,MAAM,aAAa,qBAAqB,MAAM,MAAM,MAAM,OAAO;GACjE,MAAM,SAAS,iBACb,OAAO,gBACP;IAAE;IAAW;GAAW,GACxB,MACA,MACF;GAEA,IAAI,CAAE,MAAM,OAAO,MAAM,GAEvB,MAAM,mBAAmB,IAAI,QAAQ,YAAY;IAC/C,IAAI,MAAM,OAAO,MAAM,GAAG;IAE1B,MAAM,+BACJ,qBAAqB;KAAE,OAAO,MAAM,MAAM;KAAG;KAAS;KAAQ,YAAY;IAAO,CAAC,CACpF;IACA,MAAM,sCACJ,OAAO,cACP,uBAAuB,OAAO,gBAAgB,SAAS,GACvD,UACF;GACF,CAAC;GAGH,IAAI,eAAe,QACjB,aAAa,MAAM,yBAAyB,MAAM;EAEtD;EAEA,MAAM,OAA0D,CAAC;EAEjE,KAAK,MAAM,UAAU,OAAO,SAC1B,KAAK,UAAU,WAAW,KAAK,MAAM,MAAM;EAG7C,SAAS,QAAQ;GACf,OAAO,YAAY,SAAS,QAAQ;GACpC,QACE,YAAY,UACZ,QAAQ,UACR,KAAK,MAAM,QAAQ,UAAU,gBAAgB,MAAM,eAAe,GAAG;GACvE,KAAK,WAAW,KAAK,IAAI;GACzB,GAAI,OAAO,KAAK,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC;EACjD;CACF;CAEA,OAAO;EACL;EACA,OAAO,eAAe;EACtB,QAAQ,gBAAgB;EACxB;EACA,GAAI,OAAO,QAAQ,SAAS,IAAI,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;CACjE;AACF"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { rootPath } from "../../utils/paths.mjs";
|
|
2
|
+
import { uploadsConfig } from "../uploads-config.mjs";
|
|
3
|
+
import { isPathInside } from "./is-path-inside.mjs";
|
|
4
|
+
import { variantGenerationTag, variantSourceDirectory, variantSourceKey } from "./variant-cache-key.mjs";
|
|
5
|
+
import config from "@mongez/config";
|
|
6
|
+
import { log } from "@warlock.js/logger";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import fs from "node:fs/promises";
|
|
9
|
+
|
|
10
|
+
//#region ../../packages/core/src/http/uploads/image-variant-cache.ts
|
|
11
|
+
async function realpathOrSelf(target) {
|
|
12
|
+
try {
|
|
13
|
+
return await fs.realpath(target);
|
|
14
|
+
} catch {
|
|
15
|
+
return target;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The derivative cache directory for a local disk: `uploads.images.cacheDirectory`
|
|
20
|
+
* when the app sets one (shared by every disk; derivatives are keyed by the
|
|
21
|
+
* disk's real root, so they never collide), otherwise
|
|
22
|
+
* `<diskRoot>/.cache/image-variants`. The same rule as
|
|
23
|
+
* `resolveImageVariantsConfig`.
|
|
24
|
+
*/
|
|
25
|
+
function variantCacheDirectoryFor(diskRoot) {
|
|
26
|
+
const configured = uploadsConfig("images")?.cacheDirectory;
|
|
27
|
+
return configured ? path.resolve(configured) : path.join(path.resolve(diskRoot), ".cache", "image-variants");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The directory holding every derivative of one file on a local disk, or
|
|
31
|
+
* `undefined` when the file is not inside the disk root. The file does not
|
|
32
|
+
* need to exist any more: only its parent directory is resolved through
|
|
33
|
+
* symlinks, exactly as `resolveUploadPath` resolved it when the derivative
|
|
34
|
+
* was made.
|
|
35
|
+
*/
|
|
36
|
+
async function variantSourceDirectoryForFile(diskRoot, absolutePath) {
|
|
37
|
+
const realRoot = await realpathOrSelf(path.resolve(diskRoot));
|
|
38
|
+
const realParent = await realpathOrSelf(path.dirname(path.resolve(absolutePath)));
|
|
39
|
+
const realPath = path.join(realParent, path.basename(absolutePath));
|
|
40
|
+
if (!isPathInside(realPath, realRoot)) return void 0;
|
|
41
|
+
const relativePath = path.relative(realRoot, realPath).split(path.sep).join("/");
|
|
42
|
+
return variantSourceDirectory(variantCacheDirectoryFor(diskRoot), variantSourceKey(realRoot, relativePath));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Remove every cached derivative of one file on a local disk.
|
|
46
|
+
*
|
|
47
|
+
* @returns `true` when a cache directory for the file existed and was removed
|
|
48
|
+
*/
|
|
49
|
+
async function purgeImageVariantsOfFile(diskRoot, absolutePath) {
|
|
50
|
+
const directory = await variantSourceDirectoryForFile(diskRoot, absolutePath);
|
|
51
|
+
if (!directory) return false;
|
|
52
|
+
try {
|
|
53
|
+
await fs.access(directory);
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
await fs.rm(directory, {
|
|
58
|
+
recursive: true,
|
|
59
|
+
force: true
|
|
60
|
+
});
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Remove derivatives made from older versions of a source (a different size
|
|
65
|
+
* or mtime), keeping only the `keep` generation. Called after a derivative of
|
|
66
|
+
* the current version has been written, so a source rewritten in place does
|
|
67
|
+
* not leave its old derivatives behind.
|
|
68
|
+
*/
|
|
69
|
+
async function pruneStaleVariantGenerations(sourceDirectory, keep) {
|
|
70
|
+
let entries;
|
|
71
|
+
try {
|
|
72
|
+
entries = await fs.readdir(sourceDirectory);
|
|
73
|
+
} catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
await Promise.all(entries.filter((entry) => entry !== keep).map((entry) => fs.rm(path.join(sourceDirectory, entry), {
|
|
77
|
+
recursive: true,
|
|
78
|
+
force: true
|
|
79
|
+
})));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* {@link pruneStaleVariantGenerations}, but only while `keep` is still the
|
|
83
|
+
* source's current generation.
|
|
84
|
+
*
|
|
85
|
+
* A request that read the source before it was rewritten renders an old
|
|
86
|
+
* generation; if it pruned afterwards it would delete the derivatives of the
|
|
87
|
+
* newer version another request had just written. Re-reading the source's
|
|
88
|
+
* size and mtime right before pruning keeps a stale request from doing that.
|
|
89
|
+
*/
|
|
90
|
+
async function pruneStaleVariantGenerationsIfCurrent(sourceAbsolutePath, sourceDirectory, keep) {
|
|
91
|
+
let current;
|
|
92
|
+
try {
|
|
93
|
+
const stats = await fs.stat(sourceAbsolutePath);
|
|
94
|
+
current = variantGenerationTag(stats.size, stats.mtimeMs);
|
|
95
|
+
} catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (current !== keep) return;
|
|
99
|
+
await pruneStaleVariantGenerations(sourceDirectory, keep);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* True for "no such file or directory"
|
|
103
|
+
*/
|
|
104
|
+
function isMissingFileError(error) {
|
|
105
|
+
return error?.code === "ENOENT";
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Run a derivative write, once more if it fails because its directory
|
|
109
|
+
* vanished: a concurrent request for a newer version of the source may prune
|
|
110
|
+
* the generation directory between `mkdir` and the atomic rename.
|
|
111
|
+
*/
|
|
112
|
+
async function retryOnVanishedDirectory(write) {
|
|
113
|
+
try {
|
|
114
|
+
await write();
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (!isMissingFileError(error)) throw error;
|
|
117
|
+
await write();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
let warnedPublicCache = false;
|
|
121
|
+
/**
|
|
122
|
+
* Warn once when the derivative cache sits inside the public folder.
|
|
123
|
+
*
|
|
124
|
+
* A configured `uploads.images.cacheDirectory` is shared by every local disk,
|
|
125
|
+
* so derivatives of a private disk land beside public ones. Inside
|
|
126
|
+
* `public/` (or `storage.publicRoot`) they are served to anyone, bypassing
|
|
127
|
+
* whatever guards the private disk's own route. The paths are hashes, but
|
|
128
|
+
* they are derived from the disk root and the file path, which are not
|
|
129
|
+
* secret.
|
|
130
|
+
*/
|
|
131
|
+
function warnIfVariantCacheIsPublic(cacheDirectory) {
|
|
132
|
+
if (warnedPublicCache) return;
|
|
133
|
+
const configured = config.get("storage.publicRoot", rootPath("public"));
|
|
134
|
+
const publicRoots = (Array.isArray(configured) ? configured : [configured]).filter((root) => typeof root === "string" && root.length > 0).map((root) => path.resolve(root));
|
|
135
|
+
const resolved = path.resolve(cacheDirectory);
|
|
136
|
+
if (!publicRoots.some((root) => resolved === root || isPathInside(resolved, root))) return;
|
|
137
|
+
warnedPublicCache = true;
|
|
138
|
+
log.warn("uploads", "image-variants", `The image variant cache (${resolved}) is inside the public folder, so every cached derivative, private disks included, is publicly served. Move uploads.images.cacheDirectory outside public/.`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
//#endregion
|
|
142
|
+
export { isMissingFileError, pruneStaleVariantGenerationsIfCurrent, purgeImageVariantsOfFile, retryOnVanishedDirectory, variantCacheDirectoryFor, warnIfVariantCacheIsPublic };
|
|
143
|
+
//# sourceMappingURL=image-variant-cache.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-variant-cache.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/image-variant-cache.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { log } from \"@warlock.js/logger\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { rootPath } from \"../../utils/paths\";\nimport { uploadsConfig } from \"../uploads-config\";\nimport { isPathInside } from \"./is-path-inside\";\nimport {\n variantGenerationTag,\n variantSourceDirectory,\n variantSourceKey,\n} from \"./variant-cache-key\";\n\n/*\n * Cache housekeeping for image derivatives. Deliberately free of any\n * `storage` import: the local storage driver calls into this module when it\n * deletes or moves a file, and storage must not import back into the HTTP\n * layer that already imports storage.\n */\n\nasync function realpathOrSelf(target: string): Promise<string> {\n try {\n return await fs.realpath(target);\n } catch {\n return target;\n }\n}\n\n/**\n * The derivative cache directory for a local disk: `uploads.images.cacheDirectory`\n * when the app sets one (shared by every disk; derivatives are keyed by the\n * disk's real root, so they never collide), otherwise\n * `<diskRoot>/.cache/image-variants`. The same rule as\n * `resolveImageVariantsConfig`.\n */\nexport function variantCacheDirectoryFor(diskRoot: string): string {\n const configured = uploadsConfig(\"images\")?.cacheDirectory;\n\n return configured\n ? path.resolve(configured)\n : path.join(path.resolve(diskRoot), \".cache\", \"image-variants\");\n}\n\n/**\n * The directory holding every derivative of one file on a local disk, or\n * `undefined` when the file is not inside the disk root. The file does not\n * need to exist any more: only its parent directory is resolved through\n * symlinks, exactly as `resolveUploadPath` resolved it when the derivative\n * was made.\n */\nexport async function variantSourceDirectoryForFile(\n diskRoot: string,\n absolutePath: string,\n): Promise<string | undefined> {\n const realRoot = await realpathOrSelf(path.resolve(diskRoot));\n const realParent = await realpathOrSelf(path.dirname(path.resolve(absolutePath)));\n const realPath = path.join(realParent, path.basename(absolutePath));\n\n if (!isPathInside(realPath, realRoot)) return undefined;\n\n const relativePath = path.relative(realRoot, realPath).split(path.sep).join(\"/\");\n\n return variantSourceDirectory(\n variantCacheDirectoryFor(diskRoot),\n variantSourceKey(realRoot, relativePath),\n );\n}\n\n/**\n * Remove every cached derivative of one file on a local disk.\n *\n * @returns `true` when a cache directory for the file existed and was removed\n */\nexport async function purgeImageVariantsOfFile(\n diskRoot: string,\n absolutePath: string,\n): Promise<boolean> {\n const directory = await variantSourceDirectoryForFile(diskRoot, absolutePath);\n\n if (!directory) return false;\n\n try {\n await fs.access(directory);\n } catch {\n return false;\n }\n\n await fs.rm(directory, { recursive: true, force: true });\n\n return true;\n}\n\n/**\n * Remove derivatives made from older versions of a source (a different size\n * or mtime), keeping only the `keep` generation. Called after a derivative of\n * the current version has been written, so a source rewritten in place does\n * not leave its old derivatives behind.\n */\nexport async function pruneStaleVariantGenerations(\n sourceDirectory: string,\n keep: string,\n): Promise<void> {\n let entries: string[];\n\n try {\n entries = await fs.readdir(sourceDirectory);\n } catch {\n return;\n }\n\n await Promise.all(\n entries\n .filter((entry) => entry !== keep)\n .map((entry) => fs.rm(path.join(sourceDirectory, entry), { recursive: true, force: true })),\n );\n}\n\n/**\n * {@link pruneStaleVariantGenerations}, but only while `keep` is still the\n * source's current generation.\n *\n * A request that read the source before it was rewritten renders an old\n * generation; if it pruned afterwards it would delete the derivatives of the\n * newer version another request had just written. Re-reading the source's\n * size and mtime right before pruning keeps a stale request from doing that.\n */\nexport async function pruneStaleVariantGenerationsIfCurrent(\n sourceAbsolutePath: string,\n sourceDirectory: string,\n keep: string,\n): Promise<void> {\n let current: string;\n\n try {\n const stats = await fs.stat(sourceAbsolutePath);\n current = variantGenerationTag(stats.size, stats.mtimeMs);\n } catch {\n // the source is gone; its derivatives are purged by the delete itself\n return;\n }\n\n if (current !== keep) return;\n\n await pruneStaleVariantGenerations(sourceDirectory, keep);\n}\n\n/**\n * True for \"no such file or directory\"\n */\nexport function isMissingFileError(error: unknown): boolean {\n return (error as NodeJS.ErrnoException | undefined)?.code === \"ENOENT\";\n}\n\n/**\n * Run a derivative write, once more if it fails because its directory\n * vanished: a concurrent request for a newer version of the source may prune\n * the generation directory between `mkdir` and the atomic rename.\n */\nexport async function retryOnVanishedDirectory(write: () => Promise<void>): Promise<void> {\n try {\n await write();\n } catch (error) {\n if (!isMissingFileError(error)) throw error;\n\n await write();\n }\n}\n\nlet warnedPublicCache = false;\n\n/**\n * Warn once when the derivative cache sits inside the public folder.\n *\n * A configured `uploads.images.cacheDirectory` is shared by every local disk,\n * so derivatives of a private disk land beside public ones. Inside\n * `public/` (or `storage.publicRoot`) they are served to anyone, bypassing\n * whatever guards the private disk's own route. The paths are hashes, but\n * they are derived from the disk root and the file path, which are not\n * secret.\n */\nexport function warnIfVariantCacheIsPublic(cacheDirectory: string): void {\n if (warnedPublicCache) return;\n\n const configured = config.get(\"storage.publicRoot\", rootPath(\"public\")) as unknown;\n const publicRoots = (Array.isArray(configured) ? configured : [configured])\n .filter((root): root is string => typeof root === \"string\" && root.length > 0)\n .map((root) => path.resolve(root));\n const resolved = path.resolve(cacheDirectory);\n\n if (!publicRoots.some((root) => resolved === root || isPathInside(resolved, root))) return;\n\n warnedPublicCache = true;\n log.warn(\n \"uploads\",\n \"image-variants\",\n `The image variant cache (${resolved}) is inside the public folder, so every cached derivative, private disks included, is publicly served. Move uploads.images.cacheDirectory outside public/.`,\n );\n}\n\n/**\n * Forget that the public-cache warning was shown (tests).\n */\nexport function resetVariantCacheWarnings(): void {\n warnedPublicCache = false;\n}\n"],"mappings":";;;;;;;;;;AAoBA,eAAe,eAAe,QAAiC;CAC7D,IAAI;EACF,OAAO,MAAM,GAAG,SAAS,MAAM;CACjC,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;AASA,SAAgB,yBAAyB,UAA0B;CACjE,MAAM,aAAa,cAAc,QAAQ,CAAC,EAAE;CAE5C,OAAO,aACH,KAAK,QAAQ,UAAU,IACvB,KAAK,KAAK,KAAK,QAAQ,QAAQ,GAAG,UAAU,gBAAgB;AAClE;;;;;;;;AASA,eAAsB,8BACpB,UACA,cAC6B;CAC7B,MAAM,WAAW,MAAM,eAAe,KAAK,QAAQ,QAAQ,CAAC;CAC5D,MAAM,aAAa,MAAM,eAAe,KAAK,QAAQ,KAAK,QAAQ,YAAY,CAAC,CAAC;CAChF,MAAM,WAAW,KAAK,KAAK,YAAY,KAAK,SAAS,YAAY,CAAC;CAElE,IAAI,CAAC,aAAa,UAAU,QAAQ,GAAG,OAAO;CAE9C,MAAM,eAAe,KAAK,SAAS,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;CAE/E,OAAO,uBACL,yBAAyB,QAAQ,GACjC,iBAAiB,UAAU,YAAY,CACzC;AACF;;;;;;AAOA,eAAsB,yBACpB,UACA,cACkB;CAClB,MAAM,YAAY,MAAM,8BAA8B,UAAU,YAAY;CAE5E,IAAI,CAAC,WAAW,OAAO;CAEvB,IAAI;EACF,MAAM,GAAG,OAAO,SAAS;CAC3B,QAAQ;EACN,OAAO;CACT;CAEA,MAAM,GAAG,GAAG,WAAW;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;CAEvD,OAAO;AACT;;;;;;;AAQA,eAAsB,6BACpB,iBACA,MACe;CACf,IAAI;CAEJ,IAAI;EACF,UAAU,MAAM,GAAG,QAAQ,eAAe;CAC5C,QAAQ;EACN;CACF;CAEA,MAAM,QAAQ,IACZ,QACG,QAAQ,UAAU,UAAU,IAAI,CAAC,CACjC,KAAK,UAAU,GAAG,GAAG,KAAK,KAAK,iBAAiB,KAAK,GAAG;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC,CAAC,CAC9F;AACF;;;;;;;;;;AAWA,eAAsB,sCACpB,oBACA,iBACA,MACe;CACf,IAAI;CAEJ,IAAI;EACF,MAAM,QAAQ,MAAM,GAAG,KAAK,kBAAkB;EAC9C,UAAU,qBAAqB,MAAM,MAAM,MAAM,OAAO;CAC1D,QAAQ;EAEN;CACF;CAEA,IAAI,YAAY,MAAM;CAEtB,MAAM,6BAA6B,iBAAiB,IAAI;AAC1D;;;;AAKA,SAAgB,mBAAmB,OAAyB;CAC1D,OAAQ,OAA6C,SAAS;AAChE;;;;;;AAOA,eAAsB,yBAAyB,OAA2C;CACxF,IAAI;EACF,MAAM,MAAM;CACd,SAAS,OAAO;EACd,IAAI,CAAC,mBAAmB,KAAK,GAAG,MAAM;EAEtC,MAAM,MAAM;CACd;AACF;AAEA,IAAI,oBAAoB;;;;;;;;;;;AAYxB,SAAgB,2BAA2B,gBAA8B;CACvE,IAAI,mBAAmB;CAEvB,MAAM,aAAa,OAAO,IAAI,sBAAsB,SAAS,QAAQ,CAAC;CACtE,MAAM,eAAe,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC,UAAU,EAAC,CACvE,QAAQ,SAAyB,OAAO,SAAS,YAAY,KAAK,SAAS,CAAC,CAAC,CAC7E,KAAK,SAAS,KAAK,QAAQ,IAAI,CAAC;CACnC,MAAM,WAAW,KAAK,QAAQ,cAAc;CAE5C,IAAI,CAAC,YAAY,MAAM,SAAS,aAAa,QAAQ,aAAa,UAAU,IAAI,CAAC,GAAG;CAEpF,oBAAoB;CACpB,IAAI,KACF,WACA,kBACA,4BAA4B,SAAS,2JACvC;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ImageVariantDefinition, ImageVariantOutputFormat } from "../uploads-types.mjs";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/core/src/http/uploads/image-variant-types.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* The `uploads.images` config after validation, with every default applied
|
|
6
6
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-variant-types.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"image-variant-types.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/image-variant-types.ts"],"mappings":";;;;;AAKA;KAAY,2BAAA;EACV,QAAA,EAAU,QAAA,CAAS,MAAA,SAAe,sBAAA;EAClC,OAAA,WAAkB,wBAAA;EAClB,cAAA;EACA,eAAA;EACA,cAAA;AAAA;;;;KAMU,mBAAA;;;;KAKA,mBAAA,GAAsB,mBAAmB;;;;KAKzC,mBAAA,GAAsB,mBAAmB;AAVrD;;;AAAA,KAeY,iBAAA;EACN,IAAA;AAAA;EACA,IAAA;EAAiB,OAAA;EAAiB,MAAA;AAAA;EAClC,IAAA;EAAiB,MAAA;AAAA;;AAR8B;AAKrD;;;;;;KAaY,qBAAA;EAX4B;;;EAetC,KAAA;EAd2B;AAU7B;;EASE,MAAA;EAUsB;;;EALtB,GAAA;EAKc;;;EAAd,IAAA,GAAO,OAAA,CAAQ,MAAA,CAAO,wBAAA;AAAA;;;;;KAOZ,wBAAA;EAAA;;;EAIV,GAAA;EAemB;;;EAVnB,KAAA;EAe2C;;;EAV3C,MAAA;EAKA;;;EAAA,QAAA,EAAU,QAAA,CAAS,MAAA,SAAe,qBAAA;EAKlC;;;EAAA,OAAA,YAAmB,wBAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-variants-config-error.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"image-variants-config-error.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/image-variants-config-error.ts"],"mappings":";;AAMA;;;;;cAAa,wBAAA,SAAiC,KAAK;cAC9B,OAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-variants-config-error.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"image-variants-config-error.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/image-variants-config-error.ts"],"sourcesContent":["/**\n * Thrown when the `uploads.images` configuration is invalid.\n *\n * Raised the first time a variant is requested, so a bad config fails loudly\n * on the first image instead of producing unbounded or surprising derivatives.\n */\nexport class ImageVariantsConfigError extends Error {\n public constructor(message: string) {\n super(`Invalid uploads.images configuration: ${message}`);\n\n this.name = \"ImageVariantsConfigError\";\n }\n}\n"],"mappings":";;;;;;;AAMA,IAAa,2BAAb,cAA8C,MAAM;CAClD,AAAO,YAAY,SAAiB;EAClC,MAAM,yCAAyC,SAAS;EAExD,KAAK,OAAO;CACd;AACF"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { DetectedImageFormat, GeneratedImageDescriptor, GeneratedImageVariant, ResolvedImageVariantsConfig, UploadedFileQuery, VariantOutputFormat, VariantSourceFormat } from "./image-variant-types.mjs";
|
|
2
2
|
import { GenerateImageVariantsOptions, generateImageVariants } from "./generate-image-variants.mjs";
|
|
3
3
|
import { ImageVariantsConfigError } from "./image-variants-config-error.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { PurgeImageVariantsOptions, purgeImageVariants } from "./purge-image-variants.mjs";
|
|
5
|
+
import { ResolveImageVariantInput, ResolvedImageVariant, resolveImageVariant } from "./resolve-image-variant.mjs";
|
|
6
|
+
import { UploadedFileControllerOptions, createUploadedFileController, uploadedFileController } from "./uploaded-file.controller.mjs";
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ImageVariantsConfigError } from "./image-variants-config-error.mjs";
|
|
2
2
|
import { generateImageVariants } from "./generate-image-variants.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { purgeImageVariants } from "./purge-image-variants.mjs";
|
|
4
|
+
import { resolveImageVariant } from "./resolve-image-variant.mjs";
|
|
5
|
+
import { createUploadedFileController, uploadedFileController } from "./uploaded-file.controller.mjs";
|
|
4
6
|
|
|
5
7
|
export { };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-path-inside.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"is-path-inside.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/http/uploads/is-path-inside.ts"],"sourcesContent":["import path from \"node:path\";\n\n/**\n * Whether `candidate` is strictly inside `directory` (never equal to it).\n *\n * Both paths must already be absolute and resolved. The check compares against\n * `directory + path.sep`, so a sibling sharing the directory's name as a prefix\n * (`/srv/storage-evil` next to `/srv/storage`) is outside. On win32 the\n * comparison is case-insensitive, matching the filesystem.\n */\nexport function isPathInside(candidate: string, directory: string): boolean {\n const prefix = directory.endsWith(path.sep) ? directory : directory + path.sep;\n\n if (process.platform === \"win32\") {\n return candidate.toLowerCase().startsWith(prefix.toLowerCase());\n }\n\n return candidate.startsWith(prefix);\n}\n\n/**\n * Whether `candidate` is `directory` itself or anything inside it\n */\nexport function isPathWithin(candidate: string, directory: string): boolean {\n const same =\n process.platform === \"win32\"\n ? candidate.toLowerCase() === directory.toLowerCase()\n : candidate === directory;\n\n return same || isPathInside(candidate, directory);\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,aAAa,WAAmB,WAA4B;CAC1E,MAAM,SAAS,UAAU,SAAS,KAAK,GAAG,IAAI,YAAY,YAAY,KAAK;CAE3E,IAAI,QAAQ,aAAa,SACvB,OAAO,UAAU,YAAY,CAAC,CAAC,WAAW,OAAO,YAAY,CAAC;CAGhE,OAAO,UAAU,WAAW,MAAM;AACpC;;;;AAKA,SAAgB,aAAa,WAAmB,WAA4B;CAM1E,QAJE,QAAQ,aAAa,UACjB,UAAU,YAAY,MAAM,UAAU,YAAY,IAClD,cAAc,cAEL,aAAa,WAAW,SAAS;AAClD"}
|
|
@@ -4,7 +4,7 @@ import "../../image/index.mjs";
|
|
|
4
4
|
import { detectImageFormat } from "./detect-image-format.mjs";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
|
|
7
|
-
//#region
|
|
7
|
+
//#region ../../packages/core/src/http/uploads/load-variant-source.ts
|
|
8
8
|
/**
|
|
9
9
|
* Read a variant source in full and check it before it is decoded.
|
|
10
10
|
*
|