typeorm 0.3.30 → 0.3.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 (47) hide show
  1. package/README.md +2 -2
  2. package/browser/data-source/BaseDataSourceOptions.d.ts +7 -20
  3. package/browser/data-source/BaseDataSourceOptions.js.map +1 -1
  4. package/browser/driver/types/InvalidFindOptionsWhereBehavior.d.ts +15 -0
  5. package/browser/driver/types/InvalidFindOptionsWhereBehavior.js +3 -0
  6. package/browser/driver/types/InvalidFindOptionsWhereBehavior.js.map +1 -0
  7. package/browser/entity-manager/EntityManager.d.ts +23 -0
  8. package/browser/entity-manager/EntityManager.js +79 -106
  9. package/browser/entity-manager/EntityManager.js.map +1 -1
  10. package/browser/metadata/types/ClosureTreeOptions.d.ts +1 -0
  11. package/browser/metadata/types/ClosureTreeOptions.js.map +1 -1
  12. package/browser/metadata-builder/ClosureJunctionEntityMetadataBuilder.js +6 -2
  13. package/browser/metadata-builder/ClosureJunctionEntityMetadataBuilder.js.map +1 -1
  14. package/browser/persistence/SubjectExecutor.js +0 -8
  15. package/browser/persistence/SubjectExecutor.js.map +1 -1
  16. package/browser/query-builder/QueryBuilder.js +17 -0
  17. package/browser/query-builder/QueryBuilder.js.map +1 -1
  18. package/browser/repository/TreeRepository.js +2 -2
  19. package/browser/repository/TreeRepository.js.map +1 -1
  20. package/browser/util/OrmUtils.d.ts +16 -9
  21. package/browser/util/OrmUtils.js +33 -13
  22. package/browser/util/OrmUtils.js.map +1 -1
  23. package/commands/MigrationGenerateCommand.d.ts +6 -0
  24. package/commands/MigrationGenerateCommand.js +13 -2
  25. package/commands/MigrationGenerateCommand.js.map +1 -1
  26. package/data-source/BaseDataSourceOptions.d.ts +7 -20
  27. package/data-source/BaseDataSourceOptions.js.map +1 -1
  28. package/driver/types/InvalidFindOptionsWhereBehavior.d.ts +15 -0
  29. package/driver/types/InvalidFindOptionsWhereBehavior.js +4 -0
  30. package/driver/types/InvalidFindOptionsWhereBehavior.js.map +1 -0
  31. package/entity-manager/EntityManager.d.ts +23 -0
  32. package/entity-manager/EntityManager.js +79 -106
  33. package/entity-manager/EntityManager.js.map +1 -1
  34. package/metadata/types/ClosureTreeOptions.d.ts +1 -0
  35. package/metadata/types/ClosureTreeOptions.js.map +1 -1
  36. package/metadata-builder/ClosureJunctionEntityMetadataBuilder.js +6 -2
  37. package/metadata-builder/ClosureJunctionEntityMetadataBuilder.js.map +1 -1
  38. package/package.json +18 -19
  39. package/persistence/SubjectExecutor.js +0 -8
  40. package/persistence/SubjectExecutor.js.map +1 -1
  41. package/query-builder/QueryBuilder.js +17 -0
  42. package/query-builder/QueryBuilder.js.map +1 -1
  43. package/repository/TreeRepository.js +2 -2
  44. package/repository/TreeRepository.js.map +1 -1
  45. package/util/OrmUtils.d.ts +16 -9
  46. package/util/OrmUtils.js +33 -13
  47. package/util/OrmUtils.js.map +1 -1
package/README.md CHANGED
@@ -10,8 +10,8 @@
10
10
  <br>
11
11
  <a href="https://www.npmjs.com/package/typeorm"><img src="https://img.shields.io/npm/v/typeorm" alt="NPM Version"/></a>
12
12
  <a href="https://www.npmjs.com/package/typeorm"><img src="https://img.shields.io/npm/dm/typeorm" alt="NPM Downloads"/></a>
13
- <a href="https://github.com/typeorm/typeorm/actions/workflows/tests.yml?query=branch%3Av0.3"><img src="https://github.com/typeorm/typeorm/actions/workflows/tests.yml/badge.svg?branch=v0.3" alt="Commit Validation"/></a>
14
- <a href="https://coveralls.io/github/typeorm/typeorm?branch=v0.3"><img src="https://coveralls.io/repos/github/typeorm/typeorm/badge.svg?branch=v0.3" alt="Coverage Status"/></a>
13
+ <a href="https://github.com/typeorm/typeorm/actions/workflows/tests.yml?query=branch%3Av0"><img src="https://github.com/typeorm/typeorm/actions/workflows/tests.yml/badge.svg?branch=v0" alt="Commit Validation"/></a>
14
+ <a href="https://coveralls.io/github/typeorm/typeorm?branch=v0"><img src="https://coveralls.io/repos/github/typeorm/typeorm/badge.svg?branch=v0" alt="Coverage Status"/></a>
15
15
  <a href=""><img src="https://img.shields.io/badge/License-MIT-teal.svg" alt="MIT License"/></a>
