@rtpaulino/entity 0.24.1 → 0.25.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/entity-utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-function-type */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n ParseOptions,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n SafeOperationResult,\n} from './types.js';\nimport type { EntityOptions } from './entity.js';\nimport {\n getInjectedPropertyNames,\n getInjectedPropertyOptions,\n} from './injected-property.js';\nimport { EntityDI } from './entity-di.js';\nimport { isEqualWith } from 'lodash-es';\nimport { ValidationError } from './validation-error.js';\nimport { Problem } from './problem.js';\nimport {\n prependArrayIndex,\n prependPropertyPath,\n createValidationError,\n} from './validation-utils.js';\nimport {\n isPrimitiveConstructor,\n deserializePrimitive,\n} from './primitive-deserializers.js';\nimport { ok } from 'assert';\nimport { EntityRegistry } from './entity-registry.js';\n\n/**\n * WeakMap to store validation problems for entity instances\n */\nconst problemsStorage = new WeakMap<object, Problem[]>();\n\n/**\n * WeakMap to store raw input data for entity instances\n */\nconst rawInputStorage = new WeakMap<object, unknown>();\n\nexport class EntityUtils {\n /**\n * Checks if a given object is an instance of a class decorated with @Entity()\n * or if the provided value is an entity class itself\n *\n * @param obj - The object or class to check\n * @returns true if the object is an entity instance or entity class, false otherwise\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * const user = new User();\n * console.log(EntityUtils.isEntity(user)); // true\n * console.log(EntityUtils.isEntity(User)); // true\n * console.log(EntityUtils.isEntity({})); // false\n * ```\n */\n static isEntity(obj: unknown): obj is object {\n if (obj == null) {\n return false;\n }\n\n // Check if obj is a constructor function (class)\n if (typeof obj === 'function') {\n return Reflect.hasMetadata(ENTITY_METADATA_KEY, obj);\n }\n\n // Check if obj is an object instance\n if (typeof obj !== 'object' || Array.isArray(obj)) {\n return false;\n }\n\n const constructor = Object.getPrototypeOf(obj).constructor;\n return Reflect.hasMetadata(ENTITY_METADATA_KEY, constructor);\n }\n\n /**\n * Gets the entity options for a given constructor\n *\n * @param entityOrClass - The entity class constructor or instance\n * @returns EntityOptions object (empty object if no options are defined)\n * @private\n */\n private static getEntityOptions(entityOrClass: unknown): EntityOptions {\n const constructor =\n typeof entityOrClass === 'function'\n ? entityOrClass\n : Object.getPrototypeOf(entityOrClass).constructor;\n\n const options: EntityOptions | undefined = Reflect.getMetadata(\n ENTITY_OPTIONS_METADATA_KEY,\n constructor,\n );\n return options ?? {};\n }\n\n /**\n * Gets the registered name for an entity class or instance\n *\n * @param entityOrClass - The entity class constructor or instance\n * @returns The entity name, or undefined if not found\n *\n * @example\n * ```typescript\n * @Entity({ name: 'CustomUser' })\n * class User {\n * name: string;\n * }\n *\n * console.log(EntityUtils.getEntityName(User)); // 'CustomUser'\n * console.log(EntityUtils.getEntityName(new User())); // 'CustomUser'\n * ```\n */\n static getEntityName(entityOrClass: unknown): string | undefined {\n if (!this.isEntity(entityOrClass)) {\n return undefined;\n }\n\n const options = this.getEntityOptions(entityOrClass);\n return options.name;\n }\n\n /**\n * Checks if a given entity is marked as a collection entity\n *\n * @param entityOrClass - The entity instance or class to check\n * @returns true if the entity is a collection entity, false otherwise\n *\n * @example\n * ```typescript\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n *\n * const tags = new Tags({ collection: ['a', 'b'] });\n * console.log(EntityUtils.isCollectionEntity(tags)); // true\n * console.log(EntityUtils.isCollectionEntity(Tags)); // true\n * ```\n */\n static isCollectionEntity(entityOrClass: unknown): boolean {\n if (!this.isEntity(entityOrClass)) {\n return false;\n }\n\n const options = this.getEntityOptions(entityOrClass);\n\n return options.collection === true;\n }\n\n /**\n * Checks if a given entity is marked as a stringifiable entity\n *\n * @param entityOrClass - The entity instance or class to check\n * @returns true if the entity is a stringifiable entity, false otherwise\n *\n * @example\n * ```typescript\n * @Stringifiable()\n * class UserId {\n * @StringProperty()\n * value: string;\n * }\n *\n * const userId = new UserId({ value: 'user-123' });\n * console.log(EntityUtils.isStringifiable(userId)); // true\n * console.log(EntityUtils.isStringifiable(UserId)); // true\n * ```\n */\n static isStringifiable(entityOrClass: unknown): boolean {\n if (!this.isEntity(entityOrClass)) {\n return false;\n }\n\n const options = this.getEntityOptions(entityOrClass);\n\n return options.stringifiable === true;\n }\n\n /**\n * Gets the \"wrapper\" property name for entities that act as transparent wrappers.\n * When set, this property name is excluded from error paths during validation.\n *\n * @param entityOrClass - The entity instance or class to check\n * @returns The wrapper property name, or undefined if not a wrapper entity\n * @private\n */\n private static getWrapperPropertyName(\n entityOrClass: unknown,\n ): string | undefined {\n if (!this.isEntity(entityOrClass)) {\n return undefined;\n }\n return this.getEntityOptions(entityOrClass).wrapperProperty;\n }\n\n static sameEntity(a: object, b: object): boolean {\n if (!this.isEntity(a) || !this.isEntity(b)) {\n return false;\n }\n\n return Object.getPrototypeOf(a) === Object.getPrototypeOf(b);\n }\n\n static getPropertyKeys(target: object): string[] {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n const keys: string[] = [];\n const seen = new Set<string>();\n\n // Walk the prototype chain to collect all inherited properties\n while (currentProto && currentProto !== Object.prototype) {\n // Use getOwnMetadata to only get metadata directly on this prototype\n const protoKeys: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, currentProto) || [];\n\n for (const key of protoKeys) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return keys;\n }\n\n static getPropertyOptions(\n target: object,\n propertyKey: string,\n ): PropertyOptions | undefined {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n // Walk the prototype chain to find the property options\n while (currentProto && currentProto !== Object.prototype) {\n const protoOptions: Record<string, PropertyOptions> =\n Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, currentProto) ||\n {};\n\n if (protoOptions[propertyKey]) {\n return protoOptions[propertyKey];\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return undefined;\n }\n\n static equals(a: unknown, b: unknown): boolean {\n return isEqualWith(a, b, (val1, val2) => {\n if (this.isEntity(val1)) {\n if (!this.sameEntity(val1, val2)) {\n return false;\n }\n\n const diff = this.diff(val1, val2);\n\n return diff.length === 0;\n } else if (\n val1 != null &&\n val2 != null &&\n typeof val1 === 'object' &&\n !Array.isArray(val1) &&\n typeof val2 === 'object' &&\n !Array.isArray(val2) &&\n 'equals' in val1 &&\n typeof val1.equals === 'function'\n ) {\n return val1.equals(val2);\n }\n\n return undefined;\n });\n }\n\n static diff<T extends object>(\n oldEntity: T,\n newEntity: T,\n ): { property: string; oldValue: unknown; newValue: unknown }[] {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute diff');\n }\n\n const diffs: { property: string; oldValue: unknown; newValue: unknown }[] =\n [];\n\n const keys = this.getPropertyKeys(oldEntity);\n\n for (const key of keys) {\n const oldValue = (oldEntity as any)[key];\n const newValue = (newEntity as any)[key];\n\n // Check if there's a custom equals function for this property\n const propertyOptions = this.getPropertyOptions(oldEntity, key);\n\n let areEqual: boolean;\n if (oldValue == null && newValue == null) {\n areEqual = oldValue === newValue;\n } else if (oldValue == null || newValue == null) {\n areEqual = false;\n } else {\n areEqual = propertyOptions?.equals\n ? propertyOptions.equals(oldValue, newValue)\n : this.equals(oldValue, newValue);\n }\n\n if (!areEqual) {\n diffs.push({ property: key, oldValue, newValue });\n }\n }\n\n return diffs;\n }\n\n static changes<T extends object>(oldEntity: T, newEntity: T): Partial<T> {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute changes');\n }\n\n const diff = this.diff(oldEntity, newEntity);\n\n return diff.reduce((acc, { property, newValue }) => {\n (acc as any)[property] = newValue;\n return acc;\n }, {} as Partial<T>);\n }\n\n /**\n * Serializes an entity to a plain object, converting only properties decorated with @Property()\n *\n * @param entity - The entity instance to serialize\n * @returns A plain object containing only the serialized decorated properties, or an array for collection entities\n *\n * @remarks\n * Serialization rules:\n * - Only properties decorated with @Property() are included\n * - If a property has a custom toJSON() method, it will be used\n * - Nested entities are recursively serialized using EntityUtils.toJSON()\n * - Arrays are mapped with toJSON() applied to each element\n * - Date objects are serialized to ISO strings\n * - bigint values are serialized to strings\n * - undefined values are excluded from the output\n * - null values are included in the output\n * - Circular references are not supported (will cause stack overflow)\n * - Collection entities (@CollectionEntity) are unwrapped to just their array\n *\n * @example\n * ```typescript\n * @Entity()\n * class Address {\n * @Property() street: string;\n * @Property() city: string;\n * }\n *\n * @Entity()\n * class User {\n * @Property() name: string;\n * @Property() address: Address;\n * @Property() createdAt: Date;\n * undecorated: string; // Will not be serialized\n * }\n *\n * const user = new User();\n * user.name = 'John';\n * user.address = new Address();\n * user.address.street = '123 Main St';\n * user.address.city = 'Boston';\n * user.createdAt = new Date('2024-01-01');\n * user.undecorated = 'ignored';\n *\n * const json = EntityUtils.toJSON(user);\n * // {\n * // name: 'John',\n * // address: { street: '123 Main St', city: 'Boston' },\n * // createdAt: '2024-01-01T00:00:00.000Z'\n * // }\n *\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n *\n * const tags = new Tags({ collection: ['a', 'b'] });\n * const json = EntityUtils.toJSON(tags);\n * // ['a', 'b'] - unwrapped to array\n * ```\n */\n static toJSON<T extends object>(entity: T): unknown {\n if (this.isStringifiable(entity)) {\n const valuePropertyOptions = this.getPropertyOptions(entity, 'value');\n if (!valuePropertyOptions) {\n throw new Error(\n `Stringifiable entity 'value' property is missing metadata`,\n );\n }\n if (valuePropertyOptions.array) {\n throw new Error(\n `Stringifiable entity 'value' property must not be an array`,\n );\n }\n if (valuePropertyOptions.type?.() !== String) {\n throw new Error(\n `Stringifiable entity 'value' property must be of type String`,\n );\n }\n\n return this.serializeValue((entity as any).value, valuePropertyOptions);\n }\n\n if (this.isCollectionEntity(entity)) {\n const collectionPropertyOptions = this.getPropertyOptions(\n entity,\n 'collection',\n );\n if (!collectionPropertyOptions) {\n throw new Error(\n `Collection entity 'collection' property is missing metadata`,\n );\n }\n if (!collectionPropertyOptions.array) {\n throw new Error(\n `Collection entity 'collection' property must be an array`,\n );\n }\n\n return this.serializeValue(\n (entity as any).collection,\n collectionPropertyOptions,\n );\n }\n\n const result: Record<string, unknown> = {};\n const keys = this.getPropertyKeys(entity);\n\n for (const key of keys) {\n const value = (entity as any)[key];\n\n // Skip undefined values\n if (value === undefined) {\n continue;\n }\n\n const options = this.getPropertyOptions(entity, key);\n result[key] = this.serializeValue(value, options);\n }\n\n return result;\n }\n\n /**\n * Serializes a single value according to the toJSON rules\n * @private\n */\n private static serializeValue(\n value: unknown,\n options?: PropertyOptions,\n ): unknown {\n if (value === null) {\n return null;\n }\n\n if (value === undefined) {\n return undefined;\n }\n\n const passthrough = options?.passthrough === true;\n if (passthrough) {\n return value;\n }\n\n if (Array.isArray(value)) {\n if (options?.serialize) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return value.map((item) => options.serialize!(item as any));\n }\n return value.map((item) => this.serializeValue(item, options));\n }\n\n if (options?.serialize) {\n return options.serialize(value as any);\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (typeof value === 'bigint') {\n return value.toString();\n }\n\n if (this.isEntity(value)) {\n const serialized = this.toJSON(value);\n\n // If this is a discriminated entity property, add the discriminator inline\n if (options?.discriminated === true) {\n const discriminatorProperty = options.discriminatorProperty;\n ok(discriminatorProperty, 'Discriminator property must be defined');\n\n const entityClass = Object.getPrototypeOf(value).constructor;\n const entityName = this.getEntityName(entityClass);\n\n if (!entityName) {\n throw new Error(\n `Cannot serialize discriminated entity: Entity class '${entityClass.name}' is not registered. Ensure it's decorated with @Entity().`,\n );\n }\n\n if (\n typeof serialized !== 'object' ||\n Array.isArray(serialized) ||\n serialized === null\n ) {\n throw new Error(\n `Cannot serialize discriminated entity: Expected serialized value to be an object.`,\n );\n }\n\n return {\n ...serialized,\n [discriminatorProperty]: entityName,\n } as Record<string, unknown>;\n }\n\n return serialized;\n }\n\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n\n throw new Error(\n `Cannot serialize value of type '${typeof value}'. Use passthrough: true in @Property() to explicitly allow serialization of unknown types.`,\n );\n }\n\n /**\n * Internal parse implementation with extended options\n * @private\n */\n private static async _parseInternal<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options: {\n strict?: boolean;\n skipDefaults?: boolean;\n skipMissing?: boolean;\n } = {},\n ): Promise<{ data: Record<string, unknown>; hardProblems: Problem[] }> {\n const wrapperPropertyName = this.getWrapperPropertyName(entityClass);\n\n if (wrapperPropertyName) {\n plainObject = { [wrapperPropertyName]: plainObject };\n }\n if (plainObject == null) {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n if (Array.isArray(plainObject)) {\n throw createValidationError(`Expects an object but received array`);\n }\n if (typeof plainObject !== 'object') {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n\n const strict = options.strict ?? false;\n const skipDefaults = options.skipDefaults ?? false;\n const skipMissing = options.skipMissing ?? false;\n const keys = this.getPropertyKeys(entityClass.prototype);\n const data: Record<string, unknown> = {};\n const hardProblems: Problem[] = [];\n\n for (const key of keys) {\n const propertyOptions = this.getPropertyOptions(\n entityClass.prototype,\n key,\n );\n\n if (!propertyOptions) {\n hardProblems.push(\n new Problem({\n property: key,\n message: `Property has no metadata. This should not happen if @Property() was used correctly.`,\n }),\n );\n continue;\n }\n\n const value = (plainObject as Record<string, unknown>)[key];\n\n if (propertyOptions.passthrough === true) {\n data[key] = value;\n continue;\n }\n\n const isOptional = propertyOptions.optional === true;\n\n if (!(key in plainObject) || value == null) {\n if (skipMissing) {\n continue;\n }\n\n let valueToSet = value;\n\n if (!skipDefaults && propertyOptions.default !== undefined) {\n valueToSet =\n typeof propertyOptions.default === 'function'\n ? await propertyOptions.default()\n : propertyOptions.default;\n }\n\n if (!isOptional && valueToSet == null) {\n hardProblems.push(\n new Problem({\n property: key,\n message:\n 'Required property is missing, null or undefined from input',\n }),\n );\n }\n data[key] = valueToSet;\n continue;\n }\n\n try {\n // Only pass strict to nested deserialization, not skipDefaults/skipMissing\n data[key] = await this.deserializeValue(value, propertyOptions, {\n strict,\n });\n } catch (error) {\n if (error instanceof ValidationError) {\n const isWrapperProperty = wrapperPropertyName === key;\n if (isWrapperProperty) {\n hardProblems.push(...error.problems);\n } else {\n const problems = prependPropertyPath(key, error.problems);\n hardProblems.push(...problems);\n }\n } else if (error instanceof Error) {\n hardProblems.push(\n new Problem({\n property: wrapperPropertyName === key ? '' : key,\n message: error.message,\n }),\n );\n } else {\n throw error;\n }\n }\n }\n\n return { data, hardProblems };\n }\n\n /**\n * Deserializes a plain object to an entity instance\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param parseOptions - Parse options (strict mode)\n * @returns Promise resolving to a new instance of the entity with deserialized values\n *\n * @remarks\n * Deserialization rules:\n * - All @Property() decorators must include type metadata for parse() to work\n * - Properties without type metadata will throw an error\n * - Required properties (optional !== true) must be present and not null/undefined\n * - Optional properties (optional === true) can be undefined or null\n * - Arrays are supported with the array: true option\n * - Nested entities are recursively deserialized\n * - Type conversion is strict (no coercion)\n * - Entity constructors must accept a required data parameter\n *\n * Validation behavior:\n * - If strict: true - both HARD and SOFT problems throw ValidationError\n * - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored\n * - Property validators run first, then entity validators\n * - Validators can be synchronous or asynchronous\n * - Problems are accessible via EntityUtils.getProblems()\n * - Raw input data is accessible via EntityUtils.getRawInput()\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const json = { name: 'John', age: 30 };\n * const user = await EntityUtils.parse(User, json);\n * const userStrict = await EntityUtils.parse(User, json, { strict: true });\n * ```\n */\n static async parse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n parseOptions: ParseOptions = {},\n ): Promise<T> {\n const strict = parseOptions?.strict ?? false;\n\n const { data, hardProblems } = await this._parseInternal(\n entityClass,\n plainObject,\n { strict },\n );\n\n if (hardProblems.length > 0) {\n throw new ValidationError(hardProblems);\n }\n\n await this.addInjectedDependencies(data, entityClass.prototype);\n\n const instance = new entityClass(data);\n\n rawInputStorage.set(instance, plainObject as Record<string, unknown>);\n\n const problems = await this.validate(instance);\n\n if (problems.length > 0 && strict) {\n throw new ValidationError(problems);\n }\n\n return instance;\n }\n\n /**\n * Safely deserializes a plain object to an entity instance without throwing errors\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param parseOptions - Parse options (strict mode)\n * @returns Promise resolving to a result object with success flag, data, and problems\n *\n * @remarks\n * Similar to parse() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data, problems: [] }\n * - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)\n * - On failure - returns { success: false, data: undefined, problems: [...] }\n *\n * All deserialization and validation rules from parse() apply.\n * See parse() documentation for detailed deserialization behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const result = await EntityUtils.safeParse(User, { name: 'John', age: 30 });\n * if (result.success) {\n * console.log(result.data); // User instance\n * console.log(result.problems); // [] or soft problems if not strict\n * } else {\n * console.log(result.problems); // Hard problems\n * }\n * ```\n */\n static async safeParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n parseOptions?: ParseOptions,\n ): SafeOperationResult<T> {\n try {\n const data = await this.parse(entityClass, plainObject, parseOptions);\n const problems = this.getProblems(data);\n\n return {\n success: true,\n data,\n problems,\n };\n } catch (error) {\n if (error instanceof ValidationError) {\n return {\n success: false,\n data: undefined,\n problems: error.problems,\n };\n }\n throw error;\n }\n }\n\n /**\n * Partially deserializes a plain object, returning a plain object with only present properties\n *\n * @param entityClass - The entity class constructor\n * @param plainObject - The plain object to deserialize\n * @param options - Options with strict mode\n * @returns Promise resolving to a plain object with deserialized properties (Partial<T>)\n *\n * @remarks\n * Differences from parse():\n * - Returns a plain object, not an entity instance\n * - Ignores missing properties (does not include them in result)\n * - Does NOT apply default values to missing properties\n * - When strict: false (default), properties with HARD problems are excluded from result but problems are tracked\n * - When strict: true, any HARD problem throws ValidationError\n * - Nested entities/arrays are still fully deserialized and validated as normal\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number, default: 0 }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const partial = await EntityUtils.partialParse(User, { name: 'John' });\n * // partial = { name: 'John' } (age is not included, default not applied)\n *\n * const partialWithError = await EntityUtils.partialParse(User, { name: 'John', age: 'invalid' });\n * // partialWithError = { name: 'John' } (age excluded due to HARD problem)\n * // Access problems via second return value\n * ```\n */\n static async partialParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options: { strict?: boolean } = {},\n ): Promise<Partial<T>> {\n const result = await this.safePartialParse(\n entityClass,\n plainObject,\n options,\n );\n\n if (!result.success) {\n throw new ValidationError(result.problems);\n }\n\n return result.data;\n }\n\n /**\n * Safely performs partial deserialization without throwing errors\n *\n * @param entityClass - The entity class constructor\n * @param plainObject - The plain object to deserialize\n * @param options - Options with strict mode\n * @returns Promise resolving to a result object with success flag, partial data, and problems\n *\n * @remarks\n * Similar to partialParse() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data: Partial<T>, problems: [] }\n * - On success with strict: false - returns { success: true, data: Partial<T>, problems: [...] } (includes hard problems for excluded properties)\n * - On failure (strict mode only) - returns { success: false, data: undefined, problems: [...] }\n *\n * All partial deserialization rules from partialParse() apply.\n * See partialParse() documentation for detailed behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const result = await EntityUtils.safePartialParse(User, { name: 'John', age: 'invalid' });\n * if (result.success) {\n * console.log(result.data); // { name: 'John' }\n * console.log(result.problems); // [Problem for age property]\n * } else {\n * console.log(result.problems); // Hard problems (only in strict mode)\n * }\n * ```\n */\n static async safePartialParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options?: { strict?: boolean },\n ): Promise<SafeOperationResult<Partial<T>>> {\n const strict = options?.strict ?? false;\n\n const { data, hardProblems } = await this._parseInternal(\n entityClass,\n plainObject,\n { strict, skipDefaults: true, skipMissing: true },\n );\n\n if (strict && hardProblems.length > 0) {\n return {\n success: false,\n data: undefined,\n problems: hardProblems,\n };\n }\n\n const propertyProblems = await this.validateProperties(\n data,\n entityClass.prototype,\n );\n const validationProblems = [...hardProblems, ...propertyProblems];\n\n if (strict && propertyProblems.length > 0) {\n return {\n success: false,\n data: undefined,\n problems: validationProblems,\n };\n }\n\n this.setProblems(data, validationProblems);\n\n return {\n success: true,\n data: data as Partial<T>,\n problems: validationProblems,\n };\n }\n\n /**\n * Updates an entity instance with new values, respecting preventUpdates flags on properties\n *\n * @param instance - The entity instance to update. Must be an Entity.\n * @param updates - Partial object with properties to update\n * @param options - Update options (strict mode)\n * @returns Promise resolving to a new instance with updated values\n *\n * @remarks\n * Update behavior:\n * - Creates a shallow copy of the instance\n * - For each @Property(), copies the value from updates if it exists\n * - Properties with preventUpdates: true will not be copied from updates\n * - Runs entity validators after applying updates\n * - Throws ValidationError if validation fails and strict: true\n * - Soft problems are stored on the instance if strict: false (default)\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => String, preventUpdates: true }) id!: string;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const user = new User({ id: '123', name: 'John' });\n * const updated = await EntityUtils.update(user, { id: '456', name: 'Jane' });\n * // updated.id === '123' (not updated due to preventUpdates: true)\n * // updated.name === 'Jane'\n * ```\n */\n static async update<T extends object>(\n instance: T,\n updates: Partial<T>,\n options?: { strict?: boolean },\n ): Promise<T> {\n if (!this.isEntity(instance)) {\n throw new Error('Cannot update non-entity instance');\n }\n\n const strict = options?.strict ?? false;\n const Constructor = Object.getPrototypeOf(instance).constructor;\n const keys = this.getPropertyKeys(instance);\n const data: Record<string, unknown> = {};\n\n // Copy existing properties\n for (const key of keys) {\n const value = (instance as any)[key];\n data[key] = value;\n }\n\n // Apply updates, respecting preventUpdates flag\n for (const key of keys) {\n if (key in updates) {\n const propertyOptions = this.getPropertyOptions(instance, key);\n if (propertyOptions && propertyOptions.preventUpdates === true) {\n // Skip updating this property\n continue;\n }\n data[key] = (updates as any)[key];\n }\n }\n\n const newInstance = new Constructor(data);\n\n const problems = await this.validate(newInstance);\n\n if (problems.length > 0 && strict) {\n throw new ValidationError(problems);\n }\n\n return newInstance;\n }\n\n /**\n * Safely updates an entity instance without throwing errors\n *\n * @param instance - The entity instance to update. Must be an Entity.\n * @param updates - Partial object with properties to update\n * @param options - Update options (strict mode)\n * @returns Promise resolving to a result object with success flag, data, and problems\n *\n * @remarks\n * Similar to update() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data, problems: [] }\n * - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)\n * - On failure - returns { success: false, data: undefined, problems: [...] }\n *\n * All update and validation rules from update() apply.\n * See update() documentation for detailed update behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const user = new User({ name: 'John' });\n * const result = await EntityUtils.safeUpdate(user, { name: 'Jane' });\n * if (result.success) {\n * console.log(result.data); // Updated User instance\n * console.log(result.problems); // [] or soft problems if not strict\n * } else {\n * console.log(result.problems); // Hard problems\n * }\n * ```\n */\n static async safeUpdate<T extends object>(\n instance: T,\n updates: Partial<T>,\n options?: { strict?: boolean },\n ): SafeOperationResult<T> {\n try {\n const updatedInstance = await this.update(instance, updates, options);\n const problems = this.getProblems(updatedInstance);\n\n return {\n success: true,\n data: updatedInstance,\n problems,\n };\n } catch (error) {\n if (error instanceof ValidationError) {\n return {\n success: false,\n data: undefined,\n problems: error.problems,\n };\n }\n throw error;\n }\n }\n\n /**\n * Deserializes a single value according to the type metadata\n * @private\n */\n private static async deserializeValue(\n value: unknown,\n options: PropertyOptions,\n parseOptions: ParseOptions,\n ): Promise<unknown> {\n const isArray = options.array === true;\n const isSparse = options.sparse === true;\n const isDiscriminated = options.discriminated === true;\n\n if (isArray) {\n if (!Array.isArray(value)) {\n throw createValidationError(\n `Expects an array but received ${typeof value}`,\n );\n }\n\n const arrayProblems: Problem[] = [];\n const result: unknown[] = [];\n\n for (let index = 0; index < value.length; index++) {\n const item = value[index];\n if (item === null || item === undefined) {\n if (!isSparse) {\n arrayProblems.push(\n new Problem({\n property: `[${index}]`,\n message: 'Cannot be null or undefined.',\n }),\n );\n }\n result.push(item);\n } else {\n try {\n if (options.deserialize) {\n result.push(options.deserialize(item));\n } else if (isDiscriminated) {\n result.push(\n await this.deserializeDiscriminatedValue(item, options),\n );\n } else {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const typeConstructor = options.type!();\n result.push(\n await this.deserializeSingleValue(\n item,\n typeConstructor,\n parseOptions,\n ),\n );\n }\n } catch (error) {\n if (error instanceof ValidationError) {\n const problems = prependArrayIndex(index, error);\n arrayProblems.push(...problems);\n } else {\n throw error;\n }\n }\n }\n }\n\n if (arrayProblems.length > 0) {\n throw new ValidationError(arrayProblems);\n }\n\n return result;\n }\n\n if (options.deserialize) {\n return options.deserialize(value);\n }\n\n if (isDiscriminated) {\n return await this.deserializeDiscriminatedValue(value, options);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const typeConstructor = options.type!();\n return await this.deserializeSingleValue(\n value,\n typeConstructor,\n parseOptions,\n );\n }\n\n /**\n * Deserializes a single non-array value\n * Reports validation errors with empty property (caller will prepend context)\n * @private\n */\n private static async deserializeSingleValue(\n value: unknown,\n typeConstructor: any,\n parseOptions: ParseOptions,\n ): Promise<unknown> {\n if (isPrimitiveConstructor(typeConstructor)) {\n return deserializePrimitive(value, typeConstructor);\n }\n\n if (this.isEntity(typeConstructor)) {\n return await this.parse(\n typeConstructor as new (data: any) => object,\n value as Record<string, unknown>,\n parseOptions,\n );\n }\n\n throw createValidationError(\n `Has unknown type constructor. Supported types are: String, Number, Boolean, Date, BigInt, and @Entity() classes. Use passthrough: true to explicitly allow unknown types.`,\n );\n }\n\n /**\n * Deserializes a discriminated entity value by reading the discriminator and looking up the entity class\n * @private\n */\n private static async deserializeDiscriminatedValue(\n value: unknown,\n options: PropertyOptions,\n ): Promise<unknown> {\n if (value == null) {\n throw createValidationError(\n `Cannot deserialize discriminated entity from null or undefined`,\n );\n }\n\n if (typeof value !== 'object' || Array.isArray(value)) {\n throw createValidationError(\n `Discriminated entity must be an object, received ${typeof value}`,\n );\n }\n\n const discriminatorProperty = options.discriminatorProperty;\n ok(discriminatorProperty, 'Discriminator property must be defined');\n\n const discriminatorValue = (value as Record<string, unknown>)[\n discriminatorProperty\n ];\n\n if (\n typeof discriminatorValue !== 'string' ||\n discriminatorValue.trim() === ''\n ) {\n throw createValidationError(\n `Missing or invalid discriminator property '${discriminatorProperty}'. Expected a non-empty string.`,\n );\n }\n\n const entityClass = EntityRegistry.get(discriminatorValue);\n\n if (!entityClass) {\n throw createValidationError(\n `Unknown entity type '${discriminatorValue}'. No entity registered with this name.`,\n );\n }\n\n return await this.parse(\n entityClass as new (data: any) => object,\n value as Record<string, unknown>,\n {},\n );\n }\n\n /**\n * Validates a property value by running validators and nested entity validation.\n * Prepends the property path to all returned problems.\n * @private\n */\n private static async validatePropertyValue(\n propertyPath: string,\n value: unknown,\n validators: PropertyOptions['validators'],\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n\n if (validators) {\n for (const validator of validators) {\n const validatorProblems = await validator({ value });\n if (validatorProblems.length > 0) {\n const prepended = prependPropertyPath(\n propertyPath,\n validatorProblems,\n );\n problems.push(...prepended);\n }\n }\n }\n\n if (EntityUtils.isEntity(value)) {\n const existingProblems = problemsStorage.get(value);\n const nestedProblems =\n existingProblems && existingProblems.length > 0\n ? existingProblems\n : await EntityUtils.validate(value);\n\n if (nestedProblems.length > 0) {\n const prepended = prependPropertyPath(propertyPath, nestedProblems);\n problems.push(...prepended);\n }\n }\n\n return problems;\n }\n\n /**\n * Checks if an array contains any entity elements\n * @private\n */\n private static hasEntityElements(value: unknown): boolean {\n if (!Array.isArray(value)) {\n return false;\n }\n return value.some((element) => this.isEntity(element));\n }\n\n /**\n * Runs property validators for a given property value\n * @private\n */\n private static async runPropertyValidators(\n key: string,\n value: unknown,\n options: PropertyOptions,\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n const isArray = options?.array === true;\n const isPassthrough = options?.passthrough === true;\n\n if (isPassthrough || !isArray) {\n const valueProblems = await this.validatePropertyValue(\n key,\n value,\n options.validators,\n );\n problems.push(...valueProblems);\n } else {\n ok(Array.isArray(value), 'Value must be an array for array property');\n\n const arrayValidators = options.arrayValidators || [];\n for (const validator of arrayValidators) {\n const validatorProblems = await validator({ value });\n if (validatorProblems.length > 0) {\n const prepended = prependPropertyPath(key, validatorProblems);\n problems.push(...prepended);\n }\n }\n\n const validators = options.validators || [];\n if (validators.length > 0 || this.hasEntityElements(value)) {\n for (let i = 0; i < value.length; i++) {\n const element = value[i];\n if (element !== null && element !== undefined) {\n const elementPath = `${key}[${i}]`;\n const elementProblems = await this.validatePropertyValue(\n elementPath,\n element,\n validators,\n );\n problems.push(...elementProblems);\n }\n }\n }\n }\n\n return problems;\n }\n\n /**\n * Validates all properties on an object (entity instance or plain object)\n * @private\n */\n private static async validateProperties(\n dataOrInstance: Record<string, unknown> | object,\n prototype: object,\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n const keys = Object.keys(dataOrInstance);\n const wrapperProperty = this.getWrapperPropertyName(dataOrInstance);\n\n for (const key of keys) {\n const options = this.getPropertyOptions(prototype, key);\n if (options) {\n const value = (dataOrInstance as any)[key];\n if (value != null) {\n const propertyPath = wrapperProperty === key ? '' : key;\n const validationProblems = await this.runPropertyValidators(\n propertyPath,\n value,\n options,\n );\n problems.push(...validationProblems);\n }\n }\n }\n\n return problems;\n }\n\n private static async addInjectedDependencies(\n data: Record<string, unknown>,\n prototype: object,\n ): Promise<void> {\n const injectedPropertyNames = getInjectedPropertyNames(prototype);\n if (injectedPropertyNames.length === 0) {\n return;\n }\n\n const injectedPropertyOptions = getInjectedPropertyOptions(prototype);\n\n for (const propertyName of injectedPropertyNames) {\n const token = injectedPropertyOptions[propertyName];\n if (token) {\n const dependency = await EntityDI.get(token);\n data[propertyName] = dependency;\n }\n }\n }\n\n /**\n * Validates an entity instance by running all property and entity validators\n *\n * @param instance - The entity instance to validate\n * @returns Promise resolving to array of Problems found during validation (empty if valid)\n *\n * @remarks\n * - Property validators run first, then entity validators\n * - Each validator can be synchronous or asynchronous\n * - Empty array means no problems found\n *\n * @example\n * ```typescript\n * const user = new User({ name: '', age: -5 });\n * const problems = await EntityUtils.validate(user);\n * console.log(problems); // [Problem, Problem, ...]\n * ```\n */\n static async validate<T extends object>(instance: T): Promise<Problem[]> {\n if (!this.isEntity(instance)) {\n throw new Error('Cannot validate non-entity instance');\n }\n\n const problems: Problem[] = [];\n\n const propertyProblems = await this.validateProperties(instance, instance);\n problems.push(...propertyProblems);\n\n const entityValidators = this.getEntityValidators(instance);\n for (const validatorMethod of entityValidators) {\n const validatorProblems = await (instance as any)[validatorMethod]();\n if (Array.isArray(validatorProblems)) {\n problems.push(...validatorProblems);\n }\n }\n\n EntityUtils.setProblems(instance, problems);\n\n return problems;\n }\n\n /**\n * Gets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @returns Array of Problems (empty if no problems or instance not parsed)\n *\n * @remarks\n * - Only returns problems from the last parse() call\n * - Returns empty array if instance was not created via parse()\n * - Returns empty array if parse() was called with strict: true\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, data);\n * const problems = EntityUtils.getProblems(user);\n * console.log(problems); // [Problem, ...]\n * ```\n */\n static getProblems<T extends object>(instance: T): Problem[] {\n return problemsStorage.get(instance) || [];\n }\n\n /**\n * Sets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @param problems - Array of Problems to associate with the instance\n *\n * @remarks\n * - Overwrites any existing problems for the instance\n * - Pass an empty array to clear problems\n *\n * @example\n * ```typescript\n * const user = new User({ name: 'John' });\n * EntityUtils.setProblems(user, [new Problem({ property: 'name', message: 'Invalid name' })]);\n * ```\n */\n static setProblems<T extends object>(instance: T, problems: Problem[]): void {\n if (problems.length === 0) {\n problemsStorage.delete(instance);\n } else {\n problemsStorage.set(instance, problems);\n }\n }\n\n /**\n * Gets the raw input data that was used to create an entity instance\n *\n * @param instance - The entity instance\n * @returns The raw input object, or undefined if not available\n *\n * @remarks\n * - Only available for instances created via parse()\n * - Returns a reference to the original input data (not a copy)\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, { name: 'John', age: 30 });\n * const rawInput = EntityUtils.getRawInput(user);\n * console.log(rawInput); // { name: 'John', age: 30 }\n * ```\n */\n static getRawInput<T extends object>(instance: T): unknown {\n return rawInputStorage.get(instance);\n }\n\n /**\n * Sets the raw input data for an entity instance\n *\n * @param instance - The entity instance\n * @param rawInput - The raw input object to associate with the instance\n *\n * @remarks\n * - Overwrites any existing raw input for the instance\n * - Pass undefined to clear the raw input\n *\n * @example\n * ```typescript\n * const user = new User({ name: 'John' });\n * EntityUtils.setRawInput(user, { name: 'John', age: 30 });\n * ```\n */\n static setRawInput<T extends object>(\n instance: T,\n rawInput: Record<string, unknown> | undefined,\n ): void {\n if (rawInput === undefined) {\n rawInputStorage.delete(instance);\n } else {\n rawInputStorage.set(instance, rawInput);\n }\n }\n\n /**\n * Gets all entity validator method names for an entity\n * @private\n */\n private static getEntityValidators(target: object): string[] {\n let currentProto: any;\n\n if (target.constructor && target === target.constructor.prototype) {\n currentProto = target;\n } else {\n currentProto = Object.getPrototypeOf(target);\n }\n\n const validators: string[] = [];\n const seen = new Set<string>();\n\n while (currentProto && currentProto !== Object.prototype) {\n const protoValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, currentProto) ||\n [];\n\n for (const validator of protoValidators) {\n if (!seen.has(validator)) {\n seen.add(validator);\n validators.push(validator);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return validators;\n }\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","getInjectedPropertyNames","getInjectedPropertyOptions","EntityDI","isEqualWith","ValidationError","Problem","prependArrayIndex","prependPropertyPath","createValidationError","isPrimitiveConstructor","deserializePrimitive","ok","EntityRegistry","problemsStorage","WeakMap","rawInputStorage","EntityUtils","isEntity","obj","Reflect","hasMetadata","Array","isArray","constructor","Object","getPrototypeOf","getEntityOptions","entityOrClass","options","getMetadata","getEntityName","undefined","name","isCollectionEntity","collection","isStringifiable","stringifiable","getWrapperPropertyName","wrapperProperty","sameEntity","a","b","getPropertyKeys","target","currentProto","prototype","keys","seen","Set","protoKeys","getOwnMetadata","key","has","add","push","getPropertyOptions","propertyKey","protoOptions","equals","val1","val2","diff","length","oldEntity","newEntity","Error","diffs","oldValue","newValue","propertyOptions","areEqual","property","changes","reduce","acc","toJSON","entity","valuePropertyOptions","array","type","String","serializeValue","value","collectionPropertyOptions","result","passthrough","serialize","map","item","Date","toISOString","toString","serialized","discriminated","discriminatorProperty","entityClass","entityName","_parseInternal","plainObject","wrapperPropertyName","strict","skipDefaults","skipMissing","data","hardProblems","message","isOptional","optional","valueToSet","default","deserializeValue","error","isWrapperProperty","problems","parse","parseOptions","addInjectedDependencies","instance","set","validate","safeParse","getProblems","success","partialParse","safePartialParse","propertyProblems","validateProperties","validationProblems","setProblems","update","updates","Constructor","preventUpdates","newInstance","safeUpdate","updatedInstance","isSparse","sparse","isDiscriminated","arrayProblems","index","deserialize","deserializeDiscriminatedValue","typeConstructor","deserializeSingleValue","discriminatorValue","trim","get","validatePropertyValue","propertyPath","validators","validator","validatorProblems","prepended","existingProblems","nestedProblems","hasEntityElements","some","element","runPropertyValidators","isPassthrough","valueProblems","arrayValidators","i","elementPath","elementProblems","dataOrInstance","injectedPropertyNames","injectedPropertyOptions","propertyName","token","dependency","entityValidators","getEntityValidators","validatorMethod","delete","getRawInput","setRawInput","rawInput","protoValidators"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/lib/entity-utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-function-type */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n ParseOptions,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n SafeOperationResult,\n} from './types.js';\nimport type { EntityOptions } from './entity.js';\nimport {\n getInjectedPropertyNames,\n getInjectedPropertyOptions,\n} from './injected-property.js';\nimport { EntityDI } from './entity-di.js';\nimport { isEqualWith } from 'lodash-es';\nimport { ValidationError } from './validation-error.js';\nimport { Problem } from './problem.js';\nimport {\n prependArrayIndex,\n prependPropertyPath,\n createValidationError,\n} from './validation-utils.js';\nimport {\n isPrimitiveConstructor,\n deserializePrimitive,\n} from './primitive-deserializers.js';\nimport { ok } from 'assert';\nimport { EntityRegistry } from './entity-registry.js';\nimport { PolymorphicRegistry } from './polymorphic-registry.js';\n\n/**\n * WeakMap to store validation problems for entity instances\n */\nconst problemsStorage = new WeakMap<object, Problem[]>();\n\n/**\n * WeakMap to store raw input data for entity instances\n */\nconst rawInputStorage = new WeakMap<object, unknown>();\n\nexport class EntityUtils {\n /**\n * Checks if a given object is an instance of a class decorated with @Entity()\n * or if the provided value is an entity class itself\n *\n * @param obj - The object or class to check\n * @returns true if the object is an entity instance or entity class, false otherwise\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * const user = new User();\n * console.log(EntityUtils.isEntity(user)); // true\n * console.log(EntityUtils.isEntity(User)); // true\n * console.log(EntityUtils.isEntity({})); // false\n * ```\n */\n static isEntity(obj: unknown): obj is object {\n if (obj == null) {\n return false;\n }\n\n // Check if obj is a constructor function (class)\n if (typeof obj === 'function') {\n return Reflect.hasMetadata(ENTITY_METADATA_KEY, obj);\n }\n\n // Check if obj is an object instance\n if (typeof obj !== 'object' || Array.isArray(obj)) {\n return false;\n }\n\n const constructor = Object.getPrototypeOf(obj).constructor;\n return Reflect.hasMetadata(ENTITY_METADATA_KEY, constructor);\n }\n\n /**\n * Gets the entity options for a given constructor\n *\n * @param entityOrClass - The entity class constructor or instance\n * @returns EntityOptions object (empty object if no options are defined)\n * @private\n */\n private static getEntityOptions(entityOrClass: unknown): EntityOptions {\n const constructor =\n typeof entityOrClass === 'function'\n ? entityOrClass\n : Object.getPrototypeOf(entityOrClass).constructor;\n\n const options: EntityOptions | undefined = Reflect.getMetadata(\n ENTITY_OPTIONS_METADATA_KEY,\n constructor,\n );\n return options ?? {};\n }\n\n /**\n * Gets the registered name for an entity class or instance\n *\n * @param entityOrClass - The entity class constructor or instance\n * @returns The entity name, or undefined if not found\n *\n * @example\n * ```typescript\n * @Entity({ name: 'CustomUser' })\n * class User {\n * name: string;\n * }\n *\n * console.log(EntityUtils.getEntityName(User)); // 'CustomUser'\n * console.log(EntityUtils.getEntityName(new User())); // 'CustomUser'\n * ```\n */\n static getEntityName(entityOrClass: unknown): string | undefined {\n if (!this.isEntity(entityOrClass)) {\n return undefined;\n }\n\n const options = this.getEntityOptions(entityOrClass);\n return options.name;\n }\n\n /**\n * Checks if a given entity is marked as a collection entity\n *\n * @param entityOrClass - The entity instance or class to check\n * @returns true if the entity is a collection entity, false otherwise\n *\n * @example\n * ```typescript\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n *\n * const tags = new Tags({ collection: ['a', 'b'] });\n * console.log(EntityUtils.isCollectionEntity(tags)); // true\n * console.log(EntityUtils.isCollectionEntity(Tags)); // true\n * ```\n */\n static isCollectionEntity(entityOrClass: unknown): boolean {\n if (!this.isEntity(entityOrClass)) {\n return false;\n }\n\n const options = this.getEntityOptions(entityOrClass);\n\n return options.collection === true;\n }\n\n /**\n * Checks if a given entity is marked as a stringifiable entity\n *\n * @param entityOrClass - The entity instance or class to check\n * @returns true if the entity is a stringifiable entity, false otherwise\n *\n * @example\n * ```typescript\n * @Stringifiable()\n * class UserId {\n * @StringProperty()\n * value: string;\n * }\n *\n * const userId = new UserId({ value: 'user-123' });\n * console.log(EntityUtils.isStringifiable(userId)); // true\n * console.log(EntityUtils.isStringifiable(UserId)); // true\n * ```\n */\n static isStringifiable(entityOrClass: unknown): boolean {\n if (!this.isEntity(entityOrClass)) {\n return false;\n }\n\n const options = this.getEntityOptions(entityOrClass);\n\n return options.stringifiable === true;\n }\n\n /**\n * Gets the \"wrapper\" property name for entities that act as transparent wrappers.\n * When set, this property name is excluded from error paths during validation.\n *\n * @param entityOrClass - The entity instance or class to check\n * @returns The wrapper property name, or undefined if not a wrapper entity\n * @private\n */\n private static getWrapperPropertyName(\n entityOrClass: unknown,\n ): string | undefined {\n if (!this.isEntity(entityOrClass)) {\n return undefined;\n }\n return this.getEntityOptions(entityOrClass).wrapperProperty;\n }\n\n static sameEntity(a: object, b: object): boolean {\n if (!this.isEntity(a) || !this.isEntity(b)) {\n return false;\n }\n\n return Object.getPrototypeOf(a) === Object.getPrototypeOf(b);\n }\n\n static getPropertyKeys(target: object): string[] {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n const keys: string[] = [];\n const seen = new Set<string>();\n\n // Walk the prototype chain to collect all inherited properties\n while (currentProto && currentProto !== Object.prototype) {\n // Use getOwnMetadata to only get metadata directly on this prototype\n const protoKeys: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, currentProto) || [];\n\n for (const key of protoKeys) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return keys;\n }\n\n static getPropertyOptions(\n target: object,\n propertyKey: string,\n ): PropertyOptions | undefined {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n // Walk the prototype chain to find the property options\n while (currentProto && currentProto !== Object.prototype) {\n const protoOptions: Record<string, PropertyOptions> =\n Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, currentProto) ||\n {};\n\n if (protoOptions[propertyKey]) {\n return protoOptions[propertyKey];\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return undefined;\n }\n\n static equals(a: unknown, b: unknown): boolean {\n return isEqualWith(a, b, (val1, val2) => {\n if (this.isEntity(val1)) {\n if (!this.sameEntity(val1, val2)) {\n return false;\n }\n\n const diff = this.diff(val1, val2);\n\n return diff.length === 0;\n } else if (\n val1 != null &&\n val2 != null &&\n typeof val1 === 'object' &&\n !Array.isArray(val1) &&\n typeof val2 === 'object' &&\n !Array.isArray(val2) &&\n 'equals' in val1 &&\n typeof val1.equals === 'function'\n ) {\n return val1.equals(val2);\n }\n\n return undefined;\n });\n }\n\n static diff<T extends object>(\n oldEntity: T,\n newEntity: T,\n ): { property: string; oldValue: unknown; newValue: unknown }[] {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute diff');\n }\n\n const diffs: { property: string; oldValue: unknown; newValue: unknown }[] =\n [];\n\n const keys = this.getPropertyKeys(oldEntity);\n\n for (const key of keys) {\n const oldValue = (oldEntity as any)[key];\n const newValue = (newEntity as any)[key];\n\n // Check if there's a custom equals function for this property\n const propertyOptions = this.getPropertyOptions(oldEntity, key);\n\n let areEqual: boolean;\n if (oldValue == null && newValue == null) {\n areEqual = oldValue === newValue;\n } else if (oldValue == null || newValue == null) {\n areEqual = false;\n } else {\n areEqual = propertyOptions?.equals\n ? propertyOptions.equals(oldValue, newValue)\n : this.equals(oldValue, newValue);\n }\n\n if (!areEqual) {\n diffs.push({ property: key, oldValue, newValue });\n }\n }\n\n return diffs;\n }\n\n static changes<T extends object>(oldEntity: T, newEntity: T): Partial<T> {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute changes');\n }\n\n const diff = this.diff(oldEntity, newEntity);\n\n return diff.reduce((acc, { property, newValue }) => {\n (acc as any)[property] = newValue;\n return acc;\n }, {} as Partial<T>);\n }\n\n /**\n * Serializes an entity to a plain object, converting only properties decorated with @Property()\n *\n * @param entity - The entity instance to serialize\n * @returns A plain object containing only the serialized decorated properties, or an array for collection entities\n *\n * @remarks\n * Serialization rules:\n * - Only properties decorated with @Property() are included\n * - If a property has a custom toJSON() method, it will be used\n * - Nested entities are recursively serialized using EntityUtils.toJSON()\n * - Arrays are mapped with toJSON() applied to each element\n * - Date objects are serialized to ISO strings\n * - bigint values are serialized to strings\n * - undefined values are excluded from the output\n * - null values are included in the output\n * - Circular references are not supported (will cause stack overflow)\n * - Collection entities (@CollectionEntity) are unwrapped to just their array\n *\n * @example\n * ```typescript\n * @Entity()\n * class Address {\n * @Property() street: string;\n * @Property() city: string;\n * }\n *\n * @Entity()\n * class User {\n * @Property() name: string;\n * @Property() address: Address;\n * @Property() createdAt: Date;\n * undecorated: string; // Will not be serialized\n * }\n *\n * const user = new User();\n * user.name = 'John';\n * user.address = new Address();\n * user.address.street = '123 Main St';\n * user.address.city = 'Boston';\n * user.createdAt = new Date('2024-01-01');\n * user.undecorated = 'ignored';\n *\n * const json = EntityUtils.toJSON(user);\n * // {\n * // name: 'John',\n * // address: { street: '123 Main St', city: 'Boston' },\n * // createdAt: '2024-01-01T00:00:00.000Z'\n * // }\n *\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n *\n * const tags = new Tags({ collection: ['a', 'b'] });\n * const json = EntityUtils.toJSON(tags);\n * // ['a', 'b'] - unwrapped to array\n * ```\n */\n static toJSON<T extends object>(entity: T): unknown {\n if (this.isStringifiable(entity)) {\n const valuePropertyOptions = this.getPropertyOptions(entity, 'value');\n if (!valuePropertyOptions) {\n throw new Error(\n `Stringifiable entity 'value' property is missing metadata`,\n );\n }\n if (valuePropertyOptions.array) {\n throw new Error(\n `Stringifiable entity 'value' property must not be an array`,\n );\n }\n if (valuePropertyOptions.type?.() !== String) {\n throw new Error(\n `Stringifiable entity 'value' property must be of type String`,\n );\n }\n\n return this.serializeValue((entity as any).value, valuePropertyOptions);\n }\n\n if (this.isCollectionEntity(entity)) {\n const collectionPropertyOptions = this.getPropertyOptions(\n entity,\n 'collection',\n );\n if (!collectionPropertyOptions) {\n throw new Error(\n `Collection entity 'collection' property is missing metadata`,\n );\n }\n if (!collectionPropertyOptions.array) {\n throw new Error(\n `Collection entity 'collection' property must be an array`,\n );\n }\n\n return this.serializeValue(\n (entity as any).collection,\n collectionPropertyOptions,\n );\n }\n\n const result: Record<string, unknown> = {};\n const keys = this.getPropertyKeys(entity);\n\n for (const key of keys) {\n const value = (entity as any)[key];\n\n // Skip undefined values\n if (value === undefined) {\n continue;\n }\n\n const options = this.getPropertyOptions(entity, key);\n result[key] = this.serializeValue(value, options);\n }\n\n return result;\n }\n\n /**\n * Serializes a single value according to the toJSON rules\n * @private\n */\n private static serializeValue(\n value: unknown,\n options?: PropertyOptions,\n ): unknown {\n if (value === null) {\n return null;\n }\n\n if (value === undefined) {\n return undefined;\n }\n\n const passthrough = options?.passthrough === true;\n if (passthrough) {\n return value;\n }\n\n if (Array.isArray(value)) {\n if (options?.serialize) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return value.map((item) => options.serialize!(item as any));\n }\n return value.map((item) => this.serializeValue(item, options));\n }\n\n if (options?.serialize) {\n return options.serialize(value as any);\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (typeof value === 'bigint') {\n return value.toString();\n }\n\n if (this.isEntity(value)) {\n const serialized = this.toJSON(value);\n\n // If this is a discriminated entity property, add the discriminator inline\n if (options?.discriminated === true) {\n const discriminatorProperty = options.discriminatorProperty;\n ok(discriminatorProperty, 'Discriminator property must be defined');\n\n const entityClass = Object.getPrototypeOf(value).constructor;\n const entityName = this.getEntityName(entityClass);\n\n if (!entityName) {\n throw new Error(\n `Cannot serialize discriminated entity: Entity class '${entityClass.name}' is not registered. Ensure it's decorated with @Entity().`,\n );\n }\n\n if (\n typeof serialized !== 'object' ||\n Array.isArray(serialized) ||\n serialized === null\n ) {\n throw new Error(\n `Cannot serialize discriminated entity: Expected serialized value to be an object.`,\n );\n }\n\n return {\n ...serialized,\n [discriminatorProperty]: entityName,\n } as Record<string, unknown>;\n }\n\n return serialized;\n }\n\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n\n throw new Error(\n `Cannot serialize value of type '${typeof value}'. Use passthrough: true in @Property() to explicitly allow serialization of unknown types.`,\n );\n }\n\n /**\n * Internal parse implementation with extended options\n * @private\n */\n private static async _parseInternal<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options: {\n strict?: boolean;\n skipDefaults?: boolean;\n skipMissing?: boolean;\n } = {},\n ): Promise<{\n data: Record<string, unknown>;\n hardProblems: Problem[];\n entityClass: new (data: any) => T;\n }> {\n const wrapperPropertyName = this.getWrapperPropertyName(entityClass);\n\n if (wrapperPropertyName) {\n plainObject = { [wrapperPropertyName]: plainObject };\n }\n if (plainObject == null) {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n if (Array.isArray(plainObject)) {\n throw createValidationError(`Expects an object but received array`);\n }\n if (typeof plainObject !== 'object') {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n\n // Check for polymorphic base class - resolve to concrete variant if found\n const discriminatorProperty =\n PolymorphicRegistry.getDiscriminatorProperty(entityClass);\n\n if (discriminatorProperty) {\n const discriminatorValue = (plainObject as Record<string, unknown>)[\n discriminatorProperty\n ];\n\n if (discriminatorValue === undefined) {\n throw createValidationError(\n `Missing polymorphic discriminator property '${discriminatorProperty}' ` +\n `for base class '${entityClass.name}'. ` +\n `The discriminator property is required to determine the correct variant class.`,\n );\n }\n\n const variantClass = PolymorphicRegistry.getVariant(\n entityClass,\n discriminatorValue,\n );\n\n if (!variantClass) {\n throw createValidationError(\n `Unknown polymorphic variant '${String(discriminatorValue)}' ` +\n `for base class '${entityClass.name}'. ` +\n `Discriminator property: '${discriminatorProperty}'. ` +\n `Ensure the variant class is decorated with @PolymorphicVariant.`,\n );\n }\n\n // Recursively parse as the concrete variant class\n return this._parseInternal(\n variantClass as new (data: any) => T,\n plainObject,\n options,\n );\n }\n\n const strict = options.strict ?? false;\n const skipDefaults = options.skipDefaults ?? false;\n const skipMissing = options.skipMissing ?? false;\n const keys = this.getPropertyKeys(entityClass.prototype);\n const data: Record<string, unknown> = {};\n const hardProblems: Problem[] = [];\n\n for (const key of keys) {\n const propertyOptions = this.getPropertyOptions(\n entityClass.prototype,\n key,\n );\n\n if (!propertyOptions) {\n hardProblems.push(\n new Problem({\n property: key,\n message: `Property has no metadata. This should not happen if @Property() was used correctly.`,\n }),\n );\n continue;\n }\n\n const value = (plainObject as Record<string, unknown>)[key];\n\n if (propertyOptions.passthrough === true) {\n data[key] = value;\n continue;\n }\n\n const isOptional = propertyOptions.optional === true;\n\n if (!(key in plainObject) || value == null) {\n if (skipMissing) {\n continue;\n }\n\n let valueToSet = value;\n\n if (!skipDefaults && propertyOptions.default !== undefined) {\n valueToSet =\n typeof propertyOptions.default === 'function'\n ? await propertyOptions.default()\n : propertyOptions.default;\n }\n\n if (!isOptional && valueToSet == null) {\n hardProblems.push(\n new Problem({\n property: key,\n message:\n 'Required property is missing, null or undefined from input',\n }),\n );\n }\n data[key] = valueToSet;\n continue;\n }\n\n try {\n // Only pass strict to nested deserialization, not skipDefaults/skipMissing\n data[key] = await this.deserializeValue(value, propertyOptions, {\n strict,\n });\n } catch (error) {\n if (error instanceof ValidationError) {\n const isWrapperProperty = wrapperPropertyName === key;\n if (isWrapperProperty) {\n hardProblems.push(...error.problems);\n } else {\n const problems = prependPropertyPath(key, error.problems);\n hardProblems.push(...problems);\n }\n } else if (error instanceof Error) {\n hardProblems.push(\n new Problem({\n property: wrapperPropertyName === key ? '' : key,\n message: error.message,\n }),\n );\n } else {\n throw error;\n }\n }\n }\n\n return { data, hardProblems, entityClass };\n }\n\n /**\n * Deserializes a plain object to an entity instance\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param parseOptions - Parse options (strict mode)\n * @returns Promise resolving to a new instance of the entity with deserialized values\n *\n * @remarks\n * Deserialization rules:\n * - All @Property() decorators must include type metadata for parse() to work\n * - Properties without type metadata will throw an error\n * - Required properties (optional !== true) must be present and not null/undefined\n * - Optional properties (optional === true) can be undefined or null\n * - Arrays are supported with the array: true option\n * - Nested entities are recursively deserialized\n * - Type conversion is strict (no coercion)\n * - Entity constructors must accept a required data parameter\n *\n * Validation behavior:\n * - If strict: true - both HARD and SOFT problems throw ValidationError\n * - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored\n * - Property validators run first, then entity validators\n * - Validators can be synchronous or asynchronous\n * - Problems are accessible via EntityUtils.getProblems()\n * - Raw input data is accessible via EntityUtils.getRawInput()\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const json = { name: 'John', age: 30 };\n * const user = await EntityUtils.parse(User, json);\n * const userStrict = await EntityUtils.parse(User, json, { strict: true });\n * ```\n */\n static async parse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n parseOptions: ParseOptions = {},\n ): Promise<T> {\n const strict = parseOptions?.strict ?? false;\n\n const {\n data,\n hardProblems,\n entityClass: resolvedEntityClass,\n } = await this._parseInternal(entityClass, plainObject, { strict });\n\n if (hardProblems.length > 0) {\n throw new ValidationError(hardProblems);\n }\n\n await this.addInjectedDependencies(data, resolvedEntityClass.prototype);\n\n const instance = new resolvedEntityClass(data);\n\n rawInputStorage.set(instance, plainObject as Record<string, unknown>);\n\n const problems = await this.validate(instance);\n\n if (problems.length > 0 && strict) {\n throw new ValidationError(problems);\n }\n\n return instance;\n }\n\n /**\n * Safely deserializes a plain object to an entity instance without throwing errors\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param parseOptions - Parse options (strict mode)\n * @returns Promise resolving to a result object with success flag, data, and problems\n *\n * @remarks\n * Similar to parse() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data, problems: [] }\n * - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)\n * - On failure - returns { success: false, data: undefined, problems: [...] }\n *\n * All deserialization and validation rules from parse() apply.\n * See parse() documentation for detailed deserialization behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const result = await EntityUtils.safeParse(User, { name: 'John', age: 30 });\n * if (result.success) {\n * console.log(result.data); // User instance\n * console.log(result.problems); // [] or soft problems if not strict\n * } else {\n * console.log(result.problems); // Hard problems\n * }\n * ```\n */\n static async safeParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n parseOptions?: ParseOptions,\n ): SafeOperationResult<T> {\n try {\n const data = await this.parse(entityClass, plainObject, parseOptions);\n const problems = this.getProblems(data);\n\n return {\n success: true,\n data,\n problems,\n };\n } catch (error) {\n if (error instanceof ValidationError) {\n return {\n success: false,\n data: undefined,\n problems: error.problems,\n };\n }\n throw error;\n }\n }\n\n /**\n * Partially deserializes a plain object, returning a plain object with only present properties\n *\n * @param entityClass - The entity class constructor\n * @param plainObject - The plain object to deserialize\n * @param options - Options with strict mode\n * @returns Promise resolving to a plain object with deserialized properties (Partial<T>)\n *\n * @remarks\n * Differences from parse():\n * - Returns a plain object, not an entity instance\n * - Ignores missing properties (does not include them in result)\n * - Does NOT apply default values to missing properties\n * - When strict: false (default), properties with HARD problems are excluded from result but problems are tracked\n * - When strict: true, any HARD problem throws ValidationError\n * - Nested entities/arrays are still fully deserialized and validated as normal\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number, default: 0 }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const partial = await EntityUtils.partialParse(User, { name: 'John' });\n * // partial = { name: 'John' } (age is not included, default not applied)\n *\n * const partialWithError = await EntityUtils.partialParse(User, { name: 'John', age: 'invalid' });\n * // partialWithError = { name: 'John' } (age excluded due to HARD problem)\n * // Access problems via second return value\n * ```\n */\n static async partialParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options: { strict?: boolean } = {},\n ): Promise<Partial<T>> {\n const result = await this.safePartialParse(\n entityClass,\n plainObject,\n options,\n );\n\n if (!result.success) {\n throw new ValidationError(result.problems);\n }\n\n return result.data;\n }\n\n /**\n * Safely performs partial deserialization without throwing errors\n *\n * @param entityClass - The entity class constructor\n * @param plainObject - The plain object to deserialize\n * @param options - Options with strict mode\n * @returns Promise resolving to a result object with success flag, partial data, and problems\n *\n * @remarks\n * Similar to partialParse() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data: Partial<T>, problems: [] }\n * - On success with strict: false - returns { success: true, data: Partial<T>, problems: [...] } (includes hard problems for excluded properties)\n * - On failure (strict mode only) - returns { success: false, data: undefined, problems: [...] }\n *\n * All partial deserialization rules from partialParse() apply.\n * See partialParse() documentation for detailed behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const result = await EntityUtils.safePartialParse(User, { name: 'John', age: 'invalid' });\n * if (result.success) {\n * console.log(result.data); // { name: 'John' }\n * console.log(result.problems); // [Problem for age property]\n * } else {\n * console.log(result.problems); // Hard problems (only in strict mode)\n * }\n * ```\n */\n static async safePartialParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options?: { strict?: boolean },\n ): Promise<SafeOperationResult<Partial<T>>> {\n const strict = options?.strict ?? false;\n\n const { data, hardProblems } = await this._parseInternal(\n entityClass,\n plainObject,\n { strict, skipDefaults: true, skipMissing: true },\n );\n\n if (strict && hardProblems.length > 0) {\n return {\n success: false,\n data: undefined,\n problems: hardProblems,\n };\n }\n\n const propertyProblems = await this.validateProperties(\n data,\n entityClass.prototype,\n );\n const validationProblems = [...hardProblems, ...propertyProblems];\n\n if (strict && propertyProblems.length > 0) {\n return {\n success: false,\n data: undefined,\n problems: validationProblems,\n };\n }\n\n this.setProblems(data, validationProblems);\n\n return {\n success: true,\n data: data as Partial<T>,\n problems: validationProblems,\n };\n }\n\n /**\n * Updates an entity instance with new values, respecting preventUpdates flags on properties\n *\n * @param instance - The entity instance to update. Must be an Entity.\n * @param updates - Partial object with properties to update\n * @param options - Update options (strict mode)\n * @returns Promise resolving to a new instance with updated values\n *\n * @remarks\n * Update behavior:\n * - Creates a shallow copy of the instance\n * - For each @Property(), copies the value from updates if it exists\n * - Properties with preventUpdates: true will not be copied from updates\n * - Runs entity validators after applying updates\n * - Throws ValidationError if validation fails and strict: true\n * - Soft problems are stored on the instance if strict: false (default)\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => String, preventUpdates: true }) id!: string;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const user = new User({ id: '123', name: 'John' });\n * const updated = await EntityUtils.update(user, { id: '456', name: 'Jane' });\n * // updated.id === '123' (not updated due to preventUpdates: true)\n * // updated.name === 'Jane'\n * ```\n */\n static async update<T extends object>(\n instance: T,\n updates: Partial<T>,\n options?: { strict?: boolean },\n ): Promise<T> {\n if (!this.isEntity(instance)) {\n throw new Error('Cannot update non-entity instance');\n }\n\n const strict = options?.strict ?? false;\n const Constructor = Object.getPrototypeOf(instance).constructor;\n const keys = this.getPropertyKeys(instance);\n const data: Record<string, unknown> = {};\n\n // Copy existing properties\n for (const key of keys) {\n const value = (instance as any)[key];\n data[key] = value;\n }\n\n // Apply updates, respecting preventUpdates flag\n for (const key of keys) {\n if (key in updates) {\n const propertyOptions = this.getPropertyOptions(instance, key);\n if (propertyOptions && propertyOptions.preventUpdates === true) {\n // Skip updating this property\n continue;\n }\n data[key] = (updates as any)[key];\n }\n }\n\n const newInstance = new Constructor(data);\n\n const problems = await this.validate(newInstance);\n\n if (problems.length > 0 && strict) {\n throw new ValidationError(problems);\n }\n\n return newInstance;\n }\n\n /**\n * Safely updates an entity instance without throwing errors\n *\n * @param instance - The entity instance to update. Must be an Entity.\n * @param updates - Partial object with properties to update\n * @param options - Update options (strict mode)\n * @returns Promise resolving to a result object with success flag, data, and problems\n *\n * @remarks\n * Similar to update() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data, problems: [] }\n * - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)\n * - On failure - returns { success: false, data: undefined, problems: [...] }\n *\n * All update and validation rules from update() apply.\n * See update() documentation for detailed update behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const user = new User({ name: 'John' });\n * const result = await EntityUtils.safeUpdate(user, { name: 'Jane' });\n * if (result.success) {\n * console.log(result.data); // Updated User instance\n * console.log(result.problems); // [] or soft problems if not strict\n * } else {\n * console.log(result.problems); // Hard problems\n * }\n * ```\n */\n static async safeUpdate<T extends object>(\n instance: T,\n updates: Partial<T>,\n options?: { strict?: boolean },\n ): SafeOperationResult<T> {\n try {\n const updatedInstance = await this.update(instance, updates, options);\n const problems = this.getProblems(updatedInstance);\n\n return {\n success: true,\n data: updatedInstance,\n problems,\n };\n } catch (error) {\n if (error instanceof ValidationError) {\n return {\n success: false,\n data: undefined,\n problems: error.problems,\n };\n }\n throw error;\n }\n }\n\n /**\n * Deserializes a single value according to the type metadata\n * @private\n */\n private static async deserializeValue(\n value: unknown,\n options: PropertyOptions,\n parseOptions: ParseOptions,\n ): Promise<unknown> {\n const isArray = options.array === true;\n const isSparse = options.sparse === true;\n const isDiscriminated = options.discriminated === true;\n\n if (isArray) {\n if (!Array.isArray(value)) {\n throw createValidationError(\n `Expects an array but received ${typeof value}`,\n );\n }\n\n const arrayProblems: Problem[] = [];\n const result: unknown[] = [];\n\n for (let index = 0; index < value.length; index++) {\n const item = value[index];\n if (item === null || item === undefined) {\n if (!isSparse) {\n arrayProblems.push(\n new Problem({\n property: `[${index}]`,\n message: 'Cannot be null or undefined.',\n }),\n );\n }\n result.push(item);\n } else {\n try {\n if (options.deserialize) {\n result.push(options.deserialize(item));\n } else if (isDiscriminated) {\n result.push(\n await this.deserializeDiscriminatedValue(item, options),\n );\n } else {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const typeConstructor = options.type!();\n result.push(\n await this.deserializeSingleValue(\n item,\n typeConstructor,\n parseOptions,\n ),\n );\n }\n } catch (error) {\n if (error instanceof ValidationError) {\n const problems = prependArrayIndex(index, error);\n arrayProblems.push(...problems);\n } else {\n throw error;\n }\n }\n }\n }\n\n if (arrayProblems.length > 0) {\n throw new ValidationError(arrayProblems);\n }\n\n return result;\n }\n\n if (options.deserialize) {\n return options.deserialize(value);\n }\n\n if (isDiscriminated) {\n return await this.deserializeDiscriminatedValue(value, options);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const typeConstructor = options.type!();\n return await this.deserializeSingleValue(\n value,\n typeConstructor,\n parseOptions,\n );\n }\n\n /**\n * Deserializes a single non-array value\n * Reports validation errors with empty property (caller will prepend context)\n * @private\n */\n private static async deserializeSingleValue(\n value: unknown,\n typeConstructor: any,\n parseOptions: ParseOptions,\n ): Promise<unknown> {\n if (isPrimitiveConstructor(typeConstructor)) {\n return deserializePrimitive(value, typeConstructor);\n }\n\n if (this.isEntity(typeConstructor)) {\n return await this.parse(\n typeConstructor as new (data: any) => object,\n value as Record<string, unknown>,\n parseOptions,\n );\n }\n\n throw createValidationError(\n `Has unknown type constructor. Supported types are: String, Number, Boolean, Date, BigInt, and @Entity() classes. Use passthrough: true to explicitly allow unknown types.`,\n );\n }\n\n /**\n * Deserializes a discriminated entity value by reading the discriminator and looking up the entity class\n * @private\n */\n private static async deserializeDiscriminatedValue(\n value: unknown,\n options: PropertyOptions,\n ): Promise<unknown> {\n if (value == null) {\n throw createValidationError(\n `Cannot deserialize discriminated entity from null or undefined`,\n );\n }\n\n if (typeof value !== 'object' || Array.isArray(value)) {\n throw createValidationError(\n `Discriminated entity must be an object, received ${typeof value}`,\n );\n }\n\n const discriminatorProperty = options.discriminatorProperty;\n ok(discriminatorProperty, 'Discriminator property must be defined');\n\n const discriminatorValue = (value as Record<string, unknown>)[\n discriminatorProperty\n ];\n\n if (\n typeof discriminatorValue !== 'string' ||\n discriminatorValue.trim() === ''\n ) {\n throw createValidationError(\n `Missing or invalid discriminator property '${discriminatorProperty}'. Expected a non-empty string.`,\n );\n }\n\n const entityClass = EntityRegistry.get(discriminatorValue);\n\n if (!entityClass) {\n throw createValidationError(\n `Unknown entity type '${discriminatorValue}'. No entity registered with this name.`,\n );\n }\n\n return await this.parse(\n entityClass as new (data: any) => object,\n value as Record<string, unknown>,\n {},\n );\n }\n\n /**\n * Validates a property value by running validators and nested entity validation.\n * Prepends the property path to all returned problems.\n * @private\n */\n private static async validatePropertyValue(\n propertyPath: string,\n value: unknown,\n validators: PropertyOptions['validators'],\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n\n if (validators) {\n for (const validator of validators) {\n const validatorProblems = await validator({ value });\n if (validatorProblems.length > 0) {\n const prepended = prependPropertyPath(\n propertyPath,\n validatorProblems,\n );\n problems.push(...prepended);\n }\n }\n }\n\n if (EntityUtils.isEntity(value)) {\n const existingProblems = problemsStorage.get(value);\n const nestedProblems =\n existingProblems && existingProblems.length > 0\n ? existingProblems\n : await EntityUtils.validate(value);\n\n if (nestedProblems.length > 0) {\n const prepended = prependPropertyPath(propertyPath, nestedProblems);\n problems.push(...prepended);\n }\n }\n\n return problems;\n }\n\n /**\n * Checks if an array contains any entity elements\n * @private\n */\n private static hasEntityElements(value: unknown): boolean {\n if (!Array.isArray(value)) {\n return false;\n }\n return value.some((element) => this.isEntity(element));\n }\n\n /**\n * Runs property validators for a given property value\n * @private\n */\n private static async runPropertyValidators(\n key: string,\n value: unknown,\n options: PropertyOptions,\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n const isArray = options?.array === true;\n const isPassthrough = options?.passthrough === true;\n\n if (isPassthrough || !isArray) {\n const valueProblems = await this.validatePropertyValue(\n key,\n value,\n options.validators,\n );\n problems.push(...valueProblems);\n } else {\n ok(Array.isArray(value), 'Value must be an array for array property');\n\n const arrayValidators = options.arrayValidators || [];\n for (const validator of arrayValidators) {\n const validatorProblems = await validator({ value });\n if (validatorProblems.length > 0) {\n const prepended = prependPropertyPath(key, validatorProblems);\n problems.push(...prepended);\n }\n }\n\n const validators = options.validators || [];\n if (validators.length > 0 || this.hasEntityElements(value)) {\n for (let i = 0; i < value.length; i++) {\n const element = value[i];\n if (element !== null && element !== undefined) {\n const elementPath = `${key}[${i}]`;\n const elementProblems = await this.validatePropertyValue(\n elementPath,\n element,\n validators,\n );\n problems.push(...elementProblems);\n }\n }\n }\n }\n\n return problems;\n }\n\n /**\n * Validates all properties on an object (entity instance or plain object)\n * @private\n */\n private static async validateProperties(\n dataOrInstance: Record<string, unknown> | object,\n prototype: object,\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n const keys = Object.keys(dataOrInstance);\n const wrapperProperty = this.getWrapperPropertyName(dataOrInstance);\n\n for (const key of keys) {\n const options = this.getPropertyOptions(prototype, key);\n if (options) {\n const value = (dataOrInstance as any)[key];\n if (value != null) {\n const propertyPath = wrapperProperty === key ? '' : key;\n const validationProblems = await this.runPropertyValidators(\n propertyPath,\n value,\n options,\n );\n problems.push(...validationProblems);\n }\n }\n }\n\n return problems;\n }\n\n private static async addInjectedDependencies(\n data: Record<string, unknown>,\n prototype: object,\n ): Promise<void> {\n const injectedPropertyNames = getInjectedPropertyNames(prototype);\n if (injectedPropertyNames.length === 0) {\n return;\n }\n\n const injectedPropertyOptions = getInjectedPropertyOptions(prototype);\n\n for (const propertyName of injectedPropertyNames) {\n const token = injectedPropertyOptions[propertyName];\n if (token) {\n const dependency = await EntityDI.get(token);\n data[propertyName] = dependency;\n }\n }\n }\n\n /**\n * Validates an entity instance by running all property and entity validators\n *\n * @param instance - The entity instance to validate\n * @returns Promise resolving to array of Problems found during validation (empty if valid)\n *\n * @remarks\n * - Property validators run first, then entity validators\n * - Each validator can be synchronous or asynchronous\n * - Empty array means no problems found\n *\n * @example\n * ```typescript\n * const user = new User({ name: '', age: -5 });\n * const problems = await EntityUtils.validate(user);\n * console.log(problems); // [Problem, Problem, ...]\n * ```\n */\n static async validate<T extends object>(instance: T): Promise<Problem[]> {\n if (!this.isEntity(instance)) {\n throw new Error('Cannot validate non-entity instance');\n }\n\n const problems: Problem[] = [];\n\n const propertyProblems = await this.validateProperties(instance, instance);\n problems.push(...propertyProblems);\n\n const entityValidators = this.getEntityValidators(instance);\n for (const validatorMethod of entityValidators) {\n const validatorProblems = await (instance as any)[validatorMethod]();\n if (Array.isArray(validatorProblems)) {\n problems.push(...validatorProblems);\n }\n }\n\n EntityUtils.setProblems(instance, problems);\n\n return problems;\n }\n\n /**\n * Gets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @returns Array of Problems (empty if no problems or instance not parsed)\n *\n * @remarks\n * - Only returns problems from the last parse() call\n * - Returns empty array if instance was not created via parse()\n * - Returns empty array if parse() was called with strict: true\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, data);\n * const problems = EntityUtils.getProblems(user);\n * console.log(problems); // [Problem, ...]\n * ```\n */\n static getProblems<T extends object>(instance: T): Problem[] {\n return problemsStorage.get(instance) || [];\n }\n\n /**\n * Sets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @param problems - Array of Problems to associate with the instance\n *\n * @remarks\n * - Overwrites any existing problems for the instance\n * - Pass an empty array to clear problems\n *\n * @example\n * ```typescript\n * const user = new User({ name: 'John' });\n * EntityUtils.setProblems(user, [new Problem({ property: 'name', message: 'Invalid name' })]);\n * ```\n */\n static setProblems<T extends object>(instance: T, problems: Problem[]): void {\n if (problems.length === 0) {\n problemsStorage.delete(instance);\n } else {\n problemsStorage.set(instance, problems);\n }\n }\n\n /**\n * Gets the raw input data that was used to create an entity instance\n *\n * @param instance - The entity instance\n * @returns The raw input object, or undefined if not available\n *\n * @remarks\n * - Only available for instances created via parse()\n * - Returns a reference to the original input data (not a copy)\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, { name: 'John', age: 30 });\n * const rawInput = EntityUtils.getRawInput(user);\n * console.log(rawInput); // { name: 'John', age: 30 }\n * ```\n */\n static getRawInput<T extends object>(instance: T): unknown {\n return rawInputStorage.get(instance);\n }\n\n /**\n * Sets the raw input data for an entity instance\n *\n * @param instance - The entity instance\n * @param rawInput - The raw input object to associate with the instance\n *\n * @remarks\n * - Overwrites any existing raw input for the instance\n * - Pass undefined to clear the raw input\n *\n * @example\n * ```typescript\n * const user = new User({ name: 'John' });\n * EntityUtils.setRawInput(user, { name: 'John', age: 30 });\n * ```\n */\n static setRawInput<T extends object>(\n instance: T,\n rawInput: Record<string, unknown> | undefined,\n ): void {\n if (rawInput === undefined) {\n rawInputStorage.delete(instance);\n } else {\n rawInputStorage.set(instance, rawInput);\n }\n }\n\n /**\n * Gets all entity validator method names for an entity\n * @private\n */\n private static getEntityValidators(target: object): string[] {\n let currentProto: any;\n\n if (target.constructor && target === target.constructor.prototype) {\n currentProto = target;\n } else {\n currentProto = Object.getPrototypeOf(target);\n }\n\n const validators: string[] = [];\n const seen = new Set<string>();\n\n while (currentProto && currentProto !== Object.prototype) {\n const protoValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, currentProto) ||\n [];\n\n for (const validator of protoValidators) {\n if (!seen.has(validator)) {\n seen.add(validator);\n validators.push(validator);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return validators;\n }\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","getInjectedPropertyNames","getInjectedPropertyOptions","EntityDI","isEqualWith","ValidationError","Problem","prependArrayIndex","prependPropertyPath","createValidationError","isPrimitiveConstructor","deserializePrimitive","ok","EntityRegistry","PolymorphicRegistry","problemsStorage","WeakMap","rawInputStorage","EntityUtils","isEntity","obj","Reflect","hasMetadata","Array","isArray","constructor","Object","getPrototypeOf","getEntityOptions","entityOrClass","options","getMetadata","getEntityName","undefined","name","isCollectionEntity","collection","isStringifiable","stringifiable","getWrapperPropertyName","wrapperProperty","sameEntity","a","b","getPropertyKeys","target","currentProto","prototype","keys","seen","Set","protoKeys","getOwnMetadata","key","has","add","push","getPropertyOptions","propertyKey","protoOptions","equals","val1","val2","diff","length","oldEntity","newEntity","Error","diffs","oldValue","newValue","propertyOptions","areEqual","property","changes","reduce","acc","toJSON","entity","valuePropertyOptions","array","type","String","serializeValue","value","collectionPropertyOptions","result","passthrough","serialize","map","item","Date","toISOString","toString","serialized","discriminated","discriminatorProperty","entityClass","entityName","_parseInternal","plainObject","wrapperPropertyName","getDiscriminatorProperty","discriminatorValue","variantClass","getVariant","strict","skipDefaults","skipMissing","data","hardProblems","message","isOptional","optional","valueToSet","default","deserializeValue","error","isWrapperProperty","problems","parse","parseOptions","resolvedEntityClass","addInjectedDependencies","instance","set","validate","safeParse","getProblems","success","partialParse","safePartialParse","propertyProblems","validateProperties","validationProblems","setProblems","update","updates","Constructor","preventUpdates","newInstance","safeUpdate","updatedInstance","isSparse","sparse","isDiscriminated","arrayProblems","index","deserialize","deserializeDiscriminatedValue","typeConstructor","deserializeSingleValue","trim","get","validatePropertyValue","propertyPath","validators","validator","validatorProblems","prepended","existingProblems","nestedProblems","hasEntityElements","some","element","runPropertyValidators","isPassthrough","valueProblems","arrayValidators","i","elementPath","elementProblems","dataOrInstance","injectedPropertyNames","injectedPropertyOptions","propertyName","token","dependency","entityValidators","getEntityValidators","validatorMethod","delete","getRawInput","setRawInput","rawInput","protoValidators"],"mappings":"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"}
@@ -164,4 +164,64 @@ export declare function Stringifiable(options?: Pick<EntityOptions, 'name'>): Cl
164
164
  * ```
165
165
  */
166
166
  export declare function EntityValidator(): MethodDecorator;
167
+ /**
168
+ * Decorator that registers a class as a polymorphic variant of a base class.
169
+ *
170
+ * Used for class hierarchies where an abstract base class has multiple concrete implementations,
171
+ * and a discriminator property determines which variant to instantiate during parsing.
172
+ *
173
+ * @param baseClass - The base class this variant extends (must have a @PolymorphicProperty)
174
+ * @param discriminatorValue - The value of the discriminator property that identifies this variant
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * enum SchemaPropertyType {
179
+ * STRING = 'string',
180
+ * NUMBER = 'number',
181
+ * }
182
+ *
183
+ * @Entity()
184
+ * abstract class SchemaProperty {
185
+ * @StringProperty({ minLength: 1 })
186
+ * name!: string;
187
+ *
188
+ * @PolymorphicProperty(SchemaPropertyType)
189
+ * type!: SchemaPropertyType;
190
+ * }
191
+ *
192
+ * @Entity()
193
+ * @PolymorphicVariant(SchemaProperty, SchemaPropertyType.STRING)
194
+ * class StringSchemaProperty extends SchemaProperty {
195
+ * readonly type = SchemaPropertyType.STRING;
196
+ *
197
+ * @IntProperty({ optional: true })
198
+ * minLength?: number;
199
+ *
200
+ * constructor(data: { name: string; minLength?: number }) {
201
+ * super({ ...data, type: SchemaPropertyType.STRING });
202
+ * this.minLength = data.minLength;
203
+ * }
204
+ * }
205
+ *
206
+ * @Entity()
207
+ * @PolymorphicVariant(SchemaProperty, SchemaPropertyType.NUMBER)
208
+ * class NumberSchemaProperty extends SchemaProperty {
209
+ * readonly type = SchemaPropertyType.NUMBER;
210
+ *
211
+ * @NumberProperty({ optional: true })
212
+ * min?: number;
213
+ *
214
+ * constructor(data: { name: string; min?: number }) {
215
+ * super({ ...data, type: SchemaPropertyType.NUMBER });
216
+ * this.min = data.min;
217
+ * }
218
+ * }
219
+ *
220
+ * // Parsing automatically selects the correct variant based on 'type'
221
+ * const data = { name: 'age', type: 'number', min: 0 };
222
+ * const prop = await EntityUtils.parse(SchemaProperty, data);
223
+ * // prop is NumberSchemaProperty instance
224
+ * ```
225
+ */
226
+ export declare function PolymorphicVariant<T extends Function>(baseClass: T, discriminatorValue: unknown): ClassDecorator;
167
227
  //# sourceMappingURL=entity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/lib/entity.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,cAAc,CAwBlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GACxC,cAAc,CAMhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GACxC,cAAc,CAEhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAmBjD"}
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/lib/entity.ts"],"names":[],"mappings":"AAUA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,cAAc,CAwBlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GACxC,cAAc,CAMhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GACxC,cAAc,CAEhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAmBjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,QAAQ,EACnD,SAAS,EAAE,CAAC,EACZ,kBAAkB,EAAE,OAAO,GAC1B,cAAc,CAahB"}
@@ -1,5 +1,6 @@
1
- import { ENTITY_METADATA_KEY, ENTITY_OPTIONS_METADATA_KEY, ENTITY_VALIDATOR_METADATA_KEY } from './types.js';
1
+ /* eslint-disable @typescript-eslint/no-unsafe-function-type */ import { ENTITY_METADATA_KEY, ENTITY_OPTIONS_METADATA_KEY, ENTITY_VALIDATOR_METADATA_KEY, POLYMORPHIC_VARIANT_METADATA_KEY } from './types.js';
2
2
  import { EntityRegistry } from './entity-registry.js';
3
+ import { PolymorphicRegistry } from './polymorphic-registry.js';
3
4
  /**
4
5
  * Decorator that marks a class as an Entity.
5
6
  * This allows us to identify entity instances later.
@@ -167,5 +168,75 @@ import { EntityRegistry } from './entity-registry.js';
167
168
  Reflect.defineMetadata(ENTITY_VALIDATOR_METADATA_KEY, existingValidators, target);
168
169
  };
169
170
  }
171
+ /**
172
+ * Decorator that registers a class as a polymorphic variant of a base class.
173
+ *
174
+ * Used for class hierarchies where an abstract base class has multiple concrete implementations,
175
+ * and a discriminator property determines which variant to instantiate during parsing.
176
+ *
177
+ * @param baseClass - The base class this variant extends (must have a @PolymorphicProperty)
178
+ * @param discriminatorValue - The value of the discriminator property that identifies this variant
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * enum SchemaPropertyType {
183
+ * STRING = 'string',
184
+ * NUMBER = 'number',
185
+ * }
186
+ *
187
+ * @Entity()
188
+ * abstract class SchemaProperty {
189
+ * @StringProperty({ minLength: 1 })
190
+ * name!: string;
191
+ *
192
+ * @PolymorphicProperty(SchemaPropertyType)
193
+ * type!: SchemaPropertyType;
194
+ * }
195
+ *
196
+ * @Entity()
197
+ * @PolymorphicVariant(SchemaProperty, SchemaPropertyType.STRING)
198
+ * class StringSchemaProperty extends SchemaProperty {
199
+ * readonly type = SchemaPropertyType.STRING;
200
+ *
201
+ * @IntProperty({ optional: true })
202
+ * minLength?: number;
203
+ *
204
+ * constructor(data: { name: string; minLength?: number }) {
205
+ * super({ ...data, type: SchemaPropertyType.STRING });
206
+ * this.minLength = data.minLength;
207
+ * }
208
+ * }
209
+ *
210
+ * @Entity()
211
+ * @PolymorphicVariant(SchemaProperty, SchemaPropertyType.NUMBER)
212
+ * class NumberSchemaProperty extends SchemaProperty {
213
+ * readonly type = SchemaPropertyType.NUMBER;
214
+ *
215
+ * @NumberProperty({ optional: true })
216
+ * min?: number;
217
+ *
218
+ * constructor(data: { name: string; min?: number }) {
219
+ * super({ ...data, type: SchemaPropertyType.NUMBER });
220
+ * this.min = data.min;
221
+ * }
222
+ * }
223
+ *
224
+ * // Parsing automatically selects the correct variant based on 'type'
225
+ * const data = { name: 'age', type: 'number', min: 0 };
226
+ * const prop = await EntityUtils.parse(SchemaProperty, data);
227
+ * // prop is NumberSchemaProperty instance
228
+ * ```
229
+ */ export function PolymorphicVariant(baseClass, discriminatorValue) {
230
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
231
+ return function(target) {
232
+ // Register the variant in the registry
233
+ PolymorphicRegistry.registerVariant(baseClass, discriminatorValue, target);
234
+ // Store metadata on the class for introspection
235
+ Reflect.defineMetadata(POLYMORPHIC_VARIANT_METADATA_KEY, {
236
+ baseClass,
237
+ discriminatorValue
238
+ }, target);
239
+ };
240
+ }
170
241
 
171
242
  //# sourceMappingURL=entity.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/entity.ts"],"sourcesContent":["import {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n} from './types.js';\nimport { EntityRegistry } from './entity-registry.js';\n\n/**\n * Options for Entity decorator\n */\nexport interface EntityOptions {\n /**\n * Optional name for the entity. Used for discriminated entity deserialization.\n * If not specified, the class name will be used.\n * Must be unique across all entities - conflicts will throw an error.\n */\n name?: string;\n /**\n * Whether this entity represents a collection.\n * Collection entities must have a 'collection' property that is an array.\n * When serialized, collection entities are unwrapped to just their array.\n * When deserialized from an array, the array is wrapped in { collection: [...] }.\n */\n collection?: boolean;\n /**\n * Whether this entity represents a stringifiable value.\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized, stringifiable instances are unwrapped to just their string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n */\n stringifiable?: boolean;\n /**\n * The name of the single \"wrapper\" property for entities that act as transparent wrappers.\n * When set, this property name is excluded from error paths during validation.\n * Used by @CollectionEntity ('collection') and @Stringifiable ('value').\n */\n wrapperProperty?: string;\n /**\n * Whether to register this entity in the EntityRegistry.\n * Set to false for dynamic entities to prevent memory leaks.\n * Defaults to true.\n */\n register?: boolean;\n}\n\n/**\n * Decorator that marks a class as an Entity.\n * This allows us to identify entity instances later.\n *\n * @param options - Optional configuration for the entity\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * @Entity({ name: 'CustomUser' })\n * class User {\n * name: string;\n * }\n *\n * @Entity({ collection: true })\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n * ```\n */\nexport function Entity(options: EntityOptions = {}): ClassDecorator {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n return function (target: Function) {\n // Store metadata on the class constructor\n Reflect.defineMetadata(ENTITY_METADATA_KEY, true, target);\n\n // Determine the entity name - use provided name or fall back to class name\n const entityName = options.name ?? target.name;\n\n // Register the entity in the global registry (unless explicitly disabled)\n const shouldRegister = options.register !== false;\n if (shouldRegister) {\n EntityRegistry.register(entityName, target);\n }\n\n // Store the name in options for later retrieval\n const optionsWithName = { ...options, name: entityName };\n\n Reflect.defineMetadata(\n ENTITY_OPTIONS_METADATA_KEY,\n optionsWithName,\n target,\n );\n };\n}\n\n/**\n * Decorator that marks a class as a Collection Entity.\n * This is syntax sugar for @Entity({ collection: true }).\n *\n * Collection entities must have a 'collection' property that is an array.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the array.\n * When deserialized from an array, the array is wrapped in { collection: [...] }.\n *\n * @example\n * ```typescript\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * readonly collection: string[];\n *\n * constructor(data: { collection: string[] }) {\n * this.collection = data.collection;\n * }\n * }\n *\n * @Entity()\n * class Article {\n * @EntityProperty(() => Tags)\n * tags!: Tags;\n * }\n *\n * const article = new Article(...);\n * const json = EntityUtils.toJSON(article);\n * // { tags: [\"tag1\", \"tag2\"] } - unwrapped to array\n *\n * // Also works when serializing the collection directly:\n * const tagsJson = EntityUtils.toJSON(tags);\n * // [\"tag1\", \"tag2\"] - unwrapped to array\n * ```\n */\nexport function CollectionEntity(\n options: Pick<EntityOptions, 'name'> = {},\n): ClassDecorator {\n return Entity({\n collection: true,\n wrapperProperty: 'collection',\n ...options,\n });\n}\n\n/**\n * Decorator that marks a class as Stringifiable.\n * This is syntax sugar for @Entity({ stringifiable: true }).\n *\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n *\n * @example\n * ```typescript\n * @Stringifiable()\n * class UserId {\n * @StringProperty()\n * readonly value: string;\n *\n * constructor(data: { value: string }) {\n * this.value = data.value;\n * }\n * }\n *\n * @Entity()\n * class User {\n * @EntityProperty(() => UserId)\n * id!: UserId;\n * }\n *\n * const user = new User(...);\n * const json = EntityUtils.toJSON(user);\n * // { id: \"user-123\" } - unwrapped to string\n *\n * // Also works when serializing the stringifiable directly:\n * const userId = new UserId({ value: \"user-123\" });\n * const idJson = EntityUtils.toJSON(userId);\n * // \"user-123\" - unwrapped to string\n *\n * // Parse from string:\n * const parsed = await EntityUtils.parse(UserId, \"user-456\");\n * // UserId { value: \"user-456\" }\n * ```\n */\nexport function Stringifiable(\n options: Pick<EntityOptions, 'name'> = {},\n): ClassDecorator {\n return Entity({ stringifiable: true, wrapperProperty: 'value', ...options });\n}\n\n/**\n * Decorator that marks a method as an entity validator.\n * The method should return an array of Problems.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) firstName!: string;\n * @Property({ type: () => String }) lastName!: string;\n *\n * @EntityValidator()\n * validateNames(): Problem[] {\n * const problems: Problem[] = [];\n * if (this.firstName === this.lastName) {\n * problems.push(new Problem({\n * property: 'firstName',\n * message: 'First and last name cannot be the same'\n * }));\n * }\n * return problems;\n * }\n * }\n * ```\n */\nexport function EntityValidator(): MethodDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n if (typeof propertyKey !== 'string') {\n return;\n }\n\n const existingValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, target) || [];\n\n if (!existingValidators.includes(propertyKey)) {\n existingValidators.push(propertyKey);\n }\n\n Reflect.defineMetadata(\n ENTITY_VALIDATOR_METADATA_KEY,\n existingValidators,\n target,\n );\n };\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","EntityRegistry","Entity","options","target","Reflect","defineMetadata","entityName","name","shouldRegister","register","optionsWithName","CollectionEntity","collection","wrapperProperty","Stringifiable","stringifiable","EntityValidator","propertyKey","existingValidators","getOwnMetadata","includes","push"],"mappings":"AAAA,SACEA,mBAAmB,EACnBC,2BAA2B,EAC3BC,6BAA6B,QACxB,aAAa;AACpB,SAASC,cAAc,QAAQ,uBAAuB;AAwCtD;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,OAAOC,UAAyB,CAAC,CAAC;IAChD,sEAAsE;IACtE,OAAO,SAAUC,MAAgB;QAC/B,0CAA0C;QAC1CC,QAAQC,cAAc,CAACR,qBAAqB,MAAMM;QAElD,2EAA2E;QAC3E,MAAMG,aAAaJ,QAAQK,IAAI,IAAIJ,OAAOI,IAAI;QAE9C,0EAA0E;QAC1E,MAAMC,iBAAiBN,QAAQO,QAAQ,KAAK;QAC5C,IAAID,gBAAgB;YAClBR,eAAeS,QAAQ,CAACH,YAAYH;QACtC;QAEA,gDAAgD;QAChD,MAAMO,kBAAkB;YAAE,GAAGR,OAAO;YAAEK,MAAMD;QAAW;QAEvDF,QAAQC,cAAc,CACpBP,6BACAY,iBACAP;IAEJ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GACD,OAAO,SAASQ,iBACdT,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QACZW,YAAY;QACZC,iBAAiB;QACjB,GAAGX,OAAO;IACZ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GACD,OAAO,SAASY,cACdZ,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QAAEc,eAAe;QAAMF,iBAAiB;QAAS,GAAGX,OAAO;IAAC;AAC5E;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASc;IACd,OAAO,CAACb,QAAgBc;QACtB,IAAI,OAAOA,gBAAgB,UAAU;YACnC;QACF;QAEA,MAAMC,qBACJd,QAAQe,cAAc,CAACpB,+BAA+BI,WAAW,EAAE;QAErE,IAAI,CAACe,mBAAmBE,QAAQ,CAACH,cAAc;YAC7CC,mBAAmBG,IAAI,CAACJ;QAC1B;QAEAb,QAAQC,cAAc,CACpBN,+BACAmB,oBACAf;IAEJ;AACF"}
1
+ {"version":3,"sources":["../../src/lib/entity.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-function-type */\nimport {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n POLYMORPHIC_VARIANT_METADATA_KEY,\n} from './types.js';\nimport { EntityRegistry } from './entity-registry.js';\nimport { PolymorphicRegistry } from './polymorphic-registry.js';\n\n/**\n * Options for Entity decorator\n */\nexport interface EntityOptions {\n /**\n * Optional name for the entity. Used for discriminated entity deserialization.\n * If not specified, the class name will be used.\n * Must be unique across all entities - conflicts will throw an error.\n */\n name?: string;\n /**\n * Whether this entity represents a collection.\n * Collection entities must have a 'collection' property that is an array.\n * When serialized, collection entities are unwrapped to just their array.\n * When deserialized from an array, the array is wrapped in { collection: [...] }.\n */\n collection?: boolean;\n /**\n * Whether this entity represents a stringifiable value.\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized, stringifiable instances are unwrapped to just their string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n */\n stringifiable?: boolean;\n /**\n * The name of the single \"wrapper\" property for entities that act as transparent wrappers.\n * When set, this property name is excluded from error paths during validation.\n * Used by @CollectionEntity ('collection') and @Stringifiable ('value').\n */\n wrapperProperty?: string;\n /**\n * Whether to register this entity in the EntityRegistry.\n * Set to false for dynamic entities to prevent memory leaks.\n * Defaults to true.\n */\n register?: boolean;\n}\n\n/**\n * Decorator that marks a class as an Entity.\n * This allows us to identify entity instances later.\n *\n * @param options - Optional configuration for the entity\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * @Entity({ name: 'CustomUser' })\n * class User {\n * name: string;\n * }\n *\n * @Entity({ collection: true })\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n * ```\n */\nexport function Entity(options: EntityOptions = {}): ClassDecorator {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n return function (target: Function) {\n // Store metadata on the class constructor\n Reflect.defineMetadata(ENTITY_METADATA_KEY, true, target);\n\n // Determine the entity name - use provided name or fall back to class name\n const entityName = options.name ?? target.name;\n\n // Register the entity in the global registry (unless explicitly disabled)\n const shouldRegister = options.register !== false;\n if (shouldRegister) {\n EntityRegistry.register(entityName, target);\n }\n\n // Store the name in options for later retrieval\n const optionsWithName = { ...options, name: entityName };\n\n Reflect.defineMetadata(\n ENTITY_OPTIONS_METADATA_KEY,\n optionsWithName,\n target,\n );\n };\n}\n\n/**\n * Decorator that marks a class as a Collection Entity.\n * This is syntax sugar for @Entity({ collection: true }).\n *\n * Collection entities must have a 'collection' property that is an array.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the array.\n * When deserialized from an array, the array is wrapped in { collection: [...] }.\n *\n * @example\n * ```typescript\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * readonly collection: string[];\n *\n * constructor(data: { collection: string[] }) {\n * this.collection = data.collection;\n * }\n * }\n *\n * @Entity()\n * class Article {\n * @EntityProperty(() => Tags)\n * tags!: Tags;\n * }\n *\n * const article = new Article(...);\n * const json = EntityUtils.toJSON(article);\n * // { tags: [\"tag1\", \"tag2\"] } - unwrapped to array\n *\n * // Also works when serializing the collection directly:\n * const tagsJson = EntityUtils.toJSON(tags);\n * // [\"tag1\", \"tag2\"] - unwrapped to array\n * ```\n */\nexport function CollectionEntity(\n options: Pick<EntityOptions, 'name'> = {},\n): ClassDecorator {\n return Entity({\n collection: true,\n wrapperProperty: 'collection',\n ...options,\n });\n}\n\n/**\n * Decorator that marks a class as Stringifiable.\n * This is syntax sugar for @Entity({ stringifiable: true }).\n *\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n *\n * @example\n * ```typescript\n * @Stringifiable()\n * class UserId {\n * @StringProperty()\n * readonly value: string;\n *\n * constructor(data: { value: string }) {\n * this.value = data.value;\n * }\n * }\n *\n * @Entity()\n * class User {\n * @EntityProperty(() => UserId)\n * id!: UserId;\n * }\n *\n * const user = new User(...);\n * const json = EntityUtils.toJSON(user);\n * // { id: \"user-123\" } - unwrapped to string\n *\n * // Also works when serializing the stringifiable directly:\n * const userId = new UserId({ value: \"user-123\" });\n * const idJson = EntityUtils.toJSON(userId);\n * // \"user-123\" - unwrapped to string\n *\n * // Parse from string:\n * const parsed = await EntityUtils.parse(UserId, \"user-456\");\n * // UserId { value: \"user-456\" }\n * ```\n */\nexport function Stringifiable(\n options: Pick<EntityOptions, 'name'> = {},\n): ClassDecorator {\n return Entity({ stringifiable: true, wrapperProperty: 'value', ...options });\n}\n\n/**\n * Decorator that marks a method as an entity validator.\n * The method should return an array of Problems.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) firstName!: string;\n * @Property({ type: () => String }) lastName!: string;\n *\n * @EntityValidator()\n * validateNames(): Problem[] {\n * const problems: Problem[] = [];\n * if (this.firstName === this.lastName) {\n * problems.push(new Problem({\n * property: 'firstName',\n * message: 'First and last name cannot be the same'\n * }));\n * }\n * return problems;\n * }\n * }\n * ```\n */\nexport function EntityValidator(): MethodDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n if (typeof propertyKey !== 'string') {\n return;\n }\n\n const existingValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, target) || [];\n\n if (!existingValidators.includes(propertyKey)) {\n existingValidators.push(propertyKey);\n }\n\n Reflect.defineMetadata(\n ENTITY_VALIDATOR_METADATA_KEY,\n existingValidators,\n target,\n );\n };\n}\n\n/**\n * Decorator that registers a class as a polymorphic variant of a base class.\n *\n * Used for class hierarchies where an abstract base class has multiple concrete implementations,\n * and a discriminator property determines which variant to instantiate during parsing.\n *\n * @param baseClass - The base class this variant extends (must have a @PolymorphicProperty)\n * @param discriminatorValue - The value of the discriminator property that identifies this variant\n *\n * @example\n * ```typescript\n * enum SchemaPropertyType {\n * STRING = 'string',\n * NUMBER = 'number',\n * }\n *\n * @Entity()\n * abstract class SchemaProperty {\n * @StringProperty({ minLength: 1 })\n * name!: string;\n *\n * @PolymorphicProperty(SchemaPropertyType)\n * type!: SchemaPropertyType;\n * }\n *\n * @Entity()\n * @PolymorphicVariant(SchemaProperty, SchemaPropertyType.STRING)\n * class StringSchemaProperty extends SchemaProperty {\n * readonly type = SchemaPropertyType.STRING;\n *\n * @IntProperty({ optional: true })\n * minLength?: number;\n *\n * constructor(data: { name: string; minLength?: number }) {\n * super({ ...data, type: SchemaPropertyType.STRING });\n * this.minLength = data.minLength;\n * }\n * }\n *\n * @Entity()\n * @PolymorphicVariant(SchemaProperty, SchemaPropertyType.NUMBER)\n * class NumberSchemaProperty extends SchemaProperty {\n * readonly type = SchemaPropertyType.NUMBER;\n *\n * @NumberProperty({ optional: true })\n * min?: number;\n *\n * constructor(data: { name: string; min?: number }) {\n * super({ ...data, type: SchemaPropertyType.NUMBER });\n * this.min = data.min;\n * }\n * }\n *\n * // Parsing automatically selects the correct variant based on 'type'\n * const data = { name: 'age', type: 'number', min: 0 };\n * const prop = await EntityUtils.parse(SchemaProperty, data);\n * // prop is NumberSchemaProperty instance\n * ```\n */\nexport function PolymorphicVariant<T extends Function>(\n baseClass: T,\n discriminatorValue: unknown,\n): ClassDecorator {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n return function (target: Function) {\n // Register the variant in the registry\n PolymorphicRegistry.registerVariant(baseClass, discriminatorValue, target);\n\n // Store metadata on the class for introspection\n Reflect.defineMetadata(\n POLYMORPHIC_VARIANT_METADATA_KEY,\n { baseClass, discriminatorValue },\n target,\n );\n };\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","POLYMORPHIC_VARIANT_METADATA_KEY","EntityRegistry","PolymorphicRegistry","Entity","options","target","Reflect","defineMetadata","entityName","name","shouldRegister","register","optionsWithName","CollectionEntity","collection","wrapperProperty","Stringifiable","stringifiable","EntityValidator","propertyKey","existingValidators","getOwnMetadata","includes","push","PolymorphicVariant","baseClass","discriminatorValue","registerVariant"],"mappings":"AAAA,6DAA6D,GAC7D,SACEA,mBAAmB,EACnBC,2BAA2B,EAC3BC,6BAA6B,EAC7BC,gCAAgC,QAC3B,aAAa;AACpB,SAASC,cAAc,QAAQ,uBAAuB;AACtD,SAASC,mBAAmB,QAAQ,4BAA4B;AAwChE;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,OAAOC,UAAyB,CAAC,CAAC;IAChD,sEAAsE;IACtE,OAAO,SAAUC,MAAgB;QAC/B,0CAA0C;QAC1CC,QAAQC,cAAc,CAACV,qBAAqB,MAAMQ;QAElD,2EAA2E;QAC3E,MAAMG,aAAaJ,QAAQK,IAAI,IAAIJ,OAAOI,IAAI;QAE9C,0EAA0E;QAC1E,MAAMC,iBAAiBN,QAAQO,QAAQ,KAAK;QAC5C,IAAID,gBAAgB;YAClBT,eAAeU,QAAQ,CAACH,YAAYH;QACtC;QAEA,gDAAgD;QAChD,MAAMO,kBAAkB;YAAE,GAAGR,OAAO;YAAEK,MAAMD;QAAW;QAEvDF,QAAQC,cAAc,CACpBT,6BACAc,iBACAP;IAEJ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GACD,OAAO,SAASQ,iBACdT,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QACZW,YAAY;QACZC,iBAAiB;QACjB,GAAGX,OAAO;IACZ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GACD,OAAO,SAASY,cACdZ,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QAAEc,eAAe;QAAMF,iBAAiB;QAAS,GAAGX,OAAO;IAAC;AAC5E;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASc;IACd,OAAO,CAACb,QAAgBc;QACtB,IAAI,OAAOA,gBAAgB,UAAU;YACnC;QACF;QAEA,MAAMC,qBACJd,QAAQe,cAAc,CAACtB,+BAA+BM,WAAW,EAAE;QAErE,IAAI,CAACe,mBAAmBE,QAAQ,CAACH,cAAc;YAC7CC,mBAAmBG,IAAI,CAACJ;QAC1B;QAEAb,QAAQC,cAAc,CACpBR,+BACAqB,oBACAf;IAEJ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DC,GACD,OAAO,SAASmB,mBACdC,SAAY,EACZC,kBAA2B;IAE3B,sEAAsE;IACtE,OAAO,SAAUrB,MAAgB;QAC/B,uCAAuC;QACvCH,oBAAoByB,eAAe,CAACF,WAAWC,oBAAoBrB;QAEnE,gDAAgD;QAChDC,QAAQC,cAAc,CACpBP,kCACA;YAAEyB;YAAWC;QAAmB,GAChCrB;IAEJ;AACF"}