typeorm-extensions 2.2.0-rc.0 → 2.2.0-rc.10

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 (112) hide show
  1. package/README.md +70 -39
  2. package/dist/decorators/calculated-column.decorator.d.ts +7 -3
  3. package/dist/decorators/calculated-column.decorator.js +35 -6
  4. package/dist/decorators/generated-column.decorator.d.ts +17 -0
  5. package/dist/decorators/generated-column.decorator.js +15 -0
  6. package/dist/decorators/index.d.ts +1 -0
  7. package/dist/decorators/index.js +1 -0
  8. package/dist/extensions/calculated-column.extension.d.ts +3 -3
  9. package/dist/extensions/calculated-column.extension.js +3 -16
  10. package/dist/extensions/cursor-pagination.extension.d.ts +2 -1
  11. package/dist/extensions/cursor-pagination.extension.js +7 -13
  12. package/dist/extensions/get-many-with-totals.extension.js +13 -31
  13. package/dist/extensions/join-lateral.extension.js +4 -6
  14. package/dist/extensions/order.extension.d.ts +2 -2
  15. package/dist/extensions/pagination.extension.d.ts +1 -1
  16. package/dist/extensions/period-filter.extension.d.ts +4 -0
  17. package/dist/extensions/period-filter.extension.js +1 -9
  18. package/dist/extensions/typed/index.d.ts +2 -0
  19. package/dist/extensions/typed/index.js +2 -0
  20. package/dist/extensions/typed/typed-calculated/typed-calculated-column.extension.d.ts +19 -0
  21. package/dist/extensions/typed/typed-calculated/typed-calculated-column.extension.js +17 -0
  22. package/dist/extensions/typed/typed-distinct/typed-distinct.extension.d.ts +6 -4
  23. package/dist/extensions/typed/typed-distinct/typed-distinct.extension.js +9 -2
  24. package/dist/extensions/typed/typed-group-by/typed-group-by.extension.d.ts +8 -7
  25. package/dist/extensions/typed/typed-group-by/typed-group-by.extension.js +1 -1
  26. package/dist/extensions/typed/typed-having/typed-having.extension.d.ts +4 -4
  27. package/dist/extensions/typed/typed-having/typed-having.extension.js +1 -1
  28. package/dist/extensions/typed/typed-joins/typed-join-and-map.extension.d.ts +19 -0
  29. package/dist/extensions/typed/typed-joins/typed-join-and-map.extension.js +23 -0
  30. package/dist/extensions/typed/typed-joins/typed-joins.extension.d.ts +4 -4
  31. package/dist/extensions/typed/typed-joins/typed-joins.extension.js +11 -22
  32. package/dist/extensions/typed/typed-order-by/typed-order-by-array-position.extension.d.ts +2 -2
  33. package/dist/extensions/typed/typed-order-by/typed-order-by.extension.d.ts +4 -2
  34. package/dist/extensions/typed/typed-order-by/typed-order-by.extension.js +18 -17
  35. package/dist/extensions/typed/typed-returning/typed-returning.extension.js +1 -9
  36. package/dist/extensions/typed/typed-root.extension.d.ts +26 -4
  37. package/dist/extensions/typed/typed-select/typed-select.extension.d.ts +6 -6
  38. package/dist/extensions/typed/typed-select/typed-select.extension.js +3 -9
  39. package/dist/extensions/typed/typed-where/typed-period-filter.extension.d.ts +5 -1
  40. package/dist/extensions/typed/typed-where/typed-period-filter.extension.js +1 -9
  41. package/dist/extensions/typed/typed-where/typed-where-array.extension.d.ts +13 -9
  42. package/dist/extensions/typed/typed-where/typed-where-array.extension.js +4 -16
  43. package/dist/extensions/typed/typed-where/typed-where-ilike.extension.d.ts +10 -6
  44. package/dist/extensions/typed/typed-where/typed-where-ilike.extension.js +1 -9
  45. package/dist/extensions/typed/typed-where/typed-where-is-any.extension.d.ts +10 -6
  46. package/dist/extensions/typed/typed-where/typed-where-is-any.extension.js +3 -20
  47. package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.d.ts +10 -6
  48. package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.js +3 -20
  49. package/dist/extensions/typed/typed-where/typed-where-is-in.extension.d.ts +10 -6
  50. package/dist/extensions/typed/typed-where/typed-where-is-in.extension.js +3 -21
  51. package/dist/extensions/typed/typed-where/typed-where-json.extension.d.ts +10 -6
  52. package/dist/extensions/typed/typed-where/typed-where-json.extension.js +4 -21
  53. package/dist/extensions/typed/typed-where/typed-where-lower.extension.d.ts +10 -6
  54. package/dist/extensions/typed/typed-where/typed-where-lower.extension.js +1 -9
  55. package/dist/extensions/typed/typed-where/typed-where-similar.extension.d.ts +8 -4
  56. package/dist/extensions/typed/typed-where/typed-where-similar.extension.js +1 -8
  57. package/dist/extensions/typed/typed-where/typed-where.extension.d.ts +7 -3
  58. package/dist/extensions/typed/typed-where/typed-where.extension.js +2 -10
  59. package/dist/extensions/union.extension.js +1 -1
  60. package/dist/extensions/where-array.extension.d.ts +4 -0
  61. package/dist/extensions/where-array.extension.js +1 -9
  62. package/dist/extensions/where-exists.extension.js +4 -15
  63. package/dist/extensions/where-is-any.extension.d.ts +41 -128
  64. package/dist/extensions/where-is-any.extension.js +1 -9
  65. package/dist/extensions/where-is-distinct-from.extension.d.ts +4 -0
  66. package/dist/extensions/where-is-distinct-from.extension.js +1 -9
  67. package/dist/extensions/where-is-in.extension.d.ts +38 -116
  68. package/dist/extensions/where-is-in.extension.js +11 -27
  69. package/dist/extensions/where-json.extension.d.ts +4 -0
  70. package/dist/extensions/where-json.extension.js +1 -9
  71. package/dist/extensions/where-relation-exists.extension.d.ts +6 -6
  72. package/dist/extensions/where-relation-exists.extension.js +5 -25
  73. package/dist/helpers.d.ts +5 -0
  74. package/dist/helpers.js +12 -3
  75. package/dist/index.d.ts +1 -2
  76. package/dist/index.js +1 -4
  77. package/dist/types/extensions/cursor-pagination.types.d.ts +5 -0
  78. package/dist/types/extensions/get-many-with-totals.types.d.ts +4 -0
  79. package/dist/types/extensions/index.d.ts +1 -0
  80. package/dist/types/extensions/index.js +1 -0
  81. package/dist/types/extensions/typed-group-by.types.d.ts +2 -2
  82. package/dist/types/extensions/typed-join-and-map.types.d.ts +6 -0
  83. package/dist/types/extensions/typed-join-and-map.types.js +2 -0
  84. package/dist/types/modules/typed-root.types.d.ts +13 -7
  85. package/dist/utils/calculated-column.utils.d.ts +11 -0
  86. package/dist/utils/calculated-column.utils.js +33 -4
  87. package/dist/utils/common.utils.d.ts +2 -0
  88. package/dist/utils/common.utils.js +4 -1
  89. package/dist/utils/cursor.utils.js +4 -6
  90. package/dist/utils/json-path.utils.js +2 -2
  91. package/dist/utils/pagination.utils.d.ts +7 -0
  92. package/dist/utils/pagination.utils.js +20 -10
  93. package/dist/utils/parameter.utils.d.ts +10 -0
  94. package/dist/utils/parameter.utils.js +22 -1
  95. package/dist/utils/period.utils.js +2 -1
  96. package/dist/utils/prototype.utils.d.ts +9 -0
  97. package/dist/utils/prototype.utils.js +12 -2
  98. package/dist/utils/query-builder.utils.d.ts +43 -4
  99. package/dist/utils/query-builder.utils.js +239 -36
  100. package/dist/utils/reserved-qualifier-guard.utils.d.ts +36 -0
  101. package/dist/utils/reserved-qualifier-guard.utils.js +98 -0
  102. package/dist/utils/reserved-qualifier.utils.d.ts +33 -0
  103. package/dist/utils/reserved-qualifier.utils.js +252 -0
  104. package/dist/utils/table.utils.d.ts +27 -11
  105. package/dist/utils/table.utils.js +58 -23
  106. package/dist/utils/time-zone.utils.d.ts +8 -0
  107. package/dist/utils/time-zone.utils.js +15 -5
  108. package/dist/utils/typed-where.utils.d.ts +9 -0
  109. package/dist/utils/typed-where.utils.js +17 -0
  110. package/dist/utils/where.utils.d.ts +2 -0
  111. package/dist/utils/where.utils.js +21 -14
  112. package/package.json +2 -2
