@mikro-orm/core 7.0.4 → 7.0.5-dev.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.
Files changed (206) hide show
  1. package/EntityManager.d.ts +583 -884
  2. package/EntityManager.js +1895 -1922
  3. package/MikroORM.d.ts +74 -103
  4. package/MikroORM.js +179 -178
  5. package/README.md +1 -1
  6. package/cache/CacheAdapter.d.ts +36 -36
  7. package/cache/FileCacheAdapter.d.ts +24 -30
  8. package/cache/FileCacheAdapter.js +78 -80
  9. package/cache/GeneratedCacheAdapter.d.ts +20 -18
  10. package/cache/GeneratedCacheAdapter.js +30 -30
  11. package/cache/MemoryCacheAdapter.d.ts +20 -18
  12. package/cache/MemoryCacheAdapter.js +36 -35
  13. package/cache/NullCacheAdapter.d.ts +16 -16
  14. package/cache/NullCacheAdapter.js +24 -24
  15. package/connections/Connection.d.ts +84 -95
  16. package/connections/Connection.js +168 -165
  17. package/drivers/DatabaseDriver.d.ts +80 -186
  18. package/drivers/DatabaseDriver.js +443 -450
  19. package/drivers/IDatabaseDriver.d.ts +301 -440
  20. package/entity/BaseEntity.d.ts +83 -120
  21. package/entity/BaseEntity.js +43 -43
  22. package/entity/Collection.d.ts +179 -212
  23. package/entity/Collection.js +721 -727
  24. package/entity/EntityAssigner.d.ts +77 -88
  25. package/entity/EntityAssigner.js +230 -231
  26. package/entity/EntityFactory.d.ts +54 -66
  27. package/entity/EntityFactory.js +383 -425
  28. package/entity/EntityHelper.d.ts +22 -34
  29. package/entity/EntityHelper.js +267 -280
  30. package/entity/EntityIdentifier.d.ts +4 -4
  31. package/entity/EntityIdentifier.js +10 -10
  32. package/entity/EntityLoader.d.ts +72 -98
  33. package/entity/EntityLoader.js +723 -753
  34. package/entity/EntityRepository.d.ts +201 -316
  35. package/entity/EntityRepository.js +213 -213
  36. package/entity/PolymorphicRef.d.ts +5 -5
  37. package/entity/PolymorphicRef.js +10 -10
  38. package/entity/Reference.d.ts +82 -126
  39. package/entity/Reference.js +274 -278
  40. package/entity/WrappedEntity.d.ts +72 -115
  41. package/entity/WrappedEntity.js +166 -168
  42. package/entity/defineEntity.d.ts +636 -1315
  43. package/entity/defineEntity.js +518 -527
  44. package/entity/utils.d.ts +3 -13
  45. package/entity/utils.js +73 -71
  46. package/entity/validators.js +43 -43
  47. package/entity/wrap.js +8 -8
  48. package/enums.d.ts +253 -258
  49. package/enums.js +252 -251
  50. package/errors.d.ts +72 -114
  51. package/errors.js +253 -350
  52. package/events/EventManager.d.ts +14 -26
  53. package/events/EventManager.js +77 -79
  54. package/events/EventSubscriber.d.ts +29 -29
  55. package/events/TransactionEventBroadcaster.d.ts +8 -15
  56. package/events/TransactionEventBroadcaster.js +14 -14
  57. package/exceptions.d.ts +40 -23
  58. package/exceptions.js +52 -35
  59. package/hydration/Hydrator.d.ts +17 -42
  60. package/hydration/Hydrator.js +43 -43
  61. package/hydration/ObjectHydrator.d.ts +17 -50
  62. package/hydration/ObjectHydrator.js +416 -481
  63. package/index.d.ts +2 -116
  64. package/index.js +1 -10
  65. package/logging/DefaultLogger.d.ts +32 -34
  66. package/logging/DefaultLogger.js +86 -86
  67. package/logging/Logger.d.ts +41 -41
  68. package/logging/SimpleLogger.d.ts +11 -13
  69. package/logging/SimpleLogger.js +22 -22
  70. package/logging/colors.d.ts +6 -6
  71. package/logging/colors.js +10 -11
  72. package/logging/inspect.js +7 -7
  73. package/metadata/EntitySchema.d.ts +127 -211
  74. package/metadata/EntitySchema.js +398 -397
  75. package/metadata/MetadataDiscovery.d.ts +114 -114
  76. package/metadata/MetadataDiscovery.js +1870 -1951
  77. package/metadata/MetadataProvider.d.ts +21 -24
  78. package/metadata/MetadataProvider.js +84 -82
  79. package/metadata/MetadataStorage.d.ts +32 -38
  80. package/metadata/MetadataStorage.js +118 -118
  81. package/metadata/MetadataValidator.d.ts +39 -39
  82. package/metadata/MetadataValidator.js +338 -381
  83. package/metadata/discover-entities.d.ts +2 -5
  84. package/metadata/discover-entities.js +37 -35
  85. package/metadata/types.d.ts +531 -615
  86. package/naming-strategy/AbstractNamingStrategy.d.ts +39 -54
  87. package/naming-strategy/AbstractNamingStrategy.js +85 -90
  88. package/naming-strategy/EntityCaseNamingStrategy.d.ts +6 -6
  89. package/naming-strategy/EntityCaseNamingStrategy.js +22 -22
  90. package/naming-strategy/MongoNamingStrategy.d.ts +6 -6
  91. package/naming-strategy/MongoNamingStrategy.js +18 -18
  92. package/naming-strategy/NamingStrategy.d.ts +99 -109
  93. package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
  94. package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
  95. package/not-supported.js +4 -7
  96. package/package.json +1 -1
  97. package/platforms/ExceptionConverter.d.ts +1 -1
  98. package/platforms/ExceptionConverter.js +4 -4
  99. package/platforms/Platform.d.ts +301 -310
  100. package/platforms/Platform.js +640 -663
  101. package/serialization/EntitySerializer.d.ts +26 -49
  102. package/serialization/EntitySerializer.js +218 -224
  103. package/serialization/EntityTransformer.d.ts +6 -10
  104. package/serialization/EntityTransformer.js +217 -219
  105. package/serialization/SerializationContext.d.ts +23 -27
  106. package/serialization/SerializationContext.js +105 -105
  107. package/types/ArrayType.d.ts +8 -8
  108. package/types/ArrayType.js +33 -33
  109. package/types/BigIntType.d.ts +10 -17
  110. package/types/BigIntType.js +37 -37
  111. package/types/BlobType.d.ts +3 -3
  112. package/types/BlobType.js +13 -13
  113. package/types/BooleanType.d.ts +4 -4
  114. package/types/BooleanType.js +12 -12
  115. package/types/CharacterType.d.ts +2 -2
  116. package/types/CharacterType.js +6 -6
  117. package/types/DateTimeType.d.ts +5 -5
  118. package/types/DateTimeType.js +15 -15
  119. package/types/DateType.d.ts +5 -5
  120. package/types/DateType.js +15 -15
  121. package/types/DecimalType.d.ts +7 -7
  122. package/types/DecimalType.js +26 -26
  123. package/types/DoubleType.d.ts +3 -3
  124. package/types/DoubleType.js +12 -12
  125. package/types/EnumArrayType.d.ts +5 -5
  126. package/types/EnumArrayType.js +24 -24
  127. package/types/EnumType.d.ts +3 -3
  128. package/types/EnumType.js +11 -11
  129. package/types/FloatType.d.ts +3 -3
  130. package/types/FloatType.js +9 -9
  131. package/types/IntegerType.d.ts +3 -3
  132. package/types/IntegerType.js +9 -9
  133. package/types/IntervalType.d.ts +4 -4
  134. package/types/IntervalType.js +12 -12
  135. package/types/JsonType.d.ts +8 -8
  136. package/types/JsonType.js +32 -32
  137. package/types/MediumIntType.d.ts +1 -1
  138. package/types/MediumIntType.js +3 -3
  139. package/types/SmallIntType.d.ts +3 -3
  140. package/types/SmallIntType.js +9 -9
  141. package/types/StringType.d.ts +4 -4
  142. package/types/StringType.js +12 -12
  143. package/types/TextType.d.ts +3 -3
  144. package/types/TextType.js +9 -9
  145. package/types/TimeType.d.ts +5 -5
  146. package/types/TimeType.js +17 -17
  147. package/types/TinyIntType.d.ts +3 -3
  148. package/types/TinyIntType.js +10 -10
  149. package/types/Type.d.ts +79 -83
  150. package/types/Type.js +82 -82
  151. package/types/Uint8ArrayType.d.ts +4 -4
  152. package/types/Uint8ArrayType.js +21 -21
  153. package/types/UnknownType.d.ts +4 -4
  154. package/types/UnknownType.js +12 -12
  155. package/types/UuidType.d.ts +5 -5
  156. package/types/UuidType.js +19 -19
  157. package/types/index.d.ts +49 -75
  158. package/types/index.js +26 -52
  159. package/typings.d.ts +737 -1250
  160. package/typings.js +231 -244
  161. package/unit-of-work/ChangeSet.d.ts +26 -26
  162. package/unit-of-work/ChangeSet.js +56 -56
  163. package/unit-of-work/ChangeSetComputer.d.ts +12 -12
  164. package/unit-of-work/ChangeSetComputer.js +170 -178
  165. package/unit-of-work/ChangeSetPersister.d.ts +44 -63
  166. package/unit-of-work/ChangeSetPersister.js +421 -442
  167. package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
  168. package/unit-of-work/CommitOrderCalculator.js +88 -89
  169. package/unit-of-work/IdentityMap.d.ts +31 -31
  170. package/unit-of-work/IdentityMap.js +105 -105
  171. package/unit-of-work/UnitOfWork.d.ts +141 -181
  172. package/unit-of-work/UnitOfWork.js +1183 -1200
  173. package/utils/AbstractMigrator.d.ts +91 -111
  174. package/utils/AbstractMigrator.js +275 -275
  175. package/utils/AbstractSchemaGenerator.d.ts +34 -43
  176. package/utils/AbstractSchemaGenerator.js +122 -121
  177. package/utils/AsyncContext.d.ts +3 -3
  178. package/utils/AsyncContext.js +35 -34
  179. package/utils/Configuration.d.ts +808 -852
  180. package/utils/Configuration.js +344 -359
  181. package/utils/Cursor.d.ts +22 -40
  182. package/utils/Cursor.js +127 -135
  183. package/utils/DataloaderUtils.d.ts +43 -58
  184. package/utils/DataloaderUtils.js +198 -203
  185. package/utils/EntityComparator.d.ts +81 -98
  186. package/utils/EntityComparator.js +732 -828
  187. package/utils/NullHighlighter.d.ts +1 -1
  188. package/utils/NullHighlighter.js +3 -3
  189. package/utils/QueryHelper.d.ts +51 -79
  190. package/utils/QueryHelper.js +361 -372
  191. package/utils/RawQueryFragment.d.ts +34 -50
  192. package/utils/RawQueryFragment.js +105 -107
  193. package/utils/RequestContext.d.ts +32 -32
  194. package/utils/RequestContext.js +53 -52
  195. package/utils/TransactionContext.d.ts +16 -16
  196. package/utils/TransactionContext.js +27 -27
  197. package/utils/TransactionManager.d.ts +58 -58
  198. package/utils/TransactionManager.js +197 -199
  199. package/utils/Utils.d.ts +145 -204
  200. package/utils/Utils.js +812 -812
  201. package/utils/clone.js +113 -104
  202. package/utils/env-vars.js +88 -90
  203. package/utils/fs-utils.d.ts +15 -15
  204. package/utils/fs-utils.js +181 -180
  205. package/utils/upsert-utils.d.ts +5 -20
  206. package/utils/upsert-utils.js +116 -114
