typeorm-extensions 1.3.5 → 2.1.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.
- package/README.md +12 -10
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +2 -2
- package/dist/extensions/cursor-pagination.extension.d.ts +18 -0
- package/dist/extensions/cursor-pagination.extension.js +108 -0
- package/dist/extensions/index.d.ts +7 -0
- package/dist/extensions/index.js +7 -0
- package/dist/extensions/order-by-array-position.extension.d.ts +24 -0
- package/dist/extensions/order-by-array-position.extension.js +18 -0
- package/dist/extensions/order.extension.d.ts +3 -2
- package/dist/extensions/order.extension.js +16 -13
- package/dist/extensions/period-filter.extension.d.ts +29 -0
- package/dist/extensions/period-filter.extension.js +23 -0
- package/dist/extensions/typed/index.d.ts +10 -0
- package/dist/extensions/typed/index.js +10 -0
- package/dist/extensions/typed/typed-distinct/typed-distinct.extension.js +2 -2
- package/dist/extensions/typed/typed-joins/typed-joins.extension.js +2 -1
- package/dist/extensions/typed/typed-order-by/typed-order-by-array-position.extension.d.ts +20 -0
- package/dist/extensions/typed/typed-order-by/typed-order-by-array-position.extension.js +21 -0
- package/dist/extensions/typed/typed-order-by/typed-order-by.extension.d.ts +4 -3
- package/dist/extensions/typed/typed-order-by/typed-order-by.extension.js +7 -6
- package/dist/extensions/typed/typed-returning/typed-returning.extension.d.ts +27 -0
- package/dist/extensions/typed/typed-returning/typed-returning.extension.js +38 -0
- package/dist/extensions/typed/typed-select/typed-select.extension.d.ts +6 -6
- package/dist/extensions/typed/typed-select/typed-select.extension.js +11 -8
- package/dist/extensions/typed/typed-where/typed-period-filter.extension.d.ts +31 -0
- package/dist/extensions/typed/typed-where/typed-period-filter.extension.js +25 -0
- package/dist/extensions/typed/typed-where/typed-where-array.extension.d.ts +68 -0
- package/dist/extensions/typed/typed-where/typed-where-array.extension.js +41 -0
- package/dist/extensions/typed/typed-where/typed-where-ilike.extension.d.ts +62 -0
- package/dist/extensions/typed/typed-where/typed-where-ilike.extension.js +84 -0
- package/dist/extensions/typed/typed-where/typed-where-is-any.extension.d.ts +53 -0
- package/dist/extensions/typed/typed-where/typed-where-is-any.extension.js +38 -0
- package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.d.ts +53 -0
- package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.js +38 -0
- package/dist/extensions/typed/typed-where/typed-where-is-in.extension.d.ts +36 -119
- package/dist/extensions/typed/typed-where/typed-where-is-in.extension.js +14 -29
- package/dist/extensions/typed/typed-where/typed-where-json.extension.d.ts +53 -0
- package/dist/extensions/typed/typed-where/typed-where-json.extension.js +39 -0
- package/dist/extensions/typed/typed-where/typed-where-lower.extension.d.ts +59 -0
- package/dist/extensions/typed/typed-where/typed-where-lower.extension.js +86 -0
- package/dist/extensions/typed/typed-where/typed-where-similar.extension.d.ts +54 -0
- package/dist/extensions/typed/typed-where/typed-where-similar.extension.js +73 -0
- package/dist/extensions/typed/typed-where/typed-where.extension.d.ts +30 -74
- package/dist/extensions/typed/typed-where/typed-where.extension.js +13 -21
- package/dist/extensions/where-array.extension.d.ts +70 -0
- package/dist/extensions/where-array.extension.js +47 -0
- package/dist/extensions/where-is-any.extension.d.ts +142 -0
- package/dist/extensions/where-is-any.extension.js +44 -0
- package/dist/extensions/where-is-distinct-from.extension.d.ts +56 -0
- package/dist/extensions/where-is-distinct-from.extension.js +55 -0
- package/dist/extensions/where-json.extension.d.ts +55 -0
- package/dist/extensions/where-json.extension.js +64 -0
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.js +23 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/types/extensions/cursor-pagination.types.d.ts +16 -0
- package/dist/types/extensions/cursor-pagination.types.js +2 -0
- package/dist/types/extensions/get-many-with-totals.types.d.ts +3 -1
- package/dist/types/extensions/index.d.ts +4 -0
- package/dist/types/extensions/index.js +4 -0
- package/dist/types/extensions/order.types.d.ts +1 -1
- package/dist/types/extensions/period.types.d.ts +15 -0
- package/dist/types/extensions/period.types.js +2 -0
- package/dist/types/extensions/search.types.d.ts +68 -0
- package/dist/types/extensions/search.types.js +2 -0
- package/dist/types/extensions/typed-order-by.types.d.ts +3 -0
- package/dist/types/extensions/typed-order-by.types.js +2 -0
- package/dist/types/extensions/typed-where.types.d.ts +4 -4
- package/dist/types/extensions/where.types.d.ts +1 -0
- package/dist/types/interfaces/config.interface.d.ts +17 -0
- package/dist/types/interfaces/config.interface.js +2 -0
- package/dist/types/interfaces/cursor-pagination.interface.d.ts +16 -0
- package/dist/types/interfaces/cursor-pagination.interface.js +2 -0
- package/dist/types/interfaces/index.d.ts +3 -0
- package/dist/types/interfaces/index.js +3 -0
- package/dist/types/interfaces/period.interface.d.ts +12 -0
- package/dist/types/interfaces/period.interface.js +2 -0
- package/dist/types/modules/proxy-callback.types.d.ts +50 -3
- package/dist/types/modules/typed-params.types.d.ts +45 -0
- package/dist/types/modules/typed-params.types.js +2 -0
- package/dist/types/util-types/index.d.ts +3 -1
- package/dist/utils/cursor.utils.d.ts +31 -0
- package/dist/utils/cursor.utils.js +130 -0
- package/dist/utils/json-path.utils.d.ts +22 -0
- package/dist/utils/json-path.utils.js +57 -0
- package/dist/utils/order.utils.d.ts +15 -0
- package/dist/utils/order.utils.js +45 -0
- package/dist/utils/period.utils.d.ts +9 -0
- package/dist/utils/period.utils.js +82 -0
- package/dist/utils/proxy-key.utils.d.ts +19 -3
- package/dist/utils/proxy-key.utils.js +40 -15
- package/dist/utils/query-builder.utils.d.ts +48 -0
- package/dist/utils/query-builder.utils.js +257 -0
- package/dist/utils/search.utils.d.ts +27 -0
- package/dist/utils/search.utils.js +83 -0
- package/dist/utils/sql.utils.d.ts +29 -1
- package/dist/utils/sql.utils.js +91 -9
- package/dist/utils/table.utils.d.ts +86 -0
- package/dist/utils/table.utils.js +124 -0
- package/dist/utils/where.utils.d.ts +18 -1
- package/dist/utils/where.utils.js +62 -9
- package/helpers/package.json +7 -0
- package/package.json +5 -4
- package/dist/utils/identifier.utils.d.ts +0 -15
- package/dist/utils/identifier.utils.js +0 -45
|
@@ -0,0 +1,64 @@
|
|
|
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 sql_utils_1 = require("../utils/sql.utils");
|
|
7
|
+
/* eslint-enable @typescript-eslint/no-empty-interface, @typescript-eslint/no-unused-vars */
|
|
8
|
+
// patching
|
|
9
|
+
const createWhereJsonContainsMethod = (method) => function (field, value) {
|
|
10
|
+
return attachWhereJsonContains(this, method, field, value);
|
|
11
|
+
};
|
|
12
|
+
const createWhereJsonHasKeyMethod = (method) => function (field, key) {
|
|
13
|
+
return attachWhereJsonHasKey(this, method, field, key);
|
|
14
|
+
};
|
|
15
|
+
const extension = {
|
|
16
|
+
prototype: {
|
|
17
|
+
whereJsonContains: createWhereJsonContainsMethod('where'),
|
|
18
|
+
andWhereJsonContains: createWhereJsonContainsMethod('andWhere'),
|
|
19
|
+
orWhereJsonContains: createWhereJsonContainsMethod('orWhere'),
|
|
20
|
+
whereJsonHasKey: createWhereJsonHasKeyMethod('where'),
|
|
21
|
+
andWhereJsonHasKey: createWhereJsonHasKeyMethod('andWhere'),
|
|
22
|
+
orWhereJsonHasKey: createWhereJsonHasKeyMethod('orWhere'),
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
const queryBuilders = [
|
|
26
|
+
typeorm_1.SelectQueryBuilder,
|
|
27
|
+
typeorm_1.UpdateQueryBuilder,
|
|
28
|
+
typeorm_1.DeleteQueryBuilder,
|
|
29
|
+
];
|
|
30
|
+
queryBuilders.forEach((builder) => {
|
|
31
|
+
(0, prototype_utils_1.patchPrototype)(builder, extension);
|
|
32
|
+
});
|
|
33
|
+
// implementation
|
|
34
|
+
/**
|
|
35
|
+
* `CAST("table"."column" AS jsonb)`: makes `json` and `simple-json` (text) columns work, and is dropped by PostgreSQL
|
|
36
|
+
* for `jsonb` columns, so it doesn't stop a GIN index from being used.
|
|
37
|
+
*/
|
|
38
|
+
function toJsonbOperand(builder, field) {
|
|
39
|
+
return `CAST(${(0, query_builder_utils_1.toFieldSql)(builder, field)} AS jsonb)`;
|
|
40
|
+
}
|
|
41
|
+
function attachWhereJsonContains(builder, method, field, value) {
|
|
42
|
+
if (!method || typeof field !== 'string') {
|
|
43
|
+
return builder;
|
|
44
|
+
}
|
|
45
|
+
const json = JSON.stringify(value);
|
|
46
|
+
if (json === undefined) {
|
|
47
|
+
// `undefined` / functions have no JSON form; matching nothing or everything silently would hide the bug
|
|
48
|
+
throw new TypeError(`JSON condition on "${field}" expects a JSON value, got ${String(value)}`);
|
|
49
|
+
}
|
|
50
|
+
const paramName = (0, sql_utils_1.createUniqueParameterName)(method);
|
|
51
|
+
// CAST("table"."column" AS jsonb) @> CAST(:param AS jsonb), where :param is the JSON text
|
|
52
|
+
return builder[method](`${toJsonbOperand(builder, field)} @> CAST(:${paramName} AS jsonb)`, { [paramName]: json });
|
|
53
|
+
}
|
|
54
|
+
function attachWhereJsonHasKey(builder, method, field, key) {
|
|
55
|
+
if (!method || typeof field !== 'string') {
|
|
56
|
+
return builder;
|
|
57
|
+
}
|
|
58
|
+
if (typeof key !== 'string') {
|
|
59
|
+
throw new TypeError(`JSON key condition on "${field}" expects a string key, got ${String(key)}`);
|
|
60
|
+
}
|
|
61
|
+
const paramName = (0, sql_utils_1.createUniqueParameterName)(method);
|
|
62
|
+
// the `?` operator, not jsonb_exists(): only the operator can use a GIN index
|
|
63
|
+
return builder[method](`${toJsonbOperand(builder, field)} ? :${paramName}`, { [paramName]: key });
|
|
64
|
+
}
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.escapeLikePattern = void 0;
|
|
18
|
+
// Side-effect-free entry point: `typeorm-extensions/helpers`.
|
|
19
|
+
// Exports helpers (tableHelper, escapeLikePattern) only and patches nothing; importing the package root
|
|
20
|
+
// (`typeorm-extensions`) installs every QueryBuilder extension.
|
|
21
|
+
__exportStar(require("./utils/table.utils"), exports);
|
|
22
|
+
var search_utils_1 = require("./utils/search.utils");
|
|
23
|
+
Object.defineProperty(exports, "escapeLikePattern", { enumerable: true, get: function () { return search_utils_1.escapeLikePattern; } });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export * from './extensions';
|
|
2
2
|
export * from './types/interfaces';
|
|
3
3
|
export * from './types/extensions';
|
|
4
|
+
export type { Json } from './types/modules/proxy-callback.types';
|
|
5
|
+
export * from './utils/table.utils';
|
|
6
|
+
export { escapeLikePattern } from './utils/search.utils';
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.escapeLikePattern = void 0;
|
|
17
18
|
__exportStar(require("./extensions"), exports);
|
|
18
19
|
__exportStar(require("./types/interfaces"), exports);
|
|
19
20
|
__exportStar(require("./types/extensions"), exports);
|
|
21
|
+
__exportStar(require("./utils/table.utils"), exports);
|
|
22
|
+
var search_utils_1 = require("./utils/search.utils");
|
|
23
|
+
Object.defineProperty(exports, "escapeLikePattern", { enumerable: true, get: function () { return search_utils_1.escapeLikePattern; } });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CursorOrderParam } from '../interfaces/cursor-pagination.interface';
|
|
2
|
+
export type GetManyWithCursorOptions<Entity> = {
|
|
3
|
+
/**
|
|
4
|
+
* Columns that order the rows, e.g. `[{ field: 'createdAt', direction: 'DESC' }]`.
|
|
5
|
+
* The primary key is appended when it isn't listed, so the order is total and no row is skipped
|
|
6
|
+
* or repeated between pages. It replaces any ORDER BY of the query.
|
|
7
|
+
*/
|
|
8
|
+
orderBy: Array<CursorOrderParam<Entity>>;
|
|
9
|
+
/** Use `take` instead of `limit`, as needed for queries with joined collections. Defaults to `false`. */
|
|
10
|
+
useTakeAndSkip?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export interface ListWithCursor<Entity> {
|
|
13
|
+
list: Entity[];
|
|
14
|
+
/** Cursor of the next page, `null` on the last page. */
|
|
15
|
+
nextCursor: string | null;
|
|
16
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ApplyPaginationOptions } from './pagination.types';
|
|
2
|
+
/** Also takes the pagination options, e.g. `useTakeAndSkip` for queries with joined collections. */
|
|
3
|
+
export type GetManyWithTotalsOptions = ApplyPaginationOptions & {
|
|
2
4
|
disableTotalsCalculation?: boolean;
|
|
3
5
|
loadAll?: boolean;
|
|
4
6
|
};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export * from './pagination.types';
|
|
2
2
|
export * from './order.types';
|
|
3
|
+
export * from './typed-order-by.types';
|
|
3
4
|
export * from './get-many-with-totals.types';
|
|
4
5
|
export * from './where.types';
|
|
6
|
+
export * from './period.types';
|
|
7
|
+
export * from './cursor-pagination.types';
|
|
8
|
+
export * from './search.types';
|
|
@@ -16,5 +16,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./pagination.types"), exports);
|
|
18
18
|
__exportStar(require("./order.types"), exports);
|
|
19
|
+
__exportStar(require("./typed-order-by.types"), exports);
|
|
19
20
|
__exportStar(require("./get-many-with-totals.types"), exports);
|
|
20
21
|
__exportStar(require("./where.types"), exports);
|
|
22
|
+
__exportStar(require("./period.types"), exports);
|
|
23
|
+
__exportStar(require("./cursor-pagination.types"), exports);
|
|
24
|
+
__exportStar(require("./search.types"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type ApplyPeriodOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Include the start of the period (default `true`): `field >= periodFrom`.
|
|
4
|
+
* With `false`: `field > periodFrom`, and a date-only `periodFrom` (`'2024-01-01'`) excludes that
|
|
5
|
+
* whole day: `field >= '2024-01-02'`.
|
|
6
|
+
*/
|
|
7
|
+
includeFrom?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Include the end of the period (default `true`): `field <= periodTo`, and a date-only `periodTo`
|
|
10
|
+
* (`'2024-01-31'`) includes that whole day: `field < '2024-02-01'`.
|
|
11
|
+
* With `false`: `field < periodTo`, a half-open period such as
|
|
12
|
+
* `{ periodFrom: '2024-01-01', periodTo: '2024-02-01' }`.
|
|
13
|
+
*/
|
|
14
|
+
includeTo?: boolean;
|
|
15
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ObjectLiteral } from 'typeorm';
|
|
2
|
+
import type { KeyProxyCallback } from '../modules/proxy-callback.types';
|
|
3
|
+
/**
|
|
4
|
+
* Where the search value must be found in the column:
|
|
5
|
+
* - `'contains'` → `%value%`;
|
|
6
|
+
* - `'prefix'` → `value%`;
|
|
7
|
+
* - `'suffix'` → `%value`;
|
|
8
|
+
* - `'exact'` → `value` (a case-insensitive equality).
|
|
9
|
+
*/
|
|
10
|
+
export type LikeMatch = 'contains' | 'prefix' | 'suffix' | 'exact';
|
|
11
|
+
/** A raw SQL expression to search in, used as is in parentheses: `{ sql: \`u."firstName" || ' ' || u."lastName"\` }`. */
|
|
12
|
+
export type SqlExpression = {
|
|
13
|
+
sql: string;
|
|
14
|
+
};
|
|
15
|
+
/** A column (typed callback, may select several columns) or a raw SQL expression to search in. */
|
|
16
|
+
export type SearchSelector<Type extends ObjectLiteral> = KeyProxyCallback<Type> | SqlExpression;
|
|
17
|
+
export type TypedILikeOptions = {
|
|
18
|
+
/** Where the value must be found in the column. Defaults to `'contains'`. */
|
|
19
|
+
match?: LikeMatch;
|
|
20
|
+
/**
|
|
21
|
+
* Escape `%`, `_` and `\` in the value with a backslash, so they match themselves.
|
|
22
|
+
* Defaults to `true`; `false` passes the value's own wildcards through.
|
|
23
|
+
*/
|
|
24
|
+
escape?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Compare without accents (PostgreSQL `unaccent` extension): `unaccent(col) ILIKE unaccent(:p)`.
|
|
27
|
+
* `true` calls `unaccent`; a string names the function to call, e.g. an IMMUTABLE wrapper
|
|
28
|
+
* that an expression index is built on: `'immutable_unaccent'`, `'public.f_unaccent'`.
|
|
29
|
+
*/
|
|
30
|
+
unaccent?: boolean | string;
|
|
31
|
+
};
|
|
32
|
+
export type TypedSearchOptions = TypedILikeOptions & {
|
|
33
|
+
/** A value shorter than this (after trimming) adds no condition. Defaults to 1. */
|
|
34
|
+
minLength?: number;
|
|
35
|
+
};
|
|
36
|
+
export type TypedLowerOptions = {
|
|
37
|
+
/**
|
|
38
|
+
* Wraps both the column and the parameter: `(sql) => \`LOWER(TRIM(${sql}))\``.
|
|
39
|
+
* Defaults to `(sql) => \`LOWER(${sql})\``. Write it exactly like the expression index
|
|
40
|
+
* the condition should use. The result must contain `sql`.
|
|
41
|
+
*/
|
|
42
|
+
normalize?: (sql: string) => string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* pg_trgm operator, with the column on the left:
|
|
46
|
+
* - `'%'` → `col % :term`: `similarity(col, term)` is above `pg_trgm.similarity_threshold`;
|
|
47
|
+
* - `'%>'` → `col %> :term` (same as `:term <% col`): the term is similar to a word of the column,
|
|
48
|
+
* `word_similarity(term, col)` is above `pg_trgm.word_similarity_threshold`;
|
|
49
|
+
* - `'<%'` → `col <% :term`: the column is similar to a word of the term.
|
|
50
|
+
*
|
|
51
|
+
* `%` and `%>` can use a `gin_trgm_ops` / `gist_trgm_ops` index on the column, `<%` can't.
|
|
52
|
+
*/
|
|
53
|
+
export type TrigramOperator = '%' | '%>' | '<%';
|
|
54
|
+
export type TypedSimilarOptions = {
|
|
55
|
+
/** Defaults to `'%'`. */
|
|
56
|
+
operator?: TrigramOperator;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* pg_trgm score function:
|
|
60
|
+
* - `'similarity'` → `similarity(col, :term)`, pairs with the `%` operator;
|
|
61
|
+
* - `'word_similarity'` → `word_similarity(:term, col)`, pairs with `%>`;
|
|
62
|
+
* - `'strict_word_similarity'` → `strict_word_similarity(:term, col)`.
|
|
63
|
+
*/
|
|
64
|
+
export type SimilarityFunction = 'similarity' | 'word_similarity' | 'strict_word_similarity';
|
|
65
|
+
export type SimilarityScoreOptions = {
|
|
66
|
+
/** Defaults to `'similarity'`. */
|
|
67
|
+
function?: SimilarityFunction;
|
|
68
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type TypedWhereOptions = {
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Append a placeholder for the value to the condition (`=` → `= :param`). Defaults to `true`,
|
|
4
|
+
* or to `false` when the condition already references a key of the parameters (`'= :id'` with `{ id }`).
|
|
5
|
+
*/
|
|
3
6
|
addValue?: boolean;
|
|
4
7
|
};
|
|
5
|
-
export type TypedWhereIsInOptions = {
|
|
6
|
-
useDoubleQuotes?: boolean;
|
|
7
|
-
};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export type WhereMethods = 'where' | 'andWhere' | 'orWhere';
|
|
2
2
|
export type WhereInMethods = 'whereIsIn' | 'andWhereIsIn' | 'orWhereIsIn' | 'whereIsNotIn' | 'andWhereIsNotIn' | 'orWhereIsNotIn';
|
|
3
|
+
export type WhereAnyMethods = 'whereIsAny' | 'andWhereIsAny' | 'orWhereIsAny' | 'whereIsNotAny' | 'andWhereIsNotAny' | 'orWhereIsNotAny';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time options of the library, switched on by augmenting this interface in your project:
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* declare module 'typeorm-extensions' {
|
|
6
|
+
* interface TypeormExtensionsConfig {
|
|
7
|
+
* strictParams: true;
|
|
8
|
+
* }
|
|
9
|
+
* }
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* - `strictParams`: typed where methods check their value against the type of the column the callback
|
|
13
|
+
* selects (`whereTyped(q => q.price, '>', '10')` doesn't compile for a `number` column). Applies to calls
|
|
14
|
+
* without an explicit type argument: `whereTyped<Joins>(…)` keeps unchecked parameters.
|
|
15
|
+
*/
|
|
16
|
+
export interface TypeormExtensionsConfig {
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NonFunctionPropertyNames } from '../util-types';
|
|
2
|
+
import type { OrderDirection } from './ordering.interface';
|
|
3
|
+
/** The page to load with keyset (cursor) pagination, e.g. from query parameters. */
|
|
4
|
+
export interface CursorPaginationFilter {
|
|
5
|
+
/** `nextCursor` of the previous page; missing, `null` or empty for the first page. */
|
|
6
|
+
cursor?: string | null;
|
|
7
|
+
/** Rows per page, 10 by default. */
|
|
8
|
+
pageSize?: number;
|
|
9
|
+
}
|
|
10
|
+
/** A column that orders the rows of keyset pagination. */
|
|
11
|
+
export type CursorOrderParam<Entity> = {
|
|
12
|
+
/** Property of the main entity. The column must not be `NULL`. */
|
|
13
|
+
field: NonFunctionPropertyNames<Required<Entity>>;
|
|
14
|
+
/** `'ASC'` by default. */
|
|
15
|
+
direction?: OrderDirection;
|
|
16
|
+
};
|
|
@@ -16,3 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./ordering.interface"), exports);
|
|
18
18
|
__exportStar(require("./pagination.interface"), exports);
|
|
19
|
+
__exportStar(require("./period.interface"), exports);
|
|
20
|
+
__exportStar(require("./cursor-pagination.interface"), exports);
|
|
21
|
+
__exportStar(require("./config.interface"), exports);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** A period boundary: a `Date`, or a date / timestamp string the database understands (`'2024-01-31'`). */
|
|
2
|
+
export type PeriodBoundary = Date | string;
|
|
3
|
+
/**
|
|
4
|
+
* A period to filter rows by, e.g. from query parameters. Both ends are optional and,
|
|
5
|
+
* by default, inclusive: `periodFrom` → `field >= periodFrom`, `periodTo` → `field <= periodTo`.
|
|
6
|
+
* A date-only string (`'2024-01-31'`) stands for the whole day: `periodTo: '2024-01-31'`
|
|
7
|
+
* → `field < '2024-02-01'`. See `ApplyPeriodOptions` to exclude either end.
|
|
8
|
+
*/
|
|
9
|
+
export interface PeriodFilter {
|
|
10
|
+
periodFrom?: PeriodBoundary;
|
|
11
|
+
periodTo?: PeriodBoundary;
|
|
12
|
+
}
|
|
@@ -1,7 +1,54 @@
|
|
|
1
1
|
import { ObjectLiteral } from "typeorm";
|
|
2
2
|
import { EntityOnlyWithRelations, EntityWithoutRelations } from "./relations.types";
|
|
3
3
|
import { ColumnValueType } from "./column.types";
|
|
4
|
+
import { UnwrapArray } from "../util-types";
|
|
4
5
|
export type ProxyColumnValueType = ColumnValueType | ':value';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
type ProxyPropertyType<V> = NonNullable<V> extends readonly (infer U)[] ? NonNullable<U> : NonNullable<V>;
|
|
7
|
+
/** Objects that are column values, not relations or JSON objects to go into. */
|
|
8
|
+
type ProxyLeafObject = Date | ArrayBufferView | ArrayBuffer;
|
|
9
|
+
type IsProxyBranch<V> = ProxyPropertyType<V> extends ProxyLeafObject ? false : ProxyPropertyType<V> extends object ? true : false;
|
|
10
|
+
declare const jsonColumn: unique symbol;
|
|
11
|
+
/**
|
|
12
|
+
* Marks an entity property as a JSON column for typed callbacks, which then go into it at any depth:
|
|
13
|
+
* `(e) => e.details.meta.endTs` is `"e"."details" -> 'meta' ->> 'endTs'` in where methods. Without it a typed key
|
|
14
|
+
* stops at `alias.column` (`e.details.outcome`), as it does for relations. Values are assignable both ways.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* @Column({ type: 'jsonb' })
|
|
18
|
+
* details: Json<ExecutionDetails>;
|
|
19
|
+
*/
|
|
20
|
+
export type Json<T> = T & {
|
|
21
|
+
readonly [jsonColumn]?: true;
|
|
22
|
+
};
|
|
23
|
+
type IsJsonColumn<V> = typeof jsonColumn extends keyof NonNullable<V> ? true : false;
|
|
24
|
+
type IsJsonObject<V> = NonNullable<V> extends ProxyLeafObject | readonly unknown[] ? false : NonNullable<V> extends object ? true : false;
|
|
25
|
+
/** Inside a JSON column: objects to go into at any depth, while arrays and dates are values. */
|
|
26
|
+
type JsonPathProxy<T> = {
|
|
27
|
+
[K in keyof T as K extends typeof jsonColumn ? never : K]-?: IsJsonObject<T[K]> extends true ? JsonPathProxy<NonNullable<T[K]>> : T[K];
|
|
28
|
+
};
|
|
29
|
+
type JsonColumnProxy<V> = NonNullable<V> extends readonly unknown[] ? V : JsonPathProxy<NonNullable<V>>;
|
|
30
|
+
/**
|
|
31
|
+
* End of a typed key path. Typed keys are `column` or `alias.column` (`relation.column`),
|
|
32
|
+
* so an object at the second level (a nested relation, a JSON sub-object) can be selected
|
|
33
|
+
* as a whole but not dereferenced further: `u => u.images.user.email` is a compile error.
|
|
34
|
+
* Address deeper relations by their join alias with an alias-keyed type instead, and mark
|
|
35
|
+
* JSON columns with {@link Json} to go deeper into them.
|
|
36
|
+
*/
|
|
37
|
+
export interface KeyPathEnd {
|
|
38
|
+
readonly __keyPathEnd: never;
|
|
39
|
+
}
|
|
40
|
+
type ProxySecondLevel<T> = {
|
|
41
|
+
[K in keyof T]-?: IsJsonColumn<T[K]> extends true ? JsonColumnProxy<T[K]> : IsProxyBranch<T[K]> extends true ? KeyPathEnd : T[K];
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Type of the proxy passed to typed callbacks: two levels (`column` or `alias.column`),
|
|
45
|
+
* with arrays unwrapped (`user.images.url` for `images: ImageEntity[]`) and optional
|
|
46
|
+
* properties made required. A {@link Json} column is open at any depth.
|
|
47
|
+
*/
|
|
48
|
+
export type DeepUnwrapArray<T> = {
|
|
49
|
+
[K in keyof T]-?: IsJsonColumn<T[K]> extends true ? JsonColumnProxy<T[K]> : IsProxyBranch<T[K]> extends true ? ProxySecondLevel<ProxyPropertyType<T[K]>> : T[K];
|
|
50
|
+
};
|
|
51
|
+
export type KeyProxyCallback<T extends ObjectLiteral> = (proxy: DeepUnwrapArray<T>) => ProxyColumnValueType | Array<ProxyColumnValueType>;
|
|
52
|
+
export type RelationalKeyProxyCallback<T extends ObjectLiteral> = (proxy: DeepUnwrapArray<EntityOnlyWithRelations<T>>) => ProxyColumnValueType;
|
|
53
|
+
export type NonRelationalKeyProxyCallback<T extends ObjectLiteral> = (proxy: EntityWithoutRelations<UnwrapArray<T>>) => ProxyColumnValueType;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TypeormExtensionsConfig } from '../interfaces/config.interface';
|
|
2
|
+
import type { TypedWhereOptions } from '../extensions/typed-where.types';
|
|
3
|
+
import type { ArgsType } from '../util-types';
|
|
4
|
+
declare const unsetType: unique symbol;
|
|
5
|
+
/**
|
|
6
|
+
* Default of the `Type` argument of typed methods. Tells a call without a type argument, whose proxy is
|
|
7
|
+
* typed by the query builder's entity, from an explicit `whereTyped<Joins>(…)`.
|
|
8
|
+
*/
|
|
9
|
+
export interface UnsetType {
|
|
10
|
+
readonly [unsetType]: true;
|
|
11
|
+
}
|
|
12
|
+
/** Blocks inference from this position, so the column type comes from the callback alone (`NoInfer` before TypeScript 5.4). */
|
|
13
|
+
type NoInferType<T> = [T][T extends unknown ? 0 : never];
|
|
14
|
+
/** Whether `Type` was left out; an explicit `<any>` is assignable to `UnsetType` but was given. */
|
|
15
|
+
type IsUnset<Type> = 0 extends 1 & Type ? false : [Type] extends [UnsetType] ? true : false;
|
|
16
|
+
export type TypeOrDefault<Type, Default> = IsUnset<Type> extends true ? Default : Type;
|
|
17
|
+
type IsStrict<Type> = TypeormExtensionsConfig extends {
|
|
18
|
+
strictParams: true;
|
|
19
|
+
} ? IsUnset<Type> : false;
|
|
20
|
+
/** A column's value as a parameter: never `null` / `undefined` (they turn `= :value` into `= NULL`); a date may be a string. */
|
|
21
|
+
export type ParamValue<Value> = NonNullable<Value> extends Date ? Date | string : NonNullable<Value>;
|
|
22
|
+
type IsValueObject<P> = [P] extends [Date | ArrayBufferView | ArrayBuffer | readonly unknown[]] ? true : false;
|
|
23
|
+
/**
|
|
24
|
+
* The value itself, or a `{ name: value }` params object. A plain object value (a JSON column) can only be
|
|
25
|
+
* passed in a params object: at runtime a plain object always is one.
|
|
26
|
+
*/
|
|
27
|
+
type DirectOrField<P> = [P] extends [object] ? IsValueObject<P> extends true ? P | Readonly<Record<string, P>> : Readonly<Record<string, P>> : P | Readonly<Record<string, P>>;
|
|
28
|
+
type Trim<S extends string> = S extends ` ${infer Rest}` ? Trim<Rest> : S extends `${infer Rest} ` ? Trim<Rest> : S;
|
|
29
|
+
type NoValueOperator = 'IS NULL' | 'IS NOT NULL' | 'IS TRUE' | 'IS NOT TRUE' | 'IS FALSE' | 'IS NOT FALSE' | 'IS UNKNOWN' | 'IS NOT UNKNOWN';
|
|
30
|
+
type ComparisonOperator = '=' | '!=' | '<>' | '>' | '<' | '>=' | '<=';
|
|
31
|
+
type DistinctOperator = 'IS DISTINCT FROM' | 'IS NOT DISTINCT FROM';
|
|
32
|
+
type NullOperator = 'IS' | 'IS NOT';
|
|
33
|
+
type ListOperator = 'IN' | 'NOT IN';
|
|
34
|
+
type PatternOperator = 'LIKE' | 'NOT LIKE' | 'ILIKE' | 'NOT ILIKE';
|
|
35
|
+
type LooseWhereArgs = [parameters?: unknown, options?: TypedWhereOptions];
|
|
36
|
+
type ValueArgs<P> = [parameters: DirectOrField<P>, options?: TypedWhereOptions];
|
|
37
|
+
type StrictWhereArgs<Operator extends string, Value> = Uppercase<Trim<Operator>> extends infer Op ? Op extends NoValueOperator ? [parameters?: undefined, options?: TypedWhereOptions] : Op extends ComparisonOperator ? ValueArgs<ParamValue<Value>> : Op extends DistinctOperator ? ValueArgs<ParamValue<Value> | null> : Op extends NullOperator ? ValueArgs<null> : Op extends ListOperator ? ValueArgs<readonly ParamValue<Value>[]> : Op extends PatternOperator ? ValueArgs<string> : LooseWhereArgs : never;
|
|
38
|
+
/**
|
|
39
|
+
* Parameters and options of `whereTyped`. With `strictParams` the parameter is checked against the column type
|
|
40
|
+
* for the operators it knows; conditions written as SQL (`'BETWEEN :from AND :to'`, `'= true'`) aren't checked.
|
|
41
|
+
*/
|
|
42
|
+
export type TypedWhereArgs<Type, Operator extends string, Value> = IsStrict<Type> extends true ? StrictWhereArgs<NoInferType<Operator>, NoInferType<Value>> : LooseWhereArgs;
|
|
43
|
+
/** Values of `whereIsInTyped` / `whereIsAnyTyped`; with `strictParams`, of the column's type. */
|
|
44
|
+
export type TypedArrayArg<Type, Value> = IsStrict<Type> extends true ? readonly ParamValue<NoInferType<Value>>[] : ArgsType;
|
|
45
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/** Values for IN / NOT IN conditions. Readonly so `as const` arrays are accepted; the array is only read. */
|
|
2
|
+
export type ArgsType = readonly unknown[];
|
|
2
3
|
export type FunctionPropertyNames<T> = {
|
|
3
4
|
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
|
|
4
5
|
}[keyof T] & string;
|
|
@@ -6,3 +7,4 @@ export type NonFunctionPropertyNames<T> = {
|
|
|
6
7
|
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : K;
|
|
7
8
|
}[keyof T] & string;
|
|
8
9
|
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
10
|
+
export type UnwrapArray<T> = T extends (infer U)[] ? U : T;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DatabaseType } from 'typeorm';
|
|
2
|
+
/** A cursor that can't be decoded or doesn't belong to the query's order (e.g. tampered with). */
|
|
3
|
+
export declare class InvalidCursorError extends TypeError {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
/** A key value in a cursor: as read from the database, or a JS Date tagged as `{ $date }`. */
|
|
7
|
+
export type CursorValue = string | number | boolean;
|
|
8
|
+
/** base64url-encoded JSON of the key fields and the key values of the last row. */
|
|
9
|
+
export declare function encodeCursor(fields: string[], values: unknown[]): string;
|
|
10
|
+
/** Key values of a cursor made for `fields`; throws `InvalidCursorError` otherwise. */
|
|
11
|
+
export declare function decodeCursor(cursor: string, fields: string[]): Array<CursorValue | Date>;
|
|
12
|
+
/** Databases that compare row values: `(a, b) < (1, 2)`. */
|
|
13
|
+
export declare const supportsRowValueComparison: (driver?: DatabaseType) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Type to read a date / time column as text with, so the cursor keeps the database's own precision
|
|
16
|
+
* (PostgreSQL timestamps have microseconds, a JS Date only milliseconds). `undefined` when unknown.
|
|
17
|
+
*/
|
|
18
|
+
export declare const getTextCastType: (driver?: DatabaseType) => string | undefined;
|
|
19
|
+
export type KeysetColumn = {
|
|
20
|
+
/** quoted column, e.g. `"user"."createdAt"` */
|
|
21
|
+
column: string;
|
|
22
|
+
direction: 'ASC' | 'DESC';
|
|
23
|
+
/** parameter name holding the cursor value of this column */
|
|
24
|
+
paramName: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* WHERE condition for the rows after the cursor:
|
|
28
|
+
* - same direction everywhere, on databases with row values: `(a, b) > (:a, :b)`
|
|
29
|
+
* - otherwise: `a >= :a AND (a > :a OR (a = :a AND b < :b))`, where the leading bound lets an index on `a` be used
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildKeysetCondition(keys: KeysetColumn[], useRowValues: boolean): string;
|