@mikro-orm/core 7.0.0-dev.32 → 7.0.0-dev.321

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 (216) hide show
  1. package/EntityManager.d.ts +71 -63
  2. package/EntityManager.js +365 -283
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -142
  5. package/README.md +7 -4
  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 +1 -2
  11. package/cache/index.js +0 -2
  12. package/connections/Connection.d.ts +12 -5
  13. package/connections/Connection.js +37 -15
  14. package/drivers/DatabaseDriver.d.ts +25 -18
  15. package/drivers/DatabaseDriver.js +144 -45
  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 +264 -102
  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 +63 -31
  48. package/events/EventManager.d.ts +2 -1
  49. package/events/EventManager.js +24 -13
  50. package/events/index.d.ts +1 -1
  51. package/events/index.js +0 -1
  52. package/exceptions.js +9 -2
  53. package/hydration/Hydrator.js +1 -2
  54. package/hydration/ObjectHydrator.d.ts +4 -4
  55. package/hydration/ObjectHydrator.js +105 -46
  56. package/index.d.ts +2 -2
  57. package/index.js +1 -2
  58. package/logging/DefaultLogger.d.ts +1 -1
  59. package/logging/DefaultLogger.js +3 -4
  60. package/logging/SimpleLogger.d.ts +1 -1
  61. package/logging/colors.d.ts +1 -1
  62. package/logging/colors.js +5 -7
  63. package/logging/index.d.ts +2 -1
  64. package/logging/index.js +1 -1
  65. package/logging/inspect.d.ts +2 -0
  66. package/logging/inspect.js +11 -0
  67. package/metadata/EntitySchema.d.ts +47 -23
  68. package/metadata/EntitySchema.js +103 -34
  69. package/metadata/MetadataDiscovery.d.ts +64 -9
  70. package/metadata/MetadataDiscovery.js +867 -354
  71. package/metadata/MetadataProvider.d.ts +11 -2
  72. package/metadata/MetadataProvider.js +71 -2
  73. package/metadata/MetadataStorage.d.ts +13 -11
  74. package/metadata/MetadataStorage.js +72 -41
  75. package/metadata/MetadataValidator.d.ts +32 -9
  76. package/metadata/MetadataValidator.js +214 -44
  77. package/metadata/discover-entities.d.ts +5 -0
  78. package/metadata/discover-entities.js +40 -0
  79. package/metadata/index.d.ts +1 -1
  80. package/metadata/index.js +0 -1
  81. package/metadata/types.d.ts +577 -0
  82. package/metadata/types.js +1 -0
  83. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  84. package/naming-strategy/AbstractNamingStrategy.js +26 -5
  85. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  86. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  87. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/MongoNamingStrategy.js +6 -6
  89. package/naming-strategy/NamingStrategy.d.ts +28 -4
  90. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  91. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  92. package/naming-strategy/index.d.ts +1 -1
  93. package/naming-strategy/index.js +0 -1
  94. package/not-supported.d.ts +2 -0
  95. package/not-supported.js +8 -0
  96. package/package.json +47 -36
  97. package/platforms/ExceptionConverter.js +1 -1
  98. package/platforms/Platform.d.ts +33 -15
  99. package/platforms/Platform.js +125 -69
  100. package/serialization/EntitySerializer.d.ts +6 -3
  101. package/serialization/EntitySerializer.js +53 -29
  102. package/serialization/EntityTransformer.js +33 -21
  103. package/serialization/SerializationContext.d.ts +6 -6
  104. package/serialization/SerializationContext.js +4 -4
  105. package/types/ArrayType.d.ts +1 -1
  106. package/types/ArrayType.js +2 -3
  107. package/types/BigIntType.js +1 -1
  108. package/types/BlobType.d.ts +0 -1
  109. package/types/BlobType.js +0 -3
  110. package/types/BooleanType.d.ts +1 -0
  111. package/types/BooleanType.js +3 -0
  112. package/types/DecimalType.js +2 -2
  113. package/types/DoubleType.js +1 -1
  114. package/types/EnumArrayType.js +1 -2
  115. package/types/JsonType.d.ts +1 -1
  116. package/types/JsonType.js +7 -2
  117. package/types/TinyIntType.js +1 -1
  118. package/types/Type.d.ts +2 -4
  119. package/types/Type.js +3 -3
  120. package/types/Uint8ArrayType.d.ts +0 -1
  121. package/types/Uint8ArrayType.js +1 -4
  122. package/types/UuidType.d.ts +2 -0
  123. package/types/UuidType.js +14 -2
  124. package/types/index.d.ts +3 -2
  125. package/typings.d.ts +427 -170
  126. package/typings.js +100 -45
  127. package/unit-of-work/ChangeSet.d.ts +4 -6
  128. package/unit-of-work/ChangeSet.js +8 -9
  129. package/unit-of-work/ChangeSetComputer.d.ts +3 -8
  130. package/unit-of-work/ChangeSetComputer.js +49 -26
  131. package/unit-of-work/ChangeSetPersister.d.ts +13 -12
  132. package/unit-of-work/ChangeSetPersister.js +107 -44
  133. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  134. package/unit-of-work/CommitOrderCalculator.js +17 -15
  135. package/unit-of-work/IdentityMap.d.ts +12 -0
  136. package/unit-of-work/IdentityMap.js +39 -1
  137. package/unit-of-work/UnitOfWork.d.ts +34 -4
  138. package/unit-of-work/UnitOfWork.js +294 -107
  139. package/utils/AbstractMigrator.d.ts +101 -0
  140. package/utils/AbstractMigrator.js +303 -0
  141. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  142. package/utils/AbstractSchemaGenerator.js +30 -18
  143. package/utils/AsyncContext.d.ts +6 -0
  144. package/utils/AsyncContext.js +42 -0
  145. package/utils/Configuration.d.ts +796 -211
  146. package/utils/Configuration.js +160 -197
  147. package/utils/ConfigurationLoader.d.ts +1 -52
  148. package/utils/ConfigurationLoader.js +1 -330
  149. package/utils/Cursor.d.ts +0 -3
  150. package/utils/Cursor.js +29 -14
  151. package/utils/DataloaderUtils.d.ts +10 -5
  152. package/utils/DataloaderUtils.js +42 -22
  153. package/utils/EntityComparator.d.ts +16 -9
  154. package/utils/EntityComparator.js +202 -96
  155. package/utils/QueryHelper.d.ts +34 -7
  156. package/utils/QueryHelper.js +183 -72
  157. package/utils/RawQueryFragment.d.ts +28 -34
  158. package/utils/RawQueryFragment.js +37 -72
  159. package/utils/RequestContext.js +2 -2
  160. package/utils/TransactionContext.js +2 -2
  161. package/utils/TransactionManager.js +11 -7
  162. package/utils/Utils.d.ts +16 -127
  163. package/utils/Utils.js +106 -401
  164. package/utils/clone.js +13 -23
  165. package/utils/env-vars.d.ts +7 -0
  166. package/utils/env-vars.js +98 -0
  167. package/utils/fs-utils.d.ts +34 -0
  168. package/utils/fs-utils.js +193 -0
  169. package/utils/index.d.ts +1 -3
  170. package/utils/index.js +1 -3
  171. package/utils/upsert-utils.d.ts +9 -4
  172. package/utils/upsert-utils.js +51 -5
  173. package/decorators/Check.d.ts +0 -3
  174. package/decorators/Check.js +0 -13
  175. package/decorators/CreateRequestContext.d.ts +0 -3
  176. package/decorators/CreateRequestContext.js +0 -32
  177. package/decorators/Embeddable.d.ts +0 -8
  178. package/decorators/Embeddable.js +0 -11
  179. package/decorators/Embedded.d.ts +0 -12
  180. package/decorators/Embedded.js +0 -18
  181. package/decorators/Entity.d.ts +0 -33
  182. package/decorators/Entity.js +0 -12
  183. package/decorators/Enum.d.ts +0 -9
  184. package/decorators/Enum.js +0 -16
  185. package/decorators/Filter.d.ts +0 -2
  186. package/decorators/Filter.js +0 -8
  187. package/decorators/Formula.d.ts +0 -4
  188. package/decorators/Formula.js +0 -15
  189. package/decorators/Indexed.d.ts +0 -19
  190. package/decorators/Indexed.js +0 -20
  191. package/decorators/ManyToMany.d.ts +0 -42
  192. package/decorators/ManyToMany.js +0 -14
  193. package/decorators/ManyToOne.d.ts +0 -34
  194. package/decorators/ManyToOne.js +0 -14
  195. package/decorators/OneToMany.d.ts +0 -28
  196. package/decorators/OneToMany.js +0 -17
  197. package/decorators/OneToOne.d.ts +0 -28
  198. package/decorators/OneToOne.js +0 -7
  199. package/decorators/PrimaryKey.d.ts +0 -8
  200. package/decorators/PrimaryKey.js +0 -20
  201. package/decorators/Property.d.ts +0 -250
  202. package/decorators/Property.js +0 -32
  203. package/decorators/Transactional.d.ts +0 -14
  204. package/decorators/Transactional.js +0 -28
  205. package/decorators/hooks.d.ts +0 -16
  206. package/decorators/hooks.js +0 -47
  207. package/decorators/index.d.ts +0 -17
  208. package/decorators/index.js +0 -17
  209. package/entity/ArrayCollection.d.ts +0 -118
  210. package/entity/ArrayCollection.js +0 -407
  211. package/entity/EntityValidator.d.ts +0 -19
  212. package/entity/EntityValidator.js +0 -150
  213. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  214. package/metadata/ReflectMetadataProvider.js +0 -44
  215. package/utils/resolveContextProvider.d.ts +0 -10
  216. 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,42 @@ 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
