typeorm-extensions 1.3.5 → 2.0.0

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 (101) hide show
  1. package/README.md +14 -10
  2. package/dist/constants/index.d.ts +1 -1
  3. package/dist/constants/index.js +2 -2
  4. package/dist/extensions/cursor-pagination.extension.d.ts +18 -0
  5. package/dist/extensions/cursor-pagination.extension.js +108 -0
  6. package/dist/extensions/index.d.ts +7 -0
  7. package/dist/extensions/index.js +7 -0
  8. package/dist/extensions/order-by-array-position.extension.d.ts +24 -0
  9. package/dist/extensions/order-by-array-position.extension.js +18 -0
  10. package/dist/extensions/order.extension.d.ts +3 -2
  11. package/dist/extensions/order.extension.js +16 -13
  12. package/dist/extensions/period-filter.extension.d.ts +29 -0
  13. package/dist/extensions/period-filter.extension.js +23 -0
  14. package/dist/extensions/typed/index.d.ts +10 -0
  15. package/dist/extensions/typed/index.js +10 -0
  16. package/dist/extensions/typed/typed-distinct/typed-distinct.extension.js +2 -2
  17. package/dist/extensions/typed/typed-joins/typed-joins.extension.js +2 -1
  18. package/dist/extensions/typed/typed-order-by/typed-order-by-array-position.extension.d.ts +20 -0
  19. package/dist/extensions/typed/typed-order-by/typed-order-by-array-position.extension.js +21 -0
  20. package/dist/extensions/typed/typed-order-by/typed-order-by.extension.d.ts +4 -3
  21. package/dist/extensions/typed/typed-order-by/typed-order-by.extension.js +7 -6
  22. package/dist/extensions/typed/typed-returning/typed-returning.extension.d.ts +27 -0
  23. package/dist/extensions/typed/typed-returning/typed-returning.extension.js +38 -0
  24. package/dist/extensions/typed/typed-select/typed-select.extension.d.ts +6 -6
  25. package/dist/extensions/typed/typed-select/typed-select.extension.js +11 -8
  26. package/dist/extensions/typed/typed-where/typed-period-filter.extension.d.ts +31 -0
  27. package/dist/extensions/typed/typed-where/typed-period-filter.extension.js +25 -0
  28. package/dist/extensions/typed/typed-where/typed-where-array.extension.d.ts +68 -0
  29. package/dist/extensions/typed/typed-where/typed-where-array.extension.js +41 -0
  30. package/dist/extensions/typed/typed-where/typed-where-ilike.extension.d.ts +62 -0
  31. package/dist/extensions/typed/typed-where/typed-where-ilike.extension.js +84 -0
  32. package/dist/extensions/typed/typed-where/typed-where-is-any.extension.d.ts +144 -0
  33. package/dist/extensions/typed/typed-where/typed-where-is-any.extension.js +37 -0
  34. package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.d.ts +53 -0
  35. package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.js +38 -0
  36. package/dist/extensions/typed/typed-where/typed-where-is-in.extension.d.ts +25 -26
  37. package/dist/extensions/typed/typed-where/typed-where-is-in.extension.js +16 -23
  38. package/dist/extensions/typed/typed-where/typed-where-json.extension.d.ts +53 -0
  39. package/dist/extensions/typed/typed-where/typed-where-json.extension.js +39 -0
  40. package/dist/extensions/typed/typed-where/typed-where-lower.extension.d.ts +59 -0
  41. package/dist/extensions/typed/typed-where/typed-where-lower.extension.js +86 -0
  42. package/dist/extensions/typed/typed-where/typed-where-similar.extension.d.ts +54 -0
  43. package/dist/extensions/typed/typed-where/typed-where-similar.extension.js +73 -0
  44. package/dist/extensions/typed/typed-where/typed-where.extension.d.ts +13 -13
  45. package/dist/extensions/typed/typed-where/typed-where.extension.js +4 -12
  46. package/dist/extensions/where-array.extension.d.ts +70 -0
  47. package/dist/extensions/where-array.extension.js +47 -0
  48. package/dist/extensions/where-is-any.extension.d.ts +142 -0
  49. package/dist/extensions/where-is-any.extension.js +44 -0
  50. package/dist/extensions/where-is-distinct-from.extension.d.ts +56 -0
  51. package/dist/extensions/where-is-distinct-from.extension.js +55 -0
  52. package/dist/extensions/where-json.extension.d.ts +55 -0
  53. package/dist/extensions/where-json.extension.js +64 -0
  54. package/dist/helpers.d.ts +2 -0
  55. package/dist/helpers.js +23 -0
  56. package/dist/index.d.ts +2 -0
  57. package/dist/index.js +4 -0
  58. package/dist/types/extensions/cursor-pagination.types.d.ts +16 -0
  59. package/dist/types/extensions/cursor-pagination.types.js +2 -0
  60. package/dist/types/extensions/get-many-with-totals.types.d.ts +3 -1
  61. package/dist/types/extensions/index.d.ts +4 -0
  62. package/dist/types/extensions/index.js +4 -0
  63. package/dist/types/extensions/order.types.d.ts +1 -1
  64. package/dist/types/extensions/period.types.d.ts +15 -0
  65. package/dist/types/extensions/period.types.js +2 -0
  66. package/dist/types/extensions/search.types.d.ts +68 -0
  67. package/dist/types/extensions/search.types.js +2 -0
  68. package/dist/types/extensions/typed-order-by.types.d.ts +3 -0
  69. package/dist/types/extensions/typed-order-by.types.js +2 -0
  70. package/dist/types/extensions/typed-where.types.d.ts +4 -4
  71. package/dist/types/extensions/where.types.d.ts +1 -0
  72. package/dist/types/interfaces/cursor-pagination.interface.d.ts +16 -0
  73. package/dist/types/interfaces/cursor-pagination.interface.js +2 -0
  74. package/dist/types/interfaces/index.d.ts +2 -0
  75. package/dist/types/interfaces/index.js +2 -0
  76. package/dist/types/interfaces/period.interface.d.ts +12 -0
  77. package/dist/types/interfaces/period.interface.js +2 -0
  78. package/dist/types/modules/proxy-callback.types.d.ts +26 -3
  79. package/dist/types/util-types/index.d.ts +3 -1
  80. package/dist/utils/cursor.utils.d.ts +31 -0
  81. package/dist/utils/cursor.utils.js +130 -0
  82. package/dist/utils/order.utils.d.ts +15 -0
  83. package/dist/utils/order.utils.js +45 -0
  84. package/dist/utils/period.utils.d.ts +9 -0
  85. package/dist/utils/period.utils.js +82 -0
  86. package/dist/utils/proxy-key.utils.d.ts +13 -2
  87. package/dist/utils/proxy-key.utils.js +32 -14
  88. package/dist/utils/query-builder.utils.d.ts +46 -0
  89. package/dist/utils/query-builder.utils.js +208 -0
  90. package/dist/utils/search.utils.d.ts +27 -0
  91. package/dist/utils/search.utils.js +83 -0
  92. package/dist/utils/sql.utils.d.ts +27 -1
  93. package/dist/utils/sql.utils.js +87 -9
  94. package/dist/utils/table.utils.d.ts +86 -0
  95. package/dist/utils/table.utils.js +135 -0
  96. package/dist/utils/where.utils.d.ts +18 -1
  97. package/dist/utils/where.utils.js +62 -9
  98. package/helpers/package.json +7 -0
  99. package/package.json +3 -3
  100. package/dist/utils/identifier.utils.d.ts +0 -15
  101. package/dist/utils/identifier.utils.js +0 -45
