vsrepo 2.4.0 → 2.5.0-beta
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/CHANGELOG.md +519 -0
- package/README.md +136 -51
- package/README.pt-BR.md +129 -44
- package/dist/VSRepoAdapter.d.ts +2 -0
- package/dist/VSRepoAdapter.js.map +1 -1
- package/dist/VSRepository.js +9 -1
- package/dist/VSRepository.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/validators/vsrepo.validator.js +1 -1
- package/dist/internal/validators/vsrepo.validator.js.map +1 -1
- package/dist/types/utils/infer-method-return.type.d.ts +108 -0
- package/dist/types/utils/infer-method-return.type.js +3 -0
- package/dist/types/utils/infer-method-return.type.js.map +1 -0
- package/dist/types/utils/infer-method-type.type.d.ts +66 -0
- package/dist/types/utils/infer-method-type.type.js +3 -0
- package/dist/types/utils/infer-method-type.type.js.map +1 -0
- package/dist/types/utils/ordering.type.d.ts +1 -5
- package/dist/types/vsrepo/vsrepo-options.type.d.ts +1 -1
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ VSRepository lets you create strongly-typed repositories with:
|
|
|
41
41
|
- [Which fields are eligible](#which-fields-are-eligible)
|
|
42
42
|
- [Writing an adapter](#writing-an-adapter)
|
|
43
43
|
- [`select` and `relations`](#select-and-relations)
|
|
44
|
+
- [Strict return typing with `InferMethodReturn` (BETA)](#strict-return-typing-with-infermethodreturn-beta)
|
|
44
45
|
- [Dynamic methods](#dynamic-methods)
|
|
45
46
|
- [Available prefixes](#available-prefixes)
|
|
46
47
|
- [Field filters](#field-filters)
|
|
@@ -48,6 +49,7 @@ VSRepository lets you create strongly-typed repositories with:
|
|
|
48
49
|
- [Relation filters](#relation-filters)
|
|
49
50
|
- [Ordering, pagination and distinct](#ordering-pagination-and-distinct)
|
|
50
51
|
- [Decorator options](#decorator-options)
|
|
52
|
+
- [Strict return typing with `InferMethodType`](#strict-return-typing-with-infermethodtype-beta)
|
|
51
53
|
- [Query methods (raw SQL)](#query-methods-raw-sql)
|
|
52
54
|
- [Spread arguments with `spreadArgs`](#spread-arguments-with-spreadargs)
|
|
53
55
|
- [Ad-hoc raw queries with `query()`](#ad-hoc-raw-queries-with-query)
|
|
@@ -67,22 +69,22 @@ VSRepository lets you create strongly-typed repositories with:
|
|
|
67
69
|
|
|
68
70
|
If you're coming from the [v1](https://github.com/jaobrabo123/VSRepository/tree/v1) code/docs, here's the short version. See each linked section for details.
|
|
69
71
|
|
|
70
|
-
| Area | v1 | v2
|
|
71
|
-
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
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
|
|
73
|
-
| Defining a repository | Functional `setupVSRepo<T, M>()({...}).build(prisma)`, **or** a `DynamicRepository` class | A single **class-based** API: `extends VSRepository<Entity, PKType, OrmTypes>`
|
|
74
|
-
| Dynamic methods | `methods: { findByEmail: { map: true } }` config object | `@DynamicMethod()` decorator on a `declare` field
|
|
75
|
-
| Data projections | Named, reusable `selectModels` + `defaultSelectModel` | Ad-hoc `select`/`relations` passed per call (no named models)
|
|
76
|
-
| Eager loading | `include`/`includeModels` (Prisma-specific) | ORM-agnostic `relations` option
|
|
77
|
-
| Global filters | `requiredWhere` and `pushWhere` | **Removed**; Now it only accepts `softRemoveKey` + `see: "active" \| "removed" \| "all"`
|
|
78
|
-
| Case-insensitive filter suffix | `Insensitive` | `IgnoreCase`
|
|
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
|
|
80
|
-
| Duplicate handling on `createMany` | `SkipDuplicates` suffix | `IgnoreConflicts` suffix
|
|
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`.
|
|
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
|
|
83
|
-
| Debug logging | `showWorking: true` boolean | `logLevel: VSLogLevel` (`DEBUG`/`INFO`/`WARN`/`ERROR`) + `logSlowThresholdMs` for slow-query warnings
|
|
84
|
-
| `vsrepo generate` CLI (type generation step) | Required before use | Not part of the v2 core — types come directly from your entity/ORM types
|
|
85
|
-
| CRUD extras | `patchList`, raw `options.select`/`options.include` | `select`/`relations` are the default (always "raw"); `patch`/`merge` keep the same semantics. **`patchList` was removed** — for a batch partial update, use a `updateManyBy`/`updateManyWhere` dynamic method instead
|
|
72
|
+
| Area | v1 | v2 |
|
|
73
|
+
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
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/drizzle-adapter`, ...) instead of being bundled in the core `vsrepo` package |
|
|
75
|
+
| Defining a repository | Functional `setupVSRepo<T, M>()({...}).build(prisma)`, **or** a `DynamicRepository` class | A single **class-based** API: `extends VSRepository<Entity, PKType, OrmTypes>` |
|
|
76
|
+
| Dynamic methods | `methods: { findByEmail: { map: true } }` config object | `@DynamicMethod()` decorator on a `declare` field |
|
|
77
|
+
| Data projections | Named, reusable `selectModels` + `defaultSelectModel` | Ad-hoc `select`/`relations` passed per call (no named models) |
|
|
78
|
+
| Eager loading | `include`/`includeModels` (Prisma-specific) | ORM-agnostic `relations` option |
|
|
79
|
+
| Global filters | `requiredWhere` and `pushWhere` | **Removed**; Now it only accepts `softRemoveKey` + `see: "active" \| "removed" \| "all"` |
|
|
80
|
+
| Case-insensitive filter suffix | `Insensitive` | `IgnoreCase` |
|
|
81
|
+
| 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
|
+
| Duplicate handling on `createMany` | `SkipDuplicates` suffix | `IgnoreConflicts` suffix |
|
|
83
|
+
| `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
|
+
| 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
|
+
| Debug logging | `showWorking: true` boolean | `logLevel: VSLogLevel` (`DEBUG`/`INFO`/`WARN`/`ERROR`) + `logSlowThresholdMs` for slow-query warnings |
|
|
86
|
+
| `vsrepo generate` CLI (type generation step) | Required before use | Not part of the v2 core — types come directly from your entity/ORM types |
|
|
87
|
+
| CRUD extras | `patchList`, raw `options.select`/`options.include` | `select`/`relations` are the default (always "raw"); `patch`/`merge` keep the same semantics. **`patchList` was removed** — for a batch partial update, use a `updateManyBy`/`updateManyWhere` dynamic method instead |
|
|
86
88
|
|
|
87
89
|
---
|
|
88
90
|
|
|
@@ -99,10 +101,10 @@ The Prisma 7 adapter has now been published to npm as `@vsrepo/prisma7-adapter`.
|
|
|
99
101
|
|
|
100
102
|
| Adapter | Status |
|
|
101
103
|
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
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.
|
|
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
|
+
| Prisma 7 (`@vsrepo/prisma7-adapter`) | 🟢 **Released** — published to npm, implements the `VSRepoAdapter` contract (CRUD, relations, transactions, `merge`, logging, etc.) with tests; see [`VSRepoPrisma7Adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter) for source and docs. |
|
|
105
|
+
| 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
106
|
| 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
|
|
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. |
|
|
106
108
|
|
|
107
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. 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.
|
|
108
110
|
|
|
@@ -116,8 +118,6 @@ v2 is installed as the core package plus one adapter package for your ORM, for e
|
|
|
116
118
|
npm i vsrepo @vsrepo/prisma7-adapter
|
|
117
119
|
```
|
|
118
120
|
|
|
119
|
-
> `vsrepo` v2.0.0 and `@vsrepo/prisma7-adapter` are both published to npm and ready to use. For any ORM other than Prisma 7, no adapter package exists yet — install the core and write your own adapter (see [Writing your own adapter](#writing-your-own-adapter)).
|
|
120
|
-
|
|
121
121
|
---
|
|
122
122
|
|
|
123
123
|
## Basic usage
|
|
@@ -167,14 +167,16 @@ class UserRepository extends VSRepository<User, string> {
|
|
|
167
167
|
export default new UserRepository();
|
|
168
168
|
```
|
|
169
169
|
|
|
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
|
|
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 [`@vsrepo/prisma7-adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter).
|
|
171
171
|
|
|
172
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
173
|
>
|
|
174
174
|
> ```typescript
|
|
175
|
-
>
|
|
175
|
+
> import { Prisma7OrmTypes } from "@vsrepo/prisma7-adapter";
|
|
176
|
+
>
|
|
177
|
+
> type MyOrmTypes = Prisma7OrmTypes<PrismaClient>;
|
|
176
178
|
>
|
|
177
|
-
> class UserRepository extends VSRepository<User, string,
|
|
179
|
+
> class UserRepository extends VSRepository<User, string, MyOrmTypes> {
|
|
178
180
|
> // getDbClient() now returns PrismaClient, and transaction(fn) types `tx` as Prisma.TransactionClient
|
|
179
181
|
> }
|
|
180
182
|
> ```
|
|
@@ -206,14 +208,14 @@ await userRepository.remove(user.id);
|
|
|
206
208
|
|
|
207
209
|
`VSRepoOptions<T, K>`, passed to `super(...)` inside your repository's constructor:
|
|
208
210
|
|
|
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` |
|
|
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.
|
|
211
|
+
| Option | Type | Description |
|
|
212
|
+
| -------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
213
|
+
| `adapter` | `VSRepoAdapter<T>` | **Required.** The adapter instance that translates repository calls into calls against the underlying ORM/database. |
|
|
214
|
+
| `pkName` | `keyof T` | Optional. Name of the field that represents the entity's primary key. When omitted, the repository falls back to the adapter's `getPkName()`. If the adapter does not implement it either, the constructor throws a `VSRepoError`. |
|
|
215
|
+
| `softRemoveKey` | `keyof T` | Optional. When set, enables `softRemove`, `softRemoveList`, `restore` and `restoreList`. |
|
|
216
|
+
| `defaultOrdering` | `Ordering<T>` | Optional. Default ordering applied automatically to queries that accept `order`, unless overridden per call. |
|
|
217
|
+
| `logLevel` | `VSLogLevel` | Optional. Minimum severity printed by the internal logger. Defaults to `VSLogLevel.WARN`. |
|
|
218
|
+
| `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
219
|
|
|
218
220
|
---
|
|
219
221
|
|
|
@@ -284,7 +286,7 @@ await userRepository.getAll({ see: "all" }); // everything, ignoring soft-delete
|
|
|
284
286
|
|
|
285
287
|
## Atomic and aggregate methods
|
|
286
288
|
|
|
287
|
-
Every `VSRepository` subclass gets 8
|
|
289
|
+
Every `VSRepository` subclass gets 8 methods for working with numeric fields, split into two groups:
|
|
288
290
|
|
|
289
291
|
**Atomic updates** — evaluated server-side against the row's _current_ value (`UPDATE ... SET field = field + value`), not a client-side read-modify-write:
|
|
290
292
|
|
|
@@ -371,11 +373,51 @@ const userWithAddress = await userRepository.get(id, {
|
|
|
371
373
|
>
|
|
372
374
|
> Custom adapters may map `relations` differently — consult the adapter's documentation for the exact semantics.
|
|
373
375
|
|
|
376
|
+
### Strict return typing with `InferMethodReturn` [BETA]
|
|
377
|
+
|
|
378
|
+
By default, methods are typed as returning the **whole entity**, ignoring the `select` and `relations` you pass (the same approach TypeORM takes). If you prefer a stricter type, `InferMethodReturn<T, Options>` narrows it to what was actually requested. It is opt-in and purely a type-level utility — nothing changes at runtime.
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import type { InferMethodReturn, MethodOptions } from "vsrepo";
|
|
382
|
+
|
|
383
|
+
type Address = { id: string; city: string };
|
|
384
|
+
type Product = { id: string; name: string };
|
|
385
|
+
type User = {
|
|
386
|
+
id: string;
|
|
387
|
+
name: string;
|
|
388
|
+
email: string;
|
|
389
|
+
address: Address | null;
|
|
390
|
+
products: Product[];
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const options = {
|
|
394
|
+
select: { id: true, name: true, products: { id: true } },
|
|
395
|
+
} satisfies MethodOptions<User>;
|
|
396
|
+
|
|
397
|
+
const users: InferMethodReturn<User[], typeof options> = await userRepository.getAll(options);
|
|
398
|
+
// { id: string; name: string; products: { id: string }[] }[]
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
- The **first** type argument is what the method returns: `User`, `User | null` or `User[]`. `null` and array-ness are preserved — also on relation fields (`address: Address | null`).
|
|
402
|
+
- The **second** is the options object passed to the method (`typeof options`). It can be omitted, which is the same as passing no options.
|
|
403
|
+
|
|
404
|
+
| Options passed | Inferred result |
|
|
405
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
406
|
+
| None (or `{}`) | Only the scalar fields of the entity — no relations. |
|
|
407
|
+
| `relations` | The scalar fields plus the requested relations (nested ones included); each relation brings all of its scalar fields. |
|
|
408
|
+
| `select` | Only the selected fields. A relation set to `true` brings all of its scalar fields; a nested `select` restricts it further. Relations selected this way are loaded even without `relations`. |
|
|
409
|
+
| `select` + `relations` | `select` wins and `relations` is ignored. |
|
|
410
|
+
|
|
411
|
+
- `see` and `db` don't affect the result.
|
|
412
|
+
- Keep the options' literal types, using `satisfies MethodOptions<T>` (as above) or passing them inline. If they are typed as a plain `MethodOptions<T>` (e.g. `const options: MethodOptions<User> = ...`), nothing is known at compile time and `T` is returned unchanged.
|
|
413
|
+
- Optional (`?`) fields and relations of the entity stay optional.
|
|
414
|
+
- To get this inference directly on dynamic methods, see [Strict return typing with `InferMethodType`](#strict-return-typing-with-infermethodtype-beta).
|
|
415
|
+
|
|
374
416
|
---
|
|
375
417
|
|
|
376
418
|
## Dynamic methods
|
|
377
419
|
|
|
378
|
-
Dynamic methods are declared as a `declare` field annotated with `@DynamicMethod()`. Their behavior — which adapter method to call, which filters to apply, and how arguments map to them — is inferred entirely from the field's **name
|
|
420
|
+
Dynamic methods are declared as a `declare` field annotated with `@DynamicMethod()`. Their behavior — which adapter method to call, which filters to apply, and how arguments map to them — is inferred entirely from the field's **name**.
|
|
379
421
|
|
|
380
422
|
```typescript
|
|
381
423
|
class UserRepository extends VSRepository<User, string> {
|
|
@@ -407,6 +449,8 @@ class UserRepository extends VSRepository<User, string> {
|
|
|
407
449
|
}
|
|
408
450
|
```
|
|
409
451
|
|
|
452
|
+
> Want the return type to follow the `select`/`relations` you pass, instead of always being the whole entity? Declare the method with [`InferMethodType`](#strict-return-typing-with-infermethodtype-beta).
|
|
453
|
+
|
|
410
454
|
### Available prefixes
|
|
411
455
|
|
|
412
456
|
| Prefix | Adapter method | Notes |
|
|
@@ -524,7 +568,7 @@ declare findByProductsSome: () => Promise<User[]>;
|
|
|
524
568
|
| `Ordered` | Injects an `order: Ordering<T>` argument as the **penultimate** parameter (before the optional `MethodOptions`). |
|
|
525
569
|
| `OrderedAndPaginated` | Injects `order` as the antepenultimate, then `pagination` as the penultimate — both before `MethodOptions`. |
|
|
526
570
|
| `PaginatedAndOrdered` | Injects `pagination` as the antepenultimate, then `order` as the penultimate — both before `MethodOptions`. |
|
|
527
|
-
| `OrderBy<Field>Asc` / `OrderBy<Field>Desc` |
|
|
571
|
+
| `OrderBy<Field>Asc` / `OrderBy<Field>Desc` | Bakes a fixed ordering directly into the method name — chain fields with `And` (e.g. `OrderByCreatedAtAscAndNameDesc`). No `order` argument needed. *Note: If you do not specify `Asc` or `Desc`, it defaults to `Asc`.* |
|
|
528
572
|
| `Distinct<Field>And<Field>...` | Bakes fixed `distinct` fields directly into the method name (only valid on `findBy`/`findWhere`-family methods). |
|
|
529
573
|
| `IgnoreConflicts` | On `createMany`/`createManyReturning`, skips records that would violate a unique constraint instead of throwing. _(Renamed from v1's `SkipDuplicates`.)_ |
|
|
530
574
|
|
|
@@ -582,6 +626,42 @@ declare buscarPorEmail: (email: string, options?: MethodOptions<User>) => Promis
|
|
|
582
626
|
declare findByStatus: (status: string) => Promise<User[]>;
|
|
583
627
|
```
|
|
584
628
|
|
|
629
|
+
### Strict return typing with `InferMethodType` [BETA]
|
|
630
|
+
|
|
631
|
+
Normally you write a dynamic method's signature by hand, and its return is whatever you declare (usually the whole entity). `InferMethodType<Args, Return, OrmTypes?>` declares the method for you and infers the return **on each call** from the `select`/`relations` you pass — with the same rules as [`InferMethodReturn`](#strict-return-typing-with-infermethodreturn-beta):
|
|
632
|
+
|
|
633
|
+
```typescript
|
|
634
|
+
class UserRepository extends VSRepository<User, string, MyOrmTypes> {
|
|
635
|
+
@DynamicMethod()
|
|
636
|
+
declare findByName: InferMethodType<[name: string], User[], MyOrmTypes>;
|
|
637
|
+
|
|
638
|
+
// the third generic (OrmTypes) is optional
|
|
639
|
+
@DynamicMethod()
|
|
640
|
+
declare findOneByEmail: InferMethodType<[email: string], User | null>;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
await userRepository.findByName("John");
|
|
644
|
+
// { id: string; name: string; email: string }[] (only the scalar fields)
|
|
645
|
+
|
|
646
|
+
await userRepository.findByName("John", { select: { id: true, products: { id: true } } });
|
|
647
|
+
// { id: string; products: { id: string }[] }[]
|
|
648
|
+
|
|
649
|
+
await userRepository.findOneByEmail("john@example.com", { relations: { address: true } });
|
|
650
|
+
// { id: string; name: string; email: string; address: Address | null } | null
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
| Generic | Description |
|
|
654
|
+
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
655
|
+
| `Args` | Tuple with the method's positional arguments, **without** `options` — e.g. `[name: string]` or `[where: VSRepoWhere<User>, pagination: Pagination]`. |
|
|
656
|
+
| `Return` | What the method resolves to: `Entity`, `Entity \| null` or `Entity[]`. The entity type used for `select`/`relations` is taken from here. |
|
|
657
|
+
| `OrmTypes` | _Optional._ `VSRepoOrmTypes` for your ORM, used to type the `db` option (see [Creating a repository](#creating-a-repository)). Defaults to `VSRepoOrmTypes`. |
|
|
658
|
+
|
|
659
|
+
- `options` (`MethodOptions<Entity, OrmTypes>`) is always the **last**, optional parameter, after every argument in `Args`. If one of those arguments is optional, pass `undefined` explicitly to reach `options`.
|
|
660
|
+
- Without `options` the result has only the scalar fields; with them it follows the [same rules](#strict-return-typing-with-infermethodreturn-beta) as `InferMethodReturn` (including `select` winning over `relations`).
|
|
661
|
+
- Unknown keys in `select`/`relations` (at any depth) are rejected at compile time, and the editor autocompletes them — just like with a plain `MethodOptions<Entity>` parameter.
|
|
662
|
+
- It works together with the [decorator options](#decorator-options) (`proxyTo`, `injectOrdering`).
|
|
663
|
+
- It is meant for dynamic methods that return entities (`findBy…`, `findOneBy…`, `findWhere…`, …). Methods that don't — `countBy…`, `existsBy…` — keep their regular signature.
|
|
664
|
+
|
|
585
665
|
---
|
|
586
666
|
|
|
587
667
|
## Query methods (raw SQL)
|
|
@@ -643,10 +723,10 @@ await userRepository.transaction(async tx => {
|
|
|
643
723
|
|
|
644
724
|
### Ad-hoc raw queries with `query()`
|
|
645
725
|
|
|
646
|
-
For one-off raw SQL that doesn't warrant declaring a `@QueryMethod` on the repository class, call `query()` directly — it's available on every `VSRepository` instance and
|
|
726
|
+
For one-off raw SQL that doesn't warrant declaring a `@QueryMethod` on the repository class, call `query()` directly — it's available on every `VSRepository` instance and uses the adapter's `query()` under the hood:
|
|
647
727
|
|
|
648
728
|
```typescript
|
|
649
|
-
query<T = any>(query: string, options?:
|
|
729
|
+
query<T = any>(query: string, options?: VSRepoQueryOptions<OrmTypes>): Promise<T>;
|
|
650
730
|
```
|
|
651
731
|
|
|
652
732
|
```typescript
|
|
@@ -725,6 +805,8 @@ Beyond the entity-shaping types covered above (`VSRepoSelect`, `VSRepoRelations`
|
|
|
725
805
|
import type {
|
|
726
806
|
MethodOptions,
|
|
727
807
|
RestrictMethodOptions,
|
|
808
|
+
InferMethodReturn,
|
|
809
|
+
InferMethodType,
|
|
728
810
|
Pagination,
|
|
729
811
|
Ordering,
|
|
730
812
|
OrderByField,
|
|
@@ -750,8 +832,10 @@ import type {
|
|
|
750
832
|
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
751
833
|
| `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
834
|
| `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). |
|
|
835
|
+
| `InferMethodReturn<T, Options>` | Opt-in strict return typing: narrows `T` (`Entity`, `Entity \| null` or `Entity[]`) to the fields and relations actually requested through `select`/`relations`. `select` wins over `relations`. | [Strict return typing with `InferMethodReturn`](#strict-return-typing-with-infermethodreturn-beta). |
|
|
836
|
+
| `InferMethodType<Args, Return, OrmTypes?>` | Declares a dynamic method whose return is inferred on each call from the `select`/`relations` passed as `options`. `OrmTypes` is optional and types the `db` option. | [Strict return typing with `InferMethodType`](#strict-return-typing-with-infermethodtype-beta). |
|
|
753
837
|
| `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
|
|
838
|
+
| `Ordering<T>` / `OrderByField<T>` / `SortDirection` | Ordering shape accepted by `getAll`, `defaultOrdering`, `injectOrdering` and by `Ordered` dynamic methods. A single object or a chained array. | [Constructor options](#constructor-options), [Decorator options](#decorator-options), [Ordering, pagination and distinct](#ordering-pagination-and-distinct). |
|
|
755
839
|
| `SeeMode` | `"active" \| "removed" \| "all"` — controls visibility of soft-deleted records. | [Soft-delete](#soft-delete). |
|
|
756
840
|
| `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
841
|
| `CountResult` | `{ count: number }` — the shape returned by batch operations. | `removeList`, `softRemoveList`, `restoreList`, `createManyIgnoreConflicts`. |
|
|
@@ -869,7 +953,6 @@ export abstract class VSRepoAdapter<T> {
|
|
|
869
953
|
update: DeepPartial<T>,
|
|
870
954
|
options?: AdapterMethodOptions<T>,
|
|
871
955
|
): Promise<T>;
|
|
872
|
-
|
|
873
956
|
abstract incrementOne<K extends NumericKeys<T>>(
|
|
874
957
|
field: K,
|
|
875
958
|
value: NonNullable<T[K]>,
|
|
@@ -914,9 +997,12 @@ export abstract class VSRepoAdapter<T> {
|
|
|
914
997
|
where?: VSRepoWhere<T>,
|
|
915
998
|
options?: AdapterMethodOptions<T>,
|
|
916
999
|
): Promise<number | null>;
|
|
1000
|
+
getPkName?(): string;
|
|
917
1001
|
}
|
|
918
1002
|
```
|
|
919
1003
|
|
|
1004
|
+
The optional `getPkName()` lets the adapter declare the entity's primary-key field to the repository. When instantiating a `VSRepository`, you can omit `pkName` from the constructor options and it will be read from `adapter.getPkName()`. If you omit it and the adapter doesn't implement `getPkName()`, the constructor throws a `VSRepoError`.
|
|
1005
|
+
|
|
920
1006
|
`VSRepository` never talks to the ORM directly — it only calls these methods with an already-resolved `VSRepoWhere<T>` and `AdapterMethodOptions<T>`. Once an adapter implements this contract, every base method, dynamic method, and query method works against it automatically. For a full, working implementation, see the external [`VSRepoPrisma7Adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter) repo.
|
|
921
1007
|
|
|
922
1008
|
### Logging from your adapter
|
|
@@ -972,14 +1058,14 @@ try {
|
|
|
972
1058
|
}
|
|
973
1059
|
```
|
|
974
1060
|
|
|
975
|
-
| `VSRepoErrorType` | Raised when
|
|
976
|
-
| ----------------- |
|
|
977
|
-
| `DECORATOR` | Invalid arguments were passed to `@DynamicMethod` or `@QueryMethod`.
|
|
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).
|
|
980
|
-
| `VALIDATOR` | Invalid method options or arguments were detected during validation.
|
|
981
|
-
| `BASE` | Invalid usage of a base method (`get`, `save`, `remove`, etc).
|
|
982
|
-
| `ADAPTER` | A `VSRepoAdapter` failed while talking to the underlying ORM/database — always thrown as `VSRepoAdapterError`.
|
|
1061
|
+
| `VSRepoErrorType` | Raised when |
|
|
1062
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1063
|
+
| `DECORATOR` | Invalid arguments were passed to `@DynamicMethod` or `@QueryMethod`. |
|
|
1064
|
+
| `RESOLVER` | The library failed to resolve a dynamic/query method's configuration into a callable method (e.g. an unknown method name). |
|
|
1065
|
+
| `DYNAMIC` | A resolved dynamic/query method failed at runtime (e.g. missing arguments). |
|
|
1066
|
+
| `VALIDATOR` | Invalid method options or arguments were detected during validation (e.g. a missing `pkName` when the adapter has no `getPkName()`). |
|
|
1067
|
+
| `BASE` | Invalid usage of a base method (`get`, `save`, `remove`, etc). |
|
|
1068
|
+
| `ADAPTER` | A `VSRepoAdapter` failed while talking to the underlying ORM/database — always thrown as `VSRepoAdapterError`. |
|
|
983
1069
|
|
|
984
1070
|
### `VSRepoAdapterError` and `AdapterErrorCode`
|
|
985
1071
|
|
|
@@ -1117,14 +1203,13 @@ npm pack --dry-run
|
|
|
1117
1203
|
npm pack
|
|
1118
1204
|
|
|
1119
1205
|
# 5. Consume it locally in another project
|
|
1120
|
-
npm install ../path/to/vsrepo
|
|
1206
|
+
npm install ../path/to/vsrepo-*.tgz
|
|
1121
1207
|
```
|
|
1122
1208
|
|
|
1123
1209
|
Notes:
|
|
1124
1210
|
|
|
1125
1211
|
- `pnpm build` runs `tsc -p tsconfig.build.json`, which outputs the compiled JS and generated type declarations into `dist/` with `rootDir: src`.
|
|
1126
1212
|
- The published package contains **only** the `dist/` folder plus the READMEs and `LICENSE` (see `files` in `package.json`). The adapters will live in their own `@vsrepo/*-adapter` packages.
|
|
1127
|
-
- The core is ORM-agnostic and has no `@prisma/client` peer dependency.
|
|
1128
1213
|
|
|
1129
1214
|
---
|
|
1130
1215
|
|
|
@@ -1151,8 +1236,8 @@ Notes:
|
|
|
1151
1236
|
Contributions are welcome, especially for improving the Prisma adapter and finishing the Drizzle one! (**[GitHub repository](https://github.com/jaobrabo123/VSRepository)**):
|
|
1152
1237
|
|
|
1153
1238
|
1. **Fork** the project.
|
|
1154
|
-
2. Create a branch for your change: `git checkout -b
|
|
1155
|
-
3. Push your branch: `git push origin
|
|
1239
|
+
2. Create a branch for your change: `git checkout -b my-change`.
|
|
1240
|
+
3. Push your branch: `git push origin my-change`.
|
|
1156
1241
|
4. Open a **Pull Request**.
|
|
1157
1242
|
|
|
1158
1243
|
To report issues or suggest features, open an **Issue**.
|