Initial commit - Event Planner application

This commit is contained in:
mberlin
2026-03-18 14:55:56 -03:00
commit 86d779eb4d
7548 changed files with 1006324 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
import {
type CountOptions,
type DeleteOptions,
type DriverMethodOptions,
EntityManagerType,
type FindOneOptions,
type FindOptions,
type IDatabaseDriver,
type LockOptions,
type NativeInsertUpdateManyOptions,
type NativeInsertUpdateOptions,
type OrderDefinition,
type StreamOptions,
} from './IDatabaseDriver.js';
import type {
ConnectionType,
Constructor,
Dictionary,
EntityData,
EntityDictionary,
EntityMetadata,
EntityName,
EntityProperty,
FilterQuery,
PopulateOptions,
Primary,
} from '../typings.js';
import type { MetadataStorage } from '../metadata/MetadataStorage.js';
import type { Connection, QueryResult, Transaction } from '../connections/Connection.js';
import { type Configuration, type ConnectionOptions } from '../utils/Configuration.js';
import { EntityComparator } from '../utils/EntityComparator.js';
import { type QueryOrder } from '../enums.js';
import type { Platform } from '../platforms/Platform.js';
import type { Collection } from '../entity/Collection.js';
import { EntityManager } from '../EntityManager.js';
import { DriverException } from '../exceptions.js';
import { MikroORM } from '../MikroORM.js';
/** Abstract base class for all database drivers, implementing common driver logic. */
export declare abstract class DatabaseDriver<C extends Connection> implements IDatabaseDriver<C> {
readonly config: Configuration;
protected readonly dependencies: string[];
[EntityManagerType]: EntityManager<this>;
protected readonly connection: C;
protected readonly replicas: C[];
protected readonly platform: Platform;
protected comparator: EntityComparator;
protected metadata: MetadataStorage;
protected constructor(config: Configuration, dependencies: string[]);
abstract find<T extends object, P extends string = never, F extends string = '*', E extends string = never>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options?: FindOptions<T, P, F, E>,
): Promise<EntityData<T>[]>;
abstract findOne<T extends object, P extends string = never, F extends string = '*', E extends string = never>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options?: FindOneOptions<T, P, F, E>,
): Promise<EntityData<T> | null>;
abstract nativeInsert<T extends object>(
entityName: EntityName<T>,
data: EntityDictionary<T>,
options?: NativeInsertUpdateOptions<T>,
): Promise<QueryResult<T>>;
abstract nativeInsertMany<T extends object>(
entityName: EntityName<T>,
data: EntityDictionary<T>[],
options?: NativeInsertUpdateManyOptions<T>,
transform?: (sql: string) => string,
): Promise<QueryResult<T>>;
abstract nativeUpdate<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
data: EntityDictionary<T>,
options?: NativeInsertUpdateOptions<T>,
): Promise<QueryResult<T>>;
nativeUpdateMany<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>[],
data: EntityDictionary<T>[],
options?: NativeInsertUpdateManyOptions<T>,
): Promise<QueryResult<T>>;
abstract nativeDelete<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options?: DeleteOptions<T>,
): Promise<QueryResult<T>>;
abstract count<T extends object, P extends string = never>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options?: CountOptions<T, P>,
): Promise<number>;
/** Creates a new EntityManager instance bound to this driver. */
createEntityManager(useContext?: boolean): this[typeof EntityManagerType];
findVirtual<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options: FindOptions<T, any, any, any>,
): Promise<EntityData<T>[]>;
countVirtual<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options: CountOptions<T, any>,
): Promise<number>;
aggregate(entityName: EntityName, pipeline: any[]): Promise<any[]>;
loadFromPivotTable<T extends object, O extends object>(
prop: EntityProperty,
owners: Primary<O>[][],
where?: FilterQuery<any>,
orderBy?: OrderDefinition<T>,
ctx?: Transaction,
options?: FindOptions<T, any, any, any>,
pivotJoin?: boolean,
): Promise<Dictionary<T[]>>;
syncCollections<T extends object, O extends object>(
collections: Iterable<Collection<T, O>>,
options?: DriverMethodOptions,
): Promise<void>;
/** Maps raw database result to entity data, converting column names to property names. */
mapResult<T extends object>(
result: EntityDictionary<T>,
meta?: EntityMetadata<T>,
populate?: PopulateOptions<T>[],
): EntityData<T> | null;
/** Opens the primary connection and all read replicas. */
connect(options?: { skipOnConnect?: boolean }): Promise<C>;
/** Closes all connections and re-establishes them. */
reconnect(options?: { skipOnConnect?: boolean }): Promise<C>;
/** Returns the write connection or a random read replica. */
getConnection(type?: ConnectionType): C;
/** Closes the primary connection and all read replicas. */
close(force?: boolean): Promise<void>;
/** Returns the database platform abstraction for this driver. */
getPlatform(): Platform;
/** Sets the metadata storage and initializes the comparator for all connections. */
setMetadata(metadata: MetadataStorage): void;
/** Returns the metadata storage used by this driver. */
getMetadata(): MetadataStorage;
/** Returns the names of native database dependencies required by this driver. */
getDependencies(): string[];
protected isPopulated<T extends object>(
meta: EntityMetadata<T>,
prop: EntityProperty<T>,
hint: PopulateOptions<T>,
name?: string,
): boolean;
protected processCursorOptions<T extends object, P extends string>(
meta: EntityMetadata<T>,
options: FindOptions<T, P, any, any>,
orderBy: OrderDefinition<T>,
): {
orderBy: OrderDefinition<T>[];
where: FilterQuery<T>;
};
protected createCursorCondition<T extends object>(
definition: (readonly [keyof T & string, QueryOrder])[],
offsets: Dictionary[],
inverse: boolean,
meta: EntityMetadata<T>,
): FilterQuery<T>;
/** @internal */
mapDataToFieldNames(
data: Dictionary,
stringifyJsonArrays: boolean,
properties?: Record<string, EntityProperty>,
convertCustomTypes?: boolean,
object?: boolean,
): Dictionary;
protected inlineEmbeddables<T extends object>(meta: EntityMetadata<T>, data: T, where?: boolean): void;
protected getPrimaryKeyFields<T>(meta: EntityMetadata<T>): string[];
protected createReplicas(cb: (c: ConnectionOptions) => C): C[];
/** Acquires a pessimistic lock on the given entity. */
lockPessimistic<T extends object>(entity: T, options: LockOptions): Promise<void>;
abstract stream<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options: StreamOptions<T>,
): AsyncIterableIterator<T>;
/**
* @inheritDoc
*/
convertException(exception: Error): DriverException;
protected rethrow<T>(promise: Promise<T>): Promise<T>;
/**
* @internal
*/
getTableName<T>(meta: EntityMetadata<T>, options: NativeInsertUpdateManyOptions<T>, quote?: boolean): string;
/**
* @internal
*/
getSchemaName(
meta?: EntityMetadata,
options?: {
schema?: string;
parentSchema?: string;
},
): string | undefined;
/** @internal */
getORMClass(): Constructor<MikroORM>;
}

