@mikro-orm/core 7.0.0-dev.30 → 7.0.0-dev.301

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 (210) hide show
  1. package/EntityManager.d.ts +69 -61
  2. package/EntityManager.js +365 -283
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -142
  5. package/README.md +2 -0
  6. package/cache/FileCacheAdapter.d.ts +1 -2
  7. package/cache/FileCacheAdapter.js +19 -14
  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 +37 -15
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +144 -43
  16. package/drivers/IDatabaseDriver.d.ts +118 -23
  17. package/entity/BaseEntity.d.ts +63 -4
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +101 -29
  20. package/entity/Collection.js +473 -115
  21. package/entity/EntityAssigner.js +37 -25
  22. package/entity/EntityFactory.d.ts +7 -1
  23. package/entity/EntityFactory.js +116 -64
  24. package/entity/EntityHelper.d.ts +2 -2
  25. package/entity/EntityHelper.js +69 -27
  26. package/entity/EntityLoader.d.ts +11 -10
  27. package/entity/EntityLoader.js +262 -98
  28. package/entity/EntityRepository.d.ts +28 -8
  29. package/entity/EntityRepository.js +8 -2
  30. package/entity/PolymorphicRef.d.ts +12 -0
  31. package/entity/PolymorphicRef.js +18 -0
  32. package/entity/Reference.d.ts +2 -6
  33. package/entity/Reference.js +52 -19
  34. package/entity/WrappedEntity.d.ts +3 -8
  35. package/entity/WrappedEntity.js +6 -7
  36. package/entity/defineEntity.d.ts +525 -311
  37. package/entity/defineEntity.js +134 -290
  38. package/entity/index.d.ts +2 -2
  39. package/entity/index.js +2 -2
  40. package/entity/utils.d.ts +6 -1
  41. package/entity/utils.js +46 -11
  42. package/entity/validators.d.ts +11 -0
  43. package/entity/validators.js +66 -0
  44. package/enums.d.ts +8 -6
  45. package/enums.js +13 -17
  46. package/errors.d.ts +20 -10
  47. package/errors.js +73 -31
  48. package/events/EventManager.d.ts +2 -1
  49. package/events/EventManager.js +20 -12
  50. package/exceptions.js +7 -2
  51. package/hydration/Hydrator.js +1 -2
  52. package/hydration/ObjectHydrator.d.ts +4 -4
  53. package/hydration/ObjectHydrator.js +102 -43
  54. package/index.d.ts +2 -2
  55. package/index.js +1 -2
  56. package/logging/DefaultLogger.d.ts +1 -1
  57. package/logging/DefaultLogger.js +3 -4
  58. package/logging/SimpleLogger.d.ts +1 -1
  59. package/logging/colors.d.ts +1 -1
  60. package/logging/colors.js +5 -7
  61. package/logging/index.d.ts +1 -0
  62. package/logging/index.js +1 -0
  63. package/logging/inspect.d.ts +2 -0
  64. package/logging/inspect.js +11 -0
  65. package/metadata/EntitySchema.d.ts +47 -23
  66. package/metadata/EntitySchema.js +103 -34
  67. package/metadata/MetadataDiscovery.d.ts +64 -9
  68. package/metadata/MetadataDiscovery.js +864 -352
  69. package/metadata/MetadataProvider.d.ts +11 -2
  70. package/metadata/MetadataProvider.js +71 -2
  71. package/metadata/MetadataStorage.d.ts +13 -11
  72. package/metadata/MetadataStorage.js +72 -41
  73. package/metadata/MetadataValidator.d.ts +32 -9
  74. package/metadata/MetadataValidator.js +214 -44
  75. package/metadata/discover-entities.d.ts +5 -0
  76. package/metadata/discover-entities.js +40 -0
  77. package/metadata/index.d.ts +1 -1
  78. package/metadata/index.js +1 -1
  79. package/metadata/types.d.ts +577 -0
  80. package/metadata/types.js +1 -0
  81. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  82. package/naming-strategy/AbstractNamingStrategy.js +25 -4
  83. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  84. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  85. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  86. package/naming-strategy/MongoNamingStrategy.js +6 -6
  87. package/naming-strategy/NamingStrategy.d.ts +28 -4
  88. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  89. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  90. package/not-supported.d.ts +2 -0
  91. package/not-supported.js +8 -0
  92. package/package.json +47 -36
  93. package/platforms/ExceptionConverter.js +1 -1
  94. package/platforms/Platform.d.ts +11 -15
  95. package/platforms/Platform.js +70 -67
  96. package/serialization/EntitySerializer.d.ts +6 -3
  97. package/serialization/EntitySerializer.js +53 -29
  98. package/serialization/EntityTransformer.js +33 -21
  99. package/serialization/SerializationContext.d.ts +6 -6
  100. package/serialization/SerializationContext.js +4 -4
  101. package/types/ArrayType.d.ts +1 -1
  102. package/types/ArrayType.js +2 -3
  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.js +2 -2
  109. package/types/DoubleType.js +1 -1
  110. package/types/EnumArrayType.js +1 -2
  111. package/types/JsonType.d.ts +1 -1
  112. package/types/JsonType.js +7 -2
  113. package/types/TinyIntType.js +1 -1
  114. package/types/Type.d.ts +2 -4
  115. package/types/Type.js +3 -3
  116. package/types/Uint8ArrayType.d.ts +0 -1
  117. package/types/Uint8ArrayType.js +1 -4
  118. package/types/index.d.ts +1 -1
  119. package/typings.d.ts +427 -170
  120. package/typings.js +100 -45
  121. package/unit-of-work/ChangeSet.d.ts +4 -6
  122. package/unit-of-work/ChangeSet.js +8 -9
  123. package/unit-of-work/ChangeSetComputer.d.ts +3 -8
  124. package/unit-of-work/ChangeSetComputer.js +49 -26
  125. package/unit-of-work/ChangeSetPersister.d.ts +13 -12
  126. package/unit-of-work/ChangeSetPersister.js +106 -43
  127. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  128. package/unit-of-work/CommitOrderCalculator.js +17 -15
  129. package/unit-of-work/IdentityMap.d.ts +12 -0
  130. package/unit-of-work/IdentityMap.js +39 -1
  131. package/unit-of-work/UnitOfWork.d.ts +34 -4
  132. package/unit-of-work/UnitOfWork.js +293 -106
  133. package/utils/AbstractMigrator.d.ts +101 -0
  134. package/utils/AbstractMigrator.js +303 -0
  135. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  136. package/utils/AbstractSchemaGenerator.js +30 -18
  137. package/utils/AsyncContext.d.ts +6 -0
  138. package/utils/AsyncContext.js +42 -0
  139. package/utils/Configuration.d.ts +795 -211
  140. package/utils/Configuration.js +160 -197
  141. package/utils/ConfigurationLoader.d.ts +1 -52
  142. package/utils/ConfigurationLoader.js +1 -330
  143. package/utils/Cursor.d.ts +0 -3
  144. package/utils/Cursor.js +28 -13
  145. package/utils/DataloaderUtils.d.ts +10 -5
  146. package/utils/DataloaderUtils.js +42 -22
  147. package/utils/EntityComparator.d.ts +16 -9
  148. package/utils/EntityComparator.js +197 -91
  149. package/utils/QueryHelper.d.ts +18 -6
  150. package/utils/QueryHelper.js +113 -48
  151. package/utils/RawQueryFragment.d.ts +28 -34
  152. package/utils/RawQueryFragment.js +37 -72
  153. package/utils/RequestContext.js +2 -2
  154. package/utils/TransactionContext.js +2 -2
  155. package/utils/TransactionManager.js +11 -7
  156. package/utils/Utils.d.ts +16 -127
  157. package/utils/Utils.js +104 -400
  158. package/utils/clone.js +8 -23
  159. package/utils/env-vars.d.ts +7 -0
  160. package/utils/env-vars.js +98 -0
  161. package/utils/fs-utils.d.ts +34 -0
  162. package/utils/fs-utils.js +193 -0
  163. package/utils/index.d.ts +1 -3
  164. package/utils/index.js +1 -3
  165. package/utils/upsert-utils.d.ts +9 -4
  166. package/utils/upsert-utils.js +51 -5
  167. package/decorators/Check.d.ts +0 -3
  168. package/decorators/Check.js +0 -13
  169. package/decorators/CreateRequestContext.d.ts +0 -3
  170. package/decorators/CreateRequestContext.js +0 -32
  171. package/decorators/Embeddable.d.ts +0 -8
  172. package/decorators/Embeddable.js +0 -11
  173. package/decorators/Embedded.d.ts +0 -12
  174. package/decorators/Embedded.js +0 -18
  175. package/decorators/Entity.d.ts +0 -33
  176. package/decorators/Entity.js +0 -12
  177. package/decorators/Enum.d.ts +0 -9
  178. package/decorators/Enum.js +0 -16
  179. package/decorators/Filter.d.ts +0 -2
  180. package/decorators/Filter.js +0 -8
  181. package/decorators/Formula.d.ts +0 -4
  182. package/decorators/Formula.js +0 -15
  183. package/decorators/Indexed.d.ts +0 -19
  184. package/decorators/Indexed.js +0 -20
  185. package/decorators/ManyToMany.d.ts +0 -42
  186. package/decorators/ManyToMany.js +0 -14
  187. package/decorators/ManyToOne.d.ts +0 -34
  188. package/decorators/ManyToOne.js +0 -14
  189. package/decorators/OneToMany.d.ts +0 -28
  190. package/decorators/OneToMany.js +0 -17
  191. package/decorators/OneToOne.d.ts +0 -28
  192. package/decorators/OneToOne.js +0 -7
  193. package/decorators/PrimaryKey.d.ts +0 -8
  194. package/decorators/PrimaryKey.js +0 -20
  195. package/decorators/Property.d.ts +0 -250
  196. package/decorators/Property.js +0 -32
  197. package/decorators/Transactional.d.ts +0 -14
  198. package/decorators/Transactional.js +0 -28
  199. package/decorators/hooks.d.ts +0 -16
  200. package/decorators/hooks.js +0 -47
  201. package/decorators/index.d.ts +0 -17
  202. package/decorators/index.js +0 -17
  203. package/entity/ArrayCollection.d.ts +0 -118
  204. package/entity/ArrayCollection.js +0 -407
  205. package/entity/EntityValidator.d.ts +0 -19
  206. package/entity/EntityValidator.js +0 -150
  207. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  208. package/metadata/ReflectMetadataProvider.js +0 -44
  209. package/utils/resolveContextProvider.d.ts +0 -10
  210. package/utils/resolveContextProvider.js +0 -28
