@warlock.js/core 5.19.1 → 5.21.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 +1136 -1073
- package/esm/benchmark/benchmark.mjs +34 -25
- package/esm/benchmark/benchmark.mjs.map +1 -1
- package/esm/benchmark/profiler.d.mts.map +1 -1
- package/esm/benchmark/profiler.mjs +8 -3
- package/esm/benchmark/profiler.mjs.map +1 -1
- package/esm/bootstrap.d.mts.map +1 -1
- package/esm/bootstrap.mjs +3 -0
- package/esm/bootstrap.mjs.map +1 -1
- package/esm/cache/database-cache-driver.d.mts +2 -2
- package/esm/cache/database-cache-driver.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.mjs +3 -2
- package/esm/cache/database-cache-driver.mjs.map +1 -1
- package/esm/cli/cli-commands.manager.mjs +16 -14
- package/esm/cli/cli-commands.manager.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/doctor/checks/optional-peers.check.mjs +3 -2
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs +20 -0
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs +4 -2
- 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 +22 -6
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs +22 -7
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs +8 -6
- 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/templates/stubs.mjs +7 -7
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/writer.mjs +25 -1
- package/esm/cli/commands/generate/utils/writer.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs +5 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes/with-console-on-stderr.mjs +24 -0
- package/esm/cli/commands/routes/with-console-on-stderr.mjs.map +1 -0
- package/esm/cli/commands/routes.command.mjs +3 -4
- package/esm/cli/commands/routes.command.mjs.map +1 -1
- package/esm/cli/commands/start-production.command.mjs +5 -5
- package/esm/cli/commands/start-production.command.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs +2 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/update.command.mjs +1 -0
- package/esm/cli/commands/update.command.mjs.map +1 -1
- package/esm/cli/parse-cli-args.mjs +13 -1
- package/esm/cli/parse-cli-args.mjs.map +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -1
- package/esm/commands/cli-command.mjs +4 -2
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/commands/index.mjs +3 -0
- package/esm/config/config-key-from-path.mjs +20 -0
- package/esm/config/config-key-from-path.mjs.map +1 -0
- package/esm/config/config-loader.d.mts.map +1 -1
- package/esm/config/config-loader.mjs +2 -3
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/connectors/cache-connector.d.mts.map +1 -1
- package/esm/connectors/cache-connector.mjs +5 -0
- package/esm/connectors/cache-connector.mjs.map +1 -1
- package/esm/connectors/connectors-manager.d.mts +4 -0
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +30 -2
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/single-server-warnings.mjs +33 -0
- package/esm/connectors/single-server-warnings.mjs.map +1 -0
- package/esm/connectors/socket-connector.d.mts +29 -0
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs +77 -9
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/storage.connector.d.mts.map +1 -1
- package/esm/connectors/storage.connector.mjs +6 -0
- package/esm/connectors/storage.connector.mjs.map +1 -1
- package/esm/connectors/types.d.mts +4 -2
- package/esm/connectors/types.d.mts.map +1 -1
- package/esm/connectors/types.mjs.map +1 -1
- package/esm/database/models/database-log/database-log.d.mts +47 -24
- package/esm/database/models/database-log/database-log.d.mts.map +1 -1
- package/esm/database/models/database-log/database-log.mjs +36 -4
- package/esm/database/models/database-log/database-log.mjs.map +1 -1
- package/esm/database/seed-command-action.mjs +10 -2
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +15 -4
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/file-event-handler.mjs +26 -6
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.d.mts.map +1 -1
- package/esm/dev-server/file-manager.mjs +2 -2
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +3 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +64 -5
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs +31 -9
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +11 -7
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +1 -1
- package/esm/dev-server/loader/load-hook.mjs +1 -1
- package/esm/dev-server/loader/register-loader.mjs +1 -1
- package/esm/dev-server/manifest-manager.d.mts.map +1 -1
- package/esm/dev-server/manifest-manager.mjs +12 -6
- package/esm/dev-server/manifest-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +9 -0
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/register-dev-shortcuts.mjs +2 -1
- package/esm/dev-server/register-dev-shortcuts.mjs.map +1 -1
- package/esm/dev-server/route-types-publisher.mjs +60 -0
- package/esm/dev-server/route-types-publisher.mjs.map +1 -0
- package/esm/dev-server/shortcuts.mjs +2 -2
- package/esm/dev-server/shortcuts.mjs.map +1 -1
- package/esm/dev-server/special-file-patterns.mjs +18 -0
- package/esm/dev-server/special-file-patterns.mjs.map +1 -0
- package/esm/dev-server/special-files-collector.d.mts.map +1 -1
- package/esm/dev-server/special-files-collector.mjs +2 -5
- package/esm/dev-server/special-files-collector.mjs.map +1 -1
- package/esm/dev-server/start-development-server.mjs +1 -2
- package/esm/dev-server/start-development-server.mjs.map +1 -1
- package/esm/dev-server/supervisor.mjs +23 -3
- package/esm/dev-server/supervisor.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs +22 -12
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs +10 -9
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/encryption/encrypt.d.mts.map +1 -1
- package/esm/encryption/encrypt.mjs +16 -5
- package/esm/encryption/encrypt.mjs.map +1 -1
- package/esm/encryption/hash.d.mts.map +1 -1
- package/esm/encryption/hash.mjs +2 -0
- package/esm/encryption/hash.mjs.map +1 -1
- package/esm/encryption/password.d.mts.map +1 -1
- package/esm/encryption/password.mjs +3 -1
- package/esm/encryption/password.mjs.map +1 -1
- package/esm/generations/features/index.mjs +1 -1
- package/esm/generations/features/mysql.feature.mjs +9 -2
- package/esm/generations/features/mysql.feature.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs +2 -1
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +4 -2
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/http/build-rate-limit-options.d.mts +37 -0
- package/esm/http/build-rate-limit-options.d.mts.map +1 -0
- package/esm/http/build-rate-limit-options.mjs +37 -0
- package/esm/http/build-rate-limit-options.mjs.map +1 -0
- package/esm/http/csrf-origin-policy.d.mts.map +1 -1
- package/esm/http/csrf-origin-policy.mjs +5 -2
- package/esm/http/csrf-origin-policy.mjs.map +1 -1
- package/esm/http/flush-pending-cookies.mjs +52 -0
- package/esm/http/flush-pending-cookies.mjs.map +1 -0
- package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs +4 -2
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.d.mts +5 -0
- package/esm/http/middleware/idempotency.middleware.d.mts.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs +35 -3
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +14 -3
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/rate-limit.middleware.d.mts +4 -3
- package/esm/http/middleware/rate-limit.middleware.d.mts.map +1 -1
- package/esm/http/middleware/rate-limit.middleware.mjs +4 -3
- 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 +11 -2
- package/esm/http/middleware/utils/cidr-match.mjs.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/plugins.d.mts.map +1 -1
- package/esm/http/plugins.mjs +13 -5
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/request.d.mts +12 -1
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +35 -7
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts +16 -0
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs +75 -37
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/types.d.mts +26 -14
- package/esm/http/types.d.mts.map +1 -1
- package/esm/http/uploads/uploaded-file.controller.mjs +1 -1
- package/esm/http/uploads-config.mjs +2 -1
- package/esm/http/uploads-config.mjs.map +1 -1
- package/esm/http/uploads-types.d.mts +3 -3
- package/esm/image/image.d.mts +1 -1
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs +25 -15
- package/esm/image/image.mjs.map +1 -1
- package/esm/index.d.mts +6 -4
- package/esm/index.mjs +7 -4
- package/esm/logger/logger.mjs +6 -1
- package/esm/logger/logger.mjs.map +1 -1
- package/esm/mail/config.d.mts.map +1 -1
- package/esm/mail/config.mjs +31 -21
- package/esm/mail/config.mjs.map +1 -1
- package/esm/mail/react-mail.mjs +6 -3
- package/esm/mail/react-mail.mjs.map +1 -1
- package/esm/mail/send-mail.d.mts.map +1 -1
- package/esm/mail/send-mail.mjs +40 -19
- package/esm/mail/send-mail.mjs.map +1 -1
- package/esm/production/assert-generated-imports.mjs +1 -0
- package/esm/production/assert-generated-imports.mjs.map +1 -1
- package/esm/production/build-contributions.mjs +3 -0
- package/esm/production/build-contributions.mjs.map +1 -1
- package/esm/production/esbuild-plugins.mjs +3 -2
- package/esm/production/esbuild-plugins.mjs.map +1 -1
- package/esm/production/production-builder.mjs +49 -37
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/production-supervisor.mjs +12 -5
- package/esm/production/production-supervisor.mjs.map +1 -1
- package/esm/production/route-registration-child.d.mts +1 -0
- package/esm/production/route-registration-child.mjs +65 -0
- package/esm/production/route-registration-child.mjs.map +1 -0
- package/esm/production/route-registration-snapshot.mjs +166 -0
- package/esm/production/route-registration-snapshot.mjs.map +1 -0
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts +3 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs +22 -12
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +3 -3
- package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs +88 -61
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/index.d.mts +1 -1
- package/esm/repositories/adapters/cascade/index.mjs +1 -1
- package/esm/repositories/contracts/query-builder.contract.d.mts +3 -3
- package/esm/repositories/contracts/types.d.mts +5 -0
- package/esm/repositories/contracts/types.d.mts.map +1 -1
- package/esm/repositories/index.d.mts +1 -1
- package/esm/repositories/index.mjs +1 -1
- package/esm/repositories/repository.manager.d.mts +65 -3
- package/esm/repositories/repository.manager.d.mts.map +1 -1
- package/esm/repositories/repository.manager.mjs +146 -57
- package/esm/repositories/repository.manager.mjs.map +1 -1
- package/esm/resource/resource-field-builder.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.mjs +1 -0
- package/esm/resource/resource-field-builder.mjs.map +1 -1
- package/esm/resource/resource.d.mts +10 -3
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs +19 -5
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/restful/restful.d.mts +12 -5
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs +47 -16
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/build-route-rate-limit.mjs +22 -0
- package/esm/router/build-route-rate-limit.mjs.map +1 -0
- package/esm/router/dev-dispatch.mjs +60 -0
- package/esm/router/dev-dispatch.mjs.map +1 -0
- package/esm/router/route-builder.mjs +4 -1
- package/esm/router/route-builder.mjs.map +1 -1
- package/esm/router/route-registry.d.mts.map +1 -1
- package/esm/router/route-registry.mjs +2 -2
- package/esm/router/route-registry.mjs.map +1 -1
- package/esm/router/router.d.mts +1 -1
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +57 -39
- package/esm/router/router.mjs.map +1 -1
- package/esm/socket/index.d.mts +1 -1
- package/esm/socket/types.d.mts +25 -3
- package/esm/socket/types.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts +26 -0
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs +100 -16
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/drivers/do-spaces-driver.d.mts +7 -0
- package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
- package/esm/storage/drivers/do-spaces-driver.mjs +9 -2
- package/esm/storage/drivers/do-spaces-driver.mjs.map +1 -1
- package/esm/storage/drivers/local-driver.d.mts +16 -5
- package/esm/storage/drivers/local-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.mjs +71 -28
- package/esm/storage/drivers/local-driver.mjs.map +1 -1
- package/esm/storage/index.d.mts +3 -1
- package/esm/storage/index.mjs +4 -2
- package/esm/storage/scoped-storage.d.mts +27 -1
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs +40 -8
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage.d.mts +11 -5
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/storage.mjs +39 -16
- package/esm/storage/storage.mjs.map +1 -1
- package/esm/storage/types.d.mts +43 -4
- package/esm/storage/types.d.mts.map +1 -1
- package/esm/storage/utils/classify-ip-address.d.mts +13 -0
- package/esm/storage/utils/classify-ip-address.d.mts.map +1 -0
- package/esm/storage/utils/classify-ip-address.mjs +98 -0
- package/esm/storage/utils/classify-ip-address.mjs.map +1 -0
- package/esm/storage/utils/safe-fetch.d.mts +3 -12
- package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs +3 -48
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/storage/utils/storage-capability-error.d.mts +25 -0
- package/esm/storage/utils/storage-capability-error.d.mts.map +1 -0
- package/esm/storage/utils/storage-capability-error.mjs +26 -0
- package/esm/storage/utils/storage-capability-error.mjs.map +1 -0
- package/esm/updater/update-warlock-packages.mjs +11 -1
- package/esm/updater/update-warlock-packages.mjs.map +1 -1
- package/esm/use-cases/use-case-broadcast.mjs +7 -1
- package/esm/use-cases/use-case-broadcast.mjs.map +1 -1
- package/esm/use-cases/use-cases-registry.mjs +21 -2
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/database-log.mjs +0 -3
- package/esm/utils/database-log.mjs.map +1 -1
- package/esm/utils/queue.d.mts +6 -4
- package/esm/utils/queue.d.mts.map +1 -1
- package/esm/utils/queue.mjs +20 -9
- package/esm/utils/queue.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-id.mjs +6 -2
- package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
- package/esm/validation/database/unique-except-current-id.mjs +6 -2
- package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
- package/esm/validation/init.mjs +6 -2
- package/esm/validation/init.mjs.map +1 -1
- package/esm/validation/validateAll.mjs +3 -2
- package/esm/validation/validateAll.mjs.map +1 -1
- package/esm/warlock-config/types.d.mts +14 -1
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs +3 -1
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/esm/warlock-config/warn-deprecated-config.mjs +25 -0
- package/esm/warlock-config/warn-deprecated-config.mjs.map +1 -0
- package/llms-full.txt +71 -42
- package/llms.txt +41 -41
- package/package.json +21 -15
- package/skills/add-connector/SKILL.md +5 -1
- package/skills/benchmark-code/SKILL.md +1 -1
- package/skills/build-restful/SKILL.md +1 -1
- package/skills/build-url/SKILL.md +1 -1
- package/skills/configure-app/SKILL.md +7 -1
- package/skills/create-controller/SKILL.md +1 -1
- package/skills/create-module/SKILL.md +1 -1
- package/skills/define-resource/SKILL.md +1 -1
- package/skills/encrypt-data/SKILL.md +1 -1
- package/skills/hash-password/SKILL.md +1 -1
- package/skills/health-checks/SKILL.md +1 -1
- package/skills/index.md +30 -0
- package/skills/lower-stage3-decorators/SKILL.md +1 -1
- package/skills/process-image/SKILL.md +1 -1
- package/skills/register-route/SKILL.md +1 -1
- package/skills/request-memo/SKILL.md +1 -1
- package/skills/request-tracing/SKILL.md +1 -1
- package/skills/resolve-path/SKILL.md +1 -1
- package/skills/retry-operation/SKILL.md +1 -1
- package/skills/run-app/SKILL.md +1 -1
- package/skills/send-mail/SKILL.md +1 -1
- package/skills/send-response/SKILL.md +1 -1
- package/skills/store-file/SKILL.md +11 -1
- package/skills/test-http/SKILL.md +1 -1
- package/skills/test-service/SKILL.md +1 -1
- package/skills/update-packages/SKILL.md +1 -1
- package/skills/upload-file/SKILL.md +1 -1
- package/skills/use-app-context/SKILL.md +1 -1
- package/skills/use-localization/SKILL.md +1 -1
- package/skills/use-middleware/SKILL.md +7 -2
- package/skills/use-model-transformers/SKILL.md +1 -1
- package/skills/use-repository/SKILL.md +1 -1
- package/skills/use-request-locals/SKILL.md +1 -1
- package/skills/validate-input/SKILL.md +1 -1
- package/skills/warlock-conventions/SKILL.md +1 -1
- package/skills/warlock-doctor/SKILL.md +1 -1
- package/skills/warlock-routes/SKILL.md +1 -1
- package/skills/wire-socket/SKILL.md +5 -1
- package/skills/write-cli-command/SKILL.md +1 -1
- package/skills/write-middleware/SKILL.md +1 -1
- package/skills/write-seeder/SKILL.md +1 -1
- package/skills/write-use-case/SKILL.md +1 -1
- package/esm/_virtual/_rolldown/runtime.mjs +0 -7
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { BadRequestError } from "../../../http/errors/errors.mjs";
|
|
2
|
+
import dayjs from "dayjs";
|
|
3
|
+
import customParseFormat from "dayjs/plugin/customParseFormat.js";
|
|
4
|
+
|
|
1
5
|
//#region ../core/src/repositories/adapters/cascade/filter-applicator.ts
|
|
6
|
+
dayjs.extend(customParseFormat);
|
|
2
7
|
/**
|
|
3
8
|
* Applies repository filters to a Cascade-Next query builder
|
|
4
9
|
* Translates repository filter rules into Cascade query builder method calls
|
|
@@ -118,6 +123,31 @@ var FilterApplicator = class {
|
|
|
118
123
|
}[type];
|
|
119
124
|
}
|
|
120
125
|
/**
|
|
126
|
+
* `like` is a contains match on every driver: a value without an explicit
|
|
127
|
+
* `%` is wrapped as `%value%` (Postgres would otherwise match exactly while
|
|
128
|
+
* Mongo matched a substring). Values carrying their own `%` are kept as-is.
|
|
129
|
+
*/
|
|
130
|
+
likePattern(value) {
|
|
131
|
+
if (typeof value !== "string" || value.includes("%")) return value;
|
|
132
|
+
return `%${value}%`;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Coerce a client value to an integer or reject with a 400.
|
|
136
|
+
*/
|
|
137
|
+
toInt(value) {
|
|
138
|
+
const parsed = parseInt(value);
|
|
139
|
+
if (Number.isNaN(parsed)) throw new BadRequestError(`Invalid integer filter value "${value}"`);
|
|
140
|
+
return parsed;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Coerce a client value to a number or reject with a 400.
|
|
144
|
+
*/
|
|
145
|
+
toNumber(value) {
|
|
146
|
+
const parsed = Number(value);
|
|
147
|
+
if (value === "" || value === null || Number.isNaN(parsed)) throw new BadRequestError(`Invalid number filter value "${value}"`);
|
|
148
|
+
return parsed;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
121
151
|
* Apply standard where operators
|
|
122
152
|
*/
|
|
123
153
|
applyWhereOperator(query, operator, column, columns, value, ruleName) {
|
|
@@ -143,7 +173,13 @@ var FilterApplicator = class {
|
|
|
143
173
|
query.whereNotIn(column, Array.isArray(value) ? value : [value]);
|
|
144
174
|
break;
|
|
145
175
|
case "like":
|
|
146
|
-
query.whereLike(column, value);
|
|
176
|
+
query.whereLike(column, this.likePattern(value));
|
|
177
|
+
break;
|
|
178
|
+
case "startsWith":
|
|
179
|
+
query.whereLike(column, `${value}%`);
|
|
180
|
+
break;
|
|
181
|
+
case "endsWith":
|
|
182
|
+
query.whereLike(column, `%${value}`);
|
|
147
183
|
break;
|
|
148
184
|
case "not like":
|
|
149
185
|
query.whereNotLike(column, value);
|
|
@@ -155,17 +191,24 @@ var FilterApplicator = class {
|
|
|
155
191
|
query.whereNotBetween(column, value);
|
|
156
192
|
break;
|
|
157
193
|
}
|
|
158
|
-
else if (columns) if (operator === "=")
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
194
|
+
else if (columns) if (operator === "=") this.anyOf(query, columns, (q, col) => q.where(col, value));
|
|
195
|
+
else if (operator === "like") this.anyOf(query, columns, (q, col) => q.where(col, "like", this.likePattern(value)));
|
|
196
|
+
else throw new Error(`Unsupported multi-column filter operator "${operator}" for rule "${ruleName ?? columns.join(",")}". Multi-column filters only support "=" and "like".`);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Apply a condition across several columns as ONE grouped OR: `(a OR b OR ...)`.
|
|
200
|
+
* The group is ANDed with the rest of the query, so a tenant filter
|
|
201
|
+
* (`org_id = 5`) can never be widened by a multi-column search. Never emit a
|
|
202
|
+
* top-level `orWhere` for multi-column filters.
|
|
203
|
+
*/
|
|
204
|
+
anyOf(query, columns, apply) {
|
|
205
|
+
query.where((group) => {
|
|
163
206
|
columns.forEach((col, index) => {
|
|
164
|
-
|
|
165
|
-
|
|
207
|
+
const build = (q) => apply(q, col);
|
|
208
|
+
if (index === 0) group.where(build);
|
|
209
|
+
else group.orWhere(build);
|
|
166
210
|
});
|
|
167
211
|
});
|
|
168
|
-
else throw new Error(`Unsupported multi-column filter operator "${operator}" for rule "${ruleName ?? columns.join(",")}". Multi-column filters only support "=" and "like".`);
|
|
169
212
|
}
|
|
170
213
|
/**
|
|
171
214
|
* Coerce a filter value to a boolean.
|
|
@@ -186,66 +229,50 @@ var FilterApplicator = class {
|
|
|
186
229
|
handleBoolean(query, column, columns, value) {
|
|
187
230
|
const boolValue = this.coerceBoolean(value);
|
|
188
231
|
if (column) query.where(column, boolValue);
|
|
189
|
-
else if (columns)
|
|
190
|
-
const conditions = {};
|
|
191
|
-
for (const col of columns) conditions[col] = boolValue;
|
|
192
|
-
query.orWhere(conditions);
|
|
193
|
-
}
|
|
232
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, boolValue));
|
|
194
233
|
}
|
|
195
234
|
handleInt(query, column, columns, value) {
|
|
196
|
-
const intValue =
|
|
235
|
+
const intValue = this.toInt(value);
|
|
197
236
|
if (column) query.where(column, intValue);
|
|
198
|
-
else if (columns)
|
|
199
|
-
const conditions = {};
|
|
200
|
-
for (const col of columns) conditions[col] = intValue;
|
|
201
|
-
query.orWhere(conditions);
|
|
202
|
-
}
|
|
237
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, intValue));
|
|
203
238
|
}
|
|
204
239
|
handleNotInt(query, column, columns, value) {
|
|
205
|
-
const intValue =
|
|
240
|
+
const intValue = this.toInt(value);
|
|
206
241
|
if (column) query.where(column, "!=", intValue);
|
|
207
|
-
else if (columns)
|
|
242
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, "!=", intValue));
|
|
208
243
|
}
|
|
209
244
|
handleIntComparison(query, column, columns, value, operator) {
|
|
210
|
-
const intValue =
|
|
245
|
+
const intValue = this.toInt(value);
|
|
211
246
|
if (column) query.where(column, operator, intValue);
|
|
212
|
-
else if (columns)
|
|
247
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, operator, intValue));
|
|
213
248
|
}
|
|
214
249
|
handleInInt(query, column, columns, value) {
|
|
215
|
-
const values = (Array.isArray(value) ? value : [value]).map((v) =>
|
|
250
|
+
const values = (Array.isArray(value) ? value : [value]).map((v) => this.toInt(v));
|
|
216
251
|
if (column) query.whereIn(column, values);
|
|
217
|
-
else if (columns)
|
|
252
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereIn(col, values));
|
|
218
253
|
}
|
|
219
254
|
handleNumber(query, column, columns, value) {
|
|
220
|
-
const numValue =
|
|
255
|
+
const numValue = this.toNumber(value);
|
|
221
256
|
if (column) query.where(column, numValue);
|
|
222
|
-
else if (columns)
|
|
223
|
-
const conditions = {};
|
|
224
|
-
for (const col of columns) conditions[col] = numValue;
|
|
225
|
-
query.orWhere(conditions);
|
|
226
|
-
}
|
|
257
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, numValue));
|
|
227
258
|
}
|
|
228
259
|
handleInNumber(query, column, columns, value) {
|
|
229
|
-
const values = (Array.isArray(value) ? value : [value]).map((v) =>
|
|
260
|
+
const values = (Array.isArray(value) ? value : [value]).map((v) => this.toNumber(v));
|
|
230
261
|
if (column) query.whereIn(column, values);
|
|
231
|
-
else if (columns)
|
|
262
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereIn(col, values));
|
|
232
263
|
}
|
|
233
264
|
handleFloat(query, column, columns, value) {
|
|
234
265
|
const floatValue = parseFloat(value);
|
|
235
266
|
if (column) query.where(column, floatValue);
|
|
236
|
-
else if (columns)
|
|
237
|
-
const conditions = {};
|
|
238
|
-
for (const col of columns) conditions[col] = floatValue;
|
|
239
|
-
query.orWhere(conditions);
|
|
240
|
-
}
|
|
267
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, floatValue));
|
|
241
268
|
}
|
|
242
269
|
handleNull(query, column, columns) {
|
|
243
270
|
if (column) query.whereNull(column);
|
|
244
|
-
else if (columns)
|
|
271
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereNull(col));
|
|
245
272
|
}
|
|
246
273
|
handleNotNull(query, column, columns) {
|
|
247
274
|
if (column) query.whereNotNull(column);
|
|
248
|
-
else if (columns)
|
|
275
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereNotNull(col));
|
|
249
276
|
}
|
|
250
277
|
/**
|
|
251
278
|
* Handle scope filter - applies local scope and passes the filter value.
|
|
@@ -339,67 +366,67 @@ var FilterApplicator = class {
|
|
|
339
366
|
handleDate(query, column, columns, value, options) {
|
|
340
367
|
const dateValue = this.parseDate(value, options?.dateFormat);
|
|
341
368
|
if (column) query.whereDate(column, dateValue);
|
|
342
|
-
else if (columns)
|
|
369
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereDate(col, dateValue));
|
|
343
370
|
}
|
|
344
371
|
handleDateComparison(query, column, columns, value, options, operator) {
|
|
345
372
|
const dateValue = this.parseDate(value, options?.dateFormat);
|
|
346
373
|
if (column) if (operator === ">" || operator === ">=") query.whereDateAfter(column, dateValue);
|
|
347
374
|
else query.whereDateBefore(column, dateValue);
|
|
348
|
-
else if (columns)
|
|
349
|
-
|
|
375
|
+
else if (columns) {
|
|
376
|
+
const after = operator === ">" || operator === ">=";
|
|
377
|
+
this.anyOf(query, columns, (q, col) => after ? q.whereDateAfter(col, dateValue) : q.whereDateBefore(col, dateValue));
|
|
378
|
+
}
|
|
350
379
|
}
|
|
351
380
|
handleDateBetween(query, column, columns, value, options) {
|
|
352
381
|
if (!Array.isArray(value) || value.length !== 2) return;
|
|
353
382
|
const [start, end] = value.map((v) => this.parseDate(v, options?.dateFormat));
|
|
354
383
|
if (start === void 0 || end === void 0) return;
|
|
355
384
|
if (column) query.whereDateBetween(column, [start, end]);
|
|
356
|
-
else if (columns)
|
|
385
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereDateBetween(col, [start, end]));
|
|
357
386
|
}
|
|
358
387
|
handleInDate(query, column, columns, value, options) {
|
|
359
388
|
const dates = (Array.isArray(value) ? value : [value]).map((v) => this.parseDate(v, options?.dateFormat));
|
|
360
389
|
if (column) query.whereIn(column, dates);
|
|
361
|
-
else if (columns)
|
|
390
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereIn(col, dates));
|
|
362
391
|
}
|
|
363
392
|
handleDateTime(query, column, columns, value, options) {
|
|
364
393
|
const dateValue = this.parseDateTime(value, options?.dateTimeFormat);
|
|
365
394
|
if (column) query.where(column, dateValue);
|
|
366
|
-
else if (columns)
|
|
367
|
-
const conditions = {};
|
|
368
|
-
for (const col of columns) conditions[col] = dateValue;
|
|
369
|
-
query.orWhere(conditions);
|
|
370
|
-
}
|
|
395
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, dateValue));
|
|
371
396
|
}
|
|
372
397
|
handleDateTimeComparison(query, column, columns, value, options, operator) {
|
|
373
398
|
const dateValue = this.parseDateTime(value, options?.dateTimeFormat);
|
|
374
399
|
if (column) query.where(column, operator, dateValue);
|
|
375
|
-
else if (columns)
|
|
400
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, operator, dateValue));
|
|
376
401
|
}
|
|
377
402
|
handleDateTimeBetween(query, column, columns, value, options) {
|
|
378
403
|
if (!Array.isArray(value) || value.length !== 2) return;
|
|
379
404
|
const [start, end] = value.map((v) => this.parseDateTime(v, options?.dateTimeFormat));
|
|
380
405
|
if (column) query.whereBetween(column, [start, end]);
|
|
381
|
-
else if (columns)
|
|
406
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereBetween(col, [start, end]));
|
|
382
407
|
}
|
|
383
408
|
handleInDateTime(query, column, columns, value, options) {
|
|
384
409
|
const dates = (Array.isArray(value) ? value : [value]).map((v) => this.parseDateTime(v, options?.dateTimeFormat));
|
|
385
410
|
if (column) query.whereIn(column, dates);
|
|
386
|
-
else if (columns)
|
|
411
|
+
else if (columns) this.anyOf(query, columns, (q, col) => q.whereIn(col, dates));
|
|
387
412
|
}
|
|
388
413
|
/**
|
|
389
|
-
* Parse date string
|
|
390
|
-
* TODO: Implement proper date parsing with format support
|
|
414
|
+
* Parse a date string with the declared format; invalid input is a 400.
|
|
391
415
|
*/
|
|
392
416
|
parseDate(value, format) {
|
|
393
|
-
|
|
394
|
-
return new Date(value);
|
|
417
|
+
return this.parseWithFormat(value, format);
|
|
395
418
|
}
|
|
396
419
|
/**
|
|
397
|
-
* Parse datetime string
|
|
398
|
-
* TODO: Implement proper datetime parsing with format support
|
|
420
|
+
* Parse a datetime string with the declared format; invalid input is a 400.
|
|
399
421
|
*/
|
|
400
422
|
parseDateTime(value, format) {
|
|
423
|
+
return this.parseWithFormat(value, format);
|
|
424
|
+
}
|
|
425
|
+
parseWithFormat(value, format) {
|
|
401
426
|
if (value instanceof Date) return value;
|
|
402
|
-
|
|
427
|
+
const parsed = format ? dayjs(String(value), format, true) : dayjs(value);
|
|
428
|
+
if (!parsed.isValid()) throw new BadRequestError(`Invalid date filter value "${value}"${format ? ` (expected ${format})` : ""}`);
|
|
429
|
+
return parsed.toDate();
|
|
403
430
|
}
|
|
404
431
|
};
|
|
405
432
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-applicator.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/filter-applicator.ts"],"sourcesContent":["import type {\n QueryBuilderContract as CascadeQueryBuilder,\n QueryBuilderContract,\n} from \"@warlock.js/cascade\";\nimport type { FilterOptions, FilterRule, FilterRules } from \"../../contracts\";\n\n/**\n * Applies repository filters to a Cascade-Next query builder\n * Translates repository filter rules into Cascade query builder method calls\n */\nexport class FilterApplicator {\n /**\n * Apply filters to a Cascade query builder\n *\n * @param query - Cascade query builder instance\n * @param filters - Filter structure defining how to filter\n * @param data - Data containing filter values\n * @param options - Additional filter options (date formats, etc.)\n */\n public apply(\n query: CascadeQueryBuilder<any>,\n filters: FilterRules,\n data: any,\n options: FilterOptions,\n ): void {\n // `Object.entries` hands the rule over already narrowed; a `for...in` index\n // read is `FilterRule | undefined` under the strictness contract.\n for (const [key, filterRule] of Object.entries(filters)) {\n const value = data[key];\n\n if (value === undefined) continue;\n if (filterRule === undefined) continue;\n\n const rule = this.parseFilterRule(key, filterRule);\n this.applyFilterRule(query, rule, value, data, options);\n }\n }\n\n /**\n * Parse a filter rule into a structured format\n */\n private parseFilterRule(key: string, rule: FilterRule) {\n // Handle custom function\n if (typeof rule === \"function\") {\n return { type: \"function\", fn: rule, column: key, key };\n }\n\n // Handle array format: [\"operator\"] or [\"operator\", \"column\"] or [\"operator\", [\"col1\", \"col2\"]]\n if (Array.isArray(rule)) {\n const [operator, target] = rule;\n\n if (target === undefined) {\n return { type: operator, column: key, columns: undefined, key };\n }\n\n if (Array.isArray(target)) {\n return { type: operator, column: undefined, columns: target, key };\n }\n\n return { type: operator, column: target, columns: undefined, key };\n }\n\n // Handle simple operator string\n return { type: rule, column: key, columns: undefined, key };\n }\n\n /**\n * Apply a single filter rule to the query\n */\n private applyFilterRule(\n query: CascadeQueryBuilder<any>,\n rule: any,\n value: any,\n data: any,\n options: FilterOptions,\n ): void {\n // 1. Custom function filter\n if (rule.type === \"function\") {\n rule.fn(value, query, data);\n return;\n }\n\n // 2. Predefined filter types\n const handler = this.getFilterHandler(rule.type);\n if (handler) {\n handler.call(this, query, rule.column, rule.columns, value, options);\n return;\n }\n\n // 3. Standard where operators\n this.applyWhereOperator(query, rule.type, rule.column, rule.columns, value, rule.key);\n }\n\n /**\n * Get filter handler for predefined types\n */\n private getFilterHandler(type: string): Function | undefined {\n const handlers: Record<string, Function> = {\n // Boolean filters\n bool: this.handleBoolean,\n boolean: this.handleBoolean,\n\n // Numeric filters\n int: this.handleInt,\n integer: this.handleInt,\n \"!int\": this.handleNotInt,\n \"int>\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">\"),\n \"int>=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">=\"),\n \"int<\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<\"),\n \"int<=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<=\"),\n inInt: this.handleInInt,\n number: this.handleNumber,\n inNumber: this.handleInNumber,\n float: this.handleFloat,\n double: this.handleFloat,\n inFloat: this.handleInNumber,\n\n // Null filters\n null: this.handleNull,\n notNull: this.handleNotNull,\n \"!null\": this.handleNotNull,\n\n // Date filters\n date: this.handleDate,\n \"date>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">\"),\n \"date>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">=\"),\n \"date<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<\"),\n \"date<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<=\"),\n dateBetween: this.handleDateBetween,\n inDate: this.handleInDate,\n\n // DateTime filters\n dateTime: this.handleDateTime,\n \"dateTime>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">\"),\n \"dateTime>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">=\"),\n \"dateTime<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<\"),\n \"dateTime<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<=\"),\n dateTimeBetween: this.handleDateTimeBetween,\n inDateTime: this.handleInDateTime,\n\n // Scope filter - applies local scope when value is truthy\n scope: this.handleScope,\n\n // With filter - eager-loads a relation when value is truthy\n with: this.handleWith,\n\n // JoinWith filter - eager-loads a relation via SQL JOIN when value is truthy\n joinWith: this.handleJoinWith,\n\n // Vector similarity search — calls similarTo(column, embedding[])\n similarTo: this.handleSimilarTo,\n };\n\n return handlers[type];\n }\n\n /**\n * Apply standard where operators\n */\n private applyWhereOperator(\n query: QueryBuilderContract,\n operator: string,\n column?: string,\n columns?: string[],\n value?: any,\n ruleName?: string,\n ): void {\n // Handle \"in\" prefix for array values\n if (operator.startsWith(\"in\") && operator !== \"int\" && !Array.isArray(value)) {\n value = [value];\n }\n\n // Single column\n if (column) {\n switch (operator) {\n case \"=\":\n query.where(column, value);\n break;\n case \"!=\":\n case \"<>\":\n query.where(column, \"!=\", value);\n break;\n case \">\":\n case \">=\":\n case \"<\":\n case \"<=\":\n query.where(column, operator, value);\n break;\n case \"in\":\n query.whereIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"not in\":\n query.whereNotIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"like\":\n query.whereLike(column, value);\n break;\n case \"not like\":\n query.whereNotLike(column, value);\n break;\n case \"between\":\n query.whereBetween(column, value);\n break;\n case \"not between\":\n query.whereNotBetween(column, value);\n break;\n }\n }\n // Multiple columns (OR condition)\n else if (columns) {\n if (operator === \"=\") {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = value;\n }\n query.orWhere(conditions);\n } else if (operator === \"like\") {\n // (col0 LIKE value OR col1 LIKE value OR ...), OR'd with prior conditions —\n // the object form of orWhere() only ever emits \"=\", so \"like\" needs the\n // callback/group form to keep the operator instead of silently degrading\n // to equality (D1).\n query.orWhere((sub: QueryBuilderContract) => {\n columns.forEach((col, index) => {\n if (index === 0) {\n sub.where(col, \"like\", value);\n } else {\n sub.orWhere(col, \"like\", value);\n }\n });\n });\n } else {\n // Every other operator silently degraded to equality via the object form\n // of orWhere() — refuse instead of returning a correct-looking, wrong query.\n throw new Error(\n `Unsupported multi-column filter operator \"${operator}\" for rule \"${ruleName ?? columns.join(\",\")}\". ` +\n `Multi-column filters only support \"=\" and \"like\".`,\n );\n }\n }\n }\n\n // ============================================================================\n // BOOLEAN FILTERS\n // ============================================================================\n\n /**\n * Coerce a filter value to a boolean.\n *\n * Recognized truthy forms: `true`, `1`, `\"true\"`, `\"1\"`.\n * Recognized falsy forms: `false`, `0`, `\"false\"`, `\"0\"`.\n * Anything else falls back to `Boolean(value)`.\n *\n * NOTE: this intentionally does NOT treat \"non-empty\" as true — the previous\n * `|| !isEmpty(value)` fallback coerced `false`/`0` to `true`, inverting the\n * filter for explicitly-false values.\n */\n private coerceBoolean(value: any): boolean {\n if (value === true || value === 1 || value === \"true\" || value === \"1\") {\n return true;\n }\n\n if (value === false || value === 0 || value === \"false\" || value === \"0\") {\n return false;\n }\n\n return Boolean(value);\n }\n\n private handleBoolean(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const boolValue = this.coerceBoolean(value);\n if (column) {\n query.where(column, boolValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = boolValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // NUMERIC FILTERS\n // ============================================================================\n private handleInt(query: QueryBuilderContract, column?: string, columns?: string[], value?: any) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, intValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = intValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleNotInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, \"!=\", intValue);\n } else if (columns) {\n // Use multiple orWhere calls for OR logic across columns\n for (const col of columns) {\n query.orWhere(col, \"!=\", intValue);\n }\n }\n }\n\n private handleIntComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n operator: string,\n ) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, operator, intValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere(col, operator, intValue);\n }\n }\n }\n\n private handleInInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => parseInt(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, values));\n }\n }\n }\n\n private handleNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const numValue = Number(value);\n if (column) {\n query.where(column, numValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = numValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleInNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => Number(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, values));\n }\n }\n }\n\n private handleFloat(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const floatValue = parseFloat(value);\n if (column) {\n query.where(column, floatValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = floatValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // NULL FILTERS\n // ============================================================================\n\n private handleNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNull(column);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere({ [col]: null });\n }\n }\n }\n\n private handleNotNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNotNull(column);\n } else if (columns) {\n // Use whereNotNull for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereNotNull(col));\n }\n }\n }\n\n // ============================================================================\n // SCOPE FILTER\n // ============================================================================\n\n /**\n * Handle scope filter - applies local scope and passes the filter value.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * active: \"scope\", // Uses the filter key as scope name\n * isAdmin: [\"scope\", \"admin\"] // Uses custom scope name\n * }\n * ```\n *\n * When list({ active: true }) is called, it will call query.scope(\"active\", true)\n * When list({ status: \"pending\" }) is called, it will call query.scope(\"status\", \"pending\")\n */\n private handleScope(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n // column holds the scope name (either the filter key or custom name from array format)\n if (column) {\n query.scope(column, value);\n }\n }\n\n // ============================================================================\n // WITH (EAGER LOAD) FILTER\n // ============================================================================\n\n /**\n * Handle with filter - eager-loads a relation when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"with\", \"ai_model\"], // load single relation\n * with_all: [\"with\", [\"ai_model\", \"unit\"]] // load multiple relations\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.with(\"ai_model\")\n */\n private handleWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n // Load a single named relation\n if (column) {\n if (query.with) {\n query.with(column);\n }\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n for (const relation of columns) {\n if (query.with) {\n query.with(relation);\n }\n }\n }\n }\n\n /**\n * Handle joinWith filter - eager-loads a relation via SQL JOIN when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"joinWith\", \"ai_model\"], // load single relation via join\n * with_all: [\"joinWith\", [\"ai_model\", \"unit\"]] // load multiple relations via join\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.joinWith(\"ai_model\")\n */\n private handleJoinWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n if (!query.joinWith) {\n console.warn(\n \"[Repository] joinWith is not supported by the query builder. using with instead.\",\n );\n return this.handleWith(query, column, columns, value);\n }\n\n // Load a single named relation\n if (column) {\n query.joinWith(column);\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n query.joinWith(...columns);\n }\n }\n\n // ============================================================================\n // VECTOR FILTER\n // ============================================================================\n\n /**\n * Handle similarTo filter — performs vector similarity search.\n *\n * The filter value must be a `number[]` (pre-computed embedding).\n * Delegates to `query.similarTo(column, embedding)` which is handled\n * driver-specifically (pgvector or MongoDB Atlas $vectorSearch).\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * organization_id: \"=\",\n * embedding: \"similarTo\",\n * }\n * ```\n *\n * Then in the service:\n * ```typescript\n * await vectorsRepository.list({ embedding: queryEmbedding, organization_id: orgId });\n * ```\n */\n private handleSimilarTo(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n if (!column || !Array.isArray(value)) return;\n\n // Cast to any: Cascade's internal QueryBuilderContract still uses nearestTo;\n // our wrapper (CascadeQueryBuilder.similarTo) delegates to it correctly.\n (query as any).similarTo(column, value);\n }\n\n // ============================================================================\n // DATE FILTERS\n // ============================================================================\n\n private handleDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n query.whereDate(column, dateValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereDate(col, dateValue));\n }\n }\n }\n\n private handleDateComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n if (operator === \">\" || operator === \">=\") {\n query.whereDateAfter(column, dateValue);\n } else {\n query.whereDateBefore(column, dateValue);\n }\n } else if (columns) {\n for (const col of columns) {\n if (operator === \">\" || operator === \">=\") {\n query.orWhere((q: any) => q.whereDateAfter(col, dateValue));\n } else {\n query.orWhere((q: any) => q.whereDateBefore(col, dateValue));\n }\n }\n }\n }\n\n private handleDateBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDate(v, options?.dateFormat));\n\n // `value.length === 2` is checked above, so both exist — but the compiler\n // does not narrow destructured elements from a length test. Returning\n // rather than defaulting: this builds a date RANGE for a query, and a\n // half-formed range would silently filter on a boundary the caller never\n // asked for.\n if (start === undefined || end === undefined) return;\n\n if (column) {\n query.whereDateBetween(column, [start, end]);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereDateBetween(col, [start, end]));\n }\n }\n }\n\n private handleInDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDate(v, options?.dateFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, dates));\n }\n }\n }\n\n // ============================================================================\n // DATETIME FILTERS\n // ============================================================================\n\n private handleDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, dateValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = dateValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleDateTimeComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, operator, dateValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere(col, operator, dateValue);\n }\n }\n }\n\n private handleDateTimeBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDateTime(v, options?.dateTimeFormat));\n if (column) {\n query.whereBetween(column, [start, end]);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereBetween(col, [start, end]));\n }\n }\n }\n\n private handleInDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDateTime(v, options?.dateTimeFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, dates));\n }\n }\n }\n\n // ============================================================================\n // DATE PARSING UTILITIES\n // ============================================================================\n\n /**\n * Parse date string to Date object\n * TODO: Implement proper date parsing with format support\n */\n private parseDate(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n return new Date(value);\n }\n\n /**\n * Parse datetime string to Date object\n * TODO: Implement proper datetime parsing with format support\n */\n private parseDateTime(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n return new Date(value);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filter-applicator.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/filter-applicator.ts"],"sourcesContent":["import type {\n QueryBuilderContract as CascadeQueryBuilder,\n QueryBuilderContract,\n} from \"@warlock.js/cascade\";\nimport dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat.js\";\nimport { BadRequestError } from \"../../../http/errors/errors\";\nimport type { FilterOptions, FilterRule, FilterRules } from \"../../contracts\";\n\ndayjs.extend(customParseFormat);\n\n/**\n * Applies repository filters to a Cascade-Next query builder\n * Translates repository filter rules into Cascade query builder method calls\n */\nexport class FilterApplicator {\n /**\n * Apply filters to a Cascade query builder\n *\n * @param query - Cascade query builder instance\n * @param filters - Filter structure defining how to filter\n * @param data - Data containing filter values\n * @param options - Additional filter options (date formats, etc.)\n */\n public apply(\n query: CascadeQueryBuilder<any>,\n filters: FilterRules,\n data: any,\n options: FilterOptions,\n ): void {\n // `Object.entries` hands the rule over already narrowed; a `for...in` index\n // read is `FilterRule | undefined` under the strictness contract.\n for (const [key, filterRule] of Object.entries(filters)) {\n const value = data[key];\n\n if (value === undefined) continue;\n if (filterRule === undefined) continue;\n\n const rule = this.parseFilterRule(key, filterRule);\n this.applyFilterRule(query, rule, value, data, options);\n }\n }\n\n /**\n * Parse a filter rule into a structured format\n */\n private parseFilterRule(key: string, rule: FilterRule) {\n // Handle custom function\n if (typeof rule === \"function\") {\n return { type: \"function\", fn: rule, column: key, key };\n }\n\n // Handle array format: [\"operator\"] or [\"operator\", \"column\"] or [\"operator\", [\"col1\", \"col2\"]]\n if (Array.isArray(rule)) {\n const [operator, target] = rule;\n\n if (target === undefined) {\n return { type: operator, column: key, columns: undefined, key };\n }\n\n if (Array.isArray(target)) {\n return { type: operator, column: undefined, columns: target, key };\n }\n\n return { type: operator, column: target, columns: undefined, key };\n }\n\n // Handle simple operator string\n return { type: rule, column: key, columns: undefined, key };\n }\n\n /**\n * Apply a single filter rule to the query\n */\n private applyFilterRule(\n query: CascadeQueryBuilder<any>,\n rule: any,\n value: any,\n data: any,\n options: FilterOptions,\n ): void {\n // 1. Custom function filter\n if (rule.type === \"function\") {\n rule.fn(value, query, data);\n return;\n }\n\n // 2. Predefined filter types\n const handler = this.getFilterHandler(rule.type);\n if (handler) {\n handler.call(this, query, rule.column, rule.columns, value, options);\n return;\n }\n\n // 3. Standard where operators\n this.applyWhereOperator(query, rule.type, rule.column, rule.columns, value, rule.key);\n }\n\n /**\n * Get filter handler for predefined types\n */\n private getFilterHandler(type: string): Function | undefined {\n const handlers: Record<string, Function> = {\n // Boolean filters\n bool: this.handleBoolean,\n boolean: this.handleBoolean,\n\n // Numeric filters\n int: this.handleInt,\n integer: this.handleInt,\n \"!int\": this.handleNotInt,\n \"int>\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">\"),\n \"int>=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">=\"),\n \"int<\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<\"),\n \"int<=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<=\"),\n inInt: this.handleInInt,\n number: this.handleNumber,\n inNumber: this.handleInNumber,\n float: this.handleFloat,\n double: this.handleFloat,\n inFloat: this.handleInNumber,\n\n // Null filters\n null: this.handleNull,\n notNull: this.handleNotNull,\n \"!null\": this.handleNotNull,\n\n // Date filters\n date: this.handleDate,\n \"date>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">\"),\n \"date>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">=\"),\n \"date<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<\"),\n \"date<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<=\"),\n dateBetween: this.handleDateBetween,\n inDate: this.handleInDate,\n\n // DateTime filters\n dateTime: this.handleDateTime,\n \"dateTime>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">\"),\n \"dateTime>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">=\"),\n \"dateTime<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<\"),\n \"dateTime<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<=\"),\n dateTimeBetween: this.handleDateTimeBetween,\n inDateTime: this.handleInDateTime,\n\n // Scope filter - applies local scope when value is truthy\n scope: this.handleScope,\n\n // With filter - eager-loads a relation when value is truthy\n with: this.handleWith,\n\n // JoinWith filter - eager-loads a relation via SQL JOIN when value is truthy\n joinWith: this.handleJoinWith,\n\n // Vector similarity search — calls similarTo(column, embedding[])\n similarTo: this.handleSimilarTo,\n };\n\n return handlers[type];\n }\n\n /**\n * `like` is a contains match on every driver: a value without an explicit\n * `%` is wrapped as `%value%` (Postgres would otherwise match exactly while\n * Mongo matched a substring). Values carrying their own `%` are kept as-is.\n */\n private likePattern(value: any): any {\n if (typeof value !== \"string\" || value.includes(\"%\")) return value;\n return `%${value}%`;\n }\n\n /**\n * Coerce a client value to an integer or reject with a 400.\n */\n private toInt(value: any): number {\n const parsed = parseInt(value);\n if (Number.isNaN(parsed)) {\n throw new BadRequestError(`Invalid integer filter value \"${value}\"`);\n }\n return parsed;\n }\n\n /**\n * Coerce a client value to a number or reject with a 400.\n */\n private toNumber(value: any): number {\n const parsed = Number(value);\n if (value === \"\" || value === null || Number.isNaN(parsed)) {\n throw new BadRequestError(`Invalid number filter value \"${value}\"`);\n }\n return parsed;\n }\n\n /**\n * Apply standard where operators\n */\n private applyWhereOperator(\n query: QueryBuilderContract,\n operator: string,\n column?: string,\n columns?: string[],\n value?: any,\n ruleName?: string,\n ): void {\n // Handle \"in\" prefix for array values\n if (operator.startsWith(\"in\") && operator !== \"int\" && !Array.isArray(value)) {\n value = [value];\n }\n\n // Single column\n if (column) {\n switch (operator) {\n case \"=\":\n query.where(column, value);\n break;\n case \"!=\":\n case \"<>\":\n query.where(column, \"!=\", value);\n break;\n case \">\":\n case \">=\":\n case \"<\":\n case \"<=\":\n query.where(column, operator, value);\n break;\n case \"in\":\n query.whereIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"not in\":\n query.whereNotIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"like\":\n query.whereLike(column, this.likePattern(value));\n break;\n case \"startsWith\":\n query.whereLike(column, `${value}%`);\n break;\n case \"endsWith\":\n query.whereLike(column, `%${value}`);\n break;\n case \"not like\":\n query.whereNotLike(column, value);\n break;\n case \"between\":\n query.whereBetween(column, value);\n break;\n case \"not between\":\n query.whereNotBetween(column, value);\n break;\n }\n }\n // Multiple columns (OR condition)\n else if (columns) {\n if (operator === \"=\") {\n this.anyOf(query, columns, (q, col) => q.where(col, value));\n } else if (operator === \"like\") {\n // (col0 LIKE value OR col1 LIKE value OR ...), OR'd with prior conditions —\n // the object form of orWhere() only ever emits \"=\", so \"like\" needs the\n // callback/group form to keep the operator instead of silently degrading\n // to equality (D1).\n this.anyOf(query, columns, (q, col) => q.where(col, \"like\", this.likePattern(value)));\n } else {\n // Every other operator silently degraded to equality via the object form\n // of orWhere() — refuse instead of returning a correct-looking, wrong query.\n throw new Error(\n `Unsupported multi-column filter operator \"${operator}\" for rule \"${ruleName ?? columns.join(\",\")}\". ` +\n `Multi-column filters only support \"=\" and \"like\".`,\n );\n }\n }\n }\n\n /**\n * Apply a condition across several columns as ONE grouped OR: `(a OR b OR ...)`.\n * The group is ANDed with the rest of the query, so a tenant filter\n * (`org_id = 5`) can never be widened by a multi-column search. Never emit a\n * top-level `orWhere` for multi-column filters.\n */\n private anyOf(\n query: QueryBuilderContract,\n columns: string[],\n apply: (q: QueryBuilderContract, col: string) => void,\n ) {\n query.where((group: QueryBuilderContract) => {\n columns.forEach((col, index) => {\n const build = (q: QueryBuilderContract) => apply(q, col);\n\n if (index === 0) {\n group.where(build);\n } else {\n group.orWhere(build);\n }\n });\n });\n }\n\n // ============================================================================\n // BOOLEAN FILTERS\n // ============================================================================\n\n /**\n * Coerce a filter value to a boolean.\n *\n * Recognized truthy forms: `true`, `1`, `\"true\"`, `\"1\"`.\n * Recognized falsy forms: `false`, `0`, `\"false\"`, `\"0\"`.\n * Anything else falls back to `Boolean(value)`.\n *\n * NOTE: this intentionally does NOT treat \"non-empty\" as true — the previous\n * `|| !isEmpty(value)` fallback coerced `false`/`0` to `true`, inverting the\n * filter for explicitly-false values.\n */\n private coerceBoolean(value: any): boolean {\n if (value === true || value === 1 || value === \"true\" || value === \"1\") {\n return true;\n }\n\n if (value === false || value === 0 || value === \"false\" || value === \"0\") {\n return false;\n }\n\n return Boolean(value);\n }\n\n private handleBoolean(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const boolValue = this.coerceBoolean(value);\n if (column) {\n query.where(column, boolValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, boolValue));\n }\n }\n\n // ============================================================================\n // NUMERIC FILTERS\n // ============================================================================\n private handleInt(query: QueryBuilderContract, column?: string, columns?: string[], value?: any) {\n const intValue = this.toInt(value);\n if (column) {\n query.where(column, intValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, intValue));\n }\n }\n\n private handleNotInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const intValue = this.toInt(value);\n if (column) {\n query.where(column, \"!=\", intValue);\n } else if (columns) {\n // Grouped OR across columns\n this.anyOf(query, columns, (q, col) => q.where(col, \"!=\", intValue));\n }\n }\n\n private handleIntComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n operator: string,\n ) {\n const intValue = this.toInt(value);\n if (column) {\n query.where(column, operator, intValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, operator, intValue));\n }\n }\n\n private handleInInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => this.toInt(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Grouped OR of whereIn across columns\n this.anyOf(query, columns, (q, col) => q.whereIn(col, values));\n }\n }\n\n private handleNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const numValue = this.toNumber(value);\n if (column) {\n query.where(column, numValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, numValue));\n }\n }\n\n private handleInNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => this.toNumber(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Grouped OR of whereIn across columns\n this.anyOf(query, columns, (q, col) => q.whereIn(col, values));\n }\n }\n\n private handleFloat(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const floatValue = parseFloat(value);\n if (column) {\n query.where(column, floatValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, floatValue));\n }\n }\n\n // ============================================================================\n // NULL FILTERS\n // ============================================================================\n\n private handleNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNull(column);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.whereNull(col));\n }\n }\n\n private handleNotNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNotNull(column);\n } else if (columns) {\n // Use whereNotNull for each column\n this.anyOf(query, columns, (q, col) => q.whereNotNull(col));\n }\n }\n\n // ============================================================================\n // SCOPE FILTER\n // ============================================================================\n\n /**\n * Handle scope filter - applies local scope and passes the filter value.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * active: \"scope\", // Uses the filter key as scope name\n * isAdmin: [\"scope\", \"admin\"] // Uses custom scope name\n * }\n * ```\n *\n * When list({ active: true }) is called, it will call query.scope(\"active\", true)\n * When list({ status: \"pending\" }) is called, it will call query.scope(\"status\", \"pending\")\n */\n private handleScope(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n // column holds the scope name (either the filter key or custom name from array format)\n if (column) {\n query.scope(column, value);\n }\n }\n\n // ============================================================================\n // WITH (EAGER LOAD) FILTER\n // ============================================================================\n\n /**\n * Handle with filter - eager-loads a relation when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"with\", \"ai_model\"], // load single relation\n * with_all: [\"with\", [\"ai_model\", \"unit\"]] // load multiple relations\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.with(\"ai_model\")\n */\n private handleWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n // Load a single named relation\n if (column) {\n if (query.with) {\n query.with(column);\n }\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n for (const relation of columns) {\n if (query.with) {\n query.with(relation);\n }\n }\n }\n }\n\n /**\n * Handle joinWith filter - eager-loads a relation via SQL JOIN when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"joinWith\", \"ai_model\"], // load single relation via join\n * with_all: [\"joinWith\", [\"ai_model\", \"unit\"]] // load multiple relations via join\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.joinWith(\"ai_model\")\n */\n private handleJoinWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n if (!query.joinWith) {\n console.warn(\n \"[Repository] joinWith is not supported by the query builder. using with instead.\",\n );\n return this.handleWith(query, column, columns, value);\n }\n\n // Load a single named relation\n if (column) {\n query.joinWith(column);\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n query.joinWith(...columns);\n }\n }\n\n // ============================================================================\n // VECTOR FILTER\n // ============================================================================\n\n /**\n * Handle similarTo filter — performs vector similarity search.\n *\n * The filter value must be a `number[]` (pre-computed embedding).\n * Delegates to `query.similarTo(column, embedding)` which is handled\n * driver-specifically (pgvector or MongoDB Atlas $vectorSearch).\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * organization_id: \"=\",\n * embedding: \"similarTo\",\n * }\n * ```\n *\n * Then in the service:\n * ```typescript\n * await vectorsRepository.list({ embedding: queryEmbedding, organization_id: orgId });\n * ```\n */\n private handleSimilarTo(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n if (!column || !Array.isArray(value)) return;\n\n // Cast to any: Cascade's internal QueryBuilderContract still uses nearestTo;\n // our wrapper (CascadeQueryBuilder.similarTo) delegates to it correctly.\n (query as any).similarTo(column, value);\n }\n\n // ============================================================================\n // DATE FILTERS\n // ============================================================================\n\n private handleDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n query.whereDate(column, dateValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.whereDate(col, dateValue));\n }\n }\n\n private handleDateComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n if (operator === \">\" || operator === \">=\") {\n query.whereDateAfter(column, dateValue);\n } else {\n query.whereDateBefore(column, dateValue);\n }\n } else if (columns) {\n const after = operator === \">\" || operator === \">=\";\n this.anyOf(query, columns, (q, col) =>\n after ? q.whereDateAfter(col, dateValue) : q.whereDateBefore(col, dateValue),\n );\n }\n }\n\n private handleDateBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDate(v, options?.dateFormat));\n\n // `value.length === 2` is checked above, so both exist — but the compiler\n // does not narrow destructured elements from a length test. Returning\n // rather than defaulting: this builds a date RANGE for a query, and a\n // half-formed range would silently filter on a boundary the caller never\n // asked for.\n if (start === undefined || end === undefined) return;\n\n if (column) {\n query.whereDateBetween(column, [start, end]);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.whereDateBetween(col, [start, end]));\n }\n }\n\n private handleInDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDate(v, options?.dateFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Grouped OR of whereIn across columns\n this.anyOf(query, columns, (q, col) => q.whereIn(col, dates));\n }\n }\n\n // ============================================================================\n // DATETIME FILTERS\n // ============================================================================\n\n private handleDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, dateValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, dateValue));\n }\n }\n\n private handleDateTimeComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, operator, dateValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, operator, dateValue));\n }\n }\n\n private handleDateTimeBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDateTime(v, options?.dateTimeFormat));\n if (column) {\n query.whereBetween(column, [start, end]);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.whereBetween(col, [start, end]));\n }\n }\n\n private handleInDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDateTime(v, options?.dateTimeFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Grouped OR of whereIn across columns\n this.anyOf(query, columns, (q, col) => q.whereIn(col, dates));\n }\n }\n\n // ============================================================================\n // DATE PARSING UTILITIES\n // ============================================================================\n\n /**\n * Parse a date string with the declared format; invalid input is a 400.\n */\n private parseDate(value: any, format?: string): Date {\n return this.parseWithFormat(value, format);\n }\n\n /**\n * Parse a datetime string with the declared format; invalid input is a 400.\n */\n private parseDateTime(value: any, format?: string): Date {\n return this.parseWithFormat(value, format);\n }\n\n private parseWithFormat(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n\n const parsed = format ? dayjs(String(value), format, true) : dayjs(value);\n\n if (!parsed.isValid()) {\n throw new BadRequestError(\n `Invalid date filter value \"${value}\"${format ? ` (expected ${format})` : \"\"}`,\n );\n }\n\n return parsed.toDate();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CascadeAdapter } from "./cascade-adapter.mjs";
|
|
1
|
+
import { CascadeAdapter, UPDATE_MANY_BATCH_SIZE } from "./cascade-adapter.mjs";
|
|
2
2
|
import { CascadeQueryBuilder } from "./cascade-query-builder.mjs";
|
|
@@ -286,21 +286,21 @@ interface QueryBuilderContract<T> {
|
|
|
286
286
|
* @example
|
|
287
287
|
* await query.where("status", "paid").avg("total")
|
|
288
288
|
*/
|
|
289
|
-
avg(field: string): Promise<number>;
|
|
289
|
+
avg(field: string): Promise<number | null>;
|
|
290
290
|
/**
|
|
291
291
|
* Minimum value of a field across all matching records.
|
|
292
292
|
*
|
|
293
293
|
* @param field - Field name
|
|
294
294
|
* @returns Promise resolving to the minimum value (0 when no records match)
|
|
295
295
|
*/
|
|
296
|
-
min(field: string): Promise<number>;
|
|
296
|
+
min(field: string): Promise<number | null>;
|
|
297
297
|
/**
|
|
298
298
|
* Maximum value of a field across all matching records.
|
|
299
299
|
*
|
|
300
300
|
* @param field - Field name
|
|
301
301
|
* @returns Promise resolving to the maximum value (0 when no records match)
|
|
302
302
|
*/
|
|
303
|
-
max(field: string): Promise<number>;
|
|
303
|
+
max(field: string): Promise<number | null>;
|
|
304
304
|
/**
|
|
305
305
|
* Group matching records by one or more fields and compute aggregates.
|
|
306
306
|
*
|
|
@@ -155,6 +155,11 @@ type PaginationMode = "pages" | "cursor";
|
|
|
155
155
|
* Repository list options
|
|
156
156
|
*/
|
|
157
157
|
type RepositoryOptions = {
|
|
158
|
+
/**
|
|
159
|
+
* Explicit cache key for `listCached()`. Required to cache a call whose
|
|
160
|
+
* options contain callback filters, which cannot be keyed safely.
|
|
161
|
+
*/
|
|
162
|
+
cacheKey?: string;
|
|
158
163
|
/**
|
|
159
164
|
* Pagination mode - "pages" for traditional pagination, "cursor" for cursor-based
|
|
160
165
|
* @default "pages"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../core/src/repositories/contracts/types.ts"],"mappings":";;;;;;;;AAQA;KAAY,gBAAA;EAAgB;;;EAI1B,IAAA,EAAM,CAAC;EAAD;;;EAKN,UAAA;IAcE;;;IAVA,KAAA;IAoBK;AAQT;;IAvBI,MAAA;IA2BK;;;IAtBL,IAAA;IA2BF;;;IAtBE,KAAA;IAyCA;;;IApCA,KAAA;EAAA;AAAA;;;;;KAQQ,sBAAA;EA6DV;;AAAY;EAzDZ,IAAA,EAAM,CAAC;EAmEgB;;;EA9DvB,UAAA;IA8DyC;;;IA1DvC,KAAA;IA0D0E;AAAA;AAK9E;IA1DI,MAAA;IA0DqB;;AAAA;IArDrB,OAAA;IAuEsB;;;IAlEtB,UAAA;IA8GQ;;;IAzGR,UAAA;EAAA;AAAA;;;;KAOQ,uBAAA;EAkGkB;;;EA9F5B,KAAA;EAgGO;;;EA3FP,MAAA;EA4F4B;AAAA;AAU9B;;EAhGE,SAAA;EAgGyB;;;;EA1FzB,YAAA;AAAA;;;;;;;;KAUU,aAAA,OAAoB,SAAA,EAAW,CAAA,IAAK,UAAA,aAAuB,OAAO;;;AAiFL;KA5E7D,aAAA;;;;KAkBA,cAAA,ubAuCR,aAAa;;;;KAKL,cAAA,QAAsB,oBAAA,CAAqB,CAAA,MACrD,KAAA,OACA,KAAA,EAAO,CAAA,EACP,OAAA,EAAS,MAAA;AAiBkB;AAM7B;;;;;;AAN6B,KAPjB,UAAA,KAAe,oBAAA,SACzB,cAAA,GAAiB,cAAA,CAAe,CAAA,KAAM,cAAA,KAAmB,cAAA;;AA0B7C;AAMd;KA3BY,WAAA;EAAA,CACT,GAAA,WAAc,UAAU,CAAC,CAAA;AAAA;AA0BF;AAK1B;;AAL0B,KApBd,aAAA;
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../core/src/repositories/contracts/types.ts"],"mappings":"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"}
|
|
@@ -2,5 +2,5 @@ import { RepositoryManager } from "./repository.manager.mjs";
|
|
|
2
2
|
import { RepositoryAdapterContract } from "./contracts/repository-adapter.contract.mjs";
|
|
3
3
|
import { AllRepositoryOptions, CachedRepositoryOptions, ChunkCallback, CursorPaginationOptions, CursorPaginationResult, FilterFunction, FilterOperator, FilterOptions, FilterRule, FilterRules, PaginationMode, PaginationResult, RepositoryConfigurations, RepositoryEvent, RepositoryOptions, RepositoryOptionsWithCursor, RepositoryOptionsWithPages, SaveMode, TypedAllRepositoryOptions, TypedRepositoryOptions, TypedRepositoryOptionsWithCursor, TypedRepositoryOptionsWithPages, WhereOperator } from "./contracts/types.mjs";
|
|
4
4
|
import { AggregateExpressionInput, AggregateExpressions, GroupByFields, QueryBuilderContract } from "./contracts/query-builder.contract.mjs";
|
|
5
|
-
import { CascadeAdapter } from "./adapters/cascade/cascade-adapter.mjs";
|
|
5
|
+
import { CascadeAdapter, UPDATE_MANY_BATCH_SIZE } from "./adapters/cascade/cascade-adapter.mjs";
|
|
6
6
|
import { CascadeQueryBuilder } from "./adapters/cascade/cascade-query-builder.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CascadeQueryBuilder } from "./adapters/cascade/cascade-query-builder.mjs";
|
|
2
|
-
import { CascadeAdapter } from "./adapters/cascade/cascade-adapter.mjs";
|
|
2
|
+
import { CascadeAdapter, UPDATE_MANY_BATCH_SIZE } from "./adapters/cascade/cascade-adapter.mjs";
|
|
3
3
|
import "./adapters/cascade/index.mjs";
|
|
4
4
|
import { RepositoryManager } from "./repository.manager.mjs";
|
|
5
5
|
|
|
@@ -40,6 +40,25 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
|
|
|
40
40
|
* @protected
|
|
41
41
|
*/
|
|
42
42
|
protected filterBy: FilterRules;
|
|
43
|
+
/**
|
|
44
|
+
* Columns a client may sort by (`orderBy` / `sortBy` / `cursorColumn`) when the
|
|
45
|
+
* options come from a request. `id` and the declared `filterBy` keys are always
|
|
46
|
+
* allowed; anything else is ignored.
|
|
47
|
+
* @protected
|
|
48
|
+
*/
|
|
49
|
+
protected sortable: string[];
|
|
50
|
+
/**
|
|
51
|
+
* Columns a client may request through `select`. Empty by default: a client
|
|
52
|
+
* `select` is ignored unless the repository whitelists the columns here.
|
|
53
|
+
* @protected
|
|
54
|
+
*/
|
|
55
|
+
protected selectable: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Hard cap on `limit` / `perPage` for every list, all and cursor query.
|
|
58
|
+
* @default 100
|
|
59
|
+
* @protected
|
|
60
|
+
*/
|
|
61
|
+
protected maxPerPage: number;
|
|
43
62
|
/**
|
|
44
63
|
* Get adapter instance (lazy-loaded)
|
|
45
64
|
* Resolution order:
|
|
@@ -150,6 +169,14 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
|
|
|
150
169
|
protected getIsActiveFilter(): {
|
|
151
170
|
[x: string]: any;
|
|
152
171
|
};
|
|
172
|
+
/**
|
|
173
|
+
* Attach the active scope to the options. It travels under its own key (after
|
|
174
|
+
* the caller's options, so it can't be overridden) and is applied as a real
|
|
175
|
+
* `where` by `applyOptionsToQuery`, independent of `filterBy`.
|
|
176
|
+
*/
|
|
177
|
+
protected withActive<O extends object>(options?: O): O & {
|
|
178
|
+
activeScope: Record<string, any>;
|
|
179
|
+
};
|
|
153
180
|
/**
|
|
154
181
|
* Find a record by ID
|
|
155
182
|
* @param id - Record ID or model instance
|
|
@@ -358,6 +385,33 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
|
|
|
358
385
|
*/
|
|
359
386
|
protected asTyped(opts: RepositoryOptions): TypedRepositoryOptions<F>;
|
|
360
387
|
protected asTypedAll(opts: AllRepositoryOptions): TypedAllRepositoryOptions<F>;
|
|
388
|
+
/**
|
|
389
|
+
* Option keys that control the query rather than filter it.
|
|
390
|
+
*/
|
|
391
|
+
private static readonly controlKeys;
|
|
392
|
+
/**
|
|
393
|
+
* Columns `orderBy` / `sortBy` / `cursorColumn` may target.
|
|
394
|
+
*/
|
|
395
|
+
protected getSortableColumns(): Set<string>;
|
|
396
|
+
/**
|
|
397
|
+
* Split client-controllable control options from filters: `limit` is capped by
|
|
398
|
+
* `maxPerPage`, sort / cursor columns must be whitelisted (`sortable`, `filterBy`
|
|
399
|
+
* keys or `id`) and `select` must be within `selectable`. Anything else is
|
|
400
|
+
* dropped, so `list({ ...request.all() })` cannot steer the query.
|
|
401
|
+
*/
|
|
402
|
+
protected sanitizeControls(options: RepositoryOptions): RepositoryOptions;
|
|
403
|
+
/**
|
|
404
|
+
* Read the active scope only when it is exactly what `getIsActiveFilter()`
|
|
405
|
+
* produces, so a client can't smuggle an arbitrary `where` through it.
|
|
406
|
+
*/
|
|
407
|
+
protected readActiveScope(options: RepositoryOptions): Record<string, any> | undefined;
|
|
408
|
+
/**
|
|
409
|
+
* Apply plain `{ column: scalar }` lookups that are not declared in `filterBy`
|
|
410
|
+
* as equality wheres. Used by `first` (and so `exists`, `findOrCreate`,
|
|
411
|
+
* `updateOrCreate`) so an undeclared key is never silently dropped and can't
|
|
412
|
+
* match the wrong row.
|
|
413
|
+
*/
|
|
414
|
+
protected applyLookupFilters(query: QueryBuilderContract<T>, options: RepositoryOptions): void;
|
|
361
415
|
/**
|
|
362
416
|
* Apply repository options to query
|
|
363
417
|
* @param query - Query builder instance
|
|
@@ -583,7 +637,7 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
|
|
|
583
637
|
* @returns Promise resolving to the filtered numeric average
|
|
584
638
|
* @public
|
|
585
639
|
*/
|
|
586
|
-
avg(field: string, options?: TypedRepositoryOptions<F>): Promise<number>;
|
|
640
|
+
avg(field: string, options?: TypedRepositoryOptions<F>): Promise<number | null>;
|
|
587
641
|
/**
|
|
588
642
|
* Minimum value of a field across records matching options.
|
|
589
643
|
*
|
|
@@ -595,7 +649,7 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
|
|
|
595
649
|
* @returns Promise resolving to the filtered minimum value
|
|
596
650
|
* @public
|
|
597
651
|
*/
|
|
598
|
-
min(field: string, options?: TypedRepositoryOptions<F>): Promise<number>;
|
|
652
|
+
min(field: string, options?: TypedRepositoryOptions<F>): Promise<number | null>;
|
|
599
653
|
/**
|
|
600
654
|
* Maximum value of a field across records matching options.
|
|
601
655
|
*
|
|
@@ -607,7 +661,7 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
|
|
|
607
661
|
* @returns Promise resolving to the filtered maximum value
|
|
608
662
|
* @public
|
|
609
663
|
*/
|
|
610
|
-
max(field: string, options?: TypedRepositoryOptions<F>): Promise<number>;
|
|
664
|
+
max(field: string, options?: TypedRepositoryOptions<F>): Promise<number | null>;
|
|
611
665
|
/**
|
|
612
666
|
* Group records by one or more fields and compute aggregates.
|
|
613
667
|
*
|
|
@@ -674,6 +728,10 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
|
|
|
674
728
|
* @protected
|
|
675
729
|
*/
|
|
676
730
|
protected cacheKey(key: string | Record<string, any>, moreOptions?: Record<string, any>): string;
|
|
731
|
+
/**
|
|
732
|
+
* Whether a value holds a function at any depth (cycle-safe).
|
|
733
|
+
*/
|
|
734
|
+
protected containsFunction(value: any, seen?: WeakSet<object>): boolean;
|
|
677
735
|
/**
|
|
678
736
|
* Deterministically serialize a value for use in a cache key.
|
|
679
737
|
*
|
|
@@ -768,6 +826,10 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
|
|
|
768
826
|
* export const cleanup = usersRepository.cleanup.bind(usersRepository);
|
|
769
827
|
*/
|
|
770
828
|
$cleanup(): Promise<void>;
|
|
829
|
+
/**
|
|
830
|
+
* Cache read that treats a driver failure (e.g. Redis down) as a miss.
|
|
831
|
+
*/
|
|
832
|
+
private safeCacheGet;
|
|
771
833
|
/**
|
|
772
834
|
* Clear all cache for this repository
|
|
773
835
|
* @public
|