- private static getValueType;
22
28
  static findProperty<T>(fieldName: string, options: ProcessWhereOptions<T>): EntityProperty<T> | undefined;
29
+ /**
30
+ * Converts entity references for composite FK properties into flat arrays
31
+ * of correctly-ordered join column values, before processParams flattens them
32
+ * incorrectly due to shared FK columns.
33
+ */
34
+ private static convertCompositeEntityRefs;
35
+ /**
36
+ * Extracts values for a FK's join columns from an entity by traversing the FK chain.
37
+ * Handles shared FK columns (e.g., tenant_id referenced by multiple FKs) correctly.
38
+ */
39
+ private static extractJoinColumnValues;
40
+ /**
41
+ * Extracts the value for a specific column from an entity by finding which PK property
42
+ * owns that column and recursively traversing FK references.
43
+ */
44
+ private static extractColumnValue;
45
+ /**
46
+ * Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
47
+ * RawQueryFragment symbol keys are never deduped (each is unique).
48
+ */
49
+ static mergeOrderBy<T>(...sources: (QueryOrderMap<T> | QueryOrderMap<T>[] | undefined)[]): QueryOrderMap<T>[];
23
50
  }
24
51
  interface ProcessWhereOptions<T> {
25
52
  where: FilterQuery<T>;
26
- entityName: string;
53
+ entityName: EntityName<T>;
27
54
  metadata: MetadataStorage;
28
55
  platform: Platform;
29
56
  aliased?: boolean;
30
- aliasMap?: Dictionary<string>;
57
+ aliasMap?: Dictionary<EntityName>;
31
58
  convertCustomTypes?: boolean;
32
59
  root?: boolean;
33
60
  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
  }