493
node_modules/@mikro-orm/core/drivers/DatabaseDriver.js generated vendored Normal file
View File

@@ -0,0 +1,493 @@
import { EntityManagerType } from './IDatabaseDriver.js';
import { Utils } from '../utils/Utils.js';
import { Cursor } from '../utils/Cursor.js';
import { EntityComparator } from '../utils/EntityComparator.js';
import { isRaw, raw } from '../utils/RawQueryFragment.js';
import { QueryOrderNumeric, ReferenceKind } from '../enums.js';
import { EntityManager } from '../EntityManager.js';
import { CursorError, ValidationError } from '../errors.js';
import { DriverException } from '../exceptions.js';
import { helper } from '../entity/wrap.js';
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
import { JsonType } from '../types/JsonType.js';
import { MikroORM } from '../MikroORM.js';
/** Abstract base class for all database drivers, implementing common driver logic. */
export class DatabaseDriver {
config;
dependencies;
[EntityManagerType];
connection;
replicas = [];
platform;
comparator;
metadata;
constructor(config, dependencies) {
this.config = config;
this.dependencies = dependencies;
}
async nativeUpdateMany(entityName, where, data, options) {
throw new Error(`Batch updates are not supported by ${this.constructor.name} driver`);
}
/** Creates a new EntityManager instance bound to this driver. */
createEntityManager(useContext) {
const EntityManagerClass = this.config.get('entityManager', EntityManager);
return new EntityManagerClass(this.config, this, this.metadata, useContext);
}
/* v8 ignore next */
async findVirtual(entityName, where, options) {
throw new Error(`Virtual entities are not supported by ${this.constructor.name} driver.`);
}
/* v8 ignore next */
async countVirtual(entityName, where, options) {
throw new Error(`Counting virtual entities is not supported by ${this.constructor.name} driver.`);
}
async aggregate(entityName, pipeline) {
throw new Error(`Aggregations are not supported by ${this.constructor.name} driver`);
}
async loadFromPivotTable(prop, owners, where, orderBy, ctx, options, pivotJoin) {
throw new Error(`${this.constructor.name} does not use pivot tables`);
}
async syncCollections(collections, options) {
for (const coll of collections) {
/* v8 ignore else */
if (!coll.property.owner) {
if (coll.getSnapshot() === undefined) {
throw ValidationError.cannotModifyInverseCollection(coll.owner, coll.property);
}
continue;
}
/* v8 ignore next */
{
const pk = coll.property.targetMeta.primaryKeys[0];
const data = { [coll.property.name]: coll.getIdentifiers(pk) };
await this.nativeUpdate(coll.owner.constructor, helper(coll.owner).getPrimaryKey(), data, options);
}
}
}
/** Maps raw database result to entity data, converting column names to property names. */
mapResult(result, meta, populate = []) {
if (!result || !meta) {
return result ?? null;
}
return this.comparator.mapResult(meta, result);
}
/** Opens the primary connection and all read replicas. */
async connect(options) {
await this.connection.connect(options);
await Promise.all(this.replicas.map(replica => replica.connect()));
return this.connection;
}
/** Closes all connections and re-establishes them. */
async reconnect(options) {
await this.close(true);
await this.connect(options);
return this.connection;
}
/** Returns the write connection or a random read replica. */
getConnection(type = 'write') {
if (type === 'write' || this.replicas.length === 0) {
return this.connection;
}
const rand = Utils.randomInt(0, this.replicas.length - 1);
return this.replicas[rand];
}
/** Closes the primary connection and all read replicas. */
async close(force) {
await Promise.all(this.replicas.map(replica => replica.close(force)));
await this.connection.close(force);
}
/** Returns the database platform abstraction for this driver. */
getPlatform() {
return this.platform;
}
/** Sets the metadata storage and initializes the comparator for all connections. */
setMetadata(metadata) {
this.metadata = metadata;
this.comparator = new EntityComparator(this.metadata, this.platform);
this.connection.setMetadata(metadata);
this.connection.setPlatform(this.platform);
this.replicas.forEach(replica => {
replica.setMetadata(metadata);
replica.setPlatform(this.platform);
});
}
/** Returns the metadata storage used by this driver. */
getMetadata() {
return this.metadata;
}
/** Returns the names of native database dependencies required by this driver. */
getDependencies() {
return this.dependencies;
}
isPopulated(meta, prop, hint, name) {
if (hint.field === prop.name || hint.field === name || hint.all) {
return true;
}
if (prop.embedded && hint.children && meta.properties[prop.embedded[0]].name === hint.field) {
return hint.children.some(c => this.isPopulated(meta, prop, c, prop.embedded[1]));
}
return false;
}
processCursorOptions(meta, options, orderBy) {
const { first, last, before, after, overfetch } = options;
const limit = first ?? last;
const isLast = !first && !!last;
const definition = Cursor.getDefinition(meta, orderBy);
const $and = [];
// allow POJO as well, we care only about the correct key being present
const isCursor = (val, key) => {
return !!val && typeof val === 'object' && key in val;
};
const createCursor = (val, key, inverse = false) => {
let def = isCursor(val, key) ? val[key] : val;
if (Utils.isPlainObject(def)) {
def = Cursor.for(meta, def, orderBy);
}
/* v8 ignore next */
const offsets = def ? Cursor.decode(def) : [];
if (definition.length === offsets.length) {
return this.createCursorCondition(definition, offsets, inverse, meta);
}
/* v8 ignore next */
return {};
};
if (after) {
$and.push(createCursor(after, 'endCursor'));
}
if (before) {
$and.push(createCursor(before, 'startCursor', true));
}
if (limit != null) {
options.limit = limit + (overfetch ? 1 : 0);
}
const createOrderBy = (prop, direction) => {
if (Utils.isPlainObject(direction)) {
const value = Utils.getObjectQueryKeys(direction).reduce((o, key) => {
Object.assign(o, createOrderBy(key, direction[key]));
return o;
}, {});
return { [prop]: value };
}
const desc = direction === QueryOrderNumeric.DESC || direction.toString().toLowerCase() === 'desc';
const dir = Utils.xor(desc, isLast) ? 'desc' : 'asc';
return { [prop]: dir };
};
return {
orderBy: definition.map(([prop, direction]) => createOrderBy(prop, direction)),
where: $and.length > 1 ? { $and } : { ...$and[0] },
};
}
createCursorCondition(definition, offsets, inverse, meta) {
const createCondition = (prop, direction, offset, eq = false, path = prop) => {
if (Utils.isPlainObject(direction)) {
if (offset === undefined) {
throw CursorError.missingValue(meta.className, path);
}
const value = Utils.keys(direction).reduce((o, key) => {
Object.assign(o, createCondition(key, direction[key], offset?.[key], eq, `${path}.${key}`));
return o;
}, {});
return { [prop]: value };
}
const isDesc = direction === QueryOrderNumeric.DESC || direction.toString().toLowerCase() === 'desc';
const dirStr = direction.toString().toLowerCase();
let nullsFirst;
if (dirStr.includes('nulls first')) {
nullsFirst = true;
} else if (dirStr.includes('nulls last')) {
nullsFirst = false;
} else {
// Default: NULLS LAST for ASC, NULLS FIRST for DESC (matches most databases)
nullsFirst = isDesc;
}
const operator = Utils.xor(isDesc, inverse) ? '$lt' : '$gt';
// For leaf-level properties, undefined means missing value
if (offset === undefined) {
throw CursorError.missingValue(meta.className, path);
}
// Handle null offset (intentional null cursor value)
if (offset === null) {
if (eq) {
// Equal to null
return { [prop]: null };
}
// Strict comparison with null cursor value
// hasItemsAfterNull: forward + nullsFirst, or backward + nullsLast
const hasItemsAfterNull = Utils.xor(nullsFirst, inverse);
if (hasItemsAfterNull) {
return { [prop]: { $ne: null } };
}
// No items after null in this direction, return impossible condition
return { [prop]: [] };
}
// Non-null offset
return { [prop]: { [operator + (eq ? 'e' : '')]: offset } };
};
const [order, ...otherOrders] = definition;
const [offset, ...otherOffsets] = offsets;
const [prop, direction] = order;
if (!otherOrders.length) {
return createCondition(prop, direction, offset);
}
return {
...createCondition(prop, direction, offset, true),
$or: [
createCondition(prop, direction, offset),
this.createCursorCondition(otherOrders, otherOffsets, inverse, meta),
],
};
}
/** @internal */
mapDataToFieldNames(data, stringifyJsonArrays, properties, convertCustomTypes, object) {
if (!properties || data == null) {
return data;
}
data = Object.assign({}, data); // copy first
Object.keys(data).forEach(k => {
const prop = properties[k];
if (!prop) {
return;
}
if (prop.embeddedProps && !prop.object && !object) {
const copy = data[k];
delete data[k];
Object.assign(
data,
this.mapDataToFieldNames(copy, stringifyJsonArrays, prop.embeddedProps, convertCustomTypes),
);
return;
}
if (prop.embeddedProps && (object || prop.object)) {
const copy = data[k];
delete data[k];
if (prop.array) {
data[prop.fieldNames[0]] = copy?.map(item =>
this.mapDataToFieldNames(item, stringifyJsonArrays, prop.embeddedProps, convertCustomTypes, true),
);
} else {
data[prop.fieldNames[0]] = this.mapDataToFieldNames(
copy,
stringifyJsonArrays,
prop.embeddedProps,
convertCustomTypes,
true,
);
}
if (stringifyJsonArrays && prop.array && !object) {
data[prop.fieldNames[0]] = this.platform.convertJsonToDatabaseValue(data[prop.fieldNames[0]]);
}
return;
}
// Handle polymorphic relations - convert tuple or PolymorphicRef to separate columns
// Tuple format: ['discriminator', id] or ['discriminator', id1, id2] for composite keys
// Must be checked BEFORE joinColumns array handling since polymorphic uses fieldNames (includes discriminator)
if (prop.polymorphic && prop.fieldNames && prop.fieldNames.length >= 2) {
let discriminator;
let ids;
if (Array.isArray(data[k]) && typeof data[k][0] === 'string' && prop.discriminatorMap?.[data[k][0]]) {
// Tuple format: ['discriminator', ...ids]
const [disc, ...rest] = data[k];
discriminator = disc;
ids = rest;
} else if (data[k] instanceof PolymorphicRef) {
// PolymorphicRef wrapper (internal use)
discriminator = data[k].discriminator;
const polyId = data[k].id;
// Handle object-style composite key IDs like { tenantId: 1, orgId: 100 }
if (polyId && typeof polyId === 'object' && !Array.isArray(polyId)) {
const targetEntity = prop.discriminatorMap?.[discriminator];
const targetMeta = this.metadata.get(targetEntity);
ids = targetMeta.primaryKeys.map(pk => polyId[pk]);
} else {
ids = Utils.asArray(polyId);
}
}
if (discriminator) {
const discriminatorColumn = prop.fieldNames[0];
const idColumns = prop.fieldNames.slice(1);
delete data[k];
data[discriminatorColumn] = discriminator;
idColumns.forEach((col, idx) => {
data[col] = ids[idx];
});
return;
}
}
if (prop.joinColumns && Array.isArray(data[k])) {
const copy = Utils.flatten(data[k]);
delete data[k];
prop.joinColumns.forEach((joinColumn, idx) => (data[joinColumn] = copy[idx]));
return;
}
if (prop.joinColumns?.length > 1 && data[k] == null) {
delete data[k];
prop.ownColumns.forEach(joinColumn => (data[joinColumn] = null));
return;
}
if (
prop.customType &&
convertCustomTypes &&
!(prop.customType instanceof JsonType && object) &&
!isRaw(data[k])
) {
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, {
fromQuery: true,
key: k,
mode: 'query-data',
});
}
if (prop.hasConvertToDatabaseValueSQL && !prop.object && !isRaw(data[k])) {
const quoted = this.platform.quoteValue(data[k]);
const sql = prop.customType.convertToDatabaseValueSQL(quoted, this.platform);
data[k] = raw(sql.replace(/\?/g, '\\?'));
}
if (prop.fieldNames) {
Utils.renameKey(data, k, prop.fieldNames[0]);
}
});
return data;
}
inlineEmbeddables(meta, data, where) {
/* v8 ignore next */
if (data == null) {
return;
}
Utils.keys(data).forEach(k => {
if (Utils.isOperator(k)) {
Utils.asArray(data[k]).forEach(payload => this.inlineEmbeddables(meta, payload, where));
}
});
meta.props.forEach(prop => {
if (prop.kind === ReferenceKind.EMBEDDED && prop.object && !where && Utils.isObject(data[prop.name])) {
return;
}
if (prop.kind === ReferenceKind.EMBEDDED && Utils.isObject(data[prop.name])) {
const props = prop.embeddedProps;
let unknownProp = false;
Object.keys(data[prop.name]).forEach(kk => {
// explicitly allow `$exists`, `$eq`, `$ne` and `$elemMatch` operators here as they can't be misused this way
const operator = Object.keys(data[prop.name]).some(
f => Utils.isOperator(f) && !['$exists', '$ne', '$eq', '$elemMatch'].includes(f),
);
if (operator) {
throw ValidationError.cannotUseOperatorsInsideEmbeddables(meta.class, prop.name, data);
}
if (prop.object && where) {
const inline = (payload, sub, path) => {
if (sub.kind === ReferenceKind.EMBEDDED && Utils.isObject(payload[sub.embedded[1]])) {
return Object.keys(payload[sub.embedded[1]]).forEach(kkk => {
if (!sub.embeddedProps[kkk]) {
throw ValidationError.invalidEmbeddableQuery(meta.class, kkk, sub.type);
}
inline(payload[sub.embedded[1]], sub.embeddedProps[kkk], [...path, sub.fieldNames[0]]);
});
}
data[`${path.join('.')}.${sub.fieldNames[0]}`] = payload[sub.embedded[1]];
};
const parentPropName = kk.substring(0, kk.indexOf('.'));
// we might be using some native JSON operator, e.g. with mongodb's `$geoWithin` or `$exists`
if (props[kk]) {
/* v8 ignore next */
inline(data[prop.name], props[kk] || props[parentPropName], [prop.fieldNames[0]]);
} else if (props[parentPropName]) {
data[`${prop.fieldNames[0]}.${kk}`] = data[prop.name][kk];
} else {
unknownProp = true;
}
} else if (props[kk]) {
data[props[kk].fieldNames[0]] = data[prop.name][props[kk].embedded[1]];
} else {
throw ValidationError.invalidEmbeddableQuery(meta.class, kk, prop.type);
}
});
if (!unknownProp) {
delete data[prop.name];
}
}
});
}
getPrimaryKeyFields(meta) {
return meta.getPrimaryProps().flatMap(pk => pk.fieldNames);
}
createReplicas(cb) {
const replicas = this.config.get('replicas', []);
const ret = [];
const props = [
'dbName',
'clientUrl',
'host',
'port',
'user',
'password',
'multipleStatements',
'pool',
'name',
'driverOptions',
];
for (const conf of replicas) {
const replicaConfig = Utils.copy(conf);
for (const prop of props) {
if (conf[prop]) {
continue;
}
// do not copy options that can be inferred from explicitly provided `clientUrl`
if (conf.clientUrl && ['clientUrl', 'host', 'port', 'user', 'password'].includes(prop)) {
continue;
}
if (conf.clientUrl && prop === 'dbName' && new URL(conf.clientUrl).pathname) {
continue;
}
replicaConfig[prop] = this.config.get(prop);
}
ret.push(cb(replicaConfig));
}
return ret;
}
/** Acquires a pessimistic lock on the given entity. */
async lockPessimistic(entity, options) {
throw new Error(`Pessimistic locks are not supported by ${this.constructor.name} driver`);
}
/**
* @inheritDoc
*/
convertException(exception) {
if (exception instanceof DriverException) {
return exception;
}
return this.platform.getExceptionConverter().convertException(exception);
}
rethrow(promise) {
return promise.catch(e => {
throw this.convertException(e);
});
}
/**
* @internal
*/
getTableName(meta, options, quote = true) {
const schema = this.getSchemaName(meta, options);
const tableName =
schema && schema !== this.platform.getDefaultSchemaName() ? `${schema}.${meta.tableName}` : meta.tableName;
if (quote) {
return this.platform.quoteIdentifier(tableName);
}
return tableName;
}
/**
* @internal
*/
getSchemaName(meta, options) {
if (meta?.schema && meta.schema !== '*') {
return meta.schema;
}
if (options?.schema === '*') {
return this.config.get('schema');
}
const schemaName = meta?.schema === '*' ? this.config.getSchema() : meta?.schema;
return options?.schema ?? options?.parentSchema ?? schemaName ?? this.config.getSchema();
}
/** @internal */
getORMClass() {
return MikroORM;
}
}

