@mikro-orm/core 7.0.0-dev.12 → 7.0.0-dev.120

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 (207) hide show
  1. package/EntityManager.d.ts +85 -56
  2. package/EntityManager.js +332 -293
  3. package/MikroORM.d.ts +41 -32
  4. package/MikroORM.js +100 -140
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +1 -1
  7. package/cache/FileCacheAdapter.js +8 -7
  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 +16 -7
  13. package/connections/Connection.js +23 -14
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +35 -19
  16. package/drivers/IDatabaseDriver.d.ts +38 -17
  17. package/entity/BaseEntity.d.ts +0 -1
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +95 -30
  20. package/entity/Collection.js +439 -99
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +26 -18
  23. package/entity/EntityFactory.d.ts +7 -0
  24. package/entity/EntityFactory.js +72 -53
  25. package/entity/EntityHelper.d.ts +2 -2
  26. package/entity/EntityHelper.js +30 -15
  27. package/entity/EntityLoader.d.ts +7 -6
  28. package/entity/EntityLoader.js +84 -72
  29. package/entity/EntityRepository.d.ts +1 -1
  30. package/entity/EntityRepository.js +2 -2
  31. package/entity/Reference.d.ts +6 -5
  32. package/entity/Reference.js +34 -9
  33. package/entity/WrappedEntity.d.ts +2 -7
  34. package/entity/WrappedEntity.js +3 -8
  35. package/entity/defineEntity.d.ts +568 -0
  36. package/entity/defineEntity.js +529 -0
  37. package/entity/index.d.ts +3 -2
  38. package/entity/index.js +3 -2
  39. package/entity/utils.d.ts +7 -0
  40. package/entity/utils.js +16 -4
  41. package/entity/validators.d.ts +11 -0
  42. package/entity/validators.js +65 -0
  43. package/enums.d.ts +21 -6
  44. package/enums.js +14 -1
  45. package/errors.d.ts +17 -9
  46. package/errors.js +41 -21
  47. package/events/EventManager.d.ts +2 -1
  48. package/events/EventManager.js +19 -11
  49. package/hydration/Hydrator.js +1 -2
  50. package/hydration/ObjectHydrator.d.ts +4 -4
  51. package/hydration/ObjectHydrator.js +50 -33
  52. package/index.d.ts +2 -2
  53. package/index.js +1 -2
  54. package/logging/DefaultLogger.d.ts +1 -1
  55. package/logging/DefaultLogger.js +1 -0
  56. package/logging/SimpleLogger.d.ts +1 -1
  57. package/logging/colors.d.ts +1 -1
  58. package/logging/colors.js +7 -6
  59. package/logging/index.d.ts +1 -0
  60. package/logging/index.js +1 -0
  61. package/logging/inspect.d.ts +2 -0
  62. package/logging/inspect.js +11 -0
  63. package/metadata/EntitySchema.d.ts +13 -17
  64. package/metadata/EntitySchema.js +67 -51
  65. package/metadata/MetadataDiscovery.d.ts +6 -10
  66. package/metadata/MetadataDiscovery.js +289 -298
  67. package/metadata/MetadataProvider.d.ts +11 -2
  68. package/metadata/MetadataProvider.js +46 -2
  69. package/metadata/MetadataStorage.d.ts +13 -11
  70. package/metadata/MetadataStorage.js +70 -37
  71. package/metadata/MetadataValidator.d.ts +2 -9
  72. package/metadata/MetadataValidator.js +22 -38
  73. package/metadata/discover-entities.d.ts +5 -0
  74. package/metadata/discover-entities.js +40 -0
  75. package/metadata/index.d.ts +1 -1
  76. package/metadata/index.js +1 -1
  77. package/metadata/types.d.ts +480 -0
  78. package/metadata/types.js +1 -0
  79. package/naming-strategy/AbstractNamingStrategy.d.ts +8 -4
  80. package/naming-strategy/AbstractNamingStrategy.js +8 -2
  81. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  82. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  83. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  84. package/naming-strategy/MongoNamingStrategy.js +6 -6
  85. package/naming-strategy/NamingStrategy.d.ts +14 -4
  86. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  87. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  88. package/not-supported.d.ts +2 -0
  89. package/not-supported.js +4 -0
  90. package/package.json +19 -11
  91. package/platforms/ExceptionConverter.js +1 -1
  92. package/platforms/Platform.d.ts +6 -13
  93. package/platforms/Platform.js +17 -43
  94. package/serialization/EntitySerializer.d.ts +5 -0
  95. package/serialization/EntitySerializer.js +47 -27
  96. package/serialization/EntityTransformer.js +28 -18
  97. package/serialization/SerializationContext.d.ts +6 -6
  98. package/serialization/SerializationContext.js +16 -13
  99. package/types/ArrayType.d.ts +1 -1
  100. package/types/ArrayType.js +2 -3
  101. package/types/BigIntType.d.ts +8 -6
  102. package/types/BigIntType.js +1 -1
  103. package/types/BlobType.d.ts +0 -1
  104. package/types/BlobType.js +0 -3
  105. package/types/BooleanType.d.ts +2 -1
  106. package/types/BooleanType.js +3 -0
  107. package/types/DecimalType.d.ts +6 -4
  108. package/types/DecimalType.js +3 -3
  109. package/types/DoubleType.js +2 -2
  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 +124 -86
  120. package/typings.js +50 -42
  121. package/unit-of-work/ChangeSet.d.ts +2 -6
  122. package/unit-of-work/ChangeSet.js +4 -5
  123. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  124. package/unit-of-work/ChangeSetComputer.js +14 -12
  125. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  126. package/unit-of-work/ChangeSetPersister.js +65 -33
  127. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  128. package/unit-of-work/CommitOrderCalculator.js +13 -13
  129. package/unit-of-work/UnitOfWork.d.ts +10 -3
  130. package/unit-of-work/UnitOfWork.js +139 -96
  131. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  132. package/utils/AbstractSchemaGenerator.js +18 -16
  133. package/utils/AsyncContext.d.ts +6 -0
  134. package/utils/AsyncContext.js +42 -0
  135. package/utils/Configuration.d.ts +753 -207
  136. package/utils/Configuration.js +145 -190
  137. package/utils/ConfigurationLoader.d.ts +1 -54
  138. package/utils/ConfigurationLoader.js +1 -352
  139. package/utils/Cursor.d.ts +0 -3
  140. package/utils/Cursor.js +9 -6
  141. package/utils/DataloaderUtils.d.ts +15 -5
  142. package/utils/DataloaderUtils.js +65 -17
  143. package/utils/EntityComparator.d.ts +13 -9
  144. package/utils/EntityComparator.js +85 -43
  145. package/utils/QueryHelper.d.ts +14 -6
  146. package/utils/QueryHelper.js +87 -25
  147. package/utils/RawQueryFragment.d.ts +48 -25
  148. package/utils/RawQueryFragment.js +66 -70
  149. package/utils/RequestContext.js +2 -2
  150. package/utils/TransactionContext.js +2 -2
  151. package/utils/TransactionManager.d.ts +65 -0
  152. package/utils/TransactionManager.js +223 -0
  153. package/utils/Utils.d.ts +12 -119
  154. package/utils/Utils.js +97 -373
  155. package/utils/clone.js +8 -23
  156. package/utils/env-vars.d.ts +7 -0
  157. package/utils/env-vars.js +97 -0
  158. package/utils/fs-utils.d.ts +32 -0
  159. package/utils/fs-utils.js +178 -0
  160. package/utils/index.d.ts +2 -1
  161. package/utils/index.js +2 -1
  162. package/utils/upsert-utils.d.ts +9 -4
  163. package/utils/upsert-utils.js +55 -4
  164. package/decorators/Check.d.ts +0 -3
  165. package/decorators/Check.js +0 -13
  166. package/decorators/CreateRequestContext.d.ts +0 -3
  167. package/decorators/CreateRequestContext.js +0 -32
  168. package/decorators/Embeddable.d.ts +0 -8
  169. package/decorators/Embeddable.js +0 -11
  170. package/decorators/Embedded.d.ts +0 -18
  171. package/decorators/Embedded.js +0 -18
  172. package/decorators/Entity.d.ts +0 -18
  173. package/decorators/Entity.js +0 -12
  174. package/decorators/Enum.d.ts +0 -9
  175. package/decorators/Enum.js +0 -16
  176. package/decorators/Filter.d.ts +0 -2
  177. package/decorators/Filter.js +0 -8
  178. package/decorators/Formula.d.ts +0 -4
  179. package/decorators/Formula.js +0 -15
  180. package/decorators/Indexed.d.ts +0 -19
  181. package/decorators/Indexed.js +0 -20
  182. package/decorators/ManyToMany.d.ts +0 -40
  183. package/decorators/ManyToMany.js +0 -14
  184. package/decorators/ManyToOne.d.ts +0 -30
  185. package/decorators/ManyToOne.js +0 -14
  186. package/decorators/OneToMany.d.ts +0 -28
  187. package/decorators/OneToMany.js +0 -17
  188. package/decorators/OneToOne.d.ts +0 -24
  189. package/decorators/OneToOne.js +0 -7
  190. package/decorators/PrimaryKey.d.ts +0 -8
  191. package/decorators/PrimaryKey.js +0 -20
  192. package/decorators/Property.d.ts +0 -250
  193. package/decorators/Property.js +0 -32
  194. package/decorators/Transactional.d.ts +0 -13
  195. package/decorators/Transactional.js +0 -28
  196. package/decorators/hooks.d.ts +0 -16
  197. package/decorators/hooks.js +0 -47
  198. package/decorators/index.d.ts +0 -17
  199. package/decorators/index.js +0 -17
  200. package/entity/ArrayCollection.d.ts +0 -116
  201. package/entity/ArrayCollection.js +0 -402
  202. package/entity/EntityValidator.d.ts +0 -19
  203. package/entity/EntityValidator.js +0 -150
  204. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  205. package/metadata/ReflectMetadataProvider.js +0 -44
  206. package/utils/resolveContextProvider.d.ts +0 -10
  207. package/utils/resolveContextProvider.js +0 -28
