@mikro-orm/core 6.4.17-dev.6 → 6.4.17-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 +7 -0
- package/EntityManager.js +18 -8
- package/README.md +1 -1
- package/decorators/Entity.d.ts +14 -0
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/Transactional.d.ts +1 -0
- package/decorators/Transactional.js +3 -3
- package/drivers/IDatabaseDriver.d.ts +2 -0
- package/entity/ArrayCollection.d.ts +3 -1
- package/entity/ArrayCollection.js +7 -2
- package/entity/EntityFactory.d.ts +6 -0
- package/entity/EntityFactory.js +12 -1
- package/entity/EntityHelper.js +4 -1
- package/entity/EntityLoader.js +16 -14
- package/entity/Reference.d.ts +5 -0
- package/entity/Reference.js +16 -0
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +528 -0
- package/entity/defineEntity.js +684 -0
- package/entity/index.d.ts +1 -0
- package/entity/index.js +1 -0
- package/hydration/ObjectHydrator.js +1 -1
- package/index.d.ts +1 -1
- package/index.mjs +2 -0
- package/metadata/MetadataDiscovery.d.ts +0 -1
- package/metadata/MetadataDiscovery.js +6 -11
- package/package.json +3 -3
- package/platforms/Platform.d.ts +3 -1
- package/types/BooleanType.d.ts +1 -1
- package/typings.d.ts +16 -8
- package/typings.js +10 -2
- package/unit-of-work/ChangeSetComputer.js +3 -1
- package/unit-of-work/UnitOfWork.d.ts +1 -0
- package/unit-of-work/UnitOfWork.js +17 -10
- package/utils/Configuration.d.ts +6 -0
- package/utils/Configuration.js +1 -0
- package/utils/ConfigurationLoader.js +1 -1
- package/utils/EntityComparator.js +6 -3
- package/utils/QueryHelper.js +3 -3
- package/utils/RawQueryFragment.d.ts +34 -0
- package/utils/RawQueryFragment.js +35 -0
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +24 -5
- package/utils/upsert-utils.js +9 -1
package/entity/index.js
CHANGED
|
@@ -65,7 +65,7 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
65
65
|
const ret = [];
|
|
66
66
|
const idx = this.tmpIndex++;
|
|
67
67
|
const nullVal = this.config.get('forceUndefined') ? 'undefined' : 'null';
|
|
68
|
-
if (prop.getter && !prop.setter) {
|
|
68
|
+
if (prop.getter && !prop.setter && prop.persist === false) {
|
|
69
69
|
return [];
|
|
70
70
|
}
|
|
71
71
|
if (prop.ref) {
|
package/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
* @module core
|
|
4
4
|
*/
|
|
5
|
-
export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, } from './typings';
|
|
5
|
+
export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, } from './typings';
|
|
6
6
|
export * from './enums';
|
|
7
7
|
export * from './errors';
|
|
8
8
|
export * from './exceptions';
|
package/index.mjs
CHANGED
|
@@ -182,11 +182,13 @@ export const compareBuffers = mod.compareBuffers;
|
|
|
182
182
|
export const compareObjects = mod.compareObjects;
|
|
183
183
|
export const createSqlFunction = mod.createSqlFunction;
|
|
184
184
|
export const defineConfig = mod.defineConfig;
|
|
185
|
+
export const defineEntity = mod.defineEntity;
|
|
185
186
|
export const equals = mod.equals;
|
|
186
187
|
export const getOnConflictFields = mod.getOnConflictFields;
|
|
187
188
|
export const getOnConflictReturningFields = mod.getOnConflictReturningFields;
|
|
188
189
|
export const helper = mod.helper;
|
|
189
190
|
export const parseJsonSafe = mod.parseJsonSafe;
|
|
191
|
+
export const quote = mod.quote;
|
|
190
192
|
export const raw = mod.raw;
|
|
191
193
|
export const ref = mod.ref;
|
|
192
194
|
export const rel = mod.rel;
|
|
@@ -54,7 +54,6 @@ export declare class MetadataDiscovery {
|
|
|
54
54
|
private initAutoincrement;
|
|
55
55
|
private initCheckConstraints;
|
|
56
56
|
private initGeneratedColumn;
|
|
57
|
-
private createColumnMappingObject;
|
|
58
57
|
private getDefaultVersionValue;
|
|
59
58
|
private inferDefaultValue;
|
|
60
59
|
private initDefaultValue;
|
|
@@ -969,7 +969,7 @@ class MetadataDiscovery {
|
|
|
969
969
|
}
|
|
970
970
|
}
|
|
971
971
|
initCheckConstraints(meta) {
|
|
972
|
-
const map =
|
|
972
|
+
const map = meta.createColumnMappingObject();
|
|
973
973
|
for (const check of meta.checks) {
|
|
974
974
|
const columns = check.property ? meta.properties[check.property].fieldNames : [];
|
|
975
975
|
check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
|
|
@@ -992,19 +992,11 @@ class MetadataDiscovery {
|
|
|
992
992
|
}
|
|
993
993
|
return;
|
|
994
994
|
}
|
|
995
|
-
const map =
|
|
995
|
+
const map = meta.createColumnMappingObject();
|
|
996
996
|
if (prop.generated instanceof Function) {
|
|
997
997
|
prop.generated = prop.generated(map);
|
|
998
998
|
}
|
|
999
999
|
}
|
|
1000
|
-
createColumnMappingObject(meta) {
|
|
1001
|
-
return Object.values(meta.properties).reduce((o, prop) => {
|
|
1002
|
-
if (prop.fieldNames) {
|
|
1003
|
-
o[prop.name] = prop.fieldNames[0];
|
|
1004
|
-
}
|
|
1005
|
-
return o;
|
|
1006
|
-
}, {});
|
|
1007
|
-
}
|
|
1008
1000
|
getDefaultVersionValue(meta, prop) {
|
|
1009
1001
|
if (typeof prop.defaultRaw !== 'undefined') {
|
|
1010
1002
|
return prop.defaultRaw;
|
|
@@ -1058,7 +1050,7 @@ class MetadataDiscovery {
|
|
|
1058
1050
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1059
1051
|
return;
|
|
1060
1052
|
}
|
|
1061
|
-
if (
|
|
1053
|
+
if (Array.isArray(prop.default) && prop.customType) {
|
|
1062
1054
|
val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
|
|
1063
1055
|
}
|
|
1064
1056
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
@@ -1112,6 +1104,9 @@ class MetadataDiscovery {
|
|
|
1112
1104
|
if (prop.kind === enums_1.ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1113
1105
|
prop.customType = new types_1.JsonType();
|
|
1114
1106
|
}
|
|
1107
|
+
if (prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
1108
|
+
prop.customType = new types_1.JsonType();
|
|
1109
|
+
}
|
|
1115
1110
|
if (!prop.customType && prop.array && prop.items) {
|
|
1116
1111
|
prop.customType = new types_1.EnumArrayType(`${meta.className}.${prop.name}`, prop.items);
|
|
1117
1112
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "6.4.17-dev.
|
|
3
|
+
"version": "6.4.17-dev.60",
|
|
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
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -60,11 +60,11 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"dataloader": "2.2.3",
|
|
63
|
-
"dotenv": "
|
|
63
|
+
"dotenv": "17.2.1",
|
|
64
64
|
"esprima": "4.0.1",
|
|
65
65
|
"fs-extra": "11.3.0",
|
|
66
66
|
"globby": "11.1.0",
|
|
67
|
-
"mikro-orm": "6.4.17-dev.
|
|
67
|
+
"mikro-orm": "6.4.17-dev.60",
|
|
68
68
|
"reflect-metadata": "0.2.2"
|
|
69
69
|
}
|
|
70
70
|
}
|
package/platforms/Platform.d.ts
CHANGED
|
@@ -173,7 +173,9 @@ export declare abstract class Platform {
|
|
|
173
173
|
getExtension<T>(extensionName: string, extensionKey: string, moduleName: string, em: EntityManager): T;
|
|
174
174
|
getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): ISchemaGenerator;
|
|
175
175
|
processDateProperty(value: unknown): string | number | Date;
|
|
176
|
-
quoteIdentifier(id: string
|
|
176
|
+
quoteIdentifier(id: string | {
|
|
177
|
+
toString: () => string;
|
|
178
|
+
}, quote?: string): string;
|
|
177
179
|
quoteValue(value: any): string;
|
|
178
180
|
escape(value: any): string;
|
|
179
181
|
formatQuery(sql: string, params: readonly any[]): string;
|
package/types/BooleanType.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type } from './Type';
|
|
2
2
|
import type { Platform } from '../platforms';
|
|
3
3
|
import type { EntityProperty } from '../typings';
|
|
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
7
|
ensureComparable(): boolean;
|
package/typings.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { SerializationContext, SerializeOptions } from './serialization';
|
|
|
6
6
|
import type { EntitySchema, MetadataStorage } from './metadata';
|
|
7
7
|
import type { Type, types } from './types';
|
|
8
8
|
import type { Platform } from './platforms';
|
|
9
|
-
import type { Configuration } from './utils';
|
|
9
|
+
import type { Configuration, RawQueryFragment } from './utils';
|
|
10
10
|
import type { EntityManager } from './EntityManager';
|
|
11
11
|
import type { EmbeddedPrefixMode } from './decorators/Embedded';
|
|
12
12
|
import type { EventSubscriber } from './events';
|
|
@@ -73,6 +73,7 @@ export type Primary<T> = IsAny<T> extends true ? any : T extends {
|
|
|
73
73
|
} ? ReadonlyPrimary<PK> : T extends {
|
|
74
74
|
id?: infer PK;
|
|
75
75
|
} ? ReadonlyPrimary<PK> : T;
|
|
76
|
+
/** @internal */
|
|
76
77
|
export type PrimaryProperty<T> = T extends {
|
|
77
78
|
[PrimaryKeyProp]?: infer PK;
|
|
78
79
|
} ? (PK extends keyof T ? PK : (PK extends any[] ? PK[number] : never)) : T extends {
|
|
@@ -287,8 +288,14 @@ export type EntityDTO<T, C extends TypeConfig = never> = {
|
|
|
287
288
|
} & {
|
|
288
289
|
[K in keyof T as DTOOptionalKeys<T, K>]?: EntityDTOProp<T, T[K], C> | AddOptional<T[K]>;
|
|
289
290
|
};
|
|
290
|
-
type
|
|
291
|
-
|
|
291
|
+
type PropertyName<T> = IsUnknown<T> extends false ? keyof T : string;
|
|
292
|
+
type TableName = {
|
|
293
|
+
name: string;
|
|
294
|
+
schema?: string;
|
|
295
|
+
toString: () => string;
|
|
296
|
+
};
|
|
297
|
+
export type IndexCallback<T> = (table: TableName, columns: Record<PropertyName<T>, string>) => string | RawQueryFragment;
|
|
298
|
+
export type CheckCallback<T> = (columns: Record<PropertyName<T>, string>) => string;
|
|
292
299
|
export type GeneratedColumnCallback<T> = (columns: Record<keyof T, string>) => string;
|
|
293
300
|
export interface CheckConstraint<T = any> {
|
|
294
301
|
name?: string;
|
|
@@ -396,6 +403,7 @@ export declare class EntityMetadata<T = any> {
|
|
|
396
403
|
removeProperty(name: string, sync?: boolean): void;
|
|
397
404
|
getPrimaryProps(): EntityProperty<T>[];
|
|
398
405
|
getPrimaryProp(): EntityProperty<T>;
|
|
406
|
+
createColumnMappingObject(): Dictionary<any>;
|
|
399
407
|
get tableName(): string;
|
|
400
408
|
set tableName(name: string);
|
|
401
409
|
sync(initIndexes?: boolean): void;
|
|
@@ -447,18 +455,18 @@ export interface EntityMetadata<T = any> {
|
|
|
447
455
|
uniqueProps: EntityProperty<T>[];
|
|
448
456
|
getterProps: EntityProperty<T>[];
|
|
449
457
|
indexes: {
|
|
450
|
-
properties
|
|
458
|
+
properties?: EntityKey<T> | EntityKey<T>[];
|
|
451
459
|
name?: string;
|
|
452
460
|
type?: string;
|
|
453
461
|
options?: Dictionary;
|
|
454
|
-
expression?: string
|
|
462
|
+
expression?: string | IndexCallback<T>;
|
|
455
463
|
}[];
|
|
456
464
|
uniques: {
|
|
457
|
-
properties
|
|
465
|
+
properties?: EntityKey<T> | EntityKey<T>[];
|
|
458
466
|
name?: string;
|
|
459
467
|
options?: Dictionary;
|
|
460
|
-
expression?: string
|
|
461
|
-
deferMode?: DeferMode
|
|
468
|
+
expression?: string | IndexCallback<T>;
|
|
469
|
+
deferMode?: DeferMode | `${DeferMode}`;
|
|
462
470
|
}[];
|
|
463
471
|
checks: CheckConstraint<T>[];
|
|
464
472
|
repositoryClass?: string;
|
package/typings.js
CHANGED
|
@@ -53,6 +53,14 @@ class EntityMetadata {
|
|
|
53
53
|
getPrimaryProp() {
|
|
54
54
|
return this.properties[this.primaryKeys[0]];
|
|
55
55
|
}
|
|
56
|
+
createColumnMappingObject() {
|
|
57
|
+
return Object.values(this.properties).reduce((o, prop) => {
|
|
58
|
+
if (prop.fieldNames) {
|
|
59
|
+
o[prop.name] = prop.fieldNames[0];
|
|
60
|
+
}
|
|
61
|
+
return o;
|
|
62
|
+
}, {});
|
|
63
|
+
}
|
|
56
64
|
get tableName() {
|
|
57
65
|
return this.collection;
|
|
58
66
|
}
|
|
@@ -72,7 +80,7 @@ class EntityMetadata {
|
|
|
72
80
|
// `prop.userDefined` is either `undefined` or `false`
|
|
73
81
|
const discriminator = this.root.discriminatorColumn === prop.name && prop.userDefined === false;
|
|
74
82
|
// even if we don't have a setter, do not ignore value from database!
|
|
75
|
-
const onlyGetter = prop.getter && !prop.setter;
|
|
83
|
+
const onlyGetter = prop.getter && !prop.setter && prop.persist === false;
|
|
76
84
|
return !prop.inherited && prop.hydrate !== false && !discriminator && !prop.embedded && !onlyGetter;
|
|
77
85
|
});
|
|
78
86
|
this.trackingProps = this.hydrateProps
|
|
@@ -115,7 +123,7 @@ class EntityMetadata {
|
|
|
115
123
|
wrapped.__data[prop.name] = entity_1.Reference.wrapReference(val, prop);
|
|
116
124
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
117
125
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
118
|
-
wrapped.__originalEntityData[prop.name] = Utils_1.Utils.getPrimaryKeyValues(val, prop.targetMeta
|
|
126
|
+
wrapped.__originalEntityData[prop.name] = Utils_1.Utils.getPrimaryKeyValues(val, prop.targetMeta, true);
|
|
119
127
|
}
|
|
120
128
|
else {
|
|
121
129
|
wrapped.__touched = !hydrator.isRunning();
|
|
@@ -144,7 +144,9 @@ class ChangeSetComputer {
|
|
|
144
144
|
if (!target.isDirty() && changeSet.type !== ChangeSet_1.ChangeSetType.CREATE) {
|
|
145
145
|
return;
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
if (target.isDirty()) {
|
|
148
|
+
this.collectionUpdates.add(target);
|
|
149
|
+
}
|
|
148
150
|
if (prop.owner && !this.platform.usesPivotTable()) {
|
|
149
151
|
changeSet.payload[prop.name] = target.getItems(false).map((item) => item.__helper.__identifier ?? item.__helper.getPrimaryKey());
|
|
150
152
|
}
|
|
@@ -102,6 +102,7 @@ export declare class UnitOfWork {
|
|
|
102
102
|
private commitDeleteChangeSets;
|
|
103
103
|
private commitExtraUpdates;
|
|
104
104
|
private commitCollectionUpdates;
|
|
105
|
+
private filterCollectionUpdates;
|
|
105
106
|
/**
|
|
106
107
|
* Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
|
|
107
108
|
*/
|
|
@@ -88,7 +88,7 @@ class UnitOfWork {
|
|
|
88
88
|
}
|
|
89
89
|
wrapped.__loadedProperties.add(key);
|
|
90
90
|
if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
|
|
91
|
-
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta
|
|
91
|
+
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
92
92
|
}
|
|
93
93
|
else if (prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.object && Utils_1.Utils.isPlainObject(data[prop.name])) {
|
|
94
94
|
for (const p of prop.targetMeta.props) {
|
|
@@ -96,7 +96,7 @@ class UnitOfWork {
|
|
|
96
96
|
const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
|
|
97
97
|
data[prefix + p.name] = data[prop.name][p.name];
|
|
98
98
|
}
|
|
99
|
-
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta
|
|
99
|
+
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
100
100
|
}
|
|
101
101
|
if (forceUndefined) {
|
|
102
102
|
if (data[key] === null) {
|
|
@@ -303,6 +303,7 @@ class UnitOfWork {
|
|
|
303
303
|
cs.entity.__helper.__processing = true;
|
|
304
304
|
}
|
|
305
305
|
await this.eventManager.dispatchEvent(enums_1.EventType.onFlush, { em: this.em, uow: this });
|
|
306
|
+
this.filterCollectionUpdates();
|
|
306
307
|
// nothing to do, do not start transaction
|
|
307
308
|
if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
|
|
308
309
|
return void await this.eventManager.dispatchEvent(enums_1.EventType.afterFlush, { em: this.em, uow: this });
|
|
@@ -886,23 +887,29 @@ class UnitOfWork {
|
|
|
886
887
|
}
|
|
887
888
|
}
|
|
888
889
|
async commitCollectionUpdates(ctx) {
|
|
889
|
-
|
|
890
|
+
this.filterCollectionUpdates();
|
|
891
|
+
await this.em.getDriver().syncCollections(this.collectionUpdates, { ctx, schema: this.em.schema });
|
|
890
892
|
for (const coll of this.collectionUpdates) {
|
|
893
|
+
coll.takeSnapshot();
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
filterCollectionUpdates() {
|
|
897
|
+
for (const coll of this.collectionUpdates) {
|
|
898
|
+
let skip = true;
|
|
891
899
|
if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
|
|
892
900
|
if (this.platform.usesPivotTable()) {
|
|
893
|
-
|
|
901
|
+
skip = false;
|
|
894
902
|
}
|
|
895
903
|
}
|
|
896
904
|
else if (coll.property.kind === enums_1.ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
|
|
897
|
-
|
|
905
|
+
skip = false;
|
|
898
906
|
}
|
|
899
907
|
else if (coll.property.kind === enums_1.ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
|
|
900
|
-
|
|
908
|
+
skip = false;
|
|
909
|
+
}
|
|
910
|
+
if (skip) {
|
|
911
|
+
this.collectionUpdates.delete(coll);
|
|
901
912
|
}
|
|
902
|
-
}
|
|
903
|
-
await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
|
|
904
|
-
for (const coll of this.collectionUpdates) {
|
|
905
|
-
coll.takeSnapshot();
|
|
906
913
|
}
|
|
907
914
|
}
|
|
908
915
|
/**
|
package/utils/Configuration.d.ts
CHANGED
|
@@ -77,6 +77,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
|
|
|
77
77
|
upsertManaged: true;
|
|
78
78
|
forceEntityConstructor: false;
|
|
79
79
|
forceUndefined: false;
|
|
80
|
+
processOnCreateHooksEarly: false;
|
|
80
81
|
ensureDatabase: true;
|
|
81
82
|
ensureIndexes: false;
|
|
82
83
|
batchSize: number;
|
|
@@ -345,6 +346,11 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
|
|
|
345
346
|
upsertManaged: boolean;
|
|
346
347
|
forceEntityConstructor: boolean | (Constructor<AnyEntity> | string)[];
|
|
347
348
|
forceUndefined: boolean;
|
|
349
|
+
/**
|
|
350
|
+
* Property `onCreate` hooks are normally executed during `flush` operation.
|
|
351
|
+
* With this option, they will be processed early inside `em.create()` method.
|
|
352
|
+
*/
|
|
353
|
+
processOnCreateHooksEarly: boolean;
|
|
348
354
|
forceUtcTimezone?: boolean;
|
|
349
355
|
timezone?: string;
|
|
350
356
|
ensureDatabase: boolean | EnsureDatabaseOptions;
|
package/utils/Configuration.js
CHANGED
|
@@ -213,7 +213,7 @@ class ConfigurationLoader {
|
|
|
213
213
|
const baseDir = options instanceof Configuration_1.Configuration ? options.get('baseDir') : options?.baseDir;
|
|
214
214
|
const path = process.env.MIKRO_ORM_ENV ?? ((baseDir ?? process.cwd()) + '/.env');
|
|
215
215
|
const env = {};
|
|
216
|
-
dotenv_1.default.config({ path, processEnv: env });
|
|
216
|
+
dotenv_1.default.config({ path, processEnv: env, quiet: true });
|
|
217
217
|
// only propagate known env vars
|
|
218
218
|
for (const key of Object.keys(env)) {
|
|
219
219
|
if (key.startsWith('MIKRO_ORM_')) {
|
|
@@ -534,9 +534,12 @@ class EntityComparator {
|
|
|
534
534
|
context.set('compareBuffers', Utils_1.compareBuffers);
|
|
535
535
|
context.set('compareObjects', Utils_1.compareObjects);
|
|
536
536
|
context.set('equals', Utils_1.equals);
|
|
537
|
-
meta.comparableProps
|
|
538
|
-
|
|
539
|
-
|
|
537
|
+
for (const prop of meta.comparableProps) {
|
|
538
|
+
// skip properties that are not hydrated
|
|
539
|
+
if (prop.hydrate !== false) {
|
|
540
|
+
lines.push(this.getPropertyComparator(prop, context));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
540
543
|
const code = `// compiled comparator for entity ${meta.className}\n`
|
|
541
544
|
+ `return function(last, current) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
|
|
542
545
|
const comparator = Utils_1.Utils.createFunction(context, code);
|
package/utils/QueryHelper.js
CHANGED
|
@@ -40,7 +40,7 @@ class QueryHelper {
|
|
|
40
40
|
if (Array.isArray(where)) {
|
|
41
41
|
where.forEach((item, i) => {
|
|
42
42
|
if (this.inlinePrimaryKeyObjects(item, meta, metadata, key)) {
|
|
43
|
-
where[i] = Utils_1.Utils.getPrimaryKeyValues(item, meta
|
|
43
|
+
where[i] = Utils_1.Utils.getPrimaryKeyValues(item, meta, false);
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
}
|
|
@@ -61,7 +61,7 @@ class QueryHelper {
|
|
|
61
61
|
Object.keys(where).forEach(k => {
|
|
62
62
|
const meta2 = metadata.find(meta.properties[k]?.type) || meta;
|
|
63
63
|
if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
|
|
64
|
-
where[k] = Utils_1.Utils.getPrimaryKeyValues(where[k], meta2
|
|
64
|
+
where[k] = Utils_1.Utils.getPrimaryKeyValues(where[k], meta2, true);
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
return false;
|
|
@@ -121,7 +121,7 @@ class QueryHelper {
|
|
|
121
121
|
value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
|
|
122
122
|
}
|
|
123
123
|
const isJsonProperty = prop?.customType instanceof JsonType_1.JsonType && Utils_1.Utils.isPlainObject(value) && !platform.isRaw(value) && Object.keys(value)[0] !== '$eq';
|
|
124
|
-
if (isJsonProperty) {
|
|
124
|
+
if (isJsonProperty && prop?.kind !== enums_1.ReferenceKind.EMBEDDED) {
|
|
125
125
|
return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
|
|
126
126
|
}
|
|
127
127
|
if (Array.isArray(value) && !Utils_1.Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !key.includes('?') && options.type !== 'orderBy') {
|
|
@@ -70,6 +70,24 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
|
|
|
70
70
|
* ```ts
|
|
71
71
|
* @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
|
|
72
72
|
* ```
|
|
73
|
+
*
|
|
74
|
+
* The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
|
|
75
|
+
*
|
|
76
|
+
* ```ts
|
|
77
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
|
|
78
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
|
|
79
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
|
|
80
|
+
* @Entity({ schema: 'library' })
|
|
81
|
+
* export class Author { ... }
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
|
|
85
|
+
*
|
|
86
|
+
* ```ts
|
|
87
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
88
|
+
* @Entity({ schema: 'library' })
|
|
89
|
+
* export class Author { ... }
|
|
90
|
+
* ```
|
|
73
91
|
*/
|
|
74
92
|
export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R;
|
|
75
93
|
/**
|
|
@@ -94,3 +112,19 @@ export declare namespace sql {
|
|
|
94
112
|
var upper: <T extends object>(key: string | ((alias: string) => string)) => string;
|
|
95
113
|
}
|
|
96
114
|
export declare function createSqlFunction<T extends object, R = string>(func: string, key: string | ((alias: string) => string)): R;
|
|
115
|
+
/**
|
|
116
|
+
* Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
|
|
117
|
+
*
|
|
118
|
+
* Within the template literal on which the tag function is applied, all placeholders are considered to be database identifiers, and will thus be quoted as so according to the database in use.
|
|
119
|
+
*
|
|
120
|
+
* ```ts
|
|
121
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
|
|
122
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
|
|
123
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
124
|
+
* @Entity({ schema: 'library' })
|
|
125
|
+
* export class Author { ... }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function quote(expParts: readonly string[], ...values: (string | {
|
|
129
|
+
toString(): string;
|
|
130
|
+
})[]): any;
|
|
@@ -4,6 +4,7 @@ exports.ALIAS_REPLACEMENT_RE = exports.ALIAS_REPLACEMENT = exports.RawQueryFragm
|
|
|
4
4
|
exports.raw = raw;
|
|
5
5
|
exports.sql = sql;
|
|
6
6
|
exports.createSqlFunction = createSqlFunction;
|
|
7
|
+
exports.quote = quote;
|
|
7
8
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
8
9
|
const node_util_1 = require("node:util");
|
|
9
10
|
const Utils_1 = require("./Utils");
|
|
@@ -150,6 +151,24 @@ exports.ALIAS_REPLACEMENT_RE = '\\[::alias::\\]';
|
|
|
150
151
|
* ```ts
|
|
151
152
|
* @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
|
|
152
153
|
* ```
|
|
154
|
+
*
|
|
155
|
+
* The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
|
|
156
|
+
*
|
|
157
|
+
* ```ts
|
|
158
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
|
|
159
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
|
|
160
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
|
|
161
|
+
* @Entity({ schema: 'library' })
|
|
162
|
+
* export class Author { ... }
|
|
163
|
+
* ```
|
|
164
|
+
*
|
|
165
|
+
* You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
|
|
166
|
+
*
|
|
167
|
+
* ```ts
|
|
168
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
169
|
+
* @Entity({ schema: 'library' })
|
|
170
|
+
* export class Author { ... }
|
|
171
|
+
* ```
|
|
153
172
|
*/
|
|
154
173
|
function raw(sql, params) {
|
|
155
174
|
if (sql instanceof RawQueryFragment) {
|
|
@@ -207,3 +226,19 @@ sql.ref = (...keys) => raw('??', [keys.join('.')]);
|
|
|
207
226
|
sql.now = (length) => raw('current_timestamp' + (length == null ? '' : `(${length})`));
|
|
208
227
|
sql.lower = (key) => createSqlFunction('lower', key);
|
|
209
228
|
sql.upper = (key) => createSqlFunction('upper', key);
|
|
229
|
+
/**
|
|
230
|
+
* Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
|
|
231
|
+
*
|
|
232
|
+
* Within the template literal on which the tag function is applied, all placeholders are considered to be database identifiers, and will thus be quoted as so according to the database in use.
|
|
233
|
+
*
|
|
234
|
+
* ```ts
|
|
235
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
|
|
236
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
|
|
237
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
238
|
+
* @Entity({ schema: 'library' })
|
|
239
|
+
* export class Author { ... }
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
function quote(expParts, ...values) {
|
|
243
|
+
return raw(expParts.join('??'), values);
|
|
244
|
+
}
|
package/utils/Utils.d.ts
CHANGED
|
@@ -134,14 +134,14 @@ export declare class Utils {
|
|
|
134
134
|
static getCompositeKeyHash<T>(data: EntityData<T>, meta: EntityMetadata<T>, convertCustomTypes?: boolean, platform?: Platform, flat?: boolean): string;
|
|
135
135
|
static getPrimaryKeyHash(pks: (string | Buffer | Date)[]): string;
|
|
136
136
|
static splitPrimaryKeys<T extends object>(key: string): EntityKey<T>[];
|
|
137
|
-
static getPrimaryKeyValues<T>(entity: T, primaryKeys: string[]
|
|
137
|
+
static getPrimaryKeyValues<T>(entity: T, primaryKeys: string[] | EntityMetadata<T>, allowScalar?: boolean, convertCustomTypes?: boolean): any;
|
|
138
138
|
static getPrimaryKeyCond<T>(entity: T, primaryKeys: EntityKey<T>[]): Record<string, Primary<T>> | null;
|
|
139
139
|
/**
|
|
140
140
|
* Maps nested FKs from `[1, 2, 3]` to `[1, [2, 3]]`.
|
|
141
141
|
*/
|
|
142
142
|
static mapFlatCompositePrimaryKey(fk: Primary<any>[], prop: EntityProperty, fieldNames?: string[], idx?: number): Primary<any> | Primary<any>[];
|
|
143
143
|
static getPrimaryKeyCondFromArray<T extends object>(pks: Primary<T>[], meta: EntityMetadata<T>): Record<string, Primary<T>>;
|
|
144
|
-
static getOrderedPrimaryKeys<T>(id: Primary<T> | Record<string, Primary<T>>, meta: EntityMetadata<T>, platform?: Platform, convertCustomTypes?: boolean): Primary<T>[];
|
|
144
|
+
static getOrderedPrimaryKeys<T>(id: Primary<T> | Record<string, Primary<T>>, meta: EntityMetadata<T>, platform?: Platform, convertCustomTypes?: boolean, allowScalar?: boolean): Primary<T>[];
|
|
145
145
|
/**
|
|
146
146
|
* Checks whether given object is an entity instance.
|
|
147
147
|
*/
|
package/utils/Utils.js
CHANGED
|
@@ -498,6 +498,7 @@ class Utils {
|
|
|
498
498
|
static splitPrimaryKeys(key) {
|
|
499
499
|
return key.split(this.PK_SEPARATOR);
|
|
500
500
|
}
|
|
501
|
+
// TODO v7: remove support for `primaryKeys: string[]`
|
|
501
502
|
static getPrimaryKeyValues(entity, primaryKeys, allowScalar = false, convertCustomTypes = false) {
|
|
502
503
|
/* istanbul ignore next */
|
|
503
504
|
if (entity == null) {
|
|
@@ -509,9 +510,24 @@ class Utils {
|
|
|
509
510
|
}
|
|
510
511
|
return val;
|
|
511
512
|
}
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
513
|
+
const meta = Array.isArray(primaryKeys) ? undefined : primaryKeys;
|
|
514
|
+
primaryKeys = Array.isArray(primaryKeys) ? primaryKeys : meta.primaryKeys;
|
|
515
|
+
let pk;
|
|
516
|
+
if (Utils.isEntity(entity, true)) {
|
|
517
|
+
pk = (0, wrap_1.helper)(entity).getPrimaryKey(convertCustomTypes);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
pk = primaryKeys.reduce((o, pk) => {
|
|
521
|
+
const targetMeta = meta?.properties[pk].targetMeta;
|
|
522
|
+
if (targetMeta && Utils.isPlainObject(entity[pk])) {
|
|
523
|
+
o[pk] = Utils.getPrimaryKeyValues(entity[pk], targetMeta, allowScalar, convertCustomTypes);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
o[pk] = entity[pk];
|
|
527
|
+
}
|
|
528
|
+
return o;
|
|
529
|
+
}, {});
|
|
530
|
+
}
|
|
515
531
|
if (primaryKeys.length > 1) {
|
|
516
532
|
return toArray(pk);
|
|
517
533
|
}
|
|
@@ -561,7 +577,7 @@ class Utils {
|
|
|
561
577
|
return o;
|
|
562
578
|
}, {});
|
|
563
579
|
}
|
|
564
|
-
static getOrderedPrimaryKeys(id, meta, platform, convertCustomTypes = false) {
|
|
580
|
+
static getOrderedPrimaryKeys(id, meta, platform, convertCustomTypes = false, allowScalar = false) {
|
|
565
581
|
const data = (Utils.isPrimaryKey(id) ? { [meta.primaryKeys[0]]: id } : id);
|
|
566
582
|
const pks = meta.primaryKeys.map((pk, idx) => {
|
|
567
583
|
const prop = meta.properties[pk];
|
|
@@ -571,11 +587,14 @@ class Utils {
|
|
|
571
587
|
value = prop.customType.convertToJSValue(value, platform);
|
|
572
588
|
}
|
|
573
589
|
if (prop.kind !== enums_1.ReferenceKind.SCALAR && prop.targetMeta) {
|
|
574
|
-
const value2 = this.getOrderedPrimaryKeys(value, prop.targetMeta, platform, convertCustomTypes);
|
|
590
|
+
const value2 = this.getOrderedPrimaryKeys(value, prop.targetMeta, platform, convertCustomTypes, allowScalar);
|
|
575
591
|
value = value2.length > 1 ? value2 : value2[0];
|
|
576
592
|
}
|
|
577
593
|
return value;
|
|
578
594
|
});
|
|
595
|
+
if (allowScalar && pks.length === 1) {
|
|
596
|
+
return pks[0];
|
|
597
|
+
}
|
|
579
598
|
// we need to flatten the PKs as composite PKs can be build from another composite PKs
|
|
580
599
|
// and this method is used to get the PK hash in identity map, that expects flat array
|
|
581
600
|
return Utils.flatten(pks);
|
package/utils/upsert-utils.js
CHANGED
|
@@ -75,7 +75,15 @@ function getOnConflictReturningFields(meta, data, uniqueFields, options) {
|
|
|
75
75
|
if (!meta) {
|
|
76
76
|
return '*';
|
|
77
77
|
}
|
|
78
|
-
const keys = meta.comparableProps.filter(p =>
|
|
78
|
+
const keys = meta.comparableProps.filter(p => {
|
|
79
|
+
if (p.lazy || p.embeddable) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
if (p.autoincrement) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return Array.isArray(uniqueFields) && !uniqueFields.includes(p.name);
|
|
86
|
+
}).map(p => p.name);
|
|
79
87
|
if (meta.versionProperty) {
|
|
80
88
|
keys.push(meta.versionProperty);
|
|
81
89
|
}
|