@rtpaulino/entity 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -4,7 +4,6 @@ export * from './lib/entity-utils.js';
4
4
  export * from './lib/entity-di.js';
5
5
  export * from './lib/types.js';
6
6
  export * from './lib/property.js';
7
- export * from './lib/collection-property.js';
8
7
  export * from './lib/validation-error.js';
9
8
  export * from './lib/problem.js';
10
9
  export * from './lib/validation-utils.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -4,7 +4,6 @@ export * from './lib/entity-utils.js';
4
4
  export * from './lib/entity-di.js';
5
5
  export * from './lib/types.js';
6
6
  export * from './lib/property.js';
7
- export * from './lib/collection-property.js';
8
7
  export * from './lib/validation-error.js';
9
8
  export * from './lib/problem.js';
10
9
  export * from './lib/validation-utils.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import 'reflect-metadata';\n\nexport * from './lib/entity.js';\nexport * from './lib/entity-utils.js';\nexport * from './lib/entity-di.js';\nexport * from './lib/types.js';\nexport * from './lib/property.js';\nexport * from './lib/collection-property.js';\nexport * from './lib/validation-error.js';\nexport * from './lib/problem.js';\nexport * from './lib/validation-utils.js';\nexport * from './lib/primitive-deserializers.js';\nexport * from './lib/validators.js';\nexport * from './lib/zod-property.js';\nexport * from './lib/injected-property.js';\n"],"names":[],"mappings":"AAAA,OAAO,mBAAmB;AAE1B,cAAc,kBAAkB;AAChC,cAAc,wBAAwB;AACtC,cAAc,qBAAqB;AACnC,cAAc,iBAAiB;AAC/B,cAAc,oBAAoB;AAClC,cAAc,+BAA+B;AAC7C,cAAc,4BAA4B;AAC1C,cAAc,mBAAmB;AACjC,cAAc,4BAA4B;AAC1C,cAAc,mCAAmC;AACjD,cAAc,sBAAsB;AACpC,cAAc,wBAAwB;AACtC,cAAc,6BAA6B"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import 'reflect-metadata';\n\nexport * from './lib/entity.js';\nexport * from './lib/entity-utils.js';\nexport * from './lib/entity-di.js';\nexport * from './lib/types.js';\nexport * from './lib/property.js';\nexport * from './lib/validation-error.js';\nexport * from './lib/problem.js';\nexport * from './lib/validation-utils.js';\nexport * from './lib/primitive-deserializers.js';\nexport * from './lib/validators.js';\nexport * from './lib/zod-property.js';\nexport * from './lib/injected-property.js';\n"],"names":[],"mappings":"AAAA,OAAO,mBAAmB;AAE1B,cAAc,kBAAkB;AAChC,cAAc,wBAAwB;AACtC,cAAc,qBAAqB;AACnC,cAAc,iBAAiB;AAC/B,cAAc,oBAAoB;AAClC,cAAc,4BAA4B;AAC1C,cAAc,mBAAmB;AACjC,cAAc,4BAA4B;AAC1C,cAAc,mCAAmC;AACjD,cAAc,sBAAsB;AACpC,cAAc,wBAAwB;AACtC,cAAc,6BAA6B"}