@@ -1,9 +1,10 @@
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
+ /** @internal */
7
8
  export class QueryHelper {
8
9
  static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
9
10
  static processParams(params) {
@@ -28,16 +29,57 @@ export class QueryHelper {
28
29
  return params;
29
30
  }
30
31
  static processObjectParams(params = {}) {
31
- Utils.keys(params).forEach(k => {
32
+ Utils.getObjectQueryKeys(params).forEach(k => {
32
33
  params[k] = QueryHelper.processParams(params[k]);
33
34
  });
34
35
  return params;
35
36
  }
37
+ /**
38
+ * converts `{ account: { $or: [ [Object], [Object] ] } }`
39
+ * to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
40
+ */
41
+ static liftGroupOperators(where, meta, metadata, key) {
42
+ if (!Utils.isPlainObject(where)) {
43
+ return undefined;
44
+ }
45
+ const keys = Object.keys(where);
46
+ const groupOperator = keys.find(k => {
47
+ return k in GroupOperator && 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
+ });
58
+ });
59
+ if (groupOperator) {
60
+ return groupOperator;
61
+ }
62
+ for (const k of keys) {
63
+ const value = where[k];
64
+ const prop = meta.properties[k];
65
+ if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
66
+ continue;
67
+ }
68
+ const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
69
+ if (op) {
70
+ delete where[k];
71
+ where[op] = value[op].map((v) => {
72
+ return { [k]: v };
73
+ });
74
+ }
75
+ }
76
+ return undefined;
77
+ }
36
78
  static inlinePrimaryKeyObjects(where, meta, metadata, key) {
37
79
  if (Array.isArray(where)) {
38
80
  where.forEach((item, i) => {
39
81
  if (this.inlinePrimaryKeyObjects(item, meta, metadata, key)) {
40
- where[i] = Utils.getPrimaryKeyValues(item, meta.primaryKeys, false);
82
+ where[i] = Utils.getPrimaryKeyValues(item, meta, false);
41
83
  }
42
84
  });
43
85
  }
@@ -47,7 +89,7 @@ export class QueryHelper {
47
89
  if (meta.primaryKeys.every(pk => pk in where) && Utils.getObjectKeysSize(where) === meta.primaryKeys.length) {
48
90
  return !!key && !GroupOperator[key] && key !== '$not' && Object.keys(where).every(k => !Utils.isPlainObject(where[k]) || Object.keys(where[k]).every(v => {
49
91
  if (Utils.isOperator(v, false)) {
50
- return false;
92
+ return true;
51
93
  }
52
94
  if (meta.properties[k].primary && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[k].kind)) {
53
95
  return this.inlinePrimaryKeyObjects(where[k], meta.properties[k].targetMeta, metadata, v);
@@ -56,9 +98,9 @@ export class QueryHelper {
56
98
  }));
57
99
  }
58
100
  Object.keys(where).forEach(k => {
59
- const meta2 = metadata.find(meta.properties[k]?.type) || meta;
101
+ const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
60
102
  if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
61
- where[k] = Utils.getPrimaryKeyValues(where[k], meta2.primaryKeys, true);
103
+ where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
62
104
  }
63
105
  });
64
106
  return false;
@@ -69,13 +111,14 @@ export class QueryHelper {
69
111
  const meta = metadata.find(entityName);
70
112
  // inline PK-only objects in M:N queries, so we don't join the target entity when not needed
71
113
  if (meta && root) {
114
+ QueryHelper.liftGroupOperators(where, meta, metadata);
72
115
  QueryHelper.inlinePrimaryKeyObjects(where, meta, metadata);
73
116
  }
74
117
  if (platform.getConfig().get('ignoreUndefinedInQuery') && where && typeof where === 'object') {
75
118
  Utils.dropUndefinedProperties(where);
76
119
  }
77
120
  where = QueryHelper.processParams(where) ?? {};
78
- /* v8 ignore next 3 */
121
+ /* v8 ignore next */
79
122
  if (!root && Utils.isPrimaryKey(where)) {
80
123
  return where;
81
124
  }
@@ -83,7 +126,7 @@ export class QueryHelper {
83
126
  where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
84
127
  }
85
128
  if (Array.isArray(where) && root) {
86
- const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
129
+ const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
87
130
  let cond = { [rootPrimaryKey]: { $in: where } };
88
131
  // @ts-ignore
89
132
  // detect tuple comparison, use `$or` in case the number of constituents don't match
@@ -95,12 +138,10 @@ export class QueryHelper {
95
138
  if (!Utils.isPlainObject(where)) {
96
139
  return where;
97
140
  }
98
- return Object.keys(where).reduce((o, key) => {
141
+ return Utils.getObjectQueryKeys(where).reduce((o, key) => {
99
142
  let value = where[key];
100
- const prop = this.findProperty(key, options);
101
- const keys = prop?.joinColumns?.length ?? 0;
102
- const composite = keys > 1;
103
- if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
143
+ const customExpression = Raw.isKnownFragmentSymbol(key);
144
+ if (Array.isArray(value) && value.length === 0 && customExpression) {
104
145
  o[key] = value;
105
146
  return o;
106
147
  }
@@ -114,14 +155,17 @@ export class QueryHelper {
114
155
  o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
115
156
  return o;
116
157
  }
158
+ const prop = customExpression ? null : this.findProperty(key, options);
159
+ const keys = prop?.joinColumns?.length ?? 0;
160
+ const composite = keys > 1;
117
161
  if (prop?.customType && convertCustomTypes && !isRaw(value)) {
118
162
  value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
119
163
  }
120
164
  const isJsonProperty = prop?.customType instanceof JsonType && Utils.isPlainObject(value) && !isRaw(value) && Object.keys(value)[0] !== '$eq';
121
- if (isJsonProperty) {
165
+ if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
122
166
  return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
123
167
  }
124
- if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !key.includes('?') && options.type !== 'orderBy') {
168
+ if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
125
169
  // comparing single composite key - use $eq instead of $in
126
170
  const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
127
171
  o[key] = { [op]: value };
@@ -131,7 +175,7 @@ export class QueryHelper {
131
175
  o[key] = QueryHelper.processWhere({
132
176
  ...options,
133
177
  where: value,
134
- entityName: prop?.type ?? entityName,
178
+ entityName: prop?.targetMeta?.class ?? entityName,
135
179
  root: false,
136
180
  });
137
181
  }
@@ -141,7 +185,7 @@ export class QueryHelper {
141
185
  return o;
142
186
  }, {});
143
187
  }
144
- static getActiveFilters(entityName, options, filters) {
188
+ static getActiveFilters(meta, options, filters) {
145
189
  if (options === false) {
146
190
  return [];
147
191
  }
@@ -153,14 +197,32 @@ export class QueryHelper {
153
197
  Object.keys(options).forEach(filter => opts[filter] = options[filter]);
154
198
  }
155
199
  return Object.keys(filters)
156
- .filter(f => QueryHelper.isFilterActive(entityName, f, filters[f], opts))
200
+ .filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
157
201
  .map(f => {
158
202
  filters[f].name = f;
159
203
  return filters[f];
160
204
  });
161
205
  }
162
- static isFilterActive(entityName, filterName, filter, options) {
163
- if (filter.entity && !filter.entity.includes(entityName)) {
206
+ static mergePropertyFilters(propFilters, options) {
207
+ if (!options || !propFilters || options === true || propFilters === true) {
208
+ return options ?? propFilters;
209
+ }
210
+ if (Array.isArray(propFilters)) {
211
+ propFilters = propFilters.reduce((o, item) => {
212
+ o[item] = true;
213
+ return o;
214
+ }, {});
215
+ }
216
+ if (Array.isArray(options)) {
217
+ options = options.reduce((o, item) => {
218
+ o[item] = true;
219
+ return o;
220
+ }, {});
221
+ }
222
+ return Utils.mergeConfig({}, propFilters, options);
223
+ }
224
+ static isFilterActive(meta, filterName, filter, options) {
225
+ if (filter.entity && !filter.entity.includes(meta.className)) {
164
226
  return false;
165
227
  }
166
228
  if (options[filterName] === false) {
@@ -170,8 +232,8 @@ export class QueryHelper {
170
232
  }
171
233
  static processCustomType(prop, cond, platform, key, fromQuery) {
172
234
  if (Utils.isPlainObject(cond)) {
173
- return Utils.keys(cond).reduce((o, k) => {
174
- if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
235
+ return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
236
+ if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
175
237
  o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
176
238
  }
177
239
  else {
@@ -180,12 +242,12 @@ export class QueryHelper {
180
242
  return o;
181
243
  }, {});
182
244
  }
183
- if (key && Utils.isJsonKeyOperator(key)) {
245
+ if (key && JSON_KEY_OPERATORS.includes(key)) {
184
246
  return Array.isArray(cond)
185
247
  ? platform.marshallArray(cond)
186
248
  : cond;
187
249
  }
188
- if (Array.isArray(cond) && !(key && Utils.isArrayOperator(key))) {
250
+ if (Array.isArray(cond) && !(key && ARRAY_OPERATORS.includes(key))) {
189
251
  return cond.map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery));
190
252
  }
191
253
  if (isRaw(cond)) {
@@ -1,34 +1,23 @@
1
- import { inspect } from 'node:util';
2
1
  import type { AnyString, Dictionary, EntityKey } from '../typings.js';
2
+ declare const rawFragmentSymbolBrand: unique symbol;
3
+ export type RawQueryFragmentSymbol = symbol & {
4
+ readonly [rawFragmentSymbolBrand]: true;
5
+ };
3
6
  export declare class RawQueryFragment {
4
7
  #private;
5
8
  readonly sql: string;
6
9
  readonly params: unknown[];
7
- static cloneRegistry?: Set<string>;
8
10
  constructor(sql: string, params?: unknown[]);
11
+ get key(): RawQueryFragmentSymbol;
9
12
  as(alias: string): RawQueryFragment;
10
- valueOf(): string;
13
+ [Symbol.toPrimitive](hint: 'string'): RawQueryFragmentSymbol;
14
+ get [Symbol.toStringTag](): string;
11
15
  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
- };
16
+ clone(): this;
17
+ static isKnownFragmentSymbol(key: unknown): key is RawQueryFragmentSymbol;
18
+ static hasObjectFragments(object: unknown): boolean;
19
+ static isKnownFragment(key: unknown): key is RawQueryFragment | symbol;
20
+ static getKnownFragment(key: unknown): RawQueryFragment | undefined;
32
21
  }
33
22
  export { RawQueryFragment as Raw };
34
23
  export declare function isRaw(value: unknown): value is RawQueryFragment;
@@ -72,8 +61,26 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
72
61
  * ```ts
73
62
  * @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
74
63
  * ```
64
+ *
65
+ * The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
66
+ *
67
+ * ```ts
68
+ * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
69
+ * // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
70
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
71
+ * @Entity({ schema: 'library' })
72
+ * export class Author { ... }
73
+ * ```
74
+ *
75
+ * You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
76
+ *
77
+ * ```ts
78
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
79
+ * @Entity({ schema: 'library' })
80
+ * export class Author { ... }
81
+ * ```
75
82
  */
76
- 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;
83
+ 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>): NoInfer<R>;
77
84
  /**
78
85
  * Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
79
86
  *
@@ -90,9 +97,25 @@ export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> |
90
97
  */
91
98
  export declare function sql(sql: readonly string[], ...values: unknown[]): any;
92
99
  export declare namespace sql {
93
- var ref: <T extends object>(...keys: string[]) => RawQueryFragment;
100
+ var ref: <T extends object>(...keys: string[]) => NoInfer<RawQueryFragment>;
94
101
  var now: (length?: number) => string;
95
102
  var lower: <T extends object>(key: string | ((alias: string) => string)) => string;
96
103
  var upper: <T extends object>(key: string | ((alias: string) => string)) => string;
97
104
  }
98
105
  export declare function createSqlFunction<T extends object, R = string>(func: string, key: string | ((alias: string) => string)): R;
106
+ /**
107
+ * Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
108
+ *
109
+ * Within the template literal on which the tag function is applied, all placeholders are considered to be database identifiers, and will thus be quoted as so according to the database in use.
110
+ *
111
+ * ```ts
112
+ * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
113
+ * // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
114
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
115
+ * @Entity({ schema: 'library' })
116
+ * export class Author { ... }
117
+ * ```
118
+ */
119
+ export declare function quote(expParts: readonly string[], ...values: (string | {
120
+ toString(): string;
121
+ })[]): any;
@@ -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
+ // holds a weak reference to fragments used as object keys
6
+ static #rawQueryReferences = new WeakMap();
13
7
  #key;
14
8
  constructor(sql, params = []) {
15
9
  this.sql = sql;
16
10
  this.params = params;
17
- this.#key = `[raw]: ${this.sql} (#${RawQueryFragment.#index++})`;
18
11
  }
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);
12
+ get key() {
13
+ if (!this.#key) {
14
+ this.#key = Symbol(this.toJSON());
15
+ RawQueryFragment.#rawQueryReferences.set(this.#key, this);
24
16
  }
17
+ return this.#key;
18
+ }
19
+ as(alias) {
25
20
  return new RawQueryFragment(`${this.sql} as ??`, [...this.params, alias]);
26
21
  }
27
- valueOf() {
22
+ [Symbol.toPrimitive](hint) {
23
+ // if a fragment is converted to string (used as an object key), return a unique symbol
24
+ // and save a weak reference to map so we can retrieve it when compiling the query
25
+ if (hint === 'string') {
26
+ return this.key;
27
+ }
28
28
  throw new Error(`Trying to modify raw SQL fragment: '${this.sql}'`);
29
29
  }
30
+ get [Symbol.toStringTag]() {
31
+ return this.toJSON();
32
+ }
30
33
  toJSON() {
31
- return this.#key;
34
+ return `raw('${this.sql}')`;
32
35
  }
33
- toString() {
34
- RawQueryFragment.#rawQueryCache.set(this.#key, this);
35
- this.#used++;
36
- return this.#key;
36
+ clone() {
37
+ return this;
37
38
  }
38
- /** @internal */
39
- assign() {
40
- if (this.#assigned) {
41
- throw new Error(`Cannot reassign already used RawQueryFragment: '${this.sql}'`);
42
- }
43
- this.#assigned = true;
39
+ static isKnownFragmentSymbol(key) {
40
+ return typeof key === 'symbol' && this.#rawQueryReferences.has(key);
44
41
  }
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;
42
+ static hasObjectFragments(object) {
43
+ return Utils.isPlainObject(object) && 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
  }
@@ -147,6 +113,24 @@ export const ALIAS_REPLACEMENT_RE = '\\[::alias::\\]';
147
113
  * ```ts
148
114
  * @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
149
115
  * ```
116
+ *
117
+ * The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
118
+ *
119
+ * ```ts
120
+ * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
121
+ * // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
122
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
123
+ * @Entity({ schema: 'library' })
124
+ * export class Author { ... }
125
+ * ```
126
+ *
127
+ * You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
128
+ *
129
+ * ```ts
130
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
131
+ * @Entity({ schema: 'library' })
132
+ * export class Author { ... }
133
+ * ```
150
134
  */
151
135
  export function raw(sql, params) {
152
136
  if (sql instanceof RawQueryFragment) {
@@ -189,11 +173,7 @@ export function raw(sql, params) {
189
173
  * ```
190
174
  */
191
175
  export function sql(sql, ...values) {
192
- return raw(sql.reduce((query, queryPart, i) => {
193
- const valueExists = i < values.length;
194
- const text = query + queryPart;
195
- return valueExists ? text + '?' : text;
196
- }, ''), values);
176
+ return raw(sql.join('?'), values);
197
177
  }
198
178
  export function createSqlFunction(func, key) {
199
179
  if (typeof key === 'string') {
@@ -205,3 +185,19 @@ sql.ref = (...keys) => raw('??', [keys.join('.')]);
205
185
  sql.now = (length) => raw('current_timestamp' + (length == null ? '' : `(${length})`));
206
186
  sql.lower = (key) => createSqlFunction('lower', key);
207
187
  sql.upper = (key) => createSqlFunction('upper', key);
188
+ /**
189
+ * Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
190
+ *
191
+ * Within the template literal on which the tag function is applied, all placeholders are considered to be database identifiers, and will thus be quoted as so according to the database in use.
192
+ *
193
+ * ```ts
194
+ * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
195
+ * // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
196
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
197
+ * @Entity({ schema: 'library' })
198
+ * export class Author { ... }
199
+ * ```
200
+ */
201
+ export function quote(expParts, ...values) {
202
+ return raw(expParts.join('??'), values);
203
+ }
@@ -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;
@@ -0,0 +1,65 @@
1
+ import type { EntityManager } from '../EntityManager.js';
2
+ import { type TransactionOptions } from '../enums.js';
3
+ /**
4
+ * Manages transaction lifecycle and propagation for EntityManager.
5
+ */
6
+ export declare class TransactionManager {
7
+ private readonly em;
8
+ constructor(em: EntityManager);
9
+ /**
10
+ * Main entry point for handling transactional operations with propagation support.
11
+ */
12
+ handle<T>(cb: (em: EntityManager) => T | Promise<T>, options?: TransactionOptions): Promise<T>;
13
+ /**
14
+ * Executes the callback with the specified propagation type.
15
+ */
16
+ private executeWithPropagation;
17
+ /**
18
+ * Suspends the current transaction and returns the suspended resources.
19
+ */
20
+ private suspendTransaction;
21
+ /**
22
+ * Resumes a previously suspended transaction.
23
+ */
24
+ private resumeTransaction;
25
+ /**
26
+ * Executes operation without transaction context.
27
+ */
28
+ private executeWithoutTransaction;
29
+ /**
30
+ * Creates new independent transaction, suspending any existing one.
31
+ */
32
+ private executeWithNewTransaction;
33
+ /**
34
+ * Creates new transaction context.
35
+ */
36
+ private createNewTransaction;
37
+ /**
38
+ * Executes nested transaction with savepoint.
39
+ */
40
+ private executeNestedTransaction;
41
+ /**
42
+ * Creates a fork of the EntityManager with the given options.
43
+ */
44
+ private createFork;
45
+ /**
46
+ * Determines if changes should be propagated to the upper context.
47
+ */
48
+ private shouldPropagateToUpperContext;
49
+ /**
50
+ * Merges entities from fork to parent EntityManager.
51
+ */
52
+ private mergeEntitiesToParent;
53
+ /**
54
+ * Registers a deletion handler to unset entity identities after flush.
55
+ */
56
+ private registerDeletionHandler;
57
+ /**
58
+ * Processes transaction execution.
59
+ */
60
+ private processTransaction;
61
+ /**
62
+ * Executes transaction workflow with entity synchronization.
63
+ */
64
+ private executeTransactionFlow;
65
+ }