@rtpaulino/entity 0.22.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/entity-registry.d.ts +32 -0
- package/dist/lib/entity-registry.d.ts.map +1 -0
- package/dist/lib/entity-registry.js +42 -0
- package/dist/lib/entity-registry.js.map +1 -0
- package/dist/lib/entity-utils.d.ts +37 -0
- package/dist/lib/entity-utils.d.ts.map +1 -1
- package/dist/lib/entity-utils.js +124 -29
- package/dist/lib/entity-utils.js.map +1 -1
- package/dist/lib/entity.d.ts +19 -2
- package/dist/lib/entity.d.ts.map +1 -1
- package/dist/lib/entity.js +24 -7
- package/dist/lib/entity.js.map +1 -1
- package/dist/lib/property.d.ts +48 -0
- package/dist/lib/property.d.ts.map +1 -1
- package/dist/lib/property.js +52 -2
- package/dist/lib/property.js.map +1 -1
- package/dist/lib/test-entities.d.ts +474 -0
- package/dist/lib/test-entities.d.ts.map +1 -0
- package/dist/lib/test-entities.js +987 -0
- package/dist/lib/test-entities.js.map +1 -0
- package/dist/lib/types.d.ts +21 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/validation-utils.d.ts +6 -6
- package/dist/lib/validation-utils.d.ts.map +1 -1
- package/dist/lib/validation-utils.js +7 -7
- package/dist/lib/validation-utils.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import 'reflect-metadata';
|
|
|
2
2
|
export * from './lib/entity.js';
|
|
3
3
|
export * from './lib/entity-utils.js';
|
|
4
4
|
export * from './lib/entity-di.js';
|
|
5
|
+
export * from './lib/entity-registry.js';
|
|
5
6
|
export * from './lib/types.js';
|
|
6
7
|
export * from './lib/property.js';
|
|
7
8
|
export * from './lib/validation-error.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import 'reflect-metadata';
|
|
|
2
2
|
export * from './lib/entity.js';
|
|
3
3
|
export * from './lib/entity-utils.js';
|
|
4
4
|
export * from './lib/entity-di.js';
|
|
5
|
+
export * from './lib/entity-registry.js';
|
|
5
6
|
export * from './lib/types.js';
|
|
6
7
|
export * from './lib/property.js';
|
|
7
8
|
export * from './lib/validation-error.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import 'reflect-metadata';\n\nexport * from './lib/entity.js';\nexport * from './lib/entity-utils.js';\nexport * from './lib/entity-di.js';\nexport * from './lib/types.js';\nexport * from './lib/property.js';\nexport * from './lib/validation-error.js';\nexport * from './lib/problem.js';\nexport * from './lib/validation-utils.js';\nexport * from './lib/primitive-deserializers.js';\nexport * from './lib/validators.js';\nexport * from './lib/zod-property.js';\nexport * from './lib/injected-property.js';\n"],"names":[],"mappings":"AAAA,OAAO,mBAAmB;AAE1B,cAAc,kBAAkB;AAChC,cAAc,wBAAwB;AACtC,cAAc,qBAAqB;AACnC,cAAc,iBAAiB;AAC/B,cAAc,oBAAoB;AAClC,cAAc,4BAA4B;AAC1C,cAAc,mBAAmB;AACjC,cAAc,4BAA4B;AAC1C,cAAc,mCAAmC;AACjD,cAAc,sBAAsB;AACpC,cAAc,wBAAwB;AACtC,cAAc,6BAA6B"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import 'reflect-metadata';\n\nexport * from './lib/entity.js';\nexport * from './lib/entity-utils.js';\nexport * from './lib/entity-di.js';\nexport * from './lib/entity-registry.js';\nexport * from './lib/types.js';\nexport * from './lib/property.js';\nexport * from './lib/validation-error.js';\nexport * from './lib/problem.js';\nexport * from './lib/validation-utils.js';\nexport * from './lib/primitive-deserializers.js';\nexport * from './lib/validators.js';\nexport * from './lib/zod-property.js';\nexport * from './lib/injected-property.js';\n"],"names":[],"mappings":"AAAA,OAAO,mBAAmB;AAE1B,cAAc,kBAAkB;AAChC,cAAc,wBAAwB;AACtC,cAAc,qBAAqB;AACnC,cAAc,2BAA2B;AACzC,cAAc,iBAAiB;AAC/B,cAAc,oBAAoB;AAClC,cAAc,4BAA4B;AAC1C,cAAc,mBAAmB;AACjC,cAAc,4BAA4B;AAC1C,cAAc,mCAAmC;AACjD,cAAc,sBAAsB;AACpC,cAAc,wBAAwB;AACtC,cAAc,6BAA6B"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry for entity classes decorated with @Entity()
|
|
3
|
+
* Stores entity constructors by name for discriminated entity deserialization
|
|
4
|
+
*/
|
|
5
|
+
export declare class EntityRegistry {
|
|
6
|
+
private static readonly registry;
|
|
7
|
+
/**
|
|
8
|
+
* Registers an entity class with a name
|
|
9
|
+
* @param name - The name to register the entity under
|
|
10
|
+
* @param entityClass - The entity class constructor
|
|
11
|
+
* @throws Error if an entity with this name is already registered
|
|
12
|
+
*/
|
|
13
|
+
static register(name: string, entityClass: Function): void;
|
|
14
|
+
/**
|
|
15
|
+
* Gets an entity class by name
|
|
16
|
+
* @param name - The name of the entity to retrieve
|
|
17
|
+
* @returns The entity class constructor, or undefined if not found
|
|
18
|
+
*/
|
|
19
|
+
static get(name: string): Function | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Checks if an entity with the given name is registered
|
|
22
|
+
* @param name - The name to check
|
|
23
|
+
* @returns true if an entity with this name is registered
|
|
24
|
+
*/
|
|
25
|
+
static has(name: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Gets all registered entity names
|
|
28
|
+
* @returns Array of all registered entity names
|
|
29
|
+
*/
|
|
30
|
+
static getAllNames(): string[];
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=entity-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-registry.d.ts","sourceRoot":"","sources":["../../src/lib/entity-registry.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAA+B;IAE/D;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,GAAG,IAAI;IAW1D;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAI9C;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC;;;OAGG;IACH,MAAM,CAAC,WAAW,IAAI,MAAM,EAAE;CAG/B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-function-type */ /**
|
|
2
|
+
* Registry for entity classes decorated with @Entity()
|
|
3
|
+
* Stores entity constructors by name for discriminated entity deserialization
|
|
4
|
+
*/ export class EntityRegistry {
|
|
5
|
+
static{
|
|
6
|
+
this.registry = new Map();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Registers an entity class with a name
|
|
10
|
+
* @param name - The name to register the entity under
|
|
11
|
+
* @param entityClass - The entity class constructor
|
|
12
|
+
* @throws Error if an entity with this name is already registered
|
|
13
|
+
*/ static register(name, entityClass) {
|
|
14
|
+
const existing = this.registry.get(name);
|
|
15
|
+
if (existing && existing !== entityClass) {
|
|
16
|
+
throw new Error(`Entity name conflict: An entity with name '${name}' is already registered. ` + `Existing: ${existing.name}, New: ${entityClass.name}`);
|
|
17
|
+
}
|
|
18
|
+
this.registry.set(name, entityClass);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Gets an entity class by name
|
|
22
|
+
* @param name - The name of the entity to retrieve
|
|
23
|
+
* @returns The entity class constructor, or undefined if not found
|
|
24
|
+
*/ static get(name) {
|
|
25
|
+
return this.registry.get(name);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Checks if an entity with the given name is registered
|
|
29
|
+
* @param name - The name to check
|
|
30
|
+
* @returns true if an entity with this name is registered
|
|
31
|
+
*/ static has(name) {
|
|
32
|
+
return this.registry.has(name);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Gets all registered entity names
|
|
36
|
+
* @returns Array of all registered entity names
|
|
37
|
+
*/ static getAllNames() {
|
|
38
|
+
return Array.from(this.registry.keys());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=entity-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/entity-registry.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-function-type */\n\n/**\n * Registry for entity classes decorated with @Entity()\n * Stores entity constructors by name for discriminated entity deserialization\n */\nexport class EntityRegistry {\n private static readonly registry = new Map<string, Function>();\n\n /**\n * Registers an entity class with a name\n * @param name - The name to register the entity under\n * @param entityClass - The entity class constructor\n * @throws Error if an entity with this name is already registered\n */\n static register(name: string, entityClass: Function): void {\n const existing = this.registry.get(name);\n if (existing && existing !== entityClass) {\n throw new Error(\n `Entity name conflict: An entity with name '${name}' is already registered. ` +\n `Existing: ${existing.name}, New: ${entityClass.name}`,\n );\n }\n this.registry.set(name, entityClass);\n }\n\n /**\n * Gets an entity class by name\n * @param name - The name of the entity to retrieve\n * @returns The entity class constructor, or undefined if not found\n */\n static get(name: string): Function | undefined {\n return this.registry.get(name);\n }\n\n /**\n * Checks if an entity with the given name is registered\n * @param name - The name to check\n * @returns true if an entity with this name is registered\n */\n static has(name: string): boolean {\n return this.registry.has(name);\n }\n\n /**\n * Gets all registered entity names\n * @returns Array of all registered entity names\n */\n static getAllNames(): string[] {\n return Array.from(this.registry.keys());\n }\n}\n"],"names":["EntityRegistry","registry","Map","register","name","entityClass","existing","get","Error","set","has","getAllNames","Array","from","keys"],"mappings":"AAAA,qDAAqD,GACrD,6DAA6D,GAE7D;;;CAGC,GACD,OAAO,MAAMA;;aACaC,WAAW,IAAIC;;IAEvC;;;;;GAKC,GACD,OAAOC,SAASC,IAAY,EAAEC,WAAqB,EAAQ;QACzD,MAAMC,WAAW,IAAI,CAACL,QAAQ,CAACM,GAAG,CAACH;QACnC,IAAIE,YAAYA,aAAaD,aAAa;YACxC,MAAM,IAAIG,MACR,CAAC,2CAA2C,EAAEJ,KAAK,yBAAyB,CAAC,GAC3E,CAAC,UAAU,EAAEE,SAASF,IAAI,CAAC,OAAO,EAAEC,YAAYD,IAAI,EAAE;QAE5D;QACA,IAAI,CAACH,QAAQ,CAACQ,GAAG,CAACL,MAAMC;IAC1B;IAEA;;;;GAIC,GACD,OAAOE,IAAIH,IAAY,EAAwB;QAC7C,OAAO,IAAI,CAACH,QAAQ,CAACM,GAAG,CAACH;IAC3B;IAEA;;;;GAIC,GACD,OAAOM,IAAIN,IAAY,EAAW;QAChC,OAAO,IAAI,CAACH,QAAQ,CAACS,GAAG,CAACN;IAC3B;IAEA;;;GAGC,GACD,OAAOO,cAAwB;QAC7B,OAAOC,MAAMC,IAAI,CAAC,IAAI,CAACZ,QAAQ,CAACa,IAAI;IACtC;AACF"}
|
|
@@ -30,6 +30,24 @@ export declare class EntityUtils {
|
|
|
30
30
|
* @private
|
|
31
31
|
*/
|
|
32
32
|
private static getEntityOptions;
|
|
33
|
+
/**
|
|
34
|
+
* Gets the registered name for an entity class or instance
|
|
35
|
+
*
|
|
36
|
+
* @param entityOrClass - The entity class constructor or instance
|
|
37
|
+
* @returns The entity name, or undefined if not found
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* @Entity({ name: 'CustomUser' })
|
|
42
|
+
* class User {
|
|
43
|
+
* name: string;
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* console.log(EntityUtils.getEntityName(User)); // 'CustomUser'
|
|
47
|
+
* console.log(EntityUtils.getEntityName(new User())); // 'CustomUser'
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
static getEntityName(entityOrClass: unknown): string | undefined;
|
|
33
51
|
/**
|
|
34
52
|
* Checks if a given entity is marked as a collection entity
|
|
35
53
|
*
|
|
@@ -70,6 +88,15 @@ export declare class EntityUtils {
|
|
|
70
88
|
* ```
|
|
71
89
|
*/
|
|
72
90
|
static isStringifiable(entityOrClass: unknown): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Gets the "wrapper" property name for entities that act as transparent wrappers.
|
|
93
|
+
* When set, this property name is excluded from error paths during validation.
|
|
94
|
+
*
|
|
95
|
+
* @param entityOrClass - The entity instance or class to check
|
|
96
|
+
* @returns The wrapper property name, or undefined if not a wrapper entity
|
|
97
|
+
* @private
|
|
98
|
+
*/
|
|
99
|
+
private static getWrapperPropertyName;
|
|
73
100
|
static sameEntity(a: object, b: object): boolean;
|
|
74
101
|
static getPropertyKeys(target: object): string[];
|
|
75
102
|
static getPropertyOptions(target: object, propertyKey: string): PropertyOptions | undefined;
|
|
@@ -407,12 +434,22 @@ export declare class EntityUtils {
|
|
|
407
434
|
* @private
|
|
408
435
|
*/
|
|
409
436
|
private static deserializeSingleValue;
|
|
437
|
+
/**
|
|
438
|
+
* Deserializes a discriminated entity value by reading the discriminator and looking up the entity class
|
|
439
|
+
* @private
|
|
440
|
+
*/
|
|
441
|
+
private static deserializeDiscriminatedValue;
|
|
410
442
|
/**
|
|
411
443
|
* Validates a property value by running validators and nested entity validation.
|
|
412
444
|
* Prepends the property path to all returned problems.
|
|
413
445
|
* @private
|
|
414
446
|
*/
|
|
415
447
|
private static validatePropertyValue;
|
|
448
|
+
/**
|
|
449
|
+
* Checks if an array contains any entity elements
|
|
450
|
+
* @private
|
|
451
|
+
*/
|
|
452
|
+
private static hasEntityElements;
|
|
416
453
|
/**
|
|
417
454
|
* Runs property validators for a given property value
|
|
418
455
|
* @private
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,YAAY,EAGZ,eAAe,EACf,mBAAmB,EACpB,MAAM,YAAY,CAAC;AASpB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuBvC,qBAAa,WAAW;IACtB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,MAAM;IAmB5C;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAa/B;;;;;;;;;;;;;;;;;;OAkBG;IACH,MAAM,CAAC,kBAAkB,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO;IAU1D;;;;;;;;;;;;;;;;;;OAkBG;IACH,MAAM,CAAC,eAAe,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO;IAUvD,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAQhD,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAoChD,MAAM,CAAC,kBAAkB,CACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,eAAe,GAAG,SAAS;IA8B9B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IA2B9C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,EAC1B,SAAS,EAAE,CAAC,EACZ,SAAS,EAAE,CAAC,GACX;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,EAAE;IAoC/D,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAaxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4DG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO;IA8DnD;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/entity-utils.js
CHANGED
|
@@ -4,9 +4,10 @@ import { EntityDI } from './entity-di.js';
|
|
|
4
4
|
import { isEqualWith } from 'lodash-es';
|
|
5
5
|
import { ValidationError } from './validation-error.js';
|
|
6
6
|
import { Problem } from './problem.js';
|
|
7
|
-
import {
|
|
7
|
+
import { prependArrayIndex, prependPropertyPath, createValidationError } from './validation-utils.js';
|
|
8
8
|
import { isPrimitiveConstructor, deserializePrimitive } from './primitive-deserializers.js';
|
|
9
9
|
import { ok } from 'assert';
|
|
10
|
+
import { EntityRegistry } from './entity-registry.js';
|
|
10
11
|
/**
|
|
11
12
|
* WeakMap to store validation problems for entity instances
|
|
12
13
|
*/ const problemsStorage = new WeakMap();
|
|
@@ -60,6 +61,29 @@ export class EntityUtils {
|
|
|
60
61
|
return options ?? {};
|
|
61
62
|
}
|
|
62
63
|
/**
|
|
64
|
+
* Gets the registered name for an entity class or instance
|
|
65
|
+
*
|
|
66
|
+
* @param entityOrClass - The entity class constructor or instance
|
|
67
|
+
* @returns The entity name, or undefined if not found
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* @Entity({ name: 'CustomUser' })
|
|
72
|
+
* class User {
|
|
73
|
+
* name: string;
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* console.log(EntityUtils.getEntityName(User)); // 'CustomUser'
|
|
77
|
+
* console.log(EntityUtils.getEntityName(new User())); // 'CustomUser'
|
|
78
|
+
* ```
|
|
79
|
+
*/ static getEntityName(entityOrClass) {
|
|
80
|
+
if (!this.isEntity(entityOrClass)) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const options = this.getEntityOptions(entityOrClass);
|
|
84
|
+
return options.name;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
63
87
|
* Checks if a given entity is marked as a collection entity
|
|
64
88
|
*
|
|
65
89
|
* @param entityOrClass - The entity instance or class to check
|
|
@@ -109,6 +133,19 @@ export class EntityUtils {
|
|
|
109
133
|
const options = this.getEntityOptions(entityOrClass);
|
|
110
134
|
return options.stringifiable === true;
|
|
111
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Gets the "wrapper" property name for entities that act as transparent wrappers.
|
|
138
|
+
* When set, this property name is excluded from error paths during validation.
|
|
139
|
+
*
|
|
140
|
+
* @param entityOrClass - The entity instance or class to check
|
|
141
|
+
* @returns The wrapper property name, or undefined if not a wrapper entity
|
|
142
|
+
* @private
|
|
143
|
+
*/ static getWrapperPropertyName(entityOrClass) {
|
|
144
|
+
if (!this.isEntity(entityOrClass)) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
return this.getEntityOptions(entityOrClass).wrapperProperty;
|
|
148
|
+
}
|
|
112
149
|
static sameEntity(a, b) {
|
|
113
150
|
if (!this.isEntity(a) || !this.isEntity(b)) {
|
|
114
151
|
return false;
|
|
@@ -334,7 +371,7 @@ export class EntityUtils {
|
|
|
334
371
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
335
372
|
return value.map((item)=>options.serialize(item));
|
|
336
373
|
}
|
|
337
|
-
return value.map((item)=>this.serializeValue(item));
|
|
374
|
+
return value.map((item)=>this.serializeValue(item, options));
|
|
338
375
|
}
|
|
339
376
|
if (options?.serialize) {
|
|
340
377
|
return options.serialize(value);
|
|
@@ -346,7 +383,25 @@ export class EntityUtils {
|
|
|
346
383
|
return value.toString();
|
|
347
384
|
}
|
|
348
385
|
if (this.isEntity(value)) {
|
|
349
|
-
|
|
386
|
+
const serialized = this.toJSON(value);
|
|
387
|
+
// If this is a discriminated entity property, add the discriminator inline
|
|
388
|
+
if (options?.discriminated === true) {
|
|
389
|
+
const discriminatorProperty = options.discriminatorProperty;
|
|
390
|
+
ok(discriminatorProperty, 'Discriminator property must be defined');
|
|
391
|
+
const entityClass = Object.getPrototypeOf(value).constructor;
|
|
392
|
+
const entityName = this.getEntityName(entityClass);
|
|
393
|
+
if (!entityName) {
|
|
394
|
+
throw new Error(`Cannot serialize discriminated entity: Entity class '${entityClass.name}' is not registered. Ensure it's decorated with @Entity().`);
|
|
395
|
+
}
|
|
396
|
+
if (typeof serialized !== 'object' || Array.isArray(serialized) || serialized === null) {
|
|
397
|
+
throw new Error(`Cannot serialize discriminated entity: Expected serialized value to be an object.`);
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
...serialized,
|
|
401
|
+
[discriminatorProperty]: entityName
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
return serialized;
|
|
350
405
|
}
|
|
351
406
|
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
352
407
|
return value;
|
|
@@ -357,13 +412,10 @@ export class EntityUtils {
|
|
|
357
412
|
* Internal parse implementation with extended options
|
|
358
413
|
* @private
|
|
359
414
|
*/ static async _parseInternal(entityClass, plainObject, options = {}) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
value: plainObject
|
|
363
|
-
};
|
|
364
|
-
} else if (this.isCollectionEntity(entityClass)) {
|
|
415
|
+
const wrapperPropertyName = this.getWrapperPropertyName(entityClass);
|
|
416
|
+
if (wrapperPropertyName) {
|
|
365
417
|
plainObject = {
|
|
366
|
-
|
|
418
|
+
[wrapperPropertyName]: plainObject
|
|
367
419
|
};
|
|
368
420
|
}
|
|
369
421
|
if (plainObject == null) {
|
|
@@ -420,11 +472,16 @@ export class EntityUtils {
|
|
|
420
472
|
});
|
|
421
473
|
} catch (error) {
|
|
422
474
|
if (error instanceof ValidationError) {
|
|
423
|
-
const
|
|
424
|
-
|
|
475
|
+
const isWrapperProperty = wrapperPropertyName === key;
|
|
476
|
+
if (isWrapperProperty) {
|
|
477
|
+
hardProblems.push(...error.problems);
|
|
478
|
+
} else {
|
|
479
|
+
const problems = prependPropertyPath(key, error.problems);
|
|
480
|
+
hardProblems.push(...problems);
|
|
481
|
+
}
|
|
425
482
|
} else if (error instanceof Error) {
|
|
426
483
|
hardProblems.push(new Problem({
|
|
427
|
-
property: key,
|
|
484
|
+
property: wrapperPropertyName === key ? '' : key,
|
|
428
485
|
message: error.message
|
|
429
486
|
}));
|
|
430
487
|
} else {
|
|
@@ -792,10 +849,9 @@ export class EntityUtils {
|
|
|
792
849
|
* Deserializes a single value according to the type metadata
|
|
793
850
|
* @private
|
|
794
851
|
*/ static async deserializeValue(value, options, parseOptions) {
|
|
795
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
796
|
-
const typeConstructor = options.type();
|
|
797
852
|
const isArray = options.array === true;
|
|
798
853
|
const isSparse = options.sparse === true;
|
|
854
|
+
const isDiscriminated = options.discriminated === true;
|
|
799
855
|
if (isArray) {
|
|
800
856
|
if (!Array.isArray(value)) {
|
|
801
857
|
throw createValidationError(`Expects an array but received ${typeof value}`);
|
|
@@ -816,7 +872,11 @@ export class EntityUtils {
|
|
|
816
872
|
try {
|
|
817
873
|
if (options.deserialize) {
|
|
818
874
|
result.push(options.deserialize(item));
|
|
875
|
+
} else if (isDiscriminated) {
|
|
876
|
+
result.push(await this.deserializeDiscriminatedValue(item, options));
|
|
819
877
|
} else {
|
|
878
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
879
|
+
const typeConstructor = options.type();
|
|
820
880
|
result.push(await this.deserializeSingleValue(item, typeConstructor, parseOptions));
|
|
821
881
|
}
|
|
822
882
|
} catch (error) {
|
|
@@ -837,6 +897,11 @@ export class EntityUtils {
|
|
|
837
897
|
if (options.deserialize) {
|
|
838
898
|
return options.deserialize(value);
|
|
839
899
|
}
|
|
900
|
+
if (isDiscriminated) {
|
|
901
|
+
return await this.deserializeDiscriminatedValue(value, options);
|
|
902
|
+
}
|
|
903
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
904
|
+
const typeConstructor = options.type();
|
|
840
905
|
return await this.deserializeSingleValue(value, typeConstructor, parseOptions);
|
|
841
906
|
}
|
|
842
907
|
/**
|
|
@@ -853,6 +918,28 @@ export class EntityUtils {
|
|
|
853
918
|
throw createValidationError(`Has unknown type constructor. Supported types are: String, Number, Boolean, Date, BigInt, and @Entity() classes. Use passthrough: true to explicitly allow unknown types.`);
|
|
854
919
|
}
|
|
855
920
|
/**
|
|
921
|
+
* Deserializes a discriminated entity value by reading the discriminator and looking up the entity class
|
|
922
|
+
* @private
|
|
923
|
+
*/ static async deserializeDiscriminatedValue(value, options) {
|
|
924
|
+
if (value == null) {
|
|
925
|
+
throw createValidationError(`Cannot deserialize discriminated entity from null or undefined`);
|
|
926
|
+
}
|
|
927
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
928
|
+
throw createValidationError(`Discriminated entity must be an object, received ${typeof value}`);
|
|
929
|
+
}
|
|
930
|
+
const discriminatorProperty = options.discriminatorProperty;
|
|
931
|
+
ok(discriminatorProperty, 'Discriminator property must be defined');
|
|
932
|
+
const discriminatorValue = value[discriminatorProperty];
|
|
933
|
+
if (typeof discriminatorValue !== 'string' || discriminatorValue.trim() === '') {
|
|
934
|
+
throw createValidationError(`Missing or invalid discriminator property '${discriminatorProperty}'. Expected a non-empty string.`);
|
|
935
|
+
}
|
|
936
|
+
const entityClass = EntityRegistry.get(discriminatorValue);
|
|
937
|
+
if (!entityClass) {
|
|
938
|
+
throw createValidationError(`Unknown entity type '${discriminatorValue}'. No entity registered with this name.`);
|
|
939
|
+
}
|
|
940
|
+
return await this.parse(entityClass, value, {});
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
856
943
|
* Validates a property value by running validators and nested entity validation.
|
|
857
944
|
* Prepends the property path to all returned problems.
|
|
858
945
|
* @private
|
|
@@ -863,24 +950,32 @@ export class EntityUtils {
|
|
|
863
950
|
const validatorProblems = await validator({
|
|
864
951
|
value
|
|
865
952
|
});
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
problems.push(
|
|
869
|
-
property: combinePropertyPaths(propertyPath, problem.property),
|
|
870
|
-
message: problem.message
|
|
871
|
-
}));
|
|
953
|
+
if (validatorProblems.length > 0) {
|
|
954
|
+
const prepended = prependPropertyPath(propertyPath, validatorProblems);
|
|
955
|
+
problems.push(...prepended);
|
|
872
956
|
}
|
|
873
957
|
}
|
|
874
958
|
}
|
|
875
959
|
if (EntityUtils.isEntity(value)) {
|
|
876
960
|
const existingProblems = problemsStorage.get(value);
|
|
877
961
|
const nestedProblems = existingProblems && existingProblems.length > 0 ? existingProblems : await EntityUtils.validate(value);
|
|
878
|
-
|
|
879
|
-
|
|
962
|
+
if (nestedProblems.length > 0) {
|
|
963
|
+
const prepended = prependPropertyPath(propertyPath, nestedProblems);
|
|
964
|
+
problems.push(...prepended);
|
|
965
|
+
}
|
|
880
966
|
}
|
|
881
967
|
return problems;
|
|
882
968
|
}
|
|
883
969
|
/**
|
|
970
|
+
* Checks if an array contains any entity elements
|
|
971
|
+
* @private
|
|
972
|
+
*/ static hasEntityElements(value) {
|
|
973
|
+
if (!Array.isArray(value)) {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
return value.some((element)=>this.isEntity(element));
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
884
979
|
* Runs property validators for a given property value
|
|
885
980
|
* @private
|
|
886
981
|
*/ static async runPropertyValidators(key, value, options) {
|
|
@@ -897,15 +992,13 @@ export class EntityUtils {
|
|
|
897
992
|
const validatorProblems = await validator({
|
|
898
993
|
value
|
|
899
994
|
});
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
message: problem.message
|
|
904
|
-
}));
|
|
995
|
+
if (validatorProblems.length > 0) {
|
|
996
|
+
const prepended = prependPropertyPath(key, validatorProblems);
|
|
997
|
+
problems.push(...prepended);
|
|
905
998
|
}
|
|
906
999
|
}
|
|
907
1000
|
const validators = options.validators || [];
|
|
908
|
-
if (validators.length > 0) {
|
|
1001
|
+
if (validators.length > 0 || this.hasEntityElements(value)) {
|
|
909
1002
|
for(let i = 0; i < value.length; i++){
|
|
910
1003
|
const element = value[i];
|
|
911
1004
|
if (element !== null && element !== undefined) {
|
|
@@ -924,12 +1017,14 @@ export class EntityUtils {
|
|
|
924
1017
|
*/ static async validateProperties(dataOrInstance, prototype) {
|
|
925
1018
|
const problems = [];
|
|
926
1019
|
const keys = Object.keys(dataOrInstance);
|
|
1020
|
+
const wrapperProperty = this.getWrapperPropertyName(dataOrInstance);
|
|
927
1021
|
for (const key of keys){
|
|
928
1022
|
const options = this.getPropertyOptions(prototype, key);
|
|
929
1023
|
if (options) {
|
|
930
1024
|
const value = dataOrInstance[key];
|
|
931
1025
|
if (value != null) {
|
|
932
|
-
const
|
|
1026
|
+
const propertyPath = wrapperProperty === key ? '' : key;
|
|
1027
|
+
const validationProblems = await this.runPropertyValidators(propertyPath, value, options);
|
|
933
1028
|
problems.push(...validationProblems);
|
|
934
1029
|
}
|
|
935
1030
|
}
|