View File

@@ -0,0 +1,511 @@
import type {
ConnectionType,
Constructor,
EntityData,
EntityMetadata,
EntityProperty,
FilterQuery,
Primary,
Dictionary,
IPrimaryKey,
PopulateOptions,
EntityDictionary,
AutoPath,
ObjectQuery,
FilterObject,
Populate,
EntityName,
PopulateHintOptions,
Prefixes,
} from '../typings.js';
import type { Connection, QueryResult, Transaction } from '../connections/Connection.js';
import type {
FlushMode,
LockMode,
QueryOrderMap,
QueryFlag,
LoadStrategy,
PopulateHint,
PopulatePath,
} from '../enums.js';
import type { Platform } from '../platforms/Platform.js';
import type { MetadataStorage } from '../metadata/MetadataStorage.js';
import type { Collection } from '../entity/Collection.js';
import type { EntityManager } from '../EntityManager.js';
import type { DriverException } from '../exceptions.js';
import type { Configuration } from '../utils/Configuration.js';
import type { MikroORM } from '../MikroORM.js';
import type { LoggingOptions, LogContext } from '../logging/Logger.js';
import type { Raw } from '../utils/RawQueryFragment.js';
/** Symbol used to extract the EntityManager type from a driver instance. */
export declare const EntityManagerType: unique symbol;
/** Interface defining the contract for all database drivers. */
export interface IDatabaseDriver<C extends Connection = Connection> {
[EntityManagerType]: EntityManager<this>;
readonly config: Configuration;
/** Creates a new EntityManager instance for this driver. */
createEntityManager(useContext?: boolean): this[typeof EntityManagerType];
/** Opens a connection to the database. */
connect(options?: { skipOnConnect?: boolean }): Promise<C>;
/** Closes the database connection. */
close(force?: boolean): Promise<void>;
/** Closes and re-establishes the database connection. */
reconnect(options?: { skipOnConnect?: boolean }): Promise<C>;
/** Returns the underlying database connection (write or read replica). */
getConnection(type?: ConnectionType): C;
/**
* Finds selection of entities
*/
find<T extends object, P extends string = never, F extends string = '*', E extends string = never>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options?: FindOptions<T, P, F, E>,
): Promise<EntityData<T>[]>;
/**
* Finds single entity (table row, document)
*/
findOne<T extends object, P extends string = never, F extends string = '*', E extends string = never>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options?: FindOneOptions<T, P, F, E>,
): Promise<EntityData<T> | null>;
/** Finds entities backed by a virtual (expression-based) definition. */
findVirtual<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options: FindOptions<T, any, any, any>,
): Promise<EntityData<T>[]>;
/** Returns an async iterator that streams query results one entity at a time. */
stream<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options: StreamOptions<T>,
): AsyncIterableIterator<T>;
/** Inserts a single row into the database. */
nativeInsert<T extends object>(
entityName: EntityName<T>,
data: EntityDictionary<T>,
options?: NativeInsertUpdateOptions<T>,
): Promise<QueryResult<T>>;
/** Inserts multiple rows into the database in a single batch operation. */
nativeInsertMany<T extends object>(
entityName: EntityName<T>,
data: EntityDictionary<T>[],
options?: NativeInsertUpdateManyOptions<T>,
transform?: (sql: string) => string,
): Promise<QueryResult<T>>;
/** Updates rows matching the given condition. */
nativeUpdate<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
data: EntityDictionary<T>,
options?: NativeInsertUpdateOptions<T>,
): Promise<QueryResult<T>>;
/** Updates multiple rows with different payloads in a single batch operation. */
nativeUpdateMany<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>[],
data: EntityDictionary<T>[],
options?: NativeInsertUpdateManyOptions<T>,
): Promise<QueryResult<T>>;
/** Deletes rows matching the given condition. */
nativeDelete<T extends object>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options?: NativeDeleteOptions<T>,
): Promise<QueryResult<T>>;
/** Persists changes to M:N collections (inserts/deletes pivot table rows). */
syncCollections<T extends object, O extends object>(
collections: Iterable<Collection<T, O>>,
options?: DriverMethodOptions,
): Promise<void>;
/** Counts entities matching the given condition. */
count<T extends object, P extends string = never>(
entityName: EntityName<T>,
where: FilterQuery<T>,
options?: CountOptions<T, P>,
): Promise<number>;
/** Executes a MongoDB aggregation pipeline (MongoDB driver only). */
aggregate(entityName: EntityName, pipeline: any[]): Promise<any[]>;
/** Maps raw database result to entity data, converting column names to property names. */
mapResult<T extends object>(
result: EntityDictionary<T>,
meta: EntityMetadata<T>,
populate?: PopulateOptions<T>[],
): EntityData<T> | null;
/**
* When driver uses pivot tables for M:N, this method will load identifiers for given collections from them
*/
loadFromPivotTable<T extends object, O extends object>(
prop: EntityProperty,
owners: Primary<O>[][],
where?: FilterQuery<T>,
orderBy?: OrderDefinition<T>,
ctx?: Transaction,
options?: FindOptions<T, any, any, any>,
pivotJoin?: boolean,
): Promise<Dictionary<T[]>>;
/** Returns the database platform abstraction for this driver. */
getPlatform(): Platform;
/** Sets the metadata storage used by this driver. */
setMetadata(metadata: MetadataStorage): void;
/** Returns the metadata storage used by this driver. */
getMetadata(): MetadataStorage;
/**
* Returns name of the underlying database dependencies (e.g. `mongodb` or `mysql2`)
* for SQL drivers it also returns `knex` in the array as connectors are not used directly there
*/
getDependencies(): string[];
/** Acquires a pessimistic lock on the given entity. */
lockPessimistic<T extends object>(entity: T, options: LockOptions): Promise<void>;
/**
* Converts native db errors to standardized driver exceptions
*/
convertException(exception: Error): DriverException;
/**
* @internal
*/
getSchemaName(
meta?: EntityMetadata,
options?: {
schema?: string;
parentSchema?: string;
},
): string | undefined;
/**
* @internal
*/
getORMClass(): Constructor<MikroORM>;
}
/** Represents a field selector for entity queries (property name or wildcard). */
export type EntityField<T, P extends string = PopulatePath.ALL> =
| keyof T
| PopulatePath.ALL
| AutoPath<T, P, `${PopulatePath.ALL}`>;
/** Defines the ordering for query results, either a single order map or an array of them. */
export type OrderDefinition<T> =
| (QueryOrderMap<T> & {
0?: never;
})
| QueryOrderMap<T>[];
/** Options for `em.findAll()`, extends FindOptions with an optional `where` clause. */
export interface FindAllOptions<
T,
P extends string = never,
F extends string = '*',
E extends string = never,
> extends FindOptions<T, P, F, E> {
where?: FilterQuery<T>;
}
/** Options for streaming query results via `em.stream()`. */
export interface StreamOptions<
Entity,
Populate extends string = never,
Fields extends string = '*',
Exclude extends string = never,
> extends Omit<
FindAllOptions<Entity, Populate, Fields, Exclude>,
'cache' | 'before' | 'after' | 'first' | 'last' | 'overfetch' | 'strategy'
> {
/**
* When populating to-many relations, the ORM streams fully merged entities instead of yielding every row.
* You can opt out of this behavior by specifying `mergeResults: false`. This will yield every row from
* the SQL result, but still mapped to entities, meaning that to-many collections will contain at most
* a single item, and you will get duplicate root entities when they have multiple items in the populated
* collection.
*
* @default true
*/
mergeResults?: boolean;
}
/** Configuration for enabling/disabling named filters on a query. */
export type FilterOptions = Dictionary<boolean | Dictionary> | string[] | boolean;
/** Specifies which relations to populate and which fields to select or exclude. */
export interface LoadHint<
Entity,
Hint extends string = never,
Fields extends string = PopulatePath.ALL,
Excludes extends string = never,
> {
populate?: Populate<Entity, Hint>;
fields?: readonly AutoPath<Entity, Fields, `${PopulatePath.ALL}`>[];
exclude?: readonly AutoPath<Entity, Excludes>[];
}
/** Options for `em.find()` queries, including population, ordering, pagination, and locking. */
export interface FindOptions<
Entity,
Hint extends string = never,
Fields extends string = PopulatePath.ALL,
Excludes extends string = never,
> extends LoadHint<Entity, Hint, Fields, Excludes> {
/**
* Where condition for populated relations. This will have no effect on the root entity.
* With `select-in` strategy, this is applied only to the populate queries.
* With `joined` strategy, those are applied as `join on` conditions.
* When you use a nested condition on a to-many relation, it will produce a nested inner join,
* discarding the collection items based on the child condition.
*/
populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
/**
* Filter condition for populated relations. This is similar to `populateWhere`, but will produce a `left join`
* when nesting the condition. This is used for implementation of joined filters.
*/
populateFilter?: ObjectQuery<Entity>;
/**
* Index-friendly alternative to `$or` for conditions that span joined relations.
* Each array element becomes an independent branch combined via `UNION ALL` subquery:
* `WHERE pk IN (branch_1 UNION ALL branch_2 ... branch_N)`.
* The database plans each branch independently, enabling per-table index usage
* (e.g. GIN trigram indexes for fuzzy search across related entities).
* sql only
*/
unionWhere?: ObjectQuery<Entity>[];
/**
* Strategy for combining `unionWhere` branches.
* - `'union-all'` (default) — skips deduplication, faster for most use cases.
* - `'union'` — deduplicates rows between branches; useful when branch overlap is very high.
* sql only
*/
unionWhereStrategy?: 'union-all' | 'union';
/** Used for ordering of the populate queries. If not specified, the value of `options.orderBy` is used. */
populateOrderBy?: OrderDefinition<Entity>;
/** Per-relation overrides for populate loading behavior. Keys are populate paths (same as used in `populate`). */
populateHints?: [Hint] extends [never]
? never
: {
[K in Prefixes<Hint>]?: PopulateHintOptions;
};
/** Ordering of the results.Can be an object or array of objects, keys are property names, values are ordering (asc/desc) */
orderBy?: OrderDefinition<Entity>;
/** Control result caching for this query. Result cache is by default disabled, not to be confused with the identity map. */
cache?: boolean | number | [string, number];
/**
* Limit the number of returned results. If you try to use limit/offset on a query that joins a to-many relation, pagination mechanism
* will be triggered, resulting in a subquery condition, to apply this limit only to the root entities
* instead of the cartesian product you get from a database in this case.
*/
limit?: number;
/**
* Sets the offset. If you try to use limit/offset on a query that joins a to-many relation, pagination mechanism
* will be triggered, resulting in a subquery condition, to apply this limit only to the root entities
* instead of the cartesian product you get from a database in this case.
*/
offset?: number;
/** Fetch items `before` this cursor. */
before?:
| string
| {
startCursor: string | null;
}
| FilterObject<Entity>;
/** Fetch items `after` this cursor. */
after?:
| string
| {
endCursor: string | null;
}
| FilterObject<Entity>;
/** Fetch `first` N items. */
first?: number;
/** Fetch `last` N items. */
last?: number;
/** Fetch one more item than `first`/`last`, enabled automatically in `em.findByCursor` to check if there is a next page. */
overfetch?: boolean;
refresh?: boolean;
convertCustomTypes?: boolean;
disableIdentityMap?: boolean;
schema?: string;
flags?: QueryFlag[];
/** sql only */
groupBy?: string | string[];
having?: FilterQuery<Entity>;
/** sql only */
strategy?: LoadStrategy | `${LoadStrategy}`;
flushMode?: FlushMode | `${FlushMode}`;
filters?: FilterOptions;
/** sql only */
lockMode?: Exclude<LockMode, LockMode.OPTIMISTIC>;
/** sql only */
lockTableAliases?: string[];
ctx?: Transaction;
connectionType?: ConnectionType;
/** SQL: appended to FROM clause (e.g. `'force index(my_index)'`); MongoDB: index name or spec passed as `hint`. */
indexHint?: string | Dictionary;
/** sql only */
comments?: string | string[];
/** sql only */
hintComments?: string | string[];
/** SQL: collation name string applied as COLLATE to ORDER BY; MongoDB: CollationOptions object. */
collation?: CollationOptions | string;
/** mongodb only */
maxTimeMS?: number;
/** mongodb only */
allowDiskUse?: boolean;
loggerContext?: LogContext;
logging?: LoggingOptions;
/** @internal used to apply filters to the auto-joined relations */
em?: EntityManager;
}
/** Options for cursor-based pagination via `em.findByCursor()`. */
export interface FindByCursorOptions<
T extends object,
P extends string = never,
F extends string = '*',
E extends string = never,
I extends boolean = true,
> extends Omit<FindAllOptions<T, P, F, E>, 'limit' | 'offset'> {
includeCount?: I;
}
/** Options for `em.findOne()`, extends FindOptions with optimistic lock version support. */
export interface FindOneOptions<
T,
P extends string = never,
F extends string = '*',
E extends string = never,
> extends Omit<FindOptions<T, P, F, E>, 'limit' | 'lockMode'> {
lockMode?: LockMode;
lockVersion?: number | Date;
}
/** Options for `em.findOneOrFail()`, adds a custom error handler for missing entities. */
export interface FindOneOrFailOptions<
T extends object,
P extends string = never,
F extends string = '*',
E extends string = never,
> extends FindOneOptions<T, P, F, E> {
failHandler?: (entityName: string, where: Dictionary | IPrimaryKey | any) => Error;
strict?: boolean;
}
/** Options for native insert and update operations. */
export interface NativeInsertUpdateOptions<T> {
convertCustomTypes?: boolean;
ctx?: Transaction;
schema?: string;
/** `nativeUpdate()` only option */
upsert?: boolean;
loggerContext?: LogContext;
/** sql only */
unionWhere?: ObjectQuery<T>[];
/** sql only */
unionWhereStrategy?: 'union-all' | 'union';
filters?: FilterOptions;
/** @internal */
em?: EntityManager;
}
/** Options for batch native insert and update operations. */
export interface NativeInsertUpdateManyOptions<T> extends NativeInsertUpdateOptions<T> {
processCollections?: boolean;
}
/** Options for `em.upsert()`, controlling conflict resolution behavior. */
export interface UpsertOptions<Entity, Fields extends string = never> extends Omit<
NativeInsertUpdateOptions<Entity>,
'upsert'
> {
onConflictFields?: (keyof Entity)[] | Raw;
onConflictAction?: 'ignore' | 'merge';
onConflictMergeFields?: AutoPath<Entity, Fields, `${PopulatePath.ALL}`>[];
onConflictExcludeFields?: AutoPath<Entity, Fields, `${PopulatePath.ALL}`>[];
onConflictWhere?: FilterQuery<Entity>;
disableIdentityMap?: boolean;
}
/** Options for `em.upsertMany()`, adds batch size control. */
export interface UpsertManyOptions<Entity, Fields extends string = never> extends UpsertOptions<Entity, Fields> {
batchSize?: number;
}
/** Options for `em.count()` queries. */
export interface CountOptions<T extends object, P extends string = never> {
filters?: FilterOptions;
schema?: string;
groupBy?: string | readonly string[];
having?: FilterQuery<T>;
cache?: boolean | number | [string, number];
populate?: Populate<T, P>;
populateWhere?: ObjectQuery<T> | PopulateHint | `${PopulateHint}`;
populateFilter?: ObjectQuery<T>;
/** @see FindOptions.unionWhere */
unionWhere?: ObjectQuery<T>[];
/** @see FindOptions.unionWhereStrategy */
unionWhereStrategy?: 'union-all' | 'union';
ctx?: Transaction;
connectionType?: ConnectionType;
flushMode?: FlushMode | `${FlushMode}`;
/** SQL: appended to FROM clause (e.g. `'force index(my_index)'`); MongoDB: index name or spec passed as `hint`. */
indexHint?: string | Dictionary;
/** sql only */
comments?: string | string[];
/** sql only */
hintComments?: string | string[];
/** SQL: collation name string applied as COLLATE; MongoDB: CollationOptions object. */
collation?: CollationOptions | string;
/** mongodb only */
maxTimeMS?: number;
loggerContext?: LogContext;
logging?: LoggingOptions;
/** @internal used to apply filters to the auto-joined relations */
em?: EntityManager;
}
/** Options for `em.qb().update()` operations. */
export interface UpdateOptions<T> {
filters?: FilterOptions;
schema?: string;
ctx?: Transaction;
/** sql only */
unionWhere?: ObjectQuery<T>[];
/** sql only */
unionWhereStrategy?: 'union-all' | 'union';
}
/** Options for `em.qb().delete()` operations. */
export interface DeleteOptions<T> extends DriverMethodOptions {
filters?: FilterOptions;
/** sql only */
unionWhere?: ObjectQuery<T>[];
/** sql only */
unionWhereStrategy?: 'union-all' | 'union';
/** @internal */
em?: EntityManager;
}
/** Options for `em.nativeDelete()` operations. */
export interface NativeDeleteOptions<T> extends DriverMethodOptions {
filters?: FilterOptions;
/** sql only */
unionWhere?: ObjectQuery<T>[];
/** sql only */
unionWhereStrategy?: 'union-all' | 'union';
/** @internal */
em?: EntityManager;
}
/** Options for pessimistic and optimistic lock operations. */
export interface LockOptions extends DriverMethodOptions {
lockMode?: LockMode;
lockVersion?: number | Date;
lockTableAliases?: string[];
logging?: LoggingOptions;
}
/** Base options shared by all driver methods (transaction context, schema, logging). */
export interface DriverMethodOptions {
ctx?: Transaction;
schema?: string;
loggerContext?: LogContext;
}
/** MongoDB-style collation options for locale-aware string comparison. */
export interface CollationOptions {
locale: string;
caseLevel?: boolean;
caseFirst?: string;
strength?: number;
numericOrdering?: boolean;
alternate?: string;
maxVariable?: string;
backwards?: boolean;
}
/** Options for `em.getReference()`, controlling wrapping and type conversion. */
export interface GetReferenceOptions {
wrapped?: boolean;
convertCustomTypes?: boolean;
schema?: string;
/**
* Property name to use for identity map lookup instead of the primary key.
* This is useful for creating references by unique non-PK properties.
*/
key?: string;
}

View File

@@ -0,0 +1,2 @@
/** Symbol used to extract the EntityManager type from a driver instance. */
export const EntityManagerType = Symbol('EntityManagerType');

2
node_modules/@mikro-orm/core/drivers/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export * from './IDatabaseDriver.js';
export * from './DatabaseDriver.js';

2
node_modules/@mikro-orm/core/drivers/index.js generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export * from './IDatabaseDriver.js';
export * from './DatabaseDriver.js';