@avytrix/nestjs-drizzle-crud 3.6.0 → 3.7.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
@@ -60,11 +60,16 @@ pnpm add @avytrix/nestjs-drizzle-crud
60
60
  pnpm add @nestjs/common @nestjs/core drizzle-orm reflect-metadata # peers, always
61
61
  pnpm add postgres # PostgreSQL
62
62
  pnpm add mysql2 # MySQL
63
+ pnpm add @libsql/client # SQLite / libSQL
63
64
  ```
64
65
 
65
- `postgres` is an optional peer. You only need it when you let the module build
66
- the connection from a `connectionString` on PostgreSQL. If you pass your own
67
- `db`, no driver peer is required.
66
+ `postgres` and `mysql2` are optional peers. You only need them when you let the
67
+ module build the connection from a `connectionString` (PostgreSQL or MySQL;
68
+ `connection: { poolSize, idleTimeout, ssl }` tunes it). If you pass your own
69
+ `db`, no driver peer is required. SQLite (`dialect: 'sqlite'`, also libSQL and
70
+ Cloudflare D1) always takes your own `db`. On D1 and synchronous SQLite drivers
71
+ the mass writes, nested creates and attach / detach / sync throw a
72
+ `TransactionException`; use libSQL when you need them.
68
73
 
69
74
  Requires Node >= 20 and `emitDecoratorMetadata` / `experimentalDecorators` in
70
75
  `tsconfig.json`, which the Nest CLI already sets.
@@ -87,7 +92,8 @@ DrizzleCrudModule.forRootAsync({
87
92
  ```
88
93
 
89
94
  Use `forRoot` for a static config and `forRootAsync` when the value comes from
90
- `ConfigService` or the environment. Provide exactly one of `connectionString`
95
+ `ConfigService` or the environment (`useFactory`, or `useClass` / `useExisting`
96
+ with a class that implements `DrizzleCrudOptionsFactory`). Provide exactly one of `connectionString`
91
97
  or `db`. The module is global, so feature modules do not import it again for
92
98
  the connection.
93
99
 
@@ -138,7 +144,7 @@ Those are legacy manual patterns and they break registration.
138
144
  | OpenAPI | `@ApiCrudList(source)`, `@ApiCrudQuery(source)` (needs `@nestjs/swagger`) |
139
145
  | Bulk | `massCreate`, `massUpdate`, `massUpsert`, `massSoftDelete`, `massRestore`, `massDelete`, `massForceDelete` |
140
146
  | Query string | `parseQuery(query, options)`, or the standalone `parseCrudQuery(query, table, options)` |
141
- | Search | `fullTextSearch(term, columns, pagination)`, PostgreSQL only |
147
+ | Search | `fullTextSearch(term, columns, pagination, { filters, relations, select, language, queryMode })`, PostgreSQL only |
142
148
  | Transaction | `executeSqlTransaction(cb)` |
143
149
 
144
150
  `findAll` returns `{ data, total, page, limit }`, not a bare array; with
@@ -427,7 +433,8 @@ Driver error codes are translated at the service boundary, so bad client input
427
433
  never reaches the caller as a 500: a unique violation is a 409, a value that is
428
434
  too long, fails a check, has the wrong type or is not a valid date is a 400 (on
429
435
  reads too), a foreign key pointing at a row that does not exist is a 400, and a
430
- delete blocked by dependent rows is a 409.
436
+ delete blocked by dependent rows is a 409. The same holds on MySQL (strict mode
437
+ errors included) and SQLite (constraint errors).
431
438
 
432
439
  ---
433
440
 
@@ -452,9 +459,10 @@ To insert or update by a unique key, use
452
459
  than a find followed by a create.
453
460
 
454
461
  Take a row lock with `options.lock: 'update' | 'share'` only inside a
