@mikro-orm/core 7.0.0-dev.169 → 7.0.0-dev.170

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/errors.d.ts CHANGED
@@ -66,6 +66,7 @@ export declare class MetadataError<T extends AnyEntity = AnyEntity> extends Vali
66
66
  static targetKeyNotUnique(meta: EntityMetadata, prop: EntityProperty): MetadataError<Partial<any>>;
67
67
  static targetKeyNotFound(meta: EntityMetadata, prop: EntityProperty): MetadataError<Partial<any>>;
68
68
  static dangerousPropertyName(meta: EntityMetadata, prop: EntityProperty): MetadataError<Partial<any>>;
69
+ static viewEntityWithoutExpression(meta: EntityMetadata): MetadataError;
69
70
  private static fromMessage;
70
71
  }
71
72
  export declare class NotFoundError<T extends AnyEntity = AnyEntity> extends ValidationError<T> {
package/errors.js CHANGED
@@ -225,6 +225,9 @@ export class MetadataError extends ValidationError {
225
225
  static dangerousPropertyName(meta, prop) {
226
226
  return this.fromMessage(meta, prop, `uses a dangerous property name '${prop.name}' which could lead to prototype pollution. Please use a different property name.`);
227
227
  }
228
+ static viewEntityWithoutExpression(meta) {
229
+ return new MetadataError(`View entity ${meta.className} is missing 'expression'. View entities must have an expression defining the SQL query.`);
230
+ }
228
231
  static fromMessage(meta, prop, message) {
229
232
  return new MetadataError(`${meta.className}.${prop.name} ${message}`);
230
233
  }
@@ -24,4 +24,9 @@ export declare class MetadataValidator {
24
24
  * @internal
25
25
  */
26
26
  private validatePropertyNames;
27
+ /**
28
+ * Validates view entity configuration.
29
+ * View entities must have an expression.
30
+ */
31
+ private validateViewEntity;
27
32
  }
@@ -19,7 +19,15 @@ const DANGEROUS_PROPERTY_NAMES = ['__proto__', 'constructor', 'prototype'];
19
19
  export class MetadataValidator {
20
20
  validateEntityDefinition(metadata, name, options) {
21
21
  const meta = metadata.get(name);
22
- if (meta.virtual || meta.expression) {
22
+ // View entities (expression with view flag) behave like regular tables but are read-only
23
+ // They can have primary keys and are created as actual database views
24
+ if (meta.view) {
25
+ this.validateViewEntity(meta);
26
+ return;
27
+ }
28
+ // Virtual entities (expression without view flag) have restrictions - no PKs, limited relation types
29
+ // Note: meta.virtual is set later in sync(), so we check for expression && !view here
30
+ if (meta.virtual || (meta.expression && !meta.view)) {
23
31
  for (const prop of Utils.values(meta.properties)) {
24
32
  if (![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED, ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
25
33
  throw new MetadataError(`Only scalars, embedded properties and to-many relations are allowed inside virtual entity. Found '${prop.kind}' in ${meta.className}.${prop.name}`);
@@ -240,4 +248,19 @@ export class MetadataValidator {
240
248
  }
241
249
  }
242
250
  }
251
+ /**
252
+ * Validates view entity configuration.
253
+ * View entities must have an expression.
254
+ */
255
+ validateViewEntity(meta) {
256
+ // View entities must have an expression
257
+ if (!meta.expression) {
258
+ throw MetadataError.viewEntityWithoutExpression(meta);
259
+ }
260
+ // Validate indexes if present
261
+ this.validateIndexes(meta, meta.indexes ?? [], 'index');
262
+ this.validateIndexes(meta, meta.uniques ?? [], 'unique');
263
+ // Validate property names
264
+ this.validatePropertyNames(meta);
265
+ }
243
266
  }
@@ -27,11 +27,17 @@ export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
27
27
  abstract?: boolean;
28
28
  /** Disables change tracking - such entities are ignored during flush. */
29
29
  readonly?: boolean;
30
- /** Marks entity as {@doclink virtual-entities | virtual}. This is set automatically when you use `expression` option. */
30
+ /** Marks entity as {@doclink virtual-entities | virtual}. This is set automatically when you use `expression` option (unless `view` is set). */
31
31
  virtual?: boolean;
32
+ /**
33
+ * Marks entity as a database view. Unlike virtual entities which evaluate expressions at query time,
34
+ * view entities create actual database views. The `expression` option must be provided when `view` is true.
35
+ * View entities are read-only by default.
36
+ */
37
+ view?: boolean;
32
38
  /** Used to make ORM aware of externally defined triggers. This is needed for MS SQL Server multi inserts, ignored in other dialects. */
33
39
  hasTriggers?: boolean;
34
- /** SQL query that maps to a {@doclink virtual-entities | virtual entity}. */
40
+ /** SQL query that maps to a {@doclink virtual-entities | virtual entity}, or for view entities, the view definition. */
35
41
  expression?: string | ((em: any, where: ObjectQuery<E>, options: FindOptions<E, any, any, any>, stream?: boolean) => object);
36
42
  /** Set {@doclink repositories#custom-repository | custom repository class}. */
37
43
  repository?: () => Constructor;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
3
  "type": "module",
4
- "version": "7.0.0-dev.169",
4
+ "version": "7.0.0-dev.170",
5
5
  "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.",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",
package/typings.d.ts CHANGED
@@ -472,6 +472,8 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
472
472
  schema?: string;
473
473
  pivotTable?: boolean;
474
474
  virtual?: boolean;
475
+ /** True if this entity represents a database view (not a virtual entity). */
476
+ view?: boolean;
475
477
  expression?: string | ((em: any, where: ObjectQuery<Entity>, options: FindOptions<Entity, any, any, any>, stream?: boolean) => MaybePromise<Raw | object | string>);
476
478
  discriminatorColumn?: EntityKey<Entity> | AnyString;
477
479
  discriminatorValue?: number | string;
package/typings.js CHANGED
@@ -109,7 +109,9 @@ export class EntityMetadata {
109
109
  return this.root.uniqueName === prop.targetMeta?.root.uniqueName;
110
110
  });
111
111
  this.hasUniqueProps = this.uniques.length + this.uniqueProps.length > 0;
112
- this.virtual = !!this.expression;
112
+ // If `view` is set, this is a database view entity (not a virtual entity).
113
+ // Virtual entities evaluate expressions at query time, view entities create actual database views.
114
+ this.virtual = !!this.expression && !this.view;
113
115
  if (config) {
114
116
  for (const prop of this.props) {
115
117
  if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
@@ -132,7 +134,7 @@ export class EntityMetadata {
132
134
  for (const hook of Utils.keys(this.hooks)) {
133
135
  this.hooks[hook] = Utils.removeDuplicates(this.hooks[hook]);
134
136
  }
135
- if (this.virtual) {
137
+ if (this.virtual || this.view) {
136
138
  this.readonly = true;
137
139
  }
138
140
  if (initIndexes && this.name) {
package/utils/Utils.js CHANGED
@@ -123,7 +123,7 @@ export function parseJsonSafe(value) {
123
123
  }
124
124
  export class Utils {
125
125
  static PK_SEPARATOR = '~~~';
126
- static #ORM_VERSION = '7.0.0-dev.169';
126
+ static #ORM_VERSION = '7.0.0-dev.170';
127
127
  /**
128
128
  * Checks if the argument is instance of `Object`. Returns false for arrays.
129
129
  */