@mikro-orm/core 7.1.0-dev.1 → 7.1.0-dev.3
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/EntityManager.d.ts +19 -0
- package/EntityManager.js +48 -0
- package/drivers/DatabaseDriver.d.ts +1 -0
- package/drivers/DatabaseDriver.js +3 -0
- package/drivers/IDatabaseDriver.d.ts +5 -0
- package/entity/BaseEntity.d.ts +2 -2
- package/entity/WrappedEntity.d.ts +2 -2
- package/package.json +1 -1
- package/serialization/EntitySerializer.d.ts +18 -6
- package/serialization/EntitySerializer.js +31 -5
- package/typings.d.ts +20 -5
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +1 -1
package/EntityManager.d.ts
CHANGED
|
@@ -307,6 +307,25 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
307
307
|
* Fires native insert query. Calling this has no side effects on the context (identity map).
|
|
308
308
|
*/
|
|
309
309
|
insert<Entity extends object>(entityNameOrEntity: EntityName<Entity> | Entity, data?: RequiredEntityData<Entity> | Entity, options?: NativeInsertUpdateOptions<Entity>): Promise<Primary<Entity>>;
|
|
310
|
+
/**
|
|
311
|
+
* Clones rows matching the condition at the database level via INSERT...SELECT.
|
|
312
|
+
* Automatically excludes auto-increment PKs, generated columns, and resets version properties.
|
|
313
|
+
* Returns the cloned entity, registered in the identity map.
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* // Clone by entity class + where + overrides
|
|
318
|
+
* const cloned = await em.clone(Author, { id: 1 }, { email: 'new@email.com' });
|
|
319
|
+
*
|
|
320
|
+
* // Pure clone (all non-PK fields copied)
|
|
321
|
+
* const cloned = await em.clone(Author, { id: 1 });
|
|
322
|
+
*
|
|
323
|
+
* // Clone a loaded entity
|
|
324
|
+
* const author = await em.findOneOrFail(Author, 1);
|
|
325
|
+
* const cloned = await em.clone(author, { email: 'new@email.com' });
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
clone<Entity extends object>(entityNameOrEntity: EntityName<Entity> | Entity, whereOrOverrides?: FilterQuery<NoInfer<Entity>> | EntityData<Entity>, overridesOrOptions?: EntityData<Entity> | NativeInsertUpdateOptions<Entity>, options?: NativeInsertUpdateOptions<Entity>): Promise<Entity>;
|
|
310
329
|
/**
|
|
311
330
|
* Fires native multi-insert query. Calling this has no side effects on the context (identity map).
|
|
312
331
|
*/
|
package/EntityManager.js
CHANGED
|
@@ -1211,6 +1211,54 @@ export class EntityManager {
|
|
|
1211
1211
|
});
|
|
1212
1212
|
return res.insertId;
|
|
1213
1213
|
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Clones rows matching the condition at the database level via INSERT...SELECT.
|
|
1216
|
+
* Automatically excludes auto-increment PKs, generated columns, and resets version properties.
|
|
1217
|
+
* Returns the cloned entity, registered in the identity map.
|
|
1218
|
+
*
|
|
1219
|
+
* @example
|
|
1220
|
+
* ```ts
|
|
1221
|
+
* // Clone by entity class + where + overrides
|
|
1222
|
+
* const cloned = await em.clone(Author, { id: 1 }, { email: 'new@email.com' });
|
|
1223
|
+
*
|
|
1224
|
+
* // Pure clone (all non-PK fields copied)
|
|
1225
|
+
* const cloned = await em.clone(Author, { id: 1 });
|
|
1226
|
+
*
|
|
1227
|
+
* // Clone a loaded entity
|
|
1228
|
+
* const author = await em.findOneOrFail(Author, 1);
|
|
1229
|
+
* const cloned = await em.clone(author, { email: 'new@email.com' });
|
|
1230
|
+
* ```
|
|
1231
|
+
*/
|
|
1232
|
+
async clone(entityNameOrEntity, whereOrOverrides, overridesOrOptions, options) {
|
|
1233
|
+
const em = this.getContext(false);
|
|
1234
|
+
let entityName;
|
|
1235
|
+
let where;
|
|
1236
|
+
let overrides;
|
|
1237
|
+
if (Utils.isEntity(entityNameOrEntity)) {
|
|
1238
|
+
// clone(entity, overrides?, options?)
|
|
1239
|
+
entityName = entityNameOrEntity.constructor;
|
|
1240
|
+
where = helper(entityNameOrEntity).getPrimaryKey();
|
|
1241
|
+
overrides = whereOrOverrides;
|
|
1242
|
+
options = overridesOrOptions;
|
|
1243
|
+
}
|
|
1244
|
+
else {
|
|
1245
|
+
// clone(EntityClass, where, overrides?, options?)
|
|
1246
|
+
entityName = entityNameOrEntity;
|
|
1247
|
+
where = whereOrOverrides;
|
|
1248
|
+
overrides = overridesOrOptions;
|
|
1249
|
+
}
|
|
1250
|
+
options ??= {};
|
|
1251
|
+
em.prepareOptions(options);
|
|
1252
|
+
const meta = em.metadata.get(entityName);
|
|
1253
|
+
const res = await em.driver.nativeClone(entityName, where, overrides, {
|
|
1254
|
+
ctx: em.#transactionContext,
|
|
1255
|
+
...options,
|
|
1256
|
+
});
|
|
1257
|
+
const pk = res.insertId ?? res.row?.[meta.primaryKeys[0]];
|
|
1258
|
+
return em.findOneOrFail(entityName, pk, {
|
|
1259
|
+
schema: options.schema,
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1214
1262
|
/**
|
|
1215
1263
|
* Fires native multi-insert query. Calling this has no side effects on the context (identity map).
|
|
1216
1264
|
*/
|
|
@@ -28,6 +28,7 @@ export declare abstract class DatabaseDriver<C extends Connection> implements ID
|
|
|
28
28
|
abstract nativeUpdate<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
|
|
29
29
|
nativeUpdateMany<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
|
|
30
30
|
abstract nativeDelete<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
|
|
31
|
+
nativeClone<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, overrides?: EntityData<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
|
|
31
32
|
abstract count<T extends object, P extends string = never>(entityName: EntityName<T>, where: FilterQuery<T>, options?: CountOptions<T, P>): Promise<number>;
|
|
32
33
|
/** Creates a new EntityManager instance bound to this driver. */
|
|
33
34
|
createEntityManager(useContext?: boolean): this[typeof EntityManagerType];
|
|
@@ -27,6 +27,9 @@ export class DatabaseDriver {
|
|
|
27
27
|
async nativeUpdateMany(entityName, where, data, options) {
|
|
28
28
|
throw new Error(`Batch updates are not supported by ${this.constructor.name} driver`);
|
|
29
29
|
}
|
|
30
|
+
async nativeClone(entityName, where, overrides, options) {
|
|
31
|
+
throw new Error(`${this.constructor.name} does not support nativeClone`);
|
|
32
|
+
}
|
|
30
33
|
/** Creates a new EntityManager instance bound to this driver. */
|
|
31
34
|
createEntityManager(useContext) {
|
|
32
35
|
const EntityManagerClass = this.config.get('entityManager', EntityManager);
|
|
@@ -52,6 +52,11 @@ export interface IDatabaseDriver<C extends Connection = Connection> {
|
|
|
52
52
|
nativeUpdateMany<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
|
|
53
53
|
/** Deletes rows matching the given condition. */
|
|
54
54
|
nativeDelete<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options?: NativeDeleteOptions<T>): Promise<QueryResult<T>>;
|
|
55
|
+
/**
|
|
56
|
+
* Clones rows matching the condition at the database level via INSERT...SELECT.
|
|
57
|
+
* Automatically excludes auto-increment PKs, generated columns, and resets version properties.
|
|
58
|
+
*/
|
|
59
|
+
nativeClone<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, overrides?: EntityData<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
|
|
55
60
|
/** Persists changes to M:N collections (inserts/deletes pivot table rows). */
|
|
56
61
|
syncCollections<T extends object, O extends object>(collections: Iterable<Collection<T, O>>, options?: DriverMethodOptions): Promise<void>;
|
|
57
62
|
/** Counts entities matching the given condition. */
|
package/entity/BaseEntity.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Ref } from './Reference.js';
|
|
2
|
-
import type { AutoPath, EntityData, EntityDTO, Loaded, LoadedReference, AddEager, EntityKey, FromEntityType, IsSubset, MergeSelected, SerializeDTO } from '../typings.js';
|
|
2
|
+
import type { AutoPath, EntityData, EntityDTO, ExtractFieldsHint, Loaded, LoadedReference, AddEager, EntityKey, FromEntityType, IsSubset, MergeSelected, ResolveSerializeFields, SerializeDTO, SerializeFieldsKeepPK } from '../typings.js';
|
|
3
3
|
import { type AssignOptions } from './EntityAssigner.js';
|
|
4
4
|
import type { EntityLoaderOptions } from './EntityLoader.js';
|
|
5
5
|
import { type SerializeOptions } from '../serialization/EntitySerializer.js';
|
|
@@ -80,7 +80,7 @@ export declare abstract class BaseEntity {
|
|
|
80
80
|
/** Converts the entity to a plain object, including all properties regardless of serialization rules. */
|
|
81
81
|
toPOJO<Entity extends this = this>(): EntityDTO<Entity>;
|
|
82
82
|
/** Serializes the entity with control over which relations and fields to include or exclude. */
|
|
83
|
-
serialize<Entity extends this = this, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Naked, Hint, Exclude>): SerializeDTO<Naked, Hint, Exclude
|
|
83
|
+
serialize<Entity extends this = this, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Hint extends string = never, Exclude extends string = never, Fields extends string = never>(options?: SerializeOptions<Naked, Hint, Exclude, Fields>): SerializeDTO<Naked, Hint, Exclude, never, ResolveSerializeFields<Fields, ExtractFieldsHint<Entity>>, SerializeFieldsKeepPK<Fields>>;
|
|
84
84
|
/** Assigns the given data to this entity, updating its properties and relations. */
|
|
85
85
|
assign<Entity extends this, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Convert extends boolean = false, Data extends EntityData<Naked, Convert> | Partial<EntityDTO<Naked>> = EntityData<Naked, Convert> | Partial<EntityDTO<Naked>>>(data: Data & IsSubset<EntityData<Naked>, Data>, options?: AssignOptions<Convert>): MergeSelected<Entity, Naked, keyof Data & string>;
|
|
86
86
|
/** Initializes (refreshes) the entity by reloading it from the database. Returns null if not found. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PopulatePath } from '../enums.js';
|
|
2
2
|
import type { EntityManager } from '../EntityManager.js';
|
|
3
|
-
import type { Dictionary, EntityData, EntityDictionary, EntityMetadata, IHydrator, EntityKey, PopulateOptions, Primary, AutoPath, Ref, AddEager, LoadedReference, EntityDTO, Loaded, SerializeDTO, FromEntityType, IsSubset, MergeSelected } from '../typings.js';
|
|
3
|
+
import type { Dictionary, EntityData, EntityDictionary, EntityMetadata, IHydrator, EntityKey, PopulateOptions, Primary, AutoPath, Ref, AddEager, LoadedReference, EntityDTO, Loaded, ExtractFieldsHint, ResolveSerializeFields, SerializeDTO, SerializeFieldsKeepPK, FromEntityType, IsSubset, MergeSelected } from '../typings.js';
|
|
4
4
|
import { Reference } from './Reference.js';
|
|
5
5
|
import { type AssignOptions } from './EntityAssigner.js';
|
|
6
6
|
import type { EntityLoaderOptions } from './EntityLoader.js';
|
|
@@ -54,7 +54,7 @@ export declare class WrappedEntity<Entity extends object> {
|
|
|
54
54
|
/** Converts the entity to a plain object representation, optionally excluding specified fields. */
|
|
55
55
|
toObject<Ignored extends EntityKey<Entity> = never>(ignoreFields?: Ignored[]): Omit<EntityDTO<Entity>, Ignored>;
|
|
56
56
|
/** Serializes the entity with control over which relations and fields to include or exclude. */
|
|
57
|
-
serialize<Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Entity
|
|
57
|
+
serialize<Hint extends string = never, Exclude extends string = never, Fields extends string = never>(options?: SerializeOptions<FromEntityType<Entity>, Hint, Exclude, Fields>): SerializeDTO<FromEntityType<Entity>, Hint, Exclude, never, ResolveSerializeFields<Fields, ExtractFieldsHint<Entity>>, SerializeFieldsKeepPK<Fields>>;
|
|
58
58
|
/** Converts the entity to a plain object, including all properties regardless of serialization rules. */
|
|
59
59
|
toPOJO(): EntityDTO<Entity>;
|
|
60
60
|
/** Serializes the entity using its `toJSON` method (supports `JSON.stringify`). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.1.0-dev.
|
|
3
|
+
"version": "7.1.0-dev.3",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -1,21 +1,33 @@
|
|
|
1
|
-
import type { ArrayElement, AutoPath, CleanTypeConfig, ExtractFieldsHint, FromEntityType, SerializeDTO, TypeConfig, UnboxArray } from '../typings.js';
|
|
1
|
+
import type { ArrayElement, AutoPath, CleanTypeConfig, ExtractFieldsHint, FromEntityType, ResolveSerializeFields, SerializeDTO, SerializeFieldsKeepPK, TypeConfig, UnboxArray } from '../typings.js';
|
|
2
2
|
import { type PopulatePath } from '../enums.js';
|
|
3
|
-
/** Converts entity instances to plain DTOs via `serialize()`, with fine-grained control over populate, exclude, and serialization groups. */
|
|
3
|
+
/** Converts entity instances to plain DTOs via `serialize()`, with fine-grained control over populate, exclude, fields, and serialization groups. */
|
|
4
4
|
export declare class EntitySerializer {
|
|
5
|
-
/** Serializes an entity to a plain DTO, with fine-grained control over population, exclusion, groups, and custom types. */
|
|
6
|
-
static serialize<T extends object, P extends string = never, E extends string = never>(entity: T, options?: SerializeOptions<T, P, E>): SerializeDTO<T, P, E
|
|
5
|
+
/** Serializes an entity to a plain DTO, with fine-grained control over population, exclusion, fields, groups, and custom types. */
|
|
6
|
+
static serialize<T extends object, P extends string = never, E extends string = never, F extends string = never>(entity: T, options?: SerializeOptions<T, P, E, F>): SerializeDTO<T, P, E, never, ResolveSerializeFields<F>, SerializeFieldsKeepPK<F>>;
|
|
7
7
|
private static propertyName;
|
|
8
8
|
private static processProperty;
|
|
9
9
|
private static processCustomType;
|
|
10
10
|
private static extractChildOptions;
|
|
11
|
+
/**
|
|
12
|
+
* Extracts the nested `fields` whitelist for a child property. A bare parent name (`fields: ['books']`) or a
|
|
13
|
+
* wildcard removes the whitelist on the sub-tree (everything is included), so consumers don't have to repeat
|
|
14
|
+
* every field of the child. Otherwise dot-paths are stripped of the parent prefix and passed down.
|
|
15
|
+
*/
|
|
16
|
+
private static extractChildFields;
|
|
11
17
|
private static processEntity;
|
|
12
18
|
private static processCollection;
|
|
13
19
|
}
|
|
14
|
-
export interface SerializeOptions<T, P extends string = never, E extends string = never> {
|
|
20
|
+
export interface SerializeOptions<T, P extends string = never, E extends string = never, F extends string = never> {
|
|
15
21
|
/** Specify which relation should be serialized as populated and which as a FK. */
|
|
16
22
|
populate?: readonly AutoPath<T, P, `${PopulatePath.ALL}`>[];
|
|
17
23
|
/** Specify which properties should be omitted. */
|
|
18
24
|
exclude?: readonly AutoPath<T, E>[];
|
|
25
|
+
/**
|
|
26
|
+
* Whitelist of properties to serialize, supports dot-paths (e.g. `['name', 'books.title']`). When set, only the
|
|
27
|
+
* listed properties end up in the output, including primary keys. A bare property name keeps its entire sub-tree;
|
|
28
|
+
* a dot-path additionally narrows the nested object to the listed sub-keys. `exclude` takes precedence on conflict.
|
|
29
|
+
*/
|
|
30
|
+
fields?: readonly AutoPath<T, F, `${PopulatePath.ALL}`>[];
|
|
19
31
|
/** Enforce unpopulated references to be returned as objects, e.g. `{ author: { id: 1 } }` instead of `{ author: 1 }`. */
|
|
20
32
|
forceObject?: boolean;
|
|
21
33
|
/** Ignore custom property serializers. */
|
|
@@ -41,4 +53,4 @@ export interface SerializeOptions<T, P extends string = never, E extends string
|
|
|
41
53
|
* const dto2 = wrap(user).serialize({ exclude: ['id', 'email'], forceObject: true });
|
|
42
54
|
* ```
|
|
43
55
|
*/
|
|
44
|
-
export declare function serialize<Entity extends object, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Populate extends string = never, Exclude extends string = never, Config extends TypeConfig = never>(entity: Entity, options?: Config & SerializeOptions<UnboxArray<Entity>, Populate, Exclude>): Naked extends object[] ? SerializeDTO<ArrayElement<Naked>, Populate, Exclude, CleanTypeConfig<Config>>[] : SerializeDTO<Naked, Populate, Exclude, CleanTypeConfig<Config>, ExtractFieldsHint<Entity>>;
|
|
56
|
+
export declare function serialize<Entity extends object, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Populate extends string = never, Exclude extends string = never, Fields extends string = never, Config extends TypeConfig = never>(entity: Entity, options?: Config & SerializeOptions<UnboxArray<Entity>, Populate, Exclude, Fields>): Naked extends object[] ? SerializeDTO<ArrayElement<Naked>, Populate, Exclude, CleanTypeConfig<Config>, ResolveSerializeFields<Fields, ExtractFieldsHint<Entity>>, SerializeFieldsKeepPK<Fields>>[] : SerializeDTO<Naked, Populate, Exclude, CleanTypeConfig<Config>, ResolveSerializeFields<Fields, ExtractFieldsHint<Entity>>, SerializeFieldsKeepPK<Fields>>;
|
|
@@ -4,13 +4,19 @@ import { ReferenceKind } from '../enums.js';
|
|
|
4
4
|
import { Reference } from '../entity/Reference.js';
|
|
5
5
|
import { SerializationContext } from './SerializationContext.js';
|
|
6
6
|
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
7
|
+
/** Returns true when any entry in `items` is `propName`, the wildcard `*`, or a dot-path under `propName`. */
|
|
8
|
+
function matchesPath(items, propName) {
|
|
9
|
+
return items.some(item => item === propName || item === '*' || item.startsWith(propName + '.'));
|
|
10
|
+
}
|
|
7
11
|
function isVisible(meta, propName, options) {
|
|
8
12
|
const prop = meta.properties[propName];
|
|
9
13
|
if (options.groups && prop?.groups) {
|
|
10
14
|
return prop.groups.some(g => options.groups.includes(g));
|
|
11
15
|
}
|
|
12
|
-
if (Array.isArray(options.
|
|
13
|
-
|
|
16
|
+
if (Array.isArray(options.fields) && options.fields.length > 0 && !matchesPath(options.fields, propName)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (Array.isArray(options.populate) && matchesPath(options.populate, propName)) {
|
|
14
20
|
return true;
|
|
15
21
|
}
|
|
16
22
|
if (options.exclude?.find(item => item === propName)) {
|
|
@@ -22,7 +28,8 @@ function isVisible(meta, propName, options) {
|
|
|
22
28
|
}
|
|
23
29
|
function isPopulated(propName, options) {
|
|
24
30
|
if (typeof options.populate !== 'boolean' &&
|
|
25
|
-
options.populate
|
|
31
|
+
Array.isArray(options.populate) &&
|
|
32
|
+
matchesPath(options.populate, propName)) {
|
|
26
33
|
return true;
|
|
27
34
|
}
|
|
28
35
|
if (typeof options.populate === 'boolean') {
|
|
@@ -30,9 +37,9 @@ function isPopulated(propName, options) {
|
|
|
30
37
|
}
|
|
31
38
|
return false;
|
|
32
39
|
}
|
|
33
|
-
/** Converts entity instances to plain DTOs via `serialize()`, with fine-grained control over populate, exclude, and serialization groups. */
|
|
40
|
+
/** Converts entity instances to plain DTOs via `serialize()`, with fine-grained control over populate, exclude, fields, and serialization groups. */
|
|
34
41
|
export class EntitySerializer {
|
|
35
|
-
/** Serializes an entity to a plain DTO, with fine-grained control over population, exclusion, groups, and custom types. */
|
|
42
|
+
/** Serializes an entity to a plain DTO, with fine-grained control over population, exclusion, fields, groups, and custom types. */
|
|
36
43
|
static serialize(entity, options = {}) {
|
|
37
44
|
const wrapped = helper(entity);
|
|
38
45
|
const meta = wrapped.__meta;
|
|
@@ -184,8 +191,27 @@ export class EntitySerializer {
|
|
|
184
191
|
? Utils.extractChildElements(options.populate, prop, '*')
|
|
185
192
|
: options.populate,
|
|
186
193
|
exclude: Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop) : options.exclude,
|
|
194
|
+
fields: Array.isArray(options.fields) ? this.extractChildFields(options.fields, prop) : options.fields,
|
|
187
195
|
};
|
|
188
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Extracts the nested `fields` whitelist for a child property. A bare parent name (`fields: ['books']`) or a
|
|
199
|
+
* wildcard removes the whitelist on the sub-tree (everything is included), so consumers don't have to repeat
|
|
200
|
+
* every field of the child. Otherwise dot-paths are stripped of the parent prefix and passed down.
|
|
201
|
+
*/
|
|
202
|
+
static extractChildFields(fields, prop) {
|
|
203
|
+
const out = [];
|
|
204
|
+
const dotPrefix = prop + '.';
|
|
205
|
+
for (const field of fields) {
|
|
206
|
+
if (field === prop || field === '*') {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
if (field.startsWith(dotPrefix)) {
|
|
210
|
+
out.push(field.substring(dotPrefix.length));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return out;
|
|
214
|
+
}
|
|
189
215
|
static processEntity(prop, entity, platform, options) {
|
|
190
216
|
const child = Reference.unwrapReference(entity[prop.name]);
|
|
191
217
|
const wrapped = helper(child);
|
package/typings.d.ts
CHANGED
|
@@ -341,7 +341,7 @@ export interface IWrappedEntity<Entity extends object> {
|
|
|
341
341
|
toObject<Ignored extends EntityKey<Entity>>(ignoreFields: Ignored[]): Omit<EntityDTO<Entity>, Ignored>;
|
|
342
342
|
toJSON(...args: any[]): EntityDTO<Entity>;
|
|
343
343
|
toPOJO(): EntityDTO<Entity>;
|
|
344
|
-
serialize<Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Naked, Hint, Exclude>): SerializeDTO<Naked, Hint, Exclude
|
|
344
|
+
serialize<Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Hint extends string = never, Exclude extends string = never, Fields extends string = never>(options?: SerializeOptions<Naked, Hint, Exclude, Fields>): SerializeDTO<Naked, Hint, Exclude, never, ResolveSerializeFields<Fields, ExtractFieldsHint<Entity>>, SerializeFieldsKeepPK<Fields>>;
|
|
345
345
|
setSerializationContext<Hint extends string = never, Fields extends string = never, Exclude extends string = never>(options: LoadHint<Entity, Hint, Fields, Exclude>): void;
|
|
346
346
|
assign<Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Convert extends boolean = false, Data extends EntityData<Naked, Convert> | Partial<EntityDTO<Naked>> = EntityData<Naked, Convert> | Partial<EntityDTO<Naked>>>(data: Data & IsSubset<EntityData<Naked, Convert>, Data>, options?: AssignOptions<Convert>): MergeSelected<Entity, Naked, keyof Data & string>;
|
|
347
347
|
getSchema(): string | undefined;
|
|
@@ -534,17 +534,32 @@ export type EntityDTOFlat<T, C extends TypeConfig = never> = {
|
|
|
534
534
|
type SerializeTopHints<H extends string> = H extends `${infer Top}.${string}` ? Top : H;
|
|
535
535
|
type SerializeSubHints<K extends string, H extends string> = H extends `${K}.${infer Rest}` ? Rest : never;
|
|
536
536
|
type SerializeSubFields<K extends string, F extends string> = K extends F ? '*' : [F extends `${K & string}.${infer Rest}` ? Rest : never] extends [never] ? '*' : F extends `${K & string}.${infer Rest}` ? Rest : never;
|
|
537
|
-
type SerializeFieldsFilter<T, K extends keyof T, F extends string> = K extends Prefix<T, F> | PrimaryProperty<T> ? K : never;
|
|
537
|
+
type SerializeFieldsFilter<T, K extends keyof T, F extends string, KeepPK extends boolean = true> = K extends Prefix<T, F> | (KeepPK extends true ? PrimaryProperty<T> : never) ? K : never;
|
|
538
538
|
type SerializePropValue<T, K extends keyof T, H extends string, C extends TypeConfig = never> = K & string extends SerializeTopHints<H> ? NonNullable<T[K]> extends CollectionShape<infer U> ? SerializeDTO<U & object, SerializeSubHints<K & string, H>, never, C>[] : SerializeDTO<ExpandProperty<T[K]>, SerializeSubHints<K & string, H>, never, C> | Extract<T[K], null | undefined> : EntityDTOProp<T, T[K], C>;
|
|
539
|
-
type SerializePropValueWithFields<T, K extends keyof T, H extends string, C extends TypeConfig, F extends string> = K & string extends SerializeTopHints<H> ? NonNullable<T[K]> extends CollectionShape<infer U> ? SerializeDTO<U & object, SerializeSubHints<K & string, H>, never, C, SerializeSubFields<K & string, F
|
|
539
|
+
type SerializePropValueWithFields<T, K extends keyof T, H extends string, C extends TypeConfig, F extends string, KeepPK extends boolean = true> = K & string extends SerializeTopHints<H> ? NonNullable<T[K]> extends CollectionShape<infer U> ? SerializeDTO<U & object, SerializeSubHints<K & string, H>, never, C, SerializeSubFields<K & string, F>, KeepPK>[] : SerializeDTO<ExpandProperty<T[K]>, SerializeSubHints<K & string, H>, never, C, SerializeSubFields<K & string, F>, KeepPK> | Extract<T[K], null | undefined> : EntityDTOProp<T, T[K], C>;
|
|
540
|
+
/**
|
|
541
|
+
* Resolves a user-provided `Fields` generic to the value passed to `SerializeDTO`'s `F` parameter:
|
|
542
|
+
* `never` (no `fields` option) falls back to `Default` (typically `'*'` or the entity's `ExtractFieldsHint`).
|
|
543
|
+
*/
|
|
544
|
+
export type ResolveSerializeFields<Fields extends string, Default extends string = '*'> = [Fields] extends [never] ? Default : Fields;
|
|
545
|
+
/**
|
|
546
|
+
* Whether the explicit `serialize()` path should keep primary keys in the output. When the user does not provide
|
|
547
|
+
* a `fields` option (`Fields = never`), behave like `toObject()` and keep PKs; when they do, treat it as a strict
|
|
548
|
+
* whitelist so PKs are dropped unless listed.
|
|
549
|
+
*/
|
|
550
|
+
export type SerializeFieldsKeepPK<Fields extends string> = [Fields] extends [never] ? true : false;
|
|
540
551
|
/**
|
|
541
552
|
* Return type of `serialize()`. Combines Loaded + EntityDTO in a single pass for better performance.
|
|
542
553
|
* Respects populate hints (`H`), exclude hints (`E`), and fields hints (`F`).
|
|
554
|
+
*
|
|
555
|
+
* `KeepPK` controls whether primary keys are auto-included when narrowing by `F`. The `toObject()` path keeps the
|
|
556
|
+
* default `true` (PKs always present); the explicit `serialize()` path passes `false` so the user-provided `fields`
|
|
557
|
+
* option is a strict whitelist that can drop PKs from the output.
|
|
543
558
|
*/
|
|
544
|
-
export type SerializeDTO<T, H extends string = never, E extends string = never, C extends TypeConfig = never, F extends string = '*'> = string extends H ? EntityDTOFlat<T, C> : [F] extends ['*'] ? {
|
|
559
|
+
export type SerializeDTO<T, H extends string = never, E extends string = never, C extends TypeConfig = never, F extends string = '*', KeepPK extends boolean = true> = string extends H ? EntityDTOFlat<T, C> : [F] extends ['*'] ? {
|
|
545
560
|
[K in keyof T as ExcludeHidden<T, K> & CleanKeys<T, K> & (IsNever<E> extends true ? K : Exclude<K, E>)]: SerializePropValue<T, K, H, C> | Extract<T[K], null | undefined>;
|
|
546
561
|
} : {
|
|
547
|
-
[K in keyof T as ExcludeHidden<T, K> & CleanKeys<T, K> & (IsNever<E> extends true ? K : Exclude<K, E>) & SerializeFieldsFilter<T, K, F>]: SerializePropValueWithFields<T, K, H, C, F> | Extract<T[K], null | undefined>;
|
|
562
|
+
[K in keyof T as ExcludeHidden<T, K> & CleanKeys<T, K> & (IsNever<E> extends true ? K : Exclude<K, E>) & SerializeFieldsFilter<T, K, F, KeepPK>]: SerializePropValueWithFields<T, K, H, C, F, KeepPK> | Extract<T[K], null | undefined>;
|
|
548
563
|
};
|
|
549
564
|
type TargetKeys<T> = T extends EntityClass<infer P> ? keyof P : keyof T;
|
|
550
565
|
type PropertyName<T> = IsUnknown<T> extends false ? TargetKeys<T> : string;
|
package/utils/Utils.d.ts
CHANGED
|
@@ -117,7 +117,7 @@ export declare class Utils {
|
|
|
117
117
|
* Gets string name of given class.
|
|
118
118
|
*/
|
|
119
119
|
static className<T>(classOrName: string | EntityName<T>): string;
|
|
120
|
-
static extractChildElements(items: string[], prefix: string, allSymbol?: string): string[];
|
|
120
|
+
static extractChildElements(items: readonly string[], prefix: string, allSymbol?: string): string[];
|
|
121
121
|
/**
|
|
122
122
|
* Tries to detect TypeScript support.
|
|
123
123
|
*/
|
package/utils/Utils.js
CHANGED
|
@@ -141,7 +141,7 @@ export function parseJsonSafe(value) {
|
|
|
141
141
|
/** Collection of general-purpose utility methods used throughout the ORM. */
|
|
142
142
|
export class Utils {
|
|
143
143
|
static PK_SEPARATOR = '~~~';
|
|
144
|
-
static #ORM_VERSION = '7.1.0-dev.
|
|
144
|
+
static #ORM_VERSION = '7.1.0-dev.3';
|
|
145
145
|
/**
|
|
146
146
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
147
147
|
*/
|