16
16
  <br>
17
17
  <br>
@@ -1,11 +1,12 @@
1
+ import { QueryResultCache } from "../cache/QueryResultCache";
2
+ import { MixedList } from "../common/MixedList";
3
+ import { DataSource } from "../data-source/DataSource";
4
+ import { DatabaseType } from "../driver/types/DatabaseType";
5
+ import type { InvalidFindOptionsWhereBehavior } from "../driver/types/InvalidFindOptionsWhereBehavior";
1
6
  import { EntitySchema } from "../entity-schema/EntitySchema";
7
+ import { Logger } from "../logger/Logger";
2
8
  import { LoggerOptions } from "../logger/LoggerOptions";
3
9
  import { NamingStrategyInterface } from "../naming-strategy/NamingStrategyInterface";
4
- import { DatabaseType } from "../driver/types/DatabaseType";
5
- import { Logger } from "../logger/Logger";
6
- import { DataSource } from "../data-source/DataSource";
7
- import { QueryResultCache } from "../cache/QueryResultCache";
8
- import { MixedList } from "../common/MixedList";
9
10
  /**
10
11
  * BaseDataSourceOptions is set of DataSourceOptions shared by all database types.
11
12
  */
@@ -169,19 +170,5 @@ export interface BaseDataSourceOptions {
169
170
  /**
170
171
  * Controls how null and undefined values are handled in find operations.
171
172
  */
172
- readonly invalidWhereValuesBehavior?: {
173
- /**
174
- * How to handle null values in where conditions.
175
- * - 'ignore': Skip null properties (default)
176
- * - 'sql-null': Transform null to SQL NULL
177
- * - 'throw': Throw an error when null is encountered
178
- */
179
- readonly null?: "ignore" | "sql-null" | "throw";
180
- /**
181
- * How to handle undefined values in where conditions.
182
- * - 'ignore': Skip undefined properties (default)
183
- * - 'throw': Throw an error when undefined is encountered
184
- */
185
- readonly undefined?: "ignore" | "throw";
186
- };
173
+ readonly invalidWhereValuesBehavior?: InvalidFindOptionsWhereBehavior;
187
174
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../browser/src/data-source/BaseDataSourceOptions.ts"],"names":[],"mappings":"","file":"BaseDataSourceOptions.js","sourcesContent":["import { EntitySchema } from \"../entity-schema/EntitySchema\"\nimport { LoggerOptions } from \"../logger/LoggerOptions\"\nimport { NamingStrategyInterface } from \"../naming-strategy/NamingStrategyInterface\"\nimport { DatabaseType } from \"../driver/types/DatabaseType\"\nimport { Logger } from \"../logger/Logger\"\nimport { DataSource } from \"../data-source/DataSource\"\nimport { QueryResultCache } from \"../cache/QueryResultCache\"\nimport { MixedList } from \"../common/MixedList\"\n\n/**\n * BaseDataSourceOptions is set of DataSourceOptions shared by all database types.\n */\nexport interface BaseDataSourceOptions {\n /**\n * Database type. This value is required.\n */\n readonly type: DatabaseType\n\n /**\n * Connection name. If connection name is not given then it will be called \"default\".\n * Different connections must have different names.\n *\n * @deprecated\n */\n readonly name?: string\n\n /**\n * Entities to be loaded for this connection.\n * Accepts both entity classes and directories where from entities need to be loaded.\n * Directories support glob patterns.\n */\n readonly entities?: MixedList<Function | string | EntitySchema>\n\n /**\n * Subscribers to be loaded for this connection.\n * Accepts both subscriber classes and directories where from subscribers need to be loaded.\n * Directories support glob patterns.\n */\n readonly subscribers?: MixedList<Function | string>\n\n /**\n * Migrations to be loaded for this connection.\n * Accepts both migration classes and glob patterns representing migration files.\n */\n readonly migrations?: MixedList<Function | string>\n\n /**\n * Migrations table name, in case of different name from \"migrations\".\n * Accepts single string name.\n */\n readonly migrationsTableName?: string\n\n /**\n * Transaction mode for migrations to run in\n */\n readonly migrationsTransactionMode?: \"all\" | \"none\" | \"each\"\n\n /**\n * Typeorm metadata table name, in case of different name from \"typeorm_metadata\".\n * Accepts single string name.\n */\n readonly metadataTableName?: string\n\n /**\n * Naming strategy to be used to name tables and columns in the database.\n */\n readonly namingStrategy?: NamingStrategyInterface\n\n /**\n * Logging options.\n */\n readonly logging?: LoggerOptions\n\n /**\n * Logger instance used to log queries and events in the ORM.\n */\n readonly logger?:\n | \"advanced-console\"\n | \"simple-console\"\n | \"formatted-console\"\n | \"file\"\n | \"debug\"\n | Logger\n\n /**\n * Maximum number of milliseconds query should be executed before logger log a warning.\n */\n readonly maxQueryExecutionTime?: number\n\n /**\n * Maximum number of clients the pool should contain.\n */\n readonly poolSize?: number\n\n /**\n * Indicates if database schema should be auto created on every application launch.\n * Be careful with this option and don't use this in production - otherwise you can lose production data.\n * This option is useful during debug and development.\n * Alternative to it, you can use CLI and run schema:sync command.\n *\n * Note that for MongoDB database it does not create schema, because MongoDB is schemaless.\n * Instead, it syncs just by creating indices.\n */\n readonly synchronize?: boolean\n\n /**\n * Indicates if migrations should be auto run on every application launch.\n * Alternative to it, you can use CLI and run migrations:run command.\n */\n readonly migrationsRun?: boolean\n\n /**\n * Drops the schema each time connection is being established.\n * Be careful with this option and don't use this in production - otherwise you'll lose all production data.\n * This option is useful during debug and development.\n */\n readonly dropSchema?: boolean\n\n /**\n * Prefix to use on all tables (collections) of this connection in the database.\n */\n readonly entityPrefix?: string\n\n /**\n * When creating new Entity instances, skip all constructors when true.\n */\n readonly entitySkipConstructor?: boolean\n\n /**\n * Extra connection options to be passed to the underlying driver.\n *\n * todo: deprecate this and move all database-specific types into hts own connection options object.\n */\n readonly extra?: any\n\n /**\n * Specifies how relations must be loaded - using \"joins\" or separate queries.\n * If you are loading too much data with nested joins it's better to load relations\n * using separate queries.\n *\n * Default strategy is \"join\", but this default can be changed here.\n * Also, strategy can be set per-query in FindOptions and QueryBuilder.\n */\n readonly relationLoadStrategy?: \"join\" | \"query\"\n\n /**\n * Optionally applied \"typename\" to the model.\n * If set, then each hydrated model will have this property with the target model / entity name inside.\n *\n * (works like a discriminator property).\n */\n readonly typename?: string\n\n /**\n * Holds reference to the baseDirectory where configuration file are expected.\n *\n * @internal\n */\n baseDirectory?: string\n\n /**\n * Allows to setup cache options.\n */\n readonly cache?:\n | boolean\n | {\n /**\n * Type of caching.\n *\n * - \"database\" means cached values will be stored in the separate table in database. This is default value.\n * - \"redis\" means cached values will be stored inside redis. You must provide redis connection options.\n */\n readonly type?:\n | \"database\"\n | \"redis\"\n | \"ioredis\"\n | \"ioredis/cluster\" // todo: add mongodb and other cache providers as well in the future\n\n /**\n * Factory function for custom cache providers that implement QueryResultCache.\n */\n readonly provider?: (connection: DataSource) => QueryResultCache\n\n /**\n * Configurable table name for \"database\" type cache.\n * Default value is \"query-result-cache\"\n */\n readonly tableName?: string\n\n /**\n * Used to provide redis connection options.\n */\n readonly options?: any\n\n /**\n * If set to true then queries (using find methods and QueryBuilder's methods) will always be cached.\n */\n readonly alwaysEnabled?: boolean\n\n /**\n * Time in milliseconds in which cache will expire.\n * This can be setup per-query.\n * Default value is 1000 which is equivalent to 1 second.\n */\n readonly duration?: number\n\n /**\n * Used to specify if cache errors should be ignored, and pass through the call to the Database.\n */\n readonly ignoreErrors?: boolean\n }\n\n /**\n * Allows automatic isolation of where clauses\n */\n readonly isolateWhereStatements?: boolean\n\n /**\n * Controls how null and undefined values are handled in find operations.\n */\n readonly invalidWhereValuesBehavior?: {\n /**\n * How to handle null values in where conditions.\n * - 'ignore': Skip null properties (default)\n * - 'sql-null': Transform null to SQL NULL\n * - 'throw': Throw an error when null is encountered\n */\n readonly null?: \"ignore\" | \"sql-null\" | \"throw\"\n\n /**\n * How to handle undefined values in where conditions.\n * - 'ignore': Skip undefined properties (default)\n * - 'throw': Throw an error when undefined is encountered\n */\n readonly undefined?: \"ignore\" | \"throw\"\n }\n}\n"],"sourceRoot":".."}
1
+ {"version":3,"sources":["../browser/src/data-source/BaseDataSourceOptions.ts"],"names":[],"mappings":"","file":"BaseDataSourceOptions.js","sourcesContent":["import { QueryResultCache } from \"../cache/QueryResultCache\"\nimport { MixedList } from \"../common/MixedList\"\nimport { DataSource } from \"../data-source/DataSource\"\nimport { DatabaseType } from \"../driver/types/DatabaseType\"\nimport type { InvalidFindOptionsWhereBehavior } from \"../driver/types/InvalidFindOptionsWhereBehavior\"\nimport { EntitySchema } from \"../entity-schema/EntitySchema\"\nimport { Logger } from \"../logger/Logger\"\nimport { LoggerOptions } from \"../logger/LoggerOptions\"\nimport { NamingStrategyInterface } from \"../naming-strategy/NamingStrategyInterface\"\n\n/**\n * BaseDataSourceOptions is set of DataSourceOptions shared by all database types.\n */\nexport interface BaseDataSourceOptions {\n /**\n * Database type. This value is required.\n */\n readonly type: DatabaseType\n\n /**\n * Connection name. If connection name is not given then it will be called \"default\".\n * Different connections must have different names.\n *\n * @deprecated\n */\n readonly name?: string\n\n /**\n * Entities to be loaded for this connection.\n * Accepts both entity classes and directories where from entities need to be loaded.\n * Directories support glob patterns.\n */\n readonly entities?: MixedList<Function | string | EntitySchema>\n\n /**\n * Subscribers to be loaded for this connection.\n * Accepts both subscriber classes and directories where from subscribers need to be loaded.\n * Directories support glob patterns.\n */\n readonly subscribers?: MixedList<Function | string>\n\n /**\n * Migrations to be loaded for this connection.\n * Accepts both migration classes and glob patterns representing migration files.\n */\n readonly migrations?: MixedList<Function | string>\n\n /**\n * Migrations table name, in case of different name from \"migrations\".\n * Accepts single string name.\n */\n readonly migrationsTableName?: string\n\n /**\n * Transaction mode for migrations to run in\n */\n readonly migrationsTransactionMode?: \"all\" | \"none\" | \"each\"\n\n /**\n * Typeorm metadata table name, in case of different name from \"typeorm_metadata\".\n * Accepts single string name.\n */\n readonly metadataTableName?: string\n\n /**\n * Naming strategy to be used to name tables and columns in the database.\n */\n readonly namingStrategy?: NamingStrategyInterface\n\n /**\n * Logging options.\n */\n readonly logging?: LoggerOptions\n\n /**\n * Logger instance used to log queries and events in the ORM.\n */\n readonly logger?:\n | \"advanced-console\"\n | \"simple-console\"\n | \"formatted-console\"\n | \"file\"\n | \"debug\"\n | Logger\n\n /**\n * Maximum number of milliseconds query should be executed before logger log a warning.\n */\n readonly maxQueryExecutionTime?: number\n\n /**\n * Maximum number of clients the pool should contain.\n */\n readonly poolSize?: number\n\n /**\n * Indicates if database schema should be auto created on every application launch.\n * Be careful with this option and don't use this in production - otherwise you can lose production data.\n * This option is useful during debug and development.\n * Alternative to it, you can use CLI and run schema:sync command.\n *\n * Note that for MongoDB database it does not create schema, because MongoDB is schemaless.\n * Instead, it syncs just by creating indices.\n */\n readonly synchronize?: boolean\n\n /**\n * Indicates if migrations should be auto run on every application launch.\n * Alternative to it, you can use CLI and run migrations:run command.\n */\n readonly migrationsRun?: boolean\n\n /**\n * Drops the schema each time connection is being established.\n * Be careful with this option and don't use this in production - otherwise you'll lose all production data.\n * This option is useful during debug and development.\n */\n readonly dropSchema?: boolean\n\n /**\n * Prefix to use on all tables (collections) of this connection in the database.\n */\n readonly entityPrefix?: string\n\n /**\n * When creating new Entity instances, skip all constructors when true.\n */\n readonly entitySkipConstructor?: boolean\n\n /**\n * Extra connection options to be passed to the underlying driver.\n *\n * todo: deprecate this and move all database-specific types into hts own connection options object.\n */\n readonly extra?: any\n\n /**\n * Specifies how relations must be loaded - using \"joins\" or separate queries.\n * If you are loading too much data with nested joins it's better to load relations\n * using separate queries.\n *\n * Default strategy is \"join\", but this default can be changed here.\n * Also, strategy can be set per-query in FindOptions and QueryBuilder.\n */\n readonly relationLoadStrategy?: \"join\" | \"query\"\n\n /**\n * Optionally applied \"typename\" to the model.\n * If set, then each hydrated model will have this property with the target model / entity name inside.\n *\n * (works like a discriminator property).\n */\n readonly typename?: string\n\n /**\n * Holds reference to the baseDirectory where configuration file are expected.\n *\n * @internal\n */\n baseDirectory?: string\n\n /**\n * Allows to setup cache options.\n */\n readonly cache?:\n | boolean\n | {\n /**\n * Type of caching.\n *\n * - \"database\" means cached values will be stored in the separate table in database. This is default value.\n * - \"redis\" means cached values will be stored inside redis. You must provide redis connection options.\n */\n readonly type?:\n | \"database\"\n | \"redis\"\n | \"ioredis\"\n | \"ioredis/cluster\" // todo: add mongodb and other cache providers as well in the future\n\n /**\n * Factory function for custom cache providers that implement QueryResultCache.\n */\n readonly provider?: (connection: DataSource) => QueryResultCache\n\n /**\n * Configurable table name for \"database\" type cache.\n * Default value is \"query-result-cache\"\n */\n readonly tableName?: string\n\n /**\n * Used to provide redis connection options.\n */\n readonly options?: any\n\n /**\n * If set to true then queries (using find methods and QueryBuilder's methods) will always be cached.\n */\n readonly alwaysEnabled?: boolean\n\n /**\n * Time in milliseconds in which cache will expire.\n * This can be setup per-query.\n * Default value is 1000 which is equivalent to 1 second.\n */\n readonly duration?: number\n\n /**\n * Used to specify if cache errors should be ignored, and pass through the call to the Database.\n */\n readonly ignoreErrors?: boolean\n }\n\n /**\n * Allows automatic isolation of where clauses\n */\n readonly isolateWhereStatements?: boolean\n\n /**\n * Controls how null and undefined values are handled in find operations.\n */\n readonly invalidWhereValuesBehavior?: InvalidFindOptionsWhereBehavior\n}\n"],"sourceRoot":".."}
@@ -0,0 +1,15 @@
1
+ export type InvalidFindOptionsWhereBehavior = {
2
+ /**
3
+ * How to handle null values in where conditions.
4
+ * - 'ignore': Skip null properties (default)
5
+ * - 'sql-null': Transform null to SQL NULL
6
+ * - 'throw': Throw an error when null is encountered
7
+ */
8
+ readonly null?: "ignore" | "sql-null" | "throw";
9
+ /**
10
+ * How to handle undefined values in where conditions.
11
+ * - 'ignore': Skip undefined properties (default)
12
+ * - 'throw': Throw an error when undefined is encountered
13
+ */
14
+ readonly undefined?: "ignore" | "throw";
15
+ };
@@ -0,0 +1,3 @@
1
+ export {};
2
+
3
+ //# sourceMappingURL=InvalidFindOptionsWhereBehavior.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../browser/src/driver/types/InvalidFindOptionsWhereBehavior.ts"],"names":[],"mappings":"","file":"InvalidFindOptionsWhereBehavior.js","sourcesContent":["export type InvalidFindOptionsWhereBehavior = {\n /**\n * How to handle null values in where conditions.\n * - 'ignore': Skip null properties (default)\n * - 'sql-null': Transform null to SQL NULL\n * - 'throw': Throw an error when null is encountered\n */\n readonly null?: \"ignore\" | \"sql-null\" | \"throw\"\n\n /**\n * How to handle undefined values in where conditions.\n * - 'ignore': Skip undefined properties (default)\n * - 'throw': Throw an error when undefined is encountered\n */\n readonly undefined?: \"ignore\" | \"throw\"\n}\n"],"sourceRoot":"../.."}
@@ -212,6 +212,23 @@ export declare class EntityManager {
212
212
  */
213
213
  insert<Entity extends ObjectLiteral>(target: EntityTarget<Entity>, entity: QueryDeepPartialEntity<Entity> | QueryDeepPartialEntity<Entity>[]): Promise<InsertResult>;
214
214
  upsert<Entity extends ObjectLiteral>(target: EntityTarget<Entity>, entityOrEntities: QueryDeepPartialEntity<Entity> | QueryDeepPartialEntity<Entity>[], conflictPathsOrOptions: string[] | UpsertOptions<Entity>): Promise<InsertResult>;
215
+ /**
216
+ * Shared by update/delete/softDelete/restore. Object criteria is normalized
217
+ * via {@link OrmUtils.normalizeWhereCriteria}, then rejected if it would
218
+ * produce no predicate and therefore render as an always-true `WHERE 1=1` —
219
+ * an empty object or array, an empty OR-branch, a bare primitive inside an
220
+ * OR-array, or a keyless object. Primitive id criteria is returned untouched
221
+ * for `whereInIds` (rejected only when wholly empty).
222
+ *
223
+ * @param criteria the raw criteria passed to the operation
224
+ * @param methodName the calling method, used in the error message
225
+ * @returns the criteria to build with, and whether to execute it via
226
+ * `whereInIds` (primitive) or `where` (object)
227
+ */
228
+ protected normalizeAndValidateWhereCriteria(criteria: any, methodName: string): {
229
+ criteria: any;
230
+ isPrimitive: boolean;
231
+ };
215
232
  /**
216
233
  * Updates entity partially. Entity can be found by a given condition(s).
217
234
  * Unlike save method executes a primitive operation without cascades, relations and other operations included.
@@ -372,6 +389,12 @@ export declare class EntityManager {
372
389
  * Decrements some column by provided value of the entities matched given conditions.
373
390
  */
374
391
  decrement<Entity extends ObjectLiteral>(entityClass: EntityTarget<Entity>, conditions: any, propertyPath: string, value: number | string): Promise<UpdateResult>;
392
+ /**
393
+ * Shared implementation of {@link increment} and {@link decrement}: builds a
394
+ * `column = column +/- value` UPDATE and delegates execution to {@link update},
395
+ * so the criteria handling stays aligned with the other write methods.
396
+ */
397
+ protected incrementOrDecrementBy<Entity extends ObjectLiteral>(operation: "increment" | "decrement", entityClass: EntityTarget<Entity>, conditions: any, propertyPath: string, value: number | string): Promise<UpdateResult>;
375
398
  /**
376
399
  * Gets repository for the given entity class or name.
377
400
  * If single database connection mode is used, then repository is obtained from the
@@ -336,6 +336,51 @@ export class EntityManager {
336
336
  }
337
337
  return qb.execute();
338
338
  }
339
+ /**
340
+ * Shared by update/delete/softDelete/restore. Object criteria is normalized
341
+ * via {@link OrmUtils.normalizeWhereCriteria}, then rejected if it would
342
+ * produce no predicate and therefore render as an always-true `WHERE 1=1` —
343
+ * an empty object or array, an empty OR-branch, a bare primitive inside an
344
+ * OR-array, or a keyless object. Primitive id criteria is returned untouched
345
+ * for `whereInIds` (rejected only when wholly empty).
346
+ *
347
+ * @param criteria the raw criteria passed to the operation
348
+ * @param methodName the calling method, used in the error message
349
+ * @returns the criteria to build with, and whether to execute it via
350
+ * `whereInIds` (primitive) or `where` (object)
351
+ */
352
+ normalizeAndValidateWhereCriteria(criteria, methodName) {
353
+ const rejectEmpty = () => {
354
+ throw new TypeORMError(`Empty criteria(s) are not allowed for the ${methodName} method.`);
355
+ };
356
+ if (OrmUtils.isPrimitiveCriteria(criteria)) {
357
+ if (OrmUtils.isCriteriaNullOrEmpty(criteria))
358
+ rejectEmpty();
359
+ return { criteria, isPrimitive: true };
360
+ }
361
+ const normalizedCriteria = OrmUtils.normalizeWhereCriteria(criteria, this.connection.options.invalidWhereValuesBehavior);
362
+ // On the object-criteria path, `.where()` builds a predicate only from
363
+ // an object's own keys. Anything else yields an empty predicate list
364
+ // that renders as an always-true `1=1`, so a criterion is unsafe unless
365
+ // it is a non-empty object. This must reject:
366
+ // - primitives (a bare number/string in a mixed OR-array like
367
+ // `[1, { id: 2 }]` — `.where(1)` produces no predicate),
368
+ // - empty plain objects (`{}`), empty arrays (`[]`), and
369
+ // - empty non-plain objects, e.g. an empty entity instance
370
+ // (`new Post()`), which isCriteriaNullOrEmpty does not catch.
371
+ // Value-type criteria (Date, Buffer) execute via the primitive branch
372
+ // and never reach here.
373
+ const rendersNoPredicate = (value) => value === null ||
374
+ typeof value !== "object" ||
375
+ Object.keys(value).length === 0;
376
+ const isEmpty = Array.isArray(normalizedCriteria)
377
+ ? normalizedCriteria.length === 0 ||
378
+ normalizedCriteria.some(rendersNoPredicate)
379
+ : rendersNoPredicate(normalizedCriteria);
380
+ if (isEmpty)
381
+ rejectEmpty();
382
+ return { criteria: normalizedCriteria, isPrimitive: false };
383
+ }
339
384
  /**
340
385
  * Updates entity partially. Entity can be found by a given condition(s).
341
386
  * Unlike save method executes a primitive operation without cascades, relations and other operations included.
@@ -343,32 +388,19 @@ export class EntityManager {
343
388
  * Does not check if entity exist in the database.
344
389
  * Condition(s) cannot be empty.
345
390
  */
346
- update(target, criteria, partialEntity, options) {
347
- // if user passed empty criteria or empty list of criterias, then throw an error
348
- if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
349
- return Promise.reject(new TypeORMError(`Empty criteria(s) are not allowed for the update method.`));
350
- }
351
- if (OrmUtils.isPrimitiveCriteria(criteria)) {
352
- const qb = this.createQueryBuilder()
353
- .update(target)
354
- .set(partialEntity)
355
- .whereInIds(criteria);
356
- if (options?.returning !== undefined) {
357
- qb.returning(options.returning);
358
- }
359
- return qb.execute();
391
+ async update(target, criteria, partialEntity, options) {
392
+ const { criteria: whereCriteria, isPrimitive } = this.normalizeAndValidateWhereCriteria(criteria, "update");
393
+ const qb = this.createQueryBuilder().update(target).set(partialEntity);
394
+ if (isPrimitive) {
395
+ qb.whereInIds(whereCriteria);
360
396
  }
361
397
  else {
362
- const normalizedCriteria = OrmUtils.normalizeWhereCriteria(criteria, this.connection.options.invalidWhereValuesBehavior);
363
- const qb = this.createQueryBuilder()
364
- .update(target)
365
- .set(partialEntity)
366
- .where(normalizedCriteria);
367
- if (options?.returning !== undefined) {
368
- qb.returning(options.returning);
369
- }
370
- return qb.execute();
398
+ qb.where(whereCriteria);
399
+ }
400
+ if (options?.returning !== undefined) {
401
+ qb.returning(options.returning);
371
402
  }
403
+ return qb.execute();
372
404
  }
373
405
  /**
374
406
  * Updates all entities of target type, setting fields from supplied partial entity.
@@ -391,26 +423,10 @@ export class EntityManager {
391
423
  * Does not check if entity exist in the database.
392
424
  * Condition(s) cannot be empty.
393
425
  */
394
- delete(targetOrEntity, criteria) {
395
- // if user passed empty criteria or empty list of criterias, then throw an error
396
- if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
397
- return Promise.reject(new TypeORMError(`Empty criteria(s) are not allowed for the delete method.`));
398
- }
399
- if (OrmUtils.isPrimitiveCriteria(criteria)) {
400
- return this.createQueryBuilder()
401
- .delete()
402
- .from(targetOrEntity)
403
- .whereInIds(criteria)
404
- .execute();
405
- }
406
- else {
407
- const normalizedCriteria = OrmUtils.normalizeWhereCriteria(criteria, this.connection.options.invalidWhereValuesBehavior);
408
- return this.createQueryBuilder()
409
- .delete()
410
- .from(targetOrEntity)
411
- .where(normalizedCriteria)
412
- .execute();
413
- }
426
+ async delete(targetOrEntity, criteria) {
427
+ const { criteria: whereCriteria, isPrimitive } = this.normalizeAndValidateWhereCriteria(criteria, "delete");
428
+ const qb = this.createQueryBuilder().delete().from(targetOrEntity);
429
+ return (isPrimitive ? qb.whereInIds(whereCriteria) : qb.where(whereCriteria)).execute();
414
430
  }
415
431
  /**
416
432
  * Deletes all entities of target type.
@@ -429,26 +445,10 @@ export class EntityManager {
429
445
  * Does not check if entity exist in the database.
430
446
  * Condition(s) cannot be empty.
431
447
  */
432
- softDelete(targetOrEntity, criteria) {
433
- // if user passed empty criteria or empty list of criterias, then throw an error
434
- if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
435
- return Promise.reject(new TypeORMError(`Empty criteria(s) are not allowed for the softDelete method.`));
436
- }
437
- if (OrmUtils.isPrimitiveCriteria(criteria)) {
438
- return this.createQueryBuilder()
439
- .softDelete()
440
- .from(targetOrEntity)
441
- .whereInIds(criteria)
442
- .execute();
443
- }
444
- else {
445
- const normalizedCriteria = OrmUtils.normalizeWhereCriteria(criteria, this.connection.options.invalidWhereValuesBehavior);
446
- return this.createQueryBuilder()
447
- .softDelete()
448
- .from(targetOrEntity)
449
- .where(normalizedCriteria)
450
- .execute();
451
- }
448
+ async softDelete(targetOrEntity, criteria) {
449
+ const { criteria: whereCriteria, isPrimitive } = this.normalizeAndValidateWhereCriteria(criteria, "softDelete");
450
+ const qb = this.createQueryBuilder().softDelete().from(targetOrEntity);
451
+ return (isPrimitive ? qb.whereInIds(whereCriteria) : qb.where(whereCriteria)).execute();
452
452
  }
453
453
  /**
454
454
  * Restores entities by a given condition(s).
@@ -457,26 +457,10 @@ export class EntityManager {
457
457
  * Does not check if entity exist in the database.
458
458
  * Condition(s) cannot be empty.
459
459
  */
460
- restore(targetOrEntity, criteria) {
461
- // if user passed empty criteria or empty list of criterias, then throw an error
462
- if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
463
- return Promise.reject(new TypeORMError(`Empty criteria(s) are not allowed for the restore method.`));
464
- }
465
- if (OrmUtils.isPrimitiveCriteria(criteria)) {
466
- return this.createQueryBuilder()
467
- .restore()
468
- .from(targetOrEntity)
469
- .whereInIds(criteria)
470
- .execute();
471
- }
472
- else {
473
- const normalizedCriteria = OrmUtils.normalizeWhereCriteria(criteria, this.connection.options.invalidWhereValuesBehavior);
474
- return this.createQueryBuilder()
475
- .restore()
476
- .from(targetOrEntity)
477
- .where(normalizedCriteria)
478
- .execute();
479
- }
460
+ async restore(targetOrEntity, criteria) {
461
+ const { criteria: whereCriteria, isPrimitive } = this.normalizeAndValidateWhereCriteria(criteria, "restore");
462
+ const qb = this.createQueryBuilder().restore().from(targetOrEntity);
463
+ return (isPrimitive ? qb.whereInIds(whereCriteria) : qb.where(whereCriteria)).execute();
480
464
  }
481
465
  /**
482
466
  * Checks whether any entity exists with the given options.
@@ -718,45 +702,34 @@ export class EntityManager {
718
702
  * Increments some column by provided value of the entities matched given conditions.
719
703
  */
720
704
  async increment(entityClass, conditions, propertyPath, value) {
721
- const metadata = this.connection.getMetadata(entityClass);
722
- const column = metadata.findColumnWithPropertyPath(propertyPath);
723
- if (!column)
724
- throw new TypeORMError(`Column ${propertyPath} was not found in ${metadata.targetName} entity.`);
725
- if (isNaN(Number(value)))
726
- throw new TypeORMError(`Value "${value}" is not a number.`);
727
- // convert possible embedded path "social.likes" into object { social: { like: () => value } }
728
- const values = propertyPath
729
- .split(".")
730
- .reduceRight((value, key) => ({ [key]: value }), () => this.connection.driver.escape(column.databaseName) +
731
- " + " +
732
- value);
733
- return this.createQueryBuilder(entityClass, "entity")
734
- .update(entityClass)
735
- .set(values)
736
- .where(conditions)
737
- .execute();
705
+ return this.incrementOrDecrementBy("increment", entityClass, conditions, propertyPath, value);
738
706
  }
739
707
  /**
740
708
  * Decrements some column by provided value of the entities matched given conditions.
741
709
  */
742
710
  async decrement(entityClass, conditions, propertyPath, value) {
711
+ return this.incrementOrDecrementBy("decrement", entityClass, conditions, propertyPath, value);
712
+ }
713
+ /**
714
+ * Shared implementation of {@link increment} and {@link decrement}: builds a
715
+ * `column = column +/- value` UPDATE and delegates execution to {@link update},
716
+ * so the criteria handling stays aligned with the other write methods.
717
+ */
718
+ incrementOrDecrementBy(operation, entityClass, conditions, propertyPath, value) {
743
719
  const metadata = this.connection.getMetadata(entityClass);
744
720
  const column = metadata.findColumnWithPropertyPath(propertyPath);
745
721
  if (!column)
746
722
  throw new TypeORMError(`Column ${propertyPath} was not found in ${metadata.targetName} entity.`);
747
723
  if (isNaN(Number(value)))
748
724
  throw new TypeORMError(`Value "${value}" is not a number.`);
725
+ const operator = operation === "increment" ? "+" : "-";
749
726
  // convert possible embedded path "social.likes" into object { social: { like: () => value } }
750
727
  const values = propertyPath
751
728
  .split(".")
752
729
  .reduceRight((value, key) => ({ [key]: value }), () => this.connection.driver.escape(column.databaseName) +
753
- " - " +
730
+ ` ${operator} ` +
754
731
  value);
755
- return this.createQueryBuilder(entityClass, "entity")
756
- .update(entityClass)
757
- .set(values)
758
- .where(conditions)
759
- .execute();
732
+ return this.update(entityClass, conditions, values);
760
733
  }
761
734
  /**
762
735
  * Gets repository for the given entity class or name.