@mikro-orm/core 7.0.0-dev.22 → 7.0.0-dev.221

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.
Files changed (209) hide show
  1. package/EntityManager.d.ts +101 -59
  2. package/EntityManager.js +302 -276
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -143
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +1 -1
  7. package/cache/FileCacheAdapter.js +8 -7
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +0 -1
  11. package/cache/index.js +0 -1
  12. package/connections/Connection.d.ts +16 -7
  13. package/connections/Connection.js +23 -14
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +80 -35
  16. package/drivers/IDatabaseDriver.d.ts +46 -19
  17. package/entity/BaseEntity.d.ts +61 -2
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +94 -29
  20. package/entity/Collection.js +434 -97
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +26 -18
  23. package/entity/EntityFactory.d.ts +13 -1
  24. package/entity/EntityFactory.js +84 -53
  25. package/entity/EntityHelper.d.ts +2 -2
  26. package/entity/EntityHelper.js +40 -15
  27. package/entity/EntityLoader.d.ts +6 -6
  28. package/entity/EntityLoader.js +119 -82
  29. package/entity/EntityRepository.d.ts +27 -7
  30. package/entity/EntityRepository.js +8 -2
  31. package/entity/Reference.d.ts +6 -5
  32. package/entity/Reference.js +34 -9
  33. package/entity/WrappedEntity.d.ts +0 -5
  34. package/entity/WrappedEntity.js +3 -8
  35. package/entity/defineEntity.d.ts +595 -0
  36. package/entity/defineEntity.js +533 -0
  37. package/entity/index.d.ts +3 -2
  38. package/entity/index.js +3 -2
  39. package/entity/utils.d.ts +7 -0
  40. package/entity/utils.js +16 -4
  41. package/entity/validators.d.ts +11 -0
  42. package/entity/validators.js +65 -0
  43. package/enums.d.ts +22 -7
  44. package/enums.js +15 -1
  45. package/errors.d.ts +22 -9
  46. package/errors.js +56 -21
  47. package/events/EventManager.d.ts +2 -1
  48. package/events/EventManager.js +19 -11
  49. package/hydration/Hydrator.js +1 -2
  50. package/hydration/ObjectHydrator.d.ts +4 -4
  51. package/hydration/ObjectHydrator.js +52 -33
  52. package/index.d.ts +2 -2
  53. package/index.js +1 -2
  54. package/logging/DefaultLogger.d.ts +1 -1
  55. package/logging/DefaultLogger.js +1 -0
  56. package/logging/SimpleLogger.d.ts +1 -1
  57. package/logging/colors.d.ts +1 -1
  58. package/logging/colors.js +7 -6
  59. package/logging/index.d.ts +1 -0
  60. package/logging/index.js +1 -0
  61. package/logging/inspect.d.ts +2 -0
  62. package/logging/inspect.js +11 -0
  63. package/metadata/EntitySchema.d.ts +40 -23
  64. package/metadata/EntitySchema.js +81 -34
  65. package/metadata/MetadataDiscovery.d.ts +7 -10
  66. package/metadata/MetadataDiscovery.js +391 -331
  67. package/metadata/MetadataProvider.d.ts +11 -2
  68. package/metadata/MetadataProvider.js +46 -2
  69. package/metadata/MetadataStorage.d.ts +13 -11
  70. package/metadata/MetadataStorage.js +70 -37
  71. package/metadata/MetadataValidator.d.ts +17 -9
  72. package/metadata/MetadataValidator.js +94 -40
  73. package/metadata/discover-entities.d.ts +5 -0
  74. package/metadata/discover-entities.js +40 -0
  75. package/metadata/index.d.ts +1 -1
  76. package/metadata/index.js +1 -1
  77. package/metadata/types.d.ts +498 -0
  78. package/metadata/types.js +1 -0
  79. package/naming-strategy/AbstractNamingStrategy.d.ts +12 -4
  80. package/naming-strategy/AbstractNamingStrategy.js +14 -2
  81. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  82. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  83. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  84. package/naming-strategy/MongoNamingStrategy.js +6 -6
  85. package/naming-strategy/NamingStrategy.d.ts +24 -4
  86. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  87. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  88. package/not-supported.d.ts +2 -0
  89. package/not-supported.js +4 -0
  90. package/package.json +19 -11
  91. package/platforms/ExceptionConverter.js +1 -1
  92. package/platforms/Platform.d.ts +10 -15
  93. package/platforms/Platform.js +20 -43
  94. package/serialization/EntitySerializer.d.ts +5 -0
  95. package/serialization/EntitySerializer.js +47 -27
  96. package/serialization/EntityTransformer.js +28 -18
  97. package/serialization/SerializationContext.d.ts +6 -6
  98. package/serialization/SerializationContext.js +16 -13
  99. package/types/ArrayType.d.ts +1 -1
  100. package/types/ArrayType.js +2 -3
  101. package/types/BigIntType.d.ts +8 -6
  102. package/types/BigIntType.js +1 -1
  103. package/types/BlobType.d.ts +0 -1
  104. package/types/BlobType.js +0 -3
  105. package/types/BooleanType.d.ts +2 -1
  106. package/types/BooleanType.js +3 -0
  107. package/types/DecimalType.d.ts +6 -4
  108. package/types/DecimalType.js +3 -3
  109. package/types/DoubleType.js +2 -2
  110. package/types/EnumArrayType.js +1 -2
  111. package/types/JsonType.d.ts +1 -1
  112. package/types/JsonType.js +7 -2
  113. package/types/TinyIntType.js +1 -1
  114. package/types/Type.d.ts +2 -4
  115. package/types/Type.js +3 -3
  116. package/types/Uint8ArrayType.d.ts +0 -1
  117. package/types/Uint8ArrayType.js +1 -4
  118. package/types/index.d.ts +1 -1
  119. package/typings.d.ts +315 -155
  120. package/typings.js +66 -44
  121. package/unit-of-work/ChangeSet.d.ts +2 -6
  122. package/unit-of-work/ChangeSet.js +4 -5
  123. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  124. package/unit-of-work/ChangeSetComputer.js +26 -13
  125. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  126. package/unit-of-work/ChangeSetPersister.js +70 -34
  127. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  128. package/unit-of-work/CommitOrderCalculator.js +13 -13
  129. package/unit-of-work/IdentityMap.d.ts +12 -0
  130. package/unit-of-work/IdentityMap.js +39 -1
  131. package/unit-of-work/UnitOfWork.d.ts +23 -3
  132. package/unit-of-work/UnitOfWork.js +175 -98
  133. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  134. package/utils/AbstractSchemaGenerator.js +18 -16
  135. package/utils/AsyncContext.d.ts +6 -0
  136. package/utils/AsyncContext.js +42 -0
  137. package/utils/Configuration.d.ts +791 -207
  138. package/utils/Configuration.js +147 -190
  139. package/utils/ConfigurationLoader.d.ts +1 -54
  140. package/utils/ConfigurationLoader.js +1 -352
  141. package/utils/Cursor.d.ts +0 -3
  142. package/utils/Cursor.js +27 -11
  143. package/utils/DataloaderUtils.d.ts +15 -5
  144. package/utils/DataloaderUtils.js +64 -30
  145. package/utils/EntityComparator.d.ts +13 -9
  146. package/utils/EntityComparator.js +101 -42
  147. package/utils/QueryHelper.d.ts +14 -6
  148. package/utils/QueryHelper.js +87 -25
  149. package/utils/RawQueryFragment.d.ts +60 -32
  150. package/utils/RawQueryFragment.js +68 -70
  151. package/utils/RequestContext.js +2 -2
  152. package/utils/TransactionContext.js +2 -2
  153. package/utils/TransactionManager.d.ts +65 -0
  154. package/utils/TransactionManager.js +223 -0
  155. package/utils/Utils.d.ts +13 -126
  156. package/utils/Utils.js +100 -391
  157. package/utils/clone.js +8 -23
  158. package/utils/env-vars.d.ts +7 -0
  159. package/utils/env-vars.js +97 -0
  160. package/utils/fs-utils.d.ts +33 -0
  161. package/utils/fs-utils.js +192 -0
  162. package/utils/index.d.ts +2 -1
  163. package/utils/index.js +2 -1
  164. package/utils/upsert-utils.d.ts +9 -4
  165. package/utils/upsert-utils.js +55 -4
  166. package/decorators/Check.d.ts +0 -3
  167. package/decorators/Check.js +0 -13
  168. package/decorators/CreateRequestContext.d.ts +0 -3
  169. package/decorators/CreateRequestContext.js +0 -32
  170. package/decorators/Embeddable.d.ts +0 -8
  171. package/decorators/Embeddable.js +0 -11
  172. package/decorators/Embedded.d.ts +0 -12
  173. package/decorators/Embedded.js +0 -18
  174. package/decorators/Entity.d.ts +0 -18
  175. package/decorators/Entity.js +0 -12
  176. package/decorators/Enum.d.ts +0 -9
  177. package/decorators/Enum.js +0 -16
  178. package/decorators/Filter.d.ts +0 -2
  179. package/decorators/Filter.js +0 -8
  180. package/decorators/Formula.d.ts +0 -4
  181. package/decorators/Formula.js +0 -15
  182. package/decorators/Indexed.d.ts +0 -19
  183. package/decorators/Indexed.js +0 -20
  184. package/decorators/ManyToMany.d.ts +0 -40
  185. package/decorators/ManyToMany.js +0 -14
  186. package/decorators/ManyToOne.d.ts +0 -32
  187. package/decorators/ManyToOne.js +0 -14
  188. package/decorators/OneToMany.d.ts +0 -28
  189. package/decorators/OneToMany.js +0 -17
  190. package/decorators/OneToOne.d.ts +0 -26
  191. package/decorators/OneToOne.js +0 -7
  192. package/decorators/PrimaryKey.d.ts +0 -8
  193. package/decorators/PrimaryKey.js +0 -20
  194. package/decorators/Property.d.ts +0 -250
  195. package/decorators/Property.js +0 -32
  196. package/decorators/Transactional.d.ts +0 -13
  197. package/decorators/Transactional.js +0 -28
  198. package/decorators/hooks.d.ts +0 -16
  199. package/decorators/hooks.js +0 -47
  200. package/decorators/index.d.ts +0 -17
  201. package/decorators/index.js +0 -17
  202. package/entity/ArrayCollection.d.ts +0 -116
  203. package/entity/ArrayCollection.js +0 -402
  204. package/entity/EntityValidator.d.ts +0 -19
  205. package/entity/EntityValidator.js +0 -150
  206. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  207. package/metadata/ReflectMetadataProvider.js +0 -44
  208. package/utils/resolveContextProvider.d.ts +0 -10
  209. 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 abstract class MetadataProvider {
11
+ export declare class MetadataProvider {
8
12
  protected readonly config: IConfiguration;
9
13
  constructor(config: IConfiguration);
10
- abstract loadEntityMetadata(meta: EntityMetadata, name: string): void;
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 3 */
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 ?? false;
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 { EntityMetadata, type Dictionary, type EntityData, type EntityName } from '../typings.js';
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(): Dictionary<EntityMetadata>;
17
- getByDiscriminatorColumn<T>(meta: EntityMetadata<T>, data: EntityData<T>): EntityMetadata<T> | undefined;
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(entity: string): boolean;
21
- set(entity: string, meta: EntityMetadata): EntityMetadata;
22
- reset(entity: string): void;
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 = Utils.getGlobalStorage('metadata');
8
- metadata;
13
+ static metadata = getGlobalStorage('metadata');
14
+ metadata = new Map();
15
+ idMap;
16
+ classNameMap;
17
+ uniqueNameMap;
9
18
  constructor(metadata = {}) {
10
- this.metadata = Utils.copy(metadata, false);
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
- getByDiscriminatorColumn(meta, data) {
41
- const value = data[meta.root.discriminatorColumn];
42
- if (!value) {
43
- return undefined;
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
- if (init && !this.has(entityName)) {
54
- this.metadata[entityName] = new EntityMetadata();
52
+ const className = Utils.className(entityName);
53
+ if (!init) {
54
+ throw MetadataError.missingMetadata(className);
55
55
  }
56
- return this.metadata[entityName];
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
- entityName = Utils.className(entityName);
63
- return this.metadata[entityName];
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(entity) {
66
- return entity in this.metadata;
73
+ has(entityName) {
74
+ return this.metadata.has(entityName);
67
75
  }
68
- set(entity, meta) {
69
- return this.metadata[entity] = meta;
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(entity) {
72
- delete this.metadata[entity];
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
- Object.values(this.metadata)
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 Object.values(this.metadata)) {
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,33 @@
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.virtual || meta.expression) {
22
+ // View entities (expression with view flag) behave like regular tables but are read-only
23
+ // They can have primary keys and are created as actual database views
24
+ if (meta.view) {
25
+ this.validateViewEntity(meta);
26
+ return;
27
+ }
28
+ // Virtual entities (expression without view flag) have restrictions - no PKs, limited relation types
29
+ // Note: meta.virtual is set later in sync(), so we check for expression && !view here
30
+ if (meta.virtual || (meta.expression && !meta.view)) {
21
31
  for (const prop of Utils.values(meta.properties)) {
22
32
  if (![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED, ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
23
33
  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 +46,14 @@ export class MetadataValidator {
36
46
  this.validateDuplicateFieldNames(meta, options);
37
47
  this.validateIndexes(meta, meta.indexes ?? [], 'index');
38
48
  this.validateIndexes(meta, meta.uniques ?? [], 'unique');
49
+ this.validatePropertyNames(meta);
39
50
  for (const prop of Utils.values(meta.properties)) {
40
51
  if (prop.kind !== ReferenceKind.SCALAR) {
41
- this.validateReference(meta, prop, metadata, options);
42
- this.validateBidirectional(meta, prop, metadata);
52
+ this.validateReference(meta, prop, options);
53
+ this.validateBidirectional(meta, prop);
43
54
  }
44
- else if (metadata.has(prop.type)) {
45
- throw MetadataError.propertyTargetsEntityType(meta, prop, metadata.get(prop.type));
55
+ else if (metadata.getByClassName(prop.type, false)) {
56
+ throw MetadataError.propertyTargetsEntityType(meta, prop, metadata.getByClassName(prop.type));
46
57
  }
47
58
  }
48
59
  }
@@ -50,17 +61,13 @@ export class MetadataValidator {
50
61
  if (discovered.length === 0 && options.warnWhenNoEntities) {
51
62
  throw MetadataError.noEntityDiscovered();
52
63
  }
53
- const duplicates = Utils.findDuplicates(discovered.map(meta => meta.className));
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 !== '*');
64
+ const tableNames = discovered.filter(meta => !meta.abstract && !meta.embeddable && meta === meta.root && (meta.tableName || meta.collection) && meta.schema !== '*');
58
65
  const duplicateTableNames = Utils.findDuplicates(tableNames.map(meta => {
59
66
  const tableName = meta.tableName || meta.collection;
60
67
  return (meta.schema ? '.' + meta.schema : '') + tableName;
61
68
  }));
62
- if (duplicateTableNames.length > 0 && options.checkDuplicateTableNames && options.checkDuplicateEntities) {
63
- throw MetadataError.duplicateEntityDiscovered(duplicateTableNames, 'table names');
69
+ if (duplicateTableNames.length > 0 && options.checkDuplicateTableNames) {
70
+ throw MetadataError.duplicateEntityDiscovered(duplicateTableNames);
64
71
  }
65
72
  // validate we found at least one entity (not just abstract/base entities)
66
73
  if (discovered.filter(meta => meta.name).length === 0 && options.warnWhenNoEntities) {
@@ -71,7 +78,7 @@ export class MetadataValidator {
71
78
  .replace(/\[]$/, '') // remove array suffix
72
79
  .replace(/\((.*)\)/, '$1'); // unwrap union types
73
80
  const name = (p) => {
74
- if (typeof p === 'function') {
81
+ if (typeof p === 'function' && !p.prototype) {
75
82
  return Utils.className(p());
76
83
  }
77
84
  return Utils.className(p);
@@ -95,12 +102,12 @@ export class MetadataValidator {
95
102
  }
96
103
  });
97
104
  }
98
- validateReference(meta, prop, metadata, options) {
105
+ validateReference(meta, prop, options) {
99
106
  // references do have types
100
107
  if (!prop.type) {
101
108
  throw MetadataError.fromWrongTypeDefinition(meta, prop);
102
109
  }
103
- const targetMeta = metadata.find(prop.type);
110
+ const targetMeta = prop.targetMeta;
104
111
  // references do have type of known entity
105
112
  if (!targetMeta) {
106
113
  throw MetadataError.fromWrongTypeDefinition(meta, prop);
@@ -111,31 +118,47 @@ export class MetadataValidator {
111
118
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.persist === false && targetMeta.compositePK && options.checkNonPersistentCompositeProps) {
112
119
  throw MetadataError.nonPersistentCompositeProp(meta, prop);
113
120
  }
121
+ this.validateTargetKey(meta, prop, targetMeta);
114
122
  }
115
- validateBidirectional(meta, prop, metadata) {
123
+ validateTargetKey(meta, prop, targetMeta) {
124
+ if (!prop.targetKey) {
125
+ return;
126
+ }
127
+ // targetKey is not supported for ManyToMany relations
128
+ if (prop.kind === ReferenceKind.MANY_TO_MANY) {
129
+ throw MetadataError.targetKeyOnManyToMany(meta, prop);
130
+ }
131
+ // targetKey must point to an existing property
132
+ const targetProp = targetMeta.properties[prop.targetKey];
133
+ if (!targetProp) {
134
+ throw MetadataError.targetKeyNotFound(meta, prop);
135
+ }
136
+ // targetKey must point to a unique property
137
+ if (!targetProp.unique && !targetMeta.uniques?.some(u => u.properties?.includes(prop.targetKey))) {
138
+ throw MetadataError.targetKeyNotUnique(meta, prop);
139
+ }
140
+ }
141
+ validateBidirectional(meta, prop) {
116
142
  if (prop.inversedBy) {
117
- const inverse = metadata.get(prop.type).properties[prop.inversedBy];
118
- this.validateOwningSide(meta, prop, inverse, metadata);
143
+ this.validateOwningSide(meta, prop);
119
144
  }
120
145
  else if (prop.mappedBy) {
121
- const inverse = metadata.get(prop.type).properties[prop.mappedBy];
122
- this.validateInverseSide(meta, prop, inverse, metadata);
146
+ this.validateInverseSide(meta, prop);
123
147
  }
124
- else {
148
+ else if (prop.kind === ReferenceKind.ONE_TO_MANY && !prop.mappedBy) {
125
149
  // 1:m property has `mappedBy`
126
- if (prop.kind === ReferenceKind.ONE_TO_MANY && !prop.mappedBy) {
127
- throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
128
- }
150
+ throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
129
151
  }
130
152
  }
131
- validateOwningSide(meta, prop, inverse, metadata) {
153
+ validateOwningSide(meta, prop) {
154
+ const inverse = prop.targetMeta.properties[prop.inversedBy];
132
155
  // has correct `inversedBy` on owning side
133
156
  if (!inverse) {
134
157
  throw MetadataError.fromWrongReference(meta, prop, 'inversedBy');
135
158
  }
136
- const targetClassName = metadata.find(inverse.type)?.root.className;
159
+ const targetClass = inverse.targetMeta?.root.class;
137
160
  // has correct `inversedBy` reference type
138
- if (inverse.type !== meta.className && targetClassName !== meta.root.className) {
161
+ if (inverse.type !== meta.className && targetClass !== meta.root.class) {
139
162
  throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
140
163
  }
141
164
  // inverse side is not defined as owner
@@ -143,13 +166,14 @@ export class MetadataValidator {
143
166
  throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
144
167
  }
145
168
  }
146
- validateInverseSide(meta, prop, owner, metadata) {
169
+ validateInverseSide(meta, prop) {
170
+ const owner = prop.targetMeta.properties[prop.mappedBy];
147
171
  // has correct `mappedBy` on inverse side
148
172
  if (prop.mappedBy && !owner) {
149
173
  throw MetadataError.fromWrongReference(meta, prop, 'mappedBy');
150
174
  }
151
175
  // has correct `mappedBy` reference type
152
- if (owner.type !== meta.className && metadata.find(owner.type)?.root.className !== meta.root.className) {
176
+ if (owner.type !== meta.className && owner.targetMeta?.root.class !== meta.root.class) {
153
177
  throw MetadataError.fromWrongReference(meta, prop, 'mappedBy', owner);
154
178
  }
155
179
  // owning side is not defined as inverse
@@ -192,7 +216,7 @@ export class MetadataValidator {
192
216
  return [prop.embedded ? prop.embedded.join('.') : prop.name, prop.fieldNames[0]];
193
217
  });
194
218
  });
195
- throw MetadataError.duplicateFieldName(meta.className, pairs);
219
+ throw MetadataError.duplicateFieldName(meta.class, pairs);
196
220
  }
197
221
  }
198
222
  validateVersionField(meta) {
@@ -209,4 +233,34 @@ export class MetadataValidator {
209
233
  throw MetadataError.invalidVersionFieldType(meta);
210
234
  }
211
235
  }
236
+ /**
237
+ * Validates that entity properties do not use dangerous names that could lead to
238
+ * prototype pollution vulnerabilities. This validation ensures that property names
239
+ * cannot be exploited to modify object prototypes when values are assigned during
240
+ * entity hydration or persistence operations.
241
+ *
242
+ * @internal
243
+ */
244
+ validatePropertyNames(meta) {
245
+ for (const prop of Utils.values(meta.properties)) {
246
+ if (DANGEROUS_PROPERTY_NAMES.includes(prop.name)) {
247
+ throw MetadataError.dangerousPropertyName(meta, prop);
248
+ }
249
+ }
250
+ }
251
+ /**
252
+ * Validates view entity configuration.
253
+ * View entities must have an expression.
254
+ */
255
+ validateViewEntity(meta) {
256
+ // View entities must have an expression
257
+ if (!meta.expression) {
258
+ throw MetadataError.viewEntityWithoutExpression(meta);
259
+ }
260
+ // Validate indexes if present
261
+ this.validateIndexes(meta, meta.indexes ?? [], 'index');
262
+ this.validateIndexes(meta, meta.uniques ?? [], 'unique');
263
+ // Validate property names
264
+ this.validatePropertyNames(meta);
265
+ }
212
266
  }
@@ -0,0 +1,5 @@
1
+ import { type Constructor } from '../typings.js';
2
+ import { EntitySchema } from './EntitySchema.js';
3
+ export declare function discoverEntities(paths: string | string[], options?: {
4
+ baseDir?: string;
5
+ }): Promise<Iterable<EntitySchema | Constructor>>;
@@ -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
+ }
@@ -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';