@@ -1,4 +1,4 @@
1
- import { EntityMetadata } from '../typings.js';
1
+ import { EntityMetadata, } from '../typings.js';
2
2
  import { BaseEntity } from '../entity/BaseEntity.js';
3
3
  import { Cascade, ReferenceKind } from '../enums.js';
4
4
  import { Type } from '../types/Type.js';
@@ -6,400 +6,401 @@ import { Utils } from '../utils/Utils.js';
6
6
  import { EnumArrayType } from '../types/EnumArrayType.js';
7
7
  /** Class-less entity definition that provides a programmatic API for defining entities without decorators. */
8
8
  export class EntitySchema {
9
- /**
10
- * When schema links the entity class via `class` option, this registry allows the lookup from opposite side,
11
- * so we can use the class in `entities` option just like the EntitySchema instance.
12
- */
13
- static REGISTRY = new Map();
14
- _meta;
15
- internal = false;
16
- initialized = false;
17
- constructor(meta) {
18
- meta.name = meta.class ? meta.class.name : meta.name;
19
- if (meta.name) {
20
- meta.abstract ??= false;
21
- }
22
- this._meta = new EntityMetadata({
23
- className: meta.name,
24
- ...meta,
25
- });
26
- this._meta.root ??= this._meta;
27
- if (meta.class && !meta.internal) {
28
- EntitySchema.REGISTRY.set(meta.class, this);
29
- }
30
- }
31
- /**
32
- * Checks if the given value is an EntitySchema instance, using duck-typing
33
- * as a fallback when `instanceof` fails due to CJS/ESM dual-package hazard
34
- * (e.g. when using `tsx` or `@swc-node/register` with `"type": "commonjs"` projects).
35
- */
36
- static is(item) {
37
- if (item instanceof EntitySchema) {
38
- return true;
39
- }
40
- return item != null && typeof item === 'object' && item.constructor?.name === 'EntitySchema' && 'meta' in item;
41
- }
42
- /** Creates an EntitySchema from existing EntityMetadata (used internally). */
43
- static fromMetadata(meta) {
44
- const schema = new EntitySchema({ ...meta, internal: true });
45
- schema.internal = true;
46
- return schema;
47
- }
48
- /** Adds a scalar property to the entity schema. */
49
- addProperty(name, type, options = {}) {
50
- this.renameCompositeOptions(name, options);
51
- const prop = {
52
- name,
53
- kind: ReferenceKind.SCALAR,
54
- ...options,
55
- ...this.normalizeType(options, type),
56
- };
57
- if (type && Type.isMappedType(type.prototype)) {
58
- prop.type = type;
59
- }
60
- if (typeof prop.formula === 'string') {
61
- const formula = prop.formula;
62
- prop.formula = () => formula;
63
- }
64
- if (prop.formula) {
65
- prop.persist ??= false;
66
- }
67
- this._meta.properties[name] = prop;
68
- }
69
- /** Adds an enum property to the entity schema. */
70
- addEnum(name, type, options = {}) {
71
- if (options.items instanceof Function) {
72
- options.items = Utils.extractEnumValues(options.items());
73
- }
74
- // enum arrays are simple numeric/string arrays, the constraint is enforced in the custom type only
75
- if (options.array && !options.type) {
76
- options.type = new EnumArrayType(`${this._meta.className}.${name}`, options.items);
77
- options.enum = false;
78
- }
79
- const prop = { enum: true, ...options };
80
- if (prop.array) {
81
- prop.enum = false;
82
- }
83
- // force string labels on native enums
84
- if (prop.nativeEnumName && Array.isArray(prop.items)) {
85
- prop.items = prop.items.map(val => '' + val);
86
- }
87
- this.addProperty(name, this.internal ? type : type || 'enum', prop);
88
- }
89
- /** Adds a version property for optimistic locking. */
90
- addVersion(name, type, options = {}) {
91
- this.addProperty(name, type, { version: true, ...options });
92
- }
93
- /** Adds a primary key property to the entity schema. */
94
- addPrimaryKey(name, type, options = {}) {
95
- this.addProperty(name, type, { primary: true, ...options });
96
- }
97
- /** Adds a serialized primary key property (e.g. for MongoDB ObjectId). */
98
- addSerializedPrimaryKey(name, type, options = {}) {
99
- this._meta.serializedPrimaryKey = name;
100
- this.addProperty(name, type, { serializedPrimaryKey: true, ...options });
101
- }
102
- /** Adds an embedded property to the entity schema. */
103
- addEmbedded(name, options) {
104
- this.renameCompositeOptions(name, options);
105
- Utils.defaultValue(options, 'prefix', true);
106
- if (options.array) {
107
- options.object = true; // force object mode for arrays
108
- }
109
- this._meta.properties[name] = {
110
- name,
111
- kind: ReferenceKind.EMBEDDED,
112
- ...this.normalizeType(options),
113
- ...options,
114
- };
115
- }
116
- /** Adds a many-to-one relation to the entity schema. */
117
- addManyToOne(name, type, options) {
118
- const prop = this.createProperty(ReferenceKind.MANY_TO_ONE, options);
119
- prop.owner = true;
120
- if (prop.joinColumns && !prop.fieldNames) {
121
- prop.fieldNames = prop.joinColumns;
122
- }
123
- if (prop.fieldNames && !prop.joinColumns) {
124
- prop.joinColumns = prop.fieldNames;
125
- }
126
- // By default, the foreign key constraint is created on the relation
127
- Utils.defaultValue(prop, 'createForeignKeyConstraint', true);
128
- this.addProperty(name, type, prop);
129
- }
130
- /** Adds a many-to-many relation to the entity schema. */
131
- addManyToMany(name, type, options) {
132
- options.fixedOrder = options.fixedOrder || !!options.fixedOrderColumn;
133
- if (!options.owner && !options.mappedBy) {
134
- options.owner = true;
135
- }
136
- if (options.owner) {
137
- Utils.renameKey(options, 'mappedBy', 'inversedBy');
138
- // By default, the foreign key constraint is created on the relation
139
- Utils.defaultValue(options, 'createForeignKeyConstraint', true);
140
- }
141
- const prop = this.createProperty(ReferenceKind.MANY_TO_MANY, options);
142
- this.addProperty(name, type, prop);
143
- }
144
- /** Adds a one-to-many relation to the entity schema. */
145
- addOneToMany(name, type, options) {
146
- const prop = this.createProperty(ReferenceKind.ONE_TO_MANY, options);
147
- this.addProperty(name, type, prop);
148
- }
149
- /** Adds a one-to-one relation to the entity schema. */
150
- addOneToOne(name, type, options) {
151
- const prop = this.createProperty(ReferenceKind.ONE_TO_ONE, options);
152
- Utils.defaultValue(prop, 'owner', !!prop.inversedBy || !prop.mappedBy);
153
- Utils.defaultValue(prop, 'unique', prop.owner);
154
- if (prop.owner) {
155
- if (options.mappedBy) {
156
- Utils.renameKey(prop, 'mappedBy', 'inversedBy');
157
- }
158
- // By default, the foreign key constraint is created on the relation
159
- Utils.defaultValue(prop, 'createForeignKeyConstraint', true);
160
- }
161
- if (prop.joinColumns && !prop.fieldNames) {
162
- prop.fieldNames = prop.joinColumns;
163
- }
164
- if (prop.fieldNames && !prop.joinColumns) {
165
- prop.joinColumns = prop.fieldNames;
166
- }
167
- this.addProperty(name, type, prop);
168
- }
169
- /** Adds an index definition to the entity schema. */
170
- addIndex(options) {
171
- this._meta.indexes.push(options);
172
- }
173
- /** Adds a unique constraint definition to the entity schema. */
174
- addUnique(options) {
175
- this._meta.uniques.push(options);
176
- }
177
- /** Sets a custom repository class for this entity. */
178
- setCustomRepository(repository) {
179
- this._meta.repository = repository;
180
- }
181
- /** Sets the base entity that this schema extends. */
182
- setExtends(base) {
183
- this._meta.extends = base;
184
- }
185
- /** Sets or replaces the entity class associated with this schema. */
186
- setClass(cls) {
187
- const oldClass = this._meta.class;
188
- const sameClass = this._meta.class === cls;
189
- this._meta.class = cls;
190
- this._meta.prototype = cls.prototype;
191
- this._meta.className = this._meta.name ?? cls.name;
192
- if (!sameClass || !this._meta.constructorParams) {
193
- this._meta.constructorParams = Utils.getConstructorParams(cls);
194
- }
195
- if (!this.internal) {
196
- // Remove old class from registry if it's being replaced with a different class
197
- if (oldClass && oldClass !== cls && EntitySchema.REGISTRY.get(oldClass) === this) {
198
- EntitySchema.REGISTRY.delete(oldClass);
199
- }
200
- EntitySchema.REGISTRY.set(cls, this);
201
- }
202
- const base = Object.getPrototypeOf(cls);
203
- // Only set extends if the parent is NOT the auto-generated class for this same entity.
204
- // When the user extends the auto-generated class (from defineEntity without a class option)
205
- // and registers their custom class via setClass, we don't want to discover the
206
- // auto-generated class as a separate parent entity.
207
- if (base !== BaseEntity && base.name !== this._meta.className) {
208
- this._meta.extends ??= base.name ? base : undefined;
209
- }
210
- }
211
- /** Returns the underlying EntityMetadata. */
212
- get meta() {
213
- return this._meta;
214
- }
215
- /** Returns the entity class name. */
216
- get name() {
217
- return this._meta.className;
218
- }
219
- /** Returns the database table name. */
220
- get tableName() {
221
- return this._meta.tableName;
222
- }
223
- get class() {
224
- return this._meta.class;
225
- }
226
- get properties() {
227
- return this._meta.properties;
228
- }
229
- new(...params) {
230
- return new this._meta.class(...params);
231
- }
232
- /**
233
- * @internal
234
- */
235
- init() {
236
- if (this.initialized) {
237
- return this;
238
- }
239
- this.setClass(this._meta.class);
240
- // Abstract TPT entities keep their name because they have their own table
241
- const isTPT = this._meta.inheritance === 'tpt' || this.isPartOfTPTHierarchy();
242
- if (this._meta.abstract && !this._meta.discriminatorColumn && !isTPT) {
243
- delete this._meta.name;
244
- }
245
- const tableName = this._meta.collection ?? this._meta.tableName;
246
- if (tableName?.includes('.') && !this._meta.schema) {
247
- this._meta.schema = tableName.substring(0, tableName.indexOf('.'));
248
- this._meta.tableName = tableName.substring(tableName.indexOf('.') + 1);
249
- }
250
- this.initProperties();
251
- this.initPrimaryKeys();
252
- this._meta.props = Object.values(this._meta.properties);
253
- this._meta.relations = this._meta.props.filter(
254
- prop =>
255
- typeof prop.kind !== 'undefined' && prop.kind !== ReferenceKind.SCALAR && prop.kind !== ReferenceKind.EMBEDDED,
256
- );
257
- this.initialized = true;
258
- return this;
259
- }
260
- /**
261
- * Check if this entity is part of a TPT hierarchy by walking up the extends chain.
262
- * This handles mid-level abstract entities (e.g., Animal -> Mammal -> Dog where Mammal is abstract).
263
- */
264
- isPartOfTPTHierarchy() {
265
- let parent = this._meta.extends;
266
- while (parent) {
267
- const parentSchema = EntitySchema.is(parent) ? parent : EntitySchema.REGISTRY.get(parent);
268
- if (!parentSchema) {
269
- break;
270
- }
271
- if (parentSchema._meta.inheritance === 'tpt') {
272
- return true;
273
- }
274
- parent = parentSchema._meta.extends;
275
- }
276
- return false;
277
- }
278
- initProperties() {
279
- Utils.entries(this._meta.properties).forEach(([name, options]) => {
280
- if (Type.isMappedType(options.type)) {
281
- options.type ??= options.type.constructor.name;
282
- }
283
- switch (options.kind) {
284
- case ReferenceKind.ONE_TO_ONE:
285
- this.addOneToOne(name, options.type, options);
286
- break;
287
- case ReferenceKind.ONE_TO_MANY:
288
- this.addOneToMany(name, options.type, options);
289
- break;
290
- case ReferenceKind.MANY_TO_ONE:
291
- this.addManyToOne(name, options.type, options);
292
- break;
293
- case ReferenceKind.MANY_TO_MANY:
294
- this.addManyToMany(name, options.type, options);
295
- break;
296
- case ReferenceKind.EMBEDDED:
297
- this.addEmbedded(name, options);
298
- break;
299
- default:
300
- if (options.enum) {
301
- this.addEnum(name, options.type, options);
302
- } else if (options.primary) {
303
- this.addPrimaryKey(name, options.type, options);
304
- } else if (options.serializedPrimaryKey) {
305
- this.addSerializedPrimaryKey(name, options.type, options);
306
- } else if (options.version) {
307
- this.addVersion(name, options.type, options);
308
- } else {
309
- this.addProperty(name, options.type, options);
310
- }
311
- }
312
- });
313
- }
314
- initPrimaryKeys() {
315
- const pks = Object.values(this._meta.properties).filter(prop => prop.primary);
316
- if (pks.length > 0) {
317
- this._meta.primaryKeys = pks.map(prop => prop.name);
318
- this._meta.compositePK = pks.length > 1;
319
- this._meta.simplePK = !this._meta.compositePK && pks[0].kind === ReferenceKind.SCALAR && !pks[0].customType;
320
- }
321
- if (pks.length === 1 && ['number', 'bigint'].includes(pks[0].type)) {
322
- pks[0].autoincrement ??= true;
323
- }
324
- const serializedPrimaryKey = Object.values(this._meta.properties).find(prop => prop.serializedPrimaryKey);
325
- if (serializedPrimaryKey) {
326
- this._meta.serializedPrimaryKey = serializedPrimaryKey.name;
327
- }
328
- }
329
- normalizeType(options, type) {
330
- if ('entity' in options) {
331
- /* v8 ignore next */
332
- if (typeof options.entity === 'string') {
333
- throw new Error(
334
- `Relation target needs to be an entity class or EntitySchema instance, string '${options.entity}' given instead for ${this._meta.className}.${options.name}.`,
335
- );
336
- } else if (options.entity) {
337
- const tmp = options.entity();
338
- type = options.type = Array.isArray(tmp)
339
- ? tmp
340
- .map(t => Utils.className(t))
341
- .sort()
342
- .join(' | ')
343
- : Utils.className(tmp);
344
- const target = EntitySchema.is(tmp) ? tmp.meta.class : tmp;
345
- return { type, target };
346
- }
347
- }
348
- if (type instanceof Function) {
349
- type = type.name;
350
- }
351
- if (['String', 'Number', 'Boolean', 'Array'].includes(type)) {
352
- type = type.toLowerCase();
353
- }
354
- return { type };
355
- }
356
- createProperty(kind, options) {
357
- return {
358
- kind,
359
- cascade: [Cascade.PERSIST],
360
- ...options,
361
- };
362
- }
363
- rename(data, from, to) {
364
- if (from in data && !(to in data)) {
365
- // @ts-ignore
366
- data[to] = [data[from]];
367
- // @ts-ignore
368
- delete data[from];
369
- }
370
- }
371
- renameCompositeOptions(name, options = {}) {
372
- if (name !== options.name && !options.fieldNames) {
373
- Utils.renameKey(options, 'name', 'fieldName');
374
- } else if (options.name && (options.fieldNames?.length ?? 0) > 1) {
375
- delete options.name;
376
- }
377
- this.rename(options, 'fieldName', 'fieldNames');
378
- this.rename(options, 'joinColumn', 'joinColumns');
379
- this.rename(options, 'inverseJoinColumn', 'inverseJoinColumns');
380
- this.rename(options, 'referenceColumnName', 'referencedColumnNames');
381
- this.rename(options, 'columnType', 'columnTypes');
382
- }
383
- /**
384
- * Adds a lifecycle hook handler to the entity schema.
385
- * This method allows registering hooks after the entity is defined,
386
- * which can be useful for avoiding circular type references.
387
- *
388
- * @example
389
- * ```ts
390
- * export const Article = defineEntity({
391
- * name: 'Article',
392
- * properties: { ... },
393
- * });
394
- *
395
- * Article.addHook('beforeCreate', async args => {
396
- * args.entity.slug = args.entity.title.toLowerCase();
397
- * });
398
- * ```
399
- */
400
- addHook(event, handler) {
401
- this._meta.hooks[event] ??= [];
402
- this._meta.hooks[event].push(handler);
403
- return this;
404
- }
9
+ /**
10
+ * When schema links the entity class via `class` option, this registry allows the lookup from opposite side,
11
+ * so we can use the class in `entities` option just like the EntitySchema instance.
12
+ */
13
+ static REGISTRY = new Map();
14
+ _meta;
15
+ internal = false;
16
+ initialized = false;
17
+ constructor(meta) {
18
+ meta.name = meta.class ? meta.class.name : meta.name;
19
+ if (meta.name) {
20
+ meta.abstract ??= false;
21
+ }
22
+ this._meta = new EntityMetadata({
23
+ className: meta.name,
24
+ ...meta,
25
+ });
26
+ this._meta.root ??= this._meta;
27
+ if (meta.class && !meta.internal) {
28
+ EntitySchema.REGISTRY.set(meta.class, this);
29
+ }
30
+ }
31
+ /**
32
+ * Checks if the given value is an EntitySchema instance, using duck-typing
33
+ * as a fallback when `instanceof` fails due to CJS/ESM dual-package hazard
34
+ * (e.g. when using `tsx` or `@swc-node/register` with `"type": "commonjs"` projects).
35
+ */
36
+ static is(item) {
37
+ if (item instanceof EntitySchema) {
38
+ return true;
39
+ }
40
+ return item != null && typeof item === 'object' && item.constructor?.name === 'EntitySchema' && 'meta' in item;
41
+ }
42
+ /** Creates an EntitySchema from existing EntityMetadata (used internally). */
43
+ static fromMetadata(meta) {
44
+ const schema = new EntitySchema({ ...meta, internal: true });
45
+ schema.internal = true;
46
+ return schema;
47
+ }
48
+ /** Adds a scalar property to the entity schema. */
49
+ addProperty(name, type, options = {}) {
50
+ this.renameCompositeOptions(name, options);
51
+ const prop = {
52
+ name,
53
+ kind: ReferenceKind.SCALAR,
54
+ ...options,
55
+ ...this.normalizeType(options, type),
56
+ };
57
+ if (type && Type.isMappedType(type.prototype)) {
58
+ prop.type = type;
59
+ }
60
+ if (typeof prop.formula === 'string') {
61
+ const formula = prop.formula;
62
+ prop.formula = () => formula;
63
+ }
64
+ if (prop.formula) {
65
+ prop.persist ??= false;
66
+ }
67
+ this._meta.properties[name] = prop;
68
+ }
69
+ /** Adds an enum property to the entity schema. */
70
+ addEnum(name, type, options = {}) {
71
+ if (options.items instanceof Function) {
72
+ options.items = Utils.extractEnumValues(options.items());
73
+ }
74
+ // enum arrays are simple numeric/string arrays, the constraint is enforced in the custom type only
75
+ if (options.array && !options.type) {
76
+ options.type = new EnumArrayType(`${this._meta.className}.${name}`, options.items);
77
+ options.enum = false;
78
+ }
79
+ const prop = { enum: true, ...options };
80
+ if (prop.array) {
81
+ prop.enum = false;
82
+ }
83
+ // force string labels on native enums
84
+ if (prop.nativeEnumName && Array.isArray(prop.items)) {
85
+ prop.items = prop.items.map(val => '' + val);
86
+ }
87
+ this.addProperty(name, this.internal ? type : type || 'enum', prop);
88
+ }
89
+ /** Adds a version property for optimistic locking. */
90
+ addVersion(name, type, options = {}) {
91
+ this.addProperty(name, type, { version: true, ...options });
92
+ }
93
+ /** Adds a primary key property to the entity schema. */
94
+ addPrimaryKey(name, type, options = {}) {
95
+ this.addProperty(name, type, { primary: true, ...options });
96
+ }
97
+ /** Adds a serialized primary key property (e.g. for MongoDB ObjectId). */
98
+ addSerializedPrimaryKey(name, type, options = {}) {
99
+ this._meta.serializedPrimaryKey = name;
100
+ this.addProperty(name, type, { serializedPrimaryKey: true, ...options });
101
+ }
102
+ /** Adds an embedded property to the entity schema. */
103
+ addEmbedded(name, options) {
104
+ this.renameCompositeOptions(name, options);
105
+ Utils.defaultValue(options, 'prefix', true);
106
+ if (options.array) {
107
+ options.object = true; // force object mode for arrays
108
+ }
109
+ this._meta.properties[name] = {
110
+ name,
111
+ kind: ReferenceKind.EMBEDDED,
112
+ ...this.normalizeType(options),
113
+ ...options,
114
+ };
115
+ }
116
+ /** Adds a many-to-one relation to the entity schema. */
117
+ addManyToOne(name, type, options) {
118
+ const prop = this.createProperty(ReferenceKind.MANY_TO_ONE, options);
119
+ prop.owner = true;
120
+ if (prop.joinColumns && !prop.fieldNames) {
121
+ prop.fieldNames = prop.joinColumns;
122
+ }
123
+ if (prop.fieldNames && !prop.joinColumns) {
124
+ prop.joinColumns = prop.fieldNames;
125
+ }
126
+ // By default, the foreign key constraint is created on the relation
127
+ Utils.defaultValue(prop, 'createForeignKeyConstraint', true);
128
+ this.addProperty(name, type, prop);
129
+ }
130
+ /** Adds a many-to-many relation to the entity schema. */
131
+ addManyToMany(name, type, options) {
132
+ options.fixedOrder = options.fixedOrder || !!options.fixedOrderColumn;
133
+ if (!options.owner && !options.mappedBy) {
134
+ options.owner = true;
135
+ }
136
+ if (options.owner) {
137
+ Utils.renameKey(options, 'mappedBy', 'inversedBy');
138
+ // By default, the foreign key constraint is created on the relation
139
+ Utils.defaultValue(options, 'createForeignKeyConstraint', true);
140
+ }
141
+ const prop = this.createProperty(ReferenceKind.MANY_TO_MANY, options);
142
+ this.addProperty(name, type, prop);
143
+ }
144
+ /** Adds a one-to-many relation to the entity schema. */
145
+ addOneToMany(name, type, options) {
146
+ const prop = this.createProperty(ReferenceKind.ONE_TO_MANY, options);
147
+ this.addProperty(name, type, prop);
148
+ }
149
+ /** Adds a one-to-one relation to the entity schema. */
150
+ addOneToOne(name, type, options) {
151
+ const prop = this.createProperty(ReferenceKind.ONE_TO_ONE, options);
152
+ Utils.defaultValue(prop, 'owner', !!prop.inversedBy || !prop.mappedBy);
153
+ Utils.defaultValue(prop, 'unique', prop.owner);
154
+ if (prop.owner) {
155
+ if (options.mappedBy) {
156
+ Utils.renameKey(prop, 'mappedBy', 'inversedBy');
157
+ }
158
+ // By default, the foreign key constraint is created on the relation
159
+ Utils.defaultValue(prop, 'createForeignKeyConstraint', true);
160
+ }
161
+ if (prop.joinColumns && !prop.fieldNames) {
162
+ prop.fieldNames = prop.joinColumns;
163
+ }
164
+ if (prop.fieldNames && !prop.joinColumns) {
165
+ prop.joinColumns = prop.fieldNames;
166
+ }
167
+ this.addProperty(name, type, prop);
168
+ }
169
+ /** Adds an index definition to the entity schema. */
170
+ addIndex(options) {
171
+ this._meta.indexes.push(options);
172
+ }
173
+ /** Adds a unique constraint definition to the entity schema. */
174
+ addUnique(options) {
175
+ this._meta.uniques.push(options);
176
+ }
177
+ /** Sets a custom repository class for this entity. */
178
+ setCustomRepository(repository) {
179
+ this._meta.repository = repository;
180
+ }
181
+ /** Sets the base entity that this schema extends. */
182
+ setExtends(base) {
183
+ this._meta.extends = base;
184
+ }
185
+ /** Sets or replaces the entity class associated with this schema. */
186
+ setClass(cls) {
187
+ const oldClass = this._meta.class;
188
+ const sameClass = this._meta.class === cls;
189
+ this._meta.class = cls;
190
+ this._meta.prototype = cls.prototype;
191
+ this._meta.className = this._meta.name ?? cls.name;
192
+ if (!sameClass || !this._meta.constructorParams) {
193
+ this._meta.constructorParams = Utils.getConstructorParams(cls);
194
+ }
195
+ if (!this.internal) {
196
+ // Remove old class from registry if it's being replaced with a different class
197
+ if (oldClass && oldClass !== cls && EntitySchema.REGISTRY.get(oldClass) === this) {
198
+ EntitySchema.REGISTRY.delete(oldClass);
199
+ }
200
+ EntitySchema.REGISTRY.set(cls, this);
201
+ }
202
+ const base = Object.getPrototypeOf(cls);
203
+ // Only set extends if the parent is NOT the auto-generated class for this same entity.
204
+ // When the user extends the auto-generated class (from defineEntity without a class option)
205
+ // and registers their custom class via setClass, we don't want to discover the
206
+ // auto-generated class as a separate parent entity.
207
+ if (base !== BaseEntity && base.name !== this._meta.className) {
208
+ this._meta.extends ??= base.name ? base : undefined;
209
+ }
210
+ }
211
+ /** Returns the underlying EntityMetadata. */
212
+ get meta() {
213
+ return this._meta;
214
+ }
215
+ /** Returns the entity class name. */
216
+ get name() {
217
+ return this._meta.className;
218
+ }
219
+ /** Returns the database table name. */
220
+ get tableName() {
221
+ return this._meta.tableName;
222
+ }
223
+ get class() {
224
+ return this._meta.class;
225
+ }
226
+ get properties() {
227
+ return this._meta.properties;
228
+ }
229
+ new(...params) {
230
+ return new this._meta.class(...params);
231
+ }
232
+ /**
233
+ * @internal
234
+ */
235
+ init() {
236
+ if (this.initialized) {
237
+ return this;
238
+ }
239
+ this.setClass(this._meta.class);
240
+ // Abstract TPT entities keep their name because they have their own table
241
+ const isTPT = this._meta.inheritance === 'tpt' || this.isPartOfTPTHierarchy();
242
+ if (this._meta.abstract && !this._meta.discriminatorColumn && !isTPT) {
243
+ delete this._meta.name;
244
+ }
245
+ const tableName = this._meta.collection ?? this._meta.tableName;
246
+ if (tableName?.includes('.') && !this._meta.schema) {
247
+ this._meta.schema = tableName.substring(0, tableName.indexOf('.'));
248
+ this._meta.tableName = tableName.substring(tableName.indexOf('.') + 1);
249
+ }
250
+ this.initProperties();
251
+ this.initPrimaryKeys();
252
+ this._meta.props = Object.values(this._meta.properties);
253
+ this._meta.relations = this._meta.props.filter(prop => typeof prop.kind !== 'undefined' && prop.kind !== ReferenceKind.SCALAR && prop.kind !== ReferenceKind.EMBEDDED);
254
+ this.initialized = true;
255
+ return this;
256
+ }
257
+ /**
258
+ * Check if this entity is part of a TPT hierarchy by walking up the extends chain.
259
+ * This handles mid-level abstract entities (e.g., Animal -> Mammal -> Dog where Mammal is abstract).
260
+ */
261
+ isPartOfTPTHierarchy() {
262
+ let parent = this._meta.extends;
263
+ while (parent) {
264
+ const parentSchema = EntitySchema.is(parent) ? parent : EntitySchema.REGISTRY.get(parent);
265
+ if (!parentSchema) {
266
+ break;
267
+ }
268
+ if (parentSchema._meta.inheritance === 'tpt') {
269
+ return true;
270
+ }
271
+ parent = parentSchema._meta.extends;
272
+ }
273
+ return false;
274
+ }
275
+ initProperties() {
276
+ Utils.entries(this._meta.properties).forEach(([name, options]) => {
277
+ if (Type.isMappedType(options.type)) {
278
+ options.type ??= options.type.constructor.name;
279
+ }
280
+ switch (options.kind) {
281
+ case ReferenceKind.ONE_TO_ONE:
282
+ this.addOneToOne(name, options.type, options);
283
+ break;
284
+ case ReferenceKind.ONE_TO_MANY:
285
+ this.addOneToMany(name, options.type, options);
286
+ break;
287
+ case ReferenceKind.MANY_TO_ONE:
288
+ this.addManyToOne(name, options.type, options);
289
+ break;
290
+ case ReferenceKind.MANY_TO_MANY:
291
+ this.addManyToMany(name, options.type, options);
292
+ break;
293
+ case ReferenceKind.EMBEDDED:
294
+ this.addEmbedded(name, options);
295
+ break;
296
+ default:
297
+ if (options.enum) {
298
+ this.addEnum(name, options.type, options);
299
+ }
300
+ else if (options.primary) {
301
+ this.addPrimaryKey(name, options.type, options);
302
+ }
303
+ else if (options.serializedPrimaryKey) {
304
+ this.addSerializedPrimaryKey(name, options.type, options);
305
+ }
306
+ else if (options.version) {
307
+ this.addVersion(name, options.type, options);
308
+ }
309
+ else {
310
+ this.addProperty(name, options.type, options);
311
+ }
312
+ }
313
+ });
314
+ }
315
+ initPrimaryKeys() {
316
+ const pks = Object.values(this._meta.properties).filter(prop => prop.primary);
317
+ if (pks.length > 0) {
318
+ this._meta.primaryKeys = pks.map(prop => prop.name);
319
+ this._meta.compositePK = pks.length > 1;
320
+ this._meta.simplePK = !this._meta.compositePK && pks[0].kind === ReferenceKind.SCALAR && !pks[0].customType;
321
+ }
322
+ if (pks.length === 1 && ['number', 'bigint'].includes(pks[0].type)) {
323
+ pks[0].autoincrement ??= true;
324
+ }
325
+ const serializedPrimaryKey = Object.values(this._meta.properties).find(prop => prop.serializedPrimaryKey);
326
+ if (serializedPrimaryKey) {
327
+ this._meta.serializedPrimaryKey = serializedPrimaryKey.name;
328
+ }
329
+ }
330
+ normalizeType(options, type) {
331
+ if ('entity' in options) {
332
+ /* v8 ignore next */
333
+ if (typeof options.entity === 'string') {
334
+ throw new Error(`Relation target needs to be an entity class or EntitySchema instance, string '${options.entity}' given instead for ${this._meta.className}.${options.name}.`);
335
+ }
336
+ else if (options.entity) {
337
+ const tmp = options.entity();
338
+ type = options.type = Array.isArray(tmp)
339
+ ? tmp
340
+ .map(t => Utils.className(t))
341
+ .sort()
342
+ .join(' | ')
343
+ : Utils.className(tmp);
344
+ const target = EntitySchema.is(tmp) ? tmp.meta.class : tmp;
345
+ return { type, target };
346
+ }
347
+ }
348
+ if (type instanceof Function) {
349
+ type = type.name;
350
+ }
351
+ if (['String', 'Number', 'Boolean', 'Array'].includes(type)) {
352
+ type = type.toLowerCase();
353
+ }
354
+ return { type };
355
+ }
356
+ createProperty(kind, options) {
357
+ return {
358
+ kind,
359
+ cascade: [Cascade.PERSIST],
360
+ ...options,
361
+ };
362
+ }
363
+ rename(data, from, to) {
364
+ if (from in data && !(to in data)) {
365
+ // @ts-ignore
366
+ data[to] = [data[from]];
367
+ // @ts-ignore
368
+ delete data[from];
369
+ }
370
+ }
371
+ renameCompositeOptions(name, options = {}) {
372
+ if (name !== options.name && !options.fieldNames) {
373
+ Utils.renameKey(options, 'name', 'fieldName');
374
+ }
375
+ else if (options.name && (options.fieldNames?.length ?? 0) > 1) {
376
+ delete options.name;
377
+ }
378
+ this.rename(options, 'fieldName', 'fieldNames');
379
+ this.rename(options, 'joinColumn', 'joinColumns');
380
+ this.rename(options, 'inverseJoinColumn', 'inverseJoinColumns');
381
+ this.rename(options, 'referenceColumnName', 'referencedColumnNames');
382
+ this.rename(options, 'columnType', 'columnTypes');
383
+ }
384
+ /**
385
+ * Adds a lifecycle hook handler to the entity schema.
386
+ * This method allows registering hooks after the entity is defined,
387
+ * which can be useful for avoiding circular type references.
388
+ *
389
+ * @example
390
+ * ```ts
391
+ * export const Article = defineEntity({
392
+ * name: 'Article',
393
+ * properties: { ... },
394
+ * });
395
+ *
396
+ * Article.addHook('beforeCreate', async args => {
397
+ * args.entity.slug = args.entity.title.toLowerCase();
398
+ * });
399
+ * ```
400
+ */
401
+ addHook(event, handler) {
402
+ this._meta.hooks[event] ??= [];
403
+ this._meta.hooks[event].push(handler);
404
+ return this;
405
+ }
405
406
  }