@mikro-orm/core 7.0.0-dev.2 → 7.0.0-dev.200
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 +111 -61
- package/EntityManager.js +346 -300
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +103 -143
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +8 -7
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +0 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +16 -7
- package/connections/Connection.js +23 -14
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +80 -35
- package/drivers/IDatabaseDriver.d.ts +47 -17
- package/entity/BaseEntity.d.ts +2 -2
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +95 -31
- package/entity/Collection.js +444 -102
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +13 -1
- package/entity/EntityFactory.js +88 -54
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +38 -15
- package/entity/EntityLoader.d.ts +8 -7
- package/entity/EntityLoader.js +134 -80
- package/entity/EntityRepository.d.ts +24 -4
- package/entity/EntityRepository.js +8 -2
- package/entity/Reference.d.ts +9 -12
- package/entity/Reference.js +34 -9
- package/entity/WrappedEntity.d.ts +2 -7
- package/entity/WrappedEntity.js +3 -8
- package/entity/defineEntity.d.ts +585 -0
- package/entity/defineEntity.js +533 -0
- package/entity/index.d.ts +3 -2
- package/entity/index.js +3 -2
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +16 -4
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +22 -6
- package/enums.js +15 -1
- package/errors.d.ts +23 -9
- package/errors.js +59 -21
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +19 -11
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +53 -33
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +1 -0
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +7 -6
- package/logging/index.d.ts +1 -0
- package/logging/index.js +1 -0
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +26 -26
- package/metadata/EntitySchema.js +82 -51
- package/metadata/MetadataDiscovery.d.ts +7 -10
- package/metadata/MetadataDiscovery.js +408 -335
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +46 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +70 -37
- package/metadata/MetadataValidator.d.ts +17 -9
- package/metadata/MetadataValidator.js +100 -42
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +1 -1
- package/metadata/types.d.ts +502 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +12 -4
- package/naming-strategy/AbstractNamingStrategy.js +14 -2
- 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 +24 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/not-supported.d.ts +2 -0
- package/not-supported.js +4 -0
- package/package.json +19 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +7 -13
- package/platforms/Platform.js +20 -43
- package/serialization/EntitySerializer.d.ts +5 -0
- package/serialization/EntitySerializer.js +47 -27
- package/serialization/EntityTransformer.js +28 -18
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +16 -13
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +4 -1
- 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 +3 -3
- package/types/DoubleType.js +2 -2
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/index.d.ts +1 -1
- package/typings.d.ts +300 -140
- package/typings.js +62 -44
- package/unit-of-work/ChangeSet.d.ts +2 -6
- package/unit-of-work/ChangeSet.js +4 -5
- package/unit-of-work/ChangeSetComputer.d.ts +1 -3
- package/unit-of-work/ChangeSetComputer.js +26 -13
- package/unit-of-work/ChangeSetPersister.d.ts +5 -4
- package/unit-of-work/ChangeSetPersister.js +77 -35
- 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 +23 -3
- package/unit-of-work/UnitOfWork.js +199 -106
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +22 -17
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +779 -207
- package/utils/Configuration.js +146 -190
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +3 -6
- package/utils/Cursor.js +27 -11
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +65 -17
- package/utils/EntityComparator.d.ts +13 -9
- package/utils/EntityComparator.js +164 -89
- package/utils/QueryHelper.d.ts +14 -6
- package/utils/QueryHelper.js +88 -26
- package/utils/RawQueryFragment.d.ts +48 -25
- package/utils/RawQueryFragment.js +67 -66
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +13 -120
- package/utils/Utils.js +104 -375
- package/utils/clone.js +8 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +97 -0
- package/utils/fs-utils.d.ts +32 -0
- package/utils/fs-utils.js +178 -0
- package/utils/index.d.ts +2 -1
- package/utils/index.js +2 -1
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +55 -4
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -29
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -18
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -18
- package/decorators/Entity.js +0 -13
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -5
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -17
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -40
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -30
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -24
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -9
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -13
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -116
- package/entity/ArrayCollection.js +0 -395
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import type { EntityMetadata } from '../typings.js';
|
|
2
2
|
import type { Logger } from '../logging/Logger.js';
|
|
3
|
+
import type { SyncCacheAdapter } from '../cache/CacheAdapter.js';
|
|
4
|
+
import type { Platform } from '../platforms/Platform.js';
|
|
3
5
|
export interface IConfiguration {
|
|
4
6
|
get(key: string, defaultValue?: any): any;
|
|
5
7
|
getLogger(): Logger;
|
|
8
|
+
getMetadataCacheAdapter(): SyncCacheAdapter;
|
|
9
|
+
getPlatform(): Platform;
|
|
6
10
|
}
|
|
7
|
-
export declare
|
|
11
|
+
export declare class MetadataProvider {
|
|
8
12
|
protected readonly config: IConfiguration;
|
|
9
13
|
constructor(config: IConfiguration);
|
|
10
|
-
|
|
14
|
+
loadEntityMetadata(meta: EntityMetadata): void;
|
|
11
15
|
loadFromCache(meta: EntityMetadata, cache: EntityMetadata): void;
|
|
16
|
+
static useCache(): boolean;
|
|
12
17
|
useCache(): boolean;
|
|
18
|
+
saveToCache(meta: EntityMetadata): void;
|
|
19
|
+
getCachedMetadata<T>(meta: Pick<EntityMetadata<T>, 'className' | 'path' | 'root'>, root: EntityMetadata<T>): EntityMetadata<T> | undefined;
|
|
20
|
+
combineCache(): void;
|
|
21
|
+
getCacheKey(meta: Pick<EntityMetadata, 'className' | 'path'>): string;
|
|
13
22
|
}
|
|
@@ -1,20 +1,64 @@
|
|
|
1
1
|
import { Utils } from '../utils/Utils.js';
|
|
2
|
+
import { EntitySchema } from './EntitySchema.js';
|
|
2
3
|
export class MetadataProvider {
|
|
3
4
|
config;
|
|
4
5
|
constructor(config) {
|
|
5
6
|
this.config = config;
|
|
6
7
|
}
|
|
8
|
+
loadEntityMetadata(meta) {
|
|
9
|
+
for (const prop of meta.props) {
|
|
10
|
+
/* v8 ignore next */
|
|
11
|
+
if (typeof prop.entity === 'string') {
|
|
12
|
+
prop.type = prop.entity;
|
|
13
|
+
}
|
|
14
|
+
else if (prop.entity) {
|
|
15
|
+
const tmp = prop.entity();
|
|
16
|
+
prop.type = Array.isArray(tmp) ? tmp.map(t => Utils.className(t)).sort().join(' | ') : Utils.className(tmp);
|
|
17
|
+
prop.target = tmp instanceof EntitySchema ? tmp.meta.class : tmp;
|
|
18
|
+
}
|
|
19
|
+
else if (!prop.type && !((prop.enum || prop.array) && (prop.items?.length ?? 0) > 0)) {
|
|
20
|
+
throw new Error(`Please provide either 'type' or 'entity' attribute in ${meta.className}.${prop.name}.`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
7
24
|
loadFromCache(meta, cache) {
|
|
8
25
|
Object.values(cache.properties).forEach(prop => {
|
|
9
26
|
const metaProp = meta.properties[prop.name];
|
|
10
|
-
/* v8 ignore next
|
|
27
|
+
/* v8 ignore next */
|
|
11
28
|
if (metaProp?.enum && Array.isArray(metaProp.items)) {
|
|
12
29
|
delete prop.items;
|
|
13
30
|
}
|
|
14
31
|
});
|
|
15
32
|
Utils.mergeConfig(meta, cache);
|
|
16
33
|
}
|
|
34
|
+
static useCache() {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
17
37
|
useCache() {
|
|
18
|
-
return this.config.get('metadataCache').enabled ??
|
|
38
|
+
return this.config.get('metadataCache').enabled ?? MetadataProvider.useCache();
|
|
39
|
+
}
|
|
40
|
+
saveToCache(meta) {
|
|
41
|
+
//
|
|
42
|
+
}
|
|
43
|
+
getCachedMetadata(meta, root) {
|
|
44
|
+
if (!this.useCache()) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const cache = meta.path && this.config.getMetadataCacheAdapter().get(this.getCacheKey(meta));
|
|
48
|
+
if (cache) {
|
|
49
|
+
this.loadFromCache(meta, cache);
|
|
50
|
+
meta.root = root;
|
|
51
|
+
}
|
|
52
|
+
return cache;
|
|
53
|
+
}
|
|
54
|
+
combineCache() {
|
|
55
|
+
const path = this.config.getMetadataCacheAdapter().combine?.();
|
|
56
|
+
// override the path in the options, so we can log it from the CLI in `cache:generate` command
|
|
57
|
+
if (path) {
|
|
58
|
+
this.config.get('metadataCache').combined = path;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
getCacheKey(meta) {
|
|
62
|
+
return meta.className;
|
|
19
63
|
}
|
|
20
64
|
}
|
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Dictionary, EntityMetadata, type EntityName } from '../typings.js';
|
|
2
2
|
import type { EntityManager } from '../EntityManager.js';
|
|
3
3
|
export declare class MetadataStorage {
|
|
4
4
|
static readonly PATH_SYMBOL: unique symbol;
|
|
5
5
|
private static readonly metadata;
|
|
6
6
|
private readonly metadata;
|
|
7
|
+
private readonly idMap;
|
|
8
|
+
private readonly classNameMap;
|
|
9
|
+
private readonly uniqueNameMap;
|
|
7
10
|
constructor(metadata?: Dictionary<EntityMetadata>);
|
|
8
11
|
static getMetadata(): Dictionary<EntityMetadata>;
|
|
9
12
|
static getMetadata<T = any>(entity: string, path: string): EntityMetadata<T>;
|
|
10
13
|
static isKnownEntity(name: string): boolean;
|
|
11
|
-
static getMetadataFromDecorator<T = any>(target: T & Dictionary & {
|
|
12
|
-
[MetadataStorage.PATH_SYMBOL]?: string;
|
|
13
|
-
}): EntityMetadata<T>;
|
|
14
|
-
static init(): MetadataStorage;
|
|
15
14
|
static clear(): void;
|
|
16
|
-
getAll():
|
|
17
|
-
|
|
18
|
-
get<T = any>(entityName: EntityName<T>, init?: boolean, validate?: boolean): EntityMetadata<T>;
|
|
15
|
+
getAll(): Map<EntityName, EntityMetadata>;
|
|
16
|
+
get<T = any>(entityName: EntityName<T>, init?: boolean): EntityMetadata<T>;
|
|
19
17
|
find<T = any>(entityName: EntityName<T>): EntityMetadata<T> | undefined;
|
|
20
|
-
has(
|
|
21
|
-
set(
|
|
22
|
-
reset(
|
|
18
|
+
has<T>(entityName: EntityName<T>): boolean;
|
|
19
|
+
set<T>(entityName: EntityName<T>, meta: EntityMetadata): EntityMetadata;
|
|
20
|
+
reset<T>(entityName: EntityName<T>): void;
|
|
23
21
|
decorate(em: EntityManager): void;
|
|
24
22
|
[Symbol.iterator](): IterableIterator<EntityMetadata>;
|
|
23
|
+
getById<T>(id: number): EntityMetadata<T>;
|
|
24
|
+
getByClassName<T = any, V extends boolean = true>(className: string, validate?: V): V extends true ? EntityMetadata<T> : EntityMetadata<T> | undefined;
|
|
25
|
+
getByUniqueName<T = any, V extends boolean = true>(uniqueName: string, validate?: V): V extends true ? EntityMetadata<T> : EntityMetadata<T> | undefined;
|
|
26
|
+
private validate;
|
|
25
27
|
}
|
|
@@ -2,12 +2,28 @@ import { EntityMetadata } from '../typings.js';
|
|
|
2
2
|
import { Utils } from '../utils/Utils.js';
|
|
3
3
|
import { MetadataError } from '../errors.js';
|
|
4
4
|
import { EntityHelper } from '../entity/EntityHelper.js';
|
|
5
|
+
import { EntitySchema } from './EntitySchema.js';
|
|
6
|
+
function getGlobalStorage(namespace) {
|
|
7
|
+
const key = `mikro-orm-${namespace}`;
|
|
8
|
+
globalThis[key] = globalThis[key] || {};
|
|
9
|
+
return globalThis[key];
|
|
10
|
+
}
|
|
5
11
|
export class MetadataStorage {
|
|
6
12
|
static PATH_SYMBOL = Symbol('MetadataStorage.PATH_SYMBOL');
|
|
7
|
-
static metadata =
|
|
8
|
-
metadata;
|
|
13
|
+
static metadata = getGlobalStorage('metadata');
|
|
14
|
+
metadata = new Map();
|
|
15
|
+
idMap;
|
|
16
|
+
classNameMap;
|
|
17
|
+
uniqueNameMap;
|
|
9
18
|
constructor(metadata = {}) {
|
|
10
|
-
this.
|
|
19
|
+
this.idMap = {};
|
|
20
|
+
this.uniqueNameMap = {};
|
|
21
|
+
this.classNameMap = Utils.copy(metadata, false);
|
|
22
|
+
for (const meta of Object.values(this.classNameMap)) {
|
|
23
|
+
this.idMap[meta._id] = meta;
|
|
24
|
+
this.uniqueNameMap[meta.uniqueName] = meta;
|
|
25
|
+
this.metadata.set(meta.class, meta);
|
|
26
|
+
}
|
|
11
27
|
}
|
|
12
28
|
static getMetadata(entity, path) {
|
|
13
29
|
const key = entity && path ? entity + '-' + Utils.hash(path) : null;
|
|
@@ -22,63 +38,80 @@ export class MetadataStorage {
|
|
|
22
38
|
static isKnownEntity(name) {
|
|
23
39
|
return !!Object.values(this.metadata).find(meta => meta.className === name);
|
|
24
40
|
}
|
|
25
|
-
static getMetadataFromDecorator(target) {
|
|
26
|
-
if (!Object.hasOwn(target, MetadataStorage.PATH_SYMBOL)) {
|
|
27
|
-
Object.defineProperty(target, MetadataStorage.PATH_SYMBOL, { value: Utils.lookupPathFromDecorator(target.name), writable: true });
|
|
28
|
-
}
|
|
29
|
-
return MetadataStorage.getMetadata(target.name, target[MetadataStorage.PATH_SYMBOL]);
|
|
30
|
-
}
|
|
31
|
-
static init() {
|
|
32
|
-
return new MetadataStorage(MetadataStorage.metadata);
|
|
33
|
-
}
|
|
34
41
|
static clear() {
|
|
35
42
|
Object.keys(this.metadata).forEach(k => delete this.metadata[k]);
|
|
36
43
|
}
|
|
37
44
|
getAll() {
|
|
38
45
|
return this.metadata;
|
|
39
46
|
}
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
const type = meta.root.discriminatorMap[value];
|
|
46
|
-
return this.metadata[type];
|
|
47
|
-
}
|
|
48
|
-
get(entityName, init = false, validate = true) {
|
|
49
|
-
entityName = Utils.className(entityName);
|
|
50
|
-
if (validate && !init && !this.has(entityName)) {
|
|
51
|
-
throw MetadataError.missingMetadata(entityName);
|
|
47
|
+
get(entityName, init = false) {
|
|
48
|
+
const exists = this.find(entityName);
|
|
49
|
+
if (exists) {
|
|
50
|
+
return exists;
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const className = Utils.className(entityName);
|
|
53
|
+
if (!init) {
|
|
54
|
+
throw MetadataError.missingMetadata(className);
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
const meta = new EntityMetadata({ class: entityName, name: className });
|
|
57
|
+
this.set(entityName, meta);
|
|
58
|
+
return meta;
|
|
57
59
|
}
|
|
58
60
|
find(entityName) {
|
|
59
61
|
if (!entityName) {
|
|
60
62
|
return;
|
|
61
63
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
const meta = this.metadata.get(entityName);
|
|
65
|
+
if (meta) {
|
|
66
|
+
return meta;
|
|
67
|
+
}
|
|
68
|
+
if (entityName instanceof EntitySchema) {
|
|
69
|
+
return this.metadata.get(entityName.meta.class) ?? entityName.meta;
|
|
70
|
+
}
|
|
71
|
+
return this.classNameMap[Utils.className(entityName)];
|
|
64
72
|
}
|
|
65
|
-
has(
|
|
66
|
-
return
|
|
73
|
+
has(entityName) {
|
|
74
|
+
return this.metadata.has(entityName);
|
|
67
75
|
}
|
|
68
|
-
set(
|
|
69
|
-
|
|
76
|
+
set(entityName, meta) {
|
|
77
|
+
this.metadata.set(entityName, meta);
|
|
78
|
+
this.idMap[meta._id] = meta;
|
|
79
|
+
this.uniqueNameMap[meta.uniqueName] = meta;
|
|
80
|
+
this.classNameMap[Utils.className(entityName)] = meta;
|
|
81
|
+
return meta;
|
|
70
82
|
}
|
|
71
|
-
reset(
|
|
72
|
-
|
|
83
|
+
reset(entityName) {
|
|
84
|
+
const meta = this.find(entityName);
|
|
85
|
+
if (meta) {
|
|
86
|
+
this.metadata.delete(meta.class);
|
|
87
|
+
delete this.idMap[meta._id];
|
|
88
|
+
delete this.uniqueNameMap[meta.uniqueName];
|
|
89
|
+
delete this.classNameMap[meta.className];
|
|
90
|
+
}
|
|
73
91
|
}
|
|
74
92
|
decorate(em) {
|
|
75
|
-
|
|
93
|
+
[...this.metadata.values()]
|
|
76
94
|
.filter(meta => meta.prototype)
|
|
77
95
|
.forEach(meta => EntityHelper.decorate(meta, em));
|
|
78
96
|
}
|
|
79
97
|
*[Symbol.iterator]() {
|
|
80
|
-
for (const meta of
|
|
98
|
+
for (const meta of this.metadata.values()) {
|
|
81
99
|
yield meta;
|
|
82
100
|
}
|
|
83
101
|
}
|
|
102
|
+
getById(id) {
|
|
103
|
+
return this.idMap[id];
|
|
104
|
+
}
|
|
105
|
+
getByClassName(className, validate = true) {
|
|
106
|
+
return this.validate(this.classNameMap[className], className, validate);
|
|
107
|
+
}
|
|
108
|
+
getByUniqueName(uniqueName, validate = true) {
|
|
109
|
+
return this.validate(this.uniqueNameMap[uniqueName], uniqueName, validate);
|
|
110
|
+
}
|
|
111
|
+
validate(meta, id, validate) {
|
|
112
|
+
if (!meta && validate) {
|
|
113
|
+
throw MetadataError.missingMetadata(id);
|
|
114
|
+
}
|
|
115
|
+
return meta;
|
|
116
|
+
}
|
|
84
117
|
}
|
|
@@ -1,24 +1,32 @@
|
|
|
1
|
-
import type { EntityMetadata } from '../typings.js';
|
|
1
|
+
import type { EntityMetadata, EntityName } from '../typings.js';
|
|
2
2
|
import { type MetadataDiscoveryOptions } from '../utils/Configuration.js';
|
|
3
|
-
import { ReferenceKind } from '../enums.js';
|
|
4
3
|
import type { MetadataStorage } from './MetadataStorage.js';
|
|
5
4
|
/**
|
|
6
5
|
* @internal
|
|
7
6
|
*/
|
|
8
7
|
export declare class MetadataValidator {
|
|
9
|
-
|
|
10
|
-
* Validate there is only one property decorator. This disallows using `@Property()` together with e.g. `@ManyToOne()`
|
|
11
|
-
* on the same property. One should use only `@ManyToOne()` in such case.
|
|
12
|
-
* We allow the existence of the property in metadata if the reference type is the same, this should allow things like HMR to work.
|
|
13
|
-
*/
|
|
14
|
-
static validateSingleDecorator(meta: EntityMetadata, propertyName: string, reference: ReferenceKind): void;
|
|
15
|
-
validateEntityDefinition<T>(metadata: MetadataStorage, name: string, options: MetadataDiscoveryOptions): void;
|
|
8
|
+
validateEntityDefinition<T>(metadata: MetadataStorage, name: EntityName<T>, options: MetadataDiscoveryOptions): void;
|
|
16
9
|
validateDiscovered(discovered: EntityMetadata[], options: MetadataDiscoveryOptions): void;
|
|
17
10
|
private validateReference;
|
|
11
|
+
private validateTargetKey;
|
|
18
12
|
private validateBidirectional;
|
|
19
13
|
private validateOwningSide;
|
|
20
14
|
private validateInverseSide;
|
|
21
15
|
private validateIndexes;
|
|
22
16
|
private validateDuplicateFieldNames;
|
|
23
17
|
private validateVersionField;
|
|
18
|
+
/**
|
|
19
|
+
* Validates that entity properties do not use dangerous names that could lead to
|
|
20
|
+
* prototype pollution vulnerabilities. This validation ensures that property names
|
|
21
|
+
* cannot be exploited to modify object prototypes when values are assigned during
|
|
22
|
+
* entity hydration or persistence operations.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
private validatePropertyNames;
|
|
27
|
+
/**
|
|
28
|
+
* Validates view entity configuration.
|
|
29
|
+
* View entities must have an expression.
|
|
30
|
+
*/
|
|
31
|
+
private validateViewEntity;
|
|
24
32
|
}
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import { Utils } from '../utils/Utils.js';
|
|
2
2
|
import { MetadataError } from '../errors.js';
|
|
3
3
|
import { ReferenceKind } from '../enums.js';
|
|
4
|
+
/**
|
|
5
|
+
* List of property names that could lead to prototype pollution vulnerabilities.
|
|
6
|
+
* These names should never be used as entity property names because they could
|
|
7
|
+
* allow malicious code to modify object prototypes when property values are assigned.
|
|
8
|
+
*
|
|
9
|
+
* - `__proto__`: Could modify the prototype chain
|
|
10
|
+
* - `constructor`: Could modify the constructor property
|
|
11
|
+
* - `prototype`: Could modify the prototype object
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
const DANGEROUS_PROPERTY_NAMES = ['__proto__', 'constructor', 'prototype'];
|
|
4
16
|
/**
|
|
5
17
|
* @internal
|
|
6
18
|
*/
|
|
7
19
|
export class MetadataValidator {
|
|
8
|
-
/**
|
|
9
|
-
* Validate there is only one property decorator. This disallows using `@Property()` together with e.g. `@ManyToOne()`
|
|
10
|
-
* on the same property. One should use only `@ManyToOne()` in such case.
|
|
11
|
-
* We allow the existence of the property in metadata if the reference type is the same, this should allow things like HMR to work.
|
|
12
|
-
*/
|
|
13
|
-
static validateSingleDecorator(meta, propertyName, reference) {
|
|
14
|
-
if (meta.properties[propertyName] && meta.properties[propertyName].kind !== reference) {
|
|
15
|
-
throw MetadataError.multipleDecorators(meta.className, propertyName);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
20
|
validateEntityDefinition(metadata, name, options) {
|
|
19
21
|
const meta = metadata.get(name);
|
|
20
|
-
if (meta.
|
|
22
|
+
if (meta.materialized && !meta.view) {
|
|
23
|
+
throw MetadataError.materializedWithoutView(meta);
|
|
24
|
+
}
|
|
25
|
+
// View entities (expression with view flag) behave like regular tables but are read-only
|
|
26
|
+
// They can have primary keys and are created as actual database views
|
|
27
|
+
if (meta.view) {
|
|
28
|
+
this.validateViewEntity(meta);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Virtual entities (expression without view flag) have restrictions - no PKs, limited relation types
|
|
32
|
+
// Note: meta.virtual is set later in sync(), so we check for expression && !view here
|
|
33
|
+
if (meta.virtual || (meta.expression && !meta.view)) {
|
|
21
34
|
for (const prop of Utils.values(meta.properties)) {
|
|
22
35
|
if (![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED, ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
23
36
|
throw new MetadataError(`Only scalars, embedded properties and to-many relations are allowed inside virtual entity. Found '${prop.kind}' in ${meta.className}.${prop.name}`);
|
|
@@ -36,13 +49,14 @@ export class MetadataValidator {
|
|
|
36
49
|
this.validateDuplicateFieldNames(meta, options);
|
|
37
50
|
this.validateIndexes(meta, meta.indexes ?? [], 'index');
|
|
38
51
|
this.validateIndexes(meta, meta.uniques ?? [], 'unique');
|
|
52
|
+
this.validatePropertyNames(meta);
|
|
39
53
|
for (const prop of Utils.values(meta.properties)) {
|
|
40
54
|
if (prop.kind !== ReferenceKind.SCALAR) {
|
|
41
|
-
this.validateReference(meta, prop,
|
|
42
|
-
this.validateBidirectional(meta, prop
|
|
55
|
+
this.validateReference(meta, prop, options);
|
|
56
|
+
this.validateBidirectional(meta, prop);
|
|
43
57
|
}
|
|
44
|
-
else if (metadata.
|
|
45
|
-
throw MetadataError.propertyTargetsEntityType(meta, prop, metadata.
|
|
58
|
+
else if (metadata.getByClassName(prop.type, false)) {
|
|
59
|
+
throw MetadataError.propertyTargetsEntityType(meta, prop, metadata.getByClassName(prop.type));
|
|
46
60
|
}
|
|
47
61
|
}
|
|
48
62
|
}
|
|
@@ -50,17 +64,13 @@ export class MetadataValidator {
|
|
|
50
64
|
if (discovered.length === 0 && options.warnWhenNoEntities) {
|
|
51
65
|
throw MetadataError.noEntityDiscovered();
|
|
52
66
|
}
|
|
53
|
-
const
|
|
54
|
-
if (duplicates.length > 0 && options.checkDuplicateEntities) {
|
|
55
|
-
throw MetadataError.duplicateEntityDiscovered(duplicates);
|
|
56
|
-
}
|
|
57
|
-
const tableNames = discovered.filter(meta => !meta.abstract && meta === meta.root && (meta.tableName || meta.collection) && meta.schema !== '*');
|
|
67
|
+
const tableNames = discovered.filter(meta => !meta.abstract && !meta.embeddable && meta === meta.root && (meta.tableName || meta.collection) && meta.schema !== '*');
|
|
58
68
|
const duplicateTableNames = Utils.findDuplicates(tableNames.map(meta => {
|
|
59
69
|
const tableName = meta.tableName || meta.collection;
|
|
60
70
|
return (meta.schema ? '.' + meta.schema : '') + tableName;
|
|
61
71
|
}));
|
|
62
|
-
if (duplicateTableNames.length > 0 && options.checkDuplicateTableNames
|
|
63
|
-
throw MetadataError.duplicateEntityDiscovered(duplicateTableNames
|
|
72
|
+
if (duplicateTableNames.length > 0 && options.checkDuplicateTableNames) {
|
|
73
|
+
throw MetadataError.duplicateEntityDiscovered(duplicateTableNames);
|
|
64
74
|
}
|
|
65
75
|
// validate we found at least one entity (not just abstract/base entities)
|
|
66
76
|
if (discovered.filter(meta => meta.name).length === 0 && options.warnWhenNoEntities) {
|
|
@@ -71,7 +81,7 @@ export class MetadataValidator {
|
|
|
71
81
|
.replace(/\[]$/, '') // remove array suffix
|
|
72
82
|
.replace(/\((.*)\)/, '$1'); // unwrap union types
|
|
73
83
|
const name = (p) => {
|
|
74
|
-
if (typeof p === 'function') {
|
|
84
|
+
if (typeof p === 'function' && !p.prototype) {
|
|
75
85
|
return Utils.className(p());
|
|
76
86
|
}
|
|
77
87
|
return Utils.className(p);
|
|
@@ -95,46 +105,63 @@ export class MetadataValidator {
|
|
|
95
105
|
}
|
|
96
106
|
});
|
|
97
107
|
}
|
|
98
|
-
validateReference(meta, prop,
|
|
108
|
+
validateReference(meta, prop, options) {
|
|
99
109
|
// references do have types
|
|
100
110
|
if (!prop.type) {
|
|
101
111
|
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
102
112
|
}
|
|
113
|
+
const targetMeta = prop.targetMeta;
|
|
103
114
|
// references do have type of known entity
|
|
104
|
-
if (!
|
|
115
|
+
if (!targetMeta) {
|
|
105
116
|
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
106
117
|
}
|
|
107
|
-
if (
|
|
118
|
+
if (targetMeta.abstract && !targetMeta.discriminatorColumn && !targetMeta.embeddable) {
|
|
108
119
|
throw MetadataError.targetIsAbstract(meta, prop);
|
|
109
120
|
}
|
|
110
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.persist === false &&
|
|
121
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.persist === false && targetMeta.compositePK && options.checkNonPersistentCompositeProps) {
|
|
111
122
|
throw MetadataError.nonPersistentCompositeProp(meta, prop);
|
|
112
123
|
}
|
|
124
|
+
this.validateTargetKey(meta, prop, targetMeta);
|
|
125
|
+
}
|
|
126
|
+
validateTargetKey(meta, prop, targetMeta) {
|
|
127
|
+
if (!prop.targetKey) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// targetKey is not supported for ManyToMany relations
|
|
131
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
132
|
+
throw MetadataError.targetKeyOnManyToMany(meta, prop);
|
|
133
|
+
}
|
|
134
|
+
// targetKey must point to an existing property
|
|
135
|
+
const targetProp = targetMeta.properties[prop.targetKey];
|
|
136
|
+
if (!targetProp) {
|
|
137
|
+
throw MetadataError.targetKeyNotFound(meta, prop);
|
|
138
|
+
}
|
|
139
|
+
// targetKey must point to a unique property
|
|
140
|
+
if (!targetProp.unique && !targetMeta.uniques?.some(u => u.properties?.includes(prop.targetKey))) {
|
|
141
|
+
throw MetadataError.targetKeyNotUnique(meta, prop);
|
|
142
|
+
}
|
|
113
143
|
}
|
|
114
|
-
validateBidirectional(meta, prop
|
|
144
|
+
validateBidirectional(meta, prop) {
|
|
115
145
|
if (prop.inversedBy) {
|
|
116
|
-
|
|
117
|
-
this.validateOwningSide(meta, prop, inverse, metadata);
|
|
146
|
+
this.validateOwningSide(meta, prop);
|
|
118
147
|
}
|
|
119
148
|
else if (prop.mappedBy) {
|
|
120
|
-
|
|
121
|
-
this.validateInverseSide(meta, prop, inverse, metadata);
|
|
149
|
+
this.validateInverseSide(meta, prop);
|
|
122
150
|
}
|
|
123
|
-
else {
|
|
151
|
+
else if (prop.kind === ReferenceKind.ONE_TO_MANY && !prop.mappedBy) {
|
|
124
152
|
// 1:m property has `mappedBy`
|
|
125
|
-
|
|
126
|
-
throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
|
|
127
|
-
}
|
|
153
|
+
throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
|
|
128
154
|
}
|
|
129
155
|
}
|
|
130
|
-
validateOwningSide(meta, prop
|
|
156
|
+
validateOwningSide(meta, prop) {
|
|
157
|
+
const inverse = prop.targetMeta.properties[prop.inversedBy];
|
|
131
158
|
// has correct `inversedBy` on owning side
|
|
132
159
|
if (!inverse) {
|
|
133
160
|
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy');
|
|
134
161
|
}
|
|
135
|
-
const
|
|
162
|
+
const targetClass = inverse.targetMeta?.root.class;
|
|
136
163
|
// has correct `inversedBy` reference type
|
|
137
|
-
if (inverse.type !== meta.className &&
|
|
164
|
+
if (inverse.type !== meta.className && targetClass !== meta.root.class) {
|
|
138
165
|
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
|
|
139
166
|
}
|
|
140
167
|
// inverse side is not defined as owner
|
|
@@ -142,13 +169,14 @@ export class MetadataValidator {
|
|
|
142
169
|
throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
|
|
143
170
|
}
|
|
144
171
|
}
|
|
145
|
-
validateInverseSide(meta, prop
|
|
172
|
+
validateInverseSide(meta, prop) {
|
|
173
|
+
const owner = prop.targetMeta.properties[prop.mappedBy];
|
|
146
174
|
// has correct `mappedBy` on inverse side
|
|
147
175
|
if (prop.mappedBy && !owner) {
|
|
148
176
|
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy');
|
|
149
177
|
}
|
|
150
178
|
// has correct `mappedBy` reference type
|
|
151
|
-
if (owner.type !== meta.className &&
|
|
179
|
+
if (owner.type !== meta.className && owner.targetMeta?.root.class !== meta.root.class) {
|
|
152
180
|
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy', owner);
|
|
153
181
|
}
|
|
154
182
|
// owning side is not defined as inverse
|
|
@@ -191,7 +219,7 @@ export class MetadataValidator {
|
|
|
191
219
|
return [prop.embedded ? prop.embedded.join('.') : prop.name, prop.fieldNames[0]];
|
|
192
220
|
});
|
|
193
221
|
});
|
|
194
|
-
throw MetadataError.duplicateFieldName(meta.
|
|
222
|
+
throw MetadataError.duplicateFieldName(meta.class, pairs);
|
|
195
223
|
}
|
|
196
224
|
}
|
|
197
225
|
validateVersionField(meta) {
|
|
@@ -208,4 +236,34 @@ export class MetadataValidator {
|
|
|
208
236
|
throw MetadataError.invalidVersionFieldType(meta);
|
|
209
237
|
}
|
|
210
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Validates that entity properties do not use dangerous names that could lead to
|
|
241
|
+
* prototype pollution vulnerabilities. This validation ensures that property names
|
|
242
|
+
* cannot be exploited to modify object prototypes when values are assigned during
|
|
243
|
+
* entity hydration or persistence operations.
|
|
244
|
+
*
|
|
245
|
+
* @internal
|
|
246
|
+
*/
|
|
247
|
+
validatePropertyNames(meta) {
|
|
248
|
+
for (const prop of Utils.values(meta.properties)) {
|
|
249
|
+
if (DANGEROUS_PROPERTY_NAMES.includes(prop.name)) {
|
|
250
|
+
throw MetadataError.dangerousPropertyName(meta, prop);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Validates view entity configuration.
|
|
256
|
+
* View entities must have an expression.
|
|
257
|
+
*/
|
|
258
|
+
validateViewEntity(meta) {
|
|
259
|
+
// View entities must have an expression
|
|
260
|
+
if (!meta.expression) {
|
|
261
|
+
throw MetadataError.viewEntityWithoutExpression(meta);
|
|
262
|
+
}
|
|
263
|
+
// Validate indexes if present
|
|
264
|
+
this.validateIndexes(meta, meta.indexes ?? [], 'index');
|
|
265
|
+
this.validateIndexes(meta, meta.uniques ?? [], 'unique');
|
|
266
|
+
// Validate property names
|
|
267
|
+
this.validatePropertyNames(meta);
|
|
268
|
+
}
|
|
211
269
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import { fs } from '../utils/fs-utils.js';
|
|
3
|
+
import { Utils } from '../utils/Utils.js';
|
|
4
|
+
import { MetadataStorage } from './MetadataStorage.js';
|
|
5
|
+
import { EntitySchema } from './EntitySchema.js';
|
|
6
|
+
async function getEntityClassOrSchema(filepath, allTargets, baseDir) {
|
|
7
|
+
const path = fs.normalizePath(baseDir, filepath);
|
|
8
|
+
const exports = await fs.dynamicImport(path);
|
|
9
|
+
const targets = Object.values(exports);
|
|
10
|
+
// ignore class implementations that are linked from an EntitySchema
|
|
11
|
+
for (const item of targets) {
|
|
12
|
+
if (item instanceof EntitySchema) {
|
|
13
|
+
for (const item2 of targets) {
|
|
14
|
+
if (item.meta.class === item2) {
|
|
15
|
+
targets.splice(targets.indexOf(item2), 1);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
for (const item of targets) {
|
|
21
|
+
const validTarget = item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name));
|
|
22
|
+
if (validTarget && !allTargets.has(item)) {
|
|
23
|
+
allTargets.set(item, path);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function discoverEntities(paths, options) {
|
|
28
|
+
paths = Utils.asArray(paths).map(path => fs.normalizePath(path));
|
|
29
|
+
const baseDir = fs.absolutePath(options?.baseDir ?? process.cwd());
|
|
30
|
+
const files = fs.glob(paths, fs.normalizePath(baseDir));
|
|
31
|
+
const found = new Map();
|
|
32
|
+
for (const filepath of files) {
|
|
33
|
+
const filename = basename(filepath);
|
|
34
|
+
if (!filename.match(/\.[cm]?[jt]s$/) || filename.match(/\.d\.[cm]?ts/)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
await getEntityClassOrSchema(filepath, found, baseDir);
|
|
38
|
+
}
|
|
39
|
+
return found.keys();
|
|
40
|
+
}
|
package/metadata/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
export * from './types.js';
|
|
1
2
|
export * from './EntitySchema.js';
|
|
2
3
|
export * from './MetadataDiscovery.js';
|
|
3
4
|
export * from './MetadataStorage.js';
|
|
4
5
|
export * from './MetadataProvider.js';
|
|
5
6
|
export * from './MetadataValidator.js';
|
|
6
|
-
export * from './ReflectMetadataProvider.js';
|
package/metadata/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
export * from './types.js';
|
|
1
2
|
export * from './EntitySchema.js';
|
|
2
3
|
export * from './MetadataDiscovery.js';
|
|
3
4
|
export * from './MetadataStorage.js';
|
|
4
5
|
export * from './MetadataProvider.js';
|
|
5
6
|
export * from './MetadataValidator.js';
|
|
6
|
-
export * from './ReflectMetadataProvider.js';
|