@@ -114,11 +133,14 @@ export class QueryHelper {
114
133
  QueryHelper.liftGroupOperators(where, meta, metadata);
115
134
  QueryHelper.inlinePrimaryKeyObjects(where, meta, metadata);
116
135
  }
136
+ if (meta && root) {
137
+ QueryHelper.convertCompositeEntityRefs(where, meta);
138
+ }
117
139
  if (platform.getConfig().get('ignoreUndefinedInQuery') && where && typeof where === 'object') {
118
140
  Utils.dropUndefinedProperties(where);
119
141
  }
120
142
  where = QueryHelper.processParams(where) ?? {};
121
- /* v8 ignore next 3 */
143
+ /* v8 ignore next */
122
144
  if (!root && Utils.isPrimaryKey(where)) {
123
145
  return where;
124
146
  }
@@ -126,11 +148,13 @@ export class QueryHelper {
126
148
  where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
127
149
  }
128
150
  if (Array.isArray(where) && root) {
129
- const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
151
+ const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
130
152
  let cond = { [rootPrimaryKey]: { $in: where } };
131
153
  // @ts-ignore
132
154
  // 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))))) {
155
+ if (meta &&
156
+ !where.every(c => Utils.isPrimaryKey(c) ||
157
+ (Array.isArray(c) && c.length === meta.primaryKeys.length && c.every(i => Utils.isPrimaryKey(i))))) {
134
158
  cond = { $or: where };
135
159
  }
136
160
  return QueryHelper.processWhere({ ...options, where: cond, root: false });
@@ -138,12 +162,10 @@ export class QueryHelper {
138
162
  if (!Utils.isPlainObject(where)) {
139
163
  return where;
140
164
  }
141
- return Object.keys(where).reduce((o, key) => {
165
+ return Utils.getObjectQueryKeys(where).reduce((o, key) => {
142
166
  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)) {
167
+ const customExpression = Raw.isKnownFragmentSymbol(key);
168
+ if (Array.isArray(value) && value.length === 0 && customExpression) {
147
169
  o[key] = value;
148
170
  return o;
149
171
  }
@@ -157,14 +179,19 @@ export class QueryHelper {
157
179
  o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
158
180
  return o;
159
181
  }
182
+ const prop = customExpression ? null : this.findProperty(key, options);
183
+ const keys = prop?.joinColumns?.length ?? 0;
184
+ const composite = keys > 1;
160
185
  if (prop?.customType && convertCustomTypes && !isRaw(value)) {
161
186
  value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
162
187
  }
163
- const isJsonProperty = prop?.customType instanceof JsonType && Utils.isPlainObject(value) && !isRaw(value) && Object.keys(value)[0] !== '$eq';
188
+ // oxfmt-ignore
189
+ const isJsonProperty = prop?.customType instanceof JsonType && !isRaw(value) && (Utils.isPlainObject(value) ? Object.keys(value)[0] !== '$eq' : !Array.isArray(value));
164
190
  if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
165
191
  return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
166
192
  }
167
- if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !key.includes('?') && options.type !== 'orderBy') {
193
+ // oxfmt-ignore
194
+ if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
168
195
  // comparing single composite key - use $eq instead of $in
169
196
  const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
170
197
  o[key] = { [op]: value };
@@ -174,7 +201,7 @@ export class QueryHelper {
174
201
  o[key] = QueryHelper.processWhere({
175
202
  ...options,
176
203
  where: value,
177
- entityName: prop?.type ?? entityName,
204
+ entityName: prop?.targetMeta?.class ?? entityName,
178
205
  root: false,
179
206
  });
180
207
  }
@@ -184,26 +211,44 @@ export class QueryHelper {
184
211
  return o;
185
212
  }, {});
186
213
  }
187
- static getActiveFilters(entityName, options, filters) {
214
+ static getActiveFilters(meta, options, filters) {
188
215
  if (options === false) {
189
216
  return [];
190
217
  }
191
218
  const opts = {};
192
219
  if (Array.isArray(options)) {
193
- options.forEach(filter => opts[filter] = true);
220
+ options.forEach(filter => (opts[filter] = true));
194
221
  }
195
222
  else if (Utils.isPlainObject(options)) {
196
- Object.keys(options).forEach(filter => opts[filter] = options[filter]);
223
+ Object.keys(options).forEach(filter => (opts[filter] = options[filter]));
197
224
  }
198
225
  return Object.keys(filters)
199
- .filter(f => QueryHelper.isFilterActive(entityName, f, filters[f], opts))
226
+ .filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
200
227
  .map(f => {
201
228
  filters[f].name = f;
202
229
  return filters[f];
203
230
  });
204
231
  }
205
- static isFilterActive(entityName, filterName, filter, options) {
206
- if (filter.entity && !filter.entity.includes(entityName)) {
232
+ static mergePropertyFilters(propFilters, options) {
233
+ if (!options || !propFilters || options === true || propFilters === true) {
234
+ return options ?? propFilters;
235
+ }
236
+ if (Array.isArray(propFilters)) {
237
+ propFilters = propFilters.reduce((o, item) => {
238
+ o[item] = true;
239
+ return o;
240
+ }, {});
241
+ }
242
+ if (Array.isArray(options)) {
243
+ options = options.reduce((o, item) => {
244
+ o[item] = true;
245
+ return o;
246
+ }, {});
247
+ }
248
+ return Utils.mergeConfig({}, propFilters, options);
249
+ }
250
+ static isFilterActive(meta, filterName, filter, options) {
251
+ if (filter.entity && !filter.entity.includes(meta.className)) {
207
252
  return false;
208
253
  }
209
254
  if (options[filterName] === false) {
@@ -213,8 +258,8 @@ export class QueryHelper {
213
258
  }
214
259
  static processCustomType(prop, cond, platform, key, fromQuery) {
215
260
  if (Utils.isPlainObject(cond)) {
216
- return Utils.keys(cond).reduce((o, k) => {
217
- if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
261
+ return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
262
+ if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
218
263
  o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
219
264
  }
220
265
  else {
@@ -223,12 +268,10 @@ export class QueryHelper {
223
268
  return o;
224
269
  }, {});
225
270
  }
226
- if (key && Utils.isJsonKeyOperator(key)) {
227
- return Array.isArray(cond)
228
- ? platform.marshallArray(cond)
229
- : cond;
271
+ if (key && JSON_KEY_OPERATORS.includes(key)) {
272
+ return Array.isArray(cond) ? platform.marshallArray(cond) : cond;
230
273
  }
231
- if (Array.isArray(cond) && !(key && Utils.isArrayOperator(key))) {
274
+ if (Array.isArray(cond) && !(key && ARRAY_OPERATORS.includes(key))) {
232
275
  return cond.map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery));
233
276
  }
234
277
  if (isRaw(cond)) {
@@ -240,29 +283,7 @@ export class QueryHelper {
240
283
  return !!QueryHelper.SUPPORTED_OPERATORS.find(op => key === op);
241
284
  }
242
285
  static processJsonCondition(o, value, path, platform, alias) {
243
- if (Utils.isPlainObject(value) && !Object.keys(value).some(k => Utils.isOperator(k))) {
244
- Utils.keys(value).forEach(k => {
245
- this.processJsonCondition(o, value[k], [...path, k], platform, alias);
246
- });
247
- return o;
248
- }
249
- if (path.length === 1) {
250
- o[path[0]] = value;
251
- return o;
252
- }
253
- const type = this.getValueType(value);
254
- const k = platform.getSearchJsonPropertyKey(path, type, alias, value);
255
- o[k] = value;
256
- return o;
257
- }
258
- static getValueType(value) {
259
- if (Array.isArray(value)) {
260
- return typeof value[0];
261
- }
262
- if (Utils.isPlainObject(value) && Object.keys(value).every(k => Utils.isOperator(k))) {
263
- return this.getValueType(Object.values(value)[0]);
264
- }
265
- return typeof value;
286
+ return platform.processJsonCondition(o, value, path, alias);
266
287
  }
267
288
  static findProperty(fieldName, options) {
268
289
  const parts = fieldName.split('.');
@@ -272,4 +293,94 @@ export class QueryHelper {
272
293
  const meta = entityName ? options.metadata.find(entityName) : undefined;
273
294
  return meta?.properties[propName];
274
295
  }
296
+ /**
297
+ * Converts entity references for composite FK properties into flat arrays
298
+ * of correctly-ordered join column values, before processParams flattens them
299
+ * incorrectly due to shared FK columns.
300
+ */
301
+ static convertCompositeEntityRefs(where, meta) {
302
+ if (!Utils.isPlainObject(where)) {
303
+ return;
304
+ }
305
+ for (const k of Object.keys(where)) {
306
+ if (k in GroupOperator) {
307
+ if (Array.isArray(where[k])) {
308
+ where[k].forEach((sub) => this.convertCompositeEntityRefs(sub, meta));
309
+ }
310
+ continue;
311
+ }
312
+ if (k === '$not') {
313
+ this.convertCompositeEntityRefs(where[k], meta);
314
+ continue;
315
+ }
316
+ const prop = meta.properties[k];
317
+ if (!prop?.joinColumns || prop.joinColumns.length <= 1) {
318
+ continue;
319
+ }
320
+ const w = where[k];
321
+ if (Utils.isEntity(w)) {
322
+ where[k] = this.extractJoinColumnValues(w, prop);
323
+ }
324
+ else if (Utils.isPlainObject(w)) {
325
+ for (const op of Object.keys(w)) {
326
+ if (Utils.isOperator(op, false) && Array.isArray(w[op])) {
327
+ w[op] = w[op].map((item) => Utils.isEntity(item) ? this.extractJoinColumnValues(item, prop) : item);
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }
333
+ /**
334
+ * Extracts values for a FK's join columns from an entity by traversing the FK chain.
335
+ * Handles shared FK columns (e.g., tenant_id referenced by multiple FKs) correctly.
336
+ */
337
+ static extractJoinColumnValues(entity, prop) {
338
+ return prop.referencedColumnNames.map(refCol => {
339
+ return this.extractColumnValue(entity, prop.targetMeta, refCol);
340
+ });
341
+ }
342
+ /**
343
+ * Extracts the value for a specific column from an entity by finding which PK property
344
+ * owns that column and recursively traversing FK references.
345
+ */
346
+ static extractColumnValue(entity, meta, columnName) {
347
+ for (const pk of meta.primaryKeys) {
348
+ const pkProp = meta.properties[pk];
349
+ const colIdx = pkProp.fieldNames.indexOf(columnName);
350
+ if (colIdx !== -1) {
351
+ const value = entity[pk];
352
+ if (pkProp.targetMeta && Utils.isEntity(value, true)) {
353
+ const refCol = pkProp.referencedColumnNames[colIdx];
354
+ return this.extractColumnValue(value, pkProp.targetMeta, refCol);
355
+ }
356
+ return value;
357
+ }
358
+ }
359
+ return null;
360
+ }
361
+ /**
362
+ * Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
363
+ * RawQueryFragment symbol keys are never deduped (each is unique).
364
+ */
365
+ static mergeOrderBy(...sources) {
366
+ const result = [];
367
+ const seenKeys = new Set();
368
+ for (const source of sources) {
369
+ if (source == null) {
370
+ continue;
371
+ }
372
+ for (const item of Utils.asArray(source)) {
373
+ for (const key of Utils.getObjectQueryKeys(item)) {
374
+ if (typeof key === 'symbol') {
375
+ result.push({ [key]: item[key] });
376
+ }
377
+ else if (!seenKeys.has(key)) {
378
+ seenKeys.add(key);
379
+ result.push({ [key]: item[key] });
380
+ }
381
+ }
382
+ }
383
+ }
384
+ return result;
385
+ }
275
386
  }
@@ -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;