typeorm-extensions 2.2.0-rc.7 → 2.2.0-rc.9

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 (70) hide show
  1. package/README.md +70 -39
  2. package/dist/decorators/generated-column.decorator.d.ts +17 -0
  3. package/dist/decorators/generated-column.decorator.js +15 -0
  4. package/dist/decorators/index.d.ts +1 -0
  5. package/dist/decorators/index.js +1 -0
  6. package/dist/extensions/calculated-column.extension.d.ts +3 -3
  7. package/dist/extensions/calculated-column.extension.js +3 -16
  8. package/dist/extensions/cursor-pagination.extension.js +7 -13
  9. package/dist/extensions/get-many-with-totals.extension.js +9 -32
  10. package/dist/extensions/join-lateral.extension.js +4 -6
  11. package/dist/extensions/period-filter.extension.js +1 -11
  12. package/dist/extensions/typed/index.d.ts +1 -0
  13. package/dist/extensions/typed/index.js +1 -0
  14. package/dist/extensions/typed/typed-calculated/typed-calculated-column.extension.d.ts +25 -0
  15. package/dist/extensions/typed/typed-calculated/typed-calculated-column.extension.js +24 -0
  16. package/dist/extensions/typed/typed-having/typed-having.extension.js +1 -1
  17. package/dist/extensions/typed/typed-joins/typed-join-and-map.extension.js +11 -10
  18. package/dist/extensions/typed/typed-joins/typed-joins.extension.js +11 -22
  19. package/dist/extensions/typed/typed-order-by/typed-order-by.extension.js +11 -17
  20. package/dist/extensions/typed/typed-returning/typed-returning.extension.js +1 -9
  21. package/dist/extensions/typed/typed-select/typed-select.extension.js +2 -8
  22. package/dist/extensions/typed/typed-where/typed-period-filter.extension.js +1 -11
  23. package/dist/extensions/typed/typed-where/typed-where-array.extension.js +4 -18
  24. package/dist/extensions/typed/typed-where/typed-where-ilike.extension.js +1 -11
  25. package/dist/extensions/typed/typed-where/typed-where-is-any.extension.js +3 -22
  26. package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.js +3 -22
  27. package/dist/extensions/typed/typed-where/typed-where-is-in.extension.js +3 -23
  28. package/dist/extensions/typed/typed-where/typed-where-json.extension.js +4 -23
  29. package/dist/extensions/typed/typed-where/typed-where-lower.extension.js +1 -11
  30. package/dist/extensions/typed/typed-where/typed-where-similar.extension.js +1 -10
  31. package/dist/extensions/typed/typed-where/typed-where.extension.js +2 -12
  32. package/dist/extensions/union.extension.js +1 -1
  33. package/dist/extensions/where-array.extension.js +1 -11
  34. package/dist/extensions/where-exists.extension.js +4 -15
  35. package/dist/extensions/where-is-any.extension.d.ts +38 -160
  36. package/dist/extensions/where-is-any.extension.js +1 -11
  37. package/dist/extensions/where-is-distinct-from.extension.js +1 -11
  38. package/dist/extensions/where-is-in.extension.d.ts +35 -145
  39. package/dist/extensions/where-is-in.extension.js +11 -29
  40. package/dist/extensions/where-json.extension.js +1 -11
  41. package/dist/extensions/where-relation-exists.extension.js +5 -25
  42. package/dist/helpers.d.ts +1 -0
  43. package/dist/helpers.js +2 -1
  44. package/dist/index.d.ts +1 -6
  45. package/dist/index.js +1 -12
  46. package/dist/types/extensions/cursor-pagination.types.d.ts +5 -0
  47. package/dist/types/extensions/get-many-with-totals.types.d.ts +4 -0
  48. package/dist/utils/calculated-column.utils.d.ts +8 -0
  49. package/dist/utils/calculated-column.utils.js +20 -3
  50. package/dist/utils/common.utils.d.ts +2 -0
  51. package/dist/utils/common.utils.js +4 -1
  52. package/dist/utils/cursor.utils.js +4 -6
  53. package/dist/utils/json-path.utils.js +2 -2
  54. package/dist/utils/pagination.utils.d.ts +7 -0
  55. package/dist/utils/pagination.utils.js +20 -10
  56. package/dist/utils/parameter.utils.d.ts +10 -0
  57. package/dist/utils/parameter.utils.js +22 -1
  58. package/dist/utils/period.utils.js +2 -1
  59. package/dist/utils/prototype.utils.d.ts +9 -0
  60. package/dist/utils/prototype.utils.js +12 -2
  61. package/dist/utils/query-builder.utils.d.ts +30 -2
  62. package/dist/utils/query-builder.utils.js +82 -44
  63. package/dist/utils/table.utils.js +9 -11
  64. package/dist/utils/time-zone.utils.d.ts +8 -0
  65. package/dist/utils/time-zone.utils.js +15 -5
  66. package/dist/utils/typed-where.utils.d.ts +9 -0
  67. package/dist/utils/typed-where.utils.js +17 -0
  68. package/dist/utils/where.utils.d.ts +2 -0
  69. package/dist/utils/where.utils.js +12 -14
  70. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import type { CursorOrderParam } from '../interfaces/cursor-pagination.interface';
2
+ import type { OrderType } from '../interfaces/ordering.interface';
2
3
  export type GetManyWithCursorOptions<Entity> = {
3
4
  /**
4
5
  * Columns that order the rows, e.g. `[{ field: 'createdAt', direction: 'DESC' }]`.
@@ -18,4 +19,8 @@ export interface ListWithCursor<Entity> {
18
19
  * keyset query loads `pageSize + 1` rows, so it needs no total. Equivalent to `nextCursor !== null`.
19
20
  */
20
21
  hasMore: boolean;
22
+ /** The page size used: the `pageSize` given, rounded down, or `10` when it was missing or below 1. */
23
+ pageSize: number;
24
+ /** The ORDER BY the page was read in: the `orderBy` columns, then the primary key columns not among them. */
25
+ orderBy: OrderType[];
21
26
  }
