@mikro-orm/core 7.0.0-dev.99 → 7.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +34 -17
- package/EntityManager.js +95 -103
- package/MikroORM.d.ts +5 -5
- package/MikroORM.js +25 -20
- package/cache/FileCacheAdapter.js +11 -3
- package/connections/Connection.d.ts +3 -2
- package/connections/Connection.js +4 -3
- package/drivers/DatabaseDriver.d.ts +11 -11
- package/drivers/DatabaseDriver.js +91 -25
- package/drivers/IDatabaseDriver.d.ts +50 -20
- package/entity/BaseEntity.d.ts +61 -1
- package/entity/Collection.d.ts +8 -1
- package/entity/Collection.js +12 -13
- package/entity/EntityAssigner.js +9 -9
- package/entity/EntityFactory.d.ts +6 -1
- package/entity/EntityFactory.js +40 -22
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +27 -4
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +193 -80
- package/entity/EntityRepository.d.ts +27 -7
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/WrappedEntity.d.ts +2 -2
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +89 -50
- package/entity/defineEntity.js +12 -0
- package/entity/index.d.ts +1 -0
- package/entity/index.js +1 -0
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +33 -0
- package/entity/validators.js +2 -2
- package/enums.d.ts +2 -2
- package/enums.js +1 -0
- package/errors.d.ts +16 -8
- package/errors.js +40 -13
- package/hydration/ObjectHydrator.js +63 -21
- package/index.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +7 -6
- package/logging/inspect.js +1 -6
- package/metadata/EntitySchema.d.ts +43 -13
- package/metadata/EntitySchema.js +82 -27
- package/metadata/MetadataDiscovery.d.ts +60 -3
- package/metadata/MetadataDiscovery.js +665 -154
- package/metadata/MetadataProvider.js +3 -1
- package/metadata/MetadataStorage.d.ts +13 -6
- package/metadata/MetadataStorage.js +64 -19
- package/metadata/MetadataValidator.d.ts +32 -2
- package/metadata/MetadataValidator.js +196 -31
- package/metadata/discover-entities.js +5 -5
- package/metadata/types.d.ts +111 -14
- package/naming-strategy/AbstractNamingStrategy.d.ts +11 -3
- package/naming-strategy/AbstractNamingStrategy.js +12 -0
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +17 -3
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/package.json +2 -2
- package/platforms/Platform.d.ts +4 -2
- package/platforms/Platform.js +5 -2
- package/serialization/EntitySerializer.d.ts +3 -0
- package/serialization/EntitySerializer.js +15 -13
- package/serialization/EntityTransformer.js +6 -6
- package/serialization/SerializationContext.d.ts +6 -6
- package/typings.d.ts +325 -110
- package/typings.js +84 -17
- package/unit-of-work/ChangeSet.d.ts +4 -3
- package/unit-of-work/ChangeSet.js +2 -3
- package/unit-of-work/ChangeSetComputer.d.ts +3 -6
- package/unit-of-work/ChangeSetComputer.js +34 -13
- package/unit-of-work/ChangeSetPersister.d.ts +12 -10
- package/unit-of-work/ChangeSetPersister.js +55 -25
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +21 -3
- package/unit-of-work/UnitOfWork.js +203 -56
- package/utils/AbstractSchemaGenerator.js +17 -8
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +52 -11
- package/utils/Configuration.js +12 -8
- package/utils/Cursor.js +21 -8
- package/utils/DataloaderUtils.js +13 -11
- package/utils/EntityComparator.d.ts +14 -7
- package/utils/EntityComparator.js +132 -46
- package/utils/QueryHelper.d.ts +16 -6
- package/utils/QueryHelper.js +53 -18
- package/utils/RawQueryFragment.d.ts +28 -23
- package/utils/RawQueryFragment.js +34 -56
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +1 -1
- package/utils/Utils.d.ts +7 -26
- package/utils/Utils.js +25 -79
- package/utils/clone.js +7 -21
- package/utils/env-vars.d.ts +4 -0
- package/utils/env-vars.js +13 -3
- package/utils/fs-utils.d.ts +21 -0
- package/utils/fs-utils.js +106 -11
- package/utils/upsert-utils.d.ts +4 -4
|
@@ -40,7 +40,7 @@ export class AbstractSchemaGenerator {
|
|
|
40
40
|
}
|
|
41
41
|
async clear(options) {
|
|
42
42
|
for (const meta of this.getOrderedMetadata(options?.schema).reverse()) {
|
|
43
|
-
await this.driver.nativeDelete(meta.
|
|
43
|
+
await this.driver.nativeDelete(meta.class, {}, options);
|
|
44
44
|
}
|
|
45
45
|
if (options?.clearIdentityMap ?? true) {
|
|
46
46
|
this.clearIdentityMap();
|
|
@@ -90,21 +90,30 @@ export class AbstractSchemaGenerator {
|
|
|
90
90
|
this.notImplemented();
|
|
91
91
|
}
|
|
92
92
|
getOrderedMetadata(schema) {
|
|
93
|
-
const metadata =
|
|
94
|
-
const isRootEntity = meta.root.
|
|
95
|
-
|
|
93
|
+
const metadata = [...this.metadata.getAll().values()].filter(meta => {
|
|
94
|
+
const isRootEntity = meta.root.class === meta.class;
|
|
95
|
+
const isTPTChild = meta.inheritanceType === 'tpt' && meta.tptParent;
|
|
96
|
+
return (isRootEntity || isTPTChild) && !meta.embeddable && !meta.virtual;
|
|
96
97
|
});
|
|
97
98
|
const calc = new CommitOrderCalculator();
|
|
98
|
-
metadata.forEach(meta =>
|
|
99
|
+
metadata.forEach(meta => {
|
|
100
|
+
const nodeId = meta.inheritanceType === 'tpt' && meta.tptParent ? meta._id : meta.root._id;
|
|
101
|
+
calc.addNode(nodeId);
|
|
102
|
+
});
|
|
99
103
|
let meta = metadata.pop();
|
|
100
104
|
while (meta) {
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
const nodeId = meta.inheritanceType === 'tpt' && meta.tptParent ? meta._id : meta.root._id;
|
|
106
|
+
for (const prop of meta.relations) {
|
|
107
|
+
calc.discoverProperty(prop, nodeId);
|
|
108
|
+
}
|
|
109
|
+
if (meta.inheritanceType === 'tpt' && meta.tptParent) {
|
|
110
|
+
const parentId = meta.tptParent._id;
|
|
111
|
+
calc.addDependency(parentId, nodeId, 1);
|
|
103
112
|
}
|
|
104
113
|
meta = metadata.pop();
|
|
105
114
|
}
|
|
106
115
|
return calc.sort()
|
|
107
|
-
.map(cls => this.metadata.
|
|
116
|
+
.map(cls => this.metadata.getById(cls))
|
|
108
117
|
.filter(meta => {
|
|
109
118
|
const targetSchema = meta.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
|
|
110
119
|
return schema ? [schema, '*'].includes(targetSchema) : meta.schema !== '*';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function getNodeAsyncContext() {
|
|
2
|
+
const mod = globalThis.process?.getBuiltinModule?.('node:async_hooks');
|
|
3
|
+
/* v8 ignore next */
|
|
4
|
+
if (!mod?.AsyncLocalStorage) {
|
|
5
|
+
throw new Error('AsyncLocalStorage not available');
|
|
6
|
+
}
|
|
7
|
+
return new mod.AsyncLocalStorage();
|
|
8
|
+
}
|
|
9
|
+
/* v8 ignore next */
|
|
10
|
+
function createFallbackAsyncContext() {
|
|
11
|
+
let store;
|
|
12
|
+
// eslint-disable-next-line no-console
|
|
13
|
+
console.warn('AsyncLocalStorage not available');
|
|
14
|
+
return {
|
|
15
|
+
getStore: () => store,
|
|
16
|
+
enterWith: value => store = value,
|
|
17
|
+
run: (value, cb) => {
|
|
18
|
+
const prev = store;
|
|
19
|
+
store = value;
|
|
20
|
+
try {
|
|
21
|
+
return cb();
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
store = prev;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function createAsyncContext() {
|
|
30
|
+
/* v8 ignore next */
|
|
31
|
+
const ALS = globalThis.AsyncLocalStorage;
|
|
32
|
+
/* v8 ignore next */
|
|
33
|
+
if (typeof ALS === 'function' && ALS.prototype?.run) {
|
|
34
|
+
return new ALS();
|
|
35
|
+
}
|
|
36
|
+
/* v8 ignore else */
|
|
37
|
+
if (globalThis.process?.versions?.node) {
|
|
38
|
+
return getNodeAsyncContext();
|
|
39
|
+
}
|
|
40
|
+
/* v8 ignore next */
|
|
41
|
+
return createFallbackAsyncContext();
|
|
42
|
+
}
|
package/utils/Configuration.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NamingStrategy } from '../naming-strategy/NamingStrategy.js';
|
|
2
2
|
import { type CacheAdapter, type SyncCacheAdapter } from '../cache/CacheAdapter.js';
|
|
3
3
|
import type { EntityRepository } from '../entity/EntityRepository.js';
|
|
4
|
-
import type { AnyEntity, Constructor, Dictionary, EnsureDatabaseOptions, EntityClass, EntityMetadata, FilterDef, GenerateOptions, Highlighter, HydratorConstructor, IHydrator, IMigrationGenerator, IPrimaryKey, MaybePromise, Migration, MigrationObject } from '../typings.js';
|
|
4
|
+
import type { AnyEntity, CompiledFunctions, Constructor, Dictionary, EnsureDatabaseOptions, EntityClass, EntityMetadata, FilterDef, GenerateOptions, Highlighter, HydratorConstructor, IHydrator, IMigrationGenerator, IPrimaryKey, MaybePromise, Migration, MigrationObject } from '../typings.js';
|
|
5
5
|
import { ObjectHydrator } from '../hydration/ObjectHydrator.js';
|
|
6
6
|
import { NullHighlighter } from '../utils/NullHighlighter.js';
|
|
7
7
|
import { type Logger, type LoggerNamespace, type LoggerOptions } from '../logging/Logger.js';
|
|
@@ -35,7 +35,7 @@ declare const DEFAULTS: {
|
|
|
35
35
|
readonly inferDefaultValues: true;
|
|
36
36
|
};
|
|
37
37
|
readonly validateRequired: true;
|
|
38
|
-
readonly context: (name: string) => EntityManager<IDatabaseDriver<import("
|
|
38
|
+
readonly context: (name: string) => EntityManager<IDatabaseDriver<import("../index.js").Connection>> | undefined;
|
|
39
39
|
readonly contextName: "default";
|
|
40
40
|
readonly allowGlobalContext: false;
|
|
41
41
|
readonly logger: (message?: any, ...optionalParams: any[]) => void;
|
|
@@ -69,6 +69,7 @@ declare const DEFAULTS: {
|
|
|
69
69
|
readonly upsertManaged: true;
|
|
70
70
|
readonly forceEntityConstructor: false;
|
|
71
71
|
readonly forceUndefined: false;
|
|
72
|
+
readonly forceUtcTimezone: true;
|
|
72
73
|
readonly processOnCreateHooksEarly: true;
|
|
73
74
|
readonly ensureDatabase: true;
|
|
74
75
|
readonly ensureIndexes: false;
|
|
@@ -82,7 +83,6 @@ declare const DEFAULTS: {
|
|
|
82
83
|
readonly glob: "!(*.d).{js,ts,cjs}";
|
|
83
84
|
readonly silent: false;
|
|
84
85
|
readonly transactional: true;
|
|
85
|
-
readonly disableForeignKeys: false;
|
|
86
86
|
readonly allOrNothing: true;
|
|
87
87
|
readonly dropTables: true;
|
|
88
88
|
readonly safe: false;
|
|
@@ -91,10 +91,10 @@ declare const DEFAULTS: {
|
|
|
91
91
|
readonly fileName: (timestamp: string, name?: string) => string;
|
|
92
92
|
};
|
|
93
93
|
readonly schemaGenerator: {
|
|
94
|
-
readonly disableForeignKeys: false;
|
|
95
94
|
readonly createForeignKeyConstraints: true;
|
|
96
95
|
readonly ignoreSchema: readonly [];
|
|
97
96
|
readonly skipTables: readonly [];
|
|
97
|
+
readonly skipViews: readonly [];
|
|
98
98
|
readonly skipColumns: {};
|
|
99
99
|
};
|
|
100
100
|
readonly embeddables: {
|
|
@@ -256,6 +256,21 @@ export interface ConnectionOptions {
|
|
|
256
256
|
driverOptions?: Dictionary;
|
|
257
257
|
/** Callback to execute when a new connection is created. */
|
|
258
258
|
onCreateConnection?: (connection: unknown) => Promise<void>;
|
|
259
|
+
/**
|
|
260
|
+
* SQLite/libSQL: databases to attach on connection.
|
|
261
|
+
* Each attached database acts as a schema, accessible via `schema.table` syntax.
|
|
262
|
+
* Entities can reference attached databases via `@Entity({ schema: 'db_name' })`.
|
|
263
|
+
* Note: Not supported for remote libSQL connections.
|
|
264
|
+
* @example
|
|
265
|
+
* attachDatabases: [
|
|
266
|
+
* { name: 'users_db', path: './users.db' },
|
|
267
|
+
* { name: 'logs_db', path: '/var/data/logs.db' },
|
|
268
|
+
* ]
|
|
269
|
+
*/
|
|
270
|
+
attachDatabases?: {
|
|
271
|
+
name: string;
|
|
272
|
+
path: string;
|
|
273
|
+
}[];
|
|
259
274
|
}
|
|
260
275
|
/**
|
|
261
276
|
* Configuration options for database migrations.
|
|
@@ -402,11 +417,6 @@ export interface MetadataDiscoveryOptions {
|
|
|
402
417
|
* @default true
|
|
403
418
|
*/
|
|
404
419
|
checkDuplicateFieldNames?: boolean;
|
|
405
|
-
/**
|
|
406
|
-
* Check for duplicate entities and throw an error if found.
|
|
407
|
-
* @default true
|
|
408
|
-
*/
|
|
409
|
-
checkDuplicateEntities?: boolean;
|
|
410
420
|
/**
|
|
411
421
|
* Check for composite primary keys marked as `persist: false` and throw an error if found.
|
|
412
422
|
* @default true
|
|
@@ -628,9 +638,9 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
628
638
|
processOnCreateHooksEarly?: boolean;
|
|
629
639
|
/**
|
|
630
640
|
* Force `Date` values to be stored in UTC for datetime columns without timezone.
|
|
631
|
-
* Works for MySQL (`datetime` type)
|
|
641
|
+
* Works for MySQL (`datetime` type), PostgreSQL (`timestamp` type), and MSSQL (`datetime`/`datetime2` types).
|
|
632
642
|
* SQLite does this by default.
|
|
633
|
-
* @default
|
|
643
|
+
* @default true
|
|
634
644
|
*/
|
|
635
645
|
forceUtcTimezone?: boolean;
|
|
636
646
|
/**
|
|
@@ -670,6 +680,12 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
670
680
|
* @default ObjectHydrator
|
|
671
681
|
*/
|
|
672
682
|
hydrator?: HydratorConstructor;
|
|
683
|
+
/**
|
|
684
|
+
* Pre-generated compiled functions for hydration and comparison.
|
|
685
|
+
* Use the `compile` CLI command to create these functions.
|
|
686
|
+
* Enables deployment to runtimes that prohibit `new Function`/eval (e.g. Cloudflare Workers).
|
|
687
|
+
*/
|
|
688
|
+
compiledFunctions?: CompiledFunctions;
|
|
673
689
|
/**
|
|
674
690
|
* Default loading strategy for relations.
|
|
675
691
|
* - `'joined'`: Use SQL JOINs (single query, may cause cartesian product)
|
|
@@ -741,6 +757,12 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
741
757
|
* @default false
|
|
742
758
|
*/
|
|
743
759
|
allowGlobalContext?: boolean;
|
|
760
|
+
/**
|
|
761
|
+
* When enabled, environment variables take precedence over explicitly provided config options.
|
|
762
|
+
* By default, explicit options win over env vars.
|
|
763
|
+
* @default false
|
|
764
|
+
*/
|
|
765
|
+
preferEnvVars?: boolean;
|
|
744
766
|
/**
|
|
745
767
|
* Disable the identity map.
|
|
746
768
|
* When disabled, each query returns new entity instances.
|
|
@@ -824,6 +846,10 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
824
846
|
* @default false
|
|
825
847
|
*/
|
|
826
848
|
disableForeignKeys?: boolean;
|
|
849
|
+
/**
|
|
850
|
+
* Try to disable foreign key checks during `schema.clear()`. Enabled by default for MySQL/MariaDB.
|
|
851
|
+
*/
|
|
852
|
+
disableForeignKeysForClear?: boolean;
|
|
827
853
|
/**
|
|
828
854
|
* Generate foreign key constraints.
|
|
829
855
|
* @default true
|
|
@@ -839,6 +865,11 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
839
865
|
* @default []
|
|
840
866
|
*/
|
|
841
867
|
skipTables?: (string | RegExp)[];
|
|
868
|
+
/**
|
|
869
|
+
* View names or patterns to skip during schema generation (e.g. PostGIS system views).
|
|
870
|
+
* @default []
|
|
871
|
+
*/
|
|
872
|
+
skipViews?: (string | RegExp)[];
|
|
842
873
|
/**
|
|
843
874
|
* Column names or patterns to skip during schema generation, keyed by table name.
|
|
844
875
|
* @default {}
|
|
@@ -848,6 +879,16 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
848
879
|
* Database name to use for management operations (e.g., creating/dropping databases).
|
|
849
880
|
*/
|
|
850
881
|
managementDbName?: string;
|
|
882
|
+
/**
|
|
883
|
+
* Default ON UPDATE rule for foreign keys.
|
|
884
|
+
* When not set, no rule is emitted and the database uses its native default (NO ACTION/RESTRICT).
|
|
885
|
+
*/
|
|
886
|
+
defaultUpdateRule?: 'cascade' | 'no action' | 'set null' | 'set default' | 'restrict';
|
|
887
|
+
/**
|
|
888
|
+
* Default ON DELETE rule for foreign keys.
|
|
889
|
+
* When not set, no rule is emitted and the database uses its native default (NO ACTION/RESTRICT).
|
|
890
|
+
*/
|
|
891
|
+
defaultDeleteRule?: 'cascade' | 'no action' | 'set null' | 'set default' | 'restrict';
|
|
851
892
|
};
|
|
852
893
|
/**
|
|
853
894
|
* Embeddable entity configuration options.
|
package/utils/Configuration.js
CHANGED
|
@@ -10,6 +10,7 @@ import { RequestContext } from './RequestContext.js';
|
|
|
10
10
|
import { DataloaderType, FlushMode, LoadStrategy, PopulateHint } from '../enums.js';
|
|
11
11
|
import { MemoryCacheAdapter } from '../cache/MemoryCacheAdapter.js';
|
|
12
12
|
import { EntityComparator } from './EntityComparator.js';
|
|
13
|
+
import { setEnv } from './env-vars.js';
|
|
13
14
|
const DEFAULTS = {
|
|
14
15
|
pool: {},
|
|
15
16
|
entities: [],
|
|
@@ -34,7 +35,7 @@ const DEFAULTS = {
|
|
|
34
35
|
colors: true,
|
|
35
36
|
findOneOrFailHandler: (entityName, where) => NotFoundError.findOneFailed(entityName, where),
|
|
36
37
|
findExactlyOneOrFailHandler: (entityName, where) => NotFoundError.findExactlyOneFailed(entityName, where),
|
|
37
|
-
baseDir: process
|
|
38
|
+
baseDir: globalThis.process?.cwd?.(),
|
|
38
39
|
hydrator: ObjectHydrator,
|
|
39
40
|
flushMode: FlushMode.AUTO,
|
|
40
41
|
loadStrategy: LoadStrategy.BALANCED,
|
|
@@ -61,6 +62,7 @@ const DEFAULTS = {
|
|
|
61
62
|
upsertManaged: true,
|
|
62
63
|
forceEntityConstructor: false,
|
|
63
64
|
forceUndefined: false,
|
|
65
|
+
forceUtcTimezone: true,
|
|
64
66
|
processOnCreateHooksEarly: true,
|
|
65
67
|
ensureDatabase: true,
|
|
66
68
|
ensureIndexes: false,
|
|
@@ -74,7 +76,6 @@ const DEFAULTS = {
|
|
|
74
76
|
glob: '!(*.d).{js,ts,cjs}',
|
|
75
77
|
silent: false,
|
|
76
78
|
transactional: true,
|
|
77
|
-
disableForeignKeys: false,
|
|
78
79
|
allOrNothing: true,
|
|
79
80
|
dropTables: true,
|
|
80
81
|
safe: false,
|
|
@@ -83,10 +84,10 @@ const DEFAULTS = {
|
|
|
83
84
|
fileName: (timestamp, name) => `Migration${timestamp}${name ? '_' + name : ''}`,
|
|
84
85
|
},
|
|
85
86
|
schemaGenerator: {
|
|
86
|
-
disableForeignKeys: false,
|
|
87
87
|
createForeignKeyConstraints: true,
|
|
88
88
|
ignoreSchema: [],
|
|
89
89
|
skipTables: [],
|
|
90
|
+
skipViews: [],
|
|
90
91
|
skipColumns: {},
|
|
91
92
|
},
|
|
92
93
|
embeddables: {
|
|
@@ -135,10 +136,9 @@ export class Configuration {
|
|
|
135
136
|
extensions = new Map();
|
|
136
137
|
constructor(options, validate = true) {
|
|
137
138
|
if (options.dynamicImportProvider) {
|
|
138
|
-
|
|
139
|
+
globalThis.dynamicImportProvider = options.dynamicImportProvider;
|
|
139
140
|
}
|
|
140
141
|
this.options = Utils.mergeConfig({}, DEFAULTS, options);
|
|
141
|
-
this.options.baseDir = Utils.absolutePath(this.options.baseDir);
|
|
142
142
|
if (validate) {
|
|
143
143
|
this.validateOptions();
|
|
144
144
|
}
|
|
@@ -150,6 +150,10 @@ export class Configuration {
|
|
|
150
150
|
highlighter: this.options.highlighter,
|
|
151
151
|
writer: this.options.logger,
|
|
152
152
|
});
|
|
153
|
+
const cf = this.options.compiledFunctions;
|
|
154
|
+
if (cf && cf.__version !== Utils.getORMVersion()) {
|
|
155
|
+
this.logger.warn('discovery', `Compiled functions were generated with MikroORM v${cf.__version ?? 'unknown'}, but the current version is v${Utils.getORMVersion()}. Please regenerate with \`npx mikro-orm compile\`.`);
|
|
156
|
+
}
|
|
153
157
|
if (this.options.driver) {
|
|
154
158
|
this.driver = new this.options.driver(this);
|
|
155
159
|
this.platform = this.driver.getPlatform();
|
|
@@ -240,7 +244,7 @@ export class Configuration {
|
|
|
240
244
|
* Gets instance of Comparator. (cached)
|
|
241
245
|
*/
|
|
242
246
|
getComparator(metadata) {
|
|
243
|
-
return this.getCachedService(EntityComparator, metadata, this.platform);
|
|
247
|
+
return this.getCachedService(EntityComparator, metadata, this.platform, this);
|
|
244
248
|
}
|
|
245
249
|
/**
|
|
246
250
|
* Gets instance of MetadataProvider. (cached)
|
|
@@ -293,7 +297,7 @@ export class Configuration {
|
|
|
293
297
|
metadataCache.enabled ??= useCache;
|
|
294
298
|
this.options.clientUrl ??= this.platform.getDefaultClientUrl();
|
|
295
299
|
this.options.implicitTransactions ??= this.platform.usesImplicitTransactions();
|
|
296
|
-
if (metadataCache.enabled && !metadataCache.adapter) {
|
|
300
|
+
if (validate && metadataCache.enabled && !metadataCache.adapter) {
|
|
297
301
|
throw new Error('No metadata cache adapter specified, please fill in `metadataCache.adapter` option or use the async MikroORM.init() method which can autoload it.');
|
|
298
302
|
}
|
|
299
303
|
try {
|
|
@@ -328,7 +332,7 @@ export class Configuration {
|
|
|
328
332
|
}
|
|
329
333
|
}
|
|
330
334
|
sync() {
|
|
331
|
-
|
|
335
|
+
setEnv('MIKRO_ORM_COLORS', this.options.colors);
|
|
332
336
|
this.logger.setDebugMode(this.options.debug);
|
|
333
337
|
}
|
|
334
338
|
validateOptions() {
|
package/utils/Cursor.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Utils } from './Utils.js';
|
|
|
2
2
|
import { ReferenceKind } from '../enums.js';
|
|
3
3
|
import { Reference } from '../entity/Reference.js';
|
|
4
4
|
import { helper } from '../entity/wrap.js';
|
|
5
|
-
import {
|
|
5
|
+
import { Raw } from '../utils/RawQueryFragment.js';
|
|
6
6
|
import { CursorError } from '../errors.js';
|
|
7
7
|
import { inspect } from '../logging/inspect.js';
|
|
8
8
|
/**
|
|
@@ -95,15 +95,22 @@ export class Cursor {
|
|
|
95
95
|
from(entity) {
|
|
96
96
|
const processEntity = (entity, prop, direction, object = false) => {
|
|
97
97
|
if (Utils.isPlainObject(direction)) {
|
|
98
|
+
const unwrapped = Reference.unwrapReference(entity[prop]);
|
|
99
|
+
// Check if the relation is loaded - for nested properties, undefined means not populated
|
|
100
|
+
if (Utils.isEntity(unwrapped) && !helper(unwrapped).isInitialized()) {
|
|
101
|
+
throw CursorError.entityNotPopulated(entity, prop);
|
|
102
|
+
}
|
|
98
103
|
return Utils.keys(direction).reduce((o, key) => {
|
|
99
|
-
Object.assign(o, processEntity(
|
|
104
|
+
Object.assign(o, processEntity(unwrapped, key, direction[key], true));
|
|
100
105
|
return o;
|
|
101
106
|
}, {});
|
|
102
107
|
}
|
|
103
|
-
if (entity[prop] == null) {
|
|
104
|
-
throw CursorError.entityNotPopulated(entity, prop);
|
|
105
|
-
}
|
|
106
108
|
let value = entity[prop];
|
|
109
|
+
// Allow null/undefined values in cursor - they will be handled in createCursorCondition
|
|
110
|
+
// undefined can occur with forceUndefined config option which converts null to undefined
|
|
111
|
+
if (value == null) {
|
|
112
|
+
return object ? { [prop]: null } : null;
|
|
113
|
+
}
|
|
107
114
|
if (Utils.isEntity(value, true)) {
|
|
108
115
|
value = helper(value).getPrimaryKey();
|
|
109
116
|
}
|
|
@@ -131,7 +138,13 @@ export class Cursor {
|
|
|
131
138
|
*/
|
|
132
139
|
static for(meta, entity, orderBy) {
|
|
133
140
|
const definition = this.getDefinition(meta, orderBy);
|
|
134
|
-
return Cursor.encode(definition.map(([key]) =>
|
|
141
|
+
return Cursor.encode(definition.map(([key]) => {
|
|
142
|
+
const value = entity[key];
|
|
143
|
+
if (value === undefined) {
|
|
144
|
+
throw CursorError.missingValue(meta.className, key);
|
|
145
|
+
}
|
|
146
|
+
return value;
|
|
147
|
+
}));
|
|
135
148
|
}
|
|
136
149
|
static encode(value) {
|
|
137
150
|
return Buffer.from(JSON.stringify(value)).toString('base64url');
|
|
@@ -147,8 +160,8 @@ export class Cursor {
|
|
|
147
160
|
static getDefinition(meta, orderBy) {
|
|
148
161
|
return Utils.asArray(orderBy).flatMap(order => {
|
|
149
162
|
const ret = [];
|
|
150
|
-
for (const key of Utils.
|
|
151
|
-
if (
|
|
163
|
+
for (const key of Utils.getObjectQueryKeys(order)) {
|
|
164
|
+
if (Raw.isKnownFragmentSymbol(key)) {
|
|
152
165
|
ret.push([key, order[key]]);
|
|
153
166
|
continue;
|
|
154
167
|
}
|
package/utils/DataloaderUtils.js
CHANGED
|
@@ -11,7 +11,7 @@ export class DataloaderUtils {
|
|
|
11
11
|
static groupPrimaryKeysByEntityAndOpts(refsWithOpts) {
|
|
12
12
|
const map = new Map();
|
|
13
13
|
for (const [ref, opts] of refsWithOpts) {
|
|
14
|
-
/* The key is a combination of the
|
|
14
|
+
/* The key is a combination of the uniqueName (a unique table name based identifier) and a stringified version if the load options because we want
|
|
15
15
|
to map each combination of entities/options into separate find queries in order to return accurate results.
|
|
16
16
|
This could be further optimized finding the "lowest common denominator" among the different options
|
|
17
17
|
for each Entity and firing a single query for each Entity instead of Entity+options combination.
|
|
@@ -24,7 +24,7 @@ export class DataloaderUtils {
|
|
|
24
24
|
Thus such approach should probably be configurable, if not opt-in.
|
|
25
25
|
NOTE: meta + opts multi maps (https://github.com/martian17/ds-js) might be a more elegant way
|
|
26
26
|
to implement this but not necessarily faster. */
|
|
27
|
-
const key = `${helper(ref).__meta.
|
|
27
|
+
const key = `${helper(ref).__meta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
28
28
|
let primaryKeysSet = map.get(key);
|
|
29
29
|
if (primaryKeysSet == null) {
|
|
30
30
|
primaryKeysSet = new Set();
|
|
@@ -42,9 +42,10 @@ export class DataloaderUtils {
|
|
|
42
42
|
return async (refsWithOpts) => {
|
|
43
43
|
const groupedIdsMap = DataloaderUtils.groupPrimaryKeysByEntityAndOpts(refsWithOpts);
|
|
44
44
|
const promises = Array.from(groupedIdsMap).map(([key, idsSet]) => {
|
|
45
|
-
const
|
|
45
|
+
const uniqueName = key.substring(0, key.indexOf('|'));
|
|
46
46
|
const opts = JSON.parse(key.substring(key.indexOf('|') + 1));
|
|
47
|
-
|
|
47
|
+
const meta = em.getMetadata().getByUniqueName(uniqueName);
|
|
48
|
+
return em.find(meta.class, Array.from(idsSet), opts);
|
|
48
49
|
});
|
|
49
50
|
await Promise.all(promises);
|
|
50
51
|
/* Instead of assigning each find result to the original reference we use a shortcut
|
|
@@ -70,7 +71,7 @@ export class DataloaderUtils {
|
|
|
70
71
|
The value is another Map which we can use to filter the find query to get results pertaining to the collections that have been dataloaded:
|
|
71
72
|
its keys are the props we are going to filter to and its values are the corresponding PKs.
|
|
72
73
|
*/
|
|
73
|
-
const key = `${col.property.targetMeta.
|
|
74
|
+
const key = `${col.property.targetMeta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
74
75
|
let filterMap = entitiesMap.get(key); // We are going to use this map to filter the entities pertaining to the collections that have been dataloaded.
|
|
75
76
|
if (filterMap == null) {
|
|
76
77
|
filterMap = new Map();
|
|
@@ -97,9 +98,10 @@ export class DataloaderUtils {
|
|
|
97
98
|
*/
|
|
98
99
|
static entitiesAndOptsMapToQueries(entitiesAndOptsMap, em) {
|
|
99
100
|
return Array.from(entitiesAndOptsMap, async ([key, filterMap]) => {
|
|
100
|
-
const
|
|
101
|
+
const uniqueName = key.substring(0, key.indexOf('|'));
|
|
101
102
|
const opts = JSON.parse(key.substring(key.indexOf('|') + 1));
|
|
102
|
-
const
|
|
103
|
+
const meta = em.getMetadata().getByUniqueName(uniqueName);
|
|
104
|
+
const res = await em.find(meta.class, opts?.where != null && Object.keys(opts.where).length > 0 ?
|
|
103
105
|
{
|
|
104
106
|
$and: [
|
|
105
107
|
{
|
|
@@ -121,7 +123,7 @@ export class DataloaderUtils {
|
|
|
121
123
|
...(opts.populate === false ? [] : opts.populate ?? []),
|
|
122
124
|
...Array.from(filterMap.keys()).filter(
|
|
123
125
|
// We need to do so only if the inverse side is a collection, because we can already retrieve the PK from a reference without having to load it
|
|
124
|
-
prop =>
|
|
126
|
+
prop => meta.properties[prop]?.ref !== true),
|
|
125
127
|
],
|
|
126
128
|
});
|
|
127
129
|
return [key, res];
|
|
@@ -164,7 +166,7 @@ export class DataloaderUtils {
|
|
|
164
166
|
// We need to filter the results in order to map each input collection
|
|
165
167
|
// to a subset of each query matching the collection items.
|
|
166
168
|
return collsWithOpts.map(([col, opts]) => {
|
|
167
|
-
const key = `${col.property.targetMeta.
|
|
169
|
+
const key = `${col.property.targetMeta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
168
170
|
const entities = resultsMap.get(key);
|
|
169
171
|
if (entities == null) {
|
|
170
172
|
// Should never happen
|
|
@@ -183,7 +185,7 @@ export class DataloaderUtils {
|
|
|
183
185
|
return async (collsWithOpts) => {
|
|
184
186
|
const groups = new Map();
|
|
185
187
|
for (const [col, opts] of collsWithOpts) {
|
|
186
|
-
const key = `${col.property.targetMeta.
|
|
188
|
+
const key = `${col.property.targetMeta.uniqueName}.${col.property.name}|${JSON.stringify(opts ?? {})}`;
|
|
187
189
|
const value = groups.get(key) ?? [];
|
|
188
190
|
value.push([col, opts ?? {}]);
|
|
189
191
|
groups.set(key, value);
|
|
@@ -198,7 +200,7 @@ export class DataloaderUtils {
|
|
|
198
200
|
const owners = group.map(c => c[0].owner);
|
|
199
201
|
const $or = [];
|
|
200
202
|
// a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
|
|
201
|
-
const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
|
|
203
|
+
const alias = em.config.getNamingStrategy().aliasName(Utils.className(prop.pivotEntity), 0);
|
|
202
204
|
const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
|
|
203
205
|
for (const c of group) {
|
|
204
206
|
$or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { EntityData, EntityDictionary, EntityMetadata, EntityName, EntityProperty, IMetadataStorage } from '../typings.js';
|
|
2
2
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
|
+
import type { Configuration } from './Configuration.js';
|
|
3
4
|
type Comparator<T> = (a: T, b: T, options?: {
|
|
4
5
|
includeInverseSides?: boolean;
|
|
5
6
|
}) => EntityData<T>;
|
|
@@ -9,6 +10,7 @@ type CompositeKeyPart = string | CompositeKeyPart[];
|
|
|
9
10
|
export declare class EntityComparator {
|
|
10
11
|
private readonly metadata;
|
|
11
12
|
private readonly platform;
|
|
13
|
+
private readonly config?;
|
|
12
14
|
private readonly comparators;
|
|
13
15
|
private readonly mappers;
|
|
14
16
|
private readonly snapshotGenerators;
|
|
@@ -16,23 +18,23 @@ export declare class EntityComparator {
|
|
|
16
18
|
private readonly pkGettersConverted;
|
|
17
19
|
private readonly pkSerializers;
|
|
18
20
|
private tmpIndex;
|
|
19
|
-
constructor(metadata: IMetadataStorage, platform: Platform);
|
|
21
|
+
constructor(metadata: IMetadataStorage, platform: Platform, config?: Configuration | undefined);
|
|
20
22
|
/**
|
|
21
23
|
* Computes difference between two entities.
|
|
22
24
|
*/
|
|
23
|
-
diffEntities<T>(entityName:
|
|
25
|
+
diffEntities<T extends object>(entityName: EntityName<T>, a: EntityData<T>, b: EntityData<T>, options?: {
|
|
24
26
|
includeInverseSides?: boolean;
|
|
25
27
|
}): EntityData<T>;
|
|
26
|
-
matching<T>(entityName:
|
|
28
|
+
matching<T extends object>(entityName: EntityName<T>, a: EntityData<T>, b: EntityData<T>): boolean;
|
|
27
29
|
/**
|
|
28
30
|
* Removes ORM specific code from entities and prepares it for serializing. Used before change set computation.
|
|
29
31
|
* References will be mapped to primary keys, collections to arrays of primary keys.
|
|
30
32
|
*/
|
|
31
|
-
prepareEntity<T>(entity: T): EntityData<T>;
|
|
33
|
+
prepareEntity<T extends object>(entity: T): EntityData<T>;
|
|
32
34
|
/**
|
|
33
35
|
* Maps database columns to properties.
|
|
34
36
|
*/
|
|
35
|
-
mapResult<T>(
|
|
37
|
+
mapResult<T>(meta: EntityMetadata<T>, result: EntityDictionary<T>): EntityData<T>;
|
|
36
38
|
/**
|
|
37
39
|
* @internal Highly performance-sensitive method.
|
|
38
40
|
*/
|
|
@@ -64,7 +66,7 @@ export declare class EntityComparator {
|
|
|
64
66
|
/**
|
|
65
67
|
* @internal Highly performance-sensitive method.
|
|
66
68
|
*/
|
|
67
|
-
getResultMapper<T>(
|
|
69
|
+
getResultMapper<T>(meta: EntityMetadata<T>): ResultMapper<T>;
|
|
68
70
|
private getPropertyCondition;
|
|
69
71
|
private getEmbeddedArrayPropertySnapshot;
|
|
70
72
|
/**
|
|
@@ -78,11 +80,16 @@ export declare class EntityComparator {
|
|
|
78
80
|
/**
|
|
79
81
|
* @internal Highly performance-sensitive method.
|
|
80
82
|
*/
|
|
81
|
-
getEntityComparator<T extends object>(entityName:
|
|
83
|
+
getEntityComparator<T extends object>(entityName: EntityName<T>): Comparator<T>;
|
|
82
84
|
private getGenericComparator;
|
|
83
85
|
private getPropertyComparator;
|
|
84
86
|
private wrap;
|
|
85
87
|
private safeKey;
|
|
88
|
+
/**
|
|
89
|
+
* Sets the toArray helper in the context if not already set.
|
|
90
|
+
* Used for converting composite PKs to arrays.
|
|
91
|
+
*/
|
|
92
|
+
private setToArrayHelper;
|
|
86
93
|
/**
|
|
87
94
|
* perf: used to generate list of comparable properties during discovery, so we speed up the runtime comparison
|
|
88
95
|
*/
|