@mikro-orm/core 7.0.0-dev.23 → 7.0.0-dev.231

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 +91 -59
  2. package/EntityManager.js +303 -251
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -143
  5. package/README.md +2 -0
  6. package/cache/FileCacheAdapter.d.ts +1 -1
  7. package/cache/FileCacheAdapter.js +17 -8
  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 +12 -5
  13. package/connections/Connection.js +21 -12
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +118 -35
  16. package/drivers/IDatabaseDriver.d.ts +42 -19
  17. package/entity/BaseEntity.d.ts +61 -2
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +101 -29
  20. package/entity/Collection.js +436 -104
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +26 -18
  23. package/entity/EntityFactory.d.ts +7 -1
  24. package/entity/EntityFactory.js +83 -54
  25. package/entity/EntityHelper.d.ts +2 -2
  26. package/entity/EntityHelper.js +48 -15
  27. package/entity/EntityLoader.d.ts +7 -6
  28. package/entity/EntityLoader.js +221 -93
  29. package/entity/EntityRepository.d.ts +27 -7
  30. package/entity/EntityRepository.js +8 -2
  31. package/entity/PolymorphicRef.d.ts +12 -0
  32. package/entity/PolymorphicRef.js +18 -0
  33. package/entity/Reference.d.ts +1 -5
  34. package/entity/Reference.js +21 -12
  35. package/entity/WrappedEntity.d.ts +0 -5
  36. package/entity/WrappedEntity.js +2 -7
  37. package/entity/defineEntity.d.ts +380 -310
  38. package/entity/defineEntity.js +124 -273
  39. package/entity/index.d.ts +2 -2
  40. package/entity/index.js +2 -2
  41. package/entity/utils.js +1 -1
  42. package/entity/validators.d.ts +11 -0
  43. package/entity/validators.js +65 -0
  44. package/enums.d.ts +8 -6
  45. package/enums.js +2 -1
  46. package/errors.d.ts +20 -10
  47. package/errors.js +55 -23
  48. package/events/EventManager.d.ts +2 -1
  49. package/events/EventManager.js +19 -11
  50. package/hydration/Hydrator.js +1 -2
  51. package/hydration/ObjectHydrator.d.ts +4 -4
  52. package/hydration/ObjectHydrator.js +87 -35
  53. package/index.d.ts +2 -2
  54. package/index.js +1 -2
  55. package/logging/DefaultLogger.d.ts +1 -1
  56. package/logging/DefaultLogger.js +1 -0
  57. package/logging/SimpleLogger.d.ts +1 -1
  58. package/logging/colors.d.ts +1 -1
  59. package/logging/colors.js +7 -6
  60. package/logging/index.d.ts +1 -0
  61. package/logging/index.js +1 -0
  62. package/logging/inspect.d.ts +2 -0
  63. package/logging/inspect.js +11 -0
  64. package/metadata/EntitySchema.d.ts +47 -23
  65. package/metadata/EntitySchema.js +92 -33
  66. package/metadata/MetadataDiscovery.d.ts +64 -9
  67. package/metadata/MetadataDiscovery.js +778 -325
  68. package/metadata/MetadataProvider.d.ts +11 -2
  69. package/metadata/MetadataProvider.js +46 -2
  70. package/metadata/MetadataStorage.d.ts +13 -11
  71. package/metadata/MetadataStorage.js +70 -37
  72. package/metadata/MetadataValidator.d.ts +32 -9
  73. package/metadata/MetadataValidator.js +196 -41
  74. package/metadata/discover-entities.d.ts +5 -0
  75. package/metadata/discover-entities.js +40 -0
  76. package/metadata/index.d.ts +1 -1
  77. package/metadata/index.js +1 -1
  78. package/metadata/types.d.ts +526 -0
  79. package/metadata/types.js +1 -0
  80. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  81. package/naming-strategy/AbstractNamingStrategy.js +20 -2
  82. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  83. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  84. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  85. package/naming-strategy/MongoNamingStrategy.js +6 -6
  86. package/naming-strategy/NamingStrategy.d.ts +28 -4
  87. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  89. package/not-supported.d.ts +2 -0
  90. package/not-supported.js +4 -0
  91. package/package.json +19 -11
  92. package/platforms/ExceptionConverter.js +1 -1
  93. package/platforms/Platform.d.ts +7 -14
  94. package/platforms/Platform.js +20 -43
  95. package/serialization/EntitySerializer.d.ts +5 -0
  96. package/serialization/EntitySerializer.js +47 -27
  97. package/serialization/EntityTransformer.js +28 -18
  98. package/serialization/SerializationContext.d.ts +6 -6
  99. package/serialization/SerializationContext.js +3 -3
  100. package/types/ArrayType.d.ts +1 -1
  101. package/types/ArrayType.js +2 -3
  102. package/types/BigIntType.d.ts +8 -6
  103. package/types/BigIntType.js +1 -1
  104. package/types/BlobType.d.ts +0 -1
  105. package/types/BlobType.js +0 -3
  106. package/types/BooleanType.d.ts +1 -0
  107. package/types/BooleanType.js +3 -0
  108. package/types/DecimalType.d.ts +6 -4
  109. package/types/DecimalType.js +2 -2
  110. package/types/DoubleType.js +1 -1
  111. package/types/EnumArrayType.js +1 -2
  112. package/types/JsonType.d.ts +1 -1
  113. package/types/JsonType.js +7 -2
  114. package/types/TinyIntType.js +1 -1
  115. package/types/Type.d.ts +2 -4
  116. package/types/Type.js +3 -3
  117. package/types/Uint8ArrayType.d.ts +0 -1
  118. package/types/Uint8ArrayType.js +1 -4
  119. package/types/index.d.ts +1 -1
  120. package/typings.d.ts +381 -171
  121. package/typings.js +97 -44
  122. package/unit-of-work/ChangeSet.d.ts +4 -6
  123. package/unit-of-work/ChangeSet.js +4 -5
  124. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  125. package/unit-of-work/ChangeSetComputer.js +35 -14
  126. package/unit-of-work/ChangeSetPersister.d.ts +7 -3
  127. package/unit-of-work/ChangeSetPersister.js +83 -25
  128. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  129. package/unit-of-work/CommitOrderCalculator.js +13 -13
  130. package/unit-of-work/IdentityMap.d.ts +12 -0
  131. package/unit-of-work/IdentityMap.js +39 -1
  132. package/unit-of-work/UnitOfWork.d.ts +27 -3
  133. package/unit-of-work/UnitOfWork.js +258 -92
  134. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  135. package/utils/AbstractSchemaGenerator.js +28 -17
  136. package/utils/AsyncContext.d.ts +6 -0
  137. package/utils/AsyncContext.js +42 -0
  138. package/utils/Configuration.d.ts +795 -209
  139. package/utils/Configuration.js +150 -192
  140. package/utils/ConfigurationLoader.d.ts +1 -54
  141. package/utils/ConfigurationLoader.js +1 -352
  142. package/utils/Cursor.d.ts +0 -3
  143. package/utils/Cursor.js +24 -11
  144. package/utils/DataloaderUtils.d.ts +10 -5
  145. package/utils/DataloaderUtils.js +29 -12
  146. package/utils/EntityComparator.d.ts +16 -9
  147. package/utils/EntityComparator.js +158 -58
  148. package/utils/QueryHelper.d.ts +18 -6
  149. package/utils/QueryHelper.js +76 -23
  150. package/utils/RawQueryFragment.d.ts +28 -34
  151. package/utils/RawQueryFragment.js +35 -71
  152. package/utils/RequestContext.js +2 -2
  153. package/utils/TransactionContext.js +2 -2
  154. package/utils/TransactionManager.js +28 -4
  155. package/utils/Utils.d.ts +14 -127
  156. package/utils/Utils.js +85 -397
  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 +1 -1
  163. package/utils/index.js +1 -1
  164. package/utils/upsert-utils.d.ts +9 -4
  165. package/utils/upsert-utils.js +46 -3
  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 -33
  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 -42
  185. package/decorators/ManyToMany.js +0 -14
  186. package/decorators/ManyToOne.d.ts +0 -34
  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 -28
  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 -14
  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 -118
  203. package/entity/ArrayCollection.js +0 -407
  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,12 +1,18 @@