package/README.md CHANGED
@@ -27,23 +27,27 @@ npm install typeorm-extensions
27
27
  ## Usage
28
28
 
29
29
  ```typescript
30
- import 'typeorm-extensions'; // Import the library root to extend the QueryBuilder with all extensions
30
+ import { tableHelper } from 'typeorm-extensions'; // Importing the library root extends the QueryBuilder with all extensions
31
31
 
32
32
  // Or init specific extension: import 'typeorm-extensions/dist/extensions/pagination.extension';
33
33
 
34
+ // Type-safe SQL fragments for an entity alias
35
+ const t = tableHelper<UserEntity>('users');
36
+
34
37
  const query = myDataSource
35
- .createQueryBuilder()
36
- .from(UserEntity, 'users')
38
+ .getRepository(UserEntity)
39
+ .createQueryBuilder('users')
37
40
  // Join relation defined in entity model
38
41
  .leftJoinTyped(user => user.images, 'images')
39
42
  // Select type-safe properties
40
- .selectTyped(user => ([
41
- id: user.id,
42
- name: user.name,
43
- email: user.email,
44
- ]))
43
+ .selectTyped(user => [
44
+ user.id,
45
+ user.name,
46
+ user.email,
47
+ user.images.url,
48
+ ])
45
49
  .whereTyped(user => user.name, 'ILIKE', 'John')
46
- .andWhereTyped(user => user.metadata, ` ->> 'jsonField' =`, 'some-value')
50
+ .andWhereTyped(user => user.metadata, `${t.jsonPath('metadata', '->>', 'role')} =`, 'admin')
47
51
  // Order by type-safe own and relation properties
48
52
  .orderByTyped(user => user.images.url, 'ASC', 'NULLS LAST')
49
53
  // Use pagination as simple as that
@@ -56,7 +60,7 @@ For more information, please refer to the [documentation](https://koenigstag.git
56
60
 
57
61
  ## ToDo list
58
62
 
59
- - [ ] Add tests for pagination and order extensions
63
+ - [x] Add tests for pagination and order extensions
60
64
  - [ ] Virtual column decorator and its query-builder methods (`addSelectVirtual`, `orderByVirtual`, `getManyWithVirtual`, `getOneWithVirtual`)
61
65
  - [ ] Add `withVirtualColumns` option to `findManyWithTotals` method to use `getManyWithVirtual` instead of `getMany`
62
66
  - [ ] Add `isVirtual` option to `OrderParam` to indicate usage of `orderByVirtual` method
@@ -1,5 +1,5 @@
1
1
  export declare const defaultPageSize = 10;
2
2
  export declare const defaultUseTakeAndSkip = false;
3
- export declare const defaultUseDoubleQuotes = true;
3
+ export declare const defaultUseQuotes = true;
4
4
  export declare const defaultOrderDirection = "ASC";
5
5
  export declare const defaultNullsOrder: undefined;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.defaultNullsOrder = exports.defaultOrderDirection = exports.defaultUseDoubleQuotes = exports.defaultUseTakeAndSkip = exports.defaultPageSize = void 0;
3
+ exports.defaultNullsOrder = exports.defaultOrderDirection = exports.defaultUseQuotes = exports.defaultUseTakeAndSkip = exports.defaultPageSize = void 0;
4
4
  exports.defaultPageSize = 10;
5
5
  exports.defaultUseTakeAndSkip = false;
6
- exports.defaultUseDoubleQuotes = true;
6
+ exports.defaultUseQuotes = true;
7
7
  exports.defaultOrderDirection = 'ASC';
8
8
  exports.defaultNullsOrder = undefined;
@@ -0,0 +1,18 @@
1
+ import type { CursorPaginationFilter } from '../types/interfaces/cursor-pagination.interface';
2
+ import type { GetManyWithCursorOptions, ListWithCursor } from '../types/extensions/cursor-pagination.types';
3
+ export { InvalidCursorError } from '../utils/cursor.utils';
4
+ declare module 'typeorm/query-builder/SelectQueryBuilder' {
5
+ interface SelectQueryBuilder<Entity> {
6
+ /**
7
+ * Loads a page with keyset (cursor) pagination: instead of OFFSET, the rows after the last row of
8
+ * the previous page, `WHERE (createdAt, id) < (:lastCreatedAt, :lastId)`. Pages stay fast however
9
+ * deep they are, and rows inserted meanwhile don't shift them.
10
+ *
11
+ * Orders the query by `options.orderBy` (the primary key is appended), replacing its ORDER BY,
12
+ * loads `pageSize + 1` rows to know whether there is a next page, and returns `nextCursor`.
13
+ * The query builder itself is not changed. Throws `InvalidCursorError` for a cursor that can't
14
+ * be decoded or was made for another order.
15
+ */
16
+ getManyWithCursor(cursorFilter: CursorPaginationFilter | null | undefined, options: GetManyWithCursorOptions<Entity>): Promise<ListWithCursor<Entity>>;
17
+ }
18
+ }
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidCursorError = void 0;
4
+ const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder");
5
+ const constants_1 = require("../constants");
6
+ const prototype_utils_1 = require("../utils/prototype.utils");
7
+ const order_utils_1 = require("../utils/order.utils");
8
+ const where_utils_1 = require("../utils/where.utils");
9
+ const sql_utils_1 = require("../utils/sql.utils");
10
+ const cursor_utils_1 = require("../utils/cursor.utils");
11
+ var cursor_utils_2 = require("../utils/cursor.utils");
12
+ Object.defineProperty(exports, "InvalidCursorError", { enumerable: true, get: function () { return cursor_utils_2.InvalidCursorError; } });
13
+ // patching
14
+ const extension = {
15
+ prototype: {
16
+ getManyWithCursor(cursorFilter, options) {
17
+ return getManyWithCursor(this, cursorFilter, options);
18
+ },
19
+ },
20
+ };
21
+ (0, prototype_utils_1.patchPrototype)(SelectQueryBuilder_1.SelectQueryBuilder, extension);
22
+ const isDateColumn = (column) => column.type === Date || (typeof column.type === 'string' && /date|time/i.test(column.type));
23
+ /** The columns of `orderBy`, followed by the primary key columns that aren't among them. */
24
+ function getCursorKeys(builder, orderBy) {
25
+ const mainAlias = builder.expressionMap.mainAlias;
26
+ if (!(mainAlias === null || mainAlias === void 0 ? void 0 : mainAlias.hasMetadata)) {
27
+ throw new TypeError('getManyWithCursor needs a query on an entity (createQueryBuilder of a repository or an entity)');
28
+ }
29
+ const metadata = mainAlias.metadata;
30
+ const keys = (orderBy !== null && orderBy !== void 0 ? orderBy : []).map(({ field, direction }) => {
31
+ const column = metadata.findColumnWithPropertyPath(String(field));
32
+ if (!column) {
33
+ throw new TypeError(`getManyWithCursor: "${String(field)}" is not a column of ${metadata.name}`);
34
+ }
35
+ return { column, direction: (0, order_utils_1.toSqlOrderDirection)(direction) };
36
+ });
37
+ // the primary key makes the order total: rows with equal keys can't be skipped or repeated
38
+ const lastDirection = keys.length ? keys[keys.length - 1].direction : 'ASC';
39
+ for (const column of metadata.primaryColumns) {
40
+ if (!keys.some((key) => key.column === column)) {
41
+ keys.push({ column, direction: lastDirection });
42
+ }
43
+ }
44
+ return keys;
45
+ }
46
+ const getPageSize = (pageSize) => typeof pageSize === 'number' && Number.isFinite(pageSize) && pageSize >= 1
47
+ ? Math.floor(pageSize)
48
+ : constants_1.defaultPageSize;
49
+ async function getManyWithCursor(builder, cursorFilter, options) {
50
+ const { orderBy, useTakeAndSkip = false } = options !== null && options !== void 0 ? options : {};
51
+ const keys = getCursorKeys(builder, orderBy);
52
+ const fields = keys.map((key) => key.column.propertyPath);
53
+ const pageSize = getPageSize(cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.pageSize);
54
+ const query = builder.clone();
55
+ const alias = query.alias;
56
+ const driver = query.connection.driver.options.type;
57
+ const textCastType = (0, cursor_utils_1.getTextCastType)(driver);
58
+ const quotedColumn = (column) => `${query.escape(alias)}.${query.escape(column.databaseName)}`;
59
+ if (cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.cursor) {
60
+ const values = (0, cursor_utils_1.decodeCursor)(cursorFilter.cursor, fields);
61
+ const keysetColumns = keys.map((key, i) => {
62
+ const paramName = (0, sql_utils_1.createUniqueParameterName)(`cursor${i}`);
63
+ query.setParameter(paramName, values[i]);
64
+ return { column: quotedColumn(key.column), direction: key.direction, paramName };
65
+ });
66
+ (0, where_utils_1.andWhereNarrowing)(query, (0, cursor_utils_1.buildKeysetCondition)(keysetColumns, (0, cursor_utils_1.supportsRowValueComparison)(driver)));
67
+ }
68
+ query.orderBy();
69
+ keys.forEach((key) => query.addOrderBy(`${alias}.${key.column.propertyPath}`, key.direction));
70
+ // the key values of the last row are read from the raw result: dates and times as text, so the
71
+ // cursor keeps the database's precision (PostgreSQL timestamps have microseconds, a JS Date has not)
72
+ const keyAliases = keys.map((key, i) => {
73
+ const keyAlias = `cursor_key_${i}`;
74
+ const column = quotedColumn(key.column);
75
+ const selection = isDateColumn(key.column) && textCastType ? `CAST(${column} AS ${textCastType})` : column;
76
+ query.addSelect(selection, keyAlias);
77
+ return keyAlias;
78
+ });
79
+ const primaryAliases = keys
80
+ .map((key, i) => ({ key, i }))
81
+ .filter(({ key }) => key.column.isPrimary)
82
+ .map(({ key, i }) => ({ column: key.column, keyAlias: keyAliases[i] }));
83
+ if (useTakeAndSkip) {
84
+ query.take(pageSize + 1);
85
+ }
86
+ else {
87
+ query.limit(pageSize + 1);
88
+ }
89
+ const { entities, raw } = await query.getRawAndEntities();
90
+ const list = entities.slice(0, pageSize);
91
+ if (entities.length <= pageSize) {
92
+ return { list, nextCursor: null };
93
+ }
94
+ // with joined collections there are several raw rows per entity: find one of the last entity's
95
+ const last = list[list.length - 1];
96
+ const lastRaw = raw.find((row) => primaryAliases.every(({ column, keyAlias }) => String(row[keyAlias]) === String(column.getEntityValue(last))));
97
+ if (!lastRaw) {
98
+ throw new Error('getManyWithCursor: could not find the key values of the last row');
99
+ }
100
+ const values = keyAliases.map((keyAlias, i) => {
101
+ const value = lastRaw[keyAlias];
102
+ if (value === null || value === undefined) {
103
+ throw new TypeError(`getManyWithCursor: "${fields[i]}" is NULL in the last row; cursor keys must be non-null columns`);
104
+ }
105
+ return value;
106
+ });
107
+ return { list, nextCursor: (0, cursor_utils_1.encodeCursor)(fields, values) };
108
+ }
@@ -2,4 +2,11 @@ export * from './get-many-with-totals.extension';
2
2
  export * from './order.extension';
3
3
  export * from './pagination.extension';
4
4
  export * from './where-is-in.extension';
5
+ export * from './where-is-any.extension';
6
+ export * from './where-array.extension';
7
+ export * from './where-json.extension';
8
+ export * from './where-is-distinct-from.extension';
9
+ export * from './period-filter.extension';
10
+ export * from './cursor-pagination.extension';
11
+ export * from './order-by-array-position.extension';
5
12
  export * from './typed';
@@ -18,4 +18,11 @@ __exportStar(require("./get-many-with-totals.extension"), exports);
18
18
  __exportStar(require("./order.extension"), exports);
19
19
  __exportStar(require("./pagination.extension"), exports);
20
20
  __exportStar(require("./where-is-in.extension"), exports);
21
+ __exportStar(require("./where-is-any.extension"), exports);
22
+ __exportStar(require("./where-array.extension"), exports);
23
+ __exportStar(require("./where-json.extension"), exports);
24
+ __exportStar(require("./where-is-distinct-from.extension"), exports);
25
+ __exportStar(require("./period-filter.extension"), exports);
26
+ __exportStar(require("./cursor-pagination.extension"), exports);
27
+ __exportStar(require("./order-by-array-position.extension"), exports);
21
28
  __exportStar(require("./typed"), exports);
@@ -0,0 +1,24 @@
1
+ import { ObjectLiteral } from 'typeorm';
2
+ import type { ArgsType } from '../types/util-types';
3
+ export interface OrderByArrayPositionMethods {
4
+ /**
5
+ * Sets ORDER BY the position of the column's value in `values` (PostgreSQL):
6
+ * `array_position(:values, column)`. Keeps the order of ids that come from elsewhere
7
+ * (search results, recommendations), e.g. after `whereIsAny(column, ids)`.
8
+ * Rows whose value isn't in `values` come last. `arrayType` (e.g. `'uuid[]'`) casts the array.
9
+ * Like `orderBy`, it replaces the previous ORDER BY.
10
+ *
11
+ * The position is selected under a generated alias (`array_position_…`) and ordered by it,
12
+ * which also works with `take` / `skip` and joins; it shows up in `getRawMany()` results.
13
+ */
14
+ orderByArrayPosition(field: string, values: ArgsType, arrayType?: string): this;
15
+ /**
16
+ * Adds ORDER BY the position of the column's value in `values` (PostgreSQL), like `addOrderBy`.
17
+ * See `orderByArrayPosition`.
18
+ */
19
+ addOrderByArrayPosition(field: string, values: ArgsType, arrayType?: string): this;
20
+ }
21
+ declare module 'typeorm/query-builder/SelectQueryBuilder' {
22
+ interface SelectQueryBuilder<Entity extends ObjectLiteral> extends OrderByArrayPositionMethods {
23
+ }
24
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder");
4
+ const prototype_utils_1 = require("../utils/prototype.utils");
5
+ const query_builder_utils_1 = require("../utils/query-builder.utils");
6
+ const order_utils_1 = require("../utils/order.utils");
7
+ /* eslint-enable @typescript-eslint/no-empty-interface, @typescript-eslint/no-unused-vars */
8
+ // patching
9
+ const createOrderByArrayPositionMethod = (replace) => function (field, values, arrayType) {
10
+ return (0, order_utils_1.attachOrderByArrayPosition)(this, (0, query_builder_utils_1.toFieldSql)(this, field), values, arrayType, replace);
11
+ };
12
+ const extension = {
13
+ prototype: {
14
+ orderByArrayPosition: createOrderByArrayPositionMethod(true),
15
+ addOrderByArrayPosition: createOrderByArrayPositionMethod(false),
16
+ },
17
+ };
18
+ (0, prototype_utils_1.patchPrototype)(SelectQueryBuilder_1.SelectQueryBuilder, extension);
@@ -1,8 +1,9 @@
1
1
  import type { OrderParam, OrderFilter } from '../types/interfaces/ordering.interface';
2
+ import { ObjectLiteral } from 'typeorm';
2
3
  import type { ApplyOrderOptions } from '../types/extensions/order.types';
3
4
  declare module 'typeorm/query-builder/SelectQueryBuilder' {
4
5
  interface SelectQueryBuilder<Entity> {
5
- applyOrder<OrderEntity>(orderBy: OrderParam<OrderEntity>, options?: ApplyOrderOptions): SelectQueryBuilder<Entity>;
6
- applyOrderFilter<OrderEntity>(orderFilter: OrderFilter<OrderEntity>, options?: ApplyOrderOptions): SelectQueryBuilder<Entity>;
6
+ applyOrder<OrderEntity extends ObjectLiteral>(orderBy: OrderParam<OrderEntity>, options?: ApplyOrderOptions): SelectQueryBuilder<Entity>;
7
+ applyOrderFilter<OrderEntity extends ObjectLiteral>(orderFilter: OrderFilter<OrderEntity>, options?: ApplyOrderOptions): SelectQueryBuilder<Entity>;
7
8
  }
8
9
  }
@@ -4,8 +4,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder");
5
5
  const constants_1 = require("../constants");
6
6
  const common_utils_1 = require("../utils/common.utils");
7
- const identifier_utils_1 = require("../utils/identifier.utils");
8
7
  const prototype_utils_1 = require("../utils/prototype.utils");
8
+ const table_utils_1 = require("../utils/table.utils");
9
+ const order_utils_1 = require("../utils/order.utils");
10
+ const query_builder_utils_1 = require("../utils/query-builder.utils");
9
11
  // patching
10
12
  const extension = {
11
13
  prototype: {
@@ -28,22 +30,23 @@ const alwaysAliasWhiteList = [
28
30
  'deletedAt',
29
31
  'deleted_at',
30
32
  ];
31
- function getOrderOption(orderBy, defaultAlias, options) {
32
- const { useDoubleQuotes = constants_1.defaultUseDoubleQuotes, alias: customAlias, alwaysAliasFields = alwaysAliasWhiteList, } = options !== null && options !== void 0 ? options : {};
33
+ function getOrderOption(builder, orderBy, options) {
34
+ var _a;
35
+ const defaultAlias = builder.alias;
36
+ const { useQuotes = constants_1.defaultUseQuotes, alias: customAlias, alwaysAliasFields = alwaysAliasWhiteList, } = options !== null && options !== void 0 ? options : {};
33
37
  const { field, direction, isReversed, nulls: nullsOrder, tableAlias: otherTableAlias, } = orderBy;
34
38
  const alias = otherTableAlias || customAlias ||
35
39
  (alwaysAliasFields.includes(field) ? defaultAlias : undefined);
36
- const identifier = new identifier_utils_1.Identifier(field, alias, useDoubleQuotes);
37
- const selector = identifier.fieldIdentifier();
40
+ const table = (0, table_utils_1.tableHelper)(alias, { useQuotes });
41
+ // TypeORM maps an unquoted `alias.property` to the column itself; anything else must name the
42
+ // column: `@Column({ name: 'created_at' }) createdAt` is ordered by "created_at", not "createdAt"
43
+ const column = alias && !useQuotes
44
+ ? field
45
+ : (_a = (0, query_builder_utils_1.getColumnDatabaseName)(builder, alias, field)) !== null && _a !== void 0 ? _a : field;
46
+ const selector = table.column(column);
38
47
  const order = typeof isReversed === 'boolean' && (0, common_utils_1.isNil)(direction)
39
48
  ? (isReversed ? 'DESC' : 'ASC')
40
- : typeof direction === 'boolean'
41
- ? (direction ? 'ASC' : 'DESC')
42
- : typeof direction === 'number'
43
- ? (direction === -1 ? 'DESC' : 'ASC')
44
- : typeof direction === 'string' && /asc|desc/i.test(direction)
45
- ? direction.toUpperCase()
46
- : constants_1.defaultOrderDirection;
49
+ : (0, order_utils_1.toSqlOrderDirection)(direction);
47
50
  const nulls = typeof nullsOrder === 'string' && /first|last/i.test(nullsOrder)
48
51
  ? `NULLS ${nullsOrder.toUpperCase()}`
49
52
  : constants_1.defaultNullsOrder;
@@ -55,7 +58,7 @@ function getOrderOption(orderBy, defaultAlias, options) {
55
58
  }
56
59
  function applyOrder(builder, orderBy, options) {
57
60
  const { resetPreviousOrder = false } = options !== null && options !== void 0 ? options : {};
58
- const orderOption = getOrderOption(orderBy, builder.alias, options);
61
+ const orderOption = getOrderOption(builder, orderBy, options);
59
62
  const query = resetPreviousOrder ? builder.orderBy() : builder;
60
63
  return query.addOrderBy(orderOption.selector, orderOption.order, orderOption.nulls);
61
64
  }
@@ -0,0 +1,29 @@
1
+ import { ObjectLiteral } from 'typeorm';
2
+ import type { PeriodFilter } from '../types/interfaces/period.interface';
3
+ import type { ApplyPeriodOptions } from '../types/extensions/period.types';
4
+ export interface PeriodFilterMethods {
5
+ /**
6
+ * Adds AND WHERE conditions for a period: `field >= :periodFrom AND field <= :periodTo`.
7
+ * Missing, `null` or empty ends are skipped, so an empty filter adds nothing.
8
+ * Both ends are inclusive unless `options.includeFrom` / `options.includeTo` is `false`.
9
+ * A date-only string (`'2024-01-31'`) stands for the whole day: `periodTo: '2024-01-31'` → `field < '2024-02-01'`.
10
+ * Invalid dates (`new Date('x')`, `'2024-02-30'`) throw a `TypeError`.
11
+ */
12
+ applyPeriodFilter(field: string, periodFilter: PeriodFilter | null | undefined, options?: ApplyPeriodOptions): this;
13
+ }
14
+ declare module 'typeorm/query-builder/WhereExpressionBuilder' {
15
+ interface WhereExpressionBuilder extends PeriodFilterMethods {
16
+ }
17
+ }
18
+ declare module 'typeorm/query-builder/SelectQueryBuilder' {
19
+ interface SelectQueryBuilder<Entity extends ObjectLiteral> extends PeriodFilterMethods {
20
+ }
21
+ }
22
+ declare module 'typeorm/query-builder/UpdateQueryBuilder' {
23
+ interface UpdateQueryBuilder<Entity extends ObjectLiteral> extends PeriodFilterMethods {
24
+ }
25
+ }
26
+ declare module 'typeorm/query-builder/DeleteQueryBuilder' {
27
+ interface DeleteQueryBuilder<Entity extends ObjectLiteral> extends PeriodFilterMethods {
28
+ }
29
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const typeorm_1 = require("typeorm");
4
+ const prototype_utils_1 = require("../utils/prototype.utils");
5
+ const query_builder_utils_1 = require("../utils/query-builder.utils");
6
+ const period_utils_1 = require("../utils/period.utils");
7
+ /* eslint-enable @typescript-eslint/no-empty-interface, @typescript-eslint/no-unused-vars */
8
+ // patching
9
+ const extension = {
10
+ prototype: {
11
+ applyPeriodFilter(field, periodFilter, options) {
12
+ return (0, period_utils_1.attachPeriodFilter)(this, (0, query_builder_utils_1.toFieldSql)(this, field), periodFilter, options);
13
+ },
14
+ },
15
+ };
16
+ const queryBuilders = [
17
+ typeorm_1.SelectQueryBuilder,
18
+ typeorm_1.UpdateQueryBuilder,
19
+ typeorm_1.DeleteQueryBuilder,
20
+ ];
21
+ queryBuilders.forEach((builder) => {
22
+ (0, prototype_utils_1.patchPrototype)(builder, extension);
23
+ });
@@ -4,3 +4,13 @@ export * from './typed-order-by/typed-order-by.extension';
4
4
  export * from './typed-select/typed-select.extension';
5
5
  export * from './typed-where/typed-where.extension';
6
6
  export * from './typed-where/typed-where-is-in.extension';
7
+ export * from './typed-where/typed-where-is-any.extension';
8
+ export * from './typed-where/typed-where-array.extension';
9
+ export * from './typed-where/typed-where-json.extension';
10
+ export * from './typed-where/typed-where-is-distinct-from.extension';
11
+ export * from './typed-where/typed-period-filter.extension';
12
+ export * from './typed-where/typed-where-ilike.extension';
13
+ export * from './typed-where/typed-where-lower.extension';
14
+ export * from './typed-where/typed-where-similar.extension';
15
+ export * from './typed-returning/typed-returning.extension';
16
+ export * from './typed-order-by/typed-order-by-array-position.extension';
@@ -23,3 +23,13 @@ __exportStar(require("./typed-order-by/typed-order-by.extension"), exports);
23
23
  __exportStar(require("./typed-select/typed-select.extension"), exports);
24
24
  __exportStar(require("./typed-where/typed-where.extension"), exports);
25
25
  __exportStar(require("./typed-where/typed-where-is-in.extension"), exports);
26
+ __exportStar(require("./typed-where/typed-where-is-any.extension"), exports);
27
+ __exportStar(require("./typed-where/typed-where-array.extension"), exports);
28
+ __exportStar(require("./typed-where/typed-where-json.extension"), exports);
29
+ __exportStar(require("./typed-where/typed-where-is-distinct-from.extension"), exports);
30
+ __exportStar(require("./typed-where/typed-period-filter.extension"), exports);
31
+ __exportStar(require("./typed-where/typed-where-ilike.extension"), exports);
32
+ __exportStar(require("./typed-where/typed-where-lower.extension"), exports);
33
+ __exportStar(require("./typed-where/typed-where-similar.extension"), exports);
34
+ __exportStar(require("./typed-returning/typed-returning.extension"), exports);
35
+ __exportStar(require("./typed-order-by/typed-order-by-array-position.extension"), exports);
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const typeorm_1 = require("typeorm");
4
- const proxy_key_utils_1 = require("../../../utils/proxy-key.utils");
4
+ const query_builder_utils_1 = require("../../../utils/query-builder.utils");
5
5
  // patching and implementation
6
6
  typeorm_1.SelectQueryBuilder.prototype.distinctOnTyped = function (...distinctOn) {
7
7
  if (Array.isArray(distinctOn) && distinctOn.length) {
8
- const keys = distinctOn.map((callback) => (0, proxy_key_utils_1.getSqlKeyFromProxyCallback)(callback, this.alias)).flat();
8
+ const keys = distinctOn.map((callback) => (0, query_builder_utils_1.getTypedSelectionKey)(this, callback)).flat();
9
9
  return this.distinctOn(keys);
10
10
  }
11
11
  return this;
@@ -28,6 +28,7 @@ typeorm_1.SelectQueryBuilder.prototype.innerJoinAndSelectTyped = function (selec
28
28
  return this;
29
29
  };
30
30
  function addJoin(query, method, selection, alias, condition, parameters) {
31
- const key = (0, proxy_key_utils_1.getSqlKeyFromProxyCallback)(selection, query.alias, false);
31
+ // `() => query.alias`: `(q) => q.base.images` needs no main alias, only `(u) => u.images` does
32
+ const key = (0, proxy_key_utils_1.getSqlKeyFromProxyCallback)(selection, () => query.alias, false);
32
33
  return query[method](key, alias, condition, parameters);
33
34
  }
@@ -0,0 +1,20 @@
1
+ import '../../order-by-array-position.extension';
2
+ import { ObjectLiteral } from 'typeorm';
3
+ import { DeepUnwrapArray, ProxyColumnValueType } from '../../../types/modules/proxy-callback.types';
4
+ import type { ArgsType } from '../../../types/util-types';
5
+ export interface TypedOrderByArrayPositionMethods<Entity extends ObjectLiteral> {
6
+ /**
7
+ * Sets ORDER BY the position of the property's column value in `values` (PostgreSQL):
8
+ * `array_position(:values, column)`. Rows whose value isn't in `values` come last.
9
+ * Like `orderBy`, it replaces the previous ORDER BY. See `orderByArrayPosition`.
10
+ */
11
+ orderByArrayPositionTyped<Type extends ObjectLiteral = Entity>(selection: (proxy: DeepUnwrapArray<Type>) => ProxyColumnValueType, values: ArgsType, arrayType?: string): this;
12
+ /**
13
+ * Adds ORDER BY the position of the property's column value in `values` (PostgreSQL), like `addOrderBy`.
14
+ */
15
+ addOrderByArrayPositionTyped<Type extends ObjectLiteral = Entity>(selection: (proxy: DeepUnwrapArray<Type>) => ProxyColumnValueType, values: ArgsType, arrayType?: string): this;
16
+ }
17
+ declare module 'typeorm/query-builder/SelectQueryBuilder' {
18
+ interface SelectQueryBuilder<Entity extends ObjectLiteral> extends TypedOrderByArrayPositionMethods<Entity> {
19
+ }
20
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("../../order-by-array-position.extension");
4
+ const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder");
5
+ const query_builder_utils_1 = require("../../../utils/query-builder.utils");
6
+ const prototype_utils_1 = require("../../../utils/prototype.utils");
7
+ const order_utils_1 = require("../../../utils/order.utils");
8
+ /* eslint-enable @typescript-eslint/no-empty-interface */
9
+ // patching
10
+ const createOrderByArrayPositionTypedMethod = (replace) => function (selection, values, arrayType) {
11
+ const methodName = replace ? 'orderByArrayPositionTyped' : 'addOrderByArrayPositionTyped';
12
+ const column = (0, query_builder_utils_1.getTypedSingleColumnKey)(this, selection, methodName);
13
+ return (0, order_utils_1.attachOrderByArrayPosition)(this, column, values, arrayType, replace);
14
+ };
15
+ const extension = {
16
+ prototype: {
17
+ orderByArrayPositionTyped: createOrderByArrayPositionTypedMethod(true),
18
+ addOrderByArrayPositionTyped: createOrderByArrayPositionTypedMethod(false),
19
+ },
20
+ };
21
+ (0, prototype_utils_1.patchPrototype)(SelectQueryBuilder_1.SelectQueryBuilder, extension);
@@ -1,6 +1,7 @@
1
1
  import { ObjectLiteral } from 'typeorm';
2
- import { ProxyColumnValueType } from '../../../types/modules/proxy-callback.types';
2
+ import { DeepUnwrapArray, ProxyColumnValueType } from '../../../types/modules/proxy-callback.types';
3
3
  import { KeyHolder } from '../../../utils/proxy-key.utils';
4
+ import type { TypedOrderByOptions } from '../../../types/extensions/typed-order-by.types';
4
5
  declare module 'typeorm/query-builder/SelectQueryBuilder' {
5
6
  interface SelectQueryBuilder<Entity extends ObjectLiteral> {
6
7
  /**
@@ -8,10 +9,10 @@ declare module 'typeorm/query-builder/SelectQueryBuilder' {
8
9
  * If you had previously ORDER BY expression defined,
9
10
  * calling this function will override previously set ORDER BY conditions.
10
11
  */
11
- orderByTyped<Type extends ObjectLiteral = Entity>(this: SelectQueryBuilder<Entity>, selection: (proxy: Type) => ProxyColumnValueType | KeyHolder, order?: 'ASC' | 'DESC', nulls?: 'NULLS FIRST' | 'NULLS LAST'): SelectQueryBuilder<Entity>;
12
+ orderByTyped<Type extends ObjectLiteral = Entity>(this: SelectQueryBuilder<Entity>, selection: (proxy: DeepUnwrapArray<Type>) => ProxyColumnValueType | KeyHolder, order?: 'ASC' | 'DESC', nulls?: 'NULLS FIRST' | 'NULLS LAST', options?: TypedOrderByOptions): SelectQueryBuilder<Entity>;
12
13
  /**
13
14
  * Adds ORDER BY condition in the query builder.
14
15
  */
15
- addOrderByTyped<Type extends ObjectLiteral = Entity>(this: SelectQueryBuilder<Entity>, selection: (proxy: Type) => ProxyColumnValueType | KeyHolder, order?: 'ASC' | 'DESC', nulls?: 'NULLS FIRST' | 'NULLS LAST'): SelectQueryBuilder<Entity>;
16
+ addOrderByTyped<Type extends ObjectLiteral = Entity>(this: SelectQueryBuilder<Entity>, selection: (proxy: DeepUnwrapArray<Type>) => ProxyColumnValueType | KeyHolder, order?: 'ASC' | 'DESC', nulls?: 'NULLS FIRST' | 'NULLS LAST', options?: TypedOrderByOptions): SelectQueryBuilder<Entity>;
16
17
  }
17
18
  }
@@ -2,23 +2,24 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const typeorm_1 = require("typeorm");
4
4
  const proxy_key_utils_1 = require("../../../utils/proxy-key.utils");
5
+ const query_builder_utils_1 = require("../../../utils/query-builder.utils");
5
6
  // patching and implementation
6
- typeorm_1.SelectQueryBuilder.prototype.orderByTyped = function (selection, order, nulls) {
7
+ typeorm_1.SelectQueryBuilder.prototype.orderByTyped = function (selection, order, nulls, options) {
7
8
  if (selection instanceof proxy_key_utils_1.KeyHolder) {
8
- return this.orderBy(selection.toSql(), order, nulls);
9
+ return this.orderBy(selection.toSql(undefined, options === null || options === void 0 ? void 0 : options.useQuotes), order, nulls);
9
10
  }
10
11
  if (typeof selection === 'function') {
11
- const key = (0, proxy_key_utils_1.getSqlKeyFromProxyCallback)(selection, this.alias);
12
+ const key = (0, query_builder_utils_1.getTypedSelectionKey)(this, selection, options === null || options === void 0 ? void 0 : options.useQuotes);
12
13
  return this.orderBy(key, order, nulls);
13
14
  }
14
15
  return this;
15
16
  };
16
- typeorm_1.SelectQueryBuilder.prototype.addOrderByTyped = function (selection, order, nulls, useDoubleQuotes) {
17
+ typeorm_1.SelectQueryBuilder.prototype.addOrderByTyped = function (selection, order, nulls, options) {
17
18
  if (selection instanceof proxy_key_utils_1.KeyHolder) {
18
- return this.addOrderBy(selection.toSql(), order, nulls);
19
+ return this.addOrderBy(selection.toSql(undefined, options === null || options === void 0 ? void 0 : options.useQuotes), order, nulls);
19
20
  }
20
21
  if (typeof selection === 'function') {
21
- const key = (0, proxy_key_utils_1.getSqlKeyFromProxyCallback)(selection, this.alias, useDoubleQuotes);
22
+ const key = (0, query_builder_utils_1.getTypedSelectionKey)(this, selection, options === null || options === void 0 ? void 0 : options.useQuotes);
22
23
  return this.addOrderBy(key, order, nulls);
23
24
  }
24
25
  return this;
@@ -0,0 +1,27 @@
1
+ import { ObjectLiteral } from 'typeorm';
2
+ import type { KeyProxyCallback } from '../../../types/modules/proxy-callback.types';
3
+ export interface TypedReturningMethods<Entity extends ObjectLiteral> {
4
+ /**
5
+ * Sets the columns to return from entity properties: `RETURNING` on PostgreSQL and MariaDB, `OUTPUT` on SQL Server.
6
+ * `returningTyped(u => [u.id, u.status])` returns the columns of `id` and `status`, whatever their
7
+ * database names; the rows come in `execute()`'s `raw`. A property that isn't a column of the
8
+ * entity throws a `TypeError` (TypeORM's `returning([...])` silently skips it).
9
+ */
10
+ returningTyped(selection: KeyProxyCallback<Entity>): this;
11
+ }
12
+ declare module 'typeorm/query-builder/InsertQueryBuilder' {
13
+ interface InsertQueryBuilder<Entity extends ObjectLiteral> extends TypedReturningMethods<Entity> {
14
+ }
15
+ }
16
+ declare module 'typeorm/query-builder/UpdateQueryBuilder' {
17
+ interface UpdateQueryBuilder<Entity extends ObjectLiteral> extends TypedReturningMethods<Entity> {
18
+ }
19
+ }
20
+ declare module 'typeorm/query-builder/DeleteQueryBuilder' {
21
+ interface DeleteQueryBuilder<Entity extends ObjectLiteral> extends TypedReturningMethods<Entity> {
22
+ }
23
+ }
24
+ declare module 'typeorm/query-builder/SoftDeleteQueryBuilder' {
25
+ interface SoftDeleteQueryBuilder<Entity extends ObjectLiteral> extends TypedReturningMethods<Entity> {
26
+ }
27
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const typeorm_1 = require("typeorm");
4
+ const SoftDeleteQueryBuilder_1 = require("typeorm/query-builder/SoftDeleteQueryBuilder");
5
+ const proxy_key_utils_1 = require("../../../utils/proxy-key.utils");
6
+ const prototype_utils_1 = require("../../../utils/prototype.utils");
7
+ /* eslint-enable @typescript-eslint/no-empty-interface */
8
+ // patching
9
+ const extension = {
10
+ prototype: {
11
+ // the same implementation for the Insert, Update, Delete and SoftDelete builders
12
+ returningTyped(selection) {
13
+ const keyHolders = (0, proxy_key_utils_1.getKeyHolder)(selection);
14
+ const paths = (Array.isArray(keyHolders) ? keyHolders : [keyHolders]).map((keyHolder) => keyHolder.getKeys().join('.'));
15
+ const mainAlias = this.expressionMap.mainAlias;
16
+ if (!(mainAlias === null || mainAlias === void 0 ? void 0 : mainAlias.hasMetadata)) {
17
+ // no entity to map properties with: return the columns by name
18
+ return this.returning(paths.map((path) => this.escape(path)).join(', '));
19
+ }
20
+ for (const path of paths) {
21
+ // a column, an embedded column (`address.city`) or a relation with join columns (`owner`)
22
+ if (mainAlias.metadata.findColumnsWithPropertyPath(path).length === 0) {
23
+ throw new TypeError(`returningTyped: "${path}" is not a column of ${mainAlias.metadata.name}`);
24
+ }
25
+ }
26
+ return this.returning(paths);
27
+ },
28
+ },
29
+ };
30
+ const queryBuilders = [
31
+ typeorm_1.InsertQueryBuilder,
32
+ typeorm_1.UpdateQueryBuilder,
33
+ typeorm_1.DeleteQueryBuilder,
34
+ SoftDeleteQueryBuilder_1.SoftDeleteQueryBuilder,
35
+ ];
36
+ queryBuilders.forEach((builder) => {
37
+ (0, prototype_utils_1.patchPrototype)(builder, extension);
38
+ });