@warlock.js/cascade 4.15.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/cjs/index.cjs +494 -72
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/query-builder.contract.d.mts +7 -3
- package/esm/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs +4 -3
- package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts +21 -0
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.mjs +55 -22
- package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
- package/esm/errors/unsafe-filter.error.d.mts +33 -0
- package/esm/errors/unsafe-filter.error.d.mts.map +1 -0
- package/esm/errors/unsafe-filter.error.mjs +40 -0
- package/esm/errors/unsafe-filter.error.mjs.map +1 -0
- package/esm/errors/unsafe-raw-expression.error.d.mts +23 -0
- package/esm/errors/unsafe-raw-expression.error.d.mts.map +1 -0
- package/esm/errors/unsafe-raw-expression.error.mjs +28 -0
- package/esm/errors/unsafe-raw-expression.error.mjs.map +1 -0
- package/esm/index.d.mts +5 -1
- package/esm/index.mjs +5 -1
- package/esm/model/methods/accessor-methods.mjs +39 -1
- package/esm/model/methods/accessor-methods.mjs.map +1 -1
- package/esm/model/methods/delete-methods.mjs +3 -2
- package/esm/model/methods/delete-methods.mjs.map +1 -1
- package/esm/model/methods/query-methods.mjs +7 -4
- package/esm/model/methods/query-methods.mjs.map +1 -1
- package/esm/model/methods/serialization-methods.mjs +49 -4
- package/esm/model/methods/serialization-methods.mjs.map +1 -1
- package/esm/model/methods/write-methods.d.mts.map +1 -1
- package/esm/model/methods/write-methods.mjs +2 -4
- package/esm/model/methods/write-methods.mjs.map +1 -1
- package/esm/model/model.d.mts +48 -2
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs +61 -2
- package/esm/model/model.mjs.map +1 -1
- package/esm/model/model.types.d.mts +1 -1
- package/esm/query-builder/query-builder.d.mts +13 -1
- package/esm/query-builder/query-builder.d.mts.map +1 -1
- package/esm/query-builder/query-builder.mjs +28 -11
- package/esm/query-builder/query-builder.mjs.map +1 -1
- package/esm/remover/database-remover.d.mts.map +1 -1
- package/esm/remover/database-remover.mjs +1 -1
- package/esm/remover/database-remover.mjs.map +1 -1
- package/esm/utils/database-writer.utils.d.mts +1 -0
- package/esm/utils/database-writer.utils.d.mts.map +1 -1
- package/esm/utils/escape-regex.d.mts +67 -0
- package/esm/utils/escape-regex.d.mts.map +1 -0
- package/esm/utils/escape-regex.mjs +76 -0
- package/esm/utils/escape-regex.mjs.map +1 -0
- package/esm/utils/sanitize-filter.d.mts +26 -0
- package/esm/utils/sanitize-filter.d.mts.map +1 -0
- package/esm/utils/sanitize-filter.mjs +76 -0
- package/esm/utils/sanitize-filter.mjs.map +1 -0
- package/esm/writer/database-writer.d.mts +12 -0
- package/esm/writer/database-writer.d.mts.map +1 -1
- package/esm/writer/database-writer.mjs +26 -6
- package/esm/writer/database-writer.mjs.map +1 -1
- package/llms-full.txt +59 -4
- package/llms.txt +3 -3
- package/package.json +8 -8
- package/skills/README.md +3 -3
- package/skills/define-model/SKILL.md +17 -1
- package/skills/perform-atomic-ops/SKILL.md +4 -1
- package/skills/query-data/SKILL.md +38 -2
package/esm/model/model.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../../cascade/src/model/model.ts"],"sourcesContent":["import { type GenericObject } from \"@mongez/reinforcements\";\nimport type { ObjectValidator } from \"@warlock.js/seal\";\nimport type {\n DriverContract,\n PaginationOptions,\n PaginationResult,\n RawQueryResult,\n RemoverResult,\n UpdateOperations,\n WriterOptions,\n} from \"../contracts\";\nimport { QueryBuilderContract, WhereCallback, WhereObject, WhereOperator } from \"../contracts\";\nimport type { DataSource } from \"../data-source/data-source\";\nimport { DatabaseDirtyTracker } from \"../database-dirty-tracker\";\nimport type { ModelEventListener, ModelEventName } from \"../events/model-events\";\nimport { ModelEvents } from \"../events/model-events\";\nimport type { PivotOperations } from \"../relations/pivot-operations\";\nimport type { ModelSnapshot } from \"../relations/relation-hydrator\";\nimport { attachLoadedRelation, RelationLoader } from \"../relations/relation-loader\";\nimport type {\n LoadedRelationResult,\n PivotData,\n PivotIds,\n RelationDefinition,\n} from \"../relations/types\";\nimport { modelSync } from \"../sync/model-sync\";\nimport type { ModelSyncOperationContract } from \"../sync/types\";\nimport type { DeleteStrategy, StrictMode } from \"../types\";\nimport {\n decrementField,\n getBooleanField,\n getFieldValue,\n getNumberField,\n getOnlyFields,\n getStringField,\n hasField,\n incrementField,\n mergeFields,\n setFieldValue,\n unsetFields,\n} from \"./methods/accessor-methods\";\nimport { deleteOneRecord, deleteRecords, destroyModel } from \"./methods/delete-methods\";\nimport {\n checkHasChanges,\n checkIsDirty,\n getDirtyColumns,\n getDirtyColumnsWithValues,\n getRemovedColumns,\n} from \"./methods/dirty-methods\";\nimport {\n cloneModel,\n deepFreezeObject,\n hydrateModel,\n modelFromSnapshot,\n modelToSnapshot,\n replaceModelData,\n serializeModel,\n} from \"./methods/hydration-methods\";\nimport {\n emitModelEvent,\n offModelEvent,\n onceModelEvent,\n onModelEvent,\n} from \"./methods/instance-event-methods\";\nimport {\n applyDefaultsToModel,\n generateModelNextId,\n performAtomicDecrement,\n performAtomicIncrement,\n performAtomicUpdate,\n} from \"./methods/meta-methods\";\nimport { attachPivotRelation, detachPivotRelation, pivotRelation } from \"./methods/pivot-methods\";\nimport {\n buildNewQueryBuilder,\n buildQuery,\n countRecords,\n decreaseField,\n findAll,\n findAndReplaceRecord,\n findAndUpdateRecords,\n findById,\n findFirst,\n findLast,\n findLatest,\n findOneAndDeleteRecord,\n findOneAndUpdateRecord,\n increaseField,\n paginateRecords,\n performAtomic,\n resolveDataSource,\n updateById,\n} from \"./methods/query-methods\";\nimport { restoreAllRecords, restoreRecord } from \"./methods/restore-methods\";\nimport {\n addGlobalModelScope,\n addLocalModelScope,\n removeGlobalModelScope,\n removeLocalModelScope,\n} from \"./methods/scope-methods\";\nimport { modelToJSON } from \"./methods/serialization-methods\";\nimport {\n cleanupModelEvents,\n getGlobalEvents,\n getModelEvents,\n offStaticEvent,\n onceStaticEvent,\n onStaticEvent,\n} from \"./methods/static-event-methods\";\nimport {\n type CreateManyOptions,\n createManyRecords,\n createRecord,\n findOrCreateRecord,\n saveModel,\n upsertRecord,\n} from \"./methods/write-methods\";\nimport type {\n ChildModel,\n GlobalScopeDefinition,\n GlobalScopeOptions,\n LocalScopeCallback,\n ModelSchema,\n} from \"./model.types\";\nimport { getAllModelsFromRegistry, getModelFromRegistry } from \"./register-model\";\nexport type {\n ChildModel,\n GlobalScopeDefinition,\n GlobalScopeOptions,\n LocalScopeCallback,\n ModelSchema,\n ScopeTiming,\n} from \"./model.types\";\n\n/**\n * Base class that powers all Cascade models.\n *\n * Provides:\n * - Type-safe value accessors with dot-notation support (get, set, has, unset, merge)\n * - Automatic dirty tracking for efficient partial updates\n * - Lifecycle event hooks (saving, created, deleting, etc.)\n * - Integration with the data-source registry for multi-database support\n * - Support for both per-model and global event listeners\n *\n * @template TSchema - The shape of the model's underlying data\n *\n * @example\n * ```typescript\n * interface UserSchema {\n * id: number;\n * name: string;\n * email: string;\n * }\n *\n * class User extends Model<UserSchema> {\n * public static table = \"users\";\n * }\n *\n * const user = new User({ name: \"Alice\" });\n * user.set(\"email\", \"alice@example.com\");\n * console.log(user.hasChanges()); // true\n * ```\n */\nexport abstract class Model<TSchema extends ModelSchema = ModelSchema> {\n /**\n * The database table or collection name associated with this model.\n *\n * Must be defined by each concrete model subclass.\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static table = \"users\";\n * }\n * ```\n */\n public static table: string;\n\n /**\n * Resource for this model.\n * It is a class that holds a toJSON function\n * Called when the model is being converted to JSON (by calling toJSON or JSON.stringify(model))\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static resource = UserResource;\n * }\n * ```\n */\n public static resource?: any;\n\n /**\n * Resource columns\n * Define what columns should be sent to the resource (if any) when converting to JSON\n */\n public static resourceColumns?: string[];\n\n /**\n * JSON keys for this model.\n * This could be used if resource is not passed\n * It will select only these keys from the model\n * @example\n * ```typescript\n * class User extends Model {\n * public static toJsonColumns = [\"id\", \"name\"];\n * }\n * ```\n */\n public static toJsonColumns?: string[];\n\n /**\n * Data source reference for this model.\n *\n * Can be:\n * - A string name registered in the data-source registry\n * - A DataSource instance\n * - Undefined (falls back to the default data source)\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static dataSource = \"primary\";\n * }\n * ```\n */\n public static dataSource?: string | DataSource;\n\n /**\n * Query builder class\n */\n public static builder?: new (...args: any[]) => QueryBuilderContract<Model>;\n\n /**\n * Primary key field name used to identify records.\n *\n * @default \"id\"\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static primaryKey = \"_id\"; // MongoDB\n * }\n *\n * class Product extends Model {\n * public static primaryKey = \"id\"; // SQL\n * }\n * ```\n */\n public static primaryKey: string = \"id\";\n\n /**\n * Embeded fields when document is Being embeded\n */\n public static embed?: string[];\n\n /**\n * Validation and casting schema using @warlock.js/seal.\n *\n * Defines validation rules and data transformations for the model.\n * Used automatically during save operations.\n *\n * @example\n * ```typescript\n * import { v } from \"@warlock.js/seal\";\n *\n * class User extends Model {\n * public static schema = v.object({\n * name: v.string().required().trim(),\n * age: v.number().min(0).max(120),\n * email: v.string().email().required().toLowerCase(),\n * createdAt: v.date().default(() => new Date()),\n * });\n * }\n * ```\n */\n public static schema?: ObjectValidator;\n\n /**\n * Strict mode behavior for unknown fields.\n *\n * - `\"strip\"`: Remove unknown fields silently (default, recommended for APIs)\n * - `\"fail\"`: Throw validation error on unknown fields (strict validation)\n * - `\"allow\"`: Allow unknown fields to pass through (permissive)\n *\n * @default \"strip\"\n *\n * @example\n * ```typescript\n * import { Model, type StrictMode } from \"@warlock.js/cascade\";\n *\n * class User extends Model {\n * public static strictMode: StrictMode = \"fail\"; // Throw on unknown fields\n * }\n *\n * const user = new User({ name: \"Alice\", unknownField: \"value\" });\n * await user.save(); // DatabaseWriterValidationError: unknown field\n * ```\n */\n public static strictMode: StrictMode = \"strip\";\n\n /**\n * Auto-generate incremental `id` field on insert (NoSQL only).\n *\n * When enabled, the ID generator creates a sequential integer ID\n * separate from the database's native ID (_id for MongoDB).\n *\n * **Note:** SQL databases use native AUTO_INCREMENT and don't need this.\n *\n * @default true\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static autoGenerateId = true;\n * }\n *\n * const user = new User({ name: \"Alice\" });\n * await user.save();\n * console.log(user.get(\"_id\")); // ObjectId(\"...\") - MongoDB\n * console.log(user.get(\"id\")); // 1 - Generated\n * ```\n */\n public static autoGenerateId = true;\n\n /**\n * Initial ID value for the first record.\n *\n * If not set, defaults to 1 or uses `randomInitialId`.\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static initialId = 1000; // Start from 1000\n * }\n * ```\n */\n public static initialId?: number;\n\n /**\n * Randomly generate the initial ID.\n *\n * Can be:\n * - `true`: Generate random ID between 10000-499999\n * - Function: Custom random ID generator\n * - `false`: Use `initialId` or default to 1\n *\n * @default false\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static randomInitialId = true; // Random 10000-499999\n * }\n *\n * class Product extends Model {\n * public static randomInitialId = () => Math.floor(Math.random() * 1000000);\n * }\n * ```\n */\n public static randomInitialId?: boolean | (() => number);\n\n /**\n * Amount to increment ID by for each new record.\n *\n * If not set, defaults to 1 or uses `randomIncrement`.\n *\n * @default 1\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static incrementIdBy = 5; // Increment by 5\n * }\n * ```\n */\n public static incrementIdBy?: number = 1;\n\n /**\n * Randomly generate the increment amount.\n *\n * Can be:\n * - `true`: Generate random increment between 1-10\n * - Function: Custom random increment generator\n * - `false`: Use `incrementIdBy` or default to 1\n *\n * @default false\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static randomIncrement = true; // Random 1-10\n * }\n *\n * class Product extends Model {\n * public static randomIncrement = () => Math.floor(Math.random() * 100);\n * }\n * ```\n */\n public static randomIncrement?: boolean | (() => number);\n\n /**\n * Created at column name.\n */\n public static createdAtColumn?: string | false;\n\n /**\n * Updated at column name.\n */\n public static updatedAtColumn?: string | false;\n\n /**\n * Delete strategy for this model.\n *\n * Controls how models are deleted:\n * - `\"trash\"` - Moves to trash collection, then deletes\n * - `\"permanent\"` - Direct deletion (hard delete)\n * - `\"soft\"` - Sets deletedAt timestamp (soft delete)\n *\n * Can be overridden by destroy() options.\n * Falls back to data source default if not set.\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static deleteStrategy: DeleteStrategy = \"soft\";\n * }\n * ```\n */\n public static deleteStrategy?: DeleteStrategy;\n\n /**\n * Column name for soft delete timestamp.\n *\n * Used when delete strategy is \"soft\".\n *\n * @default \"deletedAt\"\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static deletedAtColumn = \"archivedAt\";\n * }\n * ```\n */\n public static deletedAtColumn: string | false = \"deletedAt\";\n\n /**\n * Trash table/collection name override.\n *\n * If not set, defaults to `{table}Trash` or data source default.\n * Used when delete strategy is \"trash\".\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static trashTable = \"userRecycleBin\";\n * }\n * ```\n */\n public static trashTable?: string;\n\n /**\n * Global scopes that are automatically applied to all queries.\n * These scopes are inherited by child models.\n *\n * Registration via `addGlobalScope` is per-subclass: the registering class\n * gets its own map (seeded with the entries inherited so far), so a scope\n * added on one model never leaks onto sibling models.\n */\n public static globalScopes = new Map<string, GlobalScopeDefinition>();\n\n /**\n * Local scopes that can be manually applied to queries.\n * These are reusable query snippets that developers opt into.\n *\n * Registration via `addLocalScope` is per-subclass, like `globalScopes`.\n */\n public static localScopes = new Map<string, LocalScopeCallback>();\n\n /**\n * Relation definitions for this model.\n *\n * Define relationships to other models using helper functions:\n * - `hasMany()` - One-to-many (User has many Posts)\n * - `hasOne()` - One-to-one (User has one Profile)\n * - `belongsTo()` - Inverse of hasMany/hasOne (Post belongs to User)\n * - `belongsToMany()` - Many-to-many with pivot table (User has many Roles)\n *\n * @example\n * ```typescript\n * import { hasMany, belongsTo, belongsToMany, hasOne } from \"@warlock.js/cascade\";\n *\n * class User extends Model {\n * public posts?: Post[]; // Optional: for TypeScript autocomplete\n *\n * static relations = {\n * posts: hasMany(\"Post\"),\n * profile: hasOne(\"Profile\"),\n * organization: belongsTo(\"Organization\"),\n * roles: belongsToMany(\"Role\", { pivot: \"user_roles\" }),\n * };\n * }\n *\n * // Usage:\n * const users = await User.query().with(\"posts\").get();\n * console.log(users[0].posts); // Post[]\n * ```\n */\n public static relations: Readonly<Record<string, RelationDefinition>> = {};\n\n /**\n * Flag indicating whether this model instance represents a new (unsaved) record.\n *\n * - `true`: The model has not been persisted to the database yet\n * - `false`: The model represents an existing database record\n *\n * This flag is used by the writer to determine whether to perform an insert or update.\n */\n public isNew = true;\n\n /**\n * The raw mutable data backing this model instance.\n *\n * All field accessors (get, set, merge, etc.) operate on this object.\n */\n public data: TSchema;\n\n /**\n * Dirty tracker that monitors changes to the model's data.\n *\n * Tracks:\n * - Which fields have been modified (dirty columns)\n * - Which fields have been removed\n * - Original vs. current values for each dirty field\n *\n * Used by the writer to generate efficient partial update payloads.\n */\n public readonly dirtyTracker: DatabaseDirtyTracker;\n\n /**\n * Model instance events.\n * Allows registering listeners for lifecycle events on this specific instance.\n */\n public events: ModelEvents<any> = new ModelEvents();\n\n /**\n * Map of loaded relations for this model instance.\n *\n * Populated automatically when using `with()` for eager loading,\n * or when calling `load()` for lazy loading.\n *\n * @example\n * ```typescript\n * const user = await User.query().with(\"posts\").first();\n * console.log(user.loadedRelations.get(\"posts\")); // Post[]\n *\n * // Also accessible as direct properties:\n * console.log(user.posts); // Post[]\\n * ```\n */\n public loadedRelations: Map<string, any> = new Map();\n\n /**\n * Column name for active status.\n */\n protected isActiveColumn = \"isActive\";\n\n /**\n * Constructs a new model instance with optional initial data.\n *\n * Initializes the dirty tracker with a snapshot of the provided data.\n *\n * @param initialData - Partial data to populate the model\n *\n * @example\n * ```typescript\n * const user = new User({ name: \"Alice\", email: \"alice@example.com\" });\n * ```\n */\n public constructor(initialData: Partial<TSchema> = {}) {\n this.data = initialData as TSchema;\n this.dirtyTracker = this.self().getDriver().getDirtyTracker(this.data);\n }\n\n /**\n * Lazily load one or more relations for this model instance.\n *\n * This method loads relations on-demand after the model has been fetched.\n * The loaded relations are attached directly to the model instance and\n * also stored in `loadedRelations` map.\n *\n * @param relations - Relation name(s) to load\n * @returns This model instance for chaining\n *\n * @example\n * ```typescript\n * const user = await User.first();\n *\n * // Load single relation\n * await user.load(\"posts\");\n * console.log(user.posts); // Post[]\n *\n * // Load multiple relations\n * await user.load(\"posts\", \"organization\");\n *\n * // Chain with other operations\n * const posts = await user.load(\"posts\").then(() => user.posts);\n * ```\n */\n public async load(...relations: string[]): Promise<this> {\n const ModelClass = this.constructor as ChildModel<Model>;\n const loader = new RelationLoader([this], ModelClass);\n await loader.load(relations);\n return this;\n }\n\n /**\n * Check if a relation has been loaded.\n *\n * @param relationName - Name of the relation to check\n * @returns True if the relation has been loaded\n *\n * @example\n * ```typescript\n * const user = await User.first();\n *\n * console.log(user.isLoaded(\"posts\")); // false\n * await user.load(\"posts\");\n * console.log(user.isLoaded(\"posts\")); // true\n * ```\n */\n\n public isLoaded(relationName: string): boolean {\n return this.loadedRelations.has(relationName);\n }\n\n /**\n * Set relation manually\n *\n * @param relationName\n * @param relationData\n */\n public setRelation(relationName: string, relationData: LoadedRelationResult): void {\n attachLoadedRelation(this, relationName, relationData);\n }\n\n /**\n * Get a loaded relation by name.\n *\n * Returns undefined if the relation has not been loaded.\n *\n * @param relationName - Name of the relation to get\n * @returns The loaded relation data, or undefined\n *\n * @example\n * ```typescript\n * const user = await User.query().with(\"posts\").first();\n *\n * const posts = user.getRelation<Post[]>(\"posts\");\n * console.log(posts?.length);\n * ```\n */\n public getRelation<TRelation = any>(relationName: string): TRelation | undefined {\n return this.loadedRelations.get(relationName) as TRelation | undefined;\n }\n\n /**\n * Get a model class by its name from the global registry.\n *\n * Models must be decorated with @RegisterModel() to be available in the registry.\n *\n * @param name - The model class name\n * @returns The model class or undefined if not found\n *\n * @example\n * ```typescript\n * const UserModel = Model.getModel(\"User\");\n * if (UserModel) {\n * const user = await UserModel.find(1);\n * }\n * ```\n */\n public static getModel(name: string) {\n return getModelFromRegistry(name);\n }\n\n /**\n * Get all registered models from the global registry.\n *\n * Only models decorated with @RegisterModel() will appear here.\n *\n * @returns A Map of all registered model classes by name\n *\n * @example\n * ```typescript\n * const allModels = Model.getAllModels();\n * for (const [name, ModelClass] of allModels) {\n * console.log(`Found model: ${name} with table: ${ModelClass.table}`);\n * }\n * ```\n */\n public static getAllModels() {\n return getAllModelsFromRegistry();\n }\n\n // ============================================================================\n // STATIC SYNC METHODS\n // ============================================================================\n\n /**\n * Create a sync operation for a single embedded document.\n *\n * When this model is updated, the target model's field\n * will be updated with the embedded data.\n *\n * @param TargetModel - Target model class that receives data\n * @param targetField - Field path in target model\n * @returns Sync operation for chaining configuration\n *\n * @example\n * ```typescript\n * // When Category updates, update Product.category\n * Category.sync(Product, \"category\");\n * ```\n */\n public static sync<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n TargetModel: ChildModel<Model>,\n targetField: string,\n ): ModelSyncOperationContract {\n return modelSync.sync(this, TargetModel, targetField);\n }\n\n /**\n * Create a sync operation for an array of embedded documents.\n *\n * When this model is updated, the corresponding element\n * in the target model's array field will be updated.\n *\n * @param TargetModel - Target model class that receives data\n * @param targetField - Array field path in target model\n * @returns Sync operation for chaining configuration\n *\n * @example\n * ```typescript\n * // When Tag updates, update Post.tags[i] where tags[i].id matches\n * Tag.syncMany(Post, \"tags\").identifyBy(\"id\");\n * ```\n */\n public static syncMany<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n TargetModel: ChildModel<Model>,\n targetField: string,\n ): ModelSyncOperationContract {\n return modelSync.syncMany(this, TargetModel, targetField);\n }\n\n /**\n * Get model id\n */\n public get id(): number | string {\n return this.get(\"id\");\n }\n\n /**\n * String-typed accessor for the model's primary id.\n *\n * The underlying `id` field is `string | number` (MongoDB's ObjectId-as-string\n * vs SQL's auto-increment integer), which forces consumers to write\n * `string | number` everywhere they pass an id around. This getter narrows\n * the contract to `string` so callers can write functions that accept a\n * single id type without leaking the engine difference.\n *\n * The name `uuid` is historical — it does NOT validate or coerce the value\n * to a UUID. It simply returns the id, typed as a string.\n *\n * @example\n * ```typescript\n * function shareLink(modelId: string) { ... }\n * shareLink(user.uuid); // works regardless of whether the underlying id is\n * // a Mongo ObjectId string or a SQL integer\n * ```\n */\n public get uuid(): string {\n return this.get(\"id\");\n }\n\n /**\n * Retrieves a field value from the model's data.\n *\n * Supports both top-level keys and dot-notation paths for nested access.\n *\n * @param field - The field name or dot-notation path (e.g., \"address.city\")\n * @param defaultValue - Value to return if the field is missing\n * @returns The field value or the default value if not found\n *\n * @example\n * ```typescript\n * user.get(\"name\"); // \"Alice\"\n * user.get(\"address.city\", \"Unknown\"); // \"Unknown\" if address.city is missing\n * ```\n */\n public get<TKey extends keyof TSchema & string>(field: TKey): TSchema[TKey];\n public get<TKey extends keyof TSchema & string>(\n field: TKey,\n defaultValue: TSchema[TKey],\n ): TSchema[TKey];\n public get<Type extends unknown = any>(field: string): Type;\n public get<Type extends unknown = any>(field: string, defaultValue: Type): Type;\n public get(field: string, defaultValue?: unknown): any {\n return getFieldValue(this, field, defaultValue);\n }\n\n /**\n * Get only the values of the given fields\n */\n public only<TKey extends keyof TSchema & string>(fields: TKey[]): Record<TKey, TSchema[TKey]>;\n public only(fields: string[]): Record<string, unknown>;\n public only(fields: string[]): Record<string, unknown> {\n return getOnlyFields(this, fields);\n }\n\n /**\n * Get a string value\n */\n public string(key: string, defaultValue?: string): string | undefined {\n return getStringField(this, key, defaultValue);\n }\n\n /**\n * Get a number value\n */\n public number(key: string, defaultValue?: number): number | undefined {\n return getNumberField(this, key, defaultValue);\n }\n\n /**\n * Get a boolean value\n */\n public boolean(key: string, defaultValue?: boolean): boolean | undefined {\n return getBooleanField(this, key, defaultValue);\n }\n\n /**\n * Sets a field value in the model's data and marks it as dirty.\n *\n * Supports both top-level keys and dot-notation paths for nested assignment.\n * Automatically updates the dirty tracker to reflect the change.\n *\n * @param field - The field name or dot-notation path (e.g., \"address.city\")\n * @param value - The value to assign\n * @returns The model instance for method chaining\n *\n * @example\n * ```typescript\n * user.set(\"name\", \"Bob\").set(\"address.city\", \"NYC\");\n * ```\n */\n public set<TKey extends keyof TSchema & string>(field: TKey, value: TSchema[TKey]): this;\n public set(field: string, value: unknown): this;\n public set(field: string, value: unknown): this {\n return setFieldValue(this, field, value) as this;\n }\n\n /**\n * Checks whether a field exists in the model's data.\n *\n * Supports both top-level keys and dot-notation paths.\n *\n * @param field - The field name or dot-notation path\n * @returns `true` if the field exists, `false` otherwise\n *\n * @example\n * ```typescript\n * user.has(\"name\"); // true\n * user.has(\"address.zipCode\"); // false\n * ```\n */\n public has<TKey extends keyof TSchema & string>(field: TKey): boolean;\n public has(field: string): boolean;\n public has(field: string): boolean {\n return hasField(this, field);\n }\n\n /**\n * Increment the given field by the given amount\n */\n public increment<TKey extends keyof TSchema & string>(field: TKey, amount: number): this;\n public increment(field: string, amount?: number): this;\n public increment(field: string, amount?: number): this {\n return incrementField(this, field, amount) as this;\n }\n\n /**\n * Decrement the given field by the given amount\n */\n public decrement<TKey extends keyof TSchema & string>(field: TKey, amount: number): this;\n public decrement(field: string, amount?: number): this;\n public decrement(field: string, amount?: number): this {\n return decrementField(this, field, amount) as this;\n }\n\n /**\n * Removes one or more fields from the model's data and marks them as removed.\n *\n * Supports both top-level keys and dot-notation paths.\n * Automatically updates the dirty tracker to reflect the removal.\n *\n * @param fields - One or more field names or dot-notation paths to remove\n * @returns The model instance for method chaining\n *\n * @example\n * ```typescript\n * user.unset(\"tempField\", \"address.oldZip\");\n * ```\n */\n public unset(...fields: (keyof TSchema & string)[]): this;\n public unset(...fields: string[]): this;\n public unset(...fields: string[]): this {\n return unsetFields(this, ...fields) as this;\n }\n\n /**\n * Merges new values into the model's data and marks changed fields as dirty.\n *\n * Performs a deep merge, preserving existing nested structures.\n * Automatically updates the dirty tracker to reflect all changes.\n *\n * @param values - Partial data to merge into the model\n * @returns The model instance for method chaining\n *\n * @example\n * ```typescript\n * user.merge({ name: \"Charlie\", address: { city: \"LA\" } });\n * ```\n */\n public merge(values: Partial<TSchema>): this;\n public merge(values: Record<string, unknown>): this;\n public merge(values: Record<string, unknown>): this {\n return mergeFields(this, values) as this;\n }\n\n /**\n * Perform atomoic update from current model instance\n * Please note that it would require the id to be existing in the current\n * model instance\n * @returns number of affected records\n */\n public async atomicUpdate(operations: Record<string, unknown>): Promise<number> {\n return performAtomicUpdate(this, operations);\n }\n\n /**\n * Perform atomic increment\n * This would issue a query update and update the given field without\n * saving the model\n */\n public async atomicIncrement<T extends keyof TSchema & string>(\n field: T,\n amount: number = 1,\n ): Promise<number> {\n return performAtomicIncrement(this, field, amount);\n }\n\n /**\n * Perform atomic decrement\n * This would issue a query update and update the given field without\n * saving the model\n */\n public async atomicDecrement<T extends keyof TSchema & string>(\n field: T,\n amount: number = 1,\n ): Promise<number> {\n return performAtomicDecrement(this, field, amount);\n }\n\n /**\n * Determine if current model is active\n */\n public get isActive(): boolean {\n return this.get<boolean>(this.isActiveColumn);\n }\n\n /**\n * Get created at date\n */\n public get createdAt(): Date | undefined {\n const createdAtColumn = this.self().createdAtColumn;\n\n if (!createdAtColumn) return;\n\n return this.get<Date>(createdAtColumn);\n }\n\n /**\n * Get updated at date\n */\n public get updatedAt(): Date | undefined {\n const updatedAtColumn = this.self().updatedAtColumn;\n\n if (!updatedAtColumn) return;\n\n return this.get<Date>(updatedAtColumn);\n }\n\n /**\n * Check if current model record is created by the given user model\n */\n public isCreatedBy(user: Model | GenericObject): boolean {\n return this.get(`createdBy.id`) === user.id;\n }\n\n /**\n * Checks whether the model's data has changed since instantiation or last reset.\n *\n * @returns `true` if any fields have been modified or removed, `false` otherwise\n *\n * @example\n * ```typescript\n * const user = new User({ name: \"Alice\" });\n * user.hasChanges(); // false\n * user.set(\"name\", \"Bob\");\n * user.hasChanges(); // true\n * ```\n */\n public hasChanges(): boolean {\n return checkHasChanges(this);\n }\n\n /**\n * Check if the given column has been modified.\n *\n * @param column - The column name to check\n * @returns `true` if the column has been modified, `false` otherwise\n *\n * @example\n * ```typescript\n * user.set(\"name\", \"Bob\");\n * user.isDirty(\"name\"); // true\n * ```\n */\n public isDirty(column: string): boolean {\n return checkIsDirty(this, column);\n }\n\n /**\n * Retrieves all dirty columns with their old and new values.\n *\n * @returns A record mapping each dirty column to its previous and current value\n *\n * @example\n * ```typescript\n * user.set(\"name\", \"Bob\");\n * user.getDirtyColumnsWithValues();\n * // { name: { oldValue: \"Alice\", newValue: \"Bob\" } }\n * ```\n */\n public getDirtyColumnsWithValues(): Record<string, { oldValue: unknown; newValue: unknown }> {\n return getDirtyColumnsWithValues(this);\n }\n\n /**\n * Lists all columns that have been removed from the model's data.\n *\n * @returns An array of field names that were present initially but have been unset\n *\n * @example\n * ```typescript\n * user.unset(\"tempField\");\n * user.getRemovedColumns(); // [\"tempField\"]\n * ```\n */\n public getRemovedColumns(): string[] {\n return getRemovedColumns(this);\n }\n\n /**\n * Lists all columns that have been modified since instantiation or last reset.\n *\n * @returns An array of field names that have changed\n *\n * @example\n * ```typescript\n * user.set(\"name\", \"Bob\");\n * user.getDirtyColumns(); // [\"name\"]\n * ```\n */\n public getDirtyColumns(): string[] {\n return getDirtyColumns(this);\n }\n\n /**\n * Emits a lifecycle event to both per-model and global listeners.\n *\n * This method is public so that external services (like the writer) can trigger\n * lifecycle events when appropriate.\n *\n * @param event - The event name (e.g., \"saving\", \"created\", \"deleting\")\n * @param context - Optional context data to pass to listeners\n *\n * @example\n * ```typescript\n * await user.emitEvent(\"saving\");\n * await user.emitEvent(\"validated\", { errors: [] });\n * ```\n */\n public async emitEvent<TContext = unknown>(\n event: ModelEventName,\n context?: TContext,\n ): Promise<void> {\n return emitModelEvent(this, event, context);\n }\n\n /**\n * Register a listener for a model lifecycle event on this instance.\n *\n * @param event - Event name (e.g., \"saving\", \"updated\")\n * @param listener - Callback function\n * @returns Unsubscribe function\n */\n public on<TContext = unknown>(\n event: ModelEventName,\n listener: ModelEventListener<this, TContext>,\n ): () => void {\n return onModelEvent(this, event, listener as any);\n }\n\n /**\n * Register a one-time listener for a model lifecycle event on this instance.\n *\n * @param event - Event name\n * @param listener - Callback function\n * @returns Unsubscribe function\n */\n public once<TContext = unknown>(\n event: ModelEventName,\n listener: ModelEventListener<this, TContext>,\n ): () => void {\n return onceModelEvent(this, event, listener as any);\n }\n\n /**\n * Remove a listener from this instance.\n *\n * @param event - Event name\n * @param listener - Callback function to remove\n */\n public off<TContext = unknown>(\n event: ModelEventName,\n listener: ModelEventListener<this, TContext>,\n ): void {\n offModelEvent(this, event, listener as any);\n }\n\n /**\n * Resolves the data source associated with this model.\n *\n * Resolution order:\n * 1. If `dataSource` is a string, looks it up in the data-source registry\n * 2. If `dataSource` is a DataSource instance, returns it directly\n * 3. Otherwise, returns the default data source from the registry\n *\n * @returns The resolved DataSource instance\n * @throws Error if no data source is found\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static dataSource = \"primary\";\n * }\n *\n * const ds = User.getDataSource();\n * ```\n */\n public static getDataSource(): DataSource {\n return resolveDataSource(this as any);\n }\n\n /**\n * Get driver instance\n */\n public static getDriver(): DriverContract {\n return this.getDataSource().driver;\n }\n\n /**\n * Execute a raw query against this model's driver.\n *\n * Delegates to `getDriver().query`, so it inherits the driver's\n * transaction awareness: when called inside an active `transaction()`\n * scope the query auto-joins that transaction's client/session, otherwise\n * it runs on the pool. The return value is the driver's raw result\n * ({@link RawQueryResult}) — `rows` typed as `T` plus `rowCount` — not\n * hydrated model instances.\n *\n * Note: MongoDB drivers throw, as they do not support raw SQL.\n *\n * @typeParam T - The expected row shape.\n * @param sql - Raw SQL string (driver dialect).\n * @param params - Optional positional query parameters.\n * @returns The driver's raw query result.\n *\n * @example\n * ```typescript\n * const { rows } = await User.raw<{ id: number; total: number }>(\n * \"SELECT id, COUNT(*) AS total FROM orders WHERE user_id = $1 GROUP BY id\",\n * [userId],\n * );\n *\n * // Auto-joins the active transaction() scope:\n * await User.transaction(async () => {\n * await User.raw(\"UPDATE users SET active = true WHERE id = $1\", [id]);\n * });\n * ```\n */\n public static raw<T = Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n ): Promise<RawQueryResult<T>> {\n return this.getDriver().query<T>(sql, params);\n }\n\n /**\n * Generate next id and set it to current model's id\n */\n public async generateNextId(): Promise<number | string> {\n return generateModelNextId(this);\n }\n\n /**\n * Apply model defaults from data source configuration.\n *\n * This is called automatically by getDataSource() the first time\n * a model accesses its data source. Defaults are only applied if\n * the model doesn't already have its own value set.\n *\n * The hierarchy is:\n * 1. Model static property (highest priority - skipped here)\n * 2. Database config modelDefaults (passed here)\n * 3. Driver modelDefaults (merged before passing here)\n * 4. Framework defaults (fallback values in the code)\n *\n * @param defaults - Model default configuration from data source\n */\n public static applyModelDefaults(defaults: any): void {\n applyDefaultsToModel(this, defaults);\n }\n\n /**\n * Add a global scope that is automatically applied to all queries.\n *\n * Global scopes are inherited by child models and applied before query execution.\n * Use for security filters, multi-tenancy, soft deletes, etc.\n *\n * @param name - Unique name for the scope\n * @param callback - Function that modifies the query\n * @param options - Scope options (timing: 'before' | 'after')\n *\n * @example\n * ```typescript\n * // Multi-tenancy scope\n * Model.addGlobalScope('tenant', (query) => {\n * query.where('tenantId', getCurrentTenant());\n * }, { timing: 'before' });\n *\n * // Soft delete scope\n * User.addGlobalScope('notDeleted', (query) => {\n * query.whereNull('deletedAt');\n * });\n * ```\n */\n public static addGlobalScope(\n name: string,\n callback: (query: QueryBuilderContract) => void,\n options: GlobalScopeOptions = {},\n ): void {\n addGlobalModelScope(this as any, name, callback, options);\n }\n\n /**\n * Remove a global scope by name.\n *\n * @param name - Name of the scope to remove\n *\n * @example\n * ```typescript\n * Model.removeGlobalScope('tenant');\n * ```\n */\n public static removeGlobalScope(name: string): void {\n removeGlobalModelScope(this as any, name);\n }\n\n /**\n * Add a local scope that can be manually applied to queries.\n *\n * Local scopes are reusable query snippets that developers opt into.\n * They are not automatically applied.\n *\n * @param name - Unique name for the scope\n * @param callback - Function that modifies the query\n *\n * @example\n * ```typescript\n * // Define reusable scopes\n * User.addScope('active', (query) => {\n * query.where('isActive', true);\n * });\n *\n * User.addScope('admins', (query) => {\n * query.where('role', 'admin');\n * });\n *\n * // Use explicitly\n * await User.query().scope('active').get();\n * await User.query().scope('admins').get();\n * ```\n */\n public static addScope(name: string, callback: LocalScopeCallback): void {\n addLocalModelScope(this as any, name, callback);\n }\n\n /**\n * Remove a local scope by name.\n *\n * @param name - Name of the scope to remove\n *\n * @example\n * ```typescript\n * User.removeScope('active');\n * ```\n */\n public static removeScope(name: string): void {\n removeLocalModelScope(this as any, name);\n }\n\n /**\n * Create a new query builder for this model\n */\n public static query<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ): QueryBuilderContract<TModel> {\n return buildQuery(this, Model);\n }\n\n // Short hand for the query builder method with\n /**\n * Eagerly load one or more relations with the query results.\n *\n * Relations are loaded in separate optimized queries to prevent N+1 problems.\n * The loaded relations are attached to each model instance.\n *\n * @param relation - Single relation name to load\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * // Load single relation\n * const user = await User.query().with(\"posts\").find(1);\n * console.log(user.posts); // Post[]\n *\n * // Load multiple relations\n * const user = await User.query().with(\"posts\", \"organization\").find(1);\n *\n * // Load nested relations\n * const user = await User.query().with(\"posts.comments.author\").find(1);\n * ```\n */\n public static with<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relation: string,\n ): QueryBuilderContract<TModel>;\n\n /**\n * Eagerly load multiple relations.\n *\n * @param relations - Relation names to load\n * @returns Query builder for chaining\n */\n public static with<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...relations: string[]\n ): QueryBuilderContract<TModel>;\n\n /**\n * Eagerly load a relation with a constraint callback.\n *\n * The callback receives the relation query builder, allowing you to\n * add conditions, ordering, or limits to the related query.\n *\n * @param relation - Relation name to load\n * @param constraint - Callback to configure the relation query\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * const user = await User.query()\n * .with(\"posts\", (query) => {\n * query.where(\"isPublished\", true)\n * .orderBy(\"createdAt\", \"desc\")\n * .limit(5);\n * })\n * .find(1);\n * ```\n */\n public static with<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relation: string,\n constraint: (query: QueryBuilderContract) => void,\n ): QueryBuilderContract<TModel>;\n\n /**\n * Eagerly load multiple relations with constraints.\n *\n * Pass an object where keys are relation names and values are either:\n * - `true` to load without constraints\n * - A callback function to configure the relation query\n *\n * @param relations - Object mapping relation names to constraints\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * const user = await User.query()\n * .with({\n * posts: (query) => query.where(\"isPublished\", true),\n * organization: true,\n * roles: (query) => query.orderBy(\"priority\"),\n * })\n * .find(1);\n * ```\n */\n public static with<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relations: Record<string, boolean | ((query: QueryBuilderContract) => void)>,\n ): QueryBuilderContract<TModel>;\n\n public static with<TModel extends Model = Model>(this: ChildModel<TModel>, ...args: any[]) {\n return this.query().with(...args);\n }\n\n /**\n * Add a count of related records as a virtual field on each result row.\n *\n * Each relation produces a `${relationName}Count` column by default. Use\n * the `\"name as alias\"` shorthand or the object form to customise the\n * output alias or apply per-relation where-clause constraints.\n *\n * @param relation - Relation name (optionally with `as <alias>`)\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * const users = await User.withCount(\"posts\").get();\n * console.log(users[0].postsCount); // number\n * ```\n */\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relation: string,\n ): QueryBuilderContract<TModel>;\n\n /**\n * Add counts for multiple relations at once.\n *\n * @example\n * ```typescript\n * await User.withCount(\"posts\", \"comments\", \"followers\").get();\n * ```\n */\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...relations: string[]\n ): QueryBuilderContract<TModel>;\n\n /**\n * Add counts for multiple relations supplied as an array.\n */\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relations: string[],\n ): QueryBuilderContract<TModel>;\n\n /**\n * Add counts with optional per-relation constraints and alias overrides.\n *\n * Values may be `true`, an alias string, or a callback that applies\n * where-clauses inside the count subquery. Use the `as <alias>` shorthand\n * in the key to count the same relation more than once.\n *\n * @example\n * ```typescript\n * await Post.withCount({\n * comments: true,\n * \"comments as approvedCount\": (q) => q.where(\"approved\", true),\n * tags: \"tagCount\",\n * }).get();\n * ```\n */\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relations: Record<string, true | string | ((query: QueryBuilderContract) => void)>,\n ): QueryBuilderContract<TModel>;\n\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...args: any[]\n ): QueryBuilderContract<TModel> {\n return this.query().withCount(...args);\n }\n\n /**\n * Load relations using database JOINs in a single query.\n *\n * Unlike `with()` which uses separate queries, `joinWith()` uses\n * LEFT JOIN (SQL) or $lookup (MongoDB) to fetch related data\n * in a single query. The related data is hydrated into proper\n * model instances and attached to the main model.\n *\n * Best for: belongsTo and hasOne relations where you need\n * efficient single-query loading.\n *\n * @param relations - Relation names to load via JOIN\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * // Single relation\n * const post = await Post.joinWith(\"author\").first();\n * console.log(post.author); // User model instance\n * console.log(post.data); // { id, title, authorId } - no author data\n *\n * // Multiple relations\n * const post = await Post.joinWith(\"author\", \"category\").first();\n * ```\n */\n public static joinWith<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...relations: string[]\n ): QueryBuilderContract<TModel> {\n return this.query().joinWith(...relations);\n }\n\n /**\n * Create new query builder.\n *\n * If the model has a static `builder` property set to a query builder class,\n * it will be instantiated instead of the default driver query builder.\n *\n * @example\n * ```typescript\n * class UserQueryBuilder<T = User> extends MongoQueryBuilder<T> {\n * active() { return this.where(\"isActive\", true); }\n * }\n *\n * class User extends Model {\n * static builder = UserQueryBuilder; // That's it! ✨\n * }\n *\n * // Now User.query() returns UserQueryBuilder<User> with autocomplete!\n * ```\n */\n public static newQueryBuilder<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ): QueryBuilderContract<TModel> {\n return buildNewQueryBuilder(this);\n }\n\n /**\n * Get First matched record for the given filter\n */\n public static async first<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<TModel | null> {\n return findFirst(this, filter);\n }\n\n /**\n * Get last matched record for the given filter\n */\n public static async last<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<TModel | null> {\n return findLast(this, filter);\n }\n\n /**\n * Use where clause directly\n */\n public static where<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n field: string,\n value: unknown,\n ): QueryBuilderContract<TModel>;\n public static where<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n field: string,\n operator: WhereOperator,\n value: unknown,\n ): QueryBuilderContract<TModel>;\n public static where<TModel extends Model = Model>(\n this: (new (...args: any[]) => TModel) &\n Pick<typeof Model, \"query\" | \"getDataSource\" | \"table\">,\n conditions: WhereObject,\n ): QueryBuilderContract<TModel>;\n public static where<TModel extends Model = Model>(\n this: (new (...args: any[]) => TModel) &\n Pick<typeof Model, \"query\" | \"getDataSource\" | \"table\">,\n callback: WhereCallback<TModel>,\n ): QueryBuilderContract<TModel>;\n public static where<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...args: any[]\n ): QueryBuilderContract<TModel> {\n return (this.query().where as any)(...args);\n }\n\n /**\n * Count the number of records in the table\n * @param filter - The filter to apply to the query\n */\n public static count<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<number> {\n return countRecords(this, filter);\n }\n\n /**\n * Find record by id\n */\n public static async find<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n id: string | number,\n ): Promise<TModel | null> {\n return findById(this, id);\n }\n\n /**\n * Get all records from the table\n *\n * @param filter - The filter to apply to the query\n * @returns All records from the table\n */\n public static async all<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<TModel[]> {\n return findAll(this, filter);\n }\n\n /**\n * Perform pagination\n */\n public static async paginate<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n options: PaginationOptions & {\n filter?: Record<string, unknown>;\n } = {},\n ): Promise<PaginationResult<TModel>> {\n return paginateRecords(this, options);\n }\n\n /**\n * Get latest records from the table\n *\n * @param filter - The filter to apply to the query\n */\n public static async latest<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<TModel[]> {\n return findLatest(this, filter);\n }\n\n /**\n * Increment the given field by the given amount using atomic update\n *\n * @example ```typescript\n * // Increase age by 1 for user id 1\n * User.increment({id: 1}, \"age\", 1);\n * // Increase age by 1 and views by 2 for user id 1\n * User.increment({id: 1}, {age: 1, views: 2});\n * ```\n */\n public static increase<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n field: string,\n amount: number,\n ): Promise<number> {\n return increaseField(this, filter, field, amount);\n }\n\n /**\n * Decrement the given field by the given amount using atomic update\n * @example ```typescript\n * // Decrease age by 1 for user id 1\n * User.decrement({id: 1}, \"age\", 1);\n * // Decrease age by 1 and views by 2 for user id 1\n * User.decrement({id: 1}, {age: 1, views: 2});\n * ```\n */\n public static decrease<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n field: string,\n amount: number,\n ): Promise<number> {\n return decreaseField(this, filter, field, amount);\n }\n\n /**\n * Perform atomic operation\n * Example\n *\n * ```typescript\n * const user = await User.atomic({id: 1}, {$inc: {age: 1}})\n * Returns user model with updated age\n */\n public static async atomic<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n operations: UpdateOperations,\n ): Promise<number> {\n return performAtomic(this, filter, operations);\n }\n\n /**\n * Perform an atomic update for the given id\n */\n public static async update<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n id: string | number,\n data: Record<string, unknown>,\n ): Promise<number> {\n return updateById(this, id, data);\n }\n\n /**\n * Find one and update multiple records that matches the provided filter and return the updated record\n * @param filter - Filter conditions\n * @param update - Update operations ($set, $unset, $inc)\n * @returns The updated records\n */\n public static async findAndUpdate<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n update: UpdateOperations,\n ): Promise<TModel[]> {\n return findAndUpdateRecords(this, filter, update);\n }\n\n /**\n * Find one and update a single record that matches the provided filter and return the updated record\n * @param filter - Filter conditions\n * @param update - Update operations ($set, $unset, $inc)\n * @returns The updated record or null\n */\n public static async findOneAndUpdate<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n update: UpdateOperations,\n ): Promise<TModel | null> {\n return findOneAndUpdateRecord(this, filter, update);\n }\n\n /**\n * Find and replace the entire document that matches the provided filter and return the replaced document\n */\n public static async findAndReplace<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n document: Record<string, unknown>,\n ): Promise<TModel | null> {\n return findAndReplaceRecord(this, filter, document);\n }\n\n /**\n * Destroy (delete) the current model instance from the database.\n *\n * Emits lifecycle events:\n * - `deleting` - Before deletion\n * - `deleted` - After successful deletion\n *\n * @param options - Destroy options (strategy override, skipEvents)\n * @throws {Error} If the model is new (not saved) or if deletion fails\n *\n * @example\n * ```typescript\n * const user = await User.find(1);\n * await user.destroy(); // Uses default strategy\n * await user.destroy({ strategy: \"permanent\" }); // Override strategy\n * await user.destroy({ skipEvents: true }); // Silent delete\n * ```\n */\n public async destroy(options?: {\n strategy?: DeleteStrategy;\n skipEvents?: boolean;\n }): Promise<RemoverResult> {\n return destroyModel(this, options);\n }\n\n /**\n * Attach one or more related records to a `belongsToMany` pivot table.\n *\n * Thin wrapper over `createPivotOperations(this, relation).attach(ids, pivotData)`.\n * Throws if the named relation is not a `belongsToMany` relation.\n *\n * @example\n * ```typescript\n * await post.attach(\"tags\", [1, 2, 3]);\n * await post.attach(\"tags\", [4], { addedBy: currentUserId });\n * ```\n */\n public async attach(relation: string, ids: PivotIds, pivotData?: PivotData): Promise<void> {\n return attachPivotRelation(this, relation, ids, pivotData);\n }\n\n /**\n * Detach related records from a `belongsToMany` pivot table. Omit `ids`\n * to detach every row for this side of the relation.\n *\n * Thin wrapper over `createPivotOperations(this, relation).detach(ids)`.\n * Throws if the named relation is not a `belongsToMany` relation.\n *\n * @example\n * ```typescript\n * await post.detach(\"tags\", [2]);\n * await post.detach(\"tags\"); // detach all\n * ```\n */\n public async detach(relation: string, ids?: PivotIds): Promise<void> {\n return detachPivotRelation(this, relation, ids);\n }\n\n /**\n * Get the pivot-operations handle for a `belongsToMany` relation.\n *\n * Returns a `PivotOperations` object exposing `attach` / `detach` /\n * `sync` / `toggle` for the named relation's pivot table. Routing every\n * pivot mutation through `model.pivot(relation)` keeps the join-table\n * `sync` distinct from `Model.sync(Target, field)` (the denormalization\n * feature). Throws if the named relation is not a `belongsToMany` relation.\n *\n * @example\n * ```typescript\n * await post.pivot(\"tags\").attach([1, 2, 3]);\n * await post.pivot(\"tags\").sync([1, 3, 5]); // replace the whole set\n * await post.pivot(\"tags\").toggle([1, 7]); // flip each\n * ```\n */\n public pivot(relation: string): PivotOperations {\n return pivotRelation(this, relation);\n }\n\n /**\n * Get the class constructor from an instance.\n *\n * This helper method allows instance methods to access static properties\n * and methods of the model class in a type-safe way.\n *\n * @returns The model class constructor\n *\n * @example\n * ```typescript\n * const constructor = this.self();\n * const table = constructor.table;\n * await constructor.deleteOne({ id: 1 });\n * ```\n */\n public self<TModel extends Model = this>(): ChildModel<TModel> {\n return this.constructor as any as ChildModel<TModel>;\n }\n\n /**\n * Creates an immutable clone of the model with its current state.\n *\n * The cloned model:\n * - Contains a deep copy of all current data\n * - Has frozen (immutable) data that cannot be modified\n * - Preserves the `isNew` flag from the original\n * - Has no dirty changes (clean state)\n * - Cannot be saved or modified\n *\n * This is useful for:\n * - Creating snapshots of model state\n * - Passing read-only model data to other parts of the application\n * - Preventing accidental mutations\n * - Maintaining historical records\n *\n * @returns A new immutable model instance with the current state\n *\n * @example\n * ```typescript\n * const user = new User({ name: \"Alice\", email: \"alice@example.com\" });\n * await user.save();\n *\n * // Create an immutable snapshot\n * const snapshot = user.clone();\n *\n * // This will throw an error because the clone is immutable\n * snapshot.set(\"name\", \"Bob\"); // TypeError: Cannot assign to read only property\n *\n * // Original can still be modified\n * user.set(\"name\", \"Bob\");\n * await user.save();\n * ```\n */\n public clone(): this {\n return cloneModel(this);\n }\n\n /**\n * Recursively freezes an object and all its nested properties.\n *\n * @param obj - The object to freeze\n * @returns The frozen object\n */\n public deepFreeze<T>(obj: T): T {\n return deepFreezeObject(obj);\n }\n\n /**\n * Get table name\n */\n public getTableName(): string {\n return this.self().table;\n }\n\n /**\n * Get primary key name\n */\n public getPrimaryKey(): string {\n return this.self().primaryKey;\n }\n\n /**\n * Get model schema\n */\n public getSchema() {\n return this.self().schema;\n }\n\n /**\n * Check if schema has the given key\n */\n public schemaHas(key: string): boolean {\n return this.self().schema?.schema[key] !== undefined;\n }\n\n /**\n * Get strict mode\n */\n public getStrictMode(): StrictMode {\n return this.self().strictMode;\n }\n\n /**\n * Get data source (Connection)\n */\n public getConnection(): DataSource {\n return this.self().getDataSource();\n }\n\n /**\n * Delete all matching documents from the table.\n */\n public static async delete<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<number> {\n return deleteRecords(this, filter);\n }\n\n /**\n * Delete a single matching document from the table.\n */\n public static async deleteOne<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<number> {\n return deleteOneRecord(this, filter);\n }\n\n /**\n * Restore a single deleted record by its ID.\n *\n * Automatically detects whether the record was deleted via \"trash\" or \"soft\" strategy.\n * Handles ID conflicts based on options.\n *\n * @param id - The primary key value of the record to restore\n * @param options - Restorer options (onIdConflict, skipEvents)\n * @returns The restored model instance\n *\n * @throws {Error} If record not found in trash or soft-deleted records\n * @throws {Error} If ID conflict and onIdConflict is \"fail\"\n *\n * @example\n * ```typescript\n * // Restore with default options (assign new ID if conflict)\n * const user = await User.restore(123);\n *\n * // Restore and fail if ID conflict\n * const user = await User.restore(123, { onIdConflict: \"fail\" });\n *\n * // Silent restore (skip events)\n * const user = await User.restore(123, { skipEvents: true });\n * ```\n */\n public static async restore<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n id: string | number,\n options?: {\n onIdConflict?: \"fail\" | \"assignNew\";\n skipEvents?: boolean;\n },\n ): Promise<TModel> {\n return restoreRecord(this, id, options);\n }\n\n /**\n * Restore all deleted records for the model's table.\n *\n * Restores all records from the trash table (if using trash strategy)\n * or all soft-deleted records (if using soft strategy).\n *\n * @param options - Restorer options (onIdConflict, skipEvents)\n * @returns Array of restored model instances\n *\n * @example\n * ```typescript\n * // Restore all with default options\n * const users = await User.restoreAll();\n *\n * // Restore all and fail on any ID conflict\n * const users = await User.restoreAll({ onIdConflict: \"fail\" });\n * ```\n */\n public static async restoreAll<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n options?: {\n onIdConflict?: \"fail\" | \"assignNew\";\n skipEvents?: boolean;\n },\n ): Promise<TModel[]> {\n return restoreAllRecords(this, options);\n }\n\n /**\n * Create a new record in database and return the model instance.\n *\n * The data type is automatically inferred from the model's schema type.\n *\n * @param data - Partial data matching the model's schema type\n * @returns The created model instance\n *\n * @example\n * ```typescript\n * // TypeScript automatically infers UserSchema from User model\n * const user = await User.create({\n * name: \"Alice\",\n * email: \"alice@example.com\",\n * age: 30\n * });\n * // Type: User (with UserSchema inferred)\n * ```\n */\n public static async create<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(this: ChildModel<TModel>, data: Partial<TSchema>): Promise<TModel> {\n return createRecord(this, data);\n }\n\n /**\n * Create many records and return an array of created models.\n *\n * The work is always chunked into batches of `options.batchSize`\n * (default 500) so a huge input array can't overwhelm the driver.\n *\n * **Default path** (no options, or `bulk` falsy): each row is persisted\n * through `save()`, preserving model hooks, lifecycle events, casts and\n * generated ids. Each chunk runs under a single `Promise.all`.\n *\n * **Bulk path** (`bulk: true`): each chunk is routed to the driver's native\n * multi-row insert (`insertMany`) for 10–100× throughput. This SKIPS the\n * per-row save lifecycle (no `saving` / `creating` / `created` / `saved`\n * events, hooks, or sync). Casts, timestamps, defaults and id-generation are\n * still applied (rows are prepped through the same writer pipeline) so the\n * persisted columns match the default path.\n *\n * An empty array is a no-op that never touches the driver.\n *\n * @param data - The rows to insert.\n * @param options - Chunking / bulk options.\n * @returns The created model instances.\n *\n * @example\n * ```typescript\n * // Default: per-row save(), chunked in batches of 500\n * const users = await User.createMany(rows);\n *\n * // Bulk: one multi-row INSERT per 1000-row chunk (skips per-row hooks)\n * const users = await User.createMany(rows, { bulk: true, batchSize: 1000 });\n * ```\n */\n public static async createMany<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(\n this: ChildModel<TModel>,\n data: Partial<TSchema>[],\n options?: CreateManyOptions,\n ): Promise<TModel[]> {\n return createManyRecords(this, data, options);\n }\n\n /**\n * Find a record or create it if not found.\n *\n * Does NOT update existing records - returns them as-is.\n * Useful when you want to ensure a record exists without modifying it.\n *\n * @param filter - Conditions to find by\n * @param data - Data to create if not found (merged with filter)\n * @returns Promise resolving to found or created model\n *\n * @example\n * ```typescript\n * // Ensure default admin exists (don't modify if exists)\n * const admin = await User.findOrCreate(\n * { email: \"admin@example.com\" },\n * { email: \"admin@example.com\", name: \"Admin\", role: \"admin\" }\n * );\n * // If admin exists, returns existing (password unchanged)\n * // If not found, creates new admin\n * ```\n */\n public static async findOrCreate<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(this: ChildModel<TModel>, filter: Partial<TSchema>, data: Partial<TSchema>): Promise<TModel> {\n return findOrCreateRecord(this, filter, data);\n }\n\n /**\n * Upsert (insert or update) a record atomically.\n *\n * Uses the driver's native upsert operation for atomic insert-or-update.\n * More efficient than updateOrCreate as it's a single database operation.\n *\n * Includes full Model features:\n * - ID generation (if creating)\n * - createdAt timestamp (if creating)\n * - updatedAt timestamp (always)\n * - Validation & casting\n * - Lifecycle events\n *\n * @param filter - Conditions to find by (used for conflict detection)\n * @param data - Data to update or create (merged with filter)\n * @param options - Upsert options (conflictColumns for PostgreSQL, etc.)\n * @returns Promise resolving to upserted model\n *\n * @example\n * ```typescript\n * // PostgreSQL: upsert on unique email\n * const user = await User.upsert(\n * { email: \"user@example.com\" },\n * {\n * email: \"user@example.com\",\n * name: \"John Updated\",\n * lastSyncedAt: new Date()\n * },\n * { conflictColumns: [\"email\"] }\n * );\n *\n * // MongoDB: upsert by filter\n * const user = await User.upsert(\n * { externalId: \"ext-123\" },\n * {\n * externalId: \"ext-123\",\n * name: \"John Updated\",\n * email: \"john.new@example.com\"\n * }\n * );\n * ```\n */\n public static async upsert<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(\n this: ChildModel<TModel>,\n filter: Partial<TSchema>,\n data: Partial<TSchema>,\n options?: Record<string, unknown>,\n ): Promise<TModel> {\n return upsertRecord(this, filter, data, options);\n }\n\n /**\n * Update a record or create it if not found (upsert).\n *\n * @deprecated Use `upsert()` instead for better performance and atomicity.\n * This method is kept for backward compatibility but uses upsert internally.\n *\n * @param filter - Conditions to find by\n * @param data - Data to update or create (merged with filter)\n * @returns Promise resolving to updated or created model\n */\n public static async updateOrCreate<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(\n this: ChildModel<TModel>,\n filter: Partial<TSchema>,\n data: Partial<TSchema>,\n options?: Record<string, unknown>,\n ): Promise<TModel> {\n // Use upsert internally for better performance\n return await (this as any).upsert(filter, data, options);\n }\n\n /**\n * Find one and delete a record that matches the filter and return the deleted record.\n *\n * @param filter - Filter conditions\n * @param options - Optional delete options\n * @returns The deleted model instance or null if not found\n *\n * @example\n * ```typescript\n * const deleted = await User.findOneAndDelete({ id: 1 });\n * if (deleted) {\n * console.log('Deleted user:', deleted.get('name'));\n * }\n * ```\n */\n public static async findOneAndDelete<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<TModel | null> {\n return findOneAndDeleteRecord(this, filter, options);\n }\n\n /**\n * Returns embedded data for sync operations.\n * Excludes internal MongoDB fields and ensures proper date serialization.\n *\n * @returns Embedded data object suitable for syncing\n *\n * @example\n * ```typescript\n * const user = await User.find(1);\n * const embedData = user.embedData;\n * // Returns: { id: 1, name: \"Alice\", email: \"alice@example.com\", ... }\n * // Excludes: _id\n * ```\n */\n public get embedData(): Record<string, unknown> {\n return this.self().embed ? this.only(this.self().embed as any) : this.data;\n }\n\n /**\n * Tear down framework-level registrations attached to this Model class.\n *\n * Called by Warlock's HMR machinery when a model file (or any file in its\n * dependency graph) is reloaded. Removes the event listeners and registry\n * entries the Model installed at module-load time so the reloaded class\n * does not double-register.\n *\n * The `$` prefix marks this as framework-internal — application code should\n * not call this. It is part of the public surface only because the HMR\n * system needs to invoke it from outside the Model class.\n *\n * @internal\n */\n public static $cleanup() {\n cleanupModelEvents(this);\n }\n\n /**\n * Accesses the event emitter dedicated to this model constructor.\n *\n * Each model subclass gets its own isolated event emitter, allowing you to\n * register lifecycle hooks that only apply to that specific model.\n *\n * @returns The ModelEvents instance for this model constructor\n *\n * @example\n * ```typescript\n * User.events().onSaving((user) => {\n * console.log(\"User is being saved:\", user);\n * });\n * ```\n */\n public static events<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ): ModelEvents<TModel> {\n return getModelEvents<TModel>(this);\n }\n\n /**\n * Registers an event listener for this model constructor.\n *\n * Convenience shorthand for `Model.events().on(...)`.\n *\n * @param event - The event name (e.g., \"saving\", \"created\")\n * @param listener - The callback to invoke when the event fires\n * @returns An unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = User.on(\"saving\", (user) => {\n * console.log(\"Saving user:\", user);\n * });\n * ```\n */\n public static on<TModel extends Model = Model, TContext = unknown>(\n this: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n ): () => void {\n return onStaticEvent<TModel, TContext>(this, event, listener);\n }\n\n /**\n * Registers a one-time event listener for this model constructor.\n *\n * The listener will automatically unsubscribe after its first invocation.\n * Convenience shorthand for `Model.events().once(...)`.\n *\n * @param event - The event name (e.g., \"saving\", \"created\")\n * @param listener - The callback to invoke when the event fires\n * @returns An unsubscribe function\n *\n * @example\n * ```typescript\n * User.once(\"created\", (user) => {\n * console.log(\"First user created:\", user);\n * });\n * ```\n */\n public static once<TModel extends Model = Model, TContext = unknown>(\n this: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n ): () => void {\n return onceStaticEvent<TModel, TContext>(this, event, listener);\n }\n\n /**\n * Removes an event listener from this model constructor.\n *\n * Convenience shorthand for `Model.events().off(...)`.\n *\n * @param event - The event name\n * @param listener - The callback to remove\n *\n * @example\n * ```typescript\n * const listener = (user) => console.log(user);\n * User.on(\"saving\", listener);\n * User.off(\"saving\", listener);\n * ```\n */\n public static off<TModel extends Model = Model, TContext = unknown>(\n this: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n ): void {\n offStaticEvent<TModel, TContext>(this, event, listener);\n }\n\n /**\n * Accesses the global event emitter shared by all model instances.\n *\n * Use this for cross-cutting concerns like auditing, logging, or injecting\n * common fields (e.g., `createdBy`, `updatedBy`) across all models.\n *\n * @returns The global ModelEvents instance\n *\n * @example\n * ```typescript\n * Model.globalEvents().onSaving((model) => {\n * model.set(\"updatedAt\", new Date());\n * });\n * ```\n */\n public static globalEvents(): ModelEvents<Model> {\n return getGlobalEvents();\n }\n\n /**\n * Replace the model's data entirely.\n *\n * Used internally by the writer after validation to update the model\n * with validated/casted data.\n *\n * **Warning:** This replaces all data and updates the dirty tracker.\n * Use with caution in application code.\n *\n * @param data - New data to replace current data\n *\n * @example\n * ```typescript\n * // Internal usage by writer\n * model.replaceData(validatedData);\n * ```\n */\n public replaceData(data: Record<string, unknown>): void {\n replaceModelData(this, data);\n }\n\n /**\n * Save the model to the database.\n *\n * Performs insert if `isNew === true`, otherwise performs update.\n * Automatically validates, casts, generates IDs, and emits lifecycle events.\n *\n * **Features:**\n * - Validation via @warlock.js/seal schema\n * - Data casting (string → number, etc.)\n * - ID generation (NoSQL only)\n * - Partial updates (only changed fields)\n * - Lifecycle events (validating, saving, created/updated, saved)\n *\n * @param data - Optional data to merge before saving\n * @param options - Save options\n * @returns The model instance for method chaining\n *\n * @throws {ValidationError} If validation fails\n * @throws {Error} If database operation fails\n *\n * @example\n * ```typescript\n * // Simple save\n * const user = new User({ name: \"Alice\" });\n * await user.save();\n *\n * // Merge data before saving\n * await user.save({ age: 31, email: \"alice@example.com\" });\n *\n * // Silent save (no events)\n * await user.save(null, { skipEvents: true });\n *\n * // Skip validation\n * await user.save(null, { skipValidation: true });\n *\n * // Method chaining\n * await user.set(\"name\", \"Bob\").save();\n * ```\n */\n public async save(options?: WriterOptions & { merge?: Partial<TSchema> }): Promise<this> {\n return saveModel(this as any, options) as Promise<this>;\n }\n\n /**\n * Serialize the model data for storage in the database.\n *\n * Uses the driver's `serialize` to apply driver-specific type transformations\n * (e.g. Date → ISO string, BigInt → string for Postgres).\n *\n * **Not** the same as `toSnapshot` — this is a DB write concern, not a cache concern.\n */\n public serialize() {\n return serializeModel(this);\n }\n\n /**\n * Produce a plain-object snapshot of this model suitable for cache storage.\n *\n * - `data`: The model's own fields, serialized via the driver (handles Dates, BigInt, ObjectId).\n * - `relations`: Each entry in `loadedRelations` recursively snapshotted via `toSnapshot`.\n * A relation that was loaded but resolved to `null` is stored as `null` (not omitted),\n * so that `fromSnapshot` can distinguish \"loaded + null\" from \"never loaded\".\n *\n * Use `Model.fromSnapshot(snapshot)` to reconstruct.\n *\n * @example\n * ```typescript\n * await cache.set(key, chat.toSnapshot());\n * ```\n */\n public toSnapshot(): ModelSnapshot {\n return modelToSnapshot(this);\n }\n\n /**\n * Reconstruct a model instance (with relations) from a cache snapshot.\n *\n * Counterpart to `toSnapshot`. Applies driver deserialization (e.g. ISO string → Date)\n * and recursively hydrates any nested relation snapshots via `RelationHydrator`.\n *\n * @example\n * ```typescript\n * const snapshot = await cache.get(key);\n * const chat = Chat.fromSnapshot(snapshot);\n * chat.unit; // Unit model instance, fully hydrated\n * ```\n */\n public static fromSnapshot<TModel extends Model>(\n this: ChildModel<TModel>,\n snapshot: ModelSnapshot,\n ): TModel {\n return modelFromSnapshot(this, snapshot);\n }\n\n /**\n * Create a model instance from raw data (no relations).\n *\n * This is the data-only hydration path, used by the query builder when\n * converting a raw DB row into a model instance. Relations are NOT restored\n * here — use `fromSnapshot` when restoring from a cache snapshot that\n * includes relation data.\n *\n * @example\n * ```typescript\n * // Query builder internals:\n * const user = User.hydrate(rawRow);\n * ```\n */\n public static hydrate<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n data: Record<string, unknown>,\n ): TModel {\n return hydrateModel(this, data);\n }\n\n /**\n * Convert the model into JSON\n */\n public toJSON() {\n return modelToJSON(this);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../../cascade/src/model/model.ts"],"sourcesContent":["import { type GenericObject } from \"@mongez/reinforcements\";\nimport type { ObjectValidator } from \"@warlock.js/seal\";\nimport type {\n DriverContract,\n PaginationOptions,\n PaginationResult,\n RawQueryResult,\n RemoverResult,\n UpdateOperations,\n WriterOptions,\n} from \"../contracts\";\nimport { QueryBuilderContract, WhereCallback, WhereObject, WhereOperator } from \"../contracts\";\nimport type { DataSource } from \"../data-source/data-source\";\nimport { DatabaseDirtyTracker } from \"../database-dirty-tracker\";\nimport type { ModelEventListener, ModelEventName } from \"../events/model-events\";\nimport { ModelEvents } from \"../events/model-events\";\nimport type { PivotOperations } from \"../relations/pivot-operations\";\nimport type { ModelSnapshot } from \"../relations/relation-hydrator\";\nimport { attachLoadedRelation, RelationLoader } from \"../relations/relation-loader\";\nimport type {\n LoadedRelationResult,\n PivotData,\n PivotIds,\n RelationDefinition,\n} from \"../relations/types\";\nimport { modelSync } from \"../sync/model-sync\";\nimport type { ModelSyncOperationContract } from \"../sync/types\";\nimport type { DeleteStrategy, StrictMode } from \"../types\";\nimport {\n decrementField,\n getBooleanField,\n getFieldValue,\n getNumberField,\n getOnlyFields,\n getStringField,\n hasField,\n incrementField,\n mergeFields,\n setFieldValue,\n unsetFields,\n} from \"./methods/accessor-methods\";\nimport { deleteOneRecord, deleteRecords, destroyModel } from \"./methods/delete-methods\";\nimport {\n checkHasChanges,\n checkIsDirty,\n getDirtyColumns,\n getDirtyColumnsWithValues,\n getRemovedColumns,\n} from \"./methods/dirty-methods\";\nimport {\n cloneModel,\n deepFreezeObject,\n hydrateModel,\n modelFromSnapshot,\n modelToSnapshot,\n replaceModelData,\n serializeModel,\n} from \"./methods/hydration-methods\";\nimport {\n emitModelEvent,\n offModelEvent,\n onceModelEvent,\n onModelEvent,\n} from \"./methods/instance-event-methods\";\nimport {\n applyDefaultsToModel,\n generateModelNextId,\n performAtomicDecrement,\n performAtomicIncrement,\n performAtomicUpdate,\n} from \"./methods/meta-methods\";\nimport { attachPivotRelation, detachPivotRelation, pivotRelation } from \"./methods/pivot-methods\";\nimport {\n buildNewQueryBuilder,\n buildQuery,\n countRecords,\n decreaseField,\n findAll,\n findAndReplaceRecord,\n findAndUpdateRecords,\n findById,\n findFirst,\n findLast,\n findLatest,\n findOneAndDeleteRecord,\n findOneAndUpdateRecord,\n increaseField,\n paginateRecords,\n performAtomic,\n resolveDataSource,\n updateById,\n} from \"./methods/query-methods\";\nimport { restoreAllRecords, restoreRecord } from \"./methods/restore-methods\";\nimport {\n addGlobalModelScope,\n addLocalModelScope,\n removeGlobalModelScope,\n removeLocalModelScope,\n} from \"./methods/scope-methods\";\nimport { modelToJSON } from \"./methods/serialization-methods\";\nimport {\n cleanupModelEvents,\n getGlobalEvents,\n getModelEvents,\n offStaticEvent,\n onceStaticEvent,\n onStaticEvent,\n} from \"./methods/static-event-methods\";\nimport {\n type CreateManyOptions,\n createManyRecords,\n createRecord,\n findOrCreateRecord,\n saveModel,\n upsertRecord,\n} from \"./methods/write-methods\";\nimport type {\n ChildModel,\n GlobalScopeDefinition,\n GlobalScopeOptions,\n LocalScopeCallback,\n ModelSchema,\n} from \"./model.types\";\nimport { getAllModelsFromRegistry, getModelFromRegistry } from \"./register-model\";\nexport type {\n ChildModel,\n GlobalScopeDefinition,\n GlobalScopeOptions,\n LocalScopeCallback,\n ModelSchema,\n ScopeTiming,\n} from \"./model.types\";\n\n/**\n * Base class that powers all Cascade models.\n *\n * Provides:\n * - Type-safe value accessors with dot-notation support (get, set, has, unset, merge)\n * - Automatic dirty tracking for efficient partial updates\n * - Lifecycle event hooks (saving, created, deleting, etc.)\n * - Integration with the data-source registry for multi-database support\n * - Support for both per-model and global event listeners\n *\n * @template TSchema - The shape of the model's underlying data\n *\n * @example\n * ```typescript\n * interface UserSchema {\n * id: number;\n * name: string;\n * email: string;\n * }\n *\n * class User extends Model<UserSchema> {\n * public static table = \"users\";\n * }\n *\n * const user = new User({ name: \"Alice\" });\n * user.set(\"email\", \"alice@example.com\");\n * console.log(user.hasChanges()); // true\n * ```\n */\nexport abstract class Model<TSchema extends ModelSchema = ModelSchema> {\n /**\n * The database table or collection name associated with this model.\n *\n * Must be defined by each concrete model subclass.\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static table = \"users\";\n * }\n * ```\n */\n public static table: string;\n\n /**\n * Resource for this model.\n * It is a class that holds a toJSON function\n * Called when the model is being converted to JSON (by calling toJSON or JSON.stringify(model))\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static resource = UserResource;\n * }\n * ```\n */\n public static resource?: any;\n\n /**\n * Resource columns\n * Define what columns should be sent to the resource (if any) when converting to JSON\n */\n public static resourceColumns?: string[];\n\n /**\n * JSON keys for this model.\n * This could be used if resource is not passed\n * It will select only these keys from the model\n * @example\n * ```typescript\n * class User extends Model {\n * public static toJsonColumns = [\"id\", \"name\"];\n * }\n * ```\n */\n public static toJsonColumns?: string[];\n\n /**\n * Top-level fields that are ALWAYS stripped from `toJSON()` output —\n * regardless of `resource`, `resourceColumns` or `toJsonColumns`.\n *\n * `toJSON()` is invoked implicitly by `JSON.stringify(model)` (e.g.\n * `res.json(user)`), and with no `resource`/`toJsonColumns` configured it\n * returns the entire raw document. Declare credential/PII columns here so\n * they can never leak through serialization, whatever else is configured.\n *\n * @default []\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static hidden = [\"password\", \"resetToken\"];\n * }\n * ```\n */\n public static hidden: string[] = [];\n\n /**\n * Data source reference for this model.\n *\n * Can be:\n * - A string name registered in the data-source registry\n * - A DataSource instance\n * - Undefined (falls back to the default data source)\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static dataSource = \"primary\";\n * }\n * ```\n */\n public static dataSource?: string | DataSource;\n\n /**\n * Query builder class\n */\n public static builder?: new (...args: any[]) => QueryBuilderContract<Model>;\n\n /**\n * Primary key field name used to identify records.\n *\n * @default \"id\"\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static primaryKey = \"_id\"; // MongoDB\n * }\n *\n * class Product extends Model {\n * public static primaryKey = \"id\"; // SQL\n * }\n * ```\n */\n public static primaryKey: string = \"id\";\n\n /**\n * Embeded fields when document is Being embeded\n */\n public static embed?: string[];\n\n /**\n * Validation and casting schema using @warlock.js/seal.\n *\n * Defines validation rules and data transformations for the model.\n * Used automatically during save operations.\n *\n * @example\n * ```typescript\n * import { v } from \"@warlock.js/seal\";\n *\n * class User extends Model {\n * public static schema = v.object({\n * name: v.string().required().trim(),\n * age: v.number().min(0).max(120),\n * email: v.string().email().required().toLowerCase(),\n * createdAt: v.date().default(() => new Date()),\n * });\n * }\n * ```\n */\n public static schema?: ObjectValidator;\n\n /**\n * Strict mode behavior for unknown fields.\n *\n * - `\"strip\"`: Remove unknown fields silently (default, recommended for APIs)\n * - `\"fail\"`: Throw validation error on unknown fields (strict validation)\n * - `\"allow\"`: Allow unknown fields to pass through (permissive)\n *\n * @default \"strip\"\n *\n * @example\n * ```typescript\n * import { Model, type StrictMode } from \"@warlock.js/cascade\";\n *\n * class User extends Model {\n * public static strictMode: StrictMode = \"fail\"; // Throw on unknown fields\n * }\n *\n * const user = new User({ name: \"Alice\", unknownField: \"value\" });\n * await user.save(); // DatabaseWriterValidationError: unknown field\n * ```\n */\n public static strictMode: StrictMode = \"strip\";\n\n /**\n * Auto-generate incremental `id` field on insert (NoSQL only).\n *\n * When enabled, the ID generator creates a sequential integer ID\n * separate from the database's native ID (_id for MongoDB).\n *\n * **Note:** SQL databases use native AUTO_INCREMENT and don't need this.\n *\n * @default true\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static autoGenerateId = true;\n * }\n *\n * const user = new User({ name: \"Alice\" });\n * await user.save();\n * console.log(user.get(\"_id\")); // ObjectId(\"...\") - MongoDB\n * console.log(user.get(\"id\")); // 1 - Generated\n * ```\n */\n public static autoGenerateId = true;\n\n /**\n * Initial ID value for the first record.\n *\n * If not set, defaults to 1 or uses `randomInitialId`.\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static initialId = 1000; // Start from 1000\n * }\n * ```\n */\n public static initialId?: number;\n\n /**\n * Randomly generate the initial ID.\n *\n * Can be:\n * - `true`: Generate random ID between 10000-499999\n * - Function: Custom random ID generator\n * - `false`: Use `initialId` or default to 1\n *\n * @default false\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static randomInitialId = true; // Random 10000-499999\n * }\n *\n * class Product extends Model {\n * public static randomInitialId = () => Math.floor(Math.random() * 1000000);\n * }\n * ```\n */\n public static randomInitialId?: boolean | (() => number);\n\n /**\n * Amount to increment ID by for each new record.\n *\n * If not set, defaults to 1 or uses `randomIncrement`.\n *\n * @default 1\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static incrementIdBy = 5; // Increment by 5\n * }\n * ```\n */\n public static incrementIdBy?: number = 1;\n\n /**\n * Randomly generate the increment amount.\n *\n * Can be:\n * - `true`: Generate random increment between 1-10\n * - Function: Custom random increment generator\n * - `false`: Use `incrementIdBy` or default to 1\n *\n * @default false\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static randomIncrement = true; // Random 1-10\n * }\n *\n * class Product extends Model {\n * public static randomIncrement = () => Math.floor(Math.random() * 100);\n * }\n * ```\n */\n public static randomIncrement?: boolean | (() => number);\n\n /**\n * Created at column name.\n */\n public static createdAtColumn?: string | false;\n\n /**\n * Updated at column name.\n */\n public static updatedAtColumn?: string | false;\n\n /**\n * Delete strategy for this model.\n *\n * Controls how models are deleted:\n * - `\"trash\"` - Moves to trash collection, then deletes\n * - `\"permanent\"` - Direct deletion (hard delete)\n * - `\"soft\"` - Sets deletedAt timestamp (soft delete)\n *\n * Can be overridden by destroy() options.\n * Falls back to data source default if not set.\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static deleteStrategy: DeleteStrategy = \"soft\";\n * }\n * ```\n */\n public static deleteStrategy?: DeleteStrategy;\n\n /**\n * Column name for soft delete timestamp.\n *\n * Used when delete strategy is \"soft\".\n *\n * @default \"deletedAt\"\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static deletedAtColumn = \"archivedAt\";\n * }\n * ```\n */\n public static deletedAtColumn: string | false = \"deletedAt\";\n\n /**\n * Trash table/collection name override.\n *\n * If not set, defaults to `{table}Trash` or data source default.\n * Used when delete strategy is \"trash\".\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static trashTable = \"userRecycleBin\";\n * }\n * ```\n */\n public static trashTable?: string;\n\n /**\n * Global scopes that are automatically applied to all queries.\n * These scopes are inherited by child models.\n *\n * Registration via `addGlobalScope` is per-subclass: the registering class\n * gets its own map (seeded with the entries inherited so far), so a scope\n * added on one model never leaks onto sibling models.\n */\n public static globalScopes = new Map<string, GlobalScopeDefinition>();\n\n /**\n * Local scopes that can be manually applied to queries.\n * These are reusable query snippets that developers opt into.\n *\n * Registration via `addLocalScope` is per-subclass, like `globalScopes`.\n */\n public static localScopes = new Map<string, LocalScopeCallback>();\n\n /**\n * Relation definitions for this model.\n *\n * Define relationships to other models using helper functions:\n * - `hasMany()` - One-to-many (User has many Posts)\n * - `hasOne()` - One-to-one (User has one Profile)\n * - `belongsTo()` - Inverse of hasMany/hasOne (Post belongs to User)\n * - `belongsToMany()` - Many-to-many with pivot table (User has many Roles)\n *\n * @example\n * ```typescript\n * import { hasMany, belongsTo, belongsToMany, hasOne } from \"@warlock.js/cascade\";\n *\n * class User extends Model {\n * public posts?: Post[]; // Optional: for TypeScript autocomplete\n *\n * static relations = {\n * posts: hasMany(\"Post\"),\n * profile: hasOne(\"Profile\"),\n * organization: belongsTo(\"Organization\"),\n * roles: belongsToMany(\"Role\", { pivot: \"user_roles\" }),\n * };\n * }\n *\n * // Usage:\n * const users = await User.query().with(\"posts\").get();\n * console.log(users[0].posts); // Post[]\n * ```\n */\n public static relations: Readonly<Record<string, RelationDefinition>> = {};\n\n /** Backing field for {@link isNew}. */\n private newRecord = true;\n\n /**\n * The primary key value captured at the moment this instance became a\n * persisted record — the ONLY value an UPDATE/REPLACE filter may be built\n * from. See {@link trustedPrimaryKey}.\n */\n private capturedPrimaryKey: unknown = undefined;\n\n /** Whether {@link capturedPrimaryKey} holds a captured value. */\n private primaryKeyCaptured = false;\n\n /**\n * Flag indicating whether this model instance represents a new (unsaved) record.\n *\n * - `true`: The model has not been persisted to the database yet\n * - `false`: The model represents an existing database record\n *\n * This flag is used by the writer to determine whether to perform an insert or update.\n *\n * Setting it to `false` (hydration from the database, or the writer marking a\n * freshly inserted row as persisted) is also the moment the instance captures\n * its trusted primary key — see {@link trustedPrimaryKey}.\n */\n public get isNew(): boolean {\n return this.newRecord;\n }\n\n public set isNew(value: boolean) {\n this.newRecord = value;\n\n if (value) {\n // Back to \"unsaved\": there is no persisted row to be pinned to anymore.\n this.capturedPrimaryKey = undefined;\n this.primaryKeyCaptured = false;\n return;\n }\n\n this.capturedPrimaryKey = this.get(this.self().primaryKey);\n this.primaryKeyCaptured = true;\n }\n\n /**\n * The primary key value an UPDATE/REPLACE filter must be built from.\n *\n * For a persisted record this is the value the row was loaded with, captured\n * when `isNew` flipped to `false` — NOT the current value in `data`. Deriving\n * the filter from post-`merge()` state let a request body carrying\n * `{ id: \"<victim-id>\" }` redirect the write to a different document\n * (`model.merge(req.body); await model.save()`), so the two must never be the\n * same read. For a model that was never persisted it falls back to the\n * current value, which is what an insert would use anyway.\n */\n public get trustedPrimaryKey(): any {\n return this.primaryKeyCaptured\n ? this.capturedPrimaryKey\n : this.get(this.self().primaryKey);\n }\n\n /**\n * The raw mutable data backing this model instance.\n *\n * All field accessors (get, set, merge, etc.) operate on this object.\n */\n public data: TSchema;\n\n /**\n * Dirty tracker that monitors changes to the model's data.\n *\n * Tracks:\n * - Which fields have been modified (dirty columns)\n * - Which fields have been removed\n * - Original vs. current values for each dirty field\n *\n * Used by the writer to generate efficient partial update payloads.\n */\n public readonly dirtyTracker: DatabaseDirtyTracker;\n\n /**\n * Model instance events.\n * Allows registering listeners for lifecycle events on this specific instance.\n */\n public events: ModelEvents<any> = new ModelEvents();\n\n /**\n * Map of loaded relations for this model instance.\n *\n * Populated automatically when using `with()` for eager loading,\n * or when calling `load()` for lazy loading.\n *\n * @example\n * ```typescript\n * const user = await User.query().with(\"posts\").first();\n * console.log(user.loadedRelations.get(\"posts\")); // Post[]\n *\n * // Also accessible as direct properties:\n * console.log(user.posts); // Post[]\\n * ```\n */\n public loadedRelations: Map<string, any> = new Map();\n\n /**\n * Column name for active status.\n */\n protected isActiveColumn = \"isActive\";\n\n /**\n * Constructs a new model instance with optional initial data.\n *\n * Initializes the dirty tracker with a snapshot of the provided data.\n *\n * @param initialData - Partial data to populate the model\n *\n * @example\n * ```typescript\n * const user = new User({ name: \"Alice\", email: \"alice@example.com\" });\n * ```\n */\n public constructor(initialData: Partial<TSchema> = {}) {\n this.data = initialData as TSchema;\n this.dirtyTracker = this.self().getDriver().getDirtyTracker(this.data);\n }\n\n /**\n * Lazily load one or more relations for this model instance.\n *\n * This method loads relations on-demand after the model has been fetched.\n * The loaded relations are attached directly to the model instance and\n * also stored in `loadedRelations` map.\n *\n * @param relations - Relation name(s) to load\n * @returns This model instance for chaining\n *\n * @example\n * ```typescript\n * const user = await User.first();\n *\n * // Load single relation\n * await user.load(\"posts\");\n * console.log(user.posts); // Post[]\n *\n * // Load multiple relations\n * await user.load(\"posts\", \"organization\");\n *\n * // Chain with other operations\n * const posts = await user.load(\"posts\").then(() => user.posts);\n * ```\n */\n public async load(...relations: string[]): Promise<this> {\n const ModelClass = this.constructor as ChildModel<Model>;\n const loader = new RelationLoader([this], ModelClass);\n await loader.load(relations);\n return this;\n }\n\n /**\n * Check if a relation has been loaded.\n *\n * @param relationName - Name of the relation to check\n * @returns True if the relation has been loaded\n *\n * @example\n * ```typescript\n * const user = await User.first();\n *\n * console.log(user.isLoaded(\"posts\")); // false\n * await user.load(\"posts\");\n * console.log(user.isLoaded(\"posts\")); // true\n * ```\n */\n\n public isLoaded(relationName: string): boolean {\n return this.loadedRelations.has(relationName);\n }\n\n /**\n * Set relation manually\n *\n * @param relationName\n * @param relationData\n */\n public setRelation(relationName: string, relationData: LoadedRelationResult): void {\n attachLoadedRelation(this, relationName, relationData);\n }\n\n /**\n * Get a loaded relation by name.\n *\n * Returns undefined if the relation has not been loaded.\n *\n * @param relationName - Name of the relation to get\n * @returns The loaded relation data, or undefined\n *\n * @example\n * ```typescript\n * const user = await User.query().with(\"posts\").first();\n *\n * const posts = user.getRelation<Post[]>(\"posts\");\n * console.log(posts?.length);\n * ```\n */\n public getRelation<TRelation = any>(relationName: string): TRelation | undefined {\n return this.loadedRelations.get(relationName) as TRelation | undefined;\n }\n\n /**\n * Get a model class by its name from the global registry.\n *\n * Models must be decorated with @RegisterModel() to be available in the registry.\n *\n * @param name - The model class name\n * @returns The model class or undefined if not found\n *\n * @example\n * ```typescript\n * const UserModel = Model.getModel(\"User\");\n * if (UserModel) {\n * const user = await UserModel.find(1);\n * }\n * ```\n */\n public static getModel(name: string) {\n return getModelFromRegistry(name);\n }\n\n /**\n * Get all registered models from the global registry.\n *\n * Only models decorated with @RegisterModel() will appear here.\n *\n * @returns A Map of all registered model classes by name\n *\n * @example\n * ```typescript\n * const allModels = Model.getAllModels();\n * for (const [name, ModelClass] of allModels) {\n * console.log(`Found model: ${name} with table: ${ModelClass.table}`);\n * }\n * ```\n */\n public static getAllModels() {\n return getAllModelsFromRegistry();\n }\n\n // ============================================================================\n // STATIC SYNC METHODS\n // ============================================================================\n\n /**\n * Create a sync operation for a single embedded document.\n *\n * When this model is updated, the target model's field\n * will be updated with the embedded data.\n *\n * @param TargetModel - Target model class that receives data\n * @param targetField - Field path in target model\n * @returns Sync operation for chaining configuration\n *\n * @example\n * ```typescript\n * // When Category updates, update Product.category\n * Category.sync(Product, \"category\");\n * ```\n */\n public static sync<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n TargetModel: ChildModel<Model>,\n targetField: string,\n ): ModelSyncOperationContract {\n return modelSync.sync(this, TargetModel, targetField);\n }\n\n /**\n * Create a sync operation for an array of embedded documents.\n *\n * When this model is updated, the corresponding element\n * in the target model's array field will be updated.\n *\n * @param TargetModel - Target model class that receives data\n * @param targetField - Array field path in target model\n * @returns Sync operation for chaining configuration\n *\n * @example\n * ```typescript\n * // When Tag updates, update Post.tags[i] where tags[i].id matches\n * Tag.syncMany(Post, \"tags\").identifyBy(\"id\");\n * ```\n */\n public static syncMany<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n TargetModel: ChildModel<Model>,\n targetField: string,\n ): ModelSyncOperationContract {\n return modelSync.syncMany(this, TargetModel, targetField);\n }\n\n /**\n * Get model id\n */\n public get id(): number | string {\n return this.get(\"id\");\n }\n\n /**\n * String-typed accessor for the model's primary id.\n *\n * The underlying `id` field is `string | number` (MongoDB's ObjectId-as-string\n * vs SQL's auto-increment integer), which forces consumers to write\n * `string | number` everywhere they pass an id around. This getter narrows\n * the contract to `string` so callers can write functions that accept a\n * single id type without leaking the engine difference.\n *\n * The name `uuid` is historical — it does NOT validate or coerce the value\n * to a UUID. It simply returns the id, typed as a string.\n *\n * @example\n * ```typescript\n * function shareLink(modelId: string) { ... }\n * shareLink(user.uuid); // works regardless of whether the underlying id is\n * // a Mongo ObjectId string or a SQL integer\n * ```\n */\n public get uuid(): string {\n return this.get(\"id\");\n }\n\n /**\n * Retrieves a field value from the model's data.\n *\n * Supports both top-level keys and dot-notation paths for nested access.\n *\n * @param field - The field name or dot-notation path (e.g., \"address.city\")\n * @param defaultValue - Value to return if the field is missing\n * @returns The field value or the default value if not found\n *\n * @example\n * ```typescript\n * user.get(\"name\"); // \"Alice\"\n * user.get(\"address.city\", \"Unknown\"); // \"Unknown\" if address.city is missing\n * ```\n */\n public get<TKey extends keyof TSchema & string>(field: TKey): TSchema[TKey];\n public get<TKey extends keyof TSchema & string>(\n field: TKey,\n defaultValue: TSchema[TKey],\n ): TSchema[TKey];\n public get<Type extends unknown = any>(field: string): Type;\n public get<Type extends unknown = any>(field: string, defaultValue: Type): Type;\n public get(field: string, defaultValue?: unknown): any {\n return getFieldValue(this, field, defaultValue);\n }\n\n /**\n * Get only the values of the given fields\n */\n public only<TKey extends keyof TSchema & string>(fields: TKey[]): Record<TKey, TSchema[TKey]>;\n public only(fields: string[]): Record<string, unknown>;\n public only(fields: string[]): Record<string, unknown> {\n return getOnlyFields(this, fields);\n }\n\n /**\n * Get a string value\n */\n public string(key: string, defaultValue?: string): string | undefined {\n return getStringField(this, key, defaultValue);\n }\n\n /**\n * Get a number value\n */\n public number(key: string, defaultValue?: number): number | undefined {\n return getNumberField(this, key, defaultValue);\n }\n\n /**\n * Get a boolean value\n */\n public boolean(key: string, defaultValue?: boolean): boolean | undefined {\n return getBooleanField(this, key, defaultValue);\n }\n\n /**\n * Sets a field value in the model's data and marks it as dirty.\n *\n * Supports both top-level keys and dot-notation paths for nested assignment.\n * Automatically updates the dirty tracker to reflect the change.\n *\n * @param field - The field name or dot-notation path (e.g., \"address.city\")\n * @param value - The value to assign\n * @returns The model instance for method chaining\n *\n * @example\n * ```typescript\n * user.set(\"name\", \"Bob\").set(\"address.city\", \"NYC\");\n * ```\n */\n public set<TKey extends keyof TSchema & string>(field: TKey, value: TSchema[TKey]): this;\n public set(field: string, value: unknown): this;\n public set(field: string, value: unknown): this {\n return setFieldValue(this, field, value) as this;\n }\n\n /**\n * Checks whether a field exists in the model's data.\n *\n * Supports both top-level keys and dot-notation paths.\n *\n * @param field - The field name or dot-notation path\n * @returns `true` if the field exists, `false` otherwise\n *\n * @example\n * ```typescript\n * user.has(\"name\"); // true\n * user.has(\"address.zipCode\"); // false\n * ```\n */\n public has<TKey extends keyof TSchema & string>(field: TKey): boolean;\n public has(field: string): boolean;\n public has(field: string): boolean {\n return hasField(this, field);\n }\n\n /**\n * Increment the given field by the given amount\n */\n public increment<TKey extends keyof TSchema & string>(field: TKey, amount: number): this;\n public increment(field: string, amount?: number): this;\n public increment(field: string, amount?: number): this {\n return incrementField(this, field, amount) as this;\n }\n\n /**\n * Decrement the given field by the given amount\n */\n public decrement<TKey extends keyof TSchema & string>(field: TKey, amount: number): this;\n public decrement(field: string, amount?: number): this;\n public decrement(field: string, amount?: number): this {\n return decrementField(this, field, amount) as this;\n }\n\n /**\n * Removes one or more fields from the model's data and marks them as removed.\n *\n * Supports both top-level keys and dot-notation paths.\n * Automatically updates the dirty tracker to reflect the removal.\n *\n * @param fields - One or more field names or dot-notation paths to remove\n * @returns The model instance for method chaining\n *\n * @example\n * ```typescript\n * user.unset(\"tempField\", \"address.oldZip\");\n * ```\n */\n public unset(...fields: (keyof TSchema & string)[]): this;\n public unset(...fields: string[]): this;\n public unset(...fields: string[]): this {\n return unsetFields(this, ...fields) as this;\n }\n\n /**\n * Merges new values into the model's data and marks changed fields as dirty.\n *\n * Performs a deep merge, preserving existing nested structures.\n * Automatically updates the dirty tracker to reflect all changes.\n *\n * @param values - Partial data to merge into the model\n * @returns The model instance for method chaining\n *\n * @example\n * ```typescript\n * user.merge({ name: \"Charlie\", address: { city: \"LA\" } });\n * ```\n */\n public merge(values: Partial<TSchema>): this;\n public merge(values: Record<string, unknown>): this;\n public merge(values: Record<string, unknown>): this {\n return mergeFields(this, values) as this;\n }\n\n /**\n * Perform atomoic update from current model instance\n * Please note that it would require the id to be existing in the current\n * model instance\n * @returns number of affected records\n */\n public async atomicUpdate(operations: Record<string, unknown>): Promise<number> {\n return performAtomicUpdate(this, operations);\n }\n\n /**\n * Perform atomic increment\n * This would issue a query update and update the given field without\n * saving the model\n */\n public async atomicIncrement<T extends keyof TSchema & string>(\n field: T,\n amount: number = 1,\n ): Promise<number> {\n return performAtomicIncrement(this, field, amount);\n }\n\n /**\n * Perform atomic decrement\n * This would issue a query update and update the given field without\n * saving the model\n */\n public async atomicDecrement<T extends keyof TSchema & string>(\n field: T,\n amount: number = 1,\n ): Promise<number> {\n return performAtomicDecrement(this, field, amount);\n }\n\n /**\n * Determine if current model is active\n */\n public get isActive(): boolean {\n return this.get<boolean>(this.isActiveColumn);\n }\n\n /**\n * Get created at date\n */\n public get createdAt(): Date | undefined {\n const createdAtColumn = this.self().createdAtColumn;\n\n if (!createdAtColumn) return;\n\n return this.get<Date>(createdAtColumn);\n }\n\n /**\n * Get updated at date\n */\n public get updatedAt(): Date | undefined {\n const updatedAtColumn = this.self().updatedAtColumn;\n\n if (!updatedAtColumn) return;\n\n return this.get<Date>(updatedAtColumn);\n }\n\n /**\n * Check if current model record is created by the given user model\n */\n public isCreatedBy(user: Model | GenericObject): boolean {\n return this.get(`createdBy.id`) === user.id;\n }\n\n /**\n * Checks whether the model's data has changed since instantiation or last reset.\n *\n * @returns `true` if any fields have been modified or removed, `false` otherwise\n *\n * @example\n * ```typescript\n * const user = new User({ name: \"Alice\" });\n * user.hasChanges(); // false\n * user.set(\"name\", \"Bob\");\n * user.hasChanges(); // true\n * ```\n */\n public hasChanges(): boolean {\n return checkHasChanges(this);\n }\n\n /**\n * Check if the given column has been modified.\n *\n * @param column - The column name to check\n * @returns `true` if the column has been modified, `false` otherwise\n *\n * @example\n * ```typescript\n * user.set(\"name\", \"Bob\");\n * user.isDirty(\"name\"); // true\n * ```\n */\n public isDirty(column: string): boolean {\n return checkIsDirty(this, column);\n }\n\n /**\n * Retrieves all dirty columns with their old and new values.\n *\n * @returns A record mapping each dirty column to its previous and current value\n *\n * @example\n * ```typescript\n * user.set(\"name\", \"Bob\");\n * user.getDirtyColumnsWithValues();\n * // { name: { oldValue: \"Alice\", newValue: \"Bob\" } }\n * ```\n */\n public getDirtyColumnsWithValues(): Record<string, { oldValue: unknown; newValue: unknown }> {\n return getDirtyColumnsWithValues(this);\n }\n\n /**\n * Lists all columns that have been removed from the model's data.\n *\n * @returns An array of field names that were present initially but have been unset\n *\n * @example\n * ```typescript\n * user.unset(\"tempField\");\n * user.getRemovedColumns(); // [\"tempField\"]\n * ```\n */\n public getRemovedColumns(): string[] {\n return getRemovedColumns(this);\n }\n\n /**\n * Lists all columns that have been modified since instantiation or last reset.\n *\n * @returns An array of field names that have changed\n *\n * @example\n * ```typescript\n * user.set(\"name\", \"Bob\");\n * user.getDirtyColumns(); // [\"name\"]\n * ```\n */\n public getDirtyColumns(): string[] {\n return getDirtyColumns(this);\n }\n\n /**\n * Emits a lifecycle event to both per-model and global listeners.\n *\n * This method is public so that external services (like the writer) can trigger\n * lifecycle events when appropriate.\n *\n * @param event - The event name (e.g., \"saving\", \"created\", \"deleting\")\n * @param context - Optional context data to pass to listeners\n *\n * @example\n * ```typescript\n * await user.emitEvent(\"saving\");\n * await user.emitEvent(\"validated\", { errors: [] });\n * ```\n */\n public async emitEvent<TContext = unknown>(\n event: ModelEventName,\n context?: TContext,\n ): Promise<void> {\n return emitModelEvent(this, event, context);\n }\n\n /**\n * Register a listener for a model lifecycle event on this instance.\n *\n * @param event - Event name (e.g., \"saving\", \"updated\")\n * @param listener - Callback function\n * @returns Unsubscribe function\n */\n public on<TContext = unknown>(\n event: ModelEventName,\n listener: ModelEventListener<this, TContext>,\n ): () => void {\n return onModelEvent(this, event, listener as any);\n }\n\n /**\n * Register a one-time listener for a model lifecycle event on this instance.\n *\n * @param event - Event name\n * @param listener - Callback function\n * @returns Unsubscribe function\n */\n public once<TContext = unknown>(\n event: ModelEventName,\n listener: ModelEventListener<this, TContext>,\n ): () => void {\n return onceModelEvent(this, event, listener as any);\n }\n\n /**\n * Remove a listener from this instance.\n *\n * @param event - Event name\n * @param listener - Callback function to remove\n */\n public off<TContext = unknown>(\n event: ModelEventName,\n listener: ModelEventListener<this, TContext>,\n ): void {\n offModelEvent(this, event, listener as any);\n }\n\n /**\n * Resolves the data source associated with this model.\n *\n * Resolution order:\n * 1. If `dataSource` is a string, looks it up in the data-source registry\n * 2. If `dataSource` is a DataSource instance, returns it directly\n * 3. Otherwise, returns the default data source from the registry\n *\n * @returns The resolved DataSource instance\n * @throws Error if no data source is found\n *\n * @example\n * ```typescript\n * class User extends Model {\n * public static dataSource = \"primary\";\n * }\n *\n * const ds = User.getDataSource();\n * ```\n */\n public static getDataSource(): DataSource {\n return resolveDataSource(this as any);\n }\n\n /**\n * Get driver instance\n */\n public static getDriver(): DriverContract {\n return this.getDataSource().driver;\n }\n\n /**\n * Execute a raw query against this model's driver.\n *\n * Delegates to `getDriver().query`, so it inherits the driver's\n * transaction awareness: when called inside an active `transaction()`\n * scope the query auto-joins that transaction's client/session, otherwise\n * it runs on the pool. The return value is the driver's raw result\n * ({@link RawQueryResult}) — `rows` typed as `T` plus `rowCount` — not\n * hydrated model instances.\n *\n * Note: MongoDB drivers throw, as they do not support raw SQL.\n *\n * @typeParam T - The expected row shape.\n * @param sql - Raw SQL string (driver dialect).\n * @param params - Optional positional query parameters.\n * @returns The driver's raw query result.\n *\n * @example\n * ```typescript\n * const { rows } = await User.raw<{ id: number; total: number }>(\n * \"SELECT id, COUNT(*) AS total FROM orders WHERE user_id = $1 GROUP BY id\",\n * [userId],\n * );\n *\n * // Auto-joins the active transaction() scope:\n * await User.transaction(async () => {\n * await User.raw(\"UPDATE users SET active = true WHERE id = $1\", [id]);\n * });\n * ```\n */\n public static raw<T = Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n ): Promise<RawQueryResult<T>> {\n return this.getDriver().query<T>(sql, params);\n }\n\n /**\n * Generate next id and set it to current model's id\n */\n public async generateNextId(): Promise<number | string> {\n return generateModelNextId(this);\n }\n\n /**\n * Apply model defaults from data source configuration.\n *\n * This is called automatically by getDataSource() the first time\n * a model accesses its data source. Defaults are only applied if\n * the model doesn't already have its own value set.\n *\n * The hierarchy is:\n * 1. Model static property (highest priority - skipped here)\n * 2. Database config modelDefaults (passed here)\n * 3. Driver modelDefaults (merged before passing here)\n * 4. Framework defaults (fallback values in the code)\n *\n * @param defaults - Model default configuration from data source\n */\n public static applyModelDefaults(defaults: any): void {\n applyDefaultsToModel(this, defaults);\n }\n\n /**\n * Add a global scope that is automatically applied to all queries.\n *\n * Global scopes are inherited by child models and applied before query execution.\n * Use for security filters, multi-tenancy, soft deletes, etc.\n *\n * @param name - Unique name for the scope\n * @param callback - Function that modifies the query\n * @param options - Scope options (timing: 'before' | 'after')\n *\n * @example\n * ```typescript\n * // Multi-tenancy scope\n * Model.addGlobalScope('tenant', (query) => {\n * query.where('tenantId', getCurrentTenant());\n * }, { timing: 'before' });\n *\n * // Soft delete scope\n * User.addGlobalScope('notDeleted', (query) => {\n * query.whereNull('deletedAt');\n * });\n * ```\n */\n public static addGlobalScope(\n name: string,\n callback: (query: QueryBuilderContract) => void,\n options: GlobalScopeOptions = {},\n ): void {\n addGlobalModelScope(this as any, name, callback, options);\n }\n\n /**\n * Remove a global scope by name.\n *\n * @param name - Name of the scope to remove\n *\n * @example\n * ```typescript\n * Model.removeGlobalScope('tenant');\n * ```\n */\n public static removeGlobalScope(name: string): void {\n removeGlobalModelScope(this as any, name);\n }\n\n /**\n * Add a local scope that can be manually applied to queries.\n *\n * Local scopes are reusable query snippets that developers opt into.\n * They are not automatically applied.\n *\n * @param name - Unique name for the scope\n * @param callback - Function that modifies the query\n *\n * @example\n * ```typescript\n * // Define reusable scopes\n * User.addScope('active', (query) => {\n * query.where('isActive', true);\n * });\n *\n * User.addScope('admins', (query) => {\n * query.where('role', 'admin');\n * });\n *\n * // Use explicitly\n * await User.query().scope('active').get();\n * await User.query().scope('admins').get();\n * ```\n */\n public static addScope(name: string, callback: LocalScopeCallback): void {\n addLocalModelScope(this as any, name, callback);\n }\n\n /**\n * Remove a local scope by name.\n *\n * @param name - Name of the scope to remove\n *\n * @example\n * ```typescript\n * User.removeScope('active');\n * ```\n */\n public static removeScope(name: string): void {\n removeLocalModelScope(this as any, name);\n }\n\n /**\n * Create a new query builder for this model\n */\n public static query<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ): QueryBuilderContract<TModel> {\n return buildQuery(this, Model);\n }\n\n // Short hand for the query builder method with\n /**\n * Eagerly load one or more relations with the query results.\n *\n * Relations are loaded in separate optimized queries to prevent N+1 problems.\n * The loaded relations are attached to each model instance.\n *\n * @param relation - Single relation name to load\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * // Load single relation\n * const user = await User.query().with(\"posts\").find(1);\n * console.log(user.posts); // Post[]\n *\n * // Load multiple relations\n * const user = await User.query().with(\"posts\", \"organization\").find(1);\n *\n * // Load nested relations\n * const user = await User.query().with(\"posts.comments.author\").find(1);\n * ```\n */\n public static with<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relation: string,\n ): QueryBuilderContract<TModel>;\n\n /**\n * Eagerly load multiple relations.\n *\n * @param relations - Relation names to load\n * @returns Query builder for chaining\n */\n public static with<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...relations: string[]\n ): QueryBuilderContract<TModel>;\n\n /**\n * Eagerly load a relation with a constraint callback.\n *\n * The callback receives the relation query builder, allowing you to\n * add conditions, ordering, or limits to the related query.\n *\n * @param relation - Relation name to load\n * @param constraint - Callback to configure the relation query\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * const user = await User.query()\n * .with(\"posts\", (query) => {\n * query.where(\"isPublished\", true)\n * .orderBy(\"createdAt\", \"desc\")\n * .limit(5);\n * })\n * .find(1);\n * ```\n */\n public static with<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relation: string,\n constraint: (query: QueryBuilderContract) => void,\n ): QueryBuilderContract<TModel>;\n\n /**\n * Eagerly load multiple relations with constraints.\n *\n * Pass an object where keys are relation names and values are either:\n * - `true` to load without constraints\n * - A callback function to configure the relation query\n *\n * @param relations - Object mapping relation names to constraints\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * const user = await User.query()\n * .with({\n * posts: (query) => query.where(\"isPublished\", true),\n * organization: true,\n * roles: (query) => query.orderBy(\"priority\"),\n * })\n * .find(1);\n * ```\n */\n public static with<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relations: Record<string, boolean | ((query: QueryBuilderContract) => void)>,\n ): QueryBuilderContract<TModel>;\n\n public static with<TModel extends Model = Model>(this: ChildModel<TModel>, ...args: any[]) {\n return this.query().with(...args);\n }\n\n /**\n * Add a count of related records as a virtual field on each result row.\n *\n * Each relation produces a `${relationName}Count` column by default. Use\n * the `\"name as alias\"` shorthand or the object form to customise the\n * output alias or apply per-relation where-clause constraints.\n *\n * @param relation - Relation name (optionally with `as <alias>`)\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * const users = await User.withCount(\"posts\").get();\n * console.log(users[0].postsCount); // number\n * ```\n */\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relation: string,\n ): QueryBuilderContract<TModel>;\n\n /**\n * Add counts for multiple relations at once.\n *\n * @example\n * ```typescript\n * await User.withCount(\"posts\", \"comments\", \"followers\").get();\n * ```\n */\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...relations: string[]\n ): QueryBuilderContract<TModel>;\n\n /**\n * Add counts for multiple relations supplied as an array.\n */\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relations: string[],\n ): QueryBuilderContract<TModel>;\n\n /**\n * Add counts with optional per-relation constraints and alias overrides.\n *\n * Values may be `true`, an alias string, or a callback that applies\n * where-clauses inside the count subquery. Use the `as <alias>` shorthand\n * in the key to count the same relation more than once.\n *\n * @example\n * ```typescript\n * await Post.withCount({\n * comments: true,\n * \"comments as approvedCount\": (q) => q.where(\"approved\", true),\n * tags: \"tagCount\",\n * }).get();\n * ```\n */\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n relations: Record<string, true | string | ((query: QueryBuilderContract) => void)>,\n ): QueryBuilderContract<TModel>;\n\n public static withCount<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...args: any[]\n ): QueryBuilderContract<TModel> {\n return this.query().withCount(...args);\n }\n\n /**\n * Load relations using database JOINs in a single query.\n *\n * Unlike `with()` which uses separate queries, `joinWith()` uses\n * LEFT JOIN (SQL) or $lookup (MongoDB) to fetch related data\n * in a single query. The related data is hydrated into proper\n * model instances and attached to the main model.\n *\n * Best for: belongsTo and hasOne relations where you need\n * efficient single-query loading.\n *\n * @param relations - Relation names to load via JOIN\n * @returns Query builder for chaining\n *\n * @example\n * ```typescript\n * // Single relation\n * const post = await Post.joinWith(\"author\").first();\n * console.log(post.author); // User model instance\n * console.log(post.data); // { id, title, authorId } - no author data\n *\n * // Multiple relations\n * const post = await Post.joinWith(\"author\", \"category\").first();\n * ```\n */\n public static joinWith<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...relations: string[]\n ): QueryBuilderContract<TModel> {\n return this.query().joinWith(...relations);\n }\n\n /**\n * Create new query builder.\n *\n * If the model has a static `builder` property set to a query builder class,\n * it will be instantiated instead of the default driver query builder.\n *\n * @example\n * ```typescript\n * class UserQueryBuilder<T = User> extends MongoQueryBuilder<T> {\n * active() { return this.where(\"isActive\", true); }\n * }\n *\n * class User extends Model {\n * static builder = UserQueryBuilder; // That's it! ✨\n * }\n *\n * // Now User.query() returns UserQueryBuilder<User> with autocomplete!\n * ```\n */\n public static newQueryBuilder<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ): QueryBuilderContract<TModel> {\n return buildNewQueryBuilder(this);\n }\n\n /**\n * Get First matched record for the given filter\n */\n public static async first<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<TModel | null> {\n return findFirst(this, filter);\n }\n\n /**\n * Get last matched record for the given filter\n */\n public static async last<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<TModel | null> {\n return findLast(this, filter);\n }\n\n /**\n * Use where clause directly\n */\n public static where<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n field: string,\n value: unknown,\n ): QueryBuilderContract<TModel>;\n public static where<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n field: string,\n operator: WhereOperator,\n value: unknown,\n ): QueryBuilderContract<TModel>;\n public static where<TModel extends Model = Model>(\n this: (new (...args: any[]) => TModel) &\n Pick<typeof Model, \"query\" | \"getDataSource\" | \"table\">,\n conditions: WhereObject,\n ): QueryBuilderContract<TModel>;\n public static where<TModel extends Model = Model>(\n this: (new (...args: any[]) => TModel) &\n Pick<typeof Model, \"query\" | \"getDataSource\" | \"table\">,\n callback: WhereCallback<TModel>,\n ): QueryBuilderContract<TModel>;\n public static where<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ...args: any[]\n ): QueryBuilderContract<TModel> {\n return (this.query().where as any)(...args);\n }\n\n /**\n * Count the number of records in the table\n * @param filter - The filter to apply to the query\n */\n public static count<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<number> {\n return countRecords(this, filter);\n }\n\n /**\n * Find record by id\n */\n public static async find<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n id: string | number,\n ): Promise<TModel | null> {\n return findById(this, id);\n }\n\n /**\n * Get all records from the table\n *\n * @param filter - The filter to apply to the query\n * @returns All records from the table\n */\n public static async all<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<TModel[]> {\n return findAll(this, filter);\n }\n\n /**\n * Perform pagination\n */\n public static async paginate<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n options: PaginationOptions & {\n filter?: Record<string, unknown>;\n } = {},\n ): Promise<PaginationResult<TModel>> {\n return paginateRecords(this, options);\n }\n\n /**\n * Get latest records from the table\n *\n * @param filter - The filter to apply to the query\n */\n public static async latest<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<TModel[]> {\n return findLatest(this, filter);\n }\n\n /**\n * Increment the given field by the given amount using atomic update\n *\n * @example ```typescript\n * // Increase age by 1 for user id 1\n * User.increment({id: 1}, \"age\", 1);\n * // Increase age by 1 and views by 2 for user id 1\n * User.increment({id: 1}, {age: 1, views: 2});\n * ```\n */\n public static increase<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n field: string,\n amount: number,\n ): Promise<number> {\n return increaseField(this, filter, field, amount);\n }\n\n /**\n * Decrement the given field by the given amount using atomic update\n * @example ```typescript\n * // Decrease age by 1 for user id 1\n * User.decrement({id: 1}, \"age\", 1);\n * // Decrease age by 1 and views by 2 for user id 1\n * User.decrement({id: 1}, {age: 1, views: 2});\n * ```\n */\n public static decrease<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n field: string,\n amount: number,\n ): Promise<number> {\n return decreaseField(this, filter, field, amount);\n }\n\n /**\n * Perform atomic operation\n * Example\n *\n * ```typescript\n * const user = await User.atomic({id: 1}, {$inc: {age: 1}})\n * Returns user model with updated age\n */\n public static async atomic<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n operations: UpdateOperations,\n ): Promise<number> {\n return performAtomic(this, filter, operations);\n }\n\n /**\n * Perform an atomic update for the given id\n */\n public static async update<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n id: string | number,\n data: Record<string, unknown>,\n ): Promise<number> {\n return updateById(this, id, data);\n }\n\n /**\n * Find one and update multiple records that matches the provided filter and return the updated record\n * @param filter - Filter conditions\n * @param update - Update operations ($set, $unset, $inc)\n * @returns The updated records\n */\n public static async findAndUpdate<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n update: UpdateOperations,\n ): Promise<TModel[]> {\n return findAndUpdateRecords(this, filter, update);\n }\n\n /**\n * Find one and update a single record that matches the provided filter and return the updated record\n * @param filter - Filter conditions\n * @param update - Update operations ($set, $unset, $inc)\n * @returns The updated record or null\n */\n public static async findOneAndUpdate<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n update: UpdateOperations,\n ): Promise<TModel | null> {\n return findOneAndUpdateRecord(this, filter, update);\n }\n\n /**\n * Find and replace the entire document that matches the provided filter and return the replaced document\n */\n public static async findAndReplace<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n document: Record<string, unknown>,\n ): Promise<TModel | null> {\n return findAndReplaceRecord(this, filter, document);\n }\n\n /**\n * Destroy (delete) the current model instance from the database.\n *\n * Emits lifecycle events:\n * - `deleting` - Before deletion\n * - `deleted` - After successful deletion\n *\n * @param options - Destroy options (strategy override, skipEvents)\n * @throws {Error} If the model is new (not saved) or if deletion fails\n *\n * @example\n * ```typescript\n * const user = await User.find(1);\n * await user.destroy(); // Uses default strategy\n * await user.destroy({ strategy: \"permanent\" }); // Override strategy\n * await user.destroy({ skipEvents: true }); // Silent delete\n * ```\n */\n public async destroy(options?: {\n strategy?: DeleteStrategy;\n skipEvents?: boolean;\n }): Promise<RemoverResult> {\n return destroyModel(this, options);\n }\n\n /**\n * Attach one or more related records to a `belongsToMany` pivot table.\n *\n * Thin wrapper over `createPivotOperations(this, relation).attach(ids, pivotData)`.\n * Throws if the named relation is not a `belongsToMany` relation.\n *\n * @example\n * ```typescript\n * await post.attach(\"tags\", [1, 2, 3]);\n * await post.attach(\"tags\", [4], { addedBy: currentUserId });\n * ```\n */\n public async attach(relation: string, ids: PivotIds, pivotData?: PivotData): Promise<void> {\n return attachPivotRelation(this, relation, ids, pivotData);\n }\n\n /**\n * Detach related records from a `belongsToMany` pivot table. Omit `ids`\n * to detach every row for this side of the relation.\n *\n * Thin wrapper over `createPivotOperations(this, relation).detach(ids)`.\n * Throws if the named relation is not a `belongsToMany` relation.\n *\n * @example\n * ```typescript\n * await post.detach(\"tags\", [2]);\n * await post.detach(\"tags\"); // detach all\n * ```\n */\n public async detach(relation: string, ids?: PivotIds): Promise<void> {\n return detachPivotRelation(this, relation, ids);\n }\n\n /**\n * Get the pivot-operations handle for a `belongsToMany` relation.\n *\n * Returns a `PivotOperations` object exposing `attach` / `detach` /\n * `sync` / `toggle` for the named relation's pivot table. Routing every\n * pivot mutation through `model.pivot(relation)` keeps the join-table\n * `sync` distinct from `Model.sync(Target, field)` (the denormalization\n * feature). Throws if the named relation is not a `belongsToMany` relation.\n *\n * @example\n * ```typescript\n * await post.pivot(\"tags\").attach([1, 2, 3]);\n * await post.pivot(\"tags\").sync([1, 3, 5]); // replace the whole set\n * await post.pivot(\"tags\").toggle([1, 7]); // flip each\n * ```\n */\n public pivot(relation: string): PivotOperations {\n return pivotRelation(this, relation);\n }\n\n /**\n * Get the class constructor from an instance.\n *\n * This helper method allows instance methods to access static properties\n * and methods of the model class in a type-safe way.\n *\n * @returns The model class constructor\n *\n * @example\n * ```typescript\n * const constructor = this.self();\n * const table = constructor.table;\n * await constructor.deleteOne({ id: 1 });\n * ```\n */\n public self<TModel extends Model = this>(): ChildModel<TModel> {\n return this.constructor as any as ChildModel<TModel>;\n }\n\n /**\n * Creates an immutable clone of the model with its current state.\n *\n * The cloned model:\n * - Contains a deep copy of all current data\n * - Has frozen (immutable) data that cannot be modified\n * - Preserves the `isNew` flag from the original\n * - Has no dirty changes (clean state)\n * - Cannot be saved or modified\n *\n * This is useful for:\n * - Creating snapshots of model state\n * - Passing read-only model data to other parts of the application\n * - Preventing accidental mutations\n * - Maintaining historical records\n *\n * @returns A new immutable model instance with the current state\n *\n * @example\n * ```typescript\n * const user = new User({ name: \"Alice\", email: \"alice@example.com\" });\n * await user.save();\n *\n * // Create an immutable snapshot\n * const snapshot = user.clone();\n *\n * // This will throw an error because the clone is immutable\n * snapshot.set(\"name\", \"Bob\"); // TypeError: Cannot assign to read only property\n *\n * // Original can still be modified\n * user.set(\"name\", \"Bob\");\n * await user.save();\n * ```\n */\n public clone(): this {\n return cloneModel(this);\n }\n\n /**\n * Recursively freezes an object and all its nested properties.\n *\n * @param obj - The object to freeze\n * @returns The frozen object\n */\n public deepFreeze<T>(obj: T): T {\n return deepFreezeObject(obj);\n }\n\n /**\n * Get table name\n */\n public getTableName(): string {\n return this.self().table;\n }\n\n /**\n * Get primary key name\n */\n public getPrimaryKey(): string {\n return this.self().primaryKey;\n }\n\n /**\n * Get model schema\n */\n public getSchema() {\n return this.self().schema;\n }\n\n /**\n * Check if schema has the given key\n */\n public schemaHas(key: string): boolean {\n return this.self().schema?.schema[key] !== undefined;\n }\n\n /**\n * Get strict mode\n */\n public getStrictMode(): StrictMode {\n return this.self().strictMode;\n }\n\n /**\n * Get data source (Connection)\n */\n public getConnection(): DataSource {\n return this.self().getDataSource();\n }\n\n /**\n * Delete all matching documents from the table.\n */\n public static async delete<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<number> {\n return deleteRecords(this, filter);\n }\n\n /**\n * Delete a single matching document from the table.\n */\n public static async deleteOne<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n ): Promise<number> {\n return deleteOneRecord(this, filter);\n }\n\n /**\n * Restore a single deleted record by its ID.\n *\n * Automatically detects whether the record was deleted via \"trash\" or \"soft\" strategy.\n * Handles ID conflicts based on options.\n *\n * @param id - The primary key value of the record to restore\n * @param options - Restorer options (onIdConflict, skipEvents)\n * @returns The restored model instance\n *\n * @throws {Error} If record not found in trash or soft-deleted records\n * @throws {Error} If ID conflict and onIdConflict is \"fail\"\n *\n * @example\n * ```typescript\n * // Restore with default options (assign new ID if conflict)\n * const user = await User.restore(123);\n *\n * // Restore and fail if ID conflict\n * const user = await User.restore(123, { onIdConflict: \"fail\" });\n *\n * // Silent restore (skip events)\n * const user = await User.restore(123, { skipEvents: true });\n * ```\n */\n public static async restore<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n id: string | number,\n options?: {\n onIdConflict?: \"fail\" | \"assignNew\";\n skipEvents?: boolean;\n },\n ): Promise<TModel> {\n return restoreRecord(this, id, options);\n }\n\n /**\n * Restore all deleted records for the model's table.\n *\n * Restores all records from the trash table (if using trash strategy)\n * or all soft-deleted records (if using soft strategy).\n *\n * @param options - Restorer options (onIdConflict, skipEvents)\n * @returns Array of restored model instances\n *\n * @example\n * ```typescript\n * // Restore all with default options\n * const users = await User.restoreAll();\n *\n * // Restore all and fail on any ID conflict\n * const users = await User.restoreAll({ onIdConflict: \"fail\" });\n * ```\n */\n public static async restoreAll<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n options?: {\n onIdConflict?: \"fail\" | \"assignNew\";\n skipEvents?: boolean;\n },\n ): Promise<TModel[]> {\n return restoreAllRecords(this, options);\n }\n\n /**\n * Create a new record in database and return the model instance.\n *\n * The data type is automatically inferred from the model's schema type.\n *\n * @param data - Partial data matching the model's schema type\n * @returns The created model instance\n *\n * @example\n * ```typescript\n * // TypeScript automatically infers UserSchema from User model\n * const user = await User.create({\n * name: \"Alice\",\n * email: \"alice@example.com\",\n * age: 30\n * });\n * // Type: User (with UserSchema inferred)\n * ```\n */\n public static async create<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(this: ChildModel<TModel>, data: Partial<TSchema>): Promise<TModel> {\n return createRecord(this, data);\n }\n\n /**\n * Create many records and return an array of created models.\n *\n * The work is always chunked into batches of `options.batchSize`\n * (default 500) so a huge input array can't overwhelm the driver.\n *\n * **Default path** (no options, or `bulk` falsy): each row is persisted\n * through `save()`, preserving model hooks, lifecycle events, casts and\n * generated ids. Each chunk runs under a single `Promise.all`.\n *\n * **Bulk path** (`bulk: true`): each chunk is routed to the driver's native\n * multi-row insert (`insertMany`) for 10–100× throughput. This SKIPS the\n * per-row save lifecycle (no `saving` / `creating` / `created` / `saved`\n * events, hooks, or sync). Casts, timestamps, defaults and id-generation are\n * still applied (rows are prepped through the same writer pipeline) so the\n * persisted columns match the default path.\n *\n * An empty array is a no-op that never touches the driver.\n *\n * @param data - The rows to insert.\n * @param options - Chunking / bulk options.\n * @returns The created model instances.\n *\n * @example\n * ```typescript\n * // Default: per-row save(), chunked in batches of 500\n * const users = await User.createMany(rows);\n *\n * // Bulk: one multi-row INSERT per 1000-row chunk (skips per-row hooks)\n * const users = await User.createMany(rows, { bulk: true, batchSize: 1000 });\n * ```\n */\n public static async createMany<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(\n this: ChildModel<TModel>,\n data: Partial<TSchema>[],\n options?: CreateManyOptions,\n ): Promise<TModel[]> {\n return createManyRecords(this, data, options);\n }\n\n /**\n * Find a record or create it if not found.\n *\n * Does NOT update existing records - returns them as-is.\n * Useful when you want to ensure a record exists without modifying it.\n *\n * @param filter - Conditions to find by\n * @param data - Data to create if not found (merged with filter)\n * @returns Promise resolving to found or created model\n *\n * @example\n * ```typescript\n * // Ensure default admin exists (don't modify if exists)\n * const admin = await User.findOrCreate(\n * { email: \"admin@example.com\" },\n * { email: \"admin@example.com\", name: \"Admin\", role: \"admin\" }\n * );\n * // If admin exists, returns existing (password unchanged)\n * // If not found, creates new admin\n * ```\n */\n public static async findOrCreate<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(this: ChildModel<TModel>, filter: Partial<TSchema>, data: Partial<TSchema>): Promise<TModel> {\n return findOrCreateRecord(this, filter, data);\n }\n\n /**\n * Upsert (insert or update) a record atomically.\n *\n * Uses the driver's native upsert operation for atomic insert-or-update.\n * More efficient than updateOrCreate as it's a single database operation.\n *\n * Includes full Model features:\n * - ID generation (if creating)\n * - createdAt timestamp (if creating)\n * - updatedAt timestamp (always)\n * - Validation & casting\n * - Lifecycle events\n *\n * @param filter - Conditions to find by (used for conflict detection)\n * @param data - Data to update or create (merged with filter)\n * @param options - Upsert options (conflictColumns for PostgreSQL, etc.)\n * @returns Promise resolving to upserted model\n *\n * @example\n * ```typescript\n * // PostgreSQL: upsert on unique email\n * const user = await User.upsert(\n * { email: \"user@example.com\" },\n * {\n * email: \"user@example.com\",\n * name: \"John Updated\",\n * lastSyncedAt: new Date()\n * },\n * { conflictColumns: [\"email\"] }\n * );\n *\n * // MongoDB: upsert by filter\n * const user = await User.upsert(\n * { externalId: \"ext-123\" },\n * {\n * externalId: \"ext-123\",\n * name: \"John Updated\",\n * email: \"john.new@example.com\"\n * }\n * );\n * ```\n */\n public static async upsert<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(\n this: ChildModel<TModel>,\n filter: Partial<TSchema>,\n data: Partial<TSchema>,\n options?: Record<string, unknown>,\n ): Promise<TModel> {\n return upsertRecord(this, filter, data, options);\n }\n\n /**\n * Update a record or create it if not found (upsert).\n *\n * @deprecated Use `upsert()` instead for better performance and atomicity.\n * This method is kept for backward compatibility but uses upsert internally.\n *\n * @param filter - Conditions to find by\n * @param data - Data to update or create (merged with filter)\n * @returns Promise resolving to updated or created model\n */\n public static async updateOrCreate<\n TModel extends Model = Model,\n TSchema extends ModelSchema = TModel extends Model<infer S> ? S : ModelSchema,\n >(\n this: ChildModel<TModel>,\n filter: Partial<TSchema>,\n data: Partial<TSchema>,\n options?: Record<string, unknown>,\n ): Promise<TModel> {\n // Use upsert internally for better performance\n return await (this as any).upsert(filter, data, options);\n }\n\n /**\n * Find one and delete a record that matches the filter and return the deleted record.\n *\n * @param filter - Filter conditions\n * @param options - Optional delete options\n * @returns The deleted model instance or null if not found\n *\n * @example\n * ```typescript\n * const deleted = await User.findOneAndDelete({ id: 1 });\n * if (deleted) {\n * console.log('Deleted user:', deleted.get('name'));\n * }\n * ```\n */\n public static async findOneAndDelete<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n filter: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<TModel | null> {\n return findOneAndDeleteRecord(this, filter, options);\n }\n\n /**\n * Returns embedded data for sync operations.\n * Excludes internal MongoDB fields and ensures proper date serialization.\n *\n * @returns Embedded data object suitable for syncing\n *\n * @example\n * ```typescript\n * const user = await User.find(1);\n * const embedData = user.embedData;\n * // Returns: { id: 1, name: \"Alice\", email: \"alice@example.com\", ... }\n * // Excludes: _id\n * ```\n */\n public get embedData(): Record<string, unknown> {\n return this.self().embed ? this.only(this.self().embed as any) : this.data;\n }\n\n /**\n * Tear down framework-level registrations attached to this Model class.\n *\n * Called by Warlock's HMR machinery when a model file (or any file in its\n * dependency graph) is reloaded. Removes the event listeners and registry\n * entries the Model installed at module-load time so the reloaded class\n * does not double-register.\n *\n * The `$` prefix marks this as framework-internal — application code should\n * not call this. It is part of the public surface only because the HMR\n * system needs to invoke it from outside the Model class.\n *\n * @internal\n */\n public static $cleanup() {\n cleanupModelEvents(this);\n }\n\n /**\n * Accesses the event emitter dedicated to this model constructor.\n *\n * Each model subclass gets its own isolated event emitter, allowing you to\n * register lifecycle hooks that only apply to that specific model.\n *\n * @returns The ModelEvents instance for this model constructor\n *\n * @example\n * ```typescript\n * User.events().onSaving((user) => {\n * console.log(\"User is being saved:\", user);\n * });\n * ```\n */\n public static events<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n ): ModelEvents<TModel> {\n return getModelEvents<TModel>(this);\n }\n\n /**\n * Registers an event listener for this model constructor.\n *\n * Convenience shorthand for `Model.events().on(...)`.\n *\n * @param event - The event name (e.g., \"saving\", \"created\")\n * @param listener - The callback to invoke when the event fires\n * @returns An unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = User.on(\"saving\", (user) => {\n * console.log(\"Saving user:\", user);\n * });\n * ```\n */\n public static on<TModel extends Model = Model, TContext = unknown>(\n this: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n ): () => void {\n return onStaticEvent<TModel, TContext>(this, event, listener);\n }\n\n /**\n * Registers a one-time event listener for this model constructor.\n *\n * The listener will automatically unsubscribe after its first invocation.\n * Convenience shorthand for `Model.events().once(...)`.\n *\n * @param event - The event name (e.g., \"saving\", \"created\")\n * @param listener - The callback to invoke when the event fires\n * @returns An unsubscribe function\n *\n * @example\n * ```typescript\n * User.once(\"created\", (user) => {\n * console.log(\"First user created:\", user);\n * });\n * ```\n */\n public static once<TModel extends Model = Model, TContext = unknown>(\n this: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n ): () => void {\n return onceStaticEvent<TModel, TContext>(this, event, listener);\n }\n\n /**\n * Removes an event listener from this model constructor.\n *\n * Convenience shorthand for `Model.events().off(...)`.\n *\n * @param event - The event name\n * @param listener - The callback to remove\n *\n * @example\n * ```typescript\n * const listener = (user) => console.log(user);\n * User.on(\"saving\", listener);\n * User.off(\"saving\", listener);\n * ```\n */\n public static off<TModel extends Model = Model, TContext = unknown>(\n this: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n ): void {\n offStaticEvent<TModel, TContext>(this, event, listener);\n }\n\n /**\n * Accesses the global event emitter shared by all model instances.\n *\n * Use this for cross-cutting concerns like auditing, logging, or injecting\n * common fields (e.g., `createdBy`, `updatedBy`) across all models.\n *\n * @returns The global ModelEvents instance\n *\n * @example\n * ```typescript\n * Model.globalEvents().onSaving((model) => {\n * model.set(\"updatedAt\", new Date());\n * });\n * ```\n */\n public static globalEvents(): ModelEvents<Model> {\n return getGlobalEvents();\n }\n\n /**\n * Replace the model's data entirely.\n *\n * Used internally by the writer after validation to update the model\n * with validated/casted data.\n *\n * **Warning:** This replaces all data and updates the dirty tracker.\n * Use with caution in application code.\n *\n * @param data - New data to replace current data\n *\n * @example\n * ```typescript\n * // Internal usage by writer\n * model.replaceData(validatedData);\n * ```\n */\n public replaceData(data: Record<string, unknown>): void {\n replaceModelData(this, data);\n }\n\n /**\n * Save the model to the database.\n *\n * Performs insert if `isNew === true`, otherwise performs update.\n * Automatically validates, casts, generates IDs, and emits lifecycle events.\n *\n * **Features:**\n * - Validation via @warlock.js/seal schema\n * - Data casting (string → number, etc.)\n * - ID generation (NoSQL only)\n * - Partial updates (only changed fields)\n * - Lifecycle events (validating, saving, created/updated, saved)\n *\n * @param data - Optional data to merge before saving\n * @param options - Save options\n * @returns The model instance for method chaining\n *\n * @throws {ValidationError} If validation fails\n * @throws {Error} If database operation fails\n *\n * @example\n * ```typescript\n * // Simple save\n * const user = new User({ name: \"Alice\" });\n * await user.save();\n *\n * // Merge data before saving\n * await user.save({ age: 31, email: \"alice@example.com\" });\n *\n * // Silent save (no events)\n * await user.save(null, { skipEvents: true });\n *\n * // Skip validation\n * await user.save(null, { skipValidation: true });\n *\n * // Method chaining\n * await user.set(\"name\", \"Bob\").save();\n * ```\n */\n public async save(options?: WriterOptions & { merge?: Partial<TSchema> }): Promise<this> {\n return saveModel(this as any, options) as Promise<this>;\n }\n\n /**\n * Serialize the model data for storage in the database.\n *\n * Uses the driver's `serialize` to apply driver-specific type transformations\n * (e.g. Date → ISO string, BigInt → string for Postgres).\n *\n * **Not** the same as `toSnapshot` — this is a DB write concern, not a cache concern.\n */\n public serialize() {\n return serializeModel(this);\n }\n\n /**\n * Produce a plain-object snapshot of this model suitable for cache storage.\n *\n * - `data`: The model's own fields, serialized via the driver (handles Dates, BigInt, ObjectId).\n * - `relations`: Each entry in `loadedRelations` recursively snapshotted via `toSnapshot`.\n * A relation that was loaded but resolved to `null` is stored as `null` (not omitted),\n * so that `fromSnapshot` can distinguish \"loaded + null\" from \"never loaded\".\n *\n * Use `Model.fromSnapshot(snapshot)` to reconstruct.\n *\n * @example\n * ```typescript\n * await cache.set(key, chat.toSnapshot());\n * ```\n */\n public toSnapshot(): ModelSnapshot {\n return modelToSnapshot(this);\n }\n\n /**\n * Reconstruct a model instance (with relations) from a cache snapshot.\n *\n * Counterpart to `toSnapshot`. Applies driver deserialization (e.g. ISO string → Date)\n * and recursively hydrates any nested relation snapshots via `RelationHydrator`.\n *\n * @example\n * ```typescript\n * const snapshot = await cache.get(key);\n * const chat = Chat.fromSnapshot(snapshot);\n * chat.unit; // Unit model instance, fully hydrated\n * ```\n */\n public static fromSnapshot<TModel extends Model>(\n this: ChildModel<TModel>,\n snapshot: ModelSnapshot,\n ): TModel {\n return modelFromSnapshot(this, snapshot);\n }\n\n /**\n * Create a model instance from raw data (no relations).\n *\n * This is the data-only hydration path, used by the query builder when\n * converting a raw DB row into a model instance. Relations are NOT restored\n * here — use `fromSnapshot` when restoring from a cache snapshot that\n * includes relation data.\n *\n * @example\n * ```typescript\n * // Query builder internals:\n * const user = User.hydrate(rawRow);\n * ```\n */\n public static hydrate<TModel extends Model = Model>(\n this: ChildModel<TModel>,\n data: Record<string, unknown>,\n ): TModel {\n return hydrateModel(this, data);\n }\n\n /**\n * Convert the model into JSON\n */\n public toJSON() {\n return modelToJSON(this);\n }\n}\n"],"mappings":"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"}
|
|
@@ -41,7 +41,7 @@ type ModelSchema = Record<string, any>;
|
|
|
41
41
|
* ): Promise<TModel | null>
|
|
42
42
|
* ```
|
|
43
43
|
*/
|
|
44
|
-
type ChildModel<TModel extends Model> = (new (...args: any[]) => TModel) & Pick<typeof Model, "table" | "primaryKey" | "dataSource" | "schema" | "strictMode" | "autoGenerateId" | "initialId" | "randomInitialId" | "incrementIdBy" | "resource" | "resourceColumns" | "toJsonColumns" | "randomIncrement" | "getDataSource" | "getDriver" | "query" | "find" | "first" | "last" | "all" | "latest" | "count" | "where" | "increase" | "decrease" | "atomic" | "events" | "on" | "once" | "off" | "globalEvents" | "delete" | "deleteOne" | "deleteStrategy" | "trashTable" | "restore" | "restoreAll" | "deletedAtColumn" | "createdAtColumn" | "updatedAtColumn" | "create" | "createMany" | "sync" | "embed" | "syncMany" | "addGlobalScope" | "removeGlobalScope" | "addScope" | "removeScope" | "localScopes" | "globalScopes" | "relations" | "newQueryBuilder" | "builder" | "findAndUpdate" | "findOneAndUpdate" | "hydrate" | "fromSnapshot" | "findAndReplace" | "findOneAndDelete" | "findOrCreate">;
|
|
44
|
+
type ChildModel<TModel extends Model> = (new (...args: any[]) => TModel) & Pick<typeof Model, "table" | "primaryKey" | "dataSource" | "schema" | "strictMode" | "autoGenerateId" | "initialId" | "randomInitialId" | "incrementIdBy" | "resource" | "resourceColumns" | "toJsonColumns" | "hidden" | "randomIncrement" | "getDataSource" | "getDriver" | "query" | "find" | "first" | "last" | "all" | "latest" | "count" | "where" | "increase" | "decrease" | "atomic" | "events" | "on" | "once" | "off" | "globalEvents" | "delete" | "deleteOne" | "deleteStrategy" | "trashTable" | "restore" | "restoreAll" | "deletedAtColumn" | "createdAtColumn" | "updatedAtColumn" | "create" | "createMany" | "sync" | "embed" | "syncMany" | "addGlobalScope" | "removeGlobalScope" | "addScope" | "removeScope" | "localScopes" | "globalScopes" | "relations" | "newQueryBuilder" | "builder" | "findAndUpdate" | "findOneAndUpdate" | "hydrate" | "fromSnapshot" | "findAndReplace" | "findOneAndDelete" | "findOrCreate">;
|
|
45
45
|
//#endregion
|
|
46
46
|
export { ChildModel, GlobalScopeDefinition, GlobalScopeOptions, LocalScopeCallback, ModelSchema, ScopeTiming };
|
|
47
47
|
//# sourceMappingURL=model.types.d.mts.map
|
|
@@ -176,11 +176,23 @@ declare class QueryBuilder<T = unknown> {
|
|
|
176
176
|
whereNotBetween(field: string, range: [unknown, unknown]): this;
|
|
177
177
|
/**
|
|
178
178
|
* LIKE pattern match (AND).
|
|
179
|
+
*
|
|
180
|
+
* A string is a LIKE pattern (`%` wildcard) and is matched literally
|
|
181
|
+
* otherwise — regex metacharacters in it are escaped by the driver, so
|
|
182
|
+
* search input cannot alter the query. Pass an explicit `RegExp` to opt into
|
|
183
|
+
* raw pattern semantics; never build that `RegExp` from user input.
|
|
184
|
+
*
|
|
179
185
|
* @example q.whereLike("email", "%@gmail.com")
|
|
180
186
|
*/
|
|
181
187
|
whereLike(field: string, pattern: RegExp | string): this;
|
|
182
|
-
/** NOT LIKE pattern match. */
|
|
188
|
+
/** NOT LIKE pattern match. @see whereLike for the escaping rules. */
|
|
183
189
|
whereNotLike(field: string, pattern: RegExp | string): this;
|
|
190
|
+
/**
|
|
191
|
+
* Flatten a LIKE argument into operation data, keeping the distinction the
|
|
192
|
+
* drivers need: an explicit `RegExp` (developer-authored) stays a pattern,
|
|
193
|
+
* a string (potentially request input) is a literal.
|
|
194
|
+
*/
|
|
195
|
+
private likePatternData;
|
|
184
196
|
/** Starts with a prefix. */
|
|
185
197
|
whereStartsWith(field: string, value: string | number): this;
|
|
186
198
|
/** Does NOT start with a prefix. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-builder.d.mts","names":[],"sources":["../../../../../../../cascade/src/query-builder/query-builder.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"query-builder.d.mts","names":[],"sources":["../../../../../../../cascade/src/query-builder/query-builder.ts"],"mappings":"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"}
|