@rtpaulino/entity 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +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 +43 -0
- package/dist/lib/entity-utils.d.ts.map +1 -1
- package/dist/lib/entity-utils.js +119 -5
- package/dist/lib/entity-utils.js.map +1 -1
- package/dist/lib/entity.d.ts +60 -1
- package/dist/lib/entity.d.ts.map +1 -1
- package/dist/lib/entity.js +64 -5
- package/dist/lib/entity.js.map +1 -1
- package/dist/lib/property.d.ts +55 -1
- package/dist/lib/property.d.ts.map +1 -1
- package/dist/lib/property.js +68 -9
- 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/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
|
*
|
|
@@ -50,6 +68,26 @@ export declare class EntityUtils {
|
|
|
50
68
|
* ```
|
|
51
69
|
*/
|
|
52
70
|
static isCollectionEntity(entityOrClass: unknown): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Checks if a given entity is marked as a stringifiable entity
|
|
73
|
+
*
|
|
74
|
+
* @param entityOrClass - The entity instance or class to check
|
|
75
|
+
* @returns true if the entity is a stringifiable entity, false otherwise
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* @Stringifiable()
|
|
80
|
+
* class UserId {
|
|
81
|
+
* @StringProperty()
|
|
82
|
+
* value: string;
|
|
83
|
+
* }
|
|
84
|
+
*
|
|
85
|
+
* const userId = new UserId({ value: 'user-123' });
|
|
86
|
+
* console.log(EntityUtils.isStringifiable(userId)); // true
|
|
87
|
+
* console.log(EntityUtils.isStringifiable(UserId)); // true
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
static isStringifiable(entityOrClass: unknown): boolean;
|
|
53
91
|
static sameEntity(a: object, b: object): boolean;
|
|
54
92
|
static getPropertyKeys(target: object): string[];
|
|
55
93
|
static getPropertyOptions(target: object, propertyKey: string): PropertyOptions | undefined;
|
|
@@ -387,6 +425,11 @@ export declare class EntityUtils {
|
|
|
387
425
|
* @private
|
|
388
426
|
*/
|
|
389
427
|
private static deserializeSingleValue;
|
|
428
|
+
/**
|
|
429
|
+
* Deserializes a discriminated entity value by reading the discriminator and looking up the entity class
|
|
430
|
+
* @private
|
|
431
|
+
*/
|
|
432
|
+
private static deserializeDiscriminatedValue;
|
|
390
433
|
/**
|
|
391
434
|
* Validates a property value by running validators and nested entity validation.
|
|
392
435
|
* Prepends the property path to all returned problems.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,YAAY,EAGZ,eAAe,EACf,mBAAmB,EACpB,MAAM,YAAY,CAAC;AASpB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/entity-utils.js
CHANGED
|
@@ -7,6 +7,7 @@ import { Problem } from './problem.js';
|
|
|
7
7
|
import { prependPropertyPath, prependArrayIndex, createValidationError, combinePropertyPaths } 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
|
|
@@ -84,6 +108,31 @@ export class EntityUtils {
|
|
|
84
108
|
const options = this.getEntityOptions(entityOrClass);
|
|
85
109
|
return options.collection === true;
|
|
86
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Checks if a given entity is marked as a stringifiable entity
|
|
113
|
+
*
|
|
114
|
+
* @param entityOrClass - The entity instance or class to check
|
|
115
|
+
* @returns true if the entity is a stringifiable entity, false otherwise
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* @Stringifiable()
|
|
120
|
+
* class UserId {
|
|
121
|
+
* @StringProperty()
|
|
122
|
+
* value: string;
|
|
123
|
+
* }
|
|
124
|
+
*
|
|
125
|
+
* const userId = new UserId({ value: 'user-123' });
|
|
126
|
+
* console.log(EntityUtils.isStringifiable(userId)); // true
|
|
127
|
+
* console.log(EntityUtils.isStringifiable(UserId)); // true
|
|
128
|
+
* ```
|
|
129
|
+
*/ static isStringifiable(entityOrClass) {
|
|
130
|
+
if (!this.isEntity(entityOrClass)) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
const options = this.getEntityOptions(entityOrClass);
|
|
134
|
+
return options.stringifiable === true;
|
|
135
|
+
}
|
|
87
136
|
static sameEntity(a, b) {
|
|
88
137
|
if (!this.isEntity(a) || !this.isEntity(b)) {
|
|
89
138
|
return false;
|
|
@@ -254,6 +303,19 @@ export class EntityUtils {
|
|
|
254
303
|
* // ['a', 'b'] - unwrapped to array
|
|
255
304
|
* ```
|
|
256
305
|
*/ static toJSON(entity) {
|
|
306
|
+
if (this.isStringifiable(entity)) {
|
|
307
|
+
const valuePropertyOptions = this.getPropertyOptions(entity, 'value');
|
|
308
|
+
if (!valuePropertyOptions) {
|
|
309
|
+
throw new Error(`Stringifiable entity 'value' property is missing metadata`);
|
|
310
|
+
}
|
|
311
|
+
if (valuePropertyOptions.array) {
|
|
312
|
+
throw new Error(`Stringifiable entity 'value' property must not be an array`);
|
|
313
|
+
}
|
|
314
|
+
if (valuePropertyOptions.type?.() !== String) {
|
|
315
|
+
throw new Error(`Stringifiable entity 'value' property must be of type String`);
|
|
316
|
+
}
|
|
317
|
+
return this.serializeValue(entity.value, valuePropertyOptions);
|
|
318
|
+
}
|
|
257
319
|
if (this.isCollectionEntity(entity)) {
|
|
258
320
|
const collectionPropertyOptions = this.getPropertyOptions(entity, 'collection');
|
|
259
321
|
if (!collectionPropertyOptions) {
|
|
@@ -296,7 +358,7 @@ export class EntityUtils {
|
|
|
296
358
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
297
359
|
return value.map((item)=>options.serialize(item));
|
|
298
360
|
}
|
|
299
|
-
return value.map((item)=>this.serializeValue(item));
|
|
361
|
+
return value.map((item)=>this.serializeValue(item, options));
|
|
300
362
|
}
|
|
301
363
|
if (options?.serialize) {
|
|
302
364
|
return options.serialize(value);
|
|
@@ -308,7 +370,25 @@ export class EntityUtils {
|
|
|
308
370
|
return value.toString();
|
|
309
371
|
}
|
|
310
372
|
if (this.isEntity(value)) {
|
|
311
|
-
|
|
373
|
+
const serialized = this.toJSON(value);
|
|
374
|
+
// If this is a discriminated entity property, add the discriminator inline
|
|
375
|
+
if (options?.discriminated === true) {
|
|
376
|
+
const discriminatorProperty = options.discriminatorProperty;
|
|
377
|
+
ok(discriminatorProperty, 'Discriminator property must be defined');
|
|
378
|
+
const entityClass = Object.getPrototypeOf(value).constructor;
|
|
379
|
+
const entityName = this.getEntityName(entityClass);
|
|
380
|
+
if (!entityName) {
|
|
381
|
+
throw new Error(`Cannot serialize discriminated entity: Entity class '${entityClass.name}' is not registered. Ensure it's decorated with @Entity().`);
|
|
382
|
+
}
|
|
383
|
+
if (typeof serialized !== 'object' || Array.isArray(serialized) || serialized === null) {
|
|
384
|
+
throw new Error(`Cannot serialize discriminated entity: Expected serialized value to be an object.`);
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
...serialized,
|
|
388
|
+
[discriminatorProperty]: entityName
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
return serialized;
|
|
312
392
|
}
|
|
313
393
|
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
314
394
|
return value;
|
|
@@ -319,7 +399,11 @@ export class EntityUtils {
|
|
|
319
399
|
* Internal parse implementation with extended options
|
|
320
400
|
* @private
|
|
321
401
|
*/ static async _parseInternal(entityClass, plainObject, options = {}) {
|
|
322
|
-
if (this.
|
|
402
|
+
if (this.isStringifiable(entityClass)) {
|
|
403
|
+
plainObject = {
|
|
404
|
+
value: plainObject
|
|
405
|
+
};
|
|
406
|
+
} else if (this.isCollectionEntity(entityClass)) {
|
|
323
407
|
plainObject = {
|
|
324
408
|
collection: plainObject
|
|
325
409
|
};
|
|
@@ -750,10 +834,9 @@ export class EntityUtils {
|
|
|
750
834
|
* Deserializes a single value according to the type metadata
|
|
751
835
|
* @private
|
|
752
836
|
*/ static async deserializeValue(value, options, parseOptions) {
|
|
753
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
754
|
-
const typeConstructor = options.type();
|
|
755
837
|
const isArray = options.array === true;
|
|
756
838
|
const isSparse = options.sparse === true;
|
|
839
|
+
const isDiscriminated = options.discriminated === true;
|
|
757
840
|
if (isArray) {
|
|
758
841
|
if (!Array.isArray(value)) {
|
|
759
842
|
throw createValidationError(`Expects an array but received ${typeof value}`);
|
|
@@ -774,7 +857,11 @@ export class EntityUtils {
|
|
|
774
857
|
try {
|
|
775
858
|
if (options.deserialize) {
|
|
776
859
|
result.push(options.deserialize(item));
|
|
860
|
+
} else if (isDiscriminated) {
|
|
861
|
+
result.push(await this.deserializeDiscriminatedValue(item, options));
|
|
777
862
|
} else {
|
|
863
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
864
|
+
const typeConstructor = options.type();
|
|
778
865
|
result.push(await this.deserializeSingleValue(item, typeConstructor, parseOptions));
|
|
779
866
|
}
|
|
780
867
|
} catch (error) {
|
|
@@ -795,6 +882,11 @@ export class EntityUtils {
|
|
|
795
882
|
if (options.deserialize) {
|
|
796
883
|
return options.deserialize(value);
|
|
797
884
|
}
|
|
885
|
+
if (isDiscriminated) {
|
|
886
|
+
return await this.deserializeDiscriminatedValue(value, options);
|
|
887
|
+
}
|
|
888
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
889
|
+
const typeConstructor = options.type();
|
|
798
890
|
return await this.deserializeSingleValue(value, typeConstructor, parseOptions);
|
|
799
891
|
}
|
|
800
892
|
/**
|
|
@@ -811,6 +903,28 @@ export class EntityUtils {
|
|
|
811
903
|
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.`);
|
|
812
904
|
}
|
|
813
905
|
/**
|
|
906
|
+
* Deserializes a discriminated entity value by reading the discriminator and looking up the entity class
|
|
907
|
+
* @private
|
|
908
|
+
*/ static async deserializeDiscriminatedValue(value, options) {
|
|
909
|
+
if (value == null) {
|
|
910
|
+
throw createValidationError(`Cannot deserialize discriminated entity from null or undefined`);
|
|
911
|
+
}
|
|
912
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
913
|
+
throw createValidationError(`Discriminated entity must be an object, received ${typeof value}`);
|
|
914
|
+
}
|
|
915
|
+
const discriminatorProperty = options.discriminatorProperty;
|
|
916
|
+
ok(discriminatorProperty, 'Discriminator property must be defined');
|
|
917
|
+
const discriminatorValue = value[discriminatorProperty];
|
|
918
|
+
if (typeof discriminatorValue !== 'string' || discriminatorValue.trim() === '') {
|
|
919
|
+
throw createValidationError(`Missing or invalid discriminator property '${discriminatorProperty}'. Expected a non-empty string.`);
|
|
920
|
+
}
|
|
921
|
+
const entityClass = EntityRegistry.get(discriminatorValue);
|
|
922
|
+
if (!entityClass) {
|
|
923
|
+
throw createValidationError(`Unknown entity type '${discriminatorValue}'. No entity registered with this name.`);
|
|
924
|
+
}
|
|
925
|
+
return await this.parse(entityClass, value, {});
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
814
928
|
* Validates a property value by running validators and nested entity validation.
|
|
815
929
|
* Prepends the property path to all returned problems.
|
|
816
930
|
* @private
|