@@ -22,6 +22,34 @@ export declare class EntityUtils {
22
22
  * ```
23
23
  */
24
24
  static isEntity(obj: unknown): obj is object;
25
+ /**
26
+ * Gets the entity options for a given constructor
27
+ *
28
+ * @param entityOrClass - The entity class constructor or instance
29
+ * @returns EntityOptions object (empty object if no options are defined)
30
+ * @private
31
+ */
32
+ private static getEntityOptions;
33
+ /**
34
+ * Checks if a given entity is marked as a collection entity
35
+ *
36
+ * @param entityOrClass - The entity instance or class to check
37
+ * @returns true if the entity is a collection entity, false otherwise
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * @CollectionEntity()
42
+ * class Tags {
43
+ * @ArrayProperty(() => String)
44
+ * collection: string[];
45
+ * }
46
+ *
47
+ * const tags = new Tags({ collection: ['a', 'b'] });
48
+ * console.log(EntityUtils.isCollectionEntity(tags)); // true
49
+ * console.log(EntityUtils.isCollectionEntity(Tags)); // true
50
+ * ```
51
+ */
52
+ static isCollectionEntity(entityOrClass: unknown): boolean;
25
53
  static sameEntity(a: object, b: object): boolean;
26
54
  static getPropertyKeys(target: object): string[];
27
55
  static getPropertyOptions(target: object, propertyKey: string): PropertyOptions | undefined;
@@ -36,7 +64,7 @@ export declare class EntityUtils {
36
64
  * Serializes an entity to a plain object, converting only properties decorated with @Property()
37
65
  *
38
66
  * @param entity - The entity instance to serialize
39
- * @returns A plain object containing only the serialized decorated properties
67
+ * @returns A plain object containing only the serialized decorated properties, or an array for collection entities
40
68
  *
41
69
  * @remarks
42
70
  * Serialization rules:
@@ -49,6 +77,7 @@ export declare class EntityUtils {
49
77
  * - undefined values are excluded from the output
50
78
  * - null values are included in the output
51
79
  * - Circular references are not supported (will cause stack overflow)
80
+ * - Collection entities (@CollectionEntity) are unwrapped to just their array
52
81
  *
53
82
  * @example
54
83
  * ```typescript
@@ -80,9 +109,19 @@ export declare class EntityUtils {
80
109
  * // address: { street: '123 Main St', city: 'Boston' },
81
110
  * // createdAt: '2024-01-01T00:00:00.000Z'
82
111
  * // }
112
+ *
113
+ * @CollectionEntity()
114
+ * class Tags {
115
+ * @ArrayProperty(() => String)
116
+ * collection: string[];
117
+ * }
118
+ *
119
+ * const tags = new Tags({ collection: ['a', 'b'] });
120
+ * const json = EntityUtils.toJSON(tags);
121
+ * // ['a', 'b'] - unwrapped to array
83
122
  * ```
84
123
  */
85
- static toJSON<T extends object>(entity: T): Record<string, unknown>;
124
+ static toJSON<T extends object>(entity: T): unknown;
86
125
  /**
87
126
  * Serializes a single value according to the toJSON rules
88
127
  * @private
@@ -132,7 +171,7 @@ export declare class EntityUtils {
132
171
  * const userStrict = await EntityUtils.parse(User, json, { strict: true });
133
172
  * ```
134
173
  */
135
- static parse<T extends object>(entityClass: new (data: any) => T, plainObject: unknown, parseOptions?: ParseOptions, knownProblems?: Problem[]): Promise<T>;
174
+ static parse<T extends object>(entityClass: new (data: any) => T, plainObject: unknown, parseOptions?: ParseOptions): Promise<T>;
136
175
  /**
137
176
  * Safely deserializes a plain object to an entity instance without throwing errors
138
177
  *
@@ -292,7 +331,7 @@ export declare class EntityUtils {
292
331
  * console.log(problems); // [Problem, Problem, ...]
293
332
  * ```
294
333
  */
295
- static validate<T extends object>(instance: T, knownProblems?: Problem[]): Promise<Problem[]>;
334
+ static validate<T extends object>(instance: T): Promise<Problem[]>;
296
335
  /**
297
336
  * Gets the validation problems for an entity instance
298
337
  *
@@ -1 +1 @@
1
- {"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */ import { ENTITY_METADATA_KEY, ENTITY_VALIDATOR_METADATA_KEY, PROPERTY_METADATA_KEY, PROPERTY_OPTIONS_METADATA_KEY } from './types.js';
1
+ /* eslint-disable @typescript-eslint/no-unsafe-function-type */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { ENTITY_METADATA_KEY, ENTITY_OPTIONS_METADATA_KEY, ENTITY_VALIDATOR_METADATA_KEY, PROPERTY_METADATA_KEY, PROPERTY_OPTIONS_METADATA_KEY } from './types.js';
2
2
  import { getInjectedPropertyNames, getInjectedPropertyOptions } from './injected-property.js';
3
3
  import { EntityDI } from './entity-di.js';
4
4
  import { isEqualWith } from 'lodash-es';
@@ -48,6 +48,42 @@ export class EntityUtils {
48
48
  const constructor = Object.getPrototypeOf(obj).constructor;
49
49
  return Reflect.hasMetadata(ENTITY_METADATA_KEY, constructor);
50
50
  }
51
+ /**
52
+ * Gets the entity options for a given constructor
53
+ *
54
+ * @param entityOrClass - The entity class constructor or instance
55
+ * @returns EntityOptions object (empty object if no options are defined)
56
+ * @private
57
+ */ static getEntityOptions(entityOrClass) {
58
+ const constructor = typeof entityOrClass === 'function' ? entityOrClass : Object.getPrototypeOf(entityOrClass).constructor;
59
+ const options = Reflect.getMetadata(ENTITY_OPTIONS_METADATA_KEY, constructor);
60
+ return options ?? {};
61
+ }
62
+ /**
63
+ * Checks if a given entity is marked as a collection entity
64
+ *
65
+ * @param entityOrClass - The entity instance or class to check
66
+ * @returns true if the entity is a collection entity, false otherwise
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * @CollectionEntity()
71
+ * class Tags {
72
+ * @ArrayProperty(() => String)
73
+ * collection: string[];
74
+ * }
75
+ *
76
+ * const tags = new Tags({ collection: ['a', 'b'] });
77
+ * console.log(EntityUtils.isCollectionEntity(tags)); // true
78
+ * console.log(EntityUtils.isCollectionEntity(Tags)); // true
79
+ * ```
80
+ */ static isCollectionEntity(entityOrClass) {
81
+ if (!this.isEntity(entityOrClass)) {
82
+ return false;
83
+ }
84
+ const options = this.getEntityOptions(entityOrClass);
85
+ return options.collection === true;
86
+ }
51
87
  static sameEntity(a, b) {
52
88
  if (!this.isEntity(a) || !this.isEntity(b)) {
53
89
  return false;
@@ -161,7 +197,7 @@ export class EntityUtils {
161
197
  * Serializes an entity to a plain object, converting only properties decorated with @Property()
162
198
  *
163
199
  * @param entity - The entity instance to serialize
164
- * @returns A plain object containing only the serialized decorated properties
200
+ * @returns A plain object containing only the serialized decorated properties, or an array for collection entities
165
201
  *
166
202
  * @remarks
167
203
  * Serialization rules:
@@ -174,6 +210,7 @@ export class EntityUtils {
174
210
  * - undefined values are excluded from the output
175
211
  * - null values are included in the output
176
212
  * - Circular references are not supported (will cause stack overflow)
213
+ * - Collection entities (@CollectionEntity) are unwrapped to just their array
177
214
  *
178
215
  * @example
179
216
  * ```typescript
@@ -205,8 +242,28 @@ export class EntityUtils {
205
242
  * // address: { street: '123 Main St', city: 'Boston' },
206
243
  * // createdAt: '2024-01-01T00:00:00.000Z'
207
244
  * // }
245
+ *
246
+ * @CollectionEntity()
247
+ * class Tags {
248
+ * @ArrayProperty(() => String)
249
+ * collection: string[];
250
+ * }
251
+ *
252
+ * const tags = new Tags({ collection: ['a', 'b'] });
253
+ * const json = EntityUtils.toJSON(tags);
254
+ * // ['a', 'b'] - unwrapped to array
208
255
  * ```
209
256
  */ static toJSON(entity) {
257
+ if (this.isCollectionEntity(entity)) {
258
+ const collectionPropertyOptions = this.getPropertyOptions(entity, 'collection');
259
+ if (!collectionPropertyOptions) {
260
+ throw new Error(`Collection entity 'collection' property is missing metadata`);
261
+ }
262
+ if (!collectionPropertyOptions.array) {
263
+ throw new Error(`Collection entity 'collection' property must be an array`);
264
+ }
265
+ return this.serializeValue(entity.collection, collectionPropertyOptions);
266
+ }
210
267
  const result = {};
211
268
  const keys = this.getPropertyKeys(entity);
212
269
  for (const key of keys){
@@ -234,16 +291,6 @@ export class EntityUtils {
234
291
  if (passthrough) {
235
292
  return value;
236
293
  }
237
- if (options?.collection === true) {
238
- if (!this.isEntity(value)) {
239
- throw new Error(`Expected collection entity but received non-entity value`);
240
- }
241
- const collectionArray = value.collection;
242
- if (!Array.isArray(collectionArray)) {
243
- throw new Error(`Collection entity must have a 'collection' property that is an array`);
244
- }
245
- return collectionArray.map((item)=>this.serializeValue(item));
246
- }
247
294
  if (Array.isArray(value)) {
248
295
  if (options?.serialize) {
249
296
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -311,7 +358,12 @@ export class EntityUtils {
311
358
  * const user = await EntityUtils.parse(User, json);
312
359
  * const userStrict = await EntityUtils.parse(User, json, { strict: true });
313
360
  * ```
314
- */ static async parse(entityClass, plainObject, parseOptions = {}, knownProblems) {
361
+ */ static async parse(entityClass, plainObject, parseOptions = {}) {
362
+ if (this.isCollectionEntity(entityClass)) {
363
+ plainObject = {
364
+ collection: plainObject
365
+ };
366
+ }
315
367
  if (plainObject == null) {
316
368
  throw createValidationError(`Expects an object but received ${typeof plainObject}`);
317
369
  }
@@ -376,7 +428,7 @@ export class EntityUtils {
376
428
  await this.addInjectedDependencies(data, entityClass.prototype);
377
429
  const instance = new entityClass(data);
378
430
  rawInputStorage.set(instance, plainObject);
379
- const problems = await this.validate(instance, knownProblems);
431
+ const problems = await this.validate(instance);
380
432
  if (problems.length > 0 && strict) {
381
433
  throw new ValidationError(problems);
382
434
  }
@@ -567,25 +619,7 @@ export class EntityUtils {
567
619
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
568
620
  const typeConstructor = options.type();
569
621
  const isArray = options.array === true;
570
- const isCollection = options.collection === true;
571
622
  const isSparse = options.sparse === true;
572
- const problems = [];
573
- if (isCollection) {
574
- if (!this.isEntity(typeConstructor)) {
575
- throw createValidationError(`Collection property type must be an @Entity() class`);
576
- }
577
- if (!Array.isArray(value)) {
578
- problems.push(new Problem({
579
- message: `Collection property expects an array but received ${typeof value}`
580
- }));
581
- }
582
- const collectionInstance = await this.parse(typeConstructor, {
583
- collection: Array.isArray(value) ? value : []
584
- }, parseOptions, problems);
585
- // override raw input to be the array only
586
- rawInputStorage.set(collectionInstance, value);
587
- return collectionInstance;
588
- }
589
623
  if (isArray) {
590
624
  if (!Array.isArray(value)) {
591
625
  throw createValidationError(`Expects an array but received ${typeof value}`);
@@ -638,9 +672,6 @@ export class EntityUtils {
638
672
  return deserializePrimitive(value, typeConstructor);
639
673
  }
640
674
  if (this.isEntity(typeConstructor)) {
641
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
642
- throw createValidationError(`Expects an object but received ${typeof value}`);
643
- }
644
675
  return await this.parse(typeConstructor, value, parseOptions);
645
676
  }
646
677
  throw createValidationError(`Has unknown type constructor. Supported types are: String, Number, Boolean, Date, BigInt, and @Entity() classes. Use passthrough: true to explicitly allow unknown types.`);
@@ -742,7 +773,7 @@ export class EntityUtils {
742
773
  * const problems = await EntityUtils.validate(user);
743
774
  * console.log(problems); // [Problem, Problem, ...]
744
775
  * ```
745
- */ static async validate(instance, knownProblems = []) {
776
+ */ static async validate(instance) {
746
777
  if (!this.isEntity(instance)) {
747
778
  throw new Error('Cannot validate non-entity instance');
748
779
  }
@@ -765,12 +796,8 @@ export class EntityUtils {
765
796
  problems.push(...validatorProblems);
766
797
  }
767
798
  }
768
- const allProblems = [
769
- ...knownProblems,
770
- ...problems
771
- ];
772
- EntityUtils.setProblems(instance, allProblems);
773
- return allProblems;
799
+ EntityUtils.setProblems(instance, problems);
800
+ return problems;
774
801
  }
775
802
  /**
776
803
  * Gets the validation problems for an entity instance
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/entity-utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n ENTITY_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 {\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';\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 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\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 *\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 */\n static toJSON<T extends object>(entity: T): Record<string, unknown> {\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 (options?.collection === true) {\n if (!this.isEntity(value)) {\n throw new Error(\n `Expected collection entity but received non-entity value`,\n );\n }\n\n const collectionArray = (value as any).collection;\n if (!Array.isArray(collectionArray)) {\n throw new Error(\n `Collection entity must have a 'collection' property that is an array`,\n );\n }\n\n return collectionArray.map((item) => this.serializeValue(item));\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));\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 return this.toJSON(value);\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 * 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 knownProblems?: Problem[],\n ): Promise<T> {\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 = parseOptions?.strict ?? 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 let valueToSet = value;\n\n if (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 data[key] = await this.deserializeValue(\n value,\n propertyOptions,\n parseOptions,\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 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, knownProblems);\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 * 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 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const typeConstructor = options.type!();\n const isArray = options.array === true;\n const isCollection = options.collection === true;\n const isSparse = options.sparse === true;\n\n const problems: Problem[] = [];\n\n if (isCollection) {\n if (!this.isEntity(typeConstructor)) {\n throw createValidationError(\n `Collection property type must be an @Entity() class`,\n );\n }\n\n if (!Array.isArray(value)) {\n problems.push(\n new Problem({\n message: `Collection property expects an array but received ${typeof value}`,\n }),\n );\n }\n\n const collectionInstance = await this.parse(\n typeConstructor as new (data: any) => object,\n {\n collection: Array.isArray(value) ? value : [],\n },\n parseOptions,\n problems,\n );\n\n // override raw input to be the array only\n rawInputStorage.set(collectionInstance, value);\n\n return collectionInstance;\n }\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 {\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 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 if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw createValidationError(\n `Expects an object but received ${typeof value}`,\n );\n }\n\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 * 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 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>(\n instance: T,\n knownProblems: Problem[] = [],\n ): 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 keys = this.getPropertyKeys(instance);\n for (const key of keys) {\n const options = this.getPropertyOptions(instance, key);\n if (options) {\n const value = (instance 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 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 const allProblems = [...knownProblems, ...problems];\n\n EntityUtils.setProblems(instance, allProblems);\n\n return allProblems;\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_VALIDATOR_METADATA_KEY","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","getInjectedPropertyNames","getInjectedPropertyOptions","EntityDI","isEqualWith","ValidationError","Problem","prependPropertyPath","prependArrayIndex","createValidationError","combinePropertyPaths","isPrimitiveConstructor","deserializePrimitive","ok","problemsStorage","WeakMap","rawInputStorage","EntityUtils","isEntity","obj","Reflect","hasMetadata","Array","isArray","constructor","Object","getPrototypeOf","sameEntity","a","b","getPropertyKeys","target","currentProto","prototype","keys","seen","Set","protoKeys","getOwnMetadata","key","has","add","push","getPropertyOptions","propertyKey","protoOptions","undefined","equals","val1","val2","diff","length","oldEntity","newEntity","Error","diffs","oldValue","newValue","propertyOptions","areEqual","property","changes","reduce","acc","toJSON","entity","result","value","options","serializeValue","passthrough","collection","collectionArray","map","item","serialize","Date","toISOString","toString","parse","entityClass","plainObject","parseOptions","knownProblems","strict","data","hardProblems","message","isOptional","optional","valueToSet","default","deserializeValue","error","problems","addInjectedDependencies","instance","set","validate","safeParse","getProblems","success","update","updates","Constructor","preventUpdates","newInstance","safeUpdate","updatedInstance","typeConstructor","type","array","isCollection","isSparse","sparse","collectionInstance","arrayProblems","index","deserialize","deserializeSingleValue","validatePropertyValue","propertyPath","validators","validator","validatorProblems","problem","existingProblems","get","nestedProblems","prependedProblems","runPropertyValidators","isPassthrough","valueProblems","arrayValidators","i","element","elementPath","elementProblems","injectedPropertyNames","injectedPropertyOptions","propertyName","token","dependency","validationProblems","entityValidators","getEntityValidators","validatorMethod","allProblems","setProblems","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 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';\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 * 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 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.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));\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 return this.toJSON(value);\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 * 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 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 = parseOptions?.strict ?? 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 let valueToSet = value;\n\n if (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 data[key] = await this.deserializeValue(\n value,\n propertyOptions,\n parseOptions,\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 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 * 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 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const typeConstructor = options.type!();\n const isArray = options.array === true;\n const isSparse = options.sparse === 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 {\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 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 * 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 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 keys = this.getPropertyKeys(instance);\n for (const key of keys) {\n const options = this.getPropertyOptions(instance, key);\n if (options) {\n const value = (instance 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 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","problemsStorage","WeakMap","rawInputStorage","EntityUtils","isEntity","obj","Reflect","hasMetadata","Array","isArray","constructor","Object","getPrototypeOf","getEntityOptions","entityOrClass","options","getMetadata","isCollectionEntity","collection","sameEntity","a","b","getPropertyKeys","target","currentProto","prototype","keys","seen","Set","protoKeys","getOwnMetadata","key","has","add","push","getPropertyOptions","propertyKey","protoOptions","undefined","equals","val1","val2","diff","length","oldEntity","newEntity","Error","diffs","oldValue","newValue","propertyOptions","areEqual","property","changes","reduce","acc","toJSON","entity","collectionPropertyOptions","array","serializeValue","result","value","passthrough","serialize","map","item","Date","toISOString","toString","parse","entityClass","plainObject","parseOptions","strict","data","hardProblems","message","isOptional","optional","valueToSet","default","deserializeValue","error","problems","addInjectedDependencies","instance","set","validate","safeParse","getProblems","success","update","updates","Constructor","preventUpdates","newInstance","safeUpdate","updatedInstance","typeConstructor","type","isSparse","sparse","arrayProblems","index","deserialize","deserializeSingleValue","validatePropertyValue","propertyPath","validators","validator","validatorProblems","problem","existingProblems","get","nestedProblems","prependedProblems","runPropertyValidators","isPassthrough","valueProblems","arrayValidators","i","element","elementPath","elementProblems","injectedPropertyNames","injectedPropertyOptions","propertyName","token","dependency","validationProblems","entityValidators","getEntityValidators","validatorMethod","setProblems","delete","getRawInput","setRawInput","rawInput","protoValidators"],"mappings":"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"}
@@ -1,16 +1,72 @@
1
+ /**
2
+ * Options for Entity decorator
3
+ */
4
+ export interface EntityOptions {
5
+ /**
6
+ * Whether this entity represents a collection.
7
+ * Collection entities must have a 'collection' property that is an array.
8
+ * When serialized, collection entities are unwrapped to just their array.
9
+ * When deserialized from an array, the array is wrapped in { collection: [...] }.
10
+ */
11
+ collection?: boolean;
12
+ }
1
13
  /**
2
14
  * Decorator that marks a class as an Entity.
3
15
  * This allows us to identify entity instances later.
4
16
  *
17
+ * @param options - Optional configuration for the entity
18
+ *
5
19
  * @example
6
20
  * ```typescript
7
21
  * @Entity()
8
22
  * class User {
9
23
  * name: string;
10
24
  * }
25
+ *
26
+ * @Entity({ collection: true })
27
+ * class Tags {
28
+ * @ArrayProperty(() => String)
29
+ * collection: string[];
30
+ * }
31
+ * ```
32
+ */
33
+ export declare function Entity(options?: EntityOptions): ClassDecorator;
34
+ /**
35
+ * Decorator that marks a class as a Collection Entity.
36
+ * This is syntax sugar for @Entity({ collection: true }).
37
+ *
38
+ * Collection entities must have a 'collection' property that is an array.
39
+ * When serialized with EntityUtils.toJSON(), they are unwrapped to just the array.
40
+ * When deserialized from an array, the array is wrapped in { collection: [...] }.
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * @CollectionEntity()
45
+ * class Tags {
46
+ * @ArrayProperty(() => String)
47
+ * readonly collection: string[];
48
+ *
49
+ * constructor(data: { collection: string[] }) {
50
+ * this.collection = data.collection;
51
+ * }
52
+ * }
53
+ *
54
+ * @Entity()
55
+ * class Article {
56
+ * @EntityProperty(() => Tags)
57
+ * tags!: Tags;
58
+ * }
59
+ *
60
+ * const article = new Article(...);
61
+ * const json = EntityUtils.toJSON(article);
62
+ * // { tags: ["tag1", "tag2"] } - unwrapped to array
63
+ *
64
+ * // Also works when serializing the collection directly:
65
+ * const tagsJson = EntityUtils.toJSON(tags);
66
+ * // ["tag1", "tag2"] - unwrapped to array
11
67
  * ```
12
68
  */
13
- export declare function Entity(): ClassDecorator;
69
+ export declare function CollectionEntity(): ClassDecorator;
14
70
  /**
15
71
  * Decorator that marks a method as an entity validator.
16
72
  * The method should return an array of Problems.
@@ -1 +1 @@
1
- {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/lib/entity.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,IAAI,cAAc,CAMvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAmBjD"}
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/lib/entity.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,cAAc,CASlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAEjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAmBjD"}
@@ -1,22 +1,72 @@
1
- import { ENTITY_METADATA_KEY, ENTITY_VALIDATOR_METADATA_KEY } from './types.js';
1
+ import { ENTITY_METADATA_KEY, ENTITY_OPTIONS_METADATA_KEY, ENTITY_VALIDATOR_METADATA_KEY } from './types.js';
2
2
  /**
3
3
  * Decorator that marks a class as an Entity.
4
4
  * This allows us to identify entity instances later.
5
5
  *
6
+ * @param options - Optional configuration for the entity
7
+ *
6
8
  * @example
7
9
  * ```typescript
8
10
  * @Entity()
9
11
  * class User {
10
12
  * name: string;
11
13
  * }
14
+ *
15
+ * @Entity({ collection: true })
16
+ * class Tags {
17
+ * @ArrayProperty(() => String)
18
+ * collection: string[];
19
+ * }
12
20
  * ```
13
- */ export function Entity() {
21
+ */ export function Entity(options = {}) {
14
22
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
15
23
  return function(target) {
16
24
  // Store metadata on the class constructor
17
25
  Reflect.defineMetadata(ENTITY_METADATA_KEY, true, target);
26
+ if (options) {
27
+ Reflect.defineMetadata(ENTITY_OPTIONS_METADATA_KEY, options, target);
28
+ }
18
29
  };
19
30
  }
31
+ /**
32
+ * Decorator that marks a class as a Collection Entity.
33
+ * This is syntax sugar for @Entity({ collection: true }).
34
+ *
35
+ * Collection entities must have a 'collection' property that is an array.
36
+ * When serialized with EntityUtils.toJSON(), they are unwrapped to just the array.
37
+ * When deserialized from an array, the array is wrapped in { collection: [...] }.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * @CollectionEntity()
42
+ * class Tags {
43
+ * @ArrayProperty(() => String)
44
+ * readonly collection: string[];
45
+ *
46
+ * constructor(data: { collection: string[] }) {
47
+ * this.collection = data.collection;
48
+ * }
49
+ * }
50
+ *
51
+ * @Entity()
52
+ * class Article {
53
+ * @EntityProperty(() => Tags)
54
+ * tags!: Tags;
55
+ * }
56
+ *
57
+ * const article = new Article(...);
58
+ * const json = EntityUtils.toJSON(article);
59
+ * // { tags: ["tag1", "tag2"] } - unwrapped to array
60
+ *
61
+ * // Also works when serializing the collection directly:
62
+ * const tagsJson = EntityUtils.toJSON(tags);
63
+ * // ["tag1", "tag2"] - unwrapped to array
64
+ * ```
65
+ */ export function CollectionEntity() {
66
+ return Entity({
67
+ collection: true
68
+ });
69
+ }
20
70
  /**
21
71
  * Decorator that marks a method as an entity validator.
22
72
  * The method should return an array of Problems.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/entity.ts"],"sourcesContent":["import { ENTITY_METADATA_KEY, ENTITY_VALIDATOR_METADATA_KEY } from './types.js';\n\n/**\n * Decorator that marks a class as an Entity.\n * This allows us to identify entity instances later.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n * ```\n */\nexport function Entity(): 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}\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_VALIDATOR_METADATA_KEY","Entity","target","Reflect","defineMetadata","EntityValidator","propertyKey","existingValidators","getOwnMetadata","includes","push"],"mappings":"AAAA,SAASA,mBAAmB,EAAEC,6BAA6B,QAAQ,aAAa;AAEhF;;;;;;;;;;;CAWC,GACD,OAAO,SAASC;IACd,sEAAsE;IACtE,OAAO,SAAUC,MAAgB;QAC/B,0CAA0C;QAC1CC,QAAQC,cAAc,CAACL,qBAAqB,MAAMG;IACpD;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASG;IACd,OAAO,CAACH,QAAgBI;QACtB,IAAI,OAAOA,gBAAgB,UAAU;YACnC;QACF;QAEA,MAAMC,qBACJJ,QAAQK,cAAc,CAACR,+BAA+BE,WAAW,EAAE;QAErE,IAAI,CAACK,mBAAmBE,QAAQ,CAACH,cAAc;YAC7CC,mBAAmBG,IAAI,CAACJ;QAC1B;QAEAH,QAAQC,cAAc,CACpBJ,+BACAO,oBACAL;IAEJ;AACF"}
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';\n\n/**\n * Options for Entity decorator\n */\nexport interface EntityOptions {\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\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({ 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 if (options) {\n Reflect.defineMetadata(ENTITY_OPTIONS_METADATA_KEY, options, 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(): ClassDecorator {\n return Entity({ collection: true });\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","Entity","options","target","Reflect","defineMetadata","CollectionEntity","collection","EntityValidator","propertyKey","existingValidators","getOwnMetadata","includes","push"],"mappings":"AAAA,SACEA,mBAAmB,EACnBC,2BAA2B,EAC3BC,6BAA6B,QACxB,aAAa;AAepB;;;;;;;;;;;;;;;;;;;CAmBC,GACD,OAAO,SAASC,OAAOC,UAAyB,CAAC,CAAC;IAChD,sEAAsE;IACtE,OAAO,SAAUC,MAAgB;QAC/B,0CAA0C;QAC1CC,QAAQC,cAAc,CAACP,qBAAqB,MAAMK;QAClD,IAAID,SAAS;YACXE,QAAQC,cAAc,CAACN,6BAA6BG,SAASC;QAC/D;IACF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GACD,OAAO,SAASG;IACd,OAAOL,OAAO;QAAEM,YAAY;IAAK;AACnC;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC;IACd,OAAO,CAACL,QAAgBM;QACtB,IAAI,OAAOA,gBAAgB,UAAU;YACnC;QACF;QAEA,MAAMC,qBACJN,QAAQO,cAAc,CAACX,+BAA+BG,WAAW,EAAE;QAErE,IAAI,CAACO,mBAAmBE,QAAQ,CAACH,cAAc;YAC7CC,mBAAmBG,IAAI,CAACJ;QAC1B;QAEAL,QAAQC,cAAc,CACpBL,+BACAU,oBACAP;IAEJ;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"property.d.ts","sourceRoot":"","sources":["../../src/lib/property.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"property.d.ts","sourceRoot":"","sources":["../../src/lib/property.ts"],"names":[],"mappings":"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"}
@@ -32,9 +32,6 @@ import { enumValidator, intValidator, minLengthValidator, maxLengthValidator, pa
32
32
  if (options.array === true) {
33
33
  throw new Error(`Property '${propertyKey}' has passthrough: true and array: true. Passthrough cannot be combined with array.`);
34
34
  }
35
- if (options.collection === true) {
36
- throw new Error(`Property '${propertyKey}' has passthrough: true and collection: true. Passthrough cannot be combined with collection.`);
37
- }
38
35
  if (options.optional === true) {
39
36
  throw new Error(`Property '${propertyKey}' has passthrough: true and optional: true. Passthrough cannot be combined with optional.`);
40
37
  }
@@ -51,17 +48,6 @@ import { enumValidator, intValidator, minLengthValidator, maxLengthValidator, pa
51
48
  if (options.arrayValidators && options.array !== true) {
52
49
  throw new Error(`Property '${propertyKey}' has arrayValidators defined but array is not true. The arrayValidators option only applies to arrays.`);
53
50
  }
54
- if (options.collection === true) {
55
- if (options.array === true) {
56
- throw new Error(`Property '${propertyKey}' has collection: true and array: true. Collection cannot be combined with array.`);
57
- }
58
- if (options.passthrough === true) {
59
- throw new Error(`Property '${propertyKey}' has collection: true and passthrough: true. Collection cannot be combined with passthrough.`);
60
- }
61
- if (options.serialize !== undefined || options.deserialize !== undefined) {
62
- throw new Error(`Property '${propertyKey}' has collection: true and custom serialize/deserialize functions. Collection cannot be combined with serialize or deserialize.`);
63
- }
64
- }
65
51
  // Validate serialize/deserialize pairing
66
52
  const hasSerialize = options.serialize !== undefined;
67
53
  const hasDeserialize = options.deserialize !== undefined;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/property.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-wrapper-object-types */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { isEqual } from 'lodash-es';\nimport {\n AnyCtor,\n type CtorLike,\n type InstanceOfCtorLike,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n} from './types.js';\nimport {\n enumValidator,\n intValidator,\n minLengthValidator,\n maxLengthValidator,\n patternValidator,\n minValidator,\n maxValidator,\n arrayMinLengthValidator,\n arrayMaxLengthValidator,\n} from './validators.js';\n\n/**\n * Property decorator that marks class properties with metadata.\n * This decorator can be used to identify and track properties within classes.\n *\n * @param options - Configuration for the property (type is required)\n *\n * @example\n * class User {\n * @Property({ type: () => String })\n * name: string;\n *\n * @Property({ type: () => String, equals: (a, b) => a.toLowerCase() === b.toLowerCase() })\n * email: string;\n *\n * @Property({ type: () => Number })\n * age: number;\n * }\n */\nexport function Property<T, C extends CtorLike<T>>(\n options: PropertyOptions<T, C>,\n): PropertyDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n if (typeof propertyKey !== 'string') {\n return;\n }\n\n const existingProperties: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, target) || [];\n\n if (!existingProperties.includes(propertyKey)) {\n existingProperties.push(propertyKey);\n }\n\n Reflect.defineMetadata(PROPERTY_METADATA_KEY, existingProperties, target);\n\n if (options.passthrough === true) {\n if (options.array === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and array: true. Passthrough cannot be combined with array.`,\n );\n }\n if (options.collection === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and collection: true. Passthrough cannot be combined with collection.`,\n );\n }\n if (options.optional === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and optional: true. Passthrough cannot be combined with optional.`,\n );\n }\n if (options.sparse === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and sparse: true. Passthrough cannot be combined with sparse.`,\n );\n }\n if (\n options.serialize !== undefined ||\n options.deserialize !== undefined\n ) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and custom serialize/deserialize functions. Passthrough cannot be combined with serialize or deserialize.`,\n );\n }\n }\n\n if (options.sparse === true && options.array !== true) {\n throw new Error(\n `Property '${propertyKey}' has sparse: true but array is not true. The sparse option only applies to arrays.`,\n );\n }\n\n if (options.arrayValidators && options.array !== true) {\n throw new Error(\n `Property '${propertyKey}' has arrayValidators defined but array is not true. The arrayValidators option only applies to arrays.`,\n );\n }\n\n if (options.collection === true) {\n if (options.array === true) {\n throw new Error(\n `Property '${propertyKey}' has collection: true and array: true. Collection cannot be combined with array.`,\n );\n }\n if (options.passthrough === true) {\n throw new Error(\n `Property '${propertyKey}' has collection: true and passthrough: true. Collection cannot be combined with passthrough.`,\n );\n }\n if (\n options.serialize !== undefined ||\n options.deserialize !== undefined\n ) {\n throw new Error(\n `Property '${propertyKey}' has collection: true and custom serialize/deserialize functions. Collection cannot be combined with serialize or deserialize.`,\n );\n }\n }\n\n // Validate serialize/deserialize pairing\n const hasSerialize = options.serialize !== undefined;\n const hasDeserialize = options.deserialize !== undefined;\n if (hasSerialize !== hasDeserialize) {\n throw new Error(\n `Property '${propertyKey}' must define both serialize and deserialize functions, or neither. Found only ${hasSerialize ? 'serialize' : 'deserialize'}.`,\n );\n }\n\n const existingOptions: Record<\n string,\n PropertyOptions<any, any>\n > = Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, target) || {};\n\n existingOptions[propertyKey] = options;\n\n Reflect.defineMetadata(\n PROPERTY_OPTIONS_METADATA_KEY,\n existingOptions,\n target,\n );\n };\n}\n\n/**\n * Helper decorator for string properties\n * @example\n * class User {\n * @StringProperty()\n * name!: string;\n *\n * @StringProperty({ optional: true })\n * nickname?: string;\n *\n * @StringProperty({ minLength: 3, maxLength: 50 })\n * username!: string;\n *\n * @StringProperty({ pattern: /^[a-z]+$/ })\n * slug!: string;\n * }\n */\nexport function StringProperty(\n options?: Omit<PropertyOptions<string, StringConstructor>, 'type'> & {\n minLength?: number;\n maxLength?: number;\n pattern?: RegExp;\n patternMessage?: string;\n },\n): PropertyDecorator {\n const validators = [...(options?.validators || [])];\n\n if (options?.minLength !== undefined) {\n validators.unshift(minLengthValidator(options.minLength));\n }\n if (options?.maxLength !== undefined) {\n validators.unshift(maxLengthValidator(options.maxLength));\n }\n if (options?.pattern !== undefined) {\n validators.unshift(\n patternValidator(options.pattern, options.patternMessage),\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { minLength, maxLength, pattern, patternMessage, ...restOptions } =\n options || {};\n\n return Property({\n ...restOptions,\n type: () => String,\n validators: validators.length > 0 ? validators : undefined,\n });\n}\n\n/**\n * Helper decorator for enum properties (string enums)\n * Validates that the string value matches one of the enum values\n * @param enumType - The enum object (e.g., MyEnum)\n * @param options - Additional property options\n * @example\n * enum Status {\n * Active = 'active',\n * Inactive = 'inactive'\n * }\n *\n * class User {\n * @EnumProperty(Status)\n * status!: Status;\n *\n * @EnumProperty(Status, { optional: true })\n * previousStatus?: Status;\n * }\n */\nexport function EnumProperty<T extends Record<string, string>>(\n enumType: T,\n options?: Omit<PropertyOptions<string, StringConstructor>, 'type'>,\n): PropertyDecorator {\n const validators = options?.validators\n ? [enumValidator(enumType), ...options.validators]\n : [enumValidator(enumType)];\n\n return Property({ ...options, type: () => String, validators });\n}\n\n/**\n * Helper decorator for number properties\n * @example\n * class User {\n * @NumberProperty()\n * age!: number;\n *\n * @NumberProperty({ optional: true })\n * score?: number;\n *\n * @NumberProperty({ min: 0, max: 100 })\n * percentage!: number;\n * }\n */\nexport function NumberProperty(\n options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'> & {\n min?: number;\n max?: number;\n },\n): PropertyDecorator {\n const validators = [...(options?.validators || [])];\n\n if (options?.min !== undefined) {\n validators.unshift(minValidator(options.min));\n }\n if (options?.max !== undefined) {\n validators.unshift(maxValidator(options.max));\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { min, max, ...restOptions } = options || {};\n\n return Property({\n ...restOptions,\n type: () => Number,\n validators: validators.length > 0 ? validators : undefined,\n });\n}\n\n/**\n * Helper decorator for integer properties\n * Validates that the number is an integer (no decimal places)\n * @example\n * class User {\n * @IntProperty()\n * age!: number;\n *\n * @IntProperty({ optional: true })\n * count?: number;\n * }\n */\nexport function IntProperty(\n options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'>,\n): PropertyDecorator {\n const validators = options?.validators\n ? [intValidator(), ...options.validators]\n : [intValidator()];\n\n return Property({ ...options, type: () => Number, validators });\n}\n\n/**\n * Helper decorator for boolean properties\n * @example\n * class User {\n * @BooleanProperty()\n * active!: boolean;\n *\n * @BooleanProperty({ optional: true })\n * verified?: boolean;\n * }\n */\nexport function BooleanProperty(\n options?: Omit<PropertyOptions<boolean, BooleanConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Boolean });\n}\n\n/**\n * Helper decorator for Date properties\n * @example\n * class User {\n * @DateProperty()\n * createdAt!: Date;\n *\n * @DateProperty({ optional: true })\n * deletedAt?: Date;\n * }\n */\nexport function DateProperty(\n options?: Omit<PropertyOptions<Date, DateConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Date });\n}\n\n/**\n * Helper decorator for BigInt properties\n * @example\n * class User {\n * @BigIntProperty()\n * id!: bigint;\n *\n * @BigIntProperty({ optional: true })\n * balance?: bigint;\n * }\n */\nexport function BigIntProperty(\n options?: Omit<PropertyOptions<bigint, BigIntConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => BigInt });\n}\n\n/**\n * Helper decorator for entity properties\n * @example\n * class User {\n * @EntityProperty(() => Address)\n * address!: Address;\n *\n * @EntityProperty(() => Profile, { optional: true })\n * profile?: Profile;\n * }\n */\nexport function EntityProperty<\n T,\n C extends AnyCtor<T> & { new (data: any): T },\n>(\n type: () => C,\n options?: Omit<PropertyOptions<T, C>, 'type'>,\n): PropertyDecorator {\n return Property<T, C>({ ...options, type });\n}\n\n/**\n * Helper decorator for array properties\n * @example\n * class User {\n * @ArrayProperty(() => String)\n * tags!: string[];\n *\n * @ArrayProperty(() => Phone)\n * phones!: Phone[];\n *\n * @ArrayProperty(() => Number, { optional: true })\n * scores?: number[];\n *\n * @ArrayProperty(() => String, { sparse: true })\n * sparseList!: (string | null)[];\n *\n * @ArrayProperty(() => String, { minLength: 1, maxLength: 10 })\n * limitedList!: string[];\n * }\n */\nexport function ArrayProperty<T, C extends CtorLike<T>>(\n type: () => C,\n options?: Omit<PropertyOptions<T, C>, 'type' | 'array'> & {\n minLength?: number;\n maxLength?: number;\n },\n): PropertyDecorator {\n const arrayValidators = [...(options?.arrayValidators || [])];\n\n if (options?.minLength !== undefined) {\n arrayValidators.unshift(arrayMinLengthValidator(options.minLength));\n }\n if (options?.maxLength !== undefined) {\n arrayValidators.unshift(arrayMaxLengthValidator(options.maxLength));\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { minLength, maxLength, ...restOptions } = options || {};\n\n return Property({\n ...restOptions,\n type,\n array: true,\n arrayValidators: arrayValidators.length > 0 ? arrayValidators : undefined,\n });\n}\n\n/**\n * Helper decorator for passthrough properties that bypass type validation.\n * Use this for generic types like Record<string, unknown>, any, or custom objects.\n * @example\n * class Config {\n * @PassthroughProperty()\n * metadata!: Record<string, unknown>;\n *\n * @PassthroughProperty()\n * customData!: any;\n * }\n */\nexport function PassthroughProperty(): PropertyDecorator {\n // Use a dummy type since type is mandatory but not used with passthrough\n return Property({ type: () => Object, passthrough: true });\n}\n\nexport const StringifiableProperty = <\n T extends { equals?(other: T): boolean; toString(): string },\n C extends CtorLike<T> & { parse(value: string): T },\n>(\n type: () => C,\n data: Omit<\n PropertyOptions<T, C>,\n 'serialize' | 'deserialize' | 'passthrough' | 'type' | 'equals'\n > = {},\n): PropertyDecorator =>\n Property<T, C>({\n ...data,\n type,\n equals: (a, b) => (a.equals ? a.equals(b) : a.toString() === b.toString()),\n serialize: (value) => value.toString(),\n deserialize: (value) => {\n if (typeof value === 'string') {\n return type().parse(value) as InstanceOfCtorLike<C>;\n }\n throw new Error(`Invalid value ${type().name}: ${String(value)}`);\n },\n });\n\nexport const SerializableProperty = <\n T extends { equals?(other: T): boolean; toJSON(): unknown },\n C extends CtorLike<T> & { parse(value: unknown): T },\n>(\n type: () => C,\n data: Omit<\n PropertyOptions<T, C>,\n 'serialize' | 'deserialize' | 'passthrough' | 'type' | 'equals'\n > = {},\n) =>\n Property({\n ...data,\n type,\n equals: (a: T, b: T) =>\n a.equals ? a.equals(b) : isEqual(a.toJSON(), b.toJSON()),\n serialize: (value: T) => value.toJSON(),\n deserialize: (value: unknown) => {\n return type().parse(value) as InstanceOfCtorLike<C>;\n },\n });\n"],"names":["isEqual","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","enumValidator","intValidator","minLengthValidator","maxLengthValidator","patternValidator","minValidator","maxValidator","arrayMinLengthValidator","arrayMaxLengthValidator","Property","options","target","propertyKey","existingProperties","Reflect","getOwnMetadata","includes","push","defineMetadata","passthrough","array","Error","collection","optional","sparse","serialize","undefined","deserialize","arrayValidators","hasSerialize","hasDeserialize","existingOptions","StringProperty","validators","minLength","unshift","maxLength","pattern","patternMessage","restOptions","type","String","length","EnumProperty","enumType","NumberProperty","min","max","Number","IntProperty","BooleanProperty","Boolean","DateProperty","Date","BigIntProperty","BigInt","EntityProperty","ArrayProperty","PassthroughProperty","Object","StringifiableProperty","data","equals","a","b","toString","value","parse","name","SerializableProperty","toJSON"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/lib/property.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-wrapper-object-types */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { isEqual } from 'lodash-es';\nimport {\n AnyCtor,\n type CtorLike,\n type InstanceOfCtorLike,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n} from './types.js';\nimport {\n enumValidator,\n intValidator,\n minLengthValidator,\n maxLengthValidator,\n patternValidator,\n minValidator,\n maxValidator,\n arrayMinLengthValidator,\n arrayMaxLengthValidator,\n} from './validators.js';\n\n/**\n * Property decorator that marks class properties with metadata.\n * This decorator can be used to identify and track properties within classes.\n *\n * @param options - Configuration for the property (type is required)\n *\n * @example\n * class User {\n * @Property({ type: () => String })\n * name: string;\n *\n * @Property({ type: () => String, equals: (a, b) => a.toLowerCase() === b.toLowerCase() })\n * email: string;\n *\n * @Property({ type: () => Number })\n * age: number;\n * }\n */\nexport function Property<T, C extends CtorLike<T>>(\n options: PropertyOptions<T, C>,\n): PropertyDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n if (typeof propertyKey !== 'string') {\n return;\n }\n\n const existingProperties: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, target) || [];\n\n if (!existingProperties.includes(propertyKey)) {\n existingProperties.push(propertyKey);\n }\n\n Reflect.defineMetadata(PROPERTY_METADATA_KEY, existingProperties, target);\n\n if (options.passthrough === true) {\n if (options.array === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and array: true. Passthrough cannot be combined with array.`,\n );\n }\n if (options.optional === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and optional: true. Passthrough cannot be combined with optional.`,\n );\n }\n if (options.sparse === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and sparse: true. Passthrough cannot be combined with sparse.`,\n );\n }\n if (\n options.serialize !== undefined ||\n options.deserialize !== undefined\n ) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and custom serialize/deserialize functions. Passthrough cannot be combined with serialize or deserialize.`,\n );\n }\n }\n\n if (options.sparse === true && options.array !== true) {\n throw new Error(\n `Property '${propertyKey}' has sparse: true but array is not true. The sparse option only applies to arrays.`,\n );\n }\n\n if (options.arrayValidators && options.array !== true) {\n throw new Error(\n `Property '${propertyKey}' has arrayValidators defined but array is not true. The arrayValidators option only applies to arrays.`,\n );\n }\n\n // Validate serialize/deserialize pairing\n const hasSerialize = options.serialize !== undefined;\n const hasDeserialize = options.deserialize !== undefined;\n if (hasSerialize !== hasDeserialize) {\n throw new Error(\n `Property '${propertyKey}' must define both serialize and deserialize functions, or neither. Found only ${hasSerialize ? 'serialize' : 'deserialize'}.`,\n );\n }\n\n const existingOptions: Record<\n string,\n PropertyOptions<any, any>\n > = Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, target) || {};\n\n existingOptions[propertyKey] = options;\n\n Reflect.defineMetadata(\n PROPERTY_OPTIONS_METADATA_KEY,\n existingOptions,\n target,\n );\n };\n}\n\n/**\n * Helper decorator for string properties\n * @example\n * class User {\n * @StringProperty()\n * name!: string;\n *\n * @StringProperty({ optional: true })\n * nickname?: string;\n *\n * @StringProperty({ minLength: 3, maxLength: 50 })\n * username!: string;\n *\n * @StringProperty({ pattern: /^[a-z]+$/ })\n * slug!: string;\n * }\n */\nexport function StringProperty(\n options?: Omit<PropertyOptions<string, StringConstructor>, 'type'> & {\n minLength?: number;\n maxLength?: number;\n pattern?: RegExp;\n patternMessage?: string;\n },\n): PropertyDecorator {\n const validators = [...(options?.validators || [])];\n\n if (options?.minLength !== undefined) {\n validators.unshift(minLengthValidator(options.minLength));\n }\n if (options?.maxLength !== undefined) {\n validators.unshift(maxLengthValidator(options.maxLength));\n }\n if (options?.pattern !== undefined) {\n validators.unshift(\n patternValidator(options.pattern, options.patternMessage),\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { minLength, maxLength, pattern, patternMessage, ...restOptions } =\n options || {};\n\n return Property({\n ...restOptions,\n type: () => String,\n validators: validators.length > 0 ? validators : undefined,\n });\n}\n\n/**\n * Helper decorator for enum properties (string enums)\n * Validates that the string value matches one of the enum values\n * @param enumType - The enum object (e.g., MyEnum)\n * @param options - Additional property options\n * @example\n * enum Status {\n * Active = 'active',\n * Inactive = 'inactive'\n * }\n *\n * class User {\n * @EnumProperty(Status)\n * status!: Status;\n *\n * @EnumProperty(Status, { optional: true })\n * previousStatus?: Status;\n * }\n */\nexport function EnumProperty<T extends Record<string, string>>(\n enumType: T,\n options?: Omit<PropertyOptions<string, StringConstructor>, 'type'>,\n): PropertyDecorator {\n const validators = options?.validators\n ? [enumValidator(enumType), ...options.validators]\n : [enumValidator(enumType)];\n\n return Property({ ...options, type: () => String, validators });\n}\n\n/**\n * Helper decorator for number properties\n * @example\n * class User {\n * @NumberProperty()\n * age!: number;\n *\n * @NumberProperty({ optional: true })\n * score?: number;\n *\n * @NumberProperty({ min: 0, max: 100 })\n * percentage!: number;\n * }\n */\nexport function NumberProperty(\n options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'> & {\n min?: number;\n max?: number;\n },\n): PropertyDecorator {\n const validators = [...(options?.validators || [])];\n\n if (options?.min !== undefined) {\n validators.unshift(minValidator(options.min));\n }\n if (options?.max !== undefined) {\n validators.unshift(maxValidator(options.max));\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { min, max, ...restOptions } = options || {};\n\n return Property({\n ...restOptions,\n type: () => Number,\n validators: validators.length > 0 ? validators : undefined,\n });\n}\n\n/**\n * Helper decorator for integer properties\n * Validates that the number is an integer (no decimal places)\n * @example\n * class User {\n * @IntProperty()\n * age!: number;\n *\n * @IntProperty({ optional: true })\n * count?: number;\n * }\n */\nexport function IntProperty(\n options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'>,\n): PropertyDecorator {\n const validators = options?.validators\n ? [intValidator(), ...options.validators]\n : [intValidator()];\n\n return Property({ ...options, type: () => Number, validators });\n}\n\n/**\n * Helper decorator for boolean properties\n * @example\n * class User {\n * @BooleanProperty()\n * active!: boolean;\n *\n * @BooleanProperty({ optional: true })\n * verified?: boolean;\n * }\n */\nexport function BooleanProperty(\n options?: Omit<PropertyOptions<boolean, BooleanConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Boolean });\n}\n\n/**\n * Helper decorator for Date properties\n * @example\n * class User {\n * @DateProperty()\n * createdAt!: Date;\n *\n * @DateProperty({ optional: true })\n * deletedAt?: Date;\n * }\n */\nexport function DateProperty(\n options?: Omit<PropertyOptions<Date, DateConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Date });\n}\n\n/**\n * Helper decorator for BigInt properties\n * @example\n * class User {\n * @BigIntProperty()\n * id!: bigint;\n *\n * @BigIntProperty({ optional: true })\n * balance?: bigint;\n * }\n */\nexport function BigIntProperty(\n options?: Omit<PropertyOptions<bigint, BigIntConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => BigInt });\n}\n\n/**\n * Helper decorator for entity properties\n * @example\n * class User {\n * @EntityProperty(() => Address)\n * address!: Address;\n *\n * @EntityProperty(() => Profile, { optional: true })\n * profile?: Profile;\n * }\n */\nexport function EntityProperty<\n T,\n C extends AnyCtor<T> & { new (data: any): T },\n>(\n type: () => C,\n options?: Omit<PropertyOptions<T, C>, 'type'>,\n): PropertyDecorator {\n return Property<T, C>({ ...options, type });\n}\n\n/**\n * Helper decorator for array properties\n * @example\n * class User {\n * @ArrayProperty(() => String)\n * tags!: string[];\n *\n * @ArrayProperty(() => Phone)\n * phones!: Phone[];\n *\n * @ArrayProperty(() => Number, { optional: true })\n * scores?: number[];\n *\n * @ArrayProperty(() => String, { sparse: true })\n * sparseList!: (string | null)[];\n *\n * @ArrayProperty(() => String, { minLength: 1, maxLength: 10 })\n * limitedList!: string[];\n * }\n */\nexport function ArrayProperty<T, C extends CtorLike<T>>(\n type: () => C,\n options?: Omit<PropertyOptions<T, C>, 'type' | 'array'> & {\n minLength?: number;\n maxLength?: number;\n },\n): PropertyDecorator {\n const arrayValidators = [...(options?.arrayValidators || [])];\n\n if (options?.minLength !== undefined) {\n arrayValidators.unshift(arrayMinLengthValidator(options.minLength));\n }\n if (options?.maxLength !== undefined) {\n arrayValidators.unshift(arrayMaxLengthValidator(options.maxLength));\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { minLength, maxLength, ...restOptions } = options || {};\n\n return Property({\n ...restOptions,\n type,\n array: true,\n arrayValidators: arrayValidators.length > 0 ? arrayValidators : undefined,\n });\n}\n\n/**\n * Helper decorator for passthrough properties that bypass type validation.\n * Use this for generic types like Record<string, unknown>, any, or custom objects.\n * @example\n * class Config {\n * @PassthroughProperty()\n * metadata!: Record<string, unknown>;\n *\n * @PassthroughProperty()\n * customData!: any;\n * }\n */\nexport function PassthroughProperty(): PropertyDecorator {\n // Use a dummy type since type is mandatory but not used with passthrough\n return Property({ type: () => Object, passthrough: true });\n}\n\nexport const StringifiableProperty = <\n T extends { equals?(other: T): boolean; toString(): string },\n C extends CtorLike<T> & { parse(value: string): T },\n>(\n type: () => C,\n data: Omit<\n PropertyOptions<T, C>,\n 'serialize' | 'deserialize' | 'passthrough' | 'type' | 'equals'\n > = {},\n): PropertyDecorator =>\n Property<T, C>({\n ...data,\n type,\n equals: (a, b) => (a.equals ? a.equals(b) : a.toString() === b.toString()),\n serialize: (value) => value.toString(),\n deserialize: (value) => {\n if (typeof value === 'string') {\n return type().parse(value) as InstanceOfCtorLike<C>;\n }\n throw new Error(`Invalid value ${type().name}: ${String(value)}`);\n },\n });\n\nexport const SerializableProperty = <\n T extends { equals?(other: T): boolean; toJSON(): unknown },\n C extends CtorLike<T> & { parse(value: unknown): T },\n>(\n type: () => C,\n data: Omit<\n PropertyOptions<T, C>,\n 'serialize' | 'deserialize' | 'passthrough' | 'type' | 'equals'\n > = {},\n) =>\n Property({\n ...data,\n type,\n equals: (a: T, b: T) =>\n a.equals ? a.equals(b) : isEqual(a.toJSON(), b.toJSON()),\n serialize: (value: T) => value.toJSON(),\n deserialize: (value: unknown) => {\n return type().parse(value) as InstanceOfCtorLike<C>;\n },\n });\n"],"names":["isEqual","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","enumValidator","intValidator","minLengthValidator","maxLengthValidator","patternValidator","minValidator","maxValidator","arrayMinLengthValidator","arrayMaxLengthValidator","Property","options","target","propertyKey","existingProperties","Reflect","getOwnMetadata","includes","push","defineMetadata","passthrough","array","Error","optional","sparse","serialize","undefined","deserialize","arrayValidators","hasSerialize","hasDeserialize","existingOptions","StringProperty","validators","minLength","unshift","maxLength","pattern","patternMessage","restOptions","type","String","length","EnumProperty","enumType","NumberProperty","min","max","Number","IntProperty","BooleanProperty","Boolean","DateProperty","Date","BigIntProperty","BigInt","EntityProperty","ArrayProperty","PassthroughProperty","Object","StringifiableProperty","data","equals","a","b","toString","value","parse","name","SerializableProperty","toJSON"],"mappings":"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"}
@@ -11,6 +11,10 @@ export declare const PROPERTY_OPTIONS_METADATA_KEY: unique symbol;
11
11
  * Metadata key used to store entity information
12
12
  */
13
13
  export declare const ENTITY_METADATA_KEY: unique symbol;
14
+ /**
15
+ * Metadata key used to store entity options
16
+ */
17
+ export declare const ENTITY_OPTIONS_METADATA_KEY: unique symbol;
14
18
  /**
15
19
  * Metadata key used to store entity validator methods
16
20
  */
@@ -64,16 +68,6 @@ export interface PropertyOptions<T = any, C extends CtorLike<T> = AnyCtor<T> | B
64
68
  * tags!: string[];
65
69
  */
66
70
  array?: boolean;
67
- /**
68
- * Whether this property is a collection entity. Defaults to false.
69
- * When true, the property is an entity with a 'collection' array property.
70
- * During serialization, the collection entity is unwrapped to just the array.
71
- * During deserialization, the array is wrapped in a collection entity.
72
- * @example
73
- * @Property({ type: () => MyCollection, collection: true })
74
- * myCollection!: MyCollection;
75
- */
76
- collection?: boolean;
77
71
  /**
78
72
  * Whether this property is optional. Defaults to false.
79
73
  * When true, the property can be undefined or null.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
package/dist/lib/types.js CHANGED
@@ -7,6 +7,9 @@
7
7
  /**
8
8
  * Metadata key used to store entity information
9
9
  */ export const ENTITY_METADATA_KEY = Symbol('entity:metadata');
10
+ /**
11
+ * Metadata key used to store entity options
12
+ */ export const ENTITY_OPTIONS_METADATA_KEY = Symbol('entity:options:metadata');
10
13
  /**
11
14
  * Metadata key used to store entity validator methods
12
15
  */ export const ENTITY_VALIDATOR_METADATA_KEY = Symbol('entity:validator:metadata');
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/types.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-function-type */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-wrapper-object-types */\nimport type { Problem } from './problem.js';\n\n/**\n * Metadata key used to store property information\n */\nexport const PROPERTY_METADATA_KEY = Symbol('property:metadata');\n\n/**\n * Metadata key used to store property options\n */\nexport const PROPERTY_OPTIONS_METADATA_KEY = Symbol(\n 'property:options:metadata',\n);\n\n/**\n * Metadata key used to store entity information\n */\nexport const ENTITY_METADATA_KEY = Symbol('entity:metadata');\n\n/**\n * Metadata key used to store entity validator methods\n */\nexport const ENTITY_VALIDATOR_METADATA_KEY = Symbol(\n 'entity:validator:metadata',\n);\n\n/**\n * Metadata key used to store injected property information\n */\nexport const INJECTED_PROPERTY_METADATA_KEY = Symbol(\n 'injected-property:metadata',\n);\n\n/**\n * Metadata key used to store injected property options (tokens)\n */\nexport const INJECTED_PROPERTY_OPTIONS_METADATA_KEY = Symbol(\n 'injected-property:options:metadata',\n);\n\nexport type AnyCtor<T = any> = Function & { prototype: T };\n\nexport type BuiltinCtors =\n | StringConstructor\n | NumberConstructor\n | BooleanConstructor\n | BigIntConstructor\n | SymbolConstructor\n | DateConstructor;\n\n/**\n * Type constructors for primitive types that can be deserialized\n * (excludes Symbol which cannot be deserialized from JSON)\n */\nexport type PrimitiveConstructor =\n | StringConstructor\n | NumberConstructor\n | BooleanConstructor\n | BigIntConstructor\n | DateConstructor;\n\nexport type CtorLike<T> = AnyCtor<T> | BuiltinCtors;\n\nexport type InstanceOfCtorLike<C> = C extends StringConstructor\n ? string\n : C extends NumberConstructor\n ? number\n : C extends BooleanConstructor\n ? boolean\n : C extends BigIntConstructor\n ? bigint\n : C extends SymbolConstructor\n ? symbol\n : C extends DateConstructor\n ? Date\n : C extends AnyCtor<infer T>\n ? T\n : never;\n\n/**\n * Options for the Property decorator\n */\nexport interface PropertyOptions<\n T = any,\n C extends CtorLike<T> = AnyCtor<T> | BuiltinCtors,\n> {\n /**\n * Custom equality comparison function for this property\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns true if values are equal, false otherwise\n */\n equals?: (a: InstanceOfCtorLike<C>, b: InstanceOfCtorLike<C>) => boolean;\n\n /**\n * Type constructor for this property. Required for EntityUtils.parse() support.\n * Use a function that returns the type constructor to support forward references.\n * @example\n * @Property({ type: () => String })\n * name!: string;\n *\n * @Property({ type: () => Address })\n * address!: Address;\n */\n type: () => C;\n\n /**\n * Whether this property is an array. Defaults to false.\n * When true, the deserializer will map over array elements.\n * @example\n * @Property({ type: () => String, array: true })\n * tags!: string[];\n */\n array?: boolean;\n\n /**\n * Whether this property is a collection entity. Defaults to false.\n * When true, the property is an entity with a 'collection' array property.\n * During serialization, the collection entity is unwrapped to just the array.\n * During deserialization, the array is wrapped in a collection entity.\n * @example\n * @Property({ type: () => MyCollection, collection: true })\n * myCollection!: MyCollection;\n */\n collection?: boolean;\n\n /**\n * Whether this property is optional. Defaults to false.\n * When true, the property can be undefined or null.\n * When false, the property must be present and not null/undefined.\n * @example\n * @Property({ type: () => String, optional: true })\n * nickname?: string;\n */\n optional?: boolean;\n\n /**\n * Whether the array can contain null/undefined elements. Defaults to false.\n * Only applicable when array is true.\n * When false (default), null/undefined elements will cause an error.\n * When true, null/undefined elements are allowed in the array.\n * @example\n * @Property({ type: () => String, array: true, sparse: true })\n * tags!: (string | null)[];\n */\n sparse?: boolean;\n\n /**\n * Whether to bypass type validation and pass values through as-is.\n * Use this for generic types like Record<string, unknown> or any.\n * When true, no type checking or transformation is performed.\n * Also bypasses any custom serialize/deserialize callbacks.\n * @example\n * @Property({ passthrough: true })\n * metadata!: Record<string, unknown>;\n */\n passthrough?: boolean;\n\n /**\n * Whether to prevent this property from being updated via EntityUtils.update().\n * When true, this property cannot be modified through update operations.\n * Defaults to false, allowing the property to be updated.\n * @example\n * @Property({ type: () => String, preventUpdates: true })\n * id!: string; // This property cannot be updated\n */\n preventUpdates?: boolean;\n\n /**\n * Default value for the property when not provided during deserialization.\n * Can be a static value or a function that returns the default value.\n * The function can be async and return a Promise.\n * @example\n * @Property({ type: () => String, default: 'N/A' })\n * nickname?: string;\n *\n * @Property({ type: () => Date, default: () => new Date() })\n * createdAt!: Date;\n *\n * @Property({ type: () => String, default: async () => await fetchDefaultValue() })\n * value!: string;\n */\n default?:\n | InstanceOfCtorLike<C>\n | (() => InstanceOfCtorLike<C> | Promise<InstanceOfCtorLike<C>>);\n\n /**\n * Custom serialization function to convert the property value to JSON-compatible format.\n * Must be paired with deserialize - both must be defined together or both omitted.\n * Not used when passthrough is true.\n * @example\n * @Property({\n * type: () => MyClass,\n * serialize: (value) => ({ data: value.toData() }),\n * deserialize: (json) => MyClass.fromData(json.data)\n * })\n * myProperty!: MyClass;\n */\n serialize?: (value: InstanceOfCtorLike<C>) => unknown;\n\n /**\n * Custom deserialization function to convert JSON data back to the property type.\n * Must be paired with serialize - both must be defined together or both omitted.\n * Not used when passthrough is true.\n * @example\n * @Property({\n * type: () => MyClass,\n * serialize: (value) => ({ data: value.toData() }),\n * deserialize: (json) => MyClass.fromData(json.data)\n * })\n * myProperty!: MyClass;\n */\n deserialize?: (serialized: unknown) => InstanceOfCtorLike<C>;\n\n /**\n * Array of validator functions for this property.\n * Each validator receives the property value and validation context.\n * Empty array means validation passed.\n * If the property is an array (array: true), these validators will run against each item.\n * Use arrayValidators instead to validate the array as a whole.\n * If passthrough is true, validators will run against the raw value.\n * @example\n * @Property({\n * type: () => String,\n * validators: [\n * (value, { createProblem }) =>\n * value.length > 10 ? [createProblem('Too long')] : []\n * ]\n * })\n * name!: string;\n */\n validators?: PropertyValidator<InstanceOfCtorLike<C>>[];\n\n /**\n * Array of validator functions for this property when it is an array.\n * Each validator receives the array value and validation context.\n * Empty array means validation passed.\n * Only applicable when array is true.\n * Not applicable when passthrough is true.\n * @example\n * @Property({\n * type: () => Number,\n * array: true,\n * arrayValidators: [\n * (value, { createProblem }) =>\n * value.length === 0 ? [createProblem('Array cannot be empty')] : []\n * ]\n * })\n * scores!: number[];\n */\n arrayValidators?: PropertyValidator<InstanceOfCtorLike<C>[]>[];\n}\n\n/**\n * A validator function for a property.\n * The validator receives the value and returns Problems with property paths relative to the value.\n * Can be synchronous or asynchronous.\n * The calling code will prepend the actual property key to all returned problems.\n *\n * @param data - Object containing the value to validate\n * @param data.value - The value to validate\n * @returns Array of Problems (empty if valid) or Promise resolving to Problems.\n * Problems should have empty property for the value itself,\n * or relative paths for nested properties (e.g., 'name', '[0]', 'address.street')\n *\n * @example\n * ```typescript\n * // Synchronous validator\n * (({ value }) =>\n * value.length < 3 ? [new Problem({ property: '', message: 'Too short' })] : [])\n *\n * // Asynchronous validator\n * async ({ value }) => {\n * const exists = await checkDatabase(value);\n * return exists ? [] : [new Problem({ property: '', message: 'Not found' })];\n * }\n * ```\n */\nexport type PropertyValidator<T> = (data: {\n value: T;\n}) => Problem[] | Promise<Problem[]>;\n\n/**\n * A validator function for an entity.\n * Can be synchronous or asynchronous.\n * @param instance - The entity instance to validate\n * @returns Array of Problems (empty if valid) or Promise resolving to Problems\n */\nexport type EntityValidatorFn<T = any> = (\n instance: T,\n) => Problem[] | Promise<Problem[]>;\n\nexport interface Type<T = any> extends Function {\n new (...args: any[]): T;\n}\n\nexport type EntityDIToken<T = any> =\n | string\n | symbol\n | Function\n | AnyCtor<T>\n | Type<T>;\n\nexport type EntityDIProvider<T = any> =\n | {\n provide: EntityDIToken<T>;\n useValue?: T;\n useFactory?: never;\n }\n | {\n provide: EntityDIToken<T>;\n useValue?: never;\n useFactory?: () => T | Promise<T>;\n };\n\nexport type EntityDIFallbackFn = (token: EntityDIToken) => Promise<any>;\n\n/**\n * Generic type for safe operation results that can succeed or fail\n * @template T - The data type on success\n */\nexport type SafeOperationResult<T> = Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n>;\n\nexport type ParseOptions = { strict?: boolean };\n"],"names":["PROPERTY_METADATA_KEY","Symbol","PROPERTY_OPTIONS_METADATA_KEY","ENTITY_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","INJECTED_PROPERTY_METADATA_KEY","INJECTED_PROPERTY_OPTIONS_METADATA_KEY"],"mappings":"AAAA,6DAA6D,GAC7D,qDAAqD,GACrD,6DAA6D,GAG7D;;CAEC,GACD,OAAO,MAAMA,wBAAwBC,OAAO,qBAAqB;AAEjE;;CAEC,GACD,OAAO,MAAMC,gCAAgCD,OAC3C,6BACA;AAEF;;CAEC,GACD,OAAO,MAAME,sBAAsBF,OAAO,mBAAmB;AAE7D;;CAEC,GACD,OAAO,MAAMG,gCAAgCH,OAC3C,6BACA;AAEF;;CAEC,GACD,OAAO,MAAMI,iCAAiCJ,OAC5C,8BACA;AAEF;;CAEC,GACD,OAAO,MAAMK,yCAAyCL,OACpD,sCACA"}
1
+ {"version":3,"sources":["../../src/lib/types.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-function-type */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-wrapper-object-types */\nimport type { Problem } from './problem.js';\n\n/**\n * Metadata key used to store property information\n */\nexport const PROPERTY_METADATA_KEY = Symbol('property:metadata');\n\n/**\n * Metadata key used to store property options\n */\nexport const PROPERTY_OPTIONS_METADATA_KEY = Symbol(\n 'property:options:metadata',\n);\n\n/**\n * Metadata key used to store entity information\n */\nexport const ENTITY_METADATA_KEY = Symbol('entity:metadata');\n\n/**\n * Metadata key used to store entity options\n */\nexport const ENTITY_OPTIONS_METADATA_KEY = Symbol('entity:options:metadata');\n\n/**\n * Metadata key used to store entity validator methods\n */\nexport const ENTITY_VALIDATOR_METADATA_KEY = Symbol(\n 'entity:validator:metadata',\n);\n\n/**\n * Metadata key used to store injected property information\n */\nexport const INJECTED_PROPERTY_METADATA_KEY = Symbol(\n 'injected-property:metadata',\n);\n\n/**\n * Metadata key used to store injected property options (tokens)\n */\nexport const INJECTED_PROPERTY_OPTIONS_METADATA_KEY = Symbol(\n 'injected-property:options:metadata',\n);\n\nexport type AnyCtor<T = any> = Function & { prototype: T };\n\nexport type BuiltinCtors =\n | StringConstructor\n | NumberConstructor\n | BooleanConstructor\n | BigIntConstructor\n | SymbolConstructor\n | DateConstructor;\n\n/**\n * Type constructors for primitive types that can be deserialized\n * (excludes Symbol which cannot be deserialized from JSON)\n */\nexport type PrimitiveConstructor =\n | StringConstructor\n | NumberConstructor\n | BooleanConstructor\n | BigIntConstructor\n | DateConstructor;\n\nexport type CtorLike<T> = AnyCtor<T> | BuiltinCtors;\n\nexport type InstanceOfCtorLike<C> = C extends StringConstructor\n ? string\n : C extends NumberConstructor\n ? number\n : C extends BooleanConstructor\n ? boolean\n : C extends BigIntConstructor\n ? bigint\n : C extends SymbolConstructor\n ? symbol\n : C extends DateConstructor\n ? Date\n : C extends AnyCtor<infer T>\n ? T\n : never;\n\n/**\n * Options for the Property decorator\n */\nexport interface PropertyOptions<\n T = any,\n C extends CtorLike<T> = AnyCtor<T> | BuiltinCtors,\n> {\n /**\n * Custom equality comparison function for this property\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns true if values are equal, false otherwise\n */\n equals?: (a: InstanceOfCtorLike<C>, b: InstanceOfCtorLike<C>) => boolean;\n\n /**\n * Type constructor for this property. Required for EntityUtils.parse() support.\n * Use a function that returns the type constructor to support forward references.\n * @example\n * @Property({ type: () => String })\n * name!: string;\n *\n * @Property({ type: () => Address })\n * address!: Address;\n */\n type: () => C;\n\n /**\n * Whether this property is an array. Defaults to false.\n * When true, the deserializer will map over array elements.\n * @example\n * @Property({ type: () => String, array: true })\n * tags!: string[];\n */\n array?: boolean;\n\n /**\n * Whether this property is optional. Defaults to false.\n * When true, the property can be undefined or null.\n * When false, the property must be present and not null/undefined.\n * @example\n * @Property({ type: () => String, optional: true })\n * nickname?: string;\n */\n optional?: boolean;\n\n /**\n * Whether the array can contain null/undefined elements. Defaults to false.\n * Only applicable when array is true.\n * When false (default), null/undefined elements will cause an error.\n * When true, null/undefined elements are allowed in the array.\n * @example\n * @Property({ type: () => String, array: true, sparse: true })\n * tags!: (string | null)[];\n */\n sparse?: boolean;\n\n /**\n * Whether to bypass type validation and pass values through as-is.\n * Use this for generic types like Record<string, unknown> or any.\n * When true, no type checking or transformation is performed.\n * Also bypasses any custom serialize/deserialize callbacks.\n * @example\n * @Property({ passthrough: true })\n * metadata!: Record<string, unknown>;\n */\n passthrough?: boolean;\n\n /**\n * Whether to prevent this property from being updated via EntityUtils.update().\n * When true, this property cannot be modified through update operations.\n * Defaults to false, allowing the property to be updated.\n * @example\n * @Property({ type: () => String, preventUpdates: true })\n * id!: string; // This property cannot be updated\n */\n preventUpdates?: boolean;\n\n /**\n * Default value for the property when not provided during deserialization.\n * Can be a static value or a function that returns the default value.\n * The function can be async and return a Promise.\n * @example\n * @Property({ type: () => String, default: 'N/A' })\n * nickname?: string;\n *\n * @Property({ type: () => Date, default: () => new Date() })\n * createdAt!: Date;\n *\n * @Property({ type: () => String, default: async () => await fetchDefaultValue() })\n * value!: string;\n */\n default?:\n | InstanceOfCtorLike<C>\n | (() => InstanceOfCtorLike<C> | Promise<InstanceOfCtorLike<C>>);\n\n /**\n * Custom serialization function to convert the property value to JSON-compatible format.\n * Must be paired with deserialize - both must be defined together or both omitted.\n * Not used when passthrough is true.\n * @example\n * @Property({\n * type: () => MyClass,\n * serialize: (value) => ({ data: value.toData() }),\n * deserialize: (json) => MyClass.fromData(json.data)\n * })\n * myProperty!: MyClass;\n */\n serialize?: (value: InstanceOfCtorLike<C>) => unknown;\n\n /**\n * Custom deserialization function to convert JSON data back to the property type.\n * Must be paired with serialize - both must be defined together or both omitted.\n * Not used when passthrough is true.\n * @example\n * @Property({\n * type: () => MyClass,\n * serialize: (value) => ({ data: value.toData() }),\n * deserialize: (json) => MyClass.fromData(json.data)\n * })\n * myProperty!: MyClass;\n */\n deserialize?: (serialized: unknown) => InstanceOfCtorLike<C>;\n\n /**\n * Array of validator functions for this property.\n * Each validator receives the property value and validation context.\n * Empty array means validation passed.\n * If the property is an array (array: true), these validators will run against each item.\n * Use arrayValidators instead to validate the array as a whole.\n * If passthrough is true, validators will run against the raw value.\n * @example\n * @Property({\n * type: () => String,\n * validators: [\n * (value, { createProblem }) =>\n * value.length > 10 ? [createProblem('Too long')] : []\n * ]\n * })\n * name!: string;\n */\n validators?: PropertyValidator<InstanceOfCtorLike<C>>[];\n\n /**\n * Array of validator functions for this property when it is an array.\n * Each validator receives the array value and validation context.\n * Empty array means validation passed.\n * Only applicable when array is true.\n * Not applicable when passthrough is true.\n * @example\n * @Property({\n * type: () => Number,\n * array: true,\n * arrayValidators: [\n * (value, { createProblem }) =>\n * value.length === 0 ? [createProblem('Array cannot be empty')] : []\n * ]\n * })\n * scores!: number[];\n */\n arrayValidators?: PropertyValidator<InstanceOfCtorLike<C>[]>[];\n}\n\n/**\n * A validator function for a property.\n * The validator receives the value and returns Problems with property paths relative to the value.\n * Can be synchronous or asynchronous.\n * The calling code will prepend the actual property key to all returned problems.\n *\n * @param data - Object containing the value to validate\n * @param data.value - The value to validate\n * @returns Array of Problems (empty if valid) or Promise resolving to Problems.\n * Problems should have empty property for the value itself,\n * or relative paths for nested properties (e.g., 'name', '[0]', 'address.street')\n *\n * @example\n * ```typescript\n * // Synchronous validator\n * (({ value }) =>\n * value.length < 3 ? [new Problem({ property: '', message: 'Too short' })] : [])\n *\n * // Asynchronous validator\n * async ({ value }) => {\n * const exists = await checkDatabase(value);\n * return exists ? [] : [new Problem({ property: '', message: 'Not found' })];\n * }\n * ```\n */\nexport type PropertyValidator<T> = (data: {\n value: T;\n}) => Problem[] | Promise<Problem[]>;\n\n/**\n * A validator function for an entity.\n * Can be synchronous or asynchronous.\n * @param instance - The entity instance to validate\n * @returns Array of Problems (empty if valid) or Promise resolving to Problems\n */\nexport type EntityValidatorFn<T = any> = (\n instance: T,\n) => Problem[] | Promise<Problem[]>;\n\nexport interface Type<T = any> extends Function {\n new (...args: any[]): T;\n}\n\nexport type EntityDIToken<T = any> =\n | string\n | symbol\n | Function\n | AnyCtor<T>\n | Type<T>;\n\nexport type EntityDIProvider<T = any> =\n | {\n provide: EntityDIToken<T>;\n useValue?: T;\n useFactory?: never;\n }\n | {\n provide: EntityDIToken<T>;\n useValue?: never;\n useFactory?: () => T | Promise<T>;\n };\n\nexport type EntityDIFallbackFn = (token: EntityDIToken) => Promise<any>;\n\n/**\n * Generic type for safe operation results that can succeed or fail\n * @template T - The data type on success\n */\nexport type SafeOperationResult<T> = Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n>;\n\nexport type ParseOptions = { strict?: boolean };\n"],"names":["PROPERTY_METADATA_KEY","Symbol","PROPERTY_OPTIONS_METADATA_KEY","ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","INJECTED_PROPERTY_METADATA_KEY","INJECTED_PROPERTY_OPTIONS_METADATA_KEY"],"mappings":"AAAA,6DAA6D,GAC7D,qDAAqD,GACrD,6DAA6D,GAG7D;;CAEC,GACD,OAAO,MAAMA,wBAAwBC,OAAO,qBAAqB;AAEjE;;CAEC,GACD,OAAO,MAAMC,gCAAgCD,OAC3C,6BACA;AAEF;;CAEC,GACD,OAAO,MAAME,sBAAsBF,OAAO,mBAAmB;AAE7D;;CAEC,GACD,OAAO,MAAMG,8BAA8BH,OAAO,2BAA2B;AAE7E;;CAEC,GACD,OAAO,MAAMI,gCAAgCJ,OAC3C,6BACA;AAEF;;CAEC,GACD,OAAO,MAAMK,iCAAiCL,OAC5C,8BACA;AAEF;;CAEC,GACD,OAAO,MAAMM,yCAAyCN,OACpD,sCACA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtpaulino/entity",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -1,38 +0,0 @@
1
- import { AnyCtor, PropertyOptions } from './types.js';
2
- /**
3
- * Helper decorator for collection properties that wrap arrays in entity classes.
4
- * The collection entity must have a `collection` property decorated with @ArrayProperty().
5
- * When serialized with EntityUtils.toJSON(), the collection is unwrapped to just the array.
6
- *
7
- * @param type - Function returning the collection entity class constructor
8
- * @param options - Additional property options
9
- *
10
- * @example
11
- * ```typescript
12
- * @Entity()
13
- * class MyCollection {
14
- * @ArrayProperty(() => String)
15
- * readonly collection: string[];
16
- *
17
- * constructor(data: { collection: string[] }) {
18
- * this.collection = data.collection;
19
- * }
20
- * }
21
- *
22
- * @Entity()
23
- * class MyEntity {
24
- * @CollectionProperty(() => MyCollection)
25
- * myCollection: MyCollection;
26
- * }
27
- *
28
- * const entity = new MyEntity(...);
29
- * const json = EntityUtils.toJSON(entity);
30
- * // { myCollection: ["a", "b"] } - unwrapped to array
31
- * ```
32
- */
33
- export declare function CollectionProperty<T extends {
34
- collection: unknown[];
35
- }, C extends AnyCtor<T> & {
36
- new (data: any): T;
37
- }>(type: () => C, options?: Omit<PropertyOptions<T, C>, 'type' | 'collection' | 'array' | 'passthrough'>): PropertyDecorator;
38
- //# sourceMappingURL=collection-property.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"collection-property.d.ts","sourceRoot":"","sources":["../../src/lib/collection-property.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAA2B,eAAe,EAAE,MAAM,YAAY,CAAC;AAI/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,kBAAkB,CAChC,CAAC,SAAS;IAAE,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE,EACnC,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,GAAG;IAAE,KAAK,IAAI,EAAE,GAAG,GAAG,CAAC,CAAA;CAAE,EAE7C,IAAI,EAAE,MAAM,CAAC,EACb,OAAO,CAAC,EAAE,IAAI,CACZ,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EACrB,MAAM,GAAG,YAAY,GAAG,OAAO,GAAG,aAAa,CAChD,GACA,iBAAiB,CAiBnB"}
@@ -1,47 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */ import { Property } from './property.js';
2
- import { EntityUtils } from './entity-utils.js';
3
- /**
4
- * Helper decorator for collection properties that wrap arrays in entity classes.
5
- * The collection entity must have a `collection` property decorated with @ArrayProperty().
6
- * When serialized with EntityUtils.toJSON(), the collection is unwrapped to just the array.
7
- *
8
- * @param type - Function returning the collection entity class constructor
9
- * @param options - Additional property options
10
- *
11
- * @example
12
- * ```typescript
13
- * @Entity()
14
- * class MyCollection {
15
- * @ArrayProperty(() => String)
16
- * readonly collection: string[];
17
- *
18
- * constructor(data: { collection: string[] }) {
19
- * this.collection = data.collection;
20
- * }
21
- * }
22
- *
23
- * @Entity()
24
- * class MyEntity {
25
- * @CollectionProperty(() => MyCollection)
26
- * myCollection: MyCollection;
27
- * }
28
- *
29
- * const entity = new MyEntity(...);
30
- * const json = EntityUtils.toJSON(entity);
31
- * // { myCollection: ["a", "b"] } - unwrapped to array
32
- * ```
33
- */ export function CollectionProperty(type, options) {
34
- const maybeDefaultProperty = options?.optional !== true ? {
35
- default: async ()=>await EntityUtils.parse(type(), {
36
- collection: []
37
- })
38
- } : {};
39
- return Property({
40
- ...maybeDefaultProperty,
41
- ...options,
42
- type,
43
- collection: true
44
- });
45
- }
46
-
47
- //# sourceMappingURL=collection-property.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/lib/collection-property.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { AnyCtor, type InstanceOfCtorLike, PropertyOptions } from './types.js';\nimport { Property } from './property.js';\nimport { EntityUtils } from './entity-utils.js';\n\n/**\n * Helper decorator for collection properties that wrap arrays in entity classes.\n * The collection entity must have a `collection` property decorated with @ArrayProperty().\n * When serialized with EntityUtils.toJSON(), the collection is unwrapped to just the array.\n *\n * @param type - Function returning the collection entity class constructor\n * @param options - Additional property options\n *\n * @example\n * ```typescript\n * @Entity()\n * class MyCollection {\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 MyEntity {\n * @CollectionProperty(() => MyCollection)\n * myCollection: MyCollection;\n * }\n *\n * const entity = new MyEntity(...);\n * const json = EntityUtils.toJSON(entity);\n * // { myCollection: [\"a\", \"b\"] } - unwrapped to array\n * ```\n */\nexport function CollectionProperty<\n T extends { collection: unknown[] },\n C extends AnyCtor<T> & { new (data: any): T },\n>(\n type: () => C,\n options?: Omit<\n PropertyOptions<T, C>,\n 'type' | 'collection' | 'array' | 'passthrough'\n >,\n): PropertyDecorator {\n const maybeDefaultProperty =\n options?.optional !== true\n ? {\n default: async () =>\n (await EntityUtils.parse(type(), {\n collection: [],\n })) as InstanceOfCtorLike<C>,\n }\n : {};\n\n return Property<T, C>({\n ...maybeDefaultProperty,\n ...options,\n type,\n collection: true,\n });\n}\n"],"names":["Property","EntityUtils","CollectionProperty","type","options","maybeDefaultProperty","optional","default","parse","collection"],"mappings":"AAAA,qDAAqD,GAErD,SAASA,QAAQ,QAAQ,gBAAgB;AACzC,SAASC,WAAW,QAAQ,oBAAoB;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BC,GACD,OAAO,SAASC,mBAIdC,IAAa,EACbC,OAGC;IAED,MAAMC,uBACJD,SAASE,aAAa,OAClB;QACEC,SAAS,UACN,MAAMN,YAAYO,KAAK,CAACL,QAAQ;gBAC/BM,YAAY,EAAE;YAChB;IACJ,IACA,CAAC;IAEP,OAAOT,SAAe;QACpB,GAAGK,oBAAoB;QACvB,GAAGD,OAAO;QACVD;QACAM,YAAY;IACd;AACF"}