vsrepo 2.0.0 → 2.2.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 +180 -16
- package/README.pt-BR.md +180 -16
- package/dist/VSRepoAdapter.d.ts +38 -0
- package/dist/VSRepository.d.ts +45 -5
- package/dist/VSRepository.js +77 -11
- package/dist/decorators/query-method.decorator.d.ts +26 -3
- package/dist/decorators/query-method.decorator.js +25 -2
- package/dist/index.d.ts +8 -1
- package/dist/index.js +6 -1
- package/dist/internal/enums/vsrepo-error-type.enum.d.ts +1 -1
- package/dist/internal/enums/vsrepo-error-type.enum.js +1 -1
- package/dist/internal/resolvers/dynamic-methods.resolver.js +32 -6
- package/dist/internal/utils/db-arg.util.d.ts +15 -0
- package/dist/internal/utils/db-arg.util.js +22 -0
- package/dist/internal/utils/with-db.util.d.ts +19 -0
- package/dist/internal/utils/with-db.util.js +23 -0
- package/dist/internal/validators/decorators.validator.js +2 -0
- package/dist/internal/validators/vsrepo.validator.d.ts +8 -1
- package/dist/internal/validators/vsrepo.validator.js +23 -0
- package/dist/types/decorators/query-method-options.type.d.ts +35 -1
- package/dist/types/utils/decimal-like.type.d.ts +23 -0
- package/dist/types/utils/decimal-like.type.js +2 -0
- package/dist/types/utils/numeric-keys.type.d.ts +24 -0
- package/dist/types/utils/numeric-keys.type.js +2 -0
- package/dist/types/utils/numeric-like.type.d.ts +10 -0
- package/dist/types/utils/numeric-like.type.js +2 -0
- package/dist/types/utils/primitive.type.d.ts +2 -1
- package/dist/types/utils/query-args.type.d.ts +30 -0
- package/dist/types/utils/query-args.type.js +2 -0
- package/dist/types/utils/query-method-arg.type.d.ts +3 -2
- package/dist/types/utils/restrict-method-options.type.d.ts +14 -0
- package/dist/types/utils/restrict-method-options.type.js +2 -0
- package/dist/types/vsrepo/vsrepo-query-options.type.d.ts +14 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
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
17
|
|
|
18
|
-
**ORM-agnostic** repository pattern library, with full **TypeScript** support and automatic **type inference**. VSRepository v2 is a rewrite of the [v1](
|
|
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.
|
|
19
19
|
|
|
20
20
|
VSRepository lets you create strongly-typed repositories with:
|
|
21
21
|
|
|
@@ -39,6 +39,9 @@ VSRepository lets you create strongly-typed repositories with:
|
|
|
39
39
|
- [Constructor options](#constructor-options)
|
|
40
40
|
- [Base methods](#base-methods)
|
|
41
41
|
- [Soft-delete](#soft-delete)
|
|
42
|
+
- [Atomic and aggregate methods](#atomic-and-aggregate-methods)
|
|
43
|
+
- [Which fields are eligible](#which-fields-are-eligible)
|
|
44
|
+
- [Writing an adapter](#writing-an-adapter)
|
|
42
45
|
- [`select` and `relations`](#select-and-relations)
|
|
43
46
|
- [Dynamic methods](#dynamic-methods)
|
|
44
47
|
- [Available prefixes](#available-prefixes)
|
|
@@ -48,6 +51,7 @@ VSRepository lets you create strongly-typed repositories with:
|
|
|
48
51
|
- [Ordering, pagination and distinct](#ordering-pagination-and-distinct)
|
|
49
52
|
- [Decorator options](#decorator-options)
|
|
50
53
|
- [Query methods (raw SQL)](#query-methods-raw-sql)
|
|
54
|
+
- [Spread arguments with `spreadArgs`](#spread-arguments-with-spreadargs)
|
|
51
55
|
- [Ad-hoc raw queries with `query()`](#ad-hoc-raw-queries-with-query)
|
|
52
56
|
- [Transactions](#transactions)
|
|
53
57
|
- [Utility types](#utility-types)
|
|
@@ -63,7 +67,7 @@ VSRepository lets you create strongly-typed repositories with:
|
|
|
63
67
|
|
|
64
68
|
## What changed from v1
|
|
65
69
|
|
|
66
|
-
If you're coming from the [v1](
|
|
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.
|
|
67
71
|
|
|
68
72
|
| Area | v1 | v2 |
|
|
69
73
|
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
@@ -97,7 +101,7 @@ The Prisma 7 adapter has now been published to npm as `@vsrepo/prisma7-adapter`
|
|
|
97
101
|
|
|
98
102
|
| Adapter | Status |
|
|
99
103
|
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
100
|
-
| Prisma 7 (`@vsrepo/prisma7-adapter`) | 🟢 **Released** — published to npm, implements the
|
|
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. |
|
|
101
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. |
|
|
102
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. |
|
|
103
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. |
|
|
@@ -231,11 +235,19 @@ Available automatically on every `VSRepository` subclass:
|
|
|
231
235
|
| `removeList(pks, options?)` | Deletes multiple records by primary key, returning `{ count }`. |
|
|
232
236
|
| `total(options?)` | Returns the total number of records. |
|
|
233
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. |
|
|
234
246
|
| `transaction(fn, options?)` | Runs `fn` inside a native transaction of the underlying ORM. |
|
|
235
247
|
| `getDbClient()` | Returns the underlying ORM client instance used outside of transactions. |
|
|
236
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). |
|
|
237
249
|
|
|
238
|
-
|
|
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.
|
|
239
251
|
|
|
240
252
|
---
|
|
241
253
|
|
|
@@ -270,6 +282,62 @@ await userRepository.getAll({ see: "all" }); // everything, ignoring soft-delete
|
|
|
270
282
|
|
|
271
283
|
---
|
|
272
284
|
|
|
285
|
+
## Atomic and aggregate methods
|
|
286
|
+
|
|
287
|
+
Every `VSRepository` subclass gets 8 extra methods for working with numeric fields, split into two groups:
|
|
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:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
await userRepository.increment("user-1", "balance", 50); // balance = balance + 50
|
|
293
|
+
await userRepository.decrement("user-1", "balance", 50); // balance = balance - 50
|
|
294
|
+
await userRepository.multiply("user-1", "balance", 2); // balance = balance * 2
|
|
295
|
+
await userRepository.divide("user-1", "balance", 4); // balance = balance / 4
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
All four return the updated `Entity` and accept the full `MethodOptions<Entity, OrmTypes>` (`select`, `relations`, `see`, `db`) as their last argument, same as `get`/`save`/`patch`.
|
|
299
|
+
|
|
300
|
+
**Aggregates** — computed across every record matching an (optional) `where`:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
await userRepository.sum("balance"); // total balance across every active record
|
|
304
|
+
await userRepository.sum("balance", { active: true }); // ...restricted by a where
|
|
305
|
+
await userRepository.average("balance");
|
|
306
|
+
await userRepository.min("balance");
|
|
307
|
+
await userRepository.max("balance");
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
All four return `number | null` — `null` when no record matches, mirroring SQL's `SUM()`/`AVG()`/`MIN()`/`MAX()`, which return `NULL` (not `0`) over an empty set. Unlike the atomic methods, they accept the narrower `RestrictMethodOptions<Entity, OrmTypes>` (`see`, `db` only — no `select`/`relations`, since the result is a plain number, not a shaped `Entity`).
|
|
311
|
+
|
|
312
|
+
Both groups respect `softRemoveKey`/`see` the same way every other base method does — `sum("balance")` only totals non-deleted records by default, pass `{ see: "all" }` or `{ see: "removed" }` to change that.
|
|
313
|
+
|
|
314
|
+
### Which fields are eligible
|
|
315
|
+
|
|
316
|
+
`field` is constrained to `NumericKeys<Entity>` — keys whose (non-nullable) value type is a `number`, a `bigint`, or a `DecimalLike` object (anything exposing `toNumber()` and `decimalPlaces()`, matching e.g. Prisma's `Prisma.Decimal`):
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
type Product = { id: string; name: string; price: Decimal; stock: number | null };
|
|
320
|
+
|
|
321
|
+
await productRepository.increment(id, "price", new Decimal(10.5)); // ok — Decimal-like
|
|
322
|
+
await productRepository.increment(id, "stock", 5); // ok — nullable numeric fields are included
|
|
323
|
+
await productRepository.increment(id, "name", 1); // compile error — "name" isn't numeric
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
`value` is typed as `NonNullable<Entity[Field]>` — it must match the field's own type exactly. A `Decimal` field expects a `Decimal` instance, not a plain `number`/`string`:
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
await productRepository.increment(id, "price", new Decimal(10.5)); // ok
|
|
330
|
+
await productRepository.increment(id, "price", 10.5); // compile error — wrap it: new Decimal(10.5)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Note that several ORMs (Drizzle, MikroORM, TypeORM) represent `decimal`/`numeric` columns as plain `string` by default, to avoid floating-point precision loss — a `string` field does **not** satisfy `NumericKeys<Entity>` out of the box. Configure the column in a numeric mode (or a transformer) on those ORMs if you want the field to be usable with these 8 methods.
|
|
334
|
+
|
|
335
|
+
### Writing an adapter
|
|
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.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
273
341
|
## `select` and `relations`
|
|
274
342
|
|
|
275
343
|
v1's named, reusable `selectModels`/`defaultSelectModel` are gone. In v2 you pass `select` and `relations` directly on each call — there's nothing to pre-register:
|
|
@@ -531,21 +599,54 @@ class UserRepository extends VSRepository<User, string> {
|
|
|
531
599
|
|
|
532
600
|
@QueryMethod('UPDATE "user" SET active = true WHERE id = $1', { modifying: true })
|
|
533
601
|
declare activateUser: (arg: QueryMethodArg<[id: string]>) => Promise<number>;
|
|
602
|
+
|
|
603
|
+
// Only one row is ever expected here, so `singleResult` collapses the
|
|
604
|
+
// array into a single object (or `null` when no row matches).
|
|
605
|
+
@QueryMethod('SELECT * FROM "user" WHERE id = $1 LIMIT 1', { singleResult: true })
|
|
606
|
+
declare findByIdRaw: (arg: QueryMethodArg<[id: string]>) => Promise<User | null>;
|
|
534
607
|
}
|
|
535
608
|
```
|
|
536
609
|
|
|
537
|
-
| Option
|
|
538
|
-
|
|
|
539
|
-
| `modifying`
|
|
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. |
|
|
613
|
+
| `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). |
|
|
540
614
|
|
|
541
615
|
Query methods accept `{ args, db? }` at the call site — `db` lets them participate in a `transaction()` block just like base and dynamic methods.
|
|
542
616
|
|
|
617
|
+
### Spread arguments with `spreadArgs`
|
|
618
|
+
|
|
619
|
+
By default, a `@QueryMethod` receives its placeholder values through a single `QueryMethodArg` object (`method({ args: [...] })`). Set `spreadArgs: true` to receive them as separate positional arguments instead, JpaRepository style:
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
class UserRepository extends VSRepository<User, string> {
|
|
623
|
+
@QueryMethod('SELECT * FROM "user" WHERE email = $1 AND "userType" = $2', {
|
|
624
|
+
spreadArgs: true,
|
|
625
|
+
})
|
|
626
|
+
declare findByEmailAndType: (
|
|
627
|
+
...args: QueryArgs<[email: string, userType: string]>
|
|
628
|
+
) => Promise<User[]>;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const admins = await userRepository.findByEmailAndType("joao@email.com", "admin");
|
|
632
|
+
```
|
|
633
|
+
|
|
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
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
await userRepository.transaction(async (tx) => {
|
|
638
|
+
await userRepository.findByEmailAndType("joao@email.com", "admin", withDb(tx));
|
|
639
|
+
});
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
`spreadArgs` only affects `@QueryMethod`-declared fields — it's `false` by default, and calling a method declared without it using more than one argument throws, since the single-`QueryMethodArg` call style is expected instead. It has no effect on `query()`, which always accepts `{ args, db? }`.
|
|
643
|
+
|
|
543
644
|
### Ad-hoc raw queries with `query()`
|
|
544
645
|
|
|
545
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 goes through the same adapter's `query()` implementation under the hood:
|
|
546
647
|
|
|
547
648
|
```typescript
|
|
548
|
-
query<T = any>(query: string, options?: { args?: any[]; db?: any; modifying?: boolean }): Promise<T>;
|
|
649
|
+
query<T = any>(query: string, options?: { args?: any[]; db?: any; modifying?: boolean; singleResult?: boolean }): Promise<T>;
|
|
549
650
|
```
|
|
550
651
|
|
|
551
652
|
```typescript
|
|
@@ -557,13 +658,21 @@ const affectedRows = await userRepository.query<number>(
|
|
|
557
658
|
'UPDATE "user" SET active = true WHERE id = $1',
|
|
558
659
|
{ args: ["123"], modifying: true },
|
|
559
660
|
);
|
|
661
|
+
|
|
662
|
+
// Only one row is ever expected here, so `singleResult` collapses the
|
|
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
|
+
);
|
|
560
668
|
```
|
|
561
669
|
|
|
562
|
-
| Option
|
|
563
|
-
|
|
|
564
|
-
| `args`
|
|
565
|
-
| `db`
|
|
566
|
-
| `modifying`
|
|
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). |
|
|
567
676
|
|
|
568
677
|
Just like base, dynamic and query methods, `query()` accepts `db` in `options` to participate in a `transaction()` block.
|
|
569
678
|
|
|
@@ -615,6 +724,7 @@ Beyond the entity-shaping types covered above (`VSRepoSelect`, `VSRepoRelations`
|
|
|
615
724
|
```typescript
|
|
616
725
|
import type {
|
|
617
726
|
MethodOptions,
|
|
727
|
+
RestrictMethodOptions,
|
|
618
728
|
Pagination,
|
|
619
729
|
Ordering,
|
|
620
730
|
OrderByField,
|
|
@@ -623,7 +733,11 @@ import type {
|
|
|
623
733
|
DeepPartial,
|
|
624
734
|
CountResult,
|
|
625
735
|
QueryMethodArg,
|
|
736
|
+
QueryArgs,
|
|
626
737
|
KeysOfType,
|
|
738
|
+
NumericKeys,
|
|
739
|
+
NumericLike,
|
|
740
|
+
DecimalLike,
|
|
627
741
|
Primitive,
|
|
628
742
|
VSRepoWhere,
|
|
629
743
|
VSRepoOrmTypes,
|
|
@@ -634,14 +748,19 @@ import type {
|
|
|
634
748
|
|
|
635
749
|
| Type | Description | Used by |
|
|
636
750
|
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
637
|
-
| `MethodOptions<T, K>` | Options accepted as the last argument of
|
|
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). |
|
|
638
753
|
| `Pagination` | `{ limit?, offset? }` accepted by `getAll` and by `Paginated` dynamic methods. | [Base methods](#base-methods), [Ordering, pagination and distinct](#ordering-pagination-and-distinct). |
|
|
639
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). |
|
|
640
755
|
| `SeeMode` | `"active" \| "removed" \| "all"` — controls visibility of soft-deleted records. | [Soft-delete](#soft-delete). |
|
|
641
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`. |
|
|
642
757
|
| `CountResult` | `{ count: number }` — the shape returned by batch operations. | `removeList`, `softRemoveList`, `restoreList`, `createManyIgnoreConflicts`. |
|
|
643
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). |
|
|
644
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). |
|
|
645
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. |
|
|
646
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). |
|
|
647
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). |
|
|
@@ -750,6 +869,51 @@ export abstract class VSRepoAdapter<T> {
|
|
|
750
869
|
update: DeepPartial<T>,
|
|
751
870
|
options?: AdapterMethodOptions<T>,
|
|
752
871
|
): Promise<T>;
|
|
872
|
+
|
|
873
|
+
abstract incrementOne<K extends NumericKeys<T>>(
|
|
874
|
+
field: K,
|
|
875
|
+
value: NonNullable<T[K]>,
|
|
876
|
+
where: VSRepoWhere<T>,
|
|
877
|
+
options?: AdapterMethodOptions<T>,
|
|
878
|
+
): Promise<T>;
|
|
879
|
+
abstract decrementOne<K extends NumericKeys<T>>(
|
|
880
|
+
field: K,
|
|
881
|
+
value: NonNullable<T[K]>,
|
|
882
|
+
where: VSRepoWhere<T>,
|
|
883
|
+
options?: AdapterMethodOptions<T>,
|
|
884
|
+
): Promise<T>;
|
|
885
|
+
abstract multiplyOne<K extends NumericKeys<T>>(
|
|
886
|
+
field: K,
|
|
887
|
+
value: NonNullable<T[K]>,
|
|
888
|
+
where: VSRepoWhere<T>,
|
|
889
|
+
options?: AdapterMethodOptions<T>,
|
|
890
|
+
): Promise<T>;
|
|
891
|
+
abstract divideOne<K extends NumericKeys<T>>(
|
|
892
|
+
field: K,
|
|
893
|
+
value: NonNullable<T[K]>,
|
|
894
|
+
where: VSRepoWhere<T>,
|
|
895
|
+
options?: AdapterMethodOptions<T>,
|
|
896
|
+
): Promise<T>;
|
|
897
|
+
abstract sum(
|
|
898
|
+
field: NumericKeys<T>,
|
|
899
|
+
where?: VSRepoWhere<T>,
|
|
900
|
+
options?: AdapterMethodOptions<T>,
|
|
901
|
+
): Promise<number | null>;
|
|
902
|
+
abstract average(
|
|
903
|
+
field: NumericKeys<T>,
|
|
904
|
+
where?: VSRepoWhere<T>,
|
|
905
|
+
options?: AdapterMethodOptions<T>,
|
|
906
|
+
): Promise<number | null>;
|
|
907
|
+
abstract min(
|
|
908
|
+
field: NumericKeys<T>,
|
|
909
|
+
where?: VSRepoWhere<T>,
|
|
910
|
+
options?: AdapterMethodOptions<T>,
|
|
911
|
+
): Promise<number | null>;
|
|
912
|
+
abstract max(
|
|
913
|
+
field: NumericKeys<T>,
|
|
914
|
+
where?: VSRepoWhere<T>,
|
|
915
|
+
options?: AdapterMethodOptions<T>,
|
|
916
|
+
): Promise<number | null>;
|
|
753
917
|
}
|
|
754
918
|
```
|
|
755
919
|
|
|
@@ -812,7 +976,7 @@ try {
|
|
|
812
976
|
| ----------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
813
977
|
| `DECORATOR` | Invalid arguments were passed to `@DynamicMethod` or `@QueryMethod`. |
|
|
814
978
|
| `RESOLVER` | The library failed to resolve a dynamic/query method's configuration into a callable method (e.g. an unknown method name). |
|
|
815
|
-
| `DYNAMIC` | A resolved dynamic method failed at runtime (e.g. missing arguments). |
|
|
979
|
+
| `DYNAMIC` | A resolved dynamic/query method failed at runtime (e.g. missing arguments). |
|
|
816
980
|
| `VALIDATOR` | Invalid method options or arguments were detected during validation. |
|
|
817
981
|
| `BASE` | Invalid usage of a base method (`get`, `save`, `remove`, etc). |
|
|
818
982
|
| `ADAPTER` | A `VSRepoAdapter` failed while talking to the underlying ORM/database — always thrown as `VSRepoAdapterError`. |
|
|
@@ -957,7 +1121,7 @@ npm install ../path/to/vsrepo-1.4.0.tgz
|
|
|
957
1121
|
Notes:
|
|
958
1122
|
|
|
959
1123
|
- `pnpm build` runs `tsc -p tsconfig.build.json`, which outputs the compiled JS and generated type declarations into `dist/` with `rootDir: src`.
|
|
960
|
-
- The published package contains **only** the `dist/` folder plus the READMEs and `LICENSE` (see `files` in `package.json`).
|
|
1124
|
+
- 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.
|
|
961
1125
|
- The core is ORM-agnostic and has no `@prisma/client` peer dependency.
|
|
962
1126
|
|
|
963
1127
|
---
|