455
- transaction, and prefer `forNoKeyUpdate` when the key is not changing. The lock
456
- covers the service's own table only (`FOR UPDATE OF <table>`), so it works with
457
- an eager belongs-to.
462
+ transaction, and prefer `forNoKeyUpdate` when the key is not changing. On
463
+ PostgreSQL the lock covers the service's own table only (`FOR UPDATE OF
464
+ <table>`), so it works with an eager belongs-to. MySQL gets `FOR UPDATE` or
465
+ `FOR SHARE`; on SQLite a lock is a 400.
458
466
 
459
467
  ---
460
468
 
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to `@avytrix/nestjs-drizzle-crud` (published as `nestjs-drizzle-crud` up to 3.5.0) are documented here.
3
+ All notable changes to `@avytrix/nestjs-drizzle-crud` (published as `nestjs-drizzle-crud` up to 3.3.0) are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
@@ -8,6 +8,61 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  Scope note: the 3.0.x line is PostgreSQL-first. MySQL-specific paths (non-`RETURNING`
9
9
  restore, `ER_DUP_ENTRY` mapping, dialect locking) are tracked separately and unchanged here.
10
10
 
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
+ 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
+
14
+ ## [3.7.1] - 2026-11-02
15
+
16
+ ### Fixed
17
+
18
+ - The error for an upsert on a scoped entity on MySQL now reads "upsert on a scoped entity needs
19
+ PostgreSQL or SQLite". It said PostgreSQL only, though SQLite supports a scoped upsert since 3.7.0.
20
+ - `?fields=` paths named `__proto__`, `constructor` or `prototype` are treated as plain names.
21
+ Before, they reached the select tree's object internals (CodeQL js/remote-property-injection).
22
+
23
+ ## [3.7.0] - 2026-11-02
24
+
25
+ SQLite support, MySQL connections built for you, and full-text search that works like `findAll`.
26
+
27
+ ### Added
28
+
29
+ - `dialect: "sqlite"` for SQLite, libSQL (Turso) and Cloudflare D1. Pass the Drizzle instance as
30
+ `db`. Writes use `RETURNING`, `upsert` uses `ON CONFLICT DO UPDATE` with the tenant guard, and
31
+ keyset pages follow SQLite's NULL order. `LIKE` gets `ESCAPE '\'`, so `%`, `_` and `\` in a
32
+ search term match literally. Constraint errors map like the other dialects: `UNIQUE` 409,
33
+ `FOREIGN KEY` 400 on a write and 409 on a delete, `NOT NULL` and `CHECK` 400. Full-text search,
34
+ row locks and the array and JSON operators are a 400.
35
+ - On Cloudflare D1 and synchronous SQLite drivers (better-sqlite3, bun:sqlite, sql.js), the calls
36
+ that need an interactive transaction (mass writes, nested creates, `attach`, `detach`, `sync`)
37
+ throw a `TransactionException` that says why, before any statement runs. `TransactionException`
38
+ is no longer deprecated.
39
+ - `connectionString` works with `dialect: "mysql"`: the module builds a `mysql2` pool, wraps it
40
+ with `drizzle-orm/mysql2` and ends it on shutdown. `mysql2` is a new optional peer, loaded only
41
+ on that path.
42
+ - `connection: { poolSize, idleTimeout, ssl }` for the connection the module builds from
43
+ `connectionString`, on PostgreSQL (postgres-js) and MySQL (mysql2).
44
+ - `forRootAsync` takes `useClass` and `useExisting`, with a class that implements
45
+ `DrizzleCrudOptionsFactory` (`createDrizzleCrudOptions()`), next to `useFactory`.
46
+ - `fullTextSearch()` takes `options.filters`, and honours `options.relations`, `options.select`
47
+ and `options.counts`, the same as `findAll`. Before, they were ignored.
48
+ - `language` (the text search configuration, default `"english"`) and `queryMode` (`"plain"`, the
49
+ default, or `"websearch"` for `websearch_to_tsquery`) on `fullTextSearch()` options and on
50
+ `search` with `mode: "fullText"`. A language that is not a plain identifier is a 400.
51
+ - New types: `DrizzleConnectionOptions`, `DrizzleCrudAsyncOptions`, `DrizzleCrudOptionsFactory`,
52
+ `FullTextOptions`, `FullTextQueryMode` and `FullTextSearchOptions`.
53
+
54
+ ### Fixed
55
+
56
+ - MySQL: `lock` and `forNoKeyUpdate` add `FOR UPDATE` or `FOR SHARE` (`forNoKeyUpdate` becomes
57
+ `FOR UPDATE`). They used to be accepted and silently ignored.
58
+ - MySQL strict mode data errors (a missing required value, a value too long, out of range, of
59
+ the wrong type, a bad date, a failed check) are a 400 that names the column, instead of a 500.
60
+
61
+ ### Changed
62
+
63
+ - The default `sql.useReturning` is `true` for every dialect except MySQL, so SQLite gets it.
64
+ PostgreSQL and MySQL keep the values they had.
65
+
11
66
  ## [3.6.0] - 2026-11-02
