@mikro-orm/core 7.0.4-dev.9 → 7.0.4

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 +884 -583
  2. package/EntityManager.js +1922 -1895
  3. package/MikroORM.d.ts +103 -74
  4. package/MikroORM.js +178 -179
  5. package/README.md +1 -1
  6. package/cache/CacheAdapter.d.ts +36 -36
  7. package/cache/FileCacheAdapter.d.ts +30 -24
  8. package/cache/FileCacheAdapter.js +80 -78
  9. package/cache/GeneratedCacheAdapter.d.ts +18 -20
  10. package/cache/GeneratedCacheAdapter.js +30 -30
  11. package/cache/MemoryCacheAdapter.d.ts +18 -20
  12. package/cache/MemoryCacheAdapter.js +35 -36
  13. package/cache/NullCacheAdapter.d.ts +16 -16
  14. package/cache/NullCacheAdapter.js +24 -24
  15. package/connections/Connection.d.ts +95 -84
  16. package/connections/Connection.js +165 -168
  17. package/drivers/DatabaseDriver.d.ts +186 -80
  18. package/drivers/DatabaseDriver.js +450 -443
  19. package/drivers/IDatabaseDriver.d.ts +440 -301
  20. package/entity/BaseEntity.d.ts +120 -83
  21. package/entity/BaseEntity.js +43 -43
  22. package/entity/Collection.d.ts +212 -179
  23. package/entity/Collection.js +727 -721
  24. package/entity/EntityAssigner.d.ts +88 -77
  25. package/entity/EntityAssigner.js +231 -230
  26. package/entity/EntityFactory.d.ts +66 -54
  27. package/entity/EntityFactory.js +425 -383
  28. package/entity/EntityHelper.d.ts +34 -22
  29. package/entity/EntityHelper.js +280 -267
  30. package/entity/EntityIdentifier.d.ts +4 -4
  31. package/entity/EntityIdentifier.js +10 -10
  32. package/entity/EntityLoader.d.ts +98 -72
  33. package/entity/EntityLoader.js +753 -723
  34. package/entity/EntityRepository.d.ts +316 -201
  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 +126 -82
  39. package/entity/Reference.js +278 -274
  40. package/entity/WrappedEntity.d.ts +115 -72
  41. package/entity/WrappedEntity.js +168 -166
  42. package/entity/defineEntity.d.ts +1315 -636
  43. package/entity/defineEntity.js +527 -518
  44. package/entity/utils.d.ts +13 -3
  45. package/entity/utils.js +71 -73
  46. package/entity/validators.js +43 -43
  47. package/entity/wrap.js +8 -8
  48. package/enums.d.ts +258 -253
  49. package/enums.js +251 -252
  50. package/errors.d.ts +114 -72
  51. package/errors.js +350 -253
  52. package/events/EventManager.d.ts +26 -14
  53. package/events/EventManager.js +79 -77
  54. package/events/EventSubscriber.d.ts +29 -29
  55. package/events/TransactionEventBroadcaster.d.ts +15 -8
  56. package/events/TransactionEventBroadcaster.js +14 -14
  57. package/exceptions.d.ts +23 -40
  58. package/exceptions.js +35 -52
  59. package/hydration/Hydrator.d.ts +42 -17
  60. package/hydration/Hydrator.js +43 -43
  61. package/hydration/ObjectHydrator.d.ts +50 -17
  62. package/hydration/ObjectHydrator.js +481 -416
  63. package/index.d.ts +116 -2
  64. package/index.js +10 -1
  65. package/logging/DefaultLogger.d.ts +34 -32
  66. package/logging/DefaultLogger.js +86 -86
  67. package/logging/Logger.d.ts +41 -41
  68. package/logging/SimpleLogger.d.ts +13 -11
  69. package/logging/SimpleLogger.js +22 -22
  70. package/logging/colors.d.ts +6 -6
  71. package/logging/colors.js +11 -10
  72. package/logging/inspect.js +7 -7
  73. package/metadata/EntitySchema.d.ts +211 -127
  74. package/metadata/EntitySchema.js +397 -398
  75. package/metadata/MetadataDiscovery.d.ts +114 -114
  76. package/metadata/MetadataDiscovery.js +1951 -1863
  77. package/metadata/MetadataProvider.d.ts +24 -21
  78. package/metadata/MetadataProvider.js +82 -84
  79. package/metadata/MetadataStorage.d.ts +38 -32
  80. package/metadata/MetadataStorage.js +118 -118
  81. package/metadata/MetadataValidator.d.ts +39 -39
  82. package/metadata/MetadataValidator.js +381 -338
  83. package/metadata/discover-entities.d.ts +5 -2
  84. package/metadata/discover-entities.js +35 -27
  85. package/metadata/types.d.ts +615 -531
  86. package/naming-strategy/AbstractNamingStrategy.d.ts +54 -39
  87. package/naming-strategy/AbstractNamingStrategy.js +90 -85
  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 +109 -99
  93. package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
  94. package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
  95. package/not-supported.js +7 -4
  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 +310 -299
  100. package/platforms/Platform.js +663 -636
  101. package/serialization/EntitySerializer.d.ts +49 -26
  102. package/serialization/EntitySerializer.js +224 -218
  103. package/serialization/EntityTransformer.d.ts +10 -6
  104. package/serialization/EntityTransformer.js +219 -217
  105. package/serialization/SerializationContext.d.ts +27 -23
  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 +17 -10
  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 +83 -79
  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 +75 -49
  158. package/types/index.js +52 -26
  159. package/typings.d.ts +1250 -737
  160. package/typings.js +244 -231
  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 +178 -170
  165. package/unit-of-work/ChangeSetPersister.d.ts +63 -44
  166. package/unit-of-work/ChangeSetPersister.js +442 -421
  167. package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
  168. package/unit-of-work/CommitOrderCalculator.js +89 -88
  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 +181 -141
  172. package/unit-of-work/UnitOfWork.js +1200 -1183
  173. package/utils/AbstractMigrator.d.ts +111 -91
  174. package/utils/AbstractMigrator.js +275 -275
  175. package/utils/AbstractSchemaGenerator.d.ts +43 -34
  176. package/utils/AbstractSchemaGenerator.js +121 -122
  177. package/utils/AsyncContext.d.ts +3 -3
  178. package/utils/AsyncContext.js +34 -35
  179. package/utils/Configuration.d.ts +852 -808
  180. package/utils/Configuration.js +359 -344
  181. package/utils/Cursor.d.ts +40 -22
  182. package/utils/Cursor.js +135 -127
  183. package/utils/DataloaderUtils.d.ts +58 -43
  184. package/utils/DataloaderUtils.js +203 -198
  185. package/utils/EntityComparator.d.ts +98 -81
  186. package/utils/EntityComparator.js +828 -728
  187. package/utils/NullHighlighter.d.ts +1 -1
  188. package/utils/NullHighlighter.js +3 -3
  189. package/utils/QueryHelper.d.ts +79 -51
  190. package/utils/QueryHelper.js +372 -361
  191. package/utils/RawQueryFragment.d.ts +50 -34
  192. package/utils/RawQueryFragment.js +107 -105
  193. package/utils/RequestContext.d.ts +32 -32
  194. package/utils/RequestContext.js +52 -53
  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 +199 -197
  199. package/utils/Utils.d.ts +204 -145
  200. package/utils/Utils.js +812 -810
  201. package/utils/clone.js +104 -113
  202. package/utils/env-vars.js +90 -88
  203. package/utils/fs-utils.d.ts +15 -15
  204. package/utils/fs-utils.js +180 -181
  205. package/utils/upsert-utils.d.ts +20 -5
  206. package/utils/upsert-utils.js +114 -116
@@ -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,401 +6,400 @@ 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(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
- }
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
+ }
406
405
  }