vsrepo 1.3.3 β 1.3.4
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 +108 -3
- package/README.pt-BR.md +1556 -0
- package/dist/DynamicRepository.d.ts +84 -14
- package/dist/DynamicRepository.js +3 -31
- package/dist/VSRepository.d.ts +92 -9
- package/dist/VSRepository.js +22 -0
- package/dist/internal/decorators/dynamic-method.decorator.js +1 -1
- package/dist/internal/decorators/query-method.decorator.js +16 -0
- package/dist/internal/entities/dynamic-method-metadata.entity.js +3 -1
- package/dist/internal/utils/schemas.util.js +6 -1
- package/dist/internal/validation/query-method-arg.validate.js +22 -0
- package/dist/internal/validation/query-method-options.validate.js +24 -0
- package/dist/internal/validation/types/query-method-arg.type.js +2 -0
- package/dist/internal/validation/types/query-method-options.type.js +2 -0
- package/package.json +1 -1
- package/scripts/configure-prisma-import.mjs +16 -12
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|

|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
πΊπΈ You're reading the English version. [π§π· Ler em portuguΓͺs](./README.pt-BR.md)
|
|
8
|
+
|
|
7
9
|
Repository pattern library for projects using **Prisma**, with full **TypeScript** support and automatic **type inference**.
|
|
8
10
|
|
|
9
11
|
VSRepository lets you create strongly-typed repositories with:
|
|
@@ -34,6 +36,7 @@ VSRepository lets you create strongly-typed repositories with:
|
|
|
34
36
|
- [Configuring the base methods](#configuring-the-base-methods)
|
|
35
37
|
- [Select Models](#select-models)
|
|
36
38
|
- [Include Models](#include-models)
|
|
39
|
+
- [Raw include (options.include)](#raw-include-optionsinclude)
|
|
37
40
|
- [Required Where](#required-where)
|
|
38
41
|
- [Default Ordenation](#default-ordenation)
|
|
39
42
|
- [`see` option](#see-option)
|
|
@@ -46,6 +49,7 @@ VSRepository lets you create strongly-typed repositories with:
|
|
|
46
49
|
- [Distinct](#distinct)
|
|
47
50
|
- [Method configuration](#method-configuration)
|
|
48
51
|
- [Aggregate and GroupBy](#aggregate-and-groupby)
|
|
52
|
+
- [Query Methods](#query-methods)
|
|
49
53
|
- [Relations in save](#relations-in-save)
|
|
50
54
|
- [Transactions](#transactions)
|
|
51
55
|
- [Extending a repository](#extending-a-repository)
|
|
@@ -181,7 +185,7 @@ await userRepository.remove(user.id);
|
|
|
181
185
|
|
|
182
186
|
If you prefer an OOP style with decorators instead of the functional `setupVSRepo` approach, VSRepository also provides `DynamicRepository` β a class you can extend with `@DynamicMethod()` decorators to define your dynamic methods.
|
|
183
187
|
|
|
184
|
-
See **[README-DynamicRepo.md](./README-DynamicRepo.md)** for full documentation on the class-based approach, including NestJS integration examples, decorator config, and a comparison with `setupVSRepo`.
|
|
188
|
+
See **[README-DynamicRepo.md](./README-DynamicRepo.md)** (or the [pt-BR version](./README-DynamicRepo.pt-BR.md)) for full documentation on the class-based approach, including NestJS integration examples, decorator config, and a comparison with `setupVSRepo`.
|
|
185
189
|
|
|
186
190
|
---
|
|
187
191
|
|
|
@@ -572,6 +576,35 @@ await userRepository.get(id, { selectModel: "public" });
|
|
|
572
576
|
await userRepository.get(id, { selectModel: "public", includeModel: "withPosts" });
|
|
573
577
|
```
|
|
574
578
|
|
|
579
|
+
### Raw `include` (`options.include`)
|
|
580
|
+
|
|
581
|
+
Besides `includeModel` (named, pre-configured in `includeModels`), you can pass a raw Prisma `include` directly in the call, without registering it beforehand on the repository.
|
|
582
|
+
|
|
583
|
+
```ts
|
|
584
|
+
const user = await userRepository.get(id, {
|
|
585
|
+
include: { posts: true, profile: true },
|
|
586
|
+
});
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
`options.include` accepts any valid `include` for the repository's Prisma model β it's fully typed and offers the same autocomplete/validation as calling `prisma.user.findMany({ include: ... })` directly.
|
|
590
|
+
|
|
591
|
+
**Rules and behavior:**
|
|
592
|
+
|
|
593
|
+
- **Mutually exclusive with `selectModel` and `includeModel`.** Only one of the three can be provided per call; the types enforce this β passing more than one is a compile-time error.
|
|
594
|
+
- **Ad hoc, not reusable.** Unlike `includeModel`, it doesn't need to be declared in `includeModels`. Use it for one-off includes that don't justify a named model.
|
|
595
|
+
- **No `selectModel` default is applied.** As with `includeModel`, when `include` is provided the select (including `defaultSelectModel`) is ignored and only the `include` is sent to Prisma.
|
|
596
|
+
|
|
597
|
+
```ts
|
|
598
|
+
// CORRECT β
β raw include only
|
|
599
|
+
await userRepository.get(id, { include: { posts: true } });
|
|
600
|
+
|
|
601
|
+
// WRONG β β combining include with selectModel/includeModel is not allowed
|
|
602
|
+
await userRepository.get(id, { selectModel: "public", include: { posts: true } });
|
|
603
|
+
await userRepository.get(id, { includeModel: "withPosts", include: { posts: true } });
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
> **When to use `includeModel` vs. `include`:** prefer `includeModel` for includes reused across multiple calls (defined once in `includeModels`); use `include` for specific, occasional includes that don't need a name.
|
|
607
|
+
|
|
575
608
|
---
|
|
576
609
|
|
|
577
610
|
## Required Where
|
|
@@ -1022,6 +1055,7 @@ Each entry in `methods` accepts the following options:
|
|
|
1022
1055
|
| `pushWhere` | `WhereModel<M>` | β | Extra `where` added to the query in addition to `requiredWhere`. |
|
|
1023
1056
|
| `injectOrdenation` | `OrdenationModel<M>` | β | Fixed ordering automatically injected into the query. |
|
|
1024
1057
|
| `injectPagination` | `PaginationModel<M>` | β | Fixed pagination automatically injected into the query. |
|
|
1058
|
+
| `query` | `{ value: string; modifying?: boolean }` | β | Turns the method into a **Query Method** (raw SQL). Ignores every other option above β see [Query Methods](#query-methods). |
|
|
1025
1059
|
|
|
1026
1060
|
---
|
|
1027
1061
|
|
|
@@ -1044,6 +1078,77 @@ const userRepository = setupVSRepo<User, "user">()(({
|
|
|
1044
1078
|
|
|
1045
1079
|
---
|
|
1046
1080
|
|
|
1081
|
+
### Query Methods
|
|
1082
|
+
|
|
1083
|
+
Query Methods let a method run **raw SQL** directly, completely bypassing the dynamic-method name parser. They're useful for complex queries (heavy joins, CTEs, database-specific functions) that aren't practical to express with the standard prefixes/suffixes.
|
|
1084
|
+
|
|
1085
|
+
Internally, VSRepository executes the SQL through Prisma using `$queryRawUnsafe` (for reads) or `$executeRawUnsafe` (for writes), and the values in the `args` array are passed as **positional parameters** (`$1`, `$2`, ...) β the same prepared-statement technique Prisma itself uses. This means the values are never concatenated into the SQL string, which is what actually prevents SQL injection.
|
|
1086
|
+
|
|
1087
|
+
> [!WARNING]
|
|
1088
|
+
> `$1`, `$2`, ... in your SQL must always represent **values** (data parameters), never column names, table names, or dynamic SQL fragments. Identifier names (columns/tables) can't be passed as a positional parameter β if your method needs to vary those, build the SQL from a fixed, known set of options in your own code, never from untrusted input.
|
|
1089
|
+
|
|
1090
|
+
```ts
|
|
1091
|
+
const userRepository = setupVSRepo<User, "user">()({
|
|
1092
|
+
tableName: "user",
|
|
1093
|
+
pkName: "id",
|
|
1094
|
+
methods: {
|
|
1095
|
+
// Read query method (non-modifying)
|
|
1096
|
+
findActiveUsersRaw: {
|
|
1097
|
+
map: true,
|
|
1098
|
+
query: {
|
|
1099
|
+
value: 'SELECT * FROM "user" WHERE active = $1',
|
|
1100
|
+
},
|
|
1101
|
+
},
|
|
1102
|
+
|
|
1103
|
+
// Write query method (modifying: true)
|
|
1104
|
+
deactivateUsersOlderThanRaw: {
|
|
1105
|
+
map: true,
|
|
1106
|
+
query: {
|
|
1107
|
+
value: 'UPDATE "user" SET active = false WHERE "createdAt" < $1',
|
|
1108
|
+
modifying: true,
|
|
1109
|
+
},
|
|
1110
|
+
},
|
|
1111
|
+
},
|
|
1112
|
+
}).build(prisma);
|
|
1113
|
+
```
|
|
1114
|
+
|
|
1115
|
+
**Calling a Query Method:**
|
|
1116
|
+
|
|
1117
|
+
Every Query Method takes a single argument shaped as `{ args: [...], db? }`:
|
|
1118
|
+
|
|
1119
|
+
```ts
|
|
1120
|
+
// Non-modifying: returns 'any' by default, but accepts a generic to
|
|
1121
|
+
// infer/assert the return type right at the call site
|
|
1122
|
+
const activeUsers = await userRepository.findActiveUsersRaw<User[]>({
|
|
1123
|
+
args: [true],
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
// Modifying: always returns 'number' (count of affected rows)
|
|
1127
|
+
const affected = await userRepository.deactivateUsersOlderThanRaw({
|
|
1128
|
+
args: [new Date("2024-01-01")],
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
// Participating in a transaction, via 'db'
|
|
1132
|
+
await userRepository.prisma.$transaction(async (tx) => {
|
|
1133
|
+
await userRepository.deactivateUsersOlderThanRaw({
|
|
1134
|
+
args: [new Date("2024-01-01")],
|
|
1135
|
+
db: tx,
|
|
1136
|
+
});
|
|
1137
|
+
});
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
| Option | Type | Default | Description |
|
|
1141
|
+
| ------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------|
|
|
1142
|
+
| `value` | `string` | β | **Required.** Raw SQL to execute. Use `$1`, `$2`, ... for the placeholders of the values in `args`. |
|
|
1143
|
+
| `modifying` | `boolean` | `false` | When `true`, executes via `$executeRawUnsafe` and the method always resolves to `number`. When `false`, executes via `$queryRawUnsafe` and the method resolves to `TReturn` (`any` by default, inferable via a generic at the call site). |
|
|
1144
|
+
|
|
1145
|
+
> [!NOTE]
|
|
1146
|
+
> Unlike the other dynamic methods, Query Methods **completely ignore** `selectModels`, `requiredWhere`, `pushWhere`, `whereType`, `injectOrdenation`, `injectPagination`, and `proxyTo` β none of that applies, since there's no name parsing or `where`/`select` assembly by VSRepository. Free-form method names (outside the `findBy`, `updateBy`, etc. patterns) also **don't** require `proxyTo`.
|
|
1147
|
+
|
|
1148
|
+
The same functionality is available in the class-based approach via the `@QueryMethod` decorator β see [README-DynamicRepo.md](./README-DynamicRepo.md#the-querymethod-decorator).
|
|
1149
|
+
|
|
1150
|
+
---
|
|
1151
|
+
|
|
1047
1152
|
## Relations in save
|
|
1048
1153
|
|
|
1049
1154
|
Configure relations so that `save` and `patch` manage them automatically (`saveList` and `patchList` also manage relations automatically).
|
|
@@ -1438,9 +1543,9 @@ Recommended `tsconfig.json`:
|
|
|
1438
1543
|
|
|
1439
1544
|
**Select model returns unexpected fields** β Check that the select model defines exactly the fields your TypeScript type expects.
|
|
1440
1545
|
|
|
1441
|
-
**`selectModel` and `
|
|
1546
|
+
**`selectModel`, `includeModel` and `include` together in the same call** β Not allowed. Only one of the three can be provided per call: if `includeModel` or `include` is provided, the `select` (including `defaultSelectModel`) is ignored and only the `include` is sent to Prisma.
|
|
1442
1547
|
|
|
1443
|
-
**`includeModel` doesn't appear as a default repository option** β This is expected. Unlike `defaultSelectModel`, there's no `defaultIncludeModel`/`defaultInclude`. An `includeModel` can only be set in the method call, via `options.includeModel`.
|
|
1548
|
+
**`includeModel` doesn't appear as a default repository option** β This is expected. Unlike `defaultSelectModel`, there's no `defaultIncludeModel`/`defaultInclude`. An `includeModel` can only be set in the method call, via `options.includeModel`. A raw, ad hoc include can be set via `options.include`, without needing to be registered in `includeModels`.
|
|
1444
1549
|
|
|
1445
1550
|
**`softRemovekName` throws an error at build** β The provided field must be of type `DateTime` in the Prisma schema. Types like `Boolean` or `String` are not accepted.
|
|
1446
1551
|
|