@avytrix/nestjs-drizzle-crud 3.7.2 → 3.8.1
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/AGENT.md
CHANGED
|
@@ -189,7 +189,10 @@ Operators inside an object value: `eq`, `gt`, `gte`, `lt`, `lte`, `neq`, `like`,
|
|
|
189
189
|
`sql.caseSensitive`), `iStartsWith`, `iEndsWith`, `iContains` (always
|
|
190
190
|
case-insensitive), and on PostgreSQL only `arrayContains`, `arrayOverlaps` and
|
|
191
191
|
`jsonContains`. Any other operator is a 400. A bare string is an exact match,
|
|
192
|
-
not a pattern, and `%` in it is a literal character.
|
|
192
|
+
not a pattern, and `%` in it is a literal character. On a text column it is
|
|
193
|
+
`lower(col) = lower($1)` in `findAll` and `count` (index `lower(col)`), but a
|
|
194
|
+
plain, case-sensitive `=` in `findOne`. https://crud.avytrix.dev/docs/indexes/
|
|
195
|
+
lists the index each query shape needs; the package creates none. `isNull` and `isNotNull`
|
|
193
196
|
take `true` / `false` or the strings `"true"` / `"false"`.
|
|
194
197
|
|
|
195
198
|
OR and NOT need no raw Drizzle. `and`, `or` and `not` group filter objects, nest,
|
|
@@ -392,7 +395,9 @@ restore, force delete, attach included) and written onto every created row, so
|
|
|
392
395
|
another tenant's id is a 404. A missing context value is a 403, never an
|
|
393
396
|
unscoped query. A belongs-to key a write sets must point at a row of the
|
|
394
397
|
caller's tenant when the related table has the scope columns; another tenant's
|
|
395
|
-
row is the same 400 as a missing one.
|
|
398
|
+
row is the same 400 as a missing one. Nested create children get the same check
|
|
399
|
+
for the keys their nested relation declares in its own `relations`; declare
|
|
400
|
+
every belongs-to a child can set. Do not add `tenant_id` to filters or DTOs
|
|
396
401
|
by hand, and use `skipScope: true` only in trusted server code.
|
|
397
402
|
|
|
398
403
|
# Optimistic locking
|
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,146 @@ restore, `ER_DUP_ENTRY` mapping, dialect locking) are tracked separately and unc
|
|
|
11
11
|
3.3.1, 3.4.0, 3.5.0 and 3.5.2 were tagged but never published to npm. Their changes reached npm
|
|
12
12
|
in 3.5.1 (3.3.1 to 3.5.0) and 3.6.0 (3.5.2). The unscoped `nestjs-drizzle-crud` stops at 3.3.0.
|
|
13
13
|
|
|
14
|
+
## [3.8.1] - 2026-11-04
|
|
15
|
+
|
|
16
|
+
Fixes from the 3.8.0 audit (D-01 to D-11). Three of them close gaps in tenant isolation: a scope
|
|
17
|
+
handed from one service to another, a batch key that differs from another tenant's only in case,
|
|
18
|
+
and nested children that skipped the key check. No public API or override point changed.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Text keys compare exactly in the tenant key check, `attach` and `sync`: `"US"` and `"us"` are
|
|
23
|
+
two keys, as they are two rows on PostgreSQL and SQLite. Before, every key was lowercased, so a
|
|
24
|
+
batch holding both could point a row at another tenant's `"us"` and get a 201 where `"us"`
|
|
25
|
+
alone was a 400. A uuid column still ignores case, and so does MySQL, to follow its default
|
|
26
|
+
collations, which match `"us"` to a stored `"US"` and cannot hold both as distinct keys.
|
|
27
|
+
- On a scoped entity, a nested create checks the belongs-to keys of the rows it writes (a
|
|
28
|
+
belongs-to parent, has-many children, new many-to-many targets) against the caller's tenant,
|
|
29
|
+
using the nested relation's own `relations`. A child key to another tenant's row is the same
|
|
30
|
+
400 `ForeignKeyViolationException` as a key to a missing row, and nothing is written. It was a
|
|
31
|
+
201. A child key that no nested relation declares is still left to the database's foreign key.
|
|
32
|
+
- `massSoftDelete` and `massDelete` put `deleted_at IS NULL` in the `WHERE` of their write, as the
|
|
33
|
+
single-row writes have since 3.8.0. A row soft-deleted between the existence check and the
|
|
34
|
+
write is a 404 entry and the batch writes nothing, instead of being stamped again or
|
|
35
|
+
hard-deleted. `massForceDelete` still removes a row in either state.
|
|
36
|
+
- `massCreate` loads `relations` and `counts` inside its transaction again. A load that fails
|
|
37
|
+
rolls the rows back, so a retry no longer stores them twice. The other mass writes, which give
|
|
38
|
+
the same rows when retried, still load after the commit.
|
|
39
|
+
- `update` of a missing row with a value the database refuses (`update(999, { views: "abc" })`)
|
|
40
|
+
is a 404, as it was before 3.8.0, instead of a 400.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- The per-call scope memo is keyed by service and by the context it was resolved for. Options
|
|
45
|
+
handed from one service to another, the natural way for a subclass override to share a
|
|
46
|
+
transaction, carried the first service's scope: a service whose scope is `null` let a scoped
|
|
47
|
+
one read every tenant's rows, and a second service scoped by another column stored `NULL` in
|
|
48
|
+
it. Each service still runs its scope function once per call.
|
|
49
|
+
- An override of `emitEvent` or `prepareUpdate` keeps the guard read ahead of the write, like an
|
|
50
|
+
override of a `before*` hook. An `emitEvent` override saw the before event for another
|
|
51
|
+
tenant's id before the 404.
|
|
52
|
+
- `massUpdate` with a `beforeUpdate` hook that returns `NaN` for one id and `null` for another no
|
|
53
|
+
longer writes `NaN` to both, and a Drizzle `sql` expression as a value (`views + 1`) works as
|
|
54
|
+
it does in `update` instead of failing with a circular `TypeError` (a 500).
|
|
55
|
+
- The tenant key check sends its `SELECT`s, one per relation, together instead of one after the
|
|
56
|
+
other.
|
|
57
|
+
|
|
58
|
+
### Docs
|
|
59
|
+
|
|
60
|
+
- The multi-tenancy FAQ answered "No" to "Are eager-loaded relations filtered by tenant?", against
|
|
61
|
+
the section on the same page. It now says yes, when the related table has the scope columns.
|
|
62
|
+
The multi-tenancy and relations-on-writes pages say which nested keys are checked, and the bulk
|
|
63
|
+
operations page says when a mass write loads its relations.
|
|
64
|
+
- `AGENTS.md`, `CONTRIBUTING.md` and `apps/docs/AGENTS.md` point at the changelog page where it
|
|
65
|
+
lives now, `(support)/changelog.mdx`.
|
|
66
|
+
|
|
67
|
+
## [3.8.0] - 2026-11-03
|
|
68
|
+
|
|
69
|
+
Performance fixes from the 3.7.1 performance report. Calls return the same rows, in the same
|
|
70
|
+
order, with the same errors, in fewer statements and shorter transactions. The one visible
|
|
71
|
+
difference is under Changed: a row soft-deleted between the guard read and the write is now a 404
|
|
72
|
+
instead of a write. Inside `SqlBaseCrudService` the six mass writes share one skeleton, and so
|
|
73
|
+
do `softDelete`, `restore` and `delete`; no public API, override point or message changed.
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- Full-text search takes a column that already is a `tsvector`, such as a generated, stored
|
|
78
|
+
column, and uses it as is instead of wrapping it in `to_tsvector`, so a GIN index on the column
|
|
79
|
+
serves `fullTextSearch()` and `search.mode: "fullText"`. Before, such a column failed with
|
|
80
|
+
`function to_tsvector(unknown, tsvector) does not exist`. Next to text columns it is
|
|
81
|
+
`coalesce(col, ''::tsvector)`; text columns keep the 3.7.2 vector, bare for one column and
|
|
82
|
+
coalesced for several. On 200,000 rows a search on the stored column with its index took
|
|
83
|
+
0.6 ms, against 600 to 900 ms computing `to_tsvector` per row without one. The full-text page
|
|
84
|
+
shows the index for each shape.
|
|
85
|
+
- A docs page, [Database indexes for fast Drizzle queries](https://crud.avytrix.dev/docs/indexes/),
|
|
86
|
+
lists the index each query shape needs: `lower(col)` (or `citext`, or `caseSensitive: true`) for
|
|
87
|
+
the default case-insensitive equality, the sort columns plus the primary key for cursor pages,
|
|
88
|
+
GIN for full-text search, `pg_trgm` for `contains` and `search`, foreign keys and both join
|
|
89
|
+
table columns for relations, partial indexes on `deleted_at IS NULL`, and the tenant column
|
|
90
|
+
first. It says when to use `withCount: false` or the cursor on a large table. The filtering
|
|
91
|
+
page now spells out that `findOne` compares strings with a plain, case-sensitive `=` while
|
|
92
|
+
`findAll` and `count` use `lower()` by default (unchanged; a default change waits for a major),
|
|
93
|
+
and the hooks page says where after hooks run: after the commit for a single write, inside
|
|
94
|
+
the transaction for a mass write.
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
|
|
98
|
+
- A row soft-deleted between the guard read and the write is no longer written: `update` and
|
|
99
|
+
`softDelete` only touch a live row, `restore` only a row in the trash, and `delete` no longer
|
|
100
|
+
hard-deletes a row that was soft-deleted meanwhile (`forceDelete` still does). Such a call is a
|
|
101
|
+
404 instead of a write, a `false` return or a "Failed to update entity" error, and one whose
|
|
102
|
+
row vanished with an expected version is a 404 instead of a 409.
|
|
103
|
+
|
|
104
|
+
### Fixed
|
|
105
|
+
|
|
106
|
+
- Keyset pages seek instead of filtering. `findByCursor` adds a bound on the first sort column
|
|
107
|
+
(`col <= $1` descending, `col >= $1` ascending) ahead of the OR expansion, drops the `IS NULL`
|
|
108
|
+
branch for NOT NULL and primary key columns (nullable and relation columns keep it), and on
|
|
109
|
+
PostgreSQL compares `(a, id) > ($1, $2)` as one row value when every column is NOT NULL and
|
|
110
|
+
sorts the same way. With an index on the sort columns and the primary key, page 9,000 of
|
|
111
|
+
200,000 rows went from 24.4 ms to 0.6 ms (`created_at` descending) and from 50.3 ms to 0.5 ms
|
|
112
|
+
(`views` ascending) on PGlite. A sort column the row already carries is no longer selected a
|
|
113
|
+
second time for the cursor. Pages, their order and cursors are unchanged.
|
|
114
|
+
- Single-row writes skip the read first. On PostgreSQL and SQLite, `update`, `softDelete`,
|
|
115
|
+
`restore`, `delete` and `forceDelete` send one statement whose `WHERE` holds the whole guard
|
|
116
|
+
(primary key, tenant scope, expected version, soft delete state) and read the row only when
|
|
117
|
+
nothing came back, to answer with the same 404 or 409 as before. The read still runs first
|
|
118
|
+
when something a caller can observe sits between it and the write (an overridden `before*`
|
|
119
|
+
hook, `validateUpdate` or `mapUpdateDtoToEntity`, or a before event), so hooks and events see
|
|
120
|
+
exactly what they saw before, and on MySQL. A missing row is still a 404 ahead of a bad body.
|
|
121
|
+
- Mass writes send no savepoint on the happy path when they open their own transaction:
|
|
122
|
+
`massUpdate`, `massSoftDelete`, `massRestore` and `massDelete` take 4 round trips instead of 8
|
|
123
|
+
(`BEGIN`, `SELECT`, the set statement, `COMMIT`), `massCreate` and `massUpsert` 3 instead of 5.
|
|
124
|
+
When the set statement fails, the transaction rolls back and the items are replayed in a fresh
|
|
125
|
+
transaction that is rolled back too, so `BulkOperationException` names the same items with the
|
|
126
|
+
same statuses and messages. Inside a caller's transaction, or once a before event handed the
|
|
127
|
+
transaction to a subscriber, the savepoints stay, so a failed item does not abort the caller's
|
|
128
|
+
PostgreSQL transaction.
|
|
129
|
+
- `massUpdate` sends one `UPDATE` per distinct SET when `beforeUpdate` returns different data
|
|
130
|
+
per id, instead of one `UPDATE` and a savepoint pair per id: a hook with two results over 50
|
|
131
|
+
ids is 5 round trips instead of 155. Rows still come back in the input order, after hooks run
|
|
132
|
+
in that order, and a failure still names every failing id.
|
|
133
|
+
- On MySQL, `massCreate` of rows with auto-increment keys reads the inserted rows back with one
|
|
134
|
+
`SELECT ... WHERE id IN (...)` instead of one per row. The inserts stay one per row: MySQL only
|
|
135
|
+
reports the first id of a multi-row `INSERT`, and InnoDB does not promise the others are
|
|
136
|
+
consecutive.
|
|
137
|
+
- Relation loaders and relation counts split a parent key list that would not fit in one
|
|
138
|
+
statement. `loadBelongsTo`, `loadHasMany`, `loadManyToMany` and `loadCounts` send the keys in
|
|
139
|
+
chunks of 64,535 on PostgreSQL and MySQL, 31,766 on SQLite and 90 on Cloudflare D1, one query per
|
|
140
|
+
chunk, so a nested page whose level holds 70,000 keys loads in two queries instead of failing
|
|
141
|
+
with a 400 (3.7.2) or a 500 (before). Below that, it is one query per relation as before. The
|
|
142
|
+
chunk size is the new protected `relationChunkSize(db)`.
|
|
143
|
+
- Fewer statements in a few calls. `exists(id, options)` is one `SELECT` and no longer loads
|
|
144
|
+
`options.relations` or `options.counts` it throws away (4 statements to 1 with two relations and
|
|
145
|
+
a count); they are still checked against the allowlists. A nested create skips the "already
|
|
146
|
+
linked?" read for its many-to-many ids. `attach`, `sync` and `detach` read the row once and load
|
|
147
|
+
the relation onto it, instead of reading it before and after (8, 9 and 6 round trips to 7, 8 and
|
|
148
|
+
5). A mass write that opened its own transaction loads `options.relations` and `options.counts`
|
|
149
|
+
after it commits, so it holds its row locks for less time; with a count filter, or in a caller's
|
|
150
|
+
transaction, they still load inside. The returned rows are the same.
|
|
151
|
+
- A `scope` function runs once per call instead of once per statement and relation: 5 times for
|
|
152
|
+
a `findAll` with three relations and a count, 7 for a `massUpdate`, now 1 each.
|
|
153
|
+
|
|
14
154
|
## [3.7.2] - 2026-11-03
|
|
15
155
|
|
|
16
156
|
Fixes from the 3.7.1 audit. The batch limit now counts the statement the database receives.
|
|
@@ -335,7 +475,8 @@ app.
|
|
|
335
475
|
only operator on MySQL is a 400.
|
|
336
476
|
- **Keyset pagination.** `findByCursor(filters, { cursor, limit, sortBy, count }, options)` returns
|
|
337
477
|
`{ data, limit, nextCursor, total? }`. The cursor is base64url JSON of the last row's sort values
|
|
338
|
-
plus the primary key, so pages stay stable under inserts and
|
|
478
|
+
plus the primary key, so pages stay stable under inserts and, with an index on the sort columns
|
|
479
|
+
(since 3.8.0), deep pages cost the same as the
|
|
339
480
|
first. It works with every sort, filter, relation and `select`, keeps timestamp microseconds and
|
|
340
481
|
follows the database's NULL ordering. The count only runs with `count: true`. A cursor that is not
|
|
341
482
|
valid, or was made for another sort, is a 400.
|
package/README.md
CHANGED
|
@@ -563,6 +563,12 @@ instead of returning an arbitrary row.
|
|
|
563
563
|
> `like` / `ilike` operators and supply your own wildcards, or `startsWith` /
|
|
564
564
|
> `endsWith` / `contains`, which escape the value for you.
|
|
565
565
|
|
|
566
|
+
`findOne` is the exception: it compiles a string to a plain `=`, so it is
|
|
567
|
+
case-sensitive whatever `sql.caseSensitive` says. A plain index on the column
|
|
568
|
+
cannot serve `lower(column) = lower($1)`; create one on `lower(column)`. The
|
|
569
|
+
[indexes page](https://crud.avytrix.dev/docs/indexes/) lists the index each
|
|
570
|
+
filter, sort, search and relation needs.
|
|
571
|
+
|
|
566
572
|
---
|
|
567
573
|
|
|
568
574
|
## Query strings: parseQuery
|
|
@@ -654,7 +660,9 @@ in `forRoot` to apply a `created_at` fallback to every entity that doesn't defin
|
|
|
654
660
|
## Cursor pagination
|
|
655
661
|
|
|
656
662
|
`findByCursor` pages with `WHERE (sort columns) > (last row)` instead of
|
|
657
|
-
`OFFSET`,
|
|
663
|
+
`OFFSET`, starting with a bound on the first sort column, so with an index on
|
|
664
|
+
the sort columns plus the primary key (`(created_at DESC, id)` below) deep
|
|
665
|
+
pages stay as fast as the first, and rows inserted earlier do not shift the
|
|
658
666
|
next page:
|
|
659
667
|
|
|
660
668
|
```typescript
|
|
@@ -931,7 +939,9 @@ Each bulk method is one statement for the whole set (plus one `SELECT` that
|
|
|
931
939
|
checks the ids exist), inside a single transaction: `massUpdate` over 100 ids is
|
|
932
940
|
two queries. If any item fails, a `BulkOperationException` (400) is thrown and
|
|
933
941
|
the transaction rolls back. The failing items are found by replaying the batch
|
|
934
|
-
|
|
942
|
+
in a transaction (or, inside your own transaction, a savepoint) that is rolled
|
|
943
|
+
back, and the response body lists only them. The happy path sends no savepoint
|
|
944
|
+
unless it runs inside your transaction:
|
|
935
945
|
|
|
936
946
|
```json
|
|
937
947
|
{ "statusCode": 400, "error": "Bad Request",
|
|
@@ -26,6 +26,8 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
26
26
|
protected checkReadOptions(options?: SqlOperationOptions): void;
|
|
27
27
|
protected rowScope(options?: SqlOperationOptions): any[];
|
|
28
28
|
protected resolveScope(options?: SqlOperationOptions): CrudScopeValues | null;
|
|
29
|
+
private withScopeMemo;
|
|
30
|
+
private checkScope;
|
|
29
31
|
protected scopeConditions(options?: SqlOperationOptions, table?: any): any[];
|
|
30
32
|
protected relatedRowScope(target: any, options?: SqlOperationOptions): any[];
|
|
31
33
|
protected relatedScopeConditions(target: any, options?: SqlOperationOptions): any[];
|
|
@@ -61,6 +63,7 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
61
63
|
protected beforeRestore(_id: any): Promise<void>;
|
|
62
64
|
protected afterRestore(_entity: T): Promise<void>;
|
|
63
65
|
protected relationType(relation: RelationConfig): RelationType;
|
|
66
|
+
private relationNamed;
|
|
64
67
|
protected isCollection(relation: RelationConfig): boolean;
|
|
65
68
|
protected eagerRelations(relations: string[]): string[];
|
|
66
69
|
protected relationTree(paths: string[], configured?: RelationsConfig): RelationNode[];
|
|
@@ -78,7 +81,12 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
78
81
|
protected loadRelations(db: any, hosts: any[], nodes: RelationNode[], select?: SelectTree, hostTable?: any, options?: SqlOperationOptions): Promise<void>;
|
|
79
82
|
protected loadBelongsTo(db: any, hosts: any[], node: RelationNode, select: SelectTree, options?: SqlOperationOptions): Promise<void>;
|
|
80
83
|
protected loadHasMany(db: any, hosts: any[], node: RelationNode, select: SelectTree, hostTable?: any, options?: SqlOperationOptions): Promise<void>;
|
|
84
|
+
private loaderKeys;
|
|
85
|
+
private loadCollection;
|
|
86
|
+
private selectThrough;
|
|
81
87
|
protected loadManyToMany(db: any, hosts: any[], node: RelationNode, select: SelectTree, hostTable?: any, options?: SqlOperationOptions): Promise<void>;
|
|
88
|
+
protected relationChunkSize(db: any): number;
|
|
89
|
+
private inChunks;
|
|
82
90
|
private groupCollection;
|
|
83
91
|
protected applyJoins(query: any, relationNames: string[], options?: SqlOperationOptions): any;
|
|
84
92
|
protected normalizeRelations(rows: any[], eager: string[]): any[];
|
|
@@ -111,6 +119,7 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
111
119
|
vector: any;
|
|
112
120
|
query: any;
|
|
113
121
|
};
|
|
122
|
+
private isTsVector;
|
|
114
123
|
protected buildSearchCondition(search?: SqlOperationOptions["search"]): any;
|
|
115
124
|
protected caseInsensitiveLike(column: any, pattern: string): any;
|
|
116
125
|
protected likePattern(column: any, pattern: string): any;
|
|
@@ -118,14 +127,20 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
118
127
|
idKind(): "int" | "bigint" | "uuid" | "string";
|
|
119
128
|
parseId(raw: unknown): Promise<unknown>;
|
|
120
129
|
private findForGuard;
|
|
130
|
+
private guardRow;
|
|
131
|
+
private skipsGuard;
|
|
132
|
+
private guardOnError;
|
|
121
133
|
protected assertVersion(id: any, actual: unknown, expected: unknown): void;
|
|
122
134
|
protected writeWhere(ids: any[], options?: SqlOperationOptions, table?: any): any;
|
|
123
135
|
protected withVersionBump(set: Record<string, any>): Record<string, any>;
|
|
124
136
|
find(id: any, options?: SqlOperationOptions): Promise<T | null>;
|
|
125
137
|
findOne(where: Partial<T>, options?: SqlOperationOptions): Promise<T | null>;
|
|
126
138
|
private findFirst;
|
|
139
|
+
private readFirst;
|
|
140
|
+
private hydrate;
|
|
127
141
|
private planList;
|
|
128
142
|
private listConditions;
|
|
143
|
+
private parseCount;
|
|
129
144
|
private buildCountQuery;
|
|
130
145
|
findAll(filters: FilterDto | undefined, pagination: PaginationOptions & {
|
|
131
146
|
withCount: false;
|
|
@@ -138,6 +153,10 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
138
153
|
protected cursorField(column: any): any;
|
|
139
154
|
private compareCursor;
|
|
140
155
|
protected keysetCondition(specs: SortSpec[], values: unknown[]): any;
|
|
156
|
+
private keysetBound;
|
|
157
|
+
private keysetRowComparison;
|
|
158
|
+
private keysetNullable;
|
|
159
|
+
private nullsFirst;
|
|
141
160
|
private afterCursor;
|
|
142
161
|
protected emitEvent(phase: "before" | "after", operation: CrudWriteOperation, bulk: boolean, payload: {
|
|
143
162
|
ids?: unknown[];
|
|
@@ -147,6 +166,8 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
147
166
|
}, options?: SqlOperationOptions): Promise<void>;
|
|
148
167
|
private idsOf;
|
|
149
168
|
protected loadWriteRelations(db: any, rows: T[], options?: SqlOperationOptions): Promise<void>;
|
|
169
|
+
private loadsAfterCommit;
|
|
170
|
+
private writeThenLoad;
|
|
150
171
|
private expectedVersionOf;
|
|
151
172
|
protected prepareCreate(data: CreateDto, options?: SqlOperationOptions): Promise<Record<string, any>>;
|
|
152
173
|
private insertOne;
|
|
@@ -157,6 +178,7 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
157
178
|
private keysOutsideScope;
|
|
158
179
|
private assertBatchKeysInScope;
|
|
159
180
|
private assertKeysInScope;
|
|
181
|
+
private assertNestedKeysInScope;
|
|
160
182
|
private missingParentException;
|
|
161
183
|
private linkTargets;
|
|
162
184
|
attach(id: any, relation: string, relatedIds: unknown[], options?: SqlOperationOptions): Promise<T>;
|
|
@@ -171,6 +193,8 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
171
193
|
private updateWhereIn;
|
|
172
194
|
private fetchByIds;
|
|
173
195
|
private inIdOrder;
|
|
196
|
+
private guardedWrite;
|
|
197
|
+
private staleOrFalse;
|
|
174
198
|
softDelete(id: any, options?: SqlOperationOptions): Promise<boolean>;
|
|
175
199
|
private softDeleteSet;
|
|
176
200
|
restore(id: any, options?: SqlOperationOptions): Promise<T>;
|
|
@@ -178,14 +202,18 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
178
202
|
forceDelete(id: any, options?: SqlOperationOptions): Promise<boolean>;
|
|
179
203
|
upsert(data: CreateDto, upsert?: UpsertOptions, options?: SqlOperationOptions): Promise<T>;
|
|
180
204
|
massUpsert(data: CreateDto[], upsert?: UpsertOptions, options?: SqlOperationOptions): Promise<T[]>;
|
|
205
|
+
private upsertSet;
|
|
181
206
|
private conflictKeys;
|
|
182
207
|
private conflictValue;
|
|
183
208
|
private upsertRows;
|
|
184
209
|
private excludedValue;
|
|
210
|
+
private massWrite;
|
|
185
211
|
massCreate(data: CreateDto[], options?: SqlOperationOptions): Promise<T[]>;
|
|
186
212
|
private insertMany;
|
|
213
|
+
private readBackCreated;
|
|
187
214
|
private assertAllWritten;
|
|
188
215
|
massUpdate(ids: any[], data: UpdateDto, options?: SqlOperationOptions): Promise<T[]>;
|
|
216
|
+
private updateSets;
|
|
189
217
|
private runKeyed;
|
|
190
218
|
massSoftDelete(ids: any[], options?: SqlOperationOptions): Promise<boolean>;
|
|
191
219
|
massRestore(ids: any[], options?: SqlOperationOptions): Promise<T[]>;
|
|
@@ -201,12 +229,9 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
201
229
|
id?: any;
|
|
202
230
|
run: (sp: any) => Promise<unknown>;
|
|
203
231
|
}>, operation: "write" | "delete"): Promise<R>;
|
|
232
|
+
private settleSet;
|
|
204
233
|
private diagnose;
|
|
205
|
-
private
|
|
206
|
-
private massUpdatePerItem;
|
|
207
|
-
private massSoftDeletePerItem;
|
|
208
|
-
private massRestorePerItem;
|
|
209
|
-
private massDeletePerItem;
|
|
234
|
+
private eachInSavepoint;
|
|
210
235
|
protected savepoint<R>(tx: any, run: (sp: any) => Promise<R>): Promise<R>;
|
|
211
236
|
exists(id: any, options?: SqlOperationOptions): Promise<boolean>;
|
|
212
237
|
count(filters?: FilterDto, options?: SqlOperationOptions): Promise<number>;
|
|
@@ -215,6 +240,7 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
215
240
|
protected countNames(counts?: RelationCounts): string[];
|
|
216
241
|
protected withCountKeys(base: string[], counts?: RelationCounts): string[];
|
|
217
242
|
protected loadCounts(db: any, rows: any[], counts: RelationCounts | undefined, options?: SqlOperationOptions): Promise<void>;
|
|
243
|
+
private countPlan;
|
|
218
244
|
private countQuery;
|
|
219
245
|
protected pushColumnCondition(conditions: any[], column: any, value: any, caseInsensitive?: boolean): void;
|
|
220
246
|
protected buildCollectionFilter(relation: RelationConfig, value: Record<string, any>, db?: any, name?: string, options?: SqlOperationOptions): any | undefined;
|
|
@@ -241,6 +267,7 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
|
|
|
241
267
|
private patternCondition;
|
|
242
268
|
protected applyComplexFilter(conditions: any[], column: any, filterObj: any): void;
|
|
243
269
|
protected executeSqlTransaction<R>(operation: (tx: any) => Promise<R>, existingTransaction?: any): Promise<R>;
|
|
270
|
+
private readonly ownedTransactions;
|
|
244
271
|
protected assertTransactions(db: any): void;
|
|
245
272
|
protected getEntityName(): string;
|
|
246
273
|
fullTextSearch(searchTerm: string, searchColumns: string[], pagination?: PaginationOptions, options?: FullTextSearchOptions<FilterDto>): Promise<{
|