package/README.md CHANGED
@@ -5,62 +5,93 @@
5
5
  [![Issues](https://img.shields.io/github/issues-raw/koenigstag/typeorm-extensions.svg?maxAge=25000)](https://github.com/koenigstag/typeorm-extensions/issues)
6
6
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/koenigstag/typeorm-extensions/pulls)
7
7
 
8
- ## Description
8
+ **Type-safe extensions for the TypeORM QueryBuilder.** Write selects, joins, conditions and ordering as property callbacks the compiler checks against your entities, page through results with totals or cursors, and build the raw SQL you still need with helpers that quote it for the database.
9
9
 
10
- TypeORM Extensions is a library that provides additional functionality and extensions for the TypeORM QueryBuilder. It aims to simplify common tasks and enhance the capabilities of TypeORM.
10
+ ```typescript
11
+ import 'typeorm-extensions';
11
12
 
12
- ## Features
13
+ const { list, total, hasMore } = await dataSource
14
+ .getRepository(UserEntity)
15
+ .createQueryBuilder('user')
16
+ .leftJoinTyped(user => user.images, 'images')
17
+ .whereTyped(user => user.createdAt, '>', since)
18
+ // a key inside a JSON column: "user"."metadata" ->> 'role' = :param
19
+ .andWhereTyped(user => user.metadata.role, '=', 'admin')
20
+ .orderByTyped(user => user.images.url, 'ASC', 'NULLS LAST')
21
+ .getManyWithTotals({ page: 1, pageSize: 20 });
22
+ ```
13
23
 
14
- - Simplicity: applying pagination and ordering to queries is as simple as calling a method.
15
- - Type Safety: the library is written in TypeScript and provides type-safe QueryBuilder methods based on Entity metadata.
16
- - Flexibility: the library is designed to be flexible and can be used with any TypeORM entity.
24
+ Try it in the [playground](https://koenigstag.github.io/typeorm-extensions/playground/): an editor and a database in the browser.
17
25
 
18
- ## Installation
26
+ ## Why
19
27
 
20
- To install `typeorm-extensions`:
28
+ The QueryBuilder takes columns as strings. A renamed property, a typo or a half-quoted alias compiles and then fails at runtime:
21
29
 
22
- Install using npm, yarn or pnpm:
23
- ```bash
24
- npm install typeorm-extensions
30
+ ```typescript
31
+ .where('user.createdAt > :since', { since }) // still compiles after createdAt is renamed
32
+ .andWhere('order."userId" = :userId', { userId }) // PostgreSQL: syntax error at or near "order"
25
33
  ```
26
34
 
27
- ## Usage
35
+ With the extensions a column is a property of the entity type, so the compiler catches the rename, and the SQL is built and quoted for you:
28
36
 
29
37
  ```typescript
30
- import 'typeorm-extensions'; // Importing the library root extends the QueryBuilder with all extensions
38
+ .whereTyped(user => user.createdAt, '>', since)
31
39
 
32
- // Or init specific extension: import 'typeorm-extensions/dist/extensions/pagination.extension';
40
+ const orders = tableHelper(OrderEntity, 'order');
41
+ .andWhere(`${orders.userId} = :userId`, { userId }) // "order"."userId" = :userId
42
+ ```
33
43
 
34
- const query = myDataSource
35
- .getRepository(UserEntity)
36
- .createQueryBuilder('users')
37
- // Join relation defined in entity model
38
- .leftJoinTyped(user => user.images, 'images')
39
- // Select type-safe properties
40
- .selectTyped(user => [
41
- user.id,
42
- user.name,
43
- user.email,
44
- user.images.url,
45
- ])
46
- .whereTyped(user => user.name, 'ILIKE', 'John')
47
- // Paths into JSON columns: "users"."metadata" ->> 'role' = :param
48
- .andWhereTyped(user => user.metadata.role, '=', 'admin')
49
- // Order by type-safe own and relation properties
50
- .orderByTyped(user => user.images.url, 'ASC', 'NULLS LAST')
51
- // Use pagination as simple as that
52
- .applyPaginationFilter({ page: 1, pageSize: 10 }, { useTakeAndSkip: true });
44
+ ## Installation
45
+
46
+ ```bash
47
+ npm install typeorm-extensions
53
48
  ```
54
49
 
55
- ## Documentation
50
+ `typeorm` 0.3.12 or later, or 1.x, is a peer dependency. Release candidates of the next version are published under the `next` tag: `npm install typeorm-extensions@next`.
51
+
52
+ ## Features
53
+
54
+ **Typed queries** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/typed/))
55
+ - `selectTyped`, `whereTyped` (keys inside JSON columns too; `strictParams` checks the parameter against the column type), `orderByTyped`, `groupByTyped` / `havingTyped`, `distinctOnTyped`, `returningTyped`
56
+ - typed joins and map joins (`leftJoinTyped`, `innerJoinAndMapOneTyped`, …), `typed<Root>()` for the aliases a query joins, `whereRelationExists` built from the relation metadata
57
+
58
+ **Pagination** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/pagination/))
59
+ - `getManyWithTotals` and `getRawManyWithTotals`: the rows of a page, the total, `page` / `pageSize`, `hasMore` and the order it was read in
60
+ - `getManyWithCursor`: keyset pagination with an opaque cursor
61
+ - `applyPaginationFilter` for a page of any query
56
62
 
57
- For more information, please refer to the [documentation](https://koenigstag.github.io/typeorm-extensions).
63
+ **Filters and ordering** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/where/))
64
+ - `whereIsIn` / `whereIsNotIn` that handle empty arrays, `whereIsAny`, null-safe `whereIsDistinctFrom`
65
+ - case-insensitive search with escaped wildcards, `applyPeriodFilter` in a time zone
66
+ - `applyOrder` from a client's order filter, `orderByArrayPosition`
58
67
 
59
- ## ToDo list
68
+ **Subqueries and joins**
69
+ - `whereExists` / `whereNotExists`, with the subquery's parameters renamed so they never clash with the outer query's
70
+ - `leftJoinLateral` / `innerJoinLateral`, `innerJoinUnnest`, `fromUnion` and CTE unions
71
+
72
+ **SQL helpers** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/utils/tableHelper/))
73
+ - `tableHelper`: quoted column references, conditions, counts, sums, JSON objects and paths, time zones. Given the entity class it writes the database name of a renamed column
74
+ - the reserved qualifier guard: catches SQL like `order."userId"` before PostgreSQL rejects it
75
+
76
+ **Columns** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/columns/generatedColumn/))
77
+ - `@CalculatedColumn` with `addSelectCalculated` / `orderByCalculated`: a value each query computes
78
+ - `@GeneratedColumn`: a column the database generates, which TypeORM reads and never writes
79
+
80
+ ## Entry points
81
+
82
+ | Import | Gives |
83
+ |---|---|
84
+ | `import 'typeorm-extensions'` | every QueryBuilder extension, and all exports |
85
+ | `import 'typeorm-extensions/dist/extensions/pagination.extension'` | one extension |
86
+ | `import { tableHelper, GeneratedColumn } from 'typeorm-extensions/helpers'` | the helpers, with no side effects |
87
+
88
+ ## Compatibility
89
+
90
+ TypeORM 0.3.12 and later, and 1.x. CI runs the test suite on SQLite with TypeORM 0.3.12, the latest 0.3 and 1.x, and on MySQL 8.4. Features that only work on some databases (array and JSON operators, `DISTINCT ON`, `orderByArrayPosition`, …) say so in their docs.
91
+
92
+ ## Documentation
60
93
 
61
- - [x] Add tests for pagination and order extensions
62
- - [x] Calculated columns: the `@CalculatedColumn` decorator and `addSelectCalculated`. `getMany`, `getOne` and `getManyWithTotals` fill them as they are, so they need no variants of their own
63
- - [x] Order by a calculated column: `orderByCalculated`, and `applyOrder` orders a `@CalculatedColumn` field by its value
94
+ The [documentation](https://koenigstag.github.io/typeorm-extensions/) has a page for every method, with its signature, options and examples, and the [playground](https://koenigstag.github.io/typeorm-extensions/playground/) runs them.
64
95
 
65
96
  ## License
66
97
 
@@ -3,11 +3,15 @@ export type CalculatedColumnOptions = {
3
3
  /**
4
4
  * The type the selected value is converted from, as for `@Column`: `'boolean'`, `'jsonb'`, `'timestamptz'`.
5
5
  * Defaults to the property's TypeScript type with `emitDecoratorMetadata`: `Number`, `String`, `Boolean`, `Date`.
6
- * `Number` turns numeric strings (a PostgreSQL `COUNT`, an `AVG`) into numbers, decimals kept; `Boolean`
7
- * reads `0` / `1`, `'0'` / `'1'` and `t` / `f` as well.
6
+ * `Number` and the numeric SQL types (`'integer'`, `'bigint'`, `'numeric'`, `'decimal'`, `'float'`, …) turn
7
+ * numeric strings (a PostgreSQL `COUNT`, an `AVG`) into numbers, decimals kept; `Boolean` reads `0` / `1`,
8
+ * `'0'` / `'1'` and `t` / `f` as well.
8
9
  */
9
10
  type?: ColumnType;
10
- /** Converts the selected value, e.g. JSON text from SQLite. Only `from` is used. */
11
+ /**
12
+ * Converts the selected value, e.g. JSON text from SQLite. Only `from` is used. It replaces the conversion of
13
+ * the type: it gets the value as the database returned it, a PostgreSQL `COUNT` as a string.
14
+ */
11
15
  transformer?: ValueTransformer | ValueTransformer[];
12
16
  };
13
17
  /**
@@ -21,7 +21,7 @@ const typeorm_1 = require("typeorm");
21
21
  */
22
22
  function CalculatedColumn(typeOrOptions) {
23
23
  return (object, propertyName) => {
24
- var _a, _b;
24
+ var _a;
25
25
  const options = typeof typeOrOptions === 'object' ? typeOrOptions : { type: typeOrOptions };
26
26
  const reflectedType = typeof Reflect === 'object' && typeof Reflect.getMetadata === 'function'
27
27
  ? Reflect.getMetadata('design:type', object, propertyName)
@@ -32,8 +32,9 @@ function CalculatedColumn(typeOrOptions) {
32
32
  throw new TypeError(`@CalculatedColumn() on ${object.constructor.name}.${String(propertyName)} needs a type: ` +
33
33
  `@CalculatedColumn('varchar'), or a property type of number, string, boolean or Date`);
34
34
  }
35
- const conversion = ownConversions.get(type);
36
- const transformers = [(_b = options.transformer) !== null && _b !== void 0 ? _b : []].flat();
35
+ const ownConversion = ownConversions.get(typeof type === 'string' ? type.toLowerCase() : type);
36
+ // a transformer of its own replaces the conversion and takes the value as the database returned it
37
+ const conversion = options.transformer === undefined ? ownConversion : undefined;
37
38
  // the mode of TypeORM's @VirtualColumn: not a column of the table, never inserted or updated;
38
39
  // with no `query` and `select: false` TypeORM never selects it by itself
39
40
  (0, typeorm_1.getMetadataArgsStorage)().columns.push({
@@ -42,12 +43,12 @@ function CalculatedColumn(typeOrOptions) {
42
43
  mode: 'virtual-property',
43
44
  options: {
44
45
  // the drivers parseInt a Number (AVG 150.5 → 150) and read MySQL's '0' as true, so these
45
- // types are converted here; String is a type no driver converts. TypeORM applies `from` last to first
46
- type: conversion ? String : type,
46
+ // types are converted here; String is a type no driver converts
47
+ type: ownConversion ? String : type,
47
48
  select: false,
48
49
  insert: false,
49
50
  update: false,
50
- transformer: conversion ? [...transformers, conversion] : options.transformer,
51
+ transformer: conversion !== null && conversion !== void 0 ? conversion : options.transformer,
51
52
  },
52
53
  });
53
54
  };
@@ -74,8 +75,36 @@ function toBoolean(value) {
74
75
  }
75
76
  const numberConversion = { from: toNumber, to: (value) => value };
76
77
  const booleanConversion = { from: toBoolean, to: (value) => value };
78
+ // a COUNT is a bigint and an AVG a numeric, which drivers hand over as strings to keep their precision
79
+ const numericTypes = [
80
+ 'int',
81
+ 'int2',
82
+ 'int4',
83
+ 'int8',
84
+ 'int64',
85
+ 'integer',
86
+ 'tinyint',
87
+ 'smallint',
88
+ 'mediumint',
89
+ 'bigint',
90
+ 'unsigned big int',
91
+ 'long',
92
+ 'numeric',
93
+ 'decimal',
94
+ 'dec',
95
+ 'fixed',
96
+ 'smalldecimal',
97
+ 'number',
98
+ 'float',
99
+ 'float4',
100
+ 'float8',
101
+ 'double',
102
+ 'double precision',
103
+ 'real',
104
+ ];
77
105
  const ownConversions = new Map([
78
106
  [Number, numberConversion],
107
+ ...numericTypes.map((type) => [type, numberConversion]),
79
108
  [Boolean, booleanConversion],
80
109
  ['boolean', booleanConversion],
81
110
  ['bool', booleanConversion],
@@ -0,0 +1,17 @@
1
+ import { ColumnOptions, ColumnType } from 'typeorm';
2
+ /** The options of `@Column`, without those that would let TypeORM write the column. */
3
+ export type GeneratedColumnOptions = Omit<ColumnOptions, 'insert' | 'update' | 'readonly'>;
4
+ /**
5
+ * A column of the table that the database computes: `GENERATED ALWAYS AS (…) STORED`. TypeORM reads it like any
6
+ * column and never writes it. `asExpression` alone doesn't stop TypeORM (0.3 and 1.x) from sending the column in
7
+ * `save`, `insert` and `update`, which the database rejects for a generated column.
8
+ *
9
+ * Pass `asExpression` and `generatedType: 'STORED'` when TypeORM creates the schema (synchronize, generated
10
+ * migrations); a column created by a hand-written migration needs neither.
11
+ *
12
+ * @example
13
+ * \@GeneratedColumn({ type: 'integer', asExpression: 'price * quantity', generatedType: 'STORED' })
14
+ * total: number;
15
+ */
16
+ export declare function GeneratedColumn(options?: GeneratedColumnOptions): PropertyDecorator;
17
+ export declare function GeneratedColumn(type: ColumnType, options?: GeneratedColumnOptions): PropertyDecorator;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GeneratedColumn = void 0;
4
+ const typeorm_1 = require("typeorm");
5
+ function GeneratedColumn(typeOrOptions, options = {}) {
6
+ const columnOptions = typeof typeOrOptions === 'object'
7
+ ? { ...typeOrOptions }
8
+ : typeOrOptions === undefined
9
+ ? { ...options }
10
+ : { ...options, type: typeOrOptions };
11
+ // TypeORM 0.3 applies `readonly` after `update`: `readonly: false` from an untyped caller would turn writes back on
12
+ delete columnOptions.readonly;
13
+ return (0, typeorm_1.Column)({ ...columnOptions, insert: false, update: false });
14
+ }
15
+ exports.GeneratedColumn = GeneratedColumn;
@@ -1 +1,2 @@
1
1
  export * from './calculated-column.decorator';
2
+ export * from './generated-column.decorator';
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./calculated-column.decorator"), exports);
18
+ __exportStar(require("./generated-column.decorator"), exports);
@@ -1,6 +1,6 @@
1
- import { ObjectLiteral, SelectQueryBuilder } from 'typeorm';
2
- /** `addSelect`'s selection: an SQL expression, or a factory that builds a subquery on the builder it is given. */
3
- export type CalculatedSelection<SubEntity extends ObjectLiteral = ObjectLiteral> = string | ((qb: SelectQueryBuilder<ObjectLiteral>) => SelectQueryBuilder<SubEntity>);
1
+ import { ObjectLiteral } from 'typeorm';
2
+ import { CalculatedSelection } from '../utils/calculated-column.utils';
3
+ export type { CalculatedSelection };
4
4
  declare module 'typeorm/query-builder/SelectQueryBuilder' {
5
5
  interface SelectQueryBuilder<Entity extends ObjectLiteral> {
6
6
  /**
@@ -7,27 +7,14 @@ const calculated_column_utils_1 = require("../utils/calculated-column.utils");
7
7
  const extension = {
8
8
  prototype: {
9
9
  addSelectCalculated(selection, property, alias) {
10
- return addSelectCalculated(this, selection, property, alias);
10
+ return (0, calculated_column_utils_1.addSelectCalculatedColumn)(this, selection, property, alias, 'addSelectCalculated');
11
11
  },
12
12
  orderByCalculated(property, order, nulls, alias) {
13
- return attachOrderByCalculated(this, 'orderBy', property, order, nulls, alias);
13
+ return (0, calculated_column_utils_1.orderByCalculatedColumn)(this, 'orderBy', property, order, nulls, alias, 'orderByCalculated');
14
14
  },
15
15
  addOrderByCalculated(property, order, nulls, alias) {
16
- return attachOrderByCalculated(this, 'addOrderBy', property, order, nulls, alias);
16
+ return (0, calculated_column_utils_1.orderByCalculatedColumn)(this, 'addOrderBy', property, order, nulls, alias, 'addOrderByCalculated');
17
17
  },
18
18
  },
19
19
  };
20
20
  (0, prototype_utils_1.patchPrototype)(typeorm_1.SelectQueryBuilder, extension);
21
- // implementation
22
- function addSelectCalculated(builder, selection, property, aliasName) {
23
- const { key } = (0, calculated_column_utils_1.resolveCalculatedColumn)(builder, property, aliasName, 'addSelectCalculated');
24
- // two calls: addSelect's overloads take a string or a factory, not their union
25
- if (typeof selection === 'function') {
26
- return builder.addSelect(selection, key);
27
- }
28
- return builder.addSelect(selection, key);
29
- }
30
- function attachOrderByCalculated(builder, method, property, order, nulls, aliasName) {
31
- const orderAlias = (0, calculated_column_utils_1.selectCalculatedForOrder)(builder, property, aliasName, `${method}Calculated`);
32
- return builder[method](orderAlias, order, nulls);
33
- }
@@ -1,3 +1,4 @@
1
+ import { ObjectLiteral } from 'typeorm';
1
2
  import type { CursorPaginationFilter } from '../types/interfaces/cursor-pagination.interface';
2
3
  import type { GetManyWithCursorOptions, ListWithCursor } from '../types/extensions/cursor-pagination.types';
3
4
  export { InvalidCursorError } from '../utils/cursor.utils';
@@ -14,6 +15,6 @@ declare module 'typeorm/query-builder/SelectQueryBuilder' {
14
15
  * The query builder itself is not changed. Throws `InvalidCursorError` for a cursor that can't
15
16
  * be decoded or was made for another order.
16
17
  */
17
- getManyWithCursor(cursorFilter: CursorPaginationFilter | null | undefined, options: GetManyWithCursorOptions<Entity>): Promise<ListWithCursor<Entity>>;
18
+ getManyWithCursor<CursorEntity extends ObjectLiteral = Entity>(this: SelectQueryBuilder<CursorEntity>, cursorFilter: CursorPaginationFilter | null | undefined, options: GetManyWithCursorOptions<CursorEntity>): Promise<ListWithCursor<CursorEntity>>;
18
19
  }
19
20
  }
@@ -2,11 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InvalidCursorError = void 0;
4
4
  const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder");
5
- const constants_1 = require("../constants");
5
+ const pagination_utils_1 = require("../utils/pagination.utils");
6
6
  const prototype_utils_1 = require("../utils/prototype.utils");
7
7
  const order_utils_1 = require("../utils/order.utils");
8
8
  const where_utils_1 = require("../utils/where.utils");
9
9
  const sql_utils_1 = require("../utils/sql.utils");
10
+ const query_builder_utils_1 = require("../utils/query-builder.utils");
10
11
  const cursor_utils_1 = require("../utils/cursor.utils");
11
12
  var cursor_utils_2 = require("../utils/cursor.utils");
12
13
  Object.defineProperty(exports, "InvalidCursorError", { enumerable: true, get: function () { return cursor_utils_2.InvalidCursorError; } });
@@ -44,14 +45,11 @@ function getCursorKeys(builder, orderBy) {
44
45
  }
45
46
  return keys;
46
47
  }
47
- const getPageSize = (pageSize) => typeof pageSize === 'number' && Number.isFinite(pageSize) && pageSize >= 1
48
- ? Math.floor(pageSize)
49
- : constants_1.defaultPageSize;
50
48
  async function getManyWithCursor(builder, cursorFilter, options) {
51
49
  const { orderBy, useTakeAndSkip = false } = options !== null && options !== void 0 ? options : {};
52
50
  const keys = getCursorKeys(builder, orderBy);
53
51
  const fields = keys.map((key) => key.column.propertyPath);
54
- const pageSize = getPageSize(cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.pageSize);
52
+ const pageSize = (0, pagination_utils_1.normalizePageSize)(cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.pageSize);
55
53
  const query = builder.clone();
56
54
  const alias = query.alias;
57
55
  const driver = query.connection.driver.options.type;
@@ -68,6 +66,7 @@ async function getManyWithCursor(builder, cursorFilter, options) {
68
66
  }
69
67
  query.orderBy();
70
68
  keys.forEach((key) => query.addOrderBy(`${alias}.${key.column.propertyPath}`, key.direction));
69
+ const appliedOrderBy = (0, query_builder_utils_1.getAppliedOrderBy)(query);
71
70
  // the key values of the last row are read from the raw result: dates and times as text, so the
72
71
  // cursor keeps the database's precision (PostgreSQL timestamps have microseconds, a JS Date has not)
73
72
  const keyAliases = keys.map((key, i) => {
@@ -81,16 +80,11 @@ async function getManyWithCursor(builder, cursorFilter, options) {
81
80
  .map((key, i) => ({ key, i }))
82
81
  .filter(({ key }) => key.column.isPrimary)
83
82
  .map(({ key, i }) => ({ column: key.column, keyAlias: keyAliases[i] }));
84
- if (useTakeAndSkip) {
85
- query.take(pageSize + 1);
86
- }
87
- else {
88
- query.limit(pageSize + 1);
89
- }
83
+ (0, pagination_utils_1.setPageLimit)(query, pageSize + 1, useTakeAndSkip);
90
84
  const { entities, raw } = await query.getRawAndEntities();
91
85
  const list = entities.slice(0, pageSize);
92
86
  if (entities.length <= pageSize) {
93
- return { list, nextCursor: null, hasMore: false };
87
+ return { list, nextCursor: null, hasMore: false, pageSize, orderBy: appliedOrderBy };
94
88
  }
95
89
  // with joined collections there are several raw rows per entity: find one of the last entity's
96
90
  const last = list[list.length - 1];
@@ -105,5 +99,5 @@ async function getManyWithCursor(builder, cursorFilter, options) {
105
99
  }
106
100
  return value;
107
101
  });
108
- return { list, nextCursor: (0, cursor_utils_1.encodeCursor)(fields, values), hasMore: true };
102
+ return { list, nextCursor: (0, cursor_utils_1.encodeCursor)(fields, values), hasMore: true, pageSize, orderBy: appliedOrderBy };
109
103
  }
@@ -5,6 +5,7 @@ const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder")
5
5
  const pagination_utils_1 = require("../utils/pagination.utils");
6
6
  const constants_1 = require("../constants");
7
7
  const prototype_utils_1 = require("../utils/prototype.utils");
8
+ const query_builder_utils_1 = require("../utils/query-builder.utils");
8
9
  // patching
9
10
  const extension = {
10
11
  prototype: {
@@ -30,8 +31,7 @@ const extension = {
30
31
  * runner, so the one that starts second finds a transaction active and does not open its own.
31
32
  */
32
33
  function canCountConcurrently(builder) {
33
- const queryBuilder = builder;
34
- return !queryBuilder.queryRunner;
34
+ return !(0, query_builder_utils_1.getQueryRunner)(builder);
35
35
  }
36
36
  function getManyWithTotals(builder, paginationFilter, options) {
37
37
  return readWithTotals(builder, paginationFilter, options, {
@@ -57,10 +57,9 @@ function getRawManyWithTotals(builder, paginationFilter, options) {
57
57
  });
58
58
  }
59
59
  async function readWithTotals(builder, paginationFilter, options, read) {
60
- var _a;
61
60
  const { disableTotalsCalculation = false, loadAll = false, ...paginationOptions } = options || {};
62
- const { limit, offset } = loadAll
63
- ? { limit: null, offset: null }
61
+ const { limit, offset, page, pageSize } = loadAll
62
+ ? { limit: null, offset: null, page: null, pageSize: null }
64
63
  : (0, pagination_utils_1.getLimitAndOffset)(paginationFilter);
65
64
  // without a total, one row past the page is what tells whether more rows follow
66
65
  const probeForMore = disableTotalsCalculation && !loadAll && limit !== null;
@@ -70,15 +69,9 @@ async function readWithTotals(builder, paginationFilter, options, read) {
70
69
  }
71
70
  const paged = builder.applyPaginationFilter(paginationFilter, paginationOptions);
72
71
  if (probeForMore) {
73
- const probeLimit = limit + 1;
74
72
  const { useTakeAndSkip = constants_1.defaultUseTakeAndSkip } = paginationOptions;
75
73
  // replaces the limit applyPaginationFilter just set, keeping its offset
76
- if (useTakeAndSkip) {
77
- paged.take(probeLimit);
78
- }
79
- else {
80
- paged.limit(probeLimit);
81
- }
74
+ (0, pagination_utils_1.setPageLimit)(paged, limit + 1, useTakeAndSkip);
82
75
  }
83
76
  return read.rows(paged);
84
77
  };
@@ -109,18 +102,15 @@ async function readWithTotals(builder, paginationFilter, options, read) {
109
102
  hasMore =
110
103
  total !== null && offset !== null ? offset + rows.length < total : null;
111
104
  }
112
- // allOrderBys, not orderBys: it is what the SQL is built from, so without an explicit order it
113
- // falls back to the `orderBy` of @Entity, and respects the `disable-global-order` option
114
- const orderBy = Object.entries((_a = builder.expressionMap.allOrderBys) !== null && _a !== void 0 ? _a : {}).map(([selector, value]) => typeof value === 'string'
115
- ? { selector, order: value }
116
- : { selector, order: value.order, nulls: value.nulls });
117
105
  return {
118
106
  list,
119
107
  total,
108
+ page,
109
+ pageSize,
120
110
  limit,
121
111
  offset,
122
112
  hasMore,
123
- orderBy,
113
+ orderBy: (0, query_builder_utils_1.getAppliedOrderBy)(builder),
124
114
  };
125
115
  }
126
116
  /**
@@ -130,10 +120,11 @@ async function readWithTotals(builder, paginationFilter, options, read) {
130
120
  */
131
121
  async function bothOrFirstError(first, second) {
132
122
  const [a, b] = await Promise.all([settle(first), settle(second)]);
133
- if (!a.fulfilled) {
123
+ // `=== false`: without strictNullChecks `!fulfilled` doesn't narrow the union
124
+ if (a.fulfilled === false) {
134
125
  throw a.reason;
135
126
  }
136
- if (!b.fulfilled) {
127
+ if (b.fulfilled === false) {
137
128
  throw b.reason;
138
129
  }
139
130
  return [a.value, b.value];
@@ -142,9 +133,6 @@ async function bothOrFirstError(first, second) {
142
133
  function settle(promise) {
143
134
  return promise.then((value) => ({ fulfilled: true, value }), (reason) => ({ fulfilled: false, reason }));
144
135
  }
145
- function getQueryRunner(builder) {
146
- return builder.queryRunner;
147
- }
148
136
  /**
149
137
  * Counts the rows of a raw query without its pagination and ORDER BY. The query goes into a
150
138
  * subquery, so the count is that of its result rows whatever it groups or filters. A
@@ -153,13 +141,7 @@ function getQueryRunner(builder) {
153
141
  */
154
142
  async function countRawRows(builder, countExpression) {
155
143
  var _a;
156
- const unpaged = cloneForCount(builder)
157
- .orderBy()
158
- .offset(undefined)
159
- .limit(undefined)
160
- .skip(undefined)
161
- .take(undefined)
162
- .setOption('disable-global-order');
144
+ const unpaged = (0, query_builder_utils_1.withoutPaging)(cloneForCount(builder));
163
145
  let counting;
164
146
  if (countExpression) {
165
147
  // the expression counts on its own: DISTINCT ON next to an aggregate is an error
@@ -170,7 +152,7 @@ async function countRawRows(builder, countExpression) {
170
152
  else {
171
153
  // on the builder's query runner, if it has one: inside its transaction
172
154
  const counter = builder.connection
173
- .createQueryBuilder(getQueryRunner(builder))
155
+ .createQueryBuilder((0, query_builder_utils_1.getQueryRunner)(builder))
174
156
  .select('COUNT(*)', 'count')
175
157
  .from(`(${unpaged.getQuery()})`, 'counted')
176
158
  .setParameters(unpaged.getParameters());
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const typeorm_1 = require("typeorm");
4
4
  const parameter_utils_1 = require("../utils/parameter.utils");
5
+ const query_builder_utils_1 = require("../utils/query-builder.utils");
5
6
  const prototype_utils_1 = require("../utils/prototype.utils");
6
7
  /* eslint-enable @typescript-eslint/no-empty-interface, @typescript-eslint/no-unused-vars */
7
8
  // patching
@@ -32,18 +33,15 @@ function toLateralSql(builder, subQuery) {
32
33
  return { sql: `(${subQuery})`, parameters: {} };
33
34
  }
34
35
  const subBuilder = typeof subQuery === 'function' ? subQuery(detachedSubQuery(builder)) : subQuery;
35
- const query = subBuilder.getQuery();
36
- // a builder from subQuery() wraps itself in parentheses, any other doesn't
37
- const wrapped = subBuilder.expressionMap.subQuery ? query : `(${query})`;
38
- const [sql, parameters] = (0, parameter_utils_1.withUniqueParameterNames)(wrapped, subBuilder.getParameters());
39
- return { sql, parameters };
36
+ const [sql, parameters] = (0, parameter_utils_1.toSubQuerySql)(subBuilder);
37
+ return { sql: `(${sql})`, parameters };
40
38
  }
41
39
  /**
42
40
  * A subquery builder for a factory, as TypeORM's `subQuery()` but without a parent: TypeORM's would
43
41
  * set the factory's parameters on the outer query under their own names, before they can be renamed.
44
42
  */
45
43
  function detachedSubQuery(builder) {
46
- const queryRunner = builder.queryRunner;
44
+ const queryRunner = (0, query_builder_utils_1.getQueryRunner)(builder);
47
45
  const subQuery = builder.connection.createQueryBuilder(queryRunner);
48
46
  subQuery.expressionMap.subQuery = true;
49
47
  return subQuery;
@@ -3,7 +3,7 @@ import { ObjectLiteral } from 'typeorm';
3
3
  import type { ApplyOrderOptions } from '../types/extensions/order.types';
4
4
  declare module 'typeorm/query-builder/SelectQueryBuilder' {
5
5
  interface 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>;
6
+ applyOrder<OrderEntity extends ObjectLiteral>(orderBy: OrderParam<OrderEntity>, options?: ApplyOrderOptions): this;
7
+ applyOrderFilter<OrderEntity extends ObjectLiteral>(orderFilter: OrderFilter<OrderEntity>, options?: ApplyOrderOptions): this;
8
8
  }
9
9
  }
@@ -3,6 +3,6 @@ import { ObjectLiteral } from 'typeorm';
3
3
  import { ApplyPaginationOptions } from '../types/extensions/pagination.types';
4
4
  declare module 'typeorm/query-builder/SelectQueryBuilder' {
5
5
  interface SelectQueryBuilder<Entity extends ObjectLiteral> {
6
- applyPaginationFilter(paginationFilter: PaginationFilter, options?: ApplyPaginationOptions): SelectQueryBuilder<Entity>;
6
+ applyPaginationFilter(paginationFilter: PaginationFilter, options?: ApplyPaginationOptions): this;
7
7
  }
8
8
  }
@@ -27,3 +27,7 @@ declare module 'typeorm/query-builder/DeleteQueryBuilder' {
27
27
  interface DeleteQueryBuilder<Entity extends ObjectLiteral> extends PeriodFilterMethods {
28
28
  }
29
29
  }
30
+ declare module 'typeorm/query-builder/SoftDeleteQueryBuilder' {
31
+ interface SoftDeleteQueryBuilder<Entity extends ObjectLiteral> extends PeriodFilterMethods {
32
+ }
33
+ }
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const typeorm_1 = require("typeorm");
4
3
  const prototype_utils_1 = require("../utils/prototype.utils");
5
4
  const query_builder_utils_1 = require("../utils/query-builder.utils");
6
5
  const period_utils_1 = require("../utils/period.utils");
@@ -13,11 +12,4 @@ const extension = {
13
12
  },
14
13
  },
15
14
  };
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
- });
15
+ (0, prototype_utils_1.patchPrototypes)(prototype_utils_1.WHERE_QUERY_BUILDERS, extension);
@@ -2,6 +2,7 @@ export * from './typed-distinct/typed-distinct.extension';
2
2
  export * from './typed-group-by/typed-group-by.extension';
3
3
  export * from './typed-having/typed-having.extension';
4
4
  export * from './typed-joins/typed-joins.extension';
5
+ export * from './typed-joins/typed-join-and-map.extension';
5
6
  export * from './typed-order-by/typed-order-by.extension';
6
7
  export * from './typed-select/typed-select.extension';
7
8
  export * from './typed-where/typed-where.extension';
@@ -16,4 +17,5 @@ export * from './typed-where/typed-where-lower.extension';
16
17
  export * from './typed-where/typed-where-similar.extension';
17
18
  export * from './typed-returning/typed-returning.extension';
18
19
  export * from './typed-order-by/typed-order-by-array-position.extension';
20
+ export * from './typed-calculated/typed-calculated-column.extension';
19
21
  export * from './typed-root.extension';
@@ -19,6 +19,7 @@ __exportStar(require("./typed-distinct/typed-distinct.extension"), exports);
19
19
  __exportStar(require("./typed-group-by/typed-group-by.extension"), exports);
20
20
  __exportStar(require("./typed-having/typed-having.extension"), exports);
21
21
  __exportStar(require("./typed-joins/typed-joins.extension"), exports);
22
+ __exportStar(require("./typed-joins/typed-join-and-map.extension"), exports);
22
23
  __exportStar(require("./typed-order-by/typed-order-by.extension"), exports);
23
24
  __exportStar(require("./typed-select/typed-select.extension"), exports);
24
25
  __exportStar(require("./typed-where/typed-where.extension"), exports);
@@ -33,4 +34,5 @@ __exportStar(require("./typed-where/typed-where-lower.extension"), exports);
33
34
  __exportStar(require("./typed-where/typed-where-similar.extension"), exports);
34
35
  __exportStar(require("./typed-returning/typed-returning.extension"), exports);
35
36
  __exportStar(require("./typed-order-by/typed-order-by-array-position.extension"), exports);
37
+ __exportStar(require("./typed-calculated/typed-calculated-column.extension"), exports);
36
38
  __exportStar(require("./typed-root.extension"), exports);