@mikro-orm/core 7.0.0-dev.6 → 7.0.0-dev.60
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 +85 -32
- package/EntityManager.js +281 -178
- package/MikroORM.d.ts +8 -8
- package/MikroORM.js +31 -74
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +2 -1
- package/cache/FileCacheAdapter.js +5 -4
- package/connections/Connection.d.ts +11 -7
- package/connections/Connection.js +16 -13
- package/decorators/Embedded.d.ts +5 -11
- package/decorators/Entity.d.ts +18 -3
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/ManyToMany.d.ts +2 -0
- package/decorators/ManyToOne.d.ts +4 -0
- package/decorators/OneToOne.d.ts +4 -0
- package/decorators/Property.d.ts +53 -9
- package/decorators/Transactional.d.ts +3 -1
- package/decorators/Transactional.js +6 -3
- package/decorators/index.d.ts +1 -1
- package/drivers/DatabaseDriver.d.ts +11 -5
- package/drivers/DatabaseDriver.js +13 -4
- package/drivers/IDatabaseDriver.d.ts +29 -5
- package/entity/ArrayCollection.d.ts +6 -4
- package/entity/ArrayCollection.js +26 -9
- package/entity/BaseEntity.d.ts +0 -1
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +3 -4
- package/entity/Collection.js +34 -17
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +9 -1
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +40 -22
- package/entity/EntityHelper.js +25 -8
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +69 -36
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityValidator.js +2 -2
- package/entity/Reference.d.ts +9 -7
- package/entity/Reference.js +32 -5
- package/entity/WrappedEntity.d.ts +0 -2
- package/entity/WrappedEntity.js +1 -5
- package/entity/defineEntity.d.ts +555 -0
- package/entity/defineEntity.js +529 -0
- package/entity/index.d.ts +2 -0
- package/entity/index.js +2 -0
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +18 -5
- package/enums.js +13 -0
- package/errors.d.ts +6 -1
- package/errors.js +14 -4
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +35 -24
- package/index.d.ts +2 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/SimpleLogger.d.ts +1 -1
- package/metadata/EntitySchema.d.ts +8 -4
- package/metadata/EntitySchema.js +40 -20
- package/metadata/MetadataDiscovery.d.ts +5 -7
- package/metadata/MetadataDiscovery.js +150 -155
- package/metadata/MetadataStorage.js +1 -1
- package/metadata/MetadataValidator.js +4 -3
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +39 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
- package/naming-strategy/AbstractNamingStrategy.js +7 -1
- package/naming-strategy/NamingStrategy.d.ts +11 -1
- package/package.json +14 -7
- package/platforms/Platform.d.ts +5 -8
- package/platforms/Platform.js +4 -17
- package/serialization/EntitySerializer.d.ts +2 -0
- package/serialization/EntitySerializer.js +29 -11
- package/serialization/EntityTransformer.js +22 -12
- package/serialization/SerializationContext.js +14 -11
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +3 -0
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +2 -1
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +1 -1
- package/types/DoubleType.js +1 -1
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/Type.d.ts +2 -1
- package/types/Type.js +1 -1
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +0 -3
- package/types/index.d.ts +1 -1
- package/typings.d.ts +94 -50
- package/typings.js +31 -31
- package/unit-of-work/ChangeSetComputer.js +8 -3
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +37 -16
- package/unit-of-work/UnitOfWork.d.ts +8 -1
- package/unit-of-work/UnitOfWork.js +110 -53
- package/utils/AbstractSchemaGenerator.js +3 -1
- package/utils/Configuration.d.ts +201 -184
- package/utils/Configuration.js +143 -151
- package/utils/ConfigurationLoader.d.ts +9 -22
- package/utils/ConfigurationLoader.js +53 -76
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +53 -7
- package/utils/EntityComparator.d.ts +8 -4
- package/utils/EntityComparator.js +105 -58
- package/utils/QueryHelper.d.ts +9 -1
- package/utils/QueryHelper.js +66 -5
- package/utils/RawQueryFragment.d.ts +36 -4
- package/utils/RawQueryFragment.js +34 -13
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +13 -12
- package/utils/Utils.js +106 -66
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/upsert-utils.d.ts +7 -2
- package/utils/upsert-utils.js +52 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import { Utils } from '../utils/Utils.js';
|
|
3
|
+
import { MetadataStorage } from './MetadataStorage.js';
|
|
4
|
+
import { EntitySchema } from './EntitySchema.js';
|
|
5
|
+
async function getEntityClassOrSchema(filepath, allTargets, baseDir) {
|
|
6
|
+
const path = Utils.normalizePath(baseDir, filepath);
|
|
7
|
+
const exports = await Utils.dynamicImport(path);
|
|
8
|
+
const targets = Object.values(exports);
|
|
9
|
+
// ignore class implementations that are linked from an EntitySchema
|
|
10
|
+
for (const item of targets) {
|
|
11
|
+
if (item instanceof EntitySchema) {
|
|
12
|
+
for (const item2 of targets) {
|
|
13
|
+
if (item.meta.class === item2) {
|
|
14
|
+
targets.splice(targets.indexOf(item2), 1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
for (const item of targets) {
|
|
20
|
+
const validTarget = item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name));
|
|
21
|
+
if (validTarget && !allTargets.has(item)) {
|
|
22
|
+
allTargets.set(item, path);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function discoverEntities(paths, options) {
|
|
27
|
+
paths = Utils.asArray(paths).map(path => Utils.normalizePath(path));
|
|
28
|
+
const baseDir = options?.baseDir ?? process.cwd();
|
|
29
|
+
const files = Utils.glob(paths, Utils.normalizePath(baseDir));
|
|
30
|
+
const found = new Map();
|
|
31
|
+
for (const filepath of files) {
|
|
32
|
+
const filename = basename(filepath);
|
|
33
|
+
if (!filename.match(/\.[cm]?[jt]s$/) || filename.match(/\.d\.[cm]?ts/)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
await getEntityClassOrSchema(filepath, found, baseDir);
|
|
37
|
+
}
|
|
38
|
+
return found.keys();
|
|
39
|
+
}
|
|
@@ -12,7 +12,11 @@ export declare abstract class AbstractNamingStrategy implements NamingStrategy {
|
|
|
12
12
|
/**
|
|
13
13
|
* @inheritDoc
|
|
14
14
|
*/
|
|
15
|
-
getEnumClassName(columnName: string, tableName: string, schemaName?: string): string;
|
|
15
|
+
getEnumClassName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* @inheritDoc
|
|
18
|
+
*/
|
|
19
|
+
getEnumTypeName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
16
20
|
/**
|
|
17
21
|
* @inheritDoc
|
|
18
22
|
*/
|
|
@@ -48,7 +48,13 @@ export class AbstractNamingStrategy {
|
|
|
48
48
|
* @inheritDoc
|
|
49
49
|
*/
|
|
50
50
|
getEnumClassName(columnName, tableName, schemaName) {
|
|
51
|
-
return this.getEntityName(`${tableName}_${columnName}
|
|
51
|
+
return this.getEntityName(tableName ? `${tableName}_${columnName}` : columnName, schemaName);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @inheritDoc
|
|
55
|
+
*/
|
|
56
|
+
getEnumTypeName(columnName, tableName, schemaName) {
|
|
57
|
+
return 'T' + this.getEnumClassName(columnName, tableName, schemaName);
|
|
52
58
|
}
|
|
53
59
|
/**
|
|
54
60
|
* @inheritDoc
|
|
@@ -25,7 +25,17 @@ export interface NamingStrategy {
|
|
|
25
25
|
*
|
|
26
26
|
* @return A new class name that will be used for the enum.
|
|
27
27
|
*/
|
|
28
|
-
getEnumClassName(columnName: string, tableName: string, schemaName?: string): string;
|
|
28
|
+
getEnumClassName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Get an enum type name. Used with `enumType: 'dictionary'` and `enumType: 'union-type'` entity generator option.
|
|
31
|
+
*
|
|
32
|
+
* @param columnName The column name which has the enum.
|
|
33
|
+
* @param tableName The table name of the column.
|
|
34
|
+
* @param schemaName The schema name of the column.
|
|
35
|
+
*
|
|
36
|
+
* @return A new type name that will be used for the enum.
|
|
37
|
+
*/
|
|
38
|
+
getEnumTypeName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
29
39
|
/**
|
|
30
40
|
* Get an enum option name for a given enum value.
|
|
31
41
|
*
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "7.0.0-dev.
|
|
4
|
+
"version": "7.0.0-dev.60",
|
|
5
5
|
"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.",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./package.json": "./package.json",
|
|
8
|
-
".": "./index.js"
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./file-discovery": "./metadata/discover-entities.js"
|
|
9
10
|
},
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
},
|
|
40
41
|
"homepage": "https://mikro-orm.io",
|
|
41
42
|
"engines": {
|
|
42
|
-
"node": ">= 22.
|
|
43
|
+
"node": ">= 22.17.0"
|
|
43
44
|
},
|
|
44
45
|
"scripts": {
|
|
45
46
|
"build": "yarn clean && yarn compile && yarn copy",
|
|
@@ -51,11 +52,17 @@
|
|
|
51
52
|
"access": "public"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
|
-
"
|
|
55
|
-
"dotenv": "16.4.7",
|
|
55
|
+
"dotenv": "17.2.3",
|
|
56
56
|
"esprima": "4.0.1",
|
|
57
|
-
"
|
|
58
|
-
"mikro-orm": "7.0.0-dev.6",
|
|
57
|
+
"mikro-orm": "7.0.0-dev.60",
|
|
59
58
|
"reflect-metadata": "0.2.2"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"dataloader": "2.2.3"
|
|
62
|
+
},
|
|
63
|
+
"peerDependenciesMeta": {
|
|
64
|
+
"dataloader": {
|
|
65
|
+
"optional": true
|
|
66
|
+
}
|
|
60
67
|
}
|
|
61
68
|
}
|
package/platforms/Platform.d.ts
CHANGED
|
@@ -48,11 +48,6 @@ export declare abstract class Platform {
|
|
|
48
48
|
* Converts scalar primary key representation to native driver wrapper (e.g. string to mongodb's ObjectId)
|
|
49
49
|
*/
|
|
50
50
|
denormalizePrimaryKey(data: IPrimaryKey): IPrimaryKey;
|
|
51
|
-
/**
|
|
52
|
-
* Used when serializing via toObject and toJSON methods, allows to use different PK field name (like `id` instead of `_id`)
|
|
53
|
-
*/
|
|
54
|
-
getSerializedPrimaryKeyField(field: string): string;
|
|
55
|
-
usesDifferentSerializedPrimaryKey(): boolean;
|
|
56
51
|
/**
|
|
57
52
|
* Returns the SQL specific for the platform to get the current timestamp
|
|
58
53
|
*/
|
|
@@ -158,7 +153,7 @@ export declare abstract class Platform {
|
|
|
158
153
|
getFullTextIndexExpression(indexName: string, schemaName: string | undefined, tableName: string, columns: SimpleColumnMeta[]): string;
|
|
159
154
|
convertsJsonAutomatically(): boolean;
|
|
160
155
|
convertJsonToDatabaseValue(value: unknown, context?: TransformContext): unknown;
|
|
161
|
-
convertJsonToJSValue(value: unknown,
|
|
156
|
+
convertJsonToJSValue(value: unknown, context?: TransformContext): unknown;
|
|
162
157
|
convertDateToJSValue(value: string | Date): string;
|
|
163
158
|
convertIntervalToJSValue(value: string): unknown;
|
|
164
159
|
convertIntervalToDatabaseValue(value: unknown): unknown;
|
|
@@ -175,7 +170,9 @@ export declare abstract class Platform {
|
|
|
175
170
|
getExtension<T>(extensionName: string, extensionKey: string, moduleName: string, em: EntityManager): T;
|
|
176
171
|
getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): ISchemaGenerator;
|
|
177
172
|
processDateProperty(value: unknown): string | number | Date;
|
|
178
|
-
quoteIdentifier(id: string
|
|
173
|
+
quoteIdentifier(id: string | {
|
|
174
|
+
toString: () => string;
|
|
175
|
+
}, quote?: string): string;
|
|
179
176
|
quoteValue(value: any): string;
|
|
180
177
|
escape(value: any): string;
|
|
181
178
|
formatQuery(sql: string, params: readonly any[]): string;
|
|
@@ -193,7 +190,7 @@ export declare abstract class Platform {
|
|
|
193
190
|
getDefaultPrimaryName(tableName: string, columns: string[]): string;
|
|
194
191
|
supportsCustomPrimaryKeyNames(): boolean;
|
|
195
192
|
isPopulated<T>(key: string, populate: readonly PopulateOptions<T>[] | boolean): boolean;
|
|
196
|
-
shouldHaveColumn<T>(prop: EntityProperty<T>, populate: readonly PopulateOptions<T>[] | boolean, exclude?: string[], includeFormulas?: boolean): boolean;
|
|
193
|
+
shouldHaveColumn<T>(prop: EntityProperty<T>, populate: readonly PopulateOptions<T>[] | boolean, exclude?: string[], includeFormulas?: boolean, ignoreInlineEmbeddables?: boolean): boolean;
|
|
197
194
|
/**
|
|
198
195
|
* Currently not supported due to how knex does complex sqlite diffing (always based on current schema)
|
|
199
196
|
*/
|
package/platforms/Platform.js
CHANGED
|
@@ -78,15 +78,6 @@ export class Platform {
|
|
|
78
78
|
denormalizePrimaryKey(data) {
|
|
79
79
|
return data;
|
|
80
80
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Used when serializing via toObject and toJSON methods, allows to use different PK field name (like `id` instead of `_id`)
|
|
83
|
-
*/
|
|
84
|
-
getSerializedPrimaryKeyField(field) {
|
|
85
|
-
return field;
|
|
86
|
-
}
|
|
87
|
-
usesDifferentSerializedPrimaryKey() {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
81
|
/**
|
|
91
82
|
* Returns the SQL specific for the platform to get the current timestamp
|
|
92
83
|
*/
|
|
@@ -133,7 +124,7 @@ export class Platform {
|
|
|
133
124
|
return true;
|
|
134
125
|
}
|
|
135
126
|
isBigIntProperty(prop) {
|
|
136
|
-
return prop.columnTypes
|
|
127
|
+
return prop.columnTypes?.[0] === 'bigint';
|
|
137
128
|
}
|
|
138
129
|
getDefaultSchemaName() {
|
|
139
130
|
return undefined;
|
|
@@ -288,11 +279,7 @@ export class Platform {
|
|
|
288
279
|
convertJsonToDatabaseValue(value, context) {
|
|
289
280
|
return JSON.stringify(value);
|
|
290
281
|
}
|
|
291
|
-
convertJsonToJSValue(value,
|
|
292
|
-
const isObjectEmbedded = prop.embedded && prop.object;
|
|
293
|
-
if ((this.convertsJsonAutomatically() || isObjectEmbedded) && ['json', 'jsonb', this.getJsonDeclarationSQL()].includes(prop.columnTypes[0])) {
|
|
294
|
-
return value;
|
|
295
|
-
}
|
|
282
|
+
convertJsonToJSValue(value, context) {
|
|
296
283
|
return parseJsonSafe(value);
|
|
297
284
|
}
|
|
298
285
|
convertDateToJSValue(value) {
|
|
@@ -455,7 +442,7 @@ export class Platform {
|
|
|
455
442
|
isPopulated(key, populate) {
|
|
456
443
|
return populate === true || (populate !== false && populate.some(p => p.field === key || p.all));
|
|
457
444
|
}
|
|
458
|
-
shouldHaveColumn(prop, populate, exclude, includeFormulas = true) {
|
|
445
|
+
shouldHaveColumn(prop, populate, exclude, includeFormulas = true, ignoreInlineEmbeddables = true) {
|
|
459
446
|
if (exclude?.includes(prop.name)) {
|
|
460
447
|
return false;
|
|
461
448
|
}
|
|
@@ -475,7 +462,7 @@ export class Platform {
|
|
|
475
462
|
return true;
|
|
476
463
|
}
|
|
477
464
|
if (prop.kind === ReferenceKind.EMBEDDED) {
|
|
478
|
-
return
|
|
465
|
+
return prop.object || ignoreInlineEmbeddables;
|
|
479
466
|
}
|
|
480
467
|
return prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner;
|
|
481
468
|
}
|
|
@@ -17,6 +17,8 @@ export interface SerializeOptions<T, P extends string = never, E extends string
|
|
|
17
17
|
forceObject?: boolean;
|
|
18
18
|
/** Ignore custom property serializers. */
|
|
19
19
|
ignoreSerializers?: boolean;
|
|
20
|
+
/** Include properties marked as `hidden`. */
|
|
21
|
+
includeHidden?: boolean;
|
|
20
22
|
/** Skip properties with `null` value. */
|
|
21
23
|
skipNull?: boolean;
|
|
22
24
|
/** Only include properties for a specific group. If a property does not specify any group, it will be included, otherwise only properties with a matching group are included. */
|
|
@@ -15,8 +15,8 @@ function isVisible(meta, propName, options) {
|
|
|
15
15
|
if (options.exclude?.find(item => item === propName)) {
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
|
-
const visible = prop && !prop.hidden;
|
|
19
|
-
const prefixed = prop && !prop.primary && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
|
|
18
|
+
const visible = prop && !(prop.hidden && !options.includeHidden);
|
|
19
|
+
const prefixed = prop && !prop.primary && !prop.accessor && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
|
|
20
20
|
return visible && !prefixed;
|
|
21
21
|
}
|
|
22
22
|
function isPopulated(propName, options) {
|
|
@@ -41,13 +41,31 @@ export class EntitySerializer {
|
|
|
41
41
|
}
|
|
42
42
|
const root = wrapped.__serializationContext.root;
|
|
43
43
|
const ret = {};
|
|
44
|
-
const
|
|
45
|
-
|
|
44
|
+
const props = new Set();
|
|
45
|
+
if (meta.serializedPrimaryKey && !meta.compositePK) {
|
|
46
|
+
props.add(meta.serializedPrimaryKey);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
meta.primaryKeys.forEach(pk => props.add(pk));
|
|
50
|
+
}
|
|
51
|
+
if (wrapped.isInitialized() || !wrapped.hasPrimaryKey()) {
|
|
52
|
+
const entityKeys = new Set(Object.keys(entity));
|
|
53
|
+
for (const prop of meta.props) {
|
|
54
|
+
if (entityKeys.has(prop.name) || (prop.getter && prop.accessor === prop.name)) {
|
|
55
|
+
props.add(prop.name);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
for (const key of entityKeys) {
|
|
59
|
+
if (!meta.properties[key]) {
|
|
60
|
+
props.add(key);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
46
64
|
const visited = root.visited.has(entity);
|
|
47
65
|
if (!visited) {
|
|
48
66
|
root.visited.add(entity);
|
|
49
67
|
}
|
|
50
|
-
for (const prop of
|
|
68
|
+
for (const prop of props) {
|
|
51
69
|
if (!isVisible(meta, prop, options)) {
|
|
52
70
|
continue;
|
|
53
71
|
}
|
|
@@ -67,7 +85,7 @@ export class EntitySerializer {
|
|
|
67
85
|
}
|
|
68
86
|
const visible = typeof val !== 'undefined' && !(val === null && options.skipNull);
|
|
69
87
|
if (visible) {
|
|
70
|
-
ret[this.propertyName(meta, prop
|
|
88
|
+
ret[this.propertyName(meta, prop)] = val;
|
|
71
89
|
}
|
|
72
90
|
}
|
|
73
91
|
if (contextCreated) {
|
|
@@ -81,26 +99,26 @@ export class EntitySerializer {
|
|
|
81
99
|
if (prop.getterName != null) {
|
|
82
100
|
const visible = entity[prop.getterName] instanceof Function && isVisible(meta, prop.name, options);
|
|
83
101
|
if (visible) {
|
|
84
|
-
ret[this.propertyName(meta, prop.name
|
|
102
|
+
ret[this.propertyName(meta, prop.name)] = this.processProperty(prop.getterName, entity, options);
|
|
85
103
|
}
|
|
86
104
|
}
|
|
87
105
|
else {
|
|
88
106
|
// decorated getters
|
|
89
107
|
const visible = typeof entity[prop.name] !== 'undefined' && isVisible(meta, prop.name, options);
|
|
90
108
|
if (visible) {
|
|
91
|
-
ret[this.propertyName(meta, prop.name
|
|
109
|
+
ret[this.propertyName(meta, prop.name)] = this.processProperty(prop.name, entity, options);
|
|
92
110
|
}
|
|
93
111
|
}
|
|
94
112
|
}
|
|
95
113
|
return ret;
|
|
96
114
|
}
|
|
97
|
-
static propertyName(meta, prop
|
|
115
|
+
static propertyName(meta, prop) {
|
|
98
116
|
/* v8 ignore next 3 */
|
|
99
117
|
if (meta.properties[prop]?.serializedName) {
|
|
100
118
|
return meta.properties[prop].serializedName;
|
|
101
119
|
}
|
|
102
|
-
if (meta.properties[prop]?.primary &&
|
|
103
|
-
return
|
|
120
|
+
if (meta.properties[prop]?.primary && meta.serializedPrimaryKey) {
|
|
121
|
+
return meta.serializedPrimaryKey;
|
|
104
122
|
}
|
|
105
123
|
return prop;
|
|
106
124
|
}
|
|
@@ -6,7 +6,7 @@ import { isRaw } from '../utils/RawQueryFragment.js';
|
|
|
6
6
|
function isVisible(meta, propName, ignoreFields = []) {
|
|
7
7
|
const prop = meta.properties[propName];
|
|
8
8
|
const visible = prop && !prop.hidden;
|
|
9
|
-
const prefixed = prop && !prop.primary && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
|
|
9
|
+
const prefixed = prop && !prop.primary && !prop.accessor && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
|
|
10
10
|
return visible && !prefixed && !ignoreFields.includes(propName);
|
|
11
11
|
}
|
|
12
12
|
export class EntityTransformer {
|
|
@@ -27,22 +27,32 @@ export class EntityTransformer {
|
|
|
27
27
|
const root = wrapped.__serializationContext.root;
|
|
28
28
|
const meta = wrapped.__meta;
|
|
29
29
|
const ret = {};
|
|
30
|
-
const
|
|
30
|
+
const props = new Set();
|
|
31
31
|
if (meta.serializedPrimaryKey && !meta.compositePK) {
|
|
32
|
-
|
|
32
|
+
props.add(meta.serializedPrimaryKey);
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
|
-
meta.primaryKeys.forEach(pk =>
|
|
35
|
+
meta.primaryKeys.forEach(pk => props.add(pk));
|
|
36
36
|
}
|
|
37
37
|
if (wrapped.isInitialized() || !wrapped.hasPrimaryKey()) {
|
|
38
|
-
|
|
38
|
+
const entityKeys = new Set(Object.keys(entity));
|
|
39
|
+
for (const prop of meta.props) {
|
|
40
|
+
if (entityKeys.has(prop.name) || (prop.getter && prop.accessor === prop.name)) {
|
|
41
|
+
props.add(prop.name);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const key of entityKeys) {
|
|
45
|
+
if (!meta.properties[key]) {
|
|
46
|
+
props.add(key);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
39
49
|
}
|
|
40
50
|
const visited = root.visited.has(entity);
|
|
41
51
|
const includePrimaryKeys = wrapped.__config.get('serialization').includePrimaryKeys;
|
|
42
52
|
if (!visited) {
|
|
43
53
|
root.visited.add(entity);
|
|
44
54
|
}
|
|
45
|
-
for (const prop of
|
|
55
|
+
for (const prop of props) {
|
|
46
56
|
const visible = raw ? meta.properties[prop] : isVisible(meta, prop, ignoreFields);
|
|
47
57
|
if (!visible) {
|
|
48
58
|
continue;
|
|
@@ -67,7 +77,7 @@ export class EntityTransformer {
|
|
|
67
77
|
if (typeof val === 'undefined') {
|
|
68
78
|
continue;
|
|
69
79
|
}
|
|
70
|
-
ret[this.propertyName(meta, prop,
|
|
80
|
+
ret[this.propertyName(meta, prop, raw)] = val;
|
|
71
81
|
}
|
|
72
82
|
if (!wrapped.isInitialized() && wrapped.hasPrimaryKey()) {
|
|
73
83
|
return ret;
|
|
@@ -78,7 +88,7 @@ export class EntityTransformer {
|
|
|
78
88
|
const visible = !prop.hidden && entity[prop.getterName] instanceof Function;
|
|
79
89
|
const populated = root.isMarkedAsPopulated(meta.className, prop.name);
|
|
80
90
|
if (visible) {
|
|
81
|
-
ret[this.propertyName(meta, prop.name,
|
|
91
|
+
ret[this.propertyName(meta, prop.name, raw)] = this.processProperty(prop.getterName, entity, raw, populated);
|
|
82
92
|
}
|
|
83
93
|
}
|
|
84
94
|
else {
|
|
@@ -86,7 +96,7 @@ export class EntityTransformer {
|
|
|
86
96
|
const visible = !prop.hidden && typeof entity[prop.name] !== 'undefined';
|
|
87
97
|
const populated = root.isMarkedAsPopulated(meta.className, prop.name);
|
|
88
98
|
if (visible) {
|
|
89
|
-
ret[this.propertyName(meta, prop.name,
|
|
99
|
+
ret[this.propertyName(meta, prop.name, raw)] = this.processProperty(prop.name, entity, raw, populated);
|
|
90
100
|
}
|
|
91
101
|
}
|
|
92
102
|
}
|
|
@@ -95,15 +105,15 @@ export class EntityTransformer {
|
|
|
95
105
|
}
|
|
96
106
|
return ret;
|
|
97
107
|
}
|
|
98
|
-
static propertyName(meta, prop,
|
|
108
|
+
static propertyName(meta, prop, raw) {
|
|
99
109
|
if (raw) {
|
|
100
110
|
return prop;
|
|
101
111
|
}
|
|
102
112
|
if (meta.properties[prop].serializedName) {
|
|
103
113
|
return meta.properties[prop].serializedName;
|
|
104
114
|
}
|
|
105
|
-
if (meta.properties[prop].primary &&
|
|
106
|
-
return
|
|
115
|
+
if (meta.properties[prop].primary && meta.serializedPrimaryKey) {
|
|
116
|
+
return meta.serializedPrimaryKey;
|
|
107
117
|
}
|
|
108
118
|
return prop;
|
|
109
119
|
}
|
|
@@ -37,7 +37,7 @@ export class SerializationContext {
|
|
|
37
37
|
leave(entityName, prop) {
|
|
38
38
|
const last = this.path.pop();
|
|
39
39
|
/* v8 ignore next 3 */
|
|
40
|
-
if (
|
|
40
|
+
if (last?.[0] !== entityName || last[1] !== prop) {
|
|
41
41
|
throw new Error(`Trying to leave wrong property: ${entityName}.${prop} instead of ${last?.join('.')}`);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -73,18 +73,21 @@ export class SerializationContext {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
isMarkedAsPopulated(entityName, prop) {
|
|
76
|
-
let populate = this.populate;
|
|
76
|
+
let populate = this.populate ?? [];
|
|
77
77
|
for (const segment of this.path) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
const hints = populate.filter(p => p.field === segment[1]);
|
|
79
|
+
if (hints.length > 0) {
|
|
80
|
+
const childHints = [];
|
|
81
|
+
for (const hint of hints) {
|
|
82
|
+
// we need to check for cycles here too, as we could fall into endless loops for bidirectional relations
|
|
83
|
+
if (hint.all) {
|
|
84
|
+
return !this.path.find(([cls, item]) => entityName === cls && prop === item);
|
|
85
|
+
}
|
|
86
|
+
if (hint.children) {
|
|
87
|
+
childHints.push(...hint.children);
|
|
88
|
+
}
|
|
86
89
|
}
|
|
87
|
-
populate =
|
|
90
|
+
populate = childHints;
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
return !!populate?.some(p => p.field === prop);
|
package/types/BigIntType.d.ts
CHANGED
|
@@ -5,12 +5,15 @@ import type { EntityProperty } from '../typings.js';
|
|
|
5
5
|
* This type will automatically convert string values returned from the database to native JS bigints (default)
|
|
6
6
|
* or numbers (safe only for values up to `Number.MAX_SAFE_INTEGER`), or strings, depending on the `mode`.
|
|
7
7
|
*/
|
|
8
|
-
export declare class BigIntType extends
|
|
9
|
-
mode?:
|
|
10
|
-
constructor(mode?:
|
|
11
|
-
convertToDatabaseValue(value:
|
|
12
|
-
convertToJSValue(value: string | bigint | null | undefined):
|
|
13
|
-
toJSON(value:
|
|
8
|
+
export declare class BigIntType<Mode extends 'bigint' | 'number' | 'string' = 'bigint'> extends Type<JSTypeByMode<Mode> | null | undefined, string | null | undefined> {
|
|
9
|
+
mode?: Mode | undefined;
|
|
10
|
+
constructor(mode?: Mode | undefined);
|
|
11
|
+
convertToDatabaseValue(value: JSTypeByMode<Mode> | null | undefined): string | null | undefined;
|
|
12
|
+
convertToJSValue(value: string | bigint | null | undefined): JSTypeByMode<Mode> | null | undefined;
|
|
13
|
+
toJSON(value: JSTypeByMode<Mode> | null | undefined): JSTypeByMode<Mode> | null | undefined;
|
|
14
14
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
15
15
|
compareAsType(): string;
|
|
16
|
+
compareValues(a: string, b: string): boolean;
|
|
16
17
|
}
|
|
18
|
+
type JSTypeByMode<Mode extends 'bigint' | 'number' | 'string'> = Mode extends 'bigint' ? bigint : Mode extends 'number' ? number : string;
|
|
19
|
+
export {};
|
package/types/BigIntType.js
CHANGED
package/types/BlobType.d.ts
CHANGED
|
@@ -4,6 +4,5 @@ import type { EntityProperty } from '../typings.js';
|
|
|
4
4
|
export declare class BlobType extends Uint8ArrayType {
|
|
5
5
|
convertToJSValue(value: Buffer): Buffer | null;
|
|
6
6
|
compareAsType(): string;
|
|
7
|
-
ensureComparable(): boolean;
|
|
8
7
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
9
8
|
}
|
package/types/BlobType.js
CHANGED
package/types/BooleanType.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Type } from './Type.js';
|
|
2
2
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
3
|
import type { EntityProperty } from '../typings.js';
|
|
4
|
-
export declare class BooleanType extends Type<
|
|
4
|
+
export declare class BooleanType extends Type<boolean | null | undefined, boolean | null | undefined> {
|
|
5
5
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
6
6
|
compareAsType(): string;
|
|
7
|
+
convertToJSValue(value: boolean | null | undefined): boolean | null | undefined;
|
|
7
8
|
ensureComparable(): boolean;
|
|
8
9
|
}
|
package/types/BooleanType.js
CHANGED
package/types/DecimalType.d.ts
CHANGED
|
@@ -4,12 +4,14 @@ import type { EntityProperty } from '../typings.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Type that maps an SQL DECIMAL to a JS string or number.
|
|
6
6
|
*/
|
|
7
|
-
export declare class DecimalType extends
|
|
8
|
-
mode?:
|
|
9
|
-
constructor(mode?:
|
|
10
|
-
convertToJSValue(value: string):
|
|
7
|
+
export declare class DecimalType<Mode extends 'number' | 'string' = 'string'> extends Type<JSTypeByMode<Mode>, string> {
|
|
8
|
+
mode?: Mode | undefined;
|
|
9
|
+
constructor(mode?: Mode | undefined);
|
|
10
|
+
convertToJSValue(value: string): JSTypeByMode<Mode>;
|
|
11
11
|
compareValues(a: string, b: string): boolean;
|
|
12
12
|
private format;
|
|
13
13
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
14
14
|
compareAsType(): string;
|
|
15
15
|
}
|
|
16
|
+
type JSTypeByMode<Mode extends 'number' | 'string'> = Mode extends 'number' ? number : string;
|
|
17
|
+
export {};
|
package/types/DecimalType.js
CHANGED
package/types/DoubleType.js
CHANGED
package/types/JsonType.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare class JsonType extends Type<unknown, string | null> {
|
|
|
5
5
|
convertToDatabaseValue(value: unknown, platform: Platform, context?: TransformContext): string | null;
|
|
6
6
|
convertToJSValueSQL(key: string, platform: Platform): string;
|
|
7
7
|
convertToDatabaseValueSQL(key: string, platform: Platform): string;
|
|
8
|
-
convertToJSValue(value: string | unknown, platform: Platform): unknown;
|
|
8
|
+
convertToJSValue(value: string | unknown, platform: Platform, context?: TransformContext): unknown;
|
|
9
9
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
10
10
|
ensureComparable<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T>): boolean;
|
|
11
11
|
compareAsType(): string;
|
package/types/JsonType.js
CHANGED
|
@@ -12,8 +12,13 @@ export class JsonType extends Type {
|
|
|
12
12
|
convertToDatabaseValueSQL(key, platform) {
|
|
13
13
|
return key + platform.castColumn(this.prop);
|
|
14
14
|
}
|
|
15
|
-
convertToJSValue(value, platform) {
|
|
16
|
-
|
|
15
|
+
convertToJSValue(value, platform, context) {
|
|
16
|
+
const isJsonColumn = ['json', 'jsonb', platform.getJsonDeclarationSQL()].includes(this.prop.columnTypes[0]);
|
|
17
|
+
const isObjectEmbedded = this.prop.embedded && this.prop.object;
|
|
18
|
+
if ((platform.convertsJsonAutomatically() || isObjectEmbedded) && isJsonColumn && !context?.force) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
return platform.convertJsonToJSValue(value, context);
|
|
17
22
|
}
|
|
18
23
|
getColumnType(prop, platform) {
|
|
19
24
|
return platform.getJsonDeclarationSQL();
|
package/types/Type.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { Platform } from '../platforms/Platform.js';
|
|
|
3
3
|
import type { Constructor, EntityMetadata, EntityProperty } from '../typings.js';
|
|
4
4
|
export interface TransformContext {
|
|
5
5
|
fromQuery?: boolean;
|
|
6
|
+
force?: boolean;
|
|
6
7
|
key?: string;
|
|
7
8
|
mode?: 'hydration' | 'query' | 'query-data' | 'discovery' | 'serialization';
|
|
8
9
|
}
|
|
@@ -23,7 +24,7 @@ export declare abstract class Type<JSType = string, DBType = JSType> {
|
|
|
23
24
|
/**
|
|
24
25
|
* Converts a value from its database representation to its JS representation of this type.
|
|
25
26
|
*/
|
|
26
|
-
convertToJSValue(value: DBType, platform: Platform): JSType;
|
|
27
|
+
convertToJSValue(value: DBType, platform: Platform, context?: TransformContext): JSType;
|
|
27
28
|
/**
|
|
28
29
|
* Converts a value from its JS representation to its database representation of this type.
|
|
29
30
|
*/
|
package/types/Type.js
CHANGED
|
@@ -5,6 +5,5 @@ export declare class Uint8ArrayType extends Type<Uint8Array | null> {
|
|
|
5
5
|
convertToDatabaseValue(value: Uint8Array): Buffer;
|
|
6
6
|
convertToJSValue(value: Buffer): Uint8Array | null;
|
|
7
7
|
compareAsType(): string;
|
|
8
|
-
ensureComparable(): boolean;
|
|
9
8
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
10
9
|
}
|
package/types/Uint8ArrayType.js
CHANGED