@@ -18,6 +18,10 @@ export type GetRawManyWithTotalsOptions = Omit<GetManyWithTotalsOptions, 'useTak
18
18
  export interface ListWithTotals<Entity> {
19
19
  list: Entity[];
20
20
  total: number | null;
21
+ /** The page read, 1-based: the `page` given, or `1` when it was missing or below 1. `null` with `loadAll`. */
22
+ page: number | null;
23
+ /** The page size used: the `pageSize` given, or `10` when it was missing or below 1. `null` with `loadAll`. */
24
+ pageSize: number | null;
21
25
  limit: number | null;
22
26
  offset: number | null;
23
27
  /**
@@ -1,10 +1,14 @@
1
1
  import { ObjectLiteral, SelectQueryBuilder } from 'typeorm';
2
+ import type { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
3
+ /** `addSelect`'s selection: an SQL expression, or a factory that builds a subquery on the builder it is given. */
4
+ export type CalculatedSelection<SubEntity extends ObjectLiteral = ObjectLiteral> = string | ((qb: SelectQueryBuilder<ObjectLiteral>) => SelectQueryBuilder<SubEntity>);
2
5
  type ResolvedCalculatedColumn = {
3
6
  /** The key TypeORM reads the column's value from when it builds the entity. */
4
7
  key: string;
5
8
  /** `Entity.property`, for error messages. */
6
9
  label: string;
7
10
  };
11
+ export declare const isCalculated: (column: ColumnMetadata | undefined) => column is ColumnMetadata;
8
12
  /** Whether `property` of the entity of `aliasName` (the main alias by default) is a `@CalculatedColumn`. */
9
13
  export declare function isCalculatedColumn<Entity extends ObjectLiteral>(builder: SelectQueryBuilder<Entity>, property: string, aliasName?: string): boolean;
10
14
  /** The `@CalculatedColumn` `property` of the entity of `aliasName`; throws a `TypeError` for anything else. */
@@ -18,4 +22,8 @@ export declare function resolveCalculatedColumn<Entity extends ObjectLiteral>(bu
18
22
  * through with `take` / `skip` and joins, which takes only select aliases it knows unquoted.
19
23
  */
20
24
  export declare function selectCalculatedForOrder<Entity extends ObjectLiteral>(builder: SelectQueryBuilder<Entity>, property: string, aliasName: string | undefined, methodName: string): string;
25
+ /** Selects `selection` into the `@CalculatedColumn` `property` of the entity of `aliasName` (the main alias by default). */
26
+ export declare function addSelectCalculatedColumn<Entity extends ObjectLiteral, SubEntity extends ObjectLiteral>(builder: SelectQueryBuilder<Entity>, selection: CalculatedSelection<SubEntity>, property: string, aliasName: string | undefined, methodName: string): SelectQueryBuilder<Entity>;
27
+ /** ORDER BY the value of a `@CalculatedColumn` selected with `addSelectCalculated`, through `orderBy` or `addOrderBy`. */
28
+ export declare function orderByCalculatedColumn<Entity extends ObjectLiteral>(builder: SelectQueryBuilder<Entity>, method: 'orderBy' | 'addOrderBy', property: string, order: 'ASC' | 'DESC' | undefined, nulls: 'NULLS FIRST' | 'NULLS LAST' | undefined, aliasName: string | undefined, methodName: string): SelectQueryBuilder<Entity>;
21
29
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.selectCalculatedForOrder = exports.resolveCalculatedColumn = exports.isCalculatedColumn = void 0;
3
+ exports.orderByCalculatedColumn = exports.addSelectCalculatedColumn = exports.selectCalculatedForOrder = exports.resolveCalculatedColumn = exports.isCalculatedColumn = exports.isCalculated = void 0;
4
4
  const DriverUtils_1 = require("typeorm/driver/DriverUtils");
5
5
  const sql_utils_1 = require("./sql.utils");
6
6
  function findCalculatedColumn(builder, property, aliasName) {
@@ -19,10 +19,11 @@ function findCalculatedColumn(builder, property, aliasName) {
19
19
  }
20
20
  // @CalculatedColumn: a virtual property without the query of TypeORM's @VirtualColumn
21
21
  const isCalculated = (column) => !!(column === null || column === void 0 ? void 0 : column.isVirtualProperty) && !column.query;
22
+ exports.isCalculated = isCalculated;
22
23
  /** Whether `property` of the entity of `aliasName` (the main alias by default) is a `@CalculatedColumn`. */
23
24
  function isCalculatedColumn(builder, property, aliasName) {
24
25
  var _a;
25
- return isCalculated((_a = findCalculatedColumn(builder, property, aliasName)) === null || _a === void 0 ? void 0 : _a.column);
26
+ return (0, exports.isCalculated)((_a = findCalculatedColumn(builder, property, aliasName)) === null || _a === void 0 ? void 0 : _a.column);
26
27
  }
27
28
  exports.isCalculatedColumn = isCalculatedColumn;
28
29
  /** The `@CalculatedColumn` `property` of the entity of `aliasName`; throws a `TypeError` for anything else. */
@@ -33,7 +34,7 @@ function resolveCalculatedColumn(builder, property, aliasName, methodName) {
33
34
  throw new TypeError(`${methodName}: the alias "${aliasName !== null && aliasName !== void 0 ? aliasName : (_a = builder.expressionMap.mainAlias) === null || _a === void 0 ? void 0 : _a.name}" has no entity with "${property}"`);
34
35
  }
35
36
  const label = `${found.entityName}.${property}`;
36
- if (!isCalculated(found.column)) {
37
+ if (!(0, exports.isCalculated)(found.column)) {
37
38
  throw new TypeError(`${methodName}: ${label} is not a @CalculatedColumn`);
38
39
  }
39
40
  // shortened past the driver's alias length limit, as TypeORM does it
@@ -61,3 +62,19 @@ function selectCalculatedForOrder(builder, property, aliasName, methodName) {
61
62
  return orderAlias;
62
63
  }
63
64
  exports.selectCalculatedForOrder = selectCalculatedForOrder;
65
+ /** Selects `selection` into the `@CalculatedColumn` `property` of the entity of `aliasName` (the main alias by default). */
66
+ function addSelectCalculatedColumn(builder, selection, property, aliasName, methodName) {
67
+ const { key } = resolveCalculatedColumn(builder, property, aliasName, methodName);
68
+ // two calls: addSelect's overloads take a string or a factory, not their union
69
+ if (typeof selection === 'function') {
70
+ return builder.addSelect(selection, key);
71
+ }
72
+ return builder.addSelect(selection, key);
73
+ }
74
+ exports.addSelectCalculatedColumn = addSelectCalculatedColumn;
75
+ /** ORDER BY the value of a `@CalculatedColumn` selected with `addSelectCalculated`, through `orderBy` or `addOrderBy`. */
76
+ function orderByCalculatedColumn(builder, method, property, order, nulls, aliasName, methodName) {
77
+ const orderAlias = selectCalculatedForOrder(builder, property, aliasName, methodName);
78
+ return builder[method](orderAlias, order, nulls);
79
+ }
80
+ exports.orderByCalculatedColumn = orderByCalculatedColumn;
@@ -1,2 +1,4 @@
1
+ /** A Date, also one from another realm (a vm context, a test runner's sandbox), where `instanceof Date` is false. */
2
+ export declare const isDate: (value: unknown) => value is Date;
1
3
  export declare const isNil: (val: unknown) => boolean;
2
4
  export declare function randomString(length?: number): string;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.randomString = exports.isNil = void 0;
3
+ exports.randomString = exports.isNil = exports.isDate = void 0;
4
+ /** A Date, also one from another realm (a vm context, a test runner's sandbox), where `instanceof Date` is false. */
5
+ const isDate = (value) => Object.prototype.toString.call(value) === '[object Date]';
6
+ exports.isDate = isDate;
4
7
  const isNil = (val) => typeof val === 'undefined' || val === null;
5
8
  exports.isNil = isNil;
6
9
  function randomString(length = 21) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildKeysetCondition = exports.getTextCastType = exports.supportsRowValueComparison = exports.decodeCursor = exports.encodeCursor = exports.InvalidCursorError = void 0;
4
4
  const sql_utils_1 = require("./sql.utils");
5
+ const common_utils_1 = require("./common.utils");
5
6
  /** A cursor that can't be decoded or doesn't belong to the query's order (e.g. tampered with). */
6
7
  class InvalidCursorError extends TypeError {
7
8
  constructor(message) {
@@ -10,9 +11,8 @@ class InvalidCursorError extends TypeError {
10
11
  }
11
12
  }
12
13
  exports.InvalidCursorError = InvalidCursorError;
13
- const isDate = (value) => Object.prototype.toString.call(value) === '[object Date]';
14
14
  const toCursorValue = (value) => {
15
- if (isDate(value)) {
15
+ if ((0, common_utils_1.isDate)(value)) {
16
16
  return { $date: value.toISOString() };
17
17
  }
18
18
  if (typeof value === 'bigint') {
@@ -77,9 +77,7 @@ function decodeCursor(cursor, fields) {
77
77
  }
78
78
  exports.decodeCursor = decodeCursor;
79
79
  /** Databases that compare row values: `(a, b) < (1, 2)`. */
80
- const supportsRowValueComparison = (driver) => driver === 'postgres' ||
81
- driver === 'aurora-postgres' ||
82
- driver === 'cockroachdb' ||
80
+ const supportsRowValueComparison = (driver) => (0, sql_utils_1.isPostgresLikeDriver)(driver) ||
83
81
  (0, sql_utils_1.isMysqlLikeDriver)(driver) ||
84
82
  (0, sql_utils_1.isSqliteLikeDriver)(driver);
85
83
  exports.supportsRowValueComparison = supportsRowValueComparison;
@@ -88,7 +86,7 @@ exports.supportsRowValueComparison = supportsRowValueComparison;
88
86
  * (PostgreSQL timestamps have microseconds, a JS Date only milliseconds). `undefined` when unknown.
89
87
  */
90
88
  const getTextCastType = (driver) => {
91
- if (driver === 'postgres' || driver === 'aurora-postgres' || driver === 'cockroachdb' || (0, sql_utils_1.isSqliteLikeDriver)(driver)) {
89
+ if ((0, sql_utils_1.isPostgresLikeDriver)(driver) || (0, sql_utils_1.isSqliteLikeDriver)(driver)) {
92
90
  return 'text';
93
91
  }
94
92
  if ((0, sql_utils_1.isMysqlLikeDriver)(driver)) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toJsonPathSql = exports.buildJsonPath = exports.isJsonColumn = void 0;
4
4
  const sql_utils_1 = require("./sql.utils");
5
+ const common_utils_1 = require("./common.utils");
5
6
  const JSON_COLUMN_TYPES = ['json', 'jsonb', 'simple-json'];
6
7
  const isJsonColumn = (column) => JSON_COLUMN_TYPES.includes(String(column.type));
7
8
  exports.isJsonColumn = isJsonColumn;
@@ -14,7 +15,6 @@ function buildJsonPath(fieldNames, jsonOperator) {
14
15
  .join(' ');
15
16
  }
16
17
  exports.buildJsonPath = buildJsonPath;
17
- const isDate = (value) => Object.prototype.toString.call(value) === '[object Date]';
18
18
  /**
19
19
  * PostgreSQL type to compare a `->>` value (text) with `value` as: text compares `'9' >= '10'` as true.
20
20
  * An array (IN, ANY) is typed by its first value; strings and unknown values stay text.
@@ -27,7 +27,7 @@ function getJsonTextCast(value) {
27
27
  if (typeof sample === 'boolean') {
28
28
  return 'boolean';
29
29
  }
30
- if (isDate(sample)) {
30
+ if ((0, common_utils_1.isDate)(sample)) {
31
31
  return 'timestamptz';
32
32
  }
33
33
  return undefined;
@@ -1,5 +1,12 @@
1
+ import type { ObjectLiteral, SelectQueryBuilder } from "typeorm";
1
2
  import { PaginationFilter } from "../types/interfaces/pagination.interface";
3
+ /** The page size to read: a number or a numeric string of at least 1, rounded down; `defaultPageSize` otherwise. */
4
+ export declare const normalizePageSize: (pageSize: unknown) => number;
5
+ /** Limits the rows with `take`, which counts entities rather than rows when the query joins a collection, or with `limit`. */
6
+ export declare const setPageLimit: <Entity extends ObjectLiteral>(builder: SelectQueryBuilder<Entity>, limit: number, useTakeAndSkip: boolean) => SelectQueryBuilder<Entity>;
2
7
  export declare const getLimitAndOffset: ({ page, pageSize }: PaginationFilter) => {
3
8
  limit: number;
4
9
  offset: number;
10
+ page: number;
11
+ pageSize: number;
5
12
  };
@@ -1,16 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getLimitAndOffset = void 0;
3
+ exports.getLimitAndOffset = exports.setPageLimit = exports.normalizePageSize = void 0;
4
4
  const constants_1 = require("../constants");
5
+ // an unparsed query parameter is a string: "20" + 1 would be "201"
6
+ const toPositiveInteger = (value, fallback) => {
7
+ const number = typeof value === 'string' && value.trim() !== '' ? Number(value) : value;
8
+ return typeof number === 'number' && Number.isFinite(number) && number >= 1 ? Math.floor(number) : fallback;
9
+ };
10
+ /** The page size to read: a number or a numeric string of at least 1, rounded down; `defaultPageSize` otherwise. */
11
+ const normalizePageSize = (pageSize) => toPositiveInteger(pageSize, constants_1.defaultPageSize);
12
+ exports.normalizePageSize = normalizePageSize;
13
+ /** Limits the rows with `take`, which counts entities rather than rows when the query joins a collection, or with `limit`. */
14
+ const setPageLimit = (builder, limit, useTakeAndSkip) => (useTakeAndSkip ? builder.take(limit) : builder.limit(limit));
15
+ exports.setPageLimit = setPageLimit;
5
16
  const getLimitAndOffset = ({ page, pageSize }) => {
6
- if (!page || page < 1) {
7
- page = 1;
8
- }
9
- if (!pageSize || pageSize < 1) {
10
- pageSize = constants_1.defaultPageSize;
11
- }
12
- const offset = (page >= 1 ? page - 1 : 0) * pageSize;
13
- const limit = pageSize;
14
- return { limit, offset };
17
+ const normalizedPage = toPositiveInteger(page, 1);
18
+ const normalizedPageSize = (0, exports.normalizePageSize)(pageSize);
19
+ return {
20
+ limit: normalizedPageSize,
21
+ offset: (normalizedPage - 1) * normalizedPageSize,
22
+ page: normalizedPage,
23
+ pageSize: normalizedPageSize,
24
+ };
15
25
  };
16
26
  exports.getLimitAndOffset = getLimitAndOffset;
@@ -1,7 +1,17 @@
1
1
  import type { ObjectLiteral } from 'typeorm';
2
+ /** The parameter names an SQL fragment refers to, as TypeORM reads its placeholders. */
3
+ export declare function getPlaceholderNames(sql: string): string[];
2
4
  /**
3
5
  * Renames the parameters of a subquery to unique names, in its SQL and in its parameters. A query
4
6
  * has one parameter namespace, so two subqueries with a `:status` each would otherwise both read
5
7
  * whichever value was set last. Placeholders that aren't keys of `parameters` stay as they are.
6
8
  */
7
9
  export declare function withUniqueParameterNames(sql: string, parameters: ObjectLiteral): [string, ObjectLiteral];
10
+ /**
11
+ * The SQL of a query, without the parentheses a `subQuery()` builder writes around itself, and its parameters, both
12
+ * renamed unique (see `withUniqueParameterNames`).
13
+ */
14
+ export declare function toSubQuerySql(query: {
15
+ getQuery(): string;
16
+ getParameters(): ObjectLiteral;
17
+ }): [string, ObjectLiteral];
@@ -1,9 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withUniqueParameterNames = void 0;
3
+ exports.toSubQuerySql = exports.withUniqueParameterNames = exports.getPlaceholderNames = void 0;
4
4
  const sql_utils_1 = require("./sql.utils");
5
5
  // TypeORM's placeholder syntax, `:name` and `:...name`, as its drivers match it
6
6
  const PARAMETER_PLACEHOLDER = /:(\.\.\.)?([A-Za-z0-9_.]+)/g;
7
+ /** The parameter names an SQL fragment refers to, as TypeORM reads its placeholders. */
8
+ function getPlaceholderNames(sql) {
9
+ // a regex of its own: a global regex keeps its position between exec calls
10
+ const placeholder = new RegExp(PARAMETER_PLACEHOLDER.source, 'g');
11
+ const names = [];
12
+ for (let match = placeholder.exec(sql); match !== null; match = placeholder.exec(sql)) {
13
+ names.push(match[2]);
14
+ }
15
+ return names;
16
+ }
17
+ exports.getPlaceholderNames = getPlaceholderNames;
7
18
  /**
8
19
  * Renames the parameters of a subquery to unique names, in its SQL and in its parameters. A query
9
20
  * has one parameter namespace, so two subqueries with a `:status` each would otherwise both read
@@ -19,3 +30,13 @@ function withUniqueParameterNames(sql, parameters) {
19
30
  return [renamedSql, renamedParameters];
20
31
  }
21
32
  exports.withUniqueParameterNames = withUniqueParameterNames;
33
+ /**
34
+ * The SQL of a query, without the parentheses a `subQuery()` builder writes around itself, and its parameters, both
35
+ * renamed unique (see `withUniqueParameterNames`).
36
+ */
37
+ function toSubQuerySql(query) {
38
+ const sql = query.getQuery();
39
+ const wrapped = 'expressionMap' in query && query.expressionMap.subQuery;
40
+ return withUniqueParameterNames(wrapped ? sql.slice(1, -1) : sql, query.getParameters());
41
+ }
42
+ exports.toSubQuerySql = toSubQuerySql;
@@ -4,6 +4,7 @@ exports.attachPeriodFilter = void 0;
4
4
  const sql_utils_1 = require("./sql.utils");
5
5
  const where_utils_1 = require("./where.utils");
6
6
  const time_zone_utils_1 = require("./time-zone.utils");
7
+ const common_utils_1 = require("./common.utils");
7
8
  const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
8
9
  /** `'2024-01-31'` as a UTC midnight Date, or a `TypeError` if it is not a calendar date (`'2024-02-30'`). */
9
10
  function parseDateOnly(value, name) {
@@ -18,7 +19,7 @@ function normalizeBoundary(value, name) {
18
19
  if (value === undefined || value === null || value === '') {
19
20
  return undefined;
20
21
  }
21
- if (Object.prototype.toString.call(value) === '[object Date]') {
22
+ if ((0, common_utils_1.isDate)(value)) {
22
23
  if (isNaN(value.getTime())) {
23
24
  throw new TypeError(`Invalid ${name}: invalid Date`);
24
25
  }
@@ -1,9 +1,18 @@
1
+ import { DeleteQueryBuilder, SelectQueryBuilder, UpdateQueryBuilder } from 'typeorm';
2
+ import { SoftDeleteQueryBuilder } from 'typeorm/query-builder/SoftDeleteQueryBuilder';
1
3
  export type PatchPrototypeOptions = {
2
4
  /** Leave a method the target already has, own or inherited: TypeORM's, or another library's. */
3
5
  onlyMissing?: boolean;
4
6
  };
7
+ /** The query builders with a WHERE clause, which the where extensions patch. */
8
+ export declare const WHERE_QUERY_BUILDERS: (typeof SelectQueryBuilder | typeof UpdateQueryBuilder | typeof DeleteQueryBuilder | typeof SoftDeleteQueryBuilder)[];
5
9
  export declare function patchPrototype<T extends {
6
10
  prototype: any;
7
11
  }, S extends {
8
12
  prototype: any;
9
13
  }>(target: T, source: S, options?: PatchPrototypeOptions): void;
14
+ export declare function patchPrototypes<S extends {
15
+ prototype: any;
16
+ }>(targets: ReadonlyArray<{
17
+ prototype: any;
18
+ }>, source: S, options?: PatchPrototypeOptions): void;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.patchPrototype = void 0;
3
+ exports.patchPrototypes = exports.patchPrototype = exports.WHERE_QUERY_BUILDERS = void 0;
4
+ /* eslint-disable @typescript-eslint/no-explicit-any */
5
+ const typeorm_1 = require("typeorm");
6
+ const SoftDeleteQueryBuilder_1 = require("typeorm/query-builder/SoftDeleteQueryBuilder");
7
+ /** The query builders with a WHERE clause, which the where extensions patch. */
8
+ exports.WHERE_QUERY_BUILDERS = [typeorm_1.SelectQueryBuilder, typeorm_1.UpdateQueryBuilder, typeorm_1.DeleteQueryBuilder, SoftDeleteQueryBuilder_1.SoftDeleteQueryBuilder];
5
9
  function patchPrototype(target, source, options = {}) {
6
10
  for (const method of Object.keys(source.prototype)) {
7
11
  if (options.onlyMissing && method in target.prototype) {
@@ -11,3 +15,9 @@ function patchPrototype(target, source, options = {}) {
11
15
  }
12
16
  }
13
17
  exports.patchPrototype = patchPrototype;
18
+ function patchPrototypes(targets, source, options) {
19
+ for (const target of targets) {
20
+ patchPrototype(target, source, options);
21
+ }
22
+ }
23
+ exports.patchPrototypes = patchPrototypes;
@@ -1,7 +1,17 @@
1
- import type { ObjectLiteral, WhereExpressionBuilder } from 'typeorm';
1
+ import { QueryBuilder } from 'typeorm';
2
+ import type { ObjectLiteral, QueryRunner, SelectQueryBuilder, WhereExpressionBuilder } from 'typeorm';
2
3
  import type { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
4
+ import type { Alias } from 'typeorm/query-builder/Alias';
3
5
  import type { KeyProxyCallback } from '../types/modules/proxy-callback.types';
6
+ import type { OrderType } from '../types/interfaces/ordering.interface';
7
+ import { KeyHolder } from './proxy-key.utils';
4
8
  import { JsonPathSqlOptions } from './json-path.utils';
9
+ /**
10
+ * The aliases of the query and of the queries it is part of, its own first. The builder of a `Brackets` callback
11
+ * has the outer query's main alias but an empty alias list: the aliases, joins included, are on its parent. A
12
+ * subquery sees the aliases of the outer query too.
13
+ */
14
+ export declare function listQueryAliases(builder: WhereExpressionBuilder | QueryBuilder<ObjectLiteral>): Alias[];
5
15
  /** Replaces placeholders of `deferColumnKey` in SQL built by `builder`, through the aliases it has by then. */
6
16
  export declare function resolveDeferredKeys(builder: WhereExpressionBuilder, statement: string): string;
7
17
  /**
@@ -48,6 +58,14 @@ export declare function isTypedJsonPath<Type extends ObjectLiteral>(builder: Whe
48
58
  * one condition per column has no single obvious meaning (AND? OR?), so don't guess.
49
59
  */
50
60
  export declare function getTypedSingleColumnKey<Type extends ObjectLiteral>(builder: WhereExpressionBuilder, callback: KeyProxyCallback<Type>, methodName: string, jsonOptions?: JsonPathSqlOptions): string;
61
+ /**
62
+ * The alias and the entity property a typed callback picks, for methods that take a property rather than a column
63
+ * (a `@CalculatedColumn`): `u => u.imagesCount` is on the main alias, `q => q.owner.imagesCount` on `owner`.
64
+ */
65
+ export declare function resolveTypedProperty<Type extends ObjectLiteral>(builder: WhereExpressionBuilder, callback: KeyProxyCallback<Type>, methodName: string): {
66
+ aliasName: string | undefined;
67
+ propertyPath: string;
68
+ };
51
69
  /**
52
70
  * Key for typed select / order / distinct / group by on a SelectQueryBuilder.
53
71
  *
@@ -59,4 +77,14 @@ export declare function getTypedSingleColumnKey<Type extends ObjectLiteral>(buil
59
77
  * PostgreSQL folds an unquoted `cte.firstName` to `firstname`.
60
78
  * Strings are kept as is.
61
79
  */
62
- export declare function getTypedSelectionKey<Type extends ObjectLiteral>(builder: WhereExpressionBuilder, selection: KeyProxyCallback<Type> | string, useQuotes?: boolean, columnsFor?: string): string | string[];
80
+ export declare function getTypedSelectionKey<Type extends ObjectLiteral>(builder: WhereExpressionBuilder, selection: KeyProxyCallback<Type> | KeyHolder | string, useQuotes?: boolean, columnsFor?: string): string | string[];
81
+ /**
82
+ * The ORDER BY a query is built with, in the order it was applied. `allOrderBys`, not `orderBys`: it is what the SQL
83
+ * is built from, so without an explicit order it falls back to the `orderBy` of @Entity, and respects the
84
+ * `disable-global-order` option.
85
+ */
86
+ export declare function getAppliedOrderBy<Entity extends ObjectLiteral>(builder: QueryBuilder<Entity>): OrderType[];
87
+ /** The query without paging: no ORDER BY (nor the entity's default one), LIMIT, OFFSET, take or skip. */
88
+ export declare function withoutPaging<Entity extends ObjectLiteral>(query: SelectQueryBuilder<Entity>): SelectQueryBuilder<Entity>;
89
+ /** The query runner a builder was created with, from a transactional manager; `undefined` for one from the pool. */
90
+ export declare function getQueryRunner<Entity extends ObjectLiteral>(builder: QueryBuilder<Entity>): QueryRunner | undefined;
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getTypedSelectionKey = exports.getTypedSingleColumnKey = exports.isTypedJsonPath = exports.getTypedColumnKey = exports.toFieldSql = exports.findFieldColumn = exports.getColumnDatabaseName = exports.resolveDeferredKeys = void 0;
3
+ exports.getQueryRunner = exports.withoutPaging = exports.getAppliedOrderBy = exports.getTypedSelectionKey = exports.resolveTypedProperty = exports.getTypedSingleColumnKey = exports.isTypedJsonPath = exports.getTypedColumnKey = exports.toFieldSql = exports.findFieldColumn = exports.getColumnDatabaseName = exports.resolveDeferredKeys = exports.listQueryAliases = void 0;
4
4
  const typeorm_1 = require("typeorm");
5
5
  const QueryExpressionMap_1 = require("typeorm/query-builder/QueryExpressionMap");
6
6
  const proxy_key_utils_1 = require("./proxy-key.utils");
7
7
  const json_path_utils_1 = require("./json-path.utils");
8
8
  const sql_utils_1 = require("./sql.utils");
9
+ const calculated_column_utils_1 = require("./calculated-column.utils");
9
10
  /**
10
11
  * Whether single-level typed keys are qualified with the main alias (`user.firstName`).
11
12
  *
@@ -33,21 +34,24 @@ function requireMainAliasName(queryBuilder, key) {
33
34
  return mainAlias.name;
34
35
  }
35
36
  /**
36
- * An alias of the query by name, or of a query it is part of. The builder of a `Brackets` callback has the
37
- * outer query's main alias but an empty alias list: the aliases, joins included, are on its parent. A subquery
38
- * sees the aliases of the outer query too, its own first.
37
+ * The aliases of the query and of the queries it is part of, its own first. The builder of a `Brackets` callback
38
+ * has the outer query's main alias but an empty alias list: the aliases, joins included, are on its parent. A
39
+ * subquery sees the aliases of the outer query too.
39
40
  */
40
- function findQueryAlias(queryBuilder, aliasName) {
41
- var _a, _b;
41
+ function listQueryAliases(builder) {
42
+ var _a;
43
+ const aliases = [];
42
44
  // parentQueryBuilder is protected in TypeORM's types
43
- for (let query = queryBuilder; query; query = query.parentQueryBuilder) {
44
- const { aliases = [], mainAlias } = (_a = query.expressionMap) !== null && _a !== void 0 ? _a : {};
45
- const alias = (_b = aliases.find((candidate) => candidate.name === aliasName)) !== null && _b !== void 0 ? _b : ((mainAlias === null || mainAlias === void 0 ? void 0 : mainAlias.name) === aliasName ? mainAlias : undefined);
46
- if (alias) {
47
- return alias;
48
- }
45
+ for (let query = builder; query; query = query.parentQueryBuilder) {
46
+ const { aliases: own = [], mainAlias } = (_a = query.expressionMap) !== null && _a !== void 0 ? _a : {};
47
+ aliases.push(...own, ...(mainAlias ? [mainAlias] : []));
49
48
  }
50
- return undefined;
49
+ return aliases;
50
+ }
51
+ exports.listQueryAliases = listQueryAliases;
52
+ /** An alias of the query by name, or of a query it is part of (see `listQueryAliases`). */
53
+ function findQueryAlias(queryBuilder, aliasName) {
54
+ return listQueryAliases(queryBuilder).find((candidate) => candidate.name === aliasName);
51
55
  }
52
56
  // private-use characters: no identifier or SQL that TypeORM builds contains them
53
57
  const DEFERRED_KEY_START = '\uE000';
@@ -74,7 +78,7 @@ function deferColumnKey(aliasName, separator, name) {
74
78
  * subquery in the condition of a raw join): TypeORM can't map it, and PostgreSQL would fold it to lower case.
75
79
  */
76
80
  function toDeferredKeySql(alias, aliasName, separator, name, escape) {
77
- const metadata = (alias === null || alias === void 0 ? void 0 : alias.hasMetadata) ? alias.metadata : undefined;
81
+ const metadata = metadataOf(alias);
78
82
  if (separator === DEFERRED_UNQUOTED) {
79
83
  return metadata ? `${aliasName}.${name}` : `${escape(aliasName)}.${escape(name)}`;
80
84
  }
@@ -149,13 +153,11 @@ function installDeferredKeyResolution() {
149
153
  }
150
154
  }
151
155
  installDeferredKeyResolution();
156
+ const metadataOf = (alias) => (alias === null || alias === void 0 ? void 0 : alias.hasMetadata) ? alias.metadata : undefined;
152
157
  /** Entity metadata of a query alias (the main alias when `aliasName` is undefined), if it has any. */
153
158
  function getAliasMetadata(queryBuilder, aliasName) {
154
159
  var _a;
155
- const alias = aliasName === undefined
156
- ? (_a = queryBuilder.expressionMap) === null || _a === void 0 ? void 0 : _a.mainAlias
157
- : findQueryAlias(queryBuilder, aliasName);
158
- return (alias === null || alias === void 0 ? void 0 : alias.hasMetadata) ? alias.metadata : undefined;
160
+ return metadataOf(aliasName === undefined ? (_a = queryBuilder.expressionMap) === null || _a === void 0 ? void 0 : _a.mainAlias : findQueryAlias(queryBuilder, aliasName));
159
161
  }
160
162
  /**
161
163
  * Database name of an entity property's column (`@Column({ name })`), looked up in the metadata of
@@ -185,6 +187,7 @@ function findFieldColumn(builder, field) {
185
187
  const metadata = getAliasMetadata(queryBuilder, field.slice(0, dot));
186
188
  return metadata && findColumn(metadata, field.slice(dot + 1));
187
189
  }
190
+ // only by database name: an unqualified property name may mean another table's column
188
191
  return (_a = getAliasMetadata(queryBuilder, undefined)) === null || _a === void 0 ? void 0 : _a.columns.find((column) => column.databaseName === field);
189
192
  }
190
193
  exports.findFieldColumn = findFieldColumn;
@@ -205,27 +208,17 @@ exports.findFieldColumn = findFieldColumn;
205
208
  */
206
209
  function toFieldSql(builder, field) {
207
210
  const queryBuilder = builder;
211
+ const column = findFieldColumn(builder, field);
208
212
  const dot = field.indexOf('.');
209
- if (dot > 0) {
210
- const aliasName = field.slice(0, dot);
211
- const metadata = getAliasMetadata(queryBuilder, aliasName);
212
- const column = metadata && findColumn(metadata, field.slice(dot + 1));
213
- if (column) {
214
- const columnSql = queryBuilder.escape(column.databaseName);
215
- return isAliasPrefixingEnabled(queryBuilder) ? `${queryBuilder.escape(aliasName)}.${columnSql}` : columnSql;
216
- }
217
- const parts = TWO_PART_FIELD.exec(field);
218
- if (parts && !findQueryAlias(queryBuilder, aliasName)) {
219
- return deferColumnKey(parts[1], DEFERRED_FIELD, parts[2]);
220
- }
213
+ if (column) {
214
+ const columnSql = queryBuilder.escape(column.databaseName);
215
+ return dot > 0 && isAliasPrefixingEnabled(queryBuilder)
216
+ ? `${queryBuilder.escape(field.slice(0, dot))}.${columnSql}`
217
+ : columnSql;
221
218
  }
222
- else {
223
- // only by database name: an unqualified property name may mean another table's column
224
- const metadata = getAliasMetadata(queryBuilder, undefined);
225
- const column = metadata === null || metadata === void 0 ? void 0 : metadata.columns.find((candidate) => candidate.databaseName === field);
226
- if (column) {
227
- return queryBuilder.escape(column.databaseName);
228
- }
219
+ const parts = dot > 0 ? TWO_PART_FIELD.exec(field) : null;
220
+ if (parts && !findQueryAlias(queryBuilder, parts[1])) {
221
+ return deferColumnKey(parts[1], DEFERRED_FIELD, parts[2]);
229
222
  }
230
223
  return (0, sql_utils_1.stringToSQLIdentifier)(field, (0, sql_utils_1.getDriverType)(builder));
231
224
  }
@@ -266,15 +259,13 @@ function findColumnPath(metadata, keys) {
266
259
  * before `from()` the property name is used as the column name.
267
260
  */
268
261
  function resolveTypedKey(queryBuilder, keys) {
269
- var _a, _b;
270
262
  const resolved = resolveTypedKeyPath(queryBuilder, keys);
271
263
  // deeper than `alias.column` only inside a JSON column: a nested relation can't be mapped to an alias
272
264
  if (!resolved.jsonPath && keys.length > proxy_key_utils_1.MAX_KEY_DEPTH) {
273
265
  throw (0, proxy_key_utils_1.createKeyTooDeepError)(keys);
274
266
  }
275
- const column = (_a = resolved.column) !== null && _a !== void 0 ? _a : (_b = resolved.metadata) === null || _b === void 0 ? void 0 : _b.findColumnWithPropertyPath(resolved.propertyPath);
276
267
  // a @CalculatedColumn: TypeORM would write "alias"."property", a column the table doesn't have
277
- if ((column === null || column === void 0 ? void 0 : column.isVirtualProperty) && !column.query) {
268
+ if ((0, calculated_column_utils_1.isCalculated)(resolved.column)) {
278
269
  throw new TypeError(`${keys.join('.')} is a @CalculatedColumn, not a column of the table: use the SQL expression that fills it`);
279
270
  }
280
271
  return resolved;
@@ -283,10 +274,12 @@ function resolveTypedKeyPath(queryBuilder, keys) {
283
274
  const prefixing = isAliasPrefixingEnabled(queryBuilder);
284
275
  if (keys.length === 1) {
285
276
  const aliasName = prefixing ? requireMainAliasName(queryBuilder, keys[0]) : undefined;
277
+ const metadata = getAliasMetadata(queryBuilder, aliasName);
286
278
  return {
287
279
  aliasName,
288
280
  propertyPath: keys[0],
289
- metadata: getAliasMetadata(queryBuilder, aliasName),
281
+ metadata,
282
+ column: metadata === null || metadata === void 0 ? void 0 : metadata.findColumnWithPropertyPath(keys[0]),
290
283
  };
291
284
  }
292
285
  const [firstKey, ...restKeys] = keys;
@@ -314,7 +307,7 @@ function resolveTypedKeyPath(queryBuilder, keys) {
314
307
  };
315
308
  }
316
309
  }
317
- const metadata = (queryAlias === null || queryAlias === void 0 ? void 0 : queryAlias.hasMetadata) ? queryAlias.metadata : undefined;
310
+ const metadata = metadataOf(queryAlias);
318
311
  return {
319
312
  aliasName: firstKey,
320
313
  propertyPath: restKeys.join('.'),
@@ -327,11 +320,11 @@ function resolveTypedKeyPath(queryBuilder, keys) {
327
320
  * inside a JSON column: `"exec"."details" -> 'meta' ->> 'endTs'` (see `toJsonPathSql`).
328
321
  */
329
322
  function toQuotedColumnKey(queryBuilder, { aliasName, propertyPath, metadata, column, jsonPath }, jsonOptions) {
330
- var _a, _b;
323
+ var _a;
331
324
  if (aliasName !== undefined && !metadata && !findQueryAlias(queryBuilder, aliasName)) {
332
325
  return deferColumnKey(aliasName, DEFERRED_PROPERTY, propertyPath);
333
326
  }
334
- const columnName = (_b = (_a = (column !== null && column !== void 0 ? column : metadata === null || metadata === void 0 ? void 0 : metadata.findColumnWithPropertyPath(propertyPath))) === null || _a === void 0 ? void 0 : _a.databaseName) !== null && _b !== void 0 ? _b : propertyPath;
327
+ const columnName = (_a = column === null || column === void 0 ? void 0 : column.databaseName) !== null && _a !== void 0 ? _a : propertyPath;
335
328
  const columnSql = aliasName === undefined
336
329
  ? queryBuilder.escape(columnName)
337
330
  : `${queryBuilder.escape(aliasName)}.${queryBuilder.escape(columnName)}`;
@@ -377,6 +370,19 @@ function getTypedSingleColumnKey(builder, callback, methodName, jsonOptions) {
377
370
  return key;
378
371
  }
379
372
  exports.getTypedSingleColumnKey = getTypedSingleColumnKey;
373
+ /**
374
+ * The alias and the entity property a typed callback picks, for methods that take a property rather than a column
375
+ * (a `@CalculatedColumn`): `u => u.imagesCount` is on the main alias, `q => q.owner.imagesCount` on `owner`.
376
+ */
377
+ function resolveTypedProperty(builder, callback, methodName) {
378
+ const keyHolder = (0, proxy_key_utils_1.getKeyHolder)(callback);
379
+ if (Array.isArray(keyHolder)) {
380
+ throw new TypeError(`${methodName} expects the callback to pick a single property, got ${keyHolder.length}`);
381
+ }
382
+ const { aliasName, propertyPath } = resolveTypedKeyPath(builder, keyHolder.getKeys());
383
+ return { aliasName, propertyPath };
384
+ }
385
+ exports.resolveTypedProperty = resolveTypedProperty;
380
386
  /**
381
387
  * Throws for a key that names no column of its table: a relation whose join column is on the other side
382
388
  * (one-to-many, many-to-many, the inverse side of a one-to-one) or an embedded entity. TypeORM leaves such a key
@@ -442,3 +448,35 @@ function getTypedSelectionKey(builder, selection, useQuotes, columnsFor) {
442
448
  return Array.isArray(keyHolder) ? keyHolder.map(toKey) : toKey(keyHolder);
443
449
  }
444
450
  exports.getTypedSelectionKey = getTypedSelectionKey;
451
+ /**
452
+ * The ORDER BY a query is built with, in the order it was applied. `allOrderBys`, not `orderBys`: it is what the SQL
453
+ * is built from, so without an explicit order it falls back to the `orderBy` of @Entity, and respects the
454
+ * `disable-global-order` option.
455
+ */
456
+ function getAppliedOrderBy(builder) {
457
+ var _a;
458
+ return Object.entries((_a = builder.expressionMap.allOrderBys) !== null && _a !== void 0 ? _a : {}).map(([key, value]) => {
459
+ const selector = resolveDeferredKeys(builder, key);
460
+ return typeof value === 'string'
461
+ ? { selector, order: value }
462
+ : { selector, order: value.order, nulls: value.nulls };
463
+ });
464
+ }
465
+ exports.getAppliedOrderBy = getAppliedOrderBy;
466
+ /** The query without paging: no ORDER BY (nor the entity's default one), LIMIT, OFFSET, take or skip. */
467
+ function withoutPaging(query) {
468
+ return query
469
+ .orderBy()
470
+ .offset(undefined)
471
+ .limit(undefined)
472
+ .skip(undefined)
473
+ .take(undefined)
474
+ .setOption('disable-global-order');
475
+ }
476
+ exports.withoutPaging = withoutPaging;
477
+ /** The query runner a builder was created with, from a transactional manager; `undefined` for one from the pool. */
478
+ function getQueryRunner(builder) {
479
+ // queryRunner is protected in TypeORM's types
480
+ return builder.queryRunner;
481
+ }
482
+ exports.getQueryRunner = getQueryRunner;