@@ -1,11 +1,17 @@
1
- import type { Dictionary, EntityMetadata, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
1
+ import type { Dictionary, EntityMetadata, EntityName, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
2
+ import { type QueryOrderMap } from '../enums.js';
2
3
  import type { Platform } from '../platforms/Platform.js';
3
4
  import type { MetadataStorage } from '../metadata/MetadataStorage.js';
5
+ import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
4
6
  /** @internal */
5
7
  export declare class QueryHelper {
6
8
  static readonly SUPPORTED_OPERATORS: string[];
9
+ /**
10
+ * Finds the discriminator value (key) for a given entity class in a discriminator map.
11
+ */
12
+ static findDiscriminatorValue<T>(discriminatorMap: Dictionary<T>, targetClass: T): string | undefined;
7
13
  static processParams(params: unknown): any;
8
- static processObjectParams<T extends object>(params?: T): T;
14
+ static processObjectParams<T extends Dictionary>(params?: T): T;
9
15
  /**
10
16
  * converts `{ account: { $or: [ [Object], [Object] ] } }`
11
17
  * to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
@@ -13,21 +19,27 @@ export declare class QueryHelper {
13
19
  static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
14
20
  static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
15
21
  static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
16
- static getActiveFilters(entityName: string, options: Dictionary<boolean | Dictionary> | string[] | boolean, filters: Dictionary<FilterDef>): FilterDef[];
17
- static isFilterActive(entityName: string, filterName: string, filter: FilterDef, options: Dictionary<boolean | Dictionary>): boolean;
22
+ static getActiveFilters<T>(meta: EntityMetadata<T>, options: FilterOptions | undefined, filters: Dictionary<FilterDef>): FilterDef[];
23
+ static mergePropertyFilters(propFilters: FilterOptions | undefined, options: FilterOptions | undefined): FilterOptions | undefined;
24
+ static isFilterActive<T>(meta: EntityMetadata<T>, filterName: string, filter: FilterDef, options: Dictionary<boolean | Dictionary>): boolean;
18
25
  static processCustomType<T extends object>(prop: EntityProperty<T>, cond: FilterQuery<T>, platform: Platform, key?: string, fromQuery?: boolean): FilterQuery<T>;
19
26
  private static isSupportedOperator;
20
27
  private static processJsonCondition;
21
28
  private static getValueType;
22
29
  static findProperty<T>(fieldName: string, options: ProcessWhereOptions<T>): EntityProperty<T> | undefined;
30
+ /**
31
+ * Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
32
+ * RawQueryFragment symbol keys are never deduped (each is unique).
33
+ */
34
+ static mergeOrderBy<T>(...sources: (QueryOrderMap<T> | QueryOrderMap<T>[] | undefined)[]): QueryOrderMap<T>[];
23
35
  }
24
36
  interface ProcessWhereOptions<T> {
25
37
  where: FilterQuery<T>;
26
- entityName: string;
38
+ entityName: EntityName<T>;
27
39
  metadata: MetadataStorage;
28
40
  platform: Platform;
29
41
  aliased?: boolean;
30
- aliasMap?: Dictionary<string>;
42
+ aliasMap?: Dictionary<EntityName>;
31
43
  convertCustomTypes?: boolean;
32
44
  root?: boolean;
33
45
  type?: 'where' | 'orderBy';
@@ -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,17 +50,21 @@ 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 => {
48
- return Utils.isPlainObject(cond) && Object.keys(cond).every(k2 => {
49
- if (Utils.isOperator(k2, false)) {
50
- if (k2 === '$not') {
51
- return Object.keys(cond[k2]).every(k3 => meta.primaryKeys.includes(k3));
52
- }
53
- return true;
54
- }
55
- return meta.primaryKeys.includes(k2);
56
- });
57
- });
53
+ return (k in GroupOperator &&
54
+ Array.isArray(where[k]) &&
55
+ where[k].every(cond => {
56
+ return (Utils.isPlainObject(cond) &&
57
+ Object.keys(cond).every(k2 => {
58
+ if (Utils.isOperator(k2, false)) {
59
+ if (k2 === '$not') {
60
+ return Object.keys(cond[k2]).every(k3 => meta.primaryKeys.includes(k3));
61
+ }
62
+ /* v8 ignore next */
63
+ return true;
64
+ }
65
+ return meta.primaryKeys.includes(k2);
66
+ }));
67
+ }));
58
68
  });
