@rtpaulino/entity 0.20.0 → 0.22.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.
@@ -50,6 +50,26 @@ export declare class EntityUtils {
50
50
  * ```
51
51
  */
52
52
  static isCollectionEntity(entityOrClass: unknown): boolean;
53
+ /**
54
+ * Checks if a given entity is marked as a stringifiable entity
55
+ *
56
+ * @param entityOrClass - The entity instance or class to check
57
+ * @returns true if the entity is a stringifiable entity, false otherwise
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * @Stringifiable()
62
+ * class UserId {
63
+ * @StringProperty()
64
+ * value: string;
65
+ * }
66
+ *
67
+ * const userId = new UserId({ value: 'user-123' });
68
+ * console.log(EntityUtils.isStringifiable(userId)); // true
69
+ * console.log(EntityUtils.isStringifiable(UserId)); // true
70
+ * ```
71
+ */
72
+ static isStringifiable(entityOrClass: unknown): boolean;
53
73
  static sameEntity(a: object, b: object): boolean;
54
74
  static getPropertyKeys(target: object): string[];
55
75
  static getPropertyOptions(target: object, propertyKey: string): PropertyOptions | undefined;
@@ -127,6 +147,11 @@ export declare class EntityUtils {
127
147
  * @private
128
148
  */
129
149
  private static serializeValue;
150
+ /**
151
+ * Internal parse implementation with extended options
152
+ * @private
153
+ */
154
+ private static _parseInternal;
130
155
  /**
131
156
  * Deserializes a plain object to an entity instance
132
157
  *
@@ -211,6 +236,87 @@ export declare class EntityUtils {
211
236
  * ```
212
237
  */
213
238
  static safeParse<T extends object>(entityClass: new (data: any) => T, plainObject: unknown, parseOptions?: ParseOptions): SafeOperationResult<T>;
239
+ /**
240
+ * Partially deserializes a plain object, returning a plain object with only present properties
241
+ *
242
+ * @param entityClass - The entity class constructor
243
+ * @param plainObject - The plain object to deserialize
244
+ * @param options - Options with strict mode
245
+ * @returns Promise resolving to a plain object with deserialized properties (Partial<T>)
246
+ *
247
+ * @remarks
248
+ * Differences from parse():
249
+ * - Returns a plain object, not an entity instance
250
+ * - Ignores missing properties (does not include them in result)
251
+ * - Does NOT apply default values to missing properties
252
+ * - When strict: false (default), properties with HARD problems are excluded from result but problems are tracked
253
+ * - When strict: true, any HARD problem throws ValidationError
254
+ * - Nested entities/arrays are still fully deserialized and validated as normal
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * @Entity()
259
+ * class User {
260
+ * @Property({ type: () => String }) name!: string;
261
+ * @Property({ type: () => Number, default: 0 }) age!: number;
262
+ *
263
+ * constructor(data: Partial<User>) {
264
+ * Object.assign(this, data);
265
+ * }
266
+ * }
267
+ *
268
+ * const partial = await EntityUtils.partialParse(User, { name: 'John' });
269
+ * // partial = { name: 'John' } (age is not included, default not applied)
270
+ *
271
+ * const partialWithError = await EntityUtils.partialParse(User, { name: 'John', age: 'invalid' });
272
+ * // partialWithError = { name: 'John' } (age excluded due to HARD problem)
273
+ * // Access problems via second return value
274
+ * ```
275
+ */
276
+ static partialParse<T extends object>(entityClass: new (data: any) => T, plainObject: unknown, options?: {
277
+ strict?: boolean;
278
+ }): Promise<Partial<T>>;
279
+ /**
280
+ * Safely performs partial deserialization without throwing errors
281
+ *
282
+ * @param entityClass - The entity class constructor
283
+ * @param plainObject - The plain object to deserialize
284
+ * @param options - Options with strict mode
285
+ * @returns Promise resolving to a result object with success flag, partial data, and problems
286
+ *
287
+ * @remarks
288
+ * Similar to partialParse() but returns a result object instead of throwing errors:
289
+ * - On success with strict: true - returns { success: true, data: Partial<T>, problems: [] }
290
+ * - On success with strict: false - returns { success: true, data: Partial<T>, problems: [...] } (includes hard problems for excluded properties)
291
+ * - On failure (strict mode only) - returns { success: false, data: undefined, problems: [...] }
292
+ *
293
+ * All partial deserialization rules from partialParse() apply.
294
+ * See partialParse() documentation for detailed behavior.
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * @Entity()
299
+ * class User {
300
+ * @Property({ type: () => String }) name!: string;
301
+ * @Property({ type: () => Number }) age!: number;
302
+ *
303
+ * constructor(data: Partial<User>) {
304
+ * Object.assign(this, data);
305
+ * }
306
+ * }
307
+ *
308
+ * const result = await EntityUtils.safePartialParse(User, { name: 'John', age: 'invalid' });
309
+ * if (result.success) {
310
+ * console.log(result.data); // { name: 'John' }
311
+ * console.log(result.problems); // [Problem for age property]
312
+ * } else {
313
+ * console.log(result.problems); // Hard problems (only in strict mode)
314
+ * }
315
+ * ```
316
+ */
317
+ static safePartialParse<T extends object>(entityClass: new (data: any) => T, plainObject: unknown, options?: {
318
+ strict?: boolean;
319
+ }): Promise<SafeOperationResult<Partial<T>>>;
214
320
  /**
215
321
  * Updates an entity instance with new values, respecting preventUpdates flags on properties
216
322
  *
@@ -312,6 +418,11 @@ export declare class EntityUtils {
312
418
  * @private
313
419
  */
314
420
  private static runPropertyValidators;
421
+ /**
422
+ * Validates all properties on an object (entity instance or plain object)
423
+ * @private
424
+ */
425
+ private static validateProperties;
315
426
  private static addInjectedDependencies;
316
427
  /**
317
428
  * Validates an entity instance by running all property and entity validators
@@ -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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"}
@@ -84,6 +84,31 @@ export class EntityUtils {
84
84
  const options = this.getEntityOptions(entityOrClass);
85
85
  return options.collection === true;
86
86
  }
87
+ /**
88
+ * Checks if a given entity is marked as a stringifiable entity
89
+ *
90
+ * @param entityOrClass - The entity instance or class to check
91
+ * @returns true if the entity is a stringifiable entity, false otherwise
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * @Stringifiable()
96
+ * class UserId {
97
+ * @StringProperty()
98
+ * value: string;
99
+ * }
100
+ *
101
+ * const userId = new UserId({ value: 'user-123' });
102
+ * console.log(EntityUtils.isStringifiable(userId)); // true
103
+ * console.log(EntityUtils.isStringifiable(UserId)); // true
104
+ * ```
105
+ */ static isStringifiable(entityOrClass) {
106
+ if (!this.isEntity(entityOrClass)) {
107
+ return false;
108
+ }
109
+ const options = this.getEntityOptions(entityOrClass);
110
+ return options.stringifiable === true;
111
+ }
87
112
  static sameEntity(a, b) {
88
113
  if (!this.isEntity(a) || !this.isEntity(b)) {
89
114
  return false;
@@ -254,6 +279,19 @@ export class EntityUtils {
254
279
  * // ['a', 'b'] - unwrapped to array
255
280
  * ```
256
281
  */ static toJSON(entity) {
282
+ if (this.isStringifiable(entity)) {
283
+ const valuePropertyOptions = this.getPropertyOptions(entity, 'value');
284
+ if (!valuePropertyOptions) {
285
+ throw new Error(`Stringifiable entity 'value' property is missing metadata`);
286
+ }
287
+ if (valuePropertyOptions.array) {
288
+ throw new Error(`Stringifiable entity 'value' property must not be an array`);
289
+ }
290
+ if (valuePropertyOptions.type?.() !== String) {
291
+ throw new Error(`Stringifiable entity 'value' property must be of type String`);
292
+ }
293
+ return this.serializeValue(entity.value, valuePropertyOptions);
294
+ }
257
295
  if (this.isCollectionEntity(entity)) {
258
296
  const collectionPropertyOptions = this.getPropertyOptions(entity, 'collection');
259
297
  if (!collectionPropertyOptions) {
@@ -316,50 +354,14 @@ export class EntityUtils {
316
354
  throw new Error(`Cannot serialize value of type '${typeof value}'. Use passthrough: true in @Property() to explicitly allow serialization of unknown types.`);
317
355
  }
318
356
  /**
319
- * Deserializes a plain object to an entity instance
320
- *
321
- * @param entityClass - The entity class constructor. Must accept a data object parameter.
322
- * @param plainObject - The plain object to deserialize
323
- * @param parseOptions - Parse options (strict mode)
324
- * @returns Promise resolving to a new instance of the entity with deserialized values
325
- *
326
- * @remarks
327
- * Deserialization rules:
328
- * - All @Property() decorators must include type metadata for parse() to work
329
- * - Properties without type metadata will throw an error
330
- * - Required properties (optional !== true) must be present and not null/undefined
331
- * - Optional properties (optional === true) can be undefined or null
332
- * - Arrays are supported with the array: true option
333
- * - Nested entities are recursively deserialized
334
- * - Type conversion is strict (no coercion)
335
- * - Entity constructors must accept a required data parameter
336
- *
337
- * Validation behavior:
338
- * - If strict: true - both HARD and SOFT problems throw ValidationError
339
- * - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored
340
- * - Property validators run first, then entity validators
341
- * - Validators can be synchronous or asynchronous
342
- * - Problems are accessible via EntityUtils.getProblems()
343
- * - Raw input data is accessible via EntityUtils.getRawInput()
344
- *
345
- * @example
346
- * ```typescript
347
- * @Entity()
348
- * class User {
349
- * @Property({ type: () => String }) name!: string;
350
- * @Property({ type: () => Number }) age!: number;
351
- *
352
- * constructor(data: Partial<User>) {
353
- * Object.assign(this, data);
354
- * }
355
- * }
356
- *
357
- * const json = { name: 'John', age: 30 };
358
- * const user = await EntityUtils.parse(User, json);
359
- * const userStrict = await EntityUtils.parse(User, json, { strict: true });
360
- * ```
361
- */ static async parse(entityClass, plainObject, parseOptions = {}) {
362
- if (this.isCollectionEntity(entityClass)) {
357
+ * Internal parse implementation with extended options
358
+ * @private
359
+ */ static async _parseInternal(entityClass, plainObject, options = {}) {
360
+ if (this.isStringifiable(entityClass)) {
361
+ plainObject = {
362
+ value: plainObject
363
+ };
364
+ } else if (this.isCollectionEntity(entityClass)) {
363
365
  plainObject = {
364
366
  collection: plainObject
365
367
  };
@@ -373,7 +375,9 @@ export class EntityUtils {
373
375
  if (typeof plainObject !== 'object') {
374
376
  throw createValidationError(`Expects an object but received ${typeof plainObject}`);
375
377
  }
376
- const strict = parseOptions?.strict ?? false;
378
+ const strict = options.strict ?? false;
379
+ const skipDefaults = options.skipDefaults ?? false;
380
+ const skipMissing = options.skipMissing ?? false;
377
381
  const keys = this.getPropertyKeys(entityClass.prototype);
378
382
  const data = {};
379
383
  const hardProblems = [];
@@ -393,8 +397,11 @@ export class EntityUtils {
393
397
  }
394
398
  const isOptional = propertyOptions.optional === true;
395
399
  if (!(key in plainObject) || value == null) {
400
+ if (skipMissing) {
401
+ continue;
402
+ }
396
403
  let valueToSet = value;
397
- if (propertyOptions.default !== undefined) {
404
+ if (!skipDefaults && propertyOptions.default !== undefined) {
398
405
  valueToSet = typeof propertyOptions.default === 'function' ? await propertyOptions.default() : propertyOptions.default;
399
406
  }
400
407
  if (!isOptional && valueToSet == null) {
@@ -407,7 +414,10 @@ export class EntityUtils {
407
414
  continue;
408
415
  }
409
416
  try {
410
- data[key] = await this.deserializeValue(value, propertyOptions, parseOptions);
417
+ // Only pass strict to nested deserialization, not skipDefaults/skipMissing
418
+ data[key] = await this.deserializeValue(value, propertyOptions, {
419
+ strict
420
+ });
411
421
  } catch (error) {
412
422
  if (error instanceof ValidationError) {
413
423
  const problems = prependPropertyPath(key, error);
@@ -422,6 +432,59 @@ export class EntityUtils {
422
432
  }
423
433
  }
424
434
  }
435
+ return {
436
+ data,
437
+ hardProblems
438
+ };
439
+ }
440
+ /**
441
+ * Deserializes a plain object to an entity instance
442
+ *
443
+ * @param entityClass - The entity class constructor. Must accept a data object parameter.
444
+ * @param plainObject - The plain object to deserialize
445
+ * @param parseOptions - Parse options (strict mode)
446
+ * @returns Promise resolving to a new instance of the entity with deserialized values
447
+ *
448
+ * @remarks
449
+ * Deserialization rules:
450
+ * - All @Property() decorators must include type metadata for parse() to work
451
+ * - Properties without type metadata will throw an error
452
+ * - Required properties (optional !== true) must be present and not null/undefined
453
+ * - Optional properties (optional === true) can be undefined or null
454
+ * - Arrays are supported with the array: true option
455
+ * - Nested entities are recursively deserialized
456
+ * - Type conversion is strict (no coercion)
457
+ * - Entity constructors must accept a required data parameter
458
+ *
459
+ * Validation behavior:
460
+ * - If strict: true - both HARD and SOFT problems throw ValidationError
461
+ * - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored
462
+ * - Property validators run first, then entity validators
463
+ * - Validators can be synchronous or asynchronous
464
+ * - Problems are accessible via EntityUtils.getProblems()
465
+ * - Raw input data is accessible via EntityUtils.getRawInput()
466
+ *
467
+ * @example
468
+ * ```typescript
469
+ * @Entity()
470
+ * class User {
471
+ * @Property({ type: () => String }) name!: string;
472
+ * @Property({ type: () => Number }) age!: number;
473
+ *
474
+ * constructor(data: Partial<User>) {
475
+ * Object.assign(this, data);
476
+ * }
477
+ * }
478
+ *
479
+ * const json = { name: 'John', age: 30 };
480
+ * const user = await EntityUtils.parse(User, json);
481
+ * const userStrict = await EntityUtils.parse(User, json, { strict: true });
482
+ * ```
483
+ */ static async parse(entityClass, plainObject, parseOptions = {}) {
484
+ const strict = parseOptions?.strict ?? false;
485
+ const { data, hardProblems } = await this._parseInternal(entityClass, plainObject, {
486
+ strict
487
+ });
425
488
  if (hardProblems.length > 0) {
426
489
  throw new ValidationError(hardProblems);
427
490
  }
@@ -492,6 +555,119 @@ export class EntityUtils {
492
555
  }
493
556
  }
494
557
  /**
558
+ * Partially deserializes a plain object, returning a plain object with only present properties
559
+ *
560
+ * @param entityClass - The entity class constructor
561
+ * @param plainObject - The plain object to deserialize
562
+ * @param options - Options with strict mode
563
+ * @returns Promise resolving to a plain object with deserialized properties (Partial<T>)
564
+ *
565
+ * @remarks
566
+ * Differences from parse():
567
+ * - Returns a plain object, not an entity instance
568
+ * - Ignores missing properties (does not include them in result)
569
+ * - Does NOT apply default values to missing properties
570
+ * - When strict: false (default), properties with HARD problems are excluded from result but problems are tracked
571
+ * - When strict: true, any HARD problem throws ValidationError
572
+ * - Nested entities/arrays are still fully deserialized and validated as normal
573
+ *
574
+ * @example
575
+ * ```typescript
576
+ * @Entity()
577
+ * class User {
578
+ * @Property({ type: () => String }) name!: string;
579
+ * @Property({ type: () => Number, default: 0 }) age!: number;
580
+ *
581
+ * constructor(data: Partial<User>) {
582
+ * Object.assign(this, data);
583
+ * }
584
+ * }
585
+ *
586
+ * const partial = await EntityUtils.partialParse(User, { name: 'John' });
587
+ * // partial = { name: 'John' } (age is not included, default not applied)
588
+ *
589
+ * const partialWithError = await EntityUtils.partialParse(User, { name: 'John', age: 'invalid' });
590
+ * // partialWithError = { name: 'John' } (age excluded due to HARD problem)
591
+ * // Access problems via second return value
592
+ * ```
593
+ */ static async partialParse(entityClass, plainObject, options = {}) {
594
+ const result = await this.safePartialParse(entityClass, plainObject, options);
595
+ if (!result.success) {
596
+ throw new ValidationError(result.problems);
597
+ }
598
+ return result.data;
599
+ }
600
+ /**
601
+ * Safely performs partial deserialization without throwing errors
602
+ *
603
+ * @param entityClass - The entity class constructor
604
+ * @param plainObject - The plain object to deserialize
605
+ * @param options - Options with strict mode
606
+ * @returns Promise resolving to a result object with success flag, partial data, and problems
607
+ *
608
+ * @remarks
609
+ * Similar to partialParse() but returns a result object instead of throwing errors:
610
+ * - On success with strict: true - returns { success: true, data: Partial<T>, problems: [] }
611
+ * - On success with strict: false - returns { success: true, data: Partial<T>, problems: [...] } (includes hard problems for excluded properties)
612
+ * - On failure (strict mode only) - returns { success: false, data: undefined, problems: [...] }
613
+ *
614
+ * All partial deserialization rules from partialParse() apply.
615
+ * See partialParse() documentation for detailed behavior.
616
+ *
617
+ * @example
618
+ * ```typescript
619
+ * @Entity()
620
+ * class User {
621
+ * @Property({ type: () => String }) name!: string;
622
+ * @Property({ type: () => Number }) age!: number;
623
+ *
624
+ * constructor(data: Partial<User>) {
625
+ * Object.assign(this, data);
626
+ * }
627
+ * }
628
+ *
629
+ * const result = await EntityUtils.safePartialParse(User, { name: 'John', age: 'invalid' });
630
+ * if (result.success) {
631
+ * console.log(result.data); // { name: 'John' }
632
+ * console.log(result.problems); // [Problem for age property]
633
+ * } else {
634
+ * console.log(result.problems); // Hard problems (only in strict mode)
635
+ * }
636
+ * ```
637
+ */ static async safePartialParse(entityClass, plainObject, options) {
638
+ const strict = options?.strict ?? false;
639
+ const { data, hardProblems } = await this._parseInternal(entityClass, plainObject, {
640
+ strict,
641
+ skipDefaults: true,
642
+ skipMissing: true
643
+ });
644
+ if (strict && hardProblems.length > 0) {
645
+ return {
646
+ success: false,
647
+ data: undefined,
648
+ problems: hardProblems
649
+ };
650
+ }
651
+ const propertyProblems = await this.validateProperties(data, entityClass.prototype);
652
+ const validationProblems = [
653
+ ...hardProblems,
654
+ ...propertyProblems
655
+ ];
656
+ if (strict && propertyProblems.length > 0) {
657
+ return {
658
+ success: false,
659
+ data: undefined,
660
+ problems: validationProblems
661
+ };
662
+ }
663
+ this.setProblems(data, validationProblems);
664
+ return {
665
+ success: true,
666
+ data: data,
667
+ problems: validationProblems
668
+ };
669
+ }
670
+ /**
495
671
  * Updates an entity instance with new values, respecting preventUpdates flags on properties
496
672
  *
497
673
  * @param instance - The entity instance to update. Must be an Entity.
@@ -742,6 +918,24 @@ export class EntityUtils {
742
918
  }
743
919
  return problems;
744
920
  }
921
+ /**
922
+ * Validates all properties on an object (entity instance or plain object)
923
+ * @private
924
+ */ static async validateProperties(dataOrInstance, prototype) {
925
+ const problems = [];
926
+ const keys = Object.keys(dataOrInstance);
927
+ for (const key of keys){
928
+ const options = this.getPropertyOptions(prototype, key);
929
+ if (options) {
930
+ const value = dataOrInstance[key];
931
+ if (value != null) {
932
+ const validationProblems = await this.runPropertyValidators(key, value, options);
933
+ problems.push(...validationProblems);
934
+ }
935
+ }
936
+ }
937
+ return problems;
938
+ }
745
939
  static async addInjectedDependencies(data, prototype) {
746
940
  const injectedPropertyNames = getInjectedPropertyNames(prototype);
747
941
  if (injectedPropertyNames.length === 0) {
@@ -778,17 +972,8 @@ export class EntityUtils {
778
972
  throw new Error('Cannot validate non-entity instance');
779
973
  }
780
974
  const problems = [];
781
- const keys = this.getPropertyKeys(instance);
782
- for (const key of keys){
783
- const options = this.getPropertyOptions(instance, key);
784
- if (options) {
785
- const value = instance[key];
786
- if (value != null) {
787
- const validationProblems = await this.runPropertyValidators(key, value, options);
788
- problems.push(...validationProblems);
789
- }
790
- }
791
- }
975
+ const propertyProblems = await this.validateProperties(instance, instance);
976
+ problems.push(...propertyProblems);
792
977
  const entityValidators = this.getEntityValidators(instance);
793
978
  for (const validatorMethod of entityValidators){
794
979
  const validatorProblems = await instance[validatorMethod]();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/entity-utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-function-type */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n ParseOptions,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n SafeOperationResult,\n} from './types.js';\nimport type { EntityOptions } from './entity.js';\nimport {\n getInjectedPropertyNames,\n getInjectedPropertyOptions,\n} from './injected-property.js';\nimport { EntityDI } from './entity-di.js';\nimport { isEqualWith } from 'lodash-es';\nimport { ValidationError } from './validation-error.js';\nimport { Problem } from './problem.js';\nimport {\n prependPropertyPath,\n prependArrayIndex,\n createValidationError,\n combinePropertyPaths,\n} from './validation-utils.js';\nimport {\n isPrimitiveConstructor,\n deserializePrimitive,\n} from './primitive-deserializers.js';\nimport { ok } from 'assert';\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
+ {"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 /**\n * Checks if a given entity is marked as a stringifiable entity\n *\n * @param entityOrClass - The entity instance or class to check\n * @returns true if the entity is a stringifiable entity, false otherwise\n *\n * @example\n * ```typescript\n * @Stringifiable()\n * class UserId {\n * @StringProperty()\n * value: string;\n * }\n *\n * const userId = new UserId({ value: 'user-123' });\n * console.log(EntityUtils.isStringifiable(userId)); // true\n * console.log(EntityUtils.isStringifiable(UserId)); // true\n * ```\n */\n static isStringifiable(entityOrClass: unknown): boolean {\n if (!this.isEntity(entityOrClass)) {\n return false;\n }\n\n const options = this.getEntityOptions(entityOrClass);\n\n return options.stringifiable === true;\n }\n\n static sameEntity(a: object, b: object): boolean {\n if (!this.isEntity(a) || !this.isEntity(b)) {\n return false;\n }\n\n return Object.getPrototypeOf(a) === Object.getPrototypeOf(b);\n }\n\n static getPropertyKeys(target: object): string[] {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n const keys: string[] = [];\n const seen = new Set<string>();\n\n // Walk the prototype chain to collect all inherited properties\n while (currentProto && currentProto !== Object.prototype) {\n // Use getOwnMetadata to only get metadata directly on this prototype\n const protoKeys: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, currentProto) || [];\n\n for (const key of protoKeys) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return keys;\n }\n\n static getPropertyOptions(\n target: object,\n propertyKey: string,\n ): PropertyOptions | undefined {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n // Walk the prototype chain to find the property options\n while (currentProto && currentProto !== Object.prototype) {\n const protoOptions: Record<string, PropertyOptions> =\n Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, currentProto) ||\n {};\n\n if (protoOptions[propertyKey]) {\n return protoOptions[propertyKey];\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return undefined;\n }\n\n static equals(a: unknown, b: unknown): boolean {\n return isEqualWith(a, b, (val1, val2) => {\n if (this.isEntity(val1)) {\n if (!this.sameEntity(val1, val2)) {\n return false;\n }\n\n const diff = this.diff(val1, val2);\n\n return diff.length === 0;\n } else if (\n val1 != null &&\n val2 != null &&\n typeof val1 === 'object' &&\n !Array.isArray(val1) &&\n typeof val2 === 'object' &&\n !Array.isArray(val2) &&\n 'equals' in val1 &&\n typeof val1.equals === 'function'\n ) {\n return val1.equals(val2);\n }\n\n return undefined;\n });\n }\n\n static diff<T extends object>(\n oldEntity: T,\n newEntity: T,\n ): { property: string; oldValue: unknown; newValue: unknown }[] {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute diff');\n }\n\n const diffs: { property: string; oldValue: unknown; newValue: unknown }[] =\n [];\n\n const keys = this.getPropertyKeys(oldEntity);\n\n for (const key of keys) {\n const oldValue = (oldEntity as any)[key];\n const newValue = (newEntity as any)[key];\n\n // Check if there's a custom equals function for this property\n const propertyOptions = this.getPropertyOptions(oldEntity, key);\n\n let areEqual: boolean;\n if (oldValue == null && newValue == null) {\n areEqual = oldValue === newValue;\n } else if (oldValue == null || newValue == null) {\n areEqual = false;\n } else {\n areEqual = propertyOptions?.equals\n ? propertyOptions.equals(oldValue, newValue)\n : this.equals(oldValue, newValue);\n }\n\n if (!areEqual) {\n diffs.push({ property: key, oldValue, newValue });\n }\n }\n\n return diffs;\n }\n\n static changes<T extends object>(oldEntity: T, newEntity: T): Partial<T> {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute changes');\n }\n\n const diff = this.diff(oldEntity, newEntity);\n\n return diff.reduce((acc, { property, newValue }) => {\n (acc as any)[property] = newValue;\n return acc;\n }, {} as Partial<T>);\n }\n\n /**\n * Serializes an entity to a plain object, converting only properties decorated with @Property()\n *\n * @param entity - The entity instance to serialize\n * @returns A plain object containing only the serialized decorated properties, or an array for collection entities\n *\n * @remarks\n * Serialization rules:\n * - Only properties decorated with @Property() are included\n * - If a property has a custom toJSON() method, it will be used\n * - Nested entities are recursively serialized using EntityUtils.toJSON()\n * - Arrays are mapped with toJSON() applied to each element\n * - Date objects are serialized to ISO strings\n * - bigint values are serialized to strings\n * - undefined values are excluded from the output\n * - null values are included in the output\n * - Circular references are not supported (will cause stack overflow)\n * - Collection entities (@CollectionEntity) are unwrapped to just their array\n *\n * @example\n * ```typescript\n * @Entity()\n * class Address {\n * @Property() street: string;\n * @Property() city: string;\n * }\n *\n * @Entity()\n * class User {\n * @Property() name: string;\n * @Property() address: Address;\n * @Property() createdAt: Date;\n * undecorated: string; // Will not be serialized\n * }\n *\n * const user = new User();\n * user.name = 'John';\n * user.address = new Address();\n * user.address.street = '123 Main St';\n * user.address.city = 'Boston';\n * user.createdAt = new Date('2024-01-01');\n * user.undecorated = 'ignored';\n *\n * const json = EntityUtils.toJSON(user);\n * // {\n * // name: 'John',\n * // address: { street: '123 Main St', city: 'Boston' },\n * // createdAt: '2024-01-01T00:00:00.000Z'\n * // }\n *\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n *\n * const tags = new Tags({ collection: ['a', 'b'] });\n * const json = EntityUtils.toJSON(tags);\n * // ['a', 'b'] - unwrapped to array\n * ```\n */\n static toJSON<T extends object>(entity: T): unknown {\n if (this.isStringifiable(entity)) {\n const valuePropertyOptions = this.getPropertyOptions(entity, 'value');\n if (!valuePropertyOptions) {\n throw new Error(\n `Stringifiable entity 'value' property is missing metadata`,\n );\n }\n if (valuePropertyOptions.array) {\n throw new Error(\n `Stringifiable entity 'value' property must not be an array`,\n );\n }\n if (valuePropertyOptions.type?.() !== String) {\n throw new Error(\n `Stringifiable entity 'value' property must be of type String`,\n );\n }\n\n return this.serializeValue((entity as any).value, valuePropertyOptions);\n }\n\n if (this.isCollectionEntity(entity)) {\n const collectionPropertyOptions = this.getPropertyOptions(\n entity,\n 'collection',\n );\n if (!collectionPropertyOptions) {\n throw new Error(\n `Collection entity 'collection' property is missing metadata`,\n );\n }\n if (!collectionPropertyOptions.array) {\n throw new Error(\n `Collection entity 'collection' property must be an array`,\n );\n }\n\n return this.serializeValue(\n (entity as any).collection,\n collectionPropertyOptions,\n );\n }\n\n const result: Record<string, unknown> = {};\n const keys = this.getPropertyKeys(entity);\n\n for (const key of keys) {\n const value = (entity as any)[key];\n\n // Skip undefined values\n if (value === undefined) {\n continue;\n }\n\n const options = this.getPropertyOptions(entity, key);\n result[key] = this.serializeValue(value, options);\n }\n\n return result;\n }\n\n /**\n * Serializes a single value according to the toJSON rules\n * @private\n */\n private static serializeValue(\n value: unknown,\n options?: PropertyOptions,\n ): unknown {\n if (value === null) {\n return null;\n }\n\n if (value === undefined) {\n return undefined;\n }\n\n const passthrough = options?.passthrough === true;\n if (passthrough) {\n return value;\n }\n\n if (Array.isArray(value)) {\n if (options?.serialize) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return value.map((item) => options.serialize!(item as any));\n }\n return value.map((item) => this.serializeValue(item));\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 * Internal parse implementation with extended options\n * @private\n */\n private static async _parseInternal<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options: {\n strict?: boolean;\n skipDefaults?: boolean;\n skipMissing?: boolean;\n } = {},\n ): Promise<{ data: Record<string, unknown>; hardProblems: Problem[] }> {\n if (this.isStringifiable(entityClass)) {\n plainObject = { value: plainObject };\n } else if (this.isCollectionEntity(entityClass)) {\n plainObject = { collection: plainObject };\n }\n if (plainObject == null) {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n if (Array.isArray(plainObject)) {\n throw createValidationError(`Expects an object but received array`);\n }\n if (typeof plainObject !== 'object') {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n\n const strict = options.strict ?? false;\n const skipDefaults = options.skipDefaults ?? false;\n const skipMissing = options.skipMissing ?? false;\n const keys = this.getPropertyKeys(entityClass.prototype);\n const data: Record<string, unknown> = {};\n const hardProblems: Problem[] = [];\n\n for (const key of keys) {\n const propertyOptions = this.getPropertyOptions(\n entityClass.prototype,\n key,\n );\n\n if (!propertyOptions) {\n hardProblems.push(\n new Problem({\n property: key,\n message: `Property has no metadata. This should not happen if @Property() was used correctly.`,\n }),\n );\n continue;\n }\n\n const value = (plainObject as Record<string, unknown>)[key];\n\n if (propertyOptions.passthrough === true) {\n data[key] = value;\n continue;\n }\n\n const isOptional = propertyOptions.optional === true;\n\n if (!(key in plainObject) || value == null) {\n if (skipMissing) {\n continue;\n }\n\n let valueToSet = value;\n\n if (!skipDefaults && propertyOptions.default !== undefined) {\n valueToSet =\n typeof propertyOptions.default === 'function'\n ? await propertyOptions.default()\n : propertyOptions.default;\n }\n\n if (!isOptional && valueToSet == null) {\n hardProblems.push(\n new Problem({\n property: key,\n message:\n 'Required property is missing, null or undefined from input',\n }),\n );\n }\n data[key] = valueToSet;\n continue;\n }\n\n try {\n // Only pass strict to nested deserialization, not skipDefaults/skipMissing\n data[key] = await this.deserializeValue(value, propertyOptions, {\n strict,\n });\n } catch (error) {\n if (error instanceof ValidationError) {\n const problems = prependPropertyPath(key, error);\n hardProblems.push(...problems);\n } else if (error instanceof Error) {\n hardProblems.push(\n new Problem({\n property: key,\n message: error.message,\n }),\n );\n } else {\n throw error;\n }\n }\n }\n\n return { data, hardProblems };\n }\n\n /**\n * Deserializes a plain object to an entity instance\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param parseOptions - Parse options (strict mode)\n * @returns Promise resolving to a new instance of the entity with deserialized values\n *\n * @remarks\n * Deserialization rules:\n * - All @Property() decorators must include type metadata for parse() to work\n * - Properties without type metadata will throw an error\n * - Required properties (optional !== true) must be present and not null/undefined\n * - Optional properties (optional === true) can be undefined or null\n * - Arrays are supported with the array: true option\n * - Nested entities are recursively deserialized\n * - Type conversion is strict (no coercion)\n * - Entity constructors must accept a required data parameter\n *\n * Validation behavior:\n * - If strict: true - both HARD and SOFT problems throw ValidationError\n * - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored\n * - Property validators run first, then entity validators\n * - Validators can be synchronous or asynchronous\n * - Problems are accessible via EntityUtils.getProblems()\n * - Raw input data is accessible via EntityUtils.getRawInput()\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const json = { name: 'John', age: 30 };\n * const user = await EntityUtils.parse(User, json);\n * const userStrict = await EntityUtils.parse(User, json, { strict: true });\n * ```\n */\n static async parse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n parseOptions: ParseOptions = {},\n ): Promise<T> {\n const strict = parseOptions?.strict ?? false;\n\n const { data, hardProblems } = await this._parseInternal(\n entityClass,\n plainObject,\n { strict },\n );\n\n if (hardProblems.length > 0) {\n throw new ValidationError(hardProblems);\n }\n\n await this.addInjectedDependencies(data, entityClass.prototype);\n\n const instance = new entityClass(data);\n\n rawInputStorage.set(instance, plainObject as Record<string, unknown>);\n\n const problems = await this.validate(instance);\n\n if (problems.length > 0 && strict) {\n throw new ValidationError(problems);\n }\n\n return instance;\n }\n\n /**\n * Safely deserializes a plain object to an entity instance without throwing errors\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param parseOptions - Parse options (strict mode)\n * @returns Promise resolving to a result object with success flag, data, and problems\n *\n * @remarks\n * Similar to parse() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data, problems: [] }\n * - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)\n * - On failure - returns { success: false, data: undefined, problems: [...] }\n *\n * All deserialization and validation rules from parse() apply.\n * See parse() documentation for detailed deserialization behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const result = await EntityUtils.safeParse(User, { name: 'John', age: 30 });\n * if (result.success) {\n * console.log(result.data); // User instance\n * console.log(result.problems); // [] or soft problems if not strict\n * } else {\n * console.log(result.problems); // Hard problems\n * }\n * ```\n */\n static async safeParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n parseOptions?: ParseOptions,\n ): SafeOperationResult<T> {\n try {\n const data = await this.parse(entityClass, plainObject, parseOptions);\n const problems = this.getProblems(data);\n\n return {\n success: true,\n data,\n problems,\n };\n } catch (error) {\n if (error instanceof ValidationError) {\n return {\n success: false,\n data: undefined,\n problems: error.problems,\n };\n }\n throw error;\n }\n }\n\n /**\n * Partially deserializes a plain object, returning a plain object with only present properties\n *\n * @param entityClass - The entity class constructor\n * @param plainObject - The plain object to deserialize\n * @param options - Options with strict mode\n * @returns Promise resolving to a plain object with deserialized properties (Partial<T>)\n *\n * @remarks\n * Differences from parse():\n * - Returns a plain object, not an entity instance\n * - Ignores missing properties (does not include them in result)\n * - Does NOT apply default values to missing properties\n * - When strict: false (default), properties with HARD problems are excluded from result but problems are tracked\n * - When strict: true, any HARD problem throws ValidationError\n * - Nested entities/arrays are still fully deserialized and validated as normal\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number, default: 0 }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const partial = await EntityUtils.partialParse(User, { name: 'John' });\n * // partial = { name: 'John' } (age is not included, default not applied)\n *\n * const partialWithError = await EntityUtils.partialParse(User, { name: 'John', age: 'invalid' });\n * // partialWithError = { name: 'John' } (age excluded due to HARD problem)\n * // Access problems via second return value\n * ```\n */\n static async partialParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options: { strict?: boolean } = {},\n ): Promise<Partial<T>> {\n const result = await this.safePartialParse(\n entityClass,\n plainObject,\n options,\n );\n\n if (!result.success) {\n throw new ValidationError(result.problems);\n }\n\n return result.data;\n }\n\n /**\n * Safely performs partial deserialization without throwing errors\n *\n * @param entityClass - The entity class constructor\n * @param plainObject - The plain object to deserialize\n * @param options - Options with strict mode\n * @returns Promise resolving to a result object with success flag, partial data, and problems\n *\n * @remarks\n * Similar to partialParse() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data: Partial<T>, problems: [] }\n * - On success with strict: false - returns { success: true, data: Partial<T>, problems: [...] } (includes hard problems for excluded properties)\n * - On failure (strict mode only) - returns { success: false, data: undefined, problems: [...] }\n *\n * All partial deserialization rules from partialParse() apply.\n * See partialParse() documentation for detailed behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const result = await EntityUtils.safePartialParse(User, { name: 'John', age: 'invalid' });\n * if (result.success) {\n * console.log(result.data); // { name: 'John' }\n * console.log(result.problems); // [Problem for age property]\n * } else {\n * console.log(result.problems); // Hard problems (only in strict mode)\n * }\n * ```\n */\n static async safePartialParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options?: { strict?: boolean },\n ): Promise<SafeOperationResult<Partial<T>>> {\n const strict = options?.strict ?? false;\n\n const { data, hardProblems } = await this._parseInternal(\n entityClass,\n plainObject,\n { strict, skipDefaults: true, skipMissing: true },\n );\n\n if (strict && hardProblems.length > 0) {\n return {\n success: false,\n data: undefined,\n problems: hardProblems,\n };\n }\n\n const propertyProblems = await this.validateProperties(\n data,\n entityClass.prototype,\n );\n const validationProblems = [...hardProblems, ...propertyProblems];\n\n if (strict && propertyProblems.length > 0) {\n return {\n success: false,\n data: undefined,\n problems: validationProblems,\n };\n }\n\n this.setProblems(data, validationProblems);\n\n return {\n success: true,\n data: data as Partial<T>,\n problems: validationProblems,\n };\n }\n\n /**\n * Updates an entity instance with new values, respecting preventUpdates flags on properties\n *\n * @param instance - The entity instance to update. Must be an Entity.\n * @param updates - Partial object with properties to update\n * @param options - Update options (strict mode)\n * @returns Promise resolving to a new instance with updated values\n *\n * @remarks\n * Update behavior:\n * - Creates a shallow copy of the instance\n * - For each @Property(), copies the value from updates if it exists\n * - Properties with preventUpdates: true will not be copied from updates\n * - Runs entity validators after applying updates\n * - Throws ValidationError if validation fails and strict: true\n * - Soft problems are stored on the instance if strict: false (default)\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => String, preventUpdates: true }) id!: string;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const user = new User({ id: '123', name: 'John' });\n * const updated = await EntityUtils.update(user, { id: '456', name: 'Jane' });\n * // updated.id === '123' (not updated due to preventUpdates: true)\n * // updated.name === 'Jane'\n * ```\n */\n static async update<T extends object>(\n instance: T,\n updates: Partial<T>,\n options?: { strict?: boolean },\n ): Promise<T> {\n if (!this.isEntity(instance)) {\n throw new Error('Cannot update non-entity instance');\n }\n\n const strict = options?.strict ?? false;\n const Constructor = Object.getPrototypeOf(instance).constructor;\n const keys = this.getPropertyKeys(instance);\n const data: Record<string, unknown> = {};\n\n // Copy existing properties\n for (const key of keys) {\n const value = (instance as any)[key];\n data[key] = value;\n }\n\n // Apply updates, respecting preventUpdates flag\n for (const key of keys) {\n if (key in updates) {\n const propertyOptions = this.getPropertyOptions(instance, key);\n if (propertyOptions && propertyOptions.preventUpdates === true) {\n // Skip updating this property\n continue;\n }\n data[key] = (updates as any)[key];\n }\n }\n\n const newInstance = new Constructor(data);\n\n const problems = await this.validate(newInstance);\n\n if (problems.length > 0 && strict) {\n throw new ValidationError(problems);\n }\n\n return newInstance;\n }\n\n /**\n * Safely updates an entity instance without throwing errors\n *\n * @param instance - The entity instance to update. Must be an Entity.\n * @param updates - Partial object with properties to update\n * @param options - Update options (strict mode)\n * @returns Promise resolving to a result object with success flag, data, and problems\n *\n * @remarks\n * Similar to update() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data, problems: [] }\n * - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)\n * - On failure - returns { success: false, data: undefined, problems: [...] }\n *\n * All update and validation rules from update() apply.\n * See update() documentation for detailed update behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const user = new User({ name: 'John' });\n * const result = await EntityUtils.safeUpdate(user, { name: 'Jane' });\n * if (result.success) {\n * console.log(result.data); // Updated User instance\n * console.log(result.problems); // [] or soft problems if not strict\n * } else {\n * console.log(result.problems); // Hard problems\n * }\n * ```\n */\n static async safeUpdate<T extends object>(\n instance: T,\n updates: Partial<T>,\n options?: { strict?: boolean },\n ): SafeOperationResult<T> {\n try {\n const updatedInstance = await this.update(instance, updates, options);\n const problems = this.getProblems(updatedInstance);\n\n return {\n success: true,\n data: updatedInstance,\n problems,\n };\n } catch (error) {\n if (error instanceof ValidationError) {\n return {\n success: false,\n data: undefined,\n problems: error.problems,\n };\n }\n throw error;\n }\n }\n\n /**\n * Deserializes a single value according to the type metadata\n * @private\n */\n private static async deserializeValue(\n value: unknown,\n options: PropertyOptions,\n parseOptions: ParseOptions,\n ): Promise<unknown> {\n // 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 /**\n * Validates all properties on an object (entity instance or plain object)\n * @private\n */\n private static async validateProperties(\n dataOrInstance: Record<string, unknown> | object,\n prototype: object,\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n const keys = Object.keys(dataOrInstance);\n\n for (const key of keys) {\n const options = this.getPropertyOptions(prototype, key);\n if (options) {\n const value = (dataOrInstance as any)[key];\n if (value != null) {\n const validationProblems = await this.runPropertyValidators(\n key,\n value,\n options,\n );\n problems.push(...validationProblems);\n }\n }\n }\n\n return problems;\n }\n\n private static async addInjectedDependencies(\n data: Record<string, unknown>,\n prototype: object,\n ): Promise<void> {\n const injectedPropertyNames = getInjectedPropertyNames(prototype);\n if (injectedPropertyNames.length === 0) {\n return;\n }\n\n const injectedPropertyOptions = getInjectedPropertyOptions(prototype);\n\n for (const propertyName of injectedPropertyNames) {\n const token = injectedPropertyOptions[propertyName];\n if (token) {\n const dependency = await EntityDI.get(token);\n data[propertyName] = dependency;\n }\n }\n }\n\n /**\n * Validates an entity instance by running all property and entity validators\n *\n * @param instance - The entity instance to validate\n * @returns Promise resolving to array of Problems found during validation (empty if valid)\n *\n * @remarks\n * - Property validators run first, then entity validators\n * - Each validator can be synchronous or asynchronous\n * - Empty array means no problems found\n *\n * @example\n * ```typescript\n * const user = new User({ name: '', age: -5 });\n * const problems = await EntityUtils.validate(user);\n * console.log(problems); // [Problem, Problem, ...]\n * ```\n */\n static async validate<T extends object>(instance: T): Promise<Problem[]> {\n if (!this.isEntity(instance)) {\n throw new Error('Cannot validate non-entity instance');\n }\n\n const problems: Problem[] = [];\n\n const propertyProblems = await this.validateProperties(instance, instance);\n problems.push(...propertyProblems);\n\n const entityValidators = this.getEntityValidators(instance);\n for (const validatorMethod of entityValidators) {\n const validatorProblems = await (instance as any)[validatorMethod]();\n if (Array.isArray(validatorProblems)) {\n problems.push(...validatorProblems);\n }\n }\n\n EntityUtils.setProblems(instance, problems);\n\n return problems;\n }\n\n /**\n * Gets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @returns Array of Problems (empty if no problems or instance not parsed)\n *\n * @remarks\n * - Only returns problems from the last parse() call\n * - Returns empty array if instance was not created via parse()\n * - Returns empty array if parse() was called with strict: true\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, data);\n * const problems = EntityUtils.getProblems(user);\n * console.log(problems); // [Problem, ...]\n * ```\n */\n static getProblems<T extends object>(instance: T): Problem[] {\n return problemsStorage.get(instance) || [];\n }\n\n /**\n * Sets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @param problems - Array of Problems to associate with the instance\n *\n * @remarks\n * - Overwrites any existing problems for the instance\n * - Pass an empty array to clear problems\n *\n * @example\n * ```typescript\n * const user = new User({ name: 'John' });\n * EntityUtils.setProblems(user, [new Problem({ property: 'name', message: 'Invalid name' })]);\n * ```\n */\n static setProblems<T extends object>(instance: T, problems: Problem[]): void {\n if (problems.length === 0) {\n problemsStorage.delete(instance);\n } else {\n problemsStorage.set(instance, problems);\n }\n }\n\n /**\n * Gets the raw input data that was used to create an entity instance\n *\n * @param instance - The entity instance\n * @returns The raw input object, or undefined if not available\n *\n * @remarks\n * - Only available for instances created via parse()\n * - Returns a reference to the original input data (not a copy)\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, { name: 'John', age: 30 });\n * const rawInput = EntityUtils.getRawInput(user);\n * console.log(rawInput); // { name: 'John', age: 30 }\n * ```\n */\n static getRawInput<T extends object>(instance: T): unknown {\n return rawInputStorage.get(instance);\n }\n\n /**\n * Sets the raw input data for an entity instance\n *\n * @param instance - The entity instance\n * @param rawInput - The raw input object to associate with the instance\n *\n * @remarks\n * - Overwrites any existing raw input for the instance\n * - Pass undefined to clear the raw input\n *\n * @example\n * ```typescript\n * const user = new User({ name: 'John' });\n * EntityUtils.setRawInput(user, { name: 'John', age: 30 });\n * ```\n */\n static setRawInput<T extends object>(\n instance: T,\n rawInput: Record<string, unknown> | undefined,\n ): void {\n if (rawInput === undefined) {\n rawInputStorage.delete(instance);\n } else {\n rawInputStorage.set(instance, rawInput);\n }\n }\n\n /**\n * Gets all entity validator method names for an entity\n * @private\n */\n private static getEntityValidators(target: object): string[] {\n let currentProto: any;\n\n if (target.constructor && target === target.constructor.prototype) {\n currentProto = target;\n } else {\n currentProto = Object.getPrototypeOf(target);\n }\n\n const validators: string[] = [];\n const seen = new Set<string>();\n\n while (currentProto && currentProto !== Object.prototype) {\n const protoValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, currentProto) ||\n [];\n\n for (const validator of protoValidators) {\n if (!seen.has(validator)) {\n seen.add(validator);\n validators.push(validator);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return validators;\n }\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","getInjectedPropertyNames","getInjectedPropertyOptions","EntityDI","isEqualWith","ValidationError","Problem","prependPropertyPath","prependArrayIndex","createValidationError","combinePropertyPaths","isPrimitiveConstructor","deserializePrimitive","ok","problemsStorage","WeakMap","rawInputStorage","EntityUtils","isEntity","obj","Reflect","hasMetadata","Array","isArray","constructor","Object","getPrototypeOf","getEntityOptions","entityOrClass","options","getMetadata","isCollectionEntity","collection","isStringifiable","stringifiable","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","valuePropertyOptions","array","type","String","serializeValue","value","collectionPropertyOptions","result","passthrough","serialize","map","item","Date","toISOString","toString","_parseInternal","entityClass","plainObject","strict","skipDefaults","skipMissing","data","hardProblems","message","isOptional","optional","valueToSet","default","deserializeValue","error","problems","parse","parseOptions","addInjectedDependencies","instance","set","validate","safeParse","getProblems","success","partialParse","safePartialParse","propertyProblems","validateProperties","validationProblems","setProblems","update","updates","Constructor","preventUpdates","newInstance","safeUpdate","updatedInstance","typeConstructor","isSparse","sparse","arrayProblems","index","deserialize","deserializeSingleValue","validatePropertyValue","propertyPath","validators","validator","validatorProblems","problem","existingProblems","get","nestedProblems","prependedProblems","runPropertyValidators","isPassthrough","valueProblems","arrayValidators","i","element","elementPath","elementProblems","dataOrInstance","injectedPropertyNames","injectedPropertyOptions","propertyName","token","dependency","entityValidators","getEntityValidators","validatorMethod","delete","getRawInput","setRawInput","rawInput","protoValidators"],"mappings":"AAAA,6DAA6D,GAC7D,qDAAqD,GACrD,SACEA,mBAAmB,EACnBC,2BAA2B,EAC3BC,6BAA6B,EAE7BC,qBAAqB,EACrBC,6BAA6B,QAGxB,aAAa;AAEpB,SACEC,wBAAwB,EACxBC,0BAA0B,QACrB,yBAAyB;AAChC,SAASC,QAAQ,QAAQ,iBAAiB;AAC1C,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,eAAe,QAAQ,wBAAwB;AACxD,SAASC,OAAO,QAAQ,eAAe;AACvC,SACEC,mBAAmB,EACnBC,iBAAiB,EACjBC,qBAAqB,EACrBC,oBAAoB,QACf,wBAAwB;AAC/B,SACEC,sBAAsB,EACtBC,oBAAoB,QACf,+BAA+B;AACtC,SAASC,EAAE,QAAQ,SAAS;AAE5B;;CAEC,GACD,MAAMC,kBAAkB,IAAIC;AAE5B;;CAEC,GACD,MAAMC,kBAAkB,IAAID;AAE5B,OAAO,MAAME;IACX;;;;;;;;;;;;;;;;;;;GAmBC,GACD,OAAOC,SAASC,GAAY,EAAiB;QAC3C,IAAIA,OAAO,MAAM;YACf,OAAO;QACT;QAEA,iDAAiD;QACjD,IAAI,OAAOA,QAAQ,YAAY;YAC7B,OAAOC,QAAQC,WAAW,CAACzB,qBAAqBuB;QAClD;QAEA,qCAAqC;QACrC,IAAI,OAAOA,QAAQ,YAAYG,MAAMC,OAAO,CAACJ,MAAM;YACjD,OAAO;QACT;QAEA,MAAMK,cAAcC,OAAOC,cAAc,CAACP,KAAK,WAAW;QAC1D,OAAOC,QAAQC,WAAW,CAACzB,qBAAqB4B;IAClD;IAEA;;;;;;GAMC,GACD,OAAeG,iBAAiBC,aAAsB,EAAiB;QACrE,MAAMJ,cACJ,OAAOI,kBAAkB,aACrBA,gBACAH,OAAOC,cAAc,CAACE,eAAe,WAAW;QAEtD,MAAMC,UAAqCT,QAAQU,WAAW,CAC5DjC,6BACA2B;QAEF,OAAOK,WAAW,CAAC;IACrB;IAEA;;;;;;;;;;;;;;;;;;GAkBC,GACD,OAAOE,mBAAmBH,aAAsB,EAAW;QACzD,IAAI,CAAC,IAAI,CAACV,QAAQ,CAACU,gBAAgB;YACjC,OAAO;QACT;QAEA,MAAMC,UAAU,IAAI,CAACF,gBAAgB,CAACC;QAEtC,OAAOC,QAAQG,UAAU,KAAK;IAChC;IAEA;;;;;;;;;;;;;;;;;;GAkBC,GACD,OAAOC,gBAAgBL,aAAsB,EAAW;QACtD,IAAI,CAAC,IAAI,CAACV,QAAQ,CAACU,gBAAgB;YACjC,OAAO;QACT;QAEA,MAAMC,UAAU,IAAI,CAACF,gBAAgB,CAACC;QAEtC,OAAOC,QAAQK,aAAa,KAAK;IACnC;IAEA,OAAOC,WAAWC,CAAS,EAAEC,CAAS,EAAW;QAC/C,IAAI,CAAC,IAAI,CAACnB,QAAQ,CAACkB,MAAM,CAAC,IAAI,CAAClB,QAAQ,CAACmB,IAAI;YAC1C,OAAO;QACT;QAEA,OAAOZ,OAAOC,cAAc,CAACU,OAAOX,OAAOC,cAAc,CAACW;IAC5D;IAEA,OAAOC,gBAAgBC,MAAc,EAAY;QAC/C,6DAA6D;QAC7D,IAAIC;QAEJ,8EAA8E;QAC9E,iDAAiD;QACjD,IAAID,OAAO,WAAW,IAAIA,WAAWA,OAAO,WAAW,CAACE,SAAS,EAAE;YACjE,gCAAgC;YAChCD,eAAeD;QACjB,OAAO;YACL,2CAA2C;YAC3CC,eAAef,OAAOC,cAAc,CAACa;QACvC;QAEA,MAAMG,OAAiB,EAAE;QACzB,MAAMC,OAAO,IAAIC;QAEjB,+DAA+D;QAC/D,MAAOJ,gBAAgBA,iBAAiBf,OAAOgB,SAAS,CAAE;YACxD,qEAAqE;YACrE,MAAMI,YACJzB,QAAQ0B,cAAc,CAAC/C,uBAAuByC,iBAAiB,EAAE;YAEnE,KAAK,MAAMO,OAAOF,UAAW;gBAC3B,IAAI,CAACF,KAAKK,GAAG,CAACD,MAAM;oBAClBJ,KAAKM,GAAG,CAACF;oBACTL,KAAKQ,IAAI,CAACH;gBACZ;YACF;YAEAP,eAAef,OAAOC,cAAc,CAACc;QACvC;QAEA,OAAOE;IACT;IAEA,OAAOS,mBACLZ,MAAc,EACda,WAAmB,EACU;QAC7B,6DAA6D;QAC7D,IAAIZ;QAEJ,8EAA8E;QAC9E,iDAAiD;QACjD,IAAID,OAAO,WAAW,IAAIA,WAAWA,OAAO,WAAW,CAACE,SAAS,EAAE;YACjE,gCAAgC;YAChCD,eAAeD;QACjB,OAAO;YACL,2CAA2C;YAC3CC,eAAef,OAAOC,cAAc,CAACa;QACvC;QAEA,wDAAwD;QACxD,MAAOC,gBAAgBA,iBAAiBf,OAAOgB,SAAS,CAAE;YACxD,MAAMY,eACJjC,QAAQ0B,cAAc,CAAC9C,+BAA+BwC,iBACtD,CAAC;YAEH,IAAIa,YAAY,CAACD,YAAY,EAAE;gBAC7B,OAAOC,YAAY,CAACD,YAAY;YAClC;YAEAZ,eAAef,OAAOC,cAAc,CAACc;QACvC;QAEA,OAAOc;IACT;IAEA,OAAOC,OAAOnB,CAAU,EAAEC,CAAU,EAAW;QAC7C,OAAOjC,YAAYgC,GAAGC,GAAG,CAACmB,MAAMC;YAC9B,IAAI,IAAI,CAACvC,QAAQ,CAACsC,OAAO;gBACvB,IAAI,CAAC,IAAI,CAACrB,UAAU,CAACqB,MAAMC,OAAO;oBAChC,OAAO;gBACT;gBAEA,MAAMC,OAAO,IAAI,CAACA,IAAI,CAACF,MAAMC;gBAE7B,OAAOC,KAAKC,MAAM,KAAK;YACzB,OAAO,IACLH,QAAQ,QACRC,QAAQ,QACR,OAAOD,SAAS,YAChB,CAAClC,MAAMC,OAAO,CAACiC,SACf,OAAOC,SAAS,YAChB,CAACnC,MAAMC,OAAO,CAACkC,SACf,YAAYD,QACZ,OAAOA,KAAKD,MAAM,KAAK,YACvB;gBACA,OAAOC,KAAKD,MAAM,CAACE;YACrB;YAEA,OAAOH;QACT;IACF;IAEA,OAAOI,KACLE,SAAY,EACZC,SAAY,EACkD;QAC9D,IAAI,CAAC,IAAI,CAAC1B,UAAU,CAACyB,WAAWC,YAAY;YAC1C,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAMC,QACJ,EAAE;QAEJ,MAAMrB,OAAO,IAAI,CAACJ,eAAe,CAACsB;QAElC,KAAK,MAAMb,OAAOL,KAAM;YACtB,MAAMsB,WAAW,AAACJ,SAAiB,CAACb,IAAI;YACxC,MAAMkB,WAAW,AAACJ,SAAiB,CAACd,IAAI;YAExC,8DAA8D;YAC9D,MAAMmB,kBAAkB,IAAI,CAACf,kBAAkB,CAACS,WAAWb;YAE3D,IAAIoB;YACJ,IAAIH,YAAY,QAAQC,YAAY,MAAM;gBACxCE,WAAWH,aAAaC;YAC1B,OAAO,IAAID,YAAY,QAAQC,YAAY,MAAM;gBAC/CE,WAAW;YACb,OAAO;gBACLA,WAAWD,iBAAiBX,SACxBW,gBAAgBX,MAAM,CAACS,UAAUC,YACjC,IAAI,CAACV,MAAM,CAACS,UAAUC;YAC5B;YAEA,IAAI,CAACE,UAAU;gBACbJ,MAAMb,IAAI,CAAC;oBAAEkB,UAAUrB;oBAAKiB;oBAAUC;gBAAS;YACjD;QACF;QAEA,OAAOF;IACT;IAEA,OAAOM,QAA0BT,SAAY,EAAEC,SAAY,EAAc;QACvE,IAAI,CAAC,IAAI,CAAC1B,UAAU,CAACyB,WAAWC,YAAY;YAC1C,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAMJ,OAAO,IAAI,CAACA,IAAI,CAACE,WAAWC;QAElC,OAAOH,KAAKY,MAAM,CAAC,CAACC,KAAK,EAAEH,QAAQ,EAAEH,QAAQ,EAAE;YAC5CM,GAAW,CAACH,SAAS,GAAGH;YACzB,OAAOM;QACT,GAAG,CAAC;IACN;IAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DC,GACD,OAAOC,OAAyBC,MAAS,EAAW;QAClD,IAAI,IAAI,CAACxC,eAAe,CAACwC,SAAS;YAChC,MAAMC,uBAAuB,IAAI,CAACvB,kBAAkB,CAACsB,QAAQ;YAC7D,IAAI,CAACC,sBAAsB;gBACzB,MAAM,IAAIZ,MACR,CAAC,yDAAyD,CAAC;YAE/D;YACA,IAAIY,qBAAqBC,KAAK,EAAE;gBAC9B,MAAM,IAAIb,MACR,CAAC,0DAA0D,CAAC;YAEhE;YACA,IAAIY,qBAAqBE,IAAI,SAASC,QAAQ;gBAC5C,MAAM,IAAIf,MACR,CAAC,4DAA4D,CAAC;YAElE;YAEA,OAAO,IAAI,CAACgB,cAAc,CAAC,AAACL,OAAeM,KAAK,EAAEL;QACpD;QAEA,IAAI,IAAI,CAAC3C,kBAAkB,CAAC0C,SAAS;YACnC,MAAMO,4BAA4B,IAAI,CAAC7B,kBAAkB,CACvDsB,QACA;YAEF,IAAI,CAACO,2BAA2B;gBAC9B,MAAM,IAAIlB,MACR,CAAC,2DAA2D,CAAC;YAEjE;YACA,IAAI,CAACkB,0BAA0BL,KAAK,EAAE;gBACpC,MAAM,IAAIb,MACR,CAAC,wDAAwD,CAAC;YAE9D;YAEA,OAAO,IAAI,CAACgB,cAAc,CACxB,AAACL,OAAezC,UAAU,EAC1BgD;QAEJ;QAEA,MAAMC,SAAkC,CAAC;QACzC,MAAMvC,OAAO,IAAI,CAACJ,eAAe,CAACmC;QAElC,KAAK,MAAM1B,OAAOL,KAAM;YACtB,MAAMqC,QAAQ,AAACN,MAAc,CAAC1B,IAAI;YAElC,wBAAwB;YACxB,IAAIgC,UAAUzB,WAAW;gBACvB;YACF;YAEA,MAAMzB,UAAU,IAAI,CAACsB,kBAAkB,CAACsB,QAAQ1B;YAChDkC,MAAM,CAAClC,IAAI,GAAG,IAAI,CAAC+B,cAAc,CAACC,OAAOlD;QAC3C;QAEA,OAAOoD;IACT;IAEA;;;GAGC,GACD,OAAeH,eACbC,KAAc,EACdlD,OAAyB,EAChB;QACT,IAAIkD,UAAU,MAAM;YAClB,OAAO;QACT;QAEA,IAAIA,UAAUzB,WAAW;YACvB,OAAOA;QACT;QAEA,MAAM4B,cAAcrD,SAASqD,gBAAgB;QAC7C,IAAIA,aAAa;YACf,OAAOH;QACT;QAEA,IAAIzD,MAAMC,OAAO,CAACwD,QAAQ;YACxB,IAAIlD,SAASsD,WAAW;gBACtB,oEAAoE;gBACpE,OAAOJ,MAAMK,GAAG,CAAC,CAACC,OAASxD,QAAQsD,SAAS,CAAEE;YAChD;YACA,OAAON,MAAMK,GAAG,CAAC,CAACC,OAAS,IAAI,CAACP,cAAc,CAACO;QACjD;QAEA,IAAIxD,SAASsD,WAAW;YACtB,OAAOtD,QAAQsD,SAAS,CAACJ;QAC3B;QAEA,IAAIA,iBAAiBO,MAAM;YACzB,OAAOP,MAAMQ,WAAW;QAC1B;QAEA,IAAI,OAAOR,UAAU,UAAU;YAC7B,OAAOA,MAAMS,QAAQ;QACvB;QAEA,IAAI,IAAI,CAACtE,QAAQ,CAAC6D,QAAQ;YACxB,OAAO,IAAI,CAACP,MAAM,CAACO;QACrB;QAEA,IACE,OAAOA,UAAU,YACjB,OAAOA,UAAU,YACjB,OAAOA,UAAU,WACjB;YACA,OAAOA;QACT;QAEA,MAAM,IAAIjB,MACR,CAAC,gCAAgC,EAAE,OAAOiB,MAAM,2FAA2F,CAAC;IAEhJ;IAEA;;;GAGC,GACD,aAAqBU,eACnBC,WAAiC,EACjCC,WAAoB,EACpB9D,UAII,CAAC,CAAC,EAC+D;QACrE,IAAI,IAAI,CAACI,eAAe,CAACyD,cAAc;YACrCC,cAAc;gBAAEZ,OAAOY;YAAY;QACrC,OAAO,IAAI,IAAI,CAAC5D,kBAAkB,CAAC2D,cAAc;YAC/CC,cAAc;gBAAE3D,YAAY2D;YAAY;QAC1C;QACA,IAAIA,eAAe,MAAM;YACvB,MAAMlF,sBACJ,CAAC,+BAA+B,EAAE,OAAOkF,aAAa;QAE1D;QACA,IAAIrE,MAAMC,OAAO,CAACoE,cAAc;YAC9B,MAAMlF,sBAAsB,CAAC,oCAAoC,CAAC;QACpE;QACA,IAAI,OAAOkF,gBAAgB,UAAU;YACnC,MAAMlF,sBACJ,CAAC,+BAA+B,EAAE,OAAOkF,aAAa;QAE1D;QAEA,MAAMC,SAAS/D,QAAQ+D,MAAM,IAAI;QACjC,MAAMC,eAAehE,QAAQgE,YAAY,IAAI;QAC7C,MAAMC,cAAcjE,QAAQiE,WAAW,IAAI;QAC3C,MAAMpD,OAAO,IAAI,CAACJ,eAAe,CAACoD,YAAYjD,SAAS;QACvD,MAAMsD,OAAgC,CAAC;QACvC,MAAMC,eAA0B,EAAE;QAElC,KAAK,MAAMjD,OAAOL,KAAM;YACtB,MAAMwB,kBAAkB,IAAI,CAACf,kBAAkB,CAC7CuC,YAAYjD,SAAS,EACrBM;YAGF,IAAI,CAACmB,iBAAiB;gBACpB8B,aAAa9C,IAAI,CACf,IAAI5C,QAAQ;oBACV8D,UAAUrB;oBACVkD,SAAS,CAAC,mFAAmF,CAAC;gBAChG;gBAEF;YACF;YAEA,MAAMlB,QAAQ,AAACY,WAAuC,CAAC5C,IAAI;YAE3D,IAAImB,gBAAgBgB,WAAW,KAAK,MAAM;gBACxCa,IAAI,CAAChD,IAAI,GAAGgC;gBACZ;YACF;YAEA,MAAMmB,aAAahC,gBAAgBiC,QAAQ,KAAK;YAEhD,IAAI,CAAEpD,CAAAA,OAAO4C,WAAU,KAAMZ,SAAS,MAAM;gBAC1C,IAAIe,aAAa;oBACf;gBACF;gBAEA,IAAIM,aAAarB;gBAEjB,IAAI,CAACc,gBAAgB3B,gBAAgBmC,OAAO,KAAK/C,WAAW;oBAC1D8C,aACE,OAAOlC,gBAAgBmC,OAAO,KAAK,aAC/B,MAAMnC,gBAAgBmC,OAAO,KAC7BnC,gBAAgBmC,OAAO;gBAC/B;gBAEA,IAAI,CAACH,cAAcE,cAAc,MAAM;oBACrCJ,aAAa9C,IAAI,CACf,IAAI5C,QAAQ;wBACV8D,UAAUrB;wBACVkD,SACE;oBACJ;gBAEJ;gBACAF,IAAI,CAAChD,IAAI,GAAGqD;gBACZ;YACF;YAEA,IAAI;gBACF,2EAA2E;gBAC3EL,IAAI,CAAChD,IAAI,GAAG,MAAM,IAAI,CAACuD,gBAAgB,CAACvB,OAAOb,iBAAiB;oBAC9D0B;gBACF;YACF,EAAE,OAAOW,OAAO;gBACd,IAAIA,iBAAiBlG,iBAAiB;oBACpC,MAAMmG,WAAWjG,oBAAoBwC,KAAKwD;oBAC1CP,aAAa9C,IAAI,IAAIsD;gBACvB,OAAO,IAAID,iBAAiBzC,OAAO;oBACjCkC,aAAa9C,IAAI,CACf,IAAI5C,QAAQ;wBACV8D,UAAUrB;wBACVkD,SAASM,MAAMN,OAAO;oBACxB;gBAEJ,OAAO;oBACL,MAAMM;gBACR;YACF;QACF;QAEA,OAAO;YAAER;YAAMC;QAAa;IAC9B;IAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CC,GACD,aAAaS,MACXf,WAAiC,EACjCC,WAAoB,EACpBe,eAA6B,CAAC,CAAC,EACnB;QACZ,MAAMd,SAASc,cAAcd,UAAU;QAEvC,MAAM,EAAEG,IAAI,EAAEC,YAAY,EAAE,GAAG,MAAM,IAAI,CAACP,cAAc,CACtDC,aACAC,aACA;YAAEC;QAAO;QAGX,IAAII,aAAarC,MAAM,GAAG,GAAG;YAC3B,MAAM,IAAItD,gBAAgB2F;QAC5B;QAEA,MAAM,IAAI,CAACW,uBAAuB,CAACZ,MAAML,YAAYjD,SAAS;QAE9D,MAAMmE,WAAW,IAAIlB,YAAYK;QAEjC/E,gBAAgB6F,GAAG,CAACD,UAAUjB;QAE9B,MAAMa,WAAW,MAAM,IAAI,CAACM,QAAQ,CAACF;QAErC,IAAIJ,SAAS7C,MAAM,GAAG,KAAKiC,QAAQ;YACjC,MAAM,IAAIvF,gBAAgBmG;QAC5B;QAEA,OAAOI;IACT;IAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCC,GACD,aAAaG,UACXrB,WAAiC,EACjCC,WAAoB,EACpBe,YAA2B,EACH;QACxB,IAAI;YACF,MAAMX,OAAO,MAAM,IAAI,CAACU,KAAK,CAACf,aAAaC,aAAae;YACxD,MAAMF,WAAW,IAAI,CAACQ,WAAW,CAACjB;YAElC,OAAO;gBACLkB,SAAS;gBACTlB;gBACAS;YACF;QACF,EAAE,OAAOD,OAAO;YACd,IAAIA,iBAAiBlG,iBAAiB;gBACpC,OAAO;oBACL4G,SAAS;oBACTlB,MAAMzC;oBACNkD,UAAUD,MAAMC,QAAQ;gBAC1B;YACF;YACA,MAAMD;QACR;IACF;IAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCC,GACD,aAAaW,aACXxB,WAAiC,EACjCC,WAAoB,EACpB9D,UAAgC,CAAC,CAAC,EACb;QACrB,MAAMoD,SAAS,MAAM,IAAI,CAACkC,gBAAgB,CACxCzB,aACAC,aACA9D;QAGF,IAAI,CAACoD,OAAOgC,OAAO,EAAE;YACnB,MAAM,IAAI5G,gBAAgB4E,OAAOuB,QAAQ;QAC3C;QAEA,OAAOvB,OAAOc,IAAI;IACpB;IAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCC,GACD,aAAaoB,iBACXzB,WAAiC,EACjCC,WAAoB,EACpB9D,OAA8B,EACY;QAC1C,MAAM+D,SAAS/D,SAAS+D,UAAU;QAElC,MAAM,EAAEG,IAAI,EAAEC,YAAY,EAAE,GAAG,MAAM,IAAI,CAACP,cAAc,CACtDC,aACAC,aACA;YAAEC;YAAQC,cAAc;YAAMC,aAAa;QAAK;QAGlD,IAAIF,UAAUI,aAAarC,MAAM,GAAG,GAAG;YACrC,OAAO;gBACLsD,SAAS;gBACTlB,MAAMzC;gBACNkD,UAAUR;YACZ;QACF;QAEA,MAAMoB,mBAAmB,MAAM,IAAI,CAACC,kBAAkB,CACpDtB,MACAL,YAAYjD,SAAS;QAEvB,MAAM6E,qBAAqB;eAAItB;eAAiBoB;SAAiB;QAEjE,IAAIxB,UAAUwB,iBAAiBzD,MAAM,GAAG,GAAG;YACzC,OAAO;gBACLsD,SAAS;gBACTlB,MAAMzC;gBACNkD,UAAUc;YACZ;QACF;QAEA,IAAI,CAACC,WAAW,CAACxB,MAAMuB;QAEvB,OAAO;YACLL,SAAS;YACTlB,MAAMA;YACNS,UAAUc;QACZ;IACF;IAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCC,GACD,aAAaE,OACXZ,QAAW,EACXa,OAAmB,EACnB5F,OAA8B,EAClB;QACZ,IAAI,CAAC,IAAI,CAACX,QAAQ,CAAC0F,WAAW;YAC5B,MAAM,IAAI9C,MAAM;QAClB;QAEA,MAAM8B,SAAS/D,SAAS+D,UAAU;QAClC,MAAM8B,cAAcjG,OAAOC,cAAc,CAACkF,UAAU,WAAW;QAC/D,MAAMlE,OAAO,IAAI,CAACJ,eAAe,CAACsE;QAClC,MAAMb,OAAgC,CAAC;QAEvC,2BAA2B;QAC3B,KAAK,MAAMhD,OAAOL,KAAM;YACtB,MAAMqC,QAAQ,AAAC6B,QAAgB,CAAC7D,IAAI;YACpCgD,IAAI,CAAChD,IAAI,GAAGgC;QACd;QAEA,gDAAgD;QAChD,KAAK,MAAMhC,OAAOL,KAAM;YACtB,IAAIK,OAAO0E,SAAS;gBAClB,MAAMvD,kBAAkB,IAAI,CAACf,kBAAkB,CAACyD,UAAU7D;gBAC1D,IAAImB,mBAAmBA,gBAAgByD,cAAc,KAAK,MAAM;oBAE9D;gBACF;gBACA5B,IAAI,CAAChD,IAAI,GAAG,AAAC0E,OAAe,CAAC1E,IAAI;YACnC;QACF;QAEA,MAAM6E,cAAc,IAAIF,YAAY3B;QAEpC,MAAMS,WAAW,MAAM,IAAI,CAACM,QAAQ,CAACc;QAErC,IAAIpB,SAAS7C,MAAM,GAAG,KAAKiC,QAAQ;YACjC,MAAM,IAAIvF,gBAAgBmG;QAC5B;QAEA,OAAOoB;IACT;IAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCC,GACD,aAAaC,WACXjB,QAAW,EACXa,OAAmB,EACnB5F,OAA8B,EACN;QACxB,IAAI;YACF,MAAMiG,kBAAkB,MAAM,IAAI,CAACN,MAAM,CAACZ,UAAUa,SAAS5F;YAC7D,MAAM2E,WAAW,IAAI,CAACQ,WAAW,CAACc;YAElC,OAAO;gBACLb,SAAS;gBACTlB,MAAM+B;gBACNtB;YACF;QACF,EAAE,OAAOD,OAAO;YACd,IAAIA,iBAAiBlG,iBAAiB;gBACpC,OAAO;oBACL4G,SAAS;oBACTlB,MAAMzC;oBACNkD,UAAUD,MAAMC,QAAQ;gBAC1B;YACF;YACA,MAAMD;QACR;IACF;IAEA;;;GAGC,GACD,aAAqBD,iBACnBvB,KAAc,EACdlD,OAAwB,EACxB6E,YAA0B,EACR;QAClB,oEAAoE;QACpE,MAAMqB,kBAAkBlG,QAAQ+C,IAAI;QACpC,MAAMrD,UAAUM,QAAQ8C,KAAK,KAAK;QAClC,MAAMqD,WAAWnG,QAAQoG,MAAM,KAAK;QAEpC,IAAI1G,SAAS;YACX,IAAI,CAACD,MAAMC,OAAO,CAACwD,QAAQ;gBACzB,MAAMtE,sBACJ,CAAC,8BAA8B,EAAE,OAAOsE,OAAO;YAEnD;YAEA,MAAMmD,gBAA2B,EAAE;YACnC,MAAMjD,SAAoB,EAAE;YAE5B,IAAK,IAAIkD,QAAQ,GAAGA,QAAQpD,MAAMpB,MAAM,EAAEwE,QAAS;gBACjD,MAAM9C,OAAON,KAAK,CAACoD,MAAM;gBACzB,IAAI9C,SAAS,QAAQA,SAAS/B,WAAW;oBACvC,IAAI,CAAC0E,UAAU;wBACbE,cAAchF,IAAI,CAChB,IAAI5C,QAAQ;4BACV8D,UAAU,CAAC,CAAC,EAAE+D,MAAM,CAAC,CAAC;4BACtBlC,SAAS;wBACX;oBAEJ;oBACAhB,OAAO/B,IAAI,CAACmC;gBACd,OAAO;oBACL,IAAI;wBACF,IAAIxD,QAAQuG,WAAW,EAAE;4BACvBnD,OAAO/B,IAAI,CAACrB,QAAQuG,WAAW,CAAC/C;wBAClC,OAAO;4BACLJ,OAAO/B,IAAI,CACT,MAAM,IAAI,CAACmF,sBAAsB,CAC/BhD,MACA0C,iBACArB;wBAGN;oBACF,EAAE,OAAOH,OAAO;wBACd,IAAIA,iBAAiBlG,iBAAiB;4BACpC,MAAMmG,WAAWhG,kBAAkB2H,OAAO5B;4BAC1C2B,cAAchF,IAAI,IAAIsD;wBACxB,OAAO;4BACL,MAAMD;wBACR;oBACF;gBACF;YACF;YAEA,IAAI2B,cAAcvE,MAAM,GAAG,GAAG;gBAC5B,MAAM,IAAItD,gBAAgB6H;YAC5B;YAEA,OAAOjD;QACT;QAEA,IAAIpD,QAAQuG,WAAW,EAAE;YACvB,OAAOvG,QAAQuG,WAAW,CAACrD;QAC7B;QAEA,OAAO,MAAM,IAAI,CAACsD,sBAAsB,CACtCtD,OACAgD,iBACArB;IAEJ;IAEA;;;;GAIC,GACD,aAAqB2B,uBACnBtD,KAAc,EACdgD,eAAoB,EACpBrB,YAA0B,EACR;QAClB,IAAI/F,uBAAuBoH,kBAAkB;YAC3C,OAAOnH,qBAAqBmE,OAAOgD;QACrC;QAEA,IAAI,IAAI,CAAC7G,QAAQ,CAAC6G,kBAAkB;YAClC,OAAO,MAAM,IAAI,CAACtB,KAAK,CACrBsB,iBACAhD,OACA2B;QAEJ;QAEA,MAAMjG,sBACJ,CAAC,yKAAyK,CAAC;IAE/K;IAEA;;;;GAIC,GACD,aAAqB6H,sBACnBC,YAAoB,EACpBxD,KAAc,EACdyD,UAAyC,EACrB;QACpB,MAAMhC,WAAsB,EAAE;QAE9B,IAAIgC,YAAY;YACd,KAAK,MAAMC,aAAaD,WAAY;gBAClC,MAAME,oBAAoB,MAAMD,UAAU;oBAAE1D;gBAAM;gBAClD,uCAAuC;gBACvC,KAAK,MAAM4D,WAAWD,kBAAmB;oBACvClC,SAAStD,IAAI,CACX,IAAI5C,QAAQ;wBACV8D,UAAU1D,qBAAqB6H,cAAcI,QAAQvE,QAAQ;wBAC7D6B,SAAS0C,QAAQ1C,OAAO;oBAC1B;gBAEJ;YACF;QACF;QAEA,IAAIhF,YAAYC,QAAQ,CAAC6D,QAAQ;YAC/B,MAAM6D,mBAAmB9H,gBAAgB+H,GAAG,CAAC9D;YAC7C,MAAM+D,iBACJF,oBAAoBA,iBAAiBjF,MAAM,GAAG,IAC1CiF,mBACA,MAAM3H,YAAY6F,QAAQ,CAAC/B;YAEjC,MAAMgE,oBAAoBxI,oBACxBgI,cACA,IAAIlI,gBAAgByI;YAEtBtC,SAAStD,IAAI,IAAI6F;QACnB;QAEA,OAAOvC;IACT;IAEA;;;GAGC,GACD,aAAqBwC,sBACnBjG,GAAW,EACXgC,KAAc,EACdlD,OAAwB,EACJ;QACpB,MAAM2E,WAAsB,EAAE;QAC9B,MAAMjF,UAAUM,SAAS8C,UAAU;QACnC,MAAMsE,gBAAgBpH,SAASqD,gBAAgB;QAE/C,IAAI+D,iBAAiB,CAAC1H,SAAS;YAC7B,MAAM2H,gBAAgB,MAAM,IAAI,CAACZ,qBAAqB,CACpDvF,KACAgC,OACAlD,QAAQ2G,UAAU;YAEpBhC,SAAStD,IAAI,IAAIgG;QACnB,OAAO;YACLrI,GAAGS,MAAMC,OAAO,CAACwD,QAAQ;YAEzB,MAAMoE,kBAAkBtH,QAAQsH,eAAe,IAAI,EAAE;YACrD,KAAK,MAAMV,aAAaU,gBAAiB;gBACvC,MAAMT,oBAAoB,MAAMD,UAAU;oBAAE1D;gBAAM;gBAClD,KAAK,MAAM4D,WAAWD,kBAAmB;oBACvClC,SAAStD,IAAI,CACX,IAAI5C,QAAQ;wBACV8D,UAAU1D,qBAAqBqC,KAAK4F,QAAQvE,QAAQ;wBACpD6B,SAAS0C,QAAQ1C,OAAO;oBAC1B;gBAEJ;YACF;YAEA,MAAMuC,aAAa3G,QAAQ2G,UAAU,IAAI,EAAE;YAC3C,IAAIA,WAAW7E,MAAM,GAAG,GAAG;gBACzB,IAAK,IAAIyF,IAAI,GAAGA,IAAIrE,MAAMpB,MAAM,EAAEyF,IAAK;oBACrC,MAAMC,UAAUtE,KAAK,CAACqE,EAAE;oBACxB,IAAIC,YAAY,QAAQA,YAAY/F,WAAW;wBAC7C,MAAMgG,cAAc,GAAGvG,IAAI,CAAC,EAAEqG,EAAE,CAAC,CAAC;wBAClC,MAAMG,kBAAkB,MAAM,IAAI,CAACjB,qBAAqB,CACtDgB,aACAD,SACAb;wBAEFhC,SAAStD,IAAI,IAAIqG;oBACnB;gBACF;YACF;QACF;QAEA,OAAO/C;IACT;IAEA;;;GAGC,GACD,aAAqBa,mBACnBmC,cAAgD,EAChD/G,SAAiB,EACG;QACpB,MAAM+D,WAAsB,EAAE;QAC9B,MAAM9D,OAAOjB,OAAOiB,IAAI,CAAC8G;QAEzB,KAAK,MAAMzG,OAAOL,KAAM;YACtB,MAAMb,UAAU,IAAI,CAACsB,kBAAkB,CAACV,WAAWM;YACnD,IAAIlB,SAAS;gBACX,MAAMkD,QAAQ,AAACyE,cAAsB,CAACzG,IAAI;gBAC1C,IAAIgC,SAAS,MAAM;oBACjB,MAAMuC,qBAAqB,MAAM,IAAI,CAAC0B,qBAAqB,CACzDjG,KACAgC,OACAlD;oBAEF2E,SAAStD,IAAI,IAAIoE;gBACnB;YACF;QACF;QAEA,OAAOd;IACT;IAEA,aAAqBG,wBACnBZ,IAA6B,EAC7BtD,SAAiB,EACF;QACf,MAAMgH,wBAAwBxJ,yBAAyBwC;QACvD,IAAIgH,sBAAsB9F,MAAM,KAAK,GAAG;YACtC;QACF;QAEA,MAAM+F,0BAA0BxJ,2BAA2BuC;QAE3D,KAAK,MAAMkH,gBAAgBF,sBAAuB;YAChD,MAAMG,QAAQF,uBAAuB,CAACC,aAAa;YACnD,IAAIC,OAAO;gBACT,MAAMC,aAAa,MAAM1J,SAAS0I,GAAG,CAACe;gBACtC7D,IAAI,CAAC4D,aAAa,GAAGE;YACvB;QACF;IACF;IAEA;;;;;;;;;;;;;;;;;GAiBC,GACD,aAAa/C,SAA2BF,QAAW,EAAsB;QACvE,IAAI,CAAC,IAAI,CAAC1F,QAAQ,CAAC0F,WAAW;YAC5B,MAAM,IAAI9C,MAAM;QAClB;QAEA,MAAM0C,WAAsB,EAAE;QAE9B,MAAMY,mBAAmB,MAAM,IAAI,CAACC,kBAAkB,CAACT,UAAUA;QACjEJ,SAAStD,IAAI,IAAIkE;QAEjB,MAAM0C,mBAAmB,IAAI,CAACC,mBAAmB,CAACnD;QAClD,KAAK,MAAMoD,mBAAmBF,iBAAkB;YAC9C,MAAMpB,oBAAoB,MAAM,AAAC9B,QAAgB,CAACoD,gBAAgB;YAClE,IAAI1I,MAAMC,OAAO,CAACmH,oBAAoB;gBACpClC,SAAStD,IAAI,IAAIwF;YACnB;QACF;QAEAzH,YAAYsG,WAAW,CAACX,UAAUJ;QAElC,OAAOA;IACT;IAEA;;;;;;;;;;;;;;;;;GAiBC,GACD,OAAOQ,YAA8BJ,QAAW,EAAa;QAC3D,OAAO9F,gBAAgB+H,GAAG,CAACjC,aAAa,EAAE;IAC5C;IAEA;;;;;;;;;;;;;;;GAeC,GACD,OAAOW,YAA8BX,QAAW,EAAEJ,QAAmB,EAAQ;QAC3E,IAAIA,SAAS7C,MAAM,KAAK,GAAG;YACzB7C,gBAAgBmJ,MAAM,CAACrD;QACzB,OAAO;YACL9F,gBAAgB+F,GAAG,CAACD,UAAUJ;QAChC;IACF;IAEA;;;;;;;;;;;;;;;;GAgBC,GACD,OAAO0D,YAA8BtD,QAAW,EAAW;QACzD,OAAO5F,gBAAgB6H,GAAG,CAACjC;IAC7B;IAEA;;;;;;;;;;;;;;;GAeC,GACD,OAAOuD,YACLvD,QAAW,EACXwD,QAA6C,EACvC;QACN,IAAIA,aAAa9G,WAAW;YAC1BtC,gBAAgBiJ,MAAM,CAACrD;QACzB,OAAO;YACL5F,gBAAgB6F,GAAG,CAACD,UAAUwD;QAChC;IACF;IAEA;;;GAGC,GACD,OAAeL,oBAAoBxH,MAAc,EAAY;QAC3D,IAAIC;QAEJ,IAAID,OAAO,WAAW,IAAIA,WAAWA,OAAO,WAAW,CAACE,SAAS,EAAE;YACjED,eAAeD;QACjB,OAAO;YACLC,eAAef,OAAOC,cAAc,CAACa;QACvC;QAEA,MAAMiG,aAAuB,EAAE;QAC/B,MAAM7F,OAAO,IAAIC;QAEjB,MAAOJ,gBAAgBA,iBAAiBf,OAAOgB,SAAS,CAAE;YACxD,MAAM4H,kBACJjJ,QAAQ0B,cAAc,CAAChD,+BAA+B0C,iBACtD,EAAE;YAEJ,KAAK,MAAMiG,aAAa4B,gBAAiB;gBACvC,IAAI,CAAC1H,KAAKK,GAAG,CAACyF,YAAY;oBACxB9F,KAAKM,GAAG,CAACwF;oBACTD,WAAWtF,IAAI,CAACuF;gBAClB;YACF;YAEAjG,eAAef,OAAOC,cAAc,CAACc;QACvC;QAEA,OAAOgG;IACT;AACF"}
@@ -9,6 +9,13 @@ export interface EntityOptions {
9
9
  * When deserialized from an array, the array is wrapped in { collection: [...] }.
10
10
  */
11
11
  collection?: boolean;
12
+ /**
13
+ * Whether this entity represents a stringifiable value.
14
+ * Stringifiable classes must have a 'value' property that is a string.
15
+ * When serialized, stringifiable instances are unwrapped to just their string.
16
+ * When deserialized from a string, the string is wrapped in { value: "..." }.
17
+ */
18
+ stringifiable?: boolean;
12
19
  }
13
20
  /**
14
21
  * Decorator that marks a class as an Entity.
@@ -67,6 +74,47 @@ export declare function Entity(options?: EntityOptions): ClassDecorator;
67
74
  * ```
68
75
  */
69
76
  export declare function CollectionEntity(): ClassDecorator;
77
+ /**
78
+ * Decorator that marks a class as Stringifiable.
79
+ * This is syntax sugar for @Entity({ stringifiable: true }).
80
+ *
81
+ * Stringifiable classes must have a 'value' property that is a string.
82
+ * When serialized with EntityUtils.toJSON(), they are unwrapped to just the string.
83
+ * When deserialized from a string, the string is wrapped in { value: "..." }.
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * @Stringifiable()
88
+ * class UserId {
89
+ * @StringProperty()
90
+ * readonly value: string;
91
+ *
92
+ * constructor(data: { value: string }) {
93
+ * this.value = data.value;
94
+ * }
95
+ * }
96
+ *
97
+ * @Entity()
98
+ * class User {
99
+ * @EntityProperty(() => UserId)
100
+ * id!: UserId;
101
+ * }
102
+ *
103
+ * const user = new User(...);
104
+ * const json = EntityUtils.toJSON(user);
105
+ * // { id: "user-123" } - unwrapped to string
106
+ *
107
+ * // Also works when serializing the stringifiable directly:
108
+ * const userId = new UserId({ value: "user-123" });
109
+ * const idJson = EntityUtils.toJSON(userId);
110
+ * // "user-123" - unwrapped to string
111
+ *
112
+ * // Parse from string:
113
+ * const parsed = await EntityUtils.parse(UserId, "user-456");
114
+ * // UserId { value: "user-456" }
115
+ * ```
116
+ */
117
+ export declare function Stringifiable(): ClassDecorator;
70
118
  /**
71
119
  * Decorator that marks a method as an entity validator.
72
120
  * 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":"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
+ {"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;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,cAAc,CASlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAEjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,aAAa,IAAI,cAAc,CAE9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAmBjD"}
@@ -67,6 +67,50 @@ import { ENTITY_METADATA_KEY, ENTITY_OPTIONS_METADATA_KEY, ENTITY_VALIDATOR_META
67
67
  collection: true
68
68
  });
69
69
  }
70
+ /**
71
+ * Decorator that marks a class as Stringifiable.
72
+ * This is syntax sugar for @Entity({ stringifiable: true }).
73
+ *
74
+ * Stringifiable classes must have a 'value' property that is a string.
75
+ * When serialized with EntityUtils.toJSON(), they are unwrapped to just the string.
76
+ * When deserialized from a string, the string is wrapped in { value: "..." }.
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * @Stringifiable()
81
+ * class UserId {
82
+ * @StringProperty()
83
+ * readonly value: string;
84
+ *
85
+ * constructor(data: { value: string }) {
86
+ * this.value = data.value;
87
+ * }
88
+ * }
89
+ *
90
+ * @Entity()
91
+ * class User {
92
+ * @EntityProperty(() => UserId)
93
+ * id!: UserId;
94
+ * }
95
+ *
96
+ * const user = new User(...);
97
+ * const json = EntityUtils.toJSON(user);
98
+ * // { id: "user-123" } - unwrapped to string
99
+ *
100
+ * // Also works when serializing the stringifiable directly:
101
+ * const userId = new UserId({ value: "user-123" });
102
+ * const idJson = EntityUtils.toJSON(userId);
103
+ * // "user-123" - unwrapped to string
104
+ *
105
+ * // Parse from string:
106
+ * const parsed = await EntityUtils.parse(UserId, "user-456");
107
+ * // UserId { value: "user-456" }
108
+ * ```
109
+ */ export function Stringifiable() {
110
+ return Entity({
111
+ stringifiable: true
112
+ });
113
+ }
70
114
  /**
71
115
  * Decorator that marks a method as an entity validator.
72
116
  * The method should return an array of Problems.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/entity.ts"],"sourcesContent":["import {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n} from './types.js';\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
+ {"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 * Whether this entity represents a stringifiable value.\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized, stringifiable instances are unwrapped to just their string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n */\n stringifiable?: boolean;\n}\n\n/**\n * Decorator that marks a class as an Entity.\n * This allows us to identify entity instances later.\n *\n * @param options - Optional configuration for the entity\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * @Entity({ 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 class as Stringifiable.\n * This is syntax sugar for @Entity({ stringifiable: true }).\n *\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n *\n * @example\n * ```typescript\n * @Stringifiable()\n * class UserId {\n * @StringProperty()\n * readonly value: string;\n *\n * constructor(data: { value: string }) {\n * this.value = data.value;\n * }\n * }\n *\n * @Entity()\n * class User {\n * @EntityProperty(() => UserId)\n * id!: UserId;\n * }\n *\n * const user = new User(...);\n * const json = EntityUtils.toJSON(user);\n * // { id: \"user-123\" } - unwrapped to string\n *\n * // Also works when serializing the stringifiable directly:\n * const userId = new UserId({ value: \"user-123\" });\n * const idJson = EntityUtils.toJSON(userId);\n * // \"user-123\" - unwrapped to string\n *\n * // Parse from string:\n * const parsed = await EntityUtils.parse(UserId, \"user-456\");\n * // UserId { value: \"user-456\" }\n * ```\n */\nexport function Stringifiable(): ClassDecorator {\n return Entity({ stringifiable: 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","Stringifiable","stringifiable","EntityValidator","propertyKey","existingValidators","getOwnMetadata","includes","push"],"mappings":"AAAA,SACEA,mBAAmB,EACnBC,2BAA2B,EAC3BC,6BAA6B,QACxB,aAAa;AAsBpB;;;;;;;;;;;;;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GACD,OAAO,SAASC;IACd,OAAOP,OAAO;QAAEQ,eAAe;IAAK;AACtC;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC;IACd,OAAO,CAACP,QAAgBQ;QACtB,IAAI,OAAOA,gBAAgB,UAAU;YACnC;QACF;QAEA,MAAMC,qBACJR,QAAQS,cAAc,CAACb,+BAA+BG,WAAW,EAAE;QAErE,IAAI,CAACS,mBAAmBE,QAAQ,CAACH,cAAc;YAC7CC,mBAAmBG,IAAI,CAACJ;QAC1B;QAEAP,QAAQC,cAAc,CACpBL,+BACAY,oBACAT;IAEJ;AACF"}
@@ -89,9 +89,15 @@ export declare function NumberProperty(options?: Omit<PropertyOptions<number, Nu
89
89
  *
90
90
  * @IntProperty({ optional: true })
91
91
  * count?: number;
92
+ *
93
+ * @IntProperty({ min: 0, max: 100 })
94
+ * percentage!: number;
92
95
  * }
93
96
  */
94
- export declare function IntProperty(options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'>): PropertyDecorator;
97
+ export declare function IntProperty(options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'> & {
98
+ min?: number;
99
+ max?: number;
100
+ }): PropertyDecorator;
95
101
  /**
96
102
  * Helper decorator for boolean properties
97
103
  * @example
@@ -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":"AAGA,OAAO,EACL,OAAO,EACP,KAAK,QAAQ,EAIb,eAAe,EAChB,MAAM,YAAY,CAAC;AAapB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAC/C,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7B,iBAAiB,CA2EnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,GAAG;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACA,iBAAiB,CAwBnB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3D,QAAQ,EAAE,CAAC,EACX,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,GACjE,iBAAiB,CAMnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,GAAG;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GACA,iBAAiB,CAkBnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CACzB,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,GAAG;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GACA,iBAAiB,CAoBnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,kBAAkB,CAAC,EAAE,MAAM,CAAC,GACnE,iBAAiB,CAEnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,GAC7D,iBAAiB,CAEnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,GACjE,iBAAiB,CAEnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EACD,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,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,GAC5C,iBAAiB,CAEnB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EACpD,IAAI,EAAE,MAAM,CAAC,EACb,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,iBAAiB,CAmBnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,IAAI,iBAAiB,CAGvD;AAED,eAAO,MAAM,qBAAqB,GAChC,CAAC,SAAS;IAAE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IAAC,QAAQ,IAAI,MAAM,CAAA;CAAE,EAC5D,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,GAAG;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAA;CAAE,EAEnD,MAAM,MAAM,CAAC,EACb,OAAM,IAAI,CACR,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EACrB,WAAW,GAAG,aAAa,GAAG,aAAa,GAAG,MAAM,GAAG,QAAQ,CAC3D,KACL,iBAYC,CAAC;AAEL,eAAO,MAAM,oBAAoB,GAC/B,CAAC,SAAS;IAAE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IAAC,MAAM,IAAI,OAAO,CAAA;CAAE,EAC3D,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,GAAG;IAAE,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC,CAAA;CAAE,EAEpD,MAAM,MAAM,CAAC,EACb,OAAM,IAAI,CACR,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EACrB,WAAW,GAAG,aAAa,GAAG,aAAa,GAAG,MAAM,GAAG,QAAQ,CAC3D,sBAWJ,CAAC"}
@@ -168,18 +168,27 @@ import { enumValidator, intValidator, minLengthValidator, maxLengthValidator, pa
168
168
  *
169
169
  * @IntProperty({ optional: true })
170
170
  * count?: number;
171
+ *
172
+ * @IntProperty({ min: 0, max: 100 })
173
+ * percentage!: number;
171
174
  * }
172
175
  */ export function IntProperty(options) {
173
- const validators = options?.validators ? [
174
- intValidator(),
175
- ...options.validators
176
- ] : [
177
- intValidator()
176
+ const validators = [
177
+ ...options?.validators || []
178
178
  ];
179
+ if (options?.min !== undefined) {
180
+ validators.unshift(minValidator(options.min));
181
+ }
182
+ if (options?.max !== undefined) {
183
+ validators.unshift(maxValidator(options.max));
184
+ }
185
+ validators.unshift(intValidator());
186
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
187
+ const { min, max, ...restOptions } = options || {};
179
188
  return Property({
180
- ...options,
189
+ ...restOptions,
181
190
  type: ()=>Number,
182
- validators
191
+ validators: validators.length > 0 ? validators : undefined
183
192
  });
184
193
  }
185
194
  /**
@@ -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.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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"}
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 * @IntProperty({ min: 0, max: 100 })\n * percentage!: number;\n * }\n */\nexport function IntProperty(\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 validators.unshift(intValidator());\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 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtpaulino/entity",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",