12
67
 
13
68
  Fixes from the second audit. The first one is a security fix: upgrade if you use `scope`.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![license](https://img.shields.io/npm/l/@avytrix/nestjs-drizzle-crud)](./LICENSE)
6
6
  [![Sponsor @avytrix](https://img.shields.io/badge/Sponsor-%40avytrix-db61a2?style=for-the-badge&logo=githubsponsors&logoColor=white)](https://github.com/sponsors/avytrix)
7
7
 
8
- **nestjs-drizzle-crud is a NestJS + Drizzle ORM CRUD library: extend one base class and every table gets type-safe find, create, update, delete, filtering, pagination, relations and soft delete, on PostgreSQL or MySQL.**
8
+ **nestjs-drizzle-crud is a NestJS + Drizzle ORM CRUD library: extend one base class and every table gets type-safe find, create, update, delete, filtering, pagination, relations and soft delete, on PostgreSQL, MySQL or SQLite.**
9
9
 
10
10
  ```bash
11
11
  pnpm add @avytrix/nestjs-drizzle-crud drizzle-orm
@@ -94,7 +94,7 @@ sponsors are what make keeping it free sustainable.
94
94
  - 🚀 **CRUD**: `find`, `findOne`, `findAll`, `create`, `update`, `delete`, and more
95
95
  - 🧩 **Configure once**: `forRoot()` owns the connection; services only declare a table
96
96
  - ⚡ **Type-safe**: generics over your entity, create/update DTOs and filter types
97
- - 🗃️ **PostgreSQL & MySQL**: dialect-aware (`RETURNING` vs `insertId`)
97
+ - 🗃️ **PostgreSQL, MySQL & SQLite**: dialect-aware (`RETURNING` vs `insertId`), libSQL and Cloudflare D1 included
98
98
  - 🔄 **Soft delete**: opt-in, with `restore`, a trash view (`onlyDeleted`) and `forceDelete`
99
99
  - 📦 **Bulk operations**: one statement per mass create/update/delete, inside a transaction, plus `upsert`
100
100
  - 🔍 **Filtering**: comparison, `in`/`notIn`, `between`, escaped `contains`/`startsWith`, array and JSON operators, `or`/`not` groups
@@ -136,23 +136,23 @@ pnpm add @nestjs/common @nestjs/core drizzle-orm reflect-metadata
136
136
  # PostgreSQL (also required if you use `connectionString` with dialect 'postgresql')
137
137
  pnpm add postgres
138
138
 
139
- # MySQL
139
+ # MySQL (also required if you use `connectionString` with dialect 'mysql')
140
140
  pnpm add mysql2
141
141
 
142
+ # SQLite / libSQL (you build the Drizzle instance and pass it as `db`)
143
+ pnpm add @libsql/client
144
+
142
145
  # Only for ApiCrudQuery / ApiCrudList (OpenAPI docs)
143
146
  pnpm add @nestjs/swagger
144
147
  ```
145
148
 
146
149
  > Using npm or yarn? Swap `pnpm add` for `npm install` / `yarn add`.
147
150
 
148
- > **pnpm note:** `postgres` is an optional peer dependency and `mysql2` is not a peer
149
- > at all. pnpm enforces peer deps strictly, so if you build the connection from a
150
- > `connectionString` make sure `postgres` is installed in your app; for MySQL you
151
- > install `mysql2` yourself and pass the `db` you build with it.
152
-
153
- > `postgres` is an **optional** peer dependency. It's only needed when you let the
154
- > module build the connection from a `connectionString` for the `postgresql` dialect.
155
- > If you pass a pre-built `db` instead, you don't need it.
151
+ > **pnpm note:** `postgres` and `mysql2` are optional peer dependencies. pnpm
152
+ > enforces peer deps strictly, so if you build the connection from a
153
+ > `connectionString` make sure the driver for your dialect is installed in your app.
154
+ > If you pass a pre-built `db` instead, you don't need either. SQLite always takes a
155
+ > `db` you build.
156
156
 
157
157
  ---
158
158
 
@@ -285,15 +285,16 @@ export class UsersController {
285
285
 
286
286
  | Field | Type | Description |
287
287
  |---|---|---|
288
- | `dialect` | `'postgresql' \| 'mysql'` | **Required.** Database dialect. |
289
- | `connectionString` | `string` | Connection string. The module builds the connection (PostgreSQL only). |
288
+ | `dialect` | `'postgresql' \| 'mysql' \| 'sqlite'` | **Required.** Database dialect. `'sqlite'` covers SQLite, libSQL and Cloudflare D1. |
289
+ | `connectionString` | `string` | Connection string. The module builds the connection (PostgreSQL with `postgres`, MySQL with `mysql2`). |
290
+ | `connection` | `{ poolSize?, idleTimeout?, ssl? }` | Pool size, idle timeout (seconds) and TLS for the connection built from `connectionString`. |
290
291
  | `db` | `Drizzle instance` | Alternatively, pass a Drizzle instance you built yourself (any dialect). |
291
292
  | `schema` | `Record<string, unknown>` | Drizzle schema, used when building from `connectionString`. |
292
293
  | `defaults.softDelete` | `boolean` | Enable soft delete for all entities (default `true`). |
293
294
  | `defaults.timestamps` | `boolean` | Auto-manage `created_at`/`updated_at` for all entities (default `true`). |
294
295
  | `defaults.pagination` | `{ defaultLimit?, maxLimit? }` | Pagination defaults (default `{ 20, 100 }`). Either key alone keeps the other's default. |
295
296
  | `defaults.sortOrder` | `'asc' \| 'desc'` | Opt-in default sort: entities **without** an explicit `defaultSort` fall back to ordering by their `created_at` column in this direction. Omit it and `findAll` orders by the primary key only. |
296
- | `sql` | `{ caseSensitive?, useReturning? }` | Dialect tuning. `useReturning` defaults to `true` for PostgreSQL, `false` for MySQL. `jsonSupport` and `enableFullTextSearch` are deprecated and do nothing. |
297
+ | `sql` | `{ caseSensitive?, useReturning? }` | Dialect tuning. `useReturning` defaults to `true` for PostgreSQL and SQLite, `false` for MySQL. `jsonSupport` and `enableFullTextSearch` are deprecated and do nothing. |
297
298
  | `hooks` | `{ enableGlobalHooks?, subscribers? }` | Module-level write events on the `CrudEventBus`. See [Events](#events-and-global-hooks). |
298
299
 
299
300
  > **Provide exactly one of `connectionString` or `db`.** If your tables have no
@@ -306,7 +307,7 @@ export class UsersController {
306
307
  > table does not have stops the app at startup, and the message names the key
307
308
  > to use.
308
309
 
309
- **Build the connection yourself (any dialect, recommended for MySQL):**
310
+ **Build the connection yourself (any dialect, required for SQLite):**
310
311
 
311
312
  ```typescript
312
313
  import { drizzle } from 'drizzle-orm/postgres-js';
@@ -332,6 +333,27 @@ DrizzleCrudModule.forRootAsync({
332
333
  });
333
334
  ```
334
335
 
336
+ `forRootAsync` also takes `useClass` or `useExisting` with a class that
337
+ implements `DrizzleCrudOptionsFactory`:
338
+
339
+ ```typescript
340
+ @Injectable()
341
+ class CrudConfig implements DrizzleCrudOptionsFactory {
342
+ constructor(private readonly cfg: ConfigService) {}
343
+ createDrizzleCrudOptions(): DrizzleCrudConfig {
344
+ return { dialect: 'mysql', connectionString: this.cfg.get('DATABASE_URL') };
345
+ }
346
+ }
347
+
348
+ DrizzleCrudModule.forRootAsync({ useClass: CrudConfig });
349
+ ```
350
+
351
+ On SQLite (`dialect: 'sqlite'`), mass writes, nested creates and attach / detach
352
+ / sync need an async transaction: they work on libSQL and throw a
353
+ `TransactionException` on Cloudflare D1 and synchronous drivers such as
354
+ better-sqlite3. Full-text search, row locks and the array and JSON operators
355
+ are a 400 there.
356
+
335
357
  ### `DrizzleCrudModule.forFeature(entities)`
336
358
 
337
359
  Registers one or more services and binds each to its table. Per-entity overrides
@@ -1111,7 +1133,19 @@ const { data, total } = await service.fullTextSearch(
1111
1133
  ```
1112
1134
 
1113
1135
  Builds `to_tsvector(...) @@ plainto_tsquery(...)` across the given columns and
1114
- orders by `ts_rank`. Throws if the dialect is not `postgresql`.
1136
+ orders by `ts_rank`. Throws a 400 if the dialect is not `postgresql`. The options
1137
+ take `filters`, `relations`, `select` and `counts` as on `findAll`, plus
1138
+ `language` (default `'english'`) and `queryMode: 'websearch'` for
1139
+ `websearch_to_tsquery`:
1140
+
1141
+ ```typescript
1142
+ await service.fullTextSearch('"zero downtime" -kubernetes', ['title', 'body'], {}, {
1143
+ filters: { status: 'published' },
1144
+ relations: ['author'],
1145
+ language: 'english',
1146
+ queryMode: 'websearch',
1147
+ });
1148
+ ```
1115
1149
 
1116
1150
  For paginated list endpoints, prefer `findAll(..., ..., { search })` so search is
1117
1151
  combined with filters and the count query. Use `fullTextSearch` directly when
@@ -1211,7 +1245,7 @@ read-only and never touches your database. Full setup:
1211
1245
 
1212
1246
  Use these facts instead of guessing.
1213
1247
 
1214
- **Package:** `nestjs-drizzle-crud` · **Peers:** `@nestjs/common`, `@nestjs/core`, `drizzle-orm`, `reflect-metadata`; optional `postgres` (PG) / `@nestjs/swagger` (for `ApiCrudList`); `mysql2` is installed by you for MySQL, not a peer.
1248
+ **Package:** `nestjs-drizzle-crud` · **Peers:** `@nestjs/common`, `@nestjs/core`, `drizzle-orm`, `reflect-metadata`; optional `postgres` (PG `connectionString`) / `mysql2` (MySQL `connectionString`) / `@nestjs/swagger` (for `ApiCrudList`). SQLite drivers are yours, passed as `db`.
1215
1249
 
1216
1250
  **Setup is two steps and no per-service connection wiring:**
1217
1251
  1. `DrizzleCrudModule.forRoot({ dialect, connectionString | db, schema, defaults })` once in `AppModule`.
@@ -1242,15 +1276,17 @@ export class XService extends SqlBaseCrudService<X, CreateXDto, UpdateXDto, XFil
1242
1276
  - Relations: declare in forFeature `config.relations` and eager-load via `options.relations: ['relName']`, where a dotted path nests (`['states', 'states.cities']`, `['state.country']`; the parent path is pulled in automatically). Belongs-to is `{ table, localKey, references? }`, LEFT JOINed into the main query, resolving to an object or `null`. Has-many is `{ table, type: 'hasMany', foreignKey, parentKey?, orderBy? }`. Many-to-many is `{ table, type: 'manyToMany', through, throughLocalKey, throughForeignKey, references?, parentKey?, orderBy? }`. Nested relations live in a `relations` map on the relation. Every collection and every nested level costs exactly one extra query for the whole result set, never one per row. Filter via `findAll({ relName: { col: value } })` with the same operators; collection filters compile to `parentKey IN (SELECT ... WHERE ...)`, so `total` stays correct. Filters go through relation paths at any depth (`{ state: { country: { code: 'IN' } } }`), and collections take `some` / `none` / `every` (`{ posts: { none: { status: 'draft' } } }`), compiled to subqueries.
1243
1277
  - `options.select` narrows the projection and takes dotted relation paths at any depth: `['id', 'name', 'region.code', 'states.cities.name']`. Unknown names are ignored; an all-dotted list leaves the base row complete; the parent key, the child foreign key and any nested key are re-added automatically.
1244
1278
  - Primary keys: `primaryKey` (default `'id'`) per entity in forFeature config (`primaryKeyType` is deprecated and does nothing). UUID works on Postgres via RETURNING; with uuid keys, route params are strings, so don't use `ParseIntPipe`.
1245
- - Full-text search is PostgreSQL-only.
1279
+ - Full-text search is PostgreSQL-only; it takes `filters`, `relations`, `select`, `language` and `queryMode` in its options.
1280
+ - SQLite (`dialect: 'sqlite'`): pass `db`. Bulk writes need libSQL; D1 and sync drivers throw `TransactionException`. Locks, full-text and array/JSON operators are a 400.
1281
+ - Exports (3.7): `DrizzleConnectionOptions`, `DrizzleCrudAsyncOptions`, `DrizzleCrudOptionsFactory`, `FullTextOptions`, `FullTextQueryMode`, `FullTextSearchOptions`.
1246
1282
  - Exports (3.5): `createCrudController`, `CrudContext`, `CrudEventBus`, `ApiCrudQuery`, `ApiCrudList`, `MissingScopeException`, `StaleVersionException`, and the types `CrudControllerOptions`, `CrudEvent`, `CrudSubscriber`, `CrudScope`, `CollectionFilter`, `CountByRow`, `CrudPage`, `UncountedCrudPage`, `RelationCounts`.
1247
- - Exports: `SqlBaseCrudService`, `DrizzleCrudModule`, `DRIZZLE_DB`, `DRIZZLE_CRUD_CONFIG`, `TestCrudFactory`, exceptions (`EntityNotFoundException`, `DuplicateEntityException`, `ForeignKeyViolationException`, `EntityInUseException`, `ValidationFailedException`, `BulkOperationException`, plus the deprecated `DatabaseConnectionException` and `TransactionException`), and types (`SqlCrudConfig`, `SqlOperationOptions`, `DrizzleCrudConfig`, `CrudFeature`, `CrudFeatureConfig`, `PaginationOptions`, `OperationOptions`, `SqlSearchOptions`, `SqlSearchMode`, `BulkOperationResult`, `BulkOperationItemError`, `SqlDialect`, `PrimaryKeyType`, `CrudFilter`, `FilterOperators`, `SortInput`, `CursorPaginationOptions`, `CursorPage`, `CrudAllowlists`, `UpsertOptions`, `BulkMode`, `ParsedCrudQuery`), and `parseCrudQuery`.
1283
+ - Exports: `SqlBaseCrudService`, `DrizzleCrudModule`, `DRIZZLE_DB`, `DRIZZLE_CRUD_CONFIG`, `TestCrudFactory`, exceptions (`EntityNotFoundException`, `DuplicateEntityException`, `ForeignKeyViolationException`, `EntityInUseException`, `ValidationFailedException`, `BulkOperationException`, `TransactionException`, plus the deprecated `DatabaseConnectionException`), and types (`SqlCrudConfig`, `SqlOperationOptions`, `DrizzleCrudConfig`, `CrudFeature`, `CrudFeatureConfig`, `PaginationOptions`, `OperationOptions`, `SqlSearchOptions`, `SqlSearchMode`, `BulkOperationResult`, `BulkOperationItemError`, `SqlDialect`, `PrimaryKeyType`, `CrudFilter`, `FilterOperators`, `SortInput`, `CursorPaginationOptions`, `CursorPage`, `CrudAllowlists`, `UpsertOptions`, `BulkMode`, `ParsedCrudQuery`), and `parseCrudQuery`.
1248
1284
 
1249
1285
  ### `SqlCrudConfig`
1250
1286
 
1251
1287
  ```typescript
1252
1288
  interface SqlCrudConfig {
1253
- dialect: 'postgresql' | 'mysql';
1289
+ dialect: 'postgresql' | 'mysql' | 'sqlite';
1254
1290
  db: any; // Drizzle instance (injected by forFeature)
1255
1291
  table: any; // Drizzle table (set by forFeature)
1256
1292
  primaryKey: string; // default 'id'
@@ -3,7 +3,7 @@ import type { ICrudService, PaginationOptions } from "../interfaces/crud-service
3
3
  import type { CrudWriteOperation } from "../interfaces/events.interface";
4
4
  import type { CountByRow, CrudAllowlists, CrudPage, CursorPage, CursorPaginationOptions, ParseCrudQueryOptions, ParsedCrudQuery, RelationCounts, SortInput, UncountedCrudPage, UpsertOptions } from "../interfaces/query.interface";
5
5
  import type { CrudScopeValues } from "../interfaces/scope.interface";
6
- import type { RelationConfig, RelationNode, RelationsConfig, RelationType, SelectTree, SqlCrudConfig, SqlOperationOptions } from "../interfaces/sql-crud-config.interface";
6
+ import type { FullTextOptions, FullTextSearchOptions, RelationConfig, RelationNode, RelationsConfig, RelationType, SelectTree, SqlCrudConfig, SqlOperationOptions } from "../interfaces/sql-crud-config.interface";
7
7
  import type { SortOrder } from "../types/sql.types";
8
8
  interface SortSpec {
9
9
  key: string;
@@ -85,8 +85,12 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
85
85
  offset: number;
86
86
  };
87
87
  protected isUniqueViolation(error: any): boolean;
88
+ protected sqliteConstraint(error: any): string | undefined;
88
89
  protected toDuplicateException(error: any): DuplicateEntityException;
89
90
  private static readonly DATA_ERRORS;
91
+ private static readonly MYSQL_DATA_ERRORS;
92
+ private static readonly SQLITE_DATA_ERRORS;
93
+ private dataErrorMessage;
90
94
  protected isDataException(error: any): boolean;
91
95
  protected toDataException(error: any): ValidationFailedException;
92
96
  protected toClientError(error: any, operation?: "read" | "write" | "delete"): unknown;
@@ -94,8 +98,14 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
94
98
  protected isForeignKeyViolation(error: any): boolean;
95
99
  protected toForeignKeyException(error: any): ForeignKeyViolationException;
96
100
  protected applyLock(query: any, options?: SqlOperationOptions): any;
101
+ protected textSearchConfig(language?: unknown): any;
102
+ protected fullTextParts(columns: any[], term: string, options?: FullTextOptions): {
103
+ vector: any;
104
+ query: any;
105
+ };
97
106
  protected buildSearchCondition(search?: SqlOperationOptions["search"]): any;
98
107
  protected caseInsensitiveLike(column: any, pattern: string): any;
108
+ protected likePattern(column: any, pattern: string): any;
99
109
  parseQuery(query: Record<string, unknown> | null | undefined, options?: ParseCrudQueryOptions): ParsedCrudQuery;
100
110
  idKind(): "int" | "bigint" | "uuid" | "string";
101
111
  parseId(raw: unknown): Promise<unknown>;
@@ -219,8 +229,9 @@ export declare abstract class SqlBaseCrudService<T extends Record<string, any>,
219
229
  private patternCondition;
220
230
  protected applyComplexFilter(conditions: any[], column: any, filterObj: any): void;
221
231
  protected executeSqlTransaction<R>(operation: (tx: any) => Promise<R>, existingTransaction?: any): Promise<R>;
232
+ protected assertTransactions(db: any): void;
222
233
  protected getEntityName(): string;
223
- fullTextSearch(searchTerm: string, searchColumns: string[], pagination?: PaginationOptions, options?: SqlOperationOptions): Promise<{
234
+ fullTextSearch(searchTerm: string, searchColumns: string[], pagination?: PaginationOptions, options?: FullTextSearchOptions<FilterDto>): Promise<{
224
235
  data: T[];
225
236
  total: number;
226
237
  page: number;