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