@rtpaulino/entity 0.23.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/entity-utils.d.ts +14 -0
- package/dist/lib/entity-utils.d.ts.map +1 -1
- package/dist/lib/entity-utils.js +48 -25
- package/dist/lib/entity-utils.js.map +1 -1
- package/dist/lib/entity.d.ts +6 -0
- package/dist/lib/entity.d.ts.map +1 -1
- package/dist/lib/entity.js +2 -0
- package/dist/lib/entity.js.map +1 -1
- package/dist/lib/validation-utils.d.ts +6 -6
- package/dist/lib/validation-utils.d.ts.map +1 -1
- package/dist/lib/validation-utils.js +7 -7
- package/dist/lib/validation-utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -88,6 +88,15 @@ export declare class EntityUtils {
|
|
|
88
88
|
* ```
|
|
89
89
|
*/
|
|
90
90
|
static isStringifiable(entityOrClass: unknown): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Gets the "wrapper" property name for entities that act as transparent wrappers.
|
|
93
|
+
* When set, this property name is excluded from error paths during validation.
|
|
94
|
+
*
|
|
95
|
+
* @param entityOrClass - The entity instance or class to check
|
|
96
|
+
* @returns The wrapper property name, or undefined if not a wrapper entity
|
|
97
|
+
* @private
|
|
98
|
+
*/
|
|
99
|
+
private static getWrapperPropertyName;
|
|
91
100
|
static sameEntity(a: object, b: object): boolean;
|
|
92
101
|
static getPropertyKeys(target: object): string[];
|
|
93
102
|
static getPropertyOptions(target: object, propertyKey: string): PropertyOptions | undefined;
|
|
@@ -436,6 +445,11 @@ export declare class EntityUtils {
|
|
|
436
445
|
* @private
|
|
437
446
|
*/
|
|
438
447
|
private static validatePropertyValue;
|
|
448
|
+
/**
|
|
449
|
+
* Checks if an array contains any entity elements
|
|
450
|
+
* @private
|
|
451
|
+
*/
|
|
452
|
+
private static hasEntityElements;
|
|
439
453
|
/**
|
|
440
454
|
* Runs property validators for a given property value
|
|
441
455
|
* @private
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,YAAY,EAGZ,eAAe,EACf,mBAAmB,EACpB,MAAM,YAAY,CAAC;AASpB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/entity-utils.js
CHANGED
|
@@ -4,7 +4,7 @@ import { EntityDI } from './entity-di.js';
|
|
|
4
4
|
import { isEqualWith } from 'lodash-es';
|
|
5
5
|
import { ValidationError } from './validation-error.js';
|
|
6
6
|
import { Problem } from './problem.js';
|
|
7
|
-
import {
|
|
7
|
+
import { prependArrayIndex, prependPropertyPath, createValidationError } from './validation-utils.js';
|
|
8
8
|
import { isPrimitiveConstructor, deserializePrimitive } from './primitive-deserializers.js';
|
|
9
9
|
import { ok } from 'assert';
|
|
10
10
|
import { EntityRegistry } from './entity-registry.js';
|
|
@@ -133,6 +133,19 @@ export class EntityUtils {
|
|
|
133
133
|
const options = this.getEntityOptions(entityOrClass);
|
|
134
134
|
return options.stringifiable === true;
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Gets the "wrapper" property name for entities that act as transparent wrappers.
|
|
138
|
+
* When set, this property name is excluded from error paths during validation.
|
|
139
|
+
*
|
|
140
|
+
* @param entityOrClass - The entity instance or class to check
|
|
141
|
+
* @returns The wrapper property name, or undefined if not a wrapper entity
|
|
142
|
+
* @private
|
|
143
|
+
*/ static getWrapperPropertyName(entityOrClass) {
|
|
144
|
+
if (!this.isEntity(entityOrClass)) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
return this.getEntityOptions(entityOrClass).wrapperProperty;
|
|
148
|
+
}
|
|
136
149
|
static sameEntity(a, b) {
|
|
137
150
|
if (!this.isEntity(a) || !this.isEntity(b)) {
|
|
138
151
|
return false;
|
|
@@ -399,13 +412,10 @@ export class EntityUtils {
|
|
|
399
412
|
* Internal parse implementation with extended options
|
|
400
413
|
* @private
|
|
401
414
|
*/ static async _parseInternal(entityClass, plainObject, options = {}) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
value: plainObject
|
|
405
|
-
};
|
|
406
|
-
} else if (this.isCollectionEntity(entityClass)) {
|
|
415
|
+
const wrapperPropertyName = this.getWrapperPropertyName(entityClass);
|
|
416
|
+
if (wrapperPropertyName) {
|
|
407
417
|
plainObject = {
|
|
408
|
-
|
|
418
|
+
[wrapperPropertyName]: plainObject
|
|
409
419
|
};
|
|
410
420
|
}
|
|
411
421
|
if (plainObject == null) {
|
|
@@ -462,11 +472,16 @@ export class EntityUtils {
|
|
|
462
472
|
});
|
|
463
473
|
} catch (error) {
|
|
464
474
|
if (error instanceof ValidationError) {
|
|
465
|
-
const
|
|
466
|
-
|
|
475
|
+
const isWrapperProperty = wrapperPropertyName === key;
|
|
476
|
+
if (isWrapperProperty) {
|
|
477
|
+
hardProblems.push(...error.problems);
|
|
478
|
+
} else {
|
|
479
|
+
const problems = prependPropertyPath(key, error.problems);
|
|
480
|
+
hardProblems.push(...problems);
|
|
481
|
+
}
|
|
467
482
|
} else if (error instanceof Error) {
|
|
468
483
|
hardProblems.push(new Problem({
|
|
469
|
-
property: key,
|
|
484
|
+
property: wrapperPropertyName === key ? '' : key,
|
|
470
485
|
message: error.message
|
|
471
486
|
}));
|
|
472
487
|
} else {
|
|
@@ -935,24 +950,32 @@ export class EntityUtils {
|
|
|
935
950
|
const validatorProblems = await validator({
|
|
936
951
|
value
|
|
937
952
|
});
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
problems.push(
|
|
941
|
-
property: combinePropertyPaths(propertyPath, problem.property),
|
|
942
|
-
message: problem.message
|
|
943
|
-
}));
|
|
953
|
+
if (validatorProblems.length > 0) {
|
|
954
|
+
const prepended = prependPropertyPath(propertyPath, validatorProblems);
|
|
955
|
+
problems.push(...prepended);
|
|
944
956
|
}
|
|
945
957
|
}
|
|
946
958
|
}
|
|
947
959
|
if (EntityUtils.isEntity(value)) {
|
|
948
960
|
const existingProblems = problemsStorage.get(value);
|
|
949
961
|
const nestedProblems = existingProblems && existingProblems.length > 0 ? existingProblems : await EntityUtils.validate(value);
|
|
950
|
-
|
|
951
|
-
|
|
962
|
+
if (nestedProblems.length > 0) {
|
|
963
|
+
const prepended = prependPropertyPath(propertyPath, nestedProblems);
|
|
964
|
+
problems.push(...prepended);
|
|
965
|
+
}
|
|
952
966
|
}
|
|
953
967
|
return problems;
|
|
954
968
|
}
|
|
955
969
|
/**
|
|
970
|
+
* Checks if an array contains any entity elements
|
|
971
|
+
* @private
|
|
972
|
+
*/ static hasEntityElements(value) {
|
|
973
|
+
if (!Array.isArray(value)) {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
return value.some((element)=>this.isEntity(element));
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
956
979
|
* Runs property validators for a given property value
|
|
957
980
|
* @private
|
|
958
981
|
*/ static async runPropertyValidators(key, value, options) {
|
|
@@ -969,15 +992,13 @@ export class EntityUtils {
|
|
|
969
992
|
const validatorProblems = await validator({
|
|
970
993
|
value
|
|
971
994
|
});
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
message: problem.message
|
|
976
|
-
}));
|
|
995
|
+
if (validatorProblems.length > 0) {
|
|
996
|
+
const prepended = prependPropertyPath(key, validatorProblems);
|
|
997
|
+
problems.push(...prepended);
|
|
977
998
|
}
|
|
978
999
|
}
|
|
979
1000
|
const validators = options.validators || [];
|
|
980
|
-
if (validators.length > 0) {
|
|
1001
|
+
if (validators.length > 0 || this.hasEntityElements(value)) {
|
|
981
1002
|
for(let i = 0; i < value.length; i++){
|
|
982
1003
|
const element = value[i];
|
|
983
1004
|
if (element !== null && element !== undefined) {
|
|
@@ -996,12 +1017,14 @@ export class EntityUtils {
|
|
|
996
1017
|
*/ static async validateProperties(dataOrInstance, prototype) {
|
|
997
1018
|
const problems = [];
|
|
998
1019
|
const keys = Object.keys(dataOrInstance);
|
|
1020
|
+
const wrapperProperty = this.getWrapperPropertyName(dataOrInstance);
|
|
999
1021
|
for (const key of keys){
|
|
1000
1022
|
const options = this.getPropertyOptions(prototype, key);
|
|
1001
1023
|
if (options) {
|
|
1002
1024
|
const value = dataOrInstance[key];
|
|
1003
1025
|
if (value != null) {
|
|
1004
|
-
const
|
|
1026
|
+
const propertyPath = wrapperProperty === key ? '' : key;
|
|
1027
|
+
const validationProblems = await this.runPropertyValidators(propertyPath, value, options);
|
|
1005
1028
|
problems.push(...validationProblems);
|
|
1006
1029
|
}
|
|
1007
1030
|
}
|
|
@@ -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 prependPropertyPath,\n prependArrayIndex,\n createValidationError,\n combinePropertyPaths,\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 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 if (this.isStringifiable(entityClass)) {\n plainObject = { value: plainObject };\n } else if (this.isCollectionEntity(entityClass)) {\n plainObject = { collection: 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 problems = prependPropertyPath(key, error);\n hardProblems.push(...problems);\n } else if (error instanceof Error) {\n hardProblems.push(\n new Problem({\n property: 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 // Prepend propertyPath to all problems\n for (const problem of validatorProblems) {\n problems.push(\n new Problem({\n property: combinePropertyPaths(propertyPath, problem.property),\n message: problem.message,\n }),\n );\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 const prependedProblems = prependPropertyPath(\n propertyPath,\n new ValidationError(nestedProblems),\n );\n problems.push(...prependedProblems);\n }\n\n return problems;\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 for (const problem of validatorProblems) {\n problems.push(\n new Problem({\n property: combinePropertyPaths(key, problem.property),\n message: problem.message,\n }),\n );\n }\n }\n\n const validators = options.validators || [];\n if (validators.length > 0) {\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\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 validationProblems = await this.runPropertyValidators(\n key,\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","prependPropertyPath","prependArrayIndex","createValidationError","combinePropertyPaths","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","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","strict","skipDefaults","skipMissing","data","hardProblems","message","isOptional","optional","valueToSet","default","deserializeValue","error","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","problem","existingProblems","nestedProblems","prependedProblems","runPropertyValidators","isPassthrough","valueProblems","arrayValidators","i","element","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';\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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"}
|
package/dist/lib/entity.d.ts
CHANGED
|
@@ -22,6 +22,12 @@ export interface EntityOptions {
|
|
|
22
22
|
* When deserialized from a string, the string is wrapped in { value: "..." }.
|
|
23
23
|
*/
|
|
24
24
|
stringifiable?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The name of the single "wrapper" property for entities that act as transparent wrappers.
|
|
27
|
+
* When set, this property name is excluded from error paths during validation.
|
|
28
|
+
* Used by @CollectionEntity ('collection') and @Stringifiable ('value').
|
|
29
|
+
*/
|
|
30
|
+
wrapperProperty?: string;
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* Decorator that marks a class as an Entity.
|
package/dist/lib/entity.d.ts.map
CHANGED
|
@@ -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;
|
|
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;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,cAAc,CAqBlE;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"}
|
package/dist/lib/entity.js
CHANGED
|
@@ -78,6 +78,7 @@ import { EntityRegistry } from './entity-registry.js';
|
|
|
78
78
|
*/ export function CollectionEntity(options = {}) {
|
|
79
79
|
return Entity({
|
|
80
80
|
collection: true,
|
|
81
|
+
wrapperProperty: 'collection',
|
|
81
82
|
...options
|
|
82
83
|
});
|
|
83
84
|
}
|
|
@@ -123,6 +124,7 @@ import { EntityRegistry } from './entity-registry.js';
|
|
|
123
124
|
*/ export function Stringifiable(options = {}) {
|
|
124
125
|
return Entity({
|
|
125
126
|
stringifiable: true,
|
|
127
|
+
wrapperProperty: 'value',
|
|
126
128
|
...options
|
|
127
129
|
});
|
|
128
130
|
}
|
package/dist/lib/entity.js.map
CHANGED
|
@@ -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\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\n EntityRegistry.register(entityName, target);\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({
|
|
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\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\n EntityRegistry.register(entityName, target);\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","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;AAkCtD;;;;;;;;;;;;;;;;;;;;;;;;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,6CAA6C;QAC7CP,eAAeQ,QAAQ,CAACF,YAAYH;QAEpC,gDAAgD;QAChD,MAAMM,kBAAkB;YAAE,GAAGP,OAAO;YAAEK,MAAMD;QAAW;QAEvDF,QAAQC,cAAc,CACpBP,6BACAW,iBACAN;IAEJ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GACD,OAAO,SAASO,iBACdR,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QACZU,YAAY;QACZC,iBAAiB;QACjB,GAAGV,OAAO;IACZ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GACD,OAAO,SAASW,cACdX,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QAAEa,eAAe;QAAMF,iBAAiB;QAAS,GAAGV,OAAO;IAAC;AAC5E;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASa;IACd,OAAO,CAACZ,QAAgBa;QACtB,IAAI,OAAOA,gBAAgB,UAAU;YACnC;QACF;QAEA,MAAMC,qBACJb,QAAQc,cAAc,CAACnB,+BAA+BI,WAAW,EAAE;QAErE,IAAI,CAACc,mBAAmBE,QAAQ,CAACH,cAAc;YAC7CC,mBAAmBG,IAAI,CAACJ;QAC1B;QAEAZ,QAAQC,cAAc,CACpBN,+BACAkB,oBACAd;IAEJ;AACF"}
|
|
@@ -35,23 +35,23 @@ export declare function combinePropertyPaths(prefix: string, suffix: string): st
|
|
|
35
35
|
*/
|
|
36
36
|
export declare function createValidationError(message: string): ValidationError;
|
|
37
37
|
/**
|
|
38
|
-
* Prepends a property path to all problems
|
|
38
|
+
* Prepends a property path to all problems.
|
|
39
39
|
* Problems with empty property names get the path directly.
|
|
40
40
|
* Problems starting with '[' get the path without a dot separator.
|
|
41
41
|
* Other problems get the path with a dot separator.
|
|
42
42
|
*
|
|
43
43
|
* @param propertyPath - The property path to prepend (e.g., 'user', 'items[0]')
|
|
44
|
-
* @param
|
|
44
|
+
* @param problems - The array of Problems to process
|
|
45
45
|
* @returns Array of Problems with the property path prepended
|
|
46
46
|
*
|
|
47
47
|
* @example
|
|
48
48
|
* ```typescript
|
|
49
|
-
* const
|
|
49
|
+
* const problems = [
|
|
50
50
|
* new Problem({ property: '', message: 'Invalid type' }),
|
|
51
51
|
* new Problem({ property: 'name', message: 'Required' }),
|
|
52
52
|
* new Problem({ property: '[0]', message: 'Invalid element' })
|
|
53
|
-
* ]
|
|
54
|
-
* const
|
|
53
|
+
* ];
|
|
54
|
+
* const result = prependPropertyPath('user', problems);
|
|
55
55
|
* // [
|
|
56
56
|
* // { property: 'user', message: 'Invalid type' },
|
|
57
57
|
* // { property: 'user.name', message: 'Required' },
|
|
@@ -59,7 +59,7 @@ export declare function createValidationError(message: string): ValidationError;
|
|
|
59
59
|
* // ]
|
|
60
60
|
* ```
|
|
61
61
|
*/
|
|
62
|
-
export declare function prependPropertyPath(propertyPath: string,
|
|
62
|
+
export declare function prependPropertyPath(propertyPath: string, problems: Problem[]): Problem[];
|
|
63
63
|
/**
|
|
64
64
|
* Prepends an array index to all problems in a ValidationError.
|
|
65
65
|
* Problems with empty property get `[index]` directly.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation-utils.d.ts","sourceRoot":"","sources":["../../src/lib/validation-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQ3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAOtE;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,
|
|
1
|
+
{"version":3,"file":"validation-utils.d.ts","sourceRoot":"","sources":["../../src/lib/validation-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQ3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAOtE;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,OAAO,EAAE,GAClB,OAAO,EAAE,CAOX;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,eAAe,GACrB,OAAO,EAAE,CAOX"}
|
|
@@ -48,31 +48,31 @@ import { ValidationError } from './validation-error.js';
|
|
|
48
48
|
]);
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
|
-
* Prepends a property path to all problems
|
|
51
|
+
* Prepends a property path to all problems.
|
|
52
52
|
* Problems with empty property names get the path directly.
|
|
53
53
|
* Problems starting with '[' get the path without a dot separator.
|
|
54
54
|
* Other problems get the path with a dot separator.
|
|
55
55
|
*
|
|
56
56
|
* @param propertyPath - The property path to prepend (e.g., 'user', 'items[0]')
|
|
57
|
-
* @param
|
|
57
|
+
* @param problems - The array of Problems to process
|
|
58
58
|
* @returns Array of Problems with the property path prepended
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
61
61
|
* ```typescript
|
|
62
|
-
* const
|
|
62
|
+
* const problems = [
|
|
63
63
|
* new Problem({ property: '', message: 'Invalid type' }),
|
|
64
64
|
* new Problem({ property: 'name', message: 'Required' }),
|
|
65
65
|
* new Problem({ property: '[0]', message: 'Invalid element' })
|
|
66
|
-
* ]
|
|
67
|
-
* const
|
|
66
|
+
* ];
|
|
67
|
+
* const result = prependPropertyPath('user', problems);
|
|
68
68
|
* // [
|
|
69
69
|
* // { property: 'user', message: 'Invalid type' },
|
|
70
70
|
* // { property: 'user.name', message: 'Required' },
|
|
71
71
|
* // { property: 'user[0]', message: 'Invalid element' }
|
|
72
72
|
* // ]
|
|
73
73
|
* ```
|
|
74
|
-
*/ export function prependPropertyPath(propertyPath,
|
|
75
|
-
return
|
|
74
|
+
*/ export function prependPropertyPath(propertyPath, problems) {
|
|
75
|
+
return problems.map((problem)=>{
|
|
76
76
|
return new Problem({
|
|
77
77
|
property: combinePropertyPaths(propertyPath, problem.property),
|
|
78
78
|
message: problem.message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/validation-utils.ts"],"sourcesContent":["import { Problem } from './problem.js';\nimport { ValidationError } from './validation-error.js';\n\n/**\n * Combines a property path prefix with a suffix according to deterministic rules:\n * - If suffix is empty, returns prefix\n * - If suffix starts with '[', concatenates without separator (e.g., 'items' + '[0]' → 'items[0]')\n * - Otherwise, concatenates with dot separator (e.g., 'user' + 'name' → 'user.name')\n *\n * @param prefix - The property path to prepend\n * @param suffix - The property path to append\n * @returns The combined property path\n *\n * @example\n * ```typescript\n * combinePropertyPaths('user', '') // 'user'\n * combinePropertyPaths('user', 'name') // 'user.name'\n * combinePropertyPaths('items', '[0]') // 'items[0]'\n * combinePropertyPaths('[0]', 'name') // '[0].name'\n * ```\n */\nexport function combinePropertyPaths(prefix: string, suffix: string): string {\n if (suffix === '') {\n return prefix;\n }\n if (suffix.startsWith('[')) {\n return `${prefix}${suffix}`;\n }\n return `${prefix}.${suffix}`;\n}\n\n/**\n * Creates a ValidationError with a single problem that has an empty property.\n * This is used when reporting errors at the current scope level, where the caller\n * will prepend the appropriate context path.\n *\n * @param message - The error message\n * @returns A ValidationError with a single problem with empty property\n *\n * @example\n * ```typescript\n * throw createValidationError('Expects a string but received number');\n * // Creates: ValidationError([{ property: '', message: 'Expects a string but received number' }])\n * ```\n */\nexport function createValidationError(message: string): ValidationError {\n return new ValidationError([\n new Problem({\n property: '',\n message,\n }),\n ]);\n}\n\n/**\n * Prepends a property path to all problems
|
|
1
|
+
{"version":3,"sources":["../../src/lib/validation-utils.ts"],"sourcesContent":["import { Problem } from './problem.js';\nimport { ValidationError } from './validation-error.js';\n\n/**\n * Combines a property path prefix with a suffix according to deterministic rules:\n * - If suffix is empty, returns prefix\n * - If suffix starts with '[', concatenates without separator (e.g., 'items' + '[0]' → 'items[0]')\n * - Otherwise, concatenates with dot separator (e.g., 'user' + 'name' → 'user.name')\n *\n * @param prefix - The property path to prepend\n * @param suffix - The property path to append\n * @returns The combined property path\n *\n * @example\n * ```typescript\n * combinePropertyPaths('user', '') // 'user'\n * combinePropertyPaths('user', 'name') // 'user.name'\n * combinePropertyPaths('items', '[0]') // 'items[0]'\n * combinePropertyPaths('[0]', 'name') // '[0].name'\n * ```\n */\nexport function combinePropertyPaths(prefix: string, suffix: string): string {\n if (suffix === '') {\n return prefix;\n }\n if (suffix.startsWith('[')) {\n return `${prefix}${suffix}`;\n }\n return `${prefix}.${suffix}`;\n}\n\n/**\n * Creates a ValidationError with a single problem that has an empty property.\n * This is used when reporting errors at the current scope level, where the caller\n * will prepend the appropriate context path.\n *\n * @param message - The error message\n * @returns A ValidationError with a single problem with empty property\n *\n * @example\n * ```typescript\n * throw createValidationError('Expects a string but received number');\n * // Creates: ValidationError([{ property: '', message: 'Expects a string but received number' }])\n * ```\n */\nexport function createValidationError(message: string): ValidationError {\n return new ValidationError([\n new Problem({\n property: '',\n message,\n }),\n ]);\n}\n\n/**\n * Prepends a property path to all problems.\n * Problems with empty property names get the path directly.\n * Problems starting with '[' get the path without a dot separator.\n * Other problems get the path with a dot separator.\n *\n * @param propertyPath - The property path to prepend (e.g., 'user', 'items[0]')\n * @param problems - The array of Problems to process\n * @returns Array of Problems with the property path prepended\n *\n * @example\n * ```typescript\n * const problems = [\n * new Problem({ property: '', message: 'Invalid type' }),\n * new Problem({ property: 'name', message: 'Required' }),\n * new Problem({ property: '[0]', message: 'Invalid element' })\n * ];\n * const result = prependPropertyPath('user', problems);\n * // [\n * // { property: 'user', message: 'Invalid type' },\n * // { property: 'user.name', message: 'Required' },\n * // { property: 'user[0]', message: 'Invalid element' }\n * // ]\n * ```\n */\nexport function prependPropertyPath(\n propertyPath: string,\n problems: Problem[],\n): Problem[] {\n return problems.map((problem) => {\n return new Problem({\n property: combinePropertyPaths(propertyPath, problem.property),\n message: problem.message,\n });\n });\n}\n\n/**\n * Prepends an array index to all problems in a ValidationError.\n * Problems with empty property get `[index]` directly.\n * Problems with non-empty property get `[index].property` format.\n *\n * @param index - The array index to prepend\n * @param error - The ValidationError containing problems to process\n * @returns Array of Problems with the array index prepended\n *\n * @example\n * ```typescript\n * const error = new ValidationError([\n * new Problem({ property: '', message: 'Invalid type' }),\n * new Problem({ property: 'name', message: 'Required' })\n * ]);\n * const problems = prependArrayIndex(0, error);\n * // [\n * // { property: '[0]', message: 'Invalid type' },\n * // { property: '[0].name', message: 'Required' }\n * // ]\n * ```\n */\nexport function prependArrayIndex(\n index: number,\n error: ValidationError,\n): Problem[] {\n return error.problems.map((problem) => {\n return new Problem({\n property: combinePropertyPaths(`[${index}]`, problem.property),\n message: problem.message,\n });\n });\n}\n"],"names":["Problem","ValidationError","combinePropertyPaths","prefix","suffix","startsWith","createValidationError","message","property","prependPropertyPath","propertyPath","problems","map","problem","prependArrayIndex","index","error"],"mappings":"AAAA,SAASA,OAAO,QAAQ,eAAe;AACvC,SAASC,eAAe,QAAQ,wBAAwB;AAExD;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,SAASC,qBAAqBC,MAAc,EAAEC,MAAc;IACjE,IAAIA,WAAW,IAAI;QACjB,OAAOD;IACT;IACA,IAAIC,OAAOC,UAAU,CAAC,MAAM;QAC1B,OAAO,GAAGF,SAASC,QAAQ;IAC7B;IACA,OAAO,GAAGD,OAAO,CAAC,EAAEC,QAAQ;AAC9B;AAEA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASE,sBAAsBC,OAAe;IACnD,OAAO,IAAIN,gBAAgB;QACzB,IAAID,QAAQ;YACVQ,UAAU;YACVD;QACF;KACD;AACH;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASE,oBACdC,YAAoB,EACpBC,QAAmB;IAEnB,OAAOA,SAASC,GAAG,CAAC,CAACC;QACnB,OAAO,IAAIb,QAAQ;YACjBQ,UAAUN,qBAAqBQ,cAAcG,QAAQL,QAAQ;YAC7DD,SAASM,QAAQN,OAAO;QAC1B;IACF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,SAASO,kBACdC,KAAa,EACbC,KAAsB;IAEtB,OAAOA,MAAML,QAAQ,CAACC,GAAG,CAAC,CAACC;QACzB,OAAO,IAAIb,QAAQ;YACjBQ,UAAUN,qBAAqB,CAAC,CAAC,EAAEa,MAAM,CAAC,CAAC,EAAEF,QAAQL,QAAQ;YAC7DD,SAASM,QAAQN,OAAO;QAC1B;IACF;AACF"}
|