1
1
  import { Reference } from '../entity/Reference.js';
2
2
  import { Utils } from './Utils.js';
3
- import { GroupOperator, ReferenceKind } from '../enums.js';
3
+ import { ARRAY_OPERATORS, GroupOperator, JSON_KEY_OPERATORS, ReferenceKind } from '../enums.js';
4
4
  import { JsonType } from '../types/JsonType.js';
5
5
  import { helper } from '../entity/wrap.js';
6
- import { RawQueryFragment, isRaw } from './RawQueryFragment.js';
6
+ import { isRaw, Raw } from './RawQueryFragment.js';
7
7
  /** @internal */
8
8
  export class QueryHelper {
9
9
  static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
10
+ /**
11
+ * Finds the discriminator value (key) for a given entity class in a discriminator map.
12
+ */
13
+ static findDiscriminatorValue(discriminatorMap, targetClass) {
14
+ return Object.entries(discriminatorMap).find(([, cls]) => cls === targetClass)?.[0];
15
+ }
10
16
  static processParams(params) {
11
17
  if (Reference.isReference(params)) {
12
18
  params = params.unwrap();
@@ -29,7 +35,7 @@ export class QueryHelper {
29
35
  return params;
30
36
  }
31
37
  static processObjectParams(params = {}) {
32
- Utils.keys(params).forEach(k => {
38
+ Utils.getObjectQueryKeys(params).forEach(k => {
33
39
  params[k] = QueryHelper.processParams(params[k]);
34
40
  });
35
41
  return params;
@@ -44,7 +50,7 @@ export class QueryHelper {
44
50
  }
45
51
  const keys = Object.keys(where);
46
52
  const groupOperator = keys.find(k => {
47
- return Utils.isGroupOperator(k) && Array.isArray(where[k]) && where[k].every(cond => {
53
+ return k in GroupOperator && Array.isArray(where[k]) && where[k].every(cond => {
48
54
  return Utils.isPlainObject(cond) && Object.keys(cond).every(k2 => {
49
55
  if (Utils.isOperator(k2, false)) {
50
56
  if (k2 === '$not') {
@@ -62,7 +68,10 @@ export class QueryHelper {
62
68
  for (const k of keys) {
63
69
  const value = where[k];
64
70
  const prop = meta.properties[k];
65
- if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
71
+ // Polymorphic relations use multiple columns (discriminator + FK), so they cannot
72
+ // participate in the standard single-column FK expansion. Query by discriminator
73
+ // column directly instead, e.g. { likeableType: 'post', likeableId: 1 }.
74
+ if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.polymorphic) {
66
75
  continue;
67
76
  }
68
77
  const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
@@ -98,7 +107,7 @@ export class QueryHelper {
98
107
  }));
99
108
  }
100
109
  Object.keys(where).forEach(k => {
101
- const meta2 = metadata.find(meta.properties[k]?.type) || meta;
110
+ const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
102
111
  if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
103
112
  where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
104
113
  }
@@ -118,7 +127,7 @@ export class QueryHelper {
118
127
  Utils.dropUndefinedProperties(where);
119
128
  }
120
129
  where = QueryHelper.processParams(where) ?? {};
121
- /* v8 ignore next 3 */
130
+ /* v8 ignore next */
122
131
  if (!root && Utils.isPrimaryKey(where)) {
123
132
  return where;
124
133
  }
@@ -126,7 +135,7 @@ export class QueryHelper {
126
135
  where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
127
136
  }
128
137
  if (Array.isArray(where) && root) {
129
- const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
138
+ const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
130
139
  let cond = { [rootPrimaryKey]: { $in: where } };
131
140
  // @ts-ignore
132
141
  // detect tuple comparison, use `$or` in case the number of constituents don't match
@@ -138,12 +147,10 @@ export class QueryHelper {
138
147
  if (!Utils.isPlainObject(where)) {
139
148
  return where;
140
149
  }
141
- return Object.keys(where).reduce((o, key) => {
150
+ return Utils.getObjectQueryKeys(where).reduce((o, key) => {
142
151
  let value = where[key];
143
- const prop = this.findProperty(key, options);
144
- const keys = prop?.joinColumns?.length ?? 0;
145
- const composite = keys > 1;
146
- if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
152
+ const customExpression = Raw.isKnownFragmentSymbol(key);
153
+ if (Array.isArray(value) && value.length === 0 && customExpression) {
147
154
  o[key] = value;
148
155
  return o;
149
156
  }
@@ -157,6 +164,9 @@ export class QueryHelper {
157
164
  o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
158
165
  return o;
159
166
  }
167
+ const prop = customExpression ? null : this.findProperty(key, options);
168
+ const keys = prop?.joinColumns?.length ?? 0;
169
+ const composite = keys > 1;
160
170
  if (prop?.customType && convertCustomTypes && !isRaw(value)) {
161
171
  value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
162
172
  }
@@ -164,7 +174,7 @@ export class QueryHelper {
164
174
  if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
165
175
  return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
166
176
  }
167
- if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !key.includes('?') && options.type !== 'orderBy') {
177
+ if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
168
178
  // comparing single composite key - use $eq instead of $in
169
179
  const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
170
180
  o[key] = { [op]: value };
@@ -174,7 +184,7 @@ export class QueryHelper {
174
184
  o[key] = QueryHelper.processWhere({
175
185
  ...options,
176
186
  where: value,
177
- entityName: prop?.type ?? entityName,
187
+ entityName: prop?.targetMeta?.class ?? entityName,
178
188
  root: false,
179
189
  });
180
190
  }
@@ -184,7 +194,7 @@ export class QueryHelper {
184
194
  return o;
185
195
  }, {});
186
196
  }
187
- static getActiveFilters(entityName, options, filters) {
197
+ static getActiveFilters(meta, options, filters) {
188
198
  if (options === false) {
189
199
  return [];
190
200
  }
@@ -196,14 +206,32 @@ export class QueryHelper {
196
206
  Object.keys(options).forEach(filter => opts[filter] = options[filter]);
197
207
  }
198
208
  return Object.keys(filters)
199
- .filter(f => QueryHelper.isFilterActive(entityName, f, filters[f], opts))
209
+ .filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
200
210
  .map(f => {
201
211
  filters[f].name = f;
202
212
  return filters[f];
203
213
  });
204
214
  }
205
- static isFilterActive(entityName, filterName, filter, options) {
206
- if (filter.entity && !filter.entity.includes(entityName)) {
215
+ static mergePropertyFilters(propFilters, options) {
216
+ if (!options || !propFilters || options === true || propFilters === true) {
217
+ return options ?? propFilters;
218
+ }
219
+ if (Array.isArray(propFilters)) {
220
+ propFilters = propFilters.reduce((o, item) => {
221
+ o[item] = true;
222
+ return o;
223
+ }, {});
224
+ }
225
+ if (Array.isArray(options)) {
226
+ options = options.reduce((o, item) => {
227
+ o[item] = true;
228
+ return o;
229
+ }, {});
230
+ }
231
+ return Utils.mergeConfig({}, propFilters, options);
232
+ }
233
+ static isFilterActive(meta, filterName, filter, options) {
234
+ if (filter.entity && !filter.entity.includes(meta.className)) {
207
235
  return false;
208
236
  }
209
237
  if (options[filterName] === false) {
@@ -213,8 +241,8 @@ export class QueryHelper {
213
241
  }
214
242
  static processCustomType(prop, cond, platform, key, fromQuery) {
215
243
  if (Utils.isPlainObject(cond)) {
216
- return Utils.keys(cond).reduce((o, k) => {
217
- if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
244
+ return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
245
+ if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
218
246
  o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
219
247
  }
220
248
  else {
@@ -223,12 +251,12 @@ export class QueryHelper {
223
251
  return o;
224
252
  }, {});
225
253
  }
226
- if (key && Utils.isJsonKeyOperator(key)) {
254
+ if (key && JSON_KEY_OPERATORS.includes(key)) {
227
255
  return Array.isArray(cond)
228
256
  ? platform.marshallArray(cond)
229
257
  : cond;
230
258
  }
231
- if (Array.isArray(cond) && !(key && Utils.isArrayOperator(key))) {
259
+ if (Array.isArray(cond) && !(key && ARRAY_OPERATORS.includes(key))) {
232
260
  return cond.map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery));
233
261
  }
234
262
  if (isRaw(cond)) {
@@ -272,4 +300,29 @@ export class QueryHelper {
272
300
  const meta = entityName ? options.metadata.find(entityName) : undefined;
273
301
  return meta?.properties[propName];
274
302
  }
303
+ /**
304
+ * Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
305
+ * RawQueryFragment symbol keys are never deduped (each is unique).
306
+ */
307
+ static mergeOrderBy(...sources) {
308
+ const result = [];
309
+ const seenKeys = new Set();
310
+ for (const source of sources) {
311
+ if (source == null) {
312
+ continue;
313
+ }
314
+ for (const item of Utils.asArray(source)) {
315
+ for (const key of Utils.getObjectQueryKeys(item)) {
316
+ if (typeof key === 'symbol') {
317
+ result.push({ [key]: item[key] });
318
+ }
319
+ else if (!seenKeys.has(key)) {
320
+ seenKeys.add(key);
321
+ result.push({ [key]: item[key] });
322
+ }
323
+ }
324
+ }
325
+ }
326
+ return result;
327
+ }
275
328
  }
@@ -1,34 +1,25 @@
1
- import { inspect } from 'node:util';
2
1
  import type { AnyString, Dictionary, EntityKey } from '../typings.js';
3
- export declare class RawQueryFragment {
2
+ declare const rawFragmentSymbolBrand: unique symbol;
3
+ export type RawQueryFragmentSymbol = symbol & {
4
+ readonly [rawFragmentSymbolBrand]: true;
5
+ };
6
+ export declare class RawQueryFragment<Alias extends string = string> {
4
7
  #private;
5
8
  readonly sql: string;
6
9
  readonly params: unknown[];
7
- static cloneRegistry?: Set<string>;
10
+ /** @internal Type-level only - used to track the alias for type inference */
11
+ private readonly __alias?;
8
12
  constructor(sql: string, params?: unknown[]);
9
- as(alias: string): RawQueryFragment;
10
- valueOf(): string;
13
+ get key(): RawQueryFragmentSymbol;
14
+ as<A extends string>(alias: A): RawQueryFragment<A>;
15
+ [Symbol.toPrimitive](hint: 'string'): RawQueryFragmentSymbol;
16
+ get [Symbol.toStringTag](): string;
11
17
  toJSON(): string;
12
- toString(): string;
13
- /** @internal */
14
- assign(): void;
15
- clone(): RawQueryFragment;
16
- static run<T>(cb: (...args: any[]) => Promise<T>): Promise<T>;
17
- /**
18
- * @internal allows testing we don't leak memory, as the raw fragments cache needs to be cleared automatically
19
- */
20
- static checkCacheSize(): number;
21
- static isKnownFragment(key: string | RawQueryFragment): boolean;
22
- static getKnownFragment(key: string | RawQueryFragment, cleanup?: boolean): RawQueryFragment | undefined;
23
- static remove(key: string): void;
24
- /** @ignore */
25
- [inspect.custom](): {
26
- sql: string;
27
- params: unknown[];
28
- } | {
29
- sql: string;
30
- params?: undefined;
31
- };
18
+ clone(): this;
19
+ static isKnownFragmentSymbol(key: unknown): key is RawQueryFragmentSymbol;
20
+ static hasObjectFragments(object: unknown): boolean;
21
+ static isKnownFragment(key: unknown): key is RawQueryFragment | symbol;
22
+ static getKnownFragment(key: unknown): RawQueryFragment<any> | undefined;
32
23
  }
33
24
  export { RawQueryFragment as Raw };
34
25
  export declare function isRaw(value: unknown): value is RawQueryFragment;
@@ -91,7 +82,7 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
91
82
  * export class Author { ... }
92
83
  * ```
93
84
  */
94
- export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R;
85
+ export declare function raw<R = RawQueryFragment & symbol, T extends object = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R;
95
86
  /**
96
87
  * Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
97
88
  *
@@ -104,16 +95,19 @@ export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> |
104
95
  *
105
96
  * // value can be empty array
106
97
  * await em.find(User, { [sql`(select 1 = 1)`]: [] });
98
+ *
99
+ * // with type parameter for assignment without casting
100
+ * entity.date = sql<Date>`now()`;
107
101
  * ```
108
102
  */
109
- export declare function sql(sql: readonly string[], ...values: unknown[]): any;
103
+ export declare function sql<R = RawQueryFragment & symbol>(sql: readonly string[], ...values: unknown[]): R;
110
104
  export declare namespace sql {
111
- var ref: <T extends object>(...keys: string[]) => RawQueryFragment;
112
- var now: (length?: number) => string;
113
- var lower: <T extends object>(key: string | ((alias: string) => string)) => string;
114
- var upper: <T extends object>(key: string | ((alias: string) => string)) => string;
105
+ var ref: <T extends object = any>(...keys: string[]) => RawQueryFragment<string> & symbol;
106
+ var now: (length?: number) => RawQueryFragment<string> & symbol;
107
+ var lower: <R = RawQueryFragment<string> & symbol, T extends object = any>(key: string | ((alias: string) => string)) => R;
108
+ var upper: <R = RawQueryFragment<string> & symbol, T extends object = any>(key: string | ((alias: string) => string)) => R;
115
109
  }
116
- export declare function createSqlFunction<T extends object, R = string>(func: string, key: string | ((alias: string) => string)): R;
110
+ export declare function createSqlFunction<R = RawQueryFragment & symbol, T extends object = any>(func: string, key: string | ((alias: string) => string)): R;
117
111
  /**
118
112
  * Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
119
113
  *
@@ -122,11 +116,11 @@ export declare function createSqlFunction<T extends object, R = string>(func: st
122
116
  * ```ts
123
117
  * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
124
118
  * // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
125
- * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
119
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.name})` })
126
120
  * @Entity({ schema: 'library' })
127
121
  * export class Author { ... }
128
122
  * ```
129
123
  */
130
124
  export declare function quote(expParts: readonly string[], ...values: (string | {
131
125
  toString(): string;
132
- })[]): any;
126
+ })[]): RawQueryFragment<string> & symbol;
@@ -1,99 +1,64 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
- import { inspect } from 'node:util';
3
1
  import { Utils } from './Utils.js';
4
2
  export class RawQueryFragment {
5
3
  sql;
6
4
  params;
7
- static #rawQueryCache = new Map();
8
- static #storage = new AsyncLocalStorage();
9
- static #index = 0n;
10
- static cloneRegistry;
11
- #assigned = false;
12
- #used = 0;
5
+ static #rawQueryReferences = new WeakMap();
13
6
  #key;
14
7
  constructor(sql, params = []) {
15
8
  this.sql = sql;
16
9
  this.params = params;
17
- this.#key = `[raw]: ${this.sql} (#${RawQueryFragment.#index++})`;
18
10
  }
19
- as(alias) {
20
- // TODO: to be removed in v7
21
- /* istanbul ignore next */
22
- if (alias.startsWith('`') || alias.startsWith('"')) {
23
- return new RawQueryFragment(`${this.sql} as ${alias}`, this.params);
11
+ get key() {
12
+ if (!this.#key) {
13
+ this.#key = Symbol(this.toJSON());
14
+ RawQueryFragment.#rawQueryReferences.set(this.#key, this);
24
15
  }
16
+ return this.#key;
17
+ }
18
+ as(alias) {
25
19
  return new RawQueryFragment(`${this.sql} as ??`, [...this.params, alias]);
26
20
  }
27
- valueOf() {
21
+ [Symbol.toPrimitive](hint) {
22
+ // if a fragment is converted to string (used as an object key), return a unique symbol
23
+ // and save a weak reference to map so we can retrieve it when compiling the query
24
+ if (hint === 'string') {
25
+ return this.key;
26
+ }
28
27
  throw new Error(`Trying to modify raw SQL fragment: '${this.sql}'`);
29
28
  }
29
+ get [Symbol.toStringTag]() {
30
+ return this.toJSON();
31
+ }
30
32
  toJSON() {
31
- return this.#key;
33
+ return `raw('${this.sql}')`;
32
34
  }
33
- toString() {
34
- RawQueryFragment.#rawQueryCache.set(this.#key, this);
35
- this.#used++;
36
- return this.#key;
35
+ clone() {
36
+ return this;
37
37
  }
38
- /** @internal */
39
- assign() {
40
- if (this.#assigned) {
41
- throw new Error(`Cannot reassign already used RawQueryFragment: '${this.sql}'`);
42
- }
43
- this.#assigned = true;
38
+ static isKnownFragmentSymbol(key) {
39
+ return typeof key === 'symbol' && this.#rawQueryReferences.has(key);
44
40
  }
45
- clone() {
46
- RawQueryFragment.cloneRegistry?.add(this.#key);
47
- return new RawQueryFragment(this.sql, this.params);
48
- }
49
- static async run(cb) {
50
- const removeStack = new Set();
51
- const res = await this.#storage.run(removeStack, cb);
52
- removeStack.forEach(key => RawQueryFragment.remove(key));
53
- removeStack.clear();
54
- return res;
55
- }
56
- /**
57
- * @internal allows testing we don't leak memory, as the raw fragments cache needs to be cleared automatically
58
- */
59
- static checkCacheSize() {
60
- return this.#rawQueryCache.size;
41
+ static hasObjectFragments(object) {
42
+ return Utils.isPlainObject(object) && Object.getOwnPropertySymbols(object).some(symbol => this.isKnownFragmentSymbol(symbol));
61
43
  }
62
44
  static isKnownFragment(key) {
63
45
  if (key instanceof RawQueryFragment) {
64
46
  return true;
65
47
  }
66
- return this.#rawQueryCache.has(key);
48
+ return this.isKnownFragmentSymbol(key);
67
49
  }
68
- static getKnownFragment(key, cleanup = true) {
50
+ static getKnownFragment(key) {
69
51
  if (key instanceof RawQueryFragment) {
70
52
  return key;
71
53
  }
72
- const raw = this.#rawQueryCache.get(key);
73
- if (raw && cleanup) {
74
- this.remove(key);
75
- }
76
- return raw;
77
- }
78
- static remove(key) {
79
- const raw = this.#rawQueryCache.get(key);
80
- if (!raw) {
54
+ if (typeof key !== 'symbol') {
81
55
  return;
82
56
  }
83
- raw.#used--;
84
- if (raw.#used <= 0) {
85
- const removeStack = this.#storage.getStore();
86
- if (removeStack) {
87
- removeStack.add(key);
88
- }
89
- else {
90
- this.#rawQueryCache.delete(key);
91
- }
92
- }
57
+ return this.#rawQueryReferences.get(key);
93
58
  }
94
- /* v8 ignore next 8 */
95
59
  /** @ignore */
96
- [inspect.custom]() {
60
+ /* v8 ignore next */
61
+ [Symbol.for('nodejs.util.inspect.custom')]() {
97
62
  if (this.params) {
98
63
  return { sql: this.sql, params: this.params };
99
64
  }
@@ -204,14 +169,13 @@ export function raw(sql, params) {
204
169
  *
205
170
  * // value can be empty array
206
171
  * await em.find(User, { [sql`(select 1 = 1)`]: [] });
172
+ *
173
+ * // with type parameter for assignment without casting
174
+ * entity.date = sql<Date>`now()`;
207
175
  * ```
208
176
  */
209
177
  export function sql(sql, ...values) {
210
- return raw(sql.reduce((query, queryPart, i) => {
211
- const valueExists = i < values.length;
212
- const text = query + queryPart;
213
- return valueExists ? text + '?' : text;
214
- }, ''), values);
178
+ return raw(sql.join('?'), values);
215
179
  }
216
180
  export function createSqlFunction(func, key) {
217
181
  if (typeof key === 'string') {
@@ -231,7 +195,7 @@ sql.upper = (key) => createSqlFunction('upper', key);
231
195
  * ```ts
232
196
  * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
233
197
  * // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
234
- * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
198
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.name})` })
235
199
  * @Entity({ schema: 'library' })
236
200
  * export class Author { ... }
237
201
  * ```
@@ -1,10 +1,10 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
1
+ import { createAsyncContext } from './AsyncContext.js';
2
2
  /**
3
3
  * Uses `AsyncLocalStorage` to create async context that holds the current EM fork.
4
4
  */
5
5
  export class RequestContext {
6
6
  map;
7
- static storage = new AsyncLocalStorage();
7
+ static storage = createAsyncContext();
8
8
  static counter = 1;
9
9
  id = RequestContext.counter++;
10
10
  constructor(map) {
@@ -1,7 +1,7 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
1
+ import { createAsyncContext } from './AsyncContext.js';
2
2
  export class TransactionContext {
3
3
  em;
4
- static storage = new AsyncLocalStorage();
4
+ static storage = createAsyncContext();
5
5
  id;
6
6
  constructor(em) {
7
7
  this.em = em;
@@ -1,8 +1,9 @@
1
- import { TransactionPropagation } from '../enums.js';
1
+ import { ReferenceKind, TransactionPropagation } from '../enums.js';
2
2
  import { TransactionEventBroadcaster } from '../events/TransactionEventBroadcaster.js';
3
3
  import { TransactionContext } from '../utils/TransactionContext.js';
4
4
  import { ChangeSetType } from '../unit-of-work/ChangeSet.js';
5
5
  import { TransactionStateError } from '../errors.js';
6
+ import { helper } from '../entity/wrap.js';
6
7
  /**
7
8
  * Manages transaction lifecycle and propagation for EntityManager.
8
9
  */
@@ -16,9 +17,7 @@ export class TransactionManager {
16
17
  */
17
18
  async handle(cb, options = {}) {
18
19
  const em = this.em.getContext(false);
19
- // Set NESTED as the default propagation type
20
20
  options.propagation ??= TransactionPropagation.NESTED;
21
- // Set the context to the current transaction context if not already set
22
21
  options.ctx ??= em.getTransactionContext();
23
22
  const hasExistingTransaction = !!em.getTransactionContext();
24
23
  return this.executeWithPropagation(options.propagation, em, cb, options, hasExistingTransaction);
@@ -149,8 +148,33 @@ export class TransactionManager {
149
148
  * Merges entities from fork to parent EntityManager.
150
149
  */
151
150
  mergeEntitiesToParent(fork, parent) {
151
+ const parentUoW = parent.getUnitOfWork(false);
152
+ // perf: if parent is empty, we can just move all entities from the fork to skip the `em.merge` overhead
153
+ if (parentUoW.getIdentityMap().keys().length === 0) {
154
+ for (const entity of fork.getUnitOfWork(false).getIdentityMap()) {
155
+ parentUoW.getIdentityMap().store(entity);
156
+ helper(entity).__em = parent;
157
+ }
158
+ return;
159
+ }
152
160
  for (const entity of fork.getUnitOfWork(false).getIdentityMap()) {
153
- parent.merge(entity, { disableContextResolution: true, keepIdentity: true, refresh: true });
161
+ const wrapped = helper(entity);
162
+ const meta = wrapped.__meta;
163
+ // eslint-disable-next-line dot-notation
164
+ const parentEntity = parentUoW.getById(meta.class, wrapped.getPrimaryKey(), parent['_schema'], true);
165
+ if (parentEntity && parentEntity !== entity) {
166
+ const parentWrapped = helper(parentEntity);
167
+ parentWrapped.__data = wrapped.__data;
168
+ parentWrapped.__originalEntityData = wrapped.__originalEntityData;
169
+ for (const prop of meta.hydrateProps) {
170
+ if (prop.kind === ReferenceKind.SCALAR) {
171
+ parentEntity[prop.name] = entity[prop.name];
172
+ }
173
+ }
174
+ }
175
+ else {
176
+ parentUoW.merge(entity, new Set([entity]));
177
+ }
154
178
  }
155
179
  }
156
180
  /**