@warlock.js/core 5.1.0 → 5.2.1
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 +152 -0
- package/esm/application/application.d.mts.map +1 -1
- package/esm/application/application.mjs.map +1 -1
- package/esm/benchmark/benchmark-snapshots.d.mts.map +1 -1
- package/esm/benchmark/benchmark.d.mts.map +1 -1
- package/esm/benchmark/channels/console.channel.mjs.map +1 -1
- package/esm/benchmark/channels/noop.channel.d.mts.map +1 -1
- package/esm/benchmark/profiler.d.mts.map +1 -1
- package/esm/benchmark/profiler.mjs.map +1 -1
- package/esm/bootstrap/setup.d.mts.map +1 -1
- package/esm/bootstrap/setup.mjs.map +1 -1
- package/esm/bootstrap.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.mjs.map +1 -1
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/cli-commands.utils.mjs +16 -10
- package/esm/cli/cli-commands.utils.mjs.map +1 -1
- package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/name-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -1
- package/esm/cli/commands/routes/diff-page-routes.mjs +84 -0
- package/esm/cli/commands/routes/diff-page-routes.mjs.map +1 -0
- package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
- package/esm/cli/commands/routes/route-row.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-diff.action.mjs +84 -0
- package/esm/cli/commands/routes/routes-diff.action.mjs.map +1 -0
- package/esm/cli/commands/routes.command.mjs +15 -1
- package/esm/cli/commands/routes.command.mjs.map +1 -1
- package/esm/cli/commands/start-production.command.mjs +8 -1
- package/esm/cli/commands/start-production.command.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/cli/commands-loader.mjs.map +1 -1
- package/esm/cli/framework-cli-commands.mjs +2 -1
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/cli/start.mjs.map +1 -1
- package/esm/cli/string-similarity.mjs.map +1 -1
- package/esm/commands/cli-command.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.map +1 -1
- package/esm/config/config-setter.d.mts.map +1 -1
- package/esm/config/config-special-handlers.d.mts +1 -1
- package/esm/config/config-special-handlers.mjs.map +1 -1
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/connectors/access-connector.d.mts.map +1 -1
- package/esm/connectors/ai-connector.d.mts.map +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
- package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
- package/esm/connectors/base-connector.d.mts.map +1 -1
- package/esm/connectors/base-connector.mjs.map +1 -1
- package/esm/connectors/cache-connector.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/database-connector.d.mts.map +1 -1
- package/esm/connectors/describe-server-address.mjs +20 -3
- package/esm/connectors/describe-server-address.mjs.map +1 -1
- package/esm/connectors/herald-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +5 -1
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/logger-connector.d.mts.map +1 -1
- package/esm/connectors/mail-connector.d.mts.map +1 -1
- package/esm/connectors/notifications-connector.d.mts.map +1 -1
- package/esm/connectors/register-configured-connectors.mjs.map +1 -1
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/storage.connector.d.mts.map +1 -1
- package/esm/container/index.d.mts.map +1 -1
- package/esm/database/create-database-action.mjs.map +1 -1
- package/esm/database/drop-tables-action.mjs.map +1 -1
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/models/database-log/database-log.mjs.map +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.mjs.map +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/utils.mjs.map +1 -1
- package/esm/dev-server/check-for-updates.mjs.map +1 -1
- package/esm/dev-server/dependency-graph.d.mts.map +1 -1
- package/esm/dev-server/dependency-graph.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs.map +1 -1
- 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.map +1 -1
- package/esm/dev-server/file-operations.d.mts.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs.map +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.map +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.map +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
- package/esm/dev-server/layer-executor.mjs +10 -3
- package/esm/dev-server/layer-executor.mjs.map +1 -1
- package/esm/dev-server/loader/hook-thread.d.mts.map +1 -1
- package/esm/dev-server/loader/own-resolver.mjs.map +1 -1
- package/esm/dev-server/loader/register-loader.mjs.map +1 -1
- package/esm/dev-server/loader/resolve-hook.mjs.map +1 -1
- package/esm/dev-server/loader/source-slug.mjs.map +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs +12 -3
- package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
- package/esm/dev-server/loader/version-registry.mjs.map +1 -1
- package/esm/dev-server/manifest-manager.d.mts.map +1 -1
- package/esm/dev-server/manifest-manager.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/package-json-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/ready-block.mjs.map +1 -1
- package/esm/dev-server/special-files-collector.d.mts.map +1 -1
- package/esm/dev-server/special-files-collector.mjs.map +1 -1
- package/esm/dev-server/supervisor.mjs +9 -6
- package/esm/dev-server/supervisor.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/encryption/encrypt.mjs.map +1 -1
- package/esm/encryption/hash.mjs.map +1 -1
- package/esm/encryption/password.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +11 -4
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/features/access.feature.mjs +2 -1
- package/esm/generations/features/access.feature.mjs.map +1 -1
- package/esm/generations/features/ai-anthropic.feature.mjs +3 -1
- package/esm/generations/features/ai-anthropic.feature.mjs.map +1 -1
- package/esm/generations/features/ai-bedrock.feature.mjs +3 -1
- package/esm/generations/features/ai-bedrock.feature.mjs.map +1 -1
- package/esm/generations/features/ai-google.feature.mjs +3 -1
- package/esm/generations/features/ai-google.feature.mjs.map +1 -1
- package/esm/generations/features/ai-ollama.feature.mjs +3 -1
- package/esm/generations/features/ai-ollama.feature.mjs.map +1 -1
- package/esm/generations/features/ai-openai.feature.mjs +3 -1
- package/esm/generations/features/ai-openai.feature.mjs.map +1 -1
- package/esm/generations/features/ai-panoptic.feature.mjs +2 -1
- package/esm/generations/features/ai-panoptic.feature.mjs.map +1 -1
- package/esm/generations/features/ai-tools.feature.mjs +2 -1
- package/esm/generations/features/ai-tools.feature.mjs.map +1 -1
- package/esm/generations/features/ai-workspace.feature.mjs +2 -1
- package/esm/generations/features/ai-workspace.feature.mjs.map +1 -1
- package/esm/generations/features/ai.feature.mjs +2 -1
- package/esm/generations/features/ai.feature.mjs.map +1 -1
- package/esm/generations/features/herald.feature.mjs +2 -1
- package/esm/generations/features/herald.feature.mjs.map +1 -1
- package/esm/generations/features/notifications.feature.mjs +3 -2
- package/esm/generations/features/notifications.feature.mjs.map +1 -1
- package/esm/generations/features/scheduler.feature.mjs +3 -1
- package/esm/generations/features/scheduler.feature.mjs.map +1 -1
- package/esm/generations/features/shadcn.feature.mjs.map +1 -1
- package/esm/generations/features/shared/migration-timestamp.mjs.map +1 -1
- package/esm/generations/features/tailwind.feature.mjs.map +1 -1
- package/esm/generations/features/types.mjs +14 -0
- package/esm/generations/features/types.mjs.map +1 -0
- package/esm/generations/features/web.feature.mjs +10 -2
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/generations/stubs.mjs +169 -28
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/boot-port-preflight.d.mts +46 -0
- package/esm/http/boot-port-preflight.d.mts.map +1 -0
- package/esm/http/boot-port-preflight.mjs +87 -0
- package/esm/http/boot-port-preflight.mjs.map +1 -0
- package/esm/http/context/request-context.d.mts.map +1 -1
- package/esm/http/context/request-context.mjs.map +1 -1
- package/esm/http/context/request-memo.d.mts.map +1 -1
- package/esm/http/context/request-memo.mjs.map +1 -1
- package/esm/http/createHttpApplication.d.mts.map +1 -1
- package/esm/http/createHttpApplication.mjs.map +1 -1
- package/esm/http/health.d.mts.map +1 -1
- package/esm/http/index.d.mts +1 -0
- package/esm/http/index.mjs +1 -0
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- 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.mjs +1 -1
- package/esm/http/middleware/ip-filter.middleware.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
- package/esm/http/port-preflight.d.mts +8 -0
- package/esm/http/port-preflight.d.mts.map +1 -1
- package/esm/http/port-preflight.mjs +8 -1
- package/esm/http/port-preflight.mjs.map +1 -1
- package/esm/http/ready-report.d.mts.map +1 -1
- package/esm/http/request-controller.d.mts.map +1 -1
- package/esm/http/request.d.mts +14 -2
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +16 -2
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts +3 -3
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs +26 -2
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/types.d.mts +1 -3
- package/esm/http/types.d.mts.map +1 -1
- package/esm/http/uploaded-file.d.mts.map +1 -1
- package/esm/http/uploaded-file.mjs.map +1 -1
- package/esm/http/uploads-config.d.mts.map +1 -1
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs.map +1 -1
- package/esm/index.d.mts +6 -1
- package/esm/index.mjs +6 -1
- package/esm/logger/fatal-visibility.d.mts +25 -0
- package/esm/logger/fatal-visibility.d.mts.map +1 -0
- package/esm/logger/fatal-visibility.mjs +31 -0
- package/esm/logger/fatal-visibility.mjs.map +1 -0
- package/esm/logger/index.d.mts +1 -0
- package/esm/logger/index.mjs +1 -0
- package/esm/logger/logger.mjs.map +1 -1
- package/esm/mail/config.d.mts.map +1 -1
- package/esm/mail/events.d.mts.map +1 -1
- package/esm/mail/mail.d.mts.map +1 -1
- package/esm/mail/mail.mjs.map +1 -1
- package/esm/mail/mailer-pool.d.mts.map +1 -1
- package/esm/mail/mailer-pool.mjs +2 -0
- package/esm/mail/mailer-pool.mjs.map +1 -1
- package/esm/mail/send-mail.mjs.map +1 -1
- package/esm/mail/test-mailbox.d.mts.map +1 -1
- package/esm/mail/test-mailbox.mjs.map +1 -1
- package/esm/production/assert-dist-ready-to-start.mjs +24 -0
- package/esm/production/assert-dist-ready-to-start.mjs.map +1 -0
- package/esm/production/assert-generated-imports.mjs.map +1 -1
- package/esm/production/build-app-production.mjs.map +1 -1
- package/esm/production/dist-build-manifest.mjs +47 -0
- package/esm/production/dist-build-manifest.mjs.map +1 -0
- package/esm/production/production-builder.mjs +68 -13
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/production-supervisor.mjs +21 -9
- package/esm/production/production-supervisor.mjs.map +1 -1
- package/esm/production/promote-dist.mjs +115 -0
- package/esm/production/promote-dist.mjs.map +1 -0
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/repositories/repository.manager.d.mts.map +1 -1
- package/esm/repositories/repository.manager.mjs.map +1 -1
- package/esm/resource/register-resource.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.mjs.map +1 -1
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/index.d.mts +3 -0
- package/esm/router/index.mjs +3 -0
- package/esm/router/log-request-lifecycle.mjs.map +1 -1
- package/esm/router/normalize-request-path.d.mts +14 -0
- package/esm/router/normalize-request-path.d.mts.map +1 -0
- package/esm/router/normalize-request-path.mjs +18 -0
- package/esm/router/normalize-request-path.mjs.map +1 -0
- package/esm/router/normalize-route-path.d.mts +28 -0
- package/esm/router/normalize-route-path.d.mts.map +1 -0
- package/esm/router/normalize-route-path.mjs +33 -0
- package/esm/router/normalize-route-path.mjs.map +1 -0
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
- package/esm/router/route-name-method-suffix.d.mts +33 -0
- package/esm/router/route-name-method-suffix.d.mts.map +1 -0
- package/esm/router/route-name-method-suffix.mjs +39 -0
- package/esm/router/route-name-method-suffix.mjs.map +1 -0
- package/esm/router/route-registry.d.mts.map +1 -1
- package/esm/router/route-registry.mjs +3 -2
- package/esm/router/route-registry.mjs.map +1 -1
- package/esm/router/router.d.mts +14 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +50 -11
- package/esm/router/router.mjs.map +1 -1
- package/esm/socket/utils.d.mts.map +1 -1
- package/esm/storage/config.d.mts.map +1 -1
- package/esm/storage/context/storage-driver-context.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.mjs.map +1 -1
- package/esm/storage/drivers/r2-driver.d.mts.map +1 -1
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage-file.d.mts.map +1 -1
- package/esm/storage/storage-file.mjs.map +1 -1
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/types.d.mts +14 -0
- package/esm/storage/types.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs +2 -1
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-connectors-selection.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-setup-timeout.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/updater/update-warlock-packages.mjs.map +1 -1
- package/esm/use-cases/use-case-broadcast.d.mts.map +1 -1
- package/esm/use-cases/use-case-events.d.mts.map +1 -1
- package/esm/use-cases/use-case-pipeline.d.mts.map +1 -1
- package/esm/use-cases/use-case.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/database-log.d.mts.map +1 -1
- package/esm/utils/database-log.mjs.map +1 -1
- package/esm/utils/environment.d.mts.map +1 -1
- package/esm/utils/framework-vesion.mjs.map +1 -1
- package/esm/utils/get-localized.mjs.map +1 -1
- package/esm/utils/load-environment.mjs.map +1 -1
- package/esm/utils/normalized-path.d.mts.map +1 -1
- package/esm/utils/paths.d.mts.map +1 -1
- package/esm/utils/promise-all-object.d.mts.map +1 -1
- package/esm/utils/promise-all-object.mjs.map +1 -1
- package/esm/utils/sluggable.mjs.map +1 -1
- package/esm/utils/version-compare.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-user.mjs.map +1 -1
- package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
- package/esm/validation/plugins/localized-plugin.mjs.map +1 -1
- package/esm/validation/validators/file-validator.mjs.map +1 -1
- package/esm/vite/lower-stage3-decorators.d.mts.map +1 -1
- package/esm/warlock-config/default-configurations.mjs.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/llms-full.txt +205 -90
- package/llms.txt +5 -5
- package/package.json +12 -12
- package/skills/add-connector/SKILL.md +35 -28
- package/skills/create-controller/SKILL.md +1 -1
- package/skills/create-module/SKILL.md +12 -12
- package/skills/hash-password/SKILL.md +4 -4
- package/skills/run-app/SKILL.md +77 -16
- package/skills/send-mail/SKILL.md +2 -2
- package/skills/use-middleware/SKILL.md +13 -4
- package/skills/use-model-transformers/SKILL.md +2 -2
- package/skills/use-repository/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 +3 -3
- package/skills/warlock-routes/SKILL.md +47 -9
- package/skills/write-cli-command/SKILL.md +1 -1
- package/skills/write-seeder/SKILL.md +8 -8
|
@@ -13,16 +13,21 @@ const DEFAULT_READINESS_NOTICE_DELAY_MS = 1e4;
|
|
|
13
13
|
/**
|
|
14
14
|
* Run the production bundle in a child process and report honestly on it.
|
|
15
15
|
*
|
|
16
|
-
* The supervisor exists because a parent
|
|
17
|
-
* whether its child ever booted — it only sees the process exit. It
|
|
18
|
-
* opens an IPC channel and treats the child's `warlock:ready`
|
|
19
|
-
* by `Application.markBooted`, as the single source of truth
|
|
16
|
+
* The supervisor exists because a parent that only forwards `stdio` cannot
|
|
17
|
+
* see whether its child ever booted — it only sees the process exit. It
|
|
18
|
+
* therefore opens an IPC channel and treats the child's `warlock:ready`
|
|
19
|
+
* signal, emitted by `Application.markBooted`, as the single source of truth
|
|
20
|
+
* for "started":
|
|
20
21
|
*
|
|
21
22
|
* 1. The success banner is printed only on that signal.
|
|
22
23
|
* 2. A child that dies before signalling is reported as a failed start and
|
|
23
24
|
* forced to a non-zero exit, even if it exited 0.
|
|
24
25
|
* 3. A child still running without having signalled draws a note on stderr —
|
|
25
26
|
* never stdout, which would be the false-green this whole channel prevents.
|
|
27
|
+
* 4. Whether the child ever wrote to stdout/stderr before dying is tracked
|
|
28
|
+
* (`sawChildOutput`) so the failure summary only claims "the cause is
|
|
29
|
+
* printed above" when that is actually true — see the stdio comment
|
|
30
|
+
* below.
|
|
26
31
|
*
|
|
27
32
|
* Resolves rather than exiting so the outcome stays assertable in a test; the
|
|
28
33
|
* `start` command turns the result into the process exit code.
|
|
@@ -32,8 +37,8 @@ function superviseProductionProcess({ nodeArgs, cwd = process.cwd(), env = proce
|
|
|
32
37
|
const child = spawn(process.execPath, nodeArgs, {
|
|
33
38
|
stdio: [
|
|
34
39
|
"inherit",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
40
|
+
"pipe",
|
|
41
|
+
"pipe",
|
|
37
42
|
"ipc"
|
|
38
43
|
],
|
|
39
44
|
cwd,
|
|
@@ -47,6 +52,13 @@ function superviseProductionProcess({ nodeArgs, cwd = process.cwd(), env = proce
|
|
|
47
52
|
let isReady = false;
|
|
48
53
|
let isShuttingDown = false;
|
|
49
54
|
let isSettled = false;
|
|
55
|
+
let sawChildOutput = false;
|
|
56
|
+
const forwardChildOutput = (target) => (chunk) => {
|
|
57
|
+
sawChildOutput = true;
|
|
58
|
+
target.write(chunk);
|
|
59
|
+
};
|
|
60
|
+
child.stdout?.on("data", forwardChildOutput(process.stdout));
|
|
61
|
+
child.stderr?.on("data", forwardChildOutput(process.stderr));
|
|
50
62
|
const readinessNoticeTimer = setTimeout(() => {
|
|
51
63
|
if (isReady) return;
|
|
52
64
|
displayMissingReadinessNotice(Date.now() - startedAt);
|
|
@@ -69,8 +81,8 @@ function superviseProductionProcess({ nodeArgs, cwd = process.cwd(), env = proce
|
|
|
69
81
|
});
|
|
70
82
|
});
|
|
71
83
|
child.on("error", (error) => {
|
|
72
|
-
displayProductionStartFailure(1);
|
|
73
84
|
console.error(error);
|
|
85
|
+
displayProductionStartFailure(1, true);
|
|
74
86
|
settle({
|
|
75
87
|
exitCode: 1,
|
|
76
88
|
ready: false
|
|
@@ -86,10 +98,10 @@ function superviseProductionProcess({ nodeArgs, cwd = process.cwd(), env = proce
|
|
|
86
98
|
};
|
|
87
99
|
process.on("SIGTERM", onSigterm);
|
|
88
100
|
process.on("SIGINT", onSigint);
|
|
89
|
-
child.on("
|
|
101
|
+
child.on("close", (code) => {
|
|
90
102
|
if (!isReady && !isShuttingDown) {
|
|
91
103
|
const exitCode = code === 0 || code === null ? 1 : code;
|
|
92
|
-
displayProductionStartFailure(exitCode);
|
|
104
|
+
displayProductionStartFailure(exitCode, sawChildOutput);
|
|
93
105
|
return settle({
|
|
94
106
|
exitCode,
|
|
95
107
|
ready: false
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"production-supervisor.mjs","names":[],"sources":["../../../../../../../core/src/production/production-supervisor.ts"],"sourcesContent":["import { spawn } from \"child_process\";\nimport { BOOT_SIGNAL_ENV_KEY, isBootSignal } from \"../application/boot-signal\";\nimport {\n displayMissingReadinessNotice,\n displayProductionReadyBanner,\n displayProductionStartFailure,\n} from \"../cli/cli-commands.utils\";\n\n/**\n * How long a still-running child may stay silent before we say so.\n *\n * Only reached by a bundle built before readiness reporting existed; a current\n * bundle signals as soon as its late-phase connectors are up.\n */\nexport const DEFAULT_READINESS_NOTICE_DELAY_MS = 10_000;\n\nexport type ProductionSupervisorOptions = {\n /** Arguments handed to the node binary, entry file included. */\n nodeArgs: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n readinessNoticeDelayMs?: number;\n};\n\nexport type ProductionSupervisorResult = {\n /** What the CLI should exit with — never 0 for a boot that never completed. */\n exitCode: number;\n /** Whether the child ever reported a finished boot. */\n ready: boolean;\n};\n\n/**\n * Run the production bundle in a child process and report honestly on it.\n *\n * The supervisor exists because a parent with `stdio: \"inherit\"` cannot see\n * whether its child ever booted — it only sees the process exit. It therefore\n * opens an IPC channel and treats the child's `warlock:ready` signal, emitted\n * by `Application.markBooted`, as the single source of truth for \"started\":\n *\n * 1. The success banner is printed only on that signal.\n * 2. A child that dies before signalling is reported as a failed start and\n * forced to a non-zero exit, even if it exited 0.\n * 3. A child still running without having signalled draws a note on stderr —\n * never stdout, which would be the false-green this whole channel prevents.\n *\n * Resolves rather than exiting so the outcome stays assertable in a test; the\n * `start` command turns the result into the process exit code.\n */\nexport function superviseProductionProcess({\n nodeArgs,\n cwd = process.cwd(),\n env = process.env,\n readinessNoticeDelayMs = DEFAULT_READINESS_NOTICE_DELAY_MS,\n}: ProductionSupervisorOptions): Promise<ProductionSupervisorResult> {\n return new Promise<ProductionSupervisorResult>((resolve) => {\n // On Windows, we need to be careful with signals - the console sends Ctrl+C\n // to all processes in the group, so we just need to not interfere.\n // `process.execPath`, not \"node\": the bare name needs `node` on PATH, which\n // a systemd unit, a cron job, or a slim container may not provide — and a\n // PATH `node` that *is* present can be a different version than the one\n // running this CLI. execPath is the binary already executing us.\n //\n // The fourth stdio slot opens the IPC channel the child reports readiness\n // on. Without it `process.send` is undefined in the child and we would be\n // back to guessing whether the boot succeeded.\n const child = spawn(process.execPath, nodeArgs, {\n stdio: [\"inherit\", \"inherit\", \"inherit\", \"ipc\"],\n cwd,\n // The flag identifies us as a parent that speaks the boot protocol. The\n // child stays silent without it, so an app running under pm2 or any other\n // supervisor never writes to a channel that belongs to someone else.\n env: { ...env, [BOOT_SIGNAL_ENV_KEY]: \"1\" },\n // Important: keep child in same process group for proper signal handling\n detached: false,\n });\n\n const startedAt = Date.now();\n let isReady = false;\n let isShuttingDown = false;\n let isSettled = false;\n\n const readinessNoticeTimer = setTimeout(() => {\n if (isReady) {\n return;\n }\n\n displayMissingReadinessNotice(Date.now() - startedAt);\n }, readinessNoticeDelayMs);\n\n // A diagnostic timer must never be the reason the CLI stays alive.\n readinessNoticeTimer.unref?.();\n\n const settle = (result: ProductionSupervisorResult) => {\n if (isSettled) {\n return;\n }\n\n isSettled = true;\n clearTimeout(readinessNoticeTimer);\n process.off(\"SIGTERM\", onSigterm);\n process.off(\"SIGINT\", onSigint);\n\n resolve(result);\n };\n\n child.on(\"message\", (message: unknown) => {\n if (isReady || !isBootSignal(message)) {\n return;\n }\n\n isReady = true;\n clearTimeout(readinessNoticeTimer);\n\n displayProductionReadyBanner({ bootDurationMs: message.bootDurationMs }).catch((error) => {\n // The server is up either way — a banner that fails to render must not\n // be reported as, or turn into, a boot failure.\n console.error(error);\n });\n });\n\n // A spawn that never starts (missing binary, EACCES) emits `error` and no\n // `exit`, so without this the command would hang silently on a failure.\n child.on(\"error\", (error) => {\n displayProductionStartFailure(1);\n console.error(error);\n\n settle({ exitCode: 1, ready: false });\n });\n\n // SIGTERM doesn't auto-propagate like SIGINT does on Windows.\n const onSigterm = () => {\n if (isShuttingDown) {\n return;\n }\n\n isShuttingDown = true;\n child.kill(\"SIGTERM\");\n };\n\n // On Windows Ctrl+C reaches both processes, so the child already has it —\n // we only record that this exit was asked for.\n const onSigint = () => {\n isShuttingDown = true;\n };\n\n process.on(\"SIGTERM\", onSigterm);\n process.on(\"SIGINT\", onSigint);\n\n child.on(\"exit\", (code) => {\n // A child that dies before reporting readiness never served a request.\n // Exiting 0 there — which a clean `process.exit(0)` in failing app code\n // would do — reports a boot failure as a successful run, so the exit code\n // is forced non-zero to match what actually happened.\n //\n // An operator who interrupts the boot is not a failed boot, so a signalled\n // shutdown keeps the child's own exit code.\n if (!isReady && !isShuttingDown) {\n const exitCode = code === 0 || code === null ? 1 : code;\n\n displayProductionStartFailure(exitCode);\n\n return settle({ exitCode, ready: false });\n }\n\n settle({ exitCode: code ?? 0, ready: isReady });\n });\n });\n}\n"],"mappings":";;;;;;;;;;;AAcA,MAAa,oCAAoC;;;;;;;;;;;;;;;;;;AAkCjD,SAAgB,2BAA2B,EACzC,UACA,MAAM,QAAQ,IAAI,GAClB,MAAM,QAAQ,KACd,yBAAyB,qCAC0C;CACnE,OAAO,IAAI,SAAqC,YAAY;EAW1D,MAAM,QAAQ,MAAM,QAAQ,UAAU,UAAU;GAC9C,OAAO;IAAC;IAAW;IAAW;IAAW;GAAK;GAC9C;GAIA,KAAK;IAAE,GAAG;KAAM,sBAAsB;GAAI;GAE1C,UAAU;EACZ,CAAC;EAED,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI,UAAU;EACd,IAAI,iBAAiB;EACrB,IAAI,YAAY;EAEhB,MAAM,uBAAuB,iBAAiB;GAC5C,IAAI,SACF;GAGF,8BAA8B,KAAK,IAAI,IAAI,SAAS;EACtD,GAAG,sBAAsB;EAGzB,qBAAqB,QAAQ;EAE7B,MAAM,UAAU,WAAuC;GACrD,IAAI,WACF;GAGF,YAAY;GACZ,aAAa,oBAAoB;GACjC,QAAQ,IAAI,WAAW,SAAS;GAChC,QAAQ,IAAI,UAAU,QAAQ;GAE9B,QAAQ,MAAM;EAChB;EAEA,MAAM,GAAG,YAAY,YAAqB;GACxC,IAAI,WAAW,CAAC,aAAa,OAAO,GAClC;GAGF,UAAU;GACV,aAAa,oBAAoB;GAEjC,6BAA6B,EAAE,gBAAgB,QAAQ,eAAe,CAAC,CAAC,CAAC,OAAO,UAAU;IAGxF,QAAQ,MAAM,KAAK;GACrB,CAAC;EACH,CAAC;EAID,MAAM,GAAG,UAAU,UAAU;GAC3B,8BAA8B,CAAC;GAC/B,QAAQ,MAAM,KAAK;GAEnB,OAAO;IAAE,UAAU;IAAG,OAAO;GAAM,CAAC;EACtC,CAAC;EAGD,MAAM,kBAAkB;GACtB,IAAI,gBACF;GAGF,iBAAiB;GACjB,MAAM,KAAK,SAAS;EACtB;EAIA,MAAM,iBAAiB;GACrB,iBAAiB;EACnB;EAEA,QAAQ,GAAG,WAAW,SAAS;EAC/B,QAAQ,GAAG,UAAU,QAAQ;EAE7B,MAAM,GAAG,SAAS,SAAS;GAQzB,IAAI,CAAC,WAAW,CAAC,gBAAgB;IAC/B,MAAM,WAAW,SAAS,KAAK,SAAS,OAAO,IAAI;IAEnD,8BAA8B,QAAQ;IAEtC,OAAO,OAAO;KAAE;KAAU,OAAO;IAAM,CAAC;GAC1C;GAEA,OAAO;IAAE,UAAU,QAAQ;IAAG,OAAO;GAAQ,CAAC;EAChD,CAAC;CACH,CAAC;AACH"}
|
|
1
|
+
{"version":3,"file":"production-supervisor.mjs","names":[],"sources":["../../../../../../../core/src/production/production-supervisor.ts"],"sourcesContent":["import { spawn } from \"child_process\";\nimport { BOOT_SIGNAL_ENV_KEY, isBootSignal } from \"../application/boot-signal\";\nimport {\n displayMissingReadinessNotice,\n displayProductionReadyBanner,\n displayProductionStartFailure,\n} from \"../cli/cli-commands.utils\";\n\n/**\n * How long a still-running child may stay silent before we say so.\n *\n * Only reached by a bundle built before readiness reporting existed; a current\n * bundle signals as soon as its late-phase connectors are up.\n */\nexport const DEFAULT_READINESS_NOTICE_DELAY_MS = 10_000;\n\nexport type ProductionSupervisorOptions = {\n /** Arguments handed to the node binary, entry file included. */\n nodeArgs: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n readinessNoticeDelayMs?: number;\n};\n\nexport type ProductionSupervisorResult = {\n /** What the CLI should exit with — never 0 for a boot that never completed. */\n exitCode: number;\n /** Whether the child ever reported a finished boot. */\n ready: boolean;\n};\n\n/**\n * Run the production bundle in a child process and report honestly on it.\n *\n * The supervisor exists because a parent that only forwards `stdio` cannot\n * see whether its child ever booted — it only sees the process exit. It\n * therefore opens an IPC channel and treats the child's `warlock:ready`\n * signal, emitted by `Application.markBooted`, as the single source of truth\n * for \"started\":\n *\n * 1. The success banner is printed only on that signal.\n * 2. A child that dies before signalling is reported as a failed start and\n * forced to a non-zero exit, even if it exited 0.\n * 3. A child still running without having signalled draws a note on stderr —\n * never stdout, which would be the false-green this whole channel prevents.\n * 4. Whether the child ever wrote to stdout/stderr before dying is tracked\n * (`sawChildOutput`) so the failure summary only claims \"the cause is\n * printed above\" when that is actually true — see the stdio comment\n * below.\n *\n * Resolves rather than exiting so the outcome stays assertable in a test; the\n * `start` command turns the result into the process exit code.\n */\nexport function superviseProductionProcess({\n nodeArgs,\n cwd = process.cwd(),\n env = process.env,\n readinessNoticeDelayMs = DEFAULT_READINESS_NOTICE_DELAY_MS,\n}: ProductionSupervisorOptions): Promise<ProductionSupervisorResult> {\n return new Promise<ProductionSupervisorResult>((resolve) => {\n // On Windows, we need to be careful with signals - the console sends Ctrl+C\n // to all processes in the group, so we just need to not interfere.\n // `process.execPath`, not \"node\": the bare name needs `node` on PATH, which\n // a systemd unit, a cron job, or a slim container may not provide — and a\n // PATH `node` that *is* present can be a different version than the one\n // running this CLI. execPath is the binary already executing us.\n //\n // The fourth stdio slot opens the IPC channel the child reports readiness\n // on. Without it `process.send` is undefined in the child and we would be\n // back to guessing whether the boot succeeded.\n //\n // stdout/stderr are piped rather than inherited so this supervisor can\n // tell whether the child ever wrote anything, not just forward it blind.\n // `displayProductionStartFailure` used to claim \"the cause is printed\n // above\" on every failed boot, unconditionally — true when the child's\n // logger got far enough to print, false (and misleading) when it died\n // before its log channels were configured. Piping costs nothing here:\n // both streams are re-forwarded to this process's own stdout/stderr as\n // each chunk arrives, so a human watching the terminal sees the exact\n // same live output \"inherit\" gave them.\n const child = spawn(process.execPath, nodeArgs, {\n stdio: [\"inherit\", \"pipe\", \"pipe\", \"ipc\"],\n cwd,\n // The flag identifies us as a parent that speaks the boot protocol. The\n // child stays silent without it, so an app running under pm2 or any other\n // supervisor never writes to a channel that belongs to someone else.\n env: { ...env, [BOOT_SIGNAL_ENV_KEY]: \"1\" },\n // Important: keep child in same process group for proper signal handling\n detached: false,\n });\n\n const startedAt = Date.now();\n let isReady = false;\n let isShuttingDown = false;\n let isSettled = false;\n\n // Whether the child wrote anything to either stream — the only honest\n // basis for telling a developer \"the cause is printed above\". Set from\n // the first chunk on either stream; a child that dies before its first\n // write (a crash mid-import, a logger with no console channel) leaves\n // this `false`, which is exactly the case the summary must admit rather\n // than paper over.\n let sawChildOutput = false;\n\n const forwardChildOutput =\n (target: NodeJS.WritableStream) => (chunk: Buffer) => {\n sawChildOutput = true;\n target.write(chunk);\n };\n\n child.stdout?.on(\"data\", forwardChildOutput(process.stdout));\n child.stderr?.on(\"data\", forwardChildOutput(process.stderr));\n\n const readinessNoticeTimer = setTimeout(() => {\n if (isReady) {\n return;\n }\n\n displayMissingReadinessNotice(Date.now() - startedAt);\n }, readinessNoticeDelayMs);\n\n // A diagnostic timer must never be the reason the CLI stays alive.\n readinessNoticeTimer.unref?.();\n\n const settle = (result: ProductionSupervisorResult) => {\n if (isSettled) {\n return;\n }\n\n isSettled = true;\n clearTimeout(readinessNoticeTimer);\n process.off(\"SIGTERM\", onSigterm);\n process.off(\"SIGINT\", onSigint);\n\n resolve(result);\n };\n\n child.on(\"message\", (message: unknown) => {\n if (isReady || !isBootSignal(message)) {\n return;\n }\n\n isReady = true;\n clearTimeout(readinessNoticeTimer);\n\n displayProductionReadyBanner({ bootDurationMs: message.bootDurationMs }).catch((error) => {\n // The server is up either way — a banner that fails to render must not\n // be reported as, or turn into, a boot failure.\n console.error(error);\n });\n });\n\n // A spawn that never starts (missing binary, EACCES) emits `error` and no\n // `exit`, so without this the command would hang silently on a failure.\n // The error is printed BEFORE the summary that references it — the\n // summary says the cause is \"above\", so it has to already be there.\n child.on(\"error\", (error) => {\n console.error(error);\n displayProductionStartFailure(1, true);\n\n settle({ exitCode: 1, ready: false });\n });\n\n // SIGTERM doesn't auto-propagate like SIGINT does on Windows.\n const onSigterm = () => {\n if (isShuttingDown) {\n return;\n }\n\n isShuttingDown = true;\n child.kill(\"SIGTERM\");\n };\n\n // On Windows Ctrl+C reaches both processes, so the child already has it —\n // we only record that this exit was asked for.\n const onSigint = () => {\n isShuttingDown = true;\n };\n\n process.on(\"SIGTERM\", onSigterm);\n process.on(\"SIGINT\", onSigint);\n\n // `close`, not `exit`: `exit` fires as soon as the OS process ends, which\n // can race ahead of the last chunks still in flight on the piped stdout/\n // stderr streams. `close` waits for those streams to end too, so\n // `sawChildOutput` reflects everything the child actually wrote instead\n // of whatever had been delivered by the time the process happened to die.\n child.on(\"close\", (code) => {\n // A child that dies before reporting readiness never served a request.\n // Exiting 0 there — which a clean `process.exit(0)` in failing app code\n // would do — reports a boot failure as a successful run, so the exit code\n // is forced non-zero to match what actually happened.\n //\n // An operator who interrupts the boot is not a failed boot, so a signalled\n // shutdown keeps the child's own exit code.\n if (!isReady && !isShuttingDown) {\n const exitCode = code === 0 || code === null ? 1 : code;\n\n displayProductionStartFailure(exitCode, sawChildOutput);\n\n return settle({ exitCode, ready: false });\n }\n\n settle({ exitCode: code ?? 0, ready: isReady });\n });\n });\n}\n"],"mappings":";;;;;;;;;;;AAcA,MAAa,oCAAoC;;;;;;;;;;;;;;;;;;;;;;;AAuCjD,SAAgB,2BAA2B,EACzC,UACA,MAAM,QAAQ,IAAI,GAClB,MAAM,QAAQ,KACd,yBAAyB,qCAC0C;CACnE,OAAO,IAAI,SAAqC,YAAY;EAqB1D,MAAM,QAAQ,MAAM,QAAQ,UAAU,UAAU;GAC9C,OAAO;IAAC;IAAW;IAAQ;IAAQ;GAAK;GACxC;GAIA,KAAK;IAAE,GAAG;KAAM,sBAAsB;GAAI;GAE1C,UAAU;EACZ,CAAC;EAED,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI,UAAU;EACd,IAAI,iBAAiB;EACrB,IAAI,YAAY;EAQhB,IAAI,iBAAiB;EAErB,MAAM,sBACH,YAAmC,UAAkB;GACpD,iBAAiB;GACjB,OAAO,MAAM,KAAK;EACpB;EAEF,MAAM,QAAQ,GAAG,QAAQ,mBAAmB,QAAQ,MAAM,CAAC;EAC3D,MAAM,QAAQ,GAAG,QAAQ,mBAAmB,QAAQ,MAAM,CAAC;EAE3D,MAAM,uBAAuB,iBAAiB;GAC5C,IAAI,SACF;GAGF,8BAA8B,KAAK,IAAI,IAAI,SAAS;EACtD,GAAG,sBAAsB;EAGzB,qBAAqB,QAAQ;EAE7B,MAAM,UAAU,WAAuC;GACrD,IAAI,WACF;GAGF,YAAY;GACZ,aAAa,oBAAoB;GACjC,QAAQ,IAAI,WAAW,SAAS;GAChC,QAAQ,IAAI,UAAU,QAAQ;GAE9B,QAAQ,MAAM;EAChB;EAEA,MAAM,GAAG,YAAY,YAAqB;GACxC,IAAI,WAAW,CAAC,aAAa,OAAO,GAClC;GAGF,UAAU;GACV,aAAa,oBAAoB;GAEjC,6BAA6B,EAAE,gBAAgB,QAAQ,eAAe,CAAC,EAAE,OAAO,UAAU;IAGxF,QAAQ,MAAM,KAAK;GACrB,CAAC;EACH,CAAC;EAMD,MAAM,GAAG,UAAU,UAAU;GAC3B,QAAQ,MAAM,KAAK;GACnB,8BAA8B,GAAG,IAAI;GAErC,OAAO;IAAE,UAAU;IAAG,OAAO;GAAM,CAAC;EACtC,CAAC;EAGD,MAAM,kBAAkB;GACtB,IAAI,gBACF;GAGF,iBAAiB;GACjB,MAAM,KAAK,SAAS;EACtB;EAIA,MAAM,iBAAiB;GACrB,iBAAiB;EACnB;EAEA,QAAQ,GAAG,WAAW,SAAS;EAC/B,QAAQ,GAAG,UAAU,QAAQ;EAO7B,MAAM,GAAG,UAAU,SAAS;GAQ1B,IAAI,CAAC,WAAW,CAAC,gBAAgB;IAC/B,MAAM,WAAW,SAAS,KAAK,SAAS,OAAO,IAAI;IAEnD,8BAA8B,UAAU,cAAc;IAEtD,OAAO,OAAO;KAAE;KAAU,OAAO;IAAM,CAAC;GAC1C;GAEA,OAAO;IAAE,UAAU,QAAQ;IAAG,OAAO;GAAQ,CAAC;EAChD,CAAC;CACH,CAAC;AACH"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { directoryExistsAsync, removeDirectoryAsync, renameFileAsync } from "@warlock.js/fs";
|
|
3
|
+
import { randomBytes } from "crypto";
|
|
4
|
+
|
|
5
|
+
//#region ../core/src/production/promote-dist.ts
|
|
6
|
+
/**
|
|
7
|
+
* A sibling of `outDir` that esbuild bundles into instead of the real output
|
|
8
|
+
* directory. Same parent folder as `outDir` on purpose — {@link stageDistAsync}
|
|
9
|
+
* moves it into place with a directory rename, and a rename only stays a
|
|
10
|
+
* single atomic filesystem operation when source and destination share a
|
|
11
|
+
* volume.
|
|
12
|
+
*
|
|
13
|
+
* This path is a WRITE TARGET and nothing else. It is never written back into
|
|
14
|
+
* the build config: `options.outdir` names the FINAL destination at all times,
|
|
15
|
+
* because consumers bake it into runtime output (the web contribution derives
|
|
16
|
+
* its `clientDir` from it) and a runtime path pointing at a temp directory
|
|
17
|
+
* that promotion renames away is a broken artifact, not a broken build.
|
|
18
|
+
*/
|
|
19
|
+
function createTempOutputDir(outDir) {
|
|
20
|
+
const parent = path.dirname(outDir);
|
|
21
|
+
const base = path.basename(outDir);
|
|
22
|
+
const suffix = randomBytes(6).toString("hex");
|
|
23
|
+
return path.join(parent, `.${base}.build-${suffix}`);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Move a freshly-built temp directory into place as `outDir`, replacing
|
|
27
|
+
* whatever was there — without ever leaving `outDir` a mix of old and new
|
|
28
|
+
* files, and without discarding the previous build yet.
|
|
29
|
+
*
|
|
30
|
+
* `fs.rename` moves a directory as a single filesystem operation, so each
|
|
31
|
+
* rename below is atomic: `outDir` is observed as either wholly the old
|
|
32
|
+
* build, wholly the new one, or (only for the instant between the two
|
|
33
|
+
* renames below, and only when one previously existed) briefly absent —
|
|
34
|
+
* never a mix of the two.
|
|
35
|
+
*
|
|
36
|
+
* Windows refuses to rename a directory onto a path that already exists, so
|
|
37
|
+
* an existing `outDir` is moved aside first and only deleted by
|
|
38
|
+
* {@link commitDistAsync}, once the build that follows has finished writing
|
|
39
|
+
* into the new one. If the second rename throws — the only realistic way our
|
|
40
|
+
* own code could leave `outDir` missing, as opposed to the process being
|
|
41
|
+
* killed at the exact wrong instant — the old directory is moved straight
|
|
42
|
+
* back before the error propagates, so a failed promotion never costs the
|
|
43
|
+
* previous good build.
|
|
44
|
+
*
|
|
45
|
+
* Staging is deliberately separate from committing. The steps that run
|
|
46
|
+
* between them — connector `emit` hooks, and the build-success marker — need
|
|
47
|
+
* `outDir` to be the REAL path while they run, because what they write bakes
|
|
48
|
+
* that path into the artifact. Renaming first and committing last is what
|
|
49
|
+
* lets them see the final path and still be undone as a unit.
|
|
50
|
+
*
|
|
51
|
+
* @param tempDir the freshly-built directory to promote (removed as a side
|
|
52
|
+
* effect of the rename; the caller does not need to clean it up once this
|
|
53
|
+
* resolves)
|
|
54
|
+
* @param outDir the real output directory (`dist`) to replace
|
|
55
|
+
*/
|
|
56
|
+
async function stageDistAsync(tempDir, outDir) {
|
|
57
|
+
if (!await directoryExistsAsync(outDir)) {
|
|
58
|
+
await renameFileAsync(tempDir, outDir);
|
|
59
|
+
return { outDir };
|
|
60
|
+
}
|
|
61
|
+
const previousDir = `${outDir}.stale-${randomBytes(6).toString("hex")}`;
|
|
62
|
+
await renameFileAsync(outDir, previousDir);
|
|
63
|
+
try {
|
|
64
|
+
await renameFileAsync(tempDir, outDir);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
await renameFileAsync(previousDir, outDir).catch(() => void 0);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
outDir,
|
|
71
|
+
previousDir
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Finish a staged promotion: the new `dist` is complete, so the parked
|
|
76
|
+
* previous build is no longer needed.
|
|
77
|
+
*
|
|
78
|
+
* Never throws. It is the last step of a build that has already succeeded,
|
|
79
|
+
* and a leftover `dist.stale-*` directory is untidy, not incorrect — failing
|
|
80
|
+
* the build over it would turn a good artifact into a red run.
|
|
81
|
+
*/
|
|
82
|
+
async function commitDistAsync(staged) {
|
|
83
|
+
if (!staged.previousDir) return;
|
|
84
|
+
await removeDirectoryAsync(staged.previousDir).catch(() => void 0);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Undo a staged promotion after a later step failed: put the previous build
|
|
88
|
+
* back where it was, and take the half-finished one out of `dist`.
|
|
89
|
+
*
|
|
90
|
+
* The failed build is renamed aside before the previous one is renamed back,
|
|
91
|
+
* for the same reason {@link stageDistAsync} moves the old directory aside
|
|
92
|
+
* first: a recursive delete of `dist` would leave it observable as a partial
|
|
93
|
+
* tree, while a rename does not.
|
|
94
|
+
*
|
|
95
|
+
* Never throws. It runs inside a `catch`, and the error it is unwinding is
|
|
96
|
+
* the one the developer needs to see — a rollback failure must not replace
|
|
97
|
+
* it. The worst case it can leave behind is a `dist` holding an incomplete
|
|
98
|
+
* build with no build-success marker, which `warlock start` refuses by
|
|
99
|
+
* definition.
|
|
100
|
+
*/
|
|
101
|
+
async function rollbackDistAsync(staged) {
|
|
102
|
+
const { outDir, previousDir } = staged;
|
|
103
|
+
if (!previousDir) {
|
|
104
|
+
await removeDirectoryAsync(outDir).catch(() => void 0);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const discardDir = `${outDir}.failed-${randomBytes(6).toString("hex")}`;
|
|
108
|
+
await renameFileAsync(outDir, discardDir).catch(() => void 0);
|
|
109
|
+
await renameFileAsync(previousDir, outDir).catch(() => void 0);
|
|
110
|
+
await removeDirectoryAsync(discardDir).catch(() => void 0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//#endregion
|
|
114
|
+
export { commitDistAsync, createTempOutputDir, rollbackDistAsync, stageDistAsync };
|
|
115
|
+
//# sourceMappingURL=promote-dist.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote-dist.mjs","names":[],"sources":["../../../../../../../core/src/production/promote-dist.ts"],"sourcesContent":["import { directoryExistsAsync, removeDirectoryAsync, renameFileAsync } from \"@warlock.js/fs\";\nimport { randomBytes } from \"crypto\";\nimport path from \"path\";\n\n/**\n * A sibling of `outDir` that esbuild bundles into instead of the real output\n * directory. Same parent folder as `outDir` on purpose — {@link stageDistAsync}\n * moves it into place with a directory rename, and a rename only stays a\n * single atomic filesystem operation when source and destination share a\n * volume.\n *\n * This path is a WRITE TARGET and nothing else. It is never written back into\n * the build config: `options.outdir` names the FINAL destination at all times,\n * because consumers bake it into runtime output (the web contribution derives\n * its `clientDir` from it) and a runtime path pointing at a temp directory\n * that promotion renames away is a broken artifact, not a broken build.\n */\nexport function createTempOutputDir(outDir: string): string {\n const parent = path.dirname(outDir);\n const base = path.basename(outDir);\n const suffix = randomBytes(6).toString(\"hex\");\n\n return path.join(parent, `.${base}.build-${suffix}`);\n}\n\n/**\n * A promotion in flight: the temp build has been renamed onto `outDir`, and\n * the previous `outDir` (when there was one) is parked under `previousDir`\n * until the caller {@link commitDistAsync commits} or\n * {@link rollbackDistAsync rolls back}.\n */\nexport type StagedDist = {\n /** The real output directory (`dist`) the temp build now occupies */\n outDir: string;\n /**\n * The moved-aside previous build, or `undefined` when `outDir` did not\n * exist before staging — the marker for \"a rollback has nothing to restore,\n * it only has to remove what this build put there\".\n */\n previousDir?: string;\n};\n\n/**\n * Move a freshly-built temp directory into place as `outDir`, replacing\n * whatever was there — without ever leaving `outDir` a mix of old and new\n * files, and without discarding the previous build yet.\n *\n * `fs.rename` moves a directory as a single filesystem operation, so each\n * rename below is atomic: `outDir` is observed as either wholly the old\n * build, wholly the new one, or (only for the instant between the two\n * renames below, and only when one previously existed) briefly absent —\n * never a mix of the two.\n *\n * Windows refuses to rename a directory onto a path that already exists, so\n * an existing `outDir` is moved aside first and only deleted by\n * {@link commitDistAsync}, once the build that follows has finished writing\n * into the new one. If the second rename throws — the only realistic way our\n * own code could leave `outDir` missing, as opposed to the process being\n * killed at the exact wrong instant — the old directory is moved straight\n * back before the error propagates, so a failed promotion never costs the\n * previous good build.\n *\n * Staging is deliberately separate from committing. The steps that run\n * between them — connector `emit` hooks, and the build-success marker — need\n * `outDir` to be the REAL path while they run, because what they write bakes\n * that path into the artifact. Renaming first and committing last is what\n * lets them see the final path and still be undone as a unit.\n *\n * @param tempDir the freshly-built directory to promote (removed as a side\n * effect of the rename; the caller does not need to clean it up once this\n * resolves)\n * @param outDir the real output directory (`dist`) to replace\n */\nexport async function stageDistAsync(tempDir: string, outDir: string): Promise<StagedDist> {\n const previousExists = await directoryExistsAsync(outDir);\n\n if (!previousExists) {\n await renameFileAsync(tempDir, outDir);\n\n return { outDir };\n }\n\n const previousDir = `${outDir}.stale-${randomBytes(6).toString(\"hex\")}`;\n\n await renameFileAsync(outDir, previousDir);\n\n try {\n await renameFileAsync(tempDir, outDir);\n } catch (error) {\n await renameFileAsync(previousDir, outDir).catch(() => undefined);\n throw error;\n }\n\n return { outDir, previousDir };\n}\n\n/**\n * Finish a staged promotion: the new `dist` is complete, so the parked\n * previous build is no longer needed.\n *\n * Never throws. It is the last step of a build that has already succeeded,\n * and a leftover `dist.stale-*` directory is untidy, not incorrect — failing\n * the build over it would turn a good artifact into a red run.\n */\nexport async function commitDistAsync(staged: StagedDist): Promise<void> {\n if (!staged.previousDir) return;\n\n await removeDirectoryAsync(staged.previousDir).catch(() => undefined);\n}\n\n/**\n * Undo a staged promotion after a later step failed: put the previous build\n * back where it was, and take the half-finished one out of `dist`.\n *\n * The failed build is renamed aside before the previous one is renamed back,\n * for the same reason {@link stageDistAsync} moves the old directory aside\n * first: a recursive delete of `dist` would leave it observable as a partial\n * tree, while a rename does not.\n *\n * Never throws. It runs inside a `catch`, and the error it is unwinding is\n * the one the developer needs to see — a rollback failure must not replace\n * it. The worst case it can leave behind is a `dist` holding an incomplete\n * build with no build-success marker, which `warlock start` refuses by\n * definition.\n */\nexport async function rollbackDistAsync(staged: StagedDist): Promise<void> {\n const { outDir, previousDir } = staged;\n\n if (!previousDir) {\n await removeDirectoryAsync(outDir).catch(() => undefined);\n\n return;\n }\n\n const discardDir = `${outDir}.failed-${randomBytes(6).toString(\"hex\")}`;\n\n await renameFileAsync(outDir, discardDir).catch(() => undefined);\n await renameFileAsync(previousDir, outDir).catch(() => undefined);\n await removeDirectoryAsync(discardDir).catch(() => undefined);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,SAAgB,oBAAoB,QAAwB;CAC1D,MAAM,SAAS,KAAK,QAAQ,MAAM;CAClC,MAAM,OAAO,KAAK,SAAS,MAAM;CACjC,MAAM,SAAS,YAAY,CAAC,EAAE,SAAS,KAAK;CAE5C,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,SAAS,QAAQ;AACrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,eAAe,SAAiB,QAAqC;CAGzF,IAAI,CAAC,MAFwB,qBAAqB,MAAM,GAEnC;EACnB,MAAM,gBAAgB,SAAS,MAAM;EAErC,OAAO,EAAE,OAAO;CAClB;CAEA,MAAM,cAAc,GAAG,OAAO,SAAS,YAAY,CAAC,EAAE,SAAS,KAAK;CAEpE,MAAM,gBAAgB,QAAQ,WAAW;CAEzC,IAAI;EACF,MAAM,gBAAgB,SAAS,MAAM;CACvC,SAAS,OAAO;EACd,MAAM,gBAAgB,aAAa,MAAM,EAAE,YAAY,MAAS;EAChE,MAAM;CACR;CAEA,OAAO;EAAE;EAAQ;CAAY;AAC/B;;;;;;;;;AAUA,eAAsB,gBAAgB,QAAmC;CACvE,IAAI,CAAC,OAAO,aAAa;CAEzB,MAAM,qBAAqB,OAAO,WAAW,EAAE,YAAY,MAAS;AACtE;;;;;;;;;;;;;;;;AAiBA,eAAsB,kBAAkB,QAAmC;CACzE,MAAM,EAAE,QAAQ,gBAAgB;CAEhC,IAAI,CAAC,aAAa;EAChB,MAAM,qBAAqB,MAAM,EAAE,YAAY,MAAS;EAExD;CACF;CAEA,MAAM,aAAa,GAAG,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,KAAK;CAEpE,MAAM,gBAAgB,QAAQ,UAAU,EAAE,YAAY,MAAS;CAC/D,MAAM,gBAAgB,aAAa,MAAM,EAAE,YAAY,MAAS;CAChE,MAAM,qBAAqB,UAAU,EAAE,YAAY,MAAS;AAC9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-build-config.mjs","names":[],"sources":["../../../../../../../core/src/production/resolve-build-config.ts"],"sourcesContent":["import path from \"path\";\nimport { defaultWarlockConfigurations } from \"../warlock-config/default-configurations\";\nimport { normalizeBuildConfig } from \"../warlock-config/normalize-build-config\";\nimport type { WarlockConfig } from \"../warlock-config/types\";\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\n\n/**\n * Keys the builder deletes before handing the options to esbuild — ours\n * (`singleBundle`, `esmShim`), which esbuild would reject as unknown, and\n * `banner`, which is re-merged and re-applied after the user spread.\n *\n * They are optional here for a concrete reason: `Required<…>` makes `delete`\n * a type error (TS2790). Naming exactly these three says which keys are\n * removable, rather than casting at each `delete` — a cast there would also\n * hide a genuine mistake.\n *\n * `outFile` and `entryPath` stay REQUIRED even though the builder deletes\n * them too. They are always present after resolution and callers depend on\n * that; widening them to `string | undefined` pushed the lie downstream into\n * `start-production.command.ts`, which then could not trust its own config.\n */\ntype BuilderStrippedKeys = \"singleBundle\" | \"esmShim\" | \"banner\";\n\nexport type ResolvedBuildConfig = Required<\n Omit<NonNullable<WarlockConfig[\"build\"]>, \"outDirectory\" | BuilderStrippedKeys>\n> &\n Partial<Pick<NonNullable<WarlockConfig[\"build\"]>, BuilderStrippedKeys>> & {\n /** Absolute path to the bundled entry file (`{outdir}/{outFile}`) */\n entryPath: string;\n };\n\n/**\n * esbuild options the build config may not carry, and why.\n *\n * Both apply the named compiler options to EVERY file in the bundle, not just\n * the app's own sources — workspace packages compiled under their own tsconfig\n * are silently recompiled under the app's. An app-level\n * `verbatimModuleSyntax: true` reaching package sources that never opted into\n * it strips the imports their decorators rely on, and the build succeeds only\n * to die at import time with `Class extends value undefined`.\n */\nconst FORBIDDEN_BUILD_OPTIONS = {\n tsconfig: \"points esbuild at a tsconfig file\",\n tsconfigRaw: \"inlines compiler options\",\n} as const;\n\n/**\n * Raised when the build config carries an esbuild option the framework owns.\n *\n * The typed config already omits these keys; this catches the forms the type\n * cannot see — an `as any` cast, or a plain `warlock.config.js`. Rejected\n * rather than deleted: dropping the key quietly would leave the author\n * believing a setting applied when it never did.\n */\nexport class ForbiddenBuildOptionError extends Error {\n public constructor(public readonly option: keyof typeof FORBIDDEN_BUILD_OPTIONS) {\n super(\n [\n `build.${option} is not a supported warlock build option.`,\n \"\",\n `Setting it ${FORBIDDEN_BUILD_OPTIONS[option]} for every file in the bundle,`,\n \"including workspace packages that were compiled under their own tsconfig.\",\n \"Options such as `verbatimModuleSyntax` then apply to sources that never\",\n \"opted into them, and the build succeeds but crashes at import time with\",\n \"`Class extends value undefined`.\",\n \"\",\n `Remove \\`${option}\\` from the \\`build\\` block of your warlock config. The builder`,\n \"already reads the app's tsconfig for the parts it needs (path aliases); to\",\n \"change how your own sources compile, edit that tsconfig.json instead.\",\n ].join(\"\\n\"),\n );\n\n this.name = \"ForbiddenBuildOptionError\";\n }\n}\n\n/**\n * Resolve the build config with framework defaults applied.\n *\n * Both the production builder and `warlock start` call this so they\n * agree on where the bundle lives — previously each had its own local\n * fallbacks and they drifted (`.warlock/production` vs `dist`),\n * letting `build` and `start` look at different paths.\n */\nexport function resolveBuildConfig(): ResolvedBuildConfig {\n // Normalised here rather than only in `defineConfig` because a project may\n // export a plain config object that never went through it.\n const userBuild = normalizeBuildConfig(warlockConfigManager.get(\"build\") ?? {});\n\n // Before the merge, so the option cannot reach the esbuild spread in\n // production-builder — which overrides framework defaults with user config.\n for (const option of Object.keys(FORBIDDEN_BUILD_OPTIONS) as Array<\n keyof typeof FORBIDDEN_BUILD_OPTIONS\n >) {\n if (option in userBuild) {\n throw new ForbiddenBuildOptionError(option);\n }\n }\n\n const defaults = defaultWarlockConfigurations.build!;\n const merged = { ...defaults, ...userBuild } as ResolvedBuildConfig;\n\n return {\n ...merged,\n entryPath: path.resolve(merged.outdir, merged.outFile),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyCA,MAAM,0BAA0B;CAC9B,UAAU;CACV,aAAa;AACf;;;;;;;;;AAUA,IAAa,4BAAb,cAA+C,MAAM;CACnD,AAAO,YAAY,AAAgB,QAA8C;EAC/E,MACE;GACE,SAAS,OAAO;GAChB;GACA,cAAc,wBAAwB,QAAQ;GAC9C;GACA;GACA;GACA;GACA;GACA,YAAY,OAAO;GACnB;GACA;EACF,
|
|
1
|
+
{"version":3,"file":"resolve-build-config.mjs","names":[],"sources":["../../../../../../../core/src/production/resolve-build-config.ts"],"sourcesContent":["import path from \"path\";\nimport { defaultWarlockConfigurations } from \"../warlock-config/default-configurations\";\nimport { normalizeBuildConfig } from \"../warlock-config/normalize-build-config\";\nimport type { WarlockConfig } from \"../warlock-config/types\";\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\n\n/**\n * Keys the builder deletes before handing the options to esbuild — ours\n * (`singleBundle`, `esmShim`), which esbuild would reject as unknown, and\n * `banner`, which is re-merged and re-applied after the user spread.\n *\n * They are optional here for a concrete reason: `Required<…>` makes `delete`\n * a type error (TS2790). Naming exactly these three says which keys are\n * removable, rather than casting at each `delete` — a cast there would also\n * hide a genuine mistake.\n *\n * `outFile` and `entryPath` stay REQUIRED even though the builder deletes\n * them too. They are always present after resolution and callers depend on\n * that; widening them to `string | undefined` pushed the lie downstream into\n * `start-production.command.ts`, which then could not trust its own config.\n */\ntype BuilderStrippedKeys = \"singleBundle\" | \"esmShim\" | \"banner\";\n\nexport type ResolvedBuildConfig = Required<\n Omit<NonNullable<WarlockConfig[\"build\"]>, \"outDirectory\" | BuilderStrippedKeys>\n> &\n Partial<Pick<NonNullable<WarlockConfig[\"build\"]>, BuilderStrippedKeys>> & {\n /** Absolute path to the bundled entry file (`{outdir}/{outFile}`) */\n entryPath: string;\n };\n\n/**\n * esbuild options the build config may not carry, and why.\n *\n * Both apply the named compiler options to EVERY file in the bundle, not just\n * the app's own sources — workspace packages compiled under their own tsconfig\n * are silently recompiled under the app's. An app-level\n * `verbatimModuleSyntax: true` reaching package sources that never opted into\n * it strips the imports their decorators rely on, and the build succeeds only\n * to die at import time with `Class extends value undefined`.\n */\nconst FORBIDDEN_BUILD_OPTIONS = {\n tsconfig: \"points esbuild at a tsconfig file\",\n tsconfigRaw: \"inlines compiler options\",\n} as const;\n\n/**\n * Raised when the build config carries an esbuild option the framework owns.\n *\n * The typed config already omits these keys; this catches the forms the type\n * cannot see — an `as any` cast, or a plain `warlock.config.js`. Rejected\n * rather than deleted: dropping the key quietly would leave the author\n * believing a setting applied when it never did.\n */\nexport class ForbiddenBuildOptionError extends Error {\n public constructor(public readonly option: keyof typeof FORBIDDEN_BUILD_OPTIONS) {\n super(\n [\n `build.${option} is not a supported warlock build option.`,\n \"\",\n `Setting it ${FORBIDDEN_BUILD_OPTIONS[option]} for every file in the bundle,`,\n \"including workspace packages that were compiled under their own tsconfig.\",\n \"Options such as `verbatimModuleSyntax` then apply to sources that never\",\n \"opted into them, and the build succeeds but crashes at import time with\",\n \"`Class extends value undefined`.\",\n \"\",\n `Remove \\`${option}\\` from the \\`build\\` block of your warlock config. The builder`,\n \"already reads the app's tsconfig for the parts it needs (path aliases); to\",\n \"change how your own sources compile, edit that tsconfig.json instead.\",\n ].join(\"\\n\"),\n );\n\n this.name = \"ForbiddenBuildOptionError\";\n }\n}\n\n/**\n * Resolve the build config with framework defaults applied.\n *\n * Both the production builder and `warlock start` call this so they\n * agree on where the bundle lives — previously each had its own local\n * fallbacks and they drifted (`.warlock/production` vs `dist`),\n * letting `build` and `start` look at different paths.\n */\nexport function resolveBuildConfig(): ResolvedBuildConfig {\n // Normalised here rather than only in `defineConfig` because a project may\n // export a plain config object that never went through it.\n const userBuild = normalizeBuildConfig(warlockConfigManager.get(\"build\") ?? {});\n\n // Before the merge, so the option cannot reach the esbuild spread in\n // production-builder — which overrides framework defaults with user config.\n for (const option of Object.keys(FORBIDDEN_BUILD_OPTIONS) as Array<\n keyof typeof FORBIDDEN_BUILD_OPTIONS\n >) {\n if (option in userBuild) {\n throw new ForbiddenBuildOptionError(option);\n }\n }\n\n const defaults = defaultWarlockConfigurations.build!;\n const merged = { ...defaults, ...userBuild } as ResolvedBuildConfig;\n\n return {\n ...merged,\n entryPath: path.resolve(merged.outdir, merged.outFile),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyCA,MAAM,0BAA0B;CAC9B,UAAU;CACV,aAAa;AACf;;;;;;;;;AAUA,IAAa,4BAAb,cAA+C,MAAM;CACnD,AAAO,YAAY,AAAgB,QAA8C;EAC/E,MACE;GACE,SAAS,OAAO;GAChB;GACA,cAAc,wBAAwB,QAAQ;GAC9C;GACA;GACA;GACA;GACA;GACA,YAAY,OAAO;GACnB;GACA;EACF,EAAE,KAAK,IAAI,CACb;EAfiC;EAiBjC,KAAK,OAAO;CACd;AACF;;;;;;;;;AAUA,SAAgB,qBAA0C;CAGxD,MAAM,YAAY,qBAAqB,qBAAqB,IAAI,OAAO,KAAK,CAAC,CAAC;CAI9E,KAAK,MAAM,UAAU,OAAO,KAAK,uBAAuB,GAGtD,IAAI,UAAU,WACZ,MAAM,IAAI,0BAA0B,MAAM;CAK9C,MAAM,SAAS;EAAE,GADA,6BAA6B;EAChB,GAAG;CAAU;CAE3C,OAAO;EACL,GAAG;EACH,WAAW,KAAK,QAAQ,OAAO,QAAQ,OAAO,OAAO;CACvD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cascade-adapter.d.mts","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/cascade-adapter.ts"],"mappings":";;;;;;;;;;;AAiBA;cAAa,cAAA,WAAyB,KAAA,kBAAuB,yBAAA,CAA0B,CAAA;EAAA,QAK1D,KAAA;EALS;;;;cAKT,KAAA,EAAO,UAAA,CAAW,CAAA;EAS7B;;;EAAT,KAAA,
|
|
1
|
+
{"version":3,"file":"cascade-adapter.d.mts","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/cascade-adapter.ts"],"mappings":";;;;;;;;;;;AAiBA;cAAa,cAAA,WAAyB,KAAA,kBAAuB,yBAAA,CAA0B,CAAA;EAAA,QAK1D,KAAA;EALS;;;;cAKT,KAAA,EAAO,UAAA,CAAW,CAAA;EAS7B;;;EAAT,KAAA,CAAA,GAAS,sBAAA,CAAqB,CAAA;EAyCY;;;EAlC1C,cAAA,CAAe,cAAA;EA8DU;;;EAnCnB,IAAA,CAAK,EAAA,QAAU,OAAA,CAAQ,CAAA;EAkEa;;;EA3DpC,MAAA,CAAO,MAAA,UAAgB,KAAA,QAAa,OAAA,CAAQ,CAAA;EA4HG;;;EArHrD,cAAA,CAAe,KAAA,EAAO,CAAA;EA8HlB;;;EAvHJ,gBAAA,CAAiB,IAAA,QAAY,CAAA;EAkI0B;;;EA3HvD,qBAAA,CAAA;EA5E6E;;;EAmFvE,MAAA,CAAO,IAAA,QAAY,OAAA,CAAQ,CAAA;EAnF6C;;;EA0FxE,MAAA,CAAO,EAAA,OAAS,IAAA,QAAY,OAAA,CAAQ,CAAA;EArFJ;;;EAkGhC,MAAA,CAAO,EAAA,QAAU,OAAA;EAzFO;;;EAoGxB,UAAA,CAAW,MAAA,OAAa,IAAA,QAAY,OAAA;EAlE/B;;;EAwFL,UAAA,CAAW,MAAA,QAAc,OAAA;EAjFlB;;;EA2GP,KAAA,CAAM,MAAA,SAAe,OAAA;EApG3B;;;EAqHM,QAAA,CAAS,IAAA,UAAc,KAAA,WAAgB,OAAA,CAAQ,gBAAA,CAAiB,CAAA;EA9GrD;;;EAqHX,cAAA,CACX,OAAA,EAAS,uBAAA,GACR,OAAA,CAAQ,sBAAA,CAAuB,CAAA;EAzGd;;;EAoHP,KAAA,CAAM,IAAA,UAAc,QAAA,EAAU,aAAA,CAAc,CAAA,IAAK,OAAA;EA7G1C;;;EAwHb,WAAA,CAAY,IAAA,QAAY,CAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cascade-adapter.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/cascade-adapter.ts"],"sourcesContent":["import { type ChildModel, Model } from \"@warlock.js/cascade\";\nimport type {\n ChunkCallback,\n CursorPaginationOptions,\n CursorPaginationResult,\n PaginationResult,\n QueryBuilderContract,\n RepositoryAdapterContract,\n} from \"../../contracts\";\nimport { CascadeQueryBuilder } from \"./cascade-query-builder\";\n\n/**\n * Cascade adapter for Cascade-Next ORM\n * Implements RepositoryAdapterContract for @warlock.js/cascade\n *\n * @template T - The model instance type\n */\nexport class CascadeAdapter<T extends Model<any>> implements RepositoryAdapterContract<T> {\n /**\n * Constructor\n * @param model - Cascade-Next Model class\n */\n public constructor(private model: ChildModel<T>) {}\n\n // ============================================================================\n // QUERY BUILDING\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.query}\n */\n public query(): QueryBuilderContract<T> {\n return new CascadeQueryBuilder<T>(this.model.query());\n }\n\n /**\n * Register all events\n */\n public registerEvents(eventsCallback: any): any[] {\n const events: any[] = [];\n\n const modelEvents = this.model.events();\n\n events.push(\n modelEvents.onCreated((model) => {\n eventsCallback(model);\n }),\n modelEvents.onUpdated((model) => {\n eventsCallback(model);\n }),\n modelEvents.onDeleted((model) => {\n eventsCallback(model);\n }),\n );\n\n return events;\n }\n\n // ============================================================================\n // CRUD OPERATIONS\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.find}\n */\n public async find(id: any): Promise<T | null> {\n return await this.model.find(id);\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.findBy}\n */\n public async findBy(column: string, value: any): Promise<T | null> {\n return (await this.query().where(column, value).first()) as T | null;\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.serializeModel}\n */\n public serializeModel(model: T): any {\n return model.toSnapshot();\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.deserializeModel}\n */\n public deserializeModel(data: any): T {\n return this.model.fromSnapshot(data);\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.resolveRepositoryName}\n */\n public resolveRepositoryName(): string {\n return this.model.table;\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.create}\n */\n public async create(data: any): Promise<T> {\n return this.model.create(data);\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.update}\n */\n public async update(id: any, data: any): Promise<T> {\n const model = id instanceof Model ? id : await this.model.find(id);\n if (!model) {\n throw new Error(`Model not found with id ${id}`);\n }\n\n await model.save({ merge: data });\n return model as T;\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.delete}\n */\n public async delete(id: any): Promise<void> {\n await this.model.delete({ id });\n }\n\n // ============================================================================\n // BULK OPERATIONS\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.updateMany}\n */\n public async updateMany(filter: any, data: any): Promise<number> {\n const query = this.query();\n\n // Apply filter\n if (typeof filter === \"object\") {\n query.where(filter);\n }\n\n // Get matching records\n const records = await query.get();\n\n // Update each record\n for (const record of records) {\n await this.update(record.id, data);\n }\n\n return records.length;\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.deleteMany}\n */\n public async deleteMany(filter: any): Promise<number> {\n const query = this.query();\n\n // Apply filter\n if (typeof filter === \"object\") {\n query.where(filter);\n }\n\n // Get matching records\n const records = await query.get();\n\n // Delete each record\n for (const record of records) {\n await record.destroy();\n }\n\n return records.length;\n }\n\n // ============================================================================\n // COUNTING\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.count}\n */\n public async count(filter?: any): Promise<number> {\n const query = this.query();\n\n if (filter && typeof filter === \"object\") {\n query.where(filter);\n }\n\n return query.count();\n }\n\n // ============================================================================\n // PAGINATION\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.paginate}\n */\n public async paginate(page: number, limit: number): Promise<PaginationResult<T>> {\n return this.query().paginate(page, limit);\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.cursorPaginate}\n */\n public async cursorPaginate(\n options: CursorPaginationOptions,\n ): Promise<CursorPaginationResult<T>> {\n return this.query().cursorPaginate(options);\n }\n\n // ============================================================================\n // CHUNKING\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.chunk}\n */\n public async chunk(size: number, callback: ChunkCallback<T>): Promise<void> {\n return this.query().chunk(size, callback);\n }\n\n // ============================================================================\n // MODEL CREATION\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.createModel}\n */\n public createModel(data: any): T {\n return new (this.model as any)(data) as T;\n }\n}\n"],"mappings":";;;;;;;;;;AAiBA,IAAa,iBAAb,MAA0F;;;;;CAKxF,AAAO,YAAY,AAAQ,OAAsB;EAAtB;CAAuB;;;;CASlD,AAAO,QAAiC;EACtC,OAAO,IAAI,oBAAuB,KAAK,MAAM,MAAM,CAAC;CACtD;;;;CAKA,AAAO,eAAe,gBAA4B;EAChD,MAAM,SAAgB,CAAC;EAEvB,MAAM,cAAc,KAAK,MAAM,OAAO;EAEtC,OAAO,KACL,YAAY,WAAW,UAAU;GAC/B,eAAe,KAAK;EACtB,CAAC,GACD,YAAY,WAAW,UAAU;GAC/B,eAAe,KAAK;EACtB,CAAC,GACD,YAAY,WAAW,UAAU;GAC/B,eAAe,KAAK;EACtB,CAAC,CACH;EAEA,OAAO;CACT;;;;CASA,MAAa,KAAK,IAA4B;EAC5C,OAAO,MAAM,KAAK,MAAM,KAAK,EAAE;CACjC;;;;CAKA,MAAa,OAAO,QAAgB,OAA+B;EACjE,OAAQ,MAAM,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"cascade-adapter.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/cascade-adapter.ts"],"sourcesContent":["import { type ChildModel, Model } from \"@warlock.js/cascade\";\nimport type {\n ChunkCallback,\n CursorPaginationOptions,\n CursorPaginationResult,\n PaginationResult,\n QueryBuilderContract,\n RepositoryAdapterContract,\n} from \"../../contracts\";\nimport { CascadeQueryBuilder } from \"./cascade-query-builder\";\n\n/**\n * Cascade adapter for Cascade-Next ORM\n * Implements RepositoryAdapterContract for @warlock.js/cascade\n *\n * @template T - The model instance type\n */\nexport class CascadeAdapter<T extends Model<any>> implements RepositoryAdapterContract<T> {\n /**\n * Constructor\n * @param model - Cascade-Next Model class\n */\n public constructor(private model: ChildModel<T>) {}\n\n // ============================================================================\n // QUERY BUILDING\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.query}\n */\n public query(): QueryBuilderContract<T> {\n return new CascadeQueryBuilder<T>(this.model.query());\n }\n\n /**\n * Register all events\n */\n public registerEvents(eventsCallback: any): any[] {\n const events: any[] = [];\n\n const modelEvents = this.model.events();\n\n events.push(\n modelEvents.onCreated((model) => {\n eventsCallback(model);\n }),\n modelEvents.onUpdated((model) => {\n eventsCallback(model);\n }),\n modelEvents.onDeleted((model) => {\n eventsCallback(model);\n }),\n );\n\n return events;\n }\n\n // ============================================================================\n // CRUD OPERATIONS\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.find}\n */\n public async find(id: any): Promise<T | null> {\n return await this.model.find(id);\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.findBy}\n */\n public async findBy(column: string, value: any): Promise<T | null> {\n return (await this.query().where(column, value).first()) as T | null;\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.serializeModel}\n */\n public serializeModel(model: T): any {\n return model.toSnapshot();\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.deserializeModel}\n */\n public deserializeModel(data: any): T {\n return this.model.fromSnapshot(data);\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.resolveRepositoryName}\n */\n public resolveRepositoryName(): string {\n return this.model.table;\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.create}\n */\n public async create(data: any): Promise<T> {\n return this.model.create(data);\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.update}\n */\n public async update(id: any, data: any): Promise<T> {\n const model = id instanceof Model ? id : await this.model.find(id);\n if (!model) {\n throw new Error(`Model not found with id ${id}`);\n }\n\n await model.save({ merge: data });\n return model as T;\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.delete}\n */\n public async delete(id: any): Promise<void> {\n await this.model.delete({ id });\n }\n\n // ============================================================================\n // BULK OPERATIONS\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.updateMany}\n */\n public async updateMany(filter: any, data: any): Promise<number> {\n const query = this.query();\n\n // Apply filter\n if (typeof filter === \"object\") {\n query.where(filter);\n }\n\n // Get matching records\n const records = await query.get();\n\n // Update each record\n for (const record of records) {\n await this.update(record.id, data);\n }\n\n return records.length;\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.deleteMany}\n */\n public async deleteMany(filter: any): Promise<number> {\n const query = this.query();\n\n // Apply filter\n if (typeof filter === \"object\") {\n query.where(filter);\n }\n\n // Get matching records\n const records = await query.get();\n\n // Delete each record\n for (const record of records) {\n await record.destroy();\n }\n\n return records.length;\n }\n\n // ============================================================================\n // COUNTING\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.count}\n */\n public async count(filter?: any): Promise<number> {\n const query = this.query();\n\n if (filter && typeof filter === \"object\") {\n query.where(filter);\n }\n\n return query.count();\n }\n\n // ============================================================================\n // PAGINATION\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.paginate}\n */\n public async paginate(page: number, limit: number): Promise<PaginationResult<T>> {\n return this.query().paginate(page, limit);\n }\n\n /**\n * {@inheritDoc RepositoryAdapterContract.cursorPaginate}\n */\n public async cursorPaginate(\n options: CursorPaginationOptions,\n ): Promise<CursorPaginationResult<T>> {\n return this.query().cursorPaginate(options);\n }\n\n // ============================================================================\n // CHUNKING\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.chunk}\n */\n public async chunk(size: number, callback: ChunkCallback<T>): Promise<void> {\n return this.query().chunk(size, callback);\n }\n\n // ============================================================================\n // MODEL CREATION\n // ============================================================================\n\n /**\n * {@inheritDoc RepositoryAdapterContract.createModel}\n */\n public createModel(data: any): T {\n return new (this.model as any)(data) as T;\n }\n}\n"],"mappings":";;;;;;;;;;AAiBA,IAAa,iBAAb,MAA0F;;;;;CAKxF,AAAO,YAAY,AAAQ,OAAsB;EAAtB;CAAuB;;;;CASlD,AAAO,QAAiC;EACtC,OAAO,IAAI,oBAAuB,KAAK,MAAM,MAAM,CAAC;CACtD;;;;CAKA,AAAO,eAAe,gBAA4B;EAChD,MAAM,SAAgB,CAAC;EAEvB,MAAM,cAAc,KAAK,MAAM,OAAO;EAEtC,OAAO,KACL,YAAY,WAAW,UAAU;GAC/B,eAAe,KAAK;EACtB,CAAC,GACD,YAAY,WAAW,UAAU;GAC/B,eAAe,KAAK;EACtB,CAAC,GACD,YAAY,WAAW,UAAU;GAC/B,eAAe,KAAK;EACtB,CAAC,CACH;EAEA,OAAO;CACT;;;;CASA,MAAa,KAAK,IAA4B;EAC5C,OAAO,MAAM,KAAK,MAAM,KAAK,EAAE;CACjC;;;;CAKA,MAAa,OAAO,QAAgB,OAA+B;EACjE,OAAQ,MAAM,KAAK,MAAM,EAAE,MAAM,QAAQ,KAAK,EAAE,MAAM;CACxD;;;;CAKA,AAAO,eAAe,OAAe;EACnC,OAAO,MAAM,WAAW;CAC1B;;;;CAKA,AAAO,iBAAiB,MAAc;EACpC,OAAO,KAAK,MAAM,aAAa,IAAI;CACrC;;;;CAKA,AAAO,wBAAgC;EACrC,OAAO,KAAK,MAAM;CACpB;;;;CAKA,MAAa,OAAO,MAAuB;EACzC,OAAO,KAAK,MAAM,OAAO,IAAI;CAC/B;;;;CAKA,MAAa,OAAO,IAAS,MAAuB;EAClD,MAAM,QAAQ,cAAc,QAAQ,KAAK,MAAM,KAAK,MAAM,KAAK,EAAE;EACjE,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,2BAA2B,IAAI;EAGjD,MAAM,MAAM,KAAK,EAAE,OAAO,KAAK,CAAC;EAChC,OAAO;CACT;;;;CAKA,MAAa,OAAO,IAAwB;EAC1C,MAAM,KAAK,MAAM,OAAO,EAAE,GAAG,CAAC;CAChC;;;;CASA,MAAa,WAAW,QAAa,MAA4B;EAC/D,MAAM,QAAQ,KAAK,MAAM;EAGzB,IAAI,OAAO,WAAW,UACpB,MAAM,MAAM,MAAM;EAIpB,MAAM,UAAU,MAAM,MAAM,IAAI;EAGhC,KAAK,MAAM,UAAU,SACnB,MAAM,KAAK,OAAO,OAAO,IAAI,IAAI;EAGnC,OAAO,QAAQ;CACjB;;;;CAKA,MAAa,WAAW,QAA8B;EACpD,MAAM,QAAQ,KAAK,MAAM;EAGzB,IAAI,OAAO,WAAW,UACpB,MAAM,MAAM,MAAM;EAIpB,MAAM,UAAU,MAAM,MAAM,IAAI;EAGhC,KAAK,MAAM,UAAU,SACnB,MAAM,OAAO,QAAQ;EAGvB,OAAO,QAAQ;CACjB;;;;CASA,MAAa,MAAM,QAA+B;EAChD,MAAM,QAAQ,KAAK,MAAM;EAEzB,IAAI,UAAU,OAAO,WAAW,UAC9B,MAAM,MAAM,MAAM;EAGpB,OAAO,MAAM,MAAM;CACrB;;;;CASA,MAAa,SAAS,MAAc,OAA6C;EAC/E,OAAO,KAAK,MAAM,EAAE,SAAS,MAAM,KAAK;CAC1C;;;;CAKA,MAAa,eACX,SACoC;EACpC,OAAO,KAAK,MAAM,EAAE,eAAe,OAAO;CAC5C;;;;CASA,MAAa,MAAM,MAAc,UAA2C;EAC1E,OAAO,KAAK,MAAM,EAAE,MAAM,MAAM,QAAQ;CAC1C;;;;CASA,AAAO,YAAY,MAAc;EAC/B,OAAO,IAAK,KAAK,MAAc,IAAI;CACrC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cascade-query-builder.d.mts","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/cascade-query-builder.ts"],"mappings":";;;;;;;;;AAwBA;;cAAa,mBAAA,WAA8B,KAAA,aAAkB,sBAAA,CAAqB,CAAA;EAAA,QAKrD,KAAA;EALqD;;;;cAKrD,KAAA,EAAO,oBAAA,CAA4B,CAAA;EA8CtB;;;EArCjC,KAAA,CAAM,KAAA,UAAe,KAAA;EACrB,KAAA,CAAM,KAAA,UAAe,QAAA,EAAU,aAAA,EAAe,KAAA;EAC9C,KAAA,CAAM,UAAA,EAAY,MAAA;EAClB,KAAA,CAAM,QAAA,GAAW,KAAA;EAiOM;;;EAvMvB,MAAA;EA4N0B;;;EArN1B,OAAA,CAAQ,KAAA,UAAe,KAAA;EACvB,OAAA,CAAQ,KAAA,UAAe,QAAA,EAAU,aAAA,EAAe,KAAA;EAChD,OAAA,CAAQ,UAAA,EAAY,MAAA;EA2OhB;;;EAvNJ,OAAA,CAAQ,KAAA,UAAe,MAAA;EAmOnB;;;EA3NJ,UAAA,CAAW,KAAA,UAAe,MAAA;EAmOmB;;;EA3N7C,SAAA,CAAU,KAAA;EA2Pd;;;EAnPI,YAAA,CAAa,KAAA;EAhGuC;;;EAwGpD,YAAA,CAAa,KAAA,UAAe,KAAA;EAxGM;;;EAgHlC,SAAA,CAAU,KAAA,UAAe,OAAA;;;;EAQzB,SAAA,CAAU,MAAA,UAAgB,SAAA,YAAqB,KAAA;EA1G/C;;;EAsHA,MAAA,CAAO,MAAA;EACP,MAAA,
|
|
1
|
+
{"version":3,"file":"cascade-query-builder.d.mts","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/cascade-query-builder.ts"],"mappings":";;;;;;;;;AAwBA;;cAAa,mBAAA,WAA8B,KAAA,aAAkB,sBAAA,CAAqB,CAAA;EAAA,QAKrD,KAAA;EALqD;;;;cAKrD,KAAA,EAAO,oBAAA,CAA4B,CAAA;EA8CtB;;;EArCjC,KAAA,CAAM,KAAA,UAAe,KAAA;EACrB,KAAA,CAAM,KAAA,UAAe,QAAA,EAAU,aAAA,EAAe,KAAA;EAC9C,KAAA,CAAM,UAAA,EAAY,MAAA;EAClB,KAAA,CAAM,QAAA,GAAW,KAAA;EAiOM;;;EAvMvB,MAAA,CAAA;EA4N0B;;;EArN1B,OAAA,CAAQ,KAAA,UAAe,KAAA;EACvB,OAAA,CAAQ,KAAA,UAAe,QAAA,EAAU,aAAA,EAAe,KAAA;EAChD,OAAA,CAAQ,UAAA,EAAY,MAAA;EA2OhB;;;EAvNJ,OAAA,CAAQ,KAAA,UAAe,MAAA;EAmOnB;;;EA3NJ,UAAA,CAAW,KAAA,UAAe,MAAA;EAmOmB;;;EA3N7C,SAAA,CAAU,KAAA;EA2Pd;;;EAnPI,YAAA,CAAa,KAAA;EAhGuC;;;EAwGpD,YAAA,CAAa,KAAA,UAAe,KAAA;EAxGM;;;EAgHlC,SAAA,CAAU,KAAA,UAAe,OAAA;;;;EAQzB,SAAA,CAAU,MAAA,UAAgB,SAAA,YAAqB,KAAA;EA1G/C;;;EAsHA,MAAA,CAAO,MAAA;EACP,MAAA,CAAA,GAAU,MAAA;EAtHqB;;;EAmI/B,QAAA,CAAS,MAAA;EACT,QAAA,CAAA,GAAY,MAAA;EAnIN;;;EAoJN,OAAA,CAAQ,KAAA,UAAe,SAAA;EAzHvB;;;EAiIA,MAAA,CAAO,OAAA,EAAS,MAAA;EAzHhB;;;EAoIA,MAAA,CAAO,KAAA;EApIyC;;;EAgJhD,KAAA,CAAM,KAAA;EA3HN;;;EAmIA,MAAA,CAAO,MAAA;EA3HI;;;EAmIX,IAAA,CAAK,KAAA;EAnHL;;;EA8HA,YAAA,CAAa,OAAA,EAAS,WAAA,QAAmB,IAAA,OAAW,OAAA,EAAS,aAAA;EAtHjC;;;EAmItB,GAAA,CAAA,GAAO,OAAA,CAAQ,CAAA;EAnHrB;;;EA0HM,KAAA,CAAA,GAAS,OAAA,CAAQ,CAAA;EA9GvB;;;EAqHM,KAAA,CAAA,GAAS,OAAA;EAvGf;;;EA8GM,GAAA,CAAI,KAAA,WAAgB,OAAA;EA5F1B;;;EAmGM,GAAA,CAAI,KAAA,WAAgB,OAAA;EA3FV;;;EAkGV,GAAA,CAAI,KAAA,WAAgB,OAAA;EA3E1B;;;EAkFM,GAAA,CAAI,KAAA,WAAgB,OAAA;EAlE1B;;;EAyEM,OAAA,KAAY,MAAA,kBAAA,CACvB,MAAA,EAAQ,aAAA,EACR,UAAA,EAAY,oBAAA,GACX,OAAA,CAAQ,CAAA;EAjES;;;;;;EA2EP,SAAA,KAAc,MAAA,kBAAA,CACzB,UAAA,EAAY,oBAAA,GACX,OAAA,CAAQ,CAAA;EAzDE;;;EAiEA,QAAA,CAAS,IAAA,UAAc,KAAA,WAAgB,OAAA,CAAQ,gBAAA,CAAiB,CAAA;EA1DvD;;;;;;;;;;;;;;;;;EAwFT,cAAA,CACX,OAAA,EAAS,uBAAA,GACR,OAAA,CAAQ,sBAAA,CAAuB,CAAA;EArDpB;;;EAiHD,KAAA,CAAM,IAAA,UAAc,QAAA,EAAU,aAAA,CAAc,CAAA,IAAK,OAAA;EAtGjD;;;EAiHN,IAAA,CAAK,QAAA;EAhHV;;;EAwHK,QAAA,CAAA,GAAY,SAAA;EA/GG;;;EAgIf,KAAA,CAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cascade-query-builder.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/cascade-query-builder.ts"],"sourcesContent":["import type {\n QueryBuilderContract as CascadeQueryBuilderContract,\n Model,\n} from \"@warlock.js/cascade\";\nimport type {\n AggregateExpressions,\n ChunkCallback,\n CursorPaginationOptions,\n CursorPaginationResult,\n FilterOptions,\n FilterRules,\n GroupByFields,\n PaginationResult,\n QueryBuilderContract,\n WhereOperator,\n} from \"../../contracts\";\nimport { FilterApplicator } from \"./filter-applicator\";\n\n/**\n * Cascade query builder wrapper\n * Wraps Cascade-Next's ModelAggregate to implement QueryBuilderContract\n *\n * @template T - The model instance type\n */\nexport class CascadeQueryBuilder<T extends Model> implements QueryBuilderContract<T> {\n /**\n * Constructor\n * @param query - Cascade-Next QueryBuilder instance\n */\n public constructor(private query: CascadeQueryBuilderContract<T>) {}\n\n // ============================================================================\n // WHERE CLAUSES\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.where}\n */\n public where(field: string, value: any): this;\n public where(field: string, operator: WhereOperator, value: any): this;\n public where(conditions: Record<string, any>): this;\n public where(callback: (query: this) => void): this;\n public where(\n fieldOrConditionsOrCallback: string | Record<string, any> | ((query: this) => void),\n operatorOrValue?: WhereOperator | any,\n value?: any,\n ): this {\n if (typeof fieldOrConditionsOrCallback === \"function\") {\n // Callback\n fieldOrConditionsOrCallback(this);\n } else if (typeof fieldOrConditionsOrCallback === \"object\") {\n // Object conditions\n this.query.where(fieldOrConditionsOrCallback);\n } else if (value !== undefined) {\n // Field, operator, value\n this.query.where(fieldOrConditionsOrCallback, operatorOrValue, value);\n } else {\n // Field, value\n this.query.where(fieldOrConditionsOrCallback, operatorOrValue);\n }\n\n return this;\n }\n\n /**\n * Pretty display the query in terminal\n */\n public pretty() {\n return this.query.pretty();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.orWhere}\n */\n public orWhere(field: string, value: any): this;\n public orWhere(field: string, operator: WhereOperator, value: any): this;\n public orWhere(conditions: Record<string, any>): this;\n public orWhere(\n fieldOrConditions: string | Record<string, any>,\n operatorOrValue?: WhereOperator | any,\n value?: any,\n ): this {\n if (typeof fieldOrConditions === \"object\") {\n this.query.orWhere(fieldOrConditions);\n } else if (value !== undefined) {\n this.query.orWhere(fieldOrConditions, operatorOrValue, value);\n } else {\n this.query.orWhere(fieldOrConditions, operatorOrValue);\n }\n\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereIn}\n */\n public whereIn(field: string, values: any[]): this {\n this.query.whereIn(field, values);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereNotIn}\n */\n public whereNotIn(field: string, values: any[]): this {\n this.query.whereNotIn(field, values);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereNull}\n */\n public whereNull(field: string): this {\n this.query.whereNull(field);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereNotNull}\n */\n public whereNotNull(field: string): this {\n this.query.whereNotNull(field);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereBetween}\n */\n public whereBetween(field: string, range: [any, any]): this {\n this.query.whereBetween(field, range);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereLike}\n */\n public whereLike(field: string, pattern: string): this {\n this.query.whereLike(field, pattern);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.similarTo}\n */\n public similarTo(column: string, embedding: number[], alias?: string): this {\n this.query.similarTo(column, embedding, alias);\n return this;\n }\n\n // ============================================================================\n // SELECT / PROJECTION\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.select}\n */\n public select(fields: string[]): this;\n public select(...fields: string[]): this;\n public select(...fields: any[]): this {\n if (Array.isArray(fields[0])) {\n this.query.select(fields[0]);\n } else {\n this.query.select(fields);\n }\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.deselect}\n */\n public deselect(fields: string[]): this;\n public deselect(...fields: string[]): this;\n public deselect(...fields: any[]): this {\n if (Array.isArray(fields[0])) {\n this.query.deselect(fields[0]);\n } else {\n this.query.deselect(fields);\n }\n return this;\n }\n\n // ============================================================================\n // ORDERING\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.orderBy}\n */\n public orderBy(field: string, direction: \"asc\" | \"desc\" = \"asc\"): this {\n this.query.orderBy(field, direction);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.sortBy}\n */\n public sortBy(orderBy: Record<string, \"asc\" | \"desc\">): this {\n // Apply each order clause individually\n for (const [field, direction] of Object.entries(orderBy)) {\n this.query.orderBy(field, direction);\n }\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.random}\n */\n public random(limit: number): this {\n this.query.orderByRandom(limit);\n return this;\n }\n\n // ============================================================================\n // LIMITING\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.limit}\n */\n public limit(limit: number): this {\n this.query.limit(limit);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.offset}\n */\n public offset(offset: number): this {\n this.query.offset(offset);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.skip}\n */\n public skip(count: number): this {\n return this.offset(count);\n }\n\n // ============================================================================\n // FILTERING (Repository-specific)\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.applyFilters}\n */\n public applyFilters(filters: FilterRules<this>, data: any, options: FilterOptions): this {\n const applicator = new FilterApplicator();\n applicator.apply(this.query as any, filters, data, options);\n return this;\n }\n\n // ============================================================================\n // EXECUTION\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.get}\n */\n public async get(): Promise<T[]> {\n return this.query.get();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.first}\n */\n public async first(): Promise<T | null> {\n return this.query.first();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.count}\n */\n public async count(): Promise<number> {\n return this.query.count();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.sum}\n */\n public async sum(field: string): Promise<number> {\n return this.query.sum(field);\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.avg}\n */\n public async avg(field: string): Promise<number> {\n return this.query.avg(field);\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.min}\n */\n public async min(field: string): Promise<number> {\n return this.query.min(field);\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.max}\n */\n public async max(field: string): Promise<number> {\n return this.query.max(field);\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.groupBy}\n */\n public async groupBy<R = Record<string, unknown>>(\n fields: GroupByFields,\n aggregates: AggregateExpressions,\n ): Promise<R[]> {\n return this.query.groupBy(fields, aggregates).get<R>();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.aggregate}\n *\n * Whole-set aggregation: groups with no group key so every matching record\n * falls into a single group, then returns that one summary row.\n */\n public async aggregate<R = Record<string, unknown>>(\n aggregates: AggregateExpressions,\n ): Promise<R | null> {\n const rows = await this.query.groupBy([], aggregates).get<R>();\n return rows[0] ?? null;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.paginate}\n */\n public async paginate(page: number, limit: number): Promise<PaginationResult<T>> {\n const result = await this.query.paginate({ limit, page });\n\n // Convert Cascade-Next pagination to repository format\n return {\n data: result.data,\n pagination: {\n ...result.pagination,\n result: result.data.length,\n },\n };\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.cursorPaginate}\n *\n * NOTE: This method is a pure executor.\n * The caller (e.g. RepositoryManager._listImpl) is responsible for applying\n * the cursor WHERE condition and ORDER BY BEFORE calling this method,\n * so that the cursor column is always the primary sort key.\n *\n * For a forward page (`direction: \"next\"`) the caller sorts ASC and filters\n * `> cursor`, so the rows already arrive in ascending cursor order.\n *\n * For a backward page (`direction: \"prev\"`) the caller sorts DESC and filters\n * `< cursor`, so rows arrive in DESCENDING order. We re-reverse the trimmed\n * page back to ascending so callers always receive a page in the same\n * (ascending) order regardless of direction, and we derive the next/prev\n * cursors from the page boundaries accordingly.\n */\n public async cursorPaginate(\n options: CursorPaginationOptions,\n ): Promise<CursorPaginationResult<T>> {\n const { limit, cursorColumn = \"id\", direction = \"next\" } = options;\n\n // Fetch limit + 1 to detect whether more pages exist in the scan direction.\n this.limit(limit + 1);\n\n const results = await this.get();\n const hasMore = results.length > limit;\n\n // Drop the extra record used for the hasMore check. It always sits at the\n // tail because the caller ordered the scan so the next-to-fetch row is last.\n const trimmed = hasMore ? results.slice(0, limit) : results;\n\n const cursorOf = (record: T | undefined): string | number | undefined =>\n record === undefined ? undefined : ((record as any)[cursorColumn] as string | number);\n\n if (direction === \"prev\") {\n // Scan came back DESC; present it ascending.\n const documents = [...trimmed].reverse();\n const first = documents[0];\n const last = documents[documents.length - 1];\n\n return {\n data: documents,\n pagination: {\n limit,\n result: documents.length,\n hasMore,\n // Going further back is only possible when the backward scan saw more\n // rows than this page; the boundary is this page's first (smallest) row.\n prevCursor: hasMore ? cursorOf(first) : undefined,\n // Going forward always resumes after this page's last (largest) row.\n nextCursor: cursorOf(last),\n },\n };\n }\n\n // Forward page — rows already ascending.\n const documents = trimmed;\n const first = documents[0];\n const last = documents[documents.length - 1];\n\n return {\n data: documents,\n pagination: {\n limit,\n result: documents.length,\n hasMore,\n // Going forward is only possible when the forward scan saw more rows\n // than this page; the boundary is this page's last (largest) row.\n nextCursor: hasMore ? cursorOf(last) : undefined,\n // Going back resumes before this page's first (smallest) row.\n prevCursor: cursorOf(first),\n },\n };\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.chunk}\n */\n public async chunk(size: number, callback: ChunkCallback<T>): Promise<void> {\n return this.query.chunk(size, callback);\n }\n\n // ============================================================================\n // UTILITIES\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.with}\n */\n public with(relation: string): this {\n this.query.with(relation);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.joinWith}\n */\n public joinWith(...relations: string[]): this {\n if (this.query.joinWith) {\n this.query.joinWith(...relations);\n } else {\n console.warn(\n \"[Repository] joinWith is not supported by the underlying query builder. Falling back to with.\",\n );\n for (const relation of relations) {\n this.query.with(relation);\n }\n }\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.clone}\n */\n public clone(): this {\n return new CascadeQueryBuilder(this.query.clone()) as this;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cascade-query-builder.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/cascade-query-builder.ts"],"sourcesContent":["import type {\n QueryBuilderContract as CascadeQueryBuilderContract,\n Model,\n} from \"@warlock.js/cascade\";\nimport type {\n AggregateExpressions,\n ChunkCallback,\n CursorPaginationOptions,\n CursorPaginationResult,\n FilterOptions,\n FilterRules,\n GroupByFields,\n PaginationResult,\n QueryBuilderContract,\n WhereOperator,\n} from \"../../contracts\";\nimport { FilterApplicator } from \"./filter-applicator\";\n\n/**\n * Cascade query builder wrapper\n * Wraps Cascade-Next's ModelAggregate to implement QueryBuilderContract\n *\n * @template T - The model instance type\n */\nexport class CascadeQueryBuilder<T extends Model> implements QueryBuilderContract<T> {\n /**\n * Constructor\n * @param query - Cascade-Next QueryBuilder instance\n */\n public constructor(private query: CascadeQueryBuilderContract<T>) {}\n\n // ============================================================================\n // WHERE CLAUSES\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.where}\n */\n public where(field: string, value: any): this;\n public where(field: string, operator: WhereOperator, value: any): this;\n public where(conditions: Record<string, any>): this;\n public where(callback: (query: this) => void): this;\n public where(\n fieldOrConditionsOrCallback: string | Record<string, any> | ((query: this) => void),\n operatorOrValue?: WhereOperator | any,\n value?: any,\n ): this {\n if (typeof fieldOrConditionsOrCallback === \"function\") {\n // Callback\n fieldOrConditionsOrCallback(this);\n } else if (typeof fieldOrConditionsOrCallback === \"object\") {\n // Object conditions\n this.query.where(fieldOrConditionsOrCallback);\n } else if (value !== undefined) {\n // Field, operator, value\n this.query.where(fieldOrConditionsOrCallback, operatorOrValue, value);\n } else {\n // Field, value\n this.query.where(fieldOrConditionsOrCallback, operatorOrValue);\n }\n\n return this;\n }\n\n /**\n * Pretty display the query in terminal\n */\n public pretty() {\n return this.query.pretty();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.orWhere}\n */\n public orWhere(field: string, value: any): this;\n public orWhere(field: string, operator: WhereOperator, value: any): this;\n public orWhere(conditions: Record<string, any>): this;\n public orWhere(\n fieldOrConditions: string | Record<string, any>,\n operatorOrValue?: WhereOperator | any,\n value?: any,\n ): this {\n if (typeof fieldOrConditions === \"object\") {\n this.query.orWhere(fieldOrConditions);\n } else if (value !== undefined) {\n this.query.orWhere(fieldOrConditions, operatorOrValue, value);\n } else {\n this.query.orWhere(fieldOrConditions, operatorOrValue);\n }\n\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereIn}\n */\n public whereIn(field: string, values: any[]): this {\n this.query.whereIn(field, values);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereNotIn}\n */\n public whereNotIn(field: string, values: any[]): this {\n this.query.whereNotIn(field, values);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereNull}\n */\n public whereNull(field: string): this {\n this.query.whereNull(field);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereNotNull}\n */\n public whereNotNull(field: string): this {\n this.query.whereNotNull(field);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereBetween}\n */\n public whereBetween(field: string, range: [any, any]): this {\n this.query.whereBetween(field, range);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.whereLike}\n */\n public whereLike(field: string, pattern: string): this {\n this.query.whereLike(field, pattern);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.similarTo}\n */\n public similarTo(column: string, embedding: number[], alias?: string): this {\n this.query.similarTo(column, embedding, alias);\n return this;\n }\n\n // ============================================================================\n // SELECT / PROJECTION\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.select}\n */\n public select(fields: string[]): this;\n public select(...fields: string[]): this;\n public select(...fields: any[]): this {\n if (Array.isArray(fields[0])) {\n this.query.select(fields[0]);\n } else {\n this.query.select(fields);\n }\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.deselect}\n */\n public deselect(fields: string[]): this;\n public deselect(...fields: string[]): this;\n public deselect(...fields: any[]): this {\n if (Array.isArray(fields[0])) {\n this.query.deselect(fields[0]);\n } else {\n this.query.deselect(fields);\n }\n return this;\n }\n\n // ============================================================================\n // ORDERING\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.orderBy}\n */\n public orderBy(field: string, direction: \"asc\" | \"desc\" = \"asc\"): this {\n this.query.orderBy(field, direction);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.sortBy}\n */\n public sortBy(orderBy: Record<string, \"asc\" | \"desc\">): this {\n // Apply each order clause individually\n for (const [field, direction] of Object.entries(orderBy)) {\n this.query.orderBy(field, direction);\n }\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.random}\n */\n public random(limit: number): this {\n this.query.orderByRandom(limit);\n return this;\n }\n\n // ============================================================================\n // LIMITING\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.limit}\n */\n public limit(limit: number): this {\n this.query.limit(limit);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.offset}\n */\n public offset(offset: number): this {\n this.query.offset(offset);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.skip}\n */\n public skip(count: number): this {\n return this.offset(count);\n }\n\n // ============================================================================\n // FILTERING (Repository-specific)\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.applyFilters}\n */\n public applyFilters(filters: FilterRules<this>, data: any, options: FilterOptions): this {\n const applicator = new FilterApplicator();\n applicator.apply(this.query as any, filters, data, options);\n return this;\n }\n\n // ============================================================================\n // EXECUTION\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.get}\n */\n public async get(): Promise<T[]> {\n return this.query.get();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.first}\n */\n public async first(): Promise<T | null> {\n return this.query.first();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.count}\n */\n public async count(): Promise<number> {\n return this.query.count();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.sum}\n */\n public async sum(field: string): Promise<number> {\n return this.query.sum(field);\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.avg}\n */\n public async avg(field: string): Promise<number> {\n return this.query.avg(field);\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.min}\n */\n public async min(field: string): Promise<number> {\n return this.query.min(field);\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.max}\n */\n public async max(field: string): Promise<number> {\n return this.query.max(field);\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.groupBy}\n */\n public async groupBy<R = Record<string, unknown>>(\n fields: GroupByFields,\n aggregates: AggregateExpressions,\n ): Promise<R[]> {\n return this.query.groupBy(fields, aggregates).get<R>();\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.aggregate}\n *\n * Whole-set aggregation: groups with no group key so every matching record\n * falls into a single group, then returns that one summary row.\n */\n public async aggregate<R = Record<string, unknown>>(\n aggregates: AggregateExpressions,\n ): Promise<R | null> {\n const rows = await this.query.groupBy([], aggregates).get<R>();\n return rows[0] ?? null;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.paginate}\n */\n public async paginate(page: number, limit: number): Promise<PaginationResult<T>> {\n const result = await this.query.paginate({ limit, page });\n\n // Convert Cascade-Next pagination to repository format\n return {\n data: result.data,\n pagination: {\n ...result.pagination,\n result: result.data.length,\n },\n };\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.cursorPaginate}\n *\n * NOTE: This method is a pure executor.\n * The caller (e.g. RepositoryManager._listImpl) is responsible for applying\n * the cursor WHERE condition and ORDER BY BEFORE calling this method,\n * so that the cursor column is always the primary sort key.\n *\n * For a forward page (`direction: \"next\"`) the caller sorts ASC and filters\n * `> cursor`, so the rows already arrive in ascending cursor order.\n *\n * For a backward page (`direction: \"prev\"`) the caller sorts DESC and filters\n * `< cursor`, so rows arrive in DESCENDING order. We re-reverse the trimmed\n * page back to ascending so callers always receive a page in the same\n * (ascending) order regardless of direction, and we derive the next/prev\n * cursors from the page boundaries accordingly.\n */\n public async cursorPaginate(\n options: CursorPaginationOptions,\n ): Promise<CursorPaginationResult<T>> {\n const { limit, cursorColumn = \"id\", direction = \"next\" } = options;\n\n // Fetch limit + 1 to detect whether more pages exist in the scan direction.\n this.limit(limit + 1);\n\n const results = await this.get();\n const hasMore = results.length > limit;\n\n // Drop the extra record used for the hasMore check. It always sits at the\n // tail because the caller ordered the scan so the next-to-fetch row is last.\n const trimmed = hasMore ? results.slice(0, limit) : results;\n\n const cursorOf = (record: T | undefined): string | number | undefined =>\n record === undefined ? undefined : ((record as any)[cursorColumn] as string | number);\n\n if (direction === \"prev\") {\n // Scan came back DESC; present it ascending.\n const documents = [...trimmed].reverse();\n const first = documents[0];\n const last = documents[documents.length - 1];\n\n return {\n data: documents,\n pagination: {\n limit,\n result: documents.length,\n hasMore,\n // Going further back is only possible when the backward scan saw more\n // rows than this page; the boundary is this page's first (smallest) row.\n prevCursor: hasMore ? cursorOf(first) : undefined,\n // Going forward always resumes after this page's last (largest) row.\n nextCursor: cursorOf(last),\n },\n };\n }\n\n // Forward page — rows already ascending.\n const documents = trimmed;\n const first = documents[0];\n const last = documents[documents.length - 1];\n\n return {\n data: documents,\n pagination: {\n limit,\n result: documents.length,\n hasMore,\n // Going forward is only possible when the forward scan saw more rows\n // than this page; the boundary is this page's last (largest) row.\n nextCursor: hasMore ? cursorOf(last) : undefined,\n // Going back resumes before this page's first (smallest) row.\n prevCursor: cursorOf(first),\n },\n };\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.chunk}\n */\n public async chunk(size: number, callback: ChunkCallback<T>): Promise<void> {\n return this.query.chunk(size, callback);\n }\n\n // ============================================================================\n // UTILITIES\n // ============================================================================\n\n /**\n * {@inheritDoc QueryBuilderContract.with}\n */\n public with(relation: string): this {\n this.query.with(relation);\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.joinWith}\n */\n public joinWith(...relations: string[]): this {\n if (this.query.joinWith) {\n this.query.joinWith(...relations);\n } else {\n console.warn(\n \"[Repository] joinWith is not supported by the underlying query builder. Falling back to with.\",\n );\n for (const relation of relations) {\n this.query.with(relation);\n }\n }\n return this;\n }\n\n /**\n * {@inheritDoc QueryBuilderContract.clone}\n */\n public clone(): this {\n return new CascadeQueryBuilder(this.query.clone()) as this;\n }\n}\n"],"mappings":"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"}
|
|
@@ -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 for (const key in filters) {\n const value = data[key];\n if (value === undefined) continue;\n\n const rule = this.parseFilterRule(key, filters[key]);\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 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 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 for (const key in filters) {\n const value = data[key];\n if (value === undefined) continue;\n\n const rule = this.parseFilterRule(key, filters[key]);\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 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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"}
|