@warlock.js/cascade 4.0.48 → 4.0.59
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/cjs/context/database-data-source-context.d.ts +29 -0
- package/cjs/context/database-data-source-context.d.ts.map +1 -0
- package/cjs/context/database-data-source-context.js +28 -0
- package/cjs/context/database-data-source-context.js.map +1 -0
- package/cjs/context/database-transaction-context.d.ts +31 -0
- package/cjs/context/database-transaction-context.d.ts.map +1 -0
- package/cjs/context/database-transaction-context.js +34 -0
- package/cjs/context/database-transaction-context.js.map +1 -0
- package/cjs/contracts/database-driver.contract.d.ts +143 -0
- package/cjs/contracts/database-driver.contract.d.ts.map +1 -0
- package/cjs/contracts/database-id-generator.contract.d.ts +109 -0
- package/cjs/contracts/database-id-generator.contract.d.ts.map +1 -0
- package/cjs/contracts/database-remover.contract.d.ts +104 -0
- package/cjs/contracts/database-remover.contract.d.ts.map +1 -0
- package/cjs/contracts/database-restorer.contract.d.ts +143 -0
- package/cjs/contracts/database-restorer.contract.d.ts.map +1 -0
- package/cjs/contracts/database-writer.contract.d.ts +119 -0
- package/cjs/contracts/database-writer.contract.d.ts.map +1 -0
- package/cjs/contracts/driver-blueprint.contract.d.ts +45 -0
- package/cjs/contracts/driver-blueprint.contract.d.ts.map +1 -0
- package/cjs/contracts/index.d.ts +10 -0
- package/cjs/contracts/index.d.ts.map +1 -0
- package/cjs/contracts/migration-driver.contract.d.ts +365 -0
- package/cjs/contracts/migration-driver.contract.d.ts.map +1 -0
- package/cjs/contracts/query-builder.contract.d.ts +1128 -0
- package/cjs/contracts/query-builder.contract.d.ts.map +1 -0
- package/cjs/contracts/sync-adapter.contract.d.ts +58 -0
- package/cjs/contracts/sync-adapter.contract.d.ts.map +1 -0
- package/cjs/data-source/data-source-registry.d.ts +104 -0
- package/cjs/data-source/data-source-registry.d.ts.map +1 -0
- package/cjs/data-source/data-source-registry.js +138 -0
- package/cjs/data-source/data-source-registry.js.map +1 -0
- package/cjs/data-source/data-source.d.ts +106 -0
- package/cjs/data-source/data-source.d.ts.map +1 -0
- package/cjs/data-source/data-source.js +77 -0
- package/cjs/data-source/data-source.js.map +1 -0
- package/cjs/database-dirty-tracker.d.ts +253 -0
- package/cjs/database-dirty-tracker.d.ts.map +1 -0
- package/cjs/database-dirty-tracker.js +389 -0
- package/cjs/database-dirty-tracker.js.map +1 -0
- package/cjs/drivers/mongo/mongo-id-generator.d.ts +116 -0
- package/cjs/drivers/mongo/mongo-id-generator.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-id-generator.js +149 -0
- package/cjs/drivers/mongo/mongo-id-generator.js.map +1 -0
- package/cjs/drivers/mongo/mongo-migration-driver.d.ts +228 -0
- package/cjs/drivers/mongo/mongo-migration-driver.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-migration-driver.js +524 -0
- package/cjs/drivers/mongo/mongo-migration-driver.js.map +1 -0
- package/cjs/drivers/mongo/mongo-query-builder.d.ts +922 -0
- package/cjs/drivers/mongo/mongo-query-builder.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-query-builder.js +1740 -0
- package/cjs/drivers/mongo/mongo-query-builder.js.map +1 -0
- package/cjs/drivers/mongo/mongo-query-operations.d.ts +226 -0
- package/cjs/drivers/mongo/mongo-query-operations.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-query-operations.js +270 -0
- package/cjs/drivers/mongo/mongo-query-operations.js.map +1 -0
- package/cjs/drivers/mongo/mongo-query-parser.d.ts +262 -0
- package/cjs/drivers/mongo/mongo-query-parser.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-query-parser.js +1351 -0
- package/cjs/drivers/mongo/mongo-query-parser.js.map +1 -0
- package/cjs/drivers/mongo/mongo-sync-adapter.d.ts +79 -0
- package/cjs/drivers/mongo/mongo-sync-adapter.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-sync-adapter.js +146 -0
- package/cjs/drivers/mongo/mongo-sync-adapter.js.map +1 -0
- package/cjs/drivers/mongo/mongodb-blueprint.d.ts +30 -0
- package/cjs/drivers/mongo/mongodb-blueprint.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongodb-blueprint.js +51 -0
- package/cjs/drivers/mongo/mongodb-blueprint.js.map +1 -0
- package/cjs/drivers/mongo/mongodb-driver.d.ts +188 -0
- package/cjs/drivers/mongo/mongodb-driver.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongodb-driver.js +411 -0
- package/cjs/drivers/mongo/mongodb-driver.js.map +1 -0
- package/cjs/drivers/mongo/types.d.ts +43 -0
- package/cjs/drivers/mongo/types.d.ts.map +1 -0
- package/cjs/errors/missing-data-source.error.d.ts +22 -0
- package/cjs/errors/missing-data-source.error.d.ts.map +1 -0
- package/cjs/errors/missing-data-source.error.js +29 -0
- package/cjs/errors/missing-data-source.error.js.map +1 -0
- package/cjs/events/model-events.d.ts +231 -0
- package/cjs/events/model-events.d.ts.map +1 -0
- package/cjs/events/model-events.js +259 -0
- package/cjs/events/model-events.js.map +1 -0
- package/cjs/expressions/aggregate-expressions.d.ts +215 -0
- package/cjs/expressions/aggregate-expressions.d.ts.map +1 -0
- package/cjs/expressions/aggregate-expressions.js +221 -0
- package/cjs/expressions/aggregate-expressions.js.map +1 -0
- package/cjs/expressions/index.d.ts +2 -0
- package/cjs/expressions/index.d.ts.map +1 -0
- package/cjs/index.d.ts +41 -0
- package/cjs/index.d.ts.map +1 -0
- package/cjs/index.js +1 -267
- package/cjs/index.js.map +1 -1
- package/cjs/migration/column-builder.d.ts +167 -0
- package/cjs/migration/column-builder.d.ts.map +1 -0
- package/cjs/migration/column-builder.js +217 -0
- package/cjs/migration/column-builder.js.map +1 -0
- package/cjs/migration/foreign-key-builder.d.ts +110 -0
- package/cjs/migration/foreign-key-builder.d.ts.map +1 -0
- package/cjs/migration/foreign-key-builder.js +129 -0
- package/cjs/migration/foreign-key-builder.js.map +1 -0
- package/cjs/migration/index.d.ts +6 -0
- package/cjs/migration/index.d.ts.map +1 -0
- package/cjs/migration/migration-runner.d.ts +231 -0
- package/cjs/migration/migration-runner.d.ts.map +1 -0
- package/cjs/migration/migration-runner.js +443 -0
- package/cjs/migration/migration-runner.js.map +1 -0
- package/cjs/migration/migration.js +1346 -0
- package/cjs/migration/migration.js.map +1 -0
- package/cjs/migration/types.d.ts +132 -0
- package/cjs/migration/types.d.ts.map +1 -0
- package/cjs/model/model.d.ts +1267 -0
- package/cjs/model/model.d.ts.map +1 -0
- package/cjs/model/model.js +1463 -0
- package/cjs/model/model.js.map +1 -0
- package/cjs/model/register-model.d.ts +80 -0
- package/cjs/model/register-model.d.ts.map +1 -0
- package/cjs/model/register-model.js +91 -0
- package/cjs/model/register-model.js.map +1 -0
- package/cjs/remover/database-remover.d.ts +100 -0
- package/cjs/remover/database-remover.d.ts.map +1 -0
- package/cjs/remover/database-remover.js +209 -0
- package/cjs/remover/database-remover.js.map +1 -0
- package/cjs/restorer/database-restorer.d.ts +131 -0
- package/cjs/restorer/database-restorer.d.ts.map +1 -0
- package/cjs/restorer/database-restorer.js +425 -0
- package/cjs/restorer/database-restorer.js.map +1 -0
- package/cjs/sync/index.d.ts +12 -0
- package/cjs/sync/index.d.ts.map +1 -0
- package/cjs/sync/model-events.d.ts +62 -0
- package/cjs/sync/model-events.d.ts.map +1 -0
- package/cjs/sync/model-events.js +49 -0
- package/cjs/sync/model-events.js.map +1 -0
- package/cjs/sync/model-sync-operation.d.ts +163 -0
- package/cjs/sync/model-sync-operation.d.ts.map +1 -0
- package/cjs/sync/model-sync-operation.js +292 -0
- package/cjs/sync/model-sync-operation.js.map +1 -0
- package/cjs/sync/model-sync.d.ts +130 -0
- package/cjs/sync/model-sync.d.ts.map +1 -0
- package/cjs/sync/model-sync.js +178 -0
- package/cjs/sync/model-sync.js.map +1 -0
- package/cjs/sync/sync-context.d.ts +70 -0
- package/cjs/sync/sync-context.d.ts.map +1 -0
- package/cjs/sync/sync-context.js +101 -0
- package/cjs/sync/sync-context.js.map +1 -0
- package/cjs/sync/sync-manager.d.ts +213 -0
- package/cjs/sync/sync-manager.d.ts.map +1 -0
- package/cjs/sync/sync-manager.js +689 -0
- package/cjs/sync/sync-manager.js.map +1 -0
- package/cjs/sync/types.d.ts +289 -0
- package/cjs/sync/types.d.ts.map +1 -0
- package/cjs/types.d.ts +45 -0
- package/cjs/types.d.ts.map +1 -0
- package/cjs/utils/connect-to-database.d.ts +246 -0
- package/cjs/utils/connect-to-database.d.ts.map +1 -0
- package/cjs/utils/connect-to-database.js +92 -0
- package/cjs/utils/connect-to-database.js.map +1 -0
- package/cjs/utils/database-writer.utils.d.ts +15 -0
- package/cjs/utils/database-writer.utils.d.ts.map +1 -0
- package/cjs/utils/database-writer.utils.js +14 -0
- package/cjs/utils/database-writer.utils.js.map +1 -0
- package/cjs/utils/define-model.js +100 -0
- package/cjs/utils/define-model.js.map +1 -0
- package/cjs/utils/once-connected.d.ts +146 -0
- package/cjs/utils/once-connected.d.ts.map +1 -0
- package/cjs/utils/once-connected.js +252 -0
- package/cjs/utils/once-connected.js.map +1 -0
- package/cjs/validation/database-seal-plugins.d.ts +2 -0
- package/cjs/validation/database-seal-plugins.d.ts.map +1 -0
- package/cjs/validation/database-seal-plugins.js +4 -0
- package/cjs/validation/database-seal-plugins.js.map +1 -0
- package/cjs/validation/database-writer-validation-error.d.ts +97 -0
- package/cjs/validation/database-writer-validation-error.d.ts.map +1 -0
- package/cjs/validation/database-writer-validation-error.js +160 -0
- package/cjs/validation/database-writer-validation-error.js.map +1 -0
- package/cjs/validation/index.d.ts +3 -0
- package/cjs/validation/index.d.ts.map +1 -0
- package/cjs/validation/mutators/embed-mutator.d.ts +9 -0
- package/cjs/validation/mutators/embed-mutator.d.ts.map +1 -0
- package/cjs/validation/mutators/embed-mutator.js +33 -0
- package/cjs/validation/mutators/embed-mutator.js.map +1 -0
- package/cjs/validation/plugins/embed-validator-plugin.d.ts +24 -0
- package/cjs/validation/plugins/embed-validator-plugin.d.ts.map +1 -0
- package/cjs/validation/plugins/embed-validator-plugin.js +18 -0
- package/cjs/validation/plugins/embed-validator-plugin.js.map +1 -0
- package/cjs/validation/rules/database-model-rule.d.ts +7 -0
- package/cjs/validation/rules/database-model-rule.d.ts.map +1 -0
- package/cjs/validation/rules/database-model-rule.js +27 -0
- package/cjs/validation/rules/database-model-rule.js.map +1 -0
- package/cjs/validation/transformers/embed-model-transformer.d.ts +3 -0
- package/cjs/validation/transformers/embed-model-transformer.d.ts.map +1 -0
- package/cjs/validation/transformers/embed-model-transformer.js +18 -0
- package/cjs/validation/transformers/embed-model-transformer.js.map +1 -0
- package/cjs/validation/validators/embed-validator.d.ts +21 -0
- package/cjs/validation/validators/embed-validator.d.ts.map +1 -0
- package/cjs/validation/validators/embed-validator.js +42 -0
- package/cjs/validation/validators/embed-validator.js.map +1 -0
- package/cjs/writer/database-writer.d.ts +181 -0
- package/cjs/writer/database-writer.d.ts.map +1 -0
- package/cjs/writer/database-writer.js +402 -0
- package/cjs/writer/database-writer.js.map +1 -0
- package/esm/context/database-data-source-context.d.ts +29 -0
- package/esm/context/database-data-source-context.d.ts.map +1 -0
- package/esm/context/database-data-source-context.js +28 -0
- package/esm/context/database-data-source-context.js.map +1 -0
- package/esm/context/database-transaction-context.d.ts +31 -0
- package/esm/context/database-transaction-context.d.ts.map +1 -0
- package/esm/context/database-transaction-context.js +34 -0
- package/esm/context/database-transaction-context.js.map +1 -0
- package/esm/contracts/database-driver.contract.d.ts +143 -0
- package/esm/contracts/database-driver.contract.d.ts.map +1 -0
- package/esm/contracts/database-id-generator.contract.d.ts +109 -0
- package/esm/contracts/database-id-generator.contract.d.ts.map +1 -0
- package/esm/contracts/database-remover.contract.d.ts +104 -0
- package/esm/contracts/database-remover.contract.d.ts.map +1 -0
- package/esm/contracts/database-restorer.contract.d.ts +143 -0
- package/esm/contracts/database-restorer.contract.d.ts.map +1 -0
- package/esm/contracts/database-writer.contract.d.ts +119 -0
- package/esm/contracts/database-writer.contract.d.ts.map +1 -0
- package/esm/contracts/driver-blueprint.contract.d.ts +45 -0
- package/esm/contracts/driver-blueprint.contract.d.ts.map +1 -0
- package/esm/contracts/index.d.ts +10 -0
- package/esm/contracts/index.d.ts.map +1 -0
- package/esm/contracts/migration-driver.contract.d.ts +365 -0
- package/esm/contracts/migration-driver.contract.d.ts.map +1 -0
- package/esm/contracts/query-builder.contract.d.ts +1128 -0
- package/esm/contracts/query-builder.contract.d.ts.map +1 -0
- package/esm/contracts/sync-adapter.contract.d.ts +58 -0
- package/esm/contracts/sync-adapter.contract.d.ts.map +1 -0
- package/esm/data-source/data-source-registry.d.ts +104 -0
- package/esm/data-source/data-source-registry.d.ts.map +1 -0
- package/esm/data-source/data-source-registry.js +138 -0
- package/esm/data-source/data-source-registry.js.map +1 -0
- package/esm/data-source/data-source.d.ts +106 -0
- package/esm/data-source/data-source.d.ts.map +1 -0
- package/esm/data-source/data-source.js +77 -0
- package/esm/data-source/data-source.js.map +1 -0
- package/esm/database-dirty-tracker.d.ts +253 -0
- package/esm/database-dirty-tracker.d.ts.map +1 -0
- package/esm/database-dirty-tracker.js +389 -0
- package/esm/database-dirty-tracker.js.map +1 -0
- package/esm/drivers/mongo/mongo-id-generator.d.ts +116 -0
- package/esm/drivers/mongo/mongo-id-generator.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-id-generator.js +149 -0
- package/esm/drivers/mongo/mongo-id-generator.js.map +1 -0
- package/esm/drivers/mongo/mongo-migration-driver.d.ts +228 -0
- package/esm/drivers/mongo/mongo-migration-driver.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-migration-driver.js +524 -0
- package/esm/drivers/mongo/mongo-migration-driver.js.map +1 -0
- package/esm/drivers/mongo/mongo-query-builder.d.ts +922 -0
- package/esm/drivers/mongo/mongo-query-builder.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-query-builder.js +1740 -0
- package/esm/drivers/mongo/mongo-query-builder.js.map +1 -0
- package/esm/drivers/mongo/mongo-query-operations.d.ts +226 -0
- package/esm/drivers/mongo/mongo-query-operations.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-query-operations.js +270 -0
- package/esm/drivers/mongo/mongo-query-operations.js.map +1 -0
- package/esm/drivers/mongo/mongo-query-parser.d.ts +262 -0
- package/esm/drivers/mongo/mongo-query-parser.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-query-parser.js +1351 -0
- package/esm/drivers/mongo/mongo-query-parser.js.map +1 -0
- package/esm/drivers/mongo/mongo-sync-adapter.d.ts +79 -0
- package/esm/drivers/mongo/mongo-sync-adapter.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-sync-adapter.js +146 -0
- package/esm/drivers/mongo/mongo-sync-adapter.js.map +1 -0
- package/esm/drivers/mongo/mongodb-blueprint.d.ts +30 -0
- package/esm/drivers/mongo/mongodb-blueprint.d.ts.map +1 -0
- package/esm/drivers/mongo/mongodb-blueprint.js +51 -0
- package/esm/drivers/mongo/mongodb-blueprint.js.map +1 -0
- package/esm/drivers/mongo/mongodb-driver.d.ts +188 -0
- package/esm/drivers/mongo/mongodb-driver.d.ts.map +1 -0
- package/esm/drivers/mongo/mongodb-driver.js +411 -0
- package/esm/drivers/mongo/mongodb-driver.js.map +1 -0
- package/esm/drivers/mongo/types.d.ts +43 -0
- package/esm/drivers/mongo/types.d.ts.map +1 -0
- package/esm/errors/missing-data-source.error.d.ts +22 -0
- package/esm/errors/missing-data-source.error.d.ts.map +1 -0
- package/esm/errors/missing-data-source.error.js +29 -0
- package/esm/errors/missing-data-source.error.js.map +1 -0
- package/esm/events/model-events.d.ts +231 -0
- package/esm/events/model-events.d.ts.map +1 -0
- package/esm/events/model-events.js +259 -0
- package/esm/events/model-events.js.map +1 -0
- package/esm/expressions/aggregate-expressions.d.ts +215 -0
- package/esm/expressions/aggregate-expressions.d.ts.map +1 -0
- package/esm/expressions/aggregate-expressions.js +221 -0
- package/esm/expressions/aggregate-expressions.js.map +1 -0
- package/esm/expressions/index.d.ts +2 -0
- package/esm/expressions/index.d.ts.map +1 -0
- package/esm/index.d.ts +41 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +1 -40
- package/esm/index.js.map +1 -1
- package/esm/migration/column-builder.d.ts +167 -0
- package/esm/migration/column-builder.d.ts.map +1 -0
- package/esm/migration/column-builder.js +217 -0
- package/esm/migration/column-builder.js.map +1 -0
- package/esm/migration/foreign-key-builder.d.ts +110 -0
- package/esm/migration/foreign-key-builder.d.ts.map +1 -0
- package/esm/migration/foreign-key-builder.js +129 -0
- package/esm/migration/foreign-key-builder.js.map +1 -0
- package/esm/migration/index.d.ts +6 -0
- package/esm/migration/index.d.ts.map +1 -0
- package/esm/migration/migration-runner.d.ts +231 -0
- package/esm/migration/migration-runner.d.ts.map +1 -0
- package/esm/migration/migration-runner.js +443 -0
- package/esm/migration/migration-runner.js.map +1 -0
- package/esm/migration/migration.js +1346 -0
- package/esm/migration/migration.js.map +1 -0
- package/esm/migration/types.d.ts +132 -0
- package/esm/migration/types.d.ts.map +1 -0
- package/esm/model/model.d.ts +1267 -0
- package/esm/model/model.d.ts.map +1 -0
- package/esm/model/model.js +1463 -0
- package/esm/model/model.js.map +1 -0
- package/esm/model/register-model.d.ts +80 -0
- package/esm/model/register-model.d.ts.map +1 -0
- package/esm/model/register-model.js +91 -0
- package/esm/model/register-model.js.map +1 -0
- package/esm/remover/database-remover.d.ts +100 -0
- package/esm/remover/database-remover.d.ts.map +1 -0
- package/esm/remover/database-remover.js +209 -0
- package/esm/remover/database-remover.js.map +1 -0
- package/esm/restorer/database-restorer.d.ts +131 -0
- package/esm/restorer/database-restorer.d.ts.map +1 -0
- package/esm/restorer/database-restorer.js +425 -0
- package/esm/restorer/database-restorer.js.map +1 -0
- package/esm/sync/index.d.ts +12 -0
- package/esm/sync/index.d.ts.map +1 -0
- package/esm/sync/model-events.d.ts +62 -0
- package/esm/sync/model-events.d.ts.map +1 -0
- package/esm/sync/model-events.js +49 -0
- package/esm/sync/model-events.js.map +1 -0
- package/esm/sync/model-sync-operation.d.ts +163 -0
- package/esm/sync/model-sync-operation.d.ts.map +1 -0
- package/esm/sync/model-sync-operation.js +292 -0
- package/esm/sync/model-sync-operation.js.map +1 -0
- package/esm/sync/model-sync.d.ts +130 -0
- package/esm/sync/model-sync.d.ts.map +1 -0
- package/esm/sync/model-sync.js +178 -0
- package/esm/sync/model-sync.js.map +1 -0
- package/esm/sync/sync-context.d.ts +70 -0
- package/esm/sync/sync-context.d.ts.map +1 -0
- package/esm/sync/sync-context.js +101 -0
- package/esm/sync/sync-context.js.map +1 -0
- package/esm/sync/sync-manager.d.ts +213 -0
- package/esm/sync/sync-manager.d.ts.map +1 -0
- package/esm/sync/sync-manager.js +689 -0
- package/esm/sync/sync-manager.js.map +1 -0
- package/esm/sync/types.d.ts +289 -0
- package/esm/sync/types.d.ts.map +1 -0
- package/esm/types.d.ts +45 -0
- package/esm/types.d.ts.map +1 -0
- package/esm/utils/connect-to-database.d.ts +246 -0
- package/esm/utils/connect-to-database.d.ts.map +1 -0
- package/esm/utils/connect-to-database.js +92 -0
- package/esm/utils/connect-to-database.js.map +1 -0
- package/esm/utils/database-writer.utils.d.ts +15 -0
- package/esm/utils/database-writer.utils.d.ts.map +1 -0
- package/esm/utils/database-writer.utils.js +14 -0
- package/esm/utils/database-writer.utils.js.map +1 -0
- package/esm/utils/define-model.js +100 -0
- package/esm/utils/define-model.js.map +1 -0
- package/esm/utils/once-connected.d.ts +146 -0
- package/esm/utils/once-connected.d.ts.map +1 -0
- package/esm/utils/once-connected.js +252 -0
- package/esm/utils/once-connected.js.map +1 -0
- package/esm/validation/database-seal-plugins.d.ts +2 -0
- package/esm/validation/database-seal-plugins.d.ts.map +1 -0
- package/esm/validation/database-seal-plugins.js +4 -0
- package/esm/validation/database-seal-plugins.js.map +1 -0
- package/esm/validation/database-writer-validation-error.d.ts +97 -0
- package/esm/validation/database-writer-validation-error.d.ts.map +1 -0
- package/esm/validation/database-writer-validation-error.js +160 -0
- package/esm/validation/database-writer-validation-error.js.map +1 -0
- package/esm/validation/index.d.ts +3 -0
- package/esm/validation/index.d.ts.map +1 -0
- package/esm/validation/mutators/embed-mutator.d.ts +9 -0
- package/esm/validation/mutators/embed-mutator.d.ts.map +1 -0
- package/esm/validation/mutators/embed-mutator.js +33 -0
- package/esm/validation/mutators/embed-mutator.js.map +1 -0
- package/esm/validation/plugins/embed-validator-plugin.d.ts +24 -0
- package/esm/validation/plugins/embed-validator-plugin.d.ts.map +1 -0
- package/esm/validation/plugins/embed-validator-plugin.js +18 -0
- package/esm/validation/plugins/embed-validator-plugin.js.map +1 -0
- package/esm/validation/rules/database-model-rule.d.ts +7 -0
- package/esm/validation/rules/database-model-rule.d.ts.map +1 -0
- package/esm/validation/rules/database-model-rule.js +27 -0
- package/esm/validation/rules/database-model-rule.js.map +1 -0
- package/esm/validation/transformers/embed-model-transformer.d.ts +3 -0
- package/esm/validation/transformers/embed-model-transformer.d.ts.map +1 -0
- package/esm/validation/transformers/embed-model-transformer.js +18 -0
- package/esm/validation/transformers/embed-model-transformer.js.map +1 -0
- package/esm/validation/validators/embed-validator.d.ts +21 -0
- package/esm/validation/validators/embed-validator.d.ts.map +1 -0
- package/esm/validation/validators/embed-validator.js +42 -0
- package/esm/validation/validators/embed-validator.js.map +1 -0
- package/esm/writer/database-writer.d.ts +181 -0
- package/esm/writer/database-writer.d.ts.map +1 -0
- package/esm/writer/database-writer.js +402 -0
- package/esm/writer/database-writer.js.map +1 -0
- package/package.json +61 -52
|
@@ -0,0 +1,1463 @@
|
|
|
1
|
+
'use strict';var reinforcements=require('@mongez/reinforcements'),dataSourceRegistry=require('../data-source/data-source-registry.js'),databaseDirtyTracker=require('../database-dirty-tracker.js'),modelEvents=require('../events/model-events.js'),databaseRemover=require('../remover/database-remover.js'),databaseRestorer=require('../restorer/database-restorer.js'),modelSync=require('../sync/model-sync.js'),databaseWriter=require('../writer/database-writer.js'),registerModel=require('./register-model.js');/**
|
|
2
|
+
* Sentinel value used to distinguish between undefined and missing fields.
|
|
3
|
+
*/
|
|
4
|
+
const MISSING_VALUE = Symbol("missing");
|
|
5
|
+
/**
|
|
6
|
+
* WeakMap registry that associates each model constructor with its own event emitter.
|
|
7
|
+
*/
|
|
8
|
+
const modelEventsRegistry = new WeakMap();
|
|
9
|
+
/**
|
|
10
|
+
* Base class that powers all Cascade models.
|
|
11
|
+
*
|
|
12
|
+
* Provides:
|
|
13
|
+
* - Type-safe value accessors with dot-notation support (get, set, has, unset, merge)
|
|
14
|
+
* - Automatic dirty tracking for efficient partial updates
|
|
15
|
+
* - Lifecycle event hooks (saving, created, deleting, etc.)
|
|
16
|
+
* - Integration with the data-source registry for multi-database support
|
|
17
|
+
* - Support for both per-model and global event listeners
|
|
18
|
+
*
|
|
19
|
+
* @template TSchema - The shape of the model's underlying data
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* interface UserSchema {
|
|
24
|
+
* id: number;
|
|
25
|
+
* name: string;
|
|
26
|
+
* email: string;
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* class User extends Model<UserSchema> {
|
|
30
|
+
* public static table = "users";
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* const user = new User({ name: "Alice" });
|
|
34
|
+
* user.set("email", "alice@example.com");
|
|
35
|
+
* console.log(user.hasChanges()); // true
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
class Model {
|
|
39
|
+
/**
|
|
40
|
+
* The database table or collection name associated with this model.
|
|
41
|
+
*
|
|
42
|
+
* Must be defined by each concrete model subclass.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* class User extends Model {
|
|
47
|
+
* public static table = "users";
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
static table;
|
|
52
|
+
/**
|
|
53
|
+
* Resource for this model.
|
|
54
|
+
* It is a class that holds a toJSON function
|
|
55
|
+
* Called when the model is being converted to JSON (by calling toJSON or JSON.stringify(model))
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* class User extends Model {
|
|
60
|
+
* public static resource = UserResource;
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
static resource;
|
|
65
|
+
/**
|
|
66
|
+
* Resource columns
|
|
67
|
+
* Define what columns should be sent to the resource (if any) when converting to JSON
|
|
68
|
+
*/
|
|
69
|
+
static resourceColumns;
|
|
70
|
+
/**
|
|
71
|
+
* JSON keys for this model.
|
|
72
|
+
* This could be used if resource is not passed
|
|
73
|
+
* It will select only these keys from the model
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* class User extends Model {
|
|
77
|
+
* public static toJsonColumns = ["id", "name"];
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
static toJsonColumns;
|
|
82
|
+
/**
|
|
83
|
+
* Data source reference for this model.
|
|
84
|
+
*
|
|
85
|
+
* Can be:
|
|
86
|
+
* - A string name registered in the data-source registry
|
|
87
|
+
* - A DataSource instance
|
|
88
|
+
* - Undefined (falls back to the default data source)
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* class User extends Model {
|
|
93
|
+
* public static dataSource = "primary";
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
static dataSource;
|
|
98
|
+
/**
|
|
99
|
+
* Query builder class
|
|
100
|
+
*/
|
|
101
|
+
static builder;
|
|
102
|
+
/**
|
|
103
|
+
* Primary key field name used to identify records.
|
|
104
|
+
*
|
|
105
|
+
* @default "id"
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* class User extends Model {
|
|
110
|
+
* public static primaryKey = "_id"; // MongoDB
|
|
111
|
+
* }
|
|
112
|
+
*
|
|
113
|
+
* class Product extends Model {
|
|
114
|
+
* public static primaryKey = "id"; // SQL
|
|
115
|
+
* }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
static primaryKey = "id";
|
|
119
|
+
/**
|
|
120
|
+
* Embeded fields when document is Being embeded
|
|
121
|
+
*/
|
|
122
|
+
static embed;
|
|
123
|
+
/**
|
|
124
|
+
* Validation and casting schema using @warlock.js/seal.
|
|
125
|
+
*
|
|
126
|
+
* Defines validation rules and data transformations for the model.
|
|
127
|
+
* Used automatically during save operations.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* import { v } from "@warlock.js/seal";
|
|
132
|
+
*
|
|
133
|
+
* class User extends Model {
|
|
134
|
+
* public static schema = v.object({
|
|
135
|
+
* name: v.string().required().trim(),
|
|
136
|
+
* age: v.number().min(0).max(120),
|
|
137
|
+
* email: v.string().email().required().toLowerCase(),
|
|
138
|
+
* createdAt: v.date().default(() => new Date()),
|
|
139
|
+
* });
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
static schema;
|
|
144
|
+
/**
|
|
145
|
+
* Strict mode behavior for unknown fields.
|
|
146
|
+
*
|
|
147
|
+
* - `"strip"`: Remove unknown fields silently (default, recommended for APIs)
|
|
148
|
+
* - `"fail"`: Throw validation error on unknown fields (strict validation)
|
|
149
|
+
* - `"allow"`: Allow unknown fields to pass through (permissive)
|
|
150
|
+
*
|
|
151
|
+
* @default "strip"
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* import { Model, type StrictMode } from "@warlock.js/cascade";
|
|
156
|
+
*
|
|
157
|
+
* class User extends Model {
|
|
158
|
+
* public static strictMode: StrictMode = "fail"; // Throw on unknown fields
|
|
159
|
+
* }
|
|
160
|
+
*
|
|
161
|
+
* const user = new User({ name: "Alice", unknownField: "value" });
|
|
162
|
+
* await user.save(); // DatabaseWriterValidationError: unknown field
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
static strictMode = "strip";
|
|
166
|
+
/**
|
|
167
|
+
* Auto-generate incremental `id` field on insert (NoSQL only).
|
|
168
|
+
*
|
|
169
|
+
* When enabled, the ID generator creates a sequential integer ID
|
|
170
|
+
* separate from the database's native ID (_id for MongoDB).
|
|
171
|
+
*
|
|
172
|
+
* **Note:** SQL databases use native AUTO_INCREMENT and don't need this.
|
|
173
|
+
*
|
|
174
|
+
* @default true
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* class User extends Model {
|
|
179
|
+
* public static autoGenerateId = true;
|
|
180
|
+
* }
|
|
181
|
+
*
|
|
182
|
+
* const user = new User({ name: "Alice" });
|
|
183
|
+
* await user.save();
|
|
184
|
+
* console.log(user.get("_id")); // ObjectId("...") - MongoDB
|
|
185
|
+
* console.log(user.get("id")); // 1 - Generated
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
static autoGenerateId = true;
|
|
189
|
+
/**
|
|
190
|
+
* Initial ID value for the first record.
|
|
191
|
+
*
|
|
192
|
+
* If not set, defaults to 1 or uses `randomInitialId`.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```typescript
|
|
196
|
+
* class User extends Model {
|
|
197
|
+
* public static initialId = 1000; // Start from 1000
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
static initialId;
|
|
202
|
+
/**
|
|
203
|
+
* Randomly generate the initial ID.
|
|
204
|
+
*
|
|
205
|
+
* Can be:
|
|
206
|
+
* - `true`: Generate random ID between 10000-499999
|
|
207
|
+
* - Function: Custom random ID generator
|
|
208
|
+
* - `false`: Use `initialId` or default to 1
|
|
209
|
+
*
|
|
210
|
+
* @default false
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* class User extends Model {
|
|
215
|
+
* public static randomInitialId = true; // Random 10000-499999
|
|
216
|
+
* }
|
|
217
|
+
*
|
|
218
|
+
* class Product extends Model {
|
|
219
|
+
* public static randomInitialId = () => Math.floor(Math.random() * 1000000);
|
|
220
|
+
* }
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
static randomInitialId;
|
|
224
|
+
/**
|
|
225
|
+
* Amount to increment ID by for each new record.
|
|
226
|
+
*
|
|
227
|
+
* If not set, defaults to 1 or uses `randomIncrement`.
|
|
228
|
+
*
|
|
229
|
+
* @default 1
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* class User extends Model {
|
|
234
|
+
* public static incrementIdBy = 5; // Increment by 5
|
|
235
|
+
* }
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
static incrementIdBy = 1;
|
|
239
|
+
/**
|
|
240
|
+
* Randomly generate the increment amount.
|
|
241
|
+
*
|
|
242
|
+
* Can be:
|
|
243
|
+
* - `true`: Generate random increment between 1-10
|
|
244
|
+
* - Function: Custom random increment generator
|
|
245
|
+
* - `false`: Use `incrementIdBy` or default to 1
|
|
246
|
+
*
|
|
247
|
+
* @default false
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* class User extends Model {
|
|
252
|
+
* public static randomIncrement = true; // Random 1-10
|
|
253
|
+
* }
|
|
254
|
+
*
|
|
255
|
+
* class Product extends Model {
|
|
256
|
+
* public static randomIncrement = () => Math.floor(Math.random() * 100);
|
|
257
|
+
* }
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
static randomIncrement;
|
|
261
|
+
/**
|
|
262
|
+
* Created at column name.
|
|
263
|
+
*/
|
|
264
|
+
static createdAtColumn = "createdAt";
|
|
265
|
+
/**
|
|
266
|
+
* Updated at column name.
|
|
267
|
+
*/
|
|
268
|
+
static updatedAtColumn = "updatedAt";
|
|
269
|
+
/**
|
|
270
|
+
* Delete strategy for this model.
|
|
271
|
+
*
|
|
272
|
+
* Controls how models are deleted:
|
|
273
|
+
* - `"trash"` - Moves to trash collection, then deletes
|
|
274
|
+
* - `"permanent"` - Direct deletion (hard delete)
|
|
275
|
+
* - `"soft"` - Sets deletedAt timestamp (soft delete)
|
|
276
|
+
*
|
|
277
|
+
* Can be overridden by destroy() options.
|
|
278
|
+
* Falls back to data source default if not set.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* class User extends Model {
|
|
283
|
+
* public static deleteStrategy: DeleteStrategy = "soft";
|
|
284
|
+
* }
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
static deleteStrategy;
|
|
288
|
+
/**
|
|
289
|
+
* Column name for soft delete timestamp.
|
|
290
|
+
*
|
|
291
|
+
* Used when delete strategy is "soft".
|
|
292
|
+
*
|
|
293
|
+
* @default "deletedAt"
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* class User extends Model {
|
|
298
|
+
* public static deletedAtColumn = "archivedAt";
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
static deletedAtColumn = "deletedAt";
|
|
303
|
+
/**
|
|
304
|
+
* Trash table/collection name override.
|
|
305
|
+
*
|
|
306
|
+
* If not set, defaults to `{table}Trash` or data source default.
|
|
307
|
+
* Used when delete strategy is "trash".
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```typescript
|
|
311
|
+
* class User extends Model {
|
|
312
|
+
* public static trashTable = "userRecycleBin";
|
|
313
|
+
* }
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
static trashTable;
|
|
317
|
+
/**
|
|
318
|
+
* Global scopes that are automatically applied to all queries.
|
|
319
|
+
* These scopes are inherited by child models.
|
|
320
|
+
*/
|
|
321
|
+
static globalScopes = new Map();
|
|
322
|
+
/**
|
|
323
|
+
* Local scopes that can be manually applied to queries.
|
|
324
|
+
* These are reusable query snippets that developers opt into.
|
|
325
|
+
*/
|
|
326
|
+
static localScopes = new Map();
|
|
327
|
+
/**
|
|
328
|
+
* Flag indicating whether this model instance represents a new (unsaved) record.
|
|
329
|
+
*
|
|
330
|
+
* - `true`: The model has not been persisted to the database yet
|
|
331
|
+
* - `false`: The model represents an existing database record
|
|
332
|
+
*
|
|
333
|
+
* This flag is used by the writer to determine whether to perform an insert or update.
|
|
334
|
+
*/
|
|
335
|
+
isNew = true;
|
|
336
|
+
/**
|
|
337
|
+
* The raw mutable data backing this model instance.
|
|
338
|
+
*
|
|
339
|
+
* All field accessors (get, set, merge, etc.) operate on this object.
|
|
340
|
+
*/
|
|
341
|
+
data;
|
|
342
|
+
/**
|
|
343
|
+
* Dirty tracker that monitors changes to the model's data.
|
|
344
|
+
*
|
|
345
|
+
* Tracks:
|
|
346
|
+
* - Which fields have been modified (dirty columns)
|
|
347
|
+
* - Which fields have been removed
|
|
348
|
+
* - Original vs. current values for each dirty field
|
|
349
|
+
*
|
|
350
|
+
* Used by the writer to generate efficient partial update payloads.
|
|
351
|
+
*/
|
|
352
|
+
dirtyTracker;
|
|
353
|
+
/**
|
|
354
|
+
* Constructs a new model instance with optional initial data.
|
|
355
|
+
*
|
|
356
|
+
* Initializes the dirty tracker with a snapshot of the provided data.
|
|
357
|
+
*
|
|
358
|
+
* @param initialData - Partial data to populate the model
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```typescript
|
|
362
|
+
* const user = new User({ name: "Alice", email: "alice@example.com" });
|
|
363
|
+
* ```
|
|
364
|
+
*/
|
|
365
|
+
constructor(initialData = {}) {
|
|
366
|
+
this.data = initialData;
|
|
367
|
+
this.dirtyTracker = new databaseDirtyTracker.DatabaseDirtyTracker(this.data);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Get a model class by its name from the global registry.
|
|
371
|
+
*
|
|
372
|
+
* Models must be decorated with @RegisterModel() to be available in the registry.
|
|
373
|
+
*
|
|
374
|
+
* @param name - The model class name
|
|
375
|
+
* @returns The model class or undefined if not found
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* ```typescript
|
|
379
|
+
* const UserModel = Model.getModel("User");
|
|
380
|
+
* if (UserModel) {
|
|
381
|
+
* const user = await UserModel.find(1);
|
|
382
|
+
* }
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
385
|
+
static getModel(name) {
|
|
386
|
+
return registerModel.getModelFromRegistry(name);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Get all registered models from the global registry.
|
|
390
|
+
*
|
|
391
|
+
* Only models decorated with @RegisterModel() will appear here.
|
|
392
|
+
*
|
|
393
|
+
* @returns A Map of all registered model classes by name
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* ```typescript
|
|
397
|
+
* const allModels = Model.getAllModels();
|
|
398
|
+
* for (const [name, ModelClass] of allModels) {
|
|
399
|
+
* console.log(`Found model: ${name} with table: ${ModelClass.table}`);
|
|
400
|
+
* }
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
static getAllModels() {
|
|
404
|
+
return registerModel.getAllModelsFromRegistry();
|
|
405
|
+
}
|
|
406
|
+
// ============================================================================
|
|
407
|
+
// STATIC SYNC METHODS
|
|
408
|
+
// ============================================================================
|
|
409
|
+
/**
|
|
410
|
+
* Create a sync operation for a single embedded document.
|
|
411
|
+
*
|
|
412
|
+
* When this model is updated, the target model's field
|
|
413
|
+
* will be updated with the embedded data.
|
|
414
|
+
*
|
|
415
|
+
* @param TargetModel - Target model class that receives data
|
|
416
|
+
* @param targetField - Field path in target model
|
|
417
|
+
* @returns Sync operation for chaining configuration
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* ```typescript
|
|
421
|
+
* // When Category updates, update Product.category
|
|
422
|
+
* Category.sync(Product, "category");
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
static sync(TargetModel, targetField) {
|
|
426
|
+
return modelSync.modelSync.sync(this, TargetModel, targetField);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Create a sync operation for an array of embedded documents.
|
|
430
|
+
*
|
|
431
|
+
* When this model is updated, the corresponding element
|
|
432
|
+
* in the target model's array field will be updated.
|
|
433
|
+
*
|
|
434
|
+
* @param TargetModel - Target model class that receives data
|
|
435
|
+
* @param targetField - Array field path in target model
|
|
436
|
+
* @returns Sync operation for chaining configuration
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* ```typescript
|
|
440
|
+
* // When Tag updates, update Post.tags[i] where tags[i].id matches
|
|
441
|
+
* Tag.syncMany(Post, "tags").identifyBy("id");
|
|
442
|
+
* ```
|
|
443
|
+
*/
|
|
444
|
+
static syncMany(TargetModel, targetField) {
|
|
445
|
+
return modelSync.modelSync.syncMany(this, TargetModel, targetField);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Get model id
|
|
449
|
+
*/
|
|
450
|
+
get id() {
|
|
451
|
+
return this.get("id");
|
|
452
|
+
}
|
|
453
|
+
get(field, defaultValue) {
|
|
454
|
+
return reinforcements.get(this.data, field, defaultValue);
|
|
455
|
+
}
|
|
456
|
+
only(fields) {
|
|
457
|
+
return reinforcements.only(this.data, fields);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Get a string value
|
|
461
|
+
*/
|
|
462
|
+
string(key, defaultValue) {
|
|
463
|
+
return this.get(key, defaultValue);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Get a number value
|
|
467
|
+
*/
|
|
468
|
+
number(key, defaultValue) {
|
|
469
|
+
return this.get(key, defaultValue);
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Get a boolean value
|
|
473
|
+
*/
|
|
474
|
+
boolean(key, defaultValue) {
|
|
475
|
+
return this.get(key, defaultValue);
|
|
476
|
+
}
|
|
477
|
+
set(field, value) {
|
|
478
|
+
const path = String(field);
|
|
479
|
+
reinforcements.set(this.data, path, value);
|
|
480
|
+
const partial = {};
|
|
481
|
+
reinforcements.set(partial, path, value);
|
|
482
|
+
this.dirtyTracker.mergeChanges(partial);
|
|
483
|
+
return this;
|
|
484
|
+
}
|
|
485
|
+
has(field) {
|
|
486
|
+
return reinforcements.get(this.data, field, MISSING_VALUE) !== MISSING_VALUE;
|
|
487
|
+
}
|
|
488
|
+
increment(field, amount) {
|
|
489
|
+
const value = this.get(field, 0);
|
|
490
|
+
const incrementedValue = value + amount;
|
|
491
|
+
return this.set(field, incrementedValue);
|
|
492
|
+
}
|
|
493
|
+
decrement(field, amount) {
|
|
494
|
+
const value = this.get(field, 0);
|
|
495
|
+
const decrementedValue = value - amount;
|
|
496
|
+
return this.set(field, decrementedValue);
|
|
497
|
+
}
|
|
498
|
+
unset(...fields) {
|
|
499
|
+
this.data = reinforcements.unset(this.data, fields);
|
|
500
|
+
this.dirtyTracker.unset(fields);
|
|
501
|
+
return this;
|
|
502
|
+
}
|
|
503
|
+
merge(values) {
|
|
504
|
+
this.data = reinforcements.merge(this.data, values);
|
|
505
|
+
this.dirtyTracker.mergeChanges(values);
|
|
506
|
+
return this;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Checks whether the model's data has changed since instantiation or last reset.
|
|
510
|
+
*
|
|
511
|
+
* @returns `true` if any fields have been modified or removed, `false` otherwise
|
|
512
|
+
*
|
|
513
|
+
* @example
|
|
514
|
+
* ```typescript
|
|
515
|
+
* const user = new User({ name: "Alice" });
|
|
516
|
+
* user.hasChanges(); // false
|
|
517
|
+
* user.set("name", "Bob");
|
|
518
|
+
* user.hasChanges(); // true
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
hasChanges() {
|
|
522
|
+
return this.dirtyTracker.hasChanges();
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Check if the given column has been modified.
|
|
526
|
+
*
|
|
527
|
+
* @param column - The column name to check
|
|
528
|
+
* @returns `true` if the column has been modified, `false` otherwise
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```typescript
|
|
532
|
+
* user.set("name", "Bob");
|
|
533
|
+
* user.isDirty("name"); // true
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
536
|
+
isDirty(column) {
|
|
537
|
+
return this.dirtyTracker.isDirty(column);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Retrieves all dirty columns with their old and new values.
|
|
541
|
+
*
|
|
542
|
+
* @returns A record mapping each dirty column to its previous and current value
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* ```typescript
|
|
546
|
+
* user.set("name", "Bob");
|
|
547
|
+
* user.getDirtyColumnsWithValues();
|
|
548
|
+
* // { name: { oldValue: "Alice", newValue: "Bob" } }
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
551
|
+
getDirtyColumnsWithValues() {
|
|
552
|
+
return this.dirtyTracker.getDirtyColumnsWithValues();
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Lists all columns that have been removed from the model's data.
|
|
556
|
+
*
|
|
557
|
+
* @returns An array of field names that were present initially but have been unset
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```typescript
|
|
561
|
+
* user.unset("tempField");
|
|
562
|
+
* user.getRemovedColumns(); // ["tempField"]
|
|
563
|
+
* ```
|
|
564
|
+
*/
|
|
565
|
+
getRemovedColumns() {
|
|
566
|
+
return this.dirtyTracker.getRemovedColumns();
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Lists all columns that have been modified since instantiation or last reset.
|
|
570
|
+
*
|
|
571
|
+
* @returns An array of field names that have changed
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```typescript
|
|
575
|
+
* user.set("name", "Bob");
|
|
576
|
+
* user.getDirtyColumns(); // ["name"]
|
|
577
|
+
* ```
|
|
578
|
+
*/
|
|
579
|
+
getDirtyColumns() {
|
|
580
|
+
return this.dirtyTracker.getDirtyColumns();
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Emits a lifecycle event to both per-model and global listeners.
|
|
584
|
+
*
|
|
585
|
+
* This method is public so that external services (like the writer) can trigger
|
|
586
|
+
* lifecycle events when appropriate.
|
|
587
|
+
*
|
|
588
|
+
* @param event - The event name (e.g., "saving", "created", "deleting")
|
|
589
|
+
* @param context - Optional context data to pass to listeners
|
|
590
|
+
*
|
|
591
|
+
* @example
|
|
592
|
+
* ```typescript
|
|
593
|
+
* await user.emitEvent("saving");
|
|
594
|
+
* await user.emitEvent("validated", { errors: [] });
|
|
595
|
+
* ```
|
|
596
|
+
*/
|
|
597
|
+
async emitEvent(event, context) {
|
|
598
|
+
const ctor = this.constructor;
|
|
599
|
+
await ctor.events().emit(event, this, context);
|
|
600
|
+
await modelEvents.globalModelEvents.emit(event, this, context);
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Resolves the data source associated with this model.
|
|
604
|
+
*
|
|
605
|
+
* Resolution order:
|
|
606
|
+
* 1. If `dataSource` is a string, looks it up in the data-source registry
|
|
607
|
+
* 2. If `dataSource` is a DataSource instance, returns it directly
|
|
608
|
+
* 3. Otherwise, returns the default data source from the registry
|
|
609
|
+
*
|
|
610
|
+
* @returns The resolved DataSource instance
|
|
611
|
+
* @throws Error if no data source is found
|
|
612
|
+
*
|
|
613
|
+
* @example
|
|
614
|
+
* ```typescript
|
|
615
|
+
* class User extends Model {
|
|
616
|
+
* public static dataSource = "primary";
|
|
617
|
+
* }
|
|
618
|
+
*
|
|
619
|
+
* const ds = User.getDataSource();
|
|
620
|
+
* ```
|
|
621
|
+
*/
|
|
622
|
+
static getDataSource() {
|
|
623
|
+
const ref = this.dataSource;
|
|
624
|
+
let dataSource;
|
|
625
|
+
if (typeof ref === "string") {
|
|
626
|
+
dataSource = dataSourceRegistry.dataSourceRegistry.get(ref);
|
|
627
|
+
}
|
|
628
|
+
else if (ref) {
|
|
629
|
+
dataSource = ref;
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
dataSource = dataSourceRegistry.dataSourceRegistry.get();
|
|
633
|
+
}
|
|
634
|
+
// Apply model defaults from data source (only once per model class)
|
|
635
|
+
if (!this.hasOwnProperty("_defaultsApplied") && dataSource.modelDefaults) {
|
|
636
|
+
this.applyModelDefaults(dataSource.modelDefaults);
|
|
637
|
+
this._defaultsApplied = true;
|
|
638
|
+
}
|
|
639
|
+
return dataSource;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Apply model defaults from data source configuration.
|
|
643
|
+
*
|
|
644
|
+
* This is called automatically by getDataSource() the first time
|
|
645
|
+
* a model accesses its data source. Defaults are only applied if
|
|
646
|
+
* the model doesn't already have its own value set.
|
|
647
|
+
*
|
|
648
|
+
* @param defaults - Model default configuration from data source
|
|
649
|
+
* @private
|
|
650
|
+
*/
|
|
651
|
+
static applyModelDefaults(defaults) {
|
|
652
|
+
// Only apply defaults if model doesn't have its own value
|
|
653
|
+
// Note: autoGenerateId is not applied here as it's a driver-level setting
|
|
654
|
+
if (defaults.initialId !== undefined && this.initialId === undefined) {
|
|
655
|
+
this.initialId = defaults.initialId;
|
|
656
|
+
}
|
|
657
|
+
if (defaults.randomInitialId !== undefined && this.randomInitialId === undefined) {
|
|
658
|
+
this.randomInitialId = defaults.randomInitialId;
|
|
659
|
+
}
|
|
660
|
+
if (defaults.incrementIdBy !== undefined && this.incrementIdBy === undefined) {
|
|
661
|
+
this.incrementIdBy = defaults.incrementIdBy;
|
|
662
|
+
}
|
|
663
|
+
if (defaults.randomIncrement !== undefined && this.randomIncrement === undefined) {
|
|
664
|
+
this.randomIncrement = defaults.randomIncrement;
|
|
665
|
+
}
|
|
666
|
+
if (defaults.deleteStrategy !== undefined && this.deleteStrategy === undefined) {
|
|
667
|
+
this.deleteStrategy = defaults.deleteStrategy;
|
|
668
|
+
}
|
|
669
|
+
if (defaults.strictMode !== undefined && this.strictMode === undefined) {
|
|
670
|
+
this.strictMode = defaults.strictMode;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Add a global scope that is automatically applied to all queries.
|
|
675
|
+
*
|
|
676
|
+
* Global scopes are inherited by child models and applied before query execution.
|
|
677
|
+
* Use for security filters, multi-tenancy, soft deletes, etc.
|
|
678
|
+
*
|
|
679
|
+
* @param name - Unique name for the scope
|
|
680
|
+
* @param callback - Function that modifies the query
|
|
681
|
+
* @param options - Scope options (timing: 'before' | 'after')
|
|
682
|
+
*
|
|
683
|
+
* @example
|
|
684
|
+
* ```typescript
|
|
685
|
+
* // Multi-tenancy scope
|
|
686
|
+
* Model.addGlobalScope('tenant', (query) => {
|
|
687
|
+
* query.where('tenantId', getCurrentTenant());
|
|
688
|
+
* }, { timing: 'before' });
|
|
689
|
+
*
|
|
690
|
+
* // Soft delete scope
|
|
691
|
+
* User.addGlobalScope('notDeleted', (query) => {
|
|
692
|
+
* query.whereNull('deletedAt');
|
|
693
|
+
* });
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
static addGlobalScope(name, callback, options = {}) {
|
|
697
|
+
this.globalScopes.set(name, {
|
|
698
|
+
callback,
|
|
699
|
+
timing: options.timing || "before",
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Remove a global scope by name.
|
|
704
|
+
*
|
|
705
|
+
* @param name - Name of the scope to remove
|
|
706
|
+
*
|
|
707
|
+
* @example
|
|
708
|
+
* ```typescript
|
|
709
|
+
* Model.removeGlobalScope('tenant');
|
|
710
|
+
* ```
|
|
711
|
+
*/
|
|
712
|
+
static removeGlobalScope(name) {
|
|
713
|
+
this.globalScopes.delete(name);
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Add a local scope that can be manually applied to queries.
|
|
717
|
+
*
|
|
718
|
+
* Local scopes are reusable query snippets that developers opt into.
|
|
719
|
+
* They are not automatically applied.
|
|
720
|
+
*
|
|
721
|
+
* @param name - Unique name for the scope
|
|
722
|
+
* @param callback - Function that modifies the query
|
|
723
|
+
*
|
|
724
|
+
* @example
|
|
725
|
+
* ```typescript
|
|
726
|
+
* // Define reusable scopes
|
|
727
|
+
* User.addScope('active', (query) => {
|
|
728
|
+
* query.where('isActive', true);
|
|
729
|
+
* });
|
|
730
|
+
*
|
|
731
|
+
* User.addScope('admins', (query) => {
|
|
732
|
+
* query.where('role', 'admin');
|
|
733
|
+
* });
|
|
734
|
+
*
|
|
735
|
+
* // Use explicitly
|
|
736
|
+
* await User.query().scope('active').get();
|
|
737
|
+
* await User.query().scope('admins').get();
|
|
738
|
+
* ```
|
|
739
|
+
*/
|
|
740
|
+
static addScope(name, callback) {
|
|
741
|
+
this.localScopes.set(name, callback);
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Remove a local scope by name.
|
|
745
|
+
*
|
|
746
|
+
* @param name - Name of the scope to remove
|
|
747
|
+
*
|
|
748
|
+
* @example
|
|
749
|
+
* ```typescript
|
|
750
|
+
* User.removeScope('active');
|
|
751
|
+
* ```
|
|
752
|
+
*/
|
|
753
|
+
static removeScope(name) {
|
|
754
|
+
this.localScopes.delete(name);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Create a new query builder for this model
|
|
758
|
+
*/
|
|
759
|
+
static query() {
|
|
760
|
+
// Call newQueryBuilder as a static method (may be overridden in child classes)
|
|
761
|
+
const queryBuilder = this.newQueryBuilder();
|
|
762
|
+
const ModelClass = this;
|
|
763
|
+
// Collect global scopes from base Model and child model
|
|
764
|
+
const allGlobalScopes = new Map([
|
|
765
|
+
...Model.globalScopes,
|
|
766
|
+
...this.globalScopes,
|
|
767
|
+
]);
|
|
768
|
+
// Pass scopes to query builder
|
|
769
|
+
queryBuilder.pendingGlobalScopes = allGlobalScopes;
|
|
770
|
+
queryBuilder.availableLocalScopes = this.localScopes;
|
|
771
|
+
queryBuilder.disabledGlobalScopes = new Set();
|
|
772
|
+
// Emit fetching event
|
|
773
|
+
this.events().emitFetching(queryBuilder, { table: this.table, modelClass: this });
|
|
774
|
+
queryBuilder.hydrate((data) => {
|
|
775
|
+
const model = new ModelClass(data);
|
|
776
|
+
model.isNew = false;
|
|
777
|
+
return model;
|
|
778
|
+
});
|
|
779
|
+
// Wire up onFetched callback to emit model-level event
|
|
780
|
+
queryBuilder.onFetched(async (models, context) => {
|
|
781
|
+
await this.events().emit("fetched", models, context);
|
|
782
|
+
});
|
|
783
|
+
return queryBuilder;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Create new query builder.
|
|
787
|
+
*
|
|
788
|
+
* If the model has a static `builder` property set to a query builder class,
|
|
789
|
+
* it will be instantiated instead of the default driver query builder.
|
|
790
|
+
*
|
|
791
|
+
* @example
|
|
792
|
+
* ```typescript
|
|
793
|
+
* class UserQueryBuilder<T = User> extends MongoQueryBuilder<T> {
|
|
794
|
+
* active() { return this.where("isActive", true); }
|
|
795
|
+
* }
|
|
796
|
+
*
|
|
797
|
+
* class User extends Model {
|
|
798
|
+
* static builder = UserQueryBuilder; // That's it! ✨
|
|
799
|
+
* }
|
|
800
|
+
*
|
|
801
|
+
* // Now User.query() returns UserQueryBuilder<User> with autocomplete!
|
|
802
|
+
* ```
|
|
803
|
+
*/
|
|
804
|
+
static newQueryBuilder() {
|
|
805
|
+
const dataSource = this.getDataSource();
|
|
806
|
+
// Check if model has a custom builder class
|
|
807
|
+
if (this.builder) {
|
|
808
|
+
const BuilderClass = this.builder;
|
|
809
|
+
return new BuilderClass(this.table, dataSource);
|
|
810
|
+
}
|
|
811
|
+
// Use default driver query builder
|
|
812
|
+
const queryBuilder = dataSource.driver.queryBuilder(this.table);
|
|
813
|
+
return queryBuilder;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Get First matched record for the given filter
|
|
817
|
+
*/
|
|
818
|
+
static async first(filter) {
|
|
819
|
+
const query = this.query();
|
|
820
|
+
if (filter) {
|
|
821
|
+
query.where(filter);
|
|
822
|
+
}
|
|
823
|
+
return query.first();
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Get last matched record for the given filter
|
|
827
|
+
*/
|
|
828
|
+
static async last(filter) {
|
|
829
|
+
const query = this.query();
|
|
830
|
+
if (filter) {
|
|
831
|
+
query.where(filter);
|
|
832
|
+
}
|
|
833
|
+
return query.last();
|
|
834
|
+
}
|
|
835
|
+
static where(...args) {
|
|
836
|
+
return this.query().where(...args);
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Count the number of records in the table
|
|
840
|
+
* @param filter - The filter to apply to the query
|
|
841
|
+
*/
|
|
842
|
+
static count(filter) {
|
|
843
|
+
const query = this.query();
|
|
844
|
+
if (filter) {
|
|
845
|
+
query.where(filter);
|
|
846
|
+
}
|
|
847
|
+
return query.count();
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Find record by id
|
|
851
|
+
*/
|
|
852
|
+
static async find(id) {
|
|
853
|
+
const query = this.query();
|
|
854
|
+
return query.where(this.primaryKey, id).first();
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Get all records from the table
|
|
858
|
+
*
|
|
859
|
+
* @param filter - The filter to apply to the query
|
|
860
|
+
* @returns All records from the table
|
|
861
|
+
*/
|
|
862
|
+
static async all(filter) {
|
|
863
|
+
const query = this.query();
|
|
864
|
+
if (filter) {
|
|
865
|
+
query.where(filter);
|
|
866
|
+
}
|
|
867
|
+
return query.get();
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Get latest records from the table
|
|
871
|
+
*
|
|
872
|
+
* @param filter - The filter to apply to the query
|
|
873
|
+
*/
|
|
874
|
+
static async latest(filter) {
|
|
875
|
+
const query = this.query();
|
|
876
|
+
if (filter) {
|
|
877
|
+
query.where(filter);
|
|
878
|
+
}
|
|
879
|
+
return (await query.latest());
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Increment the given field by the given amount
|
|
883
|
+
*
|
|
884
|
+
* @example ```typescript
|
|
885
|
+
* // Increase age by 1 for user id 1
|
|
886
|
+
* User.increment({id: 1}, "age", 1);
|
|
887
|
+
* // Increase age by 1 and views by 2 for user id 1
|
|
888
|
+
* User.increment({id: 1}, {age: 1, views: 2});
|
|
889
|
+
* ```
|
|
890
|
+
*/
|
|
891
|
+
static increase(filter, field, amount) {
|
|
892
|
+
const query = this.query().where(filter);
|
|
893
|
+
return query.increment(field, amount);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Decrement the given field by the given amount
|
|
897
|
+
* @example ```typescript
|
|
898
|
+
* // Decrease age by 1 for user id 1
|
|
899
|
+
* User.decrement({id: 1}, "age", 1);
|
|
900
|
+
* // Decrease age by 1 and views by 2 for user id 1
|
|
901
|
+
* User.decrement({id: 1}, {age: 1, views: 2});
|
|
902
|
+
* ```
|
|
903
|
+
*/
|
|
904
|
+
static decrease(filter, field, amount) {
|
|
905
|
+
const query = this.query().where(filter);
|
|
906
|
+
return query.decrement(field, amount);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Perform atomic operation
|
|
910
|
+
*/
|
|
911
|
+
static async atomic(filter, operations) {
|
|
912
|
+
const dataSource = this.getDataSource();
|
|
913
|
+
const result = await dataSource.driver.atomic(this.table, filter, operations);
|
|
914
|
+
return result.modifiedCount;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Destroy (delete) the current model instance from the database.
|
|
918
|
+
*
|
|
919
|
+
* Emits lifecycle events:
|
|
920
|
+
* - `deleting` - Before deletion
|
|
921
|
+
* - `deleted` - After successful deletion
|
|
922
|
+
*
|
|
923
|
+
* @param options - Destroy options (strategy override, skipEvents)
|
|
924
|
+
* @throws {Error} If the model is new (not saved) or if deletion fails
|
|
925
|
+
*
|
|
926
|
+
* @example
|
|
927
|
+
* ```typescript
|
|
928
|
+
* const user = await User.find(1);
|
|
929
|
+
* await user.destroy(); // Uses default strategy
|
|
930
|
+
* await user.destroy({ strategy: "permanent" }); // Override strategy
|
|
931
|
+
* await user.destroy({ skipEvents: true }); // Silent delete
|
|
932
|
+
* ```
|
|
933
|
+
*/
|
|
934
|
+
async destroy(options) {
|
|
935
|
+
const remover = new databaseRemover.DatabaseRemover(this);
|
|
936
|
+
return await remover.destroy(options);
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Get the class constructor from an instance.
|
|
940
|
+
*
|
|
941
|
+
* This helper method allows instance methods to access static properties
|
|
942
|
+
* and methods of the model class in a type-safe way.
|
|
943
|
+
*
|
|
944
|
+
* @returns The model class constructor
|
|
945
|
+
*
|
|
946
|
+
* @example
|
|
947
|
+
* ```typescript
|
|
948
|
+
* const constructor = this.self();
|
|
949
|
+
* const table = constructor.table;
|
|
950
|
+
* await constructor.deleteOne({ id: 1 });
|
|
951
|
+
* ```
|
|
952
|
+
*/
|
|
953
|
+
self() {
|
|
954
|
+
return this.constructor;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Creates an immutable clone of the model with its current state.
|
|
958
|
+
*
|
|
959
|
+
* The cloned model:
|
|
960
|
+
* - Contains a deep copy of all current data
|
|
961
|
+
* - Has frozen (immutable) data that cannot be modified
|
|
962
|
+
* - Preserves the `isNew` flag from the original
|
|
963
|
+
* - Has no dirty changes (clean state)
|
|
964
|
+
* - Cannot be saved or modified
|
|
965
|
+
*
|
|
966
|
+
* This is useful for:
|
|
967
|
+
* - Creating snapshots of model state
|
|
968
|
+
* - Passing read-only model data to other parts of the application
|
|
969
|
+
* - Preventing accidental mutations
|
|
970
|
+
* - Maintaining historical records
|
|
971
|
+
*
|
|
972
|
+
* @returns A new immutable model instance with the current state
|
|
973
|
+
*
|
|
974
|
+
* @example
|
|
975
|
+
* ```typescript
|
|
976
|
+
* const user = new User({ name: "Alice", email: "alice@example.com" });
|
|
977
|
+
* await user.save();
|
|
978
|
+
*
|
|
979
|
+
* // Create an immutable snapshot
|
|
980
|
+
* const snapshot = user.clone();
|
|
981
|
+
*
|
|
982
|
+
* // This will throw an error because the clone is immutable
|
|
983
|
+
* snapshot.set("name", "Bob"); // TypeError: Cannot assign to read only property
|
|
984
|
+
*
|
|
985
|
+
* // Original can still be modified
|
|
986
|
+
* user.set("name", "Bob");
|
|
987
|
+
* await user.save();
|
|
988
|
+
* ```
|
|
989
|
+
*/
|
|
990
|
+
clone() {
|
|
991
|
+
// Deep copy the current data using JSON serialization
|
|
992
|
+
// This ensures nested objects are also copied
|
|
993
|
+
const clonedData = JSON.parse(JSON.stringify(this.data));
|
|
994
|
+
// Create a new instance of the same model class
|
|
995
|
+
const ModelClass = this.self();
|
|
996
|
+
const clonedModel = new ModelClass(clonedData);
|
|
997
|
+
// Preserve the isNew state
|
|
998
|
+
clonedModel.isNew = this.isNew;
|
|
999
|
+
// Freeze the data to make it immutable
|
|
1000
|
+
// This recursively freezes all nested objects
|
|
1001
|
+
this.deepFreeze(clonedModel.data);
|
|
1002
|
+
// Reset the dirty tracker to have no changes
|
|
1003
|
+
// The clone represents a clean snapshot
|
|
1004
|
+
clonedModel.dirtyTracker.reset();
|
|
1005
|
+
return clonedModel;
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Recursively freezes an object and all its nested properties.
|
|
1009
|
+
*
|
|
1010
|
+
* @param obj - The object to freeze
|
|
1011
|
+
* @returns The frozen object
|
|
1012
|
+
* @private
|
|
1013
|
+
*/
|
|
1014
|
+
deepFreeze(obj) {
|
|
1015
|
+
// Freeze the object itself
|
|
1016
|
+
Object.freeze(obj);
|
|
1017
|
+
// Recursively freeze all properties
|
|
1018
|
+
Object.getOwnPropertyNames(obj).forEach((prop) => {
|
|
1019
|
+
const value = obj[prop];
|
|
1020
|
+
// Only freeze objects and arrays, skip primitives and null
|
|
1021
|
+
if (value !== null &&
|
|
1022
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
1023
|
+
!Object.isFrozen(value)) {
|
|
1024
|
+
this.deepFreeze(value);
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
return obj;
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Get table name
|
|
1031
|
+
*/
|
|
1032
|
+
getTableName() {
|
|
1033
|
+
return this.self().table;
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Get primary key name
|
|
1037
|
+
*/
|
|
1038
|
+
getPrimaryKey() {
|
|
1039
|
+
return this.self().primaryKey;
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Get model schema
|
|
1043
|
+
*/
|
|
1044
|
+
getSchema() {
|
|
1045
|
+
return this.self().schema;
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Check if schema has the given key
|
|
1049
|
+
*/
|
|
1050
|
+
schemaHas(key) {
|
|
1051
|
+
return this.self().schema?.schema[key] !== undefined;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Get strict mode
|
|
1055
|
+
*/
|
|
1056
|
+
getStrictMode() {
|
|
1057
|
+
return this.self().strictMode;
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Get data source (Connection)
|
|
1061
|
+
*/
|
|
1062
|
+
getConnection() {
|
|
1063
|
+
return this.self().getDataSource();
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Delete all matching documents from the table.
|
|
1067
|
+
*/
|
|
1068
|
+
static async delete(filter) {
|
|
1069
|
+
return await this.getDataSource().driver.deleteMany(this.table, filter);
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Delete a single matching document from the table.
|
|
1073
|
+
*/
|
|
1074
|
+
static async deleteOne(filter) {
|
|
1075
|
+
return await this.getDataSource().driver.delete(this.table, filter);
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Restore a single deleted record by its ID.
|
|
1079
|
+
*
|
|
1080
|
+
* Automatically detects whether the record was deleted via "trash" or "soft" strategy.
|
|
1081
|
+
* Handles ID conflicts based on options.
|
|
1082
|
+
*
|
|
1083
|
+
* @param id - The primary key value of the record to restore
|
|
1084
|
+
* @param options - Restorer options (onIdConflict, skipEvents)
|
|
1085
|
+
* @returns The restored model instance
|
|
1086
|
+
*
|
|
1087
|
+
* @throws {Error} If record not found in trash or soft-deleted records
|
|
1088
|
+
* @throws {Error} If ID conflict and onIdConflict is "fail"
|
|
1089
|
+
*
|
|
1090
|
+
* @example
|
|
1091
|
+
* ```typescript
|
|
1092
|
+
* // Restore with default options (assign new ID if conflict)
|
|
1093
|
+
* const user = await User.restore(123);
|
|
1094
|
+
*
|
|
1095
|
+
* // Restore and fail if ID conflict
|
|
1096
|
+
* const user = await User.restore(123, { onIdConflict: "fail" });
|
|
1097
|
+
*
|
|
1098
|
+
* // Silent restore (skip events)
|
|
1099
|
+
* const user = await User.restore(123, { skipEvents: true });
|
|
1100
|
+
* ```
|
|
1101
|
+
*/
|
|
1102
|
+
static async restore(id, options) {
|
|
1103
|
+
const restorer = new databaseRestorer.DatabaseRestorer(this);
|
|
1104
|
+
const result = await restorer.restore(id, options);
|
|
1105
|
+
if (!result.restoredRecord) {
|
|
1106
|
+
throw new Error(`Failed to restore ${this.name} with ${this.primaryKey} ${id}: no record returned.`);
|
|
1107
|
+
}
|
|
1108
|
+
return result.restoredRecord;
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Restore all deleted records for the model's table.
|
|
1112
|
+
*
|
|
1113
|
+
* Restores all records from the trash table (if using trash strategy)
|
|
1114
|
+
* or all soft-deleted records (if using soft strategy).
|
|
1115
|
+
*
|
|
1116
|
+
* @param options - Restorer options (onIdConflict, skipEvents)
|
|
1117
|
+
* @returns Array of restored model instances
|
|
1118
|
+
*
|
|
1119
|
+
* @example
|
|
1120
|
+
* ```typescript
|
|
1121
|
+
* // Restore all with default options
|
|
1122
|
+
* const users = await User.restoreAll();
|
|
1123
|
+
*
|
|
1124
|
+
* // Restore all and fail on any ID conflict
|
|
1125
|
+
* const users = await User.restoreAll({ onIdConflict: "fail" });
|
|
1126
|
+
* ```
|
|
1127
|
+
*/
|
|
1128
|
+
static async restoreAll(options) {
|
|
1129
|
+
const restorer = new databaseRestorer.DatabaseRestorer(this);
|
|
1130
|
+
const result = await restorer.restoreAll(options);
|
|
1131
|
+
if (result.restoredCount === 0) {
|
|
1132
|
+
return [];
|
|
1133
|
+
}
|
|
1134
|
+
return result.restoredRecords;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Create a new record in database and return the model instance.
|
|
1138
|
+
*
|
|
1139
|
+
* The data type is automatically inferred from the model's schema type.
|
|
1140
|
+
*
|
|
1141
|
+
* @param data - Partial data matching the model's schema type
|
|
1142
|
+
* @returns The created model instance
|
|
1143
|
+
*
|
|
1144
|
+
* @example
|
|
1145
|
+
* ```typescript
|
|
1146
|
+
* // TypeScript automatically infers UserSchema from User model
|
|
1147
|
+
* const user = await User.create({
|
|
1148
|
+
* name: "Alice",
|
|
1149
|
+
* email: "alice@example.com",
|
|
1150
|
+
* age: 30
|
|
1151
|
+
* });
|
|
1152
|
+
* // Type: User (with UserSchema inferred)
|
|
1153
|
+
* ```
|
|
1154
|
+
*/
|
|
1155
|
+
static async create(data) {
|
|
1156
|
+
const model = new this(data);
|
|
1157
|
+
await model.save();
|
|
1158
|
+
return model;
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Create many documents and return an array of created models
|
|
1162
|
+
*/
|
|
1163
|
+
static async createMany(data) {
|
|
1164
|
+
return await Promise.all(data.map((data) => this.create(data)));
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Find a record or create it if not found.
|
|
1168
|
+
*
|
|
1169
|
+
* Does NOT update existing records - returns them as-is.
|
|
1170
|
+
* Useful when you want to ensure a record exists without modifying it.
|
|
1171
|
+
*
|
|
1172
|
+
* @param filter - Conditions to find by
|
|
1173
|
+
* @param data - Data to create if not found (merged with filter)
|
|
1174
|
+
* @returns Promise resolving to found or created model
|
|
1175
|
+
*
|
|
1176
|
+
* @example
|
|
1177
|
+
* ```typescript
|
|
1178
|
+
* // Ensure default admin exists (don't modify if exists)
|
|
1179
|
+
* const admin = await User.findOrCreate(
|
|
1180
|
+
* { email: "admin@example.com" },
|
|
1181
|
+
* { email: "admin@example.com", name: "Admin", role: "admin" }
|
|
1182
|
+
* );
|
|
1183
|
+
* // If admin exists, returns existing (password unchanged)
|
|
1184
|
+
* // If not found, creates new admin
|
|
1185
|
+
* ```
|
|
1186
|
+
*/
|
|
1187
|
+
static async findOrCreate(filter, data) {
|
|
1188
|
+
// Try to find existing record
|
|
1189
|
+
const existing = await this.first(filter);
|
|
1190
|
+
if (existing) {
|
|
1191
|
+
return existing; // Return as-is, no update
|
|
1192
|
+
}
|
|
1193
|
+
// Create new record with merged data
|
|
1194
|
+
return await this.create({ ...filter, ...data });
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Update a record or create it if not found (upsert).
|
|
1198
|
+
*
|
|
1199
|
+
* DOES update existing records with new data.
|
|
1200
|
+
* Useful when you want to ensure a record exists with the latest data.
|
|
1201
|
+
*
|
|
1202
|
+
* Includes full Model features:
|
|
1203
|
+
* - ID generation (if creating)
|
|
1204
|
+
* - createdAt timestamp (if creating)
|
|
1205
|
+
* - updatedAt timestamp (always)
|
|
1206
|
+
* - Validation & casting
|
|
1207
|
+
* - Lifecycle events
|
|
1208
|
+
* - Sync operations
|
|
1209
|
+
*
|
|
1210
|
+
* @param filter - Conditions to find by
|
|
1211
|
+
* @param data - Data to update or create (merged with filter)
|
|
1212
|
+
* @returns Promise resolving to updated or created model
|
|
1213
|
+
*
|
|
1214
|
+
* @example
|
|
1215
|
+
* ```typescript
|
|
1216
|
+
* // Sync user from external API (update if exists, create if not)
|
|
1217
|
+
* const user = await User.updateOrCreate(
|
|
1218
|
+
* { externalId: "ext-123" },
|
|
1219
|
+
* {
|
|
1220
|
+
* externalId: "ext-123",
|
|
1221
|
+
* name: "John Updated",
|
|
1222
|
+
* email: "john.new@example.com",
|
|
1223
|
+
* lastSyncedAt: new Date()
|
|
1224
|
+
* }
|
|
1225
|
+
* );
|
|
1226
|
+
* // User always has latest data from API
|
|
1227
|
+
* ```
|
|
1228
|
+
*/
|
|
1229
|
+
static async updateOrCreate(filter, data) {
|
|
1230
|
+
// Try to find existing record
|
|
1231
|
+
const existing = await this.first(filter);
|
|
1232
|
+
if (existing) {
|
|
1233
|
+
// Update existing record
|
|
1234
|
+
await existing.save({ merge: data });
|
|
1235
|
+
return existing;
|
|
1236
|
+
}
|
|
1237
|
+
// Create new record with merged data
|
|
1238
|
+
return await this.create({ ...filter, ...data });
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Returns embedded data for sync operations.
|
|
1242
|
+
* Excludes internal MongoDB fields and ensures proper date serialization.
|
|
1243
|
+
*
|
|
1244
|
+
* @returns Embedded data object suitable for syncing
|
|
1245
|
+
*
|
|
1246
|
+
* @example
|
|
1247
|
+
* ```typescript
|
|
1248
|
+
* const user = await User.find(1);
|
|
1249
|
+
* const embedData = user.embedData;
|
|
1250
|
+
* // Returns: { id: 1, name: "Alice", email: "alice@example.com", ... }
|
|
1251
|
+
* // Excludes: _id
|
|
1252
|
+
* ```
|
|
1253
|
+
*/
|
|
1254
|
+
get embedData() {
|
|
1255
|
+
return this.self().embed ? this.only(this.self().embed) : this.data;
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Accesses the event emitter dedicated to this model constructor.
|
|
1259
|
+
*
|
|
1260
|
+
* Each model subclass gets its own isolated event emitter, allowing you to
|
|
1261
|
+
* register lifecycle hooks that only apply to that specific model.
|
|
1262
|
+
*
|
|
1263
|
+
* @returns The ModelEvents instance for this model constructor
|
|
1264
|
+
*
|
|
1265
|
+
* @example
|
|
1266
|
+
* ```typescript
|
|
1267
|
+
* User.events().onSaving((user) => {
|
|
1268
|
+
* console.log("User is being saved:", user);
|
|
1269
|
+
* });
|
|
1270
|
+
* ```
|
|
1271
|
+
*/
|
|
1272
|
+
static events() {
|
|
1273
|
+
let events = modelEventsRegistry.get(this);
|
|
1274
|
+
if (!events) {
|
|
1275
|
+
events = new modelEvents.ModelEvents();
|
|
1276
|
+
modelEventsRegistry.set(this, events);
|
|
1277
|
+
}
|
|
1278
|
+
return events;
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Cleanup model events
|
|
1282
|
+
*/
|
|
1283
|
+
static $cleanup() {
|
|
1284
|
+
modelEventsRegistry.delete(this);
|
|
1285
|
+
registerModel.removeModelFromRegistery(this.name);
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Registers an event listener for this model constructor.
|
|
1289
|
+
*
|
|
1290
|
+
* Convenience shorthand for `Model.events().on(...)`.
|
|
1291
|
+
*
|
|
1292
|
+
* @param event - The event name (e.g., "saving", "created")
|
|
1293
|
+
* @param listener - The callback to invoke when the event fires
|
|
1294
|
+
* @returns An unsubscribe function
|
|
1295
|
+
*
|
|
1296
|
+
* @example
|
|
1297
|
+
* ```typescript
|
|
1298
|
+
* const unsubscribe = User.on("saving", (user) => {
|
|
1299
|
+
* console.log("Saving user:", user);
|
|
1300
|
+
* });
|
|
1301
|
+
* ```
|
|
1302
|
+
*/
|
|
1303
|
+
static on(event, listener) {
|
|
1304
|
+
return this.events().on(event, listener);
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Registers a one-time event listener for this model constructor.
|
|
1308
|
+
*
|
|
1309
|
+
* The listener will automatically unsubscribe after its first invocation.
|
|
1310
|
+
* Convenience shorthand for `Model.events().once(...)`.
|
|
1311
|
+
*
|
|
1312
|
+
* @param event - The event name (e.g., "saving", "created")
|
|
1313
|
+
* @param listener - The callback to invoke when the event fires
|
|
1314
|
+
* @returns An unsubscribe function
|
|
1315
|
+
*
|
|
1316
|
+
* @example
|
|
1317
|
+
* ```typescript
|
|
1318
|
+
* User.once("created", (user) => {
|
|
1319
|
+
* console.log("First user created:", user);
|
|
1320
|
+
* });
|
|
1321
|
+
* ```
|
|
1322
|
+
*/
|
|
1323
|
+
static once(event, listener) {
|
|
1324
|
+
return this.events().once(event, listener);
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Removes an event listener from this model constructor.
|
|
1328
|
+
*
|
|
1329
|
+
* Convenience shorthand for `Model.events().off(...)`.
|
|
1330
|
+
*
|
|
1331
|
+
* @param event - The event name
|
|
1332
|
+
* @param listener - The callback to remove
|
|
1333
|
+
*
|
|
1334
|
+
* @example
|
|
1335
|
+
* ```typescript
|
|
1336
|
+
* const listener = (user) => console.log(user);
|
|
1337
|
+
* User.on("saving", listener);
|
|
1338
|
+
* User.off("saving", listener);
|
|
1339
|
+
* ```
|
|
1340
|
+
*/
|
|
1341
|
+
static off(event, listener) {
|
|
1342
|
+
this.events().off(event, listener);
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Accesses the global event emitter shared by all model instances.
|
|
1346
|
+
*
|
|
1347
|
+
* Use this for cross-cutting concerns like auditing, logging, or injecting
|
|
1348
|
+
* common fields (e.g., `createdBy`, `updatedBy`) across all models.
|
|
1349
|
+
*
|
|
1350
|
+
* @returns The global ModelEvents instance
|
|
1351
|
+
*
|
|
1352
|
+
* @example
|
|
1353
|
+
* ```typescript
|
|
1354
|
+
* Model.globalEvents().onSaving((model) => {
|
|
1355
|
+
* model.set("updatedAt", new Date());
|
|
1356
|
+
* });
|
|
1357
|
+
* ```
|
|
1358
|
+
*/
|
|
1359
|
+
static globalEvents() {
|
|
1360
|
+
return modelEvents.globalModelEvents;
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Replace the model's data entirely.
|
|
1364
|
+
*
|
|
1365
|
+
* Used internally by the writer after validation to update the model
|
|
1366
|
+
* with validated/casted data.
|
|
1367
|
+
*
|
|
1368
|
+
* **Warning:** This replaces all data and updates the dirty tracker.
|
|
1369
|
+
* Use with caution in application code.
|
|
1370
|
+
*
|
|
1371
|
+
* @param data - New data to replace current data
|
|
1372
|
+
*
|
|
1373
|
+
* @example
|
|
1374
|
+
* ```typescript
|
|
1375
|
+
* // Internal usage by writer
|
|
1376
|
+
* model.replaceData(validatedData);
|
|
1377
|
+
* ```
|
|
1378
|
+
*/
|
|
1379
|
+
replaceData(data) {
|
|
1380
|
+
this.data = data;
|
|
1381
|
+
this.dirtyTracker.replaceCurrentData(data);
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Save the model to the database.
|
|
1385
|
+
*
|
|
1386
|
+
* Performs insert if `isNew === true`, otherwise performs update.
|
|
1387
|
+
* Automatically validates, casts, generates IDs, and emits lifecycle events.
|
|
1388
|
+
*
|
|
1389
|
+
* **Features:**
|
|
1390
|
+
* - Validation via @warlock.js/seal schema
|
|
1391
|
+
* - Data casting (string → number, etc.)
|
|
1392
|
+
* - ID generation (NoSQL only)
|
|
1393
|
+
* - Partial updates (only changed fields)
|
|
1394
|
+
* - Lifecycle events (validating, saving, created/updated, saved)
|
|
1395
|
+
*
|
|
1396
|
+
* @param data - Optional data to merge before saving
|
|
1397
|
+
* @param options - Save options
|
|
1398
|
+
* @returns The model instance for method chaining
|
|
1399
|
+
*
|
|
1400
|
+
* @throws {ValidationError} If validation fails
|
|
1401
|
+
* @throws {Error} If database operation fails
|
|
1402
|
+
*
|
|
1403
|
+
* @example
|
|
1404
|
+
* ```typescript
|
|
1405
|
+
* // Simple save
|
|
1406
|
+
* const user = new User({ name: "Alice" });
|
|
1407
|
+
* await user.save();
|
|
1408
|
+
*
|
|
1409
|
+
* // Merge data before saving
|
|
1410
|
+
* await user.save({ age: 31, email: "alice@example.com" });
|
|
1411
|
+
*
|
|
1412
|
+
* // Silent save (no events)
|
|
1413
|
+
* await user.save(null, { skipEvents: true });
|
|
1414
|
+
*
|
|
1415
|
+
* // Skip validation
|
|
1416
|
+
* await user.save(null, { skipValidation: true });
|
|
1417
|
+
*
|
|
1418
|
+
* // Method chaining
|
|
1419
|
+
* await user.set("name", "Bob").save();
|
|
1420
|
+
* ```
|
|
1421
|
+
*/
|
|
1422
|
+
async save(options) {
|
|
1423
|
+
if (options?.merge) {
|
|
1424
|
+
this.merge(options.merge);
|
|
1425
|
+
}
|
|
1426
|
+
const writer = new databaseWriter.DatabaseWriter(this);
|
|
1427
|
+
await writer.save(options);
|
|
1428
|
+
return this;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Serialize the model data for storage in database
|
|
1432
|
+
*/
|
|
1433
|
+
serialize() {
|
|
1434
|
+
return this.self().getDataSource().driver.serialize(this.data);
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Deseriaze the given data
|
|
1438
|
+
*/
|
|
1439
|
+
static deserialize(data) {
|
|
1440
|
+
const deserializedData = this.getDataSource().driver.deserialize(data);
|
|
1441
|
+
const model = new this(deserializedData);
|
|
1442
|
+
model.isNew = false;
|
|
1443
|
+
return model;
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Convert the model into JSON
|
|
1447
|
+
*/
|
|
1448
|
+
toJSON() {
|
|
1449
|
+
const resource = this.self().resource;
|
|
1450
|
+
if (!resource) {
|
|
1451
|
+
const toJsonColumns = this.self().toJsonColumns;
|
|
1452
|
+
if (toJsonColumns && toJsonColumns.length > 0) {
|
|
1453
|
+
return this.only(toJsonColumns);
|
|
1454
|
+
}
|
|
1455
|
+
return this.data;
|
|
1456
|
+
}
|
|
1457
|
+
const resourceColumns = this.self().resourceColumns;
|
|
1458
|
+
const data = resourceColumns !== undefined && resourceColumns.length > 0
|
|
1459
|
+
? this.only(resourceColumns)
|
|
1460
|
+
: this.data;
|
|
1461
|
+
return new resource(data).toJSON();
|
|
1462
|
+
}
|
|
1463
|
+
}exports.Model=Model;//# sourceMappingURL=model.js.map
|