@mikro-orm/core 7.0.0-dev.4 → 7.0.0-dev.41
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.
- package/EntityManager.d.ts +84 -18
- package/EntityManager.js +265 -172
- package/MikroORM.d.ts +7 -5
- package/MikroORM.js +0 -1
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +2 -1
- package/cache/FileCacheAdapter.js +6 -4
- package/connections/Connection.d.ts +4 -2
- package/connections/Connection.js +2 -2
- package/decorators/Check.d.ts +2 -2
- package/decorators/Embeddable.d.ts +5 -5
- package/decorators/Embeddable.js +1 -1
- package/decorators/Embedded.d.ts +6 -12
- package/decorators/Entity.d.ts +18 -3
- package/decorators/Enum.d.ts +1 -1
- package/decorators/Formula.d.ts +1 -2
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/ManyToMany.d.ts +4 -2
- package/decorators/ManyToOne.d.ts +6 -2
- package/decorators/OneToMany.d.ts +4 -4
- package/decorators/OneToOne.d.ts +5 -1
- package/decorators/PrimaryKey.d.ts +2 -3
- package/decorators/Property.d.ts +54 -4
- package/decorators/Transactional.d.ts +1 -0
- package/decorators/Transactional.js +3 -3
- package/decorators/index.d.ts +1 -1
- package/drivers/DatabaseDriver.d.ts +4 -3
- package/drivers/IDatabaseDriver.d.ts +22 -2
- package/entity/ArrayCollection.d.ts +4 -2
- package/entity/ArrayCollection.js +18 -6
- package/entity/Collection.d.ts +1 -2
- package/entity/Collection.js +19 -10
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +9 -1
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +29 -9
- package/entity/EntityHelper.js +25 -3
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +74 -37
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityValidator.js +1 -1
- package/entity/Reference.d.ts +9 -7
- package/entity/Reference.js +30 -3
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +561 -0
- package/entity/defineEntity.js +537 -0
- package/entity/index.d.ts +2 -0
- package/entity/index.js +2 -0
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +16 -3
- package/enums.js +13 -0
- package/errors.d.ts +6 -0
- package/errors.js +14 -0
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +35 -24
- package/index.d.ts +2 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/SimpleLogger.d.ts +1 -1
- package/metadata/EntitySchema.d.ts +8 -4
- package/metadata/EntitySchema.js +39 -19
- package/metadata/MetadataDiscovery.d.ts +1 -1
- package/metadata/MetadataDiscovery.js +88 -32
- package/metadata/MetadataStorage.js +1 -1
- package/metadata/MetadataValidator.js +4 -3
- package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
- package/naming-strategy/AbstractNamingStrategy.js +7 -1
- package/naming-strategy/NamingStrategy.d.ts +11 -1
- package/package.json +5 -5
- package/platforms/Platform.d.ts +5 -3
- package/platforms/Platform.js +4 -8
- package/serialization/EntitySerializer.d.ts +2 -0
- package/serialization/EntitySerializer.js +2 -2
- package/serialization/EntityTransformer.js +1 -1
- package/serialization/SerializationContext.js +14 -11
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +3 -0
- package/types/BooleanType.d.ts +1 -1
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +1 -1
- package/types/DoubleType.js +1 -1
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/Type.d.ts +2 -1
- package/types/Type.js +1 -1
- package/types/index.d.ts +1 -1
- package/typings.d.ts +88 -39
- package/typings.js +24 -4
- package/unit-of-work/ChangeSetComputer.js +3 -1
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +37 -16
- package/unit-of-work/UnitOfWork.d.ts +8 -1
- package/unit-of-work/UnitOfWork.js +109 -41
- package/utils/Configuration.d.ts +23 -5
- package/utils/Configuration.js +17 -3
- package/utils/ConfigurationLoader.d.ts +0 -2
- package/utils/ConfigurationLoader.js +2 -24
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +7 -2
- package/utils/DataloaderUtils.js +38 -7
- package/utils/EntityComparator.d.ts +6 -2
- package/utils/EntityComparator.js +104 -58
- package/utils/QueryHelper.d.ts +9 -1
- package/utils/QueryHelper.js +66 -5
- package/utils/RawQueryFragment.d.ts +36 -2
- package/utils/RawQueryFragment.js +35 -1
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +218 -0
- package/utils/Utils.d.ts +11 -5
- package/utils/Utils.js +76 -33
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/upsert-utils.d.ts +7 -2
- package/utils/upsert-utils.js +52 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { TransactionPropagation } from '../enums.js';
|
|
2
|
+
import { TransactionEventBroadcaster } from '../events/TransactionEventBroadcaster.js';
|
|
3
|
+
import { TransactionContext } from '../utils/TransactionContext.js';
|
|
4
|
+
import { ChangeSetType } from '../unit-of-work/ChangeSet.js';
|
|
5
|
+
import { TransactionStateError } from '../errors.js';
|
|
6
|
+
import { helper } from '../entity/wrap.js';
|
|
7
|
+
/**
|
|
8
|
+
* Manages transaction lifecycle and propagation for EntityManager.
|
|
9
|
+
*/
|
|
10
|
+
export class TransactionManager {
|
|
11
|
+
em;
|
|
12
|
+
constructor(em) {
|
|
13
|
+
this.em = em;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Main entry point for handling transactional operations with propagation support.
|
|
17
|
+
*/
|
|
18
|
+
async handle(cb, options = {}) {
|
|
19
|
+
const em = this.em.getContext(false);
|
|
20
|
+
options.propagation ??= TransactionPropagation.NESTED;
|
|
21
|
+
options.ctx ??= em.getTransactionContext();
|
|
22
|
+
const hasExistingTransaction = !!em.getTransactionContext();
|
|
23
|
+
return this.executeWithPropagation(options.propagation, em, cb, options, hasExistingTransaction);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Executes the callback with the specified propagation type.
|
|
27
|
+
*/
|
|
28
|
+
async executeWithPropagation(propagation, em, cb, options, hasExistingTransaction) {
|
|
29
|
+
switch (propagation) {
|
|
30
|
+
case TransactionPropagation.NOT_SUPPORTED:
|
|
31
|
+
return this.executeWithoutTransaction(em, cb, options);
|
|
32
|
+
case TransactionPropagation.REQUIRES_NEW:
|
|
33
|
+
return this.executeWithNewTransaction(em, cb, options, hasExistingTransaction);
|
|
34
|
+
case TransactionPropagation.REQUIRED:
|
|
35
|
+
if (hasExistingTransaction) {
|
|
36
|
+
return cb(em);
|
|
37
|
+
}
|
|
38
|
+
return this.createNewTransaction(em, cb, options);
|
|
39
|
+
case TransactionPropagation.NESTED:
|
|
40
|
+
if (hasExistingTransaction) {
|
|
41
|
+
return this.executeNestedTransaction(em, cb, options);
|
|
42
|
+
}
|
|
43
|
+
return this.createNewTransaction(em, cb, options);
|
|
44
|
+
case TransactionPropagation.SUPPORTS:
|
|
45
|
+
if (hasExistingTransaction) {
|
|
46
|
+
return cb(em);
|
|
47
|
+
}
|
|
48
|
+
return this.executeWithoutTransaction(em, cb, options);
|
|
49
|
+
case TransactionPropagation.MANDATORY:
|
|
50
|
+
if (!hasExistingTransaction) {
|
|
51
|
+
throw TransactionStateError.requiredTransactionNotFound(propagation);
|
|
52
|
+
}
|
|
53
|
+
return cb(em);
|
|
54
|
+
case TransactionPropagation.NEVER:
|
|
55
|
+
if (hasExistingTransaction) {
|
|
56
|
+
throw TransactionStateError.transactionNotAllowed(propagation);
|
|
57
|
+
}
|
|
58
|
+
return this.executeWithoutTransaction(em, cb, options);
|
|
59
|
+
default:
|
|
60
|
+
throw TransactionStateError.invalidPropagation(propagation);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Suspends the current transaction and returns the suspended resources.
|
|
65
|
+
*/
|
|
66
|
+
suspendTransaction(em) {
|
|
67
|
+
const suspended = em.getTransactionContext();
|
|
68
|
+
em.resetTransactionContext();
|
|
69
|
+
return suspended;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resumes a previously suspended transaction.
|
|
73
|
+
*/
|
|
74
|
+
resumeTransaction(em, suspended) {
|
|
75
|
+
if (suspended != null) {
|
|
76
|
+
em.setTransactionContext(suspended);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Executes operation without transaction context.
|
|
81
|
+
*/
|
|
82
|
+
async executeWithoutTransaction(em, cb, options) {
|
|
83
|
+
const suspended = this.suspendTransaction(em);
|
|
84
|
+
const fork = this.createFork(em, { ...options, disableTransactions: true });
|
|
85
|
+
const propagateToUpperContext = this.shouldPropagateToUpperContext(em);
|
|
86
|
+
try {
|
|
87
|
+
return await this.executeTransactionFlow(fork, cb, propagateToUpperContext, em);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
this.resumeTransaction(em, suspended);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Creates new independent transaction, suspending any existing one.
|
|
95
|
+
*/
|
|
96
|
+
async executeWithNewTransaction(em, cb, options, hasExistingTransaction) {
|
|
97
|
+
const fork = this.createFork(em, options);
|
|
98
|
+
let suspended = null;
|
|
99
|
+
// Suspend existing transaction if present
|
|
100
|
+
if (hasExistingTransaction) {
|
|
101
|
+
suspended = this.suspendTransaction(em);
|
|
102
|
+
}
|
|
103
|
+
const newOptions = { ...options, ctx: undefined };
|
|
104
|
+
try {
|
|
105
|
+
return await this.processTransaction(em, fork, cb, newOptions);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
if (suspended != null) {
|
|
109
|
+
this.resumeTransaction(em, suspended);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Creates new transaction context.
|
|
115
|
+
*/
|
|
116
|
+
async createNewTransaction(em, cb, options) {
|
|
117
|
+
const fork = this.createFork(em, options);
|
|
118
|
+
return this.processTransaction(em, fork, cb, options);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Executes nested transaction with savepoint.
|
|
122
|
+
*/
|
|
123
|
+
async executeNestedTransaction(em, cb, options) {
|
|
124
|
+
const fork = this.createFork(em, options);
|
|
125
|
+
// Pass existing context to create savepoint
|
|
126
|
+
const nestedOptions = { ...options, ctx: em.getTransactionContext() };
|
|
127
|
+
return this.processTransaction(em, fork, cb, nestedOptions);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Creates a fork of the EntityManager with the given options.
|
|
131
|
+
*/
|
|
132
|
+
createFork(em, options) {
|
|
133
|
+
return em.fork({
|
|
134
|
+
clear: options.clear ?? false,
|
|
135
|
+
flushMode: options.flushMode,
|
|
136
|
+
cloneEventManager: true,
|
|
137
|
+
disableTransactions: options.ignoreNestedTransactions,
|
|
138
|
+
loggerContext: options.loggerContext,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Determines if changes should be propagated to the upper context.
|
|
143
|
+
*/
|
|
144
|
+
shouldPropagateToUpperContext(em) {
|
|
145
|
+
return !em.global || this.em.config.get('allowGlobalContext');
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Merges entities from fork to parent EntityManager.
|
|
149
|
+
*/
|
|
150
|
+
mergeEntitiesToParent(fork, parent) {
|
|
151
|
+
const parentUoW = parent.getUnitOfWork(false);
|
|
152
|
+
// perf: if parent is empty, we can just move all entities from the fork to skip the `em.merge` overhead
|
|
153
|
+
if (parentUoW.getIdentityMap().keys().length === 0) {
|
|
154
|
+
for (const entity of fork.getUnitOfWork(false).getIdentityMap()) {
|
|
155
|
+
parentUoW.getIdentityMap().store(entity);
|
|
156
|
+
helper(entity).__em = parent;
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
for (const entity of fork.getUnitOfWork(false).getIdentityMap()) {
|
|
161
|
+
const wrapped = helper(entity);
|
|
162
|
+
const meta = wrapped.__meta;
|
|
163
|
+
// eslint-disable-next-line dot-notation
|
|
164
|
+
const parentEntity = parentUoW.getById(meta.className, wrapped.getPrimaryKey(), parent['_schema'], true);
|
|
165
|
+
if (parentEntity && parentEntity !== entity) {
|
|
166
|
+
const parentWrapped = helper(parentEntity);
|
|
167
|
+
parentWrapped.__data = helper(entity).__data;
|
|
168
|
+
parentWrapped.__originalEntityData = helper(entity).__originalEntityData;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
parentUoW.merge(entity, new Set([entity]));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Registers a deletion handler to unset entity identities after flush.
|
|
177
|
+
*/
|
|
178
|
+
registerDeletionHandler(fork, parent) {
|
|
179
|
+
fork.getEventManager().registerSubscriber({
|
|
180
|
+
afterFlush: (args) => {
|
|
181
|
+
const deletionChangeSets = args.uow.getChangeSets()
|
|
182
|
+
.filter(cs => cs.type === ChangeSetType.DELETE || cs.type === ChangeSetType.DELETE_EARLY);
|
|
183
|
+
for (const cs of deletionChangeSets) {
|
|
184
|
+
parent.getUnitOfWork(false).unsetIdentity(cs.entity);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Processes transaction execution.
|
|
191
|
+
*/
|
|
192
|
+
async processTransaction(em, fork, cb, options) {
|
|
193
|
+
const propagateToUpperContext = this.shouldPropagateToUpperContext(em);
|
|
194
|
+
const eventBroadcaster = new TransactionEventBroadcaster(fork, undefined);
|
|
195
|
+
return TransactionContext.create(fork, () => fork.getConnection().transactional(async (trx) => {
|
|
196
|
+
fork.setTransactionContext(trx);
|
|
197
|
+
return this.executeTransactionFlow(fork, cb, propagateToUpperContext, em);
|
|
198
|
+
}, { ...options, eventBroadcaster }));
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Executes transaction workflow with entity synchronization.
|
|
202
|
+
*/
|
|
203
|
+
async executeTransactionFlow(fork, cb, propagateToUpperContext, parentEm) {
|
|
204
|
+
if (!propagateToUpperContext) {
|
|
205
|
+
const ret = await cb(fork);
|
|
206
|
+
await fork.flush();
|
|
207
|
+
return ret;
|
|
208
|
+
}
|
|
209
|
+
// Setup: Register deletion handler before execution
|
|
210
|
+
this.registerDeletionHandler(fork, parentEm);
|
|
211
|
+
// Execute callback and flush
|
|
212
|
+
const ret = await cb(fork);
|
|
213
|
+
await fork.flush();
|
|
214
|
+
// Synchronization: Merge entities back to the parent
|
|
215
|
+
this.mergeEntitiesToParent(fork, parentEm);
|
|
216
|
+
return ret;
|
|
217
|
+
}
|
|
218
|
+
}
|
package/utils/Utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type GlobOptions } from 'tinyglobby';
|
|
2
2
|
import type { Dictionary, EntityData, EntityDictionary, EntityKey, EntityMetadata, EntityName, EntityProperty, IMetadataStorage, Primary } from '../typings.js';
|
|
3
3
|
import type { Collection } from '../entity/Collection.js';
|
|
4
4
|
import type { Platform } from '../platforms/Platform.js';
|
|
@@ -134,14 +134,14 @@ export declare class Utils {
|
|
|
134
134
|
static getCompositeKeyHash<T>(data: EntityData<T>, meta: EntityMetadata<T>, convertCustomTypes?: boolean, platform?: Platform, flat?: boolean): string;
|
|
135
135
|
static getPrimaryKeyHash(pks: (string | Buffer | Date)[]): string;
|
|
136
136
|
static splitPrimaryKeys<T extends object>(key: string): EntityKey<T>[];
|
|
137
|
-
static getPrimaryKeyValues<T>(entity: T, primaryKeys: string[]
|
|
137
|
+
static getPrimaryKeyValues<T>(entity: T, primaryKeys: string[] | EntityMetadata<T>, allowScalar?: boolean, convertCustomTypes?: boolean): any;
|
|
138
138
|
static getPrimaryKeyCond<T>(entity: T, primaryKeys: EntityKey<T>[]): Record<string, Primary<T>> | null;
|
|
139
139
|
/**
|
|
140
140
|
* Maps nested FKs from `[1, 2, 3]` to `[1, [2, 3]]`.
|
|
141
141
|
*/
|
|
142
142
|
static mapFlatCompositePrimaryKey(fk: Primary<any>[], prop: EntityProperty, fieldNames?: string[], idx?: number): Primary<any> | Primary<any>[];
|
|
143
143
|
static getPrimaryKeyCondFromArray<T extends object>(pks: Primary<T>[], meta: EntityMetadata<T>): Record<string, Primary<T>>;
|
|
144
|
-
static getOrderedPrimaryKeys<T>(id: Primary<T> | Record<string, Primary<T>>, meta: EntityMetadata<T>, platform?: Platform, convertCustomTypes?: boolean): Primary<T>[];
|
|
144
|
+
static getOrderedPrimaryKeys<T>(id: Primary<T> | Record<string, Primary<T>>, meta: EntityMetadata<T>, platform?: Platform, convertCustomTypes?: boolean, allowScalar?: boolean): Primary<T>[];
|
|
145
145
|
/**
|
|
146
146
|
* Checks whether given object is an entity instance.
|
|
147
147
|
*/
|
|
@@ -206,13 +206,13 @@ export declare class Utils {
|
|
|
206
206
|
* If either `path` or `baseDir` are `file:` URLs, they are converted to local paths.
|
|
207
207
|
*/
|
|
208
208
|
static absolutePath(path: string, baseDir?: string): string;
|
|
209
|
-
static hash(data: string, length?: number): string;
|
|
209
|
+
static hash(data: string, length?: number, algorithm?: 'md5' | 'sha256'): string;
|
|
210
210
|
static runIfNotEmpty(clause: () => any, data: any): void;
|
|
211
211
|
static defaultValue<T extends Dictionary>(prop: T, option: keyof T, defaultValue: any): void;
|
|
212
212
|
static findDuplicates<T>(items: T[]): T[];
|
|
213
213
|
static removeDuplicates<T>(items: T[]): T[];
|
|
214
214
|
static randomInt(min: number, max: number): number;
|
|
215
|
-
static pathExists(path: string, options?:
|
|
215
|
+
static pathExists(path: string, options?: GlobOptions): Promise<boolean>;
|
|
216
216
|
/**
|
|
217
217
|
* Extracts all possible values of a TS enum. Works with both string and numeric enums.
|
|
218
218
|
*/
|
|
@@ -230,6 +230,12 @@ export declare class Utils {
|
|
|
230
230
|
* @param [from] Location to start the node resolution
|
|
231
231
|
*/
|
|
232
232
|
static requireFrom<T extends Dictionary>(id: string, from?: string): T;
|
|
233
|
+
/**
|
|
234
|
+
* Resolve path to a module.
|
|
235
|
+
* @param id The module to require
|
|
236
|
+
* @param [from] Location to start the node resolution
|
|
237
|
+
*/
|
|
238
|
+
static resolveModulePath(id: string, from?: string): string;
|
|
233
239
|
static dynamicImport<T = any>(id: string): Promise<T>;
|
|
234
240
|
static setDynamicImportProvider(provider: (id: string) => Promise<unknown>): void;
|
|
235
241
|
static ensureDir(path: string): void;
|
package/utils/Utils.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import
|
|
2
|
+
import { glob, isDynamicPattern } from 'tinyglobby';
|
|
3
3
|
import { extname, isAbsolute, join, normalize, relative, resolve } from 'node:path';
|
|
4
|
-
import { platform } from 'node:os';
|
|
5
4
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
5
|
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
7
6
|
import { createHash } from 'node:crypto';
|
|
@@ -435,7 +434,11 @@ export class Utils {
|
|
|
435
434
|
return data;
|
|
436
435
|
}
|
|
437
436
|
if (Utils.isEntity(data, true)) {
|
|
438
|
-
|
|
437
|
+
const wrapped = helper(data);
|
|
438
|
+
if (wrapped.__meta.compositePK) {
|
|
439
|
+
return wrapped.getPrimaryKeys();
|
|
440
|
+
}
|
|
441
|
+
return wrapped.getPrimaryKey();
|
|
439
442
|
}
|
|
440
443
|
if (strict && meta && Utils.getObjectKeysSize(data) !== meta.primaryKeys.length) {
|
|
441
444
|
return null;
|
|
@@ -483,6 +486,7 @@ export class Utils {
|
|
|
483
486
|
static splitPrimaryKeys(key) {
|
|
484
487
|
return key.split(this.PK_SEPARATOR);
|
|
485
488
|
}
|
|
489
|
+
// TODO v7: remove support for `primaryKeys: string[]`
|
|
486
490
|
static getPrimaryKeyValues(entity, primaryKeys, allowScalar = false, convertCustomTypes = false) {
|
|
487
491
|
/* v8 ignore next 3 */
|
|
488
492
|
if (entity == null) {
|
|
@@ -494,9 +498,24 @@ export class Utils {
|
|
|
494
498
|
}
|
|
495
499
|
return val;
|
|
496
500
|
}
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
501
|
+
const meta = Array.isArray(primaryKeys) ? undefined : primaryKeys;
|
|
502
|
+
primaryKeys = Array.isArray(primaryKeys) ? primaryKeys : meta.primaryKeys;
|
|
503
|
+
let pk;
|
|
504
|
+
if (Utils.isEntity(entity, true)) {
|
|
505
|
+
pk = helper(entity).getPrimaryKey(convertCustomTypes);
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
pk = primaryKeys.reduce((o, pk) => {
|
|
509
|
+
const targetMeta = meta?.properties[pk].targetMeta;
|
|
510
|
+
if (targetMeta && Utils.isPlainObject(entity[pk])) {
|
|
511
|
+
o[pk] = Utils.getPrimaryKeyValues(entity[pk], targetMeta, allowScalar, convertCustomTypes);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
o[pk] = entity[pk];
|
|
515
|
+
}
|
|
516
|
+
return o;
|
|
517
|
+
}, {});
|
|
518
|
+
}
|
|
500
519
|
if (primaryKeys.length > 1) {
|
|
501
520
|
return toArray(pk);
|
|
502
521
|
}
|
|
@@ -546,7 +565,7 @@ export class Utils {
|
|
|
546
565
|
return o;
|
|
547
566
|
}, {});
|
|
548
567
|
}
|
|
549
|
-
static getOrderedPrimaryKeys(id, meta, platform, convertCustomTypes = false) {
|
|
568
|
+
static getOrderedPrimaryKeys(id, meta, platform, convertCustomTypes = false, allowScalar = false) {
|
|
550
569
|
const data = (Utils.isPrimaryKey(id) ? { [meta.primaryKeys[0]]: id } : id);
|
|
551
570
|
const pks = meta.primaryKeys.map((pk, idx) => {
|
|
552
571
|
const prop = meta.properties[pk];
|
|
@@ -556,11 +575,14 @@ export class Utils {
|
|
|
556
575
|
value = prop.customType.convertToJSValue(value, platform);
|
|
557
576
|
}
|
|
558
577
|
if (prop.kind !== ReferenceKind.SCALAR && prop.targetMeta) {
|
|
559
|
-
const value2 = this.getOrderedPrimaryKeys(value, prop.targetMeta, platform, convertCustomTypes);
|
|
578
|
+
const value2 = this.getOrderedPrimaryKeys(value, prop.targetMeta, platform, convertCustomTypes, allowScalar);
|
|
560
579
|
value = value2.length > 1 ? value2 : value2[0];
|
|
561
580
|
}
|
|
562
581
|
return value;
|
|
563
582
|
});
|
|
583
|
+
if (allowScalar && pks.length === 1) {
|
|
584
|
+
return pks[0];
|
|
585
|
+
}
|
|
564
586
|
// we need to flatten the PKs as composite PKs can be build from another composite PKs
|
|
565
587
|
// and this method is used to get the PK hash in identity map, that expects flat array
|
|
566
588
|
return Utils.flatten(pks);
|
|
@@ -625,8 +647,12 @@ export class Utils {
|
|
|
625
647
|
|| !!process.env.TS_JEST // check if ts-jest is used (works only with v27.0.4+)
|
|
626
648
|
|| !!process.env.VITEST // check if vitest is used
|
|
627
649
|
|| !!process.versions.bun // check if bun is used
|
|
628
|
-
|| process.argv.slice(1).some(arg => arg.
|
|
629
|
-
|| process.execArgv.some(arg =>
|
|
650
|
+
|| process.argv.slice(1).some(arg => arg.match(/\.([mc]?ts|tsx)$/)) // executing `.ts` file
|
|
651
|
+
|| process.execArgv.some(arg => {
|
|
652
|
+
return arg.includes('ts-node') // check for ts-node loader
|
|
653
|
+
|| arg.includes('@swc-node/register') // check for swc-node/register loader
|
|
654
|
+
|| arg.includes('node_modules/tsx/'); // check for tsx loader
|
|
655
|
+
});
|
|
630
656
|
}
|
|
631
657
|
/**
|
|
632
658
|
* Uses some dark magic to get source path to caller where decorator is used.
|
|
@@ -644,7 +670,8 @@ export class Utils {
|
|
|
644
670
|
// but those are also present in node, so we need to check this only if they weren't found.
|
|
645
671
|
if (line === -1) {
|
|
646
672
|
// here we handle bun which stack is different from nodejs so we search for reflect-metadata
|
|
647
|
-
|
|
673
|
+
// Different bun versions might have different stack traces. The "last index" works for both 1.2.6 and 1.2.7.
|
|
674
|
+
const reflectLine = stack.findLastIndex(line => Utils.normalizePath(line).includes('node_modules/reflect-metadata/Reflect.js'));
|
|
648
675
|
if (reflectLine === -1 || reflectLine + 2 >= stack.length || !stack[reflectLine + 1].includes('bun:wrap')) {
|
|
649
676
|
return name;
|
|
650
677
|
}
|
|
@@ -673,11 +700,11 @@ export class Utils {
|
|
|
673
700
|
return simple;
|
|
674
701
|
}
|
|
675
702
|
const objectType = Object.prototype.toString.call(value);
|
|
676
|
-
const type = objectType.match(
|
|
703
|
+
const type = objectType.match(/^\[object (.+)]$/)[1];
|
|
677
704
|
if (type === 'Uint8Array') {
|
|
678
705
|
return 'Buffer';
|
|
679
706
|
}
|
|
680
|
-
return
|
|
707
|
+
return type;
|
|
681
708
|
}
|
|
682
709
|
/**
|
|
683
710
|
* Checks whether the value is POJO (e.g. `{ foo: 'bar' }`, and not instance of `Foo`)
|
|
@@ -761,8 +788,9 @@ export class Utils {
|
|
|
761
788
|
}
|
|
762
789
|
return Utils.normalizePath(path);
|
|
763
790
|
}
|
|
764
|
-
static hash(data, length) {
|
|
765
|
-
const
|
|
791
|
+
static hash(data, length, algorithm) {
|
|
792
|
+
const hashAlgorithm = algorithm || 'sha256';
|
|
793
|
+
const hash = createHash(hashAlgorithm).update(data).digest('hex');
|
|
766
794
|
if (length) {
|
|
767
795
|
return hash.substring(0, length);
|
|
768
796
|
}
|
|
@@ -795,8 +823,8 @@ export class Utils {
|
|
|
795
823
|
return Math.round(Math.random() * (max - min)) + min;
|
|
796
824
|
}
|
|
797
825
|
static async pathExists(path, options = {}) {
|
|
798
|
-
if (
|
|
799
|
-
const found = await
|
|
826
|
+
if (isDynamicPattern(path)) {
|
|
827
|
+
const found = await glob(path, options);
|
|
800
828
|
return found.length > 0;
|
|
801
829
|
}
|
|
802
830
|
return this.pathExistsSync(path);
|
|
@@ -863,18 +891,25 @@ export class Utils {
|
|
|
863
891
|
}
|
|
864
892
|
return createRequire(resolve(from))(id);
|
|
865
893
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
}
|
|
894
|
+
/**
|
|
895
|
+
* Resolve path to a module.
|
|
896
|
+
* @param id The module to require
|
|
897
|
+
* @param [from] Location to start the node resolution
|
|
898
|
+
*/
|
|
899
|
+
static resolveModulePath(id, from = process.cwd()) {
|
|
900
|
+
if (!extname(from)) {
|
|
901
|
+
from = join(from, '__fake.js');
|
|
875
902
|
}
|
|
903
|
+
const path = Utils.normalizePath(createRequire(resolve(from)).resolve(id));
|
|
904
|
+
const parts = path.split('/');
|
|
905
|
+
const idx = parts.lastIndexOf(id) + 1;
|
|
906
|
+
parts.splice(idx, parts.length - idx);
|
|
907
|
+
return parts.join('/');
|
|
908
|
+
}
|
|
909
|
+
static async dynamicImport(id) {
|
|
876
910
|
/* v8 ignore next */
|
|
877
|
-
|
|
911
|
+
const specifier = id.startsWith('file://') ? id : pathToFileURL(id).href;
|
|
912
|
+
return this.dynamicImportProvider(specifier);
|
|
878
913
|
}
|
|
879
914
|
/* v8 ignore next 3 */
|
|
880
915
|
static setDynamicImportProvider(provider) {
|
|
@@ -899,8 +934,13 @@ export class Utils {
|
|
|
899
934
|
/* v8 ignore next 5 */
|
|
900
935
|
}
|
|
901
936
|
catch {
|
|
902
|
-
|
|
903
|
-
|
|
937
|
+
try {
|
|
938
|
+
// this works in production build where we do not have the `src` folder
|
|
939
|
+
return this.requireFrom('../package.json', import.meta.dirname).version;
|
|
940
|
+
}
|
|
941
|
+
catch {
|
|
942
|
+
return 'N/A';
|
|
943
|
+
}
|
|
904
944
|
}
|
|
905
945
|
}
|
|
906
946
|
static createFunction(context, code) {
|
|
@@ -1044,12 +1084,15 @@ export class Utils {
|
|
|
1044
1084
|
}
|
|
1045
1085
|
static async tryImport({ module, warning }) {
|
|
1046
1086
|
try {
|
|
1047
|
-
return await
|
|
1087
|
+
return await import(module);
|
|
1048
1088
|
}
|
|
1049
1089
|
catch (err) {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1090
|
+
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
1091
|
+
// eslint-disable-next-line no-console
|
|
1092
|
+
console.warn(warning);
|
|
1093
|
+
return undefined;
|
|
1094
|
+
}
|
|
1095
|
+
throw err;
|
|
1053
1096
|
}
|
|
1054
1097
|
}
|
|
1055
1098
|
static stripRelativePath(str) {
|
package/utils/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './DataloaderUtils.js';
|
|
|
5
5
|
export * from './Utils.js';
|
|
6
6
|
export * from './RequestContext.js';
|
|
7
7
|
export * from './TransactionContext.js';
|
|
8
|
+
export * from './TransactionManager.js';
|
|
8
9
|
export * from './QueryHelper.js';
|
|
9
10
|
export * from './NullHighlighter.js';
|
|
10
11
|
export * from './EntityComparator.js';
|
package/utils/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from './DataloaderUtils.js';
|
|
|
5
5
|
export * from './Utils.js';
|
|
6
6
|
export * from './RequestContext.js';
|
|
7
7
|
export * from './TransactionContext.js';
|
|
8
|
+
export * from './TransactionManager.js';
|
|
8
9
|
export * from './QueryHelper.js';
|
|
9
10
|
export * from './NullHighlighter.js';
|
|
10
11
|
export * from './EntityComparator.js';
|
package/utils/upsert-utils.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import type { EntityData, EntityMetadata } from '../typings.js';
|
|
1
|
+
import type { EntityData, EntityMetadata, FilterQuery } from '../typings.js';
|
|
2
2
|
import type { UpsertOptions } from '../drivers/IDatabaseDriver.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type RawQueryFragment } from '../utils/RawQueryFragment.js';
|
|
4
4
|
/** @internal */
|
|
5
5
|
export declare function getOnConflictFields<T>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | RawQueryFragment, options: UpsertOptions<T>): (keyof T)[];
|
|
6
6
|
/** @internal */
|
|
7
7
|
export declare function getOnConflictReturningFields<T, P extends string>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | RawQueryFragment, options: UpsertOptions<T, P>): (keyof T)[] | '*';
|
|
8
|
+
/** @internal */
|
|
9
|
+
export declare function getWhereCondition<T extends object>(meta: EntityMetadata<T>, onConflictFields: (keyof T)[] | RawQueryFragment | undefined, data: EntityData<T>, where: FilterQuery<T>): {
|
|
10
|
+
where: FilterQuery<T>;
|
|
11
|
+
propIndex: number | false;
|
|
12
|
+
};
|
package/utils/upsert-utils.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
2
|
+
import { Utils } from './Utils.js';
|
|
1
3
|
function expandEmbeddedProperties(prop, key) {
|
|
2
4
|
if (prop.object) {
|
|
3
5
|
return [prop.name];
|
|
@@ -71,7 +73,15 @@ export function getOnConflictReturningFields(meta, data, uniqueFields, options)
|
|
|
71
73
|
if (!meta) {
|
|
72
74
|
return '*';
|
|
73
75
|
}
|
|
74
|
-
const keys = meta.comparableProps.filter(p =>
|
|
76
|
+
const keys = meta.comparableProps.filter(p => {
|
|
77
|
+
if (p.lazy || p.embeddable) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (p.autoincrement) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return Array.isArray(uniqueFields) && !uniqueFields.includes(p.name);
|
|
84
|
+
}).map(p => p.name);
|
|
75
85
|
if (meta.versionProperty) {
|
|
76
86
|
keys.push(meta.versionProperty);
|
|
77
87
|
}
|
|
@@ -88,3 +98,44 @@ export function getOnConflictReturningFields(meta, data, uniqueFields, options)
|
|
|
88
98
|
}
|
|
89
99
|
return keys.filter(key => !(key in data));
|
|
90
100
|
}
|
|
101
|
+
function getPropertyValue(obj, key) {
|
|
102
|
+
if (key.indexOf('.') === -1) {
|
|
103
|
+
return obj[key];
|
|
104
|
+
}
|
|
105
|
+
const parts = key.split('.');
|
|
106
|
+
let curr = obj;
|
|
107
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
108
|
+
curr[parts[i]] ??= {};
|
|
109
|
+
curr = curr[parts[i]];
|
|
110
|
+
}
|
|
111
|
+
return curr[parts[parts.length - 1]];
|
|
112
|
+
}
|
|
113
|
+
/** @internal */
|
|
114
|
+
export function getWhereCondition(meta, onConflictFields, data, where) {
|
|
115
|
+
const unique = onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
116
|
+
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
117
|
+
if (onConflictFields || where == null) {
|
|
118
|
+
if (propIndex !== false && propIndex >= 0) {
|
|
119
|
+
let key = unique[propIndex];
|
|
120
|
+
if (key.includes('.')) {
|
|
121
|
+
const prop = meta.properties[key.substring(0, key.indexOf('.'))];
|
|
122
|
+
if (prop) {
|
|
123
|
+
key = `${prop.fieldNames[0]}${key.substring(key.indexOf('.'))}`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
where = { [key]: getPropertyValue(data, unique[propIndex]) };
|
|
127
|
+
}
|
|
128
|
+
else if (meta.uniques.length > 0) {
|
|
129
|
+
for (const u of meta.uniques) {
|
|
130
|
+
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
131
|
+
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
132
|
+
o[key] = data[key];
|
|
133
|
+
return o;
|
|
134
|
+
}, {});
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { where, propIndex };
|
|
141
|
+
}
|