vsrepo 2.2.1 β†’ 2.4.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 CHANGED
@@ -13,16 +13,14 @@
13
13
 
14
14
  πŸ‡ΊπŸ‡Έ You're reading the English version. [πŸ‡§πŸ‡· Ler em portuguΓͺs](./README.pt-BR.md)
15
15
 
16
- > βœ… **Released.** VSRepository v2.0.0 (the ORM-agnostic core) and the [`@vsrepo/prisma7-adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter) are both published and ready to use. Prisma 7 is the first fully supported adapter; other ORMs (TypeORM, Drizzle, etc.) are still in progress β€” see [Adapter status](#adapter-status). If you need the previous Prisma-only release, use the [`v1`](https://github.com/jaobrabo123/VSRepository/tree/v1) code/docs instead.
17
-
18
- **ORM-agnostic** repository pattern library, with full **TypeScript** support and automatic **type inference**. VSRepository v2 is a rewrite of the [v1](https://github.com/jaobrabo123/VSRepository/tree/v1) library: instead of talking to Prisma directly, the core now delegates every operation to a pluggable **adapter**, so the same repository API can work against Prisma, TypeORM, or any other ORM/database that implements the adapter contract.
16
+ **ORM-agnostic** repository pattern library, with full **TypeScript** support and automatic **type inference**. VSRepository v2 is a rewrite of the [v1](https://github.com/jaobrabo123/VSRepository/tree/v1) library: instead of talking to Prisma directly, the core now delegates every operation to a pluggable **adapter**, so the same repository API can work against Prisma, Drizzle, or any other ORM/database that implements the adapter contract.
19
17
 
20
18
  VSRepository lets you create strongly-typed repositories with:
21
19
 
22
20
  - Automatic **base methods**: `get`, `getOrThrow`, `getList`, `save`, `saveList`, `remove`, `removeList`, `patch`, `merge`, `getAll`, `total`, `has`
23
21
  - **Native soft-delete**: `softRemove`, `softRemoveList`, `restore`, `restoreList`
24
22
  - **Dynamic methods** inferred from a `declare` field name via the `@DynamicMethod` decorator: `findByEmail`, `findManyByStatusPaginated`, `updateById`
25
- - **Raw SQL query methods** via the new `@QueryMethod` decorator, bypassing the name-parsing engine entirely
23
+ - **Raw SQL query methods** via the `@QueryMethod` decorator, bypassing the name-parsing engine entirely
26
24
  - Ad-hoc **`select`/`relations`** per call β€” no more pre-declared named projections
27
25
  - **Type safety** across 100% of operations
28
26
  - Native ORM **transactions**, shared across repositories
@@ -71,16 +69,16 @@ If you're coming from the [v1](https://github.com/jaobrabo123/VSRepository/tree/
71
69
 
72
70
  | Area | v1 | v2 |
73
71
  | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
74
- | Database access | Talks to **Prisma** directly, bundled in the core package | Talks to a **`VSRepoAdapter`**; ORM support ships as separate packages (`@vsrepo/prisma7-adapter`, `@vsrepo/typeorm-adapter`, ...) instead of being bundled in the core `vsrepo` package |
72
+ | Database access | Talks to **Prisma** directly, bundled in the core package | Talks to a **`VSRepoAdapter`**; ORM support ships as separate packages (`@vsrepo/prisma7-adapter`, `@vsrepo/drizzle-adapter`, ...) instead of being bundled in the core `vsrepo` package |
75
73
  | Defining a repository | Functional `setupVSRepo<T, M>()({...}).build(prisma)`, **or** a `DynamicRepository` class | A single **class-based** API: `extends VSRepository<Entity, PKType, OrmTypes>` |
76
74
  | Dynamic methods | `methods: { findByEmail: { map: true } }` config object | `@DynamicMethod()` decorator on a `declare` field |
77
75
  | Data projections | Named, reusable `selectModels` + `defaultSelectModel` | Ad-hoc `select`/`relations` passed per call (no named models) |
78
76
  | Eager loading | `include`/`includeModels` (Prisma-specific) | ORM-agnostic `relations` option |
79
- | Global filters | `requiredWhere` (any arbitrary filter, always applied) | **Removed**; Now it only accepts `softRemoveKey` + `see: "active" \| "removed" \| "all"` |
77
+ | Global filters | `requiredWhere` and `pushWhere` | **Removed**; Now it only accepts `softRemoveKey` + `see: "active" \| "removed" \| "all"` |
80
78
  | Case-insensitive filter suffix | `Insensitive` | `IgnoreCase` |
81
79
  | Inline ordering in method name | Not supported (`order` had to be passed as an argument via `Ordered`/`Paginated`) | `OrderBy<Field>Asc`/`OrderBy<Field>Desc` chains baked directly into the method name |
82
80
  | Duplicate handling on `createMany` | `SkipDuplicates` suffix | `IgnoreConflicts` suffix |
83
- | `aggregate` / `groupBy` | Supported (Prisma-native passthrough) | **Not implemented yet** |
81
+ | `aggregate` / `groupBy` | Supported (Prisma-native passthrough) | `groupBy` is **not planned** for v2. `aggregate` as a prefix is also unlikely: the most common operations are already covered by dedicated base methods (`sum`, `average`, `min`, `max`, `increment`, `decrement`, `multiply`, `divide`) β€” see [Atomic and aggregate methods](#atomic-and-aggregate-methods). For anything more complex, use `@QueryMethod`. |
84
82
  | Error types | `VSRepoError` + subclasses (`VSRepoConfigError`, `VSRepoBuildError`, `VSRepoExtendError`, `VSRepoRuntimeError`) | A base `VSRepoError` class with a `type: VSRepoErrorType` field (`DECORATOR`, `RESOLVER`, `DYNAMIC`, `VALIDATOR`, `BASE`, `ADAPTER`), plus a `VSRepoAdapterError` subclass carrying an `AdapterErrorCode` and the original ORM error |
85
83
  | Debug logging | `showWorking: true` boolean | `logLevel: VSLogLevel` (`DEBUG`/`INFO`/`WARN`/`ERROR`) + `logSlowThresholdMs` for slow-query warnings |
86
84
  | `vsrepo generate` CLI (type generation step) | Required before use | Not part of the v2 core β€” types come directly from your entity/ORM types |
@@ -97,16 +95,16 @@ VSRepository v2 is **ORM-agnostic by design**. The core package (`vsrepo`) only
97
95
  - `@vsrepo/typeorm-adapter`
98
96
  - `@vsrepo/drizzle-adapter`
99
97
 
100
- The Prisma 7 adapter has now been published to npm as `@vsrepo/prisma7-adapter` β€” it's currently the **only** published adapter. Adapters for the other ORMs listed above (Prisma 8, TypeORM, Drizzle) are **planned**; they just haven't been published yet. Until an official `@vsrepo/*-adapter` package exists for your ORM, you're welcome to write your own for your project, and if you'd like, publish it and open a PR to help grow the ecosystem β€” contributions here are very welcome.
98
+ The Prisma 7 adapter has now been published to npm as `@vsrepo/prisma7-adapter`. The Drizzle adapter is available as an **alpha** release β€” install it with `@vsrepo/drizzle-adapter@alpha`. Adapters for other ORMs are **planned** but not published yet. Until an official `@vsrepo/*-adapter` package exists for your ORM, you're welcome to write your own for your project, and if you'd like, publish it and open a PR to help grow the ecosystem β€” contributions here are very welcome.
101
99
 
102
- | Adapter | Status |
103
- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
104
- | Prisma 7 (`@vsrepo/prisma7-adapter`) | 🟒 **Released** β€” published to npm, implements the `VSRepoAdapter` contract (CRUD, relations, transactions, `merge`, logging) with tests; see [`VSRepoPrisma7Adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter) for source and docs. **Note:** the atomic/aggregate methods (`incrementOne`, `decrementOne`, `multiplyOne`, `divideOne`, `sum`, `average`, `min`, `max` β€” see [Atomic and aggregate methods](#atomic-and-aggregate-methods)) were added to the `VSRepoAdapter` contract after this adapter's last release; confirm its changelog/version implements them before relying on `increment`/`sum`/etc. against Prisma 7. |
105
- | TypeORM (`@vsrepo/typeorm-adapter`) | 🟑 **Planned, not published yet.** Only a reference `where`-clause parser (`parseVSRepoWhere`) was written to validate the design; it's the planned starting point for the future `@vsrepo/typeorm-adapter` package. Community contributions toward this are welcome. |
106
- | Other ORMs (Prisma 8, Drizzle, etc.) | 🟑 **Planned, not published yet.** No official package exists yet β€” write your own adapter for now (see [Writing your own adapter](#writing-your-own-adapter)), and consider publishing/contributing it back. |
107
- | Custom adapters | 🟒 Fully supported today β€” implement the [`VSRepoAdapter`](#writing-your-own-adapter) abstract class yourself for any ORM/database you need, in your own project or package, following the same shape as `@vsrepo/*-adapter` is expected to have. |
100
+ | Adapter | Status |
101
+ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
102
+ | Prisma 7 (`@vsrepo/prisma7-adapter`) | 🟒 **Released** β€” published to npm, implements the `VSRepoAdapter` contract (CRUD, relations, transactions, `merge`, logging) with tests; see [`VSRepoPrisma7Adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter) for source and docs. **Note:** the atomic/aggregate methods (`incrementOne`, `decrementOne`, `multiplyOne`, `divideOne`, `sum`, `average`, `min`, `max` β€” see [Atomic and aggregate methods](#atomic-and-aggregate-methods)) were added to the `VSRepoAdapter` contract after this adapter's last release; confirm its changelog/version implements them before relying on `increment`/`sum`/etc. against Prisma 7. |
103
+ | Drizzle (`@vsrepo/drizzle-adapter`) | πŸ”΅ **Alpha** β€” an early release is available on npm; install it with `npm i @vsrepo/drizzle-adapter@alpha`. The API may still change before the stable release. Check the [`DrizzleAdapter`](https://github.com/jaobrabo123/VSRepoDrizzleAdapter) repository for the current status and known limitations, and feel free to contribute. |
104
+ | Other ORMs (Prisma 8, TypeORM, etc.) | 🟑 **Planned, not published yet.** No official package exists yet β€” write your own adapter for now (see [Writing your own adapter](#writing-your-own-adapter)), and consider publishing/contributing it back. |
105
+ | Custom adapters | 🟒 Fully supported today β€” implement the [`VSRepoAdapter`](#writing-your-own-adapter) abstract class yourself for any ORM/database you need, in your own project or package, following the same shape as `@vsrepo/*-adapter` is expected to have. |
108
106
 
109
- In short: the repository class, the `@DynamicMethod`/`@QueryMethod` decorators, the name-parsing engine, error handling and logging are all working end-to-end, and Prisma 7 support is now a released, published adapter. Official adapters for the remaining ORMs are on the roadmap and will ship as separate `@vsrepo/*-adapter` packages rather than as part of the core `vsrepo` package β€” but you don't have to wait for that: writing (and optionally publishing) your own adapter in the meantime is a fully supported way to use v2 today and to contribute back to the project.
107
+ In short: the repository class, the `@DynamicMethod`/`@QueryMethod` decorators, the name-parsing engine, error handling and logging are all working end-to-end, and Prisma 7 support is now a released, published adapter. The Drizzle adapter is available in alpha. Official adapters for the remaining ORMs are on the roadmap and will ship as separate `@vsrepo/*-adapter` packages rather than as part of the core `vsrepo` package β€” but you don't have to wait for that: writing (and optionally publishing) your own adapter in the meantime is a fully supported way to use v2 today and to contribute back to the project.
110
108
 
111
109
  ---
112
110
 
@@ -172,6 +170,7 @@ export default new UserRepository();
172
170
  > The core API (`VSRepository`, `VSRepoAdapter`, `DynamicMethod`, `QueryMethod`, `VSRepoError`, enums and types) is imported from the single `vsrepo` entry point. The concrete adapter comes from a **separate** package (`@vsrepo/*-adapter`). On Prisma 7, install the published [`@vsrepo/prisma7-adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter) (its constructor takes a config object β€” `tableName`, `pkName`, optional `relations`/`logLevel` β€” as shown above). Official adapters for other ORMs are planned but not published yet; until they are, you can implement the `VSRepoAdapter` contract yourself (see [Writing your own adapter](#writing-your-own-adapter)) β€” and publishing it to help the project is very welcome.
173
171
 
174
172
  > **The third generic parameter (`OrmTypes`):** `VSRepository<Entity, PKType, OrmTypes>` accepts an optional third type parameter describing your ORM's client/transaction types, via `VSRepoOrmTypes` (`{ dbClient; dbTransaction }`). Supplying it gives you a correctly-typed `getDbClient()`, `transaction()` callback, and `db` option on every method, instead of `any`:
173
+ >
175
174
  > ```typescript
176
175
  > type PrismaOrmTypes = { dbClient: PrismaClient; dbTransaction: Prisma.TransactionClient };
177
176
  >
@@ -179,6 +178,7 @@ export default new UserRepository();
179
178
  > // getDbClient() now returns PrismaClient, and transaction(fn) types `tx` as Prisma.TransactionClient
180
179
  > }
181
180
  > ```
181
+ >
182
182
  > If omitted, it defaults to `VSRepoOrmTypes` (`dbClient`/`dbTransaction` both `any`).
183
183
 
184
184
  ### Using the repository
@@ -206,14 +206,14 @@ await userRepository.remove(user.id);
206
206
 
207
207
  `VSRepoOptions<T, K>`, passed to `super(...)` inside your repository's constructor:
208
208
 
209
- | Option | Type | Description |
210
- | -------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------- |
211
- | `adapter` | `VSRepoAdapter<T>` | **Required.** The adapter instance that translates repository calls into calls against the underlying ORM/database. |
212
- | `pkName` | `keyof T` | **Required.** Name of the field that represents the entity's primary key. |
213
- | `softRemoveKey` | `keyof T` | Optional. When set, enables `softRemove`, `softRemoveList`, `restore` and `restoreList`. |
214
- | `defaultOrdering` | `Ordering<T>` | Optional. Default ordering applied automatically to queries that accept `order`, unless overridden per call. |
215
- | `logLevel` | `VSLogLevel` | Optional. Minimum severity printed by the internal logger. Defaults to `VSLogLevel.WARN`. |
216
- | `logSlowThresholdMs` | `number` | Optional. Duration (ms) above which a finished operation is logged as `WARN` instead of `DEBUG`. Defaults to 300ms. |
209
+ | Option | Type | Description |
210
+ | -------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
211
+ | `adapter` | `VSRepoAdapter<T>` | **Required.** The adapter instance that translates repository calls into calls against the underlying ORM/database. |
212
+ | `pkName` | `keyof T` | **Required.** Name of the field that represents the entity's primary key. |
213
+ | `softRemoveKey` | `keyof T` | Optional. When set, enables `softRemove`, `softRemoveList`, `restore` and `restoreList`. |
214
+ | `defaultOrdering` | `Ordering<T>` | Optional. Default ordering applied automatically to queries that accept `order`, unless overridden per call. |
215
+ | `logLevel` | `VSLogLevel` | Optional. Minimum severity printed by the internal logger. Defaults to `VSLogLevel.WARN`. |
216
+ | `logSlowThresholdMs` | `number \| boolean` | Optional. Duration (ms) above which a finished operation is logged as `WARN`. Defaults to 300ms. Pass `false` to disable slow-operation warnings entirely; pass `true` to use the 300ms default explicitly. |
217
217
 
218
218
  ---
219
219
 
@@ -221,31 +221,31 @@ await userRepository.remove(user.id);
221
221
 
222
222
  Available automatically on every `VSRepository` subclass:
223
223
 
224
- | Method | Description |
225
- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
226
- | `get(pk, options?)` | Fetches a record by primary key. |
227
- | `getOrThrow(pk, options?)` | Fetches a record by primary key, throwing if not found. |
228
- | `getList(pks, options?)` | Fetches multiple records by a list of primary keys. |
229
- | `getAll(options?)` | Fetches all records; accepts `pagination` and `order` in `options`. |
230
- | `save(obj, options?)` | Creates or updates (upsert) a single record. |
231
- | `saveList(objs, options?)` | Creates or updates (upsert) multiple records in one call. |
232
- | `patch(pk, obj, options?)` | Partially updates a record by primary key. |
233
- | `merge(pk, obj, options?)` | Fetches a record and returns it deep-merged, in memory, with the given object β€” does **not** persist anything. |
234
- | `remove(pk, options?)` | Deletes a record by primary key. |
235
- | `removeList(pks, options?)` | Deletes multiple records by primary key, returning `{ count }`. |
236
- | `total(options?)` | Returns the total number of records. |
237
- | `has(pk, options?)` | Checks whether a record exists, returning `boolean`. |
238
- | `increment(pk, field, value, options?)` | Atomically adds `value` to a numeric field. See [Atomic and aggregate methods](#atomic-and-aggregate-methods). |
239
- | `decrement(pk, field, value, options?)` | Atomically subtracts `value` from a numeric field. |
240
- | `multiply(pk, field, value, options?)` | Atomically multiplies a numeric field by `value`. |
241
- | `divide(pk, field, value, options?)` | Atomically divides a numeric field by `value`. |
242
- | `sum(field, where?, options?)` | Sums a numeric field across every matching record; `null` if none match. |
243
- | `average(field, where?, options?)` | Arithmetic mean of a numeric field across every matching record; `null` if none match. |
244
- | `min(field, where?, options?)` | Minimum value of a numeric field across every matching record; `null` if none match. |
245
- | `max(field, where?, options?)` | Maximum value of a numeric field across every matching record; `null` if none match. |
246
- | `transaction(fn, options?)` | Runs `fn` inside a native transaction of the underlying ORM. |
247
- | `getDbClient()` | Returns the underlying ORM client instance used outside of transactions. |
248
- | `query<T>(query, options?)` | Executes a raw SQL statement directly against the database. See [Ad-hoc raw queries with `query()`](#ad-hoc-raw-queries-with-query). |
224
+ | Method | Description |
225
+ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
226
+ | `get(pk, options?)` | Fetches a record by primary key. |
227
+ | `getOrThrow(pk, options?)` | Fetches a record by primary key, throwing if not found. |
228
+ | `getList(pks, options?)` | Fetches multiple records by a list of primary keys. |
229
+ | `getAll(options?)` | Fetches all records; accepts `pagination` and `order` in `options`. |
230
+ | `save(obj, options?)` | Creates or updates (upsert) a single record. |
231
+ | `saveList(objs, options?)` | Creates or updates (upsert) multiple records in one call. |
232
+ | `patch(pk, obj, options?)` | Partially updates a record by primary key. |
233
+ | `merge(pk, obj, options?)` | Fetches a record and returns it deep-merged, in memory, with the given object β€” does **not** persist anything. |
234
+ | `remove(pk, options?)` | Deletes a record by primary key. |
235
+ | `removeList(pks, options?)` | Deletes multiple records by primary key, returning `{ count }`. |
236
+ | `total(options?)` | Returns the total number of records. |
237
+ | `has(pk, options?)` | Checks whether a record exists, returning `boolean`. |
238
+ | `increment(pk, field, value, options?)` | Atomically adds `value` to a numeric field. See [Atomic and aggregate methods](#atomic-and-aggregate-methods). |
239
+ | `decrement(pk, field, value, options?)` | Atomically subtracts `value` from a numeric field. |
240
+ | `multiply(pk, field, value, options?)` | Atomically multiplies a numeric field by `value`. |
241
+ | `divide(pk, field, value, options?)` | Atomically divides a numeric field by `value`. |
242
+ | `sum(field, where?, options?)` | Sums a numeric field across every matching record; `null` if none match. |
243
+ | `average(field, where?, options?)` | Arithmetic mean of a numeric field across every matching record; `null` if none match. |
244
+ | `min(field, where?, options?)` | Minimum value of a numeric field across every matching record; `null` if none match. |
245
+ | `max(field, where?, options?)` | Maximum value of a numeric field across every matching record; `null` if none match. |
246
+ | `transaction(fn, options?)` | Runs `fn` inside a native transaction of the underlying ORM. |
247
+ | `getDbClient()` | Returns the ORM client instance. |
248
+ | `query<T>(query, options?)` | Executes a raw SQL statement directly against the database. See [Ad-hoc raw queries with `query()`](#ad-hoc-raw-queries-with-query). |
249
249
 
250
250
  Most of the above accept a `MethodOptions<Entity, OrmTypes>` object as their last argument (`select`, `relations`, `see`, `db`). A few β€” `total`, `has`, `removeList`, `sum`, `average`, `min`, `max`, and the soft-delete batch methods (`softRemoveList`/`restoreList`) β€” don't return/shape an `Entity`, so they accept the narrower `RestrictMethodOptions<Entity, OrmTypes>` instead (`see`, `db` only; no `select`/`relations`). `transaction`, `query`, and `getDbClient` accept their own options or none at all.
251
251
 
@@ -253,7 +253,7 @@ Most of the above accept a `MethodOptions<Entity, OrmTypes>` object as their las
253
253
 
254
254
  ## Soft-delete
255
255
 
256
- Soft-delete is now a **first-class, built-in concept**. Configure `softRemoveKey` once on the repository:
256
+ Soft-delete is a **first-class, built-in concept**. Configure `softRemoveKey` once on the repository:
257
257
 
258
258
  ```typescript
259
259
  super({
@@ -286,7 +286,7 @@ await userRepository.getAll({ see: "all" }); // everything, ignoring soft-delete
286
286
 
287
287
  Every `VSRepository` subclass gets 8 extra methods for working with numeric fields, split into two groups:
288
288
 
289
- **Atomic updates** β€” evaluated server-side against the row's *current* value (`UPDATE ... SET field = field + value`), not a client-side read-modify-write:
289
+ **Atomic updates** β€” evaluated server-side against the row's _current_ value (`UPDATE ... SET field = field + value`), not a client-side read-modify-write:
290
290
 
291
291
  ```typescript
292
292
  await userRepository.increment("user-1", "balance", 50); // balance = balance + 50
@@ -334,7 +334,7 @@ Note that several ORMs (Drizzle, MikroORM, TypeORM) represent `decimal`/`numeric
334
334
 
335
335
  ### Writing an adapter
336
336
 
337
- `VSRepoAdapter` mirrors the same 8 operations (`incrementOne`, `decrementOne`, `multiplyOne`, `divideOne`, `sum`, `average`, `min`, `max` β€” see [Writing your own adapter](#writing-your-own-adapter)). Each adapter translates them into whatever its ORM/database considers "native": Prisma has a built-in `{ field: { increment: value } }` update shape and an `aggregate()` call; other ORMs typically need a `QueryBuilder`/raw-`sql` expression (e.g. `SET field = field * :value`, `SELECT SUM(field) ...`) instead. The atomic methods must return the record reflecting the state *after* the write β€” if the ORM's atomic-update API only returns an affected-row count, issue a follow-up read rather than returning a stale in-memory copy.
337
+ `VSRepoAdapter` mirrors the same 8 operations (`incrementOne`, `decrementOne`, `multiplyOne`, `divideOne`, `sum`, `average`, `min`, `max` β€” see [Writing your own adapter](#writing-your-own-adapter)). Each adapter translates them into whatever its ORM/database considers "native": Prisma has a built-in `{ field: { increment: value } }` update shape and an `aggregate()` call; other ORMs typically need a `QueryBuilder`/raw-`sql` expression (e.g. `SET field = field * :value`, `SELECT SUM(field) ...`) instead. The atomic methods must return the record reflecting the state _after_ the write β€” if the ORM's atomic-update API only returns an affected-row count, issue a follow-up read rather than returning a stale in-memory copy.
338
338
 
339
339
  ---
340
340
 
@@ -360,14 +360,6 @@ const userWithAddress = await userRepository.get(id, {
360
360
  >
361
361
  > The core only forwards `MethodOptions.select` and `MethodOptions.relations` to the adapter β€” each adapter decides how to translate them to the underlying ORM:
362
362
  >
363
- > - **TypeORM (`@vsrepo/typeorm-adapter`)** β€” `relations` is **required** to load any relation, even when you only want a nested projection via `select`. TypeORM will not JOIN/emit the relation unless it is listed in `relations`:
364
- > ```typescript
365
- > // TypeORM: select alone is NOT enough
366
- > await userRepository.get(id, {
367
- > select: { id: true, address: { city: true } },
368
- > relations: { address: true }, // ← required in TypeORM
369
- > });
370
- > ```
371
363
  > - **Prisma 7 (`@vsrepo/prisma7-adapter` / `VSRepoPrisma7Adapter`)** β€” `relations` is converted to Prisma `include` (`parsePrismaInclude`). **If `select` is present, `relations` is ignored** because Prisma does not allow `select` + `include` in the same query:
372
364
  > ```typescript
373
365
  > // Prisma7: relations is ignored when select exists
@@ -388,7 +380,7 @@ Dynamic methods are declared as a `declare` field annotated with `@DynamicMethod
388
380
  ```typescript
389
381
  class UserRepository extends VSRepository<User, string> {
390
382
  @DynamicMethod()
391
- declare findByEmail: (email: string) => Promise<User[]>;
383
+ declare findByEmail: (email: string, options?: MethodOptions<User>) => Promise<User[]>;
392
384
 
393
385
  @DynamicMethod()
394
386
  declare findOneByEmail: (email: string) => Promise<User | null>;
@@ -404,12 +396,11 @@ class UserRepository extends VSRepository<User, string> {
404
396
  options?: MethodOptions<User>,
405
397
  ) => Promise<User[]>;
406
398
 
407
- // OrderedAndPaginated: field filters, then order, then pagination, then MethodOptions
399
+ // field filters, then pagination, then MethodOptions
408
400
  @DynamicMethod()
409
401
  declare findByNameIgnoreCaseOrAgeBetweenOrderByCreatedAtAscPaginated: (
410
402
  name: string,
411
403
  age: [number, number],
412
- order: Ordering<User>,
413
404
  pagination: Pagination,
414
405
  options?: MethodOptions<User>,
415
406
  ) => Promise<User[]>;
@@ -418,40 +409,40 @@ class UserRepository extends VSRepository<User, string> {
418
409
 
419
410
  ### Available prefixes
420
411
 
421
- | Prefix | Adapter method | Notes |
422
- | -------------------------- | --------------------- | ---------------------------------------------------------------------------------------- |
423
- | `findBy` | `findMany` | Field filters follow the prefix. |
424
- | `findOneBy` | `findOne` | Field filters follow the prefix; single result. |
425
- | `findOneOrThrowBy` | `findOneOrThrow` | Throws if no record is found. |
426
- | `findOneOrThrow` | `findOneOrThrow` | No field filters; applies only soft-delete/`see`. |
427
- | `findOneOrThrowWhere` | `findOneOrThrow` | Receives a `VSRepoWhere<T>` as the first argument. |
428
- | `findWhere` | `findMany` | Receives a `VSRepoWhere<T>` as the first argument. |
429
- | `findOneWhere` | `findOne` | Receives a `VSRepoWhere<T>` as the first argument. |
430
- | `findOne` | `findOne` | No field filters; applies only soft-delete/`see`. |
431
- | `countBy` | `count` | Field filters follow the prefix. |
432
- | `countWhere` | `count` | Receives a `VSRepoWhere<T>` as the first argument. |
433
- | `count` | `count` | No field filters. |
434
- | `existsBy` | `exists` | Returns `boolean`. |
435
- | `existsWhere` | `exists` | Receives a `VSRepoWhere<T>` as the first argument. |
436
- | `create` | `create` | Receives `data` as argument. |
437
- | `createMany` | `createMany` | Receives `data[]` as argument; supports `IgnoreConflicts`. |
438
- | `createManyReturning` | `createManyReturning` | Receives `data[]` as argument; supports `IgnoreConflicts`; returns the created records (`T[]`) instead of `CountResult`. |
439
- | `updateBy` | `update` | Field filters + `data` as argument. |
440
- | `updateWhere` | `update` | Receives a `VSRepoWhere<T>` as the first argument, then `data`. |
441
- | `updateManyBy` | `updateMany` | Field filters + `data`. |
442
- | `updateManyWhere` | `updateMany` | Receives a `VSRepoWhere<T>` as the first argument, then `data`. |
443
- | `updateManyReturningBy` | `updateManyReturning` | Field filters + `data`; returns updated records. |
444
- | `updateManyReturningWhere` | `updateManyReturning` | Receives a `VSRepoWhere<T>` as the first argument, then `data`; returns updated records. |
445
- | `upsertBy` | `upsert` | Field filters + `create`/`update` payloads. |
446
- | `upsertWhere` | `upsert` | Receives a `VSRepoWhere<T>` as the first argument, then `create`/`update` payloads. |
447
- | `deleteBy` | `delete` | Field filters follow the prefix. |
448
- | `deleteWhere` | `delete` | Receives a `VSRepoWhere<T>` as the first argument. |
449
- | `deleteManyBy` | `deleteMany` | Field filters follow the prefix. |
450
- | `deleteManyWhere` | `deleteMany` | Receives a `VSRepoWhere<T>` as the first argument. |
451
- | `deleteManyReturningBy` | `deleteManyReturning` | Field filters follow the prefix; returns deleted records. |
452
- | `deleteManyReturningWhere` | `deleteManyReturning` | Receives a `VSRepoWhere<T>` as the first argument; returns deleted records. |
453
-
454
- > `aggregate` and `groupBy` are **not implemented yet** in v2 (they existed in v1). This is planned but not currently available.
412
+ | Prefix | Adapter method | Notes |
413
+ | -------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
414
+ | `findBy` | `findMany` | Field filters follow the prefix. |
415
+ | `findOneBy` | `findOne` | Field filters follow the prefix; single result. |
416
+ | `findOneOrThrowBy` | `findOneOrThrow` | Throws if no record is found. |
417
+ | `findOneOrThrow` | `findOneOrThrow` | No field filters; applies only soft-delete/`see`. |
418
+ | `findOneOrThrowWhere` | `findOneOrThrow` | Receives a `VSRepoWhere<T>` as the first argument. |
419
+ | `findWhere` | `findMany` | Receives a `VSRepoWhere<T>` as the first argument. |
420
+ | `findOneWhere` | `findOne` | Receives a `VSRepoWhere<T>` as the first argument. |
421
+ | `findOne` | `findOne` | No field filters; applies only soft-delete/`see`. |
422
+ | `countBy` | `count` | Field filters follow the prefix. |
423
+ | `countWhere` | `count` | Receives a `VSRepoWhere<T>` as the first argument. |
424
+ | `count` | `count` | No field filters. |
425
+ | `existsBy` | `exists` | Returns `boolean`. |
426
+ | `existsWhere` | `exists` | Receives a `VSRepoWhere<T>` as the first argument. |
427
+ | `create` | `create` | Receives `DeepPartial<Entity>` as argument. |
428
+ | `createMany` | `createMany` | Receives `DeepPartial<Entity>[]` as argument; supports `IgnoreConflicts`. |
429
+ | `createManyReturning` | `createManyReturning` | Receives `DeepPartial<Entity>[]` as argument; supports `IgnoreConflicts`; returns the created records (`T[]`) instead of `CountResult`. |
430
+ | `updateBy` | `update` | Field filters + `DeepPartial<Entity>` as argument. |
431
+ | `updateWhere` | `update` | Receives a `VSRepoWhere<T>` as the first argument, then `DeepPartial<Entity>`. |
432
+ | `updateManyBy` | `updateMany` | Field filters + `DeepPartial<Entity>`. |
433
+ | `updateManyWhere` | `updateMany` | Receives a `VSRepoWhere<T>` as the first argument, then `DeepPartial<Entity>`. |
434
+ | `updateManyReturningBy` | `updateManyReturning` | Field filters + `DeepPartial<Entity>`; returns updated records. |
435
+ | `updateManyReturningWhere` | `updateManyReturning` | Receives a `VSRepoWhere<T>` as the first argument, then `DeepPartial<Entity>`; returns updated records. |
436
+ | `upsertBy` | `upsert` | Field filters + `create`/`update` payloads. |
437
+ | `upsertWhere` | `upsert` | Receives a `VSRepoWhere<T>` as the first argument, then `create`/`update` payloads. |
438
+ | `deleteBy` | `delete` | Field filters follow the prefix. |
439
+ | `deleteWhere` | `delete` | Receives a `VSRepoWhere<T>` as the first argument. |
440
+ | `deleteManyBy` | `deleteMany` | Field filters follow the prefix. |
441
+ | `deleteManyWhere` | `deleteMany` | Receives a `VSRepoWhere<T>` as the first argument. |
442
+ | `deleteManyReturningBy` | `deleteManyReturning` | Field filters follow the prefix; returns deleted records. |
443
+ | `deleteManyReturningWhere` | `deleteManyReturning` | Receives a `VSRepoWhere<T>` as the first argument; returns deleted records. |
444
+
445
+ > `groupBy` is **not planned** for v2 β€” it doesn't map cleanly onto the ORM-agnostic contract. `aggregate` as a separate prefix is also unlikely to be implemented: the most common aggregate operations (`sum`, `average`, `min`, `max`, `increment`, `decrement`, `multiply`, `divide`) are already available as dedicated base methods β€” see [Atomic and aggregate methods](#atomic-and-aggregate-methods). For anything more complex, use a `@QueryMethod` with raw SQL.
455
446
 
456
447
  ### Field filters
457
448
 
@@ -582,6 +573,11 @@ declare findOne: (options?: MethodOptions<User>) => Promise<User | null>;
582
573
  | `injectOrdering` | `Ordering<T>` | Fixed ordering automatically injected, overriding the repository's `defaultOrdering`. |
583
574
 
584
575
  ```typescript
576
+ // proxyTo: gives the method a custom name while reusing an existing pattern
577
+ @DynamicMethod<User>({ proxyTo: "findByEmail" })
578
+ declare buscarPorEmail: (email: string, options?: MethodOptions<User>) => Promise<User[]>;
579
+
580
+ // injectOrdering: always sorts by createdAt desc, overriding defaultOrdering
585
581
  @DynamicMethod<User>({ injectOrdering: { createdAt: "desc" } })
586
582
  declare findByStatus: (status: string) => Promise<User[]>;
587
583
  ```
@@ -590,7 +586,7 @@ declare findByStatus: (status: string) => Promise<User[]>;
590
586
 
591
587
  ## Query methods (raw SQL)
592
588
 
593
- `@QueryMethod` bypasses the name-parsing engine entirely and executes a raw SQL statement through the adapter's `query()` method. Use `$1`, `$2`, ... placeholders β€” never interpolate values directly into the SQL string.
589
+ `@QueryMethod` bypasses the name-parsing engine entirely and executes a raw SQL statement through the adapter's `query()` method. Use placeholders for the values passed via `args` β€” never interpolate values directly into the SQL string. **The placeholder syntax depends on the database/driver behind your adapter:** the `$1`, `$2`, ... style used in the examples below is the PostgreSQL convention β€” MySQL, for instance, uses `?`. Check your adapter's documentation for the exact syntax.
594
590
 
595
591
  ```typescript
596
592
  class UserRepository extends VSRepository<User, string> {
@@ -607,9 +603,9 @@ class UserRepository extends VSRepository<User, string> {
607
603
  }
608
604
  ```
609
605
 
610
- | Option | Type | Default | Description |
611
- | -------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
612
- | `modifying` | `boolean` | `false` | When `true`, runs as `INSERT`/`UPDATE`/`DELETE` and the method resolves to the number of affected rows. When `false`, runs as a read query and resolves to the declared return type. |
606
+ | Option | Type | Default | Description |
607
+ | -------------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
608
+ | `modifying` | `boolean` | `false` | When `true`, the method resolves to the number of affected rows. When `false`, runs as a read query and resolves to the declared return type. |
613
609
  | `singleResult` | `boolean` | `false` | When `true`, collapses an array result into its first element (`null` if empty), so you can declare the return type as a single object instead of an array. Has no effect on non-array results (e.g. a `modifying` query's affected-row count). |
614
610
 
615
611
  Query methods accept `{ args, db? }` at the call site β€” `db` lets them participate in a `transaction()` block just like base and dynamic methods.
@@ -626,6 +622,10 @@ class UserRepository extends VSRepository<User, string> {
626
622
  declare findByEmailAndType: (
627
623
  ...args: QueryArgs<[email: string, userType: string]>
628
624
  ) => Promise<User[]>;
625
+
626
+ // Instead of using `QueryArgs`, you can also simply set `DbArg` as the last parameter
627
+ @QueryMethod('SELECT * FROM "user" WHERE id = $1', { spreadArgs: true })
628
+ declare findById: (id: string, db?: DbArg) => Promise<User[]>;
629
629
  }
630
630
 
631
631
  const admins = await userRepository.findByEmailAndType("joao@email.com", "admin");
@@ -634,7 +634,7 @@ const admins = await userRepository.findByEmailAndType("joao@email.com", "admin"
634
634
  To run the query against a specific client or transaction instead of the repository's default one, pass `withDb(tx)` as the trailing argument β€” it wraps `tx` in a `DbArg`, which the resolver recognizes with `instanceof`, so it's never confused with a regular positional argument even if that argument happens to be an object:
635
635
 
636
636
  ```typescript
637
- await userRepository.transaction(async (tx) => {
637
+ await userRepository.transaction(async tx => {
638
638
  await userRepository.findByEmailAndType("joao@email.com", "admin", withDb(tx));
639
639
  });
640
640
  ```
@@ -661,18 +661,18 @@ const affectedRows = await userRepository.query<number>(
661
661
 
662
662
  // Only one row is ever expected here, so `singleResult` collapses the
663
663
  // array into a single object (or `null` when no row matches).
664
- const user = await userRepository.query<User | null>(
665
- 'SELECT * FROM "user" WHERE id = $1 LIMIT 1',
666
- { args: ["123"], singleResult: true },
667
- );
664
+ const user = await userRepository.query<User | null>('SELECT * FROM "user" WHERE id = $1 LIMIT 1', {
665
+ args: ["123"],
666
+ singleResult: true,
667
+ });
668
668
  ```
669
669
 
670
- | Option | Type | Default | Description |
671
- | -------------- | --------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
672
- | `args` | `any[]` | `undefined` | Positional parameters injected into `$1`, `$2`, ... placeholders. Never interpolate values directly into the SQL string. |
673
- | `db` | `any` | Repository's default client | Database client or transaction to run this query in. |
674
- | `modifying` | `boolean` | `false` | When `true`, treats the statement as `INSERT`/`UPDATE`/`DELETE`. |
675
- | `singleResult` | `boolean` | `false` | When `true`, collapses an array result into its first element (`null` if empty). Has no effect on non-array results (e.g. a `modifying` query's affected-row count). |
670
+ | Option | Type | Default | Description |
671
+ | -------------- | --------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
672
+ | `args` | `any[]` | `undefined` | Positional parameters injected into the SQL placeholders β€” the placeholder syntax depends on the database/driver behind your adapter. Never interpolate values directly into the SQL string. |
673
+ | `db` | `any` | Repository's default client | Database client or transaction to run this query in. |
674
+ | `modifying` | `boolean` | `false` | When `true`, returns the number of affected rows. |
675
+ | `singleResult` | `boolean` | `false` | When `true`, collapses an array result into its first element (`null` if empty). Has no effect on non-array results (e.g. a `modifying` query's affected-row count). |
676
676
 
677
677
  Just like base, dynamic and query methods, `query()` accepts `db` in `options` to participate in a `transaction()` block.
678
678
 
@@ -708,10 +708,10 @@ await userRepository.transaction(
708
708
  );
709
709
  ```
710
710
 
711
- | Option | Type | Description |
712
- | ----------------- | -------------------------- | -------------------------------------------------------------------------------- |
711
+ | Option | Type | Description |
712
+ | ---------------- | --------------------------- | ------------------------------------------------------------------------------------- |
713
713
  | `isolationLevel` | `TransactionIsolationLevel` | Isolation level to use for the transaction. Defaults to the underlying ORM's default. |
714
- | `timeoutMs` | `number` | Maximum time (in ms) the transaction is allowed to run before being aborted. |
714
+ | `timeoutMs` | `number` | Maximum time (in ms) the transaction is allowed to run before being aborted. |
715
715
 
716
716
  `TransactionIsolationLevel` mirrors the standard SQL isolation levels: `READ_UNCOMMITTED`, `READ_COMMITTED`, `REPEATABLE_READ`, `SERIALIZABLE`. Support for a given level depends on the adapter/underlying ORM and database.
717
717
 
@@ -746,26 +746,26 @@ import type {
746
746
  } from "vsrepo";
747
747
  ```
748
748
 
749
- | Type | Description | Used by |
750
- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
751
- | `MethodOptions<T, K>` | Options accepted as the last argument of most base and dynamic methods: `select`, `relations`, `see`, `db`. | [Base methods](#base-methods), [Dynamic methods](#dynamic-methods). |
752
- | `RestrictMethodOptions<T, K>` | Narrowed `MethodOptions<T, K>` exposing only `see`/`db` β€” used by methods that don't shape/return an `Entity` (`total`, `has`, `sum`, `average`, `min`, `max`, `removeList`, `softRemoveList`, `restoreList`). | [Base methods](#base-methods), [Atomic and aggregate methods](#atomic-and-aggregate-methods). |
753
- | `Pagination` | `{ limit?, offset? }` accepted by `getAll` and by `Paginated` dynamic methods. | [Base methods](#base-methods), [Ordering, pagination and distinct](#ordering-pagination-and-distinct). |
754
- | `Ordering<T>` / `OrderByField<T>` / `SortDirection` | Ordering shape accepted by `getAll`, `defaultOrdering` and `injectOrdering`, and by `Ordered` dynamic methods. A single object or a chained array; nested objects order to-one relations. | [Constructor options](#constructor-options), [Decorator options](#decorator-options), [Ordering, pagination and distinct](#ordering-pagination-and-distinct). |
755
- | `SeeMode` | `"active" \| "removed" \| "all"` β€” controls visibility of soft-deleted records. | [Soft-delete](#soft-delete). |
756
- | `DeepPartial<T>` | Recursively makes every property of `T` optional, including nested objects and array elements. | `save`, `saveList`, `patch`, `merge`, and every write method on `VSRepoAdapter`. |
757
- | `CountResult` | `{ count: number }` β€” the shape returned by batch operations. | `removeList`, `softRemoveList`, `restoreList`, `createManyIgnoreConflicts`. |
758
- | `QueryMethodArg<T>` | `{ args?: T, db? }` β€” positional SQL parameters (`$1`, `$2`, ...) and transaction client for `@QueryMethod`. | [Query methods (raw SQL)](#query-methods-raw-sql). |
759
- | `QueryArgs<T, O>` | Types the spread parameter list of a `@QueryMethod` declared with `{ spreadArgs: true }`: `T`'s values in order, followed by an optional trailing `DbArg<O>` built via `withDb()`. | [Spread arguments with `spreadArgs`](#spread-arguments-with-spreadargs). |
760
- | `KeysOfType<T, K>` | Extracts the keys of `T` whose value type is assignable to `K`. | Constrains `pkName` in [Constructor options](#constructor-options) to fields of the entity matching the configured primary-key type. |
761
- | `NumericKeys<T>` | Extracts the keys of `T` whose (non-nullable) value type is assignable to `NumericLike`. Nullable numeric fields (`number \| null`) are included. | Constrains `field` in [Atomic and aggregate methods](#atomic-and-aggregate-methods) (`increment`, `sum`, etc). |
762
- | `NumericLike` | `number \| bigint \| DecimalLike`. | [Atomic and aggregate methods](#atomic-and-aggregate-methods). |
763
- | `DecimalLike` | Structural shape of an arbitrary-precision decimal value (`{ toNumber(): number; decimalPlaces(): number }`), matching e.g. Prisma's `Prisma.Decimal` without importing it directly. | [Which fields are eligible](#which-fields-are-eligible). |
764
- | `Primitive` | Union of scalar types (`string \| number \| boolean \| bigint \| symbol \| undefined \| null \| Date`) treated as leaves β€” not relations β€” when walking an entity's shape. | Used by `Ordering<T>` to tell scalar fields apart from relation fields. |
765
- | `VSRepoWhere<T>` | ORM-agnostic filter type accepted by `*Where` dynamic methods (e.g. `findWhere`, `findOneWhere`, `updateWhere`). Supports field filters, logical operators (`AND`/`OR`/`NOT`), and relation filters. | [`findWhere`, `findOneWhere` and other `*Where` prefixes](#available-prefixes). |
749
+ | Type | Description | Used by |
750
+ | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
751
+ | `MethodOptions<T, K>` | Options accepted as the last argument by all dynamic methods and most base methods: `select`, `relations`, `see`, `db`. | [Base methods](#base-methods), [Dynamic methods](#dynamic-methods). |
752
+ | `RestrictMethodOptions<T, K>` | Narrowed `MethodOptions<T, K>` exposing only `see`/`db` β€” used by methods that don't shape/return an `Entity` (`total`, `has`, `sum`, `average`, `min`, `max`, `removeList`, `softRemoveList`, `restoreList`). | [Base methods](#base-methods), [Atomic and aggregate methods](#atomic-and-aggregate-methods). |
753
+ | `Pagination` | `{ limit?, offset? }` accepted by `getAll` and by `Paginated` dynamic methods. | [Base methods](#base-methods), [Ordering, pagination and distinct](#ordering-pagination-and-distinct). |
754
+ | `Ordering<T>` / `OrderByField<T>` / `SortDirection` | Ordering shape accepted by `getAll`, `defaultOrdering` and `injectOrdering`, and by `Ordered` dynamic methods. A single object or a chained array; nested objects order to-one relations. | [Constructor options](#constructor-options), [Decorator options](#decorator-options), [Ordering, pagination and distinct](#ordering-pagination-and-distinct). |
755
+ | `SeeMode` | `"active" \| "removed" \| "all"` β€” controls visibility of soft-deleted records. | [Soft-delete](#soft-delete). |
756
+ | `DeepPartial<T>` | Recursively makes every property of `T` optional, including nested objects and array elements. | `save`, `saveList`, `patch`, `merge`, and all the dynamic writing methods. |
757
+ | `CountResult` | `{ count: number }` β€” the shape returned by batch operations. | `removeList`, `softRemoveList`, `restoreList`, `createManyIgnoreConflicts`. |
758
+ | `QueryMethodArg<T>` | `{ args?: T, db? }` β€” positional SQL parameters (the placeholder syntax depends on the database/driver behind your adapter: `$1`, `$2`, ... for PostgreSQL, `?` for MySQL) and transaction client for `@QueryMethod`. | [Query methods (raw SQL)](#query-methods-raw-sql). |
759
+ | `QueryArgs<T, O>` | Types the spread parameter list of a `@QueryMethod` declared with `{ spreadArgs: true }`: `T`'s values in order, followed by an optional trailing `DbArg<O>` built via `withDb()`. | [Spread arguments with `spreadArgs`](#spread-arguments-with-spreadargs). |
760
+ | `KeysOfType<T, K>` | Extracts the keys of `T` whose value type is assignable to `K`. | Constrains `pkName` in [Constructor options](#constructor-options) to fields of the entity matching the configured primary-key type. |
761
+ | `NumericKeys<T>` | Extracts the keys of `T` whose (non-nullable) value type is assignable to `NumericLike`. Nullable numeric fields (`number \| null`) are included. | Constrains `field` in [Atomic and aggregate methods](#atomic-and-aggregate-methods) (`increment`, `sum`, etc). |
762
+ | `NumericLike` | `number \| bigint \| DecimalLike`. | [Atomic and aggregate methods](#atomic-and-aggregate-methods). |
763
+ | `DecimalLike` | Structural shape of an arbitrary-precision decimal value (`{ toNumber(): number; decimalPlaces(): number }`), matching e.g. Prisma's `Prisma.Decimal` without importing it directly. | [Which fields are eligible](#which-fields-are-eligible). |
764
+ | `Primitive` | Union of scalar types (`string \| number \| boolean \| bigint \| symbol \| undefined \| null \| Date \| DecimalLike`) treated as leaves β€” not relations β€” when walking an entity's shape. | Used by `Ordering<T>` to tell scalar fields apart from relation fields. |
765
+ | `VSRepoWhere<T>` | ORM-agnostic filter type accepted by `*Where` dynamic methods (e.g. `findWhere`, `findOneWhere`, `updateWhere`). Supports field filters, logical operators (`AND`/`OR`/`NOT`), and relation filters. | [`findWhere`, `findOneWhere` and other `*Where` prefixes](#available-prefixes). |
766
766
  | `VSRepoOrmTypes` | `{ dbClient; dbTransaction }` β€” describes your ORM's client/transaction types. Passed as the third generic to `VSRepository<Entity, PKType, OrmTypes>` to type `getDbClient()`, `transaction()` and the `db` option instead of `any`. | [Creating a repository](#creating-a-repository). |
767
- | `VSRepoTransactionOptions` | `{ isolationLevel?, timeoutMs? }` β€” options accepted as the second argument of `transaction()`. | [Transactions](#transactions). |
768
- | `TransactionIsolationLevel` | Enum of standard SQL isolation levels (`READ_UNCOMMITTED`, `READ_COMMITTED`, `REPEATABLE_READ`, `SERIALIZABLE`) accepted by `VSRepoTransactionOptions.isolationLevel`. | [Transactions](#transactions). |
767
+ | `VSRepoTransactionOptions` | `{ isolationLevel?, timeoutMs? }` β€” options accepted as the second argument of `transaction()`. | [Transactions](#transactions). |
768
+ | `TransactionIsolationLevel` | Enum of standard SQL isolation levels (`READ_UNCOMMITTED`, `READ_COMMITTED`, `REPEATABLE_READ`, `SERIALIZABLE`) accepted by `VSRepoTransactionOptions.isolationLevel`. | [Transactions](#transactions). |
769
769
 
770
770
  ### `DeepPartial<T>`
771
771
 
@@ -944,13 +944,13 @@ export class MyOrmAdapter<T> extends VSRepoAdapter<T> {
944
944
  }
945
945
  ```
946
946
 
947
- | Method | Description |
948
- | ----------------------------------- | -------------------------------------------------------------------------------------------------- |
949
- | `new VSLogger(logLevel, name, slowThresholdMs?)` | Creates a logger; `name` prefixes every line, `slowThresholdMs` defaults to 300. |
950
- | `logDebug/logInfo/logWarn(text, obj?)` | Logs at the given level if `logLevel` allows it; `obj` is appended as pretty-printed JSON. |
951
- | `logError(text, err?)` | Logs at `ERROR`; if `err` is an `Error`, only `name`/`message`/`stack`/`cause` are logged. |
952
- | `startPerformLog(operation)` / `endPerformLog(data)` | Bracket a block to log its duration, escalating to `WARN` if it exceeds `slowThresholdMs`. |
953
- | `getLogLevel()` | Returns the logger's configured `VSLogLevel`. |
947
+ | Method | Description |
948
+ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
949
+ | `new VSLogger(logLevel, name, slowThresholdMs?)` | Creates a logger; `name` prefixes every line. `slowThresholdMs` controls the slow-operation threshold: a `number` sets it in ms (default 300), `false` disables slow-operation warnings entirely, `true` or omitted uses the 300ms default. |
950
+ | `logDebug/logInfo/logWarn(text, obj?)` | Logs at the given level if `logLevel` allows it; `obj` is appended as pretty-printed JSON. |
951
+ | `logError(text, err?)` | Logs at `ERROR`; if `err` is an `Error`, only `name`/`message`/`stack`/`cause` are logged. |
952
+ | `startPerformLog(operation)` / `endPerformLog(data)` | Bracket a block to log its duration, escalating to `WARN` if it exceeds `slowThresholdMs`. |
953
+ | `getLogLevel()` | Returns the logger's configured `VSLogLevel`. |
954
954
 
955
955
  This is purely a convenience for adapter authors β€” nothing in the core requires your adapter to use it.
956
956
 
@@ -976,7 +976,7 @@ try {
976
976
  | ----------------- | -------------------------------------------------------------------------------------------------------------------------- |
977
977
  | `DECORATOR` | Invalid arguments were passed to `@DynamicMethod` or `@QueryMethod`. |
978
978
  | `RESOLVER` | The library failed to resolve a dynamic/query method's configuration into a callable method (e.g. an unknown method name). |
979
- | `DYNAMIC` | A resolved dynamic/query method failed at runtime (e.g. missing arguments). |
979
+ | `DYNAMIC` | A resolved dynamic/query method failed at runtime (e.g. missing arguments). |
980
980
  | `VALIDATOR` | Invalid method options or arguments were detected during validation. |
981
981
  | `BASE` | Invalid usage of a base method (`get`, `save`, `remove`, etc). |
982
982
  | `ADAPTER` | A `VSRepoAdapter` failed while talking to the underlying ORM/database β€” always thrown as `VSRepoAdapterError`. |
@@ -1034,6 +1034,7 @@ console.log(AdapterErrorCode.UNIQUE_CONSTRAINT_VIOLATION); // "UNIQUE_CONSTRAINT
1034
1034
  | Code | Meaning |
1035
1035
  | ----------------------------- | -------------------------------------------------------------------------------------------------------------- |
1036
1036
  | `UNKNOWN` | Unclassified/unknown error; the fallback when no more specific code matches. |
1037
+ | `TRANSACTION_ROLLED_BACK` | Some adapters might use this code for forced transaction rollbacks (like Drizzle's `tx.rollback()`) |
1037
1038
  | `MISSING_DB_CLIENT` | Database client (or connection pool) not provided or could not be resolved. |
1038
1039
  | `CONNECTION_FAILED` | Could not reach/connect to the database, or an established connection was lost/terminated. |
1039
1040
  | `CONNECTION_POOL_EXHAUSTED` | Connection pool exhausted/depleted β€” no connection available, all busy or the limit was reached. |
@@ -1084,7 +1085,8 @@ super({
1084
1085
  pkName: "id",
1085
1086
  adapter,
1086
1087
  logLevel: VSLogLevel.DEBUG,
1087
- logSlowThresholdMs: 200,
1088
+ logSlowThresholdMs: 200, // warn if any operation takes > 200ms
1089
+ // logSlowThresholdMs: false, // disable slow-operation warnings entirely
1088
1090
  });
1089
1091
  ```
1090
1092
 
@@ -1146,11 +1148,11 @@ Notes:
1146
1148
 
1147
1149
  ## Contributing
1148
1150
 
1149
- Contributions are welcome, especially towards finishing the Prisma and TypeORM adapters! (**[GitHub repository](https://github.com/jaobrabo123/VSRepository)**):
1151
+ Contributions are welcome, especially for improving the Prisma adapter and finishing the Drizzle one! (**[GitHub repository](https://github.com/jaobrabo123/VSRepository)**):
1150
1152
 
1151
1153
  1. **Fork** the project.
1152
- 2. Create a branch off `v2` for your change: `git checkout -b v2-my-change`.
1154
+ 2. Create a branch for your change: `git checkout -b v2-my-change`.
1153
1155
  3. Push your branch: `git push origin v2-my-change`.
1154
- 4. Open a **Pull Request** against `v2`.
1156
+ 4. Open a **Pull Request**.
1155
1157
 
1156
1158
  To report issues or suggest features, open an **Issue**.