59
69
  if (groupOperator) {
60
70
  return groupOperator;
@@ -62,7 +72,10 @@ export class QueryHelper {
62
72
  for (const k of keys) {
63
73
  const value = where[k];
64
74
  const prop = meta.properties[k];
65
- if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
75
+ // Polymorphic relations use multiple columns (discriminator + FK), so they cannot
76
+ // participate in the standard single-column FK expansion. Query by discriminator
77
+ // column directly instead, e.g. { likeableType: 'post', likeableId: 1 }.
78
+ if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.polymorphic) {
66
79
  continue;
67
80
  }
68
81
  const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
@@ -87,18 +100,24 @@ export class QueryHelper {
87
100
  return false;
88
101
  }
89
102
  if (meta.primaryKeys.every(pk => pk in where) && Utils.getObjectKeysSize(where) === meta.primaryKeys.length) {
90
- return !!key && !GroupOperator[key] && key !== '$not' && Object.keys(where).every(k => !Utils.isPlainObject(where[k]) || Object.keys(where[k]).every(v => {
91
- if (Utils.isOperator(v, false)) {
92
- return true;
93
- }
94
- if (meta.properties[k].primary && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[k].kind)) {
95
- return this.inlinePrimaryKeyObjects(where[k], meta.properties[k].targetMeta, metadata, v);
96
- }
97
- return true;
98
- }));
103
+ return (!!key &&
104
+ !GroupOperator[key] &&
105
+ key !== '$not' &&
106
+ Object.keys(where).every(k => !Utils.isPlainObject(where[k]) ||
107
+ Object.keys(where[k]).every(v => {
108
+ if (Utils.isOperator(v, false)) {
109
+ return true;
110
+ }
111
+ if (meta.properties[k].primary &&
112
+ [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[k].kind)) {
113
+ return this.inlinePrimaryKeyObjects(where[k], meta.properties[k].targetMeta, metadata, v);
114
+ }
115
+ /* v8 ignore next */
116
+ return true;
117
+ })));
99
118
  }
100
119
  Object.keys(where).forEach(k => {
101
- const meta2 = metadata.find(meta.properties[k]?.type) || meta;
120
+ const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
102
121
  if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
103
122
  where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
104
123
  }
@@ -118,7 +137,7 @@ export class QueryHelper {
118
137
  Utils.dropUndefinedProperties(where);
119
138
  }
120
139
  where = QueryHelper.processParams(where) ?? {};
121
- /* v8 ignore next 3 */
140
+ /* v8 ignore next */
122
141
  if (!root && Utils.isPrimaryKey(where)) {
123
142
  return where;
124
143
  }
@@ -126,11 +145,13 @@ export class QueryHelper {
126
145
  where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
127
146
  }
128
147
  if (Array.isArray(where) && root) {
129
- const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
148
+ const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
130
149
  let cond = { [rootPrimaryKey]: { $in: where } };
131
150
  // @ts-ignore
132
151
  // detect tuple comparison, use `$or` in case the number of constituents don't match
133
- if (meta && !where.every(c => Utils.isPrimaryKey(c) || (Array.isArray(c) && c.length === meta.primaryKeys.length && c.every(i => Utils.isPrimaryKey(i))))) {
152
+ if (meta &&
153
+ !where.every(c => Utils.isPrimaryKey(c) ||
154
+ (Array.isArray(c) && c.length === meta.primaryKeys.length && c.every(i => Utils.isPrimaryKey(i))))) {
134
155
  cond = { $or: where };
135
156
  }
136
157
  return QueryHelper.processWhere({ ...options, where: cond, root: false });
@@ -138,12 +159,10 @@ export class QueryHelper {
138
159
  if (!Utils.isPlainObject(where)) {
139
160
  return where;
140
161
  }
141
- return Object.keys(where).reduce((o, key) => {
162
+ return Utils.getObjectQueryKeys(where).reduce((o, key) => {
142
163
  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)) {
164
+ const customExpression = Raw.isKnownFragmentSymbol(key);
165
+ if (Array.isArray(value) && value.length === 0 && customExpression) {
147
166
  o[key] = value;
148
167
  return o;
149
168
  }
@@ -157,14 +176,19 @@ export class QueryHelper {
157
176
  o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
158
177
  return o;
159
178
  }
179
+ const prop = customExpression ? null : this.findProperty(key, options);
180
+ const keys = prop?.joinColumns?.length ?? 0;
181
+ const composite = keys > 1;
160
182
  if (prop?.customType && convertCustomTypes && !isRaw(value)) {
161
183
  value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
162
184
  }
185
+ // oxfmt-ignore
163
186
  const isJsonProperty = prop?.customType instanceof JsonType && Utils.isPlainObject(value) && !isRaw(value) && Object.keys(value)[0] !== '$eq';
164
187
  if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
165
188
  return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
166
189
  }
167
- if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !key.includes('?') && options.type !== 'orderBy') {
190
+ // oxfmt-ignore
191
+ if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
168
192
  // comparing single composite key - use $eq instead of $in
169
193
  const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
170
194
  o[key] = { [op]: value };
@@ -174,7 +198,7 @@ export class QueryHelper {
174
198
  o[key] = QueryHelper.processWhere({
175
199
  ...options,
176
200
  where: value,
177
- entityName: prop?.type ?? entityName,
201
+ entityName: prop?.targetMeta?.class ?? entityName,
178
202
  root: false,
179
203
  });
180
204
  }
@@ -184,26 +208,44 @@ export class QueryHelper {
184
208
  return o;
185
209
  }, {});
186
210
  }
187
- static getActiveFilters(entityName, options, filters) {
211
+ static getActiveFilters(meta, options, filters) {
188
212
  if (options === false) {
189
213
  return [];
190
214
  }
191
215
  const opts = {};
192
216
  if (Array.isArray(options)) {
193
- options.forEach(filter => opts[filter] = true);
217
+ options.forEach(filter => (opts[filter] = true));
194
218
  }
195
219
  else if (Utils.isPlainObject(options)) {
196
- Object.keys(options).forEach(filter => opts[filter] = options[filter]);
220
+ Object.keys(options).forEach(filter => (opts[filter] = options[filter]));
197
221
  }
198
222
  return Object.keys(filters)
199
- .filter(f => QueryHelper.isFilterActive(entityName, f, filters[f], opts))
223
+ .filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
200
224
  .map(f => {
201
225
  filters[f].name = f;
202
226
  return filters[f];
203
227
  });
204
228
  }
205
- static isFilterActive(entityName, filterName, filter, options) {
206
- if (filter.entity && !filter.entity.includes(entityName)) {
229
+ static mergePropertyFilters(propFilters, options) {
230
+ if (!options || !propFilters || options === true || propFilters === true) {
231
+ return options ?? propFilters;
232
+ }
233
+ if (Array.isArray(propFilters)) {
234
+ propFilters = propFilters.reduce((o, item) => {
235
+ o[item] = true;
236
+ return o;
237
+ }, {});
238
+ }
239
+ if (Array.isArray(options)) {
240
+ options = options.reduce((o, item) => {
241
+ o[item] = true;
242
+ return o;
243
+ }, {});
244
+ }
245
+ return Utils.mergeConfig({}, propFilters, options);
246
+ }
247
+ static isFilterActive(meta, filterName, filter, options) {
248
+ if (filter.entity && !filter.entity.includes(meta.className)) {
207
249
  return false;
208
250
  }
209
251
  if (options[filterName] === false) {
@@ -213,8 +255,8 @@ export class QueryHelper {
213
255
  }
214
256
  static processCustomType(prop, cond, platform, key, fromQuery) {
215
257
  if (Utils.isPlainObject(cond)) {
216
- return Utils.keys(cond).reduce((o, k) => {
217
- if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
258
+ return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
259
+ if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
218
260
  o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
219
261
  }
220
262
  else {
@@ -223,12 +265,10 @@ export class QueryHelper {
223
265
  return o;
224
266
  }, {});
225
267
  }
226
- if (key && Utils.isJsonKeyOperator(key)) {
227
- return Array.isArray(cond)
228
- ? platform.marshallArray(cond)
229
- : cond;
268
+ if (key && JSON_KEY_OPERATORS.includes(key)) {
269
+ return Array.isArray(cond) ? platform.marshallArray(cond) : cond;
230
270
  }
231
- if (Array.isArray(cond) && !(key && Utils.isArrayOperator(key))) {
271
+ if (Array.isArray(cond) && !(key && ARRAY_OPERATORS.includes(key))) {
232
272
  return cond.map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery));
233
273
  }
234
274
  if (isRaw(cond)) {
@@ -272,4 +312,29 @@ export class QueryHelper {
272
312
  const meta = entityName ? options.metadata.find(entityName) : undefined;
273
313
  return meta?.properties[propName];
274
314
  }
315
+ /**
316
+ * Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
317
+ * RawQueryFragment symbol keys are never deduped (each is unique).
318
+ */
319
+ static mergeOrderBy(...sources) {
320
+ const result = [];
321
+ const seenKeys = new Set();
322
+ for (const source of sources) {
323
+ if (source == null) {
324
+ continue;
325
+ }
326
+ for (const item of Utils.asArray(source)) {
327
+ for (const key of Utils.getObjectQueryKeys(item)) {
328
+ if (typeof key === 'symbol') {
329
+ result.push({ [key]: item[key] });
330
+ }
331
+ else if (!seenKeys.has(key)) {
332
+ seenKeys.add(key);
333
+ result.push({ [key]: item[key] });
334
+ }
335
+ }
336
+ }
337
+ }
338
+ return result;
339
+ }
275
340
  }
@@ -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,65 @@
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
- /* v8 ignore next 3 */
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) &&
43
+ Object.getOwnPropertySymbols(object).some(symbol => this.isKnownFragmentSymbol(symbol)));
61
44
  }
62
45
  static isKnownFragment(key) {
63
46
  if (key instanceof RawQueryFragment) {
64
47
  return true;
65
48
  }
66
- return this.#rawQueryCache.has(key);
49
+ return this.isKnownFragmentSymbol(key);
67
50
  }
68
- static getKnownFragment(key, cleanup = true) {
51
+ static getKnownFragment(key) {
69
52
  if (key instanceof RawQueryFragment) {
70
53
  return key;
71
54
  }
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) {
55
+ if (typeof key !== 'symbol') {
81
56
  return;
82
57
  }
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
- }
58
+ return this.#rawQueryReferences.get(key);
93
59
  }
94
- /* v8 ignore next 8 */
95
60
  /** @ignore */
96
- [inspect.custom]() {
61
+ /* v8 ignore next */
62
+ [Symbol.for('nodejs.util.inspect.custom')]() {
97
63
  if (this.params) {
98
64
  return { sql: this.sql, params: this.params };
99
65
  }
@@ -204,20 +170,19 @@ export function raw(sql, params) {
204
170
  *
205
171
  * // value can be empty array
206
172
  * await em.find(User, { [sql`(select 1 = 1)`]: [] });
173
+ *
174
+ * // with type parameter for assignment without casting
175
+ * entity.date = sql<Date>`now()`;
207
176
  * ```
208
177
  */
209
178
  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);
179
+ return raw(sql.join('?'), values);
215
180
  }
216
181
  export function createSqlFunction(func, key) {
217
182
  if (typeof key === 'string') {
218
183
  return raw(`${func}(${key})`);
219
184
  }
220
- return raw(a => `${func}(${(key(a))})`);
185
+ return raw(a => `${func}(${key(a)})`);
221
186
  }
222
187
  sql.ref = (...keys) => raw('??', [keys.join('.')]);
223
188
  sql.now = (length) => raw('current_timestamp' + (length == null ? '' : `(${length})`));
@@ -231,7 +196,7 @@ sql.upper = (key) => createSqlFunction('upper', key);
231
196
  * ```ts
232
197
  * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
233
198
  * // 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})` })
199
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.name})` })
235
200
  * @Entity({ schema: 'library' })
236
201
  * export class Author { ... }
237
202
  * ```
@@ -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;