@mikro-orm/core 7.1.0-dev.3 → 7.1.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.
Files changed (55) hide show
  1. package/EntityManager.d.ts +44 -11
  2. package/EntityManager.js +139 -31
  3. package/README.md +2 -1
  4. package/drivers/IDatabaseDriver.d.ts +39 -1
  5. package/entity/BaseEntity.d.ts +68 -1
  6. package/entity/BaseEntity.js +18 -0
  7. package/entity/Collection.d.ts +6 -3
  8. package/entity/Collection.js +15 -4
  9. package/entity/EntityFactory.js +20 -1
  10. package/entity/EntityLoader.d.ts +3 -1
  11. package/entity/EntityLoader.js +39 -16
  12. package/entity/EntityRepository.d.ts +27 -9
  13. package/entity/EntityRepository.js +12 -0
  14. package/entity/Reference.d.ts +42 -1
  15. package/entity/Reference.js +9 -0
  16. package/entity/defineEntity.d.ts +105 -23
  17. package/entity/defineEntity.js +17 -6
  18. package/entity/utils.js +4 -5
  19. package/errors.d.ts +2 -0
  20. package/errors.js +4 -0
  21. package/index.d.ts +2 -2
  22. package/index.js +1 -1
  23. package/metadata/EntitySchema.js +3 -0
  24. package/metadata/MetadataDiscovery.d.ts +12 -0
  25. package/metadata/MetadataDiscovery.js +185 -16
  26. package/metadata/MetadataValidator.d.ts +24 -0
  27. package/metadata/MetadataValidator.js +202 -1
  28. package/metadata/types.d.ts +77 -4
  29. package/naming-strategy/AbstractNamingStrategy.d.ts +1 -1
  30. package/naming-strategy/NamingStrategy.d.ts +1 -1
  31. package/package.json +1 -1
  32. package/platforms/Platform.d.ts +8 -1
  33. package/platforms/Platform.js +40 -4
  34. package/serialization/EntitySerializer.js +2 -1
  35. package/typings.d.ts +176 -18
  36. package/typings.js +22 -5
  37. package/unit-of-work/UnitOfWork.js +3 -0
  38. package/utils/AbstractMigrator.d.ts +20 -5
  39. package/utils/AbstractMigrator.js +263 -28
  40. package/utils/AbstractSchemaGenerator.d.ts +1 -1
  41. package/utils/AbstractSchemaGenerator.js +4 -1
  42. package/utils/Configuration.d.ts +25 -0
  43. package/utils/Configuration.js +1 -0
  44. package/utils/DataloaderUtils.d.ts +10 -1
  45. package/utils/DataloaderUtils.js +78 -0
  46. package/utils/EntityComparator.js +1 -1
  47. package/utils/QueryHelper.d.ts +16 -0
  48. package/utils/QueryHelper.js +15 -0
  49. package/utils/Utils.js +1 -1
  50. package/utils/fs-utils.d.ts +2 -0
  51. package/utils/fs-utils.js +7 -1
  52. package/utils/index.d.ts +1 -0
  53. package/utils/index.js +1 -0
  54. package/utils/partition-utils.d.ts +17 -0
  55. package/utils/partition-utils.js +79 -0
@@ -1,9 +1,52 @@
1
- import type { AnyEntity, Constructor, EntityName, AnyString, CheckCallback, GeneratedColumnCallback, FormulaCallback, FilterQuery, Dictionary, AutoPath, EntityClass, IndexCallback, ObjectQuery, Raw } from '../typings.js';
1
+ import type { AnyEntity, Constructor, EntityName, AnyString, CheckCallback, GeneratedColumnCallback, FormulaCallback, FilterQuery, Dictionary, AutoPath, EntityClass, IndexCallback, ObjectQuery, Raw, SchemaColumns, TriggerDef } from '../typings.js';
2
2
  import type { Cascade, LoadStrategy, DeferMode, QueryOrderMap, EmbeddedPrefixMode } from '../enums.js';
3
3
  import type { Type, types } from '../types/index.js';
4
4
  import type { EntityManager } from '../EntityManager.js';
5
5
  import type { FilterOptions, FindOptions } from '../drivers/IDatabaseDriver.js';
6
6
  import type { SerializeOptions } from '../serialization/EntitySerializer.js';
7
+ /** Supported PostgreSQL partitioning strategies. */
8
+ export type EntityPartitionType = 'hash' | 'list' | 'range';
9
+ /**
10
+ * Partition key expression for PostgreSQL partitioned tables.
11
+ *
12
+ * You can use:
13
+ * - a property name, e.g. `'type'`
14
+ * - a list of property names for composite expressions, e.g. `['tenant', 'createdAt']`
15
+ * - a raw SQL expression string, e.g. `"date_trunc('month', created_at)"`
16
+ * - a callback that receives schema column mappings and returns SQL
17
+ */
18
+ export type EntityPartitionExpression<E = AnyEntity> = (keyof E & string) | readonly (keyof E & string)[] | AnyString | ((columns: SchemaColumns<E>) => string);
19
+ /** Explicit child partition definition for PostgreSQL `list` and `range` partitioning. */
20
+ export interface EntityPartition<E = AnyEntity> {
21
+ /**
22
+ * Optional child table name. Defaults to `<tableName>_<index>`. A single `.` is treated as a
23
+ * `schema.table` separator; names with more than one `.` are rejected to avoid ambiguity.
24
+ */
25
+ name?: string;
26
+ /** Partition bound clause, either as `for values ...` or the trailing part such as `in (...)`, `from ... to ...`, or `default`. */
27
+ values: string;
28
+ }
29
+ /**
30
+ * PostgreSQL table partitioning definition.
31
+ *
32
+ * - `hash` partitions generate child tables automatically from the partition count
33
+ * - `list` and `range` partitions require explicit child partition definitions
34
+ */
35
+ export type EntityPartitionBy<E = AnyEntity> = {
36
+ type: Extract<EntityPartitionType, 'hash'>;
37
+ expression: EntityPartitionExpression<E>;
38
+ /**
39
+ * Hash partition fan-out: either a number (auto-named as `${tableName}_N`) or an array of
40
+ * explicit partition names in remainder order. Names may be `schema.name` to create the
41
+ * child in a non-default schema.
42
+ */
43
+ partitions: number | readonly string[];
44
+ } | {
45
+ type: Exclude<EntityPartitionType, 'hash'>;
46
+ expression: EntityPartitionExpression<E>;
47
+ /** Explicit child partitions created in the order provided. */
48
+ partitions: EntityPartition<E>[];
49
+ };
7
50
  export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
8
51
  /** Override default collection/table name. Alias for `collection`. */
9
52
  tableName?: string;
@@ -57,6 +100,15 @@ export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
57
100
  };
58
101
  /** Used to make ORM aware of externally defined triggers. This is needed for MS SQL Server multi inserts, ignored in other dialects. */
59
102
  hasTriggers?: boolean;
103
+ /** Database triggers to create for this entity's table. (SQL drivers only) */
104
+ triggers?: TriggerDef<E>[];
105
+ /**
106
+ * PostgreSQL partitioning definition for this table.
107
+ *
108
+ * Partitioned tables are tracked by the schema generator and introspected back from PostgreSQL.
109
+ * Changing an existing partition layout still requires a manual migration.
110
+ */
111
+ partitionBy?: EntityPartitionBy<E>;
60
112
  /** SQL query that maps to a {@doclink virtual-entities | virtual entity}, or for view entities, the view definition. */
61
113
  expression?: string | ((em: any, where: ObjectQuery<E>, options: FindOptions<E, any, any, any>, stream?: boolean) => object);
62
114
  /** Set {@doclink repositories#custom-repository | custom repository class}. */
@@ -202,6 +254,12 @@ export interface PropertyOptions<Owner> {
202
254
  * @see https://mikro-orm.io/docs/defining-entities#lazy-scalar-properties
203
255
  */
204
256
  lazy?: boolean;
257
+ /**
258
+ * Marks this property as an array. When used with a custom type, the type is automatically
259
+ * wrapped in an `ArrayType` that delegates element conversion to the inner type.
260
+ * The column type is inferred as `innerType[]` (e.g. `int[]`, `time[]`).
261
+ */
262
+ array?: boolean;
205
263
  /**
206
264
  * Set true to define entity's unique primary key identifier.
207
265
  * Alias for `@PrimaryKey()` decorator
@@ -323,6 +381,11 @@ export interface PropertyOptions<Owner> {
323
381
  * Specify comment of column for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
324
382
  */
325
383
  comment?: string;
384
+ /**
385
+ * Specify column-level collation for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
386
+ * Emits a `COLLATE` clause on the column definition; the accepted collation names are dialect-specific.
387
+ */
388
+ collation?: string;
326
389
  /** mysql only */
327
390
  extra?: string;
328
391
  /**
@@ -330,11 +393,11 @@ export interface PropertyOptions<Owner> {
330
393
  *
331
394
  * @see https://mikro-orm.io/docs/defining-entities#sql-generated-columns
332
395
  */
333
- ignoreSchemaChanges?: ('type' | 'extra' | 'default')[];
396
+ ignoreSchemaChanges?: ('type' | 'extra' | 'default' | 'collation')[];
334
397
  }
335
398
  export interface ReferenceOptions<Owner, Target> extends PropertyOptions<Owner> {
336
399
  /** Set target entity type. For polymorphic relations, pass an array of entity types. */
337
- entity?: () => EntityName<Target> | EntityName<Target>[];
400
+ entity?: () => EntityName<Target> | EntityName[];
338
401
  /** Set what actions on owning entity should be cascaded to the relationship. Defaults to [Cascade.PERSIST, Cascade.MERGE] (see {@doclink cascading}). */
339
402
  cascade?: Cascade[];
340
403
  /** Always load the relationship. Discouraged for use with to-many relations for performance reasons. */
@@ -499,7 +562,7 @@ export interface EmbeddableOptions<Owner> {
499
562
  abstract?: boolean;
500
563
  }
501
564
  export interface EnumOptions<T> extends PropertyOptions<T> {
502
- items?: readonly (number | string)[] | (() => Dictionary);
565
+ items?: readonly (number | string)[] | Dictionary | (() => Dictionary);
503
566
  array?: boolean;
504
567
  /** for postgres, by default it uses text column with check constraint */
505
568
  nativeEnumName?: string;
@@ -543,6 +606,16 @@ interface BaseOptions<T, H extends string> {
543
606
  include?: T extends EntityClass<infer P> ? Properties<P, H> : Properties<T, H>;
544
607
  /** Fill factor for the index as a percentage 0-100 (PostgreSQL, MSSQL). */
545
608
  fillFactor?: number;
609
+ /**
610
+ * Predicate for partial indexes. Object form is a `FilterQuery` and is portable across
611
+ * SQL drivers and MongoDB; string form is a raw SQL fragment (SQL drivers only).
612
+ *
613
+ * Native support: PostgreSQL, SQLite, MSSQL (`WHERE`), MongoDB (`partialFilterExpression`).
614
+ * MySQL 8.0.13+ / Oracle: emulated via `CASE WHEN` functional index (uniqueness only enforced
615
+ * where the predicate holds). MariaDB is not supported — it has no inline expression indexes;
616
+ * define a virtual generated column and index that instead.
617
+ */
618
+ where?: string | FilterQuery<NoInfer<T> extends EntityClass<infer P> ? P : NoInfer<T>>;
546
619
  }
547
620
  export interface UniqueOptions<T, H extends string = string> extends BaseOptions<T, H> {
548
621
  deferMode?: DeferMode | `${DeferMode}`;
@@ -4,7 +4,7 @@ import { type ReferenceKind } from '../enums.js';
4
4
  export declare abstract class AbstractNamingStrategy implements NamingStrategy {
5
5
  getClassName(file: string, separator?: string): string;
6
6
  classToMigrationName(timestamp: string, customMigrationName?: string): string;
7
- indexName(tableName: string, columns: string[], type: 'primary' | 'foreign' | 'unique' | 'index' | 'sequence' | 'check' | 'default'): string;
7
+ indexName(tableName: string, columns: string[], type: 'primary' | 'foreign' | 'unique' | 'index' | 'sequence' | 'check' | 'default' | 'trigger'): string;
8
8
  /**
9
9
  * @inheritDoc
10
10
  */
@@ -75,7 +75,7 @@ export interface NamingStrategy {
75
75
  /**
76
76
  * Returns key/constraint name for the given type. Some drivers might not support all the types (e.g. mysql and sqlite enforce the PK name).
77
77
  */
78
- indexName(tableName: string, columns: string[], type: 'primary' | 'foreign' | 'unique' | 'index' | 'sequence' | 'check' | 'default'): string;
78
+ indexName(tableName: string, columns: string[], type: 'primary' | 'foreign' | 'unique' | 'index' | 'sequence' | 'check' | 'default' | 'trigger'): string;
79
79
  /**
80
80
  * Returns alias name for given entity. The alias needs to be unique across the query, which is by default
81
81
  * ensured via appended index parameter. It is optional to use it as long as you ensure it will be unique.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.1.0-dev.3",
3
+ "version": "7.1.0-dev.31",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "keywords": [
6
6
  "data-mapper",
@@ -43,6 +43,8 @@ export declare abstract class Platform {
43
43
  getEnumArrayCheckConstraintExpression(column: string, items: string[]): string | null;
44
44
  /** Whether this platform supports materialized views. */
45
45
  supportsMaterializedViews(): boolean;
46
+ /** Whether this platform supports declarative table partitioning in schema generation. */
47
+ supportsPartitionedTables(): boolean;
46
48
  /** Returns the schema helper instance for this platform, or undefined if not supported. */
47
49
  getSchemaHelper(): unknown;
48
50
  /** Whether the platform automatically creates indexes on foreign key columns. */
@@ -168,7 +170,7 @@ export declare abstract class Platform {
168
170
  getUuidTypeDeclarationSQL(column: {
169
171
  length?: number;
170
172
  }): string;
171
- /** Extracts the base type name from a full SQL type declaration (e.g. "varchar(255)" -> "varchar"). */
173
+ /** Extracts the base type name from a full SQL type declaration (e.g. `varchar(255)` -> `varchar`). */
172
174
  extractSimpleType(type: string): string;
173
175
  /**
174
176
  * This should be used only to compare types, it can strip some information like the length.
@@ -211,6 +213,11 @@ export declare abstract class Platform {
211
213
  getFullTextWhereClause(prop: EntityProperty): string;
212
214
  supportsCreatingFullTextIndex(): boolean;
213
215
  getFullTextIndexExpression(indexName: string, schemaName: string | undefined, tableName: string, columns: SimpleColumnMeta[]): string;
216
+ /**
217
+ * Generates the SQL index hint clause for the given index names.
218
+ * Returns `undefined` when the platform does not support index hints (e.g. PostgreSQL, SQLite).
219
+ */
220
+ formatIndexHint(indexNames: string[]): string | undefined;
214
221
  /** Whether the driver automatically parses JSON columns into JS objects. */
215
222
  convertsJsonAutomatically(): boolean;
216
223
  /** Converts a JS value to its JSON database representation (typically JSON.stringify). */
@@ -2,12 +2,22 @@ import { clone } from '../utils/clone.js';
2
2
  import { EntityRepository } from '../entity/EntityRepository.js';
3
3
  import { UnderscoreNamingStrategy } from '../naming-strategy/UnderscoreNamingStrategy.js';
4
4
  import { ExceptionConverter } from './ExceptionConverter.js';
5
+ import { MetadataError } from '../errors.js';
5
6
  import { ArrayType, BigIntType, BlobType, BooleanType, CharacterType, DateTimeType, DateType, DecimalType, DoubleType, EnumType, FloatType, IntegerType, IntervalType, JsonType, MediumIntType, SmallIntType, StringType, TextType, TimeType, TinyIntType, Type, Uint8ArrayType, UnknownType, UuidType, } from '../types/index.js';
6
7
  import { parseJsonSafe, Utils } from '../utils/Utils.js';
7
8
  import { ReferenceKind } from '../enums.js';
8
9
  import { Raw } from '../utils/RawQueryFragment.js';
9
10
  /** Symbol used to tag cloned embeddable data for JSON serialization handling. */
10
11
  export const JsonProperty = Symbol('JsonProperty');
12
+ const MULTI_WORD_SQL_TYPES = [
13
+ 'timestamp with time zone',
14
+ 'timestamp without time zone',
15
+ 'time with time zone',
16
+ 'time without time zone',
17
+ 'character varying',
18
+ 'double precision',
19
+ 'bit varying',
20
+ ];
11
21
  /** Abstract base class providing database-specific behavior and SQL dialect differences. */
12
22
  export class Platform {
13
23
  exceptionConverter = new ExceptionConverter();
@@ -62,6 +72,10 @@ export class Platform {
62
72
  supportsMaterializedViews() {
63
73
  return false;
64
74
  }
75
+ /** Whether this platform supports declarative table partitioning in schema generation. */
76
+ supportsPartitionedTables() {
77
+ return false;
78
+ }
65
79
  /** Returns the schema helper instance for this platform, or undefined if not supported. */
66
80
  getSchemaHelper() {
67
81
  return undefined;
@@ -222,9 +236,15 @@ export class Platform {
222
236
  column.length ??= 36;
223
237
  return this.getVarcharTypeDeclarationSQL(column);
224
238
  }
225
- /** Extracts the base type name from a full SQL type declaration (e.g. "varchar(255)" -> "varchar"). */
239
+ /** Extracts the base type name from a full SQL type declaration (e.g. `varchar(255)` -> `varchar`). */
226
240
  extractSimpleType(type) {
227
- return /[^(), ]+/.exec(type.toLowerCase())[0];
241
+ const lower = type.toLowerCase();
242
+ for (const multiWord of MULTI_WORD_SQL_TYPES) {
243
+ if (lower.startsWith(multiWord)) {
244
+ return multiWord;
245
+ }
246
+ }
247
+ return /[^(), ]+/.exec(lower)[0];
228
248
  }
229
249
  /**
230
250
  * This should be used only to compare types, it can strip some information like the length.
@@ -248,6 +268,7 @@ export class Platform {
248
268
  return Type.getType(CharacterType);
249
269
  case 'string':
250
270
  case 'varchar':
271
+ case 'character varying':
251
272
  return Type.getType(StringType);
252
273
  case 'interval':
253
274
  return Type.getType(IntervalType);
@@ -267,6 +288,7 @@ export class Platform {
267
288
  case 'float':
268
289
  return Type.getType(FloatType);
269
290
  case 'double':
291
+ case 'double precision':
270
292
  return Type.getType(DoubleType);
271
293
  case 'integer':
272
294
  return Type.getType(IntegerType);
@@ -286,8 +308,12 @@ export class Platform {
286
308
  return Type.getType(DateType);
287
309
  case 'datetime':
288
310
  case 'timestamp':
311
+ case 'timestamp with time zone':
312
+ case 'timestamp without time zone':
289
313
  return Type.getType(DateTimeType);
290
314
  case 'time':
315
+ case 'time with time zone':
316
+ case 'time without time zone':
291
317
  return Type.getType(TimeType);
292
318
  case 'object':
293
319
  case 'json':
@@ -415,6 +441,13 @@ export class Platform {
415
441
  getFullTextIndexExpression(indexName, schemaName, tableName, columns) {
416
442
  throw new Error('Full text searching is not supported by this driver.');
417
443
  }
444
+ /**
445
+ * Generates the SQL index hint clause for the given index names.
446
+ * Returns `undefined` when the platform does not support index hints (e.g. PostgreSQL, SQLite).
447
+ */
448
+ formatIndexHint(indexNames) {
449
+ return undefined;
450
+ }
418
451
  /** Whether the driver automatically parses JSON columns into JS objects. */
419
452
  convertsJsonAutomatically() {
420
453
  return true;
@@ -506,7 +539,8 @@ export class Platform {
506
539
  if (raw) {
507
540
  return this.formatQuery(raw.sql, raw.params);
508
541
  }
509
- return `${quote}${id.toString().replace('.', `${quote}.${quote}`)}${quote}`;
542
+ const escaped = id.toString().replaceAll(quote, quote + quote);
543
+ return `${quote}${escaped.replace('.', `${quote}.${quote}`)}${quote}`;
510
544
  }
511
545
  /** Quotes a literal value for safe embedding in SQL. */
512
546
  quoteValue(value) {
@@ -651,7 +685,9 @@ export class Platform {
651
685
  }
652
686
  /** Platform-specific validation of entity metadata. */
653
687
  validateMetadata(meta) {
654
- return;
688
+ if (meta.partitionBy && !this.supportsPartitionedTables()) {
689
+ throw new MetadataError(`Entity ${meta.className} uses partitionBy, but ${this.constructor.name} does not support partitioned tables`);
690
+ }
655
691
  }
656
692
  /**
657
693
  * Generates a custom order by statement given a set of in order values, eg.
@@ -16,7 +16,8 @@ function isVisible(meta, propName, options) {
16
16
  if (Array.isArray(options.fields) && options.fields.length > 0 && !matchesPath(options.fields, propName)) {
17
17
  return false;
18
18
  }
19
- if (Array.isArray(options.populate) && matchesPath(options.populate, propName)) {
19
+ if (Array.isArray(options.populate) &&
20
+ options.populate.find(item => item === propName || item.startsWith(propName + '.'))) {
20
21
  return true;
21
22
  }
22
23
  if (options.exclude